github.com/MetalBlockchain/metalgo@v1.11.9/chains/atomic/test_shared_memory.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package atomic 5 6 import ( 7 "math/rand" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 12 "github.com/MetalBlockchain/metalgo/database" 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/utils/units" 15 ) 16 17 // SharedMemoryTests is a list of all shared memory tests 18 var SharedMemoryTests = []func(t *testing.T, chainID0, chainID1 ids.ID, sm0, sm1 SharedMemory, db database.Database){ 19 TestSharedMemoryPutAndGet, 20 TestSharedMemoryLargePutGetAndRemove, 21 TestSharedMemoryIndexed, 22 TestSharedMemoryLargeIndexed, 23 TestSharedMemoryCantDuplicatePut, 24 TestSharedMemoryCantDuplicateRemove, 25 TestSharedMemoryCommitOnPut, 26 TestSharedMemoryCommitOnRemove, 27 TestSharedMemoryLargeBatchSize, 28 TestPutAndRemoveBatch, 29 } 30 31 func TestSharedMemoryPutAndGet(t *testing.T, chainID0, chainID1 ids.ID, sm0, sm1 SharedMemory, _ database.Database) { 32 require := require.New(t) 33 34 require.NoError(sm0.Apply(map[ids.ID]*Requests{chainID1: {PutRequests: []*Element{{ 35 Key: []byte{0}, 36 Value: []byte{1}, 37 }}}})) 38 39 values, err := sm1.Get(chainID0, [][]byte{{0}}) 40 require.NoError(err) 41 require.Equal([][]byte{{1}}, values, "wrong values returned") 42 } 43 44 // TestSharedMemoryLargePutGetAndRemove tests to make sure that the interface 45 // can support large values. 46 func TestSharedMemoryLargePutGetAndRemove(t *testing.T, chainID0, chainID1 ids.ID, sm0, sm1 SharedMemory, _ database.Database) { 47 require := require.New(t) 48 rand.Seed(0) 49 50 totalSize := 16 * units.MiB // 16 MiB 51 elementSize := 4 * units.KiB // 4 KiB 52 pairSize := 2 * elementSize // 8 KiB 53 54 b := make([]byte, totalSize) 55 _, err := rand.Read(b) // #nosec G404 56 require.NoError(err) 57 58 elems := []*Element{} 59 keys := [][]byte{} 60 for len(b) > pairSize { 61 key := b[:elementSize] 62 b = b[elementSize:] 63 64 value := b[:elementSize] 65 b = b[elementSize:] 66 67 elems = append(elems, &Element{ 68 Key: key, 69 Value: value, 70 }) 71 keys = append(keys, key) 72 } 73 74 require.NoError(sm0.Apply(map[ids.ID]*Requests{ 75 chainID1: { 76 PutRequests: elems, 77 }, 78 })) 79 80 values, err := sm1.Get( 81 chainID0, 82 keys, 83 ) 84 require.NoError(err) 85 for i, value := range values { 86 require.Equal(elems[i].Value, value) 87 } 88 89 require.NoError(sm1.Apply(map[ids.ID]*Requests{ 90 chainID0: { 91 RemoveRequests: keys, 92 }, 93 })) 94 } 95 96 func TestSharedMemoryIndexed(t *testing.T, chainID0, chainID1 ids.ID, sm0, sm1 SharedMemory, _ database.Database) { 97 require := require.New(t) 98 99 require.NoError(sm0.Apply(map[ids.ID]*Requests{chainID1: {PutRequests: []*Element{{ 100 Key: []byte{0}, 101 Value: []byte{1}, 102 Traits: [][]byte{ 103 {2}, 104 {3}, 105 }, 106 }}}})) 107 108 require.NoError(sm0.Apply(map[ids.ID]*Requests{chainID1: {PutRequests: []*Element{{ 109 Key: []byte{4}, 110 Value: []byte{5}, 111 Traits: [][]byte{ 112 {2}, 113 {3}, 114 }, 115 }}}})) 116 117 values, _, _, err := sm0.Indexed(chainID1, [][]byte{{2}}, nil, nil, 1) 118 require.NoError(err) 119 require.Empty(values, "wrong indexed values returned") 120 121 values, _, _, err = sm1.Indexed(chainID0, [][]byte{{2}}, nil, nil, 0) 122 require.NoError(err) 123 require.Empty(values, "wrong indexed values returned") 124 125 values, _, _, err = sm1.Indexed(chainID0, [][]byte{{2}}, nil, nil, 1) 126 require.NoError(err) 127 require.Equal([][]byte{{5}}, values, "wrong indexed values returned") 128 129 values, _, _, err = sm1.Indexed(chainID0, [][]byte{{2}}, nil, nil, 2) 130 require.NoError(err) 131 require.Equal([][]byte{{5}, {1}}, values, "wrong indexed values returned") 132 133 values, _, _, err = sm1.Indexed(chainID0, [][]byte{{2}}, nil, nil, 3) 134 require.NoError(err) 135 require.Equal([][]byte{{5}, {1}}, values, "wrong indexed values returned") 136 137 values, _, _, err = sm1.Indexed(chainID0, [][]byte{{3}}, nil, nil, 3) 138 require.NoError(err) 139 require.Equal([][]byte{{5}, {1}}, values, "wrong indexed values returned") 140 141 values, _, _, err = sm1.Indexed(chainID0, [][]byte{{2}, {3}}, nil, nil, 3) 142 require.NoError(err) 143 require.Equal([][]byte{{5}, {1}}, values, "wrong indexed values returned") 144 } 145 146 func TestSharedMemoryLargeIndexed(t *testing.T, chainID0, chainID1 ids.ID, sm0, sm1 SharedMemory, _ database.Database) { 147 require := require.New(t) 148 149 totalSize := 8 * units.MiB // 8 MiB 150 elementSize := 1 * units.KiB // 1 KiB 151 pairSize := 3 * elementSize // 3 KiB 152 153 b := make([]byte, totalSize) 154 _, err := rand.Read(b) // #nosec G404 155 require.NoError(err) 156 157 elems := []*Element{} 158 allTraits := [][]byte{} 159 for len(b) > pairSize { 160 key := b[:elementSize] 161 b = b[elementSize:] 162 163 value := b[:elementSize] 164 b = b[elementSize:] 165 166 traits := [][]byte{ 167 b[:elementSize], 168 } 169 allTraits = append(allTraits, traits...) 170 b = b[elementSize:] 171 172 elems = append(elems, &Element{ 173 Key: key, 174 Value: value, 175 Traits: traits, 176 }) 177 } 178 179 require.NoError(sm0.Apply(map[ids.ID]*Requests{chainID1: {PutRequests: elems}})) 180 181 values, _, _, err := sm1.Indexed(chainID0, allTraits, nil, nil, len(elems)+1) 182 require.NoError(err) 183 require.Len(values, len(elems), "wrong number of values returned") 184 } 185 186 func TestSharedMemoryCantDuplicatePut(t *testing.T, _, chainID1 ids.ID, sm0, _ SharedMemory, _ database.Database) { 187 require := require.New(t) 188 189 err := sm0.Apply(map[ids.ID]*Requests{chainID1: {PutRequests: []*Element{ 190 { 191 Key: []byte{0}, 192 Value: []byte{1}, 193 }, 194 { 195 Key: []byte{0}, 196 Value: []byte{2}, 197 }, 198 }}}) 199 // TODO: require error to be errDuplicatedOperation 200 require.Error(err) //nolint:forbidigo // currently returns grpc errors too 201 202 require.NoError(sm0.Apply(map[ids.ID]*Requests{chainID1: {PutRequests: []*Element{{ 203 Key: []byte{0}, 204 Value: []byte{1}, 205 }}}})) 206 207 err = sm0.Apply(map[ids.ID]*Requests{chainID1: {PutRequests: []*Element{{ 208 Key: []byte{0}, 209 Value: []byte{1}, 210 }}}}) 211 // TODO: require error to be errDuplicatedOperation 212 require.Error(err) //nolint:forbidigo // currently returns grpc errors too 213 } 214 215 func TestSharedMemoryCantDuplicateRemove(t *testing.T, _, chainID1 ids.ID, sm0, _ SharedMemory, _ database.Database) { 216 require := require.New(t) 217 218 require.NoError(sm0.Apply(map[ids.ID]*Requests{chainID1: {RemoveRequests: [][]byte{{0}}}})) 219 220 err := sm0.Apply(map[ids.ID]*Requests{chainID1: {RemoveRequests: [][]byte{{0}}}}) 221 // TODO: require error to be errDuplicatedOperation 222 require.Error(err) //nolint:forbidigo // currently returns grpc errors too 223 } 224 225 func TestSharedMemoryCommitOnPut(t *testing.T, _, chainID1 ids.ID, sm0, _ SharedMemory, db database.Database) { 226 require := require.New(t) 227 228 require.NoError(db.Put([]byte{1}, []byte{2})) 229 230 batch := db.NewBatch() 231 232 require.NoError(batch.Put([]byte{0}, []byte{1})) 233 234 require.NoError(batch.Delete([]byte{1})) 235 236 require.NoError(sm0.Apply( 237 map[ids.ID]*Requests{chainID1: {PutRequests: []*Element{{ 238 Key: []byte{0}, 239 Value: []byte{1}, 240 }}}}, 241 batch, 242 )) 243 244 val, err := db.Get([]byte{0}) 245 require.NoError(err) 246 require.Equal([]byte{1}, val) 247 248 has, err := db.Has([]byte{1}) 249 require.NoError(err) 250 require.False(has) 251 } 252 253 func TestSharedMemoryCommitOnRemove(t *testing.T, _, chainID1 ids.ID, sm0, _ SharedMemory, db database.Database) { 254 require := require.New(t) 255 256 require.NoError(db.Put([]byte{1}, []byte{2})) 257 258 batch := db.NewBatch() 259 260 require.NoError(batch.Put([]byte{0}, []byte{1})) 261 262 require.NoError(batch.Delete([]byte{1})) 263 264 require.NoError(sm0.Apply( 265 map[ids.ID]*Requests{chainID1: {RemoveRequests: [][]byte{{0}}}}, 266 batch, 267 )) 268 269 val, err := db.Get([]byte{0}) 270 require.NoError(err) 271 require.Equal([]byte{1}, val) 272 273 has, err := db.Has([]byte{1}) 274 require.NoError(err) 275 require.False(has) 276 } 277 278 // TestPutAndRemoveBatch tests to make sure multiple put and remove requests work properly 279 func TestPutAndRemoveBatch(t *testing.T, chainID0, _ ids.ID, _, sm1 SharedMemory, db database.Database) { 280 require := require.New(t) 281 282 batch := db.NewBatch() 283 284 require.NoError(batch.Put([]byte{0}, []byte{1})) 285 286 batchChainsAndInputs := make(map[ids.ID]*Requests) 287 288 byteArr := [][]byte{{0}, {1}, {5}} 289 290 batchChainsAndInputs[chainID0] = &Requests{ 291 PutRequests: []*Element{{ 292 Key: []byte{2}, 293 Value: []byte{9}, 294 }}, 295 RemoveRequests: byteArr, 296 } 297 298 require.NoError(sm1.Apply(batchChainsAndInputs, batch)) 299 300 val, err := db.Get([]byte{0}) 301 require.NoError(err) 302 require.Equal([]byte{1}, val) 303 } 304 305 // TestSharedMemoryLargeBatchSize tests to make sure that the interface can 306 // support large batches. 307 func TestSharedMemoryLargeBatchSize(t *testing.T, _, chainID1 ids.ID, sm0, _ SharedMemory, db database.Database) { 308 require := require.New(t) 309 rand.Seed(0) 310 311 totalSize := 8 * units.MiB // 8 MiB 312 elementSize := 4 * units.KiB // 4 KiB 313 pairSize := 2 * elementSize // 8 KiB 314 315 bytes := make([]byte, totalSize) 316 _, err := rand.Read(bytes) // #nosec G404 317 require.NoError(err) 318 319 batch := db.NewBatch() 320 require.NotNil(batch) 321 322 initialBytes := bytes 323 for len(bytes) > pairSize { 324 key := bytes[:elementSize] 325 bytes = bytes[elementSize:] 326 327 value := bytes[:elementSize] 328 bytes = bytes[elementSize:] 329 330 require.NoError(batch.Put(key, value)) 331 } 332 333 require.NoError(db.Put([]byte{1}, []byte{2})) 334 335 require.NoError(batch.Put([]byte{0}, []byte{1})) 336 337 require.NoError(batch.Delete([]byte{1})) 338 339 require.NoError(sm0.Apply( 340 map[ids.ID]*Requests{chainID1: {RemoveRequests: [][]byte{{0}}}}, 341 batch, 342 )) 343 344 val, err := db.Get([]byte{0}) 345 require.NoError(err) 346 require.Equal([]byte{1}, val) 347 348 has, err := db.Has([]byte{1}) 349 require.NoError(err) 350 require.False(has) 351 352 batch.Reset() 353 354 bytes = initialBytes 355 for len(bytes) > pairSize { 356 key := bytes[:elementSize] 357 bytes = bytes[pairSize:] 358 359 require.NoError(batch.Delete(key)) 360 } 361 362 require.NoError(sm0.Apply( 363 map[ids.ID]*Requests{chainID1: {RemoveRequests: [][]byte{{1}}}}, 364 batch, 365 )) 366 367 batch.Reset() 368 369 bytes = initialBytes 370 for len(bytes) > pairSize { 371 key := bytes[:elementSize] 372 bytes = bytes[pairSize:] 373 374 require.NoError(batch.Delete(key)) 375 } 376 377 batchChainsAndInputs := make(map[ids.ID]*Requests) 378 379 byteArr := [][]byte{{30}, {40}, {50}} 380 381 batchChainsAndInputs[chainID1] = &Requests{ 382 PutRequests: []*Element{{ 383 Key: []byte{2}, 384 Value: []byte{9}, 385 }}, 386 RemoveRequests: byteArr, 387 } 388 389 require.NoError(sm0.Apply( 390 batchChainsAndInputs, 391 batch, 392 )) 393 }