on-demand contract code request
This commit is contained in:
parent
d253a9c864
commit
9524ebbff1
@ -86,6 +86,7 @@ enum Pending {
|
|||||||
Block(request::Body, Sender<encoded::Block>),
|
Block(request::Body, Sender<encoded::Block>),
|
||||||
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>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// On demand request service. See module docs for more details.
|
/// On demand request service. See module docs for more details.
|
||||||
@ -340,6 +341,52 @@ impl OnDemand {
|
|||||||
trace!(target: "on_demand", "No suitable peer for request");
|
trace!(target: "on_demand", "No suitable peer for request");
|
||||||
sender.complete(Err(Error::NoPeersAvailable));
|
sender.complete(Err(Error::NoPeersAvailable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request code by address, known code hash, and block header.
|
||||||
|
pub fn code(&self, ctx: &BasicContext, req: request::Code) -> Response<Bytes> {
|
||||||
|
let (sender, receiver) = oneshot::channel();
|
||||||
|
|
||||||
|
// fast path for no code.
|
||||||
|
if req.code_hash == ::util::sha3::SHA3_EMPTY {
|
||||||
|
sender.complete(Ok(Vec::new()))
|
||||||
|
} else {
|
||||||
|
self.dispatch_code(ctx, req, sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
Response(receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_code(&self, ctx: &BasicContext, req: request::Code, sender: Sender<Bytes>) {
|
||||||
|
let num = req.block_id.1;
|
||||||
|
let les_req = LesRequest::Codes(les_request::ContractCodes {
|
||||||
|
code_requests: vec![les_request::ContractCode {
|
||||||
|
block_hash: req.block_id.0,
|
||||||
|
account_key: ::util::Hashable::sha3(&req.address),
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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::Code(req, sender)
|
||||||
|
);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Err(e) =>
|
||||||
|
trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: retrying.
|
||||||
|
trace!(target: "on_demand", "No suitable peer for request");
|
||||||
|
sender.complete(Err(Error::NoPeersAvailable));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler for OnDemand {
|
impl Handler for OnDemand {
|
||||||
@ -365,6 +412,8 @@ impl Handler for OnDemand {
|
|||||||
=> self.dispatch_block_receipts(ctx, req, sender),
|
=> self.dispatch_block_receipts(ctx, req, sender),
|
||||||
Pending::Account(req, sender)
|
Pending::Account(req, sender)
|
||||||
=> self.dispatch_account(ctx, req, sender),
|
=> self.dispatch_account(ctx, req, sender),
|
||||||
|
Pending::Code(req, sender)
|
||||||
|
=> self.dispatch_code(ctx, req, sender),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -517,6 +566,34 @@ impl Handler for OnDemand {
|
|||||||
_ => panic!("Only account request fetches state proof; qed"),
|
_ => panic!("Only account request fetches state proof; qed"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_code(&self, ctx: &EventContext, req_id: ReqId, codes: &[Bytes]) {
|
||||||
|
let peer = ctx.peer();
|
||||||
|
let req = match self.pending_requests.write().remove(&req_id) {
|
||||||
|
Some(req) => req,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
match req {
|
||||||
|
Pending::Code(req, sender) => {
|
||||||
|
if let Some(code) = codes.get(0) {
|
||||||
|
match req.check_response(code.as_slice()) {
|
||||||
|
Ok(()) => {
|
||||||
|
sender.complete(Ok(code.clone()));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error handling response for code request: {:?}", e);
|
||||||
|
ctx.disable_peer(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dispatch_code(ctx.as_basic(), req, sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("Only code request fetches code; qed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -37,7 +37,7 @@ pub enum Error {
|
|||||||
BadProof,
|
BadProof,
|
||||||
/// Wrong header number.
|
/// Wrong header number.
|
||||||
WrongNumber(u64, u64),
|
WrongNumber(u64, u64),
|
||||||
/// Wrong header hash.
|
/// Wrong hash.
|
||||||
WrongHash(H256, H256),
|
WrongHash(H256, H256),
|
||||||
/// Wrong trie root.
|
/// Wrong trie root.
|
||||||
WrongTrieRoot(H256, H256),
|
WrongTrieRoot(H256, H256),
|
||||||
@ -188,6 +188,28 @@ impl Account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request for account code.
|
||||||
|
pub struct Code {
|
||||||
|
/// Block hash, number pair.
|
||||||
|
pub block_id: (H256, u64),
|
||||||
|
/// Address requested.
|
||||||
|
pub address: Address,
|
||||||
|
/// Account's code hash.
|
||||||
|
pub code_hash: H256,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Code {
|
||||||
|
/// Check a response with code against the code hash.
|
||||||
|
pub fn check_response(&self, code: &[u8]) -> Result<(), Error> {
|
||||||
|
let found_hash = code.sha3();
|
||||||
|
if found_hash == self.code_hash {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::WrongHash(self.code_hash, found_hash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -328,4 +350,17 @@ mod tests {
|
|||||||
|
|
||||||
assert!(req.check_response(&proof[..]).is_ok());
|
assert!(req.check_response(&proof[..]).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_code() {
|
||||||
|
let code = vec![1u8; 256];
|
||||||
|
let req = Code {
|
||||||
|
block_id: (Default::default(), 2),
|
||||||
|
address: Default::default(),
|
||||||
|
code_hash: ::util::Hashable::sha3(&code),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(req.check_response(&code).is_ok());
|
||||||
|
assert!(req.check_response(&[]).is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user