From 01265e3508c60236bb9812aaa7671b283295d2e8 Mon Sep 17 00:00:00 2001 From: Hein Dauven Date: Sat, 25 Jan 2025 14:06:06 +0100 Subject: [PATCH] explorer: Replace legacy event system with RUES --- explorer/CHANGELOG.md | 9 +++-- .../lib/services/__tests__/duskAPI.spec.js | 34 +++++++++---------- explorer/src/lib/services/duskAPI.js | 7 ++-- rusk/CHANGELOG.md | 5 +++ rusk/src/lib/http/rusk.rs | 25 +++++++++++++- 5 files changed, 55 insertions(+), 25 deletions(-) diff --git a/explorer/CHANGELOG.md b/explorer/CHANGELOG.md index ca7bc103e8..3bb71a775b 100644 --- a/explorer/CHANGELOG.md +++ b/explorer/CHANGELOG.md @@ -3,7 +3,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +and this project adheres to +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased @@ -11,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Replace legacy event system with RUES [#3425] + ### Removed - Remove version number from app title [#3338] @@ -59,7 +62,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Change `raw` payload to `json` in transaction details [#2364] - Change average gas price display value to “lux” [#2416] -- Update blocks table headers – `FEE` to `GAS`, `AVG` to `AVG PRICE`, and `TOTAL` to `USED` [#2416] +- Update blocks table headers – `FEE` to `GAS`, `AVG` to `AVG PRICE`, and + `TOTAL` to `USED` [#2416] - Update block rewards tooltip information [#2166] - Hide "Show More" button when error occurs [#2585] - Update footer layout [#2640] @@ -143,6 +147,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#3305]: https://github.com/dusk-network/rusk/issues/3305 [#3338]: https://github.com/dusk-network/rusk/issues/3338 [#3377]: https://github.com/dusk-network/rusk/issues/3377 +[#3425]: https://github.com/dusk-network/rusk/issues/3425 diff --git a/explorer/src/lib/services/__tests__/duskAPI.spec.js b/explorer/src/lib/services/__tests__/duskAPI.spec.js index 5016dd3a9d..1ff5ba173e 100644 --- a/explorer/src/lib/services/__tests__/duskAPI.spec.js +++ b/explorer/src/lib/services/__tests__/duskAPI.spec.js @@ -27,7 +27,7 @@ describe("duskAPI", () => { }; /** @type {URL} */ - const gqlExpectedURL = new URL("/02/Chain", node); + const gqlExpectedURL = new URL("/on/graphql/query", node); const endpointEnvName = "VITE_API_ENDPOINT"; /** @type {(data: Record | number) => Response} */ @@ -72,7 +72,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n \\n\\nfragment TransactionInfo on SpentTransaction {\\n\\tblockHash,\\n\\tblockHeight,\\n\\tblockTimestamp,\\n err,\\n\\tgasSpent,\\n\\tid,\\n tx {\\n callData {\\n contractId,\\n data,\\n fnName\\n },\\n gasLimit,\\n gasPrice,\\n id,\\n isDeploy,\\n memo,\\n txType\\n }\\n}\\n\\nfragment BlockInfo on Block {\\n header {\\n hash,\\n gasLimit,\\n height,\\n prevBlockHash,\\n seed,\\n stateHash,\\n timestamp,\\n version\\n },\\n fees,\\n gasSpent,\\n reward,\\n transactions {...TransactionInfo}\\n}\\n\\n query($id: String!) { block(hash: $id) {...BlockInfo} }\\n ","topic":"gql"}", + "body": "fragment TransactionInfo on SpentTransaction { blockHash, blockHeight, blockTimestamp, err, gasSpent, id, tx { callData { contractId, data, fnName }, gasLimit, gasPrice, id, isDeploy, memo, txType } } fragment BlockInfo on Block { header { hash, gasLimit, height, prevBlockHash, seed, stateHash, timestamp, version }, fees, gasSpent, reward, transactions {...TransactionInfo} } query($id: String!) { block(hash: $id) {...BlockInfo} }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -86,7 +86,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[1][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[1][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n query($height: Float!) { block(height: $height) { header { hash } } }\\n ","topic":"gql"}", + "body": "query($height: Float!) { block(height: $height) { header { hash } } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -130,7 +130,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n query($height: Float!) { block(height: $height) { header { hash } } }\\n ","topic":"gql"}", + "body": "query($height: Float!) { block(height: $height) { header { hash } } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -144,7 +144,7 @@ describe("duskAPI", () => { // expect(fetchSpy.mock.calls[1][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[1][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n query($height: Float!) { block(height: $height) { header { hash } } }\\n ","topic":"gql"}", + "body": "query($height: Float!) { block(height: $height) { header { hash } } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -166,7 +166,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"query($id: String!) { block(hash: $id) { header { json } } }","topic":"gql"}", + "body": "query($id: String!) { block(hash: $id) { header { json } } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -189,7 +189,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n \\n\\nfragment TransactionInfo on SpentTransaction {\\n\\tblockHash,\\n\\tblockHeight,\\n\\tblockTimestamp,\\n err,\\n\\tgasSpent,\\n\\tid,\\n tx {\\n callData {\\n contractId,\\n data,\\n fnName\\n },\\n gasLimit,\\n gasPrice,\\n id,\\n isDeploy,\\n memo,\\n txType\\n }\\n}\\n\\nfragment BlockInfo on Block {\\n header {\\n hash,\\n gasLimit,\\n height,\\n prevBlockHash,\\n seed,\\n stateHash,\\n timestamp,\\n version\\n },\\n fees,\\n gasSpent,\\n reward,\\n transactions {...TransactionInfo}\\n}\\n\\n query($amount: Int!) { blocks(last: $amount) {...BlockInfo} }\\n ","topic":"gql"}", + "body": "fragment TransactionInfo on SpentTransaction { blockHash, blockHeight, blockTimestamp, err, gasSpent, id, tx { callData { contractId, data, fnName }, gasLimit, gasPrice, id, isDeploy, memo, txType } } fragment BlockInfo on Block { header { hash, gasLimit, height, prevBlockHash, seed, stateHash, timestamp, version }, fees, gasSpent, reward, transactions {...TransactionInfo} } query($amount: Int!) { blocks(last: $amount) {...BlockInfo} }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -214,7 +214,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n \\n\\nfragment TransactionInfo on SpentTransaction {\\n\\tblockHash,\\n\\tblockHeight,\\n\\tblockTimestamp,\\n err,\\n\\tgasSpent,\\n\\tid,\\n tx {\\n callData {\\n contractId,\\n data,\\n fnName\\n },\\n gasLimit,\\n gasPrice,\\n id,\\n isDeploy,\\n memo,\\n txType\\n }\\n}\\n\\nfragment BlockInfo on Block {\\n header {\\n hash,\\n gasLimit,\\n height,\\n prevBlockHash,\\n seed,\\n stateHash,\\n timestamp,\\n version\\n },\\n fees,\\n gasSpent,\\n reward,\\n transactions {...TransactionInfo}\\n}\\n\\n query($amount: Int!) {\\n blocks(last: $amount) {...BlockInfo},\\n transactions(last: $amount) {...TransactionInfo}\\n }\\n ","topic":"gql"}", + "body": "fragment TransactionInfo on SpentTransaction { blockHash, blockHeight, blockTimestamp, err, gasSpent, id, tx { callData { contractId, data, fnName }, gasLimit, gasPrice, id, isDeploy, memo, txType } } fragment BlockInfo on Block { header { hash, gasLimit, height, prevBlockHash, seed, stateHash, timestamp, version }, fees, gasSpent, reward, transactions {...TransactionInfo} } query($amount: Int!) { blocks(last: $amount) {...BlockInfo}, transactions(last: $amount) {...TransactionInfo} }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -314,7 +314,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[1][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[1][1]).toMatchInlineSnapshot(` { - "body": "{"data":"query { block(height: -1) { header { height } } }","topic":"gql"}", + "body": "query { block(height: -1) { header { height } } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -327,7 +327,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[2][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[2][1]).toMatchInlineSnapshot(` { - "body": "{"data":"query { blocks(last: 100) { transactions { err } } }","topic":"gql"}", + "body": "query { blocks(last: 100) { transactions { err } } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -349,7 +349,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n \\nfragment TransactionInfo on SpentTransaction {\\n\\tblockHash,\\n\\tblockHeight,\\n\\tblockTimestamp,\\n err,\\n\\tgasSpent,\\n\\tid,\\n tx {\\n callData {\\n contractId,\\n data,\\n fnName\\n },\\n gasLimit,\\n gasPrice,\\n id,\\n isDeploy,\\n memo,\\n txType\\n }\\n}\\n\\n query($id: String!) { tx(hash: $id) {...TransactionInfo} }\\n ","topic":"gql"}", + "body": "fragment TransactionInfo on SpentTransaction { blockHash, blockHeight, blockTimestamp, err, gasSpent, id, tx { callData { contractId, data, fnName }, gasLimit, gasPrice, id, isDeploy, memo, txType } } query($id: String!) { tx(hash: $id) {...TransactionInfo} }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -373,7 +373,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"query($id: String!) { tx(hash: $id) { tx { json } } }","topic":"gql"}", + "body": "query($id: String!) { tx(hash: $id) { tx { json } } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -396,7 +396,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n \\nfragment TransactionInfo on SpentTransaction {\\n\\tblockHash,\\n\\tblockHeight,\\n\\tblockTimestamp,\\n err,\\n\\tgasSpent,\\n\\tid,\\n tx {\\n callData {\\n contractId,\\n data,\\n fnName\\n },\\n gasLimit,\\n gasPrice,\\n id,\\n isDeploy,\\n memo,\\n txType\\n }\\n}\\n\\n query($amount: Int!) { transactions(last: $amount) {...TransactionInfo} }\\n ","topic":"gql"}", + "body": "fragment TransactionInfo on SpentTransaction { blockHash, blockHeight, blockTimestamp, err, gasSpent, id, tx { callData { contractId, data, fnName }, gasLimit, gasPrice, id, isDeploy, memo, txType } } query($amount: Int!) { transactions(last: $amount) {...TransactionInfo} }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -498,7 +498,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n query($id: String!) {\\n block(hash: $id) { header { hash } },\\n tx(hash: $id) { id }\\n }\\n ","topic":"gql"}", + "body": "query($id: String!) { block(hash: $id) { header { hash } }, tx(hash: $id) { id } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -512,7 +512,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[1][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[1][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n query($height: Float!) { block(height: $height) { header { hash } } }\\n ","topic":"gql"}", + "body": "query($height: Float!) { block(height: $height) { header { hash } } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -541,7 +541,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n query($id: String!) {\\n block(hash: $id) { header { hash } },\\n tx(hash: $id) { id }\\n }\\n ","topic":"gql"}", + "body": "query($id: String!) { block(hash: $id) { header { hash } }, tx(hash: $id) { id } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", @@ -569,7 +569,7 @@ describe("duskAPI", () => { expect(fetchSpy.mock.calls[0][0]).toStrictEqual(gqlExpectedURL); expect(fetchSpy.mock.calls[0][1]).toMatchInlineSnapshot(` { - "body": "{"data":"\\n query($height: Float!) { block(height: $height) { header { hash } } }\\n ","topic":"gql"}", + "body": "query($height: Float!) { block(height: $height) { header { hash } } }", "headers": { "Accept": "application/json", "Accept-Charset": "utf-8", diff --git a/explorer/src/lib/services/duskAPI.js b/explorer/src/lib/services/duskAPI.js index 65b5dc4a00..7cb610477f 100644 --- a/explorer/src/lib/services/duskAPI.js +++ b/explorer/src/lib/services/duskAPI.js @@ -54,11 +54,8 @@ const toHeadersVariables = unless( * @param {{ query: string, variables?: Record }} queryInfo */ const gqlGet = (queryInfo) => - fetch(makeNodeUrl("/02/Chain"), { - body: JSON.stringify({ - data: queryInfo.query, - topic: "gql", - }), + fetch(makeNodeUrl("/on/graphql/query"), { + body: queryInfo.query.replace(/\s+/g, " ").trim(), headers: { Accept: "application/json", "Accept-Charset": "utf-8", diff --git a/rusk/CHANGELOG.md b/rusk/CHANGELOG.md index f072e7cae1..b303cce770 100644 --- a/rusk/CHANGELOG.md +++ b/rusk/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Add `/on/account:
/status` endpoint [#3422] + ### Changed - Change dependency declaration to not require strict equal [#3405] @@ -296,6 +300,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add build system that generates keys for circuits and caches them. +[#3422]: https://github.com/dusk-network/rusk/issues/3422 [#3405]: https://github.com/dusk-network/rusk/issues/3405 [#3359]: https://github.com/dusk-network/rusk/issues/3359 [#3206]: https://github.com/dusk-network/rusk/issues/3206 diff --git a/rusk/src/lib/http/rusk.rs b/rusk/src/lib/http/rusk.rs index 0ecf80f42e..4b101bdb18 100644 --- a/rusk/src/lib/http/rusk.rs +++ b/rusk/src/lib/http/rusk.rs @@ -7,12 +7,14 @@ use super::event::Event; use super::*; -use dusk_bytes::Serializable; +use dusk_bytes::{DeserializableSlice, Serializable}; use dusk_core::abi::ContractId; +use dusk_core::signatures::bls::PublicKey as BlsPublicKey; use dusk_core::stake::StakeFundOwner; use node::vm::VMExecution; use rusk_profile::CRS_17_HASH; use serde::Serialize; +use serde_json::json; use std::sync::{mpsc, Arc}; use std::thread; use tokio::task; @@ -44,6 +46,7 @@ impl HandleRequest for Rusk { match request.uri.inner() { ("contracts", Some(_), _) => true, ("node", _, "provisioners") => true, + ("account", Some(_), "status") => true, ("node", _, "crs") => true, _ => false, } @@ -59,6 +62,8 @@ impl HandleRequest for Rusk { self.handle_contract_query(contract_id, method, data, feeder) } ("node", _, "provisioners") => self.get_provisioners(), + + ("account", Some(pk), "status") => self.get_account(pk), ("node", _, "crs") => self.get_crs(), _ => Err(anyhow::anyhow!("Unsupported")), } @@ -151,6 +156,24 @@ impl Rusk { Ok(ResponseData::new(serde_json::to_value(prov)?)) } + fn get_account(&self, pk: &str) -> anyhow::Result { + let pk = bs58::decode(pk) + .into_vec() + .map_err(|_| anyhow::anyhow!("Invalid bs58 account"))?; + let pk = BlsPublicKey::from_slice(&pk) + .map_err(|_| anyhow::anyhow!("Invalid bls account"))?; + let account = self + .account(&pk) + .map(|account| { + json!({ + "balance": account.balance, + "nonce": account.nonce, + }) + }) + .map_err(|e| anyhow::anyhow!("Cannot query the state {e:?}"))?; + Ok(ResponseData::new(account)) + } + fn get_crs(&self) -> anyhow::Result { let crs = rusk_profile::get_common_reference_string()?; Ok(ResponseData::new(crs).with_header("crs-hash", CRS_17_HASH))