Skip to content
Draft
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
10 changes: 10 additions & 0 deletions e2e/config.canton.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[settings]
private_keys = [
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
"0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
"0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"
]

[canton_config]
type = "canton"
number_of_canton_validators = 1
155 changes: 155 additions & 0 deletions e2e/tests/canton/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//go:build e2e

package canton

import (
"context"
"fmt"
"time"

"github.com/google/uuid"
"github.com/noders-team/go-daml/pkg/client"
"github.com/noders-team/go-daml/pkg/model"
"github.com/noders-team/go-daml/pkg/types"
"github.com/smartcontractkit/chainlink-canton/bindings/mcms"

Check failure on line 14 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E - Nix setup

github.com/smartcontractkit/chainlink-canton@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options https://github.com/smartcontractkit/chainlink-canton in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:

Check failure on line 14 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E - Nix setup

github.com/smartcontractkit/chainlink-canton@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options https://github.com/smartcontractkit/chainlink-canton in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:

Check failure on line 14 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E

github.com/smartcontractkit/chainlink-canton@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options origin in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:

Check failure on line 14 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E

github.com/smartcontractkit/chainlink-canton@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options origin in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:

Check failure on line 14 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E

github.com/smartcontractkit/chainlink-canton@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options https://github.com/smartcontractkit/chainlink-canton in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:

Check failure on line 14 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E

github.com/smartcontractkit/chainlink-canton@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options https://github.com/smartcontractkit/chainlink-canton in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:
"github.com/smartcontractkit/chainlink-canton/contracts"

Check failure on line 15 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E - Nix setup

github.com/smartcontractkit/chainlink-canton@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options https://github.com/smartcontractkit/chainlink-canton in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:

Check failure on line 15 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E

github.com/smartcontractkit/chainlink-canton@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options origin in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:

Check failure on line 15 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E

github.com/smartcontractkit/chainlink-canton@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options https://github.com/smartcontractkit/chainlink-canton in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:
"github.com/smartcontractkit/chainlink-canton/integration-tests/testhelpers"

Check failure on line 16 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E - Nix setup

github.com/smartcontractkit/chainlink-canton/integration-tests@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options https://github.com/smartcontractkit/chainlink-canton in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:

Check failure on line 16 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E

github.com/smartcontractkit/chainlink-canton/integration-tests@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options origin in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:

Check failure on line 16 in e2e/tests/canton/common.go

View workflow job for this annotation

GitHub Actions / Tests E2E

github.com/smartcontractkit/chainlink-canton/integration-tests@v0.0.0-20260205203303-a74a56b3565d: invalid version: git ls-remote -q --end-of-options https://github.com/smartcontractkit/chainlink-canton in /home/runner/go/pkg/mod/cache/vcs/94d9727765b1ab36ed05d10c5d35ca726794e132cb25bee11cb34a7d6107b004: exit status 128:

e2e "github.com/smartcontractkit/mcms/e2e/tests"
cantonsdk "github.com/smartcontractkit/mcms/sdk/canton"
"github.com/stretchr/testify/suite"
)

type TestSuite struct {
suite.Suite
e2e.TestSetup

env testhelpers.TestEnvironment

client *client.DamlBindingClient
participant testhelpers.Participant

packageIDs []string
mcmsContractID string
}

func NewBindingClient(ctx context.Context, jwtToken, ledgerAPIURL, adminAPIURL string) (*client.DamlBindingClient, error) {
bindingClient, err := client.NewDamlClient(jwtToken, ledgerAPIURL).
WithAdminAddress(adminAPIURL).
Build(ctx)
if err != nil {
return nil, fmt.Errorf("failed to create DAML binding client: %w", err)
}

return bindingClient, nil
}

