oxen-core/tests/wallet3_tests/db_schema.cpp

124 lines
4.3 KiB
C++
Raw Normal View History

#include <catch2/catch.hpp>
2023-03-24 01:08:09 +01:00
#include <wallet3/db/walletdb.hpp>
#include <sqlitedb/database.hpp>
TEST_CASE("DB Schema", "[wallet,db]")
{
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
wallet::WalletDB db{fs::path(":memory:"), ""};
SECTION("db schema creation succeeds")
{
REQUIRE_NOTHROW(db.create_schema());
}
// will not throw if schema is already set up
REQUIRE_NOTHROW(db.create_schema());
REQUIRE(db.db.tableExists("blocks"));
SECTION("Insert and fetch block")
{
2021-12-06 23:49:08 +01:00
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO blocks VALUES(?,?,?,?);", 42, 0, "Adams", 0));
std::string hash;
REQUIRE_NOTHROW(hash = db.prepared_get<std::string>("SELECT hash FROM blocks WHERE height = 42"));
REQUIRE(hash == "Adams");
}
SECTION("Insert and fetch transaction")
{
2021-12-06 23:49:08 +01:00
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO blocks VALUES(?,?,?,?);", 0, 0, "foo", 0));
2022-03-08 06:58:59 +01:00
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO transactions VALUES(?,?,?);", 42, 0, "footx"));
std::tuple<std::string, int> res;
REQUIRE_NOTHROW(res = db.prepared_get<std::string, int>("SELECT hash,block FROM transactions WHERE id = 42"));
const auto& [hash,block] = res;
REQUIRE(hash == "footx");
REQUIRE(block == 0);
}
SECTION("Insert and fetch key image")
{
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO key_images VALUES(?,?);", 0, "key_image"));
std::string image;
REQUIRE_NOTHROW(image = db.prepared_get<std::string>("SELECT key_image FROM key_images WHERE id = 0"));
REQUIRE(image == "key_image");
// key image is unique
REQUIRE_THROWS(db.prepared_exec("INSERT INTO key_images VALUES(?,?);", 0, "key_image"));
}
}
TEST_CASE("DB Triggers", "[wallet,db]")
{
Overhaul and fix crypto::{public_key,ec_point,etc.} types - Remove implicit `operator bool` from ec_point/public_key/etc. which was causing all sorts of implicit conversion mess and bugs. - Change ec_point/public_key/etc. to use a `std::array<unsigned char, 32>` (via a base type) rather than a C-array of char that has to be reinterpret_cast<>'ed all over the place. - Add methods to ec_point/public_key/etc. that make it work more like a container of bytes (`.data()`, `.size()`, `operator[]`, `begin()`, `end()`). - Make a generic `crypto::null<T>` that is a constexpr all-0 `T`, rather than the mishmash `crypto::null_hash`, crypto::null_pkey, crypto::hash::null(), and so on. - Replace three metric tons of `crypto::hash blahblah = crypto::null_hash;` with the much simpler `crypto::hash blahblah{};`, because there's no need to make a copy of a null hash in all these cases. (Likewise for a few other null_whatevers). - Remove a whole bunch of `if (blahblah == crypto::null_hash)` and `if (blahblah != crypto::null_hash)` with the more concise `if (!blahblah)` and `if (blahblah)` (which are fine via the newly *explicit* bool conversion operators). - `crypto::signature` becomes a 64-byte container (as above) but with `c()` and `r()` to get the c() and r() data pointers. (Previously `.c` and `.r` were `ec_scalar`s). - Delete with great prejudice CRYPTO_MAKE_COMPARABLE and CRYPTO_MAKE_HASHABLE and all the other utter trash in `crypto/generic-ops.h`. - De-inline functions in very common crypto/*.h files so that they don't have to get compiled 300 times. - Remove the disgusting include-a-C-header-inside-a-C++-namespace garbage from some crypto headers trying to be both a C and *different* C++ header at once. - Remove the toxic, disgusting, shameful `operator&` on ec_scalar, etc. that replace `&x` with `reinterpret_cast x into an unsigned char*`. This was pure toxic waste. - changed some `<<` outputs to fmt - Random other small changes encountered while fixing everything that cascaded out of the above changes.
2022-10-15 03:22:44 +02:00
wallet::WalletDB db{fs::path(":memory:"), ""};
REQUIRE_NOTHROW(db.create_schema());
REQUIRE(db.db.tableExists("blocks"));
2021-12-06 23:49:08 +01:00
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO blocks VALUES(?,?,?,?);", 0, 0, "foo", 0));
2022-03-08 06:58:59 +01:00
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO transactions VALUES(?,?,?);", 0, 0, "footx"));
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO key_images VALUES(?,?);", 0, "key_image"));
2022-03-08 06:58:59 +01:00
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO outputs VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);",
0, 42, 0, 0, 0, 0, false, 0, 0, 0, "out_key", "rct_mask", 0, 0, 0));
SECTION("Confirm output insert triggers")
{
REQUIRE(db.prepared_get<int64_t>("SELECT amount FROM outputs WHERE id = 0") == 42);
2023-03-14 22:30:30 +01:00
REQUIRE(db.overall_balance() == 42);
}
2021-12-06 23:49:08 +01:00
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO blocks VALUES(?,?,?,?);", 1, 0, "bar", 0));
2022-03-08 06:58:59 +01:00
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO transactions VALUES(?,?,?);", 1, 1, "bartx"));
REQUIRE_NOTHROW(db.prepared_exec("INSERT INTO spends VALUES(?,?,?,?);", 0, 0, 1, 1));
SECTION("Confirm spend insert triggers")
{
2023-03-14 22:30:30 +01:00
REQUIRE(db.overall_balance() == 0);
REQUIRE(db.prepared_get<int64_t>("SELECT spent_height FROM outputs WHERE key_image = 0") == 1);
}
REQUIRE(db.prepared_get<int>("SELECT COUNT(*) FROM transactions;") == 2);
// should cascade and remove the transactions with block = 1 inserted above
REQUIRE_NOTHROW(db.prepared_exec("DELETE FROM blocks WHERE height = 1"));
SECTION("Output spend removal trigger")
{
REQUIRE(db.prepared_get<int>("SELECT COUNT(*) FROM transactions;") == 1);
// balance should be 42, and the spend should be removed.
// existing output's spend height should be back to 0.
REQUIRE(db.prepared_get<int>("SELECT COUNT(*) FROM spends;") == 0);
2023-03-14 22:30:30 +01:00
REQUIRE(db.overall_balance() == 42);
REQUIRE(db.prepared_get<int64_t>("SELECT spent_height FROM outputs WHERE key_image = 0") == 0);
}
REQUIRE_NOTHROW(db.prepared_exec("DELETE FROM blocks WHERE height = 0"));
SECTION("Output removal trigger")
{
REQUIRE(db.prepared_get<int>("SELECT COUNT(*) FROM transactions;") == 0);
// balance should be 0, and the output should be removed.
// key image should be removed as nothing references it.
REQUIRE(db.prepared_get<int>("SELECT COUNT(*) FROM outputs;") == 0);
2023-03-14 22:30:30 +01:00
REQUIRE(db.overall_balance() == 0);
REQUIRE(db.prepared_get<int64_t>("SELECT COUNT(*) FROM key_images;") == 0);
}
}
/*
// SQLiteCpp could throw on a bad query, so make sure the exception caught
// is the one we expect.
REQUIRE_THROWS_WITH(db.prepared_get<std::string>("SELECT hash FROM blocks WHERE id = 0"),
Catch::Matchers::Contains("got no rows"));
*/