From 6b7019292f0d21b41515765fdf6c9cae68b0baaf Mon Sep 17 00:00:00 2001 From: "daniel.vladco" Date: Mon, 28 Oct 2024 20:30:33 +0200 Subject: [PATCH] refactor: align info host-call with the latest PVM api changes --- internal/polkavm/abi_test.go | 22 +-- .../polkavm/host_call/general_functions.go | 130 +++++++----------- .../host_call/general_functions_test.go | 94 ++++++------- .../polkavm/interpreter/interpreter_test.go | 6 +- 4 files changed, 113 insertions(+), 139 deletions(-) diff --git a/internal/polkavm/abi_test.go b/internal/polkavm/abi_test.go index 0169cd93..2ed0772f 100644 --- a/internal/polkavm/abi_test.go +++ b/internal/polkavm/abi_test.go @@ -8,7 +8,7 @@ import ( ) func Test_memoryMap(t *testing.T) { - maxSize := uint32(AddressSpaceSize - uint64(VmMaxPageSize)*4) + maxSize := uint32(AddressSpaceSize - uint64(VmMaxPageSize)*5) tests := []struct { expectError bool pageSize, roDataSize, rwDataSize, stackSize uint32 @@ -22,19 +22,19 @@ func Test_memoryMap(t *testing.T) { expectedRWDataSize: 0x4000, expectedStackSize: 0x4000, expectedRWDataAddress: 0x30000, - expectedStackAddressHigh: 0xffff0000, - expectedStackAddressLow: 0xfffec000, + expectedStackAddressHigh: 0xfffe0000, + expectedStackAddressLow: 0xfffdc000, expectedHeapBase: 0x30001, - expectedMaxHeapSize: AddressSpaceSize - uint64(VmMaxPageSize)*3 - uint64(0x30001), + expectedMaxHeapSize: AddressSpaceSize - uint64(VmMaxPageSize)*4 - uint64(0x30001), }, { pageSize: 0x4000, roDataSize: maxSize, rwDataSize: 0, stackSize: 0, expectedRODataAddress: 0x10000, expectedRODataSize: maxSize, expectedRWDataAddress: 0x10000 + VmMaxPageSize + maxSize, expectedRWDataSize: 0, + expectedStackAddressHigh: VmAddressSpaceTop - VmMaxPageSize, + expectedStackAddressLow: VmAddressSpaceTop - VmMaxPageSize, expectedStackSize: 0, - expectedStackAddressHigh: VmAddressSpaceTop, - expectedStackAddressLow: VmAddressSpaceTop, expectedHeapBase: 0x10000 + VmMaxPageSize + maxSize, expectedMaxHeapSize: 0, }, { @@ -52,8 +52,8 @@ func Test_memoryMap(t *testing.T) { expectedRODataSize: 0, expectedRWDataAddress: VmMaxPageSize * 2, expectedRWDataSize: maxSize, - expectedStackAddressHigh: VmAddressSpaceTop, - expectedStackAddressLow: VmAddressSpaceTop, + expectedStackAddressHigh: VmAddressSpaceTop - VmMaxPageSize, + expectedStackAddressLow: VmAddressSpaceTop - VmMaxPageSize, expectedStackSize: 0, expectedHeapBase: VmMaxPageSize*2 + maxSize, expectedMaxHeapSize: 0, @@ -63,15 +63,15 @@ func Test_memoryMap(t *testing.T) { expectedRODataSize: 0, expectedRWDataAddress: VmMaxPageSize * 2, expectedRWDataSize: 0, - expectedStackAddressHigh: VmAddressSpaceTop, - expectedStackAddressLow: VmAddressSpaceTop - maxSize, + expectedStackAddressHigh: VmAddressSpaceTop - VmMaxPageSize, + expectedStackAddressLow: VmAddressSpaceTop - VmMaxPageSize - maxSize, expectedStackSize: maxSize, expectedHeapBase: VmMaxPageSize * 2, expectedMaxHeapSize: 0, }} for _, tc := range tests { t.Run("", func(t *testing.T) { - m, err := NewMemoryMap(tc.pageSize, tc.roDataSize, tc.rwDataSize, tc.stackSize, 0) + m, err := NewMemoryMap(uint(tc.pageSize), uint(tc.roDataSize), uint(tc.rwDataSize), uint(tc.stackSize), 0) if err != nil { if tc.expectError { return diff --git a/internal/polkavm/host_call/general_functions.go b/internal/polkavm/host_call/general_functions.go index 316da994..8e7f8ac0 100644 --- a/internal/polkavm/host_call/general_functions.go +++ b/internal/polkavm/host_call/general_functions.go @@ -31,7 +31,7 @@ type AccountInfo struct { TotalItems uint32 // ti } -// GasRemaining ΩG +// GasRemaining ΩG(ξ, ω, ...) func GasRemaining(gas polkavm.Gas, regs polkavm.Registers) (polkavm.Gas, polkavm.Registers, error) { if gas < GasRemainingCost { return gas, regs, polkavm.ErrOutOfGas @@ -45,8 +45,8 @@ func GasRemaining(gas polkavm.Gas, regs polkavm.Registers) (polkavm.Gas, polkavm return gas, regs, nil } -// Lookup ΩL -func Lookup(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, serviceId block.ServiceId, serviceState state.ServiceState) (polkavm.Gas, polkavm.Registers, polkavm.Memory, error) { +// Lookup ΩL(ξ, ω, μ, s, s, d) +func Lookup(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, s state.ServiceAccount, serviceId block.ServiceId, serviceState state.ServiceState) (polkavm.Gas, polkavm.Registers, polkavm.Memory, error) { if gas < LookupCost { return gas, regs, mem, polkavm.ErrOutOfGas } @@ -55,23 +55,15 @@ func Lookup(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, service sID := regs[polkavm.A0] // Determine the lookup key 'a' - var a state.ServiceAccount - if sID == uint32(serviceId) || sID == math.MaxUint32 { + a := s + if sID != math.MaxUint32 && sID != uint32(serviceId) { + var exists bool // Lookup service account by serviceId in the serviceState - serviceAccount, serviceExists := serviceState[serviceId] - if !serviceExists { - regs[polkavm.A0] = uint32(polkavm.HostCallResultNone) - return gas, regs, mem, nil - } - - a = serviceAccount - } else { - storedService, exists := serviceState[block.ServiceId(sID)] + a, exists = serviceState[serviceId] if !exists { regs[polkavm.A0] = uint32(polkavm.HostCallResultNone) return gas, regs, mem, nil } - a = storedService } ho := regs[polkavm.A1] @@ -112,8 +104,8 @@ func Lookup(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, service return gas, regs, mem, err } -// Read ΩR -func Read(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, serviceId block.ServiceId, serviceState state.ServiceState) (polkavm.Gas, polkavm.Registers, polkavm.Memory, error) { +// Read ΩR(ξ, ω, μ, s, s, d) +func Read(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, s state.ServiceAccount, serviceId block.ServiceId, serviceState state.ServiceState) (polkavm.Gas, polkavm.Registers, polkavm.Memory, error) { if gas < ReadCost { return gas, regs, mem, polkavm.ErrOutOfGas } @@ -125,14 +117,13 @@ func Read(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, serviceId bo := regs[polkavm.A3] bz := regs[polkavm.A4] - if sID == math.MaxUint32 { - sID = uint32(serviceId) - } - - // account 'a' - storedService, exists := serviceState[block.ServiceId(sID)] - if !exists { - return gas, regs, mem, polkavm.ErrAccountNotFound + a := s + if sID != math.MaxUint32 && sID != uint32(serviceId) { + var exists bool + a, exists = serviceState[block.ServiceId(sID)] + if !exists { + return gas, regs, mem, polkavm.ErrAccountNotFound + } } // read key data from memory at ko..ko+kz @@ -157,7 +148,7 @@ func Read(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, serviceId // Compute the hash H(E4(s) + keyData) k := blake2b.Sum256(hashInput) - v, exists := storedService.Storage[k] + v, exists := a.Storage[k] if !exists { regs[polkavm.A0] = uint32(polkavm.HostCallResultNone) return gas, regs, mem, nil @@ -179,7 +170,7 @@ func Read(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, serviceId return gas, regs, mem, nil } -// Write ΩW +// Write ΩW (ξ, ω, μ, s, s) func Write(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, serviceId block.ServiceId, s state.ServiceAccount) (polkavm.Gas, polkavm.Registers, polkavm.Memory, state.ServiceAccount, error) { if gas < WriteCost { return gas, regs, mem, s, polkavm.ErrOutOfGas @@ -235,65 +226,48 @@ func Write(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, serviceI return gas, regs, mem, a, err // return service account 'a' as opposed to 's' for not successful paths } -func MakeInfoFunc( - serviceId block.ServiceId, - serviceState state.ServiceState, - memoryMap *polkavm.MemoryMap, -) polkavm.HostFunc { - return func(instance polkavm.Instance) (uint32, error) { - if err := deductGas(instance, InfoCost); err != nil { - return 0, err - } - - sID := instance.GetReg(polkavm.A0) - omega1 := instance.GetReg(polkavm.A1) +// Info ΩI(ξ, ω, μ, s, s, d) +func Info(gas polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, s state.ServiceAccount, serviceId block.ServiceId, serviceState state.ServiceState) (polkavm.Gas, polkavm.Registers, polkavm.Memory, error) { + if gas < InfoCost { + return gas, regs, mem, polkavm.ErrOutOfGas + } + gas -= InfoCost - if sID == math.MaxUint32 { - sID = uint32(serviceId) - } + sID := regs[polkavm.A0] + omega1 := regs[polkavm.A1] - t, exists := serviceState[block.ServiceId(sID)] + t := s + if sID != math.MaxUint32 && sID != uint32(serviceId) { + var exists bool + t, exists = serviceState[block.ServiceId(sID)] if !exists { - return uint32(polkavm.HostCallResultNone), nil - } - - accountInfo := AccountInfo{ - CodeHash: t.CodeHash, - Balance: t.Balance, - ThresholdBalance: t.ThresholdBalance(), - GasLimitForAccumulator: t.GasLimitForAccumulator, - GasLimitOnTransfer: t.GasLimitOnTransfer, - TotalStorageSize: t.TotalStorageSize(), - TotalItems: t.TotalItems(), - } - - serializer := serialization.NewSerializer(codec.NewJamCodec()) - // E(tc, tb, tt, tg , tm, tl, ti) - m, err := serializer.Encode(accountInfo) - if err != nil { - return 0, err - } - - end := omega1 + uint32(len(m)) - if end > memoryMap.RWDataAddress+memoryMap.RWDataSize { - return uint32(polkavm.HostCallResultOob), nil - } - - err = instance.SetMemory(memoryMap, omega1, m) - if err != nil { - return uint32(polkavm.HostCallResultOob), nil + regs[polkavm.A0] = uint32(polkavm.HostCallResultNone) + return gas, regs, mem, nil } + } - return uint32(polkavm.HostCallResultOk), nil + accountInfo := AccountInfo{ + CodeHash: t.CodeHash, + Balance: t.Balance, + ThresholdBalance: t.ThresholdBalance(), + GasLimitForAccumulator: t.GasLimitForAccumulator, + GasLimitOnTransfer: t.GasLimitOnTransfer, + TotalStorageSize: t.TotalStorageSize(), + TotalItems: t.TotalItems(), } -} -func deductGas(instance polkavm.Instance, gasCost int64) error { - if instance.GasRemaining() < gasCost { - return polkavm.ErrOutOfGas + serializer := serialization.NewSerializer(codec.NewJamCodec()) + // E(tc, tb, tt, tg , tm, tl, ti) + m, err := serializer.Encode(accountInfo) + if err != nil { + return gas, regs, mem, polkavm.ErrPanicf(err.Error()) } - instance.DeductGas(gasCost) + if err := mem.Write(omega1, m); err != nil { + regs[polkavm.A0] = uint32(polkavm.HostCallResultOob) + return gas, regs, mem, nil + } - return nil + regs[polkavm.A0] = uint32(polkavm.HostCallResultOk) + return gas, regs, mem, nil } diff --git a/internal/polkavm/host_call/general_functions_test.go b/internal/polkavm/host_call/general_functions_test.go index edaf7985..7e355c38 100644 --- a/internal/polkavm/host_call/general_functions_test.go +++ b/internal/polkavm/host_call/general_functions_test.go @@ -72,12 +72,12 @@ func TestLookup(t *testing.T) { } mem := memoryMap.NewMemory(nil, nil, nil) initialGas := polkavm.Gas(100) - hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, x struct{}) (polkavm.Gas, polkavm.Registers, polkavm.Memory, struct{}, error) { - gasCounter, regs, mem, err = host_call.Lookup(gasCounter, regs, mem, 1, make(state.ServiceState)) + hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, x state.ServiceAccount) (polkavm.Gas, polkavm.Registers, polkavm.Memory, state.ServiceAccount, error) { + gasCounter, regs, mem, err = host_call.Lookup(gasCounter, regs, mem, state.ServiceAccount{}, 1, make(state.ServiceState)) require.NoError(t, err) - return gasCounter, regs, mem, struct{}{}, nil + return gasCounter, regs, mem, x, nil } - gasRemaining, regs, _, _, err := interpreter.InvokeHostCall(pp, memoryMap, 0, initialGas, initialRegs, mem, hostCall, struct{}{}) + gasRemaining, regs, _, _, err := interpreter.InvokeHostCall(pp, memoryMap, 0, initialGas, initialRegs, mem, hostCall, state.ServiceAccount{}) require.ErrorIs(t, err, polkavm.ErrHalt) assert.Equal(t, uint32(polkavm.HostCallResultNone), regs[polkavm.A0]) @@ -105,20 +105,21 @@ func TestLookup(t *testing.T) { polkavm.A2: bo, polkavm.A3: 32, } - serviceState := state.ServiceState{ - serviceId: state.ServiceAccount{ - Storage: map[crypto.Hash][]byte{ - hash: val, - }, + sa := state.ServiceAccount{ + Storage: map[crypto.Hash][]byte{ + hash: val, }, } + serviceState := state.ServiceState{ + serviceId: sa, + } - hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, x struct{}) (polkavm.Gas, polkavm.Registers, polkavm.Memory, struct{}, error) { - gasCounter, regs, mem, err = host_call.Lookup(gasCounter, regs, mem, serviceId, serviceState) + hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, x state.ServiceAccount) (polkavm.Gas, polkavm.Registers, polkavm.Memory, state.ServiceAccount, error) { + gasCounter, regs, mem, err = host_call.Lookup(gasCounter, regs, mem, sa, serviceId, serviceState) require.NoError(t, err) - return gasCounter, regs, mem, struct{}{}, nil + return gasCounter, regs, mem, x, nil } - gasRemaining, regs, mem, _, err := interpreter.InvokeHostCall(pp, memoryMap, 0, initialGas, initialRegs, mem, hostCall, struct{}{}) + gasRemaining, regs, mem, _, err := interpreter.InvokeHostCall(pp, memoryMap, 0, initialGas, initialRegs, mem, hostCall, sa) require.ErrorIs(t, err, polkavm.ErrHalt) actualValue := make([]byte, len(val)) @@ -156,13 +157,14 @@ func TestRead(t *testing.T) { hashInput = append(hashInput, keyData...) k := blake2b.Sum256(hashInput) - serviceState := state.ServiceState{ - serviceId: state.ServiceAccount{ - Storage: map[crypto.Hash][]byte{ - k: value, - }, + sa := state.ServiceAccount{ + Storage: map[crypto.Hash][]byte{ + k: value, }, } + serviceState := state.ServiceState{ + serviceId: sa, + } initialGas := polkavm.Gas(100) @@ -183,12 +185,12 @@ func TestRead(t *testing.T) { err = mem.Write(ko, keyData) require.NoError(t, err) - hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, x struct{}) (polkavm.Gas, polkavm.Registers, polkavm.Memory, struct{}, error) { - gasCounter, regs, mem, err = host_call.Read(gasCounter, regs, mem, serviceId, serviceState) + hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, x state.ServiceAccount) (polkavm.Gas, polkavm.Registers, polkavm.Memory, state.ServiceAccount, error) { + gasCounter, regs, mem, err = host_call.Read(gasCounter, regs, mem, x, serviceId, serviceState) require.NoError(t, err) - return gasCounter, regs, mem, struct{}{}, nil + return gasCounter, regs, mem, x, nil } - gasRemaining, regs, mem, _, err := interpreter.InvokeHostCall(pp, memoryMap, 0, initialGas, initialRegs, mem, hostCall, struct{}{}) + gasRemaining, regs, mem, _, err := interpreter.InvokeHostCall(pp, memoryMap, 0, initialGas, initialRegs, mem, hostCall, sa) require.ErrorIs(t, err, polkavm.ErrHalt) actualValue := make([]byte, len(value)) @@ -204,9 +206,6 @@ func TestRead(t *testing.T) { func TestWrite(t *testing.T) { pp := &polkavm.Program{ - RODataSize: 0, - RWDataSize: 256, - StackSize: 512, 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}, @@ -256,7 +255,7 @@ func TestWrite(t *testing.T) { err = mem.Write(vo, value) require.NoError(t, err) hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, a state.ServiceAccount) (polkavm.Gas, polkavm.Registers, polkavm.Memory, state.ServiceAccount, error) { - gasCounter, regs, mem, a, err = host_call.Write(gasCounter, regs, mem, serviceId, sa) + gasCounter, regs, mem, _, err = host_call.Write(gasCounter, regs, mem, serviceId, sa) require.NoError(t, err) return gasCounter, regs, mem, a, nil } @@ -290,22 +289,13 @@ func TestInfo(t *testing.T) { StackSize: 512, 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: 3, Length: 2}, + {Opcode: polkavm.JumpIndirect, Imm: []uint32{0}, Reg: []polkavm.Reg{polkavm.RA}, Offset: 1, Length: 2}, }, Imports: []string{"info"}, Exports: []polkavm.ProgramExport{{TargetCodeOffset: 0, Symbol: "test_info"}}, } - memoryMap, err := polkavm.NewMemoryMap( - polkavm.VmMinPageSize, - pp.RODataSize, - pp.RWDataSize, - pp.StackSize, - pp.ROData, - ) - require.NoError(t, err) - - module, err := interpreter.NewModule(pp, memoryMap) + memoryMap, err := polkavm.NewMemoryMap(polkavm.VmMinPageSize, 0, 256, 512, 0) require.NoError(t, err) serviceId := block.ServiceId(1) @@ -326,23 +316,33 @@ func TestInfo(t *testing.T) { serviceId: sampleAccount, } - module.AddHostFunc("info", host_call.MakeInfoFunc(serviceId, serviceState, memoryMap)) - - initialGas := int64(100) + initialGas := polkavm.Gas(100) omega1 := memoryMap.RWDataAddress + mem := memoryMap.NewMemory(nil, nil, nil) + initialRegs := polkavm.Registers{ + polkavm.RA: polkavm.VmAddressReturnToHost, + polkavm.SP: memoryMap.StackAddressHigh, + polkavm.A0: uint32(serviceId), + polkavm.A1: omega1, + } + hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, x state.ServiceAccount) (polkavm.Gas, polkavm.Registers, polkavm.Memory, state.ServiceAccount, error) { + gasCounter, regs, mem, err = host_call.Info(gasCounter, regs, mem, x, serviceId, serviceState) + require.NoError(t, err) + return gasCounter, regs, mem, x, nil + } + gasRemaining, regs, _, _, err := interpreter.InvokeHostCall(pp, memoryMap, 0, initialGas, initialRegs, mem, hostCall, sampleAccount) + require.ErrorIs(t, err, polkavm.ErrHalt) - result, instance, err := module.Run("test_info", initialGas, nil, uint32(serviceId), omega1) - require.NoError(t, err) - require.Equal(t, uint32(polkavm.HostCallResultOk), result) + require.Equal(t, uint32(polkavm.HostCallResultOk), regs[polkavm.A0]) var accountInfo host_call.AccountInfo serializer := serialization.NewSerializer(codec.NewJamCodec()) m, err := serializer.Encode(accountInfo) require.NoError(t, err) - dataSize := len(m) - data, err := instance.GetMemory(memoryMap, omega1, dataSize) + data := make([]byte, len(m)) + err = mem.Read(omega1, data) require.NoError(t, err) var receivedAccountInfo host_call.AccountInfo @@ -361,6 +361,6 @@ func TestInfo(t *testing.T) { require.Equal(t, expectedAccountInfo, receivedAccountInfo) - expectedGasRemaining := initialGas - host_call.InfoCost - int64(len(pp.Instructions)) - require.Equal(t, expectedGasRemaining, instance.GasRemaining()) + expectedGasRemaining := initialGas - host_call.InfoCost - polkavm.GasCosts[polkavm.Ecalli] - polkavm.GasCosts[polkavm.JumpIndirect] + require.Equal(t, expectedGasRemaining, gasRemaining) } diff --git a/internal/polkavm/interpreter/interpreter_test.go b/internal/polkavm/interpreter/interpreter_test.go index 66545298..558b2738 100644 --- a/internal/polkavm/interpreter/interpreter_test.go +++ b/internal/polkavm/interpreter/interpreter_test.go @@ -45,12 +45,12 @@ func TestInstance_Execute(t *testing.T) { polkavm.A0: 1, polkavm.A1: 10, } - hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, x nothing) (polkavm.Gas, polkavm.Registers, polkavm.Memory, nothing, polkavm.HostCallCode) { + hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, x nothing) (polkavm.Gas, polkavm.Registers, polkavm.Memory, nothing, error) { if pp.Imports[hostCall] == "get_third_number" { regs1 := getThirdNumber(regs) - return gasCounter, regs1, mem, struct{}{}, polkavm.HostCallResultOk + return gasCounter, regs1, mem, struct{}{}, nil } - return gasCounter, regs, mem, struct{}{}, polkavm.HostCallResultWhat + return gasCounter, regs, mem, struct{}{}, nil } t.Run("1 + 10 + 100 = 111", func(t *testing.T) { gasLimit := polkavm.Gas(1000)