diff --git a/LICENSE b/LICENSE index 733f09f..40bddec 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project All rights reserved. @@ -28,4 +28,4 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Parts of the project are originally copyright (c) 2012-2013 The Cryptonote -developers +developers. diff --git a/Makefile b/Makefile index 2ed9cb6..bb934f4 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,39 @@ +define LICENSE + +Copyright (c) 2014-2019, The Monero Project + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. + +endef + TARGET = monero-pool TYPE = debug @@ -6,7 +42,14 @@ ifeq ($(MAKECMDGOALS),release) TYPE = release endif -MONERO_INC = ${MONERO_ROOT}/src ${MONERO_ROOT}/external/easylogging++ ${MONERO_ROOT}/contrib/epee/include +MONERO_BUILD_ROOT = ${MONERO_ROOT}/build/$(shell echo `uname | \ + sed -e 's|[:/\\ \(\)]|_|g'`/`\ + git -C ${MONERO_ROOT} branch | grep '\* ' | cut -f2- -d' '| \ + sed -e 's|[:/\\ \(\)]|_|g'`)/release + +MONERO_INC = ${MONERO_ROOT}/src \ + ${MONERO_ROOT}/external/easylogging++ \ + ${MONERO_ROOT}/contrib/epee/include MONERO_LIBS = \ ${MONERO_BUILD_ROOT}/src/cryptonote_basic/libcryptonote_basic.a \ @@ -56,9 +99,11 @@ LDPARAM += $(LDFLAGS) LIBS := lmdb pthread microhttpd unbound ifeq ($(OS), Darwin) -LIBS += c++ boost_system-mt boost_date_time-mt boost_chrono-mt boost_filesystem-mt boost_thread-mt boost_regex-mt +LIBS += c++ boost_system-mt boost_date_time-mt boost_chrono-mt \ + boost_filesystem-mt boost_thread-mt boost_regex-mt else -LIBS += dl boost_system boost_date_time boost_chrono boost_filesystem boost_thread boost_regex uuid +LIBS += dl boost_system boost_date_time boost_chrono boost_filesystem \ + boost_thread boost_regex uuid endif PKG_LIBS := $(shell pkg-config \ @@ -105,14 +150,21 @@ SDFILES := $(addprefix $(STORE)/,$(CSOURCE:.S=.d)) $(TARGET): preflight dirs $(OBJECTS) $(COBJECTS) $(SOBJECTS) $(HTMLOBJECTS) @echo Linking $(OBJECTS)... - $(C++) -o $(STORE)/$(TARGET) $(OBJECTS) $(COBJECTS) $(SOBJECTS) $(HTMLOBJECTS) $(LDPARAM) $(MONERO_LIBS) $(foreach LIBRARY, $(LIBS),-l$(LIBRARY)) $(foreach LIB,$(LIBPATH),-L$(LIB)) $(PKG_LIBS) $(STATIC_LIBS) + $(C++) -o $(STORE)/$(TARGET) $(OBJECTS) $(COBJECTS) $(SOBJECTS) $(HTMLOBJECTS) \ + $(LDPARAM) $(MONERO_LIBS) \ + $(foreach LIBRARY, $(LIBS),-l$(LIBRARY)) \ + $(foreach LIB,$(LIBPATH),-L$(LIB)) \ + $(PKG_LIBS) $(STATIC_LIBS) @cp pool.conf $(STORE)/ @cp tools/* $(STORE)/ $(STORE)/%.o: %.cpp @echo Creating object file for $*... - $(C++) -Wp,-MMD,$(STORE)/$*.dd $(CCPARAM) $(CXXFLAGS) $(foreach INC,$(INCPATH),-I$(INC)) $(PKG_INC)\ - $(foreach CPPDEF,$(CPPDEFS),-D$(CPPDEF)) -c $< -o $@ + $(C++) -Wp,-MMD,$(STORE)/$*.dd $(CCPARAM) $(CXXFLAGS) \ + $(foreach INC,$(INCPATH),-I$(INC)) \ + $(PKG_INC) \ + $(foreach CPPDEF,$(CPPDEFS),-D$(CPPDEF)) \ + -c $< -o $@ @sed -e '1s/^\(.*\)$$/$(subst /,\/,$(dir $@))\1/' $(STORE)/$*.dd > $(STORE)/$*.d @rm -f $(STORE)/$*.dd @@ -149,15 +201,13 @@ clean: dirs: @-if [ ! -e $(STORE) ]; then mkdir -p $(STORE); fi; - @-$(foreach DIR,$(DIRS), if [ ! -e $(STORE)/$(DIR) ]; then mkdir -p $(STORE)/$(DIR); fi; ) + @-$(foreach DIR,$(DIRS), \ + if [ ! -e $(STORE)/$(DIR) ]; then mkdir -p $(STORE)/$(DIR); fi; ) preflight: ifeq ($(origin MONERO_ROOT), undefined) $(error You need to set an environment variable MONERO_ROOT to your monero repository root) endif -ifeq ($(origin MONERO_BUILD_ROOT), undefined) - $(error You need to set an environment variable MONERO_BUILD_ROOT to your monero build root) -endif ifndef PKG_LIBS $(error Missing dependencies) endif diff --git a/README.md b/README.md index c762b01..59e55c3 100644 --- a/README.md +++ b/README.md @@ -34,14 +34,13 @@ shout on IRC: jtgrassie on Freenode. The build system now requires the Monero source tree to be cloned and compiled. Follow the [instructions](https://github.com/monero-project/monero#compiling-monero-from-source) -for compiling Monero, then export the following variables: +for compiling Monero, then export the following variable: ```bash export MONERO_ROOT=/path/to/cloned/monero -export MONERO_BUILD_ROOT=$MONERO_ROOT/build/// ``` -Replacing the values appropriately. +Replacing the path appropriately. Beyond the Monero dependencies, the following extra libraries are also required to build the pool: diff --git a/src/bstack.c b/src/bstack.c new file mode 100644 index 0000000..3a624f3 --- /dev/null +++ b/src/bstack.c @@ -0,0 +1,153 @@ +/* +Copyright (c) 2014-2019, The Monero Project + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. +*/ + +#include "bstack.h" +#include +#include +#include + +struct bstack_t +{ + size_t c; + size_t cc; + size_t z; + size_t n; + size_t ni; + recycle_fun rf; + char * b; +}; + +void +bstack_new(bstack_t **out, size_t count, size_t size, recycle_fun recycle) +{ + assert(*out==NULL); + bstack_t *q = (bstack_t*) calloc(1, sizeof(bstack_t)); + q->c = count; + q->cc = 0; + q->z = size; + q->b = (char*) calloc(q->c, q->z); + q->n = 0; + q->ni = 0; + q->rf = recycle; + *out = q; +} + +void +bstack_free(bstack_t *q) +{ + assert(q); + if (q->rf) + { + char *ps = q->b; + char *pe = ps + (q->cc * q->z); + while (ps < pe) + { + q->rf(ps); + ps += q->z; + } + } + free(q->b); + free(q); +} + +void * +bstack_push(bstack_t *q, void *item) +{ + assert(q); + size_t idx = q->n++ % q->c; + void *pb = q->b + (idx * q->z); + if (q->rf && q->cc == q->c) + q->rf(pb); + if (item) + memcpy(pb, item, q->z); + else + memset(pb, 0, q->z); + if (q->cc < q->c) + q->cc++; + q->ni = q->cc; + return pb; +} + +void +bstack_drop(bstack_t *q) +{ + assert(q); + if (!q->cc) + return; + q->n--; + size_t idx = (q->n - q->cc) % q->c; + void *pb = q->b + (idx * q->z); + q->cc--; + if (q->rf) + q->rf(pb); +} + +void * +bstack_peek(bstack_t *q) +{ + assert(q); + if (!q->cc) + return NULL; + size_t idx = (q->n - (q->cc + 1)) % q->c; + if (q->cc < q->c) + idx = q->cc-1; + void *pb = idx ? q->b + (idx * q->z) : q->b; + return pb; +} + +size_t bstack_count(bstack_t *q) +{ + assert(q); + return q->cc; +} + +void * +bstack_next(bstack_t *q) +{ + assert(q); + if (!q->ni) + return NULL; + q->ni--; + size_t idx = (q->n - (q->cc - q->ni)) % q->c; + void *pb = idx ? q->b + (idx * q->z) : q->b; + return pb; +} + +void +bstack_reset(bstack_t *q) +{ + assert(q); + q->ni = q->cc; +} + diff --git a/src/bstack.h b/src/bstack.h new file mode 100644 index 0000000..5231c18 --- /dev/null +++ b/src/bstack.h @@ -0,0 +1,54 @@ +/* +Copyright (c) 2014-2019, The Monero Project + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. +*/ + +/* Bounded Stack */ + +#ifndef BSTACK_H +#define BSTACK_H + +#include + +typedef struct bstack_t bstack_t; +typedef void (*recycle_fun)(void*); + +void bstack_new(bstack_t **out, size_t count, size_t size, recycle_fun recycle); +void bstack_free(bstack_t *q); +void * bstack_push(bstack_t *q, void *item); +void bstack_drop(bstack_t *q); +void * bstack_peek(bstack_t *q); +size_t bstack_count(bstack_t *q); +void * bstack_next(bstack_t *q); +void bstack_reset(bstack_t *q); + +#endif diff --git a/src/pool.c b/src/pool.c index a77acaa..118bc54 100644 --- a/src/pool.c +++ b/src/pool.c @@ -1,33 +1,35 @@ /* - Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - Parts of the project are originally copyright (c) 2012-2013 The Cryptonote developers +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. */ #include @@ -60,6 +62,7 @@ #include #include +#include "bstack.h" #include "util.h" #include "xmr.h" #include "log.h" @@ -176,7 +179,7 @@ typedef struct pool_clients_t size_t count; } pool_clients_t; -typedef struct share_t +typedef struct share_t { uint64_t height; uint64_t difficulty; @@ -184,7 +187,7 @@ typedef struct share_t time_t timestamp; } share_t; -typedef struct block_t +typedef struct block_t { uint64_t height; char hash[64]; @@ -202,66 +205,18 @@ typedef struct payment_t char address[ADDRESS_MAX]; } payment_t; -typedef struct rpc_callback_t +typedef struct rpc_callback_t rpc_callback_t; +typedef void (*rpc_callback_fun)(const char*, rpc_callback_t*); +struct rpc_callback_t { - void (*cb)(const char*, struct rpc_callback_t*); + rpc_callback_fun f; void *data; -} rpc_callback_t; - -static int database_init(); -static void database_close(); -static int store_share(uint64_t height, share_t *share); -static int store_block(uint64_t height, block_t *block); -static int process_blocks(block_t *blocks, size_t count); -static int payout_block(block_t *block, MDB_txn *parent); -static int balance_add(const char *address, uint64_t amount, MDB_txn *parent); -static int send_payments(); -static int startup_pauout(uint64_t height); -static void update_pool_hr(); -static void block_template_free(block_template_t *block_template); -static void block_templates_free(); -static void last_block_headers_free(); -static void pool_clients_init(); -static void pool_clients_free(); -static void pool_clients_send_job(); -static void target_to_hex(uint64_t target, char *target_hex); -static void stratum_get_proxy_job_body(char *body, const client_t *client, - const char *block_hex, bool response); -static void stratum_get_job_body(char *body, const client_t *client, bool response); -static inline void stratum_get_error_body(char *body, int json_id, const char *error); -static inline void stratum_get_status_body(char *body, int json_id, const char *status); -static void client_add(int fd, struct bufferevent *bev); -static void client_find(struct bufferevent *bev, client_t **client); -static void client_clear(struct bufferevent *bev); -static void client_send_job(client_t *client, bool response); -static void client_clear_jobs(client_t *client); -static job_t * client_find_job(client_t *client, const char *job_id); -static void response_to_block_template(json_object *result, block_template_t *block_template); -static void response_to_block(json_object *block_header, block_t *block); -static void rpc_get_request_body(char *body, const char* method, char* fmt, ...); -static void rpc_on_response(struct evhttp_request *req, void *arg); -static void rpc_request(struct event_base *base, const char *body, rpc_callback_t *callback); -static void rpc_wallet_request(struct event_base *base, const char *body, rpc_callback_t *callback); -static void rpc_on_block_template(const char* data, rpc_callback_t *callback); -static void rpc_on_block_headers_range(const char* data, rpc_callback_t *callback); -static void rpc_on_block_header_by_height(const char* data, rpc_callback_t *callback); -static void rpc_on_last_block_header(const char* data, rpc_callback_t *callback); -static void rpc_on_block_submitted(const char* data, rpc_callback_t *callback); -static void rpc_on_wallet_transferred(const char* data, rpc_callback_t *callback); -static void timer_on_120s(int fd, short kind, void *ctx); -static void timer_on_10m(int fd, short kind, void *ctx); -static void client_on_login(json_object *message, client_t *client); -static void client_on_submit(json_object *message, client_t *client); -static void client_on_read(struct bufferevent *bev, void *ctx); -static void client_on_error(struct bufferevent *bev, short error, void *ctx); -static void client_on_accept(evutil_socket_t listener, short event, void *arg); -static void send_validation_error(const client_t *client, const char *message); -static void sigusr1_handler(evutil_socket_t fd, short event, void *arg); +}; static config_t config; static pool_clients_t pool_clients; -static block_t *last_block_headers[BLOCK_HEADERS_MAX]; -static block_template_t *block_templates[BLOCK_TEMPLATES_MAX]; +static bstack_t *bst; +static bstack_t *bsh; static struct event_base *base; static struct event *listener_event; static struct event *timer_120s; @@ -297,6 +252,25 @@ static FILE *fd_log; } \ } +static inline rpc_callback_t * +rpc_callback_new(rpc_callback_fun f, void *data) +{ + rpc_callback_t *c = calloc(1, sizeof(rpc_callback_t)); + c->f = f; + c->data = data; + return c; +} + +static inline void +rpc_callback_free(rpc_callback_t *callback) +{ + if (!callback) + return; + if (callback->data) + free(callback->data); + free(callback); +} + static int compare_uint64(const MDB_val *a, const MDB_val *b) { @@ -320,7 +294,8 @@ compare_block(const MDB_val *a, const MDB_val *b) const block_t *vb = (const block_t*) b->mv_data; int sc = memcmp(va->hash, vb->hash, 64); if (sc == 0) - return (va->timestamp < vb->timestamp) ? -1 : va->timestamp > vb->timestamp; + return (va->timestamp < vb->timestamp) ? -1 : + va->timestamp > vb->timestamp; else return sc; } @@ -332,7 +307,8 @@ compare_share(const MDB_val *a, const MDB_val *b) const share_t *vb = (const share_t*) b->mv_data; int sc = strcmp(va->address, vb->address); if (sc == 0) - return (va->timestamp < vb->timestamp) ? -1 : va->timestamp > vb->timestamp; + return (va->timestamp < vb->timestamp) ? -1 : + va->timestamp > vb->timestamp; else return sc; } @@ -342,11 +318,12 @@ compare_payment(const MDB_val *a, const MDB_val *b) { const payment_t *va = (const payment_t*) a->mv_data; const payment_t *vb = (const payment_t*) b->mv_data; - return (va->timestamp < vb->timestamp) ? -1 : va->timestamp > vb->timestamp; + return (va->timestamp < vb->timestamp) ? -1 : + va->timestamp > vb->timestamp; } static int -database_init() +database_init(void) { int rc; char *err; @@ -367,25 +344,28 @@ database_init() log_fatal("%s\n", err); exit(rc); } - if ((rc = mdb_dbi_open(txn, "shares", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, &db_shares)) != 0) + uint32_t flags = MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED; + if ((rc = mdb_dbi_open(txn, "shares", flags, &db_shares)) != 0) { err = mdb_strerror(rc); log_fatal("%s\n", err); exit(rc); } - if ((rc = mdb_dbi_open(txn, "blocks", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, &db_blocks)) != 0) + if ((rc = mdb_dbi_open(txn, "blocks", flags, &db_blocks)) != 0) { err = mdb_strerror(rc); log_fatal("%s\n", err); exit(rc); } - if ((rc = mdb_dbi_open(txn, "payments", MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, &db_payments)) != 0) + flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED; + if ((rc = mdb_dbi_open(txn, "payments", flags, &db_payments)) != 0) { err = mdb_strerror(rc); log_fatal("%s\n", err); exit(rc); } - if ((rc = mdb_dbi_open(txn, "balance", MDB_CREATE, &db_balance)) != 0) + flags = MDB_CREATE; + if ((rc = mdb_dbi_open(txn, "balance", flags, &db_balance)) != 0) { err = mdb_strerror(rc); log_fatal("%s\n", err); @@ -397,7 +377,7 @@ database_init() mdb_set_compare(txn, db_blocks, compare_uint64); mdb_set_dupsort(txn, db_blocks, compare_block); - + mdb_set_compare(txn, db_payments, compare_string); mdb_set_dupsort(txn, db_payments, compare_payment); @@ -408,7 +388,7 @@ database_init() } static void -database_close() +database_close(void) { log_info("Closing database"); mdb_dbi_close(env, db_shares); @@ -484,7 +464,8 @@ miner_hr(const char *address) uint64_t hr = 0; for (size_t i = 0; i < pool_clients.count; i++, c++) { - if (c->connected_since != 0 && strncmp(c->address, address, ADDRESS_MAX) == 0) + if (c->connected_since != 0 + && strncmp(c->address, address, ADDRESS_MAX) == 0) { double d = difftime(time(NULL), c->connected_since); if (d == 0.0) @@ -542,173 +523,6 @@ cleanup: return balance; } -static int -process_blocks(block_t *blocks, size_t count) -{ - log_debug("Processing blocks"); - /* - For each block, lookup block in db. - If found, make sure found is locked and not orphaned. - If both not orphaned and unlocked, payout, set unlocked. - If block heights differ / orphaned, set orphaned. - */ - int rc; - char *err; - MDB_txn *txn; - MDB_cursor *cursor; - if ((rc = mdb_txn_begin(env, NULL, 0, &txn)) != 0) - { - err = mdb_strerror(rc); - log_error("%s", err); - return rc; - } - if ((rc = mdb_cursor_open(txn, db_blocks, &cursor)) != 0) - { - err = mdb_strerror(rc); - log_error("%s", err); - mdb_txn_abort(txn); - return rc; - } - - for (int i=0; iheight); - MDB_val key = { sizeof(ib->height), (void*)&ib->height }; - MDB_val val; - MDB_cursor_op op = MDB_SET; - while (1) - { - rc = mdb_cursor_get(cursor, &key, &val, op); - op = MDB_NEXT_DUP; - if (rc == MDB_NOTFOUND || rc != 0) - { - log_trace("No stored block at height %"PRIu64, ib->height); - if (rc != MDB_NOTFOUND) - { - err = mdb_strerror(rc); - log_debug("No stored block at height %"PRIu64" with error: %d", ib->height, err); - } - break; - } - block_t *sb = (block_t*)val.mv_data; - if (sb->status != BLOCK_LOCKED) - { - continue; - } - if (ib->status & BLOCK_ORPHANED) - { - log_debug("Orphaned block at height %"PRIu64, ib->height); - block_t bp; - memcpy(&bp, sb, sizeof(block_t)); - bp.status |= BLOCK_ORPHANED; - MDB_val new_val = {sizeof(block_t), (void*)&bp}; - mdb_cursor_put(cursor, &key, &new_val, MDB_CURRENT); - continue; - } - if (memcmp(ib->hash, sb->hash, 64) == 0 && memcmp(ib->prev_hash, sb->prev_hash, 64) != 0) - { - log_warn("Have a block with matching heights but differing parents! Setting orphaned.\n"); - block_t bp; - memcpy(&bp, sb, sizeof(block_t)); - bp.status |= BLOCK_ORPHANED; - MDB_val new_val = {sizeof(block_t), (void*)&bp}; - mdb_cursor_put(cursor, &key, &new_val, MDB_CURRENT); - continue; - } - block_t bp; - memcpy(&bp, sb, sizeof(block_t)); - bp.status |= BLOCK_UNLOCKED; - bp.reward = ib->reward; - rc = payout_block(&bp, txn); - if (rc == 0) - { - log_debug("Paided out block %"PRIu64, bp.height); - MDB_val new_val = {sizeof(block_t), (void*)&bp}; - mdb_cursor_put(cursor, &key, &new_val, MDB_CURRENT); - } - else - log_trace("%s", mdb_strerror(rc)); - } - } - - rc = mdb_txn_commit(txn); - return rc; -} - -static int -payout_block(block_t *block, MDB_txn *parent) -{ - /* - PPLNS - */ - log_info("Payout on block at height %"PRIu64, block->height); - int rc; - char *err; - MDB_txn *txn; - MDB_cursor *cursor; - uint64_t height = block->height; - uint64_t total_paid = 0; - if ((rc = mdb_txn_begin(env, parent, 0, &txn)) != 0) - { - err = mdb_strerror(rc); - log_error("%s", err); - return rc; - } - if ((rc = mdb_cursor_open(txn, db_shares, &cursor)) != 0) - { - err = mdb_strerror(rc); - log_error("%s", err); - mdb_txn_abort(txn); - return rc; - } - - MDB_cursor_op op = MDB_SET; - while (1) - { - uint64_t current_height = height; - MDB_val key = { sizeof(current_height), (void*)¤t_height }; - MDB_val val; - rc = mdb_cursor_get(cursor, &key, &val, op); - op = MDB_NEXT_DUP; - if (rc == MDB_NOTFOUND && total_paid < block->reward) - { - if (height == 0) - break; - height--; - op = MDB_SET; - continue; - } - if (rc != 0 && rc != MDB_NOTFOUND) - { - log_error("Error getting balance: %s", mdb_strerror(rc)); - break; - } - if (total_paid == block->reward) - break; - - share_t *share = (share_t*)val.mv_data; - uint64_t amount = floor((double)share->difficulty / ((double)block->difficulty * config.share_mul) * block->reward); - if (total_paid + amount > block->reward) - amount = block->reward - total_paid; - total_paid += amount; - uint64_t fee = amount * config.pool_fee; - amount -= fee; - if (amount == 0) - continue; - rc = balance_add(share->address, amount, txn); - if (rc != 0) - { - mdb_cursor_close(cursor); - mdb_txn_abort(txn); - return rc; - } - } - - rc = mdb_txn_commit(txn); - return rc; -} - static int balance_add(const char *address, uint64_t amount, MDB_txn *parent) { @@ -766,20 +580,25 @@ balance_add(const char *address, uint64_t amount, MDB_txn *parent) } static int -send_payments() +payout_block(block_t *block, MDB_txn *parent) { - uint64_t threshold = 1000000000000 * config.payment_threshold; + /* + PPLNS + */ + log_info("Payout on block at height %"PRIu64, block->height); int rc; char *err; MDB_txn *txn; MDB_cursor *cursor; - if ((rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)) != 0) + uint64_t height = block->height; + uint64_t total_paid = 0; + if ((rc = mdb_txn_begin(env, parent, 0, &txn)) != 0) { err = mdb_strerror(rc); log_error("%s", err); return rc; } - if ((rc = mdb_cursor_open(txn, db_balance, &cursor)) != 0) + if ((rc = mdb_cursor_open(txn, db_shares, &cursor)) != 0) { err = mdb_strerror(rc); log_error("%s", err); @@ -787,80 +606,747 @@ send_payments() return rc; } - size_t payments_count = 0; - size_t payments_max_count = 25; - size_t payments_size = payments_max_count * sizeof(payment_t); - payment_t *payments = (payment_t*) calloc(1, payments_size); - memset(payments, 0, payments_size); - payment_t *payment = payments; - payment_t *end_payment = payment + payments_max_count; - - MDB_cursor_op op = MDB_FIRST; + MDB_cursor_op op = MDB_SET; while (1) { - MDB_val key; + uint64_t current_height = height; + MDB_val key = { sizeof(current_height), (void*)¤t_height }; MDB_val val; rc = mdb_cursor_get(cursor, &key, &val, op); - op = MDB_NEXT; - if (rc != 0) + op = MDB_NEXT_DUP; + if (rc == MDB_NOTFOUND && total_paid < block->reward) + { + if (height == 0) + break; + height--; + op = MDB_SET; + continue; + } + if (rc != 0 && rc != MDB_NOTFOUND) + { + log_error("Error getting balance: %s", mdb_strerror(rc)); + break; + } + if (total_paid == block->reward) break; - const char *address = (const char*)key.mv_data; - uint64_t amount = *(uint64_t*)val.mv_data; - - if (amount < threshold) + share_t *share = (share_t*)val.mv_data; + uint64_t amount = floor((double)share->difficulty / + ((double)block->difficulty * config.share_mul) * block->reward); + if (total_paid + amount > block->reward) + amount = block->reward - total_paid; + total_paid += amount; + uint64_t fee = amount * config.pool_fee; + amount -= fee; + if (amount == 0) continue; - - log_info("Sending payment of %"PRIu64" to %s\n", amount, address); - - memcpy(payment->address, address, ADDRESS_MAX); - payment->amount = amount; - payments_count++; - - if (++payment == end_payment) + rc = balance_add(share->address, amount, txn); + if (rc != 0) { - payments_size <<= 1; - payments = (payment_t*) realloc(payments, payments_size); - payment = payments + payments_max_count; - memset(payment, 0, sizeof(payment_t) * payments_max_count); - payments_max_count <<= 1; - end_payment = payments + payments_max_count; + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + return rc; } } - mdb_cursor_close(cursor); - mdb_txn_abort(txn); - if (payments_count) + rc = mdb_txn_commit(txn); + return rc; +} + +static int +process_blocks(block_t *blocks, size_t count) +{ + log_debug("Processing blocks"); + /* + For each block, lookup block in db. + If found, make sure found is locked and not orphaned. + If both not orphaned and unlocked, payout, set unlocked. + If block heights differ / orphaned, set orphaned. + */ + int rc; + char *err; + MDB_txn *txn; + MDB_cursor *cursor; + if ((rc = mdb_txn_begin(env, NULL, 0, &txn)) != 0) { - size_t body_size = 160 * payments_count + 128; - char *body = (char*) alloca(body_size); - char *start = body; - char *end = body + body_size; - start = stecpy(start, "{\"id\":\"0\",\"jsonrpc\":\"2.0\",\"method\":\"transfer_split\",\"params\":{" - "\"ring_size\":11,\"destinations\":[", end); - for (size_t i=0; iheight); + MDB_val key = { sizeof(ib->height), (void*)&ib->height }; + MDB_val val; + MDB_cursor_op op = MDB_SET; + while (1) { - payment_t *p = &payments[i]; - start = stecpy(start, "{\"address\":\"", end); - start = stecpy(start, p->address, end); - start = stecpy(start, "\",\"amount\":", end); - sprintf(start, "%"PRIu64"}", p->amount); - start = body + strlen(body); - if (i != payments_count -1) - start = stecpy(start, ",", end); + rc = mdb_cursor_get(cursor, &key, &val, op); + op = MDB_NEXT_DUP; + if (rc == MDB_NOTFOUND || rc != 0) + { + log_trace("No stored block at height %"PRIu64, ib->height); + if (rc != MDB_NOTFOUND) + { + err = mdb_strerror(rc); + log_debug("No stored block at height %"PRIu64 + " with error: %d", + ib->height, err); + } + break; + } + block_t *sb = (block_t*)val.mv_data; + if (sb->status != BLOCK_LOCKED) + { + continue; + } + block_t nb; + memcpy(&nb, sb, sizeof(block_t)); + if (ib->status & BLOCK_ORPHANED) + { + log_debug("Orphaned block at height %"PRIu64, ib->height); + nb.status |= BLOCK_ORPHANED; + MDB_val new_val = {sizeof(block_t), (void*)&nb}; + mdb_cursor_put(cursor, &key, &new_val, MDB_CURRENT); + continue; + } + if (memcmp(ib->hash, sb->hash, 64) == 0 + && memcmp(ib->prev_hash, sb->prev_hash, 64) != 0) + { + log_warn("Block with matching heights but differing parents! " + "Setting orphaned.\n"); + nb.status |= BLOCK_ORPHANED; + MDB_val new_val = {sizeof(block_t), (void*)&nb}; + mdb_cursor_put(cursor, &key, &new_val, MDB_CURRENT); + continue; + } + nb.status |= BLOCK_UNLOCKED; + nb.reward = ib->reward; + rc = payout_block(&nb, txn); + if (rc == 0) + { + log_debug("Paided out block %"PRIu64, nb.height); + MDB_val new_val = {sizeof(block_t), (void*)&nb}; + mdb_cursor_put(cursor, &key, &new_val, MDB_CURRENT); + } else - start = stecpy(start, "]}}", end); + log_trace("%s", mdb_strerror(rc)); } - log_trace(body); - rpc_callback_t *callback = calloc(1, sizeof(rpc_callback_t)); - callback->data = payments; - callback->cb = rpc_on_wallet_transferred; - rpc_wallet_request(base, body, callback); + } + + rc = mdb_txn_commit(txn); + return rc; +} + +static void +update_pool_hr(void) +{ + uint64_t hr = 0; + client_t *c = pool_clients.clients; + for (size_t i = 0; i < pool_clients.count; i++, c++) + { + if (c->connected_since != 0) + { + double d = difftime(time(NULL), c->connected_since); + if (d == 0.0) + continue; + hr += c->hashes / d; + } + } + pool_stats.pool_hashrate = hr; +} + +static void +template_recycle(void *item) +{ + block_template_t *bt = (block_template_t*) item; + log_trace("Recycle block template at height: %"PRIu64, bt->height); + if (bt->blockhashing_blob) + { + free(bt->blockhashing_blob); + bt->blockhashing_blob = NULL; + } + if (bt->blocktemplate_blob) + { + free(bt->blocktemplate_blob); + bt->blocktemplate_blob = NULL; + } +} + +static void +target_to_hex(uint64_t target, char *target_hex) +{ + if (target & 0xFFFFFFFF00000000) + { + log_debug("High target requested: %"PRIu64, target); + bin_to_hex((const char*)&target, 8, &target_hex[0], 16); + target_hex[16] = '\0'; + return; + } + BIGNUM *diff = BN_new(); + BIGNUM *bnt = BN_new(); +#ifdef SIXTY_FOUR_BIT_LONG + BN_set_word(bnt, target); +#else + char st[24]; + snprintf(st, 24, "%"PRIu64, target); + BN_dec2bn(&bnt, st); +#endif + BN_div(diff, NULL, base_diff, bnt, bn_ctx); + BN_rshift(diff, diff, 224); + uint32_t w = BN_get_word(diff); + bin_to_hex((const char*)&w, 4, &target_hex[0], 8); + target_hex[8] = '\0'; + BN_free(bnt); + BN_free(diff); +} + +static void +stratum_get_proxy_job_body(char *body, const client_t *client, + const char *block_hex, bool response) +{ + int json_id = client->json_id; + const char *client_id = client->client_id; + const job_t *job = &client->active_jobs[0]; + char job_id[33]; + bin_to_hex((const char*)job->id, sizeof(uuid_t), job_id, 32); + uint64_t target = job->target; + char target_hex[17]; + target_to_hex(target, &target_hex[0]); + const block_template_t *bt = job->block_template; + + if (response) + { + snprintf(body, JOB_BODY_MAX, "{\"id\":%d,\"jsonrpc\":\"2.0\"," + "\"error\":null,\"result\"" + ":{\"id\":\"%.32s\",\"job\":{\"blocktemplate_blob\":\"%s\"," + "\"job_id\":\"%.32s\"," + "\"difficulty\":%"PRIu64",\"height\":%"PRIu64"," + "\"reserved_offset\":%u," + "\"client_nonce_offset\":%u,\"client_pool_offset\":%u," + "\"target_diff\":%"PRIu64",\"target_diff_hex\":\"%s\"}," + "\"status\":\"OK\"}}\n", + json_id, client_id, block_hex, job_id, + bt->difficulty, bt->height, bt->reserved_offset, + bt->reserved_offset + 12, + bt->reserved_offset + 8, target, target_hex); } else - free(payments); + { + snprintf(body, JOB_BODY_MAX, "{\"jsonrpc\":\"2.0\",\"method\":" + "\"job\",\"params\"" + ":{\"id\":\"%.32s\",\"job\":{\"blocktemplate_blob\":\"%s\"," + "\"job_id\":\"%.32s\"," + "\"difficulty\":%"PRIu64",\"height\":%"PRIu64"," + "\"reserved_offset\":%u," + "\"client_nonce_offset\":%u,\"client_pool_offset\":%u," + "\"target_diff\":%"PRIu64",\"target_diff_hex\":\"%s\"}," + "\"status\":\"OK\"}}\n", client_id, block_hex, job_id, + bt->difficulty, bt->height, + bt->reserved_offset, bt->reserved_offset + 12, + bt->reserved_offset + 8, target, target_hex); + } +} - return 0; +static void +stratum_get_job_body(char *body, const client_t *client, bool response) +{ + int json_id = client->json_id; + const char *client_id = client->client_id; + const job_t *job = &client->active_jobs[0]; + char job_id[33]; + bin_to_hex((const char*)job->id, sizeof(uuid_t), job_id, 32); + const char *blob = job->blob; + uint64_t target = job->target; + uint64_t height = job->block_template->height; + char target_hex[17]; + target_to_hex(target, &target_hex[0]); + + if (response) + { + snprintf(body, JOB_BODY_MAX, "{\"id\":%d,\"jsonrpc\":\"2.0\"," + "\"error\":null,\"result\"" + ":{\"id\":\"%.32s\",\"job\":{" + "\"blob\":\"%s\",\"job_id\":\"%.32s\",\"target\":\"%s\"," + "\"height\":%"PRIu64"}," + "\"status\":\"OK\"}}\n", + json_id, client_id, blob, job_id, target_hex, height); + } + else + { + snprintf(body, JOB_BODY_MAX, "{\"jsonrpc\":\"2.0\",\"method\":" + "\"job\",\"params\"" + ":{\"id\":\"%.32s\",\"blob\":\"%s\",\"job_id\":\"%.32s\"," + "\"target\":\"%s\"," + "\"height\":%"PRIu64"}}\n", + client_id, blob, job_id, target_hex, height); + } +} + +static inline void +stratum_get_error_body(char *body, int json_id, const char *error) +{ + snprintf(body, ERROR_BODY_MAX, "{\"id\":%d,\"jsonrpc\":\"2.0\"," + "\"error\":" + "{\"code\":-1, \"message\":\"%s\"}}\n", json_id, error); +} + +static inline void +stratum_get_status_body(char *body, int json_id, const char *status) +{ + snprintf(body, STATUS_BODY_MAX, "{\"id\":%d,\"jsonrpc\":\"2.0\"," + "\"error\":null,\"result\":{\"status\":\"%s\"}}\n", + json_id, status); +} + +static void +send_validation_error(const client_t *client, const char *message) +{ + struct evbuffer *output = bufferevent_get_output(client->bev); + char body[ERROR_BODY_MAX]; + stratum_get_error_body(body, client->json_id, message); + evbuffer_add(output, body, strlen(body)); + log_debug("Validation error: %s", message); +} + +static void +client_clear_jobs(client_t *client) +{ + for (size_t i=0; iactive_jobs[i]; + if (job->blob != NULL) + { + free(job->blob); + job->blob = NULL; + } + if (job->submissions != NULL) + { + free(job->submissions); + job->submissions = NULL; + job->submissions_count = 0; + } + } +} + +static job_t * +client_find_job(client_t *client, const char *job_id) +{ + uuid_t jid; + hex_to_bin(job_id, (char*)&jid, sizeof(uuid_t)); + for (size_t i=0; iactive_jobs[i]; + if (memcmp(job->id, jid, sizeof(uuid_t)) == 0) + return job; + } + return NULL; +} + +static void +client_send_job(client_t *client, bool response) +{ + /* First cycle jobs */ + job_t *last = &client->active_jobs[CLIENT_JOBS_MAX-1]; + if (last->blob != NULL) + { + free(last->blob); + last->blob = NULL; + } + if (last->submissions != NULL) + { + free(last->submissions); + last->submissions = NULL; + last->submissions_count = 0; + } + for (size_t i=CLIENT_JOBS_MAX-1; i>0; i--) + { + job_t *current = &client->active_jobs[i]; + job_t *prev = &client->active_jobs[i-1]; + memcpy(current, prev, sizeof(job_t)); + } + job_t *job = &client->active_jobs[0]; + memset(job, 0, sizeof(job_t)); + + /* Quick check we actually have a block template */ + block_template_t *bt = bstack_peek(bst); + if (!bt) + { + log_warn("Cannot send client a job: No block template"); + return; + } + + /* + 1. Convert blocktemplate_blob to binary + 2. Update bytes in reserved space at reserved_offset + 3. Get block hashing blob for job + 4. Send + */ + + /* Convert template to blob */ + size_t bin_size = strlen(bt->blocktemplate_blob) >> 1; + char *block = calloc(bin_size, sizeof(char)); + hex_to_bin(bt->blocktemplate_blob, block, bin_size); + + /* Set the extra nonce in our reserved space */ + char *p = block; + p += bt->reserved_offset; + ++extra_nonce; + memcpy(p, &extra_nonce, sizeof(extra_nonce)); + job->extra_nonce = extra_nonce; + + /* Get hashong blob */ + size_t hashing_blob_size; + char *hashing_blob = NULL; + get_hashing_blob(block, bin_size, &hashing_blob, &hashing_blob_size); + + /* Make hex */ + job->blob = calloc((hashing_blob_size << 1) +1, sizeof(char)); + bin_to_hex(hashing_blob, hashing_blob_size, job->blob, + hashing_blob_size << 1); + log_trace("Miner hashing blob: %s", job->blob); + + /* Save a job id */ + uuid_generate(job->id); + + /* Hold reference to block template */ + job->block_template = bt; + + /* Send */ + char job_id[33]; + bin_to_hex((const char*)job->id, sizeof(uuid_t), job_id, 32); + + /* Retarget */ + double duration = difftime(time(NULL), client->connected_since); + uint8_t retarget_time = client->is_proxy ? 5 : 120; + uint64_t target = fmax((double)client->hashes / + duration * retarget_time, config.pool_start_diff); + job->target = target; + log_debug("Client %.32s target now %"PRIu64, client->client_id, target); + + char body[JOB_BODY_MAX]; + if (!client->is_proxy) + { + stratum_get_job_body(body, client, response); + } + else + { + char *block_hex = calloc(bin_size+1, sizeof(char)); + bin_to_hex(block, bin_size, block_hex, bin_size << 1); + stratum_get_proxy_job_body(body, client, block_hex, response); + free(block_hex); + } + log_trace("Client job: %s", body); + struct evbuffer *output = bufferevent_get_output(client->bev); + evbuffer_add(output, body, strlen(body)); + free(block); + free(hashing_blob); +} + +static void +pool_clients_send_job(void) +{ + client_t *c = pool_clients.clients; + for (size_t i = 0; i < pool_clients.count; i++, c++) + { + if (c->fd == 0) + continue; + client_send_job(c, false); + } +} + +static void +pool_clients_init(void) +{ + assert(pool_clients.count == 0); + pool_clients.count = POOL_CLIENTS_GROW; + pool_clients.clients = (client_t*) calloc(pool_clients.count, + sizeof(client_t)); +} + +static void +pool_clients_free(void) +{ + assert(pool_clients.count != 0); + client_t *c = pool_clients.clients; + for (size_t i = 0; i < pool_clients.count; i++, c++) + { + client_clear_jobs(c); + } + free(pool_clients.clients); +} + +static void +response_to_block_template(json_object *result, + block_template_t *block_template) +{ + JSON_GET_OR_WARN(blockhashing_blob, result, json_type_string); + JSON_GET_OR_WARN(blocktemplate_blob, result, json_type_string); + JSON_GET_OR_WARN(difficulty, result, json_type_int); + JSON_GET_OR_WARN(height, result, json_type_int); + JSON_GET_OR_WARN(prev_hash, result, json_type_string); + JSON_GET_OR_WARN(reserved_offset, result, json_type_int); + block_template->blockhashing_blob = strdup( + json_object_get_string(blockhashing_blob)); + block_template->blocktemplate_blob = strdup( + json_object_get_string(blocktemplate_blob)); + block_template->difficulty = json_object_get_int64(difficulty); + block_template->height = json_object_get_int64(height); + memcpy(block_template->prev_hash, json_object_get_string(prev_hash), 64); + block_template->reserved_offset = json_object_get_int(reserved_offset); +} + +static void +response_to_block(json_object *block_header, block_t *block) +{ + memset(block, 0, sizeof(block_t)); + JSON_GET_OR_WARN(height, block_header, json_type_int); + JSON_GET_OR_WARN(difficulty, block_header, json_type_int); + JSON_GET_OR_WARN(hash, block_header, json_type_string); + JSON_GET_OR_WARN(prev_hash, block_header, json_type_string); + JSON_GET_OR_WARN(timestamp, block_header, json_type_int); + JSON_GET_OR_WARN(reward, block_header, json_type_int); + JSON_GET_OR_WARN(orphan_status, block_header, json_type_boolean); + block->height = json_object_get_int64(height); + block->difficulty = json_object_get_int64(difficulty); + memcpy(block->hash, json_object_get_string(hash), 64); + memcpy(block->prev_hash, json_object_get_string(prev_hash), 64); + block->timestamp = json_object_get_int64(timestamp); + block->reward = json_object_get_int64(reward); + if (json_object_get_int(orphan_status)) + block->status |= BLOCK_ORPHANED; +} + +static void +rpc_on_response(struct evhttp_request *req, void *arg) +{ + struct evbuffer *input; + rpc_callback_t *callback = (rpc_callback_t*) arg; + + if (!req) + { + log_error("Request failure. Aborting."); + rpc_callback_free(callback); + return; + } + + int rc = evhttp_request_get_response_code(req); + if (rc < 200 || rc >= 300) + { + log_error("HTTP status code %d for %s. Aborting.", + rc, evhttp_request_get_uri(req)); + rpc_callback_free(callback); + return; + } + + input = evhttp_request_get_input_buffer(req); + size_t len = evbuffer_get_length(input); + char body[len+1]; + evbuffer_remove(input, body, len); + body[len] = '\0'; + callback->f(body, callback); + rpc_callback_free(callback); +} + +static void +rpc_request(struct event_base *base, const char *body, rpc_callback_t *callback) +{ + struct evhttp_connection *con; + struct evhttp_request *req; + struct evkeyvalq *headers; + struct evbuffer *output; + + con = evhttp_connection_base_new(base, NULL, + config.rpc_host, config.rpc_port); + evhttp_connection_free_on_completion(con); + evhttp_connection_set_timeout(con, config.rpc_timeout); + req = evhttp_request_new(rpc_on_response, callback); + output = evhttp_request_get_output_buffer(req); + evbuffer_add(output, body, strlen(body)); + headers = evhttp_request_get_output_headers(req); + evhttp_add_header(headers, "Content-Type", "application/json"); + evhttp_add_header(headers, "Connection", "close"); + evhttp_make_request(con, req, EVHTTP_REQ_POST, RPC_PATH); +} + +static void +rpc_wallet_request(struct event_base *base, const char *body, + rpc_callback_t *callback) +{ + struct evhttp_connection *con; + struct evhttp_request *req; + struct evkeyvalq *headers; + struct evbuffer *output; + + con = evhttp_connection_base_new(base, NULL, + config.wallet_rpc_host, config.wallet_rpc_port); + evhttp_connection_free_on_completion(con); + evhttp_connection_set_timeout(con, config.rpc_timeout); + req = evhttp_request_new(rpc_on_response, callback); + output = evhttp_request_get_output_buffer(req); + evbuffer_add(output, body, strlen(body)); + headers = evhttp_request_get_output_headers(req); + evhttp_add_header(headers, "Content-Type", "application/json"); + evhttp_add_header(headers, "Connection", "close"); + evhttp_make_request(con, req, EVHTTP_REQ_POST, RPC_PATH); +} + +static void +rpc_get_request_body(char *body, const char* method, char* fmt, ...) +{ + char *pb = body; + + snprintf(pb, RPC_BODY_MAX, "%s%s%s", "{\"jsonrpc\":\"2.0\",\"id\":\"0\"," + "\"method\":\"", method, "\""); + pb += strlen(pb); + + if (fmt && *fmt) + { + char *s; + uint64_t d; + snprintf(pb, RPC_BODY_MAX - strlen(body), "%s", ",\"params\":{"); + pb += strlen(pb); + va_list args; + va_start(args, fmt); + uint8_t count = 0; + while (*fmt) + { + switch (*fmt++) + { + case 's': + s = va_arg(args, char *); + snprintf(pb, RPC_BODY_MAX - strlen(body), "\"%s\"", s); + pb += strlen(pb); + break; + case 'd': + d = va_arg(args, uint64_t); + snprintf(pb, RPC_BODY_MAX - strlen(body), "%"PRIu64, d); + pb += strlen(pb); + break; + } + *pb++ = count++ % 2 ? ',' : ':'; + } + va_end(args); + *--pb = '}'; + pb++; + } + *pb++ = '}'; + *pb = '\0'; + log_trace("Payload: %s", body); +} + +static void +rpc_on_block_header_by_height(const char* data, rpc_callback_t *callback) +{ + log_trace("Got block header by height: \n%s", data); + json_object *root = json_tokener_parse(data); + JSON_GET_OR_WARN(result, root, json_type_object); + JSON_GET_OR_WARN(status, result, json_type_string); + const char *ss = json_object_get_string(status); + json_object *error = NULL; + json_object_object_get_ex(root, "error", &error); + if (error != NULL) + { + JSON_GET_OR_WARN(code, error, json_type_object); + JSON_GET_OR_WARN(message, error, json_type_string); + int ec = json_object_get_int(code); + const char *em = json_object_get_string(message); + log_error("Error (%d) getting block header by height: %s", ec, em); + json_object_put(root); + return; + } + if (status == NULL || strcmp(ss, "OK") != 0) + { + log_error("Error getting block header by height: %s", ss); + json_object_put(root); + return; + } + block_t rb; + JSON_GET_OR_WARN(block_header, result, json_type_object); + response_to_block(block_header, &rb); + process_blocks(&rb, 1); + json_object_put(root); +} + +static void +rpc_on_block_headers_range(const char* data, rpc_callback_t *callback) +{ + json_object *root = json_tokener_parse(data); + JSON_GET_OR_WARN(result, root, json_type_object); + JSON_GET_OR_WARN(status, result, json_type_string); + const char *ss = json_object_get_string(status); + json_object *error = NULL; + json_object_object_get_ex(root, "error", &error); + if (error != NULL) + { + JSON_GET_OR_WARN(code, error, json_type_object); + JSON_GET_OR_WARN(message, error, json_type_string); + int ec = json_object_get_int(code); + const char *em = json_object_get_string(message); + log_warn("Error (%d) getting block headers by range: %s", ec, em); + json_object_put(root); + return; + } + if (status == NULL || strcmp(ss, "OK") != 0) + { + log_warn("Error getting block headers by range: %s", ss); + json_object_put(root); + return; + } + + JSON_GET_OR_WARN(headers, result, json_type_array); + size_t headers_len = json_object_array_length(headers); + assert(headers_len == BLOCK_HEADERS_RANGE); + for (int i=0; iheight > height - 60) continue; - if (block->status & BLOCK_UNLOCKED || block->status & BLOCK_ORPHANED) + if (block->status != BLOCK_LOCKED) continue; char body[RPC_BODY_MAX]; - rpc_get_request_body(body, "get_block_header_by_height", "sd", "height", block->height); - rpc_callback_t *c = calloc(1, sizeof(rpc_callback_t)); - c->cb = rpc_on_block_header_by_height; - rpc_request(base, body, c); + rpc_get_request_body(body, "get_block_header_by_height", "sd", + "height", block->height); + rpc_callback_t *cb = rpc_callback_new( + rpc_on_block_header_by_height, NULL); + rpc_request(base, body, cb); } mdb_cursor_close(cursor); @@ -927,486 +1414,6 @@ startup_pauout(uint64_t height) return 0; } -static void -update_pool_hr() -{ - uint64_t hr = 0; - client_t *c = pool_clients.clients; - for (size_t i = 0; i < pool_clients.count; i++, c++) - { - if (c->connected_since != 0) - { - double d = difftime(time(NULL), c->connected_since); - if (d == 0.0) - continue; - hr += c->hashes / d; - } - } - pool_stats.pool_hashrate = hr; -} - -static void -block_template_free(block_template_t *block_template) -{ - free(block_template->blockhashing_blob); - free(block_template->blocktemplate_blob); - free(block_template); -} - -static void -block_templates_free() -{ - for (size_t i=0; iblockhashing_blob); - free(bt->blocktemplate_blob); - free(bt); - } - } -} - -static void -last_block_headers_free() -{ - for (size_t i=0; iactive_jobs[i]; - if (memcmp(job->id, jid, sizeof(uuid_t)) == 0) - return job; - } - return NULL; -} - -static void -pool_clients_send_job() -{ - client_t *c = pool_clients.clients; - for (size_t i = 0; i < pool_clients.count; i++, c++) - { - if (c->fd == 0) - continue; - client_send_job(c, false); - } -} - -static void -target_to_hex(uint64_t target, char *target_hex) -{ - if (target & 0xFFFFFFFF00000000) - { - log_debug("High target requested: %"PRIu64, target); - bin_to_hex((const char*)&target, 8, &target_hex[0], 16); - target_hex[16] = '\0'; - return; - } - BIGNUM *diff = BN_new(); - BIGNUM *bnt = BN_new(); -#ifdef SIXTY_FOUR_BIT_LONG - BN_set_word(bnt, target); -#else - char st[24]; - snprintf(st, 24, "%"PRIu64, target); - BN_dec2bn(&bnt, st); -#endif - BN_div(diff, NULL, base_diff, bnt, bn_ctx); - BN_rshift(diff, diff, 224); - uint32_t w = BN_get_word(diff); - bin_to_hex((const char*)&w, 4, &target_hex[0], 8); - target_hex[8] = '\0'; - BN_free(bnt); - BN_free(diff); -} - -static void -stratum_get_proxy_job_body(char *body, const client_t *client, const char *block_hex, bool response) -{ - int json_id = client->json_id; - const char *client_id = client->client_id; - const job_t *job = &client->active_jobs[0]; - char job_id[33]; - bin_to_hex((const char*)job->id, sizeof(uuid_t), job_id, 32); - uint64_t target = job->target; - char target_hex[17]; - target_to_hex(target, &target_hex[0]); - const block_template_t *bt = job->block_template; - - if (response) - { - snprintf(body, JOB_BODY_MAX, "{\"id\":%d,\"jsonrpc\":\"2.0\",\"error\":null,\"result\"" - ":{\"id\":\"%.32s\",\"job\":{\"blocktemplate_blob\":\"%s\",\"job_id\":\"%.32s\"," - "\"difficulty\":%"PRIu64",\"height\":%"PRIu64",\"reserved_offset\":%u,\"client_nonce_offset\":%u," - "\"client_pool_offset\":%u,\"target_diff\":%"PRIu64",\"target_diff_hex\":\"%s\"}," - "\"status\":\"OK\"}}\n", json_id, client_id, block_hex, job_id, - bt->difficulty, bt->height, bt->reserved_offset, bt->reserved_offset + 12, - bt->reserved_offset + 8, target, target_hex); - } - else - { - snprintf(body, JOB_BODY_MAX, "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\"" - ":{\"id\":\"%.32s\",\"job\":{\"blocktemplate_blob\":\"%s\",\"job_id\":\"%.32s\"," - "\"difficulty\":%"PRIu64",\"height\":%"PRIu64",\"reserved_offset\":%u,\"client_nonce_offset\":%u," - "\"client_pool_offset\":%u,\"target_diff\":%"PRIu64",\"target_diff_hex\":\"%s\"}," - "\"status\":\"OK\"}}\n", client_id, block_hex, job_id, - bt->difficulty, bt->height, bt->reserved_offset, bt->reserved_offset + 12, - bt->reserved_offset + 8, target, target_hex); - } -} - -static void -stratum_get_job_body(char *body, const client_t *client, bool response) -{ - int json_id = client->json_id; - const char *client_id = client->client_id; - const job_t *job = &client->active_jobs[0]; - char job_id[33]; - bin_to_hex((const char*)job->id, sizeof(uuid_t), job_id, 32); - const char *blob = job->blob; - uint64_t target = job->target; - uint64_t height = job->block_template->height; - char target_hex[17]; - target_to_hex(target, &target_hex[0]); - - if (response) - { - snprintf(body, JOB_BODY_MAX, "{\"id\":%d,\"jsonrpc\":\"2.0\",\"error\":null,\"result\"" - ":{\"id\":\"%.32s\",\"job\":{" - "\"blob\":\"%s\",\"job_id\":\"%.32s\",\"target\":\"%s\",\"height\":%"PRIu64"}," - "\"status\":\"OK\"}}\n", json_id, client_id, blob, job_id, target_hex, height); - } - else - { - snprintf(body, JOB_BODY_MAX, "{\"jsonrpc\":\"2.0\",\"method\":\"job\",\"params\"" - ":{\"id\":\"%.32s\",\"blob\":\"%s\",\"job_id\":\"%.32s\",\"target\":\"%s\"," - "\"height\":%"PRIu64"}}\n", - client_id, blob, job_id, target_hex, height); - } -} - -static inline void -stratum_get_error_body(char *body, int json_id, const char *error) -{ - snprintf(body, ERROR_BODY_MAX, "{\"id\":%d,\"jsonrpc\":\"2.0\",\"error\":" - "{\"code\":-1, \"message\":\"%s\"}}\n", json_id, error); -} - -static inline void -stratum_get_status_body(char *body, int json_id, const char *status) -{ - snprintf(body, STATUS_BODY_MAX, "{\"id\":%d,\"jsonrpc\":\"2.0\",\"error\":null,\"result\":{\"status\":\"%s\"}}\n", - json_id, status); -} - -static void -response_to_block_template(json_object *result, block_template_t *block_template) -{ - JSON_GET_OR_WARN(blockhashing_blob, result, json_type_string); - JSON_GET_OR_WARN(blocktemplate_blob, result, json_type_string); - JSON_GET_OR_WARN(difficulty, result, json_type_int); - JSON_GET_OR_WARN(height, result, json_type_int); - JSON_GET_OR_WARN(prev_hash, result, json_type_string); - JSON_GET_OR_WARN(reserved_offset, result, json_type_int); - block_template->blockhashing_blob = strdup(json_object_get_string(blockhashing_blob)); - block_template->blocktemplate_blob = strdup(json_object_get_string(blocktemplate_blob)); - block_template->difficulty = json_object_get_int64(difficulty); - block_template->height = json_object_get_int64(height); - memcpy(block_template->prev_hash, json_object_get_string(prev_hash), 64); - block_template->reserved_offset = json_object_get_int(reserved_offset); -} - -static void -response_to_block(json_object *block_header, block_t *block) -{ - memset(block, 0, sizeof(block_t)); - JSON_GET_OR_WARN(height, block_header, json_type_int); - JSON_GET_OR_WARN(difficulty, block_header, json_type_int); - JSON_GET_OR_WARN(hash, block_header, json_type_string); - JSON_GET_OR_WARN(prev_hash, block_header, json_type_string); - JSON_GET_OR_WARN(timestamp, block_header, json_type_int); - JSON_GET_OR_WARN(reward, block_header, json_type_int); - JSON_GET_OR_WARN(orphan_status, block_header, json_type_boolean); - block->height = json_object_get_int64(height); - block->difficulty = json_object_get_int64(difficulty); - memcpy(block->hash, json_object_get_string(hash), 64); - memcpy(block->prev_hash, json_object_get_string(prev_hash), 64); - block->timestamp = json_object_get_int64(timestamp); - block->reward = json_object_get_int64(reward); - if (json_object_get_int(orphan_status)) - block->status |= BLOCK_ORPHANED; -} - -static void -rpc_get_request_body(char *body, const char* method, char* fmt, ...) -{ - char *pb = body; - - snprintf(pb, RPC_BODY_MAX, "%s%s%s", "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"", method, "\""); - pb += strlen(pb); - - if (fmt && *fmt) - { - char *s; - uint64_t d; - snprintf(pb, RPC_BODY_MAX - strlen(body), "%s", ",\"params\":{"); - pb += strlen(pb); - va_list args; - va_start(args, fmt); - uint8_t count = 0; - while (*fmt) - { - switch (*fmt++) - { - case 's': - s = va_arg(args, char *); - snprintf(pb, RPC_BODY_MAX - strlen(body), "\"%s\"", s); - pb += strlen(pb); - break; - case 'd': - d = va_arg(args, uint64_t); - snprintf(pb, RPC_BODY_MAX - strlen(body), "%"PRIu64, d); - pb += strlen(pb); - break; - } - *pb++ = count++ % 2 ? ',' : ':'; - } - va_end(args); - *--pb = '}'; - pb++; - } - *pb++ = '}'; - *pb = '\0'; - log_trace("Payload: %s", body); -} - -static void -rpc_on_response(struct evhttp_request *req, void *arg) -{ - struct evbuffer *input; - rpc_callback_t *callback = (rpc_callback_t*) arg; - - if (!req) - { - log_error("Request failure. Aborting."); - goto cleanup; - } - - int rc = evhttp_request_get_response_code(req); - if (rc < 200 || rc >= 300) - { - log_error("HTTP status code %d for %s. Aborting.", rc, evhttp_request_get_uri(req)); - goto cleanup; - } - - input = evhttp_request_get_input_buffer(req); - size_t len = evbuffer_get_length(input); - char *data = (char*) alloca(len+1); - evbuffer_remove(input, data, len); - data[len] = '\0'; - callback->cb(data, callback); -cleanup: - if (callback->data) - free(callback->data); - free(callback); -} - -static void -rpc_request(struct event_base *base, const char *body, rpc_callback_t *callback) -{ - struct evhttp_connection *con; - struct evhttp_request *req; - struct evkeyvalq *headers; - struct evbuffer *output; - - con = evhttp_connection_base_new(base, NULL, config.rpc_host, config.rpc_port); - evhttp_connection_free_on_completion(con); - evhttp_connection_set_timeout(con, config.rpc_timeout); - req = evhttp_request_new(rpc_on_response, callback); - output = evhttp_request_get_output_buffer(req); - evbuffer_add(output, body, strlen(body)); - headers = evhttp_request_get_output_headers(req); - evhttp_add_header(headers, "Content-Type", "application/json"); - evhttp_add_header(headers, "Connection", "close"); - evhttp_make_request(con, req, EVHTTP_REQ_POST, RPC_PATH); -} - -static void -rpc_wallet_request(struct event_base *base, const char *body, rpc_callback_t *callback) -{ - struct evhttp_connection *con; - struct evhttp_request *req; - struct evkeyvalq *headers; - struct evbuffer *output; - - con = evhttp_connection_base_new(base, NULL, config.wallet_rpc_host, config.wallet_rpc_port); - evhttp_connection_free_on_completion(con); - evhttp_connection_set_timeout(con, config.rpc_timeout); - req = evhttp_request_new(rpc_on_response, callback); - output = evhttp_request_get_output_buffer(req); - evbuffer_add(output, body, strlen(body)); - headers = evhttp_request_get_output_headers(req); - evhttp_add_header(headers, "Content-Type", "application/json"); - evhttp_add_header(headers, "Connection", "close"); - evhttp_make_request(con, req, EVHTTP_REQ_POST, RPC_PATH); -} - -static void -rpc_on_block_headers_range(const char* data, rpc_callback_t *callback) -{ - json_object *root = json_tokener_parse(data); - JSON_GET_OR_WARN(result, root, json_type_object); - JSON_GET_OR_WARN(status, result, json_type_string); - const char *ss = json_object_get_string(status); - json_object *error = NULL; - json_object_object_get_ex(root, "error", &error); - if (error != NULL) - { - JSON_GET_OR_WARN(code, error, json_type_object); - JSON_GET_OR_WARN(message, error, json_type_string); - int ec = json_object_get_int(code); - const char *em = json_object_get_string(message); - log_warn("Error (%d) getting block headers by range: %s", ec, em); - json_object_put(root); - return; - } - if (status == NULL || strcmp(ss, "OK") != 0) - { - log_warn("Error getting block headers by range: %s", ss); - json_object_put(root); - return; - } - - JSON_GET_OR_WARN(headers, result, json_type_array); - size_t headers_len = json_object_array_length(headers); - assert(headers_len == BLOCK_HEADERS_RANGE); - for (int i=0; iheight); - } + JSON_GET_OR_WARN(height, block_header, json_type_int); + uint64_t bh = json_object_get_int64(height); bool need_new_template = false; - if (front != NULL && block->height > front->height) + block_t *front = bstack_peek(bsh); + if (front != NULL && bh > front->height) { - size_t i = BLOCK_HEADERS_MAX; - while (--i) - { - if (i == BLOCK_HEADERS_MAX - 1 && last_block_headers[i] != NULL) - { - free(last_block_headers[i]); - } - last_block_headers[i] = last_block_headers[i-1]; - } - last_block_headers[0] = block; need_new_template = true; + block_t *block = bstack_push(bsh, NULL); + response_to_block(block_header, block); } else if (front == NULL) { - last_block_headers[0] = block; + block_t *block = bstack_push(bsh, NULL); + response_to_block(block_header, block); + startup_pauout(block->height); need_new_template = true; } - else - free(block); - pool_stats.network_difficulty = last_block_headers[0]->difficulty; - pool_stats.network_hashrate = last_block_headers[0]->difficulty / BLOCK_TIME; - pool_stats.network_height = last_block_headers[0]->height; + front = bstack_peek(bsh); + pool_stats.network_difficulty = front->difficulty; + pool_stats.network_hashrate = front->difficulty / BLOCK_TIME; + pool_stats.network_height = front->height; update_pool_hr(); if (need_new_template) @@ -1474,17 +1471,18 @@ rpc_on_last_block_header(const char* data, rpc_callback_t *callback) log_info("Fetching new block template"); char body[RPC_BODY_MAX]; uint64_t reserve = 17; - rpc_get_request_body(body, "get_block_template", "sssd", "wallet_address", config.pool_wallet, "reserve_size", reserve); - rpc_callback_t *c1 = calloc(1, sizeof(rpc_callback_t)); - c1->cb = rpc_on_block_template; - rpc_request(base, body, c1); + rpc_get_request_body(body, "get_block_template", "sssd", + "wallet_address", config.pool_wallet, "reserve_size", reserve); + rpc_callback_t *cb1 = rpc_callback_new(rpc_on_block_template, NULL); + rpc_request(base, body, cb1); - uint64_t end = block->height - 60; + uint64_t end = front->height - 60; uint64_t start = end - BLOCK_HEADERS_RANGE + 1; - rpc_get_request_body(body, "get_block_headers_range", "sdsd", "start_height", start, "end_height", end); - rpc_callback_t *c2 = calloc(1, sizeof(rpc_callback_t)); - c2->cb = rpc_on_block_headers_range; - rpc_request(base, body, c2); + rpc_get_request_body(body, "get_block_headers_range", "sdsd", + "start_height", start, "end_height", end); + rpc_callback_t *cb2 = rpc_callback_new( + rpc_on_block_headers_range, NULL); + rpc_request(base, body, cb2); } json_object_put(root); @@ -1640,15 +1638,112 @@ cleanup: json_object_put(root); } +static int +send_payments(void) +{ + uint64_t threshold = 1000000000000 * config.payment_threshold; + int rc; + char *err; + MDB_txn *txn; + MDB_cursor *cursor; + if ((rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)) != 0) + { + err = mdb_strerror(rc); + log_error("%s", err); + return rc; + } + if ((rc = mdb_cursor_open(txn, db_balance, &cursor)) != 0) + { + err = mdb_strerror(rc); + log_error("%s", err); + mdb_txn_abort(txn); + return rc; + } + + size_t payments_count = 0; + size_t payments_max_count = 25; + size_t payments_size = payments_max_count * sizeof(payment_t); + payment_t *payments = (payment_t*) calloc(1, payments_size); + memset(payments, 0, payments_size); + payment_t *payment = payments; + payment_t *end_payment = payment + payments_max_count; + + MDB_cursor_op op = MDB_FIRST; + while (1) + { + MDB_val key; + MDB_val val; + rc = mdb_cursor_get(cursor, &key, &val, op); + op = MDB_NEXT; + if (rc != 0) + break; + + const char *address = (const char*)key.mv_data; + uint64_t amount = *(uint64_t*)val.mv_data; + + if (amount < threshold) + continue; + + log_info("Sending payment of %"PRIu64" to %s\n", amount, address); + + memcpy(payment->address, address, ADDRESS_MAX); + payment->amount = amount; + payments_count++; + + if (++payment == end_payment) + { + payments_size <<= 1; + payments = (payment_t*) realloc(payments, payments_size); + payment = payments + payments_max_count; + memset(payment, 0, sizeof(payment_t) * payments_max_count); + payments_max_count <<= 1; + end_payment = payments + payments_max_count; + } + } + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + if (payments_count) + { + size_t body_size = 160 * payments_count + 128; + char body[body_size]; + char *start = body; + char *end = body + body_size; + start = stecpy(start, "{\"id\":\"0\",\"jsonrpc\":\"2.0\",\"method\":" + "\"transfer_split\",\"params\":{" + "\"ring_size\":11,\"destinations\":[", end); + for (size_t i=0; iaddress, end); + start = stecpy(start, "\",\"amount\":", end); + sprintf(start, "%"PRIu64"}", p->amount); + start = body + strlen(body); + if (i != payments_count -1) + start = stecpy(start, ",", end); + else + start = stecpy(start, "]}}", end); + } + log_trace(body); + rpc_callback_t *cb = rpc_callback_new( + rpc_on_wallet_transferred, payments); + rpc_wallet_request(base, body, cb); + } + else + free(payments); + + return 0; +} + static void -fetch_last_block_header() +fetch_last_block_header(void) { log_info("Fetching last block header"); char body[RPC_BODY_MAX]; rpc_get_request_body(body, "get_last_block_header", NULL); - rpc_callback_t *callback = calloc(1, sizeof(rpc_callback_t)); - callback->cb = rpc_on_last_block_header; - rpc_request(base, body, callback); + rpc_callback_t *cb = rpc_callback_new(rpc_on_last_block_header, NULL); + rpc_request(base, body, cb); } static void @@ -1686,7 +1781,8 @@ client_add(int fd, struct bufferevent *bev) { pthread_mutex_lock(&mutex_clients); pool_clients.count += POOL_CLIENTS_GROW; - c = realloc(pool_clients.clients, sizeof(client_t) * pool_clients.count); + c = realloc(pool_clients.clients, sizeof(client_t) * + pool_clients.count); pool_clients.clients = c; c += pool_clients.count - POOL_CLIENTS_GROW; pthread_mutex_unlock(&mutex_clients); @@ -1728,134 +1824,6 @@ client_clear(struct bufferevent *bev) pool_stats.connected_miners--; } -static void -client_send_job(client_t *client, bool response) -{ - /* First cycle jobs */ - job_t *last = &client->active_jobs[CLIENT_JOBS_MAX-1]; - if (last->blob != NULL) - { - free(last->blob); - last->blob = NULL; - } - if (last->submissions != NULL) - { - free(last->submissions); - last->submissions = NULL; - last->submissions_count = 0; - } - for (size_t i=CLIENT_JOBS_MAX-1; i>0; i--) - { - job_t *current = &client->active_jobs[i]; - job_t *prev = &client->active_jobs[i-1]; - memcpy(current, prev, sizeof(job_t)); - } - job_t *job = &client->active_jobs[0]; - memset(job, 0, sizeof(job_t)); - - /* Quick check we actually have a block template */ - block_template_t *bt = block_templates[0]; - if (!bt) - { - log_warn("Cannot send client a job as have not yet recieved a block template"); - return; - } - - /* - 1. Convert blocktemplate_blob to binary - 2. Update bytes in reserved space at reserved_offset - 3. Get block hashing blob for job - 4. Send - */ - - /* Convert template to blob */ - size_t bin_size = strlen(bt->blocktemplate_blob) >> 1; - char *block = calloc(bin_size, sizeof(char)); - hex_to_bin(bt->blocktemplate_blob, block, bin_size); - - /* Set the extra nonce in our reserved space */ - char *p = block; - p += bt->reserved_offset; - ++extra_nonce; - memcpy(p, &extra_nonce, sizeof(extra_nonce)); - job->extra_nonce = extra_nonce; - - /* Get hashong blob */ - size_t hashing_blob_size; - char *hashing_blob = NULL; - get_hashing_blob(block, bin_size, &hashing_blob, &hashing_blob_size); - - /* Make hex */ - job->blob = calloc((hashing_blob_size << 1) +1, sizeof(char)); - bin_to_hex(hashing_blob, hashing_blob_size, job->blob, hashing_blob_size << 1); - log_trace("Miner hashing blob: %s", job->blob); - - /* Save a job id */ - uuid_generate(job->id); - - /* Hold reference to block template */ - job->block_template = bt; - - /* Send */ - char job_id[33]; - bin_to_hex((const char*)job->id, sizeof(uuid_t), job_id, 32); - - /* Retarget */ - double duration = difftime(time(NULL), client->connected_since); - uint8_t retarget_time = client->is_proxy ? 5 : 120; - uint64_t target = fmax((double)client->hashes / duration * retarget_time, config.pool_start_diff); - job->target = target; - log_debug("Client %.32s target now %"PRIu64, client->client_id, target); - - char body[JOB_BODY_MAX]; - if (!client->is_proxy) - { - stratum_get_job_body(body, client, response); - } - else - { - char *block_hex = calloc(bin_size+1, sizeof(char)); - bin_to_hex(block, bin_size, block_hex, bin_size << 1); - stratum_get_proxy_job_body(body, client, block_hex, response); - free(block_hex); - } - log_trace("Client job: %s", body); - struct evbuffer *output = bufferevent_get_output(client->bev); - evbuffer_add(output, body, strlen(body)); - free(block); - free(hashing_blob); -} - -static void -client_clear_jobs(client_t *client) -{ - for (size_t i=0; iactive_jobs[i]; - if (job->blob != NULL) - { - free(job->blob); - job->blob = NULL; - } - if (job->submissions != NULL) - { - free(job->submissions); - job->submissions = NULL; - job->submissions_count = 0; - } - } -} - -static void -send_validation_error(const client_t *client, const char *message) -{ - struct evbuffer *output = bufferevent_get_output(client->bev); - char body[ERROR_BODY_MAX]; - stratum_get_error_body(body, client->json_id, message); - evbuffer_add(output, body, strlen(body)); - log_debug("Validation error: %s", message); -} - static void client_on_login(json_object *message, client_t *client) { @@ -1867,7 +1835,8 @@ client_on_login(json_object *message, client_t *client) uint64_t prefix; parse_address(address, &prefix); if (prefix != MAINNET_ADDRESS_PREFIX && prefix != TESTNET_ADDRESS_PREFIX) - return send_validation_error(client, "login only main wallet addresses are supported"); + return send_validation_error(client, + "login only main wallet addresses are supported"); const char *worker_id = json_object_get_string(pass); @@ -1878,7 +1847,8 @@ client_on_login(json_object *message, client_t *client) if (user_agent) { strncpy(client->agent, user_agent, 255); - client->is_proxy = strstr(user_agent, "proxy") != NULL ? true : false; + client->is_proxy = strstr(user_agent, "proxy") != NULL + ? true : false; } } @@ -1924,7 +1894,8 @@ client_on_submit(json_object *message, client_t *client) if (!job) return send_validation_error(client, "cannot find job with job_id"); - log_trace("Client submitted nonce=%u, result=%s", result_nonce, result_hex); + log_trace("Client submitted nonce=%u, result=%s", + result_nonce, result_hex); /* 1. Validate submission active_job->blocktemplate_blob to bin @@ -1936,8 +1907,8 @@ client_on_submit(json_object *message, client_t *client) check result hash against block difficulty (if ge then mined block) check result hash against target difficulty (if not ge, invalid share) 2. Process share - check result hash against template difficulty (submit to network if good) - add share to db + check result hash against template difficulty + (submit to network if good) add share to db Note reserved space is: extra_nonce, instance_id, pool_nonce, worker_nonce 4 bytes each. instance_id would be used for pool threads. @@ -1980,7 +1951,8 @@ client_on_submit(json_object *message, client_t *client) *psub++ = worker_nonce; psub -= 4; - log_trace("Submission reserved values: %u %u %u %u", *psub, *(psub+1), *(psub+2), *(psub+3)); + log_trace("Submission reserved values: %u %u %u %u", + *psub, *(psub+1), *(psub+2), *(psub+3)); /* Check not already submitted */ uint128_t *submissions = job->submissions; @@ -2008,7 +1980,8 @@ client_on_submit(json_object *message, client_t *client) /* Get hashong blob */ size_t hashing_blob_size; char *hashing_blob = NULL; - if (get_hashing_blob(block, bin_size, &hashing_blob, &hashing_blob_size) != 0) + if (get_hashing_blob(block, bin_size, + &hashing_blob, &hashing_blob_size) != 0) { char body[ERROR_BODY_MAX]; stratum_get_error_body(body, client->json_id, "Invalid block"); @@ -2023,7 +1996,8 @@ client_on_submit(json_object *message, client_t *client) char submitted_hash[32]; uint8_t major_version = (uint8_t)block[0]; const int cn_variant = major_version >= 7 ? major_version - 6 : 0; - get_hash(hashing_blob, hashing_blob_size, (char**)&result_hash, cn_variant, bt->height); + get_hash(hashing_blob, hashing_blob_size, + (char**)&result_hash, cn_variant, bt->height); hex_to_bin(result_hex, submitted_hash, 32); if (memcmp(submitted_hash, result_hash, 32) != 0) @@ -2052,27 +2026,26 @@ client_on_submit(json_object *message, client_t *client) /* Process share */ client->hashes += job->target; time_t now = time(NULL); - log_trace("Checking hash against blobk difficulty: %lu, job difficulty: %lu", + bool can_store = true; + log_trace("Checking hash against blobk difficulty: " + "%lu, job difficulty: %lu", BN_get_word(bd), BN_get_word(jd)); - bool can_store = false; if (BN_cmp(hd, bd) >= 0) { - can_store = true; /* Yay! Mined a block so submit to network */ log_info("+++ MINED A BLOCK +++"); char *block_hex = calloc((bin_size << 1)+1, sizeof(char)); bin_to_hex(block, bin_size, block_hex, bin_size << 1); char body[RPC_BODY_MAX]; snprintf(body, RPC_BODY_MAX, - "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"submit_block\", \"params\":[\"%s\"]}", + "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":" + "\"submit_block\", \"params\":[\"%s\"]}", block_hex); - rpc_callback_t *callback = calloc(1, sizeof(rpc_callback_t)); - callback->cb = rpc_on_block_submitted; - callback->data = calloc(1, sizeof(block_t)); - - block_t* b = (block_t*)callback->data; + rpc_callback_t *cb = rpc_callback_new(rpc_on_block_submitted, NULL); + cb->data = calloc(1, sizeof(block_t)); + block_t* b = (block_t*) cb->data; b->height = bt->height; bin_to_hex(submitted_hash, 32, b->hash, 64); memcpy(b->prev_hash, bt->prev_hash, 64); @@ -2080,7 +2053,7 @@ client_on_submit(json_object *message, client_t *client) b->status = BLOCK_LOCKED; b->timestamp = now; - rpc_request(base, body, callback); + rpc_request(base, body, cb); free(block_hex); } else if (BN_cmp(hd, jd) < 0) @@ -2091,8 +2064,6 @@ client_on_submit(json_object *message, client_t *client) log_debug("Low difficulty (%lu) share", BN_get_word(jd)); evbuffer_add(output, body, strlen(body)); } - else - can_store = true; BN_free(hd); BN_free(jd); @@ -2280,7 +2251,8 @@ read_config(const char *config_file, const char *log_file, bool block_notified) strcat(path, "/pool.conf"); if (access(path, R_OK) != 0) { - log_fatal("Cannot find a config file in ./ or ~/ and no option supplied. Aborting."); + log_fatal("Cannot find a config file in ./ or ~/ " + "and no option supplied. Aborting."); abort(); } } @@ -2379,18 +2351,37 @@ read_config(const char *config_file, const char *log_file, bool block_notified) } if (!config.wallet_rpc_host[0] || config.wallet_rpc_port == 0) { - log_fatal("Both wallet-rpc-host and wallet-rpc-port need setting. Aborting."); + log_fatal("Both wallet-rpc-host and wallet-rpc-port need setting. " + "Aborting."); abort(); } - log_info("\nCONFIG:\n rpc_host = %s\n rpc_port = %u\n rpc_timeout = %u\n pool_wallet = %s\n " - "pool_start_diff = %"PRIu64"\n share_mul = %.2f\n pool_fee = %.2f\n payment_threshold = %.2f\n " + log_info("\nCONFIG:\n rpc_host = %s\n rpc_port = %u\n " + "rpc_timeout = %u\n pool_wallet = %s\n " + "pool_start_diff = %"PRIu64"\n share_mul = %.2f\n " + "pool_fee = %.2f\n payment_threshold = %.2f\n " "wallet_rpc_host = %s\n wallet_rpc_port = %u\n pool_port = %u\n " - "log_level = %u\n webui_port=%u\n log-file = %s\n block-notified = %u\n", + "log_level = %u\n webui_port=%u\n " + "log-file = %s\n block-notified = %u\n", config.rpc_host, config.rpc_port, config.rpc_timeout, config.pool_wallet, config.pool_start_diff, config.share_mul, config.pool_fee, config.payment_threshold, config.wallet_rpc_host, config.wallet_rpc_port, config.pool_port, - config.log_level, config.webui_port, config.log_file, config.block_notified); + config.log_level, config.webui_port, + config.log_file, config.block_notified); +} + +static void +sigusr1_handler(evutil_socket_t fd, short event, void *arg) +{ + log_trace("Fetching last block header from signal"); + fetch_last_block_header(); +} + +static void +sigint_handler(int sig) +{ + signal(SIGINT, SIG_DFL); + exit(0); } static void @@ -2432,7 +2423,8 @@ run(void) return; } - listener_event = event_new(base, listener, EV_READ|EV_PERSIST, client_on_accept, (void*)base); + listener_event = event_new(base, listener, EV_READ|EV_PERSIST, + client_on_accept, (void*)base); if (event_add(listener_event, NULL) != 0) { log_fatal("Failed to add socket listener event"); @@ -2456,7 +2448,7 @@ run(void) } static void -cleanup() +cleanup(void) { log_info("\nPerforming cleanup"); if (listener_event) @@ -2470,8 +2462,8 @@ cleanup() event_free(timer_10m); event_base_free(base); pool_clients_free(); - last_block_headers_free(); - block_templates_free(); + bstack_free(bsh); + bstack_free(bst); database_close(); BN_free(base_diff); BN_CTX_free(bn_ctx); @@ -2481,20 +2473,6 @@ cleanup() fclose(fd_log); } -static void -sigusr1_handler(evutil_socket_t fd, short event, void *arg) -{ - log_trace("Fetching last block header from signal"); - fetch_last_block_header(); -} - -static void -sigint_handler(int sig) -{ - signal(SIGINT, SIG_DFL); - exit(0); -} - int main(int argc, char **argv) { setvbuf(stdout, NULL, _IONBF, 0); @@ -2562,9 +2540,14 @@ int main(int argc, char **argv) goto cleanup; } + bstack_new(&bst, BLOCK_TEMPLATES_MAX, sizeof(block_template_t), + template_recycle); + bstack_new(&bsh, BLOCK_HEADERS_MAX, sizeof(block_t), NULL); + bn_ctx = BN_CTX_new(); base_diff = NULL; - BN_hex2bn(&base_diff, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + BN_hex2bn(&base_diff, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); pool_clients_init(); diff --git a/src/pool.h b/src/pool.h index 0bf1db1..6242e85 100644 --- a/src/pool.h +++ b/src/pool.h @@ -1,33 +1,35 @@ /* - Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - Parts of the project are originally copyright (c) 2012-2013 The Cryptonote developers +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. */ #ifndef POOL_H diff --git a/src/util.c b/src/util.c index 10fd2ef..74d4733 100644 --- a/src/util.c +++ b/src/util.c @@ -1,33 +1,35 @@ /* - Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - Parts of the project are originally copyright (c) 2012-2013 The Cryptonote developers +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. */ #include diff --git a/src/util.h b/src/util.h index e931b1f..b731efb 100644 --- a/src/util.h +++ b/src/util.h @@ -1,33 +1,35 @@ /* - Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - Parts of the project are originally copyright (c) 2012-2013 The Cryptonote developers +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. */ #ifndef UTIL_H diff --git a/src/webui.c b/src/webui.c index 87deefa..4357412 100644 --- a/src/webui.c +++ b/src/webui.c @@ -1,33 +1,35 @@ /* - Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - Parts of the project are originally copyright (c) 2012-2013 The Cryptonote developers +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. */ #define __STDC_FORMAT_MACROS @@ -75,7 +77,8 @@ send_json_stats (void *cls, struct MHD_Connection *connection) uint32_t pbf = context->pool_stats->pool_blocks_found; uint64_t mh = 0; double mb = 0.0; - const char *wa = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "wa"); + const char *wa = MHD_lookup_connection_value(connection, + MHD_COOKIE_KIND, "wa"); if (wa != NULL) { mh = miner_hr(wa); @@ -127,13 +130,14 @@ int start_web_ui(wui_context_t *context) { log_debug("Starting Web UI"); - mhd_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, context->port, NULL, NULL, + mhd_daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, + context->port, NULL, NULL, &answer_to_connection, (void*) context, MHD_OPTION_END); return mhd_daemon != NULL ? 0 : -1; } void -stop_web_ui() +stop_web_ui(void) { log_debug("Stopping Web UI"); if (mhd_daemon != NULL) diff --git a/src/webui.h b/src/webui.h index 3ca4dee..6bcaabb 100644 --- a/src/webui.h +++ b/src/webui.h @@ -1,33 +1,35 @@ /* - Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - Parts of the project are originally copyright (c) 2012-2013 The Cryptonote developers +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. */ #ifndef WEBUI_H @@ -54,6 +56,6 @@ typedef struct wui_context_t } wui_context_t; int start_web_ui(wui_context_t *context); -void stop_web_ui(); +void stop_web_ui(void); #endif diff --git a/src/xmr.cpp b/src/xmr.cpp index 8499600..b3c1c10 100644 --- a/src/xmr.cpp +++ b/src/xmr.cpp @@ -1,33 +1,35 @@ /* - Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - Parts of the project are originally copyright (c) 2012-2013 The Cryptonote developers +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. */ #include @@ -50,7 +52,8 @@ using namespace cryptonote; -int get_hashing_blob(const char *input, const size_t in_size, char **output, size_t *out_size) +int get_hashing_blob(const char *input, const size_t in_size, + char **output, size_t *out_size) { block b = AUTO_VAL_INIT(b); blobdata bd = std::string(input, in_size); @@ -79,8 +82,10 @@ int parse_address(const char *input, uint64_t *prefix) return rv ? 0 : -1; } -void get_hash(const char *input, const size_t in_size, char **output, int variant, uint64_t height) +void get_hash(const char *input, const size_t in_size, + char **output, int variant, uint64_t height) { - crypto::cn_slow_hash(input, in_size, reinterpret_cast(*output), variant, height); + crypto::cn_slow_hash(input, in_size, + reinterpret_cast(*output), variant, height); } diff --git a/src/xmr.h b/src/xmr.h index 79d04d6..82f1de7 100644 --- a/src/xmr.h +++ b/src/xmr.h @@ -1,33 +1,35 @@ /* - Copyright (c) 2014-2018, The Monero Project +Copyright (c) 2014-2019, The Monero Project - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list - of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - Parts of the project are originally copyright (c) 2012-2013 The Cryptonote developers +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. */ #ifndef XMR_H @@ -37,9 +39,11 @@ extern "C" { #endif -int get_hashing_blob(const char *input, const size_t in_size, char **output, size_t *out_size); +int get_hashing_blob(const char *input, const size_t in_size, + char **output, size_t *out_size); int parse_address(const char *input, uint64_t *prefix); -void get_hash(const char *input, const size_t in_size, char **output, int variant, uint64_t height); +void get_hash(const char *input, const size_t in_size, + char **output, int variant, uint64_t height); #ifdef __cplusplus } diff --git a/tools/inspect-data b/tools/inspect-data index da8ff89..2b63b4a 100755 --- a/tools/inspect-data +++ b/tools/inspect-data @@ -1,22 +1,90 @@ #!/usr/bin/env python +''' +Copyright (c) 2014-2019, The Monero Project + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote +developers. +''' + import argparse -import sys import lmdb -import struct from ctypes import * from datetime import datetime +class share_t(Structure): + _fields_ = [('height', c_longlong), + ('difficulty', c_longlong), + ('address', c_char*128), + ('timestamp', c_longlong)] + +class payment_t(Structure): + _fields_ = [('amount', c_longlong), + ('timestamp', c_longlong), + ('address', c_char*128)] + +class block_t(Structure): + _fields_ = [('height', c_longlong), + ('hash', c_char*64), + ('prev_hash', c_char*64), + ('difficulty', c_longlong), + ('status', c_long), + ('reward', c_longlong), + ('timestamp', c_longlong)] + +def format_block_status(status): + s = ["LOCKED", "UNLOCKED", "ORPHANED"] + return s[status] + +def format_timestamp(timestamp): + dt = datetime.fromtimestamp(timestamp) + return dt.strftime('%Y-%m-%d %H:%M:%S') + +def format_amount(amount): + return '{0:.6f}'.format(amount/1e12) + +def format_address(address): + return '{}...{}'.format(address[:8], address[-8:]) + +def address_from_key(key): + return key.decode('utf-8').rstrip('\0') + def print_balance(path): env = lmdb.open(path, readonly=True, max_dbs=1, create=False) balance = env.open_db('balance'.encode()) with env.begin(db=balance) as txn: with txn.cursor() as curs: for key, value in curs: - address = key.decode('utf-8').rstrip('\0') - address = '{}...{}'.format(address[:8], address[-8:]) + address = format_address(address_from_key(key)) amount = c_longlong.from_buffer_copy(value).value - amount = '{0:.6f}'.format(amount/1e12) + amount = format_amount(amount) print('{}\t{}'.format(address, amount)) env.close() @@ -26,26 +94,44 @@ def print_payements(path): with env.begin(db=payments) as txn: with txn.cursor() as curs: for key, value in curs: - address = key.decode('utf-8').rstrip('\0') - address = '{}...{}'.format(address[:8], address[-8:]) - amount, ts, addr = struct.unpack("Q Q 128s", value) - amount = '{0:.6f}'.format(amount/1e12) - dt = datetime.fromtimestamp(ts) - print('{}\t{}\t{}'.format(address, amount, - dt.strftime('%Y-%m-%d %H:%M:%S'))) + address = format_address(address_from_key(key)) + p = payment_t.from_buffer_copy(value) + amount = format_amount(p.amount) + dt = format_timestamp(p.timestamp) + print('{}\t{}\t{}'.format(address, amount, dt)) + env.close() + +def print_mined(path): + env = lmdb.open(path, readonly=True, max_dbs=1, create=False) + blocks = env.open_db('blocks'.encode()) + with env.begin(db=blocks) as txn: + with txn.cursor() as curs: + for key, value in curs: + height = c_longlong.from_buffer_copy(key).value + b = block_t.from_buffer_copy(value) + dt = format_timestamp(b.timestamp) + reward = format_amount(b.reward) + status = format_block_status(b.status) + print('{}\t{}\t{}\t{}'.format(height, status, reward, dt)) env.close() def main(): parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group(required=True) - group.add_argument('-b', '--balances', action='store_true', help='list miner balances') - group.add_argument('-p', '--payments', action='store_true', help='list payments made') + group.add_argument('-b', '--balances', action='store_true', + help='list miner balances') + group.add_argument('-p', '--payments', action='store_true', + help='list payments made') + group.add_argument('-m', '--mined', action='store_true', + help='list mined blocks') parser.add_argument('database', help='path to database') args = parser.parse_args() if args.balances: print_balance(args.database) elif args.payments: print_payements(args.database) + elif args.mined: + print_mined(args.database) if __name__ == '__main__': main()