Skip to content

Commit

Permalink
Merge pull request #353 from OffchainLabs/cross-compile
Browse files Browse the repository at this point in the history
support cross-compilation of stylus programs
  • Loading branch information
tsahee authored Aug 29, 2024
2 parents 85dc1b7 + 81114dd commit fb79423
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 46 deletions.
4 changes: 2 additions & 2 deletions arbitrum/apibackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ func createRegisterAPIBackend(backend *Backend, filterConfig filters.Config, fal
// discard stylus-tag on any call made from api database
dbForAPICalls := backend.chainDb
wasmStore, tag := backend.chainDb.WasmDataBase()
if tag != 0 {
dbForAPICalls = rawdb.WrapDatabaseWithWasm(backend.chainDb, wasmStore, 0)
if tag != 0 || len(backend.chainDb.WasmTargets()) > 1 {
dbForAPICalls = rawdb.WrapDatabaseWithWasm(backend.chainDb, wasmStore, 0, []ethdb.WasmTarget{rawdb.LocalTarget()})
}
backend.apiBackend = &APIBackend{
b: backend,
Expand Down
2 changes: 1 addition & 1 deletion arbitrum/recordingdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ func (r *RecordingDatabase) PrepareRecording(ctx context.Context, lastBlockHeade
defer func() { r.Dereference(finalDereference) }()
recordingKeyValue := newRecordingKV(r.db.TrieDB(), r.db.DiskDB())

recordingStateDatabase := state.NewDatabase(rawdb.WrapDatabaseWithWasm(rawdb.NewDatabase(recordingKeyValue), r.db.WasmStore(), 0))
recordingStateDatabase := state.NewDatabase(rawdb.WrapDatabaseWithWasm(rawdb.NewDatabase(recordingKeyValue), r.db.WasmStore(), 0, r.db.WasmTargets()))
var prevRoot common.Hash
if lastBlockHeader != nil {
prevRoot = lastBlockHeader.Root
Expand Down
34 changes: 15 additions & 19 deletions core/rawdb/accessors_state_arbitrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,14 @@ import (
"github.com/ethereum/go-ethereum/log"
)

type Target string

const (
TargetWavm Target = "wavm"
TargetArm64 Target = "arm64"
TargetAmd64 Target = "amd64"
TargetHost Target = "host"
TargetWavm ethdb.WasmTarget = "wavm"
TargetArm64 ethdb.WasmTarget = "arm64"
TargetAmd64 ethdb.WasmTarget = "amd64"
TargetHost ethdb.WasmTarget = "host"
)

func LocalTarget() Target {
func LocalTarget() ethdb.WasmTarget {
if runtime.GOOS == "linux" {
switch runtime.GOARCH {
case "arm64":
Expand All @@ -46,9 +44,9 @@ func LocalTarget() Target {
return TargetHost
}

func (t Target) keyPrefix() (WasmPrefix, error) {
func activatedAsmKeyPrefix(target ethdb.WasmTarget) (WasmPrefix, error) {
var prefix WasmPrefix
switch t {
switch target {
case TargetWavm:
prefix = activatedAsmWavmPrefix
case TargetArm64:
Expand All @@ -58,27 +56,25 @@ func (t Target) keyPrefix() (WasmPrefix, error) {
case TargetHost:
prefix = activatedAsmHostPrefix
default:
return WasmPrefix{}, fmt.Errorf("invalid target: %v", t)
return WasmPrefix{}, fmt.Errorf("invalid target: %v", target)
}
return prefix, nil
}

func (t Target) IsValid() bool {
_, err := t.keyPrefix()
func IsSupportedWasmTarget(target ethdb.WasmTarget) bool {
_, err := activatedAsmKeyPrefix(target)
return err == nil
}

var Targets = []Target{TargetWavm, TargetArm64, TargetAmd64, TargetHost}

func WriteActivation(db ethdb.KeyValueWriter, moduleHash common.Hash, asmMap map[Target][]byte) {
func WriteActivation(db ethdb.KeyValueWriter, moduleHash common.Hash, asmMap map[ethdb.WasmTarget][]byte) {
for target, asm := range asmMap {
WriteActivatedAsm(db, target, moduleHash, asm)
}
}

// Stores the activated asm for a given moduleHash and target
func WriteActivatedAsm(db ethdb.KeyValueWriter, target Target, moduleHash common.Hash, asm []byte) {
prefix, err := target.keyPrefix()
func WriteActivatedAsm(db ethdb.KeyValueWriter, target ethdb.WasmTarget, moduleHash common.Hash, asm []byte) {
prefix, err := activatedAsmKeyPrefix(target)
if err != nil {
log.Crit("Failed to store activated wasm asm", "err", err)
}
Expand All @@ -89,8 +85,8 @@ func WriteActivatedAsm(db ethdb.KeyValueWriter, target Target, moduleHash common
}

// Retrieves the activated asm for a given moduleHash and target
func ReadActivatedAsm(db ethdb.KeyValueReader, target Target, moduleHash common.Hash) []byte {
prefix, err := target.keyPrefix()
func ReadActivatedAsm(db ethdb.KeyValueReader, target ethdb.WasmTarget, moduleHash common.Hash) []byte {
prefix, err := activatedAsmKeyPrefix(target)
if err != nil {
log.Crit("Failed to read activated wasm asm", "err", err)
}
Expand Down
19 changes: 15 additions & 4 deletions core/rawdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ type freezerdb struct {
ethdb.AncientStore
}

// AncientDatadir returns the path of root ancient directory.
func (frdb *freezerdb) WasmDataBase() (ethdb.KeyValueStore, uint32) {
return frdb, 0
}

func (frdb *freezerdb) WasmTargets() []ethdb.WasmTarget {
return nil
}

// AncientDatadir returns the path of root ancient directory.
func (frdb *freezerdb) AncientDatadir() (string, error) {
return frdb.ancientRoot, nil
Expand Down Expand Up @@ -170,11 +173,14 @@ func (db *nofreezedb) AncientDatadir() (string, error) {
return "", errNotSupported
}

// AncientDatadir returns the path of root ancient directory.
func (db *nofreezedb) WasmDataBase() (ethdb.KeyValueStore, uint32) {
return db, 0
}

func (db *nofreezedb) WasmTargets() []ethdb.WasmTarget {
return nil
}

// NewDatabase creates a high level database on top of a given key-value data
// store without a freezer moving immutable chain segments into cold storage.
func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
Expand All @@ -185,12 +191,17 @@ type dbWithWasmEntry struct {
ethdb.Database
wasmDb ethdb.KeyValueStore
wasmCacheTag uint32
wasmTargets []ethdb.WasmTarget
}

func (db *dbWithWasmEntry) WasmDataBase() (ethdb.KeyValueStore, uint32) {
return db.wasmDb, db.wasmCacheTag
}

func (db *dbWithWasmEntry) WasmTargets() []ethdb.WasmTarget {
return db.wasmTargets
}

func (db *dbWithWasmEntry) Close() error {
dbErr := db.Database.Close()
wasmErr := db.wasmDb.Close()
Expand All @@ -200,8 +211,8 @@ func (db *dbWithWasmEntry) Close() error {
return wasmErr
}

func WrapDatabaseWithWasm(db ethdb.Database, wasm ethdb.KeyValueStore, cacheTag uint32) ethdb.Database {
return &dbWithWasmEntry{db, wasm, cacheTag}
func WrapDatabaseWithWasm(db ethdb.Database, wasm ethdb.KeyValueStore, cacheTag uint32, targets []ethdb.WasmTarget) ethdb.Database {
return &dbWithWasmEntry{db, wasm, cacheTag, targets}
}

// resolveChainFreezerDir is a helper function which resolves the absolute path
Expand Down
4 changes: 4 additions & 0 deletions core/rawdb/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func (t *table) WasmDataBase() (ethdb.KeyValueStore, uint32) {
return t.db.WasmDataBase()
}

func (t *table) WasmTargets() []ethdb.WasmTarget {
return t.db.WasmTargets()
}

// Has retrieves if a prefixed version of a key is present in the database.
func (t *table) Has(key []byte) (bool, error) {
return t.db.Has(append([]byte(t.prefix), key...))
Expand Down
24 changes: 16 additions & 8 deletions core/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ const (
// Database wraps access to tries and contract code.
type Database interface {
// Arbitrum: Read activated Stylus contracts
ActivatedAsm(target rawdb.Target, moduleHash common.Hash) (asm []byte, err error)
ActivatedAsm(target ethdb.WasmTarget, moduleHash common.Hash) (asm []byte, err error)
WasmStore() ethdb.KeyValueStore
WasmCacheTag() uint32
WasmTargets() []ethdb.WasmTarget

// OpenTrie opens the main account trie.
OpenTrie(root common.Hash) (Trie, error)
Expand Down Expand Up @@ -163,8 +164,9 @@ func NewDatabaseWithConfig(db ethdb.Database, config *triedb.Config) Database {
wasmdb, wasmTag := db.WasmDataBase()
cdb := &cachingDB{
// Arbitrum only
activatedAsmCache: lru.NewSizeConstrainedCache[activatedAsmCacheKey, []byte](activatedWasmCacheSize),
wasmTag: wasmTag,
activatedAsmCache: lru.NewSizeConstrainedCache[activatedAsmCacheKey, []byte](activatedWasmCacheSize),
wasmTag: wasmTag,
wasmDatabaseRetriever: db,

disk: db,
wasmdb: wasmdb,
Expand All @@ -180,8 +182,9 @@ func NewDatabaseWithNodeDB(db ethdb.Database, triedb *triedb.Database) Database
wasmdb, wasmTag := db.WasmDataBase()
cdb := &cachingDB{
// Arbitrum only
activatedAsmCache: lru.NewSizeConstrainedCache[activatedAsmCacheKey, []byte](activatedWasmCacheSize),
wasmTag: wasmTag,
activatedAsmCache: lru.NewSizeConstrainedCache[activatedAsmCacheKey, []byte](activatedWasmCacheSize),
wasmTag: wasmTag,
wasmDatabaseRetriever: db,

disk: db,
wasmdb: wasmdb,
Expand All @@ -194,13 +197,14 @@ func NewDatabaseWithNodeDB(db ethdb.Database, triedb *triedb.Database) Database

type activatedAsmCacheKey struct {
moduleHash common.Hash
target rawdb.Target
target ethdb.WasmTarget
}

type cachingDB struct {
// Arbitrum
activatedAsmCache *lru.SizeConstrainedCache[activatedAsmCacheKey, []byte]
wasmTag uint32
activatedAsmCache *lru.SizeConstrainedCache[activatedAsmCacheKey, []byte]
wasmTag uint32
wasmDatabaseRetriever ethdb.WasmDataBaseRetriever

disk ethdb.KeyValueStore
wasmdb ethdb.KeyValueStore
Expand All @@ -217,6 +221,10 @@ func (db *cachingDB) WasmCacheTag() uint32 {
return db.wasmTag
}

func (db *cachingDB) WasmTargets() []ethdb.WasmTarget {
return db.wasmDatabaseRetriever.WasmTargets()
}

// OpenTrie opens the main account trie at a specific root hash.
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
if db.triedb.IsVerkle() {
Expand Down
3 changes: 2 additions & 1 deletion core/state/database_arbitrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb"
)

func (db *cachingDB) ActivatedAsm(target rawdb.Target, moduleHash common.Hash) ([]byte, error) {
func (db *cachingDB) ActivatedAsm(target ethdb.WasmTarget, moduleHash common.Hash) ([]byte, error) {
cacheKey := activatedAsmCacheKey{moduleHash, target}
if asm, _ := db.activatedAsmCache.Get(cacheKey); len(asm) > 0 {
return asm, nil
Expand Down
14 changes: 7 additions & 7 deletions core/state/statedb_arbitrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
Expand All @@ -50,7 +50,7 @@ var (
StylusDiscriminant = []byte{stylusEOFMagic, stylusEOFMagicSuffix, stylusEOFVersion}
)

type ActivatedWasm map[rawdb.Target][]byte
type ActivatedWasm map[ethdb.WasmTarget][]byte

// checks if a valid Stylus prefix is present
func IsStylusProgram(b []byte) bool {
Expand All @@ -74,7 +74,7 @@ func NewStylusPrefix(dictionary byte) []byte {
return append(prefix, dictionary)
}

func (s *StateDB) ActivateWasm(moduleHash common.Hash, asmMap map[rawdb.Target][]byte) {
func (s *StateDB) ActivateWasm(moduleHash common.Hash, asmMap map[ethdb.WasmTarget][]byte) {
_, exists := s.arbExtraData.activatedWasms[moduleHash]
if exists {
return
Expand All @@ -85,7 +85,7 @@ func (s *StateDB) ActivateWasm(moduleHash common.Hash, asmMap map[rawdb.Target][
})
}

func (s *StateDB) TryGetActivatedAsm(target rawdb.Target, moduleHash common.Hash) ([]byte, error) {
func (s *StateDB) TryGetActivatedAsm(target ethdb.WasmTarget, moduleHash common.Hash) ([]byte, error) {
asmMap, exists := s.arbExtraData.activatedWasms[moduleHash]
if exists {
if asm, exists := asmMap[target]; exists {
Expand All @@ -95,7 +95,7 @@ func (s *StateDB) TryGetActivatedAsm(target rawdb.Target, moduleHash common.Hash
return s.db.ActivatedAsm(target, moduleHash)
}

func (s *StateDB) TryGetActivatedAsmMap(targets []rawdb.Target, moduleHash common.Hash) (map[rawdb.Target][]byte, error) {
func (s *StateDB) TryGetActivatedAsmMap(targets []ethdb.WasmTarget, moduleHash common.Hash) (map[ethdb.WasmTarget][]byte, error) {
asmMap := s.arbExtraData.activatedWasms[moduleHash]
if asmMap != nil {
for _, target := range targets {
Expand All @@ -106,7 +106,7 @@ func (s *StateDB) TryGetActivatedAsmMap(targets []rawdb.Target, moduleHash commo
return asmMap, nil
}
var err error
asmMap = make(map[rawdb.Target][]byte, len(targets))
asmMap = make(map[ethdb.WasmTarget][]byte, len(targets))
for _, target := range targets {
asm, dbErr := s.db.ActivatedAsm(target, moduleHash)
if dbErr == nil {
Expand Down Expand Up @@ -241,7 +241,7 @@ func (s *StateDB) StartRecording() {
s.arbExtraData.userWasms = make(UserWasms)
}

func (s *StateDB) RecordProgram(targets []rawdb.Target, moduleHash common.Hash) {
func (s *StateDB) RecordProgram(targets []ethdb.WasmTarget, moduleHash common.Hash) {
if len(targets) == 0 {
// nothing to record
return
Expand Down
8 changes: 4 additions & 4 deletions core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)

// StateDB is an EVM database for full state querying.
type StateDB interface {
// Arbitrum: manage Stylus wasms
ActivateWasm(moduleHash common.Hash, asmMap map[rawdb.Target][]byte)
TryGetActivatedAsm(target rawdb.Target, moduleHash common.Hash) (asm []byte, err error)
TryGetActivatedAsmMap(targets []rawdb.Target, moduleHash common.Hash) (asmMap map[rawdb.Target][]byte, err error)
ActivateWasm(moduleHash common.Hash, asmMap map[ethdb.WasmTarget][]byte)
TryGetActivatedAsm(target ethdb.WasmTarget, moduleHash common.Hash) (asm []byte, err error)
TryGetActivatedAsmMap(targets []ethdb.WasmTarget, moduleHash common.Hash) (asmMap map[ethdb.WasmTarget][]byte, err error)
RecordCacheWasm(wasm state.CacheWasm)
RecordEvictWasm(wasm state.EvictWasm)
GetRecentWasms() state.RecentWasms
Expand Down
2 changes: 2 additions & 0 deletions ethdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,10 @@ type AncientStore interface {
io.Closer
}

type WasmTarget string
type WasmDataBaseRetriever interface {
WasmDataBase() (KeyValueStore, uint32)
WasmTargets() []WasmTarget
}

// Database contains all the methods required by the high level database to not
Expand Down
4 changes: 4 additions & 0 deletions ethdb/remotedb/remotedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ func (t *Database) WasmDataBase() (ethdb.KeyValueStore, uint32) {
return t, 0
}

func (t *Database) WasmTargets() []ethdb.WasmTarget {
return nil
}

func (db *Database) Get(key []byte) ([]byte, error) {
var resp hexutil.Bytes
err := db.remote.Call(&resp, "debug_dbGet", hexutil.Bytes(key))
Expand Down

0 comments on commit fb79423

Please sign in to comment.