diff --git a/apiserver/sync.go b/apiserver/sync.go index 059a5e149..3c98736a7 100644 --- a/apiserver/sync.go +++ b/apiserver/sync.go @@ -107,10 +107,10 @@ func syncBlocks(client *jsonrpc.Client) error { return err } - var blocks []string - var rawBlocks []btcjson.GetBlockVerboseResult + var rawBlocks []string + var verboseBlocks []btcjson.GetBlockVerboseResult for { - blocksResult, err := client.GetBlocks(true, false, startHash) + blocksResult, err := client.GetBlocks(true, true, startHash) if err != nil { return err } @@ -118,17 +118,12 @@ func syncBlocks(client *jsonrpc.Client) error { break } - rawBlocksResult, err := client.GetBlocks(true, true, startHash) - if err != nil { - return err - } - startHash = &blocksResult.Hashes[len(blocksResult.Hashes)-1] - blocks = append(blocks, blocksResult.Blocks...) - rawBlocks = append(rawBlocks, rawBlocksResult.RawBlocks...) + rawBlocks = append(rawBlocks, blocksResult.RawBlocks...) + verboseBlocks = append(verboseBlocks, blocksResult.VerboseBlocks...) } - return addBlocks(client, blocks, rawBlocks) + return addBlocks(client, rawBlocks, verboseBlocks) } // syncSelectedParentChain attempts to download the selected parent @@ -191,7 +186,7 @@ func findHashOfBluestBlock(mustBeChainBlock bool) (*string, error) { // fetchBlock downloads the serialized block and raw block data of // the block with hash blockHash. func fetchBlock(client *jsonrpc.Client, blockHash *daghash.Hash) ( - block string, rawBlock *btcjson.GetBlockVerboseResult, err error) { + rawBlock string, verboseBlock *btcjson.GetBlockVerboseResult, err error) { msgBlock, err := client.GetBlock(blockHash, nil) if err != nil { return "", nil, err @@ -201,21 +196,21 @@ func fetchBlock(client *jsonrpc.Client, blockHash *daghash.Hash) ( if err != nil { return "", nil, err } - block = hex.EncodeToString(writer.Bytes()) + rawBlock = hex.EncodeToString(writer.Bytes()) - rawBlock, err = client.GetBlockVerboseTx(blockHash, nil) + verboseBlock, err = client.GetBlockVerboseTx(blockHash, nil) if err != nil { return "", nil, err } - return block, rawBlock, nil + return rawBlock, verboseBlock, nil } -// addBlocks inserts data in the given blocks and rawBlocks pairwise +// addBlocks inserts data in the given rawBlocks and verboseBlocks pairwise // into the database. See addBlock for further details. -func addBlocks(client *jsonrpc.Client, blocks []string, rawBlocks []btcjson.GetBlockVerboseResult) error { +func addBlocks(client *jsonrpc.Client, rawBlocks []string, verboseBlocks []btcjson.GetBlockVerboseResult) error { for i, rawBlock := range rawBlocks { - block := blocks[i] - err := addBlock(client, block, rawBlock) + verboseBlock := verboseBlocks[i] + err := addBlock(client, rawBlock, verboseBlock) if err != nil { return err } @@ -235,12 +230,12 @@ func doesBlockExist(dbTx *gorm.DB, blockHash string) (bool, error) { return !httpserverutils.IsDBRecordNotFoundError(dbErrors), nil } -// addBlocks inserts all the data that could be gleaned out of the serialized +// addBlocks inserts all the data that could be gleaned out of the verbose // block and raw block data into the database. This includes transactions, // subnetworks, and addresses. // Note that if this function may take a nil dbTx, in which case it would start // a database transaction by itself and commit it before returning. -func addBlock(client *jsonrpc.Client, block string, rawBlock btcjson.GetBlockVerboseResult) error { +func addBlock(client *jsonrpc.Client, rawBlock string, verboseBlock btcjson.GetBlockVerboseResult) error { db, err := database.DB() if err != nil { return err @@ -249,7 +244,7 @@ func addBlock(client *jsonrpc.Client, block string, rawBlock btcjson.GetBlockVer defer dbTx.RollbackUnlessCommitted() // Skip this block if it already exists. - blockExists, err := doesBlockExist(dbTx, rawBlock.Hash) + blockExists, err := doesBlockExist(dbTx, verboseBlock.Hash) if err != nil { return err } @@ -258,21 +253,21 @@ func addBlock(client *jsonrpc.Client, block string, rawBlock btcjson.GetBlockVer return nil } - dbBlock, err := insertBlock(dbTx, rawBlock) + dbBlock, err := insertBlock(dbTx, verboseBlock) if err != nil { return err } - err = insertBlockParents(dbTx, rawBlock, dbBlock) + err = insertBlockParents(dbTx, verboseBlock, dbBlock) if err != nil { return err } - err = insertBlockData(dbTx, block, dbBlock) + err = insertRawBlockData(dbTx, rawBlock, dbBlock) if err != nil { return err } blockMass := uint64(0) - for i, transaction := range rawBlock.RawTx { + for i, transaction := range verboseBlock.RawTx { dbSubnetwork, err := insertSubnetwork(dbTx, &transaction, client) if err != nil { return err @@ -307,32 +302,32 @@ func addBlock(client *jsonrpc.Client, block string, rawBlock btcjson.GetBlockVer // If the block was previously missing, remove it from // the missing blocks collection. - if _, ok := missingBlocks[rawBlock.Hash]; ok { - delete(missingBlocks, rawBlock.Hash) + if _, ok := missingBlocks[verboseBlock.Hash]; ok { + delete(missingBlocks, verboseBlock.Hash) } return nil } -func insertBlock(dbTx *gorm.DB, rawBlock btcjson.GetBlockVerboseResult) (*dbmodels.Block, error) { - bits, err := strconv.ParseUint(rawBlock.Bits, 16, 32) +func insertBlock(dbTx *gorm.DB, verboseBlock btcjson.GetBlockVerboseResult) (*dbmodels.Block, error) { + bits, err := strconv.ParseUint(verboseBlock.Bits, 16, 32) if err != nil { return nil, err } dbBlock := dbmodels.Block{ - BlockHash: rawBlock.Hash, - Version: rawBlock.Version, - HashMerkleRoot: rawBlock.HashMerkleRoot, - AcceptedIDMerkleRoot: rawBlock.AcceptedIDMerkleRoot, - UTXOCommitment: rawBlock.UTXOCommitment, - Timestamp: time.Unix(rawBlock.Time, 0), + BlockHash: verboseBlock.Hash, + Version: verboseBlock.Version, + HashMerkleRoot: verboseBlock.HashMerkleRoot, + AcceptedIDMerkleRoot: verboseBlock.AcceptedIDMerkleRoot, + UTXOCommitment: verboseBlock.UTXOCommitment, + Timestamp: time.Unix(verboseBlock.Time, 0), Bits: uint32(bits), - Nonce: rawBlock.Nonce, - BlueScore: rawBlock.BlueScore, + Nonce: verboseBlock.Nonce, + BlueScore: verboseBlock.BlueScore, IsChainBlock: false, // This must be false for updateSelectedParentChain to work properly } // Set genesis block as the initial chain block - if len(rawBlock.ParentHashes) == 0 { + if len(verboseBlock.ParentHashes) == 0 { dbBlock.IsChainBlock = true } dbResult := dbTx.Create(&dbBlock) @@ -343,14 +338,14 @@ func insertBlock(dbTx *gorm.DB, rawBlock btcjson.GetBlockVerboseResult) (*dbmode return &dbBlock, nil } -func insertBlockParents(dbTx *gorm.DB, rawBlock btcjson.GetBlockVerboseResult, dbBlock *dbmodels.Block) error { +func insertBlockParents(dbTx *gorm.DB, verboseBlock btcjson.GetBlockVerboseResult, dbBlock *dbmodels.Block) error { // Exit early if this is the genesis block - if len(rawBlock.ParentHashes) == 0 { + if len(verboseBlock.ParentHashes) == 0 { return nil } - hashesIn := make([]string, len(rawBlock.ParentHashes)) - for i, parentHash := range rawBlock.ParentHashes { + hashesIn := make([]string, len(verboseBlock.ParentHashes)) + for i, parentHash := range verboseBlock.ParentHashes { hashesIn[i] = parentHash } var dbParents []dbmodels.Block @@ -361,10 +356,10 @@ func insertBlockParents(dbTx *gorm.DB, rawBlock btcjson.GetBlockVerboseResult, d if httpserverutils.HasDBError(dbErrors) { return httpserverutils.NewErrorFromDBErrors("failed to find blocks: ", dbErrors) } - if len(dbParents) != len(rawBlock.ParentHashes) { - missingParents := make([]string, 0, len(rawBlock.ParentHashes)-len(dbParents)) + if len(dbParents) != len(verboseBlock.ParentHashes) { + missingParents := make([]string, 0, len(verboseBlock.ParentHashes)-len(dbParents)) outerLoop: - for _, parentHash := range rawBlock.ParentHashes { + for _, parentHash := range verboseBlock.ParentHashes { for _, dbParent := range dbParents { if dbParent.BlockHash == parentHash { continue outerLoop @@ -372,7 +367,7 @@ func insertBlockParents(dbTx *gorm.DB, rawBlock btcjson.GetBlockVerboseResult, d } missingParents = append(missingParents, parentHash) } - return errors.Errorf("some parents are missing for block %s: %s", rawBlock.Hash, strings.Join(missingParents, ", ")) + return errors.Errorf("some parents are missing for block %s: %s", verboseBlock.Hash, strings.Join(missingParents, ", ")) } for _, dbParent := range dbParents { @@ -389,8 +384,8 @@ func insertBlockParents(dbTx *gorm.DB, rawBlock btcjson.GetBlockVerboseResult, d return nil } -func insertBlockData(dbTx *gorm.DB, block string, dbBlock *dbmodels.Block) error { - blockData, err := hex.DecodeString(block) +func insertRawBlockData(dbTx *gorm.DB, rawBlock string, dbBlock *dbmodels.Block) error { + blockData, err := hex.DecodeString(rawBlock) if err != nil { return err } @@ -950,12 +945,12 @@ func processBlockAddedMsgs(client *jsonrpc.Client) { hash := blockAdded.Header.BlockHash() log.Debugf("Getting block %s from the RPC server", hash) - block, rawBlock, err := fetchBlock(client, hash) + rawBlock, verboseBlock, err := fetchBlock(client, hash) if err != nil { log.Warnf("Could not fetch block %s: %s", hash, err) return } - err = addBlock(client, block, *rawBlock) + err = addBlock(client, rawBlock, *verboseBlock) if err != nil { log.Errorf("Could not insert block %s: %s", hash, err) return @@ -1023,11 +1018,11 @@ func handleMissingParent(client *jsonrpc.Client, missingParentHash string) error if err != nil { return errors.Errorf("Could not create hash: %s", err) } - block, rawBlock, err := fetchBlock(client, hash) + rawBlock, verboseBlock, err := fetchBlock(client, hash) if err != nil { return errors.Errorf("Could not fetch block %s: %s", hash, err) } - err = addBlock(client, block, *rawBlock) + err = addBlock(client, rawBlock, *verboseBlock) if err != nil { return errors.Errorf("Could not insert block %s: %s", hash, err) } diff --git a/btcjson/dagsvrcmds.go b/btcjson/dagsvrcmds.go index 90ea81d38..42ad11249 100644 --- a/btcjson/dagsvrcmds.go +++ b/btcjson/dagsvrcmds.go @@ -157,18 +157,18 @@ func NewGetBlockCmd(hash string, verbose, verboseTx *bool, subnetworkID *string) // GetBlocksCmd defines the getBlocks JSON-RPC command. type GetBlocksCmd struct { - IncludeBlocks bool `json:"includeBlocks"` - VerboseBlocks bool `json:"verboseBlocks"` - StartHash *string `json:"startHash"` + IncludeRawBlockData bool `json:"includeRawBlockData"` + IncludeVerboseBlockData bool `json:"includeVerboseBlockData"` + StartHash *string `json:"startHash"` } // NewGetBlocksCmd returns a new instance which can be used to issue a // GetGetBlocks JSON-RPC command. -func NewGetBlocksCmd(includeBlocks bool, verboseBlocks bool, startHash *string) *GetBlocksCmd { +func NewGetBlocksCmd(includeRawBlockData bool, includeVerboseBlockData bool, startHash *string) *GetBlocksCmd { return &GetBlocksCmd{ - IncludeBlocks: includeBlocks, - VerboseBlocks: verboseBlocks, - StartHash: startHash, + IncludeRawBlockData: includeRawBlockData, + IncludeVerboseBlockData: includeVerboseBlockData, + StartHash: startHash, } } diff --git a/btcjson/dagsvrcmds_test.go b/btcjson/dagsvrcmds_test.go index 206644070..e1cedcd40 100644 --- a/btcjson/dagsvrcmds_test.go +++ b/btcjson/dagsvrcmds_test.go @@ -200,9 +200,9 @@ func TestDAGSvrCmds(t *testing.T) { }, marshalled: `{"jsonrpc":"1.0","method":"getBlocks","params":[true,true,"123"],"id":1}`, unmarshalled: &btcjson.GetBlocksCmd{ - IncludeBlocks: true, - VerboseBlocks: true, - StartHash: btcjson.String("123"), + IncludeRawBlockData: true, + IncludeVerboseBlockData: true, + StartHash: btcjson.String("123"), }, }, { diff --git a/btcjson/dagsvrresults.go b/btcjson/dagsvrresults.go index f3456cc22..8a437e350 100644 --- a/btcjson/dagsvrresults.go +++ b/btcjson/dagsvrresults.go @@ -507,7 +507,7 @@ type GetChainFromBlockResult struct { // GetBlocksResult models the data from the getBlocks command. type GetBlocksResult struct { - Hashes []string `json:"hashes"` - Blocks []string `json:"blocks"` - RawBlocks []GetBlockVerboseResult `json:"rawBlocks"` + Hashes []string `json:"hashes"` + RawBlocks []string `json:"rawBlocks"` + VerboseBlocks []GetBlockVerboseResult `json:"verboseBlocks"` } diff --git a/rpcclient/dag.go b/rpcclient/dag.go index 4b30791cd..831d14b01 100644 --- a/rpcclient/dag.go +++ b/rpcclient/dag.go @@ -133,15 +133,15 @@ func (r FutureGetBlocksResult) Receive() (*btcjson.GetBlocksResult, error) { // returned instance. // // See GetBlocks for the blocking version and more details. -func (c *Client) GetBlocksAsync(includeBlocks bool, verboseBlocks bool, startHash *string) FutureGetBlocksResult { - cmd := btcjson.NewGetBlocksCmd(includeBlocks, verboseBlocks, startHash) +func (c *Client) GetBlocksAsync(includeRawBlockData bool, IncludeVerboseBlockData bool, startHash *string) FutureGetBlocksResult { + cmd := btcjson.NewGetBlocksCmd(includeRawBlockData, IncludeVerboseBlockData, startHash) return c.sendCmd(cmd) } // GetBlocks returns the blocks starting from startHash up to the virtual ordered // by blue score. -func (c *Client) GetBlocks(includeBlocks bool, verboseBlocks bool, startHash *string) (*btcjson.GetBlocksResult, error) { - return c.GetBlocksAsync(includeBlocks, verboseBlocks, startHash).Receive() +func (c *Client) GetBlocks(includeRawBlockData bool, includeVerboseBlockData bool, startHash *string) (*btcjson.GetBlocksResult, error) { + return c.GetBlocksAsync(includeRawBlockData, includeVerboseBlockData, startHash).Receive() } // FutureGetBlockVerboseResult is a future promise to deliver the result of a diff --git a/server/rpc/handle_get_blocks.go b/server/rpc/handle_get_blocks.go index 14fa156a4..36fd5cdce 100644 --- a/server/rpc/handle_get_blocks.go +++ b/server/rpc/handle_get_blocks.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "github.com/daglabs/btcd/btcjson" "github.com/daglabs/btcd/database" + "github.com/daglabs/btcd/util" "github.com/daglabs/btcd/util/daghash" ) @@ -48,39 +49,41 @@ func handleGetBlocks(s *Server, cmd interface{}, closeChan <-chan struct{}) (int } result := &btcjson.GetBlocksResult{ - Hashes: hashes, - Blocks: nil, + Hashes: hashes, + RawBlocks: nil, + VerboseBlocks: nil, } - // If the user specified to include the blocks, collect them as well. - if c.IncludeBlocks { - if c.VerboseBlocks { - getBlockVerboseResults, err := hashesToGetBlockVerboseResults(s, blockHashes) + // Include more data if requested + if c.IncludeRawBlockData || c.IncludeVerboseBlockData { + blockBytesSlice, err := hashesToBlockBytes(s, blockHashes) + if err != nil { + return nil, err + } + if c.IncludeRawBlockData { + result.RawBlocks = blockBytesToStrings(blockBytesSlice) + } + if c.IncludeVerboseBlockData { + verboseBlocks, err := blockBytesToBlockVerboseResults(s, blockBytesSlice) if err != nil { return nil, err } - result.RawBlocks = getBlockVerboseResults - } else { - blocks, err := hashesToBlockStrings(s, blockHashes) - if err != nil { - return nil, err - } - result.Blocks = blocks + result.VerboseBlocks = verboseBlocks } } return result, nil } -func hashesToBlockStrings(s *Server, hashes []*daghash.Hash) ([]string, error) { - blocks := make([]string, len(hashes)) +func hashesToBlockBytes(s *Server, hashes []*daghash.Hash) ([][]byte, error) { + blocks := make([][]byte, len(hashes)) err := s.cfg.DB.View(func(dbTx database.Tx) error { for i, hash := range hashes { blockBytes, err := dbTx.FetchBlock(hash) if err != nil { return err } - blocks[i] = hex.EncodeToString(blockBytes) + blocks[i] = blockBytes } return nil }) @@ -89,3 +92,27 @@ func hashesToBlockStrings(s *Server, hashes []*daghash.Hash) ([]string, error) { } return blocks, nil } + +func blockBytesToStrings(blockBytesSlice [][]byte) []string { + rawBlocks := make([]string, len(blockBytesSlice)) + for i, blockBytes := range blockBytesSlice { + rawBlocks[i] = hex.EncodeToString(blockBytes) + } + return rawBlocks +} + +func blockBytesToBlockVerboseResults(s *Server, blockBytesSlice [][]byte) ([]btcjson.GetBlockVerboseResult, error) { + verboseBlocks := make([]btcjson.GetBlockVerboseResult, len(blockBytesSlice)) + for i, blockBytes := range blockBytesSlice { + block, err := util.NewBlockFromBytes(blockBytes) + if err != nil { + return nil, err + } + getBlockVerboseResult, err := buildGetBlockVerboseResult(s, block, false) + if err != nil { + return nil, err + } + verboseBlocks[i] = *getBlockVerboseResult + } + return verboseBlocks, nil +} diff --git a/server/rpc/rpcserverhelp.go b/server/rpc/rpcserverhelp.go index 5db0c9c72..c2fbac2b1 100644 --- a/server/rpc/rpcserverhelp.go +++ b/server/rpc/rpcserverhelp.go @@ -178,16 +178,16 @@ var helpDescsEnUS = map[string]string{ "getBlock--result0": "Hex-encoded bytes of the serialized block", // GetBlocksCmd help. - "getBlocks--synopsis": "Return the blocks starting from startHash up to the virtual ordered by blue score.", - "getBlocks-includeBlocks": "If set to true - the block contents would be also included.", - "getBlocks-verboseBlocks": "If set to true - each block is returned as a JSON object", - "getBlocks-startHash": "Hash of the block with the bottom blue score. If this hash is unknown - returns an error.", - "getBlocks--result0": "Blocks starting from startHash. The result may contains up to 1000 blocks. For the remainder, call the command again with the bluest block's hash.", + "getBlocks--synopsis": "Return the blocks starting from startHash up to the virtual ordered by blue score.", + "getBlocks-includeRawBlockData": "If set to true - the raw block data would be also included.", + "getBlocks-includeVerboseBlockData": "If set to true - the verbose block data would also be included.", + "getBlocks-startHash": "Hash of the block with the bottom blue score. If this hash is unknown - returns an error.", + "getBlocks--result0": "Blocks starting from startHash. The result may contains up to 1000 blocks. For the remainder, call the command again with the bluest block's hash.", // GetChainFromBlockResult help. - "getBlocksResult-hashes": "List of hashes from StartHash (excluding StartHash) ordered by smallest blue score to greatest.", - "getBlocksResult-blocks": "If includeBlocks=true - contains the block contents. Otherwise - omitted.", - "getBlocksResult-rawBlocks": "If includeBlocks=true and verboseBlocks=true - each block is returned as a JSON object. Otherwise - hex encoded string.", + "getBlocksResult-hashes": "List of hashes from StartHash (excluding StartHash) ordered by smallest blue score to greatest.", + "getBlocksResult-rawBlocks": "If includeBlocks=true - contains the block contents. Otherwise - omitted.", + "getBlocksResult-verboseBlocks": "If includeBlocks=true and verboseBlocks=true - each block is returned as a JSON object. Otherwise - hex encoded string.", // GetBlockChainInfoCmd help. "getBlockDagInfo--synopsis": "Returns information about the current blockDAG state and the status of any active soft-fork deployments.",