Skip to content

lnwallet/btcwallet: use CheckTransactionStandard to validate txns before broadcast#6583

Closed
Roasbeef wants to merge 4 commits into
lightningnetwork:masterfrom
Roasbeef:tx-policy-checks
Closed

lnwallet/btcwallet: use CheckTransactionStandard to validate txns before broadcast#6583
Roasbeef wants to merge 4 commits into
lightningnetwork:masterfrom
Roasbeef:tx-policy-checks

Conversation

@Roasbeef
Copy link
Copy Markdown
Member

In this commit, we start to use the CheckTransactionStandard function
from the mempool package to validate transactions further before
broadcast. This'll help catch instances where we make a massive
transaction, and therefor can't actually propagate it across the
network.

Fixes #4760
Fixes #6556

@Roasbeef Roasbeef added this to the v0.15.0 milestone May 26, 2022
@Roasbeef
Copy link
Copy Markdown
Member Author

Thanks to @ellemouton for making the change to the btcd's mempool package to export the func used here.

Roasbeef added 4 commits May 26, 2022 15:55
This also pulls in a bug fix in the rpcclient as well.
…ore broadcast

In this commit, we start to use the `CheckTransactionStandard` function
from the `mempool` package to validate transactions further before
broadcast. This'll help catch instances where we make a massive
transaction, and therefor can't actually propagate it across the
network.

Fixes lightningnetwork#4760
Fixes lightningnetwork#6556
@Roasbeef Roasbeef force-pushed the tx-policy-checks branch from 57bdb86 to 81b132f Compare May 26, 2022 22:56
@Roasbeef
Copy link
Copy Markdown
Member Author

This is a very simple approach that just ensures we won't broadcast these transactions. Ideally we should add this check before this step even, in areas like the funding manager so we can just bail out if we know coin selection is weird or w/e. This is covered in the more encompassing PR here: #6400

@Roasbeef Roasbeef requested a review from yyforyongyu May 26, 2022 22:57
Copy link
Copy Markdown
Member

@yyforyongyu yyforyongyu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up! My main question is since the wallet.PublishTransaction already handles removing the failed tx from TxStore, what's the effect of adding CheckTransactionStandard from the caller's view?
https://github.com/btcsuite/btcwallet/blob/af5562928b707e0b56c1e44052ece3205bb66552/wallet/wallet.go#L3743-L3754

By adding CheckTransactionStandard here, it does save us from the process of first writing the tx to TxStore and later removing it. re #4760, it seems the dangling tx would still be there?

Ideally we should add this check before this step even, in areas like the funding manager so we can just bail out if we know coin selection is weird or w/e.

I think we need to do this instead. From caller's view the behavior of the new btcwallet.PublishTransaction is not changed.

