Skip to content

Commit 6a1a1c8

Browse files
add changeset for secrets commands
1 parent 7e780dd commit 6a1a1c8

File tree

5 files changed

+273
-111
lines changed

5 files changed

+273
-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: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/hex"
55
"encoding/json"
66
"fmt"
7+
78
"net/http"
89
"os"
910
"path/filepath"
@@ -18,12 +19,16 @@ import (
1819

1920
"github.com/smartcontractkit/chainlink-common/pkg/capabilities/actions/vault"
2021
"github.com/smartcontractkit/chainlink-common/pkg/jsonrpc2"
22+
"github.com/smartcontractkit/chainlink/deployment/cre/workflow_registry/v2/changeset"
2123
"github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaulttypes"
2224

25+
"github.com/smartcontractkit/cre-cli/cmd/client"
26+
cmdCommon "github.com/smartcontractkit/cre-cli/cmd/common"
2327
"github.com/smartcontractkit/cre-cli/cmd/secrets/common"
2428
"github.com/smartcontractkit/cre-cli/internal/constants"
2529
"github.com/smartcontractkit/cre-cli/internal/runtime"
2630
"github.com/smartcontractkit/cre-cli/internal/settings"
31+
"github.com/smartcontractkit/cre-cli/internal/types"
2732
"github.com/smartcontractkit/cre-cli/internal/validation"
2833
)
2934

@@ -144,44 +149,98 @@ func Execute(h *common.Handler, inputs DeleteSecretsInputs, duration time.Durati
144149
return fmt.Errorf("failed to calculate request digest: %w", err)
145150
}
146151

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)
152+
gatewayPost := func() error {
153+
respBody, status, err := h.Gw.Post(requestBody)
165154
if err != nil {
166-
return fmt.Errorf("failed to pack allowlist tx: %w", err)
155+
return err
167156
}
168-
return h.LogMSIGNextSteps(txData, digest, bundlePath)
157+
if status != http.StatusOK {
158+
return fmt.Errorf("gateway returned a non-200 status code: %d", status)
159+
}
160+
return h.ParseVaultGatewayResponse(vaulttypes.MethodSecretsDelete, respBody)
169161
}
170162

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

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

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

0 commit comments

Comments
 (0)