github.com/algorand/go-algorand-sdk@v1.24.0/future/transaction_test.go (about) 1 package future 2 3 import ( 4 "encoding/base64" 5 "testing" 6 7 "github.com/algorand/go-algorand-sdk/crypto" 8 "github.com/algorand/go-algorand-sdk/encoding/msgpack" 9 "github.com/algorand/go-algorand-sdk/mnemonic" 10 "github.com/algorand/go-algorand-sdk/types" 11 "github.com/stretchr/testify/require" 12 ) 13 14 func byteFromBase64(s string) []byte { 15 b, _ := base64.StdEncoding.DecodeString(s) 16 return b 17 } 18 19 func byte32ArrayFromBase64(s string) (out [32]byte) { 20 slice := byteFromBase64(s) 21 if len(slice) != 32 { 22 panic("wrong length: input slice not 32 bytes") 23 } 24 copy(out[:], slice) 25 return 26 } 27 28 func byte64ArrayFromBase64(s string) (out [64]byte) { 29 slice := byteFromBase64(s) 30 if len(slice) != 64 { 31 panic("wrong length: input slice not 64 bytes") 32 } 33 copy(out[:], slice) 34 return 35 } 36 37 func TestMakePaymentTxn(t *testing.T) { 38 const fromAddress = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU" 39 const toAddress = "PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI" 40 const referenceTxID = "5FJDJD5LMZC3EHUYYJNH5I23U4X6H2KXABNDGPIL557ZMJ33GZHQ" 41 const mn = "advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor" 42 const golden = "gqNzaWfEQPhUAZ3xkDDcc8FvOVo6UinzmKBCqs0woYSfodlmBMfQvGbeUx3Srxy3dyJDzv7rLm26BRv9FnL2/AuT7NYfiAWjdHhui6NhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0EmKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqRub3RlxAjqABVHQ2y/lqNyY3bEIHts4k/rW6zAsWTinCIsV/X2PcOH1DkEglhBHF/hD3wCo3NuZMQg5/D4TQaBHfnzHI2HixFV9GcdUaGFwgCQhmf0SVhwaKGkdHlwZaNwYXk=" 43 gh := byteFromBase64("JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=") 44 45 params := types.SuggestedParams{ 46 Fee: 4, 47 FirstRoundValid: 12466, 48 LastRoundValid: 13466, 49 GenesisID: "devnet-v33.0", 50 GenesisHash: gh, 51 } 52 txn, err := MakePaymentTxn(fromAddress, toAddress, 1000, byteFromBase64("6gAVR0Nsv5Y="), "IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA", params) 53 require.NoError(t, err) 54 55 key, err := mnemonic.ToPrivateKey(mn) 56 require.NoError(t, err) 57 58 id, bytes, err := crypto.SignTransaction(key, txn) 59 require.NoError(t, err) 60 61 stxBytes := byteFromBase64(golden) 62 require.Equal(t, stxBytes, bytes) 63 64 require.Equal(t, referenceTxID, id) 65 } 66 67 // should fail on a lack of GenesisHash 68 func TestMakePaymentTxn2(t *testing.T) { 69 const fromAddress = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU" 70 const toAddress = "PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI" 71 params := types.SuggestedParams{ 72 Fee: 4, 73 FirstRoundValid: 12466, 74 LastRoundValid: 13466, 75 GenesisID: "devnet-v33.0", 76 } 77 _, err := MakePaymentTxn(fromAddress, toAddress, 1000, byteFromBase64("6gAVR0Nsv5Y="), "IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA", params) 78 require.Error(t, err) 79 } 80 81 func TestMakePaymentTxnWithLease(t *testing.T) { 82 const fromAddress = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU" 83 const toAddress = "PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI" 84 const referenceTxID = "7BG6COBZKF6I6W5XY72ZE4HXV6LLZ6ENSR6DASEGSTXYXR4XJOOQ" 85 const mn = "advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor" 86 const golden = "gqNzaWfEQOMmFSIKsZvpW0txwzhmbgQjxv6IyN7BbV5sZ2aNgFbVcrWUnqPpQQxfPhV/wdu9jzEPUU1jAujYtcNCxJ7ONgejdHhujKNhbXTNA+ilY2xvc2XEIEDpNJKIJWTLzpxZpptnVCaJ6aHDoqnqW2Wm6KRCH/xXo2ZlZc0FLKJmds0wsqNnZW6sZGV2bmV0LXYzMy4womdoxCAmCyAJoJOohot5WHIvpeVG7eftF+TYXEx4r7BFJpDt0qJsds00mqJseMQgAQIDBAECAwQBAgMEAQIDBAECAwQBAgMEAQIDBAECAwSkbm90ZcQI6gAVR0Nsv5ajcmN2xCB7bOJP61uswLFk4pwiLFf19j3Dh9Q5BIJYQRxf4Q98AqNzbmTEIOfw+E0GgR358xyNh4sRVfRnHVGhhcIAkIZn9ElYcGihpHR5cGWjcGF5" 87 gh := byteFromBase64("JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=") 88 params := types.SuggestedParams{ 89 Fee: 4, 90 FirstRoundValid: 12466, 91 LastRoundValid: 13466, 92 GenesisID: "devnet-v33.0", 93 GenesisHash: gh, 94 } 95 lease := [32]byte{1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4} 96 txn, err := MakePaymentTxn(fromAddress, toAddress, 1000, byteFromBase64("6gAVR0Nsv5Y="), "IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA", params) 97 require.NoError(t, err) 98 txn.AddLease(lease, 4) 99 require.NoError(t, err) 100 101 key, err := mnemonic.ToPrivateKey(mn) 102 require.NoError(t, err) 103 104 id, stxBytes, err := crypto.SignTransaction(key, txn) 105 require.NoError(t, err) 106 107 goldenBytes := byteFromBase64(golden) 108 require.Equal(t, goldenBytes, stxBytes) 109 require.Equal(t, referenceTxID, id) 110 } 111 112 func TestKeyRegTxn(t *testing.T) { 113 // preKeyRegTxn is an unsigned signed keyreg txn with zero Sender 114 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 115 a, err := types.DecodeAddress(addr) 116 require.NoError(t, err) 117 const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred" 118 expKeyRegTxn := types.Transaction{ 119 Type: types.KeyRegistrationTx, 120 Header: types.Header{ 121 Sender: a, 122 Fee: 1000, 123 FirstValid: 322575, 124 LastValid: 323575, 125 GenesisHash: byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="), 126 GenesisID: "", 127 }, 128 KeyregTxnFields: types.KeyregTxnFields{ 129 VotePK: byte32ArrayFromBase64("Kv7QI7chi1y6axoy+t7wzAVpePqRq/rkjzWh/RMYyLo="), 130 SelectionPK: byte32ArrayFromBase64("bPgrv4YogPcdaUAxrt1QysYZTVyRAuUMD4zQmCu9llc="), 131 VoteFirst: 10000, 132 VoteLast: 10111, 133 VoteKeyDilution: 11, 134 }, 135 } 136 const signedGolden = "gqNzaWfEQEA8ANbrvTRxU9c8v6WERcEPw7D/HacRgg4vICa61vEof60Wwtx6KJKDyvBuvViFeacLlngPY6vYCVP0DktTwQ2jdHhui6NmZWXNA+iiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96ZzZWxrZXnEIGz4K7+GKID3HWlAMa7dUMrGGU1ckQLlDA+M0JgrvZZXo3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaZrZXlyZWendm90ZWZzdM0nEKZ2b3Rla2QLp3ZvdGVrZXnEICr+0CO3IYtcumsaMvre8MwFaXj6kav65I81of0TGMi6p3ZvdGVsc3TNJ38=" 137 // now, sign 138 private, err := mnemonic.ToPrivateKey(addrSK) 139 require.NoError(t, err) 140 txid, newStxBytes, err := crypto.SignTransaction(private, expKeyRegTxn) 141 require.NoError(t, err) 142 require.Equal(t, "MDRIUVH5AW4Z3GMOB67WP44LYLEVM2MP3ZEPKFHUB5J47A2J6TUQ", txid) 143 require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) 144 } 145 146 func TestMakeKeyRegTxn(t *testing.T) { 147 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 148 ghAsArray := byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=") 149 params := types.SuggestedParams{ 150 Fee: 10, 151 FirstRoundValid: 322575, 152 LastRoundValid: 323575, 153 GenesisHash: ghAsArray[:], 154 } 155 tx, err := MakeKeyRegTxn(addr, []byte{45, 67}, params, "Kv7QI7chi1y6axoy+t7wzAVpePqRq/rkjzWh/RMYyLo=", "bPgrv4YogPcdaUAxrt1QysYZTVyRAuUMD4zQmCu9llc=", 10000, 10111, 11) 156 require.NoError(t, err) 157 158 a, err := types.DecodeAddress(addr) 159 require.NoError(t, err) 160 expKeyRegTxn := types.Transaction{ 161 Type: types.KeyRegistrationTx, 162 Header: types.Header{ 163 Sender: a, 164 Fee: 3060, 165 FirstValid: 322575, 166 LastValid: 323575, 167 Note: []byte{45, 67}, 168 GenesisHash: byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="), 169 GenesisID: "", 170 }, 171 KeyregTxnFields: types.KeyregTxnFields{ 172 VotePK: byte32ArrayFromBase64("Kv7QI7chi1y6axoy+t7wzAVpePqRq/rkjzWh/RMYyLo="), 173 SelectionPK: byte32ArrayFromBase64("bPgrv4YogPcdaUAxrt1QysYZTVyRAuUMD4zQmCu9llc="), 174 VoteFirst: 10000, 175 VoteLast: 10111, 176 VoteKeyDilution: 11, 177 }, 178 } 179 require.Equal(t, expKeyRegTxn, tx) 180 } 181 182 func TestMakeKeyRegTxnWithStateProofKey(t *testing.T) { 183 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 184 ghAsArray := byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=") 185 params := types.SuggestedParams{ 186 Fee: 10, 187 FirstRoundValid: 322575, 188 LastRoundValid: 323575, 189 GenesisHash: ghAsArray[:], 190 } 191 // nonparticipation 192 tx, err := MakeKeyRegTxnWithStateProofKey(addr, []byte{45, 67}, params, "", "", "", 0, 0, 0, true) 193 require.NoError(t, err) 194 a, err := types.DecodeAddress(addr) 195 require.NoError(t, err) 196 expKeyRegTxn := types.Transaction{ 197 Type: types.KeyRegistrationTx, 198 Header: types.Header{ 199 Sender: a, 200 Fee: 2020, 201 FirstValid: 322575, 202 LastValid: 323575, 203 Note: []byte{45, 67}, 204 GenesisHash: byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="), 205 GenesisID: "", 206 }, 207 KeyregTxnFields: types.KeyregTxnFields{ 208 Nonparticipation: true, 209 }, 210 } 211 require.Equal(t, expKeyRegTxn, tx) 212 213 // online 214 stateProof := "mYR0GVEObMTSNdsKM6RwYywHYPqVDqg3E4JFzxZOreH9NU8B+tKzUanyY8AQ144hETgSMX7fXWwjBdHz6AWk9w==" 215 tx, err = MakeKeyRegTxnWithStateProofKey(addr, []byte{45, 67}, params, "Kv7QI7chi1y6axoy+t7wzAVpePqRq/rkjzWh/RMYyLo=", "bPgrv4YogPcdaUAxrt1QysYZTVyRAuUMD4zQmCu9llc=", stateProof, 10000, 10111, 11, false) 216 require.NoError(t, err) 217 218 a, err = types.DecodeAddress(addr) 219 require.NoError(t, err) 220 expKeyRegTxn = types.Transaction{ 221 Type: types.KeyRegistrationTx, 222 Header: types.Header{ 223 Sender: a, 224 Fee: 3800, 225 FirstValid: 322575, 226 LastValid: 323575, 227 Note: []byte{45, 67}, 228 GenesisHash: byte32ArrayFromBase64("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="), 229 GenesisID: "", 230 }, 231 KeyregTxnFields: types.KeyregTxnFields{ 232 VotePK: byte32ArrayFromBase64("Kv7QI7chi1y6axoy+t7wzAVpePqRq/rkjzWh/RMYyLo="), 233 SelectionPK: byte32ArrayFromBase64("bPgrv4YogPcdaUAxrt1QysYZTVyRAuUMD4zQmCu9llc="), 234 VoteFirst: 10000, 235 VoteLast: 10111, 236 VoteKeyDilution: 11, 237 Nonparticipation: false, 238 StateProofPK: byte64ArrayFromBase64(stateProof), 239 }, 240 } 241 require.Equal(t, expKeyRegTxn, tx) 242 } 243 244 func TestMakeAssetCreateTxn(t *testing.T) { 245 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 246 const defaultFrozen = false 247 const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" 248 ghAsArray := byte32ArrayFromBase64(genesisHash) 249 const total = 100 250 const reserve = addr 251 const freeze = addr 252 const clawback = addr 253 const unitName = "tst" 254 const assetName = "testcoin" 255 const testURL = "website" 256 const metadataHash = "fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh" 257 params := types.SuggestedParams{ 258 Fee: 10, 259 FirstRoundValid: 322575, 260 LastRoundValid: 323575, 261 GenesisHash: ghAsArray[:], 262 } 263 tx, err := MakeAssetCreateTxn(addr, nil, params, total, 0, defaultFrozen, addr, reserve, freeze, clawback, unitName, assetName, testURL, metadataHash) 264 require.NoError(t, err) 265 266 a, err := types.DecodeAddress(addr) 267 require.NoError(t, err) 268 expectedAssetCreationTxn := types.Transaction{ 269 Type: types.AssetConfigTx, 270 Header: types.Header{ 271 Sender: a, 272 Fee: 4020, 273 FirstValid: 322575, 274 LastValid: 323575, 275 GenesisHash: byte32ArrayFromBase64(genesisHash), 276 GenesisID: "", 277 }, 278 } 279 expectedAssetCreationTxn.AssetParams = types.AssetParams{ 280 Total: total, 281 DefaultFrozen: defaultFrozen, 282 Manager: a, 283 Reserve: a, 284 Freeze: a, 285 Clawback: a, 286 UnitName: unitName, 287 AssetName: assetName, 288 URL: testURL, 289 } 290 copy(expectedAssetCreationTxn.AssetParams.MetadataHash[:], []byte(metadataHash)) 291 require.Equal(t, expectedAssetCreationTxn, tx) 292 293 const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred" 294 private, err := mnemonic.ToPrivateKey(addrSK) 295 require.NoError(t, err) 296 _, newStxBytes, err := crypto.SignTransaction(private, tx) 297 require.NoError(t, err) 298 signedGolden := "gqNzaWfEQEDd1OMRoQI/rzNlU4iiF50XQXmup3k5czI9hEsNqHT7K4KsfmA/0DUVkbzOwtJdRsHS8trm3Arjpy9r7AXlbAujdHhuh6RhcGFyiaJhbcQgZkFDUE80blJnTzU1ajFuZEFLM1c2U2djNEFQa2N5RmiiYW6odGVzdGNvaW6iYXWnd2Vic2l0ZaFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFmxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFtxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFyxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aF0ZKJ1bqN0c3SjZmVlzQ+0omZ2zgAE7A+iZ2jEIEhjtRiks8hOyBDyLU8QgcsPcfBZp6wg3sYvf3DlCToiomx2zgAE7/ejc25kxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aR0eXBlpGFjZmc=" 299 require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) 300 } 301 302 func TestMakeAssetCreateTxnWithDecimals(t *testing.T) { 303 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 304 const defaultFrozen = false 305 const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" 306 ghAsArray := byte32ArrayFromBase64(genesisHash) 307 const total = 100 308 const decimals = 1 309 const reserve = addr 310 const freeze = addr 311 const clawback = addr 312 const unitName = "tst" 313 const assetName = "testcoin" 314 const testURL = "website" 315 const metadataHash = "fACPO4nRgO55j1ndAK3W6Sgc4APkcyFh" 316 params := types.SuggestedParams{ 317 Fee: 10, 318 FirstRoundValid: 322575, 319 LastRoundValid: 323575, 320 GenesisHash: ghAsArray[:], 321 } 322 tx, err := MakeAssetCreateTxn(addr, nil, params, total, decimals, defaultFrozen, addr, reserve, freeze, clawback, unitName, assetName, testURL, metadataHash) 323 require.NoError(t, err) 324 325 a, err := types.DecodeAddress(addr) 326 require.NoError(t, err) 327 expectedAssetCreationTxn := types.Transaction{ 328 Type: types.AssetConfigTx, 329 Header: types.Header{ 330 Sender: a, 331 Fee: 4060, 332 FirstValid: 322575, 333 LastValid: 323575, 334 GenesisHash: byte32ArrayFromBase64(genesisHash), 335 GenesisID: "", 336 }, 337 } 338 expectedAssetCreationTxn.AssetParams = types.AssetParams{ 339 Total: total, 340 Decimals: decimals, 341 DefaultFrozen: defaultFrozen, 342 Manager: a, 343 Reserve: a, 344 Freeze: a, 345 Clawback: a, 346 UnitName: unitName, 347 AssetName: assetName, 348 URL: testURL, 349 } 350 copy(expectedAssetCreationTxn.AssetParams.MetadataHash[:], []byte(metadataHash)) 351 require.Equal(t, expectedAssetCreationTxn, tx) 352 353 const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred" 354 private, err := mnemonic.ToPrivateKey(addrSK) 355 require.NoError(t, err) 356 _, newStxBytes, err := crypto.SignTransaction(private, tx) 357 require.NoError(t, err) 358 signedGolden := "gqNzaWfEQCj5xLqNozR5ahB+LNBlTG+d0gl0vWBrGdAXj1ibsCkvAwOsXs5KHZK1YdLgkdJecQiWm4oiZ+pm5Yg0m3KFqgqjdHhuh6RhcGFyiqJhbcQgZkFDUE80blJnTzU1ajFuZEFLM1c2U2djNEFQa2N5RmiiYW6odGVzdGNvaW6iYXWnd2Vic2l0ZaFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aJkYwGhZsQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hbcQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hcsQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2hdGSidW6jdHN0o2ZlZc0P3KJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/3o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaRhY2Zn" 359 require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) 360 } 361 362 func TestMakeAssetConfigTxn(t *testing.T) { 363 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 364 const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" 365 ghAsArray := byte32ArrayFromBase64(genesisHash) 366 const manager = addr 367 const reserve = addr 368 const freeze = addr 369 const clawback = addr 370 const assetIndex = 1234 371 params := types.SuggestedParams{ 372 Fee: 10, 373 FirstRoundValid: 322575, 374 LastRoundValid: 323575, 375 GenesisHash: ghAsArray[:], 376 } 377 tx, err := MakeAssetConfigTxn(addr, nil, params, assetIndex, manager, reserve, freeze, clawback, false) 378 require.NoError(t, err) 379 380 a, err := types.DecodeAddress(addr) 381 require.NoError(t, err) 382 expectedAssetConfigTxn := types.Transaction{ 383 Type: types.AssetConfigTx, 384 Header: types.Header{ 385 Sender: a, 386 Fee: 3400, 387 FirstValid: 322575, 388 LastValid: 323575, 389 GenesisHash: byte32ArrayFromBase64(genesisHash), 390 GenesisID: "", 391 }, 392 } 393 394 expectedAssetConfigTxn.AssetParams = types.AssetParams{ 395 Manager: a, 396 Reserve: a, 397 Freeze: a, 398 Clawback: a, 399 } 400 expectedAssetConfigTxn.ConfigAsset = types.AssetIndex(assetIndex) 401 require.Equal(t, expectedAssetConfigTxn, tx) 402 403 const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred" 404 private, err := mnemonic.ToPrivateKey(addrSK) 405 require.NoError(t, err) 406 _, newStxBytes, err := crypto.SignTransaction(private, tx) 407 require.NoError(t, err) 408 signedGolden := "gqNzaWfEQBBkfw5n6UevuIMDo2lHyU4dS80JCCQ/vTRUcTx5m0ivX68zTKyuVRrHaTbxbRRc3YpJ4zeVEnC9Fiw3Wf4REwejdHhuiKRhcGFyhKFjxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFmxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFtxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aFyxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRjYWlkzQTSo2ZlZc0NSKJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/3o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaRhY2Zn" 409 require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) 410 } 411 412 func TestMakeAssetConfigTxnStrictChecking(t *testing.T) { 413 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 414 const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" 415 ghAsArray := byte32ArrayFromBase64(genesisHash) 416 const manager = addr 417 const reserve = addr 418 const freeze = "" 419 const clawback = addr 420 const assetIndex = 1234 421 params := types.SuggestedParams{ 422 Fee: 10, 423 FirstRoundValid: 322575, 424 LastRoundValid: 323575, 425 GenesisHash: ghAsArray[:], 426 } 427 _, err := MakeAssetConfigTxn(addr, nil, params, assetIndex, manager, reserve, freeze, clawback, true) 428 require.Error(t, err) 429 } 430 431 func TestMakeAssetDestroyTxn(t *testing.T) { 432 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 433 const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" 434 ghAsArray := byte32ArrayFromBase64(genesisHash) 435 const creator = addr 436 const assetIndex = 1 437 const firstValidRound = 322575 438 const lastValidRound = 323575 439 params := types.SuggestedParams{ 440 Fee: 10, 441 FirstRoundValid: firstValidRound, 442 LastRoundValid: lastValidRound, 443 GenesisHash: ghAsArray[:], 444 } 445 tx, err := MakeAssetDestroyTxn(creator, nil, params, assetIndex) 446 require.NoError(t, err) 447 448 a, err := types.DecodeAddress(creator) 449 require.NoError(t, err) 450 451 expectedAssetDestroyTxn := types.Transaction{ 452 Type: types.AssetConfigTx, 453 Header: types.Header{ 454 Sender: a, 455 Fee: 1880, 456 FirstValid: firstValidRound, 457 LastValid: lastValidRound, 458 GenesisHash: byte32ArrayFromBase64(genesisHash), 459 GenesisID: "", 460 }, 461 } 462 expectedAssetDestroyTxn.AssetParams = types.AssetParams{} 463 expectedAssetDestroyTxn.ConfigAsset = types.AssetIndex(assetIndex) 464 require.Equal(t, expectedAssetDestroyTxn, tx) 465 466 const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred" 467 private, err := mnemonic.ToPrivateKey(addrSK) 468 require.NoError(t, err) 469 _, newStxBytes, err := crypto.SignTransaction(private, tx) 470 require.NoError(t, err) 471 signedGolden := "gqNzaWfEQBSP7HtzD/Lvn4aVvaNpeR4T93dQgo4LvywEwcZgDEoc/WVl3aKsZGcZkcRFoiWk8AidhfOZzZYutckkccB8RgGjdHhuh6RjYWlkAaNmZWXNB1iiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWkYWNmZw==" 472 require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) 473 } 474 475 func TestMakeAssetFreezeTxn(t *testing.T) { 476 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 477 const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" 478 ghAsArray := byte32ArrayFromBase64(genesisHash) 479 const assetIndex = 1 480 const firstValidRound = 322575 481 const lastValidRound = 323576 482 const freezeSetting = true 483 const target = addr 484 params := types.SuggestedParams{ 485 Fee: 10, 486 FirstRoundValid: firstValidRound, 487 LastRoundValid: lastValidRound, 488 GenesisHash: ghAsArray[:], 489 } 490 tx, err := MakeAssetFreezeTxn(addr, nil, params, assetIndex, target, freezeSetting) 491 require.NoError(t, err) 492 493 a, err := types.DecodeAddress(addr) 494 require.NoError(t, err) 495 496 expectedAssetFreezeTxn := types.Transaction{ 497 Type: types.AssetFreezeTx, 498 Header: types.Header{ 499 Sender: a, 500 Fee: 2330, 501 FirstValid: firstValidRound, 502 LastValid: lastValidRound, 503 GenesisHash: byte32ArrayFromBase64(genesisHash), 504 GenesisID: "", 505 }, 506 } 507 expectedAssetFreezeTxn.FreezeAsset = types.AssetIndex(assetIndex) 508 expectedAssetFreezeTxn.AssetFrozen = freezeSetting 509 expectedAssetFreezeTxn.FreezeAccount = a 510 require.Equal(t, expectedAssetFreezeTxn, tx) 511 512 const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred" 513 private, err := mnemonic.ToPrivateKey(addrSK) 514 require.NoError(t, err) 515 _, newStxBytes, err := crypto.SignTransaction(private, tx) 516 require.NoError(t, err) 517 signedGolden := "gqNzaWfEQAhru5V2Xvr19s4pGnI0aslqwY4lA2skzpYtDTAN9DKSH5+qsfQQhm4oq+9VHVj7e1rQC49S28vQZmzDTVnYDQGjdHhuiaRhZnJ6w6RmYWRkxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRmYWlkAaNmZWXNCRqiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv+KNzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWkYWZyeg==" 518 require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) 519 } 520 521 func TestMakeAssetTransferTxn(t *testing.T) { 522 const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred" 523 private, err := mnemonic.ToPrivateKey(addrSK) 524 require.NoError(t, err) 525 526 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 527 const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" 528 ghAsArray := byte32ArrayFromBase64(genesisHash) 529 const sender, recipient, closeAssetsTo = addr, addr, addr 530 const assetIndex = 1 531 const firstValidRound = 322575 532 const lastValidRound = 323576 533 const amountToSend = 1 534 params := types.SuggestedParams{ 535 Fee: 10, 536 FirstRoundValid: firstValidRound, 537 LastRoundValid: lastValidRound, 538 GenesisHash: ghAsArray[:], 539 } 540 tx, err := MakeAssetTransferTxn(sender, recipient, amountToSend, nil, params, closeAssetsTo, assetIndex) 541 require.NoError(t, err) 542 543 sendAddr, err := types.DecodeAddress(sender) 544 require.NoError(t, err) 545 546 expectedAssetTransferTxn := types.Transaction{ 547 Type: types.AssetTransferTx, 548 Header: types.Header{ 549 Sender: sendAddr, 550 Fee: 2750, 551 FirstValid: firstValidRound, 552 LastValid: lastValidRound, 553 GenesisHash: byte32ArrayFromBase64(genesisHash), 554 GenesisID: "", 555 }, 556 } 557 558 expectedAssetID := types.AssetIndex(assetIndex) 559 expectedAssetTransferTxn.XferAsset = expectedAssetID 560 561 receiveAddr, err := types.DecodeAddress(recipient) 562 require.NoError(t, err) 563 expectedAssetTransferTxn.AssetReceiver = receiveAddr 564 565 closeAddr, err := types.DecodeAddress(closeAssetsTo) 566 require.NoError(t, err) 567 expectedAssetTransferTxn.AssetCloseTo = closeAddr 568 569 expectedAssetTransferTxn.AssetAmount = amountToSend 570 571 require.Equal(t, expectedAssetTransferTxn, tx) 572 573 // now compare tx against a golden 574 const signedGolden = "gqNzaWfEQNkEs3WdfFq6IQKJdF1n0/hbV9waLsvojy9pM1T4fvwfMNdjGQDy+LeesuQUfQVTneJD4VfMP7zKx4OUlItbrwSjdHhuiqRhYW10AaZhY2xvc2XEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pGFyY3bEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9o2ZlZc0KvqJmds4ABOwPomdoxCBIY7UYpLPITsgQ8i1PEIHLD3HwWaesIN7GL39w5Qk6IqJsds4ABO/4o3NuZMQgCfvSdiwI+Gxa5r9t16epAd5mdddQ4H6MXHaYZH224f2kdHlwZaVheGZlcqR4YWlkAQ==" 575 _, newStxBytes, err := crypto.SignTransaction(private, tx) 576 require.NoError(t, err) 577 require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) 578 } 579 580 func TestMakeAssetAcceptanceTxn(t *testing.T) { 581 const sender = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 582 const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" 583 ghAsArray := byte32ArrayFromBase64(genesisHash) 584 const assetIndex = 1 585 const firstValidRound = 322575 586 const lastValidRound = 323575 587 params := types.SuggestedParams{ 588 Fee: 10, 589 FirstRoundValid: firstValidRound, 590 LastRoundValid: lastValidRound, 591 GenesisHash: ghAsArray[:], 592 } 593 tx, err := MakeAssetAcceptanceTxn(sender, nil, params, assetIndex) 594 require.NoError(t, err) 595 596 sendAddr, err := types.DecodeAddress(sender) 597 require.NoError(t, err) 598 599 expectedAssetAcceptanceTxn := types.Transaction{ 600 Type: types.AssetTransferTx, 601 Header: types.Header{ 602 Sender: sendAddr, 603 Fee: 2280, 604 FirstValid: firstValidRound, 605 LastValid: lastValidRound, 606 GenesisHash: byte32ArrayFromBase64(genesisHash), 607 GenesisID: "", 608 }, 609 } 610 611 expectedAssetID := types.AssetIndex(assetIndex) 612 expectedAssetAcceptanceTxn.XferAsset = expectedAssetID 613 expectedAssetAcceptanceTxn.AssetReceiver = sendAddr 614 expectedAssetAcceptanceTxn.AssetAmount = 0 615 616 require.Equal(t, expectedAssetAcceptanceTxn, tx) 617 618 const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred" 619 private, err := mnemonic.ToPrivateKey(addrSK) 620 require.NoError(t, err) 621 _, newStxBytes, err := crypto.SignTransaction(private, tx) 622 require.NoError(t, err) 623 signedGolden := "gqNzaWfEQJ7q2rOT8Sb/wB0F87ld+1zMprxVlYqbUbe+oz0WM63FctIi+K9eYFSqT26XBZ4Rr3+VTJpBE+JLKs8nctl9hgijdHhuiKRhcmN2xCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aNmZWXNCOiiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWlYXhmZXKkeGFpZAE=" 624 require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) 625 } 626 627 func TestMakeAssetRevocationTransaction(t *testing.T) { 628 const addr = "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4" 629 const genesisHash = "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=" 630 ghAsArray := byte32ArrayFromBase64(genesisHash) 631 const revoker, recipient, revoked = addr, addr, addr 632 const assetIndex = 1 633 const firstValidRound = 322575 634 const lastValidRound = 323575 635 const amountToSend = 1 636 637 params := types.SuggestedParams{ 638 Fee: 10, 639 FirstRoundValid: firstValidRound, 640 LastRoundValid: lastValidRound, 641 GenesisHash: ghAsArray[:], 642 } 643 tx, err := MakeAssetRevocationTxn(revoker, revoked, amountToSend, recipient, nil, params, assetIndex) 644 require.NoError(t, err) 645 646 sendAddr, err := types.DecodeAddress(revoker) 647 require.NoError(t, err) 648 649 expectedAssetRevocationTxn := types.Transaction{ 650 Type: types.AssetTransferTx, 651 Header: types.Header{ 652 Sender: sendAddr, 653 Fee: 2730, 654 FirstValid: firstValidRound, 655 LastValid: lastValidRound, 656 GenesisHash: byte32ArrayFromBase64(genesisHash), 657 GenesisID: "", 658 }, 659 } 660 661 expectedAssetID := types.AssetIndex(assetIndex) 662 expectedAssetRevocationTxn.XferAsset = expectedAssetID 663 664 receiveAddr, err := types.DecodeAddress(recipient) 665 require.NoError(t, err) 666 expectedAssetRevocationTxn.AssetReceiver = receiveAddr 667 668 expectedAssetRevocationTxn.AssetAmount = amountToSend 669 670 targetAddr, err := types.DecodeAddress(revoked) 671 require.NoError(t, err) 672 expectedAssetRevocationTxn.AssetSender = targetAddr 673 674 require.Equal(t, expectedAssetRevocationTxn, tx) 675 676 const addrSK = "awful drop leaf tennis indoor begin mandate discover uncle seven only coil atom any hospital uncover make any climb actor armed measure need above hundred" 677 private, err := mnemonic.ToPrivateKey(addrSK) 678 require.NoError(t, err) 679 _, newStxBytes, err := crypto.SignTransaction(private, tx) 680 require.NoError(t, err) 681 signedGolden := "gqNzaWfEQHsgfEAmEHUxLLLR9s+Y/yq5WeoGo/jAArCbany+7ZYwExMySzAhmV7M7S8+LBtJalB4EhzEUMKmt3kNKk6+vAWjdHhuiqRhYW10AaRhcmN2xCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aRhc25kxCAJ+9J2LAj4bFrmv23Xp6kB3mZ111Dgfoxcdphkfbbh/aNmZWXNCqqiZnbOAATsD6JnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAATv96NzbmTEIAn70nYsCPhsWua/bdenqQHeZnXXUOB+jFx2mGR9tuH9pHR5cGWlYXhmZXKkeGFpZAE=" 682 require.EqualValues(t, newStxBytes, byteFromBase64(signedGolden)) 683 } 684 685 func TestMakeApplicationCallTx(t *testing.T) { 686 const fee = 1000 687 const firstRound = 2063137 688 const genesisID = "devnet-v1.0" 689 genesisHash := byteFromBase64("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E=") 690 691 params := types.SuggestedParams{ 692 Fee: fee, 693 FirstRoundValid: firstRound, 694 LastRoundValid: firstRound + 1000, 695 GenesisHash: genesisHash, 696 GenesisID: genesisID, 697 FlatFee: true, 698 } 699 note := byteFromBase64("8xMCTuLQ810=") 700 program := []byte{1, 32, 1, 1, 34} 701 args := make([][]byte, 2) 702 args[0] = []byte("123") 703 args[1] = []byte("456") 704 foreignApps := make([]uint64, 1) 705 foreignApps[0] = 10 706 foreignAssets := foreignApps 707 gSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)} 708 lSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)} 709 extraPages := uint32(2) 710 addr := make([]string, 1) 711 addr[0] = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU" 712 boxReferences := make([]types.AppBoxReference, 3) 713 boxReferences[0] = types.AppBoxReference{AppID: 2, Name: []byte("box_name")} 714 boxReferences[1] = types.AppBoxReference{AppID: 10, Name: []byte("box_name")} 715 boxReferences[2] = types.AppBoxReference{AppID: 10, Name: []byte("box_name2")} 716 717 tx, err := MakeApplicationCallTxWithBoxes(2, args, addr, foreignApps, foreignAssets, boxReferences, types.NoOpOC, program, program, gSchema, lSchema, extraPages, params, types.Address{}, note, types.Digest{}, [32]byte{}, types.Address{}) 718 require.NoError(t, err) 719 require.EqualValues(t, 2, tx.ExtraProgramPages) 720 721 // verify that the correct app index was calculated 722 require.EqualValues(t, 0, tx.BoxReferences[0].ForeignAppIdx) 723 require.EqualValues(t, 1, tx.BoxReferences[1].ForeignAppIdx) 724 require.EqualValues(t, 1, tx.BoxReferences[2].ForeignAppIdx) 725 726 // the current app can also be referenced with AppID = 0 727 boxReferences[0].AppID = 0 728 tx, err = MakeApplicationCallTxWithBoxes(2, args, addr, foreignApps, foreignAssets, boxReferences, types.NoOpOC, program, program, gSchema, lSchema, extraPages, params, types.Address{}, note, types.Digest{}, [32]byte{}, types.Address{}) 729 require.NoError(t, err) 730 require.EqualValues(t, 0, tx.BoxReferences[0].ForeignAppIdx) 731 require.EqualValues(t, 1, tx.BoxReferences[1].ForeignAppIdx) 732 require.EqualValues(t, 1, tx.BoxReferences[2].ForeignAppIdx) 733 734 // if the current app's ID is provided explicitly AND is present in the foreignApps array 735 // then the index in the array should be returned rather than the usual value of 0 736 boxReferences[0].AppID = 2 737 foreignApps = append(foreignApps, 2) 738 tx, err = MakeApplicationCallTxWithBoxes(2, args, addr, foreignApps, foreignAssets, boxReferences, types.NoOpOC, program, program, gSchema, lSchema, extraPages, params, types.Address{}, note, types.Digest{}, [32]byte{}, types.Address{}) 739 require.NoError(t, err) 740 require.EqualValues(t, 2, tx.BoxReferences[0].ForeignAppIdx) 741 require.EqualValues(t, 1, tx.BoxReferences[1].ForeignAppIdx) 742 require.EqualValues(t, 1, tx.BoxReferences[2].ForeignAppIdx) 743 } 744 745 func TestMakeApplicationCallTxInvalidBoxes(t *testing.T) { 746 const fee = 1000 747 const firstRound = 2063137 748 const genesisID = "devnet-v1.0" 749 genesisHash := byteFromBase64("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E=") 750 751 params := types.SuggestedParams{ 752 Fee: fee, 753 FirstRoundValid: firstRound, 754 LastRoundValid: firstRound + 1000, 755 GenesisHash: genesisHash, 756 GenesisID: genesisID, 757 FlatFee: true, 758 } 759 note := byteFromBase64("8xMCTuLQ810=") 760 program := []byte{1, 32, 1, 1, 34} 761 args := make([][]byte, 2) 762 args[0] = []byte("123") 763 args[1] = []byte("456") 764 foreignApps := make([]uint64, 1) 765 foreignApps[0] = 10 766 foreignAssets := foreignApps 767 gSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)} 768 lSchema := types.StateSchema{NumUint: uint64(1), NumByteSlice: uint64(1)} 769 extraPages := uint32(2) 770 addr := make([]string, 1) 771 addr[0] = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU" 772 boxReferences := make([]types.AppBoxReference, 3) 773 boxReferences[0] = types.AppBoxReference{AppID: 2, Name: []byte("box_name")} 774 boxReferences[1] = types.AppBoxReference{AppID: 10, Name: []byte("box_name")} 775 boxReferences[2] = types.AppBoxReference{AppID: 11, Name: []byte("box_name")} 776 777 _, err := MakeApplicationCallTxWithBoxes(2, args, addr, foreignApps, foreignAssets, boxReferences, types.NoOpOC, program, program, gSchema, lSchema, extraPages, params, types.Address{}, note, types.Digest{}, [32]byte{}, types.Address{}) 778 require.Error(t, err, "the app id 10 provided for this box is not in the foreignApps array") 779 } 780 781 func TestComputeGroupID(t *testing.T) { 782 // compare regular transactions created in SDK with 'goal clerk send' result 783 // compare transaction group created in SDK with 'goal clerk group' result 784 const address = "UPYAFLHSIPMJOHVXU2MPLQ46GXJKSDCEMZ6RLCQ7GWB5PRDKJUWKKXECXI" 785 const fromAddress, toAddress = address, address 786 const fee = 1000 787 const amount = 2000 788 const genesisID = "devnet-v1.0" 789 genesisHash := byteFromBase64("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E=") 790 791 const firstRound1 = 710399 792 params1 := types.SuggestedParams{ 793 Fee: fee, 794 FirstRoundValid: firstRound1, 795 LastRoundValid: firstRound1 + 1000, 796 GenesisHash: genesisHash, 797 GenesisID: genesisID, 798 FlatFee: true, 799 } 800 note1 := byteFromBase64("wRKw5cJ0CMo=") 801 tx1, err := MakePaymentTxn(fromAddress, toAddress, amount, note1, "", params1) 802 require.NoError(t, err) 803 804 const firstRound2 = 710515 805 params2 := types.SuggestedParams{ 806 Fee: fee, 807 FirstRoundValid: firstRound2, 808 LastRoundValid: firstRound2 + 1000, 809 GenesisHash: genesisHash, 810 GenesisID: genesisID, 811 FlatFee: true, 812 } 813 note2 := byteFromBase64("dBlHI6BdrIg=") 814 tx2, err := MakePaymentTxn(fromAddress, toAddress, amount, note2, "", params2) 815 require.NoError(t, err) 816 817 const goldenTx1 = "gaN0eG6Ko2FtdM0H0KNmZWXNA+iiZnbOAArW/6NnZW6rZGV2bmV0LXYxLjCiZ2jEILAtz+3tknW6iiStLW4gnSvbXUqW3ul3ghinaDc5pY9Bomx2zgAK2uekbm90ZcQIwRKw5cJ0CMqjcmN2xCCj8AKs8kPYlx63ppj1w5410qkMRGZ9FYofNYPXxGpNLKNzbmTEIKPwAqzyQ9iXHremmPXDnjXSqQxEZn0Vih81g9fEak0spHR5cGWjcGF5" 818 const goldenTx2 = "gaN0eG6Ko2FtdM0H0KNmZWXNA+iiZnbOAArXc6NnZW6rZGV2bmV0LXYxLjCiZ2jEILAtz+3tknW6iiStLW4gnSvbXUqW3ul3ghinaDc5pY9Bomx2zgAK21ukbm90ZcQIdBlHI6BdrIijcmN2xCCj8AKs8kPYlx63ppj1w5410qkMRGZ9FYofNYPXxGpNLKNzbmTEIKPwAqzyQ9iXHremmPXDnjXSqQxEZn0Vih81g9fEak0spHR5cGWjcGF5" 819 820 // goal clerk send dumps unsigned transaction as signed with empty signature in order to save tx type 821 stx1 := types.SignedTxn{Sig: types.Signature{}, Msig: types.MultisigSig{}, Txn: tx1} 822 stx2 := types.SignedTxn{Sig: types.Signature{}, Msig: types.MultisigSig{}, Txn: tx2} 823 require.Equal(t, byteFromBase64(goldenTx1), msgpack.Encode(stx1)) 824 require.Equal(t, byteFromBase64(goldenTx2), msgpack.Encode(stx2)) 825 826 gid, err := crypto.ComputeGroupID([]types.Transaction{tx1, tx2}) 827 require.NoError(t, err) 828 829 // goal clerk group sets Group to every transaction and concatenate them in output file 830 // simulating that behavior here 831 stx1.Txn.Group = gid 832 stx2.Txn.Group = gid 833 834 var txg []byte 835 txg = append(txg, msgpack.Encode(stx1)...) 836 txg = append(txg, msgpack.Encode(stx2)...) 837 838 const goldenTxg = "gaN0eG6Lo2FtdM0H0KNmZWXNA+iiZnbOAArW/6NnZW6rZGV2bmV0LXYxLjCiZ2jEILAtz+3tknW6iiStLW4gnSvbXUqW3ul3ghinaDc5pY9Bo2dycMQgLiQ9OBup9H/bZLSfQUH2S6iHUM6FQ3PLuv9FNKyt09SibHbOAAra56Rub3RlxAjBErDlwnQIyqNyY3bEIKPwAqzyQ9iXHremmPXDnjXSqQxEZn0Vih81g9fEak0so3NuZMQgo/ACrPJD2Jcet6aY9cOeNdKpDERmfRWKHzWD18RqTSykdHlwZaNwYXmBo3R4boujYW10zQfQo2ZlZc0D6KJmds4ACtdzo2dlbqtkZXZuZXQtdjEuMKJnaMQgsC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0GjZ3JwxCAuJD04G6n0f9tktJ9BQfZLqIdQzoVDc8u6/0U0rK3T1KJsds4ACttbpG5vdGXECHQZRyOgXayIo3JjdsQgo/ACrPJD2Jcet6aY9cOeNdKpDERmfRWKHzWD18RqTSyjc25kxCCj8AKs8kPYlx63ppj1w5410qkMRGZ9FYofNYPXxGpNLKR0eXBlo3BheQ==" 839 840 require.Equal(t, byteFromBase64(goldenTxg), txg) 841 842 // check transaction.AssignGroupID, do not validate correctness of Group field calculation 843 result, err := AssignGroupID([]types.Transaction{tx1, tx2}, "BH55E5RMBD4GYWXGX5W5PJ5JAHPGM5OXKDQH5DC4O2MGI7NW4H6VOE4CP4") 844 require.NoError(t, err) 845 require.Equal(t, 0, len(result)) 846 847 result, err = AssignGroupID([]types.Transaction{tx1, tx2}, address) 848 require.NoError(t, err) 849 require.Equal(t, 2, len(result)) 850 851 result, err = AssignGroupID([]types.Transaction{tx1, tx2}, "") 852 require.NoError(t, err) 853 require.Equal(t, 2, len(result)) 854 } 855 856 func TestLogicSig(t *testing.T) { 857 // validate LogicSig signed transaction against goal 858 const fromAddress = "47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU" 859 const toAddress = "PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI" 860 const referenceTxID = "5FJDJD5LMZC3EHUYYJNH5I23U4X6H2KXABNDGPIL557ZMJ33GZHQ" 861 const mn = "advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor" 862 const fee = 1000 863 const amount = 2000 864 const firstRound = 2063137 865 const genesisID = "devnet-v1.0" 866 genesisHash := byteFromBase64("sC3P7e2SdbqKJK0tbiCdK9tdSpbe6XeCGKdoNzmlj0E=") 867 note := byteFromBase64("8xMCTuLQ810=") 868 869 params := types.SuggestedParams{ 870 Fee: fee, 871 FirstRoundValid: firstRound, 872 LastRoundValid: firstRound + 1000, 873 GenesisHash: genesisHash, 874 GenesisID: genesisID, 875 FlatFee: true, 876 } 877 tx, err := MakePaymentTxn(fromAddress, toAddress, amount, note, "", params) 878 require.NoError(t, err) 879 880 // goal clerk send -o tx3 -a 2000 --fee 1000 -d ~/.algorand -w test -L sig.lsig --argb64 MTIz --argb64 NDU2 \ 881 // -f 47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU \ 882 // -t PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI 883 const golden = "gqRsc2lng6NhcmeSxAMxMjPEAzQ1NqFsxAUBIAEBIqNzaWfEQE6HXaI5K0lcq50o/y3bWOYsyw9TLi/oorZB4xaNdn1Z14351u2f6JTON478fl+JhIP4HNRRAIh/I8EWXBPpJQ2jdHhuiqNhbXTNB9CjZmVlzQPoomZ2zgAfeyGjZ2Vuq2Rldm5ldC12MS4womdoxCCwLc/t7ZJ1uookrS1uIJ0r211Klt7pd4IYp2g3OaWPQaJsds4AH38JpG5vdGXECPMTAk7i0PNdo3JjdsQge2ziT+tbrMCxZOKcIixX9fY9w4fUOQSCWEEcX+EPfAKjc25kxCDn8PhNBoEd+fMcjYeLEVX0Zx1RoYXCAJCGZ/RJWHBooaR0eXBlo3BheQ==" 884 885 program := []byte{1, 32, 1, 1, 34} 886 args := make([][]byte, 2) 887 args[0] = []byte("123") 888 args[1] = []byte("456") 889 key, err := mnemonic.ToPrivateKey(mn) 890 var pk crypto.MultisigAccount 891 require.NoError(t, err) 892 lsig, err := crypto.MakeLogicSig(program, args, key, pk) 893 require.NoError(t, err) 894 895 _, stxBytes, err := crypto.SignLogicsigTransaction(lsig, tx) 896 require.NoError(t, err) 897 898 require.Equal(t, byteFromBase64(golden), stxBytes) 899 900 sender, err := types.DecodeAddress(fromAddress) 901 require.NoError(t, err) 902 903 verified := crypto.VerifyLogicSig(lsig, sender) 904 require.True(t, verified) 905 } 906 907 func TestFee(t *testing.T) { 908 testcases := []struct { 909 name string 910 flatFee bool 911 fee types.MicroAlgos 912 expected types.MicroAlgos 913 }{ 914 { 915 name: "Use flat fee", 916 flatFee: true, 917 fee: 1001, 918 expected: 1001, 919 }, 920 { 921 name: "Flat fee does not get overridden with min fee", 922 flatFee: true, 923 fee: 999, 924 expected: 999, 925 }, 926 { 927 name: "Estimated fee overridden with min fee", 928 flatFee: false, 929 fee: 1, 930 expected: 1000, 931 }, 932 { 933 name: "Flat fee set to 0", 934 flatFee: true, 935 fee: 0, 936 expected: 0, 937 }, 938 } 939 addr := types.Address{}.String() 940 for _, testcase := range testcases { 941 t.Run(testcase.name, func(t *testing.T) { 942 var tx types.Transaction 943 var err error 944 945 params := types.SuggestedParams{ 946 FlatFee: testcase.flatFee, 947 Fee: testcase.fee, 948 FirstRoundValid: 1, 949 LastRoundValid: 1001, 950 GenesisHash: byteFromBase64("JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI="), 951 } 952 953 tx, err = MakeAssetTransferTxn(addr, addr, 1, nil, params, "", 1) 954 require.NoError(t, err) 955 require.Equal(t, testcase.expected, tx.Fee) 956 957 tx, err = MakeAssetAcceptanceTxn(addr, nil, params, 1) 958 require.NoError(t, err) 959 require.Equal(t, testcase.expected, tx.Fee) 960 961 tx, err = MakeAssetRevocationTxn(addr, addr, 1, addr, nil, params, 1) 962 require.NoError(t, err) 963 require.Equal(t, testcase.expected, tx.Fee) 964 965 tx, err = MakeAssetDestroyTxn(addr, nil, params, 1) 966 require.NoError(t, err) 967 require.Equal(t, testcase.expected, tx.Fee) 968 969 tx, err = MakeAssetCreateTxn(addr, nil, params, 1, 1, false, addr, addr, addr, addr, "", "", "", "") 970 require.NoError(t, err) 971 require.Equal(t, testcase.expected, tx.Fee) 972 973 tx, err = MakeAssetConfigTxn(addr, nil, params, 1, addr, addr, addr, addr, false) 974 require.NoError(t, err) 975 require.Equal(t, testcase.expected, tx.Fee) 976 977 tx, err = MakeAssetFreezeTxn(addr, nil, params, 1, addr, true) 978 require.NoError(t, err) 979 require.Equal(t, testcase.expected, tx.Fee) 980 }) 981 } 982 } 983 984 func TestParseBoxReferences(t *testing.T) { 985 986 genWithAppId := func(appId uint64) types.AppBoxReference { 987 return types.AppBoxReference{appId, []byte("example")} 988 } 989 990 genWithNewAppId := func() types.AppBoxReference { 991 return types.AppBoxReference{0, []byte("example")} 992 } 993 994 t.Run("appIndexExists", func(t *testing.T) { 995 appId := uint64(7) 996 abr := genWithAppId(appId) 997 998 brs, err := parseBoxReferences( 999 []types.AppBoxReference{abr}, 1000 []uint64{1, 3, 4, appId}, 1001 appId-1) 1002 require.NoError(t, err) 1003 require.Equal(t, 1004 []types.BoxReference{{ 1005 ForeignAppIdx: uint64(4), 1006 Name: abr.Name}}, 1007 brs) 1008 }) 1009 1010 t.Run("appIndexDoesNotExist", func(t *testing.T) { 1011 appId := uint64(7) 1012 abr := genWithAppId(appId) 1013 1014 _, err := parseBoxReferences( 1015 []types.AppBoxReference{abr}, 1016 []uint64{1, 3, 4}, 1017 appId-1) 1018 require.Error(t, err) 1019 }) 1020 1021 t.Run("newAppId", func(t *testing.T) { 1022 abr := genWithNewAppId() 1023 1024 brs, err := parseBoxReferences( 1025 []types.AppBoxReference{abr}, 1026 []uint64{}, 1027 uint64(1)) 1028 require.NoError(t, err) 1029 require.Equal(t, 1030 []types.BoxReference{{ 1031 ForeignAppIdx: uint64(0), 1032 Name: abr.Name}}, 1033 brs) 1034 }) 1035 1036 t.Run("fallbackToCurrentApp", func(t *testing.T) { 1037 // Mirrors priority search in goal from `cmd/goal/application.go::translateBoxRefs`. 1038 appId := uint64(7) 1039 abr := genWithAppId(appId) 1040 1041 // Prefer foreign apps index when present. 1042 brs, err := parseBoxReferences( 1043 []types.AppBoxReference{abr}, 1044 []uint64{1, 3, 4, appId}, 1045 appId) 1046 require.NoError(t, err) 1047 require.Equal(t, 1048 []types.BoxReference{{ 1049 ForeignAppIdx: uint64(4), 1050 Name: abr.Name}}, 1051 brs) 1052 1053 // Fallback to current app when absent from foreign apps. 1054 brs, err = parseBoxReferences( 1055 []types.AppBoxReference{abr}, 1056 []uint64{1, 3, 4}, 1057 appId) 1058 require.NoError(t, err) 1059 require.Equal(t, 1060 []types.BoxReference{{ 1061 ForeignAppIdx: uint64(0), 1062 Name: abr.Name}}, 1063 brs) 1064 }) 1065 }