github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/transfer_subnet_ownership_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 "go.uber.org/mock/gomock" 12 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/snow" 15 "github.com/MetalBlockchain/metalgo/utils" 16 "github.com/MetalBlockchain/metalgo/utils/constants" 17 "github.com/MetalBlockchain/metalgo/utils/units" 18 "github.com/MetalBlockchain/metalgo/vms/components/avax" 19 "github.com/MetalBlockchain/metalgo/vms/components/verify" 20 "github.com/MetalBlockchain/metalgo/vms/platformvm/fx" 21 "github.com/MetalBlockchain/metalgo/vms/platformvm/stakeable" 22 "github.com/MetalBlockchain/metalgo/vms/secp256k1fx" 23 "github.com/MetalBlockchain/metalgo/vms/types" 24 ) 25 26 func TestTransferSubnetOwnershipTxSerialization(t *testing.T) { 27 require := require.New(t) 28 29 addr := ids.ShortID{ 30 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 31 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 32 0x44, 0x55, 0x66, 0x77, 33 } 34 35 avaxAssetID, err := ids.FromString("FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z") 36 require.NoError(err) 37 38 customAssetID := ids.ID{ 39 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 40 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 41 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 42 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 43 } 44 45 txID := ids.ID{ 46 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 47 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 48 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 49 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 50 } 51 subnetID := ids.ID{ 52 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 53 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 54 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 55 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 56 } 57 58 simpleTransferSubnetOwnershipTx := &TransferSubnetOwnershipTx{ 59 BaseTx: BaseTx{ 60 BaseTx: avax.BaseTx{ 61 NetworkID: constants.MainnetID, 62 BlockchainID: constants.PlatformChainID, 63 Outs: []*avax.TransferableOutput{}, 64 Ins: []*avax.TransferableInput{ 65 { 66 UTXOID: avax.UTXOID{ 67 TxID: txID, 68 OutputIndex: 1, 69 }, 70 Asset: avax.Asset{ 71 ID: avaxAssetID, 72 }, 73 In: &secp256k1fx.TransferInput{ 74 Amt: units.MilliAvax, 75 Input: secp256k1fx.Input{ 76 SigIndices: []uint32{5}, 77 }, 78 }, 79 }, 80 }, 81 Memo: types.JSONByteSlice{}, 82 }, 83 }, 84 Subnet: subnetID, 85 SubnetAuth: &secp256k1fx.Input{ 86 SigIndices: []uint32{3}, 87 }, 88 Owner: &secp256k1fx.OutputOwners{ 89 Locktime: 0, 90 Threshold: 1, 91 Addrs: []ids.ShortID{ 92 addr, 93 }, 94 }, 95 } 96 require.NoError(simpleTransferSubnetOwnershipTx.SyntacticVerify(&snow.Context{ 97 NetworkID: 1, 98 ChainID: constants.PlatformChainID, 99 AVAXAssetID: avaxAssetID, 100 })) 101 102 expectedUnsignedSimpleTransferSubnetOwnershipTxBytes := []byte{ 103 // Codec version 104 0x00, 0x00, 105 // TransferSubnetOwnershipTx Type ID 106 0x00, 0x00, 0x00, 0x21, 107 // Mainnet network ID 108 0x00, 0x00, 0x00, 0x01, 109 // P-chain blockchain ID 110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 114 // Number of outputs 115 0x00, 0x00, 0x00, 0x00, 116 // Number of inputs 117 0x00, 0x00, 0x00, 0x01, 118 // Inputs[0] 119 // TxID 120 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 121 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 122 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 123 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 124 // Tx output index 125 0x00, 0x00, 0x00, 0x01, 126 // Mainnet AVAX assetID 127 0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a, 128 0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78, 129 0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf, 130 0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff, 131 // secp256k1fx transfer input type ID 132 0x00, 0x00, 0x00, 0x05, 133 // input amount = 1 MilliAvax 134 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 135 // number of signatures needed in input 136 0x00, 0x00, 0x00, 0x01, 137 // index of signer 138 0x00, 0x00, 0x00, 0x05, 139 // length of memo 140 0x00, 0x00, 0x00, 0x00, 141 // subnetID to modify 142 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 143 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 144 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 145 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 146 // secp256k1fx authorization type ID 147 0x00, 0x00, 0x00, 0x0a, 148 // number of signatures needed in authorization 149 0x00, 0x00, 0x00, 0x01, 150 // index of signer 151 0x00, 0x00, 0x00, 0x03, 152 // secp256k1fx output owners type ID 153 0x00, 0x00, 0x00, 0x0b, 154 // locktime 155 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 156 // threshold 157 0x00, 0x00, 0x00, 0x01, 158 // number of addrs 159 0x00, 0x00, 0x00, 0x01, 160 // Addrs[0] 161 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 162 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 163 0x44, 0x55, 0x66, 0x77, 164 } 165 var unsignedSimpleTransferSubnetOwnershipTx UnsignedTx = simpleTransferSubnetOwnershipTx 166 unsignedSimpleTransferSubnetOwnershipTxBytes, err := Codec.Marshal(CodecVersion, &unsignedSimpleTransferSubnetOwnershipTx) 167 require.NoError(err) 168 require.Equal(expectedUnsignedSimpleTransferSubnetOwnershipTxBytes, unsignedSimpleTransferSubnetOwnershipTxBytes) 169 170 complexTransferSubnetOwnershipTx := &TransferSubnetOwnershipTx{ 171 BaseTx: BaseTx{ 172 BaseTx: avax.BaseTx{ 173 NetworkID: constants.MainnetID, 174 BlockchainID: constants.PlatformChainID, 175 Outs: []*avax.TransferableOutput{ 176 { 177 Asset: avax.Asset{ 178 ID: avaxAssetID, 179 }, 180 Out: &stakeable.LockOut{ 181 Locktime: 87654321, 182 TransferableOut: &secp256k1fx.TransferOutput{ 183 Amt: 1, 184 OutputOwners: secp256k1fx.OutputOwners{ 185 Locktime: 12345678, 186 Threshold: 0, 187 Addrs: []ids.ShortID{}, 188 }, 189 }, 190 }, 191 }, 192 { 193 Asset: avax.Asset{ 194 ID: customAssetID, 195 }, 196 Out: &stakeable.LockOut{ 197 Locktime: 876543210, 198 TransferableOut: &secp256k1fx.TransferOutput{ 199 Amt: 0xffffffffffffffff, 200 OutputOwners: secp256k1fx.OutputOwners{ 201 Locktime: 0, 202 Threshold: 1, 203 Addrs: []ids.ShortID{ 204 addr, 205 }, 206 }, 207 }, 208 }, 209 }, 210 }, 211 Ins: []*avax.TransferableInput{ 212 { 213 UTXOID: avax.UTXOID{ 214 TxID: txID, 215 OutputIndex: 1, 216 }, 217 Asset: avax.Asset{ 218 ID: avaxAssetID, 219 }, 220 In: &secp256k1fx.TransferInput{ 221 Amt: units.Avax, 222 Input: secp256k1fx.Input{ 223 SigIndices: []uint32{2, 5}, 224 }, 225 }, 226 }, 227 { 228 UTXOID: avax.UTXOID{ 229 TxID: txID, 230 OutputIndex: 2, 231 }, 232 Asset: avax.Asset{ 233 ID: customAssetID, 234 }, 235 In: &stakeable.LockIn{ 236 Locktime: 876543210, 237 TransferableIn: &secp256k1fx.TransferInput{ 238 Amt: 0xefffffffffffffff, 239 Input: secp256k1fx.Input{ 240 SigIndices: []uint32{0}, 241 }, 242 }, 243 }, 244 }, 245 { 246 UTXOID: avax.UTXOID{ 247 TxID: txID, 248 OutputIndex: 3, 249 }, 250 Asset: avax.Asset{ 251 ID: customAssetID, 252 }, 253 In: &secp256k1fx.TransferInput{ 254 Amt: 0x1000000000000000, 255 Input: secp256k1fx.Input{ 256 SigIndices: []uint32{}, 257 }, 258 }, 259 }, 260 }, 261 Memo: types.JSONByteSlice("😅\nwell that's\x01\x23\x45!"), 262 }, 263 }, 264 Subnet: subnetID, 265 SubnetAuth: &secp256k1fx.Input{ 266 SigIndices: []uint32{}, 267 }, 268 Owner: &secp256k1fx.OutputOwners{ 269 Locktime: 876543210, 270 Threshold: 1, 271 Addrs: []ids.ShortID{ 272 addr, 273 }, 274 }, 275 } 276 avax.SortTransferableOutputs(complexTransferSubnetOwnershipTx.Outs, Codec) 277 utils.Sort(complexTransferSubnetOwnershipTx.Ins) 278 require.NoError(complexTransferSubnetOwnershipTx.SyntacticVerify(&snow.Context{ 279 NetworkID: 1, 280 ChainID: constants.PlatformChainID, 281 AVAXAssetID: avaxAssetID, 282 })) 283 284 expectedUnsignedComplexTransferSubnetOwnershipTxBytes := []byte{ 285 // Codec version 286 0x00, 0x00, 287 // TransferSubnetOwnershipTx Type ID 288 0x00, 0x00, 0x00, 0x21, 289 // Mainnet network ID 290 0x00, 0x00, 0x00, 0x01, 291 // P-chain blockchain ID 292 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 293 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 294 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 295 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 296 // Number of outputs 297 0x00, 0x00, 0x00, 0x02, 298 // Outputs[0] 299 // Mainnet AVAX assetID 300 0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a, 301 0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78, 302 0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf, 303 0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff, 304 // Stakeable locked output type ID 305 0x00, 0x00, 0x00, 0x16, 306 // Locktime 307 0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x7f, 0xb1, 308 // secp256k1fx transfer output type ID 309 0x00, 0x00, 0x00, 0x07, 310 // amount 311 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 312 // secp256k1fx output locktime 313 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x61, 0x4e, 314 // threshold 315 0x00, 0x00, 0x00, 0x00, 316 // number of addresses 317 0x00, 0x00, 0x00, 0x00, 318 // Outputs[1] 319 // custom asset ID 320 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 321 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 322 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 323 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 324 // Stakeable locked output type ID 325 0x00, 0x00, 0x00, 0x16, 326 // Locktime 327 0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea, 328 // secp256k1fx transfer output type ID 329 0x00, 0x00, 0x00, 0x07, 330 // amount 331 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 332 // secp256k1fx output locktime 333 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 334 // threshold 335 0x00, 0x00, 0x00, 0x01, 336 // number of addresses 337 0x00, 0x00, 0x00, 0x01, 338 // address[0] 339 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 340 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 341 0x44, 0x55, 0x66, 0x77, 342 // number of inputs 343 0x00, 0x00, 0x00, 0x03, 344 // inputs[0] 345 // TxID 346 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 347 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 348 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 349 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 350 // Tx output index 351 0x00, 0x00, 0x00, 0x01, 352 // Mainnet AVAX assetID 353 0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a, 354 0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78, 355 0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf, 356 0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff, 357 // secp256k1fx transfer input type ID 358 0x00, 0x00, 0x00, 0x05, 359 // input amount = 1 Avax 360 0x00, 0x00, 0x00, 0x00, 0x3b, 0x9a, 0xca, 0x00, 361 // number of signatures needed in input 362 0x00, 0x00, 0x00, 0x02, 363 // index of first signer 364 0x00, 0x00, 0x00, 0x02, 365 // index of second signer 366 0x00, 0x00, 0x00, 0x05, 367 // inputs[1] 368 // TxID 369 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 370 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 371 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 372 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 373 // Tx output index 374 0x00, 0x00, 0x00, 0x02, 375 // Custom asset ID 376 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 377 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 378 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 379 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 380 // Stakeable locked input type ID 381 0x00, 0x00, 0x00, 0x15, 382 // Locktime 383 0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea, 384 // secp256k1fx transfer input type ID 385 0x00, 0x00, 0x00, 0x05, 386 // input amount 387 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 388 // number of signatures needed in input 389 0x00, 0x00, 0x00, 0x01, 390 // index of signer 391 0x00, 0x00, 0x00, 0x00, 392 // inputs[2] 393 // TxID 394 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 395 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 396 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 397 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 398 // Tx output index 399 0x00, 0x00, 0x00, 0x03, 400 // custom asset ID 401 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 402 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 403 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 404 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 405 // secp256k1fx transfer input type ID 406 0x00, 0x00, 0x00, 0x05, 407 // input amount 408 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 409 // number of signatures needed in input 410 0x00, 0x00, 0x00, 0x00, 411 // length of memo 412 0x00, 0x00, 0x00, 0x14, 413 // memo 414 0xf0, 0x9f, 0x98, 0x85, 0x0a, 0x77, 0x65, 0x6c, 415 0x6c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x27, 0x73, 416 0x01, 0x23, 0x45, 0x21, 417 // subnetID to modify 418 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 419 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 420 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 421 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 422 // secp256k1fx authorization type ID 423 0x00, 0x00, 0x00, 0x0a, 424 // number of signatures needed in authorization 425 0x00, 0x00, 0x00, 0x00, 426 // secp256k1fx output owners type ID 427 0x00, 0x00, 0x00, 0x0b, 428 // locktime 429 0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea, 430 // threshold 431 0x00, 0x00, 0x00, 0x01, 432 // number of addrs 433 0x00, 0x00, 0x00, 0x01, 434 // Addrs[0] 435 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 436 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 437 0x44, 0x55, 0x66, 0x77, 438 } 439 var unsignedComplexTransferSubnetOwnershipTx UnsignedTx = complexTransferSubnetOwnershipTx 440 unsignedComplexTransferSubnetOwnershipTxBytes, err := Codec.Marshal(CodecVersion, &unsignedComplexTransferSubnetOwnershipTx) 441 require.NoError(err) 442 require.Equal(expectedUnsignedComplexTransferSubnetOwnershipTxBytes, unsignedComplexTransferSubnetOwnershipTxBytes) 443 444 aliaser := ids.NewAliaser() 445 require.NoError(aliaser.Alias(constants.PlatformChainID, "P")) 446 447 unsignedComplexTransferSubnetOwnershipTx.InitCtx(&snow.Context{ 448 NetworkID: 1, 449 ChainID: constants.PlatformChainID, 450 AVAXAssetID: avaxAssetID, 451 BCLookup: aliaser, 452 }) 453 454 unsignedComplexTransferSubnetOwnershipTxJSONBytes, err := json.MarshalIndent(unsignedComplexTransferSubnetOwnershipTx, "", "\t") 455 require.NoError(err) 456 require.Equal(`{ 457 "networkID": 1, 458 "blockchainID": "11111111111111111111111111111111LpoYY", 459 "outputs": [ 460 { 461 "assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", 462 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 463 "output": { 464 "locktime": 87654321, 465 "output": { 466 "addresses": [], 467 "amount": 1, 468 "locktime": 12345678, 469 "threshold": 0 470 } 471 } 472 }, 473 { 474 "assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc", 475 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 476 "output": { 477 "locktime": 876543210, 478 "output": { 479 "addresses": [ 480 "P-metal1g32kvaugnx4tk3z4vemc3xd2hdz92enhqaj6ex" 481 ], 482 "amount": 18446744073709551615, 483 "locktime": 0, 484 "threshold": 1 485 } 486 } 487 } 488 ], 489 "inputs": [ 490 { 491 "txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN", 492 "outputIndex": 1, 493 "assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", 494 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 495 "input": { 496 "amount": 1000000000, 497 "signatureIndices": [ 498 2, 499 5 500 ] 501 } 502 }, 503 { 504 "txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN", 505 "outputIndex": 2, 506 "assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc", 507 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 508 "input": { 509 "locktime": 876543210, 510 "input": { 511 "amount": 17293822569102704639, 512 "signatureIndices": [ 513 0 514 ] 515 } 516 } 517 }, 518 { 519 "txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN", 520 "outputIndex": 3, 521 "assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc", 522 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 523 "input": { 524 "amount": 1152921504606846976, 525 "signatureIndices": [] 526 } 527 } 528 ], 529 "memo": "0xf09f98850a77656c6c2074686174277301234521", 530 "subnetID": "SkB92YpWm4UpburLz9tEKZw2i67H3FF6YkjaU4BkFUDTG9Xm", 531 "subnetAuthorization": { 532 "signatureIndices": [] 533 }, 534 "newOwner": { 535 "addresses": [ 536 "P-metal1g32kvaugnx4tk3z4vemc3xd2hdz92enhqaj6ex" 537 ], 538 "locktime": 876543210, 539 "threshold": 1 540 } 541 }`, string(unsignedComplexTransferSubnetOwnershipTxJSONBytes)) 542 } 543 544 func TestTransferSubnetOwnershipTxSyntacticVerify(t *testing.T) { 545 type test struct { 546 name string 547 txFunc func(*gomock.Controller) *TransferSubnetOwnershipTx 548 expectedErr error 549 } 550 551 var ( 552 networkID = uint32(1337) 553 chainID = ids.GenerateTestID() 554 ) 555 556 ctx := &snow.Context{ 557 ChainID: chainID, 558 NetworkID: networkID, 559 } 560 561 // A BaseTx that already passed syntactic verification. 562 verifiedBaseTx := BaseTx{ 563 SyntacticallyVerified: true, 564 } 565 // Sanity check. 566 require.NoError(t, verifiedBaseTx.SyntacticVerify(ctx)) 567 568 // A BaseTx that passes syntactic verification. 569 validBaseTx := BaseTx{ 570 BaseTx: avax.BaseTx{ 571 NetworkID: networkID, 572 BlockchainID: chainID, 573 }, 574 } 575 // Sanity check. 576 require.NoError(t, validBaseTx.SyntacticVerify(ctx)) 577 // Make sure we're not caching the verification result. 578 require.False(t, validBaseTx.SyntacticallyVerified) 579 580 // A BaseTx that fails syntactic verification. 581 invalidBaseTx := BaseTx{} 582 583 tests := []test{ 584 { 585 name: "nil tx", 586 txFunc: func(*gomock.Controller) *TransferSubnetOwnershipTx { 587 return nil 588 }, 589 expectedErr: ErrNilTx, 590 }, 591 { 592 name: "already verified", 593 txFunc: func(*gomock.Controller) *TransferSubnetOwnershipTx { 594 return &TransferSubnetOwnershipTx{BaseTx: verifiedBaseTx} 595 }, 596 expectedErr: nil, 597 }, 598 { 599 name: "invalid BaseTx", 600 txFunc: func(*gomock.Controller) *TransferSubnetOwnershipTx { 601 return &TransferSubnetOwnershipTx{ 602 // Set subnetID so we don't error on that check. 603 Subnet: ids.GenerateTestID(), 604 BaseTx: invalidBaseTx, 605 } 606 }, 607 expectedErr: avax.ErrWrongNetworkID, 608 }, 609 { 610 name: "invalid subnetID", 611 txFunc: func(*gomock.Controller) *TransferSubnetOwnershipTx { 612 return &TransferSubnetOwnershipTx{ 613 BaseTx: validBaseTx, 614 Subnet: constants.PrimaryNetworkID, 615 } 616 }, 617 expectedErr: ErrTransferPermissionlessSubnet, 618 }, 619 { 620 name: "invalid subnetAuth", 621 txFunc: func(ctrl *gomock.Controller) *TransferSubnetOwnershipTx { 622 // This SubnetAuth fails verification. 623 invalidSubnetAuth := verify.NewMockVerifiable(ctrl) 624 invalidSubnetAuth.EXPECT().Verify().Return(errInvalidSubnetAuth) 625 return &TransferSubnetOwnershipTx{ 626 // Set subnetID so we don't error on that check. 627 Subnet: ids.GenerateTestID(), 628 BaseTx: validBaseTx, 629 SubnetAuth: invalidSubnetAuth, 630 } 631 }, 632 expectedErr: errInvalidSubnetAuth, 633 }, 634 { 635 name: "passes verification", 636 txFunc: func(ctrl *gomock.Controller) *TransferSubnetOwnershipTx { 637 // This SubnetAuth passes verification. 638 validSubnetAuth := verify.NewMockVerifiable(ctrl) 639 validSubnetAuth.EXPECT().Verify().Return(nil) 640 mockOwner := fx.NewMockOwner(ctrl) 641 mockOwner.EXPECT().Verify().Return(nil) 642 return &TransferSubnetOwnershipTx{ 643 // Set subnetID so we don't error on that check. 644 Subnet: ids.GenerateTestID(), 645 BaseTx: validBaseTx, 646 SubnetAuth: validSubnetAuth, 647 Owner: mockOwner, 648 } 649 }, 650 expectedErr: nil, 651 }, 652 } 653 654 for _, tt := range tests { 655 t.Run(tt.name, func(t *testing.T) { 656 require := require.New(t) 657 ctrl := gomock.NewController(t) 658 659 tx := tt.txFunc(ctrl) 660 err := tx.SyntacticVerify(ctx) 661 require.ErrorIs(err, tt.expectedErr) 662 if tt.expectedErr != nil { 663 return 664 } 665 require.True(tx.SyntacticallyVerified) 666 }) 667 } 668 }