Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add peer id inlining for small public keys #1230

Closed
wants to merge 3 commits into from

Conversation

AgeManning
Copy link
Contributor

This adds peer id inlining to match the behaviour of the go implementation.

Specifically, it addresses the rust side of libp2p/go-libp2p-daemon#137

A few points to note:

  • This changes the PeerId for secp256k1 and ed25519 keys
  • The forked multihash crate needed to be updated to include the identity hash. The go-implementation of this allows for up to 2^32 byte identity objects and this is mimicked here. The current rust-implementation relied on fixed hash sizes, where the identity "hash" can have sizes up to this length. There is now split logic in this implementation, between true hashes and the Identity type (which isn't great, feel free modify it, if there is a cleaner solution).

I've tested this with RSA, ed25519 and secp256k1 curves. This now connects and handshakes correctly with the go-daemon. May want to run further tests to ensure nothing upstream gets broken by these changes.

Verified

This commit was signed with the committer’s verified signature.
AgeManning Age Manning
Copy link
Contributor

@twittner twittner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if we want to support "identity" hashes for up to 4 GiB.

core/src/peer_id.rs Outdated Show resolved Hide resolved
core/src/peer_id.rs Show resolved Hide resolved
core/src/peer_id.rs Outdated Show resolved Hide resolved
misc/multihash/src/lib.rs Outdated Show resolved Hide resolved
misc/multihash/src/lib.rs Outdated Show resolved Hide resolved
misc/multihash/src/lib.rs Outdated Show resolved Hide resolved
misc/multihash/src/lib.rs Outdated Show resolved Hide resolved
@@ -60,6 +63,7 @@ impl Hash {
/// Get the hash length in bytes.
pub fn size(&self) -> u8 {
match self {
Hash::Identity => 42,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AgeManning
Copy link
Contributor Author

AgeManning commented Aug 28, 2019

I am not sure if we want to support "identity" hashes for up to 4 GiB.

I would tend to agree. The only reason I did this, was because the more featured go-implementation of multi-hashes do it. https://github.com/multiformats/go-multihash/blob/master/multihash.go#L258

It shouldnt affect libp2p really as we are only using this for known-sized keys. Happy to reduce this, am indifferent to the size of identity hashes to support.

Verified

This commit was signed with the committer’s verified signature.
AgeManning Age Manning
@tomaka
Copy link
Member

tomaka commented Sep 2, 2019

The PR looks good. The only problem with it would break any existing network using rust-libp2p (which includes Substrate and Polkadot 😅 ).

If you don't mind, I think we should for now accept both hashed and non-hashed keys, but only send out our key hashed. In other words, from_bytes should accept both Identity and SHA2256, but from_public_key should be left untouched for now. Then it a later release, we change from_public_key.

The disadvantage of doing so is that during this transition period, two different PeerIds could represent the same node on the network. This isn't great, but I also don't really see any way this could be attacked.

@AgeManning
Copy link
Contributor Author

This originally came from compatibility issues with other implementations.

If we broadcast a hashed version, the other implementations will reject that multiaddr when trying to connect on that multiaddr, which was the initial problem.

It sounds like I am the only one really with this issue, and our client uses a forked version of rust-libp2p where I've already added these changes. So perhaps if this is not causing any issues for anyone else at the moment, perhaps we leave this PR, until a later release?

However if you want these in now, I'm happy to make the changes you suggest.

@tomaka
Copy link
Member

tomaka commented Sep 2, 2019

So perhaps if this is not causing any issues for anyone else at the moment, perhaps we leave this PR, until a later release?

We definitely want this in master sooner or later.

Also, apparently js-libp2p introduced the inlining between 0.12.2 and 0.12.3, which broke compatibility with rust-libp2p until this PR is in.

@AgeManning
Copy link
Contributor Author

AgeManning commented Sep 2, 2019

Also, apparently js-libp2p introduced the inlining between 0.12.2 and 0.12.3, which broke compatibility with rust-libp2p until this PR is in.

Yeah, I usually use "the go implementation" as a synonym of "the libp2p-spec" as most of the specs are based off this and anything mismatching, we should consult the go-impl, so not surprising js-libp2p also matches this.

I'll leave this PR in your hands and let you merge/modify next release/when dependent projects are ready.

@tomaka
Copy link
Member

tomaka commented Oct 22, 2019

Small note: let's keep that PR open in order to not forget to finish this change.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
@tomaka
Copy link
Member

tomaka commented Jan 10, 2020

I merged master on top of this PR and added a comment.

@tomaka tomaka requested a review from twittner January 10, 2020 15:10
@tomaka
Copy link
Member

tomaka commented Jan 10, 2020

Tested on Polkadot. Unfortunately doesn't work, and nodes refuse our connections. Will need to figure out what is happening before merging.

@tomaka
Copy link
Member

tomaka commented Jan 10, 2020

The problem comes from here:

let peer = pubkey.clone().into_peer_id();

At the end of the secio handshake, a node on an older version of libp2p will generate a SHA256 PeerId for the remote and compare it against the expected identity PeerId, which will fail comparison.

Similarly, a node on a newer version will generate an identity PeerId at the end of the secio handshake, which will fail comparison with the expected SHA256 PeerId.

I'm not sure how to fix that. Maybe we can make id(pubkey) compare equal to sha256(hashed_pubkey)?

@twittner
Copy link
Contributor

twittner commented Jan 14, 2020

Maybe we can make id(pubkey) compare equal to sha256(hashed_pubkey)?

I think this points into the right direction. The digest of an identity hash equals the original input, so we can always hash the digest with a different algorithm and compare the result with the other one at hand:

impl PartialEq<PeerId> for PeerId {
    fn eq(&self, other: &PeerId) -> bool {
        match (self.multihash.algorithm(), other.multihash.algorithm()) {
            (a, b) if a == b => {
                self.multihash.digest() == other.multihash.digest()
            }
            (multihash::Hash::Identity, b) => {
                let x = multihash::encode(b, self.multihash.digest());
                x.as_ref() == Ok(&other.multihash)
            }
            (a, multihash::Hash::Identity) => {
                let x = multihash::encode(a, other.multihash.digest());
                x.as_ref() == Ok(&self.multihash)
            }
            _ => false
        }
    }
}

Edit: A property test to add:

    #[test]
    fn equals() {
        use multihash::Hash;
        use quickcheck::{Arbitrary, Gen};

        #[derive(Debug, Clone, PartialEq, Eq)]
        struct HashAlgo(Hash);

        impl Arbitrary for HashAlgo {
            fn arbitrary<G: Gen>(g: &mut G) -> Self {
                match g.next_u32() % 16 { // make Hash::Identity more likely
                    0 => HashAlgo(Hash::SHA1),
                    1 => HashAlgo(Hash::SHA2256),
                    2 => HashAlgo(Hash::SHA2512),
                    3 => HashAlgo(Hash::SHA3512),
                    4 => HashAlgo(Hash::SHA3256),
                    5 => HashAlgo(Hash::Keccak256),
                    6 => HashAlgo(Hash::Blake2b512),
                    7 => HashAlgo(Hash::Blake2s256),
                    _ => HashAlgo(Hash::Identity)
                }
            }
        }

        fn property(data: Vec<u8>, algo1: HashAlgo, algo2: HashAlgo) -> bool {
            let a = PeerId { multihash: multihash::encode(algo1.0, &data).unwrap() };
            let b = PeerId { multihash: multihash::encode(algo2.0, &data).unwrap() };

            if algo1 == algo2 || algo1.0 == Hash::Identity || algo2.0 == Hash::Identity {
                a == b
            } else {
                a != b
            }
        }

        quickcheck::quickcheck(property as fn(Vec<u8>, HashAlgo, HashAlgo) -> bool)
    }

@tomaka
Copy link
Member

tomaka commented Jan 14, 2020

Maybe we can make id(pubkey) compare equal to sha256(hashed_pubkey)?

One thing to not forget is that we derive Hash on PeerId, and the hash of peerid(identity(pub)) has to be the same as the one of peerid(sha2(hashed_pub)).

(nothing unfixable, but I thought I'd add a note to not forget)

@rklaehn
Copy link
Contributor

rklaehn commented Jan 25, 2020

Any chance we can get this merged behind a disabled feature flag? It is essential for interop with go-libp2p and presumably also js-libp2p.

I tried to get the chat example to work with go-ipfs, and one problem is that the peer id on the rust side is different than on the go side. Working from this branch, the peer id is identical.

@koivunej thanks for the hint

@tomaka
Copy link
Member

tomaka commented Jan 27, 2020

Any chance we can get this merged behind a disabled feature flag? It is essential for interop with go-libp2p and presumably also js-libp2p.

This PR needs the changes so that PeerIds compare equal across identity and hashed versions.
I wanted to do the changes, but I haven't had the time to yet.

@tomaka
Copy link
Member

tomaka commented Jan 29, 2020

Finished in #1413

@tomaka tomaka closed this Jan 29, 2020
@AgeManning AgeManning deleted the peerid-inlining branch July 1, 2020 07:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants