Skip to content

Commit 23f1889

Browse files
author
Larry Ruane
committed
use getblock verbose=3 instead of two getblock calls
If available, use `getblock verbose=3`. If not available, use the old way, which requires making two `getblock` RPCs. This is only a performance improvement.
1 parent 283cc64 commit 23f1889

File tree

2 files changed

+79
-28
lines changed

2 files changed

+79
-28
lines changed

common/common.go

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"errors"
1111
"fmt"
1212
"strconv"
13-
"strings"
13+
"sync"
1414
"time"
1515

1616
"github.com/sirupsen/logrus"
@@ -154,6 +154,7 @@ type (
154154
ZcashRpcReplyGetblock1 struct {
155155
Hash string
156156
Tx []string
157+
Hex string
157158
Trees struct {
158159
Sapling struct {
159160
Size uint32
@@ -295,6 +296,12 @@ func GetLightdInfo() (*walletrpc.LightdInfo, error) {
295296
}, nil
296297
}
297298

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+
298305
func getBlockFromRPC(height int) (*walletrpc.CompactBlock, error) {
299306
// `block.ParseFromSlice` correctly parses blocks containing v5
300307
// transactions, but incorrectly computes the IDs of the v5 transactions.
@@ -311,21 +318,56 @@ func getBlockFromRPC(height int) (*walletrpc.CompactBlock, error) {
311318
}
312319
params := make([]json.RawMessage, 2)
313320
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
318332
// by height in case a reorg occurs between the two getblock calls;
319333
// 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)
326368
}
327-
return nil, fmt.Errorf("error requesting verbose block: %w", rpcErr)
328369
}
370+
// This type works for both the verbose=1 or 3 reply.
329371
var block1 ZcashRpcReplyGetblock1
330372
err = json.Unmarshal(result, &block1)
331373
if err != nil {
@@ -335,26 +377,26 @@ func getBlockFromRPC(height int) (*walletrpc.CompactBlock, error) {
335377
if err != nil {
336378
Log.Fatal("getBlockFromRPC bad block hash", block1.Hash)
337379
}
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)
341385

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+
}
346390

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+
}
351395
}
352-
353-
blockData, err := hex.DecodeString(blockDataHex)
396+
blockData, err := hex.DecodeString(block1.Hex)
354397
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)
356399
}
357-
358400
block := parser.NewBlock()
359401
rest, err := block.ParseFromSlice(blockData)
360402
if err != nil {

frontend/frontend_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,11 +360,14 @@ func TestGetTaddressTxidsNilArgs(t *testing.T) {
360360
}
361361
}
362362

363+
var lmrt *testing.T
364+
363365
func getblockStub(method string, params []json.RawMessage) (json.RawMessage, error) {
364366
if method != "getblock" {
365367
testT.Fatal("unexpected method:", method)
366368
}
367369
step++
370+
testT.Log("getblockStub new step: ", step)
368371
var arg string
369372
err := json.Unmarshal(params[0], &arg)
370373
if err != nil {
@@ -381,7 +384,7 @@ func getblockStub(method string, params []json.RawMessage) (json.RawMessage, err
381384
return []byte("{\"Tx\": [\"00\"], \"Hash\": \"0000380640\"}"), nil
382385
case 2:
383386
if arg != "0000380640" {
384-
testT.Fatal("unexpected getblock height", arg)
387+
testT.Fatal("unexpected getblock hash", arg)
385388
}
386389
return blocks[0], nil
387390
case 3:
@@ -395,15 +398,18 @@ func TestGetBlock(t *testing.T) {
395398
testT = t
396399
common.RawRequest = getblockStub
397400
lwd, _ := testsetup()
401+
testT.Log("starting TestGetBlock")
398402

399403
_, err := lwd.GetBlock(context.Background(), &walletrpc.BlockID{})
400404
if err == nil {
401405
t.Fatal("GetBlock should have failed")
402406
}
407+
testT.Log("about to TestGetBlock")
403408
_, err = lwd.GetBlock(context.Background(), &walletrpc.BlockID{Height: 0})
404409
if err == nil {
405410
t.Fatal("GetBlock should have failed")
406411
}
412+
testT.Log("about to TestGetBlock")
407413
_, err = lwd.GetBlock(context.Background(), &walletrpc.BlockID{Hash: []byte{0}})
408414
if err == nil {
409415
t.Fatal("GetBlock should have failed")
@@ -413,6 +419,7 @@ func TestGetBlock(t *testing.T) {
413419
}
414420

415421
// getblockStub() case 1: return error
422+
testT.Log("about to TestGetBlock")
416423
block, err := lwd.GetBlock(context.Background(), &walletrpc.BlockID{Height: 380640})
417424
if err != nil {
418425
t.Fatal("GetBlock failed:", err)
@@ -421,13 +428,15 @@ func TestGetBlock(t *testing.T) {
421428
t.Fatal("GetBlock returned unexpected block:", err)
422429
}
423430
// getblockStub() case 2: return error
431+
testT.Log("about to TestGetBlock")
424432
block, err = lwd.GetBlock(context.Background(), &walletrpc.BlockID{Height: 380640})
425433
if err == nil {
426434
t.Fatal("GetBlock should have failed")
427435
}
428436
if block != nil {
429437
t.Fatal("GetBlock returned unexpected non-nil block")
430438
}
439+
testT.Log("end TestGetBlock")
431440
step = 0
432441
}
433442

0 commit comments

Comments
 (0)