// Copyright 2015, 2016 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity 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 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. If not, see . use rlp; use util::{Address, H256, U256, Uint, Bytes}; use util::bytes::ToPretty; use ethkey::Signature; use ethcore::miner::MinerService; use ethcore::client::MiningBlockChainClient; use ethcore::transaction::{Action, SignedTransaction, PendingTransaction, Transaction}; use ethcore::account_provider::AccountProvider; use jsonrpc_core::Error; use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload}; use v1::types::{ H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes, RichRawTransaction as RpcRichRawTransaction, ConfirmationPayload as RpcConfirmationPayload, ConfirmationResponse, SignRequest as RpcSignRequest, DecryptRequest as RpcDecryptRequest, }; pub const DEFAULT_MAC: [u8; 2] = [0, 0]; pub fn execute(client: &C, miner: &M, accounts: &AccountProvider, payload: ConfirmationPayload, pass: Option) -> Result where C: MiningBlockChainClient, M: MinerService { match payload { ConfirmationPayload::SendTransaction(request) => { sign_and_dispatch(client, miner, accounts, request, pass) .map(RpcH256::from) .map(ConfirmationResponse::SendTransaction) }, ConfirmationPayload::SignTransaction(request) => { sign_no_dispatch(client, miner, accounts, request, pass) .map(RpcRichRawTransaction::from) .map(ConfirmationResponse::SignTransaction) }, ConfirmationPayload::Signature(address, hash) => { signature(accounts, address, hash, pass) .map(RpcH520::from) .map(ConfirmationResponse::Signature) }, ConfirmationPayload::Decrypt(address, data) => { decrypt(accounts, address, data, pass) .map(RpcBytes) .map(ConfirmationResponse::Decrypt) }, } } fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: Option) -> Result { accounts.sign(address, password.clone(), hash).map_err(|e| match password { Some(_) => errors::from_password_error(e), None => errors::from_signing_error(e), }) } fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: Option) -> Result { accounts.decrypt(address, password.clone(), &DEFAULT_MAC, &msg) .map_err(|e| match password { Some(_) => errors::from_password_error(e), None => errors::from_signing_error(e), }) } pub fn dispatch_transaction(client: &C, miner: &M, signed_transaction: PendingTransaction) -> Result where C: MiningBlockChainClient, M: MinerService { let hash = signed_transaction.transaction.hash(); miner.import_own_transaction(client, signed_transaction) .map_err(errors::from_transaction_error) .map(|_| hash) } pub fn sign_no_dispatch(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: Option) -> Result where C: MiningBlockChainClient, M: MinerService { let network_id = client.signing_network_id(); let address = filled.from; let signed_transaction = { let t = Transaction { nonce: filled.nonce .or_else(|| miner .last_nonce(&filled.from) .map(|nonce| nonce + U256::one())) .unwrap_or_else(|| client.latest_nonce(&filled.from)), action: filled.to.map_or(Action::Create, Action::Call), gas: filled.gas, gas_price: filled.gas_price, value: filled.value, data: filled.data, }; let hash = t.hash(network_id); let signature = try!(signature(accounts, address, hash, password)); t.with_signature(signature, network_id) }; Ok(signed_transaction) } pub fn sign_and_dispatch(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: Option) -> Result where C: MiningBlockChainClient, M: MinerService { let network_id = client.signing_network_id(); let min_block = filled.min_block.clone(); let signed_transaction = try!(sign_no_dispatch(client, miner, accounts, filled, password)); trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", rlp::encode(&signed_transaction).to_vec().pretty(), network_id); let pending_transaction = PendingTransaction::new(signed_transaction, min_block); dispatch_transaction(&*client, &*miner, pending_transaction) } pub fn fill_optional_fields(request: TransactionRequest, client: &C, miner: &M) -> FilledTransactionRequest where C: MiningBlockChainClient, M: MinerService { FilledTransactionRequest { from: request.from, to: request.to, nonce: request.nonce, gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)), gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()), value: request.value.unwrap_or_else(|| 0.into()), data: request.data.unwrap_or_else(Vec::new), min_block: request.min_block, } } pub fn default_gas_price(client: &C, miner: &M) -> U256 where C: MiningBlockChainClient, M: MinerService { client.gas_price_median(100).unwrap_or_else(|| miner.sensible_gas_price()) } pub fn from_rpc(payload: RpcConfirmationPayload, client: &C, miner: &M) -> ConfirmationPayload where C: MiningBlockChainClient, M: MinerService { match payload { RpcConfirmationPayload::SendTransaction(request) => { ConfirmationPayload::SendTransaction(fill_optional_fields(request.into(), client, miner)) }, RpcConfirmationPayload::SignTransaction(request) => { ConfirmationPayload::SignTransaction(fill_optional_fields(request.into(), client, miner)) }, RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => { ConfirmationPayload::Decrypt(address.into(), msg.into()) }, RpcConfirmationPayload::Signature(RpcSignRequest { address, hash }) => { ConfirmationPayload::Signature(address.into(), hash.into()) }, } }