diff --git a/src/db/storage.cpp b/src/db/storage.cpp index b6ade74..8d97b4f 100644 --- a/src/db/storage.cpp +++ b/src/db/storage.cpp @@ -3090,12 +3090,21 @@ namespace db expect check_spends(std::vector& out, MDB_cursor& webhooks_cur, MDB_cursor& outputs_cur, const lws::account& user) { const account_id user_id = user.id(); - const webhook_key hook_key{user_id, webhook_type::tx_spend}; - MDB_val key = lmdb::to_val(hook_key); + MDB_val key = lmdb::to_val(user_id); MDB_val value{}; + int err = mdb_cursor_get(&outputs_cur, &key, &value, MDB_SET_KEY); + if (err) + { + if (err == MDB_NOTFOUND) + return success(); + return log_lmdb_error(err, __LINE__, __FILE__); + } + + const webhook_key hook_key{user_id, webhook_type::tx_spend}; + key = lmdb::to_val(hook_key); // Find a tx_spend for user id - int err = mdb_cursor_get(&webhooks_cur, &key, &value, MDB_SET_KEY); + err = mdb_cursor_get(&webhooks_cur, &key, &value, MDB_SET_KEY); for (;;) { if (err) @@ -3111,10 +3120,7 @@ namespace db out.reserve(user.spends().size()); for (const spend& s : user.spends()) { - key = lmdb::to_val(user_id); - value = lmdb::to_val(s.link.height); - err = mdb_cursor_get(&outputs_cur, &key, &value, MDB_GET_BOTH_RANGE); - + err = mdb_cursor_get(&outputs_cur, &key, &value, MDB_FIRST_DUP); expect meta{common_error::kInvalidArgument}; for (;;) { @@ -3125,7 +3131,14 @@ namespace db return meta.error(); if (meta->id == s.source) break; - err = mdb_cursor_get(&outputs_cur, &key, &value, MDB_PREV_DUP); + + const auto height = outputs.get_value(value); + if (!height) + return height.error(); + if (s.link.height < *height) + return log_lmdb_error(MDB_NOTFOUND, __LINE__, __FILE__); + + err = mdb_cursor_get(&outputs_cur, &key, &value, MDB_NEXT_DUP); } out.push_back( diff --git a/tests/unit/db/webhook.test.cpp b/tests/unit/db/webhook.test.cpp index 9730d2b..687519f 100644 --- a/tests/unit/db/webhook.test.cpp +++ b/tests/unit/db/webhook.test.cpp @@ -152,6 +152,43 @@ LWS_CASE("db::storage::*_webhook") EXPECT(result.empty()); } + SECTION("webhooks with empty outs") + { + const boost::uuids::uuid id = boost::uuids::random_generator{}(); + const boost::uuids::uuid id2 = boost::uuids::random_generator{}(); + { + lws::db::webhook_value value{ + lws::db::webhook_dupsort{0, id}, + lws::db::webhook_data{"http://the_url_spend", "the_token_spend"} + }; + lws::db::webhook_value value2{ + lws::db::webhook_dupsort{0, id2}, + lws::db::webhook_data{"http://the_url_spend", "the_token_spend"} + }; + MONERO_UNWRAP( + db.add_webhook(lws::db::webhook_type::tx_spend, account, std::move(value)) + ); + MONERO_UNWRAP( + db.add_webhook(lws::db::webhook_type::tx_confirmation, account, std::move(value2)) + ); + } + + const auto scan_height = lws::db::block_id(lmdb::to_native(last_block.id) + 1); + crypto::hash chain[2] = { + last_block.hash, + crypto::rand() + }; + + lws::account full_account = lws::db::test::make_account(account, view); + full_account.updated(last_block.id); + + 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() == 0); + } + SECTION("storage::update(...) one at a time") { lws::account full_account = lws::db::test::make_account(account, view); @@ -436,7 +473,7 @@ LWS_CASE("db::storage::*_webhook") EXPECT(spends.size() == 1); const auto updated = db.update(last_block.id, chain, {std::addressof(full_account), 1}, nullptr); - EXPECT(!updated.has_error()); + EXPECT(updated.error() == std::error_code{}); EXPECT(updated->accounts_updated == 1); EXPECT(updated->confirm_pubs.size() == 3); EXPECT(updated->spend_pubs.size() == 1);