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

feat(wasmer): get and cache state version in instance context #2747

Merged
merged 6 commits into from
Aug 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions dot/core/messages_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ func createExtrinsic(t *testing.T, rt runtime.Instance, genHash common.Hash, non
err = ctypes.Decode(decoded, meta)
require.NoError(t, err)

rv, err := rt.Version()
require.NoError(t, err)
rv := rt.Version()

c, err := ctypes.NewCall(meta, "System.remark", []byte{0xab, 0xcd})
require.NoError(t, err)
Expand Down
5 changes: 2 additions & 3 deletions dot/core/mocks_runtime_test.go

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

2 changes: 1 addition & 1 deletion dot/core/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ func (s *Service) GetRuntimeVersion(bhash *common.Hash) (
}

rt.SetContextStorage(ts)
return rt.Version()
return rt.Version(), nil
}

// HandleSubmittedExtrinsic is used to send a Transaction message containing a Extrinsic @ext
Expand Down
15 changes: 5 additions & 10 deletions dot/core/service_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,7 @@ func TestService_GetRuntimeVersion(t *testing.T) {
rt, err := s.blockState.GetRuntime(nil)
require.NoError(t, err)

rtExpected, err := rt.Version()
require.NoError(t, err)
rtExpected := rt.Version()

rtv, err := s.GetRuntimeVersion(nil)
require.NoError(t, err)
Expand Down Expand Up @@ -577,8 +576,7 @@ func TestService_HandleRuntimeChanges(t *testing.T) {
rt, err := s.blockState.GetRuntime(nil)
require.NoError(t, err)

v, err := rt.Version()
require.NoError(t, err)
v := rt.Version()

currSpecVersion := v.SpecVersion // genesis runtime version.
hash := s.blockState.BestBlockHash() // genesisHash
Expand Down Expand Up @@ -613,8 +611,7 @@ func TestService_HandleRuntimeChanges(t *testing.T) {
parentRt, err := s.blockState.GetRuntime(&hash)
require.NoError(t, err)

v, err = parentRt.Version()
require.NoError(t, err)
v = parentRt.Version()
require.Equal(t, v.SpecVersion, currSpecVersion)

bhash1 := newBlock1.Header.Hash()
Expand All @@ -635,15 +632,13 @@ func TestService_HandleRuntimeChanges(t *testing.T) {
rt, err = s.blockState.GetRuntime(&bhash1)
require.NoError(t, err)

v, err = rt.Version()
require.NoError(t, err)
v = rt.Version()
require.Equal(t, v.SpecVersion, currSpecVersion)

rt, err = s.blockState.GetRuntime(&rtUpdateBhash)
require.NoError(t, err)

v, err = rt.Version()
require.NoError(t, err)
v = rt.Version()
require.Equal(t, v.SpecVersion, updatedSpecVersion)
}

Expand Down
7 changes: 2 additions & 5 deletions dot/state/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ func (bs *BlockState) HandleRuntimeChanges(newState *rtstorage.TrieState,
}

// only update runtime during code substitution if runtime SpecVersion is updated
previousVersion, _ := rt.Version()
previousVersion := rt.Version()
if previousVersion.SpecVersion == newVersion.SpecVersion {
logger.Info("not upgrading runtime code during code substitution")
bs.StoreRuntime(bHash, rt)
Expand Down Expand Up @@ -633,10 +633,7 @@ func (bs *BlockState) HandleRuntimeChanges(newState *rtstorage.TrieState,
return fmt.Errorf("failed to update code substituted block hash: %w", err)
}

newVersion, err := rt.Version()
if err != nil {
return fmt.Errorf("failed to retrieve runtime version: %w", err)
}
newVersion := rt.Version()
go bs.notifyRuntimeUpdated(newVersion)
return nil
}
Expand Down
5 changes: 2 additions & 3 deletions dot/sync/mock_instance_test.go

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

3 changes: 1 addition & 2 deletions lib/babe/build_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,7 @@ func TestBuildAndApplyExtrinsic(t *testing.T) {
err = ctypes.Decode(decoded, meta)
require.NoError(t, err)

rv, err := rt.Version()
require.NoError(t, err)
rv := rt.Version()

bob, err := ctypes.NewMultiAddressFromHexAccountID(
"0x90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22")
Expand Down
5 changes: 2 additions & 3 deletions lib/blocktree/mock_instance_test.go

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

4 changes: 3 additions & 1 deletion lib/runtime/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ type Instance interface {
SetContextStorage(s Storage) // used to set the TrieState before a runtime call

GetCodeHash() common.Hash
Version() (Version, error)
// Version returns the version from the runtime.
// This should return the cached version and be cheap to execute.
Version() (version Version)
Metadata() ([]byte, error)
BabeConfiguration() (*types.BabeConfiguration, error)
GrandpaAuthorities() ([]types.Authority, error)
Expand Down
11 changes: 2 additions & 9 deletions lib/runtime/mocks/instance.go

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

2 changes: 1 addition & 1 deletion lib/runtime/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func NewTestExtrinsic(t *testing.T, rt Instance, genHash, blockHash common.Hash,
err = ctypes.Decode(decoded, meta)
require.NoError(t, err)

rv, err := rt.Version()
rv := rt.Version()
require.NoError(t, err)

c, err := ctypes.NewCall(meta, call, args...)
Expand Down
1 change: 1 addition & 0 deletions lib/runtime/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type Context struct {
Transaction TransactionState
SigVerifier *crypto.SignatureVerifier
OffchainHTTPSet *offchain.HTTPSet
Version Version
}

// NewValidateTransactionError returns an error based on a return value from TaggedTransactionQueueValidateTransaction
Expand Down
2 changes: 2 additions & 0 deletions lib/runtime/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Version struct {
ImplVersion uint32
APIItems []APIItem
TransactionVersion uint32
StateVersion uint32
}

var (
Expand Down Expand Up @@ -63,6 +64,7 @@ func DecodeVersion(encoded []byte) (version Version, err error) {

optionalFields := [...]namedValue{
{name: "transaction version", value: &version.TransactionVersion},
{name: "state version", value: &version.StateVersion},
}
for _, optionalField := range optionalFields {
err = decoder.Decode(optionalField.value)
Expand Down
40 changes: 39 additions & 1 deletion lib/runtime/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ func Test_DecodeVersion(t *testing.T) {
// errWrapped: ErrDecoding,
// errMessage: "decoding transaction version: could not decode invalid integer",
// },
// TODO add state version decode error once
// https://github.com/ChainSafe/gossamer/pull/2683
// is merged.
// "state version decode error": {
// encoded: concatBytes([][]byte{
// scaleEncode(t, []byte("a")), // spec name
// scaleEncode(t, []byte("b")), // impl name
// scaleEncode(t, uint32(1)), // authoring version
// scaleEncode(t, uint32(2)), // spec version
// scaleEncode(t, uint32(3)), // impl version
// scaleEncode(t, []APIItem{{}}), // api items
// scaleEncode(t, uint32(4)), // transaction version
// {1, 2, 3}, // state version
// }),
// errWrapped: ErrDecoding,
// errMessage: "decoding state version: could not decode invalid integer",
// },
"no optional field set": {
encoded: []byte{
0x4, 0x1, 0x4, 0x2, 0x3, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0,
Expand Down Expand Up @@ -92,6 +109,25 @@ func Test_DecodeVersion(t *testing.T) {
TransactionVersion: 7,
},
},
"transaction and state versions set": {
encoded: []byte{
0x4, 0x1, 0x4, 0x2, 0x3, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0,
0x5, 0x0, 0x0, 0x0, 0x4, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x6, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0},
version: Version{
SpecName: []byte{1},
ImplName: []byte{2},
AuthoringVersion: 3,
SpecVersion: 4,
ImplVersion: 5,
APIItems: []APIItem{{
Name: [8]byte{1, 2, 3, 4, 5, 6, 7, 8},
Ver: 6,
}},
TransactionVersion: 7,
StateVersion: 4,
},
},
}

for name, testCase := range testCases {
Expand Down Expand Up @@ -130,11 +166,12 @@ func Test_Version_Scale(t *testing.T) {
Ver: 6,
}},
TransactionVersion: 7,
StateVersion: 4,
},
encoding: []byte{
0x4, 0x1, 0x4, 0x2, 0x3, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0,
0x5, 0x0, 0x0, 0x0, 0x4, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x6, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0},
0x8, 0x6, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0},
decoded: Version{
SpecName: []byte{1},
ImplName: []byte{2},
Expand All @@ -146,6 +183,7 @@ func Test_Version_Scale(t *testing.T) {
Ver: 6,
}},
TransactionVersion: 7,
StateVersion: 4,
},
},
}
Expand Down
1 change: 1 addition & 0 deletions lib/runtime/wasmer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ type Config struct {
Network runtime.BasicNetwork
Transaction runtime.TransactionState
CodeHash common.Hash
testVersion *runtime.Version
}
12 changes: 10 additions & 2 deletions lib/runtime/wasmer/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,16 @@ func (in *Instance) ValidateTransaction(e types.Extrinsic) (*transaction.Validit
return v, err
}

// Version calls runtime function Core_Version
func (in *Instance) Version() (version runtime.Version, err error) {
// Version returns the instance version.
// This is cheap to call since the instance version is cached.
// Note the instance version is set at creation and on code update.
func (in *Instance) Version() (version runtime.Version) {
return in.ctx.Version
}

// version calls runtime function Core_Version and returns the
// decoded version structure.
func (in *Instance) version() (version runtime.Version, err error) {
res, err := in.Exec(runtime.CoreVersion, []byte{})
if err != nil {
return version, err
Expand Down
5 changes: 2 additions & 3 deletions lib/runtime/wasmer/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func Test_Instance_Version(t *testing.T) {
t.Parallel()

type InstanceVersion interface {
Version() (runtime.Version, error)
Version() (version runtime.Version)
}

testCases := map[string]struct {
Expand Down Expand Up @@ -280,8 +280,7 @@ func Test_Instance_Version(t *testing.T) {
t.Parallel()

instance := testCase.instanceBuilder(t)
version, err := instance.Version()
require.NoError(t, err)
version := instance.Version()
assert.Equal(t, testCase.expectedVersion, version)
})
}
Expand Down
30 changes: 27 additions & 3 deletions lib/runtime/wasmer/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,25 @@ func NewInstance(code []byte, cfg Config) (instance *Instance, err error) {
}
wasmInstance.SetContextData(runtimeCtx)

return &Instance{
instance = &Instance{
vm: wasmInstance,
ctx: runtimeCtx,
codeHash: cfg.CodeHash,
}, nil
}

if cfg.testVersion != nil {
instance.ctx.Version = *cfg.testVersion
} else {
instance.ctx.Version, err = instance.version()
if err != nil {
instance.close()
return nil, fmt.Errorf("getting instance version: %w", err)
}
}

wasmInstance.SetContextData(instance.ctx)

return instance, nil
}

// decompressWasm decompresses a Wasm blob that may or may not be compressed with zstd
Expand Down Expand Up @@ -153,6 +167,16 @@ func (in *Instance) UpdateRuntimeCode(code []byte) (err error) {

in.vm = wasmInstance

// Find runtime instance version and cache it in its
// instance context.
version, err := in.version()
if err != nil {
in.close()
return fmt.Errorf("getting instance version: %w", err)
}
in.ctx.Version = version
wasmInstance.SetContextData(in.ctx)

return nil
}

Expand All @@ -168,7 +192,7 @@ func GetRuntimeVersion(code []byte) (version runtime.Version, err error) {
}
defer instance.Stop()

version, err = instance.Version()
version, err = instance.version()
if err != nil {
return version, fmt.Errorf("running runtime: %w", err)
}
Expand Down
Loading