- PVSM.RU - https://www.pvsm.ru -
Недавно в IPFS добавили поддержу тривиального (identity) хеша [1]. В своей статье я расскажу о нём и покажу как его можно использовать.
Напомню: InterPlanetary File System — это новая децентрализованная сеть обмена файлами (HTTP-сервер, Content Delivery Network [2]). О ней я начал рассказ в статье "Межпланетная файловая система IPFS" [3].
Обычно при хешировании проходя через хеш-функцию [4] данные необратимо "сжимаются" и в результате получается короткий идентификатор. Этот идентификатор позволяет найти данные в сети и проверить их целостность.
Тривиальный хеш [5] — это сами данные. Данные никак не изменяются и соответственно размер "хеша" равен размеру данных.
Тривиальный хеш выполняет ту же функцию что и Data: URL [6]. Идентификатор контента в этом случае содержит сами данные вместо хеша. Это позволяет вкладывать дочерние блоки в родительский делая их доступными сразу после получения родительского. Также можно включать данные сайта непосредственно в DNS запись.
Для примера закодируем текстовую строку "Привет мир" в идентификатор контета [7](CID) с тривиальным хешем.
[8]
Структура идентификатора:
[префикс основания][varint версия CID][varint тип контента][varint ID хеша][varint длинна хеша][хеш]
Начнём с конца.
Тривиальный хеш в нашем случае это сама строка. Переведём её в HEX [9].
"Привет мир" = 0x"D09F D180 D0B8 D0B2 D0B5 D182 20 D0BC D0B8 D180"
Это HEX этой строки в кодировке utf-8 [10]. Но чтоб браузер знал наверняка что это utf-8 строка добавим к ней в начале: 0xEFBBBF
. Это маркер последовательности байтов [11](BOM).
0x"EFBBBF D09F D180 D0B8 D0B2 D0B5 D182 20 D0BC D0B8 D180"
Теперь мы можем посчитать длину "хеша". Каждые два символа HEX это один байт. Соответственно длинна получившейся строки 22 байта. В HEX это будет 0x16
.
Добавляем 0x16
в начало строки.
0x"16 EFBBBF D09F D180 D0B8 D0B2 D0B5 D182 20 D0BC D0B8 D180"
Теперь нам нужен идентификатор хеша. Тривиальный хеш или identity в таблице хешей [12] имеет идентификатор 0x00
.
Добавляем 0x00
в начало строки.
0x"00 16 EFBBBF D09F D180 D0B8 D0B2 D0B5 D182 20 D0BC D0B8 D180"
Это уже мультихеш [13] часть идентификатора можно перекодировать HEX в Base58 и мультихеш готов. Но ipfs его не распознает вне идентификатора контента(CID).
Идём дальше.
Теперь заглянем в таблицу multicodec [14] для того чтобы получить тип контента. В нашем случае это сырые(raw) данные и идентификатор соответственно 0x55
.
Добавляем 0x55
в начало строки.
0x"55 00 16 EFBBBF D09F D180 D0B8 D0B2 D0B5 D182 20 D0BC D0B8 D180"
Мы кодируем в формат первой версии идентификатора контента [15]. Поэтому добавляем 0x01.
Добавляем 0x01
в начало строки.
0x"01 55 00 16 EFBBBF D09F D180 D0B8 D0B2 D0B5 D182 20 D0BC D0B8 D180"
И так мы уже на финишной прямой.
Он указывает какой вариант кодирования бинарных данных в текст использован.
Мы можем использовать напрямую HEX сроку добавив в начале префикс основания HEX символ "F"
F01550016EFBBBFD09FD180D0B8D0B2D0B5D18220D0BCD0B8D180
Мы получили HEX идентификатор контента который содержит utf-8 строку: "Привет мир"
Тестируем: /ipfs/F01550016EFBBBFD09FD180D0B8D0B2D0B5D18220D0BCD0B8D180 [16]
Base58btc будет по короче поэтому
Нашу HEX строку мы переводим в base58btc. Можно воспользоваться онлайн конвертером [17].
0x"01 55 00 16 EFBBBF D09F D180 D0B8 D0B2 D0B5 D182 20 D0BC D0B8 D180" = "3NDGAEgXCxbPucFFCQc9s5ScqZjqVFNr56P" (base58btc)
Добавляем в начале к полученной строке символ префикс основания base58btc "z"
z3NDGAEgXCxbPucFFCQc9s5ScqZjqVFNr56P
Мы получили base58btc идентификатор контента который содержит utf-8 строку: "Привет мир"
Тестируем: /ipfs/z3NDGAEgXCxbPucFFCQc9s5ScqZjqVFNr56P [18]
Текст это хорошо но для того чтоб закодировать HTML страницу нам надо вложить её данные в DAG блок директории.
Вот наш HTML:
<b><i><u>Привет мир</u></i></b>
Аналогично по инструкции выше получаем идентификатор контента в base58btc для этого текста:
zeExnPvBXdTRwCBhfkJ1fHFDaXpdW4ghvQjfaCRHYxtQnd3H4w1MPbLczSqyCqVo
Теперь пишем JSON файл:
{
"links": [{
"Cid": {
"/": "zeExnPvBXdTRwCBhfkJ1fHFDaXpdW4ghvQjfaCRHYxtQnd3H4w1MPbLczSqyCqVo"
},
"Name": "index.html"
}],
"data": "CAE="
}
Командой ipfs dag put -f"protobuf"
конвертируем JSON в DAG блок через IPFS.
Я получил мультихеш: QmXXixn4rCzGguhxQPjXQ8Mr5rdqwZfJTKkeB6DfZLt8EZ
На этом этапе мы получили блок в котором каталог с одним файлом вписанным в блок.
Далее используя этот мультихеш выгружаем готовый блок
ipfs block get QmXXixn4rCzGguhxQPjXQ8Mr5rdqwZfJTKkeB6DfZLt8EZ > block.dag
Переводим содержимое block.dag в HEX:
0x"123F0A2F0155002BEFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E120A696E6465782E68746D6C18000A020801"
Добавляем:
0x"01 70 00 45 123F0A2F0155202BEFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E120A696E6465782E68746D6C18200A020801"
Конвертируем в Base58btc и добавляем префикс "z"
z6S3Z3W1zuRxio8AJC41jRTdyU9pZWnU6sNbvyGyypEdD8JVNdW42ZmGYWKWGbVDELLvJNWcMspaZMUPZKt7JQmhdyXCqq7j37GL
Таким образом мы получили идентификатор контента с каталогом в котором html страница index.html с текстом "Привет мир".
Тестируем: /ipfs/z6S3Z3W1zuRxio8AJC41jRTdyU9pZWnU6sNbvyGyypEdD8JVNdW42ZmGYWKWGbVDELLvJNWcMspaZMUPZKt7JQmhdyXCqq7j37GL/ [19]
Далее этот хеш также можно вложить в другой блок либо записать в DNS dnslink запись. Так в одном блоке можно уместить небольшой простенький сайт.
DAG блок можно также собрать в ручную. DAG блок это данные в формате Protocol Buffers [20]. Верхний слой это merkledag.proto [21] у которого в Data находится unixfs.proto [22].
Любой протобуфер начинается с varint идентификатора поля. Часто идентификатор занимает один байт так как его общее значение меньше 0x80. В нашем случае первый байт 0x12. Младшие 3 бита этого поля это тип. Остальное ID заданный в proto файле.
Расшифровываем идентификатор:
0x12 & 0x07 = 2 (Тип: Length-delimited)
0x12 >> 3 = 2 (ID: 2)
Length-delimited означает что дальше следует varint размер поля в байтах и непосредственно его содержимое. Этот тип используется как для различных вложенных структур так и сырых данных (string, bytes, embedded messages, packed repeated fields). Что в нём определяет уже proto файл.
Расшифруем идентификатор другого типа:
0x18 & 0x07 = 0 (Тип: Varint)
0x12 >> 3 = 3 (ID: 3)
Varint означает что дальше следует сразу значение в varint. Этот контейнер используется для записи многих типов значений (int32, int64, uint32, uint64, sint32, sint64, bool, enum). Что в нём также определяет proto файл.
Для разбора блока можно воспользоваться сайтом который автоматически разберёт любой Protocol Buffer [23] без использования proto файлов.
0x"123F0A2F0155002BEFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E120A696E6465782E68746D6C18000A020801"
Разбираем блок и сопоставляем идентификаторам из proto файлов.
// 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;
}
12 (Тип: 2 (Length-delimited). ID: 2 (PBLink PBNode.Links (merkledag.proto)))
3F (Размер: 63 байта)
0A (Тип: 2 (Length-delimited). ID: 1 (PBLink.Hash))
2F (Размер: 47 байта)
01 55 00 2B (CIDv1 Raw Identity 43 байта)
EFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E
= "<b><i><u>Привет мир</u></i></b>"
12 (Тип: 2 (Length-delimited). ID: 2 (PBLink.Name))
0A (Размер: 10 байт)
696E6465782E68746D6C = "index.html"
18 (Тип: 0 (Varint). ID: 3 (PBLink.Size))
00 (Значение: 0)
0A (Тип: 2 (Length-delimited). ID: 1 (PBNode.Data = Data (unixfs.proto)))
02 (Размер: 2 байт)
08 (Тип: 0 (Varint). ID: 1 (Data.Type))
01 (1 == Data.DataType.Directory)
Соответственно блок с двумя фалами будет выглядеть так:
12 (Тип: 2 (Length-delimited). ID: 2 (PBLink PBNode.Links (merkledag.proto)))
3B (Размер: 59 байт)
0A (Тип: 2 (Length-delimited). ID: 1 (PBLink.Hash))
2F (Размер: 47 байта)
01 55 00 2B (CIDv1 Raw Identity 43 байта)
EFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E
= "<b><i><u>Привет мир</u></i></b>"
12 (Тип: 2 (Length-delimited). ID: 2 (PBLink.Name))
06 (Размер: 6 байт)
312E68746D6C = "1.html"
18 (Тип: 0 (Varint). ID: 3 (PBLink.Size))
00 (Значение: 0)
12 (Тип: 2 (Length-delimited). ID: 2 (PBLink PBNode.Links))
3B (Размер: 59 байт)
0A (Тип: 2 (Length-delimited). ID: 1 (PBLink.Hash))
2F (Размер: 47 байта)
01 55 00 2B (CIDv1 Raw Identity 43 байта)
EFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E
= "<b><i><u>Привет мир</u></i></b>"
12 (Тип: 2 (Length-delimited). ID: 2 (PBLink.Name))
06 (Размер: 6 байт)
322E68746D6C = "2.html"
18 (Тип: 0 (Varint). ID: 3 (PBLink.Size))
00 (Значение: 0)
0A (Тип: 2 (Length-delimited). ID: 1 (PBNode.Data = Data(unixfs.proto)))
02 (Размер: 2 байт)
08 (Тип: 0 (Varint). ID: 1 (Data.Type))
01 (1 == Data.DataType.Directory)
То есть поле PBNode.Links(0x12) повторяется столько раз сколько файлов надо поместить в блок.
Для проверки добавим в начале "F 01 70 00" (HEX CIDv1 DAG Identity) и размер DAG блока "7E"(126 байт)
F 01 70 00 7E
12 3B 0A 2F 01 55 00 2B EFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E 12 06 312E68746D6C 18 00
12 3B 0A 2F 01 55 00 2B EFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E 12 06 322E68746D6C 18 00
0A 02 08 01
Надеюсь достаточно дал информации для того чтобы возможно было реализовать создание блоков и идентификаторов.
Автор: ivan386
Источник [25]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/detsentralizovanny-e-seti/292350
Ссылки в тексте:
[1] тривиального (identity) хеша: https://github.com/ipfs/go-ipfs/issues/4697
[2] Content Delivery Network: https://ru.wikipedia.org/wiki/Content_Delivery_Network
[3] "Межпланетная файловая система IPFS": https://habrahabr.ru/post/314768/
[4] хеш-функцию: https://ru.wikipedia.org/wiki/%D0%A5%D0%B5%D1%88%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5
[5] Тривиальный хеш: https://en.wikipedia.org/wiki/Hash_function#Trivial_hash_function
[6] Data: URL: https://ru.wikipedia.org/wiki/Data:_URL
[7] идентификатор контета: https://github.com/ipld/cid
[8] Image: https://habrahabr.ru/post/423073/
[9] HEX: https://ru.wikipedia.org/wiki/%D0%A8%D0%B5%D1%81%D1%82%D0%BD%D0%B0%D0%B4%D1%86%D0%B0%D1%82%D0%B5%D1%80%D0%B8%D1%87%D0%BD%D0%B0%D1%8F_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F
[10] utf-8: https://ru.wikipedia.org/wiki/UTF-8
[11] маркер последовательности байтов: https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D1%80%D0%BA%D0%B5%D1%80_%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8_%D0%B1%D0%B0%D0%B9%D1%82%D0%BE%D0%B2
[12] таблице хешей: https://github.com/multiformats/multihash/blob/master/hashtable.csv
[13] мультихеш: https://github.com/multiformats/multihash
[14] multicodec: https://github.com/multiformats/multicodec/blob/master/table.csv
[15] первой версии идентификатора контента: https://github.com/ipld/cid#cidv1
[16] /ipfs/F01550016EFBBBFD09FD180D0B8D0B2D0B5D18220D0BCD0B8D180: https://ipfs.io/ipfs/F01550016EFBBBFD09FD180D0B8D0B2D0B5D18220D0BCD0B8D180
[17] онлайн конвертером: http://lenschulwitz.com/base58
[18] /ipfs/z3NDGAEgXCxbPucFFCQc9s5ScqZjqVFNr56P: https://ipfs.io/ipfs/z3NDGAEgXCxbPucFFCQc9s5ScqZjqVFNr56P
[19] /ipfs/z6S3Z3W1zuRxio8AJC41jRTdyU9pZWnU6sNbvyGyypEdD8JVNdW42ZmGYWKWGbVDELLvJNWcMspaZMUPZKt7JQmhdyXCqq7j37GL/: https://ipfs.io/ipfs/z6S3Z3W1zuRxio8AJC41jRTdyU9pZWnU6sNbvyGyypEdD8JVNdW42ZmGYWKWGbVDELLvJNWcMspaZMUPZKt7JQmhdyXCqq7j37GL/
[20] Protocol Buffers: https://developers.google.com/protocol-buffers/docs/encoding
[21] merkledag.proto: https://github.com/ipfs/go-merkledag/blob/2a2b14cb9dbd033a684335ad27af638c30178bd3/pb/merkledag.proto
[22] unixfs.proto: https://github.com/ipfs/go-unixfs/blob/08716b95ba554c9494d01212fabe6bf9da678f6b/pb/unixfs.proto
[23] сайтом который автоматически разберёт любой Protocol Buffer: https://protogen.marcgravell.com/decode
[24] /ipfs/F0170007E123B0A2F0155002BEFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E1206312E68746D6C1800123B0A2F0155002BEFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E1206322E68746D6C18000A020801: https://ipfs.io/ipfs/F0170007E123B0A2F0155002BEFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E1206312E68746D6C1800123B0A2F0155002BEFBBBF3C623E3C693E3C753ED09FD180D0B8D0B2D0B5D18220D0BCD0B8D1803C2F753E3C2F693E3C2F623E1206322E68746D6C18000A020801
[25] Источник: https://habr.com/post/423073/?utm_campaign=423073
Нажмите здесь для печати.