@@ -10,7 +10,7 @@ import (
10
10
"errors"
11
11
"fmt"
12
12
"strconv"
13
- "strings "
13
+ "sync "
14
14
"time"
15
15
16
16
"github.com/sirupsen/logrus"
@@ -154,6 +154,7 @@ type (
154
154
ZcashRpcReplyGetblock1 struct {
155
155
Hash string
156
156
Tx []string
157
+ Hex string
157
158
Trees struct {
158
159
Sapling struct {
159
160
Size uint32
@@ -295,6 +296,12 @@ func GetLightdInfo() (*walletrpc.LightdInfo, error) {
295
296
}, nil
296
297
}
297
298
299
+ // For performance, cache whether getblock verbose level 3 is available
300
+ // (we may be using an older zcashd that doesn't support it).
301
+ var triedGetblock3 bool
302
+ var haveGetblock3 bool
303
+ var getblock3mutex sync.Mutex
304
+
298
305
func getBlockFromRPC (height int ) (* walletrpc.CompactBlock , error ) {
299
306
// `block.ParseFromSlice` correctly parses blocks containing v5
300
307
// transactions, but incorrectly computes the IDs of the v5 transactions.
@@ -311,21 +318,56 @@ func getBlockFromRPC(height int) (*walletrpc.CompactBlock, error) {
311
318
}
312
319
params := make ([]json.RawMessage , 2 )
313
320
params [0 ] = heightJSON
314
- // Fetch the block using the verbose option ("1") because it provides
315
- // both the list of txids, which we're not yet able to compute for
316
- // Orchard (V5) transactions, and the block hash (block ID), which
317
- // we need to fetch the raw data format of the same block. Don't fetch
321
+
322
+ // We're not (yet) able to compute v5 transactions IDs (from the raw
323
+ // serialized transaction), so we get this from zcashd 'getblock'.
324
+ //
325
+ // First try verbose=3, which returns a JSON object with exactly
326
+ // what's needed: the block raw hex and the list of txids.
327
+ //
328
+ // If unsuccessful (zcashd isn't upgraded),
329
+ // fetch the block using the verbose option ("1") because it provides
330
+ // both the list of txids and the block hash (block ID), which we then
331
+ // use to fetch the raw data format of the same block. Don't fetch
318
332
// by height in case a reorg occurs between the two getblock calls;
319
333
// using block hash ensures that we're fetching the same block.
320
- params [1 ] = json .RawMessage ("1" )
321
- result , rpcErr := RawRequest ("getblock" , params )
322
- if rpcErr != nil {
323
- // Check to see if we are requesting a height the zcashd doesn't have yet
324
- if (strings .Split (rpcErr .Error (), ":" ))[0 ] == "-8" {
325
- return nil , nil
334
+ var result json.RawMessage
335
+ var rpcErr error
336
+ getblock3mutex .Lock ()
337
+ tried := triedGetblock3
338
+ have := haveGetblock3
339
+ getblock3mutex .Unlock ()
340
+ if ! tried || have {
341
+ params [1 ] = json .RawMessage ("3" )
342
+ result , rpcErr = RawRequest ("getblock" , params )
343
+ if rpcErr != nil {
344
+ if rpcErr .Error () == "-8: Block height out of range" {
345
+ return nil , nil
346
+ }
347
+ // Check for an unexpected error
348
+ if rpcErr .Error () != "-8: Verbosity must be in range from 0 to 2" {
349
+ return nil , fmt .Errorf ("error requesting getblock 3: %w" , rpcErr )
350
+ }
351
+ // zcashd must not be upgraded to support verbose=3
352
+ }
353
+ // zcashd supports verbose=3 if we got a result
354
+ getblock3mutex .Lock ()
355
+ triedGetblock3 = true
356
+ haveGetblock3 = (rpcErr == nil )
357
+ getblock3mutex .Unlock ()
358
+ }
359
+ if len (result ) == 0 {
360
+ params [1 ] = json .RawMessage ("1" )
361
+ result , rpcErr = RawRequest ("getblock" , params )
362
+ if rpcErr != nil {
363
+ // Check to see if we are requesting a height zcashd doesn't have yet
364
+ if rpcErr .Error () == "-8: Block height out of range" {
365
+ return nil , nil
366
+ }
367
+ return nil , fmt .Errorf ("error requesting verbose block: %w" , rpcErr )
326
368
}
327
- return nil , fmt .Errorf ("error requesting verbose block: %w" , rpcErr )
328
369
}
370
+ // This type works for both the verbose=1 or 3 reply.
329
371
var block1 ZcashRpcReplyGetblock1
330
372
err = json .Unmarshal (result , & block1 )
331
373
if err != nil {
@@ -335,26 +377,26 @@ func getBlockFromRPC(height int) (*walletrpc.CompactBlock, error) {
335
377
if err != nil {
336
378
Log .Fatal ("getBlockFromRPC bad block hash" , block1 .Hash )
337
379
}
338
- params [0 ] = blockHash
339
- params [1 ] = json .RawMessage ("0" ) // non-verbose (raw hex)
340
- result , rpcErr = RawRequest ("getblock" , params )
380
+ if block1 .Hex == "" {
381
+ // the getblock verbose=3 attempt must have failed, get the raw hex now
382
+ params [0 ] = blockHash
383
+ params [1 ] = json .RawMessage ("0" ) // non-verbose (raw hex, non-JSON)
384
+ result , rpcErr = RawRequest ("getblock" , params )
341
385
342
- // For some reason, the error responses are not JSON
343
- if rpcErr != nil {
344
- return nil , fmt .Errorf ("error requesting block: %w" , rpcErr )
345
- }
386
+ // For some reason, the error responses are not JSON
387
+ if rpcErr != nil {
388
+ return nil , fmt .Errorf ("error requesting block: %w" , rpcErr )
389
+ }
346
390
347
- var blockDataHex string
348
- err = json . Unmarshal ( result , & blockDataHex )
349
- if err != nil {
350
- return nil , fmt . Errorf ( "error reading JSON response: %w" , err )
391
+ err = json . Unmarshal ( result , & block1 . Hex )
392
+ if err != nil {
393
+ return nil , fmt . Errorf ( "error reading JSON response: %w" , err )
394
+ }
351
395
}
352
-
353
- blockData , err := hex .DecodeString (blockDataHex )
396
+ blockData , err := hex .DecodeString (block1 .Hex )
354
397
if err != nil {
355
- return nil , fmt .Errorf ("error decoding getblock output : %w" , err )
398
+ return nil , fmt .Errorf ("error decoding block hex : %w" , err )
356
399
}
357
-
358
400
block := parser .NewBlock ()
359
401
rest , err := block .ParseFromSlice (blockData )
360
402
if err != nil {
0 commit comments