feat: rotating infura keys (#1017)

* feat: add providers which rotate api keys

* test: use rotating key in provider instead of hardcoded

* chore: fmt
This commit is contained in:
Georgios Konstantopoulos 2022-03-13 18:04:09 +02:00 committed by GitHub
parent 367f3444ec
commit ae125bcfc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 33 deletions

1
Cargo.lock generated
View File

@ -1322,6 +1322,7 @@ dependencies = [
"futures-util", "futures-util",
"hex", "hex",
"http", "http",
"once_cell",
"parking_lot", "parking_lot",
"pin-project", "pin-project",
"reqwest", "reqwest",

View File

@ -4,14 +4,11 @@
async fn nonce_manager() { async fn nonce_manager() {
use ethers_core::types::*; use ethers_core::types::*;
use ethers_middleware::{nonce_manager::NonceManagerMiddleware, signer::SignerMiddleware}; use ethers_middleware::{nonce_manager::NonceManagerMiddleware, signer::SignerMiddleware};
use ethers_providers::{Http, Middleware, Provider}; use ethers_providers::Middleware;
use ethers_signers::{LocalWallet, Signer}; use ethers_signers::{LocalWallet, Signer};
use std::{convert::TryFrom, time::Duration}; use std::time::Duration;
let provider = let provider = ethers_providers::RINKEBY.provider().interval(Duration::from_millis(2000u64));
Provider::<Http>::try_from("https://rinkeby.infura.io/v3/fd8b88b56aa84f6da87b60f5441d6778")
.unwrap()
.interval(Duration::from_millis(2000u64));
let chain_id = provider.get_chainid().await.unwrap().as_u64(); let chain_id = provider.get_chainid().await.unwrap().as_u64();
let wallet = std::env::var("RINKEBY_PRIVATE_KEY") let wallet = std::env::var("RINKEBY_PRIVATE_KEY")

View File

@ -1,5 +1,5 @@
#![allow(unused)] #![allow(unused)]
use ethers_providers::{Http, JsonRpcClient, Middleware, Provider}; use ethers_providers::{Http, JsonRpcClient, Middleware, Provider, RINKEBY};
use ethers_core::{ use ethers_core::{
types::{BlockNumber, TransactionRequest}, types::{BlockNumber, TransactionRequest},
@ -8,7 +8,7 @@ use ethers_core::{
use ethers_middleware::signer::SignerMiddleware; use ethers_middleware::signer::SignerMiddleware;
use ethers_signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer}; use ethers_signers::{coins_bip39::English, LocalWallet, MnemonicBuilder, Signer};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::{convert::TryFrom, sync::atomic::AtomicU8, time::Duration}; use std::{convert::TryFrom, iter::Cycle, sync::atomic::AtomicU8, time::Duration};
static WALLETS: Lazy<TestWallets> = Lazy::new(|| { static WALLETS: Lazy<TestWallets> = Lazy::new(|| {
TestWallets { TestWallets {
@ -54,10 +54,7 @@ async fn send_eth() {
#[tokio::test] #[tokio::test]
#[cfg(not(feature = "celo"))] #[cfg(not(feature = "celo"))]
async fn pending_txs_with_confirmations_testnet() { async fn pending_txs_with_confirmations_testnet() {
let provider = let provider = RINKEBY.provider().interval(Duration::from_millis(3000));
Provider::<Http>::try_from("https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27")
.unwrap()
.interval(Duration::from_millis(3000));
let chain_id = provider.get_chainid().await.unwrap(); let chain_id = provider.get_chainid().await.unwrap();
let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); let wallet = WALLETS.next().with_chain_id(chain_id.as_u64());
let address = wallet.address(); let address = wallet.address();
@ -97,9 +94,7 @@ async fn generic_pending_txs_test<M: Middleware>(provider: M, who: Address) {
#[tokio::test] #[tokio::test]
#[cfg(not(feature = "celo"))] #[cfg(not(feature = "celo"))]
async fn typed_txs() { async fn typed_txs() {
let provider = let provider = RINKEBY.provider();
Provider::<Http>::try_from("https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27")
.unwrap();
let chain_id = provider.get_chainid().await.unwrap(); let chain_id = provider.get_chainid().await.unwrap();
let wallet = WALLETS.next().with_chain_id(chain_id.as_u64()); let wallet = WALLETS.next().with_chain_id(chain_id.as_u64());

View File

@ -39,6 +39,7 @@ tracing = { version = "0.1.32", default-features = false }
tracing-futures = { version = "0.2.5", default-features = false, features = ["std-future"] } tracing-futures = { version = "0.2.5", default-features = false, features = ["std-future"] }
bytes = { version = "1.1.0", default-features = false, optional = true } bytes = { version = "1.1.0", default-features = false, optional = true }
once_cell = "1.10.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
# tokio # tokio

View File

@ -657,3 +657,53 @@ pub trait CeloMiddleware: Middleware {
self.provider().get_validators_bls_public_keys(block_id).await.map_err(FromErr::from) self.provider().get_validators_bls_public_keys(block_id).await.map_err(FromErr::from)
} }
} }
pub use test_provider::{GOERLI, MAINNET, RINKEBY, ROPSTEN};
/// Pre-instantiated Infura HTTP clients which rotate through multiple API keys
/// to prevent rate limits
pub mod test_provider {
use super::*;
use crate::Http;
use once_cell::sync::Lazy;
use std::{convert::TryFrom, iter::Cycle, slice::Iter, sync::Mutex};
// List of infura keys to rotate through so we don't get rate limited
const INFURA_KEYS: &[&str] = &[
"6770454bc6ea42c58aac12978531b93f",
"7a8769b798b642f6933f2ed52042bd70",
"631fd9a6539644088297dc605d35fff3",
"16a8be88795540b9b3903d8de0f7baa5",
"f4a0bdad42674adab5fc0ac077ffab2b",
"5c812e02193c4ba793f8c214317582bd",
];
pub static RINKEBY: Lazy<TestProvider> =
Lazy::new(|| TestProvider::new(INFURA_KEYS, "rinkeby"));
pub static MAINNET: Lazy<TestProvider> =
Lazy::new(|| TestProvider::new(INFURA_KEYS, "mainnet"));
pub static GOERLI: Lazy<TestProvider> = Lazy::new(|| TestProvider::new(INFURA_KEYS, "goerli"));
pub static ROPSTEN: Lazy<TestProvider> =
Lazy::new(|| TestProvider::new(INFURA_KEYS, "ropsten"));
#[derive(Debug)]
pub struct TestProvider {
network: String,
keys: Mutex<Cycle<Iter<'static, &'static str>>>,
}
impl TestProvider {
pub fn new(keys: &'static [&'static str], network: &str) -> Self {
Self { keys: Mutex::new(keys.iter().cycle()), network: network.to_owned() }
}
pub fn provider(&self) -> Provider<Http> {
let url = format!(
"https://{}.infura.io/v3/{}",
self.network,
self.keys.lock().unwrap().next().unwrap()
);
Provider::try_from(url.as_str()).unwrap()
}
}
}

View File

@ -1488,12 +1488,10 @@ mod tests {
}; };
use futures_util::StreamExt; use futures_util::StreamExt;
const INFURA: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27";
#[tokio::test] #[tokio::test]
// Test vector from: https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#id2 // Test vector from: https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#id2
async fn mainnet_resolve_name() { async fn mainnet_resolve_name() {
let provider = Provider::<HttpProvider>::try_from(INFURA).unwrap(); let provider = crate::test_provider::MAINNET.provider();
let addr = provider.resolve_name("registrar.firefly.eth").await.unwrap(); let addr = provider.resolve_name("registrar.firefly.eth").await.unwrap();
assert_eq!(addr, "6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap()); assert_eq!(addr, "6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap());
@ -1508,7 +1506,7 @@ mod tests {
#[tokio::test] #[tokio::test]
// Test vector from: https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#id2 // Test vector from: https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#id2
async fn mainnet_lookup_address() { async fn mainnet_lookup_address() {
let provider = Provider::<HttpProvider>::try_from(INFURA).unwrap(); let provider = crate::MAINNET.provider();
let name = provider let name = provider
.lookup_address("6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap()) .lookup_address("6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap())
@ -1525,7 +1523,7 @@ mod tests {
#[tokio::test] #[tokio::test]
async fn mainnet_resolve_avatar() { async fn mainnet_resolve_avatar() {
let provider = Provider::<HttpProvider>::try_from(INFURA).unwrap(); let provider = crate::MAINNET.provider();
for (ens_name, res) in &[ for (ens_name, res) in &[
// HTTPS // HTTPS

View File

@ -1,5 +1,5 @@
#![cfg(not(target_arch = "wasm32"))] #![cfg(not(target_arch = "wasm32"))]
use ethers_providers::{Http, Middleware, Provider}; use ethers_providers::{Http, Middleware, Provider, RINKEBY};
use std::{convert::TryFrom, time::Duration}; use std::{convert::TryFrom, time::Duration};
#[cfg(not(feature = "celo"))] #[cfg(not(feature = "celo"))]
@ -12,10 +12,7 @@ mod eth_tests {
#[tokio::test] #[tokio::test]
async fn non_existing_data_works() { async fn non_existing_data_works() {
let provider = Provider::<Http>::try_from( let provider = RINKEBY.provider();
"https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27",
)
.unwrap();
assert!(provider.get_transaction(H256::zero()).await.unwrap().is_none()); assert!(provider.get_transaction(H256::zero()).await.unwrap().is_none());
assert!(provider.get_transaction_receipt(H256::zero()).await.unwrap().is_none()); assert!(provider.get_transaction_receipt(H256::zero()).await.unwrap().is_none());
@ -25,10 +22,7 @@ mod eth_tests {
#[tokio::test] #[tokio::test]
async fn client_version() { async fn client_version() {
let provider = Provider::<Http>::try_from( let provider = RINKEBY.provider();
"https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27",
)
.unwrap();
// e.g., Geth/v1.10.6-omnibus-1af33248/linux-amd64/go1.16.6 // e.g., Geth/v1.10.6-omnibus-1af33248/linux-amd64/go1.16.6
assert!(provider assert!(provider
@ -95,10 +89,7 @@ mod eth_tests {
#[tokio::test] #[tokio::test]
async fn eip1559_fee_estimation() { async fn eip1559_fee_estimation() {
let provider = Provider::<Http>::try_from( let provider = ethers_providers::MAINNET.provider();
"https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27",
)
.unwrap();
let (_max_fee_per_gas, _max_priority_fee_per_gas) = let (_max_fee_per_gas, _max_priority_fee_per_gas) =
provider.estimate_eip1559_fees(None).await.unwrap(); provider.estimate_eip1559_fees(None).await.unwrap();