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 }