メインコンテンツまでスキップ

ウォレットレベル QR(bittool-signer-wallet v1)

ur:bytescanonical CBORanimatedversion 1

ウォレットレベル QR コードは、BitTool Signer の 1 つのウォレットに含まれる全アカウントを 1 回のスキャンでエクスポートします。内容は、4 つの Bitcoin アカウント(シングルシグのアドレスタイプごとに 1 つ)、EVM と TRON のアカウント、そして 3 つの Solana プリセットアドレスです。アプリでは Connect › Wallet Level にあります。

エンコード​

pairing object (text keys, see below)
→ canonical CBOR (RFC 8949 §4.2.1) "inner", 1561 bytes for the test wallet
→ CBOR byte string containing those bytes "outer"
→ UR type "bytes" (BCR-2020-006)
→ multi-part UR, 400-byte fragments, upper-case 4 frames for the test wallet

重要な点が 4 つあります。

  1. CBOR は 2 層構造です。 UR 本体は CBOR の バイト文字列 で、ペアリング用のマップはその中に入っています。外側の層をデコードしてバイト列を取り出し、そのバイト列をさらにデコードします。JSON でも、tag-24 アイテムでも、独自の ur:bittool-… 型でもありません。
  2. キーはテキストです。 マップのキーは JSON のフィールド名を CBOR テキスト文字列にしたものなので、CBOR と後述の JSON 表示は同じデータです。
  3. 正準エンコードです。 確定長、最短の整数表現を使い、マップのキーはエンコード後のバイト列の順にソートします(テキストキーの場合は短いキーが先、同じ長さならバイト順)。使われるのはテキスト、符号なし整数、配列、マップのみで、存在しないフィールドは省略されます。
  4. 常にアニメーションです。 フラグメントサイズは 400 バイトで、ペイロードの半分を上限とするため、必ず 2 フレーム以上になります。BitTool Signer は 4,096 バイトを超えるペイロードを拒否します。スキャナーは、デコードする前にすべてのフレームを(順不同で)収集する必要があります。

正準なキー順の例:オブジェクト {version: 1, mode: "full", b: "x", aa: 2} は次のようにエンコードされます。

a4 61 62 61 78 62 61 61 02 64 6d6f6465 64 66756c6c 67 76657273696f6e 01
"b" "x" "aa" 2 "mode" "full" "version" 1

ペアリングオブジェクト​

エンベロープ​

フィールド型値
protocoltext"bittool-signer-wallet"
versionuint1。それ以外のバージョンは拒否してください。より新しいバージョンを v1 として読んではいけません。
modetext"full"。BitTool Signer が生成するのは full のみです。
networktext"mainnet"
masterFingerprinttextBIP32 マスターキーのフィンガープリント。小文字の 16 進 8 桁
accountsarray7 件のエントリ(後述)。位置ではなく、必ず chain と script/profile で検索してください。

アカウント​

chainフィールド備考
btc (×4)accountPath、script、xpub、derive: {receive: "0/*", change: "1/*"}script ごとに 1 つ:p2wpkh (m/84'/0'/0')、p2tr (m/86'/0'/0')、p2sh-p2wpkh (m/49'/0'/0')、p2pkh (m/44'/0'/0')
evmaccountPath m/44'/60'/0'、xpub、derive: "0/*"すべての EVM ネットワークで 1 つのアカウント
tronaccountPath m/44'/195'/0'、xpub、derive: "0/*"
solprofile: "bip44-solana"、accounts: [{index, profile, path, address}]3 件のエントリ:standard m/44'/501'/0'/0'、trust m/44'/501'/0'、legacy m/44'/501'

すべての xpub は、SegWit や Taproot であっても、メインネット標準のバージョンバイト 0x0488B21E を使います。どのアドレスを導出するかを示すのは ypub/zpub のプレフィックスではなく、script フィールドです。マルチシグ(BIP48)の共同署名者の鍵は含まれません。マルチシグウォレットにはすべての共同署名者としきい値が必要なため、その用途には コインレベルの共同署名者エクスポート を使ってください。

