Repository: lightninglabs/taproot-assets

Issue:
[bug]: nil pointer on ListBatches
### Background

When `ListBatches` is called with `Verbose: true`. The daemon breaks with a nil pointer.

### Your environment

```sh
version:\"0.5.1-alpha commit=v0.5.1\" lnd_version:\"0.18.5-beta\" network:\"regtest\" sync_to_chain:true"
```

### Steps to reproduce

- Run Polar with `tapd`.
- Mint an asset with MintAsset (tried with Collectible)
- Call `ListBatches` with `Verbose: true`

### Expected behavior

Returns sucessfully

### Actual behavior

```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x12deb6c]
goroutine 1390 [running]:
github.com/lightninglabs/taproot-assets.marshalVerboseBatch({{0x1925830, 0x7}, 0xdab5bffa, {0x192284c, 0x5}, {0x2e95520, 0x0, 0x0}, 0x2df17e0, 0x2ca8ae0, ...}, ...)
	github.com/lightninglabs/taproot-assets/rpcserver.go:4123 +0x2c
github.com/lightninglabs/taproot-assets.(*rpcServer).ListBatches.func1(0x2?)
	github.com/lightninglabs/taproot-assets/rpcserver.go:954 +0x8c
github.com/lightninglabs/taproot-assets/fn.MapErr[...]({0x40009de770?, 0x2, 0x1db84}, 0x4000a8b720?)
	github.com/lightninglabs/taproot-assets/fn/func.go:87 +0x68
github.com/lightninglabs/taproot-assets.(*rpcServer).ListBatches(0x40006dc8a0, {0x19091e0?, 0x40006dc640?}, 0x4000b3a200)
	github.com/lightninglabs/taproot-assets/rpcserver.go:950 +0x3a8
github.com/lightninglabs/taproot-assets/taprpc/mintrpc._Mint_ListBatches_Handler.func1({0x1cd06a8?, 0x40006e3320?}, {0x17ef4c0?, 0x4000b3a200?})
	github.com/lightninglabs/taproot-assets/taprpc/mintrpc/mint_grpc.pb.go:335 +0xd0
github.com/lightninglabs/taproot-assets/rpcperms.(*InterceptorChain).CreateServerOpts.(*InterceptorChain).MacaroonUnaryServerInterceptor.func5({0x1cd06a8, 0x40006e3320}, {0x17ef4c0, 0x4000b3a200}, 0x0?, 0x4000646768)
	github.com/lightninglabs/taproot-assets/rpcperms/interceptor.go:360 +0x90
google.golang.org/grpc.getChainUnaryHandler.func1({0x1cd06a8, 0x40006e3320}, {0x17ef4c0, 0x4000b3a200})
	google.golang.org/grpc@v1.59.0/server.go:1163 +0xa0
github.com/lightninglabs/taproot-assets/rpcperms.(*InterceptorChain).CreateServerOpts.(*InterceptorChain).rpcStateUnaryServerInterceptor.func3({0x1cd06a8, 0x40006e3320}, {0x17ef4c0, 0x4000b3a200}, 0x4000216440?, 0x4000b3a280)
	github.com/lightninglabs/taproot-assets/rpcperms/interceptor.go:425 +0x90
google.golang.org/grpc.getChainUnaryHandler.func1({0x1cd06a8, 0x40006e3320}, {0x17ef4c0, 0x4000b3a200})
	google.golang.org/grpc@v1.59.0/server.go:1163 +0xa0
github.com/lightninglabs/taproot-assets/rpcperms.(*InterceptorChain).CreateServerOpts.errorLogUnaryServerInterceptor.func1({0x1cd06a8?, 0x40006e3320?}, {0x17ef4c0?, 0x4000b3a200?}, 0x4000216440, 0x4000646768?)
	github.com/lightninglabs/taproot-assets/rpcperms/interceptor.go:276 +0x48
google.golang.org/grpc.NewServer.chainUnaryServerInterceptors.chainUnaryInterceptors.func1({0x1cd06a8, 0x40006e3320}, {0x17ef4c0, 0x4000b3a200}, 0x4000216440, 0x1876b00?)
	google.golang.org/grpc@v1.59.0/server.go:1154 +0x88
github.com/lightninglabs/taproot-assets/taprpc/mintrpc._Mint_ListBatches_Handler({0x19091e0, 0x40006dc8a0}, {0x1cd06a8, 0x40006e3320}, 0x40006ee300, 0x40000a3820)
	github.com/lightninglabs/taproot-assets/taprpc/mintrpc/mint_grpc.pb.go:337 +0x148
google.golang.org/grpc.(*Server).processUnaryRPC(0x40009c0000, {0x1cd06a8, 0x40006e3260}, {0x1ce14e0, 0x400066c4e0}, 0x40002fd560, 0x40009aaea0, 0x2df2198, 0x0)
	google.golang.org/grpc@v1.59.0/server.go:1343 +0xb40
google.golang.org/grpc.(*Server).handleStream(0x40009c0000, {0x1ce14e0, 0x400066c4e0}, 0x40002fd560)
	google.golang.org/grpc@v1.59.0/server.go:1737 +0x95c
google.golang.org/grpc.(*Server).serveStreams.func1.1()
	google.golang.org/grpc@v1.59.0/server.go:986 +0x88
created by google.golang.org/grpc.(*Server).serveStreams.func1 in goroutine 1389
	google.golang.org/grpc@v1.59.0/server.go:997 +0x14c
```

