Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions .github/workflows/sui-ccip-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
# 1.5) Get core ref from PR body (optional override)
- name: Get core ref from PR body
if: github.event_name == 'pull_request'
run: |

Check failure on line 124 in .github/workflows/sui-ccip-test.yml

View workflow job for this annotation

GitHub Actions / Lint GH Actions and scripts

[actionlint] reported by reviewdog 🐶 shellcheck reported issue in this script: SC2236:style:3:6: Use -n instead of ! -z [shellcheck] Raw Output: .github/workflows/sui-ccip-test.yml:124:9: shellcheck reported issue in this script: SC2236:style:3:6: Use -n instead of ! -z [shellcheck]

Check failure on line 124 in .github/workflows/sui-ccip-test.yml

View workflow job for this annotation

GitHub Actions / Lint GH Actions and scripts

[actionlint] reported by reviewdog 🐶 shellcheck reported issue in this script: SC2086:info:2:17: Double quote to prevent globbing and word splitting [shellcheck] Raw Output: .github/workflows/sui-ccip-test.yml:124:9: shellcheck reported issue in this script: SC2086:info:2:17: Double quote to prevent globbing and word splitting [shellcheck]
comment=$(gh pr view https://github.com/${{ github.repository }}/pull/${{ github.event.pull_request.number }} --json body -q '.body')
core_ref=$(echo $comment | grep -oP 'core ref: \K\S+' || true)
if [ ! -z "$core_ref" ]; then
Expand All @@ -139,7 +139,7 @@
# 2) Read Go version
- name: Read Go version
id: go-version
run: |

Check failure on line 142 in .github/workflows/sui-ccip-test.yml

View workflow job for this annotation

GitHub Actions / Lint GH Actions and scripts

[actionlint] reported by reviewdog 🐶 shellcheck reported issue in this script: SC2086:info:2:30: Double quote to prevent globbing and word splitting [shellcheck] Raw Output: .github/workflows/sui-ccip-test.yml:142:9: shellcheck reported issue in this script: SC2086:info:2:30: Double quote to prevent globbing and word splitting [shellcheck]
GO_VER=$(grep -E '^golang ' temp/chainlink/.tool-versions | cut -d' ' -f2)
echo "GO_VERSION=$GO_VER" >> $GITHUB_OUTPUT

Expand Down Expand Up @@ -184,13 +184,35 @@
go get github.com/smartcontractkit/chainlink-sui@$REF
go get github.com/smartcontractkit/chainlink-sui/deployment@$REF

# chainlink-sui/deployment pulls a newer chainlink-ccip/deployment than chainlink
# develop pins for chains/evm; keep both sibling modules on the same commit.
CCIP_DEP=$(grep -m1 'github.com/smartcontractkit/chainlink-ccip/deployment v' \
../../chainlink-sui/deployment/go.mod | awk '{print $2}')
CCIP_REF="${CCIP_DEP##*-}"
echo "Aligning chainlink-ccip modules to commit ${CCIP_REF} (from ${CCIP_DEP})"
go get "github.com/smartcontractkit/chainlink-ccip/deployment@${CCIP_REF}"
go get "github.com/smartcontractkit/chainlink-ccip/chains/evm@${CCIP_REF}"

cd ../deployment
go get "github.com/smartcontractkit/chainlink-ccip/deployment@${CCIP_REF}"
go get "github.com/smartcontractkit/chainlink-ccip/chains/evm@${CCIP_REF}"

cd ..
make gomodtidy

# 6) Build LOOP plugin
- name: Build LOOP plugin
# 6) Install public LOOP plugins (same as chainlink integration CI), then overlay PR relayer
- name: Install LOOP plugins (public)
working-directory: temp/chainlink
env:
GOPRIVATE: github.com/smartcontractkit/*
run: |
make install-loopinstall
make install-plugins-public

- name: Build chainlink-sui LOOP plugin from PR
working-directory: temp/chainlink-sui
run: go build -o chainlink-sui ./relayer/cmd/chainlink-sui/main.go
run: |
go build -ldflags=-s -o "$(go env GOPATH)/bin/chainlink-sui" ./relayer/cmd/chainlink-sui

# 7) Setup Sui CLI
- name: Setup Sui CLI
Expand All @@ -214,7 +236,6 @@
env:
CL_DATABASE_URL: ${{ env.CL_DATABASE_URL }}
TEST_ENV_TYPE: in-memory
CL_SUI_CMD: ${{ github.workspace }}/temp/chainlink-sui/chainlink-sui
run: |
cd temp/chainlink/integration-tests
echo "=== Running ${{ matrix.test_name }} ==="
Expand Down
12 changes: 9 additions & 3 deletions bindings/bind/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ const env = "local"
type SourceModifier func(packageRoot string) error

var (
testModifierMu sync.Mutex
testModifier SourceModifier
testModifierMu sync.Mutex
testModifier SourceModifier
compilePackageMu sync.Mutex
)

// SetTestModifier sets a source modifier for the next compilation (test only)
Expand Down Expand Up @@ -208,6 +209,11 @@ func CompilePackage(packageName contracts.Package, namedAddresses map[string]str
}

func compilePackageInternal(packageName contracts.Package, namedAddresses map[string]string, isUpgrade bool, suiRPC string, modifier SourceModifier) (PackageArtifact, error) {
// CompilePackage uses a process-global SUI_CONFIG_DIR; serialize compiles so
// parallel integration tests do not cross-contaminate temp CLI configs.
compilePackageMu.Lock()
defer compilePackageMu.Unlock()

var rpcURL string
// 1️. Detect dynamic RPC from Docker
if suiRPC == "" {
Expand Down Expand Up @@ -1162,7 +1168,7 @@ func setupSuiEnv(alias, rpcURL string) error {
newCmd.Env = os.Environ()
newOut, err := newCmd.CombinedOutput()
if err != nil {
fmt.Printf("failed to create sui env '%s': %v\nOutput:\n%s", alias, err, string(newOut))
return fmt.Errorf("failed to create sui env '%s': %w\nOutput:\n%s", alias, err, string(newOut))
}

// Step 4️ — Switch to new env
Expand Down
12 changes: 10 additions & 2 deletions deployment/changesets/cs_mcms_configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
type ConfigureMCMSConfig struct {
mcmsops.ConfigureMCMSSeqInput
TimelockConfig *utils.TimelockConfig // If nil, configuration will be executed directly
IsFastCurse bool // If true, the fastcurse MCMS instance is configured
IsFastCurse bool `yaml:"isFastCurse,omitempty"` // If true, the fastcurse MCMS instance is configured
}

var _ cldf.ChangeSetV2[ConfigureMCMSConfig] = ConfigureMCMS{}
Expand Down Expand Up @@ -64,8 +64,16 @@ func (c ConfigureMCMS) Apply(e cldf.Environment, config ConfigureMCMSConfig) (cl
deps.Signer = nil
}

seqInput := config.ConfigureMCMSSeqInput
if seqInput.PackageId == "" {
seqInput.PackageId = mcmsState.PackageID
seqInput.McmsAccountOwnerCapObjectId = mcmsState.AccountOwnerCapObjectID
seqInput.McmsAccountStateObjectId = mcmsState.AccountStateObjectID
seqInput.McmsMultisigStateObjectId = mcmsState.StateObjectID
}

// Run ConfigureMCMS Sequence
configReport, err := cld_ops.ExecuteSequence(e.OperationsBundle, mcmsops.ConfigureMCMSSequence, deps, config.ConfigureMCMSSeqInput)
configReport, err := cld_ops.ExecuteSequence(e.OperationsBundle, mcmsops.ConfigureMCMSSequence, deps, seqInput)
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to configure MCMS for Sui chain %d: %w", config.ChainSelector, err)
}
Expand Down
21 changes: 14 additions & 7 deletions deployment/changesets/cs_mcms_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import (
var _ cldf.ChangeSetV2[DeployMCMSConfig] = DeployMCMS{}

// DeployMCMSConfig wraps DeployMCMSSeqInput and adds the IsFastCurse flag.
// When IsFastCurse is true all address-book entries are stored with the
// "fastcurse" label so that LoadOnchainStatesui can distinguish the two
// MCMS instances deployed on the same chain.
// When IsFastCurse is true the fast_mcms package is published and all address-book
// entries are stored with the "fastcurse" label so LoadOnchainStatesui can distinguish
// the two MCMS instances deployed on the same chain.
type DeployMCMSConfig struct {
mcmsops.DeployMCMSSeqInput
IsFastCurse bool
IsFastCurse bool `yaml:"isFastCurse,omitempty"`
}

type DeployMCMS struct{}
Expand All @@ -48,8 +48,10 @@ func (d DeployMCMS) Apply(e cldf.Environment, config DeployMCMSConfig) (cldf.Cha
SuiRPC: suiChain.URL,
}

// Run DeployMCMS Sequence
mcmsReport, err := cld_ops.ExecuteSequence(e.OperationsBundle, mcmsops.DeployMCMSSequence, deps, config.DeployMCMSSeqInput)
seqInput := config.DeployMCMSSeqInput
seqInput.FastMCMS = config.IsFastCurse

mcmsReport, err := cld_ops.ExecuteSequence(e.OperationsBundle, mcmsops.DeployMCMSSequence, deps, seqInput)
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to deploy MCMS for Sui chain %d: %w", config.ChainSelector, err)
}
Expand All @@ -59,10 +61,15 @@ func (d DeployMCMS) Apply(e cldf.Environment, config DeployMCMSConfig) (cldf.Cha
return cldf.ChangesetOutput{}, fmt.Errorf("failed to store MCMS in address book for Sui chain %d: %w", config.ChainSelector, err)
}

proposals := []mcms.TimelockProposal{}
if !seqInput.SkipOwnershipTransfer {
proposals = append(proposals, mcmsReport.Output.AcceptOwnershipProposal)
}

return cldf.ChangesetOutput{
AddressBook: ab,
Reports: seqReports,
MCMSTimelockProposals: []mcms.TimelockProposal{mcmsReport.Output.AcceptOwnershipProposal},
MCMSTimelockProposals: proposals,
}, nil
}

Expand Down
51 changes: 51 additions & 0 deletions deployment/changesets/cs_mcms_dual_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,57 @@ func TestDeployMCMS_VerifyPreconditions_RejectsDuplicateInstance(t *testing.T) {
require.Contains(t, err.Error(), "slow MCMS is already recorded")
}

func TestDeployMCMS_VerifyPreconditions_RejectsDuplicateFastInstance(t *testing.T) {
t.Parallel()

selector := cselectors.SUI_TESTNET.Selector
ab := cldf.NewMemoryAddressBook()
require.NoError(t, deployment.StoreMCMSInAddressBook(ab, selector, mcmsops.DeployMCMSSeqOutput{
PackageId: "0xfast_pkg",
Objects: mcmsops.DeployMCMSObjects{
McmsMultisigStateObjectId: "0xfast_state",
McmsRegistryObjectId: "0xfast_registry",
McmsAccountStateObjectId: "0xfast_account",
McmsAccountOwnerCapObjectId: "0xfast_owner_cap",
TimelockObjectId: "0xfast_timelock",
McmsDeployerStateObjectId: "0xfast_deployer",
},
}, deployment.MCMSInstanceFastCurse))

cs := DeployMCMS{}
err := cs.VerifyPreconditions(dualMCMSEnv(t, ab, selector), DeployMCMSConfig{
DeployMCMSSeqInput: mcmsops.DeployMCMSSeqInput{ChainSelector: selector},
IsFastCurse: true,
})
require.Error(t, err)
require.Contains(t, err.Error(), "fastcurse MCMS is already recorded")
}

func TestDeployMCMS_VerifyPreconditions_AllowsFastWhenOnlySlowExists(t *testing.T) {
t.Parallel()

selector := cselectors.SUI_TESTNET.Selector
ab := cldf.NewMemoryAddressBook()
require.NoError(t, deployment.StoreMCMSInAddressBook(ab, selector, mcmsops.DeployMCMSSeqOutput{
PackageId: "0xslow_pkg",
Objects: mcmsops.DeployMCMSObjects{
McmsMultisigStateObjectId: "0xslow_state",
McmsRegistryObjectId: "0xslow_registry",
McmsAccountStateObjectId: "0xslow_account",
McmsAccountOwnerCapObjectId: "0xslow_owner_cap",
TimelockObjectId: "0xslow_timelock",
McmsDeployerStateObjectId: "0xslow_deployer",
},
}, deployment.MCMSInstanceSlow))

cs := DeployMCMS{}
err := cs.VerifyPreconditions(dualMCMSEnv(t, ab, selector), DeployMCMSConfig{
DeployMCMSSeqInput: mcmsops.DeployMCMSSeqInput{ChainSelector: selector},
IsFastCurse: true,
})
require.NoError(t, err)
}

func TestRegisterCurserCap_VerifyPreconditions_RequiresBothMCMSInstances(t *testing.T) {
t.Parallel()

Expand Down
35 changes: 32 additions & 3 deletions deployment/ops/mcms/seq_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ type DeployMCMSSeqInput struct {
Bypasser *types.Config `json:"bypasser,omitempty" yaml:"bypasser,omitempty"`
Proposer *types.Config `json:"proposer,omitempty" yaml:"proposer,omitempty"`
Canceller *types.Config `json:"canceller,omitempty" yaml:"canceller,omitempty"`

// FastMCMS publishes contracts/mcms/fast_mcms instead of contracts/mcms/mcms.
// Typically set by the DeployMCMS changeset from IsFastCurse rather than YAML input.
FastMCMS bool `json:"fastMCMS,omitempty" yaml:"fastMCMS,omitempty"`

// SkipOwnershipTransfer omits the MCMS self-ownership init and accept-ownership proposal.
SkipOwnershipTransfer bool `json:"skipOwnershipTransfer,omitempty" yaml:"skipOwnershipTransfer,omitempty"`
}

type DeployMCMSSeqOutput struct {
Expand All @@ -39,10 +46,9 @@ var DeployMCMSSequence = cld_ops.NewSequence(
)

func deployMCMS(env cld_ops.Bundle, deps sui_ops.OpTxDeps, input DeployMCMSSeqInput) (DeployMCMSSeqOutput, error) {
// Deploy MCMS first
deployReport, err := cld_ops.ExecuteOperation(env, DeployMCMSOp, deps, cld_ops.EmptyInput{})
deployReport, err := executeMCMSDeploy(env, deps, input.FastMCMS)
if err != nil {
return DeployMCMSSeqOutput{}, fmt.Errorf("failed to deploy MCMS: %w", err)
return DeployMCMSSeqOutput{}, err
}

// Configure each timelock role if config is provided
Expand All @@ -61,6 +67,13 @@ func deployMCMS(env cld_ops.Bundle, deps sui_ops.OpTxDeps, input DeployMCMSSeqIn
return DeployMCMSSeqOutput{}, fmt.Errorf("failed to configure MCMS: %w", err)
}

if input.SkipOwnershipTransfer {
return DeployMCMSSeqOutput{
PackageId: deployReport.Output.PackageId,
Objects: deployReport.Output.Objects,
}, nil
}

// Init the ownership transfer to self
transferOwnershipInput := MCMSTransferOwnershipInput{
McmsPackageID: deployReport.Output.PackageId,
Expand Down Expand Up @@ -99,3 +112,19 @@ func deployMCMS(env cld_ops.Bundle, deps sui_ops.OpTxDeps, input DeployMCMSSeqIn

return output, nil
}

func executeMCMSDeploy(env cld_ops.Bundle, deps sui_ops.OpTxDeps, fastMCMS bool) (cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[DeployMCMSObjects]], error) {
if fastMCMS {
deployReport, err := cld_ops.ExecuteOperation(env, DeployFastMCMSOp, deps, cld_ops.EmptyInput{})
if err != nil {
return cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[DeployMCMSObjects]]{}, fmt.Errorf("failed to deploy fast MCMS: %w", err)
}
return deployReport, nil
}

deployReport, err := cld_ops.ExecuteOperation(env, DeployMCMSOp, deps, cld_ops.EmptyInput{})
if err != nil {
return cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[DeployMCMSObjects]]{}, fmt.Errorf("failed to deploy MCMS: %w", err)
}
return deployReport, nil
}
81 changes: 79 additions & 2 deletions deployment/ops/mcms/seq_deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ func generateSortedSigners(count int) []common.Address {
}

func TestDeployMCMSSeq(t *testing.T) {
t.Parallel()

signer, client := testenv.SetupEnvironment(t)

deps := sui_ops.OpTxDeps{
Expand Down Expand Up @@ -194,3 +192,82 @@ func TestDeployMCMSSeq(t *testing.T) {
require.NotEmpty(t, proposal.Operations, "Proposal should contain operations")
require.Len(t, proposal.Operations, 1, "Proposal should contain exactly one operation")
}

func TestDeployFastMCMSSeq_SkipOwnershipTransfer(t *testing.T) {
signer, client := testenv.SetupEnvironment(t)

deps := sui_ops.OpTxDeps{
Client: client,
Signer: signer,
GetCallOpts: func() *bind.CallOpts {
b := uint64(300_000_000)
return &bind.CallOpts{
WaitForExecution: true,
GasBudget: &b,
}
},
}

bundle := cld_ops.NewBundle(
context.Background,
logger.Test(t),
cld_ops.NewMemoryReporter(),
)

signers := generateSortedSigners(4)
proposerConfig := &types.Config{
Quorum: 1,
Signers: signers[:2],
}

report, err := cld_ops.ExecuteSequence(bundle, DeployMCMSSequence, deps, DeployMCMSSeqInput{
ChainSelector: cselectors.SUI_TESTNET.Selector,
FastMCMS: true,
SkipOwnershipTransfer: true,
Proposer: proposerConfig,
})
require.NoError(t, err)

require.NotEmpty(t, report.Output.PackageId)
require.NotEmpty(t, report.Output.Objects.McmsRegistryObjectId)
require.Empty(t, report.Output.AcceptOwnershipProposal.Operations)
}

func TestDeployFastMCMSSeq_PublishesDistinctPackageFromSlowMCMS(t *testing.T) {
signer, client := testenv.SetupEnvironment(t)

deps := sui_ops.OpTxDeps{
Client: client,
Signer: signer,
GetCallOpts: func() *bind.CallOpts {
b := uint64(300_000_000)
return &bind.CallOpts{
WaitForExecution: true,
GasBudget: &b,
}
},
}

bundle := cld_ops.NewBundle(
context.Background,
logger.Test(t),
cld_ops.NewMemoryReporter(),
)

slowReport, err := cld_ops.ExecuteSequence(bundle, DeployMCMSSequence, deps, DeployMCMSSeqInput{
ChainSelector: cselectors.SUI_TESTNET.Selector,
SkipOwnershipTransfer: true,
})
require.NoError(t, err)

fastReport, err := cld_ops.ExecuteSequence(bundle, DeployMCMSSequence, deps, DeployMCMSSeqInput{
ChainSelector: cselectors.SUI_TESTNET.Selector,
FastMCMS: true,
SkipOwnershipTransfer: true,
})
require.NoError(t, err)

require.NotEqual(t, slowReport.Output.PackageId, fastReport.Output.PackageId)
require.NotEqual(t, slowReport.Output.Objects.McmsMultisigStateObjectId, fastReport.Output.Objects.McmsMultisigStateObjectId)
require.NotEqual(t, slowReport.Output.Objects.McmsRegistryObjectId, fastReport.Output.Objects.McmsRegistryObjectId)
}
Loading