テストベクター​

公開テストフレーズ abandon … about、パスフレーズなしで生成しています。スキャナーをアニメーションに向けるか、下のフレームを使ってください。

ウォレットレベルのペアリング QR(テストウォレット)4 フレーム · 1,561 バイトのペイロード · アプリと同じくループ再生
UR:BYTES/1-4/LPADAACFAMCECYJYHYKIWKHKADLTHKAMCFOLIEJNJLIEIHIEIYKPJZJZIOJTIHJYKTJLJPJEIOJNHSINJTJTIHJYIOKOIHJPJKINJLJTADISHSIAIAJLKPJTJYJKLTONIEKSJOKPIDKSJLKSJOKPIDENFXHSJYHGIEINHTINJLIEJNGOIHGHFYJOETGSGHECJLJPETJTJNIDGRGLIAKPKKKOKNEMHGKKJEJKHFFGJEGRFWEEGMFDKTFXFYEOHDKKKPKOGDFEIDKOJSFPGYHKEOJPFPGDJKISHGIAGTGSJLGDEYIYGTFGGTGRFDGDGEEEHTIHHTHDHKHFGOISGSKOEHHFGTJPIMGDFXEMGDHGENHFIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYIYJOEYKTJOJEISJEHSIAIAJLKPJTJYGDHSJYISJEJNDLETEEDIDLDYDIDLDYDIONIEKSJOKPIDKSJLKSJOKPIDENFWIOFWIOJKIHJKJOHGKOFEGMFGEOGSFDGYKPENFXJTJSIEKOIYFEKOJYGTIAGYIMHKJPIAGMKNKSECEOGYGEIMGUKSHSJPIMEYHSIYHKHGIAGSJYIHJLFLHFJEKKEMFYEOGOGRFYGDESGYKKJPGSJOJPGYEOHFFXFEFXJLHKEEESKKIYIEFYFEFDFLFXJYGTGTIMESEYJOGMIHGOJKGYIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHUYSWLYTA
1 / 4
ペアリングオブジェクト(内側の CBOR の JSON 表示)
{
"protocol": "bittool-signer-wallet",
"version": 1,
"mode": "full",
"network": "mainnet",
"masterFingerprint": "73c5da0a",
"accounts": [
{
"chain": "btc",
"accountPath": "m/84'/0'/0'",
"script": "p2wpkh",
"xpub": "xpub6CatWdiZiodmUeTDp8LT5or8nmbKNcuyvz7WyksVFkKB4RHwCD3XyuvPEbvqAQY3rAPshWcMLoP2fMFMKHPJ4ZeZXYVUhLv1VMrjPC7PW6V",
"derive": {
"receive": "0/*",
"change": "1/*"
}
},
{
"chain": "btc",
"accountPath": "m/86'/0'/0'",
"script": "p2tr",
"xpub": "xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ",
"derive": {
"receive": "0/*",
"change": "1/*"
}
},
{
"chain": "btc",
"accountPath": "m/49'/0'/0'",
"script": "p2sh-p2wpkh",
"xpub": "xpub6C6nQwHaWbSrzs5tZ1q7m5R9cPK9eYpNMFesiXsYrgc1P8bvLLAet9JfHjYXKjToD8cBRswJXXbbFpXgwsswVPAZzKMa1jUp2kVkGVUaJa7",
"derive": {
"receive": "0/*",
"change": "1/*"
}
},
{
"chain": "btc",
"accountPath": "m/44'/0'/0'",
"script": "p2pkh",
"xpub": "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj",
"derive": {
"receive": "0/*",
"change": "1/*"
}
},
{
"chain": "evm",
"accountPath": "m/44'/60'/0'",
"xpub": "xpub6DCoCpSuQZB2jawqnGMEPS63ePKWkwWPH4TU45Q7LPXWuNd8TMtVxRrgjtEshuqpK3mdhaWHPFsBngh5GFZaM6si3yZdUsT8ddYM3PwnATt",
"derive": "0/*"
},
{
"chain": "tron",
"accountPath": "m/44'/195'/0'",
"xpub": "xpub6D1AabNHCupeiLM65ZR9UStMhJ1vCpyV4XbZdyhMZBiJXALQtmn9p42VTQckoHVn8WNqS7dqnJokZHAHcHGoaQgmv8D45oNUKx6DZMNZBCd",
"derive": "0/*"
},
{
"chain": "sol",
"profile": "bip44-solana",
"accounts": [
{
"index": 0,
"profile": "standard",
"path": "m/44'/501'/0'/0'",
"address": "HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk"
},
{
"index": 1,
"profile": "trust",
"path": "m/44'/501'/0'",
"address": "GjJyeC1r2RgkuoCWMyPYkCWSGSGLcz266EaAkLA27AhL"
},
{
"index": 2,
"profile": "legacy",
"path": "m/44'/501'",
"address": "D2PPQSYFe83nDzk96FqGumVU8JA7J8vj2Rhjc2oXzEi5"
}
]
}
]
}
QR フレーム(ur:bytes、4 パート)
フレーム
UR:BYTES/1-4/LPADAACFAMCECYJYHYKIWKHKADLTHKAMCFOLIEJNJLIEIHIEIYKPJZJZIOJTIHJYKTJLJPJEIOJNHSINJTJTIHJYIOKOIHJPJKINJLJTADISHSIAIAJLKPJTJYJKLTONIEKSJOKPIDKSJLKSJOKPIDENFXHSJYHGIEINHTINJLIEJNGOIHGHFYJOETGSGHECJLJPETJTJNIDGRGLIAKPKKKOKNEMHGKKJEJKHFFGJEGRFWEEGMFDKTFXFYEOHDKKKPKOGDFEIDKOJSFPGYHKEOJPFPGDJKISHGIAGTGSJLGDEYIYGTFGGTGRFDGDGEEEHTIHHTHDHKHFGOISGSKOEHHFGTJPIMGDFXEMGDHGENHFIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYIYJOEYKTJOJEISJEHSIAIAJLKPJTJYGDHSJYISJEJNDLETEEDIDLDYDIDLDYDIONIEKSJOKPIDKSJLKSJOKPIDENFWIOFWIOJKIHJKJOHGKOFEGMFGEOGSFDGYKPENFXJTJSIEKOIYFEKOJYGTIAGYIMHKJPIAGMKNKSECEOGYGEIMGUKSHSJPIMEYHSIYHKHGIAGSJYIHJLFLHFJEKKEMFYEOGOGRFYGDESGYKKJPGSJOJPGYEOHFFXFEFXJLHKEEESKKIYIEFYFEFDFLFXJYGTGTIMESEYJOGMIHGOJKGYIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHUYSWLYTA
UR:BYTES/2-4/LPAOAACFAMCECYJYHYKIWKHKADLTIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYIEJOEYJYJPJEHSIAIAJLKPJTJYGDHSJYISJEJNDLETENDIDLDYDIDLDYDIONIEKSJOKPIDKSJLKSJOKPIDENFXENJTGYKTFDHSHGIDGUJPKNJKECJYHTEHJSEMJNECGMESIAGDGRESIHHKJOGLGTFGIHJKINHDJKHKJPIOIAEHGDETIDKOGSGSFPIHJYESGEIYFDIMHKHDGRIMGHJLFYETIAFWGMJKKTGEHDHDIDIDFGJOHDIOKTJKJKKTHFGDFPHTKNGRGTHSEHIMGOJOEYJEHFJEFLHFGOHSGEHSEMIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYJEJOEYJKISDPJOEYKTJOJEISJEHSIAIAJLKPJTJYGDHSJYISJEJNDLEEESDIDLDYDIDLDYDIONIEKSJOKPIDKSJLKSJOKPIDENFWJLJKIYFXJTINIYKNKSIAFGKTJPGUKNGYINJSKPEYFYFWHFGHJKISJEFXHDHSIAKOGLJKHGFLHKGEHFHFISISHSKTFPEMIEEEGMECHGGUHGFLFGGLIDINETFPKTENHTGMIAEHIDJPKSGTKKHGGTKNFLEOFYGUGUGUGUJLIHJEJEKPIEISGOIEESKKGSIDENJSKSEOESGHESJTGTIEIMIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHBZSSDEKE
UR:BYTES/3-4/LPAXAACFAMCECYJYHYKIWKHKADLTOEIYIAISHSJTIOIHIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYIHJOEYJOJEISJEHSIAIAJLKPJTJYGDHSJYISJEJNDLEEEEDIDLDYDIDLDYDIOXIEKSJOKPIDKSJLKSJOKPIDENFYFXJLFXJOGUKPGYHTFWEYIMHSKTJSJTFLGTFEGDGUENEOIHGDGRHGJEKTHGGDFDEEGHGOEEECGYEMGSGDHDHGKPGLIEETGHGTJYHFKSGMJPIOIMJYFEJKISKPJSJOGREOJNIEISHSHGFDGDFGJKFWJTIOISECFLFGHTHSGTENJKINEOKKHTIEGOJKGHETIEIEHKGTEOGDKTJTFPGHJYIHIAISHSINJTIAIHKOJNIYIEIHJPINKOIHIADYDLDRJEHSIAIAJLKPJTJYGDHSJYISJZJNDLEEEEDIDLENDYDIDLDYDIOXIEKSJOKPIDKSJLKSJOKPIDENFYEHFPHSIDGLFDFXKPJOIHINGSGTENECHTGMESGOGUJYGTISGEEHKOFXJOKKHFEEHDIDHTIEKKISGTHTFWINGEHDFPGSGYJYJNJTESJOEEEYHFGHGYIAJEJLFDHFJTETHGGLJSGUEMIEJSJTGEJLJEHTFDFPFDIAFDFLJLHSGYIOJNKOETFYEEECJLGLGOGRKSENFYHTGTGLHTFWFXIEIHIAISHSINJTIEJYJPJLJTIYIEIHJPINKOIHIADYDLDRJEHSIAIAJLKPJTJYGDHSJYISJNJNDLEEEEDIDLEHESECDIDLPRFLWYJT
UR:BYTES/4-4/LPAAAACFAMCECYJYHYKIWKHKADLTDYDIOTIHIAISHSINJTIAJKJLJZIOJOJPJLIYINJZIHJZIDINJOEEEEDPJKJLJZHSJTHSISHSIAIAJLKPJTJYJKLSOXIEJOHSJYISJOJNDLEEEEDIDLECDYEHDIDLDYDIDLDYDIIHINJTIEIHKSAEIOHSIEIEJPIHJKJKKSDWFDFPIOJEEHEEGEJOGTGYGSIOJYENJPHFIOKOEMIAFWGYFGGEHGFGJYJLECFYJSKSINEEEMEYKPGHEOFYGRJOJSJEIOJOJPJLIYINJZIHISJKJYHSJTIEHSJPIEOXIEJOHSJYISJNJNDLEEEEDIDLECDYEHDIDLDYDIIHINJTIEIHKSADIOHSIEIEJPIHJKJKKSDWFLIMGEKKIHFXEHJPEYGMIOJEKPJLFXHGGTKKGDHKJEFXHGGUFLGUFLGSIAKNEYENENFEHSFPJEGSFPEYEMFPISGSIOJOJPJLIYINJZIHIHJYJPKPJKJYOXIEJOHSJYISIMJNDLEEEEDIDLECDYEHDIIHINJTIEIHKSAOIOHSIEIEJPIHJKJKKSDWFYEYGDGDGYGUHKFGIHETEOJTFYKNJEESENFGJSFLKPJNHFGOETGEFPEMGEETKOIMEYGMISIMIAEYJLHDKNFEINECIOJOJPJLIYINJZIHIYJZIHIOHSIAKKISJOJPJLJYJLIAJLJZKPIDINJYJYJLJLJZDPJKINIOJTIHJPDPKTHSJZJZIHJYJSJNHSJKJYIHJPFGINJTIOIHJPJOJPINJTJYISEMEOIAECIEHSDYHSVOTIMSMD
内側の正準 CBOR(16 進)とその SHA-256
内側の CBOR、1561 バイト
a6646d6f64656466756c6c676e6574776f726b676d61696e6e65746776657273696f6e01686163636f756e747387a56478707562786f78707562364361745764695a696f646d5565544470384c54356f72386e6d624b4e637579767a3757796b7356466b4b423452487743443358797576504562767141515933724150736857634d4c6f5032664d464d4b48504a345a655a58595655684c7631564d726a5043375057365665636861696e6362746366646572697665a2666368616e676563312f2a677265636569766563302f2a6673637269707466703277706b686b6163636f756e74506174686b6d2f3834272f30272f3027a56478707562786f787075623642674267736573705776455246334c48517536436e716476664576744d63516a597263527a783533514a6a537861726a3261665957634c74656f47566b79374433554b4450395179724c70725133564345436f5934397966644445484743744d4d6a393270526555735165636861696e6362746366646572697665a2666368616e676563312f2a677265636569766563302f2a6673637269707464703274726b6163636f756e74506174686b6d2f3836272f30272f3027a56478707562786f787075623643366e51774861576253727a7335745a3171376d35523963504b396559704e4d4665736958735972676331503862764c4c416574394a66486a59584b6a546f443863425273774a5858626246705867777373775650415a7a4b4d61316a5570326b566b475655614a613765636861696e6362746366646572697665a2666368616e676563312f2a677265636569766563302f2a667363726970746b703273682d703277706b686b6163636f756e74506174686b6d2f3439272f30272f3027a56478707562786f7870756236426f7366436e69667a7863467772537a51697175324442565473686b43586163764e735747594a56566868617741376434523557535747464e6269384177365a5263316272784d79574d7a473344535353536f656b6b756468556439794c62367178333954396e4d646a65636861696e6362746366646572697665a2666368616e676563312f2a677265636569766563302f2a66736372697074657032706b686b6163636f756e74506174686b6d2f3434272f30272f3027a46478707562786f787075623644436f43705375515a42326a6177716e474d455053363365504b576b77575048345455343551374c505857754e6438544d7456785272676a744573687571704b336d6468615748504673426e67683547465a614d36736933795a64557354386464594d3350776e41547465636861696e6365766d6664657269766563302f2a6b6163636f756e74506174686c6d2f3434272f3630272f3027a46478707562786f787075623644314161624e4843757065694c4d36355a52395553744d684a3176437079563458625a6479684d5a42694a58414c51746d6e39703432565451636b6f48566e38574e71533764716e4a6f6b5a4841486348476f6151676d76384434356f4e554b7836445a4d4e5a42436465636861696e6474726f6e6664657269766563302f2a6b6163636f756e74506174686d6d2f3434272f313935272f3027a365636861696e63736f6c6770726f66696c656c62697034342d736f6c616e61686163636f756e747383a46470617468706d2f3434272f353031272f30272f302765696e646578006761646472657373782c4841676b31344a704d514c6774367256677637634251464a5746746f3544717869343732755433444b70716b6770726f66696c65687374616e64617264a464706174686d6d2f3434272f353031272f302765696e646578016761646472657373782c476a4a79654331723252676b756f43574d7950596b4357534753474c637a3236364561416b4c41323741684c6770726f66696c65657472757374a464706174686a6d2f3434272f3530312765696e646578026761646472657373782c44325050515359466538336e447a6b3936467147756d5655384a41374a38766a3252686a63326f587a4569356770726f66696c65666c65676163796870726f746f636f6c75626974746f6f6c2d7369676e65722d77616c6c6574716d617374657246696e6765727072696e74683733633564613061
SHA-256(内側の CBOR)
c8db82cd92e0cf45cb6329b2429494b04a3b6bad7653c9bed1d2a5ef10f4a5fc

