Merge pull request #1461 from jagerman/ons-counts

Add registered active ONS registrations counts to get_info RPC
This commit is contained in:
Jason Rhinelander 2021-06-10 15:09:08 -03:00 committed by GitHub
commit 138dfeb023
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 125 additions and 89 deletions

View File

@ -51,6 +51,7 @@ enum struct ons_sql_type
get_mappings,
get_mappings_by_owner,
get_mappings_by_owners,
get_mapping_counts,
get_owner,
get_setting,
get_sentinel_end,
@ -455,27 +456,32 @@ bool sql_run_statement(ons_sql_type type, sql_compiled_statement& statement, voi
}
break;
case ons_sql_type::get_mappings_by_owners: /* FALLTHRU */
case ons_sql_type::get_mappings_by_owner: /* FALLTHRU */
case ons_sql_type::get_mappings: /* FALLTHRU */
case ons_sql_type::get_mappings_by_owners: [[fallthrough]];
case ons_sql_type::get_mappings_by_owner: [[fallthrough]];
case ons_sql_type::get_mappings: [[fallthrough]];
case ons_sql_type::get_mapping:
{
if (mapping_record tmp_entry = sql_get_mapping_from_statement(statement))
{
data_loaded = true;
if (type == ons_sql_type::get_mapping)
{
auto *entry = reinterpret_cast<mapping_record *>(context);
*entry = std::move(tmp_entry);
}
*static_cast<mapping_record *>(context) = std::move(tmp_entry);
else
{
auto *records = reinterpret_cast<std::vector<mapping_record> *>(context);
records->emplace_back(std::move(tmp_entry));
}
static_cast<std::vector<mapping_record>*>(context)->push_back(std::move(tmp_entry));
}
}
break;
case ons_sql_type::get_mapping_counts:
{
auto& counts = *static_cast<std::map<mapping_type, int>*>(context);
std::underlying_type_t<mapping_type> type_val;
int count;
get(statement, 0, type_val);
get(statement, 1, count);
counts.emplace(static_cast<mapping_type>(type_val), count);
data_loaded = true;
}
}
}
break;
@ -1550,34 +1556,37 @@ namespace {
bool build_default_tables(name_system_db& ons_db)
{
std::string mappings_columns = R"(
"id" INTEGER PRIMARY KEY NOT NULL,
"type" INTEGER NOT NULL,
"name_hash" VARCHAR NOT NULL,
"encrypted_value" BLOB NOT NULL,
"txid" BLOB NOT NULL,
"owner_id" INTEGER NOT NULL REFERENCES "owner" ("id"),
"backup_owner_id" INTEGER REFERENCES "owner" ("id"),
"update_height" INTEGER NOT NULL,
"expiration_height" INTEGER
id INTEGER PRIMARY KEY NOT NULL,
type INTEGER NOT NULL,
name_hash VARCHAR NOT NULL,
encrypted_value BLOB NOT NULL,
txid BLOB NOT NULL,
owner_id INTEGER NOT NULL REFERENCES owner(id),
backup_owner_id INTEGER REFERENCES owner(id),
update_height INTEGER NOT NULL,
expiration_height INTEGER
)";
const std::string BUILD_TABLE_SQL = R"(
CREATE TABLE IF NOT EXISTS "owner"(
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"address" BLOB NOT NULL UNIQUE
CREATE TABLE IF NOT EXISTS owner(
id INTEGER PRIMARY KEY AUTOINCREMENT,
address BLOB NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS "settings" (
"id" INTEGER PRIMARY KEY NOT NULL,
"top_height" INTEGER NOT NULL,
"top_hash" VARCHAR NOT NULL,
"version" INTEGER NOT NULL,
"pruned_height" INTEGER NOT NULL DEFAULT 0
CREATE TABLE IF NOT EXISTS settings (
id INTEGER PRIMARY KEY NOT NULL,
top_height INTEGER NOT NULL,
top_hash VARCHAR NOT NULL,
version INTEGER NOT NULL,
pruned_height INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS "mappings" ()" + mappings_columns + R"();
CREATE INDEX IF NOT EXISTS "owner_id_index" ON mappings("owner_id");
CREATE INDEX IF NOT EXISTS "backup_owner_id_index" ON mappings("backup_owner_index");
CREATE TABLE IF NOT EXISTS mappings ()" + mappings_columns + R"();
CREATE INDEX IF NOT EXISTS owner_id_index ON mappings(owner_id);
DROP INDEX IF EXISTS backup_owner_id_index;
CREATE INDEX IF NOT EXISTS backup_owner_index ON mappings(backup_owner_id);
CREATE UNIQUE INDEX IF NOT EXISTS name_type_update ON mappings (name_hash, type, update_height DESC);
CREATE INDEX IF NOT EXISTS mapping_type_name_exp ON mappings (type, name_hash, expiration_height DESC);
)";
char *table_err_msg = nullptr;
@ -1595,7 +1604,7 @@ CREATE INDEX IF NOT EXISTS "backup_owner_id_index" ON mappings("backup_owner_ind
bool need_mappings_migration = false;
{
sql_compiled_statement mappings_info{ons_db};
mappings_info.compile(R"(PRAGMA table_info("mappings"))", false);
mappings_info.compile("PRAGMA table_info(mappings)", false);
while (step(mappings_info) == SQLITE_ROW)
{
auto name = get<std::string_view>(mappings_info, 1);
@ -1611,21 +1620,22 @@ CREATE INDEX IF NOT EXISTS "backup_owner_id_index" ON mappings("backup_owner_ind
{
// Earlier version migration: we need "update_height" to exist (if this fails it's fine).
sqlite3_exec(ons_db.db,
R"(ALTER TABLE "mappings" ADD COLUMN "update_height" INTEGER NOT NULL DEFAULT "register_height")",
"ALTER TABLE mappings ADD COLUMN update_height INTEGER NOT NULL DEFAULT register_height",
nullptr /*callback*/, nullptr /*callback ctx*/, nullptr /*errstr*/);
LOG_PRINT_L1("Migrating ONS mappings database to new format");
const std::string migrate = R"(
BEGIN TRANSACTION;
ALTER TABLE "mappings" RENAME TO "mappings_old";
CREATE TABLE "mappings" ()" + mappings_columns + R"();
INSERT INTO "mappings"
SELECT "id", "type", "name_hash", "encrypted_value", "txid", "owner_id", "backup_owner_id", "update_height", NULL
FROM "mappings_old";
DROP TABLE "mappings_old";
CREATE UNIQUE INDEX "name_type_update" ON "mappings" ("name_hash", "type", "update_height" DESC);
CREATE INDEX "owner_id_index" ON mappings("owner_id");
CREATE INDEX "backup_owner_id_index" ON mappings("backup_owner_index");
ALTER TABLE mappings RENAME TO mappings_old;
CREATE TABLE mappings ()" + mappings_columns + R"();
INSERT INTO mappings
SELECT id, type, name_hash, encrypted_value, txid, owner_id, backup_owner_id, update_height, NULL
FROM mappings_old;
DROP TABLE mappings_old;
CREATE UNIQUE INDEX name_type_update ON mappings(name_hash, type, update_height DESC);
CREATE INDEX owner_id_index ON mappings(owner_id);
CREATE INDEX backup_owner_index ON mappings(backup_owner_id);
CREATE INDEX mapping_type_name_exp ON mappings(type, name_hash, expiration_height DESC);
COMMIT TRANSACTION;
)";
@ -1641,7 +1651,7 @@ COMMIT TRANSACTION;
// Updates to add columns; we ignore errors on these since they will fail if the column already
// exists
for (const auto& upgrade : {
R"(ALTER TABLE "settings" ADD COLUMN "pruned_height" INTEGER NOT NULL DEFAULT 0)",
"ALTER TABLE settings ADD COLUMN pruned_height INTEGER NOT NULL DEFAULT 0",
}) {
sqlite3_exec(ons_db.db, upgrade, nullptr /*callback*/, nullptr /*callback ctx*/, nullptr /*errstr*/);
}
@ -1651,12 +1661,12 @@ COMMIT TRANSACTION;
}
const std::string sql_select_mappings_and_owners_prefix = R"(
SELECT "mappings".*, "o1"."address", "o2"."address", MAX("update_height")
FROM "mappings"
JOIN "owner" "o1" ON "mappings"."owner_id" = "o1"."id"
LEFT JOIN "owner" "o2" ON "mappings"."backup_owner_id" = "o2"."id"
SELECT mappings.*, o1.address, o2.address, MAX(update_height)
FROM mappings
JOIN owner o1 ON mappings.owner_id = o1.id
LEFT JOIN owner o2 ON mappings.backup_owner_id = o2.id
)"s;
const std::string sql_select_mappings_and_owners_suffix = R"( GROUP BY "name_hash", "type")";
const std::string sql_select_mappings_and_owners_suffix = " GROUP BY name_hash, type";
struct scoped_db_transaction
{
@ -1713,7 +1723,7 @@ scoped_db_transaction::~scoped_db_transaction()
enum struct db_version { v0, v1_track_updates, v2_full_rows };
auto constexpr DB_VERSION = db_version::v2_full_rows;
constexpr auto EXPIRATION = R"( ("expiration_height" IS NULL OR "expiration_height" >= ?) )"sv;
constexpr auto EXPIRATION = " (expiration_height IS NULL OR expiration_height >= ?) "sv;
} // anon. namespace
@ -1723,31 +1733,38 @@ bool name_system_db::init(cryptonote::Blockchain const *blockchain, cryptonote::
this->db = db;
this->nettype = nettype;
std::string const get_mappings_by_owner_str = sql_select_mappings_and_owners_prefix
+ R"(WHERE ? IN ("o1"."address", "o2"."address"))"
std::string const GET_MAPPINGS_BY_OWNER_STR = sql_select_mappings_and_owners_prefix
+ "WHERE ? IN (o1.address, o2.address)"
+ sql_select_mappings_and_owners_suffix;
std::string const get_mapping_str = sql_select_mappings_and_owners_prefix
+ R"(WHERE "type" = ? AND "name_hash" = ?)"
std::string const GET_MAPPING_STR = sql_select_mappings_and_owners_prefix
+ "WHERE type = ? AND name_hash = ?"
+ sql_select_mappings_and_owners_suffix;
std::string const RESOLVE_STR =
R"(SELECT "encrypted_value", MAX("update_height")
FROM "mappings"
WHERE "type" = ? AND "name_hash" = ? AND)" + std::string{EXPIRATION};
const std::string GET_MAPPING_COUNTS_STR = R"(
SELECT type, COUNT(*) FROM (
SELECT DISTINCT type, name_hash FROM mappings WHERE )" + std::string{EXPIRATION} + R"(
)
GROUP BY type)";
constexpr auto GET_SETTINGS_STR = R"(SELECT * FROM "settings" WHERE "id" = 1)"sv;
constexpr auto GET_OWNER_BY_ID_STR = R"(SELECT * FROM "owner" WHERE "id" = ?)"sv;
constexpr auto GET_OWNER_BY_KEY_STR = R"(SELECT * FROM "owner" WHERE "address" = ?)"sv;
constexpr auto PRUNE_MAPPINGS_STR = R"(DELETE FROM "mappings" WHERE "update_height" >= ?)"sv;
std::string const RESOLVE_STR = R"(
SELECT encrypted_value, MAX(update_height)
FROM mappings
WHERE type = ? AND name_hash = ? AND)" + std::string{EXPIRATION};
constexpr auto PRUNE_OWNERS_STR =
R"(DELETE FROM "owner"
WHERE NOT EXISTS (SELECT * FROM "mappings" WHERE "owner"."id" = "mappings"."owner_id")
AND NOT EXISTS (SELECT * FROM "mappings" WHERE "owner"."id" = "mappings"."backup_owner_id"))"sv;
constexpr auto GET_SETTINGS_STR = "SELECT * FROM settings WHERE id = 1"sv;
constexpr auto GET_OWNER_BY_ID_STR = "SELECT * FROM owner WHERE id = ?"sv;
constexpr auto GET_OWNER_BY_KEY_STR = "SELECT * FROM owner WHERE address = ?"sv;
constexpr auto SAVE_MAPPING_STR = R"(INSERT INTO "mappings" ("type", "name_hash", "encrypted_value", "txid", "owner_id", "backup_owner_id", "update_height", "expiration_height") VALUES (?,?,?,?,?,?,?,?))"sv;
constexpr auto SAVE_OWNER_STR = R"(INSERT INTO "owner" ("address") VALUES (?))"sv;
constexpr auto SAVE_SETTINGS_STR = R"(INSERT OR REPLACE INTO "settings" ("id", "top_height", "top_hash", "version") VALUES (1,?,?,?))"sv;
// Prune queries used when we need to rollback to remove records added after the detach point:
constexpr auto PRUNE_MAPPINGS_STR = "DELETE FROM mappings WHERE update_height >= ?"sv;
constexpr auto PRUNE_OWNERS_STR = R"(
DELETE FROM owner
WHERE NOT EXISTS (SELECT * FROM mappings WHERE owner.id = mappings.owner_id)
AND NOT EXISTS (SELECT * FROM mappings WHERE owner.id = mappings.backup_owner_id))"sv;
constexpr auto SAVE_MAPPING_STR = "INSERT INTO mappings (type, name_hash, encrypted_value, txid, owner_id, backup_owner_id, update_height, expiration_height) VALUES (?,?,?,?,?,?,?,?)"sv;
constexpr auto SAVE_OWNER_STR = "INSERT INTO owner (address) VALUES (?)"sv;
constexpr auto SAVE_SETTINGS_STR = "INSERT OR REPLACE INTO settings (id, top_height, top_hash, version) VALUES (1,?,?,?)"sv;
if (!build_default_tables(*this))
return false;
@ -1800,7 +1817,7 @@ AND NOT EXISTS (SELECT * FROM "mappings" WHERE "owner"."id" = "mappings"."back
for (mapping_record const &record: all_mappings)
hashes.push_back(record.txid);
constexpr auto UPDATE_MAPPING_HEIGHT = R"(UPDATE "mappings" SET "update_height" = ? WHERE "id" = ?)"sv;
constexpr auto UPDATE_MAPPING_HEIGHT = "UPDATE mappings SET update_height = ? WHERE id = ?"sv;
sql_compiled_statement update_mapping_height{*this};
if (!update_mapping_height.compile(UPDATE_MAPPING_HEIGHT, false))
return false;
@ -1817,7 +1834,7 @@ AND NOT EXISTS (SELECT * FROM "mappings" WHERE "owner"."id" = "mappings"."back
if (settings.version < static_cast<decltype(settings.version)>(db_version::v2_full_rows))
{
sql_compiled_statement prune_height{*this};
if (!prune_height.compile(R"(UPDATE "settings" SET "pruned_height" = (SELECT MAX("update_height") FROM "mappings"))", false))
if (!prune_height.compile("UPDATE settings SET pruned_height = (SELECT MAX(update_height) FROM mappings)", false))
return false;
if (step(prune_height) != SQLITE_DONE)
@ -1834,8 +1851,9 @@ AND NOT EXISTS (SELECT * FROM "mappings" WHERE "owner"."id" = "mappings"."back
// Prepare commonly executed sql statements
//
// ---------------------------------------------------------------------------
if (!get_mappings_by_owner_sql.compile(get_mappings_by_owner_str) ||
!get_mapping_sql.compile(get_mapping_str) ||
if (!get_mappings_by_owner_sql.compile(GET_MAPPINGS_BY_OWNER_STR) ||
!get_mapping_sql.compile(GET_MAPPING_STR) ||
!get_mapping_counts_sql.compile(GET_MAPPING_COUNTS_STR) ||
!resolve_sql.compile(RESOLVE_STR) ||
!get_owner_by_id_sql.compile(GET_OWNER_BY_ID_STR) ||
!get_owner_by_key_sql.compile(GET_OWNER_BY_KEY_STR) ||
@ -1893,7 +1911,7 @@ AND NOT EXISTS (SELECT * FROM "mappings" WHERE "owner"."id" = "mappings"."back
// This likely means something external changed the lmdb and/or the ons.db, and we can't
// recover from it: so just drop and recreate the tables completely and rescan from scratch.
char constexpr DROP_TABLE_SQL[] = R"(DROP TABLE IF EXISTS "owner"; DROP TABLE IF EXISTS "settings"; DROP TABLE IF EXISTS "mappings")";
char constexpr DROP_TABLE_SQL[] = "DROP TABLE IF EXISTS owner; DROP TABLE IF EXISTS settings; DROP TABLE IF EXISTS mappings";
sqlite3_exec(db, DROP_TABLE_SQL, nullptr /*callback*/, nullptr /*callback context*/, nullptr);
if (!build_default_tables(*this)) return false;
}
@ -1948,25 +1966,24 @@ std::pair<std::string, std::vector<update_variant>> update_record_query(name_sys
sql.reserve(500);
sql += R"(
INSERT INTO "mappings" ("type", "name_hash", "txid", "update_height", "expiration_height", "owner_id", "backup_owner_id", "encrypted_value")
SELECT "type", "name_hash", ?, ?)";
INSERT INTO mappings (type, name_hash, txid, update_height, expiration_height, owner_id, backup_owner_id, encrypted_value)
SELECT type, name_hash, ?, ?)";
bind.emplace_back(blob_view{tx_hash.data, sizeof(tx_hash)});
bind.emplace_back(height);
constexpr auto suffix = R"(
FROM "mappings" WHERE "type" = ? AND "name_hash" = ? ORDER BY "update_height" DESC LIMIT 1)"sv;
constexpr auto suffix = " FROM mappings WHERE type = ? AND name_hash = ? ORDER BY update_height DESC LIMIT 1"sv;
if (entry.is_renewing())
{
sql += R"(, "expiration_height" + ?, "owner_id", "backup_owner_id", "encrypted_value")";
sql += ", expiration_height + ?, owner_id, backup_owner_id, encrypted_value";
bind.emplace_back(expiry_blocks(ons_db.network_type(), entry.type).value_or(0));
}
else
{
// Updating
sql += R"(, "expiration_height")";
sql += ", expiration_height";
if (entry.field_is_set(ons::extra_field::owner))
{
@ -1981,7 +1998,7 @@ FROM "mappings" WHERE "type" = ? AND "name_hash" = ? ORDER BY "update_height" DE
bind.emplace_back(*opt_id);
}
else
sql += R"(, "owner_id")";
sql += ", owner_id";
if (entry.field_is_set(ons::extra_field::backup_owner))
{
@ -1997,7 +2014,7 @@ FROM "mappings" WHERE "type" = ? AND "name_hash" = ? ORDER BY "update_height" DE
bind.emplace_back(*opt_id);
}
else
sql += R"(, "backup_owner_id")";
sql += ", backup_owner_id";
if (entry.field_is_set(ons::extra_field::encrypted_value))
{
@ -2005,7 +2022,7 @@ FROM "mappings" WHERE "type" = ? AND "name_hash" = ? ORDER BY "update_height" DE
bind.emplace_back(blob_view{entry.encrypted_value});
}
else
sql += R"(, "encrypted_value")";
sql += ", encrypted_value";
}
sql += suffix;
@ -2292,13 +2309,13 @@ std::vector<mapping_record> name_system_db::get_mappings(std::vector<mapping_typ
sql_statement.reserve(sql_select_mappings_and_owners_prefix.size() + EXPIRATION.size() + 70
+ sql_select_mappings_and_owners_suffix.size());
sql_statement += sql_select_mappings_and_owners_prefix;
sql_statement += R"(WHERE "name_hash" = ?)";
sql_statement += "WHERE name_hash = ?";
bind.emplace_back(name_base64_hash);
// Generate string statement
if (types.size())
{
sql_statement += R"( AND "type" IN ()";
sql_statement += " AND type IN (";
for (size_t i = 0; i < types.size(); i++)
{
@ -2335,8 +2352,8 @@ std::vector<mapping_record> name_system_db::get_mappings_by_owners(std::vector<g
std::vector<std::variant<blob_view, uint64_t>> bind;
// Generate string statement
{
constexpr auto SQL_WHERE_OWNER = R"(WHERE ("o1"."address" IN ()"sv;
constexpr auto SQL_OR_BACKUP_OWNER = R"() OR "o2"."address" IN ()"sv;
constexpr auto SQL_WHERE_OWNER = "WHERE (o1.address IN ("sv;
constexpr auto SQL_OR_BACKUP_OWNER = ") OR o2.address IN ("sv;
constexpr auto SQL_SUFFIX = "))"sv;
std::string placeholders;
@ -2395,6 +2412,12 @@ std::vector<mapping_record> name_system_db::get_mappings_by_owner(generic_owner
return result;
}
std::map<mapping_type, int> name_system_db::get_mapping_counts(uint64_t blockchain_height) {
std::map<mapping_type, int> result;
bind_and_run(ons_sql_type::get_mapping_counts, get_mapping_counts_sql, &result, blockchain_height);
return result;
}
settings_record name_system_db::get_settings()
{
settings_record result = {};

View File

@ -305,6 +305,9 @@ struct name_system_db
std::vector<mapping_record> get_mappings_by_owners(std::vector<generic_owner> const &keys, std::optional<uint64_t> blockchain_height = std::nullopt);
settings_record get_settings ();
// Returns the count of each type of ONS registration that is currently active.
std::map<mapping_type, int> get_mapping_counts(uint64_t blockchain_height);
// Resolves a mapping of the given type and name hash. Returns a null optional if the value was
// not found or expired, otherwise returns the encrypted value.
std::optional<mapping_value> resolve(mapping_type type, std::string_view name_hash_b64, uint64_t blockchain_height);
@ -333,6 +336,7 @@ private:
sql_compiled_statement prune_mappings_sql{*this};
sql_compiled_statement prune_owners_sql{*this};
sql_compiled_statement get_mappings_by_owner_sql{*this};
sql_compiled_statement get_mapping_counts_sql{*this};
sql_compiled_statement get_mappings_on_height_and_newer_sql{*this};
};

View File

@ -417,6 +417,13 @@ namespace cryptonote { namespace rpc {
res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
auto ons_counts = m_core.get_blockchain_storage().name_system_db().get_mapping_counts(res.height);
res.ons_counts = {
ons_counts[ons::mapping_type::session],
ons_counts[ons::mapping_type::wallet],
ons_counts[ons::mapping_type::lokinet]};
if (context.admin)
{
res.service_node = m_core.service_node();

View File

@ -314,9 +314,10 @@ KV_SERIALIZE_MAP_CODE_BEGIN(GET_INFO::response)
KV_SERIALIZE(immutable_block_hash)
KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE(block_size_limit)
KV_SERIALIZE_OPT(block_weight_limit, (uint64_t)0)
KV_SERIALIZE(block_weight_limit)
KV_SERIALIZE(block_size_median)
KV_SERIALIZE_OPT(block_weight_median, (uint64_t)0)
KV_SERIALIZE(block_weight_median)
KV_SERIALIZE(ons_counts)
KV_SERIALIZE(start_time)
KV_SERIALIZE(service_node)
KV_SERIALIZE(last_storage_server_ping)

View File

@ -640,6 +640,7 @@ namespace rpc {
uint64_t block_weight_limit; // Maximum allowed block weight.
uint64_t block_size_median; // Median block size of latest 100 blocks.
uint64_t block_weight_median; // Median block weight of latest 100 blocks.
std::array<int, 3> ons_counts; // ONS registration counts, [session, wallet, lokinet]
std::optional<bool> service_node; // Will be true if the node is running in --service-node mode.
std::optional<uint64_t> start_time; // Start time of the daemon, as UNIX time.
std::optional<uint64_t> last_storage_server_ping; // Last ping time of the storage server (0 if never or not running as a service node)