Skip to content

Commit

Permalink
lib/runtime: implement ext_local_storage_set and ext_local_storage_get (
Browse files Browse the repository at this point in the history
#1101)

* stub methods for local storage

* implement basic storage for runtime offchain workers

* cleanup comments, use database table instead of wrappers

* lint

* fix typo
  • Loading branch information
edwardmack authored Sep 22, 2020
1 parent e04eb77 commit 6d70dbe
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 26 deletions.
17 changes: 12 additions & 5 deletions dot/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"fmt"
"math/big"

database "github.com/ChainSafe/chaindb"

"github.com/ChainSafe/gossamer/dot/core"
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/rpc"
Expand Down Expand Up @@ -90,12 +92,17 @@ func createRuntime(cfg *Config, st *state.Service, ks *keystore.GenericKeystore)
return nil, err
}

ns := runtime.NodeStorage{
LocalStorage: database.NewMemDatabase(),
PersistentStorage: database.NewTable(st.DB(), "offlinestorage"),
}
rtCfg := &runtime.Config{
Storage: ts,
Keystore: ks,
Imports: runtime.RegisterImports_NodeRuntime,
LogLvl: lvl,
Role: cfg.Core.Roles,
Storage: ts,
Keystore: ks,
Imports: runtime.RegisterImports_NodeRuntime,
LogLvl: lvl,
NodeStorage: ns,
Role: cfg.Core.Roles,
}

