如何在IPFS中重新创建多哈希的哈希摘要

时间:2018-08-02 13:50:26

标签: ipfs

假设我要像这样向IPFS添加数据:

$ echo Hello World | ipfs add

这将给我QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u-一个CID,它是Base58编码的Multihash。

将其转换为Base16,告诉我IPFS添加的哈希摘要是SHA2-256哈希:

12 - 20 - 74410577111096cd817a3faed78630f2245636beded412d3b212a2e09ba593ca
<hash-type> - <hash-length> - <hash-digest>

我知道IPFS不仅会散列数据,而且实际上会先将其序列化为Unixfs protobuf,然后再将其放入dag中。

我想揭开神秘面纱,如何到达74410577111096cd817a3faed78630f2245636beded412d3b212a2e09ba593ca,但我真的不确定如何获取已创建的dag,该dag将Unixfs protobuf与数据一起保存。

例如,我可以将序列化的原始数据写入磁盘并使用protobuf解码器进行检查:

$ ipfs block get QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u > /tmp/block.raw
$ protoc --decode_raw < /tmp/block.raw

这将为我提供可读格式的序列化数据:

1 {
  1: 2
  2: "Hello World\n"
  3: 12
}

但是,通过SHA-256传递给我的哈希仍然不同,这是有道理的,因为IPFS将protobuf置于dag中并对其进行多哈希处理。

$ protoc --decode_raw < /tmp/block.raw | shasum -a 256

因此,我决定弄清楚如何掌握该dag节点,自己对其进行散列以得到我要查找的散列。

我希望使用ipfs dag get QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u可以给我一个多哈希,然后可以对其进行解码,但事实证明,它会返回一些我不知道如何检查的其他数据哈希:

$ ipfs dag get QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u
$ {"data":"CAISDEhlbGxvIFdvcmxkChgM","links":[]}

关于如何从此处解码data的任何想法?

更新

data是原始数据的Base64表示形式:https://github.com/ipfs/go-ipfs/issues/4115

3 个答案:

答案 0 :(得分:3)

您要查找的哈希是ipfs block get QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u输出的哈希。 IPFS对编码值进行哈希处理。

代替运行:

protoc --decode_raw < /tmp/block.raw | shasum -a 256

只需运行:

shasum -a 256 < /tmp/block.raw

  

但事实证明,它还会返回一些我不知道如何检查的其他数据哈希

那是因为我们当前在protobuf内部使用protobuf。外层原生虫的结构为{Data: DATA, Links: [{Name: ..., Size: ..., Hash: ...}]}

在:

1 {
  1: 2
  2: "Hello World\n"
  3: 12
}

1 { ... }部分是外部probubuf的 Data 字段。但是,protoc --decode_raw *recursively* decodes this object so it decodes the“数据”字段为:

  • 字段1(数据类型):2(文件)
  • 字段2(数据):“ Hello World \ n”
  • 字段3(文件大小):12(字节)

对于上下文,相关的protobuf定义为:

外部:

// An IPFS MerkleDAG Link
message PBLink {

  // multihash of the target object
  optional bytes Hash = 1;

  // utf string name. should be unique per object
  optional string Name = 2;

  // cumulative size of target object
  optional uint64 Tsize = 3;
}

// An IPFS MerkleDAG Node
message PBNode {

  // refs to other objects
  repeated PBLink Links = 2;

  // opaque user data
  optional bytes Data = 1;
}

内部:

message Data {
    enum DataType {
        Raw = 0;
        Directory = 1;
        File = 2;
        Metadata = 3;
        Symlink = 4;
        HAMTShard = 5;
    }

    required DataType Type = 1;
    optional bytes Data = 2;
    optional uint64 filesize = 3;
    repeated uint64 blocksizes = 4;

    optional uint64 hashType = 5;
    optional uint64 fanout = 6;
}

message Metadata {
    optional string MimeType = 1;
}

答案 1 :(得分:1)

我不确定该编码是什么,但是您可以在js-ipfs中解组dag数据字段:

const IPFS = require('ipfs')
const Unixfs = require('ipfs-unixfs')

const ipfs = new IPFS

ipfs.dag.get('QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u', (err, d) => {
  console.log(Unixfs.unmarshal(d.value.data).data.toString()))
  // prints Hello World
})

答案 2 :(得分:0)

根据史蒂文的回答,使用protobuf是有效的。这是我的方法的完整代码。

ipfs.proto

syntax = "proto3";

message PBNode {
bytes Data = 1;
}

message PBLink {
bytes Hash = 1;
string Name = 2;
uint64 Tsize = 3;
}

message Data {
enum DataType {
    Raw = 0;
    Directory = 1;
    File = 2;
    Metadata = 3;
    Symlink = 4;
    HAMTShard = 5;
}
DataType Type = 1;
bytes Data = 2;
}

cid.js

const mh = require('multihashes');
const axios = require('axios');
const crypto = require('crypto');
const protobuf = require("protobufjs");
const IPFS = protobuf.loadSync('./ipfs.proto').lookupType('PBNode');

class CID {
/**
* convert IPFS multihash to sha2-256 hash string
* @param {string} multihash
* @param {boolean} prefix
* @returns {string} sha2-256 hash string starting with 0x
*/
static toHash(multihash, prefix = false) {
    return prefix ? '0x' : ''
    + mh.decode(mh.fromB58String(multihash)).digest.toString('hex')
}

/**
* convert sha2-256 hash string to IPFS multihash
* @param {string} str
* @returns {string} IPFS multihash starting with Qm
*/
static fromHash(str) {
    str = str.startsWith('0x') ? str.slice(2) : str;
    return mh.toB58String(mh.encode(Buffer.from(str, 'hex'), 'sha2-256'))
}

/**
* hash the buffer and get the SHA256 result compatible with IPFS multihash
* @param {Buffer} buf
* @returns {string}
*/
static hash(buf) {
    const r = IPFS.encode({
    Data: {
        Type: 2,
        Data: buf,
        filesize: buf.length
    }

    }).finish();
    return crypto.createHash('sha256').update(r).digest('hex');
}
}

async function ipfsGet(cid) {
const x = await axios.get(`http://your.address.xxx/ipfs/${cid}`, {
    responseType: 'arraybuffer'
});
return Buffer.from(x.data);
}

const r = "QmfQj4DUWEudeFdWKVzPaTbYimdYzsp14DZX1VLV1BbtdN";
const hashFromCID = CID.toHash(r);
console.log(hashFromCID);
ipfsGet(r).then(buf => {
const hashCalculated = CID.hash(buf);
console.log(hashCalculated);
console.log(hashCalculated === hashFromCID);
console.log(CID.fromHash(hashCalculated) === r)
});

module.exports = CID;