diff --git a/internal/polkavm/host_call/common.go b/internal/polkavm/host_call/common.go index 4b844707..1ce004fd 100644 --- a/internal/polkavm/host_call/common.go +++ b/internal/polkavm/host_call/common.go @@ -24,6 +24,7 @@ const ( SolicitCost ForgetCost HistoricalLookupCost + ImportCost ) const ( @@ -43,6 +44,7 @@ const ( SolicitID ForgetID HistoricalLookupID + ImportID ) type Code uint64 diff --git a/internal/polkavm/host_call/refine_functions.go b/internal/polkavm/host_call/refine_functions.go index a44a8b7d..e8b852f8 100644 --- a/internal/polkavm/host_call/refine_functions.go +++ b/internal/polkavm/host_call/refine_functions.go @@ -4,13 +4,14 @@ import ( "math" "github.com/eigerco/strawberry/internal/block" + "github.com/eigerco/strawberry/internal/common" "github.com/eigerco/strawberry/internal/crypto" "github.com/eigerco/strawberry/internal/jamtime" . "github.com/eigerco/strawberry/internal/polkavm" "github.com/eigerco/strawberry/internal/service" ) -// HistoricalLookup ΩH(ϱ, ω, µ, (m, e), s,d, t) +// HistoricalLookup ΩH(ϱ, ω, µ, (m, e), s, d, t) func HistoricalLookup( gas Gas, regs Registers, @@ -53,8 +54,7 @@ func HistoricalLookup( v := a.LookupPreimage(t, h) if len(v) == 0 { - regs[A0] = uint64(NONE) - return gas, regs, mem, ctxPair, nil + return gas, withCode(regs, NONE), mem, ctxPair, nil } if uint64(len(v)) > bz { @@ -70,3 +70,39 @@ func HistoricalLookup( return gas, regs, mem, ctxPair, nil } + +// Import ΩY(ϱ, ω, µ, (m, e), i) +func Import( + gas Gas, + regs Registers, + mem Memory, + ctxPair RefineContextPair, + importedSegments []Segment, +) (Gas, Registers, Memory, RefineContextPair, error) { + if gas < ImportCost { + return gas, regs, mem, ctxPair, ErrOutOfGas + } + gas -= ImportCost + + index := regs[A0] // ω7 + offset := regs[A1] // ω8 + length := regs[A2] // ω9 + + // v = ∅ + if index >= uint64(len(importedSegments)) { + // v = ∅, return NONE + return gas, withCode(regs, NONE), mem, ctxPair, nil + } + + // v = i[ω7] + v := importedSegments[index][:] + + l := min(length, common.SizeOfSegment) + + segmentToWrite := v[:l] + if err := mem.Write(uint32(offset), segmentToWrite); err != nil { + return gas, withCode(regs, OOB), mem, ctxPair, nil + } + + return gas, withCode(regs, OK), mem, ctxPair, nil +} diff --git a/internal/polkavm/host_call/refine_functions_test.go b/internal/polkavm/host_call/refine_functions_test.go index d8fee4a8..6cdfd9b4 100644 --- a/internal/polkavm/host_call/refine_functions_test.go +++ b/internal/polkavm/host_call/refine_functions_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/eigerco/strawberry/internal/block" + "github.com/eigerco/strawberry/internal/common" "github.com/eigerco/strawberry/internal/crypto" "github.com/eigerco/strawberry/internal/jamtime" "github.com/eigerco/strawberry/internal/polkavm" @@ -104,3 +105,66 @@ func TestHistoricalLookup(t *testing.T) { expectedGasRemaining := polkavm.Gas(initialGas) - host_call.HistoricalLookupCost - polkavm.GasCosts[polkavm.Ecalli] - polkavm.GasCosts[polkavm.JumpIndirect] assert.Equal(t, expectedGasRemaining, gasRemaining) } + +func TestImport(t *testing.T) { + pp := &polkavm.Program{ + Instructions: []polkavm.Instruction{ + {Opcode: polkavm.Ecalli, Imm: []uint32{0}, Offset: 0, Length: 1}, + {Opcode: polkavm.JumpIndirect, Imm: []uint32{0}, Reg: []polkavm.Reg{polkavm.RA}, Offset: 1, Length: 2}, + }, + } + + memoryMap, err := polkavm.NewMemoryMap(0, 256, 512, 0) + require.NoError(t, err) + + initialGas := uint64(100) + + segmentData := [common.SizeOfSegment]byte{} + for i := range segmentData { + segmentData[i] = byte('A') + } + importedSegments := []polkavm.Segment{segmentData} + + bo := memoryMap.RWDataAddress + 100 + bz := uint32(50) + + initialRegs := polkavm.Registers{ + polkavm.RA: polkavm.VmAddressReturnToHost, + polkavm.SP: uint64(memoryMap.StackAddressHigh), + polkavm.A0: uint64(0), + polkavm.A1: uint64(bo), + polkavm.A2: uint64(bz), + } + + mem := memoryMap.NewMemory(nil, nil, nil) + + hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, x service.ServiceAccount) (polkavm.Gas, polkavm.Registers, polkavm.Memory, service.ServiceAccount, error) { + gasCounterOut, regsOut, memOut, _, err := host_call.Import( + gasCounter, + regs, + mem, + polkavm.RefineContextPair{}, + importedSegments, + ) + require.NoError(t, err) + return gasCounterOut, regsOut, memOut, x, err + } + + gasRemaining, regsOut, memOut, _, err := interpreter.InvokeHostCall(pp, memoryMap, 0, initialGas, initialRegs, mem, hostCall, service.ServiceAccount{}) + require.ErrorIs(t, err, polkavm.ErrHalt) + + actualValue := make([]byte, bz) + err = memOut.Read(bo, actualValue) + require.NoError(t, err) + + expectedData := make([]byte, bz) + for i := range expectedData { + expectedData[i] = 'A' + } + + assert.Equal(t, expectedData, actualValue) + assert.Equal(t, uint64(host_call.OK), regsOut[polkavm.A0]) + + expectedGasRemaining := polkavm.Gas(initialGas) - host_call.ImportCost - polkavm.GasCosts[polkavm.Ecalli] - polkavm.GasCosts[polkavm.JumpIndirect] + assert.Equal(t, expectedGasRemaining, gasRemaining) +}