@@ -5,6 +5,8 @@ import fr.acinq.bitcoin.crypto.musig2.AggregatedNonce
5
5
import fr.acinq.bitcoin.crypto.musig2.KeyAggCache
6
6
import fr.acinq.bitcoin.crypto.musig2.SecretNonce
7
7
import fr.acinq.bitcoin.crypto.musig2.Session
8
+ import fr.acinq.bitcoin.utils.Either
9
+ import fr.acinq.bitcoin.utils.flatMap
8
10
import fr.acinq.lightning.NodeParams
9
11
import fr.acinq.lightning.wire.TxAddInputTlv
10
12
@@ -25,10 +27,14 @@ class SwapInProtocol(val userPublicKey: PublicKey, val serverPublicKey: PublicKe
25
27
private val merkleRoot = scriptTree.hash()
26
28
27
29
// 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
28
- private val internalPubKeyAndCache = KeyAggCache .Companion .add(listOf (userPublicKey, serverPublicKey), null )
30
+ private val internalPubKeyAndCache = run {
31
+ val c = KeyAggCache .add(listOf (userPublicKey, serverPublicKey), null )
32
+ if (c.isLeft) error(" key aggregation failed" ) else c.right!!
33
+ }
29
34
private val internalPubKey = internalPubKeyAndCache.first
30
35
private val cache = internalPubKeyAndCache.second
31
36
37
+
32
38
// it is tweaked with the script's merkle root to get the pubkey that will be exposed
33
39
private val commonPubKeyAndParity = internalPubKey.outputKey(Crypto .TaprootTweak .ScriptTweak (merkleRoot))
34
40
val commonPubKey = commonPubKeyAndParity.first
@@ -45,30 +51,31 @@ class SwapInProtocol(val userPublicKey: PublicKey, val serverPublicKey: PublicKe
45
51
46
52
fun witnessRefund (userSig : ByteVector64 ): ScriptWitness = ScriptWitness .empty.push(userSig).push(redeemScript).push(controlBlock)
47
53
48
- fun signSwapInputUser (fundingTx : Transaction , index : Int , parentTxOuts : List <TxOut >, userPrivateKey : PrivateKey , userNonce : SecretNonce , commonNonce : AggregatedNonce ): ByteVector32 {
54
+ fun signSwapInputUser (fundingTx : Transaction , index : Int , parentTxOuts : List <TxOut >, userPrivateKey : PrivateKey , userNonce : SecretNonce , commonNonce : AggregatedNonce ): Either < Throwable , ByteVector32 > {
49
55
require(userPrivateKey.publicKey() == userPublicKey)
50
56
val txHash = Transaction .hashForSigningSchnorr(fundingTx, index, parentTxOuts, SigHash .SIGHASH_DEFAULT , SigVersion .SIGVERSION_TAPROOT )
51
- val cache1 = cache.tweak(internalPubKey.tweak(Crypto .TaprootTweak .ScriptTweak (merkleRoot)), true ).first
52
- val session = Session .build(commonNonce, txHash, cache1)
53
- return session.sign(userNonce, userPrivateKey, cache1)
57
+
58
+ return cache.tweak(internalPubKey.tweak(Crypto .TaprootTweak .ScriptTweak (merkleRoot)), true )
59
+ .flatMap { (c, _) -> Session .build(commonNonce, txHash, c).map { s -> Pair (s, c) } }
60
+ .flatMap { (s, c) -> s.sign(userNonce, userPrivateKey, c) }
54
61
}
55
62
56
63
fun signSwapInputRefund (fundingTx : Transaction , index : Int , parentTxOuts : List <TxOut >, userPrivateKey : PrivateKey ): ByteVector64 {
57
64
val txHash = Transaction .hashForSigningSchnorr(fundingTx, index, parentTxOuts, SigHash .SIGHASH_DEFAULT , SigVersion .SIGVERSION_TAPSCRIPT , merkleRoot)
58
65
return Crypto .signSchnorr(txHash, userPrivateKey, Crypto .SchnorrTweak .NoTweak )
59
66
}
60
67
61
- fun signSwapInputServer (fundingTx : Transaction , index : Int , parentTxOuts : List <TxOut >, commonNonce : AggregatedNonce , serverPrivateKey : PrivateKey , serverNonce : SecretNonce ): ByteVector32 {
68
+ fun signSwapInputServer (fundingTx : Transaction , index : Int , parentTxOuts : List <TxOut >, commonNonce : AggregatedNonce , serverPrivateKey : PrivateKey , serverNonce : SecretNonce ): Either < Throwable , ByteVector32 > {
62
69
val txHash = Transaction .hashForSigningSchnorr(fundingTx, index, parentTxOuts, SigHash .SIGHASH_DEFAULT , SigVersion .SIGVERSION_TAPROOT )
63
- val cache1 = cache.tweak(internalPubKey.tweak(Crypto .TaprootTweak .ScriptTweak (merkleRoot)), true ).first
64
- val session = Session .build(commonNonce, txHash, cache1)
65
- return session. sign(serverNonce, serverPrivateKey, cache1)
70
+ return cache.tweak(internalPubKey.tweak(Crypto .TaprootTweak .ScriptTweak (merkleRoot)), true )
71
+ .flatMap { (c, _) -> Session .build(commonNonce, txHash, c).map { s -> Pair (s, c) } }
72
+ .flatMap { (s, c) -> s. sign(serverNonce, serverPrivateKey, c) }
66
73
}
67
74
68
- fun session (fundingTx : Transaction , index : Int , parentTxOuts : List <TxOut >, commonNonce : AggregatedNonce ): Session {
75
+ fun session (fundingTx : Transaction , index : Int , parentTxOuts : List <TxOut >, commonNonce : AggregatedNonce ): Either < Throwable , Session > {
69
76
val txHash = Transaction .hashForSigningSchnorr(fundingTx, index, parentTxOuts, SigHash .SIGHASH_DEFAULT , SigVersion .SIGVERSION_TAPROOT )
70
- val cache1 = cache.tweak(internalPubKey.tweak(Crypto .TaprootTweak .ScriptTweak (merkleRoot)), true ).first
71
- return Session .build(commonNonce, txHash, cache1)
77
+ return cache.tweak(internalPubKey.tweak(Crypto .TaprootTweak .ScriptTweak (merkleRoot)), true )
78
+ .flatMap { (c, _) -> Session .build(commonNonce, txHash, c) }
72
79
}
73
80
74
81
companion object {
@@ -81,17 +88,18 @@ class SwapInProtocol(val userPublicKey: PublicKey, val serverPublicKey: PublicKe
81
88
* @param masterRefundKey master private key for the refund keys. we assume that there is a single level of derivation to compute the refund keys
82
89
* @return a taproot descriptor that can be imported in bitcoin core (from version 26 on) to recover user funds once the funding delay has passed
83
90
*/
84
- fun descriptor (chain : NodeParams .Chain , userPublicKey : PublicKey , serverPublicKey : PublicKey , refundDelay : Int , masterRefundKey : DeterministicWallet .ExtendedPrivateKey ): String {
91
+ fun descriptor (chain : NodeParams .Chain , userPublicKey : PublicKey , serverPublicKey : PublicKey , refundDelay : Int , masterRefundKey : DeterministicWallet .ExtendedPrivateKey ): Either < Throwable , String > {
85
92
// 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
86
- val (internalPubKey, _) = KeyAggCache .Companion .add(listOf (userPublicKey, serverPublicKey), null )
87
- val prefix = when (chain) {
88
- NodeParams .Chain .Mainnet -> DeterministicWallet .xprv
89
- else -> DeterministicWallet .tprv
93
+ return KeyAggCache .Companion .add(listOf (userPublicKey, serverPublicKey)).map { (internalPubKey, _) ->
94
+ val prefix = when (chain) {
95
+ NodeParams .Chain .Mainnet -> DeterministicWallet .xprv
96
+ else -> DeterministicWallet .tprv
97
+ }
98
+ val xpriv = DeterministicWallet .encode(masterRefundKey, prefix)
99
+ val desc = " tr(${internalPubKey.value} ,and_v(v:pk($xpriv /*),older($refundDelay )))"
100
+ val checksum = Descriptor .checksum(desc)
101
+ " $desc #$checksum "
90
102
}
91
- val xpriv = DeterministicWallet .encode(masterRefundKey, prefix)
92
- val desc = " tr(${internalPubKey.value} ,and_v(v:pk($xpriv /*),older($refundDelay )))"
93
- val checksum = Descriptor .checksum(desc)
94
- return " $desc #$checksum "
95
103
}
96
104
97
105
/* *
@@ -103,20 +111,20 @@ class SwapInProtocol(val userPublicKey: PublicKey, val serverPublicKey: PublicKe
103
111
* @param masterRefundKey master public key for the refund keys. we assume that there is a single level of derivation to compute the refund keys
104
112
* @return a taproot descriptor that can be imported in bitcoin core (from version 26 on) to create a watch-only wallet for your swap-in transactions
105
113
*/
106
- fun descriptor (chain : NodeParams .Chain , userPublicKey : PublicKey , serverPublicKey : PublicKey , refundDelay : Int , masterRefundKey : DeterministicWallet .ExtendedPublicKey ): String {
114
+ fun descriptor (chain : NodeParams .Chain , userPublicKey : PublicKey , serverPublicKey : PublicKey , refundDelay : Int , masterRefundKey : DeterministicWallet .ExtendedPublicKey ): Any {
107
115
// 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
108
- val (internalPubKey, _) = KeyAggCache .Companion .add(listOf (userPublicKey, serverPublicKey), null )
109
- val prefix = when (chain) {
110
- NodeParams .Chain .Mainnet -> DeterministicWallet .xpub
111
- else -> DeterministicWallet .tpub
116
+ return KeyAggCache .Companion .add(listOf (userPublicKey, serverPublicKey)).map { (internalPubKey, _) ->
117
+ val prefix = when (chain) {
118
+ NodeParams .Chain .Mainnet -> DeterministicWallet .xpub
119
+ else -> DeterministicWallet .tpub
120
+ }
121
+ val xpub = DeterministicWallet .encode(masterRefundKey, prefix)
122
+ val path = masterRefundKey.path.toString().replace(' \' ' , ' h' ).removePrefix(" m" )
123
+ val desc = " tr(${internalPubKey.value} ,and_v(v:pk($xpub$path /*),older($refundDelay )))"
124
+ val checksum = Descriptor .checksum(desc)
125
+ return " $desc #$checksum "
112
126
}
113
- val xpub = DeterministicWallet .encode(masterRefundKey, prefix)
114
- val path = masterRefundKey.path.toString().replace(' \' ' , ' h' ).removePrefix(" m" )
115
- val desc = " tr(${internalPubKey.value} ,and_v(v:pk($xpub$path /*),older($refundDelay )))"
116
- val checksum = Descriptor .checksum(desc)
117
- return " $desc #$checksum "
118
127
}
119
-
120
128
}
121
129
}
122
130
0 commit comments