github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/state/state_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 state 5 6 import ( 7 "testing" 8 "time" 9 10 "github.com/prometheus/client_golang/prometheus" 11 "github.com/stretchr/testify/require" 12 13 "github.com/MetalBlockchain/metalgo/database" 14 "github.com/MetalBlockchain/metalgo/database/memdb" 15 "github.com/MetalBlockchain/metalgo/database/versiondb" 16 "github.com/MetalBlockchain/metalgo/ids" 17 "github.com/MetalBlockchain/metalgo/version" 18 "github.com/MetalBlockchain/metalgo/vms/avm/block" 19 "github.com/MetalBlockchain/metalgo/vms/avm/fxs" 20 "github.com/MetalBlockchain/metalgo/vms/avm/txs" 21 "github.com/MetalBlockchain/metalgo/vms/components/avax" 22 "github.com/MetalBlockchain/metalgo/vms/secp256k1fx" 23 ) 24 25 const trackChecksums = false 26 27 var ( 28 parser block.Parser 29 populatedUTXO *avax.UTXO 30 populatedUTXOID ids.ID 31 populatedTx *txs.Tx 32 populatedTxID ids.ID 33 populatedBlk block.Block 34 populatedBlkHeight uint64 35 populatedBlkID ids.ID 36 ) 37 38 func init() { 39 var err error 40 parser, err = block.NewParser( 41 []fxs.Fx{ 42 &secp256k1fx.Fx{}, 43 }, 44 ) 45 if err != nil { 46 panic(err) 47 } 48 49 populatedUTXO = &avax.UTXO{ 50 UTXOID: avax.UTXOID{ 51 TxID: ids.GenerateTestID(), 52 }, 53 Asset: avax.Asset{ 54 ID: ids.GenerateTestID(), 55 }, 56 Out: &secp256k1fx.TransferOutput{ 57 Amt: 1, 58 }, 59 } 60 populatedUTXOID = populatedUTXO.InputID() 61 62 populatedTx = &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{ 63 BlockchainID: ids.GenerateTestID(), 64 }}} 65 err = populatedTx.Initialize(parser.Codec()) 66 if err != nil { 67 panic(err) 68 } 69 populatedTxID = populatedTx.ID() 70 71 populatedBlk, err = block.NewStandardBlock( 72 ids.GenerateTestID(), 73 1, 74 time.Now(), 75 []*txs.Tx{ 76 { 77 Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{ 78 BlockchainID: ids.GenerateTestID(), 79 }}, 80 }, 81 }, 82 parser.Codec(), 83 ) 84 if err != nil { 85 panic(err) 86 } 87 populatedBlkHeight = populatedBlk.Height() 88 populatedBlkID = populatedBlk.ID() 89 } 90 91 type versions struct { 92 chains map[ids.ID]Chain 93 } 94 95 func (v *versions) GetState(blkID ids.ID) (Chain, bool) { 96 c, ok := v.chains[blkID] 97 return c, ok 98 } 99 100 func TestState(t *testing.T) { 101 require := require.New(t) 102 103 db := memdb.New() 104 vdb := versiondb.New(db) 105 s, err := New(vdb, parser, prometheus.NewRegistry(), trackChecksums) 106 require.NoError(err) 107 108 s.AddUTXO(populatedUTXO) 109 s.AddTx(populatedTx) 110 s.AddBlock(populatedBlk) 111 require.NoError(s.Commit()) 112 113 s, err = New(vdb, parser, prometheus.NewRegistry(), trackChecksums) 114 require.NoError(err) 115 116 ChainUTXOTest(t, s) 117 ChainTxTest(t, s) 118 ChainBlockTest(t, s) 119 } 120 121 func TestDiff(t *testing.T) { 122 require := require.New(t) 123 124 db := memdb.New() 125 vdb := versiondb.New(db) 126 s, err := New(vdb, parser, prometheus.NewRegistry(), trackChecksums) 127 require.NoError(err) 128 129 s.AddUTXO(populatedUTXO) 130 s.AddTx(populatedTx) 131 s.AddBlock(populatedBlk) 132 require.NoError(s.Commit()) 133 134 parentID := ids.GenerateTestID() 135 d, err := NewDiff(parentID, &versions{ 136 chains: map[ids.ID]Chain{ 137 parentID: s, 138 }, 139 }) 140 require.NoError(err) 141 142 ChainUTXOTest(t, d) 143 ChainTxTest(t, d) 144 ChainBlockTest(t, d) 145 } 146 147 func ChainUTXOTest(t *testing.T, c Chain) { 148 require := require.New(t) 149 150 fetchedUTXO, err := c.GetUTXO(populatedUTXOID) 151 require.NoError(err) 152 153 // Compare IDs because [fetchedUTXO] isn't initialized 154 require.Equal(populatedUTXO.InputID(), fetchedUTXO.InputID()) 155 156 utxo := &avax.UTXO{ 157 UTXOID: avax.UTXOID{ 158 TxID: ids.GenerateTestID(), 159 }, 160 Asset: avax.Asset{ 161 ID: ids.GenerateTestID(), 162 }, 163 Out: &secp256k1fx.TransferOutput{ 164 Amt: 1, 165 }, 166 } 167 utxoID := utxo.InputID() 168 169 _, err = c.GetUTXO(utxoID) 170 require.ErrorIs(err, database.ErrNotFound) 171 172 c.AddUTXO(utxo) 173 174 fetchedUTXO, err = c.GetUTXO(utxoID) 175 require.NoError(err) 176 require.Equal(utxo, fetchedUTXO) 177 178 c.DeleteUTXO(utxoID) 179 180 _, err = c.GetUTXO(utxoID) 181 require.ErrorIs(err, database.ErrNotFound) 182 } 183 184 func ChainTxTest(t *testing.T, c Chain) { 185 require := require.New(t) 186 187 fetchedTx, err := c.GetTx(populatedTxID) 188 require.NoError(err) 189 190 // Compare IDs because [fetchedTx] differs between nil and empty fields 191 require.Equal(populatedTx.ID(), fetchedTx.ID()) 192 193 // Pull again for the cached path 194 fetchedTx, err = c.GetTx(populatedTxID) 195 require.NoError(err) 196 require.Equal(populatedTx.ID(), fetchedTx.ID()) 197 198 tx := &txs.Tx{Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{ 199 BlockchainID: ids.GenerateTestID(), 200 }}} 201 require.NoError(tx.Initialize(parser.Codec())) 202 txID := tx.ID() 203 204 _, err = c.GetTx(txID) 205 require.ErrorIs(err, database.ErrNotFound) 206 207 // Pull again for the cached path 208 _, err = c.GetTx(txID) 209 require.ErrorIs(err, database.ErrNotFound) 210 211 c.AddTx(tx) 212 213 fetchedTx, err = c.GetTx(txID) 214 require.NoError(err) 215 require.Equal(tx, fetchedTx) 216 } 217 218 func ChainBlockTest(t *testing.T, c Chain) { 219 require := require.New(t) 220 221 fetchedBlkID, err := c.GetBlockIDAtHeight(populatedBlkHeight) 222 require.NoError(err) 223 require.Equal(populatedBlkID, fetchedBlkID) 224 225 fetchedBlk, err := c.GetBlock(populatedBlkID) 226 require.NoError(err) 227 require.Equal(populatedBlk.ID(), fetchedBlk.ID()) 228 229 // Pull again for the cached path 230 fetchedBlkID, err = c.GetBlockIDAtHeight(populatedBlkHeight) 231 require.NoError(err) 232 require.Equal(populatedBlkID, fetchedBlkID) 233 234 fetchedBlk, err = c.GetBlock(populatedBlkID) 235 require.NoError(err) 236 require.Equal(populatedBlk.ID(), fetchedBlk.ID()) 237 238 blk, err := block.NewStandardBlock( 239 ids.GenerateTestID(), 240 10, 241 time.Now(), 242 []*txs.Tx{ 243 { 244 Unsigned: &txs.BaseTx{BaseTx: avax.BaseTx{ 245 BlockchainID: ids.GenerateTestID(), 246 }}, 247 }, 248 }, 249 parser.Codec(), 250 ) 251 if err != nil { 252 panic(err) 253 } 254 blkID := blk.ID() 255 blkHeight := blk.Height() 256 257 _, err = c.GetBlockIDAtHeight(blkHeight) 258 require.ErrorIs(err, database.ErrNotFound) 259 260 _, err = c.GetBlock(blkID) 261 require.ErrorIs(err, database.ErrNotFound) 262 263 // Pull again for the cached path 264 _, err = c.GetBlockIDAtHeight(blkHeight) 265 require.ErrorIs(err, database.ErrNotFound) 266 267 _, err = c.GetBlock(blkID) 268 require.ErrorIs(err, database.ErrNotFound) 269 270 c.AddBlock(blk) 271 272 fetchedBlkID, err = c.GetBlockIDAtHeight(blkHeight) 273 require.NoError(err) 274 require.Equal(blkID, fetchedBlkID) 275 276 fetchedBlk, err = c.GetBlock(blkID) 277 require.NoError(err) 278 require.Equal(blk, fetchedBlk) 279 } 280 281 func TestInitializeChainState(t *testing.T) { 282 require := require.New(t) 283 284 db := memdb.New() 285 vdb := versiondb.New(db) 286 s, err := New(vdb, parser, prometheus.NewRegistry(), trackChecksums) 287 require.NoError(err) 288 289 stopVertexID := ids.GenerateTestID() 290 genesisTimestamp := version.DefaultUpgradeTime 291 require.NoError(s.InitializeChainState(stopVertexID, genesisTimestamp)) 292 293 lastAcceptedID := s.GetLastAccepted() 294 genesis, err := s.GetBlock(lastAcceptedID) 295 require.NoError(err) 296 require.Equal(stopVertexID, genesis.Parent()) 297 require.Equal(genesisTimestamp.UnixNano(), genesis.Timestamp().UnixNano()) 298 299 childBlock, err := block.NewStandardBlock( 300 genesis.ID(), 301 genesis.Height()+1, 302 genesisTimestamp, 303 nil, 304 parser.Codec(), 305 ) 306 require.NoError(err) 307 308 s.AddBlock(childBlock) 309 s.SetLastAccepted(childBlock.ID()) 310 require.NoError(s.Commit()) 311 312 require.NoError(s.InitializeChainState(stopVertexID, genesisTimestamp)) 313 314 lastAcceptedID = s.GetLastAccepted() 315 lastAccepted, err := s.GetBlock(lastAcceptedID) 316 require.NoError(err) 317 require.Equal(genesis.ID(), lastAccepted.Parent()) 318 }