Skip to content

Commit 68bcdde

Browse files
[OPT-406]: feat(mcms): core interfaces for proposal analyzer (#731)
Add interfaces for the core objects needed for the mcms proposal analyzer. There may be some changes to the interface as we refine the implementation. JIRA: https://smartcontract-it.atlassian.net/browse/OPT-406
1 parent 7b2c80f commit 68bcdde

6 files changed

Lines changed: 239 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package analyzer
2+
3+
type AnalyzedProposal interface {
4+
Annotated
5+
BatchOperations() AnalyzedBatchOperations
6+
}
7+
8+
type AnalyzedBatchOperation interface {
9+
Annotated
10+
ChainSelector() uint64
11+
Calls() AnalyzedCalls
12+
}
13+
14+
type AnalyzedBatchOperations []AnalyzedBatchOperation
15+
16+
type AnalyzedCalls []AnalyzedCall
17+
18+
type AnalyzedCall interface {
19+
Annotated
20+
To() string
21+
Name() string
22+
Inputs() AnalyzedParameters
23+
Outputs() AnalyzedParameters
24+
Data() []byte
25+
ContractType() string
26+
ContractVersion() string
27+
AdditionalFields() map[string]any
28+
}
29+
30+
type AnalyzedParameters []AnalyzedParameter
31+
32+
type AnalyzedParameter interface {
33+
Annotated
34+
Name() string
35+
Type() string
36+
Value() any
37+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package analyzer
2+
3+
import (
4+
"context"
5+
6+
"github.com/smartcontractkit/chainlink-deployments-framework/chain"
7+
"github.com/smartcontractkit/chainlink-deployments-framework/datastore"
8+
cldfdomain "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/domain"
9+
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/decoder"
10+
)
11+
12+
// AnalyzerContext provides access to the current stage of analysis within the proposal structure.
13+
// Only some accessor methods are relevant depending on which analyzer type is being executed.
14+
// Analysis proceeds from the most granular components (parameters), up through calls and batch operations, to the full proposal.
15+
// Therefore, context accessors are applicable for the current and higher (ancestor) analysis levels.
16+
// Accessors may return zero values when invoked at stages where corresponding context is not available.
17+
type AnalyzerContext interface {
18+
// Proposal returns the current proposal-level context.
19+
// This is primarily meaningful for ProposalAnalyzer execution.
20+
Proposal() AnalyzedProposal
21+
// BatchOperation returns the current batch operation context.
22+
// This is primarily meaningful for ProposalAnalyzer and BatchOperationAnalyzer execution.
23+
BatchOperation() AnalyzedBatchOperation
24+
// Call returns the current call-level context.
25+
// This is primarily meaningful for ProposalAnalyzer, BatchOperationAnalyzer and CallAnalyzer execution.
26+
Call() AnalyzedCall
27+
28+
// GetAnnotationsFrom returns annotations from a specific analyzer.
29+
// This is useful for accessing results from dependency analyzers.
30+
// Returns empty slice if the analyzer ID is not found or no annotations exist.
31+
GetAnnotationsFrom(analyzerID string) Annotations
32+
}
33+
34+
type ExecutionContext interface {
35+
Domain() cldfdomain.Domain
36+
EnvironmentName() string
37+
BlockChains() chain.BlockChains
38+
DataStore() datastore.DataStore
39+
}
40+
41+
// AnalyzeRequest encapsulates the analyzer and execution contexts passed to analyzer methods.
42+
type AnalyzeRequest struct {
43+
AnalyzerContext AnalyzerContext
44+
ExecutionContext ExecutionContext
45+
}
46+
47+
type BaseAnalyzer interface {
48+
ID() string
49+
// Dependencies returns the IDs of analyzers that must run before this analyzer.
50+
//
51+
// The returned strings MUST correspond to the ID() values of other registered analyzers.
52+
// Implementations MUST NOT introduce circular dependencies (directly or indirectly).
53+
//
54+
// Analyzers may depend only on other analyzers of the same type.
55+
// For example, a ProposalAnalyzer may only depend on other ProposalAnalyzer instances,
56+
// a BatchOperationAnalyzer may only depend on other BatchOperationAnalyzer instances,
57+
// and a CallAnalyzer may only depend on other CallAnalyzer instances.
58+
// This restriction exists because analyzers of different types are already executed in a fixed dependency order:
59+
// parameter analyzers run before call analyzers, which run before batch operation analyzers, which in turn run before proposal analyzers.
60+
Dependencies() []string
61+
}
62+
63+
type ProposalAnalyzer interface {
64+
BaseAnalyzer
65+
CanAnalyze(ctx context.Context, req AnalyzeRequest, proposal decoder.DecodedTimelockProposal) bool
66+
Analyze(ctx context.Context, req AnalyzeRequest, proposal decoder.DecodedTimelockProposal) (Annotations, error)
67+
}
68+
69+
type BatchOperationAnalyzer interface {
70+
BaseAnalyzer
71+
CanAnalyze(ctx context.Context, req AnalyzeRequest, operation decoder.DecodedBatchOperation) bool
72+
Analyze(ctx context.Context, req AnalyzeRequest, operation decoder.DecodedBatchOperation) (Annotations, error)
73+
}
74+
75+
type CallAnalyzer interface {
76+
BaseAnalyzer
77+
CanAnalyze(ctx context.Context, req AnalyzeRequest, call decoder.DecodedCall) bool
78+
Analyze(ctx context.Context, req AnalyzeRequest, call decoder.DecodedCall) (Annotations, error)
79+
}
80+
81+
type ParameterAnalyzer interface {
82+
BaseAnalyzer
83+
CanAnalyze(ctx context.Context, req AnalyzeRequest, param decoder.DecodedParameter) bool
84+
Analyze(ctx context.Context, req AnalyzeRequest, param decoder.DecodedParameter) (Annotations, error)
85+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package analyzer
2+
3+
type Annotation interface {
4+
Name() string
5+
Type() string
6+
Value() any
7+
}
8+
9+
type Annotations []Annotation
10+
11+
type Annotated interface {
12+
// AddAnnotations mutates the underlying analyzed object by appending annotations.
13+
// Implementations are expected to be used by a single analysis pipeline and are
14+
// not required to provide internal synchronization for concurrent mutation.
15+
AddAnnotations(annotations ...Annotation)
16+
Annotations() Annotations
17+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package decoder
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
7+
"github.com/smartcontractkit/mcms"
8+
9+
"github.com/smartcontractkit/chainlink-deployments-framework/deployment"
10+
)
11+
12+
type DecodedTimelockProposal interface {
13+
BatchOperations() DecodedBatchOperations
14+
}
15+
16+
type DecodedBatchOperations []DecodedBatchOperation
17+
18+
type DecodedBatchOperation interface {
19+
ChainSelector() uint64
20+
Calls() DecodedCalls
21+
}
22+
23+
type DecodedCalls []DecodedCall
24+
25+
type DecodedCall interface {
26+
To() string // legacy analyzer uses "Address"
27+
Name() string // legacy analyzer uses "Method"
28+
Inputs() DecodedParameters
29+
Outputs() DecodedParameters
30+
Data() []byte
31+
AdditionalFields() json.RawMessage
32+
ContractType() string
33+
ContractVersion() string
34+
}
35+
36+
type DecodedParameters []DecodedParameter
37+
38+
type DecodedParameter interface {
39+
Name() string
40+
Type() string
41+
Value() any
42+
}
43+
44+
// ProposalDecoder decodes MCMS proposals into structured DecodedTimelockProposal
45+
type ProposalDecoder interface {
46+
Decode(ctx context.Context, env deployment.Environment, proposal *mcms.TimelockProposal) (DecodedTimelockProposal, error)
47+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package proposalanalysis
2+
3+
import (
4+
"context"
5+
"io"
6+
7+
"github.com/smartcontractkit/mcms"
8+
9+
"github.com/smartcontractkit/chainlink-deployments-framework/deployment"
10+
cldfdomain "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/domain"
11+
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/analyzer"
12+
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/renderer"
13+
experimentalanalyzer "github.com/smartcontractkit/chainlink-deployments-framework/experimental/analyzer"
14+
)
15+
16+
type DecodeInstructionFn = experimentalanalyzer.DecodeInstructionFn
17+
18+
// DecoderConfig configures the proposal decoder used by the analyzer engine.
19+
// The decoder is expected to support multi-chain proposal decoding using the
20+
// provided chain-specific mappings.
21+
type DecoderConfig struct {
22+
EVMABIMappings map[string]string
23+
SolanaDecoders map[string]DecodeInstructionFn
24+
}
25+
26+
type AnalyzerEngine interface {
27+
Run(ctx context.Context, domain cldfdomain.Domain, env deployment.Environment, proposal *mcms.TimelockProposal) (analyzer.AnalyzedProposal, error)
28+
29+
RegisterAnalyzer(analyzer analyzer.BaseAnalyzer) error
30+
31+
RegisterRenderer(renderer renderer.Renderer) error
32+
33+
RenderTo(w io.Writer, rendererID string, proposal analyzer.AnalyzedProposal) error
34+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package renderer
2+
3+
import (
4+
"io"
5+
6+
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalanalysis/analyzer"
7+
)
8+
9+
// RenderRequest encapsulates the domain and environment name
10+
type RenderRequest struct {
11+
Domain string
12+
EnvironmentName string
13+
}
14+
15+
// Renderer transforms an AnalyzedProposal into a specific output format
16+
type Renderer interface {
17+
ID() string
18+
RenderTo(w io.Writer, req RenderRequest, proposal analyzer.AnalyzedProposal) error
19+
}

0 commit comments

Comments
 (0)