Skip to content

Commit

Permalink
Fix several bugs:
Browse files Browse the repository at this point in the history
  * lws::account height update should only go up.
  * Webhook confirmations can start after first new block
  * Webhook confirmations could face a rescan
  • Loading branch information
vtnerd committed Jun 3, 2024
1 parent 68916b7 commit 5610ae8
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 35 deletions.
2 changes: 1 addition & 1 deletion src/db/account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ namespace lws

void account::updated(db::block_id new_height) noexcept
{
height_ = new_height;
height_ = std::max(height_, new_height);
spends_.clear();
spends_.shrink_to_fit();
outputs_.clear();
Expand Down
60 changes: 32 additions & 28 deletions src/db/storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2537,38 +2537,42 @@ namespace db
const webhook_event event =
MONERO_UNWRAP(events_by_account_id.get_value<webhook_event>(value));

MDB_val rkey = lmdb::to_val(hook_key);
MDB_val rvalue = lmdb::to_val(event.link_webhook);
MONERO_LMDB_CHECK(mdb_cursor_get(&webhooks_cur, &rkey, &rvalue, MDB_GET_BOTH));

MDB_val okey = lmdb::to_val(user);
MDB_val ovalue = lmdb::to_val(event.link);
MONERO_LMDB_CHECK(mdb_cursor_get(&outputs_cur, &okey, &ovalue, MDB_GET_BOTH));

events.push_back(
webhook_tx_confirmation{
MONERO_UNWRAP(webhooks.get_key(rkey)),
MONERO_UNWRAP(webhooks.get_value(rvalue)),
MONERO_UNWRAP(outputs.get_value<output>(ovalue))
}
);
const block_id this_begin = std::max(begin, event.link.tx.height);
if (this_begin < end)
{
MDB_val rkey = lmdb::to_val(hook_key);
MDB_val rvalue = lmdb::to_val(event.link_webhook);
MONERO_LMDB_CHECK(mdb_cursor_get(&webhooks_cur, &rkey, &rvalue, MDB_GET_BOTH));

MDB_val okey = lmdb::to_val(user);
MDB_val ovalue = lmdb::to_val(event.link);
MONERO_LMDB_CHECK(mdb_cursor_get(&outputs_cur, &okey, &ovalue, MDB_GET_BOTH));

events.push_back(
webhook_tx_confirmation{
MONERO_UNWRAP(webhooks.get_key(rkey)),
MONERO_UNWRAP(webhooks.get_value(rvalue)),
MONERO_UNWRAP(outputs.get_value<output>(ovalue))
}
);

const std::uint32_t requested_confirmations =
events.back().value.second.confirmations;
const std::uint32_t requested_confirmations =
events.back().value.second.confirmations;

events.back().value.second.confirmations =
lmdb::to_native(begin) - lmdb::to_native(event.link.tx.height) + 1;
events.back().value.second.confirmations =
lmdb::to_native(this_begin) - lmdb::to_native(event.link.tx.height) + 1;

// copy next blocks from first
for (const auto block_num : boost::counting_range(lmdb::to_native(begin) + 1, lmdb::to_native(end)))
{
// copy next blocks from first
for (const auto block_num : boost::counting_range(lmdb::to_native(this_begin) + 1, lmdb::to_native(end)))
{
if (requested_confirmations <= events.back().value.second.confirmations)
break;
events.push_back(events.back());
++(events.back().value.second.confirmations);
}
if (requested_confirmations <= events.back().value.second.confirmations)
break;
events.push_back(events.back());
++(events.back().value.second.confirmations);
}
if (requested_confirmations <= events.back().value.second.confirmations)
MONERO_LMDB_CHECK(mdb_cursor_del(&events_cur, 0));
MONERO_LMDB_CHECK(mdb_cursor_del(&events_cur, 0));
}
err = mdb_cursor_get(&events_cur, &key, &value, MDB_NEXT_DUP);
}
return success();
Expand Down
80 changes: 75 additions & 5 deletions tests/unit/db/webhook.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ LWS_CASE("db::storage::*_webhook")
EXPECT(outs.size() == 1);

lws::db::block_info head = last_block;
for (unsigned i = 0; i < 1; ++i)
for (unsigned i = 0; i < 4; ++i)
{
crypto::hash chain[2] = {head.hash, crypto::rand<crypto::hash>()};

Expand All @@ -184,24 +184,26 @@ LWS_CASE("db::storage::*_webhook")
else
EXPECT(updated->confirm_pubs.empty());

full_account.updated(head.id);
head = {lws::db::block_id(lmdb::to_native(head.id) + 1), chain[1]};
const auto next = lws::db::block_id(lmdb::to_native(head.id) + 1);
full_account.updated(next);
head = {next, chain[1]};
}
}

