-
Notifications
You must be signed in to change notification settings - Fork 118
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
staticaddr: deposit timeout fsm and timeout actions
- Loading branch information
Showing
2 changed files
with
282 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package staticaddr | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/btcsuite/btcd/wire" | ||
"github.com/lightninglabs/lndclient" | ||
"github.com/lightninglabs/loop/fsm" | ||
"github.com/lightninglabs/loop/staticaddr/script" | ||
) | ||
|
||
const ( | ||
defaultConfTarget = 3 | ||
) | ||
|
||
// SweepExpiredDepositAction ... | ||
func (f *FSM) SweepExpiredDepositAction( | ||
eventCtx fsm.EventContext) fsm.EventType { | ||
|
||
depoCtx, ok := eventCtx.(*DepositContext) | ||
if !ok { | ||
return f.HandleError(fsm.ErrInvalidContextType) | ||
} | ||
f.Debugf("SweepExpiredDepositAction: %v", depoCtx) | ||
|
||
msgTx := wire.NewMsgTx(2) | ||
|
||
msgTx.AddTxIn(&wire.TxIn{ | ||
PreviousOutPoint: f.deposit.OutPoint, | ||
Sequence: f.addressParameters.Expiry, | ||
SignatureScript: nil, | ||
}) | ||
|
||
// Estimate the fee rate of an expiry spend transaction. | ||
feeRateEstimator, err := f.cfg.Lnd.WalletKit.EstimateFeeRate( | ||
f.ctx, defaultConfTarget, | ||
) | ||
if err != nil { | ||
return f.HandleError(fmt.Errorf("timeout sweep fee "+ | ||
"estimation failed: %v", err)) | ||
} | ||
|
||
weight := script.ExpirySpendWeight() | ||
|
||
fee := feeRateEstimator.FeeForWeight(weight) | ||
|
||
// We cap the fee at 20% of the deposit value. | ||
if fee > f.deposit.Value/5 { | ||
return f.HandleError(errors.New("fee is higher than 20% of " + | ||
"deposit value")) | ||
} | ||
|
||
output := &wire.TxOut{ | ||
Value: int64(f.deposit.Value - fee), | ||
PkScript: f.deposit.TimeOutSweepPkScript, | ||
} | ||
msgTx.AddTxOut(output) | ||
|
||
signDesc, err := f.SignDescriptor() | ||
if err != nil { | ||
return f.HandleError(err) | ||
} | ||
|
||
txOut := &wire.TxOut{ | ||
Value: int64(f.deposit.Value), | ||
PkScript: f.addressParameters.PkScript, | ||
} | ||
|
||
prevOut := []*wire.TxOut{txOut} | ||
|
||
rawSigs, err := f.cfg.Lnd.Signer.SignOutputRaw( | ||
f.ctx, msgTx, []*lndclient.SignDescriptor{&signDesc}, prevOut, | ||
) | ||
if err != nil { | ||
return f.HandleError(err) | ||
} | ||
|
||
sig := rawSigs[0] | ||
msgTx.TxIn[0].Witness, err = f.staticAddress.GenTimeoutWitness(sig) | ||
if err != nil { | ||
return f.HandleError(err) | ||
} | ||
|
||
err = f.cfg.Lnd.WalletKit.PublishTransaction( | ||
f.ctx, msgTx, f.deposit.OutPoint.Hash.String()+"-close-sweep", | ||
) | ||
if err != nil { | ||
return f.HandleError(err) | ||
} | ||
|
||
f.Debugf("publishing timeout sweep with txid: %v", msgTx.TxHash()) | ||
|
||
return OnSwept | ||
} | ||
|
||
// FinalizeDeposit is the first action that is executed when the instant | ||
// out FSM is started. It will send the instant out request to the server. | ||
func (f *FSM) FinalizeDeposit(eventCtx fsm.EventContext) fsm.EventType { | ||
|
||
depoCtx, ok := eventCtx.(*DepositContext) | ||
if !ok { | ||
return f.HandleError(fsm.ErrInvalidContextType) | ||
} | ||
|
||
f.Debugf("FinalizeDeposit: %v", depoCtx) | ||
|
||
return fsm.NoOp | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
package staticaddr | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/btcsuite/btcd/chaincfg" | ||
"github.com/btcsuite/btcd/txscript" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/lightninglabs/lndclient" | ||
"github.com/lightninglabs/loop/fsm" | ||
"github.com/lightninglabs/loop/staticaddr/script" | ||
"github.com/lightningnetwork/lnd/input" | ||
"github.com/lightningnetwork/lnd/keychain" | ||
) | ||
|
||
// DepositContext contains the context parameters for a deposit state machine. | ||
type DepositContext struct { | ||
Deposit *Deposit | ||
} | ||
|
||
// States. | ||
var ( | ||
Deposited = fsm.StateType("Deposited") | ||
SweepExpiredDeposit = fsm.StateType("SweepExpiredDeposit") | ||
|
||
DepositFinalized = fsm.StateType("DepositFinalized") | ||
|
||
Failed = fsm.StateType("InstantFailedOutFailed") | ||
) | ||
|
||
// Events. | ||
var ( | ||
OnStart = fsm.EventType("OnStart") | ||
OnExpiry = fsm.EventType("OnExpiry") | ||
OnSwept = fsm.EventType("OnSwept") | ||
) | ||
|
||
// Config contains the services required for the instant out FSM. | ||
type Config struct { | ||
// Store is used to store the instant out. | ||
Store SqlStore | ||
|
||
Lnd lndclient.LndServices | ||
|
||
Manager *ManagerConfig | ||
|
||
// Network is the network that is used for the swap. | ||
Network *chaincfg.Params | ||
} | ||
|
||
// FSM is the state machine that handles the instant out. | ||
type FSM struct { | ||
*fsm.StateMachine | ||
|
||
addressParameters *AddressParameters | ||
|
||
staticAddress *script.StaticAddress | ||
|
||
deposit *Deposit | ||
|
||
ctx context.Context | ||
|
||
// cfg contains all the services that the reservation manager needs to | ||
// operate. | ||
cfg *ManagerConfig | ||
} | ||
|
||
// NewFSM creates a new state machine that can action on all static address | ||
// feature requests. | ||
func NewFSM(ctx context.Context, addressParameters *AddressParameters, | ||
staticAddress *script.StaticAddress, deposit *Deposit, | ||
cfg *ManagerConfig) (*FSM, error) { | ||
|
||
depoFsm := &FSM{ | ||
ctx: ctx, | ||
addressParameters: addressParameters, | ||
staticAddress: staticAddress, | ||
deposit: deposit, | ||
cfg: cfg, | ||
} | ||
|
||
depoFsm.StateMachine = fsm.NewStateMachine(depoFsm.DepositStates(), 20) | ||
depoFsm.ActionEntryFunc = depoFsm.DepositEntryFunction | ||
|
||
return depoFsm, nil | ||
} | ||
|
||
// DepositStates returns the states a deposit can be in. | ||
func (f *FSM) DepositStates() fsm.States { | ||
return fsm.States{ | ||
fsm.EmptyState: fsm.State{ | ||
Transitions: fsm.Transitions{ | ||
OnStart: Deposited, | ||
}, | ||
Action: fsm.NoOpAction, | ||
}, | ||
Deposited: fsm.State{ | ||
Transitions: fsm.Transitions{ | ||
OnExpiry: SweepExpiredDeposit, | ||
fsm.OnError: Failed, | ||
}, | ||
Action: fsm.NoOpAction, | ||
}, | ||
SweepExpiredDeposit: fsm.State{ | ||
Transitions: fsm.Transitions{ | ||
OnSwept: DepositFinalized, | ||
fsm.OnError: Failed, | ||
}, | ||
Action: f.SweepExpiredDepositAction, | ||
}, | ||
Failed: fsm.State{ | ||
Action: fsm.NoOpAction, | ||
}, | ||
DepositFinalized: fsm.State{ | ||
Action: f.FinalizeDeposit, | ||
}, | ||
} | ||
} | ||
|
||
// DepositEntryFunction is called after every action and updates the reservation | ||
// in the db. | ||
func (f *FSM) DepositEntryFunction(notification fsm.Notification) { | ||
log.Debugf("DepositEntryFunction: %v", notification) | ||
} | ||
|
||
// Infof logs an info message with the deposit outpoint. | ||
func (f *FSM) Infof(format string, args ...interface{}) { | ||
log.Infof( | ||
"Deposit %v: "+format, | ||
append( | ||
[]interface{}{f.deposit.OutPoint}, | ||
args..., | ||
)..., | ||
) | ||
} | ||
|
||
// Debugf logs a debug message with the deposit outpoint. | ||
func (f *FSM) Debugf(format string, args ...interface{}) { | ||
log.Debugf( | ||
"Deposit %v: "+format, | ||
append( | ||
[]interface{}{f.deposit.OutPoint}, | ||
args..., | ||
)..., | ||
) | ||
} | ||
|
||
// Errorf logs an error message with the deposit outpoint. | ||
func (f *FSM) Errorf(format string, args ...interface{}) { | ||
log.Errorf( | ||
"Deposit %v: "+format, | ||
append( | ||
[]interface{}{f.deposit.OutPoint}, | ||
args..., | ||
)..., | ||
) | ||
} | ||
|
||
// SignDescriptor returns the sign descriptor for the static address output. | ||
func (f *FSM) SignDescriptor() (lndclient.SignDescriptor, error) { | ||
return lndclient.SignDescriptor{ | ||
WitnessScript: f.staticAddress.TimeoutLeaf.Script, | ||
KeyDesc: keychain.KeyDescriptor{ | ||
PubKey: f.addressParameters.ClientPubkey, | ||
}, | ||
Output: wire.NewTxOut( | ||
int64(f.deposit.Value), f.addressParameters.PkScript, | ||
), | ||
HashType: txscript.SigHashDefault, | ||
InputIndex: 0, | ||
SignMethod: input.TaprootScriptSpendSignMethod, | ||
}, nil | ||
} |