From 9dca606eaf3c6d2aa361198c756a1f1866d48e9a Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Thu, 29 Jul 2021 23:22:25 +0300 Subject: [PATCH] fix: make chain_id mandatory (#286) * chore: update deps (#352) * chore(deps): bump elliptic-curve from 0.10.4 to 0.10.5 Bumps [elliptic-curve](https://github.com/RustCrypto/traits) from 0.10.4 to 0.10.5. - [Release notes](https://github.com/RustCrypto/traits/releases) - [Commits](https://github.com/RustCrypto/traits/compare/elliptic-curve-v0.10.4...elliptic-curve-v0.10.5) --- updated-dependencies: - dependency-name: elliptic-curve dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: bump deps Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(core): make chain id mandatory * fix(signers): make chain id mandatory * test: make chain id mandatory * test: add missing chain id * fix: add missing chain id * chore(wallet): set chain_id by default to 1 * ci: run CI on master * fix(yubi): add missing chain id * chore: skip ganache test with celo features * ci: run only on push to master * fix: add missing chain id Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 12 ++++-- Cargo.lock | 3 -- ethers-contract/tests/common/mod.rs | 3 +- ethers-contract/tests/contract.rs | 14 +++---- ethers-core/Cargo.toml | 1 + ethers-core/src/types/transaction.rs | 43 ++++++--------------- ethers-middleware/Cargo.toml | 1 - ethers-middleware/src/lib.rs | 2 +- ethers-middleware/src/signer.rs | 4 +- ethers-middleware/tests/gas_escalator.rs | 2 +- ethers-middleware/tests/nonce_manager.rs | 6 ++- ethers-middleware/tests/signer.rs | 8 +++- ethers-middleware/tests/stack.rs | 4 +- ethers-middleware/tests/transformer.rs | 6 ++- ethers-providers/tests/provider.rs | 10 +++-- ethers-signers/Cargo.toml | 1 - ethers-signers/src/ledger/app.rs | 29 +++++--------- ethers-signers/src/ledger/mod.rs | 11 +++++- ethers-signers/src/lib.rs | 17 ++++---- ethers-signers/src/wallet/mnemonic.rs | 2 +- ethers-signers/src/wallet/mod.rs | 49 +++++++++++++----------- ethers-signers/src/wallet/private_key.rs | 14 +++---- ethers-signers/src/wallet/yubi.rs | 2 +- ethers/examples/ledger.rs | 2 +- 24 files changed, 123 insertions(+), 123 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c273c5e..83fed86c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,8 @@ -on: pull_request +on: + push: + branches: + - master + pull_request: name: Tests @@ -10,7 +14,7 @@ env: jobs: tests: - name: Check + name: ethereum tests runs-on: ubuntu-latest steps: - name: Checkout sources @@ -51,7 +55,7 @@ jobs: cargo test feature-tests: - name: Check + name: celo tests runs-on: ubuntu-latest steps: - name: Checkout sources @@ -95,7 +99,7 @@ jobs: cargo test --all-features lint: - name: Check + name: lints runs-on: ubuntu-latest steps: - name: Checkout sources diff --git a/Cargo.lock b/Cargo.lock index f769b061..3aca9dc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -830,7 +830,6 @@ dependencies = [ "ethers-core", "ethers-providers", "ethers-signers", - "futures-executor", "futures-util", "hex", "rand 0.8.4", @@ -889,7 +888,6 @@ dependencies = [ "futures-util", "hex", "rand 0.8.4", - "serde_json", "sha2 0.9.5", "tempfile", "thiserror", @@ -1002,7 +1000,6 @@ dependencies = [ "futures-core", "futures-task", "futures-util", - "num_cpus", ] [[package]] diff --git a/ethers-contract/tests/common/mod.rs b/ethers-contract/tests/common/mod.rs index ea1ffca9..8404d038 100644 --- a/ethers-contract/tests/common/mod.rs +++ b/ethers-contract/tests/common/mod.rs @@ -8,7 +8,7 @@ use ethers_contract::{Contract, ContractFactory, EthEvent}; use ethers_core::utils::{GanacheInstance, Solc}; use ethers_middleware::signer::SignerMiddleware; use ethers_providers::{Http, Middleware, Provider}; -use ethers_signers::LocalWallet; +use ethers_signers::{LocalWallet, Signer}; use std::{convert::TryFrom, sync::Arc, time::Duration}; // Note: The `EthEvent` derive macro implements the necessary conversion between `Tokens` and @@ -40,6 +40,7 @@ pub fn connect(ganache: &GanacheInstance, idx: usize) -> Arc { .unwrap() .interval(Duration::from_millis(10u64)); let wallet: LocalWallet = ganache.keys()[idx].clone().into(); + let wallet = wallet.with_chain_id(1u64); Arc::new(SignerMiddleware::new(provider, wallet)) } diff --git a/ethers-contract/tests/contract.rs b/ethers-contract/tests/contract.rs index 059d0281..baa335fd 100644 --- a/ethers-contract/tests/contract.rs +++ b/ethers-contract/tests/contract.rs @@ -97,12 +97,11 @@ mod eth_tests { let contract = deploy(client.clone(), abi, bytecode).await; // make a call with `client` - let _tx_hash = *contract + let func = contract .method::<_, H256>("setValue", "hi".to_owned()) - .unwrap() - .send() - .await .unwrap(); + let tx = func.send().await.unwrap(); + let _receipt = tx.await.unwrap(); // and we can fetch the events let logs: Vec = contract @@ -504,8 +503,8 @@ mod celo_tests { use super::*; use ethers::{ middleware::signer::SignerMiddleware, - providers::{Http, Provider}, - signers::LocalWallet, + providers::{Http, Middleware, Provider}, + signers::{LocalWallet, Signer}, types::BlockNumber, }; use std::{convert::TryFrom, sync::Arc, time::Duration}; @@ -518,12 +517,13 @@ mod celo_tests { let provider = Provider::::try_from("https://alfajores-forno.celo-testnet.org") .unwrap() .interval(Duration::from_millis(6000)); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); // Funded with https://celo.org/developers/faucet let wallet = "d652abb81e8c686edba621a895531b1f291289b63b5ef09a94f686a5ecdd5db1" .parse::() .unwrap() - .set_chain_id(44787u64); + .with_chain_id(chain_id); let client = SignerMiddleware::new(provider, wallet); let client = Arc::new(client); diff --git a/ethers-core/Cargo.toml b/ethers-core/Cargo.toml index 102bcfe9..26151cc7 100644 --- a/ethers-core/Cargo.toml +++ b/ethers-core/Cargo.toml @@ -45,6 +45,7 @@ serde_json = { version = "1.0.64", default-features = false } bincode = { version = "1.3.3", default-features = false } once_cell = { version = "1.8.0" } hex-literal = "0.3.3" +futures-util = { version = "0.3.16", default-features = false } [features] celo = [] # celo support extends the transaction format with extra fields diff --git a/ethers-core/src/types/transaction.rs b/ethers-core/src/types/transaction.rs index fc3bbc21..d32f56a6 100644 --- a/ethers-core/src/types/transaction.rs +++ b/ethers-core/src/types/transaction.rs @@ -7,15 +7,14 @@ use crate::{ use rlp::RlpStream; use serde::{Deserialize, Serialize}; +const BASE_NUM_TX_FIELDS: usize = 9; + // Number of tx fields before signing #[cfg(not(feature = "celo"))] -const UNSIGNED_TX_FIELDS: usize = 6; +const NUM_TX_FIELDS: usize = BASE_NUM_TX_FIELDS; // Celo has 3 additional fields #[cfg(feature = "celo")] -const UNSIGNED_TX_FIELDS: usize = 9; - -// Unsigned fields + signature [r s v] -const SIGNED_TX_FIELDS: usize = UNSIGNED_TX_FIELDS + 3; +const NUM_TX_FIELDS: usize = BASE_NUM_TX_FIELDS + 3; /// Parameters for sending a transaction #[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Debug)] @@ -130,52 +129,34 @@ impl TransactionRequest { } /// Hashes the transaction's data with the provided chain id - pub fn sighash>(&self, chain_id: Option) -> H256 { + pub fn sighash>(&self, chain_id: T) -> H256 { keccak256(self.rlp(chain_id).as_ref()).into() } /// Gets the unsigned transaction's RLP encoding - pub fn rlp>(&self, chain_id: Option) -> Bytes { + pub fn rlp>(&self, chain_id: T) -> Bytes { let mut rlp = RlpStream::new(); - // "If [..] CHAIN_ID is available, then when computing the hash of a - // transaction for the purposes of signing, instead of hashing only - // six rlp encoded elements (nonce, gasprice, startgas, to, value, data), - // you SHOULD hash nine rlp encoded elements - // (nonce, gasprice, startgas, to, value, data, chainid, 0, 0)" - // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md#specification - let num_els = if chain_id.is_some() { - UNSIGNED_TX_FIELDS + 3 - } else { - UNSIGNED_TX_FIELDS - }; - - rlp.begin_list(num_els); + rlp.begin_list(NUM_TX_FIELDS); self.rlp_base(&mut rlp); // Only hash the 3 extra fields when preparing the // data to sign if chain_id is present - if let Some(chain_id) = chain_id { - rlp.append(&chain_id.into()); - rlp.append(&0u8); - rlp.append(&0u8); - } - + rlp.append(&chain_id.into()); + rlp.append(&0u8); + rlp.append(&0u8); rlp.out().freeze().into() } /// Produces the RLP encoding of the transaction with the provided signature pub fn rlp_signed(&self, signature: &Signature) -> Bytes { let mut rlp = RlpStream::new(); - - // construct the RLP body - rlp.begin_list(SIGNED_TX_FIELDS); + rlp.begin_list(NUM_TX_FIELDS); self.rlp_base(&mut rlp); // append the signature rlp.append(&signature.v); rlp.append(&signature.r); rlp.append(&signature.s); - rlp.out().freeze().into() } @@ -326,7 +307,7 @@ impl Transaction { pub fn rlp(&self) -> Bytes { let mut rlp = RlpStream::new(); - rlp.begin_list(SIGNED_TX_FIELDS); + rlp.begin_list(NUM_TX_FIELDS); rlp.append(&self.nonce); rlp.append(&self.gas_price); rlp.append(&self.gas); diff --git a/ethers-middleware/Cargo.toml b/ethers-middleware/Cargo.toml index ee50a813..35e1d755 100644 --- a/ethers-middleware/Cargo.toml +++ b/ethers-middleware/Cargo.toml @@ -36,7 +36,6 @@ tokio = { version = "1.5" } [dev-dependencies] ethers = { version = "0.4.0", path = "../ethers" } -futures-executor = { version = "0.3.14", features = ["thread-pool"] } hex = { version = "0.4.3", default-features = false, features = ["std"] } rand = { version = "0.8.4", default-features = false } tokio = { version = "1.5", default-features = false, features = ["rt", "macros", "time"] } diff --git a/ethers-middleware/src/lib.rs b/ethers-middleware/src/lib.rs index 9b7fe7d7..c779a042 100644 --- a/ethers-middleware/src/lib.rs +++ b/ethers-middleware/src/lib.rs @@ -21,7 +21,7 @@ //! ```no_run //! use ethers::{ //! providers::{Provider, Http}, -//! signers::LocalWallet, +//! signers::{LocalWallet, Signer}, //! middleware::{ //! gas_escalator::{GasEscalatorMiddleware, GeometricGasPrice, Frequency}, //! gas_oracle::{GasOracleMiddleware, GasNow, GasCategory}, diff --git a/ethers-middleware/src/signer.rs b/ethers-middleware/src/signer.rs index acda5427..7bd5d272 100644 --- a/ethers-middleware/src/signer.rs +++ b/ethers-middleware/src/signer.rs @@ -343,7 +343,7 @@ mod tests { let key = "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318" .parse::() .unwrap() - .set_chain_id(chain_id); + .with_chain_id(chain_id); let client = SignerMiddleware::new(provider, key); let tx = client.sign_transaction(tx).await.unwrap(); @@ -365,7 +365,7 @@ mod tests { // new SignerMiddleware let provider = Provider::try_from("http://localhost:8545").unwrap(); - let key = LocalWallet::new(&mut rand::thread_rng()); + let key = LocalWallet::new(&mut rand::thread_rng()).with_chain_id(1u32); let client = SignerMiddleware::new(provider, key); // an address that is not the signer address diff --git a/ethers-middleware/tests/gas_escalator.rs b/ethers-middleware/tests/gas_escalator.rs index 2ce00d04..ccf56256 100644 --- a/ethers-middleware/tests/gas_escalator.rs +++ b/ethers-middleware/tests/gas_escalator.rs @@ -4,7 +4,7 @@ use ethers_middleware::{ signer::SignerMiddleware, }; use ethers_providers::{Middleware, Provider, Ws}; -use ethers_signers::LocalWallet; +use ethers_signers::{LocalWallet, Signer}; use std::time::Duration; #[tokio::test] diff --git a/ethers-middleware/tests/nonce_manager.rs b/ethers-middleware/tests/nonce_manager.rs index 6348590f..a43962cd 100644 --- a/ethers-middleware/tests/nonce_manager.rs +++ b/ethers-middleware/tests/nonce_manager.rs @@ -4,7 +4,7 @@ async fn nonce_manager() { use ethers_core::types::*; use ethers_middleware::{nonce_manager::NonceManagerMiddleware, signer::SignerMiddleware}; use ethers_providers::{Http, Middleware, Provider}; - use ethers_signers::LocalWallet; + use ethers_signers::{LocalWallet, Signer}; use std::convert::TryFrom; use std::time::Duration; @@ -12,10 +12,12 @@ async fn nonce_manager() { Provider::::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 wallet = "59c37cb6b16fa2de30675f034c8008f890f4b2696c729d6267946d29736d73e4" .parse::() - .unwrap(); + .unwrap() + .with_chain_id(chain_id); let address = wallet.address(); let provider = SignerMiddleware::new(provider, wallet); diff --git a/ethers-middleware/tests/signer.rs b/ethers-middleware/tests/signer.rs index 0c7419f7..75c534c4 100644 --- a/ethers-middleware/tests/signer.rs +++ b/ethers-middleware/tests/signer.rs @@ -2,7 +2,7 @@ use ethers_providers::{Http, Middleware, Provider}; use ethers_core::types::TransactionRequest; use ethers_middleware::signer::SignerMiddleware; -use ethers_signers::LocalWallet; +use ethers_signers::{LocalWallet, Signer}; use std::{convert::TryFrom, time::Duration}; #[tokio::test] @@ -20,6 +20,8 @@ async fn send_eth() { let provider = Provider::::try_from(ganache.endpoint()) .unwrap() .interval(Duration::from_millis(10u64)); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + let wallet = wallet.with_chain_id(chain_id); let provider = SignerMiddleware::new(provider, wallet); // craft the transaction @@ -48,13 +50,14 @@ async fn test_send_transaction() { let provider = Provider::::try_from("https://alfajores-forno.celo-testnet.org") .unwrap() .interval(Duration::from_millis(3000u64)); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); // Funded with https://celo.org/developers/faucet // Please do not drain this account :) let wallet = "d652abb81e8c686edba621a895531b1f291289b63b5ef09a94f686a5ecdd5db1" .parse::() .unwrap() - .set_chain_id(44787u64); + .with_chain_id(chain_id); let client = SignerMiddleware::new(provider, wallet); let balance_before = client.get_balance(client.address(), None).await.unwrap(); @@ -71,6 +74,7 @@ async fn test_send_transaction() { } #[tokio::test] +#[cfg(not(feature = "celo"))] async fn send_transaction_handles_tx_from_field() { use ethers_core::utils::Ganache; diff --git a/ethers-middleware/tests/stack.rs b/ethers-middleware/tests/stack.rs index abd1a589..fd1ed98c 100644 --- a/ethers-middleware/tests/stack.rs +++ b/ethers-middleware/tests/stack.rs @@ -8,7 +8,7 @@ mod tests { signer::SignerMiddleware, }; use ethers_providers::{Http, Middleware, Provider}; - use ethers_signers::LocalWallet; + use ethers_signers::{LocalWallet, Signer}; use std::convert::TryFrom; #[tokio::test] @@ -58,6 +58,8 @@ mod tests { // the base provider let provider = Arc::new(Provider::::try_from(ganache.endpoint()).unwrap()); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + let signer = signer.with_chain_id(chain_id); // the Gas Price escalator middleware is the first middleware above the provider, // so that it receives the transaction last, after all the other middleware diff --git a/ethers-middleware/tests/transformer.rs b/ethers-middleware/tests/transformer.rs index 14de1663..630447e5 100644 --- a/ethers-middleware/tests/transformer.rs +++ b/ethers-middleware/tests/transformer.rs @@ -8,7 +8,7 @@ use ethers_middleware::{ SignerMiddleware, }; use ethers_providers::{Http, Middleware, Provider}; -use ethers_signers::LocalWallet; +use ethers_signers::{LocalWallet, Signer}; use rand::Rng; use std::{convert::TryFrom, sync::Arc, time::Duration}; @@ -26,6 +26,8 @@ async fn ds_proxy_transformer() { let provider = Provider::::try_from(ganache.endpoint()) .unwrap() .interval(Duration::from_millis(10u64)); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + let wallet = wallet.with_chain_id(chain_id); let signer_middleware = SignerMiddleware::new(provider.clone(), wallet); let wallet_addr = signer_middleware.address(); let provider = Arc::new(signer_middleware.clone()); @@ -111,6 +113,8 @@ async fn ds_proxy_code() { let provider = Provider::::try_from(ganache.endpoint()) .unwrap() .interval(Duration::from_millis(10u64)); + let chain_id = provider.get_chainid().await.unwrap().as_u64(); + let wallet = wallet.with_chain_id(chain_id); let signer_middleware = SignerMiddleware::new(provider.clone(), wallet); let wallet_addr = signer_middleware.address(); let provider = Arc::new(signer_middleware.clone()); diff --git a/ethers-providers/tests/provider.rs b/ethers-providers/tests/provider.rs index e37dcaca..08a586e0 100644 --- a/ethers-providers/tests/provider.rs +++ b/ethers-providers/tests/provider.rs @@ -6,7 +6,7 @@ mod eth_tests { use super::*; use ethers::{ middleware::SignerMiddleware, - signers::LocalWallet, + signers::{LocalWallet, Signer}, types::{BlockId, TransactionRequest, H256}, utils::Ganache, }; @@ -86,9 +86,11 @@ mod eth_tests { "https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", ) .unwrap(); + let chain_id = provider.get_chainid().await.unwrap(); let wallet = "59c37cb6b16fa2de30675f034c8008f890f4b2696c729d6267946d29736d73e4" .parse::() - .unwrap(); + .unwrap() + .with_chain_id(chain_id.as_u64()); let address = wallet.address(); let provider = SignerMiddleware::new(provider, wallet); generic_pending_txs_test(provider, address).await; @@ -102,9 +104,11 @@ mod eth_tests { Provider::connect("wss://rinkeby.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27") .await .unwrap(); + let chain_id = provider.get_chainid().await.unwrap(); let wallet = "ff7f80c6e9941865266ed1f481263d780169f1d98269c51167d20c630a5fdc8a" .parse::() - .unwrap(); + .unwrap() + .with_chain_id(chain_id.as_64()); let address = wallet.address(); let provider = SignerMiddleware::new(provider, wallet); generic_pending_txs_test(provider, address).await; diff --git a/ethers-signers/Cargo.toml b/ethers-signers/Cargo.toml index ca83be81..9c67c88e 100644 --- a/ethers-signers/Cargo.toml +++ b/ethers-signers/Cargo.toml @@ -35,7 +35,6 @@ yubihsm = { version = "0.39.0", features = ["secp256k1", "usb", "mockhsm"] } tempfile = "3.2.0" tokio = { version = "1.5", default-features = false, features = ["macros"] } -serde_json = { version = "1.0.64", default-features = false } [features] celo = ["ethers-core/celo"] diff --git a/ethers-signers/src/ledger/app.rs b/ethers-signers/src/ledger/app.rs index f27f1397..5d44b133 100644 --- a/ethers-signers/src/ledger/app.rs +++ b/ethers-signers/src/ledger/app.rs @@ -24,10 +24,8 @@ use super::types::*; pub struct LedgerEthereum { transport: Mutex, derivation: DerivationType, - pub chain_id: Option, - - /// The ledger's address, instantiated at runtime - pub address: Address, + pub(crate) chain_id: u64, + pub(crate) address: Address, } impl LedgerEthereum { @@ -38,14 +36,11 @@ impl LedgerEthereum { /// # async fn foo() -> Result<(), Box> { /// use ethers::signers::{Ledger, HDPath}; /// - /// let ledger = Ledger::new(HDPath::LedgerLive(0), Some(1)).await?; + /// let ledger = Ledger::new(HDPath::LedgerLive(0), 1).await?; /// # Ok(()) /// # } /// ``` - pub async fn new( - derivation: DerivationType, - chain_id: Option, - ) -> Result { + pub async fn new(derivation: DerivationType, chain_id: u64) -> Result { let transport = Ledger::init().await?; let address = Self::get_address_with_path_transport(&transport, &derivation).await?; @@ -123,13 +118,9 @@ impl LedgerEthereum { } /// Signs an Ethereum transaction (requires confirmation on the ledger) - pub async fn sign_tx( - &self, - tx: &TransactionRequest, - chain_id: Option, - ) -> Result { + pub async fn sign_tx(&self, tx: &TransactionRequest) -> Result { let mut payload = Self::path_to_bytes(&self.derivation); - payload.extend_from_slice(tx.rlp(chain_id).as_ref()); + payload.extend_from_slice(tx.rlp(self.chain_id).as_ref()); self.sign_payload(INS::SIGN, payload).await } @@ -216,7 +207,7 @@ mod tests { // Replace this with your ETH addresses. async fn test_get_address() { // Instantiate it with the default ledger derivation path - let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), None) + let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1) .await .unwrap(); assert_eq!( @@ -235,7 +226,7 @@ mod tests { #[tokio::test] #[ignore] async fn test_sign_tx() { - let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), None) + let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1) .await .unwrap(); @@ -257,7 +248,7 @@ mod tests { #[tokio::test] #[ignore] async fn test_version() { - let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), None) + let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1) .await .unwrap(); @@ -268,7 +259,7 @@ mod tests { #[tokio::test] #[ignore] async fn test_sign_message() { - let ledger = LedgerEthereum::new(DerivationType::Legacy(0), None) + let ledger = LedgerEthereum::new(DerivationType::Legacy(0), 1) .await .unwrap(); let message = "hello world"; diff --git a/ethers-signers/src/ledger/mod.rs b/ethers-signers/src/ledger/mod.rs index 7b12018e..b22466e2 100644 --- a/ethers-signers/src/ledger/mod.rs +++ b/ethers-signers/src/ledger/mod.rs @@ -24,11 +24,20 @@ impl Signer for LedgerEthereum { &self, message: &TransactionRequest, ) -> Result { - self.sign_tx(message, self.chain_id).await + self.sign_tx(message).await } /// Returns the signer's Ethereum Address fn address(&self) -> Address { self.address } + + fn with_chain_id>(mut self, chain_id: T) -> Self { + self.chain_id = chain_id.into(); + self + } + + fn chain_id(&self) -> u64 { + self.chain_id + } } diff --git a/ethers-signers/src/lib.rs b/ethers-signers/src/lib.rs index dd62fa41..c54c4f38 100644 --- a/ethers-signers/src/lib.rs +++ b/ethers-signers/src/lib.rs @@ -67,15 +67,8 @@ use ethers_core::types::{Address, Signature, TransactionRequest}; use std::error::Error; /// Applies [EIP155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) -pub fn to_eip155_v>(recovery_id: T, chain_id: Option) -> u64 { - let standard_v: u8 = recovery_id.into(); - if let Some(chain_id) = chain_id { - // When signing with a chain ID, add chain replay protection. - (standard_v as u64) + 35 + chain_id * 2 - } else { - // Otherwise, convert to 'Electrum' notation. - (standard_v as u64) + 27 - } +pub fn to_eip155_v>(recovery_id: T, chain_id: u64) -> u64 { + (recovery_id.into() as u64) + 35 + chain_id * 2 } /// Trait for signing transactions and messages @@ -98,4 +91,10 @@ pub trait Signer: std::fmt::Debug + Send + Sync { /// Returns the signer's Ethereum Address fn address(&self) -> Address; + + /// Returns the signer's chain id + fn chain_id(&self) -> u64; + + /// Sets the signer's chain id + fn with_chain_id>(self, chain_id: T) -> Self; } diff --git a/ethers-signers/src/wallet/mnemonic.rs b/ethers-signers/src/wallet/mnemonic.rs index aa2ebffd..f4d5a11b 100644 --- a/ethers-signers/src/wallet/mnemonic.rs +++ b/ethers-signers/src/wallet/mnemonic.rs @@ -183,7 +183,7 @@ impl MnemonicBuilder { Ok(Wallet:: { signer, address, - chain_id: None, + chain_id: 1, }) } } diff --git a/ethers-signers/src/wallet/mod.rs b/ethers-signers/src/wallet/mod.rs index 58211485..ab864fef 100644 --- a/ethers-signers/src/wallet/mod.rs +++ b/ethers-signers/src/wallet/mod.rs @@ -43,7 +43,7 @@ use std::fmt; /// /// // Optionally, the wallet's chain id can be set, in order to use EIP-155 /// // replay protection with different chains -/// let wallet = wallet.set_chain_id(1337u64); +/// let wallet = wallet.with_chain_id(1337u64); /// /// // The wallet can be used to sign messages /// let message = b"hello"; @@ -60,8 +60,8 @@ pub struct Wallet> { pub(crate) signer: D, /// The wallet's address pub(crate) address: Address, - /// The wallet's chain id (for EIP-155), signs w/o replay protection if left unset - pub(crate) chain_id: Option, + /// The wallet's chain id (for EIP-155) + pub(crate) chain_id: u64, } #[async_trait] @@ -75,25 +75,44 @@ impl> Signer fo let message = message.as_ref(); let message_hash = hash_message(message); - Ok(self.sign_hash_with_eip155(message_hash, None)) + Ok(self.sign_hash(message_hash, false)) } async fn sign_transaction(&self, tx: &TransactionRequest) -> Result { let sighash = tx.sighash(self.chain_id); - Ok(self.sign_hash_with_eip155(sighash, self.chain_id)) + Ok(self.sign_hash(sighash, true)) } fn address(&self) -> Address { self.address } + + /// Gets the wallet's chain id + /// + /// # Panics + /// + /// If the chain id has not already been set. + fn chain_id(&self) -> u64 { + self.chain_id + } + + /// Sets the wallet's chain_id, used in conjunction with EIP-155 signing + fn with_chain_id>(mut self, chain_id: T) -> Self { + self.chain_id = chain_id.into(); + self + } } impl> Wallet { - fn sign_hash_with_eip155(&self, hash: H256, chain_id: Option) -> Signature { + fn sign_hash(&self, hash: H256, eip155: bool) -> Signature { let recoverable_sig: RecoverableSignature = self.signer.sign_digest(Sha256Proxy::from(hash)); - let v = to_eip155_v(recoverable_sig.recovery_id(), chain_id); + let v = if eip155 { + to_eip155_v(recoverable_sig.recovery_id(), self.chain_id) + } else { + u8::from(recoverable_sig.recovery_id()) as u64 + 27 + }; let r_bytes: FieldBytes = recoverable_sig.r().into(); let s_bytes: FieldBytes = recoverable_sig.s().into(); @@ -103,26 +122,10 @@ impl> Wallet { Signature { r, s, v } } - /// Sets the wallet's chain_id, used in conjunction with EIP-155 signing - pub fn set_chain_id>(mut self, chain_id: T) -> Self { - self.chain_id = Some(chain_id.into()); - self - } - /// Gets the wallet's signer pub fn signer(&self) -> &D { &self.signer } - - /// Gets the wallet's chain id - pub fn chain_id(&self) -> Option { - self.chain_id - } - - /// Returns the wallet's address - pub fn address(&self) -> Address { - self.address - } } // do not log the signer diff --git a/ethers-signers/src/wallet/private_key.rs b/ethers-signers/src/wallet/private_key.rs index 23f202a2..cf2289a8 100644 --- a/ethers-signers/src/wallet/private_key.rs +++ b/ethers-signers/src/wallet/private_key.rs @@ -66,7 +66,7 @@ impl Wallet { Ok(Self { signer, address, - chain_id: None, + chain_id: 1, }) } @@ -82,7 +82,7 @@ impl Wallet { Ok(Self { signer, address, - chain_id: None, + chain_id: 1, }) } @@ -93,7 +93,7 @@ impl Wallet { Self { signer, address, - chain_id: None, + chain_id: 1, } } } @@ -113,7 +113,7 @@ impl From for Wallet { Self { signer, address, - chain_id: None, + chain_id: 1, } } } @@ -129,7 +129,7 @@ impl From for Wallet { Self { signer, address, - chain_id: None, + chain_id: 1, } } } @@ -224,10 +224,10 @@ mod tests { "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318" .parse() .unwrap(); - let wallet = wallet.set_chain_id(chain_id); + let wallet = wallet.with_chain_id(chain_id); let sig = wallet.sign_transaction(&tx).await.unwrap(); - let sighash = tx.sighash(Some(chain_id)); + let sighash = tx.sighash(chain_id); assert!(sig.verify(sighash, wallet.address).is_ok()); } diff --git a/ethers-signers/src/wallet/yubi.rs b/ethers-signers/src/wallet/yubi.rs index 8c78f838..b7df0c9e 100644 --- a/ethers-signers/src/wallet/yubi.rs +++ b/ethers-signers/src/wallet/yubi.rs @@ -58,7 +58,7 @@ impl From> for Wallet> { Self { signer, address, - chain_id: None, + chain_id: 1, } } } diff --git a/ethers/examples/ledger.rs b/ethers/examples/ledger.rs index cefafdd7..7dcd4226 100644 --- a/ethers/examples/ledger.rs +++ b/ethers/examples/ledger.rs @@ -9,7 +9,7 @@ async fn main() -> anyhow::Result<()> { // the wallet's index. Alternatively, you may use Legacy with the wallet's // index or supply the full HD path string. You may also provide the chain_id // (here: mainnet) for EIP155 support. - let ledger = Ledger::new(HDPath::LedgerLive(0), Some(1)).await?; + let ledger = Ledger::new(HDPath::LedgerLive(0), 1).await?; let client = SignerMiddleware::new(provider, ledger); // Create and broadcast a transaction (ENS enabled!)