func (s *TestSuite) SetupSuite() {
s.T().Log("Spinning up Canton test environment...")
s.env = testhelpers.NewTestEnvironment(s.T(), testhelpers.WithNumberOfParticipants(1))
jwt, err := s.env.Participant(1).GetToken(s.T().Context())
s.Require().NoError(err)
participant := s.env.Participant(1)
config := participant.GetConfig()
s.client, err = NewBindingClient(s.T().Context(), jwt, config.GRPCLedgerAPIURL, config.AdminAPIURL)
s.Require().NoError(err)
s.participant = participant
}

const NumGroups = 32

func (s *TestSuite) DeployMCMSContract() {
s.T().Log("Uploading MCMS DAR...")

mcmsDar, err := contracts.GetDar(contracts.MCMS, contracts.CurrentVersion)
s.Require().NoError(err)

packageIDs, err := testhelpers.UploadDARstoMultipleParticipants(s.T().Context(), [][]byte{mcmsDar}, s.participant)
s.Require().NoError(err)
s.packageIDs = packageIDs

mcmsOwner := s.participant.Party
chainId := 1
baseMcmsId := "mcms-test-001"
mcmsId := makeMcmsId(baseMcmsId, "proposer")

mcmsContractId := s.createMCMS(s.T().Context(), s.participant, mcmsOwner, chainId, mcmsId, mcms.RoleProposer)
s.mcmsContractID = mcmsContractId
}

func (s *TestSuite) createMCMS(ctx context.Context, participant testhelpers.Participant, owner string, chainId int, mcmsId string, role mcms.Role) string {
// Create empty expiring root
emptyExpiringRoot := mcms.ExpiringRoot{
Root: types.TEXT(""),
ValidUntil: types.TIMESTAMP(time.Unix(0, 0).UTC()),
OpCount: types.INT64(0),
}

// Create empty root metadata
emptyRootMetadata := mcms.RootMetadata{
ChainId: types.INT64(0),
MultisigId: types.TEXT(""),
PreOpCount: types.INT64(0),
PostOpCount: types.INT64(0),
OverridePreviousRoot: types.BOOL(false),
}

// Create MCMS contract
mcmsContract := mcms.MCMS{
Owner: types.PARTY(participant.Party),
InstanceId: types.TEXT(mcmsId),
Role: role,
ChainId: types.INT64(chainId),
McmsId: types.TEXT(mcmsId),
Config: mcms.MultisigConfig{
Signers: []mcms.SignerInfo{},
GroupQuorums: []types.INT64{types.INT64(1)},
GroupParents: []types.INT64{types.INT64(1)},
},
SeenHashes: types.GENMAP{}, // Empty map
ExpiringRoot: emptyExpiringRoot,
RootMetadata: emptyRootMetadata,
}

// Submit via binding client's CommandService
commandID := uuid.Must(uuid.NewUUID()).String()
cmds := &model.SubmitAndWaitRequest{
Commands: &model.Commands{
WorkflowID: "mcms-deploy",
UserID: participant.UserName,
CommandID: commandID,
ActAs: []string{participant.Party},
Commands: []*model.Command{{Command: mcmsContract.CreateCommand()}},
},
}

submitResp, err := s.client.CommandService.SubmitAndWaitForTransaction(ctx, cmds)
s.Require().NoError(err, "failed to submit MCMS deploy transaction")

// Retrieve the contract ID and template ID from the create event
mcmsContractID := ""
mcmsTemplateID := ""
for _, event := range submitResp.Transaction.Events {
if event.Created == nil {
continue
}

normalizedTemplateID := cantonsdk.NormalizeTemplateKey(event.Created.TemplateID)
if normalizedTemplateID == cantonsdk.MCMSTemplateKey {
mcmsContractID = event.Created.ContractID
mcmsTemplateID = event.Created.TemplateID

break
}
}

s.Require().NotEmpty(mcmsContractID, "failed to find MCMS contract in transaction events")
s.Require().NotEmpty(mcmsTemplateID, "failed to find MCMS template ID in transaction events")

return mcmsContractID
}

// TODO: Use right role types
func makeMcmsId(baseId string, role string) string {
return baseId + "-" + role
}
125 changes: 125 additions & 0 deletions e2e/tests/canton/configurer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//go:build e2e

package canton

import (
"slices"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/noders-team/go-daml/pkg/model"

cantonsdk "github.com/smartcontractkit/mcms/sdk/canton"

"github.com/smartcontractkit/mcms/types"
)

type MCMSConfigurerTestSuite struct {
TestSuite
}

// SetupSuite runs before the test suite
func (s *MCMSConfigurerTestSuite) SetupSuite() {
s.TestSuite.SetupSuite()
s.DeployMCMSContract()
}

func (s *MCMSConfigurerTestSuite) TestSetConfig() {
// Signers in each group need to be sorted alphabetically
signers := [30]common.Address{}
for i := range signers {
key, _ := crypto.GenerateKey()
signers[i] = crypto.PubkeyToAddress(key.PublicKey)
}
slices.SortFunc(signers[:], func(a, b common.Address) int {
return a.Cmp(b)
})

proposerConfig := &types.Config{
Quorum: 2,
Signers: []common.Address{
signers[0],
signers[1],
signers[2],
},
GroupSigners: []types.Config{
{
Quorum: 4,
Signers: []common.Address{
signers[3],
signers[4],
signers[5],
signers[6],
signers[7],
},
GroupSigners: []types.Config{
{
Quorum: 1,
Signers: []common.Address{
signers[8],
signers[9],
},
GroupSigners: []types.Config{},
},
},
},
{
Quorum: 3,
Signers: []common.Address{
signers[10],
signers[11],
signers[12],
signers[13],
},
GroupSigners: []types.Config{},
},
},
}

// Set config
{
configurer, err := cantonsdk.NewConfigurer(s.client, s.participant.UserName, s.participant.Party)
s.Require().NoError(err, "creating configurer for Canton mcms contract")
tx, err := configurer.SetConfig(s.T().Context(), s.mcmsContractID, proposerConfig, true)
s.Require().NoError(err, "setting config on Canton mcms contract")

// Verify transaction result
rawData, ok := tx.RawData.(map[string]any)
s.Require().True(ok)
rawTx, ok := rawData["RawTx"]
s.Require().True(ok)

submitResp, ok := rawTx.(*model.SubmitAndWaitForTransactionResponse)
s.Require().True(ok)

// Verify CompletionOffset exists
s.Require().NotZero(submitResp.CompletionOffset, "transaction should have CompletionOffset")

events := submitResp.Transaction.Events
s.Require().Len(events, 2, "transaction should have exactly 2 events (archived + created)")

// Verify event[0] is Archived (old contract)
s.Require().NotNil(events[0].Archived, "first event should be Archived event")
s.Require().Nil(events[0].Created, "first event should not be Created event")
s.Require().Equal(s.mcmsContractID, events[0].Archived.ContractID, "archived contract should be the old MCMS contract")

// Verify event[1] is Created (new contract)
s.Require().NotNil(events[1].Created, "second event should be Created event")
s.Require().Nil(events[1].Archived, "second event should not be Archived event")

// Verify Template ID matches
rawData, ok = tx.RawData.(map[string]any)
s.Require().True(ok)
newMCMSTemplateID, ok := rawData["NewMCMSTemplateID"].(string)
s.Require().True(ok)
s.Require().Contains(newMCMSTemplateID, "MCMS.Main:MCMS", "template ID should match MCMS template")
s.Require().Equal(newMCMSTemplateID, events[1].Created.TemplateID, "created event template ID should match returned template ID")

// Verify new contract ID is different from old
newMCMSContractID, ok := rawData["NewMCMSContractID"].(string)
s.Require().True(ok)
s.Require().NotEmpty(newMCMSContractID, "new contract ID should not be empty")
s.Require().NotEqual(s.mcmsContractID, newMCMSContractID, "new contract ID should be different from old contract ID")
s.Require().Equal(newMCMSContractID, events[1].Created.ContractID, "created event contract ID should match returned contract ID")
}
}
Loading
Loading