Skip to content

Commit a331d89

Browse files
author
Marcos Pernambuco Motta
committed
feat: improve cpp to solidity conversion
1 parent 4727314 commit a331d89

22 files changed

Lines changed: 654 additions & 430 deletions

‎.github/workflows/test.yml‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ jobs:
88
with:
99
submodules: recursive
1010

11-
- name: Install gpp
12-
run: sudo apt-get install -y gpp
11+
- name: Install gpp and lua
12+
run: sudo apt-get install -y gpp lua5.4 liblua5.4-dev luarocks
13+
14+
- name: Install LPEG
15+
run: sudo luarocks --lua-version=5.4 install lpeg
1316

1417
- uses: ./.github/actions/setup
1518

1619
- name: Run all tests
17-
run: make test-all
20+
run: eval "$(luarocks --lua-version=5.4 path)" && make test-all
1821

1922
coverage-mock:
2023
runs-on: ubuntu-24.04

‎Makefile‎

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
EMULATOR_DIR ?= ../emulator
2+
LUA ?= lua5.4
23
TEST_DIR := test
34
DOWNLOADDIR := downloads
45
SRC_DIR := src
@@ -36,18 +37,30 @@ help:
3637
@echo ' generate-prod - generate production library code'
3738
@echo ' generate-replay - generate replay tests'
3839
@echo ' pretest - download necessary files for tests'
40+
@echo ' local-dep - use test data from local emulator build'
3941
@echo ' submodules - initialize and update git submodules'
4042
@echo ' fmt - format solidity sources with forge fmt'
4143
@echo ' test-all - test all'
44+
@echo ' test-transpiler - test the C++ to Solidity transpiler'
4245
@echo ' test-mock - test binary files with mock library'
4346
@echo ' test-prod - test production code'
4447
@echo ' test-replay - test log files'
48+
@echo ' env-check - check that all required tools are installed'
4549
@echo 'Coverage targets:'
4650
@echo ' coverage-mock - generate coverage info for mock library tests'
4751
@echo ' coverage-prod - generate coverage info for production code tests'
4852
@echo ' coverage-report - aggregate coverage info and generate html report'
4953

5054

55+
env-check:
56+
@echo "Checking development environment..."
57+
@$(LUA) -v 2>&1 || { echo "MISSING: lua5.4 -- see README.md"; exit 1; }
58+
@$(LUA) -e 'require("lpeg")' 2>/dev/null || { echo "MISSING: lpeg -- see README.md"; exit 1; }
59+
@forge --version 2>&1 || { echo "MISSING: foundry -- see README.md"; exit 1; }
60+
@gpp --version >/dev/null 2>&1 || { echo "MISSING: gpp -- see README.md"; exit 1; }
61+
@$(SED) --version >/dev/null 2>&1 || { echo "MISSING: GNU sed (set SED=gsed on macOS) -- see README.md"; exit 1; }
62+
@echo "All tools found."
63+
5164
all: build test-all
5265

5366
build: generate-step generate-reset generate-send-cmio-response generate-constants generate-prod
@@ -58,7 +71,10 @@ clean:
5871
rm -rf $(DEPDIRS) $(DOWNLOADDIR)
5972
forge clean
6073

61-
test-all:
74+
test-transpiler:
75+
$(LUA) helper_scripts/test_generate_UArchSolidity.lua
76+
77+
test-all: test-transpiler
6278
$(MAKE) test-mock
6379
$(MAKE) test-replay
6480
$(MAKE) test-prod
@@ -112,14 +128,19 @@ generate-replay:
112128
generate-constants: $(EMULATOR_DIR)
113129
EMULATOR_DIR=$(EMULATOR_DIR) ./helper_scripts/generate_EmulatorConstants.sh
114130

115-
generate-step: $(EMULATOR_DIR)/src/uarch-step.h $(EMULATOR_DIR)/src/uarch-step.cpp
116-
EMULATOR_DIR=$(EMULATOR_DIR) ./helper_scripts/generate_UArchStep.sh
131+
GEN_SOL = $(LUA) helper_scripts/generate_UArchSolidity.lua
132+
133+
generate-step: $(EMULATOR_DIR)/src/uarch-step.hpp $(EMULATOR_DIR)/src/uarch-step.cpp
134+
$(GEN_SOL) $(EMULATOR_DIR)/src/uarch-step.cpp src/UArchStep.sol UArchStep step
135+
forge fmt src/UArchStep.sol
117136

118137
generate-reset: $(EMULATOR_DIR)/src/uarch-reset-state.cpp
119-
EMULATOR_DIR=$(EMULATOR_DIR) ./helper_scripts/generate_UArchReset.sh
138+
$(GEN_SOL) $(EMULATOR_DIR)/src/uarch-reset-state.cpp src/UArchReset.sol UArchReset reset
139+
forge fmt src/UArchReset.sol
120140

