github.com/koko1123/flow-go-1@v0.29.6/fvm/environment/unsafe_random_generator.go (about)

     1  package environment
     2  
     3  import (
     4  	"encoding/binary"
     5  	"math/rand"
     6  	"sync"
     7  
     8  	"github.com/koko1123/flow-go-1/fvm/errors"
     9  	"github.com/koko1123/flow-go-1/fvm/state"
    10  	"github.com/koko1123/flow-go-1/fvm/tracing"
    11  	"github.com/koko1123/flow-go-1/model/flow"
    12  	"github.com/koko1123/flow-go-1/module/trace"
    13  )
    14  
    15  type UnsafeRandomGenerator interface {
    16  	// UnsafeRandom returns a random uint64, where the process of random number
    17  	// derivation is not cryptographically secure.
    18  	UnsafeRandom() (uint64, error)
    19  }
    20  
    21  type unsafeRandomGenerator struct {
    22  	tracer tracing.TracerSpan
    23  
    24  	blockHeader *flow.Header
    25  
    26  	rng      *rand.Rand
    27  	seedOnce sync.Once
    28  }
    29  
    30  type ParseRestrictedUnsafeRandomGenerator struct {
    31  	txnState *state.TransactionState
    32  	impl     UnsafeRandomGenerator
    33  }
    34  
    35  func NewParseRestrictedUnsafeRandomGenerator(
    36  	txnState *state.TransactionState,
    37  	impl UnsafeRandomGenerator,
    38  ) UnsafeRandomGenerator {
    39  	return ParseRestrictedUnsafeRandomGenerator{
    40  		txnState: txnState,
    41  		impl:     impl,
    42  	}
    43  }
    44  
    45  func (gen ParseRestrictedUnsafeRandomGenerator) UnsafeRandom() (
    46  	uint64,
    47  	error,
    48  ) {
    49  	return parseRestrict1Ret(
    50  		gen.txnState,
    51  		trace.FVMEnvUnsafeRandom,
    52  		gen.impl.UnsafeRandom)
    53  }
    54  
    55  func NewUnsafeRandomGenerator(
    56  	tracer tracing.TracerSpan,
    57  	blockHeader *flow.Header,
    58  ) UnsafeRandomGenerator {
    59  	gen := &unsafeRandomGenerator{
    60  		tracer:      tracer,
    61  		blockHeader: blockHeader,
    62  	}
    63  
    64  	return gen
    65  }
    66  
    67  // seed seeds the random number generator with the block header ID.
    68  // This allows lazy seeding of the random number generator,
    69  // since not a lot of transactions/scripts use it and the time it takes to seed it is not negligible.
    70  func (gen *unsafeRandomGenerator) seed() {
    71  	gen.seedOnce.Do(func() {
    72  		if gen.blockHeader == nil {
    73  			return
    74  		}
    75  		// Seed the random number generator with entropy created from the block
    76  		// header ID. The random number generator will be used by the
    77  		// UnsafeRandom function.
    78  		id := gen.blockHeader.ID()
    79  		source := rand.NewSource(int64(binary.BigEndian.Uint64(id[:])))
    80  		gen.rng = rand.New(source)
    81  	})
    82  }
    83  
    84  // UnsafeRandom returns a random uint64, where the process of random number
    85  // derivation is not cryptographically secure.
    86  // this is not thread safe, due to gen.rng.Read(buf).
    87  // Its also not thread safe because each thread needs to be deterministically seeded with a different seed.
    88  // This is Ok because a single transaction has a single UnsafeRandomGenerator and is run in a single thread.
    89  func (gen *unsafeRandomGenerator) UnsafeRandom() (uint64, error) {
    90  	defer gen.tracer.StartExtensiveTracingChildSpan(trace.FVMEnvUnsafeRandom).End()
    91  
    92  	gen.seed()
    93  
    94  	if gen.rng == nil {
    95  		return 0, errors.NewOperationNotSupportedError("UnsafeRandom")
    96  	}
    97  
    98  	// TODO (ramtin) return errors this assumption that this always succeeds
    99  	// might not be true
   100  	buf := make([]byte, 8)
   101  	_, _ = gen.rng.Read(buf) // Always succeeds, no need to check error
   102  	return binary.LittleEndian.Uint64(buf), nil
   103  }