Initial import
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
*~
|
||||||
|
*.so
|
||||||
|
*.o
|
||||||
|
a.*
|
||||||
|
todo*
|
||||||
|
example
|
||||||
21
Makefile
Normal file
21
Makefile
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
LIBNAME := blkmaker
|
||||||
|
|
||||||
|
CFLAGS := -ggdb -O0 -Wall -Werror
|
||||||
|
|
||||||
|
all: lib$(LIBNAME).so lib$(LIBNAME)_jansson.so
|
||||||
|
|
||||||
|
example: example.o lib$(LIBNAME).so lib$(LIBNAME)_jansson.so
|
||||||
|
$(CC) $(LDFLAGS) -o $@ $^ -ljansson -lgcrypt -Wl,-rpath,.
|
||||||
|
|
||||||
|
lib$(LIBNAME).so: blkmaker.o blktemplate.o
|
||||||
|
|
||||||
|
lib$(LIBNAME)_jansson.so: blkmaker_jansson.o
|
||||||
|
|
||||||
|
%.so:
|
||||||
|
$(CC) -shared $(LDFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
%.o: %.c *.h
|
||||||
|
$(CC) -c -fPIC -I. $(CFLAGS) -std=c99 -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.so *.o
|
||||||
16
README
Normal file
16
README
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
Dependencies:
|
||||||
|
Jansson 2.0 with 'long long' support
|
||||||
|
|
||||||
|
Example dependencies:
|
||||||
|
Jansson 2.1 (to read JSON from stdin)
|
||||||
|
libgcrypt (for SHA256)
|
||||||
|
|
||||||
|
For usage, check out example.c
|
||||||
|
Note that you must assign blkmk_sha256_impl to a function pointer:
|
||||||
|
bool mysha256(void *hash_out, const void *data, size_t datasz)
|
||||||
|
hash_out must be able to overlap with data!
|
||||||
|
|
||||||
|
Also note that you should NOT roll ntime for data retrieved; while it will
|
||||||
|
probably work, there is no guarantee it won't fall outside the maxtime limits
|
||||||
|
for the blocktemplate. It is usually best to simply get more data as often
|
||||||
|
as it is needed.
|
||||||
96
blkmaker.c
Normal file
96
blkmaker.c
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include <limits.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <blkmaker.h>
|
||||||
|
#include <blktemplate.h>
|
||||||
|
|
||||||
|
static inline
|
||||||
|
void my_htole32(unsigned char *buf, uint32_t n) {
|
||||||
|
buf[0] = (n >> 0) % 256;
|
||||||
|
buf[1] = (n >> 8) % 256;
|
||||||
|
buf[2] = (n >> 16) % 256;
|
||||||
|
buf[3] = (n >> 24) % 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool (*blkmk_sha256_impl)(void *, const void *, size_t) = NULL;
|
||||||
|
|
||||||
|
static
|
||||||
|
bool dblsha256(void *hash, const void *data, size_t datasz) {
|
||||||
|
return blkmk_sha256_impl(hash, data, datasz) && blkmk_sha256_impl(hash, hash, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
bool build_merkle_root(unsigned char *mrklroot_out, blktemplate_t *tmpl) {
|
||||||
|
if (!tmpl->cbtxn)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t hashcount = tmpl->txncount + 1;
|
||||||
|
unsigned char hashes[(hashcount + 1) * 32];
|
||||||
|
|
||||||
|
if (!dblsha256(&hashes[0], tmpl->cbtxn->data, tmpl->cbtxn->datasz))
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < tmpl->txncount; ++i)
|
||||||
|
if (!dblsha256(&hashes[32 * (i + 1)], tmpl->txns[i].data, tmpl->txns[i].datasz))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (hashcount > 1)
|
||||||
|
{
|
||||||
|
if (hashcount % 2)
|
||||||
|
{
|
||||||
|
memcpy(&hashes[32 * hashcount], &hashes[32 * (hashcount - 1)], 32);
|
||||||
|
++hashcount;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < hashcount; i += 2)
|
||||||
|
// This is where we overlap input and output, on the first pair
|
||||||
|
if (!dblsha256(&hashes[i / 2 * 32], &hashes[32 * i], 64))
|
||||||
|
return false;
|
||||||
|
hashcount /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(mrklroot_out, &hashes[0], 32);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t blkmk_get_data(blktemplate_t *tmpl, void *buf, size_t bufsz, time_t usetime, int16_t *out_expire) {
|
||||||
|
if (!(blkmk_time_left(tmpl, usetime) && blkmk_work_left(tmpl)))
|
||||||
|
return 0;
|
||||||
|
if (bufsz < 76)
|
||||||
|
return 76;
|
||||||
|
|
||||||
|
unsigned char *cbuf = buf;
|
||||||
|
|
||||||
|
my_htole32(&cbuf[0], tmpl->version);
|
||||||
|
memcpy(&cbuf[4], &tmpl->prevblk, 32);
|
||||||
|
if (!build_merkle_root(&cbuf[36], tmpl))
|
||||||
|
return 0;
|
||||||
|
blktime_t timehdr = tmpl->curtime + difftime(usetime, tmpl->_time_rcvd);
|
||||||
|
if (timehdr > tmpl->maxtime)
|
||||||
|
timehdr = tmpl->maxtime;
|
||||||
|
my_htole32(&cbuf[68], timehdr);
|
||||||
|
memcpy(&cbuf[72], &tmpl->diffbits, 4);
|
||||||
|
// TODO: set *out_expire if provided
|
||||||
|
|
||||||
|
// TEMPORARY HACK:
|
||||||
|
memcpy(tmpl->_mrklroot, &cbuf[36], 32);
|
||||||
|
|
||||||
|
return 76;
|
||||||
|
}
|
||||||
|
|
||||||
|
blktime_diff_t blkmk_time_left(const blktemplate_t *tmpl, time_t nowtime) {
|
||||||
|
double age = difftime(nowtime, tmpl->_time_rcvd);
|
||||||
|
if (age >= tmpl->expires)
|
||||||
|
return 0;
|
||||||
|
return tmpl->expires - age;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long blkmk_work_left(const blktemplate_t *tmpl) {
|
||||||
|
// TODO
|
||||||
|
// TEMPORARY HACK
|
||||||
|
return (tmpl->version && !tmpl->_mrklroot[0]) ? 1 : 0;
|
||||||
|
return BLKMK_UNLIMITED_WORK_COUNT;
|
||||||
|
}
|
||||||
17
blkmaker.h
Normal file
17
blkmaker.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef BLKMAKER_H
|
||||||
|
#define BLKMAKER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <blktemplate.h>
|
||||||
|
|
||||||
|
#define BLKMAKER_VERSION (0L)
|
||||||
|
|
||||||
|
extern bool (*blkmk_sha256_impl)(void *hash_out, const void *data, size_t datasz);
|
||||||
|
|
||||||
|
extern size_t blkmk_get_data(blktemplate_t *, void *buf, size_t bufsz, time_t usetime, int16_t *out_expire);
|
||||||
|
extern blktime_diff_t blkmk_time_left(const blktemplate_t *, time_t nowtime);
|
||||||
|
extern unsigned long blkmk_work_left(const blktemplate_t *);
|
||||||
|
#define BLKMK_UNLIMITED_WORK_COUNT ULONG_MAX
|
||||||
|
|
||||||
|
#endif
|
||||||
302
blkmaker_jansson.c
Normal file
302
blkmaker_jansson.c
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
#define _BSD_SOURCE
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <jansson.h>
|
||||||
|
|
||||||
|
#include <blktemplate.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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (!(jstr = json_integer(0)))
|
||||||
|
goto err;
|
||||||
|
if (json_object_set_new(req, "capabilities", jcaps))
|
||||||
|
goto err;
|
||||||
|
if (json_object_set_new(reqf, "id", jstr))
|
||||||
|
goto err;
|
||||||
|
if (!(jstr = json_string("getblocktemplate")))
|
||||||
|
goto err;
|
||||||
|
if (json_object_set_new(reqf, "method", jstr))
|
||||||
|
goto err;
|
||||||
|
if (json_array_append_new(reqa, req))
|
||||||
|
goto err;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool my_hex2bin(void *o, const char *x, size_t len) {
|
||||||
|
unsigned char *oc = o;
|
||||||
|
unsigned char c, hc = 0x10;
|
||||||
|
len *= 2;
|
||||||
|
while (len)
|
||||||
|
{
|
||||||
|
switch (x[0]) {
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
c = x[0] - '0';
|
||||||
|
break;
|
||||||
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
||||||
|
c = x[0] - 'A' + 10;
|
||||||
|
break;
|
||||||
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
||||||
|
c = x[0] - 'a' + 10;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++x;
|
||||||
|
if (hc < 0x10)
|
||||||
|
{
|
||||||
|
(oc++)[0] = (hc << 4) | c;
|
||||||
|
hc = 0x10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
hc = c;
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
return !x[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
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 (int 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, json_t *json, time_t time_rcvd) {
|
||||||
|
if (tmpl->version)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
json_t *v;
|
||||||
|
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);
|
||||||
|
GETNUM(curtime);
|
||||||
|
GETNUM(height);
|
||||||
|
GETHEX(previousblockhash, prevblk);
|
||||||
|
my_flip(tmpl->prevblk, 32);
|
||||||
|
GETNUM(sigoplimit);
|
||||||
|
GETNUM(sizelimit);
|
||||||
|
GETNUM(version);
|
||||||
|
|
||||||
|
if ((v = json_object_get(json, "coinbasevalue")) && json_is_number(v))
|
||||||
|
tmpl->cbvalue = json_integer_value(v);
|
||||||
|
|
||||||
|
if ((v = json_object_get(json, "workid")) && json_is_string(v))
|
||||||
|
if (!(tmpl->workid = strdup(json_string_value(v))))
|
||||||
|
return "Error copying 'workid'";
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *blkmk_submit_jansson(blktemplate_t *tmpl, const unsigned char *data, blknonce_t nonce) {
|
||||||
|
unsigned char blk[80 + 8 + 1000000];
|
||||||
|
memcpy(blk, data, 76);
|
||||||
|
*(uint32_t*)(&blk[76]) = htonl(nonce);
|
||||||
|
size_t offs = 80;
|
||||||
|
offs += varintEncode(&blk[offs], 1 + tmpl->txncount);
|
||||||
|
|
||||||
|
memcpy(&blk[offs], tmpl->cbtxn->data, tmpl->cbtxn->datasz);
|
||||||
|
offs += tmpl->cbtxn->datasz;
|
||||||
|
for (int 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];
|
||||||
|
blkhex[offs * 2] = '\0';
|
||||||
|
static char hex[] = "0123456789abcdef";
|
||||||
|
for (size_t i = 0; i < offs; ++i)
|
||||||
|
{
|
||||||
|
blkhex[ i*2 ] = hex[blk[i] >> 4];
|
||||||
|
blkhex[(i*2)+1] = hex[blk[i] & 15];
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
12
blkmaker_jansson.h
Normal file
12
blkmaker_jansson.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef BLKMAKER_JANSSON_H
|
||||||
|
#define BLKMAKER_JANSSON_H
|
||||||
|
|
||||||
|
#include <jansson.h>
|
||||||
|
|
||||||
|
#include <blktemplate.h>
|
||||||
|
|
||||||
|
extern json_t *blktmpl_request_jansson(gbt_capabilities_t extracaps);
|
||||||
|
extern const char *blktmpl_add_jansson(blktemplate_t *, json_t *, time_t time_rcvd);
|
||||||
|
extern json_t *blkmk_submit_jansson(blktemplate_t *, const unsigned char *data, blknonce_t);
|
||||||
|
|
||||||
|
#endif
|
||||||
86
blktemplate.c
Normal file
86
blktemplate.c
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#define _BSD_SOURCE
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <blktemplate.h>
|
||||||
|
|
||||||
|
static const char *capnames[] = {
|
||||||
|
"coinbasetxn",
|
||||||
|
"coinbasevalue",
|
||||||
|
"workid",
|
||||||
|
|
||||||
|
"longpoll",
|
||||||
|
"proposal",
|
||||||
|
"serverlist",
|
||||||
|
NULL, NULL,
|
||||||
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||||
|
|
||||||
|
"coinbase/append",
|
||||||
|
"coinbase",
|
||||||
|
"generate",
|
||||||
|
"time/increment",
|
||||||
|
"time/decrement",
|
||||||
|
"transactions/add",
|
||||||
|
"prevblock",
|
||||||
|
NULL,
|
||||||
|
|
||||||
|
"submit/hash",
|
||||||
|
"submit/coinbase",
|
||||||
|
"submit/truncate",
|
||||||
|
"share/coinbase",
|
||||||
|
"share/merkle",
|
||||||
|
"share/truncate",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *blktmpl_capabilityname(gbt_capabilities_t caps) {
|
||||||
|
for (int i = 0; i < sizeof(capnames); ++i)
|
||||||
|
if (caps & (1 << i))
|
||||||
|
return capnames[i];
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
blktemplate_t *blktmpl_create() {
|
||||||
|
blktemplate_t *tmpl;
|
||||||
|
tmpl = calloc(1, sizeof(*tmpl));
|
||||||
|
|
||||||
|
tmpl->maxtime = 0xffffffff;
|
||||||
|
tmpl->maxtimeoff = 0x7fff;
|
||||||
|
tmpl->mintimeoff = -0x7fff;
|
||||||
|
tmpl->maxnonce = 0xffffffff;
|
||||||
|
tmpl->expires = 0x7fff;
|
||||||
|
|
||||||
|
return tmpl;
|
||||||
|
}
|
||||||
|
|
||||||
|
gbt_capabilities_t blktmpl_addcaps(const blktemplate_t *tmpl) {
|
||||||
|
// TODO: make this a lot more flexible for merging
|
||||||
|
// For now, it's a simple "filled" vs "not filled"
|
||||||
|
if (tmpl->version)
|
||||||
|
return 0;
|
||||||
|
return GBT_CBTXN | GBT_WORKID | BMM_CBAPPEND | BMM_CBSET | BMM_TIMEINC | BMM_TIMEDEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void blktxn_free(struct blktxn_t *bt) {
|
||||||
|
free(bt->data);
|
||||||
|
free(bt->hash);
|
||||||
|
free(bt->depends);
|
||||||
|
}
|
||||||
|
|
||||||
|
void blktmpl_free(blktemplate_t *tmpl) {
|
||||||
|
for (int i = 0; i < tmpl->txncount; ++i)
|
||||||
|
blktxn_free(&tmpl->txns[i]);
|
||||||
|
free(tmpl->txns);
|
||||||
|
if (tmpl->cbtxn)
|
||||||
|
{
|
||||||
|
blktxn_free(tmpl->cbtxn);
|
||||||
|
free(tmpl->cbtxn);
|
||||||
|
}
|
||||||
|
// TODO: maybe free auxnames[0..n]? auxdata too
|
||||||
|
free(tmpl->auxnames);
|
||||||
|
free(tmpl->auxdata);
|
||||||
|
free(tmpl->workid);
|
||||||
|
free(tmpl);
|
||||||
|
}
|
||||||
117
blktemplate.h
Normal file
117
blktemplate.h
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#ifndef BLKTEMPLATE_H
|
||||||
|
#define BLKTEMPLATE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
typedef uint32_t blkheight_t;
|
||||||
|
typedef uint32_t libblkmaker_hash_t[8];
|
||||||
|
typedef libblkmaker_hash_t blkhash_t;
|
||||||
|
typedef libblkmaker_hash_t txnhash_t;
|
||||||
|
typedef uint32_t blktime_t;
|
||||||
|
typedef int16_t blktime_diff_t;
|
||||||
|
typedef uint32_t blknonce_t;
|
||||||
|
|
||||||
|
struct blktxn_t {
|
||||||
|
unsigned char *data;
|
||||||
|
size_t datasz;
|
||||||
|
txnhash_t *hash;
|
||||||
|
|
||||||
|
signed long dependcount;
|
||||||
|
unsigned long *depends;
|
||||||
|
|
||||||
|
uint64_t fee;
|
||||||
|
bool required;
|
||||||
|
int16_t sigops;
|
||||||
|
};
|
||||||
|
|
||||||
|
// BIP 23: Long Polling
|
||||||
|
struct blktmpl_longpoll_req {
|
||||||
|
char *id;
|
||||||
|
char *uri;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GBT_CBTXN = 1 << 0,
|
||||||
|
GBT_CBVALUE = 1 << 1,
|
||||||
|
GBT_WORKID = 1 << 2,
|
||||||
|
|
||||||
|
GBT_LONGPOLL = 1 << 3, // BIP 22: Long Polling
|
||||||
|
GBT_PROPOSAL = 1 << 4, // BIP 23: Block Proposal
|
||||||
|
GBT_SERVICE = 1 << 5, // BIP 23: Logical Services
|
||||||
|
|
||||||
|
// BIP 23: Mutations
|
||||||
|
BMM_CBAPPEND = 1 << 0x10,
|
||||||
|
BMM_CBSET = 1 << 0x11,
|
||||||
|
BMM_GENERATE = 1 << 0x12,
|
||||||
|
BMM_TIMEINC = 1 << 0x13,
|
||||||
|
BMM_TIMEDEC = 1 << 0x14,
|
||||||
|
BMM_TXNADD = 1 << 0x15,
|
||||||
|
BMM_PREVBLK = 1 << 0x16,
|
||||||
|
|
||||||
|
// BIP 23: Submission Abbreviation
|
||||||
|
BMA_TXNHASH = 1 << 0x18,
|
||||||
|
BMAb_COINBASE = 1 << 0x19,
|
||||||
|
BMAb_TRUNCATE = 1 << 0x1a,
|
||||||
|
BMAs_COINBASE = 1 << 0x1b,
|
||||||
|
BMAs_MERKLE = 1 << 0x1c,
|
||||||
|
BMAs_TRUNCATE = 1 << 0x1d,
|
||||||
|
} gbt_capabilities_t;
|
||||||
|
#define GBT_CAPABILITY_COUNT (0x1e)
|
||||||
|
|
||||||
|
extern const char *blktmpl_capabilityname(gbt_capabilities_t);
|
||||||
|
#define BLKTMPL_LONGEST_CAPABILITY_NAME (16)
|
||||||
|
|
||||||
|
|
||||||
|
typedef gbt_capabilities_t blkmutations_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t version;
|
||||||
|
unsigned char diffbits[4];
|
||||||
|
blkheight_t height;
|
||||||
|
blkhash_t prevblk;
|
||||||
|
|
||||||
|
unsigned short sigoplimit;
|
||||||
|
unsigned long sizelimit;
|
||||||
|
|
||||||
|
unsigned long txncount;
|
||||||
|
struct blktxn_t *txns;
|
||||||
|
struct blktxn_t *cbtxn;
|
||||||
|
uint64_t cbvalue;
|
||||||
|
|
||||||
|
time_t _time_rcvd;
|
||||||
|
blktime_t curtime;
|
||||||
|
char auxcount;
|
||||||
|
char **auxnames;
|
||||||
|
unsigned char **auxdata;
|
||||||
|
|
||||||
|
char *workid;
|
||||||
|
|
||||||
|
// BIP 22: Long Polling
|
||||||
|
struct blktmpl_longpoll_req lp;
|
||||||
|
bool submitold;
|
||||||
|
|
||||||
|
// BIP 23: Basic Pool Extensions
|
||||||
|
int16_t expires;
|
||||||
|
blkhash_t target;
|
||||||
|
|
||||||
|
// BIP 23: Mutations
|
||||||
|
blkmutations_t mutations;
|
||||||
|
blktime_t maxtime;
|
||||||
|
blktime_diff_t maxtimeoff;
|
||||||
|
blktime_t mintime;
|
||||||
|
blktime_diff_t mintimeoff;
|
||||||
|
blknonce_t minnonce;
|
||||||
|
blknonce_t maxnonce;
|
||||||
|
|
||||||
|
// TEMPORARY HACK
|
||||||
|
libblkmaker_hash_t _mrklroot;
|
||||||
|
} blktemplate_t;
|
||||||
|
|
||||||
|
extern blktemplate_t *blktmpl_create();
|
||||||
|
extern gbt_capabilities_t blktmpl_addcaps(const blktemplate_t *);
|
||||||
|
extern void blktmpl_free(blktemplate_t *);
|
||||||
|
|
||||||
|
#endif
|
||||||
85
example.c
Normal file
85
example.c
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <gcrypt.h>
|
||||||
|
|
||||||
|
#include <blkmaker.h>
|
||||||
|
#include <blkmaker_jansson.h>
|
||||||
|
|
||||||
|
static
|
||||||
|
void send_json(json_t *req) {
|
||||||
|
char *s = json_dumps(req, JSON_INDENT(2));
|
||||||
|
puts(s);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
bool my_sha256(void *digest, const void *buffer, size_t length) {
|
||||||
|
gcry_md_hash_buffer(GCRY_MD_SHA256, digest, buffer, length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char**argv) {
|
||||||
|
blktemplate_t *tmpl;
|
||||||
|
json_t *req;
|
||||||
|
json_error_t jsone;
|
||||||
|
const char *err;
|
||||||
|
|
||||||
|
blkmk_sha256_impl = my_sha256;
|
||||||
|
|
||||||
|
tmpl = blktmpl_create();
|
||||||
|
assert(tmpl);
|
||||||
|
req = blktmpl_request_jansson(blktmpl_addcaps(tmpl));
|
||||||
|
assert(req);
|
||||||
|
|
||||||
|
// send req to server and parse response into req
|
||||||
|
send_json(req);
|
||||||
|
json_decref(req);
|
||||||
|
req = json_loadf(stdin, JSON_DISABLE_EOF_CHECK, &jsone);
|
||||||
|
assert(req);
|
||||||
|
|
||||||
|
err = blktmpl_add_jansson(tmpl, req, time(NULL));
|
||||||
|
json_decref(req);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error adding block template: %s", err);
|
||||||
|
assert(0 && "Error adding block template");
|
||||||
|
}
|
||||||
|
while (blkmk_time_left(tmpl, time(NULL)) && blkmk_work_left(tmpl))
|
||||||
|
{
|
||||||
|
unsigned char data[80], hash[32];
|
||||||
|
size_t datasz;
|
||||||
|
uint32_t nonce;
|
||||||
|
|
||||||
|
datasz = blkmk_get_data(tmpl, data, sizeof(data), time(NULL), NULL);
|
||||||
|
assert(datasz <= sizeof(data));
|
||||||
|
|
||||||
|
// mine the right nonce
|
||||||
|
// this is iterating in native order, even though SHA256 is big endian, because we don't implement noncerange
|
||||||
|
// however, the nonce is always interpreted as big endian, so we need to convert it as if it were big endian
|
||||||
|
for (nonce = 0; nonce < 0xffffffff; ++nonce)
|
||||||
|
{
|
||||||
|
*(uint32_t*)(&data[76]) = nonce;
|
||||||
|
assert(my_sha256(hash, data, 80));
|
||||||
|
assert(my_sha256(hash, hash, 32));
|
||||||
|
if (!*(uint32_t*)(&hash[28]))
|
||||||
|
break;
|
||||||
|
if (!(nonce % 0x1000))
|
||||||
|
{
|
||||||
|
printf("0x%8" PRIx32 " hashes done...\r", nonce);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Found nonce: 0x%8" PRIx32 " \n", nonce);
|
||||||
|
nonce = ntohl(nonce);
|
||||||
|
|
||||||
|
req = blkmk_submit_jansson(tmpl, data, nonce);
|
||||||
|
assert(req);
|
||||||
|
// send req to server
|
||||||
|
send_json(req);
|
||||||
|
}
|
||||||
|
blktmpl_free(tmpl);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user