Skip to content

Commit

Permalink
feat(wasmer): get and cache state version in instance context (#2747)
Browse files Browse the repository at this point in the history
- Codec support for Core_version and state_version
- Cache core version in runtime instance context
  - Set when creating instance
  - Update when updating runtime code
- Handle `Core_version` call not present in host API test runtimes
  • Loading branch information
qdm12 authored Aug 24, 2022
1 parent 6552e65 commit 3fd63db
Show file tree
Hide file tree
Showing 19 changed files with 117 additions and 53 deletions.
3 changes: 1 addition & 2 deletions dot/core/messages_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ func createExtrinsic(t *testing.T, rt RuntimeInstance, genHash common.Hash, nonc
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

0 comments on commit 3fd63db

Please sign in to comment.