-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cmd/rofl): Add initial support for ROFL app management
- Loading branch information
Showing
3 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,269 @@ | ||
package cmd | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
flag "github.com/spf13/pflag" | ||
|
||
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/client" | ||
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/connection" | ||
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/helpers" | ||
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/modules/rofl" | ||
|
||
"github.com/oasisprotocol/cli/cmd/common" | ||
cliConfig "github.com/oasisprotocol/cli/config" | ||
) | ||
|
||
var ( | ||
roflPolicyFn string | ||
roflAdminAddress string | ||
|
||
roflCmd = &cobra.Command{ | ||
Use: "rofl", | ||
Short: "ROFL app management", | ||
Aliases: []string{"r"}, | ||
} | ||
|
||
roflCreateCmd = &cobra.Command{ | ||
Use: "create <policy.json>", | ||
Short: "Create a new ROFL application", | ||
Args: cobra.ExactArgs(1), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
cfg := cliConfig.Global() | ||
npa := common.GetNPASelection(cfg) | ||
txCfg := common.GetTransactionConfig() | ||
policyFn := args[0] | ||
|
||
if npa.Account == nil { | ||
cobra.CheckErr("no accounts configured in your wallet") | ||
} | ||
if npa.ParaTime == nil { | ||
cobra.CheckErr("no ParaTime selected") | ||
} | ||
|
||
policy := roflLoadPolicy(policyFn) | ||
|
||
// When not in offline mode, connect to the given network endpoint. | ||
ctx := context.Background() | ||
var conn connection.Connection | ||
if !txCfg.Offline { | ||
var err error | ||
conn, err = connection.Connect(ctx, npa.Network) | ||
cobra.CheckErr(err) | ||
} | ||
|
||
// Prepare transaction. | ||
tx := rofl.NewCreateTx(nil, &rofl.Create{ | ||
Policy: *policy, | ||
}) | ||
|
||
acc := common.LoadAccount(cfg, npa.AccountName) | ||
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil) | ||
cobra.CheckErr(err) | ||
|
||
var appID rofl.AppID | ||
if !common.BroadcastOrExportTransaction(ctx, npa.ParaTime, conn, sigTx, meta, &appID) { | ||
return | ||
} | ||
|
||
fmt.Printf("Created ROFL application: %s\n", appID) | ||
}, | ||
} | ||
|
||
roflUpdateCmd = &cobra.Command{ | ||
Use: "update <app-id> --policy <policy.json> --admin <address>", | ||
Short: "Create a new ROFL application", | ||
Args: cobra.ExactArgs(1), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
cfg := cliConfig.Global() | ||
npa := common.GetNPASelection(cfg) | ||
txCfg := common.GetTransactionConfig() | ||
rawAppID := args[0] | ||
var appID rofl.AppID | ||
if err := appID.UnmarshalText([]byte(rawAppID)); err != nil { | ||
cobra.CheckErr(fmt.Errorf("malformed ROFL app ID: %w", err)) | ||
} | ||
|
||
if npa.Account == nil { | ||
cobra.CheckErr("no accounts configured in your wallet") | ||
} | ||
if npa.ParaTime == nil { | ||
cobra.CheckErr("no ParaTime selected") | ||
} | ||
|
||
if roflPolicyFn == "" || roflAdminAddress == "" { | ||
fmt.Println("You must specify both --policy and --admin.") | ||
return | ||
} | ||
|
||
// When not in offline mode, connect to the given network endpoint. | ||
ctx := context.Background() | ||
var conn connection.Connection | ||
if !txCfg.Offline { | ||
var err error | ||
conn, err = connection.Connect(ctx, npa.Network) | ||
cobra.CheckErr(err) | ||
} | ||
|
||
updateBody := rofl.Update{ | ||
ID: appID, | ||
Policy: *roflLoadPolicy(roflPolicyFn), | ||
} | ||
|
||
// Update administrator address. | ||
if roflAdminAddress == "self" { | ||
roflAdminAddress = npa.AccountName | ||
} | ||
var err error | ||
updateBody.Admin, _, err = common.ResolveLocalAccountOrAddress(npa.Network, roflAdminAddress) | ||
if err != nil { | ||
cobra.CheckErr(fmt.Errorf("bad administrator address: %w", err)) | ||
} | ||
|
||
// Prepare transaction. | ||
tx := rofl.NewUpdateTx(nil, &updateBody) | ||
|
||
acc := common.LoadAccount(cfg, npa.AccountName) | ||
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil) | ||
cobra.CheckErr(err) | ||
|
||
common.BroadcastOrExportTransaction(ctx, npa.ParaTime, conn, sigTx, meta, nil) | ||
}, | ||
} | ||
|
||
roflRemoveCmd = &cobra.Command{ | ||
Use: "remove <app-id>", | ||
Short: "Remove an existing ROFL application", | ||
Args: cobra.ExactArgs(1), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
cfg := cliConfig.Global() | ||
npa := common.GetNPASelection(cfg) | ||
txCfg := common.GetTransactionConfig() | ||
rawAppID := args[0] | ||
var appID rofl.AppID | ||
if err := appID.UnmarshalText([]byte(rawAppID)); err != nil { | ||
cobra.CheckErr(fmt.Errorf("malformed ROFL app ID: %w", err)) | ||
} | ||
|
||
if npa.Account == nil { | ||
cobra.CheckErr("no accounts configured in your wallet") | ||
} | ||
if npa.ParaTime == nil { | ||
cobra.CheckErr("no ParaTime selected") | ||
} | ||
|
||
// When not in offline mode, connect to the given network endpoint. | ||
ctx := context.Background() | ||
var conn connection.Connection | ||
if !txCfg.Offline { | ||
var err error | ||
conn, err = connection.Connect(ctx, npa.Network) | ||
cobra.CheckErr(err) | ||
} | ||
|
||
// Prepare transaction. | ||
tx := rofl.NewRemoveTx(nil, &rofl.Remove{ | ||
ID: appID, | ||
}) | ||
|
||
acc := common.LoadAccount(cfg, npa.AccountName) | ||
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil) | ||
cobra.CheckErr(err) | ||
|
||
common.BroadcastOrExportTransaction(ctx, npa.ParaTime, conn, sigTx, meta, nil) | ||
}, | ||
} | ||
|
||
roflShowCmd = &cobra.Command{ | ||
Use: "show <app-id>", | ||
Short: "Show information about a ROFL application", | ||
Args: cobra.ExactArgs(1), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
cfg := cliConfig.Global() | ||
npa := common.GetNPASelection(cfg) | ||
rawAppID := args[0] | ||
var appID rofl.AppID | ||
if err := appID.UnmarshalText([]byte(rawAppID)); err != nil { | ||
cobra.CheckErr(fmt.Errorf("malformed ROFL app ID: %w", err)) | ||
} | ||
|
||
// Establish connection with the target network. | ||
ctx := context.Background() | ||
conn, err := connection.Connect(ctx, npa.Network) | ||
cobra.CheckErr(err) | ||
|
||
appCfg, err := conn.Runtime(npa.ParaTime).ROFL.App(ctx, client.RoundLatest, appID) | ||
cobra.CheckErr(err) | ||
|
||
fmt.Printf("App ID: %s\n", appCfg.ID) | ||
fmt.Printf("Admin: ") | ||
switch appCfg.Admin { | ||
case nil: | ||
fmt.Printf("none\n") | ||
default: | ||
fmt.Printf("%s\n", *appCfg.Admin) | ||
} | ||
stakedAmnt := helpers.FormatParaTimeDenomination(npa.ParaTime, appCfg.Stake) | ||
fmt.Printf("Staked amount: %s\n", stakedAmnt) | ||
fmt.Printf("Policy:\n") | ||
policyJSON, _ := json.MarshalIndent(appCfg.Policy, " ", " ") | ||
fmt.Printf(" %s\n", string(policyJSON)) | ||
|
||
fmt.Println() | ||
fmt.Printf("=== Instances ===\n") | ||
|
||
appInstances, err := conn.Runtime(npa.ParaTime).ROFL.AppInstances(ctx, client.RoundLatest, appID) | ||
cobra.CheckErr(err) | ||
|
||
if len(appInstances) > 0 { | ||
for _, ai := range appInstances { | ||
fmt.Printf("- RAK: %s\n", ai.RAK) | ||
fmt.Printf(" Node ID: %s\n", ai.NodeID) | ||
fmt.Printf(" Expiration: %d\n", ai.Expiration) | ||
} | ||
} else { | ||
fmt.Println("No registered app instances.") | ||
} | ||
}, | ||
} | ||
) | ||
|
||
func roflLoadPolicy(fn string) *rofl.AppAuthPolicy { | ||
// Load app policy. | ||
rawPolicy, err := os.ReadFile(fn) | ||
cobra.CheckErr(err) | ||
|
||
// Parse policy. | ||
var policy rofl.AppAuthPolicy | ||
if err = json.Unmarshal(rawPolicy, &policy); err != nil { | ||
cobra.CheckErr(fmt.Errorf("malformed ROFL app policy: %w", err)) | ||
} | ||
return &policy | ||
} | ||
|
||
func init() { | ||
f := flag.NewFlagSet("", flag.ContinueOnError) | ||
f.StringVar(&roflPolicyFn, "policy", "", "set the ROFL application policy") | ||
f.StringVar(&roflAdminAddress, "admin", "", "set the administrator address") | ||
|
||
roflCreateCmd.Flags().AddFlagSet(common.SelectorFlags) | ||
roflCreateCmd.Flags().AddFlagSet(common.RuntimeTxFlags) | ||
|
||
roflUpdateCmd.Flags().AddFlagSet(common.SelectorFlags) | ||
roflUpdateCmd.Flags().AddFlagSet(common.RuntimeTxFlags) | ||
roflUpdateCmd.Flags().AddFlagSet(f) | ||
|
||
roflRemoveCmd.Flags().AddFlagSet(common.SelectorFlags) | ||
roflRemoveCmd.Flags().AddFlagSet(common.RuntimeTxFlags) | ||
|
||
roflShowCmd.Flags().AddFlagSet(common.SelectorFlags) | ||
|
||
roflCmd.AddCommand(roflCreateCmd) | ||
roflCmd.AddCommand(roflUpdateCmd) | ||
roflCmd.AddCommand(roflRemoveCmd) | ||
roflCmd.AddCommand(roflShowCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters