/** * Filters a transaction list, removing any that aren't found in the * watcher database. * @param aTransactions The array to filter. This will be modified in-place. * @param pCount The array length. This will be updated upon return. */ tABC_CC ABC_BridgeFilterTransactions(Wallet &self, tABC_TxInfo **aTransactions, unsigned int *pCount, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_TxInfo *const *end = aTransactions + *pCount; tABC_TxInfo *const *si = aTransactions; tABC_TxInfo **di = aTransactions; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); while (si < end) { tABC_TxInfo *pTx = *si++; int height; bc::hash_digest txid; if (!bc::decode_hash(txid, pTx->szMalleableTxId)) ABC_RET_ERROR(ABC_CC_ParseError, "Bad txid"); if (watcher->get_tx_height(txid, height)) { *di++ = pTx; } else { ABC_TxFreeTransaction(pTx); } } *pCount = di - aTransactions; exit: return cc; }
tABC_CC ABC_BridgeWatcherConnect(Wallet &self, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_GeneralInfo *ppInfo = NULL; const char *szServer = FALLBACK_OBELISK; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); // Pick a server: if (isTestnet()) { szServer = TESTNET_OBELISK; } else if (ABC_CC_Ok == ABC_GeneralGetInfo(&ppInfo, pError) && 0 < ppInfo->countObeliskServers) { ++gLastObelisk; if (ppInfo->countObeliskServers <= gLastObelisk) gLastObelisk = 0; szServer = ppInfo->aszObeliskServers[gLastObelisk]; } // Connect: ABC_DebugLog("Wallet %s connecting to %s", self.id().c_str(), szServer); watcher->connect(szServer); exit: ABC_GeneralFreeInfo(ppInfo); return cc; }
tABC_CC ABC_BridgeSweepKey(Wallet &self, tABC_U08Buf key, bool compressed, tABC_Sweep_Done_Callback fCallback, void *pData, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; bc::ec_secret ec_key; bc::ec_point ec_addr; bc::payment_address address; PendingSweep sweep; WatcherInfo *watcherInfo = nullptr; ABC_CHECK_NEW(watcherFind(watcherInfo, self)); // Decode key and address: ABC_CHECK_ASSERT(key.size() == ec_key.size(), ABC_CC_Error, "Bad key size"); std::copy(key.begin(), key.end(), ec_key.data()); ec_addr = bc::secret_to_public_key(ec_key, compressed); address.set(pubkeyVersion(), bc::bitcoin_short_hash(ec_addr)); // Start the sweep: sweep.address = address; sweep.key = abcd::wif_key{ec_key, compressed}; sweep.done = false; sweep.fCallback = fCallback; sweep.pData = pData; watcherInfo->sweeping.push_back(sweep); watcherInfo->watcher.watch_address(address); exit: return cc; }
tABC_CC ABC_BridgePrioritizeAddress(Wallet &self, const char *szAddress, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; bc::payment_address addr; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); if (szAddress) { if (!addr.set_encoded(szAddress)) { cc = ABC_CC_Error; ABC_DebugLog("Invalid szAddress %s\n", szAddress); goto exit; } } watcher->prioritize_address(addr); exit: return cc; }
Status watcherFind(Watcher *&result, Wallet &self) { WatcherInfo *watcherInfo = nullptr; ABC_CHECK(watcherFind(watcherInfo, self)); result = &watcherInfo->watcher; return Status(); }
Status watcherSend(Wallet &self, StatusCallback status, DataSlice tx) { Watcher *watcher = nullptr; ABC_CHECK(watcherFind(watcher, self)); watcher->sendTx(status, tx); return Status(); }
Status bridgeWatcherStop(Wallet &self) { Watcher *watcher = nullptr; ABC_CHECK(watcherFind(watcher, self)); watcher->stop(); return Status(); }
Status bridgeWatcherDisconnect(Wallet &self) { Watcher *watcher = nullptr; ABC_CHECK(watcherFind(watcher, self)); watcher->disconnect(); return Status(); }
tABC_CC ABC_BridgeWatcherLoop(Wallet &self, tABC_BitCoin_Event_Callback fAsyncCallback, void *pData, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; Watcher::block_height_callback heightCallback; Watcher::tx_callback txCallback; Watcher::tx_sent_callback sendCallback; Watcher::quiet_callback on_quiet; Watcher::fail_callback failCallback; WatcherInfo *watcherInfo = nullptr; ABC_CHECK_NEW(watcherFind(watcherInfo, self)); watcherInfo->fAsyncCallback = fAsyncCallback; watcherInfo->pData = pData; txCallback = [watcherInfo, fAsyncCallback, pData] (const libbitcoin::transaction_type& tx) { ABC_BridgeTxCallback(watcherInfo, tx, fAsyncCallback, pData); }; watcherInfo->watcher.set_tx_callback(txCallback); heightCallback = [watcherInfo, fAsyncCallback, pData](const size_t height) { if (fAsyncCallback) { tABC_AsyncBitCoinInfo info; info.eventType = ABC_AsyncEventType_BlockHeightChange; info.pData = pData; info.szDescription = "Block height change"; fAsyncCallback(&info); } watcherSave(watcherInfo->wallet); // Failure is not fatal }; watcherInfo->watcher.set_height_callback(heightCallback); on_quiet = [watcherInfo]() { ABC_BridgeQuietCallback(watcherInfo); }; watcherInfo->watcher.set_quiet_callback(on_quiet); failCallback = [watcherInfo]() { tABC_Error error; ABC_BridgeWatcherConnect(watcherInfo->wallet, &error); }; watcherInfo->watcher.set_fail_callback(failCallback); watcherInfo->watcher.loop(); exit: return cc; }
Status watcherSave(Wallet &self) { Watcher *watcher = nullptr; ABC_CHECK(watcherFind(watcher, self)); auto data = watcher->serialize();; ABC_CHECK(fileSave(data, watcherPath(self))); return Status(); }
tABC_CC ABC_BridgeWatcherDisconnect(Wallet &self, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); watcher->disconnect(); exit: return cc; }
static Status watcherLoad(Wallet &self) { Watcher *watcher = nullptr; ABC_CHECK(watcherFind(watcher, self)); DataChunk data; ABC_CHECK(fileLoad(data, watcherPath(self))); if (!watcher->load(data)) return ABC_ERROR(ABC_CC_Error, "Unable to load serialized watcher"); return Status(); }
Status watcherBridgeRawTx(Wallet &self, const char *szTxID, DataChunk &result) { Watcher *watcher = nullptr; ABC_CHECK(watcherFind(watcher, self)); bc::hash_digest txid; if (!bc::decode_hash(txid, szTxID)) return ABC_ERROR(ABC_CC_ParseError, "Bad txid"); auto tx = watcher->find_tx(txid); result.resize(satoshi_raw_size(tx)); bc::satoshi_save(tx, result.begin()); return Status(); }
tABC_CC ABC_BridgeTxBlockHeight(Wallet &self, unsigned int *height, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); *height = watcher->get_last_block_height(); if (*height == 0) { cc = ABC_CC_Synchronizing; } exit: return cc; }
Status bridgeSweepKey(Wallet &self, const std::string &wif, const std::string &address) { WatcherInfo *watcherInfo = nullptr; ABC_CHECK(watcherFind(watcherInfo, self)); // Start the sweep: PendingSweep sweep; sweep.address = address; sweep.key = wif; sweep.done = false; watcherInfo->sweeping.push_back(sweep); self.addressCache.insert(sweep.address); return Status(); }
tABC_CC ABC_BridgeTxHeight(Wallet &self, const char *szTxId, unsigned int *height, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; int height_; bc::hash_digest txid; Watcher *watcher = nullptr; ABC_CHECK_NEW(watcherFind(watcher, self)); if (!bc::decode_hash(txid, szTxId)) ABC_RET_ERROR(ABC_CC_ParseError, "Bad txid"); if (!watcher->get_tx_height(txid, height_)) { cc = ABC_CC_Synchronizing; } *height = height_; exit: return cc; }
tABC_CC ABC_BridgeWatchAddr(Wallet &self, const char *pubAddress, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; ABC_DebugLog("Watching %s for %s", pubAddress, self.id().c_str()); bc::payment_address addr; WatcherInfo *watcherInfo = nullptr; ABC_CHECK_NEW(watcherFind(watcherInfo, self)); if (!addr.set_encoded(pubAddress)) { cc = ABC_CC_Error; ABC_DebugLog("Invalid pubAddress %s\n", pubAddress); goto exit; } watcherInfo->addresses.insert(pubAddress); watcherInfo->watcher.watch_address(addr); exit: return cc; }
Status bridgeWatcherLoop(Wallet &self, tABC_BitCoin_Event_Callback fCallback, void *pData) { WatcherInfo *watcherInfo = nullptr; ABC_CHECK(watcherFind(watcherInfo, self)); // Set up the address-changed callback: auto wakeupCallback = [watcherInfo]() { watcherInfo->watcher.sendWakeup(); }; self.addressCache.wakeupCallbackSet(wakeupCallback); // Set up the address-synced callback: auto doneCallback = [watcherInfo, fCallback, pData]() { ABC_DebugLog("AddressCheckDone callback: wallet %s", watcherInfo->wallet.id().c_str()); tABC_AsyncBitCoinInfo info; info.pData = pData; info.eventType = ABC_AsyncEventType_AddressCheckDone; Status().toError(info.status, ABC_HERE()); info.szWalletUUID = watcherInfo->wallet.id().c_str(); info.szTxID = nullptr; info.sweepSatoshi = 0; fCallback(&info); }; self.addressCache.doneCallbackSet(doneCallback); // Set up new-transaction callback: auto txCallback = [watcherInfo, fCallback, pData] (const libbitcoin::transaction_type &tx) { bridgeTxCallback(watcherInfo->wallet, tx, fCallback, pData).log(); }; watcherInfo->watcher.set_tx_callback(txCallback); // Set up new-block callback: auto heightCallback = [watcherInfo, fCallback, pData](const size_t height) { // Update the GUI: ABC_DebugLog("BlockHeightChange callback: wallet %s", watcherInfo->wallet.id().c_str()); tABC_AsyncBitCoinInfo info; info.pData = pData; info.eventType = ABC_AsyncEventType_BlockHeightChange; Status().toError(info.status, ABC_HERE()); info.szWalletUUID = watcherInfo->wallet.id().c_str(); info.szTxID = nullptr; info.sweepSatoshi = 0; fCallback(&info); watcherSave(watcherInfo->wallet).log(); // Failure is not fatal }; watcherInfo->watcher.set_height_callback(heightCallback); // Set up sweep-trigger callback: auto onQuiet = [watcherInfo, fCallback, pData]() { bridgeQuietCallback(watcherInfo, fCallback, pData); }; watcherInfo->watcher.set_quiet_callback(onQuiet); // Do the loop: watcherInfo->watcher.loop(); // Cancel all callbacks: watcherInfo->watcher.set_quiet_callback(nullptr); watcherInfo->watcher.set_height_callback(nullptr); watcherInfo->watcher.set_tx_callback(nullptr); self.addressCache.wakeupCallbackSet(nullptr); self.addressCache.doneCallbackSet(nullptr); return Status(); }
tABC_CC ABC_BridgeTxDetailsSplit(Wallet &self, const char *szTxID, tABC_TxOutput ***paInputs, unsigned int *pInCount, tABC_TxOutput ***paOutputs, unsigned int *pOutCount, int64_t *pAmount, int64_t *pFees, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; tABC_TxOutput **paInArr = NULL; tABC_TxOutput **paOutArr = NULL; bc::transaction_type tx; unsigned int idx = 0, iCount = 0, oCount = 0; int64_t fees = 0; int64_t totalInSatoshi = 0, totalOutSatoshi = 0, totalMeSatoshi = 0, totalMeInSatoshi = 0; WatcherInfo *watcherInfo = nullptr; ABC_CHECK_NEW(watcherFind(watcherInfo, self)); bc::hash_digest txid; if (!bc::decode_hash(txid, szTxID)) ABC_RET_ERROR(ABC_CC_ParseError, "Bad txid"); tx = watcherInfo->watcher.find_tx(txid); idx = 0; iCount = tx.inputs.size(); paInArr = (tABC_TxOutput **) malloc(sizeof(tABC_TxOutput *) * iCount); for (auto i : tx.inputs) { bc::payment_address addr; bc::extract(addr, i.script); auto prev = i.previous_output; // Create output tABC_TxOutput *out = (tABC_TxOutput *) malloc(sizeof(tABC_TxOutput)); out->input = true; out->szTxId = stringCopy(bc::encode_hash(prev.hash)); out->szAddress = stringCopy(addr.encoded()); auto tx = watcherInfo->watcher.find_tx(prev.hash); if (prev.index < tx.outputs.size()) { out->value = tx.outputs[prev.index].value; totalInSatoshi += tx.outputs[prev.index].value; auto row = watcherInfo->addresses.find(addr.encoded()); if (row != watcherInfo->addresses.end()) totalMeInSatoshi += tx.outputs[prev.index].value; } else { out->value = 0; } paInArr[idx] = out; idx++; } idx = 0; oCount = tx.outputs.size(); paOutArr = (tABC_TxOutput **) malloc(sizeof(tABC_TxOutput *) * oCount); for (auto o : tx.outputs) { bc::payment_address addr; bc::extract(addr, o.script); // Create output tABC_TxOutput *out = (tABC_TxOutput *) malloc(sizeof(tABC_TxOutput)); out->input = false; out->value = o.value; out->szAddress = stringCopy(addr.encoded()); out->szTxId = stringCopy(szTxID); // Do we own this address? auto row = watcherInfo->addresses.find(addr.encoded()); if (row != watcherInfo->addresses.end()) { totalMeSatoshi += o.value; } totalOutSatoshi += o.value; paOutArr[idx] = out; idx++; } fees = totalInSatoshi - totalOutSatoshi; totalMeSatoshi -= totalMeInSatoshi; *paInputs = paInArr; *pInCount = iCount; *paOutputs = paOutArr; *pOutCount = oCount; *pAmount = totalMeSatoshi; *pFees = fees; paInArr = NULL; paOutArr = NULL; exit: ABC_TxFreeOutputs(paInArr, iCount); ABC_TxFreeOutputs(paOutArr, oCount); return cc; }