Skip to content
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

[7/?] StaticAddr: Loop-In #786

Open
wants to merge 9 commits into
base: static-addr-staging
Choose a base branch
from

Conversation

hieblmi
Copy link
Collaborator

@hieblmi hieblmi commented Jun 28, 2024

This PR introduces loop-ins of static address deposits.
The client/server interaction is depicted below:

image

Open tasks that need resolution until this PR can leave draft mode:

  • Refactorings, unify code with reservations/instantout
  • DB storage & fsm recovery
  • Manual quote acceptance
  • FSM transition consistency checks
  • unit + integration tests
  • signing multiple fee-version htlcs, potential anchor output

Previously merged PRs:

#642
#650
#677
#719
#721
#750

master...static-addr-staging

@hieblmi hieblmi self-assigned this Jun 28, 2024
@hieblmi hieblmi marked this pull request as draft June 28, 2024 12:59
@hieblmi hieblmi force-pushed the static-addr-staging branch 2 times, most recently from d807a95 to e635d63 Compare July 1, 2024 10:51
@hieblmi hieblmi force-pushed the static-addr-7 branch 2 times, most recently from d813b20 to 447d70d Compare July 9, 2024 10:13
@hieblmi hieblmi force-pushed the static-addr-7 branch 3 times, most recently from 7ee61f6 to 3eb5bd2 Compare July 17, 2024 07:11
@hieblmi hieblmi force-pushed the static-addr-7 branch 4 times, most recently from a151ffd to d69e9d5 Compare July 18, 2024 08:19
@hieblmi hieblmi force-pushed the static-addr-7 branch 6 times, most recently from 127d76c to a9d9e6f Compare July 30, 2024 14:38
@hieblmi hieblmi marked this pull request as ready for review July 30, 2024 14:38
@hieblmi hieblmi requested review from bhandras and starius July 30, 2024 14:38
Copy link
Collaborator

@starius starius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed only the first commit with protobuf types "looprpc: static address loop-ins".

looprpc/client.proto Show resolved Hide resolved

/*
EXPIRED indicates that the deposit has expired and the sweep transaction
has been sufficiently confirmed.
*/
EXPIRED = 6;
EXPIRED = 10;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename this state to EXPIRY_SWEEP_CONFIRMED?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These states were introduced in a different PR, so I'd add create a separate refactor PR to fix these up.

looprpc/client.proto Show resolved Hide resolved
looprpc/client.proto Show resolved Hide resolved
swapserverrpc/server.proto Outdated Show resolved Hide resolved
swapserverrpc/server.proto Show resolved Hide resolved
// Examples:
// loopd/v0.10.0-beta/commit=3b635821
// litd/v0.2.0-alpha/commit=326d754
string user_agent = 7;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is StaticAddressLoopInRequest.initiator appended to this field? Can you provide an example of such a user agent in the description, denoting where is the initiator part, please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again thanks for poking, I was using the initator as the user agent, but there's actually a function that appends the right info so that it matches the examples. Have a look at https://github.com/lightninglabs/loop/blob/master/version.go#L57. Will update the PR

swapserverrpc/server.proto Outdated Show resolved Hide resolved
Comment on lines 795 to 846
// The address that the server wants to sweep the static address deposits
// to.
string sweepless_sweep_addr = 3;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we replace an address with an unsigned tx to provide more flexibility?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With flexibility you mean that if the sweepless sweep tx has different parameters in the future we wouldn't have to modify these rpc messages, but the client would just blindly sign any tx?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is the idea. Then we can implement sweep batching without asking clients to update.

Copy link
Collaborator

@starius starius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed another 3 commits:

  • sqlc: loop-in tables and queries
  • log: static address loop-in logging
  • staticaddr: deposit changes to be squashed

loopdb/sqlc/queries/static_address_loopin.sql Outdated Show resolved Hide resolved
loopdb/sqlc/queries/static_address_loopin.sql Outdated Show resolved Hide resolved
staticaddr/deposit/fsm.go Outdated Show resolved Hide resolved
staticaddr/deposit/fsm.go Show resolved Hide resolved
staticaddr/deposit/fsm.go Outdated Show resolved Hide resolved
staticaddr/deposit/fsm.go Outdated Show resolved Hide resolved
@lightninglabs-deploy
Copy link

