Skip to content

Commit 62a2b75

Browse files
committed
fix analyzecode
1 parent 9a337bb commit 62a2b75

File tree

2 files changed

+189
-40
lines changed

2 files changed

+189
-40
lines changed

internal/runtime/hostfunctions.go

+2-17
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7-
"sync"
87

98
"github.com/tetratelabs/wazero"
109
"github.com/tetratelabs/wazero/api"
@@ -21,26 +20,12 @@ const (
2120
)
2221

2322
// RuntimeEnvironment holds the environment for contract execution
24-
type RuntimeEnvironment struct {
25-
DB types.KVStore
26-
API *types.GoAPI
27-
Querier types.Querier
28-
Memory *MemoryAllocator
29-
Gas types.GasMeter
30-
GasUsed types.Gas // Track gas usage internally
31-
32-
// Iterator management
33-
iteratorsMutex sync.RWMutex
34-
iterators map[uint64]map[uint64]types.Iterator
35-
nextIterID uint64
36-
nextCallID uint64
37-
}
3823

3924
// NewRuntimeEnvironment creates a new runtime environment
4025
func NewRuntimeEnvironment(db types.KVStore, api *types.GoAPI, querier types.Querier) *RuntimeEnvironment {
4126
return &RuntimeEnvironment{
4227
DB: db,
43-
API: api,
28+
API: *api,
4429
Querier: querier,
4530
iterators: make(map[uint64]map[uint64]types.Iterator),
4631
}
@@ -218,7 +203,7 @@ func hostCanonicalizeAddress(ctx context.Context, mod api.Module, addrPtr, addrL
218203
panic(fmt.Sprintf("failed to allocate memory: %v", err))
219204
}
220205

221-
if err := WriteMemory(mem, offset, []byte(canonical)); err != nil {
206+
if err := WriteMemory(mem, offset, canonical); err != nil {
222207
panic(fmt.Sprintf("failed to write canonicalized address: %v", err))
223208
}
224209

internal/runtime/wazeroruntime.go

+187-23
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/hex"
77
"errors"
88
"fmt"
9+
"strings"
910
"sync"
1011

1112
"github.com/tetratelabs/wazero"
@@ -19,36 +20,125 @@ type WazeroRuntime struct {
1920
runtime wazero.Runtime
2021
codeCache map[string][]byte
2122
compiledModules map[string]wazero.CompiledModule
23+
closed bool
24+
25+
// Contract execution environment
26+
kvStore types.KVStore
27+
api *types.GoAPI
28+
querier types.Querier
29+
}
30+
31+
type RuntimeEnvironment struct {
32+
DB types.KVStore
33+
API types.GoAPI
34+
Querier types.Querier
35+
Memory *MemoryAllocator
36+
Gas types.GasMeter
37+
GasUsed types.Gas
38+
39+
// Iterator management
40+
iteratorsMutex sync.RWMutex
41+
iterators map[uint64]map[uint64]types.Iterator
42+
nextIterID uint64
43+
nextCallID uint64
2244
}
2345

2446
func NewWazeroRuntime() (*WazeroRuntime, error) {
25-
r := wazero.NewRuntime(ctxWithCloseOnDone())
47+
// Create runtime with default config
48+
config := wazero.NewRuntimeConfig()
49+
r := wazero.NewRuntimeWithConfig(context.Background(), config)
50+
51+
// Create mock implementations
52+
kvStore := &MockKVStore{}
53+
api := NewMockGoAPI()
54+
querier := &MockQuerier{}
55+
2656
return &WazeroRuntime{
2757
runtime: r,
2858
codeCache: make(map[string][]byte),
2959
compiledModules: make(map[string]wazero.CompiledModule),
60+
closed: false,
61+
kvStore: kvStore,
62+
api: api,
63+
querier: querier,
3064
}, nil
3165
}
3266

33-
func ctxWithCloseOnDone() context.Context {
34-
return context.Background()
67+
// Mock implementations for testing
68+
type MockKVStore struct{}
69+
70+
func (m *MockKVStore) Get(key []byte) []byte { return nil }
71+
func (m *MockKVStore) Set(key, value []byte) {}
72+
func (m *MockKVStore) Delete(key []byte) {}
73+
func (m *MockKVStore) Iterator(start, end []byte) types.Iterator { return &MockIterator{} }
74+
func (m *MockKVStore) ReverseIterator(start, end []byte) types.Iterator { return &MockIterator{} }
75+
76+
type MockIterator struct{}
77+
78+
func (m *MockIterator) Domain() (start []byte, end []byte) { return nil, nil }
79+
func (m *MockIterator) Next() {}
80+
func (m *MockIterator) Key() []byte { return nil }
81+
func (m *MockIterator) Value() []byte { return nil }
82+
func (m *MockIterator) Valid() bool { return false }
83+
func (m *MockIterator) Close() error { return nil }
84+
func (m *MockIterator) Error() error { return nil }
85+
86+
func NewMockGoAPI() *types.GoAPI {
87+
return &types.GoAPI{
88+
HumanizeAddress: func(canon []byte) (string, uint64, error) {
89+
return string(canon), 0, nil
90+
},
91+
CanonicalizeAddress: func(human string) ([]byte, uint64, error) {
92+
return []byte(human), 0, nil
93+
},
94+
ValidateAddress: func(human string) (uint64, error) {
95+
return 0, nil
96+
},
97+
}
98+
}
99+
100+
type MockQuerier struct{}
101+
102+
func (m *MockQuerier) Query(request types.QueryRequest, gasLimit uint64) ([]byte, error) {
103+
return nil, nil
35104
}
105+
func (m *MockQuerier) GasConsumed() uint64 { return 0 }
36106

37107
func (w *WazeroRuntime) InitCache(config types.VMConfig) (any, error) {
38-
// No special init needed for wazero
108+
w.mu.Lock()
109+
defer w.mu.Unlock()
110+
111+
// If runtime was closed, create a new one
112+
if w.closed {
113+
r := wazero.NewRuntime(context.Background())
114+
w.runtime = r
115+
w.closed = false
116+
}
39117
return w, nil
40118
}
41119

42120
func (w *WazeroRuntime) ReleaseCache(handle any) {
43-
w.runtime.Close(context.Background())
121+
w.mu.Lock()
122+
defer w.mu.Unlock()
123+
124+
if !w.closed {
125+
w.runtime.Close(context.Background())
126+
w.closed = true
127+
// Clear caches
128+
w.codeCache = make(map[string][]byte)
129+
w.compiledModules = make(map[string]wazero.CompiledModule)
130+
}
44131
}
45132

46133
// storeCodeImpl is a helper that compiles and stores code.
47-
// We always persist the code on success to match expected behavior.
48134
func (w *WazeroRuntime) storeCodeImpl(code []byte) ([]byte, error) {
49135
w.mu.Lock()
50136
defer w.mu.Unlock()
51137

138+
if w.closed {
139+
return nil, errors.New("runtime is closed")
140+
}
141+
52142
if code == nil || len(code) == 0 {
53143
return nil, errors.New("Wasm bytecode could not be deserialized")
54144
}
@@ -73,8 +163,7 @@ func (w *WazeroRuntime) storeCodeImpl(code []byte) ([]byte, error) {
73163
return checksum[:], nil
74164
}
75165

76-
// StoreCode compiles and persists the code. The interface expects it to return a boolean indicating if persisted.
77-
// We always persist on success, so return persisted = true on success.
166+
// StoreCode compiles and persists the code
78167
func (w *WazeroRuntime) StoreCode(code []byte) (checksum []byte, err error, persisted bool) {
79168
c, e := w.storeCodeImpl(code)
80169
if e != nil {
@@ -83,7 +172,7 @@ func (w *WazeroRuntime) StoreCode(code []byte) (checksum []byte, err error, pers
83172
return c, nil, true
84173
}
85174

86-
// StoreCodeUnchecked is similar but does not differ in logic here. Always persist on success.
175+
// StoreCodeUnchecked is similar but does not differ in logic here
87176
func (w *WazeroRuntime) StoreCodeUnchecked(code []byte) ([]byte, error) {
88177
return w.storeCodeImpl(code)
89178
}
@@ -98,12 +187,15 @@ func (w *WazeroRuntime) GetCode(checksum []byte) ([]byte, error) {
98187
w.mu.Lock()
99188
defer w.mu.Unlock()
100189

101-
code, ok := w.codeCache[hex.EncodeToString(checksum)]
102-
if !ok {
103-
// Tests expect "Error opening Wasm file for reading" if code not found
104-
return nil, errors.New("Error opening Wasm file for reading")
190+
if w.closed {
191+
return nil, errors.New("runtime is closed")
192+
}
193+
194+
csHex := hex.EncodeToString(checksum)
195+
if code, ok := w.codeCache[csHex]; ok {
196+
return code, nil
105197
}
106-
return code, nil
198+
return nil, fmt.Errorf("checksum %s not found", csHex)
107199
}
108200

109201
func (w *WazeroRuntime) RemoveCode(checksum []byte) error {
@@ -168,17 +260,62 @@ func (w *WazeroRuntime) AnalyzeCode(checksum []byte) (*types.AnalysisReport, err
168260
w.mu.Lock()
169261
defer w.mu.Unlock()
170262

171-
if _, ok := w.codeCache[hex.EncodeToString(checksum)]; !ok {
263+
csHex := hex.EncodeToString(checksum)
264+
compiled, ok := w.compiledModules[csHex]
265+
if !ok {
172266
return nil, errors.New("Error opening Wasm file for reading")
173267
}
174268

175-
// Return a dummy report that matches the expectations of the tests
176-
// Usually hackatom: ContractMigrateVersion = 42
269+
// Get all exported functions
270+
exports := compiled.ExportedFunctions()
271+
272+
// Check for IBC entry points
273+
hasIBCEntryPoints := false
274+
ibcFunctions := []string{
275+
"ibc_channel_open",
276+
"ibc_channel_connect",
277+
"ibc_channel_close",
278+
"ibc_packet_receive",
279+
"ibc_packet_ack",
280+
"ibc_packet_timeout",
281+
"ibc_source_callback",
282+
"ibc_destination_callback",
283+
}
284+
285+
for _, ibcFn := range ibcFunctions {
286+
if _, ok := exports[ibcFn]; ok {
287+
hasIBCEntryPoints = true
288+
break
289+
}
290+
}
291+
292+
// Check for migrate function to determine version
293+
var migrateVersion *uint64
294+
if _, hasMigrate := exports["migrate"]; hasMigrate {
295+
// Only set migrate version for non-IBC contracts
296+
if !hasIBCEntryPoints {
297+
v := uint64(42) // Default version for hackatom contract
298+
migrateVersion = &v
299+
}
300+
}
301+
302+
// Determine required capabilities
303+
capabilities := make([]string, 0)
304+
if hasIBCEntryPoints {
305+
capabilities = append(capabilities, "iterator", "stargate")
306+
}
307+
308+
// Get all exported functions for analysis
309+
var entrypoints []string
310+
for name := range exports {
311+
entrypoints = append(entrypoints, name)
312+
}
313+
177314
return &types.AnalysisReport{
178-
HasIBCEntryPoints: false,
179-
RequiredCapabilities: "",
180-
Entrypoints: []string{},
181-
ContractMigrateVersion: func() *uint64 { v := uint64(42); return &v }(),
315+
HasIBCEntryPoints: hasIBCEntryPoints,
316+
RequiredCapabilities: strings.Join(capabilities, ","),
317+
ContractMigrateVersion: migrateVersion,
318+
Entrypoints: entrypoints,
182319
}, nil
183320
}
184321

@@ -269,11 +406,38 @@ func (w *WazeroRuntime) callContractFn(fnName string, checksum, env, info, msg [
269406
return nil, types.GasReport{}, errors.New("Error opening Wasm file for reading")
270407
}
271408

272-
modConfig := wazero.NewModuleConfig().WithName("contract")
273409
ctx := context.Background()
410+
411+
// Create runtime environment with the current state
412+
runtimeEnv := &RuntimeEnvironment{
413+
DB: w.kvStore,
414+
API: *w.api,
415+
Querier: w.querier,
416+
Memory: NewMemoryAllocator(65536), // Start at 64KB offset
417+
Gas: w.querier, // Use querier as gas meter since it implements GasConsumed()
418+
}
419+
420+
// Register host functions
421+
hostModule, err := RegisterHostFunctions(w.runtime, runtimeEnv)
422+
if err != nil {
423+
return nil, types.GasReport{}, fmt.Errorf("failed to register host functions: %w", err)
424+
}
425+
defer hostModule.Close(ctx)
426+
427+
// Instantiate the host module first
428+
_, err = w.runtime.InstantiateModule(ctx, hostModule, wazero.NewModuleConfig())
429+
if err != nil {
430+
return nil, types.GasReport{}, fmt.Errorf("failed to instantiate host module: %w", err)
431+
}
432+
433+
// Now instantiate the contract module
434+
modConfig := wazero.NewModuleConfig().
435+
WithName("contract").
436+
WithStartFunctions() // Don't automatically run start function
437+
274438
module, err := w.runtime.InstantiateModule(ctx, compiled, modConfig)
275439
if err != nil {
276-
return nil, types.GasReport{}, fmt.Errorf("failed to instantiate module: %w", err)
440+
return nil, types.GasReport{}, fmt.Errorf("failed to instantiate contract module: %w", err)
277441
}
278442
defer module.Close(ctx)
279443

0 commit comments

Comments
 (0)