close.](https://github.com/lightningnetwork/lnd/pull/6518)

## Neutrino
## Bug Fixes
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could remove this title as well.

}
maxTxVersion := int32(2)
err = mempool.CheckTransactionStandard(
btcutil.NewTx(tx), bestHeight, bestHeader.Timestamp,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to do bestHeight + 1 here?


txid4 := tx4.TxHash()
err = waitForMempoolTx(r, &txid4)
t.Run("no_error_on_minged_tx", func(t *testing.T) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: minged -> mined

// We will first check that publishing a transaction already in the
// mempool does NOT return an error. Create the tx.
tx1 := newTx(t, r, keyDesc.PubKey, alice, false)
t.Run("no_error_on_duplicate_tx", func(t *testing.T) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

if err != nil {
return err
}
maxTxVersion := int32(2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could make it a public constant and put it in the header?

@Roasbeef
Copy link
Copy Markdown
Member Author

lready handles removing the failed tx from TxStore, what's the effect of adding CheckTransactionStandard from the caller's view?

That's a good question: AFAICT, something is happening where even though we remove the transaction there, it is kept around somewhere and been rebroadcasted again. See this issue as an example: #6556.

@Roasbeef Roasbeef changed the title lnwallet/btcwallet: use CheckTransactionStandard to validate txns beore broadcast lnwallet/btcwallet: use CheckTransactionStandard to validate txns before broadcast May 31, 2022
@Roasbeef
Copy link
Copy Markdown
Member Author

Roasbeef commented Jun 1, 2022

I think we need to do this instead. From caller's view the behavior of the new btcwallet.PublishTransaction is not changed.

Thought about this a bit more, and it's potentially a bit more error prone: any new PublishTransaction call sites need to be aware of this required check, vs just being able to call PublishTransaction and know you'll get an error if something is up (which is effectively the same behavior as we have w/ and w/o this PR).

@Roasbeef
Copy link
Copy Markdown
Member Author

Roasbeef commented Jun 1, 2022

Here's a crude diff I used to verify the question above:

diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go
index ff0161d36..455af3126 100644
--- a/lnwallet/btcwallet/btcwallet.go
+++ b/lnwallet/btcwallet/btcwallet.go
@@ -14,7 +14,6 @@ import (
 	"github.com/btcsuite/btcd/btcutil/hdkeychain"
 	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
-	"github.com/btcsuite/btcd/mempool"
 	"github.com/btcsuite/btcd/txscript"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/btcsuite/btcwallet/chain"
@@ -1059,7 +1058,7 @@ func (b *BtcWallet) PublishTransaction(tx *wire.MsgTx, label string) error {
 	// Note that we just use the time of the last block header here vs
 	// calculating the full median time past (BIP 113). We do this as a
 	// block's timestamp MUST be greater than the MTP of the prior block.
-	bestHash, bestHeight, err := b.chain.GetBestBlock()
+	/*bestHash, bestHeight, err := b.chain.GetBestBlock()
 	if err != nil {
 		return err
 	}
@@ -1074,7 +1073,7 @@ func (b *BtcWallet) PublishTransaction(tx *wire.MsgTx, label string) error {
 	)
 	if err != nil {
 		return fmt.Errorf("tx failed policy checks: %w", err)
-	}
+	}*/
 
 	// TODO(roasbeef): can also use testmempoolaccept here
 
diff --git a/lnwallet/test/test_interface.go b/lnwallet/test/test_interface.go
index 158f0dd99..19612f730 100644
--- a/lnwallet/test/test_interface.go
+++ b/lnwallet/test/test_interface.go
@@ -2055,9 +2055,14 @@ func testPublishTransaction(r *rpctest.Harness,
 		// TODO(roasbeef): we can't use Unwrap() here as TxRuleError
 		// doesn't define it
 		err := alice.PublishTransaction(testTx, labels.External)
-		require.True(
-			t, strings.Contains(err.Error(), "max allowed weight"),
-		)
+		fmt.Println(err)
+		//require.True(
+		//	t, strings.Contains(err.Error(), "max allowed weight"),
+		//	)
+		txHash := testTx.TxHash()
+		tx, err := alice.FetchTx(txHash)
+		require.NoError(t, err)
+		fmt.Println("tx value: ", tx)
 	})
 }

In this case, after I look up the transaction it isn't present, which confirms the suspicion that we are properly removing the transaction as expected if policy is violated. So I don't think we really need this branch as is, other than the funding manger, which is already covered elsewhere.

@yyforyongyu
Copy link
Copy Markdown
Member

In this case, after I look up the transaction it isn't present, which confirms the suspicion that we are properly removing the transaction as expected if policy is violated.

Cool so it is working as intended, thanks for confirming!

My thought is more about the overall architecture, like who is responsible for what. The layering is quite clear here. lnd defines all types of transactions, btcwallet treats them as raw tx and handles the validation and broadcast. We could abstract the validation logic into the CheckTransactionStandard or using testmempoolaccept, but is it lnd's job to perform validation at the policy level? Maybe not since PublishTransaction is already handling that part.

So my thought on this is to refactor PublishTransaction to return a list of explicitly defined errors, and we handle the errors one by one in lnd. This way we can keep the abstractions clear.

@Crypt-iQ
Copy link
Copy Markdown
Collaborator

I don't want this one to get lost since it's security-related so reopening it

@Crypt-iQ Crypt-iQ reopened this Jul 28, 2023
@lightninglabs-deploy
Copy link
Copy Markdown
Collaborator

@Roasbeef, remember to re-request review from reviewers when ready

@Roasbeef
Copy link
Copy Markdown
Member Author

Superseded by #8345

@Roasbeef Roasbeef closed this Jan 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LND generated too big onchain transaction wallet: respect policy-level transaction size limits when crafting regular and funding transactions

6 participants