Solana(sol-sign-request)
sol-sign-request
| キー | フィールド | 型 | 内容 |
|---|---|---|---|
| 1 | request-id | 37(bytes) | ちょうど 16 バイトのランダム値 |
| 2 | sign-data | bytes | sign-type 1:シリアライズした メッセージ · sign-type 2:テキストメッセージのバイト列 |
| 3 | derivation-path | 304(crypto-keypath) | 3 つのプリセットパスのいずれかと、マスターフィンガープリント |
| 4 | address | bytes (32) | 署名者の公開鍵。テキストではなく、Base58 でデコードしたアドレス |
| 5 | origin | text | ユーザーに表示される |
| 6 | sign-type | uint | 1 トランザクション · 2 メッセージ |
キーの順序が eth- や tron-sign-request と異なる点に注意してください。パスはキー 3、タイプはキー 6 です。
@keystonehq/bc-ur-registry-sol の SolSignRequest.constructSOLRequest() は、アドレスを Base58 ではなく、32 バイトの鍵の 16 進 文字列として受け取ります。Base58 のアドレスを渡すとキー 4 が誤った値になり、リクエストは照合されません。
sign-type 1:トランザクション
sign-data はトランザクションの メッセージ 部分、つまり署名の対象となるバイト列です。@solana/web3.js v1 なら transaction.serializeMessage()、VersionedTransaction なら message.serialize() で得られます。(署名数とスロットから始まる)トランザクション全体でも、ハッシュでもありません。
| ルール | 詳細 |
|---|---|
| メッセージのバージョン | Legacy と v0 |
| アドレスルックアップテーブル | 非対応。 署名デバイスはオフラインでテーブルの内容を検証できません。v0 メッセージは静的なキーのみでコンパイルし、結果が大きすぎる場合は送信しないでください。 |
| 署名者 | キー 4 の鍵は、メッセージの 必須署名者 のいずれかである必要があります(手数料支払者に限らず、どのスロットでも可)。 |
| サイズ | 命令は 1〜64 個。署名済みトランザクションは 1,232 バイトのパケット上限に収まる必要があります。 |
| Durable nonce | 使用できますが、確認画面で警告されます(このようなトランザクションは無期限に有効なままです)。 |
デコードされるプログラム:System(transfer、create account、nonce、assign、allocate)、SPL Token と Token-2022(transfer、transferChecked、approve、mint、burn、close、set authority、sync native)、Associated Token Account(アドレスのチェック付き)、Compute Budget、Memo、Stake(delegate、split、withdraw、deactivate)、そして一般的な NFT プログラム(Metaplex Token Metadata、Bubblegum、Core)。Token-2022 の拡張機能と Squads の提案命令は部分的にデコードされます。その他のプログラムは生の命令として表示され、明示的な同意が必要です。
sign-type 2:メッセージ
sign-data は制御文字を含まない 1〜1,024 バイトの UTF-8 テキストです。トランザクションヘッダーとして読み取れるバイト列は拒否されるため、トランザクションを「メッセージ」と偽って署名させることはできません。BitTool Signer はメッセージのバイト列に直接 Ed25519 で署名します(プレフィックスもハッシュもなし)。これは Solana のウォレットアダプターの signMessage が想定する方式です。
sol-signature
sol-signature = {
1: 37(request-id bytes),
2: signature ; 64 bytes, Ed25519
}
これはデタッチド署名です。トランザクションを完成させる手順は次のとおりです。
- リクエスト ID を確認し、期待する公開鍵で、メッセージのバイト列そのものに対する署名を検証します。
- メッセージの必須署名者の中から署名者のインデックスを見つけ、署名を そのスロット に入れます。スロット 0 とは限りません。
- 他のスロットにすでにある署名は保持し、必須スロットがすべて埋まったらシリアライズしてブロードキャストします。
ブロードキャスト前に blockhash の有効期限が切れた場合は、新しいメッセージと新しいリクエストを作成してください。署名を別の blockhash のメッセージに移すことはできません。
コード
@solana/web3.js v1 と Keystone の @keystonehq/bc-ur-registry-sol を使う例です。
import { randomUUID } from 'node:crypto';
import { PublicKey, Transaction, SystemProgram } from '@solana/web3.js';
import { SolSignRequest, SignType, SolSignature } from '@keystonehq/bc-ur-registry-sol';
import { URDecoder } from '@ngraveio/bc-ur';
import { ed25519 } from '@noble/curves/ed25519.js';
export function buildSolTransfer({ from, to, lamports, recentBlockhash }) {
const tx = new Transaction({ feePayer: new PublicKey(from), recentBlockhash });
tx.add(SystemProgram.transfer({ fromPubkey: new PublicKey(from), toPubkey: new PublicKey(to), lamports }));
return tx;
}
export function buildSolRequest(tx, { path, masterFingerprint, address }) {
const requestId = randomUUID();
const request = SolSignRequest.constructSOLRequest(
tx.serializeMessage(), // message bytes, not the whole transaction
path, // "m/44'/501'/0'/0'"
masterFingerprint, // "73c5da0a"
SignType.Transaction, // 1 (2 = text message)
requestId,
new PublicKey(address).toBuffer().toString('hex'), // this library wants hex, not Base58
'BitTool Wallet',
);
const encoder = request.toUREncoder(400);
const frames = [];
for (let i = 0; i < encoder.fragmentsLength; i++) frames.push(encoder.nextPart().toUpperCase());
return { requestId, frames };
}
export function attachSolSignature(frames, tx, { requestId, address }) {
const decoder = new URDecoder();
for (const frame of frames) decoder.receivePart(frame);
if (!decoder.isSuccess()) throw new Error('Incomplete or invalid QR');
const ur = decoder.resultUR();
if (ur.type !== 'sol-signature') throw new Error(`Expected sol-signature, got ${ur.type}`);
const response = SolSignature.fromCBOR(ur.cbor);
if (Buffer.from(response.getRequestId()).toString('hex') !== requestId.replace(/-/g, '')) throw new Error('Signature is for another request');
const signature = Buffer.from(response.getSignature()); // 64-byte Ed25519 signature
const signer = new PublicKey(address);
if (!ed25519.verify(signature, tx.serializeMessage(), signer.toBytes())) throw new Error('Bad signature');
tx.addSignature(signer, signature); // goes into this key's own slot
return tx.serialize(); // throws if another required signature is missing
}
テストベクター
すべて standard プリセット m/44'/501'/0'/0'(HAgk14JpMQLgt6rVgv7cBQFJWFto5Dqxi472uT3DKpqk)から生成しています。blockhash は合成したものです。
- SOL 送金
- SPL トークン(USDC)
- メッセージ
レガシーメッセージで 0.25 SOL(250,000,000 lamports)を送金します。アカウントは、手数料支払者兼署名者、受取人、System Program です。
リクエスト(ウォレット → BitTool Signer)
{
"1 request-id": {
"uuid (tag 37)": "5f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0b"
},
"2 sign-data": "01000103f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f77e8c088760bfde1dddcf32c17f209b8242ee52aaf131facd88d0ea2c6d0b06f20000000000000000000000000000000000000000000000000000000000000000cc490e928cd2e3873bb343fc95da33179ca60f4dbf46c2c36e91299d55d4e6b901020200010c0200000080b2e60e00000000",
"3 derivation-path": {
"crypto-keypath (tag 304)": {
"1 components": [
44,
true,
501,
true,
0,
true,
0,
true
],
"2 source-fingerprint": 1942346250
}
},
"4 address": "f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f7",
"5 origin": "BitTool Wallet",
"6 sign-type": 1
}
QR テキストと生の CBOR を表示
UR:SOL-SIGN-REQUEST/1-2/LPADAOCSZOCYLRESBDUEHDKBOLADTPDAGDHECKNDDWKIFTGLJLLEBDCEDPFMGWHTBDAOHDMTADAEADAXWTENDIIDFGOSHPNTVLEENNTYDNBZVOEYYNGYMYSABSHESNGWCAIEVSCTNDTDHDYLKBLKAYLTHNRSUECAUTTKEYSELBCXNDLFFWWYGMPKWNEHZSSNLOTIWDDWJNBDAMWZAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAESFGAWZEETBWS
UR:SOL-SIGN-REQUEST/2-2/LPAOAOCSZOCYLRESBDUEHDKBBAMOLKTDVLLTFRQDFXZTMDTNEOCHNSOLBSGTRSFGSASRJTMEDTNTGOTYVARHADAOAOAEADBNAOAEAEAELAPRVABAAEAEAEAEAXTAADDYOEADLOCSDWYKCFADYKYKAEYKAEYKAOCYJKSKTNBKAAHDCXWTENDIIDFGOSHPNTVLEENNTYDNBZVOEYYNGYMYSABSHESNGWCAIEVSCTNDTDHDYLAHJTFWINJYGHJLJLJZCXHGHSJZJZIHJYAMADAEWYHKLNHH
a601d825505f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0b02589601000103f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f77e8c088760bfde1dddcf32c17f209b8242ee52aaf131facd88d0ea2c6d0b06f20000000000000000000000000000000000000000000000000000000000000000cc490e928cd2e3873bb343fc95da33179ca60f4dbf46c2c36e91299d55d4e6b901020200010c0200000080b2e60e0000000003d90130a20188182cf51901f5f500f500f5021a73c5da0a045820f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f7056e426974546f6f6c2057616c6c65740601
レスポンス(BitTool Signer → ウォレット)
{
"request-id": {
"uuid": "5f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0b"
},
"signature": "99b9a42fcb8eead46ae74c5e792098c247c21ab4d791af70a26d8392986d714fd4543af8f121c355ca32f747f321d52fc3856cf8b4ae678045a34648d924940c"
}
QR テキストと生の CBOR を表示
UR:SOL-SIGNATURE/OEADTPDAGDHECKNDDWKIFTGLJLLEBDCEDPFMGWHTBDAOHDFZNLRHOXDLSBMNWDTYIMVDGSHYKKCXMKSAFLSACYQZTSMEPEJOOEJNLSMOMKJNJSGWTYGHFTYAWNCLSRGOSGEYYLFLWFCLTLDLSRLPJZYAQZPLIOLAFEOTFGFDTADKMWBNDTZCBGGE
a201d825505f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0b02584099b9a42fcb8eead46ae74c5e792098c247c21ab4d791af70a26d8392986d714fd4543af8f121c355ca32f747f321d52fc3856cf8b4ae678045a34648d924940c
ウォレット側で組み立てた結果
45G7cB2HaLBrWEuZxiWR2dFYu52Cje6d2vv5GnCaZ7YSc6ifhFVdFTGpJSk1PU7tSTPHjg3H4uufoe3XPmTRQiJP
AZm5pC/LjurUaudMXnkgmMJHwhq015GvcKJtg5KYbXFP1FQ6+PEhw1XKMvdH8yHVL8OFbPi0rmeARaNGSNkklAwBAAED8DYnYkanW53jNJ7UKxXiMvZRj8IPX81PHWToH5vSWPd+jAiHYL/eHd3PMsF/IJuCQu5SqvEx+s2I0OosbQsG8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzEkOkozS44c7s0P8ldozF5ymD02/RsLDbpEpnVXU5rkBAgIAAQwCAAAAgLLmDgAAAAA=
TransferChecked(小数点以下 6 桁)で、署名者の関連トークンアカウント 5N3f1tj9v1vc5TUZ8S7mCAnVmjVKrfnzXWhxLaxyZAgt から受取人の FGETo8T8wMcN2wCjav8VK6eh3dLk63evNDPxzLSJra8B へ 12.5 USDC を送金します。
リクエスト(ウォレット → BitTool Signer)
{
"1 request-id": {
"uuid (tag 37)": "5f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0c"
},
"2 sign-data": "01000205f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f740d2f27c461f29e9feba1ab8acd39496a9c65b0ae62a713f01f941a993bf55edd3ea8cf5acaca8cd05207512175c43cef54a5dd99ede20a16b55253738f397dcc6fa7af3bedbad3a3d65f36aabc97431b1bbe4c2d2f6e0e47ca60203452f5d6106ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9cc490e928cd2e3873bb343fc95da33179ca60f4dbf46c2c36e91299d55d4e6b9010404010302000a0c20bcbe000000000006",
"3 derivation-path": {
"crypto-keypath (tag 304)": {
"1 components": [
44,
true,
501,
true,
0,
true,
0,
true
],
"2 source-fingerprint": 1942346250
}
},
"4 address": "f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f7",
"5 origin": "BitTool Wallet",
"6 sign-type": 1
}
QR テキストと生の CBOR を表示
UR:SOL-SIGN-REQUEST/1-2/LPADAOCFADFRCYGEKNLGGDHDNNOLADTPDAGDHECKNDDWKIFTGLJLLEBDCEDPFMGWHTBNAOHDTBADAEAOAHWTENDIIDFGOSHPNTVLEENNTYDNBZVOEYYNGYMYSABSHESNGWCAIEVSCTNDTDHDYLFZTDWZKEFGCTDTWLZERDCYROPSTEMWMTPTSWHPBKVADRJSFHADYTFPPTMURSGOWETEWDLKYKPSPSPDSNAHCXKPBGCHHHFXTOYKGEHLTANNUECXOYJEGODAEMETWFMSUOSWZSKNWFRNUYPMFTFSIHWFIMPYSOJYEHPARKVESATDYNVTVEKEOLAOAXFEDLHLHSAMUTSEPTJPLD
UR:SOL-SIGN-REQUEST/2-2/LPAOAOCFADFRCYGEKNLGGDHDNNYNVYTSIHOYMUTASBVYFGTOWMKKPSCEQZLPWEHEHPEMMEFTLKYKLPKBZMAEPTSFGABAMOLKTDVLLTFRQDFXZTMDTNEOCHNSOLBSGTRSFGSASRJTMEDTNTGOTYVARHADAAAAADAXAOAEBKBNCXRFRNAEAEAEAEAEAMAXTAADDYOEADLOCSDWYKCFADYKYKAEYKAEYKAOCYJKSKTNBKAAHDCXWTENDIIDFGOSHPNTVLEENNTYDNBZVOEYYNGYMYSABSHESNGWCAIEVSCTNDTDHDYLAHJTFWINJYGHJLJLJZCXHGHSJZJZIHJYAMADAEHTNETOQZ
a601d825505f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0c0258d601000205f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f740d2f27c461f29e9feba1ab8acd39496a9c65b0ae62a713f01f941a993bf55edd3ea8cf5acaca8cd05207512175c43cef54a5dd99ede20a16b55253738f397dcc6fa7af3bedbad3a3d65f36aabc97431b1bbe4c2d2f6e0e47ca60203452f5d6106ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9cc490e928cd2e3873bb343fc95da33179ca60f4dbf46c2c36e91299d55d4e6b9010404010302000a0c20bcbe00000000000603d90130a20188182cf51901f5f500f500f5021a73c5da0a045820f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f7056e426974546f6f6c2057616c6c65740601
レスポンス(BitTool Signer → ウォレット)
{
"request-id": {
"uuid": "5f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0c"
},
"signature": "3ad36b0673be3dbb71ec3b8fd346e2a96692f9ffb7b0e55ccdffe2d43f7395a447487e05522b62fed93351603483e1e2278e2193185e39761ae62d901bd1880e"
}
QR テキストと生の CBOR を表示
UR:SOL-SIGNATURE/OEADTPDAGDHECKNDDWKIFTGLJLLEBDCEDPFMGWHTBNAOHDFZFTTEJEAMJKRNFSRKJSWPFRMYTEFGVOPTIYMOYTZMRLPFVWHHSNZMVOTYFHJKMDOXFLFDKBAHGMDNIDZETAEOGYHNEELSVYVODIMNCLMUCSHYESKOCYVADPMHCWTTLOBABBPLAXRD
a201d825505f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0c0258403ad36b0673be3dbb71ec3b8fd346e2a96692f9ffb7b0e55ccdffe2d43f7395a447487e05522b62fed93351603483e1e2278e2193185e39761ae62d901bd1880e
ウォレット側で組み立てた結果
2BDTaXgjMPuTPcFZ7v87D5Vdcn9uFky9RixC5HbiStvCDGjGasBZuymph2oEEg2TmftbyB8EgTPdtHGgE1Rjmrvh
ATrTawZzvj27cew7j9NG4qlmkvn/t7DlXM3/4tQ/c5WkR0h+BVIrYv7ZM1FgNIPh4ieOIZMYXjl2GuYtkBvRiA4BAAIF8DYnYkanW53jNJ7UKxXiMvZRj8IPX81PHWToH5vSWPdA0vJ8Rh8p6f66Gris05SWqcZbCuYqcT8B+UGpk79V7dPqjPWsrKjNBSB1EhdcQ871Sl3Znt4goWtVJTc485fcxvp6877brTo9ZfNqq8l0MbG75MLS9uDkfKYCA0UvXWEG3fbh12Whk9nL4UbO63msHLSF7V9bN5E6jPWFfv8AqcxJDpKM0uOHO7ND/JXaMxecpg9Nv0bCw26RKZ1V1Oa5AQQEAQMCAAoMILy+AAAAAAAG
サインイン用のメッセージです。signatureBase58 は、dApp が signMessage から受け取ることを想定している形式です。
リクエスト(ウォレット → BitTool Signer)
{
"1 request-id": {
"uuid (tag 37)": "5f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0d"
},
"2 sign-data": "5369676e20696e20746f204578616d706c6520417070207769746820536f6c616e612e204e6f6e63653a2037516d327850",
"3 derivation-path": {
"crypto-keypath (tag 304)": {
"1 components": [
44,
true,
501,
true,
0,
true,
0,
true
],
"2 source-fingerprint": 1942346250
}
},
"4 address": "f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f7",
"5 origin": "BitTool Wallet",
"6 sign-type": 2
}
QR テキストと生の CBOR を表示
UR:SOL-SIGN-REQUEST/OLADTPDAGDHECKNDDWKIFTGLJLLEBDCEDPFMGWHTBTAOHDEHGUINIOJTCXINJTCXJYJLCXFEKSHSJNJOJZIHCXFPJOJOCXKTINJYISCXGUJLJZHSJTHSDMCXGLJLJTIAIHFTCXEMGYJNEYKSGDAXTAADDYOEADLOCSDWYKCFADYKYKAEYKAEYKAOCYJKSKTNBKAAHDCXWTENDIIDFGOSHPNTVLEENNTYDNBZVOEYYNGYMYSABSHESNGWCAIEVSCTNDTDHDYLAHJTFWINJYGHJLJLJZCXHGHSJZJZIHJYAMAORFCXVWPK
a601d825505f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0d0258315369676e20696e20746f204578616d706c6520417070207769746820536f6c616e612e204e6f6e63653a2037516d32785003d90130a20188182cf51901f5f500f500f5021a73c5da0a045820f036276246a75b9de3349ed42b15e232f6518fc20f5fcd4f1d64e81f9bd258f7056e426974546f6f6c2057616c6c65740602
レスポンス(BitTool Signer → ウォレット)
{
"request-id": {
"uuid": "5f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0d"
},
"signature": "0fad9739ab61e6c9de54b5aa59a00568fd4c12f4e7ac8d6624595c0df3f2f390b7a19b73f9ea08650115a672a81b2e8eb16810a5766519064cbd7f4fc9bb3b01"
}
QR テキストと生の CBOR を表示
UR:SOL-SIGNATURE/OEADTPDAGDHECKNDDWKIFTGLJLLEBDCEDPFMGWHTBTAOHDFZBSPMMSESPYHSVASOUEGHREPKHKNBAHISZCGSBGWKVDPSLGIYDKHKHHBTWFWZWFMHRLOYNDJKYTWDAYIHADBZOLJPPDCWDMMNPAISBEONKOIHCFAMGSRYLBGWSORKFRADOXRPNYAT
a201d825505f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0d0258400fad9739ab61e6c9de54b5aa59a00568fd4c12f4e7ac8d6624595c0df3f2f390b7a19b73f9ea08650115a672a81b2e8eb16810a5766519064cbd7f4fc9bb3b01
ウォレット側で組み立てた結果
KBTsdycDs2SLRqv9vaEaq7fKYeSWTRnohspq2ngCgAZhR38CYqB2GqykyQkphhpkYYsCoeNEU5YWiu2hdM2ajwe
複数署名者のトランザクション
トランザクションに複数の署名者が必要な場合(SPL マルチシグ、Squads の提案承認、送信者とは別の手数料支払者など)は、BitTool Signer の鍵ごとに 1 つのリクエストを、それぞれの鍵のパスとアドレスを付けて送ります。各レスポンスが 1 つのスロットを埋めます。Squads はプログラムレベルのマルチシグです。署名デバイスに見えるのは提案を承認する命令であって、提案の内容ではありません。確認画面にもそのことが表示されます。