Integrate the multiformats crates (#231)

* Remove the multihash patch

* Integrate the multiformats crates

* Fix not compiling on emscripten
This commit is contained in:
Pierre Krieger
2018-06-01 10:10:00 +02:00
committed by GitHub
parent 9e2dfd3fa5
commit 57c3103f78
40 changed files with 2495 additions and 18 deletions

View File

@ -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
View File

@ -0,0 +1,3 @@
target
Cargo.lock
*.bk

35
cid/.travis.yml Normal file
View 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
View 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
View File

@ -0,0 +1,64 @@
# rust-cid
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-ipld-blue.svg?style=flat-square)](https://github.com/ipld/ipld)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs)
[![Travis CI](https://img.shields.io/travis/ipld/rust-cid.svg?style=flat-square&branch=master)](https://travis-ci.org/ipld/rust-cid)
[![](https://img.shields.io/badge/rust-docs-blue.svg?style=flat-square)](https://docs.rs/crate/cid)
[![crates.io](https://img.shields.io/badge/crates.io-v0.1.0-orange.svg?style=flat-square )](https://crates.io/crates/cid)
[![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](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
View 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
View 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
View 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
View 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
View 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
View 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);
}
}

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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" }

View File

@ -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"

View File

@ -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
View File

@ -0,0 +1,3 @@
target
Cargo.lock
*.rs.bk

31
multiaddr/.travis.yml Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,63 @@
# rust-multiaddr
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs)
[![Travis CI](https://img.shields.io/travis/multiformats/rust-multiaddr.svg?style=flat-square&branch=master)](https://travis-ci.org/multiformats/rust-multiaddr)
[![codecov.io](https://img.shields.io/codecov/c/github/multiformats/rust-multiaddr.svg?style=flat-square&branch=master)](https://codecov.io/github/multiformats/rust-multiaddr?branch=master)
[![](https://img.shields.io/badge/rust-docs-blue.svg?style=flat-square)](https://docs.rs/crate/multiaddr)
[![crates.io](https://img.shields.io/badge/crates.io-v0.2.0-orange.svg?style=flat-square )](https://crates.io/crates/multiaddr)
[![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
target
Cargo.lock
*.bk

34
multihash/.travis.yml Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,68 @@
# rust-multihash
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs)
[![Travis CI](https://img.shields.io/travis/multiformats/rust-multihash.svg?style=flat-square&branch=master)](https://travis-ci.org/multiformats/rust-multihash)
[![codecov.io](https://img.shields.io/codecov/c/github/multiformats/rust-multihash.svg?style=flat-square&branch=master)](https://codecov.io/github/multiformats/rust-multihash?branch=master)
[![](https://img.shields.io/badge/rust-docs-blue.svg?style=flat-square)](https://docs.rs/multihash/)
[![crates.io](https://img.shields.io/badge/crates.io-v0.4.0-orange.svg?style=flat-square )](https://crates.io/crates/multihash)
[![](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](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
View 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
View 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
View 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
View 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");
}

View File

@ -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" }

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"