Skip to content

Commit 83bb199

Browse files
Larry RuaneLarryRuane
authored andcommitted
restore gRPC GetMempoolTx
This was removed as part of PR #358 (commit "remove gRPC GetMempoolTx") but should not have been, so it's being restored.
1 parent c7b1be9 commit 83bb199

File tree

5 files changed

+511
-159
lines changed

5 files changed

+511
-159
lines changed

frontend/frontend_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,3 +540,58 @@ func TestNewZRPCFromConf(t *testing.T) {
540540
t.Fatal("NewZRPCFromClient unexpected success")
541541
}
542542
}
543+
544+
func TestMempoolFilter(t *testing.T) {
545+
txidlist := []string{
546+
"2e819d0bab5c819dc7d5f92d1bfb4127ce321daf847f6602",
547+
"29e594c312eee49bc2c9ad37367ba58f857c4a7387ec9715",
548+
"d4d090e60bf9141c6573f0598b84cc1f9817543e55a4d84d",
549+
"d4714779c6dd32a72077bd79d4a70cb2153b552d7addec15",
550+
"9839c1d4deca000656caff57c1f720f4fbd114b52239edde",
551+
"ce5a28854a509ab309faa433542e73414fef6e903a3d52f5",
552+
}
553+
exclude := []string{
554+
"98aa", // common prefix (98) but no match
555+
"19", // no match
556+
"29", // one match (should not appear)
557+
"d4", // 2 matches (both should appear in result)
558+
"ce5a28854a509ab309faa433542e73414fef6e903a3d52f5", // exact match
559+
"ce5a28854a509ab309faa433542e73414fef6e903a3d52f500", // extra stuff ignored
560+
}
561+
expected := []string{
562+
"2e819d0bab5c819dc7d5f92d1bfb4127ce321daf847f6602",
563+
"9839c1d4deca000656caff57c1f720f4fbd114b52239edde",
564+
"d4714779c6dd32a72077bd79d4a70cb2153b552d7addec15",
565+
"d4d090e60bf9141c6573f0598b84cc1f9817543e55a4d84d",
566+
}
567+
actual := MempoolFilter(txidlist, exclude)
568+
if len(actual) != len(expected) {
569+
t.Fatal("mempool: wrong number of filter results")
570+
}
571+
for i := 0; i < len(actual); i++ {
572+
if actual[i] != expected[i] {
573+
t.Fatal(fmt.Sprintf("mempool: expected: %s actual: %s",
574+
expected[i], actual[i]))
575+
}
576+
}
577+
// If the exclude list is empty, return the entire mempool.
578+
actual = MempoolFilter(txidlist, []string{})
579+
expected = []string{
580+
"29e594c312eee49bc2c9ad37367ba58f857c4a7387ec9715",
581+
"2e819d0bab5c819dc7d5f92d1bfb4127ce321daf847f6602",
582+
"9839c1d4deca000656caff57c1f720f4fbd114b52239edde",
583+
"ce5a28854a509ab309faa433542e73414fef6e903a3d52f5",
584+
"d4714779c6dd32a72077bd79d4a70cb2153b552d7addec15",
585+
"d4d090e60bf9141c6573f0598b84cc1f9817543e55a4d84d",
586+
}
587+
if len(actual) != len(expected) {
588+
t.Fatal("mempool: wrong number of filter results")
589+
}
590+
for i := 0; i < len(actual); i++ {
591+
if actual[i] != expected[i] {
592+
t.Fatal(fmt.Sprintf("mempool: expected: %s actual: %s",
593+
expected[i], actual[i]))
594+
}
595+
}
596+
597+
}

frontend/service.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"errors"
1313
"io"
1414
"regexp"
15+
"sort"
1516
"strconv"
1617
"strings"
1718
"sync/atomic"
@@ -391,6 +392,134 @@ func (s *lwdStreamer) GetMempoolStream(_empty *walletrpc.Empty, resp walletrpc.C
391392
return err
392393
}
393394

