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.
|
||||
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;
|
||||
|
||||
let peers = self.peers.read();
|
||||
|
@ -23,12 +23,14 @@ use std::collections::HashMap;
|
||||
use ethcore::basic_account::BasicAccount;
|
||||
use ethcore::encoded;
|
||||
use ethcore::receipt::Receipt;
|
||||
use ethcore::state::ProvedExecution;
|
||||
use ethcore::executed::{Executed, ExecutionError};
|
||||
|
||||
use futures::{Async, Poll, Future};
|
||||
use futures::sync::oneshot::{self, Sender, Receiver};
|
||||
use network::PeerId;
|
||||
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 net::{Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId};
|
||||
@ -50,6 +52,7 @@ enum Pending {
|
||||
BlockReceipts(request::BlockReceipts, Sender<Vec<Receipt>>),
|
||||
Account(request::Account, Sender<BasicAccount>),
|
||||
Code(request::Code, Sender<Bytes>),
|
||||
TxProof(request::TransactionProof, Sender<Result<Executed, ExecutionError>>),
|
||||
}
|
||||
|
||||
/// On demand request service. See module docs for more details.
|
||||
@ -347,6 +350,50 @@ impl OnDemand {
|
||||
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
|
||||
// receiver has been dropped.
|
||||
fn dispatch_orphaned(&self, ctx: &BasicContext) {
|
||||
@ -390,6 +437,8 @@ impl OnDemand {
|
||||
if !check_hangup(&mut sender) { self.dispatch_account(ctx, req, sender) },
|
||||
Pending::Code(req, mut 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) {
|
||||
self.dispatch_orphaned(ctx)
|
||||
}
|
||||
|
@ -16,12 +16,18 @@
|
||||
|
||||
//! Request types, verification, and verification errors.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use ethcore::basic_account::BasicAccount;
|
||||
use ethcore::encoded;
|
||||
use ethcore::engines::Engine;
|
||||
use ethcore::env_info::EnvInfo;
|
||||
use ethcore::receipt::Receipt;
|
||||
use ethcore::state::{self, ProvedExecution};
|
||||
use ethcore::transaction::SignedTransaction;
|
||||
|
||||
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::sha3::Hashable;
|
||||
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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -1616,7 +1616,6 @@ impl ::client::ProvingBlockChainClient for Client {
|
||||
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);
|
||||
|
||||
|
||||
match res {
|
||||
Err(ExecutionError::Internal(_)) => return None,
|
||||
_ => return Some(state.drop().1.extract_proof()),
|
||||
|
@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Environment information for transaction execution.
|
||||
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
use util::{U256, Address, H256, Hashable};
|
||||
@ -25,7 +27,7 @@ use ethjson;
|
||||
pub type LastHashes = Vec<H256>;
|
||||
|
||||
/// Information concerning the execution environment for a message-call/contract-creation.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnvInfo {
|
||||
/// The block number.
|
||||
pub number: BlockNumber,
|
||||
|
@ -79,7 +79,6 @@
|
||||
//! cargo build --release
|
||||
//! ```
|
||||
|
||||
|
||||
extern crate ethcore_io as io;
|
||||
extern crate rustc_serialize;
|
||||
extern crate crypto;
|
||||
@ -140,12 +139,12 @@ pub mod action_params;
|
||||
pub mod db;
|
||||
pub mod verification;
|
||||
pub mod state;
|
||||
pub mod env_info;
|
||||
#[macro_use] pub mod evm;
|
||||
|
||||
mod cache_manager;
|
||||
mod blooms;
|
||||
mod basic_types;
|
||||
mod env_info;
|
||||
mod pod_account;
|
||||
mod state_db;
|
||||
mod account_db;
|
||||
|
@ -31,6 +31,7 @@ use factory::Factories;
|
||||
use trace::FlatTrace;
|
||||
use pod_account::*;
|
||||
use pod_state::{self, PodState};
|
||||
use types::executed::{Executed, ExecutionError};
|
||||
use types::state_diff::StateDiff;
|
||||
use transaction::SignedTransaction;
|
||||
use state_db::StateDB;
|
||||
@ -60,6 +61,17 @@ pub struct ApplyOutcome {
|
||||
/// Result type for the execution ("application") of a transaction.
|
||||
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)]
|
||||
/// Account modification state. Used to check if the account was
|
||||
/// 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.
|
||||
///
|
||||
/// `State` can work together with `StateDB` to share account cache.
|
||||
@ -548,16 +593,12 @@ impl<B: Backend> State<B> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Execute a given transaction.
|
||||
/// Execute a given transaction, producing a receipt and an optional trace.
|
||||
/// This will change the state accordingly.
|
||||
pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult {
|
||||
// let old = self.to_pod();
|
||||
|
||||
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true };
|
||||
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.
|
||||
let e = self.execute(env_info, engine, t, tracing)?;
|
||||
// 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 {
|
||||
self.commit()?;
|
||||
@ -570,6 +611,15 @@ impl<B: Backend> State<B> {
|
||||
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.
|
||||
/// `accounts` is mutable because we may need to commit the code or storage and record that.
|
||||
#[cfg_attr(feature="dev", allow(match_ref_pats))]
|
||||
|
Loading…
Reference in New Issue
Block a user