Ignore Ordinals in Golomb filters (#967)
This commit is contained in:
parent
a1a17b4331
commit
7d0c424ad8
@ -231,6 +231,7 @@ func (p *BitcoinLikeParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bcha
|
||||
Vout: in.PreviousOutPoint.Index,
|
||||
Sequence: in.Sequence,
|
||||
ScriptSig: s,
|
||||
Witness: in.Witness,
|
||||
}
|
||||
}
|
||||
vout := make([]bchain.Vout, len(t.TxOut))
|
||||
|
||||
@ -34,6 +34,7 @@ type BitcoinRPC struct {
|
||||
RPCMarshaler RPCMarshaler
|
||||
mempoolGolombFilterP uint8
|
||||
mempoolFilterScripts string
|
||||
mempoolUseZeroedKey bool
|
||||
}
|
||||
|
||||
// Configuration represents json config file
|
||||
@ -63,6 +64,7 @@ type Configuration struct {
|
||||
MinimumCoinbaseConfirmations int `json:"minimumCoinbaseConfirmations,omitempty"`
|
||||
MempoolGolombFilterP uint8 `json:"mempool_golomb_filter_p,omitempty"`
|
||||
MempoolFilterScripts string `json:"mempool_filter_scripts,omitempty"`
|
||||
MempoolFilterUseZeroedKey bool `json:"mempool_filter_use_zeroed_key,omitempty"`
|
||||
}
|
||||
|
||||
// NewBitcoinRPC returns new BitcoinRPC instance.
|
||||
@ -110,6 +112,7 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationT
|
||||
RPCMarshaler: JSONMarshalerV2{},
|
||||
mempoolGolombFilterP: c.MempoolGolombFilterP,
|
||||
mempoolFilterScripts: c.MempoolFilterScripts,
|
||||
mempoolUseZeroedKey: c.MempoolFilterUseZeroedKey,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
@ -155,7 +158,7 @@ func (b *BitcoinRPC) Initialize() error {
|
||||
// CreateMempool creates mempool if not already created, however does not initialize it
|
||||
func (b *BitcoinRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
|
||||
if b.Mempool == nil {
|
||||
b.Mempool = bchain.NewMempoolBitcoinType(chain, b.ChainConfig.MempoolWorkers, b.ChainConfig.MempoolSubWorkers, b.mempoolGolombFilterP, b.mempoolFilterScripts)
|
||||
b.Mempool = bchain.NewMempoolBitcoinType(chain, b.ChainConfig.MempoolWorkers, b.ChainConfig.MempoolSubWorkers, b.mempoolGolombFilterP, b.mempoolFilterScripts, b.mempoolUseZeroedKey)
|
||||
}
|
||||
return b.Mempool, nil
|
||||
}
|
||||
|
||||
133
bchain/golomb.go
133
bchain/golomb.go
@ -1,6 +1,7 @@
|
||||
package bchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/golang/glog"
|
||||
@ -14,26 +15,33 @@ const (
|
||||
FilterScriptsInvalid = FilterScriptsType(iota)
|
||||
FilterScriptsAll
|
||||
FilterScriptsTaproot
|
||||
FilterScriptsTaprootNoOrdinals
|
||||
)
|
||||
|
||||
// GolombFilter is computing golomb filter of address descriptors
|
||||
type GolombFilter struct {
|
||||
Enabled bool
|
||||
UseZeroedKey bool
|
||||
p uint8
|
||||
key string
|
||||
filterScripts string
|
||||
filterScriptsType FilterScriptsType
|
||||
filterData [][]byte
|
||||
uniqueData map[string]struct{}
|
||||
// All the unique txids that contain ordinal data
|
||||
ordinalTxIds map[string]struct{}
|
||||
// Mapping of txid to address descriptors - only used in case of taproot-noordinals
|
||||
allAddressDescriptors map[string][]AddressDescriptor
|
||||
}
|
||||
|
||||
// NewGolombFilter initializes the GolombFilter handler
|
||||
func NewGolombFilter(p uint8, filterScripts string, key string) (*GolombFilter, error) {
|
||||
func NewGolombFilter(p uint8, filterScripts string, key string, useZeroedKey bool) (*GolombFilter, error) {
|
||||
if p == 0 {
|
||||
return &GolombFilter{Enabled: false}, nil
|
||||
}
|
||||
gf := GolombFilter{
|
||||
Enabled: true,
|
||||
UseZeroedKey: useZeroedKey,
|
||||
p: p,
|
||||
key: key,
|
||||
filterScripts: filterScripts,
|
||||
@ -41,21 +49,85 @@ func NewGolombFilter(p uint8, filterScripts string, key string) (*GolombFilter,
|
||||
filterData: make([][]byte, 0),
|
||||
uniqueData: make(map[string]struct{}),
|
||||
}
|
||||
// only taproot and all is supported
|
||||
// reject invalid filterScripts
|
||||
if gf.filterScriptsType == FilterScriptsInvalid {
|
||||
return nil, errors.Errorf("Invalid/unsupported filterScripts parameter %s", filterScripts)
|
||||
}
|
||||
// set ordinal-related fields if needed
|
||||
if gf.ignoreOrdinals() {
|
||||
gf.ordinalTxIds = make(map[string]struct{})
|
||||
gf.allAddressDescriptors = make(map[string][]AddressDescriptor)
|
||||
}
|
||||
return &gf, nil
|
||||
}
|
||||
|
||||
// Gets the M parameter that we are using for the filter
|
||||
// Currently it relies on P parameter, but that can change
|
||||
func GetGolombParamM(p uint8) uint64 {
|
||||
return uint64(1 << uint64(p))
|
||||
}
|
||||
|
||||
// Checks whether this input contains ordinal data
|
||||
func isInputOrdinal(vin Vin) bool {
|
||||
byte_pattern := []byte{
|
||||
0x00, // OP_0, OP_FALSE
|
||||
0x63, // OP_IF
|
||||
0x03, // OP_PUSHBYTES_3
|
||||
0x6f, // "o"
|
||||
0x72, // "r"
|
||||
0x64, // "d"
|
||||
0x01, // OP_PUSHBYTES_1
|
||||
}
|
||||
// Witness needs to have at least 3 items and the second one needs to contain certain pattern
|
||||
return len(vin.Witness) > 2 && bytes.Contains(vin.Witness[1], byte_pattern)
|
||||
}
|
||||
|
||||
// Whether a transaction contains any ordinal data
|
||||
func txContainsOrdinal(tx *Tx) bool {
|
||||
for _, vin := range tx.Vin {
|
||||
if isInputOrdinal(vin) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Saving all the ordinal-related txIds so we can later ignore their address descriptors
|
||||
func (f *GolombFilter) markTxAndParentsAsOrdinals(tx *Tx) {
|
||||
f.ordinalTxIds[tx.Txid] = struct{}{}
|
||||
for _, vin := range tx.Vin {
|
||||
f.ordinalTxIds[vin.Txid] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Adding a new address descriptor mapped to a txid
|
||||
func (f *GolombFilter) addTxIdMapping(ad AddressDescriptor, tx *Tx) {
|
||||
f.allAddressDescriptors[tx.Txid] = append(f.allAddressDescriptors[tx.Txid], ad)
|
||||
}
|
||||
|
||||
// AddAddrDesc adds taproot address descriptor to the data for the filter
|
||||
func (f *GolombFilter) AddAddrDesc(ad AddressDescriptor) {
|
||||
if f.filterScriptsType == FilterScriptsTaproot && !ad.IsTaproot() {
|
||||
func (f *GolombFilter) AddAddrDesc(ad AddressDescriptor, tx *Tx) {
|
||||
if f.ignoreNonTaproot() && !ad.IsTaproot() {
|
||||
return
|
||||
}
|
||||
if f.ignoreOrdinals() && tx != nil && txContainsOrdinal(tx) {
|
||||
f.markTxAndParentsAsOrdinals(tx)
|
||||
return
|
||||
}
|
||||
if len(ad) == 0 {
|
||||
return
|
||||
}
|
||||
// When ignoring ordinals, we need to save all the address descriptors before
|
||||
// filtering out the "invalid" ones.
|
||||
if f.ignoreOrdinals() && tx != nil {
|
||||
f.addTxIdMapping(ad, tx)
|
||||
return
|
||||
}
|
||||
f.includeAddrDesc(ad)
|
||||
}
|
||||
|
||||
// Private function to be called with descriptors that were already validated
|
||||
func (f *GolombFilter) includeAddrDesc(ad AddressDescriptor) {
|
||||
s := string(ad)
|
||||
if _, found := f.uniqueData[s]; !found {
|
||||
f.filterData = append(f.filterData, ad)
|
||||
@ -63,20 +135,45 @@ func (f *GolombFilter) AddAddrDesc(ad AddressDescriptor) {
|
||||
}
|
||||
}
|
||||
|
||||
// Including all the address descriptors from non-ordinal transactions
|
||||
func (f *GolombFilter) includeAllAddressDescriptorsOrdinals() {
|
||||
for txid, ads := range f.allAddressDescriptors {
|
||||
// Ignoring the txids that contain ordinal data
|
||||
if _, found := f.ordinalTxIds[txid]; found {
|
||||
continue
|
||||
}
|
||||
for _, ad := range ads {
|
||||
f.includeAddrDesc(ad)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute computes golomb filter from the data
|
||||
func (f *GolombFilter) Compute() []byte {
|
||||
m := uint64(1 << uint64(f.p))
|
||||
m := GetGolombParamM(f.p)
|
||||
|
||||
// In case of ignoring the ordinals, we still need to assemble the filter data
|
||||
if f.ignoreOrdinals() {
|
||||
f.includeAllAddressDescriptorsOrdinals()
|
||||
}
|
||||
|
||||
if len(f.filterData) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, _ := hex.DecodeString(f.key)
|
||||
if len(b) < gcs.KeySize {
|
||||
return nil
|
||||
// Used key is possibly just zeroes, otherwise get it from the supplied key
|
||||
var key [gcs.KeySize]byte
|
||||
if f.UseZeroedKey {
|
||||
key = [gcs.KeySize]byte{}
|
||||
} else {
|
||||
b, _ := hex.DecodeString(f.key)
|
||||
if len(b) < gcs.KeySize {
|
||||
return nil
|
||||
}
|
||||
copy(key[:], b[:gcs.KeySize])
|
||||
}
|
||||
|
||||
filter, err := gcs.BuildGCSFilter(f.p, m, *(*[gcs.KeySize]byte)(b[:gcs.KeySize]), f.filterData)
|
||||
filter, err := gcs.BuildGCSFilter(f.p, m, key, f.filterData)
|
||||
if err != nil {
|
||||
glog.Error("Cannot create golomb filter for ", f.key, ", ", err)
|
||||
return nil
|
||||
@ -91,12 +188,30 @@ func (f *GolombFilter) Compute() []byte {
|
||||
return fb
|
||||
}
|
||||
|
||||
func (f *GolombFilter) ignoreNonTaproot() bool {
|
||||
switch f.filterScriptsType {
|
||||
case FilterScriptsTaproot, FilterScriptsTaprootNoOrdinals:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *GolombFilter) ignoreOrdinals() bool {
|
||||
switch f.filterScriptsType {
|
||||
case FilterScriptsTaprootNoOrdinals:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func filterScriptsToScriptsType(filterScripts string) FilterScriptsType {
|
||||
switch filterScripts {
|
||||
case "":
|
||||
return FilterScriptsAll
|
||||
case "taproot":
|
||||
return FilterScriptsTaproot
|
||||
case "taproot-noordinals":
|
||||
return FilterScriptsTaprootNoOrdinals
|
||||
}
|
||||
return FilterScriptsInvalid
|
||||
}
|
||||
|
||||
@ -22,11 +22,12 @@ type MempoolBitcoinType struct {
|
||||
AddrDescForOutpoint AddrDescForOutpointFunc
|
||||
golombFilterP uint8
|
||||
filterScripts string
|
||||
useZeroedKey bool
|
||||
}
|
||||
|
||||
// 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
|
||||
func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golombFilterP uint8, filterScripts string) *MempoolBitcoinType {
|
||||
func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golombFilterP uint8, filterScripts string, useZeroedKey bool) *MempoolBitcoinType {
|
||||
m := &MempoolBitcoinType{
|
||||
BaseMempool: BaseMempool{
|
||||
chain: chain,
|
||||
@ -37,6 +38,7 @@ func NewMempoolBitcoinType(chain BlockChain, workers int, subworkers int, golomb
|
||||
chanAddrIndex: make(chan txidio, 1),
|
||||
golombFilterP: golombFilterP,
|
||||
filterScripts: filterScripts,
|
||||
useZeroedKey: useZeroedKey,
|
||||
}
|
||||
for i := 0; i < workers; i++ {
|
||||
go func(i int) {
|
||||
@ -97,18 +99,18 @@ func (m *MempoolBitcoinType) getInputAddress(payload *chanInputPayload) *addrInd
|
||||
|
||||
}
|
||||
|
||||
func (m *MempoolBitcoinType) computeGolombFilter(mtx *MempoolTx) string {
|
||||
gf, _ := NewGolombFilter(m.golombFilterP, m.filterScripts, mtx.Txid)
|
||||
func (m *MempoolBitcoinType) computeGolombFilter(mtx *MempoolTx, tx *Tx) string {
|
||||
gf, _ := NewGolombFilter(m.golombFilterP, "", mtx.Txid, m.useZeroedKey)
|
||||
if gf == nil || !gf.Enabled {
|
||||
return ""
|
||||
}
|
||||
for _, vin := range mtx.Vin {
|
||||
gf.AddAddrDesc(vin.AddrDesc)
|
||||
gf.AddAddrDesc(vin.AddrDesc, tx)
|
||||
}
|
||||
for _, vout := range mtx.Vout {
|
||||
b, err := hex.DecodeString(vout.ScriptPubKey.Hex)
|
||||
if err == nil {
|
||||
gf.AddAddrDesc(b)
|
||||
gf.AddAddrDesc(b, tx)
|
||||
}
|
||||
}
|
||||
fb := gf.Compute()
|
||||
@ -168,7 +170,7 @@ func (m *MempoolBitcoinType) getTxAddrs(txid string, chanInput chan chanInputPay
|
||||
}
|
||||
var golombFilter string
|
||||
if m.golombFilterP > 0 {
|
||||
golombFilter = m.computeGolombFilter(mtx)
|
||||
golombFilter = m.computeGolombFilter(mtx, tx)
|
||||
}
|
||||
if m.OnNewTx != nil {
|
||||
m.OnNewTx(mtx)
|
||||
@ -249,5 +251,5 @@ func (m *MempoolBitcoinType) GetTxidFilterEntries(filterScripts string, fromTime
|
||||
}
|
||||
}
|
||||
m.mux.Unlock()
|
||||
return MempoolTxidFilterEntries{entries}, nil
|
||||
return MempoolTxidFilterEntries{entries, m.useZeroedKey}, nil
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ func TestMempoolBitcoinType_computeGolombFilter_taproot(t *testing.T) {
|
||||
golombFilterP: 20,
|
||||
filterScripts: "taproot",
|
||||
}
|
||||
golombFilterM := uint64(1 << uint64(m.golombFilterP))
|
||||
golombFilterM := GetGolombParamM(m.golombFilterP)
|
||||
tests := []struct {
|
||||
name string
|
||||
mtx MempoolTx
|
||||
|
||||
@ -57,6 +57,7 @@ type Vin struct {
|
||||
ScriptSig ScriptSig `json:"scriptSig"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
Addresses []string `json:"addresses"`
|
||||
Witness [][]byte `json:"witness"`
|
||||
}
|
||||
|
||||
// ScriptPubKey contains data about output script
|
||||
@ -273,8 +274,10 @@ type XpubDescriptor struct {
|
||||
type MempoolTxidEntries []MempoolTxidEntry
|
||||
|
||||
// MempoolTxidFilterEntries is a map of txids to mempool golomb filters
|
||||
// Also contains a flag whether constant zeroed key was used when calculating the filters
|
||||
type MempoolTxidFilterEntries struct {
|
||||
Entries map[string]string `json:"entries,omitempty"`
|
||||
Entries map[string]string `json:"entries,omitempty"`
|
||||
UsedZeroedKey bool `json:"usedZeroedKey,omitempty"`
|
||||
}
|
||||
|
||||
// OnNewBlockFunc is used to send notification about a new block
|
||||
|
||||
@ -9,15 +9,16 @@ import (
|
||||
|
||||
// Config struct
|
||||
type Config struct {
|
||||
CoinName string `json:"coin_name"`
|
||||
CoinShortcut string `json:"coin_shortcut"`
|
||||
CoinLabel string `json:"coin_label"`
|
||||
FourByteSignatures string `json:"fourByteSignatures"`
|
||||
FiatRates string `json:"fiat_rates"`
|
||||
FiatRatesParams string `json:"fiat_rates_params"`
|
||||
FiatRatesVsCurrencies string `json:"fiat_rates_vs_currencies"`
|
||||
BlockGolombFilterP uint8 `json:"block_golomb_filter_p"`
|
||||
BlockFilterScripts string `json:"block_filter_scripts"`
|
||||
CoinName string `json:"coin_name"`
|
||||
CoinShortcut string `json:"coin_shortcut"`
|
||||
CoinLabel string `json:"coin_label"`
|
||||
FourByteSignatures string `json:"fourByteSignatures"`
|
||||
FiatRates string `json:"fiat_rates"`
|
||||
FiatRatesParams string `json:"fiat_rates_params"`
|
||||
FiatRatesVsCurrencies string `json:"fiat_rates_vs_currencies"`
|
||||
BlockGolombFilterP uint8 `json:"block_golomb_filter_p"`
|
||||
BlockFilterScripts string `json:"block_filter_scripts"`
|
||||
BlockFilterUseZeroedKey bool `json:"block_filter_use_zeroed_key"`
|
||||
}
|
||||
|
||||
// GetConfig loads and parses the config file and returns Config struct
|
||||
|
||||
@ -94,8 +94,9 @@ type InternalState struct {
|
||||
SortedAddressContracts bool `json:"sortedAddressContracts"`
|
||||
|
||||
// golomb filter settings
|
||||
BlockGolombFilterP uint8 `json:"block_golomb_filter_p"`
|
||||
BlockFilterScripts string `json:"block_filter_scripts"`
|
||||
BlockGolombFilterP uint8 `json:"block_golomb_filter_p"`
|
||||
BlockFilterScripts string `json:"block_filter_scripts"`
|
||||
BlockFilterUseZeroedKey bool `json:"block_filter_use_zeroed_key"`
|
||||
}
|
||||
|
||||
// StartedSync signals start of synchronization
|
||||
|
||||
@ -185,7 +185,7 @@ func (b *BulkConnect) storeBulkBlockFilters(wb *grocksdb.WriteBatch) error {
|
||||
|
||||
func (b *BulkConnect) connectBlockBitcoinType(block *bchain.Block, storeBlockTxs bool) error {
|
||||
addresses := make(addressesMap)
|
||||
gf, err := bchain.NewGolombFilter(b.d.is.BlockGolombFilterP, b.d.is.BlockFilterScripts, block.BlockHeader.Hash)
|
||||
gf, err := bchain.NewGolombFilter(b.d.is.BlockGolombFilterP, b.d.is.BlockFilterScripts, block.BlockHeader.Hash, b.d.is.BlockFilterUseZeroedKey)
|
||||
if err != nil {
|
||||
glog.Error("connectBlockBitcoinType golomb filter error ", err)
|
||||
gf = nil
|
||||
|
||||
@ -349,7 +349,7 @@ func (d *RocksDB) ConnectBlock(block *bchain.Block) error {
|
||||
if chainType == bchain.ChainBitcoinType {
|
||||
txAddressesMap := make(map[string]*TxAddresses)
|
||||
balances := make(map[string]*AddrBalance)
|
||||
gf, err := bchain.NewGolombFilter(d.is.BlockGolombFilterP, d.is.BlockFilterScripts, block.BlockHeader.Hash)
|
||||
gf, err := bchain.NewGolombFilter(d.is.BlockGolombFilterP, d.is.BlockFilterScripts, block.BlockHeader.Hash, d.is.BlockFilterUseZeroedKey)
|
||||
if err != nil {
|
||||
glog.Error("ConnectBlock golomb filter error ", err)
|
||||
gf = nil
|
||||
@ -643,7 +643,7 @@ func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses add
|
||||
continue
|
||||
}
|
||||
if gf != nil {
|
||||
gf.AddAddrDesc(addrDesc)
|
||||
gf.AddAddrDesc(addrDesc, tx)
|
||||
}
|
||||
tao.AddrDesc = addrDesc
|
||||
if d.chainParser.IsAddrDescIndexable(addrDesc) {
|
||||
@ -720,7 +720,7 @@ func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses add
|
||||
glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v is double spend", block.Height, tx.Txid, input.Txid, input.Vout)
|
||||
}
|
||||
if gf != nil {
|
||||
gf.AddAddrDesc(spentOutput.AddrDesc)
|
||||
gf.AddAddrDesc(spentOutput.AddrDesc, tx)
|
||||
}
|
||||
tai.AddrDesc = spentOutput.AddrDesc
|
||||
tai.ValueSat = spentOutput.ValueSat
|
||||
@ -1894,12 +1894,13 @@ func (d *RocksDB) LoadInternalState(config *common.Config) (*common.InternalStat
|
||||
var is *common.InternalState
|
||||
if len(data) == 0 {
|
||||
is = &common.InternalState{
|
||||
Coin: config.CoinName,
|
||||
UtxoChecked: true,
|
||||
SortedAddressContracts: true,
|
||||
ExtendedIndex: d.extendedIndex,
|
||||
BlockGolombFilterP: config.BlockGolombFilterP,
|
||||
BlockFilterScripts: config.BlockFilterScripts,
|
||||
Coin: config.CoinName,
|
||||
UtxoChecked: true,
|
||||
SortedAddressContracts: true,
|
||||
ExtendedIndex: d.extendedIndex,
|
||||
BlockGolombFilterP: config.BlockGolombFilterP,
|
||||
BlockFilterScripts: config.BlockFilterScripts,
|
||||
BlockFilterUseZeroedKey: config.BlockFilterUseZeroedKey,
|
||||
}
|
||||
} else {
|
||||
is, err = common.UnpackInternalState(data)
|
||||
@ -1922,6 +1923,9 @@ func (d *RocksDB) LoadInternalState(config *common.Config) (*common.InternalStat
|
||||
if is.BlockFilterScripts != config.BlockFilterScripts {
|
||||
return nil, errors.Errorf("BlockFilterScripts does not match. DB BlockFilterScripts %v, config BlockFilterScripts %v", is.BlockFilterScripts, config.BlockFilterScripts)
|
||||
}
|
||||
if is.BlockFilterUseZeroedKey != config.BlockFilterUseZeroedKey {
|
||||
return nil, errors.Errorf("BlockFilterUseZeroedKey does not match. DB BlockFilterUseZeroedKey %v, config BlockFilterUseZeroedKey %v", is.BlockFilterUseZeroedKey, config.BlockFilterUseZeroedKey)
|
||||
}
|
||||
}
|
||||
nc, err := d.checkColumns(is)
|
||||
if err != nil {
|
||||
|
||||
@ -1230,8 +1230,15 @@ func (s *PublicServer) apiBlockIndex(r *http.Request, apiVersion int) (interface
|
||||
|
||||
func (s *PublicServer) apiBlockFilters(r *http.Request, apiVersion int) (interface{}, error) {
|
||||
// Define return type
|
||||
type blockFilterResult struct {
|
||||
BlockHash string `json:"blockHash"`
|
||||
Filter string `json:"filter"`
|
||||
}
|
||||
type resBlockFilters struct {
|
||||
BlockFilters map[int]map[string]string `json:"blockFilters"`
|
||||
ParamP uint8 `json:"P"`
|
||||
ParamM uint64 `json:"M"`
|
||||
ZeroedKey bool `json:"zeroedKey"`
|
||||
BlockFilters map[int]blockFilterResult `json:"blockFilters"`
|
||||
}
|
||||
|
||||
// Parse parameters
|
||||
@ -1247,6 +1254,11 @@ func (s *PublicServer) apiBlockFilters(r *http.Request, apiVersion int) (interfa
|
||||
if ec != nil {
|
||||
to = 0
|
||||
}
|
||||
scriptType := r.URL.Query().Get("scriptType")
|
||||
if scriptType != s.is.BlockFilterScripts {
|
||||
return nil, api.NewAPIError(fmt.Sprintf("Invalid scriptType %s. Use %s", scriptType, s.is.BlockFilterScripts), true)
|
||||
}
|
||||
// NOTE: technically, we are also accepting "m: uint64" param, but we do not use it currently
|
||||
|
||||
// Sanity checks
|
||||
if lastN == 0 && from == 0 && to == 0 {
|
||||
@ -1278,7 +1290,7 @@ func (s *PublicServer) apiBlockFilters(r *http.Request, apiVersion int) (interfa
|
||||
}
|
||||
|
||||
handleBlockFiltersResultFromTo := func(fromHeight int, toHeight int) (interface{}, error) {
|
||||
blockFiltersMap := make(map[int]map[string]string)
|
||||
blockFiltersMap := make(map[int]blockFilterResult)
|
||||
for i := fromHeight; i <= toHeight; i++ {
|
||||
blockHash, err := s.db.GetBlockHash(uint32(i))
|
||||
if err != nil {
|
||||
@ -1290,12 +1302,15 @@ func (s *PublicServer) apiBlockFilters(r *http.Request, apiVersion int) (interfa
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
resultMap := make(map[string]string)
|
||||
resultMap["blockHash"] = blockHash
|
||||
resultMap["filter"] = blockFilter
|
||||
blockFiltersMap[i] = resultMap
|
||||
blockFiltersMap[i] = blockFilterResult{
|
||||
BlockHash: blockHash,
|
||||
Filter: blockFilter,
|
||||
}
|
||||
}
|
||||
return resBlockFilters{
|
||||
ParamP: s.is.BlockGolombFilterP,
|
||||
ParamM: bchain.GetGolombParamM(s.is.BlockGolombFilterP),
|
||||
ZeroedKey: s.is.BlockFilterUseZeroedKey,
|
||||
BlockFilters: blockFiltersMap,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -657,17 +657,67 @@ func (s *WebsocketServer) sendTransaction(tx string) (res resultSendTransaction,
|
||||
return
|
||||
}
|
||||
|
||||
func (s *WebsocketServer) getMempoolFilters(r *WsMempoolFiltersReq) (res bchain.MempoolTxidFilterEntries, err error) {
|
||||
res, err = s.mempool.GetTxidFilterEntries(r.ScriptType, r.FromTimestamp)
|
||||
return
|
||||
func (s *WebsocketServer) getMempoolFilters(r *WsMempoolFiltersReq) (res interface{}, err error) {
|
||||
type resMempoolFilters struct {
|
||||
ParamP uint8 `json:"P"`
|
||||
ParamM uint64 `json:"M"`
|
||||
ZeroedKey bool `json:"zeroedKey"`
|
||||
Entries map[string]string `json:"entries"`
|
||||
}
|
||||
filterEntries, err := s.mempool.GetTxidFilterEntries(r.ScriptType, r.FromTimestamp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resMempoolFilters{
|
||||
ParamP: s.is.BlockGolombFilterP,
|
||||
ParamM: bchain.GetGolombParamM(s.is.BlockGolombFilterP),
|
||||
ZeroedKey: filterEntries.UsedZeroedKey,
|
||||
Entries: filterEntries.Entries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *WebsocketServer) getBlockFilter(r *WsBlockFilterReq) (res string, err error) {
|
||||
return s.db.GetBlockFilter(r.BlockHash)
|
||||
func (s *WebsocketServer) getBlockFilter(r *WsBlockFilterReq) (res interface{}, err error) {
|
||||
type resBlockFilter struct {
|
||||
ParamP uint8 `json:"P"`
|
||||
ParamM uint64 `json:"M"`
|
||||
ZeroedKey bool `json:"zeroedKey"`
|
||||
BlockFilter string `json:"blockFilter"`
|
||||
}
|
||||
if s.is.BlockFilterScripts != r.ScriptType {
|
||||
return nil, errors.Errorf("Unsupported script type %s", r.ScriptType)
|
||||
}
|
||||
blockFilter, err := s.db.GetBlockFilter(r.BlockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resBlockFilter{
|
||||
ParamP: s.is.BlockGolombFilterP,
|
||||
ParamM: bchain.GetGolombParamM(s.is.BlockGolombFilterP),
|
||||
ZeroedKey: s.is.BlockFilterUseZeroedKey,
|
||||
BlockFilter: blockFilter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *WebsocketServer) getBlockFiltersBatch(r *WsBlockFiltersBatchReq) (res []string, err error) {
|
||||
return s.api.GetBlockFiltersBatch(r.BlockHash, r.PageSize)
|
||||
func (s *WebsocketServer) getBlockFiltersBatch(r *WsBlockFiltersBatchReq) (res interface{}, err error) {
|
||||
type resBlockFiltersBatch struct {
|
||||
ParamP uint8 `json:"P"`
|
||||
ParamM uint64 `json:"M"`
|
||||
ZeroedKey bool `json:"zeroedKey"`
|
||||
BlockFiltersBatch []string `json:"blockFiltersBatch"`
|
||||
}
|
||||
if s.is.BlockFilterScripts != r.ScriptType {
|
||||
return nil, errors.Errorf("Unsupported script type %s", r.ScriptType)
|
||||
}
|
||||
blockFiltersBatch, err := s.api.GetBlockFiltersBatch(r.BlockHash, r.PageSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resBlockFiltersBatch{
|
||||
ParamP: s.is.BlockGolombFilterP,
|
||||
ParamM: bchain.GetGolombParamM(s.is.BlockGolombFilterP),
|
||||
ZeroedKey: s.is.BlockFilterUseZeroedKey,
|
||||
BlockFiltersBatch: blockFiltersBatch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type subscriptionResponse struct {
|
||||
|
||||
@ -79,15 +79,20 @@ type WsTransactionReq struct {
|
||||
type WsMempoolFiltersReq struct {
|
||||
ScriptType string `json:"scriptType"`
|
||||
FromTimestamp uint32 `json:"fromTimestamp"`
|
||||
ParamM uint64 `json:"M,omitempty"`
|
||||
}
|
||||
|
||||
type WsBlockFilterReq struct {
|
||||
BlockHash string `json:"blockHash"`
|
||||
ScriptType string `json:"scriptType"`
|
||||
BlockHash string `json:"blockHash"`
|
||||
ParamM uint64 `json:"M,omitempty"`
|
||||
}
|
||||
|
||||
type WsBlockFiltersBatchReq struct {
|
||||
BlockHash string `json:"bestKnownBlockHash"`
|
||||
PageSize int `json:"pageSize,omitempty"`
|
||||
ScriptType string `json:"scriptType"`
|
||||
BlockHash string `json:"bestKnownBlockHash"`
|
||||
PageSize int `json:"pageSize,omitempty"`
|
||||
ParamM uint64 `json:"M,omitempty"`
|
||||
}
|
||||
|
||||
type WsTransactionSpecificReq struct {
|
||||
|
||||
@ -87,7 +87,11 @@
|
||||
var f = pendingMessages[resp.id];
|
||||
if (f != undefined) {
|
||||
delete pendingMessages[resp.id];
|
||||
f(resp.data);
|
||||
try {
|
||||
f(resp.data);
|
||||
} catch (e) {
|
||||
alert(`Error: ${e}.\nLook into the console for websocket response.`);
|
||||
}
|
||||
} else {
|
||||
f = subscriptions[resp.id];
|
||||
if (f != undefined) {
|
||||
@ -414,8 +418,10 @@
|
||||
function getBlockFilter() {
|
||||
const method = 'getBlockFilter';
|
||||
const blockHash = document.getElementById('getBlockFilterBlockHash').value;
|
||||
const scriptType = document.getElementById('getBlockFilterBlockHashScriptType').value;
|
||||
const params = {
|
||||
blockHash,
|
||||
scriptType,
|
||||
};
|
||||
send(method, params, function (result) {
|
||||
document.getElementById('getBlockFilterResult').innerText = JSON.stringify(result).replace(/,/g, ", ");
|
||||
@ -426,8 +432,10 @@
|
||||
const method = 'getBlockFiltersBatch';
|
||||
const bestKnownBlockHash = document.getElementById('getBlockFiltersBatchBlockHash').value;
|
||||
const pageSize = parseInt(document.getElementById("getBlockFiltersBatchPageSize").value);
|
||||
const scriptType = document.getElementById('getBlockFiltersBatchScriptType').value;
|
||||
const params = {
|
||||
bestKnownBlockHash,
|
||||
scriptType,
|
||||
};
|
||||
if (pageSize) params.pageSize = pageSize;
|
||||
send(method, params, function (result) {
|
||||
@ -719,6 +727,7 @@
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<input type="text" class="form-control" id="getBlockFilterBlockHash" value="000000000000001cb4edd91be03b6775abd351fb51b1fbb0871fc1451454f362" placeholder="block hash">
|
||||
<input type="text" class="form-control" id="getBlockFilterBlockHashScriptType" value="taproot-noordinals" placeholder="filter script">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@ -732,6 +741,7 @@
|
||||
<div class="row" style="margin: 0;">
|
||||
<input type="text" class="form-control" id="getBlockFiltersBatchBlockHash" style="width: 80%; margin-right: 5px;" value="000000000000001cb4edd91be03b6775abd351fb51b1fbb0871fc1451454f362" placeholder="best known block hash">
|
||||
<input type="text" class="form-control" placeholder="page size" style="width: 15%;" id="getBlockFiltersBatchPageSize" value="">
|
||||
<input type="text" class="form-control" id="getBlockFiltersBatchScriptType" value="taproot-noordinals" placeholder="filter script">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user