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

     1  package runtime
     2  
     3  import (
     4  	"github.com/onflow/cadence"
     5  	"github.com/onflow/cadence/runtime"
     6  	"github.com/onflow/cadence/runtime/common"
     7  	"github.com/onflow/cadence/runtime/interpreter"
     8  	"github.com/onflow/cadence/runtime/sema"
     9  	"github.com/onflow/cadence/runtime/stdlib"
    10  
    11  	"github.com/onflow/flow-go/fvm/errors"
    12  )
    13  
    14  // Note: this is a subset of environment.Environment, redeclared to handle
    15  // circular dependency.
    16  type Environment interface {
    17  	runtime.Interface
    18  
    19  	RandomSourceHistory() ([]byte, error)
    20  }
    21  
    22  // randomSourceFunctionType is the type of the `randomSource` function.
    23  // This defies the signature as `func (): [UInt8]`
    24  var randomSourceFunctionType = &sema.FunctionType{
    25  	Parameters:           []sema.Parameter{},
    26  	ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.ByteArrayType),
    27  }
    28  
    29  type ReusableCadenceRuntime struct {
    30  	runtime.Runtime
    31  	TxRuntimeEnv     runtime.Environment
    32  	ScriptRuntimeEnv runtime.Environment
    33  
    34  	fvmEnv Environment
    35  }
    36  
    37  func NewReusableCadenceRuntime(rt runtime.Runtime, config runtime.Config) *ReusableCadenceRuntime {
    38  	reusable := &ReusableCadenceRuntime{
    39  		Runtime:          rt,
    40  		TxRuntimeEnv:     runtime.NewBaseInterpreterEnvironment(config),
    41  		ScriptRuntimeEnv: runtime.NewScriptInterpreterEnvironment(config),
    42  	}
    43  
    44  	// Declare the `randomSourceHistory` function. This function is **only** used by the
    45  	// System transaction, to fill the `RandomBeaconHistory` contract via the heartbeat
    46  	// resource. This allows the `RandomBeaconHistory` contract to be a standard contract,
    47  	// without any special parts.
    48  	// Since the `randomSourceHistory` function is only used by the System transaction,
    49  	// it is not part of the cadence standard library, and can just be injected from here.
    50  	// It also doesnt need user documentation, since it is not (and should not)
    51  	// be called by the user. If it is called by the user it will panic.
    52  	blockRandomSource := stdlib.StandardLibraryValue{
    53  		Name: "randomSourceHistory",
    54  		Type: randomSourceFunctionType,
    55  		Kind: common.DeclarationKindFunction,
    56  		Value: interpreter.NewUnmeteredStaticHostFunctionValue(
    57  			randomSourceFunctionType,
    58  			func(invocation interpreter.Invocation) interpreter.Value {
    59  				if len(invocation.Arguments) != 0 {
    60  					panic(errors.NewInvalidArgumentErrorf(
    61  						"randomSourceHistory should be called without arguments"))
    62  				}
    63  
    64  				var err error
    65  				var source []byte
    66  				if reusable.fvmEnv != nil {
    67  					source, err = reusable.fvmEnv.RandomSourceHistory()
    68  				} else {
    69  					err = errors.NewOperationNotSupportedError("randomSourceHistory")
    70  				}
    71  
    72  				if err != nil {
    73  					panic(err)
    74  				}
    75  
    76  				return interpreter.ByteSliceToByteArrayValue(
    77  					invocation.Interpreter,
    78  					source)
    79  			},
    80  		),
    81  	}
    82  
    83  	reusable.TxRuntimeEnv.DeclareValue(blockRandomSource, nil)
    84  
    85  	return reusable
    86  }
    87  
    88  func (reusable *ReusableCadenceRuntime) SetFvmEnvironment(fvmEnv Environment) {
    89  	reusable.fvmEnv = fvmEnv
    90  }
    91  
    92  func (reusable *ReusableCadenceRuntime) ReadStored(
    93  	address common.Address,
    94  	path cadence.Path,
    95  ) (
    96  	cadence.Value,
    97  	error,
    98  ) {
    99  	return reusable.Runtime.ReadStored(
   100  		address,
   101  		path,
   102  		runtime.Context{
   103  			Interface:   reusable.fvmEnv,
   104  			Environment: reusable.TxRuntimeEnv,
   105  		},
   106  	)
   107  }
   108  
   109  func (reusable *ReusableCadenceRuntime) InvokeContractFunction(
   110  	contractLocation common.AddressLocation,
   111  	functionName string,
   112  	arguments []cadence.Value,
   113  	argumentTypes []sema.Type,
   114  ) (
   115  	cadence.Value,
   116  	error,
   117  ) {
   118  	return reusable.Runtime.InvokeContractFunction(
   119  		contractLocation,
   120  		functionName,
   121  		arguments,
   122  		argumentTypes,
   123  		runtime.Context{
   124  			Interface:   reusable.fvmEnv,
   125  			Environment: reusable.TxRuntimeEnv,
   126  		},
   127  	)
   128  }
   129  
   130  func (reusable *ReusableCadenceRuntime) NewTransactionExecutor(
   131  	script runtime.Script,
   132  	location common.Location,
   133  ) runtime.Executor {
   134  	return reusable.Runtime.NewTransactionExecutor(
   135  		script,
   136  		runtime.Context{
   137  			Interface:   reusable.fvmEnv,
   138  			Location:    location,
   139  			Environment: reusable.TxRuntimeEnv,
   140  		},
   141  	)
   142  }
   143  
   144  func (reusable *ReusableCadenceRuntime) ExecuteScript(
   145  	script runtime.Script,
   146  	location common.Location,
   147  ) (
   148  	cadence.Value,
   149  	error,
   150  ) {
   151  	return reusable.Runtime.ExecuteScript(
   152  		script,
   153  		runtime.Context{
   154  			Interface:   reusable.fvmEnv,
   155  			Location:    location,
   156  			Environment: reusable.ScriptRuntimeEnv,
   157  		},
   158  	)
   159  }
   160  
   161  type CadenceRuntimeConstructor func(config runtime.Config) runtime.Runtime
   162  
   163  type ReusableCadenceRuntimePool struct {
   164  	pool chan *ReusableCadenceRuntime
   165  
   166  	config runtime.Config
   167  
   168  	// When newCustomRuntime is nil, the pool will create standard cadence
   169  	// interpreter runtimes via runtime.NewInterpreterRuntime.  Otherwise, the
   170  	// pool will create runtimes using this function.
   171  	//
   172  	// Note that this is primarily used for testing.
   173  	newCustomRuntime CadenceRuntimeConstructor
   174  }
   175  
   176  func newReusableCadenceRuntimePool(
   177  	poolSize int,
   178  	config runtime.Config,
   179  	newCustomRuntime CadenceRuntimeConstructor,
   180  ) ReusableCadenceRuntimePool {
   181  	var pool chan *ReusableCadenceRuntime
   182  	if poolSize > 0 {
   183  		pool = make(chan *ReusableCadenceRuntime, poolSize)
   184  	}
   185  
   186  	return ReusableCadenceRuntimePool{
   187  		pool:             pool,
   188  		config:           config,
   189  		newCustomRuntime: newCustomRuntime,
   190  	}
   191  }
   192  
   193  func NewReusableCadenceRuntimePool(
   194  	poolSize int,
   195  	config runtime.Config,
   196  ) ReusableCadenceRuntimePool {
   197  	return newReusableCadenceRuntimePool(
   198  		poolSize,
   199  		config,
   200  		nil,
   201  	)
   202  }
   203  
   204  func NewCustomReusableCadenceRuntimePool(
   205  	poolSize int,
   206  	config runtime.Config,
   207  	newCustomRuntime CadenceRuntimeConstructor,
   208  ) ReusableCadenceRuntimePool {
   209  	return newReusableCadenceRuntimePool(
   210  		poolSize,
   211  		config,
   212  		newCustomRuntime,
   213  	)
   214  }
   215  
   216  func (pool ReusableCadenceRuntimePool) newRuntime() runtime.Runtime {
   217  	if pool.newCustomRuntime != nil {
   218  		return pool.newCustomRuntime(pool.config)
   219  	}
   220  	return runtime.NewInterpreterRuntime(pool.config)
   221  }
   222  
   223  func (pool ReusableCadenceRuntimePool) Borrow(
   224  	fvmEnv Environment,
   225  ) *ReusableCadenceRuntime {
   226  	var reusable *ReusableCadenceRuntime
   227  	select {
   228  	case reusable = <-pool.pool:
   229  		// Do nothing.
   230  	default:
   231  		reusable = NewReusableCadenceRuntime(
   232  			WrappedCadenceRuntime{
   233  				pool.newRuntime(),
   234  			},
   235  			pool.config,
   236  		)
   237  	}
   238  
   239  	reusable.SetFvmEnvironment(fvmEnv)
   240  	return reusable
   241  }
   242  
   243  func (pool ReusableCadenceRuntimePool) Return(
   244  	reusable *ReusableCadenceRuntime,
   245  ) {
   246  	reusable.SetFvmEnvironment(nil)
   247  	select {
   248  	case pool.pool <- reusable:
   249  		// Do nothing.
   250  	default:
   251  		// Do nothing.  Discard the overflow entry.
   252  	}
   253  }