diff --git a/e2e/config.canton.toml b/e2e/config.canton.toml new file mode 100644 index 000000000..bae2f4da3 --- /dev/null +++ b/e2e/config.canton.toml @@ -0,0 +1,10 @@ +[settings] +private_keys = [ + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", + "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" +] + +[canton_config] +type = "canton" +number_of_canton_validators = 1 diff --git a/e2e/tests/canton/common.go b/e2e/tests/canton/common.go new file mode 100644 index 000000000..1eae4e9cf --- /dev/null +++ b/e2e/tests/canton/common.go @@ -0,0 +1,314 @@ +//go:build e2e + +package canton + +import ( + "context" + "crypto/ecdsa" + "encoding/binary" + "encoding/hex" + "fmt" + + apiv2 "github.com/digital-asset/dazl-client/v8/go/api/com/daml/ledger/api/v2" + "github.com/google/uuid" + "github.com/smartcontractkit/chainlink-canton/bindings/mcms" + "github.com/smartcontractkit/chainlink-canton/contracts" + "github.com/smartcontractkit/chainlink-canton/integration-tests/testhelpers" + "github.com/smartcontractkit/go-daml/pkg/service/ledger" + "github.com/smartcontractkit/go-daml/pkg/types" + + mcmscore "github.com/smartcontractkit/mcms" + e2e "github.com/smartcontractkit/mcms/e2e/tests" + "github.com/smartcontractkit/mcms/sdk" + cantonsdk "github.com/smartcontractkit/mcms/sdk/canton" + mcmstypes "github.com/smartcontractkit/mcms/types" + "github.com/stretchr/testify/suite" +) + +type TestSuite struct { + suite.Suite + e2e.TestSetup + + env testhelpers.TestEnvironment + + participant testhelpers.Participant + + chainSelector mcmstypes.ChainSelector + chainId int64 + packageIDs []string + mcmsContractID string + mcmsId string + proposerMcmsId string +} + +func (s *TestSuite) SetupSuite() { + s.T().Log("Spinning up Canton test environment...") + s.env = testhelpers.NewTestEnvironment(s.T(), testhelpers.WithNumberOfParticipants(1)) + participant := s.env.Participant(1) + s.participant = participant + s.chainSelector = mcmstypes.ChainSelector(s.env.Chain.ChainSelector()) +} + +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 := int64(1) + mcmsId := "mcms-test-001" + + mcmsContractId := s.createMCMS(s.T().Context(), s.participant, mcmsOwner, chainId, mcmsId, mcms.RoleProposer) + s.mcmsContractID = mcmsContractId + s.mcmsId = mcmsId + s.proposerMcmsId = fmt.Sprintf("%s-%s", mcmsId, "proposer") + s.chainId = chainId +} + +func (s *TestSuite) DeployMCMSWithConfig(config *mcmstypes.Config) { + s.DeployMCMSContract() + + // Set the config + configurer, err := cantonsdk.NewConfigurer(s.participant.CommandServiceClient, s.participant.UserName, s.participant.Party, cantonsdk.TimelockRoleProposer) + s.Require().NoError(err) + + tx, err := configurer.SetConfig(s.T().Context(), s.mcmsContractID, config, true) + s.Require().NoError(err) + + // Extract new contract ID + rawData, ok := tx.RawData.(map[string]any) + s.Require().True(ok) + newContractID, ok := rawData["NewMCMSContractID"].(string) + s.Require().True(ok) + s.mcmsContractID = newContractID +} + +func (s *MCMSExecutorTestSuite) DeployCounterContract() { + s.counterInstanceID = "counter-" + uuid.New().String()[:8] + + // Create Counter contract + counterContract := mcms.Counter{ + Owner: types.PARTY(s.participant.Party), + InstanceId: types.TEXT(s.counterInstanceID), + Value: types.INT64(0), + } + + // Parse template ID + exerciseCmd := counterContract.CreateCommand() + packageID, moduleName, entityName, err := cantonsdk.ParseTemplateIDFromString(exerciseCmd.TemplateID) + s.Require().NoError(err, "failed to parse template ID") + + // Convert create arguments to apiv2 format + createArguments := ledger.ConvertToRecord(exerciseCmd.Arguments) + + commandID := uuid.Must(uuid.NewUUID()).String() + submitResp, err := s.participant.CommandServiceClient.SubmitAndWaitForTransaction(s.T().Context(), &apiv2.SubmitAndWaitForTransactionRequest{ + Commands: &apiv2.Commands{ + WorkflowId: "counter-deploy", + CommandId: commandID, + ActAs: []string{s.participant.Party}, + Commands: []*apiv2.Command{{ + Command: &apiv2.Command_Create{ + Create: &apiv2.CreateCommand{ + TemplateId: &apiv2.Identifier{ + PackageId: packageID, + ModuleName: moduleName, + EntityName: entityName, + }, + CreateArguments: createArguments, + }, + }, + }}, + }, + }) + s.Require().NoError(err) + + // Extract contract ID + transaction := submitResp.GetTransaction() + for _, event := range transaction.GetEvents() { + if createdEv := event.GetCreated(); createdEv != nil { + templateID := cantonsdk.FormatTemplateID(createdEv.GetTemplateId()) + if templateID != "" { + s.counterCID = createdEv.GetContractId() + break + } + } + } + s.Require().NotEmpty(s.counterCID) +} + +func (s *TestSuite) createMCMS(ctx context.Context, participant testhelpers.Participant, owner string, chainId int64, mcmsId string, role mcms.Role) string { + // Create empty config + emptyConfig := mcms.MultisigConfig{ + Signers: []mcms.SignerInfo{}, + GroupQuorums: []types.INT64{types.INT64(1)}, + GroupParents: []types.INT64{types.INT64(1)}, + } + + // Create empty role state using zero values and nil for maps + emptyRoleState := mcms.RoleState{ + Config: emptyConfig, + SeenHashes: nil, + ExpiringRoot: mcms.ExpiringRoot{}, + RootMetadata: mcms.RootMetadata{}, + } + + minDelayValue := &apiv2.Value{Sum: &apiv2.Value_Record{Record: &apiv2.Record{ + Fields: []*apiv2.RecordField{ + {Label: "microseconds", Value: &apiv2.Value{Sum: &apiv2.Value_Int64{Int64: 0}}}, + }, + }}} + + // Create MCMS contract with new structure + mcmsContract := mcms.MCMS{ + Owner: types.PARTY(participant.Party), + InstanceId: types.TEXT(mcmsId), + ChainId: types.INT64(chainId), + Proposer: emptyRoleState, + Canceller: emptyRoleState, + Bypasser: emptyRoleState, + BlockedFunctions: nil, + TimelockTimestamps: nil, + } + + // Parse template ID + exerciseCmd := mcmsContract.CreateCommand() + packageID, moduleName, entityName, err := cantonsdk.ParseTemplateIDFromString(exerciseCmd.TemplateID) + s.Require().NoError(err, "failed to parse template ID") + + // Convert create arguments to apiv2 format + createArguments := ledger.ConvertToRecord(exerciseCmd.Arguments) + + // Remove minDelay from arguments + filteredFields := make([]*apiv2.RecordField, 0, len(createArguments.Fields)) + for _, field := range createArguments.Fields { + if field.Label != "minDelay" { + filteredFields = append(filteredFields, field) + } + } + createArguments.Fields = filteredFields + + // Submit via CommandService + commandID := uuid.Must(uuid.NewUUID()).String() + submitResp, err := participant.CommandServiceClient.SubmitAndWaitForTransaction(ctx, &apiv2.SubmitAndWaitForTransactionRequest{ + Commands: &apiv2.Commands{ + WorkflowId: "mcms-deploy", + CommandId: commandID, + ActAs: []string{participant.Party}, + Commands: []*apiv2.Command{{ + Command: &apiv2.Command_Create{ + Create: &apiv2.CreateCommand{ + TemplateId: &apiv2.Identifier{ + PackageId: packageID, + ModuleName: moduleName, + EntityName: entityName, + }, + CreateArguments: &apiv2.Record{Fields: append( + createArguments.Fields, + &apiv2.RecordField{Label: "minDelay", Value: minDelayValue}, + )}, + }, + }, + }}, + }, + }) + s.Require().NoError(err, "failed to submit MCMS deploy transaction") + + // Retrieve the contract ID and template ID from the create event + mcmsContractID := "" + mcmsTemplateID := "" + transaction := submitResp.GetTransaction() + for _, event := range transaction.GetEvents() { + if createdEv := event.GetCreated(); createdEv != nil { + templateID := cantonsdk.FormatTemplateID(createdEv.GetTemplateId()) + normalizedTemplateID := cantonsdk.NormalizeTemplateKey(templateID) + if normalizedTemplateID == cantonsdk.MCMSTemplateKey { + mcmsContractID = createdEv.GetContractId() + mcmsTemplateID = 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 +} + +// encodeSetConfigParams encodes SetConfig parameters for Canton MCMS +func EncodeSetConfigParams(s *TestSuite, signerAddresses []string, groupQuorums, groupParents []int64, clearRoot bool) string { + var buf []byte + + // Encode signers list + buf = append(buf, byte(len(signerAddresses))) // numSigners (1 byte) + for i, signer := range signerAddresses { + addrBytes, err := hex.DecodeString(signer) + s.Require().NoError(err, "failed to decode signer address hex") + buf = append(buf, byte(len(addrBytes))) // addressLen (1 byte) + buf = append(buf, addrBytes...) // address bytes + + // SignerIndex (4 bytes, big-endian) + indexBytes := make([]byte, 4) + binary.BigEndian.PutUint32(indexBytes, uint32(i)) //nolint:gosec + buf = append(buf, indexBytes...) + + // SignerGroup (4 bytes, big-endian) + groupBytes := make([]byte, 4) + binary.BigEndian.PutUint32(groupBytes, uint32(0)) //nolint:gosec + buf = append(buf, groupBytes...) + } + + // Encode group quorums + buf = append(buf, byte(len(groupQuorums))) // numQuorums (1 byte) + for _, quorum := range groupQuorums { + quorumBytes := make([]byte, 4) + binary.BigEndian.PutUint32(quorumBytes, uint32(quorum)) //nolint:gosec + buf = append(buf, quorumBytes...) + } + + // Encode group parents + buf = append(buf, byte(len(groupParents))) // numParents (1 byte) + for _, parent := range groupParents { + parentBytes := make([]byte, 4) + binary.BigEndian.PutUint32(parentBytes, uint32(parent)) //nolint:gosec + buf = append(buf, parentBytes...) + } + + // Encode clearRoot (1 byte) + if clearRoot { + buf = append(buf, 0x01) + } else { + buf = append(buf, 0x00) + } + + return hex.EncodeToString(buf) +} + +func (s *TestSuite) SignProposal(proposal *mcmscore.Proposal, inspector sdk.Inspector, keys []*ecdsa.PrivateKey, quorum int) (*mcmscore.Signable, []mcmstypes.Signature, error) { + inspectorsMap := map[mcmstypes.ChainSelector]sdk.Inspector{ + s.chainSelector: inspector, + } + signable, err := mcmscore.NewSignable(proposal, inspectorsMap) + if err != nil { + return nil, nil, err + } + + signatures := make([]mcmstypes.Signature, 0, quorum) + for i := 0; i < len(keys) && i < quorum; i++ { + sig, err := signable.SignAndAppend(mcmscore.NewPrivateKeySigner(keys[i])) + if err != nil { + return nil, nil, err + } + signatures = append(signatures, sig) + } + + return signable, signatures, nil +} diff --git a/e2e/tests/canton/configurer.go b/e2e/tests/canton/configurer.go new file mode 100644 index 000000000..95d828281 --- /dev/null +++ b/e2e/tests/canton/configurer.go @@ -0,0 +1,129 @@ +//go:build e2e + +package canton + +import ( + "slices" + + 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" + + 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.participant.CommandServiceClient, s.participant.UserName, s.participant.Party, cantonsdk.TimelockRoleProposer) + 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.(*apiv2.SubmitAndWaitForTransactionResponse) + s.Require().True(ok) + + // Get transaction and events + transaction := submitResp.GetTransaction() + s.Require().NotNil(transaction, "transaction should not be nil") + + events := transaction.GetEvents() + 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].GetArchived(), "first event should be Archived event") + s.Require().Nil(events[0].GetCreated(), "first event should not be Created event") + s.Require().Equal(s.mcmsContractID, events[0].GetArchived().GetContractId(), "archived contract should be the old MCMS contract") + + // Verify event[1] is Created (new contract) + s.Require().NotNil(events[1].GetCreated(), "second event should be Created event") + s.Require().Nil(events[1].GetArchived(), "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") + + createdTemplateID := cantonsdk.NormalizeTemplateKey(newMCMSTemplateID) + eventTemplateID := cantonsdk.NormalizeTemplateKey(cantonsdk.FormatTemplateID(events[1].GetCreated().GetTemplateId())) + s.Require().Equal(createdTemplateID, eventTemplateID, "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].GetCreated().GetContractId(), "created event contract ID should match returned contract ID") + } +} diff --git a/e2e/tests/canton/executor.go b/e2e/tests/canton/executor.go new file mode 100644 index 000000000..74485e558 --- /dev/null +++ b/e2e/tests/canton/executor.go @@ -0,0 +1,379 @@ +//go:build e2e + +package canton + +import ( + "crypto/ecdsa" + "encoding/json" + "fmt" + "slices" + "testing" + "time" + + 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" + + mcmscore "github.com/smartcontractkit/mcms" + "github.com/smartcontractkit/mcms/sdk" + cantonsdk "github.com/smartcontractkit/mcms/sdk/canton" + mcmstypes "github.com/smartcontractkit/mcms/types" +) + +type MCMSExecutorTestSuite struct { + TestSuite + + // Test signers + signers []*ecdsa.PrivateKey + signerAddrs []common.Address + sortedSigners []*ecdsa.PrivateKey + sortedWallets []*mcmscore.PrivateKeySigner + + // Counter contract for testing ExecuteOp + counterInstanceID string + counterCID string +} + +func TestMCMSExecutorTestSuite(t *testing.T) { + suite.Run(t, new(MCMSExecutorTestSuite)) +} + +// SetupSuite runs before the test suite +func (s *MCMSExecutorTestSuite) SetupSuite() { + s.TestSuite.SetupSuite() + + // Create 3 signers for 2-of-3 multisig + s.signers = make([]*ecdsa.PrivateKey, 3) + for i := 0; i < 3; i++ { + key, err := crypto.GenerateKey() + s.Require().NoError(err) + s.signers[i] = key + } + + // Sort signers by address + signersCopy := make([]*ecdsa.PrivateKey, len(s.signers)) + copy(signersCopy, s.signers) + slices.SortFunc(signersCopy, func(a, b *ecdsa.PrivateKey) int { + addrA := crypto.PubkeyToAddress(a.PublicKey) + addrB := crypto.PubkeyToAddress(b.PublicKey) + return addrA.Cmp(addrB) + }) + s.sortedSigners = signersCopy + s.sortedWallets = make([]*mcmscore.PrivateKeySigner, len(s.sortedSigners)) + + // Derive sorted addresses from sorted signers to ensure they correspond + s.signerAddrs = make([]common.Address, len(s.sortedSigners)) + for i, signer := range s.sortedSigners { + s.sortedWallets[i] = mcmscore.NewPrivateKeySigner(signer) + s.signerAddrs[i] = crypto.PubkeyToAddress(signer.PublicKey) + } + + // Deploy MCMS with config + config := s.create2of3Config() + s.DeployMCMSWithConfig(config) + + // Deploy Counter contract for ExecuteOp tests + s.DeployCounterContract() +} + +func (s *MCMSExecutorTestSuite) create2of3Config() *mcmstypes.Config { + return &mcmstypes.Config{ + Quorum: 2, + Signers: s.signerAddrs, + } +} + +func (s *MCMSExecutorTestSuite) TestSetRootAndExecuteCounterOp() { + ctx := s.T().Context() + + // Create metadata for Canton chain + metadata, err := cantonsdk.NewChainMetadata( + 0, // preOpCount + 1, + s.chainId, + s.proposerMcmsId, + s.mcmsContractID, + false, + ) + s.Require().NoError(err) + + // Build a test proposal with an operation to increment counter + validUntil := time.Now().Add(24 * time.Hour) + opAdditionalFields := cantonsdk.AdditionalFields{ + TargetInstanceId: fmt.Sprintf("%s@%s", s.counterInstanceID, s.participant.Party), + FunctionName: "increment", + OperationData: "", + TargetCid: s.counterCID, + ContractIds: []string{s.counterCID}, + } + opAdditionalFieldsBytes, err := json.Marshal(opAdditionalFields) + s.Require().NoError(err) + + proposal, err := mcmscore.NewProposalBuilder(). + SetVersion("v1"). + SetValidUntil(uint32(validUntil.Unix())). + SetDescription(fmt.Sprintf("Canton ExecuteOp test - %v", validUntil)). + SetOverridePreviousRoot(false). + AddChainMetadata(s.chainSelector, metadata). + AddOperation(mcmstypes.Operation{ + ChainSelector: s.chainSelector, + Transaction: mcmstypes.Transaction{ + To: s.counterCID, + Data: []byte{}, + AdditionalFields: opAdditionalFieldsBytes, + }, + }). + Build() + s.Require().NoError(err) + + // Create inspector and executor + inspector := cantonsdk.NewInspector(s.participant.StateServiceClient, s.participant.Party, cantonsdk.TimelockRoleProposer) + + encoders, err := proposal.GetEncoders() + s.Require().NoError(err) + encoder := encoders[s.chainSelector].(*cantonsdk.Encoder) + + executor, err := cantonsdk.NewExecutor(encoder, inspector, s.participant.CommandServiceClient, s.participant.UserName, s.participant.Party, cantonsdk.TimelockRoleProposer) + s.Require().NoError(err) + + executors := map[mcmstypes.ChainSelector]sdk.Executor{ + s.chainSelector: executor, + } + + // Sign with first 2 sorted signers + _, _, err = s.SignProposal(proposal, inspector, s.sortedSigners[:2], 2) + s.Require().NoError(err) + + // Create executable + executable, err := mcmscore.NewExecutable(proposal, executors) + s.Require().NoError(err) + + // First, set the root + txSetRoot, err := executable.SetRoot(ctx, s.chainSelector) + s.Require().NoError(err) + s.Require().NotEmpty(txSetRoot.Hash) + + // Update contract ID after SetRoot + rawData, ok := txSetRoot.RawData.(map[string]any) + s.Require().True(ok) + newMCMSContractID, ok := rawData["NewMCMSContractID"].(string) + s.Require().True(ok) + s.Require().NotEmpty(newMCMSContractID) + s.mcmsContractID = newMCMSContractID + + s.T().Logf("✅ SetRoot completed, new MCMS CID: %s", s.mcmsContractID) + + // Update proposal with new multisig id + newMetadata, err := cantonsdk.NewChainMetadata( + 0, // preOpCount + 1, // postOp + s.chainId, + s.proposerMcmsId, + s.mcmsContractID, + false, + ) + proposal.ChainMetadata[s.chainSelector] = newMetadata + // Now execute the operation (index 0) + txExecute, err := executable.Execute(ctx, 0) + s.Require().NoError(err) + s.Require().NotEmpty(txExecute.Hash) + + // Verify the counter was incremented by checking transaction events + rawTx, ok := txExecute.RawData.(map[string]any)["RawTx"] + s.Require().True(ok) + submitResp, ok := rawTx.(*apiv2.SubmitAndWaitForTransactionResponse) + s.Require().True(ok) + + s.verifyCounterIncremented(submitResp) + + // Verify MCMS contract was recreated with incremented opCount + foundMCMS := false + transaction := submitResp.GetTransaction() + for _, event := range transaction.GetEvents() { + if createdEv := event.GetCreated(); createdEv != nil { + templateID := cantonsdk.FormatTemplateID(createdEv.GetTemplateId()) + normalized := cantonsdk.NormalizeTemplateKey(templateID) + if normalized == cantonsdk.MCMSTemplateKey { + foundMCMS = true + newMCMSCID := createdEv.GetContractId() + s.Require().NotEmpty(newMCMSCID) + s.Require().NotEqual(s.mcmsContractID, newMCMSCID, "MCMS should be recreated after execute") + s.mcmsContractID = newMCMSCID + s.T().Logf("✅ MCMS contract recreated: %s", s.mcmsContractID) + break + } + } + } + s.Require().True(foundMCMS, "MCMS contract should be recreated after execute") + + s.T().Logf("✅ ExecuteOp completed in tx: %s", txExecute.Hash) +} + +func (s *MCMSExecutorTestSuite) TestSetRootAndExecuteMCMSOp() { + ctx := s.T().Context() + + // Encode the SetConfig operation data to change quorum from 2 to 1 + // For Canton, we need to encode the signers, group quorums, and group parents + groupQuorums := make([]int64, 32) + groupQuorums[0] = 1 // Root group needs 1 signature now + groupParents := make([]int64, 32) + // All groups point to root (0) + + // Convert signers to lowercase hex without 0x prefix (Canton format) + signerAddresses := make([]string, len(s.signerAddrs)) + signerGroups := make([]int64, len(s.signerAddrs)) + for i, addr := range s.signerAddrs { + signerAddresses[i] = addr.Hex()[2:] // Remove 0x prefix, keep checksum + signerGroups[i] = 0 // All in group 0 + } + + // Encode SetConfig params using Canton's encoding format + // This follows the format from chainlink-canton integration tests + operationData := EncodeSetConfigParams(&s.TestSuite, signerAddresses, groupQuorums, groupParents, false) + + // Create metadata for Canton chain + metadata, err := cantonsdk.NewChainMetadata( + 1, // preOpCount + 2, // postOp + s.chainId, + s.proposerMcmsId, + s.mcmsContractID, + false, + ) + s.Require().NoError(err) + + // Build a proposal with SetConfig operation targeting MCMS itself + validUntil := time.Now().Add(24 * time.Hour) + opAdditionalFields := cantonsdk.AdditionalFields{ + TargetInstanceId: "self", + FunctionName: "set_config", + OperationData: operationData, + TargetCid: s.mcmsContractID, + ContractIds: []string{s.mcmsContractID}, + } + opAdditionalFieldsBytes, err := json.Marshal(opAdditionalFields) + s.Require().NoError(err) + + proposal, err := mcmscore.NewProposalBuilder(). + SetVersion("v1"). + SetValidUntil(uint32(validUntil.Unix())). + SetDescription(fmt.Sprintf("Canton SetConfig test - change quorum to 3 - %v", validUntil)). + SetOverridePreviousRoot(false). + AddChainMetadata(s.chainSelector, metadata). + AddOperation(mcmstypes.Operation{ + ChainSelector: s.chainSelector, + Transaction: mcmstypes.Transaction{ + To: s.mcmsContractID, + Data: []byte{}, + AdditionalFields: opAdditionalFieldsBytes, + }, + }). + Build() + s.Require().NoError(err) + + // Create inspector and executor + inspector := cantonsdk.NewInspector(s.participant.StateServiceClient, s.participant.Party, cantonsdk.TimelockRoleProposer) + + encoders, err := proposal.GetEncoders() + s.Require().NoError(err) + encoder := encoders[s.chainSelector].(*cantonsdk.Encoder) + + executor, err := cantonsdk.NewExecutor(encoder, inspector, s.participant.CommandServiceClient, s.participant.UserName, s.participant.Party, cantonsdk.TimelockRoleProposer) + s.Require().NoError(err) + + executors := map[mcmstypes.ChainSelector]sdk.Executor{ + s.chainSelector: executor, + } + + // Sign with first 2 sorted signers + _, _, err = s.SignProposal(proposal, inspector, s.sortedSigners[:2], 2) + s.Require().NoError(err) + + // Create executable + executable, err := mcmscore.NewExecutable(proposal, executors) + s.Require().NoError(err) + + // First, set the root + txSetRoot, err := executable.SetRoot(ctx, s.chainSelector) + s.Require().NoError(err) + s.Require().NotEmpty(txSetRoot.Hash) + + // Update contract ID after SetRoot + rawData, ok := txSetRoot.RawData.(map[string]any) + s.Require().True(ok) + newMCMSContractID, ok := rawData["NewMCMSContractID"].(string) + s.Require().True(ok) + s.Require().NotEmpty(newMCMSContractID) + oldMCMSContractID := s.mcmsContractID + s.mcmsContractID = newMCMSContractID + s.Require().NotEqual(oldMCMSContractID, s.mcmsContractID, "MCMS contract ID should change after SetRoot") + + // Update proposal with new MCMS contract ID in metadata + + s.T().Logf("✅ SetRoot completed, new MCMS CID: %s", s.mcmsContractID) + + newMetadata, err := cantonsdk.NewChainMetadata( + 1, // preOpCount + 2, // postOp + s.chainId, + s.proposerMcmsId, + newMCMSContractID, + false, + ) + s.Require().NoError(err) + // Override proposal metadata with new MCMS contract ID + proposal.ChainMetadata[s.chainSelector] = newMetadata + + // Now execute the SetConfig operation (index 0) + txExecute, err := executable.Execute(ctx, 0) + s.Require().NoError(err) + s.Require().NotEmpty(txExecute.Hash) + + // Verify MCMS contract was recreated with new config + rawTx, ok := txExecute.RawData.(map[string]any)["RawTx"] + s.Require().True(ok) + submitResp, ok := rawTx.(*apiv2.SubmitAndWaitForTransactionResponse) + s.Require().True(ok) + + foundMCMS := false + transaction := submitResp.GetTransaction() + for _, event := range transaction.GetEvents() { + if createdEv := event.GetCreated(); createdEv != nil { + templateID := cantonsdk.FormatTemplateID(createdEv.GetTemplateId()) + normalized := cantonsdk.NormalizeTemplateKey(templateID) + if normalized == cantonsdk.MCMSTemplateKey { + foundMCMS = true + newMCMSCID := createdEv.GetContractId() + s.Require().NotEmpty(newMCMSCID) + s.Require().NotEqual(oldMCMSContractID, newMCMSCID, "MCMS should be recreated after SetConfig") + s.mcmsContractID = newMCMSCID + s.T().Logf("✅ MCMS contract recreated with new config: %s", s.mcmsContractID) + break + } + } + } + s.Require().True(foundMCMS, "MCMS contract should be recreated after SetConfig") + + s.T().Logf("✅ SetConfig operation completed in tx: %s", txExecute.Hash) + + // TODO: Inspect config when ready +} + +// Helper functions +func (s *MCMSExecutorTestSuite) verifyCounterIncremented(submitResp *apiv2.SubmitAndWaitForTransactionResponse) { + // Look for Counter contract in created events + transaction := submitResp.GetTransaction() + for _, event := range transaction.GetEvents() { + if createdEv := event.GetCreated(); createdEv != nil { + templateID := cantonsdk.FormatTemplateID(createdEv.GetTemplateId()) + normalized := cantonsdk.NormalizeTemplateKey(templateID) + if normalized == "MCMS.Counter:Counter" { + // Counter was recreated, which means it was successfully executed + s.T().Log("Counter contract was successfully incremented") + return + } + } + } + s.Fail("Counter contract not found in transaction events") +} diff --git a/e2e/tests/canton/inspector.go b/e2e/tests/canton/inspector.go new file mode 100644 index 000000000..e65cc5c4f --- /dev/null +++ b/e2e/tests/canton/inspector.go @@ -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, cantonsdk.TimelockRoleProposer) +} + +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.participant.CommandServiceClient, s.participant.UserName, s.participant.Party, cantonsdk.TimelockRoleProposer) + 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(4294905160), validUntil, "initial validUntil should be 0xffff0d48") +} + +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)) +} diff --git a/e2e/tests/runner_test.go b/e2e/tests/runner_test.go index a04ff50e7..c20a613e8 100644 --- a/e2e/tests/runner_test.go +++ b/e2e/tests/runner_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/suite" aptose2e "github.com/smartcontractkit/mcms/e2e/tests/aptos" + cantone2e "github.com/smartcontractkit/mcms/e2e/tests/canton" evme2e "github.com/smartcontractkit/mcms/e2e/tests/evm" solanae2e "github.com/smartcontractkit/mcms/e2e/tests/solana" suie2e "github.com/smartcontractkit/mcms/e2e/tests/sui" @@ -49,3 +50,10 @@ func TestTONSuite(t *testing.T) { suite.Run(t, new(tone2e.ExecutionTestSuite)) suite.Run(t, new(tone2e.TimelockInspectionTestSuite)) } + +func TestCantonSuite(t *testing.T) { + suite.Run(t, new(cantone2e.MCMSConfigurerTestSuite)) + suite.Run(t, new(cantone2e.MCMSInspectorTestSuite)) + // TODO: Proposals need to be updated to use Timelock instead of direct execution + // suite.Run(t, new(cantone2e.MCMSExecutorTestSuite)) +} diff --git a/e2e/tests/setup.go b/e2e/tests/setup.go index 65adf90f1..067cd3efc 100644 --- a/e2e/tests/setup.go +++ b/e2e/tests/setup.go @@ -43,6 +43,7 @@ type Config struct { AptosChain *blockchain.Input `toml:"aptos_config"` SuiChain *blockchain.Input `toml:"sui_config"` TonChain *blockchain.Input `toml:"ton_config"` + CantonChain *blockchain.Input `toml:"canton_config"` Settings struct { PrivateKeys []string `toml:"private_keys"` @@ -64,6 +65,7 @@ type TestSetup struct { SuiNodeURL string TonClient *ton.APIClient TonBlockchain *blockchain.Output + CantonBlockchain *blockchain.Output Config } @@ -236,6 +238,21 @@ func InitializeSharedTestSetup(t *testing.T) *TestSetup { t.Logf("Initialized TON RPC client @ %s", nodeURL) } + var ( + cantonBlockchainOutput *blockchain.Output + ) + if in.CantonChain != nil { + // Use blockchain network setup (fallback) + ports := freeport.GetN(t, 2) + port := ports[0] + faucetPort := ports[1] + in.CantonChain.Port = strconv.Itoa(port) + in.CantonChain.FaucetPort = strconv.Itoa(faucetPort) + + cantonBlockchainOutput, err = blockchain.NewBlockchainNetwork(in.CantonChain) + require.NoError(t, err, "Failed to initialize Canton blockchain") + } + sharedSetup = &TestSetup{ ClientA: ethClientA, ClientB: ethClientB, @@ -249,6 +266,7 @@ func InitializeSharedTestSetup(t *testing.T) *TestSetup { SuiNodeURL: suiNodeURL, TonClient: tonClient, TonBlockchain: tonBlockchainOutput, + CantonBlockchain: cantonBlockchainOutput, Config: *in, } }) diff --git a/factory.go b/factory.go index 37cc1cb1a..5a77148e2 100644 --- a/factory.go +++ b/factory.go @@ -7,6 +7,7 @@ import ( "github.com/smartcontractkit/mcms/sdk" "github.com/smartcontractkit/mcms/sdk/aptos" + "github.com/smartcontractkit/mcms/sdk/canton" "github.com/smartcontractkit/mcms/sdk/evm" "github.com/smartcontractkit/mcms/sdk/solana" "github.com/smartcontractkit/mcms/sdk/sui" @@ -59,6 +60,12 @@ func newEncoder( txCount, overridePreviousRoot, ) + case cselectors.FamilyCanton: + encoder = canton.NewEncoder( + csel, + txCount, + overridePreviousRoot, + ) } return encoder, nil diff --git a/go.mod b/go.mod index cb0d62c94..b5cbd11e5 100644 --- a/go.mod +++ b/go.mod @@ -1,36 +1,49 @@ module github.com/smartcontractkit/mcms -go 1.25.5 +go 1.25.6 //nolint:gomoddirectives // allow replace directive replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 +// Coming from chainlink-deployments-framework +replace github.com/fbsobreira/gotron-sdk => github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20251014120029-d73d15cc23f7 + +replace ( + github.com/digital-asset/dazl-client/v8 => github.com/noders-team/dazl-client/v8 v8.7.1-2 + github.com/smartcontractkit/go-daml => github.com/smartcontractkit/go-daml v0.0.0-20260209201116-eac8a15b0b35 +) + 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 github.com/go-playground/validator/v10 v10.28.0 github.com/google/go-cmp v0.7.0 + github.com/google/uuid v1.6.0 github.com/joho/godotenv v1.5.1 github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52 github.com/samber/lo v1.52.0 - github.com/smartcontractkit/chain-selectors v1.0.89 - github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2 + github.com/smartcontractkit/chain-selectors v1.0.92 + github.com/smartcontractkit/chainlink-aptos v0.0.0-20251212131933-e5e85d6fa4d3 + github.com/smartcontractkit/chainlink-canton v0.0.0-20260210001114-c07a75050603 + github.com/smartcontractkit/chainlink-canton/integration-tests v0.0.0-20260210001114-c07a75050603 github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 - github.com/smartcontractkit/chainlink-testing-framework/framework v0.12.1 + github.com/smartcontractkit/chainlink-testing-framework/framework v0.13.9 github.com/smartcontractkit/chainlink-ton v0.0.0-20260204205804-642f6ebe4e7e github.com/smartcontractkit/freeport v0.1.3-0.20250716200817-cb5dfd0e369e + github.com/smartcontractkit/go-daml v0.6.0 github.com/spf13/cast v1.10.0 github.com/stretchr/testify v1.11.1 github.com/xssnick/tonutils-go v1.14.1 github.com/zksync-sdk/zksync2-go v1.1.1-0.20250620124214-2c742ee399c6 go.uber.org/zap v1.27.1 golang.org/x/crypto v0.47.0 - golang.org/x/tools v0.40.0 + golang.org/x/tools v0.41.0 gotest.tools/v3 v3.5.2 ) @@ -38,26 +51,28 @@ require ( dario.cat/mergo v1.0.2 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect - github.com/BurntSushi/toml v1.5.0 // indirect + github.com/BurntSushi/toml v1.6.0 // indirect github.com/DataDog/zstd v1.5.6 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/VictoriaMetrics/fastcache v1.13.0 // indirect github.com/XSAM/otelsql v0.37.0 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/avast/retry-go/v4 v4.6.1 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.20.0 // indirect + github.com/bits-and-blooms/bitset v1.24.0 // indirect github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd v0.24.2 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/btcsuite/btcd/btcutil v1.1.5 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect + github.com/btcsuite/btcd/btcutil v1.1.6 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect - github.com/btcsuite/btcutil v1.0.2 // indirect + github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.16.1 // indirect github.com/cloudevents/sdk-go/v2 v2.16.1 // indirect @@ -82,16 +97,18 @@ require ( github.com/dchest/siphash v1.2.3 // indirect 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/distribution/reference v0.6.0 // indirect - github.com/docker/docker v28.3.3+incompatible // indirect - github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/docker v28.5.1+incompatible // indirect + github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/ebitengine/purego v0.8.4 // indirect + github.com/ebitengine/purego v0.9.0 // indirect github.com/emicklei/dot v1.6.2 // indirect github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect github.com/ethereum/go-verkle v0.2.2 // indirect github.com/fatih/color v1.18.0 // indirect + github.com/fbsobreira/gotron-sdk v0.0.0-20250403083053-2943ce8c759b // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/ferranbt/fastssz v0.1.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect @@ -105,20 +122,21 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-resty/resty/v2 v2.16.3 // indirect + github.com/go-resty/resty/v2 v2.17.1 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gofrs/flock v0.12.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v1.0.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/gorilla/rpc v1.2.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/graph-gophers/graphql-go v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.7.0 // indirect @@ -143,14 +161,14 @@ require ( github.com/jmoiron/sqlx v1.4.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.1 // indirect - github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/klauspost/compress v1.18.2 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect - github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect + github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -173,9 +191,9 @@ require ( github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oapi-codegen/runtime v1.1.2 // indirect github.com/oklog/run v1.2.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/onsi/gomega v1.34.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pelletier/go-toml v1.9.5 // indirect @@ -190,34 +208,36 @@ require ( github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.23.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v1.20.99 // indirect github.com/prometheus/procfs v0.16.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect - github.com/rs/zerolog v1.33.0 // indirect + github.com/rs/zerolog v1.34.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/scylladb/go-reflectx v1.0.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/shirou/gopsutil/v4 v4.25.5 // indirect + github.com/shirou/gopsutil/v4 v4.25.9 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 // indirect + github.com/smartcontractkit/chainlink-common v0.9.6-0.20260122165924-94e0fad14fe8 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect + github.com/smartcontractkit/chainlink-deployments-framework v0.79.0 // indirect github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect + github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9 // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect - github.com/smartcontractkit/libocr v0.0.0-20250912173940-f3ab0246e23d // indirect + github.com/smartcontractkit/libocr v0.0.0-20251212213002-0a5e2f907dda // indirect github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 // indirect github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect - github.com/testcontainers/testcontainers-go v0.38.0 // indirect + github.com/testcontainers/testcontainers-go v0.39.0 // indirect github.com/tidwall/gjson v1.18.0 // indirect - github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/match v1.2.0 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/numcpus v0.10.0 // indirect @@ -231,37 +251,37 @@ require ( go.mongodb.org/mongo-driver v1.17.2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.36.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.13.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 // indirect - go.opentelemetry.io/otel/log v0.13.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/sdk v1.38.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect - go.opentelemetry.io/proto/otlp v1.6.0 // indirect + go.opentelemetry.io/otel/log v0.15.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/sdk v1.39.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.15.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect - golang.org/x/net v0.48.0 // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect + golang.org/x/net v0.49.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/term v0.39.0 // indirect golang.org/x/text v0.33.0 // indirect - golang.org/x/time v0.12.0 // indirect + golang.org/x/time v0.14.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect - google.golang.org/grpc v1.77.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect + google.golang.org/grpc v1.78.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 6ec8cf9cb..80ba2f48f 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj4 github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY= github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= @@ -21,6 +21,7 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/XSAM/otelsql v0.37.0 h1:ya5RNw028JW0eJW8Ma4AmoKxAYsJSGuNVbC7F1J457A= @@ -30,6 +31,8 @@ github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKS github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/apache/arrow-go/v18 v18.3.1 h1:oYZT8FqONiK74JhlH3WKVv+2NKYoyZ7C2ioD4Dj3ixk= github.com/apache/arrow-go/v18 v18.3.1/go.mod h1:12QBya5JZT6PnBihi5NJTzbACrDGXYkrgjujz3MRQXU= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/aptos-labs/aptos-go-sdk v1.11.0 h1:vIL1hpjECUiu7zMl9Wz6VV8ttXsrDqKUj0HxoeaIER4= github.com/aptos-labs/aptos-go-sdk v1.11.0/go.mod h1:8YvYwRg93UcG6pTStCpZdYiscCtKh51sYfeLgIy/41c= github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk= @@ -41,12 +44,13 @@ github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= -github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bits-and-blooms/bitset v1.24.0 h1:H4x4TuulnokZKvHLfzVRTHJfFfnHEeSYJizujEZvmAM= +github.com/bits-and-blooms/bitset v1.24.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= github.com/block-vision/sui-go-sdk v1.1.4 h1:1PPgYxQjo1P9UCgFOPTvDCuGEglRL32NwjKPulR4FQk= github.com/block-vision/sui-go-sdk v1.1.4/go.mod h1:t8mWASwfyv+EyqHGO9ZrcDiCJWGOFEXqq50TMJ8GQco= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= @@ -54,20 +58,21 @@ github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c= +github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= @@ -81,8 +86,8 @@ github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMU github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= -github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= @@ -141,14 +146,15 @@ github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOV github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= github.com/cucumber/godog v0.15.1 h1:rb/6oHDdvVZKS66hrhpjFQFHjthFSrQBCOI1LwshNTI= github.com/cucumber/godog v0.15.1/go.mod h1:qju+SQDewOljHuq9NSM66s0xEhogx0q30flfxL4WUk8= github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -167,16 +173,17 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjY github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= -github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM= +github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= -github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.9.0 h1:mh0zpKBIXDceC63hpvPuGLiJ8ZAa3DfrFTudmfi8A4k= +github.com/ebitengine/purego v0.9.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -220,8 +227,13 @@ github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdF github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= +github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-json-experiment/json v0.0.0-20250223041408-d3c622f1b874 h1:F8d1AJ6M9UQCavhwmO6ZsrYLfG8zVFWfEfMS2MXPkSY= @@ -237,6 +249,12 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -245,10 +263,11 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688= github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU= -github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E= -github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4= +github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= @@ -263,6 +282,8 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1 github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -284,6 +305,7 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -303,19 +325,22 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/graph-gophers/graphql-go v1.5.0 h1:fDqblo50TEpD0LY7RXk/LFVYEVqo3+tXMNMPSVXA1yc= github.com/graph-gophers/graphql-go v1.5.0/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -415,21 +440,24 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52 h1:msKODTL1m0wigztaqILOtla9HeW1ciscYG4xjLtvk5I= github.com/karalabe/hid v1.0.1-0.20240306101548-573246063e52/go.mod h1:qk1sX/IBgppQNcGCRoj90u6EGC056EBoIc1oEjCWla8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= -github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= -github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= -github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= +github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -443,6 +471,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= +github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= @@ -455,16 +489,22 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= -github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= +github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/marcboeker/go-duckdb v1.8.5 h1:tkYp+TANippy0DaIOP5OEfBEwbUINqiFqgwMQ44jME0= github.com/marcboeker/go-duckdb v1.8.5/go.mod h1:6mK7+WQE4P4u5AFLvVBmhFxY5fvhymFptghgJX6B+/8= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -472,6 +512,8 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -514,6 +556,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= @@ -522,9 +566,17 @@ github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/noders-team/dazl-client/v8 v8.7.1-2 h1:a8PXw76lE6ozb/MVW/Akw56qXrPM6xUnpz/rT2MXzTA= +github.com/noders-team/dazl-client/v8 v8.7.1-2/go.mod h1:q1KevCJ8FpH8je2MnnjN8/QUfhstB4fKpyKyqDtqFh0= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= +github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= @@ -543,8 +595,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= +github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -554,6 +606,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= @@ -587,8 +641,8 @@ github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v1.20.99 h1:vZEybF3CT0t6L0UjsOtHRML7vuIglHocmvJMMH/se4M= +github.com/prometheus/common v1.20.99/go.mod h1:VX44Tebe4qpuTK+MQWg25h4fJGKBqzObSdxuB7y8K/Y= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/prysmaticlabs/gohashtree v0.0.4-beta h1:H/EbCuXPeTV3lpKeXGPpEV9gsUpkqOOVnWapUyeWro4= @@ -605,11 +659,11 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= @@ -621,8 +675,8 @@ github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cj github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc= -github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= +github.com/shirou/gopsutil/v4 v4.25.9 h1:JImNpf6gCVhKgZhtaAHJ0serfFGtlfIlSC08eaKdTrU= +github.com/shirou/gopsutil/v4 v4.25.9/go.mod h1:gxIxoC+7nQRwUl/xNhutXlD8lq+jxTgpIkEf3rADHL8= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -634,38 +688,51 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartcontractkit/chain-selectors v1.0.89 h1:L9oWZGqQXWyTPnC6ODXgu3b0DFyLmJ9eHv+uJrE9IZY= -github.com/smartcontractkit/chain-selectors v1.0.89/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= -github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2 h1:vGdeMwHO3ow88HvxfhA4DDPYNY0X9jmdux7L83UF/W8= -github.com/smartcontractkit/chainlink-aptos v0.0.0-20251024142440-51f2ad2652a2/go.mod h1:iteU0WORHkArACVh/HoY/1bipV4TcNcJdTmom9uIT0E= +github.com/smartcontractkit/chain-selectors v1.0.92 h1:cEapBC3DBDKNAZddp01Xj1qAArBNUJdR/4JYhp1NKxY= +github.com/smartcontractkit/chain-selectors v1.0.92/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= +github.com/smartcontractkit/chainlink-aptos v0.0.0-20251212131933-e5e85d6fa4d3 h1:bbVSKb++R+rpLkydNvyS4nZPNkcjtolUuFC8YVwtMVk= +github.com/smartcontractkit/chainlink-aptos v0.0.0-20251212131933-e5e85d6fa4d3/go.mod h1:OywVThRaVXwknATT2B8QAwjOJ1LoYBB9bTsmRpf6RPw= +github.com/smartcontractkit/chainlink-canton v0.0.0-20260210001114-c07a75050603 h1:nOoFhjYBPJRY0vk8vElgJJbmVHLIIWODOLovY0zw1Yc= +github.com/smartcontractkit/chainlink-canton v0.0.0-20260210001114-c07a75050603/go.mod h1:YtmPpxrWQTgzy4F/ObYTvR4EkjFuc8KeXFbka993Kbk= +github.com/smartcontractkit/chainlink-canton/integration-tests v0.0.0-20260210001114-c07a75050603 h1:KpoERuSiEjOXZiNwdqB2jLHsfvtdUL3xAUng1m9Cnf0= +github.com/smartcontractkit/chainlink-canton/integration-tests v0.0.0-20260210001114-c07a75050603/go.mod h1:6e5xlgAPM7PhYAaZ6jyplkqlJ1koJkIky1w2HDBbNu4= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 h1:jkChf04hhdiMBApbb+lLDxHMY62Md6UeM7v++GSw3K8= github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139/go.mod h1:wuhagkM/lU0GbV2YcrROOH0GlsfXJYwm6qmpa4CK70w= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 h1:tw3K4UkH5XfW5SoyYkvAlbzrccoGSLdz/XkxD6nyGC8= github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139/go.mod h1:1WcontO9PeuKdUf5HXfs3nuICtzUvFNnyCmrHkTCF9Y= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96 h1:ZnBBOLyMLJjgQQm7WRJl8sA9Q2RhwagJ+WR62VnA3MY= -github.com/smartcontractkit/chainlink-common v0.9.6-0.20260114142648-bd9e1b483e96/go.mod h1:DAwaVSiQMgAsCjHa8nOnIAM9GixuIQWsgEZFGpf3JxE= +github.com/smartcontractkit/chainlink-common v0.9.6-0.20260122165924-94e0fad14fe8 h1:kDHw2ta45azZGdfLldVloLAbo+JS3zIXXRlAIO8f1js= +github.com/smartcontractkit/chainlink-common v0.9.6-0.20260122165924-94e0fad14fe8/go.mod h1:Eg5rz/fQINjR9H0TxHw7j+zGZeYxprUpEQZzC5JGHG4= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= +github.com/smartcontractkit/chainlink-deployments-framework v0.79.0 h1:+2chX5WzpkSNsazvPUMTJ2CCFNNt+yEWHiJAfPizaAA= +github.com/smartcontractkit/chainlink-deployments-framework v0.79.0/go.mod h1:mWB9sP9T6wWOTkrF19v7JYeZ5WRE7whhRd5dHDnxdUM= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9 h1:QRWXJusIj/IRY5Pl3JclNvDre0cZPd/5NbILwc4RV2M= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20251124151448-0448aefdaab9/go.mod h1:jUC52kZzEnWF9tddHh85zolKybmLpbQ1oNA4FjOHt1Q= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b h1:QuI6SmQFK/zyUlVWEf0GMkiUYBPY4lssn26nKSd/bOM= github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9 h1:KyPROV+v7P8VdiU7JhVuGLcDlEBsURSpQmSCgNBTY+s= github.com/smartcontractkit/chainlink-sui v0.0.0-20260205175622-33e65031f9a9/go.mod h1:KpEWZJMLwbdMHeHQz9rbkES0vRrx4nk6OQXyhlHb9/8= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.12.1 h1:Ld3OrOQfLubJ+os0/oau2V6RISgsEdBg+Q002zkgXpQ= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.12.1/go.mod h1:r6KXRM1u9ch5KFR2jspkgtyWEC1X+gxPCL8mR63U990= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.13.9 h1:V4Uk2UJqySd+7jwcRuY2l0Xq6ni4zr52C9I8TawX3nM= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.13.9/go.mod h1:RAr+u340HtgzqHcZhAR7qi3ILgaJmh3nNRqS9nOms6E= github.com/smartcontractkit/chainlink-ton v0.0.0-20260204205804-642f6ebe4e7e h1:0tN41HRIrNAVr5Chr8xynwpJNJaYMqGxqlIu+E7SOG8= github.com/smartcontractkit/chainlink-ton v0.0.0-20260204205804-642f6ebe4e7e/go.mod h1:IZvH2r16xcQvVLB7AtjU112wnHfEku+29OlI1vCQHCQ= +github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9 h1:7Ut0g+Pdm+gcu2J/Xv8OpQOVf7uLGErMX8yhC4b4tIA= +github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20251014143056-a0c6328c91e9/go.mod h1:h9hMs6K4hT1+mjYnJD3/SW1o7yC/sKjNi0Qh8hLfiCE= +github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20251014120029-d73d15cc23f7 h1:qPGcryHOEipripujMtsip++fmVbJhyqO6eAGEq85r48= +github.com/smartcontractkit/chainlink-tron/relayer/gotron-sdk v0.0.5-0.20251014120029-d73d15cc23f7/go.mod h1:ea1LESxlSSOgc2zZBqf1RTkXTMthHaspdqUHd7W4lF0= github.com/smartcontractkit/freeport v0.1.3-0.20250716200817-cb5dfd0e369e h1:Hv9Mww35LrufCdM9wtS9yVi/rEWGI1UnjHbcKKU0nVY= github.com/smartcontractkit/freeport v0.1.3-0.20250716200817-cb5dfd0e369e/go.mod h1:T4zH9R8R8lVWKfU7tUvYz2o2jMv1OpGCdpY2j2QZXzU= +github.com/smartcontractkit/go-daml v0.0.0-20260209201116-eac8a15b0b35 h1:KGL2CpXWsgro/jZrXcBxakyLNfi4Kl1swWSdYnc2PJI= +github.com/smartcontractkit/go-daml v0.0.0-20260209201116-eac8a15b0b35/go.mod h1:K4CKO/jicmLX8mWc5AawF7XT+veiQQjNwA+IdqtAex8= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= -github.com/smartcontractkit/libocr v0.0.0-20250912173940-f3ab0246e23d h1:LokA9PoCNb8mm8mDT52c3RECPMRsGz1eCQORq+J3n74= -github.com/smartcontractkit/libocr v0.0.0-20250912173940-f3ab0246e23d/go.mod h1:Acy3BTBxou83ooMESLO90s8PKSu7RvLCzwSTbxxfOK0= +github.com/smartcontractkit/libocr v0.0.0-20251212213002-0a5e2f907dda h1:OjM+79FRuVZlj0Qd4y+q8Xmz/tEn5y8npqmiQiMMj+w= +github.com/smartcontractkit/libocr v0.0.0-20251212213002-0a5e2f907dda/go.mod h1:oJkBKVn8zoBQm7Feah9CiuEHyCqAhnp1LJBzrvloQtM= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= 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/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= @@ -698,12 +765,13 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= -github.com/testcontainers/testcontainers-go v0.38.0 h1:d7uEapLcv2P8AvH8ahLqDMMxda2W9gQN1nRbHS28HBw= -github.com/testcontainers/testcontainers-go v0.38.0/go.mod h1:C52c9MoHpWO+C4aqmgSU+hxlR5jlEayWtgYrb8Pzz1w= +github.com/testcontainers/testcontainers-go v0.39.0 h1:uCUJ5tA+fcxbFAB0uP3pIK3EJ2IjjDUHFSZ1H1UxAts= +github.com/testcontainers/testcontainers-go v0.39.0/go.mod h1:qmHpkG7H5uPf/EvOORKvS6EuDkBUPE3zpVGaH9NL7f8= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= +github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -719,9 +787,15 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0= +github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= @@ -745,23 +819,23 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2/go.mod h1:DvPtKE63knkDVP88qpatBj81JxN+w1bqfVbsbCbj1WY= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2 h1:tPLwQlXbJ8NSOfZc4OkgU5h2A38M4c9kfHSVc4PFQGs= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.12.2/go.mod h1:QTnxBwT/1rBIgAG1goq6xMydfYOBKU6KTiYF4fp5zL8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 h1:zwdo1gS2eH26Rg+CoqVQpEK1h8gvt5qyU5Kk5Bixvow= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0/go.mod h1:rUKCPscaRWWcqGT6HnEmYrK+YNe5+Sw64xgQTOJ5b30= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.36.0 h1:gAU726w9J8fwr4qRDqu1GYMNNs4gXrU+Pv20/N1UpB4= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.36.0/go.mod h1:RboSDkp7N292rgu+T0MgVt2qgFGu6qa1RpZDOtpL76w= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ= go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.13.0 h1:yEX3aC9KDgvYPhuKECHbOlr5GLwH6KTjLJ1sBSkkxkc= @@ -770,23 +844,23 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1x go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= -go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls= -go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/log v0.13.0 h1:I3CGUszjM926OphK8ZdzF+kLqFvfRY/IIoFq/TjwfaQ= -go.opentelemetry.io/otel/sdk/log v0.13.0/go.mod h1:lOrQyCCXmpZdN7NchXb6DOZZa1N5G1R2tm5GMMTpDBw= +go.opentelemetry.io/otel/log v0.15.0 h1:0VqVnc3MgyYd7QqNVIldC3dsLFKgazR6P3P3+ypkyDY= +go.opentelemetry.io/otel/log v0.15.0/go.mod h1:9c/G1zbyZfgu1HmQD7Qj84QMmwTp2QCQsZH1aeoWDE4= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/log v0.15.0 h1:WgMEHOUt5gjJE93yqfqJOkRflApNif84kxoHWS9VVHE= +go.opentelemetry.io/otel/sdk/log v0.15.0/go.mod h1:qDC/FlKQCXfH5hokGsNg9aUBGMJQsrUyeOiW5u+dKBQ= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0 h1:9yio6AFZ3QD9j9oqshV1Ibm9gPLlHNxurno5BreMtIA= go.opentelemetry.io/otel/sdk/log/logtest v0.13.0/go.mod h1:QOGiAJHl+fob8Nu85ifXfuQYmJTFAvcrxL6w5/tu168= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= -go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -821,7 +895,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -834,8 +910,8 @@ golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZP golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -848,8 +924,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= -golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -863,7 +939,9 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -879,8 +957,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -912,11 +990,13 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -945,8 +1025,8 @@ golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20251208220230-2638a1023523 h1:H52Mhyrc44wBgLTGzq6+0cmuVuF3LURCSXsLMOqfFos= -golang.org/x/telemetry v0.0.0-20251208220230-2638a1023523/go.mod h1:ArQvPJS723nJQietgilmZA+shuB3CZxH1n2iXq9VSfs= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 h1:O1cMQHRfwNpDfDJerqRoE2oD+AFlyid87D40L/OkkJo= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -971,8 +1051,10 @@ golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -984,6 +1066,7 @@ golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -993,8 +1076,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= -golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1004,8 +1087,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1015,16 +1098,16 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3 h1:X9z6obt+cWRX8XjDVOn+SZWhWe5kZHm46TThU9j+jss= google.golang.org/genproto/googleapis/api v0.0.0-20260114163908-3f89685c29c3/go.mod h1:dd646eSK+Dk9kxVBl1nChEOhJPtMXriCcVb4x3o6J+E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/sdk/canton/chain_metadata.go b/sdk/canton/chain_metadata.go new file mode 100644 index 000000000..620174884 --- /dev/null +++ b/sdk/canton/chain_metadata.go @@ -0,0 +1,108 @@ +package canton + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/smartcontractkit/mcms/types" +) + +type TimelockRole uint8 + +func (t TimelockRole) String() string { + switch t { + case TimelockRoleBypasser: + return "Bypasser" + case TimelockRoleProposer: + return "Proposer" + case TimelockRoleCanceller: + return "Canceller" + } + + return "unknown" +} + +func (t TimelockRole) Byte() uint8 { + return uint8(t) +} + +const ( + TimelockRoleBypasser TimelockRole = iota + TimelockRoleCanceller + TimelockRoleProposer +) + +// AdditionalFieldsMetadata represents the Canton-specific metadata fields +type AdditionalFieldsMetadata struct { + ChainId int64 `json:"chainId"` + MultisigId string `json:"multisigId"` + PreOpCount uint64 `json:"preOpCount"` + PostOpCount uint64 `json:"postOpCount"` + OverridePreviousRoot bool `json:"overridePreviousRoot"` +} + +func (f AdditionalFieldsMetadata) Validate() error { + if f.ChainId == 0 { + return errors.New("chainId is required") + } + if f.MultisigId == "" { + return errors.New("multisigId is required") + } + if f.PostOpCount < f.PreOpCount { + return errors.New("postOpCount must be >= preOpCount") + } + + return nil +} + +// ValidateChainMetadata validates Canton chain metadata +func ValidateChainMetadata(metadata types.ChainMetadata) error { + var additionalFields AdditionalFieldsMetadata + if err := json.Unmarshal(metadata.AdditionalFields, &additionalFields); err != nil { + return fmt.Errorf("unable to unmarshal additional fields: %w", err) + } + + if err := additionalFields.Validate(); err != nil { + return fmt.Errorf("additional fields are invalid: %w", err) + } + + return nil +} + +// NewChainMetadata creates new Canton chain metadata +func NewChainMetadata( + preOpCount uint64, + postOpCount uint64, + chainId int64, + multisigId string, + mcmsContractID string, + overridePreviousRoot bool, +) (types.ChainMetadata, error) { + if mcmsContractID == "" { + return types.ChainMetadata{}, errors.New("MCMS contract ID is required") + } + + additionalFields := AdditionalFieldsMetadata{ + ChainId: chainId, + MultisigId: multisigId, + PreOpCount: preOpCount, + PostOpCount: postOpCount, + OverridePreviousRoot: overridePreviousRoot, + } + + if err := additionalFields.Validate(); err != nil { + return types.ChainMetadata{}, fmt.Errorf("additional fields are invalid: %w", err) + } + + additionalFieldsBytes, err := json.Marshal(additionalFields) + if err != nil { + return types.ChainMetadata{}, fmt.Errorf("unable to marshal additional fields: %w", err) + } + + return types.ChainMetadata{ + StartingOpCount: preOpCount, + AdditionalFields: additionalFieldsBytes, + MCMAddress: mcmsContractID, + }, nil +} diff --git a/sdk/canton/configurer.go b/sdk/canton/configurer.go new file mode 100644 index 000000000..a723874a8 --- /dev/null +++ b/sdk/canton/configurer.go @@ -0,0 +1,140 @@ +package canton + +import ( + "context" + "fmt" + "strings" + + apiv2 "github.com/digital-asset/dazl-client/v8/go/api/com/daml/ledger/api/v2" + "github.com/google/uuid" + cselectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/go-daml/pkg/service/ledger" + + "github.com/smartcontractkit/chainlink-canton/bindings/mcms" + cantontypes "github.com/smartcontractkit/go-daml/pkg/types" + "github.com/smartcontractkit/mcms/sdk" + "github.com/smartcontractkit/mcms/types" +) + +var _ sdk.Configurer = &Configurer{} + +type Configurer struct { + client apiv2.CommandServiceClient + userId string + party string + role TimelockRole +} + +func NewConfigurer(client apiv2.CommandServiceClient, userId string, party string, role TimelockRole) (*Configurer, error) { + return &Configurer{ + client: client, + userId: userId, + party: party, + role: role, + }, nil +} + +func (c Configurer) SetConfig(ctx context.Context, mcmsAddr string, cfg *types.Config, clearRoot bool) (types.TransactionResult, error) { + groupQuorum, groupParents, signerAddresses, signerGroups, err := sdk.ExtractSetConfigInputs(cfg) + if err != nil { + return types.TransactionResult{}, fmt.Errorf("unable to extract set config inputs: %w", err) + } + + signers := make([]mcms.SignerInfo, len(signerAddresses)) + for i, addr := range signerAddresses { + addrStr := strings.ToLower(addr.String()) + addrStr = strings.TrimPrefix(addrStr, "0x") + signers[i] = mcms.SignerInfo{ + SignerAddress: cantontypes.TEXT(addrStr), + SignerGroup: cantontypes.INT64(signerGroups[i]), + SignerIndex: cantontypes.INT64(i), + } + } + + groupQuorumsTyped := make([]cantontypes.INT64, len(groupQuorum)) + for i, q := range groupQuorum { + groupQuorumsTyped[i] = cantontypes.INT64(q) + } + + groupParentsTyped := make([]cantontypes.INT64, len(groupParents)) + for i, p := range groupParents { + groupParentsTyped[i] = cantontypes.INT64(p) + } + + input := mcms.SetConfig{ + TargetRole: mcms.Role(c.role.String()), + NewSigners: signers, + NewGroupQuorums: groupQuorumsTyped, + NewGroupParents: groupParentsTyped, + ClearRoot: cantontypes.BOOL(clearRoot), + } + // Build exercise command using generated bindings + mcmsContract := mcms.MCMS{} + exerciseCmd := mcmsContract.SetConfig(mcmsAddr, input) + + // Parse template ID + packageID, moduleName, entityName, err := parseTemplateIDFromString(mcmsContract.GetTemplateID()) + if err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to parse template ID: %w", err) + } + + // Convert input to choice argument + choiceArgument := ledger.MapToValue(input) + + commandID := uuid.Must(uuid.NewUUID()).String() + submitResp, err := c.client.SubmitAndWaitForTransaction(ctx, &apiv2.SubmitAndWaitForTransactionRequest{ + Commands: &apiv2.Commands{ + WorkflowId: "mcms-set-config", + CommandId: commandID, + ActAs: []string{c.party}, + Commands: []*apiv2.Command{{ + Command: &apiv2.Command_Exercise{ + Exercise: &apiv2.ExerciseCommand{ + TemplateId: &apiv2.Identifier{ + PackageId: packageID, + ModuleName: moduleName, + EntityName: entityName, + }, + ContractId: exerciseCmd.ContractID, + Choice: exerciseCmd.Choice, + ChoiceArgument: choiceArgument, + }, + }, + }}, + }, + }) + if err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to set config: %w", err) + } + + // Extract NEW MCMS CID from Created event + newMCMSContractID := "" + newMCMSTemplateID := "" + transaction := submitResp.GetTransaction() + for _, ev := range transaction.GetEvents() { + if createdEv := ev.GetCreated(); createdEv != nil { + templateID := formatTemplateID(createdEv.GetTemplateId()) + normalized := NormalizeTemplateKey(templateID) + if normalized == MCMSTemplateKey { + newMCMSContractID = createdEv.GetContractId() + newMCMSTemplateID = templateID + break + } + } + } + + if newMCMSContractID == "" { + return types.TransactionResult{}, fmt.Errorf("set-config tx had no Created MCMS event; refusing to continue with old CID=%s", mcmsAddr) + } + + return types.TransactionResult{ + Hash: "tx.Digest", + ChainFamily: cselectors.FamilyCanton, + RawData: map[string]any{ + "NewMCMSContractID": newMCMSContractID, + "NewMCMSTemplateID": newMCMSTemplateID, + "RawTx": submitResp, + }, + }, nil + +} diff --git a/sdk/canton/encoder.go b/sdk/canton/encoder.go new file mode 100644 index 000000000..c4f8ff3d6 --- /dev/null +++ b/sdk/canton/encoder.go @@ -0,0 +1,128 @@ +package canton + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/smartcontractkit/mcms/sdk" + "github.com/smartcontractkit/mcms/types" +) + +// AdditionalFields represents the additional fields in Canton MCMS operations +type AdditionalFields struct { + TargetInstanceId string `json:"targetInstanceId"` + FunctionName string `json:"functionName"` + OperationData string `json:"operationData"` + TargetCid string `json:"targetCid"` + ContractIds []string `json:"contractIds"` +} + +var _ sdk.Encoder = &Encoder{} + +type Encoder struct { + ChainSelector types.ChainSelector + TxCount uint64 + OverridePreviousRoot bool +} + +func NewEncoder( + chainSelector types.ChainSelector, + txCount uint64, + overridePreviousRoot bool, +) *Encoder { + return &Encoder{ + ChainSelector: chainSelector, + TxCount: txCount, + OverridePreviousRoot: overridePreviousRoot, + } +} + +// HashOperation hashes an operation to get its Merkle leaf +// Matches Canton's hashOpLeafNative from Crypto.daml +func (e *Encoder) HashOperation(opCount uint32, metadata types.ChainMetadata, op types.Operation) (common.Hash, error) { + // Unmarshal Canton-specific metadata + var metadataFields AdditionalFieldsMetadata + if err := json.Unmarshal(metadata.AdditionalFields, &metadataFields); err != nil { + return common.Hash{}, fmt.Errorf("failed to unmarshal metadata additional fields: %w", err) + } + + // Unmarshal Canton-specific operation fields + var opFields AdditionalFields + if err := json.Unmarshal(op.Transaction.AdditionalFields, &opFields); err != nil { + return common.Hash{}, fmt.Errorf("failed to unmarshal operation additional fields: %w", err) + } + + // Build the encoded data following Canton's hashOpLeafNative: + encoded := padLeft32(intToHex(int(metadataFields.ChainId))) + + asciiToHex(metadataFields.MultisigId) + + padLeft32(intToHex(int(opCount))) + + asciiToHex(opFields.TargetInstanceId) + + asciiToHex(opFields.FunctionName) + + opFields.OperationData + + // Decode hex string and hash + data, err := hex.DecodeString(encoded) + if err != nil { + return common.Hash{}, fmt.Errorf("failed to decode hex string: %w", err) + } + + return crypto.Keccak256Hash(data), nil +} + +// HashMetadata hashes metadata to get its Merkle leaf +// Matches Canton's hashMetadataLeafNative from Crypto.daml +func (e *Encoder) HashMetadata(metadata types.ChainMetadata) (common.Hash, error) { + // Unmarshal Canton-specific metadata + var metadataFields AdditionalFieldsMetadata + if err := json.Unmarshal(metadata.AdditionalFields, &metadataFields); err != nil { + return common.Hash{}, fmt.Errorf("failed to unmarshal metadata additional fields: %w", err) + } + + // Build override flag + overrideFlag := "00" + if metadataFields.OverridePreviousRoot { + overrideFlag = "01" + } + + encoded := padLeft32(intToHex(int(metadataFields.ChainId))) + + asciiToHex(metadataFields.MultisigId) + + padLeft32(intToHex(int(metadataFields.PreOpCount))) + + padLeft32(intToHex(int(metadataFields.PostOpCount))) + + overrideFlag + + // Decode hex string and hash + data, err := hex.DecodeString(encoded) + if err != nil { + return common.Hash{}, fmt.Errorf("failed to decode hex string: %w", err) + } + + return crypto.Keccak256Hash(data), nil +} + +// Helper functions matching Canton Crypto.daml + +// padLeft32 pads hex string to 64 chars (32 bytes) +func padLeft32(hexStr string) string { + if len(hexStr) >= 64 { + return hexStr[:64] + } + return strings.Repeat("0", 64-len(hexStr)) + hexStr +} + +// intToHex converts int to hex string (without padding) +func intToHex(n int) string { + if n == 0 { + return "0" + } + return fmt.Sprintf("%x", n) +} + +// asciiToHex converts ASCII string to hex +func asciiToHex(s string) string { + return hex.EncodeToString([]byte(s)) +} diff --git a/sdk/canton/executor.go b/sdk/canton/executor.go new file mode 100644 index 000000000..68ee8cc4d --- /dev/null +++ b/sdk/canton/executor.go @@ -0,0 +1,345 @@ +package canton + +import ( + "context" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + 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/google/uuid" + cselectors "github.com/smartcontractkit/chain-selectors" + "github.com/smartcontractkit/go-daml/pkg/service/ledger" + + "github.com/smartcontractkit/chainlink-canton/bindings/mcms" + cantontypes "github.com/smartcontractkit/go-daml/pkg/types" + "github.com/smartcontractkit/mcms/internal/utils/abi" + "github.com/smartcontractkit/mcms/sdk" + "github.com/smartcontractkit/mcms/types" +) + +const SignMsgABI = `[{"type":"bytes32"},{"type":"uint32"}]` + +var _ sdk.Executor = &Executor{} + +type Executor struct { + *Encoder + *Inspector + client apiv2.CommandServiceClient + userId string + party string + role TimelockRole +} + +func NewExecutor(encoder *Encoder, inspector *Inspector, client apiv2.CommandServiceClient, userId string, party string, role TimelockRole) (*Executor, error) { + return &Executor{ + Encoder: encoder, + Inspector: inspector, + client: client, + userId: userId, + party: party, + role: role, + }, nil +} + +func (e Executor) ExecuteOperation( + ctx context.Context, + metadata types.ChainMetadata, + nonce uint32, + proof []common.Hash, + op types.Operation, +) (types.TransactionResult, error) { + // Extract Canton-specific operation fields from AdditionalFields + var cantonOpFields AdditionalFields + if len(op.Transaction.AdditionalFields) > 0 { + if err := json.Unmarshal(op.Transaction.AdditionalFields, &cantonOpFields); err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to unmarshal operation additional fields: %w", err) + } + } + + // Validate required Canton fields + if cantonOpFields.TargetInstanceId == "" { + return types.TransactionResult{}, errors.New("targetInstanceId is required in operation additional fields") + } + if cantonOpFields.FunctionName == "" { + return types.TransactionResult{}, errors.New("functionName is required in operation additional fields") + } + if cantonOpFields.TargetCid == "" { + return types.TransactionResult{}, errors.New("targetCid is required in operation additional fields") + } + + // Extract metadata fields for chainId and multisigId + var metadataFields struct { + ChainId int64 `json:"chainId"` + MultisigId string `json:"multisigId"` + } + if len(metadata.AdditionalFields) > 0 { + if err := json.Unmarshal(metadata.AdditionalFields, &metadataFields); err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to unmarshal metadata additional fields: %w", err) + } + } + + // Build Canton Op struct + cantonOp := mcms.Op{ + ChainId: cantontypes.INT64(metadataFields.ChainId), + MultisigId: cantontypes.TEXT(metadataFields.MultisigId), + Nonce: cantontypes.INT64(nonce), + TargetInstanceId: cantontypes.TEXT(cantonOpFields.TargetInstanceId), + FunctionName: cantontypes.TEXT(cantonOpFields.FunctionName), + OperationData: cantontypes.TEXT(cantonOpFields.OperationData), + } + + // Convert proof to Canton TEXT array + opProof := make([]cantontypes.TEXT, len(proof)) + for i, p := range proof { + opProof[i] = cantontypes.TEXT(hex.EncodeToString(p[:])) + } + + // Convert contract IDs + targetCids := make([]cantontypes.CONTRACT_ID, len(cantonOpFields.ContractIds)) + for i, cid := range cantonOpFields.ContractIds { + targetCids[i] = cantontypes.CONTRACT_ID(cid) + } + + // Build exercise command using generated bindings + mcmsContract := mcms.MCMS{} + var choice string + var choiceArgument *apiv2.Value + + input := mcms.ExecuteOp{ + TargetRole: mcms.Role(e.role.String()), + Submitter: cantontypes.PARTY(e.party), + Op: cantonOp, + OpProof: opProof, + TargetCids: targetCids, + } + exerciseCmd := mcmsContract.ExecuteOp(metadata.MCMAddress, input) + choice = exerciseCmd.Choice + choiceArgument = ledger.MapToValue(input) + + // Parse template ID + packageID, moduleName, entityName, err := parseTemplateIDFromString(mcmsContract.GetTemplateID()) + if err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to parse template ID: %w", err) + } + + commandID := uuid.Must(uuid.NewUUID()).String() + submitResp, err := e.client.SubmitAndWaitForTransaction(ctx, &apiv2.SubmitAndWaitForTransactionRequest{ + Commands: &apiv2.Commands{ + WorkflowId: "mcms-execute-op", + CommandId: commandID, + ActAs: []string{e.party}, + Commands: []*apiv2.Command{{ + Command: &apiv2.Command_Exercise{ + Exercise: &apiv2.ExerciseCommand{ + TemplateId: &apiv2.Identifier{ + PackageId: packageID, + ModuleName: moduleName, + EntityName: entityName, + }, + ContractId: metadata.MCMAddress, + Choice: choice, + ChoiceArgument: choiceArgument, + }, + }, + }}, + }, + }) + if err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to execute operation: %w", err) + } + + // Extract NEW MCMS CID from Created event + newMCMSContractID := "" + newMCMSTemplateID := "" + transaction := submitResp.GetTransaction() + for _, ev := range transaction.GetEvents() { + if createdEv := ev.GetCreated(); createdEv != nil { + templateID := formatTemplateID(createdEv.GetTemplateId()) + normalized := NormalizeTemplateKey(templateID) + if normalized == MCMSTemplateKey { + newMCMSContractID = createdEv.GetContractId() + newMCMSTemplateID = templateID + break + } + } + } + + if newMCMSContractID == "" { + return types.TransactionResult{}, fmt.Errorf("execute-op tx had no Created MCMS event; refusing to continue with old CID=%s", metadata.MCMAddress) + } + + return types.TransactionResult{ + Hash: commandID, + ChainFamily: cselectors.FamilyCanton, + RawData: map[string]any{ + "NewMCMSContractID": newMCMSContractID, + "NewMCMSTemplateID": newMCMSTemplateID, + "RawTx": submitResp, + }, + }, nil +} + +func (e Executor) SetRoot( + ctx context.Context, + metadata types.ChainMetadata, + proof []common.Hash, + root [32]byte, + validUntil uint32, + sortedSignatures []types.Signature, +) (types.TransactionResult, error) { + rootHex := hex.EncodeToString(root[:]) + // Recalculate msg hash to recover signers + inner, err := abi.Encode(SignMsgABI, root, validUntil) + if err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to decode hex for signing: %w", err) + } + innerHash := crypto.Keccak256(inner) + + // Apply EIP-191 prefix + prefix := []byte("\x19Ethereum Signed Message:\n32") + prefixedData := append(prefix, innerHash...) + cantonSignedHash := crypto.Keccak256Hash(prefixedData) + + // Convert signatures to Canton RawSignature array + signatures := make([]mcms.RawSignature, len(sortedSignatures)) + for i, sig := range sortedSignatures { + pubKey, err := sig.RecoverPublicKey(cantonSignedHash) + if err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to recover public key for signature %d: %w", i, err) + } + + // Convert public key to hex string + pubkeyHex := hex.EncodeToString(crypto.FromECDSAPub(pubKey)) + signatures[i] = mcms.RawSignature{ + PublicKey: cantontypes.TEXT(pubkeyHex), + R: cantontypes.TEXT(hex.EncodeToString(sig.R[:])), + S: cantontypes.TEXT(hex.EncodeToString(sig.S[:])), + } + } + + // Extract root metadata from ChainMetadata.AdditionalFields + var rootMetadata mcms.RootMetadata + if len(metadata.AdditionalFields) > 0 { + var additionalFields map[string]interface{} + if err := json.Unmarshal(metadata.AdditionalFields, &additionalFields); err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to unmarshal additional fields: %w", err) + } + + // Extract fields with type assertions + if chainId, ok := additionalFields["chainId"].(float64); ok { + rootMetadata.ChainId = cantontypes.INT64(int64(chainId)) + } + if multisigId, ok := additionalFields["multisigId"].(string); ok { + rootMetadata.MultisigId = cantontypes.TEXT(multisigId) + } + if preOpCount, ok := additionalFields["preOpCount"].(float64); ok { + rootMetadata.PreOpCount = cantontypes.INT64(int64(preOpCount)) + } + if postOpCount, ok := additionalFields["postOpCount"].(float64); ok { + rootMetadata.PostOpCount = cantontypes.INT64(int64(postOpCount)) + } + if overridePreviousRoot, ok := additionalFields["overridePreviousRoot"].(bool); ok { + rootMetadata.OverridePreviousRoot = cantontypes.BOOL(overridePreviousRoot) + } + } + + // Convert proof to Canton TEXT array + metadataProof := make([]cantontypes.TEXT, len(proof)) + for i, p := range proof { + metadataProof[i] = cantontypes.TEXT(hex.EncodeToString(p[:])) + } + + validUntilTime := time.Unix(time.Unix(int64(validUntil), 0).UnixMicro(), 0) + input := mcms.SetRoot{ + TargetRole: mcms.Role(e.role.String()), + Submitter: cantontypes.PARTY(e.party), + NewRoot: cantontypes.TEXT(rootHex), + ValidUntil: cantontypes.TIMESTAMP(validUntilTime), + Metadata: rootMetadata, + MetadataProof: metadataProof, + Signatures: signatures, + } + + // Build exercise command using generated bindings + mcmsContract := mcms.MCMS{} + exerciseCmd := mcmsContract.SetRoot(metadata.MCMAddress, input) + + // Parse template ID + packageID, moduleName, entityName, err := parseTemplateIDFromString(mcmsContract.GetTemplateID()) + if err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to parse template ID: %w", err) + } + + // Convert input to choice argument + choiceArgument := ledger.MapToValue(input) + + commandID := uuid.Must(uuid.NewUUID()).String() + submitResp, err := e.client.SubmitAndWaitForTransaction(ctx, &apiv2.SubmitAndWaitForTransactionRequest{ + Commands: &apiv2.Commands{ + WorkflowId: "mcms-set-root", + CommandId: commandID, + ActAs: []string{e.party}, + Commands: []*apiv2.Command{{ + Command: &apiv2.Command_Exercise{ + Exercise: &apiv2.ExerciseCommand{ + TemplateId: &apiv2.Identifier{ + PackageId: packageID, + ModuleName: moduleName, + EntityName: entityName, + }, + ContractId: metadata.MCMAddress, + Choice: exerciseCmd.Choice, + ChoiceArgument: choiceArgument, + }, + }, + }}, + }, + }) + if err != nil { + return types.TransactionResult{}, fmt.Errorf("failed to set root: %w", err) + } + + // Extract NEW MCMS CID from Created event + newMCMSContractID := "" + newMCMSTemplateID := "" + transaction := submitResp.GetTransaction() + for _, ev := range transaction.GetEvents() { + if createdEv := ev.GetCreated(); createdEv != nil { + templateID := formatTemplateID(createdEv.GetTemplateId()) + normalized := NormalizeTemplateKey(templateID) + if normalized == MCMSTemplateKey { + newMCMSContractID = createdEv.GetContractId() + newMCMSTemplateID = templateID + break + } + } + } + + if newMCMSContractID == "" { + return types.TransactionResult{}, fmt.Errorf("set-root tx had no Created MCMS event; refusing to continue with old CID=%s", metadata.MCMAddress) + } + + return types.TransactionResult{ + Hash: commandID, + ChainFamily: cselectors.FamilyCanton, + RawData: map[string]any{ + "NewMCMSContractID": newMCMSContractID, + "NewMCMSTemplateID": newMCMSTemplateID, + "RawTx": submitResp, + }, + }, nil +} + +func PadLeft32(hexStr string) string { + if len(hexStr) >= 64 { + return hexStr[:64] + } + + return strings.Repeat("0", 64-len(hexStr)) + hexStr +} diff --git a/sdk/canton/helpers.go b/sdk/canton/helpers.go new file mode 100644 index 000000000..8e5f43456 --- /dev/null +++ b/sdk/canton/helpers.go @@ -0,0 +1,53 @@ +package canton + +import ( + "fmt" + "strings" + + apiv2 "github.com/digital-asset/dazl-client/v8/go/api/com/daml/ledger/api/v2" +) + +const ( + MCMSTemplateKey = "MCMS.Main:MCMS" +) + +func NormalizeTemplateKey(tid string) string { + tid = strings.TrimPrefix(tid, "#") + parts := strings.Split(tid, ":") + if len(parts) < 3 { + return tid + } + + return parts[len(parts)-2] + ":" + parts[len(parts)-1] +} + +// parseTemplateIDFromString parses a template ID string like "#package:Module:Entity" into its components +func parseTemplateIDFromString(templateID string) (packageID, moduleName, entityName string, err error) { + if !strings.HasPrefix(templateID, "#") { + return "", "", "", fmt.Errorf("template ID must start with #") + } + parts := strings.Split(templateID, ":") + if len(parts) != 3 { + return "", "", "", fmt.Errorf("template ID must have format #package:module:entity, got: %s", templateID) + } + + return parts[0], parts[1], parts[2], nil +} + +// ParseTemplateIDFromString is the exported version of parseTemplateIDFromString +func ParseTemplateIDFromString(templateID string) (packageID, moduleName, entityName string, err error) { + return parseTemplateIDFromString(templateID) +} + +// formatTemplateID converts an apiv2.Identifier to a string template ID format +func formatTemplateID(id *apiv2.Identifier) string { + if id == nil { + return "" + } + return id.GetPackageId() + ":" + id.GetModuleName() + ":" + id.GetEntityName() +} + +// FormatTemplateID is the exported version of formatTemplateID +func FormatTemplateID(id *apiv2.Identifier) string { + return formatTemplateID(id) +} diff --git a/sdk/canton/inspector.go b/sdk/canton/inspector.go new file mode 100644 index 000000000..fa19d1695 --- /dev/null +++ b/sdk/canton/inspector.go @@ -0,0 +1,301 @@ +package canton + +import ( + "context" + "encoding/hex" + "errors" + "fmt" + "io" + "strings" + "time" + + apiv2 "github.com/digital-asset/dazl-client/v8/go/api/com/daml/ledger/api/v2" + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink-canton/bindings" + "github.com/smartcontractkit/chainlink-canton/bindings/mcms" + cantontypes "github.com/smartcontractkit/go-daml/pkg/types" + "github.com/smartcontractkit/mcms/sdk" + "github.com/smartcontractkit/mcms/types" +) + +var _ sdk.Inspector = &Inspector{} + +type Inspector struct { + stateClient apiv2.StateServiceClient + party string + contractCache *mcms.MCMS // Cache MCMS to avoid repeated RPC calls + role TimelockRole +} + +func NewInspector(stateClient apiv2.StateServiceClient, party string, role TimelockRole) *Inspector { + return &Inspector{ + stateClient: stateClient, + party: party, + role: role, + } +} + +func (i *Inspector) GetConfig(ctx context.Context, mcmsAddr string) (*types.Config, error) { + if i.contractCache == nil { + mcmsContract, err := i.getMCMSContract(ctx, mcmsAddr) + if err != nil { + return nil, fmt.Errorf("failed to get MCMS contract: %w", err) + } + i.contractCache = mcmsContract + } + + switch i.role { + case TimelockRoleProposer: + return toConfig(i.contractCache.Proposer.Config) + case TimelockRoleBypasser: + return toConfig(i.contractCache.Bypasser.Config) + case TimelockRoleCanceller: + return toConfig(i.contractCache.Canceller.Config) + default: + return nil, fmt.Errorf("unknown timelock role: %s", i.role) + } +} + +func (i *Inspector) GetOpCount(ctx context.Context, mcmsAddr string) (uint64, error) { + if i.contractCache == nil { + mcmsContract, err := i.getMCMSContract(ctx, mcmsAddr) + if err != nil { + return 0, fmt.Errorf("failed to get MCMS contract: %w", err) + } + i.contractCache = mcmsContract + } + + switch i.role { + case TimelockRoleProposer: + return uint64(i.contractCache.Proposer.ExpiringRoot.OpCount), nil + case TimelockRoleBypasser: + return uint64(i.contractCache.Bypasser.ExpiringRoot.OpCount), nil + case TimelockRoleCanceller: + return uint64(i.contractCache.Canceller.ExpiringRoot.OpCount), nil + default: + return 0, fmt.Errorf("unknown timelock role: %s", i.role) + } +} + +func (i *Inspector) GetRoot(ctx context.Context, mcmsAddr string) (common.Hash, uint32, error) { + if i.contractCache == nil { + mcmsContract, err := i.getMCMSContract(ctx, mcmsAddr) + if err != nil { + return common.Hash{}, 0, fmt.Errorf("failed to get MCMS contract: %w", err) + } + i.contractCache = mcmsContract + } + + var expiringRoot mcms.ExpiringRoot + switch i.role { + case TimelockRoleProposer: + expiringRoot = i.contractCache.Proposer.ExpiringRoot + case TimelockRoleBypasser: + expiringRoot = i.contractCache.Bypasser.ExpiringRoot + case TimelockRoleCanceller: + expiringRoot = i.contractCache.Canceller.ExpiringRoot + default: + return common.Hash{}, 0, fmt.Errorf("unknown timelock role: %s", i.role) + } + + // Parse the root from hex string + rootStr := string(expiringRoot.Root) + rootStr = strings.TrimPrefix(rootStr, "0x") + rootBytes, err := hex.DecodeString(rootStr) + if err != nil { + return common.Hash{}, 0, fmt.Errorf("failed to decode root hash: %w", err) + } + + root := common.BytesToHash(rootBytes) + + // validUntil is a TIMESTAMP (which wraps time.Time) + // Convert to Unix timestamp (uint32) + timeVal := time.Time(expiringRoot.ValidUntil) + validUntil := uint32(timeVal.Unix()) + + return root, validUntil, nil +} + +func (i *Inspector) GetRootMetadata(ctx context.Context, mcmsAddr string) (types.ChainMetadata, error) { + if i.contractCache == nil { + mcmsContract, err := i.getMCMSContract(ctx, mcmsAddr) + if err != nil { + return types.ChainMetadata{}, fmt.Errorf("failed to get MCMS contract: %w", err) + } + i.contractCache = mcmsContract + } + + var rootMetadata mcms.RootMetadata + switch i.role { + case TimelockRoleProposer: + rootMetadata = i.contractCache.Proposer.RootMetadata + case TimelockRoleBypasser: + rootMetadata = i.contractCache.Bypasser.RootMetadata + case TimelockRoleCanceller: + rootMetadata = i.contractCache.Canceller.RootMetadata + default: + return types.ChainMetadata{}, fmt.Errorf("unknown timelock role: %s", i.role) + } + + return types.ChainMetadata{ + StartingOpCount: uint64(rootMetadata.PreOpCount), + MCMAddress: string(i.contractCache.InstanceId), + }, nil +} + +// getMCMSContract queries the active MCMS contract by contract ID +func (i *Inspector) getMCMSContract(ctx context.Context, mcmsAddr string) (*mcms.MCMS, error) { + // Get current ledger offset + ledgerEndResp, err := i.stateClient.GetLedgerEnd(ctx, &apiv2.GetLedgerEndRequest{}) + if err != nil { + return nil, fmt.Errorf("failed to get ledger end: %w", err) + } + + // Query active contracts at current offset + activeContractsResp, err := i.stateClient.GetActiveContracts(ctx, &apiv2.GetActiveContractsRequest{ + ActiveAtOffset: ledgerEndResp.GetOffset(), + EventFormat: &apiv2.EventFormat{ + FiltersByParty: map[string]*apiv2.Filters{ + i.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 nil, fmt.Errorf("failed to get active contracts: %w", err) + } + defer activeContractsResp.CloseSend() + + // Stream through active contracts to find the MCMS contract with matching ID + for { + resp, err := activeContractsResp.Recv() + if errors.Is(err, io.EOF) { + // Stream ended without finding the contract + return nil, fmt.Errorf("MCMS contract with ID %s not found", mcmsAddr) + } + if err != nil { + return nil, fmt.Errorf("failed to receive active contracts: %w", err) + } + + activeContract, ok := resp.GetContractEntry().(*apiv2.GetActiveContractsResponse_ActiveContract) + if !ok { + continue + } + + createdEvent := activeContract.ActiveContract.GetCreatedEvent() + if createdEvent == nil { + continue + } + + // Check if contract ID matches + if createdEvent.ContractId != mcmsAddr { + continue + } + + // Use bindings package to unmarshal the contract + // TODO: MinDelay type from binding doesnt correspond to actual type from contract + type NoMinDelayMCMS struct { + Owner cantontypes.PARTY `json:"owner"` + InstanceId cantontypes.TEXT `json:"instanceId"` + ChainId cantontypes.INT64 `json:"chainId"` + Proposer mcms.RoleState `json:"proposer"` + Canceller mcms.RoleState `json:"canceller"` + Bypasser mcms.RoleState `json:"bypasser"` + BlockedFunctions []mcms.BlockedFunction `json:"blockedFunctions"` + TimelockTimestamps cantontypes.GENMAP `json:"timelockTimestamps"` + } + mcmsContractNoMinDelay, err := bindings.UnmarshalActiveContract[NoMinDelayMCMS](activeContract) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal MCMS contract: %w", err) + } + + mcmsContract := &mcms.MCMS{ + Owner: mcmsContractNoMinDelay.Owner, + InstanceId: mcmsContractNoMinDelay.InstanceId, + ChainId: mcmsContractNoMinDelay.ChainId, + Proposer: mcmsContractNoMinDelay.Proposer, + Canceller: mcmsContractNoMinDelay.Canceller, + Bypasser: mcmsContractNoMinDelay.Bypasser, + BlockedFunctions: mcmsContractNoMinDelay.BlockedFunctions, + TimelockTimestamps: mcmsContractNoMinDelay.TimelockTimestamps, + MinDelay: 0, // TODO: Fix bindings type + } + + return mcmsContract, nil + } +} + +// toConfig converts a Canton MultisigConfig to the chain-agnostic types.Config +func toConfig(bindConfig mcms.MultisigConfig) (*types.Config, error) { + // Group signers by group index + signersByGroup := make([][]common.Address, 32) // MCMS supports up to 32 groups + + for _, signer := range bindConfig.Signers { + groupIdx := int(signer.SignerGroup) + if groupIdx >= 32 { + return nil, fmt.Errorf("signer group index %d exceeds maximum of 31", groupIdx) + } + + // Parse signer address + addr := common.HexToAddress(string(signer.SignerAddress)) + signersByGroup[groupIdx] = append(signersByGroup[groupIdx], addr) + } + + // Build the group configs + groups := make([]types.Config, 32) + for i := 0; i < 32; i++ { + signers := signersByGroup[i] + if signers == nil { + signers = []common.Address{} + } + + quorum := uint8(0) + if i < len(bindConfig.GroupQuorums) { + quorum = uint8(bindConfig.GroupQuorums[i]) + } + + groups[i] = types.Config{ + Signers: signers, + GroupSigners: []types.Config{}, + Quorum: quorum, + } + } + + // Link the group signers; this assumes a group's parent always has a lower index + // Process in reverse order to build the tree from leaves to root + for i := 31; i >= 0; i-- { + parent := uint8(0) + if i < len(bindConfig.GroupParents) { + parent = uint8(bindConfig.GroupParents[i]) + } + + // Add non-empty child groups to their parent + // Skip the root group (i == 0) and empty groups (quorum == 0) + if i > 0 && groups[i].Quorum > 0 { + groups[parent].GroupSigners = append([]types.Config{groups[i]}, groups[parent].GroupSigners...) + } + } + + // Validate the root group config + if err := groups[0].Validate(); err != nil { + return nil, fmt.Errorf("invalid MCMS config: %w", err) + } + + return &groups[0], nil +} diff --git a/sdk/canton/inspector_test.go b/sdk/canton/inspector_test.go new file mode 100644 index 000000000..2c275a5a4 --- /dev/null +++ b/sdk/canton/inspector_test.go @@ -0,0 +1,193 @@ +//go:build e2e + +package canton + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-canton/bindings/mcms" + "github.com/smartcontractkit/go-daml/pkg/types" + mcmstypes "github.com/smartcontractkit/mcms/types" + "github.com/stretchr/testify/require" +) + +func TestToConfig(t *testing.T) { + tests := []struct { + name string + description string + input mcms.MultisigConfig + expected mcmstypes.Config + }{ + { + name: "simple_2of3", + description: "Simple 2-of-3 multisig with all signers in root group (group 0)", + input: mcms.MultisigConfig{ + Signers: []mcms.SignerInfo{ + {SignerAddress: types.TEXT("0x1111111111111111111111111111111111111111"), SignerIndex: types.INT64(0), SignerGroup: types.INT64(0)}, + {SignerAddress: types.TEXT("0x2222222222222222222222222222222222222222"), SignerIndex: types.INT64(1), SignerGroup: types.INT64(0)}, + {SignerAddress: types.TEXT("0x3333333333333333333333333333333333333333"), SignerIndex: types.INT64(2), SignerGroup: types.INT64(0)}, + }, + GroupQuorums: []types.INT64{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + GroupParents: []types.INT64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + expected: mcmstypes.Config{ + Quorum: 2, + Signers: []common.Address{ + common.HexToAddress("1111111111111111111111111111111111111111"), + common.HexToAddress("2222222222222222222222222222222222222222"), + common.HexToAddress("3333333333333333333333333333333333333333"), + }, + GroupSigners: []mcmstypes.Config{}, + }, + }, + { + name: "hierarchical_2level", + description: "2-level hierarchy: root group 0 has 1 direct signer + group 1 as child. Group 1 has 3 signers with quorum 2. Root quorum is 1 (can be satisfied by direct signer OR group 1 reaching quorum).", + input: mcms.MultisigConfig{ + Signers: []mcms.SignerInfo{ + {SignerAddress: types.TEXT("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), SignerIndex: types.INT64(0), SignerGroup: types.INT64(0)}, + {SignerAddress: types.TEXT("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), SignerIndex: types.INT64(1), SignerGroup: types.INT64(1)}, + {SignerAddress: types.TEXT("0xcccccccccccccccccccccccccccccccccccccccc"), SignerIndex: types.INT64(2), SignerGroup: types.INT64(1)}, + {SignerAddress: types.TEXT("0xdddddddddddddddddddddddddddddddddddddddd"), SignerIndex: types.INT64(3), SignerGroup: types.INT64(1)}, + }, + GroupQuorums: []types.INT64{1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + GroupParents: []types.INT64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + expected: mcmstypes.Config{ + Quorum: 1, + Signers: []common.Address{ + common.HexToAddress("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + }, + GroupSigners: []mcmstypes.Config{ + { + Quorum: 2, + Signers: []common.Address{ + common.HexToAddress("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), + common.HexToAddress("cccccccccccccccccccccccccccccccccccccccc"), + common.HexToAddress("dddddddddddddddddddddddddddddddddddddddd"), + }, + GroupSigners: []mcmstypes.Config{}, + }, + }, + }, + }, + { + name: "complex_3level", + description: "3-level hierarchy: Group 0 (root) quorum 2, Group 1 (parent 0) quorum 2, Group 2 (parent 0) quorum 1, Group 3 (parent 1) quorum 2. Tests deeper nesting with multiple child groups at same level.", + input: mcms.MultisigConfig{ + Signers: []mcms.SignerInfo{ + {SignerAddress: types.TEXT("0x1000000000000000000000000000000000000001"), SignerIndex: types.INT64(0), SignerGroup: types.INT64(0)}, + {SignerAddress: types.TEXT("0x1000000000000000000000000000000000000002"), SignerIndex: types.INT64(1), SignerGroup: types.INT64(1)}, + {SignerAddress: types.TEXT("0x1000000000000000000000000000000000000003"), SignerIndex: types.INT64(2), SignerGroup: types.INT64(1)}, + {SignerAddress: types.TEXT("0x1000000000000000000000000000000000000004"), SignerIndex: types.INT64(3), SignerGroup: types.INT64(2)}, + {SignerAddress: types.TEXT("0x1000000000000000000000000000000000000005"), SignerIndex: types.INT64(4), SignerGroup: types.INT64(2)}, + {SignerAddress: types.TEXT("0x1000000000000000000000000000000000000006"), SignerIndex: types.INT64(5), SignerGroup: types.INT64(3)}, + {SignerAddress: types.TEXT("0x1000000000000000000000000000000000000007"), SignerIndex: types.INT64(6), SignerGroup: types.INT64(3)}, + {SignerAddress: types.TEXT("0x1000000000000000000000000000000000000008"), SignerIndex: types.INT64(7), SignerGroup: types.INT64(3)}, + }, + GroupQuorums: []types.INT64{2, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + GroupParents: []types.INT64{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + expected: mcmstypes.Config{ + Quorum: 2, + Signers: []common.Address{ + common.HexToAddress("1000000000000000000000000000000000000001"), + }, + GroupSigners: []mcmstypes.Config{ + { + Quorum: 2, + Signers: []common.Address{ + common.HexToAddress("1000000000000000000000000000000000000002"), + common.HexToAddress("1000000000000000000000000000000000000003"), + }, + GroupSigners: []mcmstypes.Config{ + { + Quorum: 2, + Signers: []common.Address{ + common.HexToAddress("1000000000000000000000000000000000000006"), + common.HexToAddress("1000000000000000000000000000000000000007"), + common.HexToAddress("1000000000000000000000000000000000000008"), + }, + GroupSigners: []mcmstypes.Config{}, + }, + }, + }, + { + Quorum: 1, + Signers: []common.Address{ + common.HexToAddress("1000000000000000000000000000000000000004"), + common.HexToAddress("1000000000000000000000000000000000000005"), + }, + GroupSigners: []mcmstypes.Config{}, + }, + }, + }, + }, + { + name: "empty_groups_edge_case", + description: "Edge case: groups with quorum 0 (disabled) interspersed with active groups. Group 0 active (quorum 1), Group 1 disabled (quorum 0), Group 2 active (quorum 2, parent 0). The toConfig function should skip disabled groups.", + input: mcms.MultisigConfig{ + Signers: []mcms.SignerInfo{ + {SignerAddress: types.TEXT("0xdead000000000000000000000000000000000001"), SignerIndex: types.INT64(0), SignerGroup: types.INT64(0)}, + {SignerAddress: types.TEXT("0xdead000000000000000000000000000000000002"), SignerIndex: types.INT64(1), SignerGroup: types.INT64(2)}, + {SignerAddress: types.TEXT("0xdead000000000000000000000000000000000003"), SignerIndex: types.INT64(2), SignerGroup: types.INT64(2)}, + }, + GroupQuorums: []types.INT64{1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + GroupParents: []types.INT64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + expected: mcmstypes.Config{ + Quorum: 1, + Signers: []common.Address{ + common.HexToAddress("dead000000000000000000000000000000000001"), + }, + GroupSigners: []mcmstypes.Config{ + { + Quorum: 2, + Signers: []common.Address{ + common.HexToAddress("dead000000000000000000000000000000000002"), + common.HexToAddress("dead000000000000000000000000000000000003"), + }, + GroupSigners: []mcmstypes.Config{}, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := toConfig(tt.input) + require.NoError(t, err, tt.description) + require.NotNil(t, result) + + // Compare the result with expected + require.Equal(t, tt.expected.Quorum, result.Quorum, "quorum mismatch") + require.Equal(t, len(tt.expected.Signers), len(result.Signers), "signers count mismatch") + + // Compare signers + for i, expectedSigner := range tt.expected.Signers { + require.Equal(t, expectedSigner, result.Signers[i], "signer mismatch at index %d", i) + } + + // Compare group signers recursively + compareGroupSigners(t, tt.expected.GroupSigners, result.GroupSigners) + }) + } +} + +func compareGroupSigners(t *testing.T, expected, actual []mcmstypes.Config) { + require.Equal(t, len(expected), len(actual), "group signers count mismatch") + + for i := range expected { + require.Equal(t, expected[i].Quorum, actual[i].Quorum, "group %d quorum mismatch", i) + require.Equal(t, len(expected[i].Signers), len(actual[i].Signers), "group %d signers count mismatch", i) + + for j, expectedSigner := range expected[i].Signers { + require.Equal(t, expectedSigner, actual[i].Signers[j], "group %d signer mismatch at index %d", i, j) + } + + // Recursively compare nested group signers + compareGroupSigners(t, expected[i].GroupSigners, actual[i].GroupSigners) + } +} diff --git a/taskfiles/test/Taskfile.yml b/taskfiles/test/Taskfile.yml index 5289ec2b5..f4468e25d 100644 --- a/taskfiles/test/Taskfile.yml +++ b/taskfiles/test/Taskfile.yml @@ -57,6 +57,13 @@ tasks: cmds: - CTF_CONFIGS=$CTF_CONFIGS go test -v -tags=e2e -test.run TestTONSuite {{.CLI_ARGS}} ./e2e/tests... + e2e:canton: + desc: "Run Canton e2e tests" + env: + CTF_CONFIGS: '{{ .CTF_CONFIGS | default "../config.canton.toml" }}' + cmds: + - CTF_CONFIGS=$CTF_CONFIGS go test -v -tags=e2e -test.run TestCantonSuite {{.CLI_ARGS}} ./e2e/tests... + coverage: desc: "Run unit test suite with coverage" cmds: diff --git a/types/chain_selector.go b/types/chain_selector.go index 1bf0cdf0d..93cde4c08 100644 --- a/types/chain_selector.go +++ b/types/chain_selector.go @@ -29,6 +29,7 @@ var supportedFamilies = []string{ cselectors.FamilyAptos, cselectors.FamilySui, cselectors.FamilyTon, + cselectors.FamilyCanton, } // GetChainSelectorFamily returns the family of the chain selector. diff --git a/validation.go b/validation.go index f5355bcdc..14efca3d6 100644 --- a/validation.go +++ b/validation.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/mcms/types" "github.com/smartcontractkit/mcms/sdk/aptos" + "github.com/smartcontractkit/mcms/sdk/canton" "github.com/smartcontractkit/mcms/sdk/evm" "github.com/smartcontractkit/mcms/sdk/solana" "github.com/smartcontractkit/mcms/sdk/sui" @@ -57,6 +58,8 @@ func validateChainMetadata(metadata types.ChainMetadata, csel types.ChainSelecto return nil case cselectors.FamilySui: return sui.ValidateChainMetadata(metadata) + case cselectors.FamilyCanton: + return canton.ValidateChainMetadata(metadata) case cselectors.FamilyTon: // TODO (ton): do we need special chain metadata for TON? // Yes! We could attach MCMS -> Timelock value here which is now hardcoded default in timelock converter