mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-19 13:17:51 +02:00
auctioneer: also recover reserved account
React to the special error that is sent from the auctioneer if it only knows of a reservation. The RPC information is turned into a special error so it can be handled by the account manager later.
This commit is contained in:
parent
85cae324b0
commit
ad597c3bed
1 changed files with 110 additions and 19 deletions
|
|
@ -490,25 +490,50 @@ func (c *Client) connectAndAuthenticate(ctx context.Context,
|
|||
}
|
||||
|
||||
// Did the server find the account we're interested in?
|
||||
switch msg := srvMsg.Msg.(type) {
|
||||
switch {
|
||||
// Account exists, everything's good to continue.
|
||||
case *clmrpc.ServerAuctionMessage_Success:
|
||||
case srvMsg.GetSuccess() != nil:
|
||||
return sub, true, nil
|
||||
|
||||
// The account doesn't exist. If we're recovering accounts,
|
||||
// that's fine. We just skip this account key and try the next
|
||||
// one. If we're not in recovery mode, this is a hard failure.
|
||||
case *clmrpc.ServerAuctionMessage_Error:
|
||||
// We got an error. If we're in recovery mode, this could either
|
||||
// mean the account doesn't exist or there's only a reservation
|
||||
// for it.
|
||||
case srvMsg.GetError() != nil:
|
||||
errMsg := srvMsg.GetError()
|
||||
|
||||
// If we're not in recovery mode we don't expect any
|
||||
// error, this is a hard failure.
|
||||
if !recovery {
|
||||
return nil, false, fmt.Errorf("error "+
|
||||
"subscribing to account: %v",
|
||||
msg.Error.Error)
|
||||
errMsg.Error)
|
||||
}
|
||||
return sub, false, nil
|
||||
|
||||
// If we're in recovery mode, it is possible for an
|
||||
// account to still be in the reservation phase from the
|
||||
// point of view of the auctioneer. We cannot do the
|
||||
// normal recovery in that case because the account
|
||||
// subscription cannot be completed. This needs to be
|
||||
// handled specifically so we return a typed error now.
|
||||
if errMsg.ErrorCode == clmrpc.SubscribeError_INCOMPLETE_ACCOUNT_RESERVATION {
|
||||
return sub, true, AcctResNotCompletedErrFromRPC(
|
||||
*errMsg.AccountReservation,
|
||||
)
|
||||
}
|
||||
|
||||
// The account doesn't exist. We are in recovery mode so
|
||||
// this is fine. We just skip this account key and try
|
||||
// the next one.
|
||||
if errMsg.ErrorCode == clmrpc.SubscribeError_ACCOUNT_DOES_NOT_EXIST {
|
||||
return sub, false, nil
|
||||
}
|
||||
|
||||
return nil, false, fmt.Errorf("unknown error message "+
|
||||
"received: %v", errMsg)
|
||||
|
||||
default:
|
||||
return nil, false, fmt.Errorf("unknown message "+
|
||||
"received: %v", msg)
|
||||
"received: %v", srvMsg)
|
||||
}
|
||||
|
||||
case <-c.quit:
|
||||
|
|
@ -530,14 +555,44 @@ func (c *Client) RecoverAccounts(ctx context.Context,
|
|||
var recoveredAccounts []*account.Account
|
||||
for _, keyDesc := range accountKeys {
|
||||
acctKeyBytes := keyDesc.PubKey.SerializeCompressed()
|
||||
var resErr *AcctResNotCompletedError
|
||||
|
||||
// Start the recovery of this account by connecting and going
|
||||
// through the 3-way-handshake. There's four possibilities of
|
||||
// what can happen: 1. The account exists but only as a
|
||||
// reservation. This special case needs to be handled
|
||||
// separately. 2. We get an unexpected error in which case we
|
||||
// abort the recovery. 3. The account doesn't exist and we skip
|
||||
// it. 4. The account exists and we get a success message.
|
||||
// Normal recovery can be attempted.
|
||||
subscription, canRecover, err := c.connectAndAuthenticate(
|
||||
ctx, keyDesc, true,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not recover account %x: %v",
|
||||
acctKeyBytes, err)
|
||||
|
||||
// Case 1: Special recovery of an account that was only
|
||||
// reserved.
|
||||
if errors.As(err, &resErr) {
|
||||
acct, err := incompleteAcctFromErr(keyDesc, resErr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not recover "+
|
||||
"reserved account %x: %v", acctKeyBytes,
|
||||
err)
|
||||
}
|
||||
recoveredAccounts = append(recoveredAccounts, acct)
|
||||
|
||||
// Don't send any further messages to the auctioneer,
|
||||
// we won't get any more information than was in the
|
||||
// error.
|
||||
continue
|
||||
}
|
||||
|
||||
// Case 2: Abort on unexpected error.
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not recover account %x: "+
|
||||
"%v", acctKeyBytes, err)
|
||||
}
|
||||
|
||||
// Case 3: The auctioneer doesn't know the account.
|
||||
if !canRecover {
|
||||
numNotFoundAccounts++
|
||||
|
||||
|
|
@ -550,10 +605,11 @@ func (c *Client) RecoverAccounts(ctx context.Context,
|
|||
continue
|
||||
}
|
||||
|
||||
// Reset our not found counter as we've found a recoverable
|
||||
// account here. Then ask the auctioneer to send us back the
|
||||
// state as it knows it in its database. The response to this
|
||||
// message will be handled outside of the client.
|
||||
// Case 4: Attempt normal recovery. First we reset our not found
|
||||
// counter as we've found a recoverable account here. Then ask
|
||||
// the auctioneer to send us back the state as it knows it in
|
||||
// its database. The response to this message will be handled
|
||||
// outside of the client.
|
||||
numNotFoundAccounts = 0
|
||||
err = c.SendAuctionMessage(&clmrpc.ClientAuctionMessage{
|
||||
Msg: &clmrpc.ClientAuctionMessage_Recover{
|
||||
|
|
@ -594,6 +650,39 @@ func (c *Client) RecoverAccounts(ctx context.Context,
|
|||
return recoveredAccounts, nil
|
||||
}
|
||||
|
||||
// incompleteAcctFromErr creates an account in the state initiated from an error
|
||||
// that contains the bare minimum of recovery information.
|
||||
func incompleteAcctFromErr(traderKey *keychain.KeyDescriptor,
|
||||
resErr *AcctResNotCompletedError) (*account.Account, error) {
|
||||
|
||||
var (
|
||||
acct = &account.Account{
|
||||
Value: resErr.Value,
|
||||
State: account.StateInitiated,
|
||||
TraderKey: traderKey,
|
||||
Expiry: resErr.Expiry,
|
||||
HeightHint: resErr.HeightHint,
|
||||
}
|
||||
err error
|
||||
)
|
||||
|
||||
acct.AuctioneerKey, err = btcec.ParsePubKey(
|
||||
resErr.AuctioneerKey[:], btcec.S256(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing auctioneer key: %v", err)
|
||||
}
|
||||
|
||||
acct.BatchKey, err = btcec.ParsePubKey(
|
||||
resErr.InitialBatchKey[:], btcec.S256(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing batch key: %v", err)
|
||||
}
|
||||
|
||||
return acct, nil
|
||||
}
|
||||
|
||||
// SendAuctionMessage sends an auction message through the long-lived stream to
|
||||
// the auction server. A message can only be sent as a response to a server
|
||||
// message, therefore the stream must already be open.
|
||||
|
|
@ -784,9 +873,10 @@ func (c *Client) readIncomingStream() { // nolint:gocyclo
|
|||
case *clmrpc.ServerAuctionMessage_Error:
|
||||
errCode := t.Error.ErrorCode
|
||||
|
||||
switch errCode {
|
||||
// The server is shutting down. No need to forward this,
|
||||
// we can just shutdown the stream and try to reconnect.
|
||||
if errCode == clmrpc.SubscribeError_SERVER_SHUTDOWN {
|
||||
case clmrpc.SubscribeError_SERVER_SHUTDOWN:
|
||||
err := c.HandleServerShutdown(nil)
|
||||
if err != nil {
|
||||
select {
|
||||
|
|
@ -795,12 +885,13 @@ func (c *Client) readIncomingStream() { // nolint:gocyclo
|
|||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// We received an account not found error. This is not
|
||||
// a reason to abort in case we are in recovery mode.
|
||||
// We let the subscription decide what to do.
|
||||
if errCode == clmrpc.SubscribeError_ACCOUNT_DOES_NOT_EXIST {
|
||||
case clmrpc.SubscribeError_ACCOUNT_DOES_NOT_EXIST,
|
||||
clmrpc.SubscribeError_INCOMPLETE_ACCOUNT_RESERVATION:
|
||||
|
||||
err := c.sendToSubscription(
|
||||
t.Error.TraderKey, msg,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue