-
Notifications
You must be signed in to change notification settings - Fork 130
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
[wallet 3/3]: group key support for channel funding #1413
base: main
Are you sure you want to change the base?
Conversation
8939b42
to
ee66eb9
Compare
129dd06
to
76938ad
Compare
Pull Request Test Coverage Report for Build 14358733453Details
💛 - Coveralls |
76938ad
to
2639fa6
Compare
86112a5
to
cdc7cb2
Compare
2639fa6
to
9f24e16
Compare
cdc7cb2
to
be499e2
Compare
db987a5
to
206da82
Compare
be499e2
to
bb88417
Compare
206da82
to
ce40ed7
Compare
7356ae4
to
cd1b95b
Compare
cd1b95b
to
7dd72b9
Compare
TODO for myself: add group key to channel funding blob, see lightninglabs/lightning-terminal@4768ba2#r1982366865. |
To be done in this PR? |
// group key _or_ the asset ID but not both. That means, if the group | ||
// key is set, we ignore the asset ID and allow multiple inputs of the | ||
// same group to be selected. | ||
DistinctSpecifier bool |
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.
Interesting....makes we wonder if we should actually have two asset "specifier" types: one for the XOR case, and one for the AND case. This bool seems to give the caller a hint that the specifier should be treated in an identeifier exclusive manner (one or the other).
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.
Alternatively, for the use case we see in this PR, couldn't we have the caller create a specifier that only has a group key if funding is initiated using a group key?
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.
The problem is, that even today we have both use cases where we specify the AND
case and the XOR
case. Both are valid use cases IMO and depending on what the user wants this boolean basically switches between them.
See #1413 (comment).
@@ -7073,7 +7073,7 @@ func (r *rpcServer) FundChannel(ctx context.Context, | |||
} | |||
|
|||
assetID, groupKey, err := parseAssetSpecifier( | |||
req.GetAssetId(), "", nil, "", | |||
req.GetAssetId(), "", req.GetGroupKey(), "", |
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.
Re the other comment, this is where we can implement the either or behavior:
- We only allow asset ID or group key to be set
- We make a specifier that only has one half populated
With that change, I don't think we'd need that new bool and behavior change in the db?
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.
added something similar in 667080c
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.
Hmm, yeah, maybe we can introduce a sub type ExclusiveSpecifier
that embeds the original identifier (just so we don't need to change things everywhere)... Will think about it.
assets := commitState.LocalAssets.Val.Outputs | ||
// Next, we'll collect all the asset IDs that were committed to the | ||
// channel. | ||
assetIDs := fn.Map( |
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.
IIUC, there can potentially be duplicates here: multiple UTXOs of a given asset ID were used to fund the channel? Perhaps it should be run through as a set first.
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.
No, I don't think that should ever be the case. Even if you have multiple inputs of the same asset ID, we'd merge them into the same UTXO. Only for different asset IDs we need to keep distinct UTXOs around (because they'll be represented by distinct vPackets). Will add a comment to make that more clear.
} | ||
|
||
scriptKeys[channelAsset.AssetID.Val] = *newKey.PubKey | ||
scriptKeys[assetID] = *newKey.PubKey |
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.
Based on the comment above, we may end up with script keys thgat was created/inserted but were never used as we overrode them here in the map.
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.
Yeah, you're right... So perhaps for the very first one we should try to re-use the one we already derived?
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.
Okay, my comment right above this was incorrect. Because we shouldn't have duplicate asset IDs (as explained in #1413 (comment)), we shouldn't overwrite keys here. So we don't derive keys that we then don't end up using.
vIn := vPackets[idx].Inputs[0] | ||
if vIn.Asset().HasSplitCommitmentWitness() { | ||
//nolint:lll | ||
rootAsset := vIn.Asset().PrevWitnesses[0].SplitCommitment.RootAsset |
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.
Hmm, I recall this was one of those bugs that took be a while to track down properly.
The root asset (funding output) in this case needs to have the updated witness w/ the control block in tact, as we don't apply the segwit encoding to root assets stored as part of a split commitment.
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.
Hmm, yeah, I thought this was probably the case. But I removed these lines and ran a full integration test suite and nothing broke. Is it possible that this was fixed indirectly in a later version by adding the witness earlier in the flow? Because I checked and the commitment transaction should already have the witness (both in the root asset as well as in each split commitment's root asset copy). But I will double check again.
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.
Okay, I found exactly why this isn't needed anymore.
This was fixed with c072d3f, which was in #1154.
Which was quite a while after de5bed6 (in #938) which introduced the temporary fix in the first place.
So it seems like it was indeed needed at some point but could've been removed in #1154 already.
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.
very clean changes 🧹
will use the itest to better understand certain details, overall looking good! 👍
AssetSpecifier: specifier, | ||
Amount: amt, | ||
CoinSelectType: tapsend.Bip86Only, | ||
DistinctSpecifier: true, |
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.
after seeing that we default to true
here, I'm not sure in which scenarios we'd want to use false
?
Will we ever have the freedom of choosing whether we'll use the ID or the groupKey of a specifier that has both? Seems to me that the intuitive choice is always picking the groupKey, but LMK what you think
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.
The thing is, we already have that case. Remember when we added the itest that creates channels from a grouped asset (but only a single tranche so it would work with the code we had before this change)?
In that case we only specify the asset ID in the funding request. But because it's a grouped asset, the new logic would then also query the DB and set the group key in the specifier. And in that case we actually want to query with both, so we only fetch exactly the asset ID we supplied (and not just any asset from the group).
// Then we'll create the OP_RETURN leaf with the asset ID to make the | ||
// resulting script key unique. | ||
opReturnLeaf, err := asset.NewNonSpendableScriptLeaf( | ||
asset.OpReturnVersion, id[:], |
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.
could there be a proof collision here if we somehow ended up having the same id[:]
?
Is this collision taking place within the transfer's namespace or is it global?
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 only within a single on-chain outpoint (e.g. the funding transaction). The universe has uniqueness over the tuple (group_key_or_asset_id, script_key, outpoint)
. Which for multiple tranches of the same group key would all be the same for the individual UTXOs committed to the channel (because group_key
would be set since it's a grouped asset, see how the universe ID works). So we just need to de-duplicate the script key by adding the asset ID to its tapscript path. And since we'd only at most have one UTXO per asset ID, there should be no additional collision possible (see comments above about merging multiple inputs of the same asset ID into a single one within the channel).
if err != nil { | ||
return lfn.Errf[wire.TxWitness]("unable to serialize control "+ | ||
"block: %w", err) | ||
useUniqueScriptKey := len(vPackets) > 1 |
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 assume that two different vPacket's may not use the same asset ID?
if yes could throw a comment for clarity
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.
Correct. The definition for a vPacket that we arrived at is that it reflects all the changes of a single assetID within one on-chain transaction. Meaning that all the inputs and all the outputs of one asset ID within a transfer would be merged into the same vPacket. Will add a comment to make more clear.
@@ -7073,7 +7073,7 @@ func (r *rpcServer) FundChannel(ctx context.Context, | |||
} | |||
|
|||
assetID, groupKey, err := parseAssetSpecifier( | |||
req.GetAssetId(), "", nil, "", | |||
req.GetAssetId(), "", req.GetGroupKey(), "", |
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.
added something similar in 667080c
5f63baa
to
6e98981
Compare
@guggero, remember to re-request review from reviewers when ready |
We've decided to only ever use a single asset ID within a virtual packet. Which means we'd use multiple virtual packets to represent transfers for different asset IDs. So we don't need those early TODOs anymore.
We defined a while ago that an entry in the transfer table corresponds to one on-chain transaction. That means, such a transfer can house multiple virtual packets from different asset IDs. The transfer inputs already reflect that correctly. But the outputs were lacking the asset ID that indicates what virtual packet the output belonged to. This commit fixes the problem by adding the asset ID to the transfer output struct.
The wrong error was assigned, which caused the proof delivery failures to not surface correctly.
Not sure why the asset ID was never used in the database proof query. But now that we potentially have multiple asset outputs with the same script key but different asset IDs in the same anchor transaction, it's crucial we also add this to the query.
We changed the semantics around transfers when we implemented committing multiple asset transfers to a single TX. We now allow multiple virtual packets to be combined in a single transfer. Which means we have one entry in the transfer table for each on-chain transaction. But each transfer can have inputs and outputs from different asset IDs (meaning different active vPackets). This implies that it's also possible to have multiple transfer outputs with the same script key but different asset IDs, which means we need to change the way we identify the proof files.
One allocation entry means one on-chain output. So we should not create multiple allocations for the same balance (e.g. if there are multiple asset pieces with different asset IDs in the local or remote balance). Instead we just create one allocation for the sum, then let the coin distribution algorithm decide what output goes where.
We're doing the same thing in three places, this commit unifies the code by using the same function everywhere.
We always add the witness to the commitment TX before we create any of the commitment outputs. So this witness should already be correct.
To avoid a proof collision in the universe, we need to make sure we derive unique funding output script keys for multiple asset IDs within the same channel funding outpoint. We do that by adding a second OP_RETURN leaf into the tapscript tree of the funding script key. That should ensure uniqueness of the top-level Taproot output key and only requires us to slightly alter the control block when creating the witness (it will now include an inclusion proof element for the OP_RETURN leaf).
6e98981
to
5f672a7
Compare
Addressed all comments. |
Adds the ability to fund multiple tranches of a grouped asset into a channel.
Corresponding itest: lightninglabs/lightning-terminal#987