@bhandras: review reminder
@starius: review reminder
@sputn1ck: review reminder

Copy link
Member

@bhandras bhandras left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome and huge work! 🥇 Did the first detailed pass, going to spend some more time with testing and reading through the whole code again now 🧪

// Check that the label is valid.
err := labels.Validate(req.Label)
if err != nil {
return fmt.Errorf("invalid label: %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: %w here and everywhere else where we decorate errors.

// InitiateLoopIn initiates a loop-in swap. It passes the request to the server
// along with all relevant loop-in information.
func (m *Manager) InitiateLoopIn(req *loop.StaticAddressLoopInRequest) error {
loopIn := &StaticAddressLoopIn{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could reorder the code a bit so that validation of all fields that needs to be validated is followed by the swap creation/init etc.

deposit_outpoints BLOB NOT NULL,

-- htlc_tx contains the htlc transaction without signatures.
htlc_tx BLOB,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think storing the htlc is only minimally convenient vs regenerating it at runtime. I'd consider not storing it as a better alternative.

htlc_tx_fee_rate BIGINT NOT NULL,

-- htlc_timeout_sweep_tx_hash contains the htlc timeout sweep tx hash.
htlc_timeout_sweep_tx_hash BLOB,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to store the string converted tx id instead as it is easier to use if queried manually.


-- deposit_outpoints is a concatenated list of outpoints that are used for
-- this swap. The list has the format txid1:idx;txid2:idx;...
deposit_outpoints BLOB NOT NULL,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps consider human readable storage so it is easier to use if queried manually.

err = f.cfg.WalletKit.PublishTransaction(f.ctx, timeoutTx, txLabel)
if err != nil {
if !strings.Contains(err.Error(), "output already spent") {
log.Errorf("%v: %v", txLabel, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: f.Errorf, and some more context could be useful.


// Retrieve all deposits referenced by the outpoints and ensure that
// they are in state Deposited.
deposits, active := m.cfg.DepositManager.AllStringOutpointsActiveDeposits( //nolint:lll
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@@ -118,21 +118,53 @@ func (f *FSM) InitHtlcAction(_ fsm.EventContext) fsm.EventType {
}

f.loopIn.HtlcCltvExpiry = loopInResp.HtlcExpiry

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could you pls add more detail to the commit message?

fee := f.loopIn.HtlcTxFeeRate.FeeForWeight(f.loopIn.htlcWeight())
feeRate := chainfee.SatPerKWeight(loopInResp.HtlcFeeRate)
fee := feeRate.FeeForWeight(f.loopIn.htlcWeight())
if fee > f.loopIn.TotalDepositAmount()/5 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: instead of writing TotalDepositAmount()/5 everywhere, please add a variable tracking this value.

staticaddr/loopin/actions.go Outdated Show resolved Hide resolved
@hieblmi
Copy link
Collaborator Author

hieblmi commented Sep 9, 2024

Thanks @bhandras for your review and the nice suggestions. I've addressed your comments.

@hieblmi hieblmi force-pushed the static-addr-7 branch 4 times, most recently from d394459 to 2fe6195 Compare September 11, 2024 13:44
repeated bytes sweepless_client_sigs = 3;
}

message PushStaticAddressSweeplessSigsResponse {
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add line end

cmd/loop/main.go Outdated
@@ -280,6 +280,12 @@ func displayInDetails(req *looprpc.QuoteRequest,
"wallet.\n\n")
}

if req.DepositOutpoints != nil {
fmt.Printf("On-chain fees for static address loop-ins is not " +
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/is not/are not/

@@ -229,9 +257,21 @@ func summary(ctx *cli.Context) error {
case "withdrawn":
filterState = looprpc.DepositState_WITHDRAWN

case "loopingin":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/loopingin/looping_in/
s/loopedin/looped_in/

here and in the help message.

for _, outpoint := range outpoints {
if deposit, ok := depositMap[outpoint]; ok {
sum += deposit.Value
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do something if the outpoint is not in depositMap? Log or return an error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added an error and fail the loop-in attempt if that scenario hits.

return nil
}

func sumDeposits(outpoints []string, deposits []*looprpc.Deposit) int64 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if an outpoint or a deposit is non-unique? Should we check this and return an error if this is detected?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added containsDuplicates after the outpoint selection is done.

ClientKeyIndex: int32(loopIn.HtlcKeyLocator.Index),
}

joinedOutpoints := strings.Join(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that elements of loopIn.DepositOutpoints do not contain outpointSeparator

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡

}

// toStaticAddressLoopIn converts sql rows to an instant out struct.
func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unit test.

@@ -430,20 +408,19 @@ func (m *Manager) handleWithdrawal(deposits []*deposit.Deposit,
deposit.Withdrawn,
)
if err != nil {
log.Errorf("Error transitioning deposits: %v",
log.Errorf("Error transitioning deposits: %w",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%w should not be used in log. It will be logged as a wrong format.

I found more instances of this issue:

loop$ git grep -w log | grep %w
staticaddr/deposit/actions.go:                  log.Errorf("%v: %w", txLabel, err)
staticaddr/deposit/actions.go:          log.Debugf("error while sweeping expired deposit: %w", err)
staticaddr/deposit/manager.go:                          log.Errorf("Error sending OnStart event: %w",
staticaddr/deposit/manager.go:                  log.Errorf("Error sending OnStart event: %w", err)
staticaddr/loopin/actions.go:           log.Warnf("unable to decode sweep address: %w", err)
staticaddr/loopin/actions.go:                   log.Errorf("%v: %w", txLabel, err)
staticaddr/loopin/fsm.go:               log.Errorf("Error checking if loop-in is stored: %w", err)
staticaddr/loopin/fsm.go:               log.Errorf("Error updating loop-in: %w", err)
staticaddr/loopin/manager.go:                           log.Errorf("Error sending OnStart event: %w",
staticaddr/loopin/manager.go:                   log.Errorf("Error sending OnNewRequest event: %w", err)
staticaddr/withdraw/manager.go:                         log.Errorf("Error republishing withdrawals: %w",
staticaddr/withdraw/manager.go:                 log.Errorf("%v: %w", txLabel, err)
staticaddr/withdraw/manager.go:                         log.Errorf("Error transitioning deposits: %w",
staticaddr/withdraw/manager.go:                 log.Errorf("Error waiting for confirmation: %w", err)
staticaddr/withdraw/manager.go:                 log.Errorf("Error republishing withdrawal: %w", err)

Copy link
Collaborator Author

@hieblmi hieblmi Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the hints, fixed.

bytes swap_hash = 1;

// The nonces that the client used to generate the htlc sigs.
repeated bytes htlc_client_nonces = 2;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to make a sub-message including two fields: htlc_client_nonces and sigs. Then we can use it 3 times for normal, high and extremely high fee levels.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

int32 htlc_expiry = 2;

// The nonces that the server used to generate the partial htlc tx sigs.
repeated bytes htlc_server_nonces = 3;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to group the fields: make a sub-message including two fields: htlc_server_nonces and fee_rate. Then we can use it 3 times here for normal, high and extremely high fee levels.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very good suggestion, thanks

@sputn1ck
Copy link
Member

sputn1ck commented Sep 17, 2024

Doing some manual testing now, gonna add some notes here and update them:

  • loopcli s does not show static address help, but an error
  • loopcli s w --utxo=a19442026905827007c97bf7eb286e872db6d47dc24a61d83ac96fb806254696:1 does not work when less than 3 confs, maybe show different error message or do not show unconfirmed utxos, or add bool ready to spend
  • I think it makes sense to move the static loop in in under the static command
  • I sometimes I encounter an error with looping in and just see swap canceled without any further information, I cannot pinpoint why this error is happening yet (not trying to double spend an outpoint)
  • loop-cli in s --all does not send correct error message when i have deposits with less than 3 confs

@hieblmi
Copy link
Collaborator Author

hieblmi commented Sep 19, 2024

All addressed @sputn1ck, would be great if you give it another go. Also consider testing this PR within #814 which gives you better look-up commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants