Add handling of modified getrawmempool RPC call and existslivetickets
getrawmempool has been modified to allow for the selection of specific transaction types from the mempool. A new RPC call, existslivetickets, has been added. This call takes a blob of ticket hashes and returns a blob of bit flags specifying whether or not the tickets exist. This allows for much faster getstakeinfo calls in the wallet.
This commit is contained in:
parent
1652a0db40
commit
18acc1194f
@ -232,6 +232,23 @@ func (b *BlockChain) CheckLiveTicket(hash *chainhash.Hash) (bool, error) {
|
||||
return b.tmdb.CheckLiveTicket(*hash)
|
||||
}
|
||||
|
||||
// CheckLiveTickets returns whether or not a slice of tickets exist in the live
|
||||
// ticket map of the stake database.
|
||||
//
|
||||
// This function is NOT safe for concurrent access.
|
||||
func (b *BlockChain) CheckLiveTickets(hashes []*chainhash.Hash) ([]bool, error) {
|
||||
var err error
|
||||
existsSlice := make([]bool, len(hashes))
|
||||
for i, hash := range hashes {
|
||||
existsSlice[i], err = b.tmdb.CheckLiveTicket(*hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return existsSlice, nil
|
||||
}
|
||||
|
||||
// HaveBlock returns whether or not the chain instance has the block represented
|
||||
// by the passed hash. This includes checking the various places a block can
|
||||
// be like part of the main chain, on a side chain, or in the orphan pool.
|
||||
|
||||
@ -350,6 +350,21 @@ type existsLiveTicketResponse struct {
|
||||
err error
|
||||
}
|
||||
|
||||
// existsLiveTicketsMsg handles a request for obtaining whether or not a ticket
|
||||
// from a slice of tickets exists in the live tickets map of the blockchain stake
|
||||
// database.
|
||||
type existsLiveTicketsMsg struct {
|
||||
hashes []*chainhash.Hash
|
||||
reply chan existsLiveTicketsResponse
|
||||
}
|
||||
|
||||
// existsLiveTicketsResponse is a response to the reply channel of a
|
||||
// existsLiveTicketsMsg.
|
||||
type existsLiveTicketsResponse struct {
|
||||
Exists []bool
|
||||
err error
|
||||
}
|
||||
|
||||
// getCurrentTemplateMsg handles a request for the current mining block template.
|
||||
type getCurrentTemplateMsg struct {
|
||||
reply chan getCurrentTemplateResponse
|
||||
@ -1985,6 +2000,13 @@ out:
|
||||
err: err,
|
||||
}
|
||||
|
||||
case existsLiveTicketsMsg:
|
||||
exists, err := b.blockChain.CheckLiveTickets(msg.hashes)
|
||||
msg.reply <- existsLiveTicketsResponse{
|
||||
Exists: exists,
|
||||
err: err,
|
||||
}
|
||||
|
||||
case getCurrentTemplateMsg:
|
||||
cur := deepCopyBlockTemplate(b.cachedCurrentTemplate)
|
||||
msg.reply <- getCurrentTemplateResponse{
|
||||
@ -2657,6 +2679,15 @@ func (b *blockManager) ExistsLiveTicket(hash *chainhash.Hash) (bool, error) {
|
||||
return response.Exists, response.err
|
||||
}
|
||||
|
||||
// ExistsLiveTickets returns whether or not tickets in a slice of tickets exist
|
||||
// in the live tickets database.
|
||||
func (b *blockManager) ExistsLiveTickets(hashes []*chainhash.Hash) ([]bool, error) {
|
||||
reply := make(chan existsLiveTicketsResponse)
|
||||
b.msgChan <- existsLiveTicketsMsg{hashes: hashes, reply: reply}
|
||||
response := <-reply
|
||||
return response.Exists, response.err
|
||||
}
|
||||
|
||||
// GetCurrentTemplate gets the current block template for mining.
|
||||
func (b *blockManager) GetCurrentTemplate() *BlockTemplate {
|
||||
reply := make(chan getCurrentTemplateResponse)
|
||||
|
||||
@ -392,9 +392,31 @@ func NewGetPeerInfoCmd() *GetPeerInfoCmd {
|
||||
return &GetPeerInfoCmd{}
|
||||
}
|
||||
|
||||
// GetRawMempoolTxTypeCmd defines the type used in the getrawmempool JSON-RPC
|
||||
// command for the TxType command field.
|
||||
type GetRawMempoolTxTypeCmd string
|
||||
|
||||
const (
|
||||
// GRMAll indicates any type of transaction should be returned.
|
||||
GRMAll GetRawMempoolTxTypeCmd = "add"
|
||||
|
||||
// GRMRegular indicates only regular transactions should be returned.
|
||||
GRMRegular GetRawMempoolTxTypeCmd = "regular"
|
||||
|
||||
// GRMTickets indicates that only tickets should be returned.
|
||||
GRMTickets GetRawMempoolTxTypeCmd = "tickets"
|
||||
|
||||
// GRMVotes indicates that only votes should be returned.
|
||||
GRMVotes GetRawMempoolTxTypeCmd = "votes"
|
||||
|
||||
// GRMRevocations indicates that only revocations should be returned.
|
||||
GRMRevocations GetRawMempoolTxTypeCmd = "revocations"
|
||||
)
|
||||
|
||||
// GetRawMempoolCmd defines the getmempool JSON-RPC command.
|
||||
type GetRawMempoolCmd struct {
|
||||
Verbose *bool `jsonrpcdefault:"false"`
|
||||
TxType *string
|
||||
}
|
||||
|
||||
// NewGetRawMempoolCmd returns a new instance which can be used to issue a
|
||||
@ -402,9 +424,10 @@ type GetRawMempoolCmd struct {
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetRawMempoolCmd(verbose *bool) *GetRawMempoolCmd {
|
||||
func NewGetRawMempoolCmd(verbose *bool, txType *string) *GetRawMempoolCmd {
|
||||
return &GetRawMempoolCmd{
|
||||
Verbose: verbose,
|
||||
TxType: txType,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -452,7 +452,7 @@ func TestChainSvrCmds(t *testing.T) {
|
||||
return dcrjson.NewCmd("getrawmempool")
|
||||
},
|
||||
staticCmd: func() interface{} {
|
||||
return dcrjson.NewGetRawMempoolCmd(nil)
|
||||
return dcrjson.NewGetRawMempoolCmd(nil, nil)
|
||||
},
|
||||
marshalled: `{"jsonrpc":"1.0","method":"getrawmempool","params":[],"id":1}`,
|
||||
unmarshalled: &dcrjson.GetRawMempoolCmd{
|
||||
@ -465,13 +465,27 @@ func TestChainSvrCmds(t *testing.T) {
|
||||
return dcrjson.NewCmd("getrawmempool", false)
|
||||
},
|
||||
staticCmd: func() interface{} {
|
||||
return dcrjson.NewGetRawMempoolCmd(dcrjson.Bool(false))
|
||||
return dcrjson.NewGetRawMempoolCmd(dcrjson.Bool(false), nil)
|
||||
},
|
||||
marshalled: `{"jsonrpc":"1.0","method":"getrawmempool","params":[false],"id":1}`,
|
||||
unmarshalled: &dcrjson.GetRawMempoolCmd{
|
||||
Verbose: dcrjson.Bool(false),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getrawmempool optional 2",
|
||||
newCmd: func() (interface{}, error) {
|
||||
return dcrjson.NewCmd("getrawmempool", false, "all")
|
||||
},
|
||||
staticCmd: func() interface{} {
|
||||
return dcrjson.NewGetRawMempoolCmd(dcrjson.Bool(false), dcrjson.String("all"))
|
||||
},
|
||||
marshalled: `{"jsonrpc":"1.0","method":"getrawmempool","params":[false,"all"],"id":1}`,
|
||||
unmarshalled: &dcrjson.GetRawMempoolCmd{
|
||||
Verbose: dcrjson.Bool(false),
|
||||
TxType: dcrjson.String("all"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "getrawtransaction",
|
||||
newCmd: func() (interface{}, error) {
|
||||
|
||||
@ -33,6 +33,19 @@ func NewExistsLiveTicketCmd(txHash string) *ExistsLiveTicketCmd {
|
||||
}
|
||||
}
|
||||
|
||||
// ExistsLiveTicketsCmd defines the existslivetickets JSON-RPC command.
|
||||
type ExistsLiveTicketsCmd struct {
|
||||
TxHashBlob string
|
||||
}
|
||||
|
||||
// NewExistsLiveTicketsCmd returns a new instance which can be used to issue an
|
||||
// existslivetickets JSON-RPC command.
|
||||
func NewExistsLiveTicketsCmd(txHashBlob string) *ExistsLiveTicketsCmd {
|
||||
return &ExistsLiveTicketsCmd{
|
||||
TxHashBlob: txHashBlob,
|
||||
}
|
||||
}
|
||||
|
||||
// GetCoinSupplyCmd defines the getcoinsupply JSON-RPC command.
|
||||
type GetCoinSupplyCmd struct{}
|
||||
|
||||
@ -99,6 +112,7 @@ func init() {
|
||||
|
||||
MustRegisterCmd("existsaddress", (*ExistsAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("existsliveticket", (*ExistsLiveTicketCmd)(nil), flags)
|
||||
MustRegisterCmd("existslivetickets", (*ExistsLiveTicketsCmd)(nil), flags)
|
||||
MustRegisterCmd("getcoinsupply", (*GetCoinSupplyCmd)(nil), flags)
|
||||
MustRegisterCmd("getstakedifficulty", (*GetStakeDifficultyCmd)(nil), flags)
|
||||
MustRegisterCmd("missedtickets", (*MissedTicketsCmd)(nil), flags)
|
||||
|
||||
111
rpcserver.go
111
rpcserver.go
@ -31,6 +31,8 @@ import (
|
||||
"github.com/btcsuite/fastsha256"
|
||||
"github.com/btcsuite/websocket"
|
||||
|
||||
"github.com/decred/bitset"
|
||||
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
@ -40,6 +42,7 @@ import (
|
||||
"github.com/decred/dcrd/dcrjson"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
@ -150,6 +153,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
|
||||
"estimatefee": handleEstimateFee,
|
||||
"existsaddress": handleExistsAddress,
|
||||
"existsliveticket": handleExistsLiveTicket,
|
||||
"existslivetickets": handleExistsLiveTickets,
|
||||
"generate": handleGenerate,
|
||||
"getaddednodeinfo": handleGetAddedNodeInfo,
|
||||
"getbestblock": handleGetBestBlock,
|
||||
@ -1496,6 +1500,56 @@ func handleExistsLiveTicket(s *rpcServer, cmd interface{},
|
||||
return s.server.blockManager.ExistsLiveTicket(hash)
|
||||
}
|
||||
|
||||
// handleExistsLiveTickets implements the existslivetickets command.
|
||||
func handleExistsLiveTickets(s *rpcServer, cmd interface{},
|
||||
closeChan <-chan struct{}) (interface{}, error) {
|
||||
c := cmd.(*dcrjson.ExistsLiveTicketsCmd)
|
||||
|
||||
txHashBlob, err := hex.DecodeString(c.TxHashBlob)
|
||||
if err != nil {
|
||||
return nil, &dcrjson.RPCError{
|
||||
Code: dcrjson.ErrRPCDecodeHexString,
|
||||
Message: "bad transaction hash blob (unparseable)",
|
||||
}
|
||||
}
|
||||
|
||||
// It needs to be an exact number of hashes.
|
||||
if len(txHashBlob)%32 != 0 {
|
||||
return nil, &dcrjson.RPCError{
|
||||
Code: dcrjson.ErrRPCDecodeHexString,
|
||||
Message: "bad transaction hash blob (bad length)",
|
||||
}
|
||||
}
|
||||
|
||||
hashesLen := len(txHashBlob) / 32
|
||||
hashes := make([]*chainhash.Hash, hashesLen)
|
||||
for i := 0; i < hashesLen; i++ {
|
||||
hashes[i], err = chainhash.NewHash(
|
||||
txHashBlob[i*chainhash.HashSize : (i+1)*chainhash.HashSize])
|
||||
}
|
||||
|
||||
exists, err := s.server.blockManager.ExistsLiveTickets(hashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(exists) != hashesLen {
|
||||
return nil, &dcrjson.RPCError{
|
||||
Code: dcrjson.ErrRPCDatabase,
|
||||
Message: "output of ExistsLiveTickets wrong size",
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the slice of bools into a compacted set of bit flags.
|
||||
set := bitset.NewBytes(hashesLen)
|
||||
for i := range exists {
|
||||
if exists[i] {
|
||||
set.Set(i)
|
||||
}
|
||||
}
|
||||
|
||||
return hex.EncodeToString([]byte(set)), nil
|
||||
}
|
||||
|
||||
// handleGenerate handles generate commands.
|
||||
func handleGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||
// Respond with an error if there are no addresses to pay the
|
||||
@ -3179,6 +3233,32 @@ func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{
|
||||
mp.RLock()
|
||||
defer mp.RUnlock()
|
||||
for _, desc := range descs {
|
||||
// If we're interested in a specific transaction type,
|
||||
// skip all the transactions which are not this type.
|
||||
if c.TxType != nil {
|
||||
switch *c.TxType {
|
||||
case string(dcrjson.GRMRegular):
|
||||
if desc.Type != stake.TxTypeRegular {
|
||||
continue
|
||||
}
|
||||
case string(dcrjson.GRMTickets):
|
||||
if desc.Type != stake.TxTypeSStx {
|
||||
continue
|
||||
}
|
||||
case string(dcrjson.GRMVotes):
|
||||
if desc.Type != stake.TxTypeSSGen {
|
||||
continue
|
||||
}
|
||||
case string(dcrjson.GRMRevocations):
|
||||
if desc.Type != stake.TxTypeSSRtx {
|
||||
continue
|
||||
}
|
||||
case string(dcrjson.GRMAll):
|
||||
fallthrough
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the starting and current priority from the
|
||||
// the tx's inputs. Use zeros if one or more of the
|
||||
// input transactions can't be found for some reason.
|
||||
@ -3215,9 +3295,34 @@ func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{
|
||||
|
||||
// The response is simply an array of the transaction hashes if the
|
||||
// verbose flag is not set.
|
||||
hashStrings := make([]string, len(descs))
|
||||
for i := range hashStrings {
|
||||
hashStrings[i] = descs[i].Tx.Sha().String()
|
||||
descsLen := len(descs)
|
||||
hashStrings := make([]string, 0, descsLen)
|
||||
for i := 0; i < descsLen; i++ {
|
||||
if c.TxType != nil {
|
||||
switch *c.TxType {
|
||||
case string(dcrjson.GRMRegular):
|
||||
if descs[i].Type != stake.TxTypeRegular {
|
||||
continue
|
||||
}
|
||||
case string(dcrjson.GRMTickets):
|
||||
if descs[i].Type != stake.TxTypeSStx {
|
||||
continue
|
||||
}
|
||||
case string(dcrjson.GRMVotes):
|
||||
if descs[i].Type != stake.TxTypeSSGen {
|
||||
continue
|
||||
}
|
||||
case string(dcrjson.GRMRevocations):
|
||||
if descs[i].Type != stake.TxTypeSSRtx {
|
||||
continue
|
||||
}
|
||||
case string(dcrjson.GRMAll):
|
||||
fallthrough
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
hashStrings = append(hashStrings, descs[i].Tx.Sha().String())
|
||||
}
|
||||
|
||||
return hashStrings, nil
|
||||
|
||||
Loading…
Reference in New Issue
Block a user