From 573708cc4baeb8fd4068d463a3b0dfdecc75a962 Mon Sep 17 00:00:00 2001 From: Theodore Schnepper Date: Mon, 6 May 2024 09:31:46 -0600 Subject: [PATCH 1/9] Add explorer API to SQL backed images The explorer API that is available in the hotshot query servicer is available to archival nodes that are backed by postgres sql. In order to make the explorer API available to consume, it needs to be exposed as a module. Adds the explorer API so that it is available for all `storage-sql` featured nodes. --- sequencer/src/api/endpoints.rs | 16 +++++++++++ sequencer/src/api/options.rs | 2 ++ sequencer/src/header.rs | 50 ++++++++++++++++++++++++++++++++-- sequencer/src/state.rs | 7 +++++ 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/sequencer/src/api/endpoints.rs b/sequencer/src/api/endpoints.rs index 261da4033..4ff0960d4 100644 --- a/sequencer/src/api/endpoints.rs +++ b/sequencer/src/api/endpoints.rs @@ -18,6 +18,8 @@ use committable::Committable; use futures::{try_join, FutureExt}; use hotshot_query_service::{ availability::{self, AvailabilityDataSource, CustomSnafu, FetchBlockSnafu}, + data_source::storage::ExplorerStorage, + explorer::{self}, merklized_state::{ self, MerklizedState, MerklizedStateDataSource, MerklizedStateHeightPersistence, }, @@ -123,6 +125,20 @@ where Ok(api) } +type ExplorerApi = Api, explorer::Error, Ver>; + +pub(super) fn explorer( + bind_version: Ver, +) -> Result> +where + N: network::Type, + D: ExplorerStorage + Send + Sync + 'static, + P: SequencerPersistence, +{ + let api = explorer::define_api::, SeqTypes, Ver>(bind_version)?; + Ok(api) +} + type NodeApi = Api, node::Error, Ver>; pub(super) fn node( diff --git a/sequencer/src/api/options.rs b/sequencer/src/api/options.rs index f3d461bb2..f7857845c 100644 --- a/sequencer/src/api/options.rs +++ b/sequencer/src/api/options.rs @@ -335,6 +335,8 @@ impl Options { .init_app_modules(ds, state.clone(), tasks, bind_version) .await?; + app.register_module("explorer", endpoints::explorer(bind_version)?)?; + if self.state.is_some() { // Initialize merklized state module for block merkle tree app.register_module( diff --git a/sequencer/src/header.rs b/sequencer/src/header.rs index 5cd062f07..cf1413af4 100644 --- a/sequencer/src/header.rs +++ b/sequencer/src/header.rs @@ -2,14 +2,14 @@ use crate::{ block::{entry::TxTableEntryWord, tables::NameSpaceTable, NsTable}, chain_config::ResolvableChainConfig, l1_client::L1Snapshot, - state::{BlockMerkleCommitment, FeeAccount, FeeInfo, FeeMerkleCommitment}, - ChainConfig, L1BlockInfo, Leaf, NodeState, SeqTypes, ValidatedState, + state::{BlockMerkleCommitment, FeeAccount, FeeAmount, FeeInfo, FeeMerkleCommitment}, + ChainConfig, L1BlockInfo, Leaf, NamespaceId, NodeState, SeqTypes, ValidatedState, }; use anyhow::Context; use ark_serialize::CanonicalSerialize; use committable::{Commitment, Committable, RawCommitmentBuilder}; use ethers::types; -use hotshot_query_service::availability::QueryableHeader; +use hotshot_query_service::{availability::QueryableHeader, explorer::ExplorerHeader}; use hotshot_types::{ traits::{ block_contents::{BlockHeader, BlockPayload, BuilderFee}, @@ -426,6 +426,50 @@ impl QueryableHeader for Header { } } +impl ExplorerHeader for Header { + type BalanceAmount = FeeAmount; + type WalletAddress = FeeAccount; + type ProposerId = FeeAccount; + type NamespaceId = NamespaceId; + + fn proposer_id(&self) -> Self::ProposerId { + self.fee_info.account() + } + + fn fee_info_account(&self) -> Self::WalletAddress { + self.fee_info.account() + } + + fn fee_info_balance(&self) -> Self::BalanceAmount { + self.fee_info.amount() + } + + fn reward_balance(&self) -> Self::BalanceAmount { + FeeAmount::from(0) + } + + fn namespace_ids(&self) -> Vec { + let l = self.ns_table.len(); + let mut result: Vec = Vec::with_capacity(l); + for i in 0..l { + let (ns_id, _) = self.ns_table.get_table_entry(i); + result.push(ns_id); + } + + result + } + + fn namespace_ids_for_offset(&self, offset: usize) -> Vec { + let l = self.ns_table.len(); + if offset >= l { + return vec![]; + } + + let (ns_id, _) = self.ns_table.get_table_entry(offset); + vec![ns_id] + } +} + #[cfg(test)] mod test_headers { use std::sync::Arc; diff --git a/sequencer/src/state.rs b/sequencer/src/state.rs index 1930d2ff4..ed6b9b644 100644 --- a/sequencer/src/state.rs +++ b/sequencer/src/state.rs @@ -18,6 +18,7 @@ use hotshot::traits::ValidatedState as HotShotState; use hotshot_query_service::{ availability::{AvailabilityDataSource, LeafQueryData}, data_source::VersionedDataSource, + explorer::MonetaryValue, merklized_state::{MerklizedState, MerklizedStateHeightPersistence, UpdateStateData}, types::HeightIndexed, }; @@ -889,6 +890,12 @@ impl From for FeeAmount { } } +impl From for MonetaryValue { + fn from(value: FeeAmount) -> Self { + MonetaryValue::eth(value.0.as_u128() as i128) + } +} + impl CheckedSub for FeeAmount { fn checked_sub(&self, v: &Self) -> Option { self.0.checked_sub(v.0).map(FeeAmount) From ed989019aab30ff8c56b4a85305498cb1bf68f35 Mon Sep 17 00:00:00 2001 From: Theodore Schnepper Date: Mon, 6 May 2024 09:35:21 -0600 Subject: [PATCH 2/9] Add `block-explorer` to demos With the `explorer api` being available to to `sequencer`s backed by sql storage, we have the ability to run the `block-explorer` as a docker container. This adds the `block-explorer` to the `process-compose` and `docker-compose` files for use in the demo commands. --- docker-compose.yaml | 10 ++++++++++ process-compose.yaml | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/docker-compose.yaml b/docker-compose.yaml index 89d0f2deb..537692231 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -475,3 +475,13 @@ services: interval: 5s timeout: 4s retries: 20 + + block-explorer: + image: ghcr.io/espressosystems/espresso-block-explorer:release-cappuccino + ports: + - "3000:3000" + environment: + - QUERY_SERVICE_URI:http://localhost:$ESPRESSO_SEQUENCER1_API_PORT/v0/ + depends_on: + sequencer1: + condition: service_healthy diff --git a/process-compose.yaml b/process-compose.yaml index 1b72b0637..2bc6e7232 100644 --- a/process-compose.yaml +++ b/process-compose.yaml @@ -7,6 +7,7 @@ environment: - ESPRESSO_SEQUENCER_L1_PROVIDER=http://localhost:$ESPRESSO_SEQUENCER_L1_PORT - ESPRESSO_DEMO_L1_HTTP_PROVIDER=$ESPRESSO_SEQUENCER_L1_PROVIDER - ESPRESSO_STATE_RELAY_SERVER_URL=http://localhost:$ESPRESSO_STATE_RELAY_SERVER_PORT + - QUERY_SERVICE_URI=http://localhost:$ESPRESSO_SEQUENCER1_API_PORT/v0/ processes: # Cheating a bit here but since we don't usually have to debug go-ethereum # it's using the docker compose service which is a bit easier. @@ -429,3 +430,11 @@ processes: # See https://github.com/docker-library/postgres/issues/146 for discussion. success_threshold: 2 failure_threshold: 20 + + block-explorer: + command: docker run --rm -p 3000:3000 -e QUERY_SERVICE_URI --platform=linux/amd64 ghcr.io/espressosystems/espresso-block-explorer:release-cappuccino + ports: + - "3000:3000" + depends_on: + sequencer1: + condition: process_healthy From a5f988c695372981e2a5f952f5f98441b69f4109 Mon Sep 17 00:00:00 2001 From: Theodore Schnepper Date: Tue, 7 May 2024 07:32:46 -0600 Subject: [PATCH 3/9] Add ESPRESSO_BLOCK_EXPLORER_PORT env variable The specific port that the `Block Explorer` is being served on should be listed within the `.env` file in order to ensure that there is not a potential for port reuse. In addition this environment variable should be referenced in `docker-compose.yaml` and `process-compose.yaml` where necessary. --- .env | 1 + docker-compose.yaml | 2 +- process-compose.yaml | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.env b/.env index 451c5c6dd..f3ceaf61e 100644 --- a/.env +++ b/.env @@ -44,6 +44,7 @@ ESPRESSO_COMMITMENT_TASK_PORT=60000 ESPRESSO_SEQUENCER_DB_PORT=5432 ESPRESSO_STATE_RELAY_SERVER_PORT=40004 ESPRESSO_STATE_RELAY_SERVER_URL=http://state-relay-server:${ESPRESSO_STATE_RELAY_SERVER_PORT} +ESPRESSO_BLOCK_EXPLORER_PORT=3000 # Ethereum accounts (note 11-15 are used by the sequencer nodes) ESPRESSO_SEQUENCER_HOTSHOT_ACCOUNT_INDEX=5 diff --git a/docker-compose.yaml b/docker-compose.yaml index 537692231..1d8639bf6 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -479,7 +479,7 @@ services: block-explorer: image: ghcr.io/espressosystems/espresso-block-explorer:release-cappuccino ports: - - "3000:3000" + - "$ESPRESSO_BLOCK_EXPLORER_PORT:3000" environment: - QUERY_SERVICE_URI:http://localhost:$ESPRESSO_SEQUENCER1_API_PORT/v0/ depends_on: diff --git a/process-compose.yaml b/process-compose.yaml index 2bc6e7232..6cedc15f5 100644 --- a/process-compose.yaml +++ b/process-compose.yaml @@ -432,9 +432,7 @@ processes: failure_threshold: 20 block-explorer: - command: docker run --rm -p 3000:3000 -e QUERY_SERVICE_URI --platform=linux/amd64 ghcr.io/espressosystems/espresso-block-explorer:release-cappuccino - ports: - - "3000:3000" + command: docker run --rm -p $ESPRESSO_BLOCK_EXPLORER_PORT:3000 -e QUERY_SERVICE_URI --platform=linux/amd64 ghcr.io/espressosystems/espresso-block-explorer:release-cappuccino depends_on: sequencer1: condition: process_healthy From 9c4b8e5660951676a4a7c27f8012493bc8ca0e39 Mon Sep 17 00:00:00 2001 From: Theodore Schnepper Date: Tue, 7 May 2024 07:57:46 -0600 Subject: [PATCH 4/9] Add explorer module option The behaviors, features, and modules available within the `sequencer` binary are enabled via a command specified within the command line arguments. Since the explorer is another such module, it too should be configured to be enabled or disabled based on the same configuration pattern. This adds the explorer configuration option for optionally enabling the explorer module. It also links the dependencies of the module in order to ensure that is is enabled correctly. --- docker-compose.yaml | 2 +- process-compose.yaml | 2 +- sequencer/src/api/options.rs | 16 +++++++++++++++- sequencer/src/options.rs | 9 +++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 1d8639bf6..c39607431 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -219,7 +219,7 @@ services: image: ghcr.io/espressosystems/espresso-sequencer/sequencer:main ports: - "$ESPRESSO_SEQUENCER1_API_PORT:$ESPRESSO_SEQUENCER_API_PORT" - command: sequencer -- storage-sql -- http -- query -- catchup -- state + command: sequencer -- storage-sql -- http -- query -- catchup -- state -- explorer environment: - ESPRESSO_SEQUENCER_ORCHESTRATOR_URL - ESPRESSO_SEQUENCER_CDN_ENDPOINT diff --git a/process-compose.yaml b/process-compose.yaml index 6cedc15f5..d4021ac78 100644 --- a/process-compose.yaml +++ b/process-compose.yaml @@ -114,7 +114,7 @@ processes: failure_threshold: 100 sequencer1: - command: sequencer -- storage-sql -- http -- query -- catchup -- status -- state + command: sequencer -- storage-sql -- http -- query -- catchup -- status -- state -- explorer environment: - ESPRESSO_SEQUENCER_API_PORT=$ESPRESSO_SEQUENCER1_API_PORT - ESPRESSO_SEQUENCER_LIBP2P_BIND_ADDRESS=0.0.0.0:$ESPRESSO_DEMO_SEQUENCER_LIBP2P_PORT_1 diff --git a/sequencer/src/api/options.rs b/sequencer/src/api/options.rs index f7857845c..0ba863895 100644 --- a/sequencer/src/api/options.rs +++ b/sequencer/src/api/options.rs @@ -45,6 +45,7 @@ pub struct Options { pub catchup: Option, pub state: Option, pub hotshot_events: Option, + pub explorer: Option, pub storage_fs: Option, pub storage_sql: Option, } @@ -59,6 +60,7 @@ impl From for Options { catchup: None, state: None, hotshot_events: None, + explorer: None, storage_fs: None, storage_sql: None, } @@ -110,6 +112,12 @@ impl Options { self } + /// Add an explorer API module. + pub fn explorer(mut self, opt: Explorer) -> Self { + self.explorer = Some(opt); + self + } + /// Whether these options will run the query API. pub fn has_query_module(&self) -> bool { self.query.is_some() && (self.storage_fs.is_some() || self.storage_sql.is_some()) @@ -335,7 +343,9 @@ impl Options { .init_app_modules(ds, state.clone(), tasks, bind_version) .await?; - app.register_module("explorer", endpoints::explorer(bind_version)?)?; + if self.explorer.is_some() { + app.register_module("explorer", endpoints::explorer(bind_version)?)?; + } if self.state.is_some() { // Initialize merklized state module for block merkle tree @@ -489,3 +499,7 @@ pub struct HotshotEvents { #[clap(long, env = "ESPRESSO_SEQUENCER_HOTSHOT_EVENT_STREAMING_API_PORT")] pub events_service_port: u16, } + +/// Options for the explorer API module. +#[derive(Parser, Clone, Copy, Debug, Default)] +pub struct Explorer; diff --git a/sequencer/src/options.rs b/sequencer/src/options.rs index 779486a87..b37e9e92a 100644 --- a/sequencer/src/options.rs +++ b/sequencer/src/options.rs @@ -282,6 +282,9 @@ impl ModuleArgs { SequencerModule::HotshotEvents(m) => { curr = m.add(&mut modules.hotshot_events, &mut provided)? } + SequencerModule::Explorer(m) => { + curr = m.add(&mut modules.explorer, &mut provided)? + } } } @@ -315,6 +318,7 @@ module!("status", api::options::Status, requires: "http"); module!("state", api::options::State, requires: "http", "storage-sql"); module!("catchup", api::options::Catchup, requires: "http"); module!("hotshot-events", api::options::HotshotEvents, requires: "http"); +module!("explorer", api::options::Explorer, requires: "http", "storage-sql"); #[derive(Clone, Debug, Args)] struct Module { @@ -392,6 +396,10 @@ enum SequencerModule { /// /// This module requires the http module to be started. HotshotEvents(Module), + /// Run the explorer API module. + /// + /// This module requires the http and storage-sql modules to be started. + Explorer(Module), } #[derive(Clone, Debug, Default)] @@ -405,4 +413,5 @@ pub struct Modules { pub state: Option, pub catchup: Option, pub hotshot_events: Option, + pub explorer: Option, } From 99131f0889cf04b1d4a32606ab297812567913aa Mon Sep 17 00:00:00 2001 From: Theodore Schnepper Date: Tue, 7 May 2024 08:00:24 -0600 Subject: [PATCH 5/9] Modify referenced docker image to point to `main` Since this is the `main` branch of the project, and not a `release` branch, we should refer to the `main` branch of our corresponding docker images. --- docker-compose.yaml | 2 +- process-compose.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index c39607431..e7c3b35d3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -477,7 +477,7 @@ services: retries: 20 block-explorer: - image: ghcr.io/espressosystems/espresso-block-explorer:release-cappuccino + image: ghcr.io/espressosystems/espresso-block-explorer:main ports: - "$ESPRESSO_BLOCK_EXPLORER_PORT:3000" environment: diff --git a/process-compose.yaml b/process-compose.yaml index d4021ac78..ca801a13b 100644 --- a/process-compose.yaml +++ b/process-compose.yaml @@ -432,7 +432,7 @@ processes: failure_threshold: 20 block-explorer: - command: docker run --rm -p $ESPRESSO_BLOCK_EXPLORER_PORT:3000 -e QUERY_SERVICE_URI --platform=linux/amd64 ghcr.io/espressosystems/espresso-block-explorer:release-cappuccino + command: docker run --rm -p $ESPRESSO_BLOCK_EXPLORER_PORT:3000 -e QUERY_SERVICE_URI --platform=linux/amd64 ghcr.io/espressosystems/espresso-block-explorer:main depends_on: sequencer1: condition: process_healthy From a45beea865d880dad32f79d5c3e4dead83da37ea Mon Sep 17 00:00:00 2001 From: Theodore Schnepper Date: Tue, 7 May 2024 09:57:50 -0600 Subject: [PATCH 6/9] Add explorer consideration to http options The http options currently aren't getting passed any explorer options when configured. This is due to missing a configuration call in `init_with_storage` when adding the explorer options. --- sequencer/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sequencer/src/main.rs b/sequencer/src/main.rs index a57b5cb2f..e8674f562 100644 --- a/sequencer/src/main.rs +++ b/sequencer/src/main.rs @@ -110,6 +110,9 @@ where if let Some(hotshot_events) = modules.hotshot_events { http_opt = http_opt.hotshot_events(hotshot_events); } + if let Some(explorer) = modules.explorer { + http_opt = http_opt.explorer(explorer); + } http_opt .serve( From 25bca33758a88b628e6716de43be8e8c9b52e7fb Mon Sep 17 00:00:00 2001 From: Theodore Schnepper Date: Tue, 7 May 2024 10:35:05 -0600 Subject: [PATCH 7/9] Remove specific platform for docker command The `--platform` flag being `linux/amd64` really limits the ability to pull and run the demo on certain machines. It was here due to the `arm64` build timing out. However, this has since been resolved, and this platform flag can now be removed. --- process-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process-compose.yaml b/process-compose.yaml index ca801a13b..ff08be6ce 100644 --- a/process-compose.yaml +++ b/process-compose.yaml @@ -432,7 +432,7 @@ processes: failure_threshold: 20 block-explorer: - command: docker run --rm -p $ESPRESSO_BLOCK_EXPLORER_PORT:3000 -e QUERY_SERVICE_URI --platform=linux/amd64 ghcr.io/espressosystems/espresso-block-explorer:main + command: docker run --rm -p $ESPRESSO_BLOCK_EXPLORER_PORT:3000 -e QUERY_SERVICE_URI ghcr.io/espressosystems/espresso-block-explorer:main depends_on: sequencer1: condition: process_healthy From 4abcb029080319feb76ca63bae05bafaeb52d466 Mon Sep 17 00:00:00 2001 From: Theodore Schnepper Date: Mon, 13 May 2024 07:48:17 -0600 Subject: [PATCH 8/9] Update `ExplorerHeader` implementation The `ExplorerHeader` trait no longer has the `namespace_ids_for_offset` method. Add `ExplorerTransaction` trait implementation The new `ExplorerTransaction` trait implementation allows for the retrieval of a `NamespaceId` from a given `Transaction`. --- sequencer/src/header.rs | 10 ---------- sequencer/src/transaction.rs | 8 ++++++++ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/sequencer/src/header.rs b/sequencer/src/header.rs index da58d66a4..7870aeb25 100644 --- a/sequencer/src/header.rs +++ b/sequencer/src/header.rs @@ -476,16 +476,6 @@ impl ExplorerHeader for Header { result } - - fn namespace_ids_for_offset(&self, offset: usize) -> Vec { - let l = self.ns_table.len(); - if offset >= l { - return vec![]; - } - - let (ns_id, _) = self.ns_table.get_table_entry(offset); - vec![ns_id] - } } #[cfg(test)] diff --git a/sequencer/src/transaction.rs b/sequencer/src/transaction.rs index 16d16384d..d1015a097 100644 --- a/sequencer/src/transaction.rs +++ b/sequencer/src/transaction.rs @@ -1,6 +1,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use committable::{Commitment, Committable}; use derive_more::{Display, From, Into}; +use hotshot_query_service::explorer::ExplorerTransaction; use hotshot_types::traits::block_contents::Transaction as HotShotTransaction; use jf_merkle_tree::namespaced_merkle_tree::{Namespace, Namespaced}; use serde::{Deserialize, Serialize}; @@ -106,3 +107,10 @@ impl Committable for Transaction { "TX".into() } } + +impl ExplorerTransaction for Transaction { + type NamespaceId = NamespaceId; + fn namespace_id(&self) -> Self::NamespaceId { + self.namespace + } +} From 534cec70408a6864e64db5463cc5bd9f4f48e9d8 Mon Sep 17 00:00:00 2001 From: Theodore Schnepper Date: Mon, 13 May 2024 07:58:40 -0600 Subject: [PATCH 9/9] Add a comment explaining stub implementation Add TODO mark for stub implementation to comment Add Tracked issue reference to comment The `reward_balance` method for the `ExplorerHeader` implementation has a stub implementation as the Block Reward is not currently supported within the sequencer. In order to document this effectively, a comment has been added to track it as such, along with a TODO that explains what change will need to happen, and an issue for added visibility / lookup. --- sequencer/src/header.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sequencer/src/header.rs b/sequencer/src/header.rs index 7870aeb25..c0f992df9 100644 --- a/sequencer/src/header.rs +++ b/sequencer/src/header.rs @@ -462,6 +462,11 @@ impl ExplorerHeader for Header { self.fee_info.amount() } + /// reward_balance at the moment is only implemented as a stub, as block + /// rewards have not yet been implemented. + /// + /// TODO: update implementation when rewards have been created / supported. + /// Issue: https://github.com/EspressoSystems/espresso-sequencer/issues/1453 fn reward_balance(&self) -> Self::BalanceAmount { FeeAmount::from(0) }