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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,78 @@ var _ cldf.ChangeSetV2[UpdateAllowedSignersInput] = UpdateAllowedSigners{}
var _ cldf.ChangeSetV2[SetWorkflowOwnerConfigInput] = SetWorkflowOwnerConfig{}
var _ cldf.ChangeSetV2[SetDONLimitInput] = SetDONLimit{}
var _ cldf.ChangeSetV2[SetUserDONOverrideInput] = SetUserDONOverride{}
var _ cldf.ChangeSetV2[BatchSetUserDONOverrideInput] = BatchSetUserDONOverride{}
var _ cldf.ChangeSetV2[SetCapabilitiesRegistryInput] = SetCapabilitiesRegistry{}

// prepareWorkflowRegistryDeps loads MCMS state, EVM chain, registry contract, and the transaction
// strategy needed to execute a workflow-registry operation. The returned deps is ready to pass to
// operations.ExecuteOperation and the strategy is reachable via deps.Strategy for the subsequent
// BuildProposal call in finalizeWorkflowRegistryOutput.
//
// Extracted to remove the ~30-line setup boilerplate that was previously duplicated across each
// Apply function in this file. New code should prefer this helper; existing Apply functions can
// be migrated incrementally.
func prepareWorkflowRegistryDeps(
e cldf.Environment,
chainSelector uint64,
qualifier string,
mcmsConfig *crecontracts.MCMSConfig,
description string,
) (contracts.WorkflowRegistryOpDeps, error) {
var mcmsContracts *evmstate.MCMSWithTimelockState
if mcmsConfig != nil {
loaded, err := strategies.GetMCMSContracts(e, chainSelector, *mcmsConfig)
if err != nil {
return contracts.WorkflowRegistryOpDeps{}, fmt.Errorf("failed to get MCMS contracts: %w", err)
}
mcmsContracts = loaded
}

chain, ok := e.BlockChains.EVMChains()[chainSelector]
if !ok {
return contracts.WorkflowRegistryOpDeps{}, fmt.Errorf("chain with selector %d not found", chainSelector)
}

registry, err := contracts.GetWorkflowRegistryV2FromDatastore(&e, chainSelector, qualifier)
if err != nil {
return contracts.WorkflowRegistryOpDeps{}, fmt.Errorf("failed to get workflow registry address from datastore: %w", err)
}

strategy, err := strategies.CreateStrategy(chain, e, mcmsConfig, mcmsContracts, registry.Address(), description)
if err != nil {
return contracts.WorkflowRegistryOpDeps{}, fmt.Errorf("failed to create strategy: %w", err)
}

return contracts.WorkflowRegistryOpDeps{
Env: &e,
Strategy: strategy,
Registry: registry,
Chain: &chain,
}, nil
}

// finalizeWorkflowRegistryOutput wraps a completed operation report into a ChangesetOutput,
// building an MCMS timelock proposal from mcmsOp when non-nil.
func finalizeWorkflowRegistryOutput(
strategy strategies.TransactionStrategy,
mcmsOp *types.BatchOperation,
report operations.Report[any, any],
) (cldf.ChangesetOutput, error) {
if mcmsOp == nil {
return cldf.ChangesetOutput{
Reports: []operations.Report[any, any]{report},
}, nil
}
proposal, err := strategy.BuildProposal([]types.BatchOperation{*mcmsOp})
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to build MCMS proposal: %w", err)
}
return cldf.ChangesetOutput{
MCMSTimelockProposals: []mcms.TimelockProposal{*proposal},
Reports: []operations.Report[any, any]{report},
}, nil
}

// SetConfigInput configures metadata validation settings for workflow registry v2
type SetConfigInput struct {
ChainSelector uint64 `json:"chainSelector"`
Expand Down Expand Up @@ -405,45 +475,11 @@ func (l SetUserDONOverride) VerifyPreconditions(e cldf.Environment, config SetUs
}

func (l SetUserDONOverride) Apply(e cldf.Environment, config SetUserDONOverrideInput) (cldf.ChangesetOutput, error) {
// Get MCMS contracts if needed
var mcmsContracts *evmstate.MCMSWithTimelockState
if config.MCMSConfig != nil {
var err error
mcmsContracts, err = strategies.GetMCMSContracts(e, config.ChainSelector, *config.MCMSConfig)
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to get MCMS contracts: %w", err)
}
}

chain, ok := e.BlockChains.EVMChains()[config.ChainSelector]
if !ok {
return cldf.ChangesetOutput{}, fmt.Errorf("chain with selector %d not found", config.ChainSelector)
}

registry, err := contracts.GetWorkflowRegistryV2FromDatastore(&e, config.ChainSelector, config.WorkflowRegistryQualifier)
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to get workflow registry address from datastore: %w", err)
}

