From 400e08519720d847275900c8f9399581912b7715 Mon Sep 17 00:00:00 2001 From: sstone Date: Tue, 24 Aug 2021 20:17:17 +0200 Subject: [PATCH] Clean up --- .../publish/ReplaceableTxPublisher.scala | 28 ++++--------------- .../main/scala/fr/acinq/eclair/package.scala | 14 +++++++--- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisher.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisher.scala index 6d1fec87b6..5d6306466b 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisher.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/publish/ReplaceableTxPublisher.scala @@ -288,6 +288,7 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, case claimAnchorTx: ClaimLocalAnchorOutputTx => val claimAnchorSig = keyManager.sign(claimAnchorTx, keyManager.fundingPublicKey(cmd.commitments.localParams.fundingKeyPath), TxOwner.Local, cmd.commitments.commitmentFormat) val signedClaimAnchorTx = addSigs(claimAnchorTx, claimAnchorSig) + // update our psbt with our signature for our input, and ask bitcoin core to sign its input val psbt1 = psbt.finalize(0, signedClaimAnchorTx.tx.txIn(0).witness).get context.pipeToSelf(bitcoinClient.processPsbt(psbt1).map(processPbbtResponse => { // all inputs should be signed now @@ -399,9 +400,11 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, val dummyChangeAmount = weight2fee(anchorFeerate, claimAnchorOutputMinWeight) + dustLimit val address = publicKeyScriptToAddress(Script.pay2wpkh(PlaceHolderPubKey), nodeParams.chainHash) + // merge outptuts if needed to get a PSBT with a single output def makeSingleOutput(fundPsbtResponse: FundPsbtResponse): Future[Psbt] = { fundPsbtResponse.changePosition match { case Some(changePos) => + // add our main output to the change output val changeOutput = fundPsbtResponse.psbt.global.tx.txOut(changePos) val changeOutput1 = changeOutput.copy(amount = changeOutput.amount + dummyChangeAmount) val psbt1 = fundPsbtResponse.psbt.copy( @@ -410,6 +413,7 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, ) Future.successful(psbt1) case None => + // replace our main output with a dummy change output bitcoinClient.getChangeAddress().map(pubkeyHash => { val changeOutput1 = TxOut(dummyChangeAmount, Script.pay2wpkh(pubkeyHash)) fundPsbtResponse.psbt.copy( @@ -420,11 +424,12 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, } for { - fundPsbtResponse <- bitcoinClient.fundPsbt(Seq(computeP2WpkhAddress(PlaceHolderPubKey, nodeParams.chainHash) -> dummyChangeAmount), 0, FundPsbtOptions(anchorFeerate, lockUtxos = true, changePosition = Some(1))) + fundPsbtResponse <- bitcoinClient.fundPsbt(Seq(address -> dummyChangeAmount), 0, FundPsbtOptions(anchorFeerate, lockUtxos = true, changePosition = Some(1))) psbt <- makeSingleOutput(fundPsbtResponse) // NB: we insert the anchor input in the *first* position because our signing helpers only sign input #0. unsignedTx = txInfo.copy(tx = psbt.global.tx.copy(txIn = txInfo.tx.txIn.head +: psbt.global.tx.txIn)) adjustedTx = adjustAnchorOutputChange(unsignedTx, commitTx, fundPsbtResponse.amountIn + AnchorOutputsCommitmentFormat.anchorAmount, commitFeerate, targetFeerate, dustLimit) + // add a PSBT input for our input (i.e the one that spends our own anchor/htlc output and that we'll need to sign psbtInput = Psbt.PartiallySignedInput.empty.copy( witnessUtxo = Some(txInfo.input.txOut), witnessScript = Some(Script.parse(txInfo.input.redeemScript)) @@ -435,27 +440,6 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams, } yield { (adjustedTx, psbt1) } - - // val txNotFunded = Transaction(2, Nil, TxOut(dummyChangeAmount, Script.pay2wpkh(PlaceHolderPubKey)) :: Nil, 0) - // bitcoinClient.fundTransaction(txNotFunded, FundTransactionOptions(anchorFeerate, lockUtxos = true)).flatMap(fundTxResponse => { - // // We merge the outputs if there's more than one. - // fundTxResponse.changePosition match { - // case Some(changePos) => - // val changeOutput = fundTxResponse.tx.txOut(changePos) - // val txSingleOutput = fundTxResponse.tx.copy(txOut = Seq(changeOutput.copy(amount = changeOutput.amount + dummyChangeAmount))) - // Future.successful(fundTxResponse.copy(tx = txSingleOutput)) - // case None => - // bitcoinClient.getChangeAddress().map(pubkeyHash => { - // val txSingleOutput = fundTxResponse.tx.copy(txOut = Seq(TxOut(dummyChangeAmount, Script.pay2wpkh(pubkeyHash)))) - // fundTxResponse.copy(tx = txSingleOutput) - // }) - // } - // }).map(fundTxResponse => { - // require(fundTxResponse.tx.txOut.size == 1, "funded transaction should have a single change output") - // // NB: we insert the anchor input in the *first* position because our signing helpers only sign input #0. - // val unsignedTx = txInfo.copy(tx = fundTxResponse.tx.copy(txIn = txInfo.tx.txIn.head +: fundTxResponse.tx.txIn)) - // adjustAnchorOutputChange(unsignedTx, commitTx, fundTxResponse.amountIn + AnchorOutputsCommitmentFormat.anchorAmount, commitFeerate, targetFeerate, dustLimit) -> Psbt(unsignedTx.tx) - // }) } private def addInputs(txInfo: HtlcTx, targetFeerate: FeeratePerKw, commitments: Commitments): Future[(HtlcTx, Psbt)] = { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/package.scala b/eclair-core/src/main/scala/fr/acinq/eclair/package.scala index 433579c252..6c7624190e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/package.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/package.scala @@ -95,29 +95,35 @@ package object eclair { } } + /** + * + * @param scriptPubKey public key script + * @param chainHash hash of the chain we're on + * @return the address the this public key script on this chain + */ def publicKeyScriptToAddress(scriptPubKey: Seq[ScriptElt], chainHash: ByteVector32): String = { val base58PubkeyPrefix = chainHash match { case Block.LivenetGenesisBlock.hash => Base58.Prefix.PubkeyAddress case Block.TestnetGenesisBlock.hash | Block.RegtestGenesisBlock.hash => Base58.Prefix.PubkeyAddressTestnet - case _ => ??? + case _ => throw new IllegalArgumentException(s"invalid chain hash $chainHash") } val base58ScriptPrefix = chainHash match { case Block.LivenetGenesisBlock.hash => Base58.Prefix.ScriptAddress case Block.TestnetGenesisBlock.hash | Block.RegtestGenesisBlock.hash => Base58.Prefix.ScriptAddressTestnet - case _ => ??? + case _ => throw new IllegalArgumentException(s"invalid chain hash $chainHash") } val hrp = chainHash match { case Block.LivenetGenesisBlock.hash => "bc" case Block.TestnetGenesisBlock.hash => "tb" case Block.RegtestGenesisBlock.hash => "bcrt" - case _ => ??? + case _ => throw new IllegalArgumentException(s"invalid chain hash $chainHash") } scriptPubKey match { case OP_DUP :: OP_HASH160 :: OP_PUSHDATA(pubKeyHash, _) :: OP_EQUALVERIFY :: OP_CHECKSIG :: Nil => Base58Check.encode(base58PubkeyPrefix, pubKeyHash) case OP_HASH160 :: OP_PUSHDATA(scriptHash, _) :: OP_EQUAL :: Nil => Base58Check.encode(base58ScriptPrefix, scriptHash) case OP_0 :: OP_PUSHDATA(pubKeyHash, _) :: Nil if pubKeyHash.length == 20 => Bech32.encodeWitnessAddress(hrp, 0, pubKeyHash) case OP_0 :: OP_PUSHDATA(scriptHash, _) :: Nil if scriptHash.length == 32 => Bech32.encodeWitnessAddress(hrp, 0, scriptHash) - case _ => ??? + case _ => throw new IllegalArgumentException(s"invalid pubkey script $scriptPubKey") } }