-
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: main
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
feee5ca
to
a74f887
Compare
Not sure if you want to address everything in this PR, but do we want to make a "meta" issue like #1446 to track changes to bip-tap.mediawiki that need to be addressed later? Like I feel like there is probably a lot of new stuff related to this PR that is not documented anywhere clearly? |
a74f887
to
805d197
Compare
Added that here: #293 |
805d197
to
b8ae454
Compare
I've addressed all comments and also added a commit that addresses a user experience issue: For a v2 address with a group key, we don't really want to show an asset ID. So it's zeroed out. I think that introduced a problem in one of the itests, which I'll fix tomorrow. I'll also add more unit test coverage for the receive flow to the asset custodian. But even with the above two TODOs, I still think this is ready for another round of review. |
To make sure we support future recovery cases, we don't want to delete any messages for outpoints that haven't been spent. So we rename and clarify the meaning of the expiry block delta. No cleanup mechanism is currently implemented anyway, so this just serves as documentation for when we do add that feature in the future.
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).
For v2 addresses we want the address to only show the group key if a group key is used. The value of the asset ID becomes meaningless for an address that supports group keys. Unfortunately we can't really _remove_ the value from the address (it's always encoded). But we can set it to all zeroes so it's more apparent to the user that it's not used.
b8ae454
to
601bb7e
Compare
Okay, I've addressed all TODOs. And with the litd itest fixed, this should now also be testable on the |
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've reviewed until commit multi: don't use lndclient transaction for event
All looks good so far!
// A user-defined expiry block height delta for the message. Once the | ||
// outpoint claimed by the proof provided by this message has been spent, | ||
// then after this number of blocks after the spending transaction height | ||
// the message can be considered expired and may be deleted. | ||
uint32 expiry_block_delta = 4; |
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 is this value user-configurable? I would have expected it to be a constant defined by the authmailbox service operator.
If this is indeed intended to be set by the client, should we enforce an upper bound on the field? Even if we’re not performing cleanup yet, it still might makes sense to cap the value.
@@ -155,11 +155,14 @@ func TestAddressInsertion(t *testing.T) { | |||
|
|||
// Make a series of new addrs, then insert them into the DB. | |||
const numAddrs = 5 | |||
proofCourierAddr := address.RandProofCourierAddr(t) | |||
addrVersion := test.RandFlip(address.V0, address.V1) |
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.
Should we include V2 in this random selection? Other similar statements below.
Could potentially gain V2 address test coverage.
var assetID asset.ID | ||
copy(assetID[:], dbOutput.AssetID) |
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 use asset.NewIDFromBytes
here
// This code path is only for V0 and V1 | ||
// addresses, which only have a single output. |
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.
sanity check the addr version here and below in mapToTapAddr
?
// Ensure all addrs are of the same asset specifier, as the | ||
// wallet only supports funding for one asset or group at a | ||
// time. | ||
// | ||
// TODO(guggero): Support creating multiple virtual packets, one | ||
// for each asset ID when the user wants to send multiple asset | ||
// IDs at the same time without going through the PSBT flow. | ||
firstSpec := asset.NewSpecifierOptionalGroupPubKey( | ||
tapAddrs[0].AssetID, tapAddrs[0].GroupKey, | ||
) | ||
if idx > 0 { | ||
spec := asset.NewSpecifierOptionalGroupPubKey( | ||
tapAddrs[idx].AssetID, tapAddrs[idx].GroupKey, | ||
) | ||
if spec != firstSpec { | ||
return nil, fmt.Errorf("all addrs must be of "+ | ||
"the same asset ID or group key %v", | ||
firstSpec) | ||
} | ||
} |
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 the equality check may not work correctly if the user specifies an asset group for one of the addresses and asset IDs from that group for the others. But probably not a big deal.
const ( | ||
// DefaultSendFragmentExpiryDelta is the default number of blocks that | ||
// we expect a send fragment to be valid for after its claimed outpoint | ||
// has been spent. This is roughly equivalent to 90 days. | ||
DefaultSendFragmentExpiryDelta = 12_960 | ||
) |
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 let the user specify, we can just use this const everywhere perhaps
} | ||
|
||
return fundedVPkt, nil | ||
} | ||
|
||
// createSendFragments creates the send fragments for the given funded virtual |
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 creates the send manifests? so maybe we should rename this function and modify the doc comment?
"for output %d: %w", outIdx, err) | ||
} | ||
|
||
if addr.Version < address.V2 { |
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.
if addr.Version != address.V2 {
here? so we can be explicit about V3, but V3 shouldn't get to this point anyway, maybe we'll forget to update this part of the code or something
Maybe we should add a method to address.Tap
. Something like: SupportsSendManifest() bool
? So we leak the version switch less.
@@ -252,6 +252,12 @@ func DescribeAddrs(addrs []*address.Tap) (*FundingDescriptor, error) { | |||
firstAddr.AssetID, firstAddr.GroupKey, | |||
) | |||
|
|||
if firstAddr.Version == address.V2 && firstAddr.GroupKey != nil { |
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.
instead of if firstAddr.Version == address.V2
here maybe a method like firstAddr.SupportsGroupKey
This PR introduces a new V2 address that: