diff --git a/e2etest/submitter_e2e_test.go b/e2etest/submitter_e2e_test.go index 55284a8f..0164d042 100644 --- a/e2etest/submitter_e2e_test.go +++ b/e2etest/submitter_e2e_test.go @@ -125,7 +125,7 @@ func TestSubmitterSubmissionReplace(t *testing.T) { tm.Config.Submitter.PollingIntervalSeconds = 2 tm.Config.Submitter.ResendIntervalSeconds = 2 - tm.Config.Submitter.ResubmitFeeMultiplier = 2.1 + tm.Config.Submitter.ResubmitFeeMultiplier = 2.6 // create submitter vigilantSubmitter, _ := submitter.New( &tm.Config.Submitter, diff --git a/submitter/relayer/relayer.go b/submitter/relayer/relayer.go index 8f72bde4..12c3d63a 100644 --- a/submitter/relayer/relayer.go +++ b/submitter/relayer/relayer.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "github.com/btcsuite/btcd/btcjson" + "math" "strconv" "time" @@ -160,7 +161,7 @@ func (rl *Relayer) calculateBumpedFee(ckptInfo *types.CheckpointInfo) btcutil.Am // minus the old fee of the first transaction because we do not want to pay again for the first transaction bumpedFee := newTx1Fee + newTx2Fee - ckptInfo.Tx1.Fee - return bumpedFee.MulF64(float64(rl.config.ResubmitFeeMultiplier)) + return bumpedFee.MulF64(rl.config.ResubmitFeeMultiplier) } // resendSecondTxOfCheckpointToBTC resends the second tx of the checkpoint with bumpedFee @@ -168,7 +169,9 @@ func (rl *Relayer) resendSecondTxOfCheckpointToBTC(tx2 *types.BtcTxInfo, bumpedF // set output value of the second tx to be the balance minus the bumped fee // if the bumped fee is higher than the balance, then set the bumped fee to // be equal to the balance to ensure the output value is not negative - balance := tx2.Utxo.Amount + //balance := tx2.Utxo.Amount + balance := btcutil.Amount(tx2.Tx.TxOut[1].Value) // todo Lazar check this as well + if bumpedFee > balance { rl.logger.Debugf("the bumped fee %v Satoshis for the second tx is more than UTXO amount %v Satoshis", bumpedFee, balance) @@ -183,14 +186,14 @@ func (rl *Relayer) resendSecondTxOfCheckpointToBTC(tx2 *types.BtcTxInfo, bumpedF return nil, err } - txid, err := rl.sendTxToBTC(tx) + txID, err := rl.sendTxToBTC(tx) if err != nil { return nil, err } // update tx info tx2.Fee = bumpedFee - tx2.TxId = txid + tx2.TxId = txID return tx2, nil } @@ -250,18 +253,7 @@ func (rl *Relayer) convertCkptToTwoTxAndSubmit(ckpt *ckpttypes.RawCheckpointResp return nil, err } - utxo, err := rl.PickHighUTXO() - if err != nil { - return nil, err - } - - rl.logger.Debugf("Found one unspent tx with sufficient amount: %v", utxo.TxID) - - tx1, tx2, err := rl.ChainTwoTxAndSend( - utxo, - data1, - data2, - ) + tx1, tx2, err := rl.ChainTwoTxAndSend(data1, data2) if err != nil { return nil, err } @@ -297,17 +289,10 @@ func (rl *Relayer) convertCkptToTwoTxAndSubmit(ckpt *ckpttypes.RawCheckpointResp // ChainTwoTxAndSend consumes one utxo and build two chaining txs: // the second tx consumes the output of the first tx -func (rl *Relayer) ChainTwoTxAndSend( - utxo *types.UTXO, - data1 []byte, - data2 []byte, -) (*types.BtcTxInfo, *types.BtcTxInfo, error) { +func (rl *Relayer) ChainTwoTxAndSend(data1 []byte, data2 []byte) (*types.BtcTxInfo, *types.BtcTxInfo, error) { // recipient is a change address that all the // remaining balance of the utxo is sent to - tx1, err := rl.buildTxWithData( - utxo, - data1, - ) + tx1, err := rl.buildTxWithData(data1, nil) if err != nil { return nil, nil, fmt.Errorf("failed to add data to tx1: %w", err) } @@ -317,20 +302,9 @@ func (rl *Relayer) ChainTwoTxAndSend( return nil, nil, fmt.Errorf("failed to send tx1 to BTC: %w", err) } - changeUtxo := &types.UTXO{ - TxID: tx1.TxId, - Vout: 1, - ScriptPK: tx1.Tx.TxOut[1].PkScript, - Amount: btcutil.Amount(tx1.Tx.TxOut[1].Value), - Addr: tx1.ChangeAddress, - } - // the second tx consumes the second output (index 1) // of the first tx, as the output at index 0 is OP_RETURN - tx2, err := rl.buildTxWithData( - changeUtxo, - data2, - ) + tx2, err := rl.buildTxWithData(data2, tx1.Tx) if err != nil { return nil, nil, fmt.Errorf("failed to add data to tx2: %w", err) } @@ -345,36 +319,23 @@ func (rl *Relayer) ChainTwoTxAndSend( return tx1, tx2, nil } -// PickHighUTXO picks a UTXO that has the highest amount -func (rl *Relayer) PickHighUTXO() (*types.UTXO, error) { - // get the highest UTXO and UTXOs' sum in the list - topUTXO, sum, err := rl.BTCWallet.GetHighUTXOAndSum() - if err != nil { - return nil, err - } - utxo, err := types.NewUTXO(topUTXO, rl.GetNetParams()) - if err != nil { - return nil, fmt.Errorf("failed to convert ListUnspentResult to UTXO: %w", err) - } - rl.logger.Debugf("pick utxo with id: %v, amount: %v, confirmations: %v", utxo.TxID, utxo.Amount, topUTXO.Confirmations) - - // record metrics of UTXOs' sum - rl.metrics.AvailableBTCBalance.Set(sum) - - return utxo, nil -} - // buildTxWithData builds a tx with data inserted as OP_RETURN // note that OP_RETURN is set as the first output of the tx (index 0) // and the rest of the balance is sent to a new change address // as the second output with index 1 -func (rl *Relayer) buildTxWithData( - utxo *types.UTXO, - data []byte, -) (*types.BtcTxInfo, error) { - rl.logger.Debugf("Building a BTC tx using %v with data %x", utxo.TxID.String(), data) +func (rl *Relayer) buildTxWithData(data []byte, firstTx *wire.MsgTx) (*types.BtcTxInfo, error) { tx := wire.NewMsgTx(wire.TxVersion) + if firstTx != nil { + txID := firstTx.TxHash() + outPoint := wire.NewOutPoint(&txID, 1) + txIn := wire.NewTxIn(outPoint, nil, nil) + // Enable replace-by-fee + // See https://river.com/learn/terms/r/replace-by-fee-rbf + txIn.Sequence = math.MaxUint32 - 2 + tx.AddTxIn(txIn) + } + // build txOut for data builder := txscript.NewScriptBuilder() dataScript, err := builder.AddOp(txscript.OP_RETURN).AddData(data).Script() @@ -393,6 +354,8 @@ func (rl *Relayer) buildTxWithData( return nil, err } + rl.logger.Debugf("Building a BTC tx using %s with data %x", rawTxResult.Transaction.TxID, data) + _, addresses, _, err := txscript.ExtractPkScriptAddrs( rawTxResult.Transaction.TxOut[1].PkScript, rl.GetNetParams(), @@ -414,21 +377,24 @@ func (rl *Relayer) buildTxWithData( return nil, err } + changeAmount := btcutil.Amount(rawTxResult.Transaction.TxOut[1].Value) minRelayFee := rl.calcMinRelayFee(txSize) - if utxo.Amount < minRelayFee { - return nil, fmt.Errorf("the value of the utxo is not sufficient for relaying the tx. Require: %v. Have: %v", minRelayFee, utxo.Amount) + + if changeAmount < minRelayFee { + return nil, fmt.Errorf("the value of the utxo is not sufficient for relaying the tx. Require: %v. Have: %v", minRelayFee, changeAmount) } - txFee := rl.getFeeRate().FeeForVSize(txSize) + //txFee := rl.getFeeRate().FeeForVSize(txSize) + txFee := rawTxResult.Fee // todo Lazar, check with Konrad if we should use the calcd fee from FundRawTransaction or manually calc it // ensuring the tx fee is not lower than the minimum relay fee if txFee < minRelayFee { txFee = minRelayFee } // ensuring the tx fee is not higher than the utxo value - if utxo.Amount < txFee { - return nil, fmt.Errorf("the value of the utxo is not sufficient for paying the calculated fee of the tx. Calculated: %v. Have: %v", txFee, utxo.Amount) + if changeAmount < txFee { + return nil, fmt.Errorf("the value of the utxo is not sufficient for paying the calculated fee of the tx. Calculated: %v. Have: %v", txFee, changeAmount) } - change := utxo.Amount - txFee + change := changeAmount - txFee // sign tx tx, err = rl.signTx(rawTxResult.Transaction) @@ -444,11 +410,10 @@ func (rl *Relayer) buildTxWithData( rl.logger.Debugf("Successfully composed a BTC tx with balance of input: %v, "+ "tx fee: %v, output value: %v, tx size: %v, hex: %v", - utxo.Amount, txFee, change, txSize, hex.EncodeToString(signedTxBytes.Bytes())) + changeAmount, txFee, change, txSize, hex.EncodeToString(signedTxBytes.Bytes())) return &types.BtcTxInfo{ Tx: tx, - Utxo: utxo, ChangeAddress: changeAddr, Size: txSize, Fee: txFee, diff --git a/types/ckpt_info.go b/types/ckpt_info.go index 4c9b93fc..5d64c6b0 100644 --- a/types/ckpt_info.go +++ b/types/ckpt_info.go @@ -21,7 +21,6 @@ type BtcTxInfo struct { TxId *chainhash.Hash Tx *wire.MsgTx ChangeAddress btcutil.Address - Utxo *UTXO // the UTXO used to build this BTC tx Size int64 // the size of the BTC tx Fee btcutil.Amount // tx fee cost by the BTC tx }