diff --git a/Cargo.toml b/Cargo.toml index d3e4060..94a9703 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,20 +16,12 @@ members = ["crates/*"] [workspace.dependencies] anyhow = "1" async-trait = "0.1" -axum = { version = "0.7", features = [ - "http1", - "http2", - "json", - "macros", - "matched-path", - "tokio", - "query", -], default-features = true } bytes = "1" base64 = "0.21" ciborium = "0.2" ciborium-io = "0.2" ed25519-dalek = "2" +futures = "0.3" libflate = "1" log = "0.4" mime = "0.3" diff --git a/crates/ns-axum-web/Cargo.toml b/crates/ns-axum-web/Cargo.toml index 2833b22..8efe061 100644 --- a/crates/ns-axum-web/Cargo.toml +++ b/crates/ns-axum-web/Cargo.toml @@ -12,7 +12,6 @@ license = "CC0-1.0" [dependencies] anyhow = { workspace = true } -axum = { workspace = true } async-trait = { workspace = true } bytes = { workspace = true } base64 = { workspace = true } @@ -25,6 +24,15 @@ serde = { workspace = true } serde_json = { workspace = true } structured-logger = { workspace = true } tokio = { workspace = true } +axum = { version = "0.7", features = [ + "http1", + "http2", + "json", + "macros", + "matched-path", + "tokio", + "query", +], default-features = true } scylla = "0.11" zstd = "0.12" validator = { version = "0.16", features = ["derive"] } diff --git a/crates/ns-fetcher/Cargo.toml b/crates/ns-fetcher/Cargo.toml index c98b5bb..a9313f4 100644 --- a/crates/ns-fetcher/Cargo.toml +++ b/crates/ns-fetcher/Cargo.toml @@ -11,7 +11,7 @@ license = "CC0-1.0" [lib] [dependencies] -ns-protocol = { path = "../ns-protocol", version = "0.4" } +ns-protocol = { path = "../ns-protocol", version = "0.5" } anyhow = { workspace = true } bytes = { workspace = true } base64 = { workspace = true } diff --git a/crates/ns-fetcher/src/fetcher.rs b/crates/ns-fetcher/src/fetcher.rs index 653e5fe..1107b3a 100644 --- a/crates/ns-fetcher/src/fetcher.rs +++ b/crates/ns-fetcher/src/fetcher.rs @@ -11,7 +11,8 @@ pub type InscriptionState = ( Option<(NameState, ServiceState, Option)>, ); -// fetches all inscriptions and states from last accepted to bottom_height +// fetches all inscriptions and states from last accepted to bottom_height. +// The lowest height is 1 (the first inscription). pub fn fetch_desc( cli: Client, bottom_height: u64, @@ -29,7 +30,7 @@ pub fn fetch_desc( yield (last_accepted, Some((name_state, service_state, None))); loop { - if head_height == 0 || head_height < bottom_height { + if head_height <= 1 || head_height < bottom_height { break; } @@ -93,7 +94,7 @@ mod tests { let cli = Client::new(&ClientOptions { endpoint }).await.unwrap(); - let s = fetch_desc(cli, 0); + let s = fetch_desc(cli, 1); pin_mut!(s); // needed for iteration // first item is always the last accepted inscription diff --git a/crates/ns-indexer/Cargo.toml b/crates/ns-indexer/Cargo.toml index 8593054..2c7bc31 100644 --- a/crates/ns-indexer/Cargo.toml +++ b/crates/ns-indexer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ns-indexer" -version = "0.1.0" +version = "0.2.0" edition = "2021" rust-version = "1.64" description = "Name & Service Protocol indexer service in Rust" @@ -15,12 +15,11 @@ name = "ns-indexer" path = "src/bin/main.rs" [dependencies] -ns-protocol = { path = "../ns-protocol", version = "0.4" } +ns-protocol = { path = "../ns-protocol", version = "0.5" } ns-axum-web = { path = "../ns-axum-web", version = "0.1" } ns-scylla-orm = { path = "../ns-scylla-orm", version = "0.1" } ns-scylla-orm-macros = { path = "../ns-scylla-orm-macros", version = "0.1" } anyhow = { workspace = true } -axum = { workspace = true } bytes = { workspace = true } base64 = { workspace = true } ciborium = { workspace = true } @@ -30,7 +29,16 @@ tokio = { workspace = true } serde_json = { workspace = true } log = { workspace = true } structured-logger = { workspace = true } -futures = "0.3" +futures = { workspace = true } +axum = { version = "0.7", features = [ + "http1", + "http2", + "json", + "macros", + "matched-path", + "tokio", + "query", +], default-features = true } reqwest = { version = "0.11", features = [ "rustls-tls", "rustls-tls-webpki-roots", diff --git a/crates/ns-indexer/cql/schema.cql b/crates/ns-indexer/cql/schema.cql index b5b84d6..9e6ef1f 100644 --- a/crates/ns-indexer/cql/schema.cql +++ b/crates/ns-indexer/cql/schema.cql @@ -3,6 +3,8 @@ CREATE TABLE IF NOT EXISTS name_state ( sequence BIGINT, -- name's latest sequence block_height BIGINT, -- latest update at block height block_time BIGINT, -- latest update at block time + stale_time BIGINT, -- stale time + expire_time BIGINT, -- expire time threshold TINYINT, -- verifing threshold key_kind TINYINT, -- int8, 0: Ed25519 public_keys LIST, -- public keys @@ -70,6 +72,7 @@ CREATE TABLE IF NOT EXISTS inscription ( name TEXT, -- unique name sequence BIGINT, -- name's updating sequence height BIGINT, -- inscription's global height + name_height BIGINT, -- global name's counter previous_hash BLOB, -- previous inscription hash name_hash BLOB, -- current name state hash service_hash BLOB, -- current service state hash diff --git a/crates/ns-indexer/src/db/model_inscription.rs b/crates/ns-indexer/src/db/model_inscription.rs index 9517854..713bcf1 100644 --- a/crates/ns-indexer/src/db/model_inscription.rs +++ b/crates/ns-indexer/src/db/model_inscription.rs @@ -23,6 +23,7 @@ pub struct Inscription { pub name: String, pub sequence: i64, pub height: i64, + pub name_height: i64, pub previous_hash: Vec, pub name_hash: Vec, pub service_hash: Vec, @@ -148,6 +149,7 @@ impl Inscription { name: value.name.clone(), sequence: value.sequence as i64, height: value.height as i64, + name_height: value.name_height as i64, previous_hash: value.previous_hash.clone(), name_hash: value.name_hash.clone(), service_hash: value.service_hash.clone(), @@ -167,6 +169,7 @@ impl Inscription { name: self.name.clone(), sequence: self.sequence as u64, height: self.height as u64, + name_height: self.name_height as u64, previous_hash: self.previous_hash.clone(), name_hash: self.name_hash.clone(), service_hash: self.service_hash.clone(), diff --git a/crates/ns-indexer/src/db/model_name_state.rs b/crates/ns-indexer/src/db/model_name_state.rs index a17b94a..cc22a97 100644 --- a/crates/ns-indexer/src/db/model_name_state.rs +++ b/crates/ns-indexer/src/db/model_name_state.rs @@ -13,6 +13,8 @@ pub struct NameState { pub sequence: i64, pub block_height: i64, pub block_time: i64, + pub stale_time: i64, + pub expire_time: i64, pub threshold: i8, pub key_kind: i8, pub public_keys: Vec>, @@ -51,6 +53,8 @@ impl NameState { sequence: value.sequence as i64, block_height: value.block_height as i64, block_time: value.block_time as i64, + stale_time: value.stale_time as i64, + expire_time: value.expire_time as i64, threshold: value.threshold as i8, key_kind: value.key_kind as i8, public_keys: value.public_keys.clone(), @@ -65,6 +69,8 @@ impl NameState { sequence: self.sequence as u64, block_height: self.block_height as u64, block_time: self.block_time as u64, + stale_time: self.stale_time as u64, + expire_time: self.expire_time as u64, threshold: self.threshold as u8, key_kind: self.key_kind as u8, public_keys: self.public_keys.clone(), diff --git a/crates/ns-indexer/src/indexer.rs b/crates/ns-indexer/src/indexer.rs index 722160d..6b39a45 100644 --- a/crates/ns-indexer/src/indexer.rs +++ b/crates/ns-indexer/src/indexer.rs @@ -7,7 +7,10 @@ use tokio::sync::RwLock; use ns_protocol::{ ns::{Name, PublicKeyParams, ThresholdLevel}, - state::{hash_sha3, Inscription, InvalidInscription, NameState, ServiceProtocol, ServiceState}, + state::{ + hash_sha3, Inscription, InvalidInscription, NameState, ServiceProtocol, ServiceState, + NAME_EXPIRE_SECONDS, NAME_STALE_SECONDS, + }, }; use crate::db::{ @@ -113,6 +116,7 @@ impl Indexer { for envelope in Envelope::from_transaction(&tx) { for name in envelope.payload { + let is_new_name = name.sequence == 0; match self.index_name(block_height, block_time, &name).await { Err(err) => { if !name.name.is_empty() { @@ -141,6 +145,7 @@ impl Indexer { name: name.name.clone(), sequence: name.sequence, height: 0, + name_height: 0, previous_hash: vec![], name_hash: name_state_hash, service_hash: service_state_hash, @@ -158,6 +163,11 @@ impl Indexer { match best_inscriptions_state.back() { Some(prev_best_inscription) => { inscription.height = prev_best_inscription.height + 1; + inscription.name_height = if is_new_name { + prev_best_inscription.name_height + 1 + } else { + prev_best_inscription.name_height + }; inscription.previous_hash = prev_best_inscription .hash() .expect("hash_sha3(inscription) should not fail"); @@ -165,12 +175,19 @@ impl Indexer { None => match *self.state.last_accepted.read().await { Some(ref last_accepted_state) => { inscription.height = last_accepted_state.height + 1; + inscription.name_height = if is_new_name { + last_accepted_state.name_height + 1 + } else { + last_accepted_state.name_height + }; inscription.previous_hash = last_accepted_state .hash() .expect("hash_sha3(inscription) should not fail"); } None => { // this is the first inscription + inscription.height = 1; + inscription.name_height = 1; inscription.previous_hash = [0u8; 32].to_vec(); } }, @@ -340,6 +357,8 @@ impl Indexer { sequence: 0, block_height, block_time, + stale_time: block_time + NAME_STALE_SECONDS, + expire_time: block_time + NAME_EXPIRE_SECONDS, threshold: public_key_params .threshold .unwrap_or(public_key_params.public_keys.len() as u8), diff --git a/crates/ns-inscriber/Cargo.toml b/crates/ns-inscriber/Cargo.toml index eb21c76..0a92e1e 100644 --- a/crates/ns-inscriber/Cargo.toml +++ b/crates/ns-inscriber/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ns-inscriber" -version = "0.1.0" +version = "0.2.0" edition = "2021" rust-version = "1.64" description = "Name & Service Protocol inscriber service in Rust" @@ -15,8 +15,8 @@ name = "ns-inscriber" path = "src/bin/main.rs" [dependencies] -ns-protocol = { path = "../ns-protocol", version = "0.4" } -ns-indexer = { path = "../ns-indexer", version = "0.1" } +ns-protocol = { path = "../ns-protocol", version = "0.5" } +ns-indexer = { path = "../ns-indexer", version = "0.2" } anyhow = { workspace = true } bytes = { workspace = true } base64 = { workspace = true } @@ -28,7 +28,7 @@ serde_json = { workspace = true } log = { workspace = true } structured-logger = { workspace = true } ed25519-dalek = { workspace = true } -futures = "0.3" +futures = { workspace = true } reqwest = { version = "0.11", features = [ "rustls-tls", "rustls-tls-webpki-roots", diff --git a/crates/ns-protocol/Cargo.toml b/crates/ns-protocol/Cargo.toml index ca78524..00a6541 100644 --- a/crates/ns-protocol/Cargo.toml +++ b/crates/ns-protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ns-protocol" -version = "0.4.0" +version = "0.5.0" edition = "2021" rust-version = "1.64" description = "Name & Service Protocol in Rust" diff --git a/crates/ns-protocol/src/state.rs b/crates/ns-protocol/src/state.rs index 35a8fde..9cdb17e 100644 --- a/crates/ns-protocol/src/state.rs +++ b/crates/ns-protocol/src/state.rs @@ -16,6 +16,8 @@ pub struct NameState { pub sequence: u64, pub block_height: u64, pub block_time: u64, + pub stale_time: u64, + pub expire_time: u64, pub threshold: u8, pub key_kind: u8, pub public_keys: Vec>, @@ -36,10 +38,6 @@ impl NameState { hash_sha3(self) } - pub fn is_stale(&self, block_time: u64) -> bool { - self.block_time + NAME_STALE_SECONDS < block_time - } - pub fn verify_the_next( &self, block_height: u64, @@ -65,6 +63,8 @@ impl NameState { sequence: next.sequence, block_height, block_time, + stale_time: block_time + NAME_STALE_SECONDS, + expire_time: block_time + NAME_EXPIRE_SECONDS, threshold: self.threshold, key_kind: self.key_kind, public_keys: self.public_keys.clone(), @@ -80,6 +80,8 @@ impl NameState { next_state.sequence = next.sequence; next_state.block_height = block_height; next_state.block_time = block_time; + next_state.stale_time = block_time + NAME_STALE_SECONDS; + next_state.expire_time = block_time + NAME_EXPIRE_SECONDS; next_state.next_public_keys = None; return Ok(next_state); } @@ -96,6 +98,8 @@ impl NameState { sequence: next.sequence, block_height, block_time, + stale_time: block_time + NAME_STALE_SECONDS, + expire_time: block_time + NAME_EXPIRE_SECONDS, threshold: next_state.threshold, key_kind: next_state.key_kind, public_keys: next_state.public_keys.clone(), @@ -104,7 +108,7 @@ impl NameState { } 1 => { // update public_keys - let allow_update = (next_state.block_time + NAME_EXPIRE_SECONDS < block_time) + let allow_update = (next_state.expire_time < block_time) || (next_state.next_public_keys.is_some() && next_state.next_public_keys.as_ref().unwrap() == &public_key_params.public_keys); @@ -120,6 +124,8 @@ impl NameState { sequence: next.sequence, block_height, block_time, + stale_time: block_time + NAME_STALE_SECONDS, + expire_time: block_time + NAME_EXPIRE_SECONDS, threshold: public_key_params .threshold .unwrap_or(public_key_params.public_keys.len() as u8), @@ -243,6 +249,7 @@ pub struct Inscription { pub name: String, pub sequence: u64, pub height: u64, + pub name_height: u64, pub previous_hash: Vec, pub name_hash: Vec, pub service_hash: Vec, @@ -322,9 +329,8 @@ mod tests { block_height: 1, block_time: 1, threshold: 1, - key_kind: 0, public_keys: vec![s1.verifying_key().to_bytes().to_vec()], - next_public_keys: None, + ..Default::default() }; let mut next_name = ns::Name { @@ -394,6 +400,8 @@ mod tests { assert_eq!(1, name_state.sequence); assert_eq!(3, name_state.block_height); assert_eq!(3, name_state.block_time); + assert_eq!(3 + NAME_STALE_SECONDS, name_state.stale_time); + assert_eq!(3 + NAME_EXPIRE_SECONDS, name_state.expire_time); assert_eq!(1, name_state.threshold); assert_eq!(0, name_state.key_kind); assert_eq!( @@ -462,6 +470,8 @@ mod tests { assert_eq!(2, name_state.sequence); assert_eq!(5, name_state.block_height); assert_eq!(5, name_state.block_time); + assert_eq!(5 + NAME_STALE_SECONDS, name_state.stale_time); + assert_eq!(5 + NAME_EXPIRE_SECONDS, name_state.expire_time); assert_eq!(2, name_state.threshold); assert_eq!(0, name_state.key_kind); assert_eq!( @@ -521,6 +531,8 @@ mod tests { assert_eq!(3, name_state.sequence); assert_eq!(7, name_state.block_height); assert_eq!(7, name_state.block_time); + assert_eq!(7 + NAME_STALE_SECONDS, name_state.stale_time); + assert_eq!(7 + NAME_EXPIRE_SECONDS, name_state.expire_time); assert_eq!(1, name_state.threshold); assert_eq!(0, name_state.key_kind); assert_eq!( @@ -555,12 +567,15 @@ mod tests { next_name.sign_with(&s2).unwrap(); next_name.validate().unwrap(); + let block_time = name_state.expire_time + 1; let name_state = name_state - .verify_the_next(8, 8 + NAME_EXPIRE_SECONDS, &next_name) + .verify_the_next(8, block_time, &next_name) .unwrap(); assert_eq!(4, name_state.sequence); assert_eq!(8, name_state.block_height); - assert_eq!(8 + NAME_EXPIRE_SECONDS, name_state.block_time); + assert_eq!(block_time, name_state.block_time); + assert_eq!(block_time + NAME_STALE_SECONDS, name_state.stale_time); + assert_eq!(block_time + NAME_EXPIRE_SECONDS, name_state.expire_time); assert_eq!(1, name_state.threshold); assert_eq!(0, name_state.key_kind); assert_eq!(