Skip to content

Commit 491af95

Browse files
authored
chore(lib/runtime/wasmer): refactor Exec and exec methods (#2686)
- Remove nil storage check (temporary instances might not need it, and we should panic if this happens) - Fix behavior for pointers and/or data lengths larger than the max int32 value - Only use `Exec` instead of both directly wrapping `exec` - Inline single/2-lines functions content in `exec`: `malloc`, `clear`, `store`, `load` - Add sentinel errors and error wrappings - Add named returns
1 parent 2311061 commit 491af95

File tree

7 files changed

+57
-76
lines changed

7 files changed

+57
-76
lines changed

dot/rpc/websocket_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ var testCalls = []struct {
5050
expected: []byte(`{"jsonrpc":"2.0","result":3,"id":5}` + "\n")},
5151
{
5252
call: []byte(`{"jsonrpc":"2.0","method":"author_submitAndWatchExtrinsic","params":["0x010203"],"id":6}`),
53-
expected: []byte(`{"jsonrpc":"2.0","error":{"code":null,"message":"Failed to call the ` +
54-
"`" + `TaggedTransactionQueue_validate_transaction` + "`" + ` exported function."},"id":6}` + "\n")},
53+
expected: []byte(`{"jsonrpc":"2.0","error":{"code":null,` +
54+
`"message":"running runtime function: Failed to call the ` +
55+
"`" + `TaggedTransactionQueue_validate_transaction` + "`" +
56+
` exported function."},"id":6}` + "\n")},
5557
{
5658
call: []byte(`{"jsonrpc":"2.0","method":"state_subscribeRuntimeVersion","params":[],"id":7}`),
5759
expected: []byte(`{"jsonrpc":"2.0","result":6,"id":7}` + "\n")},