BitTool Signer が QR コードの下に表示する、各アカウントの最初のアドレスです。

アカウントパスアドレス
Bitcoin P2WPKHm/84'/0'/0'/0/0bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu
Bitcoin P2TRm/86'/0'/0'/0/0bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr
Bitcoin P2SH-P2WPKHm/49'/0'/0'/0/037VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf
Bitcoin P2PKHm/44'/0'/0'/0/01LqBGSKuX5yYUonjxT5qGfpUsXKYYWeabA
EVMm/44'/60'/0'/0/00x9858EfFD232B4033E47d90003D41EC34EcaEda94
TRONm/44'/195'/0'/0/0TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH
Solana standardm/44'/501'/0'/0'HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk
Solana trustm/44'/501'/0'GjJyeC1r2RgkuoCWMyPYkCWSGSGLcz266EaAkLA27AhL
Solana legacym/44'/501'D2PPQSYFe83nDzk96FqGumVU8JA7J8vj2Rhjc2oXzEi5

デコード方法​

このデコーダーは、UR 層に @ngraveio/bc-ur を、CBOR に strict モードの cborg を使っています。

examples/wallet-pairing.mjs
import { URDecoder } from '@ngraveio/bc-ur';
import { decode } from 'cborg';

export function decodeWalletPairing(frames) {
const decoder = new URDecoder();
for (const frame of frames) decoder.receivePart(frame); // any order; repeats are ignored
if (!decoder.isComplete()) throw new Error(`Keep scanning: ${Math.round(decoder.estimatedPercentComplete() * 100)}%`);
if (!decoder.isSuccess()) throw new Error(decoder.resultError());

const ur = decoder.resultUR();
if (ur.type !== 'bytes') throw new Error('Not a BitTool Signer wallet QR');
const strict = { strict: true, rejectDuplicateMapKeys: true };
const inner = decode(ur.cbor, strict); // layer 1: a CBOR byte string
if (!(inner instanceof Uint8Array)) throw new Error('ur:bytes must hold a byte string');
const pairing = decode(inner, strict); // layer 2: the text-keyed map

if (pairing.protocol !== 'bittool-signer-wallet') throw new Error('Unknown protocol');
if (pairing.version !== 1) throw new Error('Unsupported version');
if (pairing.mode !== 'full' && pairing.mode !== 'simple') throw new Error('Unsupported mode');
if (pairing.network !== 'mainnet') throw new Error('Unsupported network');
if (!/^[0-9a-f]{8}$/.test(pairing.masterFingerprint)) throw new Error('Bad fingerprint');
return pairing;
}

