Skip to content

Commit

Permalink
refactor: add unit tests to the rollupsmachine
Browse files Browse the repository at this point in the history
  • Loading branch information
renan061 committed Aug 15, 2024
1 parent 417d1c9 commit 479e6bd
Show file tree
Hide file tree
Showing 10 changed files with 1,502 additions and 501 deletions.
3 changes: 3 additions & 0 deletions pkg/emulator/pma.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ import "C"
const (
CmioRxBufferStart uint64 = C.PMA_CMIO_RX_BUFFER_START_DEF
CmioTxBufferStart uint64 = C.PMA_CMIO_TX_BUFFER_START_DEF

CmioRxBufferLog2Size uint64 = C.PMA_CMIO_RX_BUFFER_LOG2_SIZE_DEF
CmioTxBufferLog2Size uint64 = C.PMA_CMIO_TX_BUFFER_LOG2_SIZE_DEF
)
27 changes: 27 additions & 0 deletions pkg/rollupsmachine/abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[{
"type" : "function",
"name" : "EvmAdvance",
"inputs" : [
{ "type" : "uint256" },
{ "type" : "address" },
{ "type" : "address" },
{ "type" : "uint256" },
{ "type" : "uint256" },
{ "type" : "uint256" },
{ "type" : "bytes" }
]
}, {
"type" : "function",
"name" : "Voucher",
"inputs" : [
{ "type" : "address" },
{ "type" : "uint256" },
{ "type" : "bytes" }
]
}, {
"type" : "function",
"name" : "Notice",
"inputs" : [
{ "type" : "bytes" }
]
}]
30 changes: 30 additions & 0 deletions pkg/rollupsmachine/cartesimachine/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

// Package cartesimachine abstracts into an interface the functionalities expected from a machine
// library. It provides an implementation for that interface using the emulator package.
package cartesimachine

import "github.com/cartesi/rollups-node/pkg/emulator"

type (
RequestType uint8
YieldReason uint8
)

type CartesiMachine interface {
Fork() (CartesiMachine, error)
Continue() error
Run(until uint64) (emulator.BreakReason, error)
Close() error

IsAtManualYield() (bool, error)
ReadYieldReason() (emulator.HtifYieldReason, error)
ReadHash() ([32]byte, error)
ReadCycle() (uint64, error)
ReadMemory() ([]byte, error)
WriteRequest([]byte, RequestType) error

PayloadLengthLimit() uint
Address() string
}
235 changes: 235 additions & 0 deletions pkg/rollupsmachine/cartesimachine/machine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
// (c) Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)

package cartesimachine

import (
"errors"
"fmt"
"math"

"github.com/cartesi/rollups-node/pkg/emulator"
)

const (
AdvanceStateRequest RequestType = 0
InspectStateRequest RequestType = 1
)

var (
ErrCartesiMachine = errors.New("cartesi machine internal error")

ErrOrphanServer = errors.New("cartesi machine server was left orphan")
)

type cartesiMachine struct {
inner *emulator.Machine
server *emulator.RemoteMachineManager

address string // address of the JSON RPC remote cartesi machine server
}

// Load loads the machine stored at path into the remote server from address.
func Load(path, address string, config *emulator.MachineRuntimeConfig) (CartesiMachine, error) {
machine := &cartesiMachine{address: address}

// Creates the server machine manager (the server's manager).
server, err := emulator.NewRemoteMachineManager(address)
if err != nil {
err = fmt.Errorf("could not create the remote machine manager: %w", err)
return nil, errCartesiMachine(err)
}
machine.server = server

// Loads the machine stored at path into the server.
inner, err := server.LoadMachine(path, config)
if err != nil {
defer machine.server.Delete()
err = fmt.Errorf("could not load the machine: %w", err)
return nil, errCartesiMachine(err)
}
machine.inner = inner

return machine, nil
}

// Fork forks the machine.
//
// When Fork returns with the ErrOrphanServer error, it also returns with a non-nil CartesiMachine
// the can be used to retrieve the orphan server's address.
func (machine *cartesiMachine) Fork() (CartesiMachine, error) {
newMachine := new(cartesiMachine)

// Forks the server.
address, err := machine.server.Fork()
if err != nil {
err = fmt.Errorf("could not fork the machine: %w", err)
return nil, errCartesiMachine(err)
}
newMachine.address = address

// Instantiates the new remote machine manager.
server, err := emulator.NewRemoteMachineManager(address)
if err != nil {
err = fmt.Errorf("could not create the new remote machine manager: %w", err)
errOrphanServer := errOrphanServerWithAddress(address)
return newMachine, errors.Join(ErrCartesiMachine, err, errOrphanServer)
}
newMachine.server = server

// Gets the inner machine reference from the server.
inner, err := newMachine.server.GetMachine()
if err != nil {
err = fmt.Errorf("could not get the machine from the server: %w", err)
return newMachine, errors.Join(ErrCartesiMachine, err, newMachine.closeServer())
}
newMachine.inner = inner

return newMachine, nil
}

