Skip to content

Commit 69c1cdd

Browse files
authored
Add deploy mock forwarder changeset (#21740)
* Added deploy mock forwarder changeset * Lint
1 parent 588d4a6 commit 69c1cdd

3 files changed

Lines changed: 242 additions & 0 deletions

File tree

deployment/cre/contracts/contracts.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ var (
3535
RBACTimelock cldf.ContractType = "RBACTimelock" // no type and a version in contract https://github.com/smartcontractkit/ccip-owner-contracts/blob/main/src/RBACTimelock.sol
3636
ProposerManyChainMultiSig cldf.ContractType = "ProposerManyChainMultiSig" // no type and a version in contract https://github.com/smartcontractkit/ccip-owner-contracts/blob/main/src/ManyChainMultiSig.sol
3737
ShardConfig cldf.ContractType = "ShardConfig" // manages desired shard count configuration
38+
MockKeystoneForwarder cldf.ContractType = "MockKeystoneForwarder" // https://github.com/smartcontractkit/chainlink-evm/blob/f2272e4b4aa6a3e315126ce7d928472bb035f940/contracts/cre/src/dev/MockKeystoneForwarder.sol#L38
3839
)
3940

4041
type MCMSConfig = proposalutils.TimelockConfig
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package forwarder
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/Masterminds/semver/v3"
9+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
10+
"github.com/ethereum/go-ethereum/common"
11+
"golang.org/x/sync/errgroup"
12+
13+
chain_selectors "github.com/smartcontractkit/chain-selectors"
14+
15+
"github.com/smartcontractkit/chainlink-deployments-framework/chain/evm"
16+
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
17+
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
18+
"github.com/smartcontractkit/chainlink-deployments-framework/operations"
19+
mock_forwarder "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/mock_forwarder"
20+
)
21+
22+
var _ cldf.ChangeSetV2[DeployMockForwardersInput] = DeployMockForwarders{}
23+
24+
// DeployMockForwardersInput is the input for deploying MockKeystoneForwarder contracts.
25+
type DeployMockForwardersInput struct {
26+
Targets []uint64 `json:"targets" yaml:"targets"`
27+
Qualifier string `json:"qualifier" yaml:"qualifier"`
28+
}
29+
30+
// DeployMockForwarders is a ChangeSetV2 that deploys MockKeystoneForwarder contracts.
31+
type DeployMockForwarders struct{}
32+
33+
func (d DeployMockForwarders) VerifyPreconditions(env cldf.Environment, input DeployMockForwardersInput) error {
34+
if input.Qualifier == "" {
35+
return errors.New("qualifier is required")
36+
}
37+
for _, sel := range input.Targets {
38+
if _, err := chain_selectors.GetChainIDFromSelector(sel); err != nil {
39+
return fmt.Errorf("could not resolve chain selector %d: %w", sel, err)
40+
}
41+
if _, ok := env.BlockChains.EVMChains()[sel]; !ok {
42+
return fmt.Errorf("chain selector %d not found in environment", sel)
43+
}
44+
}
45+
return nil
46+
}
47+
48+
func (d DeployMockForwarders) Apply(env cldf.Environment, input DeployMockForwardersInput) (cldf.ChangesetOutput, error) {
49+
seqReport, err := operations.ExecuteSequence(
50+
env.OperationsBundle,
51+
DeployMockSequence,
52+
DeploySequenceDeps{Env: &env},
53+
DeploySequenceInput(input),
54+
)
55+
if err != nil {
56+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to execute deploy mock forwarders sequence: %w", err)
57+
}
58+
59+
ds := datastore.NewMemoryDataStore()
60+
addrs, err := seqReport.Output.Addresses.Fetch()
61+
if err != nil {
62+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to fetch addresses from sequence output: %w", err)
63+
}
64+
for _, addr := range addrs {
65+
if err := ds.Addresses().Add(addr); err != nil {
66+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to add address ref to mutable datastore: %w", err)
67+
}
68+
}
69+
70+
return cldf.ChangesetOutput{
71+
DataStore: ds,
72+
Reports: seqReport.ExecutionReports,
73+
}, nil
74+
}
75+
76+
type DeployMockForwarderSequenceOutput struct {
77+
Addresses datastore.AddressRefStore
78+
Datastore datastore.DataStore
79+
}
80+
81+
// DeployMockSequence deploys MockKeystoneForwarder contracts to multiple chains concurrently.
82+
var DeployMockSequence = operations.NewSequence(
83+
"deploy-mock-keystone-forwarders-seq",
84+
semver.MustParse("1.0.0"),
85+
"Deploy Mock Keystone Forwarders",
86+
func(b operations.Bundle, deps DeploySequenceDeps, input DeploySequenceInput) (DeployMockForwarderSequenceOutput, error) {
87+
as := datastore.NewMemoryDataStore()
88+
contractErrGroup := &errgroup.Group{}
89+
for _, target := range input.Targets {
90+
contractErrGroup.Go(func() error {
91+
r, err := operations.ExecuteOperation(b, DeployMockOp, DeployOpDeps(deps), DeployOpInput{
92+
ChainSelector: target,
93+
Qualifier: input.Qualifier,
94+
})
95+
if err != nil {
96+
return err
97+
}
98+
addrs, err := r.Output.Addresses.Fetch()
99+
if err != nil {
100+
return fmt.Errorf("failed to fetch MockKeystoneForwarder addresses for target %d: %w", target, err)
101+
}
102+
for _, addr := range addrs {
103+
if addrRefErr := as.AddressRefStore.Add(addr); addrRefErr != nil {
104+
return fmt.Errorf("failed to save MockKeystoneForwarder address on datastore for target %d: %w", target, addrRefErr)
105+
}
106+
}
107+
108+
return nil
109+
})
110+
}
111+
if err := contractErrGroup.Wait(); err != nil {
112+
return DeployMockForwarderSequenceOutput{Addresses: as.Addresses()}, fmt.Errorf("failed to deploy MockKeystoneForwarder contracts: %w", err)
113+
}
114+
return DeployMockForwarderSequenceOutput{Addresses: as.Addresses(), Datastore: as.Seal()}, nil
115+
},
116+
)
117+
118+
type DeployMockForwarderOpOutput struct {
119+
Addresses datastore.AddressRefStore
120+
AddressRef datastore.AddressRef // The address ref of the deployed Keystone Forwarder
121+
}
122+
123+
// DeployMockOp is an operation that deploys the MockKeystoneForwarder contract.
124+
var DeployMockOp = operations.NewOperation(
125+
"deploy-mock-keystone-forwarder-op",
126+
semver.MustParse("1.0.0"),
127+
"Deploy MockKeystoneForwarder Contract",
128+
func(b operations.Bundle, deps DeployOpDeps, input DeployOpInput) (DeployMockForwarderOpOutput, error) {
129+
chain, ok := deps.Env.BlockChains.EVMChains()[input.ChainSelector]
130+
if !ok {
131+
return DeployMockForwarderOpOutput{}, fmt.Errorf("deploy-mock-keystone-forwarder-op failed: chain selector %d not found in environment", input.ChainSelector)
132+
}
133+
addr, tv, err := deployMock(b.GetContext(), chain.DeployerKey, chain)
134+
if err != nil {
135+
return DeployMockForwarderOpOutput{}, fmt.Errorf("deploy-mock-keystone-forwarder-op failed: %w", err)
136+
}
137+
labels := tv.Labels.List()
138+
labels = append(labels, input.Labels...)
139+
r := datastore.AddressRef{
140+
ChainSelector: input.ChainSelector,
141+
Address: addr.String(),
142+
Type: datastore.ContractType(tv.Type),
143+
Version: &tv.Version,
144+
Qualifier: input.Qualifier,
145+
Labels: datastore.NewLabelSet(labels...),
146+
}
147+
ds := datastore.NewMemoryDataStore()
148+
if err := ds.AddressRefStore.Add(r); err != nil {
149+
return DeployMockForwarderOpOutput{}, fmt.Errorf("deploy-mock-keystone-forwarder-op failed: failed to add address ref to datastore: %w", err)
150+
}
151+
152+
return DeployMockForwarderOpOutput{
153+
Addresses: ds.Addresses(),
154+
AddressRef: r,
155+
}, nil
156+
},
157+
)
158+
159+
func deployMock(ctx context.Context, auth *bind.TransactOpts, chain evm.Chain) (*common.Address, *cldf.TypeAndVersion, error) {
160+
forwarderAddr, tx, mockForwarderContract, err := mock_forwarder.DeployMockKeystoneForwarder(
161+
auth,
162+
chain.Client)
163+
if err != nil {
164+
return nil, nil, fmt.Errorf("failed to deploy MockKeystoneForwarder: %w", err)
165+
}
166+
167+
_, err = chain.Confirm(tx)
168+
if err != nil {
169+
return nil, nil, fmt.Errorf("failed to confirm and save MockKeystoneForwarder: %w", err)
170+
}
171+
tvStr, err := mockForwarderContract.TypeAndVersion(&bind.CallOpts{})
172+
if err != nil {
173+
return nil, nil, fmt.Errorf("failed to get type and version: %w", err)
174+
}
175+
tv, err := cldf.TypeAndVersionFromString(tvStr)
176+
if err != nil {
177+
return nil, nil, fmt.Errorf("failed to parse type and version from %s: %w", tvStr, err)
178+
}
179+
txHash := tx.Hash()
180+
txReceipt, err := chain.Client.TransactionReceipt(ctx, tx.Hash())
181+
if err != nil {
182+
return nil, nil, fmt.Errorf("failed to get transaction receipt: %w", err)
183+
}
184+
hashLabel := fmt.Sprintf("%s: %s", DeploymentHashLabel, txHash.Hex())
185+
blockLabel := fmt.Sprintf("%s: %s", DeploymentBlockLabel, txReceipt.BlockNumber.String())
186+
tv.Labels.Add(blockLabel)
187+
tv.Labels.Add(hashLabel)
188+
189+
return &forwarderAddr, &tv, nil
190+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package forwarder_test
2+
3+
import (
4+
"testing"
5+
6+
chainsel "github.com/smartcontractkit/chain-selectors"
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
10+
"github.com/smartcontractkit/chainlink-deployments-framework/engine/test/environment"
11+
"github.com/smartcontractkit/chainlink-deployments-framework/engine/test/runtime"
12+
13+
"github.com/smartcontractkit/chainlink/deployment/cre/contracts"
14+
"github.com/smartcontractkit/chainlink/deployment/cre/forwarder"
15+
)
16+
17+
func TestDeployMockForwarder(t *testing.T) {
18+
t.Parallel()
19+
20+
registrySel := chainsel.TEST_90000001.Selector
21+
rt, err := runtime.New(t.Context(), runtime.WithEnvOpts(
22+
environment.WithEVMSimulated(t, []uint64{registrySel}),
23+
))
24+
require.NoError(t, err)
25+
26+
err = rt.Exec(
27+
runtime.ChangesetTask(forwarder.DeployMockForwarders{},
28+
forwarder.DeployMockForwardersInput{
29+
Targets: []uint64{registrySel},
30+
Qualifier: "my-test-mock-forwarder",
31+
},
32+
),
33+
)
34+
require.NoError(t, err)
35+
36+
addrs := rt.State().DataStore.Addresses().Filter(
37+
datastore.AddressRefByChainSelector(registrySel),
38+
)
39+
require.Len(t, addrs, 1)
40+
41+
mockAddrs := rt.State().DataStore.Addresses().Filter(
42+
datastore.AddressRefByType(datastore.ContractType(contracts.MockKeystoneForwarder)),
43+
)
44+
require.Len(t, mockAddrs, 1)
45+
require.Equal(t, "my-test-mock-forwarder", mockAddrs[0].Qualifier)
46+
47+
labels := mockAddrs[0].Labels.List()
48+
require.Len(t, labels, 2)
49+
require.Contains(t, labels[0], forwarder.DeploymentBlockLabel)
50+
require.Contains(t, labels[1], forwarder.DeploymentHashLabel)
51+
}

0 commit comments

Comments
 (0)