Skip to content

Commit

Permalink
Update musig2 api
Browse files Browse the repository at this point in the history
  • Loading branch information
sstone committed Jan 17, 2024
1 parent bc053e6 commit 64fbf79
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ data class SharedTransaction(
val serverNonce = receivedNonces[input.serialId]
require(serverNonce != null) { "missing server nonce for input ${input.serialId}" }
val commonNonce = IndividualNonce.aggregate(listOf(userNonce.second, serverNonce))
val psig = keyManager.swapInOnChainWallet.signSwapInputUser(unsignedTx, i, previousOutputs, userNonce.first, commonNonce).getOrThrow()
val psig = keyManager.swapInOnChainWallet.signSwapInputUser(unsignedTx, i, previousOutputs, userNonce.first, commonNonce)
TxSignatures.Companion.PartialSignature(psig, commonNonce)
}
}.filterNotNull()
Expand Down Expand Up @@ -464,7 +464,7 @@ data class SharedTransaction(
require(serverNonce != null) { "missing server nonce for input ${input.serialId}" }
val commonNonce = IndividualNonce.aggregate(listOf(userNonce.second, serverNonce))
val swapInProtocol = SwapInProtocol(input.swapInParams.userKey, serverKey.publicKey(), input.swapInParams.userRefundKey, input.swapInParams.refundDelay)
val psig = swapInProtocol.signSwapInputServer(unsignedTx, i, previousOutputs, commonNonce, serverKey, userNonce.first).getOrThrow()
val psig = swapInProtocol.signSwapInputServer(unsignedTx, i, previousOutputs, commonNonce, serverKey, userNonce.first)
TxSignatures.Companion.PartialSignature(psig, commonNonce)
}
}.filterNotNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ interface KeyManager {
return legacySwapInProtocol.signSwapInputUser(fundingTx, index, parentTxOuts[fundingTx.txIn[index].outPoint.index.toInt()] , userPrivateKey)
}

