Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refine invocation #202

Merged
merged 2 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions internal/refine/refine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package refine

import (
"errors"

"github.com/eigerco/strawberry/internal/block"
"github.com/eigerco/strawberry/internal/crypto"
"github.com/eigerco/strawberry/internal/polkavm"
"github.com/eigerco/strawberry/internal/polkavm/host_call"
"github.com/eigerco/strawberry/internal/polkavm/interpreter"
"github.com/eigerco/strawberry/internal/state"
"github.com/eigerco/strawberry/internal/work"
"github.com/eigerco/strawberry/pkg/serialization/codec/jam"
)

const (
RefineCost = 10
)

var (
ErrBad = errors.New("service’s code was not available for lookup") // BAD
ErrBig = errors.New("code beyond the maximum size allowed") // BIG
)

type Refine struct {
state state.State
}

// InvokePVM ΨR(H, NG, NS , H, Y, X, H, Y, ⟦G⟧, ⟦Y⟧, N) → (Y ∪ J, ⟦Y⟧)
func (r *Refine) InvokePVM(
serviceCodePredictionHash crypto.Hash, gas uint64, serviceIndex block.ServiceId,
workPackageHash crypto.Hash, workPayload []byte, refinementContext block.RefinementContext,
authorizerHash crypto.Hash, authorizerHashOutput []byte, importedSegments []polkavm.Segment,
extrinsicDataBlobs [][]byte, exportOffset uint64,
pantrif marked this conversation as resolved.
Show resolved Hide resolved
) ([]byte, []polkavm.Segment, error) {
// let a = E(s, y, p, c, a, ↕o, ↕[↕x | x <− x])
args, err := jam.Marshal(struct {
ServiceIndex block.ServiceId
WorkPayload []byte
WorkPackageHash crypto.Hash
RefinementContext block.RefinementContext
AuthorizerHash crypto.Hash
AuthorizerHashOutput []byte
ExtrinsicDataBlobs [][]byte
}{
ServiceIndex: serviceIndex,
WorkPayload: workPayload,
WorkPackageHash: workPackageHash,
RefinementContext: refinementContext,
AuthorizerHash: authorizerHash,
AuthorizerHashOutput: authorizerHashOutput,
ExtrinsicDataBlobs: extrinsicDataBlobs, // we assume the extrinsic data is a ordered sequence
})
if err != nil {
return nil, nil, err
}

// if s ∉ K(δ) ∨ Λ(δ[s], ct, c) = ∅ then (BAD, [])
service, ok := r.state.Services[serviceIndex]
if !ok {
return nil, nil, ErrBad
}
code := service.LookupPreimage(refinementContext.LookupAnchor.Timeslot, serviceCodePredictionHash)
if code == nil {
return nil, nil, ErrBad
}

// if |Λ(δ[s], ct, c)| > W_C then (BIG, [])
if len(code) > work.MaxSizeServiceCode {
return nil, nil, ErrBig
}

// F ∈ Ω⟨(D⟨N → M⟩, ⟦Y⟧)⟩∶ (n, ϱ, ω, μ, (m, e))
hostCall := func(hostCall uint32, gasCounter polkavm.Gas, regs polkavm.Registers, mem polkavm.Memory, ctxPair polkavm.RefineContextPair) (polkavm.Gas, polkavm.Registers, polkavm.Memory, polkavm.RefineContextPair, error) {
switch hostCall {
case host_call.HistoricalLookupID:
gasCounter, regs, mem, ctxPair, err = host_call.HistoricalLookup(gasCounter, regs, mem, ctxPair, serviceIndex, r.state.Services, refinementContext.LookupAnchor.Timeslot)
case host_call.ImportID:
gasCounter, regs, mem, ctxPair, err = host_call.Import(gasCounter, regs, mem, ctxPair, importedSegments)
case host_call.ExportID:
gasCounter, regs, mem, ctxPair, err = host_call.Export(gasCounter, regs, mem, ctxPair, exportOffset)
case host_call.GasID:
gasCounter, regs, err = host_call.GasRemaining(gasCounter, regs)
case host_call.MachineID:
gasCounter, regs, mem, ctxPair, err = host_call.Machine(gasCounter, regs, mem, ctxPair)
case host_call.PeekID:
gasCounter, regs, mem, ctxPair, err = host_call.Peek(gasCounter, regs, mem, ctxPair)
case host_call.ZeroID:
gasCounter, regs, mem, ctxPair, err = host_call.Zero(gasCounter, regs, mem, ctxPair)
case host_call.PokeID:
gasCounter, regs, mem, ctxPair, err = host_call.Poke(gasCounter, regs, mem, ctxPair)
case host_call.VoidID:
gasCounter, regs, mem, ctxPair, err = host_call.Void(gasCounter, regs, mem, ctxPair)
case host_call.InvokeID:
gasCounter, regs, mem, ctxPair, err = host_call.Invoke(gasCounter, regs, mem, ctxPair)
case host_call.ExpungeID:
gasCounter, regs, mem, ctxPair, err = host_call.Expunge(gasCounter, regs, mem, ctxPair)
default:
regs[polkavm.A0] = uint64(host_call.WHAT)
gasCounter -= RefineCost

}
return gasCounter, regs, mem, ctxPair, nil
}

// (g, r, (m, e)) = ΨM(Λ(δ[s], ct, c), 0, g, a, F, (∅, []))∶
_, result, ctxPair, err := interpreter.InvokeWholeProgram(code, 0, gas, args, hostCall, polkavm.RefineContextPair{
IntegratedPVMMap: make(map[uint64]polkavm.IntegratedPVM),
Segments: []polkavm.Segment{},
})

// if r ∈ {∞, ☇} then (r, [])
if err != nil {
panicErr := &polkavm.ErrPanic{}
if errors.Is(err, polkavm.ErrOutOfGas) || errors.As(err, &panicErr) {
return nil, nil, err
}
}

return result, ctxPair.Segments, err
}
1 change: 1 addition & 0 deletions internal/work/constants.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package work

const (
MaxSizeServiceCode = 4000000 // WC = 4000000: The maximum size of service code in octets.
MaxNumberOfEntries = 1 << 11 // WM = 2^11: The maximum number of entries in a work-package manifest.
NumberOfErasureCodecPiecesInSegment = 6 // WP = 6: The number of erasure-coded pieces in a segment.
SizeOfErasureCodecPiecesInOctets = 684 // WE = 684: The basic size of erasure-coded pieces in octets.
Expand Down
Loading