mod eth;
mod eth_pubsub;
mod overrides;
pub use eth::{
EthApi, EthApiServer, EthFilterApi, EthFilterApiServer, NetApi, NetApiServer, Web3Api, Web3ApiServer,
EthTask,
};
pub use eth_pubsub::{EthPubSubApi, EthPubSubApiServer, HexEncodedIdProvider};
pub use overrides::{StorageOverride, SchemaV1Override};
use ethereum_types::{H160, H256};
use ethereum::{
Transaction as EthereumTransaction, TransactionMessage as EthereumTransactionMessage,
};
use jsonrpc_core::{ErrorCode, Error, Value};
use rustc_hex::ToHex;
use pallet_evm::ExitReason;
use sha3::{Digest, Keccak256};
use evm::ExitError;
pub mod frontier_backend_client {
use super::internal_err;
use sp_runtime::traits::{Block as BlockT, BlakeTwo256, Zero, UniqueSaturatedInto};
use sp_storage::StorageKey;
use sp_blockchain::HeaderBackend;
use sp_api::{BlockId, HeaderT};
use sc_client_api::backend::{StorageProvider, Backend, StateBackend};
use fc_rpc_core::types::BlockNumber;
use fp_storage::PALLET_ETHEREUM_SCHEMA;
use jsonrpc_core::Result as RpcResult;
use codec::Decode;
use ethereum_types::H256;
use pallet_ethereum::EthereumStorageSchema;
pub fn native_block_id<B: BlockT, C>(client: &C, backend: &fc_db::Backend<B>, number: Option<BlockNumber>) -> RpcResult<Option<BlockId<B>>> where
B: BlockT,
C: HeaderBackend<B> + 'static,
B: BlockT<Hash=H256> + Send + Sync + 'static,
C: Send + Sync + 'static,
{
Ok(match number.unwrap_or(BlockNumber::Latest) {
BlockNumber::Hash { hash, .. } => {
load_hash::<B, C>(client, backend, hash).unwrap_or(None)
},
BlockNumber::Num(number) => {
Some(BlockId::Number(number.unique_saturated_into()))
},
BlockNumber::Latest => {
Some(BlockId::Hash(
client.info().best_hash
))
},
BlockNumber::Earliest => {
Some(BlockId::Number(Zero::zero()))
},
BlockNumber::Pending => {
None
}
})
}
pub fn load_hash<B: BlockT, C>(client: &C, backend: &fc_db::Backend<B>, hash: H256) -> RpcResult<Option<BlockId<B>>> where
B: BlockT,
C: HeaderBackend<B> + 'static,
B: BlockT<Hash=H256> + Send + Sync + 'static,
C: Send + Sync + 'static,
{
let substrate_hash = backend.mapping().block_hash(&hash)
.map_err(|err| internal_err(format!("fetch aux store failed: {:?}", err)))?;
if let Some(substrate_hash) = substrate_hash {
return Ok(Some(
BlockId::Hash(substrate_hash)
));
}
Ok(None)
}
pub fn onchain_storage_schema<B: BlockT, C, BE>(client: &C, at: BlockId<B>) -> EthereumStorageSchema where
B: BlockT,
C: StorageProvider<B, BE>,
BE: Backend<B> + 'static,
BE::State: StateBackend<BlakeTwo256>,
B: BlockT<Hash=H256> + Send + Sync + 'static,
C: Send + Sync + 'static,
{
match client.storage(&at, &StorageKey(PALLET_ETHEREUM_SCHEMA.to_vec())) {
Ok(Some(bytes)) => Decode::decode(&mut &bytes.0[..]).ok().unwrap_or(EthereumStorageSchema::Undefined),
_ => EthereumStorageSchema::Undefined,
}
}
pub fn is_canon<B: BlockT, C>(client: &C, target_hash: H256) -> bool where
B: BlockT,
C: HeaderBackend<B> + 'static,
B: BlockT<Hash=H256> + Send + Sync + 'static,
C: Send + Sync + 'static,
{
if let Ok(Some(number)) = client.number(target_hash) {
if let Ok(Some(header)) = client.header(BlockId::Number(number)) {
return header.hash() == target_hash;
}
}
false
}
pub fn load_transactions<B: BlockT, C>(client: &C, backend: &fc_db::Backend<B>, transaction_hash: H256) -> RpcResult<Option<(H256, u32)>> where
B: BlockT,
C: HeaderBackend<B> + 'static,
B: BlockT<Hash=H256> + Send + Sync + 'static,
C: Send + Sync + 'static,
{
let transaction_metadata = backend.mapping().transaction_metadata(&transaction_hash)
.map_err(|err| internal_err(format!("fetch aux store failed: {:?}", err)))?;
if transaction_metadata.len() == 1 {
Ok(Some((
transaction_metadata[0].ethereum_block_hash,
transaction_metadata[0].ethereum_index,
)))
} else if transaction_metadata.len() > 1 {
transaction_metadata
.iter()
.find(|meta| is_canon::<B, C>(client, meta.block_hash))
.map_or(
Ok(Some((
transaction_metadata[0].ethereum_block_hash,
transaction_metadata[0].ethereum_index,
))),
|meta| Ok(Some((meta.ethereum_block_hash, meta.ethereum_index))),
)
} else {
Ok(None)
}
}
}
pub fn internal_err<T: ToString>(message: T) -> Error {
Error {
code: ErrorCode::InternalError,
message: message.to_string(),
data: None
}
}
pub fn error_on_execution_failure(reason: &ExitReason, data: &[u8]) -> Result<(), Error> {
match reason {
ExitReason::Succeed(_) => Ok(()),
ExitReason::Error(e) => {
if *e == ExitError::OutOfGas || *e == ExitError::OutOfFund {
return Err(Error {
code: ErrorCode::ServerError(0),
message: format!("out of gas or fund"),
data: None,
});
}
Err(Error {
code: ErrorCode::InternalError,
message: format!("evm error: {:?}", e),
data: Some(Value::String("0x".to_string()))
})
},
ExitReason::Revert(_) => {
let mut message = "VM Exception while processing transaction: revert".to_string();
if data.len() > 68 {
let message_len = data[36..68].iter().sum::<u8>();
let body: &[u8] = &data[68..68 + message_len as usize];
if let Ok(reason) = std::str::from_utf8(body) {
message = format!("{} {}", message, reason.to_string());
}
}
Err(Error {
code: ErrorCode::InternalError,
message,
data: Some(Value::String(data.to_hex()))
})
},
ExitReason::Fatal(e) => {
Err(Error {
code: ErrorCode::InternalError,
message: format!("evm fatal: {:?}", e),
data: Some(Value::String("0x".to_string()))
})
},
}
}
pub fn public_key(transaction: &EthereumTransaction) -> Result<
[u8; 64], sp_io::EcdsaVerifyError
> {
let mut sig = [0u8; 65];
let mut msg = [0u8; 32];
sig[0..32].copy_from_slice(&transaction.signature.r()[..]);
sig[32..64].copy_from_slice(&transaction.signature.s()[..]);
sig[64] = transaction.signature.standard_v();
msg.copy_from_slice(&EthereumTransactionMessage::from(transaction.clone()).hash()[..]);
sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg)
}
pub trait EthSigner: Send + Sync {
fn accounts(&self) -> Vec<H160>;
fn sign(
&self,
message: ethereum::TransactionMessage,
address: &H160,
) -> Result<ethereum::Transaction, Error>;
}
pub struct EthDevSigner {
keys: Vec<secp256k1::SecretKey>,
}
impl EthDevSigner {
pub fn new() -> Self {
Self {
keys: vec![
secp256k1::SecretKey::parse(&[
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
]).expect("Test key is valid; qed"),
],
}
}
}
impl EthSigner for EthDevSigner {
fn accounts(&self) -> Vec<H160> {
self.keys.iter().map(|secret| {
let public = secp256k1::PublicKey::from_secret_key(secret);
let mut res = [0u8; 64];
res.copy_from_slice(&public.serialize()[1..65]);
H160::from(H256::from_slice(Keccak256::digest(&res).as_slice()))
}).collect()
}
fn sign(
&self,
message: ethereum::TransactionMessage,
address: &H160,
) -> Result<ethereum::Transaction, Error> {
let mut transaction = None;
for secret in &self.keys {
let key_address = {
let public = secp256k1::PublicKey::from_secret_key(secret);
let mut res = [0u8; 64];
res.copy_from_slice(&public.serialize()[1..65]);
H160::from(H256::from_slice(Keccak256::digest(&res).as_slice()))
};
if &key_address == address {
let signing_message = secp256k1::Message::parse_slice(&message.hash()[..])
.map_err(|_| internal_err("invalid signing message"))?;
let (signature, recid) = secp256k1::sign(&signing_message, secret);
let v = match message.chain_id {
None => 27 + recid.serialize() as u64,
Some(chain_id) => 2 * chain_id + 35 + recid.serialize() as u64,
};
let rs = signature.serialize();
let r = H256::from_slice(&rs[0..32]);
let s = H256::from_slice(&rs[32..64]);
transaction = Some(ethereum::Transaction {
nonce: message.nonce,
gas_price: message.gas_price,
gas_limit: message.gas_limit,
action: message.action,
value: message.value,
input: message.input.clone(),
signature: ethereum::TransactionSignature::new(v, r, s)
.ok_or(internal_err("signer generated invalid signature"))?,
});
break
}
}
transaction.ok_or(internal_err("signer not available"))
}
}