Skip to content

Commit

Permalink
tx chaining
Browse files Browse the repository at this point in the history
  • Loading branch information
Lazar955 committed Aug 19, 2024
1 parent a0d6c40 commit 30a7716
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 71 deletions.
2 changes: 1 addition & 1 deletion e2etest/submitter_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
103 changes: 34 additions & 69 deletions submitter/relayer/relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"github.com/btcsuite/btcd/btcjson"
"math"
"strconv"
"time"

Expand Down Expand Up @@ -160,15 +161,17 @@ 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
func (rl *Relayer) resendSecondTxOfCheckpointToBTC(tx2 *types.BtcTxInfo, bumpedFee btcutil.Amount) (*types.BtcTxInfo, error) {
// 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)
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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()
Expand All @@ -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(),
Expand All @@ -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)
Expand All @@ -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,
Expand Down
1 change: 0 additions & 1 deletion types/ckpt_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit 30a7716

Please sign in to comment.