// Create the appropriate strategy
strategy, err := strategies.CreateStrategy(
chain,
e,
config.MCMSConfig,
mcmsContracts,
registry.Address(),
contracts.SetUserDONOverrideDescription,
)
deps, err := prepareWorkflowRegistryDeps(e, config.ChainSelector, config.WorkflowRegistryQualifier, config.MCMSConfig, contracts.SetUserDONOverrideDescription)
if err != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to create strategy: %w", err)
return cldf.ChangesetOutput{}, err
}

// Execute operation
deps := contracts.WorkflowRegistryOpDeps{
Env: &e,
Strategy: strategy,
Registry: registry,
}
report, err := operations.ExecuteOperation(
e.OperationsBundle,
contracts.SetUserDONOverrideOp, deps, contracts.SetUserDONOverrideOpInput{
Expand All @@ -460,21 +496,72 @@ func (l SetUserDONOverride) Apply(e cldf.Environment, config SetUserDONOverrideI
return cldf.ChangesetOutput{}, err
}

if report.Output.MCMSOperation != nil {
proposal, mcmsErr := strategy.BuildProposal([]types.BatchOperation{*report.Output.MCMSOperation})
if mcmsErr != nil {
return cldf.ChangesetOutput{}, fmt.Errorf("failed to build MCMS proposal: %w", mcmsErr)
return finalizeWorkflowRegistryOutput(deps.Strategy, report.Output.MCMSOperation, report.ToGenericReport())
}

// BatchSetUserDONOverrideInput configures multiple user DON overrides in a single MCMS BatchOperation.
//
// This is functionally equivalent to applying SetUserDONOverride once per override entry, but produces
// a single MCMS BatchOperation containing N transactions instead of N separate batch operations.
// Use this when you have many overrides to apply to the same WorkflowRegistry to keep proposals compact
// and avoid blocking other queued proposals.
type BatchSetUserDONOverrideInput struct {
ChainSelector uint64 `json:"chainSelector"`
WorkflowRegistryQualifier string `json:"workflowRegistryQualifier"` // Qualifier to identify the specific workflow registry
Overrides []contracts.SetUserDONOverrideEntry `json:"overrides"` // Per-user overrides to apply
MCMSConfig *crecontracts.MCMSConfig `json:"mcmsConfig,omitempty"` // MCMS configuration
}

type BatchSetUserDONOverride struct{}

func (l BatchSetUserDONOverride) VerifyPreconditions(e cldf.Environment, config BatchSetUserDONOverrideInput) error {
if len(config.Overrides) == 0 {
return errors.New("must provide at least one override")
}
seen := make(map[string]struct{}, len(config.Overrides))
for i, entry := range config.Overrides {
if entry.User == (common.Address{}) {
return fmt.Errorf("override[%d]: user address must not be zero", i)
}
if entry.DONFamily == "" {
return fmt.Errorf("override[%d] (user %s): donFamily must not be empty", i, entry.User.Hex())
}
key := entry.User.Hex() + "|" + entry.DONFamily
if _, dup := seen[key]; dup {
return fmt.Errorf("override[%d]: duplicate (user %s, donFamily %s)", i, entry.User.Hex(), entry.DONFamily)
}
seen[key] = struct{}{}
}
return nil
}

return cldf.ChangesetOutput{
MCMSTimelockProposals: []mcms.TimelockProposal{*proposal},
Reports: []operations.Report[any, any]{report.ToGenericReport()},
}, nil
func (l BatchSetUserDONOverride) Apply(e cldf.Environment, config BatchSetUserDONOverrideInput) (cldf.ChangesetOutput, error) {
if err := l.VerifyPreconditions(e, config); err != nil {
return cldf.ChangesetOutput{}, err
}

return cldf.ChangesetOutput{
Reports: []operations.Report[any, any]{report.ToGenericReport()},
}, nil
// The strategy is only used at the BuildProposal step inside finalizeWorkflowRegistryOutput;
// BatchSetUserDONOverrideOp itself builds calldata directly via deps.Chain + deps.Registry so
// all overrides land in a single MCMS BatchOperation.
deps, err := prepareWorkflowRegistryDeps(e, config.ChainSelector, config.WorkflowRegistryQualifier, config.MCMSConfig, contracts.BatchSetUserDONOverrideDescription)
if err != nil {
return cldf.ChangesetOutput{}, err
}

report, err := operations.ExecuteOperation(
e.OperationsBundle,
contracts.BatchSetUserDONOverrideOp, deps, contracts.BatchSetUserDONOverrideOpInput{
ChainSelector: config.ChainSelector,
Qualifier: config.WorkflowRegistryQualifier,
Overrides: config.Overrides,
MCMSConfig: config.MCMSConfig,
},
)
if err != nil {
return cldf.ChangesetOutput{}, err
}

return finalizeWorkflowRegistryOutput(deps.Strategy, report.Output.MCMSOperation, report.ToGenericReport())
}

// SetCapabilitiesRegistryInput configures the Capabilities registry address
Expand Down
Loading
Loading