Skip to content

Commit 4e1b37b

Browse files
add changeset for secrets commands
1 parent 7e780dd commit 4e1b37b

File tree

5 files changed

+275
-111
lines changed

5 files changed

+275
-111
lines changed

cmd/client/workflow_registry_v2_client.go

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -678,42 +678,38 @@ func (wrc *WorkflowRegistryV2Client) IsRequestAllowlisted(owner common.Address,
678678

679679
// AllowlistRequest sends the request digest to the WorkflowRegistry allowlist with a default expiry of now + 10 minutes.
680680
// `requestDigestHex` should be the hex string produced by utils.CalculateRequestDigest(...), with or without "0x".
681-
func (wrc *WorkflowRegistryV2Client) AllowlistRequest(requestDigest [32]byte, duration time.Duration) error {
681+
func (wrc *WorkflowRegistryV2Client) AllowlistRequest(requestDigest [32]byte, duration time.Duration) (*TxOutput, error) {
682682
var contract workflowRegistryV2Contract
683683
if wrc.Wr != nil {
684684
contract = wrc.Wr
685685
} else {
686686
c, err := workflow_registry_v2_wrapper.NewWorkflowRegistry(wrc.ContractAddress, wrc.EthClient.Client)
687687
if err != nil {
688688
wrc.Logger.Error().Err(err).Msg("Failed to connect for AllowlistRequest")
689-
return err
689+
return nil, err
690690
}
691691
contract = c
692692
}
693693

694694
// #nosec G115 -- int64 to uint32 conversion; Unix() returns seconds since epoch, which fits in uint32 until 2106
695695
deadline := uint32(time.Now().Add(duration).Unix())
696696

697-
// Send tx; keep the same "callContractMethodV2" pattern you used for read-only calls.
698-
// Here we return the tx hash string to the helper (it may log/track it).
699-
_, err := callContractMethodV2(wrc, func() (string, error) {
700-
tx, txErr := contract.AllowlistRequest(wrc.EthClient.NewTXOpts(), requestDigest, deadline)
701-
if txErr != nil {
702-
return "", txErr
703-
}
704-
// Return the tx hash string for visibility through the helper
705-
return tx.Hash().Hex(), nil
706-
})
697+
txFn := func(opts *bind.TransactOpts) (*types.Transaction, error) {
698+
return contract.AllowlistRequest(opts, requestDigest, deadline)
699+
}
700+
txOut, err := wrc.executeTransactionByTxType(txFn, "AllowlistRequest", "RequestAllowlisted", requestDigest, duration)
707701
if err != nil {
708-
wrc.Logger.Error().Err(err).Msg("AllowlistRequest tx failed")
709-
return err
702+
wrc.Logger.Error().
703+
Str("contract", wrc.ContractAddress.Hex()).
704+
Err(err).
705+
Msg("Failed to call AllowlistRequest")
706+
return nil, err
710707
}
711-
712708
wrc.Logger.Debug().
713709
Str("digest", hex.EncodeToString(requestDigest[:])).
714710
Str("deadline", time.Unix(int64(deadline), 0).UTC().Format(time.RFC3339)).
715711
Msg("AllowlistRequest submitted")
716-
return nil
712+
return &txOut, nil
717713
}
718714

719715
func callContractMethodV2[T any](wrc *WorkflowRegistryV2Client, contractMethod func() (T, error)) (T, error) {

cmd/secrets/common/handler.go

Lines changed: 86 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,19 @@ import (
2525
"github.com/smartcontractkit/chainlink-common/pkg/capabilities/actions/vault"
2626
"github.com/smartcontractkit/chainlink-common/pkg/jsonrpc2"
2727
"github.com/smartcontractkit/chainlink-evm/gethwrappers/workflow/generated/workflow_registry_wrapper_v2"
28+
"github.com/smartcontractkit/chainlink/deployment/cre/workflow_registry/v2/changeset"
2829
"github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaulttypes"
2930
"github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy"
3031

3132
"github.com/smartcontractkit/cre-cli/cmd/client"
33+
cmdCommon "github.com/smartcontractkit/cre-cli/cmd/common"
3234
"github.com/smartcontractkit/cre-cli/internal/client/graphqlclient"
3335
"github.com/smartcontractkit/cre-cli/internal/constants"
3436
"github.com/smartcontractkit/cre-cli/internal/credentials"
3537
"github.com/smartcontractkit/cre-cli/internal/environments"
3638
"github.com/smartcontractkit/cre-cli/internal/runtime"
39+
"github.com/smartcontractkit/cre-cli/internal/settings"
40+
"github.com/smartcontractkit/cre-cli/internal/types"
3741
"github.com/smartcontractkit/cre-cli/internal/validation"
3842
)
3943

@@ -61,6 +65,7 @@ type Handler struct {
6165
Gw GatewayClient
6266
Wrc *client.WorkflowRegistryV2Client
6367
Credentials *credentials.Credentials
68+
Settings *settings.Settings
6469
}
6570

6671
// NewHandler creates a new handler instance.
@@ -85,6 +90,7 @@ func NewHandler(ctx *runtime.Context, secretsFilePath string) (*Handler, error)
8590
OwnerAddress: ctx.Settings.Workflow.UserWorkflowSettings.WorkflowOwnerAddress,
8691
EnvironmentSet: ctx.EnvironmentSet,
8792
Credentials: ctx.Credentials,
93+
Settings: ctx.Settings,
8894
}
8995
h.Gw = &HTTPClient{URL: h.EnvironmentSet.GatewayURL, Client: &http.Client{Timeout: 90 * time.Second}}
9096

@@ -173,7 +179,6 @@ func (h *Handler) ValidateInputs(inputs UpsertSecretsInputs) error {
173179
}
174180

175181
// TODO: use TxType interface
176-
// TODO: implement changeset handling
177182
func (h *Handler) PackAllowlistRequestTxData(reqDigest [32]byte, duration time.Duration) (string, error) {
178183
contractABI, err := abi.JSON(strings.NewReader(workflow_registry_wrapper_v2.WorkflowRegistryMetaData.ABI))
179184
if err != nil {
@@ -404,19 +409,54 @@ func (h *Handler) Execute(
404409
return fmt.Errorf("unsupported method %q (expected %q or %q)", method, vaulttypes.MethodSecretsCreate, vaulttypes.MethodSecretsUpdate)
405410
}
406411

407-
// MSIG step 1: write bundle & exit
408-
if ownerType == constants.WorkflowOwnerTypeMSIG {
409-
baseDir := filepath.Dir(h.SecretsFilePath)
410-
filename := DeriveBundleFilename(digest) // <digest>.json
411-
bundlePath := filepath.Join(baseDir, filename)
412+
ownerAddr := common.HexToAddress(h.OwnerAddress)
413+
414+
allowlisted, err := h.Wrc.IsRequestAllowlisted(ownerAddr, digest)
415+
if err != nil {
416+
return fmt.Errorf("allowlist check failed: %w", err)
417+
}
418+
var txOut *client.TxOutput
419+
if !allowlisted {
420+
if txOut, err = h.Wrc.AllowlistRequest(digest, duration); err != nil {
421+
return fmt.Errorf("allowlist request failed: %w", err)
422+
}
423+
}
412424

413-
ub := &UnsignedBundle{
414-
RequestID: requestID,
415-
Method: method,
416-
DigestHex: "0x" + hex.EncodeToString(digest[:]),
417-
RequestBody: requestBody,
418-
CreatedAt: time.Now().UTC(),
425+
gatewayPost := func() error {
426+
respBody, status, err := h.Gw.Post(requestBody)
427+
if err != nil {
428+
return err
429+
}
430+
if status != http.StatusOK {
431+
return fmt.Errorf("gateway returned a non-200 status code: %d", status)
419432
}
433+
return h.ParseVaultGatewayResponse(method, respBody)
434+
}
435+
436+
if txOut == nil && allowlisted {
437+
fmt.Printf("Digest already allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n", ownerAddr.Hex(), digest)
438+
return gatewayPost()
439+
}
440+
441+
baseDir := filepath.Dir(h.SecretsFilePath)
442+
filename := DeriveBundleFilename(digest) // <digest>.json
443+
bundlePath := filepath.Join(baseDir, filename)
444+
445+
ub := &UnsignedBundle{
446+
RequestID: requestID,
447+
Method: method,
448+
DigestHex: "0x" + hex.EncodeToString(digest[:]),
449+
RequestBody: requestBody,
450+
CreatedAt: time.Now().UTC(),
451+
}
452+
453+
switch txOut.Type {
454+
case client.Regular:
455+
fmt.Println("Transaction confirmed")
456+
fmt.Printf("Digest allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n", ownerAddr.Hex(), digest)
457+
fmt.Printf("View on explorer: \033]8;;%s/tx/%s\033\\%s/tx/%s\033]8;;\033\\\n", h.EnvironmentSet.WorkflowRegistryChainExplorerURL, txOut.Hash, h.EnvironmentSet.WorkflowRegistryChainExplorerURL, txOut.Hash)
458+
return gatewayPost()
459+
case client.Raw:
420460
if err := SaveBundle(bundlePath, ub); err != nil {
421461
return fmt.Errorf("failed to save unsigned bundle at %s: %w", bundlePath, err)
422462
}
@@ -426,32 +466,45 @@ func (h *Handler) Execute(
426466
return fmt.Errorf("failed to pack allowlist tx: %w", err)
427467
}
428468
return h.LogMSIGNextSteps(txData, digest, bundlePath)
429-
}
469+
case client.Changeset:
470+
chainSelector, err := settings.GetChainSelectorByChainName(h.EnvironmentSet.WorkflowRegistryChainName)
471+
if err != nil {
472+
return fmt.Errorf("failed to get chain selector for chain %q: %w", h.EnvironmentSet.WorkflowRegistryChainName, err)
473+
}
474+
mcmsConfig, err := settings.GetMCMSConfig(h.Settings, chainSelector)
475+
if err != nil {
476+
fmt.Println("\nMCMS config not found or is incorrect, skipping MCMS config in changeset")
477+
}
478+
csFile := types.ChangesetFile{
479+
Environment: h.Settings.Workflow.CLDSettings.Environment,
480+
Domain: h.Settings.Workflow.CLDSettings.Domain,
481+
Changesets: []types.Changeset{
482+
{
483+
AllowlistRequest: &types.AllowlistRequest{
484+
Payload: changeset.UserAllowlistRequestInput{
485+
ExpiryTimestamp: uint32(time.Now().Add(duration).Unix()), // #nosec G115 -- int64 to uint32 conversion; Unix() returns seconds since epoch, which fits in uint32 until 2106
486+
RequestDigest: common.Bytes2Hex(digest[:]),
487+
ChainSelector: chainSelector,
488+
MCMSConfig: mcmsConfig,
489+
WorkflowRegistryQualifier: h.Settings.Workflow.CLDSettings.WorkflowRegistryQualifier,
490+
},
491+
},
492+
},
493+
},
494+
}
430495

431-
// EOA: allowlist (if needed) and POST
432-
ownerAddr := common.HexToAddress(h.OwnerAddress)
496+
fileName := fmt.Sprintf("AllowlistRequest_%s_%s_%s.yaml", requestID, h.Settings.Workflow.UserWorkflowSettings.WorkflowOwnerAddress, time.Now().Format("20060102_150405"))
433497

434-
allowlisted, err := h.Wrc.IsRequestAllowlisted(ownerAddr, digest)
435-
if err != nil {
436-
return fmt.Errorf("allowlist check failed: %w", err)
437-
}
438-
if !allowlisted {
439-
if err := h.Wrc.AllowlistRequest(digest, duration); err != nil {
440-
return fmt.Errorf("allowlist request failed: %w", err)
498+
if err := SaveBundle(bundlePath, ub); err != nil {
499+
return fmt.Errorf("failed to save unsigned bundle at %s: %w", bundlePath, err)
441500
}
442-
fmt.Printf("Digest allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n", ownerAddr.Hex(), digest)
443-
} else {
444-
fmt.Printf("Digest already allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n", ownerAddr.Hex(), digest)
445-
}
446501

447-
respBody, status, err := h.Gw.Post(requestBody)
448-
if err != nil {
449-
return err
450-
}
451-
if status != http.StatusOK {
452-
return fmt.Errorf("gateway returned a non-200 status code: %d", status)
502+
return cmdCommon.WriteChangesetFile(fileName, &csFile, h.Settings)
503+
504+
default:
505+
h.Log.Warn().Msgf("Unsupported transaction type: %s", txOut.Type)
453506
}
454-
return h.ParseVaultGatewayResponse(method, respBody)
507+
return nil
455508
}
456509

457510
// ParseVaultGatewayResponse parses the JSON-RPC response, decodes the SignedOCRResponse payload

cmd/secrets/delete/delete.go

Lines changed: 84 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ import (
1818

1919
"github.com/smartcontractkit/chainlink-common/pkg/capabilities/actions/vault"
2020
"github.com/smartcontractkit/chainlink-common/pkg/jsonrpc2"
21+
"github.com/smartcontractkit/chainlink/deployment/cre/workflow_registry/v2/changeset"
2122
"github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaulttypes"
2223

24+
"github.com/smartcontractkit/cre-cli/cmd/client"
25+
cmdCommon "github.com/smartcontractkit/cre-cli/cmd/common"
2326
"github.com/smartcontractkit/cre-cli/cmd/secrets/common"
2427
"github.com/smartcontractkit/cre-cli/internal/constants"
2528
"github.com/smartcontractkit/cre-cli/internal/runtime"
2629
"github.com/smartcontractkit/cre-cli/internal/settings"
30+
"github.com/smartcontractkit/cre-cli/internal/types"
2731
"github.com/smartcontractkit/cre-cli/internal/validation"
2832
)
2933

@@ -144,44 +148,101 @@ func Execute(h *common.Handler, inputs DeleteSecretsInputs, duration time.Durati
144148
return fmt.Errorf("failed to calculate request digest: %w", err)
145149
}
146150

147-
// ---------------- MSIG step 1: bundle and exit ----------------
148-
if ownerType == constants.WorkflowOwnerTypeMSIG {
149-
baseDir := filepath.Dir(h.SecretsFilePath)
150-
filename := common.DeriveBundleFilename(digest) // <digest>.json
151-
bundlePath := filepath.Join(baseDir, filename)
152-
153-
ub := &common.UnsignedBundle{
154-
RequestID: requestID,
155-
Method: vaulttypes.MethodSecretsDelete,
156-
DigestHex: "0x" + hex.EncodeToString(digest[:]),
157-
RequestBody: requestBody,
158-
CreatedAt: time.Now().UTC(),
159-
}
160-
if err := common.SaveBundle(bundlePath, ub); err != nil {
161-
return fmt.Errorf("failed to save unsigned bundle at %s: %w", bundlePath, err)
162-
}
163-
164-
txData, err := h.PackAllowlistRequestTxData(digest, duration)
151+
gatewayPost := func() error {
152+
respBody, status, err := h.Gw.Post(requestBody)
165153
if err != nil {
166-
return fmt.Errorf("failed to pack allowlist tx: %w", err)
154+
return err
167155
}
168-
return h.LogMSIGNextSteps(txData, digest, bundlePath)
156+
if status != http.StatusOK {
157+
return fmt.Errorf("gateway returned a non-200 status code: %d", status)
158+
}
159+
return h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsDelete, respBody)
169160
}
170161

171-
// ---------------- EOA: allowlist (if needed) and POST ----------------
172162
ownerAddr := ethcommon.HexToAddress(h.OwnerAddress)
173163

174164
allowlisted, err := h.Wrc.IsRequestAllowlisted(ownerAddr, digest)
175165
if err != nil {
176166
return fmt.Errorf("allowlist check failed: %w", err)
177167
}
168+
var txOut *client.TxOutput
178169
if !allowlisted {
179-
if err := h.Wrc.AllowlistRequest(digest, duration); err != nil {
170+
if txOut, err = h.Wrc.AllowlistRequest(digest, duration); err != nil {
180171
return fmt.Errorf("allowlist request failed: %w", err)
181172
}
182-
fmt.Printf("Digest allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n", ownerAddr.Hex(), digest)
183173
} else {
184174
fmt.Printf("Digest already allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n", ownerAddr.Hex(), digest)
175+
return gatewayPost()
176+
}
177+
178+
baseDir := filepath.Dir(h.SecretsFilePath)
179+
filename := common.DeriveBundleFilename(digest) // <digest>.json
180+
bundlePath := filepath.Join(baseDir, filename)
181+
182+
ub := &common.UnsignedBundle{
183+
RequestID: requestID,
184+
Method: vaulttypes.MethodSecretsDelete,
185+
DigestHex: "0x" + hex.EncodeToString(digest[:]),
186+
RequestBody: requestBody,
187+
CreatedAt: time.Now().UTC(),
188+
}
189+
190+
switch txOut.Type {
191+
case client.Regular:
192+
fmt.Println("Transaction confirmed")
193+
fmt.Printf("Digest allowlisted; proceeding to gateway POST: owner=%s, digest=0x%x\n", ownerAddr.Hex(), digest)
194+
fmt.Printf("View on explorer: \033]8;;%s/tx/%s\033\\%s/tx/%s\033]8;;\033\\\n", h.EnvironmentSet.WorkflowRegistryChainExplorerURL, txOut.Hash, h.EnvironmentSet.WorkflowRegistryChainExplorerURL, txOut.Hash)
195+
return gatewayPost()
196+
case client.Raw:
197+
198+
if err := common.SaveBundle(bundlePath, ub); err != nil {
199+
return fmt.Errorf("failed to save unsigned bundle at %s: %w", bundlePath, err)
200+
}
201+
202+
txData, err := h.PackAllowlistRequestTxData(digest, duration)
203+
if err != nil {
204+
return fmt.Errorf("failed to pack allowlist tx: %w", err)
205+
}
206+
return h.LogMSIGNextSteps(txData, digest, bundlePath)
207+
208+
case client.Changeset:
209+
chainSelector, err := settings.GetChainSelectorByChainName(h.EnvironmentSet.WorkflowRegistryChainName)
210+
if err != nil {
211+
return fmt.Errorf("failed to get chain selector for chain %q: %w", h.EnvironmentSet.WorkflowRegistryChainName, err)
212+
}
213+
mcmsConfig, err := settings.GetMCMSConfig(h.Settings, chainSelector)
214+
if err != nil {
215+
fmt.Println("\nMCMS config not found or is incorrect, skipping MCMS config in changeset")
216+
}
217+
csFile := types.ChangesetFile{
218+
Environment: h.Settings.Workflow.CLDSettings.Environment,
219+
Domain: h.Settings.Workflow.CLDSettings.Domain,
220+
Changesets: []types.Changeset{
221+
{
222+
AllowlistRequest: &types.AllowlistRequest{
223+
Payload: changeset.UserAllowlistRequestInput{
224+
ExpiryTimestamp: uint32(time.Now().Add(duration).Unix()), // #nosec G115 -- int64 to uint32 conversion; Unix() returns seconds since epoch, which fits in uint32 until 2106
225+
RequestDigest: ethcommon.Bytes2Hex(digest[:]),
226+
ChainSelector: chainSelector,
227+
MCMSConfig: mcmsConfig,
228+
WorkflowRegistryQualifier: h.Settings.Workflow.CLDSettings.WorkflowRegistryQualifier,
229+
},
230+
},
231+
},
232+
},
233+
}
234+
235+
fileName := fmt.Sprintf("AllowlistRequest_%s_%s_%s.yaml", requestID, h.Settings.Workflow.UserWorkflowSettings.WorkflowOwnerAddress, time.Now().Format("20060102_150405"))
236+
237+
if err := common.SaveBundle(bundlePath, ub); err != nil {
238+
return fmt.Errorf("failed to save unsigned bundle at %s: %w", bundlePath, err)
239+
}
240+
241+
return cmdCommon.WriteChangesetFile(fileName, &csFile, h.Settings)
242+
243+
default:
244+
h.Log.Warn().Msgf("Unsupported transaction type: %s", txOut.Type)
245+
185246
}
186247

187248
// POST to gateway (HTTPClient.Post has your retry policy)

0 commit comments

Comments
 (0)