diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c57f24b..a74b105 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,7 @@ # Contributing +**Due to the JAM Prize rules, we do not accept any external contributions at the moment.** + ## Strawberry To contribute to the Strawberry implementation, start with the proper development copy. diff --git a/internal/polkavm/common.go b/internal/polkavm/common.go index 8663f09..45f664f 100644 --- a/internal/polkavm/common.go +++ b/internal/polkavm/common.go @@ -218,22 +218,22 @@ type Mutator interface { CmovIfZero(d Reg, s, c Reg) CmovIfZeroImm(d Reg, c Reg, s uint32) CmovIfNotZero(d Reg, s, c Reg) - RotL64(d Reg, s, c Reg) - RotL32(d Reg, s, c Reg) - RotR64(d Reg, s, c Reg) - RotR32(d Reg, s, c Reg) - AndInv(d Reg, s, c Reg) - OrInv(d Reg, s, c Reg) - Xnor(d Reg, s, c Reg) - Max(d Reg, s, c Reg) - MaxU(d Reg, s, c Reg) - Min(d Reg, s, c Reg) - MinU(d Reg, s, c Reg) + RotateLeft64(d Reg, s1, s2 Reg) + RotateLeft32(d Reg, s1, s2 Reg) + RotateRight64(d Reg, s1, s2 Reg) + RotateRight32(d Reg, s1, s2 Reg) + AndInverted(d Reg, s1, s2 Reg) + OrInverted(d Reg, s1, s2 Reg) + Xnor(d Reg, s1, s2 Reg) + Max(d Reg, s1, s2 Reg) + MaxUnsigned(d Reg, s1, s2 Reg) + Min(d Reg, s1, s2 Reg) + MinUnsigned(d Reg, s1, s2 Reg) CmovIfNotZeroImm(d Reg, c Reg, s uint32) - RotR64Imm(d Reg, c Reg, s uint32) - RotR64ImmAlt(d Reg, c Reg, s uint32) - RotR32Imm(d Reg, c Reg, s uint32) - RotR32ImmAlt(d Reg, c Reg, s uint32) + RotateRight64Imm(d Reg, s1 Reg, s2 uint32) + RotateRight64ImmAlt(d Reg, s1 Reg, s2 uint32) + RotateRight32Imm(d Reg, s1 Reg, s2 uint32) + RotateRight32ImmAlt(d Reg, s1 Reg, s2 uint32) StoreU8(src Reg, offset uint32) error StoreU16(src Reg, offset uint32) error StoreU32(src Reg, offset uint32) error diff --git a/internal/polkavm/host_call/accumulate_functions.go b/internal/polkavm/host_call/accumulate_functions.go index 5cd271a..a41d4a3 100644 --- a/internal/polkavm/host_call/accumulate_functions.go +++ b/internal/polkavm/host_call/accumulate_functions.go @@ -322,7 +322,42 @@ func Query(gas Gas, regs Registers, mem Memory, ctxPair AccumulateContextPair) ( } gas -= QueryCost - // TODO: implement method + addr, preimageMetaKeyLength := regs[A0], regs[A1] + + // let h = μo..o+32 if Zo..o+32 ⊂ Vμ + h := make([]byte, 32) + if err := mem.Read(uint32(addr), h); err != nil { + // otherwise ∇ => OOB + return gas, withCode(regs, OOB), mem, ctxPair, nil + } + + // let a = (xs)l[h, z] if (h, z) ∈ K((xs)l) + serviceAccount := ctxPair.RegularCtx.ServiceAccount() + key := service.PreImageMetaKey{ + Hash: crypto.Hash(h), + Length: service.PreimageLength(preimageMetaKeyLength), + } + a, exists := serviceAccount.PreimageMeta[key] + if !exists { + // a = ∇ => (NONE, 0) + regs[A1] = 0 + return gas, withCode(regs, NONE), mem, ctxPair, nil + } + + switch len(a) { + case 0: + // a = [] => (0, 0) + regs[A0], regs[A1] = 0, 0 + case 1: + // a = [x] => (1 + 2^32 * x, 0) + regs[A0], regs[A1] = 1+(uint64(a[0])<<32), 0 + case 2: + // a = [x, y] => (2 + 2^32 * x, y) + regs[A0], regs[A1] = 2+(uint64(a[0])<<32), uint64(a[1]) + case 3: + // a = [x, y, z] => (3 + 2^32 * x, y + 2^32 * z) + regs[A0], regs[A1] = 3+(uint64(a[0])<<32), uint64(a[1])+(uint64(a[2])<<32) + } return gas, regs, mem, ctxPair, nil } @@ -440,7 +475,18 @@ func Yield(gas Gas, regs Registers, mem Memory, ctxPair AccumulateContextPair) ( } gas -= YieldCost - // TODO: implement method + addr := regs[A0] - return gas, regs, mem, ctxPair, nil + // let h = μo..o+32 if Zo..o+32 ⊂ Vμ otherwise ∇ + hBytes := make([]byte, 32) + if err := mem.Read(uint32(addr), hBytes); err != nil { + // (ω′7, x′_y) = (OOB, x_y) if h = ∇ + return gas, withCode(regs, OOB), mem, ctxPair, nil + } + + // (ω′7, x′_y) = (OK, h) otherwise + h := crypto.Hash(hBytes) + ctxPair.RegularCtx.AccumulationHash = &h + + return gas, withCode(regs, OK), mem, ctxPair, nil } diff --git a/internal/polkavm/host_call/accumulate_functions_test.go b/internal/polkavm/host_call/accumulate_functions_test.go index e727964..a179f5e 100644 --- a/internal/polkavm/host_call/accumulate_functions_test.go +++ b/internal/polkavm/host_call/accumulate_functions_test.go @@ -347,6 +347,178 @@ func TestAccumulate(t *testing.T) { }, }, }, + { + name: "query_empty_timeslots", + fn: fnStd(Query), + initialGas: 100, + initialRegs: deltaRegs{ + A1: 123, + }, + alloc: alloc{ + A0: hash2bytes(randomHash), + }, + X: AccumulateContext{ + ServiceId: 999, + AccumulationState: state.AccumulationState{ + ServiceState: service.ServiceState{ + 999: { + PreimageMeta: map[service.PreImageMetaKey]service.PreimageHistoricalTimeslots{ + { + Hash: randomHash, + Length: 123, + }: {}, + }, + }, + }, + }, + }, + expectedDeltaRegs: deltaRegs{ + A0: 0, + A1: 0, + }, + expectedGas: 88, + expectedX: AccumulateContext{ + ServiceId: 999, + AccumulationState: state.AccumulationState{ + ServiceState: service.ServiceState{ + 999: { + PreimageMeta: map[service.PreImageMetaKey]service.PreimageHistoricalTimeslots{ + {Hash: randomHash, Length: 123}: {}, + }, + }, + }, + }, + }, + }, + { + name: "query_1timeslot", + fn: fnStd(Query), + initialGas: 100, + initialRegs: deltaRegs{ + A1: 123, + }, + alloc: alloc{ + A0: hash2bytes(randomHash), + }, + X: AccumulateContext{ + ServiceId: 999, + AccumulationState: state.AccumulationState{ + ServiceState: service.ServiceState{ + 999: { + PreimageMeta: map[service.PreImageMetaKey]service.PreimageHistoricalTimeslots{ + { + Hash: randomHash, + Length: 123, + }: {11}, + }, + }, + }, + }, + }, + expectedDeltaRegs: deltaRegs{ + A0: 1 + (uint64(11) << 32), + A1: 0, + }, + expectedGas: 88, + expectedX: AccumulateContext{ + ServiceId: 999, + AccumulationState: state.AccumulationState{ + ServiceState: service.ServiceState{ + 999: { + PreimageMeta: map[service.PreImageMetaKey]service.PreimageHistoricalTimeslots{ + {Hash: randomHash, Length: 123}: {11}, + }, + }, + }, + }, + }, + }, + { + name: "query_2timeslots", + fn: fnStd(Query), + initialGas: 100, + initialRegs: deltaRegs{ + A1: 123, + }, + alloc: alloc{ + A0: hash2bytes(randomHash), + }, + X: AccumulateContext{ + ServiceId: 999, + AccumulationState: state.AccumulationState{ + ServiceState: service.ServiceState{ + 999: { + PreimageMeta: map[service.PreImageMetaKey]service.PreimageHistoricalTimeslots{ + { + Hash: randomHash, + Length: 123, + }: {11, 12}, + }, + }, + }, + }, + }, + expectedDeltaRegs: deltaRegs{ + A0: 2 + (uint64(11) << 32), + A1: uint64(12), + }, + expectedGas: 88, + expectedX: AccumulateContext{ + ServiceId: 999, + AccumulationState: state.AccumulationState{ + ServiceState: service.ServiceState{ + 999: { + PreimageMeta: map[service.PreImageMetaKey]service.PreimageHistoricalTimeslots{ + {Hash: randomHash, Length: 123}: {11, 12}, + }, + }, + }, + }, + }, + }, + { + name: "query_3timeslots", + fn: fnStd(Query), + initialGas: 100, + initialRegs: deltaRegs{ + A1: 123, + }, + alloc: alloc{ + A0: hash2bytes(randomHash), + }, + X: AccumulateContext{ + ServiceId: 999, + AccumulationState: state.AccumulationState{ + ServiceState: service.ServiceState{ + 999: { + PreimageMeta: map[service.PreImageMetaKey]service.PreimageHistoricalTimeslots{ + { + Hash: randomHash, + Length: 123, + }: {11, 12, 13}, + }, + }, + }, + }, + }, + expectedDeltaRegs: deltaRegs{ + A0: 3 + (uint64(11) << 32), + A1: uint64(12) + (uint64(13) << 32), + }, + expectedGas: 88, + expectedX: AccumulateContext{ + ServiceId: 999, + AccumulationState: state.AccumulationState{ + ServiceState: service.ServiceState{ + 999: { + PreimageMeta: map[service.PreImageMetaKey]service.PreimageHistoricalTimeslots{ + {Hash: randomHash, Length: 123}: {11, 12, 13}, + }, + }, + }, + }, + }, + }, { name: "solicit_out_of_gas", fn: fnTms(Solicit), @@ -694,6 +866,25 @@ func TestAccumulate(t *testing.T) { }, }, }, + { + name: "yield", + fn: fnStd(Yield), + initialGas: 100, + alloc: alloc{ + A0: hash2bytes(randomHash), + }, + X: AccumulateContext{ + ServiceId: 999, + }, + expectedDeltaRegs: deltaRegs{ + A0: uint64(OK), + }, + expectedGas: 88, + expectedX: AccumulateContext{ + ServiceId: 999, + AccumulationHash: &randomHash, + }, + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { diff --git a/internal/polkavm/instructions.go b/internal/polkavm/instructions.go index 645bac2..2b583aa 100644 --- a/internal/polkavm/instructions.go +++ b/internal/polkavm/instructions.go @@ -620,13 +620,13 @@ func (i *Instruction) Mutate(mutator Mutator) (uint32, error) { case CmovIfNotZeroImm: mutator.CmovIfNotZeroImm(i.Reg[0], i.Reg[1], i.Imm[0]) case RotR64Imm: - mutator.RotR64Imm(i.Reg[0], i.Reg[1], i.Imm[0]) + mutator.RotateRight64Imm(i.Reg[0], i.Reg[1], i.Imm[0]) case RotR64ImmAlt: - mutator.RotR64ImmAlt(i.Reg[0], i.Reg[1], i.Imm[0]) + mutator.RotateRight64ImmAlt(i.Reg[0], i.Reg[1], i.Imm[0]) case RotR32Imm: - mutator.RotR32Imm(i.Reg[0], i.Reg[1], i.Imm[0]) + mutator.RotateRight32Imm(i.Reg[0], i.Reg[1], i.Imm[0]) case RotR32ImmAlt: - mutator.RotR32ImmAlt(i.Reg[0], i.Reg[1], i.Imm[0]) + mutator.RotateRight32ImmAlt(i.Reg[0], i.Reg[1], i.Imm[0]) case BranchEq: mutator.BranchEq(i.Reg[0], i.Reg[1], i.Imm[0]) case BranchNotEq: @@ -700,27 +700,27 @@ func (i *Instruction) Mutate(mutator Mutator) (uint32, error) { case CmovIfNotZero: mutator.CmovIfNotZero(i.Reg[0], i.Reg[1], i.Reg[2]) case RotL64: - mutator.RotL64(i.Reg[0], i.Reg[1], i.Reg[2]) + mutator.RotateLeft64(i.Reg[0], i.Reg[1], i.Reg[2]) case RotL32: - mutator.RotL32(i.Reg[0], i.Reg[1], i.Reg[2]) + mutator.RotateLeft32(i.Reg[0], i.Reg[1], i.Reg[2]) case RotR64: - mutator.RotR64(i.Reg[0], i.Reg[1], i.Reg[2]) + mutator.RotateRight64(i.Reg[0], i.Reg[1], i.Reg[2]) case RotR32: - mutator.RotR32(i.Reg[0], i.Reg[1], i.Reg[2]) + mutator.RotateRight32(i.Reg[0], i.Reg[1], i.Reg[2]) case AndInv: - mutator.AndInv(i.Reg[0], i.Reg[1], i.Reg[2]) + mutator.AndInverted(i.Reg[0], i.Reg[1], i.Reg[2]) case OrInv: - mutator.OrInv(i.Reg[0], i.Reg[1], i.Reg[2]) + mutator.OrInverted(i.Reg[0], i.Reg[1], i.Reg[2]) case Xnor: mutator.Xnor(i.Reg[0], i.Reg[1], i.Reg[2]) case Max: mutator.Max(i.Reg[0], i.Reg[1], i.Reg[2]) case MaxU: - mutator.MaxU(i.Reg[0], i.Reg[1], i.Reg[2]) + mutator.MaxUnsigned(i.Reg[0], i.Reg[1], i.Reg[2]) case Min: mutator.Min(i.Reg[0], i.Reg[1], i.Reg[2]) case MinU: - mutator.MinU(i.Reg[0], i.Reg[1], i.Reg[2]) + mutator.MinUnsigned(i.Reg[0], i.Reg[1], i.Reg[2]) case Jump: mutator.Jump(i.Imm[0]) case Ecalli: diff --git a/internal/polkavm/instructions_test.go b/internal/polkavm/instructions_test.go index 522454a..4825379 100644 --- a/internal/polkavm/instructions_test.go +++ b/internal/polkavm/instructions_test.go @@ -143,21 +143,21 @@ func TestInstruction_Mutate(t *testing.T) { mutator.EXPECT().SignExtend16(gomock.Any(), gomock.Any()) mutator.EXPECT().ZeroExtend16(gomock.Any(), gomock.Any()) mutator.EXPECT().ReverseBytes(gomock.Any(), gomock.Any()) - mutator.EXPECT().RotR64Imm(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().RotR64ImmAlt(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().RotR32Imm(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().RotR32ImmAlt(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().RotL64(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().RotL32(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().RotR64(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().RotR32(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().AndInv(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().OrInv(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().RotateRight64Imm(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().RotateRight64ImmAlt(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().RotateRight32Imm(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().RotateRight32ImmAlt(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().RotateLeft64(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().RotateLeft32(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().RotateRight64(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().RotateRight32(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().AndInverted(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().OrInverted(gomock.Any(), gomock.Any(), gomock.Any()) mutator.EXPECT().Xnor(gomock.Any(), gomock.Any(), gomock.Any()) mutator.EXPECT().Max(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().MaxU(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().MaxUnsigned(gomock.Any(), gomock.Any(), gomock.Any()) mutator.EXPECT().Min(gomock.Any(), gomock.Any(), gomock.Any()) - mutator.EXPECT().MinU(gomock.Any(), gomock.Any(), gomock.Any()) + mutator.EXPECT().MinUnsigned(gomock.Any(), gomock.Any(), gomock.Any()) for _, opcode := range allInstrOpcodes { instr := Instruction{ diff --git a/internal/polkavm/interpreter/mutator.go b/internal/polkavm/interpreter/mutator.go index 2b795e4..cfdd15b 100644 --- a/internal/polkavm/interpreter/mutator.go +++ b/internal/polkavm/interpreter/mutator.go @@ -3,6 +3,7 @@ package interpreter import ( "log" "math" + "math/bits" "github.com/eigerco/strawberry/pkg/serialization/codec/jam" @@ -150,34 +151,34 @@ func (m *Mutator) Sbrk(dst polkavm.Reg, sizeReg polkavm.Reg) error { return nil } func (m *Mutator) CountSetBits64(d polkavm.Reg, s polkavm.Reg) { - panic("todo: implement") + m.set64(d, uint64(bits.OnesCount64(m.get64(s)))) } func (m *Mutator) CountSetBits32(d polkavm.Reg, s polkavm.Reg) { - panic("todo: implement") + m.set32(d, uint32(bits.OnesCount32(m.get32(s)))) } func (m *Mutator) LeadingZeroBits64(d polkavm.Reg, s polkavm.Reg) { - panic("todo: implement") + m.set64(d, uint64(bits.LeadingZeros64(m.get64(s)))) } func (m *Mutator) LeadingZeroBits32(d polkavm.Reg, s polkavm.Reg) { - panic("todo: implement") + m.set32(d, uint32(bits.LeadingZeros32(m.get32(s)))) } func (m *Mutator) TrailingZeroBits64(d polkavm.Reg, s polkavm.Reg) { - panic("todo: implement") + m.set64(d, uint64(bits.TrailingZeros64(m.get64(s)))) } func (m *Mutator) TrailingZeroBits32(d polkavm.Reg, s polkavm.Reg) { - panic("todo: implement") + m.set32(d, uint32(bits.TrailingZeros32(m.get32(s)))) } func (m *Mutator) SignExtend8(d polkavm.Reg, s polkavm.Reg) { - panic("todo: implement") + m.set64(d, uint64(int64(int8(uint8(m.get64(s)))))) } func (m *Mutator) SignExtend16(d polkavm.Reg, s polkavm.Reg) { - panic("todo: implement") + m.set64(d, uint64(int64(int16(uint16(m.get64(s)))))) } func (m *Mutator) ZeroExtend16(d polkavm.Reg, s polkavm.Reg) { - panic("todo: implement") + m.set64(d, uint64(uint16(m.get64(s)))) } func (m *Mutator) ReverseBytes(d polkavm.Reg, s polkavm.Reg) { - panic("todo: implement") + m.set64(d, bits.ReverseBytes64(m.get64(s))) } func (m *Mutator) MoveReg(d polkavm.Reg, s polkavm.Reg) { m.setNext32(d, m.get32(s)) @@ -269,17 +270,17 @@ func (m *Mutator) SetGreaterThanSignedImm(d polkavm.Reg, s1 polkavm.Reg, s2 uint func (m *Mutator) ShiftLogicalRightImmAlt32(d polkavm.Reg, s2 polkavm.Reg, s1 uint32) { m.setNext32(d, uint32(s1)>>m.get32(s2)) } -func (m *Mutator) RotR64Imm(d polkavm.Reg, c polkavm.Reg, s uint32) { - panic("todo: implement") +func (m *Mutator) RotateRight64Imm(d polkavm.Reg, s1 polkavm.Reg, s2 uint32) { + m.set64(d, bits.RotateLeft64(m.get64(s1), -int(s2))) } -func (m *Mutator) RotR64ImmAlt(d polkavm.Reg, c polkavm.Reg, s uint32) { - panic("todo: implement") +func (m *Mutator) RotateRight64ImmAlt(d polkavm.Reg, s1 polkavm.Reg, s2 uint32) { + m.set64(d, bits.RotateLeft64(uint64(s2), -int(m.get64(s1)))) } -func (m *Mutator) RotR32Imm(d polkavm.Reg, c polkavm.Reg, s uint32) { - panic("todo: implement") +func (m *Mutator) RotateRight32Imm(d polkavm.Reg, s1 polkavm.Reg, s2 uint32) { + m.set32(d, bits.RotateLeft32(m.get32(s1), -int(s2))) } -func (m *Mutator) RotR32ImmAlt(d polkavm.Reg, c polkavm.Reg, s uint32) { - panic("todo: implement") +func (m *Mutator) RotateRight32ImmAlt(d polkavm.Reg, s1 polkavm.Reg, s2 uint32) { + m.set32(d, bits.RotateLeft32(s2, -int(m.get32(s1)))) } func (m *Mutator) ShiftLogicalRightImmAlt64(d polkavm.Reg, s2 polkavm.Reg, s1 uint32) { m.setNext32(d, s1>>m.get32(s2)) @@ -497,38 +498,38 @@ func (m *Mutator) CmovIfNotZero(d polkavm.Reg, s, c polkavm.Reg) { } m.instance.NextOffsets() } -func (m *Mutator) RotL64(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) RotateLeft64(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set64(d, bits.RotateLeft64(m.get64(s1), int(m.get64(s2)))) } -func (m *Mutator) RotL32(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) RotateLeft32(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set32(d, bits.RotateLeft32(m.get32(s1), int(m.get32(s2)))) } -func (m *Mutator) RotR64(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) RotateRight64(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set64(d, bits.RotateLeft64(m.get64(s1), -int(m.get64(s2)))) } -func (m *Mutator) RotR32(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) RotateRight32(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set32(d, bits.RotateLeft32(m.get32(s1), -int(m.get32(s2)))) } -func (m *Mutator) AndInv(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) AndInverted(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set64(d, m.get64(s1)&^m.get64(s2)) } -func (m *Mutator) OrInv(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) OrInverted(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set64(d, m.get64(s1)|^m.get64(s2)) } -func (m *Mutator) Xnor(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) Xnor(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set64(d, ^(m.get64(s1) ^ m.get64(s2))) } -func (m *Mutator) Max(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) Max(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set64(d, uint64(max(int64(m.get64(s1)), int64(m.get64(s2))))) } -func (m *Mutator) MaxU(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) MaxUnsigned(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set64(d, max(m.get64(s1), m.get64(s2))) } -func (m *Mutator) Min(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) Min(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set64(d, uint64(min(int64(m.get64(s1)), int64(m.get64(s2))))) } -func (m *Mutator) MinU(d polkavm.Reg, s, c polkavm.Reg) { - panic("todo: implement") +func (m *Mutator) MinUnsigned(d polkavm.Reg, s1, s2 polkavm.Reg) { + m.set64(d, min(m.get64(s1), m.get64(s2))) } func (m *Mutator) CmovIfNotZeroImm(d polkavm.Reg, c polkavm.Reg, s uint32) { if m.get32(c) != 0 { diff --git a/internal/polkavm/mutator_mock.go b/internal/polkavm/mutator_mock.go index 1e65062..329d613 100644 --- a/internal/polkavm/mutator_mock.go +++ b/internal/polkavm/mutator_mock.go @@ -105,16 +105,16 @@ func (mr *MockMutatorMockRecorder) AndImm(arg0, arg1, arg2 interface{}) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AndImm", reflect.TypeOf((*MockMutator)(nil).AndImm), arg0, arg1, arg2) } -// AndInv mocks base method. -func (m *MockMutator) AndInv(arg0, arg1, arg2 Reg) { +// AndInverted mocks base method. +func (m *MockMutator) AndInverted(arg0, arg1, arg2 Reg) { m.ctrl.T.Helper() - m.ctrl.Call(m, "AndInv", arg0, arg1, arg2) + m.ctrl.Call(m, "AndInverted", arg0, arg1, arg2) } -// AndInv indicates an expected call of AndInv. -func (mr *MockMutatorMockRecorder) AndInv(arg0, arg1, arg2 interface{}) *gomock.Call { +// AndInverted indicates an expected call of AndInverted. +func (mr *MockMutatorMockRecorder) AndInverted(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AndInv", reflect.TypeOf((*MockMutator)(nil).AndInv), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AndInverted", reflect.TypeOf((*MockMutator)(nil).AndInverted), arg0, arg1, arg2) } // BranchEq mocks base method. @@ -749,16 +749,16 @@ func (mr *MockMutatorMockRecorder) Max(arg0, arg1, arg2 interface{}) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Max", reflect.TypeOf((*MockMutator)(nil).Max), arg0, arg1, arg2) } -// MaxU mocks base method. -func (m *MockMutator) MaxU(arg0, arg1, arg2 Reg) { +// MaxUnsigned mocks base method. +func (m *MockMutator) MaxUnsigned(arg0, arg1, arg2 Reg) { m.ctrl.T.Helper() - m.ctrl.Call(m, "MaxU", arg0, arg1, arg2) + m.ctrl.Call(m, "MaxUnsigned", arg0, arg1, arg2) } -// MaxU indicates an expected call of MaxU. -func (mr *MockMutatorMockRecorder) MaxU(arg0, arg1, arg2 interface{}) *gomock.Call { +// MaxUnsigned indicates an expected call of MaxUnsigned. +func (mr *MockMutatorMockRecorder) MaxUnsigned(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxU", reflect.TypeOf((*MockMutator)(nil).MaxU), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxUnsigned", reflect.TypeOf((*MockMutator)(nil).MaxUnsigned), arg0, arg1, arg2) } // Min mocks base method. @@ -773,16 +773,16 @@ func (mr *MockMutatorMockRecorder) Min(arg0, arg1, arg2 interface{}) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Min", reflect.TypeOf((*MockMutator)(nil).Min), arg0, arg1, arg2) } -// MinU mocks base method. -func (m *MockMutator) MinU(arg0, arg1, arg2 Reg) { +// MinUnsigned mocks base method. +func (m *MockMutator) MinUnsigned(arg0, arg1, arg2 Reg) { m.ctrl.T.Helper() - m.ctrl.Call(m, "MinU", arg0, arg1, arg2) + m.ctrl.Call(m, "MinUnsigned", arg0, arg1, arg2) } -// MinU indicates an expected call of MinU. -func (mr *MockMutatorMockRecorder) MinU(arg0, arg1, arg2 interface{}) *gomock.Call { +// MinUnsigned indicates an expected call of MinUnsigned. +func (mr *MockMutatorMockRecorder) MinUnsigned(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MinU", reflect.TypeOf((*MockMutator)(nil).MinU), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MinUnsigned", reflect.TypeOf((*MockMutator)(nil).MinUnsigned), arg0, arg1, arg2) } // MoveReg mocks base method. @@ -929,16 +929,16 @@ func (mr *MockMutatorMockRecorder) OrImm(arg0, arg1, arg2 interface{}) *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrImm", reflect.TypeOf((*MockMutator)(nil).OrImm), arg0, arg1, arg2) } -// OrInv mocks base method. -func (m *MockMutator) OrInv(arg0, arg1, arg2 Reg) { +// OrInverted mocks base method. +func (m *MockMutator) OrInverted(arg0, arg1, arg2 Reg) { m.ctrl.T.Helper() - m.ctrl.Call(m, "OrInv", arg0, arg1, arg2) + m.ctrl.Call(m, "OrInverted", arg0, arg1, arg2) } -// OrInv indicates an expected call of OrInv. -func (mr *MockMutatorMockRecorder) OrInv(arg0, arg1, arg2 interface{}) *gomock.Call { +// OrInverted indicates an expected call of OrInverted. +func (mr *MockMutatorMockRecorder) OrInverted(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrInv", reflect.TypeOf((*MockMutator)(nil).OrInv), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OrInverted", reflect.TypeOf((*MockMutator)(nil).OrInverted), arg0, arg1, arg2) } // RemSigned32 mocks base method. @@ -1001,100 +1001,100 @@ func (mr *MockMutatorMockRecorder) ReverseBytes(arg0, arg1 interface{}) *gomock. return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReverseBytes", reflect.TypeOf((*MockMutator)(nil).ReverseBytes), arg0, arg1) } -// RotL32 mocks base method. -func (m *MockMutator) RotL32(arg0, arg1, arg2 Reg) { +// RotateLeft32 mocks base method. +func (m *MockMutator) RotateLeft32(arg0, arg1, arg2 Reg) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RotL32", arg0, arg1, arg2) + m.ctrl.Call(m, "RotateLeft32", arg0, arg1, arg2) } -// RotL32 indicates an expected call of RotL32. -func (mr *MockMutatorMockRecorder) RotL32(arg0, arg1, arg2 interface{}) *gomock.Call { +// RotateLeft32 indicates an expected call of RotateLeft32. +func (mr *MockMutatorMockRecorder) RotateLeft32(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotL32", reflect.TypeOf((*MockMutator)(nil).RotL32), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotateLeft32", reflect.TypeOf((*MockMutator)(nil).RotateLeft32), arg0, arg1, arg2) } -// RotL64 mocks base method. -func (m *MockMutator) RotL64(arg0, arg1, arg2 Reg) { +// RotateLeft64 mocks base method. +func (m *MockMutator) RotateLeft64(arg0, arg1, arg2 Reg) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RotL64", arg0, arg1, arg2) + m.ctrl.Call(m, "RotateLeft64", arg0, arg1, arg2) } -// RotL64 indicates an expected call of RotL64. -func (mr *MockMutatorMockRecorder) RotL64(arg0, arg1, arg2 interface{}) *gomock.Call { +// RotateLeft64 indicates an expected call of RotateLeft64. +func (mr *MockMutatorMockRecorder) RotateLeft64(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotL64", reflect.TypeOf((*MockMutator)(nil).RotL64), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotateLeft64", reflect.TypeOf((*MockMutator)(nil).RotateLeft64), arg0, arg1, arg2) } -// RotR32 mocks base method. -func (m *MockMutator) RotR32(arg0, arg1, arg2 Reg) { +// RotateRight32 mocks base method. +func (m *MockMutator) RotateRight32(arg0, arg1, arg2 Reg) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RotR32", arg0, arg1, arg2) + m.ctrl.Call(m, "RotateRight32", arg0, arg1, arg2) } -// RotR32 indicates an expected call of RotR32. -func (mr *MockMutatorMockRecorder) RotR32(arg0, arg1, arg2 interface{}) *gomock.Call { +// RotateRight32 indicates an expected call of RotateRight32. +func (mr *MockMutatorMockRecorder) RotateRight32(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotR32", reflect.TypeOf((*MockMutator)(nil).RotR32), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotateRight32", reflect.TypeOf((*MockMutator)(nil).RotateRight32), arg0, arg1, arg2) } -// RotR32Imm mocks base method. -func (m *MockMutator) RotR32Imm(arg0, arg1 Reg, arg2 uint32) { +// RotateRight32Imm mocks base method. +func (m *MockMutator) RotateRight32Imm(arg0, arg1 Reg, arg2 uint32) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RotR32Imm", arg0, arg1, arg2) + m.ctrl.Call(m, "RotateRight32Imm", arg0, arg1, arg2) } -// RotR32Imm indicates an expected call of RotR32Imm. -func (mr *MockMutatorMockRecorder) RotR32Imm(arg0, arg1, arg2 interface{}) *gomock.Call { +// RotateRight32Imm indicates an expected call of RotateRight32Imm. +func (mr *MockMutatorMockRecorder) RotateRight32Imm(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotR32Imm", reflect.TypeOf((*MockMutator)(nil).RotR32Imm), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotateRight32Imm", reflect.TypeOf((*MockMutator)(nil).RotateRight32Imm), arg0, arg1, arg2) } -// RotR32ImmAlt mocks base method. -func (m *MockMutator) RotR32ImmAlt(arg0, arg1 Reg, arg2 uint32) { +// RotateRight32ImmAlt mocks base method. +func (m *MockMutator) RotateRight32ImmAlt(arg0, arg1 Reg, arg2 uint32) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RotR32ImmAlt", arg0, arg1, arg2) + m.ctrl.Call(m, "RotateRight32ImmAlt", arg0, arg1, arg2) } -// RotR32ImmAlt indicates an expected call of RotR32ImmAlt. -func (mr *MockMutatorMockRecorder) RotR32ImmAlt(arg0, arg1, arg2 interface{}) *gomock.Call { +// RotateRight32ImmAlt indicates an expected call of RotateRight32ImmAlt. +func (mr *MockMutatorMockRecorder) RotateRight32ImmAlt(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotR32ImmAlt", reflect.TypeOf((*MockMutator)(nil).RotR32ImmAlt), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotateRight32ImmAlt", reflect.TypeOf((*MockMutator)(nil).RotateRight32ImmAlt), arg0, arg1, arg2) } -// RotR64 mocks base method. -func (m *MockMutator) RotR64(arg0, arg1, arg2 Reg) { +// RotateRight64 mocks base method. +func (m *MockMutator) RotateRight64(arg0, arg1, arg2 Reg) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RotR64", arg0, arg1, arg2) + m.ctrl.Call(m, "RotateRight64", arg0, arg1, arg2) } -// RotR64 indicates an expected call of RotR64. -func (mr *MockMutatorMockRecorder) RotR64(arg0, arg1, arg2 interface{}) *gomock.Call { +// RotateRight64 indicates an expected call of RotateRight64. +func (mr *MockMutatorMockRecorder) RotateRight64(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotR64", reflect.TypeOf((*MockMutator)(nil).RotR64), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotateRight64", reflect.TypeOf((*MockMutator)(nil).RotateRight64), arg0, arg1, arg2) } -// RotR64Imm mocks base method. -func (m *MockMutator) RotR64Imm(arg0, arg1 Reg, arg2 uint32) { +// RotateRight64Imm mocks base method. +func (m *MockMutator) RotateRight64Imm(arg0, arg1 Reg, arg2 uint32) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RotR64Imm", arg0, arg1, arg2) + m.ctrl.Call(m, "RotateRight64Imm", arg0, arg1, arg2) } -// RotR64Imm indicates an expected call of RotR64Imm. -func (mr *MockMutatorMockRecorder) RotR64Imm(arg0, arg1, arg2 interface{}) *gomock.Call { +// RotateRight64Imm indicates an expected call of RotateRight64Imm. +func (mr *MockMutatorMockRecorder) RotateRight64Imm(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotR64Imm", reflect.TypeOf((*MockMutator)(nil).RotR64Imm), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotateRight64Imm", reflect.TypeOf((*MockMutator)(nil).RotateRight64Imm), arg0, arg1, arg2) } -// RotR64ImmAlt mocks base method. -func (m *MockMutator) RotR64ImmAlt(arg0, arg1 Reg, arg2 uint32) { +// RotateRight64ImmAlt mocks base method. +func (m *MockMutator) RotateRight64ImmAlt(arg0, arg1 Reg, arg2 uint32) { m.ctrl.T.Helper() - m.ctrl.Call(m, "RotR64ImmAlt", arg0, arg1, arg2) + m.ctrl.Call(m, "RotateRight64ImmAlt", arg0, arg1, arg2) } -// RotR64ImmAlt indicates an expected call of RotR64ImmAlt. -func (mr *MockMutatorMockRecorder) RotR64ImmAlt(arg0, arg1, arg2 interface{}) *gomock.Call { +// RotateRight64ImmAlt indicates an expected call of RotateRight64ImmAlt. +func (mr *MockMutatorMockRecorder) RotateRight64ImmAlt(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotR64ImmAlt", reflect.TypeOf((*MockMutator)(nil).RotR64ImmAlt), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RotateRight64ImmAlt", reflect.TypeOf((*MockMutator)(nil).RotateRight64ImmAlt), arg0, arg1, arg2) } // Sbrk mocks base method. diff --git a/internal/statetransition/state_transition.go b/internal/statetransition/state_transition.go index 9b9fb5f..674715d 100644 --- a/internal/statetransition/state_transition.go +++ b/internal/statetransition/state_transition.go @@ -1814,12 +1814,13 @@ func CalculateWorkReportsAndAccumulate(header *block.Header, currentState *state gasLimit := max(service.TotalGasAccumulation, common.MaxAllocatedGasAccumulation*uint64(common.TotalNumberOfCores)+privSvcGas) // let (n, o, t, C) = ∆+(g, W∗, (χ, δ, ι, φ), χg ) (eq. 12.21) - maxReports, newAccumulationState, transfers, hashPairs := NewAccumulator(currentState, header, newTimeslot).SequentialDelta(gasLimit, accumulatableWorkReports, state.AccumulationState{ - PrivilegedServices: currentState.PrivilegedServices, - ServiceState: currentState.Services, - ValidatorKeys: currentState.ValidatorState.QueuedValidators, - PendingAuthorizersQueues: currentState.PendingAuthorizersQueues, - }, currentState.PrivilegedServices) + maxReports, newAccumulationState, transfers, hashPairs := NewAccumulator(currentState, header, newTimeslot). + SequentialDelta(gasLimit, accumulatableWorkReports, state.AccumulationState{ + PrivilegedServices: currentState.PrivilegedServices, + ServiceState: currentState.Services, + ValidatorKeys: currentState.ValidatorState.QueuedValidators, + PendingAuthorizersQueues: currentState.PendingAuthorizersQueues, + }, currentState.PrivilegedServices) // (χ′, δ†, ι′, φ′) ≡ o (eq. 12.22) intermediateServiceState := newAccumulationState.ServiceState @@ -2424,6 +2425,14 @@ func (a *Accumulator) SequentialDelta( return uint32(maxReports), newCtx, transfers, hashPairs } +func getServiceIdsAsSet(m map[block.ServiceId]service.ServiceAccount) map[block.ServiceId]struct{} { + m2 := make(map[block.ServiceId]struct{}, len(m)) + for k := range m { + m2[k] = struct{}{} + } + return m2 +} + // ParallelDelta implements equation 12.17 (∆*) func (a *Accumulator) ParallelDelta( initialAccState state.AccumulationState, @@ -2436,7 +2445,7 @@ func (a *Accumulator) ParallelDelta( ServiceHashPairs, // accumulation outputs ) { // Get all unique service indices involved (s) - // s = {rs S w ∈ w, r ∈ wr} ∪ K(f) + // s = {rs | w ∈ w, r ∈ wr} ∪ K(f) serviceIndices := make(map[block.ServiceId]struct{}) // From work reports @@ -2461,6 +2470,12 @@ func (a *Accumulator) ParallelDelta( var mu sync.Mutex var wg sync.WaitGroup + // n = ⋃[s∈s]({(∆1(o, w, f , s)o)d ∖ K(d ∖ {s})}) + allResultServices := make(map[block.ServiceId]service.ServiceAccount) + + // m = ⋃[s∈s](K(d) ∖ K((∆1(o, w, f , s)o)d)) + resultServicesExclude := make(map[block.ServiceId]struct{}) + for svcId := range serviceIndices { wg.Add(1) go func(serviceId block.ServiceId) { @@ -2487,10 +2502,28 @@ func (a *Accumulator) ParallelDelta( }) } - // d′ = {s ↦ ds S s ∈ K(d) ∖ s} ∪ [⋃ s∈s] ((∆1(o, w, f , s)o)d - for serviceId, serviceAccount := range accState.ServiceState { - newAccState.ServiceState[serviceId] = serviceAccount - } + resultServices := maps.Clone(accState.ServiceState) + + // (∆1(o, w, f , s)o)d ∖ K(d ∖ {s}) + maps.DeleteFunc(resultServices, func(id block.ServiceId, _ service.ServiceAccount) bool { + if id == serviceId { + return false + } + + _, ok := initialAccState.ServiceState[id] + return ok + }) + + maps.Copy(allResultServices, resultServices) + + initialServicesKeys := getServiceIdsAsSet(initialAccState.ServiceState) + maps.DeleteFunc(initialServicesKeys, func(id block.ServiceId, _ struct{}) bool { + _, ok := accState.ServiceState[id] + return ok + }) + + maps.Copy(resultServicesExclude, initialServicesKeys) + }(svcId) } @@ -2536,6 +2569,14 @@ func (a *Accumulator) ParallelDelta( // Wait for all goroutines to complete wg.Wait() + // (d ∪ n) ∖ m + maps.Copy(newAccState.ServiceState, initialAccState.ServiceState) + maps.Copy(newAccState.ServiceState, allResultServices) + maps.DeleteFunc(newAccState.ServiceState, func(id block.ServiceId, _ service.ServiceAccount) bool { + _, ok := resultServicesExclude[id] + return ok + }) + // Sort accumulation pairs by service ID to ensure deterministic output sort.Slice(accumHashPairs, func(i, j int) bool { return accumHashPairs[i].ServiceId < accumHashPairs[j].ServiceId @@ -2544,7 +2585,7 @@ func (a *Accumulator) ParallelDelta( return totalGasUsed, newAccState, allTransfers, accumHashPairs } -// Delta1 implements equation 12.19 (∆1) +// Delta1 implements equation 12.19 ∆1 (U, ⟦W⟧, D⟨NS → NG⟩, NS ) → (U, ⟦T⟧, H?, NG) func (a *Accumulator) Delta1( accumulationState state.AccumulationState, workReports []block.WorkReport,