Status bridgeWatcherStart(Wallet &self) { if (watchers_.end() != watchers_.find(self.id())) return ABC_ERROR(ABC_CC_Error, "Watcher already exists for " + self.id()); watchers_[self.id()].reset(new WatcherInfo(self)); return Status(); }
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; }
Status bridgeWatcherDelete(Wallet &self) { watcherSave(self).log(); // Failure is not fatal watchers_.erase(self.id()); return Status(); }
tABC_CC ABC_BridgeWatcherDelete(Wallet &self, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; watcherSave(self); // Failure is not fatal watchers_.erase(self.id()); return cc; }
static Status watcherFind(WatcherInfo *&result, Wallet &self) { std::string id = self.id(); auto row = watchers_.find(id); if (row == watchers_.end()) return ABC_ERROR(ABC_CC_Synchronizing, "Cannot find watcher for " + id); result = row->second.get(); return Status(); }
tABC_CC ABC_BridgeWatcherStart(Wallet &self, tABC_Error *pError) { tABC_CC cc = ABC_CC_Ok; std::string id = self.id(); if (watchers_.end() != watchers_.find(id)) ABC_RET_ERROR(ABC_CC_Error, ("Watcher already exists for " + id).c_str()); watchers_[id].reset(new WatcherInfo(self)); watcherLoad(self); // Failure is not fatal 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 onReceive(Wallet &wallet, const TxInfo &info, tABC_BitCoin_Event_Callback fCallback, void *pData) { wallet.balanceDirty(); ABC_CHECK(wallet.addresses.markOutputs(info)); // Does the transaction already exist? TxMeta meta; if (!wallet.txs.get(meta, info.ntxid)) { const auto balance = wallet.addresses.balance(info); meta.ntxid = info.ntxid; meta.txid = info.txid; meta.timeCreation = time(nullptr); meta.internal = false; meta.airbitzFeeWanted = 0; meta.airbitzFeeSent = 0; // Receives can accumulate Airbitz fees: const auto airbitzFeeInfo = generalAirbitzFeeInfo(); meta.airbitzFeeWanted = airbitzFeeIncoming(airbitzFeeInfo, balance); logInfo("Airbitz fee: " + std::to_string(meta.airbitzFeeWanted) + " wanted, " + std::to_string(wallet.txs.airbitzFeePending()) + " pending"); // Grab metadata from the address: for (const auto &io: info.ios) { AddressMeta address; if (wallet.addresses.get(address, io.address)) meta.metadata = address.metadata; } ABC_CHECK(gContext->exchangeCache.satoshiToCurrency( meta.metadata.amountCurrency, balance, static_cast<Currency>(wallet.currency()))); // Save the metadata: ABC_CHECK(wallet.txs.save(meta, balance, info.fee)); // Update the GUI: ABC_DebugLog("IncomingBitCoin callback: wallet %s, txid: %s", wallet.id().c_str(), info.txid.c_str()); tABC_AsyncBitCoinInfo async; async.pData = pData; async.eventType = ABC_AsyncEventType_IncomingBitCoin; Status().toError(async.status, ABC_HERE()); async.szWalletUUID = wallet.id().c_str(); async.szTxID = info.txid.c_str(); async.sweepSatoshi = 0; fCallback(&async); } else { // Update the GUI: ABC_DebugLog("BalanceUpdate callback: wallet %s, txid: %s", wallet.id().c_str(), info.txid.c_str()); tABC_AsyncBitCoinInfo async; async.pData = pData; async.eventType = ABC_AsyncEventType_BalanceUpdate; Status().toError(async.status, ABC_HERE()); async.szWalletUUID = wallet.id().c_str(); async.szTxID = info.txid.c_str(); async.sweepSatoshi = 0; fCallback(&async); } return Status(); }
static Status bridgeTxCallback(Wallet &wallet, const libbitcoin::transaction_type &tx, tABC_BitCoin_Event_Callback fAsyncCallback, void *pData) { const auto addresses = wallet.addresses.list(); const auto info = wallet.txCache.txInfo(tx, addresses); // Does this transaction concern us? if (wallet.txCache.isRelevant(tx, addresses)) { // Does the transaction already exist? Tx meta; if (!wallet.txs.get(meta, info.ntxid)) { meta.ntxid = info.ntxid; meta.txid = info.txid; meta.timeCreation = time(nullptr); meta.internal = false; // Grab metadata from the address: TxMetadata metadata; for (const auto &io: info.ios) { Address address; if (wallet.addresses.get(address, io.address)) meta.metadata = address.metadata; } meta.metadata.amountSatoshi = info.balance; meta.metadata.amountFeesMinersSatoshi = info.fee; ABC_CHECK(gContext->exchangeCache.satoshiToCurrency( meta.metadata.amountCurrency, info.balance, static_cast<Currency>(wallet.currency()))); // Save the metadata: ABC_CHECK(wallet.txs.save(meta)); // Update the transaction cache: watcherSave(wallet).log(); // Failure is not fatal wallet.balanceDirty(); ABC_CHECK(wallet.addresses.markOutputs(info.ios)); // Update the GUI: ABC_DebugLog("IncomingBitCoin callback: wallet %s, txid: %s", wallet.id().c_str(), info.txid.c_str()); tABC_AsyncBitCoinInfo async; async.pData = pData; async.eventType = ABC_AsyncEventType_IncomingBitCoin; Status().toError(async.status, ABC_HERE()); async.szWalletUUID = wallet.id().c_str(); async.szTxID = info.txid.c_str(); async.sweepSatoshi = 0; fAsyncCallback(&async); } else { // Update the transaction cache: watcherSave(wallet).log(); // Failure is not fatal wallet.balanceDirty(); ABC_CHECK(wallet.addresses.markOutputs(info.ios)); // Update the GUI: ABC_DebugLog("BalanceUpdate callback: wallet %s, txid: %s", wallet.id().c_str(), info.txid.c_str()); tABC_AsyncBitCoinInfo async; async.pData = pData; async.eventType = ABC_AsyncEventType_BalanceUpdate; Status().toError(async.status, ABC_HERE()); async.szWalletUUID = wallet.id().c_str(); async.szTxID = info.txid.c_str(); async.sweepSatoshi = 0; fAsyncCallback(&async); } } else { ABC_DebugLog("New (irrelevant) transaction: wallet %s, txid: %s", wallet.id().c_str(), info.txid.c_str()); } return Status(); }
static Status bridgeDoSweep(Wallet &wallet, PendingSweep &sweep, tABC_BitCoin_Event_Callback fAsyncCallback, void *pData) { // Find utxos for this address: AddressSet addresses; addresses.insert(sweep.address); auto utxos = wallet.txCache.get_utxos(addresses); // Bail out if there are no funds to sweep: if (!utxos.size()) { // Tell the GUI if there were funds in the past: if (wallet.txCache.has_history(sweep.address)) { ABC_DebugLog("IncomingSweep callback: wallet %s, value: 0", wallet.id().c_str()); tABC_AsyncBitCoinInfo info; info.pData = pData; info.eventType = ABC_AsyncEventType_IncomingSweep; Status().toError(info.status, ABC_HERE()); info.szWalletUUID = wallet.id().c_str(); info.szTxID = nullptr; info.sweepSatoshi = 0; fAsyncCallback(&info); sweep.done = true; } return Status(); } // Build a transaction: bc::transaction_type tx; tx.version = 1; tx.locktime = 0; // Set up the output: Address address; wallet.addresses.getNew(address); bc::transaction_output_type output; ABC_CHECK(outputScriptForAddress(output.script, address.address)); tx.outputs.push_back(output); // Set up the inputs: uint64_t fee, funds; ABC_CHECK(inputsPickMaximum(fee, funds, tx, utxos)); if (outputIsDust(funds)) return ABC_ERROR(ABC_CC_InsufficientFunds, "Not enough funds"); tx.outputs[0].value = funds; // Now sign that: KeyTable keys; keys[sweep.address] = sweep.key; ABC_CHECK(signTx(tx, wallet.txCache, keys)); // Send: bc::data_chunk raw_tx(satoshi_raw_size(tx)); bc::satoshi_save(tx, raw_tx.begin()); ABC_CHECK(broadcastTx(wallet, raw_tx)); // Calculate transaction information: const auto info = wallet.txCache.txInfo(tx, wallet.addresses.list()); // Save the transaction metadata: Tx meta; meta.ntxid = info.ntxid; meta.txid = info.txid; meta.timeCreation = time(nullptr); meta.internal = true; meta.metadata.amountSatoshi = funds; meta.metadata.amountFeesAirbitzSatoshi = 0; ABC_CHECK(gContext->exchangeCache.satoshiToCurrency( meta.metadata.amountCurrency, info.balance, static_cast<Currency>(wallet.currency()))); ABC_CHECK(wallet.txs.save(meta)); // Update the transaction cache: if (wallet.txCache.insert(tx)) watcherSave(wallet).log(); // Failure is not fatal wallet.balanceDirty(); ABC_CHECK(wallet.addresses.markOutputs(info.ios)); // Done: ABC_DebugLog("IncomingSweep callback: wallet %s, txid: %s, value: %d", wallet.id().c_str(), info.txid.c_str(), output.value); tABC_AsyncBitCoinInfo async; async.pData = pData; async.eventType = ABC_AsyncEventType_IncomingSweep; Status().toError(async.status, ABC_HERE()); async.szWalletUUID = wallet.id().c_str(); async.szTxID = info.txid.c_str(); async.sweepSatoshi = output.value; fAsyncCallback(&async); sweep.done = true; return Status(); }