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 }