From 81d50d2f1619b937b6a0d355f07b1a303be3cc36 Mon Sep 17 00:00:00 2001 From: 0xZensh Date: Wed, 3 Jan 2024 12:27:38 +0800 Subject: [PATCH] feat: add more best states API for NS-Indexer --- crates/ns-indexer/Cargo.toml | 2 +- .../doc/ns-indexer-api-insomnia-v1.yaml | 198 ++++++++++++++---- crates/ns-indexer/src/api/inscription.rs | 95 ++++++++- crates/ns-indexer/src/api/mod.rs | 4 +- crates/ns-indexer/src/api/name.rs | 27 +++ crates/ns-indexer/src/api/service.rs | 34 +++ crates/ns-indexer/src/router.rs | 10 + crates/ns-inscriber/Cargo.toml | 2 +- 8 files changed, 322 insertions(+), 50 deletions(-) diff --git a/crates/ns-indexer/Cargo.toml b/crates/ns-indexer/Cargo.toml index 75d8356..6b659ce 100644 --- a/crates/ns-indexer/Cargo.toml +++ b/crates/ns-indexer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ns-indexer" -version = "0.3.1" +version = "0.4.0" edition = "2021" rust-version = "1.64" description = "Name & Service Protocol indexer service in Rust" diff --git a/crates/ns-indexer/doc/ns-indexer-api-insomnia-v1.yaml b/crates/ns-indexer/doc/ns-indexer-api-insomnia-v1.yaml index c7b3c14..b7612a8 100644 --- a/crates/ns-indexer/doc/ns-indexer-api-insomnia-v1.yaml +++ b/crates/ns-indexer/doc/ns-indexer-api-insomnia-v1.yaml @@ -1,6 +1,6 @@ _type: export __export_format: 4 -__export_date: 2024-01-01T08:44:27.315Z +__export_date: 2024-01-03T04:26:57.860Z __export_source: insomnia.desktop.app:v8.5.1 resources: - _id: req_b1a618c6a5b7424f82a71b4d04a382db @@ -30,9 +30,9 @@ resources: _type: request - _id: wrk_6740d036556540e7972210367d3cbc69 parentId: null - modified: 1702698428340 + modified: 1704255390271 created: 1702698428340 - name: Indexer API + name: NS-Indexer API description: "" scope: collection _type: workspace @@ -63,10 +63,10 @@ resources: _type: request - _id: req_c4a8deb0e59343708f6540522b0772f6 parentId: wrk_6740d036556540e7972210367d3cbc69 - modified: 1702901552482 + modified: 1704255617700 created: 1702871420490 - url: "{{ _.api_host }}/best/inscription" - name: best_inscription/get + url: "{{ _.api_host }}/best/inscription/get_last" + name: best_inscription/get_last description: "" method: GET body: {} @@ -86,6 +86,68 @@ resources: settingRebuildPath: true settingFollowRedirects: global _type: request + - _id: req_363bdc0bd5fc427a92083954b4b75286 + parentId: wrk_6740d036556540e7972210367d3cbc69 + modified: 1704255791206 + created: 1704255639766 + url: "{{ _.api_host }}/best/inscription" + name: best_inscription/get + description: "" + method: GET + body: {} + parameters: + - id: pair_6c07f58e12cd4368bf9ac052134f175e + name: name + value: 𝚡 + description: "" + - id: pair_c69e64df93ba4498b209cf77e39cd013 + name: sequence + value: "0" + description: "" + headers: + - name: User-Agent + value: insomnia/8.4.5 + - name: Accept + value: application/json + authentication: {} + metaSortKey: -1701649934666.5781 + isPrivate: false + settingStoreCookies: true + settingSendCookies: true + settingDisableRenderRequestBody: false + settingEncodeUrl: true + settingRebuildPath: true + settingFollowRedirects: global + _type: request + - _id: req_e7b6cf526bb648cfab8f05c5d49bfeb8 + parentId: wrk_6740d036556540e7972210367d3cbc69 + modified: 1704255740036 + created: 1704255718590 + url: "{{ _.api_host }}/best/inscription/get_by_height" + name: best_inscription/get_by_height + description: "" + method: GET + body: {} + parameters: + - id: pair_6c07f58e12cd4368bf9ac052134f175e + name: height + value: "1997" + description: "" + headers: + - name: User-Agent + value: insomnia/8.4.5 + - name: Accept + value: application/json + authentication: {} + metaSortKey: -1701609605064.5234 + isPrivate: false + settingStoreCookies: true + settingSendCookies: true + settingDisableRenderRequestBody: false + settingEncodeUrl: true + settingRebuildPath: true + settingFollowRedirects: global + _type: request - _id: req_56d3cfbe6d7943098e89d202c0edd557 parentId: wrk_6740d036556540e7972210367d3cbc69 modified: 1702901563095 @@ -111,6 +173,97 @@ resources: settingRebuildPath: true settingFollowRedirects: global _type: request + - _id: req_93b0153eebef4b37b1e55d3d92a65356 + parentId: wrk_6740d036556540e7972210367d3cbc69 + modified: 1704255803344 + created: 1704255784178 + url: "{{ _.api_host }}/best/name" + name: best_name/get + description: "" + method: GET + body: {} + parameters: + - id: pair_6c07f58e12cd4368bf9ac052134f175e + name: name + value: 𝚡 + description: "" + headers: + - name: User-Agent + value: insomnia/8.4.5 + - name: Accept + value: application/json + authentication: {} + metaSortKey: -1701488616258.3594 + isPrivate: false + settingStoreCookies: true + settingSendCookies: true + settingDisableRenderRequestBody: false + settingEncodeUrl: true + settingRebuildPath: true + settingFollowRedirects: global + _type: request + - _id: req_1a7760f6eb3c4a36ad1c194412e42775 + parentId: wrk_6740d036556540e7972210367d3cbc69 + modified: 1704255862830 + created: 1704255851797 + url: "{{ _.api_host }}/best/service" + name: best_service/get + description: "" + method: GET + body: {} + parameters: + - id: pair_6c07f58e12cd4368bf9ac052134f175e + name: name + value: 𝚡 + description: "" + - id: pair_4706db882d41468397746d4ede78a07b + name: code + value: "0" + description: "" + headers: + - name: User-Agent + value: insomnia/8.4.5 + - name: Accept + value: application/json + authentication: {} + metaSortKey: -1701448286656.3047 + isPrivate: false + settingStoreCookies: true + settingSendCookies: true + settingDisableRenderRequestBody: false + settingEncodeUrl: true + settingRebuildPath: true + settingFollowRedirects: global + _type: request + - _id: req_1b571fe4b2c349eeb27f02211c72cfc3 + parentId: wrk_6740d036556540e7972210367d3cbc69 + modified: 1704255928462 + created: 1703043892732 + url: "{{ _.api_host }}/best/utxo/list" + name: best_utxo/list_by_address + description: "" + method: GET + body: {} + parameters: + - id: pair_8352d8e48b92475fb3c8f48593275b6e + name: address + value: bc1q6dukpvmcxae0pdh95zgh793l5ept8fluhqqnyc + description: "" + headers: + - name: User-Agent + value: insomnia/8.4.5 + - name: Accept + value: application/json + authentication: {} + metaSortKey: -1701428121855.2773 + isPrivate: false + settingStoreCookies: true + settingSendCookies: true + settingDisableRenderRequestBody: false + settingEncodeUrl: true + settingRebuildPath: true + settingFollowRedirects: global + _type: request - _id: req_f7592cdb232f44468a3031caedeaf11f parentId: wrk_6740d036556540e7972210367d3cbc69 modified: 1702866762535 @@ -287,7 +440,7 @@ resources: _type: request - _id: req_c1217c2f6388434a88c0d2f0cf2d7d49 parentId: wrk_6740d036556540e7972210367d3cbc69 - modified: 1703302708284 + modified: 1704255831362 created: 1702889481164 url: "{{ _.api_host }}/v1/name" name: name/get @@ -297,7 +450,7 @@ resources: parameters: - id: pair_8352d8e48b92475fb3c8f48593275b6e name: name - value: ab + value: 𝚡 description: "" headers: - name: User-Agent @@ -434,35 +587,6 @@ resources: settingRebuildPath: true settingFollowRedirects: global _type: request - - _id: req_1b571fe4b2c349eeb27f02211c72cfc3 - parentId: wrk_6740d036556540e7972210367d3cbc69 - modified: 1703481569875 - created: 1703043892732 - url: "{{ _.api_host }}/best/utxo/list" - name: utxo/list_by_address - description: "" - method: GET - body: {} - parameters: - - id: pair_8352d8e48b92475fb3c8f48593275b6e - name: address - value: bc1q6dukpvmcxae0pdh95zgh793l5ept8fluhqqnyc - description: "" - headers: - - name: User-Agent - value: insomnia/8.4.5 - - name: Accept - value: application/json - authentication: {} - metaSortKey: -1700119930355.816 - isPrivate: false - settingStoreCookies: true - settingSendCookies: true - settingDisableRenderRequestBody: false - settingEncodeUrl: true - settingRebuildPath: true - settingFollowRedirects: global - _type: request - _id: env_521c603fd2195d973072305ede8865ff09f1a5e4 parentId: wrk_6740d036556540e7972210367d3cbc69 modified: 1702698491521 diff --git a/crates/ns-indexer/src/api/inscription.rs b/crates/ns-indexer/src/api/inscription.rs index dd7ae5b..d398aaf 100644 --- a/crates/ns-indexer/src/api/inscription.rs +++ b/crates/ns-indexer/src/api/inscription.rs @@ -21,12 +21,12 @@ impl InscriptionAPI { pub async fn get_last_accepted( Extension(ctx): Extension>, to: PackObject<()>, - State(api): State>, + State(app): State>, ) -> Result>, HTTPError> { ctx.set("action", "get_last_accepted_inscription".into()) .await; - let last_accepted_state = api.state.last_accepted.read().await; + let last_accepted_state = app.state.last_accepted.read().await; match last_accepted_state.clone() { Some(inscription) => Ok(to.with(SuccessResponse::new(inscription))), @@ -34,17 +34,17 @@ impl InscriptionAPI { } } - pub async fn get_best( + pub async fn get_last_best( Extension(ctx): Extension>, to: PackObject<()>, - State(api): State>, + State(app): State>, ) -> Result>, HTTPError> { - ctx.set("action", "get_best_inscription".into()).await; + ctx.set("action", "get_last_best_inscription".into()).await; - let best_inscriptions_state = api.state.best_inscriptions.read().await; + let best_inscriptions_state = app.state.best_inscriptions.read().await; let mut inscription = best_inscriptions_state.back().cloned(); if inscription.is_none() { - let last_accepted_state = api.state.last_accepted.read().await; + let last_accepted_state = app.state.last_accepted.read().await; inscription = last_accepted_state.clone(); } @@ -54,6 +54,83 @@ impl InscriptionAPI { } } + pub async fn get_best( + State(app): State>, + Extension(ctx): Extension>, + to: PackObject<()>, + input: Query, + ) -> Result>, HTTPError> { + input.validate()?; + if input.sequence.is_none() { + return Err(HTTPError::new(400, "sequence is required".to_string())); + } + + let name = input.name.clone(); + let sequence = input.sequence.unwrap() as u64; + ctx.set_kvs(vec![ + ("action", "get_best_inscription".into()), + ("name", name.clone().into()), + ("sequence", sequence.into()), + ]) + .await; + + { + let best_inscriptions_state = app.state.best_inscriptions.read().await; + for i in best_inscriptions_state.iter() { + if i.name == name && i.sequence == sequence { + return Ok(to.with(SuccessResponse::new(i.clone()))); + } + } + } + + { + let last_accepted_state = app.state.last_accepted.read().await; + if let Some(ins) = last_accepted_state.as_ref() { + if ins.name == name && ins.sequence == sequence { + return Ok(to.with(SuccessResponse::new(ins.clone()))); + } + } + } + + Err(HTTPError::new(404, "not found".to_string())) + } + + pub async fn get_best_by_height( + State(app): State>, + Extension(ctx): Extension>, + to: PackObject<()>, + input: Query, + ) -> Result>, HTTPError> { + input.validate()?; + + let height = input.height; + ctx.set_kvs(vec![ + ("action", "get_best_inscription_by_height".into()), + ("height", height.into()), + ]) + .await; + + { + let best_inscriptions_state = app.state.best_inscriptions.read().await; + for i in best_inscriptions_state.iter() { + if i.height == height as u64 { + return Ok(to.with(SuccessResponse::new(i.clone()))); + } + } + } + + { + let last_accepted_state = app.state.last_accepted.read().await; + if let Some(ins) = last_accepted_state.as_ref() { + if ins.height == height as u64 { + return Ok(to.with(SuccessResponse::new(ins.clone()))); + } + } + } + + Err(HTTPError::new(404, "not found".to_string())) + } + pub async fn get( State(app): State>, Extension(ctx): Extension>, @@ -103,10 +180,10 @@ impl InscriptionAPI { pub async fn list_best( Extension(ctx): Extension>, to: PackObject<()>, - State(api): State>, + State(app): State>, ) -> Result>>, HTTPError> { ctx.set("action", "list_best_inscriptions".into()).await; - let best_inscriptions_state = api.state.best_inscriptions.read().await; + let best_inscriptions_state = app.state.best_inscriptions.read().await; Ok(to.with(SuccessResponse::new( best_inscriptions_state.iter().cloned().collect(), ))) diff --git a/crates/ns-indexer/src/api/mod.rs b/crates/ns-indexer/src/api/mod.rs index 7ea299b..6f74601 100644 --- a/crates/ns-indexer/src/api/mod.rs +++ b/crates/ns-indexer/src/api/mod.rs @@ -58,9 +58,9 @@ pub async fn version( pub async fn healthz( to: PackObject<()>, - State(api): State>, + State(app): State>, ) -> Result>, HTTPError> { - let last_accepted_state = api.state.last_accepted.read().await; + let last_accepted_state = app.state.last_accepted.read().await; let (block_height, height) = match *last_accepted_state { Some(ref last_accepted) => (last_accepted.block_height, last_accepted.height), None => (0, 0), diff --git a/crates/ns-indexer/src/api/name.rs b/crates/ns-indexer/src/api/name.rs index 4f8498e..20f3292 100644 --- a/crates/ns-indexer/src/api/name.rs +++ b/crates/ns-indexer/src/api/name.rs @@ -39,6 +39,33 @@ impl NameAPI { Ok(to.with(SuccessResponse::new(name_state.to_index()?))) } + pub async fn get_best( + State(app): State>, + Extension(ctx): Extension>, + to: PackObject<()>, + input: Query, + ) -> Result>, HTTPError> { + input.validate()?; + + let name = input.name.clone(); + ctx.set_kvs(vec![ + ("action", "get_best_name_state".into()), + ("name", name.clone().into()), + ]) + .await; + + { + let best_names_state = app.state.confirming_names.read().await; + if let Some(states) = best_names_state.get(&name) { + if let Some(state) = states.back() { + return Ok(to.with(SuccessResponse::new(state.0.clone()))); + } + } + } + + Err(HTTPError::new(404, "not found".to_string())) + } + pub async fn list_by_query( State(app): State>, Extension(ctx): Extension>, diff --git a/crates/ns-indexer/src/api/service.rs b/crates/ns-indexer/src/api/service.rs index f8a0533..6a32bca 100644 --- a/crates/ns-indexer/src/api/service.rs +++ b/crates/ns-indexer/src/api/service.rs @@ -44,6 +44,40 @@ impl ServiceAPI { Ok(to.with(SuccessResponse::new(service_state.to_index()?))) } + pub async fn get_best( + State(app): State>, + Extension(ctx): Extension>, + to: PackObject<()>, + input: Query, + ) -> Result>, HTTPError> { + input.validate()?; + if input.code.is_none() { + return Err(HTTPError::new(400, "service code is required".to_string())); + } + + let name = input.name.clone(); + let code = input.code.unwrap() as u64; + ctx.set_kvs(vec![ + ("action", "get_best_service_state".into()), + ("name", name.clone().into()), + ("code", code.into()), + ]) + .await; + + { + let best_names_state = app.state.confirming_names.read().await; + if let Some(states) = best_names_state.get(&name) { + for ss in states.iter().rev() { + if ss.1.code == code { + return Ok(to.with(SuccessResponse::new(ss.1.clone()))); + } + } + } + } + + Err(HTTPError::new(404, "not found".to_string())) + } + pub async fn list_by_name( State(app): State>, Extension(ctx): Extension>, diff --git a/crates/ns-indexer/src/router.rs b/crates/ns-indexer/src/router.rs index 57aa3a5..627db75 100644 --- a/crates/ns-indexer/src/router.rs +++ b/crates/ns-indexer/src/router.rs @@ -21,10 +21,20 @@ pub fn new(state: Arc) -> Router { "/best", Router::new() .route("/inscription", routing::get(api::InscriptionAPI::get_best)) + .route( + "/inscription/get_last", + routing::get(api::InscriptionAPI::get_last_best), + ) + .route( + "/inscription/get_by_height", + routing::get(api::InscriptionAPI::get_best_by_height), + ) .route( "/inscription/list", routing::get(api::InscriptionAPI::list_best), ) + .route("/name", routing::get(api::NameAPI::get_best)) + .route("/service", routing::get(api::ServiceAPI::get_best)) .route("/utxo/list", routing::get(api::UtxoAPI::list)), ) .nest( diff --git a/crates/ns-inscriber/Cargo.toml b/crates/ns-inscriber/Cargo.toml index 1a66b70..6860fa6 100644 --- a/crates/ns-inscriber/Cargo.toml +++ b/crates/ns-inscriber/Cargo.toml @@ -16,7 +16,7 @@ path = "src/bin/main.rs" [dependencies] ns-protocol = { path = "../ns-protocol", version = "0.6" } -ns-indexer = { path = "../ns-indexer", version = "0.3" } +ns-indexer = { path = "../ns-indexer", version = "0.4" } anyhow = { workspace = true } bytes = { workspace = true } base64 = { workspace = true }