mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-07-04 04:01:33 +00:00
Integrate the multiformats crates (#231)
* Remove the multihash patch * Integrate the multiformats crates * Fix not compiling on emscripten
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"cid",
|
||||
"circular-buffer",
|
||||
"datastore",
|
||||
"dns",
|
||||
@ -7,6 +8,8 @@ members = [
|
||||
"identify",
|
||||
"kad",
|
||||
"libp2p",
|
||||
"multiaddr",
|
||||
"multihash",
|
||||
"mplex",
|
||||
"multistream-select",
|
||||
"peerstore",
|
||||
@ -20,8 +23,3 @@ members = [
|
||||
"varint-rs",
|
||||
"websocket",
|
||||
]
|
||||
|
||||
[patch.crates-io]
|
||||
# Using a version of rust-multihash that compiles on emscripten
|
||||
# TODO: remove once merged upstream
|
||||
"multihash" = { git = "https://github.com/tomaka/rust-multihash", branch = "emscripten-hack" }
|
||||
|
3
cid/.gitignore
vendored
Normal file
3
cid/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
target
|
||||
Cargo.lock
|
||||
*.bk
|
35
cid/.travis.yml
Normal file
35
cid/.travis.yml
Normal file
@ -0,0 +1,35 @@
|
||||
sudo: false
|
||||
|
||||
language: rust
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
- binutils-dev
|
||||
|
||||
rust:
|
||||
- beta
|
||||
- stable
|
||||
|
||||
before_script:
|
||||
- |
|
||||
pip install 'travis-cargo<0.2' --user &&
|
||||
export PATH=$HOME/.local/bin:$PATH
|
||||
|
||||
install:
|
||||
- pip install --user travis-cargo codecov
|
||||
- export PATH=$PATH:$HOME/.local/bin
|
||||
|
||||
script:
|
||||
- |
|
||||
travis-cargo build &&
|
||||
travis-cargo test &&
|
||||
travis-cargo --only stable doc
|
||||
|
||||
after_success:
|
||||
- travis-cargo coverage --no-sudo
|
||||
- travis-cargo --only stable doc-upload
|
||||
- travis-cargo coveralls --no-sudo --verify
|
22
cid/Cargo.toml
Normal file
22
cid/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "cid"
|
||||
version = "0.2.3"
|
||||
description = "CID in rust"
|
||||
homepage = "https://github.com/ipld/rust-cid"
|
||||
authors = ["Friedel Ziegelmayer <dignifiedquire@gmail.com>"]
|
||||
|
||||
keywords = ["ipld", "ipfs", "cid", "multihash"]
|
||||
|
||||
license = "MIT"
|
||||
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
multihash = { path = "../multihash" }
|
||||
multibase = "~0.6.0"
|
||||
integer-encoding = "~1.0.3"
|
||||
|
||||
[package.metadata.release]
|
||||
upload-doc = true
|
||||
pre-release-commit-message = "Release {{version}} 🎉🎉"
|
||||
no-dev-version = true
|
64
cid/README.md
Normal file
64
cid/README.md
Normal file
@ -0,0 +1,64 @@
|
||||
# rust-cid
|
||||
|
||||
[](http://ipn.io)
|
||||
[](https://github.com/ipld/ipld)
|
||||
[](https://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://travis-ci.org/ipld/rust-cid)
|
||||
[](https://docs.rs/crate/cid)
|
||||
[](https://crates.io/crates/cid)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
|
||||
> [CID](https://github.com/ipld/cid) implementation in Rust.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
First add this to your `Cargo.toml`
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
cid = "*"
|
||||
```
|
||||
|
||||
Then run `cargo build`.
|
||||
|
||||
## Usage
|
||||
|
||||
```rust
|
||||
extern crate cid;
|
||||
extern crate multihash;
|
||||
|
||||
use multihash::Hash;
|
||||
use cid::{Cid, Codec, Version};
|
||||
let h = multihash::encode(multihash::Hash::SHA2256, b"beep boop").unwrap();
|
||||
|
||||
let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h);
|
||||
|
||||
let data = cid.to_bytes();
|
||||
let out = Cid::from(data).unwrap();
|
||||
|
||||
assert_eq!(cid, out);
|
||||
```
|
||||
## Maintainers
|
||||
|
||||
Captain: [@dignifiedquire](https://github.com/dignifiedquire).
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/ipld/rust-cid/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/ipld/ipld/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to ipld are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © 2017 Friedel Ziegelmayer
|
52
cid/src/codec.rs
Normal file
52
cid/src/codec.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use {Error, Result};
|
||||
|
||||
macro_rules! build_codec_enum {
|
||||
{$( $val:expr => $var:ident, )*} => {
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Codec {
|
||||
$( $var, )*
|
||||
}
|
||||
|
||||
use Codec::*;
|
||||
|
||||
impl Codec {
|
||||
/// Convert a number to the matching codec
|
||||
pub fn from(raw: u64) -> Result<Codec> {
|
||||
match raw {
|
||||
$( $val => Ok($var), )*
|
||||
_ => Err(Error::UnknownCodec),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Codec> for u64 {
|
||||
/// Convert to the matching integer code
|
||||
fn from(codec: Codec) -> u64 {
|
||||
match codec {
|
||||
$( $var => $val, )*
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build_codec_enum! {
|
||||
0x55 => Raw,
|
||||
0x70 => DagProtobuf,
|
||||
0x71 => DagCBOR,
|
||||
0x78 => GitRaw,
|
||||
0x90 => EthereumBlock,
|
||||
0x91 => EthereumBlockList,
|
||||
0x92 => EthereumTxTrie,
|
||||
0x93 => EthereumTx,
|
||||
0x94 => EthereumTxReceiptTrie,
|
||||
0x95 => EthereumTxReceipt,
|
||||
0x96 => EthereumStateTrie,
|
||||
0x97 => EthereumAccountSnapshot,
|
||||
0x98 => EthereumStorageTrie,
|
||||
0xb0 => BitcoinBlock,
|
||||
0xb1 => BitcoinTx,
|
||||
0xc0 => ZcashBlock,
|
||||
0xc1 => ZcashTx,
|
||||
}
|
57
cid/src/error.rs
Normal file
57
cid/src/error.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use std::{fmt, error, io};
|
||||
use multibase;
|
||||
use multihash;
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
/// Error types
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Error {
|
||||
UnknownCodec,
|
||||
InputTooShort,
|
||||
ParsingError,
|
||||
InvalidCidVersion,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
use self::Error::*;
|
||||
|
||||
match *self {
|
||||
UnknownCodec => "Unknown codec",
|
||||
InputTooShort => "Input too short",
|
||||
ParsingError => "Failed to parse multihash",
|
||||
InvalidCidVersion => "Unrecognized CID version",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(_: io::Error) -> Error {
|
||||
Error::ParsingError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<multibase::Error> for Error {
|
||||
fn from(_: multibase::Error) -> Error {
|
||||
Error::ParsingError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<multihash::Error> for Error {
|
||||
fn from(_: multihash::Error) -> Error {
|
||||
Error::ParsingError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for fmt::Error {
|
||||
fn from(_: Error) -> fmt::Error {
|
||||
fmt::Error {}
|
||||
}
|
||||
}
|
164
cid/src/lib.rs
Normal file
164
cid/src/lib.rs
Normal file
@ -0,0 +1,164 @@
|
||||
/// ! # cid
|
||||
/// !
|
||||
/// ! Implementation of [cid](https://github.com/ipld/cid) in Rust.
|
||||
|
||||
extern crate multihash;
|
||||
extern crate multibase;
|
||||
extern crate integer_encoding;
|
||||
|
||||
mod to_cid;
|
||||
mod error;
|
||||
mod codec;
|
||||
mod version;
|
||||
|
||||
pub use to_cid::ToCid;
|
||||
pub use version::Version;
|
||||
pub use codec::Codec;
|
||||
pub use error::{Error, Result};
|
||||
|
||||
use integer_encoding::{VarIntReader, VarIntWriter};
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
/// Representation of a CID.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Cid {
|
||||
pub version: Version,
|
||||
pub codec: Codec,
|
||||
pub hash: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Prefix represents all metadata of a CID, without the actual content.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Prefix {
|
||||
pub version: Version,
|
||||
pub codec: Codec,
|
||||
pub mh_type: multihash::Hash,
|
||||
pub mh_len: usize,
|
||||
}
|
||||
|
||||
impl Cid {
|
||||
/// Create a new CID.
|
||||
pub fn new(codec: Codec, version: Version, hash: &[u8]) -> Cid {
|
||||
Cid {
|
||||
version: version,
|
||||
codec: codec,
|
||||
hash: hash.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new CID from raw data (binary or multibase encoded string)
|
||||
pub fn from<T: ToCid>(data: T) -> Result<Cid> {
|
||||
data.to_cid()
|
||||
}
|
||||
|
||||
/// Create a new CID from a prefix and some data.
|
||||
pub fn new_from_prefix(prefix: &Prefix, data: &[u8]) -> Cid {
|
||||
let mut hash = multihash::encode(prefix.mh_type.to_owned(), data).unwrap();
|
||||
hash.truncate(prefix.mh_len + 2);
|
||||
Cid {
|
||||
version: prefix.version,
|
||||
codec: prefix.codec.to_owned(),
|
||||
hash: hash,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_string_v0(&self) -> String {
|
||||
use multibase::{encode, Base};
|
||||
|
||||
let mut string = encode(Base::Base58btc, self.hash.as_slice());
|
||||
|
||||
// Drop the first character as v0 does not know
|
||||
// about multibase
|
||||
string.remove(0);
|
||||
|
||||
string
|
||||
}
|
||||
|
||||
fn to_string_v1(&self) -> String {
|
||||
use multibase::{encode, Base};
|
||||
|
||||
encode(Base::Base58btc, self.to_bytes().as_slice())
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
match self.version {
|
||||
Version::V0 => self.to_string_v0(),
|
||||
Version::V1 => self.to_string_v1(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_bytes_v0(&self) -> Vec<u8> {
|
||||
self.hash.clone()
|
||||
}
|
||||
|
||||
fn to_bytes_v1(&self) -> Vec<u8> {
|
||||
let mut res = Vec::with_capacity(16);
|
||||
res.write_varint(u64::from(self.version)).unwrap();
|
||||
res.write_varint(u64::from(self.codec)).unwrap();
|
||||
res.extend_from_slice(&self.hash);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
match self.version {
|
||||
Version::V0 => self.to_bytes_v0(),
|
||||
Version::V1 => self.to_bytes_v1(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prefix(&self) -> Prefix {
|
||||
// Unwrap is safe, as this should have been validated on creation
|
||||
let mh = multihash::decode(self.hash.as_slice()).unwrap();
|
||||
|
||||
Prefix {
|
||||
version: self.version,
|
||||
codec: self.codec.to_owned(),
|
||||
mh_type: mh.alg,
|
||||
mh_len: mh.digest.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Cid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Cid::to_string(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Prefix {
|
||||
pub fn new_from_bytes(data: &[u8]) -> Result<Prefix> {
|
||||
let mut cur = Cursor::new(data);
|
||||
|
||||
let raw_version = cur.read_varint()?;
|
||||
let raw_codec = cur.read_varint()?;
|
||||
let raw_mh_type: u64 = cur.read_varint()?;
|
||||
|
||||
let version = Version::from(raw_version)?;
|
||||
let codec = Codec::from(raw_codec)?;
|
||||
|
||||
let mh_type = multihash::Hash::from_code(raw_mh_type as u8)?;
|
||||
|
||||
let mh_len = cur.read_varint()?;
|
||||
|
||||
Ok(Prefix {
|
||||
version: version,
|
||||
codec: codec,
|
||||
mh_type: mh_type,
|
||||
mh_len: mh_len,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
let mut res = Vec::with_capacity(4);
|
||||
|
||||
// io can't fail on Vec
|
||||
res.write_varint(u64::from(self.version)).unwrap();
|
||||
res.write_varint(u64::from(self.codec)).unwrap();
|
||||
res.write_varint(self.mh_type.code() as u64).unwrap();
|
||||
res.write_varint(self.mh_len as u64).unwrap();
|
||||
|
||||
res
|
||||
}
|
||||
}
|
93
cid/src/to_cid.rs
Normal file
93
cid/src/to_cid.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use std::io::Cursor;
|
||||
use multibase;
|
||||
use multihash;
|
||||
use integer_encoding::VarIntReader;
|
||||
|
||||
use {Cid, Version, Codec, Error, Result};
|
||||
|
||||
pub trait ToCid {
|
||||
fn to_cid(&self) -> Result<Cid>;
|
||||
}
|
||||
|
||||
impl ToCid for Vec<u8> {
|
||||
/// Create a Cid from a byte vector.
|
||||
#[inline]
|
||||
fn to_cid(&self) -> Result<Cid> {
|
||||
self.as_slice().to_cid()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCid for String {
|
||||
/// Create a Cid from an owned String.
|
||||
#[inline]
|
||||
fn to_cid(&self) -> Result<Cid> {
|
||||
self.as_str().to_cid()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToCid for &'a str {
|
||||
#[inline]
|
||||
fn to_cid(&self) -> Result<Cid> {
|
||||
ToCid::to_cid(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCid for str {
|
||||
fn to_cid(&self) -> Result<Cid> {
|
||||
static IPFS_DELIMETER: &'static str = "/ipfs/";
|
||||
|
||||
let hash = match self.find(IPFS_DELIMETER) {
|
||||
Some(index) => &self[index + IPFS_DELIMETER.len()..],
|
||||
_ => self
|
||||
};
|
||||
|
||||
if hash.len() < 2 {
|
||||
return Err(Error::InputTooShort);
|
||||
}
|
||||
|
||||
let (_, decoded) = if Version::is_v0_str(hash) {
|
||||
// TODO: could avoid the roundtrip here and just use underlying
|
||||
// base-x base58btc decoder here.
|
||||
let hash = multibase::Base::Base58btc.code().to_string() + &hash;
|
||||
|
||||
multibase::decode(hash)
|
||||
} else {
|
||||
multibase::decode(hash)
|
||||
}?;
|
||||
|
||||
decoded.to_cid()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToCid for &'a [u8] {
|
||||
#[inline]
|
||||
fn to_cid(&self) -> Result<Cid> {
|
||||
ToCid::to_cid(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCid for [u8] {
|
||||
/// Create a Cid from a byte slice.
|
||||
fn to_cid(&self) -> Result<Cid> {
|
||||
if Version::is_v0_binary(self) {
|
||||
// Verify that hash can be decoded, this is very cheap
|
||||
multihash::decode(self)?;
|
||||
|
||||
Ok(Cid::new(Codec::DagProtobuf, Version::V0, self))
|
||||
} else {
|
||||
let mut cur = Cursor::new(self);
|
||||
let raw_version = cur.read_varint()?;
|
||||
let raw_codec = cur.read_varint()?;
|
||||
|
||||
let version = Version::from(raw_version)?;
|
||||
let codec = Codec::from(raw_codec)?;
|
||||
|
||||
let hash = &self[cur.position() as usize..];
|
||||
|
||||
// Verify that hash can be decoded, this is very cheap
|
||||
multihash::decode(hash)?;
|
||||
|
||||
Ok(Cid::new(codec, version, hash))
|
||||
}
|
||||
}
|
||||
}
|
38
cid/src/version.rs
Normal file
38
cid/src/version.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use {Error, Result};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub enum Version {
|
||||
V0,
|
||||
V1,
|
||||
}
|
||||
|
||||
use Version::*;
|
||||
|
||||
impl Version {
|
||||
pub fn from(raw: u64) -> Result<Version> {
|
||||
match raw {
|
||||
0 => Ok(V0),
|
||||
1 => Ok(V1),
|
||||
_ => Err(Error::InvalidCidVersion)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_v0_str(data: &str) -> bool {
|
||||
// v0 is a base58btc encoded sha hash, so it has
|
||||
// fixed length and always begins with "Qm"
|
||||
data.len() == 46 && data.starts_with("Qm")
|
||||
}
|
||||
|
||||
pub fn is_v0_binary(data: &[u8]) -> bool {
|
||||
data.len() == 34 && data.starts_with(&[0x12,0x20])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Version> for u64 {
|
||||
fn from(ver: Version) -> u64 {
|
||||
match ver {
|
||||
V0 => 0,
|
||||
V1 => 1,
|
||||
}
|
||||
}
|
||||
}
|
76
cid/tests/lib.rs
Normal file
76
cid/tests/lib.rs
Normal file
@ -0,0 +1,76 @@
|
||||
extern crate cid;
|
||||
extern crate multihash;
|
||||
|
||||
use cid::{Cid, Version, Codec, Error, Prefix};
|
||||
|
||||
#[test]
|
||||
fn basic_marshalling() {
|
||||
let h = multihash::encode(multihash::Hash::SHA2256, b"beep boop").unwrap();
|
||||
|
||||
let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h);
|
||||
|
||||
let data = cid.to_bytes();
|
||||
let out = Cid::from(data).unwrap();
|
||||
|
||||
assert_eq!(cid, out);
|
||||
|
||||
let s = cid.to_string();
|
||||
let out2 = Cid::from(&s[..]).unwrap();
|
||||
|
||||
assert_eq!(cid, out2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_string() {
|
||||
assert_eq!(Cid::from(""), Err(Error::InputTooShort));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v0_handling() {
|
||||
let old = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n";
|
||||
let cid = Cid::from(old).unwrap();
|
||||
|
||||
assert_eq!(cid.version, Version::V0);
|
||||
assert_eq!(cid.to_string(), old);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v0_error() {
|
||||
let bad = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zIII";
|
||||
assert_eq!(Cid::from(bad), Err(Error::ParsingError));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefix_roundtrip() {
|
||||
let data = b"awesome test content";
|
||||
let h = multihash::encode(multihash::Hash::SHA2256, data).unwrap();
|
||||
|
||||
let cid = Cid::new(Codec::DagProtobuf, Version::V1, &h);
|
||||
let prefix = cid.prefix();
|
||||
|
||||
let cid2 = Cid::new_from_prefix(&prefix, data);
|
||||
|
||||
assert_eq!(cid, cid2);
|
||||
|
||||
let prefix_bytes = prefix.as_bytes();
|
||||
let prefix2 = Prefix::new_from_bytes(&prefix_bytes).unwrap();
|
||||
|
||||
assert_eq!(prefix, prefix2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from() {
|
||||
let the_hash = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n";
|
||||
|
||||
let cases = vec![
|
||||
format!("/ipfs/{:}", &the_hash),
|
||||
format!("https://ipfs.io/ipfs/{:}", &the_hash),
|
||||
format!("http://localhost:8080/ipfs/{:}", &the_hash),
|
||||
];
|
||||
|
||||
for case in cases {
|
||||
let cid = Cid::from(case).unwrap();
|
||||
assert_eq!(cid.version, Version::V0);
|
||||
assert_eq!(cid.to_string(), the_hash);
|
||||
}
|
||||
}
|
@ -8,8 +8,8 @@ bs58 = "0.2.0"
|
||||
bytes = "0.4"
|
||||
fnv = "1.0"
|
||||
log = "0.4"
|
||||
multiaddr = "0.3.0"
|
||||
multihash = "0.7.0"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
multihash = { path = "../multihash" }
|
||||
multistream-select = { path = "../multistream-select" }
|
||||
futures = { version = "0.1", features = ["use_std"] }
|
||||
parking_lot = "0.5.3"
|
||||
|
@ -7,7 +7,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
libp2p-core = { path = "../core" }
|
||||
log = "0.4.1"
|
||||
futures = "0.1"
|
||||
multiaddr = "0.3.0"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
tokio-dns-unofficial = "0.1"
|
||||
tokio-io = "0.1"
|
||||
|
||||
|
@ -11,7 +11,7 @@ fnv = "1.0"
|
||||
futures = "0.1"
|
||||
libp2p-core = { path = "../core" }
|
||||
log = "0.4.1"
|
||||
multiaddr = "0.3"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
parking_lot = "0.5.3"
|
||||
protobuf = "2"
|
||||
smallvec = "0.6.0"
|
||||
|
@ -9,7 +9,7 @@ futures = "0.1"
|
||||
libp2p-peerstore = { path = "../peerstore" }
|
||||
libp2p-core = { path = "../core" }
|
||||
log = "0.4.1"
|
||||
multiaddr = "0.3.0"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
protobuf = "2"
|
||||
tokio-io = "0.1.0"
|
||||
varint = { path = "../varint-rs" }
|
||||
|
@ -16,7 +16,7 @@ libp2p-peerstore = { path = "../peerstore" }
|
||||
libp2p-ping = { path = "../ping" }
|
||||
libp2p-core = { path = "../core" }
|
||||
log = "0.4"
|
||||
multiaddr = "0.3"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
parking_lot = "0.5.1"
|
||||
protobuf = "2"
|
||||
rand = "0.4.2"
|
||||
|
@ -9,7 +9,7 @@ default = ["libp2p-secio"]
|
||||
[dependencies]
|
||||
bytes = "0.4"
|
||||
futures = "0.1"
|
||||
multiaddr = "0.3"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
libp2p-mplex = { path = "../mplex" }
|
||||
libp2p-identify = { path = "../identify" }
|
||||
libp2p-kad = { path = "../kad" }
|
||||
|
3
multiaddr/.gitignore
vendored
Normal file
3
multiaddr/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
target
|
||||
Cargo.lock
|
||||
*.rs.bk
|
31
multiaddr/.travis.yml
Normal file
31
multiaddr/.travis.yml
Normal file
@ -0,0 +1,31 @@
|
||||
sudo: false
|
||||
language: rust
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
- binutils-dev
|
||||
rust:
|
||||
# - nightly # enable when passing
|
||||
- beta
|
||||
- stable
|
||||
|
||||
before_script:
|
||||
- |
|
||||
pip install 'travis-cargo<0.2' --user &&
|
||||
export PATH=$HOME/.local/bin:$PATH
|
||||
script:
|
||||
- |
|
||||
travis-cargo build &&
|
||||
travis-cargo test &&
|
||||
travis-cargo bench &&
|
||||
travis-cargo --only stable doc
|
||||
after_success:
|
||||
- travis-cargo --only stable doc-upload
|
||||
- travis-cargo coveralls --no-sudo --verify
|
||||
env:
|
||||
global:
|
||||
- TRAVIS_CARGO_NIGHTLY_FEATURE=nightly
|
||||
- secure: f/VDWCDpQazAB1TX4QlwGDumT7ft4uip5isALquR3TUT/WyKScnXe9JCAMp5/kJaVzSUeQgbqbBXsNmkzMpqBGj5Cu8GqUDIPf2TbqfFJ/wNzzQXrvwB2n3fA5affbSLBvLBw1R9Jonv14tPduYgnfkGDi89UbxMhXDRhpmwrFlKxHLcbz36+pYG5Qg3ftAGDrNDWprh5W0J1RgwyXGj56fUKLcHDzZoyvNEEzkSM3WV4OdWixCDESrS3SZLaYCjLNHLbCsbaa6AWUlGZMJXj5mrNDDxeCFU6Z9euUWG9ypJkiRA6eMo1zqXZrHYvPJM2ivqWqEYUXtKGHpugH2Sa34C/PvXOiuYkC2yXLO6TmSaAYWo5x6z1I2WrgMKSbhpqsrV0ZRKywCBuMooyQw85tQJRFPqSxbaJYPjsVhZ4yZEDnbsCvr8puhKtWAYOhz/0d15Pyom+yJ3roT4eSt4VyPVifEFAKC4/tLxbXkzB44PvOg0Ur+HxUUoAxi0dgrb1MTOwgkDxUl3iIdQtn6bAjM2D84ciCtYvlcTQAp7Rohgoda3FU99Hoxujj7YJ3eRLfBpLOclXTqUFBU0JFRDlec1xcD4MnwsLz9oWdw/hIhyh4PGfm6wuvfBKkJdQ4JhzIS7UcDCUdrsTx7LqMtA5M1CkN53mw+HNXfUncOOyLw=
|
17
multiaddr/Cargo.toml
Normal file
17
multiaddr/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
description = "Implementation of the multiaddr format"
|
||||
homepage = "https://github.com/multiformats/rust-multiaddr"
|
||||
keywords = ["multiaddr", "ipfs"]
|
||||
license = "MIT"
|
||||
name = "multiaddr"
|
||||
readme = "README.md"
|
||||
version = "0.3.0"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "~0.4"
|
||||
cid = { path = "../cid" }
|
||||
integer-encoding = "~1.0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
data-encoding = "~1.1.2"
|
21
multiaddr/LICENSE
Normal file
21
multiaddr/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (C) 2015-2016 Friedel Ziegelmayer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Status API Training Shop Blog About Pricing
|
63
multiaddr/README.md
Normal file
63
multiaddr/README.md
Normal file
@ -0,0 +1,63 @@
|
||||
# rust-multiaddr
|
||||
|
||||
[](http://ipn.io)
|
||||
[](https://github.com/multiformats/multiformats)
|
||||
[](https://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://travis-ci.org/multiformats/rust-multiaddr)
|
||||
[](https://codecov.io/github/multiformats/rust-multiaddr?branch=master)
|
||||
[](https://docs.rs/crate/multiaddr)
|
||||
[](https://crates.io/crates/multiaddr)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
|
||||
|
||||
> [multiaddr](https://github.com/multiformats/multiaddr) implementation in Rust.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
First add this to your `Cargo.toml`
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
multiaddr = "*"
|
||||
```
|
||||
|
||||
then run `cargo build`.
|
||||
|
||||
## Usage
|
||||
|
||||
```rust
|
||||
extern crate multiaddr;
|
||||
|
||||
use multiaddr::{Multiaddr, ToMultiaddr};
|
||||
|
||||
let address = "/ip4/127.0.0.1/udp/1234".parse::<Multiaddr>().unwrap();
|
||||
// or directly from a string
|
||||
let other = "/ip4/127.0.0.1".to_multiaddr().unwrap();
|
||||
|
||||
assert_eq!(address.to_string(), "/ip4/127.0.0.1/udp/1234");
|
||||
assert_eq!(other.to_string(), "/ip4/127.0.0.1");
|
||||
```
|
||||
|
||||
## Maintainers
|
||||
|
||||
Captain: [@dignifiedquire](https://github.com/dignifiedquire).
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/multiformats/rust-multiaddr/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © 2015-2017 Friedel Ziegelmeyer
|
77
multiaddr/src/errors.rs
Normal file
77
multiaddr/src/errors.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use std::{net, fmt, error, io, num, string};
|
||||
use cid;
|
||||
use byteorder;
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
/// Error types
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
UnknownProtocol,
|
||||
UnknownProtocolString,
|
||||
InvalidMultiaddr,
|
||||
MissingAddress,
|
||||
ParsingError(Box<error::Error + Send + Sync>),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::UnknownProtocol => "unknown protocol",
|
||||
Error::UnknownProtocolString => "unknown protocol string",
|
||||
Error::InvalidMultiaddr => "invalid multiaddr",
|
||||
Error::MissingAddress => "protocol requires address, none given",
|
||||
Error::ParsingError(_) => "failed to parse",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match *self {
|
||||
Error::ParsingError(ref err) => Some(&**err),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Error {
|
||||
Error::ParsingError(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<cid::Error> for Error {
|
||||
fn from(err: cid::Error) -> Error {
|
||||
Error::ParsingError(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<net::AddrParseError> for Error {
|
||||
fn from(err: net::AddrParseError) -> Error {
|
||||
Error::ParsingError(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<byteorder::Error> for Error {
|
||||
fn from(err: byteorder::Error) -> Error {
|
||||
Error::ParsingError(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<num::ParseIntError> for Error {
|
||||
fn from(err: num::ParseIntError) -> Error {
|
||||
Error::ParsingError(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<string::FromUtf8Error> for Error {
|
||||
fn from(err: string::FromUtf8Error) -> Error {
|
||||
Error::ParsingError(err.into())
|
||||
}
|
||||
}
|
411
multiaddr/src/lib.rs
Normal file
411
multiaddr/src/lib.rs
Normal file
@ -0,0 +1,411 @@
|
||||
///! # multiaddr
|
||||
///!
|
||||
///! Implementation of [multiaddr](https://github.com/jbenet/multiaddr)
|
||||
///! in Rust.
|
||||
extern crate byteorder;
|
||||
extern crate cid;
|
||||
extern crate integer_encoding;
|
||||
|
||||
mod protocol;
|
||||
mod errors;
|
||||
|
||||
pub use errors::{Result, Error};
|
||||
pub use protocol::{Protocol, ProtocolArgSize, AddrComponent};
|
||||
|
||||
use std::fmt;
|
||||
use std::iter::FromIterator;
|
||||
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Representation of a Multiaddr.
|
||||
#[derive(PartialEq, Eq, Clone, Hash)]
|
||||
pub struct Multiaddr {
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Multiaddr {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.to_string().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Multiaddr {
|
||||
/// Convert a Multiaddr to a string
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use multiaddr::Multiaddr;
|
||||
///
|
||||
/// let address: Multiaddr = "/ip4/127.0.0.1/udt".parse().unwrap();
|
||||
/// assert_eq!(address.to_string(), "/ip4/127.0.0.1/udt");
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
for s in self.iter() {
|
||||
s.to_string().fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Multiaddr {
|
||||
/// Returns the raw bytes representation of the multiaddr.
|
||||
#[inline]
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.bytes
|
||||
}
|
||||
|
||||
/// Return a copy to disallow changing the bytes directly
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.bytes.to_owned()
|
||||
}
|
||||
|
||||
/// Produces a `Multiaddr` from its bytes representation.
|
||||
pub fn from_bytes(bytes: Vec<u8>) -> Result<Multiaddr> {
|
||||
{
|
||||
let mut ptr = &bytes[..];
|
||||
while !ptr.is_empty() {
|
||||
let (_, new_ptr) = AddrComponent::from_bytes(ptr)?;
|
||||
ptr = new_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Multiaddr { bytes })
|
||||
}
|
||||
|
||||
/// Extracts a slice containing the entire underlying vector.
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
/// Return a list of protocols
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// A single protocol
|
||||
///
|
||||
/// ```
|
||||
/// use multiaddr::{Multiaddr, Protocol};
|
||||
///
|
||||
/// let address: Multiaddr = "/ip4/127.0.0.1".parse().unwrap();
|
||||
/// assert_eq!(address.protocol(), vec![Protocol::IP4]);
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
#[deprecated(note = "Use `self.iter().map(|addr| addr.protocol_id())` instead")]
|
||||
pub fn protocol(&self) -> Vec<Protocol> {
|
||||
self.iter().map(|addr| addr.protocol_id()).collect()
|
||||
}
|
||||
|
||||
/// Wrap a given Multiaddr and return the combination.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use multiaddr::Multiaddr;
|
||||
///
|
||||
/// let address: Multiaddr = "/ip4/127.0.0.1".parse().unwrap();
|
||||
/// let nested = address.encapsulate("/udt").unwrap();
|
||||
/// assert_eq!(nested, "/ip4/127.0.0.1/udt".parse().unwrap());
|
||||
/// ```
|
||||
///
|
||||
pub fn encapsulate<T: ToMultiaddr>(&self, input: T) -> Result<Multiaddr> {
|
||||
let new = input.to_multiaddr()?;
|
||||
let mut bytes = self.bytes.clone();
|
||||
|
||||
bytes.extend(new.to_bytes());
|
||||
|
||||
Ok(Multiaddr { bytes: bytes })
|
||||
}
|
||||
|
||||
/// Adds an already-parsed address component to the end of this multiaddr.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use multiaddr::{Multiaddr, AddrComponent};
|
||||
///
|
||||
/// let mut address: Multiaddr = "/ip4/127.0.0.1".parse().unwrap();
|
||||
/// address.append(AddrComponent::TCP(10000));
|
||||
/// assert_eq!(address, "/ip4/127.0.0.1/tcp/10000".parse().unwrap());
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
pub fn append(&mut self, component: AddrComponent) {
|
||||
component.write_bytes(&mut self.bytes).expect(
|
||||
"writing to a Vec never fails",
|
||||
)
|
||||
}
|
||||
|
||||
/// Remove the outermost address.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use multiaddr::{Multiaddr, ToMultiaddr};
|
||||
///
|
||||
/// let address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap();
|
||||
/// let unwrapped = address.decapsulate("/udt").unwrap();
|
||||
/// assert_eq!(unwrapped, "/ip4/127.0.0.1".parse().unwrap());
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// address.decapsulate("/udt").unwrap(),
|
||||
/// "/ip4/127.0.0.1".to_multiaddr().unwrap()
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// Returns the original if the passed in address is not found
|
||||
///
|
||||
/// ```
|
||||
/// use multiaddr::ToMultiaddr;
|
||||
///
|
||||
/// let address = "/ip4/127.0.0.1/udt/sctp/5678".to_multiaddr().unwrap();
|
||||
/// let unwrapped = address.decapsulate("/ip4/127.0.1.1").unwrap();
|
||||
/// assert_eq!(unwrapped, address);
|
||||
/// ```
|
||||
///
|
||||
pub fn decapsulate<T: ToMultiaddr>(&self, input: T) -> Result<Multiaddr> {
|
||||
let input = input.to_multiaddr()?.to_bytes();
|
||||
|
||||
let bytes_len = self.bytes.len();
|
||||
let input_length = input.len();
|
||||
|
||||
let mut input_pos = 0;
|
||||
let mut matches = false;
|
||||
|
||||
for (i, _) in self.bytes.iter().enumerate() {
|
||||
let next = i + input_length;
|
||||
|
||||
if next > bytes_len {
|
||||
continue;
|
||||
}
|
||||
|
||||
if &self.bytes[i..next] == input.as_slice() {
|
||||
matches = true;
|
||||
input_pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !matches {
|
||||
return Ok(Multiaddr { bytes: self.bytes.clone() });
|
||||
}
|
||||
|
||||
let mut bytes = self.bytes.clone();
|
||||
bytes.truncate(input_pos);
|
||||
|
||||
Ok(Multiaddr { bytes: bytes })
|
||||
}
|
||||
|
||||
/// Returns the components of this multiaddress.
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::Ipv4Addr;
|
||||
/// use multiaddr::AddrComponent;
|
||||
/// use multiaddr::Multiaddr;
|
||||
///
|
||||
/// let address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap();
|
||||
///
|
||||
/// let components = address.iter().collect::<Vec<_>>();
|
||||
/// assert_eq!(components[0], AddrComponent::IP4(Ipv4Addr::new(127, 0, 0, 1)));
|
||||
/// assert_eq!(components[1], AddrComponent::UDT);
|
||||
/// assert_eq!(components[2], AddrComponent::SCTP(5678));
|
||||
/// ```
|
||||
///
|
||||
#[inline]
|
||||
pub fn iter(&self) -> Iter {
|
||||
Iter(&self.bytes)
|
||||
}
|
||||
|
||||
/// Pops the last `AddrComponent` of this multiaddr, or `None` if the multiaddr is empty.
|
||||
/// ```
|
||||
/// use multiaddr::AddrComponent;
|
||||
/// use multiaddr::Multiaddr;
|
||||
///
|
||||
/// let mut address: Multiaddr = "/ip4/127.0.0.1/udt/sctp/5678".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(address.pop().unwrap(), AddrComponent::SCTP(5678));
|
||||
/// assert_eq!(address.pop().unwrap(), AddrComponent::UDT);
|
||||
/// ```
|
||||
///
|
||||
pub fn pop(&mut self) -> Option<AddrComponent> {
|
||||
// Note: could be more optimized
|
||||
let mut list = self.iter().collect::<Vec<_>>();
|
||||
let last_elem = list.pop();
|
||||
*self = list.into_iter().collect();
|
||||
last_elem
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AddrComponent> for Multiaddr {
|
||||
fn from(addr: AddrComponent) -> Multiaddr {
|
||||
let mut out = Vec::new();
|
||||
addr.write_bytes(&mut out).expect(
|
||||
"writing to a Vec never fails",
|
||||
);
|
||||
Multiaddr { bytes: out }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Multiaddr {
|
||||
type Item = AddrComponent;
|
||||
type IntoIter = Iter<'a>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Iter<'a> {
|
||||
Iter(&self.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<AddrComponent> for Multiaddr {
|
||||
fn from_iter<T>(iter: T) -> Self
|
||||
where
|
||||
T: IntoIterator<Item = AddrComponent>,
|
||||
{
|
||||
let mut bytes = Vec::new();
|
||||
for cmp in iter {
|
||||
cmp.write_bytes(&mut bytes).expect(
|
||||
"writing to a Vec never fails",
|
||||
);
|
||||
}
|
||||
Multiaddr { bytes: bytes }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Multiaddr {
|
||||
type Err = Error;
|
||||
|
||||
#[inline]
|
||||
fn from_str(input: &str) -> Result<Self> {
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
let mut parts = input.split('/');
|
||||
// A multiaddr must start with `/`
|
||||
if !parts.next().ok_or(Error::InvalidMultiaddr)?.is_empty() {
|
||||
return Err(Error::InvalidMultiaddr);
|
||||
}
|
||||
|
||||
while let Some(part) = parts.next() {
|
||||
let protocol: Protocol = part.parse()?;
|
||||
let addr_component = match protocol.size() {
|
||||
ProtocolArgSize::Fixed { bytes: 0 } => {
|
||||
protocol.parse_data("")? // TODO: bad design
|
||||
}
|
||||
_ => {
|
||||
let data = parts.next().ok_or(Error::MissingAddress)?;
|
||||
protocol.parse_data(data)?
|
||||
}
|
||||
};
|
||||
|
||||
addr_component.write_bytes(&mut bytes).expect(
|
||||
"writing to a Vec never fails",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Multiaddr { bytes: bytes })
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator for the address components in a multiaddr.
|
||||
pub struct Iter<'a>(&'a [u8]);
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = AddrComponent;
|
||||
|
||||
fn next(&mut self) -> Option<AddrComponent> {
|
||||
if self.0.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (component, next_data) =
|
||||
AddrComponent::from_bytes(self.0).expect("multiaddr is known to be valid");
|
||||
self.0 = next_data;
|
||||
Some(component)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for objects which can be converted to a
|
||||
/// Multiaddr.
|
||||
///
|
||||
/// This trait is implemented by default for
|
||||
///
|
||||
/// * `SocketAddr`, `SocketAddrV4` and `SocketAddrV6`, assuming that the
|
||||
/// the given port is a tcp port.
|
||||
///
|
||||
/// * `Ipv4Addr`, `Ipv6Addr`
|
||||
///
|
||||
/// * `String` and `&str`, requiring the default string format for a Multiaddr.
|
||||
///
|
||||
pub trait ToMultiaddr {
|
||||
/// Converts this object to a Multiaddr
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Any errors encountered during parsing will be returned
|
||||
/// as an `Err`.
|
||||
fn to_multiaddr(&self) -> Result<Multiaddr>;
|
||||
}
|
||||
|
||||
impl ToMultiaddr for SocketAddr {
|
||||
fn to_multiaddr(&self) -> Result<Multiaddr> {
|
||||
match *self {
|
||||
SocketAddr::V4(ref a) => (*a).to_multiaddr(),
|
||||
SocketAddr::V6(ref a) => (*a).to_multiaddr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToMultiaddr for SocketAddrV4 {
|
||||
fn to_multiaddr(&self) -> Result<Multiaddr> {
|
||||
format!("/ip4/{}/tcp/{}", self.ip(), self.port()).parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToMultiaddr for SocketAddrV6 {
|
||||
fn to_multiaddr(&self) -> Result<Multiaddr> {
|
||||
// TODO: Should how should we handle `flowinfo` and `scope_id`?
|
||||
format!("/ip6/{}/tcp/{}", self.ip(), self.port()).parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToMultiaddr for IpAddr {
|
||||
fn to_multiaddr(&self) -> Result<Multiaddr> {
|
||||
match *self {
|
||||
IpAddr::V4(ref a) => (*a).to_multiaddr(),
|
||||
IpAddr::V6(ref a) => (*a).to_multiaddr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToMultiaddr for Ipv4Addr {
|
||||
fn to_multiaddr(&self) -> Result<Multiaddr> {
|
||||
format!("/ip4/{}", &self).parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToMultiaddr for Ipv6Addr {
|
||||
fn to_multiaddr(&self) -> Result<Multiaddr> {
|
||||
format!("/ip6/{}", &self).parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToMultiaddr for String {
|
||||
fn to_multiaddr(&self) -> Result<Multiaddr> {
|
||||
self.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToMultiaddr for &'a str {
|
||||
fn to_multiaddr(&self) -> Result<Multiaddr> {
|
||||
self.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToMultiaddr for Multiaddr {
|
||||
fn to_multiaddr(&self) -> Result<Multiaddr> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
}
|
518
multiaddr/src/protocol.rs
Normal file
518
multiaddr/src/protocol.rs
Normal file
@ -0,0 +1,518 @@
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::str::FromStr;
|
||||
use std::convert::From;
|
||||
use std::io::{Cursor, Write, Result as IoResult};
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use cid::Cid;
|
||||
use integer_encoding::{VarInt, VarIntWriter};
|
||||
|
||||
use {Result, Error};
|
||||
|
||||
///! # Protocol
|
||||
///!
|
||||
///! A type to describe the possible protocol used in a
|
||||
///! Multiaddr.
|
||||
|
||||
/// Protocol is the list of all possible protocols.
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum Protocol {
|
||||
IP4 = 4,
|
||||
TCP = 6,
|
||||
UDP = 17,
|
||||
DCCP = 33,
|
||||
IP6 = 41,
|
||||
DNS4 = 54,
|
||||
DNS6 = 55,
|
||||
SCTP = 132,
|
||||
UDT = 301,
|
||||
UTP = 302,
|
||||
UNIX = 400,
|
||||
P2P = 420,
|
||||
IPFS = 421,
|
||||
HTTP = 480,
|
||||
HTTPS = 443,
|
||||
ONION = 444,
|
||||
QUIC = 460,
|
||||
WS = 477,
|
||||
WSS = 478,
|
||||
Libp2pWebsocketStar = 479,
|
||||
Libp2pWebrtcStar = 275,
|
||||
Libp2pWebrtcDirect = 276,
|
||||
P2pCircuit = 290,
|
||||
}
|
||||
|
||||
impl From<Protocol> for u32 {
|
||||
fn from(proto: Protocol) -> u32 {
|
||||
proto as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Protocol> for u64 {
|
||||
fn from(proto: Protocol) -> u64 {
|
||||
proto as u32 as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Protocol {
|
||||
fn to_string(&self) -> String {
|
||||
match *self {
|
||||
Protocol::IP4 => "ip4",
|
||||
Protocol::TCP => "tcp",
|
||||
Protocol::UDP => "udp",
|
||||
Protocol::DCCP => "dccp",
|
||||
Protocol::IP6 => "ip6",
|
||||
Protocol::DNS4 => "dns4",
|
||||
Protocol::DNS6 => "dns6",
|
||||
Protocol::SCTP => "sctp",
|
||||
Protocol::UDT => "udt",
|
||||
Protocol::UTP => "utp",
|
||||
Protocol::UNIX => "unix",
|
||||
Protocol::P2P => "p2p",
|
||||
Protocol::IPFS => "ipfs",
|
||||
Protocol::HTTP => "http",
|
||||
Protocol::HTTPS => "https",
|
||||
Protocol::ONION => "onion",
|
||||
Protocol::QUIC => "quic",
|
||||
Protocol::WS => "ws",
|
||||
Protocol::WSS => "wss",
|
||||
Protocol::Libp2pWebsocketStar => "p2p-websocket-star",
|
||||
Protocol::Libp2pWebrtcStar => "p2p-webrtc-star",
|
||||
Protocol::Libp2pWebrtcDirect => "p2p-webrtc-direct",
|
||||
Protocol::P2pCircuit => "p2p-circuit",
|
||||
}.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Protocol {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(raw: &str) -> Result<Self> {
|
||||
match raw {
|
||||
"ip4" => Ok(Protocol::IP4),
|
||||
"tcp" => Ok(Protocol::TCP),
|
||||
"udp" => Ok(Protocol::UDP),
|
||||
"dccp" => Ok(Protocol::DCCP),
|
||||
"ip6" => Ok(Protocol::IP6),
|
||||
"dns4" => Ok(Protocol::DNS4),
|
||||
"dns6" => Ok(Protocol::DNS6),
|
||||
"sctp" => Ok(Protocol::SCTP),
|
||||
"udt" => Ok(Protocol::UDT),
|
||||
"utp" => Ok(Protocol::UTP),
|
||||
"unix" => Ok(Protocol::UNIX),
|
||||
"p2p" => Ok(Protocol::P2P),
|
||||
"ipfs" => Ok(Protocol::IPFS),
|
||||
"http" => Ok(Protocol::HTTP),
|
||||
"https" => Ok(Protocol::HTTPS),
|
||||
"onion" => Ok(Protocol::ONION),
|
||||
"quic" => Ok(Protocol::QUIC),
|
||||
"ws" => Ok(Protocol::WS),
|
||||
"wss" => Ok(Protocol::WSS),
|
||||
"p2p-websocket-star" => Ok(Protocol::Libp2pWebsocketStar),
|
||||
"p2p-webrtc-star" => Ok(Protocol::Libp2pWebrtcStar),
|
||||
"p2p-webrtc-direct" => Ok(Protocol::Libp2pWebrtcDirect),
|
||||
"p2p-circuit" => Ok(Protocol::P2pCircuit),
|
||||
_ => Err(Error::UnknownProtocolString),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Protocol {
|
||||
/// Convert a `u64` based code to a `Protocol`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use multiaddr::Protocol;
|
||||
///
|
||||
/// assert_eq!(Protocol::from(6).unwrap(), Protocol::TCP);
|
||||
/// assert!(Protocol::from(455).is_err());
|
||||
/// ```
|
||||
pub fn from(raw: u64) -> Result<Protocol> {
|
||||
match raw {
|
||||
4 => Ok(Protocol::IP4),
|
||||
6 => Ok(Protocol::TCP),
|
||||
17 => Ok(Protocol::UDP),
|
||||
33 => Ok(Protocol::DCCP),
|
||||
41 => Ok(Protocol::IP6),
|
||||
54 => Ok(Protocol::DNS4),
|
||||
55 => Ok(Protocol::DNS6),
|
||||
132 => Ok(Protocol::SCTP),
|
||||
301 => Ok(Protocol::UDT),
|
||||
302 => Ok(Protocol::UTP),
|
||||
400 => Ok(Protocol::UNIX),
|
||||
420 => Ok(Protocol::P2P),
|
||||
421 => Ok(Protocol::IPFS),
|
||||
480 => Ok(Protocol::HTTP),
|
||||
443 => Ok(Protocol::HTTPS),
|
||||
444 => Ok(Protocol::ONION),
|
||||
460 => Ok(Protocol::QUIC),
|
||||
477 => Ok(Protocol::WS),
|
||||
478 => Ok(Protocol::WSS),
|
||||
479 => Ok(Protocol::Libp2pWebsocketStar),
|
||||
275 => Ok(Protocol::Libp2pWebrtcStar),
|
||||
276 => Ok(Protocol::Libp2pWebrtcDirect),
|
||||
290 => Ok(Protocol::P2pCircuit),
|
||||
_ => Err(Error::UnknownProtocol),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the size from a `Protocol`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use multiaddr::Protocol;
|
||||
/// use multiaddr::ProtocolArgSize;
|
||||
///
|
||||
/// assert_eq!(Protocol::TCP.size(), ProtocolArgSize::Fixed { bytes: 2 });
|
||||
/// ```
|
||||
///
|
||||
pub fn size(&self) -> ProtocolArgSize {
|
||||
match *self {
|
||||
Protocol::IP4 => ProtocolArgSize::Fixed { bytes: 4 },
|
||||
Protocol::TCP => ProtocolArgSize::Fixed { bytes: 2 },
|
||||
Protocol::UDP => ProtocolArgSize::Fixed { bytes: 2 },
|
||||
Protocol::DCCP => ProtocolArgSize::Fixed { bytes: 2 },
|
||||
Protocol::IP6 => ProtocolArgSize::Fixed { bytes: 16 },
|
||||
Protocol::DNS4 => ProtocolArgSize::Variable,
|
||||
Protocol::DNS6 => ProtocolArgSize::Variable,
|
||||
Protocol::SCTP => ProtocolArgSize::Fixed { bytes: 2 },
|
||||
Protocol::UDT => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
Protocol::UTP => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
Protocol::UNIX => ProtocolArgSize::Variable,
|
||||
Protocol::P2P => ProtocolArgSize::Variable,
|
||||
Protocol::IPFS => ProtocolArgSize::Variable,
|
||||
Protocol::HTTP => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
Protocol::HTTPS => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
Protocol::ONION => ProtocolArgSize::Fixed { bytes: 10 },
|
||||
Protocol::QUIC => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
Protocol::WS => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
Protocol::WSS => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
Protocol::Libp2pWebsocketStar => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
Protocol::Libp2pWebrtcStar => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
Protocol::Libp2pWebrtcDirect => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
Protocol::P2pCircuit => ProtocolArgSize::Fixed { bytes: 0 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ProtocolArgSize {
|
||||
/// The size of the argument is of fixed length. The length can be 0, in which case there is no
|
||||
/// argument.
|
||||
Fixed { bytes: usize },
|
||||
/// The size of the argument is of variable length.
|
||||
Variable,
|
||||
}
|
||||
|
||||
impl Protocol {
|
||||
/// Convert an array slice to the string representation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::Ipv4Addr;
|
||||
/// use multiaddr::AddrComponent;
|
||||
/// use multiaddr::Protocol;
|
||||
///
|
||||
/// let proto = Protocol::IP4;
|
||||
/// assert_eq!(proto.parse_data("127.0.0.1").unwrap(),
|
||||
/// AddrComponent::IP4(Ipv4Addr::new(127, 0, 0, 1)));
|
||||
/// ```
|
||||
///
|
||||
pub fn parse_data(&self, a: &str) -> Result<AddrComponent> {
|
||||
match *self {
|
||||
Protocol::IP4 => {
|
||||
let addr = Ipv4Addr::from_str(a)?;
|
||||
Ok(AddrComponent::IP4(addr))
|
||||
}
|
||||
Protocol::IP6 => {
|
||||
let addr = Ipv6Addr::from_str(a)?;
|
||||
Ok(AddrComponent::IP6(addr))
|
||||
}
|
||||
Protocol::DNS4 => {
|
||||
Ok(AddrComponent::DNS4(a.to_owned()))
|
||||
}
|
||||
Protocol::DNS6 => {
|
||||
Ok(AddrComponent::DNS6(a.to_owned()))
|
||||
}
|
||||
Protocol::TCP => {
|
||||
let parsed: u16 = a.parse()?;
|
||||
Ok(AddrComponent::TCP(parsed))
|
||||
}
|
||||
Protocol::UDP => {
|
||||
let parsed: u16 = a.parse()?;
|
||||
Ok(AddrComponent::UDP(parsed))
|
||||
}
|
||||
Protocol::DCCP => {
|
||||
let parsed: u16 = a.parse()?;
|
||||
Ok(AddrComponent::DCCP(parsed))
|
||||
}
|
||||
Protocol::SCTP => {
|
||||
let parsed: u16 = a.parse()?;
|
||||
Ok(AddrComponent::SCTP(parsed))
|
||||
}
|
||||
Protocol::P2P => {
|
||||
let bytes = Cid::from(a)?.to_bytes();
|
||||
Ok(AddrComponent::P2P(bytes))
|
||||
}
|
||||
Protocol::IPFS => {
|
||||
let bytes = Cid::from(a)?.to_bytes();
|
||||
Ok(AddrComponent::IPFS(bytes))
|
||||
}
|
||||
Protocol::ONION => unimplemented!(), // TODO:
|
||||
Protocol::QUIC => Ok(AddrComponent::QUIC),
|
||||
Protocol::UTP => Ok(AddrComponent::UTP),
|
||||
Protocol::UNIX => {
|
||||
Ok(AddrComponent::UNIX(a.to_owned()))
|
||||
}
|
||||
Protocol::UDT => Ok(AddrComponent::UDT),
|
||||
Protocol::HTTP => Ok(AddrComponent::HTTP),
|
||||
Protocol::HTTPS => Ok(AddrComponent::HTTPS),
|
||||
Protocol::WS => Ok(AddrComponent::WS),
|
||||
Protocol::WSS => Ok(AddrComponent::WSS),
|
||||
Protocol::Libp2pWebsocketStar => Ok(AddrComponent::Libp2pWebsocketStar),
|
||||
Protocol::Libp2pWebrtcStar => Ok(AddrComponent::Libp2pWebrtcStar),
|
||||
Protocol::Libp2pWebrtcDirect => Ok(AddrComponent::Libp2pWebrtcDirect),
|
||||
Protocol::P2pCircuit => Ok(AddrComponent::P2pCircuit),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum AddrComponent {
|
||||
IP4(Ipv4Addr),
|
||||
TCP(u16),
|
||||
UDP(u16),
|
||||
DCCP(u16),
|
||||
IP6(Ipv6Addr),
|
||||
DNS4(String),
|
||||
DNS6(String),
|
||||
SCTP(u16),
|
||||
UDT,
|
||||
UTP,
|
||||
UNIX(String),
|
||||
P2P(Vec<u8>),
|
||||
IPFS(Vec<u8>),
|
||||
HTTP,
|
||||
HTTPS,
|
||||
ONION(Vec<u8>),
|
||||
QUIC,
|
||||
WS,
|
||||
WSS,
|
||||
Libp2pWebsocketStar,
|
||||
Libp2pWebrtcStar,
|
||||
Libp2pWebrtcDirect,
|
||||
P2pCircuit,
|
||||
}
|
||||
|
||||
impl AddrComponent {
|
||||
/// Returns the `Protocol` corresponding to this `AddrComponent`.
|
||||
#[inline]
|
||||
pub fn protocol_id(&self) -> Protocol {
|
||||
match *self {
|
||||
AddrComponent::IP4(_) => Protocol::IP4,
|
||||
AddrComponent::TCP(_) => Protocol::TCP,
|
||||
AddrComponent::UDP(_) => Protocol::UDP,
|
||||
AddrComponent::DCCP(_) => Protocol::DCCP,
|
||||
AddrComponent::IP6(_) => Protocol::IP6,
|
||||
AddrComponent::DNS4(_) => Protocol::DNS4,
|
||||
AddrComponent::DNS6(_) => Protocol::DNS6,
|
||||
AddrComponent::SCTP(_) => Protocol::SCTP,
|
||||
AddrComponent::UDT => Protocol::UDT,
|
||||
AddrComponent::UTP => Protocol::UTP,
|
||||
AddrComponent::UNIX(_) => Protocol::UNIX,
|
||||
AddrComponent::P2P(_) => Protocol::P2P,
|
||||
AddrComponent::IPFS(_) => Protocol::IPFS,
|
||||
AddrComponent::HTTP => Protocol::HTTP,
|
||||
AddrComponent::HTTPS => Protocol::HTTPS,
|
||||
AddrComponent::ONION(_) => Protocol::ONION,
|
||||
AddrComponent::QUIC => Protocol::QUIC,
|
||||
AddrComponent::WS => Protocol::WS,
|
||||
AddrComponent::WSS => Protocol::WSS,
|
||||
AddrComponent::Libp2pWebsocketStar => Protocol::Libp2pWebsocketStar,
|
||||
AddrComponent::Libp2pWebrtcStar => Protocol::Libp2pWebrtcStar,
|
||||
AddrComponent::Libp2pWebrtcDirect => Protocol::Libp2pWebrtcDirect,
|
||||
AddrComponent::P2pCircuit => Protocol::P2pCircuit,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds an `AddrComponent` from an array that starts with a bytes representation. On
|
||||
/// success, also returns the rest of the slice.
|
||||
pub fn from_bytes(input: &[u8]) -> Result<(AddrComponent, &[u8])> {
|
||||
let (proto_num, proto_id_len) = u64::decode_var(input); // TODO: will panic if ID too large
|
||||
|
||||
let protocol_id = Protocol::from(proto_num)?;
|
||||
let (data_offset, data_size) = match protocol_id.size() {
|
||||
ProtocolArgSize::Fixed { bytes } => {
|
||||
(0, bytes)
|
||||
},
|
||||
ProtocolArgSize::Variable => {
|
||||
let (data_size, varint_len) = u64::decode_var(&input[proto_id_len..]); // TODO: will panic if ID too large
|
||||
(varint_len, data_size as usize)
|
||||
},
|
||||
};
|
||||
|
||||
let (data, rest) = input[proto_id_len..][data_offset..].split_at(data_size);
|
||||
|
||||
let addr_component = match protocol_id {
|
||||
Protocol::IP4 => {
|
||||
AddrComponent::IP4(Ipv4Addr::new(data[0], data[1], data[2], data[3]))
|
||||
},
|
||||
Protocol::IP6 => {
|
||||
let mut rdr = Cursor::new(data);
|
||||
let mut seg = vec![];
|
||||
|
||||
for _ in 0..8 {
|
||||
seg.push(rdr.read_u16::<BigEndian>()?);
|
||||
}
|
||||
|
||||
let addr = Ipv6Addr::new(seg[0],
|
||||
seg[1],
|
||||
seg[2],
|
||||
seg[3],
|
||||
seg[4],
|
||||
seg[5],
|
||||
seg[6],
|
||||
seg[7]);
|
||||
AddrComponent::IP6(addr)
|
||||
}
|
||||
Protocol::DNS4 => {
|
||||
AddrComponent::DNS4(String::from_utf8(data.to_owned())?)
|
||||
}
|
||||
Protocol::DNS6 => {
|
||||
AddrComponent::DNS6(String::from_utf8(data.to_owned())?)
|
||||
}
|
||||
Protocol::TCP => {
|
||||
let mut rdr = Cursor::new(data);
|
||||
let num = rdr.read_u16::<BigEndian>()?;
|
||||
AddrComponent::TCP(num)
|
||||
}
|
||||
Protocol::UDP => {
|
||||
let mut rdr = Cursor::new(data);
|
||||
let num = rdr.read_u16::<BigEndian>()?;
|
||||
AddrComponent::UDP(num)
|
||||
}
|
||||
Protocol::DCCP => {
|
||||
let mut rdr = Cursor::new(data);
|
||||
let num = rdr.read_u16::<BigEndian>()?;
|
||||
AddrComponent::DCCP(num)
|
||||
}
|
||||
Protocol::SCTP => {
|
||||
let mut rdr = Cursor::new(data);
|
||||
let num = rdr.read_u16::<BigEndian>()?;
|
||||
AddrComponent::SCTP(num)
|
||||
}
|
||||
Protocol::UNIX => {
|
||||
AddrComponent::UNIX(String::from_utf8(data.to_owned())?)
|
||||
}
|
||||
Protocol::P2P => {
|
||||
let bytes = Cid::from(data)?.to_bytes();
|
||||
AddrComponent::P2P(bytes)
|
||||
}
|
||||
Protocol::IPFS => {
|
||||
let bytes = Cid::from(data)?.to_bytes();
|
||||
AddrComponent::IPFS(bytes)
|
||||
}
|
||||
Protocol::ONION => unimplemented!(), // TODO:
|
||||
Protocol::QUIC => AddrComponent::QUIC,
|
||||
Protocol::UTP => AddrComponent::UTP,
|
||||
Protocol::UDT => AddrComponent::UDT,
|
||||
Protocol::HTTP => AddrComponent::HTTP,
|
||||
Protocol::HTTPS => AddrComponent::HTTPS,
|
||||
Protocol::WS => AddrComponent::WS,
|
||||
Protocol::WSS => AddrComponent::WSS,
|
||||
Protocol::Libp2pWebsocketStar => AddrComponent::Libp2pWebsocketStar,
|
||||
Protocol::Libp2pWebrtcStar => AddrComponent::Libp2pWebrtcStar,
|
||||
Protocol::Libp2pWebrtcDirect => AddrComponent::Libp2pWebrtcDirect,
|
||||
Protocol::P2pCircuit => AddrComponent::P2pCircuit,
|
||||
};
|
||||
|
||||
Ok((addr_component, rest))
|
||||
}
|
||||
|
||||
/// Turns this address component into bytes by writing it to a `Write`.
|
||||
pub fn write_bytes<W: Write>(self, out: &mut W) -> IoResult<()> {
|
||||
out.write_varint(Into::<u64>::into(self.protocol_id()))?;
|
||||
|
||||
match self {
|
||||
AddrComponent::IP4(addr) => {
|
||||
out.write_all(&addr.octets())?;
|
||||
}
|
||||
AddrComponent::IP6(addr) => {
|
||||
for &segment in &addr.segments() {
|
||||
out.write_u16::<BigEndian>(segment)?;
|
||||
}
|
||||
}
|
||||
AddrComponent::TCP(port) | AddrComponent::UDP(port) | AddrComponent::DCCP(port) |
|
||||
AddrComponent::SCTP(port) => {
|
||||
out.write_u16::<BigEndian>(port)?;
|
||||
}
|
||||
AddrComponent::DNS4(s) | AddrComponent::DNS6(s) | AddrComponent::UNIX(s) => {
|
||||
let bytes = s.as_bytes();
|
||||
out.write_varint(bytes.len())?;
|
||||
out.write_all(&bytes)?;
|
||||
}
|
||||
AddrComponent::P2P(bytes) | AddrComponent::IPFS(bytes) => {
|
||||
out.write_varint(bytes.len())?;
|
||||
out.write_all(&bytes)?;
|
||||
}
|
||||
AddrComponent::ONION(_) => {
|
||||
unimplemented!() // TODO:
|
||||
},
|
||||
AddrComponent::QUIC |
|
||||
AddrComponent::UTP |
|
||||
AddrComponent::UDT |
|
||||
AddrComponent::HTTP |
|
||||
AddrComponent::HTTPS |
|
||||
AddrComponent::WS |
|
||||
AddrComponent::WSS |
|
||||
AddrComponent::Libp2pWebsocketStar |
|
||||
AddrComponent::Libp2pWebrtcStar |
|
||||
AddrComponent::Libp2pWebrtcDirect |
|
||||
AddrComponent::P2pCircuit => {}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for AddrComponent {
|
||||
fn to_string(&self) -> String {
|
||||
match *self {
|
||||
AddrComponent::IP4(ref addr) => format!("/ip4/{}", addr),
|
||||
AddrComponent::TCP(port) => format!("/tcp/{}", port),
|
||||
AddrComponent::UDP(port) => format!("/udp/{}", port),
|
||||
AddrComponent::DCCP(port) => format!("/dccp/{}", port),
|
||||
AddrComponent::IP6(ref addr) => format!("/ip6/{}", addr),
|
||||
AddrComponent::DNS4(ref s) => format!("/dns4/{}", s.clone()),
|
||||
AddrComponent::DNS6(ref s) => format!("/dns6/{}", s.clone()),
|
||||
AddrComponent::SCTP(port) => format!("/sctp/{}", port),
|
||||
AddrComponent::UDT => format!("/udt"),
|
||||
AddrComponent::UTP => format!("/utp"),
|
||||
AddrComponent::UNIX(ref s) => format!("/unix/{}", s.clone()),
|
||||
AddrComponent::P2P(ref bytes) => {
|
||||
// TODO: meh for cloning
|
||||
let c = Cid::from(bytes.clone()).expect("cid is known to be valid");
|
||||
format!("/p2p/{}", c)
|
||||
},
|
||||
AddrComponent::IPFS(ref bytes) => {
|
||||
// TODO: meh for cloning
|
||||
let c = Cid::from(bytes.clone()).expect("cid is known to be valid");
|
||||
format!("/ipfs/{}", c)
|
||||
},
|
||||
AddrComponent::HTTP => format!("/http"),
|
||||
AddrComponent::HTTPS => format!("/https"),
|
||||
AddrComponent::ONION(_) => unimplemented!(),//format!("/onion"), // TODO:
|
||||
AddrComponent::QUIC => format!("/quic"),
|
||||
AddrComponent::WS => format!("/ws"),
|
||||
AddrComponent::WSS => format!("/wss"),
|
||||
AddrComponent::Libp2pWebsocketStar => format!("/p2p-websocket-star"),
|
||||
AddrComponent::Libp2pWebrtcStar => format!("/p2p-webrtc-star"),
|
||||
AddrComponent::Libp2pWebrtcDirect => format!("/p2p-webrtc-direct"),
|
||||
AddrComponent::P2pCircuit => format!("/p2p-circuit"),
|
||||
}
|
||||
}
|
||||
}
|
175
multiaddr/tests/lib.rs
Normal file
175
multiaddr/tests/lib.rs
Normal file
@ -0,0 +1,175 @@
|
||||
extern crate multiaddr;
|
||||
extern crate data_encoding;
|
||||
|
||||
use data_encoding::hex;
|
||||
use multiaddr::*;
|
||||
use std::net::{SocketAddrV4, SocketAddrV6, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
#[test]
|
||||
fn protocol_to_code() {
|
||||
assert_eq!(Protocol::IP4 as usize, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn protocol_to_name() {
|
||||
assert_eq!(Protocol::TCP.to_string(), "tcp");
|
||||
}
|
||||
|
||||
|
||||
fn ma_valid(source: &str, target: &str, protocols: Vec<Protocol>) {
|
||||
let parsed = source.parse::<Multiaddr>().unwrap();
|
||||
assert_eq!(hex::encode(parsed.to_bytes().as_slice()), target);
|
||||
assert_eq!(parsed.iter().map(|addr| addr.protocol_id()).collect::<Vec<_>>(), protocols);
|
||||
assert_eq!(source.parse::<Multiaddr>().unwrap().to_string(), source);
|
||||
assert_eq!(Multiaddr::from_bytes(hex::decode(target.as_bytes()).unwrap()).unwrap(), parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiaddr_eq() {
|
||||
let m1 = "/ip4/127.0.0.1/udp/1234".parse::<Multiaddr>().unwrap();
|
||||
let m2 = "/ip4/127.0.0.1/tcp/1234".parse::<Multiaddr>().unwrap();
|
||||
let m3 = "/ip4/127.0.0.1/tcp/1234".parse::<Multiaddr>().unwrap();
|
||||
|
||||
assert_ne!(m1, m2);
|
||||
assert_ne!(m2, m1);
|
||||
assert_eq!(m2, m3);
|
||||
assert_eq!(m1, m1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construct_success() {
|
||||
use Protocol::*;
|
||||
|
||||
ma_valid("/ip4/1.2.3.4", "0401020304", vec![IP4]);
|
||||
ma_valid("/ip4/0.0.0.0", "0400000000", vec![IP4]);
|
||||
ma_valid("/ip6/::1", "2900000000000000000000000000000001", vec![IP6]);
|
||||
ma_valid("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
|
||||
"29260100094F819700803ECA6566E80C21",
|
||||
vec![IP6]);
|
||||
ma_valid("/udp/0", "110000", vec![UDP]);
|
||||
ma_valid("/tcp/0", "060000", vec![TCP]);
|
||||
ma_valid("/sctp/0", "84010000", vec![SCTP]);
|
||||
ma_valid("/udp/1234", "1104D2", vec![UDP]);
|
||||
ma_valid("/tcp/1234", "0604D2", vec![TCP]);
|
||||
ma_valid("/sctp/1234", "840104D2", vec![SCTP]);
|
||||
ma_valid("/udp/65535", "11FFFF", vec![UDP]);
|
||||
ma_valid("/tcp/65535", "06FFFF", vec![TCP]);
|
||||
ma_valid("/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||
"A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
|
||||
vec![IPFS]);
|
||||
ma_valid("/udp/1234/sctp/1234", "1104D2840104D2", vec![UDP, SCTP]);
|
||||
ma_valid("/udp/1234/udt", "1104D2AD02", vec![UDP, UDT]);
|
||||
ma_valid("/udp/1234/utp", "1104D2AE02", vec![UDP, UTP]);
|
||||
ma_valid("/tcp/1234/http", "0604D2E003", vec![TCP, HTTP]);
|
||||
ma_valid("/tcp/1234/https", "0604D2BB03", vec![TCP, HTTPS]);
|
||||
ma_valid("/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
|
||||
"A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2",
|
||||
vec![IPFS, TCP]);
|
||||
ma_valid("/ip4/127.0.0.1/udp/1234",
|
||||
"047F0000011104D2",
|
||||
vec![IP4, UDP]);
|
||||
ma_valid("/ip4/127.0.0.1/udp/0", "047F000001110000", vec![IP4, UDP]);
|
||||
ma_valid("/ip4/127.0.0.1/tcp/1234",
|
||||
"047F0000010604D2",
|
||||
vec![IP4, TCP]);
|
||||
ma_valid("/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||
"047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
|
||||
vec![IP4, IPFS]);
|
||||
ma_valid("/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
|
||||
"047F000001A503221220D52EBB89D85B02A284948203A\
|
||||
62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2",
|
||||
vec![IP4, IPFS, TCP]);
|
||||
// /unix/a/b/c/d/e,
|
||||
// /unix/stdio,
|
||||
// /ip4/1.2.3.4/tcp/80/unix/a/b/c/d/e/f,
|
||||
// /ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio
|
||||
ma_valid("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:\
|
||||
7095/tcp/8000/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||
"29200108A07AC542013AC986FFFE317095061F40DD03A5\
|
||||
03221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
|
||||
vec![IP6, TCP, WS, IPFS]);
|
||||
ma_valid("/p2p-webrtc-star/ip4/127.0.0.\
|
||||
1/tcp/9090/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||
"9302047F000001062382DD03A503221220D52EBB89D85B\
|
||||
02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
|
||||
vec![Libp2pWebrtcStar, IP4, TCP, WS, IPFS]);
|
||||
ma_valid("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:\
|
||||
7095/tcp/8000/wss/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||
"29200108A07AC542013AC986FFFE317095061F40DE03A503221220D52EBB8\
|
||||
9D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
|
||||
vec![IP6, TCP, WSS, IPFS]);
|
||||
ma_valid("/ip4/127.0.0.1/tcp/9090/p2p-circuit/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
|
||||
"047F000001062382A202A503221220D52EBB89D85B\
|
||||
02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B",
|
||||
vec![IP4, TCP, P2pCircuit, IPFS]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construct_fail() {
|
||||
let addresses = ["/ip4",
|
||||
"/ip4/::1",
|
||||
"/ip4/fdpsofodsajfdoisa",
|
||||
"/ip6",
|
||||
"/udp",
|
||||
"/tcp",
|
||||
"/sctp",
|
||||
"/udp/65536",
|
||||
"/tcp/65536",
|
||||
// "/onion/9imaq4ygg2iegci7:80",
|
||||
// "/onion/aaimaq4ygg2iegci7:80",
|
||||
// "/onion/timaq4ygg2iegci7:0",
|
||||
// "/onion/timaq4ygg2iegci7:-1",
|
||||
// "/onion/timaq4ygg2iegci7",
|
||||
// "/onion/timaq4ygg2iegci@:666",
|
||||
"/udp/1234/sctp",
|
||||
"/udp/1234/udt/1234",
|
||||
"/udp/1234/utp/1234",
|
||||
"/ip4/127.0.0.1/udp/jfodsajfidosajfoidsa",
|
||||
"/ip4/127.0.0.1/udp",
|
||||
"/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa",
|
||||
"/ip4/127.0.0.1/tcp",
|
||||
"/ip4/127.0.0.1/ipfs",
|
||||
"/ip4/127.0.0.1/ipfs/tcp",
|
||||
"/p2p-circuit/50"];
|
||||
|
||||
for address in &addresses {
|
||||
assert!(address.parse::<Multiaddr>().is_err(), address.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn to_multiaddr() {
|
||||
assert_eq!(Ipv4Addr::new(127, 0, 0, 1).to_multiaddr().unwrap(),
|
||||
"/ip4/127.0.0.1".parse::<Multiaddr>().unwrap());
|
||||
assert_eq!(Ipv6Addr::new(0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21)
|
||||
.to_multiaddr()
|
||||
.unwrap(),
|
||||
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".parse::<Multiaddr>().unwrap());
|
||||
assert_eq!("/ip4/127.0.0.1/tcp/1234".to_string().to_multiaddr().unwrap(),
|
||||
"/ip4/127.0.0.1/tcp/1234".parse::<Multiaddr>().unwrap());
|
||||
assert_eq!("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".to_multiaddr().unwrap(),
|
||||
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".parse::<Multiaddr>().unwrap());
|
||||
assert_eq!(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 1234).to_multiaddr().unwrap(),
|
||||
"/ip4/127.0.0.1/tcp/1234".parse::<Multiaddr>().unwrap());
|
||||
assert_eq!(SocketAddrV6::new(Ipv6Addr::new(0x2601,
|
||||
0x9,
|
||||
0x4f81,
|
||||
0x9700,
|
||||
0x803e,
|
||||
0xca65,
|
||||
0x66e8,
|
||||
0xc21),
|
||||
1234,
|
||||
0,
|
||||
0)
|
||||
.to_multiaddr()
|
||||
.unwrap(),
|
||||
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/tcp/1234".parse::<Multiaddr>().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_bytes_fail() {
|
||||
let bytes = vec![1, 2, 3, 4];
|
||||
assert!(Multiaddr::from_bytes(bytes).is_err());
|
||||
}
|
3
multihash/.gitignore
vendored
Normal file
3
multihash/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
target
|
||||
Cargo.lock
|
||||
*.bk
|
34
multihash/.travis.yml
Normal file
34
multihash/.travis.yml
Normal file
@ -0,0 +1,34 @@
|
||||
sudo: false
|
||||
|
||||
language: rust
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
- binutils-dev
|
||||
|
||||
rust:
|
||||
- beta
|
||||
- stable
|
||||
|
||||
before_script:
|
||||
- |
|
||||
pip install 'travis-cargo<0.2' --user &&
|
||||
export PATH=$HOME/.local/bin:$PATH
|
||||
|
||||
install:
|
||||
- pip install --user travis-cargo codecov
|
||||
- export PATH=$PATH:$HOME/.local/bin
|
||||
|
||||
script:
|
||||
- |
|
||||
travis-cargo build &&
|
||||
travis-cargo test &&
|
||||
travis-cargo --only stable doc
|
||||
|
||||
after_success:
|
||||
- travis-cargo coverage --no-sudo
|
||||
- codecov --file target/kcov/kcov-merged/cobertura.xml
|
21
multihash/Cargo.toml
Normal file
21
multihash/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "multihash"
|
||||
description = "Implementation of the multihash format"
|
||||
repository = "https://github.com/multiformats/rust-multihash"
|
||||
|
||||
keywords = ["multihash", "ipfs"]
|
||||
|
||||
version = "0.8.1-pre"
|
||||
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
|
||||
license = "MIT"
|
||||
|
||||
readme = "README.md"
|
||||
|
||||
documentation = "https://docs.rs/multihash/"
|
||||
|
||||
[dependencies]
|
||||
sha1 = "0.5"
|
||||
sha2 = { version = "0.7", default-features = false }
|
||||
tiny-keccak = "1.2"
|
21
multihash/LICENSE
Normal file
21
multihash/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (C) 2015-2016 Friedel Ziegelmayer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Status API Training Shop Blog About Pricing
|
68
multihash/README.md
Normal file
68
multihash/README.md
Normal file
@ -0,0 +1,68 @@
|
||||
# rust-multihash
|
||||
|
||||
[](http://ipn.io)
|
||||
[](https://github.com/multiformats/multiformats)
|
||||
[](https://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://travis-ci.org/multiformats/rust-multihash)
|
||||
[](https://codecov.io/github/multiformats/rust-multihash?branch=master)
|
||||
[](https://docs.rs/multihash/)
|
||||
[](https://crates.io/crates/multihash)
|
||||
[](https://github.com/RichardLitt/standard-readme)
|
||||
|
||||
> [multihash](https://github.com/multiformats/multihash) implementation in Rust.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Supported Hash Types](#supported-hash-types)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
First add this to your `Cargo.toml`
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
multihash = "*"
|
||||
```
|
||||
|
||||
Then run `cargo build`.
|
||||
|
||||
## Usage
|
||||
|
||||
```rust
|
||||
extern crate multihash;
|
||||
|
||||
use multihash::{encode, decode, Hash};
|
||||
|
||||
let hash = encode(Hash::SHA2256, b"my hash").unwrap();
|
||||
let multi = decode(&hash).unwrap();
|
||||
```
|
||||
|
||||
## Supported Hash Types
|
||||
|
||||
* `SHA1`
|
||||
* `SHA2-256`
|
||||
* `SHA2-512`
|
||||
* `SHA3`/`Keccak`
|
||||
|
||||
## Maintainers
|
||||
|
||||
Captain: [@dignifiedquire](https://github.com/dignifiedquire).
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/multiformats/rust-multihash/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/multiformats/multiformats/blob/master/contributing.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to multiformats are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © 2015-2017 Friedel Ziegelmayer
|
24
multihash/src/errors.rs
Normal file
24
multihash/src/errors.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use std::{fmt, error};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
UnsupportedType,
|
||||
BadInputLength,
|
||||
UnknownCode,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::UnsupportedType => "This type is not supported yet",
|
||||
Error::BadInputLength => "Not matching input length",
|
||||
Error::UnknownCode => "Found unknown code",
|
||||
}
|
||||
}
|
||||
}
|
121
multihash/src/hashes.rs
Normal file
121
multihash/src/hashes.rs
Normal file
@ -0,0 +1,121 @@
|
||||
use errors::Error;
|
||||
|
||||
/// List of types currently supported in the multihash spec.
|
||||
///
|
||||
/// Not all hash types are supported by this library.
|
||||
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash)]
|
||||
pub enum Hash {
|
||||
/// SHA-1 (20-byte hash size)
|
||||
SHA1,
|
||||
/// SHA-256 (32-byte hash size)
|
||||
SHA2256,
|
||||
/// SHA-512 (64-byte hash size)
|
||||
SHA2512,
|
||||
/// SHA3-512 (64-byte hash size)
|
||||
SHA3512,
|
||||
/// SHA3-384 (48-byte hash size)
|
||||
SHA3384,
|
||||
/// SHA3-256 (32-byte hash size)
|
||||
SHA3256,
|
||||
/// SHA3-224 (28-byte hash size)
|
||||
SHA3224,
|
||||
/// Keccak-224 (28-byte hash size)
|
||||
Keccak224,
|
||||
/// Keccak-256 (32-byte hash size)
|
||||
Keccak256,
|
||||
/// Keccak-384 (48-byte hash size)
|
||||
Keccak384,
|
||||
/// Keccak-512 (64-byte hash size)
|
||||
Keccak512,
|
||||
/// Encoding unsupported
|
||||
Blake2b,
|
||||
/// Encoding unsupported
|
||||
Blake2s,
|
||||
}
|
||||
|
||||
impl Hash {
|
||||
/// Get the corresponding hash code
|
||||
pub fn code(&self) -> u8 {
|
||||
use Hash::*;
|
||||
|
||||
match *self {
|
||||
SHA1 => 0x11,
|
||||
SHA2256 => 0x12,
|
||||
SHA2512 => 0x13,
|
||||
SHA3224 => 0x17,
|
||||
SHA3256 => 0x16,
|
||||
SHA3384 => 0x15,
|
||||
SHA3512 => 0x14,
|
||||
Keccak224 => 0x1A,
|
||||
Keccak256 => 0x1B,
|
||||
Keccak384 => 0x1C,
|
||||
Keccak512 => 0x1D,
|
||||
Blake2b => 0x40,
|
||||
Blake2s => 0x41,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the hash length in bytes
|
||||
pub fn size(&self) -> u8 {
|
||||
use Hash::*;
|
||||
|
||||
match *self {
|
||||
SHA1 => 20,
|
||||
SHA2256 => 32,
|
||||
SHA2512 => 64,
|
||||
SHA3224 => 28,
|
||||
SHA3256 => 32,
|
||||
SHA3384 => 48,
|
||||
SHA3512 => 64,
|
||||
Keccak224 => 28,
|
||||
Keccak256 => 32,
|
||||
Keccak384 => 48,
|
||||
Keccak512 => 64,
|
||||
Blake2b => 64,
|
||||
Blake2s => 32,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the human readable name
|
||||
pub fn name(&self) -> &str {
|
||||
use Hash::*;
|
||||
|
||||
match *self {
|
||||
SHA1 => "SHA1",
|
||||
SHA2256 => "SHA2-256",
|
||||
SHA2512 => "SHA2-512",
|
||||
SHA3512 => "SHA3-512",
|
||||
SHA3384 => "SHA3-384",
|
||||
SHA3256 => "SHA3-256",
|
||||
SHA3224 => "SHA3-224",
|
||||
Keccak224 => "Keccak-224",
|
||||
Keccak256 => "Keccak-256",
|
||||
Keccak384 => "Keccak-384",
|
||||
Keccak512 => "Keccak-512",
|
||||
Blake2b => "Blake-2b",
|
||||
Blake2s => "Blake-2s",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_code(code: u8) -> Result<Hash, Error> {
|
||||
use Hash::*;
|
||||
|
||||
Ok(match code {
|
||||
0x11 => SHA1,
|
||||
0x12 => SHA2256,
|
||||
0x13 => SHA2512,
|
||||
0x14 => SHA3512,
|
||||
0x15 => SHA3384,
|
||||
0x16 => SHA3256,
|
||||
0x17 => SHA3224,
|
||||
0x1A => Keccak224,
|
||||
0x1B => Keccak256,
|
||||
0x1C => Keccak384,
|
||||
0x1D => Keccak512,
|
||||
0x40 => Blake2b,
|
||||
0x41 => Blake2s,
|
||||
_ => return Err(Error::UnknownCode),
|
||||
})
|
||||
}
|
||||
}
|
163
multihash/src/lib.rs
Normal file
163
multihash/src/lib.rs
Normal file
@ -0,0 +1,163 @@
|
||||
/// ! # multihash
|
||||
/// !
|
||||
/// ! Implementation of [multihash](https://github.com/multiformats/multihash)
|
||||
/// ! in Rust.
|
||||
/// Representation of a Multiaddr.
|
||||
|
||||
extern crate sha1;
|
||||
extern crate sha2;
|
||||
extern crate tiny_keccak;
|
||||
|
||||
use std::fmt::Write;
|
||||
use sha2::Digest;
|
||||
use tiny_keccak::Keccak;
|
||||
|
||||
mod hashes;
|
||||
pub use hashes::*;
|
||||
|
||||
mod errors;
|
||||
pub use errors::*;
|
||||
|
||||
// Helper macro for encoding input into output using sha1, sha2 or tiny_keccak
|
||||
macro_rules! encode {
|
||||
(sha1, Sha1, $input:expr, $output:expr) => ({
|
||||
let mut hasher = sha1::Sha1::new();
|
||||
hasher.update($input);
|
||||
$output.copy_from_slice(&hasher.digest().bytes());
|
||||
});
|
||||
(sha2, $algorithm:ident, $input:expr, $output:expr) => ({
|
||||
let mut hasher = sha2::$algorithm::default();
|
||||
hasher.input($input);
|
||||
$output.copy_from_slice(hasher.result().as_ref());
|
||||
});
|
||||
(tiny, $constructor:ident, $input:expr, $output:expr) => ({
|
||||
let mut kec = Keccak::$constructor();
|
||||
kec.update($input);
|
||||
kec.finalize($output);
|
||||
});
|
||||
}
|
||||
|
||||
// And another one to keep the matching DRY
|
||||
macro_rules! match_encoder {
|
||||
($hash:ident for ($input:expr, $output:expr) {
|
||||
$( $hashtype:ident => $lib:ident :: $method:ident, )*
|
||||
}) => ({
|
||||
match $hash {
|
||||
$(
|
||||
Hash::$hashtype => encode!($lib, $method, $input, $output),
|
||||
)*
|
||||
|
||||
_ => return Err(Error::UnsupportedType)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Encodes data into a multihash.
|
||||
///
|
||||
/// The returned data is raw bytes. To make is more human-friendly, you can encode it (hex,
|
||||
/// base58, base64, etc).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return an error if the specified hash type is not supported. See the docs for `Hash`
|
||||
/// to see what is supported.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use multihash::{encode, Hash};
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// encode(Hash::SHA2256, b"hello world").unwrap(),
|
||||
/// vec![18, 32, 185, 77, 39, 185, 147, 77, 62, 8, 165, 46, 82, 215, 218, 125, 171, 250, 196,
|
||||
/// 132, 239, 227, 122, 83, 128, 238, 144, 136, 247, 172, 226, 239, 205, 233]
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
pub fn encode(hash: Hash, input: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
let size = hash.size();
|
||||
let mut output = Vec::new();
|
||||
output.resize(2 + size as usize, 0);
|
||||
output[0] = hash.code();
|
||||
output[1] = size;
|
||||
|
||||
match_encoder!(hash for (input, &mut output[2..]) {
|
||||
SHA1 => sha1::Sha1,
|
||||
SHA2256 => sha2::Sha256,
|
||||
SHA2512 => sha2::Sha512,
|
||||
SHA3224 => tiny::new_sha3_224,
|
||||
SHA3256 => tiny::new_sha3_256,
|
||||
SHA3384 => tiny::new_sha3_384,
|
||||
SHA3512 => tiny::new_sha3_512,
|
||||
Keccak224 => tiny::new_keccak224,
|
||||
Keccak256 => tiny::new_keccak256,
|
||||
Keccak384 => tiny::new_keccak384,
|
||||
Keccak512 => tiny::new_keccak512,
|
||||
});
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Decodes bytes into a multihash
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the bytes are not a valid multihash.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use multihash::{decode, Hash, Multihash};
|
||||
///
|
||||
/// // use the data from the `encode` example
|
||||
/// let data = vec![18, 32, 185, 77, 39, 185, 147, 77, 62, 8, 165, 46, 82, 215, 218,
|
||||
/// 125, 171, 250, 196, 132, 239, 227, 122, 83, 128, 238, 144, 136, 247, 172, 226, 239, 205, 233];
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// decode(&data).unwrap(),
|
||||
/// Multihash {
|
||||
/// alg: Hash::SHA2256,
|
||||
/// digest: &data[2..]
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
pub fn decode(input: &[u8]) -> Result<Multihash, Error> {
|
||||
if input.is_empty() {
|
||||
return Err(Error::BadInputLength);
|
||||
}
|
||||
|
||||
let code = input[0];
|
||||
|
||||
let alg = Hash::from_code(code)?;
|
||||
let hash_len = alg.size() as usize;
|
||||
|
||||
// length of input should be exactly hash_len + 2
|
||||
if input.len() != hash_len + 2 {
|
||||
return Err(Error::BadInputLength);
|
||||
}
|
||||
|
||||
Ok(Multihash {
|
||||
alg: alg,
|
||||
digest: &input[2..],
|
||||
})
|
||||
}
|
||||
|
||||
/// Represents a valid multihash, by associating the hash algorithm with the data
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct Multihash<'a> {
|
||||
pub alg: Hash,
|
||||
pub digest: &'a [u8],
|
||||
}
|
||||
|
||||
/// Convert bytes to a hex representation
|
||||
pub fn to_hex(bytes: &[u8]) -> String {
|
||||
let mut hex = String::with_capacity(bytes.len() * 2);
|
||||
|
||||
for byte in bytes {
|
||||
write!(hex, "{:02x}", byte).expect("Can't fail on writing to string");
|
||||
}
|
||||
|
||||
hex
|
||||
}
|
104
multihash/tests/lib.rs
Normal file
104
multihash/tests/lib.rs
Normal file
@ -0,0 +1,104 @@
|
||||
extern crate multihash;
|
||||
|
||||
use multihash::*;
|
||||
|
||||
/// Helper function to convert a hex-encoded byte array back into a bytearray
|
||||
fn hex_to_bytes(s: &str) -> Vec<u8> {
|
||||
let mut c = 0;
|
||||
let mut v = Vec::new();
|
||||
while c < s.len() {
|
||||
v.push(u8::from_str_radix(&s[c..c+2], 16).unwrap());
|
||||
c += 2;
|
||||
}
|
||||
v
|
||||
|
||||
}
|
||||
|
||||
macro_rules! assert_encode {
|
||||
{$( $alg:ident, $data:expr, $expect:expr; )*} => {
|
||||
$(
|
||||
assert_eq!(
|
||||
encode(Hash::$alg, $data).expect("Must be supported"),
|
||||
hex_to_bytes($expect),
|
||||
"{} encodes correctly", Hash::$alg.name()
|
||||
);
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multihash_encode() {
|
||||
assert_encode! {
|
||||
SHA1, b"beep boop", "11147c8357577f51d4f0a8d393aa1aaafb28863d9421";
|
||||
SHA2256, b"helloworld", "1220936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af";
|
||||
SHA2256, b"beep boop", "122090ea688e275d580567325032492b597bc77221c62493e76330b85ddda191ef7c";
|
||||
SHA2512, b"hello world", "1340309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f";
|
||||
SHA3224, b"hello world", "171Cdfb7f18c77e928bb56faeb2da27291bd790bc1045cde45f3210bb6c5";
|
||||
SHA3256, b"hello world", "1620644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938";
|
||||
SHA3384, b"hello world", "153083bff28dde1b1bf5810071c6643c08e5b05bdb836effd70b403ea8ea0a634dc4997eb1053aa3593f590f9c63630dd90b";
|
||||
SHA3512, b"hello world", "1440840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a";
|
||||
Keccak224, b"hello world", "1A1C25f3ecfebabe99686282f57f5c9e1f18244cfee2813d33f955aae568";
|
||||
Keccak256, b"hello world", "1B2047173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad";
|
||||
Keccak384, b"hello world", "1C3065fc99339a2a40e99d3c40d695b22f278853ca0f925cde4254bcae5e22ece47e6441f91b6568425adc9d95b0072eb49f";
|
||||
Keccak512, b"hello world", "1D403ee2b40047b8060f68c67242175660f4174d0af5c01d47168ec20ed619b0b7c42181f40aa1046f39e2ef9efc6910782a998e0013d172458957957fac9405b67d";
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! assert_decode {
|
||||
{$( $alg:ident, $hash:expr; )*} => {
|
||||
$(
|
||||
let hash = hex_to_bytes($hash);
|
||||
assert_eq!(
|
||||
decode(&hash).unwrap().alg,
|
||||
Hash::$alg,
|
||||
"{} decodes correctly", Hash::$alg.name()
|
||||
);
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_decode() {
|
||||
assert_decode! {
|
||||
SHA1, "11147c8357577f51d4f0a8d393aa1aaafb28863d9421";
|
||||
SHA2256, "1220936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af";
|
||||
SHA2256, "122090ea688e275d580567325032492b597bc77221c62493e76330b85ddda191ef7c";
|
||||
SHA2512, "1340309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f";
|
||||
SHA3224, "171Cdfb7f18c77e928bb56faeb2da27291bd790bc1045cde45f3210bb6c5";
|
||||
SHA3256, "1620644bcc7e564373040999aac89e7622f3ca71fba1d972fd94a31c3bfbf24e3938";
|
||||
SHA3384, "153083bff28dde1b1bf5810071c6643c08e5b05bdb836effd70b403ea8ea0a634dc4997eb1053aa3593f590f9c63630dd90b";
|
||||
SHA3512, "1440840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a";
|
||||
Keccak224, "1A1C25f3ecfebabe99686282f57f5c9e1f18244cfee2813d33f955aae568";
|
||||
Keccak256, "1B2047173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad";
|
||||
Keccak384, "1C3065fc99339a2a40e99d3c40d695b22f278853ca0f925cde4254bcae5e22ece47e6441f91b6568425adc9d95b0072eb49f";
|
||||
Keccak512, "1D403ee2b40047b8060f68c67242175660f4174d0af5c01d47168ec20ed619b0b7c42181f40aa1046f39e2ef9efc6910782a998e0013d172458957957fac9405b67d";
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! assert_roundtrip {
|
||||
($( $alg:ident ),*) => {
|
||||
$(
|
||||
{
|
||||
let hash: Vec<u8> = encode(Hash::$alg, b"helloworld").unwrap();
|
||||
assert_eq!(
|
||||
decode(&hash).unwrap().alg,
|
||||
Hash::$alg
|
||||
);
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_roundtrip() {
|
||||
assert_roundtrip!(
|
||||
SHA1, SHA2256, SHA2512, SHA3224, SHA3256, SHA3384, SHA3512,
|
||||
Keccak224, Keccak256, Keccak384, Keccak512
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_types() {
|
||||
assert_eq!(Hash::SHA2256.size(), 32);
|
||||
assert_eq!(Hash::SHA2256.name(), "SHA2-256");
|
||||
}
|
@ -9,11 +9,11 @@ datastore = { path = "../datastore" }
|
||||
futures = "0.1.0"
|
||||
owning_ref = "0.3.3"
|
||||
libp2p-core = { path = "../core" }
|
||||
multiaddr = "0.3"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
multihash = "0.7.0"
|
||||
tempfile = "2.2"
|
||||
serde_json = "1.0"
|
||||
multihash = { path = "../multihash" }
|
||||
|
@ -7,7 +7,7 @@ authors = ["pierre <pierre.krieger1708@gmail.com>"]
|
||||
bytes = "0.4"
|
||||
libp2p-core = { path = "../core" }
|
||||
log = "0.4.1"
|
||||
multiaddr = "0.3.0"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
multistream-select = { path = "../multistream-select" }
|
||||
futures = "0.1"
|
||||
parking_lot = "0.5"
|
||||
|
@ -9,7 +9,7 @@ futures = "0.1"
|
||||
libp2p-peerstore = { path = "../peerstore" }
|
||||
libp2p-core = { path = "../core" }
|
||||
log = "0.4"
|
||||
multiaddr = "0.3.0"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
protobuf = "2"
|
||||
rand = "0.4"
|
||||
tokio-io = "0.1"
|
||||
|
@ -7,6 +7,6 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
libp2p-core = { path = "../core" }
|
||||
log = "0.4.1"
|
||||
futures = "0.1"
|
||||
multiaddr = "0.3.0"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
tokio-core = "0.1"
|
||||
tokio-io = "0.1"
|
||||
|
@ -6,7 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
libp2p-core = { path = "../core" }
|
||||
futures = "0.1"
|
||||
multiaddr = "0.3.0"
|
||||
multiaddr = { path = "../multiaddr" }
|
||||
log = "0.4.1"
|
||||
rw-stream-sink = { path = "../rw-stream-sink" }
|
||||
tokio-io = "0.1"
|
||||
|
Reference in New Issue
Block a user