diff --git a/cmd/cartesi-rollups-cli/root/app/util/machine.go b/cmd/cartesi-rollups-cli/root/app/util/machine.go index 2eb30b309..7ee843e5e 100644 --- a/cmd/cartesi-rollups-cli/root/app/util/machine.go +++ b/cmd/cartesi-rollups-cli/root/app/util/machine.go @@ -5,6 +5,7 @@ package util import ( "fmt" + "io" "os" "path" @@ -14,16 +15,32 @@ import ( // Reads the Cartesi Machine hash from machineDir. Returns it as a hex string or // an error func ReadHash(machineDir string) (string, error) { - path := path.Join(machineDir, "hash") - hash, err := os.ReadFile(path) + path := path.Join(machineDir, "hash_tree.sht") + f, err := os.Open(path) if err != nil { - return "", fmt.Errorf("read hash: %w", err) - } else if len(hash) != common.HashLength { + return "", err + } + defer f.Close() + + // root hash is located at this offset (0x60). Double check its value + // with the cartesi-machine-stored-hash tool. + _, err = f.Seek(0x60, io.SeekStart) + if err != nil { + return "", err + } + + // read only 0x20 bytes from it, there are more hash values after it + rawHash := make([]byte, 0x20) + n, err := f.Read(rawHash) + if err != nil { + return "", err + } + if n != common.HashLength { return "", fmt.Errorf( "read hash: wrong size; expected %v bytes but read %v", common.HashLength, - len(hash), + n, ) } - return common.Bytes2Hex(hash), nil + return common.Bytes2Hex(rawHash), nil } diff --git a/cmd/cartesi-rollups-cli/root/deploy/application.go b/cmd/cartesi-rollups-cli/root/deploy/application.go index d965b3282..445a06d72 100644 --- a/cmd/cartesi-rollups-cli/root/deploy/application.go +++ b/cmd/cartesi-rollups-cli/root/deploy/application.go @@ -7,6 +7,7 @@ import ( "context" "encoding/json" "fmt" + "io" "os" "path" "strings" @@ -522,21 +523,38 @@ func buildPrtApplicationDeployment( return request, nil } -// read the hash value from the cartesi machine hash file +// Reads the Cartesi Machine hash from machineDir. Returns it as a hex string or +// an error func readHash(machineDir string) (common.Hash, error) { zero := common.Hash{} - path := path.Join(machineDir, "hash") - hash, err := os.ReadFile(path) + path := path.Join(machineDir, "hash_tree.sht") + f, err := os.Open(path) if err != nil { - return zero, fmt.Errorf("read hash: %w", err) - } else if len(hash) != common.HashLength { + return zero, err + } + defer f.Close() + + // root hash is located at this offset (0x60). Double check its value + // with the cartesi-machine-stored-hash tool. + _, err = f.Seek(0x60, io.SeekStart) + if err != nil { + return zero, err + } + + // read only 0x20 bytes from it, there are more hash values after it + rawHash := make([]byte, 0x20) + n, err := f.Read(rawHash) + if err != nil { + return zero, err + } + if n != common.HashLength { return zero, fmt.Errorf( "read hash: wrong size; expected %v bytes but read %v", common.HashLength, - len(hash), + n, ) } - return common.BytesToHash(hash), nil + return common.BytesToHash(rawHash), nil } func parseHexHash(hash string) (common.Hash, error) { diff --git a/pkg/emulator/emulator.go b/pkg/emulator/emulator.go index 82893b3dc..9845edfe1 100644 --- a/pkg/emulator/emulator.go +++ b/pkg/emulator/emulator.go @@ -50,6 +50,6 @@ func SpawnServer(address string, timeout time.Duration) (*RemoteMachine, string, func CreateMachine(config, runtimeConfig string) (*Machine, error) { machine := &Machine{} - err := machine.Create(config, runtimeConfig) + err := machine.Create(config, runtimeConfig, "") return machine, err } diff --git a/pkg/emulator/machine.go b/pkg/emulator/machine.go index 7fb624d43..0e8ac95ce 100644 --- a/pkg/emulator/machine.go +++ b/pkg/emulator/machine.go @@ -19,6 +19,14 @@ import ( const HashSize = C.sizeof_cm_hash +type SharingMode = C.cm_sharing_mode + +const ( + SharingNone SharingMode = iota + SharingConfig + SharingAll +) + // Common type aliases type Hash = [HashSize]byte @@ -58,11 +66,12 @@ func (m *Machine) Delete() { } // create -func (m *Machine) Create(config, runtimeConfig string) error { +func (m *Machine) Create(config, runtimeConfig, dir string) error { var err error m.callCAPI(func() { var cConfig *C.char var cRuntime *C.char + var cDir *C.char if config != "" { cConfig = C.CString(config) defer C.free(unsafe.Pointer(cConfig)) @@ -71,7 +80,11 @@ func (m *Machine) Create(config, runtimeConfig string) error { cRuntime = C.CString(runtimeConfig) defer C.free(unsafe.Pointer(cRuntime)) } - err = newError(C.cm_create_new(cConfig, cRuntime, &m.ptr)) + if dir != "" { + cDir = C.CString(dir) + defer C.free(unsafe.Pointer(cDir)) + } + err = newError(C.cm_create_new(cConfig, cRuntime, cDir, &m.ptr)) }) return err } @@ -119,27 +132,6 @@ func (m *Machine) GetInitialConfig() (string, error) { return res, nil } -// get_memory_ranges -func (m *Machine) GetMemoryRanges() (string, error) { - var ranges *C.char - var err error - var res string - - m.callCAPI(func() { - err = newError(C.cm_get_memory_ranges(m.ptr, &ranges)) - if err != nil || ranges == nil { - return - } - res = C.GoString(ranges) - // no need to free 'ranges' here, as it is a static string - }) - - if err != nil { - return "", err - } - return res, nil -} - // get_proof func (m *Machine) GetProof(address uint64, log2size int32) (string, error) { var proof *C.char @@ -229,7 +221,7 @@ func (m *Machine) IsEmpty() (bool, error) { } // load -func (m *Machine) Load(dir string, runtimeConfig string) error { +func (m *Machine) Load(dir string, runtimeConfig string, sharingMode SharingMode) error { var err error m.callCAPI(func() { @@ -241,7 +233,7 @@ func (m *Machine) Load(dir string, runtimeConfig string) error { cRuntime = C.CString(runtimeConfig) defer C.free(unsafe.Pointer(cRuntime)) } - err = newError(C.cm_load(m.ptr, cDir, cRuntime)) + err = newError(C.cm_load(m.ptr, cDir, cRuntime, sharingMode)) }) return err @@ -369,6 +361,53 @@ func (m *Machine) Run(mcycleEnd uint64) (BreakReason, error) { return BreakReason(br), nil } +// collect_mcycle_root_hashes +func (m *Machine) CollectMCycleRootHashes(mcycleEnd, mcyclePeriod, mcyclePhase uint64, log2BundleMcycleCount int32, previousBackTree string) ([]byte, error) { + var err error + var result *C.char + + m.callCAPI(func() { + var previousBackTreeC *C.char + if previousBackTree != "" { + previousBackTreeC = C.CString(previousBackTree) + defer C.free(unsafe.Pointer(previousBackTreeC)) + } + err = newError(C.cm_collect_mcycle_root_hashes( + m.ptr, + C.uint64_t(mcycleEnd), + C.uint64_t(mcyclePeriod), + C.uint64_t(mcyclePhase), + C.int32_t(log2BundleMcycleCount), + previousBackTreeC, + &result)) + }) + + if err != nil { + return []byte{}, err + } + return []byte(C.GoString(result)), nil +} + +// collect_uarch_cycle_root_hashes +func (m *Machine) CollectUarchCycleRootHashes(mcycleEnd uint64, log2BundleMcycleCount int32) ([]byte, error) { + var err error + var result *C.char + + m.callCAPI(func() { + err = newError(C.cm_collect_uarch_cycle_root_hashes( + m.ptr, + C.uint64_t(mcycleEnd), + C.int32_t(log2BundleMcycleCount), + &result)) + }) + + if err != nil { + return []byte{}, err + } + return []byte(C.GoString(result)), nil +} + + // send_cmio_response func (m *Machine) SendCmioResponse(reason uint16, data []byte) error { var err error @@ -408,13 +447,13 @@ func (m *Machine) SetRuntimeConfig(runtimeJSON string) error { } // store -func (m *Machine) Store(directory string) error { +func (m *Machine) Store(directory string, sharingMode SharingMode) error { var err error m.callCAPI(func() { cDir := C.CString(directory) defer C.free(unsafe.Pointer(cDir)) - err = newError(C.cm_store(m.ptr, cDir)) + err = newError(C.cm_store(m.ptr, cDir, sharingMode)) }) return err diff --git a/pkg/emulator/types.go b/pkg/emulator/types.go index f57fe9d1f..eda8bcd9c 100644 --- a/pkg/emulator/types.go +++ b/pkg/emulator/types.go @@ -297,11 +297,11 @@ const ( ) const ( - CmioRxBufferStart uint64 = C.CM_PMA_CMIO_RX_BUFFER_START - CmioRxBufferLog2Size uint64 = C.CM_PMA_CMIO_RX_BUFFER_LOG2_SIZE + CmioRxBufferStart uint64 = C.CM_AR_CMIO_RX_BUFFER_START + CmioRxBufferLog2Size uint64 = C.CM_AR_CMIO_RX_BUFFER_LOG2_SIZE - CmioTxBufferStart uint64 = C.CM_PMA_CMIO_TX_BUFFER_START - CmioTxBufferLog2Size uint64 = C.CM_PMA_CMIO_TX_BUFFER_LOG2_SIZE + CmioTxBufferStart uint64 = C.CM_AR_CMIO_TX_BUFFER_START + CmioTxBufferLog2Size uint64 = C.CM_AR_CMIO_TX_BUFFER_LOG2_SIZE ) type MachineRuntimeConfig struct { diff --git a/pkg/machine/libcartesi.go b/pkg/machine/libcartesi.go index 5def485ad..e32bf0df3 100644 --- a/pkg/machine/libcartesi.go +++ b/pkg/machine/libcartesi.go @@ -11,20 +11,22 @@ import ( "time" "github.com/cartesi/rollups-node/pkg/emulator" + "github.com/ethereum/go-ethereum/common" ) // RemoteMachineInterface defines the interface that LibCartesiBackend needs from a remote machine type RemoteMachineInterface interface { SetTimeout(timeoutMs int64) error - Load(dir string, runtimeConfig string) error + Load(dir string, runtimeConfig string, smode emulator.SharingMode) error Run(mcycleEnd uint64) (emulator.BreakReason, error) + CollectMCycleRootHashes(mcycleEnd, mcyclePeriod, mcyclePhase uint64, log2BundleMcycleCount int32, previousBackTree string) ([]byte, error) GetRootHash() (emulator.Hash, error) GetProof(address uint64, log2size int32) (string, error) ReadReg(reg emulator.RegID) (uint64, error) SendCmioResponse(reason uint16, data []byte) error ReceiveCmioRequest() (uint8, uint16, []byte, error) WriteMemory(address uint64, data []byte) error - Store(directory string) error + Store(directory string, smode emulator.SharingMode) error Delete() ForkServer() (*emulator.RemoteMachine, string, uint32, error) ShutdownServer() error @@ -39,6 +41,16 @@ type proofJson struct { TargetHash Hash `json:"target_hash"` } +// Struct for the decoded `result` field of cm_collect_mcycle_root_hashes (originally returned as json). +// The value comes back as a json string, that needs to be decoded to this struct below. +// BackTree may or may not be present, check cm_collect_mcycle_root_hashes documentation for details. +type CollectMCycleRootHashesState struct { + RootHashes [][]byte `json:"hashes"` + MCyclePhase uint64 `json:"mcycle_phase"` + BreakReason string `json:"break_reason"` + BackTree json.RawMessage `json:"back_tree"` +} + func decodeB64To32(dst *Hash, s string) error { // accepts Std (with '=') and Raw (without '=') n, err := base64.StdEncoding.Decode(dst[:], []byte(s)) @@ -111,7 +123,7 @@ func (e *LibCartesiBackend) Load(dir string, runtimeConfig string, timeout time. if err := e.inner.SetTimeout(timeout.Milliseconds()); err != nil { return fmt.Errorf("failed to set operation timeout: %w", err) } - return e.inner.Load(dir, runtimeConfig) + return e.inner.Load(dir, runtimeConfig, emulator.SharingNone) } func (e *LibCartesiBackend) Run(mcycleEnd uint64, timeout time.Duration) (BreakReason, error) { @@ -186,7 +198,7 @@ func (e *LibCartesiBackend) Store(directory string, timeout time.Duration) error if err := e.inner.SetTimeout(timeout.Milliseconds()); err != nil { return fmt.Errorf("failed to set operation timeout: %w", err) } - return e.inner.Store(directory) + return e.inner.Store(directory, emulator.SharingNone) } func (e *LibCartesiBackend) WriteMemory(address uint64, data []byte, timeout time.Duration) error { @@ -235,6 +247,62 @@ func (e *LibCartesiBackend) RunAndCollectRootHashes( mcycleEnd uint64, state *HashCollectorState, timeout time.Duration, +) (reason BreakReason, err error) { + return e.RunAndCollectRootHashesNew(mcycleEnd, state, timeout) +} + +func (e *LibCartesiBackend) RunAndCollectRootHashesNew( + mcycleEnd uint64, + state *HashCollectorState, + timeout time.Duration, +) (reason BreakReason, err error) { + if err := e.inner.SetTimeout(timeout.Milliseconds()); err != nil { + return 0, fmt.Errorf("failed to set operation timeout: %w", err) + } + result, err := e.inner.CollectMCycleRootHashes(mcycleEnd, state.Period, state.Phase, state.BundleLog2, "") + if err != nil { + return Failed, err + } + + var hcs CollectMCycleRootHashesState + err = json.Unmarshal(result, &hcs) + if err != nil { + fmt.Println(string(result)) + return Failed, err + } + + var br BreakReason + switch hcs.BreakReason { + case "failed": + br = Failed + case "halted": + br = Halted + case "reached_target_mcycle": + br = ReachedTargetMcycle + case "yielded_automatically": + br = YieldedAutomatically + case "yielded_softly": + br = YieldedSoftly + case "yielded_manually": + br = YieldedManually + default: + return Failed, fmt.Errorf("unimplemented break reason on RunAndCollectRootHashesNew") + } + + state.Hashes = nil + for _, hash := range hcs.RootHashes { + state.Hashes = append(state.Hashes, common.BytesToHash(hash)) + } + state.Phase = hcs.MCyclePhase + state.BackTree = hcs.BackTree + // state.MaxHashes = ? + return br, nil +} + +func (e *LibCartesiBackend) RunAndCollectRootHashesOld( + mcycleEnd uint64, + state *HashCollectorState, + timeout time.Duration, ) (reason BreakReason, err error) { if state == nil { return Failed, errors.New("nil state")