Skip to content

Commit

Permalink
VG-13107 feat(BTC): Use cached balance on bitcoin accounts (#934)
Browse files Browse the repository at this point in the history
* feat(BTC): Use cached balance on bitcoin accounts

On update on bitcoin_outputs, the sql trigger updates the account balance;

* Have separated trigger in bitcoin_inputs

* comment debug raises

* Fix rollback

* Use postgres 14 for tests

* Use postgres 14 for tests

`OR REPLACE` on triggers is only supported from postgres 14

https://www.postgresql.org/docs/14/sql-createtrigger.html

* Remove triggers & update balance after sync

* don't need to upgrade postgres

* format

* Update misleading comments
  • Loading branch information
BertrandD committed Apr 11, 2023
1 parent 1cc4ed4 commit 45c9428
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 15 deletions.
2 changes: 1 addition & 1 deletion core/src/database/DatabaseSessionPool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ namespace ledger {
const std::string &dbName,
const std::string &password = "");

static const int CURRENT_DATABASE_SCHEME_VERSION = 31;
static const int CURRENT_DATABASE_SCHEME_VERSION = 32;

void performDatabaseMigration();
void performDatabaseRollback();
Expand Down
10 changes: 10 additions & 0 deletions core/src/database/migrations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1265,5 +1265,15 @@ namespace ledger {
void rollback<31>(soci::session &sql, api::DatabaseBackendType /*type*/) {
sql << "ALTER TABLE tezos_transactions DROP explorer_id";
}

template <>
void migrate<32>(soci::session &sql, api::DatabaseBackendType /*type*/) {
sql << "ALTER TABLE bitcoin_accounts ADD COLUMN balance BIGINT DEFAULT -1;";
}

template <>
void rollback<32>(soci::session &sql, api::DatabaseBackendType /*type*/) {
sql << "ALTER TABLE bitcoin_accounts DROP COLUMN balance;";
}
} // namespace core
} // namespace ledger
6 changes: 6 additions & 0 deletions core/src/database/migrations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,12 @@ namespace ledger {
void migrate<31>(soci::session &sql, api::DatabaseBackendType type);
template <>
void rollback<31>(soci::session &sql, api::DatabaseBackendType type);

// add explorer uid for tezos transactions
template <>
void migrate<32>(soci::session &sql, api::DatabaseBackendType type);
template <>
void rollback<32>(soci::session &sql, api::DatabaseBackendType type);
} // namespace core
} // namespace ledger

Expand Down
6 changes: 2 additions & 4 deletions core/src/wallet/bitcoin/BitcoinLikeAccount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,10 +577,8 @@ namespace ledger {
return FuturePtr<Amount>::async(getWallet()->getPool()->getThreadPoolExecutionContext(), [=]() -> std::shared_ptr<Amount> {
const auto &uid = self->getAccountUid();
soci::session sql(self->getWallet()->getDatabase()->getReadonlyPool());
auto keychain = self->getKeychain();
// make sure ALL UTXOs are contained within the balance - although no transaction will be crafted with those
constexpr auto worthlessUtxoAmount = 0;
const auto sum = BitcoinLikeUTXODatabaseHelper::sumUTXO(sql, uid, worthlessUtxoAmount);
auto keychain = self->getKeychain();
const auto sum = BitcoinLikeUTXODatabaseHelper::getBalance(sql, uid);
const Amount balance(self->getWallet()->getCurrency(), 0, sum);
self->getWallet()->updateBalanceCache(self->getIndex(), balance);
return std::make_shared<Amount>(balance);
Expand Down
38 changes: 31 additions & 7 deletions core/src/wallet/bitcoin/database/BitcoinLikeUTXODatabaseHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,16 @@ namespace ledger {
return out.size();
}

BigInt BitcoinLikeUTXODatabaseHelper::sumUTXO(soci::session &sql, const std::string &accountUid, int64_t dustAmount) {
const rowset<row> rows = (sql.prepare << "SELECT sum(o.amount)::bigint"
" FROM bitcoin_outputs AS o "
" LEFT OUTER JOIN bitcoin_inputs AS i ON i.previous_tx_uid = o.transaction_uid "
" AND i.previous_output_idx = o.idx"
" WHERE i.previous_tx_uid IS NULL AND o.account_uid = :uid AND o.amount > :dustAmount",
use(accountUid), use(dustAmount));
constexpr auto uncachedBalanceQuery = R"(
SELECT sum(o.amount)::bigint
FROM bitcoin_outputs AS o
LEFT OUTER JOIN bitcoin_inputs AS i ON i.previous_tx_uid = o.transaction_uid
AND i.previous_output_idx = o.idx
WHERE i.previous_tx_uid IS NULL
AND o.account_uid = :uid)";

BigInt getUncachedBalance(soci::session &sql, const std::string &accountUid) {
const rowset<soci::row> rows = (sql.prepare << uncachedBalanceQuery, use(accountUid));
for (auto &row : rows) {
if (row.get_indicator(0) != i_null) {
return row.get<BigInt>(0);
Expand All @@ -102,6 +104,28 @@ namespace ledger {
return BigInt(0);
}

BigInt BitcoinLikeUTXODatabaseHelper::getBalance(soci::session &sql, const std::string &accountUid) {
const rowset<row> rows = (sql.prepare << "SELECT balance from bitcoin_accounts WHERE uid=:uid",
use(accountUid));

for (auto &row : rows) {
if (row.get_indicator(0) != i_null) {
auto balance = row.get<BigInt>(0);
if (balance.isNegative()) {
// We compute balance from DB instead of updating it, to let only the sync (lama-bitcoin or libcore, depending on the coin) doing the update
return getUncachedBalance(sql, accountUid);
}
return balance;
}
}
return BigInt(0);
}

void BitcoinLikeUTXODatabaseHelper::updateBalance(session &sql, const std::string &accountUid) {
const rowset<row> rows = (sql.prepare << std::string{"UPDATE bitcoin_accounts SET balance = ("} + uncachedBalanceQuery + ") WHERE uid = :uid;",
use(accountUid), use(accountUid));
}

std::vector<BitcoinLikeUtxo> BitcoinLikeUTXODatabaseHelper::queryAllUtxos(
soci::session &session,
std::string const &accountUid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ namespace ledger {
int64_t dustAmount,
std::vector<BitcoinLikeBlockchainExplorerOutput> &out);

static BigInt sumUTXO(soci::session &sql,
const std::string &accountUid,
int64_t dustAmount);
static BigInt getBalance(soci::session &sql,
const std::string &accountUid);

// Only used for tests (by libcore btc sync)
static void updateBalance(soci::session &sql,
const std::string &accountUid);

static std::size_t UTXOcount(soci::session &sql,
const std::string &accountUid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
*/
#include "BlockchainExplorerAccountSynchronizer.h"

#include "wallet/bitcoin/database/BitcoinLikeUTXODatabaseHelper.h"

#include <async/FutureUtils.hpp>
#include <async/algorithm.h>
#include <debug/Benchmarker.h>
Expand Down Expand Up @@ -535,6 +537,10 @@ namespace ledger {
buddy->account->getWallet()->getName(), duration.count());
buddy->logger->error("Due to {}, {}", api::to_string(ex.getErrorCode()), ex.getMessage());
return buddy->context;
})
.then(account->getContext(), [account, buddy]() {
soci::session sql(buddy->wallet->getDatabase()->getPool());
BitcoinLikeUTXODatabaseHelper::updateBalance(sql, account->getAccountUid());
});
};

Expand Down

0 comments on commit 45c9428

Please sign in to comment.