request transaction proofs from on_demand
This commit is contained in:
parent
7c541117b3
commit
69e82e15a3
@ -1185,7 +1185,10 @@ impl LightProtocol {
|
|||||||
|
|
||||||
// Receive a request for proof-of-execution.
|
// Receive a request for proof-of-execution.
|
||||||
fn get_transaction_proof(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
|
fn get_transaction_proof(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> {
|
||||||
const MAX_GAS: usize = 10_000_000; // refuse to execute more than this amount of gas at once.
|
// refuse to execute more than this amount of gas at once.
|
||||||
|
// this is appx. the point at which the proof of execution would no longer fit in
|
||||||
|
// a single Devp2p packet.
|
||||||
|
const MAX_GAS: usize = 50_000_000;
|
||||||
use util::Uint;
|
use util::Uint;
|
||||||
|
|
||||||
let peers = self.peers.read();
|
let peers = self.peers.read();
|
||||||
|
@ -23,12 +23,14 @@ use std::collections::HashMap;
|
|||||||
use ethcore::basic_account::BasicAccount;
|
use ethcore::basic_account::BasicAccount;
|
||||||
use ethcore::encoded;
|
use ethcore::encoded;
|
||||||
use ethcore::receipt::Receipt;
|
use ethcore::receipt::Receipt;
|
||||||
|
use ethcore::state::ProvedExecution;
|
||||||
|
use ethcore::executed::{Executed, ExecutionError};
|
||||||
|
|
||||||
use futures::{Async, Poll, Future};
|
use futures::{Async, Poll, Future};
|
||||||
use futures::sync::oneshot::{self, Sender, Receiver};
|
use futures::sync::oneshot::{self, Sender, Receiver};
|
||||||
use network::PeerId;
|
use network::PeerId;
|
||||||
use rlp::{RlpStream, Stream};
|
use rlp::{RlpStream, Stream};
|
||||||
use util::{Bytes, RwLock, U256};
|
use util::{Bytes, DBValue, RwLock, U256};
|
||||||
use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY_LIST_RLP};
|
use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY_LIST_RLP};
|
||||||
|
|
||||||
use net::{Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId};
|
use net::{Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId};
|
||||||
@ -50,6 +52,7 @@ enum Pending {
|
|||||||
BlockReceipts(request::BlockReceipts, Sender<Vec<Receipt>>),
|
BlockReceipts(request::BlockReceipts, Sender<Vec<Receipt>>),
|
||||||
Account(request::Account, Sender<BasicAccount>),
|
Account(request::Account, Sender<BasicAccount>),
|
||||||
Code(request::Code, Sender<Bytes>),
|
Code(request::Code, Sender<Bytes>),
|
||||||
|
TxProof(request::TransactionProof, Sender<Result<Executed, ExecutionError>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// On demand request service. See module docs for more details.
|
/// On demand request service. See module docs for more details.
|
||||||
@ -347,6 +350,50 @@ impl OnDemand {
|
|||||||
self.orphaned_requests.write().push(pending)
|
self.orphaned_requests.write().push(pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request proof-of-execution for a transaction.
|
||||||
|
pub fn transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof) -> Receiver<Result<Executed, ExecutionError>> {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
|
||||||
|
self.dispatch_transaction_proof(ctx, req, sender);
|
||||||
|
|
||||||
|
receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof, sender: Sender<Result<Executed, ExecutionError>>) {
|
||||||
|
let num = req.header.number();
|
||||||
|
let les_req = LesRequest::TransactionProof(les_request::TransactionProof {
|
||||||
|
at: req.header.hash(),
|
||||||
|
from: req.tx.sender(),
|
||||||
|
gas: req.tx.gas,
|
||||||
|
gas_price: req.tx.gas_price,
|
||||||
|
action: req.tx.action.clone(),
|
||||||
|
value: req.tx.value,
|
||||||
|
data: req.tx.data.clone(),
|
||||||
|
});
|
||||||
|
let pending = Pending::TxProof(req, sender);
|
||||||
|
|
||||||
|
// we're looking for a peer with serveStateSince(num)
|
||||||
|
for (id, peer) in self.peers.read().iter() {
|
||||||
|
if peer.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= num) {
|
||||||
|
match ctx.request_from(*id, les_req.clone()) {
|
||||||
|
Ok(req_id) => {
|
||||||
|
trace!(target: "on_demand", "Assigning request to peer {}", id);
|
||||||
|
self.pending_requests.write().insert(
|
||||||
|
req_id,
|
||||||
|
pending
|
||||||
|
);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Err(e) =>
|
||||||
|
trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!(target: "on_demand", "No suitable peer for request");
|
||||||
|
self.orphaned_requests.write().push(pending)
|
||||||
|
}
|
||||||
|
|
||||||
// dispatch orphaned requests, and discard those for which the corresponding
|
// dispatch orphaned requests, and discard those for which the corresponding
|
||||||
// receiver has been dropped.
|
// receiver has been dropped.
|
||||||
fn dispatch_orphaned(&self, ctx: &BasicContext) {
|
fn dispatch_orphaned(&self, ctx: &BasicContext) {
|
||||||
@ -390,6 +437,8 @@ impl OnDemand {
|
|||||||
if !check_hangup(&mut sender) { self.dispatch_account(ctx, req, sender) },
|
if !check_hangup(&mut sender) { self.dispatch_account(ctx, req, sender) },
|
||||||
Pending::Code(req, mut sender) =>
|
Pending::Code(req, mut sender) =>
|
||||||
if !check_hangup(&mut sender) { self.dispatch_code(ctx, req, sender) },
|
if !check_hangup(&mut sender) { self.dispatch_code(ctx, req, sender) },
|
||||||
|
Pending::TxProof(req, mut sender) =>
|
||||||
|
if !check_hangup(&mut sender) { self.dispatch_transaction_proof(ctx, req, sender) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -596,6 +645,36 @@ impl Handler for OnDemand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_transaction_proof(&self, ctx: &EventContext, req_id: ReqId, items: &[DBValue]) {
|
||||||
|
let peer = ctx.peer();
|
||||||
|
let req = match self.pending_requests.write().remove(&req_id) {
|
||||||
|
Some(req) => req,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
match req {
|
||||||
|
Pending::TxProof(req, sender) => {
|
||||||
|
match req.check_response(items) {
|
||||||
|
ProvedExecution::Complete(executed) => {
|
||||||
|
sender.complete(Ok(executed));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ProvedExecution::Failed(err) => {
|
||||||
|
sender.complete(Err(err));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ProvedExecution::BadProof => {
|
||||||
|
warn!("Error handling response for transaction proof request");
|
||||||
|
ctx.disable_peer(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dispatch_transaction_proof(ctx.as_basic(), req, sender);
|
||||||
|
}
|
||||||
|
_ => panic!("Only transaction proof request dispatches transaction proof requests; qed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn tick(&self, ctx: &BasicContext) {
|
fn tick(&self, ctx: &BasicContext) {
|
||||||
self.dispatch_orphaned(ctx)
|
self.dispatch_orphaned(ctx)
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,18 @@
|
|||||||
|
|
||||||
//! Request types, verification, and verification errors.
|
//! Request types, verification, and verification errors.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ethcore::basic_account::BasicAccount;
|
use ethcore::basic_account::BasicAccount;
|
||||||
use ethcore::encoded;
|
use ethcore::encoded;
|
||||||
|
use ethcore::engines::Engine;
|
||||||
|
use ethcore::env_info::EnvInfo;
|
||||||
use ethcore::receipt::Receipt;
|
use ethcore::receipt::Receipt;
|
||||||
|
use ethcore::state::{self, ProvedExecution};
|
||||||
|
use ethcore::transaction::SignedTransaction;
|
||||||
|
|
||||||
use rlp::{RlpStream, Stream, UntrustedRlp, View};
|
use rlp::{RlpStream, Stream, UntrustedRlp, View};
|
||||||
use util::{Address, Bytes, HashDB, H256, U256};
|
use util::{Address, Bytes, DBValue, HashDB, H256, U256};
|
||||||
use util::memorydb::MemoryDB;
|
use util::memorydb::MemoryDB;
|
||||||
use util::sha3::Hashable;
|
use util::sha3::Hashable;
|
||||||
use util::trie::{Trie, TrieDB, TrieError};
|
use util::trie::{Trie, TrieDB, TrieError};
|
||||||
@ -231,6 +237,33 @@ impl Code {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request for transaction execution, along with the parts necessary to verify the proof.
|
||||||
|
pub struct TransactionProof {
|
||||||
|
/// The transaction to request proof of.
|
||||||
|
pub tx: SignedTransaction,
|
||||||
|
/// Block header.
|
||||||
|
pub header: encoded::Header,
|
||||||
|
/// Transaction environment info.
|
||||||
|
pub env_info: EnvInfo,
|
||||||
|
/// Consensus engine.
|
||||||
|
pub engine: Arc<Engine>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransactionProof {
|
||||||
|
/// Check the proof, returning the proved execution or indicate that the proof was bad.
|
||||||
|
pub fn check_response(&self, state_items: &[DBValue]) -> ProvedExecution {
|
||||||
|
let root = self.header.state_root();
|
||||||
|
|
||||||
|
state::check_proof(
|
||||||
|
state_items,
|
||||||
|
root,
|
||||||
|
&self.tx,
|
||||||
|
&*self.engine,
|
||||||
|
&self.env_info,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1616,7 +1616,6 @@ impl ::client::ProvingBlockChainClient for Client {
|
|||||||
let options = TransactOptions { tracing: false, vm_tracing: false, check_nonce: false };
|
let options = TransactOptions { tracing: false, vm_tracing: false, check_nonce: false };
|
||||||
let res = Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(&transaction, options);
|
let res = Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(&transaction, options);
|
||||||
|
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(ExecutionError::Internal(_)) => return None,
|
Err(ExecutionError::Internal(_)) => return None,
|
||||||
_ => return Some(state.drop().1.extract_proof()),
|
_ => return Some(state.drop().1.extract_proof()),
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Environment information for transaction execution.
|
||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use util::{U256, Address, H256, Hashable};
|
use util::{U256, Address, H256, Hashable};
|
||||||
@ -25,7 +27,7 @@ use ethjson;
|
|||||||
pub type LastHashes = Vec<H256>;
|
pub type LastHashes = Vec<H256>;
|
||||||
|
|
||||||
/// Information concerning the execution environment for a message-call/contract-creation.
|
/// Information concerning the execution environment for a message-call/contract-creation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EnvInfo {
|
pub struct EnvInfo {
|
||||||
/// The block number.
|
/// The block number.
|
||||||
pub number: BlockNumber,
|
pub number: BlockNumber,
|
||||||
|
@ -79,7 +79,6 @@
|
|||||||
//! cargo build --release
|
//! cargo build --release
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
|
||||||
extern crate ethcore_io as io;
|
extern crate ethcore_io as io;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate crypto;
|
extern crate crypto;
|
||||||
@ -140,12 +139,12 @@ pub mod action_params;
|
|||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod verification;
|
pub mod verification;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
pub mod env_info;
|
||||||
#[macro_use] pub mod evm;
|
#[macro_use] pub mod evm;
|
||||||
|
|
||||||
mod cache_manager;
|
mod cache_manager;
|
||||||
mod blooms;
|
mod blooms;
|
||||||
mod basic_types;
|
mod basic_types;
|
||||||
mod env_info;
|
|
||||||
mod pod_account;
|
mod pod_account;
|
||||||
mod state_db;
|
mod state_db;
|
||||||
mod account_db;
|
mod account_db;
|
||||||
|
@ -31,6 +31,7 @@ use factory::Factories;
|
|||||||
use trace::FlatTrace;
|
use trace::FlatTrace;
|
||||||
use pod_account::*;
|
use pod_account::*;
|
||||||
use pod_state::{self, PodState};
|
use pod_state::{self, PodState};
|
||||||
|
use types::executed::{Executed, ExecutionError};
|
||||||
use types::state_diff::StateDiff;
|
use types::state_diff::StateDiff;
|
||||||
use transaction::SignedTransaction;
|
use transaction::SignedTransaction;
|
||||||
use state_db::StateDB;
|
use state_db::StateDB;
|
||||||
@ -60,6 +61,17 @@ pub struct ApplyOutcome {
|
|||||||
/// Result type for the execution ("application") of a transaction.
|
/// Result type for the execution ("application") of a transaction.
|
||||||
pub type ApplyResult = Result<ApplyOutcome, Error>;
|
pub type ApplyResult = Result<ApplyOutcome, Error>;
|
||||||
|
|
||||||
|
/// Return type of proof validity check.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ProvedExecution {
|
||||||
|
/// Proof wasn't enough to complete execution.
|
||||||
|
BadProof,
|
||||||
|
/// The transaction failed, but not due to a bad proof.
|
||||||
|
Failed(ExecutionError),
|
||||||
|
/// The transaction successfully completd with the given proof.
|
||||||
|
Complete(Executed),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
||||||
/// Account modification state. Used to check if the account was
|
/// Account modification state. Used to check if the account was
|
||||||
/// Modified in between commits and overall.
|
/// Modified in between commits and overall.
|
||||||
@ -150,6 +162,39 @@ impl AccountEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check the given proof of execution.
|
||||||
|
/// `Err(ExecutionError::Internal)` indicates failure, everything else indicates
|
||||||
|
/// a successful proof (as the transaction itself may be poorly chosen).
|
||||||
|
pub fn check_proof(
|
||||||
|
proof: &[::util::DBValue],
|
||||||
|
root: H256,
|
||||||
|
transaction: &SignedTransaction,
|
||||||
|
engine: &Engine,
|
||||||
|
env_info: &EnvInfo,
|
||||||
|
) -> ProvedExecution {
|
||||||
|
let backend = self::backend::ProofCheck::new(proof);
|
||||||
|
let mut factories = Factories::default();
|
||||||
|
factories.accountdb = ::account_db::Factory::Plain;
|
||||||
|
|
||||||
|
let res = State::from_existing(
|
||||||
|
backend,
|
||||||
|
root,
|
||||||
|
engine.account_start_nonce(),
|
||||||
|
factories
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut state = match res {
|
||||||
|
Ok(state) => state,
|
||||||
|
Err(_) => return ProvedExecution::BadProof,
|
||||||
|
};
|
||||||
|
|
||||||
|
match state.execute(env_info, engine, transaction, false) {
|
||||||
|
Ok(executed) => ProvedExecution::Complete(executed),
|
||||||
|
Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof,
|
||||||
|
Err(e) => ProvedExecution::Failed(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Representation of the entire state of all accounts in the system.
|
/// Representation of the entire state of all accounts in the system.
|
||||||
///
|
///
|
||||||
/// `State` can work together with `StateDB` to share account cache.
|
/// `State` can work together with `StateDB` to share account cache.
|
||||||
@ -548,16 +593,12 @@ impl<B: Backend> State<B> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a given transaction.
|
/// Execute a given transaction, producing a receipt and an optional trace.
|
||||||
/// This will change the state accordingly.
|
/// This will change the state accordingly.
|
||||||
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
||||||
// let old = self.to_pod();
|
// let old = self.to_pod();
|
||||||
|
|
||||||
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
|
let e = self.execute(env_info, engine, t, tracing)?;
|
||||||
let vm_factory = self.factories.vm.clone();
|
|
||||||
let e = Executive::new(self, env_info, engine, &vm_factory).transact(t, options)?;
|
|
||||||
|
|
||||||
// TODO uncomment once to_pod() works correctly.
|
|
||||||
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod()));
|
||||||
let state_root = if env_info.number < engine.params().eip98_transition {
|
let state_root = if env_info.number < engine.params().eip98_transition {
|
||||||
self.commit()?;
|
self.commit()?;
|
||||||
@ -570,6 +611,15 @@ impl<B: Backend> State<B> {
|
|||||||
Ok(ApplyOutcome{receipt: receipt, trace: e.trace})
|
Ok(ApplyOutcome{receipt: receipt, trace: e.trace})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute a given transaction.
|
||||||
|
fn execute(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> Result<Executed, ExecutionError> {
|
||||||
|
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
|
||||||
|
let vm_factory = self.factories.vm.clone();
|
||||||
|
|
||||||
|
Executive::new(self, env_info, engine, &vm_factory).transact(t, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit.
|
/// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit.
|
||||||
/// `accounts` is mutable because we may need to commit the code or storage and record that.
|
/// `accounts` is mutable because we may need to commit the code or storage and record that.
|
||||||
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
||||||
|
Loading…
Reference in New Issue
Block a user