Skip to content

Commit

Permalink
Add export PVM function
Browse files Browse the repository at this point in the history
  • Loading branch information
pantrif committed Dec 16, 2024
1 parent d7c80f1 commit f25a7da
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 12 deletions.
2 changes: 2 additions & 0 deletions internal/polkavm/host_call/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
ForgetCost
HistoricalLookupCost
ImportCost
ExportCost
)

const (
Expand All @@ -45,6 +46,7 @@ const (
ForgetID
HistoricalLookupID
ImportID
ExportID
)

type Code uint64
Expand Down
46 changes: 46 additions & 0 deletions internal/polkavm/host_call/refine_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/eigerco/strawberry/internal/jamtime"
. "github.com/eigerco/strawberry/internal/polkavm"
"github.com/eigerco/strawberry/internal/service"
"github.com/eigerco/strawberry/internal/work"
)

// HistoricalLookup ΩH(ϱ, ω, µ, (m, e), s, d, t)
Expand Down Expand Up @@ -106,3 +107,48 @@ func Import(

return gas, withCode(regs, OK), mem, ctxPair, nil
}

// Export ΩE(ϱ, ω, µ, (m, e), ς)
func Export(
gas Gas,
regs Registers,
mem Memory,
ctxPair RefineContextPair,
exportOffset uint64,
) (Gas, Registers, Memory, RefineContextPair, error) {
if gas < ExportCost {
return gas, regs, mem, ctxPair, ErrOutOfGas
}
gas -= ExportCost

p := regs[A0] // ω7
requestedLength := regs[A1] // ω8

// let z = min(ω8,WG)
z := min(requestedLength, common.SizeOfSegment)

data := make([]byte, z)
if err := mem.Read(uint32(p), data); err != nil {
// x = ∇
return gas, withCode(regs, OOB), mem, ctxPair, nil
}

// Apply zero-padding Pn to data to make it WG-sized
paddedData := work.ZeroPadding(data, common.SizeOfSegment)

var segmentData Segment
copy(segmentData[:], paddedData)

currentCount := uint64(len(ctxPair.Segments))
if exportOffset+currentCount >= work.MaxNumberOfEntries {
return gas, withCode(regs, FULL), mem, ctxPair, nil
}

// Append x to e
ctxPair.Segments = append(ctxPair.Segments, segmentData)

// ω7 = ς + |e|
regs[A0] = exportOffset + uint64(len(ctxPair.Segments))

return gas, regs, mem, ctxPair, nil
}
63 changes: 63 additions & 0 deletions internal/polkavm/host_call/refine_functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,66 @@ func TestImport(t *testing.T) {
expectedGasRemaining := polkavm.Gas(initialGas) - host_call.ImportCost - polkavm.GasCosts[polkavm.Ecalli] - polkavm.GasCosts[polkavm.JumpIndirect]
assert.Equal(t, expectedGasRemaining, gasRemaining)
}

func TestExport(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)

dataToExport := []byte("export_data")
p := memoryMap.RWDataAddress

mem := memoryMap.NewMemory(nil, nil, nil)
err = mem.Write(p, dataToExport)
require.NoError(t, err)

exportOffset := uint64(10)

initialRegs := polkavm.Registers{
polkavm.RA: polkavm.VmAddressReturnToHost,
polkavm.SP: uint64(memoryMap.StackAddressHigh),
polkavm.A0: uint64(p),
polkavm.A1: uint64(len(dataToExport)),
}

ctxPair := polkavm.RefineContextPair{
Segments: []polkavm.Segment{},
}

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, ctxOut, err := host_call.Export(
gasCounter,
regs,
mem,
ctxPair,
exportOffset,
)
require.NoError(t, err)

ctxPair = ctxOut
return gasCounterOut, regsOut, memOut, x, err
}

gasRemaining, regsOut, _, _, err := interpreter.InvokeHostCall(pp, memoryMap, 0, initialGas, initialRegs, mem, hostCall, service.ServiceAccount{})
require.ErrorIs(t, err, polkavm.ErrHalt)

// We expect ω7 = ς + |e| = 10 + 1 = 11
assert.Equal(t, exportOffset+1, regsOut[polkavm.A0])

require.Len(t, ctxPair.Segments, 1)
seg := ctxPair.Segments[0]
expectedSegment := make([]byte, common.SizeOfSegment)
copy(expectedSegment, dataToExport)
assert.Equal(t, expectedSegment, seg[:])

expectedGasRemaining := polkavm.Gas(initialGas) - host_call.ExportCost - polkavm.GasCosts[polkavm.Ecalli] - polkavm.GasCosts[polkavm.JumpIndirect]
assert.Equal(t, expectedGasRemaining, gasRemaining)
}
18 changes: 18 additions & 0 deletions internal/work/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package work

// ZeroPadding Pn(. . . ) The octet-array zero-padding function (14.17 v0.5.2)
func ZeroPadding(x []byte, n uint) []byte {
if n == 0 {
return x
}

// ((|x|+n-1) mod n)+1...n
start, end := ((len(x)+int(n)-1)%int(n))+1, int(n)

paddingLength := end - start
if paddingLength <= 0 {
return x
}

return append(x, make([]byte, paddingLength)...)
}
50 changes: 50 additions & 0 deletions internal/work/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package work_test

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/eigerco/strawberry/internal/work"
)

func TestZeroPadding(t *testing.T) {
tests := []struct {
name string
input []byte
multiple uint
expectedOutput []byte
}{
{
name: "No padding",
input: []byte{1, 2, 3, 4},
multiple: 4,
expectedOutput: []byte{1, 2, 3, 4},
},
{
name: "Needs padding",
input: []byte{1, 2, 3, 4, 5},
multiple: 6,
expectedOutput: []byte{1, 2, 3, 4, 5, 0},
},
{
name: "Needs padding",
input: []byte("data"),
multiple: 6,
expectedOutput: []byte("data\x00\x00"),
},
{
name: "Padding with zero n",
input: []byte{1, 2, 3, 4},
multiple: 0,
expectedOutput: []byte{1, 2, 3, 4},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
output := work.ZeroPadding(tc.input, tc.multiple)
assert.Equal(t, tc.expectedOutput, output)
})
}
}
12 changes: 0 additions & 12 deletions internal/work/constants_integration.go

This file was deleted.

0 comments on commit f25a7da

Please sign in to comment.