EIP-1186: add eth_getProof RPC-Method (#9001)

* added eth_getAccount

* changed to getProof

* implemented storage_proof

* better formatting of storage proof

* fixed imports;2C

* removed spaces

* fixed whitespace

* fixed docker

* added doc

* fixed Compile-error

* expose more ports

* added eth_getAccount

* changed to getProof

* implemented storage_proof

* better formatting of storage proof

* fixed docker

* removed slockit-changes

* fixed Dockerfile

* intend

* spaces

* removed spaces

* fixed whitespace

* fixed docker

* tabs

* fixed Compile-error

* added eth_getAccount

* changed to getProof

* implemented storage_proof

* fixed docker

* removed slockit-changes

* fixed Dockerfile

* intend

* spaces

* removed spaces

* fixed whitespace

* fixed docker

* tabs

* merged changes

* fixed warnings

* added eth_getAccount

* changed to getProof

* implemented storage_proof

* better formatting of storage proof

* Update Dockerfile

* fixed docker

* removed slockit-changes

* fixed Dockerfile

* intend

* spaces

* removed spaces

* fixed whitespace

* fixed docker

* tabs

* added eth_getAccount

* changed to getProof

* implemented storage_proof

* removed spaces

* fixed whitespace

* fixed docker

* added eth_getAccount

* changed to getProof

* implemented storage_proof

* better formatting of storage proof

* fixed docker

* removed slockit-changes

* fixed Dockerfile

* intend

* spaces

* removed spaces

* fixed whitespace

* fixed docker

* tabs

* merged changes

* fixed merge error

* fixed formatting

* fixed rename_all = "camelCase"

* fixed tabs

* fixed spaces

* removed port exposer

* formatting

* fixed comment

* use filter_map

* formatting

* use better variable names

* changed casting

* fixed tabs

* remote into() from address

* remove space

Co-Authored-By: simon-jentzsch <simon@slock.it>

* fixed storage_index

Co-Authored-By: simon-jentzsch <simon@slock.it>

* fixed clone

* fixed format

Co-Authored-By: simon-jentzsch <simon@slock.it>

* fixed empty lines

* removed Option from EthAccount

* fixed storage_index

* implemented test and fixed the struct-spaces

* fixed tests

* added experimental RPCs flag for getProof

* optmized code
This commit is contained in:
Simon Jentzsch 2018-11-21 20:09:33 +01:00 committed by Afri Schoedon
parent 03600dce97
commit 8865b95818
7 changed files with 123 additions and 9 deletions

View File

@ -291,6 +291,7 @@ impl FullDependencies {
allow_pending_receipt_query: !self.geth_compatibility,
send_block_number_in_get_work: !self.geth_compatibility,
gas_price_percentile: self.gas_price_percentile,
allow_experimental_rpcs: self.experimental_rpcs,
}
);
handler.extend_with(client.to_delegate());

View File

