-
Notifications
You must be signed in to change notification settings - Fork 118
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
[7/?] StaticAddr: Loop-In #786
Conversation
d807a95
to
e635d63
Compare
d813b20
to
447d70d
Compare
7ee61f6
to
3eb5bd2
Compare
e635d63
to
2c94110
Compare
a151ffd
to
d69e9d5
Compare
2c94110
to
ae3a983
Compare
ae3a983
to
0e92035
Compare
127d76c
to
a9d9e6f
Compare
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.
Reviewed only the first commit with protobuf types "looprpc: static address loop-ins".
|
||
/* | ||
EXPIRED indicates that the deposit has expired and the sweep transaction | ||
has been sufficiently confirmed. | ||
*/ | ||
EXPIRED = 6; | ||
EXPIRED = 10; |
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 rename this state to EXPIRY_SWEEP_CONFIRMED
?
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 states were introduced in a different PR, so I'd add create a separate refactor PR to fix these up.
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.
Reviewed another 3 commits:
- sqlc: loop-in tables and queries
- log: static address loop-in logging
- staticaddr: deposit changes to be squashed
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.
Really awesome and huge work! 🥇 I only have a few questions and nit comments left.
tACK, LGTM 🌊
WHERE swap_hash = u.swap_hash | ||
) | ||
WHERE | ||
',' || $1 || ',' LIKE '%,' || u.update_state || ',%' |
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 a nice trick! I'd say sqlc.slice
is slightly more readable though, I had to test this filter condition a bit to make sure it works as expected.
@@ -34,7 +34,7 @@ const ( | |||
|
|||
// DefaultTransitionTimeout is the default timeout for transitions in | |||
// the deposit state machine. | |||
DefaultTransitionTimeout = 1 * time.Minute | |||
DefaultTransitionTimeout = 5 * time.Second |
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.
sgtm
staticaddr/deposit/manager.go
Outdated
} | ||
|
||
// AllOutpointsActiveDeposits checks if all deposits referenced by the outpoints | ||
// are active and in the specified state. | ||
// are in out in-mem active deposits map and in the specified state. If |
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: in out => in our
|
||
m.Lock() | ||
defer m.Unlock() | ||
_, deposits := m.toActiveDeposits(&outpoints) |
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 may not cause obvious problems, but iiuc AllOutpointsActiveDeposits
(and other functions using toActiveDeposits(...)
may suffer from non-atomicity. Instead of separately fetching active deposits, then locking them if they are in the target state we should be atomically filter and lock them.
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.
You are right, there's a gap in coverage. It could be that a deposit is removed from the active map after the toActiveDeposits
call.
Instead, we can m.mu.Lock()
throughout the AllOutpointsActiveDeposits
call and in other calls sites, so the in-mem deposit map is locked during filtering.
Left a comment that the caller of toActiveDeposits
should call mu.Lock
on the manager.
@@ -1429,12 +1429,17 @@ func (s *swapClientServer) WithdrawDeposits(ctx context.Context, | |||
} | |||
} | |||
|
|||
err = s.withdrawalManager.WithdrawDeposits(ctx, outpoints) | |||
txhash, pkScript, err := s.withdrawalManager.DeliverWithdrawalRequest( |
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: time to squash these.
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 was thinking to squash them on the staging branch once this PR is merged to it.
} | ||
|
||
// toStaticAddressLoopIn converts sql rows to an instant out struct. | ||
func toStaticAddressLoopIn(_ context.Context, network *chaincfg.Params, |
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.
+1, perhaps again this could come as a follow up PR to reduce scope creep.
fee := feeRate.FeeForWeight(f.loopIn.htlcWeight()) | ||
if fee > maxHtlcTxFee { | ||
// Abort the swap by pushing empty sigs to the server. | ||
pushEmptySigs() |
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: Why not push empty sigs for the above errors?
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.
Great point, if added it in all error cases after the call ServerStaticAddressLoopIn
.
if err != nil { | ||
err = fmt.Errorf("unable to derive htlc timeout sweep "+ | ||
"address: %w", err) | ||
|
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: Why not push empty sigs here?
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
deposit.SweepHtlcTimeout, | ||
) | ||
if err != nil { | ||
log.Errorf("unable to transition "+ |
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.
In this case manual intervention would be needed iiuc?
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 timeout transaction would be published and funds would be safe, but the client would show the deposits in state Deposited
(cause the payment deadline hit and reset the deposits). So for sanity the state would have to be changed manually, yes.
log.Errorf("%v: %w", txLabel, err) | ||
f.LastActionError = err | ||
return err | ||
} |
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.
Ideally we also want to keep on republishing the timeout sweep (and therefore ignore errors handled here). We don't currently republish unless we recovered and we have one in the mempool already but what happens if it is priced out?
// ServerStaticAddressLoopIn initiates a static address loop-in swap. The | ||
// server will respond with htlc details that the client can use to | ||
// construct and sign the htlc tx. | ||
rpc ServerStaticAddressLoopIn (ServerStaticAddressLoopInRequest) |
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.
wdyt about moving this to a seperate file and service?
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 it makes sense given the number of endpoints and also given the pattern we have with reservation/instantout. I think that's a candidate for a follow-up 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.
For awareness: a new service would be a breaking change
|
||
// The info the server used to generate the standard fee partial htlc tx | ||
// sigs. | ||
ServerHtlcSigningInfo standard_htlc_info = 3; |
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 could be a map as well https://protobuf.dev/programming-guides/proto3/#maps
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.
That'd be much cleaner, although these maps don't allow enum keys, just arbitrary <string,string> etc. mappings, so we'd have to setup keys for this map in code rather than in the protobuf definition, which makes it a little less clear what's transported imo.
Can change it ofc if there's strong opinions.
// Once the htlc tx is initiated, we store the loop-in in the database. | ||
err = f.cfg.Store.CreateLoopIn(ctx, f.loopIn) | ||
if err != nil { | ||
err = fmt.Errorf("unable to store loop-in in db: %w", err) |
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.
here as well called pushEmptySigs()
.
63f4946
to
a50d3d7
Compare
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.
LGTM tACK! 🥳 🥳 🥳 🥳
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.
LGTM 🏆
Few notes
Description: ` | ||
Requests a loop-in swap based on static address deposits. After the | ||
creation of a static address funds can be send to it. Once the funds are | ||
confirmed on-chain they can be swapped instantaneously. If deposited |
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 propose to add the number of confirmations needed here.
// Debugf logs a debug message with the loop-in swap hash. | ||
func (f *FSM) Debugf(format string, args ...interface{}) { | ||
if f.loopIn == nil { | ||
log.Infof(format, args...) |
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 be "log.Infof" I 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.
good catch, should be log.debugf
.
) | ||
|
||
const ( | ||
defaultConfTarget = 3 |
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 propose to change to 6
// initiation time of the swap. If more than the swaps configured payment | ||
// timeout has passed, the remaining time will be negative. | ||
func (l *StaticAddressLoopIn) RemainingPaymentTimeSeconds() int64 { | ||
elapsedSinceInitiation := time.Since(l.InitiationTime).Seconds() |
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 propose to add lnd/clock instance to StaticAddressLoopIn and use it here, so this place can be fully tested (by passing a test clock). I propose to add a unit test for this method, testing it at various times.
90c9c2c
to
f959936
Compare
2b5b1c4
to
d4967f4
Compare
rpcs for loop client and server are added to facilitate static address swap requests.
We add CRU procedures for static address swaps.
d4967f4
to
ac65726
Compare
The static address state machine adds states to reflect the state of a deposit during a loop-in swap.
In this commit we introduce maximum fee percentages for the static loop-in htlc transactions. Since the server has the ability to publish htlc transactions without settling the swap payment we have to restrict the amount the server allocates for fees of these transactions.
In this commit we add the static address loop-in state machine and its orchestration through the manager.
This PR introduces loop-ins of static address deposits.
The client/server interaction is depicted below:
Open tasks that need resolution until this PR can leave draft mode:
Previously merged PRs:
#642
#650
#677
#719
#721
#750
master...static-addr-staging