Add the new RPC function existsmempooltxs

A new RPC function has been added to the daemon to quickly check for
the existence of transactions in the mempool. It handles raw encoded
hex containing transaction hashes and outputs a raw encoded hex of
bit flags. This makes the output size as small as possible while
speeding up the JSON encoding/decoding of the data.
This commit is contained in:
cjepson 2016-03-01 19:45:33 -05:00
parent b237418c14
commit cd8ea2e309
4 changed files with 121 additions and 9 deletions

View File

@ -46,6 +46,19 @@ func NewExistsLiveTicketsCmd(txHashBlob string) *ExistsLiveTicketsCmd {
}
}
// ExistsMempoolTxsCmd defines the existsmempooltxs JSON-RPC command.
type ExistsMempoolTxsCmd struct {
TxHashBlob string
}
// NewExistsMempoolTxsCmd returns a new instance which can be used to issue an
// existslivetickets JSON-RPC command.
func NewExistsMempoolTxsCmd(txHashBlob string) *ExistsMempoolTxsCmd {
return &ExistsMempoolTxsCmd{
TxHashBlob: txHashBlob,
}
}
// GetCoinSupplyCmd defines the getcoinsupply JSON-RPC command.
type GetCoinSupplyCmd struct{}
@ -113,6 +126,7 @@ func init() {
MustRegisterCmd("existsaddress", (*ExistsAddressCmd)(nil), flags)
MustRegisterCmd("existsliveticket", (*ExistsLiveTicketCmd)(nil), flags)
MustRegisterCmd("existslivetickets", (*ExistsLiveTicketsCmd)(nil), flags)
MustRegisterCmd("existsmempooltxs", (*ExistsMempoolTxsCmd)(nil), flags)
MustRegisterCmd("getcoinsupply", (*GetCoinSupplyCmd)(nil), flags)
MustRegisterCmd("getstakedifficulty", (*GetStakeDifficultyCmd)(nil), flags)
MustRegisterCmd("missedtickets", (*MissedTicketsCmd)(nil), flags)

View File

@ -925,6 +925,30 @@ func (mp *txMemPool) HaveTransaction(hash *chainhash.Hash) bool {
return mp.haveTransaction(hash)
}
// haveTransactions returns whether or not the passed transactions already exist
// in the main pool or in the orphan pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) haveTransactions(hashes []*chainhash.Hash) []bool {
have := make([]bool, len(hashes))
for i := range hashes {
have[i] = mp.haveTransaction(hashes[i])
}
return have
}
// HaveTransactions returns whether or not the passed transactions already exist
// in the main pool or in the orphan pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) HaveTransactions(hashes []*chainhash.Hash) []bool {
// Protect concurrent access.
mp.RLock()
defer mp.RUnlock()
return mp.haveTransactions(hashes)
}
// removeTransaction is the internal function which implements the public
// RemoveTransaction. See the comment for RemoveTransaction for more details.
//

View File

@ -157,6 +157,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
"existsaddress": handleExistsAddress,
"existsliveticket": handleExistsLiveTicket,
"existslivetickets": handleExistsLiveTickets,
"existsmempooltxs": handleExistsMempoolTxs,
"generate": handleGenerate,
"getaddednodeinfo": handleGetAddedNodeInfo,
"getbestblock": handleGetBestBlock,
@ -1515,16 +1516,18 @@ func handleExistsLiveTickets(s *rpcServer, cmd interface{},
txHashBlob, err := hex.DecodeString(c.TxHashBlob)
if err != nil {
return nil, &dcrjson.RPCError{
Code: dcrjson.ErrRPCDecodeHexString,
Message: "bad transaction hash blob (unparseable)",
Code: dcrjson.ErrRPCDecodeHexString,
Message: fmt.Sprintf("bad ticket hash blob (unparseable): %v",
err.Error()),
}
}
// 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)",
Code: dcrjson.ErrRPCDecodeHexString,
Message: fmt.Sprintf("bad ticket hash blob (bad length): %v",
len(txHashBlob)),
}
}
@ -1533,6 +1536,13 @@ func handleExistsLiveTickets(s *rpcServer, cmd interface{},
for i := 0; i < hashesLen; i++ {
hashes[i], err = chainhash.NewHash(
txHashBlob[i*chainhash.HashSize : (i+1)*chainhash.HashSize])
if err != nil {
return nil, &dcrjson.RPCError{
Code: dcrjson.ErrRPCDecodeHexString,
Message: fmt.Sprintf("bad ticket hash: %v",
err.Error()),
}
}
}
exists, err := s.server.blockManager.ExistsLiveTickets(hashes)
@ -1541,8 +1551,66 @@ func handleExistsLiveTickets(s *rpcServer, cmd interface{},
}
if len(exists) != hashesLen {
return nil, &dcrjson.RPCError{
Code: dcrjson.ErrRPCDatabase,
Message: "output of ExistsLiveTickets wrong size",
Code: dcrjson.ErrRPCDatabase,
Message: fmt.Sprintf("output of ExistsLiveTickets wrong size "+
"(want %v, got %v)", hashesLen, len(exists)),
}
}
// 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
}
// handleExistsMempoolTxs implements the existsmempooltxs command.
func handleExistsMempoolTxs(s *rpcServer, cmd interface{},
closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*dcrjson.ExistsMempoolTxsCmd)
txHashBlob, err := hex.DecodeString(c.TxHashBlob)
if err != nil {
return nil, &dcrjson.RPCError{
Code: dcrjson.ErrRPCDecodeHexString,
Message: fmt.Sprintf("bad transaction hash blob (unparseable): %v",
err.Error()),
}
}
// It needs to be an exact number of hashes.
if len(txHashBlob)%32 != 0 {
return nil, &dcrjson.RPCError{
Code: dcrjson.ErrRPCDecodeHexString,
Message: fmt.Sprintf("bad transaction hash blob (bad length): %v",
len(txHashBlob)),
}
}
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])
if err != nil {
return nil, &dcrjson.RPCError{
Code: dcrjson.ErrRPCDecodeHexString,
Message: fmt.Sprintf("bad transaction hash: %v",
err.Error()),
}
}
}
exists := s.server.txMemPool.HaveTransactions(hashes)
if len(exists) != hashesLen {
return nil, &dcrjson.RPCError{
Code: dcrjson.ErrRPCDatabase,
Message: fmt.Sprintf("output of ExistsMempoolTxs wrong size "+
"(want %v, got %v)", hashesLen, len(exists)),
}
}

View File

@ -153,9 +153,14 @@ var helpDescsEnUS = map[string]string{
"existsliveticket--result0": "Bool showing if address exists in the live ticket database or not",
// ExistsLiveTicketsCmd help.
"existslivetickets--synopsis": "Test for the existance of the provided tickets",
"existslivetickets--synopsis": "Test for the existance of the provided tickets in the live ticket map",
"existslivetickets-txhashblob": "Blob containing the hashes to check",
"existslivetickets--result0": "Bool showing if address exists in the live ticket database or not",
"existslivetickets--result0": "Bool blob showing if ticket exists in the live ticket database or not",
// ExistsMempoolTxsCmd help.
"existsmempooltxs--synopsis": "Test for the existance of the provided txs in the mempool",
"existsmempooltxs-txhashblob": "Blob containing the hashes to check",
"existsmempooltxs--result0": "Bool blob showing if txs exist in the mempool or not",
// GenerateCmd help
"generate--synopsis": "Generates a set number of blocks (simnet or regtest only) and returns a JSON\n" +
@ -669,7 +674,8 @@ var rpcResultTypes = map[string][]interface{}{
"estimatefee": []interface{}{(*float64)(nil)},
"existsaddress": []interface{}{(*bool)(nil)},
"existsliveticket": []interface{}{(*bool)(nil)},
"existslivetickets": []interface{}{(*bool)(nil)},
"existslivetickets": []interface{}{(*string)(nil)},
"existsmempooltxs": []interface{}{(*string)(nil)},
"getaddednodeinfo": []interface{}{(*[]string)(nil), (*[]dcrjson.GetAddedNodeInfoResult)(nil)},
"getbestblock": []interface{}{(*dcrjson.GetBestBlockResult)(nil)},
"generate": []interface{}{(*[]string)(nil)},