github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/base_tx_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package txs 5 6 import ( 7 "encoding/json" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 12 "github.com/MetalBlockchain/metalgo/ids" 13 "github.com/MetalBlockchain/metalgo/snow" 14 "github.com/MetalBlockchain/metalgo/utils" 15 "github.com/MetalBlockchain/metalgo/utils/constants" 16 "github.com/MetalBlockchain/metalgo/utils/units" 17 "github.com/MetalBlockchain/metalgo/vms/components/avax" 18 "github.com/MetalBlockchain/metalgo/vms/platformvm/stakeable" 19 "github.com/MetalBlockchain/metalgo/vms/secp256k1fx" 20 "github.com/MetalBlockchain/metalgo/vms/types" 21 ) 22 23 func TestBaseTxSerialization(t *testing.T) { 24 require := require.New(t) 25 26 addr := ids.ShortID{ 27 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 28 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 29 0x44, 0x55, 0x66, 0x77, 30 } 31 32 avaxAssetID, err := ids.FromString("FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z") 33 require.NoError(err) 34 35 customAssetID := ids.ID{ 36 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 37 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 38 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 39 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 40 } 41 42 txID := ids.ID{ 43 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 44 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 45 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 46 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 47 } 48 49 simpleBaseTx := &BaseTx{ 50 BaseTx: avax.BaseTx{ 51 NetworkID: constants.MainnetID, 52 BlockchainID: constants.PlatformChainID, 53 Outs: []*avax.TransferableOutput{}, 54 Ins: []*avax.TransferableInput{ 55 { 56 UTXOID: avax.UTXOID{ 57 TxID: txID, 58 OutputIndex: 1, 59 }, 60 Asset: avax.Asset{ 61 ID: avaxAssetID, 62 }, 63 In: &secp256k1fx.TransferInput{ 64 Amt: units.MilliAvax, 65 Input: secp256k1fx.Input{ 66 SigIndices: []uint32{5}, 67 }, 68 }, 69 }, 70 }, 71 Memo: types.JSONByteSlice{}, 72 }, 73 } 74 require.NoError(simpleBaseTx.SyntacticVerify(&snow.Context{ 75 NetworkID: 1, 76 ChainID: constants.PlatformChainID, 77 AVAXAssetID: avaxAssetID, 78 })) 79 80 expectedUnsignedSimpleBaseTxBytes := []byte{ 81 // Codec version 82 0x00, 0x00, 83 // BaseTx Type ID 84 0x00, 0x00, 0x00, 0x22, 85 // Mainnet network ID 86 0x00, 0x00, 0x00, 0x01, 87 // P-chain blockchain ID 88 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 89 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 92 // Number of outputs 93 0x00, 0x00, 0x00, 0x00, 94 // Number of inputs 95 0x00, 0x00, 0x00, 0x01, 96 // Inputs[0] 97 // TxID 98 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 99 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 100 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 101 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 102 // Tx output index 103 0x00, 0x00, 0x00, 0x01, 104 // Mainnet AVAX assetID 105 0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a, 106 0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78, 107 0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf, 108 0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff, 109 // secp256k1fx transfer input type ID 110 0x00, 0x00, 0x00, 0x05, 111 // input amount = 1 MilliAvax 112 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 113 // number of signatures needed in input 114 0x00, 0x00, 0x00, 0x01, 115 // index of signer 116 0x00, 0x00, 0x00, 0x05, 117 // length of memo 118 0x00, 0x00, 0x00, 0x00, 119 } 120 var unsignedSimpleBaseTx UnsignedTx = simpleBaseTx 121 unsignedSimpleBaseTxBytes, err := Codec.Marshal(CodecVersion, &unsignedSimpleBaseTx) 122 require.NoError(err) 123 require.Equal(expectedUnsignedSimpleBaseTxBytes, unsignedSimpleBaseTxBytes) 124 125 complexBaseTx := &BaseTx{ 126 BaseTx: avax.BaseTx{ 127 NetworkID: constants.MainnetID, 128 BlockchainID: constants.PlatformChainID, 129 Outs: []*avax.TransferableOutput{ 130 { 131 Asset: avax.Asset{ 132 ID: avaxAssetID, 133 }, 134 Out: &stakeable.LockOut{ 135 Locktime: 87654321, 136 TransferableOut: &secp256k1fx.TransferOutput{ 137 Amt: 1, 138 OutputOwners: secp256k1fx.OutputOwners{ 139 Locktime: 12345678, 140 Threshold: 0, 141 Addrs: []ids.ShortID{}, 142 }, 143 }, 144 }, 145 }, 146 { 147 Asset: avax.Asset{ 148 ID: customAssetID, 149 }, 150 Out: &stakeable.LockOut{ 151 Locktime: 876543210, 152 TransferableOut: &secp256k1fx.TransferOutput{ 153 Amt: 0xffffffffffffffff, 154 OutputOwners: secp256k1fx.OutputOwners{ 155 Locktime: 0, 156 Threshold: 1, 157 Addrs: []ids.ShortID{ 158 addr, 159 }, 160 }, 161 }, 162 }, 163 }, 164 }, 165 Ins: []*avax.TransferableInput{ 166 { 167 UTXOID: avax.UTXOID{ 168 TxID: txID, 169 OutputIndex: 1, 170 }, 171 Asset: avax.Asset{ 172 ID: avaxAssetID, 173 }, 174 In: &secp256k1fx.TransferInput{ 175 Amt: units.Avax, 176 Input: secp256k1fx.Input{ 177 SigIndices: []uint32{2, 5}, 178 }, 179 }, 180 }, 181 { 182 UTXOID: avax.UTXOID{ 183 TxID: txID, 184 OutputIndex: 2, 185 }, 186 Asset: avax.Asset{ 187 ID: customAssetID, 188 }, 189 In: &stakeable.LockIn{ 190 Locktime: 876543210, 191 TransferableIn: &secp256k1fx.TransferInput{ 192 Amt: 0xefffffffffffffff, 193 Input: secp256k1fx.Input{ 194 SigIndices: []uint32{0}, 195 }, 196 }, 197 }, 198 }, 199 { 200 UTXOID: avax.UTXOID{ 201 TxID: txID, 202 OutputIndex: 3, 203 }, 204 Asset: avax.Asset{ 205 ID: customAssetID, 206 }, 207 In: &secp256k1fx.TransferInput{ 208 Amt: 0x1000000000000000, 209 Input: secp256k1fx.Input{ 210 SigIndices: []uint32{}, 211 }, 212 }, 213 }, 214 }, 215 Memo: types.JSONByteSlice("😅\nwell that's\x01\x23\x45!"), 216 }, 217 } 218 avax.SortTransferableOutputs(complexBaseTx.Outs, Codec) 219 utils.Sort(complexBaseTx.Ins) 220 require.NoError(complexBaseTx.SyntacticVerify(&snow.Context{ 221 NetworkID: 1, 222 ChainID: constants.PlatformChainID, 223 AVAXAssetID: avaxAssetID, 224 })) 225 226 expectedUnsignedComplexBaseTxBytes := []byte{ 227 // Codec version 228 0x00, 0x00, 229 // BaseTx Type ID 230 0x00, 0x00, 0x00, 0x22, 231 // Mainnet network ID 232 0x00, 0x00, 0x00, 0x01, 233 // P-chain blockchain ID 234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 238 // Number of outputs 239 0x00, 0x00, 0x00, 0x02, 240 // Outputs[0] 241 // Mainnet AVAX assetID 242 0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a, 243 0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78, 244 0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf, 245 0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff, 246 // Stakeable locked output type ID 247 0x00, 0x00, 0x00, 0x16, 248 // Locktime 249 0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x7f, 0xb1, 250 // secp256k1fx transfer output type ID 251 0x00, 0x00, 0x00, 0x07, 252 // amount 253 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 254 // secp256k1fx output locktime 255 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x61, 0x4e, 256 // threshold 257 0x00, 0x00, 0x00, 0x00, 258 // number of addresses 259 0x00, 0x00, 0x00, 0x00, 260 // Outputs[1] 261 // custom asset ID 262 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 263 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 264 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 265 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 266 // Stakeable locked output type ID 267 0x00, 0x00, 0x00, 0x16, 268 // Locktime 269 0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea, 270 // secp256k1fx transfer output type ID 271 0x00, 0x00, 0x00, 0x07, 272 // amount 273 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 274 // secp256k1fx output locktime 275 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 276 // threshold 277 0x00, 0x00, 0x00, 0x01, 278 // number of addresses 279 0x00, 0x00, 0x00, 0x01, 280 // address[0] 281 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 282 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 283 0x44, 0x55, 0x66, 0x77, 284 // number of inputs 285 0x00, 0x00, 0x00, 0x03, 286 // inputs[0] 287 // TxID 288 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 289 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 290 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 291 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 292 // Tx output index 293 0x00, 0x00, 0x00, 0x01, 294 // Mainnet AVAX assetID 295 0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a, 296 0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78, 297 0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf, 298 0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff, 299 // secp256k1fx transfer input type ID 300 0x00, 0x00, 0x00, 0x05, 301 // input amount = 1 Avax 302 0x00, 0x00, 0x00, 0x00, 0x3b, 0x9a, 0xca, 0x00, 303 // number of signatures needed in input 304 0x00, 0x00, 0x00, 0x02, 305 // index of first signer 306 0x00, 0x00, 0x00, 0x02, 307 // index of second signer 308 0x00, 0x00, 0x00, 0x05, 309 // inputs[1] 310 // TxID 311 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 312 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 313 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 314 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 315 // Tx output index 316 0x00, 0x00, 0x00, 0x02, 317 // Custom asset ID 318 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 319 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 320 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 321 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 322 // Stakeable locked input type ID 323 0x00, 0x00, 0x00, 0x15, 324 // Locktime 325 0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea, 326 // secp256k1fx transfer input type ID 327 0x00, 0x00, 0x00, 0x05, 328 // input amount 329 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 330 // number of signatures needed in input 331 0x00, 0x00, 0x00, 0x01, 332 // index of signer 333 0x00, 0x00, 0x00, 0x00, 334 // inputs[2] 335 // TxID 336 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 337 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 338 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 339 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 340 // Tx output index 341 0x00, 0x00, 0x00, 0x03, 342 // custom asset ID 343 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 344 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 345 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 346 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 347 // secp256k1fx transfer input type ID 348 0x00, 0x00, 0x00, 0x05, 349 // input amount 350 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 351 // number of signatures needed in input 352 0x00, 0x00, 0x00, 0x00, 353 // length of memo 354 0x00, 0x00, 0x00, 0x14, 355 // memo 356 0xf0, 0x9f, 0x98, 0x85, 0x0a, 0x77, 0x65, 0x6c, 357 0x6c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x27, 0x73, 358 0x01, 0x23, 0x45, 0x21, 359 } 360 var unsignedComplexBaseTx UnsignedTx = complexBaseTx 361 unsignedComplexBaseTxBytes, err := Codec.Marshal(CodecVersion, &unsignedComplexBaseTx) 362 require.NoError(err) 363 require.Equal(expectedUnsignedComplexBaseTxBytes, unsignedComplexBaseTxBytes) 364 365 aliaser := ids.NewAliaser() 366 require.NoError(aliaser.Alias(constants.PlatformChainID, "P")) 367 368 unsignedComplexBaseTx.InitCtx(&snow.Context{ 369 NetworkID: 1, 370 ChainID: constants.PlatformChainID, 371 AVAXAssetID: avaxAssetID, 372 BCLookup: aliaser, 373 }) 374 375 unsignedComplexBaseTxJSONBytes, err := json.MarshalIndent(unsignedComplexBaseTx, "", "\t") 376 require.NoError(err) 377 require.Equal(`{ 378 "networkID": 1, 379 "blockchainID": "11111111111111111111111111111111LpoYY", 380 "outputs": [ 381 { 382 "assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", 383 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 384 "output": { 385 "locktime": 87654321, 386 "output": { 387 "addresses": [], 388 "amount": 1, 389 "locktime": 12345678, 390 "threshold": 0 391 } 392 } 393 }, 394 { 395 "assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc", 396 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 397 "output": { 398 "locktime": 876543210, 399 "output": { 400 "addresses": [ 401 "P-metal1g32kvaugnx4tk3z4vemc3xd2hdz92enhqaj6ex" 402 ], 403 "amount": 18446744073709551615, 404 "locktime": 0, 405 "threshold": 1 406 } 407 } 408 } 409 ], 410 "inputs": [ 411 { 412 "txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN", 413 "outputIndex": 1, 414 "assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", 415 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 416 "input": { 417 "amount": 1000000000, 418 "signatureIndices": [ 419 2, 420 5 421 ] 422 } 423 }, 424 { 425 "txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN", 426 "outputIndex": 2, 427 "assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc", 428 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 429 "input": { 430 "locktime": 876543210, 431 "input": { 432 "amount": 17293822569102704639, 433 "signatureIndices": [ 434 0 435 ] 436 } 437 } 438 }, 439 { 440 "txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN", 441 "outputIndex": 3, 442 "assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc", 443 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 444 "input": { 445 "amount": 1152921504606846976, 446 "signatureIndices": [] 447 } 448 } 449 ], 450 "memo": "0xf09f98850a77656c6c2074686174277301234521" 451 }`, string(unsignedComplexBaseTxJSONBytes)) 452 }