diff --git a/go.mod b/go.mod index e9d7dd93f..0bf53550f 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/lightninglabs/lightning-terminal -go 1.24.11 +go 1.25.5 require ( github.com/btcsuite/btcd v0.24.3-0.20250318170759-4f4ea81776d6 - github.com/btcsuite/btcd/btcec/v2 v2.3.4 + github.com/btcsuite/btcd/btcec/v2 v2.3.6 github.com/btcsuite/btcd/btcutil v1.1.5 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/btclog/v2 v2.0.1-0.20250728225537-6090e87c6c5b @@ -40,7 +40,7 @@ require ( github.com/lightningnetwork/lnd/fn v1.2.5 github.com/lightningnetwork/lnd/fn/v2 v2.0.9 github.com/lightningnetwork/lnd/kvdb v1.4.16 - github.com/lightningnetwork/lnd/sqldb v1.0.12-0.20260113193010-8565d12e40b1 + github.com/lightningnetwork/lnd/sqldb v1.0.13-0.20260113150738-e26a114cdcf0 github.com/lightningnetwork/lnd/tlv v1.3.2 github.com/lightningnetwork/lnd/tor v1.1.6 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f @@ -252,3 +252,11 @@ replace github.com/golang-migrate/migrate/v4 => github.com/lightninglabs/migrate // The upstream nhooyr.io/websocket repository has been moved to github.com/coder/websocket. replace nhooyr.io/websocket => github.com/coder/websocket v1.8.7 + +replace github.com/lightninglabs/taproot-assets => github.com/GeorgeTsagk/taproot-assets v0.0.0-20260216125845-2dd50064afbe + +replace github.com/lightninglabs/taproot-assets/taprpc => github.com/GeorgeTsagk/taproot-assets/taprpc v0.0.0-20260216125845-2dd50064afbe + +replace github.com/lightningnetwork/lnd/sqldb => github.com/GeorgeTsagk/lnd/sqldb v0.0.0-20260216125012-1bb47e1ca203 + +replace github.com/lightningnetwork/lnd => github.com/GeorgeTsagk/lnd v0.0.0-20260216125012-1bb47e1ca203 diff --git a/go.sum b/go.sum index 1a3007d2f..c4dadb1d3 100644 --- a/go.sum +++ b/go.sum @@ -604,6 +604,14 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/GeorgeTsagk/lnd v0.0.0-20260216125012-1bb47e1ca203 h1:3phgYsGseaHvSknUVQkzVgbpr3vTAUVPxccnK87TkVQ= +github.com/GeorgeTsagk/lnd v0.0.0-20260216125012-1bb47e1ca203/go.mod h1:ybhzpoSuWJmTENgFS9N8pXnY9VCHwh07Lqygh1Pzjqw= +github.com/GeorgeTsagk/lnd/sqldb v0.0.0-20260216125012-1bb47e1ca203 h1:mEINJ5xD3NMx4kxYVY0ucxoW61Iadf/vhlXOzfgZ2+c= +github.com/GeorgeTsagk/lnd/sqldb v0.0.0-20260216125012-1bb47e1ca203/go.mod h1:XaG3d8AR7/e6+HUw5jvNvm+gs6MowB+iE9myFH8Rc14= +github.com/GeorgeTsagk/taproot-assets v0.0.0-20260216125845-2dd50064afbe h1:MwMakhPdguYpuRxtnPN2OnqtcGPR0UlgrGoGrqGnhsc= +github.com/GeorgeTsagk/taproot-assets v0.0.0-20260216125845-2dd50064afbe/go.mod h1:dv7o1G884kl6/ck/G3YR3GAZnXYCsB9tuXPmQQ4EXrE= +github.com/GeorgeTsagk/taproot-assets/taprpc v0.0.0-20260216125845-2dd50064afbe h1:oAwPmmB/tWqE7y/WAQkcSAPj9eXueXcfTdGBJwNewdk= +github.com/GeorgeTsagk/taproot-assets/taprpc v0.0.0-20260216125845-2dd50064afbe/go.mod h1:LZ1kr/fxhFh8wfLIQOsfr5dzcseKlQVEDxtNH1DfiQg= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= @@ -653,8 +661,8 @@ github.com/btcsuite/btcd v0.24.3-0.20250318170759-4f4ea81776d6 h1:8n9k3I7e8DkpdQ github.com/btcsuite/btcd v0.24.3-0.20250318170759-4f4ea81776d6/go.mod h1:OmM4kFtB0klaG/ZqT86rQiyw/1iyXlJgc3UHClPhhbs= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= -github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.6 h1:IzlsEr9olcSRKB/n7c4351F3xHKxS2lma+1UFGCYd4E= +github.com/btcsuite/btcd/btcec/v2 v2.3.6/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= @@ -1156,14 +1164,8 @@ github.com/lightninglabs/pool/poolrpc v1.0.1 h1:XbNx28TYwEj/PVsnnF9TnveVCMCYfS1v github.com/lightninglabs/pool/poolrpc v1.0.1/go.mod h1:836icifg/SBnZbiae0v3jeRRzCrT6LWo32SqCS/JiGk= github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display h1:w7FM5LH9Z6CpKxl13mS48idsu6F+cEZf0lkyiV+Dq9g= github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -github.com/lightninglabs/taproot-assets v0.7.1 h1:CXRAsYZ3QObkOqX8JjLTOaw6I4+t8yVCn7PJL6tt0b8= -github.com/lightninglabs/taproot-assets v0.7.1/go.mod h1:a7/MtMgHjnCvvCYxS2yVlP9rCa7LC770LFHTiqGRqH4= -github.com/lightninglabs/taproot-assets/taprpc v1.0.12 h1:mmCTesDYFHo/oP7MN5M4Q/GzIrpXpZG/X/rWN/1W/Is= -github.com/lightninglabs/taproot-assets/taprpc v1.0.12/go.mod h1:vwW5SFnlDOAM65gLDkxR+dcIy/yMEkIupnPHA95jT0c= github.com/lightningnetwork/lightning-onion v1.2.1-0.20240815225420-8b40adf04ab9 h1:6D3LrdagJweLLdFm1JNodZsBk6iU4TTsBBFLQ4yiXfI= github.com/lightningnetwork/lightning-onion v1.2.1-0.20240815225420-8b40adf04ab9/go.mod h1:EDqJ3MuZIbMq0QI1czTIKDJ/GS8S14RXPwapHw8cw6w= -github.com/lightningnetwork/lnd v0.20.1-beta h1:wDMNgks5uST1CY+WwjIZ4+McPMMFpr2pIIGJp7ytDI4= -github.com/lightningnetwork/lnd v0.20.1-beta/go.mod h1:oIKh9EqE1sJJpQPq9ZCMFc4Ot287NrotZ1oZn0zUI+M= github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf0d0Uy4qBjI= github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U= github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0= @@ -1178,8 +1180,6 @@ github.com/lightningnetwork/lnd/kvdb v1.4.16 h1:9BZgWdDfjmHRHLS97cz39bVuBAqMc4/p github.com/lightningnetwork/lnd/kvdb v1.4.16/go.mod h1:HW+bvwkxNaopkz3oIgBV6NEnV4jCEZCACFUcNg4xSjM= github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI= github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4= -github.com/lightningnetwork/lnd/sqldb v1.0.12-0.20260113193010-8565d12e40b1 h1:PkEppKL17cZh0Dr9h/T9BEVJUbd/p2tjJ/x8ffG3R0M= -github.com/lightningnetwork/lnd/sqldb v1.0.12-0.20260113193010-8565d12e40b1/go.mod h1:tB2jlqu79TIOR9uhAZOmPxpVFUhB2s+oxKnqRRL1oc0= github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM= github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA= github.com/lightningnetwork/lnd/tlv v1.3.2 h1:MO4FCk7F4k5xPMqVZF6Nb/kOpxlwPrUQpYjmyKny5s0= diff --git a/itest/assets_test.go b/itest/assets_test.go index 7ff573aba..4b361eee2 100644 --- a/itest/assets_test.go +++ b/itest/assets_test.go @@ -15,7 +15,6 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" - taprootassets "github.com/lightninglabs/taproot-assets" "github.com/lightninglabs/taproot-assets/asset" tapfn "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/itest" @@ -23,6 +22,7 @@ import ( "github.com/lightninglabs/taproot-assets/rfq" "github.com/lightninglabs/taproot-assets/rfqmath" "github.com/lightninglabs/taproot-assets/rfqmsg" + "github.com/lightninglabs/taproot-assets/rpcserver" "github.com/lightninglabs/taproot-assets/rpcutils" "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/taprpc" @@ -677,7 +677,7 @@ func createTestAssetNetworkGroupKey(ctx context.Context, t *harnessTest, AssetId: assetID1, }, AmountToBurn: burnAmount1, - ConfirmationText: taprootassets.AssetBurnConfirmationText, + ConfirmationText: rpcserver.AssetBurnConfirmationText, }) require.NoError(t.t, err) @@ -690,7 +690,7 @@ func createTestAssetNetworkGroupKey(ctx context.Context, t *harnessTest, AssetId: assetID2, }, AmountToBurn: burnAmount2, - ConfirmationText: taprootassets.AssetBurnConfirmationText, + ConfirmationText: rpcserver.AssetBurnConfirmationText, }) require.NoError(t.t, err) diff --git a/itest/litd_custom_channels_test.go b/itest/litd_custom_channels_test.go index e27b6bc5b..95856001c 100644 --- a/itest/litd_custom_channels_test.go +++ b/itest/litd_custom_channels_test.go @@ -2230,19 +2230,107 @@ func testCustomChannelsBreach(ctx context.Context, net *NetworkHarness, ) } - logBalance(t.t, nodes, assetID, "after keysend -- breach state") + logBalance(t.t, nodes, assetID, "after keysend -- balanced state") + + // Now create hodl invoices on both sides to ensure HTLCs exist on the + // commitment we're about to backup. This will test the revoked HTLC + // sweep paths (TaprootHtlcOfferedRevoke and TaprootHtlcAcceptedRevoke). + const ( + numHodlInvoices = 2 + htlcAmount = 50 + ) + + var ( + daveHodlInvoices []assetHodlInvoice + charlieHodlInvoices []assetHodlInvoice + ) + + t.Logf("Creating %d hodl invoices for each peer...", numHodlInvoices) + + // Create Dave's hodl invoices (Charlie will pay = outgoing HTLCs) + for i := 0; i < numHodlInvoices; i++ { + daveHodlInvoices = append( + daveHodlInvoices, createAssetHodlInvoice( + t.t, charlie, dave, htlcAmount, assetID, + ), + ) + } + + // Create Charlie's hodl invoices (Dave will pay = incoming HTLCs) + for i := 0; i < numHodlInvoices; i++ { + charlieHodlInvoices = append( + charlieHodlInvoices, createAssetHodlInvoice( + t.t, dave, charlie, htlcAmount, assetID, + ), + ) + } + + // Pay all invoices but don't settle (HTLCs stay in flight) + payOpt := withFailure( + lnrpc.Payment_IN_FLIGHT, + lnrpc.PaymentFailureReason_FAILURE_REASON_NONE, + ) + + t.Logf("Paying hodl invoices to create HTLCs on commitment...") + + for _, daveInv := range daveHodlInvoices { + payInvoiceWithAssets( + t.t, charlie, dave, daveInv.payReq, assetID, payOpt, + ) + } + + for _, charlieInv := range charlieHodlInvoices { + payInvoiceWithAssets( + t.t, dave, charlie, charlieInv.payReq, assetID, payOpt, + ) + } + + // Verify HTLCs are active on both sides + expectedHtlcs := numHodlInvoices * 2 + assertNumHtlcs(t.t, charlie, expectedHtlcs) + assertNumHtlcs(t.t, dave, expectedHtlcs) + + logBalance(t.t, nodes, assetID, "after hodl invoices -- breach state") // Now we'll create an on disk snapshot that we'll use to restore back - // to as our breached state. + // to as our breached state. This state has active HTLCs! require.NoError(t.t, net.StopAndBackupDB(dave)) connectAllNodes(t.t, net, nodes) - // We'll send one more keysend payment now to revoke the state we were - // just at above. + // Now we'll settle all the hodl invoices to revoke the state with + // HTLCs. This will cause the backed-up state to become revoked, which + // will trigger the breach detection when Dave broadcasts it. + t.Logf("Settling hodl invoices to revoke breach state...") + + for _, daveInv := range daveHodlInvoices { + _, err := dave.InvoicesClient.SettleInvoice( + ctx, &invoicesrpc.SettleInvoiceMsg{ + Preimage: daveInv.preimage[:], + }, + ) + require.NoError(t.t, err) + } + + for _, charlieInv := range charlieHodlInvoices { + _, err := charlie.InvoicesClient.SettleInvoice( + ctx, &invoicesrpc.SettleInvoiceMsg{ + Preimage: charlieInv.preimage[:], + }, + ) + require.NoError(t.t, err) + } + + // Send one more keysend to ensure the state with settled HTLCs is + // committed and the previous state (with active HTLCs) is revoked. sendAssetKeySendPayment( t.t, charlie, dave, keySendAmount, assetID, fn.Some(btcAmt), ) - logBalance(t.t, nodes, assetID, "after keysend -- final state") + + // Wait for all HTLCs to clear + assertNumHtlcs(t.t, charlie, 0) + assertNumHtlcs(t.t, dave, 0) + + logBalance(t.t, nodes, assetID, "after settling HTLCs -- final state") // With the final state achieved, we'll now restore Dave (who will be // force closing) to that old state, the breach state. @@ -2287,6 +2375,28 @@ func testCustomChannelsBreach(ctx context.Context, net *NetworkHarness, t.Logf("Charlie justice txid: %v", charlieJusticeTxid) + // Fetch the justice transaction to verify it sweeps both commitment + // outputs AND the revoked HTLCs. + justiceTx := t.lndHarness.Miner.GetRawTransaction(*charlieJusticeTxid[0]) + justiceMsgTx := justiceTx.MsgTx() + + // The justice transaction should sweep: + // - 2 commitment outputs (Charlie's local, Dave's remote) + // - 2 offered HTLC revocations (Charlie's outgoing HTLCs) + // - 2 accepted HTLC revocations (Charlie's incoming HTLCs) + // + // This verifies that our new revoked HTLC sweep implementations + // (TaprootHtlcOfferedRevoke and TaprootHtlcAcceptedRevoke) are working. + expectedInputs := 2 + (numHodlInvoices * 2) + require.Len(t.t, justiceMsgTx.TxIn, expectedInputs, + "justice tx should sweep %d inputs (2 commitments + %d HTLCs)", + expectedInputs, numHodlInvoices*2) + + t.Logf("Justice tx has %d inputs: 2 commitment outputs + %d revoked "+ + "HTLCs (%d offered + %d accepted)", + len(justiceMsgTx.TxIn), numHodlInvoices*2, + numHodlInvoices, numHodlInvoices) + // Next, we'll mine a block to confirm Charlie's justice transaction. mineBlocks(t, net, 1, 1) @@ -2298,15 +2408,35 @@ func testCustomChannelsBreach(ctx context.Context, net *NetworkHarness, t.Logf("Charlie justice transfer: %v", toProtoJSON(t.t, charlieJusticeTransfer)) - // Charlie's balance should now be the same as before the breach - // attempt: the amount he minted at the very start. - charlieBalance := itestAsset.Amount + // After sweeping the breach state, Charlie should have all the asset + // balance. Due to the breach taking place though, it's scattered across + // 7 individual UTXOs. itest.AssertBalances( - t.t, charlieTap, charlieBalance, itest.WithAssetID(assetID), - itest.WithNumUtxos(3), + t.t, charlieTap, itestAsset.Amount, + itest.WithAssetID(assetID), itest.WithNumUtxos(7), ) - t.Logf("Charlie balance after breach: %d", charlieBalance) + // As a final check let's make sure that Charlie can consolidate all + // the assets to a single UTXO. We'll create an address for that. + resAddr, err := charlieTap.NewAddr(ctx, &taprpc.NewAddrRequest{ + AssetId: assetID, + Amt: itestAsset.Amount, + }) + require.NoError(t.t, err) + + // Now we dispatch the send. + _, err = charlieTap.SendAsset(ctx, &taprpc.SendAssetRequest{ + TapAddrs: []string{resAddr.Encoded}, + }) + require.NoError(t.t, err) + + mineBlocks(t, net, 1, 1) + + // The balance should all be under a single UTXO. + itest.AssertBalances( + t.t, charlieTap, itestAsset.Amount, + itest.WithAssetID(assetID), itest.WithNumUtxos(1), + ) } // testCustomChannelsV1Upgrade tests the upgrade path of a taproot assets diff --git a/subservers/taproot-assets.go b/subservers/taproot-assets.go index 87cf36955..c06156232 100644 --- a/subservers/taproot-assets.go +++ b/subservers/taproot-assets.go @@ -11,6 +11,7 @@ import ( "github.com/lightninglabs/taproot-assets/address" "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/tapcfg" + "github.com/lightninglabs/taproot-assets/tapconfig" "github.com/lightninglabs/taproot-assets/taprpc" "github.com/lightningnetwork/lnd/lnrpc" "google.golang.org/grpc" @@ -179,11 +180,11 @@ func (t *taprootAssetsSubServer) WhiteListedURLs() map[string]struct{} { // is not whitelisted there. publicUniRead := strings.Contains( t.cfg.Universe.PublicAccess, - string(tap.UniversePublicAccessStatusRead), + string(tapconfig.UniversePublicAccessStatusRead), ) publicUniWrite := strings.Contains( t.cfg.Universe.PublicAccess, - string(tap.UniversePublicAccessStatusWrite), + string(tapconfig.UniversePublicAccessStatusWrite), ) return taprpc.MacaroonWhitelist(