11package fr .acinq .eclair
22
33import fr .acinq .bitcoin .scalacompat .Crypto .PublicKey
4- import fr .acinq .bitcoin .scalacompat .{ByteVector64 , DeterministicWallet , OutPoint , Satoshi , SatoshiLong , Script , ScriptWitness , Transaction , TxIn , TxOut , addressToPublicKeyScript }
4+ import fr .acinq .bitcoin .scalacompat .Musig2 .{IndividualNonce , LocalNonce }
5+ import fr .acinq .bitcoin .scalacompat .{DeterministicWallet , Musig2 , OutPoint , Satoshi , SatoshiLong , Script , ScriptWitness , Transaction , TxIn , TxOut , addressToPublicKeyScript }
56import fr .acinq .eclair .blockchain .fee .FeeratePerKw
67import fr .acinq .eclair .channel .{ChannelConfig , ChannelSpendSignature }
78import fr .acinq .eclair .transactions .Transactions ._
89import fr .acinq .eclair .transactions .{Scripts , Transactions }
910import scodec .bits .ByteVector
1011
1112import scala .concurrent .Future
13+ import scala .jdk .CollectionConverters .ConcurrentMapHasAsScala
1214
1315trait SpendFromChannelAddress {
1416
1517 this : EclairImpl =>
1618
19+ import java .util .concurrent .ConcurrentHashMap
20+ private val nonces = new ConcurrentHashMap [IndividualNonce , LocalNonce ]().asScala
21+
1722 private def buildTx (outPoint : OutPoint , outputAmount : Satoshi , pubKeyScript : ByteVector , witness : ScriptWitness ) = Transaction (2 ,
1823 txIn = Seq (TxIn (outPoint, ByteVector .empty, 0 , witness)),
1924 txOut = Seq (TxOut (outputAmount, pubKeyScript)),
@@ -26,27 +31,41 @@ trait SpendFromChannelAddress {
2631 Right (pubKeyScript) = addressToPublicKeyScript(appKit.nodeParams.chainHash, address).map(Script .write)
2732 channelKeys = appKit.nodeParams.channelKeyManager.channelKeys(ChannelConfig .standard, fundingKeyPath)
2833 localFundingPubkey = channelKeys.fundingKey(fundingTxIndex).publicKey
34+ isTaproot = Script .isPay2tr(Script .parse(pubKeyScript))
35+ (localNonce_opt, dummyWitness) = if (isTaproot) {
36+ val serverNonce = Musig2 .generateNonce(randomBytes32(), Right (localFundingPubkey), Seq (localFundingPubkey), None , None )
37+ nonces.put(serverNonce.publicNonce, serverNonce)
38+ Some (serverNonce.publicNonce) -> Script .witnessKeyPathPay2tr(PlaceHolderSig )
39+ } else {
40+ None -> Scripts .witness2of2(PlaceHolderSig , PlaceHolderSig , localFundingPubkey, localFundingPubkey)
41+ }
2942 // build the tx a first time with a zero amount to compute the weight
30- dummyWitness = Scripts .witness2of2(PlaceHolderSig , PlaceHolderSig , localFundingPubkey, localFundingPubkey)
3143 fee = Transactions .weight2fee(feerate, buildTx(outPoint, 0 .sat, pubKeyScript, dummyWitness).weight())
3244 _ = assert(inputAmount - fee > Scripts .dustLimit(pubKeyScript), s " amount insufficient (fee= $fee) " )
3345 unsignedTx = buildTx(outPoint, inputAmount - fee, pubKeyScript, dummyWitness)
34- } yield SpendFromChannelPrep (fundingTxIndex, localFundingPubkey, inputAmount, unsignedTx)
46+ } yield SpendFromChannelPrep (fundingTxIndex, localFundingPubkey, localNonce_opt, inputAmount, unsignedTx)
3547 }
3648
37- override def spendFromChannelAddress (fundingKeyPath : DeterministicWallet .KeyPath , fundingTxIndex : Long , remoteFundingPubkey : PublicKey , remoteSig : ByteVector64 , unsignedTx : Transaction ): Future [SpendFromChannelResult ] = {
49+ override def spendFromChannelAddress (fundingKeyPath : DeterministicWallet .KeyPath , fundingTxIndex : Long , remoteFundingPubkey : PublicKey , localNonce_opt : Option [ IndividualNonce ], remoteSig : ChannelSpendSignature , unsignedTx : Transaction ): Future [SpendFromChannelResult ] = {
3850 for {
3951 _ <- Future .successful(())
4052 outPoint = unsignedTx.txIn.head.outPoint
4153 inputTx <- appKit.wallet.getTransaction(outPoint.txid)
42- channelKeys = appKit.nodeParams.channelKeyManager.channelKeys(ChannelConfig .standard, fundingKeyPath)
43- localFundingKey = channelKeys.fundingKey(fundingTxIndex)
4454 inputInfo = InputInfo (outPoint, inputTx.txOut(outPoint.index.toInt))
4555 // classify as splice, doesn't really matter
4656 tx = Transactions .SpliceTx (inputInfo, unsignedTx)
47- localSig = tx.sign(localFundingKey, remoteFundingPubkey, extraUtxos = Map .empty)
48- signedTx = tx.aggregateSigs(localFundingKey.publicKey, remoteFundingPubkey, localSig, ChannelSpendSignature .IndividualSignature (remoteSig))
57+ channelKeys = appKit.nodeParams.channelKeyManager.channelKeys(ChannelConfig .standard, fundingKeyPath)
58+ localFundingKey = channelKeys.fundingKey(fundingTxIndex)
59+ signedTx = remoteSig match {
60+ case individualRemoteSig : ChannelSpendSignature .IndividualSignature =>
61+ val localSig = tx.sign(localFundingKey, remoteFundingPubkey, extraUtxos = Map .empty)
62+ tx.aggregateSigs(localFundingKey.publicKey, remoteFundingPubkey, localSig, individualRemoteSig)
63+ case remotePartialSig : ChannelSpendSignature .PartialSignatureWithNonce =>
64+ val localPrivateNonce = nonces(localNonce_opt.get)
65+ val Right (localSig) = tx.partialSign(localFundingKey, remoteFundingPubkey, extraUtxos = Map .empty, localNonce = localPrivateNonce, publicNonces = Seq (localPrivateNonce.publicNonce, remotePartialSig.nonce))
66+ val Right (signedTx) = tx.aggregateSigs(localFundingKey.publicKey, remoteFundingPubkey, localSig, remotePartialSig, extraUtxos = Map .empty)
67+ signedTx
68+ }
4969 } yield SpendFromChannelResult (signedTx)
5070 }
51-
5271}
0 commit comments