Hints from issue comments:
@Roasbeef: Are you able to reproduce this with v0.6 rc1? 

It was recently tagged: https://github.com/lightninglabs/taproot-assets/releases/tag/v0.6.0-rc1@Roasbeef: Actually I think there is an actual bug here with listing batches verbose, but when a batch isn't funded yet, or pending: 
https://github.com/lightninglabs/taproot-assets/blob/8b9cfa0d0e5d77433c3607863ae271da7d05f33e/tapgarden/planter.go#L1524-L1534@ffranr: @darioAnongba, thank you for the report! I've put up a fix PR: https://github.com/lightninglabs/taproot-assets/pull/1536

Solution patch:
diff --git a/tapgarden/planter.go b/tapgarden/planter.go
index 4ef58877..e686f7dd 100644
--- a/tapgarden/planter.go
+++ b/tapgarden/planter.go
@@ -1516,7 +1516,7 @@ func listBatches(ctx context.Context, batchStore MintingStore,
 	}
 
 	// Formulate verbose batches from the sorted batches.
-	verboseBatches := make([]*VerboseBatch, len(sortedBatches))
+	verboseBatches := make([]*VerboseBatch, 0, len(sortedBatches))
 
 	for idx := range sortedBatches {
 		currentBatch := sortedBatches[idx]
@@ -1527,6 +1527,10 @@ func listBatches(ctx context.Context, batchStore MintingStore,
 		case currentBatch.State() != BatchStatePending:
 			continue
 		case !currentBatch.IsFunded():
+			// The batch isn't funded yet, so we can't display any
+			// pending asset group information. Funding is required
+			// because the anchor transaction outpoint is needed to
+			// formulate pending asset group key requests.
 			continue
 		case len(currentBatch.Seedlings) == 0:
 			continue
@@ -1538,7 +1542,7 @@ func listBatches(ctx context.Context, batchStore MintingStore,
 			return nil, err
 		}
 
-		verboseBatches[idx] = verboseBatch
+		verboseBatches = append(verboseBatches, verboseBatch)
 	}
 
 	return verboseBatches, nil
diff --git a/taprpc/mintrpc/mint.pb.go b/taprpc/mintrpc/mint.pb.go
index 3e481ce3..4b65e761 100644
--- a/taprpc/mintrpc/mint.pb.go
+++ b/taprpc/mintrpc/mint.pb.go
@@ -1325,8 +1325,8 @@ type ListBatchRequest struct {
 	//	*ListBatchRequest_BatchKey
 	//	*ListBatchRequest_BatchKeyStr
 	Filter isListBatchRequest_Filter `protobuf_oneof:"filter"`
-	// If true, pending asset group information will be shown for the pending
-	// batch.
+	// If true, pending asset group details will be included for any funded,
+	// non-empty pending batch. Unfunded or empty batches will be excluded.
 	Verbose bool `protobuf:"varint,3,opt,name=verbose,proto3" json:"verbose,omitempty"`
 }
 
diff --git a/taprpc/mintrpc/mint.proto b/taprpc/mintrpc/mint.proto
index 8190d132..6f998eb8 100644
--- a/taprpc/mintrpc/mint.proto
+++ b/taprpc/mintrpc/mint.proto
@@ -420,8 +420,8 @@ message ListBatchRequest {
         string batch_key_str = 2;
     }
 
-    // If true, pending asset group information will be shown for the pending
-    // batch.
+    // If true, pending asset group details will be included for any funded,
+    // non-empty pending batch. Unfunded or empty batches will be excluded.
     bool verbose = 3;
 }
 
diff --git a/taprpc/mintrpc/mint.swagger.json b/taprpc/mintrpc/mint.swagger.json
index 60bef4bd..8eeb733e 100644
--- a/taprpc/mintrpc/mint.swagger.json
+++ b/taprpc/mintrpc/mint.swagger.json
@@ -85,7 +85,7 @@
           },
           {
             "name": "verbose",
-            "description": "If true, pending asset group information will be shown for the pending\nbatch.",
+            "description": "If true, pending asset group details will be included for any funded,\nnon-empty pending batch. Unfunded or empty batches will be excluded.",
             "in": "query",
             "required": false,
             "type": "boolean"



Test patch:
diff --git a/itest/assets_test.go b/itest/assets_test.go
index f0f6b9aa..453c8611 100644
--- a/itest/assets_test.go
+++ b/itest/assets_test.go
@@ -174,7 +174,17 @@ func testMintBatchResume(t *harnessTest) {
 
 	// Build one batch, but leave it as pending.
 	BuildMintingBatch(t.t, t.tapd, simpleAssets)
-	batchResp, err := t.tapd.ListBatches(ctxt, &mintrpc.ListBatchRequest{})
+
+	// The batch is pending and unfunded, so the verbose batch listing
+	// should return an empty list. An anchor transaction (BTC funding) is
+	// required for verbose listing to include group key requests.
+	batchResp, err := t.tapd.ListBatches(ctxt, &mintrpc.ListBatchRequest{
+		Verbose: true,
+	})
+	require.NoError(t.t, err)
+	require.Empty(t.t, batchResp.Batches)
+
+	batchResp, err = t.tapd.ListBatches(ctxt, &mintrpc.ListBatchRequest{})
 	require.NoError(t.t, err)
 	require.Len(t.t, batchResp.Batches, 1)
 	t.Logf("batches: %v", toJSON(t.t, batchResp))