-
Notifications
You must be signed in to change notification settings - Fork 272
Add low-level taproot helpers #3086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a bit more work to do around taproot scripts and witnesses to make it cleaner by introducing better types and encapsulation of script trees and their corresponding witnesses. My main comment that requires more work is #3086 (comment), the rest is mostly nits for now.
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/ErrorHandlers.scala
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking much better than the attempts we made before the transactions architecture refactoring, that was worth it! I think we're almost there, but can still simplify it a bit.
eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/test/scala/fr/acinq/eclair/transactions/TransactionsSpec.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Scripts.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
eclair-core/src/main/scala/fr/acinq/eclair/transactions/Transactions.scala
Outdated
Show resolved
Hide resolved
Needs rebase! |
There are no functional changes, we just define a new format that is not used anywhere yet, with dummy "not implemented" blocks when needed.
We can create, sign, spend commit and htlc transactions for the new simple taproot channels commitment format. Wire messages and channel creation/update workflow have not been modified.
We only either single leaves or branches with 2 single leaves (a timeout and a success branch for example), having specific types for them makes to code easier to read.
668da46
to
8fcc0f9
Compare
def assertWeightMatches(actualWeight: Int, expectedWeight: Int, commitmentFormat: CommitmentFormat): Unit = commitmentFormat match { | ||
case DefaultCommitmentFormat | _: AnchorOutputsCommitmentFormat => assert(Math.abs(actualWeight - expectedWeight) < 20) | ||
case SimpleTaprootChannelCommitmentFormat => assert(actualWeight == expectedWeight) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This unused function still hasn't been removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in c2d5efe
val walletPriv = randomKey() | ||
val walletPub = walletPriv.publicKey | ||
val finalPubKeyScript = Script.write(Script.pay2wpkh(walletPub)) | ||
val commitInput = Funding.makeFundingInputInfo(randomTxId(), 0, Btc(1), localFundingPriv.publicKey, remoteFundingPriv.publicKey, UnsafeLegacyAnchorOutputsCommitmentFormat) | ||
// funding tx sends to musig2 aggregate of local and remote funding keys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is wrong for non-taproot cases, we should probably just remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in c2d5efe
localPartialSig <- txInfo.partialSign(localFundingPriv, remoteFundingPriv.publicKey, Map.empty, LocalNonce(secretLocalNonce, publicLocalNonce), publicNonces) | ||
remotePartialSig <- txInfo.partialSign(remoteFundingPriv, localFundingPriv.publicKey, Map.empty, LocalNonce(secretRemoteNonce, publicRemoteNonce), publicNonces) | ||
_ = assert(txInfo.checkRemotePartialSignature(localFundingPriv.publicKey, remoteFundingPriv.publicKey, remotePartialSig, publicLocalNonce)) | ||
invalidRemotePartialSig = remotePartialSig.copy(remotePartialSig.partialSig.reverse, remotePartialSig.nonce) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this call to copy
is a bit weird, you're not specifying what you're copying?
invalidRemotePartialSig = remotePartialSig.copy(remotePartialSig.partialSig.reverse, remotePartialSig.nonce) | |
invalidRemotePartialSig = remotePartialSig.copy(partialSig = remotePartialSig.partialSig.reverse) |
Or maybe:
invalidRemotePartialSig = remotePartialSig.copy(remotePartialSig.partialSig.reverse, remotePartialSig.nonce) | |
invalidRemotePartialSig = ChannelSpendSignature.PartialSignatureWithNonce(randomBytes32(), remotePartialSig.nonce) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in c2d5efe
@@ -148,6 +149,29 @@ object Transactions { | |||
*/ | |||
case object ZeroFeeHtlcTxAnchorOutputsCommitmentFormat extends AnchorOutputsCommitmentFormat | |||
|
|||
sealed trait TaprootCommitmentFormat extends CommitmentFormat | |||
|
|||
case object SimpleTaprootChannelCommitmentFormat extends TaprootCommitmentFormat { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably become a trait
since we want two support a variation of the official commitment format where HTLC txs pay the same feerate as the commit tx, like what we do for anchor outputs (for mobile wallets until we're able to move to v3).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 6ec1d0c
@@ -183,8 +210,10 @@ object Transactions { | |||
* @param leafHash hash of the leaf script we're spending (must belong to the tree). | |||
*/ | |||
case class TaprootScriptPath(internalKey: XonlyPublicKey, scriptTree: ScriptTree, leafHash: ByteVector32) extends Taproot { | |||
require(Option(scriptTree.findScript(KotlinUtils.scala2kmp(leafHash))).nonEmpty, "script tree must contain the provided leaf") | |||
val redeemScript: ByteVector = KotlinUtils.kmp2scala(scriptTree.findScript(KotlinUtils.scala2kmp(leafHash)).getScript) | |||
import KotlinUtils._ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We import KotlinUtils
11 times in this file, why not do it a single time for the whole file in the main imports?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in c2d5efe
RedeemInfo.P2wsh(redeemScript) | ||
case SimpleTaprootChannelCommitmentFormat => | ||
val receivedHtlcTree = Taproot.receivedHtlcScriptTree(commitKeys, paymentHash, htlcExpiry) | ||
RedeemInfo.TaprootScriptPath(commitKeys.revocationPublicKey.xOnly, receivedHtlcTree.scriptTree, KotlinUtils.kmp2scala(receivedHtlcTree.success.hash())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can remove the explicit KotlinUtils.kmp2scala
if we import KotlinUtils
at the file root. Same for other similar calls in this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in c2d5efe
case SimpleTaprootChannelCommitmentFormat => | ||
if (toLocalAmount >= dustLimit || hasHtlcs) { | ||
val redeemInfo = RedeemInfo.TaprootKeyPath(commitmentKeys.localDelayedPaymentPublicKey.xOnly, Some(Taproot.anchorScriptTree)) | ||
outputs.append(ToLocalAnchor(TxOut(AnchorOutputsCommitmentFormat.anchorAmount, redeemInfo.pubkeyScript))) | ||
} | ||
if (toRemoteAmount >= dustLimit || hasHtlcs) { | ||
val redeemInfo = RedeemInfo.TaprootKeyPath(commitmentKeys.remotePaymentPublicKey.xOnly, Some(Taproot.anchorScriptTree)) | ||
|
||
outputs.append(ToRemoteAnchor(TxOut(AnchorOutputsCommitmentFormat.anchorAmount, redeemInfo.pubkeyScript))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we group AnchorOutputsCommitmentFormat
and SimpleTaprootChannelCommitmentFormat
? Most of the logic is the same, it's only the redeemInfo
construction that changes, but we already have redeemInfo
helpers exposed on the ClaimAnchorOutputTx
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in c2d5efe
We add a new commitment format for simple taproot channels, and implement creating and verifying musig2 and taproot signatures for these channels.
Wire format and channel creation/update workflows have not been modified, this will be done in another PR.