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