Skip to content

Commit

Permalink
Move create transaction to loop to prevent concurrent access
Browse files Browse the repository at this point in the history
  • Loading branch information
KonradStaniec committed Oct 16, 2024
1 parent b34531d commit 089e6a3
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 62 deletions.
51 changes: 27 additions & 24 deletions staker/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)

// we can make command to implement StakingEvent interface
var _ StakingEvent = (*stakingRequestCmd)(nil)

type stakingRequestCmd struct {
stakerAddress btcutil.Address
stakingTxHash chainhash.Hash
stakingTx *wire.MsgTx
stakingOutputIdx uint32
stakingOutputPkScript []byte
stakingOutput *wire.TxOut
feeRate chainfee.SatPerKVByte
stakingTime uint16
stakingValue btcutil.Amount
fpBtcPks []*btcec.PublicKey
Expand All @@ -36,9 +35,8 @@ func (req *stakingRequestCmd) isWatched() bool {

func newOwnedStakingCommand(
stakerAddress btcutil.Address,
stakingTx *wire.MsgTx,
stakingOutputIdx uint32,
stakingOutputPkScript []byte,
stakingOutput *wire.TxOut,
feeRate chainfee.SatPerKVByte,
stakingTime uint16,
stakingValue btcutil.Amount,
fpBtcPks []*btcec.PublicKey,
Expand All @@ -48,10 +46,8 @@ func newOwnedStakingCommand(
) *stakingRequestCmd {
return &stakingRequestCmd{
stakerAddress: stakerAddress,
stakingTxHash: stakingTx.TxHash(),
stakingTx: stakingTx,
stakingOutputIdx: stakingOutputIdx,
stakingOutputPkScript: stakingOutputPkScript,
stakingOutput: stakingOutput,
feeRate: feeRate,
stakingTime: stakingTime,
stakingValue: stakingValue,
fpBtcPks: fpBtcPks,
Expand All @@ -65,6 +61,12 @@ func newOwnedStakingCommand(
}

type watchTxDataCmd struct {
// watched tx data
stakingTxHash chainhash.Hash
stakingTx *wire.MsgTx
stakingOutputIdx uint32
stakingOutputPkScript []byte

slashingTx *wire.MsgTx
slashingTxSig *schnorr.Signature
stakerBabylonAddr sdk.AccAddress
Expand Down Expand Up @@ -97,32 +99,33 @@ func newWatchedStakingCmd(
) *stakingRequestCmd {
return &stakingRequestCmd{
stakerAddress: stakerAddress,
stakingTxHash: stakingTx.TxHash(),
stakingTx: stakingTx,
stakingOutputIdx: stakingOutputIdx,
stakingOutputPkScript: stakingOutputPkScript,
stakingTime: stakingTime,
stakingValue: stakingValue,
fpBtcPks: fpBtcPks,
requiredDepthOnBtcChain: confirmationTimeBlocks,
pop: pop,
watchTxData: &watchTxDataCmd{
slashingTx: slashingTx,
slashingTxSig: slashingTxSignature,
stakerBabylonAddr: stakerBabylonAddr,
stakerBtcPk: stakerBtcPk,
unbondingTx: unbondingTx,
slashUnbondingTx: slashUnbondingTx,
slashUnbondingTxSig: slashUnbondingTxSig,
unbondingTime: unbondingTime,
stakingTxHash: stakingTx.TxHash(),
stakingTx: stakingTx,
stakingOutputIdx: stakingOutputIdx,
stakingOutputPkScript: stakingOutputPkScript,
slashingTx: slashingTx,
slashingTxSig: slashingTxSignature,
stakerBabylonAddr: stakerBabylonAddr,
stakerBtcPk: stakerBtcPk,
unbondingTx: unbondingTx,
slashUnbondingTx: slashUnbondingTx,
slashUnbondingTxSig: slashUnbondingTxSig,
unbondingTime: unbondingTime,
},
errChan: make(chan error, 1),
successChan: make(chan *chainhash.Hash, 1),
}
}

func (event *stakingRequestCmd) EventId() chainhash.Hash {
return event.stakingTxHash
// we do not have has for this event
return chainhash.Hash{}
}

func (event *stakingRequestCmd) EventDesc() string {
Expand Down
99 changes: 61 additions & 38 deletions staker/stakerapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -1261,23 +1261,29 @@ func (app *StakerApp) sendDelegationToBabylonTask(
}
}

func (app *StakerApp) handlePreApprovalCmd(cmd *stakingRequestCmd) error {
func (app *StakerApp) handlePreApprovalCmd(
cmd *stakingRequestCmd,
stakingTx *wire.MsgTx,
stakingOutputIdx uint32,
) (*chainhash.Hash, error) {
// just to pass to buildAndSendDelegation
fakeStoredTx, err := stakerdb.CreateTrackedTransaction(
cmd.stakingTx,
cmd.stakingOutputIdx,
stakingTx,
stakingOutputIdx,
cmd.stakingTime,
cmd.fpBtcPks,
babylonPopToDbPop(cmd.pop),
cmd.stakerAddress,
)

if err != nil {
return err
return nil, err
}

stakingTxHash := stakingTx.TxHash()

req := &sendDelegationRequest{
txHash: cmd.stakingTxHash,
txHash: stakingTxHash,
inclusionInfo: nil,
requiredInclusionBlockDepth: cmd.requiredDepthOnBtcChain,
}
Expand All @@ -1289,12 +1295,12 @@ func (app *StakerApp) handlePreApprovalCmd(cmd *stakingRequestCmd) error {
)

if err != nil {
return err
return nil, err
}

err = app.txTracker.AddTransactionSentToBabylon(
cmd.stakingTx,
cmd.stakingOutputIdx,
stakingTx,
stakingOutputIdx,
cmd.stakingTime,
cmd.fpBtcPks,
babylonPopToDbPop(cmd.pop),
Expand All @@ -1304,70 +1310,88 @@ func (app *StakerApp) handlePreApprovalCmd(cmd *stakingRequestCmd) error {
)

if err != nil {
return err
return nil, err
}

app.wg.Add(1)
go app.checkForUnbondingTxSignaturesOnBabylon(&cmd.stakingTxHash)
go app.checkForUnbondingTxSignaturesOnBabylon(&stakingTxHash)

return nil
return &stakingTxHash, nil
}

func (app *StakerApp) handlePostApprovalCmd(cmd *stakingRequestCmd) error {
func (app *StakerApp) handlePostApprovalCmd(
cmd *stakingRequestCmd,
stakingTx *wire.MsgTx,
stakingOutputIdx uint32,
) (*chainhash.Hash, error) {
stakingTxHash := stakingTx.TxHash()

bestBlockHeight := app.currentBestBlockHeight.Load()

err := app.wc.UnlockWallet(defaultWalletUnlockTimeout)

if err != nil {
return err
return nil, err
}

tx, fullySignd, err := app.wc.SignRawTransaction(cmd.stakingTx)
tx, fullySignd, err := app.wc.SignRawTransaction(stakingTx)

if err != nil {
return err
return nil, err
}

if !fullySignd {
return fmt.Errorf("failed to fully sign transaction with hash %s", cmd.stakingTxHash)
return nil, fmt.Errorf("failed to fully sign transaction with hash %s", stakingTxHash)
}

_, err = app.wc.SendRawTransaction(tx, true)

if err != nil {
return err
return nil, err
}

stakingOutputPkScript := cmd.stakingTx.TxOut[cmd.stakingOutputIdx].PkScript
stakingOutputPkScript := stakingTx.TxOut[stakingOutputIdx].PkScript

if err := app.waitForStakingTransactionConfirmation(
&cmd.stakingTxHash,
&stakingTxHash,
stakingOutputPkScript,
cmd.requiredDepthOnBtcChain,
uint32(bestBlockHeight),
); err != nil {
return err
return nil, err
}

if err := app.txTracker.AddTransactionSentToBTC(
cmd.stakingTx,
cmd.stakingOutputIdx,
stakingTx,
stakingOutputIdx,
cmd.stakingTime,
cmd.fpBtcPks,
babylonPopToDbPop(cmd.pop),
cmd.stakerAddress,
); err != nil {
return err
return nil, err
}

return nil
return &stakingTxHash, nil
}

func (app *StakerApp) handleStakingCmd(cmd *stakingRequestCmd) error {
func (app *StakerApp) handleStakingCmd(cmd *stakingRequestCmd) (*chainhash.Hash, error) {
// Create unsigned transaction by wallet without signing. Signing will happen
// in next steps
stakingTx, err := app.wc.CreateTransaction(
[]*wire.TxOut{cmd.stakingOutput},
btcutil.Amount(cmd.feeRate),
cmd.stakerAddress,
app.filteUtxoFnGen(),
)
if err != nil {
return nil, fmt.Errorf("failed to build staking transaction: %w", err)
}

if cmd.usePreApprovalFlow {
return app.handlePreApprovalCmd(cmd)
return app.handlePreApprovalCmd(cmd, stakingTx, 0)
} else {
return app.handlePostApprovalCmd(cmd)
return app.handlePostApprovalCmd(cmd, stakingTx, 0)
}
}

Expand All @@ -1383,8 +1407,8 @@ func (app *StakerApp) handleStakingCommands() {
bestBlockHeight := app.currentBestBlockHeight.Load()

err := app.txTracker.AddWatchedTransaction(
cmd.stakingTx,
cmd.stakingOutputIdx,
cmd.watchTxData.stakingTx,
cmd.watchTxData.stakingOutputIdx,
cmd.stakingTime,
cmd.fpBtcPks,
babylonPopToDbPop(cmd.pop),
Expand All @@ -1406,8 +1430,8 @@ func (app *StakerApp) handleStakingCommands() {

// we assume tx is already on btc chain, so we need to wait for confirmation
if err := app.waitForStakingTransactionConfirmation(
&cmd.stakingTxHash,
cmd.stakingTx.TxOut[cmd.stakingOutputIdx].PkScript,
&cmd.watchTxData.stakingTxHash,
cmd.watchTxData.stakingTx.TxOut[cmd.watchTxData.stakingOutputIdx].PkScript,
cmd.requiredDepthOnBtcChain,
uint32(bestBlockHeight),
); err != nil {
Expand All @@ -1416,12 +1440,12 @@ func (app *StakerApp) handleStakingCommands() {
}

app.m.ValidReceivedDelegationRequests.Inc()
cmd.successChan <- &cmd.stakingTxHash
cmd.successChan <- &cmd.watchTxData.stakingTxHash
app.logStakingEventProcessed(cmd)
continue
}

err := app.handleStakingCmd(cmd)
stakingTxHash, err := app.handleStakingCmd(cmd)

if err != nil {
utils.PushOrQuit(
Expand All @@ -1432,7 +1456,7 @@ func (app *StakerApp) handleStakingCommands() {
} else {
utils.PushOrQuit(
cmd.successChan,
&cmd.stakingTxHash,
stakingTxHash,
app.quit,
)
}
Expand Down Expand Up @@ -1680,7 +1704,7 @@ func (app *StakerApp) WatchStaking(

app.logger.WithFields(logrus.Fields{
"stakerAddress": stakerAddress,
"stakingAmount": watchedRequest.stakingTx.TxOut[watchedRequest.stakingOutputIdx].Value,
"stakingAmount": watchedRequest.watchTxData.stakingTx.TxOut[watchedRequest.watchTxData.stakingOutputIdx].Value,
"btxTxHash": stakingTx.TxHash(),
}).Info("Received valid staking tx to watch")

Expand Down Expand Up @@ -1843,9 +1867,8 @@ func (app *StakerApp) StakeFunds(

req := newOwnedStakingCommand(
stakerAddress,
tx,
0,
stakingInfo.StakingOutput.PkScript,
stakingInfo.StakingOutput,
feeRate,
stakingTimeBlocks,
stakingAmount,
fpPks,
Expand Down

0 comments on commit 089e6a3

Please sign in to comment.