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] <support@github.com> * 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>
This commit is contained in:
parent
e7f603f69c
commit
9dca606eaf
|
@ -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
|
||||
|
|
|
@ -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]]
|
||||
|
|
|
@ -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<HttpWallet> {
|
|||
.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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ValueChanged> = 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::<Http>::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::<LocalWallet>()
|
||||
.unwrap()
|
||||
.set_chain_id(44787u64);
|
||||
.with_chain_id(chain_id);
|
||||
|
||||
let client = SignerMiddleware::new(provider, wallet);
|
||||
let client = Arc::new(client);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<T: Into<U64>>(&self, chain_id: Option<T>) -> H256 {
|
||||
pub fn sighash<T: Into<U64>>(&self, chain_id: T) -> H256 {
|
||||
keccak256(self.rlp(chain_id).as_ref()).into()
|
||||
}
|
||||
|
||||
/// Gets the unsigned transaction's RLP encoding
|
||||
pub fn rlp<T: Into<U64>>(&self, chain_id: Option<T>) -> Bytes {
|
||||
pub fn rlp<T: Into<U64>>(&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);
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -343,7 +343,7 @@ mod tests {
|
|||
let key = "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"
|
||||
.parse::<LocalWallet>()
|
||||
.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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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::<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 wallet = "59c37cb6b16fa2de30675f034c8008f890f4b2696c729d6267946d29736d73e4"
|
||||
.parse::<LocalWallet>()
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.with_chain_id(chain_id);
|
||||
let address = wallet.address();
|
||||
|
||||
let provider = SignerMiddleware::new(provider, wallet);
|
||||
|
|
|
@ -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::<Http>::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::<Http>::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::<LocalWallet>()
|
||||
.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;
|
||||
|
||||
|
|
|
@ -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::<Http>::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
|
||||
|
|
|
@ -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::<Http>::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::<Http>::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());
|
||||
|
|
|
@ -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::<LocalWallet>()
|
||||
.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::<LocalWallet>()
|
||||
.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;
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -24,10 +24,8 @@ use super::types::*;
|
|||
pub struct LedgerEthereum {
|
||||
transport: Mutex<Ledger>,
|
||||
derivation: DerivationType,
|
||||
pub chain_id: Option<u64>,
|
||||
|
||||
/// 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<dyn std::error::Error>> {
|
||||
/// 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<u64>,
|
||||
) -> Result<Self, LedgerError> {
|
||||
pub async fn new(derivation: DerivationType, chain_id: u64) -> Result<Self, LedgerError> {
|
||||
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<u64>,
|
||||
) -> Result<Signature, LedgerError> {
|
||||
pub async fn sign_tx(&self, tx: &TransactionRequest) -> Result<Signature, LedgerError> {
|
||||
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";
|
||||
|
|
|
@ -24,11 +24,20 @@ impl Signer for LedgerEthereum {
|
|||
&self,
|
||||
message: &TransactionRequest,
|
||||
) -> Result<Signature, Self::Error> {
|
||||
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<T: Into<u64>>(mut self, chain_id: T) -> Self {
|
||||
self.chain_id = chain_id.into();
|
||||
self
|
||||
}
|
||||
|
||||
fn chain_id(&self) -> u64 {
|
||||
self.chain_id
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T: Into<u8>>(recovery_id: T, chain_id: Option<u64>) -> 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<T: Into<u8>>(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<T: Into<u64>>(self, chain_id: T) -> Self;
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ impl<W: Wordlist> MnemonicBuilder<W> {
|
|||
Ok(Wallet::<SigningKey> {
|
||||
signer,
|
||||
address,
|
||||
chain_id: None,
|
||||
chain_id: 1,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<D: DigestSigner<Sha256Proxy, RecoverableSignature>> {
|
|||
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<u64>,
|
||||
/// The wallet's chain id (for EIP-155)
|
||||
pub(crate) chain_id: u64,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -75,25 +75,44 @@ impl<D: Sync + Send + DigestSigner<Sha256Proxy, RecoverableSignature>> 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<Signature, Self::Error> {
|
||||
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<T: Into<u64>>(mut self, chain_id: T) -> Self {
|
||||
self.chain_id = chain_id.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DigestSigner<Sha256Proxy, RecoverableSignature>> Wallet<D> {
|
||||
fn sign_hash_with_eip155(&self, hash: H256, chain_id: Option<u64>) -> 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<Secp256k1> = recoverable_sig.r().into();
|
||||
let s_bytes: FieldBytes<Secp256k1> = recoverable_sig.s().into();
|
||||
|
@ -103,26 +122,10 @@ impl<D: DigestSigner<Sha256Proxy, RecoverableSignature>> Wallet<D> {
|
|||
Signature { r, s, v }
|
||||
}
|
||||
|
||||
/// Sets the wallet's chain_id, used in conjunction with EIP-155 signing
|
||||
pub fn set_chain_id<T: Into<u64>>(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<u64> {
|
||||
self.chain_id
|
||||
}
|
||||
|
||||
/// Returns the wallet's address
|
||||
pub fn address(&self) -> Address {
|
||||
self.address
|
||||
}
|
||||
}
|
||||
|
||||
// do not log the signer
|
||||
|
|
|
@ -66,7 +66,7 @@ impl Wallet<SigningKey> {
|
|||
Ok(Self {
|
||||
signer,
|
||||
address,
|
||||
chain_id: None,
|
||||
chain_id: 1,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ impl Wallet<SigningKey> {
|
|||
Ok(Self {
|
||||
signer,
|
||||
address,
|
||||
chain_id: None,
|
||||
chain_id: 1,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ impl Wallet<SigningKey> {
|
|||
Self {
|
||||
signer,
|
||||
address,
|
||||
chain_id: None,
|
||||
chain_id: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ impl From<SigningKey> for Wallet<SigningKey> {
|
|||
Self {
|
||||
signer,
|
||||
address,
|
||||
chain_id: None,
|
||||
chain_id: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ impl From<K256SecretKey> for Wallet<SigningKey> {
|
|||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ impl From<YubiSigner<Secp256k1>> for Wallet<YubiSigner<Secp256k1>> {
|
|||
Self {
|
||||
signer,
|
||||
address,
|
||||
chain_id: None,
|
||||
chain_id: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!)
|
||||
|
|
Loading…
Reference in New Issue