2021-07-19 20:42:38 +02:00
|
|
|
#include <catch2/catch.hpp>
|
|
|
|
|
|
|
|
#include <wallet3/db_schema.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:"), ""};
|
2021-07-19 20:42:38 +02:00
|
|
|
|
|
|
|
SECTION("db schema creation succeeds")
|
|
|
|
{
|
2022-03-01 00:01:38 +01:00
|
|
|
REQUIRE_NOTHROW(db.create_schema());
|
2021-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// will not throw if schema is already set up
|
2022-03-01 00:01:38 +01:00
|
|
|
REQUIRE_NOTHROW(db.create_schema());
|
2021-07-19 20:42:38 +02:00
|
|
|
|
|
|
|
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));
|
2021-07-19 20:42:38 +02:00
|
|
|
|
|
|
|
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"));
|
2021-07-19 20:42:38 +02:00
|
|
|
|
|
|
|
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:"), ""};
|
2021-07-19 20:42:38 +02:00
|
|
|
|
2022-03-01 00:01:38 +01:00
|
|
|
REQUIRE_NOTHROW(db.create_schema());
|
2021-07-19 20:42:38 +02:00
|
|
|
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"));
|
2021-07-19 20:42:38 +02:00
|
|
|
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));
|
2021-07-19 20:42:38 +02:00
|
|
|
|
|
|
|
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-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
|
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"));
|
2021-07-19 20:42:38 +02:00
|
|
|
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);
|
2021-07-19 20:42:38 +02:00
|
|
|
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);
|
2021-07-19 20:42:38 +02:00
|
|
|
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);
|
2021-07-19 20:42:38 +02:00
|
|
|
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"));
|
|
|
|
*/
|