// create runtime executor
Expand Down
47 changes: 44 additions & 3 deletions lib/runtime/imports_old.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,8 +728,32 @@ func ext_is_validator(context unsafe.Pointer) int32 {
//export ext_local_storage_get
func ext_local_storage_get(context unsafe.Pointer, kind, key, keyLen, valueLen int32) int32 {
logger.Trace("[ext_local_storage_get] executing...")
logger.Warn("[ext_local_storage_get] Not yet implemented.")
return 0
instanceContext := wasm.IntoInstanceContext(context)
memory := instanceContext.Memory().Data()

keyM := memory[key : key+keyLen]
runtimeCtx := instanceContext.Data().(*Ctx)
var res []byte
var err error
switch kind {
case NodeStorageTypePersistent:
res, err = runtimeCtx.nodeStorage.PersistentStorage.Get(keyM)
case NodeStorageTypeLocal:
res, err = runtimeCtx.nodeStorage.LocalStorage.Get(keyM)
}

if err != nil {
logger.Error("[ext_local_storage_get]", "error", err)
return 0
}
// allocate memory for value and copy value to memory
ptr, err := runtimeCtx.allocator.Allocate(uint32(valueLen))
if err != nil {
logger.Error("[ext_local_storage_get]", "error", err)
return 0
}
copy(memory[ptr:ptr+uint32(valueLen)], res[:])
return int32(ptr)
}

//export ext_local_storage_compare_and_set
Expand All @@ -756,7 +780,24 @@ func ext_submit_transaction(context unsafe.Pointer, data, len int32) int32 {
//export ext_local_storage_set
func ext_local_storage_set(context unsafe.Pointer, kind, key, keyLen, value, valueLen int32) {
logger.Trace("[ext_local_storage_set] executing...")
logger.Warn("[ext_local_storage_set] Not yet implemented.")
instanceContext := wasm.IntoInstanceContext(context)
memory := instanceContext.Memory().Data()

keyM := memory[key : key+keyLen]
valueM := memory[value : value+valueLen]

runtimeCtx := instanceContext.Data().(*Ctx)

var err error
switch kind {
case NodeStorageTypePersistent:
err = runtimeCtx.nodeStorage.PersistentStorage.Put(keyM, valueM)
case NodeStorageTypeLocal:
err = runtimeCtx.nodeStorage.LocalStorage.Put(keyM, valueM)
}
if err != nil {
logger.Error("[ext_local_storage_set]", "error", err)
}
}

// RegisterImports_TestRuntime registers the wasm imports for the v0.6.x substrate test runtime
Expand Down
113 changes: 112 additions & 1 deletion lib/runtime/imports_old_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,118 @@ func TestExt_set_child_storage(t *testing.T) {
}
}

func TestExt_local_storage_set_local(t *testing.T) {
runtime := NewTestRuntime(t, TEST_RUNTIME)

mem := runtime.vm.Memory.Data()

key := []byte("mykey")
value := []byte("myvalue")

keyPtr := 0
keyLen := len(key)
valuePtr := keyPtr + keyLen
valueLen := len(value)

copy(mem[keyPtr:keyPtr+keyLen], key)
copy(mem[valuePtr:valuePtr+valueLen], value)

// call wasm function
testFunc, ok := runtime.vm.Exports["test_ext_local_storage_set"]
if !ok {
t.Fatal("could not find exported function")
}

_, err := testFunc(NodeStorageTypeLocal, keyPtr, keyLen, valuePtr, valueLen)
require.NoError(t, err)

resValue, err := runtime.ctx.nodeStorage.LocalStorage.Get(key)
require.NoError(t, err)
require.Equal(t, value, resValue)
}

func TestExt_local_storage_set_persistent(t *testing.T) {
runtime := NewTestRuntime(t, TEST_RUNTIME)

mem := runtime.vm.Memory.Data()

key := []byte("mykey")
value := []byte("myvalue")

keyPtr := 0
keyLen := len(key)
valuePtr := keyPtr + keyLen
valueLen := len(value)

copy(mem[keyPtr:keyPtr+keyLen], key)
copy(mem[valuePtr:valuePtr+valueLen], value)

// call wasm function
testFunc, ok := runtime.vm.Exports["test_ext_local_storage_set"]
if !ok {
t.Fatal("could not find exported function")
}

_, err := testFunc(NodeStorageTypePersistent, keyPtr, keyLen, valuePtr, valueLen)
require.NoError(t, err)

resValue, err := runtime.ctx.nodeStorage.PersistentStorage.Get(key)
require.NoError(t, err)
require.Equal(t, value, resValue)
}

func TestExt_local_storage_get_local(t *testing.T) {
runtime := NewTestRuntime(t, TEST_RUNTIME)
mem := runtime.vm.Memory.Data()

key := []byte("mykey")
value := []byte("myvalue")
runtime.ctx.nodeStorage.LocalStorage.Put(key, value)

keyPtr := 0
keyLen := len(key)
valueLen := len(value)

copy(mem[keyPtr:keyPtr+keyLen], key)

// call wasm function
testFunc, ok := runtime.vm.Exports["test_ext_local_storage_get"]
if !ok {
t.Fatal("could not find exported function")
}

res, err := testFunc(NodeStorageTypeLocal, keyPtr, keyLen, valueLen)
require.Nil(t, err)

require.Equal(t, value, mem[res.ToI32():res.ToI32()+int32(valueLen)])
}

func TestExt_local_storage_get_persistent(t *testing.T) {
runtime := NewTestRuntime(t, TEST_RUNTIME)
mem := runtime.vm.Memory.Data()

key := []byte("mykey")
value := []byte("myvalue")
runtime.ctx.nodeStorage.PersistentStorage.Put(key, value)

keyPtr := 0
keyLen := len(key)
valueLen := len(value)

copy(mem[keyPtr:keyPtr+keyLen], key)

// call wasm function
testFunc, ok := runtime.vm.Exports["test_ext_local_storage_get"]
if !ok {
t.Fatal("could not find exported function")
}

res, err := testFunc(NodeStorageTypePersistent, keyPtr, keyLen, valueLen)
require.Nil(t, err)

require.Equal(t, value, mem[res.ToI32():res.ToI32()+int32(valueLen)])
}

func TestExt_is_validator(t *testing.T) {
// test with validator
runtime := NewTestRuntimeWithRole(t, TEST_RUNTIME, byte(4))
Expand All @@ -1154,5 +1266,4 @@ func TestExt_is_validator(t *testing.T) {
res, err = testFunc()
require.NoError(t, err)
require.Equal(t, int32(0), res.ToI32())

}
6 changes: 6 additions & 0 deletions lib/runtime/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,9 @@ type Storage interface {
SetBalance(key [32]byte, balance uint64) error
GetBalance(key [32]byte) (uint64, error)
}

// BasicStorage interface for functions used by runtime offchain workers
type BasicStorage interface {
Put(key []byte, value []byte) error
Get(key []byte) ([]byte, error)
}
41 changes: 28 additions & 13 deletions lib/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,35 @@ import (
var memory, memErr = wasm.NewMemory(17, 0)
var logger = log.New("pkg", "runtime")

// NodeStorageTypePersistent flag to identify offchain storage as persistent (db)
const NodeStorageTypePersistent int32 = 1

// NodeStorageTypeLocal flog to identify offchain storage as local (memory)
const NodeStorageTypeLocal int32 = 2

// NodeStorage struct for storage of runtime offchain worker data
type NodeStorage struct {
LocalStorage BasicStorage
PersistentStorage BasicStorage
}

// Ctx struct
type Ctx struct {
storage Storage
allocator *FreeingBumpHeapAllocator
keystore *keystore.GenericKeystore
validator bool
storage Storage
allocator *FreeingBumpHeapAllocator
keystore *keystore.GenericKeystore
nodeStorage NodeStorage
validator bool
}

// Config represents a runtime configuration
type Config struct {
Storage Storage
Keystore *keystore.GenericKeystore
Imports func() (*wasm.Imports, error)
LogLvl log.Lvl
Role byte
Storage Storage
Keystore *keystore.GenericKeystore
Imports func() (*wasm.Imports, error)
LogLvl log.Lvl
NodeStorage NodeStorage
Role byte
}

// Runtime struct
Expand Down Expand Up @@ -100,10 +114,11 @@ func NewRuntime(code []byte, cfg *Config) (*Runtime, error) {
}

runtimeCtx := &Ctx{
storage: cfg.Storage,
allocator: memAllocator,
keystore: cfg.Keystore,
validator: validator,
storage: cfg.Storage,
allocator: memAllocator,
keystore: cfg.Keystore,
nodeStorage: cfg.NodeStorage,
validator: validator,
}

logger.Debug("NewRuntime", "runtimeCtx", runtimeCtx)
Expand Down
14 changes: 10 additions & 4 deletions lib/runtime/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"path/filepath"
"testing"

database "github.com/ChainSafe/chaindb"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/keystore"
"github.com/ChainSafe/gossamer/lib/trie"
Expand Down Expand Up @@ -55,11 +56,16 @@ func NewTestRuntimeWithTrie(t *testing.T, targetRuntime string, tt *trie.Trie, l
fp, err := filepath.Abs(testRuntimeFilePath)
require.Nil(t, err, "could not create testRuntimeFilePath", "targetRuntime", targetRuntime)

ns := NodeStorage{
LocalStorage: database.NewMemDatabase(),
PersistentStorage: database.NewMemDatabase(), // we're using a local storage here since this is a test runtime
}
cfg := &Config{
Storage: s,
Keystore: keystore.NewGenericKeystore("test"),
Imports: importsFunc,
LogLvl: lvl,
Storage: s,
Keystore: keystore.NewGenericKeystore("test"),
Imports: importsFunc,
LogLvl: lvl,
NodeStorage: ns,
}

r, err := NewRuntimeFromFile(fp, cfg)
Expand Down

0 comments on commit 6d70dbe

Please sign in to comment.