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

     1  package environment
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/fvm/errors"
     7  	"github.com/onflow/flow-go/fvm/storage/state"
     8  	"github.com/onflow/flow-go/fvm/tracing"
     9  	"github.com/onflow/flow-go/module/trace"
    10  	"github.com/onflow/flow-go/state/protocol/prg"
    11  )
    12  
    13  type RandomSourceHistoryProvider interface {
    14  	// RandomSourceHistory provides a source of entropy that can be
    15  	// expanded on-chain into randoms (using a pseudo-random generator).
    16  	// This random source is only destined to the history source core-contract
    17  	// to implement commit-reveal schemes.
    18  	// The returned slice should have at least 128 bits of entropy.
    19  	// The function doesn't error in normal operations, any
    20  	// error should be treated as an exception.
    21  	RandomSourceHistory() ([]byte, error)
    22  }
    23  
    24  type ParseRestrictedRandomSourceHistoryProvider struct {
    25  	txnState state.NestedTransactionPreparer
    26  	impl     RandomSourceHistoryProvider
    27  }
    28  
    29  func NewParseRestrictedRandomSourceHistoryProvider(
    30  	txnState state.NestedTransactionPreparer,
    31  	impl RandomSourceHistoryProvider,
    32  ) RandomSourceHistoryProvider {
    33  	return ParseRestrictedRandomSourceHistoryProvider{
    34  		txnState: txnState,
    35  		impl:     impl,
    36  	}
    37  }
    38  
    39  func (p ParseRestrictedRandomSourceHistoryProvider) RandomSourceHistory() ([]byte, error) {
    40  	return parseRestrict1Ret(
    41  		p.txnState,
    42  		trace.FVMEnvRandomSourceHistoryProvider,
    43  		p.impl.RandomSourceHistory,
    44  	)
    45  }
    46  
    47  // forbiddenRandomSourceHistoryProvider is a RandomSourceHistoryProvider that always returns an error.
    48  // this is the default implementation of RandomSourceHistoryProvider.
    49  type forbiddenRandomSourceHistoryProvider struct {
    50  }
    51  
    52  func NewForbiddenRandomSourceHistoryProvider() RandomSourceHistoryProvider {
    53  	return &forbiddenRandomSourceHistoryProvider{}
    54  }
    55  
    56  func (b forbiddenRandomSourceHistoryProvider) RandomSourceHistory() ([]byte, error) {
    57  	return nil, errors.NewOperationNotSupportedError("RandomSourceHistory")
    58  }
    59  
    60  type historySourceProvider struct {
    61  	tracer tracing.TracerSpan
    62  	meter  Meter
    63  	EntropyProvider
    64  }
    65  
    66  // NewRandomSourceHistoryProvider creates a new RandomSourceHistoryProvider.
    67  // If randomSourceCallAllowed is true, the returned RandomSourceHistoryProvider will
    68  // return a random source from the given EntropyProvider.
    69  // If randomSourceCallAllowed is false, the returned RandomSourceHistoryProvider will
    70  // always return an error.
    71  func NewRandomSourceHistoryProvider(
    72  	tracer tracing.TracerSpan,
    73  	meter Meter,
    74  	entropyProvider EntropyProvider,
    75  	randomSourceCallAllowed bool,
    76  ) RandomSourceHistoryProvider {
    77  	if randomSourceCallAllowed {
    78  		return &historySourceProvider{
    79  			tracer:          tracer,
    80  			meter:           meter,
    81  			EntropyProvider: entropyProvider,
    82  		}
    83  	}
    84  
    85  	return NewForbiddenRandomSourceHistoryProvider()
    86  }
    87  
    88  const randomSourceHistoryLen = 32
    89  
    90  func (b *historySourceProvider) RandomSourceHistory() ([]byte, error) {
    91  	defer b.tracer.StartExtensiveTracingChildSpan(
    92  		trace.FVMEnvRandomSourceHistoryProvider).End()
    93  
    94  	err := b.meter.MeterComputation(ComputationKindGetRandomSourceHistory, 1)
    95  	if err != nil {
    96  		return nil, fmt.Errorf("get block randomSource failed: %w", err)
    97  	}
    98  
    99  	source, err := b.RandomSource()
   100  	// `RandomSource` does not error in normal operations.
   101  	// Any error should be treated as an exception.
   102  	if err != nil {
   103  		return nil, errors.NewRandomSourceFailure(fmt.Errorf(
   104  			"get random source for block randomSource failed: %w", err))
   105  	}
   106  
   107  	// A method that derives `randomSourceHistoryLen` bytes from `source` must:
   108  	//  - extract and expand the entropy in `source`
   109  	//  - output must be independent than the expanded bytes used for Cadence's `random` function
   110  	//
   111  	// The method chosen here is to rely on the same CSPRG used to derive randoms from the source entropy
   112  	// (but other methods are possible)
   113  	//  - use the state/protocol/prg customizer defined for the execution random source history.
   114  	//   (to ensure independence of seeds, customizer must be different than the one used for Cadence's
   115  	//     `random` in random_generator.go)
   116  	csprg, err := prg.New(source, prg.ExecutionRandomSourceHistory, nil)
   117  	if err != nil {
   118  		return nil, fmt.Errorf("failed to create a PRG from source: %w", err)
   119  	}
   120  
   121  	historySource := make([]byte, randomSourceHistoryLen)
   122  	csprg.Read(historySource)
   123  
   124  	return historySource, nil
   125  }