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
74 changes: 74 additions & 0 deletions engine/cld/mcms/proposalanalysis/analyzer/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
Package analyzer defines analyzer interfaces used by the proposal analysis engine.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to also add a page on the docs/ folder with similar information and examples.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah deciding on this, will see how we go, maybe we will add a more official page on the docs.cld too


# Implementing an Analyzer

Every analyzer must implement `BaseAnalyzer`:

- ID() string: unique identifier.
- Dependencies() []string: analyzer IDs that must run first.

Then implement one of the scope-specific analyzer interfaces:

- ProposalAnalyzer
- BatchOperationAnalyzer
- CallAnalyzer
- ParameterAnalyzer

Example proposal-level analyzer:

import (
"context"

"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis"
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/analyzer"
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/analyzer/annotation"
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/decoder"
)

type RiskAnalyzer struct{}

func (RiskAnalyzer) ID() string {
return "risk-analyzer"
}

func (RiskAnalyzer) Dependencies() []string {
return []string{"dependency-analyzer"}
}

func (RiskAnalyzer) CanAnalyze(
ctx context.Context,
req analyzer.ProposalAnalyzeRequest,
proposal decoder.DecodedTimelockProposal,
) bool {
_ = ctx
_ = req
_ = proposal
return true
}

func (RiskAnalyzer) Analyze(
ctx context.Context,
req analyzer.ProposalAnalyzeRequest,
proposal decoder.DecodedTimelockProposal,
) (annotation.Annotations, error) {
_ = ctx
_ = req
_ = proposal

return annotation.Annotations{
annotation.New("risk-level", "string", "medium"),
}, nil
}

# Registering with the engine

eng := proposalanalysis.NewAnalyzerEngine()
if err := eng.RegisterAnalyzer(RiskAnalyzer{}); err != nil {
return err
}

The engine resolves analyzer execution order using `Dependencies()` and passes
dependency-scoped annotations through the request's `DependencyAnnotationStore`.
*/
package analyzer
78 changes: 78 additions & 0 deletions engine/cld/mcms/proposalanalysis/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Package proposalanalysis provides an engine for analyzing MCMS timelock proposals
and rendering the analyzed output.

# Overview

The engine workflow is:

1. Create an engine with optional configuration.
2. Register one or more analyzers.
3. Register a renderer.
4. Run analysis on a proposal.
5. Render the analyzed proposal with RenderTo.

# Usage

import (
"context"
"os"
"time"

"github.com/smartcontractkit/mcms"

"github.com/smartcontractkit/chainlink-deployments-framework/deployment"
cldfdomain "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/domain"
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis"
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/decoder"
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/renderer"
)

func Example() error {
ctx := context.Background()

engine := proposalanalysis.NewAnalyzerEngine(
proposalanalysis.WithAnalyzerTimeout(90*time.Second),
)

// Register your analyzers (implementations omitted for brevity).
if err := engine.RegisterAnalyzer(newDependencyAnalyzer()); err != nil {
return err
}
if err := engine.RegisterAnalyzer(newRiskAnalyzer()); err != nil {
return err
}

mdRenderer, err := renderer.NewMarkdownRenderer()
if err != nil {
return err
}
if err := engine.RegisterRenderer(mdRenderer); err != nil {
return err
}

domain := cldfdomain.NewDomain("/path/to/domains", "ccip")
env := ...
proposal := ...

analyzed, err := engine.Run(ctx, proposalanalysis.RunRequest{
Domain: domain,
Environment: env,
DecoderConfig: decoder.Config{},
}, proposal)
if err != nil {
return err
}

return engine.RenderTo(
os.Stdout,
renderer.IDMarkdown,
renderer.RenderRequest{
Domain: domain.Key(),
EnvironmentName: env.Name,
},
analyzed,
)
}
*/
package proposalanalysis
67 changes: 67 additions & 0 deletions engine/cld/mcms/proposalanalysis/renderer/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Package renderer defines renderer interfaces used to format analyzed proposals.

# Implementing a Renderer

A renderer must implement:

- ID() string: unique renderer identifier.
- RenderTo(io.Writer, RenderRequest, analyzer.AnalyzedProposal) error

Example custom plain-text renderer:

import (
"fmt"
"io"

"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis"
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/analyzer"
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/renderer"
)

type PlainRenderer struct{}

func (PlainRenderer) ID() string {
return "plain"
}

func (PlainRenderer) RenderTo(
w io.Writer,
req renderer.RenderRequest,
proposal analyzer.AnalyzedProposal,
) error {
if _, err := fmt.Fprintf(
w,
"domain=%s env=%s batches=%d\n",
req.Domain,
req.EnvironmentName,
len(proposal.BatchOperations()),
); err != nil {
return err
}

for _, batch := range proposal.BatchOperations() {
if _, err := fmt.Fprintf(
w,
"- chain=%d calls=%d\n",
batch.ChainSelector(),
len(batch.Calls()),
); err != nil {
return err
}
}

return nil
}

# Registering with the engine

eng := proposalanalysis.NewAnalyzerEngine()
if err := eng.RegisterRenderer(PlainRenderer{}); err != nil {
return err
}

To render output, call `eng.RenderTo(...)` with the renderer ID and a
`renderer.RenderRequest`.
*/
package renderer