// Copyright 2015-2019 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . use std::sync::Arc; use accounts::AccountProvider; use bytes::Bytes; use crypto::DEFAULT_MAC; use ethereum_types::{H256, U256, Address}; use ethkey::{Signature}; use types::transaction::{Transaction, Action, SignedTransaction}; use jsonrpc_core::Result; use v1::helpers::{errors, FilledTransactionRequest}; use super::{eth_data_hash, WithToken, SignWith, SignMessage}; /// Account-aware signer pub struct Signer { accounts: Arc, } impl Signer { /// Create new instance of signer pub fn new(accounts: Arc) -> Self { Signer { accounts } } } impl super::Accounts for Signer { fn sign_transaction(&self, filled: FilledTransactionRequest, chain_id: Option, nonce: U256, password: SignWith) -> Result> { let t = Transaction { nonce: nonce, action: filled.to.map_or(Action::Create, Action::Call), gas: filled.gas, gas_price: filled.gas_price, value: filled.value, data: filled.data, }; if self.accounts.is_hardware_address(&filled.from) { return hardware_signature(&*self.accounts, filled.from, t, chain_id).map(WithToken::No) } let hash = t.hash(chain_id); let signature = signature(&*self.accounts, filled.from, hash, password)?; Ok(signature.map(|sig| { SignedTransaction::new(t.with_signature(sig, chain_id)) .expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed") })) } fn sign_message(&self, address: Address, password: SignWith, hash: SignMessage) -> Result> { if self.accounts.is_hardware_address(&address) { return if let SignMessage::Data(data) = hash { let signature = self.accounts.sign_message_with_hardware(&address, &data) // TODO: is this correct? I guess the `token` is the wallet in this context .map(WithToken::No) .map_err(|e| errors::account("Error signing message with hardware_wallet", e)); signature } else { Err(errors::account("Error signing message with hardware_wallet", "Message signing is unsupported")) } } match hash { SignMessage::Data(data) => { let hash = eth_data_hash(data); signature(&self.accounts, address, hash, password) }, SignMessage::Hash(hash) => { signature(&self.accounts, address, hash, password) } } } fn decrypt(&self, address: Address, password: SignWith, data: Bytes) -> Result> { if self.accounts.is_hardware_address(&address) { return Err(errors::unsupported("Decrypting via hardware wallets is not supported.", None)); } match password.clone() { SignWith::Nothing => self.accounts.decrypt(address, None, &DEFAULT_MAC, &data).map(WithToken::No), SignWith::Password(pass) => self.accounts.decrypt(address, Some(pass), &DEFAULT_MAC, &data).map(WithToken::No), SignWith::Token(token) => self.accounts.decrypt_with_token(address, token, &DEFAULT_MAC, &data).map(Into::into), }.map_err(|e| match password { SignWith::Nothing => errors::signing(e), _ => errors::password(e), }) } fn supports_prospective_signing(&self, address: &Address, password: &SignWith) -> bool { // If the account is permanently unlocked we can try to sign // using prospective nonce. This should speed up sending // multiple subsequent transactions in multi-threaded RPC environment. let is_unlocked_permanently = self.accounts.is_unlocked_permanently(address); let has_password = password.is_password(); is_unlocked_permanently || has_password } fn default_account(&self) -> Address { self.accounts.default_account().ok().unwrap_or_default() } fn is_unlocked(&self, address: &Address) -> bool { self.accounts.is_unlocked(address) } } fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: SignWith) -> Result> { match password.clone() { SignWith::Nothing => accounts.sign(address, None, hash).map(WithToken::No), SignWith::Password(pass) => accounts.sign(address, Some(pass), hash).map(WithToken::No), SignWith::Token(token) => accounts.sign_with_token(address, token, hash).map(Into::into), }.map_err(|e| match password { SignWith::Nothing => errors::signing(e), _ => errors::password(e), }) } // obtain a hardware signature from the given account. fn hardware_signature(accounts: &AccountProvider, address: Address, t: Transaction, chain_id: Option) -> Result { debug_assert!(accounts.is_hardware_address(&address)); let mut stream = rlp::RlpStream::new(); t.rlp_append_unsigned_transaction(&mut stream, chain_id); let signature = accounts.sign_transaction_with_hardware(&address, &t, chain_id, &stream.as_raw()) .map_err(|e| { debug!(target: "miner", "Error signing transaction with hardware wallet: {}", e); errors::account("Error signing transaction with hardware wallet", e) })?; SignedTransaction::new(t.with_signature(signature, chain_id)) .map_err(|e| { debug!(target: "miner", "Hardware wallet has produced invalid signature: {}", e); errors::account("Invalid signature generated", e) }) }