Bitcoin API

BitcoinApp クラスは、Bitcoin のアドレス取得、メッセージ署名(BIP-137)、PSBT(Partially Signed Bitcoin Transaction)署名を提供します。

インポート

import {
  BitcoinApp,
  BTC_MAINNET_PATH,
  BTC_TESTNET_PATH,
  type BtcMessageSignature,
  type InputSigningPath,
} from '@openloop/sdk-core'

初期化

const btc = new BitcoinApp(transport)

メソッド一覧

メソッド 説明
getAddress(testnet?) SegWit アドレス取得(mainnet/testnet 自動切替)
getAddressWithPath(path) 任意の BIP32 パスでアドレス取得
getAccountXpub(coinType) アカウントレベル拡張公開鍵の取得
signMessage(message, path) BIP-137 メッセージ署名
signPsbt(psbt) PSBT 署名(バイナリ入出力)
signPsbtHex(psbt) PSBT 署名(hex 入出力)
signPsbtWithPaths(psbt, inputPaths) 入力パス指定付き PSBT 署名

getAddress

SegWit(bech32)アドレスを取得します。

async getAddress(testnet?: boolean): Promise<{
  publicKey: string   // 非圧縮公開鍵 (hex)
  address: string     // bech32 アドレス ("bc1..." or "tb1...")
  chainCode: string   // チェーンコード (hex, 32 bytes)
}>

パラメータ

名前 デフォルト 説明
testnet boolean false true: testnet パス (84'/1'/0'/0/0)

デフォルトパス

ネットワーク パス 定数
Mainnet 84'/0'/0'/0/0 BTC_MAINNET_PATH
Testnet 84'/1'/0'/0/0 BTC_TESTNET_PATH

使用例

// Mainnet
const { address } = await btc.getAddress()
// address: "bc1q..."

// Testnet
const { address } = await btc.getAddress(true)
// address: "tb1q..."

getAddressWithPath

任意の BIP32 パスでアドレスを取得します。マルチアカウントやお釣り用アドレスの取得に使用します。

async getAddressWithPath(path: string): Promise<{
  publicKey: string
  address: string
  chainCode: string
}>

使用例

// 2番目のアカウントの最初のアドレス
const { address } = await btc.getAddressWithPath("84'/0'/1'/0/0")

// お釣り用アドレス
const { address } = await btc.getAddressWithPath("84'/0'/0'/1/0")

// "m/" プレフィックスも可
const { address } = await btc.getAddressWithPath("m/84'/0'/0'/0/5")

getAccountXpub

アカウントレベルの拡張公開鍵(m/84'/coin'/0')を取得します。ソフトウェア側で子鍵導出を行い、残高確認やアドレス生成に使用します。

async getAccountXpub(coinType: number): Promise<{
  publicKey: string   // 圧縮公開鍵 (hex, 33 bytes)
  chainCode: string   // チェーンコード (hex, 32 bytes)
}>

パラメータ

名前 説明
coinType number 0: mainnet, 1: testnet

使用例

const { publicKey, chainCode } = await btc.getAccountXpub(0)  // mainnet

signMessage

BIP-137 形式でメッセージに署名します。

async signMessage(
  message: string,
  path: string
): Promise<BtcMessageSignature>

パラメータ

名前 説明
message string UTF-8 メッセージ
path string BIP32 パス(例: "84'/0'/0'/0/0"

返り値: BtcMessageSignature

フィールド 説明
v number リカバリー ID (35-38: P2WPKH native SegWit)
r string R 値 (32 bytes hex)
s string S 値 (32 bytes hex)
signature string 完全な署名 (65 bytes: V || R || S) の Base64

使用例

const sig = await btc.signMessage('Hello, Bitcoin!', BTC_MAINNET_PATH)
console.log(sig.signature) // Base64 encoded signature

signPsbt

PSBT(Partially Signed Bitcoin Transaction)に署名します。PSBT 内の BIP32_DERIVATION フィールドからパスを自動検出します。

async signPsbt(psbt: Uint8Array | string): Promise<Uint8Array>

パラメータ

名前 説明
psbt Uint8Array \| string PSBT バイナリデータまたは hex 文字列

返り値

署名済み PSBT のバイナリデータ (Uint8Array)


signPsbtHex

hex 文字列での PSBT 署名。入出力ともに hex 文字列です。

async signPsbtHex(psbt: string): Promise<string>

使用例

const signedHex = await btc.signPsbtHex('70736274ff...')

signPsbtWithPaths

各入力の BIP32 パスを明示的に指定して PSBT に署名します。PSBT に BIP32_DERIVATION フィールドがない場合や、特定の入力のみ署名したい場合に使用します。

async signPsbtWithPaths(
  psbt: string,
  inputPaths: InputSigningPath[]
): Promise<string>

パラメータ

名前 説明
psbt string PSBT hex 文字列
inputPaths InputSigningPath[] 入力ごとのパス指定

InputSigningPath

フィールド 説明
index number PSBT の入力インデックス
path string BIP32 パス(例: "84'/0'/0'/0/5"

使用例

const signedHex = await btc.signPsbtWithPaths('70736274ff...', [
  { index: 0, path: "84'/0'/0'/0/0" },
  { index: 1, path: "84'/0'/0'/0/3" },
  { index: 2, path: "84'/0'/0'/1/0" }, // お釣り
])

PSBT プロトコル詳細

PSBT 署名はデバイスとの 3 フェーズ通信で行われます。

Diagram 0

Phase 1: PSBT 送信

チャンク P1 データ形式
最初 0x00 [4B total_size][1B num_paths][paths...][psbt_data...]
継続 0x80 [psbt_data...]
最終 0xFF [remaining_data] → デバイスで署名開始

Phase 2: デバイスで署名

ユーザーがデバイス画面でトランザクション内容を確認し、承認ボタンを押します。

Phase 3: 署名済み PSBT 取得

レスポンス ステータスワード 意味
[signed_psbt_chunk] 0x61XX 続きデータあり(次のチャンクを要求)
[signed_psbt_chunk] 0x9000 最終チャンク(取得完了)

チャンクインデックスを 0 から順にインクリメントし、0x9000 が返るまで繰り返します。


bitcoinjs-lib 連携

import * as bitcoin from 'bitcoinjs-lib'
import { BitcoinApp, BTC_MAINNET_PATH } from '@openloop/sdk-core'
import { WebHidTransport } from '@openloop/transport-webhid'

const transport = await WebHidTransport.connect()
const btc = new BitcoinApp(transport)

// アドレス取得
const { address, publicKey } = await btc.getAddress()

// PSBT 作成
const psbt = new bitcoin.Psbt({ network: bitcoin.networks.bitcoin })
psbt.addInput({
  hash: 'txid...',
  index: 0,
  witnessUtxo: {
    script: Buffer.from('0014...', 'hex'),
    value: 50000,
  },
})
psbt.addOutput({
  address: 'bc1q...',
  value: 40000,
})

// PSBT をデバイスで署名
const psbtHex = psbt.toHex()
const signedHex = await btc.signPsbtWithPaths(psbtHex, [
  { index: 0, path: BTC_MAINNET_PATH },
])

// 署名済み PSBT を読み込み
const signedPsbt = bitcoin.Psbt.fromHex(signedHex)
signedPsbt.finalizeAllInputs()

// ブロードキャスト用 raw transaction
const rawTx = signedPsbt.extractTransaction().toHex()

次のステップ