[solved] How do I get the CIDv1 peer id of a key pair?

The short go file at ipfs-key/main.go at master · whyrusleeping/ipfs-key · GitHub generates keypairs and prints the peer ID in the 12D3KooW… format. I’d like to obtain the keys in the k51qzi5uqu5d… format.

I can obtain the k51… format using ipfs key import some-name private-key-file followed by ipfs key list -l, but I’m looking for a way to obtain the k51… in go, or at least without using an external process.

After a lot of searching, I found https://cid.ipfs.io/ which shows that, after decoding, that site shows some very similar info for the following two hashes (obtained using the standalone key generator and ipfs key import):

12D3KooWPVYKwhPsyD1kpaaVNvJoqd4tdZR4VgxVmo9NSzWVQjCj
k51qzi5uqu5dl8wlbw09j1cpr1i456yb3mv0r5wnpx3wz6qq8pnppkvgqdepc8

Once converted to CIDv1 base32, they become:

bafyaajaiaejcbszrcftl53w5t63k6tgiyk7uvrdr3to6pjy76azeib2h4ccm6dgi
bafzaajaiaejcbszrcftl53w5t63k6tgiyk7uvrdr3to6pjy76azeib2h4ccm6dgi

Aside from the bafy/bafz (which seems to indicate the codec 0x70 vs 0x72), there’s no difference.

I know how to convert the CIDv1 base32 bafy or bafz to a CIDv1 base36 k50 / k51 using ipfs cid format -b base36 baf…, and from what I understand it’s a simple base conversion.

I’m still stumped about how I would get the CIDv1 base32 format from the 12D3KooW…, it seems that the multicodec identifier 0x72 gets inserted somehow into the mix (because the 12D… hash has everything implicit) and there’s something related to protobuf, but I haven’t used that.

So I guess my question is, “how do I protobuf the multicodec :-p to get from 12D… to one of the CIDv1 encodings?”.

Thanks a lot :slight_smile:

3 Likes

You’re right, CIDv0 are all-implicit, meaning CID begging with Q or 1 (identity) will always be base58btc encoded.
In case of Q CIDv0, the hash algorithm will be sha2-256, otherwise for 1 CIDv0, this is indeed an identity multihash.
So you can simply decode the entire 12D3KooWPVYKwhPsyD1kpaaVNvJoqd4tdZR4VgxVmo9NSzWVQjCj as base58btc.
It will give you the following multihash (as hex): 002408011220CB311166BEEEDD9FB6AF4CC8C2BF4AC471DCDDE7A71FF032440747E084CF0CC8
We have:

  • 00 for the hash function, here: identity
  • 24 for the length in bytes (0x24 = 36 bytes = 288 bits) of the digest
  • The following is the identity digest.

You can now create the new identity using the CIDv1 you want.
For instance, we’ll create the CIDv1 base36 you asked for:

First, we add the multicodec prefix libp2p-key (0x72) and the CIDv1 prefix (0x01).
Then, we encode the whole 0172002408011220CB311166BEEEDD9FB6AF4CC8C2BF4AC471DCDDE7A71FF032440747E084CF0CC8 as base36.
We get this: 51qzi5uqu50cgk08ogw8k8ck8swgoo4kww0k08k4gscwcsccg4sscog0ssw0c
We add the k base36 multibase-prefix, and we get:
k51qzi5uqu50cgk08ogw8k8ck8swgoo4kww0k08k4gscwcsccg4sscog0ssw0c

And there you go! (but you could also use the multibase/multihash/multicodec or CID lib).
(see GitHub - multiformats/cid: Self-describing content-addressed identifiers for distributed systems for more info)

3 Likes

Thanks a lot! Exactly what I was looking for and couldn’t infer by guesswork :slight_smile:

1 Like

Were you able to get this to work in Go? Could you show how you did it? I am stuck on this same problem. Thanks!

EDIT* I was able to figure this out! Just sharing incase anyone else in the future sees this thread. First I create a keyencoder object and specify the k option:
keyEnc, err := ke.KeyEncoderFromString("k")
*Include ke "github.com/ipfs/go-ipfs/core/commands/keyencode" in the imports section

Then after generating a ed25519 keypair using:
sk, pk, err := ic.GenerateKeyPair(ic.Ed25519, 256)
*Include ic "github.com/libp2p/go-libp2p-core/crypto" in the imports section

I grab the peerID:
pidpk, err := peer.IDFromPublicKey(pk)
*include peer "github.com/libp2p/go-libp2p-core/peer" in the imports section

I formated it using:
peerID := keyEnc.FormatID(pidpk)

Then when you print you will get 'k51…"

After going deep down this rabbit hole, still can’t figure out how to create the digest from a secp256k1 public key to use as the identity hash.

I can see from importing the output ID generated from importing it into go-ipfs that it includes the public key, which is:
03E1DD7882E3AD98145004DF308C4F2E4C44845C56C91A9B300EB8144B33766DFB

So where does this preamble come from: 08021221 ?

Got it.

Seriously though if there’s a function embedded in one of the many libraries that already deals with this conversion, someone give me a clue. If not, I’ll definitely submit a pull because going from a keypair to an IPNS subdomain addressable hash is super important.