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
2 changes: 1 addition & 1 deletion e2e/tests/canton/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (s *TestSuite) createMCMS(ctx context.Context, participant testhelpers.Part
UserID: participant.UserName,
CommandID: commandID,
ActAs: []string{participant.Party},
Commands: []*model.Command{{Command: mcmsContract.CreateCommandWithPackageID(s.packageIDs[0])}},
Commands: []*model.Command{{Command: mcmsContract.CreateCommand()}},
},
}

Expand Down
238 changes: 238 additions & 0 deletions e2e/tests/canton/inspector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
//go:build e2e

package canton

import (
"context"
"io"
"slices"
"testing"

apiv2 "github.com/digital-asset/dazl-client/v8/go/api/com/daml/ledger/api/v2"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/suite"

cantonsdk "github.com/smartcontractkit/mcms/sdk/canton"
mcmstypes "github.com/smartcontractkit/mcms/types"
)

type MCMSInspectorTestSuite struct {
TestSuite
inspector *cantonsdk.Inspector
}

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

// Create inspector instance using participant's StateServiceClient
s.inspector = cantonsdk.NewInspector(s.participant.StateServiceClient, s.participant.Party)
}

func (s *MCMSInspectorTestSuite) TestGetConfig() {
ctx := s.T().Context()

// 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)
})

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

// Set config using configurer
configurer, err := cantonsdk.NewConfigurer(s.client, s.participant.UserName, s.participant.Party)
s.Require().NoError(err, "creating configurer")

_, err = configurer.SetConfig(ctx, s.mcmsContractID, expectedConfig, true)
s.Require().NoError(err, "setting config")

// Get the new contract ID after SetConfig (which archives old and creates new)
newContractID, err := s.getLatestMCMSContractID(ctx)
s.Require().NoError(err, "getting latest MCMS contract ID")

// Now test the inspector
actualConfig, err := s.inspector.GetConfig(ctx, newContractID)
s.Require().NoError(err, "getting config from inspector")
s.Require().NotNil(actualConfig, "config should not be nil")

// Verify the config matches what we set
s.verifyConfigMatch(expectedConfig, actualConfig)
}

func (s *MCMSInspectorTestSuite) TestGetOpCount() {
ctx := s.T().Context()

// Get the latest contract ID
contractID, err := s.getLatestMCMSContractID(ctx)
s.Require().NoError(err, "getting latest MCMS contract ID")

// Get op count
opCount, err := s.inspector.GetOpCount(ctx, contractID)
s.Require().NoError(err, "getting op count")

// Initially should be 0
s.Require().Equal(uint64(0), opCount, "initial op count should be 0")
}

func (s *MCMSInspectorTestSuite) TestGetRoot() {
ctx := s.T().Context()

// Get the latest contract ID
contractID, err := s.getLatestMCMSContractID(ctx)
s.Require().NoError(err, "getting latest MCMS contract ID")

// Get root
root, validUntil, err := s.inspector.GetRoot(ctx, contractID)
s.Require().NoError(err, "getting root")

// Initially root should be empty and validUntil should be 0
s.Require().Equal(common.Hash{}, root, "initial root should be empty")
s.Require().Equal(uint32(0), validUntil, "initial validUntil should be 0")
}

func (s *MCMSInspectorTestSuite) TestGetRootMetadata() {
ctx := s.T().Context()

// Get the latest contract ID
contractID, err := s.getLatestMCMSContractID(ctx)
s.Require().NoError(err, "getting latest MCMS contract ID")

// Get root metadata
metadata, err := s.inspector.GetRootMetadata(ctx, contractID)
s.Require().NoError(err, "getting root metadata")

// Verify metadata structure
s.Require().Equal(uint64(0), metadata.StartingOpCount, "initial starting op count should be 0")
s.Require().NotEmpty(metadata.MCMAddress, "MCM address should not be empty")
}

// Helper function to get the latest MCMS contract ID
func (s *MCMSInspectorTestSuite) getLatestMCMSContractID(ctx context.Context) (string, error) {
// Get current ledger offset
ledgerEndResp, err := s.participant.StateServiceClient.GetLedgerEnd(ctx, &apiv2.GetLedgerEndRequest{})
if err != nil {
return "", err
}

// Query active contracts
activeContractsResp, err := s.participant.StateServiceClient.GetActiveContracts(ctx, &apiv2.GetActiveContractsRequest{
ActiveAtOffset: ledgerEndResp.GetOffset(),
EventFormat: &apiv2.EventFormat{
FiltersByParty: map[string]*apiv2.Filters{
s.participant.Party: {
Cumulative: []*apiv2.CumulativeFilter{
{
IdentifierFilter: &apiv2.CumulativeFilter_TemplateFilter{
TemplateFilter: &apiv2.TemplateFilter{
TemplateId: &apiv2.Identifier{
PackageId: "#mcms",
ModuleName: "MCMS.Main",
EntityName: "MCMS",
},
IncludeCreatedEventBlob: false,
},
},
},
},
},
},
Verbose: true,
},
})
if err != nil {
return "", err
}
defer activeContractsResp.CloseSend()

// Get the first (and should be only) active MCMS contract
for {
resp, err := activeContractsResp.Recv()
if err == io.EOF {
break
}
if err != nil {
return "", err
}

activeContract, ok := resp.GetContractEntry().(*apiv2.GetActiveContractsResponse_ActiveContract)
if !ok {
continue
}

createdEvent := activeContract.ActiveContract.GetCreatedEvent()
if createdEvent == nil {
continue
}

return createdEvent.ContractId, nil
}

return "", nil
}

