Я работаю над проектом, который требует от меня взять мнемонику 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_id
32 байта, так как это результат SHA-256
функции, и затем он используется для вызова make_xpub()
функции, которая должна принимать 4 байта данных в качестве дочернего индекса.
Что мне здесь не хватает? Любой вклад приветствуется.
Изучив исходный код кошелька 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;
}