From f63d07dd6025f3a4f61653a22f712c8193c51ce9 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 6 Jan 2026 16:52:07 -0800 Subject: [PATCH 01/13] all: Remove support for substreams --- Cargo.lock | 23 - Cargo.toml | 1 - chain/substreams/Cargo.toml | 23 - chain/substreams/build.rs | 8 - chain/substreams/examples/README.md | 13 - chain/substreams/examples/substreams.rs | 115 --- chain/substreams/proto/codec.proto | 47 -- chain/substreams/src/block_ingestor.rs | 204 ----- chain/substreams/src/block_stream.rs | 115 --- chain/substreams/src/chain.rs | 231 ------ chain/substreams/src/codec.rs | 5 - chain/substreams/src/data_source.rs | 765 ------------------ chain/substreams/src/lib.rs | 17 - chain/substreams/src/mapper.rs | 415 ---------- .../src/protobuf/substreams.entity.v1.rs | 107 --- chain/substreams/src/trigger.rs | 255 ------ core/Cargo.toml | 1 - core/src/subgraph/instance_manager.rs | 17 - core/src/subgraph/registrar.rs | 19 - graph/src/blockchain/mod.rs | 4 - node/Cargo.toml | 1 - node/resources/tests/full_config.toml | 1 - node/src/chain.rs | 144 +--- node/src/config.rs | 237 +----- node/src/network_setup.rs | 28 +- server/index-node/Cargo.toml | 1 - server/index-node/src/resolver.rs | 20 +- tests/Cargo.toml | 1 - tests/src/fixture/mod.rs | 45 -- tests/src/fixture/substreams.rs | 34 - tests/tests/runner_tests.rs | 32 - 31 files changed, 11 insertions(+), 2918 deletions(-) delete mode 100644 chain/substreams/Cargo.toml delete mode 100644 chain/substreams/build.rs delete mode 100644 chain/substreams/examples/README.md delete mode 100644 chain/substreams/examples/substreams.rs delete mode 100644 chain/substreams/proto/codec.proto delete mode 100644 chain/substreams/src/block_ingestor.rs delete mode 100644 chain/substreams/src/block_stream.rs delete mode 100644 chain/substreams/src/chain.rs delete mode 100644 chain/substreams/src/codec.rs delete mode 100644 chain/substreams/src/data_source.rs delete mode 100644 chain/substreams/src/lib.rs delete mode 100644 chain/substreams/src/mapper.rs delete mode 100644 chain/substreams/src/protobuf/substreams.entity.v1.rs delete mode 100644 chain/substreams/src/trigger.rs delete mode 100644 tests/src/fixture/substreams.rs diff --git a/Cargo.lock b/Cargo.lock index 71299dffa21..0793210303d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2958,25 +2958,6 @@ dependencies = [ "trigger-filters", ] -[[package]] -name = "graph-chain-substreams" -version = "0.36.0" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.22.1", - "graph", - "graph-runtime-wasm", - "hex", - "prost", - "prost-types", - "semver", - "serde", - "tokio", - "tokio-stream", - "tonic-build", -] - [[package]] name = "graph-core" version = "0.36.0" @@ -2991,7 +2972,6 @@ dependencies = [ "graph", "graph-chain-ethereum", "graph-chain-near", - "graph-chain-substreams", "graph-runtime-wasm", "indoc", "itertools", @@ -3038,7 +3018,6 @@ dependencies = [ "graph", "graph-chain-ethereum", "graph-chain-near", - "graph-chain-substreams", "graph-core", "graph-graphql", "graph-server-http", @@ -3122,7 +3101,6 @@ dependencies = [ "graph", "graph-chain-ethereum", "graph-chain-near", - "graph-chain-substreams", "graph-graphql", ] @@ -3194,7 +3172,6 @@ dependencies = [ "async-trait", "graph", "graph-chain-ethereum", - "graph-chain-substreams", "graph-core", "graph-graphql", "graph-node", diff --git a/Cargo.toml b/Cargo.toml index 861b70ea958..020bb9bb117 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ members = [ "chain/common", "chain/ethereum", "chain/near", - "chain/substreams", "gnd", "graphql", "node", diff --git a/chain/substreams/Cargo.toml b/chain/substreams/Cargo.toml deleted file mode 100644 index 57a5706aca1..00000000000 --- a/chain/substreams/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "graph-chain-substreams" -version.workspace = true -edition.workspace = true - -[build-dependencies] -tonic-build = { workspace = true } - -[dependencies] -async-trait = { workspace = true } -graph = { path = "../../graph" } -graph-runtime-wasm = { path = "../../runtime/wasm" } -serde = { workspace = true } -prost = { workspace = true } -prost-types = { workspace = true } -anyhow = "1.0" -hex = "0.4.3" -semver = "1.0.27" -base64 = "0.22.1" -tokio-stream = { workspace = true } - -[dev-dependencies] -tokio = { version = "1", features = ["full"] } diff --git a/chain/substreams/build.rs b/chain/substreams/build.rs deleted file mode 100644 index 330a01a8c68..00000000000 --- a/chain/substreams/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=proto"); - tonic_build::configure() - .protoc_arg("--experimental_allow_proto3_optional") - .out_dir("src/protobuf") - .compile_protos(&["proto/codec.proto"], &["proto"]) - .expect("Failed to compile Substreams entity proto(s)"); -} diff --git a/chain/substreams/examples/README.md b/chain/substreams/examples/README.md deleted file mode 100644 index afd1882b337..00000000000 --- a/chain/substreams/examples/README.md +++ /dev/null @@ -1,13 +0,0 @@ -## Substreams example - -1. Set environmental variables -```bash -$> export SUBSTREAMS_API_TOKEN=your_sf_token -$> export SUBSTREAMS_ENDPOINT=your_sf_endpoint # you can also not define this one and use the default specified endpoint -$> export SUBSTREAMS_PACKAGE=path_to_your_spkg -``` - -2. Run `substreams` example -```bash -cargo run -p graph-chain-substreams --example substreams [module_name] # for graph entities run `graph_out` -``` diff --git a/chain/substreams/examples/substreams.rs b/chain/substreams/examples/substreams.rs deleted file mode 100644 index d2277580c37..00000000000 --- a/chain/substreams/examples/substreams.rs +++ /dev/null @@ -1,115 +0,0 @@ -use anyhow::{format_err, Context, Error}; -use graph::blockchain::block_stream::{BlockStreamEvent, FirehoseCursor}; -use graph::blockchain::client::ChainClient; -use graph::blockchain::substreams_block_stream::SubstreamsBlockStream; -use graph::endpoint::EndpointMetrics; -use graph::firehose::{FirehoseEndpoints, SubgraphLimit}; -use graph::prelude::{info, tokio, DeploymentHash, MetricsRegistry, Registry}; -use graph::{env::env_var, firehose::FirehoseEndpoint, log::logger, substreams}; -use graph_chain_substreams::mapper::Mapper; -use prost::Message; -use std::env; -use std::sync::Arc; -use tokio_stream::StreamExt; - -#[tokio::main] -async fn main() -> Result<(), Error> { - let module_name = env::args().nth(1).unwrap(); - - let token_env = env_var("SUBSTREAMS_API_TOKEN", "".to_string()); - let mut token: Option = None; - if !token_env.is_empty() { - token = Some(token_env); - } - - let endpoint = env_var( - "SUBSTREAMS_ENDPOINT", - "https://api.streamingfast.io".to_string(), - ); - - let package_file = env_var("SUBSTREAMS_PACKAGE", "".to_string()); - if package_file.is_empty() { - panic!("Environment variable SUBSTREAMS_PACKAGE must be set"); - } - - let package = read_package(&package_file)?; - - let logger = logger(true); - // Set up Prometheus registry - let prometheus_registry = Arc::new(Registry::new()); - let metrics_registry = Arc::new(MetricsRegistry::new( - logger.clone(), - prometheus_registry.clone(), - )); - - let endpoint_metrics = EndpointMetrics::new( - logger.clone(), - &[endpoint.clone()], - Arc::new(MetricsRegistry::mock()), - ); - - let firehose = Arc::new(FirehoseEndpoint::new( - "substreams", - &endpoint, - token, - None, - false, - false, - SubgraphLimit::Unlimited, - Arc::new(endpoint_metrics), - true, - )); - - let client = Arc::new(ChainClient::new_firehose(FirehoseEndpoints::for_testing( - vec![firehose], - ))); - - let mut stream: SubstreamsBlockStream = - SubstreamsBlockStream::new( - DeploymentHash::new("substreams".to_string()).unwrap(), - client, - None, - FirehoseCursor::None, - Arc::new(Mapper { - schema: None, - skip_empty_blocks: false, - }), - package.modules.clone().unwrap_or_default(), - module_name.to_string(), - vec![12369621], - vec![], - logger.clone(), - metrics_registry, - ); - - loop { - match stream.next().await { - None => { - break; - } - Some(event) => match event { - Err(_) => {} - Ok(block_stream_event) => match block_stream_event { - BlockStreamEvent::ProcessWasmBlock(_, _, _, _, _) => { - unreachable!("Cannot happen with this mapper") - } - BlockStreamEvent::Revert(_, _) => {} - BlockStreamEvent::ProcessBlock(block_with_trigger, _) => { - for change in block_with_trigger.block.changes.entity_changes { - for field in change.fields { - info!(&logger, "field: {:?}", field); - } - } - } - }, - }, - } - } - - Ok(()) -} - -fn read_package(file: &str) -> Result { - let content = std::fs::read(file).context(format_err!("read package {}", file))?; - substreams::Package::decode(content.as_ref()).context("decode command") -} diff --git a/chain/substreams/proto/codec.proto b/chain/substreams/proto/codec.proto deleted file mode 100644 index bd75e7f95c8..00000000000 --- a/chain/substreams/proto/codec.proto +++ /dev/null @@ -1,47 +0,0 @@ -syntax = "proto3"; - -package substreams.entity.v1; - -message EntityChanges { - repeated EntityChange entity_changes = 5; -} - -message EntityChange { - string entity = 1; - string id = 2; - uint64 ordinal = 3; - enum Operation { - UNSET = 0; // Protobuf default should not be used, this is used so that the consume can ensure that the value was actually specified - CREATE = 1; - UPDATE = 2; - DELETE = 3; - } - Operation operation = 4; - repeated Field fields = 5; -} - -message Value { - oneof typed { - int32 int32 = 1; - string bigdecimal = 2; - string bigint = 3; - string string = 4; - bytes bytes = 5; - bool bool = 6; - int64 timestamp = 7; - //reserved 8 to 9; // For future types - - Array array = 10; - } -} - -message Array { - repeated Value value = 1; -} - -message Field { - string name = 1; - optional Value new_value = 3; - optional Value old_value = 5; -} - diff --git a/chain/substreams/src/block_ingestor.rs b/chain/substreams/src/block_ingestor.rs deleted file mode 100644 index 46966e9e4eb..00000000000 --- a/chain/substreams/src/block_ingestor.rs +++ /dev/null @@ -1,204 +0,0 @@ -use std::{sync::Arc, time::Duration}; - -use crate::mapper::Mapper; -use anyhow::{Context, Error}; -use async_trait::async_trait; -use graph::blockchain::block_stream::{BlockStreamError, FirehoseCursor}; -use graph::blockchain::BlockchainKind; -use graph::blockchain::{ - client::ChainClient, substreams_block_stream::SubstreamsBlockStream, BlockIngestor, -}; -use graph::components::network_provider::ChainName; -use graph::components::store::ChainHeadStore; -use graph::prelude::MetricsRegistry; -use graph::slog::trace; -use graph::substreams::Package; -use graph::{ - blockchain::block_stream::BlockStreamEvent, - cheap_clone::CheapClone, - prelude::{error, info, DeploymentHash, Logger}, - util::backoff::ExponentialBackoff, -}; -use prost::Message; -use tokio_stream::StreamExt; - -const SUBSTREAMS_HEAD_TRACKER_BYTES: &[u8; 89935] = include_bytes!( - "../../../substreams/substreams-head-tracker/substreams-head-tracker-v1.0.0.spkg" -); - -pub struct SubstreamsBlockIngestor { - chain_store: Arc, - client: Arc>, - logger: Logger, - chain_name: ChainName, - metrics: Arc, -} - -impl SubstreamsBlockIngestor { - pub fn new( - chain_store: Arc, - client: Arc>, - logger: Logger, - chain_name: ChainName, - metrics: Arc, - ) -> SubstreamsBlockIngestor { - SubstreamsBlockIngestor { - chain_store, - client, - logger, - chain_name, - metrics, - } - } - - async fn fetch_head_cursor(&self) -> String { - let mut backoff = - ExponentialBackoff::new(Duration::from_millis(250), Duration::from_secs(30)); - loop { - match self.chain_store.clone().chain_head_cursor().await { - Ok(cursor) => return cursor.unwrap_or_default(), - Err(e) => { - error!(self.logger, "Fetching chain head cursor failed: {:#}", e); - - backoff.sleep_async().await; - } - } - } - } - - /// Consumes the incoming stream of blocks infinitely until it hits an error. In which case - /// the error is logged right away and the latest available cursor is returned - /// upstream for future consumption. - /// If an error is returned it indicates a fatal/deterministic error which should not be retried. - async fn process_blocks( - &self, - cursor: FirehoseCursor, - mut stream: SubstreamsBlockStream, - ) -> Result { - let mut latest_cursor = cursor; - - while let Some(message) = stream.next().await { - let (block, cursor) = match message { - Ok(BlockStreamEvent::ProcessWasmBlock( - _block_ptr, - _block_time, - _data, - _handler, - _cursor, - )) => { - unreachable!("Block ingestor should never receive raw blocks"); - } - Ok(BlockStreamEvent::ProcessBlock(triggers, cursor)) => { - (Arc::new(triggers.block), cursor) - } - Ok(BlockStreamEvent::Revert(_ptr, _cursor)) => { - trace!(self.logger, "Received undo block to ingest, skipping"); - continue; - } - Err(e) if e.is_deterministic() => { - return Err(e); - } - Err(e) => { - info!( - self.logger, - "An error occurred while streaming blocks: {}", e - ); - break; - } - }; - - let res = self.process_new_block(block, cursor.to_string()).await; - if let Err(e) = res { - error!(self.logger, "Process block failed: {:#}", e); - break; - } - - latest_cursor = cursor - } - - error!( - self.logger, - "Stream blocks complete unexpectedly, expecting stream to always stream blocks" - ); - - Ok(latest_cursor) - } - - async fn process_new_block( - &self, - block: Arc, - cursor: String, - ) -> Result<(), Error> { - trace!(self.logger, "Received new block to ingest {:?}", block); - - self.chain_store - .clone() - .set_chain_head(block, cursor) - .await - .context("Updating chain head")?; - - Ok(()) - } -} - -#[async_trait] -impl BlockIngestor for SubstreamsBlockIngestor { - async fn run(self: Box) { - let mapper = Arc::new(Mapper { - schema: None, - skip_empty_blocks: false, - }); - let mut latest_cursor = FirehoseCursor::from(self.fetch_head_cursor().await); - let mut backoff = - ExponentialBackoff::new(Duration::from_millis(250), Duration::from_secs(30)); - let package = Package::decode(SUBSTREAMS_HEAD_TRACKER_BYTES.to_vec().as_ref()).unwrap(); - - loop { - let stream = SubstreamsBlockStream::::new( - DeploymentHash::default(), - self.client.cheap_clone(), - None, - latest_cursor.clone(), - mapper.cheap_clone(), - package.modules.clone().unwrap_or_default(), - "map_blocks".to_string(), - vec![-1], - vec![], - self.logger.cheap_clone(), - self.metrics.cheap_clone(), - ); - - // Consume the stream of blocks until an error is hit - // If the error is retryable it will print the error and return the cursor - // therefore if we get an error here it has to be a fatal error. - // This is a bit brittle and should probably be improved at some point. - let res = self.process_blocks(latest_cursor.clone(), stream).await; - match res { - Ok(cursor) => { - if cursor.as_ref() != latest_cursor.as_ref() { - backoff.reset(); - latest_cursor = cursor; - } - } - Err(BlockStreamError::Fatal(e)) => { - error!( - self.logger, - "fatal error while ingesting substream blocks: {}", e - ); - return; - } - _ => unreachable!("Nobody should ever see this error message, something is wrong"), - } - - // If we reach this point, we must wait a bit before retrying - backoff.sleep_async().await; - } - } - - fn network_name(&self) -> ChainName { - self.chain_name.clone() - } - fn kind(&self) -> BlockchainKind { - BlockchainKind::Substreams - } -} diff --git a/chain/substreams/src/block_stream.rs b/chain/substreams/src/block_stream.rs deleted file mode 100644 index daad94bae20..00000000000 --- a/chain/substreams/src/block_stream.rs +++ /dev/null @@ -1,115 +0,0 @@ -use anyhow::Result; -use async_trait::async_trait; -use std::sync::Arc; - -use graph::{ - blockchain::{ - block_stream::{ - BlockStream, BlockStreamBuilder as BlockStreamBuilderTrait, FirehoseCursor, - }, - substreams_block_stream::SubstreamsBlockStream, - Blockchain, TriggerFilterWrapper, - }, - components::store::{DeploymentLocator, SourceableStore}, - data::subgraph::UnifiedMappingApiVersion, - prelude::{BlockNumber, BlockPtr}, - schema::InputSchema, - slog::o, -}; - -use crate::{ - mapper::{Mapper, WasmBlockMapper}, - Chain, TriggerFilter, -}; - -pub struct BlockStreamBuilder {} - -impl BlockStreamBuilder { - pub fn new() -> Self { - Self {} - } -} - -#[async_trait] -/// Substreams doesn't actually use Firehose, the configuration for firehose and the grpc substream -/// is very similar, so we can re-use the configuration and the builder for it. -/// This is probably something to improve but for now it works. -impl BlockStreamBuilderTrait for BlockStreamBuilder { - async fn build_substreams( - &self, - chain: &Chain, - schema: InputSchema, - deployment: DeploymentLocator, - block_cursor: FirehoseCursor, - subgraph_current_block: Option, - filter: Arc<::TriggerFilter>, - ) -> Result>> { - let logger = chain - .logger_factory - .subgraph_logger(&deployment) - .new(o!("component" => "SubstreamsBlockStream")); - - let stream = match &filter.mapping_handler { - Some(handler) => SubstreamsBlockStream::new( - deployment.hash, - chain.chain_client(), - subgraph_current_block, - block_cursor.clone(), - Arc::new(WasmBlockMapper { - handler: handler.clone(), - }), - filter.modules.clone().unwrap_or_default(), - filter.module_name.clone(), - filter.start_block.map(|x| vec![x]).unwrap_or_default(), - vec![], - logger, - chain.metrics_registry.clone(), - ), - - None => SubstreamsBlockStream::new( - deployment.hash, - chain.chain_client(), - subgraph_current_block, - block_cursor.clone(), - Arc::new(Mapper { - schema: Some(schema), - skip_empty_blocks: true, - }), - filter.modules.clone().unwrap_or_default(), - filter.module_name.clone(), - filter.start_block.map(|x| vec![x]).unwrap_or_default(), - vec![], - logger, - chain.metrics_registry.clone(), - ), - }; - - Ok(Box::new(stream)) - } - - async fn build_firehose( - &self, - _chain: &Chain, - _deployment: DeploymentLocator, - _block_cursor: FirehoseCursor, - _start_blocks: Vec, - _subgraph_current_block: Option, - _filter: Arc, - _unified_api_version: UnifiedMappingApiVersion, - ) -> Result>> { - unimplemented!() - } - - async fn build_polling( - &self, - _chain: &Chain, - _deployment: DeploymentLocator, - _start_blocks: Vec, - _source_subgraph_stores: Vec>, - _subgraph_current_block: Option, - _filter: Arc>, - _unified_api_version: UnifiedMappingApiVersion, - ) -> Result>> { - unimplemented!("polling block stream is not support for substreams") - } -} diff --git a/chain/substreams/src/chain.rs b/chain/substreams/src/chain.rs deleted file mode 100644 index 0213d01a39e..00000000000 --- a/chain/substreams/src/chain.rs +++ /dev/null @@ -1,231 +0,0 @@ -use crate::block_ingestor::SubstreamsBlockIngestor; -use crate::{data_source::*, EntityChanges, TriggerData, TriggerFilter, TriggersAdapter}; -use anyhow::Error; -use async_trait::async_trait; -use graph::blockchain::client::ChainClient; -use graph::blockchain::{ - BasicBlockchainBuilder, BlockIngestor, BlockTime, EmptyNodeCapabilities, NoopDecoderHook, - NoopRuntimeAdapter, TriggerFilterWrapper, -}; -use graph::components::network_provider::ChainName; -use graph::components::store::{ChainHeadStore, DeploymentCursorTracker, SourceableStore}; -use graph::env::EnvVars; -use graph::prelude::{BlockHash, CheapClone, Entity, LoggerFactory, MetricsRegistry}; -use graph::schema::EntityKey; -use graph::{ - blockchain::{ - self, - block_stream::{BlockStream, BlockStreamBuilder, FirehoseCursor}, - BlockPtr, Blockchain, BlockchainKind, IngestorError, RuntimeAdapter as RuntimeAdapterTrait, - }, - components::store::DeploymentLocator, - data::subgraph::UnifiedMappingApiVersion, - prelude::BlockNumber, - slog::Logger, -}; - -use std::sync::Arc; - -// ParsedChanges are an internal representation of the equivalent operations defined on the -// graph-out format used by substreams. -// Unset serves as a sentinel value, if for some reason an unknown value is sent or the value -// was empty then it's probably an unintended behaviour. This code was moved here for performance -// reasons, but the validation is still performed during trigger processing so while Unset will -// very likely just indicate an error somewhere, as far as the stream is concerned we just pass -// that along and let the downstream components deal with it. -#[derive(Debug, Clone)] -pub enum ParsedChanges { - Unset, - Delete(EntityKey), - Upsert { key: EntityKey, entity: Entity }, -} - -#[derive(Default, Debug, Clone)] -pub struct Block { - pub hash: BlockHash, - pub number: BlockNumber, - pub changes: EntityChanges, - pub parsed_changes: Vec, -} - -impl blockchain::Block for Block { - fn ptr(&self) -> BlockPtr { - BlockPtr { - hash: self.hash.clone(), - number: self.number, - } - } - - fn parent_ptr(&self) -> Option { - None - } - - fn timestamp(&self) -> BlockTime { - BlockTime::NONE - } -} - -pub struct Chain { - chain_head_store: Arc, - block_stream_builder: Arc>, - chain_id: ChainName, - - pub(crate) logger_factory: LoggerFactory, - pub(crate) client: Arc>, - pub(crate) metrics_registry: Arc, -} - -impl Chain { - pub fn new( - logger_factory: LoggerFactory, - chain_client: Arc>, - metrics_registry: Arc, - chain_store: Arc, - block_stream_builder: Arc>, - chain_id: ChainName, - ) -> Self { - Self { - logger_factory, - client: chain_client, - metrics_registry, - chain_head_store: chain_store, - block_stream_builder, - chain_id, - } - } -} - -impl std::fmt::Debug for Chain { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "chain: substreams") - } -} - -#[async_trait] -impl Blockchain for Chain { - const KIND: BlockchainKind = BlockchainKind::Substreams; - - type Client = (); - type Block = Block; - type DataSource = DataSource; - type UnresolvedDataSource = UnresolvedDataSource; - - type DataSourceTemplate = NoopDataSourceTemplate; - type UnresolvedDataSourceTemplate = NoopDataSourceTemplate; - - /// Trigger data as parsed from the triggers adapter. - type TriggerData = TriggerData; - - /// Decoded trigger ready to be processed by the mapping. - /// New implementations should have this be the same as `TriggerData`. - type MappingTrigger = TriggerData; - - /// Trigger filter used as input to the triggers adapter. - type TriggerFilter = TriggerFilter; - - type NodeCapabilities = EmptyNodeCapabilities; - - type DecoderHook = NoopDecoderHook; - - fn triggers_adapter( - &self, - _log: &DeploymentLocator, - _capabilities: &Self::NodeCapabilities, - _unified_api_version: UnifiedMappingApiVersion, - ) -> Result>, Error> { - Ok(Arc::new(TriggersAdapter {})) - } - - async fn new_block_stream( - &self, - deployment: DeploymentLocator, - store: impl DeploymentCursorTracker, - _start_blocks: Vec, - _source_subgraph_stores: Vec>, - filter: Arc>, - _unified_api_version: UnifiedMappingApiVersion, - ) -> Result>, Error> { - self.block_stream_builder - .build_substreams( - self, - store.input_schema(), - deployment, - store.firehose_cursor(), - store.block_ptr(), - filter.chain_filter.clone(), - ) - .await - } - - fn is_refetch_block_required(&self) -> bool { - false - } - async fn refetch_firehose_block( - &self, - _logger: &Logger, - _cursor: FirehoseCursor, - ) -> Result { - unimplemented!("This chain does not support Dynamic Data Sources. is_refetch_block_required always returns false, this shouldn't be called.") - } - - async fn chain_head_ptr(&self) -> Result, Error> { - self.chain_head_store.cheap_clone().chain_head_ptr().await - } - - async fn block_pointer_from_number( - &self, - _logger: &Logger, - number: BlockNumber, - ) -> Result { - // This is the same thing TriggersAdapter does, not sure if it's going to work but - // we also don't yet have a good way of getting this value until we sort out the - // chain store. - // TODO(filipe): Fix this once the chain_store is correctly setup for substreams. - Ok(BlockPtr { - hash: BlockHash::from(vec![0xff; 32]), - number, - }) - } - async fn runtime( - &self, - ) -> anyhow::Result<(Arc>, Self::DecoderHook)> { - Ok((Arc::new(NoopRuntimeAdapter::default()), NoopDecoderHook)) - } - - fn chain_client(&self) -> Arc> { - self.client.clone() - } - - async fn block_ingestor(&self) -> anyhow::Result> { - Ok(Box::new(SubstreamsBlockIngestor::new( - self.chain_head_store.cheap_clone(), - self.client.cheap_clone(), - self.logger_factory - .component_logger("SubstreamsBlockIngestor", None), - self.chain_id.clone(), - self.metrics_registry.cheap_clone(), - ))) - } -} - -#[async_trait] -impl blockchain::BlockchainBuilder for BasicBlockchainBuilder { - async fn build(self, _config: &Arc) -> Chain { - let BasicBlockchainBuilder { - logger_factory, - name, - chain_head_store, - firehose_endpoints, - metrics_registry, - } = self; - - Chain { - chain_head_store, - block_stream_builder: Arc::new(crate::BlockStreamBuilder::new()), - logger_factory, - client: Arc::new(ChainClient::new_firehose(firehose_endpoints)), - metrics_registry, - chain_id: name, - } - } -} diff --git a/chain/substreams/src/codec.rs b/chain/substreams/src/codec.rs deleted file mode 100644 index 31781baa201..00000000000 --- a/chain/substreams/src/codec.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[rustfmt::skip] -#[path = "protobuf/substreams.entity.v1.rs"] -mod pbsubstreamsentity; - -pub use pbsubstreamsentity::*; diff --git a/chain/substreams/src/data_source.rs b/chain/substreams/src/data_source.rs deleted file mode 100644 index a30d92173c5..00000000000 --- a/chain/substreams/src/data_source.rs +++ /dev/null @@ -1,765 +0,0 @@ -use std::{collections::HashSet, sync::Arc}; - -use anyhow::{anyhow, Context, Error}; -use async_trait::async_trait; -use graph::{ - blockchain, - cheap_clone::CheapClone, - components::{ - link_resolver::{LinkResolver, LinkResolverContext}, - subgraph::InstanceDSTemplateInfo, - }, - data::subgraph::DeploymentHash, - prelude::{BlockNumber, Link}, - slog::Logger, -}; - -use prost::Message; -use serde::Deserialize; - -use crate::{chain::Chain, Block, TriggerData}; - -pub const SUBSTREAMS_KIND: &str = "substreams"; - -const DYNAMIC_DATA_SOURCE_ERROR: &str = "Substreams do not support dynamic data sources"; -const TEMPLATE_ERROR: &str = "Substreams do not support templates"; - -const ALLOWED_MAPPING_KIND: [&str; 1] = ["substreams/graph-entities"]; -const SUBSTREAMS_HANDLER_KIND: &str = "substreams"; -#[derive(Clone, Debug, PartialEq)] -/// Represents the DataSource portion of the manifest once it has been parsed -/// and the substream spkg has been downloaded + parsed. -pub struct DataSource { - pub kind: String, - pub network: Option, - pub name: String, - pub(crate) source: Source, - pub mapping: Mapping, - pub context: Arc>, - pub initial_block: Option, -} - -impl blockchain::DataSource for DataSource { - fn from_template_info( - _info: InstanceDSTemplateInfo, - _template: &graph::data_source::DataSourceTemplate, - ) -> Result { - Err(anyhow!("Substreams does not support templates")) - } - - fn address(&self) -> Option<&[u8]> { - None - } - - fn start_block(&self) -> BlockNumber { - self.initial_block.unwrap_or(0) - } - - fn end_block(&self) -> Option { - None - } - - fn name(&self) -> &str { - &self.name - } - - fn kind(&self) -> &str { - &self.kind - } - - fn network(&self) -> Option<&str> { - self.network.as_deref() - } - - fn context(&self) -> Arc> { - self.context.cheap_clone() - } - - fn creation_block(&self) -> Option { - None - } - - fn api_version(&self) -> semver::Version { - self.mapping.api_version.clone() - } - - fn runtime(&self) -> Option>> { - self.mapping.handler.as_ref().map(|h| h.runtime.clone()) - } - - fn handler_kinds(&self) -> HashSet<&str> { - // This is placeholder, substreams do not have a handler kind. - vec![SUBSTREAMS_HANDLER_KIND].into_iter().collect() - } - - // match_and_decode only seems to be used on the default trigger processor which substreams - // bypasses so it should be fine to leave it unimplemented. - fn match_and_decode( - &self, - _trigger: &TriggerData, - _block: &Arc, - _logger: &Logger, - ) -> Result>, Error> { - unimplemented!() - } - - fn is_duplicate_of(&self, _other: &Self) -> bool { - self == _other - } - - fn as_stored_dynamic_data_source(&self) -> graph::components::store::StoredDynamicDataSource { - unimplemented!("{}", DYNAMIC_DATA_SOURCE_ERROR) - } - - fn validate(&self, _: &semver::Version) -> Vec { - let mut errs = vec![]; - - if &self.kind != SUBSTREAMS_KIND { - errs.push(anyhow!( - "data source has invalid `kind`, expected {} but found {}", - SUBSTREAMS_KIND, - self.kind - )) - } - - if self.name.is_empty() { - errs.push(anyhow!("name cannot be empty")); - } - - if !ALLOWED_MAPPING_KIND.contains(&self.mapping.kind.as_str()) { - errs.push(anyhow!( - "mapping kind has to be one of {:?}, found {}", - ALLOWED_MAPPING_KIND, - self.mapping.kind - )) - } - - errs - } - - fn from_stored_dynamic_data_source( - _template: &::DataSourceTemplate, - _stored: graph::components::store::StoredDynamicDataSource, - ) -> Result { - Err(anyhow!(DYNAMIC_DATA_SOURCE_ERROR)) - } -} - -#[derive(Clone, Debug, Default, PartialEq)] -/// Module name comes from the manifest, package is the parsed spkg file. -pub struct Source { - pub module_name: String, - pub package: graph::substreams::Package, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Mapping { - pub api_version: semver::Version, - pub kind: String, - pub handler: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct MappingHandler { - pub handler: String, - pub runtime: Arc>, -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] -/// Raw representation of the data source for deserialization purposes. -pub struct UnresolvedDataSource { - pub kind: String, - pub network: Option, - pub name: String, - pub(crate) source: UnresolvedSource, - pub mapping: UnresolvedMapping, -} - -#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -/// Text api_version, before parsing and validation. -pub struct UnresolvedMapping { - pub api_version: String, - pub kind: String, - pub handler: Option, - pub file: Option, -} - -#[async_trait] -impl blockchain::UnresolvedDataSource for UnresolvedDataSource { - async fn resolve( - self, - deployment_hash: &DeploymentHash, - resolver: &Arc, - logger: &Logger, - _manifest_idx: u32, - _spec_version: &semver::Version, - ) -> Result { - let content = resolver - .cat( - &LinkResolverContext::new(deployment_hash, logger), - &self.source.package.file, - ) - .await?; - - let mut package = graph::substreams::Package::decode(content.as_ref())?; - - let module = match package.modules.as_mut() { - Some(modules) => modules - .modules - .iter_mut() - .find(|module| module.name == self.source.package.module_name) - .map(|module| { - if let Some(params) = self.source.package.params { - graph::substreams::patch_module_params(params, module); - } - module - }), - None => None, - }; - - let initial_block: Option = match module { - Some(module) => match &module.kind { - Some(graph::substreams::module::Kind::KindMap(_)) => Some(module.initial_block), - _ => { - return Err(anyhow!( - "Substreams module {} must be of 'map' kind", - module.name - )) - } - }, - None => { - return Err(anyhow!( - "Substreams module {} does not exist", - self.source.package.module_name - )) - } - }; - - let initial_block = - initial_block.map(|x| x.max(self.source.start_block.unwrap_or_default() as u64)); - - let initial_block: Option = initial_block - .map_or(Ok(None), |x: u64| TryInto::::try_into(x).map(Some)) - .map_err(anyhow::Error::from)?; - - let handler = match (self.mapping.handler, self.mapping.file) { - (Some(handler), Some(file)) => { - let module_bytes = resolver - .cat(&LinkResolverContext::new(deployment_hash, logger), &file) - .await - .with_context(|| format!("failed to resolve mapping {}", file.link))?; - - Some(MappingHandler { - handler, - runtime: Arc::new(module_bytes), - }) - } - _ => None, - }; - - Ok(DataSource { - kind: SUBSTREAMS_KIND.into(), - network: self.network, - name: self.name, - source: Source { - module_name: self.source.package.module_name, - package, - }, - mapping: Mapping { - api_version: semver::Version::parse(&self.mapping.api_version)?, - kind: self.mapping.kind, - handler, - }, - context: Arc::new(None), - initial_block, - }) - } -} - -#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -/// Source is a part of the manifest and this is needed for parsing. -pub struct UnresolvedSource { - #[serde(rename = "startBlock", default)] - start_block: Option, - package: UnresolvedPackage, -} - -#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -/// The unresolved Package section of the manifest. -pub struct UnresolvedPackage { - pub module_name: String, - pub file: Link, - pub params: Option, -} - -#[derive(Debug, Clone, Default, Deserialize)] -/// This is necessary for the Blockchain trait associated types, substreams do not support -/// data source templates so this is a noop and is not expected to be called. -pub struct NoopDataSourceTemplate {} - -impl blockchain::DataSourceTemplate for NoopDataSourceTemplate { - fn name(&self) -> &str { - unimplemented!("{}", TEMPLATE_ERROR); - } - - fn api_version(&self) -> semver::Version { - unimplemented!("{}", TEMPLATE_ERROR); - } - - fn runtime(&self) -> Option>> { - unimplemented!("{}", TEMPLATE_ERROR); - } - - fn manifest_idx(&self) -> u32 { - todo!() - } - - fn kind(&self) -> &str { - unimplemented!("{}", TEMPLATE_ERROR); - } -} - -#[async_trait] -impl blockchain::UnresolvedDataSourceTemplate for NoopDataSourceTemplate { - async fn resolve( - self, - _deployment_hash: &DeploymentHash, - _resolver: &Arc, - _logger: &Logger, - _manifest_idx: u32, - _spec_version: &semver::Version, - ) -> Result { - unimplemented!("{}", TEMPLATE_ERROR) - } -} - -#[cfg(test)] -mod test { - use std::{str::FromStr, sync::Arc}; - - use anyhow::Error; - use async_trait::async_trait; - use graph::{ - blockchain::{DataSource as _, UnresolvedDataSource as _}, - components::link_resolver::{LinkResolver, LinkResolverContext}, - data::subgraph::{DeploymentHash, LATEST_VERSION, SPEC_VERSION_1_2_0}, - prelude::{serde_yaml, JsonValueStream, Link}, - slog::{o, Discard, Logger}, - substreams::{ - module::{ - input::{Input, Params}, - Kind, KindMap, KindStore, - }, - Module, Modules, Package, - }, - }; - use prost::Message; - - use crate::{DataSource, Mapping, UnresolvedDataSource, UnresolvedMapping, SUBSTREAMS_KIND}; - - #[test] - fn parse_data_source() { - let ds: UnresolvedDataSource = serde_yaml::from_str(TEMPLATE_DATA_SOURCE).unwrap(); - let expected = UnresolvedDataSource { - kind: SUBSTREAMS_KIND.into(), - network: Some("mainnet".into()), - name: "Uniswap".into(), - source: crate::UnresolvedSource { - package: crate::UnresolvedPackage { - module_name: "output".into(), - file: Link { - link: "/ipfs/QmbHnhUFZa6qqqRyubUYhXntox1TCBxqryaBM1iNGqVJzT".into(), - }, - params: None, - }, - start_block: None, - }, - mapping: UnresolvedMapping { - api_version: "0.0.7".into(), - kind: "substreams/graph-entities".into(), - handler: None, - file: None, - }, - }; - assert_eq!(ds, expected); - } - - #[test] - fn parse_data_source_with_startblock() { - let ds: UnresolvedDataSource = - serde_yaml::from_str(TEMPLATE_DATA_SOURCE_WITH_START_BLOCK).unwrap(); - let expected = UnresolvedDataSource { - kind: SUBSTREAMS_KIND.into(), - network: Some("mainnet".into()), - name: "Uniswap".into(), - source: crate::UnresolvedSource { - package: crate::UnresolvedPackage { - module_name: "output".into(), - file: Link { - link: "/ipfs/QmbHnhUFZa6qqqRyubUYhXntox1TCBxqryaBM1iNGqVJzT".into(), - }, - params: None, - }, - start_block: Some(567), - }, - mapping: UnresolvedMapping { - api_version: "0.0.7".into(), - kind: "substreams/graph-entities".into(), - handler: None, - file: None, - }, - }; - assert_eq!(ds, expected); - } - - #[test] - fn parse_data_source_with_params() { - let ds: UnresolvedDataSource = - serde_yaml::from_str(TEMPLATE_DATA_SOURCE_WITH_PARAMS).unwrap(); - let expected = UnresolvedDataSource { - kind: SUBSTREAMS_KIND.into(), - network: Some("mainnet".into()), - name: "Uniswap".into(), - source: crate::UnresolvedSource { - package: crate::UnresolvedPackage { - module_name: "output".into(), - file: Link { - link: "/ipfs/QmbHnhUFZa6qqqRyubUYhXntox1TCBxqryaBM1iNGqVJzT".into(), - }, - params: Some("x\ny\n123\n".into()), - }, - start_block: None, - }, - mapping: UnresolvedMapping { - api_version: "0.0.7".into(), - kind: "substreams/graph-entities".into(), - handler: None, - file: None, - }, - }; - assert_eq!(ds, expected); - } - - #[graph::test] - async fn data_source_conversion() { - let ds: UnresolvedDataSource = serde_yaml::from_str(TEMPLATE_DATA_SOURCE).unwrap(); - let link_resolver: Arc = Arc::new(NoopLinkResolver {}); - let logger = Logger::root(Discard, o!()); - let ds: DataSource = ds - .resolve( - &DeploymentHash::default(), - &link_resolver, - &logger, - 0, - &SPEC_VERSION_1_2_0, - ) - .await - .unwrap(); - let expected = DataSource { - kind: SUBSTREAMS_KIND.into(), - network: Some("mainnet".into()), - name: "Uniswap".into(), - source: crate::Source { - module_name: "output".into(), - package: gen_package(), - }, - mapping: Mapping { - api_version: semver::Version::from_str("0.0.7").unwrap(), - kind: "substreams/graph-entities".into(), - handler: None, - }, - context: Arc::new(None), - initial_block: Some(123), - }; - assert_eq!(ds, expected); - } - - #[graph::test] - async fn data_source_conversion_override_params() { - let mut package = gen_package(); - let mut modules = package.modules.unwrap(); - modules.modules.get_mut(0).map(|module| { - module.inputs = vec![graph::substreams::module::Input { - input: Some(Input::Params(Params { - value: "x\ny\n123\n".into(), - })), - }] - }); - package.modules = Some(modules); - - let ds: UnresolvedDataSource = - serde_yaml::from_str(TEMPLATE_DATA_SOURCE_WITH_PARAMS).unwrap(); - let link_resolver: Arc = Arc::new(NoopLinkResolver {}); - let logger = Logger::root(Discard, o!()); - let ds: DataSource = ds - .resolve( - &DeploymentHash::default(), - &link_resolver, - &logger, - 0, - &SPEC_VERSION_1_2_0, - ) - .await - .unwrap(); - let expected = DataSource { - kind: SUBSTREAMS_KIND.into(), - network: Some("mainnet".into()), - name: "Uniswap".into(), - source: crate::Source { - module_name: "output".into(), - package, - }, - mapping: Mapping { - api_version: semver::Version::from_str("0.0.7").unwrap(), - kind: "substreams/graph-entities".into(), - handler: None, - }, - context: Arc::new(None), - initial_block: Some(123), - }; - assert_eq!(ds, expected); - } - - #[test] - fn data_source_validation() { - let mut ds = gen_data_source(); - assert_eq!(true, ds.validate(LATEST_VERSION).is_empty()); - - ds.network = None; - assert_eq!(true, ds.validate(LATEST_VERSION).is_empty()); - - ds.kind = "asdasd".into(); - ds.name = "".into(); - ds.mapping.kind = "asdasd".into(); - let errs: Vec = ds - .validate(LATEST_VERSION) - .into_iter() - .map(|e| e.to_string()) - .collect(); - assert_eq!( - errs, - vec![ - "data source has invalid `kind`, expected substreams but found asdasd", - "name cannot be empty", - "mapping kind has to be one of [\"substreams/graph-entities\"], found asdasd" - ] - ); - } - - #[test] - fn parse_data_source_with_maping() { - let ds: UnresolvedDataSource = - serde_yaml::from_str(TEMPLATE_DATA_SOURCE_WITH_MAPPING).unwrap(); - - let expected = UnresolvedDataSource { - kind: SUBSTREAMS_KIND.into(), - network: Some("mainnet".into()), - name: "Uniswap".into(), - source: crate::UnresolvedSource { - package: crate::UnresolvedPackage { - module_name: "output".into(), - file: Link { - link: "/ipfs/QmbHnhUFZa6qqqRyubUYhXntox1TCBxqryaBM1iNGqVJzT".into(), - }, - params: Some("x\ny\n123\n".into()), - }, - start_block: None, - }, - mapping: UnresolvedMapping { - api_version: "0.0.7".into(), - kind: "substreams/graph-entities".into(), - handler: Some("bananas".to_string()), - file: Some(Link { - link: "./src/mappings.ts".to_string(), - }), - }, - }; - assert_eq!(ds, expected); - } - - fn gen_package() -> Package { - Package { - proto_files: vec![], - version: 0, - modules: Some(Modules { - modules: vec![ - Module { - name: "output".into(), - initial_block: 123, - binary_entrypoint: "output".into(), - binary_index: 0, - kind: Some(Kind::KindMap(KindMap { - output_type: "proto".into(), - })), - block_filter: None, - inputs: vec![], - output: None, - }, - Module { - name: "store_mod".into(), - initial_block: 0, - binary_entrypoint: "store_mod".into(), - binary_index: 0, - kind: Some(Kind::KindStore(KindStore { - update_policy: 1, - value_type: "proto1".into(), - })), - block_filter: None, - inputs: vec![], - output: None, - }, - Module { - name: "map_mod".into(), - initial_block: 123456, - binary_entrypoint: "other2".into(), - binary_index: 0, - kind: Some(Kind::KindMap(KindMap { - output_type: "proto2".into(), - })), - block_filter: None, - inputs: vec![], - output: None, - }, - ], - binaries: vec![], - }), - module_meta: vec![], - package_meta: vec![], - sink_config: None, - network: "".into(), - sink_module: "".into(), - } - } - - fn gen_data_source() -> DataSource { - DataSource { - kind: SUBSTREAMS_KIND.into(), - network: Some("mainnet".into()), - name: "Uniswap".into(), - source: crate::Source { - module_name: "".to_string(), - package: gen_package(), - }, - mapping: Mapping { - api_version: semver::Version::from_str("0.0.7").unwrap(), - kind: "substreams/graph-entities".into(), - handler: None, - }, - context: Arc::new(None), - initial_block: None, - } - } - - const TEMPLATE_DATA_SOURCE: &str = r#" - kind: substreams - name: Uniswap - network: mainnet - source: - package: - moduleName: output - file: - /: /ipfs/QmbHnhUFZa6qqqRyubUYhXntox1TCBxqryaBM1iNGqVJzT - # This IPFs path would be generated from a local path at deploy time - mapping: - kind: substreams/graph-entities - apiVersion: 0.0.7 - "#; - - const TEMPLATE_DATA_SOURCE_WITH_START_BLOCK: &str = r#" - kind: substreams - name: Uniswap - network: mainnet - source: - startBlock: 567 - package: - moduleName: output - file: - /: /ipfs/QmbHnhUFZa6qqqRyubUYhXntox1TCBxqryaBM1iNGqVJzT - # This IPFs path would be generated from a local path at deploy time - mapping: - kind: substreams/graph-entities - apiVersion: 0.0.7 - "#; - - const TEMPLATE_DATA_SOURCE_WITH_MAPPING: &str = r#" - kind: substreams - name: Uniswap - network: mainnet - source: - package: - moduleName: output - file: - /: /ipfs/QmbHnhUFZa6qqqRyubUYhXntox1TCBxqryaBM1iNGqVJzT - # This IPFs path would be generated from a local path at deploy time - params: | - x - y - 123 - mapping: - kind: substreams/graph-entities - apiVersion: 0.0.7 - file: - /: ./src/mappings.ts - handler: bananas - "#; - - const TEMPLATE_DATA_SOURCE_WITH_PARAMS: &str = r#" - kind: substreams - name: Uniswap - network: mainnet - source: - package: - moduleName: output - file: - /: /ipfs/QmbHnhUFZa6qqqRyubUYhXntox1TCBxqryaBM1iNGqVJzT - # This IPFs path would be generated from a local path at deploy time - params: | - x - y - 123 - mapping: - kind: substreams/graph-entities - apiVersion: 0.0.7 - "#; - - #[derive(Debug)] - struct NoopLinkResolver {} - - #[async_trait] - impl LinkResolver for NoopLinkResolver { - fn with_timeout(&self, _timeout: std::time::Duration) -> Box { - unimplemented!() - } - - fn with_retries(&self) -> Box { - unimplemented!() - } - - fn for_manifest(&self, _manifest_path: &str) -> Result, Error> { - unimplemented!() - } - - async fn cat(&self, _ctx: &LinkResolverContext, _link: &Link) -> Result, Error> { - Ok(gen_package().encode_to_vec()) - } - - async fn get_block( - &self, - _ctx: &LinkResolverContext, - _link: &Link, - ) -> Result, Error> { - unimplemented!() - } - - async fn json_stream( - &self, - _ctx: &LinkResolverContext, - _link: &Link, - ) -> Result { - unimplemented!() - } - } -} diff --git a/chain/substreams/src/lib.rs b/chain/substreams/src/lib.rs deleted file mode 100644 index 664ceab6d65..00000000000 --- a/chain/substreams/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -mod block_stream; -mod chain; -mod codec; -mod data_source; -mod trigger; - -pub mod block_ingestor; -pub mod mapper; - -pub use crate::chain::Chain; -pub use block_stream::BlockStreamBuilder; -pub use chain::*; -pub use codec::EntityChanges; -pub use data_source::*; -pub use trigger::*; - -pub use codec::Field; diff --git a/chain/substreams/src/mapper.rs b/chain/substreams/src/mapper.rs deleted file mode 100644 index 78788186795..00000000000 --- a/chain/substreams/src/mapper.rs +++ /dev/null @@ -1,415 +0,0 @@ -use std::collections::HashMap; -use std::str::FromStr; - -use crate::codec::{entity_change, EntityChanges}; -use anyhow::{anyhow, Error}; -use async_trait::async_trait; -use graph::blockchain::block_stream::{ - BlockStreamError, BlockStreamEvent, BlockStreamMapper, BlockWithTriggers, FirehoseCursor, - SubstreamsError, -}; -use graph::blockchain::BlockTime; -use graph::data::store::scalar::{Bytes, Timestamp}; -use graph::data::store::IdType; -use graph::data::value::Word; -use graph::data_source::CausalityRegion; -use graph::prelude::{BigDecimal, BlockPtr}; -use graph::prelude::{BigInt, BlockHash, BlockNumber, Logger, Value}; -use graph::schema::InputSchema; -use graph::slog::error; -use graph::substreams::Clock; -use prost::Message; - -use crate::{Block, Chain, ParsedChanges, TriggerData}; - -// WasmBlockMapper will not perform any transformation to the block and cannot make assumptions -// about the block format. This mode just works a passthrough from the block stream to the subgraph -// mapping which will do the decoding and store actions. -pub struct WasmBlockMapper { - pub handler: String, -} - -#[async_trait] -impl BlockStreamMapper for WasmBlockMapper { - fn decode_block( - &self, - _output: Option<&[u8]>, - ) -> Result, BlockStreamError> { - unreachable!("WasmBlockMapper does not do block decoding") - } - - async fn block_with_triggers( - &self, - _logger: &Logger, - _block: crate::Block, - ) -> Result, BlockStreamError> { - unreachable!("WasmBlockMapper does not do trigger decoding") - } - - async fn handle_substreams_block( - &self, - logger: &Logger, - clock: Clock, - cursor: FirehoseCursor, - block: Vec, - ) -> Result, BlockStreamError> { - let Clock { - id, - number, - timestamp, - } = clock; - - let block_ptr = BlockPtr { - hash: BlockHash::from(id.into_bytes()), - number: BlockNumber::from(TryInto::::try_into(number).map_err(Error::from)?), - }; - - let block_data = block.into_boxed_slice(); - - // `timestamp` is an `Option`, but it should always be set - let timestamp = match timestamp { - None => { - error!(logger, - "Substream block is missing a timestamp"; - "cursor" => cursor.to_string(), - "number" => number, - ); - return Err(anyhow!( - "Substream block is missing a timestamp at cursor {cursor}, block number {number}" - )).map_err(BlockStreamError::from); - } - Some(ts) => BlockTime::since_epoch(ts.seconds, ts.nanos as u32), - }; - - Ok(BlockStreamEvent::ProcessWasmBlock( - block_ptr, - timestamp, - block_data, - self.handler.clone(), - cursor, - )) - } -} - -// Mapper will transform the proto content coming from substreams in the graph-out format -// into the internal Block representation. If schema is passed then additional transformation -// into from the substreams block representation is performed into the Entity model used by -// the store. If schema is None then only the original block is passed. This None should only -// be used for block ingestion where entity content is empty and gets discarded. -pub struct Mapper { - pub schema: Option, - // Block ingestors need the block to be returned so they can populate the cache - // block streams, however, can shave some time by just skipping. - pub skip_empty_blocks: bool, -} - -#[async_trait] -impl BlockStreamMapper for Mapper { - fn decode_block(&self, output: Option<&[u8]>) -> Result, BlockStreamError> { - let changes: EntityChanges = match output { - Some(msg) => Message::decode(msg).map_err(SubstreamsError::DecodingError)?, - None => EntityChanges { - entity_changes: [].to_vec(), - }, - }; - - let parsed_changes = match self.schema.as_ref() { - Some(schema) => parse_changes(&changes, schema)?, - None if self.skip_empty_blocks => return Ok(None), - None => vec![], - }; - - let hash = BlockHash::zero(); - let number = BlockNumber::MIN; - let block = Block { - hash, - number, - changes, - parsed_changes, - }; - - Ok(Some(block)) - } - - async fn block_with_triggers( - &self, - logger: &Logger, - block: Block, - ) -> Result, BlockStreamError> { - let mut triggers = vec![]; - if block.changes.entity_changes.len() >= 1 { - triggers.push(TriggerData {}); - } - - Ok(BlockWithTriggers::new(block, triggers, logger)) - } - - async fn handle_substreams_block( - &self, - logger: &Logger, - clock: Clock, - cursor: FirehoseCursor, - block: Vec, - ) -> Result, BlockStreamError> { - let block_number: BlockNumber = clock.number.try_into().map_err(Error::from)?; - let block_hash = clock.id.as_bytes().to_vec().into(); - - let block = self - .decode_block(Some(&block))? - .ok_or_else(|| anyhow!("expected block to not be empty"))?; - - let block = self.block_with_triggers(logger, block).await.map(|bt| { - let mut block = bt; - - block.block.number = block_number; - block.block.hash = block_hash; - block - })?; - - Ok(BlockStreamEvent::ProcessBlock(block, cursor)) - } -} - -fn parse_changes( - changes: &EntityChanges, - schema: &InputSchema, -) -> Result, SubstreamsError> { - let mut parsed_changes = vec![]; - for entity_change in changes.entity_changes.iter() { - let mut parsed_data: HashMap = HashMap::default(); - let entity_type = schema.entity_type(&entity_change.entity)?; - - // Make sure that the `entity_id` gets set to a value - // that is safe for roundtrips through the database. In - // particular, if the type of the id is `Bytes`, we have - // to make sure that the `entity_id` starts with `0x` as - // that will be what the key for such an entity have - // when it is read from the database. - // - // Needless to say, this is a very ugly hack, and the - // real fix is what's described in [this - // issue](https://github.com/graphprotocol/graph-node/issues/4663) - let entity_id: String = match entity_type.id_type()? { - IdType::String | IdType::Int8 => entity_change.id.clone(), - IdType::Bytes => { - if entity_change.id.starts_with("0x") { - entity_change.id.clone() - } else { - format!("0x{}", entity_change.id) - } - } - }; - // Substreams don't currently support offchain data - let key = entity_type.parse_key_in(Word::from(entity_id), CausalityRegion::ONCHAIN)?; - - let id = key.id_value(); - parsed_data.insert(Word::from("id"), id); - - let changes = match entity_change.operation() { - entity_change::Operation::Create | entity_change::Operation::Update => { - for field in entity_change.fields.iter() { - let new_value: &crate::codec::value::Typed = match &field.new_value { - Some(crate::codec::Value { - typed: Some(new_value), - }) => &new_value, - _ => continue, - }; - - let value: Value = decode_value(new_value)?; - *parsed_data - .entry(Word::from(field.name.as_str())) - .or_insert(Value::Null) = value; - } - let entity = schema.make_entity(parsed_data)?; - - ParsedChanges::Upsert { key, entity } - } - entity_change::Operation::Delete => ParsedChanges::Delete(key), - entity_change::Operation::Unset => ParsedChanges::Unset, - }; - parsed_changes.push(changes); - } - - Ok(parsed_changes) -} - -fn decode_value(value: &crate::codec::value::Typed) -> anyhow::Result { - use crate::codec::value::Typed; - use base64::prelude::*; - - match value { - Typed::Int32(new_value) => Ok(Value::Int(*new_value)), - - Typed::Bigdecimal(new_value) => BigDecimal::from_str(new_value) - .map(Value::BigDecimal) - .map_err(|err| anyhow::Error::from(err)), - - Typed::Bigint(new_value) => BigInt::from_str(new_value) - .map(Value::BigInt) - .map_err(|err| anyhow::Error::from(err)), - - Typed::String(new_value) => { - let mut string = new_value.clone(); - - // Strip null characters since they are not accepted by Postgres. - if string.contains('\u{0000}') { - string = string.replace('\u{0000}', ""); - } - Ok(Value::String(string)) - } - - Typed::Bytes(new_value) => BASE64_STANDARD - .decode(new_value) - .map(|bs| Value::Bytes(Bytes::from(bs))) - .map_err(|err| anyhow::Error::from(err)), - - Typed::Bool(new_value) => Ok(Value::Bool(*new_value)), - - Typed::Timestamp(new_value) => Timestamp::from_microseconds_since_epoch(*new_value) - .map(Value::Timestamp) - .map_err(|err| anyhow::Error::from(err)), - - Typed::Array(arr) => arr - .value - .iter() - .filter_map(|item| item.typed.as_ref().map(decode_value)) - .collect::>>() - .map(Value::List), - } -} - -#[cfg(test)] -mod test { - use std::{ops::Add, str::FromStr}; - - use super::decode_value; - use crate::codec::value::Typed; - use crate::codec::{Array, Value}; - use base64::prelude::*; - use graph::{ - data::store::scalar::{Bytes, Timestamp}, - prelude::{BigDecimal, BigInt, Value as GraphValue}, - }; - - #[test] - fn validate_substreams_field_types() { - struct Case { - name: String, - value: Value, - expected_value: GraphValue, - } - - let cases = vec![ - Case { - name: "string value".to_string(), - value: Value { - typed: Some(Typed::String( - "d4325ee72c39999e778a9908f5fb0803f78e30c441a5f2ce5c65eee0e0eba59d" - .to_string(), - )), - }, - expected_value: GraphValue::String( - "d4325ee72c39999e778a9908f5fb0803f78e30c441a5f2ce5c65eee0e0eba59d".to_string(), - ), - }, - Case { - name: "bytes value".to_string(), - value: Value { - typed: Some(Typed::Bytes( - BASE64_STANDARD.encode( - hex::decode( - "445247fe150195bd866516594e087e1728294aa831613f4d48b8ec618908519f", - ) - .unwrap(), - ) - .into_bytes(), - )), - }, - expected_value: GraphValue::Bytes( - Bytes::from_str( - "0x445247fe150195bd866516594e087e1728294aa831613f4d48b8ec618908519f", - ) - .unwrap(), - ), - }, - Case { - name: "int value for block".to_string(), - value: Value { - typed: Some(Typed::Int32(12369760)), - }, - expected_value: GraphValue::Int(12369760), - }, - Case { - name: "negative int value".to_string(), - value: Value { - typed: Some(Typed::Int32(-12369760)), - }, - expected_value: GraphValue::Int(-12369760), - }, - Case { - name: "big int".to_string(), - value: Value { - typed: Some(Typed::Bigint("123".to_string())), - }, - expected_value: GraphValue::BigInt(BigInt::from(123u64)), - }, - Case { - name: "big int > u64".to_string(), - value: Value { - typed: Some(Typed::Bigint( - BigInt::from(u64::MAX).add(BigInt::from(1)).to_string(), - )), - }, - expected_value: GraphValue::BigInt(BigInt::from(u64::MAX).add(BigInt::from(1))), - }, - Case { - name: "big decimal value".to_string(), - value: Value { - typed: Some(Typed::Bigdecimal("3133363633312e35".to_string())), - }, - expected_value: GraphValue::BigDecimal(BigDecimal::new( - BigInt::from(3133363633312u64), - 35, - )), - }, - Case { - name: "bool value".to_string(), - value: Value { - typed: Some(Typed::Bool(true)), - }, - expected_value: GraphValue::Bool(true), - }, - Case { - name: "timestamp value".to_string(), - value: Value { - typed: Some(Typed::Timestamp(1234565789)), - }, - expected_value: GraphValue::Timestamp(Timestamp::from_microseconds_since_epoch(1234565789).unwrap()), - }, - Case { - name: "string array".to_string(), - value: Value { - typed: Some(Typed::Array(Array { - value: vec![ - Value { - typed: Some(Typed::String("1".to_string())), - }, - Value { - typed: Some(Typed::String("2".to_string())), - }, - Value { - typed: Some(Typed::String("3".to_string())), - }, - ], - })), - }, - expected_value: GraphValue::List(vec!["1".into(), "2".into(), "3".into()]), - }, - ]; - - for case in cases.into_iter() { - let value: GraphValue = decode_value(&case.value.typed.unwrap()).unwrap(); - assert_eq!(case.expected_value, value, "failed case: {}", case.name) - } - } -} diff --git a/chain/substreams/src/protobuf/substreams.entity.v1.rs b/chain/substreams/src/protobuf/substreams.entity.v1.rs deleted file mode 100644 index 4077f281ad7..00000000000 --- a/chain/substreams/src/protobuf/substreams.entity.v1.rs +++ /dev/null @@ -1,107 +0,0 @@ -// This file is @generated by prost-build. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EntityChanges { - #[prost(message, repeated, tag = "5")] - pub entity_changes: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EntityChange { - #[prost(string, tag = "1")] - pub entity: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub id: ::prost::alloc::string::String, - #[prost(uint64, tag = "3")] - pub ordinal: u64, - #[prost(enumeration = "entity_change::Operation", tag = "4")] - pub operation: i32, - #[prost(message, repeated, tag = "5")] - pub fields: ::prost::alloc::vec::Vec, -} -/// Nested message and enum types in `EntityChange`. -pub mod entity_change { - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] - #[repr(i32)] - pub enum Operation { - /// Protobuf default should not be used, this is used so that the consume can ensure that the value was actually specified - Unset = 0, - Create = 1, - Update = 2, - Delete = 3, - } - impl Operation { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - Self::Unset => "UNSET", - Self::Create => "CREATE", - Self::Update => "UPDATE", - Self::Delete => "DELETE", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "UNSET" => Some(Self::Unset), - "CREATE" => Some(Self::Create), - "UPDATE" => Some(Self::Update), - "DELETE" => Some(Self::Delete), - _ => None, - } - } - } -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Value { - #[prost(oneof = "value::Typed", tags = "1, 2, 3, 4, 5, 6, 7, 10")] - pub typed: ::core::option::Option, -} -/// Nested message and enum types in `Value`. -pub mod value { - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Typed { - #[prost(int32, tag = "1")] - Int32(i32), - #[prost(string, tag = "2")] - Bigdecimal(::prost::alloc::string::String), - #[prost(string, tag = "3")] - Bigint(::prost::alloc::string::String), - #[prost(string, tag = "4")] - String(::prost::alloc::string::String), - #[prost(bytes, tag = "5")] - Bytes(::prost::alloc::vec::Vec), - #[prost(bool, tag = "6")] - Bool(bool), - /// reserved 8 to 9; // For future types - #[prost(int64, tag = "7")] - Timestamp(i64), - #[prost(message, tag = "10")] - Array(super::Array), - } -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Array { - #[prost(message, repeated, tag = "1")] - pub value: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Field { - #[prost(string, tag = "1")] - pub name: ::prost::alloc::string::String, - #[prost(message, optional, tag = "3")] - pub new_value: ::core::option::Option, - #[prost(message, optional, tag = "5")] - pub old_value: ::core::option::Option, -} diff --git a/chain/substreams/src/trigger.rs b/chain/substreams/src/trigger.rs deleted file mode 100644 index 6593a079970..00000000000 --- a/chain/substreams/src/trigger.rs +++ /dev/null @@ -1,255 +0,0 @@ -use anyhow::Error; -use async_trait::async_trait; -use graph::{ - blockchain::{ - self, block_stream::BlockWithTriggers, BlockPtr, EmptyNodeCapabilities, MappingTriggerTrait, - }, - components::{ - store::{DeploymentLocator, SubgraphFork}, - subgraph::{MappingError, ProofOfIndexingEvent, SharedProofOfIndexing}, - trigger_processor::HostedTrigger, - }, - prelude::{anyhow, BlockHash, BlockNumber, BlockState, CheapClone, RuntimeHostBuilder}, - slog::Logger, - substreams::Modules, -}; -use graph_runtime_wasm::module::ToAscPtr; -use std::{collections::BTreeSet, sync::Arc}; - -use crate::{Block, Chain, NoopDataSourceTemplate, ParsedChanges}; - -#[derive(Eq, PartialEq, PartialOrd, Ord, Debug)] -pub struct TriggerData {} - -impl MappingTriggerTrait for TriggerData { - fn error_context(&self) -> String { - "Failed to process substreams block".to_string() - } -} - -impl blockchain::TriggerData for TriggerData { - // TODO(filipe): Can this be improved with some data from the block? - fn error_context(&self) -> String { - "Failed to process substreams block".to_string() - } - - fn address_match(&self) -> Option<&[u8]> { - None - } -} - -#[async_trait] -impl ToAscPtr for TriggerData { - // substreams doesn't rely on wasm on the graph-node so this is not needed. - async fn to_asc_ptr( - self, - _heap: &mut H, - _gas: &graph::runtime::gas::GasCounter, - ) -> Result, graph::runtime::HostExportError> { - unimplemented!() - } -} - -#[derive(Debug, Clone, Default)] -pub struct TriggerFilter { - pub(crate) modules: Option, - pub(crate) module_name: String, - pub(crate) start_block: Option, - pub(crate) data_sources_len: u8, - // the handler to call for subgraph mappings, if this is set then the binary block content - // should be passed to the mappings. - pub(crate) mapping_handler: Option, -} - -#[cfg(debug_assertions)] -impl TriggerFilter { - pub fn modules(&self) -> &Option { - &self.modules - } - - pub fn module_name(&self) -> &str { - &self.module_name - } - - pub fn start_block(&self) -> &Option { - &self.start_block - } - - pub fn data_sources_len(&self) -> u8 { - self.data_sources_len - } -} - -// TriggerFilter should bypass all triggers and just rely on block since all the data received -// should already have been processed. -impl blockchain::TriggerFilter for TriggerFilter { - fn extend_with_template(&mut self, _data_source: impl Iterator) { - } - - /// this function is not safe to call multiple times, only one DataSource is supported for - /// - fn extend<'a>( - &mut self, - mut data_sources: impl Iterator + Clone, - ) { - let Self { - modules, - module_name, - start_block, - data_sources_len, - mapping_handler, - } = self; - - if *data_sources_len >= 1 { - return; - } - - if let Some(ds) = data_sources.next() { - *data_sources_len = 1; - *modules = ds.source.package.modules.clone(); - *module_name = ds.source.module_name.clone(); - *start_block = ds.initial_block; - *mapping_handler = ds.mapping.handler.as_ref().map(|h| h.handler.clone()); - } - } - - fn node_capabilities(&self) -> EmptyNodeCapabilities { - EmptyNodeCapabilities::default() - } - - fn to_firehose_filter(self) -> Vec { - unimplemented!("this should never be called for this type") - } -} - -pub struct TriggersAdapter {} - -#[async_trait] -impl blockchain::TriggersAdapter for TriggersAdapter { - async fn ancestor_block( - &self, - _ptr: BlockPtr, - _offset: BlockNumber, - _root: Option, - ) -> Result, Error> { - unimplemented!() - } - - async fn load_block_ptrs_by_numbers( - &self, - _logger: Logger, - _block_numbers: BTreeSet, - ) -> Result, Error> { - unimplemented!() - } - - async fn chain_head_ptr(&self) -> Result, Error> { - unimplemented!() - } - - async fn scan_triggers( - &self, - _from: BlockNumber, - _to: BlockNumber, - _filter: &TriggerFilter, - ) -> Result<(Vec>, BlockNumber), Error> { - unimplemented!() - } - - async fn triggers_in_block( - &self, - _logger: &Logger, - _block: Block, - _filter: &TriggerFilter, - ) -> Result, Error> { - unimplemented!() - } - - async fn is_on_main_chain(&self, _ptr: BlockPtr) -> Result { - unimplemented!() - } - - async fn parent_ptr(&self, block: &BlockPtr) -> Result, Error> { - // This seems to work for a lot of the firehose chains. - Ok(Some(BlockPtr { - hash: BlockHash::from(vec![0xff; 32]), - number: block.number.saturating_sub(1), - })) - } -} - -pub struct TriggerProcessor { - pub locator: DeploymentLocator, -} - -impl TriggerProcessor { - pub fn new(locator: DeploymentLocator) -> Self { - Self { locator } - } -} - -#[async_trait] -impl graph::prelude::TriggerProcessor for TriggerProcessor -where - T: RuntimeHostBuilder, -{ - async fn process_trigger<'a>( - &'a self, - logger: &Logger, - _: Vec>, - block: &Arc, - mut state: BlockState, - proof_of_indexing: &SharedProofOfIndexing, - causality_region: &str, - _debug_fork: &Option>, - _subgraph_metrics: &Arc, - _instrument: bool, - ) -> Result { - for parsed_change in block.parsed_changes.clone().into_iter() { - match parsed_change { - ParsedChanges::Unset => { - // Potentially an issue with the server side or - // we are running an outdated version. In either case we should abort. - return Err(MappingError::Unknown(anyhow!("Detected UNSET entity operation, either a server error or there's a new type of operation and we're running an outdated protobuf"))); - } - ParsedChanges::Upsert { key, entity } => { - proof_of_indexing.write_event( - &ProofOfIndexingEvent::SetEntity { - entity_type: key.entity_type.typename(), - id: &key.entity_id.to_string(), - data: &entity, - }, - causality_region, - logger, - ); - - state - .entity_cache - .set( - key, - entity, - block.number, - Some(&mut state.write_capacity_remaining), - ) - .await?; - } - ParsedChanges::Delete(entity_key) => { - let entity_type = entity_key.entity_type.cheap_clone(); - let id = entity_key.entity_id.clone(); - state.entity_cache.remove(entity_key); - - proof_of_indexing.write_event( - &ProofOfIndexingEvent::RemoveEntity { - entity_type: entity_type.typename(), - id: &id.to_string(), - }, - causality_region, - logger, - ); - } - } - } - - Ok(state) - } -} diff --git a/core/Cargo.toml b/core/Cargo.toml index 07c01a94d05..5d946ed1e6e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -9,7 +9,6 @@ bytes = "1.0" graph = { path = "../graph" } graph-chain-ethereum = { path = "../chain/ethereum" } graph-chain-near = { path = "../chain/near" } -graph-chain-substreams = { path = "../chain/substreams" } graph-runtime-wasm = { path = "../runtime/wasm" } serde_yaml = { workspace = true } tokio = { workspace = true } diff --git a/core/src/subgraph/instance_manager.rs b/core/src/subgraph/instance_manager.rs index 7706410a33b..7c8c0799660 100644 --- a/core/src/subgraph/instance_manager.rs +++ b/core/src/subgraph/instance_manager.rs @@ -122,23 +122,6 @@ where self.start_subgraph_inner(logger, loc, runner).await } - BlockchainKind::Substreams => { - let runner = instance_manager - .build_subgraph_runner::( - logger.clone(), - self.env_vars.cheap_clone(), - loc.cheap_clone(), - raw_manifest, - stop_block, - Box::new(graph_chain_substreams::TriggerProcessor::new( - loc.clone(), - )), - deployment_status_metric, - ) - .await?; - - self.start_subgraph_inner(logger, loc, runner).await - } } } }; diff --git a/core/src/subgraph/registrar.rs b/core/src/subgraph/registrar.rs index 234d43a35ae..f34df50ad88 100644 --- a/core/src/subgraph/registrar.rs +++ b/core/src/subgraph/registrar.rs @@ -336,25 +336,6 @@ where ) .await? } - BlockchainKind::Substreams => { - create_subgraph_version::( - &logger, - self.store.clone(), - self.chains.cheap_clone(), - name.clone(), - hash.cheap_clone(), - start_block_override, - graft_block_override, - raw, - node_id, - debug_fork, - self.version_switching_mode, - &resolver, - self.amp_client.cheap_clone(), - history_blocks, - ) - .await? - } }; debug!( diff --git a/graph/src/blockchain/mod.rs b/graph/src/blockchain/mod.rs index 5066f38ac54..7ae4669ce01 100644 --- a/graph/src/blockchain/mod.rs +++ b/graph/src/blockchain/mod.rs @@ -571,8 +571,6 @@ pub enum BlockchainKind { /// NEAR chains (Mainnet, Testnet) or chains that are compatible Near, - - Substreams, } impl fmt::Display for BlockchainKind { @@ -580,7 +578,6 @@ impl fmt::Display for BlockchainKind { let value = match self { BlockchainKind::Ethereum => "ethereum", BlockchainKind::Near => "near", - BlockchainKind::Substreams => "substreams", }; write!(f, "{}", value) } @@ -593,7 +590,6 @@ impl FromStr for BlockchainKind { match s { "ethereum" => Ok(BlockchainKind::Ethereum), "near" => Ok(BlockchainKind::Near), - "substreams" => Ok(BlockchainKind::Substreams), "subgraph" => Ok(BlockchainKind::Ethereum), // TODO(krishna): We should detect the blockchain kind from the source subgraph "amp" => Ok(BlockchainKind::Ethereum), // TODO: Maybe get this from the Amp server _ => Err(anyhow!("unknown blockchain kind {}", s)), diff --git a/node/Cargo.toml b/node/Cargo.toml index 63723442423..b60128772af 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -24,7 +24,6 @@ graph = { path = "../graph" } graph-core = { path = "../core" } graph-chain-ethereum = { path = "../chain/ethereum" } graph-chain-near = { path = "../chain/near" } -graph-chain-substreams = { path = "../chain/substreams" } graph-graphql = { path = "../graphql" } graph-server-http = { path = "../server/http" } graph-server-index-node = { path = "../server/index-node" } diff --git a/node/resources/tests/full_config.toml b/node/resources/tests/full_config.toml index 1f907539194..057e774d93e 100644 --- a/node/resources/tests/full_config.toml +++ b/node/resources/tests/full_config.toml @@ -49,7 +49,6 @@ provider = [ { label = "mainnet-0", url = "http://rpc.mainnet.io", features = ["archive", "traces"] }, { label = "mainnet-1", details = { type = "web3call", url = "http://rpc.mainnet.io", features = ["archive", "traces"] }}, { label = "firehose", details = { type = "firehose", url = "http://localhost:9000", features = [] }}, - { label = "substreams", details = { type = "substreams", url = "http://localhost:9000", features = [] }}, ] [chains.ropsten] diff --git a/node/src/chain.rs b/node/src/chain.rs index 543a0cd5cfb..26d915678f3 100644 --- a/node/src/chain.rs +++ b/node/src/chain.rs @@ -11,15 +11,14 @@ use ethereum::ProviderEthRpcMetrics; use graph::anyhow::bail; use graph::blockchain::client::ChainClient; use graph::blockchain::{ - BasicBlockchainBuilder, Blockchain, BlockchainBuilder as _, BlockchainKind, BlockchainMap, - ChainIdentifier, + BasicBlockchainBuilder, BlockchainBuilder as _, BlockchainKind, BlockchainMap, ChainIdentifier, }; use graph::cheap_clone::CheapClone; use graph::components::network_provider::ChainName; -use graph::components::store::{BlockStore as _, ChainHeadStore}; +use graph::components::store::BlockStore as _; use graph::endpoint::EndpointMetrics; use graph::env::{EnvVars, ENV_VARS}; -use graph::firehose::{FirehoseEndpoint, SubgraphLimit}; +use graph::firehose::FirehoseEndpoint; use graph::futures03::future::try_join_all; use graph::itertools::Itertools; use graph::log::factory::LoggerFactory; @@ -75,74 +74,6 @@ impl ChainFilter for OneChainFilter { } } -pub fn create_substreams_networks( - logger: Logger, - config: &Config, - endpoint_metrics: Arc, - chain_filter: &dyn ChainFilter, -) -> Vec { - debug!( - logger, - "Creating firehose networks [{} chains, ingestor {}]", - config.chains.chains.len(), - config.chains.ingestor, - ); - - let mut networks_by_kind: BTreeMap<(BlockchainKind, ChainName), Vec>> = - BTreeMap::new(); - - let filtered_chains = config - .chains - .chains - .iter() - .filter(|(name, _)| chain_filter.filter(name)); - - for (name, chain) in filtered_chains { - let name: ChainName = name.as_str().into(); - for provider in &chain.providers { - if let ProviderDetails::Substreams(ref firehose) = provider.details { - info!( - logger, - "Configuring substreams endpoint"; - "provider" => &provider.label, - "network" => &name.to_string(), - ); - - let parsed_networks = networks_by_kind - .entry((chain.protocol, name.clone())) - .or_insert_with(Vec::new); - - for _ in 0..firehose.conn_pool_size { - parsed_networks.push(Arc::new(FirehoseEndpoint::new( - // This label needs to be the original label so that the metrics - // can be deduped. - &provider.label, - &firehose.url, - firehose.token.clone(), - firehose.key.clone(), - firehose.filters_enabled(), - firehose.compression_enabled(), - SubgraphLimit::Unlimited, - endpoint_metrics.clone(), - true, - ))); - } - } - } - } - - networks_by_kind - .into_iter() - .map(|((kind, chain_id), endpoints)| { - AdapterConfiguration::Substreams(FirehoseAdapterConfig { - chain_id, - kind, - adapters: endpoints.into(), - }) - }) - .collect() -} - pub fn create_firehose_networks( logger: Logger, config: &Config, @@ -400,36 +331,6 @@ pub async fn networks_as_chains( } }; - async fn add_substreams( - networks: &Networks, - config: &Arc, - chain_id: ChainName, - blockchain_map: &mut BlockchainMap, - logger_factory: LoggerFactory, - chain_head_store: Arc, - metrics_registry: Arc, - ) { - let substreams_endpoints = networks.substreams_endpoints(chain_id.clone()); - if substreams_endpoints.len() == 0 { - return; - } - - blockchain_map.insert::( - chain_id.clone(), - Arc::new( - BasicBlockchainBuilder { - logger_factory: logger_factory.clone(), - name: chain_id.clone(), - chain_head_store, - metrics_registry: metrics_registry.clone(), - firehose_endpoints: substreams_endpoints, - } - .build(config) - .await, - ), - ); - } - match kind { BlockchainKind::Ethereum => { // polling interval is set per chain so if set all adapter configuration will have @@ -480,17 +381,6 @@ pub async fn networks_as_chains( blockchain_map .insert::(chain_id.clone(), Arc::new(chain)); - - add_substreams::( - networks, - config, - chain_id.clone(), - blockchain_map, - logger_factory.clone(), - chain_store, - metrics_registry.clone(), - ) - .await; } BlockchainKind::Near => { let firehose_endpoints = networks.firehose_endpoints(chain_id.clone()); @@ -508,34 +398,6 @@ pub async fn networks_as_chains( .await, ), ); - - add_substreams::( - networks, - config, - chain_id.clone(), - blockchain_map, - logger_factory.clone(), - chain_store, - metrics_registry.clone(), - ) - .await; - } - BlockchainKind::Substreams => { - let substreams_endpoints = networks.substreams_endpoints(chain_id.clone()); - blockchain_map.insert::( - chain_id.clone(), - Arc::new( - BasicBlockchainBuilder { - logger_factory: logger_factory.clone(), - name: chain_id.clone(), - chain_head_store: chain_store, - metrics_registry: metrics_registry.clone(), - firehose_endpoints: substreams_endpoints, - } - .build(config) - .await, - ), - ); } } } diff --git a/node/src/config.rs b/node/src/config.rs index db2a5c203e9..7d21d10af77 100644 --- a/node/src/config.rs +++ b/node/src/config.rs @@ -568,30 +568,6 @@ impl Chain { provider.validate()? } - if !matches!(self.protocol, BlockchainKind::Substreams) { - let has_only_substreams_providers = self - .providers - .iter() - .all(|provider| matches!(provider.details, ProviderDetails::Substreams(_))); - if has_only_substreams_providers { - bail!( - "{} protocol requires an rpc or firehose endpoint defined", - self.protocol - ); - } - } - - // When using substreams protocol, only substreams endpoints are allowed - if matches!(self.protocol, BlockchainKind::Substreams) { - let has_non_substreams_providers = self - .providers - .iter() - .any(|provider| !matches!(provider.details, ProviderDetails::Substreams(_))); - if has_non_substreams_providers { - bail!("Substreams protocol only supports substreams providers"); - } - } - Ok(()) } } @@ -628,7 +604,6 @@ pub struct Provider { pub enum ProviderDetails { Firehose(FirehoseProvider), Web3(Web3Provider), - Substreams(FirehoseProvider), Web3Call(Web3Provider), } @@ -747,8 +722,7 @@ impl Provider { validate_name(&self.label).context("illegal provider name")?; match self.details { - ProviderDetails::Firehose(ref mut firehose) - | ProviderDetails::Substreams(ref mut firehose) => { + ProviderDetails::Firehose(ref mut firehose) => { firehose.url = shellexpand::env(&firehose.url)?.into_owned(); // A Firehose url must be a valid Uri since gRPC library we use (Tonic) @@ -904,10 +878,7 @@ impl<'de> Deserialize<'de> for Provider { } match v { - ProviderDetails::Firehose(ref mut firehose) - | ProviderDetails::Substreams(ref mut firehose) => { - firehose.rules = nodes - } + ProviderDetails::Firehose(ref mut firehose) => firehose.rules = nodes, _ => {} } @@ -1393,47 +1364,6 @@ mod tests { ); } - #[test] - fn fails_if_non_substreams_provider_for_substreams_protocol() { - let mut actual = toml::from_str::( - r#" - ingestor = "block_ingestor_node" - [mainnet] - shard = "primary" - protocol = "substreams" - provider = [ - { label = "firehose", details = { type = "firehose", url = "http://127.0.0.1:8888", token = "TOKEN", features = ["filters"] }}, - ] - "#, - ) - .unwrap(); - let err = actual.validate().unwrap_err().to_string(); - - assert!(err.contains("only supports substreams providers"), "{err}"); - } - - #[test] - fn fails_if_only_substreams_provider_for_non_substreams_protocol() { - let mut actual = toml::from_str::( - r#" - ingestor = "block_ingestor_node" - [mainnet] - shard = "primary" - protocol = "ethereum" - provider = [ - { label = "firehose", details = { type = "substreams", url = "http://127.0.0.1:8888", token = "TOKEN", features = ["filters"] }}, - ] - "#, - ) - .unwrap(); - let err = actual.validate().unwrap_err().to_string(); - - assert!( - err.contains("ethereum protocol requires an rpc or firehose endpoint defined"), - "{err}" - ); - } - #[test] fn it_works_on_new_web3_provider_from_toml() { let actual = toml::from_str( @@ -1532,54 +1462,12 @@ mod tests { } #[test] - fn it_works_on_substreams_provider_from_toml() { - let actual = toml::from_str( + fn it_fails_for_substreams() { + let _actual: Result = toml::from_str( r#" label = "bananas" details = { type = "substreams", url = "http://localhost:9000", features = [] } "#, - ) - .unwrap(); - - assert_eq!( - Provider { - label: "bananas".to_owned(), - details: ProviderDetails::Substreams(FirehoseProvider { - url: "http://localhost:9000".to_owned(), - token: None, - key: None, - features: BTreeSet::new(), - conn_pool_size: 20, - rules: vec![], - }), - }, - actual - ); - } - - #[test] - fn it_works_on_substreams_provider_from_toml_with_api_key() { - let actual = toml::from_str( - r#" - label = "authed" - details = { type = "substreams", url = "http://localhost:9000", key = "KEY", features = [] } - "#, - ) - .unwrap(); - - assert_eq!( - Provider { - label: "authed".to_owned(), - details: ProviderDetails::Substreams(FirehoseProvider { - url: "http://localhost:9000".to_owned(), - token: None, - key: Some("KEY".to_owned()), - features: BTreeSet::new(), - conn_pool_size: 20, - rules: vec![], - }), - }, - actual ); } @@ -1649,123 +1537,6 @@ mod tests { assert! { actual.validate().is_ok()}; } - #[test] - fn it_errors_on_firehose_provider_with_high_limit() { - let mut actual = toml::from_str( - r#" - label = "substreams" - details = { type = "substreams", url = "http://localhost:9000" } - match = [ - { name = "some_node_.*", limit = 101 }, - { name = "other_node_.*", limit = 0 } ] - "#, - ) - .unwrap(); - - assert_eq!( - Provider { - label: "substreams".to_owned(), - details: ProviderDetails::Substreams(FirehoseProvider { - url: "http://localhost:9000".to_owned(), - token: None, - key: None, - features: BTreeSet::new(), - conn_pool_size: 20, - rules: vec![ - Web3Rule { - name: Regex::new("some_node_.*").unwrap(), - limit: 101, - }, - Web3Rule { - name: Regex::new("other_node_.*").unwrap(), - limit: 0, - } - ], - }), - }, - actual - ); - assert! { actual.validate().is_err()}; - } - - #[test] - fn it_works_on_new_substreams_provider_with_doc_example_match() { - let mut actual = toml::from_str( - r#" - label = "substreams" - details = { type = "substreams", url = "http://localhost:9000" } - match = [ - { name = "some_node_.*", limit = 10 }, - { name = "other_node_.*", limit = 0 } ] - "#, - ) - .unwrap(); - - assert_eq!( - Provider { - label: "substreams".to_owned(), - details: ProviderDetails::Substreams(FirehoseProvider { - url: "http://localhost:9000".to_owned(), - token: None, - key: None, - features: BTreeSet::new(), - conn_pool_size: 20, - rules: vec![ - Web3Rule { - name: Regex::new("some_node_.*").unwrap(), - limit: 10, - }, - Web3Rule { - name: Regex::new("other_node_.*").unwrap(), - limit: 0, - } - ], - }), - }, - actual - ); - assert! { actual.validate().is_ok()}; - } - - #[test] - fn it_errors_on_substreams_provider_with_high_limit() { - let mut actual = toml::from_str( - r#" - label = "substreams" - details = { type = "substreams", url = "http://localhost:9000" } - match = [ - { name = "some_node_.*", limit = 101 }, - { name = "other_node_.*", limit = 0 } ] - "#, - ) - .unwrap(); - - assert_eq!( - Provider { - label: "substreams".to_owned(), - details: ProviderDetails::Substreams(FirehoseProvider { - url: "http://localhost:9000".to_owned(), - token: None, - key: None, - features: BTreeSet::new(), - conn_pool_size: 20, - rules: vec![ - Web3Rule { - name: Regex::new("some_node_.*").unwrap(), - limit: 101, - }, - Web3Rule { - name: Regex::new("other_node_.*").unwrap(), - limit: 0, - } - ], - }), - }, - actual - ); - assert! { actual.validate().is_err()}; - } - #[test] fn it_works_on_new_firehose_provider_from_toml_unsupported_features() { let actual = toml::from_str::( diff --git a/node/src/network_setup.rs b/node/src/network_setup.rs index 63cfe8097b4..b9450eb80b7 100644 --- a/node/src/network_setup.rs +++ b/node/src/network_setup.rs @@ -30,8 +30,8 @@ use graph_store_postgres::{BlockStore, ChainHeadUpdateListener}; use std::{any::Any, cmp::Ordering, sync::Arc, time::Duration}; use crate::chain::{ - create_ethereum_networks, create_firehose_networks, create_substreams_networks, - networks_as_chains, AnyChainFilter, ChainFilter, OneChainFilter, + create_ethereum_networks, create_firehose_networks, networks_as_chains, AnyChainFilter, + ChainFilter, OneChainFilter, }; #[derive(Debug, Clone)] @@ -209,17 +209,7 @@ impl Networks { endpoint_metrics.cheap_clone(), chain_filter, ); - let substreams = create_substreams_networks( - logger.cheap_clone(), - &config, - endpoint_metrics, - chain_filter, - ); - let adapters: Vec<_> = eth - .into_iter() - .chain(firehose.into_iter()) - .chain(substreams.into_iter()) - .collect(); + let adapters: Vec<_> = eth.into_iter().chain(firehose.into_iter()).collect(); Ok(Networks::new(&logger, adapters, provider_checks)) } @@ -379,21 +369,9 @@ impl Networks { BlockchainKind::Near => { block_ingestor::(logger, id, chain, &mut res).await? } - BlockchainKind::Substreams => {} } } - // substreams networks that also have other types of chain(rpc or firehose), will have - // block ingestors already running. - let visited: Vec<_> = res.iter().map(|b| b.network_name()).collect(); - - for ((_, id), chain) in blockchain_map - .iter() - .filter(|((kind, id), _)| BlockchainKind::Substreams.eq(&kind) && !visited.contains(id)) - { - block_ingestor::(logger, id, chain, &mut res).await? - } - Ok(res) } diff --git a/server/index-node/Cargo.toml b/server/index-node/Cargo.toml index 103cba19f96..9672f657e4a 100644 --- a/server/index-node/Cargo.toml +++ b/server/index-node/Cargo.toml @@ -10,5 +10,4 @@ graph = { path = "../../graph" } graph-graphql = { path = "../../graphql" } graph-chain-ethereum = { path = "../../chain/ethereum" } graph-chain-near = { path = "../../chain/near" } -graph-chain-substreams = { path = "../../chain/substreams" } git-testament = "0.2.6" diff --git a/server/index-node/src/resolver.rs b/server/index-node/src/resolver.rs index 76dd25414d0..5714a128c77 100644 --- a/server/index-node/src/resolver.rs +++ b/server/index-node/src/resolver.rs @@ -562,24 +562,6 @@ where ) .await? } - BlockchainKind::Substreams => { - let unvalidated_subgraph_manifest = - UnvalidatedSubgraphManifest::::resolve( - deployment_hash.clone(), - raw_yaml, - &self.link_resolver, - self.amp_client.cheap_clone(), - &self.logger, - max_spec_version, - ) - .await?; - - Self::validate_and_extract_features( - &self.store.subgraph_store(), - unvalidated_subgraph_manifest, - ) - .await? - } }; Ok(result) @@ -694,7 +676,7 @@ where // type. match BlockchainKind::Ethereum { // Note: we don't actually care about substreams here. - BlockchainKind::Substreams | BlockchainKind::Ethereum | BlockchainKind::Near => (), + BlockchainKind::Ethereum | BlockchainKind::Near => (), } // The given network does not exist. diff --git a/tests/Cargo.toml b/tests/Cargo.toml index e61cab6b660..737125a5533 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -10,7 +10,6 @@ async-stream = "0.3.6" async-trait = { workspace = true } graph = { path = "../graph" } graph-chain-ethereum = { path = "../chain/ethereum" } -graph-chain-substreams= {path = "../chain/substreams"} graph-node = { path = "../node" } graph-core = { path = "../core" } graph-graphql = { path = "../graphql" } diff --git a/tests/src/fixture/mod.rs b/tests/src/fixture/mod.rs index a965bab3c23..cfa6c41b019 100644 --- a/tests/src/fixture/mod.rs +++ b/tests/src/fixture/mod.rs @@ -1,5 +1,4 @@ pub mod ethereum; -pub mod substreams; use std::collections::{BTreeSet, HashMap}; use std::marker::PhantomData; @@ -135,19 +134,6 @@ impl TestChainTrait for TestChain { } } -pub struct TestChainSubstreams { - pub chain: Arc, - pub block_stream_builder: Arc, -} - -impl TestChainTrait for TestChainSubstreams { - fn set_block_stream(&self, _blocks: Vec>) {} - - fn chain(&self) -> Arc { - self.chain.clone() - } -} - pub trait TestChainTrait { fn set_block_stream(&self, blocks: Vec>); @@ -230,37 +216,6 @@ impl TestContext { .unwrap() } - pub async fn runner_substreams( - &self, - stop_block: BlockPtr, - ) -> graph_core::subgraph::SubgraphRunner< - graph_chain_substreams::Chain, - RuntimeHostBuilder, - > { - let (logger, deployment, raw) = self.get_runner_context().await; - let tp: Box> = Box::new( - graph_chain_substreams::TriggerProcessor::new(deployment.clone()), - ); - - let deployment_status_metric = self - .instance_manager - .new_deployment_status_metric(&deployment); - - self.instance_manager - .build_subgraph_runner_inner( - logger, - self.env_vars.cheap_clone(), - deployment, - raw, - Some(stop_block.block_number()), - tp, - deployment_status_metric, - true, - ) - .await - .unwrap() - } - pub async fn get_runner_context(&self) -> (Logger, DeploymentLocator, serde_yaml::Mapping) { let logger = self.logger.cheap_clone(); let deployment = self.deployment.cheap_clone(); diff --git a/tests/src/fixture/substreams.rs b/tests/src/fixture/substreams.rs deleted file mode 100644 index f94fdfa95ec..00000000000 --- a/tests/src/fixture/substreams.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::sync::Arc; - -use graph::{blockchain::client::ChainClient, components::network_provider::ChainName}; - -use super::{CommonChainConfig, Stores, TestChainSubstreams}; - -pub async fn chain(test_name: &str, stores: &Stores) -> TestChainSubstreams { - let CommonChainConfig { - logger_factory, - mock_registry, - chain_store, - firehose_endpoints, - .. - } = CommonChainConfig::new(test_name, stores).await; - - let block_stream_builder = Arc::new(graph_chain_substreams::BlockStreamBuilder::new()); - let client = Arc::new(ChainClient::::new_firehose( - firehose_endpoints, - )); - - let chain = Arc::new(graph_chain_substreams::Chain::new( - logger_factory, - client, - mock_registry, - chain_store, - block_stream_builder.clone(), - ChainName::from("test-chain"), - )); - - TestChainSubstreams { - chain, - block_stream_builder, - } -} diff --git a/tests/tests/runner_tests.rs b/tests/tests/runner_tests.rs index 6e3b7d80b0c..f35df89ce2e 100644 --- a/tests/tests/runner_tests.rs +++ b/tests/tests/runner_tests.rs @@ -23,7 +23,6 @@ use graph_tests::fixture::ethereum::{ push_test_polling_trigger, }; -use graph_tests::fixture::substreams::chain as substreams_chain; use graph_tests::fixture::{ self, test_ptr, test_ptr_reorged, MockAdapterSelector, NoopAdapterSelector, TestChainTrait, TestContext, TestInfo, @@ -438,37 +437,6 @@ async fn derived_loaders() { ); } -// This PR https://github.com/graphprotocol/graph-node/pull/4787 -// changed the way TriggerFilters were built -// A bug was introduced in the PR which resulted in filters for substreams not being included -// This test tests that the TriggerFilter is built correctly for substreams -#[graph::test] -async fn substreams_trigger_filter_construction() -> anyhow::Result<()> { - let RunnerTestRecipe { stores, test_info } = - RunnerTestRecipe::new("substreams", "substreams").await; - - let chain = substreams_chain(&test_info.test_name, &stores).await; - let ctx = fixture::setup(&test_info, &stores, &chain, None, None).await; - - let runner = ctx.runner_substreams(test_ptr(0)).await; - let filter = runner.build_filter_for_test(); - - assert_eq!(filter.chain_filter.module_name(), "graph_out"); - assert_eq!( - filter - .chain_filter - .modules() - .as_ref() - .unwrap() - .modules - .len(), - 2 - ); - assert_eq!(filter.chain_filter.start_block().unwrap(), 0); - assert_eq!(filter.chain_filter.data_sources_len(), 1); - Ok(()) -} - #[graph::test] async fn end_block() -> anyhow::Result<()> { let RunnerTestRecipe { stores, test_info } = From fcbb739b79cbec8cc9afba9ca1e77e1ff12332aa Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 6 Jan 2026 17:15:59 -0800 Subject: [PATCH 02/13] all: Remove even more substreams code --- Cargo.lock | 115 +-- Cargo.toml | 3 - chain/ethereum/examples/firehose.rs | 1 - chain/ethereum/src/chain.rs | 24 - chain/near/Cargo.toml | 3 +- chain/near/src/adapter.rs | 121 +-- chain/near/src/chain.rs | 122 +-- graph/src/blockchain/block_stream.rs | 113 --- graph/src/blockchain/builder.rs | 3 +- graph/src/blockchain/mod.rs | 1 - .../src/blockchain/substreams_block_stream.rs | 433 -------- graph/src/env/mod.rs | 8 - graph/src/firehose/endpoints.rs | 85 +- graph/src/lib.rs | 4 - graph/src/substreams/codec.rs | 5 - graph/src/substreams/mod.rs | 20 - graph/src/substreams/sf.substreams.v1.rs | 304 ------ graph/src/substreams_rpc/codec.rs | 5 - graph/src/substreams_rpc/mod.rs | 3 - graph/src/substreams_rpc/sf.firehose.v2.rs | 896 ----------------- .../substreams_rpc/sf.substreams.rpc.v2.rs | 946 ------------------ node/src/chain.rs | 3 +- substreams/substreams-head-tracker/Cargo.lock | 583 ----------- substreams/substreams-head-tracker/Cargo.toml | 8 - substreams/substreams-head-tracker/Makefile | 15 - .../rust-toolchain.toml | 2 - substreams/substreams-head-tracker/src/lib.rs | 19 - .../substreams-head-tracker-v1.0.0.spkg | Bin 89935 -> 0 bytes .../substreams-head-tracker/substreams.yaml | 17 - .../substreams-trigger-filter/Cargo.lock | 498 --------- .../substreams-trigger-filter/Cargo.toml | 21 - substreams/substreams-trigger-filter/Makefile | 35 - substreams/substreams-trigger-filter/build.rs | 12 - .../substreams-trigger-filter/bun.lockb | Bin 197771 -> 0 bytes .../substreams-trigger-filter/package.json | 1 - .../proto/near.proto | 521 ---------- .../proto/receipts.proto | 15 - .../rust-toolchain.toml | 2 - .../substreams-trigger-filter/schema.graphql | 4 - .../substreams-trigger-filter/src/lib.rs | 99 -- .../substreams-trigger-filter/src/pb/mod.rs | 8 - .../src/pb/receipts.v1.rs | 16 - .../substreams-trigger-filter/subgraph.yaml | 16 - .../substreams-trigger-filter-v0.1.0.spkg | Bin 510162 -> 0 bytes .../substreams-trigger-filter/substreams.yaml | 37 - substreams/trigger-filters/Cargo.toml | 7 - substreams/trigger-filters/src/lib.rs | 80 -- tests/src/fixture/mod.rs | 26 - 48 files changed, 12 insertions(+), 5248 deletions(-) delete mode 100644 graph/src/blockchain/substreams_block_stream.rs delete mode 100644 graph/src/substreams/codec.rs delete mode 100644 graph/src/substreams/mod.rs delete mode 100644 graph/src/substreams/sf.substreams.v1.rs delete mode 100644 graph/src/substreams_rpc/codec.rs delete mode 100644 graph/src/substreams_rpc/mod.rs delete mode 100644 graph/src/substreams_rpc/sf.firehose.v2.rs delete mode 100644 graph/src/substreams_rpc/sf.substreams.rpc.v2.rs delete mode 100755 substreams/substreams-head-tracker/Cargo.lock delete mode 100755 substreams/substreams-head-tracker/Cargo.toml delete mode 100755 substreams/substreams-head-tracker/Makefile delete mode 100755 substreams/substreams-head-tracker/rust-toolchain.toml delete mode 100644 substreams/substreams-head-tracker/src/lib.rs delete mode 100644 substreams/substreams-head-tracker/substreams-head-tracker-v1.0.0.spkg delete mode 100755 substreams/substreams-head-tracker/substreams.yaml delete mode 100755 substreams/substreams-trigger-filter/Cargo.lock delete mode 100644 substreams/substreams-trigger-filter/Cargo.toml delete mode 100755 substreams/substreams-trigger-filter/Makefile delete mode 100644 substreams/substreams-trigger-filter/build.rs delete mode 100755 substreams/substreams-trigger-filter/bun.lockb delete mode 100644 substreams/substreams-trigger-filter/package.json delete mode 100644 substreams/substreams-trigger-filter/proto/near.proto delete mode 100755 substreams/substreams-trigger-filter/proto/receipts.proto delete mode 100755 substreams/substreams-trigger-filter/rust-toolchain.toml delete mode 100644 substreams/substreams-trigger-filter/schema.graphql delete mode 100755 substreams/substreams-trigger-filter/src/lib.rs delete mode 100755 substreams/substreams-trigger-filter/src/pb/mod.rs delete mode 100644 substreams/substreams-trigger-filter/src/pb/receipts.v1.rs delete mode 100644 substreams/substreams-trigger-filter/subgraph.yaml delete mode 100644 substreams/substreams-trigger-filter/substreams-trigger-filter-v0.1.0.spkg delete mode 100755 substreams/substreams-trigger-filter/substreams.yaml delete mode 100644 substreams/trigger-filters/Cargo.toml delete mode 100644 substreams/trigger-filters/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 0793210303d..47706be430d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1368,7 +1368,7 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.0", + "unicode-width", "windows-sys 0.59.0", ] @@ -2846,7 +2846,7 @@ dependencies = [ "graphql-parser", "half", "hex", - "hex-literal 1.1.0", + "hex-literal", "http 0.2.12", "http 1.4.0", "http-body-util", @@ -2955,7 +2955,6 @@ dependencies = [ "serde", "tokio", "tonic-build", - "trigger-filters", ] [[package]] @@ -3452,12 +3451,6 @@ dependencies = [ "arrayvec 0.7.4", ] -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - [[package]] name = "hex-literal" version = "1.1.0" @@ -4851,15 +4844,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "pad" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" -dependencies = [ - "unicode-width 0.1.13", -] - [[package]] name = "parity-scale-codec" version = "3.6.12" @@ -6666,84 +6650,6 @@ dependencies = [ "syn 2.0.106", ] -[[package]] -name = "substreams" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb63116b90d4c174114fb237a8916dd995c939874f7576333990a44d78b642a" -dependencies = [ - "anyhow", - "bigdecimal 0.3.1", - "hex", - "hex-literal 0.3.4", - "num-bigint 0.4.6", - "num-integer", - "num-traits", - "pad", - "pest", - "pest_derive", - "prost", - "prost-build", - "prost-types", - "substreams-macro", - "thiserror 1.0.61", -] - -[[package]] -name = "substreams-entity-change" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0587b8d5dd7bffb0415d544c31e742c4cabdb81bbe9a3abfffff125185e4e9e8" -dependencies = [ - "base64 0.13.1", - "prost", - "prost-types", - "substreams", -] - -[[package]] -name = "substreams-head-tracker" -version = "0.36.0" - -[[package]] -name = "substreams-macro" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36f36e9da94db29f49daf3ab6b47b529b57c43fc5d58bc35b160aaad1a7233f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "thiserror 1.0.61", -] - -[[package]] -name = "substreams-near-core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ef8a763c5a5604b16f4898ab75d39494ef785c457aaca1fd7761b299f40fbf" -dependencies = [ - "bs58 0.4.0", - "getrandom 0.2.15", - "hex", - "prost", - "prost-build", - "prost-types", -] - -[[package]] -name = "substreams-trigger-filter" -version = "0.36.0" -dependencies = [ - "hex", - "prost", - "substreams", - "substreams-entity-change", - "substreams-near-core", - "tonic-build", - "trigger-filters", -] - [[package]] name = "subtle" version = "2.6.1" @@ -6904,7 +6810,7 @@ dependencies = [ "graph-node", "graph-store-postgres", "hex", - "hex-literal 1.1.0", + "hex-literal", "lazy_static", "pretty_assertions", "prost-types", @@ -7503,13 +7409,6 @@ dependencies = [ "syn 2.0.106", ] -[[package]] -name = "trigger-filters" -version = "0.36.0" -dependencies = [ - "anyhow", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -7617,12 +7516,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" -[[package]] -name = "unicode-width" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" - [[package]] name = "unicode-width" version = "0.2.0" @@ -8193,7 +8086,7 @@ dependencies = [ "bumpalo", "leb128fmt", "memchr", - "unicode-width 0.2.0", + "unicode-width", "wasm-encoder 0.244.0", ] diff --git a/Cargo.toml b/Cargo.toml index 020bb9bb117..9097b9b399d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,9 +20,6 @@ members = [ "server/metrics", "store/postgres", "store/test-store", - "substreams/substreams-head-tracker", - "substreams/substreams-trigger-filter", - "substreams/trigger-filters", "graph", "tests", "graph/derive", diff --git a/chain/ethereum/examples/firehose.rs b/chain/ethereum/examples/firehose.rs index 5a70794dfe2..e5f85964fe1 100644 --- a/chain/ethereum/examples/firehose.rs +++ b/chain/ethereum/examples/firehose.rs @@ -38,7 +38,6 @@ async fn main() -> Result<(), Error> { false, SubgraphLimit::Unlimited, metrics, - false, )); loop { diff --git a/chain/ethereum/src/chain.rs b/chain/ethereum/src/chain.rs index 11ca025e0e2..4acfc4aab8b 100644 --- a/chain/ethereum/src/chain.rs +++ b/chain/ethereum/src/chain.rs @@ -16,9 +16,7 @@ use graph::prelude::{ retry, BlockHash, ComponentLoggerConfig, ElasticComponentLoggerConfig, EthereumBlock, EthereumCallCache, LightEthereumBlock, LightEthereumBlockExt, MetricsRegistry, StoreError, }; -use graph::schema::InputSchema; use graph::slog::{debug, error, trace, warn}; -use graph::substreams::Clock; use graph::{ blockchain::{ block_stream::{ @@ -114,18 +112,6 @@ impl BlockStreamBuilder for EthereumStreamBuilder { ))) } - async fn build_substreams( - &self, - _chain: &Chain, - _schema: InputSchema, - _deployment: DeploymentLocator, - _block_cursor: FirehoseCursor, - _subgraph_current_block: Option, - _filter: Arc<::TriggerFilter>, - ) -> Result>> { - unimplemented!() - } - async fn build_subgraph_block_stream( &self, chain: &Chain, @@ -1159,16 +1145,6 @@ impl BlockStreamMapper for FirehoseMapper { .await .map_err(BlockStreamError::from) } - - async fn handle_substreams_block( - &self, - _logger: &Logger, - _clock: Clock, - _cursor: FirehoseCursor, - _block: Vec, - ) -> Result, BlockStreamError> { - unimplemented!() - } } #[async_trait] diff --git a/chain/near/Cargo.toml b/chain/near/Cargo.toml index 6984c831cd8..7df0cc53966 100644 --- a/chain/near/Cargo.toml +++ b/chain/near/Cargo.toml @@ -19,5 +19,4 @@ graph-runtime-derive = { path = "../../runtime/derive" } [dev-dependencies] diesel = { workspace = true } -trigger-filters.path = "../../substreams/trigger-filters" -tokio = { workspace = true } \ No newline at end of file +tokio = { workspace = true } diff --git a/chain/near/src/adapter.rs b/chain/near/src/adapter.rs index 4d6151aa5ca..b6e450f5741 100644 --- a/chain/near/src/adapter.rs +++ b/chain/near/src/adapter.rs @@ -251,14 +251,13 @@ mod test { use std::collections::HashSet; use super::NearBlockFilter; - use crate::adapter::{NearReceiptFilter, TriggerFilter, BASIC_RECEIPT_FILTER_TYPE_URL}; + use crate::adapter::{TriggerFilter, BASIC_RECEIPT_FILTER_TYPE_URL}; use graph::{ blockchain::TriggerFilter as _, firehose::{BasicReceiptFilter, PrefixSuffixPair}, }; use prost::Message; use prost_types::Any; - use trigger_filters::NearFilter; #[test] fn near_trigger_empty_filter() { @@ -365,124 +364,6 @@ mod test { ); } - #[test] - fn test_near_filter_params_serialization() -> anyhow::Result<()> { - struct Case<'a> { - name: &'a str, - input: NearReceiptFilter, - expected: NearFilter<'a>, - } - - let cases = vec![ - Case { - name: "empty", - input: NearReceiptFilter::default(), - expected: NearFilter::default(), - }, - Case { - name: "only full matches", - input: super::NearReceiptFilter { - accounts: HashSet::from_iter(vec!["acc1".into()]), - partial_accounts: HashSet::new(), - }, - expected: NearFilter { - accounts: HashSet::from_iter(vec!["acc1"]), - partial_accounts: HashSet::default(), - }, - }, - Case { - name: "only partial matches", - input: super::NearReceiptFilter { - accounts: HashSet::new(), - partial_accounts: HashSet::from_iter(vec![(Some("acc1".into()), None)]), - }, - expected: NearFilter { - accounts: HashSet::default(), - partial_accounts: HashSet::from_iter(vec![(Some("acc1"), None)]), - }, - }, - Case { - name: "both 1len matches", - input: super::NearReceiptFilter { - accounts: HashSet::from_iter(vec!["acc1".into()]), - partial_accounts: HashSet::from_iter(vec![(Some("s1".into()), None)]), - }, - expected: NearFilter { - accounts: HashSet::from_iter(vec!["acc1"]), - partial_accounts: HashSet::from_iter(vec![(Some("s1"), None)]), - }, - }, - Case { - name: "more partials matches", - input: super::NearReceiptFilter { - accounts: HashSet::from_iter(vec!["acc1".into()]), - partial_accounts: HashSet::from_iter(vec![ - (Some("s1".into()), None), - (None, Some("s3".into())), - (Some("s2".into()), Some("s2".into())), - ]), - }, - expected: NearFilter { - accounts: HashSet::from_iter(vec!["acc1"]), - partial_accounts: HashSet::from_iter(vec![ - (Some("s1"), None), - (None, Some("s3")), - (Some("s2"), Some("s2")), - ]), - }, - }, - Case { - name: "both matches", - input: NearReceiptFilter { - accounts: HashSet::from_iter(vec![ - "acc1".into(), - "=12-30786jhasdgmasd".into(), - "^&%^&^$".into(), - "acc3".into(), - ]), - partial_accounts: HashSet::from_iter(vec![ - (Some("1.2.2.3.45.5".into()), None), - (None, Some("kjysdfoiua6sd".into())), - (Some("120938pokasd".into()), Some("102938poai[sd]".into())), - ]), - }, - expected: NearFilter { - accounts: HashSet::from_iter(vec![ - "acc1", - "=12-30786jhasdgmasd", - "^&%^&^$", - "acc3", - ]), - partial_accounts: HashSet::from_iter(vec![ - (Some("1.2.2.3.45.5"), None), - (None, Some("kjysdfoiua6sd")), - (Some("120938pokasd"), Some("102938poai[sd]")), - ]), - }, - }, - ]; - - for case in cases.into_iter() { - let tf = TriggerFilter { - block_filter: NearBlockFilter::default(), - receipt_filter: case.input, - }; - let param = tf.to_module_params(); - let filter = NearFilter::try_from(param.as_str()).expect(&format!( - "case: {}, the filter to parse params correctly", - case.name - )); - - assert_eq!( - filter, case.expected, - "case {},param:\n{}", - case.name, param - ); - } - - Ok(()) - } - fn decode_filter(firehose_filter: Vec) -> BasicReceiptFilter { let firehose_filter = firehose_filter[0].clone(); assert_eq!( diff --git a/chain/near/src/chain.rs b/chain/near/src/chain.rs index 7bf2b50a6a8..becaf1556e2 100644 --- a/chain/near/src/chain.rs +++ b/chain/near/src/chain.rs @@ -1,22 +1,17 @@ -use anyhow::anyhow; use async_trait::async_trait; use graph::blockchain::client::ChainClient; use graph::blockchain::firehose_block_ingestor::FirehoseBlockIngestor; -use graph::blockchain::substreams_block_stream::SubstreamsBlockStream; use graph::blockchain::{ BasicBlockchainBuilder, BlockIngestor, BlockchainBuilder, BlockchainKind, NoopDecoderHook, - NoopRuntimeAdapter, Trigger, TriggerFilterWrapper, + NoopRuntimeAdapter, TriggerFilterWrapper, }; use graph::cheap_clone::CheapClone; use graph::components::network_provider::ChainName; use graph::components::store::{ChainHeadStore, DeploymentCursorTracker, SourceableStore}; use graph::data::subgraph::UnifiedMappingApiVersion; -use graph::env::EnvVars; use graph::firehose::FirehoseEndpoint; use graph::futures03::TryFutureExt; use graph::prelude::MetricsRegistry; -use graph::schema::InputSchema; -use graph::substreams::{Clock, Package}; use graph::{ anyhow::Result, blockchain::{ @@ -37,7 +32,6 @@ use std::collections::BTreeSet; use std::sync::Arc; use crate::adapter::TriggerFilter; -use crate::codec::substreams_triggers::BlockAndReceipts; use crate::codec::Block; use crate::data_source::{DataSourceTemplate, UnresolvedDataSourceTemplate}; use crate::trigger::{self, NearTrigger}; @@ -49,68 +43,10 @@ use graph::blockchain::block_stream::{ BlockStream, BlockStreamBuilder, BlockStreamError, BlockStreamMapper, FirehoseCursor, }; -const NEAR_FILTER_MODULE_NAME: &str = "near_filter"; -const SUBSTREAMS_TRIGGER_FILTER_BYTES: &[u8; 510162] = include_bytes!( - "../../../substreams/substreams-trigger-filter/substreams-trigger-filter-v0.1.0.spkg" -); - pub struct NearStreamBuilder {} #[async_trait] impl BlockStreamBuilder for NearStreamBuilder { - async fn build_substreams( - &self, - chain: &Chain, - _schema: InputSchema, - deployment: DeploymentLocator, - block_cursor: FirehoseCursor, - subgraph_current_block: Option, - filter: Arc<::TriggerFilter>, - ) -> Result>> { - let mapper = Arc::new(FirehoseMapper { - adapter: Arc::new(TriggersAdapter {}), - filter, - }); - let mut package = - Package::decode(SUBSTREAMS_TRIGGER_FILTER_BYTES.to_vec().as_ref()).unwrap(); - match package.modules.as_mut() { - Some(modules) => modules - .modules - .iter_mut() - .find(|module| module.name == NEAR_FILTER_MODULE_NAME) - .map(|module| { - graph::substreams::patch_module_params( - mapper.filter.to_module_params(), - module, - ); - module - }), - None => None, - }; - - let logger = chain - .logger_factory - .subgraph_logger(&deployment) - .new(o!("component" => "SubstreamsBlockStream")); - let start_block = subgraph_current_block - .as_ref() - .map(|b| b.number) - .unwrap_or_default(); - - Ok(Box::new(SubstreamsBlockStream::new( - deployment.hash, - chain.chain_client(), - subgraph_current_block, - block_cursor.clone(), - mapper, - package.modules.unwrap_or_default(), - NEAR_FILTER_MODULE_NAME.to_string(), - vec![start_block], - vec![], - logger, - chain.metrics_registry.clone(), - ))) - } async fn build_firehose( &self, chain: &Chain, @@ -169,7 +105,6 @@ pub struct Chain { chain_head_store: Arc, metrics_registry: Arc, block_stream_builder: Arc>, - prefer_substreams: bool, } impl std::fmt::Debug for Chain { @@ -180,7 +115,7 @@ impl std::fmt::Debug for Chain { #[async_trait] impl BlockchainBuilder for BasicBlockchainBuilder { - async fn build(self, config: &Arc) -> Chain { + async fn build(self) -> Chain { Chain { logger_factory: self.logger_factory, name: self.name, @@ -188,7 +123,6 @@ impl BlockchainBuilder for BasicBlockchainBuilder { client: Arc::new(ChainClient::new_firehose(self.firehose_endpoints)), metrics_registry: self.metrics_registry, block_stream_builder: Arc::new(NearStreamBuilder {}), - prefer_substreams: config.prefer_substreams_block_streams, } } } @@ -237,20 +171,6 @@ impl Blockchain for Chain { filter: Arc>, unified_api_version: UnifiedMappingApiVersion, ) -> Result>, Error> { - if self.prefer_substreams { - return self - .block_stream_builder - .build_substreams( - self, - store.input_schema(), - deployment, - store.firehose_cursor(), - store.block_ptr(), - filter.chain_filter.clone(), - ) - .await; - } - self.block_stream_builder .build_firehose( self, @@ -460,44 +380,6 @@ impl BlockStreamMapper for FirehoseMapper { .await .map_err(BlockStreamError::from) } - - async fn handle_substreams_block( - &self, - _logger: &Logger, - _clock: Clock, - cursor: FirehoseCursor, - message: Vec, - ) -> Result, BlockStreamError> { - let BlockAndReceipts { - block, - outcome, - receipt, - } = BlockAndReceipts::decode(message.as_ref())?; - let block = block.ok_or_else(|| anyhow!("near block is mandatory on substreams"))?; - let arc_block = Arc::new(block.clone()); - - let trigger_data = outcome - .into_iter() - .zip(receipt.into_iter()) - .map(|(outcome, receipt)| { - Trigger::Chain(NearTrigger::Receipt(Arc::new( - trigger::ReceiptWithOutcome { - outcome, - receipt, - block: arc_block.clone(), - }, - ))) - }) - .collect(); - - Ok(BlockStreamEvent::ProcessBlock( - BlockWithTriggers { - block, - trigger_data, - }, - cursor, - )) - } } #[async_trait] diff --git a/graph/src/blockchain/block_stream.rs b/graph/src/blockchain/block_stream.rs index e3568345803..81a0f37c7d7 100644 --- a/graph/src/blockchain/block_stream.rs +++ b/graph/src/blockchain/block_stream.rs @@ -1,21 +1,15 @@ use crate::blockchain::SubgraphFilter; use crate::data_source::{subgraph, CausalityRegion}; -use crate::substreams::Clock; -use crate::substreams_rpc::response::Message as SubstreamsMessage; -use crate::substreams_rpc::BlockScopedData; use anyhow::Error; use async_stream::stream; use async_trait::async_trait; use futures03::Stream; -use prost_types::Any; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::fmt; use std::sync::Arc; -use std::time::Instant; use thiserror::Error; use tokio::sync::mpsc::{self, Receiver, Sender}; -use super::substreams_block_stream::SubstreamsLogData; use super::{Block, BlockPtr, BlockTime, Blockchain, Trigger, TriggerFilterWrapper}; use crate::anyhow::Result; use crate::components::store::{BlockNumber, DeploymentLocator, SourceableStore}; @@ -23,7 +17,6 @@ use crate::data::subgraph::UnifiedMappingApiVersion; use crate::firehose::{self, FirehoseEndpoint}; use crate::futures03::stream::StreamExt as _; use crate::schema::{EntityType, InputSchema}; -use crate::substreams_rpc::response::Message; use crate::{prelude::*, prometheus::labels}; pub const BUFFERED_BLOCK_STREAM_SIZE: usize = 100; @@ -133,16 +126,6 @@ pub trait BlockStreamBuilder: Send + Sync { unified_api_version: UnifiedMappingApiVersion, ) -> Result>>; - async fn build_substreams( - &self, - chain: &C, - schema: InputSchema, - deployment: DeploymentLocator, - block_cursor: FirehoseCursor, - subgraph_current_block: Option, - filter: Arc, - ) -> Result>>; - async fn build_polling( &self, chain: &C, @@ -695,102 +678,6 @@ pub trait BlockStreamMapper: Send + Sync { logger: &Logger, block: C::Block, ) -> Result, BlockStreamError>; - - async fn handle_substreams_block( - &self, - logger: &Logger, - clock: Clock, - cursor: FirehoseCursor, - block: Vec, - ) -> Result, BlockStreamError>; - - async fn to_block_stream_event( - &self, - logger: &mut Logger, - message: Option, - log_data: &mut SubstreamsLogData, - ) -> Result>, BlockStreamError> { - match message { - Some(SubstreamsMessage::Session(session_init)) => { - info!( - &logger, - "Received session init"; - "session" => format!("{:?}", session_init), - ); - log_data.trace_id = session_init.trace_id; - return Ok(None); - } - Some(SubstreamsMessage::BlockUndoSignal(undo)) => { - let valid_block = match undo.last_valid_block { - Some(clock) => clock, - None => return Err(BlockStreamError::from(SubstreamsError::InvalidUndoError)), - }; - let valid_ptr = BlockPtr { - hash: valid_block.id.trim_start_matches("0x").try_into()?, - number: valid_block.number as i32, - }; - log_data.last_seen_block = valid_block.number; - return Ok(Some(BlockStreamEvent::Revert( - valid_ptr, - FirehoseCursor::from(undo.last_valid_cursor.clone()), - ))); - } - - Some(SubstreamsMessage::BlockScopedData(block_scoped_data)) => { - let BlockScopedData { - output, - clock, - cursor, - final_block_height: _, - debug_map_outputs: _, - debug_store_outputs: _, - } = block_scoped_data; - - let module_output = match output { - Some(out) => out, - None => return Ok(None), - }; - - let clock = match clock { - Some(clock) => clock, - None => return Err(BlockStreamError::from(SubstreamsError::MissingClockError)), - }; - - let value = match module_output.map_output { - Some(Any { type_url: _, value }) => value, - None => return Ok(None), - }; - - log_data.last_seen_block = clock.number; - let cursor = FirehoseCursor::from(cursor); - - let event = self - .handle_substreams_block(&logger, clock, cursor, value) - .await?; - - Ok(Some(event)) - } - - Some(SubstreamsMessage::Progress(progress)) => { - if log_data.last_progress.elapsed() > Duration::from_secs(30) { - info!(&logger, "{}", log_data.info_string(&progress); "trace_id" => &log_data.trace_id); - debug!(&logger, "{}", log_data.debug_string(&progress); "trace_id" => &log_data.trace_id); - trace!( - &logger, - "Received progress update"; - "progress" => format!("{:?}", progress), - "trace_id" => &log_data.trace_id, - ); - log_data.last_progress = Instant::now(); - } - Ok(None) - } - - // ignoring Progress messages and SessionInit - // We are only interested in Data and Undo signals - _ => Ok(None), - } - } } #[derive(Error, Debug)] diff --git a/graph/src/blockchain/builder.rs b/graph/src/blockchain/builder.rs index 943586770c5..773f186299f 100644 --- a/graph/src/blockchain/builder.rs +++ b/graph/src/blockchain/builder.rs @@ -4,7 +4,6 @@ use super::Blockchain; use crate::{ components::store::ChainHeadStore, data::value::Word, - env::EnvVars, firehose::FirehoseEndpoints, prelude::{LoggerFactory, MetricsRegistry}, }; @@ -26,5 +25,5 @@ pub trait BlockchainBuilder where C: Blockchain, { - async fn build(self, config: &Arc) -> C; + async fn build(self) -> C; } diff --git a/graph/src/blockchain/mod.rs b/graph/src/blockchain/mod.rs index 7ae4669ce01..fcf6f88109e 100644 --- a/graph/src/blockchain/mod.rs +++ b/graph/src/blockchain/mod.rs @@ -10,7 +10,6 @@ pub mod firehose_block_ingestor; pub mod firehose_block_stream; pub mod mock; mod noop_runtime_adapter; -pub mod substreams_block_stream; mod types; // Try to reexport most of the necessary types diff --git a/graph/src/blockchain/substreams_block_stream.rs b/graph/src/blockchain/substreams_block_stream.rs deleted file mode 100644 index c359ec1a504..00000000000 --- a/graph/src/blockchain/substreams_block_stream.rs +++ /dev/null @@ -1,433 +0,0 @@ -use super::block_stream::{ - BlockStreamError, BlockStreamMapper, FirehoseCursor, SUBSTREAMS_BUFFER_STREAM_SIZE, -}; -use super::client::ChainClient; -use crate::blockchain::block_stream::{BlockStream, BlockStreamEvent}; -use crate::blockchain::Blockchain; -use crate::firehose::ConnectionHeaders; -use crate::prelude::*; -use crate::substreams::Modules; -use crate::substreams_rpc::{ModulesProgress, Request, Response}; -use crate::util::backoff::ExponentialBackoff; -use async_stream::try_stream; -use futures03::{Stream, StreamExt}; -use humantime::format_duration; -use std::sync::Arc; -use std::task::{Context, Poll}; -use std::time::{Duration, Instant}; -use tonic::{Code, Status}; - -struct SubstreamsBlockStreamMetrics { - deployment: DeploymentHash, - restarts: CounterVec, - connect_duration: GaugeVec, - time_between_responses: HistogramVec, - responses: CounterVec, -} - -impl SubstreamsBlockStreamMetrics { - pub fn new(registry: Arc, deployment: DeploymentHash) -> Self { - Self { - deployment, - restarts: registry - .global_counter_vec( - "deployment_substreams_blockstream_restarts", - "Counts the number of times a Substreams block stream is (re)started", - vec!["deployment", "provider", "success"].as_slice(), - ) - .unwrap(), - - connect_duration: registry - .global_gauge_vec( - "deployment_substreams_blockstream_connect_duration", - "Measures the time it takes to connect a Substreams block stream", - vec!["deployment", "provider"].as_slice(), - ) - .unwrap(), - - time_between_responses: registry - .global_histogram_vec( - "deployment_substreams_blockstream_time_between_responses", - "Measures the time between receiving and processing Substreams stream responses", - vec!["deployment", "provider"].as_slice(), - ) - .unwrap(), - - responses: registry - .global_counter_vec( - "deployment_substreams_blockstream_responses", - "Counts the number of responses received from a Substreams block stream", - vec!["deployment", "provider", "kind"].as_slice(), - ) - .unwrap(), - } - } - - fn observe_successful_connection(&self, time: &mut Instant, provider: &str) { - self.restarts - .with_label_values(&[self.deployment.as_str(), &provider, "true"]) - .inc(); - self.connect_duration - .with_label_values(&[self.deployment.as_str(), &provider]) - .set(time.elapsed().as_secs_f64()); - - // Reset last connection timestamp - *time = Instant::now(); - } - - fn observe_failed_connection(&self, time: &mut Instant, provider: &str) { - self.restarts - .with_label_values(&[self.deployment.as_str(), &provider, "false"]) - .inc(); - self.connect_duration - .with_label_values(&[self.deployment.as_str(), &provider]) - .set(time.elapsed().as_secs_f64()); - - // Reset last connection timestamp - *time = Instant::now(); - } - - fn observe_response(&self, kind: &str, time: &mut Instant, provider: &str) { - self.time_between_responses - .with_label_values(&[self.deployment.as_str(), &provider]) - .observe(time.elapsed().as_secs_f64()); - self.responses - .with_label_values(&[self.deployment.as_str(), &provider, kind]) - .inc(); - - // Reset last response timestamp - *time = Instant::now(); - } -} - -pub struct SubstreamsBlockStream { - //fixme: not sure if this is ok to be set as public, maybe - // we do not want to expose the stream to the caller - stream: Pin, BlockStreamError>> + Send>>, -} - -impl SubstreamsBlockStream -where - C: Blockchain, -{ - pub fn new( - deployment: DeploymentHash, - client: Arc>, - subgraph_current_block: Option, - cursor: FirehoseCursor, - mapper: Arc, - modules: Modules, - module_name: String, - start_blocks: Vec, - end_blocks: Vec, - logger: Logger, - registry: Arc, - ) -> Self - where - F: BlockStreamMapper + 'static, - { - let manifest_start_block_num = start_blocks.into_iter().min().unwrap_or(0); - - let manifest_end_block_num = end_blocks.into_iter().min().unwrap_or(0); - - let metrics = SubstreamsBlockStreamMetrics::new(registry, deployment.clone()); - - SubstreamsBlockStream { - stream: Box::pin(stream_blocks( - client, - cursor, - deployment, - mapper, - modules, - module_name, - manifest_start_block_num, - manifest_end_block_num, - subgraph_current_block, - logger, - metrics, - )), - } - } -} - -fn stream_blocks>( - client: Arc>, - cursor: FirehoseCursor, - deployment: DeploymentHash, - mapper: Arc, - modules: Modules, - module_name: String, - manifest_start_block_num: BlockNumber, - manifest_end_block_num: BlockNumber, - subgraph_current_block: Option, - logger: Logger, - metrics: SubstreamsBlockStreamMetrics, -) -> impl Stream, BlockStreamError>> { - let mut latest_cursor = cursor.to_string(); - - let start_block_num = subgraph_current_block - .as_ref() - .map(|ptr| { - // current_block has already been processed, we start at next block - ptr.block_number() as i64 + 1 - }) - .unwrap_or(manifest_start_block_num as i64); - - let stop_block_num = manifest_end_block_num as u64; - - let headers = ConnectionHeaders::new().with_deployment(deployment.clone()); - - // Back off exponentially whenever we encounter a connection error or a stream with bad data - let mut backoff = ExponentialBackoff::new(Duration::from_millis(500), Duration::from_secs(45)); - - let mut log_data = SubstreamsLogData::new(); - - try_stream! { - // This attribute is needed because `try_stream!` seems to break detection of `skip_backoff` assignments - #[allow(unused_assignments)] - let mut skip_backoff = false; - - if !modules.modules.iter().any(|m| module_name.eq(&m.name)) { - Err(BlockStreamError::Fatal(format!( - "module `{}` not found", - module_name - )))?; - } - - let endpoint = client.firehose_endpoint().await?; - let mut logger = logger.new(o!("deployment" => deployment.clone(), "provider" => endpoint.provider.to_string())); - - loop { - // We just reconnected, assume that we want to back off on errors - skip_backoff = false; - - let mut connect_start = Instant::now(); - let request = Request { - start_block_num, - start_cursor: latest_cursor.to_string(), - stop_block_num, - modules: Some(modules.clone()), - output_module: module_name.clone(), - production_mode: true, - ..Default::default() - }; - - - let result = endpoint.clone().substreams(request, &headers).await; - - match result { - Ok(stream) => { - info!(&logger, "Blockstreams connected"); - - // Track the time it takes to set up the block stream - metrics.observe_successful_connection(&mut connect_start, &endpoint.provider); - - let mut last_response_time = Instant::now(); - let mut expected_stream_end = false; - - for await response in stream{ - match process_substreams_response( - response, - mapper.as_ref(), - &mut logger, - &mut log_data, - ).await { - Ok(block_response) => { - match block_response { - None => {} - Some(BlockResponse::Proceed(event, cursor)) => { - // Reset backoff because we got a good value from the stream - backoff.reset(); - - metrics.observe_response("proceed", &mut last_response_time, &endpoint.provider); - - yield event; - - latest_cursor = cursor; - } - } - }, - Err(BlockStreamError::SubstreamsError(e)) if e.is_deterministic() => - Err(BlockStreamError::Fatal(e.to_string()))?, - - Err(BlockStreamError::Fatal(msg)) => - Err(BlockStreamError::Fatal(msg))?, - - Err(err) => { - - info!(&logger, "received err"); - // We have an open connection but there was an error processing the Firehose - // response. We will reconnect the stream after this; this is the case where - // we actually _want_ to back off in case we keep running into the same error. - // An example of this situation is if we get invalid block or transaction data - // that cannot be decoded properly. - - metrics.observe_response("error", &mut last_response_time, &endpoint.provider); - - error!(logger, "{:#}", err); - expected_stream_end = true; - break; - } - } - } - - if !expected_stream_end { - error!(logger, "Stream blocks complete unexpectedly, expecting stream to always stream blocks"); - } - }, - Err(e) => { - // We failed to connect and will try again; this is another - // case where we actually _want_ to back off in case we keep - // having connection errors. - - metrics.observe_failed_connection(&mut connect_start, &endpoint.provider); - - error!(logger, "Unable to connect to endpoint: {:#}", e); - } - } - - // If we reach this point, we must wait a bit before retrying, unless `skip_backoff` is true - if !skip_backoff { - backoff.sleep_async().await; - } - } - } -} - -enum BlockResponse { - Proceed(BlockStreamEvent, String), -} - -async fn process_substreams_response>( - result: Result, - mapper: &F, - logger: &mut Logger, - log_data: &mut SubstreamsLogData, -) -> Result>, BlockStreamError> { - let response = match result { - Ok(v) => v, - Err(e) => { - if e.code() == Code::InvalidArgument { - return Err(BlockStreamError::Fatal(e.message().to_string())); - } - - return Err(BlockStreamError::from(anyhow!( - "An error occurred while streaming blocks: {:#}", - e - ))); - } - }; - - match mapper - .to_block_stream_event(logger, response.message, log_data) - .await - .map_err(BlockStreamError::from)? - { - Some(event) => { - let cursor = match &event { - BlockStreamEvent::Revert(_, cursor) => cursor, - BlockStreamEvent::ProcessBlock(_, cursor) => cursor, - BlockStreamEvent::ProcessWasmBlock(_, _, _, _, cursor) => cursor, - } - .to_string(); - - return Ok(Some(BlockResponse::Proceed(event, cursor))); - } - None => Ok(None), // some progress responses are ignored within to_block_stream_event - } -} - -impl Stream for SubstreamsBlockStream { - type Item = Result, BlockStreamError>; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - self.stream.poll_next_unpin(cx) - } -} - -impl BlockStream for SubstreamsBlockStream { - fn buffer_size_hint(&self) -> usize { - SUBSTREAMS_BUFFER_STREAM_SIZE - } -} - -pub struct SubstreamsLogData { - pub last_progress: Instant, - pub last_seen_block: u64, - pub trace_id: String, -} - -impl SubstreamsLogData { - fn new() -> SubstreamsLogData { - SubstreamsLogData { - last_progress: Instant::now(), - last_seen_block: 0, - trace_id: "".to_string(), - } - } - pub fn info_string(&self, progress: &ModulesProgress) -> String { - format!( - "Substreams backend graph_out last block is {}, {} stages, {} jobs", - self.last_seen_block, - progress.stages.len(), - progress.running_jobs.len() - ) - } - pub fn debug_string(&self, progress: &ModulesProgress) -> String { - let len = progress.stages.len(); - let mut stages_str = "".to_string(); - for i in (0..len).rev() { - let stage = &progress.stages[i]; - let range = if stage.completed_ranges.len() > 0 { - let b = stage.completed_ranges.iter().map(|x| x.end_block).min(); - format!(" up to {}", b.unwrap_or(0)) - } else { - "".to_string() - }; - let mlen = stage.modules.len(); - let module = if mlen == 0 { - "".to_string() - } else if mlen == 1 { - format!(" ({})", stage.modules[0]) - } else { - format!(" ({} +{})", stage.modules[mlen - 1], mlen - 1) - }; - if !stages_str.is_empty() { - stages_str.push_str(", "); - } - stages_str.push_str(&format!("#{}{}{}", i, range, module)); - } - let stage_str = if len > 0 { - format!(" Stages: [{}]", stages_str) - } else { - "".to_string() - }; - let mut jobs_str = "".to_string(); - let jlen = progress.running_jobs.len(); - for i in 0..jlen { - let job = &progress.running_jobs[i]; - if !jobs_str.is_empty() { - jobs_str.push_str(", "); - } - let duration_str = format_duration(Duration::from_millis(job.duration_ms)); - jobs_str.push_str(&format!( - "#{} on Stage {} @ {} | +{}|{} elapsed {}", - i, - job.stage, - job.start_block, - job.processed_blocks, - job.stop_block - job.start_block, - duration_str - )); - } - let job_str = if jlen > 0 { - format!(", Jobs: [{}]", jobs_str) - } else { - "".to_string() - }; - format!( - "Substreams backend graph_out last block is {},{}{}", - self.last_seen_block, stage_str, job_str, - ) - } -} diff --git a/graph/src/env/mod.rs b/graph/src/env/mod.rs index 09657c041f5..91cb2355088 100644 --- a/graph/src/env/mod.rs +++ b/graph/src/env/mod.rs @@ -206,8 +206,6 @@ pub struct EnvVars { /// Set by the env var `GRAPH_EXPERIMENTAL_SUBGRAPH_SETTINGS` which should point /// to a file with subgraph-specific settings pub subgraph_settings: Option, - /// Whether to prefer substreams blocks streams over firehose when available. - pub prefer_substreams_block_streams: bool, /// Set by the flag `GRAPH_ENABLE_DIPS_METRICS`. Whether to enable /// gas metrics. Off by default. pub enable_dips_metrics: bool, @@ -355,7 +353,6 @@ impl EnvVars { enable_sql_queries: inner.enable_sql_queries.0, ingestor_polling_interval: Duration::from_millis(inner.ingestor_polling_interval), subgraph_settings: inner.subgraph_settings, - prefer_substreams_block_streams: inner.prefer_substreams_block_streams, enable_dips_metrics: inner.enable_dips_metrics.0, history_blocks_override: inner.history_blocks_override, min_history_blocks: inner @@ -553,11 +550,6 @@ struct Inner { ingestor_polling_interval: u64, #[envconfig(from = "GRAPH_EXPERIMENTAL_SUBGRAPH_SETTINGS")] subgraph_settings: Option, - #[envconfig( - from = "GRAPH_EXPERIMENTAL_PREFER_SUBSTREAMS_BLOCK_STREAMS", - default = "false" - )] - prefer_substreams_block_streams: bool, #[envconfig(from = "GRAPH_ENABLE_DIPS_METRICS", default = "false")] enable_dips_metrics: EnvVarBoolean, #[envconfig(from = "GRAPH_HISTORY_BLOCKS_OVERRIDE")] diff --git a/graph/src/firehose/endpoints.rs b/graph/src/firehose/endpoints.rs index b05390154ed..76fefd61797 100644 --- a/graph/src/firehose/endpoints.rs +++ b/graph/src/firehose/endpoints.rs @@ -1,4 +1,3 @@ -use crate::firehose::codec::InfoRequest; use crate::firehose::fetch_client::FetchClient; use crate::firehose::interceptors::AuthInterceptor; use crate::{ @@ -11,7 +10,6 @@ use crate::{ env::ENV_VARS, firehose::decode_firehose_block, prelude::{anyhow, debug, DeploymentHash}, - substreams_rpc, }; use anyhow::Context; use async_trait::async_trait; @@ -53,7 +51,6 @@ pub struct FirehoseEndpoint { pub filters_enabled: bool, pub compression_enabled: bool, pub subgraph_limit: SubgraphLimit, - is_substreams: bool, endpoint_metrics: Arc, channel: Channel, @@ -185,7 +182,6 @@ impl FirehoseEndpoint { compression_enabled: bool, subgraph_limit: SubgraphLimit, endpoint_metrics: Arc, - is_substreams_endpoint: bool, ) -> Self { let uri = url .as_ref() @@ -254,7 +250,6 @@ impl FirehoseEndpoint { subgraph_limit, endpoint_metrics, info_response: OnceCell::new(), - is_substreams: is_substreams_endpoint, } } @@ -337,51 +332,6 @@ impl FirehoseEndpoint { client } - fn new_substreams_info_client( - &self, - ) -> crate::substreams_rpc::endpoint_info_client::EndpointInfoClient< - InterceptedService, impl tonic::service::Interceptor>, - > { - let metrics = self.metrics_interceptor(); - - let mut client = - crate::substreams_rpc::endpoint_info_client::EndpointInfoClient::with_interceptor( - metrics, - self.auth.clone(), - ) - .accept_compressed(CompressionEncoding::Gzip); - - if self.compression_enabled { - client = client.send_compressed(CompressionEncoding::Gzip); - } - - client = client.max_decoding_message_size(self.max_message_size()); - - client - } - - fn new_substreams_streaming_client( - &self, - ) -> substreams_rpc::stream_client::StreamClient< - InterceptedService, impl tonic::service::Interceptor>, - > { - let metrics = self.metrics_interceptor(); - - let mut client = substreams_rpc::stream_client::StreamClient::with_interceptor( - metrics, - self.auth.clone(), - ) - .accept_compressed(CompressionEncoding::Gzip); - - if self.compression_enabled { - client = client.send_compressed(CompressionEncoding::Gzip); - } - - client = client.max_decoding_message_size(self.max_message_size()); - - client - } - pub async fn get_block( &self, cursor: FirehoseCursor, @@ -687,19 +637,6 @@ impl FirehoseEndpoint { Ok(block_stream) } - pub async fn substreams( - self: Arc, - request: substreams_rpc::Request, - headers: &ConnectionHeaders, - ) -> Result, anyhow::Error> { - let mut client = self.new_substreams_streaming_client(); - let request = headers.add_to_request(request); - let response_stream = client.blocks(request).await?; - let block_stream = response_stream.into_inner(); - - Ok(block_stream) - } - pub async fn info( self: Arc, ) -> Result { @@ -707,20 +644,9 @@ impl FirehoseEndpoint { self.info_response .get_or_try_init(move || async move { - if endpoint.is_substreams { - let mut client = endpoint.new_substreams_info_client(); + let mut client = endpoint.new_firehose_info_client(); - client - .info(InfoRequest {}) - .await - .map(|r| r.into_inner()) - .map_err(anyhow::Error::from) - .and_then(|e| e.try_into()) - } else { - let mut client = endpoint.new_firehose_info_client(); - - client.info().await - } + client.info().await }) .await .map(ToOwned::to_owned) @@ -808,7 +734,6 @@ mod test { false, SubgraphLimit::Unlimited, Arc::new(EndpointMetrics::mock()), - false, ))]; let endpoints = FirehoseEndpoints::for_testing(endpoint); @@ -841,7 +766,6 @@ mod test { false, SubgraphLimit::Limit(2), Arc::new(EndpointMetrics::mock()), - false, ))]; let endpoints = FirehoseEndpoints::for_testing(endpoint); @@ -869,7 +793,6 @@ mod test { false, SubgraphLimit::Disabled, Arc::new(EndpointMetrics::mock()), - false, ))]; let endpoints = FirehoseEndpoints::for_testing(endpoint); @@ -896,7 +819,6 @@ mod test { false, SubgraphLimit::Unlimited, endpoint_metrics.clone(), - false, )); let high_error_adapter2 = Arc::new(FirehoseEndpoint::new( "high_error".to_string(), @@ -907,7 +829,6 @@ mod test { false, SubgraphLimit::Unlimited, endpoint_metrics.clone(), - false, )); let low_availability = Arc::new(FirehoseEndpoint::new( "low availability".to_string(), @@ -918,7 +839,6 @@ mod test { false, SubgraphLimit::Limit(2), endpoint_metrics.clone(), - false, )); let high_availability = Arc::new(FirehoseEndpoint::new( "high availability".to_string(), @@ -929,7 +849,6 @@ mod test { false, SubgraphLimit::Unlimited, endpoint_metrics.clone(), - false, )); endpoint_metrics.report_for_test(&high_error_adapter1.provider, false); diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 822adac8e44..0607cab5937 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -24,10 +24,6 @@ pub mod runtime; pub mod firehose; -pub mod substreams; - -pub mod substreams_rpc; - pub mod endpoint; pub mod schema; diff --git a/graph/src/substreams/codec.rs b/graph/src/substreams/codec.rs deleted file mode 100644 index 23edcc3b7c1..00000000000 --- a/graph/src/substreams/codec.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[rustfmt::skip] -#[path = "sf.substreams.v1.rs"] -mod pbsubstreams; - -pub use pbsubstreams::*; diff --git a/graph/src/substreams/mod.rs b/graph/src/substreams/mod.rs deleted file mode 100644 index a09801b91ee..00000000000 --- a/graph/src/substreams/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -mod codec; - -pub use codec::*; - -use self::module::input::{Input, Params}; - -/// Replace all the existing params with the provided ones. -pub fn patch_module_params(params: String, module: &mut Module) { - let mut inputs = vec![crate::substreams::module::Input { - input: Some(Input::Params(Params { value: params })), - }]; - - inputs.extend(module.inputs.iter().flat_map(|input| match input.input { - None => None, - Some(Input::Params(_)) => None, - Some(_) => Some(input.clone()), - })); - - module.inputs = inputs; -} diff --git a/graph/src/substreams/sf.substreams.v1.rs b/graph/src/substreams/sf.substreams.v1.rs deleted file mode 100644 index dd6b8930293..00000000000 --- a/graph/src/substreams/sf.substreams.v1.rs +++ /dev/null @@ -1,304 +0,0 @@ -// This file is @generated by prost-build. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Package { - /// Needs to be one so this file can be used _directly_ as a - /// buf `Image` andor a ProtoSet for grpcurl and other tools - #[prost(message, repeated, tag = "1")] - pub proto_files: ::prost::alloc::vec::Vec<::prost_types::FileDescriptorProto>, - #[prost(uint64, tag = "5")] - pub version: u64, - #[prost(message, optional, tag = "6")] - pub modules: ::core::option::Option, - #[prost(message, repeated, tag = "7")] - pub module_meta: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "8")] - pub package_meta: ::prost::alloc::vec::Vec, - /// Source network for Substreams to fetch its data from. - #[prost(string, tag = "9")] - pub network: ::prost::alloc::string::String, - #[prost(message, optional, tag = "10")] - pub sink_config: ::core::option::Option<::prost_types::Any>, - #[prost(string, tag = "11")] - pub sink_module: ::prost::alloc::string::String, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PackageMetadata { - #[prost(string, tag = "1")] - pub version: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub url: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub name: ::prost::alloc::string::String, - #[prost(string, tag = "4")] - pub doc: ::prost::alloc::string::String, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ModuleMetadata { - /// Corresponds to the index in `Package.metadata.package_meta` - #[prost(uint64, tag = "1")] - pub package_index: u64, - #[prost(string, tag = "2")] - pub doc: ::prost::alloc::string::String, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Modules { - #[prost(message, repeated, tag = "1")] - pub modules: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "2")] - pub binaries: ::prost::alloc::vec::Vec, -} -/// Binary represents some code compiled to its binary form. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Binary { - #[prost(string, tag = "1")] - pub r#type: ::prost::alloc::string::String, - #[prost(bytes = "vec", tag = "2")] - pub content: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Module { - #[prost(string, tag = "1")] - pub name: ::prost::alloc::string::String, - #[prost(uint32, tag = "4")] - pub binary_index: u32, - #[prost(string, tag = "5")] - pub binary_entrypoint: ::prost::alloc::string::String, - #[prost(message, repeated, tag = "6")] - pub inputs: ::prost::alloc::vec::Vec, - #[prost(message, optional, tag = "7")] - pub output: ::core::option::Option, - #[prost(uint64, tag = "8")] - pub initial_block: u64, - #[prost(message, optional, tag = "9")] - pub block_filter: ::core::option::Option, - #[prost(oneof = "module::Kind", tags = "2, 3, 10")] - pub kind: ::core::option::Option, -} -/// Nested message and enum types in `Module`. -pub mod module { - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct BlockFilter { - #[prost(string, tag = "1")] - pub module: ::prost::alloc::string::String, - #[prost(oneof = "block_filter::Query", tags = "2, 3")] - pub query: ::core::option::Option, - } - /// Nested message and enum types in `BlockFilter`. - pub mod block_filter { - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Query { - #[prost(string, tag = "2")] - QueryString(::prost::alloc::string::String), - #[prost(message, tag = "3")] - QueryFromParams(super::QueryFromParams), - } - } - #[derive(Clone, Copy, PartialEq, ::prost::Message)] - pub struct QueryFromParams {} - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct KindMap { - #[prost(string, tag = "1")] - pub output_type: ::prost::alloc::string::String, - } - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct KindStore { - /// The `update_policy` determines the functions available to mutate the store - /// (like `set()`, `set_if_not_exists()` or `sum()`, etc..) in - /// order to ensure that parallel operations are possible and deterministic - /// - /// Say a store cumulates keys from block 0 to 1M, and a second store - /// cumulates keys from block 1M to 2M. When we want to use this - /// store as a dependency for a downstream module, we will merge the - /// two stores according to this policy. - #[prost(enumeration = "kind_store::UpdatePolicy", tag = "1")] - pub update_policy: i32, - #[prost(string, tag = "2")] - pub value_type: ::prost::alloc::string::String, - } - /// Nested message and enum types in `KindStore`. - pub mod kind_store { - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] - #[repr(i32)] - pub enum UpdatePolicy { - Unset = 0, - /// Provides a store where you can `set()` keys, and the latest key wins - Set = 1, - /// Provides a store where you can `set_if_not_exists()` keys, and the first key wins - SetIfNotExists = 2, - /// Provides a store where you can `add_*()` keys, where two stores merge by summing its values. - Add = 3, - /// Provides a store where you can `min_*()` keys, where two stores merge by leaving the minimum value. - Min = 4, - /// Provides a store where you can `max_*()` keys, where two stores merge by leaving the maximum value. - Max = 5, - /// Provides a store where you can `append()` keys, where two stores merge by concatenating the bytes in order. - Append = 6, - /// Provides a store with both `set()` and `sum()` functions. - SetSum = 7, - } - impl UpdatePolicy { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - Self::Unset => "UPDATE_POLICY_UNSET", - Self::Set => "UPDATE_POLICY_SET", - Self::SetIfNotExists => "UPDATE_POLICY_SET_IF_NOT_EXISTS", - Self::Add => "UPDATE_POLICY_ADD", - Self::Min => "UPDATE_POLICY_MIN", - Self::Max => "UPDATE_POLICY_MAX", - Self::Append => "UPDATE_POLICY_APPEND", - Self::SetSum => "UPDATE_POLICY_SET_SUM", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "UPDATE_POLICY_UNSET" => Some(Self::Unset), - "UPDATE_POLICY_SET" => Some(Self::Set), - "UPDATE_POLICY_SET_IF_NOT_EXISTS" => Some(Self::SetIfNotExists), - "UPDATE_POLICY_ADD" => Some(Self::Add), - "UPDATE_POLICY_MIN" => Some(Self::Min), - "UPDATE_POLICY_MAX" => Some(Self::Max), - "UPDATE_POLICY_APPEND" => Some(Self::Append), - "UPDATE_POLICY_SET_SUM" => Some(Self::SetSum), - _ => None, - } - } - } - } - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct KindBlockIndex { - #[prost(string, tag = "1")] - pub output_type: ::prost::alloc::string::String, - } - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct Input { - #[prost(oneof = "input::Input", tags = "1, 2, 3, 4")] - pub input: ::core::option::Option, - } - /// Nested message and enum types in `Input`. - pub mod input { - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct Source { - /// ex: "sf.ethereum.type.v1.Block" - #[prost(string, tag = "1")] - pub r#type: ::prost::alloc::string::String, - } - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct Map { - /// ex: "block_to_pairs" - #[prost(string, tag = "1")] - pub module_name: ::prost::alloc::string::String, - } - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct Store { - #[prost(string, tag = "1")] - pub module_name: ::prost::alloc::string::String, - #[prost(enumeration = "store::Mode", tag = "2")] - pub mode: i32, - } - /// Nested message and enum types in `Store`. - pub mod store { - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] - #[repr(i32)] - pub enum Mode { - Unset = 0, - Get = 1, - Deltas = 2, - } - impl Mode { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - Self::Unset => "UNSET", - Self::Get => "GET", - Self::Deltas => "DELTAS", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "UNSET" => Some(Self::Unset), - "GET" => Some(Self::Get), - "DELTAS" => Some(Self::Deltas), - _ => None, - } - } - } - } - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct Params { - #[prost(string, tag = "1")] - pub value: ::prost::alloc::string::String, - } - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Input { - #[prost(message, tag = "1")] - Source(Source), - #[prost(message, tag = "2")] - Map(Map), - #[prost(message, tag = "3")] - Store(Store), - #[prost(message, tag = "4")] - Params(Params), - } - } - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct Output { - #[prost(string, tag = "1")] - pub r#type: ::prost::alloc::string::String, - } - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Kind { - #[prost(message, tag = "2")] - KindMap(KindMap), - #[prost(message, tag = "3")] - KindStore(KindStore), - #[prost(message, tag = "10")] - KindBlockIndex(KindBlockIndex), - } -} -/// Clock is a pointer to a block with added timestamp -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Clock { - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - #[prost(uint64, tag = "2")] - pub number: u64, - #[prost(message, optional, tag = "3")] - pub timestamp: ::core::option::Option<::prost_types::Timestamp>, -} -/// BlockRef is a pointer to a block to which we don't know the timestamp -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockRef { - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - #[prost(uint64, tag = "2")] - pub number: u64, -} diff --git a/graph/src/substreams_rpc/codec.rs b/graph/src/substreams_rpc/codec.rs deleted file mode 100644 index d70a9e53762..00000000000 --- a/graph/src/substreams_rpc/codec.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[rustfmt::skip] -#[path = "sf.substreams.rpc.v2.rs"] -mod pbsubstreamsrpc; - -pub use pbsubstreamsrpc::*; diff --git a/graph/src/substreams_rpc/mod.rs b/graph/src/substreams_rpc/mod.rs deleted file mode 100644 index 38e96fd598d..00000000000 --- a/graph/src/substreams_rpc/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod codec; - -pub use codec::*; diff --git a/graph/src/substreams_rpc/sf.firehose.v2.rs b/graph/src/substreams_rpc/sf.firehose.v2.rs deleted file mode 100644 index 905a7038bf5..00000000000 --- a/graph/src/substreams_rpc/sf.firehose.v2.rs +++ /dev/null @@ -1,896 +0,0 @@ -// This file is @generated by prost-build. -/// Generated client implementations. -pub mod stream_client { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - #[derive(Debug, Clone)] - pub struct StreamClient { - inner: tonic::client::Grpc, - } - impl StreamClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl StreamClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + std::marker::Send + 'static, - ::Error: Into + std::marker::Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> StreamClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + std::marker::Send + std::marker::Sync, - { - StreamClient::new(InterceptedService::new(inner, interceptor)) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - pub async fn blocks( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response>, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/sf.firehose.v2.Stream/Blocks", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("sf.firehose.v2.Stream", "Blocks")); - self.inner.server_streaming(req, path, codec).await - } - } -} -/// Generated client implementations. -pub mod fetch_client { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - #[derive(Debug, Clone)] - pub struct FetchClient { - inner: tonic::client::Grpc, - } - impl FetchClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl FetchClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + std::marker::Send + 'static, - ::Error: Into + std::marker::Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> FetchClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + std::marker::Send + std::marker::Sync, - { - FetchClient::new(InterceptedService::new(inner, interceptor)) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - pub async fn block( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/sf.firehose.v2.Fetch/Block", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("sf.firehose.v2.Fetch", "Block")); - self.inner.unary(req, path, codec).await - } - } -} -/// Generated client implementations. -pub mod endpoint_info_client { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - #[derive(Debug, Clone)] - pub struct EndpointInfoClient { - inner: tonic::client::Grpc, - } - impl EndpointInfoClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl EndpointInfoClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + std::marker::Send + 'static, - ::Error: Into + std::marker::Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> EndpointInfoClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + std::marker::Send + std::marker::Sync, - { - EndpointInfoClient::new(InterceptedService::new(inner, interceptor)) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - pub async fn info( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/sf.firehose.v2.EndpointInfo/Info", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("sf.firehose.v2.EndpointInfo", "Info")); - self.inner.unary(req, path, codec).await - } - } -} -/// Generated server implementations. -pub mod stream_server { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with StreamServer. - #[async_trait] - pub trait Stream: std::marker::Send + std::marker::Sync + 'static { - /// Server streaming response type for the Blocks method. - type BlocksStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result, - > - + std::marker::Send - + 'static; - async fn blocks( - &self, - request: tonic::Request, - ) -> std::result::Result, tonic::Status>; - } - #[derive(Debug)] - pub struct StreamServer { - inner: Arc, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - impl StreamServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> for StreamServer - where - T: Stream, - B: Body + std::marker::Send + 'static, - B::Error: Into + std::marker::Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - match req.uri().path() { - "/sf.firehose.v2.Stream/Blocks" => { - #[allow(non_camel_case_types)] - struct BlocksSvc(pub Arc); - impl< - T: Stream, - > tonic::server::ServerStreamingService - for BlocksSvc { - type Response = crate::firehose::Response; - type ResponseStream = T::BlocksStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::blocks(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = BlocksSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.server_streaming(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - let mut response = http::Response::new(empty_body()); - let headers = response.headers_mut(); - headers - .insert( - tonic::Status::GRPC_STATUS, - (tonic::Code::Unimplemented as i32).into(), - ); - headers - .insert( - http::header::CONTENT_TYPE, - tonic::metadata::GRPC_CONTENT_TYPE, - ); - Ok(response) - }) - } - } - } - } - impl Clone for StreamServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - /// Generated gRPC service name - pub const SERVICE_NAME: &str = "sf.firehose.v2.Stream"; - impl tonic::server::NamedService for StreamServer { - const NAME: &'static str = SERVICE_NAME; - } -} -/// Generated server implementations. -pub mod fetch_server { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with FetchServer. - #[async_trait] - pub trait Fetch: std::marker::Send + std::marker::Sync + 'static { - async fn block( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - } - #[derive(Debug)] - pub struct FetchServer { - inner: Arc, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - impl FetchServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> for FetchServer - where - T: Fetch, - B: Body + std::marker::Send + 'static, - B::Error: Into + std::marker::Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - match req.uri().path() { - "/sf.firehose.v2.Fetch/Block" => { - #[allow(non_camel_case_types)] - struct BlockSvc(pub Arc); - impl< - T: Fetch, - > tonic::server::UnaryService - for BlockSvc { - type Response = crate::firehose::SingleBlockResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::block(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = BlockSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - let mut response = http::Response::new(empty_body()); - let headers = response.headers_mut(); - headers - .insert( - tonic::Status::GRPC_STATUS, - (tonic::Code::Unimplemented as i32).into(), - ); - headers - .insert( - http::header::CONTENT_TYPE, - tonic::metadata::GRPC_CONTENT_TYPE, - ); - Ok(response) - }) - } - } - } - } - impl Clone for FetchServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - /// Generated gRPC service name - pub const SERVICE_NAME: &str = "sf.firehose.v2.Fetch"; - impl tonic::server::NamedService for FetchServer { - const NAME: &'static str = SERVICE_NAME; - } -} -/// Generated server implementations. -pub mod endpoint_info_server { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with EndpointInfoServer. - #[async_trait] - pub trait EndpointInfo: std::marker::Send + std::marker::Sync + 'static { - async fn info( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - } - #[derive(Debug)] - pub struct EndpointInfoServer { - inner: Arc, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - impl EndpointInfoServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> for EndpointInfoServer - where - T: EndpointInfo, - B: Body + std::marker::Send + 'static, - B::Error: Into + std::marker::Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - match req.uri().path() { - "/sf.firehose.v2.EndpointInfo/Info" => { - #[allow(non_camel_case_types)] - struct InfoSvc(pub Arc); - impl< - T: EndpointInfo, - > tonic::server::UnaryService - for InfoSvc { - type Response = crate::firehose::InfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::info(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = InfoSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - let mut response = http::Response::new(empty_body()); - let headers = response.headers_mut(); - headers - .insert( - tonic::Status::GRPC_STATUS, - (tonic::Code::Unimplemented as i32).into(), - ); - headers - .insert( - http::header::CONTENT_TYPE, - tonic::metadata::GRPC_CONTENT_TYPE, - ); - Ok(response) - }) - } - } - } - } - impl Clone for EndpointInfoServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - /// Generated gRPC service name - pub const SERVICE_NAME: &str = "sf.firehose.v2.EndpointInfo"; - impl tonic::server::NamedService for EndpointInfoServer { - const NAME: &'static str = SERVICE_NAME; - } -} diff --git a/graph/src/substreams_rpc/sf.substreams.rpc.v2.rs b/graph/src/substreams_rpc/sf.substreams.rpc.v2.rs deleted file mode 100644 index ff69b343d29..00000000000 --- a/graph/src/substreams_rpc/sf.substreams.rpc.v2.rs +++ /dev/null @@ -1,946 +0,0 @@ -// This file is @generated by prost-build. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Request { - #[prost(int64, tag = "1")] - pub start_block_num: i64, - #[prost(string, tag = "2")] - pub start_cursor: ::prost::alloc::string::String, - #[prost(uint64, tag = "3")] - pub stop_block_num: u64, - /// With final_block_only, you only receive blocks that are irreversible: - /// 'final_block_height' will be equal to current block and no 'undo_signal' - /// will ever be sent - #[prost(bool, tag = "4")] - pub final_blocks_only: bool, - /// Substreams has two mode when executing your module(s) either development - /// mode or production mode. Development and production modes impact the - /// execution of Substreams, important aspects of execution include: - /// * The time required to reach the first byte. - /// * The speed that large ranges get executed. - /// * The module logs and outputs sent back to the client. - /// - /// By default, the engine runs in developer mode, with richer and deeper - /// output. Differences between production and development modes include: - /// * Forward parallel execution is enabled in production mode and disabled in - /// development mode - /// * The time required to reach the first byte in development mode is faster - /// than in production mode. - /// - /// Specific attributes of development mode include: - /// * The client will receive all of the executed module's logs. - /// * It's possible to request specific store snapshots in the execution tree - /// (via `debug_initial_store_snapshot_for_modules`). - /// * Multiple module's output is possible. - /// - /// With production mode`, however, you trade off functionality for high speed - /// enabling forward parallel execution of module ahead of time. - #[prost(bool, tag = "5")] - pub production_mode: bool, - #[prost(string, tag = "6")] - pub output_module: ::prost::alloc::string::String, - #[prost(message, optional, tag = "7")] - pub modules: ::core::option::Option, - /// Available only in developer mode - #[prost(string, repeated, tag = "10")] - pub debug_initial_store_snapshot_for_modules: ::prost::alloc::vec::Vec< - ::prost::alloc::string::String, - >, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Response { - #[prost(oneof = "response::Message", tags = "1, 2, 3, 4, 5, 10, 11")] - pub message: ::core::option::Option, -} -/// Nested message and enum types in `Response`. -pub mod response { - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Message { - /// Always sent first - #[prost(message, tag = "1")] - Session(super::SessionInit), - /// Progress of data preparation, before - #[prost(message, tag = "2")] - Progress(super::ModulesProgress), - /// sending in the stream of `data` events. - #[prost(message, tag = "3")] - BlockScopedData(super::BlockScopedData), - #[prost(message, tag = "4")] - BlockUndoSignal(super::BlockUndoSignal), - #[prost(message, tag = "5")] - FatalError(super::Error), - /// Available only in developer mode, and only if - /// `debug_initial_store_snapshot_for_modules` is set. - #[prost(message, tag = "10")] - DebugSnapshotData(super::InitialSnapshotData), - /// Available only in developer mode, and only if - /// `debug_initial_store_snapshot_for_modules` is set. - #[prost(message, tag = "11")] - DebugSnapshotComplete(super::InitialSnapshotComplete), - } -} -/// BlockUndoSignal informs you that every bit of data -/// with a block number above 'last_valid_block' has been reverted -/// on-chain. Delete that data and restart from 'last_valid_cursor' -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockUndoSignal { - #[prost(message, optional, tag = "1")] - pub last_valid_block: ::core::option::Option, - #[prost(string, tag = "2")] - pub last_valid_cursor: ::prost::alloc::string::String, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockScopedData { - #[prost(message, optional, tag = "1")] - pub output: ::core::option::Option, - #[prost(message, optional, tag = "2")] - pub clock: ::core::option::Option, - #[prost(string, tag = "3")] - pub cursor: ::prost::alloc::string::String, - /// Non-deterministic, allows substreams-sink to let go of their undo data. - #[prost(uint64, tag = "4")] - pub final_block_height: u64, - #[prost(message, repeated, tag = "10")] - pub debug_map_outputs: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "11")] - pub debug_store_outputs: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SessionInit { - #[prost(string, tag = "1")] - pub trace_id: ::prost::alloc::string::String, - #[prost(uint64, tag = "2")] - pub resolved_start_block: u64, - #[prost(uint64, tag = "3")] - pub linear_handoff_block: u64, - #[prost(uint64, tag = "4")] - pub max_parallel_workers: u64, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct InitialSnapshotComplete { - #[prost(string, tag = "1")] - pub cursor: ::prost::alloc::string::String, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct InitialSnapshotData { - #[prost(string, tag = "1")] - pub module_name: ::prost::alloc::string::String, - #[prost(message, repeated, tag = "2")] - pub deltas: ::prost::alloc::vec::Vec, - #[prost(uint64, tag = "4")] - pub sent_keys: u64, - #[prost(uint64, tag = "3")] - pub total_keys: u64, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MapModuleOutput { - #[prost(string, tag = "1")] - pub name: ::prost::alloc::string::String, - #[prost(message, optional, tag = "2")] - pub map_output: ::core::option::Option<::prost_types::Any>, - /// DebugOutputInfo is available in non-production mode only - #[prost(message, optional, tag = "10")] - pub debug_info: ::core::option::Option, -} -/// StoreModuleOutput are produced for store modules in development mode. -/// It is not possible to retrieve store models in production, with -/// parallelization enabled. If you need the deltas directly, write a pass -/// through mapper module that will get them down to you. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct StoreModuleOutput { - #[prost(string, tag = "1")] - pub name: ::prost::alloc::string::String, - #[prost(message, repeated, tag = "2")] - pub debug_store_deltas: ::prost::alloc::vec::Vec, - #[prost(message, optional, tag = "10")] - pub debug_info: ::core::option::Option, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct OutputDebugInfo { - #[prost(string, repeated, tag = "1")] - pub logs: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - /// LogsTruncated is a flag that tells you if you received all the logs or if - /// they were truncated because you logged too much (fixed limit currently is - /// set to 128 KiB). - #[prost(bool, tag = "2")] - pub logs_truncated: bool, - #[prost(bool, tag = "3")] - pub cached: bool, -} -/// ModulesProgress is a message that is sent every 500ms -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ModulesProgress { - /// List of jobs running on tier2 servers - #[prost(message, repeated, tag = "2")] - pub running_jobs: ::prost::alloc::vec::Vec, - /// Execution statistics for each module - #[prost(message, repeated, tag = "3")] - pub modules_stats: ::prost::alloc::vec::Vec, - /// Stages definition and completed block ranges - #[prost(message, repeated, tag = "4")] - pub stages: ::prost::alloc::vec::Vec, - #[prost(message, optional, tag = "5")] - pub processed_bytes: ::core::option::Option, -} -#[derive(Clone, Copy, PartialEq, ::prost::Message)] -pub struct ProcessedBytes { - #[prost(uint64, tag = "1")] - pub total_bytes_read: u64, - #[prost(uint64, tag = "2")] - pub total_bytes_written: u64, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Error { - #[prost(string, tag = "1")] - pub module: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub reason: ::prost::alloc::string::String, - #[prost(string, repeated, tag = "3")] - pub logs: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - /// FailureLogsTruncated is a flag that tells you if you received all the logs - /// or if they were truncated because you logged too much (fixed limit - /// currently is set to 128 KiB). - #[prost(bool, tag = "4")] - pub logs_truncated: bool, -} -#[derive(Clone, Copy, PartialEq, ::prost::Message)] -pub struct Job { - #[prost(uint32, tag = "1")] - pub stage: u32, - #[prost(uint64, tag = "2")] - pub start_block: u64, - #[prost(uint64, tag = "3")] - pub stop_block: u64, - #[prost(uint64, tag = "4")] - pub processed_blocks: u64, - #[prost(uint64, tag = "5")] - pub duration_ms: u64, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Stage { - #[prost(string, repeated, tag = "1")] - pub modules: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - #[prost(message, repeated, tag = "2")] - pub completed_ranges: ::prost::alloc::vec::Vec, -} -/// ModuleStats gathers metrics and statistics from each module, running on tier1 -/// or tier2 All the 'count' and 'time_ms' values may include duplicate for each -/// stage going over that module -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ModuleStats { - /// name of the module - #[prost(string, tag = "1")] - pub name: ::prost::alloc::string::String, - /// total_processed_blocks is the sum of blocks sent to that module code - #[prost(uint64, tag = "2")] - pub total_processed_block_count: u64, - /// total_processing_time_ms is the sum of all time spent running that module - /// code - #[prost(uint64, tag = "3")] - pub total_processing_time_ms: u64, - /// // external_calls are chain-specific intrinsics, like "Ethereum RPC calls". - #[prost(message, repeated, tag = "4")] - pub external_call_metrics: ::prost::alloc::vec::Vec, - /// total_store_operation_time_ms is the sum of all time spent running that - /// module code waiting for a store operation (ex: read, write, delete...) - #[prost(uint64, tag = "5")] - pub total_store_operation_time_ms: u64, - /// total_store_read_count is the sum of all the store Read operations called - /// from that module code - #[prost(uint64, tag = "6")] - pub total_store_read_count: u64, - /// total_store_write_count is the sum of all store Write operations called - /// from that module code (store-only) - #[prost(uint64, tag = "10")] - pub total_store_write_count: u64, - /// total_store_deleteprefix_count is the sum of all store DeletePrefix - /// operations called from that module code (store-only) note that DeletePrefix - /// can be a costly operation on large stores - #[prost(uint64, tag = "11")] - pub total_store_deleteprefix_count: u64, - /// store_size_bytes is the uncompressed size of the full KV store for that - /// module, from the last 'merge' operation (store-only) - #[prost(uint64, tag = "12")] - pub store_size_bytes: u64, - /// total_store_merging_time_ms is the time spent merging partial stores into a - /// full KV store for that module (store-only) - #[prost(uint64, tag = "13")] - pub total_store_merging_time_ms: u64, - /// store_currently_merging is true if there is a merging operation (partial - /// store to full KV store) on the way. - #[prost(bool, tag = "14")] - pub store_currently_merging: bool, - /// highest_contiguous_block is the highest block in the highest merged full KV - /// store of that module (store-only) - #[prost(uint64, tag = "15")] - pub highest_contiguous_block: u64, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ExternalCallMetric { - #[prost(string, tag = "1")] - pub name: ::prost::alloc::string::String, - #[prost(uint64, tag = "2")] - pub count: u64, - #[prost(uint64, tag = "3")] - pub time_ms: u64, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct StoreDelta { - #[prost(enumeration = "store_delta::Operation", tag = "1")] - pub operation: i32, - #[prost(uint64, tag = "2")] - pub ordinal: u64, - #[prost(string, tag = "3")] - pub key: ::prost::alloc::string::String, - #[prost(bytes = "vec", tag = "4")] - pub old_value: ::prost::alloc::vec::Vec, - #[prost(bytes = "vec", tag = "5")] - pub new_value: ::prost::alloc::vec::Vec, -} -/// Nested message and enum types in `StoreDelta`. -pub mod store_delta { - #[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - ::prost::Enumeration - )] - #[repr(i32)] - pub enum Operation { - Unset = 0, - Create = 1, - Update = 2, - Delete = 3, - } - impl Operation { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - Self::Unset => "UNSET", - Self::Create => "CREATE", - Self::Update => "UPDATE", - Self::Delete => "DELETE", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "UNSET" => Some(Self::Unset), - "CREATE" => Some(Self::Create), - "UPDATE" => Some(Self::Update), - "DELETE" => Some(Self::Delete), - _ => None, - } - } - } -} -#[derive(Clone, Copy, PartialEq, ::prost::Message)] -pub struct BlockRange { - #[prost(uint64, tag = "2")] - pub start_block: u64, - #[prost(uint64, tag = "3")] - pub end_block: u64, -} -/// Generated client implementations. -pub mod endpoint_info_client { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - #[derive(Debug, Clone)] - pub struct EndpointInfoClient { - inner: tonic::client::Grpc, - } - impl EndpointInfoClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl EndpointInfoClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + std::marker::Send + 'static, - ::Error: Into + std::marker::Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> EndpointInfoClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + std::marker::Send + std::marker::Sync, - { - EndpointInfoClient::new(InterceptedService::new(inner, interceptor)) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - pub async fn info( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/sf.substreams.rpc.v2.EndpointInfo/Info", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("sf.substreams.rpc.v2.EndpointInfo", "Info")); - self.inner.unary(req, path, codec).await - } - } -} -/// Generated client implementations. -pub mod stream_client { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - #[derive(Debug, Clone)] - pub struct StreamClient { - inner: tonic::client::Grpc, - } - impl StreamClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl StreamClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + std::marker::Send + 'static, - ::Error: Into + std::marker::Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> StreamClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + std::marker::Send + std::marker::Sync, - { - StreamClient::new(InterceptedService::new(inner, interceptor)) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - pub async fn blocks( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response>, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/sf.substreams.rpc.v2.Stream/Blocks", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("sf.substreams.rpc.v2.Stream", "Blocks")); - self.inner.server_streaming(req, path, codec).await - } - } -} -/// Generated server implementations. -pub mod endpoint_info_server { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with EndpointInfoServer. - #[async_trait] - pub trait EndpointInfo: std::marker::Send + std::marker::Sync + 'static { - async fn info( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - } - #[derive(Debug)] - pub struct EndpointInfoServer { - inner: Arc, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - impl EndpointInfoServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> for EndpointInfoServer - where - T: EndpointInfo, - B: Body + std::marker::Send + 'static, - B::Error: Into + std::marker::Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - match req.uri().path() { - "/sf.substreams.rpc.v2.EndpointInfo/Info" => { - #[allow(non_camel_case_types)] - struct InfoSvc(pub Arc); - impl< - T: EndpointInfo, - > tonic::server::UnaryService - for InfoSvc { - type Response = crate::firehose::InfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::info(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = InfoSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - let mut response = http::Response::new(empty_body()); - let headers = response.headers_mut(); - headers - .insert( - tonic::Status::GRPC_STATUS, - (tonic::Code::Unimplemented as i32).into(), - ); - headers - .insert( - http::header::CONTENT_TYPE, - tonic::metadata::GRPC_CONTENT_TYPE, - ); - Ok(response) - }) - } - } - } - } - impl Clone for EndpointInfoServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - /// Generated gRPC service name - pub const SERVICE_NAME: &str = "sf.substreams.rpc.v2.EndpointInfo"; - impl tonic::server::NamedService for EndpointInfoServer { - const NAME: &'static str = SERVICE_NAME; - } -} -/// Generated server implementations. -pub mod stream_server { - #![allow( - unused_variables, - dead_code, - missing_docs, - clippy::wildcard_imports, - clippy::let_unit_value, - )] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with StreamServer. - #[async_trait] - pub trait Stream: std::marker::Send + std::marker::Sync + 'static { - /// Server streaming response type for the Blocks method. - type BlocksStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result, - > - + std::marker::Send - + 'static; - async fn blocks( - &self, - request: tonic::Request, - ) -> std::result::Result, tonic::Status>; - } - #[derive(Debug)] - pub struct StreamServer { - inner: Arc, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - impl StreamServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> for StreamServer - where - T: Stream, - B: Body + std::marker::Send + 'static, - B::Error: Into + std::marker::Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - match req.uri().path() { - "/sf.substreams.rpc.v2.Stream/Blocks" => { - #[allow(non_camel_case_types)] - struct BlocksSvc(pub Arc); - impl tonic::server::ServerStreamingService - for BlocksSvc { - type Response = super::Response; - type ResponseStream = T::BlocksStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::blocks(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = BlocksSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.server_streaming(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - let mut response = http::Response::new(empty_body()); - let headers = response.headers_mut(); - headers - .insert( - tonic::Status::GRPC_STATUS, - (tonic::Code::Unimplemented as i32).into(), - ); - headers - .insert( - http::header::CONTENT_TYPE, - tonic::metadata::GRPC_CONTENT_TYPE, - ); - Ok(response) - }) - } - } - } - } - impl Clone for StreamServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - /// Generated gRPC service name - pub const SERVICE_NAME: &str = "sf.substreams.rpc.v2.Stream"; - impl tonic::server::NamedService for StreamServer { - const NAME: &'static str = SERVICE_NAME; - } -} diff --git a/node/src/chain.rs b/node/src/chain.rs index 26d915678f3..308740628be 100644 --- a/node/src/chain.rs +++ b/node/src/chain.rs @@ -130,7 +130,6 @@ pub fn create_firehose_networks( firehose.compression_enabled(), firehose.limit_for(&config.node), endpoint_metrics.cheap_clone(), - false, ))); } } @@ -394,7 +393,7 @@ pub async fn networks_as_chains( firehose_endpoints, metrics_registry: metrics_registry.clone(), } - .build(config) + .build() .await, ), ); diff --git a/substreams/substreams-head-tracker/Cargo.lock b/substreams/substreams-head-tracker/Cargo.lock deleted file mode 100755 index 92ad0a04eef..00000000000 --- a/substreams/substreams-head-tracker/Cargo.lock +++ /dev/null @@ -1,583 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "bigdecimal" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fastrand" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "linux-raw-sys" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "pad" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "petgraph" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "proc-macro2" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regex" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "rustix" -version = "0.38.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bfe0f2582b4931a45d1fa608f8a8722e8b3c7ac54dd6d5f3b3212791fedef49" -dependencies = [ - "bitflags 2.4.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "substreams" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af972e374502cdfc9998132f5343848d1c58f27a295dc061a89804371f408a46" -dependencies = [ - "anyhow", - "bigdecimal", - "hex", - "hex-literal", - "num-bigint", - "num-traits", - "pad", - "prost", - "prost-build", - "prost-types", - "substreams-macro", - "thiserror", -] - -[[package]] -name = "substreams-entity-change" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d423d0c12a9284a3d6d4ec288dbc9bfec3d55f9056098ba91a6dcfa64fb3889e" -dependencies = [ - "base64", - "prost", - "prost-types", - "substreams", -] - -[[package]] -name = "substreams-head-tracker" -version = "1.0.0" -dependencies = [ - "prost", - "substreams", - "substreams-entity-change", -] - -[[package]] -name = "substreams-macro" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6521ccd011a4c3f52cd3c31fc7400733e4feba2094e0e0e6354adca25b2b3f37" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "thiserror", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys", -] - -[[package]] -name = "thiserror" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/substreams/substreams-head-tracker/Cargo.toml b/substreams/substreams-head-tracker/Cargo.toml deleted file mode 100755 index 2548160f736..00000000000 --- a/substreams/substreams-head-tracker/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "substreams-head-tracker" -version.workspace = true -edition.workspace = true - -[lib] -crate-type = ["cdylib"] - diff --git a/substreams/substreams-head-tracker/Makefile b/substreams/substreams-head-tracker/Makefile deleted file mode 100755 index 9ef9e5c3f70..00000000000 --- a/substreams/substreams-head-tracker/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -ENDPOINT ?= mainnet.eth.streamingfast.io:443 -START_BLOCK ?= 16000000 -STOP_BLOCK ?= +100 - -.PHONY: build -build: - cargo build --target wasm32-unknown-unknown --release - -.PHONY: run -run: build - substreams run -e $(ENDPOINT) substreams.yaml map_blocks -s $(START_BLOCK) -t $(STOP_BLOCK) - -.PHONY: pack -pack: build - substreams pack substreams.yaml diff --git a/substreams/substreams-head-tracker/rust-toolchain.toml b/substreams/substreams-head-tracker/rust-toolchain.toml deleted file mode 100755 index a09cf93404f..00000000000 --- a/substreams/substreams-head-tracker/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -targets = [ "wasm32-unknown-unknown" ] \ No newline at end of file diff --git a/substreams/substreams-head-tracker/src/lib.rs b/substreams/substreams-head-tracker/src/lib.rs deleted file mode 100644 index ee880963011..00000000000 --- a/substreams/substreams-head-tracker/src/lib.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![cfg(target_arch = "wasm32")] - -#[no_mangle] -pub extern "C" fn map_blocks(_params_ptr: *mut u8, _params_len: usize) {} - -#[no_mangle] -pub fn alloc(size: usize) -> *mut u8 { - let mut buf = Vec::with_capacity(size); - let ptr = buf.as_mut_ptr(); - - // Runtime is responsible of calling dealloc when no longer needed - std::mem::forget(buf); - ptr -} - -#[no_mangle] -pub unsafe fn dealloc(ptr: *mut u8, size: usize) { - std::mem::drop(Vec::from_raw_parts(ptr, size, size)) -} diff --git a/substreams/substreams-head-tracker/substreams-head-tracker-v1.0.0.spkg b/substreams/substreams-head-tracker/substreams-head-tracker-v1.0.0.spkg deleted file mode 100644 index 2e44fdf53c64cb88bcc76caa927de5c3860b144b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89935 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 /dev/null || true - bun x graph build --ipfs http://localhost:5001 subgraph.yaml - bun x graph create map_block --node http://127.0.0.1:8020 - bun x graph deploy --node http://127.0.0.1:8020 --ipfs http://127.0.0.1:5001 --version-label v0.0.1 map_block subgraph.yaml - -.PHONY: undeploy_local -undeploy_local: - graphman --config "$(GRAPH_CONFIG)" drop --force uniswap_v3 - diff --git a/substreams/substreams-trigger-filter/build.rs b/substreams/substreams-trigger-filter/build.rs deleted file mode 100644 index 22b972babc5..00000000000 --- a/substreams/substreams-trigger-filter/build.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=proto"); - tonic_build::configure() - .protoc_arg("--experimental_allow_proto3_optional") - .extern_path( - ".sf.near.codec.v1", - "::substreams_near_core::pb::sf::near::type::v1", - ) - .out_dir("src/pb") - .compile_protos(&["proto/receipts.proto"], &["proto"]) - .expect("Failed to compile Substreams entity proto(s)"); -} diff --git a/substreams/substreams-trigger-filter/bun.lockb b/substreams/substreams-trigger-filter/bun.lockb deleted file mode 100755 index 7f816d7b37de6928a634997cbe0c61ec24a55907..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197771 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 F`+xSMJh1=( diff --git a/substreams/substreams-trigger-filter/package.json b/substreams/substreams-trigger-filter/package.json deleted file mode 100644 index 00b628b1e1b..00000000000 --- a/substreams/substreams-trigger-filter/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "dependencies": { "@graphprotocol/graph-cli": "^0.92.0" } } \ No newline at end of file diff --git a/substreams/substreams-trigger-filter/proto/near.proto b/substreams/substreams-trigger-filter/proto/near.proto deleted file mode 100644 index 22a0267669a..00000000000 --- a/substreams/substreams-trigger-filter/proto/near.proto +++ /dev/null @@ -1,521 +0,0 @@ -syntax = "proto3"; - -package sf.near.codec.v1; - -option go_package = "github.com/streamingfast/sf-near/pb/sf/near/codec/v1;pbcodec"; - -message Block { - string author = 1; - BlockHeader header = 2; - repeated ChunkHeader chunk_headers = 3; - repeated IndexerShard shards = 4; - repeated StateChangeWithCause state_changes = 5; -} - -// HeaderOnlyBlock is a standard [Block] structure where all other fields are -// removed so that hydrating that object from a [Block] bytes payload will -// drastically reduced allocated memory required to hold the full block. -// -// This can be used to unpack a [Block] when only the [BlockHeader] information -// is required and greatly reduced required memory. -message HeaderOnlyBlock { - BlockHeader header = 2; -} - -message StateChangeWithCause { - StateChangeValue value = 1; - StateChangeCause cause = 2; -} - -message StateChangeCause { - oneof cause { - NotWritableToDisk not_writable_to_disk = 1; - InitialState initial_state = 2; - TransactionProcessing transaction_processing = 3; - ActionReceiptProcessingStarted action_receipt_processing_started = 4; - ActionReceiptGasReward action_receipt_gas_reward = 5; - ReceiptProcessing receipt_processing = 6; - PostponedReceipt postponed_receipt = 7; - UpdatedDelayedReceipts updated_delayed_receipts = 8; - ValidatorAccountsUpdate validator_accounts_update = 9; - Migration migration = 10; - } - - message NotWritableToDisk {} - message InitialState {} - message TransactionProcessing {CryptoHash tx_hash = 1;} - message ActionReceiptProcessingStarted {CryptoHash receipt_hash = 1;} - message ActionReceiptGasReward {CryptoHash tx_hash = 1;} - message ReceiptProcessing {CryptoHash tx_hash = 1;} - message PostponedReceipt {CryptoHash tx_hash = 1;} - message UpdatedDelayedReceipts {} - message ValidatorAccountsUpdate {} - message Migration {} -} - -message StateChangeValue { - oneof value { - AccountUpdate account_update = 1; - AccountDeletion account_deletion = 2; - AccessKeyUpdate access_key_update = 3; - AccessKeyDeletion access_key_deletion = 4; - DataUpdate data_update = 5; - DataDeletion data_deletion = 6; - ContractCodeUpdate contract_code_update = 7; - ContractCodeDeletion contract_deletion = 8; - } - - message AccountUpdate {string account_id = 1; Account account = 2;} - message AccountDeletion {string account_id = 1;} - message AccessKeyUpdate { - string account_id = 1; - PublicKey public_key = 2; - AccessKey access_key = 3; - } - message AccessKeyDeletion { - string account_id = 1; - PublicKey public_key = 2; - } - message DataUpdate { - string account_id = 1; - bytes key = 2; - bytes value = 3; - } - message DataDeletion { - string account_id = 1; - bytes key = 2; - } - message ContractCodeUpdate { - string account_id = 1; - bytes code = 2; - } - message ContractCodeDeletion { - string account_id = 1; - } -} - -message Account { - BigInt amount = 1; - BigInt locked = 2; - CryptoHash code_hash = 3; - uint64 storage_usage = 4; -} - -message BlockHeader { - uint64 height = 1; - uint64 prev_height = 2; - CryptoHash epoch_id = 3; - CryptoHash next_epoch_id = 4; - CryptoHash hash = 5; - CryptoHash prev_hash = 6; - CryptoHash prev_state_root = 7; - CryptoHash chunk_receipts_root = 8; - CryptoHash chunk_headers_root = 9; - CryptoHash chunk_tx_root = 10; - CryptoHash outcome_root = 11; - uint64 chunks_included = 12; - CryptoHash challenges_root = 13; - uint64 timestamp = 14; - uint64 timestamp_nanosec = 15; - CryptoHash random_value = 16; - repeated ValidatorStake validator_proposals = 17; - repeated bool chunk_mask = 18; - BigInt gas_price = 19; - uint64 block_ordinal = 20; - BigInt total_supply = 21; - repeated SlashedValidator challenges_result = 22; - uint64 last_final_block_height = 23; - CryptoHash last_final_block = 24; - uint64 last_ds_final_block_height = 25; - CryptoHash last_ds_final_block = 26; - CryptoHash next_bp_hash = 27; - CryptoHash block_merkle_root = 28; - bytes epoch_sync_data_hash = 29; - repeated Signature approvals = 30; - Signature signature = 31; - uint32 latest_protocol_version = 32; -} - -message BigInt { - bytes bytes = 1; -} -message CryptoHash { - bytes bytes = 1; -} - -enum CurveKind { - ED25519 = 0; - SECP256K1 = 1; -} - -message Signature { - CurveKind type = 1; - bytes bytes = 2; -} - -message PublicKey { - CurveKind type = 1; - bytes bytes = 2; -} - -message ValidatorStake { - string account_id = 1; - PublicKey public_key = 2; - BigInt stake = 3; -} - -message SlashedValidator { - string account_id = 1; - bool is_double_sign = 2; -} - -message ChunkHeader { - bytes chunk_hash = 1; - bytes prev_block_hash = 2; - bytes outcome_root = 3; - bytes prev_state_root = 4; - bytes encoded_merkle_root = 5; - uint64 encoded_length = 6; - uint64 height_created = 7; - uint64 height_included = 8; - uint64 shard_id = 9; - uint64 gas_used = 10; - uint64 gas_limit = 11; - BigInt validator_reward = 12; - BigInt balance_burnt = 13; - bytes outgoing_receipts_root = 14; - bytes tx_root = 15; - repeated ValidatorStake validator_proposals = 16; - Signature signature = 17; -} - -message IndexerShard { - uint64 shard_id = 1; - IndexerChunk chunk = 2; - repeated IndexerExecutionOutcomeWithReceipt receipt_execution_outcomes = 3; -} - -message IndexerExecutionOutcomeWithReceipt { - ExecutionOutcomeWithId execution_outcome = 1; - Receipt receipt = 2; -} - -message IndexerChunk { - string author = 1; - ChunkHeader header = 2; - repeated IndexerTransactionWithOutcome transactions = 3; - repeated Receipt receipts = 4; -} - -message IndexerTransactionWithOutcome { - SignedTransaction transaction = 1; - IndexerExecutionOutcomeWithOptionalReceipt outcome = 2; -} - -message SignedTransaction { - string signer_id = 1; - PublicKey public_key = 2; - uint64 nonce = 3; - string receiver_id = 4; - repeated Action actions = 5; - Signature signature = 6; - CryptoHash hash = 7; -} - -message IndexerExecutionOutcomeWithOptionalReceipt { - ExecutionOutcomeWithId execution_outcome = 1; - Receipt receipt = 2; -} - -message Receipt { - string predecessor_id = 1; - string receiver_id = 2; - CryptoHash receipt_id = 3; - - oneof receipt { - ReceiptAction action = 10; - ReceiptData data = 11; - } -} - -message ReceiptData { - CryptoHash data_id = 1; - bytes data = 2; -} - -message ReceiptAction { - string signer_id = 1; - PublicKey signer_public_key = 2; - BigInt gas_price = 3; - repeated DataReceiver output_data_receivers = 4; - repeated CryptoHash input_data_ids = 5; - repeated Action actions = 6; -} - -message DataReceiver { - CryptoHash data_id = 1; - string receiver_id = 2; -} - -message ExecutionOutcomeWithId { - MerklePath proof = 1; - CryptoHash block_hash = 2; - CryptoHash id = 3; - ExecutionOutcome outcome = 4; -} - -message ExecutionOutcome { - repeated string logs = 1; - repeated CryptoHash receipt_ids = 2; - uint64 gas_burnt = 3; - BigInt tokens_burnt = 4; - string executor_id = 5; - oneof status { - UnknownExecutionStatus unknown = 20; - FailureExecutionStatus failure = 21; - SuccessValueExecutionStatus success_value = 22; - SuccessReceiptIdExecutionStatus success_receipt_id = 23; - } - ExecutionMetadata metadata = 6; -} - -enum ExecutionMetadata { - ExecutionMetadataV1 = 0; -} - -message SuccessValueExecutionStatus { - bytes value = 1; -} - -message SuccessReceiptIdExecutionStatus { - CryptoHash id = 1; -} - -message UnknownExecutionStatus {} -message FailureExecutionStatus { - oneof failure { - ActionError action_error = 1; - InvalidTxError invalid_tx_error = 2; - } -} - -message ActionError { - uint64 index = 1; - oneof kind { - AccountAlreadyExistsErrorKind account_already_exist = 21; - AccountDoesNotExistErrorKind account_does_not_exist = 22; - CreateAccountOnlyByRegistrarErrorKind create_account_only_by_registrar = 23; - CreateAccountNotAllowedErrorKind create_account_not_allowed = 24; - ActorNoPermissionErrorKind actor_no_permission =25; - DeleteKeyDoesNotExistErrorKind delete_key_does_not_exist = 26; - AddKeyAlreadyExistsErrorKind add_key_already_exists = 27; - DeleteAccountStakingErrorKind delete_account_staking = 28; - LackBalanceForStateErrorKind lack_balance_for_state = 29; - TriesToUnstakeErrorKind tries_to_unstake = 30; - TriesToStakeErrorKind tries_to_stake = 31; - InsufficientStakeErrorKind insufficient_stake = 32; - FunctionCallErrorKind function_call = 33; - NewReceiptValidationErrorKind new_receipt_validation = 34; - OnlyImplicitAccountCreationAllowedErrorKind only_implicit_account_creation_allowed = 35; - DeleteAccountWithLargeStateErrorKind delete_account_with_large_state = 36; - } -} - -message AccountAlreadyExistsErrorKind { - string account_id = 1; -} - -message AccountDoesNotExistErrorKind { - string account_id = 1; -} - -/// A top-level account ID can only be created by registrar. -message CreateAccountOnlyByRegistrarErrorKind{ - string account_id = 1; - string registrar_account_id = 2; - string predecessor_id = 3; -} - -message CreateAccountNotAllowedErrorKind{ - string account_id = 1; - string predecessor_id = 2; -} - -message ActorNoPermissionErrorKind{ - string account_id = 1; - string actor_id = 2; -} - -message DeleteKeyDoesNotExistErrorKind{ - string account_id = 1; - PublicKey public_key = 2; -} - -message AddKeyAlreadyExistsErrorKind{ - string account_id = 1; - PublicKey public_key = 2; -} - -message DeleteAccountStakingErrorKind{ - string account_id = 1; -} - -message LackBalanceForStateErrorKind{ - string account_id = 1; - BigInt balance = 2; -} - -message TriesToUnstakeErrorKind{ - string account_id = 1; -} - -message TriesToStakeErrorKind{ - string account_id = 1; - BigInt stake = 2; - BigInt locked = 3; - BigInt balance = 4; -} - -message InsufficientStakeErrorKind{ - string account_id = 1; - BigInt stake = 2; - BigInt minimum_stake = 3; -} - -message FunctionCallErrorKind { - FunctionCallErrorSer error = 1; -} - -enum FunctionCallErrorSer { //todo: add more detail? - CompilationError = 0; - LinkError = 1; - MethodResolveError = 2; - WasmTrap = 3; - WasmUnknownError = 4; - HostError = 5; - _EVMError = 6; - ExecutionError = 7; -} - -message NewReceiptValidationErrorKind { - ReceiptValidationError error = 1; -} - -enum ReceiptValidationError { //todo: add more detail? - InvalidPredecessorId = 0; - InvalidReceiverAccountId = 1; - InvalidSignerAccountId = 2; - InvalidDataReceiverId = 3; - ReturnedValueLengthExceeded = 4; - NumberInputDataDependenciesExceeded = 5; - ActionsValidationError = 6; -} - -message OnlyImplicitAccountCreationAllowedErrorKind{ - string account_id = 1; -} - -message DeleteAccountWithLargeStateErrorKind{ - string account_id = 1; -} - -enum InvalidTxError { //todo: add more detail? - InvalidAccessKeyError = 0; - InvalidSignerId = 1; - SignerDoesNotExist = 2; - InvalidNonce = 3; - NonceTooLarge = 4; - InvalidReceiverId = 5; - InvalidSignature = 6; - NotEnoughBalance = 7; - LackBalanceForState = 8; - CostOverflow = 9; - InvalidChain = 10; - Expired = 11; - ActionsValidation = 12; - TransactionSizeExceeded = 13; -} - -message MerklePath { - repeated MerklePathItem path = 1; -} - -message MerklePathItem { - CryptoHash hash = 1; - Direction direction = 2; -} - -enum Direction { - left = 0; - right = 1; -} - -message Action { - oneof action { - CreateAccountAction create_account = 1; - DeployContractAction deploy_contract = 2; - FunctionCallAction function_call = 3; - TransferAction transfer = 4; - StakeAction stake = 5; - AddKeyAction add_key = 6; - DeleteKeyAction delete_key = 7; - DeleteAccountAction delete_account = 8; - } -} - -message CreateAccountAction { -} - -message DeployContractAction { - bytes code = 1; -} - -message FunctionCallAction { - string method_name = 1; - bytes args = 2; - uint64 gas = 3; - BigInt deposit = 4; -} - -message TransferAction { - BigInt deposit = 1; -} - -message StakeAction { - BigInt stake = 1; - PublicKey public_key = 2; -} - -message AddKeyAction { - PublicKey public_key = 1; - AccessKey access_key = 2; -} - -message DeleteKeyAction { - PublicKey public_key = 1; -} - -message DeleteAccountAction { - string beneficiary_id = 1; -} - -message AccessKey { - uint64 nonce = 1; - AccessKeyPermission permission = 2; -} - -message AccessKeyPermission { - oneof permission { - FunctionCallPermission function_call = 1; - FullAccessPermission full_access = 2; - } -} - -message FunctionCallPermission { - BigInt allowance = 1; - string receiver_id = 2; - repeated string method_names = 3; -} - -message FullAccessPermission { -} diff --git a/substreams/substreams-trigger-filter/proto/receipts.proto b/substreams/substreams-trigger-filter/proto/receipts.proto deleted file mode 100755 index d7e4a822573..00000000000 --- a/substreams/substreams-trigger-filter/proto/receipts.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto3"; - -import "near.proto"; - -package receipts.v1; - -message BlockAndReceipts { - sf.near.codec.v1.Block block = 1; - repeated sf.near.codec.v1.ExecutionOutcomeWithId outcome = 2; - repeated sf.near.codec.v1.Receipt receipt = 3; -} - - - - diff --git a/substreams/substreams-trigger-filter/rust-toolchain.toml b/substreams/substreams-trigger-filter/rust-toolchain.toml deleted file mode 100755 index fde0e8fe57c..00000000000 --- a/substreams/substreams-trigger-filter/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -targets = [ "wasm32-unknown-unknown" ] diff --git a/substreams/substreams-trigger-filter/schema.graphql b/substreams/substreams-trigger-filter/schema.graphql deleted file mode 100644 index 20e5f730423..00000000000 --- a/substreams/substreams-trigger-filter/schema.graphql +++ /dev/null @@ -1,4 +0,0 @@ -type Block @entity { - id: Bytes! -} - diff --git a/substreams/substreams-trigger-filter/src/lib.rs b/substreams/substreams-trigger-filter/src/lib.rs deleted file mode 100755 index 01109234fdd..00000000000 --- a/substreams/substreams-trigger-filter/src/lib.rs +++ /dev/null @@ -1,99 +0,0 @@ -#![allow(clippy::not_unsafe_ptr_arg_deref)] - -mod pb; - -use pb::receipts::v1::BlockAndReceipts; -use substreams_entity_change::pb::entity::EntityChanges; -use substreams_near_core::pb::sf::near::r#type::v1::{ - execution_outcome, receipt::Receipt, Block, IndexerExecutionOutcomeWithReceipt, -}; -use trigger_filters::NearFilter; - -fn status(outcome: &IndexerExecutionOutcomeWithReceipt) -> Option<&execution_outcome::Status> { - outcome - .execution_outcome - .as_ref() - .and_then(|o| o.outcome.as_ref()) - .and_then(|o| o.status.as_ref()) -} - -fn is_success(outcome: &IndexerExecutionOutcomeWithReceipt) -> bool { - status(outcome) - .map(|s| { - use execution_outcome::Status::*; - - match s { - Unknown(_) | Failure(_) => false, - SuccessValue(_) | SuccessReceiptId(_) => true, - } - }) - .unwrap_or(false) -} - -#[substreams::handlers::map] -fn near_filter(params: String, blk: Block) -> Result { - let mut blk = blk; - let filter = NearFilter::try_from(params.as_str())?; - let mut out = BlockAndReceipts::default(); - - blk.shards = blk - .shards - .into_iter() - .map(|shard| { - let mut shard = shard; - let receipt_execution_outcomes = shard - .receipt_execution_outcomes - .into_iter() - .filter(|outcome| { - if !is_success(&outcome) { - return false; - } - - let execution_outcome = match outcome.execution_outcome.as_ref() { - Some(eo) => eo, - None => return false, - }; - - let receipt = match outcome.receipt.as_ref() { - Some(receipt) => receipt, - None => return false, - }; - - if !matches!(receipt.receipt, Some(Receipt::Action(_))) { - return false; - } - - if !filter.matches(&receipt.receiver_id) { - return false; - } - - out.outcome.push(execution_outcome.clone()); - out.receipt.push(receipt.clone()); - true - }) - .collect(); - shard.receipt_execution_outcomes = receipt_execution_outcomes; - shard - }) - .collect(); - - out.block = Some(blk.clone()); - - Ok(out) -} - -#[substreams::handlers::map] -fn graph_out(blk: Block) -> Result { - let mut out = EntityChanges::default(); - - let hex = hex::encode(&blk.header.as_ref().unwrap().hash.as_ref().unwrap().bytes); - - out.push_change( - "Block", - &hex, - blk.header.unwrap().height, - substreams_entity_change::pb::entity::entity_change::Operation::Create, - ); - - Ok(out) -} diff --git a/substreams/substreams-trigger-filter/src/pb/mod.rs b/substreams/substreams-trigger-filter/src/pb/mod.rs deleted file mode 100755 index be6467ea7fd..00000000000 --- a/substreams/substreams-trigger-filter/src/pb/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -// @generated -pub mod receipts { - // @@protoc_insertion_point(attribute:receipts.v1) - pub mod v1 { - include!("receipts.v1.rs"); - // @@protoc_insertion_point(receipts.v1) - } -} diff --git a/substreams/substreams-trigger-filter/src/pb/receipts.v1.rs b/substreams/substreams-trigger-filter/src/pb/receipts.v1.rs deleted file mode 100644 index 76b6d1fe456..00000000000 --- a/substreams/substreams-trigger-filter/src/pb/receipts.v1.rs +++ /dev/null @@ -1,16 +0,0 @@ -// This file is @generated by prost-build. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockAndReceipts { - #[prost(message, optional, tag = "1")] - pub block: ::core::option::Option< - ::substreams_near_core::pb::sf::near::r#type::v1::Block, - >, - #[prost(message, repeated, tag = "2")] - pub outcome: ::prost::alloc::vec::Vec< - ::substreams_near_core::pb::sf::near::r#type::v1::ExecutionOutcomeWithId, - >, - #[prost(message, repeated, tag = "3")] - pub receipt: ::prost::alloc::vec::Vec< - ::substreams_near_core::pb::sf::near::r#type::v1::Receipt, - >, -} diff --git a/substreams/substreams-trigger-filter/subgraph.yaml b/substreams/substreams-trigger-filter/subgraph.yaml deleted file mode 100644 index 88bf9ebcd1e..00000000000 --- a/substreams/substreams-trigger-filter/subgraph.yaml +++ /dev/null @@ -1,16 +0,0 @@ -specVersion: 0.0.5 -description: NEAR Blocks Indexing -repository: git@github.com:streamingfast/graph-node-dev.git -schema: - file: ./schema.graphql -dataSources: - - kind: substreams - name: hello-world - network: near-mainnet - source: - package: - moduleName: graph_out - file: substreams-near-hello-world-v0.1.0.spkg - mapping: - kind: substreams/graph-entities - apiVersion: 0.0.7 diff --git a/substreams/substreams-trigger-filter/substreams-trigger-filter-v0.1.0.spkg b/substreams/substreams-trigger-filter/substreams-trigger-filter-v0.1.0.spkg deleted file mode 100644 index f1e733c6675940e6911ed11c4946ce6b54835163..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 510162 zcmd?SdvsmLecy|-&jY{)NP>ryL_x1D=)s3TP^4tbrYtKU08)@a06739Q>g_V00$%_ z00+SVMA31YHjdrcS*fmF+j5=6>Q$Whk=4XEPIBX>%eiq@m*X}`>!fM(xNF_JY`52T z9VfEmJZz`FpYLyG_TG3fR1&NGqgb|hX78Eb{N^{m`OWV&v*X`+TQN>9G@BQe8pl@G zn(NKkjfG?Ljn>@S;_7;HZNQ#Y_qZCaM2)_)@!r#mOO4^Q;#6b3`l`6Jz{3NDqCZ>!a~v{GLd#KNJ@n*FXGh}&1|b5GSTG!8`eI25Zl z$MJk)wXrhaSed(gpa>S7wcL~HUGd)4joGEextUzGm}=!(k9#qkHm@Fx_gre!pUPM5 zvZ}lFI9G8f?p|)RTHrIYetETVpk$*@wv9fcl^c1NZ#7M$>R{a2SlL*15XuH&JN-te zu@y>(nt2fIt;X8Zi*t_0Kv=*By2igtD9ovAMUTiGQ zZv~OHtG+UBZ?3Kbp>?2x`RO`w;|?H}8WXNgEmW?K#yzd(#@bwCX0AEkm|0v|XddV^ z0Jm%dFl7~os5eTr+HTh{l&jaqmDc5z_4+dhx{X78sqf>Jc+VEhcVw~qy3HG5#r^p6IbV{*frYDQjRJAwk-Afcu&d?Gi&t~ zmYn0wW7}qA3t0z70w2}XthPIaW98~_95+`Q%>|a%+!B1UGnpq;Ween5r!^d=UG>!& ztF>Q&Q_i}jxsBV-#k81LRpp6dR%+*+@%t(%}L*Ic366BiL?CgsXC{j7dhHsGQS_|9$UZic*d+`ae3 zyEYBrrFPx-rudp2L04ZN@84KiTv>0dt*$l1v2OJr`2U;$+0Jw4^0gg;wSAjFxQ9nN zJHF7~^N;w8`hKkv@83!A?6`bc{s_g{#u|G-0padT2-)&deYUZLh%Ox3ed4ZdwA-nN zfiX3yxt7(}#w8Q-WrJ|!r9segtXaG24RNR1JKm@Oq_ftc2jYurZ`@(aWWEu$wzOP@ zvE~~K^^K+VnWyVZ8x6#>8h87LkLX$TmblBUszvs|XH0sQYi^;9>WR94vejG(tZCir zfpvdh##~Nt-?a1aw(^v=N>dZ=@l#)4IP!W7nX*)b~qKToN&=d*ZI?^OGYp z!xQIDjg9mas=MRZ9-JPV7@Y2j((>r|^egY}DW(n1xd)|ug?mo)lvyj?4#4Q^M}}!q z+5Ch$?bTgzr`0(%F)`NDk;0ssu8oeL>FG>io~ccoo9u~G2xmv8rUuW9^mL^)PMx0~ znd<3IK?p6n*p+rMGJfuC&+h8pc$f7!by{dqW6zdHVA_*HnQ~C}ZhAnCYTr;?Ha3La zcaII88X23Jn4BJ+7#|#icImU)$akL`t&I#rzudFQk-=%675nDm{W}TN9hXp!7o=o$ zo!HI`25T!X^nEUh_wAsk?AXbJaoO>*+hz}MV>baZ&fdBQxORC}!->P~s)j&pMY8&a zZNlH}kFVSjgDss82;?RiT1#dA6*d8Fnp-!4`{TXa0KH^k{N_S@;7Y{Vk<^(;)O2Pd z@&0VhI||;qBMEF0wvw^$gN69oomI*mad>T9S#GRfYzDPz``&fdUT>jy&9}<$_72w_ z^g^fLZo~9#QGCsgz;{ISrZ`?)S>0H7x|cIDI_-%GFG<;r_4K`P!On1xtir1@qH3q< zI&t$hqB>-@;#s{r?wMO!Y^0u3SH%-qGs z+*30f>kF?sa6`U-!w|YqXGm4fQKLE)cQ>(2mKWa$&doL3LwBFpE}C)_91tbd;4JpY z$@0|X$jET5D?mBjT&u!R7n&)7yNqg`7n*^*5NmF3#>R|os`9r_tb9Y;d;`bWD7QAH$JNW*d#rBiarN@nX7|PWS1+z^Z+9f$u1b}y4ev0^ ze62CZoX#J(E?->+x_?m}fc@uY8Y}f#IHtbFkJf=(ta536Z38P}Zf?Y04%)lwk$7)& z_Q^TNku!iS4$4Pr!Jq+yD#roQ+DL*Z^y>Z|USJge24rGtDB3hRg z7uEyBgI2A}o_T18;vP}LmO`j@$g1t;dA?s1H`Ua7ah)EvYTZ1{db~HjR+SKs_4)dG zJy*NmYK!cM411Zi0?)OL*~=+o9d&r>SzxV~O*!v78F%OTzS1l4S8bb4f_c5Zxn^z2AaF{ju+TTBZbpqRN;T8;IYON(o^ zAeZaTNJ{0~eqRd*)T7j4xlyG$7T>bcL?j|O>TB~@nZcnoQ=emPwOHAb>nW5QSDI74 za<=^p`l{8<>??tvHq5gx+VygM75-gcyKMS-N3DYgBP#z7F_WNXsi(|;%RLqTTj^=% zza2fD{I|0w?t5n`?lu!g6Ni6B2GN|X4>LP=pSW$?HkGeEFr>}xWW}k=HP=$E7M9Xe ztZJiY&6lcY;>wdPgRR|Y_m18`JS-lQfrqCIc;~}YGvgDrvx8$b-@JN#Tw1EX@$%+v z&>kT!_H3*?-B?>(Sj6nKT6^+yvpah`28fqBjr1rN-{#HA9=&{%*mo>08-1&B>?z#S zR_%yOLld=OmK1Bq(S2rebYzHC)%S|H;)tf@mXeJ|uIP;#Ta0>p?(C_NT2Hij>A@_0 zE!MHzEP*FpHi?nzy6^ug#9cYfT!w3XX{mW>roOa@%y+C5+oM4}d8t$PKLUBTy!zf> zhcQppYiDqoDX7fo9*27vkMsSJ)Z7TmPq`_y0!mu1#9-mN~v6cHbNCU7T+$ zuQqX1&t0BbYCMfcr_Y4UcDLlF9tK9Uc4KOGa^L9i$l1w>>5=iF^E2nhUpGGS=y2u;yvRN?5b?7Bd1S~3{6it&q+E6JSX{8_P%1gZwF@^ zV&6qooY{SJo7CT-&49FyNj#K32!l2Sl~&dzde^*1${9j?vgghz)gM<^o2|w5#i!-e z5AG`&MkT^7{>|vhdRlv>aiPAYwrrqcttY%mYoh_pHwo!9JPO)t3biix%mI`#=j?!| zQ@>evt;;=gRq$WcFI-qtw*io|Y<8!Qtzz%PaYvwoTxl!N1m(-zwh=mKhaQMX(jXUG zGv>^*SW1*cxWl>@Td51R?}Mm3o5L7bKOIX=RAfb8Tnz2dwz=8dWMItKuca+}f4dNO zgvV^prPccSMFUqp74;NqR@QU0(G?^u(J%L0WogMvjrzQqP0i(HX1HZeR1|9ee8@jm zG;MvYzPPlhuBNrK))QW&l~2ak2e|X-v6y7@gn4`i3X*izuJaX!{rk|i?=yvXuNf3F z9?}@k**LDRtTfm2=sgFdxXDqBcV2}vf4%8Y8ON}|P*TaAGTT$FzEgx)qZ+jvB z$ey^+T`eALtDg9^?!>CkHJ6f88w(4KwN`R8aexlC5*a6n`TFNBDz1}YBBO4{$xw6k z^4j8si|fgWyYGILZ;*_x%nc;T;L=iJ?^;O+gAByU#r5^o*2!Zak05TY(ovuWgmEJT ziKE~xB#y;#QftgFYT3?iXb1_aeX`MNfKK8jz@E%5uGH5q8(yvcAASvSc8%~BSB|5S_C8*g9)Ui086-vFpe1=Z><13yfeBluD3KIEH1H06YgEE zUruHl;uMJ3Oo*cK6hLc2Vi_eu|3Z^>xEt(<2PO-=h=I#&w3-X+m&7JQ6b?#StBpBv z5Um!)SZm^)6~{#_$@&4aWa@p4%Vheht7=+){@C{waJO8kwhaK9-SH*8^op`9-x2v zOhz6V8J|w39vU1Q+dR-XLA4sG3H$solT#yLIYyogH3A~TgVTf7H83C-?-{_Ua|CfjI6^riwc0t0 z(i~=%AB8dueUP@pM&XHZjaIxeGEqA(u%d&_K!5V+LnAzAqQ@Pz21O8=bVJkmYIF#% zFwxmKlkt%=W20wKXh($4gupyHIyG{b2^$5sGlD5HKRQUKFbW-uBf*O)((fGWLS*JM z89kj04nG2r;ZPe6m>Tu$LZhj3Ll1eH55y<_C`tx5SS@QOlcx{_&6V%+I~amJIrKVv zlstl4rhXXVb*iqFi9BzzM&4e#f$cZ<^~`TWG>OkY6FV} zfbO_BpMRXp)xii5BoDNfq?sVW@MEqoU6z=sCsGHt?B;PQMd~Y;y-ZY^i?&ehXongD z7Y5Qg`2VGcC5W_S5p!p4y>*yzb;O0JT0Go#K)*Vw#k<;uIh1Jj>RWCLr6Q#}55~tpzECYa(DqoN_cj>Wu_kJU;l4yh zMV{?E1_eMp&~ZcjYA`4WgV#nk^z}RL);`$}NnuKMGcn^D^vWqE#Eu#cLz;r`YdyY{ z%9OhH8ithU^Uh4xkKNa2DdoUSiM7FzY^@hbD_F<`Mlh)(3 zw}{?OrM*S;b}H>HqPJ7k8+~sPy`8!xiND)=qc9vD>K#V0AXCEiCsuHYfqn(jsmH-9 zJ88b4)7$D&?NU^Vpfa4IQUsOZE*yDA&LXG`@9hgE^?t{}_>G1N&CW!3_AYNlMPA95 zh6pkXN4Qs&j+(+Q$-2CFfvB29O><@G@<1}&l+GvJD8;#CorkF_f%BOZXZW;uW^dwg zE`jrz+wO>8X*g5y;i$itZ3bjhV52rLLvtSh(R8>F?ni z4u%r_J$&RYTXltqim!`~`d1ZzyeZL20V~< zfQ+R;+5s}wg&l9R)eexczQdsekg@)w9th&}Yzm|UAZJq`9RNAob&GG-0g$sv0MY@F zvnded=S1X0Lm}z}$V3XH6Ce{^VHtJ;Wa2s}Qm6@#iCg>PKP%WI#MR=t=veQ6Eg%Gq z(0JH`Xk!7H*B>^>?>TTRB2kQHKJrZg{u5(8R;x`zz*fGyXH}_83j^Xk} z1A4Z9c)8+UZ>(wu1|c+o4Wxr?fLY)#lKaNn>P8JE`6Op481%Nz}0x3Y%Cp;(fPBoJ|e0K$aJaOo- z2ZGeD`{fWt62|ouh!$-<1)}ciemPJRAa%bStS}ed<>5<=&ZR(#0Gab5f{Fl{yW!4I z6CiVkk9bUIHt$6Qh*-(73?+cfdl5kiAoE^CPy)#OUHyQBCPnm$h3KiG>!2v=EyM@n z8{&%IiR>3jH}!SP8eQ6GvEMQ~yW@8Ek}fZly0bD9abXvkS>ZjvF6=vymFf7x4L7F1 zd3~{T1Hea~*$!OOb1?^8+v>#}a81$0eb=VIHAxql>0KUkrs>JjvGlbj>d729P1TdT z`m*2yh=FZtydMUv=Cy-oyD0?1N2=TuyYj`^9C zL|;jPNTRQ#j3bG@k_0}LL|;iUL87llfgL2#S5qL8=&LCvlIW|wPQFJ`J3v-%za##L za}*^=-?gRssuwfh%0M>a>pa(>r&WtjUx6ig z2}PbxrZW;gxaw1$+$^LR<=%t??~|YYxQsK&?`>^0q?T9~TO}cWl}yGhKwKtf$K< zhQviorE2MIg^nAMVkQ3Cs+G4Fq7N5})&XT+zrE1j6;B}}on=@0(L!l|?@)4gf<}_g zwhY+D+8VkG`q9K1ZbQAk)L2||#x1%RlY^HTzjvcfdbMoJc&EtR{ z_W`vawh&fNHIn0ZqgSnC)F#O}Y;c#SZ0Xg>;sUZJG|DMFON&pT1Lg*UKv2&aYLTG= zYGE)i&pV-Eio0Yh53=DGY4J`CKMV%meD`;3F!!8z>4On_&~r8zDP!*zox@;Y{`VA0 z*QA4CJJW-n>;Wy_Q`omZZNbWVF5iN^OAoRZ6Yy%$NEf@Gu8gI(DJTD-T= zvp4Odqgwjsc@iTa^&lg02QB`&Nc`D?XPC}v>HXsD&tNIkFC3k3ERnz{?KTZ~!r5om znj5R8a*DmPG(A&ywZFAerI^KLy6`;%$wSRcM0D2rog42S;sVR$f2I)uT~uW(i!dj+OD{<4fuhhdhgEJ#jDnc3N*G8!Jz(G%u}q37+FkM6Cvy zO=l-$cz99UnpIR7h|lka$)`A&U8o#k1e74+1$kde7>;nn)o&ku&Y{n zL1f*>lDnjR9YQE3ketZjh3>Cf8D3q0c|mvuHtVjIelpKA-L&9AHlf|L_{jpn#?WF{ zwe*3!$k;^-9%M|jixwX!^kl5OyIT6GLg~76aJy;2gT2`UTKrUDzcffE4fj+_Kb>!} zhZa1@TI`|4Ppid2-=e2l`XJ-zyG{b8wMi~H$6b(cq%o*wHbAlldB6YzsJ+$Fhh7q> zy?~--25K*$KICX6pOaGE#_V!*;=m%MD>XIsl z#mWOZ`UJh(_5x$XejcrADp=<)6#95C|S*EhJ)pPA#-&mW1!m7cl*_ zNm4?3Qgk~zMI_eG7tlWZTVVZs0qf5f5zaqfKm&7-c=@qHbfbSsmnsHGD038X1-Ygg z$HxkLuM0&WeGH@3LjuEJ$Z*28P%%Klw4j(m()fO%us7g@9Y_(JNMF+MDE_kajUO!} zqqq*Re=Rvy5{a}>A2XG&z&u%q6P~i8QFoS5fb&x*E|9jww~SoL+FEp0{^=F|zu>k( zo!%j0rV~p3?2i<8E8E#1RUAl42q%A z_E!s0()*#voOQv*7q$%y3_}^6)s!ZND<4gWf$F#U0&Jw=O^i?_`vg@)Vefi!aNuCS z6hj&>Y%DEZKB_EvOO}K}6SP{pwAjK|JapGxN42#lErQal%oyj59IYah88Ap^f+Q{o zt!|S;ivuW@3yW*Ss;zfBtLiYe-c6BSBo>>F%7|WHQ|5=pggPxOtsYKRx>{j25tZ6< zng&#EYxEFl?xdJD6#Q^hNBa}y(ju(+s~MAP8~@b;`8mEaRR7h&&9}t=(1=dmUoS+r z^sXAsQ(NpRbY`EDPs^%1ooZBMg;0%Fb4f8{KM-fck(h!`$mW!CU5Y!_u@DHjmkpnE zfl8tH^#TV09qt&&6mPmYeuv{2*z41U==R- zBssnXiM{vNIpIvR^R8lID&*g+K!-l1+V_d!`|QjK9xbDl%b23VrhfeS!^FDL8#-wv zhol}5%BZh8(kR|_seYL$G*G5@ZUHou)^}(n=1I1i$!i}-j-Pmy@uLTt(i2C4XcFjI zfu5hvctPscrwiCyj-L1mDE8eJ|Bw?d=vu#3AjI&rEI!#GI*#hRi|8=cA{_#53ZW5((9RHkE0d4X|9-2m zH;83C6%>hDe8UX^22d1q^g_ZH z4fyw1N}e(BxT_c-;c}>QieOj0`E;LkaOod$D+XYW(pgK5GbsgTe<^EgUg>aN=%rmLNpo>Y0_zeJ z7E-WGGF&3IPV6I0g6$NDdqlft-rPaX@rll1%z-txGJlkeEQ{CMYR~0Y3>aBh?ky%o z8QnNixRFMo>!3S?F>Re(M%ii-YnE?L*;yAZdTmF$&l*Puu^?qPqE2j~gJBA?XcnY% zF=J9?+O!R4L=iMKAF70 zkB&pd7?Vm@IFSkf2VI#DrqoIdyEU>@ztEpY?=lpCPv*hiPa`}YqI{+VKC$_<_!Ft@ zBxok76(nAa?EG@3xysc2a-kENE8Dkf>ac} z*ygQO_F|hNkiIIhtuR}qTKV%r+y537daut9!p)y;>`+Q0kp}VS1yU_El8Q$17hxon z=%1qNpCTjqi!c(3H2sS(5{mTy7hxn7jpVNi1WlxwPy!Og07)bHt3uap;jLA^{ctD( z>8}a|P8?2HTi+}YJa&*|R!|I(WNm%3K=|0d1=2SQ#E*RuNZ%|FK>j%?m=&U3l{ZJx zTccv{`=cE~A3@=?Ej}Opa1VBHtj8<}RzBh-uOMrY>=@!Oe$OHta%U>m%+6yAfc2&{ zQKa9xC29JpQyAHn5E;(SA@8C#)+KGVozj$ME=Gsrip|8pvPzZ6MJmx+lZ;LTp~bkp zwoILd1|UxqNfh^-AWgNojE?P>5d?#tZ;lAQ+=%;tq8Q|dqHXaPIXdhulf&z7^Rn0j zSmuW!f+T4T0RB)!2qt~N6#Y;{3?}qIg||c&gvkdTLv=9{ud^OxxW6UegO?YAB;wPZB>v#7%?b1PmKp{nAW;mEB*uRt>Po^}t9-NAP|QlhOIx2WaY8mM^iwuGPLWuU7G)nbkzCqv9Y=WXY10aQ zN=N}GEi#y&jl4gz(}dK|Mm+%q38|lrK*1r%N~CzlD?<_I%Fjl3zrrI3q>t`|pzYU+ zW^X~zKi`U=+-JZE2+Do-(F{SUogd8*l>6+X8G>aH{Ah+?83aEn1b=Qft0t~i{$tek z?~8>KNSoivsRgB9#PdMVSoVo0Z^*#eh~| z+rf~^h1I#Zx}n^5JFw#ts>6gJSJMmt0opkO0JA@Hu?7kp@uUg%oucT|7q$L#bp=yq z++~a(hU2{mE)m;&Z{!x5oweCYqlvuWq(&IAHz^yaoJb*z%sTE`8Nil6LHlJXr>RZn zvT*{EnFpJ2{t|}vri<#lx5YU=4xo+ z*@M+)8;zCNS+}%AC}Xh-u^e=H|BdXL>XFcEnnPf`vN_*FbH-fFU2NjAlKb18pKG0r zIV7iiWM`WOzBLbFNTrcdmFhBbaBv~5)N*cX^*-x0opj^$BV74LdqlWk%~-ba42SsO z2i)7ZEZn;5H$ply9n}=?Yb$xh-TVp4`bB&acne;wKXHY{NL=LMGWo6$ol8(RjENwm z9(h~b%?{I$_?b1(hD9n{JqB>~6-Nz~EU&ypG{nen!*{9XmhYNMYV{;nw7b?8r#ixY z9pR4K<0zKMl5-$&O7iH1rRHpX>1aBG_=YZEEUjJ69lx=`b0EN(N!e_NS;fJnaJ{*Q zbrw=Xw}Wy|apL&@sW24{B;+fX(&xzw2D=(hq!cuk%LUq$;5 z`;^}`dCymHJSLXyYh-_Bm2-_N$;IX+BfC=96?kg;ba#gY7N55o$GJ0g3VXEp&b|2=l@`op zjiS4HKO1p=8aD|^?KZqDhQqm29dkmJHBuV!@}xrmiXM4)s`gT3maA%{3?4`q9^Bf1 zC5Qt()2R;~W>YdtK??UJc|7I3#}A#I>PQ!v0y#v65n|q+-;4JCfW%GJSD_%>i_zM-ZT7w^bzW z2%6Cm?atfD7U*S^ashWcTV+#cM9i-Iqk}ZTOGDn&*7Xz;yKv(B@QKOqplPa=&QSeL ze3xfC`R~JNqAT;AAf}TnlZ=T9lPr_$OTkEp%_RF$)Z?596q)2NMRz(!1VxO5FGYt> z`Xw7-Bz!5l_to)X;{fXZaTJYkI%8S_%L_)uY^1EBMC&-BTkNvtsGAl+;*T>VWX=6? z)N?dcHo$kE2t|L!-AMlYri-b5% zJmFONE3sm#=R;uBp-WohY<_bM)1OSYR34w6VvTU$j_F9tb*(k_3AZ!U8TQK2bJM4f zzDgcG*t$j7#p0E!B(qMNrm11*LucKEZs;H3yr*Lslq<1?bY*I#3}y%+#w~@;sdA3F z>RimW${v$;zPWE_P-OF?SoR7k9p(>O48dztoKua{`sQ#Fo;!DDDV;-^!w@>mp?r2< z&*o5xCSQ+wg0zr1{PpOtb2ge|iemo*eoFD;QM~Up@t7D%xyOGMMgJW~`)iUj=`sm| z&z~(8d+A=u{E=Biip^Y$=K)X4T>h)5eP29`7aHK&jK2|;cK6+z0;iSK4F+k=rM)Y* z#q3~3*{uvgt9I#a2896vrqevd_5ca^ji`H9e8%@fg>SLidMob0XWubPCC=^{nOJ2< zBNi4?K)qaY8%)TG7Gz0YAi0D^dq7v;ipX*eT~Yq)h-AY1@s9$&$~yRSc-N7Nfr2-;FYj=}e~9IDLr~WOq|!vh?cu5;CFb z?lf`gQXZ(!${<#a=AO^L2v?n$K6+FJd7`t}d0ixEa_%%b{Iho z-a8lTY$!$V=cCB)l26`+rkPrd-mp%>t2lzZJK%p=L9K4%E;t zu6uem>K7UhCP|+y?nM$FLKByNDAz`Mm54+o`W5RD#T$pqT2h-FB2J8}LOu(o0#zK{ zIqM071k&Q0kga43+P~9z%B^T5hr-#+`n)R}el6y~Q~ zH$naIJt?g@O90mqr$&7Zo580%Tb7Ey_rcU{T|k5*!P6r^W7nZC61W+KDRN4o$fcSy zmWBG$2rCe9E8QcmT?;igki-!oPf=%vUYVrqLHK`2reL6`N+?ybFC?uj>kYb3;8Zc% zI!B7L!5Pord?m(woij2sq=^0v9CJ;ovsm4}nEq|&`$tH6><_j-5H`&i*QSn}P) zR69|+*Sm|UcG8YSd3Q0@PTGw_ktj}b@$x;zDC{`Oj-nX)QFhdOvVIg0c~7y&`JTDJ zfqvdoJaoiMy$%C)*K0zNe%@1j<^8@Na;KhSUHF&EouU}};k*;YbUcbJJeQ3}xl_+& zy}8e=$1I`+33?GQm(Cq)_G<@j>o;F&~UJ8#q9#gIrrI zCt0cMuo6nTHPqhSK>Dh_zt@=6&3ZSV6(*IoUTIdBOAdNb!uyLo zE)x|BLC^OWZ*mDC6rtz)i^*}%0$tGa{l&ZP<|+`8lk*EN6bVLrZ{luic2=!iXRKn@ zJJ&iJZX%*H#l5xTvjjZ(y;s$ofDH&|(uma*);z0o2|X0uTO=ikIUQOzLj!{_v~Gs> zLN+uVr+A_0!x&)8&|WBVk%80OyBXRG#XDUXgSQOrh2rs7#P4*h!byt{7Nf(xZ_c?n zxN6#Y!V+?{gc76DRn*=C%Wx>01J5iW8N0bAc{?c`&=#MFNFkvNC=^*QtJ2}_Vz?j` zhP#X5elXkQbQa`;MSVcTJQlkc?gzC3M{TY+2lA03_g>tejH7*9qP3B88PE8^~IDfzXZk0 zO_~6k;e4!hd3hF}46YyNcXO*0bIA5*qSUz3XE;PTQU5Ug4YJII#3VmE0o zfq;9UCp1Zm(DS1iJ$3x%qs7#>vOXW^g-Npb& zu5Da7>~~EDDhp9Z109$3fIA4JMR5Ox40oOP`Gt%Z_`VB8c;ORnjd9rL<3$o`f39HW zM2_B~pTBuf(`i&x|zE{i^ld+CB#s@7^>2t+X z-&~Z(v2XF`<_j6MRqAQqgo~5OFtO`dl5lsCTfqClobChW`!QC3qv(%_$I*T;|Ba$Q zB2E#^f1}6|@qbbvWm1~iZx+b{d1D$OcJUW^ivMG=C&l?4NF2>R48(bIFsP z!8!Jm3YFrx)hPswodxc})W>T2?X<{31G_O|FeYP8U(@%bXDZW1mO(Xl^Y^uAGw=-8hx;v4;MlKfqA(0#EOo$md=3(2sH6FCdQt0Z=R znMF}P(XNGEz&h9Zvg47?qs*jeplP0lIb0x|SeFBF z<`G!deF^F1X6CZwnkutA1-lw**hvy#MJMUE}gJGORy&M3Obz0IxN{jq?AulOeYBo94ro+bOXU^pM+4|~fwH`6FyoAq7`S4+# z&2l}fflt$;#jUfKJra7YKPLjIk|OL4vj>tBeurQu&|fN4HzTxVba65oLK@Dk!|;*T z7n@{0VWFGQ2;?^LqV`7d2jmOPRtO2JrcvX&Spkv`Jux?UJ++R!gDJR^wQpUN#-MkM zQNlgkM~8+lf@8t@i&S-7ttCKq)*%hRcx+u5&GKddYImzD+K(?^u-!C@g)(vk#!9(k zoUdk_3^z!!_gYK8CDU!rKhHstL3bQ2s6C94^L_S(W~FGDZEP-%u%~+&Y91eS1EHUD zt2*zu+nY?az;}jW=2YZV8YlqE5(&~kHy8bI$@{mu>}bFbmwH@6HAP!bTs?ZblsMM= z+e^_dF0N1vTyZ3Qm5p;^c-F{T&@FOW+_ZUnDHQ|?>E??pSS#M*A^wOURt@4xosU!1 zTQhMO_HhB{}zO={t8J6aXE>Kr4<%(Sg_Yc?&Ynmb|u4k#Rp; z@)ttMZTQX-%Kpo6Ayk+2_ji`O{!R%zC_4R}B1Cy-3H@Dacz3n(?o!(~y&5hT-@8j4 z`{SQfsI6OS`1?v2)$fXInb=3K0}Y8*uOGzm}mRR^EYOu&iOY^xhm(LRyXSq2_}F_X%QxP zU&){5;4LJ5U&-IoK@lc+Ux`eA?N$`oKHpcm!R0ehWcz$y>CS!<7$Co*-9J`}UMLk^ zZHrg`ScywlZXu4$Yt7G3luCPg_oRLbKGdL)iGsFPf!?W+d%)!XL}}MkeNbVKjIb6g~2 zp9%5PB@-7><+FlpPfhm{PL>f7kaRckX&zKpk!YZ^kWyo68*90}KsF)gqUx}jc6vGs z&}Dn*GX%jOrJD$~F_@KM=A&YgaEM~B&I;g%j^Zu$ZvO0v1PorAedMDB> z38vf<5UzScz0=*SA>^fim*D`H=`guP(VR2Xslq;d=ZI!Kg0Yv@-R?j}#(Z`lBS>rB z8tHr7DMpGg--pC}?~|3?t(pAAQgl!6kCbhByARKWi3x5h;sjcwO46FLG-*en;L&jk zLCY^?@Rv4XOA<5PS(;EP`&x(%UgUk{y?kgxD{H;M?aE}!L3^D4aE4|8iXP(CEAJ&C z+5ZcDt!$O_qUC>aSe?5D-R^5PiXRByZ*!k>Gbd&$dguWoIX51cZ4+!BqdK~la?X)Y z$g8u+&7>;q!C}Eat7LlsISn?SNa4wAfizRLXc4Ab?XunwEu%|B~Re!N` zlS>PuGE(&yONWkosmfJWzf_8THI=GbB)?Q@-({C(b-Syqewm%U*Y4jk<_|>DI8y2A z?y;h#%f7Letf*fuk$rR6WTo9>^(z8(y?NwwQ{#9pr1Ge((4cMxOoI1U^1vv6Vn<*; zQ9p;7=tJEMnAZ9y1nfWA)*^y^u0#&)r*?-N4Kp9zB4U=j3Bt!A7nd%Rkf9Gjx^o4{ z16#w)rdMu{iY$KR>srK7&cNtS9LZa}20Cpa3F7ifgF-Cs()S}QbJ+D}LRN~cZUOT3 z@R}(D5}xxxELjN|emc136=CP&wT<4(Wl$on>-l~YR4-rW)?G$?2G5X{RFXZXTbi6l zgly`-@w~C&*X`_&M!UtLQDVjdp9&DWd&>r>ZvQb4rZPU@bh)DR?M)-KM8M{oN<=Xi z1ha*zZcUofV@UMl#E#!PZ0@|YHAAVjae*sxEuEYx5UkWAg>lO_Mx;6|5>9uMB2dIS8CP0C+^Cb|LQsIF&EuU8lEh0YkSSk2*s$Tj0BorU|wm9U4;$P1kbn(g8` z2jJp)XSzla}h_Mwler}(*X^{JPfpm=Tt)ykBqiP-MUqVC0Xx2t7FsZmPHlycCsXy1YnUtStd|`H0)h^KScM$M9dRcZ zj(9Y5)lazH#S_?EjYn3LR&xZ^MqhJhaqduUvuq0T4E)S#!x9T(o!OMz9g*)Su7y5m ziqwg_PaN-e9OhR$<7XYrt&>{MvQX!etwws66K#iE{nu;q)Hl^b9Hvhnnw(wZ`}N2& z7))xUNpTdC#T9uWw04zY?V#zBB?4->ZHH4l^qQ_HKF2jYacK6~iQ~s#efKMmAHU}? z;TxBbBZ0igxBSeLzT(E>EE9(5R1bMi*jUpdX8z|geO$hd&z1bmhTY~p`CN&c4SieG zmd};0KN5ZyYa|70>*(BN(28w zMhkgIzEJXaGP#0%|XodR=6TBB;Gsy5or5+}- z>L`Nh*Mp-@NpOGpGCAtH<*571;HaYn9u%FUjv_kyU&>K;Bd*YHoU7lED|8R*Oy823 zPo6Zr+nlQ1imrSk_(3TFDPIJV73K2VRjvGWsqM{W7kO8j+h5D&Cl~21d6d5W9i%(} zp*#>BZwJa#r1x(J%2R~$-wu>lKHd+OUmh3fE>Zpm%icvw2|Oq|%2R}1KUnrI z(%sd{Tgq+k_4HCc-doC?-+Tgk?H0Y>R^~F=n#)tj*~D!Bri$|~n+=)Tc<-epXYNn} z6?_syxmeo6f}$Ebwcqb3^Z)_r!V!=nRC!z3`{OBs&fCg8?#3625b$kfZf*T9l9Rh- z20mLxPX4D%PTE~PFPoUuDBRIIb_WcpkOy{^oZNg$?n{!A7S2y;q+l@P3^bA#?AeS) zvaFvidm#x;pwY8sFC-}AAZtQSl{w^)%LWNYdR|rr^qsui z+~>Q2^-i$L#<@5UbLX@rBzU)Ca-6-%UHrjkLFC@@_h48?r~gllF`#_Lm@<~nd&G7U+*!k{4C8VdjVQWiRjxUB!dwIo(A=ht~ z+pn`wJVmXV-zt}Gv{=2BslWsGc^Z4b3jD1ymq*@CKuQm&@Z06mfxi9bzqH%#Qe0e? zfYox<+_$vs0dRi1eC>7deI5?we_t-`>pPfaWAXP0;+M0WUb|n!%>ftXDI-CUgaIwx zXMF5|fwHefNe~d7`%F3dowBhA?@oFb_HUXal` z>hk+9lZ@Ug8U6b~MpFV0icUsTgbcqg8T}(gd;`^L<+J6sKQ9-0zs1++Q?G6sJJ*iQ z)%Py0Z$5H&z9Nm^q#x1qUPgHri7XH=ry@-5+nOhFz1d3Ua2o0!cAuLjGf`P-Cr?f~ zz;?2aKyh}_&Lm&lkOrdfhiIqJDP(i_+I4P1Fstj&3lfx^mSYpeLnoIV`Y8$^mCLkr zCk$Kuqw+3<;qc4Ao;>M696Id81#VIIT?U4cuQt?Ny*%AKboj8dgU!+I7RNcxK@n;q zyMWP87(L}4H1GE^3topJbkOIsW$w`acgmxw>Xzv*l%xNK!24G&lg5tNc!9C$e!VPK zXjdq@U^7JX$yahhwi*}wr2-24%s1DMhAVdXBoK-lO50*rqGRM@SCn|GwmV{0Gu4C*}?PlxOQ}d@22n zd?{VX*rR3phPtbge1C3`vHP(4A>ZckfNFYK3Naz>7s|2wvKK|k(l3@veI}@^%xdO= zyAj$Rut>jH-sOa%JpjZP%lmK2ny{w7Smv5irAny=6~0t1-Ii96Fylc6Q=W=1X)#JX zS_^B!@x=52Fuzph@+;*|R)yu4%aOYml@jnMI*CIOiSy-h*FG~MDFW-u<^3+aKoLk^ zE|Vl-pNOgoxj!vO2la_4R!2VAOfVqNo}QbyY(hCNELZk1mjuL=;<|dOp|56=Ny=8> z?ocIMOh~KcLpK%*Vqp$&9ils=q6_03-?Dgp(kdYc!r5VdOzwkT7@7$6r)8i20q%yp zKNU-nq5Wz34t>B@L_(y!SdQEwc1jS5Vt}Od_7^iqBEyU2s*5{N8Avadxr)R=qV(0W zziqi1DY!#1KvHV#SIa)rkGDYjYI*N%p886S{c4%pmK`LTey!|po3C2pD8&Ftp^>j; zI4LypwX#2>N@XB@En@-d{#iM42ZF1S;sX=|B*h2*EW=6hfj=wn4g8{uR{l&3>TrTx zzFziMCs*yx4~hYj-1%S6aFRR!>*cDuI+@Bq`g)nGlmD>5wy_VFa=GolR|>sP5-Q{K zMJ<2-Y5wEnF2pI8Nk6jWKbdZ)CGC`~yE20Ny-u?WWRld3IKG6GyZ;Vl`ibx4jkDOT zxevI*>I+RLg>~BSujLQ;!M!loKCQqHSGb$`Ydvg`W6cm73#>2My1GcTS`5A!B5!hn z&P9xMyoMlXixA{|OKxCY{FmNmVyCAp)57Ay$lyFdt%%M_K9!{jxFDWB9|D}w;SmMx zb+ShHU_Qg?ZQ|R_Ilg@rvTxH=U9Y0H$hIVzO|aZ|;w>=bFI*(u4#f@|+a2v9hDzQ- zdLSW$f43bo6IuObR-8a{M;wxbLjz+CI}u0r2=`>UD|*EmA&N(c*2Zne9o%laGpNUP z`hdU3-4Fev|2_V=4F0;kN|M<*`vEPIg(SIfaglHT_>owr;SwMG32r{!sSVCZuh38z z_INzzk+9(19pRthLvM_V^JgF)GVfa7>*+H)VKFC5OlbX)Vr@x~tqAE)bHcnP&H7noqW7TTbm0B)4eLe(i3> zlw7a|8=&zQ+Nti^^?{(i6ke+@2~-pyM@SJz7dKgs1B)BZIGUUd7qkZOxKM&ECN8(5 zdg?0>)TObK*X6#edmZ*!_t;F;}R$O%Rb?ejakoCN`!HWtFP;L#bIP4vJ{*!o`ydD zr~DI#ucZv<2^ef3q#bw($}ATRuNWKAPPlHR3~mFF{UU5_7Lt<6GRE*rM3B_jxYQWo zVV7QHnZs~>V29j@^TTX4m)Wa;KsutAe!iw4r-q#}I_XELNtPbzuFYoi;jHJjMU?c} z=Z5=~P{rYOEE`5kA7En=N)8e7Ak530Uzs_2887)OIa9<8P_|)e1^NY(f^|UE0Yh5U zt$%o0R@n1={%HCo6jscTc1OoF?qfRuOU<&Z)1aYZ<;`-A$@mkCk-LSM7NuTeD%m|l zbgA_ms3h9y!}9L#6dk$66~v;{$!_6mX;FdZn8G^lVw94Hol&x{2X1#qPrP!B4uaB$ z65>=fiYRg)u6VtlA{xtwEBjqGG(}Xk4_EjMgED{j$%_0)CHm`1(ezZx)c!~XL+EYo zSS^&L;e4`E>hJx_N-~N&XItr0@eMg0vJHy*l=H~xGcp=$w2X%N;=WHC4KauD%& zot%t!N?njzb|a3~k(( zd%$x4WCfRR3Ii|bCo4DJ75Bs)4yZnC|H;ZtN8;a!{E`RAr?upN4X>X&bE-*U!xEh~{~jA4>)(!zA)jR=Ic3F9tpbYSAlTexCRt%jW(WZ!92{#qkZGh* zAaPnWk2>eR^p7-WT{q;L7IDd6WWbq~SYc2u!_}cMA8_O{ZlsJI-!YufPIZ1I&|S_R zh?>ovd>Eh3&z-y&pU%&n{1~6k&z(HjpU%%+L38(M&D~FR`?=%6H!7vWz3=K4a~rQt zF;8<1ZEvI4v2bn9@fR|2^4gs#GVEL;3K_>TN|$9PBA(52tBuwVT1(k|tJ6uwE@ZrPz43@7FyAR3CqzD;5=%9mi!a66`RxjHA zomMb*bL3J6w0zCUA)QZ=ddUdLU{}ZM&BG=;{273-n(&Q@vr^bBq0AqPSJ|%M+Bz7y z(R}{_PEZ(v;137#7~6!79ct1-6WK9LvUXuq8@q~D=NfG0(ZkoCl0jnMtzci{BNZ}= zWLh%AW(9b)&zVPo4y1TH8cMO^6F4ke@e~xt0wct=w%B?q`^s?&Zo9;kz_it7jNuei zXza9qJDh`f7;jv%2{z94=XF!12}6a?Pngn?el$WGZ@RG{OUP)z`ZdRd?cPIv_<6>q z0$pA4wfxIFfaNh@xh7%LJMRp^ht11-><$HX{GEEGO6YYE7Bc-@0yU4B)%hDx!{hB? z_)1rV6l$ilnQwCZMz)-YgL>cr4Eu$V@`+?Gy6Oe?)@eoU`JWg*FadA*h%C1%AwdF* zcu47#SvMV&lR=6^H+0sf*xa#Rp{hZh+ z80;(DtQ<*Bp+-Cvuukc&p7=ry*!yAQ$}*vmvbR|&R0+9{UfCE(NWv1DSAq;o4;=A= zF&`SFh&q0CFF}-xW&N=IHK&MlC$<98N+=84k=)?y6WMkoNBB1?KAs`XL9Xy`RQBJV zHDNpYMg?cM;u)$z`J0u}k+gyw;ylP;$|e5IJWM&ozggLTN7lq(96FrB1jM&0r4wld zof_al22;-QZ{=aiJ^roA{-aqFV1BD|_izu zk&NpMA%+V^j#2FajQaL=ubbNg2J!ZGubbNg2L1MSubbNg^t-pWd)?eUK%IMgJNn&( zA2%?5w7qnD?}J;%Eq-*Dog>?HFw^Cb?>v4l2-Jo2QXpn{OE3M=b}tC5hV{AsmUO7n zOMkST@2Xz%NP*-#+DkX}avU)85NI+?lNWv^D#Hfjo*7(-*{Y6p~D4bQy&tli!o4kYw_E+WnV|MInVM-_y>$5FhV|Z)hzX zlMmE=Sn`;ZR4dTTf5(P>v2hB(&#^i(HFkmAJX7v34M`YP)!Bwd~=^*T?N1 zzv@i`TQAMe_j};IduiWw;)a1u0~pBHsRdX+Tqv9xdTj6lKK6~-0hE?wjuQJLRE1FScO;BlkFjC7vU$R`_7`e|4FqLNnGrUS2|3Q-1DS9x z6nC{>>&b)!FYaQ6cgD(s>k&`Fb?q?G-sqs%tDQu!Vo&>R?l-w8d)dhCd4=D6xy611 zbMJv$+;1T6?Yncfg6+88yeIz3;rQlFi{aSQ$B%Ix(S|P1w>s6H&8zR}<9)5T{j94} zJrTG2+Q@_=i}k>k#rpK|0av#cDpv1}J7zh{D;EM1;tGvUsmA46TC?w!am78WR^yUf zwp3!Xtxje(8#xn;>fZ0{+ za?eKfsCrkt*H^*zU%PDA(i|vLud~+UUW}yAtB`6@AE0g>s2F)~z9K^Qo$8veT_sJ_ zS>FfRZ6LQ?vE76j=`~+n*X{djL1xUaaG-Bw}awJe5N|K z^E(a)ZB?DMPJ1F0`nZ_8JKwr`UA*tyvtW#PT_^6iBJr&LfK#X8Q{N1xDrRfs$}H|$yH%nF)z`%3 z{NlL%YW@1<;Tj2TdDoU(wkWQS_M}x7+OApejd8^dWhb%GduLqKDuy4O6r0J(huA$D zM@sZ!&y*c6Uacwur9{Jq36tF?4!@M)NrYcisUS-;Ynd~*D_UPuIH?YOYR`nVNn_N5Dg}%f# z*IvCol-TInx#H3R;Z;X;i+_bFMJWIwH@(=A0@424QSA*i0n)*l)6*z*sNdafdkVcL zlT)@eq4cNzc{9$<1V`~yDzxcHQX-sewrD8Yz7#_B(A}}y5aTi`ZT?m3j)JKI9ic?U zU7fo^NvZ%hL1{d?p{lq$+V5XUgV-IlhmziP=$hTKD$;Csb)V4~^KWk(6C*4HVXwy` z5*AgD1qHxWBZmbgDpoxflmJ%+3uPik9q76HqHBxR6(wr!>*zKN<&`S#FS*H}0FC#T zx?Dx2ffo07JM4J}$o<@=p@BstfflbR-Q+8hi%-c>fLAoTX16ZDEUO~TuE9KX%_tm* z+^q420USsNfZbF)(CHLU-l-;vr)vfyT<_U}gj1SbpEhH@i`RDskR)>!uZIy_Gb-K? z-RKb^m6MX=rYPc*dBqz@E_Fzf$XUE$?{%RBkQ;C#I3QHK(NAF%Rjd?fPS$4e#!fed zyrbETOrdMmUM=1n-QXeeIW0<|85bfHZ|-!uBiE}GUFn<}i6QWEv~sgj?jn8Z3v zP$@;`r77N8x&fz7yDI|rR<55+%T&L$>)Nag=v!0JER5Tu2LotL?(Olu_}>)`wsH8; z(rvxJQdCqhd1BK>{)Bv=7EP5Y&g+GZm2gRW_1#=v-j6g5;gy0U(c8uFpR~rW|J{crCA?6B)q_WO6o_i|S<)6^`J*@fpv~wQ3qH?5n>ZNqPou$QF87l6! zyPYtRuzUq)s50CnT3>+&znNkJvG0Qnk6#rN0_ihHw4hw?K0PO69LG+?JIN2=TS1ET z_-{eU+nu2xQGDMf@)SYpKFITQiWJiH)gaZlDpGXx(vTvk^|X-E+|g)pd1E=ftRurs z^8VGEa4Uh^t2f~W_ul`~aH~J_cfhUw%oT9cHgf+a+{)l~Ke+wFlx{4{2P(JpE?<>y z3U;q>1>DtV6EQ_zHCOkhcdMoTArqmS=2m;w1C`#~Y*)bX0rpI{0ik>hM*kq}5@OFm zma$vtrb(PCoe2xw=@O@S@8pOnC_|M4UJg)Xv_lnLZmbQ^jfs!JUiu5@N)y{B^ep60`7IiWcN8L5Qr57mR_BbBbSx&|}Sy*DeR}VRP@d?}sbb_T5VA&&i|@ zca2?K+1OYfke_4#7mQipePJzH8<_cFuy8BDo`A>09BRrv0h5OrNWg@heylW}VIpxi z*1d--SS5}T$P&kAD>wCB>o752z|4AcX0^_>oVo6us(UuqJx>63maBb2_dKEdv#5BX zdst|q^!luO(R~8CyWPO}pjw=)+?0ZVF&O1!t{cZHleunqLN}9ilYs&5cUSi3z=&bK zyONO)T~u|r&nIB(^w{r4O;f&>h+%Hvsmi16SsmNIrf9V{jzb-hY`UXLCTCan2;kEl zyEZ?A38&%7d{e5O>*(23O?aK_=-T{@rsw$ZM!qSJ9^pEqe6J$sBbWr6o~fRl@-blir9n6&W`^DzK&!j4a13m+Z-CA~_XR7;Bhvy&k$Je`{LlyubdCL1b_iTwr z;B_nxu3W~d?9fu0*;rfhW_G*E=hl`o^IIOI?wU8hpN{uo3T_Q=oaz7=O2>M0vhY~H z@9RQS-@F(g8zaL=z+1?^>9`x{yTo?Isu3*H>F7j}cUQnrr^i zrCWgripi{cB#wf*@^X)%MAsUmPErz`|DF+zqf z2j=EB9n`qL>r`Zu$4!KB%;vxLa7nq;uf3e+o=A;iJdJnQ*NH8mhK8I zmX%5BXo4n+wU>VWT|rwyTdn58`X#YRIFl2;Qb8+Sv#w7Jp!qm1;s8-|1!X6herR+m znVL8~{petAB;ogDZQ>D*>4|>pc6+;I;&gI$q&D;rUH_PLHdz(-Sr2 zaZV1_rbma)jSbeg>7vH16(fm8Hat2tG&VSTc4U|@AdJ&zGV;jC_;fP$(BRnE=7Ex+ z_~`gZP1xs$nVcE{%fVA)BkGE-&jYkJGBm9LWxs|X9te(+H#s#qGBnCB=rIBr25aa0 zJT1O^E!blXFueAcBW~(7FZ&hmFD$;~K4aWn`juUSLHBn}Po1(T7HO&P0zpY7L4Y`r5(JbiNuL!YfR4HqK;x z)%LdW7r@M4PeJIA^ZnfXjcPbY)J zj{syi)P@75Mm@XGXzJY1L!Rcazl(ty(Xh>*HKw&GDn!t)y5DJY!ogl zBIKZHLk^i7Iha0&Ou}JOTN|Lse4Mqekw4D4(ArpC}&wy~mr$nv`E?9i6> zI*CF_bW7V}C$ukGX6`U#5>~ivRh+2J@^SoaH32oj6skgK#2?^0elnRwTYXid{6)(#8|SVAS+JRs{Umwky}1A?Y}4m*MBpHttmAa$AZAyqnO zJ#mf3oh0INmB#nE*PN~iHNQWHd6IU+pB{KZKmv36w)!?qR zDMi_V4NEdRcSv&5L>iSH7oPxv#Qsa(&K)-EN{9r{6A*YrxBxfzl*r$i^eXoIku z#NxohNJWuLTOwbK0EtQE`c+%%Is7`Q(p*#>o@L8)3@y-A2bFa~x_W4q}|eRuiULTcM`Y z&vZEMl5SZn?etqR$@gUBvxPb^yx{^gZ}^@!Ap8D~GqD*|ZZd2nn#tn>Cyt-MYh{xJ ztu1aU-^H8oul#2`IF`;=`san{+TP!gHqDYhrYo*=ipwoCqb0ZUeDD9y-kX5gc@=lw z_kHixRrlMAx~~>XEm>+^Th?N0wdF-%Y)kSY+me-*WWa`zTI!b7SW*jh%L|48VGChj zvJet7fv_Yjfv^S$WFdjDB$**!fCQ3|1jrZ2H_6N-VMr$Ve*daE@4dZ9EtZ*Q=6T}B z;I4a4)u~hGymij0Q>W_ODBpPLWZI@COMstsXDoE1VZw5_>50O zPj)eK%Oh33sm;wz@-lFp#ml9$?#F8SBgKx2CgGNozc~iRNj8Su{aUB1B~u%LYT9QV zQ)!IP_lQVrKHnG}ArQntI%w!EA6m@?U9pUxR>%h_F*ZK-XsPuQsky0{-lcFeHF}1# zygX((AtE9==VxoKwhfZ^_=x06dr@og44F^fZOaQpoV7 z;gDeEVo!h3e4EPLJ*I6DoT4v`@$f5jIQMWQ!a%Jak@Z8@k!A(q52i8C-Mkct(b2t< zdws%@_NTlwKZ7vD8+2qw(b*Rkah`!$e%8d|uQGEJs`a>eR`I}2YmS>Ui0V;bv_K)0 zEw;ZAE1A-d@sg7*+Xg!5;Sv=J(ca%#dp^N@-58$v2_A$kK0kdMzA7NNHnYU=q;;K{ ztN?3TL0F~7Wz?WOIgv)Z7PHhKhw+EqW8xUE>9}AcybLnoQ{p)dE)*nt(6k2x>SnqrWXRHMTe% z;&7HW7amdLJU@0!w}sew!+BVmwIhLCJ-jaK^zetc7GolQUB=DC6HN`cz*Ogpzb{=oU|)dO?*?lwPAE#~Vy!3=ENT6EdcVjlbxn6c$~dwH_X zS`kwS@A?@ryoL%ZRmp@rZDXpN|7mR~fsLzMzQA?!A6<4n=eLQLGw>~?>m5hgVH{7z z)p=cT_fkj3>Y;GX6WrKcbcc`+8;uU5v3h3XA&HVmSD+ac`gNMOWkkMS+G)O^32baH zx_G$$Ag_u~A0K;QX<>a^G(&K#6hnn7?y`DvDl{~AzF692M6JSJbH~ul!PqHAW@$G? z+#DEiXRRaeo7X&6ESSb^yWsWwqO&FVzM`s&30**R4(ZR*(^(G5xJXMGHK;`-G4rFO z7JoKxFM6GoBoR6CCQ-U8vRcBCH-2wAzp7U>b$+s zpX|@(S1z+Zxx4D9tV5;PRa{Y7(Q+>=aFBa$`68nyziD8SSII4%ua(+f@klK*?QPjc zub3`7JVK-?g;+SO)fbzQH1Cl;8pZJ2hN^BkDs(HcxX}uYfCPO zJi)^Yu!Oj5?CIKME?%fQI@GgyM}oL0>$&j>#m#zI(kZvUM)MjjDs122D&B+6?9d`{ zOlgws(`B0hrz5OscEqA(d6QMhgHi2GzK=?S0o3hHb?Rk7Z#Wz(CT6uSPp>!R#?u#W zXB4Td(_P+x;INM@!bV>{Cwi&i4w||*?+-6Md$(HcU#n9YXqX9JX4N=6? zD4-;rDnuGx$;(s2-56MtY6=fiiW32CVSZHq+mq!bv@9ji-9fx+0)Qeg55Tv|$99u`ZNC<>$mCa=JymnVVY6}YUFth@q~BV$Er zl5k{nZ%ZK1mXXhCzx58=>+*czz^*}4J=!g3_v>CBwvD=T|E*j+JaP2ser;uM$6~Cn zVbkE=;YSGOK~?}zwhitb+nIXWLEFp27qyai(DriHOJ})*wwL$e#q8d+D=U}z2dN3P zvf~mPHgh3Z6@7L$fmIf^+wPRUtU@G#kv_6&L_621_*2AMEsbO&MGaF6fKa zn4qgm|65)q0{Jjy=5M;X^uN^vT^*l8CZJiiCV$griKZ#pEL)Q^N0ZjnXZ#nibhrF( z*ASlaPR0Qj1T5RUf4Y(T*7TCg_m_-@>0y1%M`A%Bgpe!oMN z|I+LARdyA=s!_eHvZ}mZ-(x@W82$3fp7MIVymMbu-nm~<*=#l0_vz1cyj0& z@EJN`v87bZKMYv>A5O``XB9_I&Q%fWi{P5j!cr5P1v@~Ip(@^pv=?Cfwta&?u8lSbVg-#VmPdoJ~8%)8DjXg7KHTfHN{BW3RxT4#a9M>Xaz=c&0c^KQFlBN2)j6+%|&X3wZUb=`%1`ZS)Kt)`jhW$iyJ`=-tY z_6lapmMvR1+P??4Y}sM|-Y5Crz&{(eZrgbE)`Q!w-m(1#{@tYC?4SEK4erEb%RqAB z!FXdf1;sC?MBN^zbQ`(NuD<%}8(MvN_~C~)jg3wo zH^0lX$B%91f2y%*@{!5)I>IR`imqB@lpMS)T}^RvPJa&KSg{MMF~imNEm(v2(d>EB z;1Nj%M|%;_Kdlc&1-4m^%f=Gk#6kUVO!jq#zVFc8cduVRYdxFq>$XtX;t5BJ3-k2A z=%nb5A0HWhw2e3VdXAeA6=69YoO~!+oKpRY$%n2QG=@Be)%Vb)hc-<fE-xe-0?mO=RCjTU+WlKNQaYMTxtE_bTnT59XHGe0ArSWpxC)@!f1 z=9=rSz7{eug)oKly>p>+dhC(Vby8;2KSu6lzL)ulcN!8|k0@Ly#K2{+^v`;ni`&?l zV+MGqU57woA|k112+ zyj_O&CX~jlT3B5SBvNV-nA-cOEKyKv)}CH_-k!z)+mL8=p6YHjSq@p-KqWV;vTze0 z{w}{^(->1OU2pCR#05|&7=GAu+JDh4wQe?LBWs%xy6s~gP1(4_ExqpzuG55d9z4{Ld1TO9jk|6X5F&gr29vBYjQNh^ZULgxh0gg z0Vi|rJGG^mIhC|)VHYiPRy4)aWLitjSb$#L0&53rei0s|UprXg#{EL{j8L%Vr_tg= z+g^`pX4|#f*bkO@G@9GCY`spa!`AHww`|>U^_Cr5w{P0A74iA(W|0w`h70czIh|Nd z>od3G<-U14Vc4Oj&5u4HRPzCgGS+f~NL#hxK`oDw_04yA%*>eVJ|AuROQmr};2!?| z$?-i0?m1xlvvuoRo4WGR30cFQfqEad-QDQvjfW0w=ALR|GnVYlEmk%UDL6b`8{K^S zNwNf-Jbcg5r*X40u~`glZfgV$hSe44JsjebreV&rMRb-1kBoq!iL)vF!Siz?X1h|Z zj-t1b^~W##{d=}9(y1e8jpL$mq^(DAz3GfQlIn%fw$0&QyxAH%F?s6bW%gIXRrZp} z6Yj&Kf+R=Xa1X9s^W=?dPHkK>a&XNZJJ#&ovF5<0HOKE;%fW)z^osFq#~cZ$HA>89 zr*`M~$gt@>+=lKyg8U_pQG0-cCz^4A%M$`WV}~A&hi$Ph)vT@D z;ci_{F=nWTAI(CwY@{kUSEf)#+OU?=6{v{drno){#!b>vVBYrJ~SgXBC zJ`#VF;c{|%h?z?nQ4%=O znx77yUadBbbRcSO(V8F78ABb6wVF$=+%ttghYQSBgBxzRiUXqlCvVv4LXpP?{Y*mP z9s_WS$HuGLJvM%$UaXiA{*2^9#~wVdzs6iQAI-61-Y!vS^cMNpNIVzhuf~$1?)ft8 zZlg97P~-Zdvz(j}LTq4$cfr9leTQ0=n>+!wm%Zq*i*4-(c31ze1o${loWxe7pK2@8 zPyDI7`gy$fxY=*zJ1X7Tz%_#=Fx!u~{Ybt>=EfyK`rL}mL=uBOT92;S!SMQxFQhy3 zbP=t4u+F&9@@w?YPLDui{zKJHUc@>YcU?t9V&VQ8Gy*%um8I^%iipJGciTloV&TfV zQSB-(sdcyA6>(YoZo6wmx|Y^$VHaD=1AgzhfG~aow|ksw2JXDQMgYDW!m=?ku9P(L z<1Ke6rpsIk;&K5Gfz=)EMRhsvQUIVk>8c)xihUskAo%JFDcA*S|CEqI{UHU4KnfZQCrT1e`SEOIkEFj39M3J+$rn@NF8n;D_6M4xUd# z?A_4$icE^M&Q5x8y2E^gaqHM)Kiem?@7yWH;q!VNEhff2d}-%Um;J^mfu9r#6_4gK zZIt9MiBxQ>`jI+?F z`Bilc^|pS-OJ`KRIT19EN`!MExSyPRy8TCYe9gB#>-m1`|T3d1m{aeuMUN3#Mu2O^-R*5J@AKfIZTxR6gJyc$=b1^E6MSlVFupQ$=Eh>4?KfgpUow+(TN{VGmi7?A zqV!JZP(Y2Vibk8pIZP5Y+;43f)`t38D(4RSz41eZ538b{9(MiVU-tPwt7+^nuo0tE z-kpiY6U!MVZBP>JGGENK(|Q#X!W738sl;p(i6GqkSXOkkDFq9~fJtNYaJjdd&r{E1 zqkBdgTZ%+z*lsma*#Q;#;Dh0J9fxtr9^qIV)5w*2!^`gQq7vg2(S1_5 zu)}f~#q&Ev-Jnci85K$FiC)s&T3j;i(@GP(Z8SA^%F%4T19z8cIQHeu8GQ2kqCPTu za&kBY)}8K1+b;OssyGz&8_SE%2|~*rR*0wQOox8B3s|92CZtki|mX}_qr`5*Z> zr_Jxu1Jyg`9;jCrMQg&vgrD7ywv&230r2iET-zu}b~xXj=Nog3-CD|&L20Pd5cOyC z*RrC2#&nyN$$dq`+w(>6yA)@i;aW>3--#a(|0BhMX}uHkq)c^3ZhU<8cVmqrwLdU% znA;qS-85oS?ilLPuLskQHt+P*V#HRz4z!3t&7ViVOT-`WkH5?&sD+W65lv8Hb; zVq)DnesriSs`K|mTgwkqKBVoo-OA2dXCz6al~;l zAPpqV@u<;gpgK9j^>x(}2acM}j8B5wmyWo(v{j8=&;-%kV;{Phxb#16fKkc1po>oj= z##`QmFh;;3K-ju`2sR;4wI)Zbllbsz$_mmLmteRZ-8NC2*O7q{b5EW5zoiN;fk#d141 zt3wCw2q)ze%Ds8|T-MFL0N)xYyTITL00=`16Pea;6OEgTOTAYNq!xFA>tH?NxRQAD z!C7XN4%q6Qssw*m7faeH=o{fZ1zomNOl@dk3w4Mxm&W$ul6EA0E~9~^oosDrQ433- z47u|p#9hV8b~H3NyG-sku>S|P-eS~-e58zBbah$BiSSW-XXWM`%0uro_^9po1N>|s zHVQI^-0LeCV*9Z57?O9y+S=DWN zf;}F`TfIw}9DCp#NtE}Cl-yF<38(TMg{m7+qgDc4{haZ+);Fv6UCHB@3sxIPs0|{rhC%78EPTx`?kQO9ITv^1nNx-TN3*; zc+X%+dUfht^8hX-dPka%`vx!}peK=idA7`kvx@LkUyPlo;RqbmtfL$q%|x`CI&;@gkd&B_*$1 z8lGj<%F*PQ7Js_+&!6XDRI2klM~`+erOmHgfiWwu6Ya3G(q&Hab(yO=iAaZCtB%o5 zhPf>=%n6{VAgWwI5GZ==jf2Xt#TBc}`X&e~S8;`m{Pql}r?S;O@I|2qb9rex?2VAl zpkOph0kyrUUY;lTBLtBldic8${xUtZgUZhsL#-Td~@6V}0rx`@b&U+b{fC_K?w&nt*8bPTq=tU1B;rLK=z;Fa~8 z%hSQR(yc8w3)qbNa z$?}=wf2cM*l&^M>_DcF7KS5I0=NdZVY~WDm8yGINNPKKUu!wt3+QooBl)ZH zbm+PY?&NW(=G4$JQuyE*!nN$qGRBGfG?1=JsVdpN)BDrBGLHy;CXfi&!4^Refz`qY zX;t~t8$zIw^o~88GO2o&zLwkr#YmPzqjtE#2?=33wA-I_wr&@s{3~>+XLmzwRBc)q zH)9iJ`Scm^U5US~>m{GE(JPCTt|c%OsLC`RB+mo>i1?V2PFkEgybPEQ)47?_XN|Lu zh<|qJyJbTrTn$(n>X_-=zG{$dmh72S2#0aZs^Jizxy6XXL2YB=Td+C6=1P~nnku|}Phs0zA01+gk74)(TS*fm-yN*D23MT_ z^`%DAhLp zLT~ssmX99whHoQ}9ygo%;|<@I{N@271C|f{O_k616jMj$qcFFWZyNRLZ%ca1X|Mja zG}5zCz53g7<4v{(awB?8VPe zb+6>`U8E(8HKRLWDMi~F2}3Ry!$$_0M5Mfq(dBjuHm<8Jw6XS^tLqkdss_Kgx^C5A z+>^0qxSp)2niM?t1MT_}P0wAgC;e%ZA-(J4S*bn!yP-tWYt$P`G_7tolxTYTcSDKB zSnVJZzEjlu(;cKox-@uib1`xwsqj>oRh*muzbQuHWzuHb3|V& z-iW3Al)&P~S{gV_^Bb358H{S0-?(W@utvq3YB&6EWKGXuZz4^F%TS-vO)IWW5UkyF z?ezs&o9vy(8aheFZ=cca%9m^rhXg!rwVlh8*tLyv3%tz3|*q<~PyybWbQS zia^^_<~Px!*geE**CrXqk-IDRx`NP*zB{-emNN0Ub5G6!02h1X`6v(-_i|9v*Z@IJ zk-N1?uIgjjmtPh|`abvNsiL^px6HHqP!Y6!WRGw(zR!E&LYg4zFn>=l4+PphaUl%^ z+C5y$-)pS_4CRBw0+E}`l!P68A__Ibd@mnvZ&ZW^?Ti>Fc^vwZJ9d_kZ6mlWGGy^# z5YQVAhDr`3n4wOeikt9Xp;U@N;FCTDWGZc;{J0u7L@g>$UKPkwcg8_7Kk0RZGi zf*cSPN8-gj5adQ8zfoQN9nJj>KLEoz8p8_2u#S=pMUn?n%_Tm_KoA*?m8&k#ljFIo zXaL$iUbd|Uj(2)ODat@QPF!^jUtPch@#O)4_CSdyfj-dbt0rZtNd^K*fV`7WtBkt@ z6iVP}p$+Cs0uRR1c>u^g803Jc_+UJp2ZG##@pOK^7(AK#(h5MIPsVG1AeE78S`q*P z?IfwDZ-@E$>htNmN%pPXTG{+AL^nI;K2vU(*d)l)Y}#(qj>NuCfe=le4!s3LlcyUl zW+0k8y=ryw+MJ$~bx5AM`yHww3`^K~V_*9-D*fZ}b z12U!kv7Z4sEwBu=NQ|B|3awtm;VW%+$DUzZB;uF(nFy!U@$B%g6DFIjQOLq{BJVmTO9N zXbwCSa{z!j@KDSFASyl-a{vg*J`{7HLqdNz-)Q3uz*q2ad<8%Z&%^N*0IBAZb*Uyq z9=`IbXqy@FNa%+S8`Vc*RDnQyBt{hov_}}#E(W!;G4HX;b2FdMom6`)=5wd|^2}IO z0buHx!4wcZduHsdfk1mES1+2TKu~`sd;j;#MANC3pG|__w`PNPw_my0G#xyCa#%j! z9Ox&B%jmlh;+twS5(huIkz;dVqiZL5CKly@xQ{=Kt1`#6xY%RUaco$==0c|K0~&e4 z*Pc3uL17)HZs3z+ywOkA8wDV=t)9kn>$d9$?;6`F4{+~=wE45+P(&xxi%8ZBL=T_M zj#Q_FPCmrib8sXzH&Y-KE?_-@oGzY|&G!gdl(Lt24*r(Kt%$y8)SsVKUdi*(>jwQ> znkWwdaMp@RYyvV~F^2%wWI$wKL^VH!SAr~5FV4DpQz=cpI9sqhfegcoGxjIfSd$duH@tq} z3SKgrDUC+hl1|b-H%6w-%q$gpA&Bsq3N5wBcM3>SIOm5M19-{h2{iBHwd;zze3^jLL()O}6Xxh$1J^)(s!#~hWy*JkVh9F@;bU_zA?Uz>HVNTr~@ zHe=ZrL*0$~Pi2*#3x>L>`BQRi7S(PsM6|p`h6e=IGI$;!bqSv5p5cH{eVshR9hJhL zZlQ`HKxe31iU6=gMH2v)s0{1tGgPMf(oF(I1AUkPq4fF;r5UAAdVPl03=m4M&rq8< zW~c*zZ^+P_@5%;mBg`Rb`Tj3apWYpIQ{vy%AibfpZ^%%fQEI(24Vu?_8ud43mAAz> z_E7VU83AQA7(E)tH)S=)1A;1`OB9g0Z%Pv%5UOv=2!Q0M6#hcS=~zwnXm>6!p=um| zA$_fsg8B=RzN0dZZ_Y4III0E?Frn)EdUM8#9HpSXIg8bxN8|REtma=35U79&RbqHc z)|Gf-TM397@>ZEHZ*4KGojYJc<-7tI>ZQjHrEhhH^IC7C{!3Zqogu+qYW`CC5_%=U zwu^yoYSx9(-fiZnTPm6R{Z>q3e5i5mc zn5+z#ri8_PaO9lFVYL@R-VAMX z%kTgJ99VdCmt!k&S_Y&C7tqi5WSk?LOxFbx_ItD1qJc%c7U5G z%E)MHQy&9lOVC>Bx*}N6$MAt{{f6Q{7z=z1AIfU$2KG;7q3ue48WXl*)5Q+=HS818 z%H1ZrDa`2eqJhMWrrC$GE}XkfTKb?qlwGm5xX+jY`bbvWF>p6eypv;w`FAi`*nu3A znQ5c0o|OHFp=BPOo?t2wMOpb#o40C$kqB;ZX#XzWMX~2p)OT-+xOyV)GkQU;wecfa z&#F`kUmwW^w6FcZ+xgiT`q`>< zCLmBWdG6#w66cz$*iXpNW{DMoVd)b%Or4cJ`B>KFA1Lh8osa1Qm6dgYhUC|?+7$zY zVKz5M?~Gb-hmqNOVYX;pt_;~FL-JdgFfp_7v?ojFn*>}YsKvw`mI}Zw-xa_P-2HlX z`I_Px#vKFmTbhx&iW)%-u9$$Hyn|vS|_59^3C%t^cvbUXXL4qTZygWBdT@S|45KN(Jz$O~p&S>sSju zoz+$k{IJhf9s(q{q10CnZg{+7{nayT3!WJIQlZud@47dpxr_;I(}oNWS_Y)_WPBc6 zEAJq3O|*RtvkV(y7{yNFokKfdEX24d!274Om4n3#op;VqpUY}j4LmYsf`uDW^6^UD zusdv5bx}i?aC$c49gyh)UCe{b3w3MO$<_(iFp{|Dcn(M zAlISv53)|*2LZzBA7q!UDV}Alb~S3B&uZ%j#_cY1HkYj=+;G{7V^Caqr5|nNQ7lvB zYICTy?9KKKcfl*|C$Z`*^ZC|_(*>`e&#sV-C9m~0>VK3~{y4T)I6T$`&uAS0TZe=PhU*?T1r9vV%pdC$=>j# zj0-7g93W5u6RHS*DeH1;1f`&UDYHb9d9A-u|8iFO>*z~AHNTvZXtS3g=_j*v{VQ4i z7a49t&;_RWE7|-$it7T^*+0!{y=1nW?=Thkp|kY^jQwfWt*o(*L4`ldxByKU>J~o~ ztslVnv#h7L!~p*Db`14~A6gjd{GYdD(7&&?V`zuX4=s!;7+)2J_SpUE#b0u)@=of? zMqpP0LH^6E+jsWVh4^2loqfM}`Kt`mtjWiQ4AAF0g;q)^)+hz_uhNk9i|emt7~{N9 zLk5^oMfhtOu|=a4)URbkNOgvRzD`1oDAaiZ&|mZR=j!d(^+4NEb)NXTqQE;UTas_I zP__O8CRDBe-)Ny~{r`revXyDn|0Xk6Hf?bm)chMw6j5zRg5S(A^s7lAr~-Ol0IB=U z3{!uULiL*&wth#Y@LL&ndN0(Z2TZ7v;I}d?^-&7yx0tzQjMYE8p}2DDt4du}r8kwE zAKIG!jOUe?PJLdf%0!v*2bd3Soh49wg!Fdwf#p-%^7t7<9#9yH=97z_-J@qtjz5|n zRNhfET6bu?g|Pth=HuD5ZTiaq-d;uE+M)gyQvB3>M_%+M+7o9;XffKj-CX@A&v|Ir z-ff$w`94nT+9pSron5D@a}(zN&wuq`a>UqHnVt*fczp6;vh^cxhR6yZVoGll9(Pgg zCErROESiufZe{cR=uk#5~4(!OU&zi5z zik?GfxR)ILfe*#Si3`@)!HiCt%WcPy1I-sPiixcs^eDCuP;PHO<{F@>oLs~JRex{; zw5{l}r$_Pfc*Yd;QJyjdo3F>^#*u!|vOCl^xm|WQ%rUv!`S@-n$9tQUD1ev5Jl4|0 z@0#KCJ^lL)RbVZrj0^%$1m)A+c(-%q9NZs3>m^fv>)r47_sQK{bhne$BSZ57nP!?W z?N=LWs0M}xpK`(mqH}0U?K}9rN#F$CI)pB1juzbx#ymX43!GAcri>w1&oKrqbu7ofP|59d znm?Bpi)O1eBZNh3C6FTgK30O(GpQ+``v0jDws}p_Gc zaDKUE&+oqXHI-llPR9qdiQm0=lcshO-{btzYmw+M ziv0ku*uJa&ekenC7}EcB-L)BrmG!$D;16e-f{yloTbDNs6@4wOqQ5vf${*K zZHyLmZ=iA2Md{F(YXj56DNJ8LooL#Ssu|w_&Dl)aD4EaroXw=#F|db=nyu2)-*ipq z&enC`4h_t;etX?k16qw5b2ZSmg3Z~$l;!PcG0(J7V=lJZHQm5mV>ea7Ath;mIXls& zTjuP<6qUkg`xiIoXr~YJ;zm2wj=`{Iv?5(ZGi?jkoRezmg0*x1efz$rXKI>s=|Ifc zSnt*NW#XY@cfMk|hpBqzY!B70h=*#m%4mA17Jqt5DSw@YGtB(T8hOL{>$HY>55?;= zi@KCvr)}rtf4drSu zYa-HJH%}fYVtuK0b8+gNU=gc!uihA~0rZq;SW~J!B^rh!vQ8y5c>wm5X!t_)xVmR09KiZYk;v(? zuM|lgw7ya#dOp!tiiGDA{Uw?_75huH`JnZeXnG9M51PCivF#bZQ{a=d^b?Gg?na<=lP z63fDE4v31&7P>2`Kb2U9x5x=Yqi6+B;_l~yOZJyAWVj)l&`fxd+r{fT!j24H-^%*R zV>r7)m@V}z{$gj_$hiX;ZzCA36btROxL8D zxN=GArsm$taObvn6RXPjVi@+St-MZ}iL1i@mWrUQ3jbRyiOtFtE>IC~#`u^={ADpd z?h$`kj1LeMFN^VUkNC?NAB}@K23}se&a2OtN1rJI?ea2JlD@agqtEtDtqJc!0MOQi zuN)9)Yvy})DIn0+M7}3Xdh04zIrHk`x|j{-NWVU2gFDi%kJ;dk^y_0bxFh{~W`meF zNBRv@9O*ZNqR%*3?avEzZ0?`AzN4 zO;f$xvg@&langg*5N7QaJOz$uMSxN@S$BDvH+su z)gcQYM0<6}VxFXaO~?WOv}-~ZKq?Da0D*Q*$YP!*@AlFQTm!j1n5PJ|?I8;w(6%EB zNq(MyYvUmW0Igk{%k^rWia@)z*VMEi)#M?CqoFvji+8^B%=iDgT$XvENv78=a3^jc z(5}O2O`^g`zoYVa7nRiCju4gkoIkE6_cS?(V~Vam4$FT)RD4{QTx+Zha*u0XX5_e& zeL`iIlapF}LJXuiaNoprj!@iz`=%i04%|24q!DG1yD4O04%|1#10evoxVc1=7vq{PBQ0Vc#<7j|b*U@;gg3sg0c_nk2uoM3dxq zg2sDtT(_&YR+i9x_gB7^E=#i9bKYLrfNC{0O-QqG3S7&csJsbz<}t& z-6#)DojiPx((p~QFN%RC~+JrD{9|#sH_NZZ&^`j_m&kU zs(Z_d@b^UTGUV0eC%W7OQSpiR^Lo>C`AH>3b@E9iMeT;31Vv3YE`9G$uWz_=t==C? zm%K-L%HmIl%z3nY*kZn;JZy2-av*qmN+{6owudb^fAX-!w-)mKJ4M z&(G?Sh5pf*<7f2<&rQW$j~=#+`fSAqk%uj=kL7~MQx@mWJP^x=E#`p8!xrbx{12-m z!Ch~o_CV!nP7a5>>I2bPoa!3Y6Tuw-+?@#S%>AxR34U1AsPG zqG`0p7P_{e3>l5_*{f3v8nu&^)6NB+YSojW>g1?aJr$iYk2E0P>X<`X^;C2U2y&;Q zQ+8dT?j=;MJ!`r|cTM`t<0OZo#W(qf-E&oh{MS zsk6~3y8^GCjZXFJ8Op=X1((=7zVI!oE_Zou;UfjWm!2M908#Ow=#<^;Rv(H^H5#=? zD;$QT8@cMNJ{t2#H=WgIM5h4Y?is&HPorcqEi5%JyxQrQ;$Wb zC&AnnU8A9guQ2E zC?P7mYOsg}UNr2sb1IGCZz$Eg^EtU6L5ubyXrwL9D~ognMQ_yjOOqJoSg%l31EgWB zcPzC9!Ib0tn!lU^K+^N$L9wgK`OD=fZpqV;^nCo-wT_so?5N!mMS5)0fzO{;)Npn5 zT$3PL>)8JI1VNuPIU8-3hbPm!K32jm2595MIL+W2yT>JaI;>w4Ee>hXEIwpnbPDMWnS@32Zk zQJLZIIdsyW4Oam}p$f)$|Ev?usaM7I$ZA)0QH8^y~pu<0jf_ z6NXHYh84Vu=n)EHH_s-|6*2=(MVH-ibeGlA+}mX~c10ylzfLsoC>A(B_u#1KqWa!M z()_WkSTtLM1eb~nXkAx!ZEIeTPm7*3Mr`)n6WZK%`U>7 z@34Fw72V_Tuy%_6R`ikjO- zxmD13c~-2B=0aDsHLDAev%HInyV+YVqWN1aGIeORRy*`hgy3VPw|2f7jfaY5)A)H{ zc!DsAir^6!yLK%-hn%PIar^MZkP)E$B~#ijOE82tW(vUK&2??Nn5i?PL$uW&6kBp= z94-2J&N+$m3Q;DbcB#^Eln)# zu%<3Xf^NYd!7Wx4g7$>B7Q zI?L&>bAGH#&98S&S!O^cF5r`k-k9BKa{EAUnK{s=)_s9C+)XiY%PHLxl>CRr;i4~* z=F25TP%B3(*MS zv#J}#l4qq%=yH$3i=de}gWYb?NpNGVSfrqvgdJD(&5_Z>vTObA7csC3$|>|+V3({n z64BPL2){r5D%I~3$W{Jz-(@?AeY!(bTyH=9HgK%yxy7|}$c{k1jJg7{go5BusyVQwSP;LZb`@GY=*-s4z}pBd4$74IcwrQRUKMA~9X&aAOdmur zXW^o!j`)77!~7fS^y>U6T)3e%!}YId7Tl7fAxXrU8#rC`m%m?R_r4!q2M!cPYvRu# z$K+%xKbY>JsZOTqWq0h~2UwCs^kVss_^U0l1Gg8AnR9zC?g(YB&&UR=d&vGC*j_AZ zCv=eronsH|IX%fC)9T^nS6BEaHp=Q- z*R~5_c)i9AMW?9(+b8(Ux$r)rL)Ep6%ky@E9Ttx-51&)E3)`G~P8t1p$TDF3F>z4s zwC9IE9gic+gZe}^DTfq%R=!#(1%%iqY_@)iTY*_GZ(PY!)M3G zCu3KBVFEoOV5|HPC~QO*y4fIhY8+fd$u;nWDfJs-+GZQIJBx->J7LC&Am%&i3lr;W zH$)gMhQn#GIW?XE^%7a6??{CzEs=g88C zk?lY4iC-JHs-oT=%KG7rDK7S0jrF#Wti2WOnbH&Cl z_xx(9gR1*gQ`+UIbkyM#Lwk`4uG@aR=Pbb$hfg*hS9HhY(sO6dkgRI)9OS#K_5rKh zxQ9JT+vwr|cye;-5);I1rXyUWNj*`@gUv{ZEU%-Jll1fw^Rb>8op|Xb#=N;;-zLsO zB`Vzw$tFA@P#fCPrPh%P&uL2u)S||!JYda_OdM__l{D%y<6wzu>?XVTG#>6M7S4jZ zbl@V`Fy`Ayk_M{VH15$een4D)5mYd*J~FxUjH$poQo<{q4nBza%+$n+iy-1zuJ#(1 zeR-RUMXvTI9zA``5@$I_mqM(2sNc#CP^8U+HI3pXx)iRD`Y2Y~T$p7eWf`z0iT1%p zD%F0;KY>NzNHjAQl!aq-)p`x?wPGfUXnJN(M>v7U8KR#1Nb8XMQpB?okOC$2&XlymkudwE!#2*|bJ3-$8 zKA?65F&BH}*JY02J9UmFnAGp@hE`wNpQ`S)5O?S1gl8F=@!fdxYC}Eo>)rOFasv6*@bEDr8p!smV=C4B*IoXj z@(@O~axK>;hkO_QbGeo;<57{WHMD=!&ZS0{KEdqoDAEeWyMPfh+EE022vDgPRAeE^ zEKOW(NOkWXBSaY)JNnAyFd2rmk+{&ldqIiehF~%b_K?DPm`uEf3DiYq5#M2)f5vr3 z^_{BSxTaXbpjq&fWW|l0iZ&YELyJ=BlntGdVZ`=JJB@Bx|4dcUlWPxRJn1s7>FJ6;W8ArvcTyU zHQlw%Vk-)!X1jL{IS(xjy3^}SG~b;S&AACQ4i)_~SPDz-#a{vUl_c)*PPN|!`x$W- z3rOlix(japzpa5V@qItHoJPP;gXJyf0rqjN>!Kw1S+21{ZfzEC-8V+xhNciv+^AkBqK4n7)fcHs$}_y%IxoDjwljQ_XHXk1Ui|+@ zeXcLgl||V6O|*`(Bfzr+dmq)}YSB7KScu6%(9k0g~K6+=&uzE2Tm`)q* zGm}ui0ZWz*c3g_BnmN;hUmtR8y@7zfs>z5QIa+7&dk93^!dHf@@=j8)M&r#V6>|9^?8@s*e_wViScW38a;8?8<^^^m( z55z3rPFMz82kN0>F&g5`xk*0otc12LPZN@B}nWhMu$KBqWnEPHwb*@R@{D-VqGJOGe=PKBq$6;;! zc&PtbdhC1N{Y1+f@~W6CifJ43x#nM|)wiwvRYiW0rcdgT-mP3*+}=5hNn?7?N{6xL z-_?q~>81Jx6y28&x3V+d7;4_MLP}CNi!55l&{zbd7ADg6+Bd$werUq_*4B(W500KD zKb&ohy&tp8j+p8gcU0Sv8Cu`^xX#mQ1bvo%;)~v0%x42gYWmYlWm%flukMgnC?&zM zlV`aor-Ujh$35_8*q&zDnnh&LS*QeR;~K1v=y|G!ADpzAE^Ldved8*XAi@veb^l)tCFekH7B}cbiVehtC zjIw*Y=D|l2DYUTR#xGj`Vs14*gYS7;yAtHs>5GOP3+gPF2_%pj3H$CWo(@wQr^B~6LVLTHOlh}Or|}=;@gSe4)8mH; z?09O73|shZ$nx8@Z5BTyrp5T#edGJfs_m`d=`gLdak5x8Vi7CNXJgs`x~X^;*Au$} z9hJw#d4_PY6P;GW=E}&3H8o{QPAqkM@@#WuWCSYhGj7_HoLP>q=91`Cnwhfb;i|aQ zEzYw{eeY3>J8Ob!w~KC(6P#K>{W(drF7res$(KUi;vMddUNqMvnA(BcK?>epm}dGaQ+2kt~M~_`chT) zSw^1mvwKFGj}N1PK1d?ztPvWtO?+@t~6PWJ$5t38dfkC@pQwd}HU6|MHm}wolfQ+|*s& z6N(PI$vMumJpb5dIc?ho4mC;|B@T8+S2OV*XqF?teR3scD5V>?`|zVpI0w#hTN%rc zjfcnT;_&5n78?wH}@6` zd_w7z?iy!mzrHJ0nvX9Q&N4r>Q$9L+np9oK#)i*wjw)}w+J0r9d7|hnnUqz`FEPbI zd5L)|KKZ3_BsAA7A4y53Wl6zxxkPM!JS!H?Qin73G*#x^pJice{~%h8pcba8vpq$Z z!t6_VBmDEL+S{n)3{4?c2-$z%+1=iC1#n(6I}iA-b=rAU9xSn1od0WRRh|375f`Pl zFW!9uI)_3F=#!slE-4nx)|Cxwi_X$fO_2?6-Mwx5_N_M%46z4qw_A7Z-?shQySDaa z8?Gw)XIQ5jmlO+U{CMxyzRHGQ12b5J?N z8W_OQV0ep;pM1zoBSc~BE;^n#JaGyYbA~vKkP?)P(Atjm)x`E4Jh*gH^8_Bg_1?V> z)={IqCBy#P{0)E0h|Rv8BHi(#Fd*-5KhVMPiehP$7%JC(%mx{8_lyupVxU+OrRH7G zR+i(KvpCAyF9<=aGSJJ1MsbS76#_fRxl#A;y7iG`Wc?f+p>u1C%l4f+b#(OXo^&n9 zR)F;B_=9mkC@anr1FkhDrWsWF>Ki^rEbrhTjNolN`J6`SxO6Ink6AFKE7m^-$UD|< z8eo73@*kDS&3tT>E)mM#{yaE7Zng|MvAF1uN=bCkoTuSzYlz#Uv!i`={mQxg*3(L7 z8uq=u`I>jL9N_vo#K0~_{~pHUIOgNN&Nh~IDVI`TVYB>^Gi>ih`ntr-3_Yo!b0ThBhrM0lOQgH$E2`u5*`{5wk(_kX(gjf;Uvf^>47Z(iUcA6eSS_ zFe+XukPagNMYcK`2P&lJ5qRRb^ zG6fU%Pf-L1zLF14i_8#_`X+fdfQRJZ{pL6NR5I3_J$Fn~c<^E6=dzry<4V;vczkU1 z7HQ;%OVZ}AOh%qZPmT|dkjnn# z$zl*RrS@cXc@&}}=Z-;COU*HPgOYuY*d<-a*Ge$w*@YaR1sG-SRRJa~c( zI*~_7KAx(2?(`XK=cM~deWORGE#b1|Eb?DnDo-Ud5VrbLrrQgNrfd`W3_c)7)HV*w zLT3;L6cR1Ru6dkFj~uvq z`PNiMYpbu>9<2d1YkQ(dpJFp2xK%B%R`asmsZ2F*za!B2_%5s65JmdVq50|eS-*IgfI=&H*})9AgK2ioPONEB)C@<<SD`l@EyRq@3D2r4{~SXEKZHs&FJW&%*5Y=HF|MT!o!arO12 zE}HKkylh;3WeZmItFCUr@Ut=XUK)2(e!KVASlCpo3(7X7H*L5rFnv;Q+Hg$^hVrKE zx3pjp+l}X_z6WXj&3VWb#03~4ahQI`b=grNlTCl2bOf5yc$~3JMYH*9k zlZBQh(iUXslr3oGmP-?+hppUl>Dr*|fh)JH+uVZTdCNA7`3o`F*qX;&u4=ArEtH{2 zJX?&%tt*lo0aba$6+sDD)z@xn!BF11mEiOy4JPNd6d<&!>9`GPco7ru>fG1SYE@J2 z>cT@TR;x_1Ye>0dv$3jabxq;%5{UtNUFAQxijr)v3l&w9in>0(#Z?p#W!Hy_GO6DX zVX9o_H`Mx#I1m+YXn2?^m-h`3rmCj-wxi}-0sy+bqqZ>6G);Fba^@+c%R6EdKtxwX z!E~hu0NUe9G?9GVA`eVO8EB7Nx8B526Y%)jrl7A8d3-1hia>k(lEjJT?&Gi87-&ql z8*3gVv1S21ZzM&S1P27#jSD;uC=h5jMwG;wqA)%o)Gq+7Jt5RD5NJ;b^$P^r6GHvg z)SH`X9+eWnARi_m&~CcK)fW(GHxY+YLtV?61vkeU0RY;~HD9-YK)ZQa5|>oe&9O$* zq%?Nrw|Z|(Y3wSLyQ(|q({4!t+HuoFm!hfZfLKE^6PJE!9w4;%lo78*d1H6nyIYah4OKUF&1_TS%#c4AdKx^zPbeqIlg(T z4&~jOB~v7&WNEkNdtw@!%Dc5BZ7T0Jl)IA#g!F9$A1z@>-$q{K5{C3`Bt~}5Y*%nQ zx;quC&$pw!OBnRqdzZFg&~ImNkkFafceJ5vGThOIuE~8z8@gun9iV&1*j(J1?@b+( z1n&&(g_4oX?kYP5NXPCfI|fYkyULCML;9|=V5%<3rTEh{0|b3PDf>#8 z*7nm@3B%d`KBbfM))Dy+5fC>O>$Cl#Hgt&`JAA7g^dI6RY52UuC1 zv@IA1%Z>p;`e4~HUoZ}q9Rr5+!Lnl(?f6jHF+k7{m7ICR<3nY~Jm&EsI_8`aQ22@Y zeTg$^jwcprp4o!)q>?j0NI$9M%om&|m7D>?*^^4n>XONm(YP)%K+vCDat5q&O3QN4 zpWN@s^=h0WYxn0*O`J(N-CwK>nOS_|r<9y|gyN@^oOz7mr<9xlL;5Ljrp+~jaU_K| z)leMiSYXP;24c7j0Ik(^IL^5~2*YInXptL^bFK}*(GpEF{%DD&E*>q>G~17sXw0Z% z5u%fj(x4s7J@%r9n?Ba#`rN}!A0tGkH26Gy%P9sc03sbJThq6kVz5%Cnuey;Xx;;r zAI*HZ)+j#^;ZJRhPXzZq!Y6{BkMD`#-beRDaBpKf7JEe>*|8E$ogO3cq1HaiU}G!} z_jN7FPm5se0Cf6k(P<#ip4Q_h79h}`#)zrYbpa1X1Y!Wt9xTz+=?6W4zbdL`tgUq& z;7`Wx1pu^@C7QI$$sXT)PzKsbvPF7p2sK57t=p1(szlR-ITa&A8EA?K>uAtFonuh7 zZfi+CU7|_kr+cJg+QJsjx1^{xMC})Y{9#3@o=7TcOCYF0J`+DBp zs7^-QSO8j^EL&52}!+9q5P1qh$vg3ger^)u8QXl;TqK-)EHKYJm2PQd|oa#y3sng|zlqiM9~5$0D?F zJzoggW6S(-S10wYX7zRw=H<^z>O}PZvYv zStPFZFzA0{`mPx?z5 z)n~_%sh(d#YtJr6_!7{b-Q%Zk%0PSepkirDSuJhUo|9q0RQ6UN)*C?2?FxuN@Hts0 zfj7LA-Pm(H!?IA9HEPez;M5D3u{r~KI$R1teQt(2h*D6Wt3=Y?>OiCRBN_A23kN_2 zOsE5({z%4*j8ag4Bx6cC>hebIc^SII3zvfmm{6C4`n;@1ORu`K9MtD!t1k=G6^+{S zGd@)>TtQy}J+W>*Ujgd#Gc0IP3hMJSzE*Gb(njqCtyV8(^9Sh3o9p?dF!6${OS?a{ zdMT7%kS$J3TneQZw3t}gsJ$>_Zh766paOa(<9fal)EA~q#(MzSGuL!D2&Hl2KZn#5KU!JG}!7HFs1*GoF6IDQX zeR)tNrhffZS>?4(b)IUzDw}7#Dt3OO_Ueq7^>8DILcq=hateC25?RwPN-2CzmRn|L zK@OUr1yc7lS+5d9D4jz6C$h@xq9d#2}3(nkX^UVuJcmPG-u+tWu| z8U?_G+D!-*LO+#c0E7$xT?T+w8j=M>20xV~TQusg&nj;ShLn^2^@$;6VW|B~Vh9L^ z0G%Pr!vGizSt16&U`Ux5YCoG80)inxXUK9f00u*ri~%r)x~ozDk6Gmxf*~bd|Bo5* z*40g=0jT|ahHlmru{;0*eOg)~0Km?Lsg!R1e6~c^KA}SBjp<_mqA!5n7eFiR_8i)P zpuRC<1LLR+;G42M(PeigFtsYeZ^}AtqROOG5w{ed$({Z%Zl4fj|YUTfYIVbiQXH2ZW)wd5UtSFsQ#h ztNdCp#NuCjd&Z7e-BcEX+B;J2ZA)Vy(1*l+}HK9aGSb4sM%_-ImaJ=!x1bOi?p>PORR4+!cGwX6O07B_EvrTuT%5?2Fv%BtwlCqoB ze>u`RY3oSEX4^RX5T1<>hEM&XB`znpG;C& zV2cT$Z4RKMu6;5q{F4F%^^-1@yw=yKe>$uDelVm_`E-)CQl!*AlVNjElR(HC&}D5& zQUI|!L@881lZ7QsX;NyRO|rH;DFS^f1!$$*>pH3!`fQSQAEt-e=Q8etyil?RbXik~ zR6dt=txKg)`dqetdjg^Kxs01PNu^Kv`1g`h1%eV_qJ(ti;RL{9y=*qgw4hWgWhsR|c2be@>q%5L5wuZMGaPfQx*$Z%JGLIpd4JehKOG z8Mk%nDG;cDS0&I&x!Q_S82Wt1&6cK7qf!5(tn#P9P=lI(ly&Glit~dQ`a;r?K#Uro zj~XC#zmOCiAXLAQmd%D3`eOQIfnW&GKUqL4jXend@OR3*LvS{T>d!2N+mH+YL(-=Y-n=)NrvIV3n3=ZN0>tR|C6lC z=RF{l{v^W@Dk?+iPcl+&xzuR&%V`VKkTFSM>bh3ZFDGq9DX3reEsXf5?pM;s2jrk1 z%K$D*s}JP^|X`$ zAuvE6CP342vop?Du%w1Mwn7izmc@TBF(G6ZnY{|1G;c2 z1of}89^c&ng8J8LwO0&t!CC*CEdSfAYO)8W_TRMnL*;K~HJ>|+WULhE;{-_EZ)SK0 zYS;j+GIlQ)1@r7%8D?!S)I0-pO}NPB*|)MjpRs_Tek;2~UuRxh+^GLuR{3usU`!#k zzf1ew#S-w}Cn*6z70|m1NZr5BdYmdCRR2CnX|d$+Uo-O4i6I~a4Co6!pp|wcN)qsY z%}7xvhL$wy|BzMwB^biUQu~LDK=&h=04)szw|zwJO5jZndfr|8}cYp#PC^%_uP} zF@+E4>HrW*|0BbpEJ~sDKeDC1YEx>sc!PP@8R73F+t89+iUlxH(tv*_aQc+8CbeO!`)_IL07CWO($u*`0)0--4T1Us1iOGPZa^yy z?F$I%b8^$Zd2MN<{=B^Mqrs5uWzWmmkgC^9#nAKf++_^}Lx9c@Aa$RQ71-JYgzEG2 zIJ+$sLodkV>;?oXU_uo`FUaGIrxer|7r++OGlT84osul!_)LAJji%i~PFOk#LR9u_Yk z1|QG|9}ro;B#-q85Y(6Cct&d^0BPhUd7Sx|iTRi2VQ~b45@4bvA-yz@2}!9{Ua~qU zNk}ivFZT`0G70HrX=s380?-E>5Y(4(%nDSAhxke^LAC7*;_P0@^qVWWM}#ZsUk-8ug#eD?b;LUDoQK%{zQMJ0LP|NZ$+);{xc5 z5FmBmkcWv45a!;Hhly=@qyF=Gr0yHjHw_5Y zH|m?#{V^u*H|G`IIaO=2aKG8LQ(n8YQGZKbd3!V`ll5Cvq{()vCfi%{{Fh>~0aN_e zJT^g>ikr9PH6H*VxB+yP0!ZDr<$XQ?fN=9R9{?!p%lgGM*?>R=^bZZtN*DSp00i|H z^D88SytcAYe`j9#jo?>y@^|K8BwHzl-j${R5JCs^?+TE*@5;j#2nf}8aVKS6U8%1A za-RQsbQPG|znrsA(8sY-{JuM{@w^sw1O&f;-g7`JZC;m3q5STAhw=^zl_~R{7OFlo zKsRnt2*5fD(1a;?bayN zA`R%P@=Dv4y*FQTQz`}Zz4=Z#v5H~D_P)H9SdtC_^sOO`?t{ANNVS*z%f59GBc1gdlhU=ovb$Om#>mAfRQ zLq3o{?#4hxhkUSwDjfot21h#NgDq6q&_38gMTdN-H8|2CfT>mKkPo#6M>^y~t--;D z_NzH5B%LK?#02ys^Ge+>1FpC#m4f=K`DWixt>lKe_Te0FJx7%d4bXQT6oUHUe86=$ zAgCYCuhbo(1_vA3M_Pj;8ya959NEx5lCRntgk?kfNNaGgp?x&RvDjPf0TnQ{DjV8I z^MSRg6x5IAyn^ypv7!B1OTxXN0wxK|DD`VC3Co7|Yb^<5L;F}>drHui(F|~N0zv&) zzUAHog8H%i$@fRA7|lMO*Y-uBjAnpIE;7%2JYRoDDh2i9`CWTKW$9_ue=D#2b|?wm zi2YU`Hi}hJ5}(N9@Cd|g2XrL?NZn85VcG|T>L+qD?K3SJ^-t!N-wmpR)cj=LAt%mS zZBSG{m8b$i70{^yQukAdDj-xpC8~Sib##>v1S()cmAd=A-29rX6x83#@$r-(R!iOee%i4DfeM&V zMfmsgFr!ln>hJrG6*bYQ|L45&|Acs()cntRhaVi8>dWU-Ux1(r=u`oz`}x!tK&XB` z^`)sM|1b~ZJP@dW2~~ah!@TgLEv2CTVQ$8Gb|H=W7xKy<2Sb-p^9y;0ck?na^u@#w z5L5x3Dj;>gm>2?t>K7A3mx-Y-<#_l=xImx+CR8!>r97;*l!E%D+^n{!??(O0dF9W7 zq06cH<-9{40=3%ZV(2T0At0y%I#odGekCyk2-UA7hAtOFf0{PtKzwI_em(`X(y(*^ zg8HYv6VGdF8uhQ{mA?vx)==}S>5Et+hW;Wk1O!8X&JZAV{~|F22-Uwx46PAEf0-Bp zf+0X>2+&G{AwW?7GBI>TqyF{0^4G!871aEC`tYw1L*GaY0l^TUGXzN8ZzP5Qq52JH zD6g%>c9K{AHmI(p=HH}lt`*g9CaOSC1$3%_)ct0n3JBG2rf#lPH@_8o%Nh{f1Wc&v z=C|@rzfz_Y)Nkc>gNqJq)c;Ff`TJmK9X0<;-r-kZ>%`FCC5C`t2+*klQupr?Lx52I zyVT9~jrxDfE8h;P>#6zQ@(#E1tQXb)o~Qyr70{^yQun_ns(?`a@2Q*X)y;oMyD}iU z37AmT&3{O{GD<=H2kpu9gM;G5m890}x^WbgF>V{pTbGK&bw662k^@ z`!7ihKrjTDP$h>dSMhaee}VDxj++KO4F2;t)!6FQ~c|L=c65w&fQ{7hh0y zvj-rBFRX^o29SfM08~ib7go(@1J_as=S5Yw*$AR8pqT~*TB)1J0qMn$R&zU|2%;{a z84d(e_eZNe?qg#2S}$(#t5Z2(;#Y6(UfklBdGKQ`eziviO#C7*z>;6&{E}8(9jpOU z-8yw&(yGg*_@&i2eR6!HE}*-z08;m*)tI$cHR`XZR$dh&eHArdQH?FnRTBLxtFfE| z!2zJJd4SY?Wp$bFApw!mE2}K$8tH1I{+ep#^>dB11o@ikJiDx&S5`{PYpZU`0i^K9t8Qu%i%GYs0(NYS%K93sj3@f0U7DnRr3v6yaiDga7_ZK z`?@M`a-1%5{^=GWjWl55S0nw?)!1rmY}9|YTKTycy^YlT*=mRUEo-%n66YJzZ~?&q zpnv^<)O|xWHmrd3`wi9Dux`SXP_4WvsBWU>&sRHqOTS4}-bx1@x~U&`RU$*BhzYFIL?`1g7Q$pxt>3r0y?PedPgUPW)1f zkU9hCii1Mx{!-P=rPM`sZ%+ej51<5^AxeeReY=lH4UjqUj#kGtCjecmP)OZ(B(0)% zPqlZp>gwwUOm+43zq3`BGw{2rZjGcB9eM%XYz0W&cU9f|NGXTjUv4p|Q!ZeVLnn2A zxg`fqx$kbtLC0FaRJWk+yQ}?aj5*f6rzIqvV*%YOua(z=?Ea|0@}eNOF zss8b5;!E9+;WlQ_*YW{oGsvU8FDgJF0YaF(Uqu;6WQd#F8 zAo>Ytsz4xhf2X?Cy@>(o=kHW`tn8>1ezF=SjIAajKwroJt#sMCAS@AmvU;WKyRA|M zpQ?uO42V_%eYXP$>ZhuNWpJLPDLz$=TdS>tezzJ{As|oz?K^~Fsgd8UhOY#mRSti& ztx_YOu7(vI2vk5fkphDH>1v;kDIlnyj;|ELXqC@Yd6woZ$qWbR#{mjK{Y_Z z0TNcpNeB=D;YM8Uz1|lC8Av88nMnZkLVyT7MMOkI5kx^jWm7=eRNPU)6%`Z}#RU<) zE>{F`dEct){{7BuX9mOjzxR2cKOvc^(_Pio)z#J2)qT25wE;nW&{SJcu{jUrgCk)$ zz%V#sf*#5TN5XIq+2ADU{Bq%&F8Y?&i22m~rsI~LOql)4g@;}AU0<|6MGreJSj&*^ zFWT@O=TB>a=ry1P$^ohS9p_JL0cr3%&Y#vEP%b>;q91yy2T=17*J8Iq4-l%4dK-}f zK^4$eH$dt>>ih`{AXFc9wn|W$!tV!$fItN_h5)tHGXx0g_l=4HEub!_Kgdym9`gnpLZV}U0abMDG3WP!lxk(~_#Id-{Mbc5@nSfTnm-QK=s*#} zm$RKl958l_~tWcMSys70_%PpqBcbJs_w*H&2m>XSwjCi+=4HT1d?&oxcaSP#F4E zpb7+4K%)vs-CsFR6%eYw^8RPKP#AhDM-_$u1FA6eRE{bPJrx)_s9gAsi~h?qbPzRv z!+S8Q)CUPezYSD@pbBVI0jc}jKotN8<9fuIU#Q~{~`Oc+f- zs6G=$^AHimzNfS~@_@l7zX2#2!AcF{jPLx)oHFVampbvjg* zkLO+TcfWi9Q~Z2b#t#*K{~Fc+AQ}a|CO|;yp2s%} zlp#Q}CCDTUJZv?@oHoPCrF7pD5~l638GYF?PO%sjcE`9}9IUVlqcoE{!- zk_@1{+qAFz+(#>vC-k4Xk0v6~&$jMYy^8C^`YO<@ykV&;12#UU3}gU80rb&DbR?c~fY4yW-lX{new36O0>u z9$c~*m;WhoR4?Mixslo{FRV|8(l$ST0dxc|_0_fImN7*TPZ5s&s5C^hu37!+ciKsD zns;5x%BL2)HjvfgjLmBXR!SOEk^`(-LlWp%hkXC+UD6S_QUd$%4BD$a@<|(y1eG@p zCqi=Yha@%V;KAd8H7nQo$L39^@{|dqHn7allyiA6(UVcQys0>4Ab$`|lJCm9iIV%l zX$SJ;AfwW+drM*2rNyo@Qh+C%1O#|A(%fcg-ejIb9|K~VfR-EW5?QrLhDVByR zHS3aO0)C-llr&i*D(&=X#i^s{)%3PvYo-Z{oBcXe{eIh^bp_-tz2#RIJ6D=g2CcSX z$g`}3VNgSE>#m~i<25$ZKe%UU`s|r+nLG7=mnPPg=d*|!-u?0 z+$LGwg7_tWUVUDkQAH%rzh=xAg@hM8OSlruI5unFa0xe#g=F9hX4{2KfXl0kU8ne` z<#}U%Rc~O}UPjMTH;>A2c#?bx$Qk41Kr{ao!A}gIimf-*kzC_#^Yf%P<7y@_PiIPj zWcY0Q$;FPEslFH7PQ@YvuBq9wr>tB3C*+0EJi1!!UAp}B<-m-mx4hY7O`lhs;6Dq^ zj*%^4`Uhi08KjA0Fm^C6NR!)s%_;g)z0I-d!xoDX-xvoKR%uL8ZT1XDr3=PS-4o-} z*UQtl{_}Bmj_kDmET_|XpH|d!d_%=^^lN+1(qU>}F2C{>M4#1A`q=J$^yx=fcGK~) zKaA}3ql@L?YCXmfXOHpANcqS3ryp4CswXr~j>Ncc6+2%23NQY8O)Kw9mVou5#(sA& zb(k*rJf6OqAjsAUf7p~~bOftbKB(BLCO}s+)0&3sc8k=OMZ3%$sD82!tvn6;(a*o? zfv2*o(+^)EpH;umS!}D@v&znQb+5dQtQ|@vnYlpnvN@TOwyyAn!hFhm@O*TPRaj7L zAqT-JtL16>aj@B}m6__?fP5yv#mV_nK#uH?Ciy7NWZ} zHaBhf{~)L{P(HCZ3B)z{0Hjacw0R>tvX$%lyC-Vq=sc}>So%P+x+vA!-sR;C7iZc~ z#Vp!ju3t&yq70#y<*oY3@qB=T+$vk*O`F${-(Zq*JE|edlWk*AP8O@QV&mpjYc{N0 zr#Xf(DEi?E_8Dn7+6OicNK9kPrcLY4^0`(=rA~fhD!*LolEVcR4}-+~=-;w#fV9C5 zZ)Aj+&N`-~KEH3m!0pIz$wmMwQM0L38d!0vaJ#~I_XbQd2B*urr1&T)^)^s#-O^%d zD4Nf0+Hf*-4km^qrYo6M{Ub3T2~5-FWY6p$II@2j3ok8_2NTW*<91s<(uQ0p>-7}N z+%;DVd-5jR5?Q9vhD3^juy~U$BcymY$*u3*yq0j7+6GG_+K|Qs#j+ewI^p&$cA1gy zKWoD&maknGHOUm(zI>vVy__QPJ+x&E^~z052lTt2bf2OI!yP8cZlEa$aF{}6Su`cV zynfzoIXxRP-x)o$7(3G>1#Y3VDiusW+)u$5li zAAR^;7f{?E1*+9zG2gk5Qs-aD;}N(fe_AT@U8!`A(C;i4$a1ZhlQdVyHw($k8GVsR zzAG+v4z@sJIbr|2D_Y(=RU7f@dsm1a zsAJYbP__R^{ZNLa`b<-?ZOLkKt6K;>x38+fvX5`~_Ej}^H}5ETlOv9X4U75$W^2Tu z;JdPemQ3@Q2d#>MRGrTu9#Z@AA#TM!#YyBA!S`FWqJE;v9#hVy`KDESHt4Mb;!B@-HZgFVRP>f2T-Pc|$m03=wFpQ^dB*;F*s}sMXeR)?X z9pt-B)hLykQ*kWXmlMxZLph2&Q2OWHfkF0gqRM7`_OSZRSYns?(UOc(Hh`k{bpyCR z;V+pZerq%88I?|?7u$|n^k4&%sg^YV<#!6zHqig2=Q)$&qC!E-2~4bPYt)9}zn z=s~Dh{Y5?qD!UyxpR6-tvbU47%<^(vwmcjigg!ioB$(e7PU$LaEVG>+byy^8rkICL0=1`2Y*CQ;Ir z>#}k3w5szbfYIB5wJ}TYCaXr4-i?jA3LdXT+LN_{oPyya zTHKF3je39;PJ@RVIe(fE>J4_MEW|lJ^O0@NEppk=U)0Tc_2Y4IkMWEC|Drj1K~ik? z3Nb=jYq69=(^`+6Esi{bI&hl?#u-&<_0<{XgF^mFf25UX;|wOtb8CZ5*tTG5frUyX z8m;T>NLL&@p1SGLa;>|#HM45?U~P3ZGO(NK5L%J&7-s2lJZr_v#;$V#gq! z8_%qxiWB{`dqEM~35Jw=&4c#JW4NLDUin{&ld%e$wy=eh-fxyAPBs!uNZH2>UmK64 zn)Z%b%O@Fb{b&7U@~*6rWO7Dv&8jee8_p{327n@jmaXcqpZf*nW6Zf5Vb;N zvejTv!$-{GCB<<}O=fvC=JGt&}T zYMVcAm_zY$qpTB224*<*lBbqcA+Xp&y3mzkLUeo9Ie)mTlhGj&r`BAa;VdSJvFsbS z^jG&U&hQM3)V2J~#(Y0U=4ea)&B1hC*nt*%*-;u_g2sXXw=6yRRaWDfrHtaIf$66>uxYenywYx)PgIuG}= z{ozv0Dle6~{o0XZAqYouRxae~E6@t}lB|_g8wrcc6*g-*&K1fjzDq$TF+*$R4x$B)hwHrc#}7G1 zK`PgGr_1OKt{}ZxN6O0;A$CJbOMjPAg34|4hxcZLWVH3o5OqIg?P7}r9kp?@Sf^e# z8Kr^EWbKukpj$RbPkISQ3;jH+b#SUrl8Ps7e1%o3C*7$uvLZ=XpHVfM7O38B*hKX- zMJm1Z6xnfWV_HytO|iUYL;sdjB}I7eh5?ZSd`i%IBqd!|S9KWSiS>kx`td{4diN;d z;mi)pCGCyY;H2It$cElCLaS-*)}8*4BW-bm_XmsnG<96M{nhYgdy*q>z{+pC`%6L3c`B2{UvarN?l7H1Ec z9cK_nS+Q>AW{&C^wt?R6U6h#vhcPo~nn?6TQ#CV01A{bU#-Q0UxSrVX9w|Q!B*dWs zx3E0Q=EqJ=c2y3BN8W@3!NkfT0l!+lJfQi(o)zeiT&sLzT-+~I2`uzWcI?eW;{D=9 zZdz^=$YHeKJ9Y1-EFCeTP;!=Du5`RvJYI-!KJ7_(_Vx&E8+(l~Ffmg@?Z9cT<)rT3 zUHRE#41ShcZQrz-<(yexSbja3frktUis~;5d5>j|HaC1}J95y7prUqxsD5(H48*O& zgX?hLcc)Mux)}6fgCqyn42fBmwKx>rGv{@kdp;c`SE4$I= zLiZlp?r>LnaF3N-$Xf0j)(McaHmJ(tLNQ&7Csln(aVI~aM_%2=I6A5xUYzOiM_h!* zLVs^u+;xy!GQ`u#25<~Q<$1-f%=to;<^E1E(%(5}#REJ9`q%ucUZ1X1j z!l41G>gr-;5QDi-G;ZF8w`5$&=~ZZd*{bR*i_=k&QQW4n`p=QXO7+jOK1<|s<7Vi- zt~g7SWnffKX)N0Re0;WgCh9TVHyXF(cA#Yx+j{LsT$b>9e`<_QS2q=BHu96kK$j?! zcgTEnebjJ5sK@hoQ@xin)N>l6vFev&xlEt?LF1NhcC;)C+2E|uj+W~#?u`)x^!NMf zr7%WNC6fZn;`GrSvoR!*pOufVLmYXo=Ziz@x2*TB;u!sE_qEj@te#YyJff#ZM69Vd z+oyNVAO2OdVQ4QW;AuG3|54njp`XWHmO<2qhk}N=fT|CH{&B@UUw}I|hQO;!i@OY( zW`q5#@hW0o=iZ-R$Q*d>PL&^lWMJ4%)`^= z{fp@)!qghvZ5tu@lviCg(7S%QR<6Fj*g5#~>{M+WVYlqc>#8;Q(YnJDVT^R6=*UoG zd23d=`o^YW!JN19@x@NvG>N;WK86qb?J~>(9^^C_Hess{!}efN+%<5_&g zVFPEnrwsVjVz|jTQtO3F+ZjRBK=8xo@@>~sw`{Nt%_(f_{5|BIhw)c$f--7@H>igf zTlAdyRKDwL=6kpT>+v!bG7=dIrzEy)_*==tzv`=2G(+@j%t0K@T5!hSOCEkq+-g`tg5mWf!tmOaWc)a0q;&ep zzEE|8uzib=lX>jFy*GSZW!u=hO33BYola$l62D%lU9CMpBOK-oJ&j zQ@Iyay(BJ{hmX8iLSpQzH?HEE?|Qp$$0>$=^fEpXTI|f_*hMbs=-s6)JW$m(QzFx-W$w^4Z;*u}vs)G@`jQ@5;JCyOz& z!m~Tv#vVl-V`mVpqTt%+i`(n_U46S8#rb7)39D$1hD+C~(bARTQyKfi$K0P3=QYRl zHRT#?oe4H|YF+Nlve@AM>Xc&FNIltO_hM^y6S~}1Y+icg`~wf%@8D8Y?%+1{(j_O( zKXAdZ`;}te1D}{3I);v_VIRJ7zf!cvhnUIrLQrR(jS8AmTC#EdrZwCGM@jT! zYwNf*8`c^ab3Lz26D%ow4`JiFtrnUsCFS;F%d1xQugB(*8N>Ej^B%upz((HokoTy|yU!=qhg;xyX0h9sgxDy`Acu;} zR`ERf^x~AdO^A3np)A3#p6tu&ucv_K80ZwLe!XHH+Sj5MU{_2m?zP{S&wZbSej$rPex@maJa6W<#l{T5d7rQkyU{ zOi-nEEIaX(?1h1suX!Dp8T=@8>`^JUX1;#8h5IJGrw*VJt;MFz^8J@mym;}yEjo?E zg)JxV!>!tV`v*8EWcPpCseC16-&5Hptmf-bd&}E7`))dU-yu)z9J=XbDS7GD#5bGE zO;L0QZx6K;W0K5AQCWUV%Dko8Ex)ytT^hZr*jh|lNL??#O2!XfB`QSiF~9Q>iN6b^ z8<7m*5$_ebW}e8D_8w3m!c%EHN+=I+X&FR{-I9z*A;II-Mk4}R8~L!Th%8n>fd~)txwbZWL0iyx zm(R74u*O3WX(JcbQwdV#d8DprN<96CNoB}8K9x27m6etL0ltyO3n`U<*A@3a_+ayv zQ<%-Yl`~fN@-L>pvT=Z4E2pmMUAGFH&Amll@?XDkYwxN`|HjI|>XieP)n~1er!+R4 zX5hw?``C6?PTjn5J#7Ti`kk1{rj=){+qiO7<%~7!)^Yx^8SPpl`!-1ruxiUG5S7*D z6m}52(M&U&rQq}}=-(=m4OCWdT*uGVy%kx?Dtdv7Yyy>&R?{6xJ3z((`7VJ}-Lhd5 z=el(a&?BFhKnNIHCYArFtl7XZGN(`2_o9d zda-EjWUqbzjMrqZen3>5?9~s5@tTbK$t2*BtnO%rO#;!cZk`I%vWL&u#4#9cWzel%Lzl zr^IqRbw)~$xlWq`qHLC*rt*rin@yq&;R1lBW~Y*iP9ZAJo+6K8DnwJW$$lyv%7e8ghOHvs@JsNjQnPl?qoF43pWb*CI3vO0XCg0984hRrv zJCo?s(6Cjz@cO4!`T?_s)K_T0;(QE^w^8?~Z>LT0OY2ofhVUBlCxe=&E zEuigARu~U~w)@Nj0|eUcQJKAq@;F=}bqqcv_wG4}$M~8J zgqOX0_RL|azSsUa3_p8^-perWlN{|S3k&-cY$`%CJKnM zgS?4SsUMt5D!nF5s_=g>IYT`}#e>U|Z%xbK{b1gH6fWd_Z+8eU@mi6Xs6$eDLRu<{ znjX?+%maaTh<5@o(`9#PYDNzLv_msAA$e$5;Dq-L-J!g~Zmj_vMy@5JFDB|RZ!}VQ zyVo5yC2%6Td)QukdocimfCEol3f%Xz_e&tzkcSJhR(-#wU1aA{tYoazsOf~aGMbM7eXJ6kMTCm8k z5j+U$8Mp{kYQ5bh44G8I@ z(qaxn`Y19=X62ARis#ggGxc1L#&(Bd>GRQ8?+gb0=uXMtD4bIr^rP_&0y>I)OdYx? z!!dQ}qTI*Sp^JJSqlp8gV>lNtO^y#86A8Z5TQAB)X2)h71EgceW*q~j`mtHZ%;FxK zbxg%{T*et7oE?{O1`PUf8E3$7c3j38Hsxi>^1zu)nwJ&jWquh=wH+s9oB_hw2^nX= zR6il(3>eZUWSprDIgvNGO{9RJpP07hFzAvuFDnQAL^3Rf))D!?q+MAt@1+9g=pyoe zDNe}CLH`$f?;qxQT=nJ2D?`U*!FV|zFMVCuJQ2hV)5U$K(xm z_lm4zfS|u3<4hlAcRZNoIRgg$6`DFyl!3QrUzPk@;7lyXtBOI-bisLb#u*@_U!8Gg z3(l)E&VV8P>Ws63aQ3fQToWlE=>M8=1}x>2X62y&tIy<B! z49%jZugN$Ag!F4N&VZ@@nv64GNWTWo#JOfLUK{n=$|XbbTGCUBDb|5l>G!z+sP0O? z&$U5V>G!!d1S|bMR|nwa3{5ot;;p3eWy~2((i= z%${o!8g~l$PmD=utjf^D6IqqDCUbced3}tCtWK*GO`ef&IxV`0Jk_#Il2Ja5x4DI$ zj`3>Gy^Zi{PtV48wddYOceUqU$99eP6>VhKWN6aqHM~MBY)}RpYy9TEAWL!|NhhpG zI^E|xO%Z5)9p;_`f!4=}8Iu5O$@yYvvLvs~(4^CAI|51R^jg2R79@DE&U-HapsmZ$ z#Ja2_v4WA5C3zhwLaa4}y55VppiA=l3{4bfy&oCMKwIxerXao9kPuYWZp)IqAwv_9 zZ|IO0>_sH9ByS)!lC{Q?ype=3RwPUE#tcogeq)FEM1_Rc^W8@86BWpV>o$3(4FI%F z8Jf6on>vEHNMPwU@piMdhC_FH5-7^9;q(km+`H2=Ny@I_^qn}SQ!Oc$-R5inNL>kJ z%36@jm2Pu~?frm2+e~r|LnFM@?>8HTqz$?DXIetG57*yn&Ie_n^^+mq&D$9+(0Ht5>~o_+2*VkNc-WokS@p2I%sW6)*3lL+!h|y_GO@LnQVcALeg1w zTQiY#g0?k7BU7c@+8UILM3ru9CXxwdcSa@>(o_NkCMSS)MkbO8pq-J4q*QiiCbkhQ zB*Zm=ET{woku%9CVoVYcc4tnv%`0UPIdh&l^92%pxwA5sCqm?`jL1Yfa8{c|oRebL3>>$u1RJ0dh#S0lapxe^%>eE&|dEi zP$8KF+Uuv;=CY7$bV+yno@RWll|N1Pl4sLv$m&zc6Mu+5K}-qd*@xf8cr>{ zbNrU6kW8hubFvX8gSqMBop<76%Zo*;)8$e5 z9942npBJN%hB_V8^I}bbBMi^ro%qW)`S`mOP=5NXF+2VxI>G5Y!7|)XSKd&Fl5Kz-Plh zU>GaO1AAeNav0&+pk8PKPtxXc;Z1RLF-xkvf{&q2-xT|VHR7G5ZzkTZcvai%`8JPz8clK%)vs-Ae*hKzO~xQ*9zY z!rS8La--TLHQyHdbU(6tOD~Ikf+ipe0WAj;AiJQ;;&zEErA-klbo%z#r;`IRPz5c8 z)O~xbS?dz=7Zk3Dqj&j^NXDouVxK^wRhoQf?9)B~F zO{8h7aByXi0T40(G#LPDsh2Dus8$QRExjZ5X_6@g^^Vw*0#ca-cX#G0(F!VHKo#qCXO1da-S3RMWSfQRo%wj|letg|>c?X|DMKaw-6z75-H}LEcff!u7U2_dYfu=;>i&ta zWOqn5yt{G=xKVm8x;Dp}p{i8+b4R>|sqZ;T^ng%i+AKwIrQ5=ruY zZ|rmF0Yd5C7}wWVhSI$;&YV#q$@_i5z;%evET9=UKv3@st34p7_t|Qnq@Cr$r{m~z z-Y5vWpOz^nR6E5e+#i$&h^_&eKmnOXB%M$$ zd?Ah=@Ld!6eIagzhqKL(o(tbl{oskXGlinD?!#JVd$$dF$ZZ92w4M~tO2R} z)tIP*FNNw?V;@N?i3}bLvIe3rfE~WBF!W%|xvrrKLk|X7mqgYN#heFOp~xE0WKAKI z9*W!MgiiFuO?mow++yZ)k__Wdf;9kwDxg^dKZGz~OTna&bGVU-> z4iMBQrBz8c*CpxZo{E#-#FniTnA%U}{h{)&HPAM1u6i0vc z3=w2W{}i`KgeOg>2t&^VO9BK{KpQqd>OK=J2_RIT36^AvF!bjTZvdh%fHs`~wY1$b zpaO#W=eT0=hAA@Q&*rVlhy#XJh48a^t1{xx=B)xf7jw)g<1$4JKA@QcKqx&IbD_+a zLg~4<+g5E#6_?v!)^$esFCjKGMGXsJpd$@LDFHS-c|Hzz*g1;pS3+Ta5s>o+HUeF6ma%?Y=oMXLa5 z0T?rCNDPkEbL;x;WXz0+7>0NN)*40|XO*HsFAu zzJ+a;M-{QWB@E3pfi6k>J|Boy0RyTC>5_!-u<_^mPZMUz+$J-ZbgJ+mdvSZ&qd$pbZs;)O}krqZ&$~ z{I+D*-F22v69u^}N0pTuFrdoHeOZnwEB9qND*NBJlK|P%DpGbw=7y)h}- z006?xjWz&KmYMaTplm>(0@_3a)Y3_&1%RM_DA`S9kfgK9g`1P;OP*heli!^9AlWQo z=$4=YKnNYs<`p1yZ%KSC5D==ja3-Z)oh4npHA%kcy9!M0TNC^QSpsJXzqgS^R&@;s zegUoLfLdBz7)qghTXM)@9u<}OXpSn=4A6pGv(&_VH0cSbVpKkwqXK;_N#%w|6Fi8C z0kkt3KwAA+vd2q9sa8Jz1m7wq=Jq6A>I-F&2DDXqmU?BkC%clyMG1?Exjk7dmsq8* zi0uwiQTsx%A%Nx@Qb?L@&UY=ozq1~PA zYYx?{gc#aA3AgpEHxfeww7nRGpx%>AHyaKJ>OIMxaz-eFLk#WSd~hU&1{elMVrcg! zv-k6aC5CoyJ~+hC?n}5>Y^`>H3K&|I7}|Zw^xZ=#sP`p&6Utg8hW5!^!kwT31_?`0 z>XW&IC5HCNT*AcAK9!`e@pL6<2DonkLH$%R@09@r^;5~e{=09LpxLLBlrO`nmP*hJ zFvvy1GoMc89urDI{d98d@g8+zIlVteod_!6ZoaMrE$&Zt52ze417@h@a{8Gh?eldx zViqtAvxFBulkC4Ll!E%1UM_q&iN5O1u$(!5 zIq^}T*O6?pG3j5CjO-uOxa9#KbEX9!jFGd#V*`K9sb`HQ6++ z2-U9zsz6W$G^&8q{aT<32-U9%)n)M7Q!acfiN2e}(+}OZQsX-woAzGUyR~;+Wu<*p zt+MRMihKz~o@0}5g4o*wt12hUcS!tuNPJmCmd+lU`Bu_mld(rw`gULm2$leiB|z$a zJFo-@OW(GRrfH8XC=Vx`(~IPRKm`n_vY5Z>UV5Ukfhae;gKZzffsL;nvWzcc8Xt>zC0TG0t8h+qY6mfM?+r#q55d( zOI4cue&UZmfItNdsM443Cq)Y$Pzvhz6FvSw)5?V(Ceh=bp`EGu!=%N!xwA0zqreal zQ~`}DAa#Ef7y^Xqj{-wG3qwCn{GkdEsDJ@g82WMI4^=1y^~Z@GsxaZog`XzT&pkuC zQ1hori`>CT(_Mt2Cjvu2Pz5xqfYf~=Fa!wICjvve2tz*$TOT0i8K6Y~0JYS|5&=Q| zneBCwbk}m>mr3-LXJ}Vy{xZynU4@}114BSC1ZWHaQuoQg5Fk{a3=Him4E-uF1O!8X z#t@*EdWHZ&{Z(LSw{qd>B>JsqXg6v;9VY&6!q9I5LqISDXbb^T_cwtdK&bx47)sLJ z2?Zt5A3fFGsrkFm&E19S?*mmJr~(>QK`X&ZXvaNsGnN<_gun1gbz#1vILF)cs4K3JBG|gl^82ZayFUULd*&7*M5~ z&j-JkQc#~4zgPM)SB%MD!;S(7RKS2Lg#ViOV+~3{{cDI4%oXTwLAXGm0tQs^PX88! zOQ}}&!rh}>*yf_`&Z0MaP;;B}(|!*T!@15c9zciz(5M1Z_gv=}4?w7%>-;T(J%roy zoZkTg!4P0T6)~LWy!%fnsOLE^hCM_K=Q|&p00I>-pbFvhoj;nS6x8#b1|yPm&vM}c z7hUB0vL`h!aDGVll)hZ({0L0Hp4PZmMmN0HJ!JV~4scJJh6Hc$16X?&9jy zfsLy+9#Y}$mI~i$?B(glft7349ihunlQ8xs*QAFXT)CyWH#>_n2}B{FZmI;Ni*I%o zSOTQ*V&`wg02!zTKnkgQvC~^Ia?Ka$EzY810#O%GgRcT=sU1Q9(u+%6qMJa0s0*l} z83Cz#iR+MWmR#r&*6vjvYq=A{E7qbnTCxs0?c7vANf_l-5uBFJ~SCOr;sa?;$py}AlSA)qRjfN=e8 z)6W!;!mFGG^8_+b<2nLT_bR8+JE;rw9%n1LK-2})ZV0HQc5?xcUR>=gG${~u0X3j3 zAa$?itD!p5PDc6~XE*LBl#vEh$5=q>UgIp}4ai8p*Cl(1XCx4H0e1}`b>Hjw){|%u zUuEP1Qcg%l8ZhuHBYmy&uEyTw!gVhCfFHfRsd=4iksGaPy0?h)`Y>ETZ~$ntACS7& z2b%*(zppo&!xoMJiHknusqRC~8(fQRn)VT@HwLOePz5xqfYiM)Pz8kQjY3s|HT!b& z$3^nJPM7XWy^lCO7EjW7<-*M_y3IE?kD{Ahi|xJVNprXO0Z4&p4$ztdr0y+zF;4wW zK$^S7PcAA`c&ne_0)Yw`P(@>Ibv_9!kvrW)GoOI) zac8i?`yy#ut~0aEvFzLBqj0fg$^UT>*P;XT1( z1Az({P(?8JIPXYO3hF)1JCX;K3!ik+{hpx%sQF3fqj(1hL!Sx^0YMeer~*>=Q-L8s zsD3Iibbv7Q=`d12paKR|Vd&Fgq$maT(=t+GXVP-vvo8APSawFH=4V}#d{Hswd*N{U zxiH6pC(wrI5N`4l5O3P-H`!*Oe6oFw~V52U zU^5_fzv}GJfKozL59SO?Yzi>Qp_RH1=5io5^-wMc2}S{ix<%?fz7ry7B@B2m;Q1g4vUtL`wjXVnQYtW)qOQPq=OiumRG~Cmc@<7%GK7b3X96P(=i2 z&Jm!NPMhNii->;a_Ovy0p_qc7J0J1}qE$d!0suk%x#KYv<4G*V&z(POUMSEnoR8%K zfeL5~ARwr}aQ=o9pjP&`NEeD3`K9w`UqGM&+Q9}OsK0b2JK6&T^_PB@A{ea7lg`Kd zfItPbB@7VMC*8!*bve?0(i{|#Fm-?BeDn>-Ks(O@1oc<0%TyZ>)L)ru3o16}seEuG zcn%l_M@-OD`QS+K{Hc6!FhRd|J~X;ezLOvzRX|%90jc}vurLBb_0O^}3PXp8 z7Cjpn0sWO zsG^>KaXvIbsaE#;kVDIbzq#liUJQp)^KZeB9V%k@d!PygRY0Q(NZr2&s(?`ad)VC^ zRxX^IM%#IRNqo7(sCjPc&(#hSs^_JCV+{mVK%)vs-Sbkvu?B?ddE63I;T|S3I6w6p zYambo%{&5XsSh*(f_i@HH`a%VCW^~!kg3REuNv5Q1i_xH`qmoULp)#9H;_8 z70{>xQupFO6%eWy2dYPu3vW%Mw|S~ZQ1h*+&Z0CuLa1Jv`r98ss28Bg9gw=0rrvV} zgzBZK?zob4QMvH;G`hl5T|~{dr+xx15~`P{{^9}5vQS-{wuf8>5=v}G59|J@; z0j-;W)O{CU%~2x{2-SC`{(ekN7;mERMl2c=rsPmh|k zdUei(+yns(sIt;ro%-AK)CKkGyjAL66I8CID-ECxBOs{P1eF5>^_rk^i@9NtM%VkX zSWM0LrT%M9i)Ad{ANm4BBY?&bAa&m#`T_{m_lK#zSY&ivP!1p@2x!YBpq5TCJAMp$=q2>o=4#*vZC3*)TO+K8OTLMh+4XJkzmk7T%vV~Xe0HRSqyR`yH z-5b*xb3!SN-k9!bzQq#Z_d|q0eWCCRXe%FuS~|^q3qVjml&Wu$q(`z1Pos}|evhQ) z&8ffab)+zKOHgJYr~;Y*0jYaSP-Z}=-V&7gNMY#Kpv*wX9x$K^L$?NHrWDj$gEAi} z%6wbuzYPh5S^=6e148LGzVD*Ma%x^vDsCq74iM6XTscm9oD8}qk78UG1Dl~jJ`XOsNo zLwC;E{Hvb=oGn14pPp_13e~d}!_&!d`K4KjSaW9!)rh|-M9zuN7Mf?D4J<_Cvy+_@ z*SleBryY9oQ2nNr8`hi>HOYqs%Q71|cPq+X;6JwM@+13c0PkGLAO3GP+7 z{BM6-@T{6$bHqRVm7n~ze-nQ3pG8(BAVn!3@bHHa;vXa`Kap$99;nE5-B|xCxHxvM zErzAkwHIB~!tRGpcovLJP0__5bpWNX4s%IoXH<;%jsJ-Bva8~YP`qy&$Mk?7;eTG} ziy;`da7}n(3@T6(O;m(JREU}rzIoRoA5WT;v) zQ7Vm#wB&#O6GTKRLX}_npT;TVpIEB!VbK+|>SR9VSrPkCt2h3q01N+{|C11l{5K_@ zIwcmx*Z=sQ@uv70{xrEbnpl|T&WUT=w%s4KofFj_{hj@MTz)pcqPbvCBeL%3bB@tH z2`{DFulFYPc7{W=eC|h=xZ(;+b<6{?E^b?~f^_wpNcGp>ODN;Fv|EeY{uIyM`z6;U ze~kYg|1JJ}{Ep;`hNS-TU12?nCaw?j!CdcdNV2-R|ym_qzMsU)`tN z{q8evJMpHky05!$x*xd5+)v$)+!O9+?w9VJ?kV?M_fhvd_j~sTcdh%h`?Gt_ZNqQA zjQe$WBp;4%ia(f~o7@wBH~voijrg1K*W-`HSMe2-Z^z$??~gwheG)^y&*NXje~Py! z7bF)Z7bX8Qc~kPf z)qHi}xAE`dPbKe9E_D~<(OjFpH+|mS=)T}S>#iY2^kw&MB067m-*<0kBX~6dieI^3 zxHsW?Ux^QWLHf@03f#dr<4wLT{hxTMZ%r@3*?(jD7S12O=bm<7ao=_Cch9=-xF_8Q z+|S*k?q>H#_pp21ea?N${oS3L{=>c3-R-V$KXJcyceu~H2i@P?1Mb`IgYNC_I(L`5 z!9C+1aer~eTQ8h=bNYjcF2Sg|b81mf%-{-nt#w`P z%TZsgu(cNV^$_n`H2y!8i1XdfsZq>qZz?pmv=-aiJ31$HmAWRDCryr&Rwb&n_ff4H zbwqH%TeWSKh!5kHZ4UwO_ipRIpizW zQng#V)VOLK*xluY^gphoeU+%MIgZP}w<)KE4x?i(OeREyZu;MsO zfAW8<<+!cpY;j8Z+P=k?o(4HKbGSF@#DKes8$s=vk>c8jHn&sDPxUUf`?0MC;+Vvh zcx2?pWh^F+zv@NINxJg}D%wD*4W!aQT1iK4fPrsNW}NSm6W&V+@2HXD`TMw%^ohse z`r4jhll6a(Ol^peX682fK{J`|XxH&Hwd+w|+Z!jTqg)--_qndraH^O#&2`-kYoW8K z1%K7gn3J5CkKiMb4q=mauSs-cRG+h{VVUX1YjVmzGs-)zb?{x_Z?m*j`KMtLBo{8yt~P|5|NTu_&6guOzcVHte4@VC`k*cRtaTokL|TeXQ+ zX`;2#Ix_Pte+|lv^u=|gFE`RHO1eczw^Uk2Ce4!BpbXB7{Ph;=%DN`5wIJQYh`we5ar0kZ2-BP7AGP|YG%JBRsV*$K2sg^5cyo(zXoe7g_bJq2h zE)ZX^rwcC2{GBYE*6y$hQf<9WQs5{az9{X%4lVfrVPQ|l~lVc-N@yBlgqSP zI?z|TPz5&~F1z_V!#G_u`Vd2IhUfI@I!^!FIJM=jTR5eykww-$S{bCfMb~EXcPC@y zn1+n(Qtqov?W;`bt4!~!%;>9h_f>Z4tIX`H%Z|nhRjPfJX_ZNRm7NLt4fItieFVJPp5w&y9AT~!A1$0=ccWStemjm@w6->9Ly!8{ zZV_UOsF&0R)O?BRQe_m!J(7Wx@TYiSN$pHQ0YN_CZ$Upyl}@wJ5ie$>5s;l`C6O?u zRU6ISQCF?dS8d?SX_GCU4;_xArctTtj&DTpA)~%ZEU_nvA`TnXz8=0uSeqk4b+vQ) zs;+}EPTMXO&mX=9iM}NheY&-0i0DNSW67O{(!RYTm>95lW&@5&Lpi$pg>pn&Ia8wy zq7O*xFpgW+8j5`(tp68hcf{R025l#5kDFsVo-Jza>G9Ont%VMY3|#G5!)Y9m_JM%y zw$wDPt09IkN21!LhSOO4b(Y|lm8var#?UhI8G{B{nwfp-oXZxfQSDZldFtyms?{vh zJad`nx;YJLwt1TK8rCWb&CHRNlaodqbSTX^$s){%->E)eW74kXrd`&e_DcKkIVlGp z?G4M|qt1^#E^pysYvILlV{7lRF)oQA9M!)Z?9A3~G@P>FjOyb})Rpn3*!!J#7*4(4 zS(ZW0OtbjPUG_sa(gTT7#C9u|ud5qc%Cz+j>}kcbW>7{=joS#%(2ST;yNvY@#jpj3J; zVWp84F0vnvOF%Fd69i)67)63wfymp>xm(vD1T-Bo)Q`eG)|o;)u>TVLVTl^g^1`_ zk<;uT)8eHTSkh*ZOnVJSK_o^7!k#9F$YdQZpi3hKZm1>atUZi|_#aL9AN3Qj?M+-# z8Pe^sxC_1TI+#t37g-0x1c^uUG=r2_0#t)UK!X>89UZR_u0^!s8LPj;uZ)4r4EXA1 zAAgA`r&OraRp|B=+Nto?P{Crzu~OJYCO#A%kEn5})@b|nNIqEZkarJv$b)yS-BvfN zevbi9Big-2^Ch@!!Pi&G#-i~J#(}c2yteM44(E9zbvAc_+lO^lr7xCuR7SL44LnDt z!C*K1&Izsw(>in)L~qtIFdNLg>i#Dd2@n0FGq1DA1yQ?>(c03c0}(DZjN9cMWh*7; zeBL6ZY8q{6YwSjPm(#95G_I;1ZSHZl=y6rESnlSLEw{OwY-bn?M#6V?p6Wf81zvX~ zu3pib80raBM;+t1Jv-FY4M?V+FRtVpse5~2@TjO^$i-oI8+QCKz%Pa-YOe-%d%`xa zRQ&o~~LHfCpReq9{Tu-5DCXrrpbf;NtEHe36KsfdWvkB5Yoh!orBEqp%u zd5^ba500lU(}UO&l>PZ(Yu%Eq8(=Y@E!y^+F%76J5RjTTW<|OEq*c}AYaiw8iKcXq ztVL$xJEW&_q%+cHL~gv$UPUdm)g0~kM-Q*s`>G92u+U8lND_)9Cdr1at&vq2+icLG z#Ox0q)(DHBI1Dwn7$VM$SpjthPygIY}OA9E&<}MQm-gon(LQ+ zMkl$0aX&>%@(OGENTHF4J6vL|<7(QdZ0Q}D=zt_0S%#xtk|BUYZlPeLY`NJXaVo*E zf8z$6s=sjOhwMdY0$-WAH^}_ z5>=C0yAQ7gRSiE`N#DS#U6b%d3-%-1y=Dh@iE68lr8izAEfmCcNU_5ar)s5lsJ$6g zvvso}C!KnQHGENFX$QAhN1Y7p_%LKw2x0KD&6lxhlWII%+L<3l7gL$Xto&|GZTBd) z*dn_A8wib~-rBu!1a;{L2bX$x_?^CAshIFA_~<1wu6|imU>uC8+;yq=|DYX0$RQ5W zm^JU?Xz*qfdnjV%2I>fLaPjj+vsL+IxqPwW{#`j?Q;{9sOv(6)yT~#v%SI({K;tRZ ze90T#ix_qN(zQ(p%Z=kk!S@i-)MJ;nD|zm0$k{^e1x)-qE1|HkcxqXV%B9{Giys@W0OXa`s0R2bUU zX(%@u8_gCUy75z@aU6}oV_F}_K1aTi;SY+N>o<(Djg$Mm{*J7iSqjA8qUdVP49z(9 z1%55EnxoxD)w{ICzUr74YUkvZ$U?c}REG$bM|5AgD6FbtN4w@vbF^EK8}GwxxW9@s z9=-7bYFt&feVLsd%N*JOk6?{=R0d%xo1J}d__rsv#TKiGYIZ-GZ7|yp{&0(jOO98G zTQH6T*b#0+zbGq}Y~TIV9N{+fjvuUflUl?HQY~C&V@if&AiH zEJrq6n@WW3aAKpovs}3~dif&U`AW>+7``JN#edXAJC;aUgD4jlvIYma-WV_M~); zR0j$N8d=@8UpL21=TLH{^de#dp{`c#%+sp`=TH-x;Eu)V(rC?{zO;k&T5GUc^h6Xg zcx&kbTwnXU`?p10H>@~%K;4?7KQ&rikX$K6G6kgM?9&(UI)6-K@n-qWNe!+jYD?Ra zWl)Gq*PRQ)8Y4h3emrDbqwF<_W;IoKBCj2Xs%ZhKRm3(36PHR$wX=oUt!L1X!nS#q z#@Rh3bV7JZhA{$5EQ0XjV-wnW?9H6ydZ8+fDx@7f3Q2GW@^k_)CJES2YsS8-XWwP) zJI}t{s(=B{z8jnUI$>ozB8*(-OzBL_@^#lryuf%ip}a@MkIkWT_Iob zMMJi+Gi3X{4mn6n8m)zawqKQm>=a=0HesNe9xwXfr70#7s3%Sq$!E$|wW&6j;jcaW z8>;t2ZV@u24A;M+QzX&JLBR4@k_F4}Tvd`=4@P3!Z>5poe>hZA&%o;HN#AP!n63XD zJ7Ym|m&yf2QpRfMEHFk@6(c{Ebhh*nIaU&0O(KdWL2Ei}B%%)Ajen8{y`OvL>9vcb z3x3*(R8)J>S&_KgI#7*Fcj>s^pP@g9gh{0JDo(D;I2rqvimE_+1;Zqh1bjYhYg-T@ zQMqhtqP2^jYFm(UZawr;Ni02N!D(x2`&Nq@EE|isHXHr5k=Yhk)il9{U+YTOdP`#$ zBZnHRImyKyC0v6XUgOHFhFzkouffpSvO*{tt%&?#k=Zxlsb(iXgK!Y!Nm|+B&2^RS z;HSQ_^=qAVE2`PIvxZiv+1JA4V`#rN2rkxmu2>EG1GB_)W%OA1iN`oe`js1aiGZs; z>u~MjtTg&li95>{h_t66%H{Tsawp&S$jtx5c2y$37?j%OOW}E`GMd(}8re$V7mLt| zY?YY+wz{o+DY*eLYI#zf{Hp5?2zG-rUz!k8Xgx>hs6+6_S=m}P7{M=ROFP;Fc1K(L zI8j7K%3(*bUBA`s+b9yJ8dXGrGg*jRGXgE`@+I!js}=%QqY}`9jBUgpq{PIm9|qeP z=>6|Z@_7}|=IDw?g=T=g1n4N}MTwk|;e*?_VKB31WkaKGMMo3e$!RvV%cduE)eZ!5 zcu@|RuJktOT5_wUZu_oaSzMk)V+Yv;GdTb&UI z`!PQmPplX2<9dAKOI}k~GACJWckUy9L0swt)@0~lhL2=M%I|glSf-%$wf(81AH{=Y zkcJYh`8yK6j%_>ZD+SS2V}8@PU+Mi~N>!1v$r@UvjvqpP)6?QQtCqQmb+x@3QG1@S zSGvxdY11fK`16K>Xq1Z~JgO@uULay=88o2(K6F66-{XB(RY@Th##GzWi$3zHNZb#Q z_J8nT-zq_H5JY;c!x(NvaglIxH2ku)o`kqyk_&-(iWCSxUKA%h|9S#VdH14T!(Lk)~)b!msJ;)zBX zm=PSxskw1svI$=*)OxL1|k?Pnwpf<7AWJYctL2HKGX>< zLvG5DQv%?Me2|(h2n(x+g;^1hwTezB3GT@bh68-w~nN zRNwJTy#jftcmea9ip6h&f~;lHr6}uwT18rvyGb}o0UI@Wz2#xVr95b7vZK$C!N4D#ALwU`#CSFqHCMk~^p0hK( z*cR0ucWbNZF8aG%WH=*fYbZG1zCuqn_r3Xb*zIuI0TiZ8`k!; zKw`V=6D4is*Oos1?E@ocU??j_pwc39jVn~__i7I)KhrXPZ~+GKBRUT!Y*qs= z3*BnZWYgk*@ZW@23ZHx-p!xi2`$D&=(!RLxZ?M>OLlyVogpQai;cYbBR@*uYmYxhgN1vJO+?EdBVn-D7yU#?z|e1p&VomsOWQE}5}V1!rtu+*+a zdqW*xOwiIm`Su?K7}-6S3M3MHF8KS(xI_5W5n zI$QnH*wV^wWaJ*-mG@Y$nJh>yQXLRQ8$1--`CJB}cH`aPlx`6|gskpSCCmbIw<>e6 zDr=f!y%Q}h-6o&1nu`v|5aFFCixjaION2x}l~ET(c_60DH>ET`D%kR+%Q|8e+MP>P z8R#~eph!4VtIhxe%HCL|??z>w~Co3*vj0TEZCrQl&PpbklY!OqUMQ9g$A` z$AIWR|H>Rcz}E!~66DuJ6kLXqidhxuj!lo-rG3$ES1HyrWDbS)PD4^E@L__!L$F`4 zpf#!`6H4SO6R{9v&W^*&8?_Gd)<%%GdJf7_nyJ78A}$oY@?UA_JDGLz4KBg{xr(cUj$D1nL$85^F zDISeiE9IDO>e+;LQ$v~fKMRsg*2`{FV5AIV*|pqa(a-8 zTNE>lpHws{kjN!JS)X;k1GX6M3|(sDYs0&+p@9l!AEOYar`|NV_G7|LDL5L)eefS0 zh0bDF{vwt?8=8`Z`_g=C6*j3b5@LicQrB~gb!wX~X)70|e!Y%eT~2~G z6g>Ab*&*DMXeUgzc8*#vbr(2JO7BrswV9}#$#n5@;l$OGRS(;L%B;(!g^rt+wf8Mf zWNDCj!VaQ#zRGk)ZS>6Nm#d7i_7@0SO#a@t|e7gcl{;^T2q zh2_UxpWk_KW`?VEINPJ#_e~aM-Y@s1#1rE-;DV%!AGA@bB)BIum6$i2v|ZN3vAsJR znalL6ve8Iot*`yJcyqN*KbIuZ4i#~q@ikJrjXHR8K|yyZ>0S}nlxpX^RL=-ptth(< zjCUVSD@9rWnYi=?97}naq)E0)70@^}*IiTdKwoVe`y1Wnyp*j3!?stH_|i@(tt+N` zQtW*3WG${Mmu)!%$|pDXpxS#y^iP}e8MEG`=psqn09rawD1Y@O?swXueC}X1y)L56fFo`(tCJro5%;; z?x->HrRU@vxQ#58&0+6dSzAqP0@N#@t}A9PM3MQ*f#V2TUaCPc3E-TdfkSbCY&4mvT6ug!ze_RLc;J4Nco0dlLCJ$)F*M zYJZC5csJ^a!G7o;uRlS&$dAR8-8Mhg@_URnRfv99nnWLbkL33zoD&qg z$s7r43Q7|X9PPBVmY&{ct5db<=owNlgZ6nNiRMIqlJ@K%+fIF(Yb6y9&CAbaGdM|P z^B-?TT5=I1Qq{(w_Dlb{ghKHEmiJOULze!^2vrIC5LkmHJ6o>mh67j>0}Ch4tlXV| z6E02UHzr_E>G6+V(g4)26p1|S+C~8blE{$hrGVo*T>V%liz<4}Dhw!kR;)y;2akuu zqoj9?md#T>D9MRFT5f@7&|>0O5>50qk!jPi@Qh|j&ZiNMR+hsE750oCv`Xyg3`QhI z$W+2nEZC(CBvD${!32^1Y1ic2OB@f&BlTt=`BoI-K~H?&s?FStuy23p-TK+5S7VVh zc6RM%nu-Tb=Qv5EUl=NVu_(mv3PJk1PXZR{qb`G_9}ks&E_hizARmkRC7w^(W&RD~ zv&ll&%?LGICBRhpn>jtIFanx>@t7uqF1HGr^;Q99v!zEKAf;a|m1Y{ZTLp2CtPbKm z@TvT0moD3nuot2$svns|<+_NCnhI4Gh^BFI#ka_)DO@$>T*+6@;fhx3xMFScTru2( zxDv6dN-wZGNuVMpRntm~UXKCvH(X>4v_wp6OP^i+s1!Eaa)Fg+k>cJ3B%-yOxf8XN zSm+Qvmc7dmMU{~42_h4<7Ryf06&4$mDJ(t6^c15a3M(cBwK9d(Ii71Fy$~Bjr=fd- zTov;wt%e{LwH9)N)SBw`>LNZ|vPfJJB}J*-ZPDerZhY{;lSR3zFf~L08I)KD1xF9- zXM=(b$p$5nIjD}NbjJ@0vi2IIuegfaB&K9ERmsOeco_U=kSwsl#-m*wLMSW@7;ebd zZr#S0+L`EE7l{=-=csCdCs5fMqU%fLaeEm43@a#XADmUq3 z6pOxBBDpZ69~e(kuGhl!)FQvKBw8<1o*!wI@0k&YKq3fC44dp&1@R6gjM8v;P+>t! z^=1Nl6Gjk$$_j@%EbEZPtggPyKR$*LB>;$H(G=P?zT6_^pn{!;>CkQ>Hy@xc_B5~W z7NHD$Ec|Mt#YlaBRYY$_OYTBYV?K5a}&Gr)ygC+^$DyOsl?4FR9e}vNSh0izskKZ z<3#K8t4!JjR$D~1AlWX%BskgDT0Dkh%f&y_TE3rvZ}iGJ$vtS zs!r88RdpH)DCjosbJFjoh;Z%q1_3T%+}pdKA|1$;_l7Tzk&!VTe|Sf{)r<@qxrBRf zB84=FiUt%D5{%+cP%&zv4sJBh7Ey^o(;9y!YC42#h*3ifPl-W!pYL32?H_0FQ&kPs z@~bTAbI#uD$6Ra8IoJHP=F*34Kp`BFr~5z_zqtu}Te0dpQeiGVsgPbw;!0q!wsUEX z!mdq8BNZ2`g#e)$bX#mGRtqxM>2vMBnhw#3-;}AZZ9xmBDc5Y#dYh%z?lhg#H)4em z(mvCe*^NqJiuTKgDRRVJs0jV;mwy#~gJDICfZ2Cr>y*P4JKl*VdDR5Fk+Z})Q4*zA zz-c)7m=PSr97?;Fc2xYL>~of&fxr)p7AzJ6^BAPc4tccqhNwhn*v-E$F8;~XSV7mJ zqd3aZuy0JWnDe;6{3QC&tJ2Pe?T>j?d$#>2023HLUtt+5Bj(y>jA;#`09BkQpc0N@ zA>L7evPOYsi2{*qZ7L9E3qp5h9fS{%Qjxy)6=R3tn0rEfyV;Pe=rE9yO{7GRY9`;m z)R^1Z+(f$X`9x~WPD+6FI0Eq-pJ>GuW|W>8YrIeiO?n}ZE!H+gn`1{P4NZejUP@PY zo=mh8l*l9!+6h=kCfZS$QFas;j%&h)Qo1oXw#|G;iIm9=utX-dv6O@l&;}RBv;oM_ z9(ksZ#-xcxUg0SA^!P?`2r8JM%*6YjCLbg8_@6iF6BMf!wx`s+#l5|~TG*cQ_BQwS z1OBZqY)^UnKKJ$`{;e-;PkDQXdwZ9E>kHdc-X3&s@9}SaVSCD3;5GKg*SlT8FZhDK zo;@wz9`bMP^Yr*@)t_JSKkf7M_`d4T!~Umzo*sX@`tyGO(>_m+AFTd-!2h&Q*n0Kn zL;k0IGNONU%jJKr`k(fBdVF2==e7Q)eX`X4=k@wzsjF}8DYJ2td;7L(VSCEkTin~* z{aatyp7QoK_x1z+tuJg(dHX*1_9OnSFKkbFdxv{_mw)RE+f&{ibZ_tRZ+&5V%GA#jjcJ(&r}uPzX)a+( zy!M_>-#S0lX}6+oOj?WBT$NeE2cUG!zebOzltVXm^5CG0+(`L~rAVuIw%NhYL}Xdk zx6|V@d1v7k>Ev(?CZW&hsE&Ef8K^NFpByhLT0?Pfv|-K-q@q6erbR=kl@@2R7qtw< zj2(q-V#-BB*a-uoqGj!?fqeVB4cjJz%(ha6`tN0_EDUeCnH{KQ4cr*ih@AkOC@_YN zyc!vkAoFE^W5F^@-tNxLqk?{Xi811nj%~WPhb)oK<2F_TEtPu9@lKX(s;AOG$lM_J zCbcd)@*)_@4ADs0eNhO0E^ZTN#1_tXAa-V2p^3KWs-I0g$rZNKFg{JpeEV}Tl5}pq zJwJ--)?2rq%O?5I%WW;NS7vv8)4pP}%%ZbeJ1B$vhdmRabnqa!1*GqFM8?gZfyfR) zWJ%~=(OA0}Ex}mJ0sHmoC@Zs&tQZSwm9WflsW?wMa^4_?5Avh%1~Gxc>m>8epfmaE zeL*|GJ(WM5ZuD)LZ>fFT#kVD%klS6YADJ5{F-t2Fk5J#oeG2ulFsd<`a7Aqjcs&y( zGdVfgJ>p=M>2nhKU96bWD`5z0rEOxx;zUzn3IW=KQx@RQ2aA8;^SlcMHDH2Q}DH$<|E)p!sSZhtNXD6Cq&rSw5nX+LQ z6D*cM+{Sv-Dz0F(nFoL|txI|QR|0V{VVYK~?M={lPO%0wTI3BvgABVtXqf!&t&|qk z<{S-+ftFtcJ4S_4F@*?cORuz8W={~U-Xjs@S0)9&YS0I9(j4uv3rAaBDoSl~Apq=W z`au!7fhp?aYW?$p#T4m>>6T{52*p7`=X>?%sGKR+2fmv}no)!@MGIOe-yuG`(1HWW zB2Y@bMMc?YtgJ!IcAi7r>;0gSW3>GQVS*9Iu<*@^93yGw80|90XpeIY3+rJgEI3BH z;282AJC31mj$`PX;}}Yt#g8N97;KjKW<1C6;S8Q*sIju3AdZ2Uup3*Cn7sT1fn%6} zF%8GC=u+bt_BOH5QFZ}Xj>1P9IYzx7Sc31V;23prWJVlgX1&-^MU)jB11;4=je=w3 z0*G_OJB|@17?-^`#(L9Q=NL?D!7;QQGhv=%G(nSdjET@VjKtPdG;oX=ixP!N zif_o6Ww(FWdPOY$7qwlOSOklB_aV00Y%%-TU+S}6is2C>Wc5p7rBLbGoCjIMI8R%r zD00qosB7}DLZ`hvtgU*B@YW$b*xf% zWRTts)5fUAbc!uTwGN}&k`PLVN}jwD0(2v#F;F@?k11q<(%~AMh^SEdw~WH)v!h7q z-*qIZY*bsagSf2hIqM7Ua;zddILe$U%kDFUv@o;D(!#8yh1o6H9YMNs`0R*e_gOEy zOI|2R3NeyGk0**G3KY8JD0C1gRKBqig=%Mi$SAazz4hag?mMNu*mm|n?=Bj+iQexj z@9d$qZ+}&-&6Zl*w4ME*w&-2D?U5?!5e9=TdWYdX>2~4`(mNEIoatomT~&I|DOB`+ z)#pT+{j5%*IlU_%mxOuz=6tB#<DgthYBj-5Z zw72W$wnJIr-e`n(Z1#TpoIe_}`tNVElqj?4=DA~kLzaDQ=BC}`*-@GtYnHg6zONVp{&j?6zcrKIdk2b z^WIM)7GD=6VAg|;x#Gxcu`3;{r_NhsO76UWyj^k1>5^w+jqLcXuwnj!? zpUOsqwnft&WjgC@7#b$OOlNL;u?@!D-#FT)%+c1dt7YT@BeJY;wdwHhy>6S#KJ(de z%F4}aguxYc6iFBAeD;&7Q+ChkbeXyI<}k_44BioFSWdlA>#J6)>>jgw-~}B{wZcIs zr(US@r&gzI#?vt->j~puQAfNY)Oo;0HYo85%a}Uvd4*`+84{Cxr1J)KZdi~w5n8Jo z7C;3TCNLASPx&x`nfy(jm&(7HZVp|N7;6Cp3Nx)n7g}6Xh%RVlAN73jFYI%{jZFkn z4m)^X^vI*$A%qHivCGBjI01cm{)Y(cI+*DW^Mb% zUDlkxjx;8{If@z%)@u}T#&HYcI6)SQogOP{lm6Lb78R5hi?b~0lk?(0&6mZM z!3P!0smPf!B8IThdH zh%tdL_kZi)sx6t8C;!f^-daXF-KIT})?S`~Dt>ZGvD!dMP?+$FFw)r)Tgl~C4y`H1 z({ht7>5!-*i&DaI{ZWDO-7vJ^8)?OHGbr)bia=SqP zaoGgEK4URFog!f1f$?C&7s1|O=w}9o?mhz-(Uxm$eJ5<4EJ=irDh>i!UeX0pR<1~2 zpoFL=Cl}UYXSD}fX4W=oyHN2Jf=dXWQ!8c*wF0e6t_(h!R0=aH)VhR6`%Y2MJ>>N; z+Me{h-g>&bDMVx@c7gY%yT1Tny-slfUwz>H#kMtA&Q;Qv@m!IsnnqLr{sqXjjx-QM zT<40L<3CNyK8)-o)H<_PS=nV-wxLaESNT?V&Hp=oVrz}-_^ zYdb@0W1#_(=tFDu&;S;pIq-MN)ILj(7DNt~9#J8P6`~wuHd$i5%pv6WG6$&Ta3$nN z(~>!KGR)HII(*{v!5X=I8+U`|#j|{LlCqGd_{cL3ezGclAq=vr%NGlm8QpTouBz?4 zNH<8F=D1SAsbpsT1$MF@F%sn9C)pL42Rbdn4*9hhk_+aMiRFBFJHy-MpkNell>~Gr z0IvdM@2|UwOGEwB_9T}aU~&v5ms{=3Okic47g14M%nMCWM2c=sfFM`%79=y_Z!Z)8aj3b*aI|V~8$-@a; zs)Cz#bc;h;0XKCb%j4#0V7$tQ16xiW)-xv$`=eV&Di2RSx>Z9Z&qp*zN^x&#g|h$Z z(qwebvm8bYf5d6|6*84+$d&{%+3oiLW@`n#XA~VA%_x&ti`7C7-ABF$rkPY2oV;RY zRcu5jt!(wmmmRo#zb2NX7?lUoH&|V2B+B_=L}2KWEORd6?;A*N(p`` zq8ZJm9S%3r->mZe4NZHZtHYMlIm*7nXOzw;)tHd7Pm&XUy>x%}YviWD7`gjgZXbXV z&1M(v&u+Qm9CD-3@@)1da`sJOA1HN#(p&CCpwZFwz|)CZtyT<4%qS0oA^&-y_1Luz zZP|-mdr9tOJ^Yq>Q2S(30OypP3=FoUg&*Zrhc}6)$G>U$H|GPPRCA4;m*kex0X+ut zA9V=RTC+O31hA;18#hW1NOS0#u<6dO|DXiIXNOFtF3kk}w){dWk)kDy6Hw|G*@Z#S zgrztEo*rCD=fQTEn#sBn_*U2c4n0ieDj_Y*RZNAKQ304e{QtP zRuqqVwgrTe+%Bv0`D~F-nxqOP-KdmQq0o(7QU&xpw)-jD?tM0eso*F^$C+@x!m=q; zIzZ9b%BCP~tH|x(&PH!2w*vz8n0*Pot8iNI1$fEXQTNgmB^{FGT)a^ zLZG+eWcr(ycj=HhGD3p>`j^C1j?2DhpTUbejogmzfWgaExm`koWbAd$O*dz3*m8zg zV1^N09-Sf6mDyIEs7;FPLe$okRzYN&Kznt`eq+H|qv0pEV z%O4LSw$}J<_lUqwXfT~_nlogJzP9MAepMV+)Un-U5=MiFO(9v2Q?Ma>+mX=MS3e{A z+POtv?9k1uEowr9EoVOS6&yHQd3ADmL;F{5BGUf#j}}OnX4e^=9TjK(8{=x!Qm&3= z_9Q-!{ZI=N4;Ox$`<= zVWCbvzIjUoS8%5+0S&U^mWZ$=B2>&vB^9?KBK*Kq+~ba>h_JNEf$mX&TT z7Pe&tf70=;BeBwb|4+rUo^dp-bie&{bM0>B%jb;RR=#}anw^|4|8*{Fb*DH;vVq(d zPdDq4j#4(AY~c)Z;fv*T%ckat=Wxre^$)iY=-T9++0+B`Phwxu#tyYq>_`gGIAl8O zNevrP7gKX3J>{Ycqv(f-$gwjhvPy;4>Ss_aUENG6C?j>PErr(H`It&N+R)m!492;m zA*}zkGbwK|1&$)rLo&Qcd5bALSuM+tk}16R(~?sEmqw=Gjb9bxvPeJF`GVD5ED>w2 zjVrf(#ULH(-0&&&SJvP+O&j0RHn+6Rr&CNw<%q(nX`9=#H`-k=N5yA-|Gb@F?1gL_ zlBsjQwH($v;SHQ$Or6)d`QIRA>!`M_S%XiwL)rL(mvPGA%-&Tw$=G_&g>!N3$emrH zxLgiP#)DixF?b4n_|vS*e7#DF!|CyJ6tXLqv(jbgYs$BC?b}Yi?dHiqYS!|aXIbmv zbA+xA@}tpm!y^Ofw3d?TEXSwdo+sOp;*LjCdpV589fB*|3wE!aiA=QbED8orq3Ska zop4NvR-L`0P;ZU4OO<&oOF+kw>6V-`uxw?=*4QNp*u zaGphQP>r}}(P0X0bqD|CCQGwv@0ZE6M~z*&WSGQIE9pFiZa=^>pOp3n|L98V>QSZ> zxVtQ`CYhd?wRA3@PP>thx!`h?&SfLcgAr(~1~o@+wm5^y1X(A#?pg!rru}`Ro&l{c zPBEZ0vjJqyR1IWc70Am&p@n8fD_xCyY7|e>MW1!=bOlNyUvO(n^>g!i)w6Je9 zmrk#Y=AW-DHT26ppaPYgRXpi23;dk1=alb`0E%Qnm$GQQN$1bo4g!p(1*VQLPJ0;D z5eMPa!;q`mG{CtWxP^4C04YfqG!t&7H65BwRd+*)K_X_$=BLkxFK7tT%bvMk8HQL6 zs@W7+av_GdIO^r=ILplWb);n>9L0AE*z-g>s=VmV8dGvFr&T-PIt*#nB~h~T#v*Xe z!t$7N(EVXoCt|Z3{}2IWNE1SNBY=ThkR2m%cfYOzr4wPnl-r%e-2^jD+jDIXk*%4j5&-s_TbgL8N5unq4;&+ zn$j=G`JiKFycNIl_+K2sVDRE>x2u*zzZqj-nXgb|aDlDCF+XBm{efCma689b-=`TF zd|%K5%Axo1E8CIX^L{=3o_~7J+xewrAq2;3k{JAO+U16dE@5y7-Fq$Pln!F|;X4Ev zbCWCA$V=}VKZxFURHC!zEr0f$IR3U0XS$>2v;DIrK-GZoe!a-im%&<5t71=%**Ej=DkD zZ>~5>*?iZZK^VAx>v>qr&J-?HQ683NtJPhi+i+!KXsw=yWz*Eq>l>Xi@lL>4HQ7lC zT4M>yaJBFk#A1am#^un(*rmk?qO0E8CB|`(YtQ6~mur=_Pvyl_^iAhUQ~Rb9S-Pj0 z%y4RbtrRU0puopUdC=nWm844jhkPKhme55t#_g?6f`?1%yj^@UYAC5GB&U!(C0t1g z&*i*bCt=9Su+ko}Wd}PmUnkg^>m5jpb6pgPS(D=SVo*l67jrc_+}$NnO4ho)mOpBjWTL6tQ&z zRlEZ!hDF&rftuwMG4XcspX3QtlPB$@skeCgW2T?A#9<3#8`tI@Gu;vt*=r_Vf4wCr zj`8{@&1tx`1V!YBAoOW?%|hQnG0c$i<(=kp=w%55DP@MO zKhAD?m#|PE@M%F{>LkMAd;EDSKX=Yl&M~Q%Zr&TSv-}#AHEH z2H~w!=ap0Ejwne?sPig+V5iNWHAa+@Qa4TF+)@{})J3SVIcG-QQ2CTl7oEGWv5c|+tN0ZrtC*h%DyE1xwD$YfYFWkHG~(Sl54tw-k|232iSJYF7WESG=4U(Q z6#TfI>3rr9od*qd?zOt*E8jMaM8Db?V!owZBKKJU2nSA-Ur~uSUGhKEF=x^|`5#|5 zS;w4}BXHI^0=IO`!MknK!i!gT1WvLx;d8vaPe6ge!poLo++O7(3ha(hA=FwC zKlQD3k|GUuu5)Y)3@Xt?0+(D`XUH6tX-caWg)=$552yD~D>8r=CcC84~@(PqM%c^h!?R6=UVBE^4t6j|p z5NzO&?P6CaB@wi&!y9UsPZtj-=J_Pdzo=Mlg|~-`T`k_(2P?XZU9UQ(qkGF{`7gg| z_we||j9dmc7Qp4i#jq?cC!>pN4;RpJUt8_|x#!T1Ke0ocV2EtwRyuy@^v1dNO|BhB zp|ECYf)wdG&L#cwEW0U+g%K;*hV{nPHSMj3vMK2aW?`gcSfGOR=~+F z$Bn58J5IP*YdGtevu;R(RTJK>dnM;QtDd(}iFq%W&&|%|CLGYn_u~0;=DM{T>+Pz= z8gxDzI`9>dk@f2(xw8Jkbgrx?WN%1BknJ+kNX@-+8FNtit!>tF~^p4oo>$_ z4FZxHLkT;$cbJ80b*aS;ui=rVqv|ytbDX&|?1AMzZo3dBuno?1U$jdg{o<&lkAY|m zycyglLCjvr)gIBFt~w6J8o_Nu3D` zK(sqN=c7RaluQ+P&i9Pxl#le!hFY9Qq}Dq^EqO{pt*;v2D|<>{^6Us!qq=Hbi>Q-n z<2Se}b@Foj9HcG|NxE;EFCm}5k`=MWj;aOOhF9Y^yfPO7qOaRHj&r(Wjrr^Qqb-maRUPMoDHJ9)X<>TP9DSj)ZSHYsPc_Ci{2 zv}Qw|V6@)4v8D)0%q7O3lxBS-x%yuB>4U2}^jbe;wrR@jR0Dn|SMi(S9=Q`zrW)Ne2#ZSMJ)*K9M>#WbdiIHWCkVBB=E#8!_bY2efbQytc^ zD%+(EciS2LAVuzMuw|T2!`_yGeh_RK%ceEx5cmc5ZNRrv?3+$*?xbK_!WqpUu_>cn z(w+N3cb0IZ)`BKx14I(yo^feo7p!H@Sa#-&cFCIIc0%7Kpbq=VZiElNDfSA>Mem%s zshZ^iWUc@sY3*DD6U;jX${C4Yh6e3z?u=w9om);w?S~2|G8TwExJGIRdhG+ z08++|O1#CYGbU$$YC?NCyO5<0MDJ)h3jp~XkUCjp841dS6-)%v4Ne+cvd5R42z{^5 zU?Vx%g04+Yxp5;x3YSM_zDy!j<6b?xrkUPK9c(^{$cU5GadLA>WVUHW8%e};@;W%1 zOCmVO>I>Wm4)Y&Kq#%#cLT0&uoPwI06{MR%d{AnjC{I@l^KoQ!&@j(m`4GaUS+YaE zmhmk?Oi3GoXyUL+hr}RiU0S}~U}6@4w$lBW!3`_t9;tE%QSsSNWQtQ8^mI0Qwed9> zME*t+$WXRo)Wmbis-id9PM2(FxR54uF@5Nqw^97UoFw&|a1S)r zymi!04`^tIXb+v-pB=o{_ir&MiQ+^U&U5G5>5S{eGBFh;b%rmI7Q5hqa4d>o5s5{w zGrn|e_6qx)Etx$*D%}L@66#|!0gnERFl{+L-vyJ5n-eBV`kkU$Qny0HJt@+yEU?H5 zC?d26N>+r28c)KvoeC~YQf`QGM+%MScQHSuo!MJTcIn(mk(8m0k;Y6x7SHs=a$h$o z*`D%YASMk|uVgmwGd974Vgn&7%gvU&@R`~B%@7=y$ao@AwwrB&Vun5kMZ1!N;*T58 z{jEcJVuX(NEL~xP=>9;Z8XF`G=IdZdSmP*GL5x1uByMtsp>CYOI1e` zfAn<92tuFYpDzj;IX$9u`zfvaQwgq4Qy*O2S?d6AJA1IAk!!lWx}oXz8u`GcR%Zl@ z>6U!Dyn#vXz^Pd=rl7QkE|)a&U6nnw4vtqh)&NIqzEN-xVvxF0}%4*u?FQ+L!M%DT%fdOTZk+YxlN^ms$ptI_McY- z2#%vPLMERc{DTN%D1qGX@9Co-5Ifw)5|BM?0_ri(4ytLBxAOdn`x?~7KvSl{aX&M-Ga z<`Goa9|;2^#-QY}Zen2g{sRM($G^tFF0yr>z`$sJ6B!t-1!iIh%3NLO7}!N?F|adB zwnn?%Z#FG#jhcY9ZH;w9ipgdrV6?37ycKlw;pi9t#7{{2)AI{Qb zgjD3ffv>}!vR?Kbqs%c$1F+!E1zSAQZ|VH}@$Jskba!U9*PokT7;HP{*l006Vd+F! zGaF|0;SU0h3$LlMR=3E3wF-9+l&v_%TD?JJdhj{NQd#A}Iddmw1HIr}OaCOeBWhnm zxns_mTgr~ni@6giGA9#jxSWC$czi*(-1Xvn_e*b4fzrnEdDEehg zE{4t8W!E?^2gP3n&Y;e9Q?Q#4i>Pz9GSamp&uMN*JkHGct>rKLlGxUAq1njbxxq%( z(llyU$tR6`%rM5DmXB5=Df>0FzU>C)`R>_Xj+4Z)e=zTfcd8TA?^}s zBL{2^h(6y4(d6v{mB;3=vRwd1Yw#n3NSB-VEzLL06(V7G?dK3(NU35o|BR}_rEKyq zL^#N|C&>j;vB*X5?jrGRTMd?BHg9`wLfeOZ+p|U;O>NJqZ9HcIZmU7tGkMz_WbjLT zQ8SzUn(w3pc{cB7Y7MDC?oz`xHnM_rEtb?FzO@S|g{OL9Jy8C%0hERHLD4A%1EpIA z1@Cs46>Aqz&>IZx32^tS=@D-I_Xt2};E1;V^2Bb-M`MD+G z`04^$9-Gf;6)ivQrxK$XtJ8!!#|m|bs0ou~HJGF%Z+p?T?SxP`ZUQ3dquSGFeNPa@ z$;pL7qGV4_F5;w3K?f*xgEUG}{B&Y+QT>|ha@On#R-b-z(?)jOhWh=6@7E3oZJ^)d z)UUjkTv#GIhSjHEY#m_=OfVnfY-}-MQU-XHt3Tf}^(Q1?OAzl40T> zI#G1RMpPvWMxH;W6~#%=lD2K@&uyD$=hRU+(>4aiR^6|;Ik#8lFNr!Qp`e#$z#3Bg zxmvtnr=x*f(sBN-=OU{Tlc_hu$n6!)jTKFCtjaY-1>@oCRp^&07>k`)!5|z(&lQ_? zkk8FJuDpJg*}Te(-2mlhXxeed_3LBQ^j%KF#rR?KJ=cMc*hKp2ug`D}wB|UU?x?&7 zHoqE|ikP2KUqca|Eo%W(Kp`>~)O zVm@DeC`RZu$-E_k0^qjFSlqyGn&!eI(G-A~i?Y6}Pa zn3E29e(aPmH2d@jZt=xd=FNl>S1KYY(YXyUkrLnfvrmGe9_C$;}XAZX=z2(7)v0H1sG#3MLO?>^euPW z-rjV2t&+-Aw#sJ4a#vt1BHqy2QM@AUKbh7k34JN39;OhKRS#)4NbshZl$Ar>EgP|Y zn9x6XqaZgW$aI}@1HA_q%FtB=rjc*kg3HGf)%)O1rqV4f#d4wqc;z6};|~lQqiBnB@XW;sg}Z z*1U_YYt8E~oPe^S3jr8;efkRlB`HF3Woc}H#yj0N=Q57^;e|`xG->nBzV~dWxi+P=ydik6|q!!SxJ&3D0e!sbVtvOrgrOe zr8%%MZ>}oks<~R0oLvsO<uS44+c4=IL0%2@B25VB@D_b0uGPI<`NbnDW>ZM>ynn>f_aK;Qny46rwf{cbKhpi z-ZN@ggdSaV`KCx_zA7ugEpiTKCb)A%P6sGS(23%glf)!Q2nYY%Mr@W}O zuwBrY^xr5AN%osgX;_V%)^Hu4N_lt_pPFhiAS&nKLnK7R6-#9H%8(E=#quujahRXT zjoG*P+{P!TfQQHMc{iWOvc5Vzy$Hm&)7o*l86#Z`Du}oZN@9wKc%dc{THZ8fE*CfD zfmS@ad4?7L8`iUavf+5SijQBn~s!D1VkmT4oK*NzQ?!{nD) z3H2(*4JI89e@n#b84_qGRnYV;k<-Q$EhmP{4TxTkBeX`&kAMAh>2LGdJ*JbDBWspI z&EsG;e`dO$!f{GGnr&aY0+aRolcWR!(%mTY^@$NK99QW~!JX<`BbFIQYf`lr4hJdvVnnb)$9)*7&-7jnTNv$IGRYUT4S3kI!zk z&vKRsivsU?snjzLOB-=h-X2L3%k2qYLVNY&>P7tZPl;WbmF#jL#4h$CXHdAv{ko73 zW^k2c9mxfJXR|xkz1dBzXRwunEA^d^eaM~OD6&#th*52P=aXnG(W%7N5@Ty-8{umc zYqP8{HP*(qnzOdYDlS>--nwv7XKmyA%n^x8i%r-)L0nR_Y*vS;&fvx&IwEliNp`~m z9IH^~afMV)8-d%M;KJ~x%m16=fB7NpW8qf!25u!G{f`Xu2ieE%v&`X+4IJ(mqqjW4 zt5Q^lTA#97R+t=9z&9K=AP}R#AJX^LI6<7eY^@r7gI*C_2>np4I7=4!#R#6+m6e4rfwH zE4z4QzO8V`3FpJEeJ>kB<$T!NoMuqHUvcpQUKq?Rf+-@{ACpdJd;Q_<~zy z@zs3r+ZlW+xy~I|TX6f0rqhBVr4qU1z?`SClgQyXf(76z7qxHK0&xB8Wjk_E#$mX> z0frsx!=QuxD4!#CF{amI#90u3j)yf#7=u8u=1YOUf-*4i1PShCRD0^}b zunB1EstO_V4F+Rzeqe&CB%!!(K9-$lT0{4&VXHQL(sV!07Le&C7TYA#%9b4V@7q2J z#bw@>EayApyd}zWN59RYV&ft#`8y^j&SpQbPtQf01ehnvtZdjWRGyW$7@GNjBIYDJ zTm+1ph5c|8wL>z^bl)g;CT_=jknFriAG3_Kwo6CwDp+WA-u4k@ANW<{+;}eK{HcqP1i93j&k_An}*t>l` zNg@eqGC^t7nr4-ipF^>UY_<1l~M#qVPYX zOPw4!P5##yK=ruE{Jf6k>)c)-BAn$p5tWGxvuO2sd&Ql@wnF@{<4j{eAnv^33U%^0 z_a35Gg<}+iIXLpQ6QS!@)duCyDG+KCsuq0`^H2hML(p`JCOb_{BDa>Bq%cE&Sn^94 zx}7#G&m1yIwT9{ zP}|;v%4nX}$q~0gI_o-glYl4JU{io;9*{LLetj24sSU=jZ&~1Me_EnxI_5}~ z;+re^%9E5UzA{OMIyZbub<6j$$hVv}X#%dNLu59o@9j8s&Z_ z&AmB8w@CEqM55PP)b&Y6M53Y2TdZ!`&a`PH`rmThrD;pH=xN)kom5j6Gu=kMUPSiJ z5V36{X}NyymycT4z5n${#f;wXFSK=Q<*{3*(VAHmMjMOv#`SCbt2W5V_-|U#jyXt5cTV zwguQK&K6mQ!@Rxfu5pPkcaLCKF8Y}*)%jhkQyx$59+68WhfUYmhJJ3dI^}I^KEQ_a zrl`{`)p=j7&RnR2%c-a%H&y884y#jk9$7YrV-dT8S5@9p();JPnBwxn)}1?#rb?}S z%vu|iR>6!+i>20H;9ODX2gtBImRpl!E5P(nF z$nRA=zFbG{t5D}bt5eQDn&01jfjPT^H~9X3r&xw%oM9;nMvv z6{1^c`4$%Kd5VvT4s~v}x{2p0s>75h@FRZgG0|UXyr$%7Pn-xz1l3F>@&wrthv{@G zgp=}h!g1>QVF}p0KQ6J>VaTpT*RB&?Cm)7HbZw%8O+5?=Hy%^$pEOk2y>&P(ag`BR z^o-)yY~RBO&(7=YXVdb^PiLPnidr0_#%V`c*zY%kvbJsk)fr%CKr5XR+M=v*Yr&J6 zvY@jdX*z~s0bX3)O`s>|JE1u3O$SZX^hEdRFH6K6WZ(OeK7+WsxkPtQ-;VgqloJ6Q z96#ct!w=23fVyBH>?5Thm!|7JFQo;4hscZhdXjW-D* zkhQZqINlSr1~|fuje^tYifRN0OU}ZWB?qjsZSjhg`5yL~w*48zS4Y4 z(^=ielxwuI)Z~6CX|$ovXD!^S97bxHipsf#UhMSn7lr=Eg1Hq36k2~? z&9*lD@W%)*Tgh)osV~Muy7R;?oC!CiEE{na1RE}66w0qE>3=0gQ=$Vr4_iBmGpDp= zH~3p2nPo6Fb{L9S2^UWORbN7JfjSI%J|8?DCap)H5Rh^2myP5D5M3{m0~9;J85fhwTdX< z%3gMh-1N0x{b)7Cq^UBJwJ~P*)>9z+d(-Y{fo!pafC~kzMWpVou1n9^##}b z;2MGGam5xO$BwUl;vlRvY$u76;h1q4Sq9nM>XSE^O_?x-lf9bQ?M z&r;@`JQyQn%2ZL@JQTHK?t{1Ht=4@$XXS$f4#D2gb$ON;VF-J>+2srDR6R7wmI+9X zlqrZ4_lndGDV^Oy>6}TsVWZ{igpKymDxKvVURz4%;N8M}MQzu2*-3&ryX@?(!i zOU+de&#SrIB%CF7N@?&04bxeNm%%$-XA)YDT+4rF6G6*Sspan*z+nqk%Sb#N4s%sD z%4M66p)+fh}NUq9WB*^T2X z;*i02uy0h_khA2{EWYHd7HTAtcxNSx2CtL|hJKGoYT{*daAW<+GxCf}2GD|DW#Xv@XXB*cG2{@j z8r@P2H-0BgmPWoO9(E;(hmlLPY8ttX%0|RRjg_IGiP7gw-W4N2!+1euD0HjfK!ze+ zE6HYy(pYHh=Jh1Q88`k`kTGfXYVHu47H>oYIB>Grv*QCSs$+S*`kH(*!4*qhF?tXxItF6?x*SO=1*?D-v;a zhOCY!uFl!D$t};xl`$hJvnd}ZZbL9OHR><|jisIH8>CH`1;Z*Kl8r^@zS54yqPJzl zp{;n1#D~KvEo+Qe(@1}v_sGX!ooG>}t$4DVR;Tk}tLYN%OuJ!s(rFEQ3NgQ9!)~Wj z8n#LoIHjdblfiGyUP1G74iz(H5m`YZM5M=)GlbV zov->BgHwK1TXk}?iZ(&3$+RiBv%_Yf5Em7nMZ&o-6(e~Q`mhH&((pHJz7%#yUH+e4{jZu<{u_7lYgHihez?_0wN;@^;%aVIBU3-zb z7qT5!Yb?7LN)l9kvCF-ft-UzYy%6Uq*I15syJ7VK*Ro(~p9s5D%oH!j=erUdexd~V zvc^kfZzv6(kj>?o92sM);POSehDgJuQ?KJiKHyrJVkuSN_Gs8jr?TaX<^Il;St%a8 z*6ll-O|y3wu1M&!&?+hz4iFqlxz`yAmznpCN1b_#&rLaU_cRQXb-NKvE64aTwmcQs z@{}+hMxL|f={j4Eks2}l>YwhY<9Fo51V7u65ro(TVrLlEJUEWXH>3ch<+!hHzQiEk z*^Z|w{CrIEewo|DM5U!YEXkt!J}hCJ|FogA7J#Q^?65^OZD8!?u3Uel<@R;8r3Fcb z&=bU8D&O89&O2*@JKQ#MC9Q0OjE)@0{wN`*S(G)^z`?R(m4G&N-Bf)dz_sn=kqheS%)JC#5shqVK73P=edIdLW>;vAcJPn?i%AkMbmj*K`}x+*gbe%BaO zY1-a#qV1yj>GkGabX=xK5yMI2WD}v{_@5?-82kCzS(*2AJz9@^$R(9R>ck|3*M(ln zwLbV=ZPaF*Q+v1+oGg?#;|Nn|Ke@5B-pMv6?Q-x+-w|@Jb^I#-8=2m999KAiQ(rd1 z0h|i;SUG_5b>xE<^PD_frCxB1&X^U9&atm)o}%Z|jGee0VYcMH%?Y^cu7&hknk#8# z8`ugC#%b}5X>_?pzT;WpA={PJsTk1<`%T`mH9S>l161&HyFz%GG1Ig zOFz^(Qn(DnZ5AS4Un@`Q~Ve9N0ZO} z@;L#213RSO)!ag_y+f`m;8%7?Q$RtXe2<*O--pO9Q&JqJf{AF#3yw#y1)|JJaMTuP zdTKNO0jEuhz=KE_7f4@f=uG2=Vtv+->5@7FY7mNPO2=`bE5;y8r8t8cO0lya zZaM1$1}z%9(S-LFPn~(;mMcp31#Ax7?|`3)<}uzS+gdGb&Y9l=iV9Yp*IPwG#)0ZG@b@~D40X#!pTXv z-5U1Lakrsii^p$UbI@!#zJP_V&vWLm?0xOzOG~dHEFg-PY)|xmnE*P_3ey>sBb&x9E z%E~+xI}^D(1ZE6I8gB&XV_0-rA>a|H1a{=sq`-O0IOKRUrJVCqSQE4eO-vB!=p|di zV>u%+OM1n)3)rulG;KuS{%AHV+LYJ3odr*3S2*rOm3i_zQ_ZKOQ7J{Ub0<%1$d-+w z8zJqwJ9;hB9(RE~|G2q>3kQuJ&8J3 zu*hbt2Evxo!fGK%fewG6Xp=5JjK!5&ndfY4mos>TX~?M0BFBm=!q)#jK`xURlquyh2=L|Zyml{Ma3+fIuC^khRB+#L1^#Rs?-un5CM-g@ui zpPy;FX=m--bew%r|Mxb$k<=<5DW_y-Ta6rEH=CF&WeEerna*KM{-!M(eRpuPfZP~k zcx+Ib?cTU$g9!}O5+)NE>J0`Cx8Mw2@9KE;jMDCpHXgjD9&X4q^OKkfKS;OhM4+&^ zxFMYrTx`P_=aBGOkFFHwsK8X(&6(pe8U9^s+Tbj=OsP392i@e0I8lkTGxZ|uBH2%2 z+jKixZMmbtYkz_mt<-9O=$Z9$d~Su`Gi`!@LX?o>_sp}M*ebX3f=Vl%*o#&gb`7<1 zvcgTJPGOZ}9SU`7IlkCUyi-~p3<79Lc;Kj*01!uGmx53)VvWK^r^c7#%iUYI`#7Ei zeJV$}#Jov=6!}%q6;_7MM84g&49#G1lD!3|XKnQoxk4MBHfh01ot?d_xqQRZJ-Pn} znEo>didr$dKy+Hn8Jva~N6Zx3C8EL096C*4^CKs$@OENUMr}Ap#5Vz!yB_+i!F0xT zM?(7s)>Oc`0g?By0pw84hd(GUbU-x#JRVErBur>}o=(pnpL-r=W_vEl&@sm@9%m@B z@x@i+(2?_Bp=oHbwS1FQgrmP~Qsd}89ys&fNXYt!4QR>ZhNn;X@eDPdKW>1$c|3mW zbS^!=N|N3tt$G8$3#4Atq3%sB4w)XKy8uT1IAGiF8@4?|)BKlP(+s0+u#P@9F~Er0 zP4f;-^Sv9M<|q8#wq4`-)`rJZp{|YWZ52zV$ji@&qc<$%hbTN-YG)w z==1fdxov%}@kp}oZFsVt@QZ(<#`D7skH?rB;S5*(gp|%)9B9oA`rP`NWG`!ml|(2t z0v96&)&q|Rtj7x0S8fJuQvci~jnGF!yh45KQb3VHOmFRT3uThMb`xPy zgGQ@I4vVef?;QawwuwJ+6&>z9=2VZvl`L-s^ns-czjS&15al;mp>dlM9z23X1qdvek>d!3TNR1HuE7xw#u zwqC6pYN|Y9CKjn8`S7&UKla924ityQhdn z#Ra5V0N4cDXxJVCR?FE~VWN*wel)q!%$a-VI>~M@Dx5B>i4)CsOlmZQB+D7zQviGO zW=;ZTi>`1cGxM(!R$qTMWzKUms)Gow9I3AG<5r*9(dLHOR4W=}C|WR6I`L7qsTN*6 zaIHlt6UZr*A2D{ib4wve&O=c-giPG~M%uA5c}p_atFKB+(wXyMBW}>M5|v;h#bTg3 zkm&;}*BGRz4)o#u)*(zxHW=@>mdZh|Azne*zWke#>-de)*jdkHCt~9&(iq5*Tw0F- zNu%3+L=JZX+L4e3opVyO1$Jab80mx}mw$^C8j+|3-~XOFOTAAN0Z%v}kEt6CbKt$e zC8sGKs=Pq&>wFP_Bx|R>sNqu(lQ#H2b;o)c?F@mOJ zr1EZZ@A=@(ns2U$465^{Ve)vulpP(jPA#2)!=N{*cd(56I>ZrP3?Pmcihz!r<0!U( z4&k0wEVdOR#YUt}ApE7VU$d!>Oa+sCT$bj{sS@h9$<**T6d8hJqGgmn76zTQSeQvr z4q5G}_kS}tnitEzkm-&*+S#XM2QhM2#AenS+>1Mp@Ru|q;-Ws_Ar;*8$J_@sB6Wb; zy=f}yYSLp(F96;1$bVP*E?u8OK8(xv>vQk+p%~+DxTGf}F1UQZi?<9*Vs$%NvL9YT z!OL|9X-fGzr_$v4oRR8g4Eb#j99(UkGl$z;zix5A+G$sl(aGZdTokXCN2H#Tb3NJ1 z13vZv_e{1L?q%3a<%(>zH5H;o6ArK&nJ5ozZqTE z`_O2}`TIxYC&O2gl2J-D)+Gl8w3T6_T8^(5fZ0odDY@*|_n+>5`#sR5ZBP=CEZUzY zmypXqi3+(Rf_Y`e5`L0iK?5NUi2<@2dLs7xHb)2mNWhS9VXA0>Dvk~LDnKr1ZW1*4 z#JI)s16*LeBmxVR$qflfw9MiizHl9G2c?JGaxZ%DU&;h1>!xPPR*tW{cTj1vCpPJ( zg5$0eWMpIOOAXMuVd*=d<9SVFRYdm4i$Hq)BXz1v+P5#hJX-Iqp0 zd8P3ruoR&PYQ%1a-JlEvHz>o?wAK4~OnjMWbt&*4{UCZNZ4vhYnlI|0IiXl1yKgPo zZczqp#1>6608knwC?FR%1>31$ZM7tdOrWU*36yaEHlWZ7CH2CF*%rm|lpfllUC6bg zBvUS|Hb};FvLlNx%^vzT2;vAdh00hSvKDjA(Q*us=WHi{To@X=Kvx4wzrb`Yz>OZ) z)tGSIelvZ{E_WG2IlLetfv7u983<6RJ%qh^UI1c05 zva6T((;A{CJcEVP+KO>$swvL$7+{8Lnr)`IP1%f$aLjLkdt^P)8^cd^QxF9l@qAzQ3C676O+`y(6|L_3v@6Z8EdvZ zBV;YGbDz_@+N!7I1Sy&bD(lq7pF%1Ef z=ji}dKEaCUW$sRQKs#)4Dd1_^4CLw?in=!xS^JGj_C6m)NoiJMH!E&n>)f@j znDVaOwSL7*vS*UB$!ZzLN805wb;yH0bh|o>2{?<|x?;f|ZdZp`c(AxKc!%w&kYj(C zFl@e%hQTcygHUUtK<1NjtCybx!%;u2R&Vfj)r7OgS-RqY?|r%x&4w}UDpF7-Xt74i zG?SdwI)#rEC}H+WJPlOK(jHHbQ}=7peGuE^{sh!u)Df+E5=PSod_zZ8g8Wv9EeK}W zBm6JgZ#m=ix;1Piw86W!u`COf00mN;7RelB!)4^#OoMIBxpVRinr@h4tF#R|VVDR) zIyE+oXp9dj28e1?bDA7YfjewxNE2n9OD*5J1L!{#@Q{GZ{4RlJH4D&~`AEk(J_YVP`akF|hc z^S(BOt2JQuij-eMpP`H}M0C=_toAQ5ScqEv{1i=>+MQOrv$RPd&1|Jjep#E`bsG&$ zpQo`U+U$X)X0ouEq&_31tX~V-2nzvDyA?o02J=RFxFj5$V#+yT^QiS_Bgh*v&2ns3 zElAxsS|lEZD(Nv9>Hg(W!L<&y_KiB8ZJ~*3_#Gi^mk%gpkq_9z03qY-mFybLA7sO? zm%Fd0@xFtAa(aBGee3dV7f+U8Y}X!KZJ{f7=sJrBKQL<482G46-n11qvcDJy#*Y(EIS-B&qT5;hu3pi0(S)G z%5|hLS4!oLNQE=2%A!b@WqbDW_V8H+|=EU{=u3&;df zF*iaS%}utd?Uw1gP16UnE9NC__5PDt#~k9B92}7l-}V>n#Kndh_EQ}y(GH?nE7T1W zw}8Ghh=qJl0rnR*!wKorjyQu@f_c|z!}bFG1It>g33# z3#nzqH12u*)gS%PcdmQwdvCnrj0AGs^PTH{=fm%O@GF1#G4faYB47NI|M~43?z{El zck)O@?mhIouleA^AG!H2t;krY6wQr7NP)qd$4>&81VYi-e znT?^p=NM#iGNG<5nABWb9OHw=_E3lW);V8v8j*MA%5db0h@nA7QrKFKbtrL67`r{D zMlRd^(qw{!u(D|IA$97279-Bt5pNOKAn6k26{KEm)-zdKc0h?(!J=6)KIV;XB#1KW zzS+Y1Uo`vEKw^RdIATl6ocr4ef~litN07xwe1ZJ>f>}_&7cPB0fv4Hmu7oohaxiAy zAJXdC^P65)d%mdhUno6S;q8;*>h-Ar)4fyk*!zAPDzOyN7Swu}a`4*Jlh^{tpcN^Z9zMcDd74 z%ZZa%S(G33Y{vQm`*sT7&K3?XVrt~2G6J}r0yASQ(FIse(LcMY>}5$OMOacSC1#=# zDANC`pxk{`4$58Mw68C~=v40qdk?r;?P0(fpw{z-SV@QNWOqt;f(Xi}mF%N30EkKC zB7`#;8lygq;(kgFOG|MbwBhPFaQ-KaFoMw1--Si+M@1&Fm`zknhFxi^8#i;4Ec+m9#(V ziAA-IS*DSYrtigQJ9nvR5>;~ad?%|;Ywg|zYmxal)-i!J%RI904noU&X3>x+4o$3@Eh;A!phy=iTZ&?4?&}W zd$9xcSp^X&)T2q0f+}pY{AEo04F8#K7Vid6XNLW>H=4`PT@j$ri{jleCy~<_^E9s` z)F_|MFivOpjkr2Tf+q)lP?&myKf6*Zr!A9v-1(q&v(XIaX{09$h);#i&zW>y`eHVV z3{FQg!VIq4k-cG_M^Y7Oa>(7!28GT^h|~6^xT4U-!+GtRv$Qf&rQs}Xq6SFmipou+ zb*3axbcN$zG_d^4PgK9*Zu^2aUNs zA7e$Uw=L&r@!-=UzTT_jMy_d;FLnqX71k_Nb5k6>iVjq(QqbY~d=08}u3N9Kpo7>z zX<0bREi`D#qJ(mhN2}3mLc9)b;~z$4mM>J!?Y)zAOt*ba&@pXqSaw4A16M)Un)ey8 zW&1Yd+nyhP@=yNyGk4wl327ZtG4B7)KR@*P``-MQFUpmWuRe6;mtKDJD}U!2uczGd z&OiFsA6|3ahwm~aWQSiyvFTz%AAG@9^secoU%pfl13=r66LalF^K2w`I~Yv`(^ShS zD>9ZVPA|(6Qze?^7w4)R-&d)`QQ^#}cS@@@>Pz(@jfBV>L!6=MoGSMSVgM&#?EUPS z`-cnhZod3K=TUYco4Pcezl19r3ALb>O3&*VRbr}KLLEXaY~!HLi^Zbnsnx%fI(gw4 zXjz4Mam=OQ;RCJxm#`77uHJLCRdY13n<&%4tp?|+-5Im7IlNm;VnhVxNqo*eAOE^~f(0{ymVTNA*7OQJ<%e8kxpn_vLJX;H zod`I>T(IXdqiTIxdtNqs-gfqX^)s5MB>(YX8~kxwO4N;nM-r^C-&)^#q-+`VJ-sqq zNau~iEdWKi3lsY~+y*7~#kg2eO)B9a)B3J^AToUa`o7BPYq7X#2^oCOroDaXj4lY} z3Noy~Y~ei(IlJxmA;ZB#q7Jw{Sgk7A-LKNq*9s}jxjOBT9|WUYiU?Ktz%!89eSJha;TYhHeLIsSzUWI>Ii8uW7%9GoPcrfa1&r94dJ(%0#m+cl?~1TzFM&63lP%mtlQ*kp zRB^;UYlV%{NmG|yVxr%Eu+q5lgm=zU*H~{-gWVPWhgdAGh$QIq zB{8yn438U&wWkubbXoSul@Cwv=c|3LXgj+&8(bzv3QKyfgjhe85g|83IC$XO1@No} z+km*T#a=anDFwWrG2>RKD@w6BfQXAZpb$hQ9JGm?mZqct9Z|hurd&2cEdEn&G<^j* z5Yr{JPg&|N2sKA9ag)-I60V5}gg3NdRNiJm2uYhUltTj(1rT&5n@OorD-ClK2Exp6 zI@o5gCT+2}n&%x_=7wo@ln&t{t)qCt@HIdv%tqZh6&a-+x1tzbXWFvVyUaORgp=F0 zL49MF@^%hS-K;%84V;p_Xmi8bfeRX$u481eg4sdKoJEccOk@!mGtDYM=r9K=7XpTZ zHqja}Q?N$CDJ|MV)X*ERu3!==f<$DWGJQk>*%(#ZZ#@fM9*w*Oa34}M-LP#56`i)M zdfNV9x*x!j!Cz_5Vk|t8Z4}!9I;KmeU;-r^S&FWyFU*m5wqyscK4=m(=F+dJ1p>Ar zIx8&aT8*slBgsrIVL|)? z9ta`33 zlrhXh^T>RLwJ6*RjE`B~OPe0C+}eu?*s_b}5sw09x@IO5V((Z{iq^M;XrdmT_%(Tv z!S8w-;|5k|(^$bTv@}7&lbl+{5X~^lCPq+$gq~uAK`Ho@lgiJiDUG2(IlZsp+lY3| zMnZkxINXL=&%UB16hi-U1#{XSb2da)d7GXW78pW)3@*$(eNgEUCIX}7a z1Oh<4*kX&vFNf1Cf{T~#EQi#Yl9-B>FH#5Nfgi&@%5YbRdHIt`UbNU_#mrE zbKxdIbF9%%o(n$_On*KXAMv4GYs|&p$B8cO%;P|dWypTUM|HOLT>sgY?%Z^l%k&&6 zJ>l+GSw>&8EhAWABvIz zzZA~b?!BuMO`8DVE@6CDCbNgAXa}P~p^dPDK};lOHKkUL4X0M@z_@cNI*0Laj)UPw z&u}IHmp#GSz)#t4m>Ss)MziQirm)Fp7O_~U5xD{;+W{@((vyNhQFMN^QyE^YIRI*z zaAFj`$cY}FERC=e+OF`g8rmGz2n28x+(I+4_-=}}M|g=ax@8f4@cRf`AepBV4ZS** z?UIK}dKqD&)}&lx>6$GAz&;zJ*}}B1j1&NZ%DGDrk1Jxu1k>Rh}B-$Q&{_Y^VYd9)cXZEq_BbdvEiv~Pq-bJ5|F-t zYfiMGkUQB!oe@+HJW2#ejs}mveSIF^sNaqU0)$Y6-e=^P^gF_8l!v;w8L(MkL_0?4 z5$@beD#u{!JsErV;9rc^WuVL+f#z97wviFBRs_^$Uv42nF5u&Y(QJoZ!ZsjLDdRjJ z?3KMhrvO+c z@9cJgHu$~<4i~IHnzWUxb80a%PcLTFmZ{n;?_p*n-hZsQAKS-)EnT{IH0s zH!&vr2WON(lnG(er=+YfJGBdF$FE}%Gg@{I!Do>VmX;QXSnBujH%E=QB;-g8ITH4e z3UUxe0dgdU9A>FO0IDMgq$p>XHa&fgEkX@rB*f$xQ(ea1z3r%HhvV89T*dE&j!1K6 zaGrvgWfGIaaK^ZjISAYd7ag~Rw=<4NTLxUq@d$XL4+`;5u-ck68{Q;qFdzFyc38bN zRPEWcwQ_2UC=eYf?K4~q%Q~2cqX(BCPW-uFG_$+?7|sDozh~7rA^@9{I)}5qmA&%D z+YYr}QuEo*AO)>os?W-f`YBzp2Qs~MElwG;|H92?ajjvwj?+HAhaYt)K;t4?{B+T-mL=`tb?pvi4b_U+0ASB- z3p|g9m;~jwkad?fI!rX&1*{2S9a2uhi4z%jWPfHw%sWQ&nhSnt-LSi3Z1GE_(-NJu zCmuE@%dDoT>>^`}HMgHGRO)>ri95w@@TKDmz^Uv(?C7N5*GI=3ghE3Zy?2aQJV`hd zli_nQd&H#JhxIQ8SllFpVoYU0@tBFAXlqL*An#$~2c0(y$k7R}z{g=6}tlt7!n zY867sw3h>|^;-t&O5@HHtpgyaci^+Ou8`u+YHf#!pSF@6R*w?nJ8Adic<~G=2fW!1 zd(rs~o^{V4zK742Tqfy^JFP);10pF^;?!1pR_koO&7Ogdr^*T$(m@U#7ed21AjOMm z)q~8BFn@@1g~K}e*i?)N$?QPbXqMyCim8&s!Xlj2>ZI&WXSEP8vV(a*PpfxLTrg8C zY4J|Q9)YfAMR81kd3#Dln%LwHndaPblSaLN-#iV{*ZP!X84gIxLr`cu%21BvvG$+F zkvxs_n@yTu_{GMi_|*}XJL5NIkP*iL^<5}f>vgde1!H`Ly2SOI#hE4at4+80Bc&V{4IYxp}g=5fwcF}aonzM zrygtQvkjEx?UdhoqVd7tWnHkydmr#BC}HLW0!hUfLm8lCbTndh3`=~K>pX(js-U%~ zGqw$De*%_t8nA?Ch9!K;#SBt7ETK7%B}g!~?}0$gSQ1bKtXTs^?4lnGENL8ecU+>Z zlH23jBF`$u9EdB-r|s#~gk?Op8DGX{PWgU#ENdSJW0Z@^&d11OiseXV_3N}nBcioW zZJC8QER}*r0#ib=Qw$@e>q6t8S;1k8-OQ| z)oW*WS4q`BEJSC+S=Jc}`)X7t!}tq{T!YTzcc-mpeo%DlUT- zq_oBfJWHYBvX4z=cfC=wg8rkkvYqo-D7A#HX%RXpv%>h9ZKH}TC9fb@_DGAUQ9fB) zr7hAIPCG<=IdlgxSBL)iS+3j%zsj$U`+o1ME?vp)?YSRsx{V)iT5>>2t)cCsuSZv zD$WnQ+mBCdM~^vAU42XoJRXccp0&q|nzHcMu85DbgJvs-V`0?K%= z_S)kW=a@!8d1~uzD(8R&aujUrk7F>kQd&7VnmR+RWj~_y0tAGIX6o6Ib%9+#3DrP3 zE(Vk?j+y-in$1qh-tby|-W6qsU#Bm3CWF_R%4286qwFoeqo>Q+_w4gcadyM+>A`!s zYFEW74t0zuz^Prl4IX6=RWNpI-i3R)huTF4X#XNBz>S*9R`0<$nj-iB4iFK0M{Iev z`eR%^-OHTmh~F@qU?MU@5;-APhVnMBV?|)rupjT%ccksql6moQ9^rpFr_&ooDXd8G zzc67!(nY=Otul{tv2MTF!_F(~`F`L+z5Lwzu`v$irdVlZRGsX6*Q<)tvln`fV z-bT49u^j1-fnRBntgEpzxt!8kwctxw?;|p=v`%R~Vj-G5__2TRN$)kJ*8cAYd~$=xgQf8NU?#utkz+|5LRsoFk}>*V=F1{Ypfv5aEbZ(1HVwl~f= zYYt&a)*?NyQK%_c9{mwtXpS9gdo<%5W;zt1Kx#J*>QjT}3A=D4`z`p4q*||I+W5^xXg8~@i zI@#SclH~bAc424=CUJF~eOm={QtM=#E-i!50Fehn0M;P7D2K?wCauxICTqQiZPj3t z_Vw78@^=A~s%<56588tQBkCg0{7jfP>sXJNXB{r%KGdPYf~NqX(!L!uVn#xrZc2k#;VdBICwqR| zg``;Mv9|Y?gx^R5MqE>rLHM)%J^Q~_=95+&f$`EKMzO_`YlAkz0u2D%D)D}XXb)z` z{BTxI(|-X$9oYX%90OlKp z){8dXgxTCA*(?`c3o!+y1d)_Y#|0TmV3sjRLo6DscF?Z-Q~p-~|#+_O!1B8O<>caXnh!r_L)BF6?&i%^QZ1S$upzWYlJ&x%q8knbLm7 z%yS!0XLy&I5c3;PXL!roc@g&K-z{<3g0DFszM_InKo}~6xRyX#`X{*V*esE4f9q?R z5mKq(vR7#<8C=aa(#pPTZ>0raS3DiOO79?*ihJc*Sm2CrDs!uNbG)6g*zq0D#j&*e z<8=2%q${nlXH_G;mo+kk!Y~)u>^`V{A&cs>i!me?%TeNTGbr1@+Tft2=1EBVEaii@ z%q+t;!=8p{*L@FOqhXu`kkw?_VzW5d;Y4hK&Xy+LJOaAK?A6|?S~DlQ>=-$}Z1>(0 z6(-4IL-d2w5}5Vxn;IHeG>$lDwHDnn4FDTnbDTzMgV0z=C1P`{_)$AttyC)6`wUae zYRS_bl?Q`Y=WCX4lTi=+wRP7&`sOBFpoNusJ^Z?1rPRH`7ySzT^P@t&A6qS9{DA_w zvTNRBQsg+d2Tn2z)5fZ6b>+`+~V&Ewb?b~HXvJ& z32q@YmkP3<%i7WfRdyhCD%MCaWkee>k3WZPFG4i2cq45VPWrh?D^UErNpgp)`+e@cI{LJ}wfB)^Uwt@H5=U|p zC(>4&G-*=mh7dvt5Fk+Z+LR>q$Os)7@ol4~DyXe!)mAMk+=5z*R4(E@JrI57^)^+h z3IarNiUNM1cK8*w!#qT5s2DZ){r+>Vwf8>v?sFx@mXnqj=W6Y<_gZt!wbop7&9AkV z4wYj3Bx#^<$wtnfKVi(|2YtWLdM~EFu!-F!6(dh`q(Cx2PKlalP$H@_??jzSF%n_d zO*0GIxK;*5i=Kj5Bv`DMkwbd-24V53$Ix1M*!J#CPnZBCXUCRL;ToES zQ^`?;rX!h1BdA%yV2MG6`XsLK(+*H98HOgA@eOOzq*przF%CyGG?tQc$feIDW1S9Z z_i52-tMg0p$yF>n*#ULT@!hb2r;=8kWB718b|Ou9Dv0xWT98FlLL8!U&Q>@G3Y04dPK{CSjm@bIgWP|3Hc(VVtqDF{T(4Od4ny{Yr5T3|Qk(PCATg_Y2;U-{fODRsU!qxIUMa-RknSH^b_*A6~X5hg!q z^__rpxb*?>7h8xY%0a&+BtdD_Pg!DS8i?9iONU$EWGaE_>RKGk%wl9$5>TKti5_hl zE(t_4fq#rr%^k>8pc!2!-=tI;J3E}EtC@;HrT{olP>*37U8nPdea_u|rJf5R3L(ga z;Zd{{NlWt*_<+H=0}+6bcHm?z-g+-hKh-vYD?iQfqDfoW$wuB;56Vf)W=<=XrMuFs zH^K=Kv82PJV&KcMmW%^c^l76Ln`|dwcNlTRD zMAK_O<^g_GNc7gv$dX~iB`Q=_OgHMixEIT^F~r1X{{M&aO1F-O6*U|+BuTrkqhF;% zd5cN_ujxurfRR4HH0dKWh(px5Bdl$mZ$S91%^J;Z%=|XDnd`+^*!5gZ9$$=%=32## z{yF1^*e!mUi5^Qf=j2!(Hz#u3-`9#H;AYd4t7wc4L1+xr#d=HBSGDL>=VRe`%Fe%Q zSF(J?oZYWvwav!2QLvKS9<&TkvF)O`@fcC*Dtzobp~(?DxgrG^V6WbkJlM z^dTzBUb|Ho;bkQw-^y}BTY%5tEBtmB9Ab=&fwG27meT< z-Ivk*QTy6Cn^t5|nAKb9`~0;spnn6hCJ9;3IX}leU|8rO{S#H@@93Mbsn1AFm9pTK)FlKu_Ft=GrED$k zShtDBzB#6R-r^fy;<5V!np}1QQZ?z5&TrSWzePhSgkN3b%7x_dX6woc>%-Ezasdmk znA0V^Y;Z~hmRH5T@|w2w=AgFzaDQ8GhPEF41KieYtpVfuBmHf?#w{=!*LiK)d-cZk zzx{)x{f;Nw)kfDVlVxn0WG_uxJkDZ>?c!v6*8C+r9_JAR=jIZZ3g@a%u!Q=j!(#{9 zNAV#b6t{0kjfMeTBjrgh$@9eR6!UC~VVj4qws`y@&KFDLcxG%cL7f7f3gfo+p)N1pCi*slX}HEu_au zw~|upiy*d*$D5=XDTfE=xqsr2(7eZ^aNq8)hCZ3^;7OfD7wzQn0-;1S{$3u{4|L6a z;gJk8;ZYs2D?Dej`R?%ez-+#U$D@Rd?A0p{nY%xH;@sl=bv)l>|I6!1kDy0#W~x=k z;~RLqO0hSR9_RO)Nasl(AQc*n+M9V4atz0QeFbG64}=VBW^du zLHah*6ZrKXBBhTYr~K_azC`+tN(PEgZCL%S|C4$0{D@gVdIwn!3?+wuJf7%a7Bww4 z=G{Id@3zTY*+Kk&lsSk;4=rDj3kpN&WQSz0)(+yNXOG1wA#HQm0S@ws(nBh;x0Qyk z)B0NbJnWy_;qzMiT>Cyk#hPJH&OV(N0yNY+D6pfv4;2sdMkd)<@gY8uV=OzQ{3GFO zS#kJ0?w?1)=c?lH9gpYU)>!Z0(}!P9K9?V+F#(Okn?Iiwp8*-G1&Qnpt?}MF52xRZ zd926|AAa!jpUK{N_~7S1;&3*U&Qtk<73fRKnguljPM5CwDtzi zuVm`l8v>O@W9ldC#$wz=3S`57qTZn8nIV-Ghb7mq_>hQ#Hel5jON zKN}D`t4PgTB6jo65My`ZZ6n5@AATxN7PAl_v_C~JKRG)++)9Rr+9^tkw9!oLNfi8x z>KNIsB&Y|GJxbo)XY?^kAj2ss1d#MKNud~Fika@u9O0u@2u_*)WsN+K^g%a^-2H)Q z*Vqm()u&droy@-WZfDsHPhl}8NuQ_lZPqRD<2n6+0-jaM@5}X%7sJPA!pHOVk7xOz z3g<(5BBVz{`VvNCeZLXX=R%7es17nf`b zZ78SEwCY||naWcY$U38P=HWC`%~rbK>oaeoT`C55I}x9f}CUC zNc#VDgqbzKoTnB166(o->hTh)B=yBm@2x@ovO!%@gZe;#da8taI^cS$gepB?G1Q$ksMihZ>KfEH z1*m6AsOJK%XG*BjG8RMKQG@#X26a^p>Kg;p^Ci@00i^}T=&I+mLdp3)ojaa<$l`A)yL!Y0h`MuHqVAO zUM{hb-nO`nSq|VtA~sS+`>@$oD|gY#)oJejSWe1oU%9Qda{t`Qt*o_vZ)nBU zvK7}tpI$9nAuV)qE4I|2{(FO3@6$aI8|l8G=ocgB*jy|6+g7wrkh?*MmDTN>wPhQ7 zwU%uyma+}{gd3)d_!bO zF{R2N-iA+0pud=E_D^mZylA7~OSkA1f^eQKwm2M-4_0Q0$p?o_K1i}dl~{1--BVka zhy}Y;!)wu=5>2!PMHg7#q2(V=U8I-Rj4AeW8YNR`3LGFN&zJNn>q#KG zUy4M>!HOlv{+?f;K}CJ`%tU(K?nNseu%TA&xRtAOr27IkH%e@7hS_tY#6~uq#n`N` zLH!AXTAw}lGCKX@Y7hxcXl0j+gl}zjVaOmI%>xuN>+fnlm^7K4P!C1H>4cx*SW%sBES!BDRA-6V-5t zveGtX$vE?init*TAAectL~WJ0Hk;!N+2Yk0Kd`dp4D^| z*G0{5O>Y0%VnciGpr^NoiGL+5FPCk3;d-6!e^nDT@2;d_dAX#hh#_5X91R($@XTGh zMNF3%nbhWUTzjs-4Ox4zJ4WsHHPwY#!&954a1Ir?Mo$_ zI>Mkxm4B&ZQ!+sZHnk9$=Gq$6e`Qd+HK^MoHnM^DVWZ6+Q9S;2E4Q&$t{1R5D$@mv zAGgXoHtnM&jmj{-7@O5KsK0Me>vBULpq?n9o(gR|Q9_mFeKFKkHK^bJMfG=`6=wnJ zdgE4L;qLjTysN#c%%A7sdVR>&B+~@(ue4%eOgQ+?z7F zZs{t8hr}v?h*t_9bAud`>%*Sg`60PJ?0JZ1w$Hljq;z@=^Hxp|IlU?h7~w7L6KT{5 zW0cqCG=pO5@5k~(^3)uvIYHi$o=w&MAbw0q{0Pn9F)0O==c|?~^tp-SYW~Il=tAS4 z?V=N;@TvXjg0!DS(zpr)VuMpG)*dC1C*UCK^25>|<9{mR3TB!;ERP`xo9vcI=4M`9 zRAs0RLt?D(jwEv!eUm$gVSVi+^KEy)y9b%R?}}u;^$rkYY>>=T#-DqqKE*x_96)Ic z?~hZ&!dNk-O{wi-QlulIb2kNR_*+|-qz6M{lKK{n@2v=ml1(aJvdAtmvV@o-52Q*7 zD0OWT2J{sX(*WP1NeT@vyt|_2<##Y?+fQxAc7$XBbuHYyuGG)o3~d>=z_|`h{=-J z{km2yYd5G%!qRo6$WG_Ph*69>L1OCu7gdyt=C~d7I+Pct067R^2_8+&E2fWPLi*nG z8=C&K=*fqmfZYFrl1tf zk$~yscr~eYe;!|HXC;*jSKd_<`Kmzmgnz^fe0CvtO4NSP6-9Mrbb3>m>pd36RJ*NT z@0!BsTS=-QKn-e>urlb_{K9ebAhDh!60_Dp)NO_UDl zSt3mwvnEC2T?)3*KdI{$3K6MB%y4AHMyj^iX+1U)S4V7Q(kn_a8(dOKqvpB?(uMGi z$){0ryO(S7f#=5DbgsZTYPjn?{+TA*t)(Do zL{~=<^(~DeD%u9ELrS1^^YCQ_H1enwVmOjQDKDH8itTcNnwROaAI0Ci3n(Jc-VTaj zqF8bXQicVhncX{K!v@}!GSh(fzr721qqKlG!b{n;46S!9A<03cWO7w_8y4W*4&s3I zccq*(;C(a-0cHod<#oG;rvZFrV`<_TEj?qrn0tA!a7Mgw#Mx9x+^FrJ4*kV&BksJkp_E%-M4( zS?p?G8hd1F#IjvAgNYvl(Jr2|Uy?)OIS`iV`Mylomt>;48)X&=^Dl-h=-2@(U*m+W zt#Bo_t_%;x*6Oc-BQ%x3DQ%y)8k5sqDcDLfFhc4$y8ODcWHlN zLn0Os3J$95(BfJECaa?NVSpKVaw9?!q89Nw#8M$$wp0Ti5|I{3m-w%&EiPTgQH65j zu8FWG*i}limfgygvzS2pe?z6g(Zk$0gM&wV5a;FS;rDU<#(-(8q!O*_hVxPd(@+GD z&Q}(Ob&%)~s%@?YVc;v$^%|W$2Pe5!xUKlI#T$NPzd0*%W5tzf(GcoX#bBMg<;EZE zVfV4G%DB!!_jF=OiyMCqx~CH`r5SW`Lmjc2f_EE;8Pm5ItlL8D-aA8#v2kZlBp(PT zI(^zLc$}p3gJ!3%Y;+#V!|E$*vfF&MXi+_KH}-l5>Bu0O+!r7%)zni;)5A{GkZ>g}z92LB20yv^!u_ z%2ik$%W}VB7)5J~%2|ZFs@=7?C0f>EegDGBb!rTMXRJ>LTLe^pI-;tzA{PC!6&23zRm`1X2RXq&=BO-&OPu2#Vvl2z`B%%_w*Bc*Ir1fQF6KAdtJll~2JjnB zR=pj5v)D5hB*EsYUS)pMx6<5IeshP@+N6NU2=6MtdEn0Y4SVeB{01_NT&A`J9vv^X zIblK2^Fe+(5z?bdw;G{^X|uPY)E6UxD~|n|O~T#9B@il8#eBFycT)LWfGam3%53gGV@<|XCrNDkIzCW z@;Bo-n{Jyrh7U-3`lq;S0B?vJXbKW1*J?aOS zT38R7xu9S3O%Lfm3f4wk0v5p~$D^P{Sj|yAW9*RdWg|X@!Lq(Op&s~vAGCmxs)a8` zO~zYzGPLl7weU@}@L$9ho{TL#t`@#ng(s9C$`+pV7Rrs%v_QL0flZDIr~hc-S`RA0t0MylT^s$YH>S$eK&s91W8l$-fJTW^{~TDXKQF^y&o zzG2c>y1ZDVXmd532No-OHN@G~8uiG<@9Oe$hofszyb-axy1eYYGqPiBZoRx1v%4pKb++gnV`*+Srlx_eN>=CXO+D)yhk}iy+BmdMe8oAKoU1pJDzKVm_jrh_ zJ7&WD+v(OTv0IPFxVocSfjMDw)&W%@%5FUlG2^ekXKA;Jt0@kEgy_XEKEmXH5R~?c0B*;|_ z&JVL_&5?&(HAjerqu*lW?r9pibLM1@j*yylOH1jT3AS5`Gj%PqNecSvH*U>W+_;~x z#s%#=Fw>Xh4t4SBdi0DwAN<9ok)aGkJ8xNHAet9@F&4WKi-~|_n?>Fn@BST;l;@lq z4<5&tVorarfco&|VqXPDiPgvFmVA`HEHd3&QLYpay}G}m+-Poo!6-LuPbMbjE2iAL zrxC1Z8r*?)Sy67BmfvHI3nrN`a>vzS-p4PEh915-8hq3K{snvghVE$>Dm7*{a>vHH z|4KZ{a~+Lie58NmkjSGK`8exC8)cyTtH#reG-Jsv_IS6i;Nv|kX+b`bOZtJhu%J|; zMEr=hdMeK;hs_Yz`y&LCb!^vOAw(f*Ozbk`b|}wx35>f#W6ogC*>l^d!dwi+t^4 z_tyl#a-S-5$7M;($(4p&za&(L*C-bv{UYJX+LQfT=>bv z;j+>9Zpz^gIlfoF3e21~!`!iHbFaCqLpli9c18P_& z4sdyD>l06N>(S`r+!FHi=d+@^E!)!OD$4;?EaJpTK!y{wik=Lzc^RErtI1Nk$JYPiZj#~Rpn!M zt<1q`>x*&-nG250f+KS^OxBi8c3+g+;k{78;}rMC95>4xocG?C;|O)CnXTgJfLe~1 zIXFhX0fr8s53gy%#n>s#g;sIZK6Sqwn=->GzN)PZo1;X_nH*0;^-rDuG?ET+mj8C+ zGK6Q;PV(3JV^}FN5}V%Xp+WzF11EoI7O5iD(W4fITtAJ4*eM{1$`c=AN27DqUhUWt zIK_w^TS9JnQ;$VFp$7#`g8r-M(% zbu@ZA8_mY-X!P+VN28B&G`fz6X1!ssgPVSox33#u>2^YddL%rvv737*(|%sYj2=zRZ4Np|Z8+bUqL6Of0A09-??O`qVn zE1gtSHOG*v_!?2RLOE#Fh~RibPqoKl$W9>97;qSSs@)512k@~XW-i_lIXjf@Q(hp|iC3JXv? zjD)q1yYx7^k42BAi6dNEv%ZLDtwfHe%SD`iWCoeyj zF4IJ^{ef*FzCvr_kN)0ojlFmAHNVy6zGV9Y+eD1@*2JIq{$Fk{Gm2#U1KUJhO{KZ` zy&v`iDp_SICet~f9wd_iSa|X)wbMdsAWRe3L#aOR>RH@v?I%}6iGPgBXuk* z4U#V(sePGAAB~Gar9{TnARuNv@6Uect)An8Sqb>|)>f1V@xeG!b%0@Eig~yu$_F>D zbhV|BTI{~N?%ys4!lbmi?m(W+g3wEN+M%cJ-z;-y4X|g*-1{uI4Kii->KB$Vi-`>j za75CuSX8TR!js{G9_4}@yr7|5J6|V44V0`qv|3nnYQw62qN*hWJHL2fUzG(4v!~7b zUp#whc$s-mwy(y#|EE!EE1oOqfX?}FjY5m-^)DZ*f5CuGM`~Os^YQwZxl#q@%V+9e zj@7>$jbr9xawv{>CF}KPcz?{mC1;`LdWPa;9r%g9Pp9jj=Hn+Jel{`Mb;C!^E^)3b z5pxI12}mqfqW?yk49m_W%0uc;oqr-~!_#MbcHLV~cO&zeTA zzo*$FrAJ^}vAgAYeAWwGK}7h7i0tz z9=c9wnDPb~(kHJgVAqWwl^djZp{%l9Tz7naLepQm2AE=y!xq@-bk2_?L$Pygvgav% z)V4lKi;vA(&v5_tfPJK<S^(WG6T(e#u+=mKJ6ezs>X0V;IMCCEr&wI9#zIg(9}JO)HWbf zLw-WUz*5!)p*}%0ger)kF|zoEZrv+PYz-L*GgfYzUMy!Vu#;GU^X*TlElB$I8t~OWCKO#>=HVm47tweKId%b_4w!RMtZQeX=TlDr- zTV%-b{bf;vH_w6hlpN1D<#^O{fFC(-H05~0bLe=2R`F6(j=99MuwjiXM~$&ALIlTv zr0hbGL#^UOQx-e}mWA*(vdlMS!8Kr6%*0!qZOVdgz_PIV00oB_wAY6V7Dn&S$tC@X z*TBkhfIMY=#amQ%#>-AGD!Z$_&p)hK^npF@0VrGO0P2fgAJE`q=vm|dggT!>HlxMN z4+Zz=*8Ks|hN{_|Qs(4!bAU^vgB%KybZ$~46kKQ?f zk5vTD29H4mZgZu5e+#qx5}X1*!6#R16z+h~ZJd!$w>=lf@mNXRyV?(Wm#7kWCeDqFZG#YS7a0so3NTXHEvJ}z8~|E;N?8hg*y`cd?o-h|jDoghg;X3Vi{ ztVlljEycEPFUB7)ex)^AjC>02!U%S<(feEMVJQ->Aw$@T{eL$ZjN(7wEu)$a!j%3n zR%oc?MoJy|3QU|Rx(x%BW}T&PX^KuG9L?lKb%Na0KI8*)&ys<`FJ%)9b3Fs|VjR?p zW9@3c$IEV5QWlSum1UO>WzqQ;Pi#UsQqRIwUSv7elm(xaLBV%GmXk}f6qkKES*|T2 z4&5Y4Z~gU>@`Cfq02$RtGAy>rRNeg{o2Wxt&*p{j9Eg#A&CjmM} z5Zzoe(Xmr$>7=>qB;&m0MemHrTV8P1BrQ?ANefTXJsx3jaAID{O7rx`Vq*}EZ zu{)3XGQlp``VL>YH%&1D((u6)gl##X(zU&;NkB79e~_*!l)0* zVqs;RNLMpuq(y^7#gLZ6S}-E6MOk3lKwfDo(VdTp^;W}}c!v!d6GK0AYTwAb7M*H} zk#U`bk#-f^=2t1Pg5G&k*Wtu*-Wl1Kt7S%(%Slg^r8eeMHp4bH&ahDp2ZK2_lfJ)a z6JxA<@|NU2COOk4+n9K;58<>KBY`KFD}dR7{)=rgPJjSH2dMGgdrQ@>pgh11cYQP1WIzEj=kb zC+YrhT*$^sQ5ZFa52emA#!2QC#34b%1k?vHgdw@mDiSi*FBhH`25A#X_e5A2q$p}( zn6F8A6zOU;96?Cp2<37?+_ zG91W|jSMGV3&G7M`ac$HN1l{1pSi7@84$FY>d3jaG8^BrE|&LA10aPj>HcUK0EG@~ z0FKoLfHF5+q7o!qOME5CO|vbUnTGiH7(FF1(kJ+6nBW6BxDI+({xh6bC5|v!oJnTG zL?7703a!nMsqemM3`0q}*2AJuI=G#S@^qB4IvGi27KvrDlx~Y-{Q?aU#ae@9#LI__ z-R^GS2NIHVg%pb8(iz=iN_{cbJ#j0JOPSDtT(WHa*ZmAlyY?`RA2 zwdHLY#0WzR?>ZwiRC+}B&`DCtmivzZ!#K-y=RpVkjkOX#KZ0r2*ba?c90{#}y$RZF zV&-5q&$OXY%r{tdpsj4S`@^>yY!TjTF>u)!mR>GfYGk15IPl#JuTdk>Uktck|t+xY1yAqmM=u)s+^5l^L7F>QuPx z?NJUC)0&<1&yt`*IitX8EkIE(mnt%s*3IzSmekw$-?-HZEX>9*iO^w|9LVFrHlN zA6(C%5H*+5>RjugmLw1InBGQnYkBAkdcsv8I5L@B7bBPJKpASAo5Yav`_wiDHOPnq zx+O@CuZ2);ZP1l9jj~wQo}P#z*Vy#hp)(UDTgjcIa2uu?f9=7+-_r0l7(W zvRm%h)LYL=BO5DY;c8_gv%MCL8jwMC^CwqUTFie|@moD^1iqMrZJVtUN!(@yNWUmA>?6!wu!`WG3)p_g<-W7J|S0gHq`kOVFz(F=Mv4e7hB(!r)pgL5V1{;8?v@` zvn?U7oDvpmNizgF738FIQTu%8t=e=p(DO`VftDYf3Y3HXbOUiK^w~14q|A`Y$TDo< zqTYeS0ZAOGOEHx1SDW87yf8|Jd1)Su-UJd^pmf!WcITbt-Y-83rJxOP?B;P7UI=U?6@dZxQewue#l%#b0r^n_r0-q)Mw3IV>erk+U7EraqZ= zY(Cvv0Y2t9ZCa_OMJyx57WH&njs+u_k>Xosd-z%@HiTt+s4YFq#=gw(t=#pUaMS=b zsW#aZ>8S^o?J3z4Z~wn+Wcr>;znkbxPED^^(_OQ2)#|m1y;Ow^r$+o0saeeI_T-vZ z8{q2LFn+<_LJW7S=$j^3+?W;Y|gbb=UP;@gb|A_V7+avXahNi z^~0)Hh_XwxZOw$zXGMJ3o zdL!lA=s+UnnmdqDzKtGU@rS*EVrkZ+uNHHddc{2!5b_~@McnH4##r+9tJ(z|K;V4- zj@en96|Zs^fKHdcmDMC;iiJ9yDXz4cxKd1UrOmwtuNCL0F7wS`n@P5UZ3fxfWw-;G zu}*;$GhR8H4V=x!4(%Lnt*>7z{T{m5*jl;j^!58~E=B|U=JpmUv_wQLw_Tbjw1l$` zX%DKQf4uJ+in z0$~t&x@OT>PR72Cbq%LejYYkdHKP?FI%`TV>i{?L?16NsS&Ojp>et0=%J_hEx>uE+ zk^s4~B$Oh*vwOvy_*|uIS1HQ+phuep~`^uGF3l?WuN$T_7_l>9; z5dKWd4m!?lSS&fpQZgj4LU~^_f-~^e2Fq3m0ri}N?3K$;MH~4*NWP`nuo#}RH zBjx*S8AI5z-v~H0BVCl8vERfaZH94bay%1?{P&x+gbywiSRNP3Jx{6ZtFW)`md9PO zQf53Z&n=?pQM9CQc-DkI4~-76t#!ki@>J6>tLZ0gr5yatX1fVbz1z?UhE2g6xTZ2@ zHgzzJFm=Arw&N6sHGNXGg}Rbm?U~+?tQ-URlNzX;(zu7G+er)U-+yZwr0hYpo{5&~ z#3NZEWhY?(FaoMt^^CTZF6DRhjQkCQzF90khb@`5WT(9i%o-NqB&b%ttIBFla4^se znUBowhWTi?9hx>S9@AiYmbXFXBeqt`d^Bw4qY-g0I2j?I15K05jkC2BrZ=c?haL8* zu{nCW#oo7GT8zq8ljez!M;MhfTwgUZjQrFZw91(0#Jg?~t$Y=-(4~nB7a&C^%gFhY z3es4{<_`HJou5n7VeF^snnh;neM?NqKH@r_4-y|_3pN4{XNkfNd@kP(=aolh)Iv0y zS9dXbp&6p?>ddc6lwuCxmmw~Lqssz|bAH5&R*@O)%P5=_dtq_U%R_Jr1NJ@`1XC(n z;oF?4>PI&$?zz27d(NxAlb(Z6i!sO)7r}3s`{LG&t~XK^8VQ944imCy=o==^GFJP& ziyQre&iNYM4>yw~E!U_n#}iADDeM@7C&1el@xhjMDT!y^#vUb(7qYw}QgNrSb!_Kp`2 z8kbR6$TE~;>?jJ1q2&)5p}uk#D)-Bxe$^Lm4t0Uqt?t(2?ERS#2Vl?H$gzm z@K)Y%*55?N7Us$D;FT8N7TK@{BxW=SydVG82(KI5g0iXWO!F{QePg}603F79!Q^1N zB|z}b3evIr><26jq2-|;mv`@5t45b?b{MD;_gf=WcYj?xTbp9lA@my!B@nb#BkJ2( z8ACg5wW^xwG`-gd&5oUsbiBlPV+){Bk1sx+bC9%bjl87~+{V`IDAnY=gLXI~%TMES zLGUov+nl7<%OmP#yNCKNVizggtTeAdJ&3naijt>zxtV05mks2g$tH3f@XhNa+ zCy6O((kuQiA`q@IL}iNF@E~d4`{iF| z9zX!*_isoORA|+tB9%DS4TD{{OhutNx-4OgWXLZ;;5=v);Is-L{OyEjqa~+18Efdl zAI1bj_2V`NK8UX=Ui===EyQfhc23PJF(S>i5Nd!-SR*EaZ1}A$PCQfXL+PPRTN>!f zGn{FWv=8x~Lxm^F%<`GiKZR{K>VCi0)s1wn%c_aa-R!|`K=}R^CI}`bOI&%P{E!e_ zQ+(g|Iy%A3RYS+3S7WFoF6jXB1SKIoAI7Yf*CjJZ9v1kOSMN` z!y){r`=3;isPE&VftmCP*XBmCRpgw5FGJD3E{~m z5Jsh5Ojo4+c)mhVAMH(Lli75*rVb;yh`uXDZMHX=O+nh(B;AyC{fX%@?N5-|3O$X` z54jHfx07Q!QujNuQ^mG&fEF@|qXO%ojVHW~LlrI`%U24Qj}l}6q=-v|!1-D()vR(T zY-0sVi2a(ZpnN1>Ehr!Etpdtqgi>skBh&l6)mu|+C7##efQJU-Jpl6{ukQcrT`t)A z@~+(&FeGb= zd)_T(NZ?eyk#1R2tkc&i8?dEd7r)8YOs{jnW-6M+X}|b2URS{F0iS1`$Kn1hB^6B3porsR`*i=(PD zHI`Pr#;fk$e$~sM1t%tSumJ(YpOg)*O$h9D1ZMgW2s|#@Y$IRE1i|RAH&qSK4ky5@ zw}RoBirgvS5fnXR+Y`=CjH=x^)UBXwhV?_P>w9qYtQ(8|vdB;Tq}7m|D%v4sOW ze)-lv*x34qy!8(*ZawV`+H7q5zQ(pc;BDX6-}WU~b7$!wa+xH2W#Bjb2ZO%(hk+bK z7txAc(Pi>iIYGh+WQQn!XuE>t>1cn!nkz^>gNjOclRk8`z`yJMMfYm8sbvlL~Vj80Y8z0NJqTvBcOm-0+&?s zzGo?|zrV?8f|j4ful8OwpWy1n3kkf9=XsuwhUc?9e;TDeTeY9yZ&Kv#V@ikjBS?|1 zJ%9+*$~GQoKS+Y0vX5kZf4Y}sVt+bAf*iAhWRvdJAuw5IfV0(IH*>VI&D_O7vSokT zCE3aiY9!owK14F2*4UYz*`z;m`B%2uA0dR zB*;!rkleFB{W!_G{psTrmf#S3GIStaxQos>(0&i!5kblxt$#n796&^|7;V0L&(^xa)NH93GL1@?PL=QMYCDY6a;UU9i_s)|eLg~wjZCiYPf50Eb@+e5JwX~z z=`kc+GvDfbF-bWCyOLyk2h=EY8fd}sIwpbEPY5+sMH~>G5Vx^! z7g_AyUrUnup}w!Q4MHDokLIa!#U}!XG5r;NR&5*4CY-;J%~}Sv>%E>2IA6sfow}}A(ewul)MT8G4$mleDS~+{{ovTwV+39N)VuI z=|BqtN)LBjRKXu$EZ|`rQ;YL)B(8WO-V{0}vn@UGmqq6>P&TRPI_J-2-5htJQgO?_ zvEXs7w~5#}z)-1D7XjQaQ4@dm=BT4O zvUPD-=)5_Yo>8afE`>^p-~%}K)`(W*W^pHwZ_)((F`>xJnTwa{tZrf zBFXO2tX>md4k0Dd09}YiC#=>qU2X9ZwicmDns(}tsFtRx+EnWUtB1dD&>URR^Upgp zh%>X@{}R-~OEk?i;#6lWyCR$x&+upy&t(vsd8wSLzGTF2?2MqLXu%dHk@*sT_5x^9vLY7r}5 zjMmbBS21@);>}ib6nXT@NPC*zaOaP>(KH?|f4Hr6kQ1=*skro~}c|O6E zB!BwDGqr8JeT0J!|}=bhDxGT2R~`(%?FK1dv9*k9LEgqs~WZsTdKn<*V|kn_)xw@ zJ^Ns9BM5Ge2-0CwhNSUu2+!^|-b$}-ql2@2om7q#oD$Y;8_|s4@1A0D&m4g%JFq7} zVQ=-M5wLaqWR?S{2cYc+D4_2u^Lop&!&#Kc1beMnJ1dkQ$nO=(`+7UF_1VryTdIKb zz(VlcDWCfqNpKQSvcg<{#~?&vX5LymwFD)5FJm>+kr`WQ`)?1izOI%{&&hYcypd$Suvg|!oFMcrJt7bid0l1Uxi31jk9UOH*=;9^qw zRsGNjYi>ndgq;Z(^#-nyuMVi%s0MXq$o6`&?aNl@Z^+i> zZ-npk*5q%>j5qZj$lt7TN0Z*he7~Nshi}Ob=;?UU+nhhBrxQtUQ~s7tfi*wd&9vCV z^g!H{npK-A9S2F5uVQ)UUe!*v9-GfS`8H6=vYqtJy^PHc8hal-Fq5;@2+c_DJQGS1 zoqe0VT@sa9yt7?e3lr;?clcYzL`rfPGpWXUd}vnMv7Qk3H~P~9v)Kdw#2jT6*4MqW zT9)-R6O?4x!CdgttaB%V)(%&eH6yjis|3fmnOrDBmA0&R*nLgw)~exxml(LqT|+FK zlK&!M~mPs?J$@B?>%-;HxrGVwaeUmHmLgY-f1#_&@u;SM0N9< zD_!->@L#e&-V0Y?BM}8eA~a1({icSMbFaWBk9i;XJ@z1dLp^h`(!UI)b8roFV*ds=X>ORV!eAmVpJ_ z%nfNExO#O{9W0N_)ln8vrx=D)6FR9g_Rtun$lEGEUX~w|P#4ROAo~m~Zy!nw)W@NZ zn3T0C5p;(@@}b|WEEIF`jtryxF#c^PQofRk0pv0kP)GP-)lLoT-s#x3HnK_iLpMzi ztdCrwWo;g<#U3tD4y6oLvi(Y!3Qh0)NTT@op%{JOcOWLk7f{n%@0&`*xPqFVP_ry) z#4!{3R9e!aQcj$L%+4O=929koZrbZsS<>X=2STuij4tju%>5oV}v; zMAic?ffrh+8g4##ZSnf?vG&R*M?SkE+V`$;@qHQ$e5f@aHnq|%;P7;2Z?C3UzTchS z@2S*E_l|x{W}zTuq3fwsyOM1lmP&;Y9urvGB^wkg>iW3ZcUZ*|*-KSuv?}xv3o3;k z#9KZZ3K0pw#flQT+&txLLurg7_XD|EboOOar7@06Pqg5(1P7%6>#(Y-*@5=D%rS!* zB)Ed|Rqm3-5yzcOh*l&sl6E-j9s!JGJ%RyAH(!syw?Sj9dJgrvCNP`4pE4b28hxqy zHdRgA;SQV(A-|h#>X-F&jRG)SZZl;FF+t_GROL6jZ)tsR1Le2G@~Zy??k3u4wgg}G z>ax|EJX4o;vu#nb%5#;PPb5=oStruMduLl!+ho37rN**ewk-^Va!Y!l?Ae4um{< z``@Hj$F6Agc34;JG{H}C45$ezG1jYv^bEPQ-|Fqmb}*;U9+81s(+c;((S#H`(Pv1I zJY=KCH1jbM904CD>Foz~(%kG(t!%^o^!@xqCStwFR=Wpc&4Knle&LpwAz62zosryg zpeK3?%C!pFH-goJ;DO*~ zkW3U^67m{gsBTdkJuHWY9#8YQ-5=%2u&55M6XG%Rx9VgQ_S`SA)!%A%np|iX#JgPB z%IFzlUH7@vwbM(;{*{L4340^4`GW^Eda2M1BH4|0x}vfz?<+0;8q?I6CyZ8` z9pj7QfB((P8)OVkxo8aAiWx(P%>*j9M07LMxb1M@tgu=0wQ)b0?H9{Zm_%Y{_+a$M z*aD+PTD8Mvbu*9X%L6j73Nz)jjPNn|9XN^>oz_)K_=7ndCVe&OMGFhYyEU)}l5S7i zN1}x#K-%mc4D)da+#(|i#T1D{g7u%$(TJkM{byt-lIc@ZNN790JtHJGqV7s}^j3-D zhJ+>;c?KFA&Q?j#A1=l6VQ~zjf?+_ zq8*su2EO)i7z*0U5o&@^liCff|7Su(qZ4!WuWhrN9vS7tqoL6tVQaG;6V@#VCe33v zwMMmtxOU{RwmHUh7SUI-&VmpEMDV$EwB+U zU0B3zrV=HMq8X)Y1~Pd9n>%*wEP3B1v>wu8$ezv+u&7^u|Kzce_ZICZ;w6v;q1a3k|WJA;$+IkxJ~nbdfeus*v2xh z@ro4=agBDwT3@{4@%C&(EaEAtseSoQ#e84>B_SiZh`2_h^S+qHgb&fF$40XSYQDif6DV?3vEE_6LQpn&G$Af&lQJ;^E=Qar=WwbR66){HYLOW^&<2}q_(1y%C+l!i zUiz5;ojgR6oW@Id7#H9v(&Ex@1D_BOJzrSWSN?E9xl4E{Rp=tA3O%FWq0j~U_O|52 z6xAU%9Vb=ZW2A@iG|v%mk{$^k=fcOACOz{_r3g7Mk`}CxHj#ls4 zC4;2bQ}%5S73asFD4j#W~nmsT=AhjfW0|K6yh^ISmZs?f2XU)=8}uUlU)+wY2ZY3lbrlmbHZ ziy@@yt}G%2L=?6W6LT7{x{?~+S60r}u)2iLy?Dm4x~S(s4VUeCA1Jo&0<3u?Hb-{gLQG*ZLm@a~e{K@AJlxAukjY}8;B3N={oF?E3{ z7W7@B2Fxq`gw|_qGfIEq);%u!w)NB*w5%Jr&;JpFRO|sBqgyOItzUCL%fH>f>%l|K zHJ$n`vIOh~%&Cqu0z9#w6Vm0cBi01hF0@C)KiGv=Du7))MzAm9ab8DyffNTNx5eXm7(-piH}Li}+ZS0Eih0saq#6}IZ3f1c z_MvP`Z!NXV=39Wb)~;gP3eYWVt6^Mu@uExvq%S0Srt}5^te~JjtmSg!*p*um_U2QN z&9kj}kH77}V=fYlhaG@dRMqP#&n%x2?$0E>9a<7us`{^1Bbuedl)_IeL74VeZM#DQFE5YgoKtCVfd}i4Cm2 zW2_zHId&adJ=5DoVH3$l4!6Du_}s^C#tmr#h*Wb=+7Zg~zv}FnF$5R7cz97wb`32m z!7j7|e4Kp#Og60g*-`WzPa&wY$Nm#&-n#U6H$X%yd%W4%ZV}UNYTgs~R24JD3Gw&t z?}RzoCTvc*_GVZ=(`r!iI)vk;*9cw@Jb5eqOV%H~iEm=*Z#28;%}+x%IijvD=f+=Jzvo z5_%8$Zgy9z)q7ob|9AJ)W#oSOfBgRM{pbJi^Dlq)GoR}ls-R3BZhe5NUYEV@aPs-S z43AjABeJ7L0Ba3(YYq&52b~VYq@>J3O6m^q}E^j~an8zfDdX(k94D%ix z!x!}y4YIDpKG*JO4CzlXTydS9-50)#wx)+fKns(EJdr-58~?I<=+^(PkN!ykH+;J= zN%x{xiD5~CisTQ?dp>ly{Xh!vH+64R@Gl4Wmkj;dEzY{^v59K>L*$zb(gm%7Y`ZdsQ)cisGoU`rP zJiOHZ{^+ouxW$)amZcr_a^B1fnD^glj~w(LXRMN#)89QCLJKK&wc+#g)#vMdpVhPH ztbZ>Ib^kVeJXCnza@ofkM$cHzi$kSDkPHhk=QFWsf_ceuUK#5C9l_L_%j7ejb1CFA zvzfk}uaLX21i7;j{FxGbFUz4KIiGtSjIepudyHZFw(j?6ZWUYy=}*~O23pHvWaf|{ zcE%U6q%p)>tqlBDKQm+xrSRYDNwy_hW$;(Qt+wQBQE6wZMDv`7(mm>R+`<1PB z5A%>LRmaB(S6rKQEw{@*&$6P>%A3SY6?5a7Hm(gh5A)%>P7JRpCphX^ZqJQqxn#K*l+JeD=s;7Ko~y*GmSo5CuC-N@m*XmA|Hg28yn zVZ6D5aWN$t02m8tZyzw;9AUtm4n#O}qXC}>9o7Ydb#&CY=vyjS&!ogE0P8~9dk|P} ziLfG^V+O}zoHH0F9LC!!80XXcA;CD8_6`8!Z4n0eygkA>8S#0C!#ZQI<{j3-3f9>) zxBfhn_TB-kgArDQbGnAlyumo@Fp3Js=`=qqeCE^M+ksI;7~u2H25sc$$@14N-x(EY&-W%au ziuioJ<8#ztU2$0Nt6&{V^Y;tZ(X{vVzV~|A1;+BV&ELW|?0TEb|{Q%lrqZCr0+s zt$#G4{6a+egN`x>0s;O-NBNIcC?jruNH}mi@CO0)$10SuM~viuJi@zK>(=WAYtB*q za0LsI^-l=a^`!Slf%V}E7F!5uBlvwYNU*hnf3JG+KM7m(J-ZaVmgFDtQG7n>{V7Q( ze^UE)lk6jiC4VY!A%I|Hlmv@VZBf1~o!*(jkdtBNoqTKSC8X9}$gGG4<}Zs;`Zd*?=Fc!7W*XG29?FKm}Jv@;@EB;K*2vR0n%&!!d8epQ*awxg`Iny5MTk z`)0b}&%`dE>3>%6u8rh>F2Xx*@c8oQeTWE}rDZ1_=Eo|S&nEd_5X>t{@6QACV-aSA zbE<~dC4+I=Vf@7k#^ogc7Qwib^!_X`{^DX7XCf|-IxY(a=bXd&)(Q?gK0y)=ob(9|;5h=VZP|ZqODoI|?Qc06`7Tv@Cdu$LIu>M> z6D8ZDrBNg$Gk~55oU(YYb~d9IfnrL>ff3GnQ49f?P|R-a^O8LJbl5&kJtyM=`EAy< zD61p+Uj{cKtc1q=Nblnna;KC06ZSUgeH)$h@v?u}T7D$|B*m^FIe?#w5Y|<;oEyDQ z9%x@P#rFJI{_U)+-+E3Gq*V`cLMx@{B4m#XWtpjT>E(rdHb|7xNr@~$C@0(_xVT-zcLidJ5iNXI9~ zwHJZv?@>psE{=Zj_>hoYEv>wU?D6q@qjCvdNTC7IdkoQyUUC+9pwEA(0X5v8g@m+o zX{{*g)5IZaZ-<(bqQ>L#m#Vx`O5*Dc!Ht^p(%81C%6!J>$#WcqyRl%lNbA^GF#Y=E zC|MU6Cv~1igr0ICXk$DZjtfs{pKT-=FXk^lm%eXm829B(0@Su>$c8C; z*=ou&!);4hpw<~%!aA{5n^@E|G~|0}9)Zg-Mm|HMqGJi3vq*Vs*j`7_4FV5XyV`!- zZ0Xl!YUVxlzPe+*W@GR`qK(7ApzFYu>(lO3lJ5|y5$@MRp^H`qq*z>l>Si?iV3=62 zND4l9NJeR-m-?1?jH_ygZPyU+ur*D30!R4h0j^0kwTQW}c~1c8ZV-qB>3ttvp&m!Q z)BE740orG^#1LRwa7=rKNr_I~37|3vSop69lT&0>P0r$Id@mDz^rW^{|Eo0Qn00%hWY9s1x3 zZ`&1K0?iG|c2f(o+jPEETu46&!tca=`eh6a)BzLHn(ot1hd5}ZM~ z_b0sb<|Jv$J8z6z^dN79^5LC#o(=(JyZs&0kw^@E?c@Dew|J)j7JVXmmnHn>v}6a( ziEMjlOxr@L_;!6-)<+}UOatZR6iSY_Mjx1J*meTw7d9`-}|jGv5ws`FW^lU>#*!enXASxlE5!!0>5Yz zRGrqjy9Q~SxVsrRSUiop%Oq0aCrd^@chBImyVB>Z(>AGhhYFZxZ4#I)=gV2OO4x6* zvZAnSEK*%)E3zzo+uKDe#_X79jC7~I|CihE!-KF3mHY7?OY-~qu~D+9Tjes%Y|mY5 z^ZL+cIUmF$+u5!>q0Q@UMYHPm(9E6$iUHFr4b#gqbJ?B37L?m8n?nWegOl2pNLyre zO}+ENGNZsq&;rJ33w3LP6CaKJ^4qkSKvi~q} zZO~fE1j=6T^yrdAy^|q#W`~&{NX`5J8DoBc9`+-ld-|MGX4X}ue|;!t0?=64bU8_TRJ7-C-+WF(sKtk#1cW$`t&x zn6x)6W`VhCOsoIscFqxHWHTsnWh)XOsrqt(nHudUWS)_bi2 z532M-4zMcy5YjQFAMDLw4Jx_0(XKHuRVMMHm1j=0Fxt2d_tsW$9>7TfoPE748_(88 zEXW$xa=E=b8H2TpwMhJOyH9;_i*o&gOi|J%^;xoz2FrxoxK~w#bOx0|=CRXCGxyX= zI6tM|i0dbnLWt(2We0@3v?A1FN^^n}m9iMjDTN8&B$5H1!BG|WW?{J2sFOh5B#0p7 z;9yX2v`<>gw>}Q*`lZ9VUW28sLvN((cB|{whaEb(mOiZj>tlrJOx2AaBvzGfe5ALV z!HjM)t@kaX2fLp#%^7Eg)BF|o+U>e}wCZG`vk_d+($?D}Jp99+>Vt)mhmqapBdh4d z^?_z;>e2i2OepE}jPa5}GC?(awHY$n6s+|YBISApUcI>9L(0OLP|n^MS&hC%l{L^t z^QeLaL<_LQ?fg@IV`LR&ppjOTf#qT4-mZBQ%WtqsTO#Tkxy@?BxvTNZ(Z911c^#fLXYw44E{%OyOMmk$K1iSa#r3mf|7l( zYlEN}VN#$8**Azq)j;4mA&3GsRGMf_Wx%YdbjVy=j-^`-TwgQUG1W9ePxuHaBs`*fd#E<_ zJ|+qaeJ~Dmo4tRt8LiJaTAzM3(3$`(*!{ArNymAK$mU*_g&L(+Qd;%8k~nN`AfVt( zo}L^TF?nOe{wD{M$r`rvwU=A)jOaON;&q7u7O816#+sj(5`(7ZVh~We_&@`QH4dWN z4}!U{7=+At@Zkm!n;pdZeh|>vVh{-+vPKX)97NU+0+;b(5Sl0vam*MbIqA1t+uhI6Y*uOKps*8XX@@hZqcvrI@rVsRC`&r)32wwQ5py6(nS| z7@3-jmrNbNDaR;=`5PITeAL127r%(xKa1p~@C;kz(8TQ#jB86R3&Ay8b>Lx=wNfo@ z=^>Fr3~E#i&iBfD2ol-169}mETXASBo!y^4qx4Zs3QGSXp>ayTg`+o={#jN7r5|H8 zQ2G~G4V34#xLO8*2*Na-Ji2`T+!Fd?NMf(a@8;QsWb z3DQUQr#F>;6RV2SZ$v{;`VD9(O8*ENiqiM)PoG!%J~S1j@86$3r}XR5RFr-lnu^l* z>`$*KeJ7fV(s%7oFDZRD?N|CR?N?gRex-+LztXqUex(O#ztTgrU+Ft&ztXqSex(o5 zex(O!ztXqRex+}v{YoFC{Yu|V`<3pe{Yv-Iex+}s{Yu#^{1QP?*&AuU(m%)eDgD#f zO_cs|48BTVcc87Hwd{TZL6tJ*3Q)_YJU}gb0}Gyh|7Gx3`f>1A`U&t?`fcD(T3qQc zYa)#m#~^S{)be1=>;v?j#)NIlS3~-o(vQ&BO8*pnuk=qc7E1pTW22OS^czYaW9*c& zQ|PABPaa5LQW`|AOW|4iz!8ktGLxKFigo9lQtUH|+S0)$XF_^fDI1{Ym9mTDR7g)M zWsCF)QVjj$V)sEIhTMyxPZlSmxS;AUO=7Yq<3&O;GA=pjT7X+N#@;jwe4{X-zdlQR z@*KC*n$NJ3VC#0CwpmvhKKrIZ;(| zUU~3o3{)8z;qgL}PyhDOk z9GR#ZV!b$$nNHH~+8>{#}j zgFaOQecD0aXQ1yB==-w!nnB;U>^Cs)@prFRai&(qIj;gUp}xD6a&L1L_ez2p_{~9| zuYrEXLH7)FPoR5QuNkxj!wmceW|)BTtzN}Kt%^%t1<{fEt}3=>TbrxkP{n1wIq1tZ z(9b&Py$0GG9(%DgH4f|EPPWW9Fx$zWVlN@itFD)#bCV z4czHQ4dI)P@Mc4Jv$}3Gifkjon+JWvJjyqE;!=m>S7ireX_Fwmk`0iv-OnrfD_;$o zTlm{1REy_4Lndy@{~9U;=(d3GOEr9tctJVgfG2#Zv7tB&mBU7xzX5uSdM@K{+KZ)mv)%8po?8K$ zI?!o@qk4ULR5t`vPt;I7mjJ(+I80tqRN@%VQ#UiP|<_ z!{m%(LZq8OY52wn=58dnu|eO!gyXU}p^LvwUd7p373aN*O;&}S^|LA4)Lg};4i1y1 zZ&a}&c*laLcQy; zuV(9ry1?MLw5{MKNM7f~j$$)}hdO6z_JZ7iYh}9#F2)}?o3WkwJ`{CKJh&Ed*I|Cd zvxvVQe(MD z2ZeE&s}fs6W6vx-ddhng>jlJmVp8qI=|IfJZ1=iCE0pI!k-DcQKmH?$UqEb?^e$s(<);CARqay zW!m*t_LefO9TK(&$f9IbwN$2cW2O082TI>FHbfQ&-P?{nu}xi zUgjIcck#E|v2T!RU%A-qpc`abIcBL|*zd}AHDkYP*>4mdsQ(nf6{WhOPYVbW9p# z+MPyd7Ok_B%@K{8<81}1!?=a6Z)5yOI8CI~n+nRX>;ZsRX=RBs{E zW&za(nbrdY&C5z<4Sf!(+d81p^bJ(EsnfKPa@sMug-mPbw9&Q(nbtk|c2xIx(@oj3 z@o>CZ^EWUV7bY9{8xtn1-ZjB9$h5CqYTThy}UU z^I6V@=HLk+@|8nGvra>MX;X;zqE6&mMa!bas)JT9$u7Bewe|?%jzJ7XtVFa$#znM6 z%tf?C%tf?C%tf?C%tf?C%tdBJx<%4Ov_;Y-L=fo~Nf*(UozsZ6h`ETih=W~8vzum3 zu0`qvX{5jr}N~*Sbhc_p)=(W!{TXvaXA_P>3S83$B59dGyp5BpN(kG7C*In!!w&qo2s}we z(*3IvfEg?ax~YgJ3c7Y^3QcV8ImybZ>ZT?hh2*UHZYfx|=xp;P1tp^FZ11W@C8F$X z&oijA!Q|{Dex@Z|{&YN!hTyJm1k+)=xTOv5+3-`m`{bZ5!Sej_%dHP#cOO1HdbGz* zU76A45X(j$8qkg25Bw{m{J)jHp)Oj`Vd|t8w6Bt0^Ny9KqA11!ltYOhH(f+qI-$&r zBgeVg4`n2p1{ioB$KVf0rPmk;(TP|3qX9&Wb=2V}20{oZ-e2jdk*vKbH{9k&@Iasv zC7Im)6rKyb#GG&%V)Dl@xHrkez&6TYo4&bpR zIfTwNq2uyP_o}Gh=;!$|uVtyBg8`w0c(TmZU*~+8+sZZWD>liZ5b{c>y)edSzF6Ckulna{*205j53+(-Y>auTQT3Ah1lsNMaN@rkC* zCscj1KKV|>$*0QXSqzqz?yp76iqrM{^Y#4lP!?`=>HZt55+LXN{2(#UzHwn0ig=1^ z8J@or@QmCkauyrpXX^D{=m)llZyB&(4ZtFwssn?rBU%?DByhjBdUgjiKF1-cYX7O| z=sX!6o$k7Xxmi@y$&uRmBk<$mc{`2dxqrAYSDru8YLy2Js;`?ospToLKphRf=z$(o z^Oyn&Z>c<~wkZBpMKHk5i7Ot7$GXVXYDXCTEwu;lvoa@miM7+@_|JkNUU{{{;wM91F_`CIm+aSab|y1fyd z!8pQdZ!sRdQT^n@njU8=5;xPs2I=@h@OLx@cL)bwet zx*>dh1+OGFbHQo8f~C}|B_NX5S(2>)Bjp;_DQm+ScyMAwMoZF+>B*vTdWctt+|=+(~oh^JON0FP6(Y;u-RC_w9%PN-Ivr5ETL?8`v#K)5RKm2+F8P| zV+v^mosF$Sz}~GBDjg`tQ-A=%>Wc199r^#LdlvvXifVs&dfxlYheSm|MTv-j2ok&~AgIxc z%I8JR6;$r^k?(h^s(X57dUkpzfqVaf-k$D0RdwprsZ*y;ojQelh!Sp25Hu3$_V7Lc z;}UBS`SieUU4N2lj!c#ous#3_DeG~hvQ__+5nzfPnv4vcJva~Lx4^s4No+t(?*tJj z1XYm0z>@SEM$hW5gG_gI*8z;Ogo__H>=XcInO)nF8?a+sMEegN{Ger1`am^3iptz0 zQXmiMSf@M5vVrS{I7k6`k!O=$RT(`S^hJ2#9u`ZDE2K_F(3Lv(a<^b0kCqE@K4x5i zM+b31rk5B9A(p5N{DRtsPj1@uNP>qs0+8h3M1$VM7$3LO5WE?!1?_JAB8MaMz}B}@ zWDYP!Mk+6LPzR*LY+ae%j2Avi@(bAG-B2G~wtZkQh6kk&`jJD>M>{M zzt|yV-gXg!3HdTdU)0x$s`)Ylrvs=n4}f(7w#3%le3??#DA%5+K_jm?A@VIF1?1K)B4vQ^Ve)=7t z-`HGAzsC_Ho&p_7Saw3C&>`HYg_$N^APkySiG&xtWNdQ;Eb`Y+B=X@G_xVSRS4<<3 zpX{Pap>^@FSDrc-g?gw^3Zu$Sm-A80Itdf(W5jQ8467~fr!n#2C5Zy& zfEJ!XOkv~!A=umJrP5^_+6j80{-`x05JD*dKg5NXXbf0D$5DEzx~y2p4HOH3iqfz( zMU>{o+!du^Uk4G=C*x=pj5G7(o<6TUwZ})k{yvB$K+}YkY|nU4fj{v<6wKhK(pG$m zI?YQa$pvoR&bak8A-Pb|%=I;IfLP{LinkTeVSHFAqPBtbB=N@t;R4xfGqf^9$SGqD|(N}0G^Ne#}R7dlU6y%FMa1&D}b*!)R_ zn39uRB)-E9Ldenepxfs}%ta?+FS;Njj4+<&5ac8~QPw`)nU!^XC&o16kr-sB?$Jp^&<2fF9H(< zJL*Fdb#e*IMl7^V4+Iceg+(dql{A!=l6rZhqTZSyM&L~Jiyj*N zSW9^?=>z{$FevY3K}>UH38lGNbRyKxh5Fv>=N;k+Fgh2NZ5vKIf3FQQYb5}@+qBPdD zuo=Nu&x}0FHW*+68tNRu%N@674mvIh`j!vqKu&1vV zjMmHKzr@BQz%K$_I%uT9*n=AYI-zP||G5{kr*A3)>yW1m1*hry8;o-hv+}fnp})=R zlc?xf>@Y*{GcuR?>OO5FWQ>ZyxzwiE#c$|ubdVwBCOSVh1d<#@N@UwdqXR1zYxu;D zAPX=eC@4aUNSKrUHiheNm=8#VvFwKa29HQXe*-=z5?Z7^orv$!5c72CB^{2P!?%cU z7^v6bKniv%gl7Dtbx%R!Ja3DZe-!)aAzE)bkX zNFsw7giuDr@w}XnLn!)jdWXo3IPum=UX)kTotOn1V=C;GYn}1NwMP8J7@Tyyi&7?> ztB6GK!j<^_dT}4cS0>xAlglLB2nSNRrvsWD8CCs8qXb@({DQ~qh4>A})zyH46hOC- zrz@r4l(|9dpFxp$3D*ooo`IJ$=LX@@`YtXqvGPtLbGFF+3nV?5lb1f)LU^ANazNct#Ki6P|r+ac_=1+YrwII3+?n z!-77Qh)w5+bMZW1?qlK^ssfF?@cXh9&wbRD(N6&Y6Z~8*o~My`o+|e-@jRc2XZFfL zjYe}tEAfmBh>Rd2D5*0meh3e4uQ)k3DhOGj~3J?<@4mkJ1Qn*lTVf zhQ{MT2#mA%Yj@^JcqU8wC(>8}K}UQ*HI9Rqp$;AENAY`CS zEK7Z8d(gWh=vx-piRX?jLEny`f7uZ`Ne0P|EeJp?@7xmf?|`xa2ncgjv}dR6*b$WY z$7Khl9V}G`rNUHK_U@3;v6oHVX~1luBK(vA4WpC+KFK~k_iB+wI@zm#tfn*yHm9UQ zsVs&OVFsme3v#F8@f&4Gr~!WN==!V|Vc4A~vw@fm9@OiDP8M+!uN|0}us+8|NC=J$ zf?fc^;#@D2#gLu9LHV#$=A*r8fDj491XMlUO9ui6@?*X9ow4MigtnsctEY7#WCC80 zCZr$61*oUl%gG$?MQ48yHq7G24n3{Ql~Du7=!w_lEX6S4M;~As{#SGe<{QP%24Q(+ zM2JO4<{zxCgeA@%;UqN9OaM`x)oc;g0cop-K#-5U(TRDz0b8XL@?~PvkwJ)7Rx*2o zvJf*#Agoh|)#HUK7=ZR$1HD`W#G@+I(fK|Bf=u?ZpGGi72OZ&CS2Um=swUteYD_Kp zs8xb})dbJ7pCU9P+L%2ug8{1(FHdhkj!qrAJlGDKa0trKq3JqhfF8w7K!2mzqC`Tl z>(_3{ulElOgIFq}vcQ?N1{TB?eyB>YgCaI6r?^YhkP3F`G%;G*Md5yeUV?|M0&NDE zDofEiWo_HL=;uDrdMd|1f>UUPeHgzw)liG2v${nZI6dP}@Zzaq7}y_jk}w zcrp_@ezAC#9p((s$6A5xGL>;-Lm8Y`IexHbS1}+V3_(vX^aT~rjA7st7=UmXRSinY z7T-C4(JWf9O5iE77cru9uA-uKyER^;9sS|}VKOb?6E~21SGA^QgoFHiQZHVFhI$zQ!gm>eTDR2ef zjE6S}jW4leuW|vw01&Gfs86_{0qJIxqg&Rg1O#Fx2|v!9I*sKF$SwogkT`(e?qbka za%i@g!H~I}yh|{$WlRYsf+v9>B$5jVjtsB`G!4wk6O?}%{W}L+-??+ekug~nU z2wDyd9IUJEafvyl?CAQOmrpD4ag}~*Bp(V0AHA-;;0tvC-su_v*69FzWM~Aa=m5Oy zH3E!FIt1^2jSz?9_{-f!DZCRkma@ly&;?*UG43}Ys&ccIT!;+FChHCnTFhJuG0!e1 zv=_7>;}xUZ;%qE~dCd7`_86~`_Rd;Pf7&RDOMG)tyzTZI5HA`MM$ev*LC{u0W-muZ zUj@aGWR$8y^ZZ^m^5ea?vBcL5h<`Mc$n)5aGAW&TG_NfMVNL}^bqqH{WF9rpLmeK{ ziNZTSs}N3UAfc$Qfnp3aQkXLWj3`NnDg9&DeY&Wj{>!k{H}|5%Yh6N~2)FJUdGcqH zXAp1TF-#!M(fLs0>y19ImqbY4sON_>HVCDf{J3asj0Z);%9LvAvjd2An=1LO$AP!b%A4kGhy`39eK)fX}r)^MW42 z3ePb4I5YQqh+;6Dk&WP3=RF>p49PfLmqC4dfY>%C=^8J7vui4hYV5J za^`^pz zsp*u9GWXoEcih1yzBDU$Z#2CC7zR(ct^pC`Maw3<0{`&PGnP&WMJmNjYYZ4=^5rnT}2@+=q@zYpRda9TxmYspEp*@ONG<(}Cack}dskro(E+9vn<$#P}pO zbiqn?4~T(nRL(wZ*~#dK08Xd=HyyTKN^s?O&~{V>Y@b``xSXC6R3VzV>v4pDdpGp} zGFD8(+dFACPwR;E>NQVr2HjuvaB3X@ZZiVcklMpP_sw9`I^YR<;3!Ea+)2y05f63* zX&H=lvJCN0>o;sQ1i>7M8epYmI{>#28`iNlMD{kC&*Lab~)bTcHzraA;~36dgl2FHS$b$Az-VSskktKGe&ZIc}+A9?uDX!AeyJMp(1@ zW&qrW^&dVl;>+OV?-8trtoF7q7ATQo{V5P@?@Mv$Ir5<1PqraT2d2b5bZKB*ib)lV?f|E4_w70 z2Tgt;H~>aeg9jtPX3#{a28%EO=#F+A3riJ@q)`;6)1LO#+mYB)G8 z`rIwJfZ9kR#6V5nuoH6uGs{ADAaEXA*9q(*1c><7jDR-7`GD*DW(ZzD(g067LnO7{ z$%ugiE2!xF)ZG6oO$n7mwrnG#_Glx$0Y9fV!wj7?8Av?QM*Jh_u!yz{y4h3`>>I)q zRqmlD5|+Gc_9X|5nbv$6`bapva!r%~r7 zXKvaloGE?n7QX8z%?tl(>-KtWAChOt>%)F~fw91D~eJ23%rT6J5rR={@*k)LwSX&3Hfp z$J|B|c3r?TM4EY6Knq;N?E4U}_`w8u&~eu?{JQf4GR_crdk}fhxclEEhfx&CDLmdl zBCh&)89jFGb6PTxSuj8F2++%r%bxv?ZnW%PCe=lgT~lo`(Xt`YvX8Seg#0#jbOE;4 zA>cmzUBX3V*B~?uM)^6*4ii~olz(d|b(YBO&kPLK3j({P0c?Q)dydt?$H8u`f(f?6 zAXyzW99_RNjpfx*Lo^cx(LN|)XplZ=-+fSvNgBQSVXBp6L17Y)nDL^*vS+CSU6c*K zPFUe-SVv6fVKyBU0<(Hl@@)*utEq|SfG1}0rKfdJOquq!*x^TzWMm_}NNIF;(|9%` zbJb3c3nc=YreWAkBf2p3R;bdx-=7U4F=o!gii z(dL2f&9D(-M;~|YV`T_AaYusysM8v!IamkByb411S|(*}Q1vrEpni9BdsO}Mqm&pE z4iaRh@w08JUiK7aNGe#p6lmVYm>(KqznS933CldX18~WX~ zKSh!7z(qCqYMOSV~n}4*{j=sve-sI#DIUN}oeUD`z zblrK+WO)%;0Dif&A{SuLppACepmJ_Pl$-P@@)jgzoas>vIbDyk;L;jB%GGrilzX8PA}1ua(D7kq%}5wd43!=)@c3zJ zSW@ZvqG~#VHKr6q$e^^^St9PG~>W{(SV#SlgIo+5wrVf%F{&5+8FL{{~=+1 zR3qkK3a8%YlT|*@s8F}ix|~a!4PMp~%LdZlpp~i&S^MovEVHTPvp=Ks!c_9*YPaEd zbEKbOWeAwBRAH=^X+9ZKPQCa`ST7|K5`)V;|xmo7FkHizsR)k}$QNL!&g+TlTeK`0Ln>35zs&UxB;S`*2 zKgSRfCO(tuYUb@4tL;}Cc)M=cu)$vT85@>Lj^~vmxA>s2cLLtFp-uhiN7S3;Ni~5% z{wtp_iGq6?NKrQxIl*LzqQM~Wbs7a=Lh7aNWgZqV7GOxFKeGb&5>Qt+0ASC6+Sxrk(D93(>JRy*4@PR`oPOfPyda+KG2czS+5W5f5U9fR+hk9=u8ns880)faTBmlh#6lUqzm*uVA$aW%wpNPn zo(Tl64N}~}hLOoldyOveT{h@Pq+9UV0DArHQCh~!1xf=LTRp>MA>NGeQd>o zc09MC!vB^f7FOYJ9|lg(%sLbmV|)VRzYR(CJtq3vT)eR`1+#+Zc z^nI3B4+Wq}M6df#n;LsS3`_Zou}RABo?`SQG=LY>rWZBV`Q zPz(MwbqmVyO{`>Iw{UYba6vIPP{dRkI2o8ra~NX^KeTu?Ot7KHIua> zJH(sg#oDwEKl&hF*UYd$~46ljsE{# z1}D~O8jXWS|9^_Xf!m03jc|ys$6MkT436S1Shg%;|^=EiAjH0tul-dB6Ia8LlxqU2r|bz?zC0U2r|Yz{G~yMkaVvFZCA;PRRVmQsKa6 zM>1sY)ZzZg@)Kjy59=ieMa$&*wb;n33)XADOGA=qi^gi~*R#8c!HH^ozMc(;r)|bP5n!$9c?lX{UCqmSAo;}`v>C{17tkFHIWhf01a#U3gpKwHj$=%}4tW>Q8#+)n zn`i#WTu~MV>G01p|4&r($v2>)HjFQivL%tjJfRl-OtQj zM;(ff!T1E*Hf*%j1I)*&O>edqGEtuj{W~i|=nDH9L}Hx*xpiBm4q>K#;Lymm{pU17 zO;XwA!#-1X|G?(r%{<@uNb_d~BP@AOwIY`t2Ag>(40i1V>m?Id9iCz3$+D++cL&cn8zTo6QA;uESs&bwF!9!Nvl` zEo=!k*xZ-|e2Czf8u4gnrdS-A*C*201nhAG^QM`=q1Zlq*l=>#_c(Ri*VhrirYF_! z87iXPO`K*uaM@TjxQ^}eYI9+#X5-zDTgjU9>o(qD%bkzH2}7N#i2#KO2HR72<;x(u z*i(1lKlR@Y%YJrt*8Po#8|JgP^j)EvycGDu0 zuSbdGYY1b{0&v+ls)R5GglXnZLNV}H!b%`t`^pA$+|3$pa=wt0Wsb@1Pp~1KzVHm{ zotd0_nSu15NwLQ6uzK5;HLFfA+C0TFnxJ3VA8l;Z`M+6dZEj2YBqLa(>B_p!Hn8~- z3rN)Fp5$gS#TuIlT3|1u$pp)q_FG_rQVuZUO|U)9eknh*TrBwk}KHo?mittxYpX*ow7Oobp3b@gt06Mn3zJR79d8-2Mv?zd%+^R32>1fcZ zfcq^9z$n>Jz~cm$vDlN+s(=?73qbbO7>kk}eee6MfhL$fPBfdDoYb;0e`v}scKpnn zVbdouqU#o|W_p^qP(i6IIS+WSY2c9O*}x2p6i=)2*gNTU+@1Ay5}Tveu@vq1d7$WlZsB z4ou)fbA!Jb0@Z<7|5Y83dpq?p*xw;W;hI6RsZP>L!d?4p<0#W%Awrv~ovjMkWl@0N zqJW<>t;%9n+^T>*76n-85lvOuRt4;}D1hq`wTep9`9Z4!_E{8QrAmB*sf2B)68kL* zuu>%+V+FJ^P9Cr*z)F?)87shI3e}24R1E1DmFO>wg8Cro!;> zW3#gZRxud8c;HRr?B|8fafuz;N1w#O&-^SUCz-qOmd+$%C*LBrrkgCI6v)yz6V(kR z8TKwRd$A}puw7-U2Vv^R8}f3ml!E-oK5-T;W#q1&ss+q$Fd+CzvStXVE&QbDh0Ya0 zvarE_iXy35of56*O0`|hfZ;?{3k?5FQ5PMUBRQ<&2yaXafK2r$U*x?;Kio#j%*UZd z`go3in5UTmnrx;kAC*K zC&~;^A}fkc$Ff3#p5~=j6ZDu8bWfOoWlJ>c)>HR%b=yorRbm+SbavygdxIpM-F|yz zb{HA>yP+)^8TfI2;KVdG&<6Q+D%Em3r?Ad#g7Q@mB;%))q3Q@Wg%hCA9z9-|P7bAD zWW*YliuK}w72D&X;}DI?dAc#A8&7VSz?+r`Fu?&hOTpj(vYG4LNWa1KeNaR6e%AHq zRQsy2Rga)|l&d31e|l${dq;Oq@07sQY13yMVW~c|wQ!3iyjG+7)0D7IFFC?OyM3|U z0$#HyfQ~=}C2J-Cm;ZwF5(`I_RyEj73F~Sw-J*bdSOFIH$W{eBZczZFR6`A3Uz`~N$stxvA6wqr? zz=3vauzi0ciFzywxSrsw4k2n)gWVPd;3U8X65YcJuvi{xRlr`00x&i-6tIsK&<2~n zXi-3?MFFq2TZ8dmHMW7(RQ_6mvkG?IibT6D3b2~W-@^*9aNlTEz_S(wSWV?$Vg*>l z%4k)<_Fp%W$Z9HoJ;7N8F>h7CJr)I6P30eA1z4P=*Q$U676n*M<;VYnYS50bql9(B z&T1-u8!MoV0p}r$0>o6lW?|`RR)B@0OG^@26ds9iQ=f`BnCM;VHxLwA+7DS>Mx`bzNQm zEH%^Gs=8R_NIKfJ7N4g6hplRRt%XWtWvbc5bS{fSQCbzSr?G&#Iv=o6XPTqd*0^cN z-&P2{+PDlQ+P78--2MVJu^W@jx@ZKg3b>pSPRP;s_?oqHy(V65_1+Fc1GiGT?_~Y2 zjg{m38~Yz7ZjD3rn5}BSDPbI$3(ZY*B#KIP?!z1DsSo*#frzrlCRE zIr4JdwBdS!%XlVVz>O9Kh;gWfM0c_REMoq)V##|f3a}c79%2Pp#Gq|ez&?uttW3)L zSpjXZ=>dxZEXN@xnA>RN%UjCkM_g8W9;-c;_f;2W%Vd@!4>#}!%u;UZxUA2*OoP)6 zOuSflCbuH>u14`fwgmW>m~!9KZtnM4RVy7IWd*b`HF(;h0M?Lo1-!@#Xk+>0 z)y4wq2Ao?vT|HZ+51MzZyBCb(pV_tut4DVbeHq(kWe=QLVGTipl4;QkUqih1Wn3GB z{)}%Jk8tb>c4VZD@8IT1K;mhA5Ns&>PoEo<0Du7u2i@bJ^Q<1YBK8rBx+?h8yRt{h zY{ipr1oqvv8)?IpA0tx6ogo--P#@$mOu(`~@cHcwNp3EtP*|`VyW#2safe@imgzy5 zLkZ2!Ovn1b9h9K2OZLm2p}uZr)A44uvZLY2w9|vA8hyPPN8c1?h3=83tY74CWE?A4 zeY<4rY?NJcuRJB=t_%gfK$+bd3|tiSuRqq|ST?@x((RXDyKC?G*2{uZ**b5=&mwvN zZ`1+E`)ThOmELKYW)&PZ$?nx9Prc+;kW==rCrHUaLhhm!WK%f|tPlKxtrqL$KxQ_Y zfILE_%qB`IcdbX6GJ8dvF}9Y0I9iZiqH_APAJ3>jk`l^({;Nx#B72tu-6_-naEpV= z0kfLiO=kr=P+G8OLeh8B%oujEs^jq52^0)*Z`ymoL+)EnOy0U3Tza|NQ&k z{`AWS4m!4PNz&<9oYsC?BEZSzjI2J+b^+N3Qt?`nt8IX&ftP{1s6FR^eYc(l7IgG= z;A`N!2x_#4~C%aBY z#ff!%I@6$8yZZd7Y5;8nT+EGXVjdYlNLw6rG`4?j&Rz^O5OQ7xD&6 zOET;2J*c!7ZAYehxdUPW{B5u+lQY$s*KLxM#3U0N(p|Fit`7!X%jC;%MYUbGqF4E_ z`XWmt+MEzjo8Y8P6oSBXb8+|I2M-?f!7CeL!2^;}m&(BF`oRNxiSMMq9VByU*$Z5W zFXVxOtcZniFddd}WDgv^smD=2bwWUpv?XCW7f4SrcokKA!R>Tzb${?pBw%ULYAEPh zzMxHyaoIC>K7a2k^vaJ{I2Xu)6)DGQ3FjFo z8Xe7*L7a<>J8%niHV^@80_4DsFL2(3oaP_!hS;Dcjl@5}QJet%lgJiZ{(*;`oR4++ z`+&}|u36v@SKN7WpNF1OeO|iB=-9XgAj@5}5-3ROL+AD^OMPg2(6b}xT^87hXaAO< zcSq2-?1-H*bQ;;QCFoct@7xmf?Fjk_0zDY??%63jb_6B2TSFrR1kr(X9u

-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 { - pub accounts: HashSet<&'a str>, - pub partial_accounts: HashSet<(Option<&'a str>, Option<&'a str>)>, -} - -impl<'a> NearFilter<'a> { - pub fn matches(&self, account: &str) -> bool { - let partial_match = self.partial_accounts.iter().any(|partial| match partial { - (Some(prefix), Some(suffix)) => { - account.starts_with(prefix) && account.ends_with(suffix) - } - (Some(prefix), None) => account.starts_with(prefix), - (None, Some(suffix)) => account.ends_with(suffix), - (None, None) => unreachable!(), - }); - - if !self.accounts.contains(&account) && !partial_match { - return false; - } - - true - } -} - -impl<'a> TryFrom<&'a str> for NearFilter<'a> { - type Error = anyhow::Error; - - fn try_from(params: &'a str) -> Result { - let mut accounts: HashSet<&str> = HashSet::default(); - let mut partial_accounts: HashSet<(Option<&str>, Option<&str>)> = HashSet::default(); - let mut lines = params.lines(); - let mut header = lines - .next() - .ok_or(anyhow!("header line not present"))? - .split(","); - let accs_len: usize = header - .next() - .ok_or(anyhow!("header didn't have the expected format"))? - .parse() - .map_err(|_| anyhow!("accounts len is supposed to be a usize"))?; - let partials_len: usize = header - .next() - .ok_or(anyhow!("header didn't contain patials len"))? - .parse() - .map_err(|_| anyhow!("partials len is supposed to be a usize"))?; - - let accs_line = lines.next(); - if accs_len != 0 { - accounts.extend( - accs_line - .ok_or(anyhow!("full matches line not found"))? - .split(","), - ); - } - - if partials_len != 0 { - partial_accounts.extend(lines.take(partials_len).map(|line| { - let mut parts = line.split(","); - let start = match parts.next() { - Some(x) if x.is_empty() => None, - x => x, - }; - let end = match parts.next() { - Some(x) if x.is_empty() => None, - x => x, - }; - (start, end) - })); - } - - Ok(NearFilter { - accounts, - partial_accounts, - }) - } -} diff --git a/tests/src/fixture/mod.rs b/tests/src/fixture/mod.rs index cfa6c41b019..06717fcad75 100644 --- a/tests/src/fixture/mod.rs +++ b/tests/src/fixture/mod.rs @@ -46,7 +46,6 @@ use graph::prelude::{ SubgraphName, SubgraphRegistrar, SubgraphStore as _, SubgraphVersionSwitchingMode, TriggerProcessor, }; -use graph::schema::InputSchema; use graph_chain_ethereum::chain::RuntimeAdapterBuilder; use graph_chain_ethereum::network::EthereumNetworkAdapters; use graph_chain_ethereum::Chain; @@ -106,7 +105,6 @@ impl CommonChainConfig { false, SubgraphLimit::Unlimited, Arc::new(EndpointMetrics::mock()), - false, ))]); Self { @@ -748,18 +746,6 @@ impl BlockStreamBuilder for MutexBlockStreamBuilder { .await } - async fn build_substreams( - &self, - _chain: &C, - _schema: InputSchema, - _deployment: DeploymentLocator, - _block_cursor: FirehoseCursor, - _subgraph_current_block: Option, - _filter: Arc, - ) -> anyhow::Result>> { - unimplemented!(); - } - async fn build_polling( &self, chain: &C, @@ -800,18 +786,6 @@ impl BlockStreamBuilder for StaticStreamBuilder where C::TriggerData: Clone, { - async fn build_substreams( - &self, - _chain: &C, - _schema: InputSchema, - _deployment: DeploymentLocator, - _block_cursor: FirehoseCursor, - _subgraph_current_block: Option, - _filter: Arc, - ) -> anyhow::Result>> { - unimplemented!() - } - async fn build_firehose( &self, _chain: &C, From 346f856cc3f1c5d6a448b4a9d0c3c05ab0b6ef86 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Tue, 6 Jan 2026 17:25:06 -0800 Subject: [PATCH 03/13] near, graph, node: Remove unneeded BlockchainBuilder trait --- chain/near/src/chain.rs | 26 +++++++++++++++----------- graph/src/blockchain/builder.rs | 29 ----------------------------- graph/src/blockchain/mod.rs | 2 -- node/src/chain.rs | 22 +++++++--------------- 4 files changed, 22 insertions(+), 57 deletions(-) delete mode 100644 graph/src/blockchain/builder.rs diff --git a/chain/near/src/chain.rs b/chain/near/src/chain.rs index becaf1556e2..6f25c64589a 100644 --- a/chain/near/src/chain.rs +++ b/chain/near/src/chain.rs @@ -2,14 +2,13 @@ use async_trait::async_trait; use graph::blockchain::client::ChainClient; use graph::blockchain::firehose_block_ingestor::FirehoseBlockIngestor; use graph::blockchain::{ - BasicBlockchainBuilder, BlockIngestor, BlockchainBuilder, BlockchainKind, NoopDecoderHook, - NoopRuntimeAdapter, TriggerFilterWrapper, + BlockIngestor, BlockchainKind, NoopDecoderHook, NoopRuntimeAdapter, TriggerFilterWrapper, }; use graph::cheap_clone::CheapClone; use graph::components::network_provider::ChainName; use graph::components::store::{ChainHeadStore, DeploymentCursorTracker, SourceableStore}; use graph::data::subgraph::UnifiedMappingApiVersion; -use graph::firehose::FirehoseEndpoint; +use graph::firehose::{FirehoseEndpoint, FirehoseEndpoints}; use graph::futures03::TryFutureExt; use graph::prelude::MetricsRegistry; use graph::{ @@ -113,15 +112,20 @@ impl std::fmt::Debug for Chain { } } -#[async_trait] -impl BlockchainBuilder for BasicBlockchainBuilder { - async fn build(self) -> Chain { +impl Chain { + pub fn new( + logger_factory: LoggerFactory, + name: ChainName, + chain_head_store: Arc, + firehose_endpoints: FirehoseEndpoints, + metrics_registry: Arc, + ) -> Self { Chain { - logger_factory: self.logger_factory, - name: self.name, - chain_head_store: self.chain_head_store, - client: Arc::new(ChainClient::new_firehose(self.firehose_endpoints)), - metrics_registry: self.metrics_registry, + logger_factory, + name, + chain_head_store, + client: Arc::new(ChainClient::new_firehose(firehose_endpoints)), + metrics_registry, block_stream_builder: Arc::new(NearStreamBuilder {}), } } diff --git a/graph/src/blockchain/builder.rs b/graph/src/blockchain/builder.rs deleted file mode 100644 index 773f186299f..00000000000 --- a/graph/src/blockchain/builder.rs +++ /dev/null @@ -1,29 +0,0 @@ -use tonic::async_trait; - -use super::Blockchain; -use crate::{ - components::store::ChainHeadStore, - data::value::Word, - firehose::FirehoseEndpoints, - prelude::{LoggerFactory, MetricsRegistry}, -}; -use std::sync::Arc; - -/// An implementor of [`BlockchainBuilder`] for chains that don't require -/// particularly fancy builder logic. -pub struct BasicBlockchainBuilder { - pub logger_factory: LoggerFactory, - pub name: Word, - pub chain_head_store: Arc, - pub firehose_endpoints: FirehoseEndpoints, - pub metrics_registry: Arc, -} - -/// Something that can build a [`Blockchain`]. -#[async_trait] -pub trait BlockchainBuilder -where - C: Blockchain, -{ - async fn build(self) -> C; -} diff --git a/graph/src/blockchain/mod.rs b/graph/src/blockchain/mod.rs index fcf6f88109e..f8358a209c1 100644 --- a/graph/src/blockchain/mod.rs +++ b/graph/src/blockchain/mod.rs @@ -3,7 +3,6 @@ //! trait which is the centerpiece of this module. pub mod block_stream; -mod builder; pub mod client; mod empty_node_capabilities; pub mod firehose_block_ingestor; @@ -49,7 +48,6 @@ use std::{ use web3::types::H256; pub use block_stream::{ChainHeadUpdateListener, ChainHeadUpdateStream, TriggersAdapter}; -pub use builder::{BasicBlockchainBuilder, BlockchainBuilder}; pub use empty_node_capabilities::EmptyNodeCapabilities; pub use noop_runtime_adapter::NoopRuntimeAdapter; pub use types::{BlockHash, BlockPtr, BlockTime, ChainIdentifier, ExtendedBlockPtr}; diff --git a/node/src/chain.rs b/node/src/chain.rs index 308740628be..9df653bc9d2 100644 --- a/node/src/chain.rs +++ b/node/src/chain.rs @@ -10,9 +10,7 @@ use ethereum::network::EthereumNetworkAdapter; use ethereum::ProviderEthRpcMetrics; use graph::anyhow::bail; use graph::blockchain::client::ChainClient; -use graph::blockchain::{ - BasicBlockchainBuilder, BlockchainBuilder as _, BlockchainKind, BlockchainMap, ChainIdentifier, -}; +use graph::blockchain::{BlockchainKind, BlockchainMap, ChainIdentifier}; use graph::cheap_clone::CheapClone; use graph::components::network_provider::ChainName; use graph::components::store::BlockStore as _; @@ -383,20 +381,14 @@ pub async fn networks_as_chains( } BlockchainKind::Near => { let firehose_endpoints = networks.firehose_endpoints(chain_id.clone()); - blockchain_map.insert::( + let chain = graph_chain_near::Chain::new( + logger_factory.clone(), chain_id.clone(), - Arc::new( - BasicBlockchainBuilder { - logger_factory: logger_factory.clone(), - name: chain_id.clone(), - chain_head_store: chain_store.cheap_clone(), - firehose_endpoints, - metrics_registry: metrics_registry.clone(), - } - .build() - .await, - ), + chain_store.cheap_clone(), + firehose_endpoints, + metrics_registry.clone(), ); + blockchain_map.insert::(chain_id.clone(), Arc::new(chain)); } } } From fdf54543081c3a3a333c5b6a5758610cb15eda1d Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 7 Jan 2026 08:16:23 -0800 Subject: [PATCH 04/13] all: Remove more substreams code --- graph/src/data/subgraph/mod.rs | 10 ---- graph/src/endpoint.rs | 2 - node/src/manager/commands/provider_checks.rs | 21 -------- node/src/network_setup.rs | 55 +------------------- 4 files changed, 2 insertions(+), 86 deletions(-) diff --git a/graph/src/data/subgraph/mod.rs b/graph/src/data/subgraph/mod.rs index f8047c0a807..43b2b8e89dc 100644 --- a/graph/src/data/subgraph/mod.rs +++ b/graph/src/data/subgraph/mod.rs @@ -61,8 +61,6 @@ use std::sync::Arc; use super::{graphql::IntoValue, value::Word}; -pub const SUBSTREAMS_KIND: &str = "substreams"; - /// Deserialize an Address (with or without '0x' prefix). fn deserialize_address<'de, D>(deserializer: D) -> Result, D::Error> where @@ -1215,14 +1213,6 @@ impl UnresolvedSubgraphManifest { })) .await?; - let is_substreams = data_sources.iter().any(|ds| ds.kind() == SUBSTREAMS_KIND); - if is_substreams && data_sources.len() > 1 { - return Err(anyhow!( - "A Substreams-based subgraph can only contain a single data source." - ) - .into()); - } - for ds in &data_sources { ensure!( semver::VersionReq::parse(&format!("<= {}", ENV_VARS.mappings.max_api_version)) diff --git a/graph/src/endpoint.rs b/graph/src/endpoint.rs index a9fdd99a98c..cc6c3da99c7 100644 --- a/graph/src/endpoint.rs +++ b/graph/src/endpoint.rs @@ -31,7 +31,6 @@ pub struct RequestLabels { #[derive(Clone)] pub enum ConnectionType { Firehose, - Substreams, Rpc, } @@ -39,7 +38,6 @@ impl Into<&str> for &ConnectionType { fn into(self) -> &'static str { match self { ConnectionType::Firehose => "firehose", - ConnectionType::Substreams => "substreams", ConnectionType::Rpc => "rpc", } } diff --git a/node/src/manager/commands/provider_checks.rs b/node/src/manager/commands/provider_checks.rs index a2541be6c7a..12f2e98e00b 100644 --- a/node/src/manager/commands/provider_checks.rs +++ b/node/src/manager/commands/provider_checks.rs @@ -67,27 +67,6 @@ pub async fn execute(logger: &Logger, networks: &Networks, store: BlockStore, ti } } - for adapter in networks - .substreams_provider_manager - .providers_unchecked(chain_name) - .unique_by(|x| x.provider_name()) - { - let validator = chain_id_validator(Box::new(store.cheap_clone())); - match tokio::time::timeout( - timeout, - run_checks(logger, chain_name, adapter, validator.clone()), - ) - .await - { - Ok(result) => { - errors.extend(result); - } - Err(_) => { - errors.push("Timeout".to_owned()); - } - } - } - if errors.is_empty() { println!("Chain: {chain_name}; Status: OK"); continue; diff --git a/node/src/network_setup.rs b/node/src/network_setup.rs index b9450eb80b7..c35b5fe4831 100644 --- a/node/src/network_setup.rs +++ b/node/src/network_setup.rs @@ -55,21 +55,19 @@ pub struct FirehoseAdapterConfig { pub enum AdapterConfiguration { Rpc(EthAdapterConfig), Firehose(FirehoseAdapterConfig), - Substreams(FirehoseAdapterConfig), } impl AdapterConfiguration { pub fn blockchain_kind(&self) -> &BlockchainKind { match self { AdapterConfiguration::Rpc(_) => &BlockchainKind::Ethereum, - AdapterConfiguration::Firehose(fh) | AdapterConfiguration::Substreams(fh) => &fh.kind, + AdapterConfiguration::Firehose(fh) => &fh.kind, } } pub fn chain_id(&self) -> &ChainName { match self { AdapterConfiguration::Rpc(EthAdapterConfig { chain_id, .. }) - | AdapterConfiguration::Firehose(FirehoseAdapterConfig { chain_id, .. }) - | AdapterConfiguration::Substreams(FirehoseAdapterConfig { chain_id, .. }) => chain_id, + | AdapterConfiguration::Firehose(FirehoseAdapterConfig { chain_id, .. }) => chain_id, } } @@ -90,24 +88,12 @@ impl AdapterConfiguration { pub fn is_firehose(&self) -> bool { self.as_firehose().is_none() } - - pub fn as_substreams(&self) -> Option<&FirehoseAdapterConfig> { - match self { - AdapterConfiguration::Substreams(fh) => Some(fh), - _ => None, - } - } - - pub fn is_substreams(&self) -> bool { - self.as_substreams().is_none() - } } pub struct Networks { pub adapters: Vec, pub rpc_provider_manager: ProviderManager, pub firehose_provider_manager: ProviderManager>, - pub substreams_provider_manager: ProviderManager>, } impl Networks { @@ -125,11 +111,6 @@ impl Networks { vec![].into_iter(), ProviderCheckStrategy::MarkAsValid, ), - substreams_provider_manager: ProviderManager::new( - Logger::root(Discard, o!()), - vec![].into_iter(), - ProviderCheckStrategy::MarkAsValid, - ), } } @@ -172,14 +153,6 @@ impl Networks { "firehose", ) }) - .or_else(|_| { - get_identifier( - self.substreams_provider_manager.clone(), - logger, - chain_id, - "substreams", - ) - }) .await } @@ -288,19 +261,6 @@ impl Networks { ) .collect_vec(); - let substreams_adapters = adapters - .iter() - .flat_map(|a| a.as_substreams()) - .cloned() - .map( - |FirehoseAdapterConfig { - chain_id, - kind: _, - adapters, - }| { (chain_id, adapters) }, - ) - .collect_vec(); - let s = Self { adapters: adapters2, rpc_provider_manager: ProviderManager::new( @@ -315,13 +275,6 @@ impl Networks { .map(|(chain_id, endpoints)| (chain_id, endpoints)), ProviderCheckStrategy::RequireAll(provider_checks), ), - substreams_provider_manager: ProviderManager::new( - logger.clone(), - substreams_adapters - .into_iter() - .map(|(chain_id, endpoints)| (chain_id, endpoints)), - ProviderCheckStrategy::RequireAll(provider_checks), - ), }; s @@ -405,10 +358,6 @@ impl Networks { FirehoseEndpoints::new(chain_id, self.firehose_provider_manager.clone()) } - pub fn substreams_endpoints(&self, chain_id: ChainName) -> FirehoseEndpoints { - FirehoseEndpoints::new(chain_id, self.substreams_provider_manager.clone()) - } - pub fn ethereum_rpcs(&self, chain_id: ChainName) -> EthereumNetworkAdapters { let eth_adapters = self .adapters From 3965ada926db1ec7187a14ee248e9e034910fd0e Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 7 Jan 2026 13:04:14 -0800 Subject: [PATCH 05/13] graph, chain: Remove substreams proto from build.rs --- chain/near/build.rs | 5 +- chain/near/proto/substreams-triggers.proto | 12 - graph/build.rs | 14 -- graph/proto/substreams-rpc.proto | 253 --------------------- graph/proto/substreams.proto | 163 ------------- 5 files changed, 1 insertion(+), 446 deletions(-) delete mode 100644 chain/near/proto/substreams-triggers.proto delete mode 100644 graph/proto/substreams-rpc.proto delete mode 100644 graph/proto/substreams.proto diff --git a/chain/near/build.rs b/chain/near/build.rs index 0bb50d10b27..a95e37d57ac 100644 --- a/chain/near/build.rs +++ b/chain/near/build.rs @@ -3,9 +3,6 @@ fn main() { tonic_build::configure() .out_dir("src/protobuf") .extern_path(".sf.near.codec.v1", "crate::codec::pbcodec") - .compile_protos( - &["proto/near.proto", "proto/substreams-triggers.proto"], - &["proto"], - ) + .compile_protos(&["proto/near.proto"], &["proto"]) .expect("Failed to compile Firehose NEAR proto(s)"); } diff --git a/chain/near/proto/substreams-triggers.proto b/chain/near/proto/substreams-triggers.proto deleted file mode 100644 index 947052a2566..00000000000 --- a/chain/near/proto/substreams-triggers.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; - -import "near.proto"; - -package receipts.v1; - -message BlockAndReceipts { - sf.near.codec.v1.Block block = 1; - repeated sf.near.codec.v1.ExecutionOutcomeWithId outcome = 2; - repeated sf.near.codec.v1.Receipt receipt = 3; -} - diff --git a/graph/build.rs b/graph/build.rs index d67e110edf4..ed4c31d077e 100644 --- a/graph/build.rs +++ b/graph/build.rs @@ -11,18 +11,4 @@ fn main() { &["proto"], ) .expect("Failed to compile Firehose proto(s)"); - - tonic_build::configure() - .protoc_arg("--experimental_allow_proto3_optional") - .out_dir("src/substreams") - .compile_protos(&["proto/substreams.proto"], &["proto"]) - .expect("Failed to compile Substreams proto(s)"); - - tonic_build::configure() - .protoc_arg("--experimental_allow_proto3_optional") - .extern_path(".sf.substreams.v1", "crate::substreams") - .extern_path(".sf.firehose.v2", "crate::firehose") - .out_dir("src/substreams_rpc") - .compile_protos(&["proto/substreams-rpc.proto"], &["proto"]) - .expect("Failed to compile Substreams RPC proto(s)"); } diff --git a/graph/proto/substreams-rpc.proto b/graph/proto/substreams-rpc.proto deleted file mode 100644 index 28298458480..00000000000 --- a/graph/proto/substreams-rpc.proto +++ /dev/null @@ -1,253 +0,0 @@ -syntax = "proto3"; - -package sf.substreams.rpc.v2; - -import "google/protobuf/any.proto"; -import "substreams.proto"; -import "firehose.proto"; - -service EndpointInfo { - rpc Info(sf.firehose.v2.InfoRequest) returns (sf.firehose.v2.InfoResponse); -} - -service Stream { rpc Blocks(Request) returns (stream Response); } - -message Request { - int64 start_block_num = 1; - string start_cursor = 2; - uint64 stop_block_num = 3; - - // With final_block_only, you only receive blocks that are irreversible: - // 'final_block_height' will be equal to current block and no 'undo_signal' - // will ever be sent - bool final_blocks_only = 4; - - // Substreams has two mode when executing your module(s) either development - // mode or production mode. Development and production modes impact the - // execution of Substreams, important aspects of execution include: - // * The time required to reach the first byte. - // * The speed that large ranges get executed. - // * The module logs and outputs sent back to the client. - // - // By default, the engine runs in developer mode, with richer and deeper - // output. Differences between production and development modes include: - // * Forward parallel execution is enabled in production mode and disabled in - // development mode - // * The time required to reach the first byte in development mode is faster - // than in production mode. - // - // Specific attributes of development mode include: - // * The client will receive all of the executed module's logs. - // * It's possible to request specific store snapshots in the execution tree - // (via `debug_initial_store_snapshot_for_modules`). - // * Multiple module's output is possible. - // - // With production mode`, however, you trade off functionality for high speed - // enabling forward parallel execution of module ahead of time. - bool production_mode = 5; - - string output_module = 6; - - sf.substreams.v1.Modules modules = 7; - - // Available only in developer mode - repeated string debug_initial_store_snapshot_for_modules = 10; -} - -message Response { - oneof message { - SessionInit session = 1; // Always sent first - ModulesProgress progress = 2; // Progress of data preparation, before - // sending in the stream of `data` events. - BlockScopedData block_scoped_data = 3; - BlockUndoSignal block_undo_signal = 4; - Error fatal_error = 5; - - // Available only in developer mode, and only if - // `debug_initial_store_snapshot_for_modules` is set. - InitialSnapshotData debug_snapshot_data = 10; - // Available only in developer mode, and only if - // `debug_initial_store_snapshot_for_modules` is set. - InitialSnapshotComplete debug_snapshot_complete = 11; - } -} - -// BlockUndoSignal informs you that every bit of data -// with a block number above 'last_valid_block' has been reverted -// on-chain. Delete that data and restart from 'last_valid_cursor' -message BlockUndoSignal { - sf.substreams.v1.BlockRef last_valid_block = 1; - string last_valid_cursor = 2; -} - -message BlockScopedData { - MapModuleOutput output = 1; - sf.substreams.v1.Clock clock = 2; - string cursor = 3; - - // Non-deterministic, allows substreams-sink to let go of their undo data. - uint64 final_block_height = 4; - - repeated MapModuleOutput debug_map_outputs = 10; - repeated StoreModuleOutput debug_store_outputs = 11; -} - -message SessionInit { - string trace_id = 1; - uint64 resolved_start_block = 2; - uint64 linear_handoff_block = 3; - uint64 max_parallel_workers = 4; -} - -message InitialSnapshotComplete { string cursor = 1; } - -message InitialSnapshotData { - string module_name = 1; - repeated StoreDelta deltas = 2; - uint64 sent_keys = 4; - uint64 total_keys = 3; -} - -message MapModuleOutput { - string name = 1; - google.protobuf.Any map_output = 2; - // DebugOutputInfo is available in non-production mode only - OutputDebugInfo debug_info = 10; -} - -// StoreModuleOutput are produced for store modules in development mode. -// It is not possible to retrieve store models in production, with -// parallelization enabled. If you need the deltas directly, write a pass -// through mapper module that will get them down to you. -message StoreModuleOutput { - string name = 1; - repeated StoreDelta debug_store_deltas = 2; - OutputDebugInfo debug_info = 10; -} - -message OutputDebugInfo { - repeated string logs = 1; - // LogsTruncated is a flag that tells you if you received all the logs or if - // they were truncated because you logged too much (fixed limit currently is - // set to 128 KiB). - bool logs_truncated = 2; - bool cached = 3; -} - -// ModulesProgress is a message that is sent every 500ms -message ModulesProgress { - // previously: repeated ModuleProgress modules = 1; - // these previous `modules` messages were sent in bursts and are not sent - // anymore. - reserved 1; - // List of jobs running on tier2 servers - repeated Job running_jobs = 2; - // Execution statistics for each module - repeated ModuleStats modules_stats = 3; - // Stages definition and completed block ranges - repeated Stage stages = 4; - - ProcessedBytes processed_bytes = 5; -} - -message ProcessedBytes { - uint64 total_bytes_read = 1; - uint64 total_bytes_written = 2; -} - -message Error { - string module = 1; - string reason = 2; - repeated string logs = 3; - // FailureLogsTruncated is a flag that tells you if you received all the logs - // or if they were truncated because you logged too much (fixed limit - // currently is set to 128 KiB). - bool logs_truncated = 4; -} - -message Job { - uint32 stage = 1; - uint64 start_block = 2; - uint64 stop_block = 3; - uint64 processed_blocks = 4; - uint64 duration_ms = 5; -} - -message Stage { - repeated string modules = 1; - repeated BlockRange completed_ranges = 2; -} - -// ModuleStats gathers metrics and statistics from each module, running on tier1 -// or tier2 All the 'count' and 'time_ms' values may include duplicate for each -// stage going over that module -message ModuleStats { - // name of the module - string name = 1; - - // total_processed_blocks is the sum of blocks sent to that module code - uint64 total_processed_block_count = 2; - // total_processing_time_ms is the sum of all time spent running that module - // code - uint64 total_processing_time_ms = 3; - - //// external_calls are chain-specific intrinsics, like "Ethereum RPC calls". - repeated ExternalCallMetric external_call_metrics = 4; - - // total_store_operation_time_ms is the sum of all time spent running that - // module code waiting for a store operation (ex: read, write, delete...) - uint64 total_store_operation_time_ms = 5; - // total_store_read_count is the sum of all the store Read operations called - // from that module code - uint64 total_store_read_count = 6; - - // total_store_write_count is the sum of all store Write operations called - // from that module code (store-only) - uint64 total_store_write_count = 10; - - // total_store_deleteprefix_count is the sum of all store DeletePrefix - // operations called from that module code (store-only) note that DeletePrefix - // can be a costly operation on large stores - uint64 total_store_deleteprefix_count = 11; - - // store_size_bytes is the uncompressed size of the full KV store for that - // module, from the last 'merge' operation (store-only) - uint64 store_size_bytes = 12; - - // total_store_merging_time_ms is the time spent merging partial stores into a - // full KV store for that module (store-only) - uint64 total_store_merging_time_ms = 13; - - // store_currently_merging is true if there is a merging operation (partial - // store to full KV store) on the way. - bool store_currently_merging = 14; - - // highest_contiguous_block is the highest block in the highest merged full KV - // store of that module (store-only) - uint64 highest_contiguous_block = 15; -} - -message ExternalCallMetric { - string name = 1; - uint64 count = 2; - uint64 time_ms = 3; -} - -message StoreDelta { - enum Operation { - UNSET = 0; - CREATE = 1; - UPDATE = 2; - DELETE = 3; - } - Operation operation = 1; - uint64 ordinal = 2; - string key = 3; - bytes old_value = 4; - bytes new_value = 5; -} - -message BlockRange { - uint64 start_block = 2; - uint64 end_block = 3; -} diff --git a/graph/proto/substreams.proto b/graph/proto/substreams.proto deleted file mode 100644 index 16db52419aa..00000000000 --- a/graph/proto/substreams.proto +++ /dev/null @@ -1,163 +0,0 @@ -syntax = "proto3"; - -package sf.substreams.v1; - -import "google/protobuf/timestamp.proto"; -import "google/protobuf/descriptor.proto"; -import "google/protobuf/any.proto"; - -message Package { - // Needs to be one so this file can be used _directly_ as a - // buf `Image` andor a ProtoSet for grpcurl and other tools - repeated google.protobuf.FileDescriptorProto proto_files = 1; - reserved 2 to 4; // Reserved for future: in case protosets adds fields - - uint64 version = 5; - sf.substreams.v1.Modules modules = 6; - repeated ModuleMetadata module_meta = 7; - repeated PackageMetadata package_meta = 8; - - // Source network for Substreams to fetch its data from. - string network = 9; - - google.protobuf.Any sink_config = 10; - string sink_module = 11; -} - -message PackageMetadata { - string version = 1; - string url = 2; - string name = 3; - string doc = 4; -} - -message ModuleMetadata { - // Corresponds to the index in `Package.metadata.package_meta` - uint64 package_index = 1; - string doc = 2; -} - -message Modules { - repeated Module modules = 1; - repeated Binary binaries = 2; -} - -// Binary represents some code compiled to its binary form. -message Binary { - string type = 1; - bytes content = 2; -} - -message Module { - string name = 1; - oneof kind { - KindMap kind_map = 2; - KindStore kind_store = 3; - KindBlockIndex kind_block_index = 10; - }; - - uint32 binary_index = 4; - string binary_entrypoint = 5; - - repeated Input inputs = 6; - Output output = 7; - - uint64 initial_block = 8; - - BlockFilter block_filter = 9; - - message BlockFilter { - string module = 1; - oneof query { - string query_string = 2; - QueryFromParams query_from_params = 3; - }; - } - - message QueryFromParams {} - - message KindMap { - string output_type = 1; - } - - message KindStore { - // The `update_policy` determines the functions available to mutate the store - // (like `set()`, `set_if_not_exists()` or `sum()`, etc..) in - // order to ensure that parallel operations are possible and deterministic - // - // Say a store cumulates keys from block 0 to 1M, and a second store - // cumulates keys from block 1M to 2M. When we want to use this - // store as a dependency for a downstream module, we will merge the - // two stores according to this policy. - UpdatePolicy update_policy = 1; - string value_type = 2; - - enum UpdatePolicy { - UPDATE_POLICY_UNSET = 0; - // Provides a store where you can `set()` keys, and the latest key wins - UPDATE_POLICY_SET = 1; - // Provides a store where you can `set_if_not_exists()` keys, and the first key wins - UPDATE_POLICY_SET_IF_NOT_EXISTS = 2; - // Provides a store where you can `add_*()` keys, where two stores merge by summing its values. - UPDATE_POLICY_ADD = 3; - // Provides a store where you can `min_*()` keys, where two stores merge by leaving the minimum value. - UPDATE_POLICY_MIN = 4; - // Provides a store where you can `max_*()` keys, where two stores merge by leaving the maximum value. - UPDATE_POLICY_MAX = 5; - // Provides a store where you can `append()` keys, where two stores merge by concatenating the bytes in order. - UPDATE_POLICY_APPEND = 6; - // Provides a store with both `set()` and `sum()` functions. - UPDATE_POLICY_SET_SUM = 7; - } - } - - message KindBlockIndex { - string output_type = 1; - } - - message Input { - oneof input { - Source source = 1; - Map map = 2; - Store store = 3; - Params params = 4; - } - - message Source { - string type = 1; // ex: "sf.ethereum.type.v1.Block" - } - message Map { - string module_name = 1; // ex: "block_to_pairs" - } - message Store { - string module_name = 1; - Mode mode = 2; - - enum Mode { - UNSET = 0; - GET = 1; - DELTAS = 2; - } - } - message Params { - string value = 1; - } - } - - message Output { - string type = 1; - } -} - -// Clock is a pointer to a block with added timestamp -message Clock { - string id = 1; - uint64 number = 2; - google.protobuf.Timestamp timestamp = 3; -} - -// BlockRef is a pointer to a block to which we don't know the timestamp -message BlockRef { - string id = 1; - uint64 number = 2; -} From 1856aadb0912dceb562d6e397460703a3ba91a18 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 7 Jan 2026 13:06:54 -0800 Subject: [PATCH 06/13] chain/near: Remove substreams_trigger module --- chain/near/src/codec.rs | 4 ---- chain/near/src/protobuf/receipts.v1.rs | 10 ---------- 2 files changed, 14 deletions(-) delete mode 100644 chain/near/src/protobuf/receipts.v1.rs diff --git a/chain/near/src/codec.rs b/chain/near/src/codec.rs index 6f0f2f7af4d..bbcfd6646a4 100644 --- a/chain/near/src/codec.rs +++ b/chain/near/src/codec.rs @@ -2,10 +2,6 @@ #[path = "protobuf/sf.near.codec.v1.rs"] pub mod pbcodec; -#[rustfmt::skip] -#[path = "protobuf/receipts.v1.rs"] -pub mod substreams_triggers; - use graph::{ blockchain::Block as BlockchainBlock, blockchain::{BlockPtr, BlockTime}, diff --git a/chain/near/src/protobuf/receipts.v1.rs b/chain/near/src/protobuf/receipts.v1.rs deleted file mode 100644 index 2b844103e9a..00000000000 --- a/chain/near/src/protobuf/receipts.v1.rs +++ /dev/null @@ -1,10 +0,0 @@ -// This file is @generated by prost-build. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockAndReceipts { - #[prost(message, optional, tag = "1")] - pub block: ::core::option::Option, - #[prost(message, repeated, tag = "2")] - pub outcome: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "3")] - pub receipt: ::prost::alloc::vec::Vec, -} From 9130ee45d969763965b6477a65f57e6cba24d7e4 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 7 Jan 2026 13:09:49 -0800 Subject: [PATCH 07/13] graph: Remove SubstreamsError --- graph/src/blockchain/block_stream.rs | 49 ---------------------------- 1 file changed, 49 deletions(-) diff --git a/graph/src/blockchain/block_stream.rs b/graph/src/blockchain/block_stream.rs index 81a0f37c7d7..b1020ba8467 100644 --- a/graph/src/blockchain/block_stream.rs +++ b/graph/src/blockchain/block_stream.rs @@ -21,7 +21,6 @@ use crate::{prelude::*, prometheus::labels}; pub const BUFFERED_BLOCK_STREAM_SIZE: usize = 100; pub const FIREHOSE_BUFFER_STREAM_SIZE: usize = 1; -pub const SUBSTREAMS_BUFFER_STREAM_SIZE: usize = 100; pub struct BufferedBlockStream { inner: Pin, BlockStreamError>> + Send>>, @@ -700,58 +699,10 @@ impl From for FirehoseError { } } -#[derive(Error, Debug)] -pub enum SubstreamsError { - #[error("response is missing the clock information")] - MissingClockError, - - #[error("invalid undo message")] - InvalidUndoError, - - #[error("entity validation failed {0}")] - EntityValidationError(#[from] crate::data::store::EntityValidationError), - - /// We were unable to decode the received block payload into the chain specific Block struct (e.g. chain_ethereum::pb::Block) - #[error("received gRPC block payload cannot be decoded: {0}")] - DecodingError(#[from] prost::DecodeError), - - /// Some unknown error occurred - #[error("unknown error {0}")] - UnknownError(#[from] anyhow::Error), - - #[error("multiple module output error")] - MultipleModuleOutputError, - - #[error("module output was not available (none) or wrong data provided")] - ModuleOutputNotPresentOrUnexpected, - - #[error("unexpected store delta output")] - UnexpectedStoreDeltaOutput, -} - -impl SubstreamsError { - pub fn is_deterministic(&self) -> bool { - use SubstreamsError::*; - - match self { - EntityValidationError(_) => true, - MissingClockError - | InvalidUndoError - | DecodingError(_) - | UnknownError(_) - | MultipleModuleOutputError - | ModuleOutputNotPresentOrUnexpected - | UnexpectedStoreDeltaOutput => false, - } - } -} - #[derive(Debug, Error)] pub enum BlockStreamError { #[error("Failed to decode protobuf {0}")] ProtobufDecodingError(#[from] prost::DecodeError), - #[error("substreams error: {0}")] - SubstreamsError(#[from] SubstreamsError), #[error("block stream error {0}")] Unknown(#[from] anyhow::Error), #[error("block stream fatal error {0}")] From 40e00a936b5ae2cf6bc53be715bcf7dc212569c5 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 7 Jan 2026 13:47:56 -0800 Subject: [PATCH 08/13] all: Remove 'wasm block' processing This was only used for substreams --- core/src/subgraph/context/instance/mod.rs | 4 - core/src/subgraph/context/mod.rs | 67 +----------- core/src/subgraph/runner.rs | 122 ---------------------- graph/src/blockchain/block_stream.rs | 3 +- graph/src/components/subgraph/host.rs | 14 --- runtime/wasm/src/host.rs | 96 +---------------- runtime/wasm/src/mapping.rs | 26 ----- runtime/wasm/src/module/instance.rs | 18 +--- 8 files changed, 8 insertions(+), 342 deletions(-) diff --git a/core/src/subgraph/context/instance/mod.rs b/core/src/subgraph/context/instance/mod.rs index 0d14ae8d758..4ad491e786c 100644 --- a/core/src/subgraph/context/instance/mod.rs +++ b/core/src/subgraph/context/instance/mod.rs @@ -255,8 +255,4 @@ where pub fn hosts_len(&self) -> usize { self.onchain_hosts.len() + self.offchain_hosts.len() } - - pub fn first_host(&self) -> Option<&Arc> { - self.onchain_hosts.hosts().first() - } } diff --git a/core/src/subgraph/context/mod.rs b/core/src/subgraph/context/mod.rs index 846e0d6fefb..e6f485e2552 100644 --- a/core/src/subgraph/context/mod.rs +++ b/core/src/subgraph/context/mod.rs @@ -6,11 +6,8 @@ use crate::polling_monitor::{ use anyhow::{self, Error}; use bytes::Bytes; use graph::{ - blockchain::{BlockTime, Blockchain, TriggerFilterWrapper}, - components::{ - store::{DeploymentId, SubgraphFork}, - subgraph::{HostMetrics, MappingError, RuntimeHost as _, SharedProofOfIndexing}, - }, + blockchain::{Blockchain, TriggerFilterWrapper}, + components::{store::DeploymentId, subgraph::HostMetrics}, data::subgraph::SubgraphManifest, data_source::{ causality_region::CausalityRegionSeq, @@ -20,14 +17,13 @@ use graph::{ derive::CheapClone, ipfs::IpfsContext, prelude::{ - BlockNumber, BlockPtr, BlockState, CancelGuard, CheapClone, DeploymentHash, - MetricsRegistry, RuntimeHostBuilder, SubgraphCountMetric, SubgraphInstanceMetrics, - TriggerProcessor, + BlockNumber, CancelGuard, CheapClone, DeploymentHash, MetricsRegistry, RuntimeHostBuilder, + SubgraphCountMetric, TriggerProcessor, }, slog::Logger, }; +use std::collections::HashMap; use std::sync::{Arc, RwLock}; -use std::{collections::HashMap, time::Instant}; use tokio::sync::mpsc; use self::instance::SubgraphInstance; @@ -109,59 +105,6 @@ impl> IndexingContext { } } - pub async fn process_block( - &self, - logger: &Logger, - block_ptr: BlockPtr, - block_time: BlockTime, - block_data: Box<[u8]>, - handler: String, - mut state: BlockState, - proof_of_indexing: &SharedProofOfIndexing, - causality_region: &str, - debug_fork: &Option>, - subgraph_metrics: &Arc, - instrument: bool, - ) -> Result { - let error_count = state.deterministic_errors.len(); - - proof_of_indexing.start_handler(causality_region); - - let start = Instant::now(); - - // This flow is expected to have a single data source(and a corresponding host) which - // gets executed every block. - state = self - .instance - .first_host() - .expect("Expected this flow to have exactly one host") - .process_block( - logger, - block_ptr, - block_time, - block_data, - handler, - state, - proof_of_indexing.cheap_clone(), - debug_fork, - instrument, - ) - .await?; - - let elapsed = start.elapsed().as_secs_f64(); - subgraph_metrics.observe_trigger_processing_duration(elapsed); - - if state.deterministic_errors.len() != error_count { - assert!(state.deterministic_errors.len() == error_count + 1); - - // If a deterministic error has happened, write a new - // ProofOfIndexingEvent::DeterministicError to the SharedProofOfIndexing. - proof_of_indexing.write_deterministic_error(logger, causality_region); - } - - Ok(state) - } - /// Removes data sources hosts with a creation block greater or equal to `reverted_block`, so /// that they are no longer candidates for `process_trigger`. /// diff --git a/core/src/subgraph/runner.rs b/core/src/subgraph/runner.rs index 0e647b8aa3f..7e3ad0fd353 100644 --- a/core/src/subgraph/runner.rs +++ b/core/src/subgraph/runner.rs @@ -47,7 +47,6 @@ const MINUTE: Duration = Duration::from_secs(60); const SKIP_PTR_UPDATES_THRESHOLD: Duration = Duration::from_secs(60 * 5); const HANDLE_REVERT_SECTION_NAME: &str = "handle_revert"; const PROCESS_BLOCK_SECTION_NAME: &str = "process_block"; -const PROCESS_WASM_BLOCK_SECTION_NAME: &str = "process_wasm_block"; const PROCESS_TRIGGERS_SECTION_NAME: &str = "process_triggers"; const HANDLE_CREATED_DS_SECTION_NAME: &str = "handle_new_data_sources"; @@ -869,37 +868,6 @@ where Ok(Arc::new(block)) } - async fn process_wasm_block( - &mut self, - proof_of_indexing: &SharedProofOfIndexing, - block_ptr: BlockPtr, - block_time: BlockTime, - block_data: Box<[u8]>, - handler: String, - causality_region: &str, - ) -> Result { - let block_state = BlockState::new( - self.inputs.store.clone(), - std::mem::take(&mut self.state.entity_lfu_cache), - ); - - self.ctx - .process_block( - &self.logger, - block_ptr, - block_time, - block_data, - handler, - block_state, - proof_of_indexing, - causality_region, - &self.inputs.debug_fork, - &self.metrics.subgraph, - self.inputs.instrument, - ) - .await - } - fn create_dynamic_data_sources( &mut self, created_data_sources: Vec, @@ -1171,20 +1139,6 @@ where ) -> Result { let stopwatch = &self.metrics.stream.stopwatch; let action = match event { - Some(Ok(BlockStreamEvent::ProcessWasmBlock( - block_ptr, - block_time, - data, - handler, - cursor, - ))) => { - let _section = stopwatch.start_section(PROCESS_WASM_BLOCK_SECTION_NAME); - let res = self - .handle_process_wasm_block(block_ptr.clone(), block_time, data, handler, cursor) - .await; - let start = Instant::now(); - self.handle_action(start, block_ptr, res).await? - } Some(Ok(BlockStreamEvent::ProcessBlock(block, cursor))) => { let _section = stopwatch.start_section(PROCESS_BLOCK_SECTION_NAME); self.handle_process_block(block, cursor).await? @@ -1335,82 +1289,6 @@ where C: Blockchain, T: RuntimeHostBuilder, { - async fn handle_process_wasm_block( - &mut self, - block_ptr: BlockPtr, - block_time: BlockTime, - block_data: Box<[u8]>, - handler: String, - cursor: FirehoseCursor, - ) -> Result { - let logger = self.logger.new(o!( - "block_number" => format!("{:?}", block_ptr.number), - "block_hash" => format!("{}", block_ptr.hash) - )); - - debug!(logger, "Start processing wasm block";); - - self.metrics - .stream - .deployment_head - .set(block_ptr.number as f64); - - let proof_of_indexing = - SharedProofOfIndexing::new(block_ptr.number, self.inputs.poi_version); - - // Causality region for onchain triggers. - let causality_region = PoICausalityRegion::from_network(&self.inputs.network); - - let block_state = { - match self - .process_wasm_block( - &proof_of_indexing, - block_ptr.clone(), - block_time, - block_data, - handler, - &causality_region, - ) - .await - { - // Triggers processed with no errors or with only deterministic errors. - Ok(block_state) => block_state, - - // Some form of unknown or non-deterministic error ocurred. - Err(MappingError::Unknown(e)) => return Err(ProcessingError::Unknown(e).into()), - Err(MappingError::PossibleReorg(e)) => { - info!(logger, - "Possible reorg detected, retrying"; - "error" => format!("{:#}", e), - ); - - // In case of a possible reorg, we want this function to do nothing and restart the - // block stream so it has a chance to detect the reorg. - // - // The state is unchanged at this point, except for having cleared the entity cache. - // Losing the cache is a bit annoying but not an issue for correctness. - // - // See also b21fa73b-6453-4340-99fb-1a78ec62efb1. - return Ok(Action::Restart); - } - } - }; - - self.transact_block_state( - &logger, - block_ptr.clone(), - cursor.clone(), - block_time, - block_state, - proof_of_indexing, - vec![], - vec![], - ) - .await?; - - Ok(Action::Continue) - } - async fn handle_process_block( &mut self, block: BlockWithTriggers, diff --git a/graph/src/blockchain/block_stream.rs b/graph/src/blockchain/block_stream.rs index b1020ba8467..7d87bba9398 100644 --- a/graph/src/blockchain/block_stream.rs +++ b/graph/src/blockchain/block_stream.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use thiserror::Error; use tokio::sync::mpsc::{self, Receiver, Sender}; -use super::{Block, BlockPtr, BlockTime, Blockchain, Trigger, TriggerFilterWrapper}; +use super::{Block, BlockPtr, Blockchain, Trigger, TriggerFilterWrapper}; use crate::anyhow::Result; use crate::components::store::{BlockNumber, DeploymentLocator, SourceableStore}; use crate::data::subgraph::UnifiedMappingApiVersion; @@ -721,7 +721,6 @@ pub enum BlockStreamEvent { Revert(BlockPtr, FirehoseCursor), ProcessBlock(BlockWithTriggers, FirehoseCursor), - ProcessWasmBlock(BlockPtr, BlockTime, Box<[u8]>, String, FirehoseCursor), } #[derive(Clone)] diff --git a/graph/src/components/subgraph/host.rs b/graph/src/components/subgraph/host.rs index f43c6aa3c00..0cba98912c7 100644 --- a/graph/src/components/subgraph/host.rs +++ b/graph/src/components/subgraph/host.rs @@ -6,7 +6,6 @@ use anyhow::Error; use async_trait::async_trait; use futures01::sync::mpsc; -use crate::blockchain::BlockTime; use crate::components::metrics::gas::GasMetrics; use crate::components::store::SubgraphFork; use crate::data_source::{ @@ -71,19 +70,6 @@ pub trait RuntimeHost: Send + Sync + 'static { logger: &Logger, ) -> Result>>, Error>; - async fn process_block( - &self, - logger: &Logger, - block_ptr: BlockPtr, - block_time: BlockTime, - block_data: Box<[u8]>, - handler: String, - state: BlockState, - proof_of_indexing: SharedProofOfIndexing, - debug_fork: &Option>, - instrument: bool, - ) -> Result; - async fn process_mapping_trigger( &self, logger: &Logger, diff --git a/runtime/wasm/src/host.rs b/runtime/wasm/src/host.rs index f1a44466bec..c64b55e35b1 100644 --- a/runtime/wasm/src/host.rs +++ b/runtime/wasm/src/host.rs @@ -5,7 +5,7 @@ use async_trait::async_trait; use graph::futures01::sync::mpsc::Sender; use graph::futures03::channel::oneshot::channel; -use graph::blockchain::{BlockTime, Blockchain, HostFn, RuntimeAdapter}; +use graph::blockchain::{Blockchain, HostFn, RuntimeAdapter}; use graph::components::store::{EnsLookup, SubgraphFork}; use graph::components::subgraph::{MappingError, SharedProofOfIndexing}; use graph::data_source::{ @@ -221,74 +221,6 @@ where // Discard the gas value result.map(|(block_state, _)| block_state) } - - async fn send_wasm_block_request( - &self, - logger: &Logger, - state: BlockState, - block_ptr: BlockPtr, - timestamp: BlockTime, - block_data: Box<[u8]>, - handler: String, - proof_of_indexing: SharedProofOfIndexing, - debug_fork: &Option>, - instrument: bool, - ) -> Result { - trace!( - logger, "Start processing wasm block"; - "block_ptr" => &block_ptr, - "handler" => &handler, - "data_source" => &self.data_source.name(), - ); - - let (result_sender, result_receiver) = channel(); - let start_time = Instant::now(); - let metrics = self.metrics.clone(); - - self.mapping_request_sender - .clone() - .send(WasmRequest::new_block( - MappingContext { - logger: logger.cheap_clone(), - state, - host_exports: self.host_exports.cheap_clone(), - block_ptr: block_ptr.clone(), - timestamp, - proof_of_indexing, - host_fns: self.host_fns.cheap_clone(), - debug_fork: debug_fork.cheap_clone(), - mapping_logger: Logger::new(&logger, o!("component" => "UserBlockMapping")), - instrument, - }, - handler.clone(), - block_data, - result_sender, - )) - .compat() - .await - .context("Mapping terminated before passing in wasm block")?; - - let result = result_receiver - .await - .context("Mapping terminated before handling block")?; - - let elapsed = start_time.elapsed(); - metrics.observe_handler_execution_time(elapsed.as_secs_f64(), &handler); - - // If there is an error, "gas_used" is incorrectly reported as 0. - let gas_used = result.as_ref().map(|(_, gas)| gas).unwrap_or(&Gas::ZERO); - debug!( - logger, "Done processing wasm block"; - "block_ptr" => &block_ptr, - "total_ms" => elapsed.as_millis(), - "handler" => handler, - "data_source" => &self.data_source.name(), - "gas_used" => gas_used.to_string(), - ); - - // Discard the gas value - result.map(|(block_state, _)| block_state) - } } #[async_trait] @@ -306,32 +238,6 @@ impl RuntimeHostTrait for RuntimeHost { self.data_source.match_and_decode(trigger, block, logger) } - async fn process_block( - &self, - logger: &Logger, - block_ptr: BlockPtr, - block_time: BlockTime, - block_data: Box<[u8]>, - handler: String, - state: BlockState, - proof_of_indexing: SharedProofOfIndexing, - debug_fork: &Option>, - instrument: bool, - ) -> Result { - self.send_wasm_block_request( - logger, - state, - block_ptr, - block_time, - block_data, - handler, - proof_of_indexing, - debug_fork, - instrument, - ) - .await - } - async fn process_mapping_trigger( &self, logger: &Logger, diff --git a/runtime/wasm/src/mapping.rs b/runtime/wasm/src/mapping.rs index 0e06c125c1a..75c7cd64042 100644 --- a/runtime/wasm/src/mapping.rs +++ b/runtime/wasm/src/mapping.rs @@ -81,10 +81,6 @@ where handle_trigger(&logger, module, trigger, host_metrics.cheap_clone()) .await } - WasmRequestInner::BlockRequest(BlockRequest { - block_data, - handler, - }) => module.handle_block(&logger, &handler, block_data).await, }, Err(e) => Err(MappingError::Unknown(e)), } @@ -181,32 +177,10 @@ impl WasmRequest { result_sender, } } - - pub(crate) fn new_block( - ctx: MappingContext, - handler: String, - block_data: Box<[u8]>, - result_sender: Sender>, - ) -> Self { - WasmRequest { - ctx, - inner: WasmRequestInner::BlockRequest(BlockRequest { - handler, - block_data, - }), - result_sender, - } - } } pub enum WasmRequestInner { TriggerRequest(TriggerWithHandler>), - BlockRequest(BlockRequest), -} - -pub struct BlockRequest { - pub(crate) handler: String, - pub(crate) block_data: Box<[u8]>, } pub struct MappingContext { diff --git a/runtime/wasm/src/module/instance.rs b/runtime/wasm/src/module/instance.rs index 21560bb4fe5..0b6617bcb24 100644 --- a/runtime/wasm/src/module/instance.rs +++ b/runtime/wasm/src/module/instance.rs @@ -17,7 +17,7 @@ use graph::prelude::*; use graph::runtime::{ asc_new, gas::{Gas, GasCounter, SaturatingInto}, - HostExportError, ToAscObj, + HostExportError, }; use graph::{components::subgraph::MappingError, runtime::AscPtr}; @@ -111,22 +111,6 @@ impl WasmInstance { Ok(wasm_ctx.take_state()) } - pub(crate) async fn handle_block( - mut self, - _logger: &Logger, - handler_name: &str, - block_data: Box<[u8]>, - ) -> Result<(BlockState, Gas), MappingError> { - let gas = self.gas.clone(); - let mut ctx = self.instance_ctx(); - let obj = block_data.to_vec().to_asc_obj(&mut ctx, &gas).await?; - - let obj = AscPtr::alloc_obj(obj, &mut ctx, &gas).await?; - - self.invoke_handler(handler_name, obj, Arc::new(o!()), None) - .await - } - pub(crate) async fn handle_trigger( mut self, trigger: TriggerWithHandler>, From d45d5d9371d2015c060d4108acd7faecd796b9f0 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 7 Jan 2026 16:53:32 -0800 Subject: [PATCH 09/13] test: Remove substreams example subgraph --- tests/runner-tests/substreams/.gitignore | 4 - tests/runner-tests/substreams/Cargo.lock | 980 ------------------ tests/runner-tests/substreams/Cargo.toml | 18 - tests/runner-tests/substreams/README.md | 19 - tests/runner-tests/substreams/package.json | 20 - .../substreams/proto/example.proto | 14 - .../substreams/rust-toolchain.toml | 3 - tests/runner-tests/substreams/schema.graphql | 9 - tests/runner-tests/substreams/src/lib.rs | 40 - tests/runner-tests/substreams/subgraph.yaml | 16 - .../substreams/substreams-test-v1.0.1.spkg | Bin 246354 -> 0 bytes tests/runner-tests/substreams/substreams.yaml | 33 - 12 files changed, 1156 deletions(-) delete mode 100644 tests/runner-tests/substreams/.gitignore delete mode 100644 tests/runner-tests/substreams/Cargo.lock delete mode 100644 tests/runner-tests/substreams/Cargo.toml delete mode 100644 tests/runner-tests/substreams/README.md delete mode 100644 tests/runner-tests/substreams/package.json delete mode 100644 tests/runner-tests/substreams/proto/example.proto delete mode 100644 tests/runner-tests/substreams/rust-toolchain.toml delete mode 100644 tests/runner-tests/substreams/schema.graphql delete mode 100644 tests/runner-tests/substreams/src/lib.rs delete mode 100644 tests/runner-tests/substreams/subgraph.yaml delete mode 100644 tests/runner-tests/substreams/substreams-test-v1.0.1.spkg delete mode 100644 tests/runner-tests/substreams/substreams.yaml diff --git a/tests/runner-tests/substreams/.gitignore b/tests/runner-tests/substreams/.gitignore deleted file mode 100644 index 37e5bb836a4..00000000000 --- a/tests/runner-tests/substreams/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -target/ -.idea -src/pb/ -node_modules/ \ No newline at end of file diff --git a/tests/runner-tests/substreams/Cargo.lock b/tests/runner-tests/substreams/Cargo.lock deleted file mode 100644 index e8575b5b430..00000000000 --- a/tests/runner-tests/substreams/Cargo.lock +++ /dev/null @@ -1,980 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "bigdecimal" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byte-slice-cast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "ethabi" -version = "17.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4966fba78396ff92db3b817ee71143eccd98acf0f876b8d600e585a670c5d1b" -dependencies = [ - "ethereum-types", - "hex", - "once_cell", - "regex", - "serde", - "serde_json", - "sha3", - "thiserror", - "uint", -] - -[[package]] -name = "ethbloom" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11da94e443c60508eb62cf256243a64da87304c2802ac2528847f79d750007ef" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-rlp", - "impl-serde", - "tiny-keccak", -] - -[[package]] -name = "ethereum-types" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-rlp", - "impl-serde", - "primitive-types", - "uint", -] - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - -[[package]] -name = "fixed-hash" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" -dependencies = [ - "byteorder", - "rand", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "heck" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" -dependencies = [ - "serde", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" - -[[package]] -name = "keccak" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" - -[[package]] -name = "pad" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ad9b889f1b12e0b9ee24db044b5129150d5eada288edc800f789928dc8c0e3" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "parity-scale-codec" -version = "3.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ab01d0f889e957861bc65888d5ccbe82c158d0270136ba46820d43837cdf72" -dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "petgraph" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "prettyplease" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "primitive-types" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" -dependencies = [ - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" -dependencies = [ - "once_cell", - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro2" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f8ad728fb08fe212df3c05169e940fbb6d9d16a877ddde14644a983ba2012e" -dependencies = [ - "bytes", - "heck", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost", -] - -[[package]] -name = "quote" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rustc-hex", -] - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "ryu" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "serde" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha3" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "substreams" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af972e374502cdfc9998132f5343848d1c58f27a295dc061a89804371f408a46" -dependencies = [ - "anyhow", - "bigdecimal", - "hex", - "hex-literal", - "num-bigint", - "num-traits", - "pad", - "prost", - "prost-build", - "prost-types", - "substreams-macro", - "thiserror", -] - -[[package]] -name = "substreams-entity-change" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d423d0c12a9284a3d6d4ec288dbc9bfec3d55f9056098ba91a6dcfa64fb3889e" -dependencies = [ - "base64", - "prost", - "prost-types", - "substreams", -] - -[[package]] -name = "substreams-ethereum" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78effc18ed321399fe15ec082806e96a58d213f79741d078c1cd26dd6dd53025" -dependencies = [ - "getrandom", - "num-bigint", - "substreams", - "substreams-ethereum-abigen", - "substreams-ethereum-core", - "substreams-ethereum-derive", -] - -[[package]] -name = "substreams-ethereum-abigen" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a176f39a6e09553c17a287edacd1854e5686fd20ffea3c9655dfc44d94b35e" -dependencies = [ - "anyhow", - "ethabi", - "heck", - "hex", - "prettyplease", - "proc-macro2", - "quote", - "substreams-ethereum-core", - "syn", -] - -[[package]] -name = "substreams-ethereum-core" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4700cfe408b75634a3c6b3a0caf7bddba4879601d2085c811485ea54cbde2d" -dependencies = [ - "bigdecimal", - "ethabi", - "getrandom", - "num-bigint", - "prost", - "prost-build", - "prost-types", - "substreams", -] - -[[package]] -name = "substreams-ethereum-derive" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40d6d278d926fe3f0775d996ee2b5e1dc822c1b4bf4f7bf07c7fbb5bce6c79a9" -dependencies = [ - "ethabi", - "heck", - "hex", - "num-bigint", - "proc-macro2", - "quote", - "substreams-ethereum-abigen", - "syn", -] - -[[package]] -name = "substreams-ethereum-quickstart" -version = "1.0.0" -dependencies = [ - "base64", - "prost", - "prost-types", - "substreams", - "substreams-entity-change", - "substreams-ethereum", -] - -[[package]] -name = "substreams-macro" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6521ccd011a4c3f52cd3c31fc7400733e4feba2094e0e0e6354adca25b2b3f37" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "thiserror", -] - -[[package]] -name = "syn" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "thiserror" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] diff --git a/tests/runner-tests/substreams/Cargo.toml b/tests/runner-tests/substreams/Cargo.toml deleted file mode 100644 index 108db3089a8..00000000000 --- a/tests/runner-tests/substreams/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "substreams-ethereum-quickstart" -version = "1.0.0" -edition = "2021" - -[lib] -name = "substreams" -crate-type = ["cdylib"] - -[dependencies] -substreams = "0.5" -substreams-ethereum = "0.9" -substreams-entity-change = "1.3" - -[profile.release] -lto = true -opt-level = 's' -strip = "debuginfo" diff --git a/tests/runner-tests/substreams/README.md b/tests/runner-tests/substreams/README.md deleted file mode 100644 index 50e893de7a1..00000000000 --- a/tests/runner-tests/substreams/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Substreams-powered subgraph: tracking contract creation - -A basic Substreams-powered subgraph, including the Substreams definition. This example detects new -contract deployments on Ethereum, tracking the creation block and timestamp. There is a -demonstration of the Graph Node integration, using `substreams_entity_change` types and helpers. - -## Prerequisites - -This -[requires the dependencies necessary for local Substreams development](https://substreams.streamingfast.io/developers-guide/installation-requirements). - -## Quickstart - -``` -pnpm install # install graph-cli -pnpm substreams:prepare # build and package the substreams module -pnpm subgraph:build # build the subgraph -pnpm subgraph:deploy # deploy the subgraph -``` diff --git a/tests/runner-tests/substreams/package.json b/tests/runner-tests/substreams/package.json deleted file mode 100644 index f7dba22f4bf..00000000000 --- a/tests/runner-tests/substreams/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "substreams", - "version": "0.0.0", - "private": true, - "scripts": { - "codegen": "graph codegen", - "deploy": "graph deploy", - "deploy:test": "graph deploy test/substreams --version-label v0.0.1 --ipfs $IPFS_URI --node $GRAPH_NODE_ADMIN_URI", - "subgraph:build": "graph build", - "substreams:build": "cargo build --target wasm32-unknown-unknown --release", - "substreams:clean": "rm -rf ./target && rm -rf ./src/pb", - "substreams:package": "substreams pack ./substreams.yaml", - "substreams:prepare": "pnpm substreams:protogen && pnpm substreams:build && pnpm substreams:package", - "substreams:protogen": "substreams protogen ./substreams.yaml --exclude-paths='sf/substreams,google'", - "substreams:stream": "substreams run -e mainnet.eth.streamingfast.io:443 substreams.yaml graph_out -s 12292922 -t +10" - }, - "devDependencies": { - "@graphprotocol/graph-cli": "0.61.0" - } -} diff --git a/tests/runner-tests/substreams/proto/example.proto b/tests/runner-tests/substreams/proto/example.proto deleted file mode 100644 index ac4d80b2452..00000000000 --- a/tests/runner-tests/substreams/proto/example.proto +++ /dev/null @@ -1,14 +0,0 @@ -syntax = "proto3"; - -package example; - -message Contracts { - repeated Contract contracts = 1; -} - -message Contract { - string address = 1; - uint64 blockNumber = 2; - string timestamp = 3; - uint64 ordinal = 4; -} \ No newline at end of file diff --git a/tests/runner-tests/substreams/rust-toolchain.toml b/tests/runner-tests/substreams/rust-toolchain.toml deleted file mode 100644 index e2c33ff1c31..00000000000 --- a/tests/runner-tests/substreams/rust-toolchain.toml +++ /dev/null @@ -1,3 +0,0 @@ -[toolchain] -components = ["rustfmt"] -targets = ["wasm32-unknown-unknown"] diff --git a/tests/runner-tests/substreams/schema.graphql b/tests/runner-tests/substreams/schema.graphql deleted file mode 100644 index 7b1c7d114ed..00000000000 --- a/tests/runner-tests/substreams/schema.graphql +++ /dev/null @@ -1,9 +0,0 @@ -type Contract @entity { - id: ID! - - "The timestamp when the contract was deployed" - timestamp: String! - - "The block number of the contract deployment" - blockNumber: BigInt! -} diff --git a/tests/runner-tests/substreams/src/lib.rs b/tests/runner-tests/substreams/src/lib.rs deleted file mode 100644 index 0127d9aadd9..00000000000 --- a/tests/runner-tests/substreams/src/lib.rs +++ /dev/null @@ -1,40 +0,0 @@ -mod pb; - -use pb::example::{Contract, Contracts}; - -use substreams::Hex; -use substreams_entity_change::pb::entity::EntityChanges; -use substreams_entity_change::tables::Tables; -use substreams_ethereum::pb::eth; - -#[substreams::handlers::map] -fn map_contract(block: eth::v2::Block) -> Result { - let contracts = block - .calls() - .filter(|view| !view.call.state_reverted) - .filter(|view| view.call.call_type == eth::v2::CallType::Create as i32) - .map(|view| Contract { - address: format!("0x{}", Hex(&view.call.address)), - block_number: block.number, - timestamp: block.timestamp_seconds().to_string(), - ordinal: view.call.begin_ordinal, - }) - .collect(); - - Ok(Contracts { contracts }) -} - -#[substreams::handlers::map] -pub fn graph_out(contracts: Contracts) -> Result { - // hash map of name to a table - let mut tables = Tables::new(); - - for contract in contracts.contracts.into_iter() { - tables - .create_row("Contract", contract.address) - .set("timestamp", contract.timestamp) - .set("blockNumber", contract.block_number); - } - - Ok(tables.to_entity_changes()) -} diff --git a/tests/runner-tests/substreams/subgraph.yaml b/tests/runner-tests/substreams/subgraph.yaml deleted file mode 100644 index 377e326c568..00000000000 --- a/tests/runner-tests/substreams/subgraph.yaml +++ /dev/null @@ -1,16 +0,0 @@ -specVersion: 0.0.4 -description: Ethereum Contract Tracking Subgraph (powered by Substreams) -repository: https://github.com/graphprotocol/graph-tooling -schema: - file: schema.graphql -dataSources: - - kind: substreams - name: substream_test - network: test - source: - package: - moduleName: graph_out - file: substreams-test-v1.0.1.spkg - mapping: - kind: substreams/graph-entities - apiVersion: 0.0.6 diff --git a/tests/runner-tests/substreams/substreams-test-v1.0.1.spkg b/tests/runner-tests/substreams/substreams-test-v1.0.1.spkg deleted file mode 100644 index 641e2786a4a7cf754fd66c8efc56f5c3ba02161b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246354 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 z 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 Date: Wed, 7 Jan 2026 16:57:11 -0800 Subject: [PATCH 10/13] graph: Remove unused dependency humantime --- Cargo.lock | 1 - graph/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47706be430d..cae8894e939 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2850,7 +2850,6 @@ dependencies = [ "http 0.2.12", "http 1.4.0", "http-body-util", - "humantime", "hyper 1.8.1", "hyper-util", "indoc", diff --git a/graph/Cargo.toml b/graph/Cargo.toml index 58cfba024c2..e8149e51086 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -37,7 +37,6 @@ hyper-util = { version = "0.1", features = ["full"] } futures01 = { package = "futures", version = "0.1.31" } lru_time_cache = "0.11" graphql-parser = "0.4.1" -humantime = "2.3.0" lazy_static = "1.5.0" num-bigint = { version = "=0.2.6", features = ["serde"] } num-integer = { version = "=0.1.46" } From 71b64662267255a59576039e814f1809a6021c9f Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 7 Jan 2026 17:03:09 -0800 Subject: [PATCH 11/13] test-store, tests: Allow holding the test lock across await We need to eventually fix that, but that requires quite a bit of change. For now, we suppress the clippy warning. --- store/test-store/src/store.rs | 1 + tests/src/fixture/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/store/test-store/src/store.rs b/store/test-store/src/store.rs index a671e770a6f..5719caaa9ec 100644 --- a/store/test-store/src/store.rs +++ b/store/test-store/src/store.rs @@ -123,6 +123,7 @@ where } /// Run a test with a connection into the primary database, not a full store +#[allow(clippy::await_holding_lock)] pub async fn run_test_with_conn(test: F) where F: AsyncFnOnce(&mut AsyncPgConnection), diff --git a/tests/src/fixture/mod.rs b/tests/src/fixture/mod.rs index 06717fcad75..62000dc5e8e 100644 --- a/tests/src/fixture/mod.rs +++ b/tests/src/fixture/mod.rs @@ -360,6 +360,7 @@ fn test_logger(test_name: &str) -> Logger { graph::log::logger(true).new(o!("test" => test_name.to_string())) } +#[allow(clippy::await_holding_lock)] pub async fn stores(test_name: &str, store_config_path: &str) -> Stores { let _mutex_guard = STORE_MUTEX.lock().unwrap(); From 65e6fe284c6eb6a4fb61ed76754d7940f6193524 Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Wed, 7 Jan 2026 17:06:18 -0800 Subject: [PATCH 12/13] docs: Remove mention of substreams from config docs --- docs/config.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/config.md b/docs/config.md index feae397e911..8641398867c 100644 --- a/docs/config.md +++ b/docs/config.md @@ -112,7 +112,7 @@ The configuration for a chain `name` is specified in the section - `shard`: where chain data is stored - `protocol`: the protocol type being indexed, default `ethereum` -(alternatively `near`, `cosmos`,`arweave`,`starknet`) + (alternatively `near`, `cosmos`,`arweave`,`starknet`) - `polling_interval`: the polling interval for the block ingestor (default 500ms) - `provider`: a list of providers for that chain @@ -123,7 +123,7 @@ A `provider` is an object with the following characteristics: `details` includes the following: -- `type`: one of `web3` (default), `firehose`, `substreams` or `web3call` +- `type`: one of `web3` (default), `firehose`, or `web3call` - `transport`: one of `rpc`, `ws`, and `ipc`. Defaults to `rpc`. - `url`: the URL for the provider - `features`: an array of features that the provider supports, either empty @@ -135,8 +135,8 @@ A `provider` is an object with the following characteristics: otherwise `graph-node` might not be able to handle all subgraphs. The tracking for this is approximate, and a small amount of deviation from this value should be expected. The deviation will be less than 10. -- `token`: bearer token, for Firehose and Substreams providers -- `key`: API key for Firehose and Substreams providers when using key-based authentication +- `token`: bearer token, for Firehose providers +- `key`: API key for Firehose providers when using key-based authentication Note that for backwards compatibility, Web3 provider `details` can be specified at the "top level" of the `provider`. From 8bba01ef9cdda285a88c25dde99c127702ffbffe Mon Sep 17 00:00:00 2001 From: David Lutterkort Date: Thu, 8 Jan 2026 10:06:05 -0800 Subject: [PATCH 13/13] node: Make the it_fails_for_substreams test actually assert stuff --- node/src/config.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/node/src/config.rs b/node/src/config.rs index 7d21d10af77..0d781375c43 100644 --- a/node/src/config.rs +++ b/node/src/config.rs @@ -1463,12 +1463,15 @@ mod tests { #[test] fn it_fails_for_substreams() { - let _actual: Result = toml::from_str( + let actual: Result = toml::from_str( r#" label = "bananas" details = { type = "substreams", url = "http://localhost:9000", features = [] } "#, ); + assert!(actual.is_err()); + let err = actual.unwrap_err().to_string(); + assert!(err.contains("unknown variant `substreams`")); } #[test]