github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/environment/random_generator_test.go (about)

     1  package environment_test
     2  
     3  import (
     4  	"encoding/binary"
     5  	"math"
     6  	mrand "math/rand"
     7  	"testing"
     8  
     9  	"github.com/onflow/crypto/random"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/onflow/flow-go/fvm/environment"
    13  	"github.com/onflow/flow-go/fvm/environment/mock"
    14  	"github.com/onflow/flow-go/fvm/tracing"
    15  	"github.com/onflow/flow-go/utils/unittest"
    16  )
    17  
    18  func TestRandomGenerator(t *testing.T) {
    19  	randomSourceHistoryProvider := &mock.EntropyProvider{}
    20  	randomSourceHistoryProvider.On("RandomSource").Return(unittest.RandomBytes(48), nil)
    21  
    22  	getRandoms := func(txId []byte, N int) []uint64 {
    23  		// seed the RG with the same block header
    24  		urg := environment.NewRandomGenerator(
    25  			tracing.NewTracerSpan(),
    26  			randomSourceHistoryProvider,
    27  			txId)
    28  		numbers := make([]uint64, N)
    29  		for i := 0; i < N; i++ {
    30  			var buffer [8]byte
    31  			err := urg.ReadRandom(buffer[:])
    32  			require.NoError(t, err)
    33  			numbers[i] = binary.LittleEndian.Uint64(buffer[:])
    34  		}
    35  		return numbers
    36  	}
    37  
    38  	// basic randomness test to check outputs are "uniformly" spread over the
    39  	// output space
    40  	t.Run("randomness test", func(t *testing.T) {
    41  		for i := 0; i < 10; i++ {
    42  			txId := unittest.TransactionFixture().ID()
    43  			urg := environment.NewRandomGenerator(
    44  				tracing.NewTracerSpan(),
    45  				randomSourceHistoryProvider,
    46  				txId[:])
    47  
    48  			// make sure n is a power of 2 so that there is no bias in the last class
    49  			// n is a random power of 2 (from 2 to 2^10)
    50  			n := 1 << (1 + mrand.Intn(10))
    51  			classWidth := (math.MaxUint64 / uint64(n)) + 1
    52  			random.BasicDistributionTest(t, uint64(n), uint64(classWidth), func() (uint64, error) {
    53  				var buffer [8]byte
    54  				err := urg.ReadRandom(buffer[:])
    55  				if err != nil {
    56  					return 0, err
    57  				}
    58  				return binary.LittleEndian.Uint64(buffer[:]), nil
    59  			})
    60  		}
    61  	})
    62  
    63  	// tests that has deterministic outputs.
    64  	t.Run("PRG-based Random", func(t *testing.T) {
    65  		for i := 0; i < 10; i++ {
    66  			txId := unittest.TransactionFixture().ID()
    67  			N := 100
    68  			r1 := getRandoms(txId[:], N)
    69  			r2 := getRandoms(txId[:], N)
    70  			require.Equal(t, r1, r2)
    71  		}
    72  	})
    73  
    74  	t.Run("transaction specific randomness", func(t *testing.T) {
    75  		txns := [][]uint64{}
    76  		for i := 0; i < 10; i++ {
    77  			txId := unittest.TransactionFixture().ID()
    78  			N := 2
    79  			txns = append(txns, getRandoms(txId[:], N))
    80  		}
    81  
    82  		for i, txn := range txns {
    83  			for _, otherTxn := range txns[i+1:] {
    84  				require.NotEqual(t, txn, otherTxn)
    85  			}
    86  		}
    87  	})
    88  }