From 168a1bdfedb1408a690f5387e04e3c9af7842c5e Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sun, 28 Feb 2021 10:51:53 +0000 Subject: [PATCH 01/12] Make coinbaseaux flags optional Needed for solo mining on bitcoind, starting with v0.20.0 It's probably safe to delete the coinbase flags, but leave them in as optional for now. See also: https://github.com/bitcoin/bitcoin/commit/9aedabe67eedfee9c94c6a50962f11348eb99bca --- cgminer.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cgminer.c b/cgminer.c index ece7ce100f..ab52e30a61 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2960,7 +2960,7 @@ static bool gbt_solo_decode(struct pool *pool, json_t *res_val) flags = json_string_value(json_object_get(coinbase_aux, "flags")); default_witness_commitment = json_string_value(json_object_get(res_val, "default_witness_commitment")); - if (!previousblockhash || !target || !version || !curtime || !bits || !coinbase_aux || !flags) { + if (!previousblockhash || !target || !version || !curtime || !bits) { applog(LOG_ERR, "Pool %d JSON failed to decode GBT", pool->pool_no); return false; } @@ -3039,10 +3039,12 @@ static bool gbt_solo_decode(struct pool *pool, json_t *res_val) ofs += ser_number(pool->scriptsig_base + ofs, height); // max 5 /* Followed by flags */ - len = strlen(flags) / 2; - pool->scriptsig_base[ofs++] = len; - hex2bin(pool->scriptsig_base + ofs, flags, len); - ofs += len; + if (flags) { + len = strlen(flags) / 2; + pool->scriptsig_base[ofs++] = len; + hex2bin(pool->scriptsig_base + ofs, flags, len); + ofs += len; + } /* Followed by timestamp */ cgtime(&now); From 9f5860e43147a56041c3f80758ad638eac311cd1 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Fri, 5 Mar 2021 19:30:08 +0000 Subject: [PATCH 02/12] Fix coinbase flags debug logging --- cgminer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cgminer.c b/cgminer.c index ab52e30a61..d8ce9d4a12 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2990,7 +2990,8 @@ static bool gbt_solo_decode(struct pool *pool, json_t *res_val) applog(LOG_DEBUG, "curtime: %d", curtime); applog(LOG_DEBUG, "bits: %s", bits); applog(LOG_DEBUG, "height: %d", height); - applog(LOG_DEBUG, "flags: %s", flags); + if (flags) + applog(LOG_DEBUG, "flags: %s", flags); cg_wlock(&pool->gbt_lock); hex2bin(hash_swap, previousblockhash, 32); From 71f3d829718ee92da9fb3f1812417b3e34558ade Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sun, 4 Apr 2021 21:26:48 +0100 Subject: [PATCH 03/12] Add device data to "Best share" status information Print name and id of the device that yielded the current best share. --- cgminer.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cgminer.c b/cgminer.c index d8ce9d4a12..bd393a8ee6 100644 --- a/cgminer.c +++ b/cgminer.c @@ -469,6 +469,7 @@ static char datestamp[40]; static char blocktime[32]; struct timeval block_timeval; static char best_share[8] = "0"; +static char best_device[8] = "n/a"; double current_diff = 0xFFFFFFFFFFFFFFFFULL; static char block_diff[8]; uint64_t best_diff = 0; @@ -3410,8 +3411,8 @@ static void curses_print_status(void) pool->has_gbt ? "GBT" : "LP", pool->rpc_user); } wclrtoeol(statuswin); - cg_mvwprintw(statuswin, 5, 0, " Block: %s... Diff:%s Started: %s Best share: %s ", - prev_block, block_diff, blocktime, best_share); + cg_mvwprintw(statuswin, 5, 0, " Block: %s... Diff:%s Started: %s Best share: %s (%s) ", + prev_block, block_diff, blocktime, best_share, best_device); mvwhline(statuswin, 6, 0, '-', linewidth); mvwhline(statuswin, statusy - 1, 0, '-', linewidth); #ifdef USE_USBUTILS @@ -5024,6 +5025,9 @@ uint64_t share_diff(const struct work *work) double d64, s64; uint64_t ret; + int thr_id; + struct cgpu_info *cgpu; + d64 = truediffone; s64 = le256todouble(work->hash); if (unlikely(!s64)) @@ -5036,6 +5040,10 @@ uint64_t share_diff(const struct work *work) new_best = true; best_diff = ret; suffix_string(best_diff, best_share, sizeof(best_share), 0); + + thr_id = work->thr_id; + cgpu = get_thr_cgpu(thr_id); + snprintf(best_device, sizeof(best_device), "%s %u", cgpu->drv->name, cgpu->device_id); } if (unlikely(ret > work->pool->best_diff)) work->pool->best_diff = ret; @@ -5750,6 +5758,7 @@ void zero_bestshare(void) best_diff = 0; memset(best_share, 0, 8); suffix_string(best_diff, best_share, sizeof(best_share), 0); + strcpy(best_device, "n/a"); for (i = 0; i < total_pools; i++) { struct pool *pool = pools[i]; From daf5335c9702badc87d214d881dfba4876ae0ed1 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 28 Jul 2021 17:35:07 +0100 Subject: [PATCH 04/12] Rework best_device display in status line --- cgminer.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cgminer.c b/cgminer.c index bd393a8ee6..199b4ba5b9 100644 --- a/cgminer.c +++ b/cgminer.c @@ -469,7 +469,7 @@ static char datestamp[40]; static char blocktime[32]; struct timeval block_timeval; static char best_share[8] = "0"; -static char best_device[8] = "n/a"; +static char best_device[12] = "(n/a)"; double current_diff = 0xFFFFFFFFFFFFFFFFULL; static char block_diff[8]; uint64_t best_diff = 0; @@ -3407,11 +3407,11 @@ static void curses_print_status(void) pool->sockaddr_url, pool->diff, pool->rpc_user); } else { cg_mvwprintw(statuswin, 4, 0, " Connected to %s diff %s with%s %s as user %s", - pool->sockaddr_url, pool->diff, have_longpoll ? "": "out", + pool->rpc_url, pool->diff, have_longpoll ? "": "out", pool->has_gbt ? "GBT" : "LP", pool->rpc_user); } wclrtoeol(statuswin); - cg_mvwprintw(statuswin, 5, 0, " Block: %s... Diff:%s Started: %s Best share: %s (%s) ", + cg_mvwprintw(statuswin, 5, 0, " Block: %s... Diff:%s Started: %s Best share: %s %s ", prev_block, block_diff, blocktime, best_share, best_device); mvwhline(statuswin, 6, 0, '-', linewidth); mvwhline(statuswin, statusy - 1, 0, '-', linewidth); @@ -5043,7 +5043,7 @@ uint64_t share_diff(const struct work *work) thr_id = work->thr_id; cgpu = get_thr_cgpu(thr_id); - snprintf(best_device, sizeof(best_device), "%s %u", cgpu->drv->name, cgpu->device_id); + snprintf(best_device, sizeof(best_device), "(%s %u)", cgpu->drv->name, cgpu->device_id); } if (unlikely(ret > work->pool->best_diff)) work->pool->best_diff = ret; @@ -5758,7 +5758,8 @@ void zero_bestshare(void) best_diff = 0; memset(best_share, 0, 8); suffix_string(best_diff, best_share, sizeof(best_share), 0); - strcpy(best_device, "n/a"); + memset(best_device, 0, 12); + snprintf(best_device, sizeof(best_device), "(n/a)"); for (i = 0; i < total_pools; i++) { struct pool *pool = pools[i]; From bd91dd714e54786ac2e57611dcea0bfa6302d818 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Fri, 30 Jul 2021 22:18:59 +0200 Subject: [PATCH 05/12] Reset best_share stats when block was found Allows for better tracking on a per-round basis. --- cgminer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cgminer.c b/cgminer.c index 199b4ba5b9..bc01cbc409 100644 --- a/cgminer.c +++ b/cgminer.c @@ -7961,6 +7961,8 @@ static void update_work_stats(struct thr_info *thr, struct work *work) found_blocks++; work->mandatory = true; applog(LOG_NOTICE, "Found block for pool %d!", work->pool->pool_no); + // reset best_share stats when block was found + zero_bestshare(); } mutex_lock(&stats_lock); From ca6c0a1a41eba7c191a3f25530a41858f254b03e Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sat, 31 Jul 2021 22:40:41 +0200 Subject: [PATCH 06/12] Status bar rework * Optimized for solo mining * Add block height * Add last found block * Add best round share * Change best share to best share ever --- cgminer.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/cgminer.c b/cgminer.c index bc01cbc409..2429caea43 100644 --- a/cgminer.c +++ b/cgminer.c @@ -464,15 +464,18 @@ bool curses_active; char current_hash[68]; static char prev_block[12]; static char current_block[32]; +static char last_found_block[12] = "\0"; static char datestamp[40]; static char blocktime[32]; struct timeval block_timeval; static char best_share[8] = "0"; -static char best_device[12] = "(n/a)"; +static char best_share_ever[8] = "0"; +static char best_device[12] = "n/a"; double current_diff = 0xFFFFFFFFFFFFFFFFULL; static char block_diff[8]; uint64_t best_diff = 0; +uint64_t best_diff_ever = 0; struct block { char hash[68]; @@ -3411,9 +3414,13 @@ static void curses_print_status(void) pool->has_gbt ? "GBT" : "LP", pool->rpc_user); } wclrtoeol(statuswin); - cg_mvwprintw(statuswin, 5, 0, " Block: %s... Diff:%s Started: %s Best share: %s %s ", - prev_block, block_diff, blocktime, best_share, best_device); - mvwhline(statuswin, 6, 0, '-', linewidth); + cg_mvwprintw(statuswin, 5, 0, " Block: %s... Height: %u Diff: %s Started: %s", + prev_block, pool->current_height, block_diff, blocktime); + wclrtoeol(statuswin); + cg_mvwprintw(statuswin, 6, 0, " Last block found: %s... Best share (ever): %s [%s] (%s)", + last_found_block, best_share, best_device, best_share_ever); + wclrtoeol(statuswin); + mvwhline(statuswin, 7, 0, '-', linewidth); mvwhline(statuswin, statusy - 1, 0, '-', linewidth); #ifdef USE_USBUTILS cg_mvwprintw(statuswin, devcursor - 1, 1, "[U]SB management [P]ool management [S]ettings [D]isplay options [Q]uit"); @@ -5040,10 +5047,14 @@ uint64_t share_diff(const struct work *work) new_best = true; best_diff = ret; suffix_string(best_diff, best_share, sizeof(best_share), 0); + if (unlikely(best_diff > best_diff_ever)) { + best_diff_ever = best_diff; + suffix_string(best_diff, best_share_ever, sizeof(best_share), 0); + } thr_id = work->thr_id; cgpu = get_thr_cgpu(thr_id); - snprintf(best_device, sizeof(best_device), "(%s %u)", cgpu->drv->name, cgpu->device_id); + snprintf(best_device, sizeof(best_device), "%s %u", cgpu->drv->name, cgpu->device_id); } if (unlikely(ret > work->pool->best_diff)) work->pool->best_diff = ret; @@ -5759,7 +5770,7 @@ void zero_bestshare(void) memset(best_share, 0, 8); suffix_string(best_diff, best_share, sizeof(best_share), 0); memset(best_device, 0, 12); - snprintf(best_device, sizeof(best_device), "(n/a)"); + snprintf(best_device, sizeof(best_device), "n/a"); for (i = 0; i < total_pools; i++) { struct pool *pool = pools[i]; @@ -7952,6 +7963,7 @@ bool test_nonce_diff(struct work *work, uint32_t nonce, double diff) static void update_work_stats(struct thr_info *thr, struct work *work) { double test_diff = current_diff; + char *work_hash; work->share_diff = share_diff(work); @@ -7961,6 +7973,10 @@ static void update_work_stats(struct thr_info *thr, struct work *work) found_blocks++; work->mandatory = true; applog(LOG_NOTICE, "Found block for pool %d!", work->pool->pool_no); + // update last found block + work_hash = bin2hex(work->hash, sizeof(work->hash)); + strncpy(last_found_block, work_hash, 8); + last_found_block[8] = '\0'; // reset best_share stats when block was found zero_bestshare(); } @@ -10143,7 +10159,7 @@ int main(int argc, char *argv[]) free(s); strcat(cgminer_path, "/"); - devcursor = 8; + devcursor = 9; logstart = devcursor + 1; logcursor = logstart + 1; From 90d03dd12f4b7d800eb9976356875c9c003aac2e Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sun, 1 Aug 2021 21:56:08 +0200 Subject: [PATCH 07/12] Add check for P2PKH address when solo mining cgminer only supports the P2PKH address format for the coinbase block creation. Using another address format will create malformed pubkey hashes. Maybe the support for P2SH or Bech32 formats will be added in the future. --- cgminer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cgminer.c b/cgminer.c index 2429caea43..6d9b461318 100644 --- a/cgminer.c +++ b/cgminer.c @@ -7144,6 +7144,11 @@ static bool setup_gbt_solo(CURL *curl, struct pool *pool) } goto out; } + // check for P2PKH address format + if (strncmp(opt_btc_address, "1", 1) != 0) { + applog(LOG_ERR, "Invalid Bitcoin address %s, only P2PKH address format (1...) is supported for solo mining", opt_btc_address); + goto out; + } snprintf(s, 256, "{\"id\": 1, \"method\": \"validateaddress\", \"params\": [\"%s\"]}\n", opt_btc_address); val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, s, true, false, &rolltime, pool, false); From ecdc0e2fc85eb2d4567efc1255b2082b2e6cc84b Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 2 Aug 2021 22:47:49 +0200 Subject: [PATCH 08/12] Use correct value for last found block --- cgminer.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cgminer.c b/cgminer.c index 6d9b461318..79cf899e21 100644 --- a/cgminer.c +++ b/cgminer.c @@ -3865,6 +3865,9 @@ static void show_hash(struct work *work, char *hashshow) suffix_string(work->share_diff, diffdisp, sizeof (diffdisp), 0); snprintf(hashshow, 64, "%08lx Diff %s/%"PRIu64"%s", h32, diffdisp, uintdiff, work->block? " BLOCK!" : ""); + // update last found block + if (work->block) + snprintf(last_found_block, 12, "%08lx", h32); } #ifdef HAVE_LIBCURL @@ -7978,10 +7981,6 @@ static void update_work_stats(struct thr_info *thr, struct work *work) found_blocks++; work->mandatory = true; applog(LOG_NOTICE, "Found block for pool %d!", work->pool->pool_no); - // update last found block - work_hash = bin2hex(work->hash, sizeof(work->hash)); - strncpy(last_found_block, work_hash, 8); - last_found_block[8] = '\0'; // reset best_share stats when block was found zero_bestshare(); } From 781f30beb273bd3cb119d1c61cad6dc7af159415 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 2 Mar 2022 20:42:35 +0100 Subject: [PATCH 09/12] Add -fcommon flag to CFLAGS This flag must be set explicitly now, since gcc 10+ uses -fno-common as default. --- configure.ac | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configure.ac b/configure.ac index 0a79aa5323..ee7e252305 100644 --- a/configure.ac +++ b/configure.ac @@ -85,6 +85,9 @@ esac #CFLAGS="$CFLAGS -Wimplicit-fallthrough=0" #^ causes problem on gcc 4.9 - pass in from CFLAG if needed +# Explicitly set -fcommon for gcc 10+ +CFLAGS="$CFLAGS -fcommon" + case $target in *-*-linux-gnu*) have_linux=true From 5da6b3373b86a5c5203c15839a2bf1568f4cda08 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 14 Mar 2022 22:35:11 +0100 Subject: [PATCH 10/12] Add Bech32 address support for solo mining --- Makefile.am | 3 + cgminer.c | 20 ++--- miner.h | 3 +- segwit_addr.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++ segwit_addr.h | 113 +++++++++++++++++++++++++++ util.c | 63 ++++++++++++--- util.h | 3 +- 7 files changed, 391 insertions(+), 23 deletions(-) create mode 100644 segwit_addr.c create mode 100644 segwit_addr.h diff --git a/Makefile.am b/Makefile.am index ae100806df..15c0bf86de 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,6 +53,9 @@ cgminer_SOURCES += klist.h klist.c cgminer_SOURCES += noncedup.c +# Bech32 support +cgminer_SOURCES += segwit_addr.c segwit_addr.h + if NEED_FPGAUTILS cgminer_SOURCES += fpgautils.c fpgautils.h endif diff --git a/cgminer.c b/cgminer.c index 79cf899e21..0bc7a501b9 100644 --- a/cgminer.c +++ b/cgminer.c @@ -3087,7 +3087,7 @@ static bool gbt_solo_decode(struct pool *pool, json_t *res_val) + 4 // txin sequence no + 1 // txouts + 8 // value - + 1 + 25 // txout + + 1 + pool->script_pubkey_len // txout + 4; // lock if (insert_witness) { @@ -3104,7 +3104,7 @@ static bool gbt_solo_decode(struct pool *pool, json_t *res_val) *u64 = htole64(coinbasevalue); if (insert_witness) { - unsigned char *witness = &pool->coinbase[41 + ofs + 4 + 1 + 8 + 1 + 25]; + unsigned char *witness = &pool->coinbase[41 + ofs + 4 + 1 + 8 + 1 + pool->script_pubkey_len]; memset(witness, 0, 8); witness_txout_len += 8; @@ -3117,7 +3117,7 @@ static bool gbt_solo_decode(struct pool *pool, json_t *res_val) pool->nonce2 = 0; pool->n2size = 4; - pool->coinbase_len = 41 + ofs + 4 + 1 + 8 + 1 + 25 + witness_txout_len + 4; + pool->coinbase_len = 41 + ofs + 4 + 1 + 8 + 1 + pool->script_pubkey_len + witness_txout_len + 4; cg_wunlock(&pool->gbt_lock); snprintf(header, 257, "%s%s%s%s%s%s%s", @@ -7128,8 +7128,9 @@ static void __setup_gbt_solo(struct pool *pool) { cg_wlock(&pool->gbt_lock); cg_memcpy(pool->coinbase, scriptsig_header_bin, 41); - pool->coinbase[41 + pool->n1_len + 4 + 1 + 8] = 25; - cg_memcpy(pool->coinbase + 41 + pool->n1_len + 4 + 1 + 8 + 1, pool->script_pubkey, 25); + applog(LOG_DEBUG, "script_pubkey_len=%d", pool->script_pubkey_len); + pool->coinbase[41 + pool->n1_len + 4 + 1 + 8] = pool->script_pubkey_len; + cg_memcpy(pool->coinbase + 41 + pool->n1_len + 4 + 1 + 8 + 1, pool->script_pubkey, pool->script_pubkey_len); cg_wunlock(&pool->gbt_lock); } @@ -7147,9 +7148,9 @@ static bool setup_gbt_solo(CURL *curl, struct pool *pool) } goto out; } - // check for P2PKH address format - if (strncmp(opt_btc_address, "1", 1) != 0) { - applog(LOG_ERR, "Invalid Bitcoin address %s, only P2PKH address format (1...) is supported for solo mining", opt_btc_address); + // check for P2PKH or BECH32 address format + if ((strncmp(opt_btc_address, "1", 1) != 0) && (!addr_format_is_bech32(opt_btc_address))) { + applog(LOG_ERR, "Invalid Bitcoin address %s, only P2PKH (1...) or BECH32 (bc1...) format is supported for solo mining", opt_btc_address); goto out; } snprintf(s, 256, "{\"id\": 1, \"method\": \"validateaddress\", \"params\": [\"%s\"]}\n", opt_btc_address); @@ -7169,7 +7170,8 @@ static bool setup_gbt_solo(CURL *curl, struct pool *pool) } applog(LOG_NOTICE, "Solo mining to valid address: %s", opt_btc_address); ret = true; - address_to_pubkeyhash(pool->script_pubkey, opt_btc_address); + address_to_pubkeyhash(pool->script_pubkey, &pool->script_pubkey_len, opt_btc_address); + applog(LOG_DEBUG, "script_pubkey: %s", bin2hex(pool->script_pubkey, pool->script_pubkey_len)); hex2bin(scriptsig_header_bin, scriptsig_header, 41); __setup_gbt_solo(pool); diff --git a/miner.h b/miner.h index 338ae68fbb..b9a0cc3033 100644 --- a/miner.h +++ b/miner.h @@ -1381,7 +1381,8 @@ struct pool { int transactions; char *txn_data; unsigned char scriptsig_base[100]; - unsigned char script_pubkey[25 + 3]; + unsigned char script_pubkey[42 + 3]; + int script_pubkey_len; int nValue; CURL *gbt_curl; bool gbt_curl_inuse; diff --git a/segwit_addr.c b/segwit_addr.c new file mode 100644 index 0000000000..a1164e7903 --- /dev/null +++ b/segwit_addr.c @@ -0,0 +1,209 @@ +/* Copyright (c) 2017, 2021 Pieter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include + +#include "segwit_addr.h" + +static uint32_t bech32_polymod_step(uint32_t pre) { + uint8_t b = pre >> 25; + return ((pre & 0x1FFFFFF) << 5) ^ + (-((b >> 0) & 1) & 0x3b6a57b2UL) ^ + (-((b >> 1) & 1) & 0x26508e6dUL) ^ + (-((b >> 2) & 1) & 0x1ea119faUL) ^ + (-((b >> 3) & 1) & 0x3d4233ddUL) ^ + (-((b >> 4) & 1) & 0x2a1462b3UL); +} + +static uint32_t bech32_final_constant(bech32_encoding enc) { + if (enc == BECH32_ENCODING_BECH32) return 1; + if (enc == BECH32_ENCODING_BECH32M) return 0x2bc830a3; + assert(0); +} + +static const char* charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +static const int8_t charset_rev[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 +}; + +int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len, bech32_encoding enc) { + uint32_t chk = 1; + size_t i = 0; + while (hrp[i] != 0) { + int ch = hrp[i]; + if (ch < 33 || ch > 126) { + return 0; + } + + if (ch >= 'A' && ch <= 'Z') return 0; + chk = bech32_polymod_step(chk) ^ (ch >> 5); + ++i; + } + if (i + 7 + data_len > 90) return 0; + chk = bech32_polymod_step(chk); + while (*hrp != 0) { + chk = bech32_polymod_step(chk) ^ (*hrp & 0x1f); + *(output++) = *(hrp++); + } + *(output++) = '1'; + for (i = 0; i < data_len; ++i) { + if (*data >> 5) return 0; + chk = bech32_polymod_step(chk) ^ (*data); + *(output++) = charset[*(data++)]; + } + for (i = 0; i < 6; ++i) { + chk = bech32_polymod_step(chk); + } + chk ^= bech32_final_constant(enc); + for (i = 0; i < 6; ++i) { + *(output++) = charset[(chk >> ((5 - i) * 5)) & 0x1f]; + } + *output = 0; + return 1; +} + +bech32_encoding bech32_decode(char* hrp, uint8_t *data, size_t *data_len, const char *input) { + uint32_t chk = 1; + size_t i; + size_t input_len = strlen(input); + size_t hrp_len; + int have_lower = 0, have_upper = 0; + if (input_len < 8 || input_len > 90) { + return BECH32_ENCODING_NONE; + } + *data_len = 0; + while (*data_len < input_len && input[(input_len - 1) - *data_len] != '1') { + ++(*data_len); + } + hrp_len = input_len - (1 + *data_len); + if (1 + *data_len >= input_len || *data_len < 6) { + return BECH32_ENCODING_NONE; + } + *(data_len) -= 6; + for (i = 0; i < hrp_len; ++i) { + int ch = input[i]; + if (ch < 33 || ch > 126) { + return BECH32_ENCODING_NONE; + } + if (ch >= 'a' && ch <= 'z') { + have_lower = 1; + } else if (ch >= 'A' && ch <= 'Z') { + have_upper = 1; + ch = (ch - 'A') + 'a'; + } + hrp[i] = ch; + chk = bech32_polymod_step(chk) ^ (ch >> 5); + } + hrp[i] = 0; + chk = bech32_polymod_step(chk); + for (i = 0; i < hrp_len; ++i) { + chk = bech32_polymod_step(chk) ^ (input[i] & 0x1f); + } + ++i; + while (i < input_len) { + int v = (input[i] & 0x80) ? -1 : charset_rev[(int)input[i]]; + if (input[i] >= 'a' && input[i] <= 'z') have_lower = 1; + if (input[i] >= 'A' && input[i] <= 'Z') have_upper = 1; + if (v == -1) { + return BECH32_ENCODING_NONE; + } + chk = bech32_polymod_step(chk) ^ v; + if (i + 6 < input_len) { + data[i - (1 + hrp_len)] = v; + } + ++i; + } + if (have_lower && have_upper) { + return BECH32_ENCODING_NONE; + } + if (chk == bech32_final_constant(BECH32_ENCODING_BECH32)) { + return BECH32_ENCODING_BECH32; + } else if (chk == bech32_final_constant(BECH32_ENCODING_BECH32M)) { + return BECH32_ENCODING_BECH32M; + } else { + return BECH32_ENCODING_NONE; + } +} + +static int convert_bits(uint8_t* out, size_t* outlen, int outbits, const uint8_t* in, size_t inlen, int inbits, int pad) { + uint32_t val = 0; + int bits = 0; + uint32_t maxv = (((uint32_t)1) << outbits) - 1; + while (inlen--) { + val = (val << inbits) | *(in++); + bits += inbits; + while (bits >= outbits) { + bits -= outbits; + out[(*outlen)++] = (val >> bits) & maxv; + } + } + if (pad) { + if (bits) { + out[(*outlen)++] = (val << (outbits - bits)) & maxv; + } + } else if (((val << (outbits - bits)) & maxv) || bits >= inbits) { + return 0; + } + return 1; +} + +int segwit_addr_encode(char *output, const char *hrp, int witver, const uint8_t *witprog, size_t witprog_len) { + uint8_t data[65]; + size_t datalen = 0; + bech32_encoding enc = BECH32_ENCODING_BECH32; + if (witver > 16) return 0; + if (witver == 0 && witprog_len != 20 && witprog_len != 32) return 0; + if (witprog_len < 2 || witprog_len > 40) return 0; + if (witver > 0) enc = BECH32_ENCODING_BECH32M; + data[0] = witver; + convert_bits(data + 1, &datalen, 5, witprog, witprog_len, 8, 1); + ++datalen; + return bech32_encode(output, hrp, data, datalen, enc); +} + +int segwit_addr_decode(int* witver, uint8_t* witdata, size_t* witdata_len, const char* hrp, const char* addr) { + uint8_t data[84]; + char hrp_actual[84]; + size_t data_len; + bech32_encoding enc = bech32_decode(hrp_actual, data, &data_len, addr); + if (enc == BECH32_ENCODING_NONE) return 0; + if (data_len == 0 || data_len > 65) return 0; + if (strncmp(hrp, hrp_actual, 84) != 0) return 0; + if (data[0] > 16) return 0; + if (data[0] == 0 && enc != BECH32_ENCODING_BECH32) return 0; + if (data[0] > 0 && enc != BECH32_ENCODING_BECH32M) return 0; + *witdata_len = 0; + if (!convert_bits(witdata, witdata_len, 8, data + 1, data_len - 1, 5, 0)) return 0; + if (*witdata_len < 2 || *witdata_len > 40) return 0; + if (data[0] == 0 && *witdata_len != 20 && *witdata_len != 32) return 0; + *witver = data[0]; + return 1; +} diff --git a/segwit_addr.h b/segwit_addr.h new file mode 100644 index 0000000000..511970ab8b --- /dev/null +++ b/segwit_addr.h @@ -0,0 +1,113 @@ +/* Copyright (c) 2017, 2021 Pieter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _SEGWIT_ADDR_H_ +#define _SEGWIT_ADDR_H_ 1 + +#include + + +/** Encode a SegWit address + * + * Out: output: Pointer to a buffer of size 73 + strlen(hrp) that will be + * updated to contain the null-terminated address. + * In: hrp: Pointer to the null-terminated human readable part to use + * (chain/network specific). + * ver: Version of the witness program (between 0 and 16 inclusive). + * prog: Data bytes for the witness program (between 2 and 40 bytes). + * prog_len: Number of data bytes in prog. + * Returns 1 if successful. + */ +int segwit_addr_encode( + char *output, + const char *hrp, + int ver, + const uint8_t *prog, + size_t prog_len +); + +/** Decode a SegWit address + * + * Out: ver: Pointer to an int that will be updated to contain the witness + * program version (between 0 and 16 inclusive). + * prog: Pointer to a buffer of size 40 that will be updated to + * contain the witness program bytes. + * prog_len: Pointer to a size_t that will be updated to contain the length + * of bytes in prog. + * hrp: Pointer to the null-terminated human readable part that is + * expected (chain/network specific). + * addr: Pointer to the null-terminated address. + * Returns 1 if successful. + */ +int segwit_addr_decode( + int* ver, + uint8_t* prog, + size_t* prog_len, + const char* hrp, + const char* addr +); + +/** Supported encodings. */ +typedef enum { + BECH32_ENCODING_NONE, + BECH32_ENCODING_BECH32, + BECH32_ENCODING_BECH32M +} bech32_encoding; + +/** Encode a Bech32 or Bech32m string + * + * Out: output: Pointer to a buffer of size strlen(hrp) + data_len + 8 that + * will be updated to contain the null-terminated Bech32 string. + * In: hrp : Pointer to the null-terminated human readable part. + * data : Pointer to an array of 5-bit values. + * data_len: Length of the data array. + * enc: Which encoding to use (BECH32_ENCODING_BECH32{,M}). + * Returns 1 if successful. + */ +int bech32_encode( + char *output, + const char *hrp, + const uint8_t *data, + size_t data_len, + bech32_encoding enc +); + +/** Decode a Bech32 or Bech32m string + * + * Out: hrp: Pointer to a buffer of size strlen(input) - 6. Will be + * updated to contain the null-terminated human readable part. + * data: Pointer to a buffer of size strlen(input) - 8 that will + * hold the encoded 5-bit data values. + * data_len: Pointer to a size_t that will be updated to be the number + * of entries in data. + * In: input: Pointer to a null-terminated Bech32 string. + * Returns BECH32_ENCODING_BECH32{,M} to indicate decoding was successful + * with the specified encoding standard. BECH32_ENCODING_NONE is returned if + * decoding failed. + */ +bech32_encoding bech32_decode( + char *hrp, + uint8_t *data, + size_t *data_len, + const char *input +); + +#endif diff --git a/util.c b/util.c index 52cba5de38..de82b043f3 100644 --- a/util.c +++ b/util.c @@ -44,6 +44,7 @@ #include "elist.h" #include "compat.h" #include "util.h" +#include "segwit_addr.h" #define DEFAULT_SOCKWAIT 60 #ifndef STRATUM_USER_AGENT @@ -1017,18 +1018,52 @@ void b58tobin(unsigned char *b58bin, const char *b58) } } -void address_to_pubkeyhash(unsigned char *pkh, const char *addr) +int addr_format_is_bech32(const char *addr) +{ + return (!strncasecmp(addr, "bc1", 3) || !strncasecmp(addr, "tb1", 3)); +} + +void address_to_pubkeyhash(unsigned char *pkh, int *pkh_len, const char *addr) { - unsigned char b58bin[25]; - memset(b58bin, 0, 25); - b58tobin(b58bin, addr); - pkh[0] = 0x76; - pkh[1] = 0xa9; - pkh[2] = 0x14; - cg_memcpy(&pkh[3], &b58bin[1], 20); - pkh[23] = 0x88; - pkh[24] = 0xac; + if (addr_format_is_bech32(addr)) { + uint8_t witprog[40]; + size_t witprog_len; + int witver; + const char* hrp = "bc"; + int ok = 1; + int ret = segwit_addr_decode(&witver, witprog, &witprog_len, hrp, addr); + if (!ret) { + hrp = "tb"; + ret = segwit_addr_decode(&witver, witprog, &witprog_len, hrp, addr); + } + if (!ret) { + applog(LOG_ERR, "segwit_addr_decode fails: '%s'\n", addr); + ok = 0; + } + if (ok) { + pkh[0] = witver ? (0x50 + witver) : 0; + pkh[1] = witprog_len; + cg_memcpy(&pkh[2], witprog, witprog_len); + *pkh_len = witprog_len + 2; + applog(LOG_DEBUG, "witprog=%s, witprog_len=%lu, pkh=%s, pkh_len=%d", + bin2hex(witprog, witprog_len), witprog_len, bin2hex(pkh, witprog_len + 2), *pkh_len); + } + } + else { + unsigned char b58bin[25]; + + memset(b58bin, 0, 25); + b58tobin(b58bin, addr); + pkh[0] = 0x76; + pkh[1] = 0xa9; + pkh[2] = 0x14; + cg_memcpy(&pkh[3], &b58bin[1], 20); + pkh[23] = 0x88; + pkh[24] = 0xac; + *pkh_len = 25; + applog(LOG_DEBUG, "pkh=%s", bin2hex(pkh, 25)); + } } /* For encoding nHeight into coinbase, return how many bytes were used */ @@ -1037,11 +1072,15 @@ int ser_number(unsigned char *s, int32_t val) int32_t *i32 = (int32_t *)&s[1]; int len; + if (val < 17) { + s[0] = 0x50 + val; + return 1; + } if (val < 128) len = 1; - else if (val < 16512) + else if (val < 32768) len = 2; - else if (val < 2113664) + else if (val < 8388608) len = 3; else len = 4; diff --git a/util.h b/util.h index 87db0d7576..682fc53a3e 100644 --- a/util.h +++ b/util.h @@ -116,7 +116,8 @@ struct pool; enum dev_reason; struct cgpu_info; void b58tobin(unsigned char *b58bin, const char *b58); -void address_to_pubkeyhash(unsigned char *pkh, const char *addr); +int addr_format_is_bech32(const char *addr); +void address_to_pubkeyhash(unsigned char *pkh, int *pkh_len, const char *addr); int ser_number(unsigned char *s, int32_t val); unsigned char *ser_string(char *s, int *slen); int thr_info_create(struct thr_info *thr, pthread_attr_t *attr, void *(*start) (void *), void *arg); From 33418c7ef4b230c4e8a5e343112a3b79de1e6d59 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sun, 20 Mar 2022 19:48:09 +0000 Subject: [PATCH 11/12] Increase log buffer size --- logging.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging.h b/logging.h index edf3c3db2e..55bb95a618 100644 --- a/logging.h +++ b/logging.h @@ -27,7 +27,7 @@ extern bool want_per_device_stats; /* global log_level, messages with lower or equal prio are logged */ extern int opt_log_level; -#define LOGBUFSIZ 256 +#define LOGBUFSIZ 320 extern void _applog(int prio, const char *str, bool force); extern void _simplelog(int prio, const char *str, bool force); From 59d485b83b5085d1abd1751d65f45efa0aa4f961 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Sun, 20 Mar 2022 19:48:39 +0000 Subject: [PATCH 12/12] Bump version to 4.11.2 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ee7e252305..45734f0c56 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_maj], [4]) m4_define([v_min], [11]) -m4_define([v_mic], [1]) +m4_define([v_mic], [2]) ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_ver], [v_maj.v_min.v_mic]) m4_define([lt_rev], m4_eval(v_maj + v_min))