request transaction proofs from on_demand
This commit is contained in:
@@ -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::*;
|
||||
|
||||
Reference in New Issue
Block a user