/* * Copyright 2012 Luke Dashjr * * This program is free software; you can redistribute it and/or modify it * under the terms of the standard MIT license. See COPYING for more details. */ #define _BSD_SOURCE #include #include #ifndef WIN32 #include #else #include #endif #include #include #include #include "private.h" #ifndef JSON_INTEGER_IS_LONG_LONG # error "Jansson 2.0 with long long support required!" #endif json_t *blktmpl_request_jansson(gbt_capabilities_t caps, const char *lpid) { json_t *req, *jcaps, *jstr, *reqf, *reqa; if (!(req = json_object())) return NULL; jstr = reqa = jcaps = NULL; if (!(reqf = json_object())) goto err; if (!(reqa = json_array())) goto err; if (!(jcaps = json_array())) goto err; for (int i = 0; i < GBT_CAPABILITY_COUNT; ++i) if (caps & (1 << i)) { jstr = json_string(blktmpl_capabilityname(1 << i)); if (!jstr) goto err; if (json_array_append_new(jcaps, jstr)) goto err; } jstr = NULL; if (json_object_set_new(req, "capabilities", jcaps)) goto err; jcaps = NULL; if (!(jstr = json_integer(0))) goto err; if (json_object_set_new(reqf, "id", jstr)) goto err; if (!(jstr = json_integer(BLKMAKER_MAX_BLOCK_VERSION))) goto err; if (json_object_set_new(req, "maxversion", jstr)) goto err; if (lpid) { if (!(jstr = json_string(lpid))) goto err; if (json_object_set_new(req, "longpollid", jstr)) goto err; } if (!(jstr = json_string("getblocktemplate"))) goto err; if (json_object_set_new(reqf, "method", jstr)) goto err; jstr = NULL; if (json_array_append_new(reqa, req)) goto err; req = NULL; if (json_object_set_new(reqf, "params", reqa)) goto err; return reqf; err: if (req ) json_decref(req ); if (reqa ) json_decref(reqa ); if (reqf ) json_decref(reqf ); if (jcaps) json_decref(jcaps); if (jstr ) json_decref(jstr ); return NULL; } #define my_hex2bin _blkmk_hex2bin #define GET(key, type) do { \ if (!(v = json_object_get(json, #key))) \ return "Missing '" #key "'"; \ if (!json_is_ ## type(v)) \ return "Wrong type for '" #key "'"; \ } while(0) #define GETHEX(key, skey) do { \ GET(key, string); \ if (!my_hex2bin(tmpl->skey, json_string_value(v), sizeof(tmpl->skey))) \ return "Error decoding '" #key "'"; \ } while(0) #define GETNUM(key) do { \ GET(key, number); \ tmpl->key = json_integer_value(v); \ } while(0) #define GETNUM_O2(key, skey) do { \ if ((v = json_object_get(json, #skey)) && json_is_number(v)) \ tmpl->key = json_integer_value(v); \ } while(0) #define GETNUM_O(key) GETNUM_O2(key, key) #define GETSTR(key, skey) do { \ if ((v = json_object_get(json, #key)) && json_is_string(v)) \ if (!(tmpl->skey = strdup(json_string_value(v)))) \ return "Error copying '" #key "'"; \ } while(0) #define GETBOOL(key, skey, def) do { \ if ((v = json_object_get(json, #key)) && json_is_boolean(v)) \ tmpl->skey = json_is_true(v); \ else \ if (def) \ tmpl->skey = true; \ } while(0) static const char *parse_txn(struct blktxn_t *txn, json_t *txnj) { json_t *vv; if (!((vv = json_object_get(txnj, "data")) && json_is_string(vv))) return "Missing or invalid type for transaction data"; const char *hexdata = json_string_value(vv); size_t datasz = strlen(hexdata) / 2; txn->data = malloc(datasz); txn->datasz = datasz; if (!my_hex2bin(txn->data, hexdata, datasz)) return "Error decoding transaction data"; if ((vv = json_object_get(txnj, "hash")) && json_is_string(vv)) { hexdata = json_string_value(vv); txn->hash = malloc(sizeof(*txn->hash)); if (!my_hex2bin(*txn->hash, hexdata, sizeof(*txn->hash))) { free(txn->hash); txn->hash = NULL; } } // TODO: dependcount/depends, fee, required, sigops return NULL; } static void my_flip(void *data, size_t datasz) { char *cdata = (char*)data; --datasz; size_t hds = datasz / 2; for (size_t i = 0; i <= hds; ++i) { int altp = datasz - i; char c = cdata[i]; cdata[i] = cdata[altp]; cdata[altp] = c; } } const char *blktmpl_add_jansson(blktemplate_t *tmpl, const json_t *json, time_t time_rcvd) { if (tmpl->version) return false; json_t *v, *v2; const char *s; if ((v = json_object_get(json, "result"))) { json_t *je; if ((je = json_object_get(json, "error")) && !json_is_null(je)) return "JSON result is error"; json = v; } GETHEX(bits, diffbits); my_flip(tmpl->diffbits, 4); GETNUM(curtime); GETNUM(height); GETHEX(previousblockhash, prevblk); my_flip(tmpl->prevblk, 32); GETNUM_O(sigoplimit); GETNUM_O(sizelimit); GETNUM(version); GETNUM_O2(cbvalue, coinbasevalue); GETSTR(workid, workid); GETNUM_O(expires); GETSTR(longpollid, lp.id); GETSTR(longpolluri, lp.uri); GETBOOL(submitold, submitold, true); v = json_object_get(json, "transactions"); size_t txns = tmpl->txncount = json_array_size(v); tmpl->txns = calloc(txns, sizeof(*tmpl->txns)); for (size_t i = 0; i < txns; ++i) if ((s = parse_txn(&tmpl->txns[i], json_array_get(v, i)))) return s; if ((v = json_object_get(json, "coinbasetxn")) && json_is_object(v)) { tmpl->cbtxn = calloc(1, sizeof(*tmpl->cbtxn)); if ((s = parse_txn(tmpl->cbtxn, v))) return s; } // TODO: coinbaseaux if ((v = json_object_get(json, "mutable")) && json_is_array(v)) { for (size_t i = json_array_size(v); i--; ) { v2 = json_array_get(v, i); if (!json_is_string(v2)) continue; tmpl->mutations |= blktmpl_getcapability(json_string_value(v2)); } } if (tmpl->version > 2 || (tmpl->version == 2 && !tmpl->height)) { if (tmpl->mutations & BMM_VERDROP) tmpl->version = tmpl->height ? 2 : 1; else if (!(tmpl->mutations & BMM_VERFORCE)) return "Unrecognized block version, and not allowed to reduce or force it"; } tmpl->_time_rcvd = time_rcvd; return NULL; } static char varintEncode(unsigned char *out, uint64_t n) { if (n < 0xfd) { out[0] = n; return 1; } char L; if (n <= 0xffff) { out[0] = '\xfd'; L = 3; } else if (n <= 0xffffffff) { out[0] = '\xfe'; L = 5; } else { out[0] = '\xff'; L = 9; } for (unsigned char i = 1; i < L; ++i) out[i] = (n >> ((i - 1) * 8)) % 256; return L; } #define my_bin2hex _blkmk_bin2hex json_t *blkmk_submit_jansson(blktemplate_t *tmpl, const unsigned char *data, unsigned int dataid, blknonce_t nonce) { unsigned char blk[80 + 8 + 1000000]; memcpy(blk, data, 76); *(uint32_t*)(&blk[76]) = htonl(nonce); size_t offs = 80; if (!(tmpl->mutations & BMAb_TRUNCATE && !dataid)) { offs += varintEncode(&blk[offs], 1 + tmpl->txncount); if (!_blkmk_extranonce(tmpl, &blk[offs], dataid, &offs)) return NULL; if (!(tmpl->mutations & BMAb_COINBASE)) for (unsigned long i = 0; i < tmpl->txncount; ++i) { memcpy(&blk[offs], tmpl->txns[i].data, tmpl->txns[i].datasz); offs += tmpl->txns[i].datasz; } } char blkhex[(offs * 2) + 1]; my_bin2hex(blkhex, blk, offs); json_t *rv = json_array(), *ja, *jb; jb = NULL; if (!(ja = json_string(blkhex))) goto err; if (json_array_append_new(rv, ja)) goto err; if (!(ja = json_object())) goto err; if (tmpl->workid) { if (!(jb = json_string(tmpl->workid))) goto err; if (json_object_set_new(ja, "workid", jb)) goto err; jb = NULL; } if (json_array_append_new(rv, ja)) goto err; if (!(ja = json_object())) goto err; if (!(jb = json_integer(0))) goto err; if (json_object_set_new(ja, "id", jb)) goto err; if (!(jb = json_string("submitblock"))) goto err; if (json_object_set_new(ja, "method", jb)) goto err; jb = NULL; if (json_object_set_new(ja, "params", rv)) goto err; return ja; err: json_decref(rv); if (ja) json_decref(ja); if (jb) json_decref(jb); return NULL; }