Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement transaction_by_hash Runtime API #35

Merged
merged 8 commits into from
Jun 15, 2020
65 changes: 63 additions & 2 deletions frame/ethereum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use sp_runtime::{
};
use sha3::{Digest, Keccak256};

pub use frontier_rpc_primitives::TransactionStatus;
pub use frontier_rpc_primitives::{TransactionStatus, Transaction as FullTransaction};
pub use ethereum::{Transaction, Log, Block};

/// A type alias for the balance type from this pallet's point of view.
Expand Down Expand Up @@ -61,6 +61,7 @@ decl_storage! {
BlockNumbers: map hasher(blake2_128_concat) T::BlockNumber => H256;
PendingTransactionsAndReceipts: Vec<(ethereum::Transaction, ethereum::Receipt)>;
TransactionStatuses: map hasher(blake2_128_concat) H256 => Option<TransactionStatus>;
Transactions: map hasher(blake2_128_concat) H256 => Option<(H256,u32)>;
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -187,12 +188,24 @@ decl_module! {

let block = ethereum::Block {
header,
transactions,
transactions: transactions.clone(),
ommers,
};

BlocksAndReceipts::insert(hash, (block, receipts));
BlockNumbers::<T>::insert(n, hash);

for t in &transactions.clone() {
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
let transaction_hash = H256::from_slice(
Keccak256::digest(&rlp::encode(t)).as_slice()
);
if let Some(status) = TransactionStatuses::get(transaction_hash) {
Transactions::insert(
transaction_hash,
(hash, status.transaction_index)
);
}
}
}

// A runtime code run after every block and have access to extended set of APIs.
Expand Down Expand Up @@ -225,6 +238,54 @@ impl<T: Trait> Module<T> {
pub fn transaction_status(hash: H256) -> Option<TransactionStatus> {
TransactionStatuses::get(hash)
}

pub fn transaction_by_hash(hash: H256) -> Option<FullTransaction> {
let (block_hash, transaction_index) = match Transactions::get(hash) {
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
Some(a) => a,
_ => return None,
};

let transaction_status = match TransactionStatuses::get(hash) {
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
Some(a) => a,
_ => return None,
};

let (block,_receipt) = match BlocksAndReceipts::get(block_hash) {
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
Some(a) => a,
_ => return None,
};

let transaction = &block.transactions[transaction_index as usize];

Some(FullTransaction {
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
hash: hash,
nonce: transaction.nonce,
block_hash: Some(block_hash),
block_number: Some(block.header.number),
transaction_index: Some(U256::from(
UniqueSaturatedInto::<u32>::unique_saturated_into(
transaction_status.transaction_index
)
)),
from: transaction_status.from,
to: transaction_status.to,
value: transaction.value,
gas_price: transaction.gas_price,
gas: transaction.gas_limit,
input: transaction.input.clone(),
creates: transaction_status.contract_address,
raw: vec![], // TODO,
public_key: None, // TODO,
chain_id: None, // TODO
standard_v: U256::zero(), // TODO
v: U256::zero(), // TODO
r: U256::zero(), // TODO
s: U256::zero(), // TODO
// Option<TransactionCondition>, Not supported? By now all pending
// transactions are stored on chain on_finalize.
condition: None,
})
}

pub fn block_by_number(number: T::BlockNumber) -> Option<ethereum::Block> {
if <BlockNumbers<T>>::contains_key(number) {
Expand Down
2 changes: 1 addition & 1 deletion rpc/core/src/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ pub trait EthApi {

/// Get transaction by its hash.
#[rpc(name = "eth_getTransactionByHash")]
fn transaction_by_hash(&self, _: H256) -> BoxFuture<Option<Transaction>>;
fn transaction_by_hash(&self, _: H256) -> Result<Option<Transaction>>;

/// Returns transaction at given block hash and index.
#[rpc(name = "eth_getTransactionByBlockHashAndIndex")]
Expand Down
27 changes: 26 additions & 1 deletion rpc/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

#![cfg_attr(not(feature = "std"), no_std)]

use sp_core::{H160, H256, U256};
use sp_core::{H160, H256, H512, U256};
use ethereum::{Log, Block as EthereumBlock};
use ethereum_types::Bloom;
use codec::{Encode, Decode};
Expand All @@ -33,6 +33,30 @@ pub struct TransactionStatus {
pub logs_bloom: Bloom,
}

#[derive(Eq, PartialEq, Clone, Encode, Decode, sp_runtime::RuntimeDebug)]
pub struct Transaction {
pub hash: H256,
pub nonce: U256,
pub block_hash: Option<H256>,
pub block_number: Option<U256>,
pub transaction_index: Option<U256>,
pub from: H160,
pub to: Option<H160>,
pub value: U256,
pub gas_price: U256,
pub gas: U256,
pub input: Vec<u8>,
pub creates: Option<H160>,
pub raw: Vec<u8>,
pub public_key: Option<H512>,
pub chain_id: Option<u64>,
pub standard_v: U256,
pub v: U256,
pub r: U256,
pub s: U256,
pub condition: Option<u64>,
}

sp_api::decl_runtime_apis! {
/// API necessary for Ethereum-compatibility layer.
pub trait EthereumRuntimeApi {
Expand All @@ -45,6 +69,7 @@ sp_api::decl_runtime_apis! {
fn block_by_number(number: u32) -> Option<EthereumBlock>;
fn block_transaction_count_by_number(number: u32) -> Option<U256>;
fn block_by_hash(hash: H256) -> Option<EthereumBlock>;
fn transaction_by_hash(hash: H256) -> Option<Transaction>;
}
}

Expand Down
44 changes: 41 additions & 3 deletions rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use sp_runtime::traits::BlakeTwo256;
use frontier_rpc_core::EthApi as EthApiT;
use frontier_rpc_core::types::{
BlockNumber, Bytes, CallRequest, EthAccount, Filter, Index, Log, Receipt, RichBlock,
SyncStatus, Transaction, Work, Rich, Block, BlockTransactions
SyncStatus, Transaction, Work, Rich, Block, BlockTransactions, TransactionCondition
};
use frontier_rpc_primitives::{EthereumRuntimeApi, ConvertTransaction};

Expand Down Expand Up @@ -341,8 +341,46 @@ impl<B, C, SC, P, CT, BE> EthApiT for EthApi<B, C, SC, P, CT, BE> where
unimplemented!("estimate_gas");
}

fn transaction_by_hash(&self, _: H256) -> BoxFuture<Option<Transaction>> {
unimplemented!("transaction_by_hash");
fn transaction_by_hash(&self, hash: H256) -> Result<Option<Transaction>> {
let header = self
.select_chain
.best_chain()
.map_err(|_| internal_err("fetch header failed"))?;

if let Ok(Some(transaction)) = self.client.runtime_api().transaction_by_hash(
&BlockId::Hash(header.hash()), hash) {
return Ok(Some(
Transaction {
hash: transaction.hash,
nonce: transaction.nonce,
block_hash: transaction.block_hash,
block_number: transaction.block_number,
transaction_index: transaction.transaction_index,
from: transaction.from,
to: transaction.to,
value: transaction.value,
gas_price: transaction.gas_price,
gas: transaction.gas,
input: Bytes(transaction.input),
creates: transaction.creates,
raw: Bytes(transaction.raw),
public_key: transaction.public_key,
chain_id: match transaction.chain_id {
Some(chain_id) => Some(U64::from(chain_id)),
None => None
},
standard_v: transaction.standard_v,
v: transaction.v,
r: transaction.r,
s: transaction.s,
condition: match transaction.chain_id {
Some(condition) => Some(TransactionCondition::Number(condition)),
None => None
}
}
));
}
Ok(None)
}

fn transaction_by_block_hash_and_index(
Expand Down
5 changes: 5 additions & 0 deletions template/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub use frame_support::{
StorageValue,
};
use ethereum::Block as EthereumBlock;
use frontier_rpc_primitives::Transaction as FullTransaction;


#[cfg(any(feature = "std", test))]
Expand Down Expand Up @@ -488,6 +489,10 @@ impl_runtime_apis! {
fn block_by_hash(hash: H256) -> Option<EthereumBlock> {
<ethereum::Module<Runtime>>::block_by_hash(hash)
}

fn transaction_by_hash(hash: H256) -> Option<FullTransaction> {
<ethereum::Module<Runtime>>::transaction_by_hash(hash)
}
}

impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<
Expand Down