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