Skip to content

Commit 69822b4

Browse files
committed
feat: use collect mcycle root hashes API
1 parent 9f37088 commit 69822b4

3 files changed

Lines changed: 59 additions & 102 deletions

File tree

pkg/emulator/machine.go

Lines changed: 2 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,
@@ -419,7 +419,6 @@ func (m *Machine) SendCmioResponse(revertRootHash Hash, reason uint16, data []by
419419
if sizeData > 0 {
420420
ptrData = (*C.uint8_t)(unsafe.Pointer(&data[0]))
421421
}
422-
//var cHash C.cm_hash = revertRootHash
423422
err = newError(C.cm_send_cmio_response(
424423
m.ptr,
425424
(*[32]C.uint8_t)(unsafe.Pointer(&revertRootHash)),

pkg/machine/libcartesi.go

Lines changed: 52 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,75 @@ 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+
return e.RunAndCollectRootHashesNew(mcycleEnd, state, timeout)
260+
}
261+
262+
func (e *LibCartesiBackend) RunAndCollectRootHashesNew(
263+
mcycleEnd uint64,
264+
state *HashCollectorState,
265+
timeout time.Duration,
266+
) (reason BreakReason, err error) {
267+
238268
if state == nil {
239269
return Failed, errors.New("nil state")
240270
}
241271
if state.Period == 0 {
242272
return Failed, errors.New("State.Period must be > 0")
243273
}
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
274+
log2Period := uint64(bits.Len64(state.Period) - 1)
275+
if (uint64(1) << log2Period != state.Period) {
276+
return Failed, fmt.Errorf("period must be a power of 2, got %v.", state.Period)
260277
}
261-
checkDeadline := func() error {
262-
if hasDeadline && time.Now().After(deadline) {
263-
return errors.New("runWithRootHashes: deadline exceeded")
264-
}
265-
return nil
278+
if err := e.inner.SetTimeout(timeout.Milliseconds()); err != nil {
279+
return Failed, fmt.Errorf("failed to set operation timeout: %w", err)
266280
}
267281

268-
if err := checkDeadline(); err != nil {
282+
rawResult, err := e.inner.CollectMCycleRootHashes(mcycleEnd, log2Period, state.Phase, state.BundleLog2, "")
283+
if err != nil {
269284
return Failed, err
270285
}
271-
cur, err := e.ReadMCycle(remaining())
286+
result := struct {
287+
RootHashes []string `json:"hashes"`
288+
MCyclePhase uint64 `json:"mcycle_phase"`
289+
BreakReason string `json:"break_reason"`
290+
BackTree json.RawMessage `json:"back_tree,omitempty"`
291+
}{}
292+
err = json.Unmarshal(rawResult, &result)
272293
if err != nil {
273-
return Failed, err
294+
return Failed, fmt.Errorf("failed to unmarshal CollectMCycleRootHashes result: %w", err)
274295
}
275296

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")
297+
// convert from base64 and append to collector state
298+
for i, base64Hash := range result.RootHashes {
299+
hash := Hash{}
300+
if err := decodeB64To32(&hash, base64Hash); err != nil {
301+
return Failed, fmt.Errorf("received an invalid hash during RunAndCollectRootHashes at index %v, with value: %v.", i, base64Hash)
351302
}
303+
state.Hashes = append(state.Hashes, hash)
352304
}
305+
return decodeBreakReason(result.BreakReason), nil
353306
}

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)