Add getMempoolFilters websocket method
This commit is contained in:
parent
3ab5e636ff
commit
4c41b414c4
@ -14,11 +14,13 @@ type addrIndex struct {
|
|||||||
type txEntry struct {
|
type txEntry struct {
|
||||||
addrIndexes []addrIndex
|
addrIndexes []addrIndex
|
||||||
time uint32
|
time uint32
|
||||||
|
filter string
|
||||||
}
|
}
|
||||||
|
|
||||||
type txidio struct {
|
type txidio struct {
|
||||||
txid string
|
txid string
|
||||||
io []addrIndex
|
io []addrIndex
|
||||||
|
filter string
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseMempool is mempool base handle
|
// BaseMempool is mempool base handle
|
||||||
|
|||||||
@ -383,3 +383,7 @@ func (c *mempoolWithMetrics) GetAllEntries() (v bchain.MempoolTxidEntries) {
|
|||||||
func (c *mempoolWithMetrics) GetTransactionTime(txid string) uint32 {
|
func (c *mempoolWithMetrics) GetTransactionTime(txid string) uint32 {
|
||||||
return c.mempool.GetTransactionTime(txid)
|
return c.mempool.GetTransactionTime(txid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *mempoolWithMetrics) GetTxidFilterEntries(filterScripts string, fromTimestamp uint32) (bchain.MempoolTxidFilterEntries, error) {
|
||||||
|
return c.mempool.GetTxidFilterEntries(filterScripts, fromTimestamp)
|
||||||
|
}
|
||||||
|
|||||||
@ -23,18 +23,18 @@ import (
|
|||||||
// BitcoinRPC is an interface to JSON-RPC bitcoind service.
|
// BitcoinRPC is an interface to JSON-RPC bitcoind service.
|
||||||
type BitcoinRPC struct {
|
type BitcoinRPC struct {
|
||||||
*bchain.BaseChain
|
*bchain.BaseChain
|
||||||
client http.Client
|
client http.Client
|
||||||
rpcURL string
|
rpcURL string
|
||||||
user string
|
user string
|
||||||
password string
|
password string
|
||||||
Mempool *bchain.MempoolBitcoinType
|
Mempool *bchain.MempoolBitcoinType
|
||||||
ParseBlocks bool
|
ParseBlocks bool
|
||||||
pushHandler func(bchain.NotificationType)
|
pushHandler func(bchain.NotificationType)
|
||||||
mq *bchain.MQ
|
mq *bchain.MQ
|
||||||
ChainConfig *Configuration
|
ChainConfig *Configuration
|
||||||
RPCMarshaler RPCMarshaler
|
RPCMarshaler RPCMarshaler
|
||||||
golombFilterP uint8
|
golombFilterP uint8
|
||||||
golombFilterScripts string
|
mempoolFilterScripts string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration represents json config file
|
// Configuration represents json config file
|
||||||
@ -63,7 +63,7 @@ type Configuration struct {
|
|||||||
AlternativeEstimateFeeParams string `json:"alternative_estimate_fee_params,omitempty"`
|
AlternativeEstimateFeeParams string `json:"alternative_estimate_fee_params,omitempty"`
|
||||||
MinimumCoinbaseConfirmations int `json:"minimumCoinbaseConfirmations,omitempty"`
|
MinimumCoinbaseConfirmations int `json:"minimumCoinbaseConfirmations,omitempty"`
|
||||||
GolombFilterP uint8 `json:"golomb_filter_p,omitempty"`
|
GolombFilterP uint8 `json:"golomb_filter_p,omitempty"`
|
||||||
GolombFilterScripts string `json:"golomb_filter_scripts,omitempty"`
|
MempoolFilterScripts string `json:"mempool_filter_scripts,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBitcoinRPC returns new BitcoinRPC instance.
|
// NewBitcoinRPC returns new BitcoinRPC instance.
|
||||||
@ -100,17 +100,17 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationT
|
|||||||
}
|
}
|
||||||
|
|
||||||
s := &BitcoinRPC{
|
s := &BitcoinRPC{
|
||||||
BaseChain: &bchain.BaseChain{},
|
BaseChain: &bchain.BaseChain{},
|
||||||
client: http.Client{Timeout: time.Duration(c.RPCTimeout) * time.Second, Transport: transport},
|
client: http.Client{Timeout: time.Duration(c.RPCTimeout) * time.Second, Transport: transport},
|
||||||
rpcURL: c.RPCURL,
|
rpcURL: c.RPCURL,
|
||||||
user: c.RPCUser,
|
user: c.RPCUser,
|
||||||
password: c.RPCPass,
|
password: c.RPCPass,
|
||||||
ParseBlocks: c.Parse,
|
ParseBlocks: c.Parse,
|
||||||
ChainConfig: &c,
|
ChainConfig: &c,
|
||||||
pushHandler: pushHandler,
|
pushHandler: pushHandler,
|
||||||
RPCMarshaler: JSONMarshalerV2{},
|
RPCMarshaler: JSONMarshalerV2{},
|
||||||
golombFilterP: c.GolombFilterP,
|
golombFilterP: c.GolombFilterP,
|
||||||
golombFilterScripts: c.GolombFilterScripts,
|
mempoolFilterScripts: c.MempoolFilterScripts,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
@ -156,7 +156,7 @@ func (b *BitcoinRPC) Initialize() error {
|
|||||||
// CreateMempool creates mempool if not already created, however does not initialize it
|
// CreateMempool creates mempool if not already created, however does not initialize it
|
||||||
func (b *BitcoinRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
|
func (b *BitcoinRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
|
||||||
if b.Mempool == nil {
|
if b.Mempool == nil {
|
||||||
b.Mempool = bchain.NewMempoolBitcoinType(chain, b.ChainConfig.MempoolWorkers, b.ChainConfig.MempoolSubWorkers, b.golombFilterP, b.golombFilterScripts)
|
b.Mempool = bchain.NewMempoolBitcoinType(chain, b.ChainConfig.MempoolWorkers, b.ChainConfig.MempoolSubWorkers, b.golombFilterP, b.mempoolFilterScripts)
|
||||||
}
|
}
|
||||||
return b.Mempool, nil
|
return b.Mempool, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,8 @@ package bchain
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -14,10 +16,13 @@ type chanInputPayload struct {
|
|||||||
index int
|
index int
|
||||||
}
|
}
|
||||||
|
|
||||||
type golombFilterScriptsType int
|
type filterScriptsType int
|
||||||
|
|
||||||
const golombFilterScriptsAll = golombFilterScriptsType(0)
|
const (
|
||||||
const golombFilterScriptsTaproot = golombFilterScriptsType(1)
|
filterScriptsInvalid = filterScriptsType(iota)
|
||||||
|
filterScriptsAll
|
||||||
|
filterScriptsTaproot
|
||||||
|
)
|
||||||
|
|
||||||
// MempoolBitcoinType is mempool handle.
|
// MempoolBitcoinType is mempool handle.
|
||||||
type MempoolBitcoinType struct {
|
type MempoolBitcoinType struct {
|
||||||
@ -27,20 +32,15 @@ type MempoolBitcoinType struct {
|
|||||||
AddrDescForOutpoint AddrDescForOutpointFunc
|
AddrDescForOutpoint AddrDescForOutpointFunc
|
||||||
golombFilterP uint8
|
golombFilterP uint8
|
||||||
golombFilterM uint64
|
golombFilterM uint64
|
||||||
golombFilterScripts golombFilterScriptsType
|
filterScripts filterScriptsType
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMempoolBitcoinType creates new mempool handler.
|
// NewMempoolBitcoinType creates new mempool handler.
|
||||||
// For now there is no cleanup of sync routines, the expectation is that the mempool is created only once per process
|
// For now there is no cleanup of sync routines, the expectation is that the mempool is created only once per process
|
||||||
func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golombFilterP uint8, golombFilterScripts string) *MempoolBitcoinType {
|
func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golombFilterP uint8, filterScripts string) *MempoolBitcoinType {
|
||||||
var filterScripts golombFilterScriptsType
|
filterScriptsType := filterScriptsToScriptsType(filterScripts)
|
||||||
switch golombFilterScripts {
|
if filterScriptsType == filterScriptsInvalid {
|
||||||
case "":
|
glog.Error("Invalid filterScripts ", filterScripts, ", switching off golomb filter")
|
||||||
filterScripts = golombFilterScriptsAll
|
|
||||||
case "taproot":
|
|
||||||
filterScripts = golombFilterScriptsTaproot
|
|
||||||
default:
|
|
||||||
glog.Error("Invalid golombFilterScripts ", golombFilterScripts, ", switching off golomb filter")
|
|
||||||
golombFilterP = 0
|
golombFilterP = 0
|
||||||
}
|
}
|
||||||
golombFilterM := uint64(1 << golombFilterP)
|
golombFilterM := uint64(1 << golombFilterP)
|
||||||
@ -50,11 +50,11 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golomb
|
|||||||
txEntries: make(map[string]txEntry),
|
txEntries: make(map[string]txEntry),
|
||||||
addrDescToTx: make(map[string][]Outpoint),
|
addrDescToTx: make(map[string][]Outpoint),
|
||||||
},
|
},
|
||||||
chanTxid: make(chan string, 1),
|
chanTxid: make(chan string, 1),
|
||||||
chanAddrIndex: make(chan txidio, 1),
|
chanAddrIndex: make(chan txidio, 1),
|
||||||
golombFilterP: golombFilterP,
|
golombFilterP: golombFilterP,
|
||||||
golombFilterM: golombFilterM,
|
golombFilterM: golombFilterM,
|
||||||
golombFilterScripts: filterScripts,
|
filterScripts: filterScriptsType,
|
||||||
}
|
}
|
||||||
for i := 0; i < workers; i++ {
|
for i := 0; i < workers; i++ {
|
||||||
go func(i int) {
|
go func(i int) {
|
||||||
@ -69,11 +69,11 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golomb
|
|||||||
}(j)
|
}(j)
|
||||||
}
|
}
|
||||||
for txid := range m.chanTxid {
|
for txid := range m.chanTxid {
|
||||||
io, ok := m.getTxAddrs(txid, chanInput, chanResult)
|
io, golombFilter, ok := m.getTxAddrs(txid, chanInput, chanResult)
|
||||||
if !ok {
|
if !ok {
|
||||||
io = []addrIndex{}
|
io = []addrIndex{}
|
||||||
}
|
}
|
||||||
m.chanAddrIndex <- txidio{txid, io}
|
m.chanAddrIndex <- txidio{txid, io, golombFilter}
|
||||||
}
|
}
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
@ -81,6 +81,16 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golomb
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterScriptsToScriptsType(filterScripts string) filterScriptsType {
|
||||||
|
switch filterScripts {
|
||||||
|
case "":
|
||||||
|
return filterScriptsAll
|
||||||
|
case "taproot":
|
||||||
|
return filterScriptsTaproot
|
||||||
|
}
|
||||||
|
return filterScriptsInvalid
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MempoolBitcoinType) getInputAddress(payload *chanInputPayload) *addrIndex {
|
func (m *MempoolBitcoinType) getInputAddress(payload *chanInputPayload) *addrIndex {
|
||||||
var addrDesc AddressDescriptor
|
var addrDesc AddressDescriptor
|
||||||
var value *big.Int
|
var value *big.Int
|
||||||
@ -126,7 +136,7 @@ func (m *MempoolBitcoinType) computeGolombFilter(mtx *MempoolTx) string {
|
|||||||
filterData := make([][]byte, 0)
|
filterData := make([][]byte, 0)
|
||||||
for i := range mtx.Vin {
|
for i := range mtx.Vin {
|
||||||
vin := &mtx.Vin[i]
|
vin := &mtx.Vin[i]
|
||||||
if m.golombFilterScripts == golombFilterScriptsAll || (m.golombFilterScripts == golombFilterScriptsTaproot && isTaproot(vin.AddrDesc)) {
|
if m.filterScripts == filterScriptsAll || (m.filterScripts == filterScriptsTaproot && isTaproot(vin.AddrDesc)) {
|
||||||
filterData = append(filterData, vin.AddrDesc)
|
filterData = append(filterData, vin.AddrDesc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,7 +144,7 @@ func (m *MempoolBitcoinType) computeGolombFilter(mtx *MempoolTx) string {
|
|||||||
vout := &mtx.Vout[i]
|
vout := &mtx.Vout[i]
|
||||||
b, err := hex.DecodeString(vout.ScriptPubKey.Hex)
|
b, err := hex.DecodeString(vout.ScriptPubKey.Hex)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if m.golombFilterScripts == golombFilterScriptsAll || (m.golombFilterScripts == golombFilterScriptsTaproot && isTaproot(b)) {
|
if m.filterScripts == filterScriptsAll || (m.filterScripts == filterScriptsTaproot && isTaproot(b)) {
|
||||||
filterData = append(filterData, b)
|
filterData = append(filterData, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,16 +159,21 @@ func (m *MempoolBitcoinType) computeGolombFilter(mtx *MempoolTx) string {
|
|||||||
filter, err := gcs.BuildGCSFilter(m.golombFilterP, m.golombFilterM, *(*[gcs.KeySize]byte)(b[:gcs.KeySize]), filterData)
|
filter, err := gcs.BuildGCSFilter(m.golombFilterP, m.golombFilterM, *(*[gcs.KeySize]byte)(b[:gcs.KeySize]), filterData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("Cannot create golomb filter for ", mtx.Txid, ", ", err)
|
glog.Error("Cannot create golomb filter for ", mtx.Txid, ", ", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
fb, err := filter.NBytes()
|
||||||
|
if err != nil {
|
||||||
|
glog.Error("Error getting NBytes from golomb filter for ", mtx.Txid, ", ", err)
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
fb, _ := filter.Bytes()
|
|
||||||
return hex.EncodeToString(fb)
|
return hex.EncodeToString(fb)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan chanInputPayload, chanResult chan *addrIndex) ([]addrIndex, bool) {
|
func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan chanInputPayload, chanResult chan *addrIndex) ([]addrIndex, string, bool) {
|
||||||
tx, err := m.chain.GetTransactionForMempool(txid)
|
tx, err := m.chain.GetTransactionForMempool(txid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("cannot get transaction ", txid, ": ", err)
|
glog.Error("cannot get transaction ", txid, ": ", err)
|
||||||
return nil, false
|
return nil, "", false
|
||||||
}
|
}
|
||||||
glog.V(2).Info("mempool: gettxaddrs ", txid, ", ", len(tx.Vin), " inputs")
|
glog.V(2).Info("mempool: gettxaddrs ", txid, ", ", len(tx.Vin), " inputs")
|
||||||
mtx := m.txToMempoolTx(tx)
|
mtx := m.txToMempoolTx(tx)
|
||||||
@ -205,13 +220,14 @@ func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan chanInputPay
|
|||||||
io = append(io, *ai)
|
io = append(io, *ai)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var golombFilter string
|
||||||
if m.golombFilterP > 0 {
|
if m.golombFilterP > 0 {
|
||||||
mtx.GolombFilter = m.computeGolombFilter(mtx)
|
golombFilter = m.computeGolombFilter(mtx)
|
||||||
}
|
}
|
||||||
if m.OnNewTx != nil {
|
if m.OnNewTx != nil {
|
||||||
m.OnNewTx(mtx)
|
m.OnNewTx(mtx)
|
||||||
}
|
}
|
||||||
return io, true
|
return io, golombFilter, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resync gets mempool transactions and maps outputs to transactions.
|
// Resync gets mempool transactions and maps outputs to transactions.
|
||||||
@ -248,7 +264,7 @@ func (m *MempoolBitcoinType) Resync() (int, error) {
|
|||||||
select {
|
select {
|
||||||
// store as many processed transactions as possible
|
// store as many processed transactions as possible
|
||||||
case tio := <-m.chanAddrIndex:
|
case tio := <-m.chanAddrIndex:
|
||||||
onNewEntry(tio.txid, txEntry{tio.io, txTime})
|
onNewEntry(tio.txid, txEntry{tio.io, txTime, tio.filter})
|
||||||
dispatched--
|
dispatched--
|
||||||
// send transaction to be processed
|
// send transaction to be processed
|
||||||
case m.chanTxid <- txid:
|
case m.chanTxid <- txid:
|
||||||
@ -260,7 +276,7 @@ func (m *MempoolBitcoinType) Resync() (int, error) {
|
|||||||
}
|
}
|
||||||
for i := 0; i < dispatched; i++ {
|
for i := 0; i < dispatched; i++ {
|
||||||
tio := <-m.chanAddrIndex
|
tio := <-m.chanAddrIndex
|
||||||
onNewEntry(tio.txid, txEntry{tio.io, txTime})
|
onNewEntry(tio.txid, txEntry{tio.io, txTime, tio.filter})
|
||||||
}
|
}
|
||||||
|
|
||||||
for txid, entry := range m.txEntries {
|
for txid, entry := range m.txEntries {
|
||||||
@ -273,3 +289,19 @@ func (m *MempoolBitcoinType) Resync() (int, error) {
|
|||||||
glog.Info("mempool: resync finished in ", time.Since(start), ", ", len(m.txEntries), " transactions in mempool")
|
glog.Info("mempool: resync finished in ", time.Since(start), ", ", len(m.txEntries), " transactions in mempool")
|
||||||
return len(m.txEntries), nil
|
return len(m.txEntries), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTxidFilterEntries returns all mempool entries with golomb filter from
|
||||||
|
func (m *MempoolBitcoinType) GetTxidFilterEntries(filterScripts string, fromTimestamp uint32) (MempoolTxidFilterEntries, error) {
|
||||||
|
if m.filterScripts != filterScriptsToScriptsType(filterScripts) {
|
||||||
|
return MempoolTxidFilterEntries{}, errors.New(fmt.Sprint("Unsupported script filter ", filterScripts))
|
||||||
|
}
|
||||||
|
m.mux.Lock()
|
||||||
|
entries := make(map[string]string)
|
||||||
|
for txid, entry := range m.txEntries {
|
||||||
|
if entry.filter != "" && entry.time >= fromTimestamp {
|
||||||
|
entries[txid] = entry.filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.mux.Unlock()
|
||||||
|
return MempoolTxidFilterEntries{entries}, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -15,19 +15,17 @@ func hexToBytes(h string) []byte {
|
|||||||
func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
|
func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
|
||||||
randomScript := hexToBytes("a914ff074800343a81ada8fe86c2d5d5a0e55b93dd7a87")
|
randomScript := hexToBytes("a914ff074800343a81ada8fe86c2d5d5a0e55b93dd7a87")
|
||||||
m := &MempoolBitcoinType{
|
m := &MempoolBitcoinType{
|
||||||
golombFilterP: 20,
|
golombFilterP: 20,
|
||||||
golombFilterM: uint64(1 << 20),
|
golombFilterM: uint64(1 << 20),
|
||||||
golombFilterScripts: golombFilterScriptsTaproot,
|
filterScripts: filterScriptsTaproot,
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
N uint32
|
|
||||||
mtx MempoolTx
|
mtx MempoolTx
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "taproot",
|
name: "taproot",
|
||||||
N: 2,
|
|
||||||
mtx: MempoolTx{
|
mtx: MempoolTx{
|
||||||
Txid: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
|
Txid: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
|
||||||
Vin: []MempoolVin{
|
Vin: []MempoolVin{
|
||||||
@ -47,11 +45,10 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: "35dddcce5d60",
|
want: "0235dddcce5d60",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "taproot multiple",
|
name: "taproot multiple",
|
||||||
N: 7,
|
|
||||||
mtx: MempoolTx{
|
mtx: MempoolTx{
|
||||||
Txid: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
|
Txid: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
|
||||||
Vin: []MempoolVin{
|
Vin: []MempoolVin{
|
||||||
@ -103,11 +100,10 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: "1143e4ad12730965a5247ac15db8c81c89b0bc",
|
want: "071143e4ad12730965a5247ac15db8c81c89b0bc",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "partial taproot",
|
name: "partial taproot",
|
||||||
N: 1,
|
|
||||||
mtx: MempoolTx{
|
mtx: MempoolTx{
|
||||||
Txid: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
|
Txid: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
|
||||||
Vin: []MempoolVin{
|
Vin: []MempoolVin{
|
||||||
@ -127,11 +123,10 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: "1aeee8",
|
want: "011aeee8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no taproot",
|
name: "no taproot",
|
||||||
N: 0,
|
|
||||||
mtx: MempoolTx{
|
mtx: MempoolTx{
|
||||||
Txid: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
|
Txid: "86336c62a63f509a278624e3f400cdd50838d035a44e0af8a7d6d133c04cc2d2",
|
||||||
Vin: []MempoolVin{
|
Vin: []MempoolVin{
|
||||||
@ -162,7 +157,7 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
|
|||||||
}
|
}
|
||||||
if got != "" {
|
if got != "" {
|
||||||
// build the filter from computed value
|
// build the filter from computed value
|
||||||
filter, err := gcs.FromBytes(tt.N, m.golombFilterP, m.golombFilterM, hexToBytes(got))
|
filter, err := gcs.FromNBytes(m.golombFilterP, m.golombFilterM, hexToBytes(got))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("gcs.BuildGCSFilter() unexpected error %v", err)
|
t.Errorf("gcs.BuildGCSFilter() unexpected error %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package bchain
|
package bchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
@ -165,3 +166,8 @@ func (m *MempoolEthereumType) RemoveTransactionFromMempool(txid string) {
|
|||||||
}
|
}
|
||||||
m.mux.Unlock()
|
m.mux.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTxidFilterEntries returns all mempool entries with golomb filter from
|
||||||
|
func (m *MempoolEthereumType) GetTxidFilterEntries(filterScripts string, fromTimestamp uint32) (MempoolTxidFilterEntries, error) {
|
||||||
|
return MempoolTxidFilterEntries{}, errors.New("Not supported")
|
||||||
|
}
|
||||||
|
|||||||
@ -113,7 +113,6 @@ type MempoolTx struct {
|
|||||||
Blocktime int64 `json:"blocktime,omitempty"`
|
Blocktime int64 `json:"blocktime,omitempty"`
|
||||||
TokenTransfers TokenTransfers `json:"-"`
|
TokenTransfers TokenTransfers `json:"-"`
|
||||||
CoinSpecificData interface{} `json:"-"`
|
CoinSpecificData interface{} `json:"-"`
|
||||||
GolombFilter string `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenType - type of token
|
// TokenType - type of token
|
||||||
@ -266,6 +265,11 @@ type XpubDescriptor struct {
|
|||||||
// MempoolTxidEntries is array of MempoolTxidEntry
|
// MempoolTxidEntries is array of MempoolTxidEntry
|
||||||
type MempoolTxidEntries []MempoolTxidEntry
|
type MempoolTxidEntries []MempoolTxidEntry
|
||||||
|
|
||||||
|
// MempoolTxidFilterEntries is a map of txids to mempool golomb filters
|
||||||
|
type MempoolTxidFilterEntries struct {
|
||||||
|
Entries map[string]string `json:"entries,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// OnNewBlockFunc is used to send notification about a new block
|
// OnNewBlockFunc is used to send notification about a new block
|
||||||
type OnNewBlockFunc func(hash string, height uint32)
|
type OnNewBlockFunc func(hash string, height uint32)
|
||||||
|
|
||||||
@ -379,4 +383,5 @@ type Mempool interface {
|
|||||||
GetAddrDescTransactions(addrDesc AddressDescriptor) ([]Outpoint, error)
|
GetAddrDescTransactions(addrDesc AddressDescriptor) ([]Outpoint, error)
|
||||||
GetAllEntries() MempoolTxidEntries
|
GetAllEntries() MempoolTxidEntries
|
||||||
GetTransactionTime(txid string) uint32
|
GetTransactionTime(txid string) uint32
|
||||||
|
GetTxidFilterEntries(filterScripts string, fromTimestamp uint32) (MempoolTxidFilterEntries, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -309,7 +309,8 @@ export interface WsReq {
|
|||||||
| 'ping'
|
| 'ping'
|
||||||
| 'getCurrentFiatRates'
|
| 'getCurrentFiatRates'
|
||||||
| 'getFiatRatesForTimestamps'
|
| 'getFiatRatesForTimestamps'
|
||||||
| 'getFiatRatesTickersList';
|
| 'getFiatRatesTickersList'
|
||||||
|
| 'getMempoolFilters';
|
||||||
params: any;
|
params: any;
|
||||||
}
|
}
|
||||||
export interface WsRes {
|
export interface WsRes {
|
||||||
@ -412,3 +413,10 @@ export interface WsFiatRatesTickersListReq {
|
|||||||
timestamp?: number;
|
timestamp?: number;
|
||||||
token?: string;
|
token?: string;
|
||||||
}
|
}
|
||||||
|
export interface WsMempoolFiltersReq {
|
||||||
|
scriptType: string;
|
||||||
|
fromTimestamp: number;
|
||||||
|
}
|
||||||
|
export interface MempoolTxidFilterEntries {
|
||||||
|
entries?: { [key: string]: string };
|
||||||
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tkrajina/typescriptify-golang-structs/typescriptify"
|
"github.com/tkrajina/typescriptify-golang-structs/typescriptify"
|
||||||
"github.com/trezor/blockbook/api"
|
"github.com/trezor/blockbook/api"
|
||||||
|
"github.com/trezor/blockbook/bchain"
|
||||||
"github.com/trezor/blockbook/server"
|
"github.com/trezor/blockbook/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -56,6 +57,8 @@ func main() {
|
|||||||
t.Add(server.WsCurrentFiatRatesReq{})
|
t.Add(server.WsCurrentFiatRatesReq{})
|
||||||
t.Add(server.WsFiatRatesForTimestampsReq{})
|
t.Add(server.WsFiatRatesForTimestampsReq{})
|
||||||
t.Add(server.WsFiatRatesTickersListReq{})
|
t.Add(server.WsFiatRatesTickersListReq{})
|
||||||
|
t.Add(server.WsMempoolFiltersReq{})
|
||||||
|
t.Add(bchain.MempoolTxidFilterEntries{})
|
||||||
|
|
||||||
err := t.ConvertToFile("blockbook-api.ts")
|
err := t.ConvertToFile("blockbook-api.ts")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -69,7 +69,7 @@
|
|||||||
"fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH",
|
"fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH",
|
||||||
"fiat_rates_params": "{\"coin\": \"bitcoin\", \"periodSeconds\": 900}",
|
"fiat_rates_params": "{\"coin\": \"bitcoin\", \"periodSeconds\": 900}",
|
||||||
"golomb_filter_p": 20,
|
"golomb_filter_p": 20,
|
||||||
"golomb_filter_scripts": "taproot"
|
"mempool_filter_scripts": "taproot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -65,7 +65,7 @@
|
|||||||
"slip44": 1,
|
"slip44": 1,
|
||||||
"additional_params": {
|
"additional_params": {
|
||||||
"golomb_filter_p": 20,
|
"golomb_filter_p": 20,
|
||||||
"golomb_filter_scripts": "taproot"
|
"mempool_filter_scripts": "taproot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1451,6 +1451,26 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
|||||||
},
|
},
|
||||||
want: `{"id":"40","data":{"error":{"message":"Not supported"}}}`,
|
want: `{"id":"40","data":{"error":{"message":"Not supported"}}}`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "websocket getMempoolFilters",
|
||||||
|
req: websocketReq{
|
||||||
|
Method: "getMempoolFilters",
|
||||||
|
Params: map[string]interface{}{
|
||||||
|
"scriptType": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `{"id":"41","data":{}}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "websocket getMempoolFilters invalid type",
|
||||||
|
req: websocketReq{
|
||||||
|
Method: "getMempoolFilters",
|
||||||
|
Params: map[string]interface{}{
|
||||||
|
"scriptType": "invalid",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `{"id":"42","data":{"error":{"message":"Unsupported script filter invalid"}}}`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// send all requests at once
|
// send all requests at once
|
||||||
|
|||||||
@ -342,6 +342,14 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *WsRe
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
|
"getMempoolFilters": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
|
r := WsMempoolFiltersReq{}
|
||||||
|
err = json.Unmarshal(req.Params, &r)
|
||||||
|
if err == nil {
|
||||||
|
rv, err = s.getMempoolFilters(&r)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
"subscribeNewBlock": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
"subscribeNewBlock": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
return s.subscribeNewBlock(c, req)
|
return s.subscribeNewBlock(c, req)
|
||||||
},
|
},
|
||||||
@ -632,6 +640,11 @@ func (s *WebsocketServer) sendTransaction(tx string) (res resultSendTransaction,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *WebsocketServer) getMempoolFilters(r *WsMempoolFiltersReq) (res bchain.MempoolTxidFilterEntries, err error) {
|
||||||
|
res, err = s.mempool.GetTxidFilterEntries(r.ScriptType, r.FromTimestamp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type subscriptionResponse struct {
|
type subscriptionResponse struct {
|
||||||
Subscribed bool `json:"subscribed"`
|
Subscribed bool `json:"subscribed"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import "encoding/json"
|
|||||||
|
|
||||||
type WsReq struct {
|
type WsReq struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Method string `json:"method" ts_type:"'getAccountInfo' | 'getInfo' | 'getBlockHash'| 'getBlock' | 'getAccountUtxo' | 'getBalanceHistory' | 'getTransaction' | 'getTransactionSpecific' | 'estimateFee' | 'sendTransaction' | 'subscribeNewBlock' | 'unsubscribeNewBlock' | 'subscribeNewTransaction' | 'unsubscribeNewTransaction' | 'subscribeAddresses' | 'unsubscribeAddresses' | 'subscribeFiatRates' | 'unsubscribeFiatRates' | 'ping' | 'getCurrentFiatRates' | 'getFiatRatesForTimestamps' | 'getFiatRatesTickersList'"`
|
Method string `json:"method" ts_type:"'getAccountInfo' | 'getInfo' | 'getBlockHash'| 'getBlock' | 'getAccountUtxo' | 'getBalanceHistory' | 'getTransaction' | 'getTransactionSpecific' | 'estimateFee' | 'sendTransaction' | 'subscribeNewBlock' | 'unsubscribeNewBlock' | 'subscribeNewTransaction' | 'unsubscribeNewTransaction' | 'subscribeAddresses' | 'unsubscribeAddresses' | 'subscribeFiatRates' | 'unsubscribeFiatRates' | 'ping' | 'getCurrentFiatRates' | 'getFiatRatesForTimestamps' | 'getFiatRatesTickersList' | 'getMempoolFilters'"`
|
||||||
Params json.RawMessage `json:"params" ts_type:"any"`
|
Params json.RawMessage `json:"params" ts_type:"any"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +76,11 @@ type WsTransactionReq struct {
|
|||||||
Txid string `json:"txid"`
|
Txid string `json:"txid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WsMempoolFiltersReq struct {
|
||||||
|
ScriptType string `json:"scriptType"`
|
||||||
|
FromTimestamp uint32 `json:"fromTimestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
type WsTransactionSpecificReq struct {
|
type WsTransactionSpecificReq struct {
|
||||||
Txid string `json:"txid"`
|
Txid string `json:"txid"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -397,6 +397,20 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMempoolFilters() {
|
||||||
|
const method = 'getMempoolFilters';
|
||||||
|
var timestamp = document.getElementById('getMempoolFiltersFromTimestamp').value;
|
||||||
|
var scriptType = document.getElementById('getMempoolFiltersScriptType').value;
|
||||||
|
fromTimestamp = parseInt(timestamp);
|
||||||
|
const params = {
|
||||||
|
scriptType,
|
||||||
|
fromTimestamp,
|
||||||
|
};
|
||||||
|
send(method, params, function (result) {
|
||||||
|
document.getElementById('getMempoolFiltersResult').innerText = JSON.stringify(result).replace(/,/g, ", ");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function subscribeNewFiatRatesTicker() {
|
function subscribeNewFiatRatesTicker() {
|
||||||
const method = 'subscribeFiatRates';
|
const method = 'subscribeFiatRates';
|
||||||
var currency = document.getElementById('subscribeFiatRatesCurrency').value;
|
var currency = document.getElementById('subscribeFiatRatesCurrency').value;
|
||||||
@ -661,6 +675,20 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col" id="getFiatRatesTickersListResult"></div>
|
<div class="col" id="getFiatRatesTickersListResult"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-2">
|
||||||
|
<input class="btn btn-secondary" type="button" value="get mempool filters" onclick="getMempoolFilters()">
|
||||||
|
</div>
|
||||||
|
<div class="col-5">
|
||||||
|
<input type="text" class="form-control" id="getMempoolFiltersScriptType" value="taproot" placeholder="filter script">
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<input type="text" class="form-control" id="getMempoolFiltersFromTimestamp" value="" placeholder="From unix timestamp">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col" id="getMempoolFiltersResult"></div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<input class="btn btn-secondary" type="button" value="subscribe new block" onclick="subscribeNewBlock()">
|
<input class="btn btn-secondary" type="button" value="subscribe new block" onclick="subscribeNewBlock()">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user