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  }