int script_parse_pubkey_hash(const uint8 *scriptPubKey, size_t scriptLength, uint160 *pubkey) { enum script_txout_type type; struct buff buf; size_t datalen; uint8 *data; int res; uint160_zero_out(pubkey); buff_init(&buf, (uint8 *)scriptPubKey, scriptLength); res = script_match_type(&buf, &type, &data, &datalen); if (res) { NOT_TESTED(); return 1; } switch (type) { case TX_PUBKEY: Warning(LGPFX " script TX_PUBKEY\n"); NOT_TESTED(); hash160_calc(data, datalen, pubkey); break; case TX_PUBKEYHASH: ASSERT(datalen == sizeof(uint160)); memcpy(pubkey, data, sizeof(uint160)); break; default: NOT_TESTED(); res = 1; break; } free(data); return res; }
static int bitcui_notify_init(int *readFd, int *writeFd) { int fd[2]; int flags; int res; res = pipe(fd); if (res != 0) { res = errno; Log(LGPFX " Failed to create pipe: %s\n", strerror(res)); return res; } *readFd = fd[0]; *writeFd = fd[1]; flags = fcntl(*readFd, F_GETFL, 0); if (flags < 0) { NOT_TESTED(); return flags; } res = fcntl(*readFd, F_SETFL, flags | O_NONBLOCK); if (res < 0) { NOT_TESTED(); return res; } poll_callback_device(btcui->poll, btcui->eventFd, 1, 0, 1, bitcui_notify_cb, NULL); btcui->notifyInit = 1; return 0; }
bool key_sign(struct key *k, const void *data, size_t datalen, uint8 **sig, size_t *siglen) { unsigned int len; uint8 *sig0; int res; ASSERT(sig); ASSERT(siglen); len = ECDSA_size(k->key); sig0 = safe_calloc(1, len); res = ECDSA_sign(0, data, datalen, sig0, &len, k->key); if (res != 1) { NOT_TESTED(); free(sig0); return 0; } *sig = sig0; *siglen = len; return 1; }
static void txdb_select_coins(struct txdb *txdb, const struct btc_tx_desc *desc, btc_msg_tx *tx, uint64 *change) { struct txo_entry *txo_array; uint64 value; int txo_num; int i; i = 0; value = 0; tx->in_count = 0; txo_array = txdb_get_coins_sorted(txdb); txo_num = hashtable_getnumentries(txdb->hash_txo); /* * txo_array is sorted in chronological order, so we'll be consuming old * coins first. */ Log(LGPFX" select_coins: total_value=%llu fee=%llu\n", desc->total_value, desc->fee); while (value < (desc->total_value + desc->fee) && i < txo_num) { struct txo_entry *txo_ent = &txo_array[i++]; char hashStr[80]; if (txo_ent->spent == 1 || txo_ent->spendable == 0) { continue; } if (uint256_iszero(&txo_ent->blkHash)) { NOT_TESTED(); continue; } uint256_snprintf_reverse(hashStr, sizeof hashStr, &txo_ent->txHash); Log(LGPFX" using txo for %s id=%3u of %s\n", txo_ent->btc_addr, txo_ent->outIdx, hashStr); value += txo_ent->value; memcpy(&tx->tx_in[tx->in_count].prevTxHash, &txo_ent->txHash, sizeof txo_ent->txHash); tx->tx_in[tx->in_count].prevTxOutIdx = txo_ent->outIdx; tx->tx_in[tx->in_count].sequence = UINT_MAX; tx->in_count++; } ASSERT(value >= desc->total_value); *change = value - desc->total_value - desc->fee; Log(LGPFX" change=%llu\n", *change); free(txo_array); }
static struct wallet * wallet_open_file(struct config *config, struct secure_area *pass, char **errStr, enum wallet_state *wallet_state) { struct wallet *wallet; struct config *wcfg; int res; *wallet_state = WALLET_UNKNOWN; wallet = safe_calloc(1, sizeof *wallet); wallet->filename = wallet_get_filename(); wallet->hash_keys = hashtable_create(); wallet->pass = pass; wallet->ckey_store = secure_alloc(sizeof *wallet->ckey); wallet->ckey = (struct crypt_key *)wallet->ckey_store->buf; if (!file_exists(wallet->filename)) { wcfg = config_create(); } else { res = config_load(wallet->filename, &wcfg); if (res) { *errStr = "failed to read wallet file"; NOT_TESTED(); goto exit; } } res = wallet_load_keys(wallet, errStr, wcfg, wallet_state); config_free(wcfg); if (res) { goto exit; } ASSERT(wallet); return wallet; exit: wallet_close(wallet); return NULL; }
static bool key_get_pubkey_int(struct key *k, uint8 **pub, size_t *len) { uint8 *data; ASSERT(pub); *pub = NULL; *len = 0; if (!EC_KEY_check_key(k->key)) { NOT_TESTED(); return 0; } *len = i2o_ECPublicKey(k->key, 0); ASSERT(*len <= 65); data = safe_malloc(*len); *pub = data; i2o_ECPublicKey(k->key, &data); return 1; }
int64 config_getint64(struct config *config, int64 defaultValue, const char *format, ...) { struct KeyValuePair *e; char key[1024]; va_list ap; ASSERT(config); ASSERT(format); va_start(ap, format); vsnprintf(key, sizeof key, format, ap); va_end(ap); e = config_get(config, key); if (e) { if (e->type == CONFIG_KV_UNKNOWN) { #ifdef __CYGWIN__ int64 v = atol(e->u.str); NOT_TESTED(); #else int64 v = atoll(e->u.str); #endif free(e->u.str); e->u.val = v; e->type = CONFIG_KV_INT64; } else { ASSERT(e->type == CONFIG_KV_INT64); } return e->u.val; } else { config_setint64(config, defaultValue, "%s", key); e = config_get(config, key); e->save = 0; return defaultValue; } }
static int script_match_type(struct buff *buf, enum script_txout_type *type, uint8 **data_addr, size_t *data_len) { struct script *script; ASSERT(type); ASSERT(data_addr); ASSERT(data_len); *data_addr = NULL; script = script_parse(buf); if (script == NULL) { NOT_TESTED(); return 1; } *type = script_classify(script); switch (*type) { case TX_PUBKEY: *data_len = script->inst[0].len; *data_addr = safe_malloc(*data_len); memcpy(*data_addr, script->inst[0].data, *data_len); ASSERT(*data_addr); break; case TX_PUBKEYHASH: *data_len = script->inst[2].len; *data_addr = safe_malloc(*data_len); ASSERT(*data_len == sizeof(uint160)); memcpy(*data_addr, script->inst[2].data, *data_len); break; default: *data_addr = NULL; *data_len = 0; break; } script_free(script); return 0; }
static void txdb_remove_from_hashtable(struct txdb *txdb, const uint256 *txHash) { struct tx_entry *txe; char hashStr[80]; bool s; txe = txdb_get_tx_entry(txdb, txHash); if (txe == NULL || txe->relevant == 1) { NOT_TESTED(); return; } txdb_free_tx_entry(txe); uint256_snprintf_reverse(hashStr, sizeof hashStr, txHash); s = hashtable_remove(txdb->hash_tx, txHash, sizeof *txHash); Warning(LGPFX" %s removed from hash_tx: %d (count=%u)\n", hashStr, s, hashtable_getnumentries(txdb->hash_tx)); }
int deserialize_str(struct buff *buf, char *str, size_t size) { uint64 len; int res; memset(str, 0, size); res = deserialize_varint(buf, &len); if (res) { return res; } if (len == 0) { return 0; } if (len >= size) { NOT_TESTED(); return 1; } str[size - 1] = '\0'; if (len < size) { str[len] = '\0'; } return deserialize_bytes(buf, str, len); }
static int blockset_open_file(struct blockstore *blockStore, struct blockset *bs) { uint64 offset; mtime_t ts; int res; res = file_open(bs->filename, 0 /* R/O */, 0 /* !unbuf */, &bs->desc); if (res) { return res; } bs->filesize = file_getsize(bs->desc); if (bs->filesize < 0) { return errno; } if (bs->filesize > 0) { char *s = print_size(bs->filesize); char *name = file_getname(bs->filename); Log(LGPFX" reading file %s -- %s -- %llu headers.\n", name, s, bs->filesize / sizeof(btc_block_header)); free(name); free(s); } ts = time_get(); offset = 0; while (offset < bs->filesize) { btc_block_header buf[10000]; size_t numRead; size_t numBytes; int numHeaders; int i; numBytes = MIN(bs->filesize - offset, sizeof buf); res = file_pread(bs->desc, offset, buf, numBytes, &numRead); if (res != 0) { break; } if (btc->stop != 0) { res = 1; NOT_TESTED(); break; } numHeaders = numRead / sizeof(btc_block_header); for (i = 0; i < numHeaders; i++) { struct blockentry *be; uint256 hash; be = blockstore_alloc_entry(buf + i); be->written = 1; hash256_calc(buf + i, sizeof buf[0], &hash); if (!blockstore_validate_chkpt(&hash, blockStore->height + 1)) { return 1; } blockstore_add_entry(blockStore, be, &hash); if (i == numHeaders - 1) { bitcui_set_status("loading headers .. %llu%%", (offset + numBytes) * 100 / bs->filesize); } if (i == numHeaders - 1 || (numBytes < sizeof buf && i > numHeaders - 256)) { bitcui_set_last_block_info(&hash, blockStore->height, be->header.timestamp); } } offset += numRead; } ts = time_get() - ts; char hashStr[80]; char *latStr; uint256_snprintf_reverse(hashStr, sizeof hashStr, &blockStore->best_hash); Log(LGPFX" loaded blocks up to %s\n", hashStr); latStr = print_latency(ts); Log(LGPFX" this took %s\n", latStr); free(latStr); return res; }
static int txdb_remember_tx(struct txdb *txdb, bool alreadySaved, mtime_t ts, const uint8 *buf, size_t len, const uint256 *txHash, const uint256 *blkHash, bool *relevant) { struct tx_entry *txe; char hashStr[80]; int res; ASSERT(txHash); ASSERT(relevant); *relevant = 0; /* * We always store the tx in the memory pool, that way we know quickly * whether we've already seen it. */ res = txdb_add_to_hashtable(txdb, buf, len, txHash, blkHash, ts, &txe); if (res) { NOT_TESTED(); return res; } uint256_snprintf_reverse(hashStr, sizeof hashStr, txHash); txdb_process_tx_entry(txdb, txHash, blkHash, &txe->tx, &txe->relevant); if (txe->relevant == 0) { Warning(LGPFX" tx %s not relevant (%u)\n", hashStr, hashtable_getnumentries(txdb->hash_tx)); return 0; } if (alreadySaved) { return 0; } /* * OK -- this transaction is relevant to our wallet. */ *relevant = 1; Warning(LGPFX" tx %s ok (%u)\n", hashStr, hashtable_getnumentries(txdb->hash_tx)); res = txdb_save_tx(txdb, blkHash, txHash, ts, buf, len); if (res == 0) { txdb->tx_seq++; } txdb_export_tx_info(txdb); if (bitc_state_ready()) { int64 value = txdb_get_tx_credit(&txe->tx) - txdb_get_tx_debit(&txe->tx); bitcui_set_status("New payment %s: %.8f BTC", value > 0 ? "received" : "made", 1.0 * value / ONE_BTC); } return res; }
void txdb_confirm_one_tx(struct txdb *txdb, const uint256 *blkHash, const uint256 *txHash) { leveldb_iterator_t *iter; struct tx_entry *txe; char bkHashStr[80]; char txHashStr[80]; ASSERT(!uint256_iszero(blkHash)); ASSERT(!uint256_iszero(txHash)); txe = txdb_get_tx_entry(txdb, txHash); if (txe == NULL) { return; } if (txe->relevant == 0) { txdb_remove_from_hashtable(txdb, txHash); NOT_TESTED(); return; } if (!uint256_iszero(&txe->blkHash)) { /* * It's possible for the ASSERT below to fire if a tx is confirmed in * a block that is later orphaned. The tx should then be relayed again * until it finds its way in a new block. */ ASSERT(uint256_issame(&txe->blkHash, blkHash)); return; } peergroup_stop_broadcast_tx(btc->peerGroup, txHash); memcpy(&txe->blkHash, blkHash, sizeof *blkHash); uint256_snprintf_reverse(bkHashStr, sizeof bkHashStr, blkHash); uint256_snprintf_reverse(txHashStr, sizeof txHashStr, txHash); Warning(LGPFX" %s confirmed in %s\n", txHashStr, bkHashStr); NOT_TESTED(); iter = leveldb_create_iterator(txdb->db, txdb->rd_opts); leveldb_iter_seek_to_first(iter); while (leveldb_iter_valid(iter)) { struct tx_ser_key *txkey; struct tx_ser_data *txdata; struct buff *buf; const char *key; const char *val; size_t klen; size_t vlen; char *err = NULL; key = leveldb_iter_key(iter, &klen); txkey = txdb_deserialize_tx_key(key, klen); if (txkey == NULL || uint256_issame(txHash, &txkey->txHash) == 0) { free(txkey); leveldb_iter_next(iter); continue; } NOT_TESTED(); val = leveldb_iter_value(iter, &vlen); txdata = txdb_deserialize_tx_data(val, vlen); ASSERT(uint256_iszero(&txdata->blkHash)); ASSERT(txdata->timestamp != 0); memcpy(&txdata->blkHash, blkHash, sizeof *blkHash); buf = txdb_serialize_tx_data(txdata); leveldb_put(txdb->db, txdb->wr_opts, key, klen, buff_base(buf), buff_curlen(buf), &err); buff_free(buf); if (err) { Warning(LGPFX" failed to write tx entry: %s\n", err); } txdb_export_tx_info(txdb); free(txkey); free(txdata->buf); free(txdata); break; } leveldb_iter_destroy(iter); }
int script_sign(struct wallet *wallet, const struct btc_msg_tx_out *txo, struct btc_msg_tx *tx, uint32 idx, enum script_hash_type hashType) { struct btc_msg_tx_in *txi; enum script_txout_type type; struct buff *scriptSig; struct buff scriptPubKey; uint8 *data_addr; size_t data_len; uint256 hash; int res; ASSERT(idx < tx->in_count); scriptSig = buff_alloc(); buff_init(&scriptPubKey, txo->scriptPubKey, txo->scriptLength); Log_Bytes("scriptPubKey:", txo->scriptPubKey, txo->scriptLength); script_tx_sighash(wallet, &hash, &scriptPubKey, tx, idx, hashType); res = script_match_type(&scriptPubKey, &type, &data_addr, &data_len); if (res) { NOT_TESTED(); goto exit; } switch (type) { case TX_PUBKEY: Warning(LGPFX " script TX_PUBKEY\n"); NOT_IMPLEMENTED(); break; case TX_PUBKEYHASH: (void)0; // XXX: clang bug? uint160 *keyHash = (uint160 *)data_addr; ASSERT(data_len == sizeof(uint160)); res = script_sign_hash(wallet, keyHash, &hash, hashType, scriptSig); if (res) { NOT_TESTED(); goto exit; } res = script_push_pubkey(wallet, keyHash, scriptSig); if (res) { NOT_TESTED(); goto exit; } break; default: NOT_IMPLEMENTED(); Warning(LGPFX " script TX_NONSTANDARD\n"); break; } txi = tx->tx_in + idx; txi->scriptLength = buff_curlen(scriptSig); txi->scriptSig = buff_base(scriptSig); exit: free(scriptSig); free(data_addr); return res; }