github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/txs/transform_subnet_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/reward" 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 TestTransformSubnetTxSerialization(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 simpleTransformTx := &TransformSubnetTx{ 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: 10 * units.Avax, 75 Input: secp256k1fx.Input{ 76 SigIndices: []uint32{5}, 77 }, 78 }, 79 }, 80 { 81 UTXOID: avax.UTXOID{ 82 TxID: txID, 83 OutputIndex: 2, 84 }, 85 Asset: avax.Asset{ 86 ID: customAssetID, 87 }, 88 In: &secp256k1fx.TransferInput{ 89 Amt: 0xefffffffffffffff, 90 Input: secp256k1fx.Input{ 91 SigIndices: []uint32{0}, 92 }, 93 }, 94 }, 95 }, 96 Memo: types.JSONByteSlice{}, 97 }, 98 }, 99 Subnet: subnetID, 100 AssetID: customAssetID, 101 InitialSupply: 0x1000000000000000, 102 MaximumSupply: 0xffffffffffffffff, 103 MinConsumptionRate: 1_000, 104 MaxConsumptionRate: 1_000_000, 105 MinValidatorStake: 1, 106 MaxValidatorStake: 0xffffffffffffffff, 107 MinStakeDuration: 1, 108 MaxStakeDuration: 365 * 24 * 60 * 60, 109 MinDelegationFee: reward.PercentDenominator, 110 MinDelegatorStake: 1, 111 MaxValidatorWeightFactor: 1, 112 UptimeRequirement: .95 * reward.PercentDenominator, 113 SubnetAuth: &secp256k1fx.Input{ 114 SigIndices: []uint32{3}, 115 }, 116 } 117 require.NoError(simpleTransformTx.SyntacticVerify(&snow.Context{ 118 NetworkID: 1, 119 ChainID: constants.PlatformChainID, 120 AVAXAssetID: avaxAssetID, 121 })) 122 123 expectedUnsignedSimpleTransformTxBytes := []byte{ 124 // Codec version 125 0x00, 0x00, 126 // TransformSubnetTx type ID 127 0x00, 0x00, 0x00, 0x18, 128 // Mainnet network ID 129 0x00, 0x00, 0x00, 0x01, 130 // P-chain blockchain ID 131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 135 // number of outputs 136 0x00, 0x00, 0x00, 0x00, 137 // number of inputs 138 0x00, 0x00, 0x00, 0x02, 139 // inputs[0] 140 // TxID 141 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 142 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 143 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 144 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 145 // Tx output index 146 0x00, 0x00, 0x00, 0x01, 147 // Mainnet AVAX assetID 148 0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a, 149 0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78, 150 0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf, 151 0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff, 152 // secp256k1fx transfer input type ID 153 0x00, 0x00, 0x00, 0x05, 154 // input amount = 10 AVAX 155 0x00, 0x00, 0x00, 0x02, 0x54, 0x0b, 0xe4, 0x00, 156 // number of signatures needed in input 157 0x00, 0x00, 0x00, 0x01, 158 // index of signer 159 0x00, 0x00, 0x00, 0x05, 160 // inputs[1] 161 // TxID 162 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 163 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 164 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 165 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 166 // Tx output index 167 0x00, 0x00, 0x00, 0x02, 168 // custom asset ID 169 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 170 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 171 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 172 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 173 // secp256k1fx transfer input type ID 174 0x00, 0x00, 0x00, 0x05, 175 // input amount 176 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 177 // number of signatures needed in input 178 0x00, 0x00, 0x00, 0x01, 179 // index of signer 180 0x00, 0x00, 0x00, 0x00, 181 // length of memo 182 0x00, 0x00, 0x00, 0x00, 183 // subnetID being transformed 184 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 185 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 186 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 187 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 188 // staking asset ID 189 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 190 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 191 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 192 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 193 // initial supply 194 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 195 // maximum supply 196 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 197 // minimum consumption rate 198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8, 199 // maximum consumption rate 200 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 201 // minimum staking amount 202 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 203 // maximum staking amount 204 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 205 // minimum staking duration 206 0x00, 0x00, 0x00, 0x01, 207 // maximum staking duration 208 0x01, 0xe1, 0x33, 0x80, 209 // minimum delegation fee 210 0x00, 0x0f, 0x42, 0x40, 211 // minimum delegation amount 212 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 213 // maximum validator weight factor 214 0x01, 215 // uptime requirement 216 0x00, 0x0e, 0x7e, 0xf0, 217 // secp256k1fx authorization type ID 218 0x00, 0x00, 0x00, 0x0a, 219 // number of signatures needed in authorization 220 0x00, 0x00, 0x00, 0x01, 221 // authorization signfature index 222 0x00, 0x00, 0x00, 0x03, 223 } 224 var unsignedSimpleTransformTx UnsignedTx = simpleTransformTx 225 unsignedSimpleTransformTxBytes, err := Codec.Marshal(CodecVersion, &unsignedSimpleTransformTx) 226 require.NoError(err) 227 require.Equal(expectedUnsignedSimpleTransformTxBytes, unsignedSimpleTransformTxBytes) 228 229 complexTransformTx := &TransformSubnetTx{ 230 BaseTx: BaseTx{ 231 BaseTx: avax.BaseTx{ 232 NetworkID: constants.MainnetID, 233 BlockchainID: constants.PlatformChainID, 234 Outs: []*avax.TransferableOutput{ 235 { 236 Asset: avax.Asset{ 237 ID: avaxAssetID, 238 }, 239 Out: &stakeable.LockOut{ 240 Locktime: 87654321, 241 TransferableOut: &secp256k1fx.TransferOutput{ 242 Amt: 1, 243 OutputOwners: secp256k1fx.OutputOwners{ 244 Locktime: 12345678, 245 Threshold: 0, 246 Addrs: []ids.ShortID{}, 247 }, 248 }, 249 }, 250 }, 251 { 252 Asset: avax.Asset{ 253 ID: customAssetID, 254 }, 255 Out: &stakeable.LockOut{ 256 Locktime: 876543210, 257 TransferableOut: &secp256k1fx.TransferOutput{ 258 Amt: 0xffffffffffffffff, 259 OutputOwners: secp256k1fx.OutputOwners{ 260 Locktime: 0, 261 Threshold: 1, 262 Addrs: []ids.ShortID{ 263 addr, 264 }, 265 }, 266 }, 267 }, 268 }, 269 }, 270 Ins: []*avax.TransferableInput{ 271 { 272 UTXOID: avax.UTXOID{ 273 TxID: txID, 274 OutputIndex: 1, 275 }, 276 Asset: avax.Asset{ 277 ID: avaxAssetID, 278 }, 279 In: &secp256k1fx.TransferInput{ 280 Amt: units.KiloAvax, 281 Input: secp256k1fx.Input{ 282 SigIndices: []uint32{2, 5}, 283 }, 284 }, 285 }, 286 { 287 UTXOID: avax.UTXOID{ 288 TxID: txID, 289 OutputIndex: 2, 290 }, 291 Asset: avax.Asset{ 292 ID: customAssetID, 293 }, 294 In: &stakeable.LockIn{ 295 Locktime: 876543210, 296 TransferableIn: &secp256k1fx.TransferInput{ 297 Amt: 0xefffffffffffffff, 298 Input: secp256k1fx.Input{ 299 SigIndices: []uint32{0}, 300 }, 301 }, 302 }, 303 }, 304 { 305 UTXOID: avax.UTXOID{ 306 TxID: txID, 307 OutputIndex: 3, 308 }, 309 Asset: avax.Asset{ 310 ID: customAssetID, 311 }, 312 In: &secp256k1fx.TransferInput{ 313 Amt: 0x1000000000000000, 314 Input: secp256k1fx.Input{ 315 SigIndices: []uint32{}, 316 }, 317 }, 318 }, 319 }, 320 Memo: types.JSONByteSlice("😅\nwell that's\x01\x23\x45!"), 321 }, 322 }, 323 Subnet: subnetID, 324 AssetID: customAssetID, 325 InitialSupply: 0x1000000000000000, 326 MaximumSupply: 0x1000000000000000, 327 MinConsumptionRate: 0, 328 MaxConsumptionRate: 0, 329 MinValidatorStake: 1, 330 MaxValidatorStake: 0x1000000000000000, 331 MinStakeDuration: 1, 332 MaxStakeDuration: 1, 333 MinDelegationFee: 0, 334 MinDelegatorStake: 0xffffffffffffffff, 335 MaxValidatorWeightFactor: 255, 336 UptimeRequirement: 0, 337 SubnetAuth: &secp256k1fx.Input{ 338 SigIndices: []uint32{}, 339 }, 340 } 341 avax.SortTransferableOutputs(complexTransformTx.Outs, Codec) 342 utils.Sort(complexTransformTx.Ins) 343 require.NoError(complexTransformTx.SyntacticVerify(&snow.Context{ 344 NetworkID: 1, 345 ChainID: constants.PlatformChainID, 346 AVAXAssetID: avaxAssetID, 347 })) 348 349 expectedUnsignedComplexTransformTxBytes := []byte{ 350 // Codec version 351 0x00, 0x00, 352 // TransformSubnetTx type ID 353 0x00, 0x00, 0x00, 0x18, 354 // Mainnet network ID 355 0x00, 0x00, 0x00, 0x01, 356 // P-chain blockchain ID 357 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 358 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 359 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 360 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 361 // number of outputs 362 0x00, 0x00, 0x00, 0x02, 363 // outputs[0] 364 // Mainnet AVAX asset ID 365 0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a, 366 0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78, 367 0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf, 368 0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff, 369 // Stakeable locked output type ID 370 0x00, 0x00, 0x00, 0x16, 371 // Locktime 372 0x00, 0x00, 0x00, 0x00, 0x05, 0x39, 0x7f, 0xb1, 373 // seck256k1fx tranfer output type ID 374 0x00, 0x00, 0x00, 0x07, 375 // amount 376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 377 // secp256k1fx locktime 378 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x61, 0x4e, 379 // threshold 380 0x00, 0x00, 0x00, 0x00, 381 // number of addresses 382 0x00, 0x00, 0x00, 0x00, 383 // outputs[1] 384 // custom assest ID 385 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 386 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 387 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 388 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 389 // Stakeable locked output type ID 390 0x00, 0x00, 0x00, 0x16, 391 // Locktime 392 0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea, 393 // seck256k1fx tranfer output type ID 394 0x00, 0x00, 0x00, 0x07, 395 // amount 396 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 397 // secp256k1fx locktime 398 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 399 // threshold 400 0x00, 0x00, 0x00, 0x01, 401 // number of addresses 402 0x00, 0x00, 0x00, 0x01, 403 // address[0] 404 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 405 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 406 0x44, 0x55, 0x66, 0x77, 407 // number of inputs 408 0x00, 0x00, 0x00, 0x03, 409 // inputs[0] 410 // TxID 411 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 412 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 413 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 414 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 415 // Tx output index 416 0x00, 0x00, 0x00, 0x01, 417 // Mainnet AVAX asset ID 418 0x21, 0xe6, 0x73, 0x17, 0xcb, 0xc4, 0xbe, 0x2a, 419 0xeb, 0x00, 0x67, 0x7a, 0xd6, 0x46, 0x27, 0x78, 420 0xa8, 0xf5, 0x22, 0x74, 0xb9, 0xd6, 0x05, 0xdf, 421 0x25, 0x91, 0xb2, 0x30, 0x27, 0xa8, 0x7d, 0xff, 422 // secp256k1fx transfer input type ID 423 0x00, 0x00, 0x00, 0x05, 424 // amount = 1,000 AVAX 425 0x00, 0x00, 0x00, 0xe8, 0xd4, 0xa5, 0x10, 0x00, 426 // number of signatures indices 427 0x00, 0x00, 0x00, 0x02, 428 // first signature index 429 0x00, 0x00, 0x00, 0x02, 430 // second signature index 431 0x00, 0x00, 0x00, 0x05, 432 // inputs[1] 433 // TxID 434 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 435 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 436 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 437 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 438 // Tx output index 439 0x00, 0x00, 0x00, 0x02, 440 // custom asset ID 441 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 442 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 443 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 444 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 445 // stakeable locked input type ID 446 0x00, 0x00, 0x00, 0x15, 447 // locktime 448 0x00, 0x00, 0x00, 0x00, 0x34, 0x3e, 0xfc, 0xea, 449 // secp256k1fx transfer input type ID 450 0x00, 0x00, 0x00, 0x05, 451 // input amount 452 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 453 // number of signatures needed in input 454 0x00, 0x00, 0x00, 0x01, 455 // index of signer 456 0x00, 0x00, 0x00, 0x00, 457 // inputs[2] 458 // TxID 459 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 460 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 461 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 462 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 463 // Tx output index 464 0x00, 0x00, 0x00, 0x03, 465 // custom asset ID 466 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 467 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 468 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 469 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 470 // secp256k1fx transfer input type ID 471 0x00, 0x00, 0x00, 0x05, 472 // input amount 473 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 474 // number of signatures needed in input 475 0x00, 0x00, 0x00, 0x00, 476 // memo length 477 0x00, 0x00, 0x00, 0x14, 478 // memo 479 0xf0, 0x9f, 0x98, 0x85, 0x0a, 0x77, 0x65, 0x6c, 480 0x6c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x27, 0x73, 481 0x01, 0x23, 0x45, 0x21, 482 // subnetID being transformed 483 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 484 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 485 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 486 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 487 // staking asset ID 488 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 489 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 490 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 491 0x99, 0x77, 0x55, 0x77, 0x11, 0x33, 0x55, 0x31, 492 // initial supply 493 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 494 // maximum supply 495 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 496 // minimum consumption rate 497 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 498 // maximum consumption rate 499 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 500 // minimum staking amount 501 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 502 // maximum staking amount 503 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 504 // minimum staking duration 505 0x00, 0x00, 0x00, 0x01, 506 // maximum staking duration 507 0x00, 0x00, 0x00, 0x01, 508 // minimum delegation fee 509 0x00, 0x00, 0x00, 0x00, 510 // minimum delegation amount 511 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 512 // maximum validator weight factor 513 0xff, 514 // uptime requirement 515 0x00, 0x00, 0x00, 0x00, 516 // secp256k1fx authorization type ID 517 0x00, 0x00, 0x00, 0x0a, 518 // number of signatures needed in authorization 519 0x00, 0x00, 0x00, 0x00, 520 } 521 var unsignedComplexTransformTx UnsignedTx = complexTransformTx 522 unsignedComplexTransformTxBytes, err := Codec.Marshal(CodecVersion, &unsignedComplexTransformTx) 523 require.NoError(err) 524 require.Equal(expectedUnsignedComplexTransformTxBytes, unsignedComplexTransformTxBytes) 525 526 aliaser := ids.NewAliaser() 527 require.NoError(aliaser.Alias(constants.PlatformChainID, "P")) 528 529 unsignedComplexTransformTx.InitCtx(&snow.Context{ 530 NetworkID: 1, 531 ChainID: constants.PlatformChainID, 532 AVAXAssetID: avaxAssetID, 533 BCLookup: aliaser, 534 }) 535 536 unsignedComplexTransformTxJSONBytes, err := json.MarshalIndent(unsignedComplexTransformTx, "", "\t") 537 require.NoError(err) 538 require.Equal(`{ 539 "networkID": 1, 540 "blockchainID": "11111111111111111111111111111111LpoYY", 541 "outputs": [ 542 { 543 "assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", 544 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 545 "output": { 546 "locktime": 87654321, 547 "output": { 548 "addresses": [], 549 "amount": 1, 550 "locktime": 12345678, 551 "threshold": 0 552 } 553 } 554 }, 555 { 556 "assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc", 557 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 558 "output": { 559 "locktime": 876543210, 560 "output": { 561 "addresses": [ 562 "P-metal1g32kvaugnx4tk3z4vemc3xd2hdz92enhqaj6ex" 563 ], 564 "amount": 18446744073709551615, 565 "locktime": 0, 566 "threshold": 1 567 } 568 } 569 } 570 ], 571 "inputs": [ 572 { 573 "txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN", 574 "outputIndex": 1, 575 "assetID": "FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z", 576 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 577 "input": { 578 "amount": 1000000000000, 579 "signatureIndices": [ 580 2, 581 5 582 ] 583 } 584 }, 585 { 586 "txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN", 587 "outputIndex": 2, 588 "assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc", 589 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 590 "input": { 591 "locktime": 876543210, 592 "input": { 593 "amount": 17293822569102704639, 594 "signatureIndices": [ 595 0 596 ] 597 } 598 } 599 }, 600 { 601 "txID": "2wiU5PnFTjTmoAXGZutHAsPF36qGGyLHYHj9G1Aucfmb3JFFGN", 602 "outputIndex": 3, 603 "assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc", 604 "fxID": "spdxUxVJQbX85MGxMHbKw1sHxMnSqJ3QBzDyDYEP3h6TLuxqQ", 605 "input": { 606 "amount": 1152921504606846976, 607 "signatureIndices": [] 608 } 609 } 610 ], 611 "memo": "0xf09f98850a77656c6c2074686174277301234521", 612 "subnetID": "SkB92YpWm4UpburLz9tEKZw2i67H3FF6YkjaU4BkFUDTG9Xm", 613 "assetID": "2Ab62uWwJw1T6VvmKD36ufsiuGZuX1pGykXAvPX1LtjTRHxwcc", 614 "initialSupply": 1152921504606846976, 615 "maximumSupply": 1152921504606846976, 616 "minConsumptionRate": 0, 617 "maxConsumptionRate": 0, 618 "minValidatorStake": 1, 619 "maxValidatorStake": 1152921504606846976, 620 "minStakeDuration": 1, 621 "maxStakeDuration": 1, 622 "minDelegationFee": 0, 623 "minDelegatorStake": 18446744073709551615, 624 "maxValidatorWeightFactor": 255, 625 "uptimeRequirement": 0, 626 "subnetAuthorization": { 627 "signatureIndices": [] 628 } 629 }`, string(unsignedComplexTransformTxJSONBytes)) 630 } 631 632 func TestTransformSubnetTxSyntacticVerify(t *testing.T) { 633 type test struct { 634 name string 635 txFunc func(*gomock.Controller) *TransformSubnetTx 636 err error 637 } 638 639 var ( 640 networkID = uint32(1337) 641 chainID = ids.GenerateTestID() 642 ) 643 644 ctx := &snow.Context{ 645 ChainID: chainID, 646 NetworkID: networkID, 647 AVAXAssetID: ids.GenerateTestID(), 648 } 649 650 // A BaseTx that already passed syntactic verification. 651 verifiedBaseTx := BaseTx{ 652 SyntacticallyVerified: true, 653 } 654 655 // A BaseTx that passes syntactic verification. 656 validBaseTx := BaseTx{ 657 BaseTx: avax.BaseTx{ 658 NetworkID: networkID, 659 BlockchainID: chainID, 660 }, 661 } 662 663 // A BaseTx that fails syntactic verification. 664 invalidBaseTx := BaseTx{} 665 666 tests := []test{ 667 { 668 name: "nil tx", 669 txFunc: func(*gomock.Controller) *TransformSubnetTx { 670 return nil 671 }, 672 err: ErrNilTx, 673 }, 674 { 675 name: "already verified", 676 txFunc: func(*gomock.Controller) *TransformSubnetTx { 677 return &TransformSubnetTx{ 678 BaseTx: verifiedBaseTx, 679 } 680 }, 681 err: nil, 682 }, 683 { 684 name: "invalid subnetID", 685 txFunc: func(*gomock.Controller) *TransformSubnetTx { 686 return &TransformSubnetTx{ 687 BaseTx: validBaseTx, 688 Subnet: constants.PrimaryNetworkID, 689 } 690 }, 691 err: errCantTransformPrimaryNetwork, 692 }, 693 { 694 name: "empty assetID", 695 txFunc: func(*gomock.Controller) *TransformSubnetTx { 696 return &TransformSubnetTx{ 697 BaseTx: validBaseTx, 698 Subnet: ids.GenerateTestID(), 699 AssetID: ids.Empty, 700 } 701 }, 702 err: errEmptyAssetID, 703 }, 704 { 705 name: "AVAX assetID", 706 txFunc: func(*gomock.Controller) *TransformSubnetTx { 707 return &TransformSubnetTx{ 708 BaseTx: validBaseTx, 709 Subnet: ids.GenerateTestID(), 710 AssetID: ctx.AVAXAssetID, 711 } 712 }, 713 err: errAssetIDCantBeAVAX, 714 }, 715 { 716 name: "initialSupply == 0", 717 txFunc: func(*gomock.Controller) *TransformSubnetTx { 718 return &TransformSubnetTx{ 719 BaseTx: validBaseTx, 720 Subnet: ids.GenerateTestID(), 721 AssetID: ids.GenerateTestID(), 722 InitialSupply: 0, 723 } 724 }, 725 err: errInitialSupplyZero, 726 }, 727 { 728 name: "initialSupply > maximumSupply", 729 txFunc: func(*gomock.Controller) *TransformSubnetTx { 730 return &TransformSubnetTx{ 731 BaseTx: validBaseTx, 732 Subnet: ids.GenerateTestID(), 733 AssetID: ids.GenerateTestID(), 734 InitialSupply: 2, 735 MaximumSupply: 1, 736 } 737 }, 738 err: errInitialSupplyGreaterThanMaxSupply, 739 }, 740 { 741 name: "minConsumptionRate > maxConsumptionRate", 742 txFunc: func(*gomock.Controller) *TransformSubnetTx { 743 return &TransformSubnetTx{ 744 BaseTx: validBaseTx, 745 Subnet: ids.GenerateTestID(), 746 AssetID: ids.GenerateTestID(), 747 InitialSupply: 1, 748 MaximumSupply: 1, 749 MinConsumptionRate: 2, 750 MaxConsumptionRate: 1, 751 } 752 }, 753 err: errMinConsumptionRateTooLarge, 754 }, 755 { 756 name: "maxConsumptionRate > 100%", 757 txFunc: func(*gomock.Controller) *TransformSubnetTx { 758 return &TransformSubnetTx{ 759 BaseTx: validBaseTx, 760 Subnet: ids.GenerateTestID(), 761 AssetID: ids.GenerateTestID(), 762 InitialSupply: 1, 763 MaximumSupply: 1, 764 MinConsumptionRate: 0, 765 MaxConsumptionRate: reward.PercentDenominator + 1, 766 } 767 }, 768 err: errMaxConsumptionRateTooLarge, 769 }, 770 { 771 name: "minValidatorStake == 0", 772 txFunc: func(*gomock.Controller) *TransformSubnetTx { 773 return &TransformSubnetTx{ 774 BaseTx: validBaseTx, 775 Subnet: ids.GenerateTestID(), 776 AssetID: ids.GenerateTestID(), 777 InitialSupply: 1, 778 MaximumSupply: 1, 779 MinConsumptionRate: 0, 780 MaxConsumptionRate: reward.PercentDenominator, 781 MinValidatorStake: 0, 782 } 783 }, 784 err: errMinValidatorStakeZero, 785 }, 786 { 787 name: "minValidatorStake > initialSupply", 788 txFunc: func(*gomock.Controller) *TransformSubnetTx { 789 return &TransformSubnetTx{ 790 BaseTx: validBaseTx, 791 Subnet: ids.GenerateTestID(), 792 AssetID: ids.GenerateTestID(), 793 InitialSupply: 1, 794 MaximumSupply: 1, 795 MinConsumptionRate: 0, 796 MaxConsumptionRate: reward.PercentDenominator, 797 MinValidatorStake: 2, 798 } 799 }, 800 err: errMinValidatorStakeAboveSupply, 801 }, 802 { 803 name: "minValidatorStake > maxValidatorStake", 804 txFunc: func(*gomock.Controller) *TransformSubnetTx { 805 return &TransformSubnetTx{ 806 BaseTx: validBaseTx, 807 Subnet: ids.GenerateTestID(), 808 AssetID: ids.GenerateTestID(), 809 InitialSupply: 10, 810 MaximumSupply: 10, 811 MinConsumptionRate: 0, 812 MaxConsumptionRate: reward.PercentDenominator, 813 MinValidatorStake: 2, 814 MaxValidatorStake: 1, 815 } 816 }, 817 err: errMinValidatorStakeAboveMax, 818 }, 819 { 820 name: "maxValidatorStake > maximumSupply", 821 txFunc: func(*gomock.Controller) *TransformSubnetTx { 822 return &TransformSubnetTx{ 823 BaseTx: validBaseTx, 824 Subnet: ids.GenerateTestID(), 825 AssetID: ids.GenerateTestID(), 826 InitialSupply: 10, 827 MaximumSupply: 10, 828 MinConsumptionRate: 0, 829 MaxConsumptionRate: reward.PercentDenominator, 830 MinValidatorStake: 2, 831 MaxValidatorStake: 11, 832 } 833 }, 834 err: errMaxValidatorStakeTooLarge, 835 }, 836 { 837 name: "minStakeDuration == 0", 838 txFunc: func(*gomock.Controller) *TransformSubnetTx { 839 return &TransformSubnetTx{ 840 BaseTx: validBaseTx, 841 Subnet: ids.GenerateTestID(), 842 AssetID: ids.GenerateTestID(), 843 InitialSupply: 10, 844 MaximumSupply: 10, 845 MinConsumptionRate: 0, 846 MaxConsumptionRate: reward.PercentDenominator, 847 MinValidatorStake: 2, 848 MaxValidatorStake: 10, 849 MinStakeDuration: 0, 850 } 851 }, 852 err: errMinStakeDurationZero, 853 }, 854 { 855 name: "minStakeDuration > maxStakeDuration", 856 txFunc: func(*gomock.Controller) *TransformSubnetTx { 857 return &TransformSubnetTx{ 858 BaseTx: validBaseTx, 859 Subnet: ids.GenerateTestID(), 860 AssetID: ids.GenerateTestID(), 861 InitialSupply: 10, 862 MaximumSupply: 10, 863 MinConsumptionRate: 0, 864 MaxConsumptionRate: reward.PercentDenominator, 865 MinValidatorStake: 2, 866 MaxValidatorStake: 10, 867 MinStakeDuration: 2, 868 MaxStakeDuration: 1, 869 } 870 }, 871 err: errMinStakeDurationTooLarge, 872 }, 873 { 874 name: "minDelegationFee > 100%", 875 txFunc: func(*gomock.Controller) *TransformSubnetTx { 876 return &TransformSubnetTx{ 877 BaseTx: validBaseTx, 878 Subnet: ids.GenerateTestID(), 879 AssetID: ids.GenerateTestID(), 880 InitialSupply: 10, 881 MaximumSupply: 10, 882 MinConsumptionRate: 0, 883 MaxConsumptionRate: reward.PercentDenominator, 884 MinValidatorStake: 2, 885 MaxValidatorStake: 10, 886 MinStakeDuration: 1, 887 MaxStakeDuration: 2, 888 MinDelegationFee: reward.PercentDenominator + 1, 889 } 890 }, 891 err: errMinDelegationFeeTooLarge, 892 }, 893 { 894 name: "minDelegatorStake == 0", 895 txFunc: func(*gomock.Controller) *TransformSubnetTx { 896 return &TransformSubnetTx{ 897 BaseTx: validBaseTx, 898 Subnet: ids.GenerateTestID(), 899 AssetID: ids.GenerateTestID(), 900 InitialSupply: 10, 901 MaximumSupply: 10, 902 MinConsumptionRate: 0, 903 MaxConsumptionRate: reward.PercentDenominator, 904 MinValidatorStake: 2, 905 MaxValidatorStake: 10, 906 MinStakeDuration: 1, 907 MaxStakeDuration: 2, 908 MinDelegationFee: reward.PercentDenominator, 909 MinDelegatorStake: 0, 910 } 911 }, 912 err: errMinDelegatorStakeZero, 913 }, 914 { 915 name: "maxValidatorWeightFactor == 0", 916 txFunc: func(*gomock.Controller) *TransformSubnetTx { 917 return &TransformSubnetTx{ 918 BaseTx: validBaseTx, 919 Subnet: ids.GenerateTestID(), 920 AssetID: ids.GenerateTestID(), 921 InitialSupply: 10, 922 MaximumSupply: 10, 923 MinConsumptionRate: 0, 924 MaxConsumptionRate: reward.PercentDenominator, 925 MinValidatorStake: 2, 926 MaxValidatorStake: 10, 927 MinStakeDuration: 1, 928 MaxStakeDuration: 2, 929 MinDelegationFee: reward.PercentDenominator, 930 MinDelegatorStake: 1, 931 MaxValidatorWeightFactor: 0, 932 } 933 }, 934 err: errMaxValidatorWeightFactorZero, 935 }, 936 { 937 name: "uptimeRequirement > 100%", 938 txFunc: func(*gomock.Controller) *TransformSubnetTx { 939 return &TransformSubnetTx{ 940 BaseTx: validBaseTx, 941 Subnet: ids.GenerateTestID(), 942 AssetID: ids.GenerateTestID(), 943 InitialSupply: 10, 944 MaximumSupply: 10, 945 MinConsumptionRate: 0, 946 MaxConsumptionRate: reward.PercentDenominator, 947 MinValidatorStake: 2, 948 MaxValidatorStake: 10, 949 MinStakeDuration: 1, 950 MaxStakeDuration: 2, 951 MinDelegationFee: reward.PercentDenominator, 952 MinDelegatorStake: 1, 953 MaxValidatorWeightFactor: 1, 954 UptimeRequirement: reward.PercentDenominator + 1, 955 } 956 }, 957 err: errUptimeRequirementTooLarge, 958 }, 959 { 960 name: "invalid subnetAuth", 961 txFunc: func(ctrl *gomock.Controller) *TransformSubnetTx { 962 // This SubnetAuth fails verification. 963 invalidSubnetAuth := verify.NewMockVerifiable(ctrl) 964 invalidSubnetAuth.EXPECT().Verify().Return(errInvalidSubnetAuth) 965 return &TransformSubnetTx{ 966 BaseTx: validBaseTx, 967 Subnet: ids.GenerateTestID(), 968 AssetID: ids.GenerateTestID(), 969 InitialSupply: 10, 970 MaximumSupply: 10, 971 MinConsumptionRate: 0, 972 MaxConsumptionRate: reward.PercentDenominator, 973 MinValidatorStake: 2, 974 MaxValidatorStake: 10, 975 MinStakeDuration: 1, 976 MaxStakeDuration: 2, 977 MinDelegationFee: reward.PercentDenominator, 978 MinDelegatorStake: 1, 979 MaxValidatorWeightFactor: 1, 980 UptimeRequirement: reward.PercentDenominator, 981 SubnetAuth: invalidSubnetAuth, 982 } 983 }, 984 err: errInvalidSubnetAuth, 985 }, 986 { 987 name: "invalid BaseTx", 988 txFunc: func(*gomock.Controller) *TransformSubnetTx { 989 return &TransformSubnetTx{ 990 BaseTx: invalidBaseTx, 991 Subnet: ids.GenerateTestID(), 992 AssetID: ids.GenerateTestID(), 993 InitialSupply: 10, 994 MaximumSupply: 10, 995 MinConsumptionRate: 0, 996 MaxConsumptionRate: reward.PercentDenominator, 997 MinValidatorStake: 2, 998 MaxValidatorStake: 10, 999 MinStakeDuration: 1, 1000 MaxStakeDuration: 2, 1001 MinDelegationFee: reward.PercentDenominator, 1002 MinDelegatorStake: 1, 1003 MaxValidatorWeightFactor: 1, 1004 UptimeRequirement: reward.PercentDenominator, 1005 } 1006 }, 1007 err: avax.ErrWrongNetworkID, 1008 }, 1009 { 1010 name: "passes verification", 1011 txFunc: func(ctrl *gomock.Controller) *TransformSubnetTx { 1012 // This SubnetAuth passes verification. 1013 validSubnetAuth := verify.NewMockVerifiable(ctrl) 1014 validSubnetAuth.EXPECT().Verify().Return(nil) 1015 return &TransformSubnetTx{ 1016 BaseTx: validBaseTx, 1017 Subnet: ids.GenerateTestID(), 1018 AssetID: ids.GenerateTestID(), 1019 InitialSupply: 10, 1020 MaximumSupply: 10, 1021 MinConsumptionRate: 0, 1022 MaxConsumptionRate: reward.PercentDenominator, 1023 MinValidatorStake: 2, 1024 MaxValidatorStake: 10, 1025 MinStakeDuration: 1, 1026 MaxStakeDuration: 2, 1027 MinDelegationFee: reward.PercentDenominator, 1028 MinDelegatorStake: 1, 1029 MaxValidatorWeightFactor: 1, 1030 UptimeRequirement: reward.PercentDenominator, 1031 SubnetAuth: validSubnetAuth, 1032 } 1033 }, 1034 err: nil, 1035 }, 1036 } 1037 1038 for _, tt := range tests { 1039 t.Run(tt.name, func(t *testing.T) { 1040 ctrl := gomock.NewController(t) 1041 1042 tx := tt.txFunc(ctrl) 1043 err := tx.SyntacticVerify(ctx) 1044 require.ErrorIs(t, err, tt.err) 1045 }) 1046 } 1047 }