@ -21,12 +21,12 @@ use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH};
use std::sync::Arc;
use rlp::Rlp;
use ethereum_types::{U256, H256, Address};
use ethereum_types::{U256, H256, H160, Address};
use parking_lot::Mutex;
use ethash::{self, SeedHashCompute};
use ethcore::account_provider::AccountProvider;
use ethcore::client::{BlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo};
use ethcore::client::{BlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo, ProvingBlockChainClient};
use ethcore::filter::Filter as EthcoreFilter;
use ethcore::header::{BlockNumber as EthBlockNumber};
use ethcore::miner::{self, MinerService};
@ -35,6 +35,7 @@ use ethcore::encoded;
use sync::SyncProvider;
use miner::external::ExternalMinerService;
use transaction::{SignedTransaction, LocalizedTransaction};
use hash::keccak;
use jsonrpc_core::{BoxFuture, Result};
use jsonrpc_core::futures::future;
@ -46,7 +47,7 @@ use v1::helpers::block_import::is_major_importing;
use v1::traits::Eth;
use v1::types::{
RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo,
Transaction, CallRequest, Index, Filter, Log, Receipt, Work,
Transaction, CallRequest, Index, Filter, Log, Receipt, Work, EthAccount, StorageProof,
H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256, block_number_to_id,
U64 as RpcU64,
};
@ -64,6 +65,8 @@ pub struct EthClientOptions {
pub send_block_number_in_get_work: bool,
/// Gas Price Percentile used as default gas price.
pub gas_price_percentile: usize,
/// Enable Experimental RPC-Calls
pub allow_experimental_rpcs: bool,
}
impl EthClientOptions {
@ -83,6 +86,7 @@ impl Default for EthClientOptions {
allow_pending_receipt_query: true,
send_block_number_in_get_work: true,
gas_price_percentile: 50,
allow_experimental_rpcs: false,
}
}
}
@ -450,7 +454,7 @@ fn check_known<C>(client: &C, number: BlockNumber) -> Result<()> where C: BlockC
const MAX_QUEUE_SIZE_TO_MINE_ON: usize = 4; // because uncles go back 6.
impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<C, SN, S, M, EM> where
C: miner::BlockChainClient + BlockChainClient + StateClient<State=T> + Call<State=T> + EngineInfo + 'static,
C: miner::BlockChainClient + StateClient<State=T> + ProvingBlockChainClient + Call<State=T> + EngineInfo + 'static,
SN: SnapshotService + 'static,
S: SyncProvider + 'static,
M: MinerService<State=T> + 'static,
@ -547,6 +551,50 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
Box::new(future::done(res))
}
fn proof(&self, address: RpcH160, values: Vec<RpcH256>, num: Trailing<BlockNumber>) -> BoxFuture<EthAccount> {
try_bf!(errors::require_experimental(self.options.allow_experimental_rpcs, "1186"));
let a: H160 = address.clone().into();
let key1 = keccak(a);
let num = num.unwrap_or_default();
let id = match num {
BlockNumber::Num(n) => BlockId::Number(n),
BlockNumber::Earliest => BlockId::Earliest,
BlockNumber::Latest => BlockId::Latest,
BlockNumber::Pending => {
warn!("`Pending` is deprecated and may be removed in future versions. Falling back to `Latest`");
BlockId::Latest
}
};
try_bf!(check_known(&*self.client, num.clone()));
let res = match self.client.prove_account(key1, id) {
Some((proof,account)) => Ok(EthAccount {
address: address,
balance: account.balance.into(),
nonce: account.nonce.into(),
code_hash: account.code_hash.into(),
storage_hash: account.storage_root.into(),
account_proof: proof.into_iter().map(Bytes::new).collect(),
storage_proof: values.into_iter().filter_map(|storage_index| {
let key2: H256 = storage_index.into();
self.client.prove_storage(key1, keccak(key2), id)
.map(|(storage_proof,storage_value)| StorageProof {
key: key2.into(),
value: storage_value.into(),
proof: storage_proof.into_iter().map(Bytes::new).collect()
})
})
.collect::<Vec<StorageProof>>()
}),
None => Err(errors::state_pruned()),
};
Box::new(future::done(res))
}
fn storage_at(&self, address: RpcH160, pos: RpcU256, num: Trailing<BlockNumber>) -> BoxFuture<RpcH256> {
let address: Address = RpcH160::into(address);
let position: U256 = RpcU256::into(pos);

View File

@ -47,7 +47,7 @@ use v1::helpers::light_fetch::{self, LightFetch};
use v1::traits::Eth;
use v1::types::{
RichBlock, Block, BlockTransactions, BlockNumber, LightBlockNumber, Bytes, SyncStatus, SyncInfo,
Transaction, CallRequest, Index, Filter, Log, Receipt, Work,
Transaction, CallRequest, Index, Filter, Log, Receipt, Work, EthAccount,
H64 as RpcH64, H256 as RpcH256, H160 as RpcH160, U256 as RpcU256,
U64 as RpcU64,
};
@ -475,6 +475,10 @@ impl<T: LightChainClient + 'static> Eth for EthClient<T> {
}))
}
fn proof(&self, _address: RpcH160, _values:Vec<RpcH256>, _num: Trailing<BlockNumber>) -> BoxFuture<EthAccount> {
Box::new(future::err(errors::unimplemented(None)))
}
fn compilers(&self) -> Result<Vec<String>> {
Err(errors::deprecated("Compilation functionality is deprecated.".to_string()))
}

View File

