github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/block/parse_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 block 5 6 import ( 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/require" 11 12 "github.com/MetalBlockchain/metalgo/codec" 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1" 15 "github.com/MetalBlockchain/metalgo/vms/components/avax" 16 "github.com/MetalBlockchain/metalgo/vms/platformvm/txs" 17 "github.com/MetalBlockchain/metalgo/vms/secp256k1fx" 18 ) 19 20 var preFundedKeys = secp256k1.TestKeys() 21 22 func TestStandardBlocks(t *testing.T) { 23 // check Apricot standard block can be built and parsed 24 require := require.New(t) 25 blkTimestamp := time.Now() 26 parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'} 27 height := uint64(2022) 28 decisionTxs, err := testDecisionTxs() 29 require.NoError(err) 30 31 for _, cdc := range []codec.Manager{Codec, GenesisCodec} { 32 // build block 33 apricotStandardBlk, err := NewApricotStandardBlock(parentID, height, decisionTxs) 34 require.NoError(err) 35 36 // parse block 37 parsed, err := Parse(cdc, apricotStandardBlk.Bytes()) 38 require.NoError(err) 39 40 // compare content 41 require.Equal(apricotStandardBlk.ID(), parsed.ID()) 42 require.Equal(apricotStandardBlk.Bytes(), parsed.Bytes()) 43 require.Equal(apricotStandardBlk.Parent(), parsed.Parent()) 44 require.Equal(apricotStandardBlk.Height(), parsed.Height()) 45 46 require.IsType(&ApricotStandardBlock{}, parsed) 47 require.Equal(decisionTxs, parsed.Txs()) 48 49 // check that banff standard block can be built and parsed 50 banffStandardBlk, err := NewBanffStandardBlock(blkTimestamp, parentID, height, decisionTxs) 51 require.NoError(err) 52 53 // parse block 54 parsed, err = Parse(cdc, banffStandardBlk.Bytes()) 55 require.NoError(err) 56 57 // compare content 58 require.Equal(banffStandardBlk.ID(), parsed.ID()) 59 require.Equal(banffStandardBlk.Bytes(), parsed.Bytes()) 60 require.Equal(banffStandardBlk.Parent(), parsed.Parent()) 61 require.Equal(banffStandardBlk.Height(), parsed.Height()) 62 require.IsType(&BanffStandardBlock{}, parsed) 63 parsedBanffStandardBlk := parsed.(*BanffStandardBlock) 64 require.Equal(decisionTxs, parsedBanffStandardBlk.Txs()) 65 66 // timestamp check for banff blocks only 67 require.Equal(banffStandardBlk.Timestamp(), parsedBanffStandardBlk.Timestamp()) 68 69 // backward compatibility check 70 require.Equal(parsed.Txs(), parsedBanffStandardBlk.Txs()) 71 } 72 } 73 74 func TestProposalBlocks(t *testing.T) { 75 // check Apricot proposal block can be built and parsed 76 require := require.New(t) 77 blkTimestamp := time.Now() 78 parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'} 79 height := uint64(2022) 80 proposalTx, err := testProposalTx() 81 require.NoError(err) 82 decisionTxs, err := testDecisionTxs() 83 require.NoError(err) 84 85 for _, cdc := range []codec.Manager{Codec, GenesisCodec} { 86 // build block 87 apricotProposalBlk, err := NewApricotProposalBlock( 88 parentID, 89 height, 90 proposalTx, 91 ) 92 require.NoError(err) 93 94 // parse block 95 parsed, err := Parse(cdc, apricotProposalBlk.Bytes()) 96 require.NoError(err) 97 98 // compare content 99 require.Equal(apricotProposalBlk.ID(), parsed.ID()) 100 require.Equal(apricotProposalBlk.Bytes(), parsed.Bytes()) 101 require.Equal(apricotProposalBlk.Parent(), parsed.Parent()) 102 require.Equal(apricotProposalBlk.Height(), parsed.Height()) 103 104 require.IsType(&ApricotProposalBlock{}, parsed) 105 parsedApricotProposalBlk := parsed.(*ApricotProposalBlock) 106 require.Equal([]*txs.Tx{proposalTx}, parsedApricotProposalBlk.Txs()) 107 108 // check that banff proposal block can be built and parsed 109 banffProposalBlk, err := NewBanffProposalBlock( 110 blkTimestamp, 111 parentID, 112 height, 113 proposalTx, 114 []*txs.Tx{}, 115 ) 116 require.NoError(err) 117 118 // parse block 119 parsed, err = Parse(cdc, banffProposalBlk.Bytes()) 120 require.NoError(err) 121 122 // compare content 123 require.Equal(banffProposalBlk.ID(), parsed.ID()) 124 require.Equal(banffProposalBlk.Bytes(), parsed.Bytes()) 125 require.Equal(banffProposalBlk.Parent(), parsed.Parent()) 126 require.Equal(banffProposalBlk.Height(), parsed.Height()) 127 require.IsType(&BanffProposalBlock{}, parsed) 128 parsedBanffProposalBlk := parsed.(*BanffProposalBlock) 129 require.Equal([]*txs.Tx{proposalTx}, parsedBanffProposalBlk.Txs()) 130 131 // timestamp check for banff blocks only 132 require.Equal(banffProposalBlk.Timestamp(), parsedBanffProposalBlk.Timestamp()) 133 134 // backward compatibility check 135 require.Equal(parsedApricotProposalBlk.Txs(), parsedBanffProposalBlk.Txs()) 136 137 // check that banff proposal block with decisionTxs can be built and parsed 138 banffProposalBlkWithDecisionTxs, err := NewBanffProposalBlock( 139 blkTimestamp, 140 parentID, 141 height, 142 proposalTx, 143 decisionTxs, 144 ) 145 require.NoError(err) 146 147 // parse block 148 parsed, err = Parse(cdc, banffProposalBlkWithDecisionTxs.Bytes()) 149 require.NoError(err) 150 151 // compare content 152 require.Equal(banffProposalBlkWithDecisionTxs.ID(), parsed.ID()) 153 require.Equal(banffProposalBlkWithDecisionTxs.Bytes(), parsed.Bytes()) 154 require.Equal(banffProposalBlkWithDecisionTxs.Parent(), parsed.Parent()) 155 require.Equal(banffProposalBlkWithDecisionTxs.Height(), parsed.Height()) 156 require.IsType(&BanffProposalBlock{}, parsed) 157 parsedBanffProposalBlkWithDecisionTxs := parsed.(*BanffProposalBlock) 158 159 l := len(decisionTxs) 160 expectedTxs := make([]*txs.Tx, l+1) 161 copy(expectedTxs, decisionTxs) 162 expectedTxs[l] = proposalTx 163 require.Equal(expectedTxs, parsedBanffProposalBlkWithDecisionTxs.Txs()) 164 165 require.Equal(banffProposalBlkWithDecisionTxs.Timestamp(), parsedBanffProposalBlkWithDecisionTxs.Timestamp()) 166 } 167 } 168 169 func TestCommitBlock(t *testing.T) { 170 // check Apricot commit block can be built and parsed 171 require := require.New(t) 172 blkTimestamp := time.Now() 173 parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'} 174 height := uint64(2022) 175 176 for _, cdc := range []codec.Manager{Codec, GenesisCodec} { 177 // build block 178 apricotCommitBlk, err := NewApricotCommitBlock(parentID, height) 179 require.NoError(err) 180 181 // parse block 182 parsed, err := Parse(cdc, apricotCommitBlk.Bytes()) 183 require.NoError(err) 184 185 // compare content 186 require.Equal(apricotCommitBlk.ID(), parsed.ID()) 187 require.Equal(apricotCommitBlk.Bytes(), parsed.Bytes()) 188 require.Equal(apricotCommitBlk.Parent(), parsed.Parent()) 189 require.Equal(apricotCommitBlk.Height(), parsed.Height()) 190 191 // check that banff commit block can be built and parsed 192 banffCommitBlk, err := NewBanffCommitBlock(blkTimestamp, parentID, height) 193 require.NoError(err) 194 195 // parse block 196 parsed, err = Parse(cdc, banffCommitBlk.Bytes()) 197 require.NoError(err) 198 199 // compare content 200 require.Equal(banffCommitBlk.ID(), parsed.ID()) 201 require.Equal(banffCommitBlk.Bytes(), parsed.Bytes()) 202 require.Equal(banffCommitBlk.Parent(), parsed.Parent()) 203 require.Equal(banffCommitBlk.Height(), parsed.Height()) 204 205 // timestamp check for banff blocks only 206 require.IsType(&BanffCommitBlock{}, parsed) 207 parsedBanffCommitBlk := parsed.(*BanffCommitBlock) 208 require.Equal(banffCommitBlk.Timestamp(), parsedBanffCommitBlk.Timestamp()) 209 } 210 } 211 212 func TestAbortBlock(t *testing.T) { 213 // check Apricot abort block can be built and parsed 214 require := require.New(t) 215 blkTimestamp := time.Now() 216 parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'} 217 height := uint64(2022) 218 219 for _, cdc := range []codec.Manager{Codec, GenesisCodec} { 220 // build block 221 apricotAbortBlk, err := NewApricotAbortBlock(parentID, height) 222 require.NoError(err) 223 224 // parse block 225 parsed, err := Parse(cdc, apricotAbortBlk.Bytes()) 226 require.NoError(err) 227 228 // compare content 229 require.Equal(apricotAbortBlk.ID(), parsed.ID()) 230 require.Equal(apricotAbortBlk.Bytes(), parsed.Bytes()) 231 require.Equal(apricotAbortBlk.Parent(), parsed.Parent()) 232 require.Equal(apricotAbortBlk.Height(), parsed.Height()) 233 234 // check that banff abort block can be built and parsed 235 banffAbortBlk, err := NewBanffAbortBlock(blkTimestamp, parentID, height) 236 require.NoError(err) 237 238 // parse block 239 parsed, err = Parse(cdc, banffAbortBlk.Bytes()) 240 require.NoError(err) 241 242 // compare content 243 require.Equal(banffAbortBlk.ID(), parsed.ID()) 244 require.Equal(banffAbortBlk.Bytes(), parsed.Bytes()) 245 require.Equal(banffAbortBlk.Parent(), parsed.Parent()) 246 require.Equal(banffAbortBlk.Height(), parsed.Height()) 247 248 // timestamp check for banff blocks only 249 require.IsType(&BanffAbortBlock{}, parsed) 250 parsedBanffAbortBlk := parsed.(*BanffAbortBlock) 251 require.Equal(banffAbortBlk.Timestamp(), parsedBanffAbortBlk.Timestamp()) 252 } 253 } 254 255 func TestAtomicBlock(t *testing.T) { 256 // check atomic block can be built and parsed 257 require := require.New(t) 258 parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't', 'I', 'D'} 259 height := uint64(2022) 260 atomicTx, err := testAtomicTx() 261 require.NoError(err) 262 263 for _, cdc := range []codec.Manager{Codec, GenesisCodec} { 264 // build block 265 atomicBlk, err := NewApricotAtomicBlock( 266 parentID, 267 height, 268 atomicTx, 269 ) 270 require.NoError(err) 271 272 // parse block 273 parsed, err := Parse(cdc, atomicBlk.Bytes()) 274 require.NoError(err) 275 276 // compare content 277 require.Equal(atomicBlk.ID(), parsed.ID()) 278 require.Equal(atomicBlk.Bytes(), parsed.Bytes()) 279 require.Equal(atomicBlk.Parent(), parsed.Parent()) 280 require.Equal(atomicBlk.Height(), parsed.Height()) 281 282 require.IsType(&ApricotAtomicBlock{}, parsed) 283 parsedAtomicBlk := parsed.(*ApricotAtomicBlock) 284 require.Equal([]*txs.Tx{atomicTx}, parsedAtomicBlk.Txs()) 285 } 286 } 287 288 func testAtomicTx() (*txs.Tx, error) { 289 utx := &txs.ImportTx{ 290 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 291 NetworkID: 10, 292 BlockchainID: ids.ID{'c', 'h', 'a', 'i', 'n', 'I', 'D'}, 293 Outs: []*avax.TransferableOutput{{ 294 Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}}, 295 Out: &secp256k1fx.TransferOutput{ 296 Amt: uint64(1234), 297 OutputOwners: secp256k1fx.OutputOwners{ 298 Threshold: 1, 299 Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, 300 }, 301 }, 302 }}, 303 Ins: []*avax.TransferableInput{{ 304 UTXOID: avax.UTXOID{ 305 TxID: ids.ID{'t', 'x', 'I', 'D'}, 306 OutputIndex: 2, 307 }, 308 Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}}, 309 In: &secp256k1fx.TransferInput{ 310 Amt: uint64(5678), 311 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 312 }, 313 }}, 314 Memo: []byte{1, 2, 3, 4, 5, 6, 7, 8}, 315 }}, 316 SourceChain: ids.ID{'c', 'h', 'a', 'i', 'n'}, 317 ImportedInputs: []*avax.TransferableInput{{ 318 UTXOID: avax.UTXOID{ 319 TxID: ids.Empty.Prefix(1), 320 OutputIndex: 1, 321 }, 322 Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}}, 323 In: &secp256k1fx.TransferInput{ 324 Amt: 50000, 325 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 326 }, 327 }}, 328 } 329 signers := [][]*secp256k1.PrivateKey{{preFundedKeys[0]}} 330 return txs.NewSigned(utx, txs.Codec, signers) 331 } 332 333 func testDecisionTxs() ([]*txs.Tx, error) { 334 countTxs := 2 335 decisionTxs := make([]*txs.Tx, 0, countTxs) 336 for i := 0; i < countTxs; i++ { 337 // Create the tx 338 utx := &txs.CreateChainTx{ 339 BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{ 340 NetworkID: 10, 341 BlockchainID: ids.ID{'c', 'h', 'a', 'i', 'n', 'I', 'D'}, 342 Outs: []*avax.TransferableOutput{{ 343 Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}}, 344 Out: &secp256k1fx.TransferOutput{ 345 Amt: uint64(1234), 346 OutputOwners: secp256k1fx.OutputOwners{ 347 Threshold: 1, 348 Addrs: []ids.ShortID{preFundedKeys[0].PublicKey().Address()}, 349 }, 350 }, 351 }}, 352 Ins: []*avax.TransferableInput{{ 353 UTXOID: avax.UTXOID{ 354 TxID: ids.ID{'t', 'x', 'I', 'D'}, 355 OutputIndex: 2, 356 }, 357 Asset: avax.Asset{ID: ids.ID{'a', 's', 's', 'e', 'r', 't'}}, 358 In: &secp256k1fx.TransferInput{ 359 Amt: uint64(5678), 360 Input: secp256k1fx.Input{SigIndices: []uint32{0}}, 361 }, 362 }}, 363 Memo: []byte{1, 2, 3, 4, 5, 6, 7, 8}, 364 }}, 365 SubnetID: ids.ID{'s', 'u', 'b', 'n', 'e', 't', 'I', 'D'}, 366 ChainName: "a chain", 367 VMID: ids.GenerateTestID(), 368 FxIDs: []ids.ID{ids.GenerateTestID()}, 369 GenesisData: []byte{'g', 'e', 'n', 'D', 'a', 't', 'a'}, 370 SubnetAuth: &secp256k1fx.Input{SigIndices: []uint32{1}}, 371 } 372 373 signers := [][]*secp256k1.PrivateKey{{preFundedKeys[0]}} 374 tx, err := txs.NewSigned(utx, txs.Codec, signers) 375 if err != nil { 376 return nil, err 377 } 378 decisionTxs = append(decisionTxs, tx) 379 } 380 return decisionTxs, nil 381 } 382 383 func testProposalTx() (*txs.Tx, error) { 384 utx := &txs.RewardValidatorTx{ 385 TxID: ids.ID{'r', 'e', 'w', 'a', 'r', 'd', 'I', 'D'}, 386 } 387 388 signers := [][]*secp256k1.PrivateKey{{preFundedKeys[0]}} 389 return txs.NewSigned(utx, txs.Codec, signers) 390 }