github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/cli/txctx/tx.go (about)

     1  /*
     2  Package txctx contains helper functions that deal with transactions in CLI context.
     3  */
     4  package txctx
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"time"
    10  
    11  	"github.com/nspcc-dev/neo-go/cli/flags"
    12  	"github.com/nspcc-dev/neo-go/cli/input"
    13  	"github.com/nspcc-dev/neo-go/cli/paramcontext"
    14  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    15  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    16  	"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
    17  	"github.com/nspcc-dev/neo-go/pkg/util"
    18  	"github.com/nspcc-dev/neo-go/pkg/wallet"
    19  	"github.com/urfave/cli"
    20  )
    21  
    22  var (
    23  	// GasFlag is a flag used for the additional network fee.
    24  	GasFlag = flags.Fixed8Flag{
    25  		Name:  "gas, g",
    26  		Usage: "network fee to add to the transaction (prioritizing it)",
    27  	}
    28  	// SysGasFlag is a flag used for the additional system fee.
    29  	SysGasFlag = flags.Fixed8Flag{
    30  		Name:  "sysgas, e",
    31  		Usage: "system fee to add to the transaction (compensating for execution)",
    32  	}
    33  	// OutFlag is a flag used for file output.
    34  	OutFlag = cli.StringFlag{
    35  		Name:  "out",
    36  		Usage: "file (JSON) to put signature context with a transaction to",
    37  	}
    38  	// ForceFlag is a flag used to force transaction send.
    39  	ForceFlag = cli.BoolFlag{
    40  		Name:  "force",
    41  		Usage: "Do not ask for a confirmation (and ignore errors)",
    42  	}
    43  	// AwaitFlag is a flag used to wait for the transaction to be included in a block.
    44  	AwaitFlag = cli.BoolFlag{
    45  		Name:  "await",
    46  		Usage: "wait for the transaction to be included in a block",
    47  	}
    48  )
    49  
    50  // SignAndSend adds network and system fees to the provided transaction and
    51  // either sends it to the network (with a confirmation or --force flag) or saves
    52  // it into a file (given in the --out flag).
    53  func SignAndSend(ctx *cli.Context, act *actor.Actor, acc *wallet.Account, tx *transaction.Transaction) error {
    54  	var (
    55  		err    error
    56  		gas    = flags.Fixed8FromContext(ctx, "gas")
    57  		sysgas = flags.Fixed8FromContext(ctx, "sysgas")
    58  		ver    = act.GetVersion()
    59  		aer    *state.AppExecResult
    60  	)
    61  
    62  	tx.SystemFee += int64(sysgas)
    63  	tx.NetworkFee += int64(gas)
    64  
    65  	if outFile := ctx.String("out"); outFile != "" {
    66  		// Make a long-lived transaction, it's to be signed manually.
    67  		tx.ValidUntilBlock += (ver.Protocol.MaxValidUntilBlockIncrement - uint32(ver.Protocol.ValidatorsCount)) - 2
    68  		err = paramcontext.InitAndSave(ver.Protocol.Network, tx, acc, outFile)
    69  	} else {
    70  		if !ctx.Bool("force") {
    71  			promptTime := time.Now()
    72  			err := input.ConfirmTx(ctx.App.Writer, tx)
    73  			if err != nil {
    74  				return cli.NewExitError(err, 1)
    75  			}
    76  			waitTime := time.Since(promptTime)
    77  			// Compensate for confirmation waiting.
    78  			tx.ValidUntilBlock += uint32(waitTime.Milliseconds()/int64(ver.Protocol.MillisecondsPerBlock)) + 2
    79  		}
    80  		var (
    81  			resTx util.Uint256
    82  			vub   uint32
    83  		)
    84  		resTx, vub, err = act.SignAndSend(tx)
    85  		if err != nil {
    86  			return cli.NewExitError(err, 1)
    87  		}
    88  		if ctx.Bool("await") {
    89  			aer, err = act.Wait(resTx, vub, err)
    90  			if err != nil {
    91  				return cli.NewExitError(fmt.Errorf("failed to await transaction %s: %w", resTx.StringLE(), err), 1)
    92  			}
    93  		}
    94  	}
    95  	if err != nil {
    96  		return cli.NewExitError(err, 1)
    97  	}
    98  
    99  	DumpTransactionInfo(ctx.App.Writer, tx.Hash(), aer)
   100  	return nil
   101  }
   102  
   103  // DumpTransactionInfo prints transaction info to the given writer.
   104  func DumpTransactionInfo(w io.Writer, h util.Uint256, res *state.AppExecResult) {
   105  	fmt.Fprintln(w, h.StringLE())
   106  	if res != nil {
   107  		fmt.Fprintf(w, "OnChain:\t%t\n", res != nil)
   108  		fmt.Fprintf(w, "VMState:\t%s\n", res.VMState.String())
   109  		if res.FaultException != "" {
   110  			fmt.Fprintf(w, "FaultException:\t%s\n", res.FaultException)
   111  		}
   112  	}
   113  }