fun signSwapInputUser(fundingTx: Transaction, index: Int, parentTxOuts: List<TxOut>, userNonce: SecretNonce, commonNonce: AggregatedNonce): Result<ByteVector32> {
fun signSwapInputUser(fundingTx: Transaction, index: Int, parentTxOuts: List<TxOut>, userNonce: SecretNonce, commonNonce: AggregatedNonce): ByteVector32 {
return swapInProtocol.signSwapInputUser(fundingTx, index, parentTxOuts, userPrivateKey, userNonce, commonNonce)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class SwapInProtocol(val userPublicKey: PublicKey, val serverPublicKey: PublicKe
// the redeem script is just the refund script. it is generated from this policy: and_v(v:pk(user),older(refundDelay))
// it does not depend upon the user's or server's key, just the user's refund key and the refund delay
val redeemScript = listOf(OP_PUSHDATA(userRefundKey.xOnly()), OP_CHECKSIGVERIFY, OP_PUSHDATA(Script.encodeNumber(refundDelay)), OP_CHECKSEQUENCEVERIFY)
private val scriptTree = ScriptTree.Leaf(ScriptLeaf(0, Script.write(redeemScript).byteVector(), Script.TAPROOT_LEAF_TAPSCRIPT))
private val merkleRoot = ScriptTree.hash(scriptTree)
private val scriptTree = ScriptTree.Leaf(0, redeemScript)
private val merkleRoot = scriptTree.hash()

// the internal pubkey is the musig2 aggregation of the user's and server's public keys: it does not depend upon the user's refund's key
private val internalPubKeyAndCache = KeyAggCache.Companion.add(listOf(userPublicKey, serverPublicKey), null)
Expand All @@ -35,7 +35,6 @@ class SwapInProtocol(val userPublicKey: PublicKey, val serverPublicKey: PublicKe
private val parity = commonPubKeyAndParity.second
val pubkeyScript: List<ScriptElt> = Script.pay2tr(commonPubKey)

private val executionData = Script.ExecutionData(annex = null, tapleafHash = merkleRoot)
private val controlBlock = byteArrayOf((Script.TAPROOT_LEAF_TAPSCRIPT + (if (parity) 1 else 0)).toByte()) + internalPubKey.value.toByteArray()

fun isMine(txOut: TxOut): Boolean = txOut.publicKeyScript.contentEquals(Script.write(pubkeyScript))
Expand All @@ -46,24 +45,24 @@ class SwapInProtocol(val userPublicKey: PublicKey, val serverPublicKey: PublicKe

fun witnessRefund(userSig: ByteVector64): ScriptWitness = ScriptWitness.empty.push(userSig).push(redeemScript).push(controlBlock)

fun signSwapInputUser(fundingTx: Transaction, index: Int, parentTxOuts: List<TxOut>, userPrivateKey: PrivateKey, userNonce: SecretNonce, commonNonce: AggregatedNonce): Result<ByteVector32> {
fun signSwapInputUser(fundingTx: Transaction, index: Int, parentTxOuts: List<TxOut>, userPrivateKey: PrivateKey, userNonce: SecretNonce, commonNonce: AggregatedNonce): ByteVector32 {
require(userPrivateKey.publicKey() == userPublicKey)
val txHash = Transaction.hashForSigningSchnorr(fundingTx, index, parentTxOuts, SigHash.SIGHASH_DEFAULT, SigVersion.SIGVERSION_TAPROOT)
val cache1 = cache.tweak(internalPubKey.tweak(Crypto.TaprootTweak.ScriptTweak(merkleRoot)), true).first
val session = Session.build(commonNonce, txHash, cache1)
return kotlin.runCatching { session.sign(userNonce, userPrivateKey, cache1) }
return session.sign(userNonce, userPrivateKey, cache1)
}

fun signSwapInputRefund(fundingTx: Transaction, index: Int, parentTxOuts: List<TxOut>, userPrivateKey: PrivateKey): ByteVector64 {
val txHash = Transaction.hashForSigningSchnorr(fundingTx, index, parentTxOuts, SigHash.SIGHASH_DEFAULT, SigVersion.SIGVERSION_TAPSCRIPT, executionData)
val txHash = Transaction.hashForSigningSchnorr(fundingTx, index, parentTxOuts, SigHash.SIGHASH_DEFAULT, SigVersion.SIGVERSION_TAPSCRIPT, merkleRoot)
return Crypto.signSchnorr(txHash, userPrivateKey, Crypto.SchnorrTweak.NoTweak)
}

fun signSwapInputServer(fundingTx: Transaction, index: Int, parentTxOuts: List<TxOut>, commonNonce: AggregatedNonce, serverPrivateKey: PrivateKey, serverNonce: SecretNonce): Result<ByteVector32> {
fun signSwapInputServer(fundingTx: Transaction, index: Int, parentTxOuts: List<TxOut>, commonNonce: AggregatedNonce, serverPrivateKey: PrivateKey, serverNonce: SecretNonce): ByteVector32 {
val txHash = Transaction.hashForSigningSchnorr(fundingTx, index, parentTxOuts, SigHash.SIGHASH_DEFAULT, SigVersion.SIGVERSION_TAPROOT)
val cache1 = cache.tweak(internalPubKey.tweak(Crypto.TaprootTweak.ScriptTweak(merkleRoot)), true).first
val session = Session.build(commonNonce, txHash, cache1)
return kotlin.runCatching { session.sign(serverNonce, serverPrivateKey, cache1) }
return session.sign(serverNonce, serverPrivateKey, cache1)
}

fun session(fundingTx: Transaction, index: Int, parentTxOuts: List<TxOut>, commonNonce: AggregatedNonce): Session {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,10 +497,10 @@ class TransactionsTestsCommon : LightningTestSuite() {

// we have a simple script tree with 2 leaves
val scriptTree = ScriptTree.Branch(
ScriptTree.Leaf(ScriptLeaf(0, write(mutualScript).byteVector(), Script.TAPROOT_LEAF_TAPSCRIPT)),
ScriptTree.Leaf(ScriptLeaf(1, write(refundScript).byteVector(), Script.TAPROOT_LEAF_TAPSCRIPT))
ScriptTree.Leaf(0, mutualScript),
ScriptTree.Leaf(1, refundScript)
)
val merkleRoot = ScriptTree.hash(scriptTree)
val merkleRoot = scriptTree.hash()

// we choose a pubkey that does not have a corresponding private key: our swap-in tx can only be spent through the script path, not the key path
val internalPubkey = XonlyPublicKey(PublicKey.fromHex("0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"))
Expand All @@ -524,9 +524,9 @@ class TransactionsTestsCommon : LightningTestSuite() {
// we want to spend the left leave of the tree, so we provide the hash of the right leave (to be able to recompute the merkle root of the tree)
val controlBlock = byteArrayOf((Script.TAPROOT_LEAF_TAPSCRIPT + (if (parity) 1 else 0)).toByte()) +
internalPubkey.value.toByteArray() +
ScriptTree.hash(scriptTree.right).toByteArray()
scriptTree.right.hash().toByteArray()

val txHash = Transaction.hashForSigningSchnorr(tx, 0, swapInTx.txOut, SigHash.SIGHASH_DEFAULT, SigVersion.SIGVERSION_TAPSCRIPT, Script.ExecutionData(null, ScriptTree.hash(scriptTree.left)))
val txHash = Transaction.hashForSigningSchnorr(tx, 0, swapInTx.txOut, SigHash.SIGHASH_DEFAULT, SigVersion.SIGVERSION_TAPSCRIPT, scriptTree.left.hash())
val userSig = Crypto.signSchnorr(txHash, userPrivateKey, Crypto.SchnorrTweak.NoTweak)
val serverSig = Crypto.signSchnorr(txHash, serverPrivateKey, Crypto.SchnorrTweak.NoTweak)

Expand All @@ -544,8 +544,8 @@ class TransactionsTestsCommon : LightningTestSuite() {
)
val controlBlock = byteArrayOf((Script.TAPROOT_LEAF_TAPSCRIPT + (if (parity) 1 else 0)).toByte()) +
internalPubkey.value.toByteArray() +
ScriptTree.hash(scriptTree.left).toByteArray()
val txHash = Transaction.hashForSigningSchnorr(tx, 0, swapInTx.txOut, SigHash.SIGHASH_DEFAULT, SigVersion.SIGVERSION_TAPSCRIPT, Script.ExecutionData(null, ScriptTree.hash(scriptTree.right)))
scriptTree.left.hash().toByteArray()
val txHash = Transaction.hashForSigningSchnorr(tx, 0, swapInTx.txOut, SigHash.SIGHASH_DEFAULT, SigVersion.SIGVERSION_TAPSCRIPT, scriptTree.right.hash())
val userSig = Crypto.signSchnorr(txHash, userPrivateKey, Crypto.SchnorrTweak.NoTweak)
val signedTx = tx.updateWitness(0, ScriptWitness.empty.push(userSig).push(refundScript).push(controlBlock))
Transaction.correctlySpends(signedTx, swapInTx, ScriptFlags.STANDARD_SCRIPT_VERIFY_FLAGS)
Expand Down Expand Up @@ -585,8 +585,8 @@ class TransactionsTestsCommon : LightningTestSuite() {
val userNonce = SecretNonce.generate(randomBytes32(), userPrivateKey, userPrivateKey.publicKey(), null, cache, null)
val serverNonce = SecretNonce.generate(randomBytes32(), serverPrivateKey, serverPrivateKey.publicKey(), null, cache, null)
val commonNonce = IndividualNonce.aggregate(listOf(userNonce.second, serverNonce.second))
val userSig = swapInProtocol.signSwapInputUser(tx, 0, swapInTx.txOut, userPrivateKey, userNonce.first, commonNonce).getOrThrow()
val serverSig = swapInProtocol.signSwapInputServer(tx, 0, swapInTx.txOut, commonNonce, serverPrivateKey, serverNonce.first).getOrThrow()
val userSig = swapInProtocol.signSwapInputUser(tx, 0, swapInTx.txOut, userPrivateKey, userNonce.first, commonNonce)
val serverSig = swapInProtocol.signSwapInputServer(tx, 0, swapInTx.txOut, commonNonce, serverPrivateKey, serverNonce.first)
val ctx = swapInProtocol.session(tx, 0, swapInTx.txOut, commonNonce)
val commonSig = ctx.add(listOf(userSig, serverSig))
val signedTx = tx.updateWitness(0, swapInProtocol.witness(commonSig))
Expand Down

0 comments on commit 64fbf79

Please sign in to comment.