@ -38,7 +38,7 @@ use parity_runtime::Runtime;
use jsonrpc_core::IoHandler;
use v1::helpers::dispatch::FullDispatcher;
use v1::helpers::nonce;
use v1::impls::{EthClient, SigningUnsafeClient};
use v1::impls::{EthClient, EthClientOptions, SigningUnsafeClient};
use v1::metadata::Metadata;
use v1::tests::helpers::{TestSnapshotService, TestSyncProvider, Config};
use v1::traits::eth::Eth;
@ -140,7 +140,13 @@ impl EthTester {
&opt_account_provider,
&miner_service,
&external_miner,
Default::default(),
EthClientOptions {
pending_nonce_from_queue: false,
allow_pending_receipt_query: true,
send_block_number_in_get_work: true,
gas_price_percentile: 50,
allow_experimental_rpcs: true,
},
);
let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
@ -198,6 +204,33 @@ fn eth_get_balance() {
assert_eq!(tester.handler.handle_request_sync(req_new_acc).unwrap(), res_new_acc);
}
#[test]
fn eth_get_proof() {
let chain = extract_chain!("BlockchainTests/bcWalletTest/wallet2outOf3txs");
let tester = EthTester::from_chain(&chain);
// final account state
let req_latest = r#"{
"jsonrpc": "2.0",
"method": "eth_getProof",
"params": ["0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa", [], "latest"],
"id": 1
}"#;
let res_latest = r#","address":"0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa","balance":"0x9","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","nonce":"0x0","storageHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","storageProof":[]},"id":1}"#.to_owned();
assert!(tester.handler.handle_request_sync(req_latest).unwrap().to_string().ends_with(res_latest.as_str()));
// non-existant account
let req_new_acc = r#"{
"jsonrpc": "2.0",
"method": "eth_getProof",
"params": ["0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",[],"latest"],
"id": 3
}"#;
let res_new_acc = r#","address":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","balance":"0x0","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","nonce":"0x0","storageHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","storageProof":[]},"id":3}"#.to_owned();
assert!(tester.handler.handle_request_sync(req_new_acc).unwrap().to_string().ends_with(res_new_acc.as_str()));
}
#[test]
fn eth_block_number() {
let chain = extract_chain!("BlockchainTests/bcGasPricerTest/RPC_API_Test");

View File

@ -18,7 +18,7 @@
use jsonrpc_core::{Result, BoxFuture};
use jsonrpc_macros::Trailing;
use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index};
use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChanges, Index, EthAccount};
use v1::types::{Log, Receipt, SyncStatus, Transaction, Work};
use v1::types::{H64, H160, H256, U256, U64};
@ -69,6 +69,10 @@ build_rpc_trait! {
#[rpc(name = "eth_getBalance")]
fn balance(&self, H160, Trailing<BlockNumber>) -> BoxFuture<U256>;
/// Returns the account- and storage-values of the specified account including the Merkle-proof
#[rpc(name = "eth_getProof")]
fn proof(&self, H160, Vec<H256>, Trailing<BlockNumber>) -> BoxFuture<EthAccount>;
/// Returns content of the storage at given address.
#[rpc(name = "eth_getStorageAt")]
fn storage_at(&self, H160, U256, Trailing<BlockNumber>) -> BoxFuture<H256>;

View File

@ -13,6 +13,7 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use v1::types::{H160, H256, U256, Bytes};
/// Account information.
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
@ -21,6 +22,28 @@ pub struct AccountInfo {
pub name: String,
}
/// Datastructure with proof for one single storage-entry
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StorageProof {
pub key: U256,
pub value: U256,
pub proof: Vec<Bytes>
}
/// Account information.
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EthAccount {
pub address: H160,
pub balance: U256,
pub nonce: U256,
pub code_hash: H256,
pub storage_hash: H256,
pub account_proof: Vec<Bytes>,
pub storage_proof: Vec<StorageProof>,
}
/// Extended account information (used by `parity_allAccountInfo`).
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
pub struct ExtAccountInfo {

View File

@ -46,8 +46,9 @@ mod private_receipt;
mod eip191;
pub mod pubsub;
pub use self::eip191::{EIP191Version, PresignedTransaction};
pub use self::account_info::{AccountInfo, ExtAccountInfo, HwAccountInfo};
pub use self::account_info::{AccountInfo, ExtAccountInfo, HwAccountInfo, EthAccount, StorageProof};
pub use self::bytes::Bytes;
pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich};
pub use self::block_number::{BlockNumber, LightBlockNumber, block_number_to_id};