次にアドレスを導出します。Bitcoin は 4 つのスクリプトタイプを使います。EVM と TRON は非圧縮公開鍵を Keccak-256 でハッシュし、末尾の 20 バイトを使います(TRON はさらに 0x41 プレフィックスを付けて Base58Check でエンコードします)。Solana のアドレスはペイロードにすでに含まれています。

examples/wallet-pairing.mjs
import { HDKey } from '@scure/bip32';
import { keccak_256 } from '@noble/hashes/sha3.js';
import { sha256 } from '@noble/hashes/sha2.js';
import { secp256k1 } from '@noble/curves/secp256k1.js';
import { base58check } from '@scure/base';
import { p2wpkh, p2tr, p2sh, p2pkh } from '@scure/btc-signer';
import { getAddress } from 'ethers';

const child = (xpub, branch, index) => HDKey.fromExtendedKey(xpub).deriveChild(branch).deriveChild(index);

export function firstAddresses(pairing) {
const out = {};
for (const account of pairing.accounts) {
if (account.chain === 'btc') {
const pub = child(account.xpub, 0, 0).publicKey; // receive branch 0, index 0
out[`btc:${account.script}`] = {
p2wpkh: () => p2wpkh(pub).address,
p2tr: () => p2tr(pub.subarray(1)).address, // BIP86: untweaked x-only key
'p2sh-p2wpkh': () => p2sh(p2wpkh(pub)).address,
p2pkh: () => p2pkh(pub).address,
}[account.script]();
} else if (account.chain === 'evm' || account.chain === 'tron') {
const point = secp256k1.Point.fromBytes(child(account.xpub, 0, 0).publicKey);
const hash = keccak_256(point.toBytes(false).subarray(1)).subarray(-20);
out[account.chain] = account.chain === 'evm'
? getAddress('0x' + Buffer.from(hash).toString('hex'))
: base58check(sha256).encode(Uint8Array.from([0x41, ...hash]));
} else if (account.chain === 'sol') {
for (const sol of account.accounts) out[`sol:${sol.profile}`] = sol.address;
}
}
return out;
}

