1 #include "../include/libcoin/ether.h"
3 #define MAX_WEI_HEX "effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
4 #define WEI_IN_ETHER "1000000000000000000"
12 static void init () __attribute__ ((constructor
));
13 static void done () __attribute__ ((destructor
));
15 mpz_init_set_str(max_wei
, MAX_WEI_HEX
, 16);
16 mpz_init_set_str(wei_in_ether
, WEI_IN_ETHER
, 10);
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);
27 mpz_clear(eth_fast_price
);
28 mpz_clear(eth_average_price
);
29 mpz_clear(eth_slow_price
);
31 mpz_clear(wei_in_ether
);
35 char *ether_hex_to_int_str (const char *amount
) {
37 if (strlen(amount
) < 3)
39 mpz_init_set_str(z
, amount
+2, 16);
40 char *res
= mpz_get_str(NULL
, 10, z
);
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
);
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
;
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);
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("("));
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
))) {
81 coin_errcode
= COIN_INVJSON
;
82 strcpy(coin_errmsg
, "invalid input json");
86 strnadd(&proto
, CONST_STR_LEN(","));
87 strnadd(&proto
, jt
->data
.s
.ptr
, jt
->data
.s
.len
);
90 } while (li
!= args
->head
);
92 strnadd(&proto
, CONST_STR_LEN(")"));
96 static int get_outputs (eth_method_t
*m
, list_t
*outputs
) {
97 if (0 == outputs
->len
)
99 m
->outputs
= calloc(outputs
->len
, sizeof(char*));
100 list_item_t
*li
= outputs
->head
;
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");
108 m
->outputs
[m
->output_len
++] = strndup(j_type
->data
.s
.ptr
, j_type
->data
.s
.len
);
110 } while (li
!= outputs
->head
);
114 static int declare_function (eth_contract_t
*contract
, json_item_t
*jf
) {
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
))
126 if (0 != cmpstr(j_type
->data
.s
.ptr
, j_type
->data
.s
.len
, CONST_STR_LEN("function")))
128 if (!(proto
= get_proto(j_name
->data
.s
.ptr
, j_name
->data
.s
.len
, j_inputs
->data
.a
)))
130 if (!(m
= eth_declare_method(contract
, proto
->ptr
, proto
->len
))) {
135 if (0 == cmpstr(j_state
->data
.s
.ptr
, j_state
->data
.s
.len
, CONST_STR_LEN("view")))
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
)
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
);
152 coin_errcode
= COIN_NOJSON
;
153 strcpy(coin_errmsg
, "input isn't json");
156 if (JSON_ARRAY
!= json
->type
) {
157 coin_errcode
= COIN_INVJSON
;
158 strcpy(coin_errmsg
, "invalid json");
161 contract
= eth_declare_contract(address
);
162 if ((li
= json
->data
.a
->head
)) {
164 json_item_t
*jf
= (json_item_t
*)li
->ptr
;
165 if (-1 == declare_function(contract
, jf
))
168 } while (li
!= json
->data
.a
->head
);
174 eth_free_contract(contract
);
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
];
187 for (size_t j
= 0; j
< m
->output_len
; ++j
)
192 ARRAY_FREE(contract
->methods
);
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
);
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
) {
205 struct curl_slist
*hdrs
= NULL
;
206 hdrs
= curl_slist_append(hdrs
, "Content-Type: application/json");
207 coin_errcode
= COIN_SUCCESS
;
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
);
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
;
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);
229 if (!(*j_result
= json_find(json
->data
.o
, CONST_STR_LEN("result"), -1))) {
232 coin_errcode
= COIN_INVJSON
;
233 strcpy(coin_errmsg
, "ivalid json");
239 coin_errcode
= COIN_INVJSON
;
240 strcpy(coin_errmsg
, "ivalid json");
243 coin_errcode
= COIN_NOJSON
;
244 strcpy(coin_errmsg
, "result isn't json");
247 strcpy(coin_errmsg
, curl_easy_strerror(coin_errcode
));
248 curl_slist_free_all(hdrs
);
252 static void query_open (strbuf_t
*buf
, const char *method
, size_t method_len
) {
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
) {
268 CURL
*curl
= curl_easy_init();
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
);
277 json_t
*json
= do_rpc(curl
, &buf
, &j_result
);
280 rc
= JSON_TRUE
== j_result
->type
? 1 : 0;
284 curl_easy_cleanup(curl
);
288 char *eth_get_coinbase (char *address
, size_t address_len
) {
291 json_item_t
*j_result
;
293 CURL
*curl
= curl_easy_init();
294 strbufalloc(&buf
, 64, 64);
295 query_open(&buf
, CONST_STR_LEN("eth_coinbase"));
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
);
307 curl_easy_cleanup(curl
);
311 char *eth_new_account (const char *pass
, char *address
, size_t address_len
) {
314 json_item_t
*j_result
;
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
);
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
);
331 curl_easy_cleanup(curl
);
335 str_t
*eth_sha3 (const char *param
, size_t param_len
) {
337 json_item_t
*j_result
;
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
);
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);
352 curl_easy_cleanup(curl
);
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
};
366 SORTED_ARRAY_FIND(methods
, t
, idx
);
368 coin_errcode
= COIN_NOMETHOD
;
369 snprintf(coin_errmsg
, sizeof coin_errmsg
, "unknown contract method %s", proto
);
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"));
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
;
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
);
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);
401 strpad(&str
, 64, '0', STR_RIGHT
);
402 strbufadd(&ctx
->buf
, str
->ptr
, str
->len
);
406 int eth_exec (eth_ctx_t
*ctx
, const char *value
, const char *gas
, const char *gas_price
) {
407 CURL
*curl
= curl_easy_init();
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
;
416 strbufadd(&ctx
->buf
, CONST_STR_LEN("\""));
418 strbufadd(&ctx
->buf
, CONST_STR_LEN(","));
419 json_add_str(&ctx
->buf
, CONST_STR_LEN("value"), value
, strlen(value
), JSON_END
);
422 strbufadd(&ctx
->buf
, CONST_STR_LEN(","));
423 json_add_str(&ctx
->buf
, CONST_STR_LEN("gas"), gas
, strlen(gas
), JSON_END
);
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);
441 #define PREPARE_EXEC \
442 CURL *curl = curl_easy_init(); \
445 json_item_t *jr = NULL; \
446 strbufalloc(&buf, 64, 64); \
448 coin_errmsg[0] = '\0';
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
) {
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
);
463 json_add_str(&buf
, CONST_STR_LEN("gas"), gas
, strlen(gas
), gas_price
? JSON_NEXT
: JSON_END
);
465 json_add_str(&buf
, CONST_STR_LEN("gasPrice"), gas_price
, strlen(gas_price
), JSON_END
);
466 json_close_object(&buf
, JSON_END
);
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
);
477 char *eth_newaccount (const char *passwd
) {
480 query_open(&buf
, CONST_STR_LEN("personal_newAccount"));
481 json_add_str(&buf
, CONST_STR_NULL
, passwd
, strlen(passwd
), JSON_END
);
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
);
492 char *eth_coinbase () {
495 query_open(&buf
, CONST_STR_LEN("eth_coinbase"));
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
);
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
) {
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
);
524 char *eth_getbalance (const char *address
, size_t address_len
) {
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
);
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
);
540 void eth_rawstr_to_wei (const char *rawstr
, mpz_t amount
) {
542 mpz_set_str(amount
, rawstr
+2, 16);
545 void eth_rawstr_to_ether (const char *rawstr
, mpf_t amount
) {
548 eth_rawstr_to_wei(rawstr
, z
);
552 mpz_set_str(z
, WEI_IN_ETHER
, 10);
556 mpf_set_q(amount
, qx
);
562 int eth_accounts (json_item_h fn
, void *userdata
, int flags
) {
565 query_open(&buf
, CONST_STR_LEN("eth_accounts"));
567 if ((json
= do_rpc(curl
, &buf
, &jr
))) {
568 if (JSON_ARRAY
== jr
->type
) {
569 json_enum_array(jr
->data
.a
, fn
, userdata
, flags
);
578 int eth_gettransaction_by_hash (const char *hash
, size_t hash_len
, json_item_h fn
, void *userdata
) {
581 query_open(&buf
, CONST_STR_LEN("eth_getTransactionByHash"));
582 json_add_str(&buf
, CONST_STR_NULL
, hash
, hash_len
, JSON_END
);
584 if ((json
= do_rpc(curl
, &buf
, &jr
))) {
585 if (JSON_OBJECT
== jr
->type
)
586 rc
= fn(jr
, userdata
);
594 query_open(&buf
, CONST_STR_LEN("miner_start"));
596 if ((json
= do_rpc(curl
, &buf
, &jr
)))