Light friendly dapps (#5634)
* move native_contracts ABIs to JSON files, add urlhint * port hash-fetch to futures, fix tests * fix dapps compilation, defer async port to later * activate dapps server in the light client * better formatting
This commit is contained in:
committed by
Gav Wood
parent
95d9706fe1
commit
b1eab698d2
@@ -87,6 +87,28 @@ impl From<io::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_hash(path: PathBuf, hash: H256, result: Result<Response, FetchError>) -> Result<PathBuf, Error> {
|
||||
let response = result?;
|
||||
if !response.is_success() {
|
||||
return Err(Error::InvalidStatus);
|
||||
}
|
||||
|
||||
// Read the response
|
||||
let mut reader = io::BufReader::new(response);
|
||||
let mut writer = io::BufWriter::new(fs::File::create(&path)?);
|
||||
io::copy(&mut reader, &mut writer)?;
|
||||
writer.flush()?;
|
||||
|
||||
// And validate the hash
|
||||
let mut file_reader = io::BufReader::new(fs::File::open(&path)?);
|
||||
let content_hash = sha3(&mut file_reader)?;
|
||||
if content_hash != hash {
|
||||
Err(Error::HashMismatch{ got: content_hash, expected: hash })
|
||||
} else {
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
|
||||
/// Default Hash-fetching client using on-chain contract to resolve hashes to URLs.
|
||||
pub struct Client<F: Fetch + 'static = FetchClient> {
|
||||
contract: URLHintContract,
|
||||
@@ -103,7 +125,6 @@ impl Client {
|
||||
}
|
||||
|
||||
impl<F: Fetch + 'static> Client<F> {
|
||||
|
||||
/// Creates new instance of the `Client` given on-chain contract client, fetch service and task runner.
|
||||
pub fn with_fetch(contract: Arc<ContractClient>, fetch: F, remote: Remote) -> Self {
|
||||
Client {
|
||||
@@ -119,44 +140,22 @@ impl<F: Fetch + 'static> HashFetch for Client<F> {
|
||||
fn fetch(&self, hash: H256, on_done: Box<Fn(Result<PathBuf, Error>) + Send>) {
|
||||
debug!(target: "fetch", "Fetching: {:?}", hash);
|
||||
|
||||
let url = self.contract.resolve(hash.to_vec()).map(|content| match content {
|
||||
URLHintResult::Dapp(dapp) => {
|
||||
dapp.url()
|
||||
},
|
||||
URLHintResult::Content(content) => {
|
||||
content.url
|
||||
},
|
||||
}).ok_or_else(|| Error::NoResolution);
|
||||
|
||||
debug!(target: "fetch", "Resolved {:?} to {:?}. Fetching...", hash, url);
|
||||
|
||||
match url {
|
||||
Err(err) => on_done(Err(err)),
|
||||
Ok(url) => {
|
||||
let random_path = self.random_path.clone();
|
||||
let future = self.fetch.fetch(&url).then(move |result| {
|
||||
fn validate_hash(path: PathBuf, hash: H256, result: Result<Response, FetchError>) -> Result<PathBuf, Error> {
|
||||
let response = result?;
|
||||
if !response.is_success() {
|
||||
return Err(Error::InvalidStatus);
|
||||
}
|
||||
|
||||
// Read the response
|
||||
let mut reader = io::BufReader::new(response);
|
||||
let mut writer = io::BufWriter::new(fs::File::create(&path)?);
|
||||
io::copy(&mut reader, &mut writer)?;
|
||||
writer.flush()?;
|
||||
|
||||
// And validate the hash
|
||||
let mut file_reader = io::BufReader::new(fs::File::open(&path)?);
|
||||
let content_hash = sha3(&mut file_reader)?;
|
||||
if content_hash != hash {
|
||||
Err(Error::HashMismatch{ got: content_hash, expected: hash })
|
||||
} else {
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
|
||||
let random_path = self.random_path.clone();
|
||||
let remote_fetch = self.fetch.clone();
|
||||
let future = self.contract.resolve(hash.to_vec())
|
||||
.map_err(|e| { warn!("Error resolving URL: {}", e); Error::NoResolution })
|
||||
.and_then(|maybe_url| maybe_url.ok_or(Error::NoResolution))
|
||||
.map(|content| match content {
|
||||
URLHintResult::Dapp(dapp) => {
|
||||
dapp.url()
|
||||
},
|
||||
URLHintResult::Content(content) => {
|
||||
content.url
|
||||
},
|
||||
})
|
||||
.and_then(move |url| {
|
||||
debug!(target: "fetch", "Resolved {:?} to {:?}. Fetching...", hash, url);
|
||||
let future = remote_fetch.fetch(&url).then(move |result| {
|
||||
debug!(target: "fetch", "Content fetched, validating hash ({:?})", hash);
|
||||
let path = random_path();
|
||||
let res = validate_hash(path.clone(), hash, result);
|
||||
@@ -165,13 +164,13 @@ impl<F: Fetch + 'static> HashFetch for Client<F> {
|
||||
// Remove temporary file in case of error
|
||||
let _ = fs::remove_file(&path);
|
||||
}
|
||||
on_done(res);
|
||||
|
||||
Ok(()) as Result<(), ()>
|
||||
res
|
||||
});
|
||||
self.remote.spawn(self.fetch.process(future));
|
||||
},
|
||||
}
|
||||
remote_fetch.process(future)
|
||||
})
|
||||
.then(move |res| { on_done(res); Ok(()) as Result<(), ()> });
|
||||
|
||||
self.remote.spawn(future);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +224,7 @@ mod tests {
|
||||
let mut registrar = FakeRegistrar::new();
|
||||
registrar.responses = Mutex::new(vec![
|
||||
Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()),
|
||||
Ok("00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000deadcafebeefbeefcafedeaddeedfeedffffffff000000000000000000000000000000000000000000000000000000000000003d68747470733a2f2f657468636f72652e696f2f6173736574732f696d616765732f657468636f72652d626c61636b2d686f72697a6f6e74616c2e706e67000000".from_hex().unwrap()),
|
||||
Ok("00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000deadcafebeefbeefcafedeaddeedfeedffffffff000000000000000000000000000000000000000000000000000000000000003c68747470733a2f2f7061726974792e696f2f6173736574732f696d616765732f657468636f72652d626c61636b2d686f72697a6f6e74616c2e706e6700000000".from_hex().unwrap()),
|
||||
]);
|
||||
registrar
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user