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