github.com/aeternity/aepp-sdk-go/v6@v6.0.0/cmd/tx.go (about)

     1  package cmd
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math/big"
     7  
     8  	"github.com/aeternity/aepp-sdk-go/v6/binary"
     9  	"github.com/aeternity/aepp-sdk-go/v6/config"
    10  	"github.com/aeternity/aepp-sdk-go/v6/naet"
    11  	"github.com/aeternity/aepp-sdk-go/v6/transactions"
    12  	"github.com/aeternity/aepp-sdk-go/v6/utils"
    13  
    14  	"github.com/aeternity/aepp-sdk-go/v6/aeternity"
    15  
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  // txCmd implments the tx command. All tx subcommands should work offline,
    20  // without any connection to the node.
    21  var txCmd = &cobra.Command{
    22  	Use:   "tx SUBCOMMAND [ARGS]...",
    23  	Short: "Handle transactions creation",
    24  	Long:  ``,
    25  }
    26  
    27  // txSpendCmd implements the tx spend subcommand.
    28  // It returns an unsigned spend transaction (to be signed with account sign)
    29  var txSpendCmd = &cobra.Command{
    30  	Use:   "spend SENDER_ADDRESS RECIPIENT_ADDRESS AMOUNT",
    31  	Short: "Create a transaction to another account (unsigned)",
    32  	Long:  ``,
    33  	Args:  cobra.ExactArgs(3),
    34  	RunE: func(cmd *cobra.Command, args []string) error {
    35  		node := newAeNode()
    36  		ttlFunc := aeternity.GenerateGetTTL(node)
    37  		nonceFunc := aeternity.GenerateGetNextNonce(node)
    38  		return txSpendFunc(ttlFunc, nonceFunc, args)
    39  	},
    40  }
    41  
    42  func txSpendFunc(ttlFunc aeternity.GetTTLFunc, nonceFunc aeternity.GetNextNonceFunc, args []string) (err error) {
    43  	var (
    44  		sender    string
    45  		recipient string
    46  		amount    *big.Int
    47  		feeBigInt *big.Int
    48  	)
    49  
    50  	// Load variables from arguments
    51  	sender = args[0]
    52  	recipient = args[1]
    53  	amount, err = utils.NewIntFromString(args[2])
    54  	feeBigInt, _ = utils.NewIntFromString(fee)
    55  
    56  	// Validate arguments
    57  	if !IsAddress(sender) {
    58  		return errors.New("Error, missing or invalid sender address")
    59  	}
    60  	if !IsAddress(recipient) {
    61  		return errors.New("Error, missing or invalid recipient address")
    62  	}
    63  	if amount.Cmp(big.NewInt(0)) == -1 {
    64  		return errors.New("Error, missing or invalid amount")
    65  	}
    66  	if feeBigInt.Cmp(big.NewInt(0)) == -1 {
    67  		return errors.New("Error, missing or invalid fee")
    68  	}
    69  
    70  	// If nonce was not specified as an argument, connect to the node to
    71  	// query it
    72  	if nonce == 0 {
    73  		nonce, err = nonceFunc(sender)
    74  		if err != nil {
    75  			return err
    76  		}
    77  	}
    78  
    79  	// If TTL was not specified as an argument, connect to the node to calculate
    80  	// it
    81  	if ttl == 0 {
    82  		ttl, err = ttlFunc(config.Client.TTL)
    83  		if err != nil {
    84  			return err
    85  		}
    86  	}
    87  
    88  	tx := transactions.NewSpendTx(sender, recipient, amount, feeBigInt, []byte(spendTxPayload), ttl, nonce)
    89  	base64Tx, err := transactions.SerializeTx(tx)
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	// Print the result
    95  	Pp(
    96  		"Sender acount", tx.SenderID,
    97  		"Recipient account", tx.RecipientID,
    98  		"Amount", tx.Amount,
    99  		"TTL", tx.TTL,
   100  		"Fee", tx.Fee,
   101  		"Nonce", tx.Nonce,
   102  		"Payload", tx.Payload,
   103  		"Encoded", base64Tx,
   104  	)
   105  	return nil
   106  }
   107  
   108  var txContractCreateCmd = &cobra.Command{
   109  	Use:   "deploy OWNER_ID CONTRACT_BYTECODE INIT_CALLDATA",
   110  	Short: "Create a smart contract on the blockchain",
   111  	Long:  ``,
   112  	Args:  cobra.ExactArgs(3),
   113  	RunE: func(cmd *cobra.Command, args []string) error {
   114  		node := newAeNode()
   115  		ttlFunc := aeternity.GenerateGetTTL(node)
   116  		nonceFunc := aeternity.GenerateGetNextNonce(node)
   117  		return txContractCreateFunc(ttlFunc, nonceFunc, args)
   118  	},
   119  }
   120  
   121  type getHeightAccounter interface {
   122  	naet.GetHeighter
   123  	naet.GetAccounter
   124  }
   125  
   126  func txContractCreateFunc(ttlFunc aeternity.GetTTLFunc, nonceFunc aeternity.GetNextNonceFunc, args []string) (err error) {
   127  	var (
   128  		owner    string
   129  		contract string
   130  		calldata string
   131  	)
   132  
   133  	// Load variables from arguments and validate them
   134  	owner = args[0]
   135  	if !IsAddress(owner) {
   136  		return errors.New("Error, missing or invalid owner address")
   137  	}
   138  	contract = args[1]
   139  	if !IsBytecode(contract) {
   140  		return errors.New("Error, missing or invalid contract bytecode")
   141  	}
   142  	calldata = args[2]
   143  	if !IsBytecode(calldata) {
   144  		return errors.New("Error, missing or invalid init calldata bytecode")
   145  	}
   146  
   147  	// If nonce was not specified as an argument, connect to the node to
   148  	// query it
   149  	if nonce == 0 {
   150  		nonce, err = nonceFunc(owner)
   151  		if err != nil {
   152  			return err
   153  		}
   154  	}
   155  
   156  	// If TTL was not specified as an argument, connect to the node to calculate
   157  	// it
   158  	if ttl == 0 {
   159  		ttl, err = ttlFunc(config.Client.TTL)
   160  		if err != nil {
   161  			return err
   162  		}
   163  	}
   164  
   165  	tx := transactions.NewContractCreateTx(owner, nonce, contract, config.Client.Contracts.VMVersion, config.Client.Contracts.ABIVersion, config.Client.Contracts.Deposit, config.Client.Contracts.Amount, config.Client.Contracts.GasLimit, config.Client.GasPrice, config.Client.Fee, ttl, calldata)
   166  
   167  	txStr, err := transactions.SerializeTx(tx)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	// Print the result
   173  	Pp(
   174  		"OwnerID", tx.OwnerID,
   175  		"AccountNonce", tx.AccountNonce,
   176  		"Code", tx.Code,
   177  		"VMVersion", tx.VMVersion,
   178  		"ABIVersion", tx.AbiVersion,
   179  		"Deposit", tx.Deposit,
   180  		"Amount", tx.Amount,
   181  		"GasLimit", tx.GasLimit,
   182  		"GasPrice", tx.GasPrice,
   183  		"TTL", tx.TTL,
   184  		"Fee", tx.Fee,
   185  		"CallData", tx.CallData,
   186  		"Encoded", txStr,
   187  	)
   188  
   189  	return
   190  }
   191  
   192  // txVerifyCmd implements the tx verify subcommand.
   193  // It verfies the signature of a signed transaction
   194  var txVerifyCmd = &cobra.Command{
   195  	Use:          "verify SENDER_ADDRESS SIGNED_TRANSACTION",
   196  	Short:        "Verify the signature of a signed base64 transaction",
   197  	Long:         ``,
   198  	Args:         cobra.ExactArgs(2),
   199  	RunE:         txVerifyFunc,
   200  	SilenceUsage: true,
   201  }
   202  
   203  func txVerifyFunc(cmd *cobra.Command, args []string) (err error) {
   204  	// Load variables from arguments
   205  	sender := args[0]
   206  	txSignedBase64 := args[1]
   207  
   208  	if !IsAddress(sender) {
   209  		return errors.New("Error, missing or invalid sender address")
   210  	}
   211  	if !IsTransaction(txSignedBase64) {
   212  		return errors.New("Error, missing or invalid base64 encoded transaction")
   213  	}
   214  	valid, err := transactions.VerifySignedTx(sender, txSignedBase64, config.Node.NetworkID)
   215  	if err != nil {
   216  		err := fmt.Errorf("error while verifying signature: %s", err)
   217  		return err
   218  	}
   219  	if valid {
   220  		fmt.Printf("The signature is valid (network-id: %s)\n", config.Node.NetworkID)
   221  	} else {
   222  		message := fmt.Sprintf("The signature is invalid (expecting network-id: %s)", config.Node.NetworkID)
   223  		// fmt.Println(message)
   224  		err = errors.New(message)
   225  	}
   226  	return err
   227  }
   228  
   229  // txDumpRawCmd implements the tx dumpraw subcommand.
   230  // It decodes a base58/64 input down into its RLP byte level representation.
   231  var txDumpRawCmd = &cobra.Command{
   232  	Use:   "dumpraw TRANSACTION",
   233  	Short: "Show the RLP byte level representation of a base58/64 encoded object",
   234  	Long:  ``,
   235  	Args:  cobra.ExactArgs(1),
   236  	RunE:  txDumpRawFunc,
   237  }
   238  
   239  func txDumpRawFunc(cmd *cobra.Command, args []string) (err error) {
   240  	tx := args[0]
   241  	if !IsTransaction(tx) {
   242  		return errors.New("Error, missing or invalid base64 encoded transaction")
   243  	}
   244  	txRaw, err := binary.Decode(tx)
   245  	if err != nil {
   246  		return err
   247  	}
   248  	res := binary.DecodeRLPMessage(txRaw)
   249  	fmt.Println(res)
   250  	return nil
   251  }
   252  
   253  func init() {
   254  	RootCmd.AddCommand(txCmd)
   255  	txCmd.AddCommand(txSpendCmd)
   256  	txCmd.AddCommand(txContractCreateCmd)
   257  	txCmd.AddCommand(txVerifyCmd)
   258  	txCmd.AddCommand(txDumpRawCmd)
   259  
   260  	// tx spend command
   261  	txSpendCmd.Flags().StringVar(&fee, "fee", config.Client.Fee.String(), fmt.Sprintf("Set the transaction fee (default=%s)", config.Client.Fee.String()))
   262  	txSpendCmd.Flags().Uint64Var(&ttl, "ttl", 0, fmt.Sprintf("Set the TTL in keyblocks (default=%d)", 0))
   263  	txSpendCmd.Flags().Uint64Var(&nonce, "nonce", 0, fmt.Sprint("Set the sender account nonce, if not the chain will be queried for its value"))
   264  	txSpendCmd.Flags().StringVar(&spendTxPayload, "payload", "", fmt.Sprint("Optional text payload for Spend Transactions, which will be turned into a bytearray"))
   265  }