github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/cli/util/cancel.go (about) 1 package util 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 8 "github.com/nspcc-dev/neo-go/cli/cmdargs" 9 "github.com/nspcc-dev/neo-go/cli/flags" 10 "github.com/nspcc-dev/neo-go/cli/options" 11 "github.com/nspcc-dev/neo-go/cli/txctx" 12 "github.com/nspcc-dev/neo-go/pkg/core/state" 13 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 14 "github.com/nspcc-dev/neo-go/pkg/neorpc/result" 15 "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" 16 "github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter" 17 "github.com/nspcc-dev/neo-go/pkg/util" 18 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 19 "github.com/urfave/cli" 20 ) 21 22 func cancelTx(ctx *cli.Context) error { 23 args := ctx.Args() 24 if len(args) == 0 { 25 return cli.NewExitError("transaction hash is missing", 1) 26 } else if len(args) > 1 { 27 return cli.NewExitError("only one transaction hash is accepted", 1) 28 } 29 30 txHash, err := util.Uint256DecodeStringLE(strings.TrimPrefix(args[0], "0x")) 31 if err != nil { 32 return cli.NewExitError(fmt.Sprintf("invalid tx hash: %s", args[0]), 1) 33 } 34 35 gctx, cancel := options.GetTimeoutContext(ctx) 36 defer cancel() 37 38 acc, w, err := options.GetAccFromContext(ctx) 39 if err != nil { 40 return cli.NewExitError(fmt.Errorf("failed to get account from context to sign the conflicting transaction: %w", err), 1) 41 } 42 defer w.Close() 43 44 signers, err := cmdargs.GetSignersAccounts(acc, w, nil, transaction.CalledByEntry) 45 if err != nil { 46 return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1) 47 } 48 c, a, exitErr := options.GetRPCWithActor(gctx, ctx, signers) 49 if exitErr != nil { 50 return exitErr 51 } 52 53 mainTx, _ := c.GetRawTransactionVerbose(txHash) 54 if mainTx != nil && !mainTx.Blockhash.Equals(util.Uint256{}) { 55 return cli.NewExitError(fmt.Errorf("target transaction %s is accepted at block %s", txHash, mainTx.Blockhash.StringLE()), 1) 56 } 57 58 if mainTx != nil && !mainTx.HasSigner(acc.ScriptHash()) { 59 return cli.NewExitError(fmt.Errorf("account %s is not a signer of the conflicting transaction", acc.Address), 1) 60 } 61 62 resHash, resVub, err := a.SendTunedRun([]byte{byte(opcode.RET)}, []transaction.Attribute{{Type: transaction.ConflictsT, Value: &transaction.Conflicts{Hash: txHash}}}, func(r *result.Invoke, t *transaction.Transaction) error { 63 err := actor.DefaultCheckerModifier(r, t) 64 if err != nil { 65 return err 66 } 67 if mainTx != nil && t.NetworkFee < mainTx.NetworkFee+1 { 68 t.NetworkFee = mainTx.NetworkFee + 1 69 } 70 t.NetworkFee += int64(flags.Fixed8FromContext(ctx, "gas")) 71 if mainTx != nil { 72 t.ValidUntilBlock = mainTx.ValidUntilBlock 73 } 74 return nil 75 }) 76 if err != nil { 77 return cli.NewExitError(fmt.Errorf("failed to send conflicting transaction: %w", err), 1) 78 } 79 var res *state.AppExecResult 80 if ctx.Bool("await") { 81 res, err = a.WaitAny(gctx, resVub, txHash, resHash) 82 if err != nil { 83 if errors.Is(err, waiter.ErrTxNotAccepted) { 84 if mainTx == nil { 85 return cli.NewExitError(fmt.Errorf("neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of conlicting transaction). Main transaction is unknown to the provided RPC node, thus still has chances to be accepted, you may try cancellation again", resVub), 1) 86 } 87 fmt.Fprintf(ctx.App.Writer, "Neither target nor conflicting transaction is accepted before the current height %d (ValidUntilBlock value of both target and conflicting transactions). Main transaction is not valid anymore, cancellation is successful\n", resVub) 88 return nil 89 } 90 return cli.NewExitError(fmt.Errorf("failed to await target/ conflicting transaction %s/ %s: %w", txHash.StringLE(), resHash.StringLE(), err), 1) 91 } 92 if txHash.Equals(res.Container) { 93 tx, err := c.GetRawTransactionVerbose(txHash) 94 if err != nil { 95 return cli.NewExitError(fmt.Errorf("target transaction %s is accepted", txHash), 1) 96 } 97 return cli.NewExitError(fmt.Errorf("target transaction %s is accepted at block %s", txHash, tx.Blockhash.StringLE()), 1) 98 } 99 fmt.Fprintln(ctx.App.Writer, "Conflicting transaction accepted") 100 } 101 txctx.DumpTransactionInfo(ctx.App.Writer, resHash, res) 102 return nil 103 }