Electrum 2FA Wallet — получение третьего XPub

Я работаю над проектом, который требует от меня взять мнемонику Electrum, определить ее тип и получить соответствующие адреса для этого кошелька.

Я нашел много информации о том, как определить мнемонический тип и скрыть его в основном семени, но я изо всех сил пытаюсь понять, как получаются адреса для кошелька 2FA. Точнее, мне удалось получить обе расширенные пары ключей ( x1/и x2/в файле кошелька), но я не могу получить третью xpub ( x3/в файле кошелька).

Я просмотрел исходный код, и кажется, что этот ключ получен путем объединения обоих ключей xpub (ключи должны быть ORDERED ), которые могут быть получены из основного начального числа ( x1/и x2/), хеширования их с использованием SHA-256и использования в качестве индекса при получении третий xpub. В качестве корня используется жестко закодированный xpub.

У меня проблемы с пониманием/реализацией этого, потому что SHA-256алгоритм возвращает 32 байта данных, а дочерний индекс должен иметь длину 4 байта (согласно BIP32спецификации).

Вот код от Electrum, который обрабатывает это.

def get_user_id(storage):
    def make_long_id(xpub_hot, xpub_cold):
        return bitcoin.sha256(''.join(sorted([xpub_hot, xpub_cold])))
    xpub1 = storage.get('x1/')['xpub']
    xpub2 = storage.get('x2/')['xpub']
    long_id = make_long_id(xpub1, xpub2)
    short_id = hashlib.sha256(long_id).hexdigest()
    return long_id, short_id

def make_xpub(xpub, s):
    version, _, _, _, c, cK = deserialize_xpub(xpub)
    cK2, c2 = bitcoin._CKD_pub(cK, c, s)
    return bitcoin.serialize_xpub(version, c2, cK2)

Затем функции вызываются следующим образом:

xpub1 = wizard.storage.get('x1/')['xpub']
xpub2 = wizard.storage.get('x2/')['xpub']
# Generate third key deterministically.
long_user_id, short_id = get_user_id(wizard.storage)
xpub3 = make_xpub(signing_xpub, long_user_id)

Длина long_user_id32 байта, так как это результат SHA-256функции, и затем он используется для вызова make_xpub()функции, которая должна принимать 4 байта данных в качестве дочернего индекса.

Что мне здесь не хватает? Любой вклад приветствуется.

Ответы (1)

Изучив исходный код кошелька Electrum немного глубже, я смог найти работающую реализацию на JavaScript.

Проблема, которую я описал в своем вопросе, заключалась в том, что long_user_idэто 32 байта, а вам нужно всего 4 байта для получения дочернего ключа. Оказывается, что третий xpub на самом деле не является дочерним элементом жестко закодированного xpub подписи, а скорее корневой ключ, полученный из этого ключа подписи (т.е. он имеет совершенно другой код цепи и его индекс 0).

Вот функция, которую я написал, которая берет два xpub кошелька (найденных в файле wallet/keystore x1/и x2/в нем) и жестко запрограммированный xpub из Trustedcoin и выводит третий xpub.

import HDKey from 'hdkey';
import crypto from 'crypto';
import secp256k1 from 'secp256k1';

function getCosignerXPub(xpub_x1, xpub_x2){
    const signing_xpub = "xpub661MyMwAqRbcGnMkaTx2594P9EDuiEqMq25PM2aeG6UmwzaohgA6uDmNsvSUV8ubqwA3Wpste1hg69XHgjUuCD5HLcEp2QPzyV1HMrPppsL";

    const xpubs = [xpub_x1, xpub_x2].sort().join("");
    const long_user_id = new Buffer(sha256(xpubs), 'hex');

    const hdkey = HDKey.fromExtendedKey(signing_xpub);

    const data = Buffer.concat([hdkey.publicKey, long_user_id]);

    const I = crypto.createHmac('sha512', hdkey.chainCode).update(data).digest();

    const IL = I.slice(0, 32);
    const IR = I.slice(32);

    const hd = new HDKey(hdkey.versions);

    hd.publicKey = secp256k1.publicKeyTweakAdd(hdkey.publicKey, IL, true);
    hd.chainCode = IR;

    return hd.publicExtendedKey;
}