// Helper to verify config matches
func (s *MCMSInspectorTestSuite) verifyConfigMatch(expected, actual *mcmstypes.Config) {
s.Require().Equal(expected.Quorum, actual.Quorum, "quorum should match")
s.Require().Equal(len(expected.Signers), len(actual.Signers), "number of signers should match")

// Verify signers
for i, expectedSigner := range expected.Signers {
s.Require().Equal(expectedSigner, actual.Signers[i], "signer %d should match", i)
}

// Verify group signers recursively
s.Require().Equal(len(expected.GroupSigners), len(actual.GroupSigners), "number of group signers should match")
for i, expectedGroup := range expected.GroupSigners {
s.verifyConfigMatch(&expectedGroup, &actual.GroupSigners[i])
}
}

func TestMCMSInspectorSuite(t *testing.T) {
suite.Run(t, new(MCMSInspectorTestSuite))
}
1 change: 1 addition & 0 deletions e2e/tests/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ func TestTONSuite(t *testing.T) {

func TestCantonSuite(t *testing.T) {
suite.Run(t, new(cantone2e.MCMSConfigurerTestSuite))
suite.Run(t, new(cantone2e.MCMSInspectorTestSuite))
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ replace github.com/fbsobreira/gotron-sdk => github.com/smartcontractkit/chainlin

replace github.com/digital-asset/dazl-client/v8 => github.com/noders-team/dazl-client/v8 v8.7.1-2

replace github.com/noders-team/go-daml => github.com/stackman27/go-daml v0.0.0-20260129035354-bee9c994446f
replace github.com/noders-team/go-daml => github.com/stackman27/go-daml v0.0.0-20260204001938-550ee9d8ab10

require (
github.com/aptos-labs/aptos-go-sdk v1.11.0
github.com/block-vision/sui-go-sdk v1.1.4
github.com/digital-asset/dazl-client/v8 v8.8.0
github.com/ethereum/go-ethereum v1.16.8
github.com/gagliardetto/binary v0.8.0
github.com/gagliardetto/solana-go v1.13.0
Expand Down Expand Up @@ -96,7 +97,6 @@ require (
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/deepmap/oapi-codegen v1.8.2 // indirect
github.com/digital-asset/dazl-client/v8 v8.8.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v28.5.1+incompatible // indirect
github.com/docker/go-connections v0.6.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -743,8 +743,8 @@ github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qq
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stackman27/go-daml v0.0.0-20260129035354-bee9c994446f h1:I79FWYle5/t1QxAZHClHc8OWJoKeUc97PNEX0XV9meM=
github.com/stackman27/go-daml v0.0.0-20260129035354-bee9c994446f/go.mod h1:yi458NGE4dlDOhlyCZvQ2XgsIOdHHvepwoHRgEusbo8=
github.com/stackman27/go-daml v0.0.0-20260204001938-550ee9d8ab10 h1:vihbDjQcH7ipRkIWQSIWcmjQ/wJQn2G4aBVc+erx4fM=
github.com/stackman27/go-daml v0.0.0-20260204001938-550ee9d8ab10/go.mod h1:yi458NGE4dlDOhlyCZvQ2XgsIOdHHvepwoHRgEusbo8=
github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 h1:ba4VRWSkRzgdP5hB5OxexIzBXZbSwgcw8bEu06ivGQI=
github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863/go.mod h1:oPTjPNrRucLv9mU27iNPj6n0CWWcNFhoXFOLVGJwHCA=
github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo=
Expand Down
2 changes: 1 addition & 1 deletion sdk/canton/configurer.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (c Configurer) SetConfig(ctx context.Context, mcmsAddr string, cfg *types.C
ActAs: []string{c.party},
Commands: []*model.Command{{
Command: &model.ExerciseCommand{
TemplateID: fmt.Sprintf("%s:%s:%s", mcmsPkgID, "MCMS.Main", "MCMS"),
TemplateID: mcmsContract.GetTemplateID(),
ContractID: exerciseCmd.ContractID,
Choice: exerciseCmd.Choice,
Arguments: exerciseCmd.Arguments,
Expand Down
Loading