395+
// Key is 32-byte txid (as a 64-character string), data is pointer to compact tx.
396+
var mempoolMap *map[string]*walletrpc.CompactTx
397+
var mempoolList []string
398+
399+
// Last time we pulled a copy of the mempool from zcashd.
400+
var lastMempool time.Time
401+
402+
func (s *lwdStreamer) GetMempoolTx(exclude *walletrpc.Exclude, resp walletrpc.CompactTxStreamer_GetMempoolTxServer) error {
403+
if time.Now().Sub(lastMempool).Seconds() >= 2 {
404+
lastMempool = time.Now()
405+
// Refresh our copy of the mempool.
406+
params := make([]json.RawMessage, 0)
407+
result, rpcErr := common.RawRequest("getrawmempool", params)
408+
if rpcErr != nil {
409+
return rpcErr
410+
}
411+
err := json.Unmarshal(result, &mempoolList)
412+
if err != nil {
413+
return err
414+
}
415+
newmempoolMap := make(map[string]*walletrpc.CompactTx)
416+
if mempoolMap == nil {
417+
mempoolMap = &newmempoolMap
418+
}
419+
for _, txidstr := range mempoolList {
420+
if ctx, ok := (*mempoolMap)[txidstr]; ok {
421+
// This ctx has already been fetched, copy pointer to it.
422+
newmempoolMap[txidstr] = ctx
423+
continue
424+
}
425+
txidJSON, err := json.Marshal(txidstr)
426+
if err != nil {
427+
return err
428+
}
429+
// The "0" is because we only need the raw hex, which is returned as
430+
// just a hex string, and not even a json string (with quotes).
431+
params := []json.RawMessage{txidJSON, json.RawMessage("0")}
432+
result, rpcErr := common.RawRequest("getrawtransaction", params)
433+
if rpcErr != nil {
434+
// Not an error; mempool transactions can disappear
435+
continue
436+
}
437+
// strip the quotes
438+
var txStr string
439+
err = json.Unmarshal(result, &txStr)
440+
if err != nil {
441+
return err
442+
}
443+
444+
// conver to binary
445+
txBytes, err := hex.DecodeString(txStr)
446+
if err != nil {
447+
return err
448+
}
449+
tx := parser.NewTransaction()
450+
txdata, err := tx.ParseFromSlice(txBytes)
451+
if len(txdata) > 0 {
452+
return errors.New("extra data deserializing transaction")
453+
}
454+
newmempoolMap[txidstr] = &walletrpc.CompactTx{}
455+
if tx.HasShieldedElements() {
456+
newmempoolMap[txidstr] = tx.ToCompact( /* height */ 0)
457+
}
458+
}
459+
mempoolMap = &newmempoolMap
460+
}
461+
excludeHex := make([]string, len(exclude.Txid))
462+
for i := 0; i < len(exclude.Txid); i++ {
463+
excludeHex[i] = hex.EncodeToString(parser.Reverse(exclude.Txid[i]))
464+
}
465+
for _, txid := range MempoolFilter(mempoolList, excludeHex) {
466+
tx := (*mempoolMap)[txid]
467+
if len(tx.Hash) > 0 {
468+
err := resp.Send(tx)
469+
if err != nil {
470+
return err
471+
}
472+
}
473+
}
474+
return nil
475+
}
476+
477+
// Return the subset of items that aren't excluded, but
478+
// if more than one item matches an exclude entry, return
479+
// all those items.
480+
func MempoolFilter(items, exclude []string) []string {
481+
sort.Slice(items, func(i, j int) bool {
482+
return items[i] < items[j]
483+
})
484+
sort.Slice(exclude, func(i, j int) bool {
485+
return exclude[i] < exclude[j]
486+
})
487+
// Determine how many items match each exclude item.
488+
nmatches := make([]int, len(exclude))
489+
// is the exclude string less than the item string?
490+
lessthan := func(e, i string) bool {
491+
l := len(e)
492+
if l > len(i) {
493+
l = len(i)
494+
}
495+
return e < i[0:l]
496+
}
497+
ei := 0
498+
for _, item := range items {
499+
for ei < len(exclude) && lessthan(exclude[ei], item) {
500+
ei++
501+
}
502+
match := ei < len(exclude) && strings.HasPrefix(item, exclude[ei])
503+
if match {
504+
nmatches[ei]++
505+
}
506+
}
507+
508+
// Add each item that isn't uniquely excluded to the results.
509+
tosend := make([]string, 0)
510+
ei = 0
511+
for _, item := range items {
512+
for ei < len(exclude) && lessthan(exclude[ei], item) {
513+
ei++
514+
}
515+
match := ei < len(exclude) && strings.HasPrefix(item, exclude[ei])
516+
if !match || nmatches[ei] > 1 {
517+
tosend = append(tosend, item)
518+
}
519+
}
520+
return tosend
521+
}
522+
394523
func getAddressUtxos(arg *walletrpc.GetAddressUtxosArg, f func(*walletrpc.GetAddressUtxosReply) error) error {
395524
for _, a := range arg.Addresses {
396525
if err := checkTaddress(a); err != nil {
@@ -501,6 +630,8 @@ func (s *DarksideStreamer) Reset(ctx context.Context, ms *walletrpc.DarksideMeta
501630
if err != nil {
502631
return nil, err
503632
}
633+
mempoolMap = nil
634+
mempoolList = nil
504635
return &walletrpc.Empty{}, nil
505636
}
506637

0 commit comments

Comments
 (0)