Skip to content

Instantly share code, notes, and snippets.

@azuchi
Last active July 18, 2024 06:20
Show Gist options
  • Select an option

  • Save azuchi/8024402394fed65d58353092b1c15319 to your computer and use it in GitHub Desktop.

Select an option

Save azuchi/8024402394fed65d58353092b1c15319 to your computer and use it in GitHub Desktop.
BIP-328に従ってMuSig2の集約公開鍵から子鍵を導出して子鍵に対する署名を生成
require 'bitcoin'
# 子鍵の導出処理(Bitcoin::ExtPubkeyにメソッドがあるけど、内部の調整値が後で必要になるので最低限必要な箇所だけ抽出)
def derive(ext_key, number)
new_key = Bitcoin::ExtPubkey.new
new_key.depth = ext_key.depth + 1
new_key.number = number
new_key.parent_fingerprint = ext_key.fingerprint
data = ext_key.pub.htb << [number].pack('N')
l = Bitcoin.hmac_sha512(ext_key.chain_code, data)
left = l[0..31].bth.to_i(16)
l_priv = ECDSA::Format::IntegerOctetString.encode(left, 32)
p1 = Bitcoin::Key.new(priv_key: l_priv.bth, key_type: Bitcoin::Key::TYPES[:uncompressed]).to_point
p2 = Bitcoin::Key.new(pubkey: ext_key.pubkey, key_type: ext_key.key_type).to_point
new_key.pubkey = (p1 + p2).to_hex
new_key.chain_code = l[32..-1]
new_key.ver = ext_key.version
[new_key, l_priv.bth]
end
# 2-of-2 マルチシグ
alice = Bitcoin::Key.new(priv_key: 'e49aa7b20cec34e1d980c47a4be57978eae6748521b789bb417470c94c6c61c0')
bob = Bitcoin::Key.new(priv_key: 'c306b555ec0dbbcd9c8c831ae65c0712b32bce1ad688c04931f3f24ab16e67bd')
# 両者の公開鍵をソート
participants = Schnorr::MuSig2.sort_pubkeys([alice.pubkey, bob.pubkey])
# 集約公開鍵を計算
agg_context = Schnorr::MuSig2.aggregate(participants)
puts "agg_pubkey = #{agg_context.x_only_pubkey}"
# 集約公開鍵を拡張公開鍵へ
ext_key = Bitcoin::ExtPubkey.new
ext_key.pubkey = agg_context.q.to_hex
ext_key.depth = 0
ext_key.number = 0
ext_key.chain_code = '868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965'.htb
ext_key.parent_fingerprint = Bitcoin::ExtKey::MASTER_FINGERPRINT
# 拡張公開鍵/0/0の子鍵を導出
child_key, il_1 = derive(ext_key, 0)
child_key, il_2 = derive(child_key, 0)
# 子鍵導出の際に使用したI_Lは署名時の調整値に使用する
tweaks = [il_1, il_2]
# 調整値を適用する際のモード(調整時にx-only公開鍵を使うかプレーンな33バイトの公開鍵を使うかを決めるパラメーター)
modes = [false, false]
puts "child_key = #{child_key.key.xonly_pubkey}"
# 導出した子鍵に対する集約署名の作成
msg = SecureRandom.bytes(32) # 署名対象の任意のメッセージ
# アリスがnonceを生成
sec_nonce_alice, pub_nonce_alice = Schnorr::MuSig2.gen_nonce(pk: alice.pubkey)
# ボブがnonceを生成
sec_nonce_bob, pub_nonce_bob = Schnorr::MuSig2.gen_nonce(pk: bob.pubkey)
# 両者のpublic nonceから集約nonceを計算
agg_nonce = Schnorr::MuSig2.aggregate_nonce([pub_nonce_alice, pub_nonce_bob])
# 部分署名を生成を生成するセッションコンテキストを生成(ここで子鍵導出時の調整値をセット)
session_ctx = Schnorr::MuSig2::SessionContext.new(
agg_nonce,
participants,
msg,
tweaks,
modes
)
# アリスが元の親鍵で部分署名
sig_alice = session_ctx.sign(sec_nonce_alice, alice.priv_key)
# ボブが元の親鍵で部分署名
sig_bob = session_ctx.sign(sec_nonce_bob, bob.priv_key)
# 部分署名を集約(この集約時に調整値が加味される)
sig = session_ctx.aggregate_partial_sigs([sig_alice, sig_bob])
# 導出した子鍵に対して署名を検証
puts Schnorr.valid_sig?(msg, child_key.key.xonly_pubkey, sig.encode)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment