Skip to content

Commit f0317de

Browse files
Merge pull request #7 from OffchainLabs/execute-compiled-wasm
Add piping for compiled wasm and wasm execution
2 parents 5ba141e + 2e90f74 commit f0317de

15 files changed

+319
-49
lines changed

arbitrum/recordingdb.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ func (db *RecordingKV) Get(key []byte) ([]byte, error) {
4747
// Retrieving code
4848
copy(hash[:], key[len(rawdb.CodePrefix):])
4949
res, err = db.inner.DiskDB().Get(key)
50+
} else if ok, _, _ := rawdb.IsCompiledWasmCodeKey(key); ok {
51+
// Just return the compiled wasm without recording it since it's not in consensus and the replay will regenerate it
52+
return db.inner.DiskDB().Get(key)
5053
} else {
5154
err = fmt.Errorf("recording KV attempted to access non-hash key %v", hex.EncodeToString(key))
5255
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2020 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package rawdb
18+
19+
import (
20+
"github.com/ethereum/go-ethereum/common"
21+
"github.com/ethereum/go-ethereum/ethdb"
22+
"github.com/ethereum/go-ethereum/log"
23+
)
24+
25+
// ReadCompiledWasmCode retrieves the compiled wasm contract code of the provided code hash.
26+
func ReadCompiledWasmCode(db ethdb.KeyValueReader, version uint32, hash common.Hash) []byte {
27+
data, _ := db.Get(compiledWasmCodeKey(version, hash))
28+
return data
29+
}
30+
31+
// WriteCompiledWasmCode writes the provided contract compiled wasm code database.
32+
func WriteCompiledWasmCode(db ethdb.KeyValueWriter, version uint32, hash common.Hash, code []byte) {
33+
if err := db.Put(compiledWasmCodeKey(version, hash), code); err != nil {
34+
log.Crit("Failed to store compiled wasm contract code", "err", err)
35+
}
36+
}

core/rawdb/schema_arbitrum.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2018 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
// Package rawdb contains a collection of low level database accessors.
18+
19+
package rawdb
20+
21+
import (
22+
"bytes"
23+
"encoding/binary"
24+
25+
"github.com/ethereum/go-ethereum/common"
26+
)
27+
28+
var (
29+
CompiledWasmCodePrefix = []byte{0x00, 'w'} // (prefix, version, code_hash) -> account compiled wasm code
30+
)
31+
32+
// compiledWasmCodeKey = CompiledWasmCodePrefix + version + hash
33+
func compiledWasmCodeKey(version uint32, hash common.Hash) []byte {
34+
var versionBytes [4]byte
35+
binary.BigEndian.PutUint32(versionBytes[:], version)
36+
return append(append(CompiledWasmCodePrefix, versionBytes[:]...), hash.Bytes()...)
37+
}
38+
39+
// IsCompiledWasmCodeKey reports whether the given byte slice is the key of compiled wasm contract code,
40+
// if so return the raw code hash and version as well.
41+
func IsCompiledWasmCodeKey(key []byte) (bool, common.Hash, uint32) {
42+
43+
wasmKeyLen := len(CompiledWasmCodePrefix) + 4 + common.HashLength
44+
start := len(CompiledWasmCodePrefix)
45+
46+
if bytes.HasPrefix(key, CompiledWasmCodePrefix) && len(key) == wasmKeyLen {
47+
version := binary.BigEndian.Uint32(key[start : start+4])
48+
codeHash := common.BytesToHash(key[start+4:])
49+
return true, codeHash, version
50+
}
51+
return false, common.Hash{}, 0
52+
}

core/state/database.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ import (
3131
)
3232

3333
const (
34+
// Arbitrum: Cache size granted for caching clean compiled wasm code.
35+
compiledWasmCodeCacheSize = 64 * 1024 * 1024
36+
3437
// Number of codehash->size associations to keep.
3538
codeSizeCacheSize = 100000
3639

@@ -40,6 +43,9 @@ const (
4043

4144
// Database wraps access to tries and contract code.
4245
type Database interface {
46+
// Arbitrum: CompiledWasmContractCode retrieves a particular contract's user wasm code.
47+
CompiledWasmContractCode(codeHash common.Hash, version uint32) ([]byte, error)
48+
4349
// OpenTrie opens the main account trie.
4450
OpenTrie(root common.Hash) (Trie, error)
4551

@@ -133,12 +139,18 @@ func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
133139
db: trie.NewDatabaseWithConfig(db, config),
134140
codeSizeCache: csc,
135141
codeCache: fastcache.New(codeCacheSize),
142+
143+
// Arbitrum only
144+
compiledWasmCache: fastcache.New(compiledWasmCodeCacheSize),
136145
}
137146
runtime.SetFinalizer(cdb, (*cachingDB).finalizer)
138147
return cdb
139148
}
140149

141150
type cachingDB struct {
151+
// Arbitrum
152+
compiledWasmCache *fastcache.Cache
153+
142154
db *trie.Database
143155
codeSizeCache *lru.Cache
144156
codeCache *fastcache.Cache
@@ -156,6 +168,9 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
156168
// fastcache chunks are not mannaged by GC.
157169
func (db *cachingDB) finalizer() {
158170
db.codeCache.Reset()
171+
172+
// Arbitrum Only
173+
db.compiledWasmCache.Reset()
159174
}
160175

161176
// OpenStorageTrie opens the storage trie of an account.

core/state/database_arbitrum.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package state
2+
3+
import (
4+
"errors"
5+
6+
"github.com/ethereum/go-ethereum/common"
7+
"github.com/ethereum/go-ethereum/core/rawdb"
8+
)
9+
10+
// CompiledWasmContractCode retrieves a particular contract's compiled wasm code.
11+
func (db *cachingDB) CompiledWasmContractCode(codeHash common.Hash, version uint32) ([]byte, error) {
12+
if code := db.compiledWasmCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
13+
return code, nil
14+
}
15+
code := rawdb.ReadCompiledWasmCode(db.db.DiskDB(), version, codeHash)
16+
if len(code) > 0 {
17+
db.compiledWasmCache.Set(codeHash.Bytes(), code)
18+
return code, nil
19+
}
20+
return nil, errors.New("not found")
21+
}

core/state/journal_arbitrum.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package state
2+
3+
import "github.com/ethereum/go-ethereum/common"
4+
5+
type wasmCodeChange struct {
6+
account *common.Address
7+
version uint32
8+
}
9+
10+
func (ch wasmCodeChange) revert(s *StateDB) {
11+
s.getStateObject(*ch.account).setWASMCode(nil, ch.version)
12+
}
13+
14+
func (ch wasmCodeChange) dirtied() *common.Address {
15+
return ch.account
16+
}

core/state/snapshot/conversion.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ func GenerateTrie(snaptree *Tree, root common.Hash, src ethdb.Database, dst ethd
7979
return common.Hash{}, errors.New("failed to read contract code")
8080
}
8181
rawdb.WriteCode(dst, codeHash, code)
82+
83+
// TODO: How do we migrate the compiled wasm code
8284
}
8385
// Then migrate all storage trie nodes into the tmp db.
8486
storageIt, err := snaptree.StorageIterator(root, accountHash, common.Hash{})

core/state/state_object.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ func (s Storage) Copy() Storage {
6666
// Account values can be accessed and modified through the object.
6767
// Finally, call CommitTrie to write the modified storage trie into a database.
6868
type stateObject struct {
69+
// Arbitrum: write caches and cache flags
70+
compiledWasmCode CompiledWasms // compiled wasm bytecode which gets set when wasm is loaded
71+
6972
address common.Address
7073
addrHash common.Hash // hash of ethereum address of the account
7174
data types.StateAccount
@@ -119,6 +122,9 @@ func newObject(db *StateDB, address common.Address, data types.StateAccount) *st
119122
originStorage: make(Storage),
120123
pendingStorage: make(Storage),
121124
dirtyStorage: make(Storage),
125+
126+
// Arbitrum Only
127+
compiledWasmCode: make(CompiledWasms),
122128
}
123129
}
124130

@@ -455,6 +461,10 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
455461
stateObject.suicided = s.suicided
456462
stateObject.dirtyCode = s.dirtyCode
457463
stateObject.deleted = s.deleted
464+
465+
// Arbitrum Only
466+
stateObject.compiledWasmCode = s.compiledWasmCode.Copy()
467+
458468
return stateObject
459469
}
460470

core/state/state_object_arbitrum.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2014 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package state
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/ethereum/go-ethereum/common"
23+
)
24+
25+
type CompiledWasmCache struct {
26+
code Code
27+
dirty bool
28+
}
29+
type CompiledWasms map[uint32]CompiledWasmCache
30+
31+
func (c CompiledWasms) Copy() CompiledWasms {
32+
cpy := make(CompiledWasms, len(c))
33+
for key, value := range c {
34+
cpy[key] = value
35+
}
36+
37+
return cpy
38+
}
39+
40+
// CompiledWasmCode returns the user wasm contract code associated with this object, if any.
41+
func (s *stateObject) CompiledWasmCode(db Database, version uint32) []byte {
42+
if wasm, ok := s.compiledWasmCode[version]; ok {
43+
return wasm.code
44+
}
45+
if version == 0 {
46+
return nil
47+
}
48+
compiledWasmCode, err := db.CompiledWasmContractCode(common.BytesToHash(s.CodeHash()), version)
49+
if err != nil {
50+
s.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
51+
}
52+
s.compiledWasmCode[version] = CompiledWasmCache{
53+
code: compiledWasmCode,
54+
dirty: false,
55+
}
56+
return compiledWasmCode
57+
}
58+
59+
func (s *stateObject) SetCompiledWasmCode(code []byte, version uint32) {
60+
// Can only be compiled once, so if it's being compiled, it was previous empty
61+
s.db.journal.append(wasmCodeChange{
62+
account: &s.address,
63+
version: version,
64+
})
65+
s.setWASMCode(code, version)
66+
}
67+
68+
func (s *stateObject) setWASMCode(code []byte, version uint32) {
69+
s.compiledWasmCode[version] = CompiledWasmCache{
70+
code: code,
71+
dirty: true,
72+
}
73+
}

