From da0363918b2157f405aed14e940dcf35970bce77 Mon Sep 17 00:00:00 2001 From: Andrea Simeoni Date: Wed, 4 Jan 2023 11:36:31 +0100 Subject: [PATCH] ci: mdbook (#2003) * Applied same book structure as Reth * ci: [WIP] added book workflow * fix warning mdbook version different from template plugin version * ci: added deploy step * Http provider docs * IPC provider docs * RetryClient docs * WebSocket provider docs Co-authored-by: Andrea Simeoni <> --- .github/workflows/book.yml | 61 +++++++++++++++++ Cargo.lock | 1 + book.toml | 27 ++++++++ {docs/mdbook/src => book}/SUMMARY.md | 8 +-- .../big-numbers/comparison-and-equivalence.md | 6 ++ book/big-numbers/conversion.md | 5 ++ book/big-numbers/creating_instances.md | 5 ++ book/big-numbers/intro.md | 1 + book/big-numbers/math-operations.md | 5 ++ book/big-numbers/utilities.md | 5 ++ .../connect_to_an_ethereum_node.md | 0 .../src => book}/getting-started/intro.md | 0 .../getting-started/start_a_new_project.md | 0 book/providers/http.md | 5 ++ book/providers/ipc.md | 5 ++ book/providers/retry.md | 5 ++ book/providers/ws.md | 5 ++ docs/mdbook/.gitignore | 1 - docs/mdbook/book.toml | 6 -- .../big-numbers/comparison-and-equivalence.md | 6 -- docs/mdbook/src/big-numbers/conversion.md | 5 -- .../src/big-numbers/creating_instances.md | 5 -- docs/mdbook/src/big-numbers/intro.md | 1 - .../mdbook/src/big-numbers/math-operations.md | 5 -- docs/mdbook/src/big-numbers/utilities.md | 5 -- examples/README.md | 6 +- examples/providers/Cargo.toml | 1 + examples/providers/examples/http.rs | 67 +++++++++++++++++++ examples/providers/examples/ipc.rs | 12 ++++ examples/providers/examples/retry.rs | 35 ++++++++++ examples/providers/examples/ws.rs | 51 ++++++++++++++ 31 files changed, 309 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/book.yml create mode 100644 book.toml rename {docs/mdbook/src => book}/SUMMARY.md (93%) create mode 100644 book/big-numbers/comparison-and-equivalence.md create mode 100644 book/big-numbers/conversion.md create mode 100644 book/big-numbers/creating_instances.md create mode 100644 book/big-numbers/intro.md create mode 100644 book/big-numbers/math-operations.md create mode 100644 book/big-numbers/utilities.md rename {docs/mdbook/src => book}/getting-started/connect_to_an_ethereum_node.md (100%) rename {docs/mdbook/src => book}/getting-started/intro.md (100%) rename {docs/mdbook/src => book}/getting-started/start_a_new_project.md (100%) create mode 100644 book/providers/http.md create mode 100644 book/providers/ipc.md create mode 100644 book/providers/retry.md create mode 100644 book/providers/ws.md delete mode 100644 docs/mdbook/.gitignore delete mode 100644 docs/mdbook/book.toml delete mode 100644 docs/mdbook/src/big-numbers/comparison-and-equivalence.md delete mode 100644 docs/mdbook/src/big-numbers/conversion.md delete mode 100644 docs/mdbook/src/big-numbers/creating_instances.md delete mode 100644 docs/mdbook/src/big-numbers/intro.md delete mode 100644 docs/mdbook/src/big-numbers/math-operations.md delete mode 100644 docs/mdbook/src/big-numbers/utilities.md create mode 100644 examples/providers/examples/http.rs create mode 100644 examples/providers/examples/retry.rs create mode 100644 examples/providers/examples/ws.rs diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml new file mode 100644 index 00000000..7e00a34e --- /dev/null +++ b/.github/workflows/book.yml @@ -0,0 +1,61 @@ +name: book +on: + push: + branches: [master] + paths: + - 'book/**' + - 'book.toml' + pull_request: + branches: [master] + paths: + - 'book/**' + - 'book.toml' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install mdbook + run: | + mkdir mdbook + curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.21/mdbook-v0.4.21-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + echo `pwd`/mdbook >> $GITHUB_PATH + + - name: Install mdbook-template + run: | + mkdir mdbook-template + curl -sSL https://github.com/sgoudham/mdbook-template/releases/latest/download/mdbook-template-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook-template + echo `pwd`/mdbook-template >> $GITHUB_PATH + + - name: Build + run: mdbook build + + - name: Save pages artifact + uses: actions/upload-pages-artifact@v1 + with: + path: target/book + + deploy: + # Only deploy if a push to master + if: github.ref_name == 'master' && github.event_name == 'push' + runs-on: ubuntu-latest + needs: [build] + + # Grant GITHUB_TOKEN the permissions required to make a Pages deployment + permissions: + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + # reference: https://github.com/actions/deploy-pages + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 75cf5208..cb472448 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1684,6 +1684,7 @@ version = "1.0.2" dependencies = [ "ethers", "eyre", + "reqwest", "serde", "serde_json", "tokio", diff --git a/book.toml b/book.toml new file mode 100644 index 00000000..469d4a38 --- /dev/null +++ b/book.toml @@ -0,0 +1,27 @@ +[book] +authors = ["The ethers-rs contributors"] +language = "en" +multilingual = false +src = "book" +title = "Ethers.rs: The Ethereum Library for Rust" +description = "A book on all things ethers-rs" + +[output.html] +git-repository-url = "https://github.com/gakonst/ethers-rs" +default-theme = "ayu" +no-section-label = true + +[output.html.fold] +enable = true +level = 1 + +[build] +build-dir = "target/book" + +[preprocessor.template] +before = [ "links" ] + +[preprocessor.index] + +[preprocessor.links] + diff --git a/docs/mdbook/src/SUMMARY.md b/book/SUMMARY.md similarity index 93% rename from docs/mdbook/src/SUMMARY.md rename to book/SUMMARY.md index 01c93f96..991ba0a9 100644 --- a/docs/mdbook/src/SUMMARY.md +++ b/book/SUMMARY.md @@ -7,13 +7,13 @@ # Reference guide - [Providers]() - - [Http]() - - [IPC]() + - [Http](./providers/http.md) + - [IPC](./providers/ipc.md) - [Mock]() - [Quorum]() - - [Retry]() + - [Retry](./providers/retry.md) - [RW]() - - [WS]() + - [WebSocket](./providers/ws.md) - [Middleware]() - [Builder]() - [Create custom middleware]() diff --git a/book/big-numbers/comparison-and-equivalence.md b/book/big-numbers/comparison-and-equivalence.md new file mode 100644 index 00000000..90f4d5b0 --- /dev/null +++ b/book/big-numbers/comparison-and-equivalence.md @@ -0,0 +1,6 @@ + +# Comparison and equivalence + +```rust +{{#include ../../examples/big-numbers/examples/comparison_equivalence.rs}} +``` diff --git a/book/big-numbers/conversion.md b/book/big-numbers/conversion.md new file mode 100644 index 00000000..34bc5b74 --- /dev/null +++ b/book/big-numbers/conversion.md @@ -0,0 +1,5 @@ +# Conversion + +```rust +{{#include ../../examples/big-numbers/examples/conversion.rs}} +``` diff --git a/book/big-numbers/creating_instances.md b/book/big-numbers/creating_instances.md new file mode 100644 index 00000000..5c8e3f57 --- /dev/null +++ b/book/big-numbers/creating_instances.md @@ -0,0 +1,5 @@ +# Creating instances + +```rust +{{#include ../../examples/big-numbers/examples/create_instances.rs}} +``` \ No newline at end of file diff --git a/book/big-numbers/intro.md b/book/big-numbers/intro.md new file mode 100644 index 00000000..eeedaca6 --- /dev/null +++ b/book/big-numbers/intro.md @@ -0,0 +1 @@ +{{#include ../../examples/big-numbers/README.md}} diff --git a/book/big-numbers/math-operations.md b/book/big-numbers/math-operations.md new file mode 100644 index 00000000..89a3007c --- /dev/null +++ b/book/big-numbers/math-operations.md @@ -0,0 +1,5 @@ +# Math operations + +```rust +{{#include ../../examples/big-numbers/examples/math_operations.rs}} +``` diff --git a/book/big-numbers/utilities.md b/book/big-numbers/utilities.md new file mode 100644 index 00000000..4bd23e68 --- /dev/null +++ b/book/big-numbers/utilities.md @@ -0,0 +1,5 @@ +# Utilities + +```rust +{{#include ../../examples/big-numbers/examples/utilities.rs}} +``` \ No newline at end of file diff --git a/docs/mdbook/src/getting-started/connect_to_an_ethereum_node.md b/book/getting-started/connect_to_an_ethereum_node.md similarity index 100% rename from docs/mdbook/src/getting-started/connect_to_an_ethereum_node.md rename to book/getting-started/connect_to_an_ethereum_node.md diff --git a/docs/mdbook/src/getting-started/intro.md b/book/getting-started/intro.md similarity index 100% rename from docs/mdbook/src/getting-started/intro.md rename to book/getting-started/intro.md diff --git a/docs/mdbook/src/getting-started/start_a_new_project.md b/book/getting-started/start_a_new_project.md similarity index 100% rename from docs/mdbook/src/getting-started/start_a_new_project.md rename to book/getting-started/start_a_new_project.md diff --git a/book/providers/http.md b/book/providers/http.md new file mode 100644 index 00000000..46e04a40 --- /dev/null +++ b/book/providers/http.md @@ -0,0 +1,5 @@ +# Http provider + +```rust +{{#include ../../examples/providers/examples/http.rs}} +``` \ No newline at end of file diff --git a/book/providers/ipc.md b/book/providers/ipc.md new file mode 100644 index 00000000..ad4dc5eb --- /dev/null +++ b/book/providers/ipc.md @@ -0,0 +1,5 @@ +# IPC provider + +```rust +{{#include ../../examples/providers/examples/ipc.rs}} +``` \ No newline at end of file diff --git a/book/providers/retry.md b/book/providers/retry.md new file mode 100644 index 00000000..1f9fd3eb --- /dev/null +++ b/book/providers/retry.md @@ -0,0 +1,5 @@ +# Retry client + +```rust +{{#include ../../examples/providers/examples/retry.rs}} +``` \ No newline at end of file diff --git a/book/providers/ws.md b/book/providers/ws.md new file mode 100644 index 00000000..ef6ca53a --- /dev/null +++ b/book/providers/ws.md @@ -0,0 +1,5 @@ +# WebSocket provider + +```rust +{{#include ../../examples/providers/examples/ws.rs}} +``` \ No newline at end of file diff --git a/docs/mdbook/.gitignore b/docs/mdbook/.gitignore deleted file mode 100644 index 7585238e..00000000 --- a/docs/mdbook/.gitignore +++ /dev/null @@ -1 +0,0 @@ -book diff --git a/docs/mdbook/book.toml b/docs/mdbook/book.toml deleted file mode 100644 index 5fb585f3..00000000 --- a/docs/mdbook/book.toml +++ /dev/null @@ -1,6 +0,0 @@ -[book] -authors = ["The ethers-rs contributors"] -language = "en" -multilingual = false -src = "src" -title = "Ethers.rs: The Ethereum Library for Rust" diff --git a/docs/mdbook/src/big-numbers/comparison-and-equivalence.md b/docs/mdbook/src/big-numbers/comparison-and-equivalence.md deleted file mode 100644 index a71d2d06..00000000 --- a/docs/mdbook/src/big-numbers/comparison-and-equivalence.md +++ /dev/null @@ -1,6 +0,0 @@ - -# Comparison and equivalence - -```rust -{{#include ../../../../examples/big-numbers/examples/comparison_equivalence.rs}} -``` diff --git a/docs/mdbook/src/big-numbers/conversion.md b/docs/mdbook/src/big-numbers/conversion.md deleted file mode 100644 index 1eacde55..00000000 --- a/docs/mdbook/src/big-numbers/conversion.md +++ /dev/null @@ -1,5 +0,0 @@ -# Conversion - -```rust -{{#include ../../../../examples/big-numbers/examples/conversion.rs}} -``` diff --git a/docs/mdbook/src/big-numbers/creating_instances.md b/docs/mdbook/src/big-numbers/creating_instances.md deleted file mode 100644 index 968ad59b..00000000 --- a/docs/mdbook/src/big-numbers/creating_instances.md +++ /dev/null @@ -1,5 +0,0 @@ -# Creating instances - -```rust -{{#include ../../../../examples/big-numbers/examples/create_instances.rs}} -``` \ No newline at end of file diff --git a/docs/mdbook/src/big-numbers/intro.md b/docs/mdbook/src/big-numbers/intro.md deleted file mode 100644 index 7c6afdce..00000000 --- a/docs/mdbook/src/big-numbers/intro.md +++ /dev/null @@ -1 +0,0 @@ -{{#include ../../../../examples/big-numbers/README.md}} diff --git a/docs/mdbook/src/big-numbers/math-operations.md b/docs/mdbook/src/big-numbers/math-operations.md deleted file mode 100644 index d393d552..00000000 --- a/docs/mdbook/src/big-numbers/math-operations.md +++ /dev/null @@ -1,5 +0,0 @@ -# Math operations - -```rust -{{#include ../../../../examples/big-numbers/examples/math_operations.rs}} -``` diff --git a/docs/mdbook/src/big-numbers/utilities.md b/docs/mdbook/src/big-numbers/utilities.md deleted file mode 100644 index c1a757c1..00000000 --- a/docs/mdbook/src/big-numbers/utilities.md +++ /dev/null @@ -1,5 +0,0 @@ -# Utilities - -```rust -{{#include ../../../../examples/big-numbers/examples/utilities.rs}} -``` \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index 87fbfc19..8bc4d73d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -35,13 +35,13 @@ - [ ] Time lag - [ ] Transformer - [ ] Providers - - [ ] Http + - [x] Http - [x] IPC - [ ] Mock - [x] Quorum - - [ ] Retry + - [x] Retry - [x] RW - - [ ] WS + - [x] WS - [ ] Queries - [ ] Blocks - [x] Contracts diff --git a/examples/providers/Cargo.toml b/examples/providers/Cargo.toml index 71a92210..337e4e1d 100644 --- a/examples/providers/Cargo.toml +++ b/examples/providers/Cargo.toml @@ -12,6 +12,7 @@ ipc = [] ethers = { path = "../..", version = "1.0.0", features = ["abigen", "ipc", "rustls", "ws"] } eyre = "0.6" +reqwest = { version = "0.11.13", default-features = false } serde = { version = "1.0.144", features = ["derive"] } serde_json = "1.0.64" tokio = { version = "1.18", features = ["macros"] } diff --git a/examples/providers/examples/http.rs b/examples/providers/examples/http.rs new file mode 100644 index 00000000..ff45119f --- /dev/null +++ b/examples/providers/examples/http.rs @@ -0,0 +1,67 @@ +use ethers::prelude::*; +use reqwest::header::{HeaderMap, HeaderValue}; +use std::sync::Arc; + +const RPC_URL: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; + +/// The Http transport is used to send JSON-RPC requests over Http to an +/// Ethereum node. It allows you to perform various actions on the Ethereum blockchain, such as +/// reading and writing data, sending transactions, and more. To use the Http transport, you will +/// need to create a new `Provider` instance as described in this example. +#[tokio::main] +async fn main() -> eyre::Result<()> { + create_instance().await?; + share_providers_across_tasks().await?; + Ok(()) +} + +async fn create_instance() -> eyre::Result<()> { + // An Http provider can be created from an http(s) URI. + // In case of https you must add the "rustls" or "openssl" feature + // to the ethers library dependency in `Cargo.toml`. + let _provider = Provider::::try_from(RPC_URL)?; + + // Instantiate with auth to append basic authorization headers across requests + let url = reqwest::Url::parse(RPC_URL)?; + let auth = Authorization::basic("username", "password"); + let _provider = Http::new_with_auth(url, auth)?; + + // Instantiate from custom Http Client if you need + // finer control over the Http client configuration + // (TLS, Proxy, Cookies, Headers, etc.) + let url = reqwest::Url::parse(RPC_URL)?; + + let mut headers = HeaderMap::new(); + headers.insert("Authorization", HeaderValue::from_static("Bearer my token")); + headers.insert("X-MY-HEADERS", HeaderValue::from_static("Some value")); + + let http_client = reqwest::Client::builder() + .default_headers(headers) + .proxy(reqwest::Proxy::all("http://proxy.example.com:8080")?) + .build()?; + + let _provider = Http::new_with_client(url, http_client); + + Ok(()) +} + +/// Providers can be easily shared across tasks using `Arc` smart pointers +async fn share_providers_across_tasks() -> eyre::Result<()> { + let provider: Provider = Provider::::try_from(RPC_URL)?; + + let client_1 = Arc::new(provider); + let client_2 = Arc::clone(&client_1); + + let handle1 = + tokio::spawn(async move { client_1.get_block(BlockNumber::Latest).await.unwrap_or(None) }); + + let handle2 = + tokio::spawn(async move { client_2.get_block(BlockNumber::Latest).await.unwrap_or(None) }); + + let block1: Option> = handle1.await?; + let block2: Option> = handle2.await?; + + println!("{block1:?} {block2:?}"); + + Ok(()) +} diff --git a/examples/providers/examples/ipc.rs b/examples/providers/examples/ipc.rs index 048f0f3e..89fe05f1 100644 --- a/examples/providers/examples/ipc.rs +++ b/examples/providers/examples/ipc.rs @@ -1,11 +1,23 @@ +/// The IPC (Inter-Process Communication) transport is a way for a process to communicate with a +/// running Ethereum client over a local Unix domain socket. Using the IPC transport allows the +/// ethers library to send JSON-RPC requests to the Ethereum client and receive responses, without +/// the need for a network connection or HTTP server. This can be useful for interacting with a +/// local Ethereum node that is running on the same machine. #[tokio::main] #[cfg(feature = "ipc")] async fn main() -> eyre::Result<()> { use ethers::prelude::*; + // We instantiate the provider using the path of a local Unix domain socket + // -------------------------------------------------------------------------------- + // NOTE: The IPC transport supports push notifications, but we still need to specify a polling + // interval because only subscribe RPC calls (e.g., transactions, blocks, events) support push + // notifications in Ethereum's RPC API. For other calls we must use repeated polling for many + // operations even with the IPC transport. let provider = Provider::connect_ipc("~/.ethereum/geth.ipc") .await? .interval(std::time::Duration::from_millis(2000)); + let block = provider.get_block_number().await?; println!("Current block: {block}"); let mut stream = provider.watch_blocks().await?.stream(); diff --git a/examples/providers/examples/retry.rs b/examples/providers/examples/retry.rs new file mode 100644 index 00000000..9146bd84 --- /dev/null +++ b/examples/providers/examples/retry.rs @@ -0,0 +1,35 @@ +use ethers::prelude::*; +use reqwest::Url; +use std::time::Duration; + +const RPC_URL: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; + +/// The RetryClient is a type that wraps around a JsonRpcClient and automatically retries failed +/// requests using an exponential backoff and filtering based on a RetryPolicy. It presents as a +/// JsonRpcClient, but with additional functionality for retrying requests. +/// +/// The RetryPolicy can be customized for specific applications and endpoints, mainly to handle +/// rate-limiting errors. In addition to the RetryPolicy, errors caused by connectivity issues such +/// as timed out connections or responses in the 5xx range can also be retried separately. +#[tokio::main] +async fn main() -> eyre::Result<()> { + let provider = Http::new(Url::parse(RPC_URL)?); + + let client = RetryClientBuilder::default() + .rate_limit_retries(10) + .timeout_retries(3) + .initial_backoff(Duration::from_millis(500)) + .build(provider, Box::new(HttpRateLimitRetryPolicy::default())); + + // Send a JSON-RPC request for the latest block + let block_num = "latest".to_string(); + let txn_details = false; + let params = (block_num, txn_details); + + let block: Block = + JsonRpcClient::request(&client, "eth_getBlockByNumber", params).await?; + + println!("{block:?}"); + + Ok(()) +} diff --git a/examples/providers/examples/ws.rs b/examples/providers/examples/ws.rs new file mode 100644 index 00000000..9268ce38 --- /dev/null +++ b/examples/providers/examples/ws.rs @@ -0,0 +1,51 @@ +use std::time::Duration; + +use ethers::prelude::*; + +const WSS_URL: &str = "wss://mainnet.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27"; + +type BoxErr = Box; + +/// The Ws transport allows you to send JSON-RPC requests and receive responses over WebSocket +/// connections. It is useful for connecting to Ethereum nodes that support WebSockets. +/// This allows to interact with the Ethereum network in real-time without the need for HTTP +/// polling. +#[tokio::main] +async fn main() -> Result<(), BoxErr> { + create_instance().await?; + watch_blocks().await?; + Ok(()) +} + +async fn create_instance() -> Result<(), BoxErr> { + // An Ws provider can be created from an ws(s) URI. + // In case of wss you must add the "rustls" or "openssl" feature + // to the ethers library dependency in `Cargo.toml`. + //------------------------------------------------------------------------------------------ + // NOTE: The Ws transport supports push notifications, but we still need to specify a polling + // interval because only subscribe RPC calls (e.g., transactions, blocks, events) support push + // notifications in Ethereum's RPC API. For other calls we must use repeated polling for many + // operations even with the Ws transport. + let _provider = Provider::::connect(WSS_URL).await?.interval(Duration::from_millis(500)); + + // Instantiate with auth to send basic authorization headers on connection. + let url = reqwest::Url::parse(WSS_URL)?; + let auth = Authorization::basic("username", "password"); + if let Ok(_provider) = Provider::::connect_with_auth(url, auth).await { + println!("Create Ws provider with auth"); + } + + Ok(()) +} + +/// Let's show how the Ws connection enables listening for blocks using a persistent TCP connection +async fn watch_blocks() -> Result<(), BoxErr> { + let provider = Provider::::connect(WSS_URL).await?; + let mut stream = provider.watch_blocks().await?.take(1); + + while let Some(block_hash) = stream.next().await { + println!("{block_hash:?}"); + } + + Ok(()) +}