func (machine *cartesiMachine) Run(until uint64) (emulator.BreakReason, error) {
breakReason, err := machine.inner.Run(until)
if err != nil {
assert(breakReason == emulator.BreakReasonFailed, breakReason.String())
err = fmt.Errorf("machine run failed: %w", err)
return breakReason, errCartesiMachine(err)
}
return breakReason, nil
}

func (machine *cartesiMachine) IsAtManualYield() (bool, error) {
iflagsY, err := machine.inner.ReadIFlagsY()
if err != nil {
err = fmt.Errorf("could not read iflagsY: %w", err)
return iflagsY, errCartesiMachine(err)
}
return iflagsY, nil
}

func (machine *cartesiMachine) ReadYieldReason() (emulator.HtifYieldReason, error) {
tohost, err := machine.readHtifToHostData()
if err != nil {
return emulator.HtifYieldReason(0), err
}
yieldReason := tohost >> 32 //nolint:mnd
return emulator.HtifYieldReason(yieldReason), nil
}

func (machine *cartesiMachine) ReadHash() ([32]byte, error) {
hash, err := machine.inner.GetRootHash()
if err != nil {
err := fmt.Errorf("could not get the machine's root hash: %w", err)
return hash, errCartesiMachine(err)
}
return hash, nil
}

func (machine *cartesiMachine) ReadMemory() ([]byte, error) {
tohost, err := machine.readHtifToHostData()
if err != nil {
return nil, err
}
length := tohost & 0x00000000ffffffff //nolint:mnd

read, err := machine.inner.ReadMemory(emulator.CmioTxBufferStart, length)
if err != nil {
err := fmt.Errorf("could not read from the memory: %w", err)
return nil, errCartesiMachine(err)
}

return read, nil
}

func (machine *cartesiMachine) WriteRequest(data []byte, type_ RequestType) error {
// Writes the request's data.
err := machine.inner.WriteMemory(emulator.CmioRxBufferStart, data)
if err != nil {
err := fmt.Errorf("could not write to the memory: %w", err)
return errCartesiMachine(err)
}

// Writes the request's type and length.
fromhost := ((uint64(type_) << 32) | (uint64(len(data)) & 0xffffffff)) //nolint:mnd
err = machine.inner.WriteHtifFromHostData(fromhost)
if err != nil {
err := fmt.Errorf("could not write HTIF fromhost data: %w", err)
return errCartesiMachine(err)
}

return nil
}

func (machine *cartesiMachine) Continue() error {
err := machine.inner.ResetIFlagsY()
if err != nil {
err = fmt.Errorf("could not reset iflagsY: %w", err)
return errCartesiMachine(err)
}
return nil
}

func (machine *cartesiMachine) ReadCycle() (uint64, error) {
cycle, err := machine.inner.ReadMCycle()
if err != nil {
err = fmt.Errorf("could not read the machine's current cycle: %w", err)
return cycle, errCartesiMachine(err)
}
return cycle, nil
}

func (machine cartesiMachine) PayloadLengthLimit() uint {
expo := float64(emulator.CmioRxBufferLog2Size)
var payloadLengthLimit = uint(math.Pow(2, expo)) //nolint:mnd
return payloadLengthLimit
}

func (machine cartesiMachine) Address() string {
return machine.address
}

// Close closes the cartesi machine. It also shuts down the remote cartesi machine server.
func (machine *cartesiMachine) Close() error {
machine.inner.Delete()
machine.inner = nil
return machine.closeServer()
}

// ------------------------------------------------------------------------------------------------

// closeServer shuts down the server and deletes its reference.
func (machine *cartesiMachine) closeServer() error {
err := machine.server.Shutdown()
if err != nil {
err = fmt.Errorf("could not shut down the server: %w", err)
err = errors.Join(errCartesiMachine(err), errOrphanServerWithAddress(machine.address))
}
machine.server.Delete()
machine.server = nil
return err
}

func (machine *cartesiMachine) readHtifToHostData() (uint64, error) {
tohost, err := machine.inner.ReadHtifToHostData()
if err != nil {
err = fmt.Errorf("could not read HTIF tohost data: %w", err)
return tohost, errCartesiMachine(err)
}
return tohost, nil
}

// ------------------------------------------------------------------------------------------------

func errCartesiMachine(err error) error {
return errors.Join(ErrCartesiMachine, err)
}

func errOrphanServerWithAddress(address string) error {
return fmt.Errorf("%w at address %s", ErrOrphanServer, address)
}

func assert(condition bool, s string) {
if !condition {
panic("assertion error: " + s)
}
}
Loading

0 comments on commit 479e6bd

Please sign in to comment.