mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[NOD-269] Implement GetChainFromBlock api-call (#364)
* [NOD-269] Added a skeleton for getChainFromBlock. * [NOD-269] Made startHash and includeBlocks optional. * [NOD-269] Implemented chainBlock collection. * [NOD-269] Extracted GetBlockVerboseResult building to its own method. * [NOD-269] Implemented the IncludeBlocks part of GetChainFromBlock. * [NOD-269] Added a comment for NewGetChainFromBlockCmd. * [NOD-269] Made IsInSelectedPathChain return an error. * [NOD-269] Fixed a very wrong comment. * [NOD-269] Made SelectedPathChain allocate only the required amount of space. * [NOD-269] Renamed pathChain to parentChain. * [NOD-269] Split handleGetChainFromBlock to separate functions. * [NOD-269] Fixed some grammar.
This commit is contained in:
parent
534cb2bf5b
commit
5f49115cac
@ -1382,9 +1382,9 @@ func (dag *BlockDAG) acceptingBlock(node *blockNode) (*blockNode, error) {
|
||||
}
|
||||
|
||||
// If the node is a chain-block itself, the accepting block is its chain-child
|
||||
if dag.IsInSelectedPathChain(node.hash) {
|
||||
if dag.IsInSelectedParentChain(node.hash) {
|
||||
for _, child := range node.children {
|
||||
if dag.IsInSelectedPathChain(child.hash) {
|
||||
if dag.IsInSelectedParentChain(child.hash) {
|
||||
return child, nil
|
||||
}
|
||||
}
|
||||
@ -1409,21 +1409,70 @@ func (dag *BlockDAG) acceptingBlock(node *blockNode) (*blockNode, error) {
|
||||
// oldestChainBlockWithBlueScoreGreaterThan finds the oldest chain block with a blue score
|
||||
// greater than blueScore. If no such block exists, this method returns nil
|
||||
func (dag *BlockDAG) oldestChainBlockWithBlueScoreGreaterThan(blueScore uint64) *blockNode {
|
||||
chainBlockIndex, ok := util.SearchSlice(len(dag.virtual.selectedPathChainSlice), func(i int) bool {
|
||||
selectedPathNode := dag.virtual.selectedPathChainSlice[i]
|
||||
chainBlockIndex, ok := util.SearchSlice(len(dag.virtual.selectedParentChainSlice), func(i int) bool {
|
||||
selectedPathNode := dag.virtual.selectedParentChainSlice[i]
|
||||
return selectedPathNode.blueScore > blueScore
|
||||
})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return dag.virtual.selectedPathChainSlice[chainBlockIndex]
|
||||
return dag.virtual.selectedParentChainSlice[chainBlockIndex]
|
||||
}
|
||||
|
||||
// IsInSelectedPathChain returns whether or not a block hash is found in the selected path
|
||||
// IsInSelectedParentChain returns whether or not a block hash is found in the selected
|
||||
// parent chain.
|
||||
//
|
||||
// This method MUST be called with the DAG lock held
|
||||
func (dag *BlockDAG) IsInSelectedPathChain(blockHash *daghash.Hash) bool {
|
||||
return dag.virtual.selectedPathChainSet.containsHash(blockHash)
|
||||
func (dag *BlockDAG) IsInSelectedParentChain(blockHash *daghash.Hash) bool {
|
||||
return dag.virtual.selectedParentChainSet.containsHash(blockHash)
|
||||
}
|
||||
|
||||
// SelectedParentChain returns the selected parent chain starting from startHash (exclusive) up
|
||||
// to the virtual (exclusive). If startHash is nil then the genesis block is used.
|
||||
//
|
||||
// This method MUST be called with the DAG lock held
|
||||
func (dag *BlockDAG) SelectedParentChain(startHash *daghash.Hash) ([]*daghash.Hash, error) {
|
||||
if startHash == nil {
|
||||
startHash = dag.genesis.hash
|
||||
}
|
||||
if !dag.IsInSelectedParentChain(startHash) {
|
||||
return nil, fmt.Errorf("startHash %s is not the selected parent chain", startHash)
|
||||
}
|
||||
|
||||
// Find the index of the startHash in the selectedParentChainSlice
|
||||
startHashIndex := len(dag.virtual.selectedParentChainSlice) - 1
|
||||
for startHashIndex >= 0 {
|
||||
node := dag.virtual.selectedParentChainSlice[startHashIndex]
|
||||
if node.hash.IsEqual(startHash) {
|
||||
break
|
||||
}
|
||||
startHashIndex--
|
||||
}
|
||||
|
||||
// Copy all the hashes starting from startHashIndex (exclusive)
|
||||
hashes := make([]*daghash.Hash, len(dag.virtual.selectedParentChainSlice)-startHashIndex)
|
||||
for i, node := range dag.virtual.selectedParentChainSlice[startHashIndex+1:] {
|
||||
hashes[i] = node.hash
|
||||
}
|
||||
|
||||
return hashes, nil
|
||||
}
|
||||
|
||||
// BluesTxsAcceptanceData returns the acceptance data of all the transactions that
|
||||
// were accepted by the block with hash blockHash.
|
||||
func (dag *BlockDAG) BluesTxsAcceptanceData(blockHash *daghash.Hash) (MultiBlockTxsAcceptanceData, error) {
|
||||
node := dag.index.LookupNode(blockHash)
|
||||
if node == nil {
|
||||
err := fmt.Errorf("block %s is not known", blockHash)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, bluesTxsAcceptanceData, err := dag.pastUTXO(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bluesTxsAcceptanceData, nil
|
||||
}
|
||||
|
||||
// ChainHeight return the chain-height of the selected tip. In other words - it returns
|
||||
@ -1486,7 +1535,7 @@ func (dag *BlockDAG) BlockLocatorFromHash(hash *daghash.Hash) BlockLocator {
|
||||
defer dag.dagLock.RUnlock()
|
||||
node := dag.index.LookupNode(hash)
|
||||
if node != nil {
|
||||
for !dag.IsInSelectedPathChain(node.hash) {
|
||||
for !dag.IsInSelectedParentChain(node.hash) {
|
||||
node = node.selectedParent
|
||||
}
|
||||
}
|
||||
|
@ -562,7 +562,7 @@ func dbFetchTxAcceptingBlock(dbTx database.Tx, txID *daghash.TxID, dag *blockdag
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dag.IsInSelectedPathChain(blockHash) {
|
||||
if dag.IsInSelectedParentChain(blockHash) {
|
||||
return blockHash, nil
|
||||
}
|
||||
}
|
||||
|
@ -15,14 +15,14 @@ type virtualBlock struct {
|
||||
utxoSet *FullUTXOSet
|
||||
blockNode
|
||||
|
||||
// selectedPathChainSet is a block set that includes all the blocks
|
||||
// selectedParentChainSet is a block set that includes all the blocks
|
||||
// that belong to the chain of selected parents from the virtual block.
|
||||
selectedPathChainSet blockSet
|
||||
selectedParentChainSet blockSet
|
||||
|
||||
// selectedPathChainSlice is an ordered slice that includes all the
|
||||
// selectedParentChainSlice is an ordered slice that includes all the
|
||||
// blocks that belong the the chain of selected parents from the
|
||||
// virtual block.
|
||||
selectedPathChainSlice []*blockNode
|
||||
selectedParentChainSlice []*blockNode
|
||||
}
|
||||
|
||||
// newVirtualBlock creates and returns a new VirtualBlock.
|
||||
@ -31,8 +31,8 @@ func newVirtualBlock(tips blockSet, phantomK uint32) *virtualBlock {
|
||||
var virtual virtualBlock
|
||||
virtual.phantomK = phantomK
|
||||
virtual.utxoSet = NewFullUTXOSet()
|
||||
virtual.selectedPathChainSet = newSet()
|
||||
virtual.selectedPathChainSlice = nil
|
||||
virtual.selectedParentChainSet = newSet()
|
||||
virtual.selectedParentChainSlice = nil
|
||||
virtual.setTips(tips)
|
||||
|
||||
return &virtual
|
||||
@ -41,10 +41,10 @@ func newVirtualBlock(tips blockSet, phantomK uint32) *virtualBlock {
|
||||
// clone creates and returns a clone of the virtual block.
|
||||
func (v *virtualBlock) clone() *virtualBlock {
|
||||
return &virtualBlock{
|
||||
phantomK: v.phantomK,
|
||||
utxoSet: v.utxoSet,
|
||||
blockNode: v.blockNode,
|
||||
selectedPathChainSet: v.selectedPathChainSet,
|
||||
phantomK: v.phantomK,
|
||||
utxoSet: v.utxoSet,
|
||||
blockNode: v.blockNode,
|
||||
selectedParentChainSet: v.selectedParentChainSet,
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,10 +56,10 @@ func (v *virtualBlock) clone() *virtualBlock {
|
||||
func (v *virtualBlock) setTips(tips blockSet) {
|
||||
oldSelectedParent := v.selectedParent
|
||||
v.blockNode = *newBlockNode(nil, tips, v.phantomK)
|
||||
v.updateSelectedPathSet(oldSelectedParent)
|
||||
v.updateSelectedParentSet(oldSelectedParent)
|
||||
}
|
||||
|
||||
// updateSelectedPathSet updates the selectedPathSet to match the
|
||||
// updateSelectedParentSet updates the selectedParentSet to match the
|
||||
// new selected parent of the virtual block.
|
||||
// Every time the new selected parent is not a child of
|
||||
// the old one, it updates the selected path by removing from
|
||||
@ -67,11 +67,11 @@ func (v *virtualBlock) setTips(tips blockSet) {
|
||||
// parent and are not selected ancestors of the new one, and adding
|
||||
// blocks that are selected ancestors of the new selected parent
|
||||
// and aren't selected ancestors of the old one.
|
||||
func (v *virtualBlock) updateSelectedPathSet(oldSelectedParent *blockNode) {
|
||||
func (v *virtualBlock) updateSelectedParentSet(oldSelectedParent *blockNode) {
|
||||
var intersectionNode *blockNode
|
||||
nodesToAdd := make([]*blockNode, 0)
|
||||
for node := v.blockNode.selectedParent; intersectionNode == nil && node != nil; node = node.selectedParent {
|
||||
if v.selectedPathChainSet.contains(node) {
|
||||
if v.selectedParentChainSet.contains(node) {
|
||||
intersectionNode = node
|
||||
} else {
|
||||
nodesToAdd = append(nodesToAdd, node)
|
||||
@ -79,19 +79,19 @@ func (v *virtualBlock) updateSelectedPathSet(oldSelectedParent *blockNode) {
|
||||
}
|
||||
|
||||
if intersectionNode == nil && oldSelectedParent != nil {
|
||||
panic("updateSelectedPathSet: Cannot find intersection node. The block index may be corrupted.")
|
||||
panic("updateSelectedParentSet: Cannot find intersection node. The block index may be corrupted.")
|
||||
}
|
||||
|
||||
// Remove the nodes in the set from the oldSelectedParent down to the intersectionNode
|
||||
removeCount := 0
|
||||
if intersectionNode != nil {
|
||||
for node := oldSelectedParent; !node.hash.IsEqual(intersectionNode.hash); node = node.selectedParent {
|
||||
v.selectedPathChainSet.remove(node)
|
||||
v.selectedParentChainSet.remove(node)
|
||||
removeCount++
|
||||
}
|
||||
}
|
||||
// Remove the last removeCount nodes from the slice
|
||||
v.selectedPathChainSlice = v.selectedPathChainSlice[:len(v.selectedPathChainSlice)-removeCount]
|
||||
v.selectedParentChainSlice = v.selectedParentChainSlice[:len(v.selectedParentChainSlice)-removeCount]
|
||||
|
||||
// Reverse nodesToAdd, since we collected them in reverse order
|
||||
for left, right := 0, len(nodesToAdd)-1; left < right; left, right = left+1, right-1 {
|
||||
@ -99,9 +99,9 @@ func (v *virtualBlock) updateSelectedPathSet(oldSelectedParent *blockNode) {
|
||||
}
|
||||
// Add the nodes to the set and to the slice
|
||||
for _, node := range nodesToAdd {
|
||||
v.selectedPathChainSet.add(node)
|
||||
v.selectedParentChainSet.add(node)
|
||||
}
|
||||
v.selectedPathChainSlice = append(v.selectedPathChainSlice, nodesToAdd...)
|
||||
v.selectedParentChainSlice = append(v.selectedParentChainSlice, nodesToAdd...)
|
||||
}
|
||||
|
||||
// SetTips replaces the tips of the virtual block with the blocks in the
|
||||
|
@ -123,14 +123,14 @@ func TestSelectedPath(t *testing.T) {
|
||||
virtual.AddTip(tip)
|
||||
}
|
||||
// For now we don't have any DAG, just chain, the selected path should include all the blocks on the chain.
|
||||
if !reflect.DeepEqual(virtual.selectedPathChainSet, firstPath) {
|
||||
if !reflect.DeepEqual(virtual.selectedParentChainSet, firstPath) {
|
||||
t.Fatalf("TestSelectedPath: selectedPathSet doesn't include the expected values. got %v, want %v", virtual.selectedParent, firstPath)
|
||||
}
|
||||
// We expect that selectedPathChainSlice should have all the blocks we've added so far
|
||||
// We expect that selectedParentChainSlice should have all the blocks we've added so far
|
||||
wantLen := 11
|
||||
gotLen := len(virtual.selectedPathChainSlice)
|
||||
gotLen := len(virtual.selectedParentChainSlice)
|
||||
if wantLen != gotLen {
|
||||
t.Fatalf("TestSelectedPath: selectedPathChainSlice doesn't have the expected length. got %d, want %d", gotLen, wantLen)
|
||||
t.Fatalf("TestSelectedPath: selectedParentChainSlice doesn't have the expected length. got %d, want %d", gotLen, wantLen)
|
||||
}
|
||||
|
||||
secondPath := initialPath.clone()
|
||||
@ -141,14 +141,14 @@ func TestSelectedPath(t *testing.T) {
|
||||
virtual.AddTip(tip)
|
||||
}
|
||||
// Because we added a chain that is much longer than the previous chain, the selected path should be re-organized.
|
||||
if !reflect.DeepEqual(virtual.selectedPathChainSet, secondPath) {
|
||||
if !reflect.DeepEqual(virtual.selectedParentChainSet, secondPath) {
|
||||
t.Fatalf("TestSelectedPath: selectedPathSet didn't handle the re-org as expected. got %v, want %v", virtual.selectedParent, firstPath)
|
||||
}
|
||||
// We expect that selectedPathChainSlice should have all the blocks we've added so far except the old chain
|
||||
// We expect that selectedParentChainSlice should have all the blocks we've added so far except the old chain
|
||||
wantLen = 106
|
||||
gotLen = len(virtual.selectedPathChainSlice)
|
||||
gotLen = len(virtual.selectedParentChainSlice)
|
||||
if wantLen != gotLen {
|
||||
t.Fatalf("TestSelectedPath: selectedPathChainSlice doesn't have"+
|
||||
t.Fatalf("TestSelectedPath: selectedParentChainSlice doesn't have"+
|
||||
"the expected length, possibly because it didn't handle the re-org as expected. got %d, want %d", gotLen, wantLen)
|
||||
}
|
||||
|
||||
@ -158,23 +158,23 @@ func TestSelectedPath(t *testing.T) {
|
||||
virtual.AddTip(tip)
|
||||
}
|
||||
// Because we added a very short chain, the selected path should not be affected.
|
||||
if !reflect.DeepEqual(virtual.selectedPathChainSet, secondPath) {
|
||||
if !reflect.DeepEqual(virtual.selectedParentChainSet, secondPath) {
|
||||
t.Fatalf("TestSelectedPath: selectedPathSet did an unexpected re-org. got %v, want %v", virtual.selectedParent, firstPath)
|
||||
}
|
||||
// We expect that selectedPathChainSlice not to change
|
||||
// We expect that selectedParentChainSlice not to change
|
||||
wantLen = 106
|
||||
gotLen = len(virtual.selectedPathChainSlice)
|
||||
gotLen = len(virtual.selectedParentChainSlice)
|
||||
if wantLen != gotLen {
|
||||
t.Fatalf("TestSelectedPath: selectedPathChainSlice doesn't"+
|
||||
t.Fatalf("TestSelectedPath: selectedParentChainSlice doesn't"+
|
||||
"have the expected length, possibly due to unexpected did an unexpected re-org. got %d, want %d", gotLen, wantLen)
|
||||
}
|
||||
|
||||
// We call updateSelectedPathSet manually without updating the tips, to check if it panics
|
||||
// We call updateSelectedParentSet manually without updating the tips, to check if it panics
|
||||
virtual2 := newVirtualBlock(nil, phantomK)
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatalf("updateSelectedPathSet didn't panic")
|
||||
t.Fatalf("updateSelectedParentSet didn't panic")
|
||||
}
|
||||
}()
|
||||
virtual2.updateSelectedPathSet(buildNode(setFromSlice()))
|
||||
virtual2.updateSelectedParentSet(buildNode(setFromSlice()))
|
||||
}
|
||||
|
@ -331,6 +331,21 @@ func NewGetCFilterHeaderCmd(hash string,
|
||||
}
|
||||
}
|
||||
|
||||
// GetChainFromBlockCmd defines the getChainFromBlock JSON-RPC command.
|
||||
type GetChainFromBlockCmd struct {
|
||||
StartHash *string `json:"startHash"`
|
||||
IncludeBlocks *bool `json:"includeBlocks"`
|
||||
}
|
||||
|
||||
// NewGetChainFromBlockCmd returns a new instance which can be used to issue a
|
||||
// GetChainFromBlock JSON-RPC command.
|
||||
func NewGetChainFromBlockCmd(startHash *string, includeBlocks *bool) *GetChainFromBlockCmd {
|
||||
return &GetChainFromBlockCmd{
|
||||
StartHash: startHash,
|
||||
IncludeBlocks: includeBlocks,
|
||||
}
|
||||
}
|
||||
|
||||
// GetDAGTipsCmd defines the getDagTips JSON-RPC command.
|
||||
type GetDAGTipsCmd struct{}
|
||||
|
||||
@ -789,6 +804,7 @@ func init() {
|
||||
MustRegisterCmd("getBlockTemplate", (*GetBlockTemplateCmd)(nil), flags)
|
||||
MustRegisterCmd("getCFilter", (*GetCFilterCmd)(nil), flags)
|
||||
MustRegisterCmd("getCFilterHeader", (*GetCFilterHeaderCmd)(nil), flags)
|
||||
MustRegisterCmd("getChainFromBlock", (*GetChainFromBlockCmd)(nil), flags)
|
||||
MustRegisterCmd("getDagTips", (*GetDAGTipsCmd)(nil), flags)
|
||||
MustRegisterCmd("getConnectionCount", (*GetConnectionCountCmd)(nil), flags)
|
||||
MustRegisterCmd("getDifficulty", (*GetDifficultyCmd)(nil), flags)
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
func TestDAGSvrCmds(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testID := int(1)
|
||||
testID := 1
|
||||
tests := []struct {
|
||||
name string
|
||||
newCmd func() (interface{}, error)
|
||||
@ -352,6 +352,20 @@ func TestDAGSvrCmds(t *testing.T) {
|
||||
FilterType: wire.GCSFilterExtended,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getChainFromBlock",
|
||||
newCmd: func() (interface{}, error) {
|
||||
return btcjson.NewCmd("getChainFromBlock", "123", true)
|
||||
},
|
||||
staticCmd: func() interface{} {
|
||||
return btcjson.NewGetChainFromBlockCmd(btcjson.String("123"), btcjson.Bool(true))
|
||||
},
|
||||
marshalled: `{"jsonrpc":"1.0","method":"getChainFromBlock","params":["123",true],"id":1}`,
|
||||
unmarshalled: &btcjson.GetChainFromBlockCmd{
|
||||
StartHash: btcjson.String("123"),
|
||||
IncludeBlocks: btcjson.Bool(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getDagTips",
|
||||
newCmd: func() (interface{}, error) {
|
||||
|
@ -521,3 +521,9 @@ type AcceptedBlock struct {
|
||||
Hash string `json:"hash"`
|
||||
AcceptedTxIds []string `json:"acceptedTxIds"`
|
||||
}
|
||||
|
||||
// GetChainFromBlockResult models the data from the getChainFromBlock command.
|
||||
type GetChainFromBlockResult struct {
|
||||
SelectedParentChain []ChainBlock `json:"selectedParentChain"`
|
||||
Blocks []GetBlockVerboseResult `json:"blocks"`
|
||||
}
|
||||
|
@ -147,6 +147,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
|
||||
"getBlockTemplate": handleGetBlockTemplate,
|
||||
"getCFilter": handleGetCFilter,
|
||||
"getCFilterHeader": handleGetCFilterHeader,
|
||||
"getChainFromBlock": handleGetChainFromBlock,
|
||||
"getConnectionCount": handleGetConnectionCount,
|
||||
"getCurrentNet": handleGetCurrentNet,
|
||||
"getDifficulty": handleGetDifficulty,
|
||||
@ -219,6 +220,7 @@ var rpcLimited = map[string]struct{}{
|
||||
"getBlockHeader": {},
|
||||
"getCFilter": {},
|
||||
"getCFilterHeader": {},
|
||||
"getChainFromBlock": {},
|
||||
"getCurrentNet": {},
|
||||
"getDifficulty": {},
|
||||
"getHeaders": {},
|
||||
@ -1087,6 +1089,18 @@ func handleGetBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
|
||||
return nil, internalRPCError(err.Error(), context)
|
||||
}
|
||||
|
||||
blockReply, err := buildGetBlockVerboseResult(s, blk, c.VerboseTx == nil || !*c.VerboseTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return blockReply, nil
|
||||
}
|
||||
|
||||
func buildGetBlockVerboseResult(s *Server, block *util.Block, isVerboseTx bool) (*btcjson.GetBlockVerboseResult, error) {
|
||||
hash := block.Hash()
|
||||
params := s.cfg.DAGParams
|
||||
blockHeader := block.MsgBlock().Header
|
||||
|
||||
// Get the block chain height.
|
||||
blockChainHeight, err := s.cfg.DAG.BlockChainHeightByHash(hash)
|
||||
if err != nil {
|
||||
@ -1111,10 +1125,8 @@ func handleGetBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
|
||||
return nil, internalRPCError(err.Error(), context)
|
||||
}
|
||||
|
||||
params := s.cfg.DAGParams
|
||||
blockHeader := &blk.MsgBlock().Header
|
||||
blockReply := btcjson.GetBlockVerboseResult{
|
||||
Hash: c.Hash,
|
||||
result := &btcjson.GetBlockVerboseResult{
|
||||
Hash: hash.String(),
|
||||
Version: blockHeader.Version,
|
||||
VersionHex: fmt.Sprintf("%08x", blockHeader.Version),
|
||||
HashMerkleRoot: blockHeader.HashMerkleRoot.String(),
|
||||
@ -1124,22 +1136,22 @@ func handleGetBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
|
||||
Time: blockHeader.Timestamp.Unix(),
|
||||
Confirmations: blockConfirmations,
|
||||
Height: blockChainHeight,
|
||||
Size: int32(len(blkBytes)),
|
||||
Size: int32(block.MsgBlock().SerializeSize()),
|
||||
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
|
||||
Difficulty: getDifficultyRatio(blockHeader.Bits, params),
|
||||
NextHashes: nextHashStrings,
|
||||
}
|
||||
|
||||
if c.VerboseTx == nil || !*c.VerboseTx {
|
||||
transactions := blk.Transactions()
|
||||
if isVerboseTx {
|
||||
transactions := block.Transactions()
|
||||
txNames := make([]string, len(transactions))
|
||||
for i, tx := range transactions {
|
||||
txNames[i] = tx.ID().String()
|
||||
}
|
||||
|
||||
blockReply.Tx = txNames
|
||||
result.Tx = txNames
|
||||
} else {
|
||||
txns := blk.Transactions()
|
||||
txns := block.Transactions()
|
||||
rawTxns := make([]btcjson.TxRawResult, len(txns))
|
||||
for i, tx := range txns {
|
||||
var acceptingBlock *daghash.Hash
|
||||
@ -1156,16 +1168,16 @@ func handleGetBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
|
||||
confirmations = &txConfirmations
|
||||
}
|
||||
rawTxn, err := createTxRawResult(params, tx.MsgTx(), tx.ID().String(),
|
||||
blockHeader, hash.String(), acceptingBlock, confirmations, false)
|
||||
&blockHeader, hash.String(), acceptingBlock, confirmations, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawTxns[i] = *rawTxn
|
||||
}
|
||||
blockReply.RawTx = rawTxns
|
||||
result.RawTx = rawTxns
|
||||
}
|
||||
|
||||
return blockReply, nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// softForkStatus converts a ThresholdState state into a human readable string
|
||||
@ -2190,6 +2202,118 @@ func handleGetCFilterHeader(s *Server, cmd interface{}, closeChan <-chan struct{
|
||||
return hash.String(), nil
|
||||
}
|
||||
|
||||
// handleGetChainFromBlock implements the getChainFromBlock command.
|
||||
func handleGetChainFromBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||
c := cmd.(*btcjson.GetChainFromBlockCmd)
|
||||
var startHash *daghash.Hash
|
||||
if c.StartHash != nil {
|
||||
err := daghash.Decode(startHash, *c.StartHash)
|
||||
if err != nil {
|
||||
return nil, rpcDecodeHexError(*c.StartHash)
|
||||
}
|
||||
}
|
||||
|
||||
s.cfg.DAG.RLock()
|
||||
defer s.cfg.DAG.RUnlock()
|
||||
|
||||
// If startHash is not in the selected parent chain, there's nothing
|
||||
// to do; return an error.
|
||||
if !s.cfg.DAG.IsInSelectedParentChain(startHash) {
|
||||
return nil, &btcjson.RPCError{
|
||||
Code: btcjson.ErrRPCBlockNotFound,
|
||||
Message: "Block not found in selected parent chain",
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the selected parent chain.
|
||||
selectedParentChain, err := s.cfg.DAG.SelectedParentChain(startHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Collect chainBlocks.
|
||||
chainBlocks, err := collectChainBlocks(s, selectedParentChain)
|
||||
if err != nil {
|
||||
return nil, &btcjson.RPCError{
|
||||
Code: btcjson.ErrRPCInternal.Code,
|
||||
Message: fmt.Sprintf("could not collect chain blocks: %s", err),
|
||||
}
|
||||
}
|
||||
|
||||
result := &btcjson.GetChainFromBlockResult{
|
||||
SelectedParentChain: chainBlocks,
|
||||
Blocks: nil,
|
||||
}
|
||||
|
||||
// If the user specified to include the blocks, collect them as well.
|
||||
if c.IncludeBlocks != nil && *c.IncludeBlocks {
|
||||
getBlockVerboseResults, err := hashesToGetBlockVerboseResults(s, selectedParentChain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Blocks = getBlockVerboseResults
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func collectChainBlocks(s *Server, selectedParentChain []*daghash.Hash) ([]btcjson.ChainBlock, error) {
|
||||
chainBlocks := make([]btcjson.ChainBlock, 0, len(selectedParentChain))
|
||||
for _, hash := range selectedParentChain {
|
||||
acceptanceData, err := s.cfg.DAG.BluesTxsAcceptanceData(hash)
|
||||
if err != nil {
|
||||
return nil, &btcjson.RPCError{
|
||||
Code: btcjson.ErrRPCInternal.Code,
|
||||
Message: fmt.Sprintf("could not retrieve acceptance data for block %s", hash),
|
||||
}
|
||||
}
|
||||
|
||||
acceptedBlocks := make([]btcjson.AcceptedBlock, 0, len(acceptanceData))
|
||||
for blockHash, blockAcceptanceData := range acceptanceData {
|
||||
acceptedTxIds := make([]string, 0, len(blockAcceptanceData))
|
||||
for _, txAcceptanceData := range blockAcceptanceData {
|
||||
if txAcceptanceData.IsAccepted {
|
||||
acceptedTxIds = append(acceptedTxIds, txAcceptanceData.Tx.Hash().String())
|
||||
}
|
||||
}
|
||||
acceptedBlock := btcjson.AcceptedBlock{
|
||||
Hash: blockHash.String(),
|
||||
AcceptedTxIds: acceptedTxIds,
|
||||
}
|
||||
acceptedBlocks = append(acceptedBlocks, acceptedBlock)
|
||||
}
|
||||
|
||||
chainBlock := btcjson.ChainBlock{
|
||||
Hash: hash.String(),
|
||||
AcceptedBlocks: acceptedBlocks,
|
||||
}
|
||||
chainBlocks = append(chainBlocks, chainBlock)
|
||||
}
|
||||
return chainBlocks, nil
|
||||
}
|
||||
|
||||
func hashesToGetBlockVerboseResults(s *Server, hashes []*daghash.Hash) ([]btcjson.GetBlockVerboseResult, error) {
|
||||
getBlockVerboseResults := make([]btcjson.GetBlockVerboseResult, 0, len(hashes))
|
||||
for _, blockHash := range hashes {
|
||||
block, err := s.cfg.DAG.BlockByHash(blockHash)
|
||||
if err != nil {
|
||||
return nil, &btcjson.RPCError{
|
||||
Code: btcjson.ErrRPCInternal.Code,
|
||||
Message: fmt.Sprintf("could not retrieve block %s.", blockHash),
|
||||
}
|
||||
}
|
||||
getBlockVerboseResult, err := buildGetBlockVerboseResult(s, block, false)
|
||||
if err != nil {
|
||||
return nil, &btcjson.RPCError{
|
||||
Code: btcjson.ErrRPCInternal.Code,
|
||||
Message: fmt.Sprintf("could not build getBlockVerboseResult for block %s.", blockHash),
|
||||
}
|
||||
}
|
||||
getBlockVerboseResults = append(getBlockVerboseResults, *getBlockVerboseResult)
|
||||
}
|
||||
return getBlockVerboseResults, nil
|
||||
}
|
||||
|
||||
// handleGetConnectionCount implements the getConnectionCount command.
|
||||
func handleGetConnectionCount(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||
return s.cfg.ConnMgr.ConnectedCount(), nil
|
||||
|
@ -90,6 +90,14 @@ var helpDescsEnUS = map[string]string{
|
||||
"vout-n": "The index of this transaction output",
|
||||
"vout-scriptPubKey": "The public key script used to pay coins as a JSON object",
|
||||
|
||||
// ChainBlock help.
|
||||
"chainBlock-hash": "The hash of the chain block",
|
||||
"chainBlock-acceptedBlocks": "The blocks accepted by this chain block",
|
||||
|
||||
// AcceptedBlock help.
|
||||
"acceptedBlock-hash": "The hash of the accepted block",
|
||||
"acceptedBlock-acceptedTxIds": "The transactions in this block accepted by the chain block",
|
||||
|
||||
// TxRawDecodeResult help.
|
||||
"txRawDecodeResult-txId": "The hash of the transaction",
|
||||
"txRawDecodeResult-version": "The transaction version",
|
||||
@ -342,6 +350,16 @@ var helpDescsEnUS = map[string]string{
|
||||
"getCFilter-hash": "The hash of the block",
|
||||
"getCFilter--result0": "The block's committed filter",
|
||||
|
||||
// GetChainFromBlockCmd help.
|
||||
"getChainFromBlock--synopsis": "Return the selected parent chain starting from startHash up to the virtual.",
|
||||
"getChainFromBlock-startHash": "Hash of the bottom of the requested chain. If this hash is unknown or is not a chain block - returns an error.",
|
||||
"getChainFromBlock-includeBlocks": "If set to true - the block contents would be also included.",
|
||||
"getChainFromBlock--result0": "The selected parent chain.",
|
||||
|
||||
// GetChainFromBlockResult help.
|
||||
"getChainFromBlockResult-selectedParentChain": "List of ChainBlocks from Virtual.SelectedTip to StartHash (excluding StartHash) ordered bottom-to-top.",
|
||||
"getChainFromBlockResult-blocks": "If includeBlocks=true - contains the contents of all chain and accepted blocks in the SelectedParentChain. Otherwise - omitted.",
|
||||
|
||||
// GetCFilterHeaderCmd help.
|
||||
"getCFilterHeader--synopsis": "Returns a block's compact filter header given its hash.",
|
||||
"getCFilterHeader-filterType": "The type of filter header to return (0=regular, 1=extended)",
|
||||
@ -662,6 +680,7 @@ var rpcResultTypes = map[string][]interface{}{
|
||||
"getBlockDagInfo": {(*btcjson.GetBlockDAGInfoResult)(nil)},
|
||||
"getCFilter": {(*string)(nil)},
|
||||
"getCFilterHeader": {(*string)(nil)},
|
||||
"getChainFromBlock": {(*btcjson.GetChainFromBlockResult)(nil)},
|
||||
"getConnectionCount": {(*int32)(nil)},
|
||||
"getCurrentNet": {(*uint32)(nil)},
|
||||
"getDifficulty": {(*float64)(nil)},
|
||||
|
Loading…
x
Reference in New Issue
Block a user