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 }