跳至主要内容

UR 與 CBOR 入門

除了純地址與 PSBT 文字之外,BitTool Signer 讀取或寫出的每個 QR Code 都是 Uniform Resource(BCR-2020-005)。你不需要自行實作 UR(請使用函式庫),但了解各層結構能讓除錯容易得多。

從 CBOR 到 QR 文字​

CBOR bytes
→ append CRC-32 (4 bytes)
→ Bytewords "minimal": each byte becomes 2 letters (first + last letter of a 4-letter word)
→ "ur:" + type + "/" + bytewords single part

例如,一個簡短的 EVM 簽名回應:

ur:eth-signature(單一部分)
UR:ETH-SIGNATURE/OEADTPDAGDHECKNDDWKIFTGLJLLEBDCEDPFMGWHTADAOHDFPJNBYWYMUYLLTIMKSWDCFWKYTSPVWGYVEGMDNJNWMLSGAOLHGDPATJEGLIOTOQZFPJNQDYNMHQDESRSIHLEHHLGDRTDYLHHUREYGMWTLBMWBKLFCTIHRKJZRHCKMSLFOYADCWJNLUAY

文字不區分大小寫。產生 QR Code 前請將它轉為大寫:這樣字元就能使用 QR 的 英數字 模式,其密度遠高於位元組模式。

多部分(動態)UR​

當酬載太大、一個 QR Code 放不下時,編碼器會將它拆分為等長的片段,並產生多個部分:

ur:<type>/<seq>-<count>/<bytewords of CBOR [seq, count, message-length, checksum, fragment]>
  • 部分 1…count 依序承載各片段(「純」部分)。
  • count 之後的部分是 fountain 部分:對一組偽隨機選取的片段計算 XOR,選取依據是由 seq 與校驗碼衍生的種子。解碼器可以利用它們重建遺漏的片段。
  • 同一工作階段中的每個部分,其 count、message-length 與 checksum(整則訊息的 CRC-32)都相同。BitTool Signer 會拒絕混合兩個工作階段的掃描。

實務上的影響:

  • 以任意順序掃描各部分;忽略重複的部分。
  • 顯示時:在迴圈中持續以 nextPart() 產生新的部分,而不是重播前 count 個部分。
  • 片段大小是 QR 密度與幀數之間的取捨。BitTool Signer 在錢包層級配對使用 400 位元組,簽名回應則使用 180 位元組。

錢包層級配對 QR Code 的第 1 幀:

ur:bytes,第 1 部分(共 4 部分)
UR:BYTES/1-4/LPADAACFAMCECYJYHYKIWKHKADLTHKAMCFOLIEJNJLIEIHIEIYKPJZJZIOJTIHJYKTJLJPJEIOJNHSINJTJTIHJYIOKOIHJPJKINJLJTADISHSIAIAJLKPJTJYJKLTONIEKSJOKPIDKSJLKSJOKPIDENFXHSJYHGIEINHTINJLIEJNGOIHGHFYJOETGSGHECJLJPETJTJNIDGRGLIAKPKKKOKNEMHGKKJEJKHFFGJEGRFWEEGMFDKTFXFYEOHDKKKPKOGDFEIDKOJSFPGYHKEOJPFPGDJKISHGIAGTGSJLGDEYIYGTFGGTGRFDGDGEEEHTIHHTHDHKHFGOISGSKOEHHFGTJPIMGDFXEMGDHGENHFIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHIAEHDLDRIOJPIHIAIHINKOIHIADYDLDRIYJKIAJPINJOJYIYJOEYKTJOJEISJEHSIAIAJLKPJTJYGDHSJYISJEJNDLETEEDIDLDYDIDLDYDIONIEKSJOKPIDKSJLKSJOKPIDENFWIOFWIOJKIHJKJOHGKOFEGMFGEOGSFDGYKPENFXJTJSIEKOIYFEKOJYGTIAGYIMHKJPIAGMKNKSECEOGYGEIMGUKSHSJPIMEYHSIYHKHGIAGSJYIHJLFLHFJEKKEMFYEOGOGRFYGDESGYKKJPGSJOJPGYEOHFFXFEFXJLHKEEESKKIYIEFYFEFDFLFXJYGTGTIMESEYJOGMIHGOJKGYIHIAISHSINJTIAIDJYIAIYIEIHJPINKOIHOEIYIAISHSJTIOIHUYSWLYTA

這些酬載中的 CBOR​

只會出現 CBOR 的一小部分子集:

主要類型用途
0 unsigned intmap 鍵、資料類型、chain ID、指紋、路徑索引
2 byte string簽名資料、金鑰、chain code、簽名、PSBT
3 text stringorigin、名稱、配對 map 的鍵與值
4 array路徑元件、描述元清單、配對帳戶
5 map所有請求、回應與金鑰結構
6 tag具型別的項目(見下方)
7 simpletrue / false(is-private、強化旗標)

請求必須使用 確定長度,不得有重複的 map 鍵,也不得有多餘的尾端位元組。浮點數、負整數與不定長度項目不會出現在這些結構中。

標籤​

標籤名稱包裝內容
37uuid請求 ID 位元組
303crypto-hdkeyHD 金鑰 map
304crypto-keypath路徑 map {1: components, 2: source-fingerprint, 3: depth}
305crypto-coin-info{1: coin type, 2: network}
400script-hashsh(…)
401witness-script-hashwsh(…)
403public-key-hashpkh(…)
404witness-public-key-hashwpkh(…)
409taproottr(…)

位元組字串與文字的區別很重要:請求 ID 是標籤 37 內的位元組,而不是文字形式的 UUID;鍵 6 中的 EVM 地址是 20 個原始位元組,而不是 "0x…"。

手動解讀請求​

EIP-1559 範例的 CBOR,附上註解:

a7 map, 7 entries
01 d8 25 50 5f1e9b2c…5a01 1: tag 37 (uuid), 16 bytes
02 58 31 02ef010c…80c0 2: 49 bytes: 0x02 ‖ rlp(unsigned EIP-1559 tx)
03 04 3: data-type 4
04 01 4: chain-id 1
05 d9 0130 a2 5: tag 304 (crypto-keypath), map 2
01 8a 18 2c f5 18 3c f5 00 f5 00 f4 00 f4 components [44,true,60,true,0,true,0,false,0,false]
02 1a 73c5da0a source-fingerprint 0x73c5da0a
06 54 9858effd…eda94 6: 20-byte address
07 6e "BitTool Wallet" 7: origin
完整的相同位元組
a701d825505f1e9b2c7d3a4e6f8a0b1c2d3e4f5a0102583102ef010c843b9aca008506fc23ac0082520894000000000000000000000000000000000000dead872386f26fc1000080c00304040105d90130a2018a182cf5183cf500f500f400f4021a73c5da0a06549858effd232b4033e47d90003d41ec34ecaeda94076e426974546f6f6c2057616c6c6574

函式庫​

語言URCBOR
JavaScript / TypeScript@ngraveio/bc-ur;具型別的類別可使用 Keystone 的 @keystonehq/bc-ur-registry*cborg
SwiftURKitURKit 的 DCBOR、SwiftCBOR
Kotlin / JavaHummingbirdHummingbird、com.upokecenter:cbor
Rusturminicbor、ciborium