fixed eth_call, eth_sendTransaction and eth_estimateGas
This commit is contained in:
parent
0cdac6de3c
commit
a0cbe7cd7e
@ -24,17 +24,26 @@ use jsonrpc_core::*;
|
|||||||
use util::numbers::*;
|
use util::numbers::*;
|
||||||
use util::sha3::*;
|
use util::sha3::*;
|
||||||
use util::rlp::{encode, UntrustedRlp, View};
|
use util::rlp::{encode, UntrustedRlp, View};
|
||||||
|
use util::crypto::KeyPair;
|
||||||
use ethcore::client::*;
|
use ethcore::client::*;
|
||||||
use ethcore::block::IsBlock;
|
use ethcore::block::IsBlock;
|
||||||
use ethcore::views::*;
|
use ethcore::views::*;
|
||||||
use ethcore::ethereum::Ethash;
|
use ethcore::ethereum::Ethash;
|
||||||
use ethcore::ethereum::denominations::shannon;
|
use ethcore::ethereum::denominations::shannon;
|
||||||
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction};
|
use ethcore::transaction::{Transaction as EthTransaction, SignedTransaction, Action};
|
||||||
use v1::traits::{Eth, EthFilter};
|
use v1::traits::{Eth, EthFilter};
|
||||||
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, OptionalValue, Index, Filter, Log, Receipt};
|
use v1::types::{Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, Transaction, TransactionRequest, CallRequest, OptionalValue, Index, Filter, Log, Receipt};
|
||||||
use v1::helpers::{PollFilter, PollManager, ExternalMinerService, ExternalMiner};
|
use v1::helpers::{PollFilter, PollManager, ExternalMinerService, ExternalMiner};
|
||||||
use util::keys::store::AccountProvider;
|
use util::keys::store::AccountProvider;
|
||||||
|
|
||||||
|
fn default_gas() -> U256 {
|
||||||
|
U256::from(21_000)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_gas_price() -> U256 {
|
||||||
|
shannon() * U256::from(50)
|
||||||
|
}
|
||||||
|
|
||||||
/// Eth rpc implementation.
|
/// Eth rpc implementation.
|
||||||
pub struct EthClient<C, S, A, M, EM = ExternalMiner>
|
pub struct EthClient<C, S, A, M, EM = ExternalMiner>
|
||||||
where C: BlockChainClient,
|
where C: BlockChainClient,
|
||||||
@ -157,6 +166,35 @@ impl<C, S, A, M, EM> EthClient<C, S, A, M, EM>
|
|||||||
None => Ok(Value::Null)
|
None => Ok(Value::Null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sign_call(client: &Arc<C>, accounts: &Arc<A>, request: CallRequest) -> Option<SignedTransaction> {
|
||||||
|
match request.from {
|
||||||
|
Some(ref from) => {
|
||||||
|
let transaction = EthTransaction {
|
||||||
|
nonce: request.nonce.unwrap_or_else(|| client.nonce(from)),
|
||||||
|
action: request.to.map_or(Action::Create, Action::Call),
|
||||||
|
gas: request.gas.unwrap_or_else(default_gas),
|
||||||
|
gas_price: request.gas_price.unwrap_or_else(default_gas_price),
|
||||||
|
value: request.value.unwrap_or_else(U256::zero),
|
||||||
|
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
|
||||||
|
};
|
||||||
|
|
||||||
|
accounts.account_secret(from).ok().map(|secret| transaction.sign(&secret))
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let transaction = EthTransaction {
|
||||||
|
nonce: request.nonce.unwrap_or_else(U256::zero),
|
||||||
|
action: request.to.map_or(Action::Create, Action::Call),
|
||||||
|
gas: request.gas.unwrap_or_else(default_gas),
|
||||||
|
gas_price: request.gas_price.unwrap_or_else(default_gas_price),
|
||||||
|
value: request.value.unwrap_or_else(U256::zero),
|
||||||
|
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
|
||||||
|
};
|
||||||
|
|
||||||
|
KeyPair::create().ok().map(|kp| transaction.sign(kp.secret()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
||||||
@ -217,7 +255,7 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
|||||||
|
|
||||||
fn gas_price(&self, params: Params) -> Result<Value, Error> {
|
fn gas_price(&self, params: Params) -> Result<Value, Error> {
|
||||||
match params {
|
match params {
|
||||||
Params::None => to_value(&(shannon() * U256::from(50))),
|
Params::None => to_value(&default_gas_price()),
|
||||||
_ => Err(Error::invalid_params())
|
_ => Err(Error::invalid_params())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -405,14 +443,22 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
|||||||
|
|
||||||
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(TransactionRequest, )>(params)
|
from_params::<(TransactionRequest, )>(params)
|
||||||
.and_then(|(transaction_request, )| {
|
.and_then(|(request, )| {
|
||||||
let accounts = take_weak!(self.accounts);
|
let accounts = take_weak!(self.accounts);
|
||||||
match accounts.account_secret(&transaction_request.from) {
|
match accounts.account_secret(&request.from) {
|
||||||
Ok(secret) => {
|
Ok(secret) => {
|
||||||
let miner = take_weak!(self.miner);
|
let miner = take_weak!(self.miner);
|
||||||
let client = take_weak!(self.client);
|
let client = take_weak!(self.client);
|
||||||
|
|
||||||
let transaction: EthTransaction = transaction_request.into();
|
let transaction = EthTransaction {
|
||||||
|
nonce: request.nonce.unwrap_or_else(|| client.nonce(&request.from)),
|
||||||
|
action: request.to.map_or(Action::Create, Action::Call),
|
||||||
|
gas: request.gas.unwrap_or_else(default_gas),
|
||||||
|
gas_price: request.gas_price.unwrap_or_else(default_gas_price),
|
||||||
|
value: request.value.unwrap_or_else(U256::zero),
|
||||||
|
data: request.data.map_or_else(Vec::new, |d| d.to_vec())
|
||||||
|
};
|
||||||
|
|
||||||
let signed_transaction = transaction.sign(&secret);
|
let signed_transaction = transaction.sign(&secret);
|
||||||
let hash = signed_transaction.hash();
|
let hash = signed_transaction.hash();
|
||||||
|
|
||||||
@ -461,47 +507,30 @@ impl<C, S, A, M, EM> Eth for EthClient<C, S, A, M, EM>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn call(&self, params: Params) -> Result<Value, Error> {
|
fn call(&self, params: Params) -> Result<Value, Error> {
|
||||||
println!("params: {:?}", params);
|
from_params::<(CallRequest, BlockNumber)>(params)
|
||||||
from_params::<(TransactionRequest, BlockNumber)>(params)
|
.and_then(|(request, _block_number)| {
|
||||||
.and_then(|(transaction_request, _block_number)| {
|
let client = take_weak!(self.client);
|
||||||
let accounts = take_weak!(self.accounts);
|
let accounts = take_weak!(self.accounts);
|
||||||
match accounts.account_secret(&transaction_request.from) {
|
let signed = Self::sign_call(&client, &accounts, request);
|
||||||
Ok(secret) => {
|
let output = signed.map(|tx| client.call(&tx)
|
||||||
let client = take_weak!(self.client);
|
.map(|e| Bytes::new(e.output))
|
||||||
|
.unwrap_or(Bytes::default()));
|
||||||
|
|
||||||
let transaction: EthTransaction = transaction_request.into();
|
to_value(&output)
|
||||||
let signed_transaction = transaction.sign(&secret);
|
|
||||||
|
|
||||||
let output = client.call(&signed_transaction)
|
|
||||||
.map(|e| Bytes::new(e.output))
|
|
||||||
.unwrap_or(Bytes::default());
|
|
||||||
|
|
||||||
to_value(&output)
|
|
||||||
},
|
|
||||||
Err(_) => { to_value(&Bytes::default()) }
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn estimate_gas(&self, params: Params) -> Result<Value, Error> {
|
fn estimate_gas(&self, params: Params) -> Result<Value, Error> {
|
||||||
from_params::<(TransactionRequest, BlockNumber)>(params)
|
from_params::<(CallRequest, BlockNumber)>(params)
|
||||||
.and_then(|(transaction_request, _block_number)| {
|
.and_then(|(request, _block_number)| {
|
||||||
|
let client = take_weak!(self.client);
|
||||||
let accounts = take_weak!(self.accounts);
|
let accounts = take_weak!(self.accounts);
|
||||||
match accounts.account_secret(&transaction_request.from) {
|
let signed = Self::sign_call(&client, &accounts, request);
|
||||||
Ok(secret) => {
|
let output = signed.map(|tx| client.call(&tx)
|
||||||
let client = take_weak!(self.client);
|
.map(|e| e.gas_used + e.refunded)
|
||||||
|
.unwrap_or(U256::zero()));
|
||||||
|
|
||||||
let transaction: EthTransaction = transaction_request.into();
|
to_value(&output)
|
||||||
let signed_transaction = transaction.sign(&secret);
|
|
||||||
|
|
||||||
let gas_used = client.call(&signed_transaction)
|
|
||||||
.map(|e| e.gas_used + e.refunded)
|
|
||||||
.unwrap_or(U256::zero());
|
|
||||||
|
|
||||||
to_value(&gas_used)
|
|
||||||
},
|
|
||||||
Err(_) => { to_value(&U256::zero()) }
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
106
rpc/src/v1/types/call_request.rs
Normal file
106
rpc/src/v1/types/call_request.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use util::hash::Address;
|
||||||
|
use util::numbers::U256;
|
||||||
|
use v1::types::Bytes;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq, Deserialize)]
|
||||||
|
pub struct CallRequest {
|
||||||
|
pub from: Option<Address>,
|
||||||
|
pub to: Option<Address>,
|
||||||
|
#[serde(rename="gasPrice")]
|
||||||
|
pub gas_price: Option<U256>,
|
||||||
|
pub gas: Option<U256>,
|
||||||
|
pub value: Option<U256>,
|
||||||
|
pub data: Option<Bytes>,
|
||||||
|
pub nonce: Option<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
use rustc_serialize::hex::FromHex;
|
||||||
|
use serde_json;
|
||||||
|
use util::numbers::{Uint, U256};
|
||||||
|
use util::hash::Address;
|
||||||
|
use ethcore::transaction::{Transaction, Action};
|
||||||
|
use v1::types::Bytes;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_request_deserialize() {
|
||||||
|
let s = r#"{
|
||||||
|
"from":"0x0000000000000000000000000000000000000001",
|
||||||
|
"to":"0x0000000000000000000000000000000000000002",
|
||||||
|
"gasPrice":"0x1",
|
||||||
|
"gas":"0x2",
|
||||||
|
"value":"0x3",
|
||||||
|
"data":"0x123456",
|
||||||
|
"nonce":"0x4"
|
||||||
|
}"#;
|
||||||
|
let deserialized: CallRequest = serde_json::from_str(s).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(deserialized, CallRequest {
|
||||||
|
from: Some(Address::from(1)),
|
||||||
|
to: Some(Address::from(2)),
|
||||||
|
gas_price: Some(U256::from(1)),
|
||||||
|
gas: Some(U256::from(2)),
|
||||||
|
value: Some(U256::from(3)),
|
||||||
|
data: Some(Bytes::new(vec![0x12, 0x34, 0x56])),
|
||||||
|
nonce: Some(U256::from(4)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_request_deserialize2() {
|
||||||
|
let s = r#"{
|
||||||
|
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
|
||||||
|
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
|
||||||
|
"gas": "0x76c0",
|
||||||
|
"gasPrice": "0x9184e72a000",
|
||||||
|
"value": "0x9184e72a",
|
||||||
|
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
|
||||||
|
}"#;
|
||||||
|
let deserialized: CallRequest = serde_json::from_str(s).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(deserialized, CallRequest {
|
||||||
|
from: Some(Address::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()),
|
||||||
|
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||||
|
gas_price: Some(U256::from_str("9184e72a000").unwrap()),
|
||||||
|
gas: Some(U256::from_str("76c0").unwrap()),
|
||||||
|
value: Some(U256::from_str("9184e72a").unwrap()),
|
||||||
|
data: Some(Bytes::new("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap())),
|
||||||
|
nonce: None
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transaction_request_deserialize_empty() {
|
||||||
|
let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#;
|
||||||
|
let deserialized: CallRequest = serde_json::from_str(s).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(deserialized, CallRequest {
|
||||||
|
from: Some(Address::from(1)),
|
||||||
|
to: None,
|
||||||
|
gas_price: None,
|
||||||
|
gas: None,
|
||||||
|
value: None,
|
||||||
|
data: None,
|
||||||
|
nonce: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ mod optionals;
|
|||||||
mod sync;
|
mod sync;
|
||||||
mod transaction;
|
mod transaction;
|
||||||
mod transaction_request;
|
mod transaction_request;
|
||||||
|
mod call_request;
|
||||||
mod receipt;
|
mod receipt;
|
||||||
|
|
||||||
pub use self::block::{Block, BlockTransactions};
|
pub use self::block::{Block, BlockTransactions};
|
||||||
@ -36,5 +37,6 @@ pub use self::optionals::OptionalValue;
|
|||||||
pub use self::sync::{SyncStatus, SyncInfo};
|
pub use self::sync::{SyncStatus, SyncInfo};
|
||||||
pub use self::transaction::Transaction;
|
pub use self::transaction::Transaction;
|
||||||
pub use self::transaction_request::TransactionRequest;
|
pub use self::transaction_request::TransactionRequest;
|
||||||
|
pub use self::call_request::CallRequest;
|
||||||
pub use self::receipt::Receipt;
|
pub use self::receipt::Receipt;
|
||||||
|
|
||||||
|
@ -15,8 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::hash::Address;
|
use util::hash::Address;
|
||||||
use util::numbers::{Uint, U256};
|
use util::numbers::U256;
|
||||||
use ethcore::transaction::{Action, Transaction};
|
|
||||||
use v1::types::Bytes;
|
use v1::types::Bytes;
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Deserialize)]
|
#[derive(Debug, Default, PartialEq, Deserialize)]
|
||||||
@ -31,19 +30,6 @@ pub struct TransactionRequest {
|
|||||||
pub nonce: Option<U256>,
|
pub nonce: Option<U256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Transaction> for TransactionRequest {
|
|
||||||
fn into(self) -> Transaction {
|
|
||||||
Transaction {
|
|
||||||
nonce: self.nonce.unwrap_or_else(U256::zero),
|
|
||||||
action: self.to.map_or(Action::Create, Action::Call),
|
|
||||||
gas: self.gas.unwrap_or_else(U256::zero),
|
|
||||||
gas_price: self.gas_price.unwrap_or_else(U256::zero),
|
|
||||||
value: self.value.unwrap_or_else(U256::zero),
|
|
||||||
data: self.data.map_or_else(Vec::new, |d| d.to_vec()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@ -55,50 +41,6 @@ mod tests {
|
|||||||
use v1::types::Bytes;
|
use v1::types::Bytes;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn transaction_request_into_transaction() {
|
|
||||||
let tr = TransactionRequest {
|
|
||||||
from: Address::default(),
|
|
||||||
to: Some(Address::from(10)),
|
|
||||||
gas_price: Some(U256::from(20)),
|
|
||||||
gas: Some(U256::from(10_000)),
|
|
||||||
value: Some(U256::from(1)),
|
|
||||||
data: Some(Bytes::new(vec![10, 20])),
|
|
||||||
nonce: Some(U256::from(12)),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(Transaction {
|
|
||||||
nonce: U256::from(12),
|
|
||||||
action: Action::Call(Address::from(10)),
|
|
||||||
gas: U256::from(10_000),
|
|
||||||
gas_price: U256::from(20),
|
|
||||||
value: U256::from(1),
|
|
||||||
data: vec![10, 20],
|
|
||||||
}, tr.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn empty_transaction_request_into_transaction() {
|
|
||||||
let tr = TransactionRequest {
|
|
||||||
from: Address::default(),
|
|
||||||
to: None,
|
|
||||||
gas_price: None,
|
|
||||||
gas: None,
|
|
||||||
value: None,
|
|
||||||
data: None,
|
|
||||||
nonce: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(Transaction {
|
|
||||||
nonce: U256::zero(),
|
|
||||||
action: Action::Create,
|
|
||||||
gas: U256::zero(),
|
|
||||||
gas_price: U256::zero(),
|
|
||||||
value: U256::zero(),
|
|
||||||
data: vec![],
|
|
||||||
}, tr.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transaction_request_deserialize() {
|
fn transaction_request_deserialize() {
|
||||||
let s = r#"{
|
let s = r#"{
|
||||||
|
@ -20,6 +20,7 @@ use numbers::*;
|
|||||||
use bytes::*;
|
use bytes::*;
|
||||||
use secp256k1::{key, Secp256k1};
|
use secp256k1::{key, Secp256k1};
|
||||||
use rand::os::OsRng;
|
use rand::os::OsRng;
|
||||||
|
use sha3::Hashable;
|
||||||
|
|
||||||
/// Secret key for secp256k1 EC operations. 256 bit generic "hash" data.
|
/// Secret key for secp256k1 EC operations. 256 bit generic "hash" data.
|
||||||
pub type Secret = H256;
|
pub type Secret = H256;
|
||||||
@ -135,15 +136,22 @@ impl KeyPair {
|
|||||||
public: p,
|
public: p,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns public key
|
/// Returns public key
|
||||||
pub fn public(&self) -> &Public {
|
pub fn public(&self) -> &Public {
|
||||||
&self.public
|
&self.public
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns private key
|
/// Returns private key
|
||||||
pub fn secret(&self) -> &Secret {
|
pub fn secret(&self) -> &Secret {
|
||||||
&self.secret
|
&self.secret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns address.
|
||||||
|
pub fn address(&self) -> Address {
|
||||||
|
Address::from(self.public.sha3())
|
||||||
|
}
|
||||||
|
|
||||||
/// Sign a message with our secret key.
|
/// Sign a message with our secret key.
|
||||||
pub fn sign(&self, message: &H256) -> Result<Signature, CryptoError> { ec::sign(&self.secret, message) }
|
pub fn sign(&self, message: &H256) -> Result<Signature, CryptoError> { ec::sign(&self.secret, message) }
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user