SECTION("storage::update(...) all at once")
{
const crypto::hash chain[5] = {
const crypto::hash chain[6] = {
last_block.hash,
crypto::rand<crypto::hash>(),
crypto::rand<crypto::hash>(),
crypto::rand<crypto::hash>(),
crypto::rand<crypto::hash>(),
crypto::rand<crypto::hash>()
};

lws::account full_account = lws::db::test::make_account(account, view);
full_account.updated(last_block.id);
EXPECT(add_out(full_account, last_block.id, 500));
EXPECT(add_out(full_account, lws::db::block_id(lmdb::to_native(last_block.id) + 1), 500));

const std::vector<lws::db::output> outs = full_account.outputs();
EXPECT(outs.size() == 1);
Expand All @@ -228,6 +230,74 @@ LWS_CASE("db::storage::*_webhook")
EXPECT(updated->confirm_pubs[i].tx_info.payment_id.short_ == outs[0].payment_id.short_);
}
}

SECTION("rescan with existing event")
{
crypto::hash chain[2] = {
last_block.hash,
crypto::rand<crypto::hash>()
};

lws::account full_account = lws::db::test::make_account(account, view);
full_account.updated(last_block.id);
EXPECT(add_out(full_account, last_block.id, 500));

const std::vector<lws::db::output> outs = full_account.outputs();
EXPECT(outs.size() == 1);

auto updated = db.update(last_block.id, chain, {std::addressof(full_account), 1}, nullptr);
EXPECT(updated.has_value());
EXPECT(updated->spend_pubs.empty());
EXPECT(updated->accounts_updated == 1);
EXPECT(updated->confirm_pubs.size() == 1);

EXPECT(updated->confirm_pubs[0].key.user == lws::db::account_id(1));
EXPECT(updated->confirm_pubs[0].key.type == lws::db::webhook_type::tx_confirmation);
EXPECT(updated->confirm_pubs[0].value.first.payment_id == 500);
EXPECT(updated->confirm_pubs[0].value.first.event_id == id);
EXPECT(updated->confirm_pubs[0].value.second.url == "http://the_url");
EXPECT(updated->confirm_pubs[0].value.second.token == "the_token");
EXPECT(updated->confirm_pubs[0].value.second.confirmations == 1);

EXPECT(updated->confirm_pubs[0].tx_info.link == outs[0].link);
EXPECT(updated->confirm_pubs[0].tx_info.spend_meta.id == outs[0].spend_meta.id);
EXPECT(updated->confirm_pubs[0].tx_info.pub == outs[0].pub);
EXPECT(updated->confirm_pubs[0].tx_info.payment_id.short_ == outs[0].payment_id.short_);

// issue a rescan, and ensure that
const std::size_t chain_size = std::end(chain) - std::begin(chain);
const auto new_height = lws::db::block_id(lmdb::to_native(last_block.id) - chain_size);
const auto rescanned =
db.rescan(new_height, {std::addressof(full_account.db_address()), 1});
EXPECT(rescanned.has_value());
EXPECT(rescanned->size() == 1);

{
lws::db::account db_account{
full_account.id(),
lws::db::account_time(1),
full_account.db_address(),
lws::db::view_key{},
new_height,
new_height,
lws::db::account_time(1),
lws::db::account_flags::default_account
};

std::memcpy(
std::addressof(db_account.key),
std::addressof(unwrap(unwrap(full_account.view_key()))),
sizeof(db_account.key)
);
full_account = lws::account{db_account, {}, {}};
}
updated = db.update(new_height, chain, {std::addressof(full_account), 1}, nullptr);
EXPECT(updated.has_value());
EXPECT(updated->spend_pubs.empty());
EXPECT(updated->accounts_updated == 1);
EXPECT(updated->confirm_pubs.size() == 0);
}

SECTION("Add db spend")
{
const boost::uuids::uuid other_id = boost::uuids::random_generator{}();
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/scanner.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ namespace
transaction out{};
EXPECT(
cryptonote::construct_tx_and_get_tx_key(
keys, subaddresses, sources, destinations, keys.m_account_address, {}, out.tx, 0, unused_key,
keys, subaddresses, sources, destinations, keys.m_account_address, {}, out.tx, /* 0, */ unused_key,
out.additional_keys, true, {rct::RangeProofType::RangeProofBulletproof, 2}, use_view_tag
)
);
Expand Down

0 comments on commit 5610ae8

Please sign in to comment.