121-
generate-send-cmio-response: $(EMULATOR_DIR)/src/uarch-reset-state.cpp
122-
EMULATOR_DIR=$(EMULATOR_DIR) ./helper_scripts/generate_SendCmioResponse.sh
141+
generate-send-cmio-response: $(EMULATOR_DIR)/src/send-cmio-response.cpp
142+
$(GEN_SOL) $(EMULATOR_DIR)/src/send-cmio-response.cpp src/SendCmioResponse.sol SendCmioResponse sendCmioResponse
143+
forge fmt src/SendCmioResponse.sol
123144

124145
fmt:
125146
forge fmt src test
@@ -130,6 +151,16 @@ pretest: dep
130151

131152
dep: $(DEPDIRS)
132153

154+
EMULATOR_TESTS_DIR = $(EMULATOR_DIR)/tests
155+
EMULATOR_UARCH_BIN_DIR = $(EMULATOR_TESTS_DIR)/build/uarch
156+
EMULATOR_UARCH_LOG_DIR = $(EMULATOR_TESTS_DIR)/build/uarch-riscv-tests-json-logs
157+
158+
local-dep: $(EMULATOR_UARCH_BIN_DIR) $(EMULATOR_UARCH_LOG_DIR)
159+
rm -rf $(TESTS_DATA_DIR) $(LOG_TEST_DIR)
160+
mkdir -p $(TESTS_DATA_DIR) $(LOG_TEST_DIR)
161+
cp $(EMULATOR_UARCH_BIN_DIR)/*.bin $(TESTS_DATA_DIR)/
162+
cp $(EMULATOR_UARCH_LOG_DIR)/*.json $(LOG_TEST_DIR)/
163+
133164
$(DOWNLOADDIR):
134165
@mkdir -p $(DOWNLOADDIR)
135166
@wget -nc $(TESTS_DATA_DOWNLOAD_URL) -P $(DOWNLOADDIR)
@@ -154,4 +185,4 @@ $(LOG_TEST_DIR): | download
154185
submodules:
155186
git submodule update --init --recursive
156187

157-
.PHONY: help all build clean checksum-download shasum-download fmt generate-mock generate-prod generate-replay generate-step download pretest dep submodules test-all test-mock test-prod test-replay generate-constants generate-reset generate-send-cmio-response coverage-mock coverage-prod coverage-report
188+
.PHONY: help all build clean checksum-download shasum-download fmt generate-mock generate-prod generate-replay generate-step download pretest dep local-dep submodules test-all test-mock test-prod test-replay test-transpiler env-check generate-constants generate-reset generate-send-cmio-response coverage-mock coverage-prod coverage-report

‎README.md‎

Lines changed: 113 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,152 @@
11
# Cartesi RISC-V Solidity Emulator
22

3-
The Cartesi RISC-V Solidity Emulator is the on-chain host implementation of the Cartesi Machine Specification. The libraries and contracts are written in Solidity, and the testing scripts are written in Solidity (with the help of [Foundry](https://github.com/foundry-rs/foundry)). **_Do not recommend running `forge` command directly, as there are many scripts and templates working together. Please use `make` instead._**
3+
On-chain Solidity implementation of the [Cartesi Machine](https://github.com/cartesi/machine-emulator) state transition function. The core libraries (`UArchStep`, `UArchReset`, `SendCmioResponse`) are transpiled directly from the emulator's C++ sources to guarantee bit-exact equivalence.
44

5-
For Cartesi's design to work, this implementation must have the exact transition function as the off-chain [Cartesi RISC-V Emulator](https://github.com/cartesi/machine-emulator), meaning that if given the same initial state (s[i]) both implementation's step functions should reach a bit by bit consistent state s[i + 1].
5+
> Use `make` for all build and test operations -- code generation must run before compilation.
66
7-
Since the cost of storing a full Cartesi Machine state within the blockchain is prohibitive, all machine states are represented in the blockchain as cryptographic hashes. The contents of those states and memory represented by those hashes are only known off-chain.
7+
## Quick start
88

9-
Cartesi uses Merkle tree operations and properties to ensure that the blockchain has the ability to correctly verify a state transition without having full state-access. However, the RISC-V Solidity emulator abstracts these operations away and acts as if it knows the full contents of a machine state - it uses the Memory Manager interface to fetch or write any necessary words to memory.
9+
Set up your environment, then build and test:
1010

11-
## AccessLogs
11+
export EMULATOR_DIR=../machine-emulator # path to machine-emulator checkout
12+
export SED=gsed # macOS only (brew install gnu-sed)
1213

13-
The `AccessLogs.Context` struct is consumed by the RISC-V Solidity emulator as if the entire state content was available - since the off and on-chain emulators match down to the order in which accesses are logged. When a dispute arises, Alice packs her off-chain state access log referent to the disagreement step in an struct `AccessLogs.Context`, which will guide the execution of a `step` (i.e state transition function).
14-
15-
The `AccessLogs` library implements the RISC-V Solidity emulator all necessary read and write operations.
14+
make submodules
15+
make build
16+
make test-all
1617

17-
It also makes sure that all accesses performed by the `step` function match the ones provided by Alice and are consistent with the Merkle proofs provided by her. If that is not the case, Alice loses the dispute.
18+
## Prerequisites
1819

19-
## Step function
20+
make env-check # verify everything is installed
2021

21-
`MetaStep.step` is the top-level state transition function, it is meant to take the machine from state s[i] to state[i + 1], using the `AccessLogs` as an assistant. It receives a `counter` and an `AccessLogs.Context` - the latter populated with the access log generated by the emulator off-chain - and returns a `UArchStepStatus` signaling the result of its execution. Internally it runs one micro-step via `UArchStep.step` and, every `1 << LOG2_CYCLES_TO_RESET` cycles, also runs `UArchReset.reset` to reset the micro-architecture state.
22+
- [Foundry](https://book.getfoundry.sh/) 1.4.3
23+
- GNU Make >= 3.81
24+
- Lua 5.4, LuaRocks, LPEG
25+
- GNU sed (gsed on macOS)
26+
- GPP >= 2.27
2227

23-
During a `MetaStep.step` execution, every necessary read or write (be it to memory, registers etc) is processed and verified by the `AccessLogs`.
28+
<details>
29+
<summary>macOS (Homebrew)</summary>
2430

25-
## Execute Instruction
31+
brew install lua luarocks gpp gnu-sed
32+
luarocks install lpeg
33+
curl -L https://foundry.paradigm.xyz | bash
34+
foundryup -i v1.4.3
2635

27-
The `UArchStep` library implements a single micro-architecture instruction step (`UArchStep.step`), and is invoked by `MetaStep.step`. It consists of the machine instruction logic, such as decoding, executing, opcode matching and etc. The Solidity implementation is converted from the Cpp implementation directly through a translator script. This is to assure that the implementations in two languages are identical. Yet the low level differences in two languages are wrapped in the Compatibility Layer.
36+
Add to your shell profile:
2837

29-
## Compatibility Layer
38+
eval "$(luarocks path)"
3039

31-
The `EmulatorCompat` contract is taking care of all the differences in the two implementations. Ranging from programming languages (Cpp versus Solidity) to architectural differences (RISC-V versus EVM).
40+
</details>
3241

33-
## Getting Started
42+
<details>
43+
<summary>macOS (MacPorts)</summary>
3444

35-
Run `make help` for a list of target options. Here are some of them:
45+
sudo port install lua54 luarocks gpp gsed
46+
luarocks install lpeg
47+
curl -L https://foundry.paradigm.xyz | bash
48+
foundryup -i v1.4.3
3649

37-
```
38-
Cleaning targets:
39-
clean - clean the cache artifacts and generated files
40-
Generic targets:
41-
* all - build solidity code. To build from a clean clone, run: make submodules all
42-
build - build solidity code
43-
generate-step - generate solidity-step code from cpp
44-
generate-reset - generate solidity-reset code from cpp
45-
generate-send-cmio-response - generate solidity-send-cmio-response code from cpp
46-
generate-constants - generate solidity-constants code by querying the cartesi machine
47-
generate-mock - generate mock library code
48-
generate-prod - generate production library code
49-
generate-replay - generate replay tests
50-
pretest - download necessary files for tests
51-
submodules - initialize and update git submodules
52-
fmt - format solidity sources with forge fmt
53-
test-all - test all
54-
test-mock - test binary files with mock library
55-
test-prod - test production code
56-
test-replay - test log files
57-
Coverage targets:
58-
coverage-mock - generate coverage info for mock library tests
59-
coverage-prod - generate coverage info for production code tests
60-
coverage-report - aggregate coverage info and generate html report
61-
```
50+
Add to your shell profile:
6251

63-
### Prerequisite
52+
eval "$(luarocks path)"
53+
export SED=gsed
6454

65-
- Build [Cartesi docker image](https://github.com/cartesi/machine-emulator#getting-started)
55+
</details>
6656

67-
### Requirements
57+
<details>
58+
<summary>Linux (Ubuntu/Debian)</summary>
6859

69-
- Foundry 1.4.3
70-
- GNU Make >= 3.81
71-
- GNU sed >= 4.9
72-
- GPP >= 2.27
60+
sudo apt install lua5.4 liblua5.4-dev luarocks gpp
61+
sudo luarocks install lpeg
62+
curl -L https://foundry.paradigm.xyz | bash
63+
foundryup -i v1.4.3
7364

74-
Different version of tools maybe working but is not guaranteed.
65+
Add to your shell profile:
7566

76-
### Install
67+
eval "$(luarocks path)"
7768

78-
Install dependencies and build:
69+
</details>
7970

80-
make submodules all
71+
## Testing
8172

82-
### Run tests
73+
make test-transpiler # transpiler unit tests (fast)
74+
make test-mock # rv64i instruction tests against mock AccessLogs
75+
make test-prod # production AccessLogs integration tests
76+
make test-replay # replay 56 emulator step logs with full Merkle verification (~1 min)
77+
make test-all # all of the above
8378

84-
There are two types of tests that can be run on the Solidity Emulator.
79+
### Using local emulator test data
8580

86-
1. Load binary rv64i test programs in Solidity and verify the execution result
87-
2. Collect step logs and proofs of a test program and replay them in Solidity, verify all accesses and proofs.
81+
By default, test data (binaries and JSON step logs) is downloaded from a
82+
GitHub release. When developing against a local emulator build, generate and
83+
use local test data instead:
8884

89-
Run all tests:
85+
# one-time: generate step logs in the emulator repo
86+
make -C $EMULATOR_DIR/tests test-generate-uarch-logs
9087

88+
# use them here
89+
make local-dep
9190
make test-all
9291

93-
(**_target test-replay is very time consuming_**)
92+
This ensures the constants and test data come from the same emulator build.
9493

95-
## Contributing
94+
### Coverage
95+
96+
make coverage-mock
97+
make coverage-prod
98+
make coverage-report # html report in coverage/
99+
100+
## Code generation
101+
102+
The build step runs these automatically; you only need them when
103+
iterating on a specific piece:
104+
105+
make generate-step # uarch-step.cpp -> UArchStep.sol
106+
make generate-reset # uarch-reset-state.cpp -> UArchReset.sol
107+
make generate-send-cmio-response # send-cmio-response.cpp -> SendCmioResponse.sol
108+
make generate-constants # EmulatorConstants.sol (from emulator Lua bindings)
109+
make generate-mock # mock AccessLogs library
110+
make generate-prod # production AccessLogs library
111+
112+
## Architecture
113+
114+
This implementation must produce the exact same state transition as the
115+
off-chain emulator: given initial state s[i], both must reach s[i+1] bit for
116+
bit. Machine states live on-chain as Merkle tree root hashes; their contents
117+
are only known off-chain. Merkle proofs let the blockchain verify individual
118+
transitions without storing the full state.
96119

97-
Thank you for your interest in Cartesi! Head over to our [Contributing Guidelines](CONTRIBUTING.md) for instructions on how to sign our Contributors Agreement and get started with Cartesi!
120+
### Components
121+
122+
**MetaStep** -- Entry point. Runs one micro-step via `UArchStep.step` and
123+
periodically resets the micro-architecture via `UArchReset.reset`.
124+
125+
**UArchStep** -- One micro-architecture instruction step (transpiled from
126+
`uarch-step.cpp`). Decodes and executes RISC-V opcodes.
127+
128+
**UArchReset** -- Resets micro-architecture state (transpiled from
129+
`uarch-reset-state.cpp`).
130+
131+
**SendCmioResponse** -- Handles CMIO yield responses (transpiled from
132+
`send-cmio-response.cpp`).
133+
134+
**AccessLogs** -- Provides Merkle-verified read/write access to machine state.
135+
During a dispute, the claimant supplies an access log with sibling hashes;
136+
every read and write is checked against the Merkle root. Inconsistencies mean
137+
the claimant loses.
138+
139+
**EmulatorCompat** -- Bridges C++/Solidity differences (endianness, type
140+
widths, calling conventions).
141+
142+
**EmulatorConstants** -- Addresses, sizes, and hashes auto-generated from the
143+
emulator's Lua bindings.
144+
145+
## Contributing
98146

99-
Please note we have a [Code of Conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project.
147+
See [Contributing Guidelines](CONTRIBUTING.md) and our
148+
[Code of Conduct](CODE_OF_CONDUCT.md).
100149

101150
## License
102151

103-
The machine-solidity-step repository and all contributions are licensed under
104-
[APACHE 2.0](https://www.apache.org/licenses/LICENSE-2.0). Please review our [LICENSE](LICENSE) file.
152+
[Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). See [LICENSE](LICENSE).

‎helper_scripts/generate_SendCmioResponse.sh‎

Lines changed: 0 additions & 54 deletions
This file was deleted.

0 commit comments

Comments
 (0)