- PVSM.RU - https://www.pvsm.ru -

Хватит качать и хранить нули

Скачиваемый файл заполнен нулями

Нуль-блоками я называю блоки(части файла) заполненные нулевыми байтами. Можно заранее посчитать их хеши и не запрашивать эти блоки у источников а сразу помечать их уже загруженными.

Нуль-блоки не надо хранить на диске. Благодаря sparse флагу операционная система просто помечает этот участок файла как заполненный нулями и не хранит эти нули на диске.

Свойства файла заполненного нулями

Файл размером 16MB занимает на диске 4KB

Также показывая что участки скачиваемого файла заполнены нулями можно мотивировать пользователя отказаться от скачивания и распространения битого файла. В моей версии Shareaza эти участки помечаются красной полосой сверху на полосе прогресса загрузки файла.

Откуда берутся нуль-блоки в файле

  1. Раздающий не дождался полного скачивания и проверки файла и выложил неполный(partial) файл.
  2. Результат повреждения сектора диска раздающего.

Это те варианты которые пришли в голову.

Почему эти файлы продолжают распространяться

  1. Видео/аудио файл может иметь нуль-блок в середине и спокойно воспроизводится просто перескакивая то место где попался нуль-блок. Тем самым он может казаться целым.
  2. Образ диска также может иметь нуль-блоки в разных местах и это проявит себя только при попытке чтения файлов из этих блоков.

Как это работает

  1. Перед загрузкой файла p2p [1] клиент получает от источника список хешей блоков.
  2. Каждый хеш из списка сравнивается с заранее вычисленными хешами нуль-блоков.
  3. Те блоки у которых хеш совпал с хешем нуль-блока помечаются уже загруженными. Пользователю дополнительно отображается что в этом месте одни нули.

Инструменты для вычисления нуль-блоков

  1. RHash [2] — он будет непосредственно считать хеши.
  2. Lua [3] — будет загружать в RHash необходимое количество нулей и выводить результаты.
  3. Общие функции скрипта:

    local lua = "lua5.1"                  -- интерпретатор
    local script_name = "zero-block-hash" -- название скрипта (имя файла без расширения)
    
    -- декодирует строку из hex в бинарное представление
    -- https://stackoverflow.com/a/9140231
    function string.fromhex(str)
        return (str:gsub('..', function (cc)
            return string.char(tonumber(cc, 16))
        end))
    end
    
    -- выводит в io.stdout заданное количество нулей
    function std_write(size)
        index = index or 0;
    
        local buffer_size = 1024*1024*16;
    
        if ( size <= buffer_size ) then
            io.stdout:write( (''):rep( size  ) );
        else
            local zero_buffer = (''):rep( buffer_size );
    
            local count = math.floor( size / #zero_buffer );
            local tail = math.fmod( size, #zero_buffer );
    
            for i = 1, count do
                io.stdout:write( zero_buffer );
            end
    
            if ( tail > 0 ) then
                io.stdout:write( zero_buffer:sub( 1, tail ) );
            end
        end
    end

Не качаем нули из сети EDonkey2000

md4(block_data)

ED2K хеш [4] самый простой. У него фиксированный размер блока 9728000 байт. Блоки обрабатываются функцией md4 [5]. Понадобится вычислить только одно значение хеша.

Считаем md4 хеш:

--ED2K zero block
local md4_cmd = lua..' -l"'..script_name..'" -e"std_write(9728000);"|rhash -p"%x{md4}" -';

function gen_md4_hash()
    -- запускаем функцию std_write которая передаёт в RHash необходимое количество нулевых байт.
    local md4 = io.popen(md4_cmd, "rb");

    -- получаем результат вычислений
    local hash = md4:read("*a"):upper(); 

    -- выводим результат
    print("")
    print("// md4_hash")
    print("// Hash: "..hash, "Size: 9728000");
    md4:close();
end

Запускаем:

lua5.1 -l "zero-block-hash" -e"gen_md4_hash()"

Результат:

// md4_hash
// Hash: D7DEF262A127CD79096A108E7A9FC138       Size: 9728000

MD4 от 9728000 нулевых байт: D7DEF262A127CD79096A108E7A9FC138

Детектор ED2K нуль-блока Shareaza:

BOOL CED2K::IsZeroBlock(uint32 nBlock) const
{
    // Hash: D7DEF262A127CD79096A108E7A9FC138      Size: 9728000
    static const uint32 ZeroHash[ 4 ] = { 0x62F2DED7, 0x79CD27A1, 0x8E106A09, 0x38C19F7A };
    return memcmp( &ZeroHash, m_pList[ nBlock ].data, sizeof( ZeroHash ) ) == 0;
}

GitHub: ED2K.cpp#L334 [6]

Не качаем нули из сети BitTorrent

С BitTorrent [7] посложнее. Минимальный размер блока у BitTorrent это 16384 байт. Далее размер блока удваивается.

Хеш блока это результат работы функции SHA1 [8] над данными блока:

sha1(block_data)

Считаем sha1 хеши:

--Bittorrent zero block
local sha1_cmd = lua..' -l"'..script_name..'" -e"std_write(%s);"|rhash -p"%%x{sha1}" -';

function gen_sha1_hashes(hash_count)
    local size = 16384 -- минимальный размер блока
    local sha1 = {};
    hash_count = hash_count or 13; -- по умолчанию считаем 13 хешей

    -- параллельные вычисления
    -- запускаем счёт хешей передавая размер блока в std_write
    for i = 1, hash_count do
        sha1[i] = io.popen(sha1_cmd:format(size), "rb");
        size = size * 2;
    end

    size = 16384
    -- получаем результаты
    print("")
    print("// sha1_hashes")
    for i = 1, #sha1 do
        local hash = sha1[i]:read("*a"):upper();
        sha1[i]:close();
        print( "// Hash: " .. hash .. "tSize: " .. size );
        size = size * 2;
    end
end

Запускаем:

lua5.1 -l "zero-block-hash" -e"gen_sha1_hashes()"

Мне терпения хватило дождаться вычисления 22 нуль-блоков. Но и этого с избытком. С трудом представляю себе торрент с блоком в 34GB:

// sha1_hashes
// Hash: 897256B6709E1A4DA9DABA92B6BDE39CCFCCD8C1       Size: 16384
// Hash: 5188431849B4613152FD7BDBA6A3FF0A4FD6424B       Size: 32768
// Hash: 1ADC95BEBE9EEA8C112D40CD04AB7A8D75C4F961       Size: 65536
// Hash: 67DFD19F3EB3649D6F3F6631E44D0BD36B8D8D19       Size: 131072
// Hash: 2E000FA7E85759C7F4C254D4D9C33EF481E459A7       Size: 262144
// Hash: 6A521E1D2A632C26E53B83D2CC4B0EDECFC1E68C       Size: 524288
// Hash: 3B71F43FF30F4B15B5CD85DD9E95EBC7E84EB5A3       Size: 1048576
// Hash: 7D76D48D64D7AC5411D714A4BB83F37E3E5B8DF6       Size: 2097152
// Hash: 2BCCBD2F38F15C13EB7D5A89FD9D85F595E23BC3       Size: 4194304
// Hash: 5FDE1CCE603E6566D20DA811C9C8BCCCB044D4AE       Size: 8388608
// Hash: 3B4417FC421CEE30A9AD0FD9319220A8DAE32DA2       Size: 16777216
// Hash: 57B587E1BF2D09335BDAC6DB18902D43DFE76449       Size: 33554432
// Hash: 44FAC4BEDDE4DF04B9572AC665D3AC2C5CD00C7D       Size: 67108864
// Hash: BA713B819C1202DCB0D178DF9D2B3222BA1BBA44       Size: 134217728
// Hash: 7B91DBDC56C5781EDF6C8847B4AA6965566C5C75       Size: 268435456
// Hash: 5B088492C9F4778F409B7AE61477DEC124C99033       Size: 536870912
// Hash: 2A492F15396A6768BCBCA016993F4B4C8B0B5307       Size: 1073741824
// Hash: 91D50642DD930E9542C39D36F0516D45F4E1AF0D       Size: 2147483648
// Hash: 1BF99EE9F374E58E201E4DDA4F474E570EB77229       Size: 4294967296
// Hash: BCC8C0CA9E402EEE924A6046966D18B1F66EB577       Size: 8589934592
// Hash: DC44DD38511BD6D1233701D63C15B87D0BD9F3A5       Size: 17179869184
// Hash: 7FFB233B3B2806328171FB8B5C209F48DC095B72       Size: 34359738368

Детектор BitTorrent нуль-блока в Shareaza

BOOL CBTInfo::IsZeroBlock(uint32 nBlock) const
{
    static const uint32 ZeroHash[22][5] = {
        // Hash: 897256B6709E1A4DA9DABA92B6BDE39CCFCCD8C1       Size: 16384
        { 0xB6567289, 0x4D1A9E70, 0x92BADAA9, 0x9CE3BDB6, 0xC1D8CCCF },
        // Hash: 5188431849B4613152FD7BDBA6A3FF0A4FD6424B       Size: 32768
        { 0x18438851, 0x3161B449, 0xDB7BFD52, 0x0AFFA3A6, 0x4B42D64F },
        // Hash: 1ADC95BEBE9EEA8C112D40CD04AB7A8D75C4F961       Size: 65536
        { 0xBE95DC1A, 0x8CEA9EBE, 0xCD402D11, 0x8D7AAB04, 0x61F9C475 },
        // Hash: 67DFD19F3EB3649D6F3F6631E44D0BD36B8D8D19       Size: 131072
        { 0x9FD1DF67, 0x9D64B33E, 0x31663F6F, 0xD30B4DE4, 0x198D8D6B },
        // Hash: 2E000FA7E85759C7F4C254D4D9C33EF481E459A7       Size: 262144
        { 0xA70F002E, 0xC75957E8, 0xD454C2F4, 0xF43EC3D9, 0xA759E481 },
        // Hash: 6A521E1D2A632C26E53B83D2CC4B0EDECFC1E68C       Size: 524288
        { 0x1D1E526A, 0x262C632A, 0xD2833BE5, 0xDE0E4BCC, 0x8CE6C1CF },
        // Hash: 3B71F43FF30F4B15B5CD85DD9E95EBC7E84EB5A3       Size: 1048576
        { 0x3FF4713B, 0x154B0FF3, 0xDD85CDB5, 0xC7EB959E, 0xA3B54EE8 },
        // Hash: 7D76D48D64D7AC5411D714A4BB83F37E3E5B8DF6       Size: 2097152
        { 0x8DD4767D, 0x54ACD764, 0xA414D711, 0x7EF383BB, 0xF68D5B3E },
        // Hash: 2BCCBD2F38F15C13EB7D5A89FD9D85F595E23BC3       Size: 4194304
        { 0x2FBDCC2B, 0x135CF138, 0x895A7DEB, 0xF5859DFD, 0xC33BE295 },
        // Hash: 5FDE1CCE603E6566D20DA811C9C8BCCCB044D4AE       Size: 8388608
        { 0xCE1CDE5F, 0x66653E60, 0x11A80DD2, 0xCCBCC8C9, 0xAED444B0 },
        // Hash: 3B4417FC421CEE30A9AD0FD9319220A8DAE32DA2       Size: 16777216
        { 0xFC17443B, 0x30EE1C42, 0xD90FADA9, 0xA8209231, 0xA22DE3DA },
        // Hash: 57B587E1BF2D09335BDAC6DB18902D43DFE76449       Size: 33554432
        { 0xE187B557, 0x33092DBF, 0xDBC6DA5B, 0x432D9018, 0x4964E7DF },
        // Hash: 44FAC4BEDDE4DF04B9572AC665D3AC2C5CD00C7D       Size: 67108864
        { 0xBEC4FA44, 0x04DFE4DD, 0xC62A57B9, 0x2CACD365, 0x7D0CD05C },
        // Hash: BA713B819C1202DCB0D178DF9D2B3222BA1BBA44       Size: 134217728
        { 0x813B71BA, 0xDC02129C, 0xDF78D1B0, 0x22322B9D, 0x44BA1BBA },
        // Hash: 7B91DBDC56C5781EDF6C8847B4AA6965566C5C75       Size: 268435456
        { 0xDCDB917B, 0x1E78C556, 0x47886CDF, 0x6569AAB4, 0x755C6C56 },
        // Hash: 5B088492C9F4778F409B7AE61477DEC124C99033       Size: 536870912
        { 0x9284085B, 0x8F77F4C9, 0xE67A9B40, 0xC1DE7714, 0x3390C924 },
        // Hash: 2A492F15396A6768BCBCA016993F4B4C8B0B5307       Size: 1073741824
        { 0x152F492A, 0x68676A39, 0x16A0BCBC, 0x4C4B3F99, 0x07530B8B },
        // Hash: 91D50642DD930E9542C39D36F0516D45F4E1AF0D       Size: 2147483648
        { 0x4206D591, 0x950E93DD, 0x369DC342, 0x456D51F0, 0x0DAFE1F4 },
        // Hash: 1BF99EE9F374E58E201E4DDA4F474E570EB77229       Size: 4294967296
        { 0xE99EF91B, 0x8EE574F3, 0xDA4D1E20, 0x574E474F, 0x2972B70E },
        // Hash: BCC8C0CA9E402EEE924A6046966D18B1F66EB577       Size: 8589934592
        { 0xCAC0C8BC, 0xEE2E409E, 0x46604A92, 0xB1186D96, 0x77B56EF6 },
        // Hash: DC44DD38511BD6D1233701D63C15B87D0BD9F3A5       Size: 17179869184
        { 0x38DD44DC, 0xD1D61B51, 0xD6013723, 0x7DB8153C, 0xA5F3D90B },
        // Hash: 7FFB233B3B2806328171FB8B5C209F48DC095B72       Size: 34359738368
        { 0x3B23FB7F, 0x3206283B, 0x8BFB7181, 0x489F205C, 0x725B09DC }
    };

    int i = 0;
    for(; m_nBlockSize > ( (uint64) 16384 << i ); i++)
        if ( i > 21 )
            return FALSE;

    return memcmp( &m_pBlockBTH[ nBlock ], ZeroHash[ i ], sizeof( ZeroHash[ i ] ) ) == 0;
}

GitHub: BTInfo.cpp#L1611 [9]

Не качаем нули из сетей DirectConnect, Gnutella и Gnutella2

Эти три сети используют Tree Tiger Hash (TTH). Исходя из названия это дерево хешей [10] а для вычисления используется функция Tiger [11]. Я такие типы хешей называю "деревянные". TTH на мой взгляд самое простое дерево и благодаря его свойствам мы очень быстро можем вычислить хеш для разных размеров блока.

Минимальный размер блока у TTH это 1024 байта. Далее размер блока удваивается на каждом уровне.

Вычисляется он так:

Tiger(0x00 + block_data)

0x00 — байт префикс Leaf блока
block_data — данные блока(в нашем случае это 1024 нулевых байта)
+ — конкатенация
Tiger — хеш функция

Далее для того чтобы вычислить нуль-блоки большего размера мы используем хеш от нуль-блока меньшего размера.

Tiger(0x01 + hash + hash)

0x01 — байт префикс для пары хешей
hash — хеш нуль-блока который мы получили на предыдущем уровне.
Tiger — хеш функция

Пишем функции для вычисления:

--Tiger Tree Hash Leaf block
local leaf_hash_cmd = lua..' -l"'..script_name..'" -e"std_write_leaf_hash()"';

--Tiger Tree Hash Internal block
local internal_hash_cmd = lua..' -l"'..script_name..'" -e"std_write_internal_hash('%s')"';

-- передаёт в rhash нуль-блок с префиксом ''  
function std_write_leaf_hash()
    local tiger = io.popen('rhash -p"%x{tiger}" -', "wb") 
    tiger:write(''..(''):rep(1024))
    tiger:close()
end

-- передаёт в rhash пару одинаковых хешей с префиксом '1'
function std_write_internal_hash(hash)
    local tiger = io.popen('rhash -p"%x{tiger}" -', "wb");
    hash = hash:fromhex();
    tiger:write('1'..hash..hash)
    tiger:close()
end

-- хеш от нуль-блока
function tth_leaf()
    local rhash = io.popen(leaf_hash_cmd, "rb");
    local hash = rhash:read("*a");
    rhash:close();
    return hash;
end

-- хеш от пары одинаковых хешей
function tth_root(hash_hex)
    local rhash = io.popen(internal_hash_cmd:format(hash_hex), "rb");
    local hash = rhash:read("*a");
    rhash:close();
    return hash;
end

-- вычисляем заданное количество хешей
function gen_tth_hashes(hash_count)
    -- получаем хеш от нуль-блока
    local hash_hex = tth_leaf():upper();
    hash_count = hash_count or 37;
    hash_count = hash_count - 1;
    print("")
    print("// tth_hashes")
    for i = 0, hash_count do
        print("// Hash: "..hash_hex, " Size: "..(1024*2^i));
         -- получаем хеш от нуль-блока вдвое большего размера
        hash_hex = tth_root(hash_hex):upper();
    end
end

Запускаем:

lua5.1 -l "zero-block-hash" -e"gen_tth_hashes()"

В результате я очень быстро получил 37 нуль-блоков. Дальше были проблемы с отображением размера блока у скрипта. Но и этих значений с избытком. Последний блок размером в 70TB.

// tth_hashes
// Hash: 13143C45D95485EACD9C47D72630EF0139436CB77DF2632B        Size: 1024
// Hash: 855DCE7FE3E963F50295A673120E6259165CED9F086DB031        Size: 2048
// Hash: 38FB763B44ECA3B13F40182C75694360AC8DA0865DDB29D6        Size: 4096
// Hash: 721BEF53CBBDA47BE44BD26C43EC048F136D371E918200CF        Size: 8192
// Hash: AFDDF505C1E1D5AF8FAE007BBE4E64578F34D912345E23D8        Size: 16384
// Hash: 53CC478ED14FF7FB671F94ECE0FD7C8C5DCB2FE611ACAC6B        Size: 32768
// Hash: 098B212D6EE0398D319D4F1807E87235A0B8665BA46EF77F        Size: 65536
// Hash: 69940A3C20C43576D258BD210339565711D696E94A3511EB        Size: 131072
// Hash: FA4317C074C2D7CD9BBFD7F4C8BD3F9F79F330F0C27B61B8        Size: 262144
// Hash: AF8E46E049A800C2339E863AF390C5CFF02BCC39025D44AA        Size: 524288
// Hash: 650022207EA4EB454E24D3279539F3CCD92F034E2F83CCB7        Size: 1048576
// Hash: 0BED4DF002309E7D33D52ED0D5C3C24B1ECAA330CBAFB723        Size: 2097152
// Hash: 2FFF449E538E158CD346C5BF7778F2FF67383707955C72C1        Size: 4194304
// Hash: F2D3852A12C25C0C1EE124C07144C6CFA3CD0E72DB9364F8        Size: 8388608
// Hash: 8E6FD02F7F9A0D5233E9287C6D139D44DE76BB80BCBD8BEC        Size: 16777216
// Hash: F98C3CB14C4B501DCEF346D6FB92E56AC3F96102B17468F4        Size: 33554432
// Hash: 1830D2019F1A54C7A8A3947E36D34A4E676523FF0735E0FC        Size: 67108864
// Hash: 3D002613BA2F88DA7D7E1AB165677FC939B5EC6FFD5D2E73        Size: 134217728
// Hash: BC0466EE7A0C30E31EFD803598BE8F69400B96AE3126AF70        Size: 268435456
// Hash: 31D3A13D9F1BD0D2E16FF2BF6749F830D81693D63E4C1903        Size: 536870912
// Hash: 6EF9A41AEC7C0C0B821D3A845994E6F18E5268E37BC982C1        Size: 1073741824
// Hash: 13132A77BAB0B8A0130FC2B5BF6C36701C622A36AFFBD175        Size: 2147483648
// Hash: E684CA0E3D759457F3F2B4183A0889B25C49F70AB5B5AD8E        Size: 4294967296
// Hash: 8C4AEAB1D5A2E3ABBD19848EBC9813121A83D196320EFE54        Size: 8589934592
// Hash: 2CB4627DB09C230212258BAD4120AA0A1C4A185BD2CC4C57        Size: 17179869184
// Hash: B58DE81DC064E964720A0C181AE6EF415F865BAA18E9F019        Size: 34359738368
// Hash: EC0B596EFA9EDBEFE275539914F30757E2E3EB82C30B6FB8        Size: 68719476736
// Hash: BA0078DAD436099159ADA9CFA1457806EB581730364084E0        Size: 137438953472
// Hash: D96DA2416DBF7DAC663872838F8F4E7D8E7C4D2D2A2051AB        Size: 274877906944
// Hash: 74816B22B67E4E6995FECAEB84302D01E489BCD76845444B        Size: 549755813888
// Hash: 307DB672C03531EB0E9B19FC2ED134ACCEFFB4E04D8EB62D        Size: 1099511627776
// Hash: 43CD6009D7931ECC1FFC484D8156A92EC673DEF3D6AE7CF9        Size: 2199023255552
// Hash: 84814323435A450426EECC6700349387D61BD5027F6E7085        Size: 4398046511104
// Hash: 05275B3D69A996B1E8ABDA6EACE8605D5BB7DD8964AC4C79        Size: 8796093022208
// Hash: 434934E2D0EFDEE9864982221FB8A0A872D842B4DA6C59E7        Size: 17592186044416
// Hash: 435396F0F684A6B3E5B5940A79800EE384915CCAD7C52385        Size: 35184372088832
// Hash: 7F377469FB6883D13331667F52CF23194846311094A363C4        Size: 70368744177664

Детектор TigerTree нуль-блока в Shareaza

BOOL CTigerTree::IsZeroBlock(uint32 nBlock) const
{
    static const uint64 ZeroHash[37][3] =
    {
        // Hash: 13143C45D95485EACD9C47D72630EF0139436CB77DF2632B        Size: 1024
        { 0xEA8554D9453C1413, 0x01EF3026D7479CCD, 0x2B63F27DB76C4339 },
        // Hash: 855DCE7FE3E963F50295A673120E6259165CED9F086DB031        Size: 2048
        { 0xF563E9E37FCE5D85, 0x59620E1273A69502, 0x31B06D089FED5C16 },
        // Hash: 38FB763B44ECA3B13F40182C75694360AC8DA0865DDB29D6        Size: 4096
        { 0xB1A3EC443B76FB38, 0x604369752C18403F, 0xD629DB5D86A08DAC },
        // Hash: 721BEF53CBBDA47BE44BD26C43EC048F136D371E918200CF        Size: 8192
        { 0x7BA4BDCB53EF1B72, 0x8F04EC436CD24BE4, 0xCF0082911E376D13 },
        // Hash: AFDDF505C1E1D5AF8FAE007BBE4E64578F34D912345E23D8        Size: 16384
        { 0xAFD5E1C105F5DDAF, 0x57644EBE7B00AE8F, 0xD8235E3412D9348F },
        // Hash: 53CC478ED14FF7FB671F94ECE0FD7C8C5DCB2FE611ACAC6B        Size: 32768
        { 0xFBF74FD18E47CC53, 0x8C7CFDE0EC941F67, 0x6BACAC11E62FCB5D },
        // Hash: 098B212D6EE0398D319D4F1807E87235A0B8665BA46EF77F        Size: 65536
        { 0x8D39E06E2D218B09, 0x3572E807184F9D31, 0x7FF76EA45B66B8A0 },
        // Hash: 69940A3C20C43576D258BD210339565711D696E94A3511EB        Size: 131072
        { 0x7635C4203C0A9469, 0x5756390321BD58D2, 0xEB11354AE996D611 },
        // Hash: FA4317C074C2D7CD9BBFD7F4C8BD3F9F79F330F0C27B61B8        Size: 262144
        { 0xCDD7C274C01743FA, 0x9F3FBDC8F4D7BF9B, 0xB8617BC2F030F379 },
        // Hash: AF8E46E049A800C2339E863AF390C5CFF02BCC39025D44AA        Size: 524288
        { 0xC200A849E0468EAF, 0xCFC590F33A869E33, 0xAA445D0239CC2BF0 },
        // Hash: 650022207EA4EB454E24D3279539F3CCD92F034E2F83CCB7        Size: 1048576
        { 0x45EBA47E20220065, 0xCCF3399527D3244E, 0xB7CC832F4E032FD9 },
        // Hash: 0BED4DF002309E7D33D52ED0D5C3C24B1ECAA330CBAFB723        Size: 2097152
        { 0x7D9E3002F04DED0B, 0x4BC2C3D5D02ED533, 0x23B7AFCB30A3CA1E },
        // Hash: 2FFF449E538E158CD346C5BF7778F2FF67383707955C72C1        Size: 4194304
        { 0x8C158E539E44FF2F, 0xFFF27877BFC546D3, 0xC1725C9507373867 },
        // Hash: F2D3852A12C25C0C1EE124C07144C6CFA3CD0E72DB9364F8        Size: 8388608
        { 0x0C5CC2122A85D3F2, 0xCFC64471C024E11E, 0xF86493DB720ECDA3 },
        // Hash: 8E6FD02F7F9A0D5233E9287C6D139D44DE76BB80BCBD8BEC        Size: 16777216
        { 0x520D9A7F2FD06F8E, 0x449D136D7C28E933, 0xEC8BBDBC80BB76DE },
        // Hash: F98C3CB14C4B501DCEF346D6FB92E56AC3F96102B17468F4        Size: 33554432
        { 0x1D504B4CB13C8CF9, 0x6AE592FBD646F3CE, 0xF46874B10261F9C3 },
        // Hash: 1830D2019F1A54C7A8A3947E36D34A4E676523FF0735E0FC        Size: 67108864
        { 0xC7541A9F01D23018, 0x4E4AD3367E94A3A8, 0xFCE03507FF236567 },
        // Hash: 3D002613BA2F88DA7D7E1AB165677FC939B5EC6FFD5D2E73        Size: 134217728
        { 0xDA882FBA1326003D, 0xC97F6765B11A7E7D, 0x732E5DFD6FECB539 },
        // Hash: BC0466EE7A0C30E31EFD803598BE8F69400B96AE3126AF70        Size: 268435456
        { 0xE3300C7AEE6604BC, 0x698FBE983580FD1E, 0x70AF2631AE960B40 },
        // Hash: 31D3A13D9F1BD0D2E16FF2BF6749F830D81693D63E4C1903        Size: 536870912
        { 0xD2D01B9F3DA1D331, 0x30F84967BFF26FE1, 0x03194C3ED69316D8 },
        // Hash: 6EF9A41AEC7C0C0B821D3A845994E6F18E5268E37BC982C1        Size: 1073741824
        { 0x0B0C7CEC1AA4F96E, 0xF1E69459843A1D82, 0xC182C97BE368528E },
        // Hash: 13132A77BAB0B8A0130FC2B5BF6C36701C622A36AFFBD175        Size: 2147483648
        { 0xA0B8B0BA772A1313, 0x70366CBFB5C20F13, 0x75D1FBAF362A621C },
        // Hash: E684CA0E3D759457F3F2B4183A0889B25C49F70AB5B5AD8E        Size: 4294967296
        { 0x5794753D0ECA84E6, 0xB289083A18B4F2F3, 0x8EADB5B50AF7495C },
        // Hash: 8C4AEAB1D5A2E3ABBD19848EBC9813121A83D196320EFE54        Size: 8589934592
        { 0xABE3A2D5B1EA4A8C, 0x121398BC8E8419BD, 0x54FE0E3296D1831A },
        // Hash: 2CB4627DB09C230212258BAD4120AA0A1C4A185BD2CC4C57        Size: 17179869184
        { 0x02239CB07D62B42C, 0x0AAA2041AD8B2512, 0x574CCCD25B184A1C },
        // Hash: B58DE81DC064E964720A0C181AE6EF415F865BAA18E9F019        Size: 34359738368
        { 0x64E964C01DE88DB5, 0x41EFE61A180C0A72, 0x19F0E918AA5B865F },
        // Hash: EC0B596EFA9EDBEFE275539914F30757E2E3EB82C30B6FB8        Size: 68719476736
        { 0xEFDB9EFA6E590BEC, 0x5707F314995375E2, 0xB86F0BC382EBE3E2 },
        // Hash: BA0078DAD436099159ADA9CFA1457806EB581730364084E0        Size: 137438953472
        { 0x910936D4DA7800BA, 0x067845A1CFA9AD59, 0xE0844036301758EB },
        // Hash: D96DA2416DBF7DAC663872838F8F4E7D8E7C4D2D2A2051AB        Size: 274877906944
        { 0xAC7DBF6D41A26DD9, 0x7D4E8F8F83723866, 0xAB51202A2D4D7C8E },
        // Hash: 74816B22B67E4E6995FECAEB84302D01E489BCD76845444B        Size: 549755813888
        { 0x694E7EB6226B8174, 0x012D3084EBCAFE95, 0x4B444568D7BC89E4 },
        // Hash: 307DB672C03531EB0E9B19FC2ED134ACCEFFB4E04D8EB62D        Size: 1099511627776
        { 0xEB3135C072B67D30, 0xAC34D12EFC199B0E, 0x2DB68E4DE0B4FFCE },
        // Hash: 43CD6009D7931ECC1FFC484D8156A92EC673DEF3D6AE7CF9        Size: 2199023255552
        { 0xCC1E93D70960CD43, 0x2EA956814D48FC1F, 0xF97CAED6F3DE73C6 },
        // Hash: 84814323435A450426EECC6700349387D61BD5027F6E7085        Size: 4398046511104
        { 0x04455A4323438184, 0x8793340067CCEE26, 0x85706E7F02D51BD6 },
        // Hash: 05275B3D69A996B1E8ABDA6EACE8605D5BB7DD8964AC4C79        Size: 8796093022208
        { 0xB196A9693D5B2705, 0x5D60E8AC6EDAABE8, 0x794CAC6489DDB75B },
        // Hash: 434934E2D0EFDEE9864982221FB8A0A872D842B4DA6C59E7        Size: 17592186044416
        { 0xE9DEEFD0E2344943, 0xA8A0B81F22824986, 0xE7596CDAB442D872 },
        // Hash: 435396F0F684A6B3E5B5940A79800EE384915CCAD7C52385        Size: 35184372088832
        { 0xB3A684F6F0965343, 0xE30E80790A94B5E5, 0x8523C5D7CA5C9184 },
        // Hash: 7F377469FB6883D13331667F52CF23194846311094A363C4        Size: 70368744177664
        { 0xD18368FB6974377F, 0x1923CF527F663133, 0xC463A39410314648 }
    };

    CSectionLock oLock( &m_pSection );

    if ( nBlock >= m_nBaseUsed ) return FALSE;
    if ( m_nActualHeight < m_nHeight ) return FALSE;

    uint32 nBlockHeight = m_nActualHeight - m_nHeight;

    if ( nBlockHeight > 36 ) return FALSE;

    CTigerNode* pBase = m_pNode + m_nNodeCount - m_nNodeBase + nBlock;
    return memcmp( ZeroHash[ nBlockHeight ], pBase->value, sizeof( pBase->value ) ) == 0;
}

GitHub: TigerTree.cpp#L1298 [12]

Хвосты

Хвост это неполный блок в конце файла. Хвост у файла один и мы можем для него индивидуально посчитать хеш если бы он был заполнен нулями. И в случае если хеш хвоста и вычисленный совпадают помечаем его также загруженным.

Ещё не реализовал данную функцию.

Заключение

Надеюсь данную инструкцию возьмут на вооружение и добавят этот функционал в свои peer-to-peer клиенты и в сети будет циркулировать больше полезных данных.

Ссылки

Скрипт: zero-block-hash.lua [13]
Торрент c файлом заполненным нулями: testfile.torrent [14]

Автор: ivan386

Источник [15]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/peer-to-peer/310715

Ссылки в тексте:

[1] p2p: https://ru.wikipedia.org/wiki/%D0%9E%D0%B4%D0%BD%D0%BE%D1%80%D0%B0%D0%BD%D0%B3%D0%BE%D0%B2%D0%B0%D1%8F_%D1%81%D0%B5%D1%82%D1%8C

[2] RHash: https://github.com/rhash/RHash

[3] Lua: https://www.lua.org/

[4] ED2K хеш: https://ru.wikipedia.org/wiki/EDonkey2000_(%D1%81%D0%B5%D1%82%D1%8C)#%D0%A5%D0%B5%D1%88-%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_ed2k

[5] md4: https://ru.wikipedia.org/wiki/MD4

[6] ED2K.cpp#L334: https://github.com/ivan386/Shareaza/blob/c04fb06fb8f1d39e779d22d884133808b6bce639/HashLib/ED2K.cpp#L334

[7] BitTorrent: https://ru.wikipedia.org/wiki/BitTorrent_(%D0%BF%D1%80%D0%BE%D1%82%D0%BE%D0%BA%D0%BE%D0%BB)

[8] SHA1: https://ru.wikipedia.org/wiki/SHA-1

[9] BTInfo.cpp#L1611: https://github.com/ivan386/Shareaza/blob/c04fb06fb8f1d39e779d22d884133808b6bce639/shareaza/BTInfo.cpp#L1611

[10] дерево хешей: https://ru.wikipedia.org/wiki/%D0%94%D0%B5%D1%80%D0%B5%D0%B2%D0%BE_%D1%85%D0%B5%D1%88%D0%B5%D0%B9

[11] Tiger: https://ru.wikipedia.org/wiki/Tiger_(%D1%85%D0%B5%D1%88-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D1%8F)

[12] TigerTree.cpp#L1298: https://github.com/ivan386/Shareaza/blob/c04fb06fb8f1d39e779d22d884133808b6bce639/HashLib/TigerTree.cpp#L1298

[13] zero-block-hash.lua: https://github.com/ivan386/lua-zero-block-hash/blob/master/zero-block-hash.lua

[14] testfile.torrent: https://cloudflare-ipfs.com/ipfs/uAXAAPBI2CiISIItKMo_BGqFkBCQqJ1BgMZ-dXHaIFsLlc6oCxmXxKWXDEhB0ZXN0ZmlsZS50b3JyZW50CgIIAQ/testfile.torrent

[15] Источник: https://habr.com/ru/post/442722/?utm_campaign=442722