github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/environment/uuids_test.go (about) 1 package environment 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/rs/zerolog" 8 "github.com/stretchr/testify/require" 9 10 "github.com/onflow/flow-go/fvm/storage/state" 11 "github.com/onflow/flow-go/fvm/tracing" 12 "github.com/onflow/flow-go/model/flow" 13 ) 14 15 func TestUUIDPartition(t *testing.T) { 16 blockHeader := &flow.Header{} 17 18 usedPartitions := map[byte]struct{}{} 19 20 // With enough samples, all partitions should be used. (The first 1500 blocks 21 // only uses 254 partitions) 22 for numBlocks := 0; numBlocks < 2000; numBlocks++ { 23 blockId := blockHeader.ID() 24 25 partition0 := uuidPartition(blockId, 0) 26 usedPartitions[partition0] = struct{}{} 27 28 for txnIndex := 0; txnIndex < 256; txnIndex++ { 29 partition := uuidPartition(blockId, uint32(txnIndex)) 30 31 // Ensure neighboring transactions uses neighoring partitions. 32 require.Equal(t, partition, partition0+byte(txnIndex)) 33 34 // Ensure wrap around. 35 for i := 0; i < 5; i++ { 36 require.Equal( 37 t, 38 partition, 39 uuidPartition(blockId, uint32(txnIndex+i*256))) 40 } 41 } 42 43 blockHeader.ParentID = blockId 44 } 45 46 require.Len(t, usedPartitions, 256) 47 } 48 49 func TestUUIDGeneratorInitializePartitionNoHeader(t *testing.T) { 50 for txnIndex := uint32(0); txnIndex < 256; txnIndex++ { 51 uuids := NewUUIDGenerator( 52 tracing.NewTracerSpan(), 53 zerolog.Nop(), 54 nil, 55 nil, 56 nil, 57 txnIndex) 58 require.False(t, uuids.initialized) 59 60 uuids.maybeInitializePartition() 61 62 require.True(t, uuids.initialized) 63 require.Equal(t, uuids.partition, byte(0)) 64 require.Equal(t, uuids.registerId, flow.UUIDRegisterID(byte(0))) 65 } 66 } 67 68 func TestUUIDGeneratorInitializePartition(t *testing.T) { 69 blockHeader := &flow.Header{} 70 71 for numBlocks := 0; numBlocks < 10; numBlocks++ { 72 blockId := blockHeader.ID() 73 74 for txnIndex := uint32(0); txnIndex < 256; txnIndex++ { 75 uuids := NewUUIDGenerator( 76 tracing.NewTracerSpan(), 77 zerolog.Nop(), 78 nil, 79 nil, 80 blockHeader, 81 txnIndex) 82 require.False(t, uuids.initialized) 83 84 uuids.maybeInitializePartition() 85 86 require.True(t, uuids.initialized) 87 88 expectedPartition := uuidPartition(blockId, txnIndex) 89 90 require.Equal(t, uuids.partition, expectedPartition) 91 require.Equal( 92 t, 93 uuids.registerId, 94 flow.UUIDRegisterID(expectedPartition)) 95 } 96 97 blockHeader.ParentID = blockId 98 } 99 } 100 101 func TestUUIDGeneratorIdGeneration(t *testing.T) { 102 for txnIndex := uint32(0); txnIndex < 256; txnIndex++ { 103 testUUIDGenerator(t, &flow.Header{}, txnIndex) 104 } 105 } 106 107 func testUUIDGenerator(t *testing.T, blockHeader *flow.Header, txnIndex uint32) { 108 generator := NewUUIDGenerator( 109 tracing.NewTracerSpan(), 110 zerolog.Nop(), 111 nil, 112 nil, 113 blockHeader, 114 txnIndex) 115 generator.maybeInitializePartition() 116 117 partition := generator.partition 118 partitionMinValue := uint64(partition) << 40 119 maxUint56 := uint64(0xFFFFFFFFFFFFFF) 120 maxUint56Split := uint64(0xFFFF00FFFFFFFFFF) 121 122 t.Run( 123 fmt.Sprintf("basic get and set uint (partition: %d)", partition), 124 func(t *testing.T) { 125 txnState := state.NewTransactionState(nil, state.DefaultParameters()) 126 uuidsA := NewUUIDGenerator( 127 tracing.NewTracerSpan(), 128 zerolog.Nop(), 129 NewMeter(txnState), 130 txnState, 131 blockHeader, 132 txnIndex) 133 uuidsA.maybeInitializePartition() 134 135 uuid, err := uuidsA.getCounter() // start from zero 136 require.NoError(t, err) 137 require.Equal(t, uint64(0), uuid) 138 139 err = uuidsA.setCounter(5) 140 require.NoError(t, err) 141 142 // create new UUIDs instance 143 uuidsB := NewUUIDGenerator( 144 tracing.NewTracerSpan(), 145 zerolog.Nop(), 146 NewMeter(txnState), 147 txnState, 148 blockHeader, 149 txnIndex) 150 uuidsB.maybeInitializePartition() 151 152 uuid, err = uuidsB.getCounter() // should read saved value 153 require.NoError(t, err) 154 155 require.Equal(t, uint64(5), uuid) 156 }) 157 158 t.Run( 159 fmt.Sprintf("basic id generation (partition: %d)", partition), 160 func(t *testing.T) { 161 txnState := state.NewTransactionState(nil, state.DefaultParameters()) 162 genA := NewUUIDGenerator( 163 tracing.NewTracerSpan(), 164 zerolog.Nop(), 165 NewMeter(txnState), 166 txnState, 167 blockHeader, 168 txnIndex) 169 170 uuidA, err := genA.GenerateUUID() 171 require.NoError(t, err) 172 uuidB, err := genA.GenerateUUID() 173 require.NoError(t, err) 174 uuidC, err := genA.GenerateUUID() 175 require.NoError(t, err) 176 177 require.Equal(t, partitionMinValue, uuidA) 178 require.Equal(t, partitionMinValue+1, uuidB) 179 require.Equal(t, partitionMinValue|1, uuidB) 180 require.Equal(t, partitionMinValue+2, uuidC) 181 require.Equal(t, partitionMinValue|2, uuidC) 182 183 // Create new generator instance from same ledger 184 genB := NewUUIDGenerator( 185 tracing.NewTracerSpan(), 186 zerolog.Nop(), 187 NewMeter(txnState), 188 txnState, 189 blockHeader, 190 txnIndex) 191 192 uuidD, err := genB.GenerateUUID() 193 require.NoError(t, err) 194 uuidE, err := genB.GenerateUUID() 195 require.NoError(t, err) 196 uuidF, err := genB.GenerateUUID() 197 require.NoError(t, err) 198 199 require.Equal(t, partitionMinValue+3, uuidD) 200 require.Equal(t, partitionMinValue|3, uuidD) 201 require.Equal(t, partitionMinValue+4, uuidE) 202 require.Equal(t, partitionMinValue|4, uuidE) 203 require.Equal(t, partitionMinValue+5, uuidF) 204 require.Equal(t, partitionMinValue|5, uuidF) 205 }) 206 207 t.Run( 208 fmt.Sprintf("setCounter overflows (partition: %d)", partition), 209 func(t *testing.T) { 210 txnState := state.NewTransactionState(nil, state.DefaultParameters()) 211 uuids := NewUUIDGenerator( 212 tracing.NewTracerSpan(), 213 zerolog.Nop(), 214 NewMeter(txnState), 215 txnState, 216 blockHeader, 217 txnIndex) 218 uuids.maybeInitializePartition() 219 220 err := uuids.setCounter(maxUint56) 221 require.NoError(t, err) 222 223 value, err := uuids.getCounter() 224 require.NoError(t, err) 225 require.Equal(t, value, maxUint56) 226 227 err = uuids.setCounter(maxUint56 + 1) 228 require.ErrorContains(t, err, "overflowed") 229 230 value, err = uuids.getCounter() 231 require.NoError(t, err) 232 require.Equal(t, value, maxUint56) 233 }) 234 235 t.Run( 236 fmt.Sprintf("id generation overflows (partition: %d)", partition), 237 func(t *testing.T) { 238 txnState := state.NewTransactionState(nil, state.DefaultParameters()) 239 uuids := NewUUIDGenerator( 240 tracing.NewTracerSpan(), 241 zerolog.Nop(), 242 NewMeter(txnState), 243 txnState, 244 blockHeader, 245 txnIndex) 246 uuids.maybeInitializePartition() 247 248 err := uuids.setCounter(maxUint56 - 1) 249 require.NoError(t, err) 250 251 value, err := uuids.GenerateUUID() 252 require.NoError(t, err) 253 require.Equal(t, value, partitionMinValue+maxUint56Split-1) 254 require.Equal(t, value, partitionMinValue|(maxUint56Split-1)) 255 256 value, err = uuids.getCounter() 257 require.NoError(t, err) 258 require.Equal(t, value, maxUint56) 259 260 _, err = uuids.GenerateUUID() 261 require.ErrorContains(t, err, "overflowed") 262 263 value, err = uuids.getCounter() 264 require.NoError(t, err) 265 require.Equal(t, value, maxUint56) 266 }) 267 } 268 269 func TestUUIDGeneratorHardcodedPartitionIdGeneration(t *testing.T) { 270 txnState := state.NewTransactionState(nil, state.DefaultParameters()) 271 uuids := NewUUIDGenerator( 272 tracing.NewTracerSpan(), 273 zerolog.Nop(), 274 NewMeter(txnState), 275 txnState, 276 nil, 277 0) 278 279 // Hardcoded the partition to check for exact bytes 280 uuids.initialized = true 281 uuids.partition = 0xde 282 uuids.registerId = flow.UUIDRegisterID(0xde) 283 284 value, err := uuids.GenerateUUID() 285 require.NoError(t, err) 286 require.Equal(t, value, uint64(0x0000de0000000000)) 287 288 value, err = uuids.getCounter() 289 require.NoError(t, err) 290 require.Equal(t, value, uint64(1)) 291 292 value, err = uuids.GenerateUUID() 293 require.NoError(t, err) 294 require.Equal(t, value, uint64(0x0000de0000000001)) 295 296 value, err = uuids.getCounter() 297 require.NoError(t, err) 298 require.Equal(t, value, uint64(2)) 299 300 value, err = uuids.GenerateUUID() 301 require.NoError(t, err) 302 require.Equal(t, value, uint64(0x0000de0000000002)) 303 304 value, err = uuids.getCounter() 305 require.NoError(t, err) 306 require.Equal(t, value, uint64(3)) 307 308 // pretend we increamented the counter up to cafBad 309 cafBad := uint64(0x1c2a3f4b5a6d70) 310 decafBad := uint64(0x1c2ade3f4b5a6d70) 311 312 err = uuids.setCounter(cafBad) 313 require.NoError(t, err) 314 315 for i := 0; i < 5; i++ { 316 value, err = uuids.GenerateUUID() 317 require.NoError(t, err) 318 require.Equal(t, value, decafBad+uint64(i)) 319 } 320 321 value, err = uuids.getCounter() 322 require.NoError(t, err) 323 require.Equal(t, value, cafBad+uint64(5)) 324 325 // pretend we increamented the counter up to overflow - 2 326 maxUint56Minus2 := uint64(0xfffffffffffffd) 327 err = uuids.setCounter(maxUint56Minus2) 328 require.NoError(t, err) 329 330 value, err = uuids.GenerateUUID() 331 require.NoError(t, err) 332 require.Equal(t, value, uint64(0xffffdefffffffffd)) 333 334 value, err = uuids.getCounter() 335 require.NoError(t, err) 336 require.Equal(t, value, maxUint56Minus2+1) 337 338 value, err = uuids.GenerateUUID() 339 require.NoError(t, err) 340 require.Equal(t, value, uint64(0xffffdefffffffffe)) 341 342 value, err = uuids.getCounter() 343 require.NoError(t, err) 344 require.Equal(t, value, maxUint56Minus2+2) 345 346 _, err = uuids.GenerateUUID() 347 require.ErrorContains(t, err, "overflowed") 348 349 value, err = uuids.getCounter() 350 require.NoError(t, err) 351 require.Equal(t, value, maxUint56Minus2+2) 352 } 353 354 func TestContinuati(t *testing.T) { 355 txnState := state.NewTransactionState(nil, state.DefaultParameters()) 356 uuids := NewUUIDGenerator( 357 tracing.NewTracerSpan(), 358 zerolog.Nop(), 359 NewMeter(txnState), 360 txnState, 361 nil, 362 0) 363 364 // Hardcoded the partition to check for exact bytes 365 uuids.initialized = true 366 uuids.partition = 0x01 367 uuids.registerId = flow.UUIDRegisterID(0x01) 368 369 value, err := uuids.GenerateUUID() 370 require.NoError(t, err) 371 require.Equal(t, value, uint64(0x0000010000000000)) 372 373 err = uuids.setCounter(0xFFFFFFFFFF) 374 require.NoError(t, err) 375 376 value, err = uuids.GenerateUUID() 377 require.NoError(t, err) 378 require.Equal(t, value, uint64(0x000001FFFFFFFFFF)) 379 380 value, err = uuids.GenerateUUID() 381 require.NoError(t, err) 382 require.Equal(t, value, uint64(0x0001010000000000)) 383 384 value, err = uuids.GenerateUUID() 385 require.NoError(t, err) 386 require.Equal(t, value, uint64(0x0001010000000001)) 387 388 }