Generating Peer Id

Hello libp2p team,

I have been trying to make peer id to setup nodes and connect from bottom up. Where can I find the library to generate them?

1 Like

Hi @hskang9, thanks for making a thread for this - it’s a good question that’s not covered well enough in the docs yet.

In go-libp2p, you can generate a PeerId using the IdFromPublicKey helper method in go-libp2p-peer. To get the public key, you can use GenerateKeyPair to create a new key pair.

Once you have the PublicKey and PrivateKey generated, you can call IdFromPublicKey to generate the peer id. To save the key for future use, call MarshalPublicKey and MarshalPrivateKey to serialize the keys to byte slices, which you can then save to disk and deserialize using the UnmarshalPublicKey and UnmarshalPrivateKey.

Let me know if that helps and please keep the questions coming :slight_smile:

2 Likes

Thank you for your answer @yusef. I am intereseted in building javascript port of peerID generation. If I could get the specification for it like SLIP or paper, please let me know. I will be happy to contribute. Also, I am wondering if I can apply other id schemes to communicate between nodes. I feel like using Ethereum address scheme and apply it to chatting apps so that ethereum users can actually use chatting app built in libp2p with knowing their address in qr code.

Ah, I should have asked before assuming you were using go!

In javascript, you can use the js-peer-id library to generate a peer id.

By default, if you use PeerId.create it will generate an RSA key, but you can also generate your own key and pass it into the PeerId constructor.

It is possible to use secp256k1 keys to generate PeerIds if you use the js-libp2p-crypto-secp256k1 module. If you generate a keypair with that module and pass in the public and private keys to the PeerId constructor, it should generate a valid peer id.

If I recall correctly, Ethereum addresses are actually Keccak-256 hashes of secp256k1 public keys. Libp2p Peer Ids are also hashes of public keys, encoded as multihashes.

Unfortunately, Ethereum addresses drop the first 12 bytes of the Keccak-256 output to produce the address. This decision is completely baffling to me, since it makes verifying the address needlessly complicated and IMO saving 12 bytes per address isn’t worth it. At any rate, it means that an Ethereum address can’t be used directly as a libp2p Peer Id, since there’s no way to convert them back to a valid hash.

It is possible to obtain the public key for an Ethereum account if you have a message signed by the account (e.g. an Ethereum transaction). So in theory it would be possible to use the same keys for libp2p peer ids and Ethereum accounts, but you’d have to jump through some hoops to get the public key corresponding to the account. Once you have that, you could generate a multihash using any supported hash algorithm and pass the multihash and the public key into new PeerId.

Also, secp256k1 support is optional in libp2p and not enabled by default. This isn’t a problem if you’re building your own network, but I wanted to mention it because your Peer Ids will only validate with other peers that have enabled secp256k1 keys. So most libp2p nodes deployed today won’t see your nodes as valid.

Good luck & keep us posted!

1 Like

@hskang9 I was thinking a bit more about this after I posted and it occurred to me that while you can’t go directly from an Ethereum address to a valid libp2p Peer ID, you can go the other direction, if you create peer ids using secp256k1 keys and hash them using Keccak-256.

The basic process I’m thinking of would work like this:

  1. generate a secp256k1 keypair for your node
  2. hash the public key using Keccak-256
  3. use the encode method from js-multihash to turn the raw Keccak-256 hash into a multihash: const myMultihash = mh.encode(bufferWithRawHash, 'keccak-256') - this will return a new Buffer with the two-byte code for Keccak-256, followed by the raw hash value.
  4. Pass the multihash and the public and private keys from step 1 into the Peer Id constructor: const peerId = new PeerId(myMultihash, publicKey, privateKey)
  5. When you want to use the id as an Ethereum address, call peerId.toBytes() and drop the first 14 bytes (the 2-byte multihash code prefix, plus the 12 bytes that Ethereum drops when converting hashes to addresses)

I haven’t tried this myself, but it seems like it should work, assuming all peers for your app support secp256k1 keys. I’d be very interested in hearing if you manage to get it to work :slight_smile:

2 Likes

Thank you @yusef on my way to implement eth-multihash. I will try to put progress on a thread in Implementors and contributors category.

So far the github link is [here].(https://github.com/hskang9/eth-multihash)

Hello @yusef, As told in the basic process, I made test code to generate peer id based on ethereum address here. Can you check the process? I will try to come up with a method in peerid for this.

Hey, @hskang9, nice work :slight_smile: Everything looks good except the last part, where you convert from the PeerId buffer to ethereum address. I think that you want to take the last 20 bytes of the buffer to use as the address, but your code is taking the first twelve and prefixing it with two empty bytes.

I think it should work if you do something like:

const idBytes = peerId.toBytes();
const start = idBytes.length - 20;
const ethAccount = idBytes.slice(start);

You could test this by also generating an ethereum address from the private key directly and asserting that the resulting buffer is equal to the one derived from the peer id.

1 Like

Thank you @yusef, I will apply that. I have a question for peer-id nowadays. Will peer-id become as an identity platform which is also used to store assets? It would be great if peer-id can be connected with personalized storage with IPFS using eth address generated by it.

I think my question over here is relevant to this: Browser <-> go bootstrap and connect

Specifically my method of converting an ethereum ecdsa key to libp2p seems like it works in go, but not in js.

1 Like

Hi @yusuf can you please guide me how to do the same in Rust? I don’t seem to find MarshalPublickKey similar functions in Rust. Thanks.

You can use PublicKey::to_protobuf_encoding.

1 Like