Skip to content

Commit 4bc3805

Browse files
committed
feat: use collect mcycle root hashes API
1 parent a134185 commit 4bc3805

3 files changed

Lines changed: 54 additions & 102 deletions

File tree

pkg/emulator/machine.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ func (m *Machine) Run(mcycleEnd uint64) (BreakReason, error) {
354354
}
355355

356356
// collect_mcycle_root_hashes
357-
func (m *Machine) CollectMCycleRootHashes(mcycleEnd, mcyclePeriod, mcyclePhase uint64, log2BundleMcycleCount int32, previousBackTree string) ([]byte, error) {
357+
func (m *Machine) CollectMCycleRootHashes(mcycleEnd, log2McyclePeriod, mcyclePhase uint64, log2BundleMcycleCount int32, previousBackTree string) ([]byte, error) {
358358
var err error
359359
var result []byte
360360

@@ -368,7 +368,7 @@ func (m *Machine) CollectMCycleRootHashes(mcycleEnd, mcyclePeriod, mcyclePhase u
368368
err = newError(C.cm_collect_mcycle_root_hashes(
369369
m.ptr,
370370
C.uint64_t(mcycleEnd),
371-
C.uint64_t(mcyclePeriod),
371+
C.uint64_t(log2McyclePeriod),
372372
C.uint64_t(mcyclePhase),
373373
C.int32_t(log2BundleMcycleCount),
374374
previousBackTreeC,
@@ -425,10 +425,10 @@ func (m *Machine) SendCmioResponse(revertRootHash *Hash, reason uint16, data []b
425425
}
426426
err = newError(C.cm_send_cmio_response(
427427
m.ptr,
428-
ptrHash,
429428
C.uint16_t(reason),
430429
ptrData,
431430
sizeData,
431+
ptrHash,
432432
))
433433
})
434434

pkg/machine/libcartesi.go

Lines changed: 46 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"encoding/json"
99
"errors"
1010
"fmt"
11+
"math/bits"
1112
"time"
1213

1314
"github.com/cartesi/rollups-node/pkg/emulator"
@@ -28,6 +29,7 @@ type RemoteMachineInterface interface {
2829
Delete()
2930
ForkServer() (*emulator.RemoteMachine, string, uint32, error)
3031
ShutdownServer() error
32+
CollectMCycleRootHashes(mcycleEnd, log2McyclePeriod, mcyclePhase uint64, log2BundleMcycleCount int32, previousBackTree string) ([]byte, error)
3133
}
3234

3335
type proofJson struct {
@@ -230,124 +232,69 @@ func (e *LibCartesiBackend) CmioRxBufferSize() uint64 {
230232
return 1 << emulator.CmioRxBufferLog2Size
231233
}
232234

235+
func decodeBreakReason(s string) BreakReason {
236+
switch s {
237+
case "yielded_automatically":
238+
return YieldedAutomatically
239+
case "yielded_manually":
240+
return YieldedManually
241+
case "yielded_softly":
242+
return YieldedSoftly
243+
case "reached_target_mcycle":
244+
return ReachedTargetMcycle
245+
case "halted":
246+
return Halted
247+
case "failed":
248+
return Failed
249+
default:
250+
return Failed
251+
}
252+
}
253+
233254
func (e *LibCartesiBackend) RunAndCollectRootHashes(
234255
mcycleEnd uint64,
235256
state *HashCollectorState,
236257
timeout time.Duration,
237258
) (reason BreakReason, err error) {
259+
238260
if state == nil {
239261
return Failed, errors.New("nil state")
240262
}
241263
if state.Period == 0 {
242264
return Failed, errors.New("State.Period must be > 0")
243265
}
244-
245-
// Set up timeout management: calculate absolute deadline if timeout is specified
246-
var deadline time.Time
247-
hasDeadline := timeout > 0
248-
if hasDeadline {
249-
deadline = time.Now().Add(timeout)
250-
}
251-
remaining := func() time.Duration {
252-
if !hasDeadline {
253-
return 0
254-
}
255-
d := time.Until(deadline)
256-
if d <= 0 {
257-
return time.Nanosecond
258-
}
259-
return d
266+
log2Period := uint64(bits.Len64(state.Period) - 1)
267+
if (uint64(1) << log2Period != state.Period) {
268+
return Failed, fmt.Errorf("period must be a power of 2, got %v.", state.Period)
260269
}
261-
checkDeadline := func() error {
262-
if hasDeadline && time.Now().After(deadline) {
263-
return errors.New("runWithRootHashes: deadline exceeded")
264-
}
265-
return nil
270+
if err := e.inner.SetTimeout(timeout.Milliseconds()); err != nil {
271+
return Failed, fmt.Errorf("failed to set operation timeout: %w", err)
266272
}
267273

268-
if err := checkDeadline(); err != nil {
274+
rawResult, err := e.inner.CollectMCycleRootHashes(mcycleEnd, log2Period, state.Phase, state.BundleLog2, "")
275+
if err != nil {
269276
return Failed, err
270277
}
271-
cur, err := e.ReadMCycle(remaining())
278+
result := struct {
279+
RootHashes []string `json:"hashes"`
280+
MCyclePhase uint64 `json:"mcycle_phase"`
281+
BreakReason string `json:"break_reason"`
282+
BackTree json.RawMessage `json:"back_tree,omitempty"`
283+
}{}
284+
err = json.Unmarshal(rawResult, &result)
272285
if err != nil {
273-
return Failed, err
286+
return Failed, fmt.Errorf("failed to unmarshal CollectMCycleRootHashes result: %w", err)
274287
}
275288

276-
collected := (uint64)(0)
277-
278-
for {
279-
if err := checkDeadline(); err != nil {
280-
return Failed, err
281-
}
282-
if cur >= mcycleEnd {
283-
// No more cycles to execute
284-
return ReachedTargetMcycle, nil
285-
}
286-
287-
// Calculate the next collection point: distance to the next multiple of the period
288-
// This ensures we collect hashes at regular intervals aligned with the period
289-
var step uint64
290-
if r := state.Phase % state.Period; r == 0 {
291-
step = state.Period
292-
} else {
293-
step = state.Period - r
294-
}
295-
296-
nextHashCycle := cur + step
297-
target := min(nextHashCycle, mcycleEnd)
298-
299-
// Run the machine until target cycle or until it yields/halts
300-
br, err := e.Run(target, remaining())
301-
if err != nil {
302-
return Failed, err
303-
}
304-
305-
// Check where we stopped after the run
306-
if err := checkDeadline(); err != nil {
307-
return Failed, err
308-
}
309-
pos, err := e.ReadMCycle(remaining())
310-
if err != nil {
311-
return Failed, err
312-
}
313-
314-
advanced := pos - cur
315-
state.Phase = (state.Phase + advanced) % state.Period
316-
cur = pos
317-
318-
// Only collect hash if we reached the exact boundary (pos == nextHashCycle)
319-
// This ensures "hash after each complete period", matching the C API behavior
320-
// and avoiding duplicate collections if the machine stops early due to yields
321-
if pos == nextHashCycle {
322-
if err := checkDeadline(); err != nil {
323-
return Failed, err
324-
}
325-
h, err := e.GetRootHash(remaining())
326-
if err != nil {
327-
return Failed, err
328-
}
329-
330-
state.Hashes = append(state.Hashes, h)
331-
332-
collected++
333-
if state.MaxHashes > 0 && collected >= state.MaxHashes {
334-
return YieldedSoftly, nil
335-
}
336-
}
337-
338-
switch br {
339-
case ReachedTargetMcycle:
340-
if cur >= mcycleEnd {
341-
return ReachedTargetMcycle, nil
342-
}
343-
case YieldedManually:
344-
return br, nil
345-
case YieldedAutomatically, YieldedSoftly, Halted:
346-
return br, nil
347-
case Failed:
348-
return Failed, errors.New("run failed")
349-
default:
350-
return Failed, errors.New("unknown break reason")
289+
// convert from base64 and append to collector state
290+
for i, base64Hash := range result.RootHashes {
291+
hash := Hash{}
292+
if err := decodeB64To32(&hash, base64Hash); err != nil {
293+
return Failed, fmt.Errorf("received an invalid hash during RunAndCollectRootHashes at index %v, with value: %v.", i, base64Hash)
351294
}
295+
state.Hashes = append(state.Hashes, hash)
352296
}
297+
state.Phase = result.MCyclePhase
298+
state.BackTree = result.BackTree
299+
return decodeBreakReason(result.BreakReason), nil
353300
}

pkg/machine/libcartesi_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,3 +486,8 @@ func (m *MockRemoteMachine) ShutdownServer() error {
486486
args := m.Called()
487487
return args.Error(0)
488488
}
489+
490+
func (m *MockRemoteMachine) CollectMCycleRootHashes(mcycleEnd, log2McyclePeriod, mcyclePhase uint64, log2BundleMcycleCount int32, previousBackTree string) ([]byte, error) {
491+
args := m.Called(mcycleEnd, log2McyclePeriod, mcyclePhase, log2BundleMcycleCount, previousBackTree)
492+
return args.Get(0).([]byte), args.Error(1)
493+
}

0 commit comments

Comments
 (0)