From 5cecc774482026adfb3afa3ed842892bf5078f13 Mon Sep 17 00:00:00 2001 From: Giorgio Gambino <151543+giogam@users.noreply.github.com> Date: Mon, 24 Feb 2025 13:40:15 +0000 Subject: [PATCH 01/14] chore(data-streams): adds don related label to node registration changeset (#16534) --- .../data-streams/changeset/jd_register_nodes.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/deployment/data-streams/changeset/jd_register_nodes.go b/deployment/data-streams/changeset/jd_register_nodes.go index 42be94446cc..a1ec40d9308 100644 --- a/deployment/data-streams/changeset/jd_register_nodes.go +++ b/deployment/data-streams/changeset/jd_register_nodes.go @@ -66,14 +66,20 @@ func validateNodeSlice(nodes []NodeCfg, nodeType string, donIndex int) error { return nil } -func registerNodesForDON(e deployment.Environment, nodes []NodeCfg, baseLabels []*ptypes.Label, nodeType NodeType) { +func registerNodesForDON(e deployment.Environment, donName string, donID int, nodes []NodeCfg, baseLabels []*ptypes.Label, nodeType NodeType) { ntStr := nodeType.String() for _, node := range nodes { labels := append([]*ptypes.Label(nil), baseLabels...) + labels = append(labels, &ptypes.Label{ Key: "nodeType", Value: &ntStr, }) + + labels = append(labels, &ptypes.Label{ + Key: fmt.Sprintf("don-%d-%s", donID, donName), + }) + nodeID, err := e.Offchain.RegisterNode(e.GetContext(), &nodev1.RegisterNodeRequest{ Name: node.Name, PublicKey: node.CSAKey, @@ -100,8 +106,8 @@ func RegisterNodesWithJD(e deployment.Environment, cfg RegisterNodesInput) (depl } for _, don := range cfg.DONsList { - registerNodesForDON(e, don.Nodes, baseLabels, NodeTypeOracle) - registerNodesForDON(e, don.BootstrapNodes, baseLabels, NodeTypeBootstrap) + registerNodesForDON(e, don.Name, don.ID, don.Nodes, baseLabels, NodeTypeOracle) + registerNodesForDON(e, don.Name, don.ID, don.BootstrapNodes, baseLabels, NodeTypeBootstrap) } return deployment.ChangesetOutput{}, nil From 3c01f57fde66d80dfc69a3868aed293140c3aeed Mon Sep 17 00:00:00 2001 From: dimitris Date: Mon, 24 Feb 2025 15:48:51 +0200 Subject: [PATCH 02/14] Upgrade cl-ccip (#16533) * chain fee async cfg * upgrade cl-ccip * upgrade cl-ccip * upgrade cl-ccip * upgrade cl-ccip * upgrade cl-ccip * upgrade cl-ccip * update cl-ccip --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- deployment/ccip/changeset/v1_6/cs_ccip_home.go | 2 ++ deployment/go.mod | 2 +- deployment/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 ++-- system-tests/lib/go.mod | 2 +- system-tests/lib/go.sum | 4 ++-- system-tests/tests/go.mod | 2 +- system-tests/tests/go.sum | 4 ++-- 15 files changed, 23 insertions(+), 21 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index a82a435148a..680f47423a4 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -340,7 +340,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.1.0 // indirect github.com/smartcontractkit/chain-selectors v1.0.40 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 6a06a01b3e7..721b690ba76 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1086,8 +1086,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 h1:voKtyPNWsT4o/IilRbkEMsvYEWhYMpkl94mi3fDQz60= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= diff --git a/deployment/ccip/changeset/v1_6/cs_ccip_home.go b/deployment/ccip/changeset/v1_6/cs_ccip_home.go index 3ed6017f243..699e66f51bb 100644 --- a/deployment/ccip/changeset/v1_6/cs_ccip_home.go +++ b/deployment/ccip/changeset/v1_6/cs_ccip_home.go @@ -222,6 +222,8 @@ func WithDefaultCommitOffChainConfig(feedChainSel uint64, tokenInfo map[ccipocr3 MerkleRootAsyncObserverDisabled: false, MerkleRootAsyncObserverSyncFreq: 4 * time.Second, MerkleRootAsyncObserverSyncTimeout: 12 * time.Second, + ChainFeeAsyncObserverSyncFreq: 10 * time.Second, + ChainFeeAsyncObserverSyncTimeout: 12 * time.Second, } } else { if params.CommitOffChainConfig.TokenInfo == nil { diff --git a/deployment/go.mod b/deployment/go.mod index 1e0f662ff55..aba9cc0aaec 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -30,7 +30,7 @@ require ( github.com/sethvargo/go-retry v0.2.4 github.com/smartcontractkit/ccip-owner-contracts v0.1.0 github.com/smartcontractkit/chain-selectors v1.0.40 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20250211162441-3d6cea220efb diff --git a/deployment/go.sum b/deployment/go.sum index 5524af3ebc2..c37601139cf 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1134,8 +1134,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 h1:voKtyPNWsT4o/IilRbkEMsvYEWhYMpkl94mi3fDQz60= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= diff --git a/go.mod b/go.mod index 70e2667a0dc..bdd5ee85fff 100644 --- a/go.mod +++ b/go.mod @@ -76,7 +76,7 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chain-selectors v1.0.40 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 diff --git a/go.sum b/go.sum index d99750cf925..bde89e1c8a3 100644 --- a/go.sum +++ b/go.sum @@ -1018,8 +1018,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 h1:voKtyPNWsT4o/IilRbkEMsvYEWhYMpkl94mi3fDQz60= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index c0b4f401ba1..d6c4f6b6b93 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -46,7 +46,7 @@ require ( github.com/slack-go/slack v0.15.0 github.com/smartcontractkit/chain-selectors v1.0.40 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea github.com/smartcontractkit/chainlink-integrations/evm v0.0.0-20250213145514-41d874782c02 github.com/smartcontractkit/chainlink-protos/job-distributor v0.9.0 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 7ef48b47531..f59223f1a01 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1384,8 +1384,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 h1:voKtyPNWsT4o/IilRbkEMsvYEWhYMpkl94mi3fDQz60= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index 61f8b6e7465..ca9802216f3 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -28,7 +28,7 @@ require ( github.com/rs/zerolog v1.33.0 github.com/slack-go/slack v0.15.0 github.com/smartcontractkit/chain-selectors v1.0.40 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea github.com/smartcontractkit/chainlink-integrations/evm v0.0.0-20250213145514-41d874782c02 github.com/smartcontractkit/chainlink-testing-framework/lib v1.51.0 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index a482b75366b..fa62425009d 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1369,8 +1369,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 h1:voKtyPNWsT4o/IilRbkEMsvYEWhYMpkl94mi3fDQz60= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index bb0dfe89443..b1453a2d2e2 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -338,7 +338,7 @@ require ( github.com/smartcontractkit/ccip-owner-contracts v0.1.0 // indirect github.com/smartcontractkit/chain-selectors v1.0.40 // indirect github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index 4bcb3866c1c..f9f9026508d 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1122,8 +1122,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 h1:voKtyPNWsT4o/IilRbkEMsvYEWhYMpkl94mi3fDQz60= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index 395b0882f5d..42dd042a2df 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -341,7 +341,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.1.0 // indirect github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 // indirect diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index d2bd8b1075e..720962bbaa1 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1122,8 +1122,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4 h1:voKtyPNWsT4o/IilRbkEMsvYEWhYMpkl94mi3fDQz60= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250221121938-dd0db587bff4/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= From aefb2b75f0e7b6f099176f57e4a19e3ce15fe7cc Mon Sep 17 00:00:00 2001 From: Yashvardhan Nevatia Date: Mon, 24 Feb 2025 13:54:15 +0000 Subject: [PATCH 03/14] TokenPool split and other QOL (#16472) * modularising billing to be reused in cs_deploy * adding offramp instruction * adding remote billing for token transfers * loading bytes * Adding tp types to state * deploy and preload tps * bug fix * adding spltokens array and func to map tokenaddress to program * token pool split in changesets * deprecating old token pool, just commenting first * lint * adding wsol to spltokens * removing consts * using same type * changes * removing token program name from billinh * removing old consts * changing type and using index 1 * renaming and adding config * cascading changes * removing redundant const * cascading changes and adding billing through config * adding validate * renaming packages * linting * include spltokens in validation * abstract deployToken in test * fix logging * dont add link and wsol to token array * use token symbol as input * lint and bug * bug * lint * fix --- .../changeset/solana/cs_add_remote_chain.go | 67 ++-- .../ccip/changeset/solana/cs_billing.go | 86 ++--- .../changeset/solana/cs_chain_contracts.go | 26 +- .../solana/cs_chain_contracts_test.go | 259 +++++++-------- .../ccip/changeset/solana/cs_deploy_chain.go | 195 +++++++---- .../changeset/solana/cs_deploy_chain_test.go | 127 ++++---- .../ccip/changeset/solana/cs_set_ocr3.go | 3 +- .../ccip/changeset/solana/cs_solana_token.go | 25 +- .../changeset/solana/cs_solana_token_test.go | 6 +- .../solana/cs_token_admin_registry.go | 14 +- .../ccip/changeset/solana/cs_token_pool.go | 302 +++++++++++++----- ...ransfer_ccip_to_mcms_with_timelock_test.go | 31 +- deployment/ccip/changeset/solana_state.go | 77 +++-- deployment/ccip/changeset/state.go | 34 +- .../changeset/testhelpers/test_environment.go | 58 +++- .../changeset/testhelpers/test_helpers.go | 66 ++-- .../ccip/changeset/v1_6/cs_ccip_home.go | 4 +- .../ccip/changeset/v1_6/cs_chain_contracts.go | 9 +- deployment/environment/memory/chain.go | 20 +- deployment/solana_chain.go | 11 - 20 files changed, 855 insertions(+), 565 deletions(-) diff --git a/deployment/ccip/changeset/solana/cs_add_remote_chain.go b/deployment/ccip/changeset/solana/cs_add_remote_chain.go index 6d84816ad22..fcf8ffe1465 100644 --- a/deployment/ccip/changeset/solana/cs_add_remote_chain.go +++ b/deployment/ccip/changeset/solana/cs_add_remote_chain.go @@ -15,10 +15,8 @@ import ( solCommonUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" solState "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state" - chainsel "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/chainlink/deployment" - cs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" ) @@ -30,7 +28,7 @@ type AddRemoteChainToSolanaConfig struct { UpdatesByChain map[uint64]RemoteChainConfigSolana // Disallow mixing MCMS/non-MCMS per chain for simplicity. // (can still be achieved by calling this function multiple times) - MCMS *cs.MCMSConfig + MCMS *ccipChangeset.MCMSConfig } type RemoteChainConfigSolana struct { @@ -42,7 +40,7 @@ type RemoteChainConfigSolana struct { } func (cfg AddRemoteChainToSolanaConfig) Validate(e deployment.Environment) error { - state, err := cs.LoadOnchainState(e) + state, err := ccipChangeset.LoadOnchainState(e) if err != nil { return fmt.Errorf("failed to load onchain state: %w", err) } @@ -84,7 +82,7 @@ func (cfg AddRemoteChainToSolanaConfig) Validate(e deployment.Environment) error if remote == routerConfigAccount.SvmChainSelector { return fmt.Errorf("cannot add remote chain %d with same chain selector as current chain %d", remote, cfg.ChainSelector) } - if err := state.ValidateRamp(remote, cs.OnRamp); err != nil { + if err := state.ValidateRamp(remote, ccipChangeset.OnRamp); err != nil { return err } routerDestChainPDA, err := solState.FindDestChainStatePDA(remote, chainState.Router) @@ -106,7 +104,7 @@ func AddRemoteChainToSolana(e deployment.Environment, cfg AddRemoteChainToSolana return deployment.ChangesetOutput{}, err } - s, err := cs.LoadOnchainState(e) + s, err := ccipChangeset.LoadOnchainState(e) if err != nil { return deployment.ChangesetOutput{}, err } @@ -121,7 +119,7 @@ func AddRemoteChainToSolana(e deployment.Environment, cfg AddRemoteChainToSolana func doAddRemoteChainToSolana( e deployment.Environment, - s cs.CCIPOnChainState, + s ccipChangeset.CCIPOnChainState, chainSel uint64, updates map[uint64]RemoteChainConfigSolana, ab deployment.AddressBook) error { @@ -134,33 +132,39 @@ func doAddRemoteChainToSolana( for remoteChainSel, update := range updates { var onRampBytes [64]byte // already verified, skipping errcheck - remoteChainFamily, _ := chainsel.GetSelectorFamily(remoteChainSel) - var addressBytes []byte - switch remoteChainFamily { - case chainsel.FamilySolana: - addressBytes, _ = s.SolChains[remoteChainSel].OnRampBytes() - case chainsel.FamilyEVM: - addressBytes, _ = s.Chains[remoteChainSel].OnRampBytes() - } + addressBytes, _ := s.GetOnRampAddressBytes(remoteChainSel) addressBytes = common.LeftPadBytes(addressBytes, 64) copy(onRampBytes[:], addressBytes) // verified while loading state - fqDestChainPDA, _, _ := solState.FindFqDestChainPDA(remoteChainSel, feeQuoterID) - routerDestChainPDA, _ := solState.FindDestChainStatePDA(remoteChainSel, ccipRouterID) - offRampSourceChainPDA, _, _ := solState.FindOfframpSourceChainPDA(remoteChainSel, s.SolChains[chainSel].OffRamp) + fqRemoteChainPDA, _, _ := solState.FindFqDestChainPDA(remoteChainSel, feeQuoterID) + routerRemoteStatePDA, _ := solState.FindDestChainStatePDA(remoteChainSel, ccipRouterID) + offRampRemoteStatePDA, _, _ := solState.FindOfframpSourceChainPDA(remoteChainSel, offRampID) + allowedOffRampRemotePDA, _ := solState.FindAllowedOfframpPDA(remoteChainSel, offRampID, ccipRouterID) lookUpTableEntries = append(lookUpTableEntries, - fqDestChainPDA, - routerDestChainPDA, - offRampSourceChainPDA, + fqRemoteChainPDA, + routerRemoteStatePDA, + offRampRemoteStatePDA, ) solRouter.SetProgramID(ccipRouterID) routerIx, err := solRouter.NewAddChainSelectorInstruction( remoteChainSel, update.RouterDestinationConfig, - routerDestChainPDA, + routerRemoteStatePDA, + s.SolChains[chainSel].RouterConfigPDA, + chain.DeployerKey.PublicKey(), + solana.SystemProgramID, + ).ValidateAndBuild() + if err != nil { + return fmt.Errorf("failed to generate instructions: %w", err) + } + + routerOfframpIx, err := solRouter.NewAddOfframpInstruction( + remoteChainSel, + offRampID, + allowedOffRampRemotePDA, s.SolChains[chainSel].RouterConfigPDA, chain.DeployerKey.PublicKey(), solana.SystemProgramID, @@ -174,7 +178,7 @@ func doAddRemoteChainToSolana( remoteChainSel, update.FeeQuoterDestinationConfig, s.SolChains[chainSel].FeeQuoterConfigPDA, - fqDestChainPDA, + fqRemoteChainPDA, chain.DeployerKey.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() @@ -190,37 +194,38 @@ func doAddRemoteChainToSolana( offRampIx, err := solOffRamp.NewAddSourceChainInstruction( remoteChainSel, validSourceChainConfig, - offRampSourceChainPDA, + offRampRemoteStatePDA, s.SolChains[chainSel].OffRampConfigPDA, chain.DeployerKey.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() + if err != nil { return fmt.Errorf("failed to generate instructions: %w", err) } - err = chain.Confirm([]solana.Instruction{routerIx, feeQuoterIx, offRampIx}) + err = chain.Confirm([]solana.Instruction{routerIx, routerOfframpIx, feeQuoterIx, offRampIx}) if err != nil { return fmt.Errorf("failed to confirm instructions: %w", err) } - tv := deployment.NewTypeAndVersion(cs.RemoteDest, deployment.Version1_0_0) + tv := deployment.NewTypeAndVersion(ccipChangeset.RemoteDest, deployment.Version1_0_0) remoteChainSelStr := strconv.FormatUint(remoteChainSel, 10) tv.AddLabel(remoteChainSelStr) - err = ab.Save(chainSel, routerDestChainPDA.String(), tv) + err = ab.Save(chainSel, routerRemoteStatePDA.String(), tv) if err != nil { return fmt.Errorf("failed to save dest chain state to address book: %w", err) } - tv = deployment.NewTypeAndVersion(cs.RemoteSource, deployment.Version1_0_0) + tv = deployment.NewTypeAndVersion(ccipChangeset.RemoteSource, deployment.Version1_0_0) tv.AddLabel(remoteChainSelStr) - err = ab.Save(chainSel, offRampSourceChainPDA.String(), tv) + err = ab.Save(chainSel, allowedOffRampRemotePDA.String(), tv) if err != nil { return fmt.Errorf("failed to save source chain state to address book: %w", err) } } - addressLookupTable, err := cs.FetchOfframpLookupTable(e.GetContext(), chain, offRampID) + addressLookupTable, err := ccipChangeset.FetchOfframpLookupTable(e.GetContext(), chain, offRampID) if err != nil { return fmt.Errorf("failed to get offramp reference addresses: %w", err) } diff --git a/deployment/ccip/changeset/solana/cs_billing.go b/deployment/ccip/changeset/solana/cs_billing.go index 2c68bea9a02..5eeffde3e2f 100644 --- a/deployment/ccip/changeset/solana/cs_billing.go +++ b/deployment/ccip/changeset/solana/cs_billing.go @@ -14,18 +14,17 @@ import ( ata "github.com/gagliardetto/solana-go/programs/associated-token-account" "github.com/smartcontractkit/chainlink/deployment" - cs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" ) -var _ deployment.ChangeSet[BillingTokenConfig] = AddBillingToken +var _ deployment.ChangeSet[BillingTokenConfig] = AddBillingTokenChangeset var _ deployment.ChangeSet[BillingTokenForRemoteChainConfig] = AddBillingTokenForRemoteChain // ADD BILLING TOKEN type BillingTokenConfig struct { - ChainSelector uint64 - TokenPubKey string - TokenProgramName string - Config solFeeQuoter.BillingTokenConfig + ChainSelector uint64 + TokenPubKey string + Config solFeeQuoter.BillingTokenConfig } func (cfg BillingTokenConfig) Validate(e deployment.Environment) error { @@ -33,16 +32,16 @@ func (cfg BillingTokenConfig) Validate(e deployment.Environment) error { if err := commonValidation(e, cfg.ChainSelector, tokenPubKey); err != nil { return err } - if _, err := GetTokenProgramID(cfg.TokenProgramName); err != nil { - return err - } chain := e.SolChains[cfg.ChainSelector] - state, _ := cs.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] if err := validateFeeQuoterConfig(chain, chainState); err != nil { return err } + if _, err := chainState.TokenToTokenProgram(tokenPubKey); err != nil { + return err + } // check if already setup billingConfigPDA, _, err := solState.FindFqBillingTokenConfigPDA(tokenPubKey, chainState.FeeQuoter) if err != nil { @@ -55,32 +54,23 @@ func (cfg BillingTokenConfig) Validate(e deployment.Environment) error { return nil } -func AddBillingToken(e deployment.Environment, cfg BillingTokenConfig) (deployment.ChangesetOutput, error) { - if err := cfg.Validate(e); err != nil { - return deployment.ChangesetOutput{}, err - } - chain, ok := e.SolChains[cfg.ChainSelector] - if !ok { - return deployment.ChangesetOutput{}, fmt.Errorf("chain selector %d not found in environment", cfg.ChainSelector) - } - state, _ := cs.LoadOnchainState(e) - chainState := state.SolChains[cfg.ChainSelector] - tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey) - // verified - tokenprogramID, _ := GetTokenProgramID(cfg.TokenProgramName) +func AddBillingToken( + e deployment.Environment, + chain deployment.SolChain, + chainState ccipChangeset.SolCCIPChainState, + billingConfig solFeeQuoter.BillingTokenConfig, +) error { + tokenPubKey := solana.MustPublicKeyFromBase58(billingConfig.Mint.String()) tokenBillingPDA, _, _ := solState.FindFqBillingTokenConfigPDA(tokenPubKey, chainState.FeeQuoter) - - // addressing errcheck in the next PR billingSignerPDA, _, _ := solState.FindFeeBillingSignerPDA(chainState.Router) - token2022Receiver, _, _ := solTokenUtil.FindAssociatedTokenAddress(tokenprogramID, tokenPubKey, billingSignerPDA) - - e.Logger.Infow("chainState.FeeQuoterConfigPDA", "feeQuoterConfigPDA", chainState.FeeQuoterConfigPDA.String()) - solFeeQuoter.SetProgramID(chainState.FeeQuoter) + tokenProgramID, _ := chainState.TokenToTokenProgram(tokenPubKey) + token2022Receiver, _, _ := solTokenUtil.FindAssociatedTokenAddress(tokenProgramID, tokenPubKey, billingSignerPDA) + feeQuoterConfigPDA, _, _ := solState.FindFqConfigPDA(chainState.FeeQuoter) ixConfig, cerr := solFeeQuoter.NewAddBillingTokenConfigInstruction( - cfg.Config, - chainState.FeeQuoterConfigPDA, + billingConfig, + feeQuoterConfigPDA, tokenBillingPDA, - tokenprogramID, + tokenProgramID, tokenPubKey, token2022Receiver, chain.DeployerKey.PublicKey(), // ccip admin @@ -89,15 +79,33 @@ func AddBillingToken(e deployment.Environment, cfg BillingTokenConfig) (deployme solana.SystemProgramID, ).ValidateAndBuild() if cerr != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("failed to generate instructions: %w", cerr) + return fmt.Errorf("failed to generate instructions: %w", cerr) } - instructions := []solana.Instruction{ixConfig} if err := chain.Confirm(instructions); err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm instructions: %w", err) + return fmt.Errorf("failed to confirm instructions: %w", err) } + return nil +} + +func AddBillingTokenChangeset(e deployment.Environment, cfg BillingTokenConfig) (deployment.ChangesetOutput, error) { + if err := cfg.Validate(e); err != nil { + return deployment.ChangesetOutput{}, err + } + chain := e.SolChains[cfg.ChainSelector] + state, _ := ccipChangeset.LoadOnchainState(e) + chainState := state.SolChains[cfg.ChainSelector] + + solFeeQuoter.SetProgramID(chainState.FeeQuoter) + + if err := AddBillingToken(e, chain, chainState, cfg.Config); err != nil { + return deployment.ChangesetOutput{}, err + } + + tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey) + tokenBillingPDA, _, _ := solState.FindFqBillingTokenConfigPDA(tokenPubKey, chainState.FeeQuoter) - addressLookupTable, err := cs.FetchOfframpLookupTable(e.GetContext(), chain, chainState.OffRamp) + addressLookupTable, err := ccipChangeset.FetchOfframpLookupTable(e.GetContext(), chain, chainState.OffRamp) if err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("failed to get offramp reference addresses: %w", err) } @@ -129,7 +137,7 @@ func (cfg BillingTokenForRemoteChainConfig) Validate(e deployment.Environment) e if err := commonValidation(e, cfg.ChainSelector, tokenPubKey); err != nil { return err } - state, _ := cs.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] chain := e.SolChains[cfg.ChainSelector] if err := validateFeeQuoterConfig(chain, chainState); err != nil { @@ -153,7 +161,7 @@ func AddBillingTokenForRemoteChain(e deployment.Environment, cfg BillingTokenFor } chain := e.SolChains[cfg.ChainSelector] - state, _ := cs.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey) remoteBillingPDA, _, _ := solState.FindFqPerChainPerTokenConfigPDA(cfg.RemoteChainSelector, tokenPubKey, chainState.FeeQuoter) @@ -175,7 +183,7 @@ func AddBillingTokenForRemoteChain(e deployment.Environment, cfg BillingTokenFor return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm instructions: %w", err) } - addressLookupTable, err := cs.FetchOfframpLookupTable(e.GetContext(), chain, chainState.OffRamp) + addressLookupTable, err := ccipChangeset.FetchOfframpLookupTable(e.GetContext(), chain, chainState.OffRamp) if err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("failed to get offramp reference addresses: %w", err) } diff --git a/deployment/ccip/changeset/solana/cs_chain_contracts.go b/deployment/ccip/changeset/solana/cs_chain_contracts.go index a7978504c06..db29b12642e 100644 --- a/deployment/ccip/changeset/solana/cs_chain_contracts.go +++ b/deployment/ccip/changeset/solana/cs_chain_contracts.go @@ -12,13 +12,13 @@ import ( solState "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state" "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6" ) var _ deployment.ChangeSet[v1_6.SetOCR3OffRampConfig] = SetOCR3ConfigSolana var _ deployment.ChangeSet[AddRemoteChainToSolanaConfig] = AddRemoteChainToSolana -var _ deployment.ChangeSet[BillingTokenConfig] = AddBillingToken +var _ deployment.ChangeSet[BillingTokenConfig] = AddBillingTokenChangeset var _ deployment.ChangeSet[BillingTokenForRemoteChainConfig] = AddBillingTokenForRemoteChain var _ deployment.ChangeSet[RegisterTokenAdminRegistryConfig] = RegisterTokenAdminRegistry var _ deployment.ChangeSet[TransferAdminRoleTokenAdminRegistryConfig] = TransferAdminRoleTokenAdminRegistry @@ -27,15 +27,15 @@ var _ deployment.ChangeSet[SetFeeAggregatorConfig] = SetFeeAggregator // HELPER FUNCTIONS // GetTokenProgramID returns the program ID for the given token program name -func GetTokenProgramID(programName string) (solana.PublicKey, error) { - tokenPrograms := map[string]solana.PublicKey{ - deployment.SPLTokens: solana.TokenProgramID, // not used yet - deployment.SPL2022Tokens: solana.Token2022ProgramID, +func GetTokenProgramID(programName deployment.ContractType) (solana.PublicKey, error) { + tokenPrograms := map[deployment.ContractType]solana.PublicKey{ + ccipChangeset.SPLTokens: solana.TokenProgramID, + ccipChangeset.SPL2022Tokens: solana.Token2022ProgramID, } programID, ok := tokenPrograms[programName] if !ok { - return solana.PublicKey{}, fmt.Errorf("invalid token program: %s. Must be one of: %s, %s", programName, deployment.SPLTokens, deployment.SPL2022Tokens) + return solana.PublicKey{}, fmt.Errorf("invalid token program: %s. Must be one of: %s, %s", programName, ccipChangeset.SPLTokens, ccipChangeset.SPL2022Tokens) } return programID, nil } @@ -45,7 +45,7 @@ func commonValidation(e deployment.Environment, selector uint64, tokenPubKey sol if !ok { return fmt.Errorf("chain selector %d not found in environment", selector) } - state, err := changeset.LoadOnchainState(e) + state, err := ccipChangeset.LoadOnchainState(e) if err != nil { return fmt.Errorf("failed to load onchain state: %w", err) } @@ -57,7 +57,9 @@ func commonValidation(e deployment.Environment, selector uint64, tokenPubKey sol return nil } exists := false - for _, token := range chainState.SPL2022Tokens { + allTokens := chainState.SPL2022Tokens + allTokens = append(allTokens, chainState.SPLTokens...) + for _, token := range allTokens { if token.Equals(tokenPubKey) { exists = true break @@ -69,7 +71,7 @@ func commonValidation(e deployment.Environment, selector uint64, tokenPubKey sol return nil } -func validateRouterConfig(chain deployment.SolChain, chainState changeset.SolCCIPChainState) error { +func validateRouterConfig(chain deployment.SolChain, chainState ccipChangeset.SolCCIPChainState) error { if chainState.Router.IsZero() { return fmt.Errorf("router not found in existing state, deploy the router first for chain %d", chain.Selector) } @@ -82,7 +84,7 @@ func validateRouterConfig(chain deployment.SolChain, chainState changeset.SolCCI return nil } -func validateFeeQuoterConfig(chain deployment.SolChain, chainState changeset.SolCCIPChainState) error { +func validateFeeQuoterConfig(chain deployment.SolChain, chainState ccipChangeset.SolCCIPChainState) error { if chainState.FeeQuoter.IsZero() { return fmt.Errorf("fee quoter not found in existing state, deploy the fee quoter first for chain %d", chain.Selector) } @@ -95,7 +97,7 @@ func validateFeeQuoterConfig(chain deployment.SolChain, chainState changeset.Sol return nil } -func validateOffRampConfig(chain deployment.SolChain, chainState changeset.SolCCIPChainState) error { +func validateOffRampConfig(chain deployment.SolChain, chainState ccipChangeset.SolCCIPChainState) error { if chainState.OffRamp.IsZero() { return fmt.Errorf("offramp not found in existing state, deploy the offramp first for chain %d", chain.Selector) } diff --git a/deployment/ccip/changeset/solana/cs_chain_contracts_test.go b/deployment/ccip/changeset/solana/cs_chain_contracts_test.go index a9ce0721c7a..5132bbec813 100644 --- a/deployment/ccip/changeset/solana/cs_chain_contracts_test.go +++ b/deployment/ccip/changeset/solana/cs_chain_contracts_test.go @@ -7,6 +7,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/stretchr/testify/require" + solBaseTokenPool "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/base_token_pool" solRouter "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" solFeeQuoter "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/fee_quoter" solTestTokenPool "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/test_token_pool" @@ -17,7 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - changeset_solana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" + ccipChangesetSolana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6" @@ -25,6 +26,25 @@ import ( commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" ) +func deployToken(t *testing.T, tenv deployment.Environment, solChain uint64) (deployment.Environment, solana.PublicKey, error) { + e, err := commonchangeset.Apply(t, tenv, nil, + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeploySolanaToken), + ccipChangesetSolana.DeploySolanaTokenConfig{ + ChainSelector: solChain, + TokenProgramName: ccipChangeset.SPL2022Tokens, + TokenDecimals: 9, + TokenSymbol: "TEST_TOKEN", + }, + ), + ) + require.NoError(t, err) + state, err := ccipChangeset.LoadOnchainStateSolana(e) + require.NoError(t, err) + tokenAddress := state.SolChains[solChain].SPL2022Tokens[0] + return e, tokenAddress, err +} + func TestAddRemoteChain(t *testing.T) { t.Parallel() ctx := testcontext.Get(t) @@ -54,10 +74,10 @@ func TestAddRemoteChain(t *testing.T) { }, ), commonchangeset.Configure( - deployment.CreateLegacyChangeSet(changeset_solana.AddRemoteChainToSolana), - changeset_solana.AddRemoteChainToSolanaConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.AddRemoteChainToSolana), + ccipChangesetSolana.AddRemoteChainToSolanaConfig{ ChainSelector: solChain, - UpdatesByChain: map[uint64]changeset_solana.RemoteChainConfigSolana{ + UpdatesByChain: map[uint64]ccipChangesetSolana.RemoteChainConfigSolana{ evmChain: { EnabledAsSource: true, RouterDestinationConfig: solRouter.DestChainConfig{}, @@ -108,87 +128,87 @@ func TestAddTokenPool(t *testing.T) { evmChain := tenv.Env.AllChainSelectors()[0] solChain := tenv.Env.AllChainSelectorsSolana()[0] - - e, err := commonchangeset.Apply(t, tenv.Env, nil, - commonchangeset.Configure( - deployment.CreateLegacyChangeSet(changeset_solana.DeploySolanaToken), - changeset_solana.DeploySolanaTokenConfig{ - ChainSelector: solChain, - TokenProgramName: deployment.SPL2022Tokens, - TokenDecimals: 9, - }, - ), - ) + e, newTokenAddress, err := deployToken(t, tenv.Env, solChain) require.NoError(t, err) - state, err := ccipChangeset.LoadOnchainStateSolana(e) require.NoError(t, err) - newTokenAddress := state.SolChains[solChain].SPL2022Tokens[0] - - remoteConfig := solTestTokenPool.RemoteConfig{ + remoteConfig := solBaseTokenPool.RemoteConfig{ PoolAddresses: []solTestTokenPool.RemoteAddress{{Address: []byte{1, 2, 3}}}, TokenAddress: solTestTokenPool.RemoteAddress{Address: []byte{4, 5, 6}}, Decimals: 9, } - inboundConfig := solTestTokenPool.RateLimitConfig{ + inboundConfig := solBaseTokenPool.RateLimitConfig{ Enabled: true, Capacity: uint64(1000), Rate: 1, } - outboundConfig := solTestTokenPool.RateLimitConfig{ + outboundConfig := solBaseTokenPool.RateLimitConfig{ Enabled: false, Capacity: 0, Rate: 0, } - tokenMap := map[string]solana.PublicKey{ - deployment.SPL2022Tokens: newTokenAddress, - deployment.SPLTokens: state.SolChains[solChain].WSOL, + tokenMap := map[deployment.ContractType]solana.PublicKey{ + ccipChangeset.SPL2022Tokens: newTokenAddress, + ccipChangeset.SPLTokens: state.SolChains[solChain].WSOL, } - for tokenProgramName, tokenAddress := range tokenMap { - e, err = commonchangeset.Apply(t, e, nil, - commonchangeset.Configure( - deployment.CreateLegacyChangeSet(changeset_solana.AddTokenPool), - changeset_solana.TokenPoolConfig{ - ChainSelector: solChain, - TokenPubKey: tokenAddress.String(), - TokenProgramName: tokenProgramName, - PoolType: solTestTokenPool.LockAndRelease_PoolType, - // this works for testing, but if we really want some other authority we need to pass in a private key for signing purposes - Authority: e.SolChains[solChain].DeployerKey.PublicKey().String(), - }, - ), - commonchangeset.Configure( - deployment.CreateLegacyChangeSet(changeset_solana.SetupTokenPoolForRemoteChain), - changeset_solana.RemoteChainTokenPoolConfig{ - SolChainSelector: solChain, - RemoteChainSelector: evmChain, - SolTokenPubKey: tokenAddress.String(), - RemoteConfig: remoteConfig, - InboundRateLimit: inboundConfig, - OutboundRateLimit: outboundConfig, - }, - ), - ) - require.NoError(t, err) - - // test AddTokenPool results - poolConfigPDA, err := solTokenUtil.TokenPoolConfigAddress(tokenAddress, state.SolChains[solChain].TokenPool) - require.NoError(t, err) - var configAccount solTestTokenPool.State - err = e.SolChains[solChain].GetAccountDataBorshInto(ctx, poolConfigPDA, &configAccount) - require.NoError(t, err) - require.Equal(t, solTestTokenPool.LockAndRelease_PoolType, configAccount.PoolType) - require.Equal(t, tokenAddress, configAccount.Config.Mint) - - // test SetupTokenPoolForRemoteChain results - remoteChainConfigPDA, _, _ := solTokenUtil.TokenPoolChainConfigPDA(evmChain, tokenAddress, state.SolChains[solChain].TokenPool) - var remoteChainConfigAccount solTestTokenPool.ChainConfig - err = e.SolChains[solChain].GetAccountDataBorshInto(ctx, remoteChainConfigPDA, &remoteChainConfigAccount) - require.NoError(t, err) - require.Equal(t, uint8(9), remoteChainConfigAccount.Base.Remote.Decimals) + type poolTestType struct { + poolType solTestTokenPool.PoolType + poolAddress solana.PublicKey + } + testCases := []poolTestType{ + { + poolType: solTestTokenPool.BurnAndMint_PoolType, + poolAddress: state.SolChains[solChain].BurnMintTokenPool, + }, + { + poolType: solTestTokenPool.LockAndRelease_PoolType, + poolAddress: state.SolChains[solChain].LockReleaseTokenPool, + }, } + for _, testCase := range testCases { + for _, tokenAddress := range tokenMap { + e, err = commonchangeset.Apply(t, e, nil, + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(ccipChangesetSolana.AddTokenPool), + ccipChangesetSolana.TokenPoolConfig{ + ChainSelector: solChain, + TokenPubKey: tokenAddress.String(), + PoolType: testCase.poolType, + // this works for testing, but if we really want some other authority we need to pass in a private key for signing purposes + Authority: tenv.Env.SolChains[solChain].DeployerKey.PublicKey().String(), + }, + ), + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(ccipChangesetSolana.SetupTokenPoolForRemoteChain), + ccipChangesetSolana.RemoteChainTokenPoolConfig{ + SolChainSelector: solChain, + RemoteChainSelector: evmChain, + SolTokenPubKey: tokenAddress.String(), + RemoteConfig: remoteConfig, + InboundRateLimit: inboundConfig, + OutboundRateLimit: outboundConfig, + PoolType: testCase.poolType, + }, + ), + ) + require.NoError(t, err) + // test AddTokenPool results + configAccount := solTestTokenPool.State{} + poolConfigPDA, _ := solTokenUtil.TokenPoolConfigAddress(tokenAddress, testCase.poolAddress) + err = e.SolChains[solChain].GetAccountDataBorshInto(ctx, poolConfigPDA, &configAccount) + require.NoError(t, err) + require.Equal(t, tokenAddress, configAccount.Config.Mint) + // test SetupTokenPoolForRemoteChain results + remoteChainConfigPDA, _, _ := solTokenUtil.TokenPoolChainConfigPDA(evmChain, tokenAddress, testCase.poolAddress) + var remoteChainConfigAccount solTestTokenPool.ChainConfig + err = e.SolChains[solChain].GetAccountDataBorshInto(ctx, remoteChainConfigPDA, &remoteChainConfigAccount) + require.NoError(t, err) + require.Equal(t, uint8(9), remoteChainConfigAccount.Base.Remote.Decimals) + } + } + } func TestBilling(t *testing.T) { @@ -199,21 +219,10 @@ func TestBilling(t *testing.T) { evmChain := tenv.Env.AllChainSelectors()[0] solChain := tenv.Env.AllChainSelectorsSolana()[0] - e, err := commonchangeset.Apply(t, tenv.Env, nil, - commonchangeset.Configure( - deployment.CreateLegacyChangeSet(changeset_solana.DeploySolanaToken), - changeset_solana.DeploySolanaTokenConfig{ - ChainSelector: solChain, - TokenProgramName: deployment.SPL2022Tokens, - TokenDecimals: 9, - }, - ), - ) + e, tokenAddress, err := deployToken(t, tenv.Env, solChain) require.NoError(t, err) - state, err := ccipChangeset.LoadOnchainStateSolana(e) require.NoError(t, err) - tokenAddress := state.SolChains[solChain].SPL2022Tokens[0] validTimestamp := int64(100) value := [28]uint8{} bigNum, ok := new(big.Int).SetString("19816680000000000000", 10) @@ -221,11 +230,10 @@ func TestBilling(t *testing.T) { bigNum.FillBytes(value[:]) e, err = commonchangeset.Apply(t, e, nil, commonchangeset.Configure( - deployment.CreateLegacyChangeSet(changeset_solana.AddBillingToken), - changeset_solana.BillingTokenConfig{ - ChainSelector: solChain, - TokenPubKey: tokenAddress.String(), - TokenProgramName: deployment.SPL2022Tokens, + deployment.CreateLegacyChangeSet(ccipChangesetSolana.AddBillingTokenChangeset), + ccipChangesetSolana.BillingTokenConfig{ + ChainSelector: solChain, + TokenPubKey: tokenAddress.String(), Config: solFeeQuoter.BillingTokenConfig{ Enabled: true, Mint: tokenAddress, @@ -238,8 +246,8 @@ func TestBilling(t *testing.T) { }, ), commonchangeset.Configure( - deployment.CreateLegacyChangeSet(changeset_solana.AddBillingTokenForRemoteChain), - changeset_solana.BillingTokenForRemoteChainConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.AddBillingTokenForRemoteChain), + ccipChangesetSolana.BillingTokenForRemoteChainConfig{ ChainSelector: solChain, RemoteChainSelector: evmChain, TokenPubKey: tokenAddress.String(), @@ -275,48 +283,34 @@ func TestTokenAdminRegistry(t *testing.T) { t.Parallel() ctx := testcontext.Get(t) tenv, _ := testhelpers.NewMemoryEnvironment(t, testhelpers.WithSolChains(1)) - solChain := tenv.Env.AllChainSelectorsSolana()[0] - - e, err := commonchangeset.Apply(t, tenv.Env, nil, - commonchangeset.Configure( - deployment.CreateLegacyChangeSet(changeset_solana.DeploySolanaToken), - changeset_solana.DeploySolanaTokenConfig{ - ChainSelector: solChain, - TokenProgramName: deployment.SPL2022Tokens, - TokenDecimals: 9, - }, - ), - ) + e, tokenAddress, err := deployToken(t, tenv.Env, solChain) require.NoError(t, err) - state, err := ccipChangeset.LoadOnchainStateSolana(e) require.NoError(t, err) - tokenAddress := state.SolChains[solChain].SPL2022Tokens[0] - tokenAdminRegistryAdminPrivKey, _ := solana.NewRandomPrivateKey() - - // We have to do run the ViaOwnerInstruction testcase for linkToken as we already register a PDA for tokenAddress in the previous testcase linkTokenAddress := state.SolChains[solChain].LinkToken + tokenAdminRegistryAdminPrivKey, _ := solana.NewRandomPrivateKey() + e, err = commonchangeset.Apply(t, e, nil, commonchangeset.Configure( // register token admin registry for tokenAddress via admin instruction - deployment.CreateLegacyChangeSet(changeset_solana.RegisterTokenAdminRegistry), - changeset_solana.RegisterTokenAdminRegistryConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.RegisterTokenAdminRegistry), + ccipChangesetSolana.RegisterTokenAdminRegistryConfig{ ChainSelector: solChain, TokenPubKey: tokenAddress.String(), TokenAdminRegistryAdmin: tokenAdminRegistryAdminPrivKey.PublicKey().String(), - RegisterType: changeset_solana.ViaGetCcipAdminInstruction, + RegisterType: ccipChangesetSolana.ViaGetCcipAdminInstruction, }, ), commonchangeset.Configure( // register token admin registry for linkToken via owner instruction - deployment.CreateLegacyChangeSet(changeset_solana.RegisterTokenAdminRegistry), - changeset_solana.RegisterTokenAdminRegistryConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.RegisterTokenAdminRegistry), + ccipChangesetSolana.RegisterTokenAdminRegistryConfig{ ChainSelector: solChain, TokenPubKey: linkTokenAddress.String(), TokenAdminRegistryAdmin: tokenAdminRegistryAdminPrivKey.PublicKey().String(), - RegisterType: changeset_solana.ViaOwnerInstruction, + RegisterType: ccipChangesetSolana.ViaOwnerInstruction, }, ), ) @@ -339,8 +333,8 @@ func TestTokenAdminRegistry(t *testing.T) { e, err = commonchangeset.Apply(t, e, nil, commonchangeset.Configure( // accept admin role for tokenAddress - deployment.CreateLegacyChangeSet(changeset_solana.AcceptAdminRoleTokenAdminRegistry), - changeset_solana.AcceptAdminRoleTokenAdminRegistryConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.AcceptAdminRoleTokenAdminRegistry), + ccipChangesetSolana.AcceptAdminRoleTokenAdminRegistryConfig{ ChainSelector: solChain, TokenPubKey: tokenAddress.String(), NewRegistryAdminPrivateKey: tokenAdminRegistryAdminPrivKey.String(), @@ -358,8 +352,8 @@ func TestTokenAdminRegistry(t *testing.T) { e, err = commonchangeset.Apply(t, e, nil, commonchangeset.Configure( // transfer admin role for tokenAddress - deployment.CreateLegacyChangeSet(changeset_solana.TransferAdminRoleTokenAdminRegistry), - changeset_solana.TransferAdminRoleTokenAdminRegistryConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.TransferAdminRoleTokenAdminRegistry), + ccipChangesetSolana.TransferAdminRoleTokenAdminRegistryConfig{ ChainSelector: solChain, TokenPubKey: tokenAddress.String(), NewRegistryAdminPublicKey: newTokenAdminRegistryAdminPrivKey.PublicKey().String(), @@ -377,40 +371,22 @@ func TestPoolLookupTable(t *testing.T) { t.Parallel() ctx := testcontext.Get(t) tenv, _ := testhelpers.NewMemoryEnvironment(t, testhelpers.WithSolChains(1)) - solChain := tenv.Env.AllChainSelectorsSolana()[0] - e, err := commonchangeset.Apply(t, tenv.Env, nil, - commonchangeset.Configure( - // deploy token - deployment.CreateLegacyChangeSet(changeset_solana.DeploySolanaToken), - changeset_solana.DeploySolanaTokenConfig{ - ChainSelector: solChain, - TokenProgramName: deployment.SPL2022Tokens, - TokenDecimals: 9, - }, - ), - ) - require.NoError(t, err) - - state, err := ccipChangeset.LoadOnchainStateSolana(e) + e, tokenAddress, err := deployToken(t, tenv.Env, solChain) require.NoError(t, err) - tokenAddress := state.SolChains[solChain].SPL2022Tokens[0] - e, err = commonchangeset.Apply(t, e, nil, commonchangeset.Configure( // add token pool lookup table - deployment.CreateLegacyChangeSet(changeset_solana.AddTokenPoolLookupTable), - changeset_solana.TokenPoolLookupTableConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.AddTokenPoolLookupTable), + ccipChangesetSolana.TokenPoolLookupTableConfig{ ChainSelector: solChain, TokenPubKey: tokenAddress.String(), - TokenProgram: deployment.SPL2022Tokens, }, ), ) require.NoError(t, err) - - state, err = ccipChangeset.LoadOnchainStateSolana(e) + state, err := ccipChangeset.LoadOnchainStateSolana(e) require.NoError(t, err) lookupTablePubKey := state.SolChains[solChain].TokenPoolLookupTable[tokenAddress] @@ -424,18 +400,18 @@ func TestPoolLookupTable(t *testing.T) { e, err = commonchangeset.Apply(t, e, nil, commonchangeset.Configure( // register token admin registry for linkToken via owner instruction - deployment.CreateLegacyChangeSet(changeset_solana.RegisterTokenAdminRegistry), - changeset_solana.RegisterTokenAdminRegistryConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.RegisterTokenAdminRegistry), + ccipChangesetSolana.RegisterTokenAdminRegistryConfig{ ChainSelector: solChain, TokenPubKey: tokenAddress.String(), TokenAdminRegistryAdmin: tokenAdminRegistryAdminPrivKey.PublicKey().String(), - RegisterType: changeset_solana.ViaGetCcipAdminInstruction, + RegisterType: ccipChangesetSolana.ViaGetCcipAdminInstruction, }, ), commonchangeset.Configure( // accept admin role for tokenAddress - deployment.CreateLegacyChangeSet(changeset_solana.AcceptAdminRoleTokenAdminRegistry), - changeset_solana.AcceptAdminRoleTokenAdminRegistryConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.AcceptAdminRoleTokenAdminRegistry), + ccipChangesetSolana.AcceptAdminRoleTokenAdminRegistryConfig{ ChainSelector: solChain, TokenPubKey: tokenAddress.String(), NewRegistryAdminPrivateKey: tokenAdminRegistryAdminPrivKey.String(), @@ -443,11 +419,10 @@ func TestPoolLookupTable(t *testing.T) { ), commonchangeset.Configure( // set pool -> this updates tokenAdminRegistryPDA, hence above changeset is required - deployment.CreateLegacyChangeSet(changeset_solana.SetPool), - changeset_solana.SetPoolConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.SetPool), + ccipChangesetSolana.SetPoolConfig{ ChainSelector: solChain, TokenPubKey: tokenAddress.String(), - PoolLookupTable: lookupTablePubKey.String(), TokenAdminRegistryAdminPrivateKey: tokenAdminRegistryAdminPrivKey.String(), WritableIndexes: []uint8{3, 4, 7}, }, diff --git a/deployment/ccip/changeset/solana/cs_deploy_chain.go b/deployment/ccip/changeset/solana/cs_deploy_chain.go index 78aa985ec5f..95cbb45ca0a 100644 --- a/deployment/ccip/changeset/solana/cs_deploy_chain.go +++ b/deployment/ccip/changeset/solana/cs_deploy_chain.go @@ -16,7 +16,7 @@ import ( mcmsTypes "github.com/smartcontractkit/mcms/types" "github.com/smartcontractkit/chainlink/deployment" - cs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6" "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" @@ -36,18 +36,33 @@ const ( RouterProgramName = "ccip_router" OffRampProgramName = "ccip_offramp" FeeQuoterProgramName = "fee_quoter" - TokenPoolProgramName = "test_token_pool" + BurnMintTokenPool = "example_burnmint_token_pool" + LockReleaseTokenPool = "example_lockrelease_token_pool" ) -var _ deployment.ChangeSet[DeployChainContractsConfigSolana] = DeployChainContractsChangesetSolana +var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContractsChangeset -type DeployChainContractsConfigSolana struct { - DeployChainContractsConfig v1_6.DeployChainContractsConfig - UpgradeConfig UpgradeConfigSolana - NewUpgradeAuthority *solana.PublicKey // if set, sets router and fee quoter upgrade authority +type DeployChainContractsConfig struct { + HomeChainSelector uint64 + ContractParamsPerChain map[uint64]ChainContractParams + UpgradeConfig UpgradeConfig + NewUpgradeAuthority *solana.PublicKey // if set, sets router and fee quoter upgrade authority } -type UpgradeConfigSolana struct { +type ChainContractParams struct { + FeeQuoterParams FeeQuoterParams + OffRampParams OffRampParams +} + +type FeeQuoterParams struct { + DefaultMaxFeeJuelsPerMsg solBinary.Uint128 + BillingConfig []solFeeQuoter.BillingTokenConfig +} + +type OffRampParams struct { + EnableExecutionAfter int64 +} +type UpgradeConfig struct { NewFeeQuoterVersion *semver.Version NewRouterVersion *semver.Version // Offramp is redeployed with the existing deployer key while the other programs are upgraded in place @@ -55,10 +70,10 @@ type UpgradeConfigSolana struct { // SpillAddress and UpgradeAuthority must be set SpillAddress solana.PublicKey UpgradeAuthority solana.PublicKey - MCMS *cs.MCMSConfig + MCMS *ccipChangeset.MCMSConfig } -func (cfg UpgradeConfigSolana) Validate(e deployment.Environment, chainSelector uint64) error { +func (cfg UpgradeConfig) Validate(e deployment.Environment, chainSelector uint64) error { if cfg.NewFeeQuoterVersion == nil && cfg.NewRouterVersion == nil && cfg.NewOffRampVersion == nil { return nil } @@ -73,13 +88,25 @@ func (cfg UpgradeConfigSolana) Validate(e deployment.Environment, chainSelector return ValidateMCMSConfig(e, chainSelector, cfg.MCMS) } -func DeployChainContractsChangesetSolana(e deployment.Environment, config DeployChainContractsConfigSolana) (deployment.ChangesetOutput, error) { - c := config.DeployChainContractsConfig +func (c DeployChainContractsConfig) Validate() error { + if err := deployment.IsValidChainSelector(c.HomeChainSelector); err != nil { + return fmt.Errorf("invalid home chain selector: %d - %w", c.HomeChainSelector, err) + } + for cs := range c.ContractParamsPerChain { + if err := deployment.IsValidChainSelector(cs); err != nil { + return fmt.Errorf("invalid chain selector: %d - %w", cs, err) + } + } + return nil +} + +func DeployChainContractsChangeset(e deployment.Environment, c DeployChainContractsConfig) (deployment.ChangesetOutput, error) { + // c := config.DeployChainContractsConfig if err := c.Validate(); err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("invalid DeployChainContractsConfig: %w", err) } newAddresses := deployment.NewMemoryAddressBook() - existingState, err := cs.LoadOnchainState(e) + existingState, err := ccipChangeset.LoadOnchainState(e) if err != nil { e.Logger.Errorw("Failed to load existing onchain state", "err", err) return deployment.ChangesetOutput{}, err @@ -107,7 +134,7 @@ func DeployChainContractsChangesetSolana(e deployment.Environment, config Deploy if existingState.SolChains[chainSel].LinkToken.IsZero() { return deployment.ChangesetOutput{}, fmt.Errorf("fee tokens not found for chain %d", chainSel) } - if err := config.UpgradeConfig.Validate(e, chainSel); err != nil { + if err := c.UpgradeConfig.Validate(e, chainSel); err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("invalid UpgradeConfig: %w", err) } addresses, _ := e.ExistingAddresses.AddressesForChain(chainSel) @@ -120,7 +147,7 @@ func DeployChainContractsChangesetSolana(e deployment.Environment, config Deploy proposers[chainSel] = mcmsSolana.ContractAddress(mcmState.McmProgram, mcmsSolana.PDASeed(mcmState.ProposerMcmSeed)) inspectors[chainSel] = mcmsSolana.NewInspector(chain.Client) - mcmsTxs, err := deployChainContractsSolana(e, chain, newAddresses, config) + mcmsTxs, err := deployChainContractsSolana(e, chain, newAddresses, c) if err != nil { e.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "newAddresses", newAddresses) return deployment.ChangesetOutput{}, err @@ -134,7 +161,7 @@ func DeployChainContractsChangesetSolana(e deployment.Environment, config Deploy } } - if config.UpgradeConfig.MCMS != nil { + if c.UpgradeConfig.MCMS != nil { proposal, err := proposalutils.BuildProposalFromBatchesV2( e.GetContext(), timelocks, @@ -142,7 +169,7 @@ func DeployChainContractsChangesetSolana(e deployment.Environment, config Deploy inspectors, batches, "proposal to upgrade CCIP contracts", - config.UpgradeConfig.MCMS.MinDelay) + c.UpgradeConfig.MCMS.MinDelay) if err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("failed to build proposal: %w", err) } @@ -240,6 +267,7 @@ func initializeFeeQuoter( linkTokenAddress solana.PublicKey, feeQuoterAddress solana.PublicKey, offRampAddress solana.PublicKey, + params FeeQuoterParams, ) error { e.Logger.Debugw("Initializing fee quoter", "chain", chain.String(), "feeQuoterAddress", feeQuoterAddress.String()) programData, err := solProgramData(e, chain, feeQuoterAddress) @@ -250,7 +278,7 @@ func initializeFeeQuoter( instruction, err := solFeeQuoter.NewInitializeInstruction( linkTokenAddress, - deployment.SolDefaultMaxFeeJuelsPerMsg, + params.DefaultMaxFeeJuelsPerMsg, ccipRouterProgram, feeQuoterConfigPDA, chain.DeployerKey.PublicKey(), @@ -258,6 +286,9 @@ func initializeFeeQuoter( feeQuoterAddress, programData.Address, ).ValidateAndBuild() + if err != nil { + return fmt.Errorf("failed to build instruction: %w", err) + } offRampBillingSignerPDA, _, _ := solState.FindOfframpBillingSignerPDA(offRampAddress) fqAllowedPriceUpdaterOfframpPDA, _, _ := solState.FindFqAllowedPriceUpdaterPDA(offRampBillingSignerPDA, feeQuoterAddress) @@ -287,6 +318,7 @@ func initializeOffRamp( feeQuoterAddress solana.PublicKey, offRampAddress solana.PublicKey, addressLookupTable solana.PublicKey, + params OffRampParams, ) error { e.Logger.Debugw("Initializing offRamp", "chain", chain.String(), "offRampAddress", offRampAddress.String()) programData, err := solProgramData(e, chain, offRampAddress) @@ -319,7 +351,7 @@ func initializeOffRamp( initConfigIx, err := solOffRamp.NewInitializeConfigInstruction( chain.Selector, - deployment.EnableExecutionAfter, + params.EnableExecutionAfter, offRampConfigPDA, chain.DeployerKey.PublicKey(), solana.SystemProgramID, @@ -341,11 +373,11 @@ func deployChainContractsSolana( e deployment.Environment, chain deployment.SolChain, ab deployment.AddressBook, - config DeployChainContractsConfigSolana, + config DeployChainContractsConfig, ) ([]mcmsTypes.Transaction, error) { // we may need to gather instructions and submit them as part of MCMS ixns := make([]mcmsTypes.Transaction, 0) - state, err := cs.LoadOnchainStateSolana(e) + state, err := ccipChangeset.LoadOnchainStateSolana(e) if err != nil { e.Logger.Errorw("Failed to load existing onchain state", "err", err) return ixns, err @@ -358,6 +390,8 @@ func deployChainContractsSolana( return ixns, fmt.Errorf("failed to get link token address for chain %s", chain.String()) } + params := config.ContractParamsPerChain[chain.Selector] + // FEE QUOTER DEPLOY var feeQuoterAddress solana.PublicKey //nolint:gocritic // this is a false positive, we need to check if the address is zero @@ -412,10 +446,10 @@ func deployChainContractsSolana( upgradeTx, err := mcmsSolana.NewTransaction( solana.BPFLoaderUpgradeableProgramID.String(), upgradeData, - big.NewInt(0), // e.g. value - upgradeIxn.Accounts(), // pass along needed accounts - string(cs.FeeQuoter), // some string identifying the target - []string{}, // any relevant metadata + big.NewInt(0), // e.g. value + upgradeIxn.Accounts(), // pass along needed accounts + string(ccipChangeset.FeeQuoter), // some string identifying the target + []string{}, // any relevant metadata ) if err != nil { return ixns, fmt.Errorf("failed to create upgrade transaction: %w", err) @@ -427,10 +461,10 @@ func deployChainContractsSolana( closeTx, err := mcmsSolana.NewTransaction( solana.BPFLoaderUpgradeableProgramID.String(), closeData, - big.NewInt(0), // e.g. value - closeIxn.Accounts(), // pass along needed accounts - string(cs.FeeQuoter), // some string identifying the target - []string{}, // any relevant metadata + big.NewInt(0), // e.g. value + closeIxn.Accounts(), // pass along needed accounts + string(ccipChangeset.FeeQuoter), // some string identifying the target + []string{}, // any relevant metadata ) if err != nil { return ixns, fmt.Errorf("failed to create close transaction: %w", err) @@ -443,10 +477,10 @@ func deployChainContractsSolana( extendTx, err := mcmsSolana.NewTransaction( solana.BPFLoaderUpgradeableProgramID.String(), extendData, - big.NewInt(0), // e.g. value - extendIxn.Accounts(), // pass along needed accounts - string(cs.FeeQuoter), // some string identifying the target - []string{}, // any relevant metadata + big.NewInt(0), // e.g. value + extendIxn.Accounts(), // pass along needed accounts + string(ccipChangeset.FeeQuoter), // some string identifying the target + []string{}, // any relevant metadata ) if err != nil { return ixns, fmt.Errorf("failed to create extend transaction: %w", err) @@ -515,10 +549,10 @@ func deployChainContractsSolana( upgradeTx, err := mcmsSolana.NewTransaction( solana.BPFLoaderUpgradeableProgramID.String(), upgradeData, - big.NewInt(0), // e.g. value - upgradeIxn.Accounts(), // pass along needed accounts - string(cs.Router), // some string identifying the target - []string{}, // any relevant metadata + big.NewInt(0), // e.g. value + upgradeIxn.Accounts(), // pass along needed accounts + string(ccipChangeset.Router), // some string identifying the target + []string{}, // any relevant metadata ) if err != nil { return ixns, fmt.Errorf("failed to create upgrade transaction: %w", err) @@ -530,10 +564,10 @@ func deployChainContractsSolana( closeTx, err := mcmsSolana.NewTransaction( solana.BPFLoaderUpgradeableProgramID.String(), closeData, - big.NewInt(0), // e.g. value - closeIxn.Accounts(), // pass along needed accounts - string(cs.Router), // some string identifying the target - []string{}, // any relevant metadata + big.NewInt(0), // e.g. value + closeIxn.Accounts(), // pass along needed accounts + string(ccipChangeset.Router), // some string identifying the target + []string{}, // any relevant metadata ) if err != nil { return ixns, fmt.Errorf("failed to create close transaction: %w", err) @@ -546,10 +580,10 @@ func deployChainContractsSolana( extendTx, err := mcmsSolana.NewTransaction( solana.BPFLoaderUpgradeableProgramID.String(), extendData, - big.NewInt(0), // e.g. value - extendIxn.Accounts(), // pass along needed accounts - string(cs.Router), // some string identifying the target - []string{}, // any relevant metadata + big.NewInt(0), // e.g. value + extendIxn.Accounts(), // pass along needed accounts + string(ccipChangeset.Router), // some string identifying the target + []string{}, // any relevant metadata ) if err != nil { return ixns, fmt.Errorf("failed to create extend transaction: %w", err) @@ -578,12 +612,12 @@ func deployChainContractsSolana( return ixns, fmt.Errorf("failed to deploy program: %w", err) } } else if config.UpgradeConfig.NewOffRampVersion != nil { - tv := deployment.NewTypeAndVersion(cs.OffRamp, *config.UpgradeConfig.NewOffRampVersion) + tv := deployment.NewTypeAndVersion(ccipChangeset.OffRamp, *config.UpgradeConfig.NewOffRampVersion) existingAddresses, err := e.ExistingAddresses.AddressesForChain(chain.Selector) if err != nil { return ixns, fmt.Errorf("failed to get existing addresses: %w", err) } - offRampAddress = cs.FindSolanaAddress(tv, existingAddresses) + offRampAddress = ccipChangeset.FindSolanaAddress(tv, existingAddresses) if offRampAddress.IsZero() { // deploy offramp, not upgraded in place so upgrade is false offRampAddress, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, OffRampProgramName, *config.UpgradeConfig.NewOffRampVersion, false) @@ -613,10 +647,10 @@ func deployChainContractsSolana( priceUpdaterTx, err := mcmsSolana.NewTransaction( feeQuoterAddress.String(), priceUpdaterData, - big.NewInt(0), // e.g. value - priceUpdaterix.Accounts(), // pass along needed accounts - string(cs.OffRamp), // some string identifying the target - []string{}, // any relevant metadata + big.NewInt(0), // e.g. value + priceUpdaterix.Accounts(), // pass along needed accounts + string(ccipChangeset.OffRamp), // some string identifying the target + []string{}, // any relevant metadata ) if err != nil { return ixns, fmt.Errorf("failed to create price updater transaction: %w", err) @@ -633,7 +667,7 @@ func deployChainContractsSolana( feeQuoterConfigPDA, _, _ := solState.FindFqConfigPDA(feeQuoterAddress) err = chain.GetAccountDataBorshInto(e.GetContext(), feeQuoterConfigPDA, &fqConfig) if err != nil { - if err2 := initializeFeeQuoter(e, chain, ccipRouterProgram, chainState.LinkToken, feeQuoterAddress, offRampAddress); err2 != nil { + if err2 := initializeFeeQuoter(e, chain, ccipRouterProgram, chainState.LinkToken, feeQuoterAddress, offRampAddress, params.FeeQuoterParams); err2 != nil { return ixns, err2 } } else { @@ -675,7 +709,7 @@ func deployChainContractsSolana( if err2 != nil { return ixns, fmt.Errorf("failed to create address lookup table: %w", err) } - if err2 := initializeOffRamp(e, chain, ccipRouterProgram, feeQuoterAddress, offRampAddress, table); err2 != nil { + if err2 := initializeOffRamp(e, chain, ccipRouterProgram, feeQuoterAddress, offRampAddress, table, params.OffRampParams); err2 != nil { return ixns, err2 } // Initializing a new offramp means we need a new lookup table and need to fully populate it @@ -696,29 +730,48 @@ func deployChainContractsSolana( e.Logger.Infow("Offramp already initialized, skipping initialization", "chain", chain.String()) } - // TOKEN POOL DEPLOY - var tokenPoolProgram solana.PublicKey - if chainState.TokenPool.IsZero() { - // TODO: there should be two token pools deployed one of each type (lock/burn) - // separate token pools are not ready yet - tokenPoolProgram, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, TokenPoolProgramName, deployment.Version1_0_0, false) + var burnMintTokenPool solana.PublicKey + if chainState.BurnMintTokenPool.IsZero() { + burnMintTokenPool, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, BurnMintTokenPool, deployment.Version1_0_0, false) + if err != nil { + return ixns, fmt.Errorf("failed to deploy program: %w", err) + } + needTokenPoolinLookupTable = true + } else { + e.Logger.Infow("Using existing burn mint token pool", "addr", chainState.BurnMintTokenPool.String()) + burnMintTokenPool = chainState.BurnMintTokenPool + } + + var lockReleaseTokenPool solana.PublicKey + if chainState.LockReleaseTokenPool.IsZero() { + lockReleaseTokenPool, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, LockReleaseTokenPool, deployment.Version1_0_0, false) if err != nil { return ixns, fmt.Errorf("failed to deploy program: %w", err) } needTokenPoolinLookupTable = true } else { - e.Logger.Infow("Using existing token pool", "addr", chainState.TokenPool.String()) - tokenPoolProgram = chainState.TokenPool + e.Logger.Infow("Using existing lock release token pool", "addr", chainState.LockReleaseTokenPool.String()) + lockReleaseTokenPool = chainState.LockReleaseTokenPool + } + + for _, billingConfig := range params.FeeQuoterParams.BillingConfig { + if err := AddBillingToken( + e, chain, chainState, billingConfig, + ); err != nil { + return ixns, err + } } if needFQinLookupTable { linkFqBillingConfigPDA, _, _ := solState.FindFqBillingTokenConfigPDA(chainState.LinkToken, feeQuoterAddress) + wsolFqBillingConfigPDA, _, _ := solState.FindFqBillingTokenConfigPDA(chainState.WSOL, feeQuoterAddress) feeQuoterConfigPDA, _, _ := solState.FindFqConfigPDA(feeQuoterAddress) lookupTableKeys = append(lookupTableKeys, []solana.PublicKey{ // fee quoter feeQuoterConfigPDA, feeQuoterAddress, linkFqBillingConfigPDA, + wsolFqBillingConfigPDA, }...) } @@ -740,12 +793,13 @@ func deployChainContractsSolana( if needTokenPoolinLookupTable { lookupTableKeys = append(lookupTableKeys, []solana.PublicKey{ // token pools - tokenPoolProgram, + burnMintTokenPool, + lockReleaseTokenPool, }...) } if len(lookupTableKeys) > 0 { - addressLookupTable, err := cs.FetchOfframpLookupTable(e.GetContext(), chain, offRampAddress) + addressLookupTable, err := ccipChangeset.FetchOfframpLookupTable(e.GetContext(), chain, offRampAddress) if err != nil { return ixns, fmt.Errorf("failed to get offramp reference addresses: %w", err) } @@ -784,10 +838,11 @@ func DeployAndMaybeSaveToAddressBook( address := solana.MustPublicKeyFromBase58(programID) programNameToType := map[string]deployment.ContractType{ - RouterProgramName: cs.Router, - OffRampProgramName: cs.OffRamp, - FeeQuoterProgramName: cs.FeeQuoter, - TokenPoolProgramName: cs.TokenPool, + RouterProgramName: ccipChangeset.Router, + OffRampProgramName: ccipChangeset.OffRamp, + FeeQuoterProgramName: ccipChangeset.FeeQuoter, + BurnMintTokenPool: ccipChangeset.BurnMintTokenPool, + LockReleaseTokenPool: ccipChangeset.LockReleaseTokenPool, } programType, ok := programNameToType[programName] if !ok { @@ -952,7 +1007,7 @@ type SetFeeAggregatorConfig struct { } func (cfg SetFeeAggregatorConfig) Validate(e deployment.Environment) error { - state, err := cs.LoadOnchainState(e) + state, err := ccipChangeset.LoadOnchainState(e) if err != nil { return fmt.Errorf("failed to load onchain state: %w", err) } @@ -983,7 +1038,7 @@ func SetFeeAggregator(e deployment.Environment, cfg SetFeeAggregatorConfig) (dep return deployment.ChangesetOutput{}, err } - state, _ := cs.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] chain := e.SolChains[cfg.ChainSelector] @@ -1005,7 +1060,7 @@ func SetFeeAggregator(e deployment.Environment, cfg SetFeeAggregatorConfig) (dep return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm instructions: %w", err) } newAddresses := deployment.NewMemoryAddressBook() - err = newAddresses.Save(cfg.ChainSelector, cfg.FeeAggregator, deployment.NewTypeAndVersion(cs.FeeAggregator, deployment.Version1_0_0)) + err = newAddresses.Save(cfg.ChainSelector, cfg.FeeAggregator, deployment.NewTypeAndVersion(ccipChangeset.FeeAggregator, deployment.Version1_0_0)) if err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("failed to save address: %w", err) } diff --git a/deployment/ccip/changeset/solana/cs_deploy_chain_test.go b/deployment/ccip/changeset/solana/cs_deploy_chain_test.go index a516bc2914f..4800df91f50 100644 --- a/deployment/ccip/changeset/solana/cs_deploy_chain_test.go +++ b/deployment/ccip/changeset/solana/cs_deploy_chain_test.go @@ -6,24 +6,24 @@ import ( "testing" "time" + "github.com/gagliardetto/solana-go" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/gagliardetto/solana-go" + solBinary "github.com/gagliardetto/binary" "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - cs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - cs_solana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" - solanachangesets "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" + ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + ccipChangesetSolana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6" + commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/globals" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" - commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" ) @@ -32,7 +32,7 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { lggr := logger.TestLogger(t) e := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ Bootstraps: 1, - Chains: 2, + Chains: 1, SolChains: 1, Nodes: 4, }) @@ -50,9 +50,9 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { OffRampParams: v1_6.DefaultOffRampParams(), } } - prereqCfg := make([]changeset.DeployPrerequisiteConfigPerChain, 0) + prereqCfg := make([]ccipChangeset.DeployPrerequisiteConfigPerChain, 0) for _, chain := range e.AllChainSelectors() { - prereqCfg = append(prereqCfg, changeset.DeployPrerequisiteConfigPerChain{ + prereqCfg = append(prereqCfg, ccipChangeset.DeployPrerequisiteConfigPerChain{ ChainSelector: chain, }) } @@ -85,14 +85,13 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { deployment.CreateLegacyChangeSet(commonchangeset.DeployLinkToken), e.AllChainSelectorsSolana(), ), - commonchangeset.Configure( deployment.CreateLegacyChangeSet(commonchangeset.DeployMCMSWithTimelockV2), cfg, ), commonchangeset.Configure( - deployment.CreateLegacyChangeSet(changeset.DeployPrerequisitesChangeset), - changeset.DeployPrerequisiteConfig{ + deployment.CreateLegacyChangeSet(ccipChangeset.DeployPrerequisitesChangeset), + ccipChangeset.DeployPrerequisiteConfig{ Configs: prereqCfg, }, ), @@ -115,6 +114,7 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { }, ), ) + require.NoError(t, err) addresses, err := e.ExistingAddresses.AddressesForChain(solChainSelectors[0]) require.NoError(t, err) mcmState, err := commonState.MaybeLoadMCMSWithTimelockChainStateSolana(e.SolChains[solChainSelectors[0]], addresses) @@ -132,8 +132,8 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { // we can't upgrade in place locally so we have to change where we build buildCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(cs_solana.BuildSolanaChangeset), - cs_solana.BuildSolanaConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.BuildSolanaChangeset), + ccipChangesetSolana.BuildSolanaConfig{ ChainSelector: solChainSelectors[0], GitCommitSha: "0863d8fed5fbada9f352f33c405e1753cbb7d72c", DestinationDir: e.SolChains[solChainSelectors[0]].ProgramsPath, @@ -141,14 +141,16 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { }, ) deployCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(cs_solana.DeployChainContractsChangesetSolana), - cs_solana.DeployChainContractsConfigSolana{ - DeployChainContractsConfig: v1_6.DeployChainContractsConfig{ - HomeChainSelector: homeChainSel, - ContractParamsPerChain: map[uint64]v1_6.ChainContractParams{ - solChainSelectors[0]: { - FeeQuoterParams: v1_6.DefaultFeeQuoterParams(), - OffRampParams: v1_6.DefaultOffRampParams(), + deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), + ccipChangesetSolana.DeployChainContractsConfig{ + HomeChainSelector: homeChainSel, + ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ + solChainSelectors[0]: { + FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ + DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + }, + OffRampParams: ccipChangesetSolana.OffRampParams{ + EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), }, }, }, @@ -156,17 +158,17 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { ) // set the fee aggregator address feeAggregatorCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(cs_solana.SetFeeAggregator), - cs_solana.SetFeeAggregatorConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.SetFeeAggregator), + ccipChangesetSolana.SetFeeAggregatorConfig{ ChainSelector: solChainSelectors[0], FeeAggregator: feeAggregatorPubKey.String(), }, ) transferOwnershipCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(solanachangesets.TransferCCIPToMCMSWithTimelockSolana), - solanachangesets.TransferCCIPToMCMSWithTimelockSolanaConfig{ + deployment.CreateLegacyChangeSet(ccipChangesetSolana.TransferCCIPToMCMSWithTimelockSolana), + ccipChangesetSolana.TransferCCIPToMCMSWithTimelockSolanaConfig{ MinDelay: 1 * time.Second, - ContractsByChain: map[uint64]solanachangesets.CCIPContractsToTransfer{ + ContractsByChain: map[uint64]ccipChangesetSolana.CCIPContractsToTransfer{ solChainSelectors[0]: { Router: true, FeeQuoter: true, @@ -177,14 +179,16 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { ) // make sure idempotency works and setting the upgrade authority upgradeAuthorityCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(cs_solana.DeployChainContractsChangesetSolana), - cs_solana.DeployChainContractsConfigSolana{ - DeployChainContractsConfig: v1_6.DeployChainContractsConfig{ - HomeChainSelector: homeChainSel, - ContractParamsPerChain: map[uint64]v1_6.ChainContractParams{ - solChainSelectors[0]: { - FeeQuoterParams: v1_6.DefaultFeeQuoterParams(), - OffRampParams: v1_6.DefaultOffRampParams(), + deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), + ccipChangesetSolana.DeployChainContractsConfig{ + HomeChainSelector: homeChainSel, + ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ + solChainSelectors[0]: { + FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ + DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + }, + OffRampParams: ccipChangesetSolana.OffRampParams{ + EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), }, }, }, @@ -192,23 +196,25 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { }, ) upgradeCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(cs_solana.DeployChainContractsChangesetSolana), - cs_solana.DeployChainContractsConfigSolana{ - DeployChainContractsConfig: v1_6.DeployChainContractsConfig{ - HomeChainSelector: homeChainSel, - ContractParamsPerChain: map[uint64]v1_6.ChainContractParams{ - solChainSelectors[0]: { - FeeQuoterParams: v1_6.DefaultFeeQuoterParams(), - OffRampParams: v1_6.DefaultOffRampParams(), + deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), + ccipChangesetSolana.DeployChainContractsConfig{ + HomeChainSelector: homeChainSel, + ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ + solChainSelectors[0]: { + FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ + DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + }, + OffRampParams: ccipChangesetSolana.OffRampParams{ + EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), }, }, }, - UpgradeConfig: cs_solana.UpgradeConfigSolana{ + UpgradeConfig: ccipChangesetSolana.UpgradeConfig{ NewFeeQuoterVersion: &deployment.Version1_1_0, NewRouterVersion: &deployment.Version1_1_0, UpgradeAuthority: upgradeAuthority, SpillAddress: upgradeAuthority, - MCMS: &cs.MCMSConfig{ + MCMS: &ccipChangeset.MCMSConfig{ MinDelay: 1 * time.Second, }, }, @@ -216,18 +222,20 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { ) // because we cannot upgrade in place locally, we can't redeploy offramp offRampCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(cs_solana.DeployChainContractsChangesetSolana), - cs_solana.DeployChainContractsConfigSolana{ - DeployChainContractsConfig: v1_6.DeployChainContractsConfig{ - HomeChainSelector: homeChainSel, - ContractParamsPerChain: map[uint64]v1_6.ChainContractParams{ - solChainSelectors[0]: { - FeeQuoterParams: v1_6.DefaultFeeQuoterParams(), - OffRampParams: v1_6.DefaultOffRampParams(), + deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), + ccipChangesetSolana.DeployChainContractsConfig{ + HomeChainSelector: homeChainSel, + ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ + solChainSelectors[0]: { + FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ + DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + }, + OffRampParams: ccipChangesetSolana.OffRampParams{ + EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), }, }, }, - UpgradeConfig: cs_solana.UpgradeConfigSolana{ + UpgradeConfig: ccipChangesetSolana.UpgradeConfig{ NewOffRampVersion: &deployment.Version1_1_0, }, }, @@ -240,7 +248,7 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { transferOwnershipCs, }) require.NoError(t, err) - state, err := changeset.LoadOnchainStateSolana(e) + state, err := ccipChangeset.LoadOnchainStateSolana(e) require.NoError(t, err) oldOffRampAddress := state.SolChains[solChainSelectors[0]].OffRamp // add a second offramp address @@ -251,7 +259,7 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { }) require.NoError(t, err) // verify the offramp address is different - state, err = changeset.LoadOnchainStateSolana(e) + state, err = ccipChangeset.LoadOnchainStateSolana(e) require.NoError(t, err) newOffRampAddress := state.SolChains[solChainSelectors[0]].OffRamp require.NotEqual(t, oldOffRampAddress, newOffRampAddress) @@ -273,13 +281,13 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { numFeeQuoters := 0 numOffRamps := 0 for _, address := range addresses { - if address.Type == changeset.Router { + if address.Type == ccipChangeset.Router { numRouters++ } - if address.Type == changeset.FeeQuoter { + if address.Type == ccipChangeset.FeeQuoter { numFeeQuoters++ } - if address.Type == changeset.OffRamp { + if address.Type == ccipChangeset.OffRamp { numOffRamps++ } } @@ -293,5 +301,4 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { require.NoError(t, err) // solana verification testhelpers.ValidateSolanaState(t, e, solChainSelectors) - } diff --git a/deployment/ccip/changeset/solana/cs_set_ocr3.go b/deployment/ccip/changeset/solana/cs_set_ocr3.go index 2c71cb28fc8..4eab1aca7bc 100644 --- a/deployment/ccip/changeset/solana/cs_set_ocr3.go +++ b/deployment/ccip/changeset/solana/cs_set_ocr3.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6" ) @@ -106,7 +107,7 @@ func SetOCR3ConfigSolana(e deployment.Environment, cfg v1_6.SetOCR3OffRampConfig func isOCR3ConfigSetOnOffRampSolana( e deployment.Environment, chain deployment.SolChain, - chainState changeset.SolCCIPChainState, + chainState ccipChangeset.SolCCIPChainState, args []internal.MultiOCR3BaseOCRConfigArgsSolana, ) (bool, error) { var configAccount solOffRamp.Config diff --git a/deployment/ccip/changeset/solana/cs_solana_token.go b/deployment/ccip/changeset/solana/cs_solana_token.go index fb16c125944..9932887acc2 100644 --- a/deployment/ccip/changeset/solana/cs_solana_token.go +++ b/deployment/ccip/changeset/solana/cs_solana_token.go @@ -8,6 +8,7 @@ import ( "github.com/gagliardetto/solana-go/rpc" "github.com/smartcontractkit/chainlink/deployment" + ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" solCommomUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" solTokenUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens" @@ -21,8 +22,9 @@ var _ deployment.ChangeSet[CreateSolanaTokenATAConfig] = CreateSolanaTokenATA // might need to take authority private key if it needs to sign that type DeploySolanaTokenConfig struct { ChainSelector uint64 - TokenProgramName string + TokenProgramName deployment.ContractType TokenDecimals uint8 + TokenSymbol string } func NewTokenInstruction(chain deployment.SolChain, cfg DeploySolanaTokenConfig) ([]solana.Instruction, solana.PrivateKey, error) { @@ -62,15 +64,16 @@ func DeploySolanaToken(e deployment.Environment, cfg DeploySolanaTokenConfig) (d } err = chain.Confirm(instructions, solCommomUtil.AddSigners(mintPrivKey)) if err != nil { - e.Logger.Errorw("Failed to confirm instructions for link token deployment", "chain", chain.String(), "err", err) + e.Logger.Errorw("Failed to confirm instructions for token deployment", "chain", chain.String(), "err", err) return deployment.ChangesetOutput{}, err } newAddresses := deployment.NewMemoryAddressBook() tv := deployment.NewTypeAndVersion(deployment.ContractType(cfg.TokenProgramName), deployment.Version1_0_0) + tv.AddLabel(cfg.TokenSymbol) err = newAddresses.Save(cfg.ChainSelector, mint.String(), tv) if err != nil { - e.Logger.Errorw("Failed to save link token", "chain", chain.String(), "err", err) + e.Logger.Errorw("Failed to save token", "chain", chain.String(), "err", err) return deployment.ChangesetOutput{}, err } @@ -83,17 +86,19 @@ func DeploySolanaToken(e deployment.Environment, cfg DeploySolanaTokenConfig) (d type MintSolanaTokenConfig struct { ChainSelector uint64 - TokenProgram string TokenPubkey string AmountToAddress map[string]uint64 // address -> amount } func (cfg MintSolanaTokenConfig) Validate(e deployment.Environment) error { chain := e.SolChains[cfg.ChainSelector] - // get addresses tokenAddress := solana.MustPublicKeyFromBase58(cfg.TokenPubkey) - // get token program id - tokenprogramID, err := GetTokenProgramID(cfg.TokenProgram) + state, err := ccipChangeset.LoadOnchainState(e) + if err != nil { + return err + } + chainState := state.SolChains[cfg.ChainSelector] + tokenprogramID, err := chainState.TokenToTokenProgram(tokenAddress) if err != nil { return err } @@ -121,10 +126,12 @@ func MintSolanaToken(e deployment.Environment, cfg MintSolanaTokenConfig) (deplo } // get chain chain := e.SolChains[cfg.ChainSelector] + state, _ := ccipChangeset.LoadOnchainState(e) + chainState := state.SolChains[cfg.ChainSelector] // get addresses tokenAddress := solana.MustPublicKeyFromBase58(cfg.TokenPubkey) // get token program id - tokenprogramID, _ := GetTokenProgramID(cfg.TokenProgram) + tokenprogramID, _ := chainState.TokenToTokenProgram(tokenAddress) // get mint instructions instructions := []solana.Instruction{} @@ -153,7 +160,7 @@ func MintSolanaToken(e deployment.Environment, cfg MintSolanaTokenConfig) (deplo type CreateSolanaTokenATAConfig struct { ChainSelector uint64 TokenPubkey solana.PublicKey - TokenProgram string + TokenProgram deployment.ContractType ATAList []string // addresses to create ATAs for } diff --git a/deployment/ccip/changeset/solana/cs_solana_token_test.go b/deployment/ccip/changeset/solana/cs_solana_token_test.go index d50eee74360..4534bbfa5a2 100644 --- a/deployment/ccip/changeset/solana/cs_solana_token_test.go +++ b/deployment/ccip/changeset/solana/cs_solana_token_test.go @@ -33,8 +33,9 @@ func TestSolanaTokenOps(t *testing.T) { deployment.CreateLegacyChangeSet(changeset_solana.DeploySolanaToken), changeset_solana.DeploySolanaTokenConfig{ ChainSelector: solChain1, - TokenProgramName: deployment.SPL2022Tokens, + TokenProgramName: ccipChangeset.SPL2022Tokens, TokenDecimals: 9, + TokenSymbol: "TEST_TOKEN", }, ), ) @@ -55,7 +56,7 @@ func TestSolanaTokenOps(t *testing.T) { changeset_solana.CreateSolanaTokenATAConfig{ ChainSelector: solChain1, TokenPubkey: tokenAddress, - TokenProgram: deployment.SPL2022Tokens, + TokenProgram: ccipChangeset.SPL2022Tokens, ATAList: []string{deployerKey.String(), testUserPubKey.String()}, }, ), @@ -65,7 +66,6 @@ func TestSolanaTokenOps(t *testing.T) { changeset_solana.MintSolanaTokenConfig{ ChainSelector: solChain1, TokenPubkey: tokenAddress.String(), - TokenProgram: deployment.SPL2022Tokens, AmountToAddress: map[string]uint64{ deployerKey.String(): uint64(1000), testUserPubKey.String(): uint64(1000), diff --git a/deployment/ccip/changeset/solana/cs_token_admin_registry.go b/deployment/ccip/changeset/solana/cs_token_admin_registry.go index 8b4ee857852..550a7581325 100644 --- a/deployment/ccip/changeset/solana/cs_token_admin_registry.go +++ b/deployment/ccip/changeset/solana/cs_token_admin_registry.go @@ -12,7 +12,7 @@ import ( solState "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state" "github.com/smartcontractkit/chainlink/deployment" - cs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" ) type RegisterTokenAdminRegistryType int @@ -42,7 +42,7 @@ func (cfg RegisterTokenAdminRegistryConfig) Validate(e deployment.Environment) e if err := commonValidation(e, cfg.ChainSelector, tokenPubKey); err != nil { return err } - state, _ := cs.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] chain := e.SolChains[cfg.ChainSelector] if err := validateRouterConfig(chain, chainState); err != nil { @@ -64,7 +64,7 @@ func RegisterTokenAdminRegistry(e deployment.Environment, cfg RegisterTokenAdmin return deployment.ChangesetOutput{}, err } chain := e.SolChains[cfg.ChainSelector] - state, _ := cs.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey) @@ -136,7 +136,7 @@ func (cfg TransferAdminRoleTokenAdminRegistryConfig) Validate(e deployment.Envir ) } - state, _ := cs.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] chain := e.SolChains[cfg.ChainSelector] if err := validateRouterConfig(chain, chainState); err != nil { @@ -158,7 +158,7 @@ func TransferAdminRoleTokenAdminRegistry(e deployment.Environment, cfg TransferA return deployment.ChangesetOutput{}, err } chain := e.SolChains[cfg.ChainSelector] - state, _ := cs.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey) @@ -198,7 +198,7 @@ func (cfg AcceptAdminRoleTokenAdminRegistryConfig) Validate(e deployment.Environ if err := commonValidation(e, cfg.ChainSelector, tokenPubKey); err != nil { return err } - state, _ := cs.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] chain := e.SolChains[cfg.ChainSelector] if err := validateRouterConfig(chain, chainState); err != nil { @@ -230,7 +230,7 @@ func AcceptAdminRoleTokenAdminRegistry(e deployment.Environment, cfg AcceptAdmin return deployment.ChangesetOutput{}, err } chain := e.SolChains[cfg.ChainSelector] - state, _ := cs.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey) newRegistryAdminPrivateKey := solana.MustPrivateKeyFromBase58(cfg.NewRegistryAdminPrivateKey) diff --git a/deployment/ccip/changeset/solana/cs_token_pool.go b/deployment/ccip/changeset/solana/cs_token_pool.go index cdf61704cf9..7d85ecfa4fb 100644 --- a/deployment/ccip/changeset/solana/cs_token_pool.go +++ b/deployment/ccip/changeset/solana/cs_token_pool.go @@ -6,26 +6,43 @@ import ( "github.com/gagliardetto/solana-go" + solBaseTokenPool "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/base_token_pool" solRouter "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" + solBurnMintTokenPool "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/example_burnmint_token_pool" + solLockReleaseTokenPool "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/example_lockrelease_token_pool" solTestTokenPool "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/test_token_pool" solCommonUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" solState "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state" solTokenUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens" "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - state2 "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" ) var _ deployment.ChangeSet[TokenPoolConfig] = AddTokenPool var _ deployment.ChangeSet[RemoteChainTokenPoolConfig] = SetupTokenPoolForRemoteChain +func validatePoolDeployment(s ccipChangeset.SolCCIPChainState, poolType solTestTokenPool.PoolType, selector uint64) error { + switch poolType { + case solTestTokenPool.BurnAndMint_PoolType: + if s.BurnMintTokenPool.IsZero() { + return fmt.Errorf("token pool of type BurnAndMint not found in existing state, deploy the token pool first for chain %d", selector) + } + case solTestTokenPool.LockAndRelease_PoolType: + if s.LockReleaseTokenPool.IsZero() { + return fmt.Errorf("token pool of type LockAndRelease not found in existing state, deploy the token pool first for chain %d", selector) + } + default: + return fmt.Errorf("invalid pool type: %s", poolType) + } + return nil +} + type TokenPoolConfig struct { - ChainSelector uint64 - PoolType solTestTokenPool.PoolType - Authority string - TokenPubKey string - TokenProgramName string + ChainSelector uint64 + PoolType solTestTokenPool.PoolType + Authority string + TokenPubKey string } func (cfg TokenPoolConfig) Validate(e deployment.Environment) error { @@ -33,24 +50,38 @@ func (cfg TokenPoolConfig) Validate(e deployment.Environment) error { if err := commonValidation(e, cfg.ChainSelector, tokenPubKey); err != nil { return err } - state, _ := state2.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] - if chainState.TokenPool.IsZero() { - return fmt.Errorf("token pool not found in existing state, deploy the token pool first for chain %d", cfg.ChainSelector) + + if _, err := chainState.TokenToTokenProgram(tokenPubKey); err != nil { + return fmt.Errorf("failed to get token program for token address %s: %w", tokenPubKey.String(), err) } - if _, err := GetTokenProgramID(cfg.TokenProgramName); err != nil { + + if err := validatePoolDeployment(chainState, cfg.PoolType, cfg.ChainSelector); err != nil { return err } - tokenPool := chainState.TokenPool + var tokenPool solana.PublicKey + var poolConfigAccount interface{} + + switch cfg.PoolType { + case solTestTokenPool.BurnAndMint_PoolType: + tokenPool = chainState.BurnMintTokenPool + poolConfigAccount = solBurnMintTokenPool.State{} + case solTestTokenPool.LockAndRelease_PoolType: + tokenPool = chainState.LockReleaseTokenPool + poolConfigAccount = solLockReleaseTokenPool.State{} + default: + return fmt.Errorf("invalid pool type: %s", cfg.PoolType) + } + poolConfigPDA, err := solTokenUtil.TokenPoolConfigAddress(tokenPubKey, tokenPool) if err != nil { return fmt.Errorf("failed to get token pool config address (mint: %s, pool: %s): %w", tokenPubKey.String(), tokenPool.String(), err) } chain := e.SolChains[cfg.ChainSelector] - var poolConfigAccount solTestTokenPool.State if err := chain.GetAccountDataBorshInto(context.Background(), poolConfigPDA, &poolConfigAccount); err == nil { - return fmt.Errorf("token pool config already exists for (mint: %s, pool: %s)", tokenPubKey.String(), tokenPool.String()) + return fmt.Errorf("token pool config already exists for (mint: %s, pool: %s, type: %s)", tokenPubKey.String(), tokenPool.String(), cfg.PoolType) } return nil } @@ -60,15 +91,24 @@ func AddTokenPool(e deployment.Environment, cfg TokenPoolConfig) (deployment.Cha return deployment.ChangesetOutput{}, err } chain := e.SolChains[cfg.ChainSelector] - state, _ := state2.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] authorityPubKey := solana.MustPublicKeyFromBase58(cfg.Authority) tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey) + tokenPool := solana.PublicKey{} + + if cfg.PoolType == solTestTokenPool.BurnAndMint_PoolType { + tokenPool = chainState.BurnMintTokenPool + solBurnMintTokenPool.SetProgramID(tokenPool) + } else if cfg.PoolType == solTestTokenPool.LockAndRelease_PoolType { + tokenPool = chainState.LockReleaseTokenPool + solLockReleaseTokenPool.SetProgramID(tokenPool) + } // verified - tokenprogramID, _ := GetTokenProgramID(cfg.TokenProgramName) - poolConfigPDA, _ := solTokenUtil.TokenPoolConfigAddress(tokenPubKey, chainState.TokenPool) - poolSigner, _ := solTokenUtil.TokenPoolSignerAddress(tokenPubKey, chainState.TokenPool) + tokenprogramID, _ := chainState.TokenToTokenProgram(tokenPubKey) + poolConfigPDA, _ := solTokenUtil.TokenPoolConfigAddress(tokenPubKey, tokenPool) + poolSigner, _ := solTokenUtil.TokenPoolSignerAddress(tokenPubKey, tokenPool) // ata for token pool createI, tokenPoolATA, err := solTokenUtil.CreateAssociatedTokenAccount( @@ -78,24 +118,38 @@ func AddTokenPool(e deployment.Environment, cfg TokenPoolConfig) (deployment.Cha chain.DeployerKey.PublicKey(), ) if err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("failed to create associated token account for tokenpool (mint: %s, pool: %s): %w", tokenPubKey.String(), chainState.TokenPool.String(), err) + return deployment.ChangesetOutput{}, fmt.Errorf("failed to create associated token account for tokenpool (mint: %s, pool: %s): %w", tokenPubKey.String(), tokenPool.String(), err) } + instructions := []solana.Instruction{createI} - solTestTokenPool.SetProgramID(chainState.TokenPool) - // initialize token pool for token - poolInitI, err := solTestTokenPool.NewInitializeInstruction( - cfg.PoolType, - chainState.Router, - poolConfigPDA, - tokenPubKey, - authorityPubKey, // this is assumed to be chain.DeployerKey for now (owner of token pool) - solana.SystemProgramID, - ).ValidateAndBuild() + var poolInitI solana.Instruction + switch cfg.PoolType { + case solTestTokenPool.BurnAndMint_PoolType: + // initialize token pool for token + poolInitI, err = solBurnMintTokenPool.NewInitializeInstruction( + chainState.Router, + poolConfigPDA, + tokenPubKey, + authorityPubKey, // this is assumed to be chain.DeployerKey for now (owner of token pool) + solana.SystemProgramID, + ).ValidateAndBuild() + case solTestTokenPool.LockAndRelease_PoolType: + // initialize token pool for token + poolInitI, err = solLockReleaseTokenPool.NewInitializeInstruction( + chainState.Router, + poolConfigPDA, + tokenPubKey, + authorityPubKey, // this is assumed to be chain.DeployerKey for now (owner of token pool) + solana.SystemProgramID, + ).ValidateAndBuild() + default: + return deployment.ChangesetOutput{}, fmt.Errorf("invalid pool type: %s", cfg.PoolType) + } if err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("failed to generate instructions: %w", err) } - instructions := []solana.Instruction{createI, poolInitI} + instructions = append(instructions, poolInitI) if cfg.PoolType == solTestTokenPool.BurnAndMint_PoolType && tokenPubKey != solana.SolMint { // make pool mint_authority for token @@ -126,10 +180,11 @@ type RemoteChainTokenPoolConfig struct { SolChainSelector uint64 RemoteChainSelector uint64 SolTokenPubKey string + PoolType solTestTokenPool.PoolType // this is actually derivable from on chain given token symbol - RemoteConfig solTestTokenPool.RemoteConfig - InboundRateLimit solTestTokenPool.RateLimitConfig - OutboundRateLimit solTestTokenPool.RateLimitConfig + RemoteConfig solBaseTokenPool.RemoteConfig + InboundRateLimit solBaseTokenPool.RateLimitConfig + OutboundRateLimit solBaseTokenPool.RateLimitConfig } func (cfg RemoteChainTokenPoolConfig) Validate(e deployment.Environment) error { @@ -137,23 +192,38 @@ func (cfg RemoteChainTokenPoolConfig) Validate(e deployment.Environment) error { if err := commonValidation(e, cfg.SolChainSelector, tokenPubKey); err != nil { return err } - state, _ := state2.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.SolChainSelector] - if chainState.TokenPool.IsZero() { - return fmt.Errorf("token pool not found in existing state, deploy token pool for chain %d", cfg.SolChainSelector) + chain := e.SolChains[cfg.SolChainSelector] + + if err := validatePoolDeployment(chainState, cfg.PoolType, cfg.SolChainSelector); err != nil { + return err } - chain := e.SolChains[cfg.SolChainSelector] - tokenPool := chainState.TokenPool + var tokenPool solana.PublicKey + var poolConfigAccount interface{} + var remoteChainConfigAccount interface{} + + switch cfg.PoolType { + case solTestTokenPool.BurnAndMint_PoolType: + tokenPool = chainState.BurnMintTokenPool + poolConfigAccount = solBurnMintTokenPool.State{} + remoteChainConfigAccount = solBurnMintTokenPool.ChainConfig{} + case solTestTokenPool.LockAndRelease_PoolType: + tokenPool = chainState.LockReleaseTokenPool + poolConfigAccount = solLockReleaseTokenPool.State{} + remoteChainConfigAccount = solLockReleaseTokenPool.ChainConfig{} + default: + return fmt.Errorf("invalid pool type: %s", cfg.PoolType) + } // check if pool config exists (cannot do remote setup without it) poolConfigPDA, err := solTokenUtil.TokenPoolConfigAddress(tokenPubKey, tokenPool) if err != nil { return fmt.Errorf("failed to get token pool config address (mint: %s, pool: %s): %w", tokenPubKey.String(), tokenPool.String(), err) } - var poolConfigAccount solTestTokenPool.State if err := chain.GetAccountDataBorshInto(context.Background(), poolConfigPDA, &poolConfigAccount); err != nil { - return fmt.Errorf("token pool config not found (mint: %s, pool: %s): %w", tokenPubKey.String(), chainState.TokenPool.String(), err) + return fmt.Errorf("token pool config not found (mint: %s, pool: %s, type: %s): %w", tokenPubKey.String(), tokenPool.String(), cfg.PoolType, err) } // check if this remote chain is already configured for this token @@ -161,9 +231,8 @@ func (cfg RemoteChainTokenPoolConfig) Validate(e deployment.Environment) error { if err != nil { return fmt.Errorf("failed to get token pool remote chain config pda (remoteSelector: %d, mint: %s, pool: %s): %w", cfg.RemoteChainSelector, tokenPubKey.String(), tokenPool.String(), err) } - var remoteChainConfigAccount solTestTokenPool.ChainConfig if err := chain.GetAccountDataBorshInto(context.Background(), remoteChainConfigPDA, &remoteChainConfigAccount); err == nil { - return fmt.Errorf("remote chain config already exists for (remoteSelector: %d, mint: %s, pool: %s)", cfg.RemoteChainSelector, tokenPubKey.String(), tokenPool.String()) + return fmt.Errorf("remote chain config already exists for (remoteSelector: %d, mint: %s, pool: %s, type: %s)", cfg.RemoteChainSelector, tokenPubKey.String(), tokenPool.String(), cfg.PoolType) } return nil } @@ -172,16 +241,44 @@ func SetupTokenPoolForRemoteChain(e deployment.Environment, cfg RemoteChainToken if err := cfg.Validate(e); err != nil { return deployment.ChangesetOutput{}, err } - tokenPubKey := solana.MustPublicKeyFromBase58(cfg.SolTokenPubKey) + chain := e.SolChains[cfg.SolChainSelector] - state, _ := state2.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.SolChainSelector] - // verified - poolConfigPDA, _ := solTokenUtil.TokenPoolConfigAddress(tokenPubKey, chainState.TokenPool) - remoteChainConfigPDA, _, _ := solTokenUtil.TokenPoolChainConfigPDA(cfg.RemoteChainSelector, tokenPubKey, chainState.TokenPool) + tokenPubKey := solana.MustPublicKeyFromBase58(cfg.SolTokenPubKey) - solTestTokenPool.SetProgramID(chainState.TokenPool) - ixConfigure, err := solTestTokenPool.NewInitChainRemoteConfigInstruction( + var instructions []solana.Instruction + var err error + switch cfg.PoolType { + case solTestTokenPool.BurnAndMint_PoolType: + instructions, err = getInstructionsForBurnMint(chain, chainState, cfg) + case solTestTokenPool.LockAndRelease_PoolType: + instructions, err = getInstructionsForLockRelease(chain, chainState, cfg) + default: + return deployment.ChangesetOutput{}, fmt.Errorf("invalid pool type: %s", cfg.PoolType) + } + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to generate instructions: %w", err) + } + + err = chain.Confirm(instructions) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm instructions: %w", err) + } + e.Logger.Infow("Configured token pool for remote chain", "remote_chain_selector", cfg.RemoteChainSelector, "token_pubkey", tokenPubKey.String()) + return deployment.ChangesetOutput{}, nil +} + +func getInstructionsForBurnMint( + chain deployment.SolChain, + chainState ccipChangeset.SolCCIPChainState, + cfg RemoteChainTokenPoolConfig, +) ([]solana.Instruction, error) { + tokenPubKey := solana.MustPublicKeyFromBase58(cfg.SolTokenPubKey) + poolConfigPDA, _ := solTokenUtil.TokenPoolConfigAddress(tokenPubKey, chainState.BurnMintTokenPool) + remoteChainConfigPDA, _, _ := solTokenUtil.TokenPoolChainConfigPDA(cfg.RemoteChainSelector, tokenPubKey, chainState.BurnMintTokenPool) + solBurnMintTokenPool.SetProgramID(chainState.BurnMintTokenPool) + ixConfigure, err := solBurnMintTokenPool.NewInitChainRemoteConfigInstruction( cfg.RemoteChainSelector, tokenPubKey, cfg.RemoteConfig, @@ -191,9 +288,9 @@ func SetupTokenPoolForRemoteChain(e deployment.Environment, cfg RemoteChainToken solana.SystemProgramID, ).ValidateAndBuild() if err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("failed to generate instructions: %w", err) + return nil, fmt.Errorf("failed to generate instructions: %w", err) } - ixRates, err := solTestTokenPool.NewSetChainRateLimitInstruction( + ixRates, err := solBurnMintTokenPool.NewSetChainRateLimitInstruction( cfg.RemoteChainSelector, tokenPubKey, cfg.InboundRateLimit, @@ -204,10 +301,9 @@ func SetupTokenPoolForRemoteChain(e deployment.Environment, cfg RemoteChainToken solana.SystemProgramID, ).ValidateAndBuild() if err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("failed to generate instructions: %w", err) + return nil, fmt.Errorf("failed to generate instructions: %w", err) } - - ixAppend, err := solTestTokenPool.NewAppendRemotePoolAddressesInstruction( + ixAppend, err := solBurnMintTokenPool.NewAppendRemotePoolAddressesInstruction( cfg.RemoteChainSelector, tokenPubKey, cfg.RemoteConfig.PoolAddresses, // i dont know why this is a list (is it for different types of pool of the same token?) @@ -217,23 +313,65 @@ func SetupTokenPoolForRemoteChain(e deployment.Environment, cfg RemoteChainToken solana.SystemProgramID, ).ValidateAndBuild() if err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("failed to generate instructions: %w", err) + return nil, fmt.Errorf("failed to generate instructions: %w", err) } + return []solana.Instruction{ixConfigure, ixRates, ixAppend}, nil +} - instructions := []solana.Instruction{ixConfigure, ixRates, ixAppend} - err = chain.Confirm(instructions) +func getInstructionsForLockRelease( + chain deployment.SolChain, + chainState ccipChangeset.SolCCIPChainState, + cfg RemoteChainTokenPoolConfig, +) ([]solana.Instruction, error) { + tokenPubKey := solana.MustPublicKeyFromBase58(cfg.SolTokenPubKey) + poolConfigPDA, _ := solTokenUtil.TokenPoolConfigAddress(tokenPubKey, chainState.LockReleaseTokenPool) + remoteChainConfigPDA, _, _ := solTokenUtil.TokenPoolChainConfigPDA(cfg.RemoteChainSelector, tokenPubKey, chainState.LockReleaseTokenPool) + solLockReleaseTokenPool.SetProgramID(chainState.LockReleaseTokenPool) + ixConfigure, err := solLockReleaseTokenPool.NewInitChainRemoteConfigInstruction( + cfg.RemoteChainSelector, + tokenPubKey, + cfg.RemoteConfig, + poolConfigPDA, + remoteChainConfigPDA, + chain.DeployerKey.PublicKey(), + solana.SystemProgramID, + ).ValidateAndBuild() if err != nil { - return deployment.ChangesetOutput{}, fmt.Errorf("failed to confirm instructions: %w", err) + return nil, fmt.Errorf("failed to generate instructions: %w", err) } - e.Logger.Infow("Configured token pool for remote chain", "remote_chain_selector", cfg.RemoteChainSelector, "token_pubkey", tokenPubKey.String()) - return deployment.ChangesetOutput{}, nil + ixRates, err := solLockReleaseTokenPool.NewSetChainRateLimitInstruction( + cfg.RemoteChainSelector, + tokenPubKey, + cfg.InboundRateLimit, + cfg.OutboundRateLimit, + poolConfigPDA, + remoteChainConfigPDA, + chain.DeployerKey.PublicKey(), + solana.SystemProgramID, + ).ValidateAndBuild() + if err != nil { + return nil, fmt.Errorf("failed to generate instructions: %w", err) + } + ixAppend, err := solLockReleaseTokenPool.NewAppendRemotePoolAddressesInstruction( + cfg.RemoteChainSelector, + tokenPubKey, + cfg.RemoteConfig.PoolAddresses, // i dont know why this is a list (is it for different types of pool of the same token?) + poolConfigPDA, + remoteChainConfigPDA, + chain.DeployerKey.PublicKey(), + solana.SystemProgramID, + ).ValidateAndBuild() + if err != nil { + return nil, fmt.Errorf("failed to generate instructions: %w", err) + } + return []solana.Instruction{ixConfigure, ixRates, ixAppend}, nil } // ADD TOKEN POOL LOOKUP TABLE type TokenPoolLookupTableConfig struct { ChainSelector uint64 TokenPubKey string - TokenProgram string // this can go as a address book tag + PoolType solTestTokenPool.PoolType } func (cfg TokenPoolLookupTableConfig) Validate(e deployment.Environment) error { @@ -241,13 +379,13 @@ func (cfg TokenPoolLookupTableConfig) Validate(e deployment.Environment) error { if err := commonValidation(e, cfg.ChainSelector, tokenPubKey); err != nil { return err } - state, _ := state2.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] - if chainState.TokenPool.IsZero() { - return fmt.Errorf("token pool not found in existing state, deploy the token pool first for chain %d", cfg.ChainSelector) + _, err := chainState.TokenToTokenProgram(tokenPubKey) + if err != nil { + return fmt.Errorf("failed to get token program for token address %s: %w", tokenPubKey.String(), err) } - - return nil + return validatePoolDeployment(chainState, cfg.PoolType, cfg.ChainSelector) } func AddTokenPoolLookupTable(e deployment.Environment, cfg TokenPoolLookupTableConfig) (deployment.ChangesetOutput, error) { @@ -257,15 +395,20 @@ func AddTokenPoolLookupTable(e deployment.Environment, cfg TokenPoolLookupTableC chain := e.SolChains[cfg.ChainSelector] ctx := e.GetContext() client := chain.Client - state, _ := state2.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] authorityPrivKey := chain.DeployerKey // assuming the authority is the deployer key tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey) - + tokenPool := solana.PublicKey{} + if cfg.PoolType == solTestTokenPool.BurnAndMint_PoolType { + tokenPool = chainState.BurnMintTokenPool + } else if cfg.PoolType == solTestTokenPool.LockAndRelease_PoolType { + tokenPool = chainState.LockReleaseTokenPool + } tokenAdminRegistryPDA, _, _ := solState.FindTokenAdminRegistryPDA(tokenPubKey, chainState.Router) - tokenPoolChainConfigPDA, _ := solTokenUtil.TokenPoolConfigAddress(tokenPubKey, chainState.TokenPool) - tokenPoolSigner, _ := solTokenUtil.TokenPoolSignerAddress(tokenPubKey, chainState.TokenPool) - tokenProgram, _ := GetTokenProgramID(cfg.TokenProgram) + tokenPoolChainConfigPDA, _ := solTokenUtil.TokenPoolConfigAddress(tokenPubKey, tokenPool) + tokenPoolSigner, _ := solTokenUtil.TokenPoolSignerAddress(tokenPubKey, tokenPool) + tokenProgram, _ := chainState.TokenToTokenProgram(tokenPubKey) poolTokenAccount, _, _ := solTokenUtil.FindAssociatedTokenAddress(tokenProgram, tokenPubKey, tokenPoolSigner) feeTokenConfigPDA, _, _ := solState.FindFqBillingTokenConfigPDA(tokenPubKey, chainState.FeeQuoter) @@ -279,7 +422,7 @@ func AddTokenPoolLookupTable(e deployment.Environment, cfg TokenPoolLookupTableC list := solana.PublicKeySlice{ table, // 0 tokenAdminRegistryPDA, // 1 - chainState.TokenPool, // 2 + tokenPool, // 2 tokenPoolChainConfigPDA, // 3 - writable poolTokenAccount, // 4 - writable tokenPoolSigner, // 5 @@ -294,7 +437,7 @@ func AddTokenPoolLookupTable(e deployment.Environment, cfg TokenPoolLookupTableC return deployment.ChangesetOutput{}, fmt.Errorf("failed to await slot change while extending lookup table: %w", err) } newAddressBook := deployment.NewMemoryAddressBook() - tv := deployment.NewTypeAndVersion(changeset.TokenPoolLookupTable, deployment.Version1_0_0) + tv := deployment.NewTypeAndVersion(ccipChangeset.TokenPoolLookupTable, deployment.Version1_0_0) tv.Labels.Add(tokenPubKey.String()) if err := newAddressBook.Save(cfg.ChainSelector, table.String(), tv); err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("failed to save tokenpool address lookup table: %w", err) @@ -309,7 +452,6 @@ type SetPoolConfig struct { ChainSelector uint64 TokenPubKey string TokenAdminRegistryAdminPrivateKey string - PoolLookupTable string WritableIndexes []uint8 } @@ -318,12 +460,9 @@ func (cfg SetPoolConfig) Validate(e deployment.Environment) error { if err := commonValidation(e, cfg.ChainSelector, tokenPubKey); err != nil { return err } - state, _ := state2.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] chain := e.SolChains[cfg.ChainSelector] - if chainState.TokenPool.IsZero() { - return fmt.Errorf("token pool not found in existing state, deploy the token pool first for chain %d", cfg.ChainSelector) - } if err := validateRouterConfig(chain, chainState); err != nil { return err } @@ -335,6 +474,9 @@ func (cfg SetPoolConfig) Validate(e deployment.Environment) error { if err := chain.GetAccountDataBorshInto(context.Background(), tokenAdminRegistryPDA, &tokenAdminRegistryAccount); err != nil { return fmt.Errorf("token admin registry not found for (mint: %s, router: %s), cannot set pool", tokenPubKey.String(), chainState.Router.String()) } + if _, ok := chainState.TokenPoolLookupTable[tokenPubKey]; !ok { + return fmt.Errorf("token pool lookup table not found for (mint: %s)", tokenPubKey.String()) + } return nil } @@ -345,13 +487,13 @@ func SetPool(e deployment.Environment, cfg SetPoolConfig) (deployment.ChangesetO } chain := e.SolChains[cfg.ChainSelector] - state, _ := state2.LoadOnchainState(e) + state, _ := ccipChangeset.LoadOnchainState(e) chainState := state.SolChains[cfg.ChainSelector] tokenPubKey := solana.MustPublicKeyFromBase58(cfg.TokenPubKey) routerConfigPDA, _, _ := solState.FindConfigPDA(chainState.Router) tokenAdminRegistryPDA, _, _ := solState.FindTokenAdminRegistryPDA(tokenPubKey, chainState.Router) tokenAdminRegistryAdminPrivKey := solana.MustPrivateKeyFromBase58(cfg.TokenAdminRegistryAdminPrivateKey) - lookupTablePubKey := solana.MustPublicKeyFromBase58(cfg.PoolLookupTable) + lookupTablePubKey := chainState.TokenPoolLookupTable[tokenPubKey] base := solRouter.NewSetPoolInstruction( cfg.WritableIndexes, diff --git a/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go b/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go index 7afa768b196..94f34753ba6 100644 --- a/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go +++ b/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go @@ -22,11 +22,14 @@ import ( "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6" "github.com/smartcontractkit/chainlink/v2/core/logger" + solBinary "github.com/gagliardetto/binary" + "github.com/smartcontractkit/chainlink/deployment" solanachangesets "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/globals" commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" @@ -245,11 +248,18 @@ func prepareEnvironmentForOwnershipTransfer(t *testing.T) (deployment.Environmen }, ), commonchangeset.Configure( - deployment.CreateLegacyChangeSet(solanachangesets.DeployChainContractsChangesetSolana), - solanachangesets.DeployChainContractsConfigSolana{ - DeployChainContractsConfig: v1_6.DeployChainContractsConfig{ - HomeChainSelector: homeChainSel, - ContractParamsPerChain: contractParams, + deployment.CreateLegacyChangeSet(solanachangesets.DeployChainContractsChangeset), + solanachangesets.DeployChainContractsConfig{ + HomeChainSelector: homeChainSel, + ContractParamsPerChain: map[uint64]solanachangesets.ChainContractParams{ + solChain1: { + FeeQuoterParams: solanachangesets.FeeQuoterParams{ + DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + }, + OffRampParams: solanachangesets.OffRampParams{ + EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), + }, + }, }, }, ), @@ -257,7 +267,7 @@ func prepareEnvironmentForOwnershipTransfer(t *testing.T) (deployment.Environmen deployment.CreateLegacyChangeSet(solanachangesets.DeploySolanaToken), solanachangesets.DeploySolanaTokenConfig{ ChainSelector: solChain1, - TokenProgramName: deployment.SPL2022Tokens, + TokenProgramName: changeset.SPL2022Tokens, TokenDecimals: 9, }, ), @@ -285,11 +295,10 @@ func prepareEnvironmentForOwnershipTransfer(t *testing.T) (deployment.Environmen commonchangeset.Configure( deployment.CreateLegacyChangeSet(solanachangesets.AddTokenPool), solanachangesets.TokenPoolConfig{ - ChainSelector: solChain1, - TokenPubKey: tokenAddress.String(), - TokenProgramName: deployment.SPL2022Tokens, - PoolType: test_token_pool.LockAndRelease_PoolType, - Authority: e.SolChains[solChain1].DeployerKey.PublicKey().String(), + ChainSelector: solChain1, + TokenPubKey: tokenAddress.String(), + PoolType: test_token_pool.LockAndRelease_PoolType, + Authority: e.SolChains[solChain1].DeployerKey.PublicKey().String(), }, ), }) diff --git a/deployment/ccip/changeset/solana_state.go b/deployment/ccip/changeset/solana_state.go index eb585d219e6..e48f5165677 100644 --- a/deployment/ccip/changeset/solana_state.go +++ b/deployment/ccip/changeset/solana_state.go @@ -18,12 +18,14 @@ import ( commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" ) -var ( - TokenPool deployment.ContractType = "TokenPool" - Receiver deployment.ContractType = "Receiver" - SPL2022Tokens deployment.ContractType = "SPL2022Tokens" - WSOL deployment.ContractType = "WSOL" - FeeAggregator deployment.ContractType = "FeeAggregator" +const ( + OfframpAddressLookupTable deployment.ContractType = "OfframpAddressLookupTable" + TokenPool deployment.ContractType = "TokenPool" + Receiver deployment.ContractType = "Receiver" + SPL2022Tokens deployment.ContractType = "SPL2022Tokens" + SPLTokens deployment.ContractType = "SPLTokens" + WSOL deployment.ContractType = "WSOL" + FeeAggregator deployment.ContractType = "FeeAggregator" // for PDAs from AddRemoteChainToSolana RemoteSource deployment.ContractType = "RemoteSource" RemoteDest deployment.ContractType = "RemoteDest" @@ -35,15 +37,20 @@ var ( // SolCCIPChainState holds public keys for all the currently deployed CCIP programs // on a chain. If a key has zero value, it means the program does not exist on the chain. type SolCCIPChainState struct { - LinkToken solana.PublicKey - Router solana.PublicKey - Receiver solana.PublicKey // for tests only - SPL2022Tokens []solana.PublicKey - TokenPool solana.PublicKey - WSOL solana.PublicKey - FeeQuoter solana.PublicKey - OffRamp solana.PublicKey - FeeAggregator solana.PublicKey + LinkToken solana.PublicKey + Router solana.PublicKey + OfframpAddressLookupTable solana.PublicKey + Receiver solana.PublicKey // for tests only + SPL2022Tokens []solana.PublicKey + SPLTokens []solana.PublicKey + // TokenPool solana.PublicKey + BurnMintTokenPool solana.PublicKey + LockReleaseTokenPool solana.PublicKey + WSOL solana.PublicKey + FeeQuoter solana.PublicKey + OffRamp solana.PublicKey + FeeAggregator solana.PublicKey + // PDAs to avoid redundant lookups RouterConfigPDA solana.PublicKey SourceChainStatePDAs map[uint64]solana.PublicKey // deprecated @@ -92,6 +99,8 @@ func LoadChainStateSolana(chain deployment.SolChain, addresses map[string]deploy SourceChainStatePDAs: make(map[uint64]solana.PublicKey), DestChainStatePDAs: make(map[uint64]solana.PublicKey), SPL2022Tokens: make([]solana.PublicKey, 0), + SPLTokens: make([]solana.PublicKey, 0), + WSOL: solana.SolMint, TokenPoolLookupTable: make(map[solana.PublicKey]solana.PublicKey), } // Most programs upgraded in place, but some are not so we always want to @@ -116,9 +125,9 @@ func LoadChainStateSolana(chain deployment.SolChain, addresses map[string]deploy case SPL2022Tokens: pub := solana.MustPublicKeyFromBase58(address) state.SPL2022Tokens = append(state.SPL2022Tokens, pub) - case TokenPool: + case SPLTokens: pub := solana.MustPublicKeyFromBase58(address) - state.TokenPool = pub + state.SPLTokens = append(state.SPLTokens, pub) case RemoteSource: pub := solana.MustPublicKeyFromBase58(address) // Labels should only have one entry @@ -179,6 +188,12 @@ func LoadChainStateSolana(chain deployment.SolChain, addresses map[string]deploy case FeeAggregator: pub := solana.MustPublicKeyFromBase58(address) state.FeeAggregator = pub + case BurnMintTokenPool: + pub := solana.MustPublicKeyFromBase58(address) + state.BurnMintTokenPool = pub + case LockReleaseTokenPool: + pub := solana.MustPublicKeyFromBase58(address) + state.LockReleaseTokenPool = pub default: log.Warn().Str("address", address).Str("type", string(tvStr.Type)).Msg("Unknown address type") continue @@ -190,10 +205,29 @@ func LoadChainStateSolana(chain deployment.SolChain, addresses map[string]deploy } versions[tvStr.Type] = tvStr.Version } - state.WSOL = solana.SolMint return state, nil } +func (s SolCCIPChainState) TokenToTokenProgram(tokenAddress solana.PublicKey) (solana.PublicKey, error) { + if tokenAddress.Equals(s.LinkToken) { + return solana.Token2022ProgramID, nil + } + if tokenAddress.Equals(s.WSOL) { + return solana.TokenProgramID, nil + } + for _, spl2022Token := range s.SPL2022Tokens { + if spl2022Token.Equals(tokenAddress) { + return solana.Token2022ProgramID, nil + } + } + for _, splToken := range s.SPLTokens { + if splToken.Equals(tokenAddress) { + return solana.TokenProgramID, nil + } + } + return solana.PublicKey{}, fmt.Errorf("token program not found for token address %s", tokenAddress.String()) +} + func FindSolanaAddress(tv deployment.TypeAndVersion, addresses map[string]deployment.TypeAndVersion) solana.PublicKey { for address, tvStr := range addresses { if tv.String() == tvStr.String() { @@ -203,10 +237,3 @@ func FindSolanaAddress(tv deployment.TypeAndVersion, addresses map[string]deploy } return solana.PublicKey{} } - -func (c SolCCIPChainState) OnRampBytes() ([]byte, error) { - if !c.Router.IsZero() { - return c.Router.Bytes(), nil - } - return nil, errors.New("no onramp found in the state") -} diff --git a/deployment/ccip/changeset/state.go b/deployment/ccip/changeset/state.go index d813fb97d74..865521b7432 100644 --- a/deployment/ccip/changeset/state.go +++ b/deployment/ccip/changeset/state.go @@ -230,13 +230,6 @@ func (c CCIPChainState) LinkTokenAddress() (common.Address, error) { return common.Address{}, errors.New("no link token found in the state") } -func (c CCIPChainState) OnRampBytes() ([]byte, error) { - if c.OnRamp != nil { - return c.OnRamp.Address().Bytes(), nil - } - return nil, errors.New("no onramp found in the state") -} - func (c CCIPChainState) GenerateView() (view.ChainView, error) { chainView := view.NewChain() if c.Router != nil { @@ -574,7 +567,7 @@ func (s CCIPOnChainState) View(chains []uint64) (map[string]view.ChainView, erro return m, nil } -func (s CCIPOnChainState) GetOffRampAddress(chainSelector uint64) ([]byte, error) { +func (s CCIPOnChainState) GetOffRampAddressBytes(chainSelector uint64) ([]byte, error) { family, err := chain_selectors.GetSelectorFamily(chainSelector) if err != nil { return nil, err @@ -593,6 +586,31 @@ func (s CCIPOnChainState) GetOffRampAddress(chainSelector uint64) ([]byte, error return offRampAddress, nil } +func (s CCIPOnChainState) GetOnRampAddressBytes(chainSelector uint64) ([]byte, error) { + family, err := chain_selectors.GetSelectorFamily(chainSelector) + if err != nil { + return nil, err + } + + var onRampAddressBytes []byte + switch family { + case chain_selectors.FamilyEVM: + if s.Chains[chainSelector].OnRamp == nil { + return nil, fmt.Errorf("no onramp found in the state for chain %d", chainSelector) + } + onRampAddressBytes = s.Chains[chainSelector].OnRamp.Address().Bytes() + case chain_selectors.FamilySolana: + if s.SolChains[chainSelector].Router.IsZero() { + return nil, fmt.Errorf("no router found in the state for chain %d", chainSelector) + } + onRampAddressBytes = s.SolChains[chainSelector].Router.Bytes() + default: + return nil, fmt.Errorf("unsupported chain family %s", family) + } + + return onRampAddressBytes, nil +} + func (s CCIPOnChainState) ValidateRamp(chainSelector uint64, rampType deployment.ContractType) error { family, err := chain_selectors.GetSelectorFamily(chainSelector) if err != nil { diff --git a/deployment/ccip/changeset/testhelpers/test_environment.go b/deployment/ccip/changeset/testhelpers/test_environment.go index 520bacde3e6..cd8f009060c 100644 --- a/deployment/ccip/changeset/testhelpers/test_environment.go +++ b/deployment/ccip/changeset/testhelpers/test_environment.go @@ -21,14 +21,16 @@ import ( commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" + solBinary "github.com/gagliardetto/binary" + "github.com/smartcontractkit/chainlink-ccip/chainconfig" + solFeeQuoter "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/fee_quoter" cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" "github.com/smartcontractkit/chainlink-ccip/pluginconfig" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/globals" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" - changeset_solana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" + ccipChangeSetSolana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" @@ -526,7 +528,7 @@ func AddCCIPContractsToEnvironment(t *testing.T, allChains []uint64, tEnv TestEn // no proposals to be made, timelock can be passed as nil here var apps []commonchangeset.ConfiguredChangeSet evmContractParams := make(map[uint64]v1_6.ChainContractParams) - solContractParams := make(map[uint64]v1_6.ChainContractParams) + solContractParams := make(map[uint64]ccipChangeSetSolana.ChainContractParams) evmChains := []uint64{} for _, chain := range allChains { if _, ok := e.Env.Chains[chain]; ok { @@ -548,10 +550,40 @@ func AddCCIPContractsToEnvironment(t *testing.T, allChains []uint64, tEnv TestEn } } + value := [28]uint8{} + bigNum, ok := new(big.Int).SetString("19816680000000000000", 10) + require.True(t, ok) + bigNum.FillBytes(value[:]) + state, err := changeset.LoadOnchainState(e.Env) + require.NoError(t, err) for _, chain := range solChains { - solContractParams[chain] = v1_6.ChainContractParams{ - FeeQuoterParams: v1_6.DefaultFeeQuoterParams(), - OffRampParams: v1_6.DefaultOffRampParams(), + solContractParams[chain] = ccipChangeSetSolana.ChainContractParams{ + FeeQuoterParams: ccipChangeSetSolana.FeeQuoterParams{ + DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + BillingConfig: []solFeeQuoter.BillingTokenConfig{ + { + Enabled: true, + Mint: state.SolChains[chain].LinkToken, + UsdPerToken: solFeeQuoter.TimestampedPackedU224{ + Value: value, + Timestamp: int64(100), + }, + PremiumMultiplierWeiPerEth: 100, + }, + { + Enabled: true, + Mint: state.SolChains[chain].WSOL, + UsdPerToken: solFeeQuoter.TimestampedPackedU224{ + Value: value, + Timestamp: int64(100), + }, + PremiumMultiplierWeiPerEth: 100, + }, + }, + }, + OffRampParams: ccipChangeSetSolana.OffRampParams{ + EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), + }, } } @@ -576,19 +608,17 @@ func AddCCIPContractsToEnvironment(t *testing.T, allChains []uint64, tEnv TestEn }, ), commonchangeset.Configure( - deployment.CreateLegacyChangeSet(solana.DeployChainContractsChangesetSolana), - solana.DeployChainContractsConfigSolana{ - DeployChainContractsConfig: v1_6.DeployChainContractsConfig{ - HomeChainSelector: e.HomeChainSel, - ContractParamsPerChain: solContractParams, - }, + deployment.CreateLegacyChangeSet(ccipChangeSetSolana.DeployChainContractsChangeset), + ccipChangeSetSolana.DeployChainContractsConfig{ + HomeChainSelector: e.HomeChainSel, + ContractParamsPerChain: solContractParams, }, ), }...) e.Env, err = commonchangeset.ApplyChangesets(t, e.Env, nil, apps) require.NoError(t, err) - state, err := changeset.LoadOnchainState(e.Env) + state, err = changeset.LoadOnchainState(e.Env) require.NoError(t, err) // Assert link present if tc.IsStaticLink { @@ -772,7 +802,7 @@ func AddCCIPContractsToEnvironment(t *testing.T, allChains []uint64, tEnv TestEn ), commonchangeset.Configure( // Enable the OCR config on the remote chains. - deployment.CreateLegacyChangeSet(changeset_solana.SetOCR3ConfigSolana), + deployment.CreateLegacyChangeSet(ccipChangeSetSolana.SetOCR3ConfigSolana), v1_6.SetOCR3OffRampConfig{ HomeChainSel: e.HomeChainSel, RemoteChainSels: solChains, diff --git a/deployment/ccip/changeset/testhelpers/test_helpers.go b/deployment/ccip/changeset/testhelpers/test_helpers.go index f6e2b4fb53a..33683f0c217 100644 --- a/deployment/ccip/changeset/testhelpers/test_helpers.go +++ b/deployment/ccip/changeset/testhelpers/test_helpers.go @@ -20,7 +20,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - changeset_solana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" + ccipChangeSetSolana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6" commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" @@ -455,10 +455,10 @@ func addLaneSolanaChangesets(t *testing.T, solChainSelector, remoteChainSelector } solanaChangesets := []commoncs.ConfiguredChangeSet{ commoncs.Configure( - deployment.CreateLegacyChangeSet(changeset_solana.AddRemoteChainToSolana), - changeset_solana.AddRemoteChainToSolanaConfig{ + deployment.CreateLegacyChangeSet(ccipChangeSetSolana.AddRemoteChainToSolana), + ccipChangeSetSolana.AddRemoteChainToSolanaConfig{ ChainSelector: solChainSelector, - UpdatesByChain: map[uint64]changeset_solana.RemoteChainConfigSolana{ + UpdatesByChain: map[uint64]ccipChangeSetSolana.RemoteChainConfigSolana{ remoteChainSelector: { EnabledAsSource: true, RouterDestinationConfig: solRouter.DestChainConfig{}, @@ -864,10 +864,10 @@ func DeployTransferableTokenSolana( e, err = commoncs.Apply(t, e, nil, commoncs.Configure( // this makes the deployer the mint authority by default - deployment.CreateLegacyChangeSet(changeset_solana.DeploySolanaToken), - changeset_solana.DeploySolanaTokenConfig{ + deployment.CreateLegacyChangeSet(ccipChangeSetSolana.DeploySolanaToken), + ccipChangeSetSolana.DeploySolanaTokenConfig{ ChainSelector: solChainSel, - TokenProgramName: deployment.SPL2022Tokens, + TokenProgramName: changeset.SPL2022Tokens, TokenDecimals: 9, }, ), @@ -881,21 +881,20 @@ func DeployTransferableTokenSolana( e, err = commoncs.Apply(t, e, nil, commoncs.Configure( // create the ata for the deployerKey - deployment.CreateLegacyChangeSet(changeset_solana.CreateSolanaTokenATA), - changeset_solana.CreateSolanaTokenATAConfig{ + deployment.CreateLegacyChangeSet(ccipChangeSetSolana.CreateSolanaTokenATA), + ccipChangeSetSolana.CreateSolanaTokenATAConfig{ ChainSelector: solChainSel, TokenPubkey: solTokenAddress, - TokenProgram: deployment.SPL2022Tokens, + TokenProgram: changeset.SPL2022Tokens, ATAList: []string{solDeployerKey.String()}, }, ), commoncs.Configure( // mint the token to the deployerKey - deployment.CreateLegacyChangeSet(changeset_solana.MintSolanaToken), - changeset_solana.MintSolanaTokenConfig{ + deployment.CreateLegacyChangeSet(ccipChangeSetSolana.MintSolanaToken), + ccipChangeSetSolana.MintSolanaTokenConfig{ ChainSelector: solChainSel, TokenPubkey: solTokenAddress.String(), - TokenProgram: deployment.SPL2022Tokens, AmountToAddress: map[string]uint64{ solDeployerKey.String(): uint64(1000), }, @@ -903,20 +902,19 @@ func DeployTransferableTokenSolana( ), commoncs.Configure( // deploy token pool and set the burn/mint authority to the tokenPool - deployment.CreateLegacyChangeSet(changeset_solana.AddTokenPool), - changeset_solana.TokenPoolConfig{ - ChainSelector: solChainSel, - TokenPubKey: solTokenAddress.String(), - TokenProgramName: deployment.SPL2022Tokens, - PoolType: solTestTokenPool.BurnAndMint_PoolType, - Authority: solDeployerKey.String(), + deployment.CreateLegacyChangeSet(ccipChangeSetSolana.AddTokenPool), + ccipChangeSetSolana.TokenPoolConfig{ + ChainSelector: solChainSel, + TokenPubKey: solTokenAddress.String(), + PoolType: solTestTokenPool.BurnAndMint_PoolType, + Authority: solDeployerKey.String(), }, ), ) require.NoError(t, err) // configure evm - poolConfigPDA, err := solTokenUtil.TokenPoolConfigAddress(solTokenAddress, state.SolChains[solChainSel].TokenPool) + poolConfigPDA, err := solTokenUtil.TokenPoolConfigAddress(solTokenAddress, state.SolChains[solChainSel].BurnMintTokenPool) require.NoError(t, err) err = setTokenPoolCounterPart(e.Chains[evmChainSel], evmPool, evmDeployer, solChainSel, solTokenAddress.Bytes(), poolConfigPDA.Bytes()) require.NoError(t, err) @@ -927,8 +925,8 @@ func DeployTransferableTokenSolana( // configure solana e, err = commoncs.Apply(t, e, nil, commoncs.Configure( - deployment.CreateLegacyChangeSet(changeset_solana.SetupTokenPoolForRemoteChain), - changeset_solana.RemoteChainTokenPoolConfig{ + deployment.CreateLegacyChangeSet(ccipChangeSetSolana.SetupTokenPoolForRemoteChain), + ccipChangeSetSolana.RemoteChainTokenPoolConfig{ SolChainSelector: solChainSel, RemoteChainSelector: evmChainSel, SolTokenPubKey: solTokenAddress.String(), @@ -956,6 +954,22 @@ func DeployTransferableTokenSolana( }, }, ), + commoncs.Configure( + deployment.CreateLegacyChangeSet(ccipChangeSetSolana.AddBillingTokenForRemoteChain), + ccipChangeSetSolana.BillingTokenForRemoteChainConfig{ + ChainSelector: solChainSel, + RemoteChainSelector: evmChainSel, + TokenPubKey: solTokenAddress.String(), + Config: solFeeQuoter.TokenTransferFeeConfig{ + MinFeeUsdcents: 800, + MaxFeeUsdcents: 1600, + DeciBps: 0, + DestGasOverhead: 100, + DestBytesOverhead: 100, + IsEnabled: true, + }, + }, + ), ) require.NoError(t, err) @@ -1542,6 +1556,12 @@ func SavePreloadedSolAddresses(t *testing.T, e deployment.Environment, solChainS tv = deployment.NewTypeAndVersion(changeset.OffRamp, deployment.Version1_0_0) err = e.ExistingAddresses.Save(solChainSelector, solTestConfig.CcipOfframpProgram.String(), tv) require.NoError(t, err) + tv = deployment.NewTypeAndVersion(changeset.BurnMintTokenPool, deployment.Version1_0_0) + err = e.ExistingAddresses.Save(solChainSelector, "TokenPooL11111111111111111111111111BurnMint", tv) + require.NoError(t, err) + tv = deployment.NewTypeAndVersion(changeset.LockReleaseTokenPool, deployment.Version1_0_0) + err = e.ExistingAddresses.Save(solChainSelector, "TokenPooL11111111111111111111111LockReLease", tv) + require.NoError(t, err) tv = deployment.NewTypeAndVersion(commontypes.ManyChainMultisigProgram, deployment.Version1_0_0) err = e.ExistingAddresses.Save(solChainSelector, memory.SolanaProgramIDs["mcm"], tv) require.NoError(t, err) diff --git a/deployment/ccip/changeset/v1_6/cs_ccip_home.go b/deployment/ccip/changeset/v1_6/cs_ccip_home.go index 699e66f51bb..c93865e9b0d 100644 --- a/deployment/ccip/changeset/v1_6/cs_ccip_home.go +++ b/deployment/ccip/changeset/v1_6/cs_ccip_home.go @@ -632,7 +632,7 @@ func AddDonAndSetCandidateChangeset( } var donMcmsTxs []mcmstypes.Transaction for chainSelector, params := range cfg.PluginInfo.OCRConfigPerRemoteChainSelector { - offRampAddress, err := state.GetOffRampAddress(chainSelector) + offRampAddress, err := state.GetOffRampAddressBytes(chainSelector) if err != nil { return deployment.ChangesetOutput{}, err } @@ -827,7 +827,7 @@ func SetCandidateChangeset( for _, plugin := range cfg.PluginInfo { pluginInfos = append(pluginInfos, plugin.String()) for chainSelector, params := range plugin.OCRConfigPerRemoteChainSelector { - offRampAddress, err := state.GetOffRampAddress(chainSelector) + offRampAddress, err := state.GetOffRampAddressBytes(chainSelector) if err != nil { return deployment.ChangesetOutput{}, err } diff --git a/deployment/ccip/changeset/v1_6/cs_chain_contracts.go b/deployment/ccip/changeset/v1_6/cs_chain_contracts.go index bd45ea24e94..fdb66bf6087 100644 --- a/deployment/ccip/changeset/v1_6/cs_chain_contracts.go +++ b/deployment/ccip/changeset/v1_6/cs_chain_contracts.go @@ -1154,15 +1154,8 @@ func UpdateOffRampSourcesChangeset(e deployment.Environment, cfg UpdateOffRampSo } else { router = state.Chains[chainSel].Router.Address() } - sourceChainFamily, _ := chain_selectors.GetSelectorFamily(source) - - onRampBytes := []byte{} // can ignore err as validation checks for nil addresses - if sourceChainFamily == chain_selectors.FamilyEVM { - onRampBytes, _ = state.Chains[source].OnRampBytes() - } else if sourceChainFamily == chain_selectors.FamilySolana { - onRampBytes, _ = state.SolChains[source].OnRampBytes() - } + onRampBytes, _ := state.GetOnRampAddressBytes(source) args = append(args, offramp.OffRampSourceChainConfigArgs{ SourceChainSelector: source, diff --git a/deployment/environment/memory/chain.go b/deployment/environment/memory/chain.go index b6fd091ec12..2c91c86ab8a 100644 --- a/deployment/environment/memory/chain.go +++ b/deployment/environment/memory/chain.go @@ -226,15 +226,17 @@ func evmChain(t *testing.T, numUsers int) EVMChain { } var SolanaProgramIDs = map[string]string{ - "ccip_router": solTestConfig.CcipRouterProgram.String(), - "test_token_pool": solTestConfig.CcipTokenPoolProgram.String(), - "fee_quoter": solTestConfig.FeeQuoterProgram.String(), - "test_ccip_receiver": solTestConfig.CcipLogicReceiver.String(), - "ccip_offramp": solTestConfig.CcipOfframpProgram.String(), - "mcm": solTestConfig.McmProgram.String(), - "timelock": solTestConfig.TimelockProgram.String(), - "access_controller": solTestConfig.AccessControllerProgram.String(), - "external_program_cpi_stub": solTestConfig.ExternalCpiStubProgram.String(), + "ccip_router": solTestConfig.CcipRouterProgram.String(), + "test_token_pool": solTestConfig.CcipTokenPoolProgram.String(), + "example_burnmint_token_pool": "TokenPooL11111111111111111111111111BurnMint", + "example_lockrelease_token_pool": "TokenPooL11111111111111111111111LockReLease", + "fee_quoter": solTestConfig.FeeQuoterProgram.String(), + "test_ccip_receiver": solTestConfig.CcipLogicReceiver.String(), + "ccip_offramp": solTestConfig.CcipOfframpProgram.String(), + "mcm": solTestConfig.McmProgram.String(), + "timelock": solTestConfig.TimelockProgram.String(), + "access_controller": solTestConfig.AccessControllerProgram.String(), + "external_program_cpi_stub": solTestConfig.ExternalCpiStubProgram.String(), } var once = &sync.Once{} diff --git a/deployment/solana_chain.go b/deployment/solana_chain.go index bdf9f42b4c4..c38bcb403de 100644 --- a/deployment/solana_chain.go +++ b/deployment/solana_chain.go @@ -15,27 +15,16 @@ import ( solRpc "github.com/gagliardetto/solana-go/rpc" "github.com/pkg/errors" - solBinary "github.com/gagliardetto/binary" "github.com/gagliardetto/solana-go/rpc" solCommonUtil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" "github.com/smartcontractkit/chainlink-common/pkg/logger" - - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/globals" -) - -var ( - SolDefaultGasLimit = solBinary.Uint128{Lo: 3000, Hi: 0, Endianness: nil} - SolDefaultMaxFeeJuelsPerMsg = solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil} - EnableExecutionAfter = int64(globals.PermissionLessExecutionThreshold.Seconds()) ) const ( ProgramIDPrefix = "Program Id: " BufferIDPrefix = "Buffer: " SolDefaultCommitment = rpc.CommitmentConfirmed - SPL2022Tokens = "SPL2022Tokens" - SPLTokens = "SPLTokens" ) // SolChain represents a Solana chain. From 29f27b8520ac4dab5bfc93ada7be8ebf751c0d49 Mon Sep 17 00:00:00 2001 From: Austin <107539019+0xAustinWang@users.noreply.github.com> Date: Mon, 24 Feb 2025 22:49:56 +0800 Subject: [PATCH 04/14] add fee limit to load test (#16537) * set add fee limit to load test * add comment --- integration-tests/load/ccip/destination_gun.go | 5 +++-- integration-tests/testconfig/ccip/ccip.toml | 9 +++++---- integration-tests/testconfig/ccip/load.go | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/integration-tests/load/ccip/destination_gun.go b/integration-tests/load/ccip/destination_gun.go index 095e6b68d38..e38cdf7ff59 100644 --- a/integration-tests/load/ccip/destination_gun.go +++ b/integration-tests/load/ccip/destination_gun.go @@ -92,7 +92,6 @@ func (m *DestinationGun) Validate() error { func (m *DestinationGun) Call(_ *wasp.Generator) *wasp.Response { m.roundNum.Add(1) - requestedRound := m.roundNum.Load() waspGroup := fmt.Sprintf("%d-%s", m.chainSelector, "messageOnly") @@ -129,10 +128,12 @@ func (m *DestinationGun) Call(_ *wasp.Generator) *wasp.Response { acc.Value = fee defer func() { acc.Value = nil }() } + // todo: remove once CCIP-5143 is implemented + acc.GasLimit = *m.testConfig.GasLimit + m.l.Debugw("sending message ", "srcChain", src, "dstChain", m.chainSelector, - "round", requestedRound, "fee", fee, "msg", msg) tx, err := r.CcipSend( diff --git a/integration-tests/testconfig/ccip/ccip.toml b/integration-tests/testconfig/ccip/ccip.toml index 32e21beaff1..d1241a6ea1a 100644 --- a/integration-tests/testconfig/ccip/ccip.toml +++ b/integration-tests/testconfig/ccip/ccip.toml @@ -243,12 +243,13 @@ ephemeral_addresses_number = 0 [Load.CCIP.Load] # MessageTypeWeights corresponds with [data only, token only, message with token] -MessageTypeWeights = [100,0,0] +MessageTypeWeights = [10,45,45] # each destination chain will receive 1 incoming request per RequestFrequency for the duration of LoadDuration RequestFrequency = "5s" -LoadDuration = "5h" +LoadDuration = "1h" # destination chain selectors to send messages to -NumDestinationChains = 8 +NumDestinationChains = 6 # Directory where we receive environment configuration from crib CribEnvDirectory = "../../../../crib/deployments/ccip-v2/.tmp" -TimeoutDuration = "30m" \ No newline at end of file +TimeoutDuration = "30m" +GasLimit = 700_000 \ No newline at end of file diff --git a/integration-tests/testconfig/ccip/load.go b/integration-tests/testconfig/ccip/load.go index 5378ecf1412..08653fcbf6c 100644 --- a/integration-tests/testconfig/ccip/load.go +++ b/integration-tests/testconfig/ccip/load.go @@ -17,6 +17,7 @@ type LoadConfig struct { NumDestinationChains *int TimeoutDuration *string TestLabel *string + GasLimit *uint64 } func (l *LoadConfig) Validate(t *testing.T, e *deployment.Environment) { From 8196f5b7c8b6103ab48a505f75d996d74514fb51 Mon Sep 17 00:00:00 2001 From: Yashvardhan Nevatia Date: Mon, 24 Feb 2025 17:05:22 +0000 Subject: [PATCH 05/14] add remote chain (#16540) * add remote chain * add remote chain * wip * wip * wip * lint --------- Co-authored-by: Terry Tata --- .../changeset/solana/cs_add_remote_chain.go | 173 +++++++-- .../ccip/changeset/solana/cs_build_solana.go | 55 ++- .../solana/cs_chain_contracts_test.go | 68 ++++ .../ccip/changeset/solana/cs_deploy_chain.go | 367 +++++++----------- .../changeset/solana/cs_deploy_chain_test.go | 284 ++++++-------- .../solana/ownership_transfer_helpers.go | 17 +- ...ransfer_ccip_to_mcms_with_timelock_test.go | 40 +- deployment/ccip/changeset/solana/utils.go | 27 ++ deployment/ccip/changeset/solana_state.go | 60 +++ .../changeset/testhelpers/test_helpers.go | 59 +++ .../ccip/changeset/v1_6/cs_chain_contracts.go | 17 +- .../changeset/deploy_mcms_with_timelock.go | 17 +- 12 files changed, 685 insertions(+), 499 deletions(-) diff --git a/deployment/ccip/changeset/solana/cs_add_remote_chain.go b/deployment/ccip/changeset/solana/cs_add_remote_chain.go index fcf8ffe1465..e12d69badc0 100644 --- a/deployment/ccip/changeset/solana/cs_add_remote_chain.go +++ b/deployment/ccip/changeset/solana/cs_add_remote_chain.go @@ -2,13 +2,18 @@ package solana import ( "context" - // "errors" + "fmt" "strconv" "github.com/ethereum/go-ethereum/common" "github.com/gagliardetto/solana-go" + "github.com/smartcontractkit/mcms" + "github.com/smartcontractkit/mcms/sdk" + mcmsSolana "github.com/smartcontractkit/mcms/sdk/solana" + mcmsTypes "github.com/smartcontractkit/mcms/types" + solOffRamp "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_offramp" solRouter "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" solFeeQuoter "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/fee_quoter" @@ -17,8 +22,9 @@ import ( "github.com/smartcontractkit/chainlink/deployment" ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" - commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" + + "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" ) // ADD REMOTE CHAIN @@ -29,6 +35,11 @@ type AddRemoteChainToSolanaConfig struct { // Disallow mixing MCMS/non-MCMS per chain for simplicity. // (can still be achieved by calling this function multiple times) MCMS *ccipChangeset.MCMSConfig + // Public key of program authorities. Depending on when this changeset is called, some may be under + // the control of the deployer, and some may be under the control of the timelock. (e.g. during new offramp deploy) + RouterAuthority solana.PublicKey + FeeQuoterAuthority solana.PublicKey + OffRampAuthority solana.PublicKey } type RemoteChainConfigSolana struct { @@ -55,19 +66,23 @@ func (cfg AddRemoteChainToSolanaConfig) Validate(e deployment.Environment) error if err := validateOffRampConfig(chain, chainState); err != nil { return err } + if err := ValidateMCMSConfig(e, cfg.ChainSelector, cfg.MCMS); err != nil { + return err + } + routerUsingMCMS := cfg.MCMS != nil && !cfg.RouterAuthority.IsZero() + feeQuoterUsingMCMS := cfg.MCMS != nil && !cfg.FeeQuoterAuthority.IsZero() + offRampUsingMCMS := cfg.MCMS != nil && !cfg.OffRampAuthority.IsZero() chain, ok := e.SolChains[cfg.ChainSelector] if !ok { return fmt.Errorf("chain %d not found in environment", cfg.ChainSelector) } - addresses, err := e.ExistingAddresses.AddressesForChain(cfg.ChainSelector) - if err != nil { - return err + if err := ccipChangeset.ValidateOwnershipSolana(&e, chain, routerUsingMCMS, e.SolChains[cfg.ChainSelector].DeployerKey.PublicKey(), chainState.Router, ccipChangeset.Router); err != nil { + return fmt.Errorf("failed to validate ownership: %w", err) } - mcmState, err := commonState.MaybeLoadMCMSWithTimelockChainStateSolana(chain, addresses) - if err != nil { - return fmt.Errorf("error loading MCMS state for chain %d: %w", cfg.ChainSelector, err) + if err := ccipChangeset.ValidateOwnershipSolana(&e, chain, feeQuoterUsingMCMS, e.SolChains[cfg.ChainSelector].DeployerKey.PublicKey(), chainState.FeeQuoter, ccipChangeset.FeeQuoter); err != nil { + return fmt.Errorf("failed to validate ownership: %w", err) } - if err := commoncs.ValidateOwnershipSolana(e.GetContext(), cfg.MCMS != nil, e.SolChains[cfg.ChainSelector].DeployerKey.PublicKey(), mcmState.TimelockProgram, mcmState.TimelockSeed, chainState.Router); err != nil { + if err := ccipChangeset.ValidateOwnershipSolana(&e, chain, offRampUsingMCMS, e.SolChains[cfg.ChainSelector].DeployerKey.PublicKey(), chainState.OffRamp, ccipChangeset.OffRamp); err != nil { return fmt.Errorf("failed to validate ownership: %w", err) } var routerConfigAccount solRouter.Config @@ -110,23 +125,66 @@ func AddRemoteChainToSolana(e deployment.Environment, cfg AddRemoteChainToSolana } ab := deployment.NewMemoryAddressBook() - err = doAddRemoteChainToSolana(e, s, cfg.ChainSelector, cfg.UpdatesByChain, ab) + txns, err := doAddRemoteChainToSolana(e, s, cfg, ab) if err != nil { return deployment.ChangesetOutput{AddressBook: ab}, err } + + // create proposals for ixns + if len(txns) > 0 { + timelocks := map[uint64]string{} + proposers := map[uint64]string{} + inspectors := map[uint64]sdk.Inspector{} + batches := make([]mcmsTypes.BatchOperation, 0) + chain := e.SolChains[cfg.ChainSelector] + addresses, _ := e.ExistingAddresses.AddressesForChain(cfg.ChainSelector) + mcmState, _ := state.MaybeLoadMCMSWithTimelockChainStateSolana(chain, addresses) + + timelocks[cfg.ChainSelector] = mcmsSolana.ContractAddress( + mcmState.TimelockProgram, + mcmsSolana.PDASeed(mcmState.TimelockSeed), + ) + proposers[cfg.ChainSelector] = mcmsSolana.ContractAddress(mcmState.McmProgram, mcmsSolana.PDASeed(mcmState.ProposerMcmSeed)) + inspectors[cfg.ChainSelector] = mcmsSolana.NewInspector(chain.Client) + batches = append(batches, mcmsTypes.BatchOperation{ + ChainSelector: mcmsTypes.ChainSelector(cfg.ChainSelector), + Transactions: txns, + }) + proposal, err := proposalutils.BuildProposalFromBatchesV2( + e.GetContext(), + timelocks, + proposers, + inspectors, + batches, + "proposal to add remote chains to Solana", + cfg.MCMS.MinDelay) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to build proposal: %w", err) + } + return deployment.ChangesetOutput{ + MCMSTimelockProposals: []mcms.TimelockProposal{*proposal}, + AddressBook: ab, + }, nil + } return deployment.ChangesetOutput{AddressBook: ab}, nil } func doAddRemoteChainToSolana( e deployment.Environment, s ccipChangeset.CCIPOnChainState, - chainSel uint64, - updates map[uint64]RemoteChainConfigSolana, - ab deployment.AddressBook) error { + cfg AddRemoteChainToSolanaConfig, + ab deployment.AddressBook) ([]mcmsTypes.Transaction, error) { + txns := make([]mcmsTypes.Transaction, 0) + ixns := make([]solana.Instruction, 0) + chainSel := cfg.ChainSelector + updates := cfg.UpdatesByChain chain := e.SolChains[chainSel] ccipRouterID := s.SolChains[chainSel].Router feeQuoterID := s.SolChains[chainSel].FeeQuoter offRampID := s.SolChains[chainSel].OffRamp + routerUsingMCMS := cfg.MCMS != nil && !cfg.RouterAuthority.IsZero() + feeQuoterUsingMCMS := cfg.MCMS != nil && !cfg.FeeQuoterAuthority.IsZero() + offRampUsingMCMS := cfg.MCMS != nil && !cfg.OffRampAuthority.IsZero() lookUpTableEntries := make([]solana.PublicKey, 0) for remoteChainSel, update := range updates { @@ -149,16 +207,31 @@ func doAddRemoteChainToSolana( ) solRouter.SetProgramID(ccipRouterID) + var authority solana.PublicKey + if routerUsingMCMS { + authority = cfg.RouterAuthority + } else { + authority = chain.DeployerKey.PublicKey() + } routerIx, err := solRouter.NewAddChainSelectorInstruction( remoteChainSel, update.RouterDestinationConfig, routerRemoteStatePDA, s.SolChains[chainSel].RouterConfigPDA, - chain.DeployerKey.PublicKey(), + authority, solana.SystemProgramID, ).ValidateAndBuild() if err != nil { - return fmt.Errorf("failed to generate instructions: %w", err) + return txns, fmt.Errorf("failed to generate instructions: %w", err) + } + if routerUsingMCMS { + tx, err := BuildMCMSTxn(routerIx, ccipRouterID.String(), ccipChangeset.Router) + if err != nil { + return txns, fmt.Errorf("failed to create transaction: %w", err) + } + txns = append(txns, *tx) + } else { + ixns = append(ixns, routerIx) } routerOfframpIx, err := solRouter.NewAddOfframpInstruction( @@ -166,24 +239,47 @@ func doAddRemoteChainToSolana( offRampID, allowedOffRampRemotePDA, s.SolChains[chainSel].RouterConfigPDA, - chain.DeployerKey.PublicKey(), + authority, solana.SystemProgramID, ).ValidateAndBuild() if err != nil { - return fmt.Errorf("failed to generate instructions: %w", err) + return txns, fmt.Errorf("failed to generate instructions: %w", err) + } + if routerUsingMCMS { + tx, err := BuildMCMSTxn(routerOfframpIx, ccipRouterID.String(), ccipChangeset.Router) + if err != nil { + return txns, fmt.Errorf("failed to create transaction: %w", err) + } + txns = append(txns, *tx) + } else { + ixns = append(ixns, routerOfframpIx) } solFeeQuoter.SetProgramID(feeQuoterID) + if feeQuoterUsingMCMS { + authority = cfg.RouterAuthority + } else { + authority = chain.DeployerKey.PublicKey() + } feeQuoterIx, err := solFeeQuoter.NewAddDestChainInstruction( remoteChainSel, update.FeeQuoterDestinationConfig, s.SolChains[chainSel].FeeQuoterConfigPDA, fqRemoteChainPDA, - chain.DeployerKey.PublicKey(), + authority, solana.SystemProgramID, ).ValidateAndBuild() if err != nil { - return fmt.Errorf("failed to generate instructions: %w", err) + return txns, fmt.Errorf("failed to generate instructions: %w", err) + } + if feeQuoterUsingMCMS { + tx, err := BuildMCMSTxn(feeQuoterIx, feeQuoterID.String(), ccipChangeset.FeeQuoter) + if err != nil { + return txns, fmt.Errorf("failed to create transaction: %w", err) + } + txns = append(txns, *tx) + } else { + ixns = append(ixns, feeQuoterIx) } solOffRamp.SetProgramID(offRampID) @@ -191,22 +287,37 @@ func doAddRemoteChainToSolana( OnRamp: [2][64]byte{onRampBytes, [64]byte{}}, IsEnabled: update.EnabledAsSource, } + if offRampUsingMCMS { + authority = cfg.RouterAuthority + } else { + authority = chain.DeployerKey.PublicKey() + } offRampIx, err := solOffRamp.NewAddSourceChainInstruction( remoteChainSel, validSourceChainConfig, offRampRemoteStatePDA, s.SolChains[chainSel].OffRampConfigPDA, - chain.DeployerKey.PublicKey(), + authority, solana.SystemProgramID, ).ValidateAndBuild() if err != nil { - return fmt.Errorf("failed to generate instructions: %w", err) + return txns, fmt.Errorf("failed to generate instructions: %w", err) } - - err = chain.Confirm([]solana.Instruction{routerIx, routerOfframpIx, feeQuoterIx, offRampIx}) - if err != nil { - return fmt.Errorf("failed to confirm instructions: %w", err) + if offRampUsingMCMS { + tx, err := BuildMCMSTxn(offRampIx, offRampID.String(), ccipChangeset.OffRamp) + if err != nil { + return txns, fmt.Errorf("failed to create transaction: %w", err) + } + txns = append(txns, *tx) + } else { + ixns = append(ixns, offRampIx) + } + if len(ixns) > 0 { + err = chain.Confirm(ixns) + if err != nil { + return txns, fmt.Errorf("failed to confirm instructions: %w", err) + } } tv := deployment.NewTypeAndVersion(ccipChangeset.RemoteDest, deployment.Version1_0_0) @@ -214,20 +325,20 @@ func doAddRemoteChainToSolana( tv.AddLabel(remoteChainSelStr) err = ab.Save(chainSel, routerRemoteStatePDA.String(), tv) if err != nil { - return fmt.Errorf("failed to save dest chain state to address book: %w", err) + return txns, fmt.Errorf("failed to save dest chain state to address book: %w", err) } tv = deployment.NewTypeAndVersion(ccipChangeset.RemoteSource, deployment.Version1_0_0) tv.AddLabel(remoteChainSelStr) err = ab.Save(chainSel, allowedOffRampRemotePDA.String(), tv) if err != nil { - return fmt.Errorf("failed to save source chain state to address book: %w", err) + return txns, fmt.Errorf("failed to save source chain state to address book: %w", err) } } addressLookupTable, err := ccipChangeset.FetchOfframpLookupTable(e.GetContext(), chain, offRampID) if err != nil { - return fmt.Errorf("failed to get offramp reference addresses: %w", err) + return txns, fmt.Errorf("failed to get offramp reference addresses: %w", err) } if err := solCommonUtil.ExtendLookupTable( @@ -237,8 +348,8 @@ func doAddRemoteChainToSolana( *chain.DeployerKey, lookUpTableEntries, ); err != nil { - return fmt.Errorf("failed to extend lookup table: %w", err) + return txns, fmt.Errorf("failed to extend lookup table: %w", err) } - return nil + return txns, nil } diff --git a/deployment/ccip/changeset/solana/cs_build_solana.go b/deployment/ccip/changeset/solana/cs_build_solana.go index 4430d511ae5..c8d2c6bdabc 100644 --- a/deployment/ccip/changeset/solana/cs_build_solana.go +++ b/deployment/ccip/changeset/solana/cs_build_solana.go @@ -6,10 +6,12 @@ import ( "os" "os/exec" "path/filepath" + "regexp" chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/deployment" + cs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" ) var _ deployment.ChangeSet[BuildSolanaConfig] = BuildSolanaChangeset @@ -22,6 +24,13 @@ const ( deployDir = "chains/solana/contracts/target/deploy" ) +// Map program names to their Rust file paths (relative to the Anchor project root) +// Needed for upgrades in place +var programToFileMap = map[deployment.ContractType]string{ + cs.Router: "programs/ccip-router/src/lib.rs", + cs.FeeQuoter: "programs/fee-quoter/src/lib.rs", +} + // Run a command in a specific directory func runCommand(command string, args []string, workDir string) (string, error) { cmd := exec.Command(command, args...) @@ -37,8 +46,14 @@ func runCommand(command string, args []string, workDir string) (string, error) { } // Clone and checkout the specific revision of the repo -func cloneRepo(e deployment.Environment, revision string) error { +func cloneRepo(e deployment.Environment, revision string, forceClean bool) error { // Check if the repository already exists + if forceClean { + e.Logger.Debugw("Cleaning repository", "dir", cloneDir) + if err := os.RemoveAll(cloneDir); err != nil { + return fmt.Errorf("failed to clean repository: %w", err) + } + } if _, err := os.Stat(filepath.Join(cloneDir, ".git")); err == nil { e.Logger.Debugw("Repository already exists, discarding local changes and updating", "dir", cloneDir) @@ -83,6 +98,32 @@ func replaceKeys(e deployment.Environment) error { return nil } +func replaceKeysForUpgrade(e deployment.Environment, keys map[deployment.ContractType]string) error { + e.Logger.Debug("Replacing keys in Rust files...") + for program, key := range keys { + programStr := string(program) + filePath, exists := programToFileMap[program] + if !exists { + return fmt.Errorf("no file path found for program %s", programStr) + } + + fullPath := filepath.Join(cloneDir, anchorDir, filePath) + content, err := os.ReadFile(fullPath) + if err != nil { + return fmt.Errorf("failed to read file %s: %w", fullPath, err) + } + + // Replace declare_id!("..."); with the new key + updatedContent := regexp.MustCompile(`declare_id!\(".*?"\);`).ReplaceAllString(string(content), fmt.Sprintf(`declare_id!("%s");`, key)) + err = os.WriteFile(fullPath, []byte(updatedContent), 0600) + if err != nil { + return fmt.Errorf("failed to write updated keys to file %s: %w", fullPath, err) + } + e.Logger.Debugw("Updated key for program %s in file %s\n", programStr, filePath) + } + return nil +} + func copyFile(srcFile string, destDir string) error { output, err := runCommand("cp", []string{srcFile, destDir}, ".") if err != nil { @@ -108,6 +149,9 @@ type BuildSolanaConfig struct { DestinationDir string CleanDestinationDir bool CreateDestinationDir bool + // Forces re-clone of git directory. Useful for forcing regeneration of keys + CleanGitDir bool + UpgradeKeys map[deployment.ContractType]string } func BuildSolanaChangeset(e deployment.Environment, config BuildSolanaConfig) (deployment.ChangesetOutput, error) { @@ -124,14 +168,21 @@ func BuildSolanaChangeset(e deployment.Environment, config BuildSolanaConfig) (d } // Clone the repository - if err := cloneRepo(e, config.GitCommitSha); err != nil { + if err := cloneRepo(e, config.GitCommitSha, config.CleanGitDir); err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("error cloning repo: %w", err) } + // Replace keys in Rust files using anchor keys sync if err := replaceKeys(e); err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("error replacing keys: %w", err) } + // Replace keys in Rust files for upgrade by replacing the declare_id!() macro explicitly + // We need to do this so the keys will match the existing deployed program + if err := replaceKeysForUpgrade(e, config.UpgradeKeys); err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("error replacing keys for upgrade: %w", err) + } + // Build the project with Anchor if err := buildProject(e); err != nil { return deployment.ChangesetOutput{}, fmt.Errorf("error building project: %w", err) diff --git a/deployment/ccip/changeset/solana/cs_chain_contracts_test.go b/deployment/ccip/changeset/solana/cs_chain_contracts_test.go index 5132bbec813..b35f0aab139 100644 --- a/deployment/ccip/changeset/solana/cs_chain_contracts_test.go +++ b/deployment/ccip/changeset/solana/cs_chain_contracts_test.go @@ -3,6 +3,7 @@ package solana_test import ( "math/big" "testing" + "time" "github.com/gagliardetto/solana-go" "github.com/stretchr/testify/require" @@ -53,6 +54,7 @@ func TestAddRemoteChain(t *testing.T) { tenv, _ := testhelpers.NewMemoryEnvironment(t, testhelpers.WithSolChains(1)) evmChain := tenv.Env.AllChainSelectors()[0] + evmChain2 := tenv.Env.AllChainSelectors()[1] solChain := tenv.Env.AllChainSelectorsSolana()[0] _, err := ccipChangeset.LoadOnchainStateSolana(tenv.Env) @@ -114,6 +116,72 @@ func TestAddRemoteChain(t *testing.T) { require.NoError(t, err, "failed to get account info") require.Equal(t, solFeeQuoter.TimestampedPackedU224{}, destChainFqAccount.State.UsdPerUnitGas) require.True(t, destChainFqAccount.Config.IsEnabled) + + timelockSignerPDA, _ := testhelpers.TransferOwnershipSolana(t, &tenv.Env, solChain, true, true, true, true) + + tenv.Env, err = commonchangeset.ApplyChangesetsV2(t, tenv.Env, + []commonchangeset.ConfiguredChangeSet{ + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(v1_6.UpdateOnRampsDestsChangeset), + v1_6.UpdateOnRampDestsConfig{ + UpdatesByChain: map[uint64]map[uint64]v1_6.OnRampDestinationUpdate{ + evmChain2: { + solChain: { + IsEnabled: true, + TestRouter: false, + AllowListEnabled: false, + }, + }, + }, + }, + ), + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(ccipChangesetSolana.AddRemoteChainToSolana), + ccipChangesetSolana.AddRemoteChainToSolanaConfig{ + ChainSelector: solChain, + UpdatesByChain: map[uint64]ccipChangesetSolana.RemoteChainConfigSolana{ + evmChain2: { + EnabledAsSource: true, + RouterDestinationConfig: solRouter.DestChainConfig{}, + FeeQuoterDestinationConfig: solFeeQuoter.DestChainConfig{ + IsEnabled: true, + DefaultTxGasLimit: 200000, + MaxPerMsgGasLimit: 3000000, + MaxDataBytes: 30000, + MaxNumberOfTokensPerMsg: 5, + DefaultTokenDestGasOverhead: 5000, + // bytes4(keccak256("CCIP ChainFamilySelector EVM")) + // TODO: do a similar test for other chain families + // https://smartcontract-it.atlassian.net/browse/INTAUTO-438 + ChainFamilySelector: [4]uint8{40, 18, 213, 44}, + }, + }, + }, + MCMS: &ccipChangeset.MCMSConfig{ + MinDelay: 1 * time.Second, + }, + RouterAuthority: timelockSignerPDA, + FeeQuoterAuthority: timelockSignerPDA, + OffRampAuthority: timelockSignerPDA, + }, + ), + }, + ) + + require.NoError(t, err) + + state, err = ccipChangeset.LoadOnchainStateSolana(tenv.Env) + require.NoError(t, err) + + evmDestChainStatePDA = state.SolChains[solChain].DestChainStatePDAs[evmChain2] + err = tenv.Env.SolChains[solChain].GetAccountDataBorshInto(ctx, evmDestChainStatePDA, &destChainStateAccount) + require.NoError(t, err) + + fqEvmDestChainPDA, _, _ = solState.FindFqDestChainPDA(evmChain2, state.SolChains[solChain].FeeQuoter) + err = tenv.Env.SolChains[solChain].GetAccountDataBorshInto(ctx, fqEvmDestChainPDA, &destChainFqAccount) + require.NoError(t, err, "failed to get account info") + require.Equal(t, solFeeQuoter.TimestampedPackedU224{}, destChainFqAccount.State.UsdPerUnitGas) + require.True(t, destChainFqAccount.Config.IsEnabled) } func TestDeployCCIPContracts(t *testing.T) { diff --git a/deployment/ccip/changeset/solana/cs_deploy_chain.go b/deployment/ccip/changeset/solana/cs_deploy_chain.go index 95cbb45ca0a..ffe4cb10c89 100644 --- a/deployment/ccip/changeset/solana/cs_deploy_chain.go +++ b/deployment/ccip/changeset/solana/cs_deploy_chain.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "math" - "math/big" "github.com/Masterminds/semver/v3" "github.com/gagliardetto/solana-go" @@ -42,6 +41,16 @@ const ( var _ deployment.ChangeSet[DeployChainContractsConfig] = DeployChainContractsChangeset +func getTypeToProgramDeployName() map[deployment.ContractType]string { + return map[deployment.ContractType]string{ + ccipChangeset.Router: RouterProgramName, + ccipChangeset.OffRamp: OffRampProgramName, + ccipChangeset.FeeQuoter: FeeQuoterProgramName, + ccipChangeset.BurnMintTokenPool: BurnMintTokenPool, + ccipChangeset.LockReleaseTokenPool: LockReleaseTokenPool, + } +} + type DeployChainContractsConfig struct { HomeChainSelector uint64 ContractParamsPerChain map[uint64]ChainContractParams @@ -70,20 +79,22 @@ type UpgradeConfig struct { // SpillAddress and UpgradeAuthority must be set SpillAddress solana.PublicKey UpgradeAuthority solana.PublicKey - MCMS *ccipChangeset.MCMSConfig + // MCMS config must be set for upgrades and offramp redploys (to configure the fee quoter after redeploy) + MCMS *ccipChangeset.MCMSConfig } func (cfg UpgradeConfig) Validate(e deployment.Environment, chainSelector uint64) error { if cfg.NewFeeQuoterVersion == nil && cfg.NewRouterVersion == nil && cfg.NewOffRampVersion == nil { return nil } - if cfg.NewFeeQuoterVersion != nil || cfg.NewRouterVersion != nil { - if cfg.SpillAddress.IsZero() { - return errors.New("spill address must be set for fee quoter and router upgrades") - } - if cfg.UpgradeAuthority.IsZero() { - return errors.New("upgrade authority must be set for fee quoter and router upgrades") - } + if cfg.MCMS == nil { + return errors.New("MCMS config must be set for upgrades") + } + if cfg.SpillAddress.IsZero() { + return errors.New("spill address must be set for fee quoter and router upgrades") + } + if cfg.UpgradeAuthority.IsZero() { + return errors.New("upgrade authority must be set for fee quoter and router upgrades") } return ValidateMCMSConfig(e, chainSelector, cfg.MCMS) } @@ -152,7 +163,7 @@ func DeployChainContractsChangeset(e deployment.Environment, c DeployChainContra e.Logger.Errorw("Failed to deploy CCIP contracts", "err", err, "newAddresses", newAddresses) return deployment.ChangesetOutput{}, err } - // create proposals for ixns + // create proposals for txns if len(mcmsTxs) > 0 { batches = append(batches, mcmsTypes.BatchOperation{ ChainSelector: mcmsTypes.ChainSelector(chainSel), @@ -161,7 +172,7 @@ func DeployChainContractsChangeset(e deployment.Environment, c DeployChainContra } } - if c.UpgradeConfig.MCMS != nil { + if len(batches) > 0 { proposal, err := proposalutils.BuildProposalFromBatchesV2( e.GetContext(), timelocks, @@ -376,18 +387,18 @@ func deployChainContractsSolana( config DeployChainContractsConfig, ) ([]mcmsTypes.Transaction, error) { // we may need to gather instructions and submit them as part of MCMS - ixns := make([]mcmsTypes.Transaction, 0) + txns := make([]mcmsTypes.Transaction, 0) state, err := ccipChangeset.LoadOnchainStateSolana(e) if err != nil { e.Logger.Errorw("Failed to load existing onchain state", "err", err) - return ixns, err + return txns, err } chainState, chainExists := state.SolChains[chain.Selector] if !chainExists { - return ixns, fmt.Errorf("chain %s not found in existing state, deploy the link token first", chain.String()) + return txns, fmt.Errorf("chain %s not found in existing state, deploy the link token first", chain.String()) } if chainState.LinkToken.IsZero() { - return ixns, fmt.Errorf("failed to get link token address for chain %s", chain.String()) + return txns, fmt.Errorf("failed to get link token address for chain %s", chain.String()) } params := config.ContractParamsPerChain[chain.Selector] @@ -396,98 +407,18 @@ func deployChainContractsSolana( var feeQuoterAddress solana.PublicKey //nolint:gocritic // this is a false positive, we need to check if the address is zero if chainState.FeeQuoter.IsZero() { - feeQuoterAddress, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, FeeQuoterProgramName, deployment.Version1_0_0, false) + feeQuoterAddress, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, ccipChangeset.FeeQuoter, deployment.Version1_0_0, false) if err != nil { - return ixns, fmt.Errorf("failed to deploy program: %w", err) + return txns, fmt.Errorf("failed to deploy program: %w", err) } } else if config.UpgradeConfig.NewFeeQuoterVersion != nil { // fee quoter updated in place - bufferProgram, err := DeployAndMaybeSaveToAddressBook(e, chain, ab, FeeQuoterProgramName, *config.UpgradeConfig.NewFeeQuoterVersion, true) - if err != nil { - return ixns, fmt.Errorf("failed to deploy program: %w", err) - } - if err := setUpgradeAuthority(&e, &chain, bufferProgram, chain.DeployerKey, config.UpgradeConfig.UpgradeAuthority.ToPointer(), true); err != nil { - return ixns, fmt.Errorf("failed to set upgrade authority: %w", err) - } - extendIxn, err := generateExtendIxn( - &e, - chain, - chainState.FeeQuoter, - bufferProgram, - config.UpgradeConfig.SpillAddress, - ) - if err != nil { - return ixns, fmt.Errorf("failed to generate extend instruction: %w", err) - } - upgradeIxn, err := generateUpgradeIxn( - &e, - chainState.FeeQuoter, - bufferProgram, - config.UpgradeConfig.SpillAddress, - config.UpgradeConfig.UpgradeAuthority, - ) - if err != nil { - return ixns, fmt.Errorf("failed to generate upgrade instruction: %w", err) - } - closeIxn, err := generateCloseBufferIxn( - &e, - bufferProgram, - config.UpgradeConfig.SpillAddress, - config.UpgradeConfig.UpgradeAuthority, - ) - if err != nil { - return ixns, fmt.Errorf("failed to generate close buffer instruction: %w", err) - } feeQuoterAddress = chainState.FeeQuoter - upgradeData, err := upgradeIxn.Data() - if err != nil { - return ixns, fmt.Errorf("failed to extract upgrade data: %w", err) - } - upgradeTx, err := mcmsSolana.NewTransaction( - solana.BPFLoaderUpgradeableProgramID.String(), - upgradeData, - big.NewInt(0), // e.g. value - upgradeIxn.Accounts(), // pass along needed accounts - string(ccipChangeset.FeeQuoter), // some string identifying the target - []string{}, // any relevant metadata - ) + newTxns, err := generateUpgradeTxns(e, chain, ab, config, config.UpgradeConfig.NewFeeQuoterVersion, chainState.FeeQuoter, ccipChangeset.FeeQuoter) if err != nil { - return ixns, fmt.Errorf("failed to create upgrade transaction: %w", err) + return txns, fmt.Errorf("failed to generate upgrade txns: %w", err) } - closeData, err := closeIxn.Data() - if err != nil { - return ixns, fmt.Errorf("failed to extract close data: %w", err) - } - closeTx, err := mcmsSolana.NewTransaction( - solana.BPFLoaderUpgradeableProgramID.String(), - closeData, - big.NewInt(0), // e.g. value - closeIxn.Accounts(), // pass along needed accounts - string(ccipChangeset.FeeQuoter), // some string identifying the target - []string{}, // any relevant metadata - ) - if err != nil { - return ixns, fmt.Errorf("failed to create close transaction: %w", err) - } - if extendIxn != nil { - extendData, err := extendIxn.Data() - if err != nil { - return ixns, fmt.Errorf("failed to extract extend data: %w", err) - } - extendTx, err := mcmsSolana.NewTransaction( - solana.BPFLoaderUpgradeableProgramID.String(), - extendData, - big.NewInt(0), // e.g. value - extendIxn.Accounts(), // pass along needed accounts - string(ccipChangeset.FeeQuoter), // some string identifying the target - []string{}, // any relevant metadata - ) - if err != nil { - return ixns, fmt.Errorf("failed to create extend transaction: %w", err) - } - ixns = append(ixns, extendTx) - } - ixns = append(ixns, upgradeTx, closeTx) + txns = append(txns, newTxns...) } else { e.Logger.Infow("Using existing fee quoter", "addr", chainState.FeeQuoter.String()) feeQuoterAddress = chainState.FeeQuoter @@ -499,98 +430,18 @@ func deployChainContractsSolana( //nolint:gocritic // this is a false positive, we need to check if the address is zero if chainState.Router.IsZero() { // deploy router - ccipRouterProgram, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, RouterProgramName, deployment.Version1_0_0, false) + ccipRouterProgram, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, ccipChangeset.Router, deployment.Version1_0_0, false) if err != nil { - return ixns, fmt.Errorf("failed to deploy program: %w", err) + return txns, fmt.Errorf("failed to deploy program: %w", err) } } else if config.UpgradeConfig.NewRouterVersion != nil { // router updated in place - bufferProgram, err := DeployAndMaybeSaveToAddressBook(e, chain, ab, RouterProgramName, *config.UpgradeConfig.NewRouterVersion, true) - if err != nil { - return ixns, fmt.Errorf("failed to deploy program: %w", err) - } - if err := setUpgradeAuthority(&e, &chain, bufferProgram, chain.DeployerKey, config.UpgradeConfig.UpgradeAuthority.ToPointer(), true); err != nil { - return ixns, fmt.Errorf("failed to set upgrade authority: %w", err) - } - upgradeIxn, err := generateUpgradeIxn( - &e, - chainState.Router, - bufferProgram, - config.UpgradeConfig.SpillAddress, - config.UpgradeConfig.UpgradeAuthority, - ) - if err != nil { - return ixns, fmt.Errorf("failed to generate upgrade instruction: %w", err) - } - extendIxn, err := generateExtendIxn( - &e, - chain, - chainState.Router, - bufferProgram, - config.UpgradeConfig.SpillAddress, - ) - if err != nil { - return ixns, fmt.Errorf("failed to generate extend instruction: %w", err) - } - closeIxn, err := generateCloseBufferIxn( - &e, - bufferProgram, - config.UpgradeConfig.SpillAddress, - config.UpgradeConfig.UpgradeAuthority, - ) - if err != nil { - return ixns, fmt.Errorf("failed to generate close buffer instruction: %w", err) - } ccipRouterProgram = chainState.Router - upgradeData, err := upgradeIxn.Data() - if err != nil { - return ixns, fmt.Errorf("failed to extract upgrade data: %w", err) - } - upgradeTx, err := mcmsSolana.NewTransaction( - solana.BPFLoaderUpgradeableProgramID.String(), - upgradeData, - big.NewInt(0), // e.g. value - upgradeIxn.Accounts(), // pass along needed accounts - string(ccipChangeset.Router), // some string identifying the target - []string{}, // any relevant metadata - ) - if err != nil { - return ixns, fmt.Errorf("failed to create upgrade transaction: %w", err) - } - closeData, err := closeIxn.Data() + newTxns, err := generateUpgradeTxns(e, chain, ab, config, config.UpgradeConfig.NewRouterVersion, chainState.Router, ccipChangeset.Router) if err != nil { - return ixns, fmt.Errorf("failed to extract close data: %w", err) + return txns, fmt.Errorf("failed to generate upgrade txns: %w", err) } - closeTx, err := mcmsSolana.NewTransaction( - solana.BPFLoaderUpgradeableProgramID.String(), - closeData, - big.NewInt(0), // e.g. value - closeIxn.Accounts(), // pass along needed accounts - string(ccipChangeset.Router), // some string identifying the target - []string{}, // any relevant metadata - ) - if err != nil { - return ixns, fmt.Errorf("failed to create close transaction: %w", err) - } - if extendIxn != nil { - extendData, err := extendIxn.Data() - if err != nil { - return ixns, fmt.Errorf("failed to extract extend data: %w", err) - } - extendTx, err := mcmsSolana.NewTransaction( - solana.BPFLoaderUpgradeableProgramID.String(), - extendData, - big.NewInt(0), // e.g. value - extendIxn.Accounts(), // pass along needed accounts - string(ccipChangeset.Router), // some string identifying the target - []string{}, // any relevant metadata - ) - if err != nil { - return ixns, fmt.Errorf("failed to create extend transaction: %w", err) - } - ixns = append(ixns, extendTx) - } - ixns = append(ixns, upgradeTx, closeTx) + txns = append(txns, newTxns...) } else { e.Logger.Infow("Using existing router", "addr", chainState.Router.String()) ccipRouterProgram = chainState.Router @@ -607,22 +458,22 @@ func deployChainContractsSolana( //nolint:gocritic // this is a false positive, we need to check if the address is zero if chainState.OffRamp.IsZero() { // deploy offramp - offRampAddress, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, OffRampProgramName, deployment.Version1_0_0, false) + offRampAddress, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, ccipChangeset.OffRamp, deployment.Version1_0_0, false) if err != nil { - return ixns, fmt.Errorf("failed to deploy program: %w", err) + return txns, fmt.Errorf("failed to deploy program: %w", err) } } else if config.UpgradeConfig.NewOffRampVersion != nil { tv := deployment.NewTypeAndVersion(ccipChangeset.OffRamp, *config.UpgradeConfig.NewOffRampVersion) existingAddresses, err := e.ExistingAddresses.AddressesForChain(chain.Selector) if err != nil { - return ixns, fmt.Errorf("failed to get existing addresses: %w", err) + return txns, fmt.Errorf("failed to get existing addresses: %w", err) } offRampAddress = ccipChangeset.FindSolanaAddress(tv, existingAddresses) if offRampAddress.IsZero() { // deploy offramp, not upgraded in place so upgrade is false - offRampAddress, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, OffRampProgramName, *config.UpgradeConfig.NewOffRampVersion, false) + offRampAddress, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, ccipChangeset.OffRamp, *config.UpgradeConfig.NewOffRampVersion, false) if err != nil { - return ixns, fmt.Errorf("failed to deploy program: %w", err) + return txns, fmt.Errorf("failed to deploy program: %w", err) } } @@ -634,28 +485,17 @@ func deployChainContractsSolana( offRampBillingSignerPDA, fqAllowedPriceUpdaterOfframpPDA, feeQuoterConfigPDA, - chain.DeployerKey.PublicKey(), + config.UpgradeConfig.UpgradeAuthority, solana.SystemProgramID, ).ValidateAndBuild() if err != nil { - return ixns, fmt.Errorf("failed to build instruction: %w", err) + return txns, fmt.Errorf("failed to build instruction: %w", err) } - priceUpdaterData, err := priceUpdaterix.Data() - if err != nil { - return ixns, fmt.Errorf("failed to extract price updater data: %w", err) - } - priceUpdaterTx, err := mcmsSolana.NewTransaction( - feeQuoterAddress.String(), - priceUpdaterData, - big.NewInt(0), // e.g. value - priceUpdaterix.Accounts(), // pass along needed accounts - string(ccipChangeset.OffRamp), // some string identifying the target - []string{}, // any relevant metadata - ) + priceUpdaterTx, err := BuildMCMSTxn(priceUpdaterix, feeQuoterAddress.String(), ccipChangeset.FeeQuoter) if err != nil { - return ixns, fmt.Errorf("failed to create price updater transaction: %w", err) + return txns, fmt.Errorf("failed to create price updater transaction: %w", err) } - ixns = append(ixns, priceUpdaterTx) + txns = append(txns, *priceUpdaterTx) } else { e.Logger.Infow("Using existing offramp", "addr", chainState.OffRamp.String()) offRampAddress = chainState.OffRamp @@ -668,7 +508,7 @@ func deployChainContractsSolana( err = chain.GetAccountDataBorshInto(e.GetContext(), feeQuoterConfigPDA, &fqConfig) if err != nil { if err2 := initializeFeeQuoter(e, chain, ccipRouterProgram, chainState.LinkToken, feeQuoterAddress, offRampAddress, params.FeeQuoterParams); err2 != nil { - return ixns, err2 + return txns, err2 } } else { e.Logger.Infow("Fee quoter already initialized, skipping initialization", "chain", chain.String()) @@ -681,7 +521,7 @@ func deployChainContractsSolana( err = chain.GetAccountDataBorshInto(e.GetContext(), routerConfigPDA, &routerConfigAccount) if err != nil { if err2 := initializeRouter(e, chain, ccipRouterProgram, chainState.LinkToken, feeQuoterAddress); err2 != nil { - return ixns, err2 + return txns, err2 } } else { e.Logger.Infow("Router already initialized, skipping initialization", "chain", chain.String()) @@ -707,10 +547,10 @@ func deployChainContractsSolana( solana.SPLAssociatedTokenAccountProgramID, }) if err2 != nil { - return ixns, fmt.Errorf("failed to create address lookup table: %w", err) + return txns, fmt.Errorf("failed to create address lookup table: %w", err) } if err2 := initializeOffRamp(e, chain, ccipRouterProgram, feeQuoterAddress, offRampAddress, table, params.OffRampParams); err2 != nil { - return ixns, err2 + return txns, err2 } // Initializing a new offramp means we need a new lookup table and need to fully populate it needFQinLookupTable = true @@ -732,9 +572,9 @@ func deployChainContractsSolana( var burnMintTokenPool solana.PublicKey if chainState.BurnMintTokenPool.IsZero() { - burnMintTokenPool, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, BurnMintTokenPool, deployment.Version1_0_0, false) + burnMintTokenPool, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, ccipChangeset.BurnMintTokenPool, deployment.Version1_0_0, false) if err != nil { - return ixns, fmt.Errorf("failed to deploy program: %w", err) + return txns, fmt.Errorf("failed to deploy program: %w", err) } needTokenPoolinLookupTable = true } else { @@ -744,9 +584,9 @@ func deployChainContractsSolana( var lockReleaseTokenPool solana.PublicKey if chainState.LockReleaseTokenPool.IsZero() { - lockReleaseTokenPool, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, LockReleaseTokenPool, deployment.Version1_0_0, false) + lockReleaseTokenPool, err = DeployAndMaybeSaveToAddressBook(e, chain, ab, ccipChangeset.LockReleaseTokenPool, deployment.Version1_0_0, false) if err != nil { - return ixns, fmt.Errorf("failed to deploy program: %w", err) + return txns, fmt.Errorf("failed to deploy program: %w", err) } needTokenPoolinLookupTable = true } else { @@ -758,7 +598,7 @@ func deployChainContractsSolana( if err := AddBillingToken( e, chain, chainState, billingConfig, ); err != nil { - return ixns, err + return txns, err } } @@ -801,11 +641,11 @@ func deployChainContractsSolana( if len(lookupTableKeys) > 0 { addressLookupTable, err := ccipChangeset.FetchOfframpLookupTable(e.GetContext(), chain, offRampAddress) if err != nil { - return ixns, fmt.Errorf("failed to get offramp reference addresses: %w", err) + return txns, fmt.Errorf("failed to get offramp reference addresses: %w", err) } e.Logger.Debugw("Populating lookup table", "lookupTable", addressLookupTable.String(), "keys", lookupTableKeys) if err := solCommonUtil.ExtendLookupTable(e.GetContext(), chain.Client, addressLookupTable, *chain.DeployerKey, lookupTableKeys); err != nil { - return ixns, fmt.Errorf("failed to extend lookup table: %w", err) + return txns, fmt.Errorf("failed to extend lookup table: %w", err) } } @@ -814,12 +654,77 @@ func deployChainContractsSolana( e.Logger.Infow("Setting upgrade authority", "newUpgradeAuthority", config.NewUpgradeAuthority.String()) for _, programID := range []solana.PublicKey{ccipRouterProgram, feeQuoterAddress} { if err := setUpgradeAuthority(&e, &chain, programID, chain.DeployerKey, config.NewUpgradeAuthority, false); err != nil { - return ixns, fmt.Errorf("failed to set upgrade authority: %w", err) + return txns, fmt.Errorf("failed to set upgrade authority: %w", err) } } } - return ixns, nil + return txns, nil +} + +func generateUpgradeTxns( + e deployment.Environment, + chain deployment.SolChain, + ab deployment.AddressBook, + config DeployChainContractsConfig, + newVersion *semver.Version, + programID solana.PublicKey, + contractType deployment.ContractType, +) ([]mcmsTypes.Transaction, error) { + txns := make([]mcmsTypes.Transaction, 0) + bufferProgram, err := DeployAndMaybeSaveToAddressBook(e, chain, ab, contractType, *newVersion, true) + if err != nil { + return txns, fmt.Errorf("failed to deploy program: %w", err) + } + if err := setUpgradeAuthority(&e, &chain, bufferProgram, chain.DeployerKey, config.UpgradeConfig.UpgradeAuthority.ToPointer(), true); err != nil { + return txns, fmt.Errorf("failed to set upgrade authority: %w", err) + } + extendIxn, err := generateExtendIxn( + &e, + chain, + programID, + bufferProgram, + config.UpgradeConfig.SpillAddress, + ) + if err != nil { + return txns, fmt.Errorf("failed to generate extend instruction: %w", err) + } + upgradeIxn, err := generateUpgradeIxn( + &e, + programID, + bufferProgram, + config.UpgradeConfig.SpillAddress, + config.UpgradeConfig.UpgradeAuthority, + ) + if err != nil { + return txns, fmt.Errorf("failed to generate upgrade instruction: %w", err) + } + closeIxn, err := generateCloseBufferIxn( + &e, + bufferProgram, + config.UpgradeConfig.SpillAddress, + config.UpgradeConfig.UpgradeAuthority, + ) + if err != nil { + return txns, fmt.Errorf("failed to generate close buffer instruction: %w", err) + } + upgradeTx, err := BuildMCMSTxn(upgradeIxn, solana.BPFLoaderUpgradeableProgramID.String(), contractType) + if err != nil { + return txns, fmt.Errorf("failed to create upgrade transaction: %w", err) + } + closeTx, err := BuildMCMSTxn(closeIxn, solana.BPFLoaderUpgradeableProgramID.String(), contractType) + if err != nil { + return txns, fmt.Errorf("failed to create close transaction: %w", err) + } + if extendIxn != nil { + extendTx, err := BuildMCMSTxn(extendIxn, solana.BPFLoaderUpgradeableProgramID.String(), contractType) + if err != nil { + return txns, fmt.Errorf("failed to create extend transaction: %w", err) + } + txns = append(txns, *extendTx) + } + txns = append(txns, *upgradeTx, *closeTx) + return txns, nil } // DeployAndMaybeSaveToAddressBook deploys a program to the Solana chain and saves it to the address book @@ -828,30 +733,20 @@ func DeployAndMaybeSaveToAddressBook( e deployment.Environment, chain deployment.SolChain, ab deployment.AddressBook, - programName string, + contractType deployment.ContractType, version semver.Version, isUpgrade bool) (solana.PublicKey, error) { + programName := getTypeToProgramDeployName()[contractType] programID, err := chain.DeployProgram(e.Logger, programName, isUpgrade) if err != nil { return solana.PublicKey{}, fmt.Errorf("failed to deploy program: %w", err) } address := solana.MustPublicKeyFromBase58(programID) - programNameToType := map[string]deployment.ContractType{ - RouterProgramName: ccipChangeset.Router, - OffRampProgramName: ccipChangeset.OffRamp, - FeeQuoterProgramName: ccipChangeset.FeeQuoter, - BurnMintTokenPool: ccipChangeset.BurnMintTokenPool, - LockReleaseTokenPool: ccipChangeset.LockReleaseTokenPool, - } - programType, ok := programNameToType[programName] - if !ok { - return solana.PublicKey{}, fmt.Errorf("unknown program name: %s", programName) - } - e.Logger.Infow("Deployed program", "Program", programType, "addr", programID, "chain", chain.String(), "isUpgrade", isUpgrade) + e.Logger.Infow("Deployed program", "Program", contractType, "addr", programID, "chain", chain.String(), "isUpgrade", isUpgrade) if !isUpgrade { - tv := deployment.NewTypeAndVersion(programType, version) + tv := deployment.NewTypeAndVersion(contractType, version) err = ab.Save(chain.Selector, programID, tv) if err != nil { return solana.PublicKey{}, fmt.Errorf("failed to save address: %w", err) @@ -916,7 +811,7 @@ func generateUpgradeIxn( solana.NewAccountMeta(spillAddress, true, false), // Spill account (writable) solana.NewAccountMeta(solana.SysVarRentPubkey, false, false), // System program solana.NewAccountMeta(solana.SysVarClockPubkey, false, false), // System program - solana.NewAccountMeta(upgradeAuthority, false, false), // Current upgrade authority (signer) + solana.NewAccountMeta(upgradeAuthority, false, true), // Current upgrade authority (signer) } instruction := solana.NewInstruction( @@ -967,7 +862,7 @@ func generateExtendIxn( solana.NewAccountMeta(programDataAccount, true, false), // Program data account (writable) solana.NewAccountMeta(programID, true, false), // Program account (writable) solana.NewAccountMeta(solana.SystemProgramID, false, false), // System program - solana.NewAccountMeta(payer, true, false), // Payer for rent + solana.NewAccountMeta(payer, true, true), // Payer for rent } ixn := solana.NewInstruction( @@ -988,7 +883,7 @@ func generateCloseBufferIxn( keys := solana.AccountMetaSlice{ solana.NewAccountMeta(bufferAddress, true, false), solana.NewAccountMeta(recipient, true, false), - solana.NewAccountMeta(upgradeAuthority, false, false), + solana.NewAccountMeta(upgradeAuthority, false, true), } instruction := solana.NewInstruction( diff --git a/deployment/ccip/changeset/solana/cs_deploy_chain_test.go b/deployment/ccip/changeset/solana/cs_deploy_chain_test.go index 4800df91f50..55803335097 100644 --- a/deployment/ccip/changeset/solana/cs_deploy_chain_test.go +++ b/deployment/ccip/changeset/solana/cs_deploy_chain_test.go @@ -1,7 +1,6 @@ package solana_test import ( - "math/big" "os" "testing" "time" @@ -13,11 +12,12 @@ import ( solBinary "github.com/gagliardetto/binary" "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" ccipChangeset "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + cs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" ccipChangesetSolana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6" - commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" "github.com/smartcontractkit/chainlink/deployment/environment/memory" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -60,11 +60,26 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { feeAggregatorPrivKey, _ := solana.NewRandomPrivateKey() feeAggregatorPubKey := feeAggregatorPrivKey.PublicKey() ci := os.Getenv("CI") == "true" + // we can't upgrade in place locally if we preload addresses so we have to change where we build + // we also don't want to incur two builds in CI, so only do it locally if ci { testhelpers.SavePreloadedSolAddresses(t, e, solChainSelectors[0]) + } else { + e, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{ + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(ccipChangesetSolana.BuildSolanaChangeset), + ccipChangesetSolana.BuildSolanaConfig{ + ChainSelector: solChainSelectors[0], + GitCommitSha: "3da552ac9d30b821310718b8b67e6a298335a485", + DestinationDir: e.SolChains[solChainSelectors[0]].ProgramsPath, + CleanDestinationDir: true, + }, + ), + }) + require.NoError(t, err) } - e, err = commonchangeset.Apply(t, e, nil, + e, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{ commonchangeset.Configure( deployment.CreateLegacyChangeSet(v1_6.DeployHomeChainChangeset), v1_6.DeployHomeChainConfig{ @@ -103,179 +118,136 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { }, ), commonchangeset.Configure( - deployment.CreateLegacyChangeSet(commonchangeset.DeployMCMSWithTimelockV2), - map[uint64]commontypes.MCMSWithTimelockConfigV2{ - solChainSelectors[0]: { - Canceller: proposalutils.SingleGroupMCMSV2(t), - Proposer: proposalutils.SingleGroupMCMSV2(t), - Bypasser: proposalutils.SingleGroupMCMSV2(t), - TimelockMinDelay: big.NewInt(0), + deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), + ccipChangesetSolana.DeployChainContractsConfig{ + HomeChainSelector: homeChainSel, + ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ + solChainSelectors[0]: { + FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ + DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + }, + OffRampParams: ccipChangesetSolana.OffRampParams{ + EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), + }, + }, }, }, ), - ) - require.NoError(t, err) - addresses, err := e.ExistingAddresses.AddressesForChain(solChainSelectors[0]) - require.NoError(t, err) - mcmState, err := commonState.MaybeLoadMCMSWithTimelockChainStateSolana(e.SolChains[solChainSelectors[0]], addresses) + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(ccipChangesetSolana.SetFeeAggregator), + ccipChangesetSolana.SetFeeAggregatorConfig{ + ChainSelector: solChainSelectors[0], + FeeAggregator: feeAggregatorPubKey.String(), + }, + ), + }) require.NoError(t, err) - - // Fund signer PDAs for timelock and mcm - // If we don't fund, execute() calls will fail with "no funds" errors. - timelockSignerPDA := commonState.GetTimelockSignerPDA(mcmState.TimelockProgram, mcmState.TimelockSeed) - mcmSignerPDA := commonState.GetMCMSignerPDA(mcmState.McmProgram, mcmState.ProposerMcmSeed) - memory.FundSolanaAccounts(e.GetContext(), t, []solana.PublicKey{timelockSignerPDA, mcmSignerPDA}, - 100, e.SolChains[solChainSelectors[0]].Client) - t.Logf("funded timelock signer PDA: %s", timelockSignerPDA.String()) - t.Logf("funded mcm signer PDA: %s", mcmSignerPDA.String()) + testhelpers.ValidateSolanaState(t, e, solChainSelectors) + timelockSignerPDA, _ := testhelpers.TransferOwnershipSolana(t, &e, solChainSelectors[0], true, true, true, true) upgradeAuthority := timelockSignerPDA + state, err := changeset.LoadOnchainStateSolana(e) + require.NoError(t, err) - // we can't upgrade in place locally so we have to change where we build - buildCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(ccipChangesetSolana.BuildSolanaChangeset), - ccipChangesetSolana.BuildSolanaConfig{ - ChainSelector: solChainSelectors[0], - GitCommitSha: "0863d8fed5fbada9f352f33c405e1753cbb7d72c", - DestinationDir: e.SolChains[solChainSelectors[0]].ProgramsPath, - CleanDestinationDir: true, - }, - ) - deployCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), - ccipChangesetSolana.DeployChainContractsConfig{ - HomeChainSelector: homeChainSel, - ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ - solChainSelectors[0]: { - FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ - DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, - }, - OffRampParams: ccipChangesetSolana.OffRampParams{ - EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), + e, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{ + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), + ccipChangesetSolana.DeployChainContractsConfig{ + HomeChainSelector: homeChainSel, + ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ + solChainSelectors[0]: { + FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ + DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + }, + OffRampParams: ccipChangesetSolana.OffRampParams{ + EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), + }, }, }, + NewUpgradeAuthority: &upgradeAuthority, }, - }, - ) - // set the fee aggregator address - feeAggregatorCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(ccipChangesetSolana.SetFeeAggregator), - ccipChangesetSolana.SetFeeAggregatorConfig{ - ChainSelector: solChainSelectors[0], - FeeAggregator: feeAggregatorPubKey.String(), - }, - ) - transferOwnershipCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(ccipChangesetSolana.TransferCCIPToMCMSWithTimelockSolana), - ccipChangesetSolana.TransferCCIPToMCMSWithTimelockSolanaConfig{ - MinDelay: 1 * time.Second, - ContractsByChain: map[uint64]ccipChangesetSolana.CCIPContractsToTransfer{ - solChainSelectors[0]: { - Router: true, - FeeQuoter: true, - OffRamp: true, + ), + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(ccipChangesetSolana.BuildSolanaChangeset), + ccipChangesetSolana.BuildSolanaConfig{ + ChainSelector: solChainSelectors[0], + GitCommitSha: "0863d8fed5fbada9f352f33c405e1753cbb7d72c", + DestinationDir: e.SolChains[solChainSelectors[0]].ProgramsPath, + CleanDestinationDir: true, + CleanGitDir: true, + UpgradeKeys: map[deployment.ContractType]string{ + cs.Router: state.SolChains[solChainSelectors[0]].Router.String(), + cs.FeeQuoter: state.SolChains[solChainSelectors[0]].FeeQuoter.String(), }, }, - }, - ) - // make sure idempotency works and setting the upgrade authority - upgradeAuthorityCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), - ccipChangesetSolana.DeployChainContractsConfig{ - HomeChainSelector: homeChainSel, - ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ - solChainSelectors[0]: { - FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ - DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, - }, - OffRampParams: ccipChangesetSolana.OffRampParams{ - EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), + ), + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), + ccipChangesetSolana.DeployChainContractsConfig{ + HomeChainSelector: homeChainSel, + ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ + solChainSelectors[0]: { + FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ + DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + }, + OffRampParams: ccipChangesetSolana.OffRampParams{ + EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), + }, }, }, - }, - NewUpgradeAuthority: &upgradeAuthority, - }, - ) - upgradeCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), - ccipChangesetSolana.DeployChainContractsConfig{ - HomeChainSelector: homeChainSel, - ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ - solChainSelectors[0]: { - FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ - DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, - }, - OffRampParams: ccipChangesetSolana.OffRampParams{ - EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), + UpgradeConfig: ccipChangesetSolana.UpgradeConfig{ + NewFeeQuoterVersion: &deployment.Version1_1_0, + NewRouterVersion: &deployment.Version1_1_0, + UpgradeAuthority: upgradeAuthority, + SpillAddress: upgradeAuthority, + MCMS: &ccipChangeset.MCMSConfig{ + MinDelay: 1 * time.Second, }, }, }, - UpgradeConfig: ccipChangesetSolana.UpgradeConfig{ - NewFeeQuoterVersion: &deployment.Version1_1_0, - NewRouterVersion: &deployment.Version1_1_0, - UpgradeAuthority: upgradeAuthority, - SpillAddress: upgradeAuthority, - MCMS: &ccipChangeset.MCMSConfig{ - MinDelay: 1 * time.Second, - }, - }, - }, - ) - // because we cannot upgrade in place locally, we can't redeploy offramp - offRampCs := commonchangeset.Configure( - deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), - ccipChangesetSolana.DeployChainContractsConfig{ - HomeChainSelector: homeChainSel, - ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ - solChainSelectors[0]: { - FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ - DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + ), + }) + require.NoError(t, err) + testhelpers.ValidateSolanaState(t, e, solChainSelectors) + state, err = changeset.LoadOnchainStateSolana(e) + require.NoError(t, err) + oldOffRampAddress := state.SolChains[solChainSelectors[0]].OffRamp + // add a second offramp address + e, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{ + commonchangeset.Configure( + deployment.CreateLegacyChangeSet(ccipChangesetSolana.DeployChainContractsChangeset), + ccipChangesetSolana.DeployChainContractsConfig{ + HomeChainSelector: homeChainSel, + ContractParamsPerChain: map[uint64]ccipChangesetSolana.ChainContractParams{ + solChainSelectors[0]: { + FeeQuoterParams: ccipChangesetSolana.FeeQuoterParams{ + DefaultMaxFeeJuelsPerMsg: solBinary.Uint128{Lo: 300000000, Hi: 0, Endianness: nil}, + }, + OffRampParams: ccipChangesetSolana.OffRampParams{ + EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), + }, }, - OffRampParams: ccipChangesetSolana.OffRampParams{ - EnableExecutionAfter: int64(globals.PermissionLessExecutionThreshold.Seconds()), + }, + UpgradeConfig: ccipChangesetSolana.UpgradeConfig{ + NewOffRampVersion: &deployment.Version1_1_0, + UpgradeAuthority: upgradeAuthority, + SpillAddress: upgradeAuthority, + MCMS: &ccipChangeset.MCMSConfig{ + MinDelay: 1 * time.Second, }, }, }, - UpgradeConfig: ccipChangesetSolana.UpgradeConfig{ - NewOffRampVersion: &deployment.Version1_1_0, - }, - }, - ) - if ci { - e, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{ - deployCs, - feeAggregatorCs, - upgradeAuthorityCs, - transferOwnershipCs, - }) - require.NoError(t, err) - state, err := ccipChangeset.LoadOnchainStateSolana(e) - require.NoError(t, err) - oldOffRampAddress := state.SolChains[solChainSelectors[0]].OffRamp - // add a second offramp address - e, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{ - buildCs, - upgradeCs, - offRampCs, - }) - require.NoError(t, err) - // verify the offramp address is different - state, err = ccipChangeset.LoadOnchainStateSolana(e) - require.NoError(t, err) - newOffRampAddress := state.SolChains[solChainSelectors[0]].OffRamp - require.NotEqual(t, oldOffRampAddress, newOffRampAddress) - } else { - e, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{ - buildCs, - deployCs, - feeAggregatorCs, - upgradeAuthorityCs, - upgradeCs, - }) - } + ), + }) + require.NoError(t, err) + // verify the offramp address is different + state, err = changeset.LoadOnchainStateSolana(e) require.NoError(t, err) + newOffRampAddress := state.SolChains[solChainSelectors[0]].OffRamp + require.NotEqual(t, oldOffRampAddress, newOffRampAddress) + // Verify router and fee quoter upgraded in place // and offramp had 2nd address added - addresses, err = e.ExistingAddresses.AddressesForChain(solChainSelectors[0]) + addresses, err := e.ExistingAddresses.AddressesForChain(solChainSelectors[0]) require.NoError(t, err) numRouters := 0 numFeeQuoters := 0 @@ -293,11 +265,7 @@ func TestDeployChainContractsChangesetSolana(t *testing.T) { } require.Equal(t, 1, numRouters) require.Equal(t, 1, numFeeQuoters) - if ci { - require.Equal(t, 2, numOffRamps) - } else { - require.Equal(t, 1, numOffRamps) - } + require.Equal(t, 2, numOffRamps) require.NoError(t, err) // solana verification testhelpers.ValidateSolanaState(t, e, solChainSelectors) diff --git a/deployment/ccip/changeset/solana/ownership_transfer_helpers.go b/deployment/ccip/changeset/solana/ownership_transfer_helpers.go index 094c3d3bc75..406846f5ee3 100644 --- a/deployment/ccip/changeset/solana/ownership_transfer_helpers.go +++ b/deployment/ccip/changeset/solana/ownership_transfer_helpers.go @@ -2,10 +2,8 @@ package solana import ( "fmt" - "math/big" "github.com/gagliardetto/solana-go" - mcmsSolana "github.com/smartcontractkit/mcms/sdk/solana" mcmsTypes "github.com/smartcontractkit/mcms/types" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_offramp" @@ -60,25 +58,14 @@ func transferAndWrapAcceptOwnership( if err != nil { return mcmsTypes.Transaction{}, fmt.Errorf("%s: failed to create accept ownership instruction: %w", label, err) } - acceptData, err := ixAccept.Data() - if err != nil { - return mcmsTypes.Transaction{}, fmt.Errorf("%s: failed to extract accept data: %w", label, err) - } // 4. Wrap in MCMS transaction - mcmsTx, err := mcmsSolana.NewTransaction( - programID.String(), - acceptData, - big.NewInt(0), // e.g. value - ixAccept.Accounts(), // pass along needed accounts - string(label), // some string identifying the target - []string{}, // any relevant metadata - ) + mcmsTx, err := BuildMCMSTxn(ixAccept, programID.String(), label) if err != nil { return mcmsTypes.Transaction{}, fmt.Errorf("%s: failed to create MCMS transaction: %w", label, err) } - return mcmsTx, nil + return *mcmsTx, nil } // transferOwnershipRouter transfers ownership of the router to the timelock. diff --git a/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go b/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go index 94f34753ba6..986a06d90f8 100644 --- a/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go +++ b/deployment/ccip/changeset/solana/transfer_ccip_to_mcms_with_timelock_test.go @@ -30,7 +30,6 @@ import ( commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/globals" - commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/environment/memory" @@ -310,37 +309,7 @@ func TestTransferCCIPToMCMSWithTimelockSolana(t *testing.T) { e, state := prepareEnvironmentForOwnershipTransfer(t) solChain1 := e.AllChainSelectorsSolana()[0] solChain := e.SolChains[solChain1] - // tokenAddress := state.SolChains[solChain1].SPL2022Tokens[0] - addresses, err := e.ExistingAddresses.AddressesForChain(solChain1) - require.NoError(t, err) - mcmState, err := commonState.MaybeLoadMCMSWithTimelockChainStateSolana(e.SolChains[solChain1], addresses) - require.NoError(t, err) - - // Fund signer PDAs for timelock and mcm - // If we don't fund, execute() calls will fail with "no funds" errors. - timelockSignerPDA := commonState.GetTimelockSignerPDA(mcmState.TimelockProgram, mcmState.TimelockSeed) - mcmSignerPDA := commonState.GetMCMSignerPDA(mcmState.McmProgram, mcmState.ProposerMcmSeed) - memory.FundSolanaAccounts(e.GetContext(), t, []solana.PublicKey{timelockSignerPDA, mcmSignerPDA}, - 100, solChain.Client) - t.Logf("funded timelock signer PDA: %s", timelockSignerPDA.String()) - t.Logf("funded mcm signer PDA: %s", mcmSignerPDA.String()) - // Apply transfer ownership changeset - e, err = commonchangeset.ApplyChangesetsV2(t, e, []commonchangeset.ConfiguredChangeSet{ - commonchangeset.Configure( - deployment.CreateLegacyChangeSet(solanachangesets.TransferCCIPToMCMSWithTimelockSolana), - solanachangesets.TransferCCIPToMCMSWithTimelockSolanaConfig{ - MinDelay: 1 * time.Second, - ContractsByChain: map[uint64]solanachangesets.CCIPContractsToTransfer{ - solChain1: { - Router: true, - FeeQuoter: true, - OffRamp: true, - }, - }, - }, - ), - }) - require.NoError(t, err) + timelockSignerPDA, _ := testhelpers.TransferOwnershipSolana(t, &e, solChain1, false, true, true, true) // 5. Now verify on-chain that each contract’s “config account” authority is the Timelock PDA. // Typically, each contract has its own config account: RouterConfigPDA, FeeQuoterConfigPDA, @@ -352,7 +321,8 @@ func TestTransferCCIPToMCMSWithTimelockSolana(t *testing.T) { routerConfigPDA := state.SolChains[solChain1].RouterConfigPDA t.Logf("Checking Router Config PDA ownership data configPDA: %s", routerConfigPDA.String()) programData := ccip_router.Config{} - err = solChain.GetAccountDataBorshInto(ctx, routerConfigPDA, &programData) + err := solChain.GetAccountDataBorshInto(ctx, routerConfigPDA, &programData) + require.NoError(t, err) return timelockSignerPDA.String() == programData.Owner.String() }, 30*time.Second, 5*time.Second, "Router config PDA owner was not changed to timelock signer PDA") @@ -361,7 +331,7 @@ func TestTransferCCIPToMCMSWithTimelockSolana(t *testing.T) { feeQuoterConfigPDA := state.SolChains[solChain1].FeeQuoterConfigPDA t.Logf("Checking Fee Quoter PDA ownership data configPDA: %s", feeQuoterConfigPDA.String()) programData := fee_quoter.Config{} - err = solChain.GetAccountDataBorshInto(ctx, feeQuoterConfigPDA, &programData) + err := solChain.GetAccountDataBorshInto(ctx, feeQuoterConfigPDA, &programData) require.NoError(t, err) return timelockSignerPDA.String() == programData.Owner.String() }, 30*time.Second, 5*time.Second, "Fee Quoter config PDA owner was not changed to timelock signer PDA") @@ -371,7 +341,7 @@ func TestTransferCCIPToMCMSWithTimelockSolana(t *testing.T) { offRampConfigPDA := state.SolChains[solChain1].OffRampConfigPDA programData := ccip_offramp.Config{} t.Logf("Checking Off Ramp PDA ownership data configPDA: %s", offRampConfigPDA.String()) - err = solChain.GetAccountDataBorshInto(ctx, offRampConfigPDA, &programData) + err := solChain.GetAccountDataBorshInto(ctx, offRampConfigPDA, &programData) require.NoError(t, err) return timelockSignerPDA.String() == programData.Owner.String() }, 30*time.Second, 5*time.Second, "OffRamp config PDA owner was not changed to timelock signer PDA") diff --git a/deployment/ccip/changeset/solana/utils.go b/deployment/ccip/changeset/solana/utils.go index ac571187a2d..47107b7d11c 100644 --- a/deployment/ccip/changeset/solana/utils.go +++ b/deployment/ccip/changeset/solana/utils.go @@ -2,8 +2,11 @@ package solana import ( "fmt" + "math/big" + "github.com/gagliardetto/solana-go" mcmsSolana "github.com/smartcontractkit/mcms/sdk/solana" + mcmsTypes "github.com/smartcontractkit/mcms/types" "github.com/smartcontractkit/chainlink/deployment" cs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" @@ -33,3 +36,27 @@ func ValidateMCMSConfig(e deployment.Environment, chainSelector uint64, mcms *cs } return nil } + +func BuildMCMSTxn(ixn solana.Instruction, programID string, contractType deployment.ContractType) (*mcmsTypes.Transaction, error) { + data, err := ixn.Data() + if err != nil { + return nil, fmt.Errorf("failed to extract data: %w", err) + } + for _, account := range ixn.Accounts() { + if account.IsSigner { + account.IsSigner = false + } + } + tx, err := mcmsSolana.NewTransaction( + programID, + data, + big.NewInt(0), // e.g. value + ixn.Accounts(), // pass along needed accounts + string(contractType), // some string identifying the target + []string{}, // any relevant metadata + ) + if err != nil { + return nil, fmt.Errorf("failed to create transaction: %w", err) + } + return &tx, nil +} diff --git a/deployment/ccip/changeset/solana_state.go b/deployment/ccip/changeset/solana_state.go index e48f5165677..a5ed3a93671 100644 --- a/deployment/ccip/changeset/solana_state.go +++ b/deployment/ccip/changeset/solana_state.go @@ -12,9 +12,14 @@ import ( solState "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_offramp" solOffRamp "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_offramp" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/fee_quoter" "github.com/smartcontractkit/chainlink/deployment" + commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" ) @@ -237,3 +242,58 @@ func FindSolanaAddress(tv deployment.TypeAndVersion, addresses map[string]deploy } return solana.PublicKey{} } + +func ValidateOwnershipSolana( + e *deployment.Environment, + chain deployment.SolChain, + mcms bool, + deployerKey solana.PublicKey, + programID solana.PublicKey, + contractType deployment.ContractType, +) error { + addresses, err := e.ExistingAddresses.AddressesForChain(chain.Selector) + if err != nil { + return fmt.Errorf("failed to get existing addresses: %w", err) + } + mcmState, err := state.MaybeLoadMCMSWithTimelockChainStateSolana(chain, addresses) + if err != nil { + return fmt.Errorf("failed to load MCMS with timelock chain state: %w", err) + } + timelockSignerPDA := state.GetTimelockSignerPDA(mcmState.TimelockProgram, mcmState.TimelockSeed) + config, _, err := solState.FindConfigPDA(programID) + if err != nil { + return fmt.Errorf("failed to find config PDA: %w", err) + } + switch contractType { + case Router: + programData := ccip_router.Config{} + err = chain.GetAccountDataBorshInto(e.GetContext(), config, &programData) + if err != nil { + return fmt.Errorf("failed to get account data: %w", err) + } + if err := commoncs.ValidateOwnershipSolanaCommon(mcms, deployerKey, timelockSignerPDA, programData.Owner); err != nil { + return fmt.Errorf("failed to validate ownership for router: %w", err) + } + case OffRamp: + programData := ccip_offramp.Config{} + err = chain.GetAccountDataBorshInto(e.GetContext(), config, &programData) + if err != nil { + return fmt.Errorf("failed to get account data: %w", err) + } + if err := commoncs.ValidateOwnershipSolanaCommon(mcms, deployerKey, timelockSignerPDA, programData.Owner); err != nil { + return fmt.Errorf("failed to validate ownership for offramp: %w", err) + } + case FeeQuoter: + programData := fee_quoter.Config{} + err = chain.GetAccountDataBorshInto(e.GetContext(), config, &programData) + if err != nil { + return fmt.Errorf("failed to get account data: %w", err) + } + if err := commoncs.ValidateOwnershipSolanaCommon(mcms, deployerKey, timelockSignerPDA, programData.Owner); err != nil { + return fmt.Errorf("failed to validate ownership for feequoter: %w", err) + } + default: + return fmt.Errorf("unsupported contract type: %s", contractType) + } + return nil +} diff --git a/deployment/ccip/changeset/testhelpers/test_helpers.go b/deployment/ccip/changeset/testhelpers/test_helpers.go index 33683f0c217..45e3a2a8b88 100644 --- a/deployment/ccip/changeset/testhelpers/test_helpers.go +++ b/deployment/ccip/changeset/testhelpers/test_helpers.go @@ -23,6 +23,8 @@ import ( ccipChangeSetSolana "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/solana" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6" commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/v1_6_0/fee_quoter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" @@ -1630,6 +1632,63 @@ func FindReceiverTargetAccount(receiverID solana.PublicKey) solana.PublicKey { return receiverTargetAccount } +func TransferOwnershipSolana( + t *testing.T, + e *deployment.Environment, + solChain uint64, + needTimelockDeployed bool, + transferRouter, transferFeeQuoter, transferOffRamp bool) (solana.PublicKey, solana.PublicKey) { + var err error + if needTimelockDeployed { + *e, err = commoncs.ApplyChangesetsV2(t, *e, []commoncs.ConfiguredChangeSet{ + commoncs.Configure( + deployment.CreateLegacyChangeSet(commoncs.DeployMCMSWithTimelockV2), + map[uint64]commontypes.MCMSWithTimelockConfigV2{ + solChain: { + Canceller: proposalutils.SingleGroupMCMSV2(t), + Proposer: proposalutils.SingleGroupMCMSV2(t), + Bypasser: proposalutils.SingleGroupMCMSV2(t), + TimelockMinDelay: big.NewInt(0), + }, + }, + ), + }) + require.NoError(t, err) + } + + addresses, err := e.ExistingAddresses.AddressesForChain(solChain) + require.NoError(t, err) + mcmState, err := state.MaybeLoadMCMSWithTimelockChainStateSolana(e.SolChains[solChain], addresses) + require.NoError(t, err) + + // Fund signer PDAs for timelock and mcm + // If we don't fund, execute() calls will fail with "no funds" errors. + timelockSignerPDA := state.GetTimelockSignerPDA(mcmState.TimelockProgram, mcmState.TimelockSeed) + mcmSignerPDA := state.GetMCMSignerPDA(mcmState.McmProgram, mcmState.ProposerMcmSeed) + memory.FundSolanaAccounts(e.GetContext(), t, []solana.PublicKey{timelockSignerPDA, mcmSignerPDA}, + 100, e.SolChains[solChain].Client) + t.Logf("funded timelock signer PDA: %s", timelockSignerPDA.String()) + t.Logf("funded mcm signer PDA: %s", mcmSignerPDA.String()) + // Apply transfer ownership changeset + *e, err = commoncs.ApplyChangesetsV2(t, *e, []commoncs.ConfiguredChangeSet{ + commoncs.Configure( + deployment.CreateLegacyChangeSet(ccipChangeSetSolana.TransferCCIPToMCMSWithTimelockSolana), + ccipChangeSetSolana.TransferCCIPToMCMSWithTimelockSolanaConfig{ + MinDelay: 1 * time.Second, + ContractsByChain: map[uint64]ccipChangeSetSolana.CCIPContractsToTransfer{ + solChain: { + Router: transferRouter, + FeeQuoter: transferFeeQuoter, + OffRamp: transferOffRamp, + }, + }, + }, + ), + }) + require.NoError(t, err) + return timelockSignerPDA, mcmSignerPDA +} + func GenTestTransferOwnershipConfig( e DeployedEnv, chains []uint64, diff --git a/deployment/ccip/changeset/v1_6/cs_chain_contracts.go b/deployment/ccip/changeset/v1_6/cs_chain_contracts.go index fdb66bf6087..0d0f6c5f959 100644 --- a/deployment/ccip/changeset/v1_6/cs_chain_contracts.go +++ b/deployment/ccip/changeset/v1_6/cs_chain_contracts.go @@ -20,7 +20,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - commonState "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/v1_6_0/fee_quoter" "github.com/smartcontractkit/chainlink/deployment" @@ -1422,24 +1421,12 @@ func (c SetOCR3OffRampConfig) validateRemoteChain(e *deployment.Environment, sta } switch family { case chain_selectors.FamilySolana: - chain, ok := e.SolChains[chainSelector] - if !ok { - return fmt.Errorf("chain %d not found in environment", chainSelector) - } chainState, ok := state.SolChains[chainSelector] if !ok { return fmt.Errorf("remote chain %d not found in onchain state", chainSelector) } - addresses, err := e.ExistingAddresses.AddressesForChain(chainSelector) - if err != nil { - return err - } - mcmState, err := commonState.MaybeLoadMCMSWithTimelockChainStateSolana(chain, addresses) - if err != nil { - return fmt.Errorf("error loading MCMS state for chain %d: %w", chainSelector, err) - } - if err := commoncs.ValidateOwnershipSolana(e.GetContext(), c.MCMS != nil, e.SolChains[chainSelector].DeployerKey.PublicKey(), mcmState.TimelockProgram, mcmState.TimelockSeed, chainState.Router); err != nil { - return err + if chainState.OffRamp.IsZero() { + return fmt.Errorf("missing OffRamp for chain %d", chainSelector) } case chain_selectors.FamilyEVM: chainState, ok := state.Chains[chainSelector] diff --git a/deployment/common/changeset/deploy_mcms_with_timelock.go b/deployment/common/changeset/deploy_mcms_with_timelock.go index cfe77a23c08..4ec3a140b82 100644 --- a/deployment/common/changeset/deploy_mcms_with_timelock.go +++ b/deployment/common/changeset/deploy_mcms_with_timelock.go @@ -13,7 +13,6 @@ import ( "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal" evminternal "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal/evm" solanainternal "github.com/smartcontractkit/chainlink/deployment/common/changeset/internal/solana" - "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" "github.com/smartcontractkit/chainlink/deployment/common/types" ) @@ -82,11 +81,15 @@ func ValidateOwnership(ctx context.Context, mcms bool, deployerKey, timelock com return nil } -// TODO: SOLANA_CCIP -func ValidateOwnershipSolana( - ctx context.Context, mcms bool, deployerKey, timelock solana.PublicKey, timelockSeed state.PDASeed, - ccipRouter solana.PublicKey, -) error { - // TODO: implement +func ValidateOwnershipSolanaCommon(mcms bool, deployerKey solana.PublicKey, timelockSignerPDA solana.PublicKey, programOwner solana.PublicKey) error { + if !mcms { + if deployerKey.String() != programOwner.String() { + return fmt.Errorf("deployer key %s does not match owner %s", deployerKey.String(), programOwner.String()) + } + } else { + if timelockSignerPDA.String() != programOwner.String() { + return fmt.Errorf("timelock signer PDA %s does not match owner %s", timelockSignerPDA.String(), programOwner.String()) + } + } return nil } From 93a406f617ec8bae08613c309460188debc028b0 Mon Sep 17 00:00:00 2001 From: Vyzaldy Sanchez Date: Mon, 24 Feb 2025 13:16:55 -0400 Subject: [PATCH 06/14] Sends extra config + chainlink-common bump (#16247) * Sends extra config + chainlink-common bump * Fixes CI * Bumps chainlink-common * Fixes CI * Fixes CI * Fixes CI * gomods tidy * Renames import * Renames Accept/Transmit fields into ShouldAccept/ShouldTransmit * bumps `chainlink-common` * bumps deps * bumps deps * bumps deps * bumps deps * bumps deps * bumps deps * bumps deps * Fixes test * gomods tidy * Fixes test * Generates OCR3 config view (#16395) * Generates OCR3 config view * WIP - parses the OCR3 config event into readable values * CRE-226: mv ContractSet; cleanup used compatibility * implement continue-on-error view * Loads readable `OracleConfig` into view * Fixes lint * Fixes view implementation * Adds test validating the `OracleConfig` view generation * Tests cleanup * Lint cleanup --------- Co-authored-by: krehermann <16602512+krehermann@users.noreply.github.com> * Fixes merge + gomods tidy * gomods tidy * Fixes test * Renames view field * Renames view field * Hex encodes view signers field values * Fixes merge conflicts issues * gomods tidy * Fixes merge conflicts issues * Fixes lint * Prevents break on 0 `RequestTimeout` * Prevents break on 0 `RequestTimeout` * gomods tidy * git merge conflicts * fixes lint * gomods tidy * gomods tidy * gomods tidy * Fixes merge conflicts issues * Fixes merge conflicts issues * bumps chainlink-common + fixes(?) * gomods tidy * gomods tidy * remove unused file * fixes conflicts * Updates comment * gomods tidy * gomods tidy * Renames property correctly * Sets lower `MaxBatchSize` on examples/tests * Sets lower `MaxBatchSize` on examples/tests * Fixes test --------- Co-authored-by: krehermann <16602512+krehermann@users.noreply.github.com> --- core/scripts/go.mod | 6 +- core/scripts/go.sum | 4 +- core/scripts/keystone/ocr_config.json | 6 +- .../src/generate_local_ocr3_config.go | 6 +- .../keystone/src/testdata/SampleConfig.json | 8 +- .../nodeclient/chainlink_models_test.go | 1 + deployment/go.mod | 4 +- deployment/go.sum | 4 +- .../keystone/changeset/accept_ownership.go | 4 +- .../changeset/append_node_capabilities.go | 2 +- deployment/keystone/changeset/compatiblity.go | 17 -- .../keystone/changeset/deploy_forwarder.go | 2 +- deployment/keystone/changeset/deploy_ocr3.go | 2 +- .../keystone/changeset/internal/deploy.go | 4 +- .../keystone/changeset/internal/ocr3config.go | 88 +++++++- .../changeset/internal/ocr3config_test.go | 12 +- .../keystone/changeset/internal/state.go | 55 +---- .../keystone/changeset/internal/state_test.go | 1 + deployment/keystone/changeset/state.go | 213 ++++++++++++++++++ .../keystone/changeset/test/env_setup.go | 30 +-- deployment/keystone/changeset/types.go | 13 ++ deployment/keystone/changeset/update_don.go | 2 +- .../changeset/update_node_capabilities.go | 2 +- deployment/keystone/changeset/update_nodes.go | 2 +- deployment/keystone/changeset/view.go | 37 ++- .../keystone/changeset/view_contracts.go | 164 ++++++++++++++ deployment/keystone/changeset/view_test.go | 145 ++++++++++-- deployment/keystone/view/view.go | 32 --- integration-tests/go.mod | 6 +- integration-tests/go.sum | 4 +- integration-tests/load/go.mod | 8 +- integration-tests/load/go.sum | 4 +- system-tests/lib/cre/contracts/contracts.go | 7 +- system-tests/lib/go.mod | 6 +- system-tests/lib/go.sum | 4 +- system-tests/tests/go.mod | 6 +- system-tests/tests/go.sum | 4 +- 37 files changed, 696 insertions(+), 219 deletions(-) create mode 100644 deployment/keystone/changeset/state.go create mode 100644 deployment/keystone/changeset/types.go create mode 100644 deployment/keystone/changeset/view_contracts.go delete mode 100644 deployment/keystone/view/view.go diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 680f47423a4..7fb73eed291 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -12,8 +12,8 @@ replace github.com/smartcontractkit/chainlink/deployment => ../../deployment // Using a separate `require` here to avoid surrounding line changes // creating potential merge conflicts. require ( - github.com/smartcontractkit/chainlink/deployment v0.0.0-20250128231431-9279badae2f0 - github.com/smartcontractkit/chainlink/v2 v2.19.0-ccip1.5.16-alpha.0.0.20250129223716-34cbaaab2d04 + github.com/smartcontractkit/chainlink/deployment v0.0.0-20250221182743-098d1b0a763a + github.com/smartcontractkit/chainlink/v2 v2.0.0-20250221182743-098d1b0a763a ) require ( @@ -34,7 +34,7 @@ require ( github.com/prometheus/client_golang v1.20.5 github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea + github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 github.com/smartcontractkit/chainlink-integrations/evm v0.0.0-20250213145514-41d874782c02 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.22 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 721b690ba76..fbe0b40ff8c 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1090,8 +1090,8 @@ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea/go.mod h1:Z2e1ynSJ4pg83b4Qldbmryc5lmnrI3ojOdg1FUloa68= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 h1:CvDfgWoLoYPapOumE/UZCplfCu5oNmy9BuH+6V6+fJ8= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5/go.mod h1:pDZagSGjs9U+l4YIFhveDznMHqxuuz+5vRxvVgpbdr8= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= diff --git a/core/scripts/keystone/ocr_config.json b/core/scripts/keystone/ocr_config.json index 31fe8988056..96691e48620 100644 --- a/core/scripts/keystone/ocr_config.json +++ b/core/scripts/keystone/ocr_config.json @@ -3,7 +3,7 @@ "MaxQueryLengthBytes": 1000000, "MaxObservationLengthBytes": 1000000, "MaxReportLengthBytes": 1000000, - "MaxRequestBatchSize": 1000, + "MaxBatchSize": 20, "UniqueReports": true, "DeltaProgressMillis": 5000, @@ -19,8 +19,8 @@ "MaxDurationQueryMillis": 1000, "MaxDurationObservationMillis": 1000, "MaxDurationReportMillis": 1000, - "MaxDurationAcceptMillis": 1000, - "MaxDurationTransmitMillis": 1000, + "MaxDurationShouldAcceptMillis": 1000, + "MaxDurationShouldTransmitMillis": 1000, "MaxFaultyOracles": 1 } diff --git a/core/scripts/keystone/src/generate_local_ocr3_config.go b/core/scripts/keystone/src/generate_local_ocr3_config.go index 68b1cade0c5..5dff8019179 100644 --- a/core/scripts/keystone/src/generate_local_ocr3_config.go +++ b/core/scripts/keystone/src/generate_local_ocr3_config.go @@ -76,7 +76,7 @@ func (g *generateLocalOCR3Config) Run(args []string) { "MaxQueryLengthBytes": 1000000, "MaxObservationLengthBytes": 1000000, "MaxReportLengthBytes": 1000000, - "MaxRequestBatchSize": 1000, + "MaxBatchSize": 20, "UniqueReports": true, "DeltaProgressMillis": 5000, "DeltaResendMillis": 5000, @@ -90,8 +90,8 @@ func (g *generateLocalOCR3Config) Run(args []string) { "MaxDurationQueryMillis": 1000, "MaxDurationObservationMillis": 1000, "MaxDurationReportMillis": 1000, - "MaxDurationAcceptMillis": 1000, - "MaxDurationTransmitMillis": 1000, + "MaxDurationShouldAcceptMillis": 1000, + "MaxDurationShouldTransmitMillis": 1000, "MaxFaultyOracles": 1}`) var cfg changeset.OracleConfig err = json.Unmarshal(config, &cfg) diff --git a/core/scripts/keystone/src/testdata/SampleConfig.json b/core/scripts/keystone/src/testdata/SampleConfig.json index 5d7893cb432..d39dc759af7 100644 --- a/core/scripts/keystone/src/testdata/SampleConfig.json +++ b/core/scripts/keystone/src/testdata/SampleConfig.json @@ -3,8 +3,9 @@ "MaxQueryLengthBytes": 1000000, "MaxObservationLengthBytes": 1000000, "MaxReportLengthBytes": 1000000, - "MaxRequestBatchSize": 1000, + "MaxBatchSize": 20, "UniqueReports": true, + "RequestTimeout": "30s", "DeltaProgressMillis": 5000, "DeltaResendMillis": 5000, "DeltaInitialMillis": 5000, @@ -19,8 +20,9 @@ "MaxDurationQueryMillis": 1000, "MaxDurationObservationMillis": 1000, "MaxDurationReportMillis": 1000, - "MaxDurationAcceptMillis": 1000, - "MaxDurationTransmitMillis": 1000, + "MaxDurationShouldAcceptMillis": 1000, + "MaxDurationShouldTransmitMillis": 1000, + "MaxFaultyOracles": 1 } } \ No newline at end of file diff --git a/deployment/environment/nodeclient/chainlink_models_test.go b/deployment/environment/nodeclient/chainlink_models_test.go index ae1e35eb33f..4f4c06dcbf2 100644 --- a/deployment/environment/nodeclient/chainlink_models_test.go +++ b/deployment/environment/nodeclient/chainlink_models_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/codec" + "github.com/smartcontractkit/chainlink/v2/core/services/job" evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) diff --git a/deployment/go.mod b/deployment/go.mod index aba9cc0aaec..f1a0f6fb3f3 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -7,7 +7,7 @@ replace github.com/smartcontractkit/chainlink/v2 => ../ // Using a separate inline `require` here to avoid surrounding line changes // creating potential merge conflicts. -require github.com/smartcontractkit/chainlink/v2 v2.0.0-20250128231431-9279badae2f0 +require github.com/smartcontractkit/chainlink/v2 v2.0.0-20250221182743-098d1b0a763a require ( github.com/Khan/genqlient v0.7.0 @@ -32,7 +32,7 @@ require ( github.com/smartcontractkit/chain-selectors v1.0.40 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 - github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea + github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20250211162441-3d6cea220efb github.com/smartcontractkit/chainlink-integrations/evm v0.0.0-20250213145514-41d874782c02 github.com/smartcontractkit/chainlink-protos/job-distributor v0.9.0 diff --git a/deployment/go.sum b/deployment/go.sum index c37601139cf..df1bf1dc209 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1138,8 +1138,8 @@ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea/go.mod h1:Z2e1ynSJ4pg83b4Qldbmryc5lmnrI3ojOdg1FUloa68= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 h1:CvDfgWoLoYPapOumE/UZCplfCu5oNmy9BuH+6V6+fJ8= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5/go.mod h1:pDZagSGjs9U+l4YIFhveDznMHqxuuz+5vRxvVgpbdr8= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= diff --git a/deployment/keystone/changeset/accept_ownership.go b/deployment/keystone/changeset/accept_ownership.go index dd709523bc2..684df720164 100644 --- a/deployment/keystone/changeset/accept_ownership.go +++ b/deployment/keystone/changeset/accept_ownership.go @@ -5,8 +5,6 @@ import ( "github.com/ethereum/go-ethereum/common" - kslib "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal" - "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/common/changeset" ) @@ -25,7 +23,7 @@ func AcceptAllOwnershipsProposal(e deployment.Environment, req *AcceptAllOwnersh chain := e.Chains[chainSelector] addrBook := e.ExistingAddresses - r, err := kslib.GetContractSets(e.Logger, &kslib.GetContractSetsRequest{ + r, err := GetContractSets(e.Logger, &GetContractSetsRequest{ Chains: map[uint64]deployment.Chain{ req.ChainSelector: chain, }, diff --git a/deployment/keystone/changeset/append_node_capabilities.go b/deployment/keystone/changeset/append_node_capabilities.go index fcd20b856ad..c2c3739a566 100644 --- a/deployment/keystone/changeset/append_node_capabilities.go +++ b/deployment/keystone/changeset/append_node_capabilities.go @@ -70,7 +70,7 @@ func (req *AppendNodeCapabilitiesRequest) convert(e deployment.Environment) (*in return nil, nil, fmt.Errorf("failed to validate UpdateNodeCapabilitiesRequest: %w", err) } registryChain := e.Chains[req.RegistryChainSel] // exists because of the validation above - resp, err := internal.GetContractSets(e.Logger, &internal.GetContractSetsRequest{ + resp, err := GetContractSets(e.Logger, &GetContractSetsRequest{ Chains: map[uint64]deployment.Chain{req.RegistryChainSel: registryChain}, AddressBook: e.ExistingAddresses, }) diff --git a/deployment/keystone/changeset/compatiblity.go b/deployment/keystone/changeset/compatiblity.go index b7a6e1b7c95..96e665f2525 100644 --- a/deployment/keystone/changeset/compatiblity.go +++ b/deployment/keystone/changeset/compatiblity.go @@ -19,21 +19,6 @@ type TopLevelConfigSource = internal.TopLevelConfigSource // GenerateOCR3Config generates an OCR3 config var GenerateOCR3Config = internal.GenerateOCR3Config -// FeedConsumer is a feed consumer contract type -var FeedConsumer = internal.FeedConsumer - -// KeystoneForwarder is a keystone forwarder contract type -var KeystoneForwarder = internal.KeystoneForwarder - -// GetContractSetsRequest is a request to get contract sets -type GetContractSetsRequest = internal.GetContractSetsRequest - -// GetContractSetsResponse is a response to get contract sets -type GetContractSetsResponse = internal.GetContractSetsResponse - -// GetContractSets gets contract sets -var GetContractSets = internal.GetContractSets - // RegisterNOPSRequest is a request to register NOPS type RegisterNOPSRequest = internal.RegisterNOPSRequest @@ -87,5 +72,3 @@ type DONCapabilityWithConfig = internal.DONCapabilityWithConfig type DeployRequest = internal.DeployRequest type DeployResponse = internal.DeployResponse - -type ContractSet = internal.ContractSet diff --git a/deployment/keystone/changeset/deploy_forwarder.go b/deployment/keystone/changeset/deploy_forwarder.go index 8a9cdf4d681..feccc14b6ce 100644 --- a/deployment/keystone/changeset/deploy_forwarder.go +++ b/deployment/keystone/changeset/deploy_forwarder.go @@ -87,7 +87,7 @@ func ConfigureForwardContracts(env deployment.Environment, req ConfigureForwardC return deployment.ChangesetOutput{}, fmt.Errorf("failed to configure forward contracts: %w", err) } - cresp, err := internal.GetContractSets(env.Logger, &internal.GetContractSetsRequest{ + cresp, err := GetContractSets(env.Logger, &GetContractSetsRequest{ Chains: env.Chains, AddressBook: env.ExistingAddresses, }) diff --git a/deployment/keystone/changeset/deploy_ocr3.go b/deployment/keystone/changeset/deploy_ocr3.go index c9da8b81237..070c7ef59ce 100644 --- a/deployment/keystone/changeset/deploy_ocr3.go +++ b/deployment/keystone/changeset/deploy_ocr3.go @@ -84,7 +84,7 @@ func ConfigureOCR3Contract(env deployment.Environment, cfg ConfigureOCR3Config) if resp.Ops == nil { return out, errors.New("expected MCMS operation to be non-nil") } - r, err := kslib.GetContractSets(env.Logger, &kslib.GetContractSetsRequest{ + r, err := GetContractSets(env.Logger, &GetContractSetsRequest{ Chains: env.Chains, AddressBook: env.ExistingAddresses, }) diff --git a/deployment/keystone/changeset/internal/deploy.go b/deployment/keystone/changeset/internal/deploy.go index bed5fd5e23c..e67ea047d7d 100644 --- a/deployment/keystone/changeset/internal/deploy.go +++ b/deployment/keystone/changeset/internal/deploy.go @@ -317,7 +317,7 @@ func ConfigureOCR3Contract(env *deployment.Environment, chainSel uint64, dons [] return fmt.Errorf("failed to get contract set for chain %d", chainSel) } - contract, err := contracts.GetOCR3Contract(nil) + contract, err := contracts.getOCR3Contract(nil) if err != nil { env.Logger.Errorf("failed to get OCR3 contract: %s", err) return fmt.Errorf("failed to get OCR3 contract: %w", err) @@ -375,7 +375,7 @@ func ConfigureOCR3ContractFromJD(env *deployment.Environment, cfg ConfigureOCR3C return nil, fmt.Errorf("failed to get contract set for chain %d", cfg.ChainSel) } - contract, err := contracts.GetOCR3Contract(cfg.Address) + contract, err := contracts.getOCR3Contract(cfg.Address) if err != nil { env.Logger.Errorf("%sfailed to get OCR3 contract at %s : %s", prefix, cfg.Address, err) return nil, fmt.Errorf("failed to get OCR3 contract: %w", err) diff --git a/deployment/keystone/changeset/internal/ocr3config.go b/deployment/keystone/changeset/internal/ocr3config.go index e8038134f02..39f1c3817b2 100644 --- a/deployment/keystone/changeset/internal/ocr3config.go +++ b/deployment/keystone/changeset/internal/ocr3config.go @@ -13,11 +13,15 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - mcmstypes "github.com/smartcontractkit/mcms/types" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/durationpb" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + mcmstypes "github.com/smartcontractkit/mcms/types" + + capocr3types "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types" "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" @@ -35,8 +39,12 @@ type OracleConfig struct { MaxQueryLengthBytes uint32 MaxObservationLengthBytes uint32 MaxReportLengthBytes uint32 - MaxRequestBatchSize uint32 + MaxOutcomeLengthBytes uint32 + MaxReportCount uint32 + MaxBatchSize uint32 + OutcomePruningThreshold uint64 UniqueReports bool + RequestTimeout time.Duration DeltaProgressMillis uint32 DeltaResendMillis uint32 @@ -48,14 +56,50 @@ type OracleConfig struct { MaxRoundsPerEpoch uint64 TransmissionSchedule []int - MaxDurationQueryMillis uint32 - MaxDurationObservationMillis uint32 - MaxDurationAcceptMillis uint32 - MaxDurationTransmitMillis uint32 + MaxDurationQueryMillis uint32 + MaxDurationObservationMillis uint32 + MaxDurationShouldAcceptMillis uint32 + MaxDurationShouldTransmitMillis uint32 MaxFaultyOracles int } +func (oc *OracleConfig) UnmarshalJSON(data []byte) error { + type aliasT OracleConfig + temp := &struct { + RequestTimeout string `json:"RequestTimeout"` + *aliasT + }{ + aliasT: (*aliasT)(oc), + } + if err := json.Unmarshal(data, temp); err != nil { + return fmt.Errorf("failed to unmarshal OracleConfig: %w", err) + } + + if temp.RequestTimeout == "" { + oc.RequestTimeout = 0 + } else { + requestTimeout, err := time.ParseDuration(temp.RequestTimeout) + if err != nil { + return fmt.Errorf("failed to parse RequestTimeout: %w", err) + } + oc.RequestTimeout = requestTimeout + } + + return nil +} + +func (oc OracleConfig) MarshalJSON() ([]byte, error) { + type aliasT OracleConfig + return json.Marshal(&struct { + RequestTimeout string `json:"RequestTimeout"` + *aliasT + }{ + RequestTimeout: oc.RequestTimeout.String(), + aliasT: (*aliasT)(&oc), + }) +} + type NodeKeys struct { EthAddress string `json:"EthAddress"` AptosAccount string `json:"AptosAccount"` @@ -202,7 +246,7 @@ func GenerateOCR3Config(cfg OracleConfig, nca []NodeKeys, secrets deployment.OCR return OCR2OracleConfig{}, fmt.Errorf("wrong num elements copied from ocr2 offchain public key. expected %d but got %d", ed25519.PublicKeySize, nCopied) } - offchainPubKeysBytes = append(offchainPubKeysBytes, types.OffchainPublicKey(pkBytesFixed)) + offchainPubKeysBytes = append(offchainPubKeysBytes, pkBytesFixed) } configPubKeysBytes := []types.ConfigEncryptionPublicKey{} @@ -218,7 +262,7 @@ func GenerateOCR3Config(cfg OracleConfig, nca []NodeKeys, secrets deployment.OCR return OCR2OracleConfig{}, fmt.Errorf("wrong num elements copied from ocr2 config public key. expected %d but got %d", ed25519.PublicKeySize, n) } - configPubKeysBytes = append(configPubKeysBytes, types.ConfigEncryptionPublicKey(pkBytesFixed)) + configPubKeysBytes = append(configPubKeysBytes, pkBytesFixed) } identities := []confighelper.OracleIdentityExtra{} @@ -234,6 +278,26 @@ func GenerateOCR3Config(cfg OracleConfig, nca []NodeKeys, secrets deployment.OCR }) } + // let's keep reqTimeout as nil if it's 0, so we can use the default value within `chainlink-common`. + // See: https://github.com/smartcontractkit/chainlink-common/blob/main/pkg/capabilities/consensus/ocr3/factory.go#L73 + var reqTimeout *durationpb.Duration + if cfg.RequestTimeout > 0 { + reqTimeout = durationpb.New(cfg.RequestTimeout) + } + cfgBytes, err := proto.Marshal(&capocr3types.ReportingPluginConfig{ + MaxQueryLengthBytes: cfg.MaxQueryLengthBytes, + MaxObservationLengthBytes: cfg.MaxObservationLengthBytes, + MaxReportLengthBytes: cfg.MaxReportLengthBytes, + MaxOutcomeLengthBytes: cfg.MaxOutcomeLengthBytes, + MaxReportCount: cfg.MaxReportCount, + MaxBatchSize: cfg.MaxBatchSize, + OutcomePruningThreshold: cfg.OutcomePruningThreshold, + RequestTimeout: reqTimeout, + }) + if err != nil { + return OCR2OracleConfig{}, fmt.Errorf("failed to marshal ReportingPluginConfig: %w", err) + } + signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig, err := ocr3confighelper.ContractSetConfigArgsDeterministic( secrets.EphemeralSk, secrets.SharedSecret, @@ -247,12 +311,12 @@ func GenerateOCR3Config(cfg OracleConfig, nca []NodeKeys, secrets deployment.OCR cfg.MaxRoundsPerEpoch, cfg.TransmissionSchedule, identities, - nil, // reportingPluginConfig - nil, // maxDurationInitialization + cfgBytes, // reportingPluginConfig + nil, // maxDurationInitialization time.Duration(cfg.MaxDurationQueryMillis)*time.Millisecond, time.Duration(cfg.MaxDurationObservationMillis)*time.Millisecond, - time.Duration(cfg.MaxDurationAcceptMillis)*time.Millisecond, - time.Duration(cfg.MaxDurationTransmitMillis)*time.Millisecond, + time.Duration(cfg.MaxDurationShouldAcceptMillis)*time.Millisecond, + time.Duration(cfg.MaxDurationShouldTransmitMillis)*time.Millisecond, cfg.MaxFaultyOracles, nil, // empty onChain config ) diff --git a/deployment/keystone/changeset/internal/ocr3config_test.go b/deployment/keystone/changeset/internal/ocr3config_test.go index 6703f4d9b04..1c7f8e5b7ef 100644 --- a/deployment/keystone/changeset/internal/ocr3config_test.go +++ b/deployment/keystone/changeset/internal/ocr3config_test.go @@ -49,7 +49,7 @@ var wantOCR3Config = `{ "F": 3, "OnchainConfig": "0x", "OffchainConfigVersion": 30, - "OffchainConfig": "0xc80180e497d012d00180e497d012d80180a8d6b907e00180cab5ee01e80180d88ee16ff0010afa01010a82022003dacd15fc96c965c648e3623180de002b71a97cf6eeca9affb91f461dcd6ce1820220255096a3b7ade10e29c648e0b407fc486180464f713446b1da04f013df6179c8820220dba3c61e5f8bec594be481bcaf67ecea0d1c2950edb15b158ce3dbc77877def3820220b4c4993d6c15fee63800db901a8b35fa419057610962caab1c1d7bed557091278202202a4c7dec127fdd8145e48c5edb9467225098bd8c8ad1dade868325b787affbde820220283471ed66d61fbe11f64eff65d738b59a0301c9a4f846280db26c64c9fdd3f8820220aa3419628ea3536783742d17d8adf05681aa6a6bd2b206fbde78c7e5aa38586d82022001496edce35663071d74472e02119432ba059b3904d205e4358014410e4f2be3820220ad08c2a5878cada53521f4e2bb449f191ccca7899246721a0deeea19f7b83f70820220c805572b813a072067eab2087ddbee8aa719090e12890b15c01094f0d3f74a5f8a02008a02008a02008a02008a02008a02008a02008a02008a02008a020098028094ebdc03a0028094ebdc03a8028094ebdc03b0028094ebdc03ba02f8010a20da47a8cc1c10796dd43f98ed113c648625e2e504c16ac5da9c65669e2377241b1220f5beca3bb11406079dc174183105c474c862a73c257ce8b3d9f5ca065e6264691a10805015e4203740495a23e93c1bd06ba81a10ca58ff36ffb0545dc3f800ddd6f8d0481a1076f664639ca8b5209e488895faa5460f1a104a1e89a7f2d8c89158f18856bf289c2a1a10c2f4330787831f419713ad4990e347d31a10fd403ec0797c001a2794b51d6178916d1a10e14fff88fdd3d1554ed861104ddc56a81a10b0284b9817fec2c3066c6f2651d17fc41a10b090233a67d502f78191c9e19a2a032b1a10e483414860bb612af50ee15ce8cd8ef5c00280e497d012c8028094ebdc03" + "OffchainConfig": "0xc80180e497d012d00180e497d012d80180a8d6b907e00180cab5ee01e80180d88ee16ff0010afa01010a82022003dacd15fc96c965c648e3623180de002b71a97cf6eeca9affb91f461dcd6ce1820220255096a3b7ade10e29c648e0b407fc486180464f713446b1da04f013df6179c8820220dba3c61e5f8bec594be481bcaf67ecea0d1c2950edb15b158ce3dbc77877def3820220b4c4993d6c15fee63800db901a8b35fa419057610962caab1c1d7bed557091278202202a4c7dec127fdd8145e48c5edb9467225098bd8c8ad1dade868325b787affbde820220283471ed66d61fbe11f64eff65d738b59a0301c9a4f846280db26c64c9fdd3f8820220aa3419628ea3536783742d17d8adf05681aa6a6bd2b206fbde78c7e5aa38586d82022001496edce35663071d74472e02119432ba059b3904d205e4358014410e4f2be3820220ad08c2a5878cada53521f4e2bb449f191ccca7899246721a0deeea19f7b83f70820220c805572b813a072067eab2087ddbee8aa719090e12890b15c01094f0d3f74a5f8a02008a02008a02008a02008a02008a02008a02008a02008a02008a020092021b08c0843d10c0843d18c0843d20c0843d2814301438901c4202081e98028094ebdc03a0028094ebdc03a8028094ebdc03b0028094ebdc03ba02f8010a20da47a8cc1c10796dd43f98ed113c648625e2e504c16ac5da9c65669e2377241b1220f5beca3bb11406079dc174183105c474c862a73c257ce8b3d9f5ca065e6264691a10805015e4203740495a23e93c1bd06ba81a10ca58ff36ffb0545dc3f800ddd6f8d0481a1076f664639ca8b5209e488895faa5460f1a104a1e89a7f2d8c89158f18856bf289c2a1a10c2f4330787831f419713ad4990e347d31a10fd403ec0797c001a2794b51d6178916d1a10e14fff88fdd3d1554ed861104ddc56a81a10b0284b9817fec2c3066c6f2651d17fc41a10b090233a67d502f78191c9e19a2a032b1a10e483414860bb612af50ee15ce8cd8ef5c00280e497d012c8028094ebdc03" }` var ocr3Cfg = ` @@ -57,8 +57,12 @@ var ocr3Cfg = ` "MaxQueryLengthBytes": 1000000, "MaxObservationLengthBytes": 1000000, "MaxReportLengthBytes": 1000000, - "MaxRequestBatchSize": 1000, + "MaxOutcomeLengthBytes": 1000000, + "MaxReportCount": 20, + "MaxBatchSize": 20, + "OutcomePruningThreshold": 3600, "UniqueReports": true, + "RequestTimeout": "30s", "DeltaProgressMillis": 5000, "DeltaResendMillis": 5000, "DeltaInitialMillis": 5000, @@ -73,8 +77,8 @@ var ocr3Cfg = ` "MaxDurationQueryMillis": 1000, "MaxDurationObservationMillis": 1000, "MaxDurationReportMillis": 1000, - "MaxDurationAcceptMillis": 1000, - "MaxDurationTransmitMillis": 1000, + "MaxDurationShouldAcceptMillis": 1000, + "MaxDurationShouldTransmitMillis": 1000, "MaxFaultyOracles": 3 }` diff --git a/deployment/keystone/changeset/internal/state.go b/deployment/keystone/changeset/internal/state.go index 1a4a1ea34aa..2c68688b67b 100644 --- a/deployment/keystone/changeset/internal/state.go +++ b/deployment/keystone/changeset/internal/state.go @@ -10,8 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/deployment" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" - common_v1_0 "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" - "github.com/smartcontractkit/chainlink/deployment/keystone/view" + capabilities_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0" forwarder "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder_1_0_0" ocr3_capability "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability_1_0_0" @@ -32,7 +31,10 @@ type GetContractSetsResponse struct { ContractSets map[uint64]ContractSet } -// TODO move this out of internal +// ContractSet is a set of contracts for a single chain +// It is a mirror of changeset.ContractSet, and acts an an adapter to the internal package +// +// TODO: remove after CRE-227 type ContractSet struct { commonchangeset.MCMSWithTimelockState OCR3 map[common.Address]*ocr3_capability.OCR3Capability @@ -41,52 +43,7 @@ type ContractSet struct { WorkflowRegistry *workflow_registry.WorkflowRegistry } -func (cs ContractSet) TransferableContracts() []common.Address { - var out []common.Address - if cs.OCR3 != nil { - for _, ocr := range cs.OCR3 { - out = append(out, ocr.Address()) - } - } - if cs.Forwarder != nil { - out = append(out, cs.Forwarder.Address()) - } - if cs.CapabilitiesRegistry != nil { - out = append(out, cs.CapabilitiesRegistry.Address()) - } - if cs.WorkflowRegistry != nil { - out = append(out, cs.WorkflowRegistry.Address()) - } - return out -} - -func (cs ContractSet) View() (view.KeystoneChainView, error) { - out := view.NewKeystoneChainView() - if cs.CapabilitiesRegistry != nil { - capRegView, err := common_v1_0.GenerateCapabilityRegistryView(cs.CapabilitiesRegistry) - if err != nil { - return view.KeystoneChainView{}, err - } - out.CapabilityRegistry[cs.CapabilitiesRegistry.Address().String()] = capRegView - } - - // Process the workflow registry and print if WorkflowRegistryError errors. - if cs.WorkflowRegistry != nil { - wrView, wrErrs := common_v1_0.GenerateWorkflowRegistryView(cs.WorkflowRegistry) - for _, err := range wrErrs { - var wre *common_v1_0.WorkflowRegistryError - if !errors.As(err, &wre) { - return view.KeystoneChainView{}, err - } - fmt.Println("WorkflowRegistry error:", err) - } - out.WorkflowRegistry[cs.WorkflowRegistry.Address().String()] = wrView - } - - return out, nil -} - -func (cs ContractSet) GetOCR3Contract(addr *common.Address) (*ocr3_capability.OCR3Capability, error) { +func (cs ContractSet) getOCR3Contract(addr *common.Address) (*ocr3_capability.OCR3Capability, error) { return getOCR3Contract(cs.OCR3, addr) } diff --git a/deployment/keystone/changeset/internal/state_test.go b/deployment/keystone/changeset/internal/state_test.go index bfed62266ca..057cad4c10c 100644 --- a/deployment/keystone/changeset/internal/state_test.go +++ b/deployment/keystone/changeset/internal/state_test.go @@ -8,6 +8,7 @@ import ( chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/deployment" commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" diff --git a/deployment/keystone/changeset/state.go b/deployment/keystone/changeset/state.go new file mode 100644 index 00000000000..be7f63b7410 --- /dev/null +++ b/deployment/keystone/changeset/state.go @@ -0,0 +1,213 @@ +package changeset + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + + "github.com/smartcontractkit/chainlink/deployment" + commonchangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset" + common_v1_0 "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" + "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal" + + capabilities_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0" + forwarder "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder_1_0_0" + ocr3_capability "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability_1_0_0" + workflow_registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/workflow/generated/workflow_registry_wrapper" +) + +type GetContractSetsRequest struct { + Chains map[uint64]deployment.Chain + AddressBook deployment.AddressBook +} + +type GetContractSetsResponse struct { + ContractSets map[uint64]ContractSet +} + +type ContractSet struct { + commonchangeset.MCMSWithTimelockState + OCR3 map[common.Address]*ocr3_capability.OCR3Capability + Forwarder *forwarder.KeystoneForwarder + CapabilitiesRegistry *capabilities_registry.CapabilitiesRegistry + WorkflowRegistry *workflow_registry.WorkflowRegistry +} + +func (cs ContractSet) Convert() internal.ContractSet { + return internal.ContractSet{ + MCMSWithTimelockState: commonchangeset.MCMSWithTimelockState{ + MCMSWithTimelockContracts: cs.MCMSWithTimelockContracts, + }, + Forwarder: cs.Forwarder, + WorkflowRegistry: cs.WorkflowRegistry, + OCR3: cs.OCR3, + CapabilitiesRegistry: cs.CapabilitiesRegistry, + } +} + +func (cs ContractSet) TransferableContracts() []common.Address { + var out []common.Address + if cs.OCR3 != nil { + for _, ocr := range cs.OCR3 { + out = append(out, ocr.Address()) + } + } + if cs.Forwarder != nil { + out = append(out, cs.Forwarder.Address()) + } + if cs.CapabilitiesRegistry != nil { + out = append(out, cs.CapabilitiesRegistry.Address()) + } + if cs.WorkflowRegistry != nil { + out = append(out, cs.WorkflowRegistry.Address()) + } + return out +} + +// View is a view of the keystone chain +// It is best effort and logs errors +func (cs ContractSet) View(lggr logger.Logger) (KeystoneChainView, error) { + out := NewKeystoneChainView() + var allErrs error + if cs.CapabilitiesRegistry != nil { + capRegView, err := common_v1_0.GenerateCapabilityRegistryView(cs.CapabilitiesRegistry) + if err != nil { + allErrs = errors.Join(allErrs, err) + lggr.Warn("failed to generate capability registry view: %w", err) + } + out.CapabilityRegistry[cs.CapabilitiesRegistry.Address().String()] = capRegView + } + + if cs.OCR3 != nil { + for addr, ocr3Cap := range cs.OCR3 { + oc := *ocr3Cap + addrCopy := addr + ocrView, err := GenerateOCR3ConfigView(oc) + if err != nil { + allErrs = errors.Join(allErrs, err) + // don't block view on single OCR3 not being configured + if errors.Is(err, ErrOCR3NotConfigured) { + lggr.Warnf("ocr3 not configured for address %s", addr) + } else { + lggr.Errorf("failed to generate OCR3 config view: %v", err) + } + } + out.OCRContracts[addrCopy.String()] = ocrView + } + } + + // Process the workflow registry and print if WorkflowRegistryError errors. + if cs.WorkflowRegistry != nil { + wrView, wrErrs := common_v1_0.GenerateWorkflowRegistryView(cs.WorkflowRegistry) + for _, err := range wrErrs { + allErrs = errors.Join(allErrs, err) + lggr.Errorf("WorkflowRegistry error: %v", err) + } + out.WorkflowRegistry[cs.WorkflowRegistry.Address().String()] = wrView + } + + return out, allErrs +} + +func (cs ContractSet) GetOCR3Contract(addr *common.Address) (*ocr3_capability.OCR3Capability, error) { + return getOCR3Contract(cs.OCR3, addr) +} + +func GetContractSets(lggr logger.Logger, req *GetContractSetsRequest) (*GetContractSetsResponse, error) { + resp := &GetContractSetsResponse{ + ContractSets: make(map[uint64]ContractSet), + } + for id, chain := range req.Chains { + addrs, err := req.AddressBook.AddressesForChain(id) + if err != nil { + return nil, fmt.Errorf("failed to get addresses for chain %d: %w", id, err) + } + cs, err := loadContractSet(lggr, chain, addrs) + if err != nil { + return nil, fmt.Errorf("failed to load contract set for chain %d: %w", id, err) + } + resp.ContractSets[id] = *cs + } + return resp, nil +} + +func loadContractSet(lggr logger.Logger, chain deployment.Chain, addresses map[string]deployment.TypeAndVersion) (*ContractSet, error) { + var out ContractSet + mcmsWithTimelock, err := commonchangeset.MaybeLoadMCMSWithTimelockChainState(chain, addresses) + if err != nil { + return nil, fmt.Errorf("failed to load mcms contract: %w", err) + } + out.MCMSWithTimelockState = *mcmsWithTimelock + + for addr, tv := range addresses { + // todo handle versions + switch tv.Type { + case CapabilitiesRegistry: + c, err := capabilities_registry.NewCapabilitiesRegistry(common.HexToAddress(addr), chain.Client) + if err != nil { + return nil, fmt.Errorf("failed to create capability registry contract from address %s: %w", addr, err) + } + out.CapabilitiesRegistry = c + case KeystoneForwarder: + c, err := forwarder.NewKeystoneForwarder(common.HexToAddress(addr), chain.Client) + if err != nil { + return nil, fmt.Errorf("failed to create forwarder contract from address %s: %w", addr, err) + } + out.Forwarder = c + case OCR3Capability: + c, err := ocr3_capability.NewOCR3Capability(common.HexToAddress(addr), chain.Client) + if err != nil { + return nil, fmt.Errorf("failed to create OCR3Capability contract from address %s: %w", addr, err) + } + if out.OCR3 == nil { + out.OCR3 = make(map[common.Address]*ocr3_capability.OCR3Capability) + } + out.OCR3[common.HexToAddress(addr)] = c + case WorkflowRegistry: + c, err := workflow_registry.NewWorkflowRegistry(common.HexToAddress(addr), chain.Client) + if err != nil { + return nil, fmt.Errorf("failed to create OCR3Capability contract from address %s: %w", addr, err) + } + out.WorkflowRegistry = c + default: + lggr.Warnw("unknown contract type", "type", tv.Type) + // ignore unknown contract types + } + } + return &out, nil +} + +// getOCR3Contract returns the OCR3 contract from the contract set. By default, it returns the only +// contract in the set if there is no address specified. If an address is specified, it returns the +// contract with that address. If the address is specified but not found in the contract set, it returns +// an error. +func getOCR3Contract(contracts map[common.Address]*ocr3_capability.OCR3Capability, addr *common.Address) (*ocr3_capability.OCR3Capability, error) { + // Fail if the OCR3 contract address is unspecified and there are multiple OCR3 contracts + if addr == nil && len(contracts) > 1 { + return nil, errors.New("OCR contract address is unspecified") + } + + // Use the first OCR3 contract if the address is unspecified + if addr == nil && len(contracts) == 1 { + // use the first OCR3 contract + for _, c := range contracts { + return c, nil + } + } + + // Select the OCR3 contract by address + if contract, ok := contracts[*addr]; ok { + return contract, nil + } + + addrSet := make([]string, 0, len(contracts)) + for a := range contracts { + addrSet = append(addrSet, a.String()) + } + + // Fail if the OCR3 contract address is specified but not found in the contract set + return nil, fmt.Errorf("OCR3 contract address %s not found in contract set %v", *addr, addrSet) +} diff --git a/deployment/keystone/changeset/test/env_setup.go b/deployment/keystone/changeset/test/env_setup.go index 2bce7fd3db7..036762dade6 100644 --- a/deployment/keystone/changeset/test/env_setup.go +++ b/deployment/keystone/changeset/test/env_setup.go @@ -21,7 +21,7 @@ import ( commontypes "github.com/smartcontractkit/chainlink/deployment/common/types" "github.com/smartcontractkit/chainlink/deployment/environment/memory" envtest "github.com/smartcontractkit/chainlink/deployment/environment/test" - kschangeset "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" + changeset "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal" "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/workflowregistry" @@ -51,7 +51,7 @@ func (c DonConfig) Validate() error { } type testEnvIface interface { - ContractSets() map[uint64]internal.ContractSet + ContractSets() map[uint64]changeset.ContractSet CapabilitiesRegistry() *kcr.CapabilitiesRegistry CapabilityInfos() []kcr.CapabilitiesRegistryCapabilityInfo Nops() []kcr.CapabilitiesRegistryNodeOperatorAdded @@ -102,8 +102,8 @@ type EnvWrapper struct { dons testDons } -func (te EnvWrapper) ContractSets() map[uint64]internal.ContractSet { - r, err := internal.GetContractSets(te.Env.Logger, &internal.GetContractSetsRequest{ +func (te EnvWrapper) ContractSets() map[uint64]changeset.ContractSet { + r, err := changeset.GetContractSets(te.Env.Logger, &changeset.GetContractSetsRequest{ Chains: te.Env.Chains, AddressBook: te.Env.ExistingAddresses, }) @@ -112,7 +112,7 @@ func (te EnvWrapper) ContractSets() map[uint64]internal.ContractSet { } func (te EnvWrapper) CapabilitiesRegistry() *kcr.CapabilitiesRegistry { - r, err := internal.GetContractSets(te.Env.Logger, &internal.GetContractSetsRequest{ + r, err := changeset.GetContractSets(te.Env.Logger, &changeset.GetContractSetsRequest{ Chains: te.Env.Chains, AddressBook: te.Env.ExistingAddresses, }) @@ -161,16 +161,16 @@ func initEnv(t *testing.T, nChains int) (registryChainSel uint64, env deployment } env, err := commonchangeset.Apply(t, env, nil, commonchangeset.Configure( - deployment.CreateLegacyChangeSet(kschangeset.DeployCapabilityRegistry), + deployment.CreateLegacyChangeSet(changeset.DeployCapabilityRegistry), registryChainSel, ), commonchangeset.Configure( - deployment.CreateLegacyChangeSet(kschangeset.DeployOCR3), + deployment.CreateLegacyChangeSet(changeset.DeployOCR3), registryChainSel, ), commonchangeset.Configure( - deployment.CreateLegacyChangeSet(kschangeset.DeployForwarder), - kschangeset.DeployForwarderRequest{}, + deployment.CreateLegacyChangeSet(changeset.DeployForwarder), + changeset.DeployForwarderRequest{}, ), commonchangeset.Configure( deployment.CreateLegacyChangeSet(workflowregistry.Deploy), @@ -263,7 +263,7 @@ func setupTestEnv(t *testing.T, c EnvWrapperConfig) EnvWrapper { } var allDons = []internal.DonCapabilities{wfDonCapabilities, cwDonCapabilities, assetDonCapabilities} - csOut, err := kschangeset.ConfigureInitialContractsChangeset(env, kschangeset.InitialContractsCfg{ + csOut, err := changeset.ConfigureInitialContractsChangeset(env, changeset.InitialContractsCfg{ RegistryChainSel: registryChainSel, Dons: allDons, OCR3Config: &ocr3Config, @@ -271,12 +271,12 @@ func setupTestEnv(t *testing.T, c EnvWrapperConfig) EnvWrapper { require.NoError(t, err) require.Nil(t, csOut.AddressBook, "no new addresses should be created in configure initial contracts") - req := &internal.GetContractSetsRequest{ + req := &changeset.GetContractSetsRequest{ Chains: env.Chains, AddressBook: env.ExistingAddresses, } - contractSetsResp, err := internal.GetContractSets(lggr, req) + contractSetsResp, err := changeset.GetContractSets(lggr, req) require.NoError(t, err) require.Len(t, contractSetsResp.ContractSets, len(env.Chains)) // check the registry @@ -311,7 +311,7 @@ func setupTestEnv(t *testing.T, c EnvWrapperConfig) EnvWrapper { ) require.NoError(t, err) // extract the MCMS address - r, err := internal.GetContractSets(lggr, &internal.GetContractSetsRequest{ + r, err := changeset.GetContractSets(lggr, &changeset.GetContractSetsRequest{ Chains: env.Chains, AddressBook: env.ExistingAddresses, }) @@ -327,8 +327,8 @@ func setupTestEnv(t *testing.T, c EnvWrapperConfig) EnvWrapper { sel: {Timelock: mcms.Timelock, CallProxy: mcms.CallProxy}, }, commonchangeset.Configure( - deployment.CreateLegacyChangeSet(kschangeset.AcceptAllOwnershipsProposal), - &kschangeset.AcceptAllOwnershipRequest{ + deployment.CreateLegacyChangeSet(changeset.AcceptAllOwnershipsProposal), + &changeset.AcceptAllOwnershipRequest{ ChainSelector: sel, MinDelay: 0, }, diff --git a/deployment/keystone/changeset/types.go b/deployment/keystone/changeset/types.go new file mode 100644 index 00000000000..ae5718c4e3f --- /dev/null +++ b/deployment/keystone/changeset/types.go @@ -0,0 +1,13 @@ +package changeset + +import "github.com/smartcontractkit/chainlink/deployment" + +var ( + CapabilitiesRegistry deployment.ContractType = "CapabilitiesRegistry" // https://github.com/smartcontractkit/chainlink/blob/50c1b3dbf31bd145b312739b08967600a5c67f30/contracts/src/v0.8/keystone/CapabilitiesRegistry.sol#L392 + WorkflowRegistry deployment.ContractType = "WorkflowRegistry" // https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/workflow/WorkflowRegistry.sol + KeystoneForwarder deployment.ContractType = "KeystoneForwarder" // https://github.com/smartcontractkit/chainlink/blob/50c1b3dbf31bd145b312739b08967600a5c67f30/contracts/src/v0.8/keystone/KeystoneForwarder.sol#L90 + OCR3Capability deployment.ContractType = "OCR3Capability" // https://github.com/smartcontractkit/chainlink/blob/50c1b3dbf31bd145b312739b08967600a5c67f30/contracts/src/v0.8/keystone/OCR3Capability.sol#L12 + FeedConsumer deployment.ContractType = "FeedConsumer" // no type and a version in contract https://github.com/smartcontractkit/chainlink/blob/89183a8a5d22b1aeca0ade3b76d16aa84067aa57/contracts/src/v0.8/keystone/KeystoneFeedsConsumer.sol#L1 + RBACTimelock deployment.ContractType = "RBACTimelock" // no type and a version in contract https://github.com/smartcontractkit/ccip-owner-contracts/blob/main/src/RBACTimelock.sol + ProposerManyChainMultiSig deployment.ContractType = "ProposerManyChainMultiSig" // no type and a version in contract https://github.com/smartcontractkit/ccip-owner-contracts/blob/main/src/ManyChainMultiSig.sol +) diff --git a/deployment/keystone/changeset/update_don.go b/deployment/keystone/changeset/update_don.go index 7d02db0eda0..aa942a60542 100644 --- a/deployment/keystone/changeset/update_don.go +++ b/deployment/keystone/changeset/update_don.go @@ -98,7 +98,7 @@ func appendRequest(r *UpdateDonRequest) *AppendNodeCapabilitiesRequest { } func updateDonRequest(env deployment.Environment, r *UpdateDonRequest) (*internal.UpdateDonRequest, error) { - resp, err := internal.GetContractSets(env.Logger, &internal.GetContractSetsRequest{ + resp, err := GetContractSets(env.Logger, &GetContractSetsRequest{ Chains: env.Chains, AddressBook: env.ExistingAddresses, }) diff --git a/deployment/keystone/changeset/update_node_capabilities.go b/deployment/keystone/changeset/update_node_capabilities.go index 38cd8b6dfb5..6a9a22a78a9 100644 --- a/deployment/keystone/changeset/update_node_capabilities.go +++ b/deployment/keystone/changeset/update_node_capabilities.go @@ -85,7 +85,7 @@ func (req *MutateNodeCapabilitiesRequest) updateNodeCapabilitiesImplRequest(e de return nil, nil, fmt.Errorf("failed to validate UpdateNodeCapabilitiesRequest: %w", err) } registryChain := e.Chains[req.RegistryChainSel] // exists because of the validation above - resp, err := internal.GetContractSets(e.Logger, &internal.GetContractSetsRequest{ + resp, err := GetContractSets(e.Logger, &GetContractSetsRequest{ Chains: map[uint64]deployment.Chain{req.RegistryChainSel: registryChain}, AddressBook: e.ExistingAddresses, }) diff --git a/deployment/keystone/changeset/update_nodes.go b/deployment/keystone/changeset/update_nodes.go index a09a2b86ced..70857ef41c7 100644 --- a/deployment/keystone/changeset/update_nodes.go +++ b/deployment/keystone/changeset/update_nodes.go @@ -63,7 +63,7 @@ func UpdateNodes(env deployment.Environment, req *UpdateNodesRequest) (deploymen if !ok { return deployment.ChangesetOutput{}, fmt.Errorf("registry chain selector %d does not exist in environment", req.RegistryChainSel) } - cresp, err := internal.GetContractSets(env.Logger, &internal.GetContractSetsRequest{ + cresp, err := GetContractSets(env.Logger, &GetContractSetsRequest{ Chains: env.Chains, AddressBook: env.ExistingAddresses, }) diff --git a/deployment/keystone/changeset/view.go b/deployment/keystone/changeset/view.go index f6f495fd30b..0a5667781ed 100644 --- a/deployment/keystone/changeset/view.go +++ b/deployment/keystone/changeset/view.go @@ -2,48 +2,61 @@ package changeset import ( "encoding/json" + "errors" "fmt" chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink/deployment" commonview "github.com/smartcontractkit/chainlink/deployment/common/view" - "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal" - "github.com/smartcontractkit/chainlink/deployment/keystone/view" ) var _ deployment.ViewState = ViewKeystone func ViewKeystone(e deployment.Environment) (json.Marshaler, error) { - state, err := internal.GetContractSets(e.Logger, &internal.GetContractSetsRequest{ + lggr := e.Logger + state, err := GetContractSets(e.Logger, &GetContractSetsRequest{ Chains: e.Chains, AddressBook: e.ExistingAddresses, }) + // this error is unrecoverable if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get contract sets: %w", err) } - chainViews := make(map[string]view.KeystoneChainView) + var viewErrs error + chainViews := make(map[string]KeystoneChainView) for chainSel, contracts := range state.ContractSets { chainid, err := chainsel.ChainIdFromSelector(chainSel) if err != nil { - return nil, fmt.Errorf("failed to resolve chain id for selector %d: %w", chainSel, err) + err2 := fmt.Errorf("failed to resolve chain id for selector %d: %w", chainSel, err) + lggr.Error(err2) + viewErrs = errors.Join(viewErrs, err2) + continue } chainName, err := chainsel.NameFromChainId(chainid) if err != nil { - return nil, fmt.Errorf("failed to get name for chainid %d selector %d:%w", chainid, chainSel, err) + err2 := fmt.Errorf("failed to resolve chain name for chain id %d: %w", chainid, err) + lggr.Error(err2) + viewErrs = errors.Join(viewErrs, err2) + continue } - v, err := contracts.View() + v, err := contracts.View(e.Logger) if err != nil { - return nil, fmt.Errorf("failed to view contract set: %w", err) + err2 := fmt.Errorf("failed to view chain %s: %w", chainName, err) + lggr.Error(err2) + viewErrs = errors.Join(viewErrs, err2) + // don't continue; add the partial view } chainViews[chainName] = v } nopsView, err := commonview.GenerateNopsView(e.NodeIDs, e.Offchain) if err != nil { - return nil, fmt.Errorf("failed to view nops: %w", err) + err2 := fmt.Errorf("failed to view nops: %w", err) + lggr.Error(err2) + viewErrs = errors.Join(viewErrs, err2) } - return &view.KeystoneView{ + return &KeystoneView{ Chains: chainViews, Nops: nopsView, - }, nil + }, viewErrs } diff --git a/deployment/keystone/changeset/view_contracts.go b/deployment/keystone/changeset/view_contracts.go new file mode 100644 index 00000000000..a5868f4d74e --- /dev/null +++ b/deployment/keystone/changeset/view_contracts.go @@ -0,0 +1,164 @@ +package changeset + +import ( + "encoding/hex" + "encoding/json" + "errors" + "math" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "google.golang.org/protobuf/proto" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + capocr3types "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types" + + ocr3_capability "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability_1_0_0" + + "github.com/smartcontractkit/chainlink/deployment/common/view" + common_v1_0 "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" +) + +type KeystoneChainView struct { + CapabilityRegistry map[string]common_v1_0.CapabilityRegistryView `json:"capabilityRegistry,omitempty"` + // OCRContracts is a map of OCR3 contract addresses to their configuration view + OCRContracts map[string]OCR3ConfigView `json:"ocrContracts,omitempty"` + WorkflowRegistry map[string]common_v1_0.WorkflowRegistryView `json:"workflowRegistry,omitempty"` +} + +type OCR3ConfigView struct { + Signers []string `json:"signers"` + Transmitters []ocr2types.Account `json:"transmitters"` + F uint8 `json:"f"` + OnchainConfig []byte `json:"onchainConfig"` + OffchainConfigVersion uint64 `json:"offchainConfigVersion"` + OffchainConfig OracleConfig `json:"offchainConfig"` +} + +var ErrOCR3NotConfigured = errors.New("OCR3 not configured") + +func GenerateOCR3ConfigView(ocr3Cap ocr3_capability.OCR3Capability) (OCR3ConfigView, error) { + details, err := ocr3Cap.LatestConfigDetails(nil) + if err != nil { + return OCR3ConfigView{}, err + } + + blockNumber := uint64(details.BlockNumber) + configIterator, err := ocr3Cap.FilterConfigSet(&bind.FilterOpts{ + Start: blockNumber, + End: &blockNumber, + Context: nil, + }) + if err != nil { + return OCR3ConfigView{}, err + } + var config *ocr3_capability.OCR3CapabilityConfigSet + for configIterator.Next() { + // We wait for the iterator to receive an event + if configIterator.Event == nil { + return OCR3ConfigView{}, ErrOCR3NotConfigured + } + + config = configIterator.Event + } + if config == nil { + return OCR3ConfigView{}, ErrOCR3NotConfigured + } + + var signers []ocr2types.OnchainPublicKey + var readableSigners []string + for _, s := range config.Signers { + signers = append(signers, s) + readableSigners = append(readableSigners, hex.EncodeToString(s)) + } + var transmitters []ocr2types.Account + for _, t := range config.Transmitters { + transmitters = append(transmitters, ocr2types.Account(t.String())) + } + // `PublicConfigFromContractConfig` returns the `ocr2types.PublicConfig` that contains all the `OracleConfig` fields we need, including the + // report plugin config. + publicConfig, err := ocr3confighelper.PublicConfigFromContractConfig(true, ocr2types.ContractConfig{ + ConfigDigest: config.ConfigDigest, + ConfigCount: config.ConfigCount, + Signers: signers, + Transmitters: transmitters, + F: config.F, + OnchainConfig: nil, // empty onChain config, currently we always use a nil onchain config when calling SetConfig + OffchainConfigVersion: config.OffchainConfigVersion, + OffchainConfig: config.OffchainConfig, + }) + if err != nil { + return OCR3ConfigView{}, err + } + var cfg capocr3types.ReportingPluginConfig + if err = proto.Unmarshal(publicConfig.ReportingPluginConfig, &cfg); err != nil { + return OCR3ConfigView{}, err + } + oracleConfig := OracleConfig{ + MaxQueryLengthBytes: cfg.MaxQueryLengthBytes, + MaxObservationLengthBytes: cfg.MaxObservationLengthBytes, + MaxReportLengthBytes: cfg.MaxReportLengthBytes, + MaxOutcomeLengthBytes: cfg.MaxOutcomeLengthBytes, + MaxReportCount: cfg.MaxReportCount, + MaxBatchSize: cfg.MaxBatchSize, + OutcomePruningThreshold: cfg.OutcomePruningThreshold, + RequestTimeout: cfg.RequestTimeout.AsDuration(), + UniqueReports: true, // This is hardcoded to true in the OCR3 contract + + DeltaProgressMillis: millisecondsToUint32(publicConfig.DeltaProgress), + DeltaResendMillis: millisecondsToUint32(publicConfig.DeltaResend), + DeltaInitialMillis: millisecondsToUint32(publicConfig.DeltaInitial), + DeltaRoundMillis: millisecondsToUint32(publicConfig.DeltaRound), + DeltaGraceMillis: millisecondsToUint32(publicConfig.DeltaGrace), + DeltaCertifiedCommitRequestMillis: millisecondsToUint32(publicConfig.DeltaCertifiedCommitRequest), + DeltaStageMillis: millisecondsToUint32(publicConfig.DeltaStage), + MaxRoundsPerEpoch: publicConfig.RMax, + TransmissionSchedule: publicConfig.S, + + MaxDurationQueryMillis: millisecondsToUint32(publicConfig.MaxDurationQuery), + MaxDurationObservationMillis: millisecondsToUint32(publicConfig.MaxDurationObservation), + MaxDurationShouldAcceptMillis: millisecondsToUint32(publicConfig.MaxDurationShouldAcceptAttestedReport), + MaxDurationShouldTransmitMillis: millisecondsToUint32(publicConfig.MaxDurationShouldTransmitAcceptedReport), + + MaxFaultyOracles: publicConfig.F, + } + + return OCR3ConfigView{ + Signers: readableSigners, + Transmitters: transmitters, + F: config.F, + OnchainConfig: nil, // empty onChain config + OffchainConfigVersion: config.OffchainConfigVersion, + OffchainConfig: oracleConfig, + }, nil +} + +func millisecondsToUint32(dur time.Duration) uint32 { + ms := dur.Milliseconds() + if ms > int64(math.MaxUint32) { + return math.MaxUint32 + } + //nolint:gosec // disable G115 as it is practically impossible to overflow here + return uint32(ms) +} + +func NewKeystoneChainView() KeystoneChainView { + return KeystoneChainView{ + CapabilityRegistry: make(map[string]common_v1_0.CapabilityRegistryView), + OCRContracts: make(map[string]OCR3ConfigView), + WorkflowRegistry: make(map[string]common_v1_0.WorkflowRegistryView), + } +} + +type KeystoneView struct { + Chains map[string]KeystoneChainView `json:"chains,omitempty"` + Nops map[string]view.NopView `json:"nops,omitempty"` +} + +func (v KeystoneView) MarshalJSON() ([]byte, error) { + // Alias to avoid recursive calls + type Alias KeystoneView + return json.MarshalIndent(&struct{ Alias }{Alias: Alias(v)}, "", " ") +} diff --git a/deployment/keystone/changeset/view_test.go b/deployment/keystone/changeset/view_test.go index 5b32699fd89..aa21ba4619b 100644 --- a/deployment/keystone/changeset/view_test.go +++ b/deployment/keystone/changeset/view_test.go @@ -1,40 +1,135 @@ -package changeset +package changeset_test import ( + "bytes" + "encoding/json" "testing" + "time" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-common/pkg/logger" + chain_selectors "github.com/smartcontractkit/chain-selectors" - "github.com/smartcontractkit/chainlink/deployment/environment/memory" + "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" + "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/internal" + "github.com/smartcontractkit/chainlink/deployment/keystone/changeset/test" ) +var oracleConfig = changeset.OracleConfig{ + DeltaProgressMillis: 30000, + DeltaResendMillis: 5000, + DeltaInitialMillis: 5000, + DeltaRoundMillis: 2000, + DeltaGraceMillis: 500, + DeltaCertifiedCommitRequestMillis: 1000, + DeltaStageMillis: 30000, + MaxRoundsPerEpoch: 10, + TransmissionSchedule: []int{}, + MaxDurationQueryMillis: 1000, + MaxDurationObservationMillis: 1000, + MaxDurationShouldAcceptMillis: 1000, + MaxDurationShouldTransmitMillis: 1000, + MaxFaultyOracles: 1, + MaxQueryLengthBytes: 1000000, + MaxObservationLengthBytes: 1000000, + MaxReportLengthBytes: 1000000, + MaxOutcomeLengthBytes: 1000000, + MaxReportCount: 20, + MaxBatchSize: 20, + OutcomePruningThreshold: 3600, + UniqueReports: true, + RequestTimeout: 30 * time.Second, +} + func TestKeystoneView(t *testing.T) { t.Parallel() - env := memory.NewMemoryEnvironment(t, logger.Test(t), zapcore.DebugLevel, memory.MemoryEnvironmentConfig{ - Nodes: 1, - Chains: 2, + env := test.SetupContractTestEnv(t, test.EnvWrapperConfig{ + WFDonConfig: test.DonConfig{N: 4, Name: "wfDon"}, + AssetDonConfig: test.DonConfig{N: 4, Name: "assetDon"}, + WriterDonConfig: test.DonConfig{N: 4, Name: "writerDon"}, + NumChains: 1, }) - registryChain := env.AllChainSelectors()[0] - resp, err := DeployCapabilityRegistry(env, registryChain) - require.NoError(t, err) - require.NotNil(t, resp) - require.NoError(t, env.ExistingAddresses.Merge(resp.AddressBook)) - resp, err = DeployOCR3(env, registryChain) - require.NoError(t, err) - require.NotNil(t, resp) - require.NoError(t, env.ExistingAddresses.Merge(resp.AddressBook)) - resp, err = DeployForwarder(env, DeployForwarderRequest{}) - require.NoError(t, err) - require.NotNil(t, resp) - require.NoError(t, env.ExistingAddresses.Merge(resp.AddressBook)) + registryChain := env.Env.AllChainSelectors()[0] + oracleConfig.TransmissionSchedule = []int{len(env.Env.NodeIDs)} - a, err := ViewKeystone(env) + addrs, err := env.Env.ExistingAddresses.AddressesForChain(registryChain) require.NoError(t, err) - b, err := a.MarshalJSON() - require.NoError(t, err) - require.NotEmpty(t, b) - t.Log(string(b)) + + var newOCR3Addr string + for addr, tv := range addrs { + if tv.Type == internal.OCR3Capability { + newOCR3Addr = addr + break + } + } + + t.Run("successfully generates a view of the keystone state", func(t *testing.T) { + oracleConfigCopy := oracleConfig + + w := &bytes.Buffer{} + na := common.HexToAddress(newOCR3Addr) + cfg := changeset.ConfigureOCR3Config{ + ChainSel: env.RegistrySelector, + NodeIDs: env.Env.NodeIDs, + Address: &na, + OCR3Config: &oracleConfigCopy, + WriteGeneratedConfig: w, + } + _, err = changeset.ConfigureOCR3Contract(env.Env, cfg) + require.NoError(t, err) + + a, err := changeset.ViewKeystone(env.Env) + require.NoError(t, err) + b, err := a.MarshalJSON() + require.NoError(t, err) + require.NotEmpty(t, b) + + var outView changeset.KeystoneView + require.NoError(t, json.Unmarshal(b, &outView)) + + chainID, err := chain_selectors.ChainIdFromSelector(registryChain) + require.NoError(t, err) + chainName, err := chain_selectors.NameFromChainId(chainID) + require.NoError(t, err) + + viewChain, ok := outView.Chains[chainName] + require.True(t, ok) + viewOCR3Config, ok := viewChain.OCRContracts[newOCR3Addr] + require.True(t, ok) + require.Equal(t, oracleConfig, viewOCR3Config.OffchainConfig) + }) + + t.Run("fails to generate a view of the keystone state with OCR3 not configured", func(t *testing.T) { + // Deploy a new OCR3 contract + resp, err := changeset.DeployOCR3(env.Env, registryChain) + require.NoError(t, err) + require.NotNil(t, resp) + require.NoError(t, env.Env.ExistingAddresses.Merge(resp.AddressBook)) + + _, err = changeset.ViewKeystone(env.Env) + require.ErrorContains(t, err, "failed to view chain") + require.ErrorContains(t, err, "OCR3 not configured") + }) + + t.Run("fails to generate a view of the keystone state with a bad OracleConfig", func(t *testing.T) { + oracleConfigCopy := oracleConfig + oracleConfigCopy.DeltaRoundMillis = 0 + oracleConfigCopy.DeltaProgressMillis = 0 + + w := &bytes.Buffer{} + na := common.HexToAddress(newOCR3Addr) + cfg := changeset.ConfigureOCR3Config{ + ChainSel: env.RegistrySelector, + NodeIDs: env.Env.NodeIDs, + Address: &na, + OCR3Config: &oracleConfigCopy, + WriteGeneratedConfig: w, + } + _, err = changeset.ConfigureOCR3Contract(env.Env, cfg) + require.NoError(t, err) + _, err = changeset.ViewKeystone(env.Env) + require.ErrorContains(t, err, "failed to view chain") + require.ErrorContains(t, err, "DeltaRound (0s) must be less than DeltaProgress (0s)") + }) } diff --git a/deployment/keystone/view/view.go b/deployment/keystone/view/view.go deleted file mode 100644 index c74b4628d1e..00000000000 --- a/deployment/keystone/view/view.go +++ /dev/null @@ -1,32 +0,0 @@ -package view - -import ( - "encoding/json" - - "github.com/smartcontractkit/chainlink/deployment/common/view" - common_v1_0 "github.com/smartcontractkit/chainlink/deployment/common/view/v1_0" -) - -type KeystoneChainView struct { - CapabilityRegistry map[string]common_v1_0.CapabilityRegistryView `json:"capabilityRegistry,omitempty"` - WorkflowRegistry map[string]common_v1_0.WorkflowRegistryView `json:"workflowRegistry,omitempty"` - // TODO forwarders etc -} - -func NewKeystoneChainView() KeystoneChainView { - return KeystoneChainView{ - CapabilityRegistry: make(map[string]common_v1_0.CapabilityRegistryView), - WorkflowRegistry: make(map[string]common_v1_0.WorkflowRegistryView), - } -} - -type KeystoneView struct { - Chains map[string]KeystoneChainView `json:"chains,omitempty"` - Nops map[string]view.NopView `json:"nops,omitempty"` -} - -func (v KeystoneView) MarshalJSON() ([]byte, error) { - // Alias to avoid recursive calls - type Alias KeystoneView - return json.MarshalIndent(&struct{ Alias }{Alias: Alias(v)}, "", " ") -} diff --git a/integration-tests/go.mod b/integration-tests/go.mod index d6c4f6b6b93..47986b51869 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -12,8 +12,8 @@ replace github.com/smartcontractkit/chainlink/deployment => ../deployment // Using a separate `require` here to avoid surrounding line changes // creating potential merge conflicts. require ( - github.com/smartcontractkit/chainlink/deployment v0.0.0-20250128231431-9279badae2f0 - github.com/smartcontractkit/chainlink/v2 v2.0.0-20250128231431-9279badae2f0 + github.com/smartcontractkit/chainlink/deployment v0.0.0-20250221182743-098d1b0a763a + github.com/smartcontractkit/chainlink/v2 v2.0.0-20250221182743-098d1b0a763a ) require ( @@ -47,7 +47,7 @@ require ( github.com/smartcontractkit/chain-selectors v1.0.40 github.com/smartcontractkit/chainlink-automation v0.8.1 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb - github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea + github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 github.com/smartcontractkit/chainlink-integrations/evm v0.0.0-20250213145514-41d874782c02 github.com/smartcontractkit/chainlink-protos/job-distributor v0.9.0 github.com/smartcontractkit/chainlink-testing-framework/havoc v1.50.2 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f59223f1a01..a8bf643a088 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1388,8 +1388,8 @@ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea/go.mod h1:Z2e1ynSJ4pg83b4Qldbmryc5lmnrI3ojOdg1FUloa68= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 h1:CvDfgWoLoYPapOumE/UZCplfCu5oNmy9BuH+6V6+fJ8= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5/go.mod h1:pDZagSGjs9U+l4YIFhveDznMHqxuuz+5vRxvVgpbdr8= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index ca9802216f3..e699c548a0a 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -14,9 +14,9 @@ replace github.com/smartcontractkit/chainlink/integration-tests => ../ // Using a separate `require` here to avoid surrounding line changes // creating potential merge conflicts. require ( - github.com/smartcontractkit/chainlink/deployment v0.0.0-20250128231431-9279badae2f0 - github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20250128231431-9279badae2f0 - github.com/smartcontractkit/chainlink/v2 v2.0.0-20250128231431-9279badae2f0 + github.com/smartcontractkit/chainlink/deployment v0.0.0-20250221182743-098d1b0a763a + github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20250221184958-34c2d51a64bc + github.com/smartcontractkit/chainlink/v2 v2.0.0-20250221182743-098d1b0a763a ) require ( @@ -29,7 +29,7 @@ require ( github.com/slack-go/slack v0.15.0 github.com/smartcontractkit/chain-selectors v1.0.40 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb - github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea + github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 github.com/smartcontractkit/chainlink-integrations/evm v0.0.0-20250213145514-41d874782c02 github.com/smartcontractkit/chainlink-testing-framework/lib v1.51.0 github.com/smartcontractkit/chainlink-testing-framework/seth v1.50.10 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index fa62425009d..04569532d2f 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1373,8 +1373,8 @@ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea/go.mod h1:Z2e1ynSJ4pg83b4Qldbmryc5lmnrI3ojOdg1FUloa68= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 h1:CvDfgWoLoYPapOumE/UZCplfCu5oNmy9BuH+6V6+fJ8= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5/go.mod h1:pDZagSGjs9U+l4YIFhveDznMHqxuuz+5vRxvVgpbdr8= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= diff --git a/system-tests/lib/cre/contracts/contracts.go b/system-tests/lib/cre/contracts/contracts.go index 84c63d02fe7..c86eaf79034 100644 --- a/system-tests/lib/cre/contracts/contracts.go +++ b/system-tests/lib/cre/contracts/contracts.go @@ -11,6 +11,7 @@ import ( "github.com/rs/zerolog" capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" + "github.com/smartcontractkit/chainlink/deployment" keystone_changeset "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry_1_1_0" @@ -140,13 +141,13 @@ func ConfigureKeystone(input types.ConfigureKeystoneInput) error { TransmissionSchedule: transmissionSchedule, MaxDurationQueryMillis: 1000, MaxDurationObservationMillis: 1000, - MaxDurationAcceptMillis: 1000, - MaxDurationTransmitMillis: 1000, + MaxDurationShouldAcceptMillis: 1000, + MaxDurationShouldTransmitMillis: 1000, MaxFaultyOracles: 1, MaxQueryLengthBytes: 1000000, MaxObservationLengthBytes: 1000000, MaxReportLengthBytes: 1000000, - MaxRequestBatchSize: 1000, + MaxBatchSize: 1000, UniqueReports: true, } diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index b1453a2d2e2..8868a5e7f6e 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -3,8 +3,8 @@ module github.com/smartcontractkit/chainlink/system-tests/lib go 1.23.3 require ( - github.com/smartcontractkit/chainlink/deployment v0.0.0-20250206093113-4e72c05bdba6 - github.com/smartcontractkit/chainlink/v2 v2.19.0-ccip1.5.16-alpha.0.0.20250206093113-4e72c05bdba6 + github.com/smartcontractkit/chainlink/deployment v0.0.0-20250221182743-098d1b0a763a + github.com/smartcontractkit/chainlink/v2 v2.0.0-20250221182743-098d1b0a763a ) // Make sure we're working with the latest chainlink libs @@ -17,7 +17,7 @@ require ( github.com/google/uuid v1.6.0 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.33.0 - github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea + github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 github.com/smartcontractkit/chainlink-protos/job-distributor v0.9.0 github.com/smartcontractkit/chainlink-testing-framework/framework v0.5.4 github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.23 diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index f9f9026508d..a52fa0c717d 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1126,8 +1126,8 @@ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea/go.mod h1:Z2e1ynSJ4pg83b4Qldbmryc5lmnrI3ojOdg1FUloa68= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 h1:CvDfgWoLoYPapOumE/UZCplfCu5oNmy9BuH+6V6+fJ8= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5/go.mod h1:pDZagSGjs9U+l4YIFhveDznMHqxuuz+5vRxvVgpbdr8= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index 42dd042a2df..c46809f5f61 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -5,8 +5,8 @@ go 1.23.3 // Using a separate `require` here to avoid surrounding line changes // creating potential merge conflicts. require ( - github.com/smartcontractkit/chainlink/deployment v0.0.0-20250206093113-4e72c05bdba6 - github.com/smartcontractkit/chainlink/v2 v2.19.0-ccip1.5.16-alpha.0.0.20250206093113-4e72c05bdba6 + github.com/smartcontractkit/chainlink/deployment v0.0.0-20250221182743-098d1b0a763a + github.com/smartcontractkit/chainlink/v2 v2.0.0-20250221182743-098d1b0a763a ) // Make sure we're working with the latest chainlink libs @@ -343,7 +343,7 @@ require ( github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect - github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea // indirect + github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a // indirect diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index 720962bbaa1..57a42423877 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1126,8 +1126,8 @@ github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1 github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= -github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea/go.mod h1:Z2e1ynSJ4pg83b4Qldbmryc5lmnrI3ojOdg1FUloa68= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= +github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0/go.mod h1:YQuXIqQpmpAqstWV0LHaDTJ5nsSWuip5ivEM+Fisb+4= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 h1:CvDfgWoLoYPapOumE/UZCplfCu5oNmy9BuH+6V6+fJ8= github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5/go.mod h1:pDZagSGjs9U+l4YIFhveDznMHqxuuz+5vRxvVgpbdr8= github.com/smartcontractkit/chainlink-feeds v0.1.1 h1:JzvUOM/OgGQA1sOqTXXl52R6AnNt+Wg64sVG+XSA49c= From ff8c4ec12374194d5d3baa725d2d47c7894d1137 Mon Sep 17 00:00:00 2001 From: Suryansh <39276431+0xsuryansh@users.noreply.github.com> Date: Mon, 24 Feb 2025 22:53:29 +0530 Subject: [PATCH 07/14] CCIP-4234 CCIP Chain Reader E2E Tests DiscoverContract & GetContractAddress (#16515) * feat: E2E test CCIP chain reader GetContractAddress * feat: E2E test Discover contract, interplay with Sync * remove printing contract address * minor style fix * fix: pass address param codec in DiscoverContract test * fix lint * fix comment * remove unecessary comment * update DiscoverContract test to assert with require.Eventually * replace with tests.WaitTimeout(t) --- .../smoke/ccip/ccip_reader_test.go | 254 +++++++++++++++++- 1 file changed, 252 insertions(+), 2 deletions(-) diff --git a/integration-tests/smoke/ccip/ccip_reader_test.go b/integration-tests/smoke/ccip/ccip_reader_test.go index a45d47f58ba..2023ca184fb 100644 --- a/integration-tests/smoke/ccip/ccip_reader_test.go +++ b/integration-tests/smoke/ccip/ccip_reader_test.go @@ -1,6 +1,7 @@ package ccip import ( + "bytes" "context" "fmt" "math/big" @@ -731,7 +732,6 @@ func TestCCIPReader_NextSeqNum(t *testing.T) { func TestCCIPReader_GetExpectedNextSequenceNumber(t *testing.T) { t.Parallel() ctx := tests.Context(t) - //env := NewMemoryEnvironmentContractsOnly(t, logger.TestLogger(t), 2, 4, nil) env, _ := testhelpers.NewMemoryEnvironment(t) state, err := changeset.LoadOnchainState(env.Env) require.NoError(t, err) @@ -839,6 +839,257 @@ func TestCCIPReader_Nonces(t *testing.T) { } } +func TestCCIPReader_GetContractAddress(t *testing.T) { + t.Parallel() + ctx := tests.Context(t) + sb, auth := setupSimulatedBackendAndAuth(t) + + s := testSetup(ctx, t, testSetupParams{ + ReaderChain: chainS1, + DestChain: chainD, + OnChainSeqNums: nil, + Cfg: evmconfig.DestReaderConfig, + BindTester: true, + ContractNameToBind: consts.ContractNameOffRamp, + SimulatedBackend: sb, + Auth: auth, + UseHeavyDB: false, + }) + + t.Run("success - single bound address", func(t *testing.T) { + myContractName := consts.ContractNameOffRamp + myAddress := s.contractAddr + + err := s.extendedCR.Bind(ctx, []types.BoundContract{ + { + Address: myAddress.String(), + Name: myContractName, + }, + }) + require.NoError(t, err) + + gotBytes, err := s.reader.GetContractAddress(myContractName, chainS1) + require.NoError(t, err) + + require.Equal(t, myAddress.Bytes(), gotBytes, "expected the bound contract address to match") + }) + + t.Run("error - no bindings found", func(t *testing.T) { + _, err := s.reader.GetContractAddress("UnboundContract", chainS1) + require.Error(t, err) + require.Contains(t, err.Error(), "expected one binding for the UnboundContract contract, got 0") + }) + + t.Run("success - multiple bindings, return override binding", func(t *testing.T) { + myContractName := consts.ContractNameOffRamp + addr1 := s.contractAddr + addr2, _, _, err := ccip_reader_tester.DeployCCIPReaderTester(auth, sb.Client()) + require.NoError(t, err) + sb.Commit() + + err = s.extendedCR.Bind(ctx, []types.BoundContract{ + { + Address: addr1.String(), + Name: myContractName, + }, + { + Address: addr2.String(), + Name: myContractName, + }, + }) + require.NoError(t, err) + + gotBytes, err := s.reader.GetContractAddress(myContractName, chainS1) + require.NoError(t, err) + + require.Equal(t, addr2.Bytes(), gotBytes, "expected the bound contract override address to match") + }) + + t.Run("error - chain not supported", func(t *testing.T) { + // Suppose chainS2 is not set up in this test environment (no contract reader). + // The call should fail with "contract reader not found for chain". + _, err := s.reader.GetContractAddress("TestContract", chainS2) + require.Error(t, err) + require.Contains(t, err.Error(), "contract reader not found for chain 2") + }) +} + +func TestCCIPReader_DiscoverContracts(t *testing.T) { + t.Parallel() + ctx := tests.Context(t) + sb, auth := setupSimulatedBackendAndAuth(t) + + //--------------------------------Setup--------------------------------// + onRampS1StaticConfig := onramp.OnRampStaticConfig{ + ChainSelector: uint64(chainS1), + RmnRemote: utils.RandomAddress(), + NonceManager: utils.RandomAddress(), + TokenAdminRegistry: utils.RandomAddress(), + } + + onRampS1DynamicConfig := onramp.OnRampDynamicConfig{ + FeeQuoter: utils.RandomAddress(), + ReentrancyGuardEntered: false, + MessageInterceptor: utils.ZeroAddress, + FeeAggregator: utils.RandomAddress(), + AllowlistAdmin: utils.RandomAddress(), + } + + destinationChainConfigArgs := []onramp.OnRampDestChainConfigArgs{ + { + DestChainSelector: uint64(chainD), + Router: utils.RandomAddress(), + AllowlistEnabled: false, + }, + } + onRampS1Addr, _, _, err := onramp.DeployOnRamp(auth, sb.Client(), onRampS1StaticConfig, onRampS1DynamicConfig, destinationChainConfigArgs) + require.NoError(t, err) + sb.Commit() + + offRampDStaticConfig := offramp.OffRampStaticConfig{ + ChainSelector: uint64(chainD), + GasForCallExactCheck: 0, + RmnRemote: utils.RandomAddress(), + TokenAdminRegistry: utils.RandomAddress(), + NonceManager: utils.RandomAddress(), + } + + offRampDDynamicConfig := offramp.OffRampDynamicConfig{ + FeeQuoter: utils.RandomAddress(), + PermissionLessExecutionThresholdSeconds: 1, + MessageInterceptor: utils.ZeroAddress, + } + + offRampDSourceChainConfigArgs := []offramp.OffRampSourceChainConfigArgs{ + { + Router: destinationChainConfigArgs[0].Router, + SourceChainSelector: onRampS1StaticConfig.ChainSelector, + IsEnabled: true, + IsRMNVerificationDisabled: true, + OnRamp: common.LeftPadBytes(onRampS1Addr.Bytes(), 32), + }, + } + offRampDestAddr, _, _, err := offramp.DeployOffRamp(auth, sb.Client(), offRampDStaticConfig, offRampDDynamicConfig, offRampDSourceChainConfigArgs) + require.NoError(t, err) + sb.Commit() + + clS1 := client.NewSimulatedBackendClient(t, sb, big.NewInt(0).SetUint64(uint64(chainS1))) + headTrackerS1 := headstest.NewSimulatedHeadTracker(clS1, true, 1) + ormS1 := logpoller.NewORM(big.NewInt(0).SetUint64(uint64(chainS1)), pgtest.NewSqlxDB(t), logger.TestLogger(t)) + lpOpts := logpoller.Opts{ + PollPeriod: time.Millisecond, + FinalityDepth: 0, + BackfillBatchSize: 10, + RPCBatchSize: 10, + KeepFinalizedBlocksDepth: 100000, + } + lpS1 := logpoller.NewLogPoller( + ormS1, + clS1, + logger.TestLogger(t), + headTrackerS1, + lpOpts, + ) + require.NoError(t, lpS1.Start(ctx)) + + clD := client.NewSimulatedBackendClient(t, sb, big.NewInt(0).SetUint64(uint64(chainD))) + headTrackerD := headstest.NewSimulatedHeadTracker(clD, true, 1) + ormD := logpoller.NewORM(big.NewInt(0).SetUint64(uint64(chainD)), pgtest.NewSqlxDB(t), logger.TestLogger(t)) + lpD := logpoller.NewLogPoller( + ormD, + clD, + logger.TestLogger(t), + headTrackerD, + lpOpts, + ) + require.NoError(t, lpD.Start(ctx)) + + crS1, err := evm.NewChainReaderService(ctx, logger.TestLogger(t), lpS1, headTrackerS1, clS1, evmconfig.SourceReaderConfig) + require.NoError(t, err) + extendedCrS1 := contractreader.NewExtendedContractReader(crS1) + + crD, err := evm.NewChainReaderService(ctx, logger.TestLogger(t), lpD, headTrackerD, clD, evmconfig.DestReaderConfig) + require.NoError(t, err) + extendedCrD := contractreader.NewExtendedContractReader(crD) + err = extendedCrD.Bind(ctx, []types.BoundContract{ + { + Address: offRampDestAddr.String(), + Name: consts.ContractNameOffRamp, + }, + }) + require.NoError(t, err) + + err = crS1.Start(ctx) + require.NoError(t, err) + err = crD.Start(ctx) + require.NoError(t, err) + + contractReaders := map[cciptypes.ChainSelector]contractreader.Extended{} + contractReaders[chainS1] = extendedCrS1 + contractReaders[chainD] = extendedCrD + + contractWriters := make(map[cciptypes.ChainSelector]types.ContractWriter) + + addrCodec := ccipcommon.NewAddressCodec(ccipcommon.NewAddressCodecParams(ccipevm.AddressCodec{}, ccipsolana.AddressCodec{})) + reader := ccipreaderpkg.NewCCIPReaderWithExtendedContractReaders(ctx, logger.TestLogger(t), contractReaders, contractWriters, chainD, offRampDestAddr.Bytes(), addrCodec) + + t.Cleanup(func() { + assert.NoError(t, crS1.Close()) + assert.NoError(t, lpS1.Close()) + assert.NoError(t, crD.Close()) + assert.NoError(t, lpD.Close()) + }) + //--------------------------------Setup done--------------------------------// + + // Call the ccip chain reader with DiscoverContracts for test + contractAddresses, err := reader.DiscoverContracts(ctx, []cciptypes.ChainSelector{chainS1, chainD}) + require.NoError(t, err) + + require.Equal(t, contractAddresses[consts.ContractNameOnRamp][chainS1], cciptypes.UnknownAddress(common.LeftPadBytes(onRampS1Addr.Bytes(), 32))) + require.Equal(t, contractAddresses[consts.ContractNameRouter][chainD], cciptypes.UnknownAddress(destinationChainConfigArgs[0].Router.Bytes())) + require.Equal(t, contractAddresses[consts.ContractNameRMNRemote][chainD], cciptypes.UnknownAddress(offRampDStaticConfig.RmnRemote.Bytes())) + require.Equal(t, contractAddresses[consts.ContractNameNonceManager][chainD], cciptypes.UnknownAddress(offRampDStaticConfig.NonceManager.Bytes())) + require.Equal(t, contractAddresses[consts.ContractNameFeeQuoter][chainD], cciptypes.UnknownAddress(offRampDDynamicConfig.FeeQuoter.Bytes())) + + // Now Sync the CCIP Reader's S1 chain contract reader with OnRamp binding + onRampContractMapping := make(ccipreaderpkg.ContractAddresses) + onRampContractMapping[consts.ContractNameOnRamp] = make(map[cciptypes.ChainSelector]cciptypes.UnknownAddress) + onRampContractMapping[consts.ContractNameOnRamp][chainS1] = onRampS1Addr.Bytes() + + err = reader.Sync(ctx, onRampContractMapping) + require.NoError(t, err) + + // Since config poller has default refresh interval of 30s, we need to wait for the contract to be discovered + require.Eventually(t, func() bool { + contractAddresses, err = reader.DiscoverContracts(ctx, []cciptypes.ChainSelector{chainS1, chainD}) + if err != nil { + return false + } + + // Check if router and FeeQuoter addresses on source chain are now discovered + routerS1, routerExists := contractAddresses[consts.ContractNameRouter][chainS1] + feeQuoterS1, feeQuoterExists := contractAddresses[consts.ContractNameFeeQuoter][chainS1] + + return routerExists && feeQuoterExists && + bytes.Equal(routerS1, destinationChainConfigArgs[0].Router.Bytes()) && + bytes.Equal(feeQuoterS1, onRampS1DynamicConfig.FeeQuoter.Bytes()) + }, tests.WaitTimeout(t), 100*time.Millisecond, "Router and FeeQuoter addresses were not discovered on source chain in time") + + // Final assertions again for completeness: + contractAddresses, err = reader.DiscoverContracts(ctx, []cciptypes.ChainSelector{chainS1, chainD}) + require.NoError(t, err) + + require.Equal(t, contractAddresses[consts.ContractNameOnRamp][chainS1], cciptypes.UnknownAddress(common.LeftPadBytes(onRampS1Addr.Bytes(), 32))) + require.Equal(t, contractAddresses[consts.ContractNameRouter][chainD], cciptypes.UnknownAddress(destinationChainConfigArgs[0].Router.Bytes())) + require.Equal(t, contractAddresses[consts.ContractNameRMNRemote][chainD], cciptypes.UnknownAddress(offRampDStaticConfig.RmnRemote.Bytes())) + require.Equal(t, contractAddresses[consts.ContractNameNonceManager][chainD], cciptypes.UnknownAddress(offRampDStaticConfig.NonceManager.Bytes())) + require.Equal(t, contractAddresses[consts.ContractNameFeeQuoter][chainD], cciptypes.UnknownAddress(offRampDDynamicConfig.FeeQuoter.Bytes())) + + // Final assert to confirm source chain addresses discovered + require.Equal(t, contractAddresses[consts.ContractNameRouter][chainS1], cciptypes.UnknownAddress(destinationChainConfigArgs[0].Router.Bytes())) + require.Equal(t, contractAddresses[consts.ContractNameFeeQuoter][chainS1], cciptypes.UnknownAddress(onRampS1DynamicConfig.FeeQuoter.Bytes())) +} + func Test_GetChainFeePriceUpdates(t *testing.T) { t.Parallel() ctx := tests.Context(t) @@ -876,7 +1127,6 @@ func Test_GetChainFeePriceUpdates(t *testing.T) { ctx, t, chain1, - //evmconfig.DestReaderConfig, map[cciptypes.ChainSelector][]types.BoundContract{ cciptypes.ChainSelector(chain1): { { From 456f822c74484bcaeef3e7e0c02491c4a7a4e3ba Mon Sep 17 00:00:00 2001 From: Pablo Estrada <139084212+ecPablo@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:53:03 -0600 Subject: [PATCH 08/14] feat: add example changeset to transfer from timelock signer account (#16476) * feat: add changeset to transfer from timelock signer account * fix: linting errors * fix: code cleanup * fix: use NewTransactionFromInstruction * fix: cleanup accounts signer set --- .../changeset/example/solana_transfer_mcm.go | 136 ++++++++++ .../example/solana_transfer_mcm_test.go | 242 ++++++++++++++++++ deployment/common/changeset/solana/helpers.go | 23 +- 3 files changed, 394 insertions(+), 7 deletions(-) create mode 100644 deployment/common/changeset/example/solana_transfer_mcm.go create mode 100644 deployment/common/changeset/example/solana_transfer_mcm_test.go diff --git a/deployment/common/changeset/example/solana_transfer_mcm.go b/deployment/common/changeset/example/solana_transfer_mcm.go new file mode 100644 index 00000000000..6fdc197fe73 --- /dev/null +++ b/deployment/common/changeset/example/solana_transfer_mcm.go @@ -0,0 +1,136 @@ +package example + +import ( + "errors" + "fmt" + "time" + + "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" + "github.com/smartcontractkit/mcms" + mcmssolanasdk "github.com/smartcontractkit/mcms/sdk/solana" + "github.com/smartcontractkit/mcms/types" + + "github.com/smartcontractkit/chainlink/deployment" + solanachangeset "github.com/smartcontractkit/chainlink/deployment/common/changeset/solana" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" +) + +var _ deployment.ChangeSetV2[TransferFromTimelockConfig] = TransferFromTimelock{} + +type TransferData struct { + To solana.PublicKey + Amount uint64 +} +type TransferFromTimelockConfig struct { + TimelockDelay time.Duration + AmountsPerChain map[uint64]TransferData +} + +// TransferFromTimelock is a changeset that transfer funds from the timelock signer PDA +// to the address provided in the config. It will return an mcms proposal to sign containing +// the funds transfer transaction. +type TransferFromTimelock struct{} + +// VerifyPreconditions checks if the deployer has enough SOL to fund the MCMS signers on each chain. +func (f TransferFromTimelock) VerifyPreconditions(e deployment.Environment, config TransferFromTimelockConfig) error { + // the number of accounts to fund per chain (bypasser, canceller, proposer, timelock) + for chainSelector, amountCfg := range config.AmountsPerChain { + solChain, ok := e.SolChains[chainSelector] + if !ok { + return fmt.Errorf("solana chain not found for selector %d", chainSelector) + } + if amountCfg.To.IsZero() { + return errors.New("destination address is empty") + } + addresses, err := e.ExistingAddresses.AddressesForChain(chainSelector) + if err != nil { + return fmt.Errorf("failed to get existing addresses: %w", err) + } + mcmState, err := state.MaybeLoadMCMSWithTimelockChainStateSolana(solChain, addresses) + if err != nil { + return fmt.Errorf("failed to load MCMS state: %w", err) + } + // Check if seeds are empty + if mcmState.TimelockSeed == [32]byte{} { + return errors.New("timelock seeds are empty, please deploy MCMS contracts first") + } + // Check if program IDs exists + if mcmState.TimelockProgram.IsZero() { + return errors.New("timelock program IDs are empty, please deploy timelock program first") + } + result, err := solChain.Client.GetBalance(e.GetContext(), solChain.DeployerKey.PublicKey(), rpc.CommitmentConfirmed) + if err != nil { + return fmt.Errorf("failed to get deployer balance: %w", err) + } + if result.Value < amountCfg.Amount { + return fmt.Errorf("deployer balance is insufficient, required: %d, actual: %d", amountCfg.Amount, result.Value) + } + } + return nil +} + +// Apply funds the MCMS signers on each chain. +func (f TransferFromTimelock) Apply(e deployment.Environment, config TransferFromTimelockConfig) (deployment.ChangesetOutput, error) { + timelocks := map[uint64]string{} + proposers := map[uint64]string{} + var batches []types.BatchOperation + inspectors, err := proposalutils.McmsInspectors(e) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to get MCMS inspectors: %w", err) + } + for chainSelector, cfgAmounts := range config.AmountsPerChain { + solChain := e.SolChains[chainSelector] + addreses, err := e.ExistingAddresses.AddressesForChain(chainSelector) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to get existing addresses: %w", err) + } + mcmState, err := state.MaybeLoadMCMSWithTimelockChainStateSolana(solChain, addreses) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to load MCMS state: %w", err) + } + timelockSignerPDA := state.GetTimelockSignerPDA(mcmState.TimelockProgram, mcmState.TimelockSeed) + timelockID := mcmssolanasdk.ContractAddress(mcmState.TimelockProgram, mcmssolanasdk.PDASeed(mcmState.TimelockSeed)) + proposerID := mcmssolanasdk.ContractAddress(mcmState.McmProgram, mcmssolanasdk.PDASeed(mcmState.ProposerMcmSeed)) + timelocks[chainSelector] = timelockID + proposers[chainSelector] = proposerID + ixs, err := solanachangeset.FundFromAddressIxs( + solChain, + timelockSignerPDA, + []solana.PublicKey{cfgAmounts.To}, + cfgAmounts.Amount) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to fund timelock signer on chain %d: %w", chainSelector, err) + } + + var transactions []types.Transaction + + for _, ix := range ixs { + solanaTx, err := mcmssolanasdk.NewTransactionFromInstruction(ix, "SystemProgram", []string{}) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to create transaction: %w", err) + } + transactions = append(transactions, solanaTx) + } + batches = append(batches, types.BatchOperation{ + ChainSelector: types.ChainSelector(chainSelector), + Transactions: transactions, + }) + } + proposal, err := proposalutils.BuildProposalFromBatchesV2( + e.GetContext(), + timelocks, + proposers, + inspectors, + batches, + "transfer funds from timelock singer", + config.TimelockDelay, + ) + if err != nil { + return deployment.ChangesetOutput{}, fmt.Errorf("failed to build proposal: %w", err) + } + return deployment.ChangesetOutput{ + MCMSTimelockProposals: []mcms.TimelockProposal{*proposal}, + }, nil +} diff --git a/deployment/common/changeset/example/solana_transfer_mcm_test.go b/deployment/common/changeset/example/solana_transfer_mcm_test.go new file mode 100644 index 00000000000..28a0cbc4a58 --- /dev/null +++ b/deployment/common/changeset/example/solana_transfer_mcm_test.go @@ -0,0 +1,242 @@ +package example_test + +import ( + "fmt" + "testing" + "time" + + "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" + chainselectors "github.com/smartcontractkit/chain-selectors" + mcmsSolana "github.com/smartcontractkit/mcms/sdk/solana" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" + "github.com/smartcontractkit/chainlink/deployment/common/changeset" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/example" + "github.com/smartcontractkit/chainlink/deployment/common/changeset/state" + "github.com/smartcontractkit/chainlink/deployment/common/proposalutils" + "github.com/smartcontractkit/chainlink/deployment/common/types" + "github.com/smartcontractkit/chainlink/deployment/environment/memory" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +// setupFundingTestEnv deploys all required contracts for the funding test +func setupFundingTestEnv(t *testing.T) deployment.Environment { + lggr := logger.TestLogger(t) + cfg := memory.MemoryEnvironmentConfig{ + SolChains: 1, + } + env := memory.NewMemoryEnvironment(t, lggr, zapcore.DebugLevel, cfg) + chainSelector := env.AllChainSelectorsSolana()[0] + + config := proposalutils.SingleGroupTimelockConfigV2(t) + testhelpers.SavePreloadedSolAddresses(t, env, chainSelector) + // Initialize the address book with a dummy address to avoid deploy precondition errors. + err := env.ExistingAddresses.Save(chainSelector, "dummyAddress", deployment.TypeAndVersion{Type: "dummy", Version: deployment.Version1_0_0}) + require.NoError(t, err) + + // Deploy MCMS and Timelock + env, err = changeset.Apply(t, env, nil, + changeset.Configure( + deployment.CreateLegacyChangeSet(changeset.DeployMCMSWithTimelockV2), + map[uint64]types.MCMSWithTimelockConfigV2{ + chainSelector: config, + }, + ), + ) + require.NoError(t, err) + + return env +} + +func TestTransferFromTimelockConfig_VerifyPreconditions(t *testing.T) { + lggr := logger.TestLogger(t) + validEnv := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{SolChains: 1}) + validEnv.SolChains[chainselectors.SOLANA_DEVNET.Selector] = deployment.SolChain{} + validSolChainSelector := validEnv.AllChainSelectorsSolana()[0] + receiverKey := solana.NewWallet().PublicKey() + cs := example.TransferFromTimelock{} + timelockID := mcmsSolana.ContractAddress( + solana.NewWallet().PublicKey(), + [32]byte{'t', 'e', 's', 't'}, + ) + err := validEnv.ExistingAddresses.Save(validSolChainSelector, timelockID, deployment.TypeAndVersion{ + Type: types.RBACTimelock, + Version: deployment.Version1_0_0, + }) + require.NoError(t, err) + + // Create an environment that simulates a chain where the MCMS contracts have not been deployed, + // e.g. missing the required addresses so that the state loader returns empty seeds. + noTimelockEnv := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + SolChains: 1, + }) + noTimelockEnv.SolChains[chainselectors.SOLANA_DEVNET.Selector] = deployment.SolChain{} + err = noTimelockEnv.ExistingAddresses.Save(chainselectors.SOLANA_DEVNET.Selector, "dummy", deployment.TypeAndVersion{ + Type: "Sometype", + Version: deployment.Version1_0_0, + }) + require.NoError(t, err) + + // Create an environment with a Solana chain that has an invalid (zero) underlying chain. + invalidSolChainEnv := memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + SolChains: 0, + }) + invalidSolChainEnv.SolChains[validSolChainSelector] = deployment.SolChain{} + + tests := []struct { + name string + env deployment.Environment + config example.TransferFromTimelockConfig + expectedError string + }{ + { + name: "All preconditions satisfied", + env: validEnv, + config: example.TransferFromTimelockConfig{ + AmountsPerChain: map[uint64]example.TransferData{validSolChainSelector: { + Amount: 100, + To: receiverKey, + }}, + }, + expectedError: "", + }, + { + name: "No Solana chains found in environment", + env: memory.NewMemoryEnvironment(t, lggr, zapcore.InfoLevel, memory.MemoryEnvironmentConfig{ + Bootstraps: 1, + Chains: 1, + SolChains: 0, + Nodes: 1, + }), + config: example.TransferFromTimelockConfig{ + AmountsPerChain: map[uint64]example.TransferData{validSolChainSelector: { + Amount: 100, + To: receiverKey, + }}, + }, + expectedError: fmt.Sprintf("solana chain not found for selector %d", validSolChainSelector), + }, + { + name: "Chain selector not found in environment", + env: validEnv, + config: example.TransferFromTimelockConfig{AmountsPerChain: map[uint64]example.TransferData{99999: { + Amount: 100, + To: receiverKey, + }}}, + expectedError: "solana chain not found for selector 99999", + }, + { + name: "timelock contracts not deployed (empty seeds)", + env: noTimelockEnv, + config: example.TransferFromTimelockConfig{ + AmountsPerChain: map[uint64]example.TransferData{chainselectors.SOLANA_DEVNET.Selector: { + Amount: 100, + To: receiverKey, + }}, + }, + expectedError: "timelock seeds are empty, please deploy MCMS contracts first", + }, + { + name: "Insufficient deployer balance", + env: validEnv, + config: example.TransferFromTimelockConfig{ + AmountsPerChain: map[uint64]example.TransferData{ + validSolChainSelector: { + Amount: 999999999999999999, + To: receiverKey, + }, + }, + }, + expectedError: "deployer balance is insufficient", + }, + { + name: "Insufficient deployer balance", + env: validEnv, + config: example.TransferFromTimelockConfig{ + AmountsPerChain: map[uint64]example.TransferData{ + validSolChainSelector: { + Amount: 999999999999999999, + To: receiverKey, + }, + }, + }, + expectedError: "deployer balance is insufficient", + }, + { + name: "Invalid Solana chain in environment", + env: invalidSolChainEnv, + config: example.TransferFromTimelockConfig{ + AmountsPerChain: map[uint64]example.TransferData{validSolChainSelector: { + Amount: 100, + To: receiverKey, + }}, + }, + expectedError: "failed to get existing addresses: chain selector 12463857294658392847: chain not found", + }, + { + name: "empty from field", + env: invalidSolChainEnv, + config: example.TransferFromTimelockConfig{ + AmountsPerChain: map[uint64]example.TransferData{validSolChainSelector: { + Amount: 100, + To: solana.PublicKey{}, + }}, + }, + expectedError: "destination address is empty", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := cs.VerifyPreconditions(tt.env, tt.config) + if tt.expectedError == "" { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedError) + } + }) + } +} + +func TestTransferFromTimelockConfig_Apply(t *testing.T) { + env := setupFundingTestEnv(t) + cfgAmounts := example.TransferData{ + Amount: 100 * solana.LAMPORTS_PER_SOL, + To: solana.NewWallet().PublicKey(), + } + amountsPerChain := make(map[uint64]example.TransferData) + for chainSelector := range env.SolChains { + amountsPerChain[chainSelector] = cfgAmounts + } + config := example.TransferFromTimelockConfig{ + TimelockDelay: 1 * time.Second, + AmountsPerChain: amountsPerChain, + } + addresses, err := env.ExistingAddresses.AddressesForChain(env.AllChainSelectorsSolana()[0]) + require.NoError(t, err) + mcmState, err := state.MaybeLoadMCMSWithTimelockChainStateSolana(env.SolChains[env.AllChainSelectorsSolana()[0]], addresses) + require.NoError(t, err) + timelockSigner := state.GetTimelockSignerPDA(mcmState.TimelockProgram, mcmState.TimelockSeed) + mcmSigner := state.GetMCMSignerPDA(mcmState.McmProgram, mcmState.ProposerMcmSeed) + chainSelector := env.AllChainSelectorsSolana()[0] + solChain := env.SolChains[chainSelector] + memory.FundSolanaAccounts(env.GetContext(), t, []solana.PublicKey{timelockSigner, mcmSigner, solChain.DeployerKey.PublicKey()}, 150, solChain.Client) + + changesetInstance := example.TransferFromTimelock{} + + env, err = changeset.ApplyChangesetsV2(t, env, []changeset.ConfiguredChangeSet{ + changeset.Configure(changesetInstance, config), + }) + require.NoError(t, err) + + balance, err := solChain.Client.GetBalance(env.GetContext(), cfgAmounts.To, rpc.CommitmentConfirmed) + require.NoError(t, err) + t.Logf("Account: %s, Balance: %d", cfgAmounts.To, balance.Value) + + require.Equal(t, cfgAmounts.Amount, balance.Value) +} diff --git a/deployment/common/changeset/solana/helpers.go b/deployment/common/changeset/solana/helpers.go index e6dc93c6590..e23ac65ac20 100644 --- a/deployment/common/changeset/solana/helpers.go +++ b/deployment/common/changeset/solana/helpers.go @@ -9,25 +9,34 @@ import ( "github.com/smartcontractkit/chainlink/deployment" ) -// FundFromDeployerKey transfers SOL from the deployer to each provided account and waits for confirmations. -func FundFromDeployerKey(solChain deployment.SolChain, accounts []solana.PublicKey, amount uint64) error { +// FundFromAddressIxs transfers SOL from the given address to each provided account and waits for confirmations. +func FundFromAddressIxs(solChain deployment.SolChain, from solana.PublicKey, accounts []solana.PublicKey, amount uint64) ([]solana.Instruction, error) { var ixs []solana.Instruction for _, account := range accounts { // Create a transfer instruction using the provided builder. ix, err := system.NewTransferInstruction( amount, - solChain.DeployerKey.PublicKey(), // funding account (sender) - account, // recipient account + from, // funding account (sender) + account, // recipient account ).ValidateAndBuild() if err != nil { - return fmt.Errorf("failed to create transfer instruction: %w", err) + return nil, fmt.Errorf("failed to create transfer instruction: %w", err) } ixs = append(ixs, ix) } - err := solChain.Confirm(ixs) + return ixs, nil +} + +// FundFromDeployerKey transfers SOL from the deployer to each provided account and waits for confirmations. +func FundFromDeployerKey(solChain deployment.SolChain, accounts []solana.PublicKey, amount uint64) error { + ixs, err := FundFromAddressIxs(solChain, solChain.DeployerKey.PublicKey(), accounts, amount) + if err != nil { + return fmt.Errorf("failed to create transfer instructions: %w", err) + } + err = solChain.Confirm(ixs) if err != nil { - return fmt.Errorf("failed to create transaction: %w", err) + return fmt.Errorf("failed to confirm transaction: %w", err) } return nil } From c44201af990aacc9aca7a2d2f64afe9bbdb7580f Mon Sep 17 00:00:00 2001 From: pavel-raykov <165708424+pavel-raykov@users.noreply.github.com> Date: Mon, 24 Feb 2025 20:10:49 +0100 Subject: [PATCH 09/14] Move Mailboxes.md to docs. (#16546) * Minor * Minor --- .changeset/pink-bears-tickle.md | 5 +++++ core/utils/Mailboxes.md => docs/core/DATA_FLOW.md | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 .changeset/pink-bears-tickle.md rename core/utils/Mailboxes.md => docs/core/DATA_FLOW.md (98%) diff --git a/.changeset/pink-bears-tickle.md b/.changeset/pink-bears-tickle.md new file mode 100644 index 00000000000..49d9f9fa1bb --- /dev/null +++ b/.changeset/pink-bears-tickle.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#updated Moved Mailboxes.md to docs. diff --git a/core/utils/Mailboxes.md b/docs/core/DATA_FLOW.md similarity index 98% rename from core/utils/Mailboxes.md rename to docs/core/DATA_FLOW.md index a6c6615a92b..a2a1a4df1da 100644 --- a/core/utils/Mailboxes.md +++ b/docs/core/DATA_FLOW.md @@ -1,6 +1,9 @@ # Mailboxes ```mermaid +--- +title: Description of pkg/utils/mailbox/mailbox.go from the chainlink-common repository. +--- flowchart subgraph Legend style Legend fill:none @@ -21,6 +24,9 @@ flowchart ``` ```mermaid +--- +title: Description of the general data flow. +--- flowchart TB subgraph core/chains/evm subgraph gas From 182b3916e84c0860f3692ed4ac39b2ad8c9d246b Mon Sep 17 00:00:00 2001 From: Makram Date: Mon, 24 Feb 2025 22:22:36 +0200 Subject: [PATCH 10/14] integration-tests/smoke/ccip: rm feeboosting test & cfgs (#16538) * integration-tests/smoke/ccip: rm feeboosting test * deployment/ccip: remove references to boosting * bump cl-ccip to main --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- deployment/ccip/changeset/globals/config.go | 1 - .../ccip/changeset/v1_6/cs_ccip_home.go | 1 - deployment/go.mod | 2 +- deployment/go.sum | 4 +- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- integration-tests/load/go.mod | 2 +- integration-tests/load/go.sum | 4 +- .../smoke/ccip/ccip_add_chain_test.go | 4 - .../smoke/ccip/ccip_fee_boosting_test.go | 287 ------------------ system-tests/lib/go.mod | 2 +- system-tests/lib/go.sum | 4 +- system-tests/tests/go.mod | 2 +- system-tests/tests/go.sum | 4 +- 18 files changed, 21 insertions(+), 314 deletions(-) delete mode 100644 integration-tests/smoke/ccip/ccip_fee_boosting_test.go diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 7fb73eed291..1db0b021428 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -340,7 +340,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.1.0 // indirect github.com/smartcontractkit/chain-selectors v1.0.40 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20250207205350-420ccacab78a // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index fbe0b40ff8c..b4e4f8abb1a 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1086,8 +1086,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 h1:XUz6jj+pPIvRbmlalXyFf6O0hGicU8thQtjFvw7dPLo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= diff --git a/deployment/ccip/changeset/globals/config.go b/deployment/ccip/changeset/globals/config.go index c93fdc2a97b..a0ca800f1d7 100644 --- a/deployment/ccip/changeset/globals/config.go +++ b/deployment/ccip/changeset/globals/config.go @@ -14,7 +14,6 @@ const ( RemoteGasPriceBatchWriteFrequency = 30 * time.Minute TokenPriceBatchWriteFrequency = 30 * time.Minute BatchGasLimit = 6_500_000 - RelativeBoostPerWaitHour = 0.5 InflightCacheExpiry = 10 * time.Minute RootSnoozeTime = 30 * time.Minute BatchingStrategyID = 0 diff --git a/deployment/ccip/changeset/v1_6/cs_ccip_home.go b/deployment/ccip/changeset/v1_6/cs_ccip_home.go index c93865e9b0d..01ce9f4224b 100644 --- a/deployment/ccip/changeset/v1_6/cs_ccip_home.go +++ b/deployment/ccip/changeset/v1_6/cs_ccip_home.go @@ -242,7 +242,6 @@ func WithDefaultExecuteOffChainConfig(tokenDataObservers []pluginconfig.TokenDat if params.ExecuteOffChainConfig == nil { params.ExecuteOffChainConfig = &pluginconfig.ExecuteOffchainConfig{ BatchGasLimit: globals.BatchGasLimit, - RelativeBoostPerWaitHour: globals.RelativeBoostPerWaitHour, InflightCacheExpiry: *config.MustNewDuration(globals.InflightCacheExpiry), RootSnoozeTime: *config.MustNewDuration(globals.RootSnoozeTime), MessageVisibilityInterval: *config.MustNewDuration(globals.PermissionLessExecutionThreshold), diff --git a/deployment/go.mod b/deployment/go.mod index f1a0f6fb3f3..2fa4e14ef63 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -30,7 +30,7 @@ require ( github.com/sethvargo/go-retry v0.2.4 github.com/smartcontractkit/ccip-owner-contracts v0.1.0 github.com/smartcontractkit/chain-selectors v1.0.40 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20250211162441-3d6cea220efb diff --git a/deployment/go.sum b/deployment/go.sum index df1bf1dc209..f5125d641fc 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -1134,8 +1134,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 h1:XUz6jj+pPIvRbmlalXyFf6O0hGicU8thQtjFvw7dPLo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= diff --git a/go.mod b/go.mod index bdd5ee85fff..b257bdecab1 100644 --- a/go.mod +++ b/go.mod @@ -76,7 +76,7 @@ require ( github.com/shopspring/decimal v1.4.0 github.com/smartcontractkit/chain-selectors v1.0.40 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 diff --git a/go.sum b/go.sum index bde89e1c8a3..b9fe39601a3 100644 --- a/go.sum +++ b/go.sum @@ -1018,8 +1018,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 h1:XUz6jj+pPIvRbmlalXyFf6O0hGicU8thQtjFvw7dPLo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250214231858-f365e2bdecea h1:/1f/pWf7vSV9acTR9UPn2exPAwQG/LHGa4l9OywhS00= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 47986b51869..072af129c63 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -46,7 +46,7 @@ require ( github.com/slack-go/slack v0.15.0 github.com/smartcontractkit/chain-selectors v1.0.40 github.com/smartcontractkit/chainlink-automation v0.8.1 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 github.com/smartcontractkit/chainlink-integrations/evm v0.0.0-20250213145514-41d874782c02 github.com/smartcontractkit/chainlink-protos/job-distributor v0.9.0 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index a8bf643a088..dd2bac1d964 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1384,8 +1384,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 h1:XUz6jj+pPIvRbmlalXyFf6O0hGicU8thQtjFvw7dPLo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= diff --git a/integration-tests/load/go.mod b/integration-tests/load/go.mod index e699c548a0a..2e7558cce85 100644 --- a/integration-tests/load/go.mod +++ b/integration-tests/load/go.mod @@ -28,7 +28,7 @@ require ( github.com/rs/zerolog v1.33.0 github.com/slack-go/slack v0.15.0 github.com/smartcontractkit/chain-selectors v1.0.40 - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 github.com/smartcontractkit/chainlink-integrations/evm v0.0.0-20250213145514-41d874782c02 github.com/smartcontractkit/chainlink-testing-framework/lib v1.51.0 diff --git a/integration-tests/load/go.sum b/integration-tests/load/go.sum index 04569532d2f..f45501304d7 100644 --- a/integration-tests/load/go.sum +++ b/integration-tests/load/go.sum @@ -1369,8 +1369,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 h1:XUz6jj+pPIvRbmlalXyFf6O0hGicU8thQtjFvw7dPLo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= diff --git a/integration-tests/smoke/ccip/ccip_add_chain_test.go b/integration-tests/smoke/ccip/ccip_add_chain_test.go index 22c5acea9fa..0b5f9a3ad46 100644 --- a/integration-tests/smoke/ccip/ccip_add_chain_test.go +++ b/integration-tests/smoke/ccip/ccip_add_chain_test.go @@ -40,10 +40,6 @@ func Test_AddChain(t *testing.T) { testhelpers.WithPrerequisiteDeploymentOnly(nil), testhelpers.WithNumOfUsersPerChain(usersPerChain), testhelpers.WithNoJobsAndContracts(), - testhelpers.WithOCRConfigOverride(func(params *v1_6.CCIPOCRParams) { - // Only 1 boost (=OCR round) is enough to cover the fee - params.ExecuteOffChainConfig.RelativeBoostPerWaitHour = 1 - }), ) allChains := maps.Keys(e.Env.Chains) diff --git a/integration-tests/smoke/ccip/ccip_fee_boosting_test.go b/integration-tests/smoke/ccip/ccip_fee_boosting_test.go deleted file mode 100644 index e8e300b466f..00000000000 --- a/integration-tests/smoke/ccip/ccip_fee_boosting_test.go +++ /dev/null @@ -1,287 +0,0 @@ -package ccip - -import ( - "context" - "math/big" - "testing" - "time" - - "github.com/smartcontractkit/chainlink-common/pkg/config" - - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" - "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_6" - commoncs "github.com/smartcontractkit/chainlink/deployment/common/changeset" - testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/require" - "golang.org/x/exp/maps" - - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" - - cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" - - "github.com/smartcontractkit/chainlink/v2/core/capabilities/ccip/ccipevm" - - "github.com/smartcontractkit/chainlink/deployment" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/v1_2_0/router" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/v1_6_0/onramp" -) - -var ( - linkPrice = deployment.E18Mult(100) - wethPrice = deployment.E18Mult(4000) -) - -func Test_CCIPFeeBoosting(t *testing.T) { - t.Skip("Skipping test as Fee Boosting is disabled") - e, _, _ := testsetups.NewIntegrationEnvironment( - t, - testhelpers.WithOCRConfigOverride(func(params *v1_6.CCIPOCRParams) { - params.ExecuteOffChainConfig.RelativeBoostPerWaitHour = 1 - // Disable token price updates - params.CommitOffChainConfig.TokenPriceBatchWriteFrequency = *config.MustNewDuration(1_000_000 * time.Hour) - // Disable gas price updates - params.CommitOffChainConfig.RemoteGasPriceBatchWriteFrequency = *config.MustNewDuration(1_000_000 * time.Hour) - // Disable token price updates - params.CommitOffChainConfig.TokenInfo = nil - }), - ) - - state, err := changeset.LoadOnchainState(e.Env) - require.NoError(t, err) - - allChainSelectors := maps.Keys(e.Env.Chains) - require.Len(t, allChainSelectors, 2) - sourceChain := allChainSelectors[0] - destChain := allChainSelectors[1] - t.Log("All chain selectors:", allChainSelectors, - ", home chain selector:", e.HomeChainSel, - ", feed chain selector:", e.FeedChainSel, - ", source chain selector:", sourceChain, - ", dest chain selector:", destChain, - ) - - // TODO: discrepancy between client and the gas estimator gas price to be fixed - hardcoded for now - // fetchedGasPriceDest, err := e.Env.Chains[destChain].Client.SuggestGasPrice(tests.Context(t)) - fetchedGasPriceDest := big.NewInt(20e9) // 20 Gwei = default gas price - require.NoError(t, err) - originalGasPriceDestUSD := new(big.Int).Div( - new(big.Int).Mul(fetchedGasPriceDest, wethPrice), - big.NewInt(1e18), - ) - t.Log("Gas price on dest chain (USD):", originalGasPriceDestUSD) - - // Adjust destination gas price on source fee quoter to 95% of the current value - adjustedGasPriceDest := - new(big.Int).Div( - new(big.Int).Mul(originalGasPriceDestUSD, big.NewInt(99)), - big.NewInt(100), - ) - t.Log("Adjusted gas price on dest chain:", adjustedGasPriceDest) - - feeQuoterCfg := v1_6.DefaultFeeQuoterDestChainConfig(true) - // the default adds 10% to the gas price, we want to increase it - // to make sure the fee boosting will be finished in proper time for testing - feeQuoterCfg.GasMultiplierWeiPerEth = 120e16 - - testhelpers.AddLane(t, &e, sourceChain, destChain, false, - map[uint64]*big.Int{ - destChain: testhelpers.ToPackedFee(adjustedGasPriceDest, big.NewInt(0)), - }, - map[common.Address]*big.Int{ - state.Chains[sourceChain].LinkToken.Address(): linkPrice, - state.Chains[sourceChain].Weth9.Address(): wethPrice, - }, - feeQuoterCfg) - - // Update token prices in destination chain FeeQuoter - e.Env, err = commoncs.Apply(t, e.Env, e.TimelockContracts(t), - commoncs.Configure( - deployment.CreateLegacyChangeSet(v1_6.UpdateFeeQuoterPricesChangeset), - v1_6.UpdateFeeQuoterPricesConfig{ - PricesByChain: map[uint64]v1_6.FeeQuoterPriceUpdatePerSource{ - destChain: { - TokenPrices: map[common.Address]*big.Int{ - state.Chains[destChain].LinkToken.Address(): linkPrice, - state.Chains[destChain].Weth9.Address(): wethPrice, - }, - }, - }, - }, - ), - ) - require.NoError(t, err) - - startBlocks := make(map[uint64]*uint64) - expectedSeqNum := make(map[testhelpers.SourceDestPair]uint64) - expectedSeqNumExec := make(map[testhelpers.SourceDestPair][]uint64) - - latesthdr, err := e.Env.Chains[sourceChain].Client.HeaderByNumber(testcontext.Get(t), nil) - require.NoError(t, err) - block := latesthdr.Number.Uint64() - msgSentEvent := testhelpers.TestSendRequest(t, e.Env, state, sourceChain, destChain, false, router.ClientEVM2AnyMessage{ - Receiver: common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), - Data: []byte("message that needs fee boosting"), - TokenAmounts: nil, - FeeToken: common.HexToAddress("0x0"), - ExtraArgs: nil, - }) - startBlocks[sourceChain] = &block - expectedSeqNum[testhelpers.SourceDestPair{ - SourceChainSelector: sourceChain, - DestChainSelector: destChain, - }] = msgSentEvent.SequenceNumber - expectedSeqNumExec[testhelpers.SourceDestPair{ - SourceChainSelector: sourceChain, - DestChainSelector: destChain, - }] = []uint64{msgSentEvent.SequenceNumber} - - e.Env, err = commoncs.Apply(t, e.Env, e.TimelockContracts(t), - commoncs.Configure( - deployment.CreateLegacyChangeSet(v1_6.UpdateFeeQuoterPricesChangeset), - v1_6.UpdateFeeQuoterPricesConfig{ - PricesByChain: map[uint64]v1_6.FeeQuoterPriceUpdatePerSource{ - sourceChain: { - GasPrices: map[uint64]*big.Int{ - destChain: originalGasPriceDestUSD, - }, - }, - }, - }, - ), - ) - require.NoError(t, err) - - // Confirm gas prices are updated - srcFeeQuoter := state.Chains[sourceChain].FeeQuoter - err = testhelpers.ConfirmGasPriceUpdated(t, e.Env.Chains[destChain], srcFeeQuoter, 0, originalGasPriceDestUSD) - require.NoError(t, err) - - // Confirm that fee boosting will be triggered - require.True(t, willTriggerFeeBoosting(t, msgSentEvent, state, sourceChain, destChain)) - - // hack - time.Sleep(30 * time.Second) - replayBlocks := make(map[uint64]uint64) - replayBlocks[sourceChain] = 1 - replayBlocks[destChain] = 1 - testhelpers.ReplayLogs(t, e.Env.Offchain, replayBlocks) - - // Confirm that the message is committed and executed - testhelpers.ConfirmCommitForAllWithExpectedSeqNums(t, e.Env, state, expectedSeqNum, startBlocks) - testhelpers.ConfirmExecWithSeqNrsForAll(t, e.Env, state, expectedSeqNumExec, startBlocks) -} - -// TODO: Find a more accurate way to determine if fee boosting will be triggered -func willTriggerFeeBoosting( - t *testing.T, - msgSentEvent *onramp.OnRampCCIPMessageSent, - state changeset.CCIPOnChainState, - srcChain, destChain uint64) bool { - msg := convertToMessage(msgSentEvent.Message) - t.Log("\n=== Fee Boosting Analysis ===") - t.Logf("Src Chain: %d", msg.Header.SourceChainSelector) - t.Logf("Dest Chain: %d", msg.Header.DestChainSelector) - - ep := ccipevm.NewGasEstimateProvider() - chainState, exists := state.Chains[srcChain] - require.True(t, exists) - feeQuoter := chainState.FeeQuoter - - premium, err := feeQuoter.GetPremiumMultiplierWeiPerEth(&bind.CallOpts{Context: context.Background()}, chainState.Weth9.Address()) - require.NoError(t, err) - t.Logf("Premium: %d", premium) - - // Get LINK price - linkPrice, err := feeQuoter.GetTokenPrice(&bind.CallOpts{Context: context.Background()}, chainState.LinkToken.Address()) - require.NoError(t, err) - t.Logf("LINK Price: %s", linkPrice.Value.String()) - t.Logf("Juels in message: %s", msg.FeeValueJuels.String()) - - // Calculate fee in USD token - fee := new(big.Int).Div( - new(big.Int).Mul(linkPrice.Value, msg.FeeValueJuels.Int), - new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil), - ) - t.Logf("Fee paid (in USD token): %s", fee.String()) - - // Calculate message gas - messageGas := new(big.Int).SetUint64(ep.CalculateMessageMaxGas(msg)) - t.Logf("Estimated message gas: %s", messageGas.String()) - - // Get token and gas prices - nativeTokenAddress := chainState.Weth9.Address() - tokenAndGasPrice, err := feeQuoter.GetTokenAndGasPrices(&bind.CallOpts{Context: context.Background()}, nativeTokenAddress, destChain) - require.NoError(t, err) - t.Logf("Raw gas price (uint224): %s for chain: %d", tokenAndGasPrice.GasPriceValue.String(), destChain) - - // Extract uint112 gas price - gasPrice, err := convertGasPriceToUint112(tokenAndGasPrice.GasPriceValue) - require.NoError(t, err) - t.Logf("Extracted gas price (uint112): %s", gasPrice.String()) - t.Logf("Native token price: %s", tokenAndGasPrice.TokenPrice.String()) - - // Calculate total execution cost - execCost := new(big.Int).Mul(messageGas, gasPrice) - t.Logf("Total execution cost: %s", execCost.String()) - - // Check if fee boosting will trigger - willBoost := execCost.Cmp(fee) > 0 - t.Logf("\nWill fee boosting trigger? %v", willBoost) - t.Logf("Execution cost / Fee ratio: %.2f", - new(big.Float).Quo( - new(big.Float).SetInt(execCost), - new(big.Float).SetInt(fee), - ), - ) - - return execCost.Cmp(fee) > 0 -} - -func convertGasPriceToUint112(gasPrice *big.Int) (*big.Int, error) { - // Create a mask for uint112 (112 bits of 1s) - mask := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 112), big.NewInt(1)) - - // Extract the lower 112 bits using AND operation - result := new(big.Int).And(gasPrice, mask) - - return result, nil -} - -func convertToMessage(msg onramp.InternalEVM2AnyRampMessage) cciptypes.Message { - // Convert header - header := cciptypes.RampMessageHeader{ - MessageID: cciptypes.Bytes32(msg.Header.MessageId), - SourceChainSelector: cciptypes.ChainSelector(msg.Header.SourceChainSelector), - DestChainSelector: cciptypes.ChainSelector(msg.Header.DestChainSelector), - SequenceNumber: cciptypes.SeqNum(msg.Header.SequenceNumber), - Nonce: msg.Header.Nonce, - } - - // Convert token amounts - tokenAmounts := make([]cciptypes.RampTokenAmount, len(msg.TokenAmounts)) - for i, ta := range msg.TokenAmounts { - tokenAmounts[i] = cciptypes.RampTokenAmount{ - SourcePoolAddress: cciptypes.UnknownAddress(ta.SourcePoolAddress.Bytes()), - DestTokenAddress: cciptypes.UnknownAddress(ta.DestTokenAddress), - ExtraData: cciptypes.Bytes(ta.ExtraData), - Amount: cciptypes.BigInt{Int: ta.Amount}, - DestExecData: cciptypes.Bytes(ta.DestExecData), - } - } - - return cciptypes.Message{ - Header: header, - Sender: cciptypes.UnknownAddress(msg.Sender.Bytes()), - Data: cciptypes.Bytes(msg.Data), - Receiver: cciptypes.UnknownAddress(msg.Receiver), - ExtraArgs: cciptypes.Bytes(msg.ExtraArgs), - FeeToken: cciptypes.UnknownAddress(msg.FeeToken.Bytes()), - FeeTokenAmount: cciptypes.BigInt{Int: msg.FeeTokenAmount}, - FeeValueJuels: cciptypes.BigInt{Int: msg.FeeValueJuels}, - TokenAmounts: tokenAmounts, - } -} diff --git a/system-tests/lib/go.mod b/system-tests/lib/go.mod index 8868a5e7f6e..815fe491643 100644 --- a/system-tests/lib/go.mod +++ b/system-tests/lib/go.mod @@ -338,7 +338,7 @@ require ( github.com/smartcontractkit/ccip-owner-contracts v0.1.0 // indirect github.com/smartcontractkit/chain-selectors v1.0.40 // indirect github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 // indirect github.com/smartcontractkit/chainlink-feeds v0.1.1 // indirect diff --git a/system-tests/lib/go.sum b/system-tests/lib/go.sum index a52fa0c717d..22041e117e8 100644 --- a/system-tests/lib/go.sum +++ b/system-tests/lib/go.sum @@ -1122,8 +1122,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 h1:XUz6jj+pPIvRbmlalXyFf6O0hGicU8thQtjFvw7dPLo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index c46809f5f61..be753da2e5c 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -341,7 +341,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/ccip-owner-contracts v0.1.0 // indirect github.com/smartcontractkit/chainlink-automation v0.8.1 // indirect - github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb // indirect + github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 // indirect github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 // indirect github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 // indirect github.com/smartcontractkit/chainlink-data-streams v0.1.1-0.20250128203428-08031923fbe5 // indirect diff --git a/system-tests/tests/go.sum b/system-tests/tests/go.sum index 57a42423877..7a02d902f03 100644 --- a/system-tests/tests/go.sum +++ b/system-tests/tests/go.sum @@ -1122,8 +1122,8 @@ github.com/smartcontractkit/chain-selectors v1.0.40 h1:iLvvoZeehVq6/7F+zzolQLF0D github.com/smartcontractkit/chain-selectors v1.0.40/go.mod h1:xsKM0aN3YGcQKTPRPDDtPx2l4mlTN1Djmg0VVXV40b8= github.com/smartcontractkit/chainlink-automation v0.8.1 h1:sTc9LKpBvcKPc1JDYAmgBc2xpDKBco/Q4h4ydl6+UUU= github.com/smartcontractkit/chainlink-automation v0.8.1/go.mod h1:Iij36PvWZ6blrdC5A/nrQUBuf3MH3JvsBB9sSyc9W08= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb h1:Z5QRY8DtXnxnPwbo+mR1mxwaL+OClza3ATUqcQ1Ynz8= -github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224132459-c57ae4a97dcb/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512 h1:XUz6jj+pPIvRbmlalXyFf6O0hGicU8thQtjFvw7dPLo= +github.com/smartcontractkit/chainlink-ccip v0.0.0-20250224184104-b25011bb3512/go.mod h1:Hht/OJq/PxC+gnBCIPyzHt4Otsw6mYwUVsmtOqIvlxo= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01 h1:R3OD6Phi0ULIQ2uvHiKVWYdgpi/O1Mt46CUK1UApcXU= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20250214202341-4190f2db1c01/go.mod h1:Bmwq4lNb5tE47sydN0TKetcLEGbgl+VxHEWp4S0LI60= github.com/smartcontractkit/chainlink-common v0.4.2-0.20250221174903-e1e47fdb11b0 h1:BTN2nQgFKBxgas6oqY3ym82O+wT++WlpP1+a6KzIfY0= From 73b6542360a051845e2916a00032c534a8263521 Mon Sep 17 00:00:00 2001 From: pavel-raykov <165708424+pavel-raykov@users.noreply.github.com> Date: Mon, 24 Feb 2025 21:44:24 +0100 Subject: [PATCH 11/14] Minor (#16550) --- .changeset/late-goats-exercise.md | 5 +++++ core/utils/json_normalization_test.go | 7 +++---- 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 .changeset/late-goats-exercise.md diff --git a/.changeset/late-goats-exercise.md b/.changeset/late-goats-exercise.md new file mode 100644 index 00000000000..9433719dc4e --- /dev/null +++ b/.changeset/late-goats-exercise.md @@ -0,0 +1,5 @@ +--- +"chainlink": minor +--- + +#updated Removed unnecessary dependendies from json_normalization_test.go. diff --git a/core/utils/json_normalization_test.go b/core/utils/json_normalization_test.go index 943145c53e3..f01dd0dacaf 100644 --- a/core/utils/json_normalization_test.go +++ b/core/utils/json_normalization_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "testing" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/stretchr/testify/assert" @@ -34,12 +33,12 @@ func TestNormalizedJSON(t *testing.T) { false, }, {"reordering", - cltest.JSONFromString(t, `{"a": "!", "A": "1"}`), + map[string]string{"a": "!", "A": "1"}, `{"A":"1","a":"!"}`, false, }, {"more key reordering", - cltest.JSONFromString(t, `{"a": "!", "A": "1", "B": "@", "b":"?", "c":"..."}`), + map[string]string{"a": "!", "A": "1", "B": "@", "b": "?", "c": "..."}, `{"A":"1","B":"@","a":"!","b":"?","c":"..."}`, false, }, @@ -52,7 +51,7 @@ func TestNormalizedJSON(t *testing.T) { str, err := utils.NormalizedJSON(jsonBytes) - cltest.AssertError(t, test.wantError, err) + assert.Equal(t, test.wantError, err != nil) assert.Equal(t, test.want, str) }) } From 4e86fb8523085ae0e13b9ddad3f98ca18490d3b3 Mon Sep 17 00:00:00 2001 From: Cedric Date: Mon, 24 Feb 2025 21:30:38 +0000 Subject: [PATCH 12/14] [CAPPL-523] Filter events by DON ID (#16420) * [CAPPL-523] Filter events by DON ID * Lint fixes --- .../workflows/syncer/workflow_syncer_test.go | 77 +++++++++++++++++++ .../workflows/syncer/workflow_registry.go | 31 +++++++- 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/core/services/relay/evm/capabilities/workflows/syncer/workflow_syncer_test.go b/core/services/relay/evm/capabilities/workflows/syncer/workflow_syncer_test.go index ca9674700c9..26b7015a6c7 100644 --- a/core/services/relay/evm/capabilities/workflows/syncer/workflow_syncer_test.go +++ b/core/services/relay/evm/capabilities/workflows/syncer/workflow_syncer_test.go @@ -399,6 +399,83 @@ func Test_SecretsWorker(t *testing.T) { }, tests.WaitTimeout(t), time.Second) } +func Test_RegistrySyncer_SkipsEventsNotBelongingToDON(t *testing.T) { + var ( + lggr = logger.TestLogger(t) + backendTH = testutils.NewEVMBackendTH(t) + + giveTicker = time.NewTicker(500 * time.Millisecond) + giveBinaryURL = "https://original-url.com" + donID = uint32(1) + otherDonID = uint32(2) + skippedWorkflow = RegisterWorkflowCMD{ + Name: "test-wf2", + DonID: otherDonID, + Status: uint8(1), + BinaryURL: giveBinaryURL, + } + giveWorkflow = RegisterWorkflowCMD{ + Name: "test-wf", + DonID: donID, + Status: uint8(1), + BinaryURL: "someurl", + } + wantContents = "updated contents" + ) + + defer giveTicker.Stop() + + // Deploy a test workflow_registry + wfRegistryAddr, _, wfRegistryC, err := workflow_registry_wrapper.DeployWorkflowRegistry(backendTH.ContractsOwner, backendTH.Backend.Client()) + backendTH.Backend.Commit() + require.NoError(t, err) + + from := [20]byte(backendTH.ContractsOwner.From) + id, err := pkgworkflows.GenerateWorkflowID(from[:], "test-wf", []byte(wantContents), []byte(""), "") + require.NoError(t, err) + giveWorkflow.ID = id + + from = [20]byte(backendTH.ContractsOwner.From) + id, err = pkgworkflows.GenerateWorkflowID(from[:], "test-wf", []byte(wantContents), []byte("dummy config"), "") + require.NoError(t, err) + skippedWorkflow.ID = id + + handler := newTestEvtHandler() + + worker := syncer.NewWorkflowRegistry( + lggr, + func(ctx context.Context, bytes []byte) (syncer.ContractReader, error) { + return backendTH.NewContractReader(ctx, t, bytes) + }, + wfRegistryAddr.Hex(), + syncer.WorkflowEventPollerConfig{QueryCount: 20}, + handler, + &testDonNotifier{ + don: capabilities.DON{ + ID: donID, + }, + err: nil, + }, + syncer.WithTicker(giveTicker.C), + ) + + // setup contract state to allow the secrets to be updated + updateAllowedDONs(t, backendTH, wfRegistryC, []uint32{donID, otherDonID}, true) + updateAuthorizedAddress(t, backendTH, wfRegistryC, []common.Address{backendTH.ContractsOwner.From}, true) + + servicetest.Run(t, worker) + + // generate a log event + registerWorkflow(t, backendTH, wfRegistryC, skippedWorkflow) + registerWorkflow(t, backendTH, wfRegistryC, giveWorkflow) + + require.Eventually(t, func() bool { + // we process events in order, and should only receive 1 event + // the first is skipped as it belongs to another don. + return len(handler.GetEvents()) == 1 + }, tests.WaitTimeout(t), time.Second) +} + func Test_RegistrySyncer_WorkflowRegistered_InitiallyPaused(t *testing.T) { var ( ctx = coretestutils.Context(t) diff --git a/core/services/workflows/syncer/workflow_registry.go b/core/services/workflows/syncer/workflow_registry.go index 65ecdf1f467..b5da8f58c5b 100644 --- a/core/services/workflows/syncer/workflow_registry.go +++ b/core/services/workflows/syncer/workflow_registry.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "iter" + "strconv" "strings" "sync" "time" @@ -63,6 +64,7 @@ type WorkflowRegistryEvent struct { Data any EventType WorkflowRegistryEventType Head Head + DonID *uint32 } func (we WorkflowRegistryEvent) GetEventType() WorkflowRegistryEventType { @@ -231,7 +233,7 @@ func (w *workflowRegistry) Start(_ context.Context) error { } } - w.readRegistryEvents(ctx, reader, loadWorkflowsHead.Height) + w.readRegistryEvents(ctx, don, reader, loadWorkflowsHead.Height) }() return nil @@ -259,7 +261,7 @@ func (w *workflowRegistry) Name() string { } // readRegistryEvents polls the contract for events and send them to the events channel. -func (w *workflowRegistry) readRegistryEvents(ctx context.Context, reader ContractReader, lastReadBlockNumber string) { +func (w *workflowRegistry) readRegistryEvents(ctx context.Context, don capabilities.DON, reader ContractReader, lastReadBlockNumber string) { ticker := w.getTicker() var keyQueries = make([]types.ContractKeyFilter, 0, len(w.eventTypes)) @@ -325,7 +327,25 @@ func (w *workflowRegistry) readRegistryEvents(ctx context.Context, reader Contra continue } - events = append(events, toWorkflowRegistryEventResponse(log.Sequence, log.EventType, w.lggr)) + event := toWorkflowRegistryEventResponse(log.Sequence, log.EventType, w.lggr) + + switch { + case event.Event.DonID == nil: + // event is missing a DonID, so don't filter it out; + // it applies to all Dons + events = append(events, event) + case *event.Event.DonID == don.ID: + // event has a DonID and matches, so it applies to this DON. + events = append(events, event) + default: + // event doesn't match, let's skip it + donID := "MISSING_DON_ID" + if event.Event.DonID != nil { + donID = strconv.FormatUint(uint64(*event.Event.DonID), 10) + } + w.lggr.Debugw("event belongs to a different don, skipping...", "don", don.ID, "gotDON", donID) + } + cursor = log.Sequence.Cursor } @@ -540,6 +560,7 @@ func toWorkflowRegistryEventResponse( return resp } resp.Event.Data = data + resp.Event.DonID = &data.DonID case WorkflowUpdatedEvent: var data WorkflowRegistryWorkflowUpdatedV1 if err := dataAsValuesMap.UnwrapTo(&data); err != nil { @@ -549,6 +570,7 @@ func toWorkflowRegistryEventResponse( return resp } resp.Event.Data = data + resp.Event.DonID = &data.DonID case WorkflowPausedEvent: var data WorkflowRegistryWorkflowPausedV1 if err := dataAsValuesMap.UnwrapTo(&data); err != nil { @@ -558,6 +580,7 @@ func toWorkflowRegistryEventResponse( return resp } resp.Event.Data = data + resp.Event.DonID = &data.DonID case WorkflowActivatedEvent: var data WorkflowRegistryWorkflowActivatedV1 if err := dataAsValuesMap.UnwrapTo(&data); err != nil { @@ -567,6 +590,7 @@ func toWorkflowRegistryEventResponse( return resp } resp.Event.Data = data + resp.Event.DonID = &data.DonID case WorkflowDeletedEvent: var data WorkflowRegistryWorkflowDeletedV1 if err := dataAsValuesMap.UnwrapTo(&data); err != nil { @@ -576,6 +600,7 @@ func toWorkflowRegistryEventResponse( return resp } resp.Event.Data = data + resp.Event.DonID = &data.DonID default: lggr.Errorf("unknown event type: %s", evt) resp.Event = nil From 5609153fca504445bea11f89196e5c293cc30a3e Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 24 Feb 2025 15:53:16 -0600 Subject: [PATCH 13/14] core/chains/evm/txm: move mocks internal (#16399) --- .mockery.yaml | 8 +- core/chains/evm/forwarders/mocks/orm.go | 335 --------- ...uilder.go => mock_attempt_builder_test.go} | 52 +- .../{mocks/client.go => mock_client_test.go} | 68 +- core/chains/evm/txm/mock_keystore_test.go | 98 +++ core/chains/evm/txm/mock_tx_store_test.go | 647 ++++++++++++++++++ core/chains/evm/txm/mocks/keystore.go | 98 --- core/chains/evm/txm/mocks/tx_store.go | 647 ------------------ core/chains/evm/txm/txm_test.go | 39 +- 9 files changed, 829 insertions(+), 1163 deletions(-) delete mode 100644 core/chains/evm/forwarders/mocks/orm.go rename core/chains/evm/txm/{mocks/attempt_builder.go => mock_attempt_builder_test.go} (52%) rename core/chains/evm/txm/{mocks/client.go => mock_client_test.go} (50%) create mode 100644 core/chains/evm/txm/mock_keystore_test.go create mode 100644 core/chains/evm/txm/mock_tx_store_test.go delete mode 100644 core/chains/evm/txm/mocks/keystore.go delete mode 100644 core/chains/evm/txm/mocks/tx_store.go diff --git a/.mockery.yaml b/.mockery.yaml index 7ad18b97802..e49546519fd 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -34,9 +34,6 @@ packages: interfaces: Dispatcher: Receiver: - github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders: - interfaces: - ORM: github.com/smartcontractkit/chainlink/v2/core/chains/evm/log: interfaces: Broadcaster: @@ -48,6 +45,11 @@ packages: interfaces: LogPoller: github.com/smartcontractkit/chainlink/v2/core/chains/evm/txm: + config: + dir: core/chains/evm/txm + outpkg: txm + mockname: "mock{{ .InterfaceName }}" + filename: "mock_{{ .InterfaceName | snakecase }}_test.go" interfaces: Client: TxStore: diff --git a/core/chains/evm/forwarders/mocks/orm.go b/core/chains/evm/forwarders/mocks/orm.go deleted file mode 100644 index 86783dc3b1e..00000000000 --- a/core/chains/evm/forwarders/mocks/orm.go +++ /dev/null @@ -1,335 +0,0 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. - -package mocks - -import ( - common "github.com/ethereum/go-ethereum/common" - big "github.com/smartcontractkit/chainlink-integrations/evm/utils/big" - - context "context" - - forwarders "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" - - mock "github.com/stretchr/testify/mock" - - sqlutil "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" -) - -// ORM is an autogenerated mock type for the ORM type -type ORM struct { - mock.Mock -} - -type ORM_Expecter struct { - mock *mock.Mock -} - -func (_m *ORM) EXPECT() *ORM_Expecter { - return &ORM_Expecter{mock: &_m.Mock} -} - -// CreateForwarder provides a mock function with given fields: ctx, addr, evmChainId -func (_m *ORM) CreateForwarder(ctx context.Context, addr common.Address, evmChainId big.Big) (forwarders.Forwarder, error) { - ret := _m.Called(ctx, addr, evmChainId) - - if len(ret) == 0 { - panic("no return value specified for CreateForwarder") - } - - var r0 forwarders.Forwarder - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, big.Big) (forwarders.Forwarder, error)); ok { - return rf(ctx, addr, evmChainId) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, big.Big) forwarders.Forwarder); ok { - r0 = rf(ctx, addr, evmChainId) - } else { - r0 = ret.Get(0).(forwarders.Forwarder) - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, big.Big) error); ok { - r1 = rf(ctx, addr, evmChainId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ORM_CreateForwarder_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateForwarder' -type ORM_CreateForwarder_Call struct { - *mock.Call -} - -// CreateForwarder is a helper method to define mock.On call -// - ctx context.Context -// - addr common.Address -// - evmChainId big.Big -func (_e *ORM_Expecter) CreateForwarder(ctx interface{}, addr interface{}, evmChainId interface{}) *ORM_CreateForwarder_Call { - return &ORM_CreateForwarder_Call{Call: _e.mock.On("CreateForwarder", ctx, addr, evmChainId)} -} - -func (_c *ORM_CreateForwarder_Call) Run(run func(ctx context.Context, addr common.Address, evmChainId big.Big)) *ORM_CreateForwarder_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(big.Big)) - }) - return _c -} - -func (_c *ORM_CreateForwarder_Call) Return(fwd forwarders.Forwarder, err error) *ORM_CreateForwarder_Call { - _c.Call.Return(fwd, err) - return _c -} - -func (_c *ORM_CreateForwarder_Call) RunAndReturn(run func(context.Context, common.Address, big.Big) (forwarders.Forwarder, error)) *ORM_CreateForwarder_Call { - _c.Call.Return(run) - return _c -} - -// DeleteForwarder provides a mock function with given fields: ctx, id, cleanup -func (_m *ORM) DeleteForwarder(ctx context.Context, id int64, cleanup func(sqlutil.DataSource, int64, common.Address) error) error { - ret := _m.Called(ctx, id, cleanup) - - if len(ret) == 0 { - panic("no return value specified for DeleteForwarder") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, int64, func(sqlutil.DataSource, int64, common.Address) error) error); ok { - r0 = rf(ctx, id, cleanup) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// ORM_DeleteForwarder_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteForwarder' -type ORM_DeleteForwarder_Call struct { - *mock.Call -} - -// DeleteForwarder is a helper method to define mock.On call -// - ctx context.Context -// - id int64 -// - cleanup func(sqlutil.DataSource , int64 , common.Address) error -func (_e *ORM_Expecter) DeleteForwarder(ctx interface{}, id interface{}, cleanup interface{}) *ORM_DeleteForwarder_Call { - return &ORM_DeleteForwarder_Call{Call: _e.mock.On("DeleteForwarder", ctx, id, cleanup)} -} - -func (_c *ORM_DeleteForwarder_Call) Run(run func(ctx context.Context, id int64, cleanup func(sqlutil.DataSource, int64, common.Address) error)) *ORM_DeleteForwarder_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int64), args[2].(func(sqlutil.DataSource, int64, common.Address) error)) - }) - return _c -} - -func (_c *ORM_DeleteForwarder_Call) Return(_a0 error) *ORM_DeleteForwarder_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *ORM_DeleteForwarder_Call) RunAndReturn(run func(context.Context, int64, func(sqlutil.DataSource, int64, common.Address) error) error) *ORM_DeleteForwarder_Call { - _c.Call.Return(run) - return _c -} - -// FindForwarders provides a mock function with given fields: ctx, offset, limit -func (_m *ORM) FindForwarders(ctx context.Context, offset int, limit int) ([]forwarders.Forwarder, int, error) { - ret := _m.Called(ctx, offset, limit) - - if len(ret) == 0 { - panic("no return value specified for FindForwarders") - } - - var r0 []forwarders.Forwarder - var r1 int - var r2 error - if rf, ok := ret.Get(0).(func(context.Context, int, int) ([]forwarders.Forwarder, int, error)); ok { - return rf(ctx, offset, limit) - } - if rf, ok := ret.Get(0).(func(context.Context, int, int) []forwarders.Forwarder); ok { - r0 = rf(ctx, offset, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]forwarders.Forwarder) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int, int) int); ok { - r1 = rf(ctx, offset, limit) - } else { - r1 = ret.Get(1).(int) - } - - if rf, ok := ret.Get(2).(func(context.Context, int, int) error); ok { - r2 = rf(ctx, offset, limit) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// ORM_FindForwarders_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindForwarders' -type ORM_FindForwarders_Call struct { - *mock.Call -} - -// FindForwarders is a helper method to define mock.On call -// - ctx context.Context -// - offset int -// - limit int -func (_e *ORM_Expecter) FindForwarders(ctx interface{}, offset interface{}, limit interface{}) *ORM_FindForwarders_Call { - return &ORM_FindForwarders_Call{Call: _e.mock.On("FindForwarders", ctx, offset, limit)} -} - -func (_c *ORM_FindForwarders_Call) Run(run func(ctx context.Context, offset int, limit int)) *ORM_FindForwarders_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(int), args[2].(int)) - }) - return _c -} - -func (_c *ORM_FindForwarders_Call) Return(_a0 []forwarders.Forwarder, _a1 int, _a2 error) *ORM_FindForwarders_Call { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *ORM_FindForwarders_Call) RunAndReturn(run func(context.Context, int, int) ([]forwarders.Forwarder, int, error)) *ORM_FindForwarders_Call { - _c.Call.Return(run) - return _c -} - -// FindForwardersByChain provides a mock function with given fields: ctx, evmChainId -func (_m *ORM) FindForwardersByChain(ctx context.Context, evmChainId big.Big) ([]forwarders.Forwarder, error) { - ret := _m.Called(ctx, evmChainId) - - if len(ret) == 0 { - panic("no return value specified for FindForwardersByChain") - } - - var r0 []forwarders.Forwarder - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, big.Big) ([]forwarders.Forwarder, error)); ok { - return rf(ctx, evmChainId) - } - if rf, ok := ret.Get(0).(func(context.Context, big.Big) []forwarders.Forwarder); ok { - r0 = rf(ctx, evmChainId) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]forwarders.Forwarder) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, big.Big) error); ok { - r1 = rf(ctx, evmChainId) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ORM_FindForwardersByChain_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindForwardersByChain' -type ORM_FindForwardersByChain_Call struct { - *mock.Call -} - -// FindForwardersByChain is a helper method to define mock.On call -// - ctx context.Context -// - evmChainId big.Big -func (_e *ORM_Expecter) FindForwardersByChain(ctx interface{}, evmChainId interface{}) *ORM_FindForwardersByChain_Call { - return &ORM_FindForwardersByChain_Call{Call: _e.mock.On("FindForwardersByChain", ctx, evmChainId)} -} - -func (_c *ORM_FindForwardersByChain_Call) Run(run func(ctx context.Context, evmChainId big.Big)) *ORM_FindForwardersByChain_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(big.Big)) - }) - return _c -} - -func (_c *ORM_FindForwardersByChain_Call) Return(_a0 []forwarders.Forwarder, _a1 error) *ORM_FindForwardersByChain_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *ORM_FindForwardersByChain_Call) RunAndReturn(run func(context.Context, big.Big) ([]forwarders.Forwarder, error)) *ORM_FindForwardersByChain_Call { - _c.Call.Return(run) - return _c -} - -// FindForwardersInListByChain provides a mock function with given fields: ctx, evmChainId, addrs -func (_m *ORM) FindForwardersInListByChain(ctx context.Context, evmChainId big.Big, addrs []common.Address) ([]forwarders.Forwarder, error) { - ret := _m.Called(ctx, evmChainId, addrs) - - if len(ret) == 0 { - panic("no return value specified for FindForwardersInListByChain") - } - - var r0 []forwarders.Forwarder - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, big.Big, []common.Address) ([]forwarders.Forwarder, error)); ok { - return rf(ctx, evmChainId, addrs) - } - if rf, ok := ret.Get(0).(func(context.Context, big.Big, []common.Address) []forwarders.Forwarder); ok { - r0 = rf(ctx, evmChainId, addrs) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]forwarders.Forwarder) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, big.Big, []common.Address) error); ok { - r1 = rf(ctx, evmChainId, addrs) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ORM_FindForwardersInListByChain_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FindForwardersInListByChain' -type ORM_FindForwardersInListByChain_Call struct { - *mock.Call -} - -// FindForwardersInListByChain is a helper method to define mock.On call -// - ctx context.Context -// - evmChainId big.Big -// - addrs []common.Address -func (_e *ORM_Expecter) FindForwardersInListByChain(ctx interface{}, evmChainId interface{}, addrs interface{}) *ORM_FindForwardersInListByChain_Call { - return &ORM_FindForwardersInListByChain_Call{Call: _e.mock.On("FindForwardersInListByChain", ctx, evmChainId, addrs)} -} - -func (_c *ORM_FindForwardersInListByChain_Call) Run(run func(ctx context.Context, evmChainId big.Big, addrs []common.Address)) *ORM_FindForwardersInListByChain_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(big.Big), args[2].([]common.Address)) - }) - return _c -} - -func (_c *ORM_FindForwardersInListByChain_Call) Return(_a0 []forwarders.Forwarder, _a1 error) *ORM_FindForwardersInListByChain_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *ORM_FindForwardersInListByChain_Call) RunAndReturn(run func(context.Context, big.Big, []common.Address) ([]forwarders.Forwarder, error)) *ORM_FindForwardersInListByChain_Call { - _c.Call.Return(run) - return _c -} - -// NewORM creates a new instance of ORM. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewORM(t interface { - mock.TestingT - Cleanup(func()) -}) *ORM { - mock := &ORM{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/chains/evm/txm/mocks/attempt_builder.go b/core/chains/evm/txm/mock_attempt_builder_test.go similarity index 52% rename from core/chains/evm/txm/mocks/attempt_builder.go rename to core/chains/evm/txm/mock_attempt_builder_test.go index 91961e5d420..db0f036026c 100644 --- a/core/chains/evm/txm/mocks/attempt_builder.go +++ b/core/chains/evm/txm/mock_attempt_builder_test.go @@ -1,6 +1,6 @@ // Code generated by mockery v2.50.0. DO NOT EDIT. -package mocks +package txm import ( context "context" @@ -11,21 +11,21 @@ import ( types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txm/types" ) -// AttemptBuilder is an autogenerated mock type for the AttemptBuilder type -type AttemptBuilder struct { +// mockAttemptBuilder is an autogenerated mock type for the AttemptBuilder type +type mockAttemptBuilder struct { mock.Mock } -type AttemptBuilder_Expecter struct { +type mockAttemptBuilder_Expecter struct { mock *mock.Mock } -func (_m *AttemptBuilder) EXPECT() *AttemptBuilder_Expecter { - return &AttemptBuilder_Expecter{mock: &_m.Mock} +func (_m *mockAttemptBuilder) EXPECT() *mockAttemptBuilder_Expecter { + return &mockAttemptBuilder_Expecter{mock: &_m.Mock} } // NewAttempt provides a mock function with given fields: _a0, _a1, _a2, _a3 -func (_m *AttemptBuilder) NewAttempt(_a0 context.Context, _a1 logger.Logger, _a2 *types.Transaction, _a3 bool) (*types.Attempt, error) { +func (_m *mockAttemptBuilder) NewAttempt(_a0 context.Context, _a1 logger.Logger, _a2 *types.Transaction, _a3 bool) (*types.Attempt, error) { ret := _m.Called(_a0, _a1, _a2, _a3) if len(ret) == 0 { @@ -54,8 +54,8 @@ func (_m *AttemptBuilder) NewAttempt(_a0 context.Context, _a1 logger.Logger, _a2 return r0, r1 } -// AttemptBuilder_NewAttempt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewAttempt' -type AttemptBuilder_NewAttempt_Call struct { +// mockAttemptBuilder_NewAttempt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewAttempt' +type mockAttemptBuilder_NewAttempt_Call struct { *mock.Call } @@ -64,29 +64,29 @@ type AttemptBuilder_NewAttempt_Call struct { // - _a1 logger.Logger // - _a2 *types.Transaction // - _a3 bool -func (_e *AttemptBuilder_Expecter) NewAttempt(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *AttemptBuilder_NewAttempt_Call { - return &AttemptBuilder_NewAttempt_Call{Call: _e.mock.On("NewAttempt", _a0, _a1, _a2, _a3)} +func (_e *mockAttemptBuilder_Expecter) NewAttempt(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *mockAttemptBuilder_NewAttempt_Call { + return &mockAttemptBuilder_NewAttempt_Call{Call: _e.mock.On("NewAttempt", _a0, _a1, _a2, _a3)} } -func (_c *AttemptBuilder_NewAttempt_Call) Run(run func(_a0 context.Context, _a1 logger.Logger, _a2 *types.Transaction, _a3 bool)) *AttemptBuilder_NewAttempt_Call { +func (_c *mockAttemptBuilder_NewAttempt_Call) Run(run func(_a0 context.Context, _a1 logger.Logger, _a2 *types.Transaction, _a3 bool)) *mockAttemptBuilder_NewAttempt_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(logger.Logger), args[2].(*types.Transaction), args[3].(bool)) }) return _c } -func (_c *AttemptBuilder_NewAttempt_Call) Return(_a0 *types.Attempt, _a1 error) *AttemptBuilder_NewAttempt_Call { +func (_c *mockAttemptBuilder_NewAttempt_Call) Return(_a0 *types.Attempt, _a1 error) *mockAttemptBuilder_NewAttempt_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *AttemptBuilder_NewAttempt_Call) RunAndReturn(run func(context.Context, logger.Logger, *types.Transaction, bool) (*types.Attempt, error)) *AttemptBuilder_NewAttempt_Call { +func (_c *mockAttemptBuilder_NewAttempt_Call) RunAndReturn(run func(context.Context, logger.Logger, *types.Transaction, bool) (*types.Attempt, error)) *mockAttemptBuilder_NewAttempt_Call { _c.Call.Return(run) return _c } // NewBumpAttempt provides a mock function with given fields: _a0, _a1, _a2, _a3 -func (_m *AttemptBuilder) NewBumpAttempt(_a0 context.Context, _a1 logger.Logger, _a2 *types.Transaction, _a3 types.Attempt) (*types.Attempt, error) { +func (_m *mockAttemptBuilder) NewBumpAttempt(_a0 context.Context, _a1 logger.Logger, _a2 *types.Transaction, _a3 types.Attempt) (*types.Attempt, error) { ret := _m.Called(_a0, _a1, _a2, _a3) if len(ret) == 0 { @@ -115,8 +115,8 @@ func (_m *AttemptBuilder) NewBumpAttempt(_a0 context.Context, _a1 logger.Logger, return r0, r1 } -// AttemptBuilder_NewBumpAttempt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewBumpAttempt' -type AttemptBuilder_NewBumpAttempt_Call struct { +// mockAttemptBuilder_NewBumpAttempt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NewBumpAttempt' +type mockAttemptBuilder_NewBumpAttempt_Call struct { *mock.Call } @@ -125,34 +125,34 @@ type AttemptBuilder_NewBumpAttempt_Call struct { // - _a1 logger.Logger // - _a2 *types.Transaction // - _a3 types.Attempt -func (_e *AttemptBuilder_Expecter) NewBumpAttempt(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *AttemptBuilder_NewBumpAttempt_Call { - return &AttemptBuilder_NewBumpAttempt_Call{Call: _e.mock.On("NewBumpAttempt", _a0, _a1, _a2, _a3)} +func (_e *mockAttemptBuilder_Expecter) NewBumpAttempt(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *mockAttemptBuilder_NewBumpAttempt_Call { + return &mockAttemptBuilder_NewBumpAttempt_Call{Call: _e.mock.On("NewBumpAttempt", _a0, _a1, _a2, _a3)} } -func (_c *AttemptBuilder_NewBumpAttempt_Call) Run(run func(_a0 context.Context, _a1 logger.Logger, _a2 *types.Transaction, _a3 types.Attempt)) *AttemptBuilder_NewBumpAttempt_Call { +func (_c *mockAttemptBuilder_NewBumpAttempt_Call) Run(run func(_a0 context.Context, _a1 logger.Logger, _a2 *types.Transaction, _a3 types.Attempt)) *mockAttemptBuilder_NewBumpAttempt_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(logger.Logger), args[2].(*types.Transaction), args[3].(types.Attempt)) }) return _c } -func (_c *AttemptBuilder_NewBumpAttempt_Call) Return(_a0 *types.Attempt, _a1 error) *AttemptBuilder_NewBumpAttempt_Call { +func (_c *mockAttemptBuilder_NewBumpAttempt_Call) Return(_a0 *types.Attempt, _a1 error) *mockAttemptBuilder_NewBumpAttempt_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *AttemptBuilder_NewBumpAttempt_Call) RunAndReturn(run func(context.Context, logger.Logger, *types.Transaction, types.Attempt) (*types.Attempt, error)) *AttemptBuilder_NewBumpAttempt_Call { +func (_c *mockAttemptBuilder_NewBumpAttempt_Call) RunAndReturn(run func(context.Context, logger.Logger, *types.Transaction, types.Attempt) (*types.Attempt, error)) *mockAttemptBuilder_NewBumpAttempt_Call { _c.Call.Return(run) return _c } -// NewAttemptBuilder creates a new instance of AttemptBuilder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// newMockAttemptBuilder creates a new instance of mockAttemptBuilder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func NewAttemptBuilder(t interface { +func newMockAttemptBuilder(t interface { mock.TestingT Cleanup(func()) -}) *AttemptBuilder { - mock := &AttemptBuilder{} +}) *mockAttemptBuilder { + mock := &mockAttemptBuilder{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/core/chains/evm/txm/mocks/client.go b/core/chains/evm/txm/mock_client_test.go similarity index 50% rename from core/chains/evm/txm/mocks/client.go rename to core/chains/evm/txm/mock_client_test.go index cac2e55491a..68bf3001427 100644 --- a/core/chains/evm/txm/mocks/client.go +++ b/core/chains/evm/txm/mock_client_test.go @@ -1,6 +1,6 @@ // Code generated by mockery v2.50.0. DO NOT EDIT. -package mocks +package txm import ( context "context" @@ -13,21 +13,21 @@ import ( types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txm/types" ) -// Client is an autogenerated mock type for the Client type -type Client struct { +// mockClient is an autogenerated mock type for the Client type +type mockClient struct { mock.Mock } -type Client_Expecter struct { +type mockClient_Expecter struct { mock *mock.Mock } -func (_m *Client) EXPECT() *Client_Expecter { - return &Client_Expecter{mock: &_m.Mock} +func (_m *mockClient) EXPECT() *mockClient_Expecter { + return &mockClient_Expecter{mock: &_m.Mock} } // NonceAt provides a mock function with given fields: _a0, _a1, _a2 -func (_m *Client) NonceAt(_a0 context.Context, _a1 common.Address, _a2 *big.Int) (uint64, error) { +func (_m *mockClient) NonceAt(_a0 context.Context, _a1 common.Address, _a2 *big.Int) (uint64, error) { ret := _m.Called(_a0, _a1, _a2) if len(ret) == 0 { @@ -54,8 +54,8 @@ func (_m *Client) NonceAt(_a0 context.Context, _a1 common.Address, _a2 *big.Int) return r0, r1 } -// Client_NonceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NonceAt' -type Client_NonceAt_Call struct { +// mockClient_NonceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'NonceAt' +type mockClient_NonceAt_Call struct { *mock.Call } @@ -63,29 +63,29 @@ type Client_NonceAt_Call struct { // - _a0 context.Context // - _a1 common.Address // - _a2 *big.Int -func (_e *Client_Expecter) NonceAt(_a0 interface{}, _a1 interface{}, _a2 interface{}) *Client_NonceAt_Call { - return &Client_NonceAt_Call{Call: _e.mock.On("NonceAt", _a0, _a1, _a2)} +func (_e *mockClient_Expecter) NonceAt(_a0 interface{}, _a1 interface{}, _a2 interface{}) *mockClient_NonceAt_Call { + return &mockClient_NonceAt_Call{Call: _e.mock.On("NonceAt", _a0, _a1, _a2)} } -func (_c *Client_NonceAt_Call) Run(run func(_a0 context.Context, _a1 common.Address, _a2 *big.Int)) *Client_NonceAt_Call { +func (_c *mockClient_NonceAt_Call) Run(run func(_a0 context.Context, _a1 common.Address, _a2 *big.Int)) *mockClient_NonceAt_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(common.Address), args[2].(*big.Int)) }) return _c } -func (_c *Client_NonceAt_Call) Return(_a0 uint64, _a1 error) *Client_NonceAt_Call { +func (_c *mockClient_NonceAt_Call) Return(_a0 uint64, _a1 error) *mockClient_NonceAt_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *Client_NonceAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) (uint64, error)) *Client_NonceAt_Call { +func (_c *mockClient_NonceAt_Call) RunAndReturn(run func(context.Context, common.Address, *big.Int) (uint64, error)) *mockClient_NonceAt_Call { _c.Call.Return(run) return _c } // PendingNonceAt provides a mock function with given fields: _a0, _a1 -func (_m *Client) PendingNonceAt(_a0 context.Context, _a1 common.Address) (uint64, error) { +func (_m *mockClient) PendingNonceAt(_a0 context.Context, _a1 common.Address) (uint64, error) { ret := _m.Called(_a0, _a1) if len(ret) == 0 { @@ -112,37 +112,37 @@ func (_m *Client) PendingNonceAt(_a0 context.Context, _a1 common.Address) (uint6 return r0, r1 } -// Client_PendingNonceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingNonceAt' -type Client_PendingNonceAt_Call struct { +// mockClient_PendingNonceAt_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PendingNonceAt' +type mockClient_PendingNonceAt_Call struct { *mock.Call } // PendingNonceAt is a helper method to define mock.On call // - _a0 context.Context // - _a1 common.Address -func (_e *Client_Expecter) PendingNonceAt(_a0 interface{}, _a1 interface{}) *Client_PendingNonceAt_Call { - return &Client_PendingNonceAt_Call{Call: _e.mock.On("PendingNonceAt", _a0, _a1)} +func (_e *mockClient_Expecter) PendingNonceAt(_a0 interface{}, _a1 interface{}) *mockClient_PendingNonceAt_Call { + return &mockClient_PendingNonceAt_Call{Call: _e.mock.On("PendingNonceAt", _a0, _a1)} } -func (_c *Client_PendingNonceAt_Call) Run(run func(_a0 context.Context, _a1 common.Address)) *Client_PendingNonceAt_Call { +func (_c *mockClient_PendingNonceAt_Call) Run(run func(_a0 context.Context, _a1 common.Address)) *mockClient_PendingNonceAt_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(common.Address)) }) return _c } -func (_c *Client_PendingNonceAt_Call) Return(_a0 uint64, _a1 error) *Client_PendingNonceAt_Call { +func (_c *mockClient_PendingNonceAt_Call) Return(_a0 uint64, _a1 error) *mockClient_PendingNonceAt_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *Client_PendingNonceAt_Call) RunAndReturn(run func(context.Context, common.Address) (uint64, error)) *Client_PendingNonceAt_Call { +func (_c *mockClient_PendingNonceAt_Call) RunAndReturn(run func(context.Context, common.Address) (uint64, error)) *mockClient_PendingNonceAt_Call { _c.Call.Return(run) return _c } // SendTransaction provides a mock function with given fields: ctx, tx, attempt -func (_m *Client) SendTransaction(ctx context.Context, tx *types.Transaction, attempt *types.Attempt) error { +func (_m *mockClient) SendTransaction(ctx context.Context, tx *types.Transaction, attempt *types.Attempt) error { ret := _m.Called(ctx, tx, attempt) if len(ret) == 0 { @@ -159,8 +159,8 @@ func (_m *Client) SendTransaction(ctx context.Context, tx *types.Transaction, at return r0 } -// Client_SendTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendTransaction' -type Client_SendTransaction_Call struct { +// mockClient_SendTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendTransaction' +type mockClient_SendTransaction_Call struct { *mock.Call } @@ -168,34 +168,34 @@ type Client_SendTransaction_Call struct { // - ctx context.Context // - tx *types.Transaction // - attempt *types.Attempt -func (_e *Client_Expecter) SendTransaction(ctx interface{}, tx interface{}, attempt interface{}) *Client_SendTransaction_Call { - return &Client_SendTransaction_Call{Call: _e.mock.On("SendTransaction", ctx, tx, attempt)} +func (_e *mockClient_Expecter) SendTransaction(ctx interface{}, tx interface{}, attempt interface{}) *mockClient_SendTransaction_Call { + return &mockClient_SendTransaction_Call{Call: _e.mock.On("SendTransaction", ctx, tx, attempt)} } -func (_c *Client_SendTransaction_Call) Run(run func(ctx context.Context, tx *types.Transaction, attempt *types.Attempt)) *Client_SendTransaction_Call { +func (_c *mockClient_SendTransaction_Call) Run(run func(ctx context.Context, tx *types.Transaction, attempt *types.Attempt)) *mockClient_SendTransaction_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(*types.Transaction), args[2].(*types.Attempt)) }) return _c } -func (_c *Client_SendTransaction_Call) Return(_a0 error) *Client_SendTransaction_Call { +func (_c *mockClient_SendTransaction_Call) Return(_a0 error) *mockClient_SendTransaction_Call { _c.Call.Return(_a0) return _c } -func (_c *Client_SendTransaction_Call) RunAndReturn(run func(context.Context, *types.Transaction, *types.Attempt) error) *Client_SendTransaction_Call { +func (_c *mockClient_SendTransaction_Call) RunAndReturn(run func(context.Context, *types.Transaction, *types.Attempt) error) *mockClient_SendTransaction_Call { _c.Call.Return(run) return _c } -// NewClient creates a new instance of Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// newMockClient creates a new instance of mockClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func NewClient(t interface { +func newMockClient(t interface { mock.TestingT Cleanup(func()) -}) *Client { - mock := &Client{} +}) *mockClient { + mock := &mockClient{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/core/chains/evm/txm/mock_keystore_test.go b/core/chains/evm/txm/mock_keystore_test.go new file mode 100644 index 00000000000..f30e17e5024 --- /dev/null +++ b/core/chains/evm/txm/mock_keystore_test.go @@ -0,0 +1,98 @@ +// Code generated by mockery v2.50.0. DO NOT EDIT. + +package txm + +import ( + context "context" + big "math/big" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" +) + +// mockKeystore is an autogenerated mock type for the Keystore type +type mockKeystore struct { + mock.Mock +} + +type mockKeystore_Expecter struct { + mock *mock.Mock +} + +func (_m *mockKeystore) EXPECT() *mockKeystore_Expecter { + return &mockKeystore_Expecter{mock: &_m.Mock} +} + +// EnabledAddressesForChain provides a mock function with given fields: ctx, chainID +func (_m *mockKeystore) EnabledAddressesForChain(ctx context.Context, chainID *big.Int) ([]common.Address, error) { + ret := _m.Called(ctx, chainID) + + if len(ret) == 0 { + panic("no return value specified for EnabledAddressesForChain") + } + + var r0 []common.Address + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) ([]common.Address, error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) []common.Address); ok { + r0 = rf(ctx, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]common.Address) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockKeystore_EnabledAddressesForChain_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnabledAddressesForChain' +type mockKeystore_EnabledAddressesForChain_Call struct { + *mock.Call +} + +// EnabledAddressesForChain is a helper method to define mock.On call +// - ctx context.Context +// - chainID *big.Int +func (_e *mockKeystore_Expecter) EnabledAddressesForChain(ctx interface{}, chainID interface{}) *mockKeystore_EnabledAddressesForChain_Call { + return &mockKeystore_EnabledAddressesForChain_Call{Call: _e.mock.On("EnabledAddressesForChain", ctx, chainID)} +} + +func (_c *mockKeystore_EnabledAddressesForChain_Call) Run(run func(ctx context.Context, chainID *big.Int)) *mockKeystore_EnabledAddressesForChain_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*big.Int)) + }) + return _c +} + +func (_c *mockKeystore_EnabledAddressesForChain_Call) Return(addresses []common.Address, err error) *mockKeystore_EnabledAddressesForChain_Call { + _c.Call.Return(addresses, err) + return _c +} + +func (_c *mockKeystore_EnabledAddressesForChain_Call) RunAndReturn(run func(context.Context, *big.Int) ([]common.Address, error)) *mockKeystore_EnabledAddressesForChain_Call { + _c.Call.Return(run) + return _c +} + +// newMockKeystore creates a new instance of mockKeystore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockKeystore(t interface { + mock.TestingT + Cleanup(func()) +}) *mockKeystore { + mock := &mockKeystore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/chains/evm/txm/mock_tx_store_test.go b/core/chains/evm/txm/mock_tx_store_test.go new file mode 100644 index 00000000000..18dd11a4196 --- /dev/null +++ b/core/chains/evm/txm/mock_tx_store_test.go @@ -0,0 +1,647 @@ +// Code generated by mockery v2.50.0. DO NOT EDIT. + +package txm + +import ( + context "context" + + common "github.com/ethereum/go-ethereum/common" + + mock "github.com/stretchr/testify/mock" + + types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txm/types" +) + +// mockTxStore is an autogenerated mock type for the TxStore type +type mockTxStore struct { + mock.Mock +} + +type mockTxStore_Expecter struct { + mock *mock.Mock +} + +func (_m *mockTxStore) EXPECT() *mockTxStore_Expecter { + return &mockTxStore_Expecter{mock: &_m.Mock} +} + +// AbandonPendingTransactions provides a mock function with given fields: _a0, _a1 +func (_m *mockTxStore) AbandonPendingTransactions(_a0 context.Context, _a1 common.Address) error { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for AbandonPendingTransactions") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockTxStore_AbandonPendingTransactions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AbandonPendingTransactions' +type mockTxStore_AbandonPendingTransactions_Call struct { + *mock.Call +} + +// AbandonPendingTransactions is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 common.Address +func (_e *mockTxStore_Expecter) AbandonPendingTransactions(_a0 interface{}, _a1 interface{}) *mockTxStore_AbandonPendingTransactions_Call { + return &mockTxStore_AbandonPendingTransactions_Call{Call: _e.mock.On("AbandonPendingTransactions", _a0, _a1)} +} + +func (_c *mockTxStore_AbandonPendingTransactions_Call) Run(run func(_a0 context.Context, _a1 common.Address)) *mockTxStore_AbandonPendingTransactions_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address)) + }) + return _c +} + +func (_c *mockTxStore_AbandonPendingTransactions_Call) Return(_a0 error) *mockTxStore_AbandonPendingTransactions_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockTxStore_AbandonPendingTransactions_Call) RunAndReturn(run func(context.Context, common.Address) error) *mockTxStore_AbandonPendingTransactions_Call { + _c.Call.Return(run) + return _c +} + +// AppendAttemptToTransaction provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *mockTxStore) AppendAttemptToTransaction(_a0 context.Context, _a1 uint64, _a2 common.Address, _a3 *types.Attempt) error { + ret := _m.Called(_a0, _a1, _a2, _a3) + + if len(ret) == 0 { + panic("no return value specified for AppendAttemptToTransaction") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address, *types.Attempt) error); ok { + r0 = rf(_a0, _a1, _a2, _a3) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockTxStore_AppendAttemptToTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AppendAttemptToTransaction' +type mockTxStore_AppendAttemptToTransaction_Call struct { + *mock.Call +} + +// AppendAttemptToTransaction is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 uint64 +// - _a2 common.Address +// - _a3 *types.Attempt +func (_e *mockTxStore_Expecter) AppendAttemptToTransaction(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *mockTxStore_AppendAttemptToTransaction_Call { + return &mockTxStore_AppendAttemptToTransaction_Call{Call: _e.mock.On("AppendAttemptToTransaction", _a0, _a1, _a2, _a3)} +} + +func (_c *mockTxStore_AppendAttemptToTransaction_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 common.Address, _a3 *types.Attempt)) *mockTxStore_AppendAttemptToTransaction_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(common.Address), args[3].(*types.Attempt)) + }) + return _c +} + +func (_c *mockTxStore_AppendAttemptToTransaction_Call) Return(_a0 error) *mockTxStore_AppendAttemptToTransaction_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockTxStore_AppendAttemptToTransaction_Call) RunAndReturn(run func(context.Context, uint64, common.Address, *types.Attempt) error) *mockTxStore_AppendAttemptToTransaction_Call { + _c.Call.Return(run) + return _c +} + +// CreateEmptyUnconfirmedTransaction provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *mockTxStore) CreateEmptyUnconfirmedTransaction(_a0 context.Context, _a1 common.Address, _a2 uint64, _a3 uint64) (*types.Transaction, error) { + ret := _m.Called(_a0, _a1, _a2, _a3) + + if len(ret) == 0 { + panic("no return value specified for CreateEmptyUnconfirmedTransaction") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, uint64) (*types.Transaction, error)); ok { + return rf(_a0, _a1, _a2, _a3) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, uint64) *types.Transaction); ok { + r0 = rf(_a0, _a1, _a2, _a3) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint64, uint64) error); ok { + r1 = rf(_a0, _a1, _a2, _a3) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockTxStore_CreateEmptyUnconfirmedTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateEmptyUnconfirmedTransaction' +type mockTxStore_CreateEmptyUnconfirmedTransaction_Call struct { + *mock.Call +} + +// CreateEmptyUnconfirmedTransaction is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 common.Address +// - _a2 uint64 +// - _a3 uint64 +func (_e *mockTxStore_Expecter) CreateEmptyUnconfirmedTransaction(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *mockTxStore_CreateEmptyUnconfirmedTransaction_Call { + return &mockTxStore_CreateEmptyUnconfirmedTransaction_Call{Call: _e.mock.On("CreateEmptyUnconfirmedTransaction", _a0, _a1, _a2, _a3)} +} + +func (_c *mockTxStore_CreateEmptyUnconfirmedTransaction_Call) Run(run func(_a0 context.Context, _a1 common.Address, _a2 uint64, _a3 uint64)) *mockTxStore_CreateEmptyUnconfirmedTransaction_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].(uint64), args[3].(uint64)) + }) + return _c +} + +func (_c *mockTxStore_CreateEmptyUnconfirmedTransaction_Call) Return(_a0 *types.Transaction, _a1 error) *mockTxStore_CreateEmptyUnconfirmedTransaction_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockTxStore_CreateEmptyUnconfirmedTransaction_Call) RunAndReturn(run func(context.Context, common.Address, uint64, uint64) (*types.Transaction, error)) *mockTxStore_CreateEmptyUnconfirmedTransaction_Call { + _c.Call.Return(run) + return _c +} + +// CreateTransaction provides a mock function with given fields: _a0, _a1 +func (_m *mockTxStore) CreateTransaction(_a0 context.Context, _a1 *types.TxRequest) (*types.Transaction, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for CreateTransaction") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.TxRequest) (*types.Transaction, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.TxRequest) *types.Transaction); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.TxRequest) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockTxStore_CreateTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateTransaction' +type mockTxStore_CreateTransaction_Call struct { + *mock.Call +} + +// CreateTransaction is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *types.TxRequest +func (_e *mockTxStore_Expecter) CreateTransaction(_a0 interface{}, _a1 interface{}) *mockTxStore_CreateTransaction_Call { + return &mockTxStore_CreateTransaction_Call{Call: _e.mock.On("CreateTransaction", _a0, _a1)} +} + +func (_c *mockTxStore_CreateTransaction_Call) Run(run func(_a0 context.Context, _a1 *types.TxRequest)) *mockTxStore_CreateTransaction_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.TxRequest)) + }) + return _c +} + +func (_c *mockTxStore_CreateTransaction_Call) Return(_a0 *types.Transaction, _a1 error) *mockTxStore_CreateTransaction_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockTxStore_CreateTransaction_Call) RunAndReturn(run func(context.Context, *types.TxRequest) (*types.Transaction, error)) *mockTxStore_CreateTransaction_Call { + _c.Call.Return(run) + return _c +} + +// DeleteAttemptForUnconfirmedTx provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *mockTxStore) DeleteAttemptForUnconfirmedTx(_a0 context.Context, _a1 uint64, _a2 *types.Attempt, _a3 common.Address) error { + ret := _m.Called(_a0, _a1, _a2, _a3) + + if len(ret) == 0 { + panic("no return value specified for DeleteAttemptForUnconfirmedTx") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, *types.Attempt, common.Address) error); ok { + r0 = rf(_a0, _a1, _a2, _a3) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockTxStore_DeleteAttemptForUnconfirmedTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteAttemptForUnconfirmedTx' +type mockTxStore_DeleteAttemptForUnconfirmedTx_Call struct { + *mock.Call +} + +// DeleteAttemptForUnconfirmedTx is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 uint64 +// - _a2 *types.Attempt +// - _a3 common.Address +func (_e *mockTxStore_Expecter) DeleteAttemptForUnconfirmedTx(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *mockTxStore_DeleteAttemptForUnconfirmedTx_Call { + return &mockTxStore_DeleteAttemptForUnconfirmedTx_Call{Call: _e.mock.On("DeleteAttemptForUnconfirmedTx", _a0, _a1, _a2, _a3)} +} + +func (_c *mockTxStore_DeleteAttemptForUnconfirmedTx_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 *types.Attempt, _a3 common.Address)) *mockTxStore_DeleteAttemptForUnconfirmedTx_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(*types.Attempt), args[3].(common.Address)) + }) + return _c +} + +func (_c *mockTxStore_DeleteAttemptForUnconfirmedTx_Call) Return(_a0 error) *mockTxStore_DeleteAttemptForUnconfirmedTx_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockTxStore_DeleteAttemptForUnconfirmedTx_Call) RunAndReturn(run func(context.Context, uint64, *types.Attempt, common.Address) error) *mockTxStore_DeleteAttemptForUnconfirmedTx_Call { + _c.Call.Return(run) + return _c +} + +// FetchUnconfirmedTransactionAtNonceWithCount provides a mock function with given fields: _a0, _a1, _a2 +func (_m *mockTxStore) FetchUnconfirmedTransactionAtNonceWithCount(_a0 context.Context, _a1 uint64, _a2 common.Address) (*types.Transaction, int, error) { + ret := _m.Called(_a0, _a1, _a2) + + if len(ret) == 0 { + panic("no return value specified for FetchUnconfirmedTransactionAtNonceWithCount") + } + + var r0 *types.Transaction + var r1 int + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address) (*types.Transaction, int, error)); ok { + return rf(_a0, _a1, _a2) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address) *types.Transaction); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, common.Address) int); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Get(1).(int) + } + + if rf, ok := ret.Get(2).(func(context.Context, uint64, common.Address) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// mockTxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FetchUnconfirmedTransactionAtNonceWithCount' +type mockTxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call struct { + *mock.Call +} + +// FetchUnconfirmedTransactionAtNonceWithCount is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 uint64 +// - _a2 common.Address +func (_e *mockTxStore_Expecter) FetchUnconfirmedTransactionAtNonceWithCount(_a0 interface{}, _a1 interface{}, _a2 interface{}) *mockTxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call { + return &mockTxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call{Call: _e.mock.On("FetchUnconfirmedTransactionAtNonceWithCount", _a0, _a1, _a2)} +} + +func (_c *mockTxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 common.Address)) *mockTxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(common.Address)) + }) + return _c +} + +func (_c *mockTxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call) Return(_a0 *types.Transaction, _a1 int, _a2 error) *mockTxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *mockTxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call) RunAndReturn(run func(context.Context, uint64, common.Address) (*types.Transaction, int, error)) *mockTxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call { + _c.Call.Return(run) + return _c +} + +// MarkConfirmedAndReorgedTransactions provides a mock function with given fields: _a0, _a1, _a2 +func (_m *mockTxStore) MarkConfirmedAndReorgedTransactions(_a0 context.Context, _a1 uint64, _a2 common.Address) ([]*types.Transaction, []uint64, error) { + ret := _m.Called(_a0, _a1, _a2) + + if len(ret) == 0 { + panic("no return value specified for MarkConfirmedAndReorgedTransactions") + } + + var r0 []*types.Transaction + var r1 []uint64 + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address) ([]*types.Transaction, []uint64, error)); ok { + return rf(_a0, _a1, _a2) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address) []*types.Transaction); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64, common.Address) []uint64); ok { + r1 = rf(_a0, _a1, _a2) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]uint64) + } + } + + if rf, ok := ret.Get(2).(func(context.Context, uint64, common.Address) error); ok { + r2 = rf(_a0, _a1, _a2) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// mockTxStore_MarkConfirmedAndReorgedTransactions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarkConfirmedAndReorgedTransactions' +type mockTxStore_MarkConfirmedAndReorgedTransactions_Call struct { + *mock.Call +} + +// MarkConfirmedAndReorgedTransactions is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 uint64 +// - _a2 common.Address +func (_e *mockTxStore_Expecter) MarkConfirmedAndReorgedTransactions(_a0 interface{}, _a1 interface{}, _a2 interface{}) *mockTxStore_MarkConfirmedAndReorgedTransactions_Call { + return &mockTxStore_MarkConfirmedAndReorgedTransactions_Call{Call: _e.mock.On("MarkConfirmedAndReorgedTransactions", _a0, _a1, _a2)} +} + +func (_c *mockTxStore_MarkConfirmedAndReorgedTransactions_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 common.Address)) *mockTxStore_MarkConfirmedAndReorgedTransactions_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(common.Address)) + }) + return _c +} + +func (_c *mockTxStore_MarkConfirmedAndReorgedTransactions_Call) Return(_a0 []*types.Transaction, _a1 []uint64, _a2 error) *mockTxStore_MarkConfirmedAndReorgedTransactions_Call { + _c.Call.Return(_a0, _a1, _a2) + return _c +} + +func (_c *mockTxStore_MarkConfirmedAndReorgedTransactions_Call) RunAndReturn(run func(context.Context, uint64, common.Address) ([]*types.Transaction, []uint64, error)) *mockTxStore_MarkConfirmedAndReorgedTransactions_Call { + _c.Call.Return(run) + return _c +} + +// MarkTxFatal provides a mock function with given fields: _a0, _a1, _a2 +func (_m *mockTxStore) MarkTxFatal(_a0 context.Context, _a1 *types.Transaction, _a2 common.Address) error { + ret := _m.Called(_a0, _a1, _a2) + + if len(ret) == 0 { + panic("no return value specified for MarkTxFatal") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) error); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockTxStore_MarkTxFatal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarkTxFatal' +type mockTxStore_MarkTxFatal_Call struct { + *mock.Call +} + +// MarkTxFatal is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *types.Transaction +// - _a2 common.Address +func (_e *mockTxStore_Expecter) MarkTxFatal(_a0 interface{}, _a1 interface{}, _a2 interface{}) *mockTxStore_MarkTxFatal_Call { + return &mockTxStore_MarkTxFatal_Call{Call: _e.mock.On("MarkTxFatal", _a0, _a1, _a2)} +} + +func (_c *mockTxStore_MarkTxFatal_Call) Run(run func(_a0 context.Context, _a1 *types.Transaction, _a2 common.Address)) *mockTxStore_MarkTxFatal_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.Transaction), args[2].(common.Address)) + }) + return _c +} + +func (_c *mockTxStore_MarkTxFatal_Call) Return(_a0 error) *mockTxStore_MarkTxFatal_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockTxStore_MarkTxFatal_Call) RunAndReturn(run func(context.Context, *types.Transaction, common.Address) error) *mockTxStore_MarkTxFatal_Call { + _c.Call.Return(run) + return _c +} + +// MarkUnconfirmedTransactionPurgeable provides a mock function with given fields: _a0, _a1, _a2 +func (_m *mockTxStore) MarkUnconfirmedTransactionPurgeable(_a0 context.Context, _a1 uint64, _a2 common.Address) error { + ret := _m.Called(_a0, _a1, _a2) + + if len(ret) == 0 { + panic("no return value specified for MarkUnconfirmedTransactionPurgeable") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address) error); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockTxStore_MarkUnconfirmedTransactionPurgeable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarkUnconfirmedTransactionPurgeable' +type mockTxStore_MarkUnconfirmedTransactionPurgeable_Call struct { + *mock.Call +} + +// MarkUnconfirmedTransactionPurgeable is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 uint64 +// - _a2 common.Address +func (_e *mockTxStore_Expecter) MarkUnconfirmedTransactionPurgeable(_a0 interface{}, _a1 interface{}, _a2 interface{}) *mockTxStore_MarkUnconfirmedTransactionPurgeable_Call { + return &mockTxStore_MarkUnconfirmedTransactionPurgeable_Call{Call: _e.mock.On("MarkUnconfirmedTransactionPurgeable", _a0, _a1, _a2)} +} + +func (_c *mockTxStore_MarkUnconfirmedTransactionPurgeable_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 common.Address)) *mockTxStore_MarkUnconfirmedTransactionPurgeable_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(common.Address)) + }) + return _c +} + +func (_c *mockTxStore_MarkUnconfirmedTransactionPurgeable_Call) Return(_a0 error) *mockTxStore_MarkUnconfirmedTransactionPurgeable_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockTxStore_MarkUnconfirmedTransactionPurgeable_Call) RunAndReturn(run func(context.Context, uint64, common.Address) error) *mockTxStore_MarkUnconfirmedTransactionPurgeable_Call { + _c.Call.Return(run) + return _c +} + +// UpdateTransactionBroadcast provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4 +func (_m *mockTxStore) UpdateTransactionBroadcast(_a0 context.Context, _a1 uint64, _a2 uint64, _a3 common.Hash, _a4 common.Address) error { + ret := _m.Called(_a0, _a1, _a2, _a3, _a4) + + if len(ret) == 0 { + panic("no return value specified for UpdateTransactionBroadcast") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, common.Hash, common.Address) error); ok { + r0 = rf(_a0, _a1, _a2, _a3, _a4) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockTxStore_UpdateTransactionBroadcast_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateTransactionBroadcast' +type mockTxStore_UpdateTransactionBroadcast_Call struct { + *mock.Call +} + +// UpdateTransactionBroadcast is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 uint64 +// - _a2 uint64 +// - _a3 common.Hash +// - _a4 common.Address +func (_e *mockTxStore_Expecter) UpdateTransactionBroadcast(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}, _a4 interface{}) *mockTxStore_UpdateTransactionBroadcast_Call { + return &mockTxStore_UpdateTransactionBroadcast_Call{Call: _e.mock.On("UpdateTransactionBroadcast", _a0, _a1, _a2, _a3, _a4)} +} + +func (_c *mockTxStore_UpdateTransactionBroadcast_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 uint64, _a3 common.Hash, _a4 common.Address)) *mockTxStore_UpdateTransactionBroadcast_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64), args[2].(uint64), args[3].(common.Hash), args[4].(common.Address)) + }) + return _c +} + +func (_c *mockTxStore_UpdateTransactionBroadcast_Call) Return(_a0 error) *mockTxStore_UpdateTransactionBroadcast_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockTxStore_UpdateTransactionBroadcast_Call) RunAndReturn(run func(context.Context, uint64, uint64, common.Hash, common.Address) error) *mockTxStore_UpdateTransactionBroadcast_Call { + _c.Call.Return(run) + return _c +} + +// UpdateUnstartedTransactionWithNonce provides a mock function with given fields: _a0, _a1, _a2 +func (_m *mockTxStore) UpdateUnstartedTransactionWithNonce(_a0 context.Context, _a1 common.Address, _a2 uint64) (*types.Transaction, error) { + ret := _m.Called(_a0, _a1, _a2) + + if len(ret) == 0 { + panic("no return value specified for UpdateUnstartedTransactionWithNonce") + } + + var r0 *types.Transaction + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64) (*types.Transaction, error)); ok { + return rf(_a0, _a1, _a2) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64) *types.Transaction); ok { + r0 = rf(_a0, _a1, _a2) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint64) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// mockTxStore_UpdateUnstartedTransactionWithNonce_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUnstartedTransactionWithNonce' +type mockTxStore_UpdateUnstartedTransactionWithNonce_Call struct { + *mock.Call +} + +// UpdateUnstartedTransactionWithNonce is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 common.Address +// - _a2 uint64 +func (_e *mockTxStore_Expecter) UpdateUnstartedTransactionWithNonce(_a0 interface{}, _a1 interface{}, _a2 interface{}) *mockTxStore_UpdateUnstartedTransactionWithNonce_Call { + return &mockTxStore_UpdateUnstartedTransactionWithNonce_Call{Call: _e.mock.On("UpdateUnstartedTransactionWithNonce", _a0, _a1, _a2)} +} + +func (_c *mockTxStore_UpdateUnstartedTransactionWithNonce_Call) Run(run func(_a0 context.Context, _a1 common.Address, _a2 uint64)) *mockTxStore_UpdateUnstartedTransactionWithNonce_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(common.Address), args[2].(uint64)) + }) + return _c +} + +func (_c *mockTxStore_UpdateUnstartedTransactionWithNonce_Call) Return(_a0 *types.Transaction, _a1 error) *mockTxStore_UpdateUnstartedTransactionWithNonce_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *mockTxStore_UpdateUnstartedTransactionWithNonce_Call) RunAndReturn(run func(context.Context, common.Address, uint64) (*types.Transaction, error)) *mockTxStore_UpdateUnstartedTransactionWithNonce_Call { + _c.Call.Return(run) + return _c +} + +// newMockTxStore creates a new instance of mockTxStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockTxStore(t interface { + mock.TestingT + Cleanup(func()) +}) *mockTxStore { + mock := &mockTxStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/chains/evm/txm/mocks/keystore.go b/core/chains/evm/txm/mocks/keystore.go deleted file mode 100644 index 3d11a6fa549..00000000000 --- a/core/chains/evm/txm/mocks/keystore.go +++ /dev/null @@ -1,98 +0,0 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - big "math/big" - - common "github.com/ethereum/go-ethereum/common" - - mock "github.com/stretchr/testify/mock" -) - -// Keystore is an autogenerated mock type for the Keystore type -type Keystore struct { - mock.Mock -} - -type Keystore_Expecter struct { - mock *mock.Mock -} - -func (_m *Keystore) EXPECT() *Keystore_Expecter { - return &Keystore_Expecter{mock: &_m.Mock} -} - -// EnabledAddressesForChain provides a mock function with given fields: ctx, chainID -func (_m *Keystore) EnabledAddressesForChain(ctx context.Context, chainID *big.Int) ([]common.Address, error) { - ret := _m.Called(ctx, chainID) - - if len(ret) == 0 { - panic("no return value specified for EnabledAddressesForChain") - } - - var r0 []common.Address - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) ([]common.Address, error)); ok { - return rf(ctx, chainID) - } - if rf, ok := ret.Get(0).(func(context.Context, *big.Int) []common.Address); ok { - r0 = rf(ctx, chainID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]common.Address) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { - r1 = rf(ctx, chainID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Keystore_EnabledAddressesForChain_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnabledAddressesForChain' -type Keystore_EnabledAddressesForChain_Call struct { - *mock.Call -} - -// EnabledAddressesForChain is a helper method to define mock.On call -// - ctx context.Context -// - chainID *big.Int -func (_e *Keystore_Expecter) EnabledAddressesForChain(ctx interface{}, chainID interface{}) *Keystore_EnabledAddressesForChain_Call { - return &Keystore_EnabledAddressesForChain_Call{Call: _e.mock.On("EnabledAddressesForChain", ctx, chainID)} -} - -func (_c *Keystore_EnabledAddressesForChain_Call) Run(run func(ctx context.Context, chainID *big.Int)) *Keystore_EnabledAddressesForChain_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*big.Int)) - }) - return _c -} - -func (_c *Keystore_EnabledAddressesForChain_Call) Return(addresses []common.Address, err error) *Keystore_EnabledAddressesForChain_Call { - _c.Call.Return(addresses, err) - return _c -} - -func (_c *Keystore_EnabledAddressesForChain_Call) RunAndReturn(run func(context.Context, *big.Int) ([]common.Address, error)) *Keystore_EnabledAddressesForChain_Call { - _c.Call.Return(run) - return _c -} - -// NewKeystore creates a new instance of Keystore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewKeystore(t interface { - mock.TestingT - Cleanup(func()) -}) *Keystore { - mock := &Keystore{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/chains/evm/txm/mocks/tx_store.go b/core/chains/evm/txm/mocks/tx_store.go deleted file mode 100644 index 318b36942b8..00000000000 --- a/core/chains/evm/txm/mocks/tx_store.go +++ /dev/null @@ -1,647 +0,0 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - common "github.com/ethereum/go-ethereum/common" - - mock "github.com/stretchr/testify/mock" - - types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txm/types" -) - -// TxStore is an autogenerated mock type for the TxStore type -type TxStore struct { - mock.Mock -} - -type TxStore_Expecter struct { - mock *mock.Mock -} - -func (_m *TxStore) EXPECT() *TxStore_Expecter { - return &TxStore_Expecter{mock: &_m.Mock} -} - -// AbandonPendingTransactions provides a mock function with given fields: _a0, _a1 -func (_m *TxStore) AbandonPendingTransactions(_a0 context.Context, _a1 common.Address) error { - ret := _m.Called(_a0, _a1) - - if len(ret) == 0 { - panic("no return value specified for AbandonPendingTransactions") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address) error); ok { - r0 = rf(_a0, _a1) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// TxStore_AbandonPendingTransactions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AbandonPendingTransactions' -type TxStore_AbandonPendingTransactions_Call struct { - *mock.Call -} - -// AbandonPendingTransactions is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 common.Address -func (_e *TxStore_Expecter) AbandonPendingTransactions(_a0 interface{}, _a1 interface{}) *TxStore_AbandonPendingTransactions_Call { - return &TxStore_AbandonPendingTransactions_Call{Call: _e.mock.On("AbandonPendingTransactions", _a0, _a1)} -} - -func (_c *TxStore_AbandonPendingTransactions_Call) Run(run func(_a0 context.Context, _a1 common.Address)) *TxStore_AbandonPendingTransactions_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address)) - }) - return _c -} - -func (_c *TxStore_AbandonPendingTransactions_Call) Return(_a0 error) *TxStore_AbandonPendingTransactions_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *TxStore_AbandonPendingTransactions_Call) RunAndReturn(run func(context.Context, common.Address) error) *TxStore_AbandonPendingTransactions_Call { - _c.Call.Return(run) - return _c -} - -// AppendAttemptToTransaction provides a mock function with given fields: _a0, _a1, _a2, _a3 -func (_m *TxStore) AppendAttemptToTransaction(_a0 context.Context, _a1 uint64, _a2 common.Address, _a3 *types.Attempt) error { - ret := _m.Called(_a0, _a1, _a2, _a3) - - if len(ret) == 0 { - panic("no return value specified for AppendAttemptToTransaction") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address, *types.Attempt) error); ok { - r0 = rf(_a0, _a1, _a2, _a3) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// TxStore_AppendAttemptToTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AppendAttemptToTransaction' -type TxStore_AppendAttemptToTransaction_Call struct { - *mock.Call -} - -// AppendAttemptToTransaction is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 -// - _a2 common.Address -// - _a3 *types.Attempt -func (_e *TxStore_Expecter) AppendAttemptToTransaction(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *TxStore_AppendAttemptToTransaction_Call { - return &TxStore_AppendAttemptToTransaction_Call{Call: _e.mock.On("AppendAttemptToTransaction", _a0, _a1, _a2, _a3)} -} - -func (_c *TxStore_AppendAttemptToTransaction_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 common.Address, _a3 *types.Attempt)) *TxStore_AppendAttemptToTransaction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].(common.Address), args[3].(*types.Attempt)) - }) - return _c -} - -func (_c *TxStore_AppendAttemptToTransaction_Call) Return(_a0 error) *TxStore_AppendAttemptToTransaction_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *TxStore_AppendAttemptToTransaction_Call) RunAndReturn(run func(context.Context, uint64, common.Address, *types.Attempt) error) *TxStore_AppendAttemptToTransaction_Call { - _c.Call.Return(run) - return _c -} - -// CreateEmptyUnconfirmedTransaction provides a mock function with given fields: _a0, _a1, _a2, _a3 -func (_m *TxStore) CreateEmptyUnconfirmedTransaction(_a0 context.Context, _a1 common.Address, _a2 uint64, _a3 uint64) (*types.Transaction, error) { - ret := _m.Called(_a0, _a1, _a2, _a3) - - if len(ret) == 0 { - panic("no return value specified for CreateEmptyUnconfirmedTransaction") - } - - var r0 *types.Transaction - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, uint64) (*types.Transaction, error)); ok { - return rf(_a0, _a1, _a2, _a3) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64, uint64) *types.Transaction); ok { - r0 = rf(_a0, _a1, _a2, _a3) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint64, uint64) error); ok { - r1 = rf(_a0, _a1, _a2, _a3) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// TxStore_CreateEmptyUnconfirmedTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateEmptyUnconfirmedTransaction' -type TxStore_CreateEmptyUnconfirmedTransaction_Call struct { - *mock.Call -} - -// CreateEmptyUnconfirmedTransaction is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 common.Address -// - _a2 uint64 -// - _a3 uint64 -func (_e *TxStore_Expecter) CreateEmptyUnconfirmedTransaction(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *TxStore_CreateEmptyUnconfirmedTransaction_Call { - return &TxStore_CreateEmptyUnconfirmedTransaction_Call{Call: _e.mock.On("CreateEmptyUnconfirmedTransaction", _a0, _a1, _a2, _a3)} -} - -func (_c *TxStore_CreateEmptyUnconfirmedTransaction_Call) Run(run func(_a0 context.Context, _a1 common.Address, _a2 uint64, _a3 uint64)) *TxStore_CreateEmptyUnconfirmedTransaction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(uint64), args[3].(uint64)) - }) - return _c -} - -func (_c *TxStore_CreateEmptyUnconfirmedTransaction_Call) Return(_a0 *types.Transaction, _a1 error) *TxStore_CreateEmptyUnconfirmedTransaction_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *TxStore_CreateEmptyUnconfirmedTransaction_Call) RunAndReturn(run func(context.Context, common.Address, uint64, uint64) (*types.Transaction, error)) *TxStore_CreateEmptyUnconfirmedTransaction_Call { - _c.Call.Return(run) - return _c -} - -// CreateTransaction provides a mock function with given fields: _a0, _a1 -func (_m *TxStore) CreateTransaction(_a0 context.Context, _a1 *types.TxRequest) (*types.Transaction, error) { - ret := _m.Called(_a0, _a1) - - if len(ret) == 0 { - panic("no return value specified for CreateTransaction") - } - - var r0 *types.Transaction - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *types.TxRequest) (*types.Transaction, error)); ok { - return rf(_a0, _a1) - } - if rf, ok := ret.Get(0).(func(context.Context, *types.TxRequest) *types.Transaction); ok { - r0 = rf(_a0, _a1) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *types.TxRequest) error); ok { - r1 = rf(_a0, _a1) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// TxStore_CreateTransaction_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateTransaction' -type TxStore_CreateTransaction_Call struct { - *mock.Call -} - -// CreateTransaction is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *types.TxRequest -func (_e *TxStore_Expecter) CreateTransaction(_a0 interface{}, _a1 interface{}) *TxStore_CreateTransaction_Call { - return &TxStore_CreateTransaction_Call{Call: _e.mock.On("CreateTransaction", _a0, _a1)} -} - -func (_c *TxStore_CreateTransaction_Call) Run(run func(_a0 context.Context, _a1 *types.TxRequest)) *TxStore_CreateTransaction_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*types.TxRequest)) - }) - return _c -} - -func (_c *TxStore_CreateTransaction_Call) Return(_a0 *types.Transaction, _a1 error) *TxStore_CreateTransaction_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *TxStore_CreateTransaction_Call) RunAndReturn(run func(context.Context, *types.TxRequest) (*types.Transaction, error)) *TxStore_CreateTransaction_Call { - _c.Call.Return(run) - return _c -} - -// DeleteAttemptForUnconfirmedTx provides a mock function with given fields: _a0, _a1, _a2, _a3 -func (_m *TxStore) DeleteAttemptForUnconfirmedTx(_a0 context.Context, _a1 uint64, _a2 *types.Attempt, _a3 common.Address) error { - ret := _m.Called(_a0, _a1, _a2, _a3) - - if len(ret) == 0 { - panic("no return value specified for DeleteAttemptForUnconfirmedTx") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, *types.Attempt, common.Address) error); ok { - r0 = rf(_a0, _a1, _a2, _a3) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// TxStore_DeleteAttemptForUnconfirmedTx_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteAttemptForUnconfirmedTx' -type TxStore_DeleteAttemptForUnconfirmedTx_Call struct { - *mock.Call -} - -// DeleteAttemptForUnconfirmedTx is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 -// - _a2 *types.Attempt -// - _a3 common.Address -func (_e *TxStore_Expecter) DeleteAttemptForUnconfirmedTx(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}) *TxStore_DeleteAttemptForUnconfirmedTx_Call { - return &TxStore_DeleteAttemptForUnconfirmedTx_Call{Call: _e.mock.On("DeleteAttemptForUnconfirmedTx", _a0, _a1, _a2, _a3)} -} - -func (_c *TxStore_DeleteAttemptForUnconfirmedTx_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 *types.Attempt, _a3 common.Address)) *TxStore_DeleteAttemptForUnconfirmedTx_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].(*types.Attempt), args[3].(common.Address)) - }) - return _c -} - -func (_c *TxStore_DeleteAttemptForUnconfirmedTx_Call) Return(_a0 error) *TxStore_DeleteAttemptForUnconfirmedTx_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *TxStore_DeleteAttemptForUnconfirmedTx_Call) RunAndReturn(run func(context.Context, uint64, *types.Attempt, common.Address) error) *TxStore_DeleteAttemptForUnconfirmedTx_Call { - _c.Call.Return(run) - return _c -} - -// FetchUnconfirmedTransactionAtNonceWithCount provides a mock function with given fields: _a0, _a1, _a2 -func (_m *TxStore) FetchUnconfirmedTransactionAtNonceWithCount(_a0 context.Context, _a1 uint64, _a2 common.Address) (*types.Transaction, int, error) { - ret := _m.Called(_a0, _a1, _a2) - - if len(ret) == 0 { - panic("no return value specified for FetchUnconfirmedTransactionAtNonceWithCount") - } - - var r0 *types.Transaction - var r1 int - var r2 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address) (*types.Transaction, int, error)); ok { - return rf(_a0, _a1, _a2) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address) *types.Transaction); ok { - r0 = rf(_a0, _a1, _a2) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, common.Address) int); ok { - r1 = rf(_a0, _a1, _a2) - } else { - r1 = ret.Get(1).(int) - } - - if rf, ok := ret.Get(2).(func(context.Context, uint64, common.Address) error); ok { - r2 = rf(_a0, _a1, _a2) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// TxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FetchUnconfirmedTransactionAtNonceWithCount' -type TxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call struct { - *mock.Call -} - -// FetchUnconfirmedTransactionAtNonceWithCount is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 -// - _a2 common.Address -func (_e *TxStore_Expecter) FetchUnconfirmedTransactionAtNonceWithCount(_a0 interface{}, _a1 interface{}, _a2 interface{}) *TxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call { - return &TxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call{Call: _e.mock.On("FetchUnconfirmedTransactionAtNonceWithCount", _a0, _a1, _a2)} -} - -func (_c *TxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 common.Address)) *TxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].(common.Address)) - }) - return _c -} - -func (_c *TxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call) Return(_a0 *types.Transaction, _a1 int, _a2 error) *TxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *TxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call) RunAndReturn(run func(context.Context, uint64, common.Address) (*types.Transaction, int, error)) *TxStore_FetchUnconfirmedTransactionAtNonceWithCount_Call { - _c.Call.Return(run) - return _c -} - -// MarkConfirmedAndReorgedTransactions provides a mock function with given fields: _a0, _a1, _a2 -func (_m *TxStore) MarkConfirmedAndReorgedTransactions(_a0 context.Context, _a1 uint64, _a2 common.Address) ([]*types.Transaction, []uint64, error) { - ret := _m.Called(_a0, _a1, _a2) - - if len(ret) == 0 { - panic("no return value specified for MarkConfirmedAndReorgedTransactions") - } - - var r0 []*types.Transaction - var r1 []uint64 - var r2 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address) ([]*types.Transaction, []uint64, error)); ok { - return rf(_a0, _a1, _a2) - } - if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address) []*types.Transaction); ok { - r0 = rf(_a0, _a1, _a2) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]*types.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, uint64, common.Address) []uint64); ok { - r1 = rf(_a0, _a1, _a2) - } else { - if ret.Get(1) != nil { - r1 = ret.Get(1).([]uint64) - } - } - - if rf, ok := ret.Get(2).(func(context.Context, uint64, common.Address) error); ok { - r2 = rf(_a0, _a1, _a2) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 -} - -// TxStore_MarkConfirmedAndReorgedTransactions_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarkConfirmedAndReorgedTransactions' -type TxStore_MarkConfirmedAndReorgedTransactions_Call struct { - *mock.Call -} - -// MarkConfirmedAndReorgedTransactions is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 -// - _a2 common.Address -func (_e *TxStore_Expecter) MarkConfirmedAndReorgedTransactions(_a0 interface{}, _a1 interface{}, _a2 interface{}) *TxStore_MarkConfirmedAndReorgedTransactions_Call { - return &TxStore_MarkConfirmedAndReorgedTransactions_Call{Call: _e.mock.On("MarkConfirmedAndReorgedTransactions", _a0, _a1, _a2)} -} - -func (_c *TxStore_MarkConfirmedAndReorgedTransactions_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 common.Address)) *TxStore_MarkConfirmedAndReorgedTransactions_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].(common.Address)) - }) - return _c -} - -func (_c *TxStore_MarkConfirmedAndReorgedTransactions_Call) Return(_a0 []*types.Transaction, _a1 []uint64, _a2 error) *TxStore_MarkConfirmedAndReorgedTransactions_Call { - _c.Call.Return(_a0, _a1, _a2) - return _c -} - -func (_c *TxStore_MarkConfirmedAndReorgedTransactions_Call) RunAndReturn(run func(context.Context, uint64, common.Address) ([]*types.Transaction, []uint64, error)) *TxStore_MarkConfirmedAndReorgedTransactions_Call { - _c.Call.Return(run) - return _c -} - -// MarkTxFatal provides a mock function with given fields: _a0, _a1, _a2 -func (_m *TxStore) MarkTxFatal(_a0 context.Context, _a1 *types.Transaction, _a2 common.Address) error { - ret := _m.Called(_a0, _a1, _a2) - - if len(ret) == 0 { - panic("no return value specified for MarkTxFatal") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) error); ok { - r0 = rf(_a0, _a1, _a2) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// TxStore_MarkTxFatal_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarkTxFatal' -type TxStore_MarkTxFatal_Call struct { - *mock.Call -} - -// MarkTxFatal is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 *types.Transaction -// - _a2 common.Address -func (_e *TxStore_Expecter) MarkTxFatal(_a0 interface{}, _a1 interface{}, _a2 interface{}) *TxStore_MarkTxFatal_Call { - return &TxStore_MarkTxFatal_Call{Call: _e.mock.On("MarkTxFatal", _a0, _a1, _a2)} -} - -func (_c *TxStore_MarkTxFatal_Call) Run(run func(_a0 context.Context, _a1 *types.Transaction, _a2 common.Address)) *TxStore_MarkTxFatal_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(*types.Transaction), args[2].(common.Address)) - }) - return _c -} - -func (_c *TxStore_MarkTxFatal_Call) Return(_a0 error) *TxStore_MarkTxFatal_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *TxStore_MarkTxFatal_Call) RunAndReturn(run func(context.Context, *types.Transaction, common.Address) error) *TxStore_MarkTxFatal_Call { - _c.Call.Return(run) - return _c -} - -// MarkUnconfirmedTransactionPurgeable provides a mock function with given fields: _a0, _a1, _a2 -func (_m *TxStore) MarkUnconfirmedTransactionPurgeable(_a0 context.Context, _a1 uint64, _a2 common.Address) error { - ret := _m.Called(_a0, _a1, _a2) - - if len(ret) == 0 { - panic("no return value specified for MarkUnconfirmedTransactionPurgeable") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, common.Address) error); ok { - r0 = rf(_a0, _a1, _a2) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// TxStore_MarkUnconfirmedTransactionPurgeable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'MarkUnconfirmedTransactionPurgeable' -type TxStore_MarkUnconfirmedTransactionPurgeable_Call struct { - *mock.Call -} - -// MarkUnconfirmedTransactionPurgeable is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 -// - _a2 common.Address -func (_e *TxStore_Expecter) MarkUnconfirmedTransactionPurgeable(_a0 interface{}, _a1 interface{}, _a2 interface{}) *TxStore_MarkUnconfirmedTransactionPurgeable_Call { - return &TxStore_MarkUnconfirmedTransactionPurgeable_Call{Call: _e.mock.On("MarkUnconfirmedTransactionPurgeable", _a0, _a1, _a2)} -} - -func (_c *TxStore_MarkUnconfirmedTransactionPurgeable_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 common.Address)) *TxStore_MarkUnconfirmedTransactionPurgeable_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].(common.Address)) - }) - return _c -} - -func (_c *TxStore_MarkUnconfirmedTransactionPurgeable_Call) Return(_a0 error) *TxStore_MarkUnconfirmedTransactionPurgeable_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *TxStore_MarkUnconfirmedTransactionPurgeable_Call) RunAndReturn(run func(context.Context, uint64, common.Address) error) *TxStore_MarkUnconfirmedTransactionPurgeable_Call { - _c.Call.Return(run) - return _c -} - -// UpdateTransactionBroadcast provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4 -func (_m *TxStore) UpdateTransactionBroadcast(_a0 context.Context, _a1 uint64, _a2 uint64, _a3 common.Hash, _a4 common.Address) error { - ret := _m.Called(_a0, _a1, _a2, _a3, _a4) - - if len(ret) == 0 { - panic("no return value specified for UpdateTransactionBroadcast") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, uint64, uint64, common.Hash, common.Address) error); ok { - r0 = rf(_a0, _a1, _a2, _a3, _a4) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// TxStore_UpdateTransactionBroadcast_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateTransactionBroadcast' -type TxStore_UpdateTransactionBroadcast_Call struct { - *mock.Call -} - -// UpdateTransactionBroadcast is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 uint64 -// - _a2 uint64 -// - _a3 common.Hash -// - _a4 common.Address -func (_e *TxStore_Expecter) UpdateTransactionBroadcast(_a0 interface{}, _a1 interface{}, _a2 interface{}, _a3 interface{}, _a4 interface{}) *TxStore_UpdateTransactionBroadcast_Call { - return &TxStore_UpdateTransactionBroadcast_Call{Call: _e.mock.On("UpdateTransactionBroadcast", _a0, _a1, _a2, _a3, _a4)} -} - -func (_c *TxStore_UpdateTransactionBroadcast_Call) Run(run func(_a0 context.Context, _a1 uint64, _a2 uint64, _a3 common.Hash, _a4 common.Address)) *TxStore_UpdateTransactionBroadcast_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(uint64), args[2].(uint64), args[3].(common.Hash), args[4].(common.Address)) - }) - return _c -} - -func (_c *TxStore_UpdateTransactionBroadcast_Call) Return(_a0 error) *TxStore_UpdateTransactionBroadcast_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *TxStore_UpdateTransactionBroadcast_Call) RunAndReturn(run func(context.Context, uint64, uint64, common.Hash, common.Address) error) *TxStore_UpdateTransactionBroadcast_Call { - _c.Call.Return(run) - return _c -} - -// UpdateUnstartedTransactionWithNonce provides a mock function with given fields: _a0, _a1, _a2 -func (_m *TxStore) UpdateUnstartedTransactionWithNonce(_a0 context.Context, _a1 common.Address, _a2 uint64) (*types.Transaction, error) { - ret := _m.Called(_a0, _a1, _a2) - - if len(ret) == 0 { - panic("no return value specified for UpdateUnstartedTransactionWithNonce") - } - - var r0 *types.Transaction - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64) (*types.Transaction, error)); ok { - return rf(_a0, _a1, _a2) - } - if rf, ok := ret.Get(0).(func(context.Context, common.Address, uint64) *types.Transaction); ok { - r0 = rf(_a0, _a1, _a2) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*types.Transaction) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, common.Address, uint64) error); ok { - r1 = rf(_a0, _a1, _a2) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// TxStore_UpdateUnstartedTransactionWithNonce_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateUnstartedTransactionWithNonce' -type TxStore_UpdateUnstartedTransactionWithNonce_Call struct { - *mock.Call -} - -// UpdateUnstartedTransactionWithNonce is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 common.Address -// - _a2 uint64 -func (_e *TxStore_Expecter) UpdateUnstartedTransactionWithNonce(_a0 interface{}, _a1 interface{}, _a2 interface{}) *TxStore_UpdateUnstartedTransactionWithNonce_Call { - return &TxStore_UpdateUnstartedTransactionWithNonce_Call{Call: _e.mock.On("UpdateUnstartedTransactionWithNonce", _a0, _a1, _a2)} -} - -func (_c *TxStore_UpdateUnstartedTransactionWithNonce_Call) Run(run func(_a0 context.Context, _a1 common.Address, _a2 uint64)) *TxStore_UpdateUnstartedTransactionWithNonce_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(common.Address), args[2].(uint64)) - }) - return _c -} - -func (_c *TxStore_UpdateUnstartedTransactionWithNonce_Call) Return(_a0 *types.Transaction, _a1 error) *TxStore_UpdateUnstartedTransactionWithNonce_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *TxStore_UpdateUnstartedTransactionWithNonce_Call) RunAndReturn(run func(context.Context, common.Address, uint64) (*types.Transaction, error)) *TxStore_UpdateUnstartedTransactionWithNonce_Call { - _c.Call.Return(run) - return _c -} - -// NewTxStore creates a new instance of TxStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewTxStore(t interface { - mock.TestingT - Cleanup(func()) -}) *TxStore { - mock := &TxStore{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/chains/evm/txm/txm_test.go b/core/chains/evm/txm/txm_test.go index 7b5e5bdd702..13b987000ed 100644 --- a/core/chains/evm/txm/txm_test.go +++ b/core/chains/evm/txm/txm_test.go @@ -19,7 +19,6 @@ import ( "github.com/smartcontractkit/chainlink-integrations/evm/assets" "github.com/smartcontractkit/chainlink-integrations/evm/gas" "github.com/smartcontractkit/chainlink-integrations/evm/testutils" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txm/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txm/storage" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txm/types" ) @@ -27,13 +26,13 @@ import ( func TestLifecycle(t *testing.T) { t.Parallel() - client := mocks.NewClient(t) - ab := mocks.NewAttemptBuilder(t) + client := newMockClient(t) + ab := newMockAttemptBuilder(t) address1 := testutils.NewAddress() address2 := testutils.NewAddress() assert.NotEqual(t, address1, address2) addresses := []common.Address{address1, address2} - keystore := mocks.NewKeystore(t) + keystore := newMockKeystore(t) t.Run("retries if initial pending nonce call fails", func(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) @@ -73,7 +72,7 @@ func TestTrigger(t *testing.T) { t.Parallel() address := testutils.NewAddress() - keystore := mocks.NewKeystore(t) + keystore := newMockKeystore(t) t.Run("Trigger fails if Txm is unstarted", func(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) txm := NewTxm(lggr, nil, nil, nil, nil, nil, Config{}, keystore) @@ -85,8 +84,8 @@ func TestTrigger(t *testing.T) { lggr := logger.Test(t) txStore := storage.NewInMemoryStoreManager(lggr, testutils.FixtureChainID) require.NoError(t, txStore.Add(address)) - client := mocks.NewClient(t) - ab := mocks.NewAttemptBuilder(t) + client := newMockClient(t) + ab := newMockAttemptBuilder(t) config := Config{BlockTime: 1 * time.Minute, RetryBlockThreshold: 10} keystore.On("EnabledAddressesForChain", mock.Anything, mock.Anything).Return([]common.Address{address}, nil) txm := NewTxm(lggr, testutils.FixtureChainID, client, ab, txStore, nil, config, keystore) @@ -102,14 +101,14 @@ func TestBroadcastTransaction(t *testing.T) { t.Parallel() ctx := tests.Context(t) - client := mocks.NewClient(t) - ab := mocks.NewAttemptBuilder(t) + client := newMockClient(t) + ab := newMockAttemptBuilder(t) config := Config{} address := testutils.NewAddress() - keystore := mocks.NewKeystore(t) + keystore := newMockKeystore(t) t.Run("fails if FetchUnconfirmedTransactionAtNonceWithCount for unconfirmed transactions fails", func(t *testing.T) { - mTxStore := mocks.NewTxStore(t) + mTxStore := newMockTxStore(t) mTxStore.On("FetchUnconfirmedTransactionAtNonceWithCount", mock.Anything, mock.Anything, mock.Anything).Return(nil, 0, errors.New("call failed")).Once() txm := NewTxm(logger.Test(t), testutils.FixtureChainID, client, ab, mTxStore, nil, config, keystore) bo, err := txm.broadcastTransaction(ctx, address) @@ -120,7 +119,7 @@ func TestBroadcastTransaction(t *testing.T) { t.Run("throws a warning and returns if unconfirmed transactions exceed maxInFlightTransactions", func(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - mTxStore := mocks.NewTxStore(t) + mTxStore := newMockTxStore(t) mTxStore.On("FetchUnconfirmedTransactionAtNonceWithCount", mock.Anything, mock.Anything, mock.Anything).Return(nil, maxInFlightTransactions+1, nil).Once() txm := NewTxm(lggr, testutils.FixtureChainID, client, ab, mTxStore, nil, config, keystore) bo, err := txm.broadcastTransaction(ctx, address) @@ -131,7 +130,7 @@ func TestBroadcastTransaction(t *testing.T) { t.Run("checks pending nonce if unconfirmed transactions are equal or more than maxInFlightSubset", func(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) - mTxStore := mocks.NewTxStore(t) + mTxStore := newMockTxStore(t) txm := NewTxm(lggr, testutils.FixtureChainID, client, ab, mTxStore, nil, config, keystore) txm.setNonce(address, 1) mTxStore.On("FetchUnconfirmedTransactionAtNonceWithCount", mock.Anything, mock.Anything, mock.Anything).Return(nil, maxInFlightSubset, nil).Twice() @@ -150,7 +149,7 @@ func TestBroadcastTransaction(t *testing.T) { }) t.Run("fails if UpdateUnstartedTransactionWithNonce fails", func(t *testing.T) { - mTxStore := mocks.NewTxStore(t) + mTxStore := newMockTxStore(t) mTxStore.On("FetchUnconfirmedTransactionAtNonceWithCount", mock.Anything, mock.Anything, mock.Anything).Return(nil, 0, nil).Once() txm := NewTxm(logger.Test(t), testutils.FixtureChainID, client, ab, mTxStore, nil, config, keystore) mTxStore.On("UpdateUnstartedTransactionWithNonce", mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("call failed")).Once() @@ -217,12 +216,12 @@ func TestBackfillTransactions(t *testing.T) { t.Parallel() ctx := tests.Context(t) - client := mocks.NewClient(t) - ab := mocks.NewAttemptBuilder(t) - txStore := mocks.NewTxStore(t) + client := newMockClient(t) + ab := newMockAttemptBuilder(t) + txStore := newMockTxStore(t) config := Config{} address := testutils.NewAddress() - keystore := mocks.NewKeystore(t) + keystore := newMockKeystore(t) t.Run("fails if latest nonce fetching fails", func(t *testing.T) { txm := NewTxm(logger.Test(t), testutils.FixtureChainID, client, ab, txStore, nil, config, keystore) @@ -248,7 +247,7 @@ func TestBackfillTransactions(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) txStore := storage.NewInMemoryStoreManager(lggr, testutils.FixtureChainID) require.NoError(t, txStore.Add(address)) - ab := mocks.NewAttemptBuilder(t) + ab := newMockAttemptBuilder(t) c := Config{EIP1559: false, BlockTime: 10 * time.Minute, RetryBlockThreshold: 10, EmptyTxLimitDefault: 22000} txm := NewTxm(lggr, testutils.FixtureChainID, client, ab, txStore, nil, c, keystore) emptyMetrics, err := NewTxmMetrics(testutils.FixtureChainID) @@ -290,7 +289,7 @@ func TestBackfillTransactions(t *testing.T) { lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) txStore := storage.NewInMemoryStoreManager(lggr, testutils.FixtureChainID) require.NoError(t, txStore.Add(address)) - ab := mocks.NewAttemptBuilder(t) + ab := newMockAttemptBuilder(t) c := Config{EIP1559: false, BlockTime: 1 * time.Second, RetryBlockThreshold: 1, EmptyTxLimitDefault: 22000} txm := NewTxm(lggr, testutils.FixtureChainID, client, ab, txStore, nil, c, keystore) emptyMetrics, err := NewTxmMetrics(testutils.FixtureChainID) From 37d65f78487e702803a213b42dc330c24107a80a Mon Sep 17 00:00:00 2001 From: Erik Burton Date: Mon, 24 Feb 2025 16:35:34 -0800 Subject: [PATCH 14/14] chore: goreleaser workflow maintanence (#16551) --- .../goreleaser-build-sign-publish/action.yml | 16 ++++++---------- .github/workflows/build-publish-develop-pr.yml | 4 ++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/actions/goreleaser-build-sign-publish/action.yml b/.github/actions/goreleaser-build-sign-publish/action.yml index dcf21844e3d..0c7c05fe618 100644 --- a/.github/actions/goreleaser-build-sign-publish/action.yml +++ b/.github/actions/goreleaser-build-sign-publish/action.yml @@ -19,7 +19,7 @@ inputs: required: false # goreleaser inputs goreleaser-release-type: - description: The goreleaser release type, it can be either "nightly", "merge", "snapshot", "release" + description: The goreleaser release type, it can be either "nightly", "merge", "snapshot", "release" default: "snapshot" required: false goreleaser-config: @@ -33,22 +33,18 @@ inputs: buildkit debug information, and goreleaser debug, etc. required: false default: "${{ runner.debug == '1' }}" - + runs: using: composite steps: - - # We need QEMU to test the cross architecture builds after they're built. - name: Set up QEMU - uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 + - # We need QEMU to test the cross architecture builds after they're built. + name: Set up QEMU + uses: docker/setup-qemu-action@4574d27a4764455b42196d70a065bc6853246a25 # v3.4.0 - name: Setup docker buildx - uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.0 + uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3.9.0 with: buildkitd-flags: ${{ inputs.enable-debug == 'true' && '--debug' || '' }} - # v0.16.0 until grpc fix is released - # see: https://github.com/docker/buildx/issues/2789#issuecomment-2487981922 - driver-opts: | - image=moby/buildkit:v0.16.0 - name: Set up Go uses: ./.github/actions/setup-go diff --git a/.github/workflows/build-publish-develop-pr.yml b/.github/workflows/build-publish-develop-pr.yml index f870d12d900..9f918541673 100644 --- a/.github/workflows/build-publish-develop-pr.yml +++ b/.github/workflows/build-publish-develop-pr.yml @@ -23,6 +23,10 @@ on: required: false default: "false" +concurrency: + group: "build-publish-${{ github.ref_name }}-${{ github.sha }}" + cancel-in-progress: true + env: # Use github.sha here otherwise a race condition exists if # a commit is pushed to develop before merge is run.