Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PoC: WebAssembly API #2760

Draft
wants to merge 49 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
2b7c365
add support for dictionary types
turbolent Aug 31, 2023
9fdfb4d
update wasmtime
turbolent Sep 1, 2023
cbc1ece
improve encodeArgs helper
turbolent Sep 1, 2023
a780497
add the start of a WebAssembly contract
turbolent Sep 1, 2023
188b162
Merge branch 'feature/stable-cadence' into bastian/webassembly
turbolent Sep 1, 2023
02a0122
add comment explaining the program
turbolent Sep 22, 2023
8d9cbb6
convert type
turbolent Sep 22, 2023
254cb3d
Merge branch 'feature/stable-cadence' into bastian/webassembly
turbolent Sep 22, 2023
eaaf1ea
Merge branch 'bastian/allow-type-injection' into bastian/webassembly
turbolent Sep 22, 2023
d4265f6
inject WebAssembly standard library type into environment
turbolent Sep 22, 2023
384eef2
gate WebAssembly behind feture flag
turbolent Sep 23, 2023
4240bf9
move wasmtime-based WebAssembly module to runtime, so it can be reused
turbolent Sep 23, 2023
57b8d6a
rename
turbolent Sep 23, 2023
8d30d3a
extract constructor for wasmtime-based WebAssembly module
turbolent Sep 23, 2023
606c3a2
add metering using fuel mechanism
turbolent Sep 23, 2023
a6e7b9a
fix return type conversion
turbolent Sep 23, 2023
f54b831
Merge branch 'bastian/sync-stable-cadence-10' into bastian/webassembly
turbolent Oct 5, 2023
f8270bd
Merge branch 'bastian/sync-stable-cadence-10' into bastian/webassembly
turbolent Oct 5, 2023
692aa2a
re-generate
turbolent Oct 5, 2023
eb98a3d
Merge branch 'feature/stable-cadence' into bastian/webassembly
turbolent Oct 5, 2023
b4c634b
update comment to reflect current functionality
turbolent Mar 3, 2024
cbfcbd3
Merge branch 'master' into bastian/webassembly
turbolent Jun 25, 2024
b67d23f
update to wasmtime v22
turbolent Jun 25, 2024
e470d46
intgrate WebAssembly standard library values and types into existing …
turbolent Jun 25, 2024
ead17b8
lint
turbolent Jun 25, 2024
e3c752f
update copyright
turbolent Jun 25, 2024
f197db0
improve config
turbolent Jun 25, 2024
23795cb
improve metering: always meter, even on failure
turbolent Jun 25, 2024
f6b6acf
disabling SIMD is broken
turbolent Jun 25, 2024
8bcbf8c
fix check
turbolent Jun 25, 2024
12b56a5
limit store contents
turbolent Jun 25, 2024
9089bc6
add comments
turbolent Jun 26, 2024
6b6c8fb
update wasmtime
turbolent Jun 26, 2024
e55b81e
improve wasmtime setup
turbolent Jun 26, 2024
8bd4f7f
update wasmtime
turbolent Jul 12, 2024
d737d3d
add tests
turbolent Jul 12, 2024
efa0800
convert wasmtime trap to generic stdlib trap error
turbolent Jul 12, 2024
d9a6321
make trap error a user error
turbolent Jul 12, 2024
12817cb
improve error for invalid non-function export
turbolent Jul 12, 2024
ab88756
improve error for WebAssembly module compilation failure
turbolent Jul 12, 2024
a3fe557
convert wasmtime call trap into generic WebAssembly trap error
turbolent Jul 12, 2024
9b4c462
GetFuel/SetFuel should never error
turbolent Jul 12, 2024
755d0d3
add runtime interface method to get remaining computation for given kind
turbolent Jul 12, 2024
ef5d371
use new computation remaining for fuel
turbolent Jul 12, 2024
2d42183
Merge branch 'master' into bastian/webassembly
turbolent Jul 13, 2024
e6c5f1b
Merge branch 'master' into bastian/webassembly
turbolent Oct 16, 2024
d3cca43
fix merge: implement new runtime interface functions
turbolent Oct 16, 2024
c0a9bf1
update to wasmtime v25
turbolent Oct 16, 2024
91b3e19
Merge branch 'master' into bastian/webassembly
turbolent Nov 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cmd/check/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,10 @@ func runPath(
location := common.NewStringLocation(nil, path)

// standard library handler is only needed for execution, but we're only checking
standardLibraryValues := stdlib.DefaultScriptStandardLibraryValues(nil)
standardLibraryValues := stdlib.DefaultScriptStandardLibraryValues(
nil,
stdlib.DefaultStandardLibraryOptions,
)

func() {
defer func() {
Expand Down
5 changes: 5 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ func PrepareInterpreter(filename string, debugger *interpreter.Debugger) (*inter

standardLibraryValues := stdlib.DefaultScriptStandardLibraryValues(
&StandardLibraryHandler{},
stdlib.DefaultStandardLibraryOptions,
)

checker, must := PrepareChecker(
Expand Down Expand Up @@ -403,6 +404,10 @@ func (*StandardLibraryHandler) BLSAggregateSignatures(_ [][]byte) ([]byte, error
return nil, goerrors.New("crypto functionality is not available in this environment")
}

func (*StandardLibraryHandler) CompileWebAssembly(_ []byte) (stdlib.WebAssemblyModule, error) {
return nil, goerrors.New("WebAssembly functionality is not available in this environment")
}

func (h *StandardLibraryHandler) NewOnEventEmittedHandler() interpreter.OnEventEmittedFunc {
return func(
inter *interpreter.Interpreter,
Expand Down
5 changes: 4 additions & 1 deletion cmd/compile/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ func main() {
program, must := cmd.PrepareProgramFromFile(location, codes)

// standard library handler is only needed for execution, but we're only checking
standardLibraryValues := stdlib.DefaultScriptStandardLibraryValues(nil)
standardLibraryValues := stdlib.DefaultScriptStandardLibraryValues(
nil,
stdlib.DefaultStandardLibraryOptions,
)

checker, must := cmd.PrepareChecker(
program,
Expand Down
5 changes: 4 additions & 1 deletion cmd/info/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,10 @@ func dumpBuiltinValues() {
}

allBaseSemaValueTypes := sema_utils.AllBaseSemaValueTypes()
standardLibraryValues := stdlib.DefaultScriptStandardLibraryValues(nil)
standardLibraryValues := stdlib.DefaultScriptStandardLibraryValues(
nil,
stdlib.DefaultStandardLibraryOptions,
)

valueTypes := make([]valueType, 0, len(allBaseSemaValueTypes)+len(standardLibraryValues))

Expand Down
4 changes: 4 additions & 0 deletions common/computationkind.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,8 @@ const (
// RLP
ComputationKindSTDLIBRLPDecodeString
ComputationKindSTDLIBRLPDecodeList
_
_
// WebAssembly
ComputationKindWebAssemblyFuel
)
4 changes: 4 additions & 0 deletions common/computationkind_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ go 1.22

require (
github.com/bits-and-blooms/bitset v1.5.0
github.com/bytecodealliance/wasmtime-go/v7 v7.0.0
github.com/c-bata/go-prompt v0.2.6
github.com/dave/dst v0.27.2
github.com/fxamacker/cbor/v2 v2.4.1-0.20230228173756-c0c9f774e40c
Expand All @@ -31,6 +30,7 @@ require (

require (
github.com/SaveTheRbtz/mph v0.1.1-0.20240117162131-4166ec7869bc
github.com/bytecodealliance/wasmtime-go/v25 v25.0.0
github.com/k0kubun/pp v3.0.1+incompatible
github.com/kodova/html-to-markdown v1.0.1
github.com/onflow/crypto v0.25.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8=
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bytecodealliance/wasmtime-go/v7 v7.0.0 h1:/rBNjgFju2HCZnkPb1eL+W4GBwP8DMbaQu7i+GR9DH4=
github.com/bytecodealliance/wasmtime-go/v7 v7.0.0/go.mod h1:bu6fic7trDt20w+LMooX7j3fsOwv4/ln6j8gAdP6vmA=
github.com/bytecodealliance/wasmtime-go/v25 v25.0.0 h1:ZTn4Ho+srrk0466ugqPfTDCITczsWdT48A0ZMA/TpRU=
github.com/bytecodealliance/wasmtime-go/v25 v25.0.0/go.mod h1:8mMIYQ92CpVDwXPIb6udnhtFGI3vDZ/937cGeQr5I68=
github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI=
github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
Expand Down
2 changes: 2 additions & 0 deletions interpreter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type Config struct {
OnResourceOwnerChange OnResourceOwnerChangeFunc
// OnMeterComputation is triggered when a computation is about to happen
OnMeterComputation OnMeterComputationFunc
// OnComputationRemaining is used to determine how much computation is remaining
OnComputationRemaining OnComputationRemainingFunc
// InjectedCompositeFieldsHandler is used to initialize new composite values' fields
InjectedCompositeFieldsHandler InjectedCompositeFieldsHandlerFunc
// ContractValueHandler is used to handle imports of values
Expand Down
16 changes: 16 additions & 0 deletions interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ type OnMeterComputationFunc func(
intensity uint,
)

// OnComputationRemainingFunc is a function that is used to determine how much computation is remaining.
type OnComputationRemainingFunc func(
kind common.ComputationKind,
) uint

// CapabilityBorrowHandlerFunc is a function that is used to borrow ID capabilities.
type CapabilityBorrowHandlerFunc func(
inter *Interpreter,
Expand Down Expand Up @@ -4996,6 +5001,17 @@ func (interpreter *Interpreter) ReportComputation(compKind common.ComputationKin
}
}

func (interpreter *Interpreter) ComputationRemaining(compKind common.ComputationKind) uint {
config := interpreter.SharedState.Config

onComputationRemaining := config.OnComputationRemaining
if onComputationRemaining != nil {
return onComputationRemaining(compKind)
}

return math.MaxUint
}

func (interpreter *Interpreter) getAccessOfMember(self Value, identifier string) sema.Access {
typ, err := interpreter.ConvertStaticToSemaType(self.StaticType(interpreter))
// some values (like transactions) do not have types that can be looked up this way. These types
Expand Down
5 changes: 4 additions & 1 deletion interpreter/memory_metering_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,10 @@ func TestInterpretHostFunctionMetering(t *testing.T) {

baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation)
baseActivation := activations.NewActivation(nil, interpreter.BaseActivation)
for _, valueDeclaration := range stdlib.DefaultStandardLibraryValues(nil) {
for _, valueDeclaration := range stdlib.DefaultStandardLibraryValues(
nil,
stdlib.DefaultStandardLibraryOptions,
) {
baseValueActivation.DeclareValue(valueDeclaration)
interpreter.Declare(baseActivation, valueDeclaration)
}
Expand Down
10 changes: 5 additions & 5 deletions runtime/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -630,10 +630,10 @@ func TestRuntimeAuthAccountKeysAdd(t *testing.T) {
err := rt.ExecuteTransaction(
Script{
Source: []byte(code),
Arguments: encodeArgs([]cadence.Value{
Arguments: encodeArgs(
pubKey1Value,
pubKey2Value,
}),
),
},
Context{
Location: nextTransactionLocation(),
Expand Down Expand Up @@ -1370,7 +1370,7 @@ func addPublicKeyValidation(runtimeInterface *TestRuntimeInterface, returnError
}
}

func encodeArgs(argValues []cadence.Value) [][]byte {
func encodeArgs(argValues ...cadence.Value) [][]byte {
args := make([][]byte, len(argValues))
for i, arg := range argValues {
var err error
Expand All @@ -1393,7 +1393,7 @@ func (test accountKeyTestCase) executeTransaction(
runtimeInterface *TestRuntimeInterface,
location Location,
) error {
args := encodeArgs(test.args)
args := encodeArgs(test.args...)

err := runtime.ExecuteTransaction(
Script{
Expand All @@ -1413,7 +1413,7 @@ func (test accountKeyTestCase) executeScript(
runtimeInterface *TestRuntimeInterface,
) (cadence.Value, error) {

args := encodeArgs(test.args)
args := encodeArgs(test.args...)

value, err := runtime.ExecuteScript(
Script{
Expand Down
2 changes: 2 additions & 0 deletions runtime/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ type Config struct {
CoverageReport *CoverageReport
// LegacyContractUpgradeEnabled enabled specifies whether to use the old parser when parsing an old contract
LegacyContractUpgradeEnabled bool
// WebAssemblyEnabled specifies if the WebAssembly API is enabled
WebAssemblyEnabled bool
}
4 changes: 2 additions & 2 deletions runtime/contract_update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,10 @@ func TestRuntimeContractRedeployInSameTransaction(t *testing.T) {
err := runtime.ExecuteTransaction(
Script{
Source: tx,
Arguments: encodeArgs([]cadence.Value{
Arguments: encodeArgs(
cadence.String(foo1),
cadence.String(foo2),
}),
),
},
Context{
Interface: runtimeInterface,
Expand Down
10 changes: 5 additions & 5 deletions runtime/crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func TestRuntimeSignatureAlgorithmImport(t *testing.T) {
value, err := runtime.ExecuteScript(
Script{
Source: []byte(script),
Arguments: encodeArgs([]cadence.Value{
Arguments: encodeArgs(
cadence.NewEnum([]cadence.Value{
cadence.UInt8(algo.RawValue()),
}).WithType(cadence.NewEnumType(
Expand All @@ -291,7 +291,7 @@ func TestRuntimeSignatureAlgorithmImport(t *testing.T) {
},
nil,
)),
}),
),
},
Context{
Interface: runtimeInterface,
Expand Down Expand Up @@ -357,7 +357,7 @@ func TestRuntimeHashAlgorithmImport(t *testing.T) {
value, err := runtime.ExecuteScript(
Script{
Source: []byte(script),
Arguments: encodeArgs([]cadence.Value{
Arguments: encodeArgs(
cadence.NewEnum([]cadence.Value{
cadence.UInt8(algo.RawValue()),
}).WithType(cadence.NewEnumType(
Expand All @@ -372,7 +372,7 @@ func TestRuntimeHashAlgorithmImport(t *testing.T) {
},
nil,
)),
}),
),
},
Context{
Interface: runtimeInterface,
Expand Down Expand Up @@ -734,7 +734,7 @@ func TestRuntimeTraversingMerkleProof(t *testing.T) {
_, err := runtime.ExecuteScript(
Script{
Source: script,
Arguments: encodeArgs([]cadence.Value{rootHash, address, accountProof}),
Arguments: encodeArgs(rootHash, address, accountProof),
},
Context{
Interface: runtimeInterface,
Expand Down
9 changes: 9 additions & 0 deletions runtime/empty.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/onflow/cadence/common"
"github.com/onflow/cadence/interpreter"
"github.com/onflow/cadence/sema"
"github.com/onflow/cadence/stdlib"
)

// EmptyRuntimeInterface is an empty implementation of runtime.Interface.
Expand Down Expand Up @@ -63,6 +64,10 @@ func (EmptyRuntimeInterface) ComputationUsed() (uint64, error) {
panic("unexpected call to ComputationUsed")
}

func (i EmptyRuntimeInterface) ComputationRemaining(_ common.ComputationKind) uint {
panic("unexpected call to ComputationRemaining")
}

func (EmptyRuntimeInterface) MemoryUsed() (uint64, error) {
panic("unexpected call to MemoryUsed")
}
Expand Down Expand Up @@ -264,3 +269,7 @@ func (EmptyRuntimeInterface) ValidateAccountCapabilitiesPublish(
func (EmptyRuntimeInterface) MinimumRequiredVersion() (string, error) {
return "0.0.0", nil
}

func (EmptyRuntimeInterface) CompileWebAssembly(_ []byte) (stdlib.WebAssemblyModule, error) {
panic("unexpected call to CompileWebAssembly")
}
35 changes: 33 additions & 2 deletions runtime/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ func newInterpreterEnvironment(config Config) *interpreterEnvironment {
}
env.InterpreterConfig = env.newInterpreterConfig()
env.CheckerConfig = env.newCheckerConfig()

env.compositeValueFunctionsHandlers = stdlib.DefaultStandardLibraryCompositeValueFunctionHandlers(env)

return env
}

Expand Down Expand Up @@ -191,6 +193,7 @@ func (e *interpreterEnvironment) newInterpreterConfig() *interpreter.Config {
Debugger: e.config.Debugger,
OnStatement: e.newOnStatementHandler(),
OnMeterComputation: e.newOnMeterComputation(),
OnComputationRemaining: e.newOnComputationRemaining(),
OnFunctionInvocation: e.newOnFunctionInvocationHandler(),
OnInvokedFunctionReturn: e.newOnInvokedFunctionReturnHandler(),
CapabilityBorrowHandler: e.newCapabilityBorrowHandler(),
Expand All @@ -213,19 +216,33 @@ func (e *interpreterEnvironment) newCheckerConfig() *sema.Config {
}
}

func StandardLibraryOptionsFromConfig(config Config) stdlib.StandardLibraryOptions {
return stdlib.StandardLibraryOptions{
WebAssemblyEnabled: config.WebAssemblyEnabled,
}
}

func NewBaseInterpreterEnvironment(config Config) *interpreterEnvironment {
env := newInterpreterEnvironment(config)
for _, valueDeclaration := range stdlib.DefaultStandardLibraryValues(env) {
options := StandardLibraryOptionsFromConfig(config)
for _, valueDeclaration := range stdlib.DefaultStandardLibraryValues(env, options) {
env.DeclareValue(valueDeclaration, nil)
}
for _, typeDeclaration := range stdlib.DefaultStandardLibraryTypes(options) {
env.DeclareType(typeDeclaration, nil)
}
return env
}

func NewScriptInterpreterEnvironment(config Config) Environment {
env := newInterpreterEnvironment(config)
for _, valueDeclaration := range stdlib.DefaultScriptStandardLibraryValues(env) {
options := StandardLibraryOptionsFromConfig(config)
for _, valueDeclaration := range stdlib.DefaultScriptStandardLibraryValues(env, options) {
env.DeclareValue(valueDeclaration, nil)
}
for _, typeDeclaration := range stdlib.DefaultStandardLibraryTypes(options) {
env.DeclareType(typeDeclaration, nil)
}
return env
}

Expand Down Expand Up @@ -914,6 +931,10 @@ func (e *interpreterEnvironment) Hash(data []byte, tag string, algorithm sema.Ha
return e.runtimeInterface.Hash(data, tag, algorithm)
}

func (e *interpreterEnvironment) CompileWebAssembly(bytes []byte) (stdlib.WebAssemblyModule, error) {
return e.runtimeInterface.CompileWebAssembly(bytes)
}

func (e *interpreterEnvironment) DecodeArgument(argument []byte, argumentType cadence.Type) (cadence.Value, error) {
return e.runtimeInterface.DecodeArgument(argument, argumentType)
}
Expand Down Expand Up @@ -1150,6 +1171,16 @@ func (e *interpreterEnvironment) newOnMeterComputation() interpreter.OnMeterComp
}
}

func (e *interpreterEnvironment) newOnComputationRemaining() interpreter.OnComputationRemainingFunc {
return func(compKind common.ComputationKind) uint {
var remaining uint
errors.WrapPanic(func() {
remaining = e.runtimeInterface.ComputationRemaining(compKind)
})
return remaining
}
}

func (e *interpreterEnvironment) InterpretContract(
location common.AddressLocation,
program *interpreter.Program,
Expand Down
Loading
Loading