openethereum/ipfs/src/route.rs

247 lines
6.9 KiB
Rust
Raw Normal View History

// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
2017-02-14 20:03:25 +01:00
// Parity Ethereum is free software: you can redistribute it and/or modify
2017-02-14 20:03:25 +01:00
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity Ethereum is distributed in the hope that it will be useful,
2017-02-14 20:03:25 +01:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
2017-02-14 20:03:25 +01:00
use {rlp, multihash, IpfsHandler};
2017-02-14 19:30:37 +01:00
use error::{Error, Result};
use cid::{ToCid, Codec};
use multihash::Hash;
use ethereum_types::H256;
use bytes::Bytes;
use ethcore::client::{BlockId, TransactionId};
2017-02-14 19:30:37 +01:00
type Reason = &'static str;
2017-02-15 18:07:30 +01:00
/// Keeps the state of the response to send out
#[derive(Debug, PartialEq)]
2017-02-14 19:30:37 +01:00
pub enum Out {
OctetStream(Bytes),
NotFound(Reason),
Bad(Reason),
}
impl IpfsHandler {
2017-02-15 18:07:30 +01:00
/// Route path + query string to a specialized method
pub fn route(&self, path: &str, query: Option<&str>) -> Out {
match path {
2017-02-15 18:07:30 +01:00
"/api/v0/block/get" => {
let arg = query.and_then(|q| get_param(q, "arg")).unwrap_or("");
2017-02-14 19:30:37 +01:00
2017-02-15 18:07:30 +01:00
self.route_cid(arg).unwrap_or_else(Into::into)
},
2017-02-14 19:30:37 +01:00
2017-02-15 18:07:30 +01:00
_ => Out::NotFound("Route not found")
}
2017-02-15 18:07:30 +01:00
}
2017-02-14 19:30:37 +01:00
2017-02-15 18:07:30 +01:00
/// Attempt to read Content ID from `arg` query parameter, get a hash and
/// route further by the CID's codec.
fn route_cid(&self, cid: &str) -> Result<Out> {
let cid = cid.to_cid()?;
2017-02-14 19:30:37 +01:00
let mh = multihash::decode(&cid.hash)?;
if mh.alg != Hash::Keccak256 { return Err(Error::UnsupportedHash); }
Upgrade ethereum types (#10670) * cargo upgrade "ethereum-types" --all --allow-prerelease * [ethash] fix compilation errors * [ethkey] fix compilation errors * [journaldb] fix compilation errors * [dir] fix compilation errors * [ethabi] update to 0.7 * wip * [eip-712] fix compilation errors * [ethjson] fix compilation errors * [Cargo.toml] add TODO to remove patches * [ethstore] fix compilation errors * use patched keccak-hash with new primitive-types * wip * [ethcore-network-devp2p] fix compilation errors * [vm] fix compilation errors * [common-types, evm, wasm] fix compilation errors * [ethcore-db] Require AsRef instead of Deref for keys * [ethcore-blockchain] fix some compilation errors * [blooms-db] fix compilation errors Thanks a lot @dvdplm :) * we don't need no rlp ethereum feature * [ethcore] fix some compilation errors * [parity-ipfs-api] fix compilation error * [ethcore-light] fix compilation errors * [Cargo.lock] update parity-common * [ethcore-private-tx] fix some compilation errors * wip * [ethcore-private-tx] fix compilation errors * [parity-updater] fix compilation errors * [parity-rpc] fix compilation errors * [parity-bin] fix other compilation errors * update to new ethereum-types * update keccak-hash * [fastmap] fix compilation in tests * [blooms-db] fix compilation in tests * [common-types] fix compilation in tests * [triehash-ethereum] fix compilation in tests * [ethkey] fix compilation in tests * [pwasm-run-test] fix compilation errors * [wasm] fix compilation errors * [ethjson] fix compilation in tests * [eip-712] fix compilation in tests * [ethcore-blockchain] fix compilation in tests * [ethstore] fix compilation in tests * [ethstore-accounts] fix compilation in tests * [parity-hash-fetch] fix compilation in tests * [parity-whisper] fix compilation in tests * [ethcore-miner] fix compilation in tests * [ethcore-network-devp2p] fix compilation in tests * [*] upgrade rand to 0.6 * [evm] get rid of num-bigint conversions * [ethcore] downgrade trie-standardmap and criterion * [ethcore] fix some warnings * [ethcore] fix compilation in tests * [evmbin] fix compilation in tests * [updater] fix compilation in tests * [ethash] fix compilation in tests * [ethcore-secretstore] fix compilation in tests * [ethcore-sync] fix compilation in tests * [parity-rpc] fix compilation in tests * [ethcore] finally fix compilation in tests FUCK YEAH!!! * [ethstore] lazy_static is unused * [ethcore] fix test * fix up bad merge * [Cargo.toml] remove unused patches * [*] replace some git dependencies with crates.io * [Cargo.toml] remove unused lazy_static * [*] clean up * [ethcore] fix transaction_filter_deprecated test * [private-tx] fix serialization tests * fix more serialization tests * [ethkey] fix smoky test * [rpc] fix tests, please? * [ethcore] remove commented out code * Apply suggestions from code review Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * [ethstore] remove unused dev-dependency * [ethcore] remove resolved TODO * [*] resolve keccak-hash TODO * [*] s/Address::default()/Address::zero() * [rpc] remove Subscribers::new_test * [rpc] remove EthPubSubClient::new_test * [ethcore] use trie-standardmap from crates.io * [dir] fix db_root_path * [ethcore] simplify snapshot::tests::helpers::fill_storage * Apply suggestions from code review Co-Authored-By: David <dvdplm@gmail.com> * [ethcore-secretstore] resolve TODO in serialization * [ethcore-network-devp2p] resolve TODO in save_key * [Cargo.lock] update triehash * [*] use ethabi from crates.io * [ethkey] use secp256k1 from master branch * [Cargo.lock] update eth-secp256k1
2019-06-03 15:36:21 +02:00
let hash = H256::from_slice(&mh.digest);
2017-02-14 19:30:37 +01:00
match cid.codec {
2017-02-15 18:07:30 +01:00
Codec::EthereumBlock => self.block(hash),
Codec::EthereumBlockList => self.block_list(hash),
Codec::EthereumTx => self.transaction(hash),
Codec::EthereumStateTrie => self.state_trie(hash),
Codec::Raw => self.contract_code(hash),
2017-02-14 19:30:37 +01:00
_ => return Err(Error::UnsupportedCid),
}
}
2017-02-15 18:07:30 +01:00
/// Get block header by hash as raw binary.
fn block(&self, hash: H256) -> Result<Out> {
2017-02-14 19:30:37 +01:00
let block_id = BlockId::Hash(hash);
let block = self.client().block_header(block_id).ok_or(Error::BlockNotFound)?;
2017-02-14 19:30:37 +01:00
2017-02-15 18:07:30 +01:00
Ok(Out::OctetStream(block.into_inner()))
2017-02-14 19:30:37 +01:00
}
2017-02-15 18:07:30 +01:00
/// Get list of block ommers by hash as raw binary.
fn block_list(&self, hash: H256) -> Result<Out> {
let uncles = self.client().find_uncles(&hash).ok_or(Error::BlockNotFound)?;
2017-02-14 19:30:37 +01:00
Ok(Out::OctetStream(rlp::encode_list(&uncles)))
2017-02-14 19:30:37 +01:00
}
2017-02-15 18:07:30 +01:00
/// Get transaction by hash and return as raw binary.
fn transaction(&self, hash: H256) -> Result<Out> {
2017-02-14 19:30:37 +01:00
let tx_id = TransactionId::Hash(hash);
let tx = self.client().transaction(tx_id).ok_or(Error::TransactionNotFound)?;
2017-02-14 19:30:37 +01:00
Ok(Out::OctetStream(rlp::encode(&*tx)))
2017-02-14 19:30:37 +01:00
}
2017-02-15 18:07:30 +01:00
/// Get state trie node by hash and return as raw binary.
fn state_trie(&self, hash: H256) -> Result<Out> {
let data = self.client().state_data(&hash).ok_or(Error::StateRootNotFound)?;
2017-02-14 19:30:37 +01:00
2017-02-15 18:07:30 +01:00
Ok(Out::OctetStream(data))
2017-02-14 19:30:37 +01:00
}
/// Get state trie node by hash and return as raw binary.
fn contract_code(&self, hash: H256) -> Result<Out> {
let data = self.client().state_data(&hash).ok_or(Error::ContractNotFound)?;
Ok(Out::OctetStream(data))
}
2017-02-14 19:30:37 +01:00
}
/// Get a query parameter's value by name.
2017-02-15 18:07:30 +01:00
fn get_param<'a>(query: &'a str, name: &str) -> Option<&'a str> {
2017-02-14 19:30:37 +01:00
query.split('&')
.find(|part| part.starts_with(name) && part[name.len()..].starts_with("="))
.map(|part| &part[name.len() + 1..])
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
2017-02-14 19:30:37 +01:00
use super::*;
2017-02-15 18:07:30 +01:00
use ethcore::client::TestBlockChainClient;
fn get_mocked_handler() -> IpfsHandler {
2017-08-28 15:22:59 +02:00
IpfsHandler::new(None.into(), None.into(), Arc::new(TestBlockChainClient::new()))
2017-02-15 18:07:30 +01:00
}
2017-02-14 19:30:37 +01:00
2017-02-15 18:07:30 +01:00
#[test]
2017-02-14 19:30:37 +01:00
fn test_get_param() {
let query = "foo=100&bar=200&qux=300";
assert_eq!(get_param(query, "foo"), Some("100"));
assert_eq!(get_param(query, "bar"), Some("200"));
assert_eq!(get_param(query, "qux"), Some("300"));
assert_eq!(get_param(query, "bar="), None);
assert_eq!(get_param(query, "200"), None);
2017-02-15 18:07:30 +01:00
assert_eq!(get_param("", "foo"), None);
assert_eq!(get_param("foo", "foo"), None);
assert_eq!(get_param("foo&bar", "foo"), None);
assert_eq!(get_param("bar&foo", "foo"), None);
}
#[test]
fn cid_route_block() {
let handler = get_mocked_handler();
// `eth-block` with Keccak-256
let cid = "z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM";
assert_eq!(Err(Error::BlockNotFound), handler.route_cid(cid));
}
#[test]
fn cid_route_block_list() {
let handler = get_mocked_handler();
// `eth-block-list` with Keccak-256
let cid = "z43c7o7FsNxqdLJW8Ucj19tuCALtnmUb2EkDptj4W6xSkFVTqWs";
assert_eq!(Err(Error::BlockNotFound), handler.route_cid(cid));
}
#[test]
fn cid_route_tx() {
let handler = get_mocked_handler();
// `eth-tx` with Keccak-256
let cid = "z44VCrqbpbPcb8SUBc8Tba4EaKuoDz2grdEoQXx4TP7WYh9ZGBu";
assert_eq!(Err(Error::TransactionNotFound), handler.route_cid(cid));
}
#[test]
fn cid_route_state_trie() {
let handler = get_mocked_handler();
// `eth-state-trie` with Keccak-256
let cid = "z45oqTS7kR2n2peRGJQ4VCJEeaG9sorqcCyfmznZPJM7FMdhQCT";
assert_eq!(Err(Error::StateRootNotFound), handler.route_cid(&cid));
}
#[test]
fn cid_route_contract_code() {
let handler = get_mocked_handler();
// `raw` with Keccak-256
let cid = "zb34WAp1Q5fhtLGZ3w3jhnTWaNbVV5ZZvGq4vuJQzERj6Pu3H";
assert_eq!(Err(Error::ContractNotFound), handler.route_cid(&cid));
}
2017-02-15 18:07:30 +01:00
#[test]
fn cid_route_invalid_hash() {
let handler = get_mocked_handler();
// `eth-block` with SHA3-256 hash
let cid = "z43Aa9gr1MM7TENJh4Em9d9Ttr7p3UcfyMpNei6WLVeCmSEPu8F";
assert_eq!(Err(Error::UnsupportedHash), handler.route_cid(cid));
}
#[test]
fn cid_route_invalid_codec() {
let handler = get_mocked_handler();
// `bitcoin-block` with Keccak-256
let cid = "z4HFyHvb8CarYARyxz4cCcPaciduXd49TFPCKLhYmvNxf7Auvwu";
assert_eq!(Err(Error::UnsupportedCid), handler.route_cid(&cid));
}
#[test]
fn route_block() {
let handler = get_mocked_handler();
2017-02-15 18:07:30 +01:00
let out = handler.route("/api/v0/block/get", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
2017-02-15 18:07:30 +01:00
assert_eq!(out, Out::NotFound("Block not found"));
2017-02-15 18:07:30 +01:00
}
#[test]
fn route_block_missing_query() {
let handler = get_mocked_handler();
2017-02-15 18:07:30 +01:00
let out = handler.route("/api/v0/block/get", None);
2017-02-15 18:07:30 +01:00
assert_eq!(out, Out::Bad("CID parsing failed"));
2017-02-15 18:07:30 +01:00
}
#[test]
fn route_block_invalid_query() {
let handler = get_mocked_handler();
2017-02-15 18:07:30 +01:00
let out = handler.route("/api/v0/block/get", Some("arg=foobarz43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
2017-02-15 18:07:30 +01:00
assert_eq!(out, Out::Bad("CID parsing failed"));
2017-02-15 18:07:30 +01:00
}
#[test]
fn route_invalid_route() {
let handler = get_mocked_handler();
2017-02-15 18:07:30 +01:00
let out = handler.route("/foo/bar/baz", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
2017-02-15 18:07:30 +01:00
assert_eq!(out, Out::NotFound("Route not found"));
2017-02-14 19:30:37 +01:00
}
}