mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-14 00:59:33 +00:00
Merge remote-tracking branch 'origin/dev-20-primitive-blockdag' into dev-31-20-tmp-branch
This commit is contained in:
commit
2c29cb2f20
@ -20,7 +20,7 @@ type GetBlockHeaderVerboseResult struct {
|
|||||||
Nonce uint64 `json:"nonce"`
|
Nonce uint64 `json:"nonce"`
|
||||||
Bits string `json:"bits"`
|
Bits string `json:"bits"`
|
||||||
Difficulty float64 `json:"difficulty"`
|
Difficulty float64 `json:"difficulty"`
|
||||||
PreviousHash string `json:"previousblockhash,omitempty"`
|
PreviousHashes []string `json:"previousblockhashes,omitempty"`
|
||||||
NextHash string `json:"nextblockhash,omitempty"`
|
NextHash string `json:"nextblockhash,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ type GetBlockVerboseResult struct {
|
|||||||
Nonce uint32 `json:"nonce"`
|
Nonce uint32 `json:"nonce"`
|
||||||
Bits string `json:"bits"`
|
Bits string `json:"bits"`
|
||||||
Difficulty float64 `json:"difficulty"`
|
Difficulty float64 `json:"difficulty"`
|
||||||
PreviousHash string `json:"previousblockhash"`
|
PreviousHashes []string `json:"previousblockhashes"`
|
||||||
NextHash string `json:"nextblockhash,omitempty"`
|
NextHash string `json:"nextblockhash,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ type GetBlockTemplateResult struct {
|
|||||||
Bits string `json:"bits"`
|
Bits string `json:"bits"`
|
||||||
CurTime int64 `json:"curtime"`
|
CurTime int64 `json:"curtime"`
|
||||||
Height int64 `json:"height"`
|
Height int64 `json:"height"`
|
||||||
PreviousHash string `json:"previousblockhash"`
|
PreviousHashes []string `json:"previousblockhashes"`
|
||||||
SigOpLimit int64 `json:"sigoplimit,omitempty"`
|
SigOpLimit int64 `json:"sigoplimit,omitempty"`
|
||||||
SizeLimit int64 `json:"sizelimit,omitempty"`
|
SizeLimit int64 `json:"sizelimit,omitempty"`
|
||||||
Transactions []GetBlockTemplateResultTx `json:"transactions"`
|
Transactions []GetBlockTemplateResultTx `json:"transactions"`
|
||||||
|
@ -177,32 +177,6 @@ func NewStopNotifySpentCmd(outPoints []OutPoint) *StopNotifySpentCmd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RescanCmd defines the rescan JSON-RPC command.
|
|
||||||
//
|
|
||||||
// NOTE: Deprecated. Use RescanBlocksCmd instead.
|
|
||||||
type RescanCmd struct {
|
|
||||||
BeginBlock string
|
|
||||||
Addresses []string
|
|
||||||
OutPoints []OutPoint
|
|
||||||
EndBlock *string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRescanCmd returns a new instance which can be used to issue a rescan
|
|
||||||
// JSON-RPC command.
|
|
||||||
//
|
|
||||||
// The parameters which are pointers indicate they are optional. Passing nil
|
|
||||||
// for optional parameters will use the default value.
|
|
||||||
//
|
|
||||||
// NOTE: Deprecated. Use NewRescanBlocksCmd instead.
|
|
||||||
func NewRescanCmd(beginBlock string, addresses []string, outPoints []OutPoint, endBlock *string) *RescanCmd {
|
|
||||||
return &RescanCmd{
|
|
||||||
BeginBlock: beginBlock,
|
|
||||||
Addresses: addresses,
|
|
||||||
OutPoints: outPoints,
|
|
||||||
EndBlock: endBlock,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RescanBlocksCmd defines the rescan JSON-RPC command.
|
// RescanBlocksCmd defines the rescan JSON-RPC command.
|
||||||
//
|
//
|
||||||
// NOTE: This is a btcd extension ported from github.com/decred/dcrd/dcrjson
|
// NOTE: This is a btcd extension ported from github.com/decred/dcrd/dcrjson
|
||||||
@ -236,6 +210,5 @@ func init() {
|
|||||||
MustRegisterCmd("stopnotifynewtransactions", (*StopNotifyNewTransactionsCmd)(nil), flags)
|
MustRegisterCmd("stopnotifynewtransactions", (*StopNotifyNewTransactionsCmd)(nil), flags)
|
||||||
MustRegisterCmd("stopnotifyspent", (*StopNotifySpentCmd)(nil), flags)
|
MustRegisterCmd("stopnotifyspent", (*StopNotifySpentCmd)(nil), flags)
|
||||||
MustRegisterCmd("stopnotifyreceived", (*StopNotifyReceivedCmd)(nil), flags)
|
MustRegisterCmd("stopnotifyreceived", (*StopNotifyReceivedCmd)(nil), flags)
|
||||||
MustRegisterCmd("rescan", (*RescanCmd)(nil), flags)
|
|
||||||
MustRegisterCmd("rescanblocks", (*RescanBlocksCmd)(nil), flags)
|
MustRegisterCmd("rescanblocks", (*RescanBlocksCmd)(nil), flags)
|
||||||
}
|
}
|
||||||
|
@ -154,45 +154,6 @@ func TestDAGSvrWsCmds(t *testing.T) {
|
|||||||
OutPoints: []btcjson.OutPoint{{Hash: "123", Index: 0}},
|
OutPoints: []btcjson.OutPoint{{Hash: "123", Index: 0}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "rescan",
|
|
||||||
newCmd: func() (interface{}, error) {
|
|
||||||
return btcjson.NewCmd("rescan", "123", `["1Address"]`, `[{"hash":"0000000000000000000000000000000000000000000000000000000000000123","index":0}]`)
|
|
||||||
},
|
|
||||||
staticCmd: func() interface{} {
|
|
||||||
addrs := []string{"1Address"}
|
|
||||||
ops := []btcjson.OutPoint{{
|
|
||||||
Hash: "0000000000000000000000000000000000000000000000000000000000000123",
|
|
||||||
Index: 0,
|
|
||||||
}}
|
|
||||||
return btcjson.NewRescanCmd("123", addrs, ops, nil)
|
|
||||||
},
|
|
||||||
marshalled: `{"jsonrpc":"1.0","method":"rescan","params":["123",["1Address"],[{"hash":"0000000000000000000000000000000000000000000000000000000000000123","index":0}]],"id":1}`,
|
|
||||||
unmarshalled: &btcjson.RescanCmd{
|
|
||||||
BeginBlock: "123",
|
|
||||||
Addresses: []string{"1Address"},
|
|
||||||
OutPoints: []btcjson.OutPoint{{Hash: "0000000000000000000000000000000000000000000000000000000000000123", Index: 0}},
|
|
||||||
EndBlock: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "rescan optional",
|
|
||||||
newCmd: func() (interface{}, error) {
|
|
||||||
return btcjson.NewCmd("rescan", "123", `["1Address"]`, `[{"hash":"123","index":0}]`, "456")
|
|
||||||
},
|
|
||||||
staticCmd: func() interface{} {
|
|
||||||
addrs := []string{"1Address"}
|
|
||||||
ops := []btcjson.OutPoint{{Hash: "123", Index: 0}}
|
|
||||||
return btcjson.NewRescanCmd("123", addrs, ops, btcjson.String("456"))
|
|
||||||
},
|
|
||||||
marshalled: `{"jsonrpc":"1.0","method":"rescan","params":["123",["1Address"],[{"hash":"123","index":0}],"456"],"id":1}`,
|
|
||||||
unmarshalled: &btcjson.RescanCmd{
|
|
||||||
BeginBlock: "123",
|
|
||||||
Addresses: []string{"1Address"},
|
|
||||||
OutPoints: []btcjson.OutPoint{{Hash: "123", Index: 0}},
|
|
||||||
EndBlock: btcjson.String("456"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "loadtxfilter",
|
name: "loadtxfilter",
|
||||||
newCmd: func() (interface{}, error) {
|
newCmd: func() (interface{}, error) {
|
||||||
|
@ -33,6 +33,15 @@ func (hash Hash) String() string {
|
|||||||
return hex.EncodeToString(hash[:])
|
return hex.EncodeToString(hash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Strings(hashes []Hash) []string {
|
||||||
|
strings := make([]string, len(hashes))
|
||||||
|
for i, hash := range hashes {
|
||||||
|
strings[i] = hash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings
|
||||||
|
}
|
||||||
|
|
||||||
// CloneBytes returns a copy of the bytes which represent the hash as a byte
|
// CloneBytes returns a copy of the bytes which represent the hash as a byte
|
||||||
// slice.
|
// slice.
|
||||||
//
|
//
|
||||||
|
@ -1136,172 +1136,6 @@ func (r FutureRescanResult) Receive() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// RescanAsync returns an instance of a type that can be used to get the result
|
|
||||||
// of the RPC at some future time by invoking the Receive function on the
|
|
||||||
// returned instance.
|
|
||||||
//
|
|
||||||
// See Rescan for the blocking version and more details.
|
|
||||||
//
|
|
||||||
// NOTE: Rescan requests are not issued on client reconnect and must be
|
|
||||||
// performed manually (ideally with a new start height based on the last
|
|
||||||
// rescan progress notification). See the OnClientConnected notification
|
|
||||||
// callback for a good callsite to reissue rescan requests on connect and
|
|
||||||
// reconnect.
|
|
||||||
//
|
|
||||||
// NOTE: This is a btcd extension and requires a websocket connection.
|
|
||||||
//
|
|
||||||
// NOTE: Deprecated. Use RescanBlocksAsync instead.
|
|
||||||
func (c *Client) RescanAsync(startBlock *daghash.Hash,
|
|
||||||
addresses []btcutil.Address,
|
|
||||||
outpoints []*wire.OutPoint) FutureRescanResult {
|
|
||||||
|
|
||||||
// Not supported in HTTP POST mode.
|
|
||||||
if c.config.HTTPPostMode {
|
|
||||||
return newFutureError(ErrWebsocketsRequired)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore the notification if the client is not interested in
|
|
||||||
// notifications.
|
|
||||||
if c.ntfnHandlers == nil {
|
|
||||||
return newNilFutureResult()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert block hashes to strings.
|
|
||||||
var startBlockHashStr string
|
|
||||||
if startBlock != nil {
|
|
||||||
startBlockHashStr = startBlock.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert addresses to strings.
|
|
||||||
addrs := make([]string, 0, len(addresses))
|
|
||||||
for _, addr := range addresses {
|
|
||||||
addrs = append(addrs, addr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert outpoints.
|
|
||||||
ops := make([]btcjson.OutPoint, 0, len(outpoints))
|
|
||||||
for _, op := range outpoints {
|
|
||||||
ops = append(ops, newOutPointFromWire(op))
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := btcjson.NewRescanCmd(startBlockHashStr, addrs, ops, nil)
|
|
||||||
return c.sendCmd(cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rescan rescans the block chain starting from the provided starting block to
|
|
||||||
// the end of the longest chain for transactions that pay to the passed
|
|
||||||
// addresses and transactions which spend the passed outpoints.
|
|
||||||
//
|
|
||||||
// The notifications of found transactions are delivered to the notification
|
|
||||||
// handlers associated with client and this call will not return until the
|
|
||||||
// rescan has completed. Calling this function has no effect if there are no
|
|
||||||
// notification handlers and will result in an error if the client is configured
|
|
||||||
// to run in HTTP POST mode.
|
|
||||||
//
|
|
||||||
// The notifications delivered as a result of this call will be via one of
|
|
||||||
// OnRedeemingTx (for transactions which spend from the one of the
|
|
||||||
// passed outpoints), OnRecvTx (for transactions that receive funds
|
|
||||||
// to one of the passed addresses), and OnRescanProgress (for rescan progress
|
|
||||||
// updates).
|
|
||||||
//
|
|
||||||
// See RescanEndBlock to also specify an ending block to finish the rescan
|
|
||||||
// without continuing through the best block on the main chain.
|
|
||||||
//
|
|
||||||
// NOTE: Rescan requests are not issued on client reconnect and must be
|
|
||||||
// performed manually (ideally with a new start height based on the last
|
|
||||||
// rescan progress notification). See the OnClientConnected notification
|
|
||||||
// callback for a good callsite to reissue rescan requests on connect and
|
|
||||||
// reconnect.
|
|
||||||
//
|
|
||||||
// NOTE: This is a btcd extension and requires a websocket connection.
|
|
||||||
//
|
|
||||||
// NOTE: Deprecated. Use RescanBlocks instead.
|
|
||||||
func (c *Client) Rescan(startBlock *daghash.Hash,
|
|
||||||
addresses []btcutil.Address,
|
|
||||||
outpoints []*wire.OutPoint) error {
|
|
||||||
|
|
||||||
return c.RescanAsync(startBlock, addresses, outpoints).Receive()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RescanEndBlockAsync returns an instance of a type that can be used to get
|
|
||||||
// the result of the RPC at some future time by invoking the Receive function on
|
|
||||||
// the returned instance.
|
|
||||||
//
|
|
||||||
// See RescanEndBlock for the blocking version and more details.
|
|
||||||
//
|
|
||||||
// NOTE: This is a btcd extension and requires a websocket connection.
|
|
||||||
//
|
|
||||||
// NOTE: Deprecated. Use RescanBlocksAsync instead.
|
|
||||||
func (c *Client) RescanEndBlockAsync(startBlock *daghash.Hash,
|
|
||||||
addresses []btcutil.Address, outpoints []*wire.OutPoint,
|
|
||||||
endBlock *daghash.Hash) FutureRescanResult {
|
|
||||||
|
|
||||||
// Not supported in HTTP POST mode.
|
|
||||||
if c.config.HTTPPostMode {
|
|
||||||
return newFutureError(ErrWebsocketsRequired)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore the notification if the client is not interested in
|
|
||||||
// notifications.
|
|
||||||
if c.ntfnHandlers == nil {
|
|
||||||
return newNilFutureResult()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert block hashes to strings.
|
|
||||||
var startBlockHashStr, endBlockHashStr string
|
|
||||||
if startBlock != nil {
|
|
||||||
startBlockHashStr = startBlock.String()
|
|
||||||
}
|
|
||||||
if endBlock != nil {
|
|
||||||
endBlockHashStr = endBlock.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert addresses to strings.
|
|
||||||
addrs := make([]string, 0, len(addresses))
|
|
||||||
for _, addr := range addresses {
|
|
||||||
addrs = append(addrs, addr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert outpoints.
|
|
||||||
ops := make([]btcjson.OutPoint, 0, len(outpoints))
|
|
||||||
for _, op := range outpoints {
|
|
||||||
ops = append(ops, newOutPointFromWire(op))
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := btcjson.NewRescanCmd(startBlockHashStr, addrs, ops,
|
|
||||||
&endBlockHashStr)
|
|
||||||
return c.sendCmd(cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RescanEndHeight rescans the block chain starting from the provided starting
|
|
||||||
// block up to the provided ending block for transactions that pay to the
|
|
||||||
// passed addresses and transactions which spend the passed outpoints.
|
|
||||||
//
|
|
||||||
// The notifications of found transactions are delivered to the notification
|
|
||||||
// handlers associated with client and this call will not return until the
|
|
||||||
// rescan has completed. Calling this function has no effect if there are no
|
|
||||||
// notification handlers and will result in an error if the client is configured
|
|
||||||
// to run in HTTP POST mode.
|
|
||||||
//
|
|
||||||
// The notifications delivered as a result of this call will be via one of
|
|
||||||
// OnRedeemingTx (for transactions which spend from the one of the
|
|
||||||
// passed outpoints), OnRecvTx (for transactions that receive funds
|
|
||||||
// to one of the passed addresses), and OnRescanProgress (for rescan progress
|
|
||||||
// updates).
|
|
||||||
//
|
|
||||||
// See Rescan to also perform a rescan through current end of the longest chain.
|
|
||||||
//
|
|
||||||
// NOTE: This is a btcd extension and requires a websocket connection.
|
|
||||||
//
|
|
||||||
// NOTE: Deprecated. Use RescanBlocks instead.
|
|
||||||
func (c *Client) RescanEndHeight(startBlock *daghash.Hash,
|
|
||||||
addresses []btcutil.Address, outpoints []*wire.OutPoint,
|
|
||||||
endBlock *daghash.Hash) error {
|
|
||||||
|
|
||||||
return c.RescanEndBlockAsync(startBlock, addresses, outpoints,
|
|
||||||
endBlock).Receive()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FutureLoadTxFilterResult is a future promise to deliver the result
|
// FutureLoadTxFilterResult is a future promise to deliver the result
|
||||||
// of a LoadTxFilterAsync RPC invocation (or an applicable error).
|
// of a LoadTxFilterAsync RPC invocation (or an applicable error).
|
||||||
//
|
//
|
||||||
|
56
rpcserver.go
56
rpcserver.go
@ -138,7 +138,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
|
|||||||
"getbestblock": handleGetBestBlock,
|
"getbestblock": handleGetBestBlock,
|
||||||
"getbestblockhash": handleGetBestBlockHash,
|
"getbestblockhash": handleGetBestBlockHash,
|
||||||
"getblock": handleGetBlock,
|
"getblock": handleGetBlock,
|
||||||
"getblockchaininfo": handleGetBlockChainInfo,
|
"getblockdaginfo": handleGetBlockDAGInfo,
|
||||||
"getblockcount": handleGetBlockCount,
|
"getblockcount": handleGetBlockCount,
|
||||||
"getblockhash": handleGetBlockHash,
|
"getblockhash": handleGetBlockHash,
|
||||||
"getblockheader": handleGetBlockHeader,
|
"getblockheader": handleGetBlockHeader,
|
||||||
@ -170,7 +170,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
|
|||||||
"submitblock": handleSubmitBlock,
|
"submitblock": handleSubmitBlock,
|
||||||
"uptime": handleUptime,
|
"uptime": handleUptime,
|
||||||
"validateaddress": handleValidateAddress,
|
"validateaddress": handleValidateAddress,
|
||||||
"verifychain": handleVerifyChain,
|
"verifydag": handleVerifyDAG,
|
||||||
"verifymessage": handleVerifyMessage,
|
"verifymessage": handleVerifyMessage,
|
||||||
"version": handleVersion,
|
"version": handleVersion,
|
||||||
}
|
}
|
||||||
@ -1100,7 +1100,7 @@ func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i
|
|||||||
Version: blockHeader.Version,
|
Version: blockHeader.Version,
|
||||||
VersionHex: fmt.Sprintf("%08x", blockHeader.Version),
|
VersionHex: fmt.Sprintf("%08x", blockHeader.Version),
|
||||||
MerkleRoot: blockHeader.MerkleRoot.String(),
|
MerkleRoot: blockHeader.MerkleRoot.String(),
|
||||||
PreviousHash: blockHeader.PrevBlock.String(),
|
PreviousHashes: daghash.Strings(blockHeader.PrevBlocks),
|
||||||
Nonce: blockHeader.Nonce,
|
Nonce: blockHeader.Nonce,
|
||||||
Time: blockHeader.Timestamp.Unix(),
|
Time: blockHeader.Timestamp.Unix(),
|
||||||
Confirmations: uint64(1 + dagState.SelectedTip.Height - blockHeight),
|
Confirmations: uint64(1 + dagState.SelectedTip.Height - blockHeight),
|
||||||
@ -1156,19 +1156,19 @@ func softForkStatus(state blockdag.ThresholdState) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleGetBlockChainInfo implements the getblockchaininfo command.
|
// handleGetBlockDAGInfo implements the getblockdaginfo command.
|
||||||
func handleGetBlockChainInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
func handleGetBlockDAGInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||||
// Obtain a snapshot of the current best known blockchain state. We'll
|
// Obtain a snapshot of the current best known DAG state. We'll
|
||||||
// populate the response to this call primarily from this snapshot.
|
// populate the response to this call primarily from this snapshot.
|
||||||
params := s.cfg.ChainParams
|
params := s.cfg.ChainParams
|
||||||
chain := s.cfg.DAG
|
dag := s.cfg.DAG
|
||||||
dagState := chain.GetDAGState()
|
dagState := dag.GetDAGState()
|
||||||
|
|
||||||
chainInfo := &btcjson.GetBlockChainInfoResult{
|
chainInfo := &btcjson.GetBlockDAGInfoResult{
|
||||||
Chain: params.Name,
|
DAG: params.Name,
|
||||||
Blocks: dagState.SelectedTip.Height,
|
Blocks: dagState.SelectedTip.Height,
|
||||||
Headers: dagState.SelectedTip.Height,
|
Headers: dagState.SelectedTip.Height,
|
||||||
BestBlockHash: dagState.SelectedTip.Hash.String(),
|
TipHashes: daghash.Strings(dagState.TipHashes),
|
||||||
Difficulty: getDifficultyRatio(dagState.SelectedTip.Bits, params),
|
Difficulty: getDifficultyRatio(dagState.SelectedTip.Bits, params),
|
||||||
MedianTime: dagState.SelectedTip.MedianTime.Unix(),
|
MedianTime: dagState.SelectedTip.MedianTime.Unix(),
|
||||||
Pruned: false,
|
Pruned: false,
|
||||||
@ -1218,9 +1218,9 @@ func handleGetBlockChainInfo(s *rpcServer, cmd interface{}, closeChan <-chan str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query the chain for the current status of the deployment as
|
// Query the dag for the current status of the deployment as
|
||||||
// identified by its deployment ID.
|
// identified by its deployment ID.
|
||||||
deploymentStatus, err := chain.ThresholdState(uint32(deployment))
|
deploymentStatus, err := dag.ThresholdState(uint32(deployment))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
context := "Failed to obtain deployment status"
|
context := "Failed to obtain deployment status"
|
||||||
return nil, internalRPCError(err.Error(), context)
|
return nil, internalRPCError(err.Error(), context)
|
||||||
@ -1330,7 +1330,7 @@ func handleGetBlockHeader(s *rpcServer, cmd interface{}, closeChan <-chan struct
|
|||||||
VersionHex: fmt.Sprintf("%08x", blockHeader.Version),
|
VersionHex: fmt.Sprintf("%08x", blockHeader.Version),
|
||||||
MerkleRoot: blockHeader.MerkleRoot.String(),
|
MerkleRoot: blockHeader.MerkleRoot.String(),
|
||||||
NextHash: nextHashString,
|
NextHash: nextHashString,
|
||||||
PreviousHash: blockHeader.PrevBlock.String(),
|
PreviousHashes: daghash.Strings(blockHeader.PrevBlocks),
|
||||||
Nonce: uint64(blockHeader.Nonce),
|
Nonce: uint64(blockHeader.Nonce),
|
||||||
Time: blockHeader.Timestamp.Unix(),
|
Time: blockHeader.Timestamp.Unix(),
|
||||||
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
|
Bits: strconv.FormatInt(int64(blockHeader.Bits), 16),
|
||||||
@ -1698,7 +1698,7 @@ func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld
|
|||||||
Bits: strconv.FormatInt(int64(header.Bits), 16),
|
Bits: strconv.FormatInt(int64(header.Bits), 16),
|
||||||
CurTime: header.Timestamp.Unix(),
|
CurTime: header.Timestamp.Unix(),
|
||||||
Height: int64(template.Height),
|
Height: int64(template.Height),
|
||||||
PreviousHash: header.PrevBlock.String(),
|
PreviousHashes: daghash.Strings(header.PrevBlocks),
|
||||||
SigOpLimit: blockdag.MaxSigOpsPerBlock,
|
SigOpLimit: blockdag.MaxSigOpsPerBlock,
|
||||||
SizeLimit: wire.MaxBlockPayload,
|
SizeLimit: wire.MaxBlockPayload,
|
||||||
Transactions: transactions,
|
Transactions: transactions,
|
||||||
@ -1790,7 +1790,7 @@ func handleGetBlockTemplateLongPoll(s *rpcServer, longPollID string, useCoinbase
|
|||||||
// Return the block template now if the specific block template
|
// Return the block template now if the specific block template
|
||||||
// identified by the long poll ID no longer matches the current block
|
// identified by the long poll ID no longer matches the current block
|
||||||
// template as this means the provided template is stale.
|
// template as this means the provided template is stale.
|
||||||
prevTemplateHash := &state.template.Block.Header.PrevBlock
|
prevTemplateHash := &state.template.Block.Header.PrevBlocks[0] // TODO: (Stas) This is probably wrong. Modified only to satisfy compilation
|
||||||
if !prevHash.IsEqual(prevTemplateHash) ||
|
if !prevHash.IsEqual(prevTemplateHash) ||
|
||||||
lastGenerated != state.lastGenerated.Unix() {
|
lastGenerated != state.lastGenerated.Unix() {
|
||||||
|
|
||||||
@ -1838,7 +1838,7 @@ func handleGetBlockTemplateLongPoll(s *rpcServer, longPollID string, useCoinbase
|
|||||||
// Include whether or not it is valid to submit work against the old
|
// Include whether or not it is valid to submit work against the old
|
||||||
// block template depending on whether or not a solution has already
|
// block template depending on whether or not a solution has already
|
||||||
// been found and added to the block chain.
|
// been found and added to the block chain.
|
||||||
submitOld := prevHash.IsEqual(&state.template.Block.Header.PrevBlock)
|
submitOld := prevHash.IsEqual(&state.template.Block.Header.PrevBlocks[0]) // TODO: (Stas) This is probably wrong. Modified only to satisfy compilation
|
||||||
result, err := state.blockTemplateResult(useCoinbaseValue, &submitOld)
|
result, err := state.blockTemplateResult(useCoinbaseValue, &submitOld)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -2064,10 +2064,10 @@ func handleGetBlockTemplateProposal(s *rpcServer, request *btcjson.TemplateReque
|
|||||||
}
|
}
|
||||||
block := btcutil.NewBlock(&msgBlock)
|
block := btcutil.NewBlock(&msgBlock)
|
||||||
|
|
||||||
// Ensure the block is building from the expected previous block.
|
// Ensure the block is building from the expected previous blocks.
|
||||||
expectedPrevHash := s.cfg.DAG.GetDAGState().SelectedTip.Hash
|
expectedPrevHashes := s.cfg.DAG.GetDAGState().TipHashes
|
||||||
prevHash := &block.MsgBlock().Header.PrevBlock
|
prevHashes := block.MsgBlock().Header.PrevBlocks
|
||||||
if !expectedPrevHash.IsEqual(prevHash) {
|
if !daghash.AreEqual(expectedPrevHashes, prevHashes) {
|
||||||
return "bad-prevblk", nil
|
return "bad-prevblk", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2246,7 +2246,7 @@ func handleGetHeaders(s *rpcServer, cmd interface{}, closeChan <-chan struct{})
|
|||||||
// that are not related to wallet functionality.
|
// that are not related to wallet functionality.
|
||||||
func handleGetInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
func handleGetInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||||
dagState := s.cfg.DAG.GetDAGState()
|
dagState := s.cfg.DAG.GetDAGState()
|
||||||
ret := &btcjson.InfoChainResult{
|
ret := &btcjson.InfoDAGResult{
|
||||||
Version: int32(1000000*appMajor + 10000*appMinor + 100*appPatch),
|
Version: int32(1000000*appMajor + 10000*appMinor + 100*appPatch),
|
||||||
ProtocolVersion: int32(maxProtocolVersion),
|
ProtocolVersion: int32(maxProtocolVersion),
|
||||||
Blocks: dagState.SelectedTip.Height,
|
Blocks: dagState.SelectedTip.Height,
|
||||||
@ -3399,7 +3399,7 @@ func handleUptime(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (int
|
|||||||
func handleValidateAddress(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
func handleValidateAddress(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||||
c := cmd.(*btcjson.ValidateAddressCmd)
|
c := cmd.(*btcjson.ValidateAddressCmd)
|
||||||
|
|
||||||
result := btcjson.ValidateAddressChainResult{}
|
result := btcjson.ValidateAddressResult{}
|
||||||
addr, err := btcutil.DecodeAddress(c.Address, s.cfg.ChainParams)
|
addr, err := btcutil.DecodeAddress(c.Address, s.cfg.ChainParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Return the default value (false) for IsValid.
|
// Return the default value (false) for IsValid.
|
||||||
@ -3412,7 +3412,7 @@ func handleValidateAddress(s *rpcServer, cmd interface{}, closeChan <-chan struc
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyChain(s *rpcServer, level, depth int32) error {
|
func verifyDAG(s *rpcServer, level, depth int32) error {
|
||||||
dagState := s.cfg.DAG.GetDAGState()
|
dagState := s.cfg.DAG.GetDAGState()
|
||||||
finishHeight := dagState.SelectedTip.Height - depth
|
finishHeight := dagState.SelectedTip.Height - depth
|
||||||
if finishHeight < 0 {
|
if finishHeight < 0 {
|
||||||
@ -3447,9 +3447,9 @@ func verifyChain(s *rpcServer, level, depth int32) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleVerifyChain implements the verifychain command.
|
// handleVerifyDAG implements the verifydag command.
|
||||||
func handleVerifyChain(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
func handleVerifyDAG(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||||
c := cmd.(*btcjson.VerifyChainCmd)
|
c := cmd.(*btcjson.VerifyDAGCmd)
|
||||||
|
|
||||||
var checkLevel, checkDepth int32
|
var checkLevel, checkDepth int32
|
||||||
if c.CheckLevel != nil {
|
if c.CheckLevel != nil {
|
||||||
@ -3459,7 +3459,7 @@ func handleVerifyChain(s *rpcServer, cmd interface{}, closeChan <-chan struct{})
|
|||||||
checkDepth = *c.CheckDepth
|
checkDepth = *c.CheckDepth
|
||||||
}
|
}
|
||||||
|
|
||||||
err := verifyChain(s, checkLevel, checkDepth)
|
err := verifyDAG(s, checkLevel, checkDepth)
|
||||||
return err == nil, nil
|
return err == nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,24 +167,24 @@ var helpDescsEnUS = map[string]string{
|
|||||||
"getblock--result0": "Hex-encoded bytes of the serialized block",
|
"getblock--result0": "Hex-encoded bytes of the serialized block",
|
||||||
|
|
||||||
// GetBlockChainInfoCmd help.
|
// GetBlockChainInfoCmd help.
|
||||||
"getblockchaininfo--synopsis": "Returns information about the current blockchain state and the status of any active soft-fork deployments.",
|
"getblockdaginfo--synopsis": "Returns information about the current blockDAG state and the status of any active soft-fork deployments.",
|
||||||
|
|
||||||
// GetBlockChainInfoResult help.
|
// GetBlockDAGInfoResult help.
|
||||||
"getblockchaininforesult-chain": "The name of the chain the daemon is on (testnet, mainnet, etc)",
|
"getblockdaginforesult-dag": "The name of the DAG the daemon is on (testnet, mainnet, etc)",
|
||||||
"getblockchaininforesult-blocks": "The number of blocks in the best known chain",
|
"getblockdaginforesult-blocks": "The number of blocks in the best known chain",
|
||||||
"getblockchaininforesult-headers": "The number of headers that we've gathered for in the best known chain",
|
"getblockdaginforesult-headers": "The number of headers that we've gathered for in the best known chain",
|
||||||
"getblockchaininforesult-bestblockhash": "The block hash for the latest block in the main chain",
|
"getblockdaginforesult-tiphashes": "The block hashes for the tips in the DAG",
|
||||||
"getblockchaininforesult-difficulty": "The current chain difficulty",
|
"getblockdaginforesult-difficulty": "The current chain difficulty",
|
||||||
"getblockchaininforesult-mediantime": "The median time from the PoV of the best block in the chain",
|
"getblockdaginforesult-mediantime": "The median time from the PoV of the best block in the chain",
|
||||||
"getblockchaininforesult-verificationprogress": "An estimate for how much of the best chain we've verified",
|
"getblockdaginforesult-verificationprogress": "An estimate for how much of the best chain we've verified",
|
||||||
"getblockchaininforesult-pruned": "A bool that indicates if the node is pruned or not",
|
"getblockdaginforesult-pruned": "A bool that indicates if the node is pruned or not",
|
||||||
"getblockchaininforesult-pruneheight": "The lowest block retained in the current pruned chain",
|
"getblockdaginforesult-pruneheight": "The lowest block retained in the current pruned chain",
|
||||||
"getblockchaininforesult-chainwork": "The total cumulative work in the best chain",
|
"getblockdaginforesult-dagwork": "The total cumulative work in the DAG",
|
||||||
"getblockchaininforesult-softforks": "The status of the super-majority soft-forks",
|
"getblockdaginforesult-softforks": "The status of the super-majority soft-forks",
|
||||||
"getblockchaininforesult-bip9_softforks": "JSON object describing active BIP0009 deployments",
|
"getblockdaginforesult-bip9_softforks": "JSON object describing active BIP0009 deployments",
|
||||||
"getblockchaininforesult-bip9_softforks--key": "bip9_softforks",
|
"getblockdaginforesult-bip9_softforks--key": "bip9_softforks",
|
||||||
"getblockchaininforesult-bip9_softforks--value": "An object describing a particular BIP009 deployment",
|
"getblockdaginforesult-bip9_softforks--value": "An object describing a particular BIP009 deployment",
|
||||||
"getblockchaininforesult-bip9_softforks--desc": "The status of any defined BIP0009 soft-fork deployments",
|
"getblockdaginforesult-bip9_softforks--desc": "The status of any defined BIP0009 soft-fork deployments",
|
||||||
|
|
||||||
// SoftForkDescription help.
|
// SoftForkDescription help.
|
||||||
"softforkdescription-reject": "The current activation status of the softfork",
|
"softforkdescription-reject": "The current activation status of the softfork",
|
||||||
@ -234,7 +234,7 @@ var helpDescsEnUS = map[string]string{
|
|||||||
"getblockverboseresult-nonce": "The block nonce",
|
"getblockverboseresult-nonce": "The block nonce",
|
||||||
"getblockverboseresult-bits": "The bits which represent the block difficulty",
|
"getblockverboseresult-bits": "The bits which represent the block difficulty",
|
||||||
"getblockverboseresult-difficulty": "The proof-of-work difficulty as a multiple of the minimum difficulty",
|
"getblockverboseresult-difficulty": "The proof-of-work difficulty as a multiple of the minimum difficulty",
|
||||||
"getblockverboseresult-previousblockhash": "The hash of the previous block",
|
"getblockverboseresult-previousblockhashes": "The hashes of the previous blocks",
|
||||||
"getblockverboseresult-nextblockhash": "The hash of the next block (only if there is one)",
|
"getblockverboseresult-nextblockhash": "The hash of the next block (only if there is one)",
|
||||||
|
|
||||||
// GetBlockCountCmd help.
|
// GetBlockCountCmd help.
|
||||||
@ -265,7 +265,7 @@ var helpDescsEnUS = map[string]string{
|
|||||||
"getblockheaderverboseresult-nonce": "The block nonce",
|
"getblockheaderverboseresult-nonce": "The block nonce",
|
||||||
"getblockheaderverboseresult-bits": "The bits which represent the block difficulty",
|
"getblockheaderverboseresult-bits": "The bits which represent the block difficulty",
|
||||||
"getblockheaderverboseresult-difficulty": "The proof-of-work difficulty as a multiple of the minimum difficulty",
|
"getblockheaderverboseresult-difficulty": "The proof-of-work difficulty as a multiple of the minimum difficulty",
|
||||||
"getblockheaderverboseresult-previousblockhash": "The hash of the previous block",
|
"getblockheaderverboseresult-previousblockhashes": "The hashes of the previous blocks",
|
||||||
"getblockheaderverboseresult-nextblockhash": "The hash of the next block (only if there is one)",
|
"getblockheaderverboseresult-nextblockhash": "The hash of the next block (only if there is one)",
|
||||||
|
|
||||||
// TemplateRequest help.
|
// TemplateRequest help.
|
||||||
@ -293,7 +293,7 @@ var helpDescsEnUS = map[string]string{
|
|||||||
"getblocktemplateresult-bits": "Hex-encoded compressed difficulty",
|
"getblocktemplateresult-bits": "Hex-encoded compressed difficulty",
|
||||||
"getblocktemplateresult-curtime": "Current time as seen by the server (recommended for block time); must fall within mintime/maxtime rules",
|
"getblocktemplateresult-curtime": "Current time as seen by the server (recommended for block time); must fall within mintime/maxtime rules",
|
||||||
"getblocktemplateresult-height": "Height of the block to be solved",
|
"getblocktemplateresult-height": "Height of the block to be solved",
|
||||||
"getblocktemplateresult-previousblockhash": "Hex-encoded big-endian hash of the previous block",
|
"getblocktemplateresult-previousblockhashes": "Hex-encoded big-endian hashes of the previous blocks",
|
||||||
"getblocktemplateresult-sigoplimit": "Number of sigops allowed in blocks ",
|
"getblocktemplateresult-sigoplimit": "Number of sigops allowed in blocks ",
|
||||||
"getblocktemplateresult-sizelimit": "Number of bytes allowed in blocks",
|
"getblocktemplateresult-sizelimit": "Number of bytes allowed in blocks",
|
||||||
"getblocktemplateresult-transactions": "Array of transactions as JSON objects",
|
"getblocktemplateresult-transactions": "Array of transactions as JSON objects",
|
||||||
@ -355,17 +355,17 @@ var helpDescsEnUS = map[string]string{
|
|||||||
"gethashespersec--synopsis": "Returns a recent hashes per second performance measurement while generating coins (mining).",
|
"gethashespersec--synopsis": "Returns a recent hashes per second performance measurement while generating coins (mining).",
|
||||||
"gethashespersec--result0": "The number of hashes per second",
|
"gethashespersec--result0": "The number of hashes per second",
|
||||||
|
|
||||||
// InfoChainResult help.
|
// InfoDAGResult help.
|
||||||
"infochainresult-version": "The version of the server",
|
"infodagresult-version": "The version of the server",
|
||||||
"infochainresult-protocolversion": "The latest supported protocol version",
|
"infodagresult-protocolversion": "The latest supported protocol version",
|
||||||
"infochainresult-blocks": "The number of blocks processed",
|
"infodagresult-blocks": "The number of blocks processed",
|
||||||
"infochainresult-timeoffset": "The time offset",
|
"infodagresult-timeoffset": "The time offset",
|
||||||
"infochainresult-connections": "The number of connected peers",
|
"infodagresult-connections": "The number of connected peers",
|
||||||
"infochainresult-proxy": "The proxy used by the server",
|
"infodagresult-proxy": "The proxy used by the server",
|
||||||
"infochainresult-difficulty": "The current target difficulty",
|
"infodagresult-difficulty": "The current target difficulty",
|
||||||
"infochainresult-testnet": "Whether or not server is using testnet",
|
"infodagresult-testnet": "Whether or not server is using testnet",
|
||||||
"infochainresult-relayfee": "The minimum relay fee for non-free transactions in BTC/KB",
|
"infodagresult-relayfee": "The minimum relay fee for non-free transactions in BTC/KB",
|
||||||
"infochainresult-errors": "Any current errors",
|
"infodagresult-errors": "Any current errors",
|
||||||
|
|
||||||
// InfoWalletResult help.
|
// InfoWalletResult help.
|
||||||
"infowalletresult-version": "The version of the server",
|
"infowalletresult-version": "The version of the server",
|
||||||
@ -551,22 +551,22 @@ var helpDescsEnUS = map[string]string{
|
|||||||
"submitblock--result1": "The reason the block was rejected",
|
"submitblock--result1": "The reason the block was rejected",
|
||||||
|
|
||||||
// ValidateAddressResult help.
|
// ValidateAddressResult help.
|
||||||
"validateaddresschainresult-isvalid": "Whether or not the address is valid",
|
"validateaddressresult-isvalid": "Whether or not the address is valid",
|
||||||
"validateaddresschainresult-address": "The bitcoin address (only when isvalid is true)",
|
"validateaddressresult-address": "The bitcoin address (only when isvalid is true)",
|
||||||
|
|
||||||
// ValidateAddressCmd help.
|
// ValidateAddressCmd help.
|
||||||
"validateaddress--synopsis": "Verify an address is valid.",
|
"validateaddress--synopsis": "Verify an address is valid.",
|
||||||
"validateaddress-address": "Bitcoin address to validate",
|
"validateaddress-address": "Bitcoin address to validate",
|
||||||
|
|
||||||
// VerifyChainCmd help.
|
// VerifyChainCmd help.
|
||||||
"verifychain--synopsis": "Verifies the block chain database.\n" +
|
"verifydag--synopsis": "Verifies the block DAG database.\n" +
|
||||||
"The actual checks performed by the checklevel parameter are implementation specific.\n" +
|
"The actual checks performed by the checklevel parameter are implementation specific.\n" +
|
||||||
"For btcd this is:\n" +
|
"For btcd this is:\n" +
|
||||||
"checklevel=0 - Look up each block and ensure it can be loaded from the database.\n" +
|
"checklevel=0 - Look up each block and ensure it can be loaded from the database.\n" +
|
||||||
"checklevel=1 - Perform basic context-free sanity checks on each block.",
|
"checklevel=1 - Perform basic context-free sanity checks on each block.",
|
||||||
"verifychain-checklevel": "How thorough the block verification is",
|
"verifydag-checklevel": "How thorough the block verification is",
|
||||||
"verifychain-checkdepth": "The number of blocks to check",
|
"verifydag-checkdepth": "The number of blocks to check",
|
||||||
"verifychain--result0": "Whether or not the chain verified",
|
"verifydag--result0": "Whether or not the DAG verified",
|
||||||
|
|
||||||
// VerifyMessageCmd help.
|
// VerifyMessageCmd help.
|
||||||
"verifymessage--synopsis": "Verify a signed message.",
|
"verifymessage--synopsis": "Verify a signed message.",
|
||||||
@ -678,7 +678,7 @@ var rpcResultTypes = map[string][]interface{}{
|
|||||||
"getblockhash": {(*string)(nil)},
|
"getblockhash": {(*string)(nil)},
|
||||||
"getblockheader": {(*string)(nil), (*btcjson.GetBlockHeaderVerboseResult)(nil)},
|
"getblockheader": {(*string)(nil), (*btcjson.GetBlockHeaderVerboseResult)(nil)},
|
||||||
"getblocktemplate": {(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil},
|
"getblocktemplate": {(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil},
|
||||||
"getblockchaininfo": {(*btcjson.GetBlockChainInfoResult)(nil)},
|
"getblockdaginfo": {(*btcjson.GetBlockDAGInfoResult)(nil)},
|
||||||
"getcfilter": {(*string)(nil)},
|
"getcfilter": {(*string)(nil)},
|
||||||
"getcfilterheader": {(*string)(nil)},
|
"getcfilterheader": {(*string)(nil)},
|
||||||
"getconnectioncount": {(*int32)(nil)},
|
"getconnectioncount": {(*int32)(nil)},
|
||||||
@ -687,7 +687,7 @@ var rpcResultTypes = map[string][]interface{}{
|
|||||||
"getgenerate": {(*bool)(nil)},
|
"getgenerate": {(*bool)(nil)},
|
||||||
"gethashespersec": {(*float64)(nil)},
|
"gethashespersec": {(*float64)(nil)},
|
||||||
"getheaders": {(*[]string)(nil)},
|
"getheaders": {(*[]string)(nil)},
|
||||||
"getinfo": {(*btcjson.InfoChainResult)(nil)},
|
"getinfo": {(*btcjson.InfoDAGResult)(nil)},
|
||||||
"getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)},
|
"getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)},
|
||||||
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
|
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
|
||||||
"getnettotals": {(*btcjson.GetNetTotalsResult)(nil)},
|
"getnettotals": {(*btcjson.GetNetTotalsResult)(nil)},
|
||||||
@ -705,8 +705,8 @@ var rpcResultTypes = map[string][]interface{}{
|
|||||||
"stop": {(*string)(nil)},
|
"stop": {(*string)(nil)},
|
||||||
"submitblock": {nil, (*string)(nil)},
|
"submitblock": {nil, (*string)(nil)},
|
||||||
"uptime": {(*int64)(nil)},
|
"uptime": {(*int64)(nil)},
|
||||||
"validateaddress": {(*btcjson.ValidateAddressChainResult)(nil)},
|
"validateaddress": {(*btcjson.ValidateAddressResult)(nil)},
|
||||||
"verifychain": {(*bool)(nil)},
|
"verifydag": {(*bool)(nil)},
|
||||||
"verifymessage": {(*bool)(nil)},
|
"verifymessage": {(*bool)(nil)},
|
||||||
"version": {(*map[string]btcjson.VersionResult)(nil)},
|
"version": {(*map[string]btcjson.VersionResult)(nil)},
|
||||||
|
|
||||||
|
536
rpcwebsocket.go
536
rpcwebsocket.go
@ -16,7 +16,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -26,7 +25,6 @@ import (
|
|||||||
"github.com/daglabs/btcd/btcjson"
|
"github.com/daglabs/btcd/btcjson"
|
||||||
"github.com/daglabs/btcd/dagconfig"
|
"github.com/daglabs/btcd/dagconfig"
|
||||||
"github.com/daglabs/btcd/dagconfig/daghash"
|
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||||
"github.com/daglabs/btcd/database"
|
|
||||||
"github.com/daglabs/btcd/txscript"
|
"github.com/daglabs/btcd/txscript"
|
||||||
"github.com/daglabs/btcd/wire"
|
"github.com/daglabs/btcd/wire"
|
||||||
"github.com/daglabs/btcutil"
|
"github.com/daglabs/btcutil"
|
||||||
@ -75,7 +73,6 @@ var wsHandlersBeforeInit = map[string]wsCommandHandler{
|
|||||||
"stopnotifynewtransactions": handleStopNotifyNewTransactions,
|
"stopnotifynewtransactions": handleStopNotifyNewTransactions,
|
||||||
"stopnotifyspent": handleStopNotifySpent,
|
"stopnotifyspent": handleStopNotifySpent,
|
||||||
"stopnotifyreceived": handleStopNotifyReceived,
|
"stopnotifyreceived": handleStopNotifyReceived,
|
||||||
"rescan": handleRescan,
|
|
||||||
"rescanblocks": handleRescanBlocks,
|
"rescanblocks": handleRescanBlocks,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1977,168 +1974,6 @@ func deserializeOutpoints(serializedOuts []btcjson.OutPoint) ([]*wire.OutPoint,
|
|||||||
return outpoints, nil
|
return outpoints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type rescanKeys struct {
|
|
||||||
fallbacks map[string]struct{}
|
|
||||||
pubKeyHashes map[[ripemd160.Size]byte]struct{}
|
|
||||||
scriptHashes map[[ripemd160.Size]byte]struct{}
|
|
||||||
compressedPubKeys map[[33]byte]struct{}
|
|
||||||
uncompressedPubKeys map[[65]byte]struct{}
|
|
||||||
unspent map[wire.OutPoint]struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unspentSlice returns a slice of currently-unspent outpoints for the rescan
|
|
||||||
// lookup keys. This is primarily intended to be used to register outpoints
|
|
||||||
// for continuous notifications after a rescan has completed.
|
|
||||||
func (r *rescanKeys) unspentSlice() []*wire.OutPoint {
|
|
||||||
ops := make([]*wire.OutPoint, 0, len(r.unspent))
|
|
||||||
for op := range r.unspent {
|
|
||||||
opCopy := op
|
|
||||||
ops = append(ops, &opCopy)
|
|
||||||
}
|
|
||||||
return ops
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrRescanReorg defines the error that is returned when an unrecoverable
|
|
||||||
// reorganize is detected during a rescan.
|
|
||||||
var ErrRescanReorg = btcjson.RPCError{
|
|
||||||
Code: btcjson.ErrRPCDatabase,
|
|
||||||
Message: "Reorganize",
|
|
||||||
}
|
|
||||||
|
|
||||||
// rescanBlock rescans all transactions in a single block. This is a helper
|
|
||||||
// function for handleRescan.
|
|
||||||
func rescanBlock(wsc *wsClient, lookups *rescanKeys, blk *btcutil.Block) {
|
|
||||||
for _, tx := range blk.Transactions() {
|
|
||||||
// Hexadecimal representation of this tx. Only created if
|
|
||||||
// needed, and reused for later notifications if already made.
|
|
||||||
var txHex string
|
|
||||||
|
|
||||||
// All inputs and outputs must be iterated through to correctly
|
|
||||||
// modify the unspent map, however, just a single notification
|
|
||||||
// for any matching transaction inputs or outputs should be
|
|
||||||
// created and sent.
|
|
||||||
spentNotified := false
|
|
||||||
recvNotified := false
|
|
||||||
|
|
||||||
for _, txin := range tx.MsgTx().TxIn {
|
|
||||||
if _, ok := lookups.unspent[txin.PreviousOutPoint]; ok {
|
|
||||||
delete(lookups.unspent, txin.PreviousOutPoint)
|
|
||||||
|
|
||||||
if spentNotified {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if txHex == "" {
|
|
||||||
txHex = txHexString(tx.MsgTx())
|
|
||||||
}
|
|
||||||
marshalledJSON, err := newRedeemingTxNotification(txHex, tx.Index(), blk)
|
|
||||||
if err != nil {
|
|
||||||
rpcsLog.Errorf("Failed to marshal redeemingtx notification: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err = wsc.QueueNotification(marshalledJSON)
|
|
||||||
// Stop the rescan early if the websocket client
|
|
||||||
// disconnected.
|
|
||||||
if err == ErrClientQuit {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
spentNotified = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for txOutIdx, txout := range tx.MsgTx().TxOut {
|
|
||||||
_, addrs, _, _ := txscript.ExtractPkScriptAddrs(
|
|
||||||
txout.PkScript, wsc.server.cfg.ChainParams)
|
|
||||||
|
|
||||||
for _, addr := range addrs {
|
|
||||||
switch a := addr.(type) {
|
|
||||||
case *btcutil.AddressPubKeyHash:
|
|
||||||
if _, ok := lookups.pubKeyHashes[*a.Hash160()]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
case *btcutil.AddressScriptHash:
|
|
||||||
if _, ok := lookups.scriptHashes[*a.Hash160()]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
case *btcutil.AddressPubKey:
|
|
||||||
found := false
|
|
||||||
switch sa := a.ScriptAddress(); len(sa) {
|
|
||||||
case 33: // Compressed
|
|
||||||
var key [33]byte
|
|
||||||
copy(key[:], sa)
|
|
||||||
if _, ok := lookups.compressedPubKeys[key]; ok {
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
|
|
||||||
case 65: // Uncompressed
|
|
||||||
var key [65]byte
|
|
||||||
copy(key[:], sa)
|
|
||||||
if _, ok := lookups.uncompressedPubKeys[key]; ok {
|
|
||||||
found = true
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
rpcsLog.Warnf("Skipping rescanned pubkey of unknown "+
|
|
||||||
"serialized length %d", len(sa))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the transaction output pays to the pubkey of
|
|
||||||
// a rescanned P2PKH address, include it as well.
|
|
||||||
if !found {
|
|
||||||
pkh := a.AddressPubKeyHash()
|
|
||||||
if _, ok := lookups.pubKeyHashes[*pkh.Hash160()]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// A new address type must have been added. Encode as a
|
|
||||||
// payment address string and check the fallback map.
|
|
||||||
addrStr := addr.EncodeAddress()
|
|
||||||
_, ok := lookups.fallbacks[addrStr]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outpoint := wire.OutPoint{
|
|
||||||
Hash: *tx.Hash(),
|
|
||||||
Index: uint32(txOutIdx),
|
|
||||||
}
|
|
||||||
lookups.unspent[outpoint] = struct{}{}
|
|
||||||
|
|
||||||
if recvNotified {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if txHex == "" {
|
|
||||||
txHex = txHexString(tx.MsgTx())
|
|
||||||
}
|
|
||||||
ntfn := btcjson.NewRecvTxNtfn(txHex,
|
|
||||||
blockDetails(blk, tx.Index()))
|
|
||||||
|
|
||||||
marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn)
|
|
||||||
if err != nil {
|
|
||||||
rpcsLog.Errorf("Failed to marshal recvtx notification: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = wsc.QueueNotification(marshalledJSON)
|
|
||||||
// Stop the rescan early if the websocket client
|
|
||||||
// disconnected.
|
|
||||||
if err == ErrClientQuit {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
recvNotified = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rescanBlockFilter rescans a block for any relevant transactions for the
|
// rescanBlockFilter rescans a block for any relevant transactions for the
|
||||||
// passed lookup keys. Any discovered transactions are returned hex encoded as
|
// passed lookup keys. Any discovered transactions are returned hex encoded as
|
||||||
// a string slice.
|
// a string slice.
|
||||||
@ -2248,7 +2083,7 @@ func handleRescanBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) {
|
|||||||
Message: "Failed to fetch block: " + err.Error(),
|
Message: "Failed to fetch block: " + err.Error(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if lastBlockHash != nil && block.MsgBlock().Header.PrevBlock != *lastBlockHash {
|
if lastBlockHash != nil && block.MsgBlock().Header.PrevBlocks[0] != *lastBlockHash { // TODO: (Stas) This is likely wrong. Modified to satisfy compilation.
|
||||||
return nil, &btcjson.RPCError{
|
return nil, &btcjson.RPCError{
|
||||||
Code: btcjson.ErrRPCInvalidParameter,
|
Code: btcjson.ErrRPCInvalidParameter,
|
||||||
Message: fmt.Sprintf("Block %v is not a child of %v",
|
Message: fmt.Sprintf("Block %v is not a child of %v",
|
||||||
@ -2269,375 +2104,6 @@ func handleRescanBlocks(wsc *wsClient, icmd interface{}) (interface{}, error) {
|
|||||||
return &discoveredData, nil
|
return &discoveredData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// recoverFromReorg attempts to recover from a detected reorganize during a
|
|
||||||
// rescan. It fetches a new range of block shas from the database and
|
|
||||||
// verifies that the new range of blocks is on the same fork as a previous
|
|
||||||
// range of blocks. If this condition does not hold true, the JSON-RPC error
|
|
||||||
// for an unrecoverable reorganize is returned.
|
|
||||||
func recoverFromReorg(dag *blockdag.BlockDAG, minBlock, maxBlock int32,
|
|
||||||
lastBlock *daghash.Hash) ([]daghash.Hash, error) {
|
|
||||||
|
|
||||||
hashList, err := dag.HeightRange(minBlock, maxBlock)
|
|
||||||
if err != nil {
|
|
||||||
rpcsLog.Errorf("Error looking up block range: %v", err)
|
|
||||||
return nil, &btcjson.RPCError{
|
|
||||||
Code: btcjson.ErrRPCDatabase,
|
|
||||||
Message: "Database error: " + err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if lastBlock == nil || len(hashList) == 0 {
|
|
||||||
return hashList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
blk, err := dag.BlockByHash(&hashList[0])
|
|
||||||
if err != nil {
|
|
||||||
rpcsLog.Errorf("Error looking up possibly reorged block: %v",
|
|
||||||
err)
|
|
||||||
return nil, &btcjson.RPCError{
|
|
||||||
Code: btcjson.ErrRPCDatabase,
|
|
||||||
Message: "Database error: " + err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jsonErr := descendantBlock(lastBlock, blk)
|
|
||||||
if jsonErr != nil {
|
|
||||||
return nil, jsonErr
|
|
||||||
}
|
|
||||||
return hashList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// descendantBlock returns the appropriate JSON-RPC error if a current block
|
|
||||||
// fetched during a reorganize is not a direct child of the parent block hash.
|
|
||||||
func descendantBlock(prevHash *daghash.Hash, curBlock *btcutil.Block) error {
|
|
||||||
curHash := &curBlock.MsgBlock().Header.PrevBlock
|
|
||||||
if !prevHash.IsEqual(curHash) {
|
|
||||||
rpcsLog.Errorf("Stopping rescan for reorged block %v "+
|
|
||||||
"(replaced by block %v)", prevHash, curHash)
|
|
||||||
return &ErrRescanReorg
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleRescan implements the rescan command extension for websocket
|
|
||||||
// connections.
|
|
||||||
//
|
|
||||||
// NOTE: This does not smartly handle reorgs, and fixing requires database
|
|
||||||
// changes (for safe, concurrent access to full block ranges, and support
|
|
||||||
// for other chains than the best chain). It will, however, detect whether
|
|
||||||
// a reorg removed a block that was previously processed, and result in the
|
|
||||||
// handler erroring. Clients must handle this by finding a block still in
|
|
||||||
// the chain (perhaps from a rescanprogress notification) to resume their
|
|
||||||
// rescan.
|
|
||||||
func handleRescan(wsc *wsClient, icmd interface{}) (interface{}, error) {
|
|
||||||
cmd, ok := icmd.(*btcjson.RescanCmd)
|
|
||||||
if !ok {
|
|
||||||
return nil, btcjson.ErrRPCInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
outpoints := make([]*wire.OutPoint, 0, len(cmd.OutPoints))
|
|
||||||
for i := range cmd.OutPoints {
|
|
||||||
cmdOutpoint := &cmd.OutPoints[i]
|
|
||||||
blockHash, err := daghash.NewHashFromStr(cmdOutpoint.Hash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, rpcDecodeHexError(cmdOutpoint.Hash)
|
|
||||||
}
|
|
||||||
outpoint := wire.NewOutPoint(blockHash, cmdOutpoint.Index)
|
|
||||||
outpoints = append(outpoints, outpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
numAddrs := len(cmd.Addresses)
|
|
||||||
if numAddrs == 1 {
|
|
||||||
rpcsLog.Info("Beginning rescan for 1 address")
|
|
||||||
} else {
|
|
||||||
rpcsLog.Infof("Beginning rescan for %d addresses", numAddrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build lookup maps.
|
|
||||||
lookups := rescanKeys{
|
|
||||||
fallbacks: map[string]struct{}{},
|
|
||||||
pubKeyHashes: map[[ripemd160.Size]byte]struct{}{},
|
|
||||||
scriptHashes: map[[ripemd160.Size]byte]struct{}{},
|
|
||||||
compressedPubKeys: map[[33]byte]struct{}{},
|
|
||||||
uncompressedPubKeys: map[[65]byte]struct{}{},
|
|
||||||
unspent: map[wire.OutPoint]struct{}{},
|
|
||||||
}
|
|
||||||
var compressedPubkey [33]byte
|
|
||||||
var uncompressedPubkey [65]byte
|
|
||||||
params := wsc.server.cfg.ChainParams
|
|
||||||
for _, addrStr := range cmd.Addresses {
|
|
||||||
addr, err := btcutil.DecodeAddress(addrStr, params)
|
|
||||||
if err != nil {
|
|
||||||
jsonErr := btcjson.RPCError{
|
|
||||||
Code: btcjson.ErrRPCInvalidAddressOrKey,
|
|
||||||
Message: "Rescan address " + addrStr + ": " +
|
|
||||||
err.Error(),
|
|
||||||
}
|
|
||||||
return nil, &jsonErr
|
|
||||||
}
|
|
||||||
switch a := addr.(type) {
|
|
||||||
case *btcutil.AddressPubKeyHash:
|
|
||||||
lookups.pubKeyHashes[*a.Hash160()] = struct{}{}
|
|
||||||
|
|
||||||
case *btcutil.AddressScriptHash:
|
|
||||||
lookups.scriptHashes[*a.Hash160()] = struct{}{}
|
|
||||||
|
|
||||||
case *btcutil.AddressPubKey:
|
|
||||||
pubkeyBytes := a.ScriptAddress()
|
|
||||||
switch len(pubkeyBytes) {
|
|
||||||
case 33: // Compressed
|
|
||||||
copy(compressedPubkey[:], pubkeyBytes)
|
|
||||||
lookups.compressedPubKeys[compressedPubkey] = struct{}{}
|
|
||||||
|
|
||||||
case 65: // Uncompressed
|
|
||||||
copy(uncompressedPubkey[:], pubkeyBytes)
|
|
||||||
lookups.uncompressedPubKeys[uncompressedPubkey] = struct{}{}
|
|
||||||
|
|
||||||
default:
|
|
||||||
jsonErr := btcjson.RPCError{
|
|
||||||
Code: btcjson.ErrRPCInvalidAddressOrKey,
|
|
||||||
Message: "Pubkey " + addrStr + " is of unknown length",
|
|
||||||
}
|
|
||||||
return nil, &jsonErr
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// A new address type must have been added. Use encoded
|
|
||||||
// payment address string as a fallback until a fast path
|
|
||||||
// is added.
|
|
||||||
lookups.fallbacks[addrStr] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, outpoint := range outpoints {
|
|
||||||
lookups.unspent[*outpoint] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
dag := wsc.server.cfg.DAG
|
|
||||||
|
|
||||||
minBlockHash, err := daghash.NewHashFromStr(cmd.BeginBlock)
|
|
||||||
if err != nil {
|
|
||||||
return nil, rpcDecodeHexError(cmd.BeginBlock)
|
|
||||||
}
|
|
||||||
minBlock, err := dag.BlockHeightByHash(minBlockHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, &btcjson.RPCError{
|
|
||||||
Code: btcjson.ErrRPCBlockNotFound,
|
|
||||||
Message: "Error getting block: " + err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
maxBlock := int32(math.MaxInt32)
|
|
||||||
if cmd.EndBlock != nil {
|
|
||||||
maxBlockHash, err := daghash.NewHashFromStr(*cmd.EndBlock)
|
|
||||||
if err != nil {
|
|
||||||
return nil, rpcDecodeHexError(*cmd.EndBlock)
|
|
||||||
}
|
|
||||||
maxBlock, err = dag.BlockHeightByHash(maxBlockHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, &btcjson.RPCError{
|
|
||||||
Code: btcjson.ErrRPCBlockNotFound,
|
|
||||||
Message: "Error getting block: " + err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lastBlock and lastBlockHash track the previously-rescanned block.
|
|
||||||
// They equal nil when no previous blocks have been rescanned.
|
|
||||||
var lastBlock *btcutil.Block
|
|
||||||
var lastBlockHash *daghash.Hash
|
|
||||||
|
|
||||||
// A ticker is created to wait at least 10 seconds before notifying the
|
|
||||||
// websocket client of the current progress completed by the rescan.
|
|
||||||
ticker := time.NewTicker(10 * time.Second)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
// Instead of fetching all block shas at once, fetch in smaller chunks
|
|
||||||
// to ensure large rescans consume a limited amount of memory.
|
|
||||||
fetchRange:
|
|
||||||
for minBlock < maxBlock {
|
|
||||||
// Limit the max number of hashes to fetch at once to the
|
|
||||||
// maximum number of items allowed in a single inventory.
|
|
||||||
// This value could be higher since it's not creating inventory
|
|
||||||
// messages, but this mirrors the limiting logic used in the
|
|
||||||
// peer-to-peer protocol.
|
|
||||||
maxLoopBlock := maxBlock
|
|
||||||
if maxLoopBlock-minBlock > wire.MaxInvPerMsg {
|
|
||||||
maxLoopBlock = minBlock + wire.MaxInvPerMsg
|
|
||||||
}
|
|
||||||
hashList, err := dag.HeightRange(minBlock, maxLoopBlock)
|
|
||||||
if err != nil {
|
|
||||||
rpcsLog.Errorf("Error looking up block range: %v", err)
|
|
||||||
return nil, &btcjson.RPCError{
|
|
||||||
Code: btcjson.ErrRPCDatabase,
|
|
||||||
Message: "Database error: " + err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(hashList) == 0 {
|
|
||||||
// The rescan is finished if no blocks hashes for this
|
|
||||||
// range were successfully fetched and a stop block
|
|
||||||
// was provided.
|
|
||||||
if maxBlock != math.MaxInt32 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the rescan is through the current block, set up
|
|
||||||
// the client to continue to receive notifications
|
|
||||||
// regarding all rescanned addresses and the current set
|
|
||||||
// of unspent outputs.
|
|
||||||
//
|
|
||||||
// This is done safely by temporarily grabbing exclusive
|
|
||||||
// access of the block manager. If no more blocks have
|
|
||||||
// been attached between this pause and the fetch above,
|
|
||||||
// then it is safe to register the websocket client for
|
|
||||||
// continuous notifications if necessary. Otherwise,
|
|
||||||
// continue the fetch loop again to rescan the new
|
|
||||||
// blocks (or error due to an irrecoverable reorganize).
|
|
||||||
pauseGuard := wsc.server.cfg.SyncMgr.Pause()
|
|
||||||
dagState := wsc.server.cfg.DAG.GetDAGState()
|
|
||||||
curHash := &dagState.SelectedTip.Hash
|
|
||||||
again := true
|
|
||||||
if lastBlockHash == nil || *lastBlockHash == *curHash {
|
|
||||||
again = false
|
|
||||||
n := wsc.server.ntfnMgr
|
|
||||||
n.RegisterSpentRequests(wsc, lookups.unspentSlice())
|
|
||||||
n.RegisterTxOutAddressRequests(wsc, cmd.Addresses)
|
|
||||||
}
|
|
||||||
close(pauseGuard)
|
|
||||||
if err != nil {
|
|
||||||
rpcsLog.Errorf("Error fetching best block "+
|
|
||||||
"hash: %v", err)
|
|
||||||
return nil, &btcjson.RPCError{
|
|
||||||
Code: btcjson.ErrRPCDatabase,
|
|
||||||
Message: "Database error: " +
|
|
||||||
err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if again {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
loopHashList:
|
|
||||||
for i := range hashList {
|
|
||||||
blk, err := dag.BlockByHash(&hashList[i])
|
|
||||||
if err != nil {
|
|
||||||
// Only handle reorgs if a block could not be
|
|
||||||
// found for the hash.
|
|
||||||
if dbErr, ok := err.(database.Error); !ok ||
|
|
||||||
dbErr.ErrorCode != database.ErrBlockNotFound {
|
|
||||||
|
|
||||||
rpcsLog.Errorf("Error looking up "+
|
|
||||||
"block: %v", err)
|
|
||||||
return nil, &btcjson.RPCError{
|
|
||||||
Code: btcjson.ErrRPCDatabase,
|
|
||||||
Message: "Database error: " +
|
|
||||||
err.Error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If an absolute max block was specified, don't
|
|
||||||
// attempt to handle the reorg.
|
|
||||||
if maxBlock != math.MaxInt32 {
|
|
||||||
rpcsLog.Errorf("Stopping rescan for "+
|
|
||||||
"reorged block %v",
|
|
||||||
cmd.EndBlock)
|
|
||||||
return nil, &ErrRescanReorg
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the lookup for the previously valid block
|
|
||||||
// hash failed, there may have been a reorg.
|
|
||||||
// Fetch a new range of block hashes and verify
|
|
||||||
// that the previously processed block (if there
|
|
||||||
// was any) still exists in the database. If it
|
|
||||||
// doesn't, we error.
|
|
||||||
//
|
|
||||||
// A goto is used to branch executation back to
|
|
||||||
// before the range was evaluated, as it must be
|
|
||||||
// reevaluated for the new hashList.
|
|
||||||
minBlock += int32(i)
|
|
||||||
hashList, err = recoverFromReorg(dag,
|
|
||||||
minBlock, maxBlock, lastBlockHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(hashList) == 0 {
|
|
||||||
break fetchRange
|
|
||||||
}
|
|
||||||
goto loopHashList
|
|
||||||
}
|
|
||||||
if i == 0 && lastBlockHash != nil {
|
|
||||||
// Ensure the new hashList is on the same fork
|
|
||||||
// as the last block from the old hashList.
|
|
||||||
jsonErr := descendantBlock(lastBlockHash, blk)
|
|
||||||
if jsonErr != nil {
|
|
||||||
return nil, jsonErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A select statement is used to stop rescans if the
|
|
||||||
// client requesting the rescan has disconnected.
|
|
||||||
select {
|
|
||||||
case <-wsc.quit:
|
|
||||||
rpcsLog.Debugf("Stopped rescan at height %v "+
|
|
||||||
"for disconnected client", blk.Height())
|
|
||||||
return nil, nil
|
|
||||||
default:
|
|
||||||
rescanBlock(wsc, &lookups, blk)
|
|
||||||
lastBlock = blk
|
|
||||||
lastBlockHash = blk.Hash()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Periodically notify the client of the progress
|
|
||||||
// completed. Continue with next block if no progress
|
|
||||||
// notification is needed yet.
|
|
||||||
select {
|
|
||||||
case <-ticker.C: // fallthrough
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
n := btcjson.NewRescanProgressNtfn(hashList[i].String(),
|
|
||||||
blk.Height(), blk.MsgBlock().Header.Timestamp.Unix())
|
|
||||||
mn, err := btcjson.MarshalCmd(nil, n)
|
|
||||||
if err != nil {
|
|
||||||
rpcsLog.Errorf("Failed to marshal rescan "+
|
|
||||||
"progress notification: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = wsc.QueueNotification(mn); err == ErrClientQuit {
|
|
||||||
// Finished if the client disconnected.
|
|
||||||
rpcsLog.Debugf("Stopped rescan at height %v "+
|
|
||||||
"for disconnected client", blk.Height())
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
minBlock += int32(len(hashList))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify websocket client of the finished rescan. Due to how btcd
|
|
||||||
// asynchronously queues notifications to not block calling code,
|
|
||||||
// there is no guarantee that any of the notifications created during
|
|
||||||
// rescan (such as rescanprogress, recvtx and redeemingtx) will be
|
|
||||||
// received before the rescan RPC returns. Therefore, another method
|
|
||||||
// is needed to safely inform clients that all rescan notifications have
|
|
||||||
// been sent.
|
|
||||||
n := btcjson.NewRescanFinishedNtfn(lastBlockHash.String(),
|
|
||||||
lastBlock.Height(),
|
|
||||||
lastBlock.MsgBlock().Header.Timestamp.Unix())
|
|
||||||
if mn, err := btcjson.MarshalCmd(nil, n); err != nil {
|
|
||||||
rpcsLog.Errorf("Failed to marshal rescan finished "+
|
|
||||||
"notification: %v", err)
|
|
||||||
} else {
|
|
||||||
// The rescan is finished, so we don't care whether the client
|
|
||||||
// has disconnected at this point, so discard error.
|
|
||||||
_ = wsc.QueueNotification(mn)
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcsLog.Info("Finished rescan")
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
wsHandlers = wsHandlersBeforeInit
|
wsHandlers = wsHandlersBeforeInit
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user