@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
package/native/addon.cc
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
// N-API bindings for libcma Ether MVP.
|
|
2
|
+
// Host: native/mock (behavioral).
|
|
3
|
+
// riscv64: native/real linked against libcma.a (proof-identical).
|
|
4
|
+
|
|
5
|
+
#include <cstdio>
|
|
6
|
+
#include <cstring>
|
|
7
|
+
#include <string>
|
|
8
|
+
|
|
9
|
+
#include <napi.h>
|
|
10
|
+
|
|
11
|
+
#include "ledger_backend.h"
|
|
12
|
+
|
|
13
|
+
namespace {
|
|
14
|
+
|
|
15
|
+
Napi::Error ledger_error(Napi::Env env, int rc, const char *what) {
|
|
16
|
+
const char *detail = cma_node_last_error();
|
|
17
|
+
char msg[256];
|
|
18
|
+
std::snprintf(msg, sizeof msg, "%s failed (%d)%s%s", what, rc,
|
|
19
|
+
(detail && detail[0]) ? ": " : "", (detail && detail[0]) ? detail : "");
|
|
20
|
+
Napi::Error err = Napi::Error::New(env, msg);
|
|
21
|
+
err.Set("code", Napi::Number::New(env, rc));
|
|
22
|
+
err.Set("syscall", Napi::String::New(env, what));
|
|
23
|
+
return err;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
bool read_address(Napi::Env env, const Napi::Value &value, uint8_t out[20]) {
|
|
27
|
+
if (!value.IsTypedArray() || value.As<Napi::TypedArray>().TypedArrayType() != napi_uint8_array) {
|
|
28
|
+
Napi::TypeError::New(env, "address must be a Uint8Array of 20 bytes").ThrowAsJavaScriptException();
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
auto arr = value.As<Napi::Uint8Array>();
|
|
32
|
+
if (arr.ByteLength() != 20) {
|
|
33
|
+
Napi::TypeError::New(env, "address must be exactly 20 bytes").ThrowAsJavaScriptException();
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
std::memcpy(out, arr.Data(), 20);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
bool read_u256(Napi::Env env, const Napi::Value &value, uint8_t out[32]) {
|
|
41
|
+
if (!value.IsBigInt()) {
|
|
42
|
+
Napi::TypeError::New(env, "amount must be a bigint").ThrowAsJavaScriptException();
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
std::memset(out, 0, 32);
|
|
46
|
+
int sign = 0;
|
|
47
|
+
size_t word_count = 4;
|
|
48
|
+
uint64_t words[4] = {0, 0, 0, 0};
|
|
49
|
+
value.As<Napi::BigInt>().ToWords(&sign, &word_count, words);
|
|
50
|
+
if (sign != 0) {
|
|
51
|
+
Napi::RangeError::New(env, "amount must be non-negative").ThrowAsJavaScriptException();
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
for (size_t w = 0; w < word_count && w < 4; w++) {
|
|
55
|
+
uint64_t word = words[w];
|
|
56
|
+
for (int b = 0; b < 8; b++) {
|
|
57
|
+
size_t idx = 31 - (w * 8 + static_cast<size_t>(b));
|
|
58
|
+
out[idx] = static_cast<uint8_t>(word & 0xff);
|
|
59
|
+
word >>= 8;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Napi::BigInt u256_to_bigint(Napi::Env env, const uint8_t amount[32]) {
|
|
66
|
+
uint64_t words[4] = {0, 0, 0, 0};
|
|
67
|
+
for (size_t w = 0; w < 4; w++) {
|
|
68
|
+
uint64_t word = 0;
|
|
69
|
+
for (int b = 0; b < 8; b++) {
|
|
70
|
+
size_t idx = 31 - (w * 8 + static_cast<size_t>(b));
|
|
71
|
+
word |= static_cast<uint64_t>(amount[idx]) << (8 * b);
|
|
72
|
+
}
|
|
73
|
+
words[w] = word;
|
|
74
|
+
}
|
|
75
|
+
return Napi::BigInt::New(env, 0, 4, words);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
class LedgerWrap : public Napi::ObjectWrap<LedgerWrap> {
|
|
79
|
+
public:
|
|
80
|
+
static Napi::FunctionReference constructor;
|
|
81
|
+
|
|
82
|
+
static Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
83
|
+
Napi::Function func = DefineClass(env, "NativeLedger",
|
|
84
|
+
{
|
|
85
|
+
InstanceMethod("depositEther", &LedgerWrap::DepositEther),
|
|
86
|
+
InstanceMethod("transferEther", &LedgerWrap::TransferEther),
|
|
87
|
+
InstanceMethod("withdrawEther", &LedgerWrap::WithdrawEther),
|
|
88
|
+
InstanceMethod("getEtherBalance", &LedgerWrap::GetEtherBalance),
|
|
89
|
+
InstanceMethod("close", &LedgerWrap::Close),
|
|
90
|
+
InstanceAccessor("kind", &LedgerWrap::Kind, nullptr),
|
|
91
|
+
});
|
|
92
|
+
constructor = Napi::Persistent(func);
|
|
93
|
+
constructor.SuppressDestruct();
|
|
94
|
+
|
|
95
|
+
exports.Set("NativeLedger", func);
|
|
96
|
+
exports.Set("openEtherBuffer", Napi::Function::New(env, OpenEtherBuffer));
|
|
97
|
+
exports.Set("openEtherFile", Napi::Function::New(env, OpenEtherFile));
|
|
98
|
+
return exports;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
explicit LedgerWrap(const Napi::CallbackInfo &info) : Napi::ObjectWrap<LedgerWrap>(info) {}
|
|
102
|
+
|
|
103
|
+
~LedgerWrap() override {
|
|
104
|
+
CloseInternal();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void Attach(CmaNodeLedger *ledger) {
|
|
108
|
+
ledger_ = ledger;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private:
|
|
112
|
+
static Napi::Value OpenEtherBuffer(const Napi::CallbackInfo &info) {
|
|
113
|
+
Napi::Env env = info.Env();
|
|
114
|
+
if (info.Length() < 1 || !info[0].IsNumber()) {
|
|
115
|
+
Napi::TypeError::New(env, "openEtherBuffer(maxAccounts)").ThrowAsJavaScriptException();
|
|
116
|
+
return env.Null();
|
|
117
|
+
}
|
|
118
|
+
size_t max_accounts = info[0].As<Napi::Number>().Uint32Value();
|
|
119
|
+
CmaNodeLedger *ledger = cma_node_open_buffer(max_accounts);
|
|
120
|
+
if (!ledger) {
|
|
121
|
+
ledger_error(env, -1001, "openEtherBuffer").ThrowAsJavaScriptException();
|
|
122
|
+
return env.Null();
|
|
123
|
+
}
|
|
124
|
+
Napi::Object obj = constructor.New({});
|
|
125
|
+
LedgerWrap::Unwrap(obj)->Attach(ledger);
|
|
126
|
+
return obj;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static Napi::Value OpenEtherFile(const Napi::CallbackInfo &info) {
|
|
130
|
+
Napi::Env env = info.Env();
|
|
131
|
+
if (info.Length() < 4 || !info[0].IsString() || !info[1].IsNumber() || !info[2].IsNumber() ||
|
|
132
|
+
!info[3].IsNumber()) {
|
|
133
|
+
Napi::TypeError::New(env, "openEtherFile(path, offset, memoryLength, maxAccounts)")
|
|
134
|
+
.ThrowAsJavaScriptException();
|
|
135
|
+
return env.Null();
|
|
136
|
+
}
|
|
137
|
+
std::string path = info[0].As<Napi::String>().Utf8Value();
|
|
138
|
+
size_t offset = static_cast<size_t>(info[1].As<Napi::Number>().Int64Value());
|
|
139
|
+
size_t memory_length = static_cast<size_t>(info[2].As<Napi::Number>().Int64Value());
|
|
140
|
+
size_t max_accounts = info[3].As<Napi::Number>().Uint32Value();
|
|
141
|
+
CmaNodeLedger *ledger = cma_node_open_file(path.c_str(), offset, memory_length, max_accounts);
|
|
142
|
+
if (!ledger) {
|
|
143
|
+
ledger_error(env, -1001, "openEtherFile").ThrowAsJavaScriptException();
|
|
144
|
+
return env.Null();
|
|
145
|
+
}
|
|
146
|
+
Napi::Object obj = constructor.New({});
|
|
147
|
+
LedgerWrap::Unwrap(obj)->Attach(ledger);
|
|
148
|
+
return obj;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
void CloseInternal() {
|
|
152
|
+
if (ledger_) {
|
|
153
|
+
cma_node_close(ledger_);
|
|
154
|
+
ledger_ = nullptr;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
bool Ensure(Napi::Env env) {
|
|
159
|
+
if (!ledger_) {
|
|
160
|
+
ledger_error(env, -1001, "ledger").ThrowAsJavaScriptException();
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
void DepositEther(const Napi::CallbackInfo &info) {
|
|
167
|
+
Napi::Env env = info.Env();
|
|
168
|
+
if (!Ensure(env)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (info.Length() < 2) {
|
|
172
|
+
Napi::TypeError::New(env, "depositEther(account, amount)").ThrowAsJavaScriptException();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
uint8_t addr[20];
|
|
176
|
+
uint8_t amount[32];
|
|
177
|
+
if (!read_address(env, info[0], addr) || !read_u256(env, info[1], amount)) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
int rc = cma_node_deposit_ether(ledger_, addr, amount);
|
|
181
|
+
if (rc < 0) {
|
|
182
|
+
ledger_error(env, rc, "depositEther").ThrowAsJavaScriptException();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
void TransferEther(const Napi::CallbackInfo &info) {
|
|
187
|
+
Napi::Env env = info.Env();
|
|
188
|
+
if (!Ensure(env)) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (info.Length() < 3) {
|
|
192
|
+
Napi::TypeError::New(env, "transferEther(from, to, amount)").ThrowAsJavaScriptException();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
uint8_t from[20];
|
|
196
|
+
uint8_t to[20];
|
|
197
|
+
uint8_t amount[32];
|
|
198
|
+
if (!read_address(env, info[0], from) || !read_address(env, info[1], to) || !read_u256(env, info[2], amount)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
int rc = cma_node_transfer_ether(ledger_, from, to, amount);
|
|
202
|
+
if (rc < 0) {
|
|
203
|
+
ledger_error(env, rc, "transferEther").ThrowAsJavaScriptException();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
void WithdrawEther(const Napi::CallbackInfo &info) {
|
|
208
|
+
Napi::Env env = info.Env();
|
|
209
|
+
if (!Ensure(env)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (info.Length() < 2) {
|
|
213
|
+
Napi::TypeError::New(env, "withdrawEther(account, amount)").ThrowAsJavaScriptException();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
uint8_t addr[20];
|
|
217
|
+
uint8_t amount[32];
|
|
218
|
+
if (!read_address(env, info[0], addr) || !read_u256(env, info[1], amount)) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
int rc = cma_node_withdraw_ether(ledger_, addr, amount);
|
|
222
|
+
if (rc < 0) {
|
|
223
|
+
ledger_error(env, rc, "withdrawEther").ThrowAsJavaScriptException();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
Napi::Value GetEtherBalance(const Napi::CallbackInfo &info) {
|
|
228
|
+
Napi::Env env = info.Env();
|
|
229
|
+
if (!Ensure(env)) {
|
|
230
|
+
return env.Null();
|
|
231
|
+
}
|
|
232
|
+
if (info.Length() < 1) {
|
|
233
|
+
Napi::TypeError::New(env, "getEtherBalance(account)").ThrowAsJavaScriptException();
|
|
234
|
+
return env.Null();
|
|
235
|
+
}
|
|
236
|
+
uint8_t addr[20];
|
|
237
|
+
uint8_t amount[32];
|
|
238
|
+
if (!read_address(env, info[0], addr)) {
|
|
239
|
+
return env.Null();
|
|
240
|
+
}
|
|
241
|
+
int rc = cma_node_get_ether_balance(ledger_, addr, amount);
|
|
242
|
+
if (rc < 0) {
|
|
243
|
+
ledger_error(env, rc, "getEtherBalance").ThrowAsJavaScriptException();
|
|
244
|
+
return env.Null();
|
|
245
|
+
}
|
|
246
|
+
return u256_to_bigint(env, amount);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
void Close(const Napi::CallbackInfo &) {
|
|
250
|
+
CloseInternal();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
Napi::Value Kind(const Napi::CallbackInfo &info) {
|
|
254
|
+
return Napi::String::New(info.Env(), cma_node_kind());
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
CmaNodeLedger *ledger_ = nullptr;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
Napi::FunctionReference LedgerWrap::constructor;
|
|
261
|
+
|
|
262
|
+
Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
|
|
263
|
+
return LedgerWrap::Init(env, exports);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
} // namespace
|
|
267
|
+
|
|
268
|
+
NODE_API_MODULE(libcma, InitAll)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstddef>
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
|
|
6
|
+
#ifdef __cplusplus
|
|
7
|
+
extern "C" {
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
typedef struct CmaNodeLedger CmaNodeLedger;
|
|
11
|
+
|
|
12
|
+
CmaNodeLedger *cma_node_open_buffer(size_t max_accounts);
|
|
13
|
+
CmaNodeLedger *cma_node_open_file(const char *path, size_t offset, size_t memory_length, size_t max_accounts);
|
|
14
|
+
void cma_node_close(CmaNodeLedger *ledger);
|
|
15
|
+
|
|
16
|
+
int cma_node_deposit_ether(CmaNodeLedger *ledger, const uint8_t account[20], const uint8_t amount_be[32]);
|
|
17
|
+
int cma_node_transfer_ether(CmaNodeLedger *ledger, const uint8_t from[20], const uint8_t to[20],
|
|
18
|
+
const uint8_t amount_be[32]);
|
|
19
|
+
int cma_node_withdraw_ether(CmaNodeLedger *ledger, const uint8_t account[20], const uint8_t amount_be[32]);
|
|
20
|
+
int cma_node_get_ether_balance(CmaNodeLedger *ledger, const uint8_t account[20], uint8_t out_amount_be[32]);
|
|
21
|
+
|
|
22
|
+
const char *cma_node_last_error(void);
|
|
23
|
+
/** "native-mock" on host, "native-libcma" when linked to real libcma. */
|
|
24
|
+
const char *cma_node_kind(void);
|
|
25
|
+
|
|
26
|
+
#ifdef __cplusplus
|
|
27
|
+
}
|
|
28
|
+
#endif
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Host behavioral mock — NOT proof-identical to C++ libcma.
|
|
2
|
+
|
|
3
|
+
#include "ledger_backend.h"
|
|
4
|
+
|
|
5
|
+
#include <cstring>
|
|
6
|
+
#include <map>
|
|
7
|
+
#include <string>
|
|
8
|
+
|
|
9
|
+
namespace {
|
|
10
|
+
|
|
11
|
+
constexpr int kSuccess = 0;
|
|
12
|
+
constexpr int kUnknown = -1001;
|
|
13
|
+
constexpr int kInsufficientFunds = -1003;
|
|
14
|
+
constexpr int kMaxAccounts = -1012;
|
|
15
|
+
|
|
16
|
+
struct MockState {
|
|
17
|
+
std::map<std::string, __uint128_t> balances;
|
|
18
|
+
size_t max_accounts = 0;
|
|
19
|
+
bool open = false;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
std::string key_of(const uint8_t *addr) {
|
|
23
|
+
static const char *hex = "0123456789abcdef";
|
|
24
|
+
std::string out(40, '0');
|
|
25
|
+
for (int i = 0; i < 20; i++) {
|
|
26
|
+
out[size_t(i) * 2] = hex[addr[i] >> 4];
|
|
27
|
+
out[size_t(i) * 2 + 1] = hex[addr[i] & 0xf];
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
__uint128_t u256_be_to_u128(const uint8_t amount[32]) {
|
|
33
|
+
for (int i = 0; i < 16; i++) {
|
|
34
|
+
if (amount[i] != 0) {
|
|
35
|
+
return ~__uint128_t{0};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
__uint128_t v = 0;
|
|
39
|
+
for (int i = 16; i < 32; i++) {
|
|
40
|
+
v = (v << 8) | amount[i];
|
|
41
|
+
}
|
|
42
|
+
return v;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
void u128_to_u256_be(__uint128_t v, uint8_t out[32]) {
|
|
46
|
+
std::memset(out, 0, 32);
|
|
47
|
+
for (int i = 31; i >= 16; i--) {
|
|
48
|
+
out[i] = static_cast<uint8_t>(v & 0xff);
|
|
49
|
+
v >>= 8;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
thread_local std::string g_last_error;
|
|
54
|
+
|
|
55
|
+
} // namespace
|
|
56
|
+
|
|
57
|
+
struct CmaNodeLedger {
|
|
58
|
+
MockState state;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
extern "C" {
|
|
62
|
+
|
|
63
|
+
CmaNodeLedger *cma_node_open_buffer(size_t max_accounts) {
|
|
64
|
+
auto *ledger = new CmaNodeLedger();
|
|
65
|
+
ledger->state.max_accounts = max_accounts;
|
|
66
|
+
ledger->state.open = true;
|
|
67
|
+
g_last_error.clear();
|
|
68
|
+
return ledger;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
CmaNodeLedger *cma_node_open_file(const char * /*path*/, size_t /*offset*/, size_t /*memory_length*/,
|
|
72
|
+
size_t max_accounts) {
|
|
73
|
+
return cma_node_open_buffer(max_accounts);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
void cma_node_close(CmaNodeLedger *ledger) {
|
|
77
|
+
delete ledger;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
int cma_node_deposit_ether(CmaNodeLedger *ledger, const uint8_t account[20], const uint8_t amount_be[32]) {
|
|
81
|
+
if (!ledger || !ledger->state.open) {
|
|
82
|
+
g_last_error = "ledger closed";
|
|
83
|
+
return kUnknown;
|
|
84
|
+
}
|
|
85
|
+
__uint128_t amount = u256_be_to_u128(amount_be);
|
|
86
|
+
if (amount == 0) {
|
|
87
|
+
return kSuccess;
|
|
88
|
+
}
|
|
89
|
+
auto key = key_of(account);
|
|
90
|
+
auto it = ledger->state.balances.find(key);
|
|
91
|
+
if (it == ledger->state.balances.end()) {
|
|
92
|
+
if (ledger->state.balances.size() >= ledger->state.max_accounts) {
|
|
93
|
+
g_last_error = "max accounts reached";
|
|
94
|
+
return kMaxAccounts;
|
|
95
|
+
}
|
|
96
|
+
ledger->state.balances.emplace(key, amount);
|
|
97
|
+
} else {
|
|
98
|
+
it->second += amount;
|
|
99
|
+
}
|
|
100
|
+
return kSuccess;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
int cma_node_transfer_ether(CmaNodeLedger *ledger, const uint8_t from[20], const uint8_t to[20],
|
|
104
|
+
const uint8_t amount_be[32]) {
|
|
105
|
+
if (!ledger || !ledger->state.open) {
|
|
106
|
+
g_last_error = "ledger closed";
|
|
107
|
+
return kUnknown;
|
|
108
|
+
}
|
|
109
|
+
__uint128_t amount = u256_be_to_u128(amount_be);
|
|
110
|
+
if (amount == 0) {
|
|
111
|
+
return kSuccess;
|
|
112
|
+
}
|
|
113
|
+
auto from_key = key_of(from);
|
|
114
|
+
auto to_key = key_of(to);
|
|
115
|
+
auto from_it = ledger->state.balances.find(from_key);
|
|
116
|
+
__uint128_t bal = from_it == ledger->state.balances.end() ? 0 : from_it->second;
|
|
117
|
+
if (bal < amount) {
|
|
118
|
+
g_last_error = "insufficient funds";
|
|
119
|
+
return kInsufficientFunds;
|
|
120
|
+
}
|
|
121
|
+
if (to_key != from_key) {
|
|
122
|
+
auto to_it = ledger->state.balances.find(to_key);
|
|
123
|
+
if (to_it == ledger->state.balances.end() &&
|
|
124
|
+
ledger->state.balances.size() >= ledger->state.max_accounts) {
|
|
125
|
+
g_last_error = "max accounts reached";
|
|
126
|
+
return kMaxAccounts;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
from_it->second = bal - amount;
|
|
130
|
+
if (from_it->second == 0) {
|
|
131
|
+
ledger->state.balances.erase(from_it);
|
|
132
|
+
}
|
|
133
|
+
auto to_it = ledger->state.balances.find(to_key);
|
|
134
|
+
if (to_it == ledger->state.balances.end()) {
|
|
135
|
+
ledger->state.balances.emplace(to_key, amount);
|
|
136
|
+
} else {
|
|
137
|
+
to_it->second += amount;
|
|
138
|
+
}
|
|
139
|
+
return kSuccess;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
int cma_node_withdraw_ether(CmaNodeLedger *ledger, const uint8_t account[20], const uint8_t amount_be[32]) {
|
|
143
|
+
if (!ledger || !ledger->state.open) {
|
|
144
|
+
g_last_error = "ledger closed";
|
|
145
|
+
return kUnknown;
|
|
146
|
+
}
|
|
147
|
+
__uint128_t amount = u256_be_to_u128(amount_be);
|
|
148
|
+
if (amount == 0) {
|
|
149
|
+
return kSuccess;
|
|
150
|
+
}
|
|
151
|
+
auto key = key_of(account);
|
|
152
|
+
auto it = ledger->state.balances.find(key);
|
|
153
|
+
__uint128_t bal = it == ledger->state.balances.end() ? 0 : it->second;
|
|
154
|
+
if (bal < amount) {
|
|
155
|
+
g_last_error = "insufficient funds";
|
|
156
|
+
return kInsufficientFunds;
|
|
157
|
+
}
|
|
158
|
+
it->second = bal - amount;
|
|
159
|
+
if (it->second == 0) {
|
|
160
|
+
ledger->state.balances.erase(it);
|
|
161
|
+
}
|
|
162
|
+
return kSuccess;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
int cma_node_get_ether_balance(CmaNodeLedger *ledger, const uint8_t account[20], uint8_t out_amount_be[32]) {
|
|
166
|
+
if (!ledger || !ledger->state.open) {
|
|
167
|
+
g_last_error = "ledger closed";
|
|
168
|
+
return kUnknown;
|
|
169
|
+
}
|
|
170
|
+
auto key = key_of(account);
|
|
171
|
+
auto it = ledger->state.balances.find(key);
|
|
172
|
+
__uint128_t bal = it == ledger->state.balances.end() ? 0 : it->second;
|
|
173
|
+
u128_to_u256_be(bal, out_amount_be);
|
|
174
|
+
return kSuccess;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const char *cma_node_last_error(void) {
|
|
178
|
+
return g_last_error.c_str();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const char *cma_node_kind(void) {
|
|
182
|
+
return "native-mock";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
} // extern "C"
|