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 }