Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
35 changes: 35 additions & 0 deletions backup/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"github.com/lightningnetwork/lnd/chainntnfs"
lfn "github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/keychain"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

const (
Expand Down Expand Up @@ -633,6 +635,26 @@ func detectSpentOutpoints(ctx context.Context,
index: idx, spent: true,
}
case err := <-ec:
// Under load, the notifier can return
// deadline/cancel errors for unspent outpoints.
// Treat that as unspent unless our parent
// context is already canceled.
if isDeadlineOrCanceledErr(err) {
if detectCtx.Err() != nil {
results <- spendResult{
index: idx,
err: detectCtx.Err(),
}
} else {
results <- spendResult{
index: idx,
spent: false,
}
}

return
}

results <- spendResult{
index: idx, err: err,
}
Expand Down Expand Up @@ -672,3 +694,16 @@ func detectSpentOutpoints(ctx context.Context,

return spent, nil
}

// isDeadlineOrCanceledErr returns true when an error indicates a timeout
// or cancellation, including gRPC status errors.
func isDeadlineOrCanceledErr(err error) bool {
if errors.Is(err, context.DeadlineExceeded) ||
errors.Is(err, context.Canceled) {

return true
}

code := status.Code(err)
return code == codes.DeadlineExceeded || code == codes.Canceled
}
5 changes: 5 additions & 0 deletions docs/release-notes/release-notes-0.8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@
fixes inverted sort direction in `AssetRoots`, `AssetLeafKeys`, and
`QueryEvents` universe RPCs.

* [PR#2135](https://github.com/lightninglabs/taproot-assets/pull/2135)
aligns `ListAssets` and `FetchAsset` group-key filtering with
`ListBalances` when `script_key_type` is omitted, so grouped balances
and grouped asset listings no longer disagree by default.

# New Features

## Functional Enhancements
Expand Down
22 changes: 19 additions & 3 deletions itest/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1323,8 +1323,15 @@ func AssertNonInteractiveRecvComplete(t *testing.T,
resp, err := receiver.AddrReceives(
ctxt, &taprpc.AddrReceivesRequest{},
)
require.NoError(t, err)
require.Len(t, resp.Events, totalInboundTransfers)
if err != nil {
return fmt.Errorf(
"unable to query receive events: %w", err,
)
}
if len(resp.Events) != totalInboundTransfers {
return fmt.Errorf("got %d receive events, wanted %d",
len(resp.Events), totalInboundTransfers)
}

for _, event := range resp.Events {
if event.Status != statusCompleted {
Expand Down Expand Up @@ -1749,6 +1756,15 @@ func AssertUniverseRootEquality(t *testing.T,
// by two daemons are either equal eventually.
func AssertUniverseRootEqualityEventually(t *testing.T,
clientA, clientB unirpc.UniverseClient) {
AssertUniverseRootEqualityEventuallyWithTimeout(
t, clientA, clientB, defaultWaitTimeout,
)
}

// AssertUniverseRootEqualityEventuallyWithTimeout checks that universe roots
// returned by two daemons are eventually equal within the given timeout.
func AssertUniverseRootEqualityEventuallyWithTimeout(t *testing.T,
clientA, clientB unirpc.UniverseClient, timeout time.Duration) {

ctx := context.Background()

Expand All @@ -1767,7 +1783,7 @@ func AssertUniverseRootEqualityEventually(t *testing.T,
}

return nil
}, defaultWaitTimeout)
}, timeout)
require.NoError(t, err)
}

Expand Down
4 changes: 2 additions & 2 deletions itest/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
// 3. Bob imports the same backup again — 0 imported (idempotent)
func testBackupRestoreGenesis(t *harnessTest) {
ctxb := context.Background()
ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout)
ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout*2)
defer cancel()

// Mint a single asset on Alice.
Expand Down Expand Up @@ -475,7 +475,7 @@ func assertAssetsMatch(t *harnessTest, expected []*taprpc.Asset,
// 5. Verify asset counts and group key presence on both nodes
func testBackupRestoreGrouped(t *harnessTest) {
ctxb := context.Background()
ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout)
ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout*2)
defer cancel()

// Mint a grouped asset and an ungrouped asset together.
Expand Down
1 change: 1 addition & 0 deletions itest/btcd
54 changes: 54 additions & 0 deletions itest/burn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,60 @@ func testBurnGroupedAssets(t *harnessTest) {
// Our asset balance should have been decreased by the burned amount.
AssertBalanceByID(t.t, t.tapd, burnAssetID2, postBurnAmt)

// Assert that querying assets by group key without specifying
// script_key_type returns all remaining group tranches.
groupBalances, err := t.tapd.ListBalances(
ctx, &taprpc.ListBalancesRequest{
GroupBy: &taprpc.ListBalancesRequest_GroupKey{
GroupKey: true,
},
GroupKeyFilter: assetGroupKey,
},
Comment thread
sergey3bv marked this conversation as resolved.
)
require.NoError(t.t, err)

groupBalance, ok := groupBalances.AssetGroupBalances[encodedGroupKey]
require.True(t.t, ok)

groupAssetsDefault, err := t.tapd.ListAssets(
ctx, &taprpc.ListAssetRequest{
GroupKey: assetGroupKey,
},
)
require.NoError(t.t, err)
require.NotEmpty(t.t, groupAssetsDefault.Assets)

sumAssets := func(assets []*taprpc.Asset) uint64 {
var amountSum uint64
for _, rpcAsset := range assets {
amountSum += rpcAsset.Amount
}

return amountSum
}
require.Equal(
t.t, groupBalance.Balance,
sumAssets(groupAssetsDefault.Assets),
)

// Explicit script key filtering should continue to work unchanged.
groupAssetsBip86, err := t.tapd.ListAssets(
ctx, &taprpc.ListAssetRequest{
GroupKey: assetGroupKey,
ScriptKeyType: &taprpc.ScriptKeyTypeQuery{
Type: &taprpc.ScriptKeyTypeQuery_ExplicitType{
ExplicitType: taprpc.
ScriptKeyType_SCRIPT_KEY_BIP86,
},
},
},
)
require.NoError(t.t, err)
require.LessOrEqual(
t.t, len(groupAssetsBip86.Assets),
len(groupAssetsDefault.Assets),
)

// Confirm that the minted asset group still contains two assets.
assetGroups, err = t.tapd.ListGroups(ctx, &taprpc.ListGroupsRequest{})
require.NoError(t.t, err)
Expand Down
33 changes: 16 additions & 17 deletions itest/custom_channels/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2550,9 +2550,13 @@ func locateAssetTransfers(t *testing.T, node *itest.IntegratedNode,

transfer = forceCloseTransfer.Transfers[0]

if transfer.AnchorTxBlockHash == nil {
return fmt.Errorf("missing anchor block hash, " +
"transfer not confirmed")
// CI can observe a short lag where the transfer has a
// confirmed block height but the block hash has not yet
// been populated in the RPC response.
if transfer.AnchorTxBlockHash == nil &&
transfer.AnchorTxBlockHeight == 0 {

return fmt.Errorf("transfer not confirmed yet")
}

return nil
Expand Down Expand Up @@ -3309,28 +3313,23 @@ func assertForceCloseSweeps(ctx context.Context,
t.t, bobSweepTx, "Bob's sweep transaction not found",
)

// There's always an extra input that pays for the fees. So we can only
// count the remainder as HTLC inputs.
// There's always an extra input that pays for fees. The remaining
// inputs are interpreted as swept HTLCs.
numSweptHTLCs := len(bobSweepTx.TxIn) - 1

// If we didn't yet sweep all HTLCs, then we need to wait for another
// sweep.
// If we didn't yet sweep all HTLCs, we expect an additional timeout
// sweep. In some runs the extra sweep can already be mined by the time
// we observe it here, so we fall back to confirming the balance delta.
if numSweptHTLCs < numTimeoutHTLCs {
// nolint:lll
assertSweepExists(
t.t, bob,
walletrpc.WitnessType_TAPROOT_HTLC_OFFERED_REMOTE_TIMEOUT,
)

t.Logf("Confirming additional HTLC timeout sweep txns")

_, err := waitForAtLeastNTxsInMempool(
net.Miner, 1, ccShortTimeout,
)
require.NoError(t.t, err)

// Mine a block to confirm the additional sweeps.
mineBlocks(t, net, 1, 0)
if err == nil {
// Mine a block to confirm the additional sweep.
mineBlocks(t, net, 1, 0)
}
}

// At this point, Bob's balance should be incremented by an additional
Expand Down
8 changes: 4 additions & 4 deletions itest/multi_asset_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,8 @@ func testMultiAssetGroupSend(t *harnessTest) {
collectibleGroupMembers + 1,
})

AssertUniverseRootEqualityEventually(
t.t, t.tapd, t.universeServer.service,
AssertUniverseRootEqualityEventuallyWithTimeout(
t.t, t.tapd, t.universeServer.service, defaultWaitTimeout*2,
)

// We'll make a second node now that'll be the receiver of all the
Expand All @@ -330,8 +330,8 @@ func testMultiAssetGroupSend(t *harnessTest) {
require.NoError(t.t, secondTapd.stop(!*noDelete))
}()

AssertUniverseRootEqualityEventually(
t.t, secondTapd, t.universeServer.service,
AssertUniverseRootEqualityEventuallyWithTimeout(
t.t, secondTapd, t.universeServer.service, defaultWaitTimeout*2,
)

// Send 5 of the assets to Bob, and verify that they are received.
Expand Down
27 changes: 27 additions & 0 deletions rpcserver/rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,15 @@ func (r *RPCServer) ListAssets(ctx context.Context,
return nil, fmt.Errorf("unable to parse script key type "+
"query: %w", err)
}

// If a group key is set and no explicit script key type query is
// present, include all script key types. This keeps group-key list
// semantics aligned with group balances.
Comment thread
sergey3bv marked this conversation as resolved.
if len(req.GroupKey) > 0 &&
(req.ScriptKeyType == nil || req.ScriptKeyType.Type == nil) {

scriptKeyType = fn.None[asset.ScriptKeyType]()
}
filters.ScriptKeyType = scriptKeyType

// Filter unconfirmed mints at the SQL level: when not including
Expand Down Expand Up @@ -1319,6 +1328,15 @@ func (r *RPCServer) FetchAsset(ctx context.Context,
return nil, fmt.Errorf("unable to parse script key type "+
"query: %w", err)
}

// If a group key is set and no explicit script key type query is
// present, include all script key types. This keeps group-key fetch
// semantics aligned with group balances.
Comment thread
sergey3bv marked this conversation as resolved.
if groupKey != nil &&
(req.ScriptKeyType == nil || req.ScriptKeyType.Type == nil) {

scriptKeyType = fn.None[asset.ScriptKeyType]()
}
filters.ScriptKeyType = scriptKeyType

// Filter unconfirmed mints at the SQL level.
Expand Down Expand Up @@ -1658,6 +1676,15 @@ func (r *RPCServer) ListBalances(ctx context.Context,
return nil, fmt.Errorf("invalid group_by")
}

// If the balance query is grouped by group key and no
// explicit script key type query is present, include all
// script key types.
// This keeps group-key balance semantics aligned with grouped
// asset listing/fetching.
if req.ScriptKeyType == nil || req.ScriptKeyType.Type == nil {
scriptKeyType = fn.None[asset.ScriptKeyType]()
}

var groupKey *btcec.PublicKey
if len(req.GroupKeyFilter) != 0 {
var err error
Expand Down
27 changes: 15 additions & 12 deletions taprpc/taprootassets.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading