request transaction proofs from on_demand

This commit is contained in:
Robert Habermeier
2017-02-25 20:10:38 +01:00
parent 7c541117b3
commit 69e82e15a3
7 changed files with 178 additions and 13 deletions

View File

@@ -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();

View File

@@ -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)
}

View File

@@ -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::*;