fix, building
[libcoin.git] / src / ether.c
blobb5427307e4ebd77c26e17e595e2358faa0c8c4df
1 #include "../include/libcoin/ether.h"
3 #define MAX_WEI_HEX "effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
4 #define WEI_IN_ETHER "1000000000000000000"
5 mpz_t max_wei;
6 mpz_t wei_in_ether,
7 eth_gwei,
8 eth_fast_price,
9 eth_average_price,
10 eth_slow_price;
12 static void init () __attribute__ ((constructor));
13 static void done () __attribute__ ((destructor));
14 static void init () {
15 mpz_init_set_str(max_wei, MAX_WEI_HEX, 16);
16 mpz_init_set_str(wei_in_ether, WEI_IN_ETHER, 10);
17 mpz_init(eth_gwei);
18 mpz_init(eth_fast_price);
19 mpz_init(eth_average_price);
20 mpz_init(eth_slow_price);
21 mpz_set_ui(eth_gwei, 1000000000);
22 mpz_set_ui(eth_fast_price, 40000000000);
23 mpz_set_ui(eth_average_price, 4000000000);
24 mpz_set_ui(eth_slow_price, 600000000);
26 static void done () {
27 mpz_clear(eth_fast_price);
28 mpz_clear(eth_average_price);
29 mpz_clear(eth_slow_price);
30 mpz_clear(eth_gwei);
31 mpz_clear(wei_in_ether);
32 mpz_clear(max_wei);
35 char *ether_hex_to_int_str (const char *amount) {
36 mpz_t z;
37 if (strlen(amount) < 3)
38 return NULL;
39 mpz_init_set_str(z, amount+2, 16);
40 char *res = mpz_get_str(NULL, 10, z);
41 mpz_clear(z);
42 return res;
45 static int method_compare (eth_method_t *x, eth_method_t *y) {
46 return strcmp(x->proto, y->proto);
49 eth_contract_t *eth_declare_contract (const char *address) {
50 eth_contract_t *contract = calloc(1, sizeof(eth_contract_t));
51 contract->address = strdup(address);
52 INIT_SORTED_ARRAY(eth_method_list_t, contract->methods, 8, 8, NULL, method_compare);
53 return contract;
56 eth_method_t *eth_declare_method (eth_contract_t *contract, const char *proto, size_t proto_len) {
57 str_t *sha = eth_sha3(proto, proto_len);
58 eth_method_t *res = NULL;
59 if (sha) {
60 eth_method_t m = { .proto = (char*)proto };
61 SORTED_ARRAY_ADD(contract->methods, m);
62 res = &contract->methods->ptr[contract->methods->idx];
63 memset(res, 0, sizeof(eth_method_t));
64 res->proto = strdup(proto);
65 res->sign = strndup(sha->ptr, 10);
66 free(sha);
68 return res;
71 static str_t *get_proto (const char *name, size_t name_len, list_t *args) {
72 list_item_t *li = args->head;
73 str_t *proto = mkstr(name, name_len, 128);
74 strnadd(&proto, CONST_STR_LEN("("));
75 if (li) {
76 int is_first = 1;
77 do {
78 json_item_t *j = (json_item_t*)li->ptr, *jt;
79 if (JSON_OBJECT != j->type || !(jt = json_find(j->data.o, CONST_STR_LEN("type"), JSON_STRING))) {
80 free(proto);
81 coin_errcode = COIN_INVJSON;
82 strcpy(coin_errmsg, "invalid input json");
83 return NULL;
85 if (!is_first)
86 strnadd(&proto, CONST_STR_LEN(","));
87 strnadd(&proto, jt->data.s.ptr, jt->data.s.len);
88 is_first = 0;
89 li = li->next;
90 } while (li != args->head);
92 strnadd(&proto, CONST_STR_LEN(")"));
93 return proto;
96 static int get_outputs (eth_method_t *m, list_t *outputs) {
97 if (0 == outputs->len)
98 return 0;
99 m->outputs = calloc(outputs->len, sizeof(char*));
100 list_item_t *li = outputs->head;
101 do {
102 json_item_t *jo = (json_item_t*)li->ptr, *j_type;
103 if (JSON_OBJECT != jo->type || !(j_type = json_find(jo->data.o, CONST_STR_LEN("type"), JSON_STRING))) {
104 coin_errcode = COIN_INVJSON;
105 strcpy(coin_errmsg, "invalid json");
106 return -1;
108 m->outputs[m->output_len++] = strndup(j_type->data.s.ptr, j_type->data.s.len);
109 li = li->next;
110 } while (li != outputs->head);
111 return 0;
114 static int declare_function (eth_contract_t *contract, json_item_t *jf) {
115 str_t *proto;
116 eth_method_t *m;
117 json_item_t *j_type, *j_name, *j_state, *j_inputs, *j_payable, *j_outputs;
118 if (!(j_type = json_find(jf->data.o, CONST_STR_LEN("type"), JSON_STRING)) ||
119 !(j_name = json_find(jf->data.o, CONST_STR_LEN("name"), JSON_STRING)) ||
120 !(j_state = json_find(jf->data.o, CONST_STR_LEN("stateMutability"), JSON_STRING)) ||
121 !(j_payable = json_find(jf->data.o, CONST_STR_LEN("payable"), -1)) ||
122 !(j_inputs = json_find(jf->data.o, CONST_STR_LEN("inputs"), JSON_ARRAY)) ||
123 !(j_outputs = json_find(jf->data.o, CONST_STR_LEN("outputs"), JSON_ARRAY)) ||
124 (JSON_TRUE != j_payable->type && JSON_FALSE != j_payable->type))
125 return 0;
126 if (0 != cmpstr(j_type->data.s.ptr, j_type->data.s.len, CONST_STR_LEN("function")))
127 return 0;
128 if (!(proto = get_proto(j_name->data.s.ptr, j_name->data.s.len, j_inputs->data.a)))
129 return -1;
130 if (!(m = eth_declare_method(contract, proto->ptr, proto->len))) {
131 free(proto);
132 return -1;
134 free(proto);
135 if (0 == cmpstr(j_state->data.s.ptr, j_state->data.s.len, CONST_STR_LEN("view")))
136 m->state = ST_VIEW;
137 else
138 if (0 == cmpstr(j_state->data.s.ptr, j_state->data.s.len, CONST_STR_LEN("nonpayable")))
139 m->state = ST_NONPAYABLE;
140 if (JSON_TRUE == j_payable->type)
141 m->is_payable = 1;
142 return get_outputs(m, j_outputs->data.a);
145 eth_contract_t *eth_load_contract (const char *address, const char *intf, size_t intf_len) {
146 eth_contract_t *contract = NULL;
147 json_t *json = json_parse_len(intf, intf_len);
148 list_item_t *li;
149 coin_errcode = 0;
150 *coin_errmsg = '\0';
151 if (!json) {
152 coin_errcode = COIN_NOJSON;
153 strcpy(coin_errmsg, "input isn't json");
154 goto err;
156 if (JSON_ARRAY != json->type) {
157 coin_errcode = COIN_INVJSON;
158 strcpy(coin_errmsg, "invalid json");
159 goto err;
161 contract = eth_declare_contract(address);
162 if ((li = json->data.a->head)) {
163 do {
164 json_item_t *jf = (json_item_t*)li->ptr;
165 if (-1 == declare_function(contract, jf))
166 goto err;
167 li = li->next;
168 } while (li != json->data.a->head);
170 json_free(json);
171 return contract;
172 err:
173 if (contract)
174 eth_free_contract(contract);
175 if (json)
176 json_free(json);
177 return NULL;
180 void eth_free_contract (eth_contract_t *contract) {
181 free(contract->address);
182 for (size_t i = 0; i < contract->methods->len; ++i) {
183 eth_method_t *m = &contract->methods->ptr[i];
184 free(m->proto);
185 free(m->sign);
186 if (m->outputs) {
187 for (size_t j = 0; j < m->output_len; ++j)
188 free(m->outputs[j]);
189 free(m->outputs);
192 ARRAY_FREE(contract->methods);
193 free(contract);
196 static size_t write_callback (char *ptr, size_t size, size_t nmemb, strbuf_t *buf) {
197 size_t len = size * nmemb;
198 strbufadd(buf, ptr, len);
199 return len;
202 typedef size_t (*write_callback_h) (char*, size_t, size_t, void*);
203 static json_t *do_rpc (CURL *curl, strbuf_t *buf, json_item_t **j_result) {
204 json_t *json = NULL;
205 struct curl_slist *hdrs = NULL;
206 hdrs = curl_slist_append(hdrs, "Content-Type: application/json");
207 coin_errcode = COIN_SUCCESS;
208 *coin_errmsg = '\0';
209 curl_easy_setopt(curl, CURLOPT_URL, eth_conf.host->ptr);
210 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, buf->len);
211 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, buf->ptr);
212 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (write_callback_h)write_callback);
213 curl_easy_setopt(curl, CURLOPT_WRITEDATA, buf);
214 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hdrs);
215 buf->len = 0;
216 if (CURLE_OK == (coin_errcode = curl_easy_perform(curl))) {
217 buf->ptr[buf->len] = '\0';
218 if ((json = json_parse_len(buf->ptr, buf->len))) {
219 if (JSON_OBJECT == json->type) {
220 json_item_t *j_err = json_find(json->data.o, CONST_STR_LEN("error"), JSON_OBJECT), *j_code, *j_msg;
221 if (j_err &&
222 (j_code = json_find(j_err->data.o, CONST_STR_LEN("code"), JSON_INTEGER)) &&
223 (j_msg = json_find(j_err->data.o, CONST_STR_LEN("message"), JSON_STRING))) {
224 coin_errcode = j_code->data.i;
225 strncpy(coin_errmsg, j_msg->data.s.ptr, j_msg->data.s.len < sizeof(coin_errmsg)-1 ? j_msg->data.s.len : sizeof(coin_errmsg)-1);
226 json_free(json);
227 json = NULL;
228 } else {
229 if (!(*j_result = json_find(json->data.o, CONST_STR_LEN("result"), -1))) {
230 json_free(json);
231 json = NULL;
232 coin_errcode = COIN_INVJSON;
233 strcpy(coin_errmsg, "ivalid json");
236 } else {
237 json_free(json);
238 json = NULL;
239 coin_errcode = COIN_INVJSON;
240 strcpy(coin_errmsg, "ivalid json");
242 } else {
243 coin_errcode = COIN_NOJSON;
244 strcpy(coin_errmsg, "result isn't json");
246 } else
247 strcpy(coin_errmsg, curl_easy_strerror(coin_errcode));
248 curl_slist_free_all(hdrs);
249 return json;
252 static void query_open (strbuf_t *buf, const char *method, size_t method_len) {
253 buf->len = 0;
254 json_begin_object(buf);
255 json_add_int(buf, CONST_STR_LEN("id"), 1, JSON_NEXT);
256 json_add_str(buf, CONST_STR_LEN("jsonrpc"), CONST_STR_LEN("2.0"), JSON_NEXT);
257 json_add_str(buf, CONST_STR_LEN("method"), method, method_len, JSON_NEXT);
258 json_open_array(buf, CONST_STR_LEN("params"));
261 static void query_close (strbuf_t *buf) {
262 json_close_array(buf, JSON_END);
263 json_close_object(buf, JSON_END);
266 int eth_unlock_account (const char *account, const char *passwd, int timeout) {
267 int rc = -1;
268 CURL *curl = curl_easy_init();
269 strbuf_t buf;
270 json_item_t *j_result;
271 strbufalloc(&buf, 64, 64);
272 query_open(&buf, CONST_STR_LEN("personal_unlockAccount"));
273 json_add_str(&buf, CONST_STR_NULL, account, strlen(account), JSON_NEXT);
274 json_add_str(&buf, CONST_STR_NULL, passwd, strlen(passwd), JSON_NEXT);
275 json_add_int(&buf, CONST_STR_NULL, timeout <= 0 ? ETH_UNLOCK_TIMEOUT : timeout, JSON_END);
276 query_close(&buf);
277 json_t *json = do_rpc(curl, &buf, &j_result);
278 if (json) {
279 if (j_result)
280 rc = JSON_TRUE == j_result->type ? 1 : 0;
281 json_free(json);
283 free(buf.ptr);
284 curl_easy_cleanup(curl);
285 return rc;
288 char *eth_get_coinbase (char *address, size_t address_len) {
289 char *res = NULL;
290 json_t *json;
291 json_item_t *j_result;
292 strbuf_t buf;
293 CURL *curl = curl_easy_init();
294 strbufalloc(&buf, 64, 64);
295 query_open(&buf, CONST_STR_LEN("eth_coinbase"));
296 query_close(&buf);
297 if ((json = do_rpc(curl, &buf, &j_result))) {
298 if (j_result && JSON_STRING == j_result->type) {
299 size_t len = j_result->data.s.len < address_len ? j_result->data.s.len : address_len;
300 strncpy(address, j_result->data.s.ptr, len);
301 address[len] = '\0';
302 res = address;
304 json_free(json);
306 free(buf.ptr);
307 curl_easy_cleanup(curl);
308 return res;
311 char *eth_new_account (const char *pass, char *address, size_t address_len) {
312 char *res = NULL;
313 json_t *json;
314 json_item_t *j_result;
315 strbuf_t buf;
316 CURL *curl = curl_easy_init();
317 strbufalloc(&buf, 64, 64);
318 query_open(&buf, CONST_STR_LEN("personal_newAccount"));
319 json_add_str(&buf, CONST_STR_NULL, pass, strlen(pass), JSON_END);
320 query_close(&buf);
321 if ((json = do_rpc(curl, &buf, &j_result))) {
322 if (j_result && JSON_STRING == j_result->type) {
323 size_t len = j_result->data.s.len < address_len ? j_result->data.s.len : address_len;
324 strncpy(address, j_result->data.s.ptr, len);
325 address[len] = '\0';
326 res = address;
328 json_free(json);
330 free(buf.ptr);
331 curl_easy_cleanup(curl);
332 return res;
335 str_t *eth_sha3 (const char *param, size_t param_len) {
336 json_t *json;
337 json_item_t *j_result;
338 strbuf_t buf;
339 str_t *str = strhex("0x", param, param_len, 8), *result = NULL;
340 CURL *curl = curl_easy_init();
341 strbufalloc(&buf, 256, 64);
342 query_open(&buf, CONST_STR_LEN("web3_sha3"));
343 json_add_str(&buf, CONST_STR_NULL, str->ptr, str->len, JSON_END);
344 query_close(&buf);
345 free(str);
346 if ((json = do_rpc(curl, &buf, &j_result))) {
347 if (j_result && JSON_STRING == j_result->type)
348 result = mkstr(j_result->data.s.ptr, j_result->data.s.len, 8);
349 json_free(json);
351 free(buf.ptr);
352 curl_easy_cleanup(curl);
353 return result;
356 void eth_ctx_clear (eth_ctx_t *ctx) {
357 if (ctx->buf.ptr) free(ctx->buf.ptr);
358 if (ctx->json) json_free(ctx->json);
359 if (ctx->result) free(ctx->result);
362 int eth_prepare_exec (eth_contract_t *contract, const char *from, const char *proto, eth_ctx_t *ctx) {
363 eth_method_list_t *methods = contract->methods;
364 eth_method_t t = { .proto = (char*)proto };
365 size_t idx;
366 SORTED_ARRAY_FIND(methods, t, idx);
367 if (0 == errno) {
368 coin_errcode = COIN_NOMETHOD;
369 snprintf(coin_errmsg, sizeof coin_errmsg, "unknown contract method %s", proto);
370 return -1;
372 eth_method_t *m = &methods->ptr[methods->idx];
373 strbuf_t *buf = &ctx->buf;
374 strbufalloc(buf, 256, 256);
375 if (ST_VIEW == m->state)
376 query_open(buf, CONST_STR_LEN("eth_call"));
377 else
378 query_open(buf, CONST_STR_LEN("eth_sendTransaction"));
379 json_open_object(buf, CONST_STR_NULL);
380 json_add_str(buf, CONST_STR_LEN("from"), from, strlen(from), JSON_NEXT);
381 json_add_str(buf, CONST_STR_LEN("to"), contract->address, strlen(contract->address), JSON_NEXT);
382 json_add_key(buf, CONST_STR_LEN("data"));
383 strbufadd(buf, CONST_STR_LEN("\""));
384 strbufadd(buf, m->sign, strlen(m->sign));
385 ctx->contract = contract;
386 ctx->method = m;
387 return 0;
390 void eth_param_address (eth_ctx_t *ctx, const char *address) {
391 str_t *str = mkstr(address+2, strlen(address)-2, 64);
392 strpad(&str, 64, '0', STR_RIGHT);
393 strbufadd(&ctx->buf, str->ptr, str->len);
394 free(str);
397 void eth_param_int (eth_ctx_t *ctx, mpz_t x) {
398 char *s = mpz_get_str(NULL, 16, x);
399 str_t *str = mkstr(s, strlen(s), 64);
400 free(s);
401 strpad(&str, 64, '0', STR_RIGHT);
402 strbufadd(&ctx->buf, str->ptr, str->len);
403 free(str);
406 int eth_exec (eth_ctx_t *ctx, const char *value, const char *gas, const char *gas_price) {
407 CURL *curl = curl_easy_init();
408 int rc = -1;
409 if (ST_VIEW == ctx->method->state) {
410 strbufadd(&ctx->buf, CONST_STR_LEN("\""));
411 json_close_object(&ctx->buf, JSON_NEXT);
412 json_add_str(&ctx->buf, CONST_STR_NULL, CONST_STR_LEN("latest"), JSON_END);
413 query_close(&ctx->buf);
414 ctx->call_type = ETH_CALL;
415 } else {
416 strbufadd(&ctx->buf, CONST_STR_LEN("\""));
417 if (value) {
418 strbufadd(&ctx->buf, CONST_STR_LEN(","));
419 json_add_str(&ctx->buf, CONST_STR_LEN("value"), value, strlen(value), JSON_END);
421 if (gas) {
422 strbufadd(&ctx->buf, CONST_STR_LEN(","));
423 json_add_str(&ctx->buf, CONST_STR_LEN("gas"), gas, strlen(gas), JSON_END);
425 if (gas_price) {
426 strbufadd(&ctx->buf, CONST_STR_LEN(","));
427 json_add_str(&ctx->buf, CONST_STR_LEN("gasPrice"), gas_price, strlen(gas_price), JSON_END);
429 json_close_object(&ctx->buf, JSON_END);
430 query_close(&ctx->buf);
431 ctx->call_type = ETH_SENDTRAN;
433 json_item_t *j_result = NULL;
434 if ((ctx->json = do_rpc(curl, &ctx->buf, &j_result)) && (j_result)) {
435 ctx->result = mkstr(j_result->data.s.ptr, j_result->data.s.len, 8);
436 rc = 0;
438 return rc;
441 #define PREPARE_EXEC \
442 CURL *curl = curl_easy_init(); \
443 strbuf_t buf; \
444 json_t *json; \
445 json_item_t *jr = NULL; \
446 strbufalloc(&buf, 64, 64); \
447 coin_errcode = 0; \
448 coin_errmsg[0] = '\0';
450 #define DONE_EXEC \
451 free(buf.ptr); \
452 curl_easy_cleanup(curl);
454 char *eth_send (const char *from, const char *to, const char *amount, const char *gas, const char *gas_price) {
455 PREPARE_EXEC
456 char *txid = NULL;
457 query_open(&buf, CONST_STR_LEN("eth_sendTransaction"));
458 json_open_object(&buf, CONST_STR_NULL);
459 json_add_str(&buf, CONST_STR_LEN("from"), from, strlen(from), JSON_NEXT);
460 json_add_str(&buf, CONST_STR_LEN("to"), to, strlen(to), JSON_NEXT);
461 json_add_str(&buf, CONST_STR_LEN("value"), amount, strlen(amount), gas ? JSON_NEXT : JSON_END);
462 if (gas)
463 json_add_str(&buf, CONST_STR_LEN("gas"), gas, strlen(gas), gas_price ? JSON_NEXT : JSON_END);
464 if (gas_price)
465 json_add_str(&buf, CONST_STR_LEN("gasPrice"), gas_price, strlen(gas_price), JSON_END);
466 json_close_object(&buf, JSON_END);
467 query_close(&buf);
468 if ((json = do_rpc(curl, &buf, &jr))) {
469 if (JSON_STRING == jr->type)
470 txid = strndup(jr->data.s.ptr, jr->data.s.len);
471 json_free(json);
473 DONE_EXEC
474 return txid;
477 char *eth_newaccount (const char *passwd) {
478 PREPARE_EXEC
479 char *addr = NULL;
480 query_open(&buf, CONST_STR_LEN("personal_newAccount"));
481 json_add_str(&buf, CONST_STR_NULL, passwd, strlen(passwd), JSON_END);
482 query_close(&buf);
483 if ((json = do_rpc(curl, &buf, &jr))) {
484 if (JSON_STRING == jr->type)
485 addr = strndup(jr->data.s.ptr, jr->data.s.len);
486 json_free(json);
488 DONE_EXEC
489 return addr;
492 char *eth_coinbase () {
493 PREPARE_EXEC
494 char *addr = NULL;
495 query_open(&buf, CONST_STR_LEN("eth_coinbase"));
496 query_close(&buf);
497 if ((json = do_rpc(curl, &buf, &jr))) {
498 if (JSON_STRING == jr->type)
499 addr = strndup(jr->data.s.ptr, jr->data.s.len);
500 json_free(json);
502 DONE_EXEC
503 return addr;
506 char *eth_send_wei (const char *from, const char *to, mpz_srcptr amount, mpz_srcptr gas, mpz_srcptr gas_price) {
507 char s_amount [256] = "0x", s_gas [256] = "0x0", s_price [256] = "0x0";
508 mpz_get_str(s_amount+2, 16, amount);
509 if (gas) mpz_get_str(s_gas+2, 16, gas);
510 if (gas_price) mpz_get_str(s_price+2, 16, gas_price);
511 return eth_send(from, to, s_amount, s_gas, s_price);
514 char *eth_send_ether (const char *from, const char *to, mpq_srcptr amount, mpz_srcptr gas, mpz_srcptr gas_price) {
515 mpq_t q;
516 mpq_init(q);
517 mpq_set_z(q, wei_in_ether);
518 mpq_mul(q, q, amount);
519 char *txid = eth_send_wei(from, to, &q->_mp_num, gas , gas_price);
520 mpq_clear(q);
521 return txid;
524 char *eth_getbalance (const char *address, size_t address_len) {
525 PREPARE_EXEC
526 char *res = NULL;
527 query_open(&buf, CONST_STR_LEN("eth_getBalance"));
528 json_add_str(&buf, CONST_STR_NULL, address, address_len, JSON_NEXT);
529 json_add_str(&buf, CONST_STR_NULL, CONST_STR_LEN("latest"), JSON_END);
530 query_close(&buf);
531 if ((json = do_rpc(curl, &buf, &jr))) {
532 if (JSON_STRING == jr->type)
533 res = strndup(jr->data.s.ptr, jr->data.s.len);
534 json_free(json);
536 DONE_EXEC
537 return res;
540 void eth_rawstr_to_wei (const char *rawstr, mpz_t amount) {
541 mpz_init(amount);
542 mpz_set_str(amount, rawstr+2, 16);
545 void eth_rawstr_to_ether (const char *rawstr, mpf_t amount) {
546 mpz_t z;
547 mpq_t qx, qy;
548 eth_rawstr_to_wei(rawstr, z);
549 mpq_init(qx);
550 mpq_init(qy);
551 mpq_set_z(qx, z);
552 mpz_set_str(z, WEI_IN_ETHER, 10);
553 mpq_set_z(qy, z);
554 mpq_div(qx, qx, qy);
555 mpf_init(amount);
556 mpf_set_q(amount, qx);
557 mpz_clear(z);
558 mpq_clear(qx);
559 mpq_clear(qy);
562 int eth_accounts (json_item_h fn, void *userdata, int flags) {
563 PREPARE_EXEC
564 int rc = -1;
565 query_open(&buf, CONST_STR_LEN("eth_accounts"));
566 query_close(&buf);
567 if ((json = do_rpc(curl, &buf, &jr))) {
568 if (JSON_ARRAY == jr->type) {
569 json_enum_array(jr->data.a, fn, userdata, flags);
570 rc = 0;
572 json_free(json);
574 DONE_EXEC
575 return rc;
578 int eth_gettransaction_by_hash (const char *hash, size_t hash_len, json_item_h fn, void *userdata) {
579 PREPARE_EXEC
580 int rc = -1;
581 query_open(&buf, CONST_STR_LEN("eth_getTransactionByHash"));
582 json_add_str(&buf, CONST_STR_NULL, hash, hash_len, JSON_END);
583 query_close(&buf);
584 if ((json = do_rpc(curl, &buf, &jr))) {
585 if (JSON_OBJECT == jr->type)
586 rc = fn(jr, userdata);
588 DONE_EXEC
589 return rc;
592 void eth_mine () {
593 PREPARE_EXEC
594 query_open(&buf, CONST_STR_LEN("miner_start"));
595 query_close(&buf);
596 if ((json = do_rpc(curl, &buf, &jr)))
597 json_free(json);
598 DONE_EXEC