跳至主要内容

透過 QR Code 簽名

配對完成後,每一次簽名都只需一次往返:你的錢包顯示 請求 QR Code,BitTool Signer 顯示 回應 QR Code。

鏈請求(你 → 簽名器)回應(簽名器 → 你)頁面
Bitcoinur:crypto-psbt(亦支援 ur:psbt、BBQr、Base64/hex 文字)相同類型,並加入簽名Bitcoin
EVMur:eth-sign-requestur:eth-signatureEVM
TRONur:tron-sign-requestur:tron-signatureTRON
Solanaur:sol-sign-requestur:sol-signatureSolana

這些是公開 Keystone UR registry 中的類型。錢包層級配對格式僅用於匯入帳戶;並沒有 BitTool 專屬的簽名封裝格式,也不應自行發明。

請求的基本組成​

EVM、TRON 與 Solana 的請求是以 整數鍵 組成的 CBOR map,直接作為 UR 主體傳送(只有一層 CBOR,不同於錢包層級配對)。它們共用三個基本組成元件。

請求 ID:37(bytes)​

一個隨機識別碼,即以 CBOR 標籤 37 包住的位元組字串。BitTool Signer 會將它原封不動地複製到回應中,讓你能將回應與待處理的請求對應起來。

  • 每個請求都產生 16 個隨機位元組(使用 UUID 的位元組,而非其文字形式)。
  • sol-sign-request 與 tron-sign-request 要求正好 16 個位元組。為了相容使用較長不透明 ID 的錢包,eth-sign-request 也接受 1–64 個位元組。
  • 以位元組比對 ID。ID 相符並不代表簽名有效;你仍然必須驗證簽名。

衍生路徑:304(crypto-keypath)​

必須進行簽名的金鑰的完整路徑,加上主指紋:

304({
1: [44, true, 60, true, 0, true, 0, false, 0, false], ; m/44'/60'/0'/0/0: index, hardened, index, hardened …
2: 1942346250 ; master fingerprint 0x73c5da0a as a big-endian uint32
})
  • 各元件以 index, hardened? 交替排列。不要 再對強化索引加上 0x80000000。
  • 使用具體的葉節點路徑(…/0/5),絕不要使用帳戶路徑或 * 萬用字元。
  • 一律附上指紋(鍵 2)。少了它,比對能力會變弱,有些請求甚至完全無法比對。

使用 cborg 建構它的輔助函式:

examples/keypath.mjs
import { Tagged } from 'cborg';

/** crypto-keypath (tag 304): "m/44'/195'/0'/0/0" + "73c5da0a" →
* 304({1: [44, true, 195, true, 0, true, 0, false, 0, false], 2: 1942346250}) */
export function keypath(path, masterFingerprint) {
const components = path.replace(/^m\//, '').split('/')
.flatMap((part) => [Number.parseInt(part, 10), part.endsWith("'")]);
return new Tagged(304, new Map([[1, components], [2, Number.parseInt(masterFingerprint, 16)]]));
}

Origin:文字​

一個簡短的標籤,會向使用者顯示為請求的來源,例如 "BitTool Wallet"。最多 256 個字元。它只用於顯示,不經過驗證:BitTool Signer 絕不會把它當成任何事情的證明。

不得有額外欄位​

只接受各類型所定義的鍵:eth-sign-request 為 1–7,sol- 與 tron-sign-request 為 1–6。出現未知的鍵會使整個請求無效,因此請勿加入自己的中繼資料。

動態 QR Code​

請求的大小經常超出單一 QR Code 的容量。請將它編碼為多部分 UR,並循環播放各幀:

  • 將 UR 文字轉為大寫:UR:ETH-SIGN-REQUEST/… 可使用 QR 的英數字模式,所需位元數比位元組模式少約 30%。
  • 每個片段 200–400 位元組、每秒 3–5 幀,在手機上能穩定掃描。
  • 持續呼叫編碼器的 nextPart(),而不是重播第一輪。第一輪之後,bc-ur 編碼器會產生混合的「fountain」部分,即使漏掉幾幀,掃描器也能完成接收。

BitTool Signer 以 180 位元組的片段編碼自己的回應,因此較短的簽名會是單一靜態 QR Code,而已簽名的 PSBT 則可能是動態的。你的掃描器必須兩者都能處理。

回應​

  • 先檢查 UR 類型:eth-sign-request 的回應必須是 eth-signature,以此類推。Bitcoin 會以你送出的相同類型回應。
  • 檢查請求 ID(Bitcoin:檢查 PSBT 內的未簽名交易未被更動)。
  • 以預期的金鑰,針對你要求簽署的確切位元組,以密碼學方式驗證簽名,然後組裝並廣播。

沒有拒絕回應。 如果使用者在 BitTool Signer 中取消,就不會顯示任何內容。你的 UI 應讓使用者可以返回、再次顯示 QR Code 或重新開始,且絕不應無限期等待回覆。

保存待處理紀錄​

儲存每個請求,直到它得到回應或被放棄為止:

pendingId, walletId, chain/network, signer path + key/address,
requestId, request type, exact bytes to sign, digest,
unsigned transaction, created at, expiry (nonce / blockhash / TRON expiration),
status: awaitingSignature → signatureVerified → broadcast → confirmed | failed | cancelled

如果交易中的任何內容有所變更(手續費、nonce、blockhash、金額),請以新的 ID 建立一個 新的 請求。絕不要把同一個簽名重複用於不同的位元組,並拒絕針對已取消或已過期請求所傳回的回應。

BitTool Signer 如何找到金鑰​

你不需要告訴 BitTool Signer 要使用哪個錢包;它會將請求中的路徑、指紋以及地址或公鑰,與手機上每個錢包的公鑰進行比對來判斷。必須恰好有一個錢包相符。完整規則請參閱簽名器會檢查什麼。實務上:每個請求都送出完整路徑、指紋與地址(或公鑰),BitTool Signer 就能直接找到金鑰。