-
Notifications
You must be signed in to change notification settings - Fork 132
[group key addrs 7/7]: send and receive support for V2 addresses #1587
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: more-group-key-refactors
Are you sure you want to change the base?
Conversation
proof/send.go
Outdated
// ScriptKeyDerivationUniquePedersen means the script key is derived | ||
// using the address's recipient ID key and a single leaf that contains | ||
// an un-spendable Pedersen commitment key | ||
// (OP_CHECKSIG <NUMS_key + asset_id * G>). This can be used to |
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.
Ah ok, so this is just to ensure unique script keys for each unique asset ID in a send fragment, but we still plan on inserting that new asset TLV containing the shared secret hash to make all asset commitments for a given addr unique?
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. Will add the odd TLV value to make sure identical sends (e.g. same amounts and same asset IDs) will still produce a different on-chain output for better privacy.
// TxProof is the proof of the transaction that contains the asset | ||
// outputs that are being sent. This is used as proof-of work to show | ||
// to the auth mailbox server. | ||
TxProof TxProof |
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.
Looks like we're duplicating the block header + height here across TxProof
and SendFragment
. Or is the idea that the encoding is smart enough to only encode once and copy the fields over as needed?
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 envelope itself isn't encoded, only the SendFragment
within. So the TxProof
here is is what's required to send the fragment to the server.
Perhaps "envelope" isn't the best analogy here, as that's also sent along with a letter/message. Going to rename this to SendManifest
instead, perhaps that makes more sense.
proof/courier.go
Outdated
Proof: &mboxrpc.SendMessageRequest_TxProof{ | ||
TxProof: rpcProof, | ||
}, | ||
// TODO(guggero): what value? |
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.
Good question...I think will depend on what we end up using as the max block height/expiry value for the server.
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.
Went with 210k blocks for now, can perhaps make it configurable later on. And currently we don't delete messages yet. Not sure if we want to add message cleanup (both after user ACK and expiry) in this PR or whether I should create an issue for it.
rpcserver.go
Outdated
if err != nil { | ||
return nil, fmt.Errorf("unable to decode "+ | ||
"addr: %w", err) | ||
} | ||
|
||
// TODO(guggero): Need to add send fragment envelopes to |
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 spin out into a sub-issue.
Do the vPSBTs actually need to be updated though? IIUC, we just need to specify the new proof courier semantics.
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.
Actually, I think we should be able to support the vPSBT flow by just adding the TAP address as a new field to the vOutput
. Otherwise we'd have to add safety checks to make sure users don't use the vPSBT flow with V2 addresses and then fail at the proof transfer. So adding vPSBT support is probably the easier way.
Added it as a TODO that I'm going to address before re-requesting review.
tapfreighter/chain_porter.go
Outdated
"service handle: %w", err) | ||
} | ||
|
||
err = courier.DeliverFragment(ctx, envelope) |
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.
Any reason to not fold this into the loop below where we'll attempt to send out the proofs/fragments in parallel?
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.
Good point. The cardinality is different for fragments vs. proofs. But I made it so that we can always try again to send the same message, so we just try in the same loop. Also makes the whole backoff and re-try logic work seamlessly.
taprpc/taprootassets.proto
Outdated
// required for V2 addresses that don't specify an amount (amount of zero). | ||
// If an address does specify an amount, then it cannot be overridden in | ||
// this map. | ||
map<string, uint64> address_amounts = 5; |
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, for parsing, what if we allowed tap_addrs
to be blank if this is specified, then we only read out address_amounts
. This way a user doesn't need to specify an addr twice if it's a v2 addr.
Another alternative would be to upgrade tap_addrs
to be a new message type, that can specify the addr, then also a custom amount.
Mainly thinking about how we can streamline the process of a new user potentially unknowingly using a re-useable addr.
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.
Good point here. I updated the RPC to have two fields, the old tap_addrs
and new addresses_with_amounts
list, both being mutually exclusive. So we can deprecate the older one eventually.
a5c2fd0
to
b482ea3
Compare
b482ea3
to
8f8e164
Compare
094b31e
to
3d1a126
Compare
8f8e164
to
060159b
Compare
dc426c6
to
74953ff
Compare
db7f36c
to
a3dbdd3
Compare
5687720
to
46d9159
Compare
a3dbdd3
to
ad2c782
Compare
e5fdf7e
to
eea552e
Compare
ae1aefb
to
cd9b477
Compare
eea552e
to
555c711
Compare
// addresses_with_amounts list to specify the amount to send to each | ||
// address. The tap_addrs and addresses_with_amounts lists are mutually | ||
// exclusive, meaning that if addresses_with_amounts is set, then tap_addrs | ||
// must be empty, and vice versa. | ||
repeated string tap_addrs = 1; | ||
|
||
// The optional fee rate to use for the minting transaction, in sat/kw. | ||
uint32 fee_rate = 2; |
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.
Why do we not use the field name sat_per_vbyte
like in SendCoinsRequest
(https://github.com/lightningnetwork/lnd/blob/6b326152d419d0777f739a23c0d9330164d26c4d/lnrpc/lightning.proto#L1213-L1217)?
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.
Legacy reasons, should fix in separate PR.
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.
Created #1661 to track this.
// addresses_with_amounts list to specify the amount to send to each | ||
// address. The tap_addrs and addresses_with_amounts lists are mutually | ||
// exclusive, meaning that if addresses_with_amounts is set, then tap_addrs | ||
// must be empty, and vice versa. | ||
repeated string tap_addrs = 1; | ||
|
||
// The optional fee rate to use for the minting transaction, in sat/kw. | ||
uint32 fee_rate = 2; |
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 we get a target_conf
option like in SendCoinsRequest
(https://github.com/lightningnetwork/lnd/blob/6b326152d419d0777f739a23c0d9330164d26c4d/lnrpc/lightning.proto#L1209-L1213)?
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.
Yes, but not in this PR.
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.
Created #1662 to track this.
@@ -1374,12 +1409,17 @@ message AddrReceivesResponse { | |||
} | |||
|
|||
message SendAssetRequest { |
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.
Do we want to add a min_confs
and/or spend_unconfirmed
option like in SendCoinsRequest
(https://github.com/lightningnetwork/lnd/blob/6b326152d419d0777f739a23c0d9330164d26c4d/lnrpc/lightning.proto#L1231-L1238) or does no one use those options?
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.
Yes, but not in this PR.
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.
spend_unconfirmed
will never work with Taproot Assets, because to create a valid proof the transaction always has to be in a block. Or I guess not "never", just not without a major protocol enhancement/upgrade.
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.
Created #1663 to track this
I need some guidance on what litd commit I can put this PR into. If I try to build from lightninglabs/lightning-terminal@a0915a3 using the
|
I'm also using the |
2de283d
to
3bbfa95
Compare
I've added an integration test for that in the latest push.
I've added release notes (and the template) to the last commit. We'll need to back-fill this with PRs already merged for |
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.
Maybe we should spin the first 7 commits into their own PR since they don't involve db migration. I've reviewed up to the 10th commit: multi: add addr v2 send support
.
tapfreighter/chain_porter.go
Outdated
// To make sure we have the correct internal key with | ||
// the family and index set, we attempt to fetch it | ||
// from the database. If it exists, then we know we | ||
// stored it with the correct information. | ||
dbKey, err := p.cfg.AssetWallet.FetchScriptKey( | ||
ctx, key.PubKey, | ||
) | ||
if err == nil { | ||
return p.cfg.KeyRing.IsLocalKey( | ||
ctx, dbKey.RawKey, | ||
) | ||
} |
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 don't think we're handling all database errors correctly here. For example, if we fail to read from the database, an error might be returned and we miss the opportunity to classify it as local.
We should handle errors normally here and base the remote/local distinction on address.ErrScriptKeyNotFound
.
Do we need to keep the fallback method below? It would be cleaner and easier to debug any issues if we relied solely on the database query method above.
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 added an error return value to the callback.
Yes, we need the fallback, in case the user only registered the internal key but not the outer script key.
authmailbox/server.go
Outdated
if err != nil { | ||
return nil, proof.ErrTxMerkleProofExists | ||
} |
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 you mean to do if err == nil
here?
// different ways an asset can be spent. For V2 addresses, this key is | ||
// not the tweaked Taproot output key but instead the bare/raw/internal | ||
// script key. The sender will use this key to encrypt the send fragment | ||
// that they post to the proof courier's mailbox. The raw script key | ||
// will also be used by the sender to derive different Taproot output | ||
// script keys for each asset ID. | ||
ScriptKey btcec.PublicKey |
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.
// not the tweaked Taproot output key but instead the bare/raw/internal
// script key.
"tweaked Taproot output key" is not a thing, right? The Taproot output key itself is the result of tweaking the internal public key; it is not something that gets tweaked further.
but instead the bare/raw/internal script key.
Is it correct to just say: "but the Taproot internal public key"?
// The amount is allowed to be zero for V2 addresses, where the sender | ||
// will post a fragment containing the asset IDs and amounts to the | ||
// proof courier's mailbox. | ||
Amount uint64 |
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.
All these edge cases for V2, would be nice to make Tap
into an interface and have a distinct TapV2
type. And then struct documentation and fields can be specific to the V2 version.
tappsbt/decode.go
Outdated
// addressDecoder returns a decoder function that can handle nil Taproot | ||
// addresses. | ||
func addressDecoder(a **address.Tap, |
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 would name this newAddressDecoder
to hint that's it's a function factory rather than the actual decoder function. And same below for encode. I'm not sure what's most golang idiomatic.
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.
All the xxxDecoder
and xxxEncoder
functions in this file work the same way. I don't necessarily disagree with you but want to keep things consistent and not expand the diff by renaming all of the other ones too.
// SendOutput holds the information about a single asset output that was sent | ||
// as part of an incoming asset transfer. Each event can have multiple outputs, | ||
// in case multiple tranches of a grouped asset were transferred using a V2 | ||
// address. | ||
type SendOutput struct { |
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.
Perhaps we can generalize the name to AssetOutput
, since the distinction between send and receive doesn’t seem relevant here. Also, SendOutput
doesn’t clearly convey that this refers to an asset-level output.
-- An event can now have multiple outputs, in case multiple tranches of a | ||
-- grouped asset were sent as part of a V2 address transfer. | ||
CREATE TABLE IF NOT EXISTS addr_event_outputs ( |
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.
These are asset outputs I think. To give more clarity and confidence to a future reader, can we say: An event can now have multiple asset outputs
// We generate an ephemeral key pair that we'll use to encrypt the | ||
// send fragment before sending it to the receiver. The public key of | ||
// the pair will be part of the encrypted payload, so we can throw away | ||
// the private key after doing the ECDH operation below. |
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 we should mention the "shared secret" in this comment, something like:
The private key is used solely to derive a shared secret. Since the counterparty receives the public key, they can derive the same shared secret on their side. Once we've completed the derivation, the private key is no longer needed and can be safely discarded.
Please update Line 33 in 3bbfa95
|
@ZZiigguurraatt merged here: #1657 |
you're saying this just needs a rebase then? |
yes, on it |
This commit adds a new proof courier protocol that's based on the universe RPC protocol but also adds the auth mailbox capability. We assume that all our official universes will be upgraded to run an auth mailbox compatible universe, so we change the protocol for all our default instances.
This is a preparatory commit that deals with all the database changes required to allow a single address receive event to have multiple asset outputs, and with that multiple proofs associated with it.
As a preparation for creating events from wire transactions that aren't notified by the lnd wallet (and therefore don't come through the lndclient calls), we make the arguments to GetOrCreateEvents a bit more explicit.
We refactor the proof handling to allow multiple outputs to be processed and proofs for them to be fetched.
Adds the new --addrs_with_amounts flag that allows to specify the amount to send to an address that allows sending user-defined amounts (address V2).
3bbfa95
to
b581962
Compare
are ideal as re-usable, long-term static addresses (with on-chain privacy | ||
guarantees similar to BIP-0352 Silent Payments). | ||
V2 addresses require the use of a proof courier that supports the | ||
new `authmailbox+universerpc://` protocol. Any `tapd` that runs with version |
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 thought we decided we were going to make it auto discover the courier type, or do you want to create an issue to do that in a separate commit?
Depends on #1658.
This PR introduces a new V2 address that: