diff --git a/src/bitcoind.c b/src/bitcoind.c index e978702..67231ed 100644 --- a/src/bitcoind.c +++ b/src/bitcoind.c @@ -373,7 +373,10 @@ int bitcoind_parse_template(void *result_json, copy_hex64(t->prev_hash_hex, jp->valuestring); copy_hex64(t->target_hex, jt->valuestring); - if (has_value) { + /* BIP22 lets a template carry both. A server that ships its own coinbase + * (the CUSF enforcer) puts mandatory outputs in it, so building our own + * from coinbasevalue would drop them: coinbasetxn always wins. */ + if (has_value && !has_cbtxn) { t->coinbase_value_sats = (int64_t)jc->valuedouble; } else { /* coinbasetxn: keep the raw coinbase hex; the value is reported as a diff --git a/tests/test_bitcoind.c b/tests/test_bitcoind.c index c1aa486..fa520c7 100644 --- a/tests/test_bitcoind.c +++ b/tests/test_bitcoind.c @@ -147,6 +147,21 @@ static void test_parse_coinbasetxn(void) { cJSON_Delete(root); } +/* BIP22 allows both fields. The server's coinbase carries mandatory outputs + * (drivechain commitments), so it must win over building our own. */ +static void test_parse_prefers_coinbasetxn(void) { + cJSON *root = cJSON_Parse(SAMPLE_GBT_COINBASETXN); + assert(root); + cJSON_AddNumberToObject(root, "coinbasevalue", 1234); + bitcoind_template_t *t = NULL; + char err[256] = {0}; + CHECK(bitcoind_parse_template(root, &t, err, sizeof(err)) == 0); + CHECK(t != NULL && t->coinbasetxn_hex != NULL); + CHECK(t != NULL && t->coinbase_value_sats == 5000000000LL); + bitcoind_template_free(t); + cJSON_Delete(root); +} + static void test_parse_missing_prevhash(void) { const char *bad = "{ \"version\":1, \"coinbasevalue\":100, \"target\":\"00\"," @@ -459,6 +474,7 @@ static void test_getblockhash_returns_the_hash(void) { int main(void) { test_parse_ok(); test_parse_coinbasetxn(); + test_parse_prefers_coinbasetxn(); test_parse_missing_prevhash(); test_parse_empty_txs(); test_double_free_safe();