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

feature: do not use inputs already on babyon #67

Merged
merged 7 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* [#65](https://github.com/babylonlabs-io/btc-staker/pull/65) Various fixes to
pre-approval flow. Do not send signed staking transactions to Babylon.

* [#67](https://github.com/babylonlabs-io/btc-staker/pull/67) Enable concurrent
sending of multiple pre-approval staking transactions

## v0.7.2

### Bug fix
Expand Down
84 changes: 75 additions & 9 deletions itest/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ func (tm *TestManager) FinalizeUntilEpoch(t *testing.T, epoch uint64) {
},
2000,
tm.MinerAddr,
nil,
)
require.NoError(t, err)
_, err = tm.Sa.Wallet().SendRawTransaction(tx1, true)
Expand All @@ -557,6 +558,7 @@ func (tm *TestManager) FinalizeUntilEpoch(t *testing.T, epoch uint64) {
},
2000,
tm.MinerAddr,
nil,
)
require.NoError(t, err)
_, err = tm.Sa.Wallet().SendRawTransaction(tx2, true)
Expand Down Expand Up @@ -740,7 +742,7 @@ func (tm *TestManager) sendStakingTxBTC(
return hashFromString
}

func (tm *TestManager) sendMultipleStakingTx(t *testing.T, testStakingData []*testStakingData) []*chainhash.Hash {
func (tm *TestManager) sendMultipleStakingTx(t *testing.T, testStakingData []*testStakingData, sendToBabylonFirst bool) []*chainhash.Hash {
var hashes []*chainhash.Hash
for _, data := range testStakingData {
fpBTCPKs := []string{}
Expand All @@ -754,7 +756,7 @@ func (tm *TestManager) sendMultipleStakingTx(t *testing.T, testStakingData []*te
data.StakingAmount,
fpBTCPKs,
int64(data.StakingTime),
false,
sendToBabylonFirst,
)
require.NoError(t, err)
txHash, err := chainhash.NewHashFromStr(res.TxHash)
Expand All @@ -768,14 +770,21 @@ func (tm *TestManager) sendMultipleStakingTx(t *testing.T, testStakingData []*te
stakingDetails, err := tm.StakerClient.StakingDetails(context.Background(), hashStr)
require.NoError(t, err)
require.Equal(t, stakingDetails.StakingTxHash, hashStr)
require.Equal(t, stakingDetails.StakingState, proto.TransactionState_SENT_TO_BTC.String())

if sendToBabylonFirst {
require.Equal(t, stakingDetails.StakingState, proto.TransactionState_SENT_TO_BABYLON.String())
} else {
require.Equal(t, stakingDetails.StakingState, proto.TransactionState_SENT_TO_BTC.String())
}
}

mBlock := tm.mineBlock(t)
require.Equal(t, len(hashes)+1, len(mBlock.Transactions))
if !sendToBabylonFirst {
mBlock := tm.mineBlock(t)
require.Equal(t, len(hashes)+1, len(mBlock.Transactions))

_, err := tm.BabylonClient.InsertBtcBlockHeaders([]*wire.BlockHeader{&mBlock.Header})
require.NoError(t, err)
_, err := tm.BabylonClient.InsertBtcBlockHeaders([]*wire.BlockHeader{&mBlock.Header})
require.NoError(t, err)
}
return hashes
}

Expand Down Expand Up @@ -804,6 +813,7 @@ func (tm *TestManager) sendWatchedStakingTx(
[]*wire.TxOut{stakingInfo.StakingOutput},
2000,
tm.MinerAddr,
nil,
)
require.NoError(t, err)
txHash := tx.TxHash()
Expand Down Expand Up @@ -1346,7 +1356,7 @@ func TestMultipleWithdrawableStakingTransactions(t *testing.T) {
testStakingData3,
testStakingData4,
testStakingData5,
})
}, false)

go tm.mineNEmptyBlocks(t, params.ConfirmationTimeBlocks, true)

Expand Down Expand Up @@ -1380,6 +1390,61 @@ func TestMultipleWithdrawableStakingTransactions(t *testing.T) {
require.Equal(t, withdrawableTransactionsResp.Transactions[2].TransactionIdx, "4")
}

func TestMultiplePreApprovalTransactions(t *testing.T) {
t.Parallel()
// need to have at least 300 block on testnet as only then segwit is activated.
// Mature output is out which has 100 confirmations, which means 200mature outputs
// will generate 300 blocks
numMatureOutputs := uint32(200)
ctx, cancel := context.WithCancel(context.Background())
tm := StartManager(t, ctx, numMatureOutputs)
defer tm.Stop(t, cancel)
tm.insertAllMinedBlocksToBabylon(t)

cl := tm.Sa.BabylonController()
params, err := cl.Params()
require.NoError(t, err)
minStakingTime := params.MinStakingTime
stakingTime1 := minStakingTime
stakingTime2 := minStakingTime + 4
stakingTime3 := minStakingTime + 1

testStakingData1 := tm.getTestStakingData(t, tm.WalletPubKey, stakingTime1, 10000, 1)
testStakingData2 := testStakingData1.withStakingTime(stakingTime2)
testStakingData3 := testStakingData1.withStakingTime(stakingTime3)

tm.createAndRegisterFinalityProviders(t, testStakingData1)
txHashes := tm.sendMultipleStakingTx(t, []*testStakingData{
testStakingData1,
testStakingData2,
testStakingData3,
}, true)

for _, txHash := range txHashes {
txHash := txHash
tm.waitForStakingTxState(t, txHash, proto.TransactionState_SENT_TO_BABYLON)
}

pend, err := tm.BabylonClient.QueryPendingBTCDelegations()
require.NoError(t, err)
require.Len(t, pend, 3)
tm.insertCovenantSigForDelegation(t, pend[0])
tm.insertCovenantSigForDelegation(t, pend[1])
tm.insertCovenantSigForDelegation(t, pend[2])

for _, txHash := range txHashes {
txHash := txHash
tm.waitForStakingTxState(t, txHash, proto.TransactionState_VERIFIED)
}

// Ultimately we will get 3 tx in the mempool meaning all staking transactions
// use valid inputs
require.Eventually(t, func() bool {
txFromMempool := retrieveTransactionFromMempool(t, tm.TestRpcClient, txHashes)
return len(txFromMempool) == 3
}, eventuallyWaitTimeOut, eventuallyPollTime)
}

func TestSendingWatchedStakingTransaction(t *testing.T) {
t.Parallel()
// need to have at least 300 block on testnet as only then segwit is activated.
Expand Down Expand Up @@ -1456,7 +1521,7 @@ func TestRestartingTxNotOnBabylon(t *testing.T) {
txHashes := tm.sendMultipleStakingTx(t, []*testStakingData{
testStakingData1,
testStakingData2,
})
}, false)

// Confirm tx on btc
minedBlocks := tm.mineNEmptyBlocks(t, params.ConfirmationTimeBlocks, false)
Expand Down Expand Up @@ -1677,6 +1742,7 @@ func TestBitcoindWalletRpcApi(t *testing.T) {
[]*wire.TxOut{newOutput},
btcutil.Amount(2000),
walletAddress,
nil,
)
require.NoError(t, err)

Expand Down
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
Loading
Loading