From 850aae010326f87ea7fe3dd9054759e1fc650be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matev=C5=BE=20Jekovec?= Date: Thu, 7 Aug 2025 11:42:25 +0200 Subject: [PATCH 1/3] cmd/rofl/deploy: Show instance expiration date and top-up --- cmd/rofl/deploy.go | 39 +++++++++++++++++++-------------- examples/rofl/deploy.out.static | 3 ++- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/cmd/rofl/deploy.go b/cmd/rofl/deploy.go index 64729c57..bc7912fe 100644 --- a/cmd/rofl/deploy.go +++ b/cmd/rofl/deploy.go @@ -150,7 +150,7 @@ var ( }, } - obtainMachine := func() (*buildRofl.Machine, error) { + obtainMachine := func() (*buildRofl.Machine, *roflmarket.Instance, error) { if deployOffer != "" { machine.Offer = deployOffer } @@ -168,7 +168,7 @@ var ( } if offer == nil { showProviderOffers(ctx, npa, conn, *providerAddr) - return nil, fmt.Errorf("offer '%s' not found for provider '%s'", machine.Offer, providerAddr) + return nil, nil, fmt.Errorf("offer '%s' not found for provider '%s'", machine.Offer, providerAddr) } fmt.Printf("Taking offer:\n") @@ -176,7 +176,7 @@ var ( term := detectTerm(offer) if deployTermCount < 1 { - return nil, fmt.Errorf("number of terms must be at least 1") + return nil, nil, fmt.Errorf("number of terms must be at least 1") } // Calculate total price. @@ -206,23 +206,27 @@ var ( var machineID roflmarket.InstanceID if !common.BroadcastOrExportTransaction(ctx, npa, conn, sigTx, meta, &machineID) { - return nil, fmt.Errorf("broadcast transaction failed") + return nil, nil, fmt.Errorf("broadcast transaction failed") } rawMachineID, _ := machineID.MarshalText() - cobra.CheckErr(err) machine.ID = string(rawMachineID) deployment.Machines[deployMachine] = machine fmt.Printf("Created machine: %s\n", machine.ID) - return machine, nil + + var insDsc *roflmarket.Instance + insDsc, err = conn.Runtime(npa.ParaTime).ROFLMarket.Instance(ctx, client.RoundLatest, *providerAddr, machineID) + cobra.CheckErr(err) + + return machine, insDsc, nil } - doDeployMachine := func(machine *buildRofl.Machine) error { + doDeployMachine := func(machine *buildRofl.Machine) (*roflmarket.Instance, error) { // Deploy into existing machine. var machineID roflmarket.InstanceID if err = machineID.UnmarshalText([]byte(machine.ID)); err != nil { - return fmt.Errorf("malformed machine ID: %w", err) + return nil, fmt.Errorf("malformed machine ID: %w", err) } fmt.Printf("Deploying into existing machine: %s\n", machine.ID) @@ -237,8 +241,8 @@ var ( if deployReplaceMachine { fmt.Printf("Machine instance not found. Obtaining new one...") machine.ID = "" - _, err = obtainMachine() - return err + _, _, err = obtainMachine() + return nil, err } cobra.CheckErr("Machine instance not found.\nTip: If your instance expired, run this command with --replace-machine flag to replace it with a new machine.") @@ -248,7 +252,7 @@ var ( if insDsc.Deployment != nil && insDsc.Deployment.AppID != machineDeployment.AppID && !deployForce { fmt.Printf("Machine already contains a deployment of ROFL app '%s'.\n", insDsc.Deployment.AppID) fmt.Printf("You are trying to replace it with ROFL app '%s'.\n", machineDeployment.AppID) - return fmt.Errorf("refusing to change existing ROFL app, use --force to override") + return nil, fmt.Errorf("refusing to change existing ROFL app, use --force to override") } // Prepare transaction. @@ -269,23 +273,26 @@ var ( cobra.CheckErr(err) if !common.BroadcastOrExportTransaction(ctx, npa, conn, sigTx, meta, nil) { - return fmt.Errorf("broadcast transaction failed") + return nil, fmt.Errorf("broadcast transaction failed") } - return nil + return insDsc, nil } + var insDsc *roflmarket.Instance if machine.ID == "" { // When machine is not set, we need to obtain one. fmt.Printf("No pre-existing machine configured, creating a new one...\n") - machine, err = obtainMachine() + machine, insDsc, err = obtainMachine() cobra.CheckErr(err) } else { - cobra.CheckErr(doDeployMachine(machine)) + insDsc, err = doDeployMachine(machine) + cobra.CheckErr(err) } fmt.Printf("Deployment into machine scheduled.\n") - fmt.Printf("Use `oasis rofl machine show` to see status.\n") + fmt.Printf("This machine expires on %s. Use `oasis rofl machine top-up` to extend it.\n", time.Unix(int64(insDsc.PaidUntil), 0)) + fmt.Printf("Use `oasis rofl machine show` to check status.\n") if err = manifest.Save(); err != nil { cobra.CheckErr(fmt.Errorf("failed to update manifest: %w", err)) diff --git a/examples/rofl/deploy.out.static b/examples/rofl/deploy.out.static index 23ba40fa..5e47866b 100644 --- a/examples/rofl/deploy.out.static +++ b/examples/rofl/deploy.out.static @@ -41,4 +41,5 @@ Transaction hash: bce96976f38485546b5950f8b2a7f9b7c43b9656935cc472a90680187469f4 Execution successful. Created machine: 0000000000000000 Deployment into machine scheduled. -Use `oasis rofl machine show` to see status. +This machine expires on 2025-08-07 12:35:47 +0200 CEST. Use `oasis rofl machine top-up` to extend it. +Use `oasis rofl machine show` to check status. From 9dc7f63191e1c1d5f6107687ccae54231d13f478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matev=C5=BE=20Jekovec?= Date: Thu, 7 Aug 2025 17:25:07 +0200 Subject: [PATCH 2/3] cmd/rofl: Move term and termCount params to roflCommon --- cmd/rofl/common/flags.go | 13 +++++++++++++ cmd/rofl/deploy.go | 17 +++++++---------- cmd/rofl/machine/mgmt.go | 25 ++++++++++--------------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/cmd/rofl/common/flags.go b/cmd/rofl/common/flags.go index c63d1b08..77084a5e 100644 --- a/cmd/rofl/common/flags.go +++ b/cmd/rofl/common/flags.go @@ -16,6 +16,9 @@ var ( // NoUpdateFlag is the flag for disabling the rofl.yaml manifest file update. NoUpdateFlag *flag.FlagSet + // TermFlags provide the term and count setting. + TermFlags *flag.FlagSet + // DeploymentName is the name of the ROFL app deployment. DeploymentName string @@ -24,6 +27,12 @@ var ( // NoUpdate disables updating the rofl.yaml manifest file. NoUpdate bool + + // Term specifies the rental base unit. + Term string + + // TermCount specific the rental base unit multiplier. + TermCount uint64 ) func init() { @@ -35,4 +44,8 @@ func init() { NoUpdateFlag = flag.NewFlagSet("", flag.ContinueOnError) NoUpdateFlag.BoolVar(&NoUpdate, "no-update-manifest", false, "do not update the manifest") + + TermFlags = flag.NewFlagSet("", flag.ContinueOnError) + TermFlags.StringVar(&Term, "term", "", "term to pay for in advance [hour, month, year]") + TermFlags.Uint64Var(&TermCount, "term-count", 1, "number of terms to pay for in advance") } diff --git a/cmd/rofl/deploy.go b/cmd/rofl/deploy.go index bc7912fe..04058b1d 100644 --- a/cmd/rofl/deploy.go +++ b/cmd/rofl/deploy.go @@ -38,8 +38,6 @@ var ( deployProvider string deployOffer string deployMachine string - deployTerm string - deployTermCount uint64 deployForce bool deployShowOffers bool deployReplaceMachine bool @@ -175,12 +173,12 @@ var ( showProviderOffer(ctx, offer) term := detectTerm(offer) - if deployTermCount < 1 { + if roflCommon.TermCount < 1 { return nil, nil, fmt.Errorf("number of terms must be at least 1") } // Calculate total price. - qTermCount := quantity.NewFromUint64(deployTermCount) + qTermCount := quantity.NewFromUint64(roflCommon.TermCount) totalPrice, ok := offer.Payment.Native.Terms[term] if !ok { cobra.CheckErr("internal error: previously selected term not found in offer terms") @@ -197,7 +195,7 @@ var ( Offer: offer.ID, Deployment: &machineDeployment, Term: term, - TermCount: deployTermCount, + TermCount: roflCommon.TermCount, }) var sigTx, meta any @@ -334,11 +332,11 @@ func detectTerm(offer *roflmarket.Offer) (term roflmarket.Term) { cobra.CheckErr(fmt.Errorf("no payment terms available for offer '%s'", offer.ID)) } - if deployTerm != "" { + if roflCommon.Term != "" { // Custom deploy term. - term = roflCommon.ParseMachineTerm(deployTerm) + term = roflCommon.ParseMachineTerm(roflCommon.Term) if _, ok := offer.Payment.Native.Terms[term]; !ok { - cobra.CheckErr(fmt.Errorf("term '%s' is not available for offer '%s'", deployTerm, offer.ID)) + cobra.CheckErr(fmt.Errorf("term '%s' is not available for offer '%s'", roflCommon.Term, offer.ID)) } return } @@ -454,8 +452,6 @@ func init() { providerFlags.StringVar(&deployProvider, "provider", "", "set the provider address") providerFlags.StringVar(&deployOffer, "offer", "", "set the provider's offer identifier") providerFlags.StringVar(&deployMachine, "machine", buildRofl.DefaultMachineName, "machine to deploy into") - providerFlags.StringVar(&deployTerm, "term", "", "term to pay for in advance") - providerFlags.Uint64Var(&deployTermCount, "term-count", 1, "number of terms to pay for in advance") providerFlags.BoolVar(&deployForce, "force", false, "force deployment") providerFlags.BoolVar(&deployShowOffers, "show-offers", false, "show all provider offers and quit") providerFlags.BoolVar(&deployReplaceMachine, "replace-machine", false, "rent a new machine if the provided one expired") @@ -464,4 +460,5 @@ func init() { deployCmd.Flags().AddFlagSet(providerFlags) deployCmd.Flags().AddFlagSet(roflCommon.DeploymentFlags) deployCmd.Flags().AddFlagSet(roflCommon.WipeFlags) + deployCmd.Flags().AddFlagSet(roflCommon.TermFlags) } diff --git a/cmd/rofl/machine/mgmt.go b/cmd/rofl/machine/mgmt.go index 9d43e3ef..c9f09fec 100644 --- a/cmd/rofl/machine/mgmt.go +++ b/cmd/rofl/machine/mgmt.go @@ -6,7 +6,6 @@ import ( "time" "github.com/spf13/cobra" - flag "github.com/spf13/pflag" "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-sdk/client-sdk/go/client" @@ -22,9 +21,6 @@ import ( ) var ( - topUpTerm string - topUpTermCount uint64 - showCmd = &cobra.Command{ Use: "show []", Short: "Show information about a machine", @@ -262,13 +258,16 @@ var ( // Resolve provider address. providerAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, machine.Provider) if err != nil { - cobra.CheckErr(fmt.Sprintf("Invalid provider address: %s", err)) + cobra.CheckErr(fmt.Sprintf("invalid provider address: %s", err)) } // Parse machine payment term. - term := roflCommon.ParseMachineTerm(topUpTerm) - if topUpTermCount < 1 { - cobra.CheckErr("Number of terms must be at least 1.") + if roflCommon.Term == "" { + cobra.CheckErr("no term period specified. Use --term to specify it") + } + term := roflCommon.ParseMachineTerm(roflCommon.Term) + if roflCommon.TermCount < 1 { + cobra.CheckErr("number of terms must be at least 1.") } // When not in offline mode, connect to the given network endpoint. @@ -281,14 +280,14 @@ var ( fmt.Printf("Using provider: %s (%s)\n", machine.Provider, providerAddr) fmt.Printf("Top-up machine: %s [%s]\n", machineName, machine.ID) - fmt.Printf("Top-up term: %d x %s\n", topUpTermCount, topUpTerm) + fmt.Printf("Top-up term: %d x %s\n", roflCommon.TermCount, roflCommon.Term) // Prepare transaction. tx := roflmarket.NewInstanceTopUpTx(nil, &roflmarket.InstanceTopUp{ Provider: *providerAddr, ID: machineID, Term: term, - TermCount: topUpTermCount, + TermCount: roflCommon.TermCount, }) acc := common.LoadAccount(cliConfig.Global(), npa.AccountName) @@ -402,10 +401,6 @@ func showCommandArgs[V any](npa *common.NPASelection, raw []byte, args V) { } func init() { - topUpFlags := flag.NewFlagSet("", flag.ContinueOnError) - topUpFlags.StringVar(&topUpTerm, "term", roflCommon.TermMonth, "term to pay for in advance") - topUpFlags.Uint64Var(&topUpTermCount, "term-count", 1, "number of terms to pay for in advance") - showCmd.Flags().AddFlagSet(common.SelectorFlags) showCmd.Flags().AddFlagSet(roflCommon.DeploymentFlags) @@ -426,5 +421,5 @@ func init() { topUpCmd.Flags().AddFlagSet(common.SelectorFlags) topUpCmd.Flags().AddFlagSet(common.RuntimeTxFlags) topUpCmd.Flags().AddFlagSet(roflCommon.DeploymentFlags) - topUpCmd.Flags().AddFlagSet(topUpFlags) + topUpCmd.Flags().AddFlagSet(roflCommon.TermFlags) } From 2a44a8298750fc0a2188e3df87d677ed079b6738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matev=C5=BE=20Jekovec?= Date: Thu, 7 Aug 2025 12:07:04 +0200 Subject: [PATCH 3/3] docs: Document rofl top-up command --- docs/rofl.md | 13 ++++++++++ examples/rofl/machine-top-up.in.static | 1 + examples/rofl/machine-top-up.out.static | 34 +++++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 examples/rofl/machine-top-up.in.static create mode 100644 examples/rofl/machine-top-up.out.static diff --git a/docs/rofl.md b/docs/rofl.md index 34cdd1b7..8adaa61d 100644 --- a/docs/rofl.md +++ b/docs/rofl.md @@ -194,6 +194,19 @@ offer: [ROFL marketplace]: https://github.com/oasisprotocol/oasis-sdk/blob/main/docs/rofl/features/marketplace.mdx +## Top-up payment for the machine {#top-up} + +Run `rofl machine top-up` to extend the rental of the machine obtained from +the [ROFL marketplace]. The rental is extended under the terms of the original +offer. Specify the extension period with [`--term`][term-flags] and +[`--term-count`][term-flags] parameters. + +![code shell](../examples/rofl/machine-top-up.in.static) + +![code](../examples/rofl/machine-top-up.out.static) + +[term-flags]: #deploy + ## Advanced ### Upgrade ROFL app dependencies {#upgrade} diff --git a/examples/rofl/machine-top-up.in.static b/examples/rofl/machine-top-up.in.static new file mode 100644 index 00000000..1861e27d --- /dev/null +++ b/examples/rofl/machine-top-up.in.static @@ -0,0 +1 @@ +oasis rofl machine top-up --term hour --term-count 12 diff --git a/examples/rofl/machine-top-up.out.static b/examples/rofl/machine-top-up.out.static new file mode 100644 index 00000000..5815af84 --- /dev/null +++ b/examples/rofl/machine-top-up.out.static @@ -0,0 +1,34 @@ +Using provider: oasis1qp2ens0hsp7gh23wajxa4hpetkdek3swyyulyrmz (oasis1qp2ens0hsp7gh23wajxa4hpetkdek3swyyulyrmz) +Top-up machine: default [000000000000022a] +Top-up term: 12 x hour +Unlock your account. +? Passphrase: +You are about to sign the following transaction: +Format: plain +Method: roflmarket.InstanceTopUp +Body: + { + "provider": "oasis1qp2ens0hsp7gh23wajxa4hpetkdek3swyyulyrmz", + "id": "000000000000022a", + "term": 1, + "term_count": 12 + } +Authorized signer(s): + 1. AyZKkxNFeyqLI5HGTYqEmCcYxKGo/kueOzSHzdnrSePO (secp256k1eth) + Nonce: 996 +Fee: + Amount: 0.0013614 TEST + Gas limit: 13614 + (gas price: 0.0000001 TEST per gas unit) + +Network: testnet +ParaTime: sapphire +Account: dave +? Sign this transaction? Yes +(In case you are using a hardware-based signer you may need to confirm on device.) +Broadcasting transaction... +Transaction included in block successfully. +Round: 12917124 +Transaction hash: 094ddc21c39acd96789153003016bda5d2a0077e7be11635bb755b6c49c287ac +Execution successful. +Machine topped up.