core/state/statedb.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
type revision struct {
4040
id int
4141
journalIndex int
42+
4243
// Arbitrum: track the total balance change across all accounts
4344
unexpectedBalanceDelta *big.Int
4445
}
@@ -65,9 +66,10 @@ func (n *proofList) Delete(key []byte) error {
6566
// * Contracts
6667
// * Accounts
6768
type StateDB struct {
68-
// Arbitrum: track the total balance change across all accounts
69-
unexpectedBalanceDelta *big.Int
70-
userWasms UserWasms
69+
// Arbitrum
70+
unexpectedBalanceDelta *big.Int // total balance change across all accounts
71+
userWasms UserWasms // user wasms encountered during execution
72+
deterministic bool // whether the order in which deletes are committed should be deterministic
7173

7274
db Database
7375
prefetcher *triePrefetcher
@@ -132,8 +134,6 @@ type StateDB struct {
132134
StorageUpdated int
133135
AccountDeleted int
134136
StorageDeleted int
135-
136-
deterministic bool
137137
}
138138

139139
// New creates a new state from a given trie.
@@ -168,15 +168,6 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
168168
return sdb, nil
169169
}
170170

171-
func NewDeterministic(root common.Hash, db Database) (*StateDB, error) {
172-
sdb, err := New(root, db, nil)
173-
if err != nil {
174-
return nil, err
175-
}
176-
sdb.deterministic = true
177-
return sdb, nil
178-
}
179-
180171
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
181172
// state trie concurrently while the state is mutated so that when we reach the
182173
// commit phase, most of the needed data is already hot.
@@ -967,6 +958,16 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
967958
rawdb.WriteCode(codeWriter, common.BytesToHash(obj.CodeHash()), obj.code)
968959
obj.dirtyCode = false
969960
}
961+
962+
// Arbitrum Only
963+
for version, wasm := range obj.compiledWasmCode {
964+
if wasm.dirty {
965+
codeHash := common.BytesToHash(obj.CodeHash())
966+
rawdb.WriteCompiledWasmCode(codeWriter, version, codeHash, wasm.code)
967+
wasm.dirty = false
968+
}
969+
}
970+
970971
// Write any storage changes in the state object to its storage trie
971972
set, err := obj.CommitTrie(s.db)
972973
if err != nil {

0 commit comments

Comments
 (0)