2023-11-27 11:20:18 +00:00
package util
import (
2023-12-28 11:58:38 +00:00
"errors"
2023-11-27 11:20:18 +00:00
"fmt"
"strings"
2023-12-07 07:35:37 +00:00
"github.com/nspcc-dev/neo-go/cli/cmdargs"
2023-11-27 11:20:18 +00:00
"github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/options"
2023-12-28 11:58:38 +00:00
"github.com/nspcc-dev/neo-go/cli/txctx"
"github.com/nspcc-dev/neo-go/pkg/core/state"
2023-11-27 11:20:18 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
2023-12-28 11:58:38 +00:00
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
2023-11-27 11:20:18 +00:00
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/urfave/cli"
)
func cancelTx ( ctx * cli . Context ) error {
args := ctx . Args ( )
if len ( args ) == 0 {
return cli . NewExitError ( "transaction hash is missing" , 1 )
} else if len ( args ) > 1 {
return cli . NewExitError ( "only one transaction hash is accepted" , 1 )
}
txHash , err := util . Uint256DecodeStringLE ( strings . TrimPrefix ( args [ 0 ] , "0x" ) )
if err != nil {
return cli . NewExitError ( fmt . Sprintf ( "invalid tx hash: %s" , args [ 0 ] ) , 1 )
}
gctx , cancel := options . GetTimeoutContext ( ctx )
defer cancel ( )
2023-12-07 07:35:37 +00:00
acc , w , err := options . GetAccFromContext ( ctx )
2023-11-27 11:20:18 +00:00
if err != nil {
2023-12-07 07:35:37 +00:00
return cli . NewExitError ( fmt . Errorf ( "failed to get account from context to sign the conflicting transaction: %w" , err ) , 1 )
}
defer w . Close ( )
signers , err := cmdargs . GetSignersAccounts ( acc , w , nil , transaction . CalledByEntry )
if err != nil {
return cli . NewExitError ( fmt . Errorf ( "invalid signers: %w" , err ) , 1 )
}
c , a , exitErr := options . GetRPCWithActor ( gctx , ctx , signers )
if exitErr != nil {
return exitErr
2023-11-27 11:20:18 +00:00
}
mainTx , _ := c . GetRawTransactionVerbose ( txHash )
if mainTx != nil && ! mainTx . Blockhash . Equals ( util . Uint256 { } ) {
2024-04-01 17:51:33 +00:00
return cli . NewExitError ( fmt . Errorf ( "target transaction %s is accepted at block %s" , txHash , mainTx . Blockhash . StringLE ( ) ) , 1 )
2023-11-27 11:20:18 +00:00
}
if mainTx != nil && ! mainTx . HasSigner ( acc . ScriptHash ( ) ) {
return cli . NewExitError ( fmt . Errorf ( "account %s is not a signer of the conflicting transaction" , acc . Address ) , 1 )
}
2023-12-28 11:58:38 +00:00
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 {
2023-11-27 11:20:18 +00:00
err := actor . DefaultCheckerModifier ( r , t )
if err != nil {
return err
}
if mainTx != nil && t . NetworkFee < mainTx . NetworkFee + 1 {
t . NetworkFee = mainTx . NetworkFee + 1
}
t . NetworkFee += int64 ( flags . Fixed8FromContext ( ctx , "gas" ) )
2023-12-27 09:26:02 +00:00
if mainTx != nil {
t . ValidUntilBlock = mainTx . ValidUntilBlock
}
2023-11-27 11:20:18 +00:00
return nil
} )
if err != nil {
return cli . NewExitError ( fmt . Errorf ( "failed to send conflicting transaction: %w" , err ) , 1 )
}
2024-04-01 17:51:33 +00:00
var res * state . AppExecResult
2023-12-28 11:58:38 +00:00
if ctx . Bool ( "await" ) {
res , err = a . WaitAny ( gctx , resVub , txHash , resHash )
if err != nil {
if errors . Is ( err , waiter . ErrTxNotAccepted ) {
if mainTx == nil {
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 )
}
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 )
return nil
}
return cli . NewExitError ( fmt . Errorf ( "failed to await target/ conflicting transaction %s/ %s: %w" , txHash . StringLE ( ) , resHash . StringLE ( ) , err ) , 1 )
}
if txHash . Equals ( res . Container ) {
2024-04-01 17:51:33 +00:00
tx , err := c . GetRawTransactionVerbose ( txHash )
if err != nil {
return cli . NewExitError ( fmt . Errorf ( "target transaction %s is accepted" , txHash ) , 1 )
}
return cli . NewExitError ( fmt . Errorf ( "target transaction %s is accepted at block %s" , txHash , tx . Blockhash . StringLE ( ) ) , 1 )
2023-12-28 11:58:38 +00:00
}
2024-04-01 17:51:33 +00:00
fmt . Fprintln ( ctx . App . Writer , "Conflicting transaction accepted" )
2023-12-28 11:58:38 +00:00
}
2024-04-01 17:51:33 +00:00
txctx . DumpTransactionInfo ( ctx . App . Writer , resHash , res )
2023-11-27 11:20:18 +00:00
return nil
}