From 83fc5c33d485eb2e03a9bf144a91d43411c9a73d Mon Sep 17 00:00:00 2001 From: Tyler Cloutier Date: Tue, 22 Oct 2024 18:56:20 -0700 Subject: [PATCH] The banishment of Address (#1880) Co-authored-by: Jeffrey Dallatezza --- crates/bench/benches/subscription.rs | 4 +- ...ps__spacetimedb_bindings_dependencies.snap | 1 + crates/cli/src/api.rs | 13 +- crates/cli/src/subcommands/call.rs | 39 ++--- crates/cli/src/subcommands/delete.rs | 8 +- crates/cli/src/subcommands/describe.rs | 10 +- crates/cli/src/subcommands/dns.rs | 34 ++-- crates/cli/src/subcommands/list.rs | 14 +- crates/cli/src/subcommands/logs.rs | 8 +- crates/cli/src/subcommands/publish.rs | 38 +++-- crates/cli/src/subcommands/sql.rs | 10 +- crates/cli/src/util.rs | 28 ++-- crates/client-api-messages/src/name.rs | 38 ++--- crates/client-api-messages/src/name/tests.rs | 6 +- crates/client-api/src/lib.rs | 44 +++-- crates/client-api/src/routes/database.rs | 145 +++++++++-------- crates/client-api/src/routes/identity.rs | 24 ++- crates/client-api/src/routes/subscribe.rs | 12 +- crates/client-api/src/util.rs | 83 +++++----- crates/core/src/client/client_connection.rs | 2 +- crates/core/src/client/message_handlers.rs | 2 +- crates/core/src/database_logger.rs | 6 +- .../locking_tx_datastore/committed_state.rs | 12 +- .../locking_tx_datastore/datastore.rs | 103 ++++++------ .../datastore/locking_tx_datastore/mut_tx.rs | 101 ++++++------ .../locking_tx_datastore/state_view.rs | 6 +- crates/core/src/db/datastore/system_tables.rs | 14 +- crates/core/src/db/datastore/traits.rs | 4 +- crates/core/src/db/db_metrics/mod.rs | 30 ++-- crates/core/src/db/relational_db.rs | 152 +++++++++++------- crates/core/src/db/update.rs | 6 +- crates/core/src/execution_context.rs | 36 ++--- crates/core/src/host/host_controller.rs | 39 +++-- crates/core/src/host/instance_env.rs | 2 +- crates/core/src/host/module_host.rs | 30 ++-- crates/core/src/host/scheduler.rs | 10 +- .../src/host/wasm_common/module_host_actor.rs | 16 +- crates/core/src/messages/control_db.rs | 6 +- crates/core/src/replica_context.rs | 2 +- crates/core/src/sql/execute.rs | 4 +- .../subscription/module_subscription_actor.rs | 10 +- .../module_subscription_manager.rs | 2 +- crates/core/src/subscription/query.rs | 6 +- crates/core/src/util/lending_pool.rs | 8 +- crates/core/src/worker_metrics/mod.rs | 20 +-- crates/lib/Cargo.toml | 1 + crates/lib/src/identity.rs | 10 ++ crates/sdk/src/db_connection.rs | 6 +- crates/snapshot/src/lib.rs | 24 +-- crates/standalone/src/control_db.rs | 89 +++++----- crates/standalone/src/control_db/tests.rs | 4 +- crates/standalone/src/lib.rs | 57 +++---- crates/testing/src/modules.rs | 17 +- smoketests/__init__.py | 4 +- smoketests/tests/domains.py | 2 +- tools/clippy.sh | 2 +- tools/run-all-tests.sh | 2 +- 57 files changed, 739 insertions(+), 667 deletions(-) diff --git a/crates/bench/benches/subscription.rs b/crates/bench/benches/subscription.rs index 84b7c4b9110..1b385830957 100644 --- a/crates/bench/benches/subscription.rs +++ b/crates/bench/benches/subscription.rs @@ -103,7 +103,7 @@ fn eval(c: &mut Criterion) { let tx = raw.db.begin_tx(); let query = compile_read_only_query(&raw.db, &AuthCtx::for_testing(), &tx, sql).unwrap(); let query: ExecutionSet = query.into(); - let ctx = &ExecutionContext::subscribe(raw.db.address()); + let ctx = &ExecutionContext::subscribe(raw.db.database_identity()); b.iter(|| { drop(black_box(query.eval::( ctx, @@ -134,7 +134,7 @@ fn eval(c: &mut Criterion) { ); bench_eval(c, "full-join", &name); - let ctx_incr = &ExecutionContext::incremental_update(raw.db.address()); + let ctx_incr = &ExecutionContext::incremental_update(raw.db.database_identity()); // To profile this benchmark for 30s // samply record -r 10000000 cargo bench --bench=subscription --profile=profiling -- incr-select --exact --profile-time=30 diff --git a/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap b/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap index 7dfef03b9af..f5c0d4f3103 100644 --- a/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap +++ b/crates/bindings/tests/snapshots/deps__spacetimedb_bindings_dependencies.snap @@ -71,6 +71,7 @@ spacetimedb │ │ └── syn (*) │ ├── hex │ ├── itertools (*) +│ ├── rand (*) │ ├── spacetimedb_bindings_macro (*) │ ├── spacetimedb_data_structures │ │ ├── ahash diff --git a/crates/cli/src/api.rs b/crates/cli/src/api.rs index 837a9c3827c..544e5db3722 100644 --- a/crates/cli/src/api.rs +++ b/crates/cli/src/api.rs @@ -5,21 +5,28 @@ use serde_json::value::RawValue; use spacetimedb_lib::db::raw_def::v9::RawModuleDefV9; use spacetimedb_lib::de::serde::DeserializeWrapper; use spacetimedb_lib::sats::ProductType; -use spacetimedb_lib::Address; +use spacetimedb_lib::Identity; static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); #[derive(Debug, Clone)] pub struct Connection { pub(crate) host: String, - pub(crate) address: Address, + pub(crate) database_identity: Identity, pub(crate) database: String, pub(crate) auth_header: Option, } impl Connection { pub fn db_uri(&self, endpoint: &str) -> String { - [&self.host, "/database/", endpoint, "/", &self.address.to_hex()].concat() + [ + &self.host, + "/database/", + endpoint, + "/", + &self.database_identity.to_hex(), + ] + .concat() } } diff --git a/crates/cli/src/subcommands/call.rs b/crates/cli/src/subcommands/call.rs index fff18820ce5..926702e16d0 100644 --- a/crates/cli/src/subcommands/call.rs +++ b/crates/cli/src/subcommands/call.rs @@ -2,14 +2,15 @@ use crate::common_args; use crate::config::Config; use crate::edit_distance::{edit_distance, find_best_match_for_name}; use crate::util; -use crate::util::{add_auth_header_opt, database_address, get_auth_header_only}; +use crate::util::{add_auth_header_opt, database_identity, get_auth_header_only}; use anyhow::{bail, Context, Error}; use clap::{Arg, ArgMatches}; use itertools::Either; use serde_json::Value; +use spacetimedb::Identity; use spacetimedb_lib::de::serde::deserialize_from; use spacetimedb_lib::sats::{AlgebraicType, AlgebraicTypeRef, Typespace}; -use spacetimedb_lib::{Address, ProductTypeElement}; +use spacetimedb_lib::ProductTypeElement; use std::fmt::Write; use std::iter; @@ -19,7 +20,7 @@ pub fn cli() -> clap::Command { .arg( Arg::new("database") .required(true) - .help("The database domain or address to use to invoke the call"), + .help("The database name or identity to use to invoke the call"), ) .arg( Arg::new("reducer_name") @@ -46,19 +47,19 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), Error> { let identity = args.get_one::("identity"); let anon_identity = args.get_flag("anon_identity"); - let address = database_address(&config, database, server).await?; + let database_identity = database_identity(&config, database, server).await?; let builder = reqwest::Client::new().post(format!( "{}/database/call/{}/{}", config.get_host_url(server)?, - address.clone(), + database_identity.clone(), reducer_name )); let auth_header = get_auth_header_only(&mut config, anon_identity, identity, server).await?; let builder = add_auth_header_opt(builder, &auth_header); let describe_reducer = util::describe_reducer( &mut config, - address, + database_identity, server.map(|x| x.to_string()), reducer_name.clone(), anon_identity, @@ -90,11 +91,11 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), Error> { let error = Err(e).context(format!("Response text: {}", response_text)); let error_msg = if response_text.starts_with("no such reducer") { - no_such_reducer(config, &address, database, &auth_header, reducer_name, server).await + no_such_reducer(config, &database_identity, database, &auth_header, reducer_name, server).await } else if response_text.starts_with("invalid arguments") { invalid_arguments( config, - &address, + &database_identity, database, &auth_header, reducer_name, @@ -115,7 +116,7 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), Error> { /// Returns an error message for when `reducer` is called with wrong arguments. async fn invalid_arguments( config: Config, - addr: &Address, + identity: &Identity, db: &str, auth_header: &Option, reducer: &str, @@ -123,8 +124,8 @@ async fn invalid_arguments( server: Option<&str>, ) -> String { let mut error = format!( - "Invalid arguments provided for reducer `{}` for database `{}` resolving to address `{}`.", - reducer, db, addr + "Invalid arguments provided for reducer `{}` for database `{}` resolving to identity `{}`.", + reducer, db, identity ); if let Some((actual, expected)) = find_actual_expected(text).filter(|(a, e)| a != e) { @@ -136,7 +137,7 @@ async fn invalid_arguments( .unwrap(); } - if let Some(sig) = schema_json(config, addr, auth_header, true, server) + if let Some(sig) = schema_json(config, identity, auth_header, true, server) .await .and_then(|schema| reducer_signature(schema, reducer)) { @@ -198,18 +199,18 @@ fn reducer_signature(schema_json: Value, reducer_name: &str) -> Option { /// Returns an error message for when `reducer` does not exist in `db`. async fn no_such_reducer( config: Config, - addr: &Address, + database_identity: &Identity, db: &str, auth_header: &Option, reducer: &str, server: Option<&str>, ) -> String { let mut error = format!( - "No such reducer `{}` for database `{}` resolving to address `{}`.", - reducer, db, addr + "No such reducer `{}` for database `{}` resolving to identity `{}`.", + reducer, db, database_identity ); - if let Some(schema) = schema_json(config, addr, auth_header, false, server).await { + if let Some(schema) = schema_json(config, database_identity, auth_header, false, server).await { add_reducer_ctx_to_err(&mut error, schema, reducer); } @@ -255,12 +256,12 @@ fn add_reducer_ctx_to_err(error: &mut String, schema_json: Value, reducer_name: } } -/// Fetch the schema as JSON for the database at `address`. +/// Fetch the schema as JSON for the database at `identity`. /// /// The value of `expand` determines how detailed information to fetch. async fn schema_json( config: Config, - address: &Address, + identity: &Identity, auth_header: &Option, expand: bool, server: Option<&str>, @@ -268,7 +269,7 @@ async fn schema_json( let builder = reqwest::Client::new().get(format!( "{}/database/schema/{}", config.get_host_url(server).ok()?, - address + identity )); let builder = add_auth_header_opt(builder, auth_header); diff --git a/crates/cli/src/subcommands/delete.rs b/crates/cli/src/subcommands/delete.rs index 19bcc1e0054..c154db309e4 100644 --- a/crates/cli/src/subcommands/delete.rs +++ b/crates/cli/src/subcommands/delete.rs @@ -1,6 +1,6 @@ use crate::common_args; use crate::config::Config; -use crate::util::{add_auth_header_opt, database_address, get_auth_header_only}; +use crate::util::{add_auth_header_opt, database_identity, get_auth_header_only}; use clap::{Arg, ArgMatches}; pub fn cli() -> clap::Command { @@ -9,7 +9,7 @@ pub fn cli() -> clap::Command { .arg( Arg::new("database") .required(true) - .help("The domain or address of the database to delete"), + .help("The name or identity of the database to delete"), ) .arg( common_args::identity() @@ -28,9 +28,9 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E let database = args.get_one::("database").unwrap(); let identity_or_name = args.get_one::("identity"); - let address = database_address(&config, database, server).await?; + let identity = database_identity(&config, database, server).await?; - let builder = reqwest::Client::new().post(format!("{}/database/delete/{}", config.get_host_url(server)?, address)); + let builder = reqwest::Client::new().post(format!("{}/database/delete/{}", config.get_host_url(server)?, identity)); let auth_header = get_auth_header_only(&mut config, false, identity_or_name, server).await?; let builder = add_auth_header_opt(builder, &auth_header); builder.send().await?.error_for_status()?; diff --git a/crates/cli/src/subcommands/describe.rs b/crates/cli/src/subcommands/describe.rs index 4d53a2f0348..dc9a40d9b6f 100644 --- a/crates/cli/src/subcommands/describe.rs +++ b/crates/cli/src/subcommands/describe.rs @@ -1,6 +1,6 @@ use crate::common_args; use crate::config::Config; -use crate::util::{add_auth_header_opt, database_address, get_auth_header_only}; +use crate::util::{add_auth_header_opt, database_identity, get_auth_header_only}; use clap::{Arg, ArgMatches}; pub fn cli() -> clap::Command { @@ -9,7 +9,7 @@ pub fn cli() -> clap::Command { .arg( Arg::new("database") .required(true) - .help("The domain or address of the database to describe"), + .help("The name or identity of the database to describe"), ) .arg( Arg::new("entity_type") @@ -46,14 +46,14 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E let identity = args.get_one::("identity"); let anon_identity = args.get_flag("anon_identity"); - let address = database_address(&config, database, server).await?; + let database_identity = database_identity(&config, database, server).await?; let builder = reqwest::Client::new().get(match entity_name { - None => format!("{}/database/schema/{}", config.get_host_url(server)?, address), + None => format!("{}/database/schema/{}", config.get_host_url(server)?, database_identity), Some(entity_name) => format!( "{}/database/schema/{}/{}/{}", config.get_host_url(server)?, - address, + database_identity, entity_type.unwrap(), entity_name ), diff --git a/crates/cli/src/subcommands/dns.rs b/crates/cli/src/subcommands/dns.rs index cec4c61fa01..75c6ad4a152 100644 --- a/crates/cli/src/subcommands/dns.rs +++ b/crates/cli/src/subcommands/dns.rs @@ -40,7 +40,7 @@ fn get_subcommands() -> Vec { ) .after_help("Run `spacetime dns register-tld --help` for more detailed information.\n"), Command::new("lookup") - .about("Resolves a domain to a database address") + .about("Resolves a domain to a database identity") .arg(Arg::new("domain").required(true).help("The name of the domain to lookup")) .arg( common_args::server() @@ -48,17 +48,17 @@ fn get_subcommands() -> Vec { ) .after_help("Run `spacetime dns lookup --help` for more detailed information"), Command::new("reverse-lookup") - .about("Returns the domains for the provided database address") - .arg(Arg::new("address").required(true).help("The address you would like to find all of the known domains for")) + .about("Returns the domains for the provided database identity") + .arg(Arg::new("database-identity").required(true).help("The database identity you would like to find all of the known domains for")) .arg( common_args::server() - .help("The nickname, host name or URL of the server on which to look up the address"), + .help("The nickname, host name or URL of the server on which to look up the database identity"), ) .after_help("Run `spacetime dns reverse-lookup --help` for more detailed information.\n"), Command::new("set-name") .about("Sets the domain of the database") .arg(Arg::new("domain").required(true).help("The domain you would like to assign or create")) - .arg(Arg::new("address").required(true).help("The database address to assign to the domain")) + .arg(Arg::new("database-identity").required(true).help("The database identity to assign to the domain")) .arg(common_args::identity().long_help( "The identity that owns the tld for this domain. If no identity is specified, the default identity is used.", ).help("The identity that owns the tld for this domain")) @@ -107,8 +107,8 @@ pub async fn exec_dns_lookup(config: Config, args: &ArgMatches) -> Result<(), an let response = spacetime_dns(&config, domain, server).await?; match response { - DnsLookupResponse::Success { domain: _, address } => { - println!("{}", address); + DnsLookupResponse::Success { domain: _, identity } => { + println!("{}", identity); } DnsLookupResponse::Failure { domain } => { println!("No such database: {}", domain); @@ -118,11 +118,14 @@ pub async fn exec_dns_lookup(config: Config, args: &ArgMatches) -> Result<(), an } pub async fn exec_reverse_dns(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> { - let addr = args.get_one::("address").unwrap(); + let database_identity = args.get_one::("database-identity").unwrap(); let server = args.get_one::("server").map(|s| s.as_ref()); - let response = spacetime_reverse_dns(&config, addr, server).await?; + let response = spacetime_reverse_dns(&config, database_identity, server).await?; if response.names.is_empty() { - Err(anyhow::anyhow!("Could not find a name for the address: {}", addr)) + Err(anyhow::anyhow!( + "Could not find a name for the identity: {}", + database_identity + )) } else { for name in response.names { println!("{}", name); @@ -133,7 +136,7 @@ pub async fn exec_reverse_dns(config: Config, args: &ArgMatches) -> Result<(), a pub async fn exec_set_name(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> { let domain = args.get_one::("domain").unwrap(); - let address = args.get_one::("address").unwrap(); + let database_identity = args.get_one::("database-identity").unwrap(); let identity = args.get_one::("identity"); let server = args.get_one::("server").map(|s| s.as_ref()); let auth_header = get_auth_header_only(&mut config, false, identity, server).await?; @@ -142,7 +145,7 @@ pub async fn exec_set_name(mut config: Config, args: &ArgMatches) -> Result<(), format!("{}/database/set_name", config.get_host_url(server)?).as_str(), [ ("domain", domain.clone()), - ("address", address.clone()), + ("database_identity", database_identity.clone()), ("register_tld", "true".to_string()), ], )?); @@ -153,8 +156,11 @@ pub async fn exec_set_name(mut config: Config, args: &ArgMatches) -> Result<(), println!("{}", String::from_utf8_lossy(&bytes[..])); let result: InsertDomainResult = serde_json::from_slice(&bytes[..]).unwrap(); match result { - InsertDomainResult::Success { domain, address } => { - println!("Domain set to {} for address {}.", domain, address); + InsertDomainResult::Success { + domain, + database_identity, + } => { + println!("Domain set to {} for identity {}.", domain, database_identity); } InsertDomainResult::TldNotRegistered { domain } => { return Err(anyhow::anyhow!( diff --git a/crates/cli/src/subcommands/list.rs b/crates/cli/src/subcommands/list.rs index fe435916a00..7f8ec335005 100644 --- a/crates/cli/src/subcommands/list.rs +++ b/crates/cli/src/subcommands/list.rs @@ -4,7 +4,7 @@ use anyhow::Context; use clap::{ArgMatches, Command}; use reqwest::StatusCode; use serde::Deserialize; -use spacetimedb_lib::Address; +use spacetimedb::Identity; use tabled::{ settings::{object::Columns, Alignment, Modify, Style}, Table, Tabled, @@ -23,13 +23,13 @@ pub fn cli() -> Command { #[derive(Deserialize)] struct DatabasesResult { - pub addresses: Vec, + pub identities: Vec, } #[derive(Tabled, Deserialize)] #[serde(transparent)] -struct AddressRow { - pub db_address: Address, +struct IdentityRow { + pub db_identity: Identity, } pub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> { @@ -64,12 +64,12 @@ pub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error let result: DatabasesResult = res.json().await?; let identity = identity_config.nick_or_identity(); - if !result.addresses.is_empty() { - let mut table = Table::new(result.addresses); + if !result.identities.is_empty() { + let mut table = Table::new(result.identities); table .with(Style::psql()) .with(Modify::new(Columns::first()).with(Alignment::left())); - println!("Associated database addresses for {}:\n", identity); + println!("Associated database identities for {}:\n", identity); println!("{}", table); } else { println!("No databases found for {}.", identity); diff --git a/crates/cli/src/subcommands/logs.rs b/crates/cli/src/subcommands/logs.rs index 4d121e5937b..71cf2a20788 100644 --- a/crates/cli/src/subcommands/logs.rs +++ b/crates/cli/src/subcommands/logs.rs @@ -3,7 +3,7 @@ use std::io::{self, Write}; use crate::common_args; use crate::config::Config; -use crate::util::{add_auth_header_opt, database_address, get_auth_header_only}; +use crate::util::{add_auth_header_opt, database_identity, get_auth_header_only}; use clap::{Arg, ArgAction, ArgMatches}; use futures::{AsyncBufReadExt, TryStreamExt}; use is_terminal::IsTerminal; @@ -16,7 +16,7 @@ pub fn cli() -> clap::Command { .arg( Arg::new("database") .required(true) - .help("The domain or address of the database to print logs from"), + .help("The name or identity of the database to print logs from"), ) .arg( common_args::server() @@ -123,7 +123,7 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E let auth_header = get_auth_header_only(&mut config, false, identity, server).await?; - let address = database_address(&config, database, server).await?; + let database_identity = database_identity(&config, database, server).await?; if follow && num_lines.is_none() { // We typically don't want logs from the very beginning if we're also following. @@ -133,7 +133,7 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E let host_url = config.get_host_url(server)?; - let builder = reqwest::Client::new().get(format!("{}/database/logs/{}", host_url, address)); + let builder = reqwest::Client::new().get(format!("{}/database/logs/{}", host_url, database_identity)); let builder = add_auth_header_opt(builder, &auth_header); let mut res = builder.query(&query_parms).send().await?; let status = res.status(); diff --git a/crates/cli/src/subcommands/publish.rs b/crates/cli/src/subcommands/publish.rs index 529891787f5..c67929e0621 100644 --- a/crates/cli/src/subcommands/publish.rs +++ b/crates/cli/src/subcommands/publish.rs @@ -4,7 +4,7 @@ use clap::ArgAction::{Set, SetTrue}; use clap::ArgMatches; use reqwest::{StatusCode, Url}; use spacetimedb_client_api_messages::name::PublishOp; -use spacetimedb_client_api_messages::name::{is_address, parse_domain_name, PublishResult}; +use spacetimedb_client_api_messages::name::{is_identity, parse_domain_name, PublishResult}; use std::fs; use std::path::PathBuf; @@ -21,8 +21,8 @@ pub fn cli() -> clap::Command { .long("delete-data") .short('c') .action(SetTrue) - .requires("name|address") - .help("When publishing to an existing address, first DESTROY all data associated with the module"), + .requires("name|identity") + .help("When publishing to an existing database identity, first DESTROY all data associated with the module"), ) .arg( Arg::new("build_options") @@ -60,8 +60,8 @@ pub fn cli() -> clap::Command { common_args::anonymous() ) .arg( - Arg::new("name|address") - .help("A valid domain or address for this database"), + Arg::new("name|identity") + .help("A valid domain or identity for this database"), ) .arg(common_args::server() .help("The nickname, domain name or URL of the server to host the database."), @@ -75,7 +75,7 @@ pub fn cli() -> clap::Command { pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> { let server = args.get_one::("server").map(|s| s.as_str()); let identity = args.get_one::("identity").map(String::as_str); - let name_or_address = args.get_one::("name|address"); + let name_or_identity = args.get_one::("name|identity"); let path_to_project = args.get_one::("project_path").unwrap(); let clear_database = args.get_flag("clear_database"); let force = args.get_flag("force"); @@ -96,13 +96,13 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E query_params.push(("host_type", "wasm")); query_params.push(("register_tld", "true")); - // If a domain or address was provided, we should locally make sure it looks correct and + // If a domain or identity was provided, we should locally make sure it looks correct and // append it as a query parameter - if let Some(name_or_address) = name_or_address { - if !is_address(name_or_address) { - parse_domain_name(name_or_address)?; + if let Some(name_or_identity) = name_or_identity { + if !is_identity(name_or_identity) { + parse_domain_name(name_or_identity)?; } - query_params.push(("name_or_address", name_or_address.as_str())); + query_params.push(("name_or_identity", name_or_identity.as_str())); } if !path_to_project.exists() { @@ -130,16 +130,16 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E ); if clear_database { - // Note: `name_or_address` should be set, because it is `required` in the CLI arg config. + // Note: `name_or_identity` should be set, because it is `required` in the CLI arg config. println!( "This will DESTROY the current {} module, and ALL corresponding data.", - name_or_address.unwrap() + name_or_identity.unwrap() ); if !y_or_n( force, format!( "Are you sure you want to proceed? [deleting {}]", - name_or_address.unwrap() + name_or_identity.unwrap() ) .as_str(), )? { @@ -177,15 +177,19 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E let response: PublishResult = serde_json::from_slice(&bytes[..]).unwrap(); match response { - PublishResult::Success { domain, address, op } => { + PublishResult::Success { + domain, + database_identity, + op, + } => { let op = match op { PublishOp::Created => "Created new", PublishOp::Updated => "Updated", }; if let Some(domain) = domain { - println!("{} database with domain: {}, address: {}", op, domain, address); + println!("{} database with name: {}, identity: {}", op, domain, database_identity); } else { - println!("{} database with address: {}", op, address); + println!("{} database with identity: {}", op, database_identity); } } PublishResult::TldNotRegistered { domain } => { diff --git a/crates/cli/src/subcommands/sql.rs b/crates/cli/src/subcommands/sql.rs index c0efbb66a6c..c63b6a21dba 100644 --- a/crates/cli/src/subcommands/sql.rs +++ b/crates/cli/src/subcommands/sql.rs @@ -11,7 +11,7 @@ use tabled::settings::Style; use crate::config::Config; use crate::errors::error_for_status; -use crate::util::{database_address, get_auth_header_only}; +use crate::util::{database_identity, get_auth_header_only}; pub fn cli() -> clap::Command { clap::Command::new("sql") @@ -19,7 +19,7 @@ pub fn cli() -> clap::Command { .arg( Arg::new("database") .required(true) - .help("The domain or address of the database you would like to query"), + .help("The name or identity of the database you would like to query"), ) .arg( Arg::new("query") @@ -49,15 +49,15 @@ pub fn cli() -> clap::Command { pub(crate) async fn parse_req(mut config: Config, args: &ArgMatches) -> Result { let server = args.get_one::("server").map(|s| s.as_ref()); - let database = args.get_one::("database").unwrap(); + let database_name_or_identity = args.get_one::("database").unwrap(); let identity = args.get_one::("identity"); let anon_identity = args.get_flag("anon_identity"); Ok(Connection { host: config.get_host_url(server)?, auth_header: get_auth_header_only(&mut config, anon_identity, identity, server).await?, - address: database_address(&config, database, server).await?, - database: database.to_string(), + database_identity: database_identity(&config, database_name_or_identity, server).await?, + database: database_name_or_identity.to_string(), }) } diff --git a/crates/cli/src/util.rs b/crates/cli/src/util.rs index c8d410307c0..58c89d64ee9 100644 --- a/crates/cli/src/util.rs +++ b/crates/cli/src/util.rs @@ -4,24 +4,28 @@ use reqwest::RequestBuilder; use serde::Deserialize; use spacetimedb_client_api_messages::name::{DnsLookupResponse, RegisterTldResult, ReverseDNSResponse}; use spacetimedb_data_structures::map::HashMap; -use spacetimedb_lib::{Address, AlgebraicType, Identity}; +use spacetimedb_lib::{AlgebraicType, Identity}; use std::io::Write; use std::path::Path; use crate::config::{Config, IdentityConfig}; -/// Determine the address of the `database`. -pub async fn database_address(config: &Config, database: &str, server: Option<&str>) -> Result { - if let Ok(address) = Address::from_hex(database) { - return Ok(address); +/// Determine the identity of the `database`. +pub async fn database_identity( + config: &Config, + name_or_identity: &str, + server: Option<&str>, +) -> Result { + if let Ok(identity) = Identity::from_hex(name_or_identity) { + return Ok(identity); } - match spacetime_dns(config, database, server).await? { - DnsLookupResponse::Success { domain: _, address } => Ok(address), + match spacetime_dns(config, name_or_identity, server).await? { + DnsLookupResponse::Success { domain: _, identity } => Ok(identity), DnsLookupResponse::Failure { domain } => Err(anyhow::anyhow!("The dns resolution of `{}` failed.", domain)), } } -/// Converts a name to a database address. +/// Converts a name to a database identity. pub async fn spacetime_dns( config: &Config, domain: &str, @@ -62,14 +66,14 @@ pub async fn spacetime_server_fingerprint(url: &str) -> anyhow::Result { Ok(fingerprint) } -/// Returns all known names for the given address. +/// Returns all known names for the given identity. pub async fn spacetime_reverse_dns( config: &Config, - address: &str, + identity: &str, server: Option<&str>, ) -> Result { let client = reqwest::Client::new(); - let url = format!("{}/database/reverse_dns/{}", config.get_host_url(server)?, address); + let url = format!("{}/database/reverse_dns/{}", config.get_host_url(server)?, identity); let res = client.get(url).send().await?.error_for_status()?; let bytes = res.bytes().await.unwrap(); Ok(serde_json::from_slice(&bytes[..]).unwrap()) @@ -185,7 +189,7 @@ pub struct DescribeElementName { pub async fn describe_reducer( config: &mut Config, - database: Address, + database: Identity, server: Option, reducer_name: String, anon_identity: bool, diff --git a/crates/client-api-messages/src/name.rs b/crates/client-api-messages/src/name.rs index 77542da24be..0678992303b 100644 --- a/crates/client-api-messages/src/name.rs +++ b/crates/client-api-messages/src/name.rs @@ -1,7 +1,7 @@ use spacetimedb_sats::{impl_deserialize, impl_serialize, impl_st}; use std::{borrow::Borrow, fmt, ops::Deref, str::FromStr}; -use spacetimedb_lib::Address; +use spacetimedb_lib::Identity; #[cfg(test)] mod tests; @@ -10,7 +10,7 @@ mod tests; pub enum InsertDomainResult { Success { domain: DomainName, - address: Address, + database_identity: Identity, }, /// The top level domain for the database name is not registered. For example: @@ -18,9 +18,7 @@ pub enum InsertDomainResult { /// - `clockworklabs/bitcraft` /// /// if `clockworklabs` is not registered, this error is returned. - TldNotRegistered { - domain: DomainName, - }, + TldNotRegistered { domain: DomainName }, /// The top level domain for the database name is registered, but the identity that you provided does /// not have permission to insert the given database name. For example: @@ -30,9 +28,7 @@ pub enum InsertDomainResult { /// If you were trying to insert this database name, but the tld `clockworklabs` is /// owned by an identity other than the identity that you provided, then you will receive /// this error. - PermissionDenied { - domain: DomainName, - }, + PermissionDenied { domain: DomainName }, /// Some unspecified error occurred. OtherError(String), @@ -52,14 +48,14 @@ pub enum PublishResult { /// otherwise. /// /// In other words, this echoes back a domain name if one was given. If - /// the database name given was in fact a database address, this will be + /// the database name given was in fact a database identity, this will be /// `None`. domain: Option, - /// The address of the published database. + /// The identity of the published database. /// /// Always set, regardless of whether publish resolved a domain name first /// or not. - address: Address, + database_identity: Identity, op: PublishOp, }, @@ -84,8 +80,8 @@ pub enum PublishResult { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub enum DnsLookupResponse { - /// The lookup was successful and the domain and address are returned. - Success { domain: DomainName, address: Address }, + /// The lookup was successful and the domain and identity are returned. + Success { domain: DomainName, identity: Identity }, /// There was no domain registered with the given domain name Failure { domain: DomainName }, @@ -428,11 +424,11 @@ pub struct ReverseDNSResponse { pub names: Vec, } -/// Returns whether a hex string is a valid address. +/// Returns whether a hex string is a valid identity. /// -/// Any string that is a valid address is an invalid database name. -pub fn is_address(hex: &str) -> bool { - Address::from_hex(hex).is_ok() +/// Any string that is a valid identity is an invalid database name. +pub fn is_identity(hex: &str) -> bool { + Identity::from_hex(hex).is_ok() } #[derive(thiserror::Error, Debug)] @@ -445,8 +441,8 @@ pub struct DomainParsingError(#[from] ParseError); enum ParseError { #[error("Database names cannot be empty")] Empty, - #[error("Addresses cannot be database names: `{part}`")] - Address { part: String }, + #[error("Identities cannot be database names: `{part}`")] + Identity { part: String }, #[error("Database names must not start with a slash: `{input}`")] StartsSlash { input: String }, #[error("Database names must not end with a slash: `{input}`")] @@ -521,8 +517,8 @@ fn ensure_domain_tld(input: &str) -> Result<(), ParseError> { let DomainSegment(input) = DomainSegment::try_from(input)?; if input.contains('/') { Err(ParseError::ContainsSlash { part: input.to_owned() }) - } else if is_address(input) { - Err(ParseError::Address { part: input.to_owned() }) + } else if is_identity(input) { + Err(ParseError::Identity { part: input.to_owned() }) } else { Ok(()) } diff --git a/crates/client-api-messages/src/name/tests.rs b/crates/client-api-messages/src/name/tests.rs index b6c75541377..fd5dbe4bed4 100644 --- a/crates/client-api-messages/src/name/tests.rs +++ b/crates/client-api-messages/src/name/tests.rs @@ -111,16 +111,16 @@ proptest! { } #[test] - fn prop_tld_cannot_be_address(addr_bytes in any::<[u8; 16]>()) { + fn prop_tld_cannot_be_identity(addr_bytes in any::<[u8; 32]>()) { let addr = hex::encode(addr_bytes); assert!(matches!( parse_domain_name(addr), - Err(DomainParsingError(ParseError::Address { .. })) + Err(DomainParsingError(ParseError::Identity { .. })) )) } #[test] - fn prop_but_tld_can_be_some_other_hex_value(bytes in any::<[u8; 32]>()) { + fn prop_but_tld_can_be_some_other_hex_value(bytes in any::<[u8; 16]>()) { let addr = hex::encode(bytes); parse_domain_name(addr)?; } diff --git a/crates/client-api/src/lib.rs b/crates/client-api/src/lib.rs index 37527f9a95a..f0c5dadb1e4 100644 --- a/crates/client-api/src/lib.rs +++ b/crates/client-api/src/lib.rs @@ -4,7 +4,6 @@ use async_trait::async_trait; use axum::response::ErrorResponse; use http::StatusCode; -use spacetimedb::address::Address; use spacetimedb::auth::identity::{DecodingKey, EncodingKey}; use spacetimedb::client::ClientActorIndex; use spacetimedb::energy::{EnergyBalance, EnergyQuanta}; @@ -46,10 +45,10 @@ pub trait NodeDelegate: Send + Sync { /// /// See [`ControlStateDelegate::publish_database`]. pub struct DatabaseDef { - /// The [`Address`] the database shall have. + /// The [`Identity`] the database shall have. /// /// Addresses are allocated via [`ControlStateDelegate::create_address`]. - pub address: Address, + pub database_identity: Identity, /// The compiled program of the database module. pub program_bytes: Vec, /// The desired number of replicas the database shall have. @@ -89,7 +88,7 @@ pub trait ControlStateReadAccess { // Databases fn get_database_by_id(&self, id: u64) -> anyhow::Result>; - fn get_database_by_address(&self, address: &Address) -> anyhow::Result>; + fn get_database_by_identity(&self, database_identity: &Identity) -> anyhow::Result>; fn get_databases(&self) -> anyhow::Result>; // Replicas @@ -101,16 +100,13 @@ pub trait ControlStateReadAccess { fn get_energy_balance(&self, identity: &Identity) -> anyhow::Result>; // DNS - fn lookup_address(&self, domain: &DomainName) -> anyhow::Result>; - fn reverse_lookup(&self, address: &Address) -> anyhow::Result>; + fn lookup_identity(&self, domain: &DomainName) -> anyhow::Result>; + fn reverse_lookup(&self, database_identity: &Identity) -> anyhow::Result>; } /// Write operations on the SpacetimeDB control plane. #[async_trait] pub trait ControlStateWriteAccess: Send + Sync { - // Databases - async fn create_address(&self) -> anyhow::Result
; - /// Publish a database acc. to [`DatabaseDef`]. /// /// If the database with the given address was successfully published before, @@ -121,11 +117,11 @@ pub trait ControlStateWriteAccess: Send + Sync { /// initialized. async fn publish_database( &self, - identity: &Identity, + publisher: &Identity, spec: DatabaseDef, ) -> anyhow::Result>; - async fn delete_database(&self, identity: &Identity, address: &Address) -> anyhow::Result<()>; + async fn delete_database(&self, caller_identity: &Identity, database_identity: &Identity) -> anyhow::Result<()>; // Energy async fn add_energy(&self, identity: &Identity, amount: EnergyQuanta) -> anyhow::Result<()>; @@ -137,7 +133,7 @@ pub trait ControlStateWriteAccess: Send + Sync { &self, identity: &Identity, domain: &DomainName, - address: &Address, + database_identity: &Identity, ) -> anyhow::Result; } @@ -157,8 +153,8 @@ impl ControlStateReadAccess for Arc { fn get_database_by_id(&self, id: u64) -> anyhow::Result> { (**self).get_database_by_id(id) } - fn get_database_by_address(&self, address: &Address) -> anyhow::Result> { - (**self).get_database_by_address(address) + fn get_database_by_identity(&self, identity: &Identity) -> anyhow::Result> { + (**self).get_database_by_identity(identity) } fn get_databases(&self) -> anyhow::Result> { (**self).get_databases() @@ -181,21 +177,17 @@ impl ControlStateReadAccess for Arc { } // DNS - fn lookup_address(&self, domain: &DomainName) -> anyhow::Result> { - (**self).lookup_address(domain) + fn lookup_identity(&self, domain: &DomainName) -> anyhow::Result> { + (**self).lookup_identity(domain) } - fn reverse_lookup(&self, address: &Address) -> anyhow::Result> { - (**self).reverse_lookup(address) + fn reverse_lookup(&self, database_identity: &Identity) -> anyhow::Result> { + (**self).reverse_lookup(database_identity) } } #[async_trait] impl ControlStateWriteAccess for Arc { - async fn create_address(&self) -> anyhow::Result
{ - (**self).create_address().await - } - async fn publish_database( &self, identity: &Identity, @@ -204,8 +196,8 @@ impl ControlStateWriteAccess for Arc { (**self).publish_database(identity, spec).await } - async fn delete_database(&self, identity: &Identity, address: &Address) -> anyhow::Result<()> { - (**self).delete_database(identity, address).await + async fn delete_database(&self, caller_identity: &Identity, database_identity: &Identity) -> anyhow::Result<()> { + (**self).delete_database(caller_identity, database_identity).await } async fn add_energy(&self, identity: &Identity, amount: EnergyQuanta) -> anyhow::Result<()> { @@ -223,9 +215,9 @@ impl ControlStateWriteAccess for Arc { &self, identity: &Identity, domain: &DomainName, - address: &Address, + database_identity: &Identity, ) -> anyhow::Result { - (**self).create_dns_record(identity, domain, address).await + (**self).create_dns_record(identity, domain, database_identity).await } } diff --git a/crates/client-api/src/routes/database.rs b/crates/client-api/src/routes/database.rs index 00071e926a2..a2a8342074d 100644 --- a/crates/client-api/src/routes/database.rs +++ b/crates/client-api/src/routes/database.rs @@ -3,7 +3,7 @@ use crate::auth::{ SpacetimeIdentity, SpacetimeIdentityToken, }; use crate::routes::subscribe::generate_random_address; -use crate::util::{ByteStringBody, NameOrAddress}; +use crate::util::{ByteStringBody, NameOrIdentity}; use crate::{log_and_500, ControlStateDelegate, DatabaseDef, NodeDelegate}; use axum::body::{Body, Bytes}; use axum::extract::{DefaultBodyLimit, Path, Query, State}; @@ -35,6 +35,8 @@ use spacetimedb_lib::ser::serde::SerializeWrapper; use spacetimedb_lib::ProductTypeElement; use spacetimedb_schema::def::{ReducerDef, TableDef}; +use super::identity::IdentityForUrl; + pub(crate) struct DomainParsingRejection; impl IntoResponse for DomainParsingRejection { @@ -45,7 +47,7 @@ impl IntoResponse for DomainParsingRejection { #[derive(Deserialize)] pub struct CallParams { - name_or_address: NameOrAddress, + name_or_identity: NameOrIdentity, reducer: String, } @@ -58,7 +60,7 @@ pub async fn call( State(worker_ctx): State, Extension(auth): Extension, Path(CallParams { - name_or_address, + name_or_identity: name_or_address, reducer, }): Path, Query(CallQueryParams { client_address }): Query, @@ -181,10 +183,10 @@ pub struct DatabaseInformation { async fn extract_db_call_info( ctx: &(impl ControlStateDelegate + NodeDelegate + ?Sized), auth: SpacetimeAuth, - address: &Address, + database_identity: &Identity, ) -> Result { - let database = worker_ctx_find_database(ctx, address).await?.ok_or_else(|| { - log::error!("Could not find database: {}", address.to_hex()); + let database = worker_ctx_find_database(ctx, database_identity).await?.ok_or_else(|| { + log::error!("Could not find database: {}", database_identity.to_hex()); (StatusCode::NOT_FOUND, "No such database.") })?; @@ -249,7 +251,7 @@ fn entity_description_json(description: WithTypespace) -> Option( State(worker_ctx): State, Path(DescribeParams { - name_or_address, + name_or_identity, entity_type, entity, }): Path, @@ -266,7 +268,7 @@ pub async fn describe( where S: ControlStateDelegate + NodeDelegate, { - let address = name_or_address.resolve(&worker_ctx).await?.into(); + let address = name_or_identity.resolve(&worker_ctx).await?.into(); let database = worker_ctx_find_database(&worker_ctx, &address) .await? .ok_or((StatusCode::NOT_FOUND, "No such database."))?; @@ -318,7 +320,7 @@ fn get_entity<'a>(host: &'a ModuleHost, entity: &'_ str, entity_type: DescribedE #[derive(Deserialize)] pub struct CatalogParams { - name_or_address: NameOrAddress, + name_or_identity: NameOrIdentity, } #[derive(Deserialize)] pub struct CatalogQueryParams { @@ -327,14 +329,14 @@ pub struct CatalogQueryParams { } pub async fn catalog( State(worker_ctx): State, - Path(CatalogParams { name_or_address }): Path, + Path(CatalogParams { name_or_identity }): Path, Query(CatalogQueryParams { module_def }): Query, Extension(auth): Extension, ) -> axum::response::Result where S: ControlStateDelegate + NodeDelegate, { - let address = name_or_address.resolve(&worker_ctx).await?.into(); + let address = name_or_identity.resolve(&worker_ctx).await?.into(); let database = worker_ctx_find_database(&worker_ctx, &address) .await? .ok_or((StatusCode::NOT_FOUND, "No such database."))?; @@ -376,14 +378,14 @@ where #[derive(Deserialize)] pub struct InfoParams { - name_or_address: NameOrAddress, + name_or_identity: NameOrIdentity, } pub async fn info( State(worker_ctx): State, - Path(InfoParams { name_or_address }): Path, + Path(InfoParams { name_or_identity }): Path, ) -> axum::response::Result { - log::trace!("Trying to resolve address: {:?}", name_or_address); - let address = name_or_address.resolve(&worker_ctx).await?.into(); + log::trace!("Trying to resolve address: {:?}", name_or_identity); + let address = name_or_identity.resolve(&worker_ctx).await?.into(); log::trace!("Resolved address to: {address:?}"); let database = worker_ctx_find_database(&worker_ctx, &address) .await? @@ -392,7 +394,7 @@ pub async fn info( let host_type: &str = database.host_type.as_ref(); let response_json = json!({ - "address": database.address, + "address": database.database_identity, "owner_identity": database.owner_identity, "host_type": host_type, "initial_program": database.initial_program, @@ -402,7 +404,7 @@ pub async fn info( #[derive(Deserialize)] pub struct LogsParams { - name_or_address: NameOrAddress, + name_or_identity: NameOrIdentity, } #[derive(Deserialize)] @@ -414,7 +416,7 @@ pub struct LogsQuery { pub async fn logs( State(worker_ctx): State, - Path(LogsParams { name_or_address }): Path, + Path(LogsParams { name_or_identity }): Path, Query(LogsQuery { num_lines, follow }): Query, Extension(auth): Extension, ) -> axum::response::Result @@ -424,8 +426,8 @@ where // You should not be able to read the logs from a database that you do not own // so, unless you are the owner, this will fail. - let address = name_or_address.resolve(&worker_ctx).await?.into(); - let database = worker_ctx_find_database(&worker_ctx, &address) + let database_identity: Identity = name_or_identity.resolve(&worker_ctx).await?.into(); + let database = worker_ctx_find_database(&worker_ctx, &database_identity) .await? .ok_or((StatusCode::NOT_FOUND, "No such database."))?; @@ -446,7 +448,7 @@ where .ok_or((StatusCode::NOT_FOUND, "Replica not scheduled to this node yet."))?; let replica_id = replica.id; - let filepath = DatabaseLogger::filepath(&address, replica_id); + let filepath = DatabaseLogger::filepath(&database_identity, replica_id); let lines = DatabaseLogger::read_latest(&filepath, num_lines).await; let body = if follow { @@ -461,7 +463,11 @@ where std::future::ready(match x { Ok(log) => Some(log), Err(tokio_stream::wrappers::errors::BroadcastStreamRecvError::Lagged(skipped)) => { - log::trace!("Skipped {} lines in log for module {}", skipped, address.to_hex()); + log::trace!( + "Skipped {} lines in log for module {}", + skipped, + database_identity.to_hex() + ); None } }) @@ -490,14 +496,16 @@ fn mime_ndjson() -> mime::Mime { async fn worker_ctx_find_database( worker_ctx: &(impl ControlStateDelegate + ?Sized), - address: &Address, + database_identity: &Identity, ) -> axum::response::Result> { - worker_ctx.get_database_by_address(address).map_err(log_and_500) + worker_ctx + .get_database_by_identity(database_identity) + .map_err(log_and_500) } #[derive(Deserialize)] pub struct SqlParams { - name_or_address: NameOrAddress, + name_or_identity: NameOrIdentity, } #[derive(Deserialize)] @@ -505,7 +513,7 @@ pub struct SqlQueryParams {} pub async fn sql( State(worker_ctx): State, - Path(SqlParams { name_or_address }): Path, + Path(SqlParams { name_or_identity }): Path, Query(SqlQueryParams {}): Query, Extension(auth): Extension, body: String, @@ -516,7 +524,7 @@ where // Anyone is authorized to execute SQL queries. The SQL engine will determine // which queries this identity is allowed to execute against the database. - let address = name_or_address.resolve(&worker_ctx).await?.into(); + let address = name_or_identity.resolve(&worker_ctx).await?.into(); let database = worker_ctx_find_database(&worker_ctx, &address) .await? .ok_or((StatusCode::NOT_FOUND, "No such database."))?; @@ -585,7 +593,7 @@ pub struct DNSParams { #[derive(Deserialize)] pub struct ReverseDNSParams { - database_address: AddressForUrl, + database_identity: IdentityForUrl, } #[derive(Deserialize)] @@ -597,9 +605,12 @@ pub async fn dns( Query(DNSQueryParams {}): Query, ) -> axum::response::Result { let domain = database_name.parse().map_err(|_| DomainParsingRejection)?; - let address = ctx.lookup_address(&domain).map_err(log_and_500)?; - let response = if let Some(address) = address { - DnsLookupResponse::Success { domain, address } + let db_identity = ctx.lookup_identity(&domain).map_err(log_and_500)?; + let response = if let Some(db_identity) = db_identity { + DnsLookupResponse::Success { + domain, + identity: db_identity, + } } else { DnsLookupResponse::Failure { domain } }; @@ -609,9 +620,9 @@ pub async fn dns( pub async fn reverse_dns( State(ctx): State, - Path(ReverseDNSParams { database_address }): Path, + Path(ReverseDNSParams { database_identity }): Path, ) -> axum::response::Result { - let database_address = Address::from(database_address); + let database_address = Identity::from(database_identity); let names = ctx.reverse_lookup(&database_address).map_err(log_and_500)?; @@ -644,12 +655,12 @@ pub struct PublishDatabaseParams {} pub struct PublishDatabaseQueryParams { #[serde(default)] clear: bool, - name_or_address: Option, + name_or_identity: Option, } impl PublishDatabaseQueryParams { - pub fn name_or_address(&self) -> Option<&NameOrAddress> { - self.name_or_address.as_ref() + pub fn name_or_address(&self) -> Option<&NameOrIdentity> { + self.name_or_identity.as_ref() } } @@ -660,34 +671,37 @@ pub async fn publish( Extension(auth): Extension, body: Bytes, ) -> axum::response::Result> { - let PublishDatabaseQueryParams { name_or_address, clear } = query_params; + let PublishDatabaseQueryParams { + name_or_identity, + clear, + } = query_params; // You should not be able to publish to a database that you do not own // so, unless you are the owner, this will fail. - let (db_addr, db_name) = match name_or_address { + let (db_addr, db_name) = match name_or_identity { Some(noa) => match noa.try_resolve(&ctx).await? { Ok(resolved) => resolved.into(), Err(domain) => { // `name_or_address` was a `NameOrAddress::Name`, but no record // exists yet. Create it now with a fresh address. - let addr = ctx.create_address().await.map_err(log_and_500)?; - ctx.create_dns_record(&auth.identity, &domain, &addr) + let database_identity = Identity::placeholder(); + ctx.create_dns_record(&auth.identity, &domain, &database_identity) .await .map_err(log_and_500)?; - (addr, Some(domain)) + (database_identity, Some(domain)) } }, None => { - let addr = ctx.create_address().await.map_err(log_and_500)?; - (addr, None) + let database_identity = Identity::placeholder(); + (database_identity, None) } }; log::trace!("Publishing to the address: {}", db_addr.to_hex()); let op = { - let exists = ctx.get_database_by_address(&db_addr).map_err(log_and_500)?.is_some(); + let exists = ctx.get_database_by_identity(&db_addr).map_err(log_and_500)?.is_some(); if clear && exists { ctx.delete_database(&auth.identity, &db_addr) @@ -706,7 +720,7 @@ pub async fn publish( .publish_database( &auth.identity, DatabaseDef { - address: db_addr, + database_identity: db_addr, program_bytes: body.into(), num_replicas: 1, host_type: HostType::Wasm, @@ -733,22 +747,24 @@ pub async fn publish( Ok(axum::Json(PublishResult::Success { domain: db_name.as_ref().map(ToString::to_string), - address: db_addr, + database_identity: db_addr, op, })) } #[derive(Deserialize)] pub struct DeleteDatabaseParams { - address: AddressForUrl, + database_identity: IdentityForUrl, } pub async fn delete_database( State(ctx): State, - Path(DeleteDatabaseParams { address }): Path, + Path(DeleteDatabaseParams { + database_identity: address, + }): Path, Extension(auth): Extension, ) -> axum::response::Result { - let address = Address::from(address); + let address = Identity::from(address); ctx.delete_database(&auth.identity, &address) .await @@ -760,18 +776,21 @@ pub async fn delete_database( #[derive(Deserialize)] pub struct SetNameQueryParams { domain: String, - address: AddressForUrl, + database_identity: IdentityForUrl, } pub async fn set_name( State(ctx): State, - Query(SetNameQueryParams { domain, address }): Query, + Query(SetNameQueryParams { + domain, + database_identity: address, + }): Query, Extension(auth): Extension, ) -> axum::response::Result { - let address = Address::from(address); + let address = Identity::from(address); let database = ctx - .get_database_by_address(&address) + .get_database_by_identity(&address) .map_err(log_and_500)? .ok_or((StatusCode::NOT_FOUND, "No such database."))?; @@ -802,12 +821,12 @@ where use axum::routing::{get, post}; axum::Router::new() .route("/dns/:database_name", get(dns::)) - .route("/reverse_dns/:database_address", get(reverse_dns::)) + .route("/reverse_dns/:database_identity", get(reverse_dns::)) .route("/set_name", get(set_name::)) .route("/ping", get(ping::)) .route("/register_tld", get(register_tld::)) .route("/publish", post(publish::).layer(DefaultBodyLimit::disable())) - .route("/delete/:address", post(delete_database::)) + .route("/delete/:database_identity", post(delete_database::)) .route_layer(axum::middleware::from_fn_with_state(ctx, anon_auth_middleware::)) } @@ -818,14 +837,14 @@ where use axum::routing::{get, post}; axum::Router::new() .route( - "/subscribe/:name_or_address", + "/subscribe/:name_or_identity", get(super::subscribe::handle_websocket::), ) - .route("/call/:name_or_address/:reducer", post(call::)) - .route("/schema/:name_or_address/:entity_type/:entity", get(describe::)) - .route("/schema/:name_or_address", get(catalog::)) - .route("/info/:name_or_address", get(info::)) - .route("/logs/:name_or_address", get(logs::)) - .route("/sql/:name_or_address", post(sql::)) + .route("/call/:name_or_identity/:reducer", post(call::)) + .route("/schema/:name_or_identity/:entity_type/:entity", get(describe::)) + .route("/schema/:name_or_identity", get(catalog::)) + .route("/info/:name_or_identity", get(info::)) + .route("/logs/:name_or_identity", get(logs::)) + .route("/sql/:name_or_identity", post(sql::)) .route_layer(axum::middleware::from_fn_with_state(ctx, anon_auth_middleware::)) } diff --git a/crates/client-api/src/routes/identity.rs b/crates/client-api/src/routes/identity.rs index 1db0590c8c7..b30dd058449 100644 --- a/crates/client-api/src/routes/identity.rs +++ b/crates/client-api/src/routes/identity.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use spacetimedb::auth::identity::encode_token_with_expiry; use spacetimedb_lib::de::serde::DeserializeWrapper; -use spacetimedb_lib::{Address, Identity}; +use spacetimedb_lib::Identity; use crate::auth::{SpacetimeAuth, SpacetimeAuthRequired}; use crate::{log_and_500, ControlStateDelegate, NodeDelegate}; @@ -42,9 +42,21 @@ pub async fn create_identity( /// This newtype around `Identity` implements `Deserialize` /// directly from the inner identity bytes, /// without the enclosing `ProductValue` wrapper. -#[derive(derive_more::Into)] +#[derive(derive_more::Into, Clone, Debug, Copy)] pub struct IdentityForUrl(Identity); +impl From for IdentityForUrl { + fn from(i: Identity) -> Self { + IdentityForUrl(i) + } +} + +impl IdentityForUrl { + pub fn into_inner(&self) -> Identity { + self.0 + } +} + impl<'de> serde::Deserialize<'de> for IdentityForUrl { fn deserialize>(de: D) -> Result { <_>::deserialize(de).map(|DeserializeWrapper(b)| IdentityForUrl(Identity::from_byte_array(b))) @@ -58,7 +70,7 @@ pub struct GetDatabasesParams { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GetDatabasesResponse { - addresses: Vec
, + identities: Vec, } pub async fn get_databases( @@ -71,12 +83,12 @@ pub async fn get_databases( log::error!("Failure when retrieving databases for search: {}", e); StatusCode::INTERNAL_SERVER_ERROR })?; - let addresses = all_dbs + let identities = all_dbs .iter() .filter(|db| db.owner_identity == identity) - .map(|db| db.address) + .map(|db| db.database_identity) .collect(); - Ok(axum::Json(GetDatabasesResponse { addresses })) + Ok(axum::Json(GetDatabasesResponse { identities })) } #[derive(Debug, Serialize)] diff --git a/crates/client-api/src/routes/subscribe.rs b/crates/client-api/src/routes/subscribe.rs index 2d723b3c4ba..f9a931bf17a 100644 --- a/crates/client-api/src/routes/subscribe.rs +++ b/crates/client-api/src/routes/subscribe.rs @@ -27,7 +27,7 @@ use crate::auth::SpacetimeAuth; use crate::util::websocket::{ CloseCode, CloseFrame, Message as WsMessage, WebSocketConfig, WebSocketStream, WebSocketUpgrade, }; -use crate::util::{NameOrAddress, XForwardedFor}; +use crate::util::{NameOrIdentity, XForwardedFor}; use crate::{log_and_500, ControlStateDelegate, NodeDelegate}; #[allow(clippy::declare_interior_mutable_const)] @@ -37,7 +37,7 @@ pub const BIN_PROTOCOL: HeaderValue = HeaderValue::from_static("v1.bsatn.spaceti #[derive(Deserialize)] pub struct SubscribeParams { - pub name_or_address: NameOrAddress, + pub name_or_identity: NameOrIdentity, } #[derive(Deserialize)] @@ -56,7 +56,7 @@ pub fn generate_random_address() -> Address { pub async fn handle_websocket( State(ctx): State, - Path(SubscribeParams { name_or_address }): Path, + Path(SubscribeParams { name_or_identity }): Path, Query(SubscribeQueryParams { client_address, compression, @@ -79,7 +79,7 @@ where ))?; } - let db_address = name_or_address.resolve(&ctx).await?.into(); + let db_address = name_or_identity.resolve(&ctx).await?.into(); let (res, ws_upgrade, protocol) = ws.select_protocol([(BIN_PROTOCOL, Protocol::Binary), (TEXT_PROTOCOL, Protocol::Text)]); @@ -91,7 +91,7 @@ where // to connect to multiple modules let database = ctx - .get_database_by_address(&db_address) + .get_database_by_identity(&db_address) .unwrap() .ok_or(StatusCode::NOT_FOUND)?; let replica = ctx @@ -212,7 +212,7 @@ async fn ws_client_actor_inner( let mut closed = false; let mut rx_buf = Vec::new(); - let addr = client.module.info().address; + let addr = client.module.info().database_identity; loop { rx_buf.clear(); diff --git a/crates/client-api/src/util.rs b/crates/client-api/src/util.rs index 5960c7fcaf6..27c222fe73e 100644 --- a/crates/client-api/src/util.rs +++ b/crates/client-api/src/util.rs @@ -10,11 +10,11 @@ use axum::response::IntoResponse; use bytestring::ByteString; use http::{HeaderName, HeaderValue, StatusCode}; -use spacetimedb::address::Address; +use spacetimedb::Identity; use spacetimedb_client_api_messages::name::DomainName; -use spacetimedb_lib::address::AddressForUrl; use crate::routes::database::DomainParsingRejection; +use crate::routes::identity::IdentityForUrl; use crate::{log_and_500, ControlStateReadAccess}; pub struct ByteStringBody(pub ByteString); @@ -58,49 +58,49 @@ impl headers::Header for XForwardedFor { } #[derive(Clone, Debug)] -pub enum NameOrAddress { - Address(AddressForUrl), +pub enum NameOrIdentity { + Identity(IdentityForUrl), Name(String), } -impl NameOrAddress { +impl NameOrIdentity { pub fn into_string(self) -> String { match self { - NameOrAddress::Address(addr) => Address::from(addr).to_hex().to_string(), - NameOrAddress::Name(name) => name, + NameOrIdentity::Identity(addr) => Identity::from(addr).to_hex().to_string(), + NameOrIdentity::Name(name) => name, } } - /// Resolve this [`NameOrAddress`]. + /// Resolve this [`NameOrIdentity`]. /// - /// If `self` is a [`NameOrAddress::Address`], the inner [`Address`] is + /// If `self` is a [`NameOrIdentity::Address`], the inner [`Address`] is /// returned in a [`ResolvedAddress`] without a [`DomainName`]. /// - /// Otherwise, if `self` is a [`NameOrAddress::Name`], the [`Address`] is + /// Otherwise, if `self` is a [`NameOrIdentity::Name`], the [`Address`] is /// looked up by that name in the SpacetimeDB DNS and returned in a /// [`ResolvedAddress`] alongside `Some` [`DomainName`]. /// - /// Errors are returned if [`NameOrAddress::Name`] cannot be parsed into a + /// Errors are returned if [`NameOrIdentity::Name`] cannot be parsed into a /// [`DomainName`], or the DNS lookup fails. /// /// An `Ok` result is itself a [`Result`], which is `Err(DomainName)` if the - /// given [`NameOrAddress::Name`] is not registered in the SpacetimeDB DNS, + /// given [`NameOrIdentity::Name`] is not registered in the SpacetimeDB DNS, /// i.e. no corresponding [`Address`] exists. pub async fn try_resolve( &self, ctx: &(impl ControlStateReadAccess + ?Sized), - ) -> axum::response::Result> { + ) -> axum::response::Result> { Ok(match self { - Self::Address(addr) => Ok(ResolvedAddress { - address: Address::from(*addr), + Self::Identity(addr) => Ok(ResolvedIdentity { + identity: Identity::from(*addr), domain: None, }), Self::Name(name) => { let domain = name.parse().map_err(|_| DomainParsingRejection)?; - let address = ctx.lookup_address(&domain).map_err(log_and_500)?; + let address = ctx.lookup_identity(&domain).map_err(log_and_500)?; match address { - Some(address) => Ok(ResolvedAddress { - address, + Some(address) => Ok(ResolvedIdentity { + identity: address, domain: Some(domain), }), None => Err(domain), @@ -110,51 +110,51 @@ impl NameOrAddress { } /// A variant of [`Self::try_resolve()`] which maps to a 404 (Not Found) - /// response if `self` is a [`NameOrAddress::Name`] for which no - /// corresponding [`Address`] is found in the SpacetimeDB DNS. + /// response if `self` is a [`NameOrIdentity::Name`] for which no + /// corresponding [`Identity`] is found in the SpacetimeDB DNS. pub async fn resolve( &self, ctx: &(impl ControlStateReadAccess + ?Sized), - ) -> axum::response::Result { + ) -> axum::response::Result { self.try_resolve(ctx).await?.map_err(|_| StatusCode::NOT_FOUND.into()) } } -impl<'de> serde::Deserialize<'de> for NameOrAddress { +impl<'de> serde::Deserialize<'de> for NameOrIdentity { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { String::deserialize(deserializer).map(|s| { - if let Ok(addr) = Address::from_hex(&s) { - NameOrAddress::Address(AddressForUrl::from(addr)) + if let Ok(addr) = Identity::from_hex(&s) { + NameOrIdentity::Identity(IdentityForUrl::from(addr)) } else { - NameOrAddress::Name(s) + NameOrIdentity::Name(s) } }) } } -impl fmt::Display for NameOrAddress { +impl fmt::Display for NameOrIdentity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Address(addr) => f.write_str(&Address::from(*addr).to_hex()), + Self::Identity(addr) => f.write_str(addr.into_inner().to_hex().as_str()), Self::Name(name) => f.write_str(name), } } } -/// A resolved [`NameOrAddress`]. +/// A resolved [`NameOrIdentity`]. /// -/// Constructed by [`NameOrAddress::try_resolve()`]. -pub struct ResolvedAddress { - address: Address, +/// Constructed by [`NameOrIdentity::try_resolve()`]. +pub struct ResolvedIdentity { + identity: Identity, domain: Option, } -impl ResolvedAddress { - pub fn address(&self) -> &Address { - &self.address +impl ResolvedIdentity { + pub fn identity(&self) -> &Identity { + &self.identity } pub fn domain(&self) -> Option<&DomainName> { @@ -162,14 +162,19 @@ impl ResolvedAddress { } } -impl From for Address { - fn from(value: ResolvedAddress) -> Self { - value.address +impl From for Identity { + fn from(value: ResolvedIdentity) -> Self { + value.identity } } -impl From for (Address, Option) { - fn from(ResolvedAddress { address, domain }: ResolvedAddress) -> Self { +impl From for (Identity, Option) { + fn from( + ResolvedIdentity { + identity: address, + domain, + }: ResolvedIdentity, + ) -> Self { (address, domain) } } diff --git a/crates/core/src/client/client_connection.rs b/crates/core/src/client/client_connection.rs index 2d0136cf59c..eeea9c31a3f 100644 --- a/crates/core/src/client/client_connection.rs +++ b/crates/core/src/client/client_connection.rs @@ -165,7 +165,7 @@ impl ClientConnection { let (sendtx, sendrx) = mpsc::channel::(CLIENT_CHANNEL_CAPACITY); - let db = module.info().address; + let db = module.info().database_identity; let (fut_tx, fut_rx) = oneshot::channel::(); // weird dance so that we can get an abort_handle into ClientConnection diff --git a/crates/core/src/client/message_handlers.rs b/crates/core/src/client/message_handlers.rs index 1033c14aa13..b2461ccdf91 100644 --- a/crates/core/src/client/message_handlers.rs +++ b/crates/core/src/client/message_handlers.rs @@ -58,7 +58,7 @@ pub async fn handle(client: &ClientConnection, message: DataMessage, timer: Inst } }; - let address = client.module.info().address; + let address = client.module.info().database_identity; let res = match message { ClientMessage::CallReducer(CallReducer { ref reducer, diff --git a/crates/core/src/database_logger.rs b/crates/core/src/database_logger.rs index 8e226640250..a20e155753e 100644 --- a/crates/core/src/database_logger.rs +++ b/crates/core/src/database_logger.rs @@ -1,5 +1,5 @@ use core::str::FromStr; -use spacetimedb_lib::address::Address; +use spacetimedb_lib::Identity; use std::fs::OpenOptions; use std::fs::{self, File}; use std::io::{self, prelude::*, SeekFrom}; @@ -126,9 +126,9 @@ impl DatabaseLogger { // PathBuf::from(path) // } - pub fn filepath(address: &Address, replica_id: u64) -> PathBuf { + pub fn filepath(identity: &Identity, replica_id: u64) -> PathBuf { let root = crate::stdb_path("worker_node/replicas"); - root.join(&*address.to_hex()) + root.join(&*identity.to_hex()) .join(replica_id.to_string()) .join("module_logs") } diff --git a/crates/core/src/db/datastore/locking_tx_datastore/committed_state.rs b/crates/core/src/db/datastore/locking_tx_datastore/committed_state.rs index e6f1a53e405..c7a9c69ed25 100644 --- a/crates/core/src/db/datastore/locking_tx_datastore/committed_state.rs +++ b/crates/core/src/db/datastore/locking_tx_datastore/committed_state.rs @@ -27,8 +27,8 @@ use core::ops::RangeBounds; use itertools::Itertools; use spacetimedb_data_structures::map::{HashSet, IntMap}; use spacetimedb_lib::{ - address::Address, db::auth::{StAccess, StTableType}, + Identity, }; use spacetimedb_primitives::{ColList, ColSet, TableId}; use spacetimedb_sats::{AlgebraicType, AlgebraicValue, ProductValue}; @@ -123,13 +123,13 @@ fn ignore_duplicate_insert_error(res: std::result::Result) -> impl CommittedState { /// Extremely delicate function to bootstrap the system tables. /// Don't update this unless you know what you're doing. - pub(super) fn bootstrap_system_tables(&mut self, database_address: Address) -> Result<()> { + pub(super) fn bootstrap_system_tables(&mut self, database_identity: Identity) -> Result<()> { // NOTE: the `rdb_num_table_rows` metric is used by the query optimizer, // and therefore has performance implications and must not be disabled. let with_label_values = |table_id: TableId, table_name: &str| { DB_METRICS .rdb_num_table_rows - .with_label_values(&database_address, &table_id.0, table_name) + .with_label_values(&database_identity, &table_id.0, table_name) }; let schemas = system_tables().map(Arc::new); @@ -274,7 +274,7 @@ impl CommittedState { with_label_values(ST_SEQUENCE_ID, ST_SEQUENCE_NAME).inc(); } - self.reset_system_table_schemas(database_address)?; + self.reset_system_table_schemas(database_identity)?; Ok(()) } @@ -286,9 +286,9 @@ impl CommittedState { /// for objects like indexes and constraints /// which are computed at insert-time, /// and therefore not included in the hardcoded schemas. - pub(super) fn reset_system_table_schemas(&mut self, database_address: Address) -> Result<()> { + pub(super) fn reset_system_table_schemas(&mut self, database_identity: Identity) -> Result<()> { // Re-read the schema with the correct ids... - let ctx = ExecutionContext::internal(database_address); + let ctx = ExecutionContext::internal(database_identity); for schema in system_tables() { self.tables.get_mut(&schema.table_id).unwrap().schema = Arc::new(self.schema_for_table_raw(&ctx, schema.table_id)?); diff --git a/crates/core/src/db/datastore/locking_tx_datastore/datastore.rs b/crates/core/src/db/datastore/locking_tx_datastore/datastore.rs index 7c332eff14a..c711e317b19 100644 --- a/crates/core/src/db/datastore/locking_tx_datastore/datastore.rs +++ b/crates/core/src/db/datastore/locking_tx_datastore/datastore.rs @@ -62,7 +62,7 @@ pub struct Locking { /// The state of sequence generation in this database. sequence_state: Arc>, /// The address of this database. - pub(crate) database_address: Address, + pub(crate) database_identity: Identity, } impl MemoryUsage for Locking { @@ -70,35 +70,35 @@ impl MemoryUsage for Locking { let Self { committed_state, sequence_state, - database_address, + database_identity, } = self; std::mem::size_of_val(&**committed_state) + committed_state.read().heap_usage() + std::mem::size_of_val(&**sequence_state) + sequence_state.lock().heap_usage() - + database_address.heap_usage() + + database_identity.heap_usage() } } impl Locking { - pub fn new(database_address: Address) -> Self { + pub fn new(database_identity: Identity) -> Self { Self { committed_state: <_>::default(), sequence_state: <_>::default(), - database_address, + database_identity, } } /// IMPORTANT! This the most delicate function in the entire codebase. /// DO NOT CHANGE UNLESS YOU KNOW WHAT YOU'RE DOING!!! - pub fn bootstrap(database_address: Address) -> Result { + pub fn bootstrap(database_identity: Identity) -> Result { log::trace!("DATABASE: BOOTSTRAPPING SYSTEM TABLES..."); // NOTE! The bootstrapping process does not take plan in a transaction. // This is intentional. - let datastore = Self::new(database_address); + let datastore = Self::new(database_identity); let mut commit_state = datastore.committed_state.write_arc(); - let database_address = datastore.database_address; + let database_identity = datastore.database_identity; // TODO(cloutiertyler): One thing to consider in the future is, should // we persist the bootstrap transaction in the message log? My intuition // is no, because then if we change the schema of the system tables we @@ -107,7 +107,7 @@ impl Locking { // for code that relies on the old schema... // Create the system tables and insert information about themselves into - commit_state.bootstrap_system_tables(database_address)?; + commit_state.bootstrap_system_tables(database_identity)?; // The database tables are now initialized with the correct data. // Now we have to build our in memory structures. commit_state.build_sequence_state(&mut datastore.sequence_state.lock())?; @@ -143,7 +143,7 @@ impl Locking { /// _before_ the transaction is applied to the database state. pub fn replay(&self, progress: F) -> Replay { Replay { - database_address: self.database_address, + database_identity: self.database_identity, committed_state: self.committed_state.clone(), progress: RefCell::new(progress), } @@ -163,18 +163,18 @@ impl Locking { /// after replaying the suffix of the commitlog. pub fn restore_from_snapshot(snapshot: ReconstructedSnapshot) -> Result { let ReconstructedSnapshot { - database_address, + database_identity, tx_offset, blob_store, tables, .. } = snapshot; - let datastore = Self::new(database_address); + let datastore = Self::new(database_identity); let mut committed_state = datastore.committed_state.write_arc(); committed_state.blob_store = blob_store; - let ctx = ExecutionContext::internal(datastore.database_address); + let ctx = ExecutionContext::internal(datastore.database_identity); // Note that `tables` is a `BTreeMap`, and so iterates in increasing order. // This means that we will instantiate and populate the system tables before any user tables. @@ -203,19 +203,19 @@ impl Locking { // and therefore has performance implications and must not be disabled. DB_METRICS .rdb_num_table_rows - .with_label_values(&database_address, &table_id.0, &schema.table_name) + .with_label_values(&database_identity, &table_id.0, &schema.table_name) .set(table.row_count as i64); // Also set the `rdb_table_size` metric for the table. let table_size = table.bytes_occupied_overestimate(); DB_METRICS .rdb_table_size - .with_label_values(&database_address, &table_id.into(), &schema.table_name) + .with_label_values(&database_identity, &table_id.into(), &schema.table_name) .set(table_size as i64); } // Fix up auto_inc IDs in the cached system table schemas. - committed_state.reset_system_table_schemas(database_address)?; + committed_state.reset_system_table_schemas(database_identity)?; // The next TX offset after restoring from a snapshot is one greater than the snapshotted offset. committed_state.next_tx_offset = tx_offset + 1; @@ -243,7 +243,7 @@ impl Locking { .table_id_from_name_mut_tx(tx, &name)? .ok_or_else(|| TableError::NotFound(name.into()))?; - tx.alter_table_access(self.database_address, table_id, access) + tx.alter_table_access(self.database_identity, table_id, access) } } @@ -312,7 +312,7 @@ impl TxDatastore for Locking { } fn table_id_from_name_tx(&self, tx: &Self::Tx, table_name: &str) -> Result> { - tx.table_id_from_name(table_name, self.database_address) + tx.table_id_from_name(table_name, self.database_identity) } fn table_name_from_id_tx<'a>(&'a self, tx: &'a Self::Tx, table_id: TableId) -> Result>> { @@ -320,7 +320,7 @@ impl TxDatastore for Locking { } fn schema_for_table_tx(&self, tx: &Self::Tx, table_id: TableId) -> Result> { - tx.schema_for_table(&ExecutionContext::internal(self.database_address), table_id) + tx.schema_for_table(&ExecutionContext::internal(self.database_identity), table_id) } fn get_all_tables_tx(&self, ctx: &ExecutionContext, tx: &Self::Tx) -> Result>> { @@ -353,7 +353,7 @@ impl TxDatastore for Locking { impl MutTxDatastore for Locking { fn create_table_mut_tx(&self, tx: &mut Self::MutTx, schema: TableSchema) -> Result { - tx.create_table(schema, self.database_address) + tx.create_table(schema, self.database_identity) } /// This function is used to get the `ProductType` of the rows in a @@ -370,7 +370,7 @@ impl MutTxDatastore for Locking { /// /// This function is known to be called quite frequently. fn row_type_for_table_mut_tx<'tx>(&self, tx: &'tx Self::MutTx, table_id: TableId) -> Result> { - tx.row_type_for_table(table_id, self.database_address) + tx.row_type_for_table(table_id, self.database_identity) } /// IMPORTANT! This function is relatively expensive, and much more @@ -378,21 +378,21 @@ impl MutTxDatastore for Locking { /// `row_type_for_table_mut_tx` if you only need to access the `ProductType` /// of the table. fn schema_for_table_mut_tx(&self, tx: &Self::MutTx, table_id: TableId) -> Result> { - tx.schema_for_table(&ExecutionContext::internal(self.database_address), table_id) + tx.schema_for_table(&ExecutionContext::internal(self.database_identity), table_id) } /// This function is relatively expensive because it needs to be /// transactional, however we don't expect to be dropping tables very often. fn drop_table_mut_tx(&self, tx: &mut Self::MutTx, table_id: TableId) -> Result<()> { - tx.drop_table(table_id, self.database_address) + tx.drop_table(table_id, self.database_identity) } fn rename_table_mut_tx(&self, tx: &mut Self::MutTx, table_id: TableId, new_name: &str) -> Result<()> { - tx.rename_table(table_id, new_name, self.database_address) + tx.rename_table(table_id, new_name, self.database_identity) } fn table_id_from_name_mut_tx(&self, tx: &Self::MutTx, table_name: &str) -> Result> { - tx.table_id_from_name(table_name, self.database_address) + tx.table_id_from_name(table_name, self.database_identity) } fn table_id_exists_mut_tx(&self, tx: &Self::MutTx, table_id: &TableId) -> bool { @@ -410,41 +410,41 @@ impl MutTxDatastore for Locking { } fn create_index_mut_tx(&self, tx: &mut Self::MutTx, index_schema: IndexSchema, is_unique: bool) -> Result { - let ctx = &ExecutionContext::internal(self.database_address); + let ctx = &ExecutionContext::internal(self.database_identity); tx.create_index(ctx, index_schema, is_unique) } fn drop_index_mut_tx(&self, tx: &mut Self::MutTx, index_id: IndexId) -> Result<()> { - tx.drop_index(index_id, self.database_address) + tx.drop_index(index_id, self.database_identity) } fn index_id_from_name_mut_tx(&self, tx: &Self::MutTx, index_name: &str) -> Result> { - tx.index_id_from_name(index_name, self.database_address) + tx.index_id_from_name(index_name, self.database_identity) } fn get_next_sequence_value_mut_tx(&self, tx: &mut Self::MutTx, seq_id: SequenceId) -> Result { - tx.get_next_sequence_value(seq_id, self.database_address) + tx.get_next_sequence_value(seq_id, self.database_identity) } fn create_sequence_mut_tx(&self, tx: &mut Self::MutTx, sequence_schema: SequenceSchema) -> Result { - tx.create_sequence(sequence_schema, self.database_address) + tx.create_sequence(sequence_schema, self.database_identity) } fn drop_sequence_mut_tx(&self, tx: &mut Self::MutTx, seq_id: SequenceId) -> Result<()> { - tx.drop_sequence(seq_id, self.database_address) + tx.drop_sequence(seq_id, self.database_identity) } fn sequence_id_from_name_mut_tx(&self, tx: &Self::MutTx, sequence_name: &str) -> Result> { - tx.sequence_id_from_name(sequence_name, self.database_address) + tx.sequence_id_from_name(sequence_name, self.database_identity) } fn drop_constraint_mut_tx(&self, tx: &mut Self::MutTx, constraint_id: ConstraintId) -> Result<()> { - let ctx = &ExecutionContext::internal(self.database_address); + let ctx = &ExecutionContext::internal(self.database_identity); tx.drop_constraint(ctx, constraint_id) } fn constraint_id_from_name(&self, tx: &Self::MutTx, constraint_name: &str) -> Result> { - let ctx = &ExecutionContext::internal(self.database_address); + let ctx = &ExecutionContext::internal(self.database_identity); tx.constraint_id_from_name(ctx, constraint_name) } @@ -527,17 +527,17 @@ impl MutTxDatastore for Locking { table_id: TableId, mut row: ProductValue, ) -> Result<(AlgebraicValue, RowRef<'a>)> { - let (gens, row_ref) = tx.insert(table_id, &mut row, self.database_address)?; + let (gens, row_ref) = tx.insert(table_id, &mut row, self.database_identity)?; Ok((gens, row_ref.collapse())) } fn metadata_mut_tx(&self, tx: &Self::MutTx) -> Result> { - let ctx = ExecutionContext::internal(self.database_address); + let ctx = ExecutionContext::internal(self.database_identity); tx.iter(&ctx, ST_MODULE_ID)?.next().map(metadata_from_row).transpose() } fn update_program(&self, tx: &mut Self::MutTx, program_kind: ModuleKind, program: Program) -> Result<()> { - let ctx = ExecutionContext::internal(self.database_address); + let ctx = ExecutionContext::internal(self.database_identity); let old = tx .iter(&ctx, ST_MODULE_ID)? .next() @@ -554,11 +554,15 @@ impl MutTxDatastore for Locking { row.program_bytes = program.bytes; tx.delete(ST_MODULE_ID, ptr)?; - tx.insert(ST_MODULE_ID, &mut row.into(), self.database_address) + tx.insert(ST_MODULE_ID, &mut row.into(), self.database_identity) .map(drop) } - None => Err(anyhow!("database {} improperly initialized: no metadata", self.database_address).into()), + None => Err(anyhow!( + "database {} improperly initialized: no metadata", + self.database_identity + ) + .into()), } } } @@ -573,7 +577,7 @@ pub(super) fn record_metrics( committed_state: Option<&CommittedState>, ) { let workload = &ctx.workload(); - let db = &ctx.database(); + let db = &ctx.database_identity(); let reducer = ctx.reducer_name(); let elapsed_time = tx_timer.elapsed(); let cpu_time = elapsed_time - lock_wait_time; @@ -599,7 +603,7 @@ pub(super) fn record_metrics( /// Update table rows and table size gauges, /// and sets them to zero if no table is present. - fn update_table_gauges(db: &Address, table_id: &TableId, table_name: &str, table: Option<&Table>) { + fn update_table_gauges(db: &Identity, table_id: &TableId, table_name: &str, table: Option<&Table>) { let (mut table_rows, mut table_size) = (0, 0); if let Some(table) = table { table_rows = table.row_count as i64; @@ -699,7 +703,7 @@ pub enum ReplayError { /// A [`spacetimedb_commitlog::Decoder`] suitable for replaying a transaction /// history into the database state. pub struct Replay { - database_address: Address, + database_identity: Identity, committed_state: Arc>, progress: RefCell, } @@ -708,7 +712,7 @@ impl Replay { fn using_visitor(&self, f: impl FnOnce(&mut ReplayVisitor) -> T) -> T { let mut committed_state = self.committed_state.write_arc(); let mut visitor = ReplayVisitor { - database_address: &self.database_address, + database_identity: &self.database_identity, committed_state: &mut committed_state, progress: &mut *self.progress.borrow_mut(), }; @@ -813,7 +817,7 @@ impl spacetimedb_commitlog::Decoder for &mut Replay { // unnecessary. struct ReplayVisitor<'a, F> { - database_address: &'a Address, + database_identity: &'a Identity, committed_state: &'a mut CommittedState, progress: &'a mut F, } @@ -860,7 +864,7 @@ impl spacetimedb_commitlog::payload::txdata::Visitor for ReplayVi // and therefore has performance implications and must not be disabled. DB_METRICS .rdb_num_table_rows - .with_label_values(self.database_address, &table_id.into(), &schema.table_name) + .with_label_values(self.database_identity, &table_id.into(), &schema.table_name) .inc(); Ok(row) @@ -890,7 +894,7 @@ impl spacetimedb_commitlog::payload::txdata::Visitor for ReplayVi // and therefore has performance implications and must not be disabled. DB_METRICS .rdb_num_table_rows - .with_label_values(self.database_address, &table_id.into(), &table_name) + .with_label_values(self.database_identity, &table_id.into(), &table_name) .dec(); Ok(row) @@ -932,7 +936,7 @@ impl spacetimedb_commitlog::payload::txdata::Visitor for ReplayVi /// reading only the columns necessary to construct the value. fn metadata_from_row(row: RowRef<'_>) -> Result { Ok(Metadata { - database_address: read_addr_from_col(row, StModuleFields::DatabaseAddress)?, + database_identity: read_addr_from_col(row, StModuleFields::DatabaseIdentity)?, owner_identity: read_identity_from_col(row, StModuleFields::OwnerIdentity)?, program_hash: read_hash_from_col(row, StModuleFields::ProgramHash)?, }) @@ -954,7 +958,6 @@ mod tests { use crate::error::{DBError, IndexError}; use itertools::Itertools; use pretty_assertions::assert_eq; - use spacetimedb_lib::address::Address; use spacetimedb_lib::db::auth::{StAccess, StTableType}; use spacetimedb_lib::error::ResultTest; use spacetimedb_lib::resolved_type_via_v9; @@ -1058,7 +1061,7 @@ mod tests { } fn get_datastore() -> Result { - Locking::bootstrap(Address::ZERO) + Locking::bootstrap(Identity::ZERO) } fn col(col: u16) -> ColList { @@ -1374,7 +1377,7 @@ mod tests { ColRow { table: ST_CONSTRAINT_ID.into(), pos: 2, name: "table_id", ty: TableId::get_type() }, ColRow { table: ST_CONSTRAINT_ID.into(), pos: 3, name: "constraint_data", ty: resolved_type_via_v9::() }, - ColRow { table: ST_MODULE_ID.into(), pos: 0, name: "database_address", ty: AlgebraicType::U128 }, + ColRow { table: ST_MODULE_ID.into(), pos: 0, name: "database_identity", ty: AlgebraicType::U256 }, ColRow { table: ST_MODULE_ID.into(), pos: 1, name: "owner_identity", ty: AlgebraicType::U256 }, ColRow { table: ST_MODULE_ID.into(), pos: 2, name: "program_kind", ty: AlgebraicType::U8 }, ColRow { table: ST_MODULE_ID.into(), pos: 3, name: "program_hash", ty: AlgebraicType::U256 }, diff --git a/crates/core/src/db/datastore/locking_tx_datastore/mut_tx.rs b/crates/core/src/db/datastore/locking_tx_datastore/mut_tx.rs index 22cacebc9df..c944841edb8 100644 --- a/crates/core/src/db/datastore/locking_tx_datastore/mut_tx.rs +++ b/crates/core/src/db/datastore/locking_tx_datastore/mut_tx.rs @@ -23,13 +23,12 @@ use crate::{ use core::ops::RangeBounds; use core::{iter, ops::Bound}; use smallvec::SmallVec; -use spacetimedb_lib::db::raw_def::v9::RawSql; use spacetimedb_lib::{ - address::Address, bsatn::Deserializer, db::{auth::StAccess, raw_def::SEQUENCE_ALLOCATION_STEP}, de::DeserializeSeed, }; +use spacetimedb_lib::{db::raw_def::v9::RawSql, Identity}; use spacetimedb_primitives::{ColId, ColList, ColSet, ConstraintId, IndexId, ScheduleId, SequenceId, TableId}; use spacetimedb_sats::{ bsatn::{self, DecodeError}, @@ -71,9 +70,9 @@ impl MutTxId { table_id: TableId, col_pos: ColId, value: &AlgebraicValue, - database_address: Address, + database_identity: Identity, ) -> Result<()> { - let ctx = ExecutionContext::internal(database_address); + let ctx = ExecutionContext::internal(database_identity); let rows = self.iter_by_col_eq(&ctx, table_id, col_pos, value)?; let ptrs_to_delete = rows.map(|row_ref| row_ref.pointer()).collect::>(); if ptrs_to_delete.is_empty() { @@ -100,7 +99,7 @@ impl MutTxId { /// - An in-memory insert table is created for the transaction, allowing the transaction to insert rows into the table. /// - The table metadata is inserted into the system tables. /// - The returned ID is unique and not `TableId::SENTINEL`. - pub fn create_table(&mut self, mut table_schema: TableSchema, database_address: Address) -> Result { + pub fn create_table(&mut self, mut table_schema: TableSchema, database_identity: Identity) -> Result { if table_schema.table_id != TableId::SENTINEL { return Err(anyhow::anyhow!("`table_id` must be `TableId::SENTINEL` in `{:#?}`", table_schema).into()); // checks for children are performed in the relevant `create_...` functions. @@ -119,7 +118,7 @@ impl MutTxId { table_primary_key: table_schema.primary_key.map(Into::into), }; let table_id = self - .insert(ST_TABLE_ID, &mut row.into(), database_address)? + .insert(ST_TABLE_ID, &mut row.into(), database_identity)? .1 .collapse() .read_col(StTableFields::TableId)?; @@ -136,7 +135,7 @@ impl MutTxId { col_name: col.col_name.clone(), col_type: col.col_type.clone().into(), }; - self.insert(ST_COLUMN_ID, &mut row.into(), database_address)?; + self.insert(ST_COLUMN_ID, &mut row.into(), database_identity)?; } let mut schema_internal = table_schema.clone(); @@ -158,7 +157,7 @@ impl MutTxId { reducer_name: schedule.reducer_name, at_column: schedule.at_column, }; - let (generated, ..) = self.insert(ST_SCHEDULED_ID, &mut row.into(), database_address)?; + let (generated, ..) = self.insert(ST_SCHEDULED_ID, &mut row.into(), database_identity)?; let id = generated.as_u32(); if let Some(&id) = id { @@ -175,14 +174,14 @@ impl MutTxId { } // Insert constraints into `st_constraints` - let ctx = ExecutionContext::internal(database_address); + let ctx = ExecutionContext::internal(database_identity); for constraint in table_schema.constraints.iter().cloned() { self.create_constraint(&ctx, constraint)?; } // Insert sequences into `st_sequences` for seq in table_schema.sequences { - self.create_sequence(seq, database_address)?; + self.create_sequence(seq, database_identity)?; } // Create the indexes for the table @@ -221,14 +220,14 @@ impl MutTxId { .map(|table| table.get_row_type()) } - pub fn row_type_for_table(&self, table_id: TableId, database_address: Address) -> Result> { + pub fn row_type_for_table(&self, table_id: TableId, database_identity: Identity) -> Result> { // Fetch the `ProductType` from the in memory table if it exists. // The `ProductType` is invalidated if the schema of the table changes. if let Some(row_type) = self.get_row_type(table_id) { return Ok(RowTypeForTable::Ref(row_type)); } - let ctx = ExecutionContext::internal(database_address); + let ctx = ExecutionContext::internal(database_identity); // Look up the columns for the table in question. // NOTE: This is quite an expensive operation, although we only need @@ -239,16 +238,16 @@ impl MutTxId { Ok(RowTypeForTable::Arc(self.schema_for_table(&ctx, table_id)?)) } - pub fn drop_table(&mut self, table_id: TableId, database_address: Address) -> Result<()> { - let ctx = &ExecutionContext::internal(database_address); + pub fn drop_table(&mut self, table_id: TableId, database_identity: Identity) -> Result<()> { + let ctx = &ExecutionContext::internal(database_identity); let schema = &*self.schema_for_table(ctx, table_id)?; for row in &schema.indexes { - self.drop_index(row.index_id, database_address)?; + self.drop_index(row.index_id, database_identity)?; } for row in &schema.sequences { - self.drop_sequence(row.sequence_id, database_address)?; + self.drop_sequence(row.sequence_id, database_identity)?; } for row in &schema.constraints { @@ -260,20 +259,20 @@ impl MutTxId { ST_TABLE_ID, StTableFields::TableId.col_id(), &table_id.into(), - database_address, + database_identity, )?; self.drop_col_eq( ST_COLUMN_ID, StColumnFields::TableId.col_id(), &table_id.into(), - database_address, + database_identity, )?; if let Some(schedule) = &schema.schedule { self.drop_col_eq( ST_SCHEDULED_ID, StScheduledFields::ScheduleId.col_id(), &schedule.schedule_id.into(), - database_address, + database_identity, )?; } @@ -286,16 +285,16 @@ impl MutTxId { Ok(()) } - pub fn rename_table(&mut self, table_id: TableId, new_name: &str, database_address: Address) -> Result<()> { - let ctx = ExecutionContext::internal(database_address); + pub fn rename_table(&mut self, table_id: TableId, new_name: &str, database_identity: Identity) -> Result<()> { + let ctx = ExecutionContext::internal(database_identity); // Update the table's name in st_tables. - self.update_st_table_row(&ctx, database_address, table_id, |st| st.table_name = new_name.into()) + self.update_st_table_row(&ctx, database_identity, table_id, |st| st.table_name = new_name.into()) } fn update_st_table_row( &mut self, ctx: &ExecutionContext, - database_address: Address, + database_identity: Identity, table_id: TableId, updater: impl FnOnce(&mut StTableRow), ) -> Result<()> { @@ -310,13 +309,13 @@ impl MutTxId { // Delete the row, run updates, and insert again. self.delete(ST_TABLE_ID, ptr)?; updater(&mut row); - self.insert(ST_TABLE_ID, &mut row.into(), database_address)?; + self.insert(ST_TABLE_ID, &mut row.into(), database_identity)?; Ok(()) } - pub fn table_id_from_name(&self, table_name: &str, database_address: Address) -> Result> { - let ctx = ExecutionContext::internal(database_address); + pub fn table_id_from_name(&self, table_name: &str, database_identity: Identity) -> Result> { + let ctx = ExecutionContext::internal(database_identity); let table_name = &table_name.into(); let row = self .iter_by_col_eq(&ctx, ST_TABLE_ID, StTableFields::TableName, table_name)? @@ -361,7 +360,7 @@ impl MutTxId { /// Set the table access of `table_id` to `access`. pub(crate) fn alter_table_access( &mut self, - database_address: Address, + database_identity: Identity, table_id: TableId, access: StAccess, ) -> Result<()> { @@ -370,8 +369,8 @@ impl MutTxId { table.with_mut_schema(|s| s.table_access = access); // Update system tables. - let ctx = ExecutionContext::internal(database_address); - self.update_st_table_row(&ctx, database_address, table_id, |st| st.table_access = access)?; + let ctx = ExecutionContext::internal(database_identity); + self.update_st_table_row(&ctx, database_identity, table_id, |st| st.table_access = access)?; Ok(()) } @@ -416,7 +415,7 @@ impl MutTxId { index_algorithm: index.index_algorithm.clone().into(), }; let index_id = self - .insert(ST_INDEX_ID, &mut row.into(), ctx.database())? + .insert(ST_INDEX_ID, &mut row.into(), ctx.database_identity())? .1 .collapse() .read_col(StIndexFields::IndexId)?; @@ -459,9 +458,9 @@ impl MutTxId { Ok(index_id) } - pub fn drop_index(&mut self, index_id: IndexId, database_address: Address) -> Result<()> { + pub fn drop_index(&mut self, index_id: IndexId, database_identity: Identity) -> Result<()> { log::trace!("INDEX DROPPING: {}", index_id); - let ctx = ExecutionContext::internal(database_address); + let ctx = ExecutionContext::internal(database_identity); // Find the index in `st_indexes`. let st_index_ref = self @@ -500,8 +499,8 @@ impl MutTxId { Ok(()) } - pub fn index_id_from_name(&self, index_name: &str, database_address: Address) -> Result> { - let ctx = ExecutionContext::internal(database_address); + pub fn index_id_from_name(&self, index_name: &str, database_identity: Identity) -> Result> { + let ctx = ExecutionContext::internal(database_identity); let name = &index_name.into(); let row = self .iter_by_col_eq(&ctx, ST_INDEX_ID, StIndexFields::IndexName, name)? @@ -695,7 +694,7 @@ impl MutTxId { (range_start, range_end) } - pub fn get_next_sequence_value(&mut self, seq_id: SequenceId, database_address: Address) -> Result { + pub fn get_next_sequence_value(&mut self, seq_id: SequenceId, database_identity: Identity) -> Result { { let Some(sequence) = self.sequence_state_lock.get_sequence_mut(seq_id) else { return Err(SequenceError::NotFound(seq_id).into()); @@ -711,7 +710,7 @@ impl MutTxId { } // Allocate new sequence values // If we're out of allocations, then update the sequence row in st_sequences to allocate a fresh batch of sequences. - let ctx = ExecutionContext::internal(database_address); + let ctx = ExecutionContext::internal(database_identity); let old_seq_row_ref = self .iter_by_col_eq(&ctx, ST_SEQUENCE_ID, StSequenceFields::SequenceId, &seq_id.into())? .last() @@ -755,7 +754,7 @@ impl MutTxId { /// Ensures: /// - The sequence metadata is inserted into the system tables (and other data structures reflecting them). /// - The returned ID is unique and not `SequenceId::SENTINEL`. - pub fn create_sequence(&mut self, seq: SequenceSchema, database_address: Address) -> Result { + pub fn create_sequence(&mut self, seq: SequenceSchema, database_identity: Identity) -> Result { if seq.sequence_id != SequenceId::SENTINEL { return Err(anyhow::anyhow!("`sequence_id` must be `SequenceId::SENTINEL` in `{:#?}`", seq).into()); } @@ -785,7 +784,7 @@ impl MutTxId { min_value: seq.min_value, max_value: seq.max_value, }; - let row = self.insert(ST_SEQUENCE_ID, &mut sequence_row.clone().into(), database_address)?; + let row = self.insert(ST_SEQUENCE_ID, &mut sequence_row.clone().into(), database_identity)?; let seq_id = row.1.collapse().read_col(StSequenceFields::SequenceId)?; sequence_row.sequence_id = seq_id; @@ -800,8 +799,8 @@ impl MutTxId { Ok(seq_id) } - pub fn drop_sequence(&mut self, sequence_id: SequenceId, database_address: Address) -> Result<()> { - let ctx = ExecutionContext::internal(database_address); + pub fn drop_sequence(&mut self, sequence_id: SequenceId, database_identity: Identity) -> Result<()> { + let ctx = ExecutionContext::internal(database_identity); let st_sequence_ref = self .iter_by_col_eq(&ctx, ST_SEQUENCE_ID, StSequenceFields::SequenceId, &sequence_id.into())? @@ -824,8 +823,8 @@ impl MutTxId { Ok(()) } - pub fn sequence_id_from_name(&self, seq_name: &str, database_address: Address) -> Result> { - let ctx = ExecutionContext::internal(database_address); + pub fn sequence_id_from_name(&self, seq_name: &str, database_identity: Identity) -> Result> { + let ctx = ExecutionContext::internal(database_identity); let name = &>::from(seq_name).into(); self.iter_by_col_eq(&ctx, ST_SEQUENCE_ID, StSequenceFields::SequenceName, name) .map(|mut iter| { @@ -880,7 +879,7 @@ impl MutTxId { let constraint_row = self.insert( ST_CONSTRAINT_ID, &mut ProductValue::from(constraint_row), - ctx.database(), + ctx.database_identity(), )?; let constraint_id = constraint_row.1.collapse().read_col(StConstraintFields::ConstraintId)?; let existed = matches!(constraint_row.1, RowRefInsertion::Existed(_)); @@ -985,7 +984,11 @@ impl MutTxId { sql: row_level_security_schema.sql, }; - let row = self.insert(ST_ROW_LEVEL_SECURITY_ID, &mut ProductValue::from(row), ctx.database())?; + let row = self.insert( + ST_ROW_LEVEL_SECURITY_ID, + &mut ProductValue::from(row), + ctx.database_identity(), + )?; let row_level_security_sql = row.1.collapse().read_col(StRowLevelSecurityFields::Sql)?; let existed = matches!(row.1, RowRefInsertion::Existed(_)); @@ -1217,9 +1220,9 @@ impl MutTxId { &'a mut self, table_id: TableId, row: &mut ProductValue, - database_address: Address, + database_identity: Identity, ) -> Result<(AlgebraicValue, RowRefInsertion<'a>)> { - let generated = self.write_sequence_values(table_id, row, database_address)?; + let generated = self.write_sequence_values(table_id, row, database_identity)?; let row_ref = self.insert_row_internal(table_id, row)?; Ok((generated, row_ref)) } @@ -1230,9 +1233,9 @@ impl MutTxId { &mut self, table_id: TableId, row: &mut ProductValue, - database_address: Address, + database_identity: Identity, ) -> Result { - let ctx = ExecutionContext::internal(database_address); + let ctx = ExecutionContext::internal(database_identity); // TODO: Executing schema_for_table for every row insert is expensive. // However we ask for the schema in the [Table] struct instead. @@ -1249,7 +1252,7 @@ impl MutTxId { // Update every column in the row that needs it. // We assume here that column with a sequence is of a sequence-compatible type. for (col_id, sequence_id) in cols_to_update.iter().zip(seqs_to_use) { - let seq_val = self.get_next_sequence_value(sequence_id, database_address)?; + let seq_val = self.get_next_sequence_value(sequence_id, database_identity)?; let col_typ = &schema.columns()[col_id.idx()].col_type; let gen_val = AlgebraicValue::from_sequence_value(col_typ, seq_val); row.elements[col_id.idx()] = gen_val; diff --git a/crates/core/src/db/datastore/locking_tx_datastore/state_view.rs b/crates/core/src/db/datastore/locking_tx_datastore/state_view.rs index 83bfc50eb49..7521fa7e294 100644 --- a/crates/core/src/db/datastore/locking_tx_datastore/state_view.rs +++ b/crates/core/src/db/datastore/locking_tx_datastore/state_view.rs @@ -11,7 +11,7 @@ use crate::{ execution_context::ExecutionContext, }; use core::ops::RangeBounds; -use spacetimedb_lib::address::Address; +use spacetimedb_lib::Identity; use spacetimedb_primitives::{ColList, TableId}; use spacetimedb_sats::AlgebraicValue; use spacetimedb_schema::schema::{ColumnSchema, TableSchema}; @@ -23,8 +23,8 @@ use std::sync::Arc; pub trait StateView { fn get_schema(&self, table_id: TableId) -> Option<&Arc>; - fn table_id_from_name(&self, table_name: &str, database_address: Address) -> Result> { - let ctx = ExecutionContext::internal(database_address); + fn table_id_from_name(&self, table_name: &str, database_identity: Identity) -> Result> { + let ctx = ExecutionContext::internal(database_identity); let name = &>::from(table_name).into(); let row = self .iter_by_col_eq(&ctx, ST_TABLE_ID, StTableFields::TableName, name)? diff --git a/crates/core/src/db/datastore/system_tables.rs b/crates/core/src/db/datastore/system_tables.rs index 173a97eed20..e4de97029a3 100644 --- a/crates/core/src/db/datastore/system_tables.rs +++ b/crates/core/src/db/datastore/system_tables.rs @@ -243,7 +243,7 @@ st_fields_enum!(enum StRowLevelSecurityFields { }); // WARNING: For a stable schema, don't change the field names and discriminants. st_fields_enum!(enum StModuleFields { - "database_address", DatabaseAddress = 0, + "database_identity", DatabaseIdentity = 0, "owner_identity", OwnerIdentity = 1, "program_kind", ProgramKind = 2, "program_hash", ProgramHash = 3, @@ -831,20 +831,20 @@ impl From for IdentityViaU256 { /// This table holds exactly one row, describing the latest version of the /// SpacetimeDB module associated with the database: /// -/// * `database_address` is the [`Address`] of the database. +/// * `database_identity` is the [`Identity`] of the database. /// * `owner_identity` is the [`Identity`] of the owner of the database. /// * `program_kind` is the [`ModuleKind`] (currently always [`WASM_MODULE`]). /// * `program_hash` is the [`Hash`] of the raw bytes of the (compiled) module. /// * `program_bytes` are the raw bytes of the (compiled) module. /// * `module_version` is the version of the module. /// -/// | database_address | owner_identity | program_kind | program_bytes | program_hash | module_version | +/// | identity | owner_identity | program_kind | program_bytes | program_hash | module_version | /// |------------------|----------------|---------------|---------------|---------------------|----------------| /// | | | 0 | | | | #[derive(Clone, Debug, Eq, PartialEq, SpacetimeType)] #[sats(crate = spacetimedb_lib)] pub struct StModuleRow { - pub(crate) database_address: AddressViaU128, + pub(crate) database_identity: IdentityViaU256, pub(crate) owner_identity: IdentityViaU256, pub(crate) program_kind: ModuleKind, pub(crate) program_hash: Hash, @@ -869,9 +869,9 @@ pub fn read_bytes_from_col(row: RowRef<'_>, col: impl StFields) -> Result, col: impl StFields) -> Result { - let val: u128 = row.read_col(col.col_id())?; - Ok(val.into()) +pub fn read_addr_from_col(row: RowRef<'_>, col: impl StFields) -> Result { + let val: u256 = row.read_col(col.col_id())?; + Ok(Identity::from_u256(val)) } /// Read an [`Identity`] directly from the column `col` in `row`. diff --git a/crates/core/src/db/datastore/traits.rs b/crates/core/src/db/datastore/traits.rs index 5691184a2b3..d398e5a614e 100644 --- a/crates/core/src/db/datastore/traits.rs +++ b/crates/core/src/db/datastore/traits.rs @@ -8,7 +8,7 @@ use super::Result; use crate::db::datastore::system_tables::ST_TABLE_ID; use crate::execution_context::ExecutionContext; use spacetimedb_data_structures::map::IntMap; -use spacetimedb_lib::{hash_bytes, Address, Identity}; +use spacetimedb_lib::{hash_bytes, Identity}; use spacetimedb_primitives::*; use spacetimedb_sats::hash::Hash; use spacetimedb_sats::{AlgebraicValue, ProductType, ProductValue}; @@ -307,7 +307,7 @@ pub trait MutTx { #[derive(Debug)] pub struct Metadata { /// The stable address of the database. - pub database_address: Address, + pub database_identity: Identity, /// The identity of the database's owner . pub owner_identity: Identity, /// The hash of the binary module set for the database. diff --git a/crates/core/src/db/db_metrics/mod.rs b/crates/core/src/db/db_metrics/mod.rs index 8a22515dea3..8dd965fa200 100644 --- a/crates/core/src/db/db_metrics/mod.rs +++ b/crates/core/src/db/db_metrics/mod.rs @@ -1,7 +1,7 @@ use crate::execution_context::WorkloadType; use once_cell::sync::Lazy; use prometheus::{HistogramVec, IntCounterVec, IntGaugeVec}; -use spacetimedb_lib::Address; +use spacetimedb_lib::Identity; use spacetimedb_metrics::metrics_group; use spacetimedb_primitives::TableId; @@ -10,42 +10,42 @@ metrics_group!( pub struct DbMetrics { #[name = spacetime_num_table_rows] #[help = "The number of rows in a table"] - #[labels(db: Address, table_id: u32, table_name: str)] + #[labels(db: Identity, table_id: u32, table_name: str)] pub rdb_num_table_rows: IntGaugeVec, #[name = spacetime_num_rows_inserted_total] #[help = "The cumulative number of rows inserted into a table"] - #[labels(txn_type: WorkloadType, db: Address, reducer_or_query: str, table_id: u32, table_name: str)] + #[labels(txn_type: WorkloadType, db: Identity, reducer_or_query: str, table_id: u32, table_name: str)] pub rdb_num_rows_inserted: IntCounterVec, #[name = spacetime_num_rows_deleted_total] #[help = "The cumulative number of rows deleted from a table"] - #[labels(txn_type: WorkloadType, db: Address, reducer_or_query: str, table_id: u32, table_name: str)] + #[labels(txn_type: WorkloadType, db: Identity, reducer_or_query: str, table_id: u32, table_name: str)] pub rdb_num_rows_deleted: IntCounterVec, #[name = spacetime_num_rows_fetched_total] #[help = "The cumulative number of rows fetched from a table"] - #[labels(txn_type: WorkloadType, db: Address, reducer_or_query: str, table_id: u32, table_name: str)] + #[labels(txn_type: WorkloadType, db: Identity, reducer_or_query: str, table_id: u32, table_name: str)] pub rdb_num_rows_fetched: IntCounterVec, #[name = spacetime_num_index_keys_scanned_total] #[help = "The cumulative number of keys scanned from an index"] - #[labels(txn_type: WorkloadType, db: Address, reducer_or_query: str, table_id: u32, table_name: str)] + #[labels(txn_type: WorkloadType, db: Identity, reducer_or_query: str, table_id: u32, table_name: str)] pub rdb_num_keys_scanned: IntCounterVec, #[name = spacetime_num_index_seeks_total] #[help = "The cumulative number of index seeks"] - #[labels(txn_type: WorkloadType, db: Address, reducer_or_query: str, table_id: u32, table_name: str)] + #[labels(txn_type: WorkloadType, db: Identity, reducer_or_query: str, table_id: u32, table_name: str)] pub rdb_num_index_seeks: IntCounterVec, #[name = spacetime_num_txns_total] #[help = "The cumulative number of transactions, including both commits and rollbacks"] - #[labels(txn_type: WorkloadType, db: Address, reducer: str, committed: bool)] + #[labels(txn_type: WorkloadType, db: Identity, reducer: str, committed: bool)] pub rdb_num_txns: IntCounterVec, #[name = spacetime_txn_elapsed_time_sec] #[help = "The total elapsed (wall) time of a transaction (in seconds)"] - #[labels(txn_type: WorkloadType, db: Address, reducer: str)] + #[labels(txn_type: WorkloadType, db: Identity, reducer: str)] // Prometheus histograms have default buckets, // which broadly speaking, // are tailored to measure the response time of a network service. @@ -58,7 +58,7 @@ metrics_group!( #[name = spacetime_txn_cpu_time_sec] #[help = "The time spent executing a transaction (in seconds), excluding time spent waiting to acquire database locks"] - #[labels(txn_type: WorkloadType, db: Address, reducer: str)] + #[labels(txn_type: WorkloadType, db: Identity, reducer: str)] // Prometheus histograms have default buckets, // which broadly speaking, // are tailored to measure the response time of a network service. @@ -71,17 +71,17 @@ metrics_group!( #[name = spacetime_message_log_size_bytes] #[help = "For a given database, the number of bytes occupied by its message log"] - #[labels(db: Address)] + #[labels(db: Identity)] pub message_log_size: IntGaugeVec, #[name = spacetime_module_log_file_size_bytes] #[help = "For a given module, the size of its log file (in bytes)"] - #[labels(db: Address)] + #[labels(db: Identity)] pub module_log_file_size: IntGaugeVec, #[name = spacetime_table_size_bytes] #[help = "The number of bytes in a table with the precision of a page size"] - #[labels(db: Address, table_id: u32, table_name: str)] + #[labels(db: Identity, table_id: u32, table_name: str)] pub rdb_table_size: IntGaugeVec, } ); @@ -89,9 +89,9 @@ metrics_group!( pub static DB_METRICS: Lazy = Lazy::new(DbMetrics::new); /// Returns the number of committed rows in the table named by `table_name` and identified by `table_id` in the database `db_address`. -pub fn table_num_rows(db_address: Address, table_id: TableId, table_name: &str) -> u64 { +pub fn table_num_rows(db_identity: Identity, table_id: TableId, table_name: &str) -> u64 { DB_METRICS .rdb_num_table_rows - .with_label_values(&db_address, &table_id.0, table_name) + .with_label_values(&db_identity, &table_id.0, table_name) .get() as _ } diff --git a/crates/core/src/db/relational_db.rs b/crates/core/src/db/relational_db.rs index bfb0b33ef2d..88c505ea956 100644 --- a/crates/core/src/db/relational_db.rs +++ b/crates/core/src/db/relational_db.rs @@ -81,7 +81,7 @@ pub type ConnectedClients = HashSet<(Identity, Address)>; #[derive(Clone)] pub struct RelationalDB { - address: Address, + database_identity: Identity, owner_identity: Identity, inner: Locking, @@ -140,7 +140,7 @@ impl SnapshotWorker { }; log::info!( "Capturing snapshot of database {:?} at TX offset {}", - snapshot_repo.database_address(), + snapshot_repo.database_identity(), tx_offset, ); @@ -155,12 +155,12 @@ impl SnapshotWorker { if let Err(e) = snapshot_repo.create_snapshot(tables.values_mut(), blob_store, tx_offset) { log::error!( "Error capturing snapshot of database {:?}: {e:?}", - snapshot_repo.database_address() + snapshot_repo.database_identity() ); } else { log::info!( "Captured snapshot of database {:?} at TX offset {} in {:?}", - snapshot_repo.database_address(), + snapshot_repo.database_identity(), tx_offset, start_time.elapsed() ); @@ -175,14 +175,16 @@ const SNAPSHOT_FREQUENCY: u64 = 1_000_000; impl std::fmt::Debug for RelationalDB { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("RelationalDB").field("address", &self.address).finish() + f.debug_struct("RelationalDB") + .field("address", &self.database_identity) + .finish() } } impl RelationalDB { fn new( lock: LockFile, - address: Address, + database_identity: Identity, owner_identity: Identity, inner: Locking, durability: Option<(Arc>, DiskSizeFn)>, @@ -196,10 +198,10 @@ impl RelationalDB { durability, snapshot_worker, - address, + database_identity, owner_identity, - row_count_fn: default_row_count_fn(address), + row_count_fn: default_row_count_fn(database_identity), disk_size_fn, _lock: lock, @@ -287,13 +289,13 @@ impl RelationalDB { /// [ModuleHost]: crate::host::module_host::ModuleHost pub fn open( root: &Path, - address: Address, + database_identity: Identity, owner_identity: Identity, history: impl durability::History, durability: Option<(Arc>, DiskSizeFn)>, snapshot_repo: Option>, ) -> Result<(Self, ConnectedClients), DBError> { - log::trace!("[{}] DATABASE: OPEN", address); + log::trace!("[{}] DATABASE: OPEN", database_identity); let lock = LockFile::lock(root)?; @@ -306,15 +308,28 @@ impl RelationalDB { .as_deref() .and_then(|durability| durability.durable_tx_offset()); - log::info!("[{address}] DATABASE: durable_tx_offset is {durable_tx_offset:?}"); - let inner = Self::restore_from_snapshot_or_bootstrap(address, snapshot_repo.as_deref(), durable_tx_offset)?; + log::info!("[{database_identity}] DATABASE: durable_tx_offset is {durable_tx_offset:?}"); + let inner = + Self::restore_from_snapshot_or_bootstrap(database_identity, snapshot_repo.as_deref(), durable_tx_offset)?; - apply_history(&inner, address, history)?; - let db = Self::new(lock, address, owner_identity, inner, durability, snapshot_repo); + apply_history(&inner, database_identity, history)?; + let db = Self::new( + lock, + database_identity, + owner_identity, + inner, + durability, + snapshot_repo, + ); if let Some(meta) = db.metadata()? { - if meta.database_address != address { - return Err(anyhow!("mismatched database address: {} != {}", meta.database_address, address).into()); + if meta.database_identity != database_identity { + return Err(anyhow!( + "mismatched database identity: {} != {}", + meta.database_identity, + database_identity + ) + .into()); } if meta.owner_identity != owner_identity { return Err(anyhow!( @@ -341,7 +356,7 @@ impl RelationalDB { pub fn set_initialized(&self, tx: &mut MutTx, host_type: HostType, program: Program) -> Result<(), DBError> { log::trace!( "[{}] DATABASE: set initialized owner={} program_hash={}", - self.address, + self.database_identity, self.owner_identity, program.hash ); @@ -350,15 +365,15 @@ impl RelationalDB { // Ignore if it would be a no-op. if let Some(meta) = self.inner.metadata_mut_tx(tx)? { if program.hash == meta.program_hash - && self.address == meta.database_address + && self.database_identity == meta.database_identity && self.owner_identity == meta.owner_identity { return Ok(()); } - return Err(anyhow!("database {} already initialized", self.address).into()); + return Err(anyhow!("database {} already initialized", self.database_identity).into()); } let row = StModuleRow { - database_address: self.address.into(), + database_identity: self.database_identity.into(), owner_identity: self.owner_identity.into(), program_kind: match host_type { @@ -375,7 +390,7 @@ impl RelationalDB { /// /// `None` if the database is not yet fully initialized. pub fn metadata(&self) -> Result, DBError> { - let ctx = ExecutionContext::internal(self.address); + let ctx = ExecutionContext::internal(self.database_identity); self.with_read_only(&ctx, |tx| self.inner.metadata(&ctx, tx)) } @@ -384,13 +399,13 @@ impl RelationalDB { /// `None` if the database is not yet fully initialized. /// Note that a `Some` result may yield an empty slice. pub fn program(&self) -> Result, DBError> { - let ctx = ExecutionContext::internal(self.address); + let ctx = ExecutionContext::internal(self.database_identity); self.with_read_only(&ctx, |tx| self.inner.program(&ctx, tx)) } /// Read the set of clients currently connected to the database. pub fn connected_clients(&self) -> Result { - let ctx = ExecutionContext::internal(self.address); + let ctx = ExecutionContext::internal(self.database_identity); self.with_read_only(&ctx, |tx| self.inner.connected_clients(&ctx, tx)?.collect()) } @@ -412,7 +427,7 @@ impl RelationalDB { } fn restore_from_snapshot_or_bootstrap( - address: Address, + database_identity: Identity, snapshot_repo: Option<&SnapshotRepository>, durable_tx_offset: Option, ) -> Result { @@ -423,34 +438,34 @@ impl RelationalDB { if let Some(tx_offset) = snapshot_repo.latest_snapshot_older_than(durable_tx_offset)? { // Mark any newer snapshots as invalid, as the new history will diverge from their state. snapshot_repo.invalidate_newer_snapshots(durable_tx_offset)?; - log::info!("[{address}] DATABASE: restoring snapshot of tx_offset {tx_offset}"); + log::info!("[{database_identity}] DATABASE: restoring snapshot of tx_offset {tx_offset}"); let start = std::time::Instant::now(); let snapshot = snapshot_repo.read_snapshot(tx_offset)?; log::info!( - "[{address}] DATABASE: read snapshot of tx_offset {tx_offset} in {:?}", + "[{database_identity}] DATABASE: read snapshot of tx_offset {tx_offset} in {:?}", start.elapsed(), ); - if snapshot.database_address != address { + if snapshot.database_identity != database_identity { // TODO: return a proper typed error return Err(anyhow::anyhow!( - "Snapshot has incorrect database_address: expected {address} but found {}", - snapshot.database_address, + "Snapshot has incorrect database_address: expected {database_identity} but found {}", + snapshot.database_identity, ) .into()); } let start = std::time::Instant::now(); let res = Locking::restore_from_snapshot(snapshot); log::info!( - "[{address}] DATABASE: restored from snapshot of tx_offset {tx_offset} in {:?}", + "[{database_identity}] DATABASE: restored from snapshot of tx_offset {tx_offset} in {:?}", start.elapsed(), ); return res; } } - log::info!("[{address}] DATABASE: no snapshot on disk"); + log::info!("[{database_identity}] DATABASE: no snapshot on disk"); } - Locking::bootstrap(address) + Locking::bootstrap(database_identity) } /// Apply the provided [`spacetimedb_durability::History`] onto the database @@ -463,7 +478,7 @@ impl RelationalDB { where T: durability::History, { - apply_history(&self.inner, self.address, history)?; + apply_history(&self.inner, self.database_identity, history)?; Ok(self) } @@ -479,9 +494,9 @@ impl RelationalDB { self } - /// Returns the address for this database - pub fn address(&self) -> Address { - self.address + /// Returns the identity for this database + pub fn database_identity(&self) -> Identity { + self.database_identity } /// The number of bytes on disk occupied by the durability layer. @@ -519,12 +534,12 @@ impl RelationalDB { pub fn get_all_tables_mut(&self, tx: &MutTx) -> Result>, DBError> { self.inner - .get_all_tables_mut_tx(&ExecutionContext::internal(self.address), tx) + .get_all_tables_mut_tx(&ExecutionContext::internal(self.database_identity), tx) } pub fn get_all_tables(&self, tx: &Tx) -> Result>, DBError> { self.inner - .get_all_tables_tx(&ExecutionContext::internal(self.address), tx) + .get_all_tables_tx(&ExecutionContext::internal(self.database_identity), tx) } pub fn table_scheduled_id_and_at( @@ -914,7 +929,7 @@ impl RelationalDB { self.inner.drop_table_mut_tx(tx, table_id).map(|_| { DB_METRICS .rdb_num_table_rows - .with_label_values(&self.address, &table_id.into(), &table_name) + .with_label_values(&self.database_identity, &table_id.into(), &table_name) .set(0) }) } @@ -1023,12 +1038,12 @@ impl RelationalDB { tx: &mut MutTx, row_level_security_schema: RowLevelSecuritySchema, ) -> Result { - let ctx = &ExecutionContext::internal(self.inner.database_address); + let ctx = &ExecutionContext::internal(self.inner.database_identity); tx.create_row_level_security(ctx, row_level_security_schema) } pub fn drop_row_level_security(&self, tx: &mut MutTx, sql: RawSql) -> Result<(), DBError> { - let ctx = &ExecutionContext::internal(self.inner.database_address); + let ctx = &ExecutionContext::internal(self.inner.database_identity); tx.drop_row_level_security(ctx, sql) } @@ -1037,7 +1052,7 @@ impl RelationalDB { tx: &mut MutTx, table_id: TableId, ) -> Result, DBError> { - let ctx = &ExecutionContext::internal(self.inner.database_address); + let ctx = &ExecutionContext::internal(self.inner.database_identity); tx.row_level_security_for_table_id(ctx, table_id) } @@ -1162,7 +1177,7 @@ impl RelationalDB { /// Clear all rows from a table without dropping it. pub fn clear_table(&self, tx: &mut MutTx, table_id: TableId) -> Result<(), DBError> { let relation = self - .iter_mut(&ExecutionContext::internal(self.address), tx, table_id)? + .iter_mut(&ExecutionContext::internal(self.database_identity), tx, table_id)? .map(|row_ref| row_ref.pointer()) .collect::>(); self.delete(tx, table_id, relation); @@ -1217,11 +1232,11 @@ impl fmt::Debug for LockFile { } } -fn apply_history(datastore: &Locking, address: Address, history: H) -> Result<(), DBError> +fn apply_history(datastore: &Locking, database_identity: Identity, history: H) -> Result<(), DBError> where H: durability::History, { - log::info!("[{}] DATABASE: applying transaction history...", address); + log::info!("[{}] DATABASE: applying transaction history...", database_identity); // TODO: Revisit once we actually replay history suffixes, ie. starting // from an offset larger than the history's min offset. @@ -1235,12 +1250,18 @@ where if let Some(max_tx_offset) = max_tx_offset { let percentage = f64::floor((tx_offset as f64 / max_tx_offset as f64) * 100.0) as i32; if percentage > last_logged_percentage && percentage % 10 == 0 { - log::info!("[{}] Loaded {}% ({}/{})", address, percentage, tx_offset, max_tx_offset); + log::info!( + "[{}] Loaded {}% ({}/{})", + database_identity, + percentage, + tx_offset, + max_tx_offset + ); last_logged_percentage = percentage; } // Print _something_ even if we don't know what's still ahead. } else if tx_offset % 10_000 == 0 { - log::info!("[{}] Loading transaction {}", address, tx_offset); + log::info!("[{}] Loading transaction {}", database_identity, tx_offset); } }; @@ -1249,9 +1270,9 @@ where history .fold_transactions_from(start, &mut replay) .map_err(anyhow::Error::from)?; - log::info!("[{}] DATABASE: applied transaction history", address); + log::info!("[{}] DATABASE: applied transaction history", database_identity); datastore.rebuild_state_after_replay()?; - log::info!("[{}] DATABASE: rebuilt state after replay", address); + log::info!("[{}] DATABASE: rebuilt state after replay", database_identity); Ok(()) } @@ -1294,21 +1315,21 @@ pub async fn local_durability(db_path: &Path) -> io::Result<(Arc Result, Box> { let snapshot_dir = db_path.join("snapshots"); std::fs::create_dir_all(&snapshot_dir).map_err(|e| Box::new(SnapshotError::from(e)))?; - SnapshotRepository::open(snapshot_dir, database_address, replica_id) + SnapshotRepository::open(snapshot_dir, database_identity, replica_id) .map(Arc::new) .map_err(Box::new) } -fn default_row_count_fn(address: Address) -> RowCountFn { +fn default_row_count_fn(db: Identity) -> RowCountFn { Arc::new(move |table_id, table_name| { DB_METRICS .rdb_num_table_rows - .with_label_values(&address, &table_id.into(), table_name) + .with_label_values(&db, &table_id.into(), table_name) .get() }) } @@ -1347,7 +1368,8 @@ pub mod tests_utils { } impl TestDB { - pub const ADDRESS: Address = Address::ZERO; + pub const DATABASE_IDENTITY: Identity = Identity::ZERO; + // pub const DATABASE_IDENTITY: Identity = Identity::ZERO; pub const OWNER: Identity = Identity::ZERO; /// Create a [`TestDB`] which does not store data on disk. @@ -1473,7 +1495,7 @@ pub mod tests_utils { let (local, disk_size_fn) = rt.block_on(local_durability(root))?; let history = local.clone(); let durability = local.clone() as Arc>; - let snapshot_repo = open_snapshot_repo(root, Address::default(), 0)?; + let snapshot_repo = open_snapshot_repo(root, Identity::ZERO, 0)?; let db = Self::open_db(root, history, Some((durability, disk_size_fn)), Some(snapshot_repo))?; Ok((db, local)) @@ -1485,11 +1507,17 @@ pub mod tests_utils { durability: Option<(Arc>, DiskSizeFn)>, snapshot_repo: Option>, ) -> Result { - let (db, connected_clients) = - RelationalDB::open(root, Self::ADDRESS, Self::OWNER, history, durability, snapshot_repo)?; + let (db, connected_clients) = RelationalDB::open( + root, + Self::DATABASE_IDENTITY, + Self::OWNER, + history, + durability, + snapshot_repo, + )?; debug_assert!(connected_clients.is_empty()); let db = db.with_row_count(Self::row_count_fn()); - db.with_auto_commit(&ExecutionContext::internal(db.address()), |tx| { + db.with_auto_commit(&ExecutionContext::internal(db.database_identity()), |tx| { db.set_initialized(tx, HostType::Wasm, Program::empty()) })?; Ok(db) @@ -1614,7 +1642,7 @@ mod tests { match RelationalDB::open( stdb.path(), - Address::ZERO, + Identity::ZERO, Identity::ZERO, EmptyHistory::new(), None, @@ -2227,7 +2255,7 @@ mod tests { let timestamp = Timestamp::now(); let ctx = ExecutionContext::reducer( - stdb.address(), + stdb.database_identity(), ReducerContext { name: "abstract_concrete_proxy_factory_impl".into(), caller_identity: Identity::__dummy(), @@ -2252,7 +2280,7 @@ mod tests { let tx = stdb.begin_mut_tx(IsolationLevel::Serializable); stdb.commit_tx( &ExecutionContext::reducer( - stdb.address(), + stdb.database_identity(), ReducerContext { name: "__identity_connected__".into(), caller_identity: Identity::__dummy(), @@ -2283,7 +2311,7 @@ mod tests { let mut tx = stdb.begin_mut_tx(IsolationLevel::Serializable); stdb.insert(&mut tx, table_id, product!(AlgebraicValue::I32(-42))) .expect("failed to insert row"); - stdb.commit_tx(&ExecutionContext::sql(stdb.address()), tx) + stdb.commit_tx(&ExecutionContext::sql(stdb.database_identity()), tx) .expect("failed to commit tx"); } diff --git a/crates/core/src/db/update.rs b/crates/core/src/db/update.rs index 46ba4835447..9ed6b075192 100644 --- a/crates/core/src/db/update.rs +++ b/crates/core/src/db/update.rs @@ -79,9 +79,9 @@ fn auto_migrate_database( .map(|table| (table.table_name.clone(), table)) .collect::>(); - let ctx = &ExecutionContext::internal(stdb.address()); + let ctx = &ExecutionContext::internal(stdb.database_identity()); - log::info!("Running database update prechecks: {}", stdb.address()); + log::info!("Running database update prechecks: {}", stdb.database_identity()); for precheck in plan.prechecks { match precheck { @@ -110,7 +110,7 @@ fn auto_migrate_database( } } - log::info!("Running database update steps: {}", stdb.address()); + log::info!("Running database update steps: {}", stdb.database_identity()); for step in plan.steps { match step { diff --git a/crates/core/src/execution_context.rs b/crates/core/src/execution_context.rs index bb2fcbc1ea0..419c362846a 100644 --- a/crates/core/src/execution_context.rs +++ b/crates/core/src/execution_context.rs @@ -71,14 +71,14 @@ impl Metrics { } #[allow(dead_code)] - fn flush(&mut self, workload: &WorkloadType, database: &Address, reducer: &str) { + fn flush(&mut self, workload: &WorkloadType, database_identity: &Identity, reducer: &str) { macro_rules! flush_metric { ($db_metric:expr, $metric:expr, $metric_field:ident) => { if $metric.$metric_field > 0 { $db_metric .with_label_values( workload, - database, + database_identity, reducer, &$metric.table_id.0, &$metric.cache_table_name, @@ -101,8 +101,8 @@ impl Metrics { /// More generally it acts as a container for information that database operations may require to function correctly. #[derive(Default, Clone)] pub struct ExecutionContext { - /// The database on which a transaction is being executed. - database: Address, + /// The identity of the database on which a transaction is being executed. + database_identity: Identity, /// The reducer from which the current transaction originated. reducer: Option, /// The type of workload that is being executed. @@ -183,9 +183,9 @@ impl Default for WorkloadType { impl ExecutionContext { /// Returns an [ExecutionContext] with the provided parameters and empty metrics. - fn new(database: Address, reducer: Option, workload: WorkloadType) -> Self { + fn new(database_identity: Identity, reducer: Option, workload: WorkloadType) -> Self { Self { - database, + database_identity, reducer, workload, metrics: <_>::default(), @@ -193,40 +193,40 @@ impl ExecutionContext { } /// Returns an [ExecutionContext] for a reducer transaction. - pub fn reducer(database: Address, ctx: ReducerContext) -> Self { - Self::new(database, Some(ctx), WorkloadType::Reducer) + pub fn reducer(database_identity: Identity, ctx: ReducerContext) -> Self { + Self::new(database_identity, Some(ctx), WorkloadType::Reducer) } /// Returns an [ExecutionContext] for a one-off sql query. - pub fn sql(database: Address) -> Self { - Self::new(database, None, WorkloadType::Sql) + pub fn sql(database_identity: Identity) -> Self { + Self::new(database_identity, None, WorkloadType::Sql) } /// Returns an [ExecutionContext] for an initial subscribe call. - pub fn subscribe(database: Address) -> Self { + pub fn subscribe(database: Identity) -> Self { Self::new(database, None, WorkloadType::Subscribe) } /// Returns an [ExecutionContext] for a subscription update. - pub fn incremental_update(database: Address) -> Self { + pub fn incremental_update(database: Identity) -> Self { Self::new(database, None, WorkloadType::Update) } /// Returns an [ExecutionContext] for an incremental subscription update, /// where this update is the result of a reducer mutation. - pub fn incremental_update_for_reducer(database: Address, ctx: ReducerContext) -> Self { + pub fn incremental_update_for_reducer(database: Identity, ctx: ReducerContext) -> Self { Self::new(database, Some(ctx), WorkloadType::Update) } /// Returns an [ExecutionContext] for an internal database operation. - pub fn internal(database: Address) -> Self { - Self::new(database, None, WorkloadType::Internal) + pub fn internal(database_identity: Identity) -> Self { + Self::new(database_identity, None, WorkloadType::Internal) } /// Returns the address of the database on which we are operating. #[inline] - pub fn database(&self) -> Address { - self.database + pub fn database_identity(&self) -> Identity { + self.database_identity } /// If this is a reducer context, returns the name of the reducer. @@ -251,7 +251,7 @@ impl ExecutionContext { impl Drop for ExecutionContext { fn drop(&mut self) { let workload = self.workload; - let database = self.database; + let database = self.database_identity; let reducer = self.reducer_name(); self.metrics.write().flush(&workload, &database, reducer); } diff --git a/crates/core/src/host/host_controller.rs b/crates/core/src/host/host_controller.rs index 316e3afbc1d..eb52d8a63f5 100644 --- a/crates/core/src/host/host_controller.rs +++ b/crates/core/src/host/host_controller.rs @@ -228,7 +228,7 @@ impl HostController { { let guard = self.acquire_read_lock(replica_id).await; if let Some(host) = &*guard { - trace!("cached host {}/{}", database.address, replica_id); + trace!("cached host {}/{}", database.database_identity, replica_id); return Ok(host.module.subscribe()); } } @@ -238,11 +238,15 @@ impl HostController { // we'll need to check again if a module was added meanwhile. let mut guard = self.acquire_write_lock(replica_id).await; if let Some(host) = &*guard { - trace!("cached host {}/{} (lock upgrade)", database.address, replica_id); + trace!( + "cached host {}/{} (lock upgrade)", + database.database_identity, + replica_id + ); return Ok(host.module.subscribe()); } - trace!("launch host {}/{}", database.address, replica_id); + trace!("launch host {}/{}", database.database_identity, replica_id); let host = self.try_init_host(database, replica_id).await?; let rx = host.module.subscribe(); @@ -262,7 +266,7 @@ impl HostController { F: FnOnce(&RelationalDB) -> T + Send + 'static, T: Send + 'static, { - trace!("using database {}/{}", database.address, replica_id); + trace!("using database {}/{}", database.database_identity, replica_id); let module = self.get_or_launch_module_host(database, replica_id).await?; let on_panic = self.unregister_fn(replica_id); let result = tokio::task::spawn_blocking(move || f(&module.replica_ctx().relational_db)) @@ -297,7 +301,7 @@ impl HostController { }; trace!( "update module host {}/{}: genesis={} update-to={}", - database.address, + database.database_identity, replica_id, database.initial_program, program.hash @@ -347,9 +351,9 @@ impl HostController { replica_id: u64, expected_hash: Option, ) -> anyhow::Result> { - trace!("custom bootstrap {}/{}", database.address, replica_id); + trace!("custom bootstrap {}/{}", database.database_identity, replica_id); - let db_addr = database.address; + let db_addr = database.database_identity; let host_type = database.host_type; let program_hash = database.initial_program; @@ -502,7 +506,7 @@ async fn make_replica_ctx( replica_id: u64, relational_db: Arc, ) -> anyhow::Result { - let log_path = DatabaseLogger::filepath(&database.address, replica_id); + let log_path = DatabaseLogger::filepath(&database.database_identity, replica_id); let logger = tokio::task::block_in_place(|| Arc::new(DatabaseLogger::open(log_path))); let subscriptions = ModuleSubscriptions::new(relational_db.clone(), database.owner_identity); @@ -568,7 +572,7 @@ async fn launch_module( relational_db: Arc, energy_monitor: Arc, ) -> anyhow::Result<(Program, LaunchedModule)> { - let address = database.address; + let address = database.database_identity; let host_type = database.host_type; let replica_ctx = make_replica_ctx(database, replica_id, relational_db) @@ -613,7 +617,7 @@ async fn update_module( program: Program, old_module_info: Arc, ) -> anyhow::Result { - let addr = db.address(); + let addr = db.database_identity(); match stored_program_hash(db)? { None => Err(anyhow!("database `{}` not yet initialized", addr)), Some(stored) => { @@ -672,13 +676,13 @@ impl Host { on_panic: impl Fn() + Send + Sync + 'static, ) -> anyhow::Result { let mut db_path = root_dir.to_path_buf(); - db_path.extend([&*database.address.to_hex(), &*replica_id.to_string()]); + db_path.extend([&*database.database_identity.to_hex(), &*replica_id.to_string()]); db_path.push("database"); let (db, connected_clients) = match config.storage { db::Storage::Memory => RelationalDB::open( &db_path, - database.address, + database.database_identity, database.owner_identity, EmptyHistory::new(), None, @@ -686,11 +690,12 @@ impl Host { )?, db::Storage::Disk => { let (durability, disk_size_fn) = relational_db::local_durability(&db_path).await?; - let snapshot_repo = relational_db::open_snapshot_repo(&db_path, database.address, replica_id)?; + let snapshot_repo = + relational_db::open_snapshot_repo(&db_path, database.database_identity, replica_id)?; let history = durability.clone(); RelationalDB::open( &db_path, - database.address, + database.database_identity, database.owner_identity, history, Some((durability, disk_size_fn)), @@ -749,7 +754,7 @@ impl Host { .with_context(|| { format!( "Error calling disconnect for {} {} on {}", - identity, address, replica_ctx.address + identity, address, replica_ctx.database_identity ) })?; } @@ -846,13 +851,13 @@ async fn storage_monitor(replica_ctx: Arc, energy_monitor: Arc, subscriptions: ModuleSubscriptions, @@ -208,8 +208,8 @@ impl ModuleInfo { Arc::new(ModuleInfo { module_def, reducers_map, - identity, - address, + owner_identity, + database_identity, module_hash, log_tx, subscriptions, @@ -368,7 +368,7 @@ impl fmt::Debug for ModuleHost { #[async_trait::async_trait] trait DynModuleHost: Send + Sync + 'static { - async fn get_instance(&self, db: Address) -> Result, NoSuchModule>; + async fn get_instance(&self, db: Identity) -> Result, NoSuchModule>; fn replica_ctx(&self) -> &ReplicaContext; fn exit(&self) -> Closed<'_>; fn exited(&self) -> Closed<'_>; @@ -405,7 +405,7 @@ async fn select_first>(fut_a: A, fut_b: B) -> #[async_trait::async_trait] impl DynModuleHost for HostControllerActor { - async fn get_instance(&self, db: Address) -> Result, NoSuchModule> { + async fn get_instance(&self, db: Identity) -> Result, NoSuchModule> { // in the future we should do something like in the else branch here -- add more instances based on load. // we need to do write-skew retries first - right now there's only ever once instance per module. let inst = if true { @@ -522,9 +522,9 @@ impl ModuleHost { // Record the time spent waiting in the queue let _guard = WORKER_METRICS .reducer_wait_time - .with_label_values(&self.info.address, reducer) + .with_label_values(&self.info.database_identity, reducer) .start_timer(); - self.inner.get_instance(self.info.address).await? + self.inner.get_instance(self.info.database_identity).await? }; let result = tokio::task::spawn_blocking(move || f(&mut *inst)) @@ -569,7 +569,7 @@ impl ModuleHost { let db = &self.inner.replica_ctx().relational_db; let ctx = || { ExecutionContext::reducer( - db.address(), + db.database_identity(), ReducerContext { name: reducer_name.to_owned(), caller_identity, @@ -638,7 +638,7 @@ impl ModuleHost { connected: bool, ) -> Result<(), DBError> { let db = &*self.inner.replica_ctx().relational_db; - let ctx = &ExecutionContext::internal(db.address()); + let ctx = &ExecutionContext::internal(db.database_identity()); let row = &StClientRow { identity: caller_identity.into(), address: caller_address.into(), @@ -827,7 +827,7 @@ impl ModuleHost { let db = &replica_ctx.relational_db; let auth = AuthCtx::new(replica_ctx.owner_identity, caller_identity); log::debug!("One-off query: {query}"); - let ctx = &ExecutionContext::sql(db.address()); + let ctx = &ExecutionContext::sql(db.database_identity()); db.with_read_only(ctx, |tx| { let ast = sql::compiler::compile_sql(db, &auth, tx, &query)?; sql::execute::execute_sql_tx(db, tx, &query, ast, auth)? @@ -840,7 +840,7 @@ impl ModuleHost { /// Note: this doesn't drop the table, it just clears it! pub fn clear_table(&self, table_name: &str) -> Result<(), anyhow::Error> { let db = &*self.replica_ctx().relational_db; - db.with_auto_commit(&ExecutionContext::internal(db.address()), |tx| { + db.with_auto_commit(&ExecutionContext::internal(db.database_identity()), |tx| { let tables = db.get_all_tables_mut(tx)?; // We currently have unique table names, // so we can assume there's only one table to clear. diff --git a/crates/core/src/host/scheduler.rs b/crates/core/src/host/scheduler.rs index 57cf8ed5a41..6396d8baff7 100644 --- a/crates/core/src/host/scheduler.rs +++ b/crates/core/src/host/scheduler.rs @@ -87,7 +87,7 @@ impl SchedulerStarter { // time to make it better right now. pub fn start(mut self, module_host: &ModuleHost) -> anyhow::Result<()> { let mut queue: DelayQueue = DelayQueue::new(); - let ctx = &ExecutionContext::internal(self.db.address()); + let ctx = &ExecutionContext::internal(self.db.database_identity()); let tx = self.db.begin_tx(); // Draining rx before processing schedules from the DB to ensure there are no in-flight messages, @@ -302,8 +302,8 @@ impl SchedulerActor { return; }; let db = module_host.replica_ctx().relational_db.clone(); - let ctx = ExecutionContext::internal(db.address()); - let caller_identity = module_host.info().identity; + let ctx = ExecutionContext::internal(db.database_identity()); + let caller_identity = module_host.info().owner_identity; let module_info = module_host.info.clone(); let call_reducer_params = move |tx: &MutTxId| -> Result, anyhow::Error> { @@ -360,7 +360,7 @@ impl SchedulerActor { let db = module_host.replica_ctx().relational_db.clone(); let module_host_clone = module_host.clone(); - let ctx = ExecutionContext::internal(db.address()); + let ctx = ExecutionContext::internal(db.database_identity()); let res = tokio::spawn(async move { module_host.call_scheduled_reducer(call_reducer_params).await }).await; @@ -435,7 +435,7 @@ impl SchedulerActor { } fn commit_and_broadcast_deletion_event(ctx: &ExecutionContext, tx: MutTxId, module_host: ModuleHost) { - let caller_identity = module_host.info().identity; + let caller_identity = module_host.info().owner_identity; let event = ModuleEvent { timestamp: Timestamp::now(), diff --git a/crates/core/src/host/wasm_common/module_host_actor.rs b/crates/core/src/host/wasm_common/module_host_actor.rs index 852b7e2bb74..ea6fb82912e 100644 --- a/crates/core/src/host/wasm_common/module_host_actor.rs +++ b/crates/core/src/host/wasm_common/module_host_actor.rs @@ -139,7 +139,7 @@ impl WasmModuleHostActor { let module_hash = program.hash; log::trace!( "Making new module host actor for database {} with module {}", - replica_context.address, + replica_context.database_identity, module_hash, ); let log_tx = replica_context.logger.tx.clone(); @@ -165,7 +165,7 @@ impl WasmModuleHostActor { let info = ModuleInfo::new( def, replica_context.owner_identity, - replica_context.address, + replica_context.database_identity, module_hash, log_tx, replica_context.subscriptions.clone(), @@ -263,7 +263,7 @@ impl ModuleInstance for WasmModuleInstance { log::debug!("init database"); let timestamp = Timestamp::now(); let stdb = &*self.replica_context().relational_db; - let ctx = ExecutionContext::internal(stdb.address()); + let ctx = ExecutionContext::internal(stdb.database_identity()); let auth_ctx = AuthCtx::for_current(self.replica_context().database.owner_identity); let tx = stdb.begin_mut_tx(IsolationLevel::Serializable); let (tx, ()) = stdb @@ -343,7 +343,7 @@ impl ModuleInstance for WasmModuleInstance { } }; let stdb = &*self.replica_context().relational_db; - let ctx = Lazy::new(|| ExecutionContext::internal(stdb.address())); + let ctx = Lazy::new(|| ExecutionContext::internal(stdb.database_identity())); let program_hash = program.hash; let tx = stdb.begin_mut_tx(IsolationLevel::Serializable); @@ -355,7 +355,7 @@ impl ModuleInstance for WasmModuleInstance { match res { Err(e) => { - log::warn!("Database update failed: {} @ {}", e, stdb.address()); + log::warn!("Database update failed: {} @ {}", e, stdb.database_identity()); self.system_logger().warn(&format!("Database update failed: {e}")); stdb.rollback_mut_tx(&ctx, tx); Ok(UpdateDatabaseResult::ErrorExecutingMigration(e)) @@ -363,7 +363,7 @@ impl ModuleInstance for WasmModuleInstance { Ok(()) => { stdb.commit_tx(&ctx, tx)?; self.system_logger().info("Database updated"); - log::info!("Database updated, {}", stdb.address()); + log::info!("Database updated, {}", stdb.database_identity()); Ok(UpdateDatabaseResult::UpdatePerformed) } } @@ -407,7 +407,7 @@ impl WasmModuleInstance { let replica_ctx = self.replica_context(); let stdb = &*replica_ctx.relational_db.clone(); - let address = replica_ctx.address; + let address = replica_ctx.database_identity; let reducer_name = self .info .reducers_map @@ -423,7 +423,7 @@ impl WasmModuleInstance { let energy_fingerprint = ReducerFingerprint { module_hash: self.info.module_hash, - module_identity: self.info.identity, + module_identity: self.info.owner_identity, caller_identity, reducer_name, }; diff --git a/crates/core/src/messages/control_db.rs b/crates/core/src/messages/control_db.rs index 4525432af19..4ec5c7c80d4 100644 --- a/crates/core/src/messages/control_db.rs +++ b/crates/core/src/messages/control_db.rs @@ -3,8 +3,6 @@ use spacetimedb_sats::de::Deserialize; use spacetimedb_sats::hash::Hash; use spacetimedb_sats::ser::Serialize; -use crate::address::Address; - #[derive(Clone, PartialEq, Serialize, Deserialize)] pub struct IdentityEmail { pub identity: Identity, @@ -26,8 +24,8 @@ pub struct EnergyBalance { pub struct Database { /// Internal id of the database, assigned by the control database. pub id: u64, - /// Public identity (i.e. [`Address`]) of the database. - pub address: Address, + /// Public identity (i.e. [`Identity`]) of the database. + pub database_identity: Identity, /// [`Identity`] of the database's owner. pub owner_identity: Identity, /// [`HostType`] of the module associated with the database. diff --git a/crates/core/src/replica_context.rs b/crates/core/src/replica_context.rs index 0a88f345b77..c7fab2b5753 100644 --- a/crates/core/src/replica_context.rs +++ b/crates/core/src/replica_context.rs @@ -23,7 +23,7 @@ pub struct ReplicaContext { impl ReplicaContext { pub fn scheduler_db_path(&self, root_db_path: PathBuf) -> PathBuf { let mut scheduler_db_path = root_db_path; - scheduler_db_path.extend([&*self.address.to_hex(), &*self.replica_id.to_string()]); + scheduler_db_path.extend([&*self.database_identity.to_hex(), &*self.replica_id.to_string()]); scheduler_db_path.push("scheduler"); scheduler_db_path } diff --git a/crates/core/src/sql/execute.rs b/crates/core/src/sql/execute.rs index ca376970102..a8c74121912 100644 --- a/crates/core/src/sql/execute.rs +++ b/crates/core/src/sql/execute.rs @@ -61,7 +61,7 @@ pub(crate) fn collect_result( } pub fn ctx_sql(db: &RelationalDB) -> ExecutionContext { - ExecutionContext::sql(db.address()) + ExecutionContext::sql(db.database_identity()) } fn execute( @@ -321,7 +321,7 @@ pub(crate) mod tests { let tx = db.begin_tx(); let schema = db.schema_for_table(&tx, ST_TABLE_ID).unwrap(); - db.release_tx(&ExecutionContext::internal(db.address()), tx); + db.release_tx(&ExecutionContext::internal(db.database_identity()), tx); let result = run_for_testing( &db, &format!("SELECT * FROM {} WHERE table_id = {}", ST_TABLE_NAME, ST_TABLE_ID), diff --git a/crates/core/src/subscription/module_subscription_actor.rs b/crates/core/src/subscription/module_subscription_actor.rs index f77272b92f0..9940f968877 100644 --- a/crates/core/src/subscription/module_subscription_actor.rs +++ b/crates/core/src/subscription/module_subscription_actor.rs @@ -55,7 +55,7 @@ impl ModuleSubscriptions { timer: Instant, _assert: Option, ) -> Result<(), DBError> { - let ctx = ExecutionContext::subscribe(self.relational_db.address()); + let ctx = ExecutionContext::subscribe(self.relational_db.database_identity()); let tx = scopeguard::guard(self.relational_db.begin_tx(), |tx| { self.relational_db.release_tx(&ctx, tx); }); @@ -151,7 +151,7 @@ impl ModuleSubscriptions { WORKER_METRICS .subscription_queries - .with_label_values(&self.relational_db.address()) + .with_label_values(&self.relational_db.database_identity()) .set(num_queries as i64); #[cfg(test)] @@ -176,7 +176,7 @@ impl ModuleSubscriptions { subscriptions.remove_subscription(&(client_id.identity, client_id.address)); WORKER_METRICS .subscription_queries - .with_label_values(&self.relational_db.address()) + .with_label_values(&self.relational_db.database_identity()) .set(subscriptions.num_queries() as i64); } @@ -215,9 +215,9 @@ impl ModuleSubscriptions { // New execution context for the incremental subscription update. // TODO: The tx and the ExecutionContext should be coupled together. let ctx = if let Some(reducer_ctx) = ctx.reducer_context() { - ExecutionContext::incremental_update_for_reducer(stdb.address(), reducer_ctx.clone()) + ExecutionContext::incremental_update_for_reducer(stdb.database_identity(), reducer_ctx.clone()) } else { - ExecutionContext::incremental_update(stdb.address()) + ExecutionContext::incremental_update(stdb.database_identity()) }; match &event.status { diff --git a/crates/core/src/subscription/module_subscription_manager.rs b/crates/core/src/subscription/module_subscription_manager.rs index 0d78dbc3c9a..324f0051b47 100644 --- a/crates/core/src/subscription/module_subscription_manager.rs +++ b/crates/core/src/subscription/module_subscription_manager.rs @@ -531,7 +531,7 @@ mod tests { timer: None, }); - let ctx = ExecutionContext::incremental_update(db.address()); + let ctx = ExecutionContext::incremental_update(db.database_identity()); db.with_read_only(&ctx, |tx| { subscriptions.eval_updates(&ctx, &db, tx, event, Some(&client0), None) }); diff --git a/crates/core/src/subscription/query.rs b/crates/core/src/subscription/query.rs index 0f20b5b67c8..2e0c8e08f98 100644 --- a/crates/core/src/subscription/query.rs +++ b/crates/core/src/subscription/query.rs @@ -265,7 +265,7 @@ mod tests { total_tables: usize, rows: &[ProductValue], ) -> ResultTest<()> { - let ctx = &ExecutionContext::incremental_update(db.address()); + let ctx = &ExecutionContext::incremental_update(db.database_identity()); let tx = &tx.into(); let update = update.tables.iter().collect::>(); let result = s.eval_incr_for_test(ctx, db, tx, &update, None); @@ -371,7 +371,7 @@ mod tests { let query: ExecutionSet = singleton_execution_set(query, sql.into())?; - let ctx = &ExecutionContext::incremental_update(db.address()); + let ctx = &ExecutionContext::incremental_update(db.database_identity()); let tx = (&tx).into(); let update = update.tables.iter().collect::>(); let result = query.eval_incr_for_test(ctx, &db, &tx, &update, None); @@ -552,7 +552,7 @@ mod tests { let row_2 = product!(2u64, "jhon doe"); let tx = db.begin_tx(); let s = get_all(&db, &tx, &AuthCtx::for_testing())?.into(); - let ctx = ExecutionContext::subscribe(db.address()); + let ctx = ExecutionContext::subscribe(db.database_identity()); check_query_eval(&ctx, &db, &tx, &s, 2, &[row_1.clone(), row_2.clone()])?; let data1 = DatabaseTableUpdate { diff --git a/crates/core/src/util/lending_pool.rs b/crates/core/src/util/lending_pool.rs index d9295f69b4d..6c29ef4498d 100644 --- a/crates/core/src/util/lending_pool.rs +++ b/crates/core/src/util/lending_pool.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use std::task::{Context, Poll}; use parking_lot::Mutex; -use spacetimedb_lib::Address; +use spacetimedb_lib::Identity; use tokio::sync::{OwnedSemaphorePermit, Semaphore}; use crate::worker_metrics::WORKER_METRICS; @@ -52,7 +52,7 @@ pub struct PoolClosed; /// A scope guard for the reducer queue length metric, /// ensuring an increment is always be paired with one and only one decrement. struct QueueMetric { - db: Address, + db: Identity, } impl Drop for QueueMetric { @@ -67,7 +67,7 @@ impl Drop for QueueMetric { } impl QueueMetric { - fn inc(db: Address) -> Self { + fn inc(db: Identity) -> Self { WORKER_METRICS.instance_queue_length.with_label_values(&db).inc(); let queue_length = WORKER_METRICS.instance_queue_length.with_label_values(&db).get(); WORKER_METRICS @@ -83,7 +83,7 @@ impl LendingPool { Self::from_iter(std::iter::empty()) } - pub fn request_with_context(&self, db: Address) -> impl Future, PoolClosed>> { + pub fn request_with_context(&self, db: Identity) -> impl Future, PoolClosed>> { let acq = self.sem.clone().acquire_owned(); let pool_inner = self.inner.clone(); diff --git a/crates/core/src/worker_metrics/mod.rs b/crates/core/src/worker_metrics/mod.rs index 6b4ff3f034a..8dbd3e5b1ae 100644 --- a/crates/core/src/worker_metrics/mod.rs +++ b/crates/core/src/worker_metrics/mod.rs @@ -9,7 +9,7 @@ metrics_group!( pub struct WorkerMetrics { #[name = spacetime_worker_connected_clients] #[help = "Number of clients connected to the worker."] - #[labels(database_address: Address)] + #[labels(database_identity: Identity)] pub connected_clients: IntGaugeVec, #[name = spacetime_websocket_requests_total] @@ -24,7 +24,7 @@ metrics_group!( #[name = spacetime_websocket_sent_msg_size_bytes] #[help = "The size of messages sent to connected sessions"] - #[labels(db: Address, workload: WorkloadType)] + #[labels(db: Identity, workload: WorkloadType)] // Prometheus histograms have default buckets, // which broadly speaking, // are tailored to measure the response time of a network service. @@ -39,7 +39,7 @@ metrics_group!( #[name = spacetime_websocket_sent_num_rows] #[help = "The number of rows sent to connected sessions"] - #[labels(db: Address, workload: WorkloadType)] + #[labels(db: Identity, workload: WorkloadType)] // Prometheus histograms have default buckets, // which broadly speaking, // are tailored to measure the response time of a network service. @@ -54,12 +54,12 @@ metrics_group!( #[name = spacetime_worker_instance_operation_queue_length] #[help = "Length of the wait queue for access to a module instance."] - #[labels(database_address: Address)] + #[labels(database_identity: Identity)] pub instance_queue_length: IntGaugeVec, #[name = spacetime_worker_instance_operation_queue_length_histogram] #[help = "Length of the wait queue for access to a module instance."] - #[labels(database_address: Address)] + #[labels(database_identity: Identity)] // Prometheus histograms have default buckets, // which broadly speaking, // are tailored to measure the response time of a network service. @@ -69,7 +69,7 @@ metrics_group!( #[name = spacetime_reducer_wait_time_sec] #[help = "The amount of time (in seconds) a reducer spends in the queue waiting to run"] - #[labels(db: Address, reducer: str)] + #[labels(db: Identity, reducer: str)] // Prometheus histograms have default buckets, // which broadly speaking, // are tailored to measure the response time of a network service. @@ -82,22 +82,22 @@ metrics_group!( #[name = spacetime_worker_wasm_instance_errors_total] #[help = "The number of fatal WASM instance errors, such as reducer panics."] - #[labels(identity: Identity, module_hash: Hash, database_address: Address, reducer_symbol: str)] + #[labels(caller_identity: Identity, module_hash: Hash, caller_address: Address, reducer_symbol: str)] pub wasm_instance_errors: IntCounterVec, #[name = spacetime_active_queries] #[help = "The number of active subscription queries"] - #[labels(database_address: Address)] + #[labels(database_identity: Identity)] pub subscription_queries: IntGaugeVec, #[name = spacetime_request_round_trip_time] #[help = "The total time it takes for request to complete"] - #[labels(txn_type: WorkloadType, database_address: Address, reducer_symbol: str)] + #[labels(txn_type: WorkloadType, database_identity: Identity, reducer_symbol: str)] pub request_round_trip: HistogramVec, #[name = spacetime_reducer_plus_query_duration_sec] #[help = "The time spent executing a reducer (in seconds), plus the time spent evaluating its subscription queries"] - #[labels(db: Address, reducer: str)] + #[labels(db: Identity, reducer: str)] pub reducer_plus_query_duration: HistogramVec, } ); diff --git a/crates/lib/Cargo.toml b/crates/lib/Cargo.toml index 7a056075709..091f64ed6aa 100644 --- a/crates/lib/Cargo.toml +++ b/crates/lib/Cargo.toml @@ -42,6 +42,7 @@ thiserror.workspace = true # For the 'proptest' feature. proptest = { workspace = true, optional = true } proptest-derive = { workspace = true, optional = true } +rand.workspace = true [dev-dependencies] spacetimedb-sats = { workspace = true, features = ["test"] } diff --git a/crates/lib/src/identity.rs b/crates/lib/src/identity.rs index b2e262d1263..8ea8b4911a0 100644 --- a/crates/lib/src/identity.rs +++ b/crates/lib/src/identity.rs @@ -47,9 +47,19 @@ impl spacetimedb_metrics::typed_prometheus::AsPrometheusLabel for Identity { } } +use rand; +use rand::Rng; + impl Identity { pub const ZERO: Self = Self::from_u256(u256::ZERO); + pub fn placeholder() -> Self { + // Generate a random identity. + let mut rng = rand::thread_rng(); + let mut random_bytes = [0u8; 32]; + rng.fill(&mut random_bytes); + Identity::from_byte_array(random_bytes) + } /// Returns an `Identity` defined as the given `bytes` byte array. pub const fn from_byte_array(bytes: [u8; 32]) -> Self { // SAFETY: The transmute is an implementation of `u256::from_ne_bytes`, diff --git a/crates/sdk/src/db_connection.rs b/crates/sdk/src/db_connection.rs index 12883f8ae2d..f035d878835 100644 --- a/crates/sdk/src/db_connection.rs +++ b/crates/sdk/src/db_connection.rs @@ -861,9 +861,9 @@ but you must call one of them, or else the connection will never progress. self } - /// Set the name or address of the remote module. - pub fn with_module_name(mut self, name_or_address: impl ToString) -> Self { - self.module_name = Some(name_or_address.to_string()); + /// Set the name or identity of the remote module. + pub fn with_module_name(mut self, name_or_identity: impl ToString) -> Self { + self.module_name = Some(name_or_identity.to_string()); self } diff --git a/crates/snapshot/src/lib.rs b/crates/snapshot/src/lib.rs index 325c2ce2ac2..746f072685a 100644 --- a/crates/snapshot/src/lib.rs +++ b/crates/snapshot/src/lib.rs @@ -32,7 +32,7 @@ use spacetimedb_lib::{ bsatn::{self}, de::Deserialize, ser::Serialize, - Address, + Identity, }; use spacetimedb_primitives::TableId; use spacetimedb_table::{ @@ -160,8 +160,8 @@ pub struct Snapshot { /// The snapshot version number. Must be equal to [`CURRENT_SNAPSHOT_VERSION`]. version: u8, - /// The address of the snapshotted database. - pub database_address: Address, + /// The identity of the snapshotted database. + pub database_identity: Identity, /// The instance ID of the snapshotted database. pub replica_id: u64, @@ -467,7 +467,7 @@ pub struct SnapshotRepository { root: PathBuf, /// The database address of the database instance for which this repository stores snapshots. - database_address: Address, + database_identity: Identity, /// The database instance ID of the database instance for which this repository stores snapshots. replica_id: u64, @@ -478,8 +478,8 @@ pub struct SnapshotRepository { impl SnapshotRepository { /// Returns [`Address`] of the database this [`SnapshotRepository`] is configured to snapshot. - pub fn database_address(&self) -> Address { - self.database_address + pub fn database_identity(&self) -> Identity { + self.database_identity } /// Capture a snapshot of the state of the database at `tx_offset`, @@ -547,7 +547,7 @@ impl SnapshotRepository { log::info!( "[{}] SNAPSHOT {:0>20}: Hardlinked {} objects and wrote {} objects", - self.database_address, + self.database_identity, tx_offset, counter.objects_hardlinked, counter.objects_written, @@ -562,7 +562,7 @@ impl SnapshotRepository { Snapshot { magic: MAGIC, version: CURRENT_SNAPSHOT_VERSION, - database_address: self.database_address, + database_identity: self.database_identity, replica_id: self.replica_id, module_abi_version: CURRENT_MODULE_ABI_VERSION, tx_offset, @@ -677,7 +677,7 @@ impl SnapshotRepository { let tables = snapshot.reconstruct_tables(&object_repo)?; Ok(ReconstructedSnapshot { - database_address: snapshot.database_address, + database_identity: snapshot.database_identity, replica_id: snapshot.replica_id, tx_offset: snapshot.tx_offset, module_abi_version: snapshot.module_abi_version, @@ -690,13 +690,13 @@ impl SnapshotRepository { /// /// Calls [`Path::is_dir`] and requires that the result is `true`. /// See that method for more detailed preconditions on this function. - pub fn open(root: PathBuf, database_address: Address, replica_id: u64) -> Result { + pub fn open(root: PathBuf, database_identity: Identity, replica_id: u64) -> Result { if !root.is_dir() { return Err(SnapshotError::NotDirectory { root }); } Ok(Self { root, - database_address, + database_identity, replica_id, }) } @@ -776,7 +776,7 @@ impl SnapshotRepository { pub struct ReconstructedSnapshot { /// The address of the snapshotted database. - pub database_address: Address, + pub database_identity: Identity, /// The instance ID of the snapshotted database. pub replica_id: u64, /// The transaction offset of the state this snapshot reflects. diff --git a/crates/standalone/src/control_db.rs b/crates/standalone/src/control_db.rs index fc116ca1488..4a2bfa7363d 100644 --- a/crates/standalone/src/control_db.rs +++ b/crates/standalone/src/control_db.rs @@ -1,5 +1,3 @@ -use spacetimedb::address::Address; -use spacetimedb::hash::hash_bytes; use spacetimedb::identity::Identity; use spacetimedb::messages::control_db::{Database, EnergyBalance, Node, Replica}; use spacetimedb::{energy, stdb_path}; @@ -27,8 +25,8 @@ pub enum Error { Database(sled::Error), #[error("record with the name {0} already exists")] RecordAlreadyExists(DomainName), - #[error("database with address {0} already exists")] - DatabaseAlreadyExists(Address), + #[error("database with identity {0} already exists")] + DatabaseAlreadyExists(Identity), #[error("failed to register {0} domain")] DomainRegistrationFailure(DomainName), #[error("failed to decode data")] @@ -74,18 +72,18 @@ impl ControlDb { } impl ControlDb { - pub fn spacetime_dns(&self, domain: &DomainName) -> Result> { + pub fn spacetime_dns(&self, domain: &DomainName) -> Result> { let tree = self.db.open_tree("dns")?; let value = tree.get(domain.to_lowercase().as_bytes())?; if let Some(value) = value { - return Ok(Some(Address::from_slice(&value[..]))); + return Ok(Some(Identity::from_slice(&value[..]))); } Ok(None) } - pub fn spacetime_reverse_dns(&self, address: &Address) -> Result> { + pub fn spacetime_reverse_dns(&self, database_identity: &Identity) -> Result> { let tree = self.db.open_tree("reverse_dns")?; - let value = tree.get(address.as_byte_array())?; + let value = tree.get(database_identity.to_byte_array())?; if let Some(value) = value { let vec: Vec = serde_json::from_slice(&value[..])?; return Ok(vec); @@ -93,7 +91,7 @@ impl ControlDb { Ok(vec![]) } - /// Creates a new domain which points to address. For example: + /// Creates a new domain which points to the database identity. For example: /// * `my_domain/my_database` /// * `my_company/my_team/my_product` /// @@ -103,17 +101,17 @@ impl ControlDb { /// * `...` /// /// # Arguments - /// * `address` - The address the database name should point to + /// * `database_identity` - The identity the database name should point to /// * `database_name` - The database name to register /// * `owner_identity` - The identity that is publishing the database name pub fn spacetime_insert_domain( &self, - address: &Address, + database_identity: &Identity, domain: DomainName, owner_identity: Identity, try_register_tld: bool, ) -> Result { - let address = *address; + let database_identity = *database_identity; if self.spacetime_dns(&domain)?.is_some() { return Err(Error::RecordAlreadyExists(domain)); } @@ -140,23 +138,26 @@ impl ControlDb { } } - let addr_bytes = address.as_byte_array(); + let identity_bytes = database_identity.to_byte_array(); let tree = self.db.open_tree("dns")?; - tree.insert(domain.to_lowercase().as_bytes(), &addr_bytes)?; + tree.insert(domain.to_lowercase().as_bytes(), &identity_bytes)?; let tree = self.db.open_tree("reverse_dns")?; - match tree.get(addr_bytes)? { + match tree.get(identity_bytes)? { Some(value) => { let mut vec: Vec = serde_json::from_slice(&value[..])?; vec.push(domain.clone()); - tree.insert(addr_bytes, serde_json::to_string(&vec)?.as_bytes())?; + tree.insert(identity_bytes, serde_json::to_string(&vec)?.as_bytes())?; } None => { - tree.insert(addr_bytes, serde_json::to_string(&vec![&domain])?.as_bytes())?; + tree.insert(identity_bytes, serde_json::to_string(&vec![&domain])?.as_bytes())?; } } - Ok(InsertDomainResult::Success { domain, address }) + Ok(InsertDomainResult::Success { + domain, + database_identity, + }) } /// Inserts a top level domain that will be owned by `owner_identity`. @@ -196,20 +197,6 @@ impl ControlDb { } } - pub fn alloc_spacetime_address(&self) -> Result
{ - // TODO: this really doesn't need to be a single global count - // We could do something more intelligent for addresses... - // A. generating them randomly - // B. doing ipv6 generation - let id = self.db.generate_id()?; - let bytes: &[u8] = &id.to_le_bytes(); - let name = b"clockworklabs:"; - let bytes = [name, bytes].concat(); - let hash = hash_bytes(bytes); - let address = Address::from_slice(hash.abbreviate()); - Ok(address) - } - pub fn get_databases(&self) -> Result> { let tree = self.db.open_tree("database")?; let mut databases = Vec::new(); @@ -231,9 +218,9 @@ impl ControlDb { Ok(None) } - pub fn get_database_by_address(&self, address: &Address) -> Result> { - let tree = self.db.open_tree("database_by_address")?; - let key = address.to_hex(); + pub fn get_database_by_identity(&self, identity: &Identity) -> Result> { + let tree = self.db.open_tree("database_by_identity")?; + let key = identity.to_hex(); let value = tree.get(key.as_bytes())?; if let Some(value) = value { let database = compat::Database::from_slice(&value[..]).unwrap().into(); @@ -244,11 +231,11 @@ impl ControlDb { pub fn insert_database(&self, mut database: Database) -> Result { let id = self.db.generate_id()?; - let tree = self.db.open_tree("database_by_address")?; + let tree = self.db.open_tree("database_by_identity")?; - let key = database.address.to_hex(); + let key = database.database_identity.to_hex(); if tree.contains_key(key)? { - return Err(Error::DatabaseAlreadyExists(database.address)); + return Err(Error::DatabaseAlreadyExists(database.database_identity)); } database.id = id; @@ -265,13 +252,13 @@ impl ControlDb { pub fn delete_database(&self, id: u64) -> Result> { let tree = self.db.open_tree("database")?; - let tree_by_address = self.db.open_tree("database_by_address")?; + let tree_by_identity = self.db.open_tree("database_by_identity")?; if let Some(old_value) = tree.get(id.to_be_bytes())? { let database = compat::Database::from_slice(&old_value[..])?; - let key = database.address().to_hex(); + let key = database.database_identity().to_hex(); - tree_by_address.remove(key.as_bytes())?; + tree_by_identity.remove(key.as_bytes())?; tree.remove(id.to_be_bytes())?; return Ok(Some(id)); } @@ -462,7 +449,7 @@ mod compat { use spacetimedb::Identity; use spacetimedb_lib::bsatn::ser::BsatnError; use spacetimedb_lib::bsatn::{self, DecodeError}; - use spacetimedb_lib::{de::Deserialize, ser::Serialize, Address}; + use spacetimedb_lib::{de::Deserialize, ser::Serialize}; /// Serialized form of a [`spacetimedb::messages::control_db::Database`]. /// @@ -470,17 +457,15 @@ mod compat { #[derive(Serialize, Deserialize)] pub(super) struct Database { id: u64, - address: Address, + database_identity: Identity, owner_identity: Identity, host_type: HostType, initial_program: Hash, - // deprecated - publisher_address: Option
, } impl Database { - pub fn address(&self) -> Address { - self.address + pub fn database_identity(&self) -> Identity { + self.database_identity } #[inline] @@ -498,16 +483,15 @@ mod compat { fn from( Database { id, - address, + database_identity, owner_identity, host_type, initial_program, - publisher_address: _, }: Database, ) -> Self { Self { id, - address, + database_identity, owner_identity, host_type, initial_program, @@ -519,7 +503,7 @@ mod compat { fn from( CanonicalDatabase { id, - address, + database_identity, owner_identity, host_type, initial_program, @@ -527,11 +511,10 @@ mod compat { ) -> Self { Self { id, - address, + database_identity, owner_identity, host_type, initial_program, - publisher_address: None, } } } diff --git a/crates/standalone/src/control_db/tests.rs b/crates/standalone/src/control_db/tests.rs index 00bb501f93a..b73ddd628a0 100644 --- a/crates/standalone/src/control_db/tests.rs +++ b/crates/standalone/src/control_db/tests.rs @@ -48,7 +48,7 @@ fn test_domain() -> anyhow::Result<()> { let cdb = ControlDb::at(tmp.path())?; - let addr = Address::ZERO; + let addr = Identity::ZERO; let res = cdb.spacetime_insert_domain(&addr, domain.clone(), *ALICE, true)?; assert!(matches!(res, InsertDomainResult::Success { .. })); @@ -97,7 +97,7 @@ fn test_decode() -> ResultTest<()> { let db = Database { id: 0, - address: Default::default(), + database_identity: Default::default(), owner_identity: id, host_type: HostType::Wasm, initial_program: Hash::ZERO, diff --git a/crates/standalone/src/lib.rs b/crates/standalone/src/lib.rs index 26876ec5523..71351726cee 100644 --- a/crates/standalone/src/lib.rs +++ b/crates/standalone/src/lib.rs @@ -14,7 +14,6 @@ use energy_monitor::StandaloneEnergyMonitor; use openssl::ec::{EcGroup, EcKey}; use openssl::nid::Nid; use openssl::pkey::PKey; -use spacetimedb::address::Address; use spacetimedb::auth::identity::{DecodingKey, EncodingKey}; use spacetimedb::client::ClientActorIndex; use spacetimedb::db::{db_metrics::DB_METRICS, Config}; @@ -204,8 +203,8 @@ impl spacetimedb_client_api::ControlStateReadAccess for StandaloneEnv { Ok(self.control_db.get_database_by_id(id)?) } - fn get_database_by_address(&self, address: &Address) -> anyhow::Result> { - Ok(self.control_db.get_database_by_address(address)?) + fn get_database_by_identity(&self, database_identity: &Identity) -> anyhow::Result> { + Ok(self.control_db.get_database_by_identity(database_identity)?) } fn get_databases(&self) -> anyhow::Result> { @@ -231,27 +230,23 @@ impl spacetimedb_client_api::ControlStateReadAccess for StandaloneEnv { } // DNS - fn lookup_address(&self, domain: &DomainName) -> anyhow::Result> { + fn lookup_identity(&self, domain: &DomainName) -> anyhow::Result> { Ok(self.control_db.spacetime_dns(domain)?) } - fn reverse_lookup(&self, address: &Address) -> anyhow::Result> { - Ok(self.control_db.spacetime_reverse_dns(address)?) + fn reverse_lookup(&self, database_identity: &Identity) -> anyhow::Result> { + Ok(self.control_db.spacetime_reverse_dns(database_identity)?) } } #[async_trait] impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { - async fn create_address(&self) -> anyhow::Result
{ - Ok(self.control_db.alloc_spacetime_address()?) - } - async fn publish_database( &self, - identity: &Identity, + publisher: &Identity, spec: spacetimedb_client_api::DatabaseDef, ) -> anyhow::Result> { - let existing_db = self.control_db.get_database_by_address(&spec.address)?; + let existing_db = self.control_db.get_database_by_identity(&spec.database_identity)?; match existing_db { // The database does not already exist, so we'll create it. @@ -259,8 +254,8 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { let initial_program = self.program_store.put(&spec.program_bytes).await?; let mut database = Database { id: 0, - address: spec.address, - owner_identity: *identity, + database_identity: spec.database_identity, + owner_identity: *publisher, host_type: spec.host_type, initial_program, }; @@ -275,19 +270,19 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { // If that fails, we'll keep the old one. Some(database) => { ensure!( - &database.owner_identity == identity, + &database.owner_identity == publisher, "Permission denied: `{}` does not own database `{}`", - identity, - spec.address.to_abbreviated_hex() + publisher, + spec.database_identity.to_abbreviated_hex() ); let database_id = database.id; - let database_addr = database.address; + let database_identity = database.database_identity; let leader = self .control_db .get_leader_replica_by_database(database_id) - .with_context(|| format!("Not found: leader instance for database `{}`", database_addr))?; + .with_context(|| format!("Not found: leader instance for database `{}`", database_identity))?; let update_result = self .host_controller .update_module_host(database, spec.host_type, leader.id, spec.program_bytes.into()) @@ -297,7 +292,7 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { let replicas = self.control_db.get_replicas_by_database(database_id)?; let desired_replicas = spec.num_replicas as usize; if desired_replicas == 0 { - log::info!("Decommissioning all replicas of database {}", database_addr); + log::info!("Decommissioning all replicas of database {}", database_identity); for instance in replicas { self.delete_replica(instance.id).await?; } @@ -305,7 +300,7 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { let n = desired_replicas - replicas.len(); log::info!( "Scaling up database {} from {} to {} replicas", - database_addr, + database_identity, replicas.len(), n ); @@ -322,7 +317,7 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { let n = replicas.len() - desired_replicas; log::info!( "Scaling down database {} from {} to {} replicas", - database_addr, + database_identity, replicas.len(), n ); @@ -333,7 +328,7 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { log::debug!( "Desired replica count {} for database {} already satisfied", desired_replicas, - database_addr + database_identity ); } } @@ -343,17 +338,17 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { } } - async fn delete_database(&self, identity: &Identity, address: &Address) -> anyhow::Result<()> { - let Some(database) = self.control_db.get_database_by_address(address)? else { + async fn delete_database(&self, caller_identity: &Identity, database_identity: &Identity) -> anyhow::Result<()> { + let Some(database) = self.control_db.get_database_by_identity(database_identity)? else { return Ok(()); }; anyhow::ensure!( - &database.owner_identity == identity, + &database.owner_identity == caller_identity, // TODO: `PermissionDenied` should be a variant of `Error`, // so we can match on it and return better error responses // from HTTP endpoints. - "Permission denied: `{identity}` does not own database `{}`", - address.to_abbreviated_hex() + "Permission denied: `{caller_identity}` does not own database `{}`", + database_identity.to_abbreviated_hex() ); self.control_db.delete_database(database.id)?; @@ -385,13 +380,13 @@ impl spacetimedb_client_api::ControlStateWriteAccess for StandaloneEnv { async fn create_dns_record( &self, - identity: &Identity, + owner_identity: &Identity, domain: &DomainName, - address: &Address, + database_identity: &Identity, ) -> anyhow::Result { Ok(self .control_db - .spacetime_insert_domain(address, domain.clone(), *identity, true)?) + .spacetime_insert_domain(database_identity, domain.clone(), *owner_identity, true)?) } } diff --git a/crates/testing/src/modules.rs b/crates/testing/src/modules.rs index 69226880a68..c82dfd5a629 100644 --- a/crates/testing/src/modules.rs +++ b/crates/testing/src/modules.rs @@ -6,11 +6,10 @@ use std::time::Instant; use spacetimedb::messages::control_db::HostType; use spacetimedb::Identity; +use spacetimedb_client_api::routes::subscribe::generate_random_address; use spacetimedb_lib::ser::serde::SerializeWrapper; use tokio::runtime::{Builder, Runtime}; -use spacetimedb::address::Address; - use spacetimedb::client::{ClientActorId, ClientConnection, DataMessage, Protocol}; use spacetimedb::config::{FilesLocal, SpacetimeDbFiles}; use spacetimedb::database_logger::DatabaseLogger; @@ -46,7 +45,7 @@ pub struct ModuleHandle { // Needs to hold a reference to the standalone env. _env: Arc, pub client: ClientConnection, - pub db_address: Address, + pub db_identity: Identity, } impl ModuleHandle { @@ -76,7 +75,7 @@ impl ModuleHandle { } pub async fn read_log(&self, size: Option) -> String { - let filepath = DatabaseLogger::filepath(&self.db_address, self.client.replica_id); + let filepath = DatabaseLogger::filepath(&self.db_identity, self.client.replica_id); DatabaseLogger::read_latest(&filepath, size).await } } @@ -153,8 +152,8 @@ impl CompiledModule { let env = spacetimedb_standalone::StandaloneEnv::init(config).await.unwrap(); // TODO: Fix this when we update identity generation. let identity = Identity::ZERO; - let db_address = env.create_address().await.unwrap(); - let client_address = env.create_address().await.unwrap(); + let db_identity = Identity::placeholder(); + let client_address = generate_random_address(); let program_bytes = self .program_bytes @@ -164,7 +163,7 @@ impl CompiledModule { env.publish_database( &identity, DatabaseDef { - address: db_address, + database_identity: db_identity, program_bytes, num_replicas: 1, host_type: HostType::Wasm, @@ -173,7 +172,7 @@ impl CompiledModule { .await .unwrap(); - let database = env.get_database_by_address(&db_address).unwrap().unwrap(); + let database = env.get_database_by_identity(&db_identity).unwrap().unwrap(); let instance = env.get_leader_replica_by_database(database.id).unwrap(); let client_id = ClientActorId { @@ -196,7 +195,7 @@ impl CompiledModule { ModuleHandle { _env: env, client: ClientConnection::dummy(client_id, Protocol::Text, instance.id, module_rx), - db_address, + db_identity, } } } diff --git a/smoketests/__init__.py b/smoketests/__init__.py index a6c08701a0a..83a8e32b1fa 100644 --- a/smoketests/__init__.py +++ b/smoketests/__init__.py @@ -183,8 +183,8 @@ def publish_module(self, domain=None, *, clear=True, capture_stderr=True): "--project-path", self.project_path, capture_stderr=capture_stderr, ) - self.resolved_address = re.search(r"address: ([0-9a-fA-F]+)", publish_output)[1] - self.address = domain if domain is not None else self.resolved_address + self.resolved_identity = re.search(r"identity: ([0-9a-fA-F]+)", publish_output)[1] + self.address = domain if domain is not None else self.resolved_identity @classmethod def reset_config(cls): diff --git a/smoketests/tests/domains.py b/smoketests/tests/domains.py index 83a65d67773..bd72eaabbf1 100644 --- a/smoketests/tests/domains.py +++ b/smoketests/tests/domains.py @@ -30,7 +30,7 @@ def test_reverse_dns(self): self.publish_module(rand_domain) - names = self.spacetime("dns", "reverse-lookup", self.resolved_address).splitlines() + names = self.spacetime("dns", "reverse-lookup", self.resolved_identity).splitlines() self.assertIn(rand_domain, names) def test_set_name(self): diff --git a/tools/clippy.sh b/tools/clippy.sh index 16dad03ff77..e72a9d4951c 100755 --- a/tools/clippy.sh +++ b/tools/clippy.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash script_dir="$(readlink -f "$(dirname "$0")")" stdb_root="$(realpath "$script_dir/../")" diff --git a/tools/run-all-tests.sh b/tools/run-all-tests.sh index a478ae90790..a2d0dbf9958 100755 --- a/tools/run-all-tests.sh +++ b/tools/run-all-tests.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/usr/bin/env bash set -euo pipefail