Skip to content

Commit e0a26b2

Browse files
committed
database functions
1 parent 62a2b75 commit e0a26b2

File tree

2 files changed

+290
-72
lines changed

2 files changed

+290
-72
lines changed

internal/runtime/hostfunctions.go

+218-57
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package runtime
22

33
import (
44
"context"
5+
"encoding/binary"
56
"encoding/json"
67
"fmt"
78

@@ -229,7 +230,7 @@ func hostValidateAddress(ctx context.Context, mod api.Module, addrPtr, addrLen u
229230
}
230231

231232
// hostScan implements db_scan
232-
func hostScan(ctx context.Context, mod api.Module, startPtr, startLen, endPtr, endLen uint32, order uint32) (uint64, uint64, uint32) {
233+
func hostScan(ctx context.Context, mod api.Module, startPtr, startLen, endPtr, endLen, order uint32) (uint64, uint64, uint32) {
233234
env := ctx.Value("env").(*RuntimeEnvironment)
234235
mem := mod.Memory()
235236

@@ -239,17 +240,25 @@ func hostScan(ctx context.Context, mod api.Module, startPtr, startLen, endPtr, e
239240
}
240241
env.GasUsed += gasCostIteratorCreate
241242

242-
start, err := ReadMemory(mem, startPtr, startLen)
243-
if err != nil {
244-
panic(fmt.Sprintf("failed to read start key from memory: %v", err))
243+
// Read start and end keys
244+
var start, end []byte
245+
var err error
246+
247+
if startPtr != 0 {
248+
start, err = ReadMemory(mem, startPtr, startLen)
249+
if err != nil {
250+
panic(fmt.Sprintf("failed to read start key from memory: %v", err))
251+
}
245252
}
246253

247-
end, err := ReadMemory(mem, endPtr, endLen)
248-
if err != nil {
249-
panic(fmt.Sprintf("failed to read end key from memory: %v", err))
254+
if endPtr != 0 {
255+
end, err = ReadMemory(mem, endPtr, endLen)
256+
if err != nil {
257+
panic(fmt.Sprintf("failed to read end key from memory: %v", err))
258+
}
250259
}
251260

252-
// Check iterator limits
261+
// Start a new call context for this iterator
253262
callID := env.StartCall()
254263
if len(env.iterators[callID]) >= maxIteratorsPerCall {
255264
return 0, 0, 2 // Return error code 2 for too many iterators
@@ -424,74 +433,226 @@ func hostCloseIterator(ctx context.Context, mod api.Module, callID, iterID uint6
424433
}
425434
}
426435

427-
// RegisterHostFunctions registers all host functions into a module named "env"
428-
func RegisterHostFunctions(r wazero.Runtime, env *RuntimeEnvironment) (wazero.CompiledModule, error) {
429-
// Initialize memory allocator if not already done
430-
if env.Memory == nil {
431-
env.Memory = NewMemoryAllocator(65536) // Start at 64KB offset
436+
// hostAbort implements the abort function required by Wasm modules
437+
func hostAbort(ctx context.Context, mod api.Module, code uint32) {
438+
panic(fmt.Sprintf("Wasm contract aborted with code: %d", code))
439+
}
440+
441+
// hostDbRead implements db_read
442+
func hostDbRead(ctx context.Context, mod api.Module, keyPtr uint32) uint32 {
443+
env := ctx.Value("env").(*RuntimeEnvironment)
444+
mem := mod.Memory()
445+
446+
// Read length prefix (4 bytes) from the key pointer
447+
lenBytes, err := ReadMemory(mem, keyPtr, 4)
448+
if err != nil {
449+
panic(fmt.Sprintf("failed to read key length from memory: %v", err))
432450
}
451+
keyLen := binary.LittleEndian.Uint32(lenBytes)
433452

434-
// Build a module that exports these functions
435-
hostModBuilder := r.NewHostModuleBuilder("env")
453+
// Read the actual key
454+
key, err := ReadMemory(mem, keyPtr+4, keyLen)
455+
if err != nil {
456+
panic(fmt.Sprintf("failed to read key from memory: %v", err))
457+
}
436458

437-
// Register memory management functions
438-
RegisterMemoryManagement(hostModBuilder, env.Memory)
459+
value := env.DB.Get(key)
460+
if len(value) == 0 {
461+
return 0
462+
}
439463

440-
// Register DB functions
441-
hostModBuilder.NewFunctionBuilder().
442-
WithFunc(hostGet).
443-
Export("db_get")
464+
// Allocate memory for the result: 4 bytes for length + actual value
465+
totalLen := 4 + len(value)
466+
offset, err := env.Memory.Allocate(mem, uint32(totalLen))
467+
if err != nil {
468+
panic(fmt.Sprintf("failed to allocate memory: %v", err))
469+
}
444470

445-
hostModBuilder.NewFunctionBuilder().
446-
WithFunc(hostSet).
447-
Export("db_set")
471+
// Write length prefix
472+
lenData := make([]byte, 4)
473+
binary.LittleEndian.PutUint32(lenData, uint32(len(value)))
474+
if err := WriteMemory(mem, offset, lenData); err != nil {
475+
panic(fmt.Sprintf("failed to write value length to memory: %v", err))
476+
}
448477

449-
// Register API functions
450-
hostModBuilder.NewFunctionBuilder().
451-
WithFunc(hostHumanizeAddress).
452-
Export("api_humanize_address")
478+
// Write value
479+
if err := WriteMemory(mem, offset+4, value); err != nil {
480+
panic(fmt.Sprintf("failed to write value to memory: %v", err))
481+
}
453482

454-
hostModBuilder.NewFunctionBuilder().
455-
WithFunc(hostCanonicalizeAddress).
456-
Export("api_canonicalize_address")
483+
return offset
484+
}
457485

458-
hostModBuilder.NewFunctionBuilder().
459-
WithFunc(hostValidateAddress).
460-
Export("api_validate_address")
486+
// hostDbWrite implements db_write
487+
func hostDbWrite(ctx context.Context, mod api.Module, keyPtr, valuePtr uint32) {
488+
env := ctx.Value("env").(*RuntimeEnvironment)
489+
mem := mod.Memory()
461490

462-
// Register Query functions
463-
hostModBuilder.NewFunctionBuilder().
464-
WithFunc(hostQueryExternal).
465-
Export("querier_query")
491+
// Read key length prefix (4 bytes)
492+
keyLenBytes, err := ReadMemory(mem, keyPtr, 4)
493+
if err != nil {
494+
panic(fmt.Sprintf("failed to read key length from memory: %v", err))
495+
}
496+
keyLen := binary.LittleEndian.Uint32(keyLenBytes)
497+
498+
// Read value length prefix (4 bytes)
499+
valLenBytes, err := ReadMemory(mem, valuePtr, 4)
500+
if err != nil {
501+
panic(fmt.Sprintf("failed to read value length from memory: %v", err))
502+
}
503+
valLen := binary.LittleEndian.Uint32(valLenBytes)
466504

467-
// Register Iterator functions
468-
hostModBuilder.NewFunctionBuilder().
469-
WithFunc(hostScan).
505+
// Read the actual key and value
506+
key, err := ReadMemory(mem, keyPtr+4, keyLen)
507+
if err != nil {
508+
panic(fmt.Sprintf("failed to read key from memory: %v", err))
509+
}
510+
511+
value, err := ReadMemory(mem, valuePtr+4, valLen)
512+
if err != nil {
513+
panic(fmt.Sprintf("failed to read value from memory: %v", err))
514+
}
515+
516+
env.DB.Set(key, value)
517+
}
518+
519+
// hostDbRemove implements db_remove
520+
func hostDbRemove(ctx context.Context, mod api.Module, keyPtr uint32) {
521+
env := ctx.Value("env").(*RuntimeEnvironment)
522+
mem := mod.Memory()
523+
524+
// Read length prefix (4 bytes) from the key pointer
525+
lenBytes, err := ReadMemory(mem, keyPtr, 4)
526+
if err != nil {
527+
panic(fmt.Sprintf("failed to read key length from memory: %v", err))
528+
}
529+
keyLen := binary.LittleEndian.Uint32(lenBytes)
530+
531+
// Read the actual key
532+
key, err := ReadMemory(mem, keyPtr+4, keyLen)
533+
if err != nil {
534+
panic(fmt.Sprintf("failed to read key from memory: %v", err))
535+
}
536+
537+
env.DB.Delete(key)
538+
}
539+
540+
// RegisterHostFunctions registers all host functions with the wazero runtime
541+
func RegisterHostFunctions(runtime wazero.Runtime, env *RuntimeEnvironment) (wazero.CompiledModule, error) {
542+
builder := runtime.NewHostModuleBuilder("env")
543+
544+
// Register abort function
545+
builder.NewFunctionBuilder().
546+
WithFunc(func(ctx context.Context, m api.Module, code uint32) {
547+
ctx = context.WithValue(ctx, "env", env)
548+
hostAbort(ctx, m, code)
549+
}).
550+
WithParameterNames("code").
551+
Export("abort")
552+
553+
// Register DB functions
554+
builder.NewFunctionBuilder().
555+
WithFunc(func(ctx context.Context, m api.Module, keyPtr, keyLen uint32) (uint32, uint32) {
556+
ctx = context.WithValue(ctx, "env", env)
557+
return hostGet(ctx, m, keyPtr, keyLen)
558+
}).
559+
WithParameterNames("key_ptr", "key_len").
560+
Export("db_get")
561+
562+
builder.NewFunctionBuilder().
563+
WithFunc(func(ctx context.Context, m api.Module, keyPtr, keyLen, valPtr, valLen uint32) {
564+
ctx = context.WithValue(ctx, "env", env)
565+
hostSet(ctx, m, keyPtr, keyLen, valPtr, valLen)
566+
}).
567+
WithParameterNames("key_ptr", "key_len", "val_ptr", "val_len").
568+
Export("db_set")
569+
570+
builder.NewFunctionBuilder().
571+
WithFunc(func(ctx context.Context, m api.Module, startPtr, startLen, endPtr, endLen, order uint32) (uint64, uint64, uint32) {
572+
ctx = context.WithValue(ctx, "env", env)
573+
return hostScan(ctx, m, startPtr, startLen, endPtr, endLen, order)
574+
}).
575+
WithParameterNames("start_ptr", "start_len", "end_ptr", "end_len", "order").
470576
Export("db_scan")
471577

472-
hostModBuilder.NewFunctionBuilder().
473-
WithFunc(hostNext).
578+
builder.NewFunctionBuilder().
579+
WithFunc(func(ctx context.Context, m api.Module, callID, iterID uint64) (uint32, uint32, uint32, uint32, uint32) {
580+
ctx = context.WithValue(ctx, "env", env)
581+
return hostNext(ctx, m, callID, iterID)
582+
}).
583+
WithParameterNames("call_id", "iter_id").
474584
Export("db_next")
475585

476-
hostModBuilder.NewFunctionBuilder().
477-
WithFunc(hostNextKey).
586+
builder.NewFunctionBuilder().
587+
WithFunc(func(ctx context.Context, m api.Module, callID, iterID uint64) (uint32, uint32, uint32) {
588+
ctx = context.WithValue(ctx, "env", env)
589+
return hostNextKey(ctx, m, callID, iterID)
590+
}).
591+
WithParameterNames("call_id", "iter_id").
478592
Export("db_next_key")
479593

480-
hostModBuilder.NewFunctionBuilder().
481-
WithFunc(hostNextValue).
482-
Export("db_next_value")
594+
// Register API functions
595+
builder.NewFunctionBuilder().
596+
WithFunc(func(ctx context.Context, m api.Module, addrPtr, addrLen uint32) (uint32, uint32) {
597+
ctx = context.WithValue(ctx, "env", env)
598+
return hostHumanizeAddress(ctx, m, addrPtr, addrLen)
599+
}).
600+
WithParameterNames("addr_ptr", "addr_len").
601+
Export("api_humanize_address")
483602

484-
hostModBuilder.NewFunctionBuilder().
485-
WithFunc(hostCloseIterator).
486-
Export("db_close_iterator")
603+
builder.NewFunctionBuilder().
604+
WithFunc(func(ctx context.Context, m api.Module, addrPtr, addrLen uint32) (uint32, uint32) {
605+
ctx = context.WithValue(ctx, "env", env)
606+
return hostCanonicalizeAddress(ctx, m, addrPtr, addrLen)
607+
}).
608+
WithParameterNames("addr_ptr", "addr_len").
609+
Export("api_canonicalize_address")
487610

488-
// Compile the host module
489-
compiled, err := hostModBuilder.Compile(context.Background())
490-
if err != nil {
491-
return nil, fmt.Errorf("failed to compile host module: %w", err)
492-
}
611+
builder.NewFunctionBuilder().
612+
WithFunc(func(ctx context.Context, m api.Module, addrPtr, addrLen uint32) uint32 {
613+
ctx = context.WithValue(ctx, "env", env)
614+
return hostValidateAddress(ctx, m, addrPtr, addrLen)
615+
}).
616+
WithParameterNames("addr_ptr", "addr_len").
617+
Export("api_validate_address")
618+
619+
// Register Query functions
620+
builder.NewFunctionBuilder().
621+
WithFunc(func(ctx context.Context, m api.Module, reqPtr, reqLen, gasLimit uint32) (uint32, uint32) {
622+
ctx = context.WithValue(ctx, "env", env)
623+
return hostQueryExternal(ctx, m, reqPtr, reqLen, gasLimit)
624+
}).
625+
WithParameterNames("req_ptr", "req_len", "gas_limit").
626+
Export("querier_query")
493627

494-
return compiled, nil
628+
// Register DB read function
629+
builder.NewFunctionBuilder().
630+
WithFunc(func(ctx context.Context, m api.Module, keyPtr uint32) uint32 {
631+
ctx = context.WithValue(ctx, "env", env)
632+
return hostDbRead(ctx, m, keyPtr)
633+
}).
634+
WithParameterNames("key_ptr").
635+
Export("db_read")
636+
637+
// Register DB write function
638+
builder.NewFunctionBuilder().
639+
WithFunc(func(ctx context.Context, m api.Module, keyPtr, valuePtr uint32) {
640+
ctx = context.WithValue(ctx, "env", env)
641+
hostDbWrite(ctx, m, keyPtr, valuePtr)
642+
}).
643+
WithParameterNames("key_ptr", "value_ptr").
644+
Export("db_write")
645+
646+
// Register DB remove function
647+
builder.NewFunctionBuilder().
648+
WithFunc(func(ctx context.Context, m api.Module, keyPtr uint32) {
649+
ctx = context.WithValue(ctx, "env", env)
650+
hostDbRemove(ctx, m, keyPtr)
651+
}).
652+
WithParameterNames("key_ptr").
653+
Export("db_remove")
654+
655+
return builder.Compile(context.Background())
495656
}
496657

497658
// When you instantiate a contract, you can do something like:

0 commit comments

Comments
 (0)