lib/runtime/errors.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,3 @@ var ErrInvalidTransaction = &json2.Error{Code: 1010, Message: "Invalid Transacti
1919
// ErrUnknownTransaction is returned if the call to runtime function
2020
// TaggedTransactionQueueValidateTransaction fails with value of [1, 1, x]
2121
var ErrUnknownTransaction = &json2.Error{Code: 1011, Message: "Unknown Transaction Validity"}
22-
23-
// ErrNilStorage is returned when the runtime context storage isn't set
24-
var ErrNilStorage = errors.New("runtime context storage is nil")

lib/runtime/wasmer/exports.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
// ValidateTransaction runs the extrinsic through the runtime function
1717
// TaggedTransactionQueue_validate_transaction and returns *Validity
1818
func (in *Instance) ValidateTransaction(e types.Extrinsic) (*transaction.Validity, error) {
19-
ret, err := in.exec(runtime.TaggedTransactionQueueValidateTransaction, e)
19+
ret, err := in.Exec(runtime.TaggedTransactionQueueValidateTransaction, e)
2020
if err != nil {
2121
return nil, err
2222
}
@@ -33,7 +33,7 @@ func (in *Instance) ValidateTransaction(e types.Extrinsic) (*transaction.Validit
3333

3434
// Version calls runtime function Core_Version
3535
func (in *Instance) Version() (runtime.Version, error) {
36-
res, err := in.exec(runtime.CoreVersion, []byte{})
36+
res, err := in.Exec(runtime.CoreVersion, []byte{})
3737
if err != nil {
3838
return nil, err
3939
}
@@ -56,12 +56,12 @@ func (in *Instance) Version() (runtime.Version, error) {
5656

5757
// Metadata calls runtime function Metadata_metadata
5858
func (in *Instance) Metadata() ([]byte, error) {
59-
return in.exec(runtime.Metadata, []byte{})
59+
return in.Exec(runtime.Metadata, []byte{})
6060
}
6161

6262
// BabeConfiguration gets the configuration data for BABE from the runtime
6363
func (in *Instance) BabeConfiguration() (*types.BabeConfiguration, error) {
64-
data, err := in.exec(runtime.BabeAPIConfiguration, []byte{})
64+
data, err := in.Exec(runtime.BabeAPIConfiguration, []byte{})
6565
if err != nil {
6666
return nil, err
6767
}
@@ -77,7 +77,7 @@ func (in *Instance) BabeConfiguration() (*types.BabeConfiguration, error) {
7777

7878
// GrandpaAuthorities returns the genesis authorities from the runtime
7979
func (in *Instance) GrandpaAuthorities() ([]types.Authority, error) {
80-
ret, err := in.exec(runtime.GrandpaAuthorities, []byte{})
80+
ret, err := in.Exec(runtime.GrandpaAuthorities, []byte{})
8181
if err != nil {
8282
return nil, err
8383
}
@@ -98,23 +98,23 @@ func (in *Instance) InitializeBlock(header *types.Header) error {
9898
return fmt.Errorf("cannot encode header: %w", err)
9999
}
100100

101-
_, err = in.exec(runtime.CoreInitializeBlock, encodedHeader)
101+
_, err = in.Exec(runtime.CoreInitializeBlock, encodedHeader)
102102
return err
103103
}
104104

105105
// InherentExtrinsics calls runtime API function BlockBuilder_inherent_extrinsics
106106
func (in *Instance) InherentExtrinsics(data []byte) ([]byte, error) {
107-
return in.exec(runtime.BlockBuilderInherentExtrinsics, data)
107+
return in.Exec(runtime.BlockBuilderInherentExtrinsics, data)
108108
}
109109

110110
// ApplyExtrinsic calls runtime API function BlockBuilder_apply_extrinsic
111111
func (in *Instance) ApplyExtrinsic(data types.Extrinsic) ([]byte, error) {
112-
return in.exec(runtime.BlockBuilderApplyExtrinsic, data)
112+
return in.Exec(runtime.BlockBuilderApplyExtrinsic, data)
113113
}
114114

115115
// FinalizeBlock calls runtime API function BlockBuilder_finalize_block
116116
func (in *Instance) FinalizeBlock() (*types.Header, error) {
117-
data, err := in.exec(runtime.BlockBuilderFinalizeBlock, []byte{})
117+
data, err := in.Exec(runtime.BlockBuilderFinalizeBlock, []byte{})
118118
if err != nil {
119119
return nil, err
120120
}
@@ -161,7 +161,7 @@ func (in *Instance) ExecuteBlock(block *types.Block) ([]byte, error) {
161161

162162
// DecodeSessionKeys decodes the given public session keys. Returns a list of raw public keys including their key type.
163163
func (in *Instance) DecodeSessionKeys(enc []byte) ([]byte, error) {
164-
return in.exec(runtime.DecodeSessionKeys, enc)
164+
return in.Exec(runtime.DecodeSessionKeys, enc)
165165
}
166166

167167
// PaymentQueryInfo returns information of a given extrinsic
@@ -171,7 +171,7 @@ func (in *Instance) PaymentQueryInfo(ext []byte) (*types.TransactionPaymentQuery
171171
return nil, err
172172
}
173173

174-
resBytes, err := in.exec(runtime.TransactionPaymentAPIQueryInfo, append(ext, encLen...))
174+
resBytes, err := in.Exec(runtime.TransactionPaymentAPIQueryInfo, append(ext, encLen...))
175175
if err != nil {
176176
return nil, err
177177
}

lib/runtime/wasmer/exports_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package wasmer
55

66
import (
77
"encoding/json"
8-
"errors"
98
"fmt"
109
"math/big"
1110
"os"
@@ -1022,15 +1021,14 @@ func TestInstance_DecodeSessionKeys(t *testing.T) {
10221021

10231022
func TestInstance_PaymentQueryInfo(t *testing.T) {
10241023
tests := []struct {
1025-
extB []byte
1026-
ext string
1027-
err error
1028-
expect *types.TransactionPaymentQueryInfo
1024+
extB []byte
1025+
ext string
1026+
errMessage string
1027+
expect *types.TransactionPaymentQueryInfo
10291028
}{
10301029
{
10311030
// Was made with @polkadot/api on https://github.com/danforbes/polkadot-js-scripts/tree/create-signed-tx
10321031
ext: "0xd1018400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01bc2b6e35929aabd5b8bc4e5b0168c9bee59e2bb9d6098769f6683ecf73e44c776652d947a270d59f3d37eb9f9c8c17ec1b4cc473f2f9928ffdeef0f3abd43e85d502000000012844616e20466f72626573", //nolint:lll
1033-
err: nil,
10341032
expect: &types.TransactionPaymentQueryInfo{
10351033
Weight: 1973000,
10361034
Class: 0,
@@ -1043,12 +1041,14 @@ func TestInstance_PaymentQueryInfo(t *testing.T) {
10431041
{
10441042
// incomplete extrinsic
10451043
ext: "0x4ccde39a5684e7a56da23b22d4d9fbadb023baa19c56495432884d0640000000000000000000000000000000",
1046-
err: errors.New("Failed to call the `TransactionPaymentApi_query_info` exported function."), //nolint:revive
1044+
errMessage: "running runtime function: " +
1045+
"Failed to call the `TransactionPaymentApi_query_info` exported function.",
10471046
},
10481047
{
10491048
// incomplete extrinsic
10501049
extB: nil,
1051-
err: errors.New("Failed to call the `TransactionPaymentApi_query_info` exported function."), //nolint:revive
1050+
errMessage: "running runtime function: " +
1051+
"Failed to call the `TransactionPaymentApi_query_info` exported function.",
10521052
},
10531053
}
10541054

@@ -1066,11 +1066,11 @@ func TestInstance_PaymentQueryInfo(t *testing.T) {
10661066
ins := NewTestInstance(t, runtime.NODE_RUNTIME)
10671067
info, err := ins.PaymentQueryInfo(extBytes)
10681068

1069-
if test.err != nil {
1070-
require.Error(t, err)
1071-
require.Equal(t, err.Error(), test.err.Error())
1069+
if test.errMessage != "" {
1070+
assert.EqualError(t, err, test.errMessage)
10721071
continue
10731072
}
1073+
require.NoError(t, err)
10741074

10751075
fmt.Println(info.PartialFee.String())
10761076
fmt.Println(test.expect.PartialFee.String())

lib/runtime/wasmer/imports_test.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ func Test_ext_offchain_timestamp_version_1(t *testing.T) {
4141
res, err := runtimeFunc(0, 0)
4242
require.NoError(t, err)
4343

44-
offset, length := runtime.Int64ToPointerAndSize(res.ToI64())
45-
data := inst.load(offset, length)
44+
outputPtr, outputLength := runtime.Int64ToPointerAndSize(res.ToI64())
45+
memory := inst.vm.Memory.Data()
46+
data := memory[outputPtr : outputPtr+outputLength]
4647
var timestamp int64
4748
err = scale.Unmarshal(data, &timestamp)
4849
require.NoError(t, err)
@@ -718,10 +719,12 @@ func Test_ext_crypto_ed25519_generate_version_1(t *testing.T) {
718719
// we manually store and call the runtime function here since inst.exec assumes
719720
// the data returned from the function is a pointer-size, but for ext_crypto_ed25519_generate_version_1,
720721
// it's just a pointer
721-
ptr, err := inst.malloc(uint32(len(params)))
722+
ptr, err := inst.ctx.Allocator.Allocate(uint32(len(params)))
722723
require.NoError(t, err)
723724

724-
inst.store(params, int32(ptr))
725+
memory := inst.vm.Memory.Data()
726+
copy(memory[ptr:ptr+uint32(len(params))], params)
727+
725728
dataLen := int32(len(params))
726729

727730
runtimeFunc, ok := inst.vm.Exports["rtm_ext_crypto_ed25519_generate_version_1"]
@@ -921,15 +924,15 @@ func Test_ext_crypto_ecdsa_verify_version_2_Table(t *testing.T) {
921924
key: []byte{132, 2, 39, 55, 134, 131, 142, 43, 100, 63, 134, 96, 14, 253, 15, 222, 119, 154, 110, 188, 20, 159, 62, 125, 42, 59, 127, 19, 16, 0, 161, 236, 109}, //nolint:lll
922925
err: wasmer.NewExportedFunctionError(
923926
"rtm_ext_crypto_ecdsa_verify_version_2",
924-
"Failed to call the `%s` exported function."),
927+
"running runtime function: Failed to call the `%s` exported function."),
925928
},
926929
"invalid message": {
927930
sig: []byte{5, 1, 187, 179, 88, 183, 46, 115, 242, 32, 9, 54, 141, 207, 44, 15, 238, 42, 217, 196, 111, 173, 239, 204, 128, 93, 49, 179, 137, 150, 162, 125, 226, 225, 28, 145, 122, 127, 15, 154, 185, 11, 3, 66, 27, 187, 204, 242, 107, 68, 26, 111, 245, 30, 115, 141, 85, 74, 158, 211, 161, 217, 43, 151, 120, 125, 1}, //nolint:lll
928931
msg: []byte{48, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100},
929932
key: []byte{132, 2, 39, 206, 55, 134, 131, 142, 43, 100, 63, 134, 96, 14, 253, 15, 222, 119, 154, 110, 188, 20, 159, 62, 125, 42, 59, 127, 19, 16, 0, 161, 236, 109}, //nolint:lll
930933
err: wasmer.NewExportedFunctionError(
931934
"rtm_ext_crypto_ecdsa_verify_version_2",
932-
"Failed to call the `%s` exported function."),
935+
"running runtime function: Failed to call the `%s` exported function."),
933936
},
934937
}
935938
for name, tc := range testCases {
@@ -1592,7 +1595,8 @@ func Test_ext_default_child_storage_storage_kill_version_3(t *testing.T) {
15921595
key: []byte(`fakekey`),
15931596
limit: optLimit2,
15941597
expected: []byte{0, 0, 0, 0, 0},
1595-
errMsg: "Failed to call the `rtm_ext_default_child_storage_storage_kill_version_3` exported function.",
1598+
errMsg: "running runtime function: " +
1599+
"Failed to call the `rtm_ext_default_child_storage_storage_kill_version_3` exported function.",
15961600
},
15971601
{key: testChildKey, limit: optLimit2, expected: []byte{1, 2, 0, 0, 0}},
15981602
{key: testChildKey, limit: nil, expected: []byte{0, 1, 0, 0, 0}},

lib/runtime/wasmer/instance.go

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -264,67 +264,45 @@ func (in *Instance) Stop() {
264264
}
265265
}
266266

267-
// Store func
268-
func (in *Instance) store(data []byte, location int32) {
269-
mem := in.vm.Memory.Data()
270-
copy(mem[location:location+int32(len(data))], data)
271-
}
272-
273-
// Load load
274-
func (in *Instance) load(location, length int32) []byte {
275-
mem := in.vm.Memory.Data()
276-
return mem[location : location+length]
277-
}
267+
var (
268+
ErrInstanceIsStopped = errors.New("instance is stopped")
269+
ErrExportFunctionNotFound = errors.New("export function not found")
270+
)
278271

279272
// Exec calls the given function with the given data
280-
func (in *Instance) Exec(function string, data []byte) ([]byte, error) {
281-
return in.exec(function, data)
282-
}
283-
284-
// Exec func
285-
func (in *Instance) exec(function string, data []byte) ([]byte, error) {
286-
if in.ctx.Storage == nil {
287-
return nil, runtime.ErrNilStorage
288-
}
289-
273+
func (in *Instance) Exec(function string, data []byte) (result []byte, err error) {
290274
in.Lock()
291275
defer in.Unlock()
292276

293277
if in.isClosed {
294-
return nil, errors.New("instance is stopped")
278+
return nil, ErrInstanceIsStopped
295279
}
296280

297-
ptr, err := in.malloc(uint32(len(data)))
281+
dataLength := uint32(len(data))
282+
inputPtr, err := in.ctx.Allocator.Allocate(dataLength)
298283
if err != nil {
299-
return nil, err
284+
return nil, fmt.Errorf("allocating input memory: %w", err)
300285
}
301286

302-
defer in.clear()
287+
defer in.ctx.Allocator.Clear()
303288

304289
// Store the data into memory
305-
in.store(data, int32(ptr))
306-
datalen := int32(len(data))
290+
memory := in.vm.Memory.Data()
291+
copy(memory[inputPtr:inputPtr+dataLength], data)
307292

308293
runtimeFunc, ok := in.vm.Exports[function]
309294
if !ok {
310-
return nil, fmt.Errorf("could not find exported function %s", function)
295+
return nil, fmt.Errorf("%w: %s", ErrExportFunctionNotFound, function)
311296
}
312297

313-
res, err := runtimeFunc(int32(ptr), datalen)
298+
wasmValue, err := runtimeFunc(int32(inputPtr), int32(dataLength))
314299
if err != nil {
315-
return nil, err
300+
return nil, fmt.Errorf("running runtime function: %w", err)
316301
}
317302

318-
offset, length := runtime.Int64ToPointerAndSize(res.ToI64())
319-
return in.load(offset, length), nil
320-
}
321-
322-
func (in *Instance) malloc(size uint32) (uint32, error) {
323-
return in.ctx.Allocator.Allocate(size)
324-
}
325-
326-
func (in *Instance) clear() {
327-
in.ctx.Allocator.Clear()
303+
outputPtr, outputLength := runtime.Int64ToPointerAndSize(wasmValue.ToI64())
304+
memory = in.vm.Memory.Data() // call Data() again to get larger slice
305+
return memory[outputPtr : outputPtr+outputLength], nil
328306
}
329307

330308
// NodeStorage to get reference to runtime node service

lib/runtime/wasmer/instance_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ func TestConcurrentRuntimeCalls(t *testing.T) {
2020

2121
// execute 2 concurrent calls to the runtime
2222
go func() {
23-
_, _ = instance.exec(runtime.CoreVersion, []byte{})
23+
_, _ = instance.Exec(runtime.CoreVersion, []byte{})
2424
}()
2525
go func() {
26-
_, _ = instance.exec(runtime.CoreVersion, []byte{})
26+
_, _ = instance.Exec(runtime.CoreVersion, []byte{})
2727
}()
2828
}
2929

0 commit comments

Comments
 (0)