どちらの関数も、ドキュメントリポジトリ内の examples/test.mjs で上記のテストベクターに対して実行されています。

受信側のルール​

堅牢な受信側の実装では、次のことを行ってください。

  • protocol == "bittool-signer-wallet"、整数の version == 1、mode == "full"、network == "mainnet"、有効な 16 進 8 桁の masterFingerprint を 必須とする。どのチェックに失敗したかを明確に示す。
  • アカウントは位置ではなく キーで検索し、既知のアカウントが欠けている、または重複しているペイロードは拒否する。
  • 未知のフィールドは無視する(今後の v1 ペイロードで追加される可能性があります)。受け取ったバイト列のまま検証すること。未知のフィールドを取り除いて再エンコードしてから正規エンコードのチェックをすると、正当な新しいペイロードまで拒否してしまう。
  • すべての xpub を検証する:Base58Check、バージョン 0x0488B21E、深さ 3、強化(hardened)子インデックス、有効な圧縮 secp256k1 鍵であること。xprv、tpub、ypub/zpub は拒否する。
  • パスを上の表と照合する。未知のパスを黙ってデフォルトに書き換えてはいけない。
  • Solana の鍵を検証する:Base58 デコードでちょうど 32 バイトになること、有効な Ed25519 の点であること、重複がないこと。
  • 信頼できない入力として扱う:フレーム数とサイズに上限を設け、CBOR の重複キーや末尾の余分なバイトは拒否する。BitTool Signer 自身のリファレンスデコーダーは、非正準な CBOR も拒否します。

xpub の構造はチェックできますが、その出所はチェックできません。ペイロードの中に、鍵が記載どおりのフィンガープリントやパスから得られたものであることを証明する情報はありません。だからこそ、ユーザーによるアドレスの照合が必要なのです。

セキュリティに関する注意​

  • ペイロードに含まれるのは、拡張 公開 鍵とアドレスだけです。署名デバイスの認証済み公開インデックスから作成され、Vault(保管庫)が開かれることはありません。
  • これをスキャンした人は誰でも、これらのアカウントのすべてのアドレスとその全履歴を見ることができます。通常の xpub と同様に扱い、ログに記録したり、アナリティクスにアップロードしたり、クラッシュレポートに含めたりしないでください。
  • QR コードには署名がなく、受信側のウォレットを認証することもありません。チェックの役割を果たすのは、ユーザーによるアドレスの照合です。