@riseandshaheen/libcma 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +348 -0
- package/LICENSE +21 -0
- package/README.md +157 -0
- package/binding.gyp +52 -0
- package/deps/machine-asset-tools/LICENSE +202 -0
- package/deps/machine-asset-tools/README.md +237 -0
- package/deps/machine-asset-tools/include/libcma/ledger.h +161 -0
- package/deps/machine-asset-tools/include/libcma/parser.h +305 -0
- package/deps/machine-asset-tools/include/libcma/types.h +22 -0
- package/deps/machine-guest-tools/LICENSE +202 -0
- package/deps/machine-guest-tools/README.md +57 -0
- package/deps/machine-guest-tools/sys-utils/libcmt/README.md +130 -0
- package/deps/machine-guest-tools/sys-utils/libcmt/include/libcmt/abi.h +578 -0
- package/deps/machine-guest-tools/sys-utils/libcmt/include/libcmt/buf.h +82 -0
- package/deps/machine-guest-tools/sys-utils/libcmt/include/libcmt/io.h +168 -0
- package/deps/machine-guest-tools/sys-utils/libcmt/include/libcmt/keccak.h +131 -0
- package/deps/machine-guest-tools/sys-utils/libcmt/include/libcmt/merkle.h +118 -0
- package/deps/machine-guest-tools/sys-utils/libcmt/include/libcmt/rollup.h +260 -0
- package/deps/machine-guest-tools/sys-utils/libcmt/include/libcmt/util.h +34 -0
- package/lib/index.cjs +445 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +156 -0
- package/lib/index.d.ts +156 -0
- package/lib/index.js +428 -0
- package/lib/index.js.map +1 -0
- package/native/addon.cc +268 -0
- package/native/ledger_backend.h +28 -0
- package/native/mock/ledger_mock.cc +185 -0
- package/native/real/ledger_real.cc +271 -0
- package/package.json +74 -0
- package/scripts/build-libcma-riscv64.sh +80 -0
- package/scripts/build-native-riscv64.sh +40 -0
- package/scripts/install-native.mjs +94 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// Real libcma backend (riscv64 / LIBCMA_USE_REAL).
|
|
2
|
+
// Uses cma_ledger_init_single_* with AssetType::BASE (Ether) — matches contracts v3
|
|
3
|
+
// emergency-withdrawal accounts-drive layout (32-byte single-asset records).
|
|
4
|
+
|
|
5
|
+
#include "ledger_backend.h"
|
|
6
|
+
|
|
7
|
+
#include <cstdlib>
|
|
8
|
+
#include <cstring>
|
|
9
|
+
#include <new>
|
|
10
|
+
#include <string>
|
|
11
|
+
|
|
12
|
+
extern "C" {
|
|
13
|
+
#include <libcma/ledger.h>
|
|
14
|
+
#include <libcma/types.h>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
namespace {
|
|
18
|
+
|
|
19
|
+
thread_local std::string g_last_error;
|
|
20
|
+
|
|
21
|
+
void set_err(const char *msg) {
|
|
22
|
+
g_last_error = msg ? msg : "";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
void set_err_from_libcma() {
|
|
26
|
+
const char *msg = cma_ledger_get_last_error_message();
|
|
27
|
+
g_last_error = msg ? msg : "libcma error";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
bool amount_is_zero(const uint8_t amount_be[32]) {
|
|
31
|
+
for (int i = 0; i < 32; i++) {
|
|
32
|
+
if (amount_be[i] != 0) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void copy_amount(cma_amount_t *dst, const uint8_t amount_be[32]) {
|
|
40
|
+
std::memcpy(dst->data, amount_be, 32);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
void copy_amount_out(uint8_t out[32], const cma_amount_t *src) {
|
|
44
|
+
std::memcpy(out, src->data, 32);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
int resolve_account(cma_ledger_t *ledger, const uint8_t account[20], cma_ledger_account_id_t *out_id,
|
|
48
|
+
cma_ledger_retrieve_operation_t op) {
|
|
49
|
+
cma_ledger_account_id_t account_id = 0;
|
|
50
|
+
cma_ledger_account_t account_obj{};
|
|
51
|
+
cma_ledger_account_type_t account_type = CMA_LEDGER_ACCOUNT_TYPE_WALLET_ADDRESS;
|
|
52
|
+
int rc = cma_ledger_retrieve_account(ledger, &account_id, &account_obj, account, nullptr, &account_type, op);
|
|
53
|
+
if (rc < 0) {
|
|
54
|
+
set_err_from_libcma();
|
|
55
|
+
return rc;
|
|
56
|
+
}
|
|
57
|
+
*out_id = account_id;
|
|
58
|
+
return CMA_LEDGER_SUCCESS;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
} // namespace
|
|
62
|
+
|
|
63
|
+
struct CmaNodeLedger {
|
|
64
|
+
cma_ledger_t ledger{};
|
|
65
|
+
void *owned_buffer = nullptr;
|
|
66
|
+
size_t owned_length = 0;
|
|
67
|
+
bool open = false;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
extern "C" {
|
|
71
|
+
|
|
72
|
+
CmaNodeLedger *cma_node_open_buffer(size_t max_accounts) {
|
|
73
|
+
// Single-asset estimate grows with account capacity; 4 MiB covers the default
|
|
74
|
+
// log2_max_num_of_accounts=17 drive and is safe for smaller capacities.
|
|
75
|
+
constexpr size_t kDefaultMem = 4u * 1024u * 1024u;
|
|
76
|
+
size_t mem_length = kDefaultMem;
|
|
77
|
+
if (max_accounts <= 256) {
|
|
78
|
+
mem_length = CMA_LEDGER_MIN_MEM_LENGTH;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
void *buffer = std::calloc(1, mem_length);
|
|
82
|
+
if (!buffer) {
|
|
83
|
+
set_err("out of memory");
|
|
84
|
+
return nullptr;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
auto *node = new (std::nothrow) CmaNodeLedger();
|
|
88
|
+
if (!node) {
|
|
89
|
+
std::free(buffer);
|
|
90
|
+
set_err("out of memory");
|
|
91
|
+
return nullptr;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
int rc = cma_ledger_init_single_buffer(&node->ledger, buffer, mem_length, max_accounts,
|
|
95
|
+
CMA_LEDGER_ASSET_TYPE_BASE, nullptr);
|
|
96
|
+
if (rc < 0) {
|
|
97
|
+
set_err_from_libcma();
|
|
98
|
+
std::free(buffer);
|
|
99
|
+
delete node;
|
|
100
|
+
return nullptr;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
node->owned_buffer = buffer;
|
|
104
|
+
node->owned_length = mem_length;
|
|
105
|
+
node->open = true;
|
|
106
|
+
g_last_error.clear();
|
|
107
|
+
return node;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
CmaNodeLedger *cma_node_open_file(const char *path, size_t offset, size_t memory_length, size_t max_accounts) {
|
|
111
|
+
if (!path || memory_length == 0 || max_accounts == 0) {
|
|
112
|
+
set_err("invalid open_file arguments");
|
|
113
|
+
return nullptr;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
auto *node = new (std::nothrow) CmaNodeLedger();
|
|
117
|
+
if (!node) {
|
|
118
|
+
set_err("out of memory");
|
|
119
|
+
return nullptr;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
int rc = cma_ledger_init_single_file(&node->ledger, path, offset, memory_length, max_accounts,
|
|
123
|
+
CMA_LEDGER_ASSET_TYPE_BASE, nullptr);
|
|
124
|
+
if (rc < 0) {
|
|
125
|
+
set_err_from_libcma();
|
|
126
|
+
delete node;
|
|
127
|
+
return nullptr;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
node->open = true;
|
|
131
|
+
g_last_error.clear();
|
|
132
|
+
return node;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
void cma_node_close(CmaNodeLedger *ledger) {
|
|
136
|
+
if (!ledger) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (ledger->open) {
|
|
140
|
+
(void) cma_ledger_fini(&ledger->ledger);
|
|
141
|
+
ledger->open = false;
|
|
142
|
+
}
|
|
143
|
+
if (ledger->owned_buffer) {
|
|
144
|
+
std::free(ledger->owned_buffer);
|
|
145
|
+
ledger->owned_buffer = nullptr;
|
|
146
|
+
}
|
|
147
|
+
delete ledger;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
int cma_node_deposit_ether(CmaNodeLedger *ledger, const uint8_t account[20], const uint8_t amount_be[32]) {
|
|
151
|
+
if (!ledger || !ledger->open) {
|
|
152
|
+
set_err("ledger closed");
|
|
153
|
+
return CMA_LEDGER_ERROR_UNKNOWN;
|
|
154
|
+
}
|
|
155
|
+
if (amount_is_zero(amount_be)) {
|
|
156
|
+
return CMA_LEDGER_SUCCESS;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
cma_ledger_account_id_t account_id = 0;
|
|
160
|
+
int rc = resolve_account(&ledger->ledger, account, &account_id, CMA_LEDGER_OP_FIND_OR_CREATE);
|
|
161
|
+
if (rc < 0) {
|
|
162
|
+
return rc;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
cma_amount_t amount{};
|
|
166
|
+
copy_amount(&amount, amount_be);
|
|
167
|
+
// Single-asset Ether ledger: fixed asset id 0.
|
|
168
|
+
rc = cma_ledger_deposit(&ledger->ledger, 0, account_id, &amount);
|
|
169
|
+
if (rc < 0) {
|
|
170
|
+
set_err_from_libcma();
|
|
171
|
+
return rc;
|
|
172
|
+
}
|
|
173
|
+
return CMA_LEDGER_SUCCESS;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
int cma_node_transfer_ether(CmaNodeLedger *ledger, const uint8_t from[20], const uint8_t to[20],
|
|
177
|
+
const uint8_t amount_be[32]) {
|
|
178
|
+
if (!ledger || !ledger->open) {
|
|
179
|
+
set_err("ledger closed");
|
|
180
|
+
return CMA_LEDGER_ERROR_UNKNOWN;
|
|
181
|
+
}
|
|
182
|
+
if (amount_is_zero(amount_be)) {
|
|
183
|
+
return CMA_LEDGER_SUCCESS;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
cma_ledger_account_id_t from_id = 0;
|
|
187
|
+
cma_ledger_account_id_t to_id = 0;
|
|
188
|
+
int rc = resolve_account(&ledger->ledger, from, &from_id, CMA_LEDGER_OP_FIND);
|
|
189
|
+
if (rc < 0) {
|
|
190
|
+
return rc;
|
|
191
|
+
}
|
|
192
|
+
rc = resolve_account(&ledger->ledger, to, &to_id, CMA_LEDGER_OP_FIND_OR_CREATE);
|
|
193
|
+
if (rc < 0) {
|
|
194
|
+
return rc;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
cma_amount_t amount{};
|
|
198
|
+
copy_amount(&amount, amount_be);
|
|
199
|
+
rc = cma_ledger_transfer(&ledger->ledger, 0, from_id, to_id, &amount);
|
|
200
|
+
if (rc < 0) {
|
|
201
|
+
set_err_from_libcma();
|
|
202
|
+
return rc;
|
|
203
|
+
}
|
|
204
|
+
return CMA_LEDGER_SUCCESS;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
int cma_node_withdraw_ether(CmaNodeLedger *ledger, const uint8_t account[20], const uint8_t amount_be[32]) {
|
|
208
|
+
if (!ledger || !ledger->open) {
|
|
209
|
+
set_err("ledger closed");
|
|
210
|
+
return CMA_LEDGER_ERROR_UNKNOWN;
|
|
211
|
+
}
|
|
212
|
+
if (amount_is_zero(amount_be)) {
|
|
213
|
+
return CMA_LEDGER_SUCCESS;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
cma_ledger_account_id_t account_id = 0;
|
|
217
|
+
int rc = resolve_account(&ledger->ledger, account, &account_id, CMA_LEDGER_OP_FIND);
|
|
218
|
+
if (rc < 0) {
|
|
219
|
+
return rc;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
cma_amount_t amount{};
|
|
223
|
+
copy_amount(&amount, amount_be);
|
|
224
|
+
rc = cma_ledger_withdraw(&ledger->ledger, 0, account_id, &amount);
|
|
225
|
+
if (rc < 0) {
|
|
226
|
+
set_err_from_libcma();
|
|
227
|
+
return rc;
|
|
228
|
+
}
|
|
229
|
+
return CMA_LEDGER_SUCCESS;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
int cma_node_get_ether_balance(CmaNodeLedger *ledger, const uint8_t account[20], uint8_t out_amount_be[32]) {
|
|
233
|
+
if (!ledger || !ledger->open) {
|
|
234
|
+
set_err("ledger closed");
|
|
235
|
+
return CMA_LEDGER_ERROR_UNKNOWN;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
cma_ledger_account_id_t account_id = 0;
|
|
239
|
+
int rc = resolve_account(&ledger->ledger, account, &account_id, CMA_LEDGER_OP_FIND);
|
|
240
|
+
if (rc < 0) {
|
|
241
|
+
// Missing account → zero balance (matches memory backend semantics).
|
|
242
|
+
if (rc == CMA_LEDGER_ERROR_ACCOUNT_NOT_FOUND) {
|
|
243
|
+
std::memset(out_amount_be, 0, 32);
|
|
244
|
+
return CMA_LEDGER_SUCCESS;
|
|
245
|
+
}
|
|
246
|
+
return rc;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
cma_amount_t balance{};
|
|
250
|
+
rc = cma_ledger_get_balance(&ledger->ledger, 0, account_id, &balance, nullptr);
|
|
251
|
+
if (rc < 0) {
|
|
252
|
+
if (rc == CMA_LEDGER_ERROR_BALANCE_NOT_FOUND) {
|
|
253
|
+
std::memset(out_amount_be, 0, 32);
|
|
254
|
+
return CMA_LEDGER_SUCCESS;
|
|
255
|
+
}
|
|
256
|
+
set_err_from_libcma();
|
|
257
|
+
return rc;
|
|
258
|
+
}
|
|
259
|
+
copy_amount_out(out_amount_be, &balance);
|
|
260
|
+
return CMA_LEDGER_SUCCESS;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const char *cma_node_last_error(void) {
|
|
264
|
+
return g_last_error.c_str();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const char *cma_node_kind(void) {
|
|
268
|
+
return "native-libcma";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
} // extern "C"
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@riseandshaheen/libcma",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Node.js / TypeScript bindings for libcma — Cartesi proveable asset ledger",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "riseandshaheen",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/riseandshaheen/libcma-binding-node.git"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./lib/index.cjs",
|
|
13
|
+
"module": "./lib/index.js",
|
|
14
|
+
"types": "./lib/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": {
|
|
18
|
+
"types": "./lib/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"require": {
|
|
22
|
+
"types": "./lib/index.d.cts",
|
|
23
|
+
"default": "./lib/index.cjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"LICENSE",
|
|
30
|
+
"README.md",
|
|
31
|
+
"ARCHITECTURE.md",
|
|
32
|
+
"binding.gyp",
|
|
33
|
+
"lib/",
|
|
34
|
+
"native/",
|
|
35
|
+
"scripts/",
|
|
36
|
+
"prebuilds/",
|
|
37
|
+
"deps/machine-asset-tools/include/",
|
|
38
|
+
"deps/machine-guest-tools/sys-utils/libcmt/include/"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"clean": "rm -rf lib build",
|
|
42
|
+
"build:ts": "tsup",
|
|
43
|
+
"build:native": "node-gyp rebuild",
|
|
44
|
+
"build:libcma:riscv64": "bash scripts/build-libcma-riscv64.sh",
|
|
45
|
+
"build:native:riscv64": "bash scripts/build-native-riscv64.sh",
|
|
46
|
+
"build": "npm run build:ts",
|
|
47
|
+
"install": "node scripts/install-native.mjs",
|
|
48
|
+
"test": "npm run build:ts && node --test test/*.test.js",
|
|
49
|
+
"typecheck": "tsc --noEmit"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=20"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"node-addon-api": "^8.3.0",
|
|
56
|
+
"node-gyp-build": "^4.8.4"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/node": "^22.15.0",
|
|
60
|
+
"node-gyp": "^11.2.0",
|
|
61
|
+
"tsup": "^8.4.0",
|
|
62
|
+
"typescript": "^5.8.3"
|
|
63
|
+
},
|
|
64
|
+
"binary": {
|
|
65
|
+
"napi_versions": [8]
|
|
66
|
+
},
|
|
67
|
+
"keywords": [
|
|
68
|
+
"cartesi",
|
|
69
|
+
"rollups",
|
|
70
|
+
"libcma",
|
|
71
|
+
"ledger",
|
|
72
|
+
"emergency-withdrawal"
|
|
73
|
+
]
|
|
74
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Cross-build (or native-build on riscv64) libcma.a into
|
|
3
|
+
# deps/machine-asset-tools/build/riscv64/libcma.a
|
|
4
|
+
#
|
|
5
|
+
# Mirrors libcma_binding_rust/build.rs. Requires:
|
|
6
|
+
# - GNU make, wget, network
|
|
7
|
+
# - On non-riscv64 hosts: g++-14-riscv64-linux-gnu / gcc-14-riscv64-linux-gnu
|
|
8
|
+
# (override with CMA_RISCV64_CXX / CMA_RISCV64_CC)
|
|
9
|
+
# - Or run inside a riscv64 container / Cartesi SDK image (native gcc)
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
14
|
+
MAT="${ROOT}/deps/machine-asset-tools"
|
|
15
|
+
OUT="${MAT}/build/riscv64/libcma.a"
|
|
16
|
+
|
|
17
|
+
if [[ ! -d "${MAT}/include" ]]; then
|
|
18
|
+
echo "error: ${MAT}/include missing — clone machine-asset-tools into deps/" >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if [[ -f "${OUT}" && "${LIBCMA_FORCE_REBUILD:-}" != "1" ]]; then
|
|
23
|
+
echo "[libcma] ${OUT} already exists (set LIBCMA_FORCE_REBUILD=1 to rebuild)"
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
ARCH="$(uname -m)"
|
|
28
|
+
NLOHMANN="${MAT}/third-party/nlohmann/json.hpp"
|
|
29
|
+
mkdir -p "${MAT}/third-party/nlohmann"
|
|
30
|
+
|
|
31
|
+
if [[ ! -f "${NLOHMANN}" ]]; then
|
|
32
|
+
echo "[libcma] fetching nlohmann/json.hpp"
|
|
33
|
+
wget -qO "${NLOHMANN}" \
|
|
34
|
+
https://github.com/nlohmann/json/releases/download/v3.12.0/json.hpp
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
cd "${MAT}"
|
|
38
|
+
|
|
39
|
+
if [[ "${ARCH}" == "riscv64" ]]; then
|
|
40
|
+
echo "[libcma] native riscv64 build"
|
|
41
|
+
# Makefile uses `CXX := g++` (overrides env); pass on the command line.
|
|
42
|
+
# Prefer g++-14 when present (-fhardened needs GCC ≥ 14).
|
|
43
|
+
if [[ -z "${CXX:-}" ]]; then
|
|
44
|
+
if command -v g++-14 >/dev/null 2>&1; then
|
|
45
|
+
CXX=g++-14
|
|
46
|
+
else
|
|
47
|
+
CXX=g++
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
if [[ -z "${CC:-}" ]]; then
|
|
51
|
+
if command -v gcc-14 >/dev/null 2>&1; then
|
|
52
|
+
CC=gcc-14
|
|
53
|
+
else
|
|
54
|
+
CC=gcc
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
echo "[libcma] using CXX=${CXX} CC=${CC}"
|
|
58
|
+
make third-party "CXX=${CXX}" "CC=${CC}"
|
|
59
|
+
make build/riscv64/libcma.a "CXX=${CXX}" "CC=${CC}" "AR=${AR:-ar}"
|
|
60
|
+
else
|
|
61
|
+
CXX="${CMA_RISCV64_CXX:-riscv64-linux-gnu-g++-14}"
|
|
62
|
+
CC="${CMA_RISCV64_CC:-riscv64-linux-gnu-gcc-14}"
|
|
63
|
+
if ! command -v "${CXX}" >/dev/null 2>&1; then
|
|
64
|
+
echo "error: ${CXX} not found." >&2
|
|
65
|
+
echo "Install the RISC-V GCC 14 cross toolchain, or run this script on riscv64 / in Docker." >&2
|
|
66
|
+
echo "Example (Debian/Ubuntu): apt-get install g++-14-riscv64-linux-gnu" >&2
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
echo "[libcma] cross-building with ${CXX}"
|
|
70
|
+
make third-party TOOLCHAIN_PREFIX=riscv64-linux-gnu-
|
|
71
|
+
make build/riscv64/libcma.a \
|
|
72
|
+
TOOLCHAIN_PREFIX=riscv64-linux-gnu- \
|
|
73
|
+
"CXX=${CXX}" \
|
|
74
|
+
"CC=${CC}" \
|
|
75
|
+
AR=riscv64-linux-gnu-ar
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
test -f "${OUT}"
|
|
79
|
+
echo "[libcma] wrote ${OUT}"
|
|
80
|
+
ls -lh "${OUT}"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Build the N-API addon for linux-riscv64 against real libcma.a.
|
|
3
|
+
# Intended to run inside a riscv64 Node image (or qemu-user), e.g.:
|
|
4
|
+
#
|
|
5
|
+
# docker run --rm --platform linux/riscv64 -v "$PWD":/work -w /work \
|
|
6
|
+
# cartesi/node:22.14.0-noble \
|
|
7
|
+
# bash -lc 'apt-get update && apt-get install -y build-essential python3 make g++ wget &&
|
|
8
|
+
# npm ci && npm run build:libcma:riscv64 && npm run build:native'
|
|
9
|
+
#
|
|
10
|
+
# Produces: build/Release/cma_napi.node with kind === "native-libcma"
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
15
|
+
cd "${ROOT}"
|
|
16
|
+
|
|
17
|
+
if [[ "$(uname -m)" != "riscv64" && "${LIBCMA_FORCE_REAL:-}" != "1" ]]; then
|
|
18
|
+
echo "warning: host is $(uname -m); set LIBCMA_FORCE_REAL=1 only if libcma.a matches this arch." >&2
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
bash "${ROOT}/scripts/build-libcma-riscv64.sh"
|
|
22
|
+
|
|
23
|
+
export LIBCMA_FORCE_REAL=1
|
|
24
|
+
export LIBCMA_LIB_DIR="${LIBCMA_LIB_DIR:-${ROOT}/deps/machine-asset-tools/build/riscv64}"
|
|
25
|
+
|
|
26
|
+
npm run build:native
|
|
27
|
+
|
|
28
|
+
node -e "
|
|
29
|
+
const { createRequire } = require('module');
|
|
30
|
+
const requireFromRoot = createRequire(require('path').join('${ROOT}', 'package.json'));
|
|
31
|
+
const binding = requireFromRoot('node-gyp-build')('${ROOT}');
|
|
32
|
+
const l = binding.openEtherBuffer(8);
|
|
33
|
+
console.log('kind=', l.kind);
|
|
34
|
+
if (l.kind !== 'native-libcma') {
|
|
35
|
+
console.error('expected native-libcma, got', l.kind);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
l.close();
|
|
39
|
+
console.log('ok: real libcma backend loaded');
|
|
40
|
+
"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native install:
|
|
3
|
+
* - Prefer prebuild / existing .node
|
|
4
|
+
* - On riscv64 (or LIBCMA_FORCE_REAL=1): ensure libcma.a then rebuild against real libcma
|
|
5
|
+
* - Else: host mock addon (best-effort; memory backend always works)
|
|
6
|
+
*/
|
|
7
|
+
import { spawnSync } from "node:child_process";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import { dirname, join } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { arch } from "node:os";
|
|
13
|
+
|
|
14
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
|
|
17
|
+
if (process.env.LIBCMA_SKIP_NATIVE === "1") {
|
|
18
|
+
console.log("[libcma] LIBCMA_SKIP_NATIVE=1 — skipping native build");
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const useReal =
|
|
23
|
+
process.env.LIBCMA_FORCE_REAL === "1" || arch() === "riscv64";
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const binding = require("node-gyp-build")(root);
|
|
27
|
+
// If we need real libcma but an old mock prebuild loaded, rebuild.
|
|
28
|
+
if (useReal) {
|
|
29
|
+
const probe = binding.openEtherBuffer?.(4);
|
|
30
|
+
const kind = probe?.kind;
|
|
31
|
+
probe?.close?.();
|
|
32
|
+
if (kind === "native-libcma") {
|
|
33
|
+
console.log("[libcma] native-libcma addon ready");
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
console.log(`[libcma] found addon kind=${kind}; rebuilding for real libcma`);
|
|
37
|
+
} else {
|
|
38
|
+
console.log("[libcma] native addon loaded (prebuild or previous build)");
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
// Fall through to compile.
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!existsSync(join(root, "binding.gyp"))) {
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (useReal) {
|
|
50
|
+
const libPath = join(
|
|
51
|
+
root,
|
|
52
|
+
process.env.LIBCMA_LIB_DIR || "deps/machine-asset-tools/build/riscv64",
|
|
53
|
+
"libcma.a",
|
|
54
|
+
);
|
|
55
|
+
if (!existsSync(libPath)) {
|
|
56
|
+
console.log("[libcma] building libcma.a for riscv64…");
|
|
57
|
+
const built = spawnSync("bash", ["scripts/build-libcma-riscv64.sh"], {
|
|
58
|
+
cwd: root,
|
|
59
|
+
stdio: "inherit",
|
|
60
|
+
});
|
|
61
|
+
if (built.status !== 0) {
|
|
62
|
+
console.warn(
|
|
63
|
+
"[libcma] failed to build libcma.a — install cross toolchain or run on riscv64.",
|
|
64
|
+
);
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const env = { ...process.env };
|
|
71
|
+
if (useReal) {
|
|
72
|
+
env.LIBCMA_FORCE_REAL = "1";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const result = spawnSync(
|
|
76
|
+
process.platform === "win32" ? "node-gyp.cmd" : "node-gyp",
|
|
77
|
+
["rebuild"],
|
|
78
|
+
{
|
|
79
|
+
cwd: root,
|
|
80
|
+
stdio: "inherit",
|
|
81
|
+
shell: process.platform === "win32",
|
|
82
|
+
env,
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
if (result.status !== 0) {
|
|
87
|
+
console.warn(
|
|
88
|
+
"[libcma] native build failed — TypeScript memory backend remains available for host use.",
|
|
89
|
+
);
|
|
90
|
+
console.warn(
|
|
91
|
+
"[libcma] Set LIBCMA_SKIP_NATIVE=1 to silence this, or install build tools and retry `npm run build:native`.",
|
|
92
|
+
);
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|