github.com/MetalBlockchain/metalgo@v1.11.9/snow/engine/avalanche/state/unique_vertex_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 "bytes" 8 "context" 9 "errors" 10 "testing" 11 12 "github.com/stretchr/testify/require" 13 14 "github.com/MetalBlockchain/metalgo/database/memdb" 15 "github.com/MetalBlockchain/metalgo/ids" 16 "github.com/MetalBlockchain/metalgo/snow/choices" 17 "github.com/MetalBlockchain/metalgo/snow/consensus/snowstorm" 18 "github.com/MetalBlockchain/metalgo/snow/engine/avalanche/vertex" 19 "github.com/MetalBlockchain/metalgo/utils/hashing" 20 "github.com/MetalBlockchain/metalgo/utils/logging" 21 ) 22 23 var errUnknownTx = errors.New("unknown tx") 24 25 func newTestSerializer(t *testing.T, parse func(context.Context, []byte) (snowstorm.Tx, error)) *Serializer { 26 vm := vertex.TestVM{} 27 vm.T = t 28 vm.Default(true) 29 vm.ParseTxF = parse 30 31 baseDB := memdb.New() 32 s := NewSerializer( 33 SerializerConfig{ 34 ChainID: ids.Empty, 35 VM: &vm, 36 DB: baseDB, 37 Log: logging.NoLog{}, 38 }, 39 ) 40 41 return s.(*Serializer) 42 } 43 44 func TestUnknownUniqueVertexErrors(t *testing.T) { 45 require := require.New(t) 46 s := newTestSerializer(t, nil) 47 48 uVtx := &uniqueVertex{ 49 serializer: s, 50 id: ids.Empty, 51 } 52 53 status := uVtx.Status() 54 require.Equal(choices.Unknown, status) 55 56 _, err := uVtx.Parents() 57 require.ErrorIs(err, errGetParents) 58 59 _, err = uVtx.Height() 60 require.ErrorIs(err, errGetHeight) 61 62 _, err = uVtx.Txs(context.Background()) 63 require.ErrorIs(err, errGetTxs) 64 } 65 66 func TestUniqueVertexCacheHit(t *testing.T) { 67 require := require.New(t) 68 69 testTx := &snowstorm.TestTx{TestDecidable: choices.TestDecidable{ 70 IDV: ids.ID{1}, 71 }} 72 73 s := newTestSerializer(t, func(_ context.Context, b []byte) (snowstorm.Tx, error) { 74 require.Equal([]byte{0}, b) 75 return testTx, nil 76 }) 77 78 id := ids.ID{2} 79 parentID := ids.ID{'p', 'a', 'r', 'e', 'n', 't'} 80 parentIDs := []ids.ID{parentID} 81 height := uint64(1) 82 vtx, err := vertex.Build( // regular, non-stop vertex 83 s.ChainID, 84 height, 85 parentIDs, 86 [][]byte{{0}}, 87 ) 88 require.NoError(err) 89 90 uVtx := &uniqueVertex{ 91 id: id, 92 serializer: s, 93 } 94 require.NoError(uVtx.setVertex(context.Background(), vtx)) 95 96 newUVtx := &uniqueVertex{ 97 id: id, 98 serializer: s, 99 } 100 101 parents, err := newUVtx.Parents() 102 require.NoError(err) 103 require.Len(parents, 1) 104 require.Equal(parentID, parents[0].ID()) 105 106 newHeight, err := newUVtx.Height() 107 require.NoError(err) 108 require.Equal(height, newHeight) 109 110 txs, err := newUVtx.Txs(context.Background()) 111 require.NoError(err) 112 require.Len(txs, 1) 113 require.Equal(testTx, txs[0]) 114 115 require.Equal(uVtx.v, newUVtx.v) 116 } 117 118 func TestUniqueVertexCacheMiss(t *testing.T) { 119 require := require.New(t) 120 121 txBytesParent := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9} 122 testTxParent := &snowstorm.TestTx{ 123 TestDecidable: choices.TestDecidable{ 124 IDV: ids.ID{1}, 125 StatusV: choices.Accepted, 126 }, 127 BytesV: txBytesParent, 128 } 129 130 txBytes := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9} 131 testTx := &snowstorm.TestTx{ 132 TestDecidable: choices.TestDecidable{ 133 IDV: ids.ID{1}, 134 }, 135 BytesV: txBytes, 136 } 137 parseTx := func(_ context.Context, b []byte) (snowstorm.Tx, error) { 138 if bytes.Equal(txBytesParent, b) { 139 return testTxParent, nil 140 } 141 if bytes.Equal(txBytes, b) { 142 return testTx, nil 143 } 144 require.FailNow("asked to parse unexpected transaction") 145 return nil, nil 146 } 147 148 s := newTestSerializer(t, parseTx) 149 150 uvtxParent := newTestUniqueVertex(t, s, nil, [][]byte{txBytesParent}, false) 151 require.NoError(uvtxParent.Accept(context.Background())) 152 153 parentID := uvtxParent.ID() 154 parentIDs := []ids.ID{parentID} 155 height := uint64(1) 156 innerVertex, err := vertex.Build( // regular, non-stop vertex 157 s.ChainID, 158 height, 159 parentIDs, 160 [][]byte{txBytes}, 161 ) 162 require.NoError(err) 163 164 id := innerVertex.ID() 165 vtxBytes := innerVertex.Bytes() 166 167 uVtx := uniqueVertex{ 168 id: id, 169 serializer: s, 170 } 171 172 // Register a cache miss 173 require.Equal(choices.Unknown, uVtx.Status()) 174 175 // Register cache hit 176 vtx, err := newUniqueVertex(context.Background(), s, vtxBytes) 177 require.NoError(err) 178 179 require.Equal(choices.Processing, vtx.Status()) 180 181 validateVertex := func(vtx *uniqueVertex, expectedStatus choices.Status) { 182 require.Equal(expectedStatus, vtx.Status()) 183 184 // Call bytes first to check for regression bug 185 // where it's unsafe to call Bytes or Verify directly 186 // after calling Status to refresh a vertex 187 require.Equal(vtxBytes, vtx.Bytes()) 188 189 vtxParents, err := vtx.Parents() 190 require.NoError(err) 191 require.Len(vtxParents, 1) 192 require.Equal(parentID, vtxParents[0].ID()) 193 194 vtxHeight, err := vtx.Height() 195 require.NoError(err) 196 require.Equal(height, vtxHeight) 197 198 vtxTxs, err := vtx.Txs(context.Background()) 199 require.NoError(err) 200 require.Len(vtxTxs, 1) 201 require.Equal(txBytes, vtxTxs[0].Bytes()) 202 } 203 204 // Replace the vertex, so that it loses reference to parents, etc. 205 vtx = &uniqueVertex{ 206 id: id, 207 serializer: s, 208 } 209 210 // Check that the vertex refreshed from the cache is valid 211 validateVertex(vtx, choices.Processing) 212 213 // Check that a newly parsed vertex refreshed from the cache is valid 214 vtx, err = newUniqueVertex(context.Background(), s, vtxBytes) 215 require.NoError(err) 216 validateVertex(vtx, choices.Processing) 217 218 // Check that refreshing a vertex when it has been removed from 219 // the cache works correctly 220 221 s.state.uniqueVtx.Flush() 222 vtx = &uniqueVertex{ 223 id: id, 224 serializer: s, 225 } 226 validateVertex(vtx, choices.Processing) 227 228 s.state.uniqueVtx.Flush() 229 vtx, err = newUniqueVertex(context.Background(), s, vtxBytes) 230 require.NoError(err) 231 validateVertex(vtx, choices.Processing) 232 } 233 234 func TestParseVertexWithIncorrectChainID(t *testing.T) { 235 require := require.New(t) 236 237 statelessVertex, err := vertex.Build( // regular, non-stop vertex 238 ids.GenerateTestID(), 239 0, 240 nil, 241 [][]byte{{1}}, 242 ) 243 require.NoError(err) 244 vtxBytes := statelessVertex.Bytes() 245 246 s := newTestSerializer(t, func(_ context.Context, b []byte) (snowstorm.Tx, error) { 247 if bytes.Equal(b, []byte{1}) { 248 return &snowstorm.TestTx{}, nil 249 } 250 return nil, errUnknownTx 251 }) 252 253 _, err = s.ParseVtx(context.Background(), vtxBytes) 254 require.ErrorIs(err, errWrongChainID) 255 } 256 257 func TestParseVertexWithInvalidTxs(t *testing.T) { 258 require := require.New(t) 259 260 s := newTestSerializer(t, func(_ context.Context, b []byte) (snowstorm.Tx, error) { 261 switch { 262 case bytes.Equal(b, []byte{2}): 263 return &snowstorm.TestTx{}, nil 264 default: 265 return nil, errUnknownTx 266 } 267 }) 268 269 statelessVertex, err := vertex.Build( // regular, non-stop vertex 270 s.ChainID, 271 0, 272 nil, 273 [][]byte{{1}}, 274 ) 275 require.NoError(err) 276 vtxBytes := statelessVertex.Bytes() 277 278 _, err = s.ParseVtx(context.Background(), vtxBytes) 279 require.ErrorIs(err, errUnknownTx) 280 281 _, err = s.ParseVtx(context.Background(), vtxBytes) 282 require.ErrorIs(err, errUnknownTx) 283 284 id := hashing.ComputeHash256Array(vtxBytes) 285 _, err = s.GetVtx(context.Background(), id) 286 require.ErrorIs(err, errUnknownVertex) 287 288 childStatelessVertex, err := vertex.Build( // regular, non-stop vertex 289 s.ChainID, 290 1, 291 []ids.ID{id}, 292 [][]byte{{2}}, 293 ) 294 require.NoError(err) 295 childVtxBytes := childStatelessVertex.Bytes() 296 297 childVtx, err := s.ParseVtx(context.Background(), childVtxBytes) 298 require.NoError(err) 299 300 parents, err := childVtx.Parents() 301 require.NoError(err) 302 require.Len(parents, 1) 303 parent := parents[0] 304 305 require.False(parent.Status().Fetched()) 306 } 307 308 func newTestUniqueVertex( 309 t *testing.T, 310 s *Serializer, 311 parentIDs []ids.ID, 312 txs [][]byte, 313 stopVertex bool, 314 ) *uniqueVertex { 315 require := require.New(t) 316 317 var ( 318 vtx vertex.StatelessVertex 319 err error 320 ) 321 if !stopVertex { 322 vtx, err = vertex.Build( 323 s.ChainID, 324 uint64(1), 325 parentIDs, 326 txs, 327 ) 328 } else { 329 vtx, err = vertex.BuildStopVertex( 330 s.ChainID, 331 uint64(1), 332 parentIDs, 333 ) 334 } 335 require.NoError(err) 336 uvtx, err := newUniqueVertex(context.Background(), s, vtx.Bytes()) 337 require.NoError(err) 338 return uvtx 339 }