Compare commits

..

75 commits

Author SHA1 Message Date
Jonathan Moody
d2193e980a
blockchain.transaction.broadcast implementation (#80)
* Generate secondary hashXNotification(s) on every headerNotification.

* Attempt LBCD connection with rpc.Client.

* Optional --daemon-url.

* Correct HashXStatusKey field. Should be HASHX_LEN.

* Connect to lbcd using lbcd/rpcclient library.

* Handle deprecation of node.js 12 actions.

* Add --daemon-ca-path argument and establish HTTPS connection if specified.

* Remove dead code. Tighten definition of TransactionBroadcastReq.

* Correct default daemon URL.
2022-12-16 13:54:19 -06:00
Jeffrey Picard
711c4b4b7b
WIP: Json rpc federation, search/getclaimbyid, and shutdown (#76)
* Cleanup shutdown and peers subscribe

this has errors currently, need to figure out data race

* fixed data race finally....

* getclaimbyid and search

* hook jsonrpc peers subcribe into current federation

* cleanup some peers stuff

* remove commented code

* tie into session manager in jsonrpc peers subscribe

* use int for port args

* cleanup and fix a bunch of compiler warnings

* use logrus everywhere

* cleanup test
2022-12-07 11:01:36 -05:00
Jonathan Moody
317cdf7129
WIP: blockchain.transaction.yyy JSON RPC implementations (#78)
* Partial blockchain.transaction.yyy RPC implementations.

* Register RPC service object.

* Move session manager start/stop to a better place.

* Attempt to fill in the details of transaction.get_batch,
including merkle path.

* Correct interpretation of DBStateValue Genesis hash.

* Convert Args.Port to int and validate. Run UDP ping server on JSONRPCPort too.

* Add BlockHeader to HeightHash notification.

* Limit session-based JSON RPC service to IPv4.
Client not ready for IPv6.

* Adapt to new HeightHash struct.

* Fine tune JSON RPC handlers and types to match lbry-sdk expectations.
Implement UnmarshalJSON()/MarshalJSON() for several types.

* Add more special handling of DBStateValue.Genesis hash.

* Set IncludeStop=false generally to avoid returning extra rows.
Other misc fixes.
2022-12-06 16:14:28 -05:00
Jonathan Moody
e070e8a51e
JSON RPC compatibility workarounds to support lbry-sdk (#75)
* Fix log spam (Already shutting down...)

* Workaround to allow lbry-sdk to call server.version and server.features.
Incoming/outgoing JSON is patched using yet another codec (jsonPatchingCodec).
Add more logging of raw/patched JSON.

* Elaborate comment on jsonPatchingCodec.
2022-10-29 18:42:24 +03:00
Jeffrey Picard
cd7f20a461
Server endpoints goroutine refactor (#69)
* server.xxx endpoints

Additional server endpoints in jsonrpc and also some refactoring

* server.banner

* more endpoints

* use lbry.go stop pattern

* set genesis hash properly

* updates and refactors

* remove shutdowncalled and itmut

* remove OpenIterators

* remove shutdown and done channels from db and use stop group

* currently broken, incorporated stop groups into the session manager

* set the rest of the default args for tests

* add default json rpc http port and cleanup

* tests for new jsonrpc endpoints

* cleanup and add manage goroutine to stopper pattern

* cleanup

* NewDebug

* asdf

* fix
2022-10-25 08:48:13 +03:00
Jeffrey Picard
b85556499f v0.2022.10.05.1 2022-10-05 06:25:42 +03:00
Jeffrey Picard
8eb7841600
little fixes for debugging and shutdown (#67) 2022-10-05 06:24:42 +03:00
Jeffrey Picard
6d4b9b5e37 v0.2022.10.04.1 2022-10-04 20:52:47 +03:00
Jeffrey Picard
537b8c7ddd
integration testing scripts (#64)
* integration testing scripts

some scripts for integration testing and a docker file for an action.
Still need to figure out how to properly run a more realistic version
in ci.

* update

* changes

* db shutdown racecondition fix

* changes per pr

* changes per code review

* fix testing

* add shutdowncalled bool to db
2022-10-04 20:25:44 +03:00
Jonathan Moody
8fb3db8136
Add subscribe/unsubscribe RPCs. Add session, sessionManager, and serve JSON RPC (without HTTP). (#66)
* Move and rename BlockchainCodec, BlockchainCodecRequest.
These are not specifically "blockchain", rather they are
specific to how gorilla/rpc works.

* Move claimtrie-related service/handlers to jsonrpc_claimtrie.go.

* Pull out decode logic into named func newBlockHeaderElectrum().

* Rename BlockchainService -> BlockchainBlockService.

* Drop http.Request arg from handlers, and use RegisterTCPService().

* Implement GetStatus() to pull data from HashXStatus table.

* Make the service objects independent, so we don't have inheritance.

* Add core session/subscription logic (session.go).
Implement subsribe/unsubscribe handlers.

* Support both pure JSON and JSON-over-HTTP services.
Forward NotifierChan messages to sessionManager.

* Only assign default port (50001) if neither --json-rpc-port nor
--json-rpc-http-port are specified.

* Handle failures with goto instead of break. Update error logging.

* Add --max-sessions, --session-timeout args. Enforce max sessions.

* Changes to make session.go testable. Conn created with Pipe()
used in testing has no unique Addr.

* Add tests for headers, headers.subscribe, address.subscribe.

* HashXStatus, HashXMempoolStatus not populated by default. Fix GetStatus().

* Use time.Ticker object to drive management activity.
2022-10-04 17:05:06 +03:00
Jonathan Moody
979d0d16b6
Adjust EffectiveAmountValue to include ActivatedSupportSum. (#61)
Make use of this in GetEffectiveAmount() tests.
2022-09-16 18:03:15 +03:00
Jonathan Moody
7d24ff82bf
Merge pull request #55 from moodyjon/blockchain_rpc1
Add some blockchain RPC handlers and DB fetching routines
2022-09-14 10:23:34 -05:00
Jonathan Moody
86a287ec69 Skip TestUDPPing if production server is down. 2022-09-13 15:35:54 -05:00
Jonathan Moody
891f63fb5c Update jsonrpc_blockchain_tests.go for new sample data. 2022-09-13 15:33:24 -05:00
Jonathan Moody
789974227f Add sample data from test_variety_of_transactions_and_longish_history.
Rework tests to use the sample data.
2022-09-13 15:05:11 -05:00
Jonathan Moody
71e79c553e One more RPC (get_server_height), and update comment
to include full RPC name.
2022-09-08 13:17:52 -05:00
Jonathan Moody
b298454727 Fix RPC handler registration and BlockGetChunkResp name. 2022-09-08 11:50:06 -05:00
Jonathan Moody
8c8871b4d2 Register blockchain.* handlers in jsonrpc_service.go. 2022-09-07 15:01:47 -05:00
Jonathan Moody
20e32437e9 Rename blockchain.go -> jsonrpc_blockchain.go. 2022-09-07 14:32:34 -05:00
Jonathan Moody
d0d6145f9d Refactor blockchain.go handlers to be compatible with gorilla/rpc.
Add speculative BlockchainCodecRequest which might handle rewriting
incoming method names.
2022-09-07 14:32:34 -05:00
Jonathan Moody
90afae7cd5 Add scripthash variants of RPC handlers. 2022-09-07 14:32:34 -05:00
Jonathan Moody
f5b8f2ce0d Add --chain=X argument, and use it to determine chain when DB is empty. 2022-09-07 14:32:34 -05:00
Jonathan Moody
321bcf6420 Fix encoding of TX and Block hashes in response. 2022-09-07 14:32:34 -05:00
Jonathan Moody
293a3f685e Fix some DB logic and add tests (blockchain_test.go). 2022-09-07 14:32:34 -05:00
Jonathan Moody
f55a5ed777 Continuing blockchain RPC handler work. Add JSON tags.
Fetch Height using TxCounts.
2022-09-07 14:32:34 -05:00
Jonathan Moody
50f7e91ead Infer chain (mainnet, testnet3, regtest) based on DBStateValue.
Correct typo DDVersion -> DBVersion. Misc logging improvements.
2022-09-07 14:32:34 -05:00
Jonathan Moody
fe18c70bf7 Add some blockchain RPC handlers and database fetching routines. 2022-09-07 14:32:20 -05:00
Jeffrey Picard
9403d84a83
WIP: Resolve json rpc (#57)
* jsonrpc

* update readme for open file limits

* add CGO flags to readme

* remove uneeded logging

* don't start jsonrpc server in unit tests

* cleanup and add args for json rpc

* correct rpc default port

* remove unused test_rpc.sh script

Co-authored-by: Ubuntu <ubuntu@ns5010184.ip-15-235-15.net>
2022-09-07 21:36:07 +03:00
Jonathan Moody
09fd939b60
Merge pull request #60 from moodyjon/slicebacked_type_param
Update go.mod, go.sum for use of constraints (x/exp).
2022-09-07 07:22:01 -05:00
Jonathan Moody
dc9b4ada2a Update go.mod, go.sum for use of constraints (x/exp). 2022-09-07 07:13:51 -05:00
Jonathan Moody
c38134b645
Merge pull request #56 from moodyjon/slicebacked_type_param
Add element type param T to SlicedBacked[T]. Require T satisfy
2022-09-07 05:39:34 -05:00
Jonathan Moody
d025ea1616
Add "on: pull_request" to worflow. (#59) 2022-09-06 22:13:13 +03:00
Jonathan Moody
5b690ff2ff
Merge pull request #58 from moodyjon/hashx_history_fix
Fix struct annotation for HashXHistoryValue. TxNums now little-endian.
2022-09-06 13:03:07 -05:00
Jonathan Moody
aa16207aa5 Fix struct annotation for HashXHistoryValue. TxNums now little-endian. 2022-09-06 12:57:45 -05:00
Jonathan Moody
78b9a625eb
Merge pull request #54 from moodyjon/hashx_history_fix
Payload of HashXHistoryValue should be an array of uint32 representing "txnums"
2022-09-06 12:40:06 -05:00
Jonathan Moody
e46ac7c913 HashXHistoryValue TxNums are unique in that they are little-endian
(at least when written by Python scribe on ARM64 Mac or x86).
2022-09-01 13:01:16 -05:00
Jonathan Moody
8ac89195db Add element type param T to SlicedBacked[T]. Require T satisfy
constraints.Ordered to make BisectRight() statically type-safe.
2022-08-30 16:24:43 -05:00
Jonathan Moody
4e11433325 Payload of HashXHistoryValue should be an array of uint32 representing "txnums". 2022-08-26 10:18:01 -04:00
Jonathan Moody
9d9c73f97f
Add RepostedCount, EffectiveAmount prefix rows (#51)
* Rename prefix EffectiveAmount -> BidOrder.

* Add RepostedCount, EffectiveAmount prefix rows. Add testdata.

* Update db_get.go helpers to use EffectiveAmount, RepostedCount
tables. Update tests.
2022-08-26 16:24:39 +03:00
Jeffrey Picard
cbdcc5faeb v0.2022.08.16.1 2022-08-16 14:56:19 +03:00
Jeffrey Picard
3a53f46114
Updates for build (#50)
* Updates for build

* go 1.18.1 in dockerfile

* use go 1.18.5

* trying this ...

* asdf
2022-08-16 14:52:26 +03:00
Jonathan Moody
071aa2a7ad
Catchup to python-herald schema. Plus lots of refactoring. (#49)
* Make prefixes_test.go more resilient against garbage left
by a prior crash. Also correct error logging.

* Don't do the ones' complement thing with DBStateValue fields
HistFlushCount, CompFlushCount, CompCursor. Python-herald
doesn't do it, and it presents one more irregular case for
(un)marshalling fields.

* Simplify type-specific partial packing, and simplify dispatch for pack key/value.

* Add struct field annotations and refactor to prepare for
use of "restruct" generic packing/unpacking.

* Add dynamic pack/unpack based on "restruct" module.
Dispatch normal pack/unpack through tableRegistry[] map
instead of switch.

* Add 5 new prefixes/tables (TrendingNotifications..HashXMempoolStatus).

* Undo rename. TouchedOrDeleted -> ClaimDiff.

* Fixup callers of eliminated partial pack functions. Have them use key.PartialPack(n).

* Add pluggable SerializationAPI. Use it in prefixes_test.
Populate PrefixRowKV.RawKey,RawValue when appropriate.

* Undo accidental bump of rocksdb version.

* Add .vscode dir to gitignore.

* Fix ClaimToChannelValue annotation. Implement BlockTxsValue workaround
as I can't find the right annotation to get it marshalled/unmarshalled.

* Strengthen partial packing verification. Fix bugs
in UnpackKey/UnpackValue for new types.

* Remove .DS_Store, and ignore in future.

* Fix MempoolTxKey, TouchedHashXValue. Remove some unneeded struct tags.

* Generate test data and complete the tests for the new tables.
Add Fuzz tests for TouchedHashXKey, TouchedHashXValue with
happy path test data (only).

* Move tableRegistry to prefixes.go and rename it prefixRegistry.
Other minor fixes, comments.

* Add test that runs through GetPrefixes() contents, and verifies
they are registered in prefixRegistry.
2022-08-16 08:45:41 +03:00
Jeffrey Picard
b018217899
fix release script (#48) 2022-08-10 21:06:56 +03:00
Jack Robison
13479794ed
Update readme.md 2022-08-10 11:05:39 -04:00
Jeffrey Picard
ab2a39ac6c v0.2022.08.09.1 2022-08-09 17:19:53 +03:00
Jonathan Moody
ffa426b899
Bump to grpc v1.46.0. (#45) 2022-08-09 14:44:00 +03:00
Jeffrey Picard
b9f7d595bd
Herald.go (#47)
* switch herald to herald.go

* update ci/cd stuff

* fix issues with binary name

* we're no longer building dynamically, so turn CGO back on, and fix names

* update package names in proto files
2022-08-09 14:43:01 +03:00
Jonathan Moody
49e5d7b8c1
Fix release_time query to match python hub. (#41)
* Revise error handling and cleanup in GetProdDB(). Avoids panic when DB not found.

* Fix release_time query to match python hub. Claims with unset release_time match the query.
2022-08-03 19:19:25 +03:00
Jack Robison
8b4b27bdb9
Update readme.md 2022-08-02 11:53:23 -04:00
Jeffrey Picard
b2dd70bc7c
Jeffreypicard/feature/move to herald (#37)
* changes for repo move

* Go back to protobuf 1.27.1, update go to 1.18

* update readme for new rocksdb version
2022-07-11 20:31:27 +03:00
Jeffrey Picard
7fcb4a750d remove old workflow 2022-04-29 22:30:11 +00:00
Jeffrey Picard
3dfe5a5bc3 fix workflows 2022-04-29 17:05:15 +00:00
Jeffrey Picard
28a299efa8
rocksdb (#29)
* Initial rocksdb commit

Basic reading from rocksdb works

* Try github action thing

* try local dockerfile

* asdf

* qwer

* asdf

* Try adding test db with git-lfs

* update action

* cleanup

* Don't hardcode stop on read

* Progress of reading rocksdb

* fixes and arg test

* asdf

* Fix rocksdb iterator and tests

* update script

* asdf

* Better iterator. Need to implement a lot of keys next, and tests, maybe
tests needed.

* asdf

* asdf

* asdf

* Implementation, testing, and cleanup.

Implemented more prefixes. Figured out a good test that should work for
all prefixes. Removed binary databases so we can just store human
readable csv files.

* more tests, prefixes and small refactor

* Another prefix

* EffectiveAmount

* ActiveAmount

* ActivatedClaimAndSupport

* PendingActivation

* ClaimTakeover

* ClaimExpiration

* SupportToClaim

* ClaimToSupport

* Fix bug with variable length keys

* ChannelToClaim

* ClaimToChannel

* ClaimShortID

* TXOToClaim

* ClaimToTXO

* BlockHeader

* BlockHash

* Undo

* HashXHistory

* Tx and big refactor

* rest the the keys

* Refactor and starting to add resolve

* asdf

* Refactor tests and add column families

* changes

* more work on implementing resolve

* code cleanup, function tests

* small code refactoring

* start building pieces of the test data set for full resolve.

* Export constant, add test

* another test

* TestGetTxHash

* more tests

* more tests

* More tests

* Refactor db functions into three files

* added slice backed stack, need to fix tests

* fix some issues with test suite

* some cleanup and adding arguments and db load / refresh to server command

* fix some bugs, start using logrus for leveled logging, upgrade to go 1.17, run go mod tidy

* logrus, protobuf updates, resolve grpc endpoint

* don't run integration test with unit tests

* signal handling and cleanup functions

* signal handling code files

* Unit tests for db stack

* reorganize bisect function so we lock it properly

* fix txcounts loading

* cleanup some logic around iterators and fix a bug where I was running two detect changes threads

* add some metrics

* cleanup

* blocking and filtering implemented

* add params for blocking and filtering channels and streams

* updates and fixes for integration tests

* use newer version of lbry.go when possible

* Add height endpoint and move string functions internal

* remove gitattributes, unused

* some cleanup

* more cleanup / refactor. almost ready for another review

* More cleanup

* use chainhash.Hash types from lbcd where appropriate

* update github action to go-1.17.8

* update go version needed

* trying to fix these builds

* cleanup

* trying to fix memory leak

* fix memory leak (iterator never finished so cleanup didn't run)

* changes per code review

* remove lbry.go v2

* rename sort.go search.go

* fix test
2022-04-29 11:04:01 -04:00
Jeffrey Picard
ff0694def1 v0.2022.01.21.1 2022-01-21 12:27:09 -05:00
Alex Grin
78f35cc96c
Merge pull request #31 from lbryio/bugfix/x/jeffreypicard/return-empty-list-on-no-index
Fix behavior when the index isn't there.
2022-01-10 11:30:26 -05:00
Jeffrey Picard
d51f5ca064 Fix behavior when the index isn't there.
One of the tests expects an empty list returned, we're currently
returning an error.
2022-01-05 10:07:15 -05:00
Jeffrey Picard
9e2a39e938 v0.2021.12.18.1 2021-12-18 11:14:37 -05:00
Victor Shyba
8e99bc56d0
add sd_hash prefix search (#30)
* add sd_hash to search request

* search sd_hash by prefix
2021-12-17 09:37:32 -05:00
Jeffrey Picard
447e931f18 v0.2021.12.06.1 2021-12-06 17:42:34 -05:00
Jeffrey Picard
516b95d96b
Merge pull request #24 from lbryio/feature/18/jeffreypicard/udp-endpoint
UDPServer / ip address resolution
2021-12-06 11:57:47 -05:00
Jeffrey Picard
2a1d6fa7d4 Updates based on code review 2021-12-06 11:32:45 -05:00
Jeffrey Picard
2e52c1639c Refactor and fixes related to PR comments. 2021-12-06 11:32:45 -05:00
Jeffrey Picard
1c1d288654 Set UDP flags correctly. 2021-12-06 11:32:45 -05:00
Jeffrey Picard
283686ecac Fix startup flags 2021-12-06 11:32:45 -05:00
Jeffrey Picard
ca0e8562f3 Add disable-federation flag, default false 2021-12-06 11:32:45 -05:00
Jeffrey Picard
72ea236d86 Run gofmt 2021-12-06 11:31:51 -05:00
Jeffrey Picard
0a1ba43d66 Full SPVPong parsing and tests against prod server. 2021-12-06 11:26:29 -05:00
Jeffrey Picard
4e6b47c2a3 Test UDPPing country parsing. 2021-12-06 11:26:29 -05:00
Jeffrey Picard
ee5fcaef14 Added test for getting IP with udp from prod servers. 2021-12-06 11:26:29 -05:00
Jeffrey Picard
159f4b941b Add claim protobuf definition for UDP country encoding. 2021-12-06 11:26:29 -05:00
Jeffrey Picard
355eab682c changes based on comments 2021-12-06 11:26:29 -05:00
Jeffrey Picard
395e1db489 UDPServer / ip address resolution
Got the UDPServer ping/pong protocol working internally, only tested
against other running go hub servers. Should in theory work with
python server and clients, but still need to test that.

Also switched to serving udp on the same port as grpc, and taking that
into account when pinging other hubs with udp.

Unit test for udp ip address lookup.
2021-12-06 11:26:29 -05:00
Jeffrey Picard
602292281c
Merge pull request #28 from lbryio/feature/jeffreypicard/update-range-query
Update RangeField
2021-12-06 11:24:47 -05:00
Jeffrey Picard
d511b08736 Switch RangeField to int32 in the protobuf, as none of those fields use
strings.
2021-12-03 19:39:40 -05:00
Jeffrey Picard
2537a84b86 Update RangeField
Allow repeated range fields per term (for i.e. 0 < x < 100)
2021-12-03 13:02:06 -05:00
136 changed files with 31981 additions and 889 deletions

View file

@ -1,14 +1,12 @@
name: 'Build and Test Hub' name: 'Build and Test Hub'
on: on: ["push", "pull_request"]
push:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - name: Checkout
- uses: actions/setup-go@v2.1.3 uses: actions/checkout@v3
with: - name: Build and Test
go-version: 1.16.5 uses: ./
- run: go build .
- run: cd server && go test -v -race

View file

@ -1,33 +0,0 @@
name: 'Build and Test Hub'
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2.1.3
with:
go-version: 1.16.5
- run: |
sudo apt update && \
sudo apt upgrade && \
sudo apt-get install autoconf automake libtool curl make g++ unzip && \
cd /tmp && \
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.17.1/protobuf-all-3.17.1.tar.gz && \
tar xfzv protobuf-all-3.17.1.tar.gz && \
cd protobuf-3.17.1 && \
./autogen.sh && \
./configure && \
make && \
sudo make install && \
sudo ldconfig && \
cd
- run: pip install grpcio grpcio-tools github3.py
- run: go get github.com/golang/protobuf/protoc-gen-go google.golang.org/grpc/cmd/protoc-gen-go-grpc
- run: go build .
- run: ./protobuf/build.sh
- run: cd server && go test -v -race

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.idea/
.vscode/
.DS_Store
.venv

View file

@ -1,10 +0,0 @@
FROM golang:alpine as stage1
EXPOSE 50051
RUN mkdir /app
WORKDIR /app
COPY . /app
RUN go build
FROM alpine:latest
COPY --from=stage1 /app/hub /hub
ENTRYPOINT ["/hub", "serve"]

5
action.yml Normal file
View file

@ -0,0 +1,5 @@
name: 'Build and Test'
description: 'Build and test hub'
runs:
using: 'docker'
image: 'jeffreypicard/hub-github-action:dev'

1079
db/db.go Normal file

File diff suppressed because it is too large Load diff

1157
db/db_get.go Normal file

File diff suppressed because it is too large Load diff

576
db/db_resolve.go Normal file
View file

@ -0,0 +1,576 @@
package db
// db_resolve.go contains functions relevant to resolving a claim.
import (
"bytes"
"encoding/hex"
"fmt"
"math"
"sort"
"strings"
"github.com/lbryio/herald.go/db/prefixes"
"github.com/lbryio/herald.go/db/stack"
"github.com/lbryio/herald.go/internal"
pb "github.com/lbryio/herald.go/protobuf/go"
lbryurl "github.com/lbryio/lbry.go/v3/url"
log "github.com/sirupsen/logrus"
)
// PrepareResolveResult prepares a ResolveResult to return
func PrepareResolveResult(
db *ReadOnlyDBColumnFamily,
txNum uint32,
position uint16,
claimHash []byte,
name string,
rootTxNum uint32,
rootPosition uint16,
activationHeight uint32,
signatureValid bool) (*ResolveResult, error) {
normalizedName := internal.NormalizeName(name)
controllingClaim, err := db.GetControllingClaim(normalizedName)
if err != nil {
return nil, err
}
txHash, err := db.GetTxHash(txNum)
if err != nil {
return nil, err
}
heights := stack.BisectRight(db.TxCounts, []uint32{txNum, rootTxNum})
height, createdHeight := heights[0], heights[1]
lastTakeoverHeight := controllingClaim.Height
expirationHeight := GetExpirationHeight(height)
supportAmount, err := db.GetSupportAmount(claimHash)
if err != nil {
return nil, err
}
claimToTxo, err := db.GetCachedClaimTxo(claimHash, true)
if err != nil {
return nil, err
}
claimAmount := claimToTxo.Amount
effectiveAmount, err := db.GetEffectiveAmount(claimHash, false)
if err != nil {
return nil, err
}
channelHash, err := db.GetChannelForClaim(claimHash, txNum, position)
if err != nil {
return nil, err
}
repostedClaimHash, err := db.GetRepost(claimHash)
if err != nil {
return nil, err
}
var repostTxHash []byte
var repostTxPostition uint16
var repostHeight uint32
if repostedClaimHash != nil {
repostTxo, err := db.GetCachedClaimTxo(repostedClaimHash, true)
if err != nil {
return nil, err
}
if repostTxo != nil {
repostTxHash, err = db.GetTxHash(repostTxo.TxNum)
if err != nil {
return nil, err
}
repostTxPostition = repostTxo.Position
repostHeight = stack.BisectRight(db.TxCounts, []uint32{repostTxo.TxNum})[0]
}
}
shortUrl, err := db.GetShortClaimIdUrl(name, normalizedName, claimHash, txNum, rootPosition)
if err != nil {
return nil, err
}
var canonicalUrl string = shortUrl
claimsInChannel, err := db.GetClaimsInChannelCount(claimHash)
if err != nil {
return nil, err
}
var channelTxHash []byte
var channelTxPostition uint16
var channelHeight uint32
if channelHash != nil {
// Ignore error because we already have this set if this doesn't work
channelVals, _ := db.GetCachedClaimTxo(channelHash, true)
log.Printf("channelVals: %#v\n", channelVals)
if channelVals != nil {
channelShortUrl, _ := db.GetShortClaimIdUrl(
channelVals.Name,
channelVals.NormalizedName(),
channelHash, channelVals.RootTxNum,
channelVals.RootPosition,
)
canonicalUrl = fmt.Sprintf("%s/%s", channelShortUrl, shortUrl)
channelTxHash, err = db.GetTxHash(channelVals.TxNum)
if err != nil {
return nil, err
}
channelTxPostition = channelVals.Position
channelHeight = stack.BisectRight(db.TxCounts, []uint32{channelVals.TxNum})[0]
}
}
reposted, err := db.GetRepostedCount(claimHash)
if err != nil {
return nil, err
}
isControlling := bytes.Equal(controllingClaim.ClaimHash, claimHash)
return &ResolveResult{
Name: name,
NormalizedName: normalizedName,
ClaimHash: claimHash,
TxNum: txNum,
Position: position,
TxHash: txHash,
Height: height,
Amount: claimAmount,
ShortUrl: shortUrl,
IsControlling: isControlling,
CanonicalUrl: canonicalUrl,
CreationHeight: createdHeight,
ActivationHeight: activationHeight,
ExpirationHeight: expirationHeight,
EffectiveAmount: effectiveAmount,
SupportAmount: supportAmount,
Reposted: reposted,
LastTakeoverHeight: lastTakeoverHeight,
ClaimsInChannel: claimsInChannel,
ChannelHash: channelHash,
RepostedClaimHash: repostedClaimHash,
SignatureValid: signatureValid,
RepostTxHash: repostTxHash,
RepostTxPostition: repostTxPostition,
RepostHeight: repostHeight,
ChannelTxHash: channelTxHash,
ChannelTxPostition: channelTxPostition,
ChannelHeight: channelHeight,
}, nil
}
func (db *ReadOnlyDBColumnFamily) ResolveParsedUrl(parsed *PathSegment) (*ResolveResult, error) {
normalizedName := internal.NormalizeName(parsed.name)
if (parsed.amountOrder == -1 && parsed.claimId == "") || parsed.amountOrder == 1 {
log.Warn("Resolving claim by name")
ch := db.ControllingClaimIter()
for kv := range ch {
key := kv.Key.(*prefixes.ClaimTakeoverKey)
val := kv.Value.(*prefixes.ClaimTakeoverValue)
log.Tracef("ClaimTakeoverKey: %#v", key)
log.Tracef("ClaimTakeoverValue: %#v", val)
}
controlling, err := db.GetControllingClaim(normalizedName)
log.Warnf("controlling: %#v", controlling)
log.Warnf("err: %#v", err)
if err != nil {
return nil, err
}
if controlling == nil {
return nil, nil
}
return db.FsGetClaimByHash(controlling.ClaimHash)
}
var amountOrder int = int(math.Max(float64(parsed.amountOrder), 1))
log.Println("amountOrder:", amountOrder)
// Resolve by claimId
if parsed.claimId != "" {
if len(parsed.claimId) == 40 {
claimHash, err := hex.DecodeString(parsed.claimId)
if err != nil {
return nil, err
}
// Maybe don't use caching version, when I actually implement the cache
claimTxo, err := db.GetCachedClaimTxo(claimHash, true)
if err != nil {
return nil, err
}
if claimTxo == nil || claimTxo.NormalizedName() != normalizedName {
return nil, nil
}
activation, err := db.GetActivation(claimTxo.TxNum, claimTxo.Position)
if err != nil {
return nil, err
}
log.Warn("claimTxo.ChannelSignatureIsValid:", claimTxo.ChannelSignatureIsValid)
return PrepareResolveResult(
db,
claimTxo.TxNum,
claimTxo.Position,
claimHash,
claimTxo.Name,
claimTxo.RootTxNum,
claimTxo.RootPosition,
activation,
claimTxo.ChannelSignatureIsValid,
)
}
log.Println("nomalizedName:", normalizedName)
log.Println("claimId:", parsed.claimId)
// max short id length
var j int = 10
if len(parsed.claimId) < j {
j = len(parsed.claimId)
}
ch := db.ClaimShortIdIter(normalizedName, parsed.claimId[:j])
row := <-ch
if row == nil {
return nil, nil
}
key := row.Key.(*prefixes.ClaimShortIDKey)
claimTxo := row.Value.(*prefixes.ClaimShortIDValue)
fullClaimHash, err := db.GetCachedClaimHash(claimTxo.TxNum, claimTxo.Position)
if err != nil {
return nil, err
}
c, err := db.GetCachedClaimTxo(fullClaimHash.ClaimHash, true)
if err != nil {
return nil, err
}
nonNormalizedName := c.Name
signatureIsValid := c.ChannelSignatureIsValid
activation, err := db.GetActivation(claimTxo.TxNum, claimTxo.Position)
if err != nil {
return nil, err
}
log.Warn("signatureIsValid:", signatureIsValid)
return PrepareResolveResult(
db,
claimTxo.TxNum,
claimTxo.Position,
fullClaimHash.ClaimHash,
nonNormalizedName,
key.RootTxNum,
key.RootPosition,
activation,
signatureIsValid,
)
}
// Resolve by amount ordering
log.Warn("resolving by amount ordering")
ch := db.BidOrderNameIter(normalizedName)
var i = 0
for kv := range ch {
if i+1 < amountOrder {
i++
continue
}
key := kv.Key.(*prefixes.BidOrderKey)
claimVal := kv.Value.(*prefixes.BidOrderValue)
claimTxo, err := db.GetCachedClaimTxo(claimVal.ClaimHash, true)
if err != nil {
return nil, err
}
activation, err := db.GetActivation(key.TxNum, key.Position)
if err != nil {
return nil, err
}
return PrepareResolveResult(
db,
key.TxNum,
key.Position,
claimVal.ClaimHash,
key.NormalizedName,
claimTxo.RootTxNum,
claimTxo.RootPosition,
activation,
claimTxo.ChannelSignatureIsValid,
)
}
return nil, nil
}
func (db *ReadOnlyDBColumnFamily) ResolveClaimInChannel(channelHash []byte, normalizedName string) (*ResolveResult, error) {
handle, err := db.EnsureHandle(prefixes.ChannelToClaim)
if err != nil {
return nil, err
}
key := prefixes.NewChannelToClaimKey(channelHash, normalizedName)
rawKeyPrefix := key.PartialPack(2)
options := NewIterateOptions().WithDB(db).WithCfHandle(handle).WithPrefix(rawKeyPrefix)
options = options.WithIncludeValue(true) //.WithIncludeStop(true)
ch := IterCF(db.DB, options)
// TODO: what's a good default size for this?
var candidates []*ResolveResult = make([]*ResolveResult, 0, 100)
var i = 0
for row := range ch {
key := row.Key.(*prefixes.ChannelToClaimKey)
stream := row.Value.(*prefixes.ChannelToClaimValue)
effectiveAmount, err := db.GetEffectiveAmount(stream.ClaimHash, false)
if err != nil {
return nil, err
}
if i == 0 || candidates[i-1].Amount == effectiveAmount {
candidates = append(
candidates,
&ResolveResult{
TxNum: key.TxNum,
Position: key.Position,
ClaimHash: stream.ClaimHash,
Amount: effectiveAmount,
ChannelHash: channelHash,
NormalizedName: normalizedName,
},
)
i++
} else {
break
}
}
log.Printf("candidates: %#v\n", candidates)
if len(candidates) == 0 {
return nil, nil
} else {
// return list(sorted(candidates, key=lambda item: item[1]))[0]
sort.Slice(candidates, func(i, j int) bool {
return candidates[i].Amount < candidates[j].Amount
})
return candidates[0], nil
}
}
func (db *ReadOnlyDBColumnFamily) Resolve(url string) *ExpandedResolveResult {
var res = NewExpandedResolveResult()
var channel *PathSegment = nil
var stream *PathSegment = nil
parsed, err := lbryurl.Parse(url, false)
log.Warnf("parsed: %#v", parsed)
if err != nil {
log.Warn("lbryurl.Parse:", err)
res.Stream = &optionalResolveResultOrError{
err: &ResolveError{Error: err},
}
return res
}
// has stream in channel
if strings.Compare(parsed.StreamName, "") != 0 && strings.Compare(parsed.ChannelName, "") != 0 {
channel = &PathSegment{
name: parsed.ClaimName,
claimId: parsed.ChannelClaimId,
amountOrder: parsed.PrimaryBidPosition,
}
stream = &PathSegment{
name: parsed.StreamName,
claimId: parsed.StreamClaimId,
amountOrder: parsed.SecondaryBidPosition,
}
} else if parsed.IsChannelUrl() {
channel = &PathSegment{
name: parsed.ClaimName,
claimId: parsed.ChannelClaimId,
amountOrder: parsed.PrimaryBidPosition,
}
} else if strings.Compare(parsed.StreamName, "") != 0 {
stream = &PathSegment{
name: parsed.StreamName,
claimId: parsed.StreamClaimId,
amountOrder: parsed.PrimaryBidPosition,
}
}
log.Printf("channel: %#v\n", channel)
log.Printf("stream: %#v\n", stream)
var resolvedChannel *ResolveResult = nil
var resolvedStream *ResolveResult = nil
if channel != nil {
resolvedChannel, err = db.ResolveParsedUrl(channel)
if err != nil {
res.Channel = &optionalResolveResultOrError{
err: &ResolveError{Error: err},
}
return res
} else if resolvedChannel == nil {
res.Channel = &optionalResolveResultOrError{
err: &ResolveError{
Error: fmt.Errorf("Could not find claim at \"%s\".", url),
ErrorType: uint8(pb.Error_NOT_FOUND),
},
}
return res
}
}
if resolvedChannel != nil {
log.Printf("resolvedChannel: %#v\n", resolvedChannel)
log.Printf("resolvedChannel.TxHash: %s\n", hex.EncodeToString(resolvedChannel.TxHash))
log.Printf("resolvedChannel.ClaimHash: %s\n", hex.EncodeToString(resolvedChannel.ClaimHash))
log.Printf("resolvedChannel.ChannelHash: %s\n", hex.EncodeToString(resolvedChannel.ChannelHash))
}
if stream != nil {
if resolvedChannel != nil {
streamClaim, err := db.ResolveClaimInChannel(resolvedChannel.ClaimHash, stream.Normalized())
log.Printf("streamClaim %#v\n", streamClaim)
if streamClaim != nil {
log.Printf("streamClaim.ClaimHash: %s\n", hex.EncodeToString(streamClaim.ClaimHash))
log.Printf("streamClaim.ChannelHash: %s\n", hex.EncodeToString(streamClaim.ChannelHash))
}
// TODO: Confirm error case
if err != nil {
res.Stream = &optionalResolveResultOrError{
err: &ResolveError{Error: err},
}
return res
}
if streamClaim != nil {
resolvedStream, err = db.FsGetClaimByHash(streamClaim.ClaimHash)
// TODO: Confirm error case
if err != nil {
res.Stream = &optionalResolveResultOrError{
err: &ResolveError{Error: err},
}
return res
}
}
} else {
resolvedStream, err = db.ResolveParsedUrl(stream)
// TODO: Confirm error case
if err != nil {
res.Stream = &optionalResolveResultOrError{
err: &ResolveError{Error: err},
}
return res
}
if channel == nil && resolvedChannel == nil && resolvedStream != nil && len(resolvedStream.ChannelHash) > 0 {
resolvedChannel, err = db.FsGetClaimByHash(resolvedStream.ChannelHash)
// TODO: Confirm error case
if err != nil {
res.Channel = &optionalResolveResultOrError{
err: &ResolveError{Error: err},
}
return res
}
}
}
if resolvedStream == nil {
res.Stream = &optionalResolveResultOrError{
err: &ResolveError{
Error: fmt.Errorf("Could not find claim at \"%s\".", url),
ErrorType: uint8(pb.Error_NOT_FOUND),
},
}
return res
}
}
// Getting blockers and filters
var repost *ResolveResult = nil
var repostedChannel *ResolveResult = nil
if resolvedChannel != nil && resolvedStream != nil {
log.Printf("about to get blockers and filters: %#v, %#v\n", resolvedChannel, resolvedStream)
}
if resolvedStream != nil || resolvedChannel != nil {
var claim *ResolveResult = nil
var claimHash []byte = nil
var respostedClaimHash []byte = nil
var blockerHash []byte = nil
if resolvedStream != nil {
claim = resolvedStream
claimHash = resolvedStream.ClaimHash
respostedClaimHash = resolvedStream.RepostedClaimHash
} else {
claim = resolvedChannel
claimHash = resolvedChannel.ClaimHash
}
blockerHash, _, err = db.GetBlockerHash(claimHash, respostedClaimHash, claim.ChannelHash)
log.Printf("blockerHash: %s\n", hex.EncodeToString(blockerHash))
if err != nil {
res.Channel = &optionalResolveResultOrError{
err: &ResolveError{Error: err},
}
return res
}
if blockerHash != nil {
reasonRow, err := db.FsGetClaimByHash(blockerHash)
if err != nil {
res.Channel = &optionalResolveResultOrError{
err: &ResolveError{Error: err},
}
return res
}
res.Channel = &optionalResolveResultOrError{
err: &ResolveError{Error: fmt.Errorf("%s, %v, %v", url, blockerHash, reasonRow)},
}
return res
}
if claim.RepostedClaimHash != nil {
repost, err = db.FsGetClaimByHash(claim.RepostedClaimHash)
if err != nil {
res.Channel = &optionalResolveResultOrError{
err: &ResolveError{Error: err},
}
return res
}
if repost != nil && repost.ChannelHash != nil && repost.SignatureValid {
repostedChannel, err = db.FsGetClaimByHash(repost.ChannelHash)
if err != nil {
res.Channel = &optionalResolveResultOrError{
err: &ResolveError{Error: err},
}
return res
}
}
}
}
res.Channel = &optionalResolveResultOrError{
res: resolvedChannel,
}
res.Stream = &optionalResolveResultOrError{
res: resolvedStream,
}
res.Repost = &optionalResolveResultOrError{
res: repost,
}
res.RepostedChannel = &optionalResolveResultOrError{
res: repostedChannel,
}
log.Warnf("leaving Resolve, parsed: %#v\n", parsed)
log.Warnf("leaving Resolve, res: %s\n", res)
return res
}

880
db/db_test.go Normal file
View file

@ -0,0 +1,880 @@
package db_test
import (
"bytes"
"encoding/csv"
"encoding/hex"
"os"
"strings"
"testing"
dbpkg "github.com/lbryio/herald.go/db"
"github.com/lbryio/herald.go/db/prefixes"
"github.com/lbryio/herald.go/internal"
"github.com/lbryio/lbry.go/v3/extras/stop"
"github.com/linxGnu/grocksdb"
log "github.com/sirupsen/logrus"
)
////////////////////////////////////////////////////////////////////////////////
// Utility functions for testing
////////////////////////////////////////////////////////////////////////////////
// OpenAndFillTmpDBColumnFamlies opens a db and fills it with data from a csv file using the given column family names
func OpenAndFillTmpDBColumnFamlies(filePath string) (*dbpkg.ReadOnlyDBColumnFamily, [][]string, error) {
log.Println(filePath)
file, err := os.Open(filePath)
if err != nil {
log.Println(err)
}
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
return nil, nil, err
}
wOpts := grocksdb.NewDefaultWriteOptions()
opts := grocksdb.NewDefaultOptions()
opts.SetCreateIfMissing(true)
db, err := grocksdb.OpenDb(opts, "tmp")
if err != nil {
return nil, nil, err
}
var handleMap map[string]*grocksdb.ColumnFamilyHandle = make(map[string]*grocksdb.ColumnFamilyHandle)
// Make sure we always create the TxCounts column family
var cfNameRunes string = records[0][0]
txCountPrefix := string(prefixes.TxCount)
if !strings.Contains(cfNameRunes, txCountPrefix) {
cfNameRunes = cfNameRunes + txCountPrefix
}
for _, cfNameRune := range cfNameRunes {
cfName := string(cfNameRune)
log.Println(cfName)
handle, err := db.CreateColumnFamily(opts, cfName)
if err != nil {
return nil, nil, err
}
handleMap[cfName] = handle
}
toDefer := func() {
db.Close()
err = os.RemoveAll("./tmp")
if err != nil {
log.Println(err)
}
}
for _, record := range records[1:] {
cf := record[0]
if err != nil {
return nil, nil, err
}
handle := handleMap[string(cf)]
key, err := hex.DecodeString(record[1])
if err != nil {
return nil, nil, err
}
val, err := hex.DecodeString(record[2])
if err != nil {
return nil, nil, err
}
db.PutCF(wOpts, handle, key, val)
}
myDB := &dbpkg.ReadOnlyDBColumnFamily{
DB: db,
Handles: handleMap,
Opts: grocksdb.NewDefaultReadOptions(),
BlockedStreams: make(map[string][]byte),
BlockedChannels: make(map[string][]byte),
FilteredStreams: make(map[string][]byte),
FilteredChannels: make(map[string][]byte),
TxCounts: nil,
LastState: nil,
Height: 0,
Headers: nil,
Grp: stop.New(),
Cleanup: toDefer,
}
// err = dbpkg.ReadDBState(myDB) //TODO: Figure out right place for this
// if err != nil {
// return nil, nil, nil, err
// }
err = myDB.InitTxCounts()
if err != nil {
return nil, nil, err
}
// err = dbpkg.InitHeaders(myDB)
// if err != nil {
// return nil, nil, nil, err
// }
return myDB, records, nil
}
// OpenAndFillTmpDBCF opens a db and fills it with data from a csv file
// using the given column family handle. Old version, should probably remove.
func OpenAndFillTmpDBCF(filePath string) (*grocksdb.DB, [][]string, func(), *grocksdb.ColumnFamilyHandle, error) {
log.Println(filePath)
file, err := os.Open(filePath)
if err != nil {
log.Println(err)
}
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
return nil, nil, nil, nil, err
}
wOpts := grocksdb.NewDefaultWriteOptions()
opts := grocksdb.NewDefaultOptions()
opts.SetCreateIfMissing(true)
db, err := grocksdb.OpenDb(opts, "tmp")
if err != nil {
return nil, nil, nil, nil, err
}
handle, err := db.CreateColumnFamily(opts, records[0][0])
if err != nil {
return nil, nil, nil, nil, err
}
toDefer := func() {
db.Close()
err = os.RemoveAll("./tmp")
if err != nil {
log.Println(err)
}
}
for _, record := range records[1:] {
key, err := hex.DecodeString(record[0])
if err != nil {
return nil, nil, nil, nil, err
}
val, err := hex.DecodeString(record[1])
if err != nil {
return nil, nil, nil, nil, err
}
db.PutCF(wOpts, handle, key, val)
}
return db, records, toDefer, handle, nil
}
// OpenAndFillTmpDB opens a db and fills it with data from a csv file.
// Old funciont, should probably remove.
func OpenAndFillTmpDB(filePath string) (*grocksdb.DB, [][]string, func(), error) {
log.Println(filePath)
file, err := os.Open(filePath)
if err != nil {
log.Println(err)
}
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
return nil, nil, nil, err
}
wOpts := grocksdb.NewDefaultWriteOptions()
opts := grocksdb.NewDefaultOptions()
opts.SetCreateIfMissing(true)
db, err := grocksdb.OpenDb(opts, "tmp")
if err != nil {
return nil, nil, nil, err
}
toDefer := func() {
db.Close()
err = os.RemoveAll("./tmp")
if err != nil {
log.Println(err)
}
}
for _, record := range records {
key, err := hex.DecodeString(record[0])
if err != nil {
return nil, nil, nil, err
}
val, err := hex.DecodeString(record[1])
if err != nil {
return nil, nil, nil, err
}
db.Put(wOpts, key, val)
}
return db, records, toDefer, nil
}
// CatCSV Reads a csv version of the db and prints it to stdout,
// while decoding types.
func CatCSV(filePath string) {
log.Println(filePath)
file, err := os.Open(filePath)
if err != nil {
log.Println(err)
}
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
log.Println(err)
return
}
for _, record := range records[1:] {
log.Println(record[1])
keyRaw, err := hex.DecodeString(record[1])
key, _ := prefixes.UnpackGenericKey(keyRaw)
log.Println(key)
if err != nil {
log.Println(err)
return
}
valRaw, err := hex.DecodeString(record[2])
// val := prefixes.ClaimTakeoverValueUnpack(valRaw)
val, _ := prefixes.UnpackGenericValue(keyRaw, valRaw)
log.Println(val)
if err != nil {
log.Println(err)
return
}
}
}
func TestCatFullDB(t *testing.T) {
t.Skip("Skipping full db test")
grp := stop.New()
// url := "lbry://@lothrop#2/lothrop-livestream-games-and-code#c"
// "lbry://@lbry", "lbry://@lbry#3", "lbry://@lbry3f", "lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a", "lbry://@lbry:1", "lbry://@lbry$1"
// url := "lbry://@Styxhexenhammer666#2/legacy-media-baron-les-moonves-(cbs#9"
// url := "lbry://@lbry"
// url := "lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a"
dbPath := "/mnt/sda1/wallet_server/_data/lbry-rocksdb/"
// dbPath := "/mnt/d/data/snapshot_1072108/lbry-rocksdb/"
secondaryPath := "asdf"
db, err := dbpkg.GetProdDB(dbPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
ch := db.ClaimShortIdIter("@lbry", "")
for row := range ch {
key := row.Key.(*prefixes.ClaimShortIDKey)
val := row.Value.(*prefixes.ClaimShortIDValue)
log.Printf("%#v, %#v\n", key, val)
}
}
////////////////////////////////////////////////////////////////////////////////
// End utility functions
////////////////////////////////////////////////////////////////////////////////
// TestOpenFullDB Tests running a resolve on a full db.
func TestOpenFullDB(t *testing.T) {
t.Skip("Skipping full db test")
grp := stop.New()
// url := "lbry://@lothrop#2/lothrop-livestream-games-and-code#c"
// "lbry://@lbry", "lbry://@lbry#3", "lbry://@lbry3f", "lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a", "lbry://@lbry:1", "lbry://@lbry$1"
// url := "lbry://@Styxhexenhammer666#2/legacy-media-baron-les-moonves-(cbs#9"
// url := "lbry://@lbry"
// url := "lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a"
// url := "lbry://@lbry$1"
url := "https://lbry.tv/@lothrop:2/lothrop-livestream-games-and-code:c"
dbPath := "/mnt/sda1/wallet_server/_data/lbry-rocksdb/"
// dbPath := "/mnt/d/data/snapshot_1072108/lbry-rocksdb/"
secondaryPath := "asdf"
db, err := dbpkg.GetProdDB(dbPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
expandedResolveResult := db.Resolve(url)
log.Printf("expandedResolveResult: %#v\n", expandedResolveResult)
log.Printf("expandedResolveResult: %s\n", expandedResolveResult)
}
// TODO: Finish the constructed data set for the stream part of this resolve.
func TestResolve(t *testing.T) {
url := "lbry://@Styxhexenhammer666#2/legacy-media-baron-les-moonves-(cbs#9"
filePath := "../testdata/FULL_resolve.csv"
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
expandedResolveResult := db.Resolve(url)
log.Printf("%#v\n", expandedResolveResult)
if expandedResolveResult != nil && expandedResolveResult.Channel != nil {
log.Println(expandedResolveResult.Channel.GetError())
}
if expandedResolveResult != nil && expandedResolveResult.Stream != nil {
log.Println(expandedResolveResult.Stream.GetError())
}
}
func TestGetDBState(t *testing.T) {
filePath := "../testdata/s_resolve.csv"
want := uint32(1072108)
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
state, err := db.GetDBState()
if err != nil {
t.Error(err)
}
log.Printf("state: %#v\n", state)
if state.Height != want {
t.Errorf("Expected %d, got %d", want, state.Height)
}
}
func TestGetRepostedClaim(t *testing.T) {
t.Skip("skipping obsolete? test of prefix W (Reposted)")
channelHash, _ := hex.DecodeString("2556ed1cab9d17f2a9392030a9ad7f5d138f11bd")
want := 5
// Should be non-existent
channelHash2, _ := hex.DecodeString("2556ed1cab9d17f2a9392030a9ad7f5d138f11bf")
filePath := "../testdata/W_resolve.csv"
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
count, err := db.GetRepostedCount(channelHash)
if err != nil {
t.Error(err)
}
log.Println(count)
if count != want {
t.Errorf("Expected %d, got %d", want, count)
}
count2, err := db.GetRepostedCount(channelHash2)
if err != nil {
t.Error(err)
}
if count2 != 0 {
t.Errorf("Expected 0, got %d", count2)
}
}
func TestGetRepostedCount(t *testing.T) {
channelHash, _ := hex.DecodeString("2556ed1cab9d17f2a9392030a9ad7f5d138f11bd")
want := 5
// Should be non-existent
channelHash2, _ := hex.DecodeString("2556ed1cab9d17f2a9392030a9ad7f5d138f11bf")
filePath := "../testdata/j_resolve.csv"
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
count, err := db.GetRepostedCount(channelHash)
if err != nil {
t.Error(err)
}
log.Println(count)
if count != want {
t.Errorf("Expected %d, got %d", want, count)
}
count2, err := db.GetRepostedCount(channelHash2)
if err != nil {
t.Error(err)
}
if count2 != 0 {
t.Errorf("Expected 0, got %d", count2)
}
}
func TestPrintRepost(t *testing.T) {
filePath := "../testdata/V_resolve.csv"
CatCSV(filePath)
}
func TestGetRepost(t *testing.T) {
channelHash, _ := hex.DecodeString("2556ed1cab9d17f2a9392030a9ad7f5d138f11bd")
channelHash2, _ := hex.DecodeString("000009ca6e0caaaef16872b4bd4f6f1b8c2363e2")
filePath := "../testdata/V_resolve.csv"
// want := uint32(3670)
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
res, err := db.GetRepost(channelHash)
if err != nil {
t.Error(err)
}
if !bytes.Equal(res, []byte{}) {
t.Errorf("Expected empty, got %#v", res)
}
res2, err := db.GetRepost(channelHash2)
if err != nil {
t.Error(err)
}
if bytes.Equal(res2, []byte{}) {
t.Errorf("Expected non empty, got %#v", res2)
}
}
func TestPrintChannelCount(t *testing.T) {
filePath := "../testdata/Z_resolve.csv"
CatCSV(filePath)
}
func TestGetClaimsInChannelCount(t *testing.T) {
channelHash, _ := hex.DecodeString("2556ed1cab9d17f2a9392030a9ad7f5d138f11bd")
filePath := "../testdata/Z_resolve.csv"
want := uint32(3670)
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
count, err := db.GetClaimsInChannelCount(channelHash)
if err != nil {
t.Error(err)
}
if count != want {
t.Errorf("Expected %d, got %d", want, count)
}
}
func TestPrintClaimShortId(t *testing.T) {
filePath := "../testdata/F_resolve.csv"
CatCSV(filePath)
}
// TestGetShortClaimIdUrl tests resolving a claim to a short url.
func TestGetShortClaimIdUrl(t *testing.T) {
name := "@Styxhexenhammer666"
normalName := internal.NormalizeName(name)
claimHash, _ := hex.DecodeString("2556ed1cab9d17f2a9392030a9ad7f5d138f11bd")
// claimHash := []byte{}
var rootTxNum uint32 = 0x61ec7c
var position uint16 = 0
filePath := "../testdata/F_resolve.csv"
log.Println(filePath)
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
shortUrl, err := db.GetShortClaimIdUrl(name, normalName, claimHash, rootTxNum, position)
if err != nil {
t.Error(err)
}
log.Println(shortUrl)
}
// TestClaimShortIdIter Tests the function to get an iterator of ClaimShortIds
// with a noramlized name and a partial claim id.
func TestClaimShortIdIter(t *testing.T) {
filePath := "../testdata/F_cat.csv"
normalName := "cat"
claimId := "0"
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
ch := db.ClaimShortIdIter(normalName, claimId)
for row := range ch {
key := row.Key.(*prefixes.ClaimShortIDKey)
log.Println(key)
if key.NormalizedName != normalName {
t.Errorf("Expected %s, got %s", normalName, key.NormalizedName)
}
}
}
// TestPrintTXOToCLaim Utility function to cat the TXOToClaim csv.
func TestPrintTXOToClaim(t *testing.T) {
filePath := "../testdata/G_2.csv"
CatCSV(filePath)
}
// TestGetTXOToClaim Tests getting a claim hash from the db given
// a txNum and position.
func TestGetTXOToClaim(t *testing.T) {
//&{[71] 1456296 0}
var txNum uint32 = 1456296
var position uint16 = 0
filePath := "../testdata/G_2.csv"
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
val, err := db.GetCachedClaimHash(txNum, position)
if err != nil {
t.Error(err)
} else if val.Name != "one" {
t.Error(err)
}
}
func TestGetClaimToChannel(t *testing.T) {
streamHashStr := "9a0ed686ecdad9b6cb965c4d6681c02f0bbc66a6"
claimHashStr := "2556ed1cab9d17f2a9392030a9ad7f5d138f11bd"
claimHash, _ := hex.DecodeString(claimHashStr)
streamHash, _ := hex.DecodeString(streamHashStr)
txNum := uint32(0x6284e3)
position := uint16(0x0)
streamTxNum := uint32(0x369e2b2)
streamPosition := uint16(0x0)
var val []byte = nil
filePath := "../testdata/I_resolve.csv"
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
val, err = db.GetChannelForClaim(claimHash, txNum, position)
if err != nil {
t.Error(err)
}
if val != nil {
t.Errorf("Expected nil, got %s", hex.EncodeToString(val))
}
val, err = db.GetChannelForClaim(streamHash, streamTxNum, streamPosition)
if err != nil {
t.Error(err)
}
valStr := hex.EncodeToString(val)
if valStr != claimHashStr {
t.Errorf("Expected %s, got %s", claimHashStr, valStr)
}
}
func TestGetEffectiveAmountSupportOnly(t *testing.T) {
filePath := "../testdata/Si_resolve.csv"
want := uint64(20000006)
claimHashStr := "00000324e40fcb63a0b517a3660645e9bd99244a"
claimHash, _ := hex.DecodeString(claimHashStr)
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
db.Height = 999999999
amount, err := db.GetEffectiveAmount(claimHash, true)
if err != nil {
t.Error(err)
}
if amount != want {
t.Errorf("Expected %d, got %d", want, amount)
}
// Cross-check against iterator-based implementation.
iteratorAmount, err := db.GetActiveAmount(claimHash, prefixes.ActivatedSupportTXOType, db.Height)
if err != nil {
t.Error(err)
}
if iteratorAmount != want {
t.Errorf("Expected %d, got %d", want, iteratorAmount)
}
}
func TestGetEffectiveAmount(t *testing.T) {
filePath := "../testdata/Si_resolve.csv"
want := uint64(21000006)
claimHashStr := "00000324e40fcb63a0b517a3660645e9bd99244a"
claimHash, _ := hex.DecodeString(claimHashStr)
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
db.Height = 999999999
amount, err := db.GetEffectiveAmount(claimHash, false)
if err != nil {
t.Error(err)
}
if amount != want {
t.Errorf("Expected %d, got %d", want, amount)
}
// Cross-check against iterator-based implementation.
iteratorAmount1, err := db.GetActiveAmount(claimHash, prefixes.ActivatedSupportTXOType, db.Height)
if err != nil {
t.Error(err)
}
iteratorAmount2, err := db.GetActiveAmount(claimHash, prefixes.ActivateClaimTXOType, db.Height)
if err != nil {
t.Error(err)
}
if iteratorAmount1+iteratorAmount2 != want {
t.Errorf("Expected %d, got %d (%d + %d)", want, iteratorAmount1+iteratorAmount2, iteratorAmount1, iteratorAmount2)
}
}
func TestGetSupportAmount(t *testing.T) {
want := uint64(8654754160700)
claimHashStr := "2556ed1cab9d17f2a9392030a9ad7f5d138f11bd"
claimHash, err := hex.DecodeString(claimHashStr)
if err != nil {
t.Error(err)
}
filePath := "../testdata/a_resolve.csv"
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
res, err := db.GetSupportAmount(claimHash)
if err != nil {
t.Error(err)
}
if res != want {
t.Errorf("Expected %d, got %d", want, res)
}
}
// TODO: verify where this hash comes from exactly.
func TestGetTxHash(t *testing.T) {
txNum := uint32(0x6284e3)
want := "54e14ff0c404c29b3d39ae4d249435f167d5cd4ce5a428ecb745b3df1c8e3dde"
filePath := "../testdata/X_resolve.csv"
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
resHash, err := db.GetTxHash(txNum)
if err != nil {
t.Error(err)
}
resStr := hex.EncodeToString(resHash)
if want != resStr {
t.Errorf("Expected %s, got %s", want, resStr)
}
}
func TestGetExpirationHeight(t *testing.T) {
var lastUpdated uint32 = 0
var expHeight uint32 = 0
expHeight = dbpkg.GetExpirationHeight(lastUpdated)
if lastUpdated+dbpkg.OriginalClaimExpirationTime != expHeight {
t.Errorf("Expected %d, got %d", lastUpdated+dbpkg.OriginalClaimExpirationTime, expHeight)
}
lastUpdated = dbpkg.ExtendedClaimExpirationForkHeight + 1
expHeight = dbpkg.GetExpirationHeight(lastUpdated)
if lastUpdated+dbpkg.ExtendedClaimExpirationTime != expHeight {
t.Errorf("Expected %d, got %d", lastUpdated+dbpkg.ExtendedClaimExpirationTime, expHeight)
}
lastUpdated = 0
expHeight = dbpkg.GetExpirationHeightFull(lastUpdated, true)
if lastUpdated+dbpkg.ExtendedClaimExpirationTime != expHeight {
t.Errorf("Expected %d, got %d", lastUpdated+dbpkg.ExtendedClaimExpirationTime, expHeight)
}
}
func TestGetActivation(t *testing.T) {
filePath := "../testdata/R_resolve.csv"
txNum := uint32(0x6284e3)
position := uint16(0x0)
want := uint32(0xa6b65)
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
}
activation, err := db.GetActivation(txNum, position)
if err != nil {
t.Error(err)
}
if activation != want {
t.Errorf("Expected %d, got %d", want, activation)
}
log.Printf("activation: %#v\n", activation)
}
// TestPrintClaimToTXO Utility function to cat the ClaimToTXO csv.
func TestPrintClaimToTXO(t *testing.T) {
filePath := "../testdata/E_resolve.csv"
CatCSV(filePath)
}
// TestGetClaimToTXO Tests getting a ClaimToTXO value from the db.
func TestGetClaimToTXO(t *testing.T) {
claimHashStr := "2556ed1cab9d17f2a9392030a9ad7f5d138f11bd"
want := uint32(0x6284e3)
claimHash, err := hex.DecodeString(claimHashStr)
if err != nil {
t.Error(err)
return
}
filePath := "../testdata/E_resolve.csv"
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
res, err := db.GetCachedClaimTxo(claimHash, true)
if err != nil {
t.Error(err)
return
}
if res.TxNum != want {
t.Errorf("Expected %d, got %d", want, res.TxNum)
}
log.Printf("res: %#v\n", res)
}
// TestPrintClaimTakeover Utility function to cat the ClaimTakeover csv.
func TestPrintClaimTakeover(t *testing.T) {
filePath := "../testdata/P_resolve.csv"
CatCSV(filePath)
}
// TestGetControlingClaim Tests getting a controlling claim value from the db
// based on a name.
func TestGetControllingClaim(t *testing.T) {
claimName := internal.NormalizeName("@Styxhexenhammer666")
claimHash := "2556ed1cab9d17f2a9392030a9ad7f5d138f11bd"
filePath := "../testdata/P_resolve.csv"
db, _, err := OpenAndFillTmpDBColumnFamlies(filePath)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
res, err := db.GetControllingClaim(claimName)
if err != nil {
t.Error(err)
}
got := hex.EncodeToString(res.ClaimHash)
if claimHash != got {
t.Errorf("Expected %s, got %s", claimHash, got)
}
log.Println(res)
}
// TestIter Tests the db iterator. Probably needs data format updated.
func TestIter(t *testing.T) {
filePath := "../testdata/W.csv"
db, records, toDefer, handle, err := OpenAndFillTmpDBCF(filePath)
if err != nil {
t.Error(err)
return
}
// skip the cf
records = records[1:]
defer toDefer()
// test prefix
options := dbpkg.NewIterateOptions().WithPrefix([]byte{prefixes.RepostedClaim}).WithIncludeValue(true)
options = options.WithCfHandle(handle)
// ch := dbpkg.Iter(db, options)
ch := dbpkg.IterCF(db, options)
var i = 0
for kv := range ch {
// log.Println(kv.Key)
gotKey := kv.Key.(*prefixes.RepostedKey).PackKey()
keyPartial3 := kv.Key.(*prefixes.RepostedKey).PartialPack(3)
keyPartial2 := kv.Key.(*prefixes.RepostedKey).PartialPack(2)
keyPartial1 := kv.Key.(*prefixes.RepostedKey).PartialPack(1)
// Check pack partial for sanity
if !bytes.HasPrefix(gotKey, keyPartial3) {
t.Errorf("%+v should be prefix of %+v\n", keyPartial3, gotKey)
}
if !bytes.HasPrefix(gotKey, keyPartial2) {
t.Errorf("%+v should be prefix of %+v\n", keyPartial2, gotKey)
}
if !bytes.HasPrefix(gotKey, keyPartial1) {
t.Errorf("%+v should be prefix of %+v\n", keyPartial1, gotKey)
}
got := kv.Value.(*prefixes.RepostedValue).PackValue()
wantKey, err := hex.DecodeString(records[i][0])
if err != nil {
log.Println(err)
}
want, err := hex.DecodeString(records[i][1])
if err != nil {
log.Println(err)
}
if !bytes.Equal(gotKey, wantKey) {
t.Errorf("gotKey: %+v, wantKey: %+v\n", got, want)
}
if !bytes.Equal(got, want) {
t.Errorf("got: %+v, want: %+v\n", got, want)
}
i++
}
// Test start / stop
start, err := hex.DecodeString(records[0][0])
if err != nil {
log.Println(err)
}
stop, err := hex.DecodeString(records[9][0])
if err != nil {
log.Println(err)
}
options2 := dbpkg.NewIterateOptions().WithStart(start).WithStop(stop).WithIncludeValue(true)
options2 = options2.WithCfHandle(handle)
ch2 := dbpkg.IterCF(db, options2)
i = 0
for kv := range ch2 {
got := kv.Value.(*prefixes.RepostedValue).PackValue()
want, err := hex.DecodeString(records[i][1])
if err != nil {
log.Println(err)
}
if !bytes.Equal(got, want) {
t.Errorf("got: %+v, want: %+v\n", got, want)
}
i++
}
}

213
db/iteroptions.go Normal file
View file

@ -0,0 +1,213 @@
package db
// iteroptions.go contains the implementation for iterators on rocksdb used by the hub
import (
"bytes"
"github.com/lbryio/herald.go/db/prefixes"
"github.com/lbryio/lbry.go/v3/extras/stop"
"github.com/linxGnu/grocksdb"
log "github.com/sirupsen/logrus"
)
type IterOptions struct {
FillCache bool
Prefix []byte
Start []byte //interface{}
Stop []byte //interface{}
IncludeStart bool
IncludeStop bool
IncludeKey bool
IncludeValue bool
RawKey bool
RawValue bool
Grp *stop.Group
// DB *ReadOnlyDBColumnFamily
CfHandle *grocksdb.ColumnFamilyHandle
It *grocksdb.Iterator
Serializer *prefixes.SerializationAPI
}
// NewIterateOptions creates a defualt options structure for a db iterator.
func NewIterateOptions() *IterOptions {
return &IterOptions{
FillCache: false,
Prefix: []byte{},
Start: nil,
Stop: nil,
IncludeStart: true,
IncludeStop: false,
IncludeKey: true,
IncludeValue: false,
RawKey: false,
RawValue: false,
Grp: nil,
// DB: nil,
CfHandle: nil,
It: nil,
Serializer: prefixes.ProductionAPI,
}
}
func (o *IterOptions) WithCfHandle(cfHandle *grocksdb.ColumnFamilyHandle) *IterOptions {
o.CfHandle = cfHandle
return o
}
func (o *IterOptions) WithFillCache(fillCache bool) *IterOptions {
o.FillCache = fillCache
return o
}
func (o *IterOptions) WithPrefix(prefix []byte) *IterOptions {
o.Prefix = prefix
return o
}
func (o *IterOptions) WithStart(start []byte) *IterOptions {
o.Start = start
return o
}
func (o *IterOptions) WithStop(stop []byte) *IterOptions {
o.Stop = stop
return o
}
func (o *IterOptions) WithIncludeStart(includeStart bool) *IterOptions {
o.IncludeStart = includeStart
return o
}
func (o *IterOptions) WithIncludeStop(includeStop bool) *IterOptions {
o.IncludeStop = includeStop
return o
}
func (o *IterOptions) WithIncludeKey(includeKey bool) *IterOptions {
o.IncludeKey = includeKey
return o
}
func (o *IterOptions) WithIncludeValue(includeValue bool) *IterOptions {
o.IncludeValue = includeValue
return o
}
func (o *IterOptions) WithRawKey(rawKey bool) *IterOptions {
o.RawKey = rawKey
return o
}
func (o *IterOptions) WithRawValue(rawValue bool) *IterOptions {
o.RawValue = rawValue
return o
}
func (o *IterOptions) WithDB(db *ReadOnlyDBColumnFamily) *IterOptions {
// o.Grp.AddNamed(1, iterKey)
o.Grp = stop.New(db.Grp)
o.Grp.Add(1)
return o
}
func (o *IterOptions) WithSerializer(serializer *prefixes.SerializationAPI) *IterOptions {
o.Serializer = serializer
return o
}
// ReadRow reads a row from the db, returns nil when no more rows are available.
func (opts *IterOptions) ReadRow(prevKey *[]byte) *prefixes.PrefixRowKV {
it := opts.It
if !it.Valid() {
log.Trace("ReadRow iterator not valid returning nil")
return nil
}
key := it.Key()
defer key.Free()
keyData := key.Data()
keyLen := len(keyData)
value := it.Value()
defer value.Free()
valueData := value.Data()
valueLen := len(valueData)
var outKey prefixes.BaseKey = nil
var outValue prefixes.BaseValue = nil
var rawOutKey []byte = nil
var rawOutValue []byte = nil
var err error = nil
log.Trace("keyData:", keyData)
log.Trace("valueData:", valueData)
// We need to check the current key if we're not including the stop
// key.
if !opts.IncludeStop && opts.StopIteration(keyData) {
log.Trace("ReadRow returning nil")
return nil
}
// We have to copy the key no matter what because we need to check
// it on the next iterations to see if we're going to stop.
newKeyData := make([]byte, keyLen)
copy(newKeyData, keyData)
if opts.IncludeKey && !opts.RawKey {
outKey, err = opts.Serializer.UnpackKey(newKeyData)
if err != nil {
log.Error(err)
}
} else if opts.IncludeKey {
rawOutKey = newKeyData
}
// Value could be quite large, so this setting could be important
// for performance in some cases.
if opts.IncludeValue {
newValueData := make([]byte, valueLen)
copy(newValueData, valueData)
if !opts.RawValue {
outValue, err = opts.Serializer.UnpackValue(newKeyData, newValueData)
if err != nil {
log.Error(err)
}
} else {
rawOutValue = newValueData
}
}
kv := &prefixes.PrefixRowKV{
Key: outKey,
Value: outValue,
RawKey: rawOutKey,
RawValue: rawOutValue,
}
*prevKey = newKeyData
return kv
}
// StopIteration returns true if we've hit the criteria to end iteration on this key
func (o *IterOptions) StopIteration(key []byte) bool {
if key == nil {
return false
}
maxLenStop := intMin(len(key), len(o.Stop))
maxLenStart := intMin(len(key), len(o.Start))
if o.Stop != nil &&
(bytes.HasPrefix(key, o.Stop) || bytes.Compare(o.Stop, key[:maxLenStop]) < 0) {
return true
} else if o.Start != nil &&
bytes.Compare(o.Start, key[:maxLenStart]) > 0 {
return true
} else if o.Prefix != nil && !bytes.HasPrefix(key, o.Prefix) {
return true
}
return false
}

230
db/prefixes/generic.go Normal file
View file

@ -0,0 +1,230 @@
package prefixes
import (
"encoding/binary"
"fmt"
"reflect"
"strings"
"github.com/go-restruct/restruct"
"github.com/lbryio/herald.go/internal"
"github.com/lbryio/lbcd/chaincfg/chainhash"
)
func init() {
restruct.EnableExprBeta()
}
// Type OnesComplementEffectiveAmount (uint64) has to be encoded specially
// to get the desired sort ordering.
// Implement the Sizer, Packer, Unpacker interface to handle it manually.
func (amt *OnesComplementEffectiveAmount) SizeOf() int {
return 8
}
func (amt *OnesComplementEffectiveAmount) Pack(buf []byte, order binary.ByteOrder) ([]byte, error) {
binary.BigEndian.PutUint64(buf, OnesCompTwiddle64-uint64(*amt))
return buf[8:], nil
}
func (amt *OnesComplementEffectiveAmount) Unpack(buf []byte, order binary.ByteOrder) ([]byte, error) {
*amt = OnesComplementEffectiveAmount(OnesCompTwiddle64 - binary.BigEndian.Uint64(buf))
return buf[8:], nil
}
// Struct BlockTxsValue has a field TxHashes of type []*chainhash.Hash.
// I haven't been able to figure out the right annotations to make
// restruct.Pack,Unpack work automagically.
// Implement the Sizer, Packer, Unpacker interface to handle it manually.
func (kv *BlockTxsValue) SizeOf() int {
return 32 * len(kv.TxHashes)
}
func (kv *BlockTxsValue) Pack(buf []byte, order binary.ByteOrder) ([]byte, error) {
offset := 0
for _, h := range kv.TxHashes {
offset += copy(buf[offset:], h[:])
}
return buf[offset:], nil
}
func (kv *BlockTxsValue) Unpack(buf []byte, order binary.ByteOrder) ([]byte, error) {
offset := 0
kv.TxHashes = make([]*chainhash.Hash, len(buf)/32)
for i := range kv.TxHashes {
kv.TxHashes[i] = (*chainhash.Hash)(buf[offset:32])
offset += 32
}
return buf[offset:], nil
}
// Struct BigEndianChainHash is a chainhash.Hash stored in external
// byte-order (opposite of other 32 byte chainhash.Hash values). In order
// to reuse chainhash.Hash we need to correct the byte-order.
// Currently this type is used for field Genesis of DBStateValue.
func (kv *BigEndianChainHash) SizeOf() int {
return chainhash.HashSize
}
func (kv *BigEndianChainHash) Pack(buf []byte, order binary.ByteOrder) ([]byte, error) {
offset := 0
hash := kv.CloneBytes()
// HACK: Instances of chainhash.Hash use the internal byte-order.
// Python scribe writes bytes of genesis hash in external byte-order.
internal.ReverseBytesInPlace(hash)
offset += copy(buf[offset:chainhash.HashSize], hash[:])
return buf[offset:], nil
}
func (kv *BigEndianChainHash) Unpack(buf []byte, order binary.ByteOrder) ([]byte, error) {
offset := 0
offset += copy(kv.Hash[:], buf[offset:32])
// HACK: Instances of chainhash.Hash use the internal byte-order.
// Python scribe writes bytes of genesis hash in external byte-order.
internal.ReverseBytesInPlace(kv.Hash[:])
return buf[offset:], nil
}
func genericNew(prefix []byte, key bool) (interface{}, error) {
t, ok := prefixRegistry[prefix[0]]
if !ok {
panic(fmt.Sprintf("not handled: prefix=%v", prefix))
}
if key {
return t.newKey(), nil
}
return t.newValue(), nil
}
func GenericPack(kv interface{}, fields int) ([]byte, error) {
// Locate the byte offset of the first excluded field.
offset := 0
if fields > 0 {
v := reflect.ValueOf(kv)
t := v.Type()
// Handle indirection to reach kind=Struct.
switch t.Kind() {
case reflect.Interface, reflect.Pointer:
v = v.Elem()
t = v.Type()
default:
panic(fmt.Sprintf("not handled: %v", t.Kind()))
}
count := 0
for _, sf := range reflect.VisibleFields(t) {
if !sf.IsExported() {
continue
}
if sf.Anonymous && strings.HasPrefix(sf.Name, "LengthEncoded") {
fields += 1 // Skip it but process NameLen and Name instead.
continue
}
if count > fields {
break
}
sz, err := restruct.SizeOf(v.FieldByIndex(sf.Index).Interface())
if err != nil {
panic(fmt.Sprintf("not handled: %v: %v", sf.Name, sf.Type.Kind()))
}
offset += sz
count += 1
}
}
// Pack the struct. No ability to partially pack.
buf, err := restruct.Pack(binary.BigEndian, kv)
if err != nil {
panic(fmt.Sprintf("not handled: %v", err))
}
// Return a prefix if some fields were excluded.
if fields > 0 {
return buf[:offset], nil
}
return buf, nil
}
func GenericUnpack(pfx []byte, key bool, buf []byte) (interface{}, error) {
kv, _ := genericNew(pfx, key)
err := restruct.Unpack(buf, binary.BigEndian, kv)
if err != nil {
panic(fmt.Sprintf("not handled: %v", err))
}
return kv, nil
}
func GetSerializationAPI(prefix []byte) *SerializationAPI {
t, ok := prefixRegistry[prefix[0]]
if !ok {
panic(fmt.Sprintf("not handled: prefix=%v", prefix))
}
if t.API != nil {
return t.API
}
return ProductionAPI
}
type SerializationAPI struct {
PackKey func(key BaseKey) ([]byte, error)
PackPartialKey func(key BaseKey, fields int) ([]byte, error)
PackValue func(value BaseValue) ([]byte, error)
UnpackKey func(key []byte) (BaseKey, error)
UnpackValue func(prefix []byte, value []byte) (BaseValue, error)
}
var ProductionAPI = &SerializationAPI{
PackKey: PackGenericKey,
PackPartialKey: PackPartialGenericKey,
PackValue: PackGenericValue,
UnpackKey: UnpackGenericKey,
UnpackValue: UnpackGenericValue,
}
var RegressionAPI_1 = &SerializationAPI{
PackKey: func(key BaseKey) ([]byte, error) {
return GenericPack(key, -1)
},
PackPartialKey: func(key BaseKey, fields int) ([]byte, error) {
return GenericPack(key, fields)
},
PackValue: func(value BaseValue) ([]byte, error) {
return GenericPack(value, -1)
},
UnpackKey: UnpackGenericKey,
UnpackValue: UnpackGenericValue,
}
var RegressionAPI_2 = &SerializationAPI{
PackKey: PackGenericKey,
PackPartialKey: PackPartialGenericKey,
PackValue: PackGenericValue,
UnpackKey: func(key []byte) (BaseKey, error) {
k, err := GenericUnpack(key, true, key)
return k.(BaseKey), err
},
UnpackValue: func(prefix []byte, value []byte) (BaseValue, error) {
k, err := GenericUnpack(prefix, false, value)
return k.(BaseValue), err
},
}
var RegressionAPI_3 = &SerializationAPI{
PackKey: func(key BaseKey) ([]byte, error) {
return GenericPack(key, -1)
},
PackPartialKey: func(key BaseKey, fields int) ([]byte, error) {
return GenericPack(key, fields)
},
PackValue: func(value BaseValue) ([]byte, error) {
return GenericPack(value, -1)
},
UnpackKey: func(key []byte) (BaseKey, error) {
k, err := GenericUnpack(key, true, key)
return k.(BaseKey), err
},
UnpackValue: func(prefix []byte, value []byte) (BaseValue, error) {
k, err := GenericUnpack(prefix, false, value)
return k.(BaseValue), err
},
}

4099
db/prefixes/prefixes.go Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,567 @@
package prefixes_test
import (
"bytes"
"crypto/rand"
"encoding/csv"
"encoding/hex"
"fmt"
"math"
"math/big"
"os"
"sort"
"testing"
dbpkg "github.com/lbryio/herald.go/db"
prefixes "github.com/lbryio/herald.go/db/prefixes"
"github.com/linxGnu/grocksdb"
log "github.com/sirupsen/logrus"
)
func TestPrefixRegistry(t *testing.T) {
for _, prefix := range prefixes.GetPrefixes() {
if prefixes.GetSerializationAPI(prefix) == nil {
t.Errorf("prefix %c not registered", prefix)
}
}
}
func testInit(filePath string) (*grocksdb.DB, [][]string, func(), *grocksdb.ColumnFamilyHandle) {
log.Println(filePath)
file, err := os.Open(filePath)
if err != nil {
log.Println(err)
}
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
log.Println(err)
}
columnFamily := records[0][0]
records = records[1:]
cleanupFiles := func() {
err = os.RemoveAll("./tmp")
if err != nil {
log.Println(err)
}
}
// wOpts := grocksdb.NewDefaultWriteOptions()
opts := grocksdb.NewDefaultOptions()
opts.SetCreateIfMissing(true)
db, err := grocksdb.OpenDb(opts, "tmp")
if err != nil {
log.Println(err)
// Garbage might have been left behind by a prior crash.
cleanupFiles()
db, err = grocksdb.OpenDb(opts, "tmp")
if err != nil {
log.Println(err)
}
}
handle, err := db.CreateColumnFamily(opts, columnFamily)
if err != nil {
log.Println(err)
}
toDefer := func() {
db.Close()
cleanupFiles()
}
return db, records, toDefer, handle
}
func testGeneric(filePath string, prefix byte, numPartials int) func(*testing.T) {
return func(t *testing.T) {
APIs := []*prefixes.SerializationAPI{
prefixes.GetSerializationAPI([]byte{prefix}),
// Verify combinations of production vs. "restruct" implementations of
// serialization API (e.g production Pack() with "restruct" Unpack()).
prefixes.RegressionAPI_1,
prefixes.RegressionAPI_2,
prefixes.RegressionAPI_3,
}
for _, api := range APIs {
opts := dbpkg.NewIterateOptions().WithPrefix([]byte{prefix}).WithSerializer(api).WithIncludeValue(true)
testGenericOptions(opts, filePath, prefix, numPartials)(t)
}
}
}
func testGenericOptions(options *dbpkg.IterOptions, filePath string, prefix byte, numPartials int) func(*testing.T) {
return func(t *testing.T) {
wOpts := grocksdb.NewDefaultWriteOptions()
db, records, toDefer, handle := testInit(filePath)
defer toDefer()
for _, record := range records {
key, err := hex.DecodeString(record[0])
if err != nil {
log.Println(err)
}
val, err := hex.DecodeString(record[1])
if err != nil {
log.Println(err)
}
// db.Put(wOpts, key, val)
db.PutCF(wOpts, handle, key, val)
}
// test prefix
options = options.WithCfHandle(handle)
ch := dbpkg.IterCF(db, options)
var i = 0
for kv := range ch {
// log.Println(kv.Key)
gotKey, err := options.Serializer.PackKey(kv.Key)
if err != nil {
log.Println(err)
}
if numPartials != kv.Key.NumFields() {
t.Errorf("key reports %v fields but %v expected", kv.Key.NumFields(), numPartials)
}
for j := 1; j <= numPartials; j++ {
keyPartial, _ := options.Serializer.PackPartialKey(kv.Key, j)
// Check pack partial for sanity
if j < numPartials {
if !bytes.HasPrefix(gotKey, keyPartial) || (len(keyPartial) >= len(gotKey)) {
t.Errorf("%+v should be prefix of %+v\n", keyPartial, gotKey)
}
} else {
if !bytes.Equal(gotKey, keyPartial) {
t.Errorf("%+v should be equal to %+v\n", keyPartial, gotKey)
}
}
}
got, err := options.Serializer.PackValue(kv.Value)
if err != nil {
log.Println(err)
}
wantKey, err := hex.DecodeString(records[i][0])
if err != nil {
log.Println(err)
}
want, err := hex.DecodeString(records[i][1])
if err != nil {
log.Println(err)
}
if !bytes.Equal(gotKey, wantKey) {
t.Errorf("gotKey: %+v, wantKey: %+v\n", gotKey, wantKey)
}
if !bytes.Equal(got, want) {
t.Errorf("got: %+v, want: %+v\n", got, want)
}
i++
}
// Test start / stop
start, err := hex.DecodeString(records[0][0])
if err != nil {
log.Println(err)
}
numRecords := i
// var numRecords = 9
// if prefix == prefixes.Undo || prefix == prefixes.DBState {
// numRecords = 1
// }
stop, err := hex.DecodeString(records[numRecords-1][0])
if err != nil {
log.Println(err)
}
options2 := dbpkg.NewIterateOptions().WithSerializer(options.Serializer).WithStart(start).WithStop(stop).WithIncludeValue(true)
options2 = options2.WithCfHandle(handle)
ch2 := dbpkg.IterCF(db, options2)
i = 0
for kv := range ch2 {
got, err := options2.Serializer.PackValue(kv.Value)
if err != nil {
log.Println(err)
}
want, err := hex.DecodeString(records[i][1])
if err != nil {
log.Println(err)
}
if !bytes.Equal(got, want) {
t.Errorf("got: %+v, want: %+v\n", got, want)
}
i++
}
}
}
func TestSupportAmount(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.SupportAmount)
testGeneric(filePath, prefixes.SupportAmount, 1)(t)
}
func TestChannelCount(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ChannelCount)
testGeneric(filePath, prefixes.ChannelCount, 1)(t)
}
func TestDBState(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.DBState)
testGeneric(filePath, prefixes.DBState, 0)(t)
}
func TestBlockTxs(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.BlockTXs)
testGeneric(filePath, prefixes.BlockTXs, 1)(t)
}
func TestTxCount(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.TxCount)
testGeneric(filePath, prefixes.TxCount, 1)(t)
}
func TestTxHash(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.TxHash)
testGeneric(filePath, prefixes.TxHash, 1)(t)
}
func TestTxNum(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.TxNum)
testGeneric(filePath, prefixes.TxNum, 1)(t)
}
func TestTx(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.Tx)
testGeneric(filePath, prefixes.Tx, 1)(t)
}
func TestHashXHistory(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.HashXHistory)
testGeneric(filePath, prefixes.HashXHistory, 2)(t)
}
func TestUndo(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.Undo)
testGeneric(filePath, prefixes.Undo, 1)(t)
}
func TestBlockHash(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.BlockHash)
testGeneric(filePath, prefixes.BlockHash, 1)(t)
}
func TestBlockHeader(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.Header)
testGeneric(filePath, prefixes.Header, 1)(t)
}
func TestClaimToTXO(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ClaimToTXO)
testGeneric(filePath, prefixes.ClaimToTXO, 1)(t)
}
func TestTXOToClaim(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.TXOToClaim)
testGeneric(filePath, prefixes.TXOToClaim, 2)(t)
}
func TestClaimShortID(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ClaimShortIdPrefix)
testGeneric(filePath, prefixes.ClaimShortIdPrefix, 4)(t)
}
func TestClaimToChannel(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ClaimToChannel)
testGeneric(filePath, prefixes.ClaimToChannel, 3)(t)
}
func TestChannelToClaim(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ChannelToClaim)
testGeneric(filePath, prefixes.ChannelToClaim, 4)(t)
}
func TestClaimToSupport(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ClaimToSupport)
testGeneric(filePath, prefixes.ClaimToSupport, 3)(t)
}
func TestSupportToClaim(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.SupportToClaim)
testGeneric(filePath, prefixes.SupportToClaim, 2)(t)
}
func TestClaimExpiration(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ClaimExpiration)
testGeneric(filePath, prefixes.ClaimExpiration, 3)(t)
}
func TestClaimTakeover(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ClaimTakeover)
testGeneric(filePath, prefixes.ClaimTakeover, 1)(t)
}
func TestPendingActivation(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.PendingActivation)
testGeneric(filePath, prefixes.PendingActivation, 4)(t)
}
func TestActivated(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ActivatedClaimAndSupport)
testGeneric(filePath, prefixes.ActivatedClaimAndSupport, 3)(t)
}
func TestActiveAmount(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ActiveAmount)
testGeneric(filePath, prefixes.ActiveAmount, 5)(t)
}
func TestBidOrder(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.BidOrder)
testGeneric(filePath, prefixes.BidOrder, 4)(t)
}
func TestRepost(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.Repost)
testGeneric(filePath, prefixes.Repost, 1)(t)
}
func TestRepostedClaim(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.RepostedClaim)
testGeneric(filePath, prefixes.RepostedClaim, 3)(t)
}
func TestRepostedCount(t *testing.T) {
prefix := byte(prefixes.RepostedCount)
filePath := fmt.Sprintf("../../testdata/%c.csv", prefix)
//synthesizeTestData([]byte{prefix}, filePath, []int{20}, []int{4}, [][3]int{})
key := &prefixes.RepostedCountKey{}
testGeneric(filePath, prefix, key.NumFields())(t)
}
func TestClaimDiff(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.ClaimDiff)
testGeneric(filePath, prefixes.ClaimDiff, 1)(t)
}
func TestUTXO(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.UTXO)
testGeneric(filePath, prefixes.UTXO, 3)(t)
}
func TestHashXUTXO(t *testing.T) {
filePath := fmt.Sprintf("../../testdata/%c.csv", prefixes.HashXUTXO)
testGeneric(filePath, prefixes.HashXUTXO, 3)(t)
}
func TestUTXOKey_String(t *testing.T) {
tests := []struct {
name string
prefix []byte
hashx []byte
txnum uint32
nout uint16
want string
}{
{
name: "Converts to string",
prefix: []byte("u"),
hashx: []byte("AAAAAAAAAA"),
txnum: 0,
nout: 0,
want: "*prefixes.UTXOKey(hashX=41414141414141414141, tx_num=0, nout=0)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
key := &prefixes.UTXOKey{
Prefix: tt.prefix,
HashX: tt.hashx,
TxNum: tt.txnum,
Nout: tt.nout,
}
got := fmt.Sprint(key)
log.Println(got)
if got != tt.want {
t.Errorf("got: %s, want: %s\n", got, tt.want)
}
})
}
}
func TestTrendingNotifications(t *testing.T) {
prefix := byte(prefixes.TrendingNotifications)
filePath := fmt.Sprintf("../../testdata/%c.csv", prefix)
//synthesizeTestData([]byte{prefix}, filePath, []int{4, 20}, []int{8, 8}, [][3]int{})
key := &prefixes.TrendingNotificationKey{}
testGeneric(filePath, prefix, key.NumFields())(t)
}
func TestMempoolTx(t *testing.T) {
prefix := byte(prefixes.MempoolTx)
filePath := fmt.Sprintf("../../testdata/%c.csv", prefix)
//synthesizeTestData([]byte{prefix}, filePath, []int{32}, []int{}, [][3]int{{20, 100, 1}})
key := &prefixes.MempoolTxKey{}
testGeneric(filePath, prefix, key.NumFields())(t)
}
func TestTouchedHashX(t *testing.T) {
prefix := byte(prefixes.TouchedHashX)
filePath := fmt.Sprintf("../../testdata/%c.csv", prefix)
//synthesizeTestData([]byte{prefix}, filePath, []int{4}, []int{}, [][3]int{{1, 5, 11}})
key := &prefixes.TouchedHashXKey{}
testGeneric(filePath, prefix, key.NumFields())(t)
}
func TestHashXStatus(t *testing.T) {
prefix := byte(prefixes.HashXStatus)
filePath := fmt.Sprintf("../../testdata/%c.csv", prefix)
//synthesizeTestData([]byte{prefix}, filePath, []int{20}, []int{32}, [][3]int{})
key := &prefixes.HashXStatusKey{}
testGeneric(filePath, prefix, key.NumFields())(t)
}
func TestHashXMempoolStatus(t *testing.T) {
prefix := byte(prefixes.HashXMempoolStatus)
filePath := fmt.Sprintf("../../testdata/%c.csv", prefix)
//synthesizeTestData([]byte{prefix}, filePath, []int{20}, []int{32}, [][3]int{})
key := &prefixes.HashXMempoolStatusKey{}
testGeneric(filePath, prefix, key.NumFields())(t)
}
func TestEffectiveAmount(t *testing.T) {
prefix := byte(prefixes.EffectiveAmount)
filePath := fmt.Sprintf("../../testdata/%c.csv", prefix)
//synthesizeTestData([]byte{prefix}, filePath, []int{20}, []int{8, 8}, [][3]int{})
key := &prefixes.EffectiveAmountKey{}
testGeneric(filePath, prefix, key.NumFields())(t)
}
func synthesizeTestData(prefix []byte, filePath string, keyFixed, valFixed []int, valVariable [][3]int) {
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
if err != nil {
panic(err)
}
defer file.Close()
records := make([][2][]byte, 0, 20)
for r := 0; r < 20; r++ {
key := make([]byte, 0, 1000)
key = append(key, prefix...)
val := make([]byte, 0, 1000)
// Handle fixed columns of key.
for _, width := range keyFixed {
v := make([]byte, width)
rand.Read(v)
key = append(key, v...)
}
// Handle fixed columns of value.
for _, width := range valFixed {
v := make([]byte, width)
rand.Read(v)
val = append(val, v...)
}
// Handle variable length array in value. Each element is "chunk" size.
for _, w := range valVariable {
low, high, chunk := w[0], w[1], w[2]
n, _ := rand.Int(rand.Reader, big.NewInt(int64(high-low)))
v := make([]byte, chunk*(low+int(n.Int64())))
rand.Read(v)
val = append(val, v...)
}
records = append(records, [2][]byte{key, val})
}
sort.Slice(records, func(i, j int) bool { return bytes.Compare(records[i][0], records[j][0]) == -1 })
wr := csv.NewWriter(file)
wr.Write([]string{string(prefix), ""}) // column headers
for _, rec := range records {
encoded := []string{hex.EncodeToString(rec[0]), hex.EncodeToString(rec[1])}
err := wr.Write(encoded)
if err != nil {
panic(err)
}
}
wr.Flush()
}
// Fuzz tests for various Key and Value types (EXPERIMENTAL)
func FuzzTouchedHashXKey(f *testing.F) {
kvs := []prefixes.TouchedHashXKey{
{
Prefix: []byte{prefixes.TouchedHashX},
Height: 0,
},
{
Prefix: []byte{prefixes.TouchedHashX},
Height: 1,
},
{
Prefix: []byte{prefixes.TouchedHashX},
Height: math.MaxUint32,
},
}
for _, kv := range kvs {
seed := make([]byte, 0, 200)
seed = append(seed, kv.PackKey()...)
f.Add(seed)
}
f.Fuzz(func(t *testing.T, in []byte) {
t.Logf("testing: %+v", in)
out := make([]byte, 0, 200)
var kv prefixes.TouchedHashXKey
kv.UnpackKey(in)
out = append(out, kv.PackKey()...)
if len(in) >= 5 {
if !bytes.HasPrefix(in, out) {
t.Fatalf("%v: not equal after round trip: %v", in, out)
}
}
})
}
func FuzzTouchedHashXValue(f *testing.F) {
kvs := []prefixes.TouchedHashXValue{
{
TouchedHashXs: [][]byte{},
},
{
TouchedHashXs: [][]byte{
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
},
},
{
TouchedHashXs: [][]byte{
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
},
},
{
TouchedHashXs: [][]byte{
{0xff, 0xff, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{0, 1, 0xff, 0xff, 4, 5, 6, 7, 8, 9, 10},
{0, 1, 2, 3, 0xff, 0xff, 6, 7, 8, 9, 10},
},
},
}
for _, kv := range kvs {
seed := make([]byte, 0, 200)
seed = append(seed, kv.PackValue()...)
f.Add(seed)
}
f.Fuzz(func(t *testing.T, in []byte) {
t.Logf("testing: %+v", in)
out := make([]byte, 0, 200)
var kv prefixes.TouchedHashXValue
kv.UnpackValue(in)
out = append(out, kv.PackValue()...)
if len(in) >= 5 {
if !bytes.HasPrefix(in, out) {
t.Fatalf("%v: not equal after round trip: %v", in, out)
}
}
})
}

102
db/stack/stack.go Normal file
View file

@ -0,0 +1,102 @@
package stack
// The db_stack package contains the implementation of a generic slice backed stack
// used for tracking various states in the hub, i.e. headers and txcounts
import (
"sync"
"github.com/lbryio/herald.go/internal"
"golang.org/x/exp/constraints"
)
type SliceBacked[T any] struct {
slice []T
len uint32
mut sync.RWMutex
}
func NewSliceBacked[T any](size int) *SliceBacked[T] {
return &SliceBacked[T]{
slice: make([]T, size),
len: 0,
mut: sync.RWMutex{},
}
}
func (s *SliceBacked[T]) Push(v T) {
s.mut.Lock()
defer s.mut.Unlock()
if s.len == uint32(len(s.slice)) {
s.slice = append(s.slice, v)
} else {
s.slice[s.len] = v
}
s.len++
}
func (s *SliceBacked[T]) Pop() T {
s.mut.Lock()
defer s.mut.Unlock()
if s.len == 0 {
var null T
return null
}
s.len--
return s.slice[s.len]
}
func (s *SliceBacked[T]) Get(i uint32) T {
s.mut.RLock()
defer s.mut.RUnlock()
if i >= s.len {
var null T
return null
}
return s.slice[i]
}
func (s *SliceBacked[T]) GetTip() T {
s.mut.RLock()
defer s.mut.RUnlock()
if s.len == 0 {
var null T
return null
}
return s.slice[s.len-1]
}
func (s *SliceBacked[T]) Len() uint32 {
s.mut.RLock()
defer s.mut.RUnlock()
return s.len
}
func (s *SliceBacked[T]) Cap() int {
s.mut.RLock()
defer s.mut.RUnlock()
return cap(s.slice)
}
func (s *SliceBacked[T]) GetSlice() []T {
// This is not thread safe so I won't bother with locking
return s.slice
}
func BisectRight[T constraints.Ordered](s *SliceBacked[T], searchKeys []T) []uint32 {
s.mut.RLock()
defer s.mut.RUnlock()
found := make([]uint32, len(searchKeys))
for i, k := range searchKeys {
found[i] = internal.BisectRight(s.slice[:s.Len()], k)
}
return found
}

123
db/stack/stack_test.go Normal file
View file

@ -0,0 +1,123 @@
package stack_test
import (
"testing"
"time"
"github.com/lbryio/herald.go/db/stack"
)
func TestPush(t *testing.T) {
var want uint32 = 3
stack := stack.NewSliceBacked[int](10)
stack.Push(0)
stack.Push(1)
stack.Push(2)
if got := stack.Len(); got != want {
t.Errorf("got %v, want %v", got, want)
}
}
func TestPushPop(t *testing.T) {
stack := stack.NewSliceBacked[int](10)
for i := 0; i < 5; i++ {
stack.Push(i)
}
for i := 0; i < 5; i++ {
wantLen := 5 - i
if got := stack.Len(); int(got) != wantLen {
t.Errorf("got %v, want %v", got, wantLen)
}
if got := stack.Pop(); got != 5-i-1 {
t.Errorf("got %v, want %v", got, 5-i-1)
}
wantLen -= 1
if got := stack.Len(); int(got) != wantLen {
t.Errorf("got %v, want %v", got, wantLen)
}
}
}
func doPushes(stack *stack.SliceBacked[int], numPushes int) {
for i := 0; i < numPushes; i++ {
stack.Push(i)
}
}
func doPops(stack *stack.SliceBacked[int], numPops int) {
for i := 0; i < numPops; i++ {
stack.Pop()
}
}
func TestMultiThreaded(t *testing.T) {
stack := stack.NewSliceBacked[int](100000)
go doPushes(stack, 100000)
go doPushes(stack, 100000)
go doPushes(stack, 100000)
time.Sleep(time.Second)
if stack.Len() != 300000 {
t.Errorf("got %v, want %v", stack.Len(), 300000)
}
go doPops(stack, 100000)
go doPops(stack, 100000)
go doPops(stack, 100000)
time.Sleep(time.Second)
if stack.Len() != 0 {
t.Errorf("got %v, want %v", stack.Len(), 0)
}
}
func TestGet(t *testing.T) {
stack := stack.NewSliceBacked[int](10)
for i := 0; i < 5; i++ {
stack.Push(i)
}
if got := stack.GetTip(); got != 4 {
t.Errorf("got %v, want %v", got, 4)
}
for i := 0; i < 5; i++ {
if got := stack.Get(uint32(i)); got != i {
t.Errorf("got %v, want %v", got, i)
}
}
if got := stack.Get(5); got != 0 {
t.Errorf("got %v, want %v", got, 0)
}
slice := stack.GetSlice()
if len(slice) != 10 {
t.Errorf("got %v, want %v", len(slice), 10)
}
}
func TestLenCap(t *testing.T) {
stack := stack.NewSliceBacked[int](10)
if got := stack.Len(); got != 0 {
t.Errorf("got %v, want %v", got, 0)
}
if got := stack.Cap(); got != 10 {
t.Errorf("got %v, want %v", got, 10)
}
}

10
docker/Dockerfile Normal file
View file

@ -0,0 +1,10 @@
FROM golang:alpine as stage1
EXPOSE 50051
RUN mkdir /app
WORKDIR /app
COPY . /app
RUN go build -o herald .
FROM alpine:latest
COPY --from=stage1 /app/herald /herald
ENTRYPOINT ["/herald", "serve"]

11
docker/Dockerfile.action Normal file
View file

@ -0,0 +1,11 @@
FROM jeffreypicard/hub-github-env:dev
COPY scripts/build_and_test.sh /build_and_test.sh
# COPY . /hub
# WORKDIR /hub
ENV CGO_LDFLAGS "-L/usr/local/lib -lrocksdb -lstdc++ -lm -lz -lsnappy -llz4 -lzstd"
ENV CGO_CFLAGS "-I/usr/local/include/rocksdb"
ENV LD_LIBRARY_PATH /usr/local/lib
ENTRYPOINT ["/build_and_test.sh"]

View file

@ -0,0 +1,13 @@
FROM jeffreypicard/hub-github-env:dev
COPY scripts/integration_tests.sh /integration_tests.sh
COPY scripts/cicd_integration_test_runner.sh /cicd_integration_test_runner.sh
COPY herald /herald
RUN apt install -y jq curl
ENV CGO_LDFLAGS "-L/usr/local/lib -lrocksdb -lstdc++ -lm -lz -lsnappy -llz4 -lzstd"
ENV CGO_CFLAGS "-I/usr/local/include/rocksdb"
ENV LD_LIBRARY_PATH /usr/local/lib
ENTRYPOINT ["/cicd_integration_test_runner.sh"]

25
docker/Dockerfile.github Normal file
View file

@ -0,0 +1,25 @@
FROM golang:1.18.5-bullseye
RUN apt-get update -y && \
apt-get upgrade -y && \
apt-get install -y dnsutils git libsnappy-dev liblz4-dev libzstd-dev zlib1g-dev \
autoconf automake libtool curl make g++
RUN cd /tmp && \
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.17.1/protobuf-all-3.17.1.tar.gz && \
tar xfzv protobuf-all-3.17.1.tar.gz && \
cd protobuf-3.17.1 && \
./autogen.sh && \
./configure && \
make && \
make install && \
ldconfig && \
rm -rf /tmp/proto*
RUN cd /tmp && \
git clone https://github.com/facebook/rocksdb.git && \
cd rocksdb && \
git checkout v6.29.5 && \
make static_lib && \
make install && \
rm -rf /tmp/rocksdb
CMD ["bash"]

46
go.mod
View file

@ -1,18 +1,50 @@
module github.com/lbryio/hub module github.com/lbryio/herald.go
go 1.16 go 1.18
// replace github.com/lbryio/lbry.go/v3 => /home/loki/dev/lbry/lbry.go
require ( require (
github.com/ReneKroon/ttlcache/v2 v2.8.1 github.com/ReneKroon/ttlcache/v2 v2.8.1
github.com/akamensky/argparse v1.2.2 github.com/akamensky/argparse v1.2.2
github.com/lbryio/lbry.go/v2 v2.7.2-0.20210625145058-2b155597bf57 github.com/go-restruct/restruct v1.2.0-alpha
github.com/gorilla/mux v1.7.3
github.com/gorilla/rpc v1.2.0
github.com/lbryio/lbcutil v1.0.202
github.com/lbryio/lbry.go/v3 v3.0.1-beta
github.com/linxGnu/grocksdb v1.6.42
github.com/olivere/elastic/v7 v7.0.24 github.com/olivere/elastic/v7 v7.0.24
github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_golang v1.11.0
github.com/prometheus/client_model v0.2.0 github.com/prometheus/client_model v0.2.0
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 // indirect github.com/sirupsen/logrus v1.8.1
golang.org/x/text v0.3.6 golang.org/x/exp v0.0.0-20220907003533-145caa8ea1d0
google.golang.org/genproto v0.0.0-20210524171403-669157292da3 // indirect golang.org/x/text v0.3.7
google.golang.org/grpc v1.38.0 google.golang.org/grpc v1.46.0
google.golang.org/protobuf v1.27.1 google.golang.org/protobuf v1.27.1
gopkg.in/karalabe/cookiejar.v1 v1.0.0-20141109175019-e1490cae028c gopkg.in/karalabe/cookiejar.v1 v1.0.0-20141109175019-e1490cae028c
) )
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/lbryio/lbcd v0.22.201-beta-rc4
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/common v0.26.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/stretchr/testify v1.7.0 // indirect
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

645
go.sum
View file

@ -1,9 +1,38 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ReneKroon/ttlcache/v2 v2.8.1 h1:0Exdyt5+vEsdRoFO1T7qDIYM3gq/ETbeYV+vjgcPxZk= github.com/ReneKroon/ttlcache/v2 v2.8.1 h1:0Exdyt5+vEsdRoFO1T7qDIYM3gq/ETbeYV+vjgcPxZk=
github.com/ReneKroon/ttlcache/v2 v2.8.1/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY= github.com/ReneKroon/ttlcache/v2 v2.8.1/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/akamensky/argparse v1.2.2 h1:P17T0ZjlUNJuWTPPJ2A5dM1wxarHgHqfYH+AZTo2xQA= github.com/akamensky/argparse v1.2.2 h1:P17T0ZjlUNJuWTPPJ2A5dM1wxarHgHqfYH+AZTo2xQA=
github.com/akamensky/argparse v1.2.2/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA= github.com/akamensky/argparse v1.2.2/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -11,60 +40,178 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.38.3/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.38.3/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac=
github.com/cockroachdb/errors v1.8.6/go.mod h1:hOm5fabihW+xEyY1kuypGwqT+Vt7rafg04ytBtIpeIQ=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
github.com/cockroachdb/pebble v0.0.0-20210525181856-e45797baeb78/go.mod h1:1XpB4cLQcF189RAcWi4gUc110zJgtOfT7SVNGY8sOe0=
github.com/cockroachdb/pebble v0.0.0-20211124004043-0dc90bc41e62/go.mod h1:buxOO9GBtOcq1DiXDpIPYrmxY020K2A8lOrwno5FetU=
github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/redact v1.1.1/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/fgprof v0.9.1/go.mod h1:7/HK6JFtFaARhIljgP2IV8rJLIoHDoOYoUphsnGvqxE=
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-errors/errors v1.1.1 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-ini/ini v1.48.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.48.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc=
github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
@ -76,6 +223,13 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -83,105 +237,318 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20200615235658-03e1cf38a040/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190915194858-d3ddacdb130f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190915194858-d3ddacdb130f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk=
github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA=
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lbryio/lbry.go/v2 v2.7.2-0.20210625145058-2b155597bf57 h1:Dzg3a7M9EWS48D0mbYInBjeqX/LB5qRuDgG29ktpmw8= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lbryio/lbry.go/v2 v2.7.2-0.20210625145058-2b155597bf57/go.mod h1:I1q8W9fwU+t0IWNiprPgE1SorWQwcO6ser0nzP3L5Pk= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lbryio/lbcd v0.22.100-beta/go.mod h1:u8SaFX4xdGMMR5xasBGfgApC8pvD4rnK2OujZnrq5gs=
github.com/lbryio/lbcd v0.22.100-beta-rc5/go.mod h1:9PbFSlHYX7WlnDQwcTxHVf1W35VAnRsattCSyKOO55g=
github.com/lbryio/lbcd v0.22.200-beta/go.mod h1:kNuzGWf808ipTGB0y0WogzsGv5BVM4Qv85Z+JYwC9FA=
github.com/lbryio/lbcd v0.22.201-beta-rc4 h1:Xh751Bh/GWRcP5bI6NJ2+zueo2otTcTWapFvFbryP5c=
github.com/lbryio/lbcd v0.22.201-beta-rc4/go.mod h1:Jgo48JDINhdOgHHR83J70Q6G42x3WAo9DI//QogcL+E=
github.com/lbryio/lbcutil v1.0.201/go.mod h1:gDHc/b+Rdz3J7+VB8e5/Bl9roVf8Q5/8FQCyuK9dXD0=
github.com/lbryio/lbcutil v1.0.202-rc3/go.mod h1:LGPtVBBzh4cFXfLFb8ginlFcbA2QwumLNFd0yk/as2o=
github.com/lbryio/lbcutil v1.0.202 h1:L0aRMs2bdCUAicD8Xe4NmUEvevDDea3qkIpCSACnftI=
github.com/lbryio/lbcutil v1.0.202/go.mod h1:LGPtVBBzh4cFXfLFb8ginlFcbA2QwumLNFd0yk/as2o=
github.com/lbryio/lbry.go/v2 v2.7.1/go.mod h1:sUhhSKqPNkiwgBqvBzJIqfLLzGH8hkDGrrO/HcaXzFc=
github.com/lbryio/lbry.go/v3 v3.0.1-beta h1:oIpQ5czhtdVSoWZCiOHE9SrqnNsahyCnMhXvXsd2IiM=
github.com/lbryio/lbry.go/v3 v3.0.1-beta/go.mod h1:v03OVXSBGNZNDfGoAVyjQV/ZOzBGQyTnWs3jpkssxGM=
github.com/lbryio/ozzo-validation v0.0.0-20170323141101-d1008ad1fd04/go.mod h1:fbG/dzobG8r95KzMwckXiLMHfFjZaBRQqC9hPs2XAQ4= github.com/lbryio/ozzo-validation v0.0.0-20170323141101-d1008ad1fd04/go.mod h1:fbG/dzobG8r95KzMwckXiLMHfFjZaBRQqC9hPs2XAQ4=
github.com/lbryio/types v0.0.0-20201019032447-f0b4476ef386/go.mod h1:CG3wsDv5BiVYQd5i1Jp7wGsaVyjZTJshqXeWMVKsISE= github.com/lbryio/types v0.0.0-20201019032447-f0b4476ef386/go.mod h1:CG3wsDv5BiVYQd5i1Jp7wGsaVyjZTJshqXeWMVKsISE=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/linxGnu/grocksdb v1.6.42 h1:nJLoXFuzwBwQQQrXTUgRGRz1QRm7y8pR6CNV/gwrbqs=
github.com/linxGnu/grocksdb v1.6.42/go.mod h1:JcMMDBFaDNhRXFYcYXmgQwb/RarSld1PulTI7UzE+w0=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/lyoshenka/bencode v0.0.0-20180323155644-b7abd7672df5/go.mod h1:H0aPCWffGOaDcjkw1iB7W9DVLp6GXmfcJY/7YZCWPA4= github.com/lyoshenka/bencode v0.0.0-20180323155644-b7abd7672df5/go.mod h1:H0aPCWffGOaDcjkw1iB7W9DVLp6GXmfcJY/7YZCWPA4=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nlopes/slack v0.6.0 h1:jt0jxVQGhssx1Ib7naAOZEZcGdtIhTzkP0nopK0AsRA= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nlopes/slack v0.6.0/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk= github.com/nlopes/slack v0.6.0/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olivere/elastic/v7 v7.0.24 h1:9ZcCQP3Pvgese7TaypYiVAL49sCEphyIwkVxtRf8jb8= github.com/olivere/elastic/v7 v7.0.24 h1:9ZcCQP3Pvgese7TaypYiVAL49sCEphyIwkVxtRf8jb8=
github.com/olivere/elastic/v7 v7.0.24/go.mod h1:OuWmD2DiuYhddWegBKPWQuelVKBLrW0fa/VUYgxuGTY= github.com/olivere/elastic/v7 v7.0.24/go.mod h1:OuWmD2DiuYhddWegBKPWQuelVKBLrW0fa/VUYgxuGTY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sebdah/goldie v0.0.0-20190531093107-d313ffb52c77/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= github.com/sebdah/goldie v0.0.0-20190531093107-d313ffb52c77/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil/v3 v3.21.7/go.mod h1:RGl11Y7XMTQPmHh8F0ayC6haKNBgH4PXMJuTAcMOlz4=
github.com/shopspring/decimal v0.0.0-20191009025716-f1972eb1d1f5/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20191009025716-f1972eb1d1f5/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak= github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -191,118 +558,325 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tklauser/go-sysconf v0.3.7/go.mod h1:JZIdXh4RmBvZDBZ41ld2bGxRV3n4daiiqA3skYhAoQ4=
github.com/tklauser/numcpus v0.2.3/go.mod h1:vpEPS/JC+oZGGQ/My/vJnNsvMDQL6PwOqt8dsCw5j+E=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/vmihailenco/msgpack/v5 v5.3.2/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
github.com/ybbus/jsonrpc v0.0.0-20180411222309-2a548b7d822d/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE= github.com/ybbus/jsonrpc v0.0.0-20180411222309-2a548b7d822d/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b h1:QAqMVf3pSa6eeTsuklijukjXBlj7Es2QQplab+/RbQ4=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20211123021643-48cbe7f80d7c/go.mod h1:b9TAUYHmRtqA6klRHApnXMnj+OyLce4yF5cZCUbk2ps=
golang.org/x/exp v0.0.0-20220907003533-145caa8ea1d0 h1:17k44ji3KFYG94XS5QEFC8pyuOlMh3IoR+vkmTZmJJs=
golang.org/x/exp v0.0.0-20220907003533-145caa8ea1d0/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211123173158-ef496fb156ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20210524171403-669157292da3 h1:xFyh6GBb+NO1L0xqb978I3sBPQpk6FrKO0jJGRvdj/0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84 h1:R1r5J0u6Cx+RNl/6mezTw6oA14cmKC96FeUwL6A9bd4=
google.golang.org/genproto v0.0.0-20210524171403-669157292da3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -319,22 +893,43 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.48.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.48.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/karalabe/cookiejar.v1 v1.0.0-20141109175019-e1490cae028c h1:4GYkPhjcYLPrPAnoxHVQlH/xcXtWN8pEgqBnHrPAs8c= gopkg.in/karalabe/cookiejar.v1 v1.0.0-20141109175019-e1490cae028c h1:4GYkPhjcYLPrPAnoxHVQlH/xcXtWN8pEgqBnHrPAs8c=
gopkg.in/karalabe/cookiejar.v1 v1.0.0-20141109175019-e1490cae028c/go.mod h1:xd7qpr5uPMNy4hsRJ5JEBXA8tJjTFmUI1soCjlCIgAE= gopkg.in/karalabe/cookiejar.v1 v1.0.0-20141109175019-e1490cae028c/go.mod h1:xd7qpr5uPMNy4hsRJ5JEBXA8tJjTFmUI1soCjlCIgAE=
gopkg.in/nullbio/null.v6 v6.0.0-20161116030900-40264a2e6b79 h1:FpCr9V8wuOei4BAen+93HtVJ+XSi+KPbaPKm0Vj5R64= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/nullbio/null.v6 v6.0.0-20161116030900-40264a2e6b79/go.mod h1:gWkaRU7CoXpezCBWfWjm3999QqS+1pYPXGbqQCTMzo8= gopkg.in/nullbio/null.v6 v6.0.0-20161116030900-40264a2e6b79/go.mod h1:gWkaRU7CoXpezCBWfWjm3999QqS+1pYPXGbqQCTMzo8=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

View file

@ -18,8 +18,8 @@ var (
Help: "Number of errors by type", Help: "Number of errors by type",
}, []string{"error_type"}) }, []string{"error_type"})
QueryTime = promauto.NewHistogramVec(prometheus.HistogramOpts{ QueryTime = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "query_time", Name: "query_time",
Help: "Histogram of query times", Help: "Histogram of query times",
Buckets: HistogramBuckets, Buckets: HistogramBuckets,
}, []string{"method"}) }, []string{"method"})
PeersKnown = promauto.NewGauge(prometheus.GaugeOpts{ PeersKnown = promauto.NewGauge(prometheus.GaugeOpts{
@ -30,5 +30,12 @@ var (
Name: "peers_subbed", Name: "peers_subbed",
Help: "Number of peers that are subscribed to us.", Help: "Number of peers that are subscribed to us.",
}) })
BlockCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "block_count",
Help: "Number of blocks we have processed.",
})
ReorgCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "reorg_count",
Help: "Number of blockchain reorgs we have done.",
})
) )

14
internal/search.go Normal file
View file

@ -0,0 +1,14 @@
package internal
import (
"sort"
"golang.org/x/exp/constraints"
)
// BisectRight returns the index of the first element in the list that is greater than or equal to the value.
// https://stackoverflow.com/questions/29959506/is-there-a-go-analog-of-pythons-bisect-module
func BisectRight[T constraints.Ordered](arr []T, val T) uint32 {
i := sort.Search(len(arr), func(i int) bool { return arr[i] >= val })
return uint32(i)
}

54
internal/strings.go Normal file
View file

@ -0,0 +1,54 @@
package internal
import (
"encoding/hex"
"strings"
"golang.org/x/text/cases"
"golang.org/x/text/unicode/norm"
)
func StringSplitArg(stringToSplit, separator string) []interface{} {
split := strings.Split(stringToSplit, separator)
splitInterface := make([]interface{}, len(split))
for i, s := range split {
splitInterface[i] = s
}
return splitInterface
}
// NormalizeName Normalize names to remove weird characters and account to capitalization
func NormalizeName(s string) string {
c := cases.Fold()
return c.String(norm.NFD.String(s))
}
// ReverseBytesInPlace reverse the bytes. thanks, Satoshi 😒
func ReverseBytesInPlace(s []byte) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
// TxIdToTxHash convert the txid to a hash for returning from the hub
func TxIdToTxHash(txid string) []byte {
t, err := hex.DecodeString(txid)
if err != nil {
return nil
}
ReverseBytesInPlace(t)
return t
}
// TxHashToTxId convert the txHash from the response format back to an id
func TxHashToTxId(txHash []byte) string {
t := make([]byte, len(txHash))
copy(t, txHash)
ReverseBytesInPlace(t)
return hex.EncodeToString(t)
}

10
internal/types.go Normal file
View file

@ -0,0 +1,10 @@
package internal
// internal types that need their own file to avoid circular imports.
// HeightHash struct for the height subscription endpoint.
type HeightHash struct {
Height uint64
BlockHash []byte
BlockHeader []byte
}

49
main.go
View file

@ -3,17 +3,25 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"time" "time"
pb "github.com/lbryio/hub/protobuf/go" _ "net/http/pprof"
"github.com/lbryio/hub/server"
"github.com/lbryio/lbry.go/v2/extras/util" "github.com/lbryio/herald.go/internal"
pb "github.com/lbryio/herald.go/protobuf/go"
"github.com/lbryio/herald.go/server"
"github.com/lbryio/lbry.go/v3/extras/stop"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
) )
func main() { func main() {
log.SetFormatter(&log.TextFormatter{
FullTimestamp: true,
})
ctx := context.Background() ctx := context.Background()
searchRequest := &pb.SearchRequest{} searchRequest := &pb.SearchRequest{}
@ -21,29 +29,22 @@ func main() {
if args.CmdType == server.ServeCmd { if args.CmdType == server.ServeCmd {
// This will cancel goroutines with the server finishes. // This will cancel goroutines with the server finishes.
ctxWCancel, cancel := context.WithCancel(ctx) stopGroup := stop.New()
defer cancel()
s := server.MakeHubServer(ctxWCancel, args) initsignals()
s.Run() interrupt := interruptListener()
//l, err := net.Listen("tcp", ":"+args.Port)
//if err != nil { s := server.MakeHubServer(stopGroup, args)
// log.Fatalf("failed to listen: %v", err) go s.Run()
//}
// defer s.Stop()
//pb.RegisterHubServer(s.GrpcServer, s)
//reflection.Register(s.GrpcServer) <-interrupt
//
//log.Printf("listening on %s\n", l.Addr().String())
//log.Println(s.Args)
//if err := s.GrpcServer.Serve(l); err != nil {
// log.Fatalf("failed to serve: %v", err)
//}
return return
} }
conn, err := grpc.Dial("localhost:"+args.Port, conn, err := grpc.Dial("localhost:"+fmt.Sprintf("%d", args.Port),
grpc.WithInsecure(), grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(), grpc.WithBlock(),
) )
if err != nil { if err != nil {
@ -67,7 +68,7 @@ func main() {
log.Printf("found %d results\n", r.GetTotal()) log.Printf("found %d results\n", r.GetTotal())
for _, t := range r.Txos { for _, t := range r.Txos {
fmt.Printf("%s:%d\n", util.TxHashToTxId(t.TxHash), t.Nout) fmt.Printf("%s:%d\n", internal.TxHashToTxId(t.TxHash), t.Nout)
} }
default: default:
log.Fatalln("Unknown Command Type!") log.Fatalln("Unknown Command Type!")

View file

@ -20,7 +20,7 @@ MIN_VERSION="3.0"
version_gte "$VERSION" "$MIN_VERSION" || { echo >&2 "error: protoc version must be >= $MIN_VERSION (your $PROTOC is $VERSION)"; exit 1; } version_gte "$VERSION" "$MIN_VERSION" || { echo >&2 "error: protoc version must be >= $MIN_VERSION (your $PROTOC is $VERSION)"; exit 1; }
hash protoc-gen-go-grpc 2>/dev/null || go get github.com/golang/protobuf/protoc-gen-go google.golang.org/grpc/cmd/protoc-gen-go-grpc hash protoc-gen-go-grpc 2>/dev/null || go install google.golang.org/protobuf/cmd/protoc-gen-go google.golang.org/grpc/cmd/protoc-gen-go-grpc
hash protoc-gen-go-grpc 2>/dev/null || { echo >&2 'error: Make sure $GOPATH/bin is in your $PATH'; exit 1; } hash protoc-gen-go-grpc 2>/dev/null || { echo >&2 'error: Make sure $GOPATH/bin is in your $PATH'; exit 1; }

File diff suppressed because it is too large Load diff

View file

@ -1,19 +1,22 @@
syntax = "proto3"; syntax = "proto3";
option go_package = "github.com/lbryio/hub/protobuf/go/pb"; option go_package = "github.com/lbryio/herald.go/protobuf/go/pb";
import "result.proto"; import "result.proto";
package pb; package pb;
service Hub { service Hub {
rpc Search (SearchRequest) returns (Outputs) {} rpc Search(SearchRequest) returns (Outputs) {}
rpc Ping (EmptyMessage) returns (StringValue) {} rpc Ping(EmptyMessage) returns (StringValue) {}
rpc Hello (HelloMessage) returns (HelloMessage) {} rpc Hello(HelloMessage) returns (HelloMessage) {}
rpc AddPeer (ServerMessage) returns (StringValue) {} rpc AddPeer(ServerMessage) returns (StringValue) {}
rpc PeerSubscribe (ServerMessage) returns (StringValue) {} rpc PeerSubscribe(ServerMessage) returns (StringValue) {}
rpc Version (EmptyMessage) returns (StringValue) {} rpc Version(EmptyMessage) returns (StringValue) {}
rpc Features (EmptyMessage) returns (StringValue) {} rpc Features(EmptyMessage) returns (StringValue) {}
rpc Broadcast(EmptyMessage) returns (UInt32Value) {} rpc Broadcast(EmptyMessage) returns (UInt32Value) {}
rpc Height(EmptyMessage) returns (UInt32Value) {}
rpc HeightSubscribe(UInt32Value) returns (stream UInt32Value) {}
rpc Resolve(StringArray) returns (Outputs) {}
} }
message EmptyMessage {} message EmptyMessage {}
@ -38,6 +41,10 @@ message StringValue {
string value = 1; string value = 1;
} }
message StringArray {
repeated string value = 1;
}
message BoolValue { message BoolValue {
bool value = 1; bool value = 1;
} }
@ -55,7 +62,7 @@ message RangeField {
GT = 4; GT = 4;
} }
Op op = 1; Op op = 1;
repeated string value = 2; repeated int32 value = 2;
} }
message SearchRequest { message SearchRequest {
@ -69,35 +76,34 @@ message SearchRequest {
string last_take_over_height = 8; string last_take_over_height = 8;
string claim_name = 9; string claim_name = 9;
string normalized_name = 10; string normalized_name = 10;
RangeField tx_position = 11; repeated RangeField tx_position = 11;
RangeField amount = 12; repeated RangeField amount = 12;
RangeField timestamp = 13; repeated RangeField timestamp = 13;
RangeField creation_timestamp = 14; repeated RangeField creation_timestamp = 14;
RangeField height = 15; repeated RangeField height = 15;
RangeField creation_height = 16; repeated RangeField creation_height = 16;
RangeField activation_height = 17; repeated RangeField activation_height = 17;
RangeField expiration_height = 18; repeated RangeField expiration_height = 18;
RangeField release_time = 19; repeated RangeField release_time = 19;
string short_url = 20; string short_url = 20;
string canonical_url = 21; string canonical_url = 21;
string title = 22; string title = 22;
string author = 23; string author = 23;
string description = 24; string description = 24;
repeated string claim_type = 25; repeated string claim_type = 25;
RangeField repost_count = 26; repeated RangeField repost_count = 26;
repeated string stream_type = 27; repeated string stream_type = 27;
repeated string media_type = 28; repeated string media_type = 28;
RangeField fee_amount = 29; repeated RangeField fee_amount = 29;
string fee_currency = 30; string fee_currency = 30;
RangeField duration = 31; repeated RangeField duration = 31;
string reposted_claim_id = 32; string reposted_claim_id = 32;
RangeField censor_type = 33; repeated RangeField censor_type = 33;
string claims_in_channel = 34; string claims_in_channel = 34;
RangeField channel_join = 35;
BoolValue is_signature_valid = 36; BoolValue is_signature_valid = 36;
RangeField effective_amount = 37; repeated RangeField effective_amount = 37;
RangeField support_amount = 38; repeated RangeField support_amount = 38;
RangeField trending_score = 39; repeated RangeField trending_score = 39;
string tx_id = 43; string tx_id = 43;
UInt32Value tx_nout = 44; UInt32Value tx_nout = 44;
string signature = 45; string signature = 45;
@ -114,4 +120,5 @@ message SearchRequest {
repeated string all_languages = 56; repeated string all_languages = 56;
bool remove_duplicates = 57; bool remove_duplicates = 57;
bool no_totals = 58; bool no_totals = 58;
} string sd_hash = 59;
}

View file

@ -1,6 +1,6 @@
syntax = "proto3"; syntax = "proto3";
option go_package = "github.com/lbryio/hub/protobuf/go/pb"; option go_package = "github.com/lbryio/herald.go/protobuf/go/pb";
package pb; package pb;
@ -38,8 +38,6 @@ message ClaimMeta {
uint64 effective_amount = 20; uint64 effective_amount = 20;
uint64 support_amount = 21; uint64 support_amount = 21;
double trending_score = 22; double trending_score = 22;
uint64 staked_amount = 23;
} }
message Error { message Error {

5031
protobuf/go/claim.pb.go Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.1 // protoc v3.17.1
// source: hub.proto // source: hub.proto
@ -72,7 +72,7 @@ func (x RangeField_Op) Number() protoreflect.EnumNumber {
// Deprecated: Use RangeField_Op.Descriptor instead. // Deprecated: Use RangeField_Op.Descriptor instead.
func (RangeField_Op) EnumDescriptor() ([]byte, []int) { func (RangeField_Op) EnumDescriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{7, 0} return file_hub_proto_rawDescGZIP(), []int{8, 0}
} }
type EmptyMessage struct { type EmptyMessage struct {
@ -333,6 +333,53 @@ func (x *StringValue) GetValue() string {
return "" return ""
} }
type StringArray struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value []string `protobuf:"bytes,1,rep,name=value,proto3" json:"value"`
}
func (x *StringArray) Reset() {
*x = StringArray{}
if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *StringArray) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*StringArray) ProtoMessage() {}
func (x *StringArray) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use StringArray.ProtoReflect.Descriptor instead.
func (*StringArray) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{5}
}
func (x *StringArray) GetValue() []string {
if x != nil {
return x.Value
}
return nil
}
type BoolValue struct { type BoolValue struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -344,7 +391,7 @@ type BoolValue struct {
func (x *BoolValue) Reset() { func (x *BoolValue) Reset() {
*x = BoolValue{} *x = BoolValue{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[5] mi := &file_hub_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -357,7 +404,7 @@ func (x *BoolValue) String() string {
func (*BoolValue) ProtoMessage() {} func (*BoolValue) ProtoMessage() {}
func (x *BoolValue) ProtoReflect() protoreflect.Message { func (x *BoolValue) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[5] mi := &file_hub_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -370,7 +417,7 @@ func (x *BoolValue) ProtoReflect() protoreflect.Message {
// Deprecated: Use BoolValue.ProtoReflect.Descriptor instead. // Deprecated: Use BoolValue.ProtoReflect.Descriptor instead.
func (*BoolValue) Descriptor() ([]byte, []int) { func (*BoolValue) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{5} return file_hub_proto_rawDescGZIP(), []int{6}
} }
func (x *BoolValue) GetValue() bool { func (x *BoolValue) GetValue() bool {
@ -391,7 +438,7 @@ type UInt32Value struct {
func (x *UInt32Value) Reset() { func (x *UInt32Value) Reset() {
*x = UInt32Value{} *x = UInt32Value{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[6] mi := &file_hub_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -404,7 +451,7 @@ func (x *UInt32Value) String() string {
func (*UInt32Value) ProtoMessage() {} func (*UInt32Value) ProtoMessage() {}
func (x *UInt32Value) ProtoReflect() protoreflect.Message { func (x *UInt32Value) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[6] mi := &file_hub_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -417,7 +464,7 @@ func (x *UInt32Value) ProtoReflect() protoreflect.Message {
// Deprecated: Use UInt32Value.ProtoReflect.Descriptor instead. // Deprecated: Use UInt32Value.ProtoReflect.Descriptor instead.
func (*UInt32Value) Descriptor() ([]byte, []int) { func (*UInt32Value) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{6} return file_hub_proto_rawDescGZIP(), []int{7}
} }
func (x *UInt32Value) GetValue() uint32 { func (x *UInt32Value) GetValue() uint32 {
@ -433,13 +480,13 @@ type RangeField struct {
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
Op RangeField_Op `protobuf:"varint,1,opt,name=op,proto3,enum=pb.RangeField_Op" json:"op"` Op RangeField_Op `protobuf:"varint,1,opt,name=op,proto3,enum=pb.RangeField_Op" json:"op"`
Value []string `protobuf:"bytes,2,rep,name=value,proto3" json:"value"` Value []int32 `protobuf:"varint,2,rep,packed,name=value,proto3" json:"value"`
} }
func (x *RangeField) Reset() { func (x *RangeField) Reset() {
*x = RangeField{} *x = RangeField{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[7] mi := &file_hub_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -452,7 +499,7 @@ func (x *RangeField) String() string {
func (*RangeField) ProtoMessage() {} func (*RangeField) ProtoMessage() {}
func (x *RangeField) ProtoReflect() protoreflect.Message { func (x *RangeField) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[7] mi := &file_hub_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -465,7 +512,7 @@ func (x *RangeField) ProtoReflect() protoreflect.Message {
// Deprecated: Use RangeField.ProtoReflect.Descriptor instead. // Deprecated: Use RangeField.ProtoReflect.Descriptor instead.
func (*RangeField) Descriptor() ([]byte, []int) { func (*RangeField) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{7} return file_hub_proto_rawDescGZIP(), []int{8}
} }
func (x *RangeField) GetOp() RangeField_Op { func (x *RangeField) GetOp() RangeField_Op {
@ -475,7 +522,7 @@ func (x *RangeField) GetOp() RangeField_Op {
return RangeField_EQ return RangeField_EQ
} }
func (x *RangeField) GetValue() []string { func (x *RangeField) GetValue() []int32 {
if x != nil { if x != nil {
return x.Value return x.Value
} }
@ -497,35 +544,34 @@ type SearchRequest struct {
LastTakeOverHeight string `protobuf:"bytes,8,opt,name=last_take_over_height,json=lastTakeOverHeight,proto3" json:"last_take_over_height"` LastTakeOverHeight string `protobuf:"bytes,8,opt,name=last_take_over_height,json=lastTakeOverHeight,proto3" json:"last_take_over_height"`
ClaimName string `protobuf:"bytes,9,opt,name=claim_name,json=claimName,proto3" json:"claim_name"` ClaimName string `protobuf:"bytes,9,opt,name=claim_name,json=claimName,proto3" json:"claim_name"`
NormalizedName string `protobuf:"bytes,10,opt,name=normalized_name,json=normalizedName,proto3" json:"normalized_name"` NormalizedName string `protobuf:"bytes,10,opt,name=normalized_name,json=normalizedName,proto3" json:"normalized_name"`
TxPosition *RangeField `protobuf:"bytes,11,opt,name=tx_position,json=txPosition,proto3" json:"tx_position"` TxPosition []*RangeField `protobuf:"bytes,11,rep,name=tx_position,json=txPosition,proto3" json:"tx_position"`
Amount *RangeField `protobuf:"bytes,12,opt,name=amount,proto3" json:"amount"` Amount []*RangeField `protobuf:"bytes,12,rep,name=amount,proto3" json:"amount"`
Timestamp *RangeField `protobuf:"bytes,13,opt,name=timestamp,proto3" json:"timestamp"` Timestamp []*RangeField `protobuf:"bytes,13,rep,name=timestamp,proto3" json:"timestamp"`
CreationTimestamp *RangeField `protobuf:"bytes,14,opt,name=creation_timestamp,json=creationTimestamp,proto3" json:"creation_timestamp"` CreationTimestamp []*RangeField `protobuf:"bytes,14,rep,name=creation_timestamp,json=creationTimestamp,proto3" json:"creation_timestamp"`
Height *RangeField `protobuf:"bytes,15,opt,name=height,proto3" json:"height"` Height []*RangeField `protobuf:"bytes,15,rep,name=height,proto3" json:"height"`
CreationHeight *RangeField `protobuf:"bytes,16,opt,name=creation_height,json=creationHeight,proto3" json:"creation_height"` CreationHeight []*RangeField `protobuf:"bytes,16,rep,name=creation_height,json=creationHeight,proto3" json:"creation_height"`
ActivationHeight *RangeField `protobuf:"bytes,17,opt,name=activation_height,json=activationHeight,proto3" json:"activation_height"` ActivationHeight []*RangeField `protobuf:"bytes,17,rep,name=activation_height,json=activationHeight,proto3" json:"activation_height"`
ExpirationHeight *RangeField `protobuf:"bytes,18,opt,name=expiration_height,json=expirationHeight,proto3" json:"expiration_height"` ExpirationHeight []*RangeField `protobuf:"bytes,18,rep,name=expiration_height,json=expirationHeight,proto3" json:"expiration_height"`
ReleaseTime *RangeField `protobuf:"bytes,19,opt,name=release_time,json=releaseTime,proto3" json:"release_time"` ReleaseTime []*RangeField `protobuf:"bytes,19,rep,name=release_time,json=releaseTime,proto3" json:"release_time"`
ShortUrl string `protobuf:"bytes,20,opt,name=short_url,json=shortUrl,proto3" json:"short_url"` ShortUrl string `protobuf:"bytes,20,opt,name=short_url,json=shortUrl,proto3" json:"short_url"`
CanonicalUrl string `protobuf:"bytes,21,opt,name=canonical_url,json=canonicalUrl,proto3" json:"canonical_url"` CanonicalUrl string `protobuf:"bytes,21,opt,name=canonical_url,json=canonicalUrl,proto3" json:"canonical_url"`
Title string `protobuf:"bytes,22,opt,name=title,proto3" json:"title"` Title string `protobuf:"bytes,22,opt,name=title,proto3" json:"title"`
Author string `protobuf:"bytes,23,opt,name=author,proto3" json:"author"` Author string `protobuf:"bytes,23,opt,name=author,proto3" json:"author"`
Description string `protobuf:"bytes,24,opt,name=description,proto3" json:"description"` Description string `protobuf:"bytes,24,opt,name=description,proto3" json:"description"`
ClaimType []string `protobuf:"bytes,25,rep,name=claim_type,json=claimType,proto3" json:"claim_type"` ClaimType []string `protobuf:"bytes,25,rep,name=claim_type,json=claimType,proto3" json:"claim_type"`
RepostCount *RangeField `protobuf:"bytes,26,opt,name=repost_count,json=repostCount,proto3" json:"repost_count"` RepostCount []*RangeField `protobuf:"bytes,26,rep,name=repost_count,json=repostCount,proto3" json:"repost_count"`
StreamType []string `protobuf:"bytes,27,rep,name=stream_type,json=streamType,proto3" json:"stream_type"` StreamType []string `protobuf:"bytes,27,rep,name=stream_type,json=streamType,proto3" json:"stream_type"`
MediaType []string `protobuf:"bytes,28,rep,name=media_type,json=mediaType,proto3" json:"media_type"` MediaType []string `protobuf:"bytes,28,rep,name=media_type,json=mediaType,proto3" json:"media_type"`
FeeAmount *RangeField `protobuf:"bytes,29,opt,name=fee_amount,json=feeAmount,proto3" json:"fee_amount"` FeeAmount []*RangeField `protobuf:"bytes,29,rep,name=fee_amount,json=feeAmount,proto3" json:"fee_amount"`
FeeCurrency string `protobuf:"bytes,30,opt,name=fee_currency,json=feeCurrency,proto3" json:"fee_currency"` FeeCurrency string `protobuf:"bytes,30,opt,name=fee_currency,json=feeCurrency,proto3" json:"fee_currency"`
Duration *RangeField `protobuf:"bytes,31,opt,name=duration,proto3" json:"duration"` Duration []*RangeField `protobuf:"bytes,31,rep,name=duration,proto3" json:"duration"`
RepostedClaimId string `protobuf:"bytes,32,opt,name=reposted_claim_id,json=repostedClaimId,proto3" json:"reposted_claim_id"` RepostedClaimId string `protobuf:"bytes,32,opt,name=reposted_claim_id,json=repostedClaimId,proto3" json:"reposted_claim_id"`
CensorType *RangeField `protobuf:"bytes,33,opt,name=censor_type,json=censorType,proto3" json:"censor_type"` CensorType []*RangeField `protobuf:"bytes,33,rep,name=censor_type,json=censorType,proto3" json:"censor_type"`
ClaimsInChannel string `protobuf:"bytes,34,opt,name=claims_in_channel,json=claimsInChannel,proto3" json:"claims_in_channel"` ClaimsInChannel string `protobuf:"bytes,34,opt,name=claims_in_channel,json=claimsInChannel,proto3" json:"claims_in_channel"`
ChannelJoin *RangeField `protobuf:"bytes,35,opt,name=channel_join,json=channelJoin,proto3" json:"channel_join"`
IsSignatureValid *BoolValue `protobuf:"bytes,36,opt,name=is_signature_valid,json=isSignatureValid,proto3" json:"is_signature_valid"` IsSignatureValid *BoolValue `protobuf:"bytes,36,opt,name=is_signature_valid,json=isSignatureValid,proto3" json:"is_signature_valid"`
EffectiveAmount *RangeField `protobuf:"bytes,37,opt,name=effective_amount,json=effectiveAmount,proto3" json:"effective_amount"` EffectiveAmount []*RangeField `protobuf:"bytes,37,rep,name=effective_amount,json=effectiveAmount,proto3" json:"effective_amount"`
SupportAmount *RangeField `protobuf:"bytes,38,opt,name=support_amount,json=supportAmount,proto3" json:"support_amount"` SupportAmount []*RangeField `protobuf:"bytes,38,rep,name=support_amount,json=supportAmount,proto3" json:"support_amount"`
TrendingScore *RangeField `protobuf:"bytes,39,opt,name=trending_score,json=trendingScore,proto3" json:"trending_score"` TrendingScore []*RangeField `protobuf:"bytes,39,rep,name=trending_score,json=trendingScore,proto3" json:"trending_score"`
TxId string `protobuf:"bytes,43,opt,name=tx_id,json=txId,proto3" json:"tx_id"` TxId string `protobuf:"bytes,43,opt,name=tx_id,json=txId,proto3" json:"tx_id"`
TxNout *UInt32Value `protobuf:"bytes,44,opt,name=tx_nout,json=txNout,proto3" json:"tx_nout"` TxNout *UInt32Value `protobuf:"bytes,44,opt,name=tx_nout,json=txNout,proto3" json:"tx_nout"`
Signature string `protobuf:"bytes,45,opt,name=signature,proto3" json:"signature"` Signature string `protobuf:"bytes,45,opt,name=signature,proto3" json:"signature"`
@ -542,12 +588,13 @@ type SearchRequest struct {
AllLanguages []string `protobuf:"bytes,56,rep,name=all_languages,json=allLanguages,proto3" json:"all_languages"` AllLanguages []string `protobuf:"bytes,56,rep,name=all_languages,json=allLanguages,proto3" json:"all_languages"`
RemoveDuplicates bool `protobuf:"varint,57,opt,name=remove_duplicates,json=removeDuplicates,proto3" json:"remove_duplicates"` RemoveDuplicates bool `protobuf:"varint,57,opt,name=remove_duplicates,json=removeDuplicates,proto3" json:"remove_duplicates"`
NoTotals bool `protobuf:"varint,58,opt,name=no_totals,json=noTotals,proto3" json:"no_totals"` NoTotals bool `protobuf:"varint,58,opt,name=no_totals,json=noTotals,proto3" json:"no_totals"`
SdHash string `protobuf:"bytes,59,opt,name=sd_hash,json=sdHash,proto3" json:"sd_hash"`
} }
func (x *SearchRequest) Reset() { func (x *SearchRequest) Reset() {
*x = SearchRequest{} *x = SearchRequest{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_hub_proto_msgTypes[8] mi := &file_hub_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -560,7 +607,7 @@ func (x *SearchRequest) String() string {
func (*SearchRequest) ProtoMessage() {} func (*SearchRequest) ProtoMessage() {}
func (x *SearchRequest) ProtoReflect() protoreflect.Message { func (x *SearchRequest) ProtoReflect() protoreflect.Message {
mi := &file_hub_proto_msgTypes[8] mi := &file_hub_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -573,7 +620,7 @@ func (x *SearchRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead. // Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead.
func (*SearchRequest) Descriptor() ([]byte, []int) { func (*SearchRequest) Descriptor() ([]byte, []int) {
return file_hub_proto_rawDescGZIP(), []int{8} return file_hub_proto_rawDescGZIP(), []int{9}
} }
func (x *SearchRequest) GetClaimId() *InvertibleField { func (x *SearchRequest) GetClaimId() *InvertibleField {
@ -646,63 +693,63 @@ func (x *SearchRequest) GetNormalizedName() string {
return "" return ""
} }
func (x *SearchRequest) GetTxPosition() *RangeField { func (x *SearchRequest) GetTxPosition() []*RangeField {
if x != nil { if x != nil {
return x.TxPosition return x.TxPosition
} }
return nil return nil
} }
func (x *SearchRequest) GetAmount() *RangeField { func (x *SearchRequest) GetAmount() []*RangeField {
if x != nil { if x != nil {
return x.Amount return x.Amount
} }
return nil return nil
} }
func (x *SearchRequest) GetTimestamp() *RangeField { func (x *SearchRequest) GetTimestamp() []*RangeField {
if x != nil { if x != nil {
return x.Timestamp return x.Timestamp
} }
return nil return nil
} }
func (x *SearchRequest) GetCreationTimestamp() *RangeField { func (x *SearchRequest) GetCreationTimestamp() []*RangeField {
if x != nil { if x != nil {
return x.CreationTimestamp return x.CreationTimestamp
} }
return nil return nil
} }
func (x *SearchRequest) GetHeight() *RangeField { func (x *SearchRequest) GetHeight() []*RangeField {
if x != nil { if x != nil {
return x.Height return x.Height
} }
return nil return nil
} }
func (x *SearchRequest) GetCreationHeight() *RangeField { func (x *SearchRequest) GetCreationHeight() []*RangeField {
if x != nil { if x != nil {
return x.CreationHeight return x.CreationHeight
} }
return nil return nil
} }
func (x *SearchRequest) GetActivationHeight() *RangeField { func (x *SearchRequest) GetActivationHeight() []*RangeField {
if x != nil { if x != nil {
return x.ActivationHeight return x.ActivationHeight
} }
return nil return nil
} }
func (x *SearchRequest) GetExpirationHeight() *RangeField { func (x *SearchRequest) GetExpirationHeight() []*RangeField {
if x != nil { if x != nil {
return x.ExpirationHeight return x.ExpirationHeight
} }
return nil return nil
} }
func (x *SearchRequest) GetReleaseTime() *RangeField { func (x *SearchRequest) GetReleaseTime() []*RangeField {
if x != nil { if x != nil {
return x.ReleaseTime return x.ReleaseTime
} }
@ -751,7 +798,7 @@ func (x *SearchRequest) GetClaimType() []string {
return nil return nil
} }
func (x *SearchRequest) GetRepostCount() *RangeField { func (x *SearchRequest) GetRepostCount() []*RangeField {
if x != nil { if x != nil {
return x.RepostCount return x.RepostCount
} }
@ -772,7 +819,7 @@ func (x *SearchRequest) GetMediaType() []string {
return nil return nil
} }
func (x *SearchRequest) GetFeeAmount() *RangeField { func (x *SearchRequest) GetFeeAmount() []*RangeField {
if x != nil { if x != nil {
return x.FeeAmount return x.FeeAmount
} }
@ -786,7 +833,7 @@ func (x *SearchRequest) GetFeeCurrency() string {
return "" return ""
} }
func (x *SearchRequest) GetDuration() *RangeField { func (x *SearchRequest) GetDuration() []*RangeField {
if x != nil { if x != nil {
return x.Duration return x.Duration
} }
@ -800,7 +847,7 @@ func (x *SearchRequest) GetRepostedClaimId() string {
return "" return ""
} }
func (x *SearchRequest) GetCensorType() *RangeField { func (x *SearchRequest) GetCensorType() []*RangeField {
if x != nil { if x != nil {
return x.CensorType return x.CensorType
} }
@ -814,13 +861,6 @@ func (x *SearchRequest) GetClaimsInChannel() string {
return "" return ""
} }
func (x *SearchRequest) GetChannelJoin() *RangeField {
if x != nil {
return x.ChannelJoin
}
return nil
}
func (x *SearchRequest) GetIsSignatureValid() *BoolValue { func (x *SearchRequest) GetIsSignatureValid() *BoolValue {
if x != nil { if x != nil {
return x.IsSignatureValid return x.IsSignatureValid
@ -828,21 +868,21 @@ func (x *SearchRequest) GetIsSignatureValid() *BoolValue {
return nil return nil
} }
func (x *SearchRequest) GetEffectiveAmount() *RangeField { func (x *SearchRequest) GetEffectiveAmount() []*RangeField {
if x != nil { if x != nil {
return x.EffectiveAmount return x.EffectiveAmount
} }
return nil return nil
} }
func (x *SearchRequest) GetSupportAmount() *RangeField { func (x *SearchRequest) GetSupportAmount() []*RangeField {
if x != nil { if x != nil {
return x.SupportAmount return x.SupportAmount
} }
return nil return nil
} }
func (x *SearchRequest) GetTrendingScore() *RangeField { func (x *SearchRequest) GetTrendingScore() []*RangeField {
if x != nil { if x != nil {
return x.TrendingScore return x.TrendingScore
} }
@ -961,6 +1001,13 @@ func (x *SearchRequest) GetNoTotals() bool {
return false return false
} }
func (x *SearchRequest) GetSdHash() string {
if x != nil {
return x.SdHash
}
return ""
}
var File_hub_proto protoreflect.FileDescriptor var File_hub_proto protoreflect.FileDescriptor
var file_hub_proto_rawDesc = []byte{ var file_hub_proto_rawDesc = []byte{
@ -983,189 +1030,199 @@ var file_hub_proto_rawDesc = []byte{
0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x75, 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x21, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x69, 0x6e,
0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x67, 0x41, 0x72, 0x72, 0x61, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x23, 0x0a, 0x0b, 0x55, 0x49, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x21, 0x0a, 0x09,
0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
0x75, 0x0a, 0x0a, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x21, 0x0a, 0x23, 0x0a, 0x0b, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14,
0x02, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76,
0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x4f, 0x70, 0x52, 0x02, 0x6f, 0x70, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x75, 0x0a, 0x0a, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65,
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x6c, 0x64, 0x12, 0x21, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2e, 0x0a, 0x02, 0x4f, 0x70, 0x12, 0x06, 0x0a, 0x02, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x4f,
0x45, 0x51, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x54, 0x45, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x70, 0x52, 0x02, 0x6f, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
0x03, 0x47, 0x54, 0x45, 0x10, 0x02, 0x12, 0x06, 0x0a, 0x02, 0x4c, 0x54, 0x10, 0x03, 0x12, 0x06, 0x20, 0x03, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2e, 0x0a, 0x02, 0x4f,
0x0a, 0x02, 0x47, 0x54, 0x10, 0x04, 0x22, 0xe0, 0x11, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x70, 0x12, 0x06, 0x0a, 0x02, 0x45, 0x51, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x54, 0x45,
0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x6c, 0x61, 0x69, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x47, 0x54, 0x45, 0x10, 0x02, 0x12, 0x06, 0x0a, 0x02, 0x4c,
0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x10, 0x03, 0x12, 0x06, 0x0a, 0x02, 0x47, 0x54, 0x10, 0x04, 0x22, 0xc6, 0x11, 0x0a, 0x0d,
0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a,
0x07, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x08, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x46,
0x62, 0x2e, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x69, 0x62, 0x6c, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x49, 0x64, 0x12, 0x32, 0x0a,
0x64, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x69, 0x62, 0x6c,
0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49,
0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x62, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04,
0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f,
0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x73, 0x5f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74,
0x08, 0x52, 0x0d, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x25,
0x12, 0x31, 0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x6b, 0x65, 0x5f, 0x6f, 0x76, 0x0a, 0x0e, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67,
0x65, 0x72, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x61, 0x6b, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x48, 0x65, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x12, 0x31, 0x0a, 0x15, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x61,
0x67, 0x68, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x6e, 0x61, 0x6d, 0x6b, 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x08,
0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x4e, 0x61, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x54, 0x61, 0x6b, 0x65, 0x4f, 0x76,
0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x65, 0x72, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x61, 0x69,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6e, 0x6f, 0x72, 0x6d, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c,
0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x0b, 0x74, 0x61, 0x69, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6e, 0x6f, 0x72, 0x6d, 0x61,
0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09,
0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0e, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65,
0x52, 0x0a, 0x74, 0x78, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x06, 0x12, 0x2f, 0x0a, 0x0b, 0x74, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18,
0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65,
0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x61, 0x6d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0a, 0x74, 0x78, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f,
0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x6e, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x03, 0x28,
0x70, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e,
0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x12, 0x3d, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e,
0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x11,
0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x12, 0x26, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c,
0x64, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x37, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x09, 0x74, 0x69, 0x6d,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x10, 0x20, 0x01, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70,
0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x09, 0x74, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3d, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0e, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69,
0x65, 0x6c, 0x64, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x26, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74,
0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67,
0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x37,
0x0a, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68,
0x74, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e,
0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x3b, 0x0a, 0x11, 0x61, 0x63, 0x74, 0x69, 0x76,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x11, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65,
0x6c, 0x64, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x6c, 0x64, 0x52, 0x10, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65,
0x68, 0x74, 0x12, 0x3b, 0x0a, 0x11, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x12, 0x3b, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69,
0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x10, 0x61, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52,
0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68,
0x3b, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x65, 0x74, 0x12, 0x31, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x69, 0x6d,
0x69, 0x67, 0x68, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x65, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e,
0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x10, 0x65, 0x78, 0x70, 0x69, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x31, 0x0a, 0x0c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x75, 0x72,
0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x13, 0x20, 0x01, 0x6c, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x55, 0x72,
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x75,
0x6c, 0x64, 0x52, 0x0b, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x72, 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69,
0x1b, 0x0a, 0x09, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x14, 0x20, 0x01, 0x63, 0x61, 0x6c, 0x55, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18,
0x28, 0x09, 0x52, 0x08, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06,
0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x15, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x75,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x72, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x69, 0x6f, 0x6e, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f,
0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x74, 0x79, 0x70, 0x65, 0x18, 0x19, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x61, 0x69,
0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x18, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x31, 0x0a, 0x0c, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x5f,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62,
0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0b, 0x72, 0x65, 0x70,
0x19, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65,
0x12, 0x31, 0x0a, 0x0c, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73,
0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x64,
0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x69, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6d,
0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x79, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x0a, 0x66, 0x65, 0x65, 0x5f,
0x70, 0x65, 0x18, 0x1b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x1d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70,
0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5f, 0x74, 0x79, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x09, 0x66, 0x65,
0x70, 0x65, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x65, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x63,
0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x0a, 0x66, 0x65, 0x65, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66,
0x74, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x65, 0x65, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x08, 0x64, 0x75,
0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x09, 0x66, 0x65, 0x65, 0x41, 0x6d, 0x6f, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x1f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70,
0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x65, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x08, 0x64, 0x75,
0x63, 0x79, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x65, 0x65, 0x43, 0x75, 0x72, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74,
0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x65, 0x64, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x20, 0x20, 0x01, 0x28,
0x6e, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x61, 0x69, 0x6d,
0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x74, 0x79, 0x70,
0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6c, 0x65, 0x18, 0x21, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e,
0x61, 0x69, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x20, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0a, 0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x54,
0x70, 0x6f, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x69, 0x6e,
0x0b, 0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x21, 0x20, 0x01, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f,
0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x49, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12,
0x6c, 0x64, 0x52, 0x0a, 0x63, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x3b, 0x0a, 0x12, 0x69, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f,
0x0a, 0x11, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62,
0x6e, 0x65, 0x6c, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x69, 0x73, 0x53, 0x69,
0x73, 0x49, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x31, 0x0a, 0x0c, 0x63, 0x68, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x10,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x25, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67,
0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x3b, 0x0a, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0f, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76,
0x12, 0x69, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x76, 0x61, 0x65, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x0e, 0x73, 0x75, 0x70, 0x70, 0x6f,
0x6c, 0x69, 0x64, 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x72, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x26, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x69, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52,
0x61, 0x74, 0x75, 0x72, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x10, 0x65, 0x66, 0x0d, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x35,
0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x25, 0x0a, 0x0e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x18, 0x27, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67,
0x69, 0x65, 0x6c, 0x64, 0x52, 0x0f, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0d, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67,
0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x0e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x2b,
0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x07, 0x74, 0x78,
0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0d, 0x73, 0x5f, 0x6e, 0x6f, 0x75, 0x74, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x62,
0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x0e, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x74, 0x78,
0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x27, 0x4e, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x46, 0x65, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
0x69, 0x65, 0x6c, 0x64, 0x52, 0x0d, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x63, 0x72, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f,
0x6f, 0x72, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x74, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x2b, 0x20, 0x01, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x69,
0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x6e, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a,
0x6f, 0x75, 0x74, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x10, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65,
0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x74, 0x78, 0x4e, 0x6f, 0x73, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
0x75, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x65, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69,
0x2d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x30, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
0x12, 0x29, 0x0a, 0x10, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x64, 0x69, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x61,
0x67, 0x65, 0x73, 0x74, 0x18, 0x2e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x6e, 0x79, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x31, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61,
0x61, 0x74, 0x75, 0x72, 0x65, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x6e, 0x79, 0x54, 0x61, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x61,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x67, 0x73, 0x18, 0x32, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x67,
0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x33, 0x20,
0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x6f, 0x74, 0x54, 0x61, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x15,
0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x30, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x75, 0x68, 0x61, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x67, 0x6e,
0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6e, 0x79, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x34, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x68, 0x61, 0x73,
0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x31, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x6e, 0x79, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
0x54, 0x61, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x35,
0x18, 0x32, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61,
0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x33, 0x20, 0x03, 0x28, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x68, 0x61, 0x73, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37,
0x09, 0x52, 0x07, 0x6e, 0x6f, 0x74, 0x54, 0x61, 0x67, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x68, 0x61, 0x0a, 0x18, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x70,
0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x36, 0x20, 0x01, 0x28, 0x05,
0x75, 0x72, 0x65, 0x18, 0x34, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x68, 0x61, 0x73, 0x43, 0x68, 0x52, 0x15, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x50, 0x65, 0x72,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x2c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6e, 0x79, 0x5f, 0x6c,
0x0a, 0x0a, 0x68, 0x61, 0x73, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x35, 0x20, 0x01, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x18, 0x37, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c,
0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x61, 0x6e, 0x79, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d,
0x65, 0x52, 0x09, 0x68, 0x61, 0x73, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x61, 0x6c, 0x6c, 0x5f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x18, 0x38, 0x20,
0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,
0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x36, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x64, 0x75, 0x70, 0x6c,
0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x50, 0x65, 0x72, 0x43, 0x68, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x18, 0x39, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6e, 0x79, 0x5f, 0x6c, 0x61, 0x6e, 0x6d, 0x6f, 0x76, 0x65, 0x44, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1b,
0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x18, 0x37, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x6e, 0x0a, 0x09, 0x6e, 0x6f, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x18, 0x3a, 0x20, 0x01, 0x28,
0x79, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x73,
0x6c, 0x5f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x18, 0x38, 0x20, 0x03, 0x28, 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x64,
0x09, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x73, 0x12, 0x48, 0x61, 0x73, 0x68, 0x32, 0x9b, 0x04, 0x0a, 0x03, 0x48, 0x75, 0x62, 0x12, 0x2a, 0x0a, 0x06,
0x2b, 0x0a, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x61, 0x72,
0x61, 0x74, 0x65, 0x73, 0x18, 0x39, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x4f,
0x76, 0x65, 0x44, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67,
0x6e, 0x6f, 0x5f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x08, 0x6e, 0x6f, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x73, 0x32, 0x88, 0x03, 0x0a, 0x03, 0x48, 0x75, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61,
0x62, 0x12, 0x2a, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x11, 0x2e, 0x70, 0x62, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x10,
0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x67, 0x65, 0x22, 0x00, 0x12, 0x2f, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x12,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61,
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x05, 0x48, 0x65, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61,
0x6c, 0x6c, 0x6f, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x4d, 0x65, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x0d, 0x50, 0x65, 0x65, 0x72, 0x53, 0x75, 0x62,
0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x2f, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53,
0x50, 0x65, 0x65, 0x72, 0x12, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x07,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70,
0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x0d, 0x50, 0x65, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53,
0x65, 0x72, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x11, 0x2e, 0x70, 0x62, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x2f, 0x0a, 0x08,
0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d,
0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e,
0x00, 0x12, 0x2e, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x2e, 0x70, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x30, 0x0a,
0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e,
0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70,
0x00, 0x12, 0x2f, 0x0a, 0x08, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x10, 0x2e, 0x62, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12,
0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x2d, 0x0a, 0x06, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45,
0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62,
0x22, 0x00, 0x12, 0x30, 0x0a, 0x09, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x37,
0x10, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x0a, 0x0f, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x65, 0x12, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c,
0x75, 0x65, 0x22, 0x00, 0x42, 0x26, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x75, 0x65, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61,
0x6f, 0x6d, 0x2f, 0x6c, 0x62, 0x72, 0x79, 0x69, 0x6f, 0x2f, 0x68, 0x75, 0x62, 0x2f, 0x70, 0x72, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x29, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x6f, 0x6c,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x76, 0x65, 0x12, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72,
0x6f, 0x74, 0x6f, 0x33, 0x72, 0x61, 0x79, 0x1a, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73,
0x22, 0x00, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x6c, 0x62, 0x72, 0x79, 0x69, 0x6f, 0x2f, 0x68, 0x65, 0x72, 0x61, 0x6c, 0x64, 0x2e, 0x67,
0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x62,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -1181,7 +1238,7 @@ func file_hub_proto_rawDescGZIP() []byte {
} }
var file_hub_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_hub_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_hub_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_hub_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_hub_proto_goTypes = []interface{}{ var file_hub_proto_goTypes = []interface{}{
(RangeField_Op)(0), // 0: pb.RangeField.Op (RangeField_Op)(0), // 0: pb.RangeField.Op
(*EmptyMessage)(nil), // 1: pb.EmptyMessage (*EmptyMessage)(nil), // 1: pb.EmptyMessage
@ -1189,58 +1246,64 @@ var file_hub_proto_goTypes = []interface{}{
(*HelloMessage)(nil), // 3: pb.HelloMessage (*HelloMessage)(nil), // 3: pb.HelloMessage
(*InvertibleField)(nil), // 4: pb.InvertibleField (*InvertibleField)(nil), // 4: pb.InvertibleField
(*StringValue)(nil), // 5: pb.StringValue (*StringValue)(nil), // 5: pb.StringValue
(*BoolValue)(nil), // 6: pb.BoolValue (*StringArray)(nil), // 6: pb.StringArray
(*UInt32Value)(nil), // 7: pb.UInt32Value (*BoolValue)(nil), // 7: pb.BoolValue
(*RangeField)(nil), // 8: pb.RangeField (*UInt32Value)(nil), // 8: pb.UInt32Value
(*SearchRequest)(nil), // 9: pb.SearchRequest (*RangeField)(nil), // 9: pb.RangeField
(*Outputs)(nil), // 10: pb.Outputs (*SearchRequest)(nil), // 10: pb.SearchRequest
(*Outputs)(nil), // 11: pb.Outputs
} }
var file_hub_proto_depIdxs = []int32{ var file_hub_proto_depIdxs = []int32{
2, // 0: pb.HelloMessage.servers:type_name -> pb.ServerMessage 2, // 0: pb.HelloMessage.servers:type_name -> pb.ServerMessage
0, // 1: pb.RangeField.op:type_name -> pb.RangeField.Op 0, // 1: pb.RangeField.op:type_name -> pb.RangeField.Op
4, // 2: pb.SearchRequest.claim_id:type_name -> pb.InvertibleField 4, // 2: pb.SearchRequest.claim_id:type_name -> pb.InvertibleField
4, // 3: pb.SearchRequest.channel_id:type_name -> pb.InvertibleField 4, // 3: pb.SearchRequest.channel_id:type_name -> pb.InvertibleField
8, // 4: pb.SearchRequest.tx_position:type_name -> pb.RangeField 9, // 4: pb.SearchRequest.tx_position:type_name -> pb.RangeField
8, // 5: pb.SearchRequest.amount:type_name -> pb.RangeField 9, // 5: pb.SearchRequest.amount:type_name -> pb.RangeField
8, // 6: pb.SearchRequest.timestamp:type_name -> pb.RangeField 9, // 6: pb.SearchRequest.timestamp:type_name -> pb.RangeField
8, // 7: pb.SearchRequest.creation_timestamp:type_name -> pb.RangeField 9, // 7: pb.SearchRequest.creation_timestamp:type_name -> pb.RangeField
8, // 8: pb.SearchRequest.height:type_name -> pb.RangeField 9, // 8: pb.SearchRequest.height:type_name -> pb.RangeField
8, // 9: pb.SearchRequest.creation_height:type_name -> pb.RangeField 9, // 9: pb.SearchRequest.creation_height:type_name -> pb.RangeField
8, // 10: pb.SearchRequest.activation_height:type_name -> pb.RangeField 9, // 10: pb.SearchRequest.activation_height:type_name -> pb.RangeField
8, // 11: pb.SearchRequest.expiration_height:type_name -> pb.RangeField 9, // 11: pb.SearchRequest.expiration_height:type_name -> pb.RangeField
8, // 12: pb.SearchRequest.release_time:type_name -> pb.RangeField 9, // 12: pb.SearchRequest.release_time:type_name -> pb.RangeField
8, // 13: pb.SearchRequest.repost_count:type_name -> pb.RangeField 9, // 13: pb.SearchRequest.repost_count:type_name -> pb.RangeField
8, // 14: pb.SearchRequest.fee_amount:type_name -> pb.RangeField 9, // 14: pb.SearchRequest.fee_amount:type_name -> pb.RangeField
8, // 15: pb.SearchRequest.duration:type_name -> pb.RangeField 9, // 15: pb.SearchRequest.duration:type_name -> pb.RangeField
8, // 16: pb.SearchRequest.censor_type:type_name -> pb.RangeField 9, // 16: pb.SearchRequest.censor_type:type_name -> pb.RangeField
8, // 17: pb.SearchRequest.channel_join:type_name -> pb.RangeField 7, // 17: pb.SearchRequest.is_signature_valid:type_name -> pb.BoolValue
6, // 18: pb.SearchRequest.is_signature_valid:type_name -> pb.BoolValue 9, // 18: pb.SearchRequest.effective_amount:type_name -> pb.RangeField
8, // 19: pb.SearchRequest.effective_amount:type_name -> pb.RangeField 9, // 19: pb.SearchRequest.support_amount:type_name -> pb.RangeField
8, // 20: pb.SearchRequest.support_amount:type_name -> pb.RangeField 9, // 20: pb.SearchRequest.trending_score:type_name -> pb.RangeField
8, // 21: pb.SearchRequest.trending_score:type_name -> pb.RangeField 8, // 21: pb.SearchRequest.tx_nout:type_name -> pb.UInt32Value
7, // 22: pb.SearchRequest.tx_nout:type_name -> pb.UInt32Value 7, // 22: pb.SearchRequest.has_source:type_name -> pb.BoolValue
6, // 23: pb.SearchRequest.has_source:type_name -> pb.BoolValue 10, // 23: pb.Hub.Search:input_type -> pb.SearchRequest
9, // 24: pb.Hub.Search:input_type -> pb.SearchRequest 1, // 24: pb.Hub.Ping:input_type -> pb.EmptyMessage
1, // 25: pb.Hub.Ping:input_type -> pb.EmptyMessage 3, // 25: pb.Hub.Hello:input_type -> pb.HelloMessage
3, // 26: pb.Hub.Hello:input_type -> pb.HelloMessage 2, // 26: pb.Hub.AddPeer:input_type -> pb.ServerMessage
2, // 27: pb.Hub.AddPeer:input_type -> pb.ServerMessage 2, // 27: pb.Hub.PeerSubscribe:input_type -> pb.ServerMessage
2, // 28: pb.Hub.PeerSubscribe:input_type -> pb.ServerMessage 1, // 28: pb.Hub.Version:input_type -> pb.EmptyMessage
1, // 29: pb.Hub.Version:input_type -> pb.EmptyMessage 1, // 29: pb.Hub.Features:input_type -> pb.EmptyMessage
1, // 30: pb.Hub.Features:input_type -> pb.EmptyMessage 1, // 30: pb.Hub.Broadcast:input_type -> pb.EmptyMessage
1, // 31: pb.Hub.Broadcast:input_type -> pb.EmptyMessage 1, // 31: pb.Hub.Height:input_type -> pb.EmptyMessage
10, // 32: pb.Hub.Search:output_type -> pb.Outputs 8, // 32: pb.Hub.HeightSubscribe:input_type -> pb.UInt32Value
5, // 33: pb.Hub.Ping:output_type -> pb.StringValue 6, // 33: pb.Hub.Resolve:input_type -> pb.StringArray
3, // 34: pb.Hub.Hello:output_type -> pb.HelloMessage 11, // 34: pb.Hub.Search:output_type -> pb.Outputs
5, // 35: pb.Hub.AddPeer:output_type -> pb.StringValue 5, // 35: pb.Hub.Ping:output_type -> pb.StringValue
5, // 36: pb.Hub.PeerSubscribe:output_type -> pb.StringValue 3, // 36: pb.Hub.Hello:output_type -> pb.HelloMessage
5, // 37: pb.Hub.Version:output_type -> pb.StringValue 5, // 37: pb.Hub.AddPeer:output_type -> pb.StringValue
5, // 38: pb.Hub.Features:output_type -> pb.StringValue 5, // 38: pb.Hub.PeerSubscribe:output_type -> pb.StringValue
7, // 39: pb.Hub.Broadcast:output_type -> pb.UInt32Value 5, // 39: pb.Hub.Version:output_type -> pb.StringValue
32, // [32:40] is the sub-list for method output_type 5, // 40: pb.Hub.Features:output_type -> pb.StringValue
24, // [24:32] is the sub-list for method input_type 8, // 41: pb.Hub.Broadcast:output_type -> pb.UInt32Value
24, // [24:24] is the sub-list for extension type_name 8, // 42: pb.Hub.Height:output_type -> pb.UInt32Value
24, // [24:24] is the sub-list for extension extendee 8, // 43: pb.Hub.HeightSubscribe:output_type -> pb.UInt32Value
0, // [0:24] is the sub-list for field type_name 11, // 44: pb.Hub.Resolve:output_type -> pb.Outputs
34, // [34:45] is the sub-list for method output_type
23, // [23:34] is the sub-list for method input_type
23, // [23:23] is the sub-list for extension type_name
23, // [23:23] is the sub-list for extension extendee
0, // [0:23] is the sub-list for field type_name
} }
func init() { file_hub_proto_init() } func init() { file_hub_proto_init() }
@ -1311,7 +1374,7 @@ func file_hub_proto_init() {
} }
} }
file_hub_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { file_hub_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BoolValue); i { switch v := v.(*StringArray); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -1323,7 +1386,7 @@ func file_hub_proto_init() {
} }
} }
file_hub_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { file_hub_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UInt32Value); i { switch v := v.(*BoolValue); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -1335,7 +1398,7 @@ func file_hub_proto_init() {
} }
} }
file_hub_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { file_hub_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RangeField); i { switch v := v.(*UInt32Value); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -1347,6 +1410,18 @@ func file_hub_proto_init() {
} }
} }
file_hub_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { file_hub_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RangeField); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_hub_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SearchRequest); i { switch v := v.(*SearchRequest); i {
case 0: case 0:
return &v.state return &v.state
@ -1365,7 +1440,7 @@ func file_hub_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_hub_proto_rawDesc, RawDescriptor: file_hub_proto_rawDesc,
NumEnums: 1, NumEnums: 1,
NumMessages: 9, NumMessages: 10,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View file

@ -1,4 +1,8 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.17.1
// source: hub.proto
package pb package pb
@ -26,6 +30,9 @@ type HubClient interface {
Version(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*StringValue, error) Version(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*StringValue, error)
Features(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*StringValue, error) Features(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*StringValue, error)
Broadcast(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*UInt32Value, error) Broadcast(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*UInt32Value, error)
Height(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*UInt32Value, error)
HeightSubscribe(ctx context.Context, in *UInt32Value, opts ...grpc.CallOption) (Hub_HeightSubscribeClient, error)
Resolve(ctx context.Context, in *StringArray, opts ...grpc.CallOption) (*Outputs, error)
} }
type hubClient struct { type hubClient struct {
@ -108,6 +115,56 @@ func (c *hubClient) Broadcast(ctx context.Context, in *EmptyMessage, opts ...grp
return out, nil return out, nil
} }
func (c *hubClient) Height(ctx context.Context, in *EmptyMessage, opts ...grpc.CallOption) (*UInt32Value, error) {
out := new(UInt32Value)
err := c.cc.Invoke(ctx, "/pb.Hub/Height", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *hubClient) HeightSubscribe(ctx context.Context, in *UInt32Value, opts ...grpc.CallOption) (Hub_HeightSubscribeClient, error) {
stream, err := c.cc.NewStream(ctx, &Hub_ServiceDesc.Streams[0], "/pb.Hub/HeightSubscribe", opts...)
if err != nil {
return nil, err
}
x := &hubHeightSubscribeClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Hub_HeightSubscribeClient interface {
Recv() (*UInt32Value, error)
grpc.ClientStream
}
type hubHeightSubscribeClient struct {
grpc.ClientStream
}
func (x *hubHeightSubscribeClient) Recv() (*UInt32Value, error) {
m := new(UInt32Value)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *hubClient) Resolve(ctx context.Context, in *StringArray, opts ...grpc.CallOption) (*Outputs, error) {
out := new(Outputs)
err := c.cc.Invoke(ctx, "/pb.Hub/Resolve", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// HubServer is the server API for Hub service. // HubServer is the server API for Hub service.
// All implementations must embed UnimplementedHubServer // All implementations must embed UnimplementedHubServer
// for forward compatibility // for forward compatibility
@ -120,6 +177,9 @@ type HubServer interface {
Version(context.Context, *EmptyMessage) (*StringValue, error) Version(context.Context, *EmptyMessage) (*StringValue, error)
Features(context.Context, *EmptyMessage) (*StringValue, error) Features(context.Context, *EmptyMessage) (*StringValue, error)
Broadcast(context.Context, *EmptyMessage) (*UInt32Value, error) Broadcast(context.Context, *EmptyMessage) (*UInt32Value, error)
Height(context.Context, *EmptyMessage) (*UInt32Value, error)
HeightSubscribe(*UInt32Value, Hub_HeightSubscribeServer) error
Resolve(context.Context, *StringArray) (*Outputs, error)
mustEmbedUnimplementedHubServer() mustEmbedUnimplementedHubServer()
} }
@ -151,6 +211,15 @@ func (UnimplementedHubServer) Features(context.Context, *EmptyMessage) (*StringV
func (UnimplementedHubServer) Broadcast(context.Context, *EmptyMessage) (*UInt32Value, error) { func (UnimplementedHubServer) Broadcast(context.Context, *EmptyMessage) (*UInt32Value, error) {
return nil, status.Errorf(codes.Unimplemented, "method Broadcast not implemented") return nil, status.Errorf(codes.Unimplemented, "method Broadcast not implemented")
} }
func (UnimplementedHubServer) Height(context.Context, *EmptyMessage) (*UInt32Value, error) {
return nil, status.Errorf(codes.Unimplemented, "method Height not implemented")
}
func (UnimplementedHubServer) HeightSubscribe(*UInt32Value, Hub_HeightSubscribeServer) error {
return status.Errorf(codes.Unimplemented, "method HeightSubscribe not implemented")
}
func (UnimplementedHubServer) Resolve(context.Context, *StringArray) (*Outputs, error) {
return nil, status.Errorf(codes.Unimplemented, "method Resolve not implemented")
}
func (UnimplementedHubServer) mustEmbedUnimplementedHubServer() {} func (UnimplementedHubServer) mustEmbedUnimplementedHubServer() {}
// UnsafeHubServer may be embedded to opt out of forward compatibility for this service. // UnsafeHubServer may be embedded to opt out of forward compatibility for this service.
@ -308,6 +377,63 @@ func _Hub_Broadcast_Handler(srv interface{}, ctx context.Context, dec func(inter
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Hub_Height_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HubServer).Height(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.Hub/Height",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HubServer).Height(ctx, req.(*EmptyMessage))
}
return interceptor(ctx, in, info, handler)
}
func _Hub_HeightSubscribe_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(UInt32Value)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(HubServer).HeightSubscribe(m, &hubHeightSubscribeServer{stream})
}
type Hub_HeightSubscribeServer interface {
Send(*UInt32Value) error
grpc.ServerStream
}
type hubHeightSubscribeServer struct {
grpc.ServerStream
}
func (x *hubHeightSubscribeServer) Send(m *UInt32Value) error {
return x.ServerStream.SendMsg(m)
}
func _Hub_Resolve_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(StringArray)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HubServer).Resolve(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.Hub/Resolve",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HubServer).Resolve(ctx, req.(*StringArray))
}
return interceptor(ctx, in, info, handler)
}
// Hub_ServiceDesc is the grpc.ServiceDesc for Hub service. // Hub_ServiceDesc is the grpc.ServiceDesc for Hub service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@ -347,7 +473,21 @@ var Hub_ServiceDesc = grpc.ServiceDesc{
MethodName: "Broadcast", MethodName: "Broadcast",
Handler: _Hub_Broadcast_Handler, Handler: _Hub_Broadcast_Handler,
}, },
{
MethodName: "Height",
Handler: _Hub_Height_Handler,
},
{
MethodName: "Resolve",
Handler: _Hub_Resolve_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "HeightSubscribe",
Handler: _Hub_HeightSubscribe_Handler,
ServerStreams: true,
},
}, },
Streams: []grpc.StreamDesc{},
Metadata: "hub.proto", Metadata: "hub.proto",
} }

View file

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.26.0 // protoc-gen-go v1.27.1
// protoc v3.17.1 // protoc v3.17.1
// source: result.proto // source: result.proto
@ -282,7 +282,6 @@ type ClaimMeta struct {
EffectiveAmount uint64 `protobuf:"varint,20,opt,name=effective_amount,json=effectiveAmount,proto3" json:"effective_amount"` EffectiveAmount uint64 `protobuf:"varint,20,opt,name=effective_amount,json=effectiveAmount,proto3" json:"effective_amount"`
SupportAmount uint64 `protobuf:"varint,21,opt,name=support_amount,json=supportAmount,proto3" json:"support_amount"` SupportAmount uint64 `protobuf:"varint,21,opt,name=support_amount,json=supportAmount,proto3" json:"support_amount"`
TrendingScore float64 `protobuf:"fixed64,22,opt,name=trending_score,json=trendingScore,proto3" json:"trending_score"` TrendingScore float64 `protobuf:"fixed64,22,opt,name=trending_score,json=trendingScore,proto3" json:"trending_score"`
StakedAmount uint64 `protobuf:"varint,23,opt,name=staked_amount,json=stakedAmount,proto3" json:"staked_amount"`
} }
func (x *ClaimMeta) Reset() { func (x *ClaimMeta) Reset() {
@ -415,13 +414,6 @@ func (x *ClaimMeta) GetTrendingScore() float64 {
return 0 return 0
} }
func (x *ClaimMeta) GetStakedAmount() uint64 {
if x != nil {
return x.StakedAmount
}
return 0
}
type Error struct { type Error struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -567,7 +559,7 @@ var file_result_proto_rawDesc = []byte{
0x61, 0x48, 0x00, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x21, 0x0a, 0x05, 0x65, 0x72, 0x61, 0x48, 0x00, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x12, 0x21, 0x0a, 0x05, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x72, 0x6f, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45,
0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x06, 0x0a, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x06, 0x0a,
0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0xd1, 0x04, 0x0a, 0x09, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4d, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0xac, 0x04, 0x0a, 0x09, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x4d,
0x65, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x65, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74,
0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x22, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x22, 0x0a, 0x06, 0x72, 0x65, 0x70,
@ -602,27 +594,25 @@ var file_result_proto_rawDesc = []byte{
0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a,
0x0e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x0e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18,
0x16, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53, 0x16, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0d, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x53,
0x63, 0x6f, 0x72, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x6b, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x6f, 0x72, 0x65, 0x22, 0xa9, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x22,
0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x70,
0x6b, 0x65, 0x64, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xa9, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x62, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f,
0x72, 0x6f, 0x72, 0x12, 0x22, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x0e, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x43, 0x6f, 0x64, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x25, 0x0a, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65,
0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x25, 0x0a, 0x07, 0x62, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x22, 0x41, 0x0a,
0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,
0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x54, 0x5f, 0x46,
0x65, 0x64, 0x22, 0x41, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x4e, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49,
0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x03,
0x4e, 0x4f, 0x54, 0x5f, 0x46, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x22, 0x45, 0x0a, 0x07, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63,
0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x42, 0x4c, 0x4f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e,
0x4b, 0x45, 0x44, 0x10, 0x03, 0x22, 0x45, 0x0a, 0x07, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x74, 0x12, 0x24, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01,
0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x07,
0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75,
0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x62, 0x72, 0x79, 0x69, 0x6f, 0x2f, 0x68, 0x65, 0x72,
0x70, 0x75, 0x74, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x42, 0x26, 0x5a, 0x24, 0x61, 0x6c, 0x64, 0x2e, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x62, 0x72, 0x79, 0x69, 0x67, 0x6f, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x2f, 0x68, 0x75, 0x62, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67,
0x6f, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,4 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc

132
protobuf/python/hub_pb2.py Normal file
View file

@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: hub.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
import result_pb2 as result__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\thub.proto\x12\x02pb\x1a\x0cresult.proto\"\x0e\n\x0c\x45mptyMessage\".\n\rServerMessage\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x0c\n\x04port\x18\x02 \x01(\t\"N\n\x0cHelloMessage\x12\x0c\n\x04port\x18\x01 \x01(\t\x12\x0c\n\x04host\x18\x02 \x01(\t\x12\"\n\x07servers\x18\x03 \x03(\x0b\x32\x11.pb.ServerMessage\"0\n\x0fInvertibleField\x12\x0e\n\x06invert\x18\x01 \x01(\x08\x12\r\n\x05value\x18\x02 \x03(\t\"\x1c\n\x0bStringValue\x12\r\n\x05value\x18\x01 \x01(\t\"\x1c\n\x0bStringArray\x12\r\n\x05value\x18\x01 \x03(\t\"\x1a\n\tBoolValue\x12\r\n\x05value\x18\x01 \x01(\x08\"\x1c\n\x0bUInt32Value\x12\r\n\x05value\x18\x01 \x01(\r\"j\n\nRangeField\x12\x1d\n\x02op\x18\x01 \x01(\x0e\x32\x11.pb.RangeField.Op\x12\r\n\x05value\x18\x02 \x03(\x05\".\n\x02Op\x12\x06\n\x02\x45Q\x10\x00\x12\x07\n\x03LTE\x10\x01\x12\x07\n\x03GTE\x10\x02\x12\x06\n\x02LT\x10\x03\x12\x06\n\x02GT\x10\x04\"\x8e\x0c\n\rSearchRequest\x12%\n\x08\x63laim_id\x18\x01 \x01(\x0b\x32\x13.pb.InvertibleField\x12\'\n\nchannel_id\x18\x02 \x01(\x0b\x32\x13.pb.InvertibleField\x12\x0c\n\x04text\x18\x03 \x01(\t\x12\r\n\x05limit\x18\x04 \x01(\x05\x12\x10\n\x08order_by\x18\x05 \x03(\t\x12\x0e\n\x06offset\x18\x06 \x01(\r\x12\x16\n\x0eis_controlling\x18\x07 \x01(\x08\x12\x1d\n\x15last_take_over_height\x18\x08 \x01(\t\x12\x12\n\nclaim_name\x18\t \x01(\t\x12\x17\n\x0fnormalized_name\x18\n \x01(\t\x12#\n\x0btx_position\x18\x0b \x03(\x0b\x32\x0e.pb.RangeField\x12\x1e\n\x06\x61mount\x18\x0c \x03(\x0b\x32\x0e.pb.RangeField\x12!\n\ttimestamp\x18\r \x03(\x0b\x32\x0e.pb.RangeField\x12*\n\x12\x63reation_timestamp\x18\x0e \x03(\x0b\x32\x0e.pb.RangeField\x12\x1e\n\x06height\x18\x0f \x03(\x0b\x32\x0e.pb.RangeField\x12\'\n\x0f\x63reation_height\x18\x10 \x03(\x0b\x32\x0e.pb.RangeField\x12)\n\x11\x61\x63tivation_height\x18\x11 \x03(\x0b\x32\x0e.pb.RangeField\x12)\n\x11\x65xpiration_height\x18\x12 \x03(\x0b\x32\x0e.pb.RangeField\x12$\n\x0crelease_time\x18\x13 \x03(\x0b\x32\x0e.pb.RangeField\x12\x11\n\tshort_url\x18\x14 \x01(\t\x12\x15\n\rcanonical_url\x18\x15 \x01(\t\x12\r\n\x05title\x18\x16 \x01(\t\x12\x0e\n\x06\x61uthor\x18\x17 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x18 \x01(\t\x12\x12\n\nclaim_type\x18\x19 \x03(\t\x12$\n\x0crepost_count\x18\x1a \x03(\x0b\x32\x0e.pb.RangeField\x12\x13\n\x0bstream_type\x18\x1b \x03(\t\x12\x12\n\nmedia_type\x18\x1c \x03(\t\x12\"\n\nfee_amount\x18\x1d \x03(\x0b\x32\x0e.pb.RangeField\x12\x14\n\x0c\x66\x65\x65_currency\x18\x1e \x01(\t\x12 \n\x08\x64uration\x18\x1f \x03(\x0b\x32\x0e.pb.RangeField\x12\x19\n\x11reposted_claim_id\x18 \x01(\t\x12#\n\x0b\x63\x65nsor_type\x18! \x03(\x0b\x32\x0e.pb.RangeField\x12\x19\n\x11\x63laims_in_channel\x18\" \x01(\t\x12)\n\x12is_signature_valid\x18$ \x01(\x0b\x32\r.pb.BoolValue\x12(\n\x10\x65\x66\x66\x65\x63tive_amount\x18% \x03(\x0b\x32\x0e.pb.RangeField\x12&\n\x0esupport_amount\x18& \x03(\x0b\x32\x0e.pb.RangeField\x12&\n\x0etrending_score\x18\' \x03(\x0b\x32\x0e.pb.RangeField\x12\r\n\x05tx_id\x18+ \x01(\t\x12 \n\x07tx_nout\x18, \x01(\x0b\x32\x0f.pb.UInt32Value\x12\x11\n\tsignature\x18- \x01(\t\x12\x18\n\x10signature_digest\x18. \x01(\t\x12\x18\n\x10public_key_bytes\x18/ \x01(\t\x12\x15\n\rpublic_key_id\x18\x30 \x01(\t\x12\x10\n\x08\x61ny_tags\x18\x31 \x03(\t\x12\x10\n\x08\x61ll_tags\x18\x32 \x03(\t\x12\x10\n\x08not_tags\x18\x33 \x03(\t\x12\x1d\n\x15has_channel_signature\x18\x34 \x01(\x08\x12!\n\nhas_source\x18\x35 \x01(\x0b\x32\r.pb.BoolValue\x12 \n\x18limit_claims_per_channel\x18\x36 \x01(\x05\x12\x15\n\rany_languages\x18\x37 \x03(\t\x12\x15\n\rall_languages\x18\x38 \x03(\t\x12\x19\n\x11remove_duplicates\x18\x39 \x01(\x08\x12\x11\n\tno_totals\x18: \x01(\x08\x12\x0f\n\x07sd_hash\x18; \x01(\t2\x9b\x04\n\x03Hub\x12*\n\x06Search\x12\x11.pb.SearchRequest\x1a\x0b.pb.Outputs\"\x00\x12+\n\x04Ping\x12\x10.pb.EmptyMessage\x1a\x0f.pb.StringValue\"\x00\x12-\n\x05Hello\x12\x10.pb.HelloMessage\x1a\x10.pb.HelloMessage\"\x00\x12/\n\x07\x41\x64\x64Peer\x12\x11.pb.ServerMessage\x1a\x0f.pb.StringValue\"\x00\x12\x35\n\rPeerSubscribe\x12\x11.pb.ServerMessage\x1a\x0f.pb.StringValue\"\x00\x12.\n\x07Version\x12\x10.pb.EmptyMessage\x1a\x0f.pb.StringValue\"\x00\x12/\n\x08\x46\x65\x61tures\x12\x10.pb.EmptyMessage\x1a\x0f.pb.StringValue\"\x00\x12\x30\n\tBroadcast\x12\x10.pb.EmptyMessage\x1a\x0f.pb.UInt32Value\"\x00\x12-\n\x06Height\x12\x10.pb.EmptyMessage\x1a\x0f.pb.UInt32Value\"\x00\x12\x37\n\x0fHeightSubscribe\x12\x0f.pb.UInt32Value\x1a\x0f.pb.UInt32Value\"\x00\x30\x01\x12)\n\x07Resolve\x12\x0f.pb.StringArray\x1a\x0b.pb.Outputs\"\x00\x42,Z*github.com/lbryio/herald.go/protobuf/go/pbb\x06proto3')
_EMPTYMESSAGE = DESCRIPTOR.message_types_by_name['EmptyMessage']
_SERVERMESSAGE = DESCRIPTOR.message_types_by_name['ServerMessage']
_HELLOMESSAGE = DESCRIPTOR.message_types_by_name['HelloMessage']
_INVERTIBLEFIELD = DESCRIPTOR.message_types_by_name['InvertibleField']
_STRINGVALUE = DESCRIPTOR.message_types_by_name['StringValue']
_STRINGARRAY = DESCRIPTOR.message_types_by_name['StringArray']
_BOOLVALUE = DESCRIPTOR.message_types_by_name['BoolValue']
_UINT32VALUE = DESCRIPTOR.message_types_by_name['UInt32Value']
_RANGEFIELD = DESCRIPTOR.message_types_by_name['RangeField']
_SEARCHREQUEST = DESCRIPTOR.message_types_by_name['SearchRequest']
_RANGEFIELD_OP = _RANGEFIELD.enum_types_by_name['Op']
EmptyMessage = _reflection.GeneratedProtocolMessageType('EmptyMessage', (_message.Message,), {
'DESCRIPTOR' : _EMPTYMESSAGE,
'__module__' : 'hub_pb2'
# @@protoc_insertion_point(class_scope:pb.EmptyMessage)
})
_sym_db.RegisterMessage(EmptyMessage)
ServerMessage = _reflection.GeneratedProtocolMessageType('ServerMessage', (_message.Message,), {
'DESCRIPTOR' : _SERVERMESSAGE,
'__module__' : 'hub_pb2'
# @@protoc_insertion_point(class_scope:pb.ServerMessage)
})
_sym_db.RegisterMessage(ServerMessage)
HelloMessage = _reflection.GeneratedProtocolMessageType('HelloMessage', (_message.Message,), {
'DESCRIPTOR' : _HELLOMESSAGE,
'__module__' : 'hub_pb2'
# @@protoc_insertion_point(class_scope:pb.HelloMessage)
})
_sym_db.RegisterMessage(HelloMessage)
InvertibleField = _reflection.GeneratedProtocolMessageType('InvertibleField', (_message.Message,), {
'DESCRIPTOR' : _INVERTIBLEFIELD,
'__module__' : 'hub_pb2'
# @@protoc_insertion_point(class_scope:pb.InvertibleField)
})
_sym_db.RegisterMessage(InvertibleField)
StringValue = _reflection.GeneratedProtocolMessageType('StringValue', (_message.Message,), {
'DESCRIPTOR' : _STRINGVALUE,
'__module__' : 'hub_pb2'
# @@protoc_insertion_point(class_scope:pb.StringValue)
})
_sym_db.RegisterMessage(StringValue)
StringArray = _reflection.GeneratedProtocolMessageType('StringArray', (_message.Message,), {
'DESCRIPTOR' : _STRINGARRAY,
'__module__' : 'hub_pb2'
# @@protoc_insertion_point(class_scope:pb.StringArray)
})
_sym_db.RegisterMessage(StringArray)
BoolValue = _reflection.GeneratedProtocolMessageType('BoolValue', (_message.Message,), {
'DESCRIPTOR' : _BOOLVALUE,
'__module__' : 'hub_pb2'
# @@protoc_insertion_point(class_scope:pb.BoolValue)
})
_sym_db.RegisterMessage(BoolValue)
UInt32Value = _reflection.GeneratedProtocolMessageType('UInt32Value', (_message.Message,), {
'DESCRIPTOR' : _UINT32VALUE,
'__module__' : 'hub_pb2'
# @@protoc_insertion_point(class_scope:pb.UInt32Value)
})
_sym_db.RegisterMessage(UInt32Value)
RangeField = _reflection.GeneratedProtocolMessageType('RangeField', (_message.Message,), {
'DESCRIPTOR' : _RANGEFIELD,
'__module__' : 'hub_pb2'
# @@protoc_insertion_point(class_scope:pb.RangeField)
})
_sym_db.RegisterMessage(RangeField)
SearchRequest = _reflection.GeneratedProtocolMessageType('SearchRequest', (_message.Message,), {
'DESCRIPTOR' : _SEARCHREQUEST,
'__module__' : 'hub_pb2'
# @@protoc_insertion_point(class_scope:pb.SearchRequest)
})
_sym_db.RegisterMessage(SearchRequest)
_HUB = DESCRIPTOR.services_by_name['Hub']
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'Z*github.com/lbryio/herald.go/protobuf/go/pb'
_EMPTYMESSAGE._serialized_start=31
_EMPTYMESSAGE._serialized_end=45
_SERVERMESSAGE._serialized_start=47
_SERVERMESSAGE._serialized_end=93
_HELLOMESSAGE._serialized_start=95
_HELLOMESSAGE._serialized_end=173
_INVERTIBLEFIELD._serialized_start=175
_INVERTIBLEFIELD._serialized_end=223
_STRINGVALUE._serialized_start=225
_STRINGVALUE._serialized_end=253
_STRINGARRAY._serialized_start=255
_STRINGARRAY._serialized_end=283
_BOOLVALUE._serialized_start=285
_BOOLVALUE._serialized_end=311
_UINT32VALUE._serialized_start=313
_UINT32VALUE._serialized_end=341
_RANGEFIELD._serialized_start=343
_RANGEFIELD._serialized_end=449
_RANGEFIELD_OP._serialized_start=403
_RANGEFIELD_OP._serialized_end=449
_SEARCHREQUEST._serialized_start=452
_SEARCHREQUEST._serialized_end=2002
_HUB._serialized_start=2005
_HUB._serialized_end=2544
# @@protoc_insertion_point(module_scope)

View file

@ -0,0 +1,397 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import hub_pb2 as hub__pb2
import result_pb2 as result__pb2
class HubStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.Search = channel.unary_unary(
'/pb.Hub/Search',
request_serializer=hub__pb2.SearchRequest.SerializeToString,
response_deserializer=result__pb2.Outputs.FromString,
)
self.Ping = channel.unary_unary(
'/pb.Hub/Ping',
request_serializer=hub__pb2.EmptyMessage.SerializeToString,
response_deserializer=hub__pb2.StringValue.FromString,
)
self.Hello = channel.unary_unary(
'/pb.Hub/Hello',
request_serializer=hub__pb2.HelloMessage.SerializeToString,
response_deserializer=hub__pb2.HelloMessage.FromString,
)
self.AddPeer = channel.unary_unary(
'/pb.Hub/AddPeer',
request_serializer=hub__pb2.ServerMessage.SerializeToString,
response_deserializer=hub__pb2.StringValue.FromString,
)
self.PeerSubscribe = channel.unary_unary(
'/pb.Hub/PeerSubscribe',
request_serializer=hub__pb2.ServerMessage.SerializeToString,
response_deserializer=hub__pb2.StringValue.FromString,
)
self.Version = channel.unary_unary(
'/pb.Hub/Version',
request_serializer=hub__pb2.EmptyMessage.SerializeToString,
response_deserializer=hub__pb2.StringValue.FromString,
)
self.Features = channel.unary_unary(
'/pb.Hub/Features',
request_serializer=hub__pb2.EmptyMessage.SerializeToString,
response_deserializer=hub__pb2.StringValue.FromString,
)
self.Broadcast = channel.unary_unary(
'/pb.Hub/Broadcast',
request_serializer=hub__pb2.EmptyMessage.SerializeToString,
response_deserializer=hub__pb2.UInt32Value.FromString,
)
self.Height = channel.unary_unary(
'/pb.Hub/Height',
request_serializer=hub__pb2.EmptyMessage.SerializeToString,
response_deserializer=hub__pb2.UInt32Value.FromString,
)
self.HeightSubscribe = channel.unary_stream(
'/pb.Hub/HeightSubscribe',
request_serializer=hub__pb2.UInt32Value.SerializeToString,
response_deserializer=hub__pb2.UInt32Value.FromString,
)
self.Resolve = channel.unary_unary(
'/pb.Hub/Resolve',
request_serializer=hub__pb2.StringArray.SerializeToString,
response_deserializer=result__pb2.Outputs.FromString,
)
class HubServicer(object):
"""Missing associated documentation comment in .proto file."""
def Search(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Ping(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Hello(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def AddPeer(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def PeerSubscribe(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Version(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Features(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Broadcast(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Height(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def HeightSubscribe(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Resolve(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_HubServicer_to_server(servicer, server):
rpc_method_handlers = {
'Search': grpc.unary_unary_rpc_method_handler(
servicer.Search,
request_deserializer=hub__pb2.SearchRequest.FromString,
response_serializer=result__pb2.Outputs.SerializeToString,
),
'Ping': grpc.unary_unary_rpc_method_handler(
servicer.Ping,
request_deserializer=hub__pb2.EmptyMessage.FromString,
response_serializer=hub__pb2.StringValue.SerializeToString,
),
'Hello': grpc.unary_unary_rpc_method_handler(
servicer.Hello,
request_deserializer=hub__pb2.HelloMessage.FromString,
response_serializer=hub__pb2.HelloMessage.SerializeToString,
),
'AddPeer': grpc.unary_unary_rpc_method_handler(
servicer.AddPeer,
request_deserializer=hub__pb2.ServerMessage.FromString,
response_serializer=hub__pb2.StringValue.SerializeToString,
),
'PeerSubscribe': grpc.unary_unary_rpc_method_handler(
servicer.PeerSubscribe,
request_deserializer=hub__pb2.ServerMessage.FromString,
response_serializer=hub__pb2.StringValue.SerializeToString,
),
'Version': grpc.unary_unary_rpc_method_handler(
servicer.Version,
request_deserializer=hub__pb2.EmptyMessage.FromString,
response_serializer=hub__pb2.StringValue.SerializeToString,
),
'Features': grpc.unary_unary_rpc_method_handler(
servicer.Features,
request_deserializer=hub__pb2.EmptyMessage.FromString,
response_serializer=hub__pb2.StringValue.SerializeToString,
),
'Broadcast': grpc.unary_unary_rpc_method_handler(
servicer.Broadcast,
request_deserializer=hub__pb2.EmptyMessage.FromString,
response_serializer=hub__pb2.UInt32Value.SerializeToString,
),
'Height': grpc.unary_unary_rpc_method_handler(
servicer.Height,
request_deserializer=hub__pb2.EmptyMessage.FromString,
response_serializer=hub__pb2.UInt32Value.SerializeToString,
),
'HeightSubscribe': grpc.unary_stream_rpc_method_handler(
servicer.HeightSubscribe,
request_deserializer=hub__pb2.UInt32Value.FromString,
response_serializer=hub__pb2.UInt32Value.SerializeToString,
),
'Resolve': grpc.unary_unary_rpc_method_handler(
servicer.Resolve,
request_deserializer=hub__pb2.StringArray.FromString,
response_serializer=result__pb2.Outputs.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'pb.Hub', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class Hub(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def Search(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/pb.Hub/Search',
hub__pb2.SearchRequest.SerializeToString,
result__pb2.Outputs.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Ping(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/pb.Hub/Ping',
hub__pb2.EmptyMessage.SerializeToString,
hub__pb2.StringValue.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Hello(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/pb.Hub/Hello',
hub__pb2.HelloMessage.SerializeToString,
hub__pb2.HelloMessage.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def AddPeer(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/pb.Hub/AddPeer',
hub__pb2.ServerMessage.SerializeToString,
hub__pb2.StringValue.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def PeerSubscribe(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/pb.Hub/PeerSubscribe',
hub__pb2.ServerMessage.SerializeToString,
hub__pb2.StringValue.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Version(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/pb.Hub/Version',
hub__pb2.EmptyMessage.SerializeToString,
hub__pb2.StringValue.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Features(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/pb.Hub/Features',
hub__pb2.EmptyMessage.SerializeToString,
hub__pb2.StringValue.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Broadcast(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/pb.Hub/Broadcast',
hub__pb2.EmptyMessage.SerializeToString,
hub__pb2.UInt32Value.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Height(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/pb.Hub/Height',
hub__pb2.EmptyMessage.SerializeToString,
hub__pb2.UInt32Value.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def HeightSubscribe(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_stream(request, target, '/pb.Hub/HeightSubscribe',
hub__pb2.UInt32Value.SerializeToString,
hub__pb2.UInt32Value.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Resolve(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/pb.Hub/Resolve',
hub__pb2.StringArray.SerializeToString,
result__pb2.Outputs.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)

View file

@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: result.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cresult.proto\x12\x02pb\"\x97\x01\n\x07Outputs\x12\x18\n\x04txos\x18\x01 \x03(\x0b\x32\n.pb.Output\x12\x1e\n\nextra_txos\x18\x02 \x03(\x0b\x32\n.pb.Output\x12\r\n\x05total\x18\x03 \x01(\r\x12\x0e\n\x06offset\x18\x04 \x01(\r\x12\x1c\n\x07\x62locked\x18\x05 \x03(\x0b\x32\x0b.pb.Blocked\x12\x15\n\rblocked_total\x18\x06 \x01(\r\"{\n\x06Output\x12\x0f\n\x07tx_hash\x18\x01 \x01(\x0c\x12\x0c\n\x04nout\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x1e\n\x05\x63laim\x18\x07 \x01(\x0b\x32\r.pb.ClaimMetaH\x00\x12\x1a\n\x05\x65rror\x18\x0f \x01(\x0b\x32\t.pb.ErrorH\x00\x42\x06\n\x04meta\"\xe6\x02\n\tClaimMeta\x12\x1b\n\x07\x63hannel\x18\x01 \x01(\x0b\x32\n.pb.Output\x12\x1a\n\x06repost\x18\x02 \x01(\x0b\x32\n.pb.Output\x12\x11\n\tshort_url\x18\x03 \x01(\t\x12\x15\n\rcanonical_url\x18\x04 \x01(\t\x12\x16\n\x0eis_controlling\x18\x05 \x01(\x08\x12\x18\n\x10take_over_height\x18\x06 \x01(\r\x12\x17\n\x0f\x63reation_height\x18\x07 \x01(\r\x12\x19\n\x11\x61\x63tivation_height\x18\x08 \x01(\r\x12\x19\n\x11\x65xpiration_height\x18\t \x01(\r\x12\x19\n\x11\x63laims_in_channel\x18\n \x01(\r\x12\x10\n\x08reposted\x18\x0b \x01(\r\x12\x18\n\x10\x65\x66\x66\x65\x63tive_amount\x18\x14 \x01(\x04\x12\x16\n\x0esupport_amount\x18\x15 \x01(\x04\x12\x16\n\x0etrending_score\x18\x16 \x01(\x01\"\x94\x01\n\x05\x45rror\x12\x1c\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x0e.pb.Error.Code\x12\x0c\n\x04text\x18\x02 \x01(\t\x12\x1c\n\x07\x62locked\x18\x03 \x01(\x0b\x32\x0b.pb.Blocked\"A\n\x04\x43ode\x12\x10\n\x0cUNKNOWN_CODE\x10\x00\x12\r\n\tNOT_FOUND\x10\x01\x12\x0b\n\x07INVALID\x10\x02\x12\x0b\n\x07\x42LOCKED\x10\x03\"5\n\x07\x42locked\x12\r\n\x05\x63ount\x18\x01 \x01(\r\x12\x1b\n\x07\x63hannel\x18\x02 \x01(\x0b\x32\n.pb.OutputB,Z*github.com/lbryio/herald.go/protobuf/go/pbb\x06proto3')
_OUTPUTS = DESCRIPTOR.message_types_by_name['Outputs']
_OUTPUT = DESCRIPTOR.message_types_by_name['Output']
_CLAIMMETA = DESCRIPTOR.message_types_by_name['ClaimMeta']
_ERROR = DESCRIPTOR.message_types_by_name['Error']
_BLOCKED = DESCRIPTOR.message_types_by_name['Blocked']
_ERROR_CODE = _ERROR.enum_types_by_name['Code']
Outputs = _reflection.GeneratedProtocolMessageType('Outputs', (_message.Message,), {
'DESCRIPTOR' : _OUTPUTS,
'__module__' : 'result_pb2'
# @@protoc_insertion_point(class_scope:pb.Outputs)
})
_sym_db.RegisterMessage(Outputs)
Output = _reflection.GeneratedProtocolMessageType('Output', (_message.Message,), {
'DESCRIPTOR' : _OUTPUT,
'__module__' : 'result_pb2'
# @@protoc_insertion_point(class_scope:pb.Output)
})
_sym_db.RegisterMessage(Output)
ClaimMeta = _reflection.GeneratedProtocolMessageType('ClaimMeta', (_message.Message,), {
'DESCRIPTOR' : _CLAIMMETA,
'__module__' : 'result_pb2'
# @@protoc_insertion_point(class_scope:pb.ClaimMeta)
})
_sym_db.RegisterMessage(ClaimMeta)
Error = _reflection.GeneratedProtocolMessageType('Error', (_message.Message,), {
'DESCRIPTOR' : _ERROR,
'__module__' : 'result_pb2'
# @@protoc_insertion_point(class_scope:pb.Error)
})
_sym_db.RegisterMessage(Error)
Blocked = _reflection.GeneratedProtocolMessageType('Blocked', (_message.Message,), {
'DESCRIPTOR' : _BLOCKED,
'__module__' : 'result_pb2'
# @@protoc_insertion_point(class_scope:pb.Blocked)
})
_sym_db.RegisterMessage(Blocked)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'Z*github.com/lbryio/herald.go/protobuf/go/pb'
_OUTPUTS._serialized_start=21
_OUTPUTS._serialized_end=172
_OUTPUT._serialized_start=174
_OUTPUT._serialized_end=297
_CLAIMMETA._serialized_start=300
_CLAIMMETA._serialized_end=658
_ERROR._serialized_start=661
_ERROR._serialized_end=809
_ERROR_CODE._serialized_start=744
_ERROR_CODE._serialized_end=809
_BLOCKED._serialized_start=811
_BLOCKED._serialized_end=864
# @@protoc_insertion_point(module_scope)

View file

@ -0,0 +1,4 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc

View file

@ -1,17 +1,16 @@
# LBRY Hub # LBRY Herald
A hub provides back-end services to LBRY clients. Services include herald.go is a not yet feature complete go rewrite of the [existing implementation in python](https://github.com/lbryio/hub/tree/master/hub/herald). A herald server provides back-end services to LBRY clients. Services include
- URL resolution - URL resolution
- search - search
- wallet services (getting and sending transactions, address subscription)
- hub federation and discovery - hub federation and discovery
This project will eventually subsume and replace the This project will eventually subsume and replace the
[wallet server](https://github.com/lbryio/lbry-sdk/blob/v0.92.0/docker/Dockerfile.wallet_server) [herald](https://github.com/lbryio/hub/blob/master/docs/docker_examples/hub-compose.yml#L38)
and the [lighthouse](https://github.com/lbryio/lighthouse) search provider. and the [lighthouse](https://github.com/lbryio/lighthouse) search provider.
![](./diagram.png) ![](https://raw.githubusercontent.com/lbryio/hub/master/docs/diagram.png)
## Installation ## Installation
@ -26,13 +25,18 @@ Follow the instructions [here](https://lbry.tech/resources/wallet-server).
### Run this hub ### Run this hub
```bash ```bash
./hub serve ./herald serve
```
```bash
# run with remote services disabled so it can run completely solo
./herald serve --disable-rocksdb-refresh --disable-load-peers --disable-resolve --disable-es --disable-blocking-and-filtering
``` ```
### Search for stuff ### Search for stuff
```bash ```bash
./hub search text goes here ./herald search text goes here
``` ```
## Contributing ## Contributing
@ -41,9 +45,9 @@ Contributions to this project are welcome, encouraged, and compensated. Details
### Dev Dependencies ### Dev Dependencies
Install Go 1.14+ Install Go 1.18+
- Ubuntu: `sudo add-apt-repository ppa:longsleep/golang-backports && sudo apt install golang-go` - Ubuntu: `sudo snap install go`
- OSX: `brew install go` - OSX: `brew install go`
- Windows https://golang.org/doc/install - Windows https://golang.org/doc/install
@ -59,12 +63,38 @@ pip install grpcio grpcio-tools github3.py
Lastly the hub needs protobuf version 3.17.1, it may work with newer version but this is what it's built with, on ubuntu systems you'll have to install this from source see the GitHub actions in `.github/workflows` for an example of this. Lastly the hub needs protobuf version 3.17.1, it may work with newer version but this is what it's built with, on ubuntu systems you'll have to install this from source see the GitHub actions in `.github/workflows` for an example of this.
Install rocksdb and dependencies your CGO flags, on ubuntu. We use v6.29.5 for feature and statis build support.
```
sudo apt-get install libgflags-dev libsnappy-dev zlib1g-dev libbz2-dev libzstd-dev liblz4-dev
wget https://github.com/facebook/rocksdb/archive/refs/tags/v6.29.5.tar.gz
tar xfzv rocksdb-6.29.5.tar.gz
cd rocksdb-6.29.5
make static_lib
sudo make install
export CGO_CFLAGS="-I/usr/local/lib"
export CGO_LDFLAGS="-L/usr/local/lib -lrocksdb -lstdc++ -lm -lz -lsnappy -llz4 -lzstd -lbz2"
```
``` ```
https://github.com/protocolbuffers/protobuf/releases/download/v3.17.1/protobuf-all-3.17.1.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/v3.17.1/protobuf-all-3.17.1.tar.gz
``` ```
If you can run `./protobuf/build.sh` without errors, you have `go` and `protoc` installed correctly. If you can run `./protobuf/build.sh` without errors, you have `go` and `protoc` installed correctly.
On Linux you probably need to instead the open file limits
```
ulimit -n 1000000
sysctl -w fs.file-max=1000000
```
and `/etc/security/limits.conf` or `/etc/sysctl.conf` change:
```
fs.file-max = 1000000
```
Finally, run the block processor as described under Usage. Finally, run the block processor as described under Usage.
### Running from Source ### Running from Source

16
requirements.txt Normal file
View file

@ -0,0 +1,16 @@
certifi==2022.6.15
cffi==1.15.1
charset-normalizer==2.1.0
cryptography==37.0.4
github3.py==3.2.0
grpcio==1.47.0
grpcio-tools==1.47.0
idna==3.3
protobuf==3.20.1
pycparser==2.21
PyJWT==2.4.0
python-dateutil==2.8.2
requests==2.28.1
six==1.16.0
uritemplate==4.1.1
urllib3==1.26.11

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
IMPORT_PATH="github.com/lbryio/hub" IMPORT_PATH="github.com/lbryio/herald.go"
function print_and_die() { function print_and_die() {
echo "$1" echo "$1"
@ -21,8 +21,9 @@ echo "using tag $LATEST_TAG"
# Checkout latest tag # Checkout latest tag
git checkout "$LATEST_TAG" git checkout "$LATEST_TAG"
CGO_ENABLED=0 go build -v -ldflags "-X ${IMPORT_PATH}/meta.Version=${VERSION}" # CGO_ENABLED=0 go build -v -ldflags "-X ${IMPORT_PATH}/meta.Version=${VERSION}"
docker build . -t lbry/hub:latest go build -o herald -v -ldflags "-X ${IMPORT_PATH}/meta.Version=${VERSION}"
docker tag lbry/hub:latest lbry/hub:"$LATEST_TAG" docker build . -t lbry/herald.go:latest
docker push lbry/hub:latest docker tag lbry/herald.go:latest lbry/herald.go:"$LATEST_TAG"
docker push lbry/hub:"$LATEST_TAG" docker push lbry/herald.go:latest
docker push lbry/herald.go:"$LATEST_TAG"

5
scripts/build_and_test.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
./protobuf/build.sh
go version
go build -o herald .
go test -v -race -cover ./...

View file

@ -0,0 +1,14 @@
#!/bin/bash
#
# cicd_integration_test_runner.sh
#
# simple script to kick off herald and call the integration testing
# script
#
# N.B. this currently just works locally until we figure a way to have
# the data in the cicd environment.
#
./herald serve --db-path /mnt/sdb1/wallet_server/_data/lbry-rocksdb &
./integration_tests.sh

235
scripts/integration_tests.sh Executable file
View file

@ -0,0 +1,235 @@
#!/bin/bash
#
# integration_testing.sh
#
# GitHub Action CI/CD based integration tests for herald.go
# These are smoke / sanity tests for the server behaving correctly on a "live"
# system, and looks for reasonable response codes, not specific correct
# behavior. Those are covered in unit tests.
#
# N.B.
# For the curl based json tests the `id` field existing is needed.
#
# global variables
RES=(0)
FINALRES=0
# functions
function logical_or {
for res in ${RES[@]}; do
if [ $res -eq 1 -o $FINALRES -eq 1 ]; then
FINALRES=1
return
fi
done
}
function want_got {
if [ "${WANT}" != "${GOT}" ]; then
echo "WANT: ${WANT}"
echo "GOT: ${GOT}"
RES+=(1)
else
RES+=(0)
fi
}
function want_greater {
if [ ${WANT} -ge ${GOT} ]; then
echo "WANT: ${WANT}"
echo "GOT: ${GOT}"
RES+=(1)
else
RES+=(0)
fi
}
function test_command_with_want {
echo $CMD
GOT=`eval $CMD`
want_got
}
# grpc endpoint testing
read -r -d '' CMD <<- EOM
grpcurl -plaintext -d '{"value": ["@Styxhexenhammer666:2"]}' 127.0.0.1:50051 pb.Hub.Resolve
| jq .txos[0].txHash | sed 's/"//g'
EOM
WANT="VOFP8MQEwps9Oa5NJJQ18WfVzUzlpCjst0Wz3xyOPd4="
test_command_with_want
# GOT=`eval $CMD`
#want_got
##
## N.B. This is a degenerate case that takes a long time to run.
## The runtime should be fixed, but in the meantime, we definitely should
## ensure this behaves as expected.
##
## TODO: Test runtime doesn't exceed worst case.
##
#WANT=806389
#read -r -d '' CMD <<- EOM
# grpcurl -plaintext -d '{"value": ["foo"]}' 127.0.0.1:50051 pb.Hub.Resolve | jq .txos[0].height
#EOM
# test_command_with_want
# json rpc endpoint testing
## blockchain.block
### blockchain.block.get_chunk
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "blockchain.block.get_chunk", "params": [0]}'
| jq .result | sed 's/"//g' | head -c 100
EOM
WANT="010000000000000000000000000000000000000000000000000000000000000000000000cc59e59ff97ac092b55e423aa549"
test_command_with_want
### blockchain.block.get_header
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "blockchain.block.get_header", "params": []}'
| jq .result.timestamp
EOM
WANT=1446058291
test_command_with_want
### blockchain.block.headers
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "blockchain.block.headers", "params": []}'
| jq .result.count
EOM
WANT=0
test_command_with_want
## blockchain.claimtrie
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "blockchain.claimtrie.resolve", "params":[{"Data": ["@Styxhexenhammer666:2"]}]}'
| jq .result.txos[0].tx_hash | sed 's/"//g'
EOM
WANT="VOFP8MQEwps9Oa5NJJQ18WfVzUzlpCjst0Wz3xyOPd4="
test_command_with_want
## blockchain.address
### blockchain.address.get_balance
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "blockchain.address.get_balance", "params":[{"Address": "bGqWuXRVm5bBqLvLPEQQpvsNxJ5ubc6bwN"}]}'
| jq .result.confirmed
EOM
WANT=44415602186
test_command_with_want
## blockchain.address.get_history
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "blockchain.address.get_history", "params":[{"Address": "bGqWuXRVm5bBqLvLPEQQpvsNxJ5ubc6bwN"}]}'
| jq '.result.confirmed | length'
EOM
WANT=82
test_command_with_want
## blockchain.address.listunspent
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "blockchain.address.listunspent", "params":[{"Address": "bGqWuXRVm5bBqLvLPEQQpvsNxJ5ubc6bwN"}]}'
| jq '.result | length'
EOM
WANT=32
test_command_with_want
# blockchain.scripthash
## blockchain.scripthash.get_mempool
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "blockchain.scripthash.get_mempool", "params":[{"scripthash": "bGqWuXRVm5bBqLvLPEQQpvsNxJ5ubc6bwN"}]}'
| jq .error | sed 's/"//g'
EOM
WANT="encoding/hex: invalid byte: U+0047 'G'"
test_command_with_want
## blockchain.scripthash.get_history
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "blockchain.scripthash.get_history", "params":[{"scripthash": "bGqWuXRVm5bBqLvLPEQQpvsNxJ5ubc6bwN"}]}'
| jq .error | sed 's/"//g'
EOM
WANT="encoding/hex: invalid byte: U+0047 'G'"
test_command_with_want
## blockchain.scripthash.listunspent
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "blockchain.scripthash.listunspent", "params":[{"scripthash": "bGqWuXRVm5bBqLvLPEQQpvsNxJ5ubc6bwN"}]}'
| jq .error | sed 's/"//g'
EOM
WANT="encoding/hex: invalid byte: U+0047 'G'"
test_command_with_want
## server.banner
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "server.banner", "params":[]}'
| jq .result | sed 's/"//g'
EOM
WANT="You are connected to an 0.107.0 server."
test_command_with_want
## server.version
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "server.version", "params":[]}'
| jq .result | sed 's/"//g'
EOM
WANT="0.107.0"
test_command_with_want
## server.features
read -r -d '' CMD <<- EOM
curl http://127.0.0.1:50002/rpc -s -H "Content-Type: application/json"
--data '{"id": 1, "method": "server.features", "params":[]}'
EOM
WANT='{"result":{"hosts":{},"pruning":"","server_version":"0.107.0","protocol_min":"0.54.0","protocol_max":"0.199.0","genesis_hash":"9c89283ba0f3227f6c03b70216b9f665f0118d5e0fa729cedf4fb34d6a34f463","description":"Herald","payment_address":"","donation_address":"","daily_fee":"1.0","hash_function":"sha256","trending_algorithm":"fast_ar"},"error":null,"id":1}'
test_command_with_want
# metrics endpoint testing
WANT=0
GOT=$(curl http://127.0.0.1:2112/metrics -s | grep requests | grep resolve | awk '{print $NF}')
want_greater
# caclulate return value
logical_or $RES
if [ $FINALRES -eq 1 ]; then
echo "Failed!"
exit 1
else
echo "Passed!"
exit 0
fi

View file

@ -25,12 +25,12 @@ AREA_RENAME = {
def build_upload_binary(release: github3.repos.release.Release) -> None: def build_upload_binary(release: github3.repos.release.Release) -> None:
# os.chdir(absolute_path) # os.chdir(absolute_path)
# os.system("go build .") # os.system("go build .")
cmd = f'CGO_ENABLED=0 go build -v -ldflags "-X github.com/lbryio/hub/meta.Version={release.name}"' cmd = f'go build -o herald -v -ldflags "-X github.com/lbryio/herald.go/meta.Version={release.name}"'
print(cmd) print(cmd)
# os.system("go build .") # os.system("go build .")
os.system(cmd) os.system(cmd)
with open("./hub", "rb") as f: with open("./herald", "rb") as f:
release.upload_asset("binary", "hub", f) release.upload_asset("binary", "herald", f)
def get_github(): def get_github():
@ -152,7 +152,7 @@ def get_draft_prerelease_vars(args) -> (bool, bool):
def release(args): def release(args):
gh = get_github() gh = get_github()
repo = gh.repository('lbryio', 'hub') repo = gh.repository('lbryio', 'herald.go')
try: try:
version_file = repo.file_contents('version.txt') version_file = repo.file_contents('version.txt')
current_version = Version.from_content(version_file) current_version = Version.from_content(version_file)
@ -185,7 +185,7 @@ def release(args):
unlabeled = [] unlabeled = []
fixups = [] fixups = []
areas = {} areas = {}
for pr in gh.search_issues(f"merged:>={previous_release._json_data['created_at']} repo:lbryio/hub"): for pr in gh.search_issues(f"merged:>={previous_release._json_data['created_at']} repo:lbryio/herald.go"):
area_labels = list(get_labels(pr, 'area')) area_labels = list(get_labels(pr, 'area'))
type_label = get_label(pr, 'type') type_label = get_label(pr, 'type')
if area_labels and type_label: if area_labels and type_label:

View file

@ -1,59 +1,184 @@
package server package server
import ( import (
"fmt"
"log" "log"
"net/url"
"os" "os"
"strconv"
"strings" "strings"
"github.com/akamensky/argparse" "github.com/akamensky/argparse"
pb "github.com/lbryio/hub/protobuf/go" pb "github.com/lbryio/herald.go/protobuf/go"
"github.com/lbryio/lbcd/chaincfg"
) )
const ( const (
ServeCmd = iota ServeCmd = iota
SearchCmd = iota SearchCmd = iota
DBCmd = iota
) )
// Args struct contains the arguments to the hub server. // Args struct contains the arguments to the hub server.
type Args struct { type Args struct {
CmdType int CmdType int
Host string Host string
Port string Port int
UDPPort string DBPath string
EsHost string Chain *string
EsPort string DaemonURL *url.URL
PrometheusPort string DaemonCAPath string
EsIndex string EsHost string
RefreshDelta int EsPort int
CacheTTL int PrometheusPort int
PeerFile string NotifierPort int
Country string JSONRPCPort int
DisableEs bool JSONRPCHTTPPort int
Debug bool MaxSessions int
LoadPeers bool SessionTimeout int
StartPrometheus bool EsIndex string
StartUDP bool RefreshDelta int
WritePeers bool CacheTTL int
PeerFile string
Banner *string
Country string
BlockingChannelIds []string
FilteringChannelIds []string
GenesisHash string
ServerVersion string
ProtocolMin string
ProtocolMax string
ServerDescription string
PaymentAddress string
DonationAddress string
DailyFee string
Debug bool
DisableEs bool
DisableLoadPeers bool
DisableStartPrometheus bool
DisableStartUDP bool
DisableWritePeers bool
DisableFederation bool
DisableRocksDBRefresh bool
DisableResolve bool
DisableBlockingAndFiltering bool
DisableStartNotifier bool
DisableStartJSONRPC bool
} }
const ( const (
DefaultHost = "0.0.0.0" DefaultHost = "0.0.0.0"
DefaultPort = "50051" DefaultPort = 50051
DefaultUdpPort = "41119" DefaultDBPath = "/mnt/d/data/snapshot_1072108/lbry-rocksdb/" // FIXME
DefaultEsHost = "http://localhost" DefaultEsHost = "http://localhost"
DefaultEsIndex = "claims" DefaultEsIndex = "claims"
DefaultEsPort = "9200" DefaultEsPort = 9200
DefaultPrometheusPort = "2112" DefaultPrometheusPort = 2112
DefaultNotifierPort = 18080
DefaultJSONRPCPort = 50001
DefaultJSONRPCHTTPPort = 50002
DefaultMaxSessions = 10000
DefaultSessionTimeout = 300
DefaultRefreshDelta = 5 DefaultRefreshDelta = 5
DefaultCacheTTL = 5 DefaultCacheTTL = 5
DefaultPeerFile = "peers.txt" DefaultPeerFile = "peers.txt"
DefaultBannerFile = ""
DefaultCountry = "US" DefaultCountry = "US"
DefaultLoadPeers = true
DefaultStartPrometheus = true HUB_PROTOCOL_VERSION = "0.107.0"
DefaultStartUDP = true PROTOCOL_MIN = "0.54.0"
DefaultWritePeers = true PROTOCOL_MAX = "0.199.0"
DefaultServerDescription = "Herald"
DefaultPaymentAddress = ""
DefaultDonationAddress = ""
DefaultDailyFee = "1.0"
DefaultDisableLoadPeers = false
DefaultDisableStartPrometheus = false
DefaultDisableStartUDP = false
DefaultDisableWritePeers = false
DefaultDisableFederation = false
DefaultDisableRockDBRefresh = false
DefaultDisableResolve = false
DefaultDisableBlockingAndFiltering = false
DisableStartNotifier = false
DisableStartJSONRPC = false
) )
var (
DefaultBlockingChannelIds = []string{}
DefaultFilteringChannelIds = []string{}
)
func loadBanner(bannerFile *string, serverVersion string) *string {
var banner string
data, err := os.ReadFile(*bannerFile)
if err != nil {
banner = fmt.Sprintf("You are connected to an %s server.", serverVersion)
} else {
banner = string(data)
}
/*
banner := os.Getenv("BANNER")
if banner == "" {
return nil
}
*/
return &banner
}
// MakeDefaultArgs creates a default set of arguments for testing the server.
func MakeDefaultTestArgs() *Args {
args := &Args{
CmdType: ServeCmd,
Host: DefaultHost,
Port: DefaultPort,
DBPath: DefaultDBPath,
EsHost: DefaultEsHost,
EsPort: DefaultEsPort,
PrometheusPort: DefaultPrometheusPort,
NotifierPort: DefaultNotifierPort,
JSONRPCPort: DefaultJSONRPCPort,
JSONRPCHTTPPort: DefaultJSONRPCHTTPPort,
MaxSessions: DefaultMaxSessions,
SessionTimeout: DefaultSessionTimeout,
EsIndex: DefaultEsIndex,
RefreshDelta: DefaultRefreshDelta,
CacheTTL: DefaultCacheTTL,
PeerFile: DefaultPeerFile,
Banner: nil,
Country: DefaultCountry,
GenesisHash: chaincfg.TestNet3Params.GenesisHash.String(),
ServerVersion: HUB_PROTOCOL_VERSION,
ProtocolMin: PROTOCOL_MIN,
ProtocolMax: PROTOCOL_MAX,
ServerDescription: DefaultServerDescription,
PaymentAddress: DefaultPaymentAddress,
DonationAddress: DefaultDonationAddress,
DailyFee: DefaultDailyFee,
DisableEs: true,
Debug: true,
DisableLoadPeers: true,
DisableStartPrometheus: true,
DisableStartUDP: true,
DisableWritePeers: true,
DisableRocksDBRefresh: true,
DisableResolve: true,
DisableBlockingAndFiltering: true,
DisableStartNotifier: true,
DisableStartJSONRPC: true,
}
return args
}
// GetEnvironment takes the environment variables as an array of strings // GetEnvironment takes the environment variables as an array of strings
// and a getkeyval function to turn it into a map. // and a getkeyval function to turn it into a map.
func GetEnvironment(data []string, getkeyval func(item string) (key, val string)) map[string]string { func GetEnvironment(data []string, getkeyval func(item string) (key, val string)) map[string]string {
@ -79,30 +204,72 @@ func GetEnvironmentStandard() map[string]string {
func ParseArgs(searchRequest *pb.SearchRequest) *Args { func ParseArgs(searchRequest *pb.SearchRequest) *Args {
environment := GetEnvironmentStandard() environment := GetEnvironmentStandard()
parser := argparse.NewParser("hub", "hub server and client") parser := argparse.NewParser("herald", "herald server and client")
serveCmd := parser.NewCommand("serve", "start the hub server") serveCmd := parser.NewCommand("serve", "start the herald server")
searchCmd := parser.NewCommand("search", "claim search") searchCmd := parser.NewCommand("search", "claim search")
dbCmd := parser.NewCommand("db", "db testing")
defaultDaemonURL := "http://localhost:9245"
if url, ok := environment["DAEMON_URL"]; ok {
defaultDaemonURL = url
}
validateURL := func(arg []string) error {
_, err := url.Parse(arg[0])
return err
}
validatePort := func(arg []string) error {
_, err := strconv.ParseUint(arg[0], 10, 16)
return err
}
// main server config arguments
host := parser.String("", "rpchost", &argparse.Options{Required: false, Help: "RPC host", Default: DefaultHost}) host := parser.String("", "rpchost", &argparse.Options{Required: false, Help: "RPC host", Default: DefaultHost})
port := parser.String("", "rpcport", &argparse.Options{Required: false, Help: "RPC port", Default: DefaultPort}) port := parser.Int("", "rpcport", &argparse.Options{Required: false, Help: "RPC port", Validate: validatePort, Default: DefaultPort})
dbPath := parser.String("", "db-path", &argparse.Options{Required: false, Help: "RocksDB path", Default: DefaultDBPath})
chain := parser.Selector("", "chain", []string{chaincfg.MainNetParams.Name, chaincfg.TestNet3Params.Name, chaincfg.RegressionNetParams.Name, "testnet"},
&argparse.Options{Required: false, Help: "Which chain to use, default is 'mainnet'. Values 'regtest' and 'testnet' are for testing", Default: chaincfg.MainNetParams.Name})
daemonURLStr := parser.String("", "daemon-url", &argparse.Options{Required: false, Help: "URL for rpc to lbrycrd or lbcd, <rpcuser>:<rpcpassword>@<lbcd rpc ip><lbrcd rpc port>.", Validate: validateURL, Default: defaultDaemonURL})
daemonCAPath := parser.String("", "daemon-ca-path", &argparse.Options{Required: false, Help: "Path to the lbcd CA file. Use SSL certificate to verify connection to lbcd."})
esHost := parser.String("", "eshost", &argparse.Options{Required: false, Help: "elasticsearch host", Default: DefaultEsHost}) esHost := parser.String("", "eshost", &argparse.Options{Required: false, Help: "elasticsearch host", Default: DefaultEsHost})
esPort := parser.String("", "esport", &argparse.Options{Required: false, Help: "elasticsearch port", Default: DefaultEsPort}) esPort := parser.Int("", "esport", &argparse.Options{Required: false, Help: "elasticsearch port", Default: DefaultEsPort})
udpPort := parser.String("", "uspport", &argparse.Options{Required: false, Help: "udp ping port", Default: DefaultUdpPort}) prometheusPort := parser.Int("", "prometheus-port", &argparse.Options{Required: false, Help: "prometheus port", Default: DefaultPrometheusPort})
prometheusPort := parser.String("", "prometheus-port", &argparse.Options{Required: false, Help: "prometheus port", Default: DefaultPrometheusPort}) notifierPort := parser.Int("", "notifier-port", &argparse.Options{Required: false, Help: "notifier port", Default: DefaultNotifierPort})
jsonRPCPort := parser.Int("", "json-rpc-port", &argparse.Options{Required: false, Help: "JSON RPC port", Validate: validatePort, Default: DefaultJSONRPCPort})
jsonRPCHTTPPort := parser.Int("", "json-rpc-http-port", &argparse.Options{Required: false, Help: "JSON RPC over HTTP port", Validate: validatePort, Default: DefaultJSONRPCHTTPPort})
maxSessions := parser.Int("", "max-sessions", &argparse.Options{Required: false, Help: "Maximum number of electrum clients that can be connected", Default: DefaultMaxSessions})
sessionTimeout := parser.Int("", "session-timeout", &argparse.Options{Required: false, Help: "Session inactivity timeout (seconds)", Default: DefaultSessionTimeout})
esIndex := parser.String("", "esindex", &argparse.Options{Required: false, Help: "elasticsearch index name", Default: DefaultEsIndex}) esIndex := parser.String("", "esindex", &argparse.Options{Required: false, Help: "elasticsearch index name", Default: DefaultEsIndex})
refreshDelta := parser.Int("", "refresh-delta", &argparse.Options{Required: false, Help: "elasticsearch index refresh delta in seconds", Default: DefaultRefreshDelta}) refreshDelta := parser.Int("", "refresh-delta", &argparse.Options{Required: false, Help: "elasticsearch index refresh delta in seconds", Default: DefaultRefreshDelta})
cacheTTL := parser.Int("", "cachettl", &argparse.Options{Required: false, Help: "Cache TTL in minutes", Default: DefaultCacheTTL}) cacheTTL := parser.Int("", "cachettl", &argparse.Options{Required: false, Help: "Cache TTL in minutes", Default: DefaultCacheTTL})
peerFile := parser.String("", "peerfile", &argparse.Options{Required: false, Help: "Initial peer file for federation", Default: DefaultPeerFile}) peerFile := parser.String("", "peerfile", &argparse.Options{Required: false, Help: "Initial peer file for federation", Default: DefaultPeerFile})
bannerFile := parser.String("", "bannerfile", &argparse.Options{Required: false, Help: "Banner file server.banner", Default: DefaultBannerFile})
country := parser.String("", "country", &argparse.Options{Required: false, Help: "Country this node is running in. Default US.", Default: DefaultCountry}) country := parser.String("", "country", &argparse.Options{Required: false, Help: "Country this node is running in. Default US.", Default: DefaultCountry})
blockingChannelIds := parser.StringList("", "blocking-channel-ids", &argparse.Options{Required: false, Help: "Blocking channel ids", Default: DefaultBlockingChannelIds})
filteringChannelIds := parser.StringList("", "filtering-channel-ids", &argparse.Options{Required: false, Help: "Filtering channel ids", Default: DefaultFilteringChannelIds})
// arguments for server features
serverDescription := parser.String("", "server-description", &argparse.Options{Required: false, Help: "Server description", Default: DefaultServerDescription})
paymentAddress := parser.String("", "payment-address", &argparse.Options{Required: false, Help: "Payment address", Default: DefaultPaymentAddress})
donationAddress := parser.String("", "donation-address", &argparse.Options{Required: false, Help: "Donation address", Default: DefaultDonationAddress})
dailyFee := parser.String("", "daily-fee", &argparse.Options{Required: false, Help: "Daily fee", Default: DefaultDailyFee})
// flags for disabling features
debug := parser.Flag("", "debug", &argparse.Options{Required: false, Help: "enable debug logging", Default: false}) debug := parser.Flag("", "debug", &argparse.Options{Required: false, Help: "enable debug logging", Default: false})
disableEs := parser.Flag("", "disable-es", &argparse.Options{Required: false, Help: "Disable elastic search, for running/testing independently", Default: false}) disableEs := parser.Flag("", "disable-es", &argparse.Options{Required: false, Help: "Disable elastic search, for running/testing independently", Default: false})
loadPeers := parser.Flag("", "load-peers", &argparse.Options{Required: false, Help: "load peers from disk at startup", Default: DefaultLoadPeers}) disableLoadPeers := parser.Flag("", "disable-load-peers", &argparse.Options{Required: false, Help: "Disable load peers from disk at startup", Default: DefaultDisableLoadPeers})
startPrometheus := parser.Flag("", "start-prometheus", &argparse.Options{Required: false, Help: "Start prometheus server", Default: DefaultStartPrometheus}) disableStartPrometheus := parser.Flag("", "disable-start-prometheus", &argparse.Options{Required: false, Help: "Disable start prometheus server", Default: DefaultDisableStartPrometheus})
startUdp := parser.Flag("", "start-udp", &argparse.Options{Required: false, Help: "Start UDP ping server", Default: DefaultStartUDP}) disableStartUdp := parser.Flag("", "disable-start-udp", &argparse.Options{Required: false, Help: "Disable start UDP ping server", Default: DefaultDisableStartUDP})
writePeers := parser.Flag("", "write-peers", &argparse.Options{Required: false, Help: "Write peer to disk as we learn about them", Default: DefaultWritePeers}) disableWritePeers := parser.Flag("", "disable-write-peers", &argparse.Options{Required: false, Help: "Disable write peer to disk as we learn about them", Default: DefaultDisableWritePeers})
disableFederation := parser.Flag("", "disable-federation", &argparse.Options{Required: false, Help: "Disable server federation", Default: DefaultDisableFederation})
disableRocksDBRefresh := parser.Flag("", "disable-rocksdb-refresh", &argparse.Options{Required: false, Help: "Disable rocksdb refreshing", Default: DefaultDisableRockDBRefresh})
disableResolve := parser.Flag("", "disable-resolve", &argparse.Options{Required: false, Help: "Disable resolve endpoint (and rocksdb loading)", Default: DefaultDisableRockDBRefresh})
disableBlockingAndFiltering := parser.Flag("", "disable-blocking-and-filtering", &argparse.Options{Required: false, Help: "Disable blocking and filtering of channels and streams", Default: DefaultDisableBlockingAndFiltering})
disableStartNotifier := parser.Flag("", "disable-start-notifier", &argparse.Options{Required: false, Help: "Disable start notifier", Default: DisableStartNotifier})
disableStartJSONRPC := parser.Flag("", "disable-start-jsonrpc", &argparse.Options{Required: false, Help: "Disable start jsonrpc endpoint", Default: DisableStartJSONRPC})
// search command arguments
text := parser.String("", "text", &argparse.Options{Required: false, Help: "text query"}) text := parser.String("", "text", &argparse.Options{Required: false, Help: "text query"})
name := parser.String("", "name", &argparse.Options{Required: false, Help: "name"}) name := parser.String("", "name", &argparse.Options{Required: false, Help: "name"})
claimType := parser.String("", "claim_type", &argparse.Options{Required: false, Help: "claim_type"}) claimType := parser.String("", "claim_type", &argparse.Options{Required: false, Help: "claim_type"})
@ -119,25 +286,64 @@ func ParseArgs(searchRequest *pb.SearchRequest) *Args {
log.Fatalln(parser.Usage(err)) log.Fatalln(parser.Usage(err))
} }
// Use default JSON RPC port only if *neither* JSON RPC arg is specified.
if *jsonRPCPort == 0 && *jsonRPCHTTPPort == 0 {
*jsonRPCPort = DefaultJSONRPCPort
}
daemonURL, err := url.Parse(*daemonURLStr)
if err != nil {
log.Fatalf("URL parse failed: %v", err)
}
banner := loadBanner(bannerFile, HUB_PROTOCOL_VERSION)
args := &Args{ args := &Args{
CmdType: SearchCmd, CmdType: SearchCmd,
Host: *host, Host: *host,
Port: *port, Port: *port,
EsHost: *esHost, DBPath: *dbPath,
EsPort: *esPort, Chain: chain,
UDPPort: *udpPort, DaemonURL: daemonURL,
PrometheusPort: *prometheusPort, DaemonCAPath: *daemonCAPath,
EsIndex: *esIndex, EsHost: *esHost,
RefreshDelta: *refreshDelta, EsPort: *esPort,
CacheTTL: *cacheTTL, PrometheusPort: *prometheusPort,
PeerFile: *peerFile, NotifierPort: *notifierPort,
Country: *country, JSONRPCPort: *jsonRPCPort,
DisableEs: *disableEs, JSONRPCHTTPPort: *jsonRPCHTTPPort,
Debug: *debug, MaxSessions: *maxSessions,
LoadPeers: *loadPeers, SessionTimeout: *sessionTimeout,
StartPrometheus: *startPrometheus, EsIndex: *esIndex,
StartUDP: *startUdp, RefreshDelta: *refreshDelta,
WritePeers: *writePeers, CacheTTL: *cacheTTL,
PeerFile: *peerFile,
Banner: banner,
Country: *country,
BlockingChannelIds: *blockingChannelIds,
FilteringChannelIds: *filteringChannelIds,
GenesisHash: "",
ServerVersion: HUB_PROTOCOL_VERSION,
ProtocolMin: PROTOCOL_MIN,
ProtocolMax: PROTOCOL_MAX,
ServerDescription: *serverDescription,
PaymentAddress: *paymentAddress,
DonationAddress: *donationAddress,
DailyFee: *dailyFee,
Debug: *debug,
DisableEs: *disableEs,
DisableLoadPeers: *disableLoadPeers,
DisableStartPrometheus: *disableStartPrometheus,
DisableStartUDP: *disableStartUdp,
DisableWritePeers: *disableWritePeers,
DisableFederation: *disableFederation,
DisableRocksDBRefresh: *disableRocksDBRefresh,
DisableResolve: *disableResolve,
DisableBlockingAndFiltering: *disableBlockingAndFiltering,
DisableStartNotifier: *disableStartNotifier,
DisableStartJSONRPC: *disableStartJSONRPC,
} }
if esHost, ok := environment["ELASTIC_HOST"]; ok { if esHost, ok := environment["ELASTIC_HOST"]; ok {
@ -149,15 +355,21 @@ func ParseArgs(searchRequest *pb.SearchRequest) *Args {
} }
if esPort, ok := environment["ELASTIC_PORT"]; ok { if esPort, ok := environment["ELASTIC_PORT"]; ok {
args.EsPort = esPort args.EsPort, err = strconv.Atoi(esPort)
if err != nil {
log.Fatal(err)
}
} }
if prometheusPort, ok := environment["GOHUB_PROMETHEUS_PORT"]; ok { if prometheusPort, ok := environment["GOHUB_PROMETHEUS_PORT"]; ok {
args.PrometheusPort = prometheusPort args.PrometheusPort, err = strconv.Atoi(prometheusPort)
if err != nil {
log.Fatal(err)
}
} }
/* /*
Verify no invalid argument combinations Verify no invalid argument combinations
*/ */
if len(*channelIds) > 0 && *channelId != "" { if len(*channelIds) > 0 && *channelId != "" {
log.Fatal("Cannot specify both channel_id and channel_ids") log.Fatal("Cannot specify both channel_id and channel_ids")
@ -167,6 +379,8 @@ func ParseArgs(searchRequest *pb.SearchRequest) *Args {
args.CmdType = ServeCmd args.CmdType = ServeCmd
} else if searchCmd.Happened() { } else if searchCmd.Happened() {
args.CmdType = SearchCmd args.CmdType = SearchCmd
} else if dbCmd.Happened() {
args.CmdType = DBCmd
} }
if *text != "" { if *text != "" {
@ -198,4 +412,4 @@ func ParseArgs(searchRequest *pb.SearchRequest) *Args {
} }
return args return args
} }

35
server/args_test.go Normal file
View file

@ -0,0 +1,35 @@
package server
import (
"os"
"testing"
pb "github.com/lbryio/herald.go/protobuf/go"
)
// TestParseArgs
func TestParseArgs(t *testing.T) {
tests := []struct {
name string
want bool
}{
{
name: "Correctly disables elastic search",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
os.Args = []string{"serve", "--disable-es"}
searchRequest := new(pb.SearchRequest)
args := ParseArgs(searchRequest)
got := args.DisableEs
if got != tt.want {
t.Errorf("flags: got: %v, want: %v\n", got, tt.want)
}
})
}
}

View file

@ -3,42 +3,46 @@ package server
import ( import (
"bufio" "bufio"
"context" "context"
"log" "math"
"net"
"os" "os"
"strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/lbryio/hub/internal/metrics" "github.com/lbryio/herald.go/internal/metrics"
pb "github.com/lbryio/hub/protobuf/go" pb "github.com/lbryio/herald.go/protobuf/go"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
) )
// FederatedServer hold relevant information about peers that we known about. // Peer holds relevant information about peers that we know about.
type FederatedServer struct { type Peer struct {
Address string Address string
Port string Port string
Ts time.Time LastSeen time.Time
} }
var ( var (
localHosts = map[string]bool{ localHosts = map[string]bool{
"127.0.0.1": true, "127.0.0.1": true,
"0.0.0.0": true, "0.0.0.0": true,
"localhost": true, "localhost": true,
"<nil>": true, // Empty net.IP turned into a string
} }
) )
// peerKey takes a peer and returns the key that for that peer
// peerKey takes a ServerMessage object and returns the key that for that peer
// in our peer table. // in our peer table.
func peerKey(msg *pb.ServerMessage) string { func peerKey(peer *Peer) string {
return msg.Address + ":" + msg.Port return peer.Address + ":" + peer.Port
} }
// peerKey is a function on a FederatedServer struct to return the key for that // peerKey is a function on a FederatedServer struct to return the key for that
// peer is out peer table. // peer is out peer table.
func (peer *FederatedServer) peerKey() string { func (peer *Peer) peerKey() string {
return peer.Address + ":" + peer.Port return peer.Address + ":" + peer.Port
} }
@ -66,16 +70,57 @@ func (s *Server) getNumSubs() int64 {
return *s.NumPeerSubs return *s.NumPeerSubs
} }
// getAndSetExternalIp detects the server's external IP and stores it.
func (s *Server) getAndSetExternalIp(ip, port string) error {
pong, err := UDPPing(ip, port)
if err != nil {
return err
}
myIp := pong.DecodeAddress()
log.Println("my ip: ", myIp)
s.ExternalIP = myIp
return nil
}
// loadPeers takes the arguments given to the hub at startup and loads the // loadPeers takes the arguments given to the hub at startup and loads the
// previously known peers from disk and verifies their existence before // previously known peers from disk and verifies their existence before
// storing them as known peers. Returns a map of peerKey -> object // storing them as known peers. Returns a map of peerKey -> object
func (s *Server) loadPeers() error { func (s *Server) loadPeers() error {
peerFile := s.Args.PeerFile peerFile := s.Args.PeerFile
port := s.Args.Port port := strconv.Itoa(s.Args.Port)
// First we make sure our server has come up, so we can answer back to peers.
var failures = 0
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
// log.Println("loadPeers #### waiting for server to come up")
retry:
time.Sleep(time.Second * time.Duration(math.Pow(float64(failures), 2)))
conn, err := grpc.DialContext(ctx,
"0.0.0.0:"+port,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
)
if err != nil {
if failures > 3 {
log.Println("Warning! Our endpoint doesn't seem to have come up, didn't load peers")
return err
}
failures += 1
goto retry
}
if err = conn.Close(); err != nil {
log.Println(err)
}
cancel()
// log.Println("loadPeers #### Past checking for server to come up")
f, err := os.Open(peerFile) f, err := os.Open(peerFile)
if err != nil { if err != nil {
log.Println(err) // log.Println(err)
return err return err
} }
scanner := bufio.NewScanner(f) scanner := bufio.NewScanner(f)
@ -90,26 +135,30 @@ func (s *Server) loadPeers() error {
} }
for _, line := range text { for _, line := range text {
ipPort := strings.Split(line,":") ipPort := strings.Split(line, ":")
if len(ipPort) != 2 { if len(ipPort) != 2 {
log.Println("Malformed entry in peer file") log.Println("Malformed entry in peer file")
continue continue
} }
// If the peer is us, skip // If the peer is us, skip
log.Println(ipPort) log.Println(ipPort)
if ipPort[1] == port && localHosts[ipPort[0]] { if ipPort[1] == port &&
(localHosts[ipPort[0]] || ipPort[0] == s.ExternalIP.String()) {
log.Println("Self peer, skipping ...") log.Println("Self peer, skipping ...")
continue continue
} }
srvMsg := &pb.ServerMessage{
Address: ipPort[0], newPeer := &Peer{
Port: ipPort[1], Address: ipPort[0],
Port: ipPort[1],
LastSeen: time.Now(),
} }
log.Printf("pinging peer %+v\n", srvMsg) log.Printf("pinging peer %+v\n", newPeer)
err := s.addPeer(srvMsg, true) err = s.addPeer(newPeer, true, true)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
} }
log.Println("Returning from loadPeers") log.Println("Returning from loadPeers")
@ -118,13 +167,13 @@ func (s *Server) loadPeers() error {
// subscribeToPeer subscribes us to a peer to we'll get updates about their // subscribeToPeer subscribes us to a peer to we'll get updates about their
// known peers. // known peers.
func (s *Server) subscribeToPeer(peer *FederatedServer) error { func (s *Server) subscribeToPeer(peer *Peer) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second) ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel() defer cancel()
conn, err := grpc.DialContext(ctx, conn, err := grpc.DialContext(ctx,
peer.Address+":"+peer.Port, peer.Address+":"+peer.Port,
grpc.WithInsecure(), grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(), grpc.WithBlock(),
) )
if err != nil { if err != nil {
@ -133,20 +182,18 @@ func (s *Server) subscribeToPeer(peer *FederatedServer) error {
defer conn.Close() defer conn.Close()
msg := &pb.ServerMessage{ msg := &pb.ServerMessage{
Address: s.Args.Host, Address: s.ExternalIP.String(),
Port: s.Args.Port, Port: strconv.Itoa(s.Args.Port),
} }
c := pb.NewHubClient(conn) c := pb.NewHubClient(conn)
log.Printf("%s:%s subscribing to %+v\n", s.Args.Host, s.Args.Port, peer) log.Printf("%s:%d subscribing to %+v\n", s.ExternalIP, s.Args.Port, peer)
_, err = c.PeerSubscribe(ctx, msg) _, err = c.PeerSubscribe(ctx, msg)
if err != nil { if err != nil {
return err return err
} }
s.Subscribed = true
return nil return nil
} }
@ -155,14 +202,14 @@ func (s *Server) subscribeToPeer(peer *FederatedServer) error {
// This is used to confirm existence of peers on start and let them // This is used to confirm existence of peers on start and let them
// know about us. Returns the response from the server on success, // know about us. Returns the response from the server on success,
// nil otherwise. // nil otherwise.
func (s *Server) helloPeer(server *FederatedServer) (*pb.HelloMessage, error) { func (s *Server) helloPeer(peer *Peer) (*pb.HelloMessage, error) {
log.Println("In helloPeer") log.Println("In helloPeer")
ctx, cancel := context.WithTimeout(context.Background(), time.Second) ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel() defer cancel()
conn, err := grpc.DialContext(ctx, conn, err := grpc.DialContext(ctx,
server.Address+":"+server.Port, peer.Address+":"+peer.Port,
grpc.WithInsecure(), grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(), grpc.WithBlock(),
) )
if err != nil { if err != nil {
@ -174,12 +221,12 @@ func (s *Server) helloPeer(server *FederatedServer) (*pb.HelloMessage, error) {
c := pb.NewHubClient(conn) c := pb.NewHubClient(conn)
msg := &pb.HelloMessage{ msg := &pb.HelloMessage{
Port: s.Args.Port, Port: strconv.Itoa(s.Args.Port),
Host: s.Args.Host, Host: s.ExternalIP.String(),
Servers: []*pb.ServerMessage{}, Servers: []*pb.ServerMessage{},
} }
log.Printf("%s:%s saying hello to %+v\n", s.Args.Host, s.Args.Port, server) log.Printf("%s:%d saying hello to %+v\n", s.ExternalIP, s.Args.Port, peer)
res, err := c.Hello(ctx, msg) res, err := c.Hello(ctx, msg)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
@ -193,7 +240,7 @@ func (s *Server) helloPeer(server *FederatedServer) (*pb.HelloMessage, error) {
// writePeers writes our current known peers to disk. // writePeers writes our current known peers to disk.
func (s *Server) writePeers() { func (s *Server) writePeers() {
if !s.Args.WritePeers { if s.Args.DisableWritePeers {
return return
} }
f, err := os.Create(s.Args.PeerFile) f, err := os.Create(s.Args.PeerFile)
@ -203,7 +250,7 @@ func (s *Server) writePeers() {
} }
writer := bufio.NewWriter(f) writer := bufio.NewWriter(f)
for key, _ := range s.PeerServers { for key := range s.PeerServers {
line := key + "\n" line := key + "\n"
_, err := writer.WriteString(line) _, err := writer.WriteString(line)
if err != nil { if err != nil {
@ -222,14 +269,17 @@ func (s *Server) writePeers() {
} }
// notifyPeer takes a peer to notify and a new peer we just learned about // notifyPeer takes a peer to notify and a new peer we just learned about
// and calls AddPeer on the first. // and informs the already known peer about the new peer.
func notifyPeer(peerToNotify *FederatedServer, newPeer *FederatedServer) error { func (s *Server) notifyPeer(peerToNotify *Peer, newPeer *Peer) error {
if s.Args.DisableFederation {
return nil
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second) ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel() defer cancel()
conn, err := grpc.DialContext(ctx, conn, err := grpc.DialContext(ctx,
peerToNotify.Address+":"+peerToNotify.Port, peerToNotify.Address+":"+peerToNotify.Port,
grpc.WithInsecure(), grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(), grpc.WithBlock(),
) )
if err != nil { if err != nil {
@ -254,12 +304,12 @@ func notifyPeer(peerToNotify *FederatedServer, newPeer *FederatedServer) error {
// notifyPeerSubs takes a new peer server we just learned about and notifies // notifyPeerSubs takes a new peer server we just learned about and notifies
// all the peers that have subscribed to us about it. // all the peers that have subscribed to us about it.
func (s *Server) notifyPeerSubs(newServer *FederatedServer) { func (s *Server) notifyPeerSubs(newPeer *Peer) {
var unsubscribe []string var unsubscribe []string
s.PeerSubsMut.RLock() s.PeerSubsMut.RLock()
for key, peer := range s.PeerSubs { for key, peer := range s.PeerSubs {
log.Printf("Notifying peer %s of new node %+v\n", key, newServer) log.Printf("Notifying peer %s of new node %+v\n", key, newPeer)
err := notifyPeer(peer, newServer) err := s.notifyPeer(peer, newPeer)
if err != nil { if err != nil {
log.Println("Failed to send data to ", key) log.Println("Failed to send data to ", key)
log.Println(err) log.Println(err)
@ -279,25 +329,36 @@ func (s *Server) notifyPeerSubs(newServer *FederatedServer) {
s.PeerSubsMut.Unlock() s.PeerSubsMut.Unlock()
} }
// addPeer takes a new peer as a pb.ServerMessage, optionally checks to see // addPeer takes a new peer, optionally checks to see if they're online, and
// if they're online, and adds them to our list of peer. If we're not currently // adds them to our list of peers. It will also optionally subscribe to it.
// subscribed to a peer, it will also subscribe to it. func (s *Server) addPeer(newPeer *Peer, ping bool, subscribe bool) error {
func (s *Server) addPeer(msg *pb.ServerMessage, ping bool) error { if s.Args.DisableFederation {
if s.Args.Port == msg.Port &&
(localHosts[msg.Address] || msg.Address == s.Args.Host) {
log.Printf("%s:%s addPeer: Self peer, skipping...\n", s.Args.Host, s.Args.Port)
return nil return nil
} }
k := peerKey(msg) // First thing we get our external ip if we don't have it, otherwise we
newServer := &FederatedServer{ // could end up subscribed to our self, which is silly.
Address: msg.Address, nilIP := net.IP{}
Port: msg.Port, localIP1 := net.IPv4(127, 0, 0, 1)
Ts: time.Now(), if s.ExternalIP.Equal(nilIP) || s.ExternalIP.Equal(localIP1) {
err := s.getAndSetExternalIp(newPeer.Address, newPeer.Port)
if err != nil {
log.Println(err)
log.Println("WARNING: can't determine external IP, continuing with ", s.Args.Host)
}
} }
log.Printf("%s:%s adding peer %+v\n", s.Args.Host, s.Args.Port, msg)
if oldServer, loaded := s.PeerServersLoadOrStore(newServer); !loaded { if strconv.Itoa(s.Args.Port) == newPeer.Port &&
(localHosts[newPeer.Address] || newPeer.Address == s.ExternalIP.String()) {
log.Printf("%s:%d addPeer: Self peer, skipping...\n", s.ExternalIP, s.Args.Port)
return nil
}
k := peerKey(newPeer)
log.Printf("%s:%d adding peer %+v\n", s.ExternalIP, s.Args.Port, newPeer)
if oldServer, loaded := s.PeerServersLoadOrStore(newPeer); !loaded {
if ping { if ping {
_, err := s.helloPeer(newServer) _, err := s.helloPeer(newPeer)
if err != nil { if err != nil {
s.PeerServersMut.Lock() s.PeerServersMut.Lock()
delete(s.PeerServers, k) delete(s.PeerServers, k)
@ -309,26 +370,35 @@ func (s *Server) addPeer(msg *pb.ServerMessage, ping bool) error {
s.incNumPeers() s.incNumPeers()
metrics.PeersKnown.Inc() metrics.PeersKnown.Inc()
s.writePeers() s.writePeers()
s.notifyPeerSubs(newServer) s.notifyPeerSubs(newPeer)
// This is weird because we're doing grpc and jsonrpc here.
// Do we still want to custom grpc?
log.Warn("Sending peer to NotifierChan")
s.NotifierChan <- peerNotification{newPeer.Address, newPeer.Port}
// Subscribe to all our peers for now // Subscribe to all our peers for now
err := s.subscribeToPeer(newServer) if subscribe {
if err != nil { err := s.subscribeToPeer(newPeer)
return err if err != nil {
} else { return err
s.Subscribed = true }
} }
} else { } else {
oldServer.Ts = time.Now() oldServer.LastSeen = time.Now()
} }
return nil return nil
} }
// mergeFederatedServers is an internal convenience function to add a list of // mergePeers is an internal convenience function to add a list of
// peers. // peers.
func (s *Server) mergeFederatedServers(servers []*pb.ServerMessage) { func (s *Server) mergePeers(servers []*pb.ServerMessage) {
for _, srvMsg := range servers { for _, srvMsg := range servers {
err := s.addPeer(srvMsg, false) newPeer := &Peer{
Address: srvMsg.Address,
Port: srvMsg.Port,
LastSeen: time.Now(),
}
err := s.addPeer(newPeer, false, true)
// This shouldn't happen because we're not pinging them. // This shouldn't happen because we're not pinging them.
if err != nil { if err != nil {
log.Println(err) log.Println(err)
@ -345,14 +415,14 @@ func (s *Server) makeHelloMessage() *pb.HelloMessage {
for _, peer := range s.PeerServers { for _, peer := range s.PeerServers {
servers = append(servers, &pb.ServerMessage{ servers = append(servers, &pb.ServerMessage{
Address: peer.Address, Address: peer.Address,
Port: peer.Port, Port: peer.Port,
}) })
} }
s.PeerServersMut.RUnlock() s.PeerServersMut.RUnlock()
return &pb.HelloMessage{ return &pb.HelloMessage{
Port: s.Args.Port, Port: strconv.Itoa(s.Args.Port),
Host: s.Args.Host, Host: s.ExternalIP.String(),
Servers: servers, Servers: servers,
} }
} }

View file

@ -1,24 +1,34 @@
package server package server_test
import ( import (
"bufio" "bufio"
"context" "context"
"fmt" "fmt"
"log" "net"
"os" "os"
"strconv"
"strings" "strings"
"testing" "testing"
"github.com/lbryio/hub/internal/metrics" "github.com/lbryio/herald.go/internal/metrics"
pb "github.com/lbryio/hub/protobuf/go" pb "github.com/lbryio/herald.go/protobuf/go"
"github.com/lbryio/herald.go/server"
"github.com/lbryio/lbry.go/v3/extras/stop"
dto "github.com/prometheus/client_model/go" dto "github.com/prometheus/client_model/go"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
) )
// lineCountFile takes a fileName and counts the number of lines in it. // lineCountFile takes a fileName and counts the number of lines in it.
func lineCountFile(fileName string) int { func lineCountFile(fileName string) int {
f, err := os.Open(fileName) f, err := os.Open(fileName)
defer f.Close() defer func() {
err := f.Close()
if err != nil {
log.Warn(err)
}
}()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return 0 return 0
@ -42,40 +52,17 @@ func removeFile(fileName string) {
} }
} }
func makeDefaultArgs() *Args {
args := &Args{
CmdType: ServeCmd,
Host: DefaultHost,
Port: DefaultPort,
EsHost: DefaultEsHost,
EsPort: DefaultEsPort,
UDPPort: DefaultUdpPort,
PrometheusPort: DefaultPrometheusPort,
EsIndex: DefaultEsIndex,
RefreshDelta: DefaultRefreshDelta,
CacheTTL: DefaultCacheTTL,
PeerFile: DefaultPeerFile,
Country: DefaultCountry,
DisableEs: true,
Debug: true,
LoadPeers: false,
StartPrometheus: false,
StartUDP: false,
WritePeers: false,
}
return args
}
// TestAddPeer tests the ability to add peers // TestAddPeer tests the ability to add peers
func TestAddPeer(t *testing.T) { func TestAddPeer(t *testing.T) {
ctx := context.Background() // ctx := context.Background()
args := makeDefaultArgs() ctx := stop.NewDebug()
args := server.MakeDefaultTestArgs()
args.DisableStartNotifier = false
tests := []struct { tests := []struct {
name string name string
want int want int
} { }{
{ {
name: "Add 10 peers", name: "Add 10 peers",
want: 10, want: 10,
@ -87,27 +74,27 @@ func TestAddPeer(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T){ t.Run(tt.name, func(t *testing.T) {
server := MakeHubServer(ctx, args) hubServer := server.MakeHubServer(ctx, args)
server.Subscribed = true hubServer.ExternalIP = net.IPv4(0, 0, 0, 0)
metrics.PeersKnown.Set(0) metrics.PeersKnown.Set(0)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
var msg *pb.ServerMessage var peer *server.Peer
if strings.Contains(tt.name, "1 unique") { if strings.Contains(tt.name, "1 unique") {
msg = &pb.ServerMessage{ peer = &server.Peer{
Address: "1.1.1.1", Address: "1.1.1.1",
Port: "50051", Port: "50051",
} }
} else { } else {
x := i + 1 x := i + 1
msg = &pb.ServerMessage{ peer = &server.Peer{
Address: fmt.Sprintf("%d.%d.%d.%d", x, x, x, x), Address: fmt.Sprintf("%d.%d.%d.%d", x, x, x, x),
Port: "50051", Port: "50051",
} }
} }
//log.Printf("Adding peer %+v\n", msg) //log.Printf("Adding peer %+v\n", msg)
err := server.addPeer(msg, false) err := hubServer.AddPeerExported()(peer, false, false)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
@ -120,6 +107,7 @@ func TestAddPeer(t *testing.T) {
if got != tt.want { if got != tt.want {
t.Errorf("len(server.PeerServers) = %d, want %d\n", got, tt.want) t.Errorf("len(server.PeerServers) = %d, want %d\n", got, tt.want)
} }
hubServer.Stop()
}) })
} }
@ -127,14 +115,15 @@ func TestAddPeer(t *testing.T) {
// TestPeerWriter tests that peers get written properly // TestPeerWriter tests that peers get written properly
func TestPeerWriter(t *testing.T) { func TestPeerWriter(t *testing.T) {
ctx := context.Background() ctx := stop.NewDebug()
args := makeDefaultArgs() args := server.MakeDefaultTestArgs()
args.WritePeers = true args.DisableWritePeers = false
args.DisableStartNotifier = false
tests := []struct { tests := []struct {
name string name string
want int want int
} { }{
{ {
name: "Add 10 peers", name: "Add 10 peers",
want: 10, want: 10,
@ -146,35 +135,34 @@ func TestPeerWriter(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T){ t.Run(tt.name, func(t *testing.T) {
server := MakeHubServer(ctx, args) hubServer := server.MakeHubServer(ctx, args)
server.Subscribed = true hubServer.ExternalIP = net.IPv4(0, 0, 0, 0)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
var msg *pb.ServerMessage var peer *server.Peer
if strings.Contains(tt.name, "1 unique") { if strings.Contains(tt.name, "1 unique") {
msg = &pb.ServerMessage{ peer = &server.Peer{
Address: "1.1.1.1", Address: "1.1.1.1",
Port: "50051", Port: "50051",
} }
} else { } else {
x := i + 1 x := i + 1
msg = &pb.ServerMessage{ peer = &server.Peer{
Address: fmt.Sprintf("%d.%d.%d.%d", x, x, x, x), Address: fmt.Sprintf("%d.%d.%d.%d", x, x, x, x),
Port: "50051", Port: "50051",
} }
} }
//log.Printf("Adding peer %+v\n", msg) err := hubServer.AddPeerExported()(peer, false, false)
err := server.addPeer(msg, false)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
} }
//log.Println("Counting lines...") got := lineCountFile(hubServer.Args.PeerFile)
got := lineCountFile(server.Args.PeerFile)
if got != tt.want { if got != tt.want {
t.Errorf("lineCountFile(peers.txt) = %d, want %d", got, tt.want) t.Errorf("lineCountFile(peers.txt) = %d, want %d", got, tt.want)
} }
hubServer.Stop()
}) })
} }
@ -183,17 +171,19 @@ func TestPeerWriter(t *testing.T) {
// TestAddPeerEndpoint tests the ability to add peers // TestAddPeerEndpoint tests the ability to add peers
func TestAddPeerEndpoint(t *testing.T) { func TestAddPeerEndpoint(t *testing.T) {
ctx := context.Background() ctx := stop.NewDebug()
args := makeDefaultArgs() args := server.MakeDefaultTestArgs()
args2 := makeDefaultArgs() args.DisableStartNotifier = false
args2.Port = "50052" args2 := server.MakeDefaultTestArgs()
args2.DisableStartNotifier = false
args2.Port = 50052
args2.NotifierPort = 18081
tests := []struct { tests := []struct {
name string name string
wantServerOne int64 wantServerOne int64
wantServerTwo int64 wantServerTwo int64
} { }{
{ {
// outside -> server1.AddPeer(server2, ping=true) : server1 = 1, server2 = 0 // outside -> server1.AddPeer(server2, ping=true) : server1 = 1, server2 = 0
// server1 -> server2.Hello(server1) : server1 = 1, server2 = 0 // server1 -> server2.Hello(server1) : server1 = 1, server2 = 0
@ -204,22 +194,21 @@ func TestAddPeerEndpoint(t *testing.T) {
// server1 -> server2.AddPeer(server2) : server1 = 1, server2 = 1 // server1 -> server2.AddPeer(server2) : server1 = 1, server2 = 1
// server2 self peer, skipping : server1 = 1, server2 = 1 // server2 self peer, skipping : server1 = 1, server2 = 1
// server1 -> server2.PeerSubscribe(server1) : server1 = 1, server2 = 1 // server1 -> server2.PeerSubscribe(server1) : server1 = 1, server2 = 1
name: "Add 1 peer", name: "Add 1 peer",
wantServerOne: 1, wantServerOne: 1,
wantServerTwo: 1, wantServerTwo: 1,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T){ t.Run(tt.name, func(t *testing.T) {
server := MakeHubServer(ctx, args) hubServer := server.MakeHubServer(ctx, args)
server2 := MakeHubServer(ctx, args2) hubServer2 := server.MakeHubServer(ctx, args2)
metrics.PeersKnown.Set(0) metrics.PeersKnown.Set(0)
go server.Run() go hubServer.Run()
go server2.Run() go hubServer2.Run()
//go server.Run() conn, err := grpc.Dial("localhost:"+strconv.Itoa(args.Port),
conn, err := grpc.Dial("localhost:"+args.Port, grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithInsecure(),
grpc.WithBlock(), grpc.WithBlock(),
) )
if err != nil { if err != nil {
@ -238,16 +227,16 @@ func TestAddPeerEndpoint(t *testing.T) {
log.Println(err) log.Println(err)
} }
server.GrpcServer.GracefulStop() got1 := hubServer.GetNumPeersExported()()
server2.GrpcServer.GracefulStop() got2 := hubServer2.GetNumPeersExported()()
got1 := server.getNumPeers()
got2 := server2.getNumPeers()
if got1 != tt.wantServerOne { if got1 != tt.wantServerOne {
t.Errorf("len(server.PeerServers) = %d, want %d\n", got1, tt.wantServerOne) t.Errorf("len(hubServer.PeerServers) = %d, want %d\n", got1, tt.wantServerOne)
} }
if got2 != tt.wantServerTwo { if got2 != tt.wantServerTwo {
t.Errorf("len(server2.PeerServers) = %d, want %d\n", got2, tt.wantServerTwo) t.Errorf("len(hubServer2.PeerServers) = %d, want %d\n", got2, tt.wantServerTwo)
} }
hubServer.Stop()
hubServer2.Stop()
}) })
} }
@ -255,22 +244,26 @@ func TestAddPeerEndpoint(t *testing.T) {
// TestAddPeerEndpoint2 tests the ability to add peers // TestAddPeerEndpoint2 tests the ability to add peers
func TestAddPeerEndpoint2(t *testing.T) { func TestAddPeerEndpoint2(t *testing.T) {
ctx := context.Background() ctx := stop.NewDebug()
args := makeDefaultArgs() args := server.MakeDefaultTestArgs()
args2 := makeDefaultArgs() args2 := server.MakeDefaultTestArgs()
args3 := makeDefaultArgs() args3 := server.MakeDefaultTestArgs()
args2.Port = "50052" args2.Port = 50052
args3.Port = "50053" args3.Port = 50053
args.DisableStartNotifier = false
args2.DisableStartNotifier = false
args3.DisableStartNotifier = false
args2.NotifierPort = 18081
args3.NotifierPort = 18082
tests := []struct { tests := []struct {
name string name string
wantServerOne int64 wantServerOne int64
wantServerTwo int64 wantServerTwo int64
wantServerThree int64 wantServerThree int64
} { }{
{ {
name: "Add 2 peers", name: "Add 2 peers",
wantServerOne: 2, wantServerOne: 2,
wantServerTwo: 2, wantServerTwo: 2,
wantServerThree: 2, wantServerThree: 2,
@ -278,16 +271,16 @@ func TestAddPeerEndpoint2(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T){ t.Run(tt.name, func(t *testing.T) {
server := MakeHubServer(ctx, args) hubServer := server.MakeHubServer(ctx, args)
server2 := MakeHubServer(ctx, args2) hubServer2 := server.MakeHubServer(ctx, args2)
server3 := MakeHubServer(ctx, args3) hubServer3 := server.MakeHubServer(ctx, args3)
metrics.PeersKnown.Set(0) metrics.PeersKnown.Set(0)
go server.Run() go hubServer.Run()
go server2.Run() go hubServer2.Run()
go server3.Run() go hubServer3.Run()
conn, err := grpc.Dial("localhost:"+args.Port, conn, err := grpc.Dial("localhost:"+strconv.Itoa(args.Port),
grpc.WithInsecure(), grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(), grpc.WithBlock(),
) )
if err != nil { if err != nil {
@ -315,45 +308,48 @@ func TestAddPeerEndpoint2(t *testing.T) {
log.Println(err) log.Println(err)
} }
server.GrpcServer.GracefulStop() got1 := hubServer.GetNumPeersExported()()
server2.GrpcServer.GracefulStop() got2 := hubServer2.GetNumPeersExported()()
server3.GrpcServer.GracefulStop() got3 := hubServer3.GetNumPeersExported()()
got1 := server.getNumPeers()
got2 := server2.getNumPeers()
got3 := server3.getNumPeers()
if got1 != tt.wantServerOne { if got1 != tt.wantServerOne {
t.Errorf("len(server.PeerServers) = %d, want %d\n", got1, tt.wantServerOne) t.Errorf("len(hubServer.PeerServers) = %d, want %d\n", got1, tt.wantServerOne)
} }
if got2 != tt.wantServerTwo { if got2 != tt.wantServerTwo {
t.Errorf("len(server2.PeerServers) = %d, want %d\n", got2, tt.wantServerTwo) t.Errorf("len(hubServer2.PeerServers) = %d, want %d\n", got2, tt.wantServerTwo)
} }
if got3 != tt.wantServerThree { if got3 != tt.wantServerThree {
t.Errorf("len(server3.PeerServers) = %d, want %d\n", got3, tt.wantServerThree) t.Errorf("len(hubServer3.PeerServers) = %d, want %d\n", got3, tt.wantServerThree)
} }
hubServer.Stop()
hubServer2.Stop()
hubServer3.Stop()
}) })
} }
} }
// TestAddPeerEndpoint3 tests the ability to add peers // TestAddPeerEndpoint3 tests the ability to add peers
func TestAddPeerEndpoint3(t *testing.T) { func TestAddPeerEndpoint3(t *testing.T) {
ctx := context.Background() ctx := stop.NewDebug()
args := makeDefaultArgs() args := server.MakeDefaultTestArgs()
args2 := makeDefaultArgs() args2 := server.MakeDefaultTestArgs()
args3 := makeDefaultArgs() args3 := server.MakeDefaultTestArgs()
args2.Port = "50052" args2.Port = 50052
args3.Port = "50053" args3.Port = 50053
args.DisableStartNotifier = false
args2.DisableStartNotifier = false
args3.DisableStartNotifier = false
args2.NotifierPort = 18081
args3.NotifierPort = 18082
tests := []struct { tests := []struct {
name string name string
wantServerOne int64 wantServerOne int64
wantServerTwo int64 wantServerTwo int64
wantServerThree int64 wantServerThree int64
} { }{
{ {
name: "Add 1 peer to each", name: "Add 1 peer to each",
wantServerOne: 2, wantServerOne: 2,
wantServerTwo: 2, wantServerTwo: 2,
wantServerThree: 2, wantServerThree: 2,
@ -361,23 +357,23 @@ func TestAddPeerEndpoint3(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T){ t.Run(tt.name, func(t *testing.T) {
server := MakeHubServer(ctx, args) hubServer := server.MakeHubServer(ctx, args)
server2 := MakeHubServer(ctx, args2) hubServer2 := server.MakeHubServer(ctx, args2)
server3 := MakeHubServer(ctx, args3) hubServer3 := server.MakeHubServer(ctx, args3)
metrics.PeersKnown.Set(0) metrics.PeersKnown.Set(0)
go server.Run() go hubServer.Run()
go server2.Run() go hubServer2.Run()
go server3.Run() go hubServer3.Run()
conn, err := grpc.Dial("localhost:"+args.Port, conn, err := grpc.Dial("localhost:"+strconv.Itoa(args.Port),
grpc.WithInsecure(), grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(), grpc.WithBlock(),
) )
if err != nil { if err != nil {
log.Fatalf("did not connect: %v", err) log.Fatalf("did not connect: %v", err)
} }
conn2, err := grpc.Dial("localhost:50052", conn2, err := grpc.Dial("localhost:50052",
grpc.WithInsecure(), grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(), grpc.WithBlock(),
) )
if err != nil { if err != nil {
@ -406,20 +402,75 @@ func TestAddPeerEndpoint3(t *testing.T) {
log.Println(err) log.Println(err)
} }
server.GrpcServer.GracefulStop() hubServer.Stop()
server2.GrpcServer.GracefulStop() hubServer2.Stop()
server3.GrpcServer.GracefulStop() hubServer3.Stop()
got1 := server.getNumPeers() got1 := hubServer.GetNumPeersExported()()
got2 := server2.getNumPeers() got2 := hubServer2.GetNumPeersExported()()
got3 := server3.getNumPeers() got3 := hubServer3.GetNumPeersExported()()
if got1 != tt.wantServerOne { if got1 != tt.wantServerOne {
t.Errorf("len(server.PeerServers) = %d, want %d\n", got1, tt.wantServerOne) t.Errorf("len(hubServer.PeerServers) = %d, want %d\n", got1, tt.wantServerOne)
} }
if got2 != tt.wantServerTwo { if got2 != tt.wantServerTwo {
t.Errorf("len(server2.PeerServers) = %d, want %d\n", got2, tt.wantServerTwo) t.Errorf("len(hubServer2.PeerServers) = %d, want %d\n", got2, tt.wantServerTwo)
} }
if got3 != tt.wantServerThree { if got3 != tt.wantServerThree {
t.Errorf("len(server3.PeerServers) = %d, want %d\n", got3, tt.wantServerThree) t.Errorf("len(hubServer3.PeerServers) = %d, want %d\n", got3, tt.wantServerThree)
}
})
}
}
// TestAddPeer tests the ability to add peers
func TestUDPServer(t *testing.T) {
ctx := stop.NewDebug()
args := server.MakeDefaultTestArgs()
args2 := server.MakeDefaultTestArgs()
args2.Port = 50052
args.DisableStartUDP = false
args2.DisableStartUDP = false
tests := []struct {
name string
want string
}{
{
name: "hubs hubServer external ip",
want: "127.0.0.1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hubServer := server.MakeHubServer(ctx, args)
hubServer2 := server.MakeHubServer(ctx, args2)
go hubServer.Run()
go hubServer2.Run()
metrics.PeersKnown.Set(0)
peer := &server.Peer{
Address: "0.0.0.0",
Port: "50052",
}
err := hubServer.AddPeerExported()(peer, true, true)
if err != nil {
log.Println(err)
}
hubServer.Stop()
hubServer2.Stop()
got1 := hubServer.ExternalIP.String()
if got1 != tt.want {
t.Errorf("hubServer.ExternalIP = %s, want %s\n", got1, tt.want)
t.Errorf("hubServer.Args.Port = %d\n", hubServer.Args.Port)
}
got2 := hubServer2.ExternalIP.String()
if got2 != tt.want {
t.Errorf("hubServer2.ExternalIP = %s, want %s\n", got2, tt.want)
t.Errorf("hubServer2.Args.Port = %d\n", hubServer2.Args.Port)
} }
}) })
} }

View file

@ -0,0 +1,924 @@
package server
import (
"bytes"
"compress/zlib"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"github.com/lbryio/herald.go/db"
"github.com/lbryio/herald.go/internal"
"github.com/lbryio/lbcd/chaincfg"
"github.com/lbryio/lbcd/chaincfg/chainhash"
"github.com/lbryio/lbcd/txscript"
"github.com/lbryio/lbcd/wire"
"github.com/lbryio/lbcutil"
"golang.org/x/exp/constraints"
log "github.com/sirupsen/logrus"
)
// BlockchainBlockService methods handle "blockchain.block.*" RPCs
type BlockchainBlockService struct {
DB *db.ReadOnlyDBColumnFamily
Chain *chaincfg.Params
}
// BlockchainBlockService methods handle "blockchain.headers.*" RPCs
type BlockchainHeadersService struct {
DB *db.ReadOnlyDBColumnFamily
Chain *chaincfg.Params
// needed for subscribe/unsubscribe
sessionMgr *sessionManager
session *session
}
// BlockchainAddressService methods handle "blockchain.address.*" RPCs
type BlockchainAddressService struct {
DB *db.ReadOnlyDBColumnFamily
Chain *chaincfg.Params
// needed for subscribe/unsubscribe
sessionMgr *sessionManager
session *session
}
// BlockchainScripthashService methods handle "blockchain.scripthash.*" RPCs
type BlockchainScripthashService struct {
DB *db.ReadOnlyDBColumnFamily
Chain *chaincfg.Params
// needed for subscribe/unsubscribe
sessionMgr *sessionManager
session *session
}
// BlockchainTransactionService methods handle "blockchain.transaction.*" RPCs
type BlockchainTransactionService struct {
DB *db.ReadOnlyDBColumnFamily
Chain *chaincfg.Params
// needed for broadcast TX
sessionMgr *sessionManager
}
const CHUNK_SIZE = 96
const MAX_CHUNK_SIZE = 40960
const HEADER_SIZE = wire.MaxBlockHeaderPayload
const HASHX_LEN = 11
func min[Ord constraints.Ordered](x, y Ord) Ord {
if x < y {
return x
}
return y
}
func max[Ord constraints.Ordered](x, y Ord) Ord {
if x > y {
return x
}
return y
}
type BlockHeaderElectrum struct {
Version uint32 `json:"version"`
PrevBlockHash string `json:"prev_block_hash"`
MerkleRoot string `json:"merkle_root"`
ClaimTrieRoot string `json:"claim_trie_root"`
Timestamp uint32 `json:"timestamp"`
Bits uint32 `json:"bits"`
Nonce uint32 `json:"nonce"`
BlockHeight uint32 `json:"block_height"`
}
func newBlockHeaderElectrum(header *[HEADER_SIZE]byte, height uint32) *BlockHeaderElectrum {
var h1, h2, h3 chainhash.Hash
h1.SetBytes(header[4:36])
h2.SetBytes(header[36:68])
h3.SetBytes(header[68:100])
return &BlockHeaderElectrum{
Version: binary.LittleEndian.Uint32(header[0:]),
PrevBlockHash: h1.String(),
MerkleRoot: h2.String(),
ClaimTrieRoot: h3.String(),
Timestamp: binary.LittleEndian.Uint32(header[100:]),
Bits: binary.LittleEndian.Uint32(header[104:]),
Nonce: binary.LittleEndian.Uint32(header[108:]),
BlockHeight: height,
}
}
type BlockGetServerHeightReq struct{}
type BlockGetServerHeightResp uint32
// blockchain.block.get_server_height
func (s *BlockchainBlockService) Get_server_height(req *BlockGetServerHeightReq, resp **BlockGetServerHeightResp) error {
if s.DB == nil || s.DB.LastState == nil {
return fmt.Errorf("unknown height")
}
result := BlockGetServerHeightResp(s.DB.LastState.Height)
*resp = &result
return nil
}
type BlockGetChunkReq uint32
type BlockGetChunkResp string
// 'blockchain.block.get_chunk'
func (s *BlockchainBlockService) Get_chunk(req *BlockGetChunkReq, resp **BlockGetChunkResp) error {
index := uint32(*req)
db_headers, err := s.DB.GetHeaders(index*CHUNK_SIZE, CHUNK_SIZE)
if err != nil {
log.Warn(err)
return err
}
raw := make([]byte, 0, HEADER_SIZE*len(db_headers))
for _, h := range db_headers {
raw = append(raw, h[:]...)
}
headers := BlockGetChunkResp(hex.EncodeToString(raw))
*resp = &headers
return err
}
type BlockGetHeaderReq uint32
type BlockGetHeaderResp struct {
BlockHeaderElectrum
}
// 'blockchain.block.get_header'
func (s *BlockchainBlockService) Get_header(req *BlockGetHeaderReq, resp **BlockGetHeaderResp) error {
height := uint32(*req)
headers, err := s.DB.GetHeaders(height, 1)
if err != nil {
log.Warn(err)
return err
}
if len(headers) < 1 {
return errors.New("not found")
}
*resp = &BlockGetHeaderResp{*newBlockHeaderElectrum(&headers[0], height)}
return err
}
type BlockHeadersReq struct {
StartHeight uint32 `json:"start_height"`
Count uint32 `json:"count"`
CpHeight uint32 `json:"cp_height"`
B64 bool `json:"b64"`
}
func (req *BlockHeadersReq) UnmarshalJSON(b []byte) error {
var params [4]interface{}
err := json.Unmarshal(b, &params)
if err != nil {
return err
}
switch params[0].(type) {
case float64:
req.StartHeight = uint32(params[0].(float64))
default:
return fmt.Errorf("expected numeric argument #0 (start_height)")
}
switch params[1].(type) {
case float64:
req.Count = uint32(params[1].(float64))
default:
return fmt.Errorf("expected numeric argument #1 (count)")
}
switch params[2].(type) {
case float64:
req.CpHeight = uint32(params[2].(float64))
default:
return fmt.Errorf("expected numeric argument #2 (cp_height)")
}
switch params[3].(type) {
case bool:
req.B64 = params[3].(bool)
default:
return fmt.Errorf("expected boolean argument #3 (b64)")
}
return nil
}
type BlockHeadersResp struct {
Base64 string `json:"base64,omitempty"`
Hex string `json:"hex"`
Count uint32 `json:"count"`
Max uint32 `json:"max"`
Branch string `json:"branch,omitempty"`
Root string `json:"root,omitempty"`
}
// 'blockchain.block.headers'
func (s *BlockchainBlockService) Headers(req *BlockHeadersReq, resp **BlockHeadersResp) error {
count := min(req.Count, MAX_CHUNK_SIZE)
db_headers, err := s.DB.GetHeaders(req.StartHeight, count)
if err != nil {
log.Warn(err)
return err
}
count = uint32(len(db_headers))
raw := make([]byte, 0, HEADER_SIZE*count)
for _, h := range db_headers {
raw = append(raw, h[:]...)
}
result := &BlockHeadersResp{
Count: count,
Max: MAX_CHUNK_SIZE,
}
if req.B64 {
zipped := bytes.Buffer{}
w := zlib.NewWriter(&zipped)
w.Write(raw)
w.Close()
result.Base64 = base64.StdEncoding.EncodeToString(zipped.Bytes())
} else {
result.Hex = hex.EncodeToString(raw)
}
if count > 0 && req.CpHeight > 0 {
// TODO
//last_height := height + count - 1
}
*resp = result
return err
}
type HeadersSubscribeReq struct {
Raw bool `json:"raw"`
}
func (req *HeadersSubscribeReq) UnmarshalJSON(b []byte) error {
var params [1]interface{}
err := json.Unmarshal(b, &params)
if err != nil {
return err
}
switch params[0].(type) {
case bool:
req.Raw = params[0].(bool)
default:
return fmt.Errorf("expected bool argument #0 (raw)")
}
return nil
}
type HeadersSubscribeResp struct {
BlockHeaderElectrum
}
type HeadersSubscribeRawResp struct {
Hex string `json:"hex"`
Height uint32 `json:"height"`
}
// 'blockchain.headers.subscribe'
func (s *BlockchainHeadersService) Subscribe(req *HeadersSubscribeReq, resp *interface{}) error {
if s.sessionMgr == nil || s.session == nil {
return errors.New("no session, rpc not supported")
}
s.sessionMgr.headersSubscribe(s.session, req.Raw, true /*subscribe*/)
height := s.DB.Height
if s.DB.LastState != nil {
height = s.DB.LastState.Height
}
headers, err := s.DB.GetHeaders(height, 1)
if err != nil {
s.sessionMgr.headersSubscribe(s.session, req.Raw, false /*subscribe*/)
return err
}
if len(headers) < 1 {
return errors.New("not found")
}
if req.Raw {
*resp = &HeadersSubscribeRawResp{
Hex: hex.EncodeToString(headers[0][:]),
Height: height,
}
} else {
*resp = &HeadersSubscribeResp{*newBlockHeaderElectrum(&headers[0], height)}
}
return err
}
func decodeScriptHash(scripthash string) ([]byte, error) {
sh, err := hex.DecodeString(scripthash)
if err != nil {
return nil, err
}
if len(sh) != chainhash.HashSize {
return nil, fmt.Errorf("invalid scripthash: %v (length %v)", scripthash, len(sh))
}
internal.ReverseBytesInPlace(sh)
return sh, nil
}
func hashX(scripthash []byte) []byte {
return scripthash[:HASHX_LEN]
}
func hashXScript(script []byte, coin *chaincfg.Params) []byte {
if _, err := txscript.ExtractClaimScript(script); err == nil {
baseScript := txscript.StripClaimScriptPrefix(script)
if class, addrs, _, err := txscript.ExtractPkScriptAddrs(baseScript, coin); err == nil {
switch class {
case txscript.PubKeyHashTy, txscript.ScriptHashTy, txscript.PubKeyTy:
script, _ := txscript.PayToAddrScript(addrs[0])
return hashXScript(script, coin)
}
}
}
sum := sha256.Sum256(script)
return sum[:HASHX_LEN]
}
type AddressGetBalanceReq struct {
Address string `json:"address"`
}
type AddressGetBalanceResp struct {
Confirmed uint64 `json:"confirmed"`
Unconfirmed uint64 `json:"unconfirmed"`
}
// 'blockchain.address.get_balance'
func (s *BlockchainAddressService) Get_balance(req *AddressGetBalanceReq, resp **AddressGetBalanceResp) error {
address, err := lbcutil.DecodeAddress(req.Address, s.Chain)
if err != nil {
log.Warn(err)
return err
}
script, err := txscript.PayToAddrScript(address)
if err != nil {
log.Warn(err)
return err
}
hashX := hashXScript(script, s.Chain)
confirmed, unconfirmed, err := s.DB.GetBalance(hashX)
if err != nil {
log.Warn(err)
return err
}
*resp = &AddressGetBalanceResp{confirmed, unconfirmed}
return err
}
type scripthashGetBalanceReq struct {
ScriptHash string `json:"scripthash"`
}
type ScripthashGetBalanceResp struct {
Confirmed uint64 `json:"confirmed"`
Unconfirmed uint64 `json:"unconfirmed"`
}
// 'blockchain.scripthash.get_balance'
func (s *BlockchainScripthashService) Get_balance(req *scripthashGetBalanceReq, resp **ScripthashGetBalanceResp) error {
scripthash, err := decodeScriptHash(req.ScriptHash)
if err != nil {
log.Warn(err)
return err
}
hashX := hashX(scripthash)
confirmed, unconfirmed, err := s.DB.GetBalance(hashX)
if err != nil {
log.Warn(err)
return err
}
*resp = &ScripthashGetBalanceResp{confirmed, unconfirmed}
return err
}
type AddressGetHistoryReq struct {
Address string `json:"address"`
}
func (req *AddressGetHistoryReq) UnmarshalJSON(b []byte) error {
var params [1]interface{}
json.Unmarshal(b, &params)
err := json.Unmarshal(b, &params)
if err != nil {
return err
}
switch params[0].(type) {
case string:
req.Address = params[0].(string)
default:
return fmt.Errorf("expected string argument #0 (address)")
}
return nil
}
type TxInfo struct {
TxHash string `json:"tx_hash"`
Height uint32 `json:"height"`
}
type TxInfoFee struct {
TxInfo
Fee uint64 `json:"fee"`
}
type AddressGetHistoryResp []TxInfoFee
// 'blockchain.address.get_history'
func (s *BlockchainAddressService) Get_history(req *AddressGetHistoryReq, resp **AddressGetHistoryResp) error {
address, err := lbcutil.DecodeAddress(req.Address, s.Chain)
if err != nil {
log.Warn(err)
return err
}
script, err := txscript.PayToAddrScript(address)
if err != nil {
log.Warn(err)
return err
}
hashX := hashXScript(script, s.Chain)
dbTXs, err := s.DB.GetHistory(hashX)
if err != nil {
log.Warn(err)
return err
}
confirmed := make([]TxInfo, 0, len(dbTXs))
for _, tx := range dbTXs {
confirmed = append(confirmed,
TxInfo{
TxHash: tx.TxHash.String(),
Height: tx.Height,
})
}
unconfirmed := []TxInfoFee{} // TODO
result := make(AddressGetHistoryResp, len(confirmed)+len(unconfirmed))
i := 0
for _, tx := range confirmed {
result[i].TxInfo = tx
i += 1
}
for _, tx := range unconfirmed {
result[i] = tx
i += 1
}
*resp = &result
return err
}
type ScripthashGetHistoryReq struct {
ScriptHash string `json:"scripthash"`
}
type ScripthashGetHistoryResp struct {
Confirmed []TxInfo `json:"confirmed"`
Unconfirmed []TxInfoFee `json:"unconfirmed"`
}
// 'blockchain.scripthash.get_history'
func (s *BlockchainScripthashService) Get_history(req *ScripthashGetHistoryReq, resp **ScripthashGetHistoryResp) error {
scripthash, err := decodeScriptHash(req.ScriptHash)
if err != nil {
log.Warn(err)
return err
}
hashX := hashX(scripthash)
dbTXs, err := s.DB.GetHistory(hashX)
if err != nil {
log.Warn(err)
return err
}
confirmed := make([]TxInfo, 0, len(dbTXs))
for _, tx := range dbTXs {
confirmed = append(confirmed,
TxInfo{
TxHash: tx.TxHash.String(),
Height: tx.Height,
})
}
result := &ScripthashGetHistoryResp{
Confirmed: confirmed,
Unconfirmed: []TxInfoFee{}, // TODO
}
*resp = result
return err
}
type AddressGetMempoolReq struct {
Address string `json:"address"`
}
type AddressGetMempoolResp []TxInfoFee
// 'blockchain.address.get_mempool'
func (s *BlockchainAddressService) Get_mempool(req *AddressGetMempoolReq, resp **AddressGetMempoolResp) error {
address, err := lbcutil.DecodeAddress(req.Address, s.Chain)
if err != nil {
log.Warn(err)
return err
}
script, err := txscript.PayToAddrScript(address)
if err != nil {
log.Warn(err)
return err
}
hashX := hashXScript(script, s.Chain)
// TODO...
internal.ReverseBytesInPlace(hashX)
unconfirmed := make([]TxInfoFee, 0, 100)
result := AddressGetMempoolResp(unconfirmed)
*resp = &result
return err
}
type ScripthashGetMempoolReq struct {
ScriptHash string `json:"scripthash"`
}
type ScripthashGetMempoolResp []TxInfoFee
// 'blockchain.scripthash.get_mempool'
func (s *BlockchainScripthashService) Get_mempool(req *ScripthashGetMempoolReq, resp **ScripthashGetMempoolResp) error {
scripthash, err := decodeScriptHash(req.ScriptHash)
if err != nil {
log.Warn(err)
return err
}
hashX := hashX(scripthash)
// TODO...
internal.ReverseBytesInPlace(hashX)
unconfirmed := make([]TxInfoFee, 0, 100)
result := ScripthashGetMempoolResp(unconfirmed)
*resp = &result
return err
}
type AddressListUnspentReq struct {
Address string `json:"address"`
}
type TXOInfo struct {
TxHash string `json:"tx_hash"`
TxPos uint16 `json:"tx_pos"`
Height uint32 `json:"height"`
Value uint64 `json:"value"`
}
type AddressListUnspentResp []TXOInfo
// 'blockchain.address.listunspent'
func (s *BlockchainAddressService) Listunspent(req *AddressListUnspentReq, resp **AddressListUnspentResp) error {
address, err := lbcutil.DecodeAddress(req.Address, s.Chain)
if err != nil {
log.Warn(err)
return err
}
script, err := txscript.PayToAddrScript(address)
if err != nil {
log.Warn(err)
return err
}
hashX := hashXScript(script, s.Chain)
dbTXOs, err := s.DB.GetUnspent(hashX)
unspent := make([]TXOInfo, 0, len(dbTXOs))
for _, txo := range dbTXOs {
unspent = append(unspent,
TXOInfo{
TxHash: txo.TxHash.String(),
TxPos: txo.TxPos,
Height: txo.Height,
Value: txo.Value,
})
}
result := AddressListUnspentResp(unspent)
*resp = &result
return err
}
type ScripthashListUnspentReq struct {
ScriptHash string `json:"scripthash"`
}
type ScripthashListUnspentResp []TXOInfo
// 'blockchain.scripthash.listunspent'
func (s *BlockchainScripthashService) Listunspent(req *ScripthashListUnspentReq, resp **ScripthashListUnspentResp) error {
scripthash, err := decodeScriptHash(req.ScriptHash)
if err != nil {
log.Warn(err)
return err
}
hashX := hashX(scripthash)
dbTXOs, err := s.DB.GetUnspent(hashX)
unspent := make([]TXOInfo, 0, len(dbTXOs))
for _, txo := range dbTXOs {
unspent = append(unspent,
TXOInfo{
TxHash: txo.TxHash.String(),
TxPos: txo.TxPos,
Height: txo.Height,
Value: txo.Value,
})
}
result := ScripthashListUnspentResp(unspent)
*resp = &result
return err
}
type AddressSubscribeReq []string
type AddressSubscribeResp []string
// 'blockchain.address.subscribe'
func (s *BlockchainAddressService) Subscribe(req *AddressSubscribeReq, resp **AddressSubscribeResp) error {
if s.sessionMgr == nil || s.session == nil {
return errors.New("no session, rpc not supported")
}
result := make([]string, 0, len(*req))
for _, addr := range *req {
address, err := lbcutil.DecodeAddress(addr, s.Chain)
if err != nil {
return err
}
script, err := txscript.PayToAddrScript(address)
if err != nil {
return err
}
hashX := hashXScript(script, s.Chain)
s.sessionMgr.hashXSubscribe(s.session, hashX, addr, true /*subscribe*/)
status, err := s.DB.GetStatus(hashX)
if err != nil {
return err
}
result = append(result, hex.EncodeToString(status))
}
*resp = (*AddressSubscribeResp)(&result)
return nil
}
// 'blockchain.address.unsubscribe'
func (s *BlockchainAddressService) Unsubscribe(req *AddressSubscribeReq, resp **AddressSubscribeResp) error {
if s.sessionMgr == nil || s.session == nil {
return errors.New("no session, rpc not supported")
}
for _, addr := range *req {
address, err := lbcutil.DecodeAddress(addr, s.Chain)
if err != nil {
return err
}
script, err := txscript.PayToAddrScript(address)
if err != nil {
return err
}
hashX := hashXScript(script, s.Chain)
s.sessionMgr.hashXSubscribe(s.session, hashX, addr, false /*subscribe*/)
}
*resp = (*AddressSubscribeResp)(nil)
return nil
}
type ScripthashSubscribeReq string
type ScripthashSubscribeResp string
// 'blockchain.scripthash.subscribe'
func (s *BlockchainScripthashService) Subscribe(req *ScripthashSubscribeReq, resp **ScripthashSubscribeResp) error {
if s.sessionMgr == nil || s.session == nil {
return errors.New("no session, rpc not supported")
}
var result string
scripthash, err := decodeScriptHash(string(*req))
if err != nil {
return err
}
hashX := hashX(scripthash)
s.sessionMgr.hashXSubscribe(s.session, hashX, string(*req), true /*subscribe*/)
status, err := s.DB.GetStatus(hashX)
if err != nil {
return err
}
result = hex.EncodeToString(status)
*resp = (*ScripthashSubscribeResp)(&result)
return nil
}
// 'blockchain.scripthash.unsubscribe'
func (s *BlockchainScripthashService) Unsubscribe(req *ScripthashSubscribeReq, resp **ScripthashSubscribeResp) error {
if s.sessionMgr == nil || s.session == nil {
return errors.New("no session, rpc not supported")
}
scripthash, err := decodeScriptHash(string(*req))
if err != nil {
return err
}
hashX := hashX(scripthash)
s.sessionMgr.hashXSubscribe(s.session, hashX, string(*req), false /*subscribe*/)
*resp = (*ScripthashSubscribeResp)(nil)
return nil
}
type TransactionBroadcastReq [1]string
type TransactionBroadcastResp string
// 'blockchain.transaction.broadcast'
func (s *BlockchainTransactionService) Broadcast(req *TransactionBroadcastReq, resp **TransactionBroadcastResp) error {
if s.sessionMgr == nil {
return errors.New("no session manager, rpc not supported")
}
strTx := string((*req)[0])
rawTx, err := hex.DecodeString(strTx)
if err != nil {
return err
}
txhash, err := s.sessionMgr.broadcastTx(rawTx)
if err != nil {
return err
}
result := txhash.String()
*resp = (*TransactionBroadcastResp)(&result)
return nil
}
type TransactionGetReq string
type TXFullDetail struct {
Height int `json:"block_height"`
Pos uint32 `json:"pos"`
Merkle []string `json:"merkle"`
}
type TXDetail struct {
Height int `json:"block_height"`
}
type TXGetItem struct {
TxHash string
TxRaw string
Detail interface{} // TXFullDetail or TXDetail struct
}
type TransactionGetResp TXGetItem
// 'blockchain.transaction.get'
func (s *BlockchainTransactionService) Get(req *TransactionGetReq, resp **TransactionGetResp) error {
txids := [1]string{string(*req)}
request := TransactionGetBatchReq(txids[:])
var response *TransactionGetBatchResp
err := s.Get_batch(&request, &response)
if err != nil {
return err
}
if len(*response) < 1 {
return errors.New("tx not found")
}
switch (*response)[0].Detail.(type) {
case TXFullDetail:
break
case TXDetail:
default:
return errors.New("tx not confirmed")
}
*resp = (*TransactionGetResp)(&(*response)[0])
return err
}
type TransactionGetBatchReq []string
func (req *TransactionGetBatchReq) UnmarshalJSON(b []byte) error {
var params []interface{}
json.Unmarshal(b, &params)
if len(params) > 100 {
return fmt.Errorf("too many tx hashes in request: %v", len(params))
}
for i, txhash := range params {
switch params[0].(type) {
case string:
*req = append(*req, txhash.(string))
default:
return fmt.Errorf("expected string argument #%d (tx_hash)", i)
}
}
return nil
}
type TransactionGetBatchResp []TXGetItem
func (resp *TransactionGetBatchResp) MarshalJSON() ([]byte, error) {
// encode key/value pairs as variable length JSON object
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
buf.WriteString("{")
for i, r := range *resp {
if i > 0 {
buf.WriteString(",")
}
txhash, raw, detail := r.TxHash, r.TxRaw, r.Detail
err := enc.Encode(txhash)
if err != nil {
return nil, err
}
buf.WriteString(":[")
err = enc.Encode(raw)
if err != nil {
return nil, err
}
buf.WriteString(",")
err = enc.Encode(detail)
if err != nil {
return nil, err
}
buf.WriteString("]")
}
buf.WriteString("}")
return buf.Bytes(), nil
}
// 'blockchain.transaction.get_batch'
func (s *BlockchainTransactionService) Get_batch(req *TransactionGetBatchReq, resp **TransactionGetBatchResp) error {
if len(*req) > 100 {
return fmt.Errorf("too many tx hashes in request: %v", len(*req))
}
tx_hashes := make([]chainhash.Hash, 0, len(*req))
for i, txid := range *req {
tx_hashes = append(tx_hashes, chainhash.Hash{})
if err := chainhash.Decode(&tx_hashes[i], txid); err != nil {
return err
}
}
dbResult, err := s.DB.GetTxMerkle(tx_hashes)
if err != nil {
return err
}
result := make([]TXGetItem, 0, len(dbResult))
for _, r := range dbResult {
merkles := make([]string, len(r.Merkle))
for i, m := range r.Merkle {
merkles[i] = m.String()
}
detail := TXFullDetail{
Height: r.Height,
Pos: r.Pos,
Merkle: merkles,
}
result = append(result, TXGetItem{r.TxHash.String(), hex.EncodeToString(r.RawTx), &detail})
}
*resp = (*TransactionGetBatchResp)(&result)
return err
}
type TransactionGetMerkleReq struct {
TxHash string `json:"tx_hash"`
Height uint32 `json:"height"`
}
type TransactionGetMerkleResp TXGetItem
// 'blockchain.transaction.get_merkle'
func (s *BlockchainTransactionService) Get_merkle(req *TransactionGetMerkleReq, resp **TransactionGetMerkleResp) error {
txids := [1]string{string(req.TxHash)}
request := TransactionGetBatchReq(txids[:])
var response *TransactionGetBatchResp
err := s.Get_batch(&request, &response)
if err != nil {
return err
}
if len(*response) < 1 {
return errors.New("tx not found")
}
switch (*response)[0].Detail.(type) {
case TXFullDetail:
break
case TXDetail:
default:
return errors.New("tx not confirmed")
}
*resp = (*TransactionGetMerkleResp)(&(*response)[0])
return err
}
type TransactionGetHeightReq string
type TransactionGetHeightResp uint32
// 'blockchain.transaction.get_height'
func (s *BlockchainTransactionService) Get_height(req *TransactionGetHeightReq, resp **TransactionGetHeightResp) error {
txid := string(*(req))
txhash, err := chainhash.NewHashFromStr(txid)
if err != nil {
return err
}
height, err := s.DB.GetTxHeight(txhash)
*resp = (*TransactionGetHeightResp)(&height)
return err
}
type TransactionInfoReq string
type TransactionInfoResp TXGetItem
// 'blockchain.transaction.info'
func (s *BlockchainTransactionService) Info(req *TransactionInfoReq, resp **TransactionInfoResp) error {
txids := [1]string{string(*req)}
request := TransactionGetBatchReq(txids[:])
var response *TransactionGetBatchResp
err := s.Get_batch(&request, &response)
if err != nil {
return err
}
if len(*response) < 1 {
return errors.New("tx not found")
}
switch (*response)[0].Detail.(type) {
case TXFullDetail:
break
case TXDetail:
default:
if (*response)[0].TxHash == "" {
return errors.New("no such mempool or blockchain transaction")
}
}
*resp = (*TransactionInfoResp)(&(*response)[0])
return err
}

View file

@ -0,0 +1,469 @@
package server
import (
"encoding/hex"
"encoding/json"
"net"
"strconv"
"sync"
"testing"
"github.com/lbryio/herald.go/db"
"github.com/lbryio/herald.go/internal"
"github.com/lbryio/lbcd/chaincfg"
"github.com/lbryio/lbcd/txscript"
"github.com/lbryio/lbcutil"
"github.com/lbryio/lbry.go/v3/extras/stop"
)
// Source: test_variety_of_transactions_and_longish_history (lbry-sdk/tests/integration/transactions)
const regTestDBPath = "../testdata/test_variety_of_transactions/lbry-rocksdb"
const regTestHeight = 502
var regTestAddrs = [30]string{
"mtgiQkd35xpx3TaZ4RBNirf3uSMQ8tXQ7z",
"mqMjBtzGTtRty7Y54RqeNLk9QE8rYUfpm3",
"n2q8ASDZmib4adu2eU4dPvVvjYeU97pks4",
"mzxYWTJogAtduNaeyH9pSSmBSPkJj33HDJ",
"mweCKeZkeUUi8RQdHry3Mziphb87vCwiiW",
"mp7ZuiZgBNJHFX6DVmeZrCj8SuzVQNDLwb",
"n2zZoBocGCcxe6jFo1anbbAsUFMPXdYfnY",
"msps28KwRJF77DxhzqD98prdwCrZwdUxJc",
"mjvkjuss63pq2mpsRn4Q5tsNKVMLG9qUt7",
"miF9cJn8HiX6vsorRDXtZEgcW7BeWowqkX",
"mx87wRYFchYaLjXyNaboMuEMRLRboFSPDD",
"mhvb94idtQvTSCQk9EB16wLLkSrbWizPRG",
"mx3Fu8FDM4nKR9VYtHWPtSGKVt1D588Ay1",
"mhqvhX7kLNQ2bUNWZxMhE1z6QEJKrqdV8T",
"mgekw8L4xEezFtkYdSarL4sk5Sc8n9UtzG",
"myhFrTz99ZHwbGo7qV4D7fJKfji7YJ3vZ8",
"mnf8UCVoo6DBq6Tg4QpnFFdV1mFVHi43TF",
"mn7hKyh6EA8oLAPkvTd9vPEgzLRejLxkj2",
"msfarwFff7LX6DkXk295x3YMnJtR5Yw8uy",
"mn8sUv6ryiLn4kzssBTqNaB1oL6qcKDzJ4",
"mhwgeQFyi1z1RxNR1CphE8PcwG2xBWcxDp",
"n2jKpDXhVaQHiKqhdQYwwykhoYtKtbh8P1",
"mhnt4btqpAuiNwjAfFxPEaA4ekCE8faRYN",
"mmTFCt6Du1VsdxSKc7f21vYsT75KnRy7NM",
"mm1nx1xSmgRponM5tmdq15KREa7f6M36La",
"mxMXmMKUqoj19hxEA5r3hZJgirT6nCQh14",
"mx2L4iqNGzpuNNsDmjvCpcomefDWLAjdv1",
"mohJcUzQdCYL7nEySKNQC8PUzowNS5gGvo",
"mjv1vErZiDXsh9TvBDGCBpzobZx7aVYuy7",
"mwDPTZzHsM6p1DfDnBeojDLRCDceTcejkT",
}
// const dbPath := "/Users/swdev1/hub/scribe_db.599529/lbry-rocksdb"
// const dbPath := "/mnt/d/data/snapshot_1072108/lbry-rocksdb"
func TestServerGetHeight(t *testing.T) {
secondaryPath := "asdf"
grp := stop.NewDebug()
db, err := db.GetProdDB(regTestDBPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
s := &BlockchainBlockService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
}
req := BlockGetServerHeightReq{}
var resp *BlockGetServerHeightResp
err = s.Get_server_height(&req, &resp)
if err != nil {
t.Errorf("handler err: %v", err)
}
marshalled, err := json.MarshalIndent(resp, "", " ")
if err != nil {
t.Errorf("unmarshal err: %v", err)
}
t.Logf("resp: %v", string(marshalled))
if string(marshalled) != strconv.FormatInt(regTestHeight, 10) {
t.Errorf("bad height: %v", string(marshalled))
}
}
func TestGetChunk(t *testing.T) {
secondaryPath := "asdf"
grp := stop.NewDebug()
db, err := db.GetProdDB(regTestDBPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
s := &BlockchainBlockService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
}
for index := 0; index < 10; index++ {
req := BlockGetChunkReq(index)
var resp *BlockGetChunkResp
err := s.Get_chunk(&req, &resp)
if err != nil {
t.Errorf("index: %v handler err: %v", index, err)
}
marshalled, err := json.MarshalIndent(resp, "", " ")
if err != nil {
t.Errorf("index: %v unmarshal err: %v", index, err)
}
t.Logf("index: %v resp: %v", index, string(marshalled))
switch index {
case 0, 1, 2, 3, 4:
if len(*resp) != (CHUNK_SIZE * HEADER_SIZE * 2) {
t.Errorf("index: %v bad length: %v", index, len(*resp))
}
case 5:
if len(*resp) != 23*112*2 {
t.Errorf("index: %v bad length: %v", index, len(*resp))
}
default:
if len(*resp) != 0 {
t.Errorf("index: %v bad length: %v", index, len(*resp))
}
}
}
}
func TestGetHeader(t *testing.T) {
secondaryPath := "asdf"
grp := stop.NewDebug()
db, err := db.GetProdDB(regTestDBPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
s := &BlockchainBlockService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
}
for height := 0; height < 700; height += 100 {
req := BlockGetHeaderReq(height)
var resp *BlockGetHeaderResp
err := s.Get_header(&req, &resp)
if err != nil && height <= 500 {
t.Errorf("height: %v handler err: %v", height, err)
}
marshalled, err := json.MarshalIndent(resp, "", " ")
if err != nil {
t.Errorf("height: %v unmarshal err: %v", height, err)
}
t.Logf("height: %v resp: %v", height, string(marshalled))
}
}
func TestHeaders(t *testing.T) {
secondaryPath := "asdf"
grp := stop.NewDebug()
db, err := db.GetProdDB(regTestDBPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
s := &BlockchainBlockService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
}
for height := uint32(0); height < 700; height += 100 {
req := BlockHeadersReq{
StartHeight: height,
Count: 1,
CpHeight: 0,
B64: false,
}
var resp *BlockHeadersResp
err := s.Headers(&req, &resp)
if err != nil {
t.Errorf("Headers: %v", err)
}
marshalled, err := json.MarshalIndent(resp, "", " ")
if err != nil {
t.Errorf("height: %v unmarshal err: %v", height, err)
}
t.Logf("height: %v resp: %v", height, string(marshalled))
}
}
func TestHeadersSubscribe(t *testing.T) {
args := MakeDefaultTestArgs()
grp := stop.NewDebug()
secondaryPath := "asdf"
db, err := db.GetProdDB(regTestDBPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
sm := newSessionManager(nil, db, args, grp, &chaincfg.RegressionNetParams, nil)
sm.start()
defer sm.stop()
client1, server1 := net.Pipe()
sess1 := sm.addSession(server1)
client2, server2 := net.Pipe()
sess2 := sm.addSession(server2)
// Set up logic to read a notification.
var received sync.WaitGroup
recv := func(client net.Conn) {
defer received.Done()
buf := make([]byte, 1024)
len, err := client.Read(buf)
if err != nil {
t.Errorf("read err: %v", err)
}
t.Logf("len: %v notification: %v", len, string(buf))
}
received.Add(2)
go recv(client1)
go recv(client2)
s1 := &BlockchainHeadersService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
sessionMgr: sm,
session: sess1,
}
s2 := &BlockchainHeadersService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
sessionMgr: sm,
session: sess2,
}
// Subscribe with Raw: false.
req1 := HeadersSubscribeReq{Raw: false}
var r any
err = s1.Subscribe(&req1, &r)
if err != nil {
t.Errorf("handler err: %v", err)
}
resp1 := r.(*HeadersSubscribeResp)
marshalled1, err := json.MarshalIndent(resp1, "", " ")
if err != nil {
t.Errorf("unmarshal err: %v", err)
}
// Subscribe with Raw: true.
t.Logf("resp: %v", string(marshalled1))
req2 := HeadersSubscribeReq{Raw: true}
err = s2.Subscribe(&req2, &r)
if err != nil {
t.Errorf("handler err: %v", err)
}
resp2 := r.(*HeadersSubscribeRawResp)
marshalled2, err := json.MarshalIndent(resp2, "", " ")
if err != nil {
t.Errorf("unmarshal err: %v", err)
}
t.Logf("resp: %v", string(marshalled2))
// Now send a notification.
header500, err := hex.DecodeString("00000020e9537f98ae80a3aa0936dd424439b2b9305e5e9d9d5c7aa571b4422c447741e739b3109304ed4f0330d6854271db17da221559a46b68db4ceecfebd9f0c75dbe0100000000000000000000000000000000000000000000000000000000000000b3e02063ffff7f2001000000")
if err != nil {
t.Errorf("decode err: %v", err)
}
note1 := headerNotification{
HeightHash: internal.HeightHash{Height: 500, BlockHeader: header500},
blockHeaderElectrum: nil,
blockHeaderStr: "",
}
t.Logf("sending notification")
sm.doNotify(note1)
t.Logf("waiting to receive notification(s)...")
received.Wait()
}
func TestGetBalance(t *testing.T) {
secondaryPath := "asdf"
grp := stop.NewDebug()
db, err := db.GetProdDB(regTestDBPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
s := &BlockchainAddressService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
}
for _, addr := range regTestAddrs {
req := AddressGetBalanceReq{addr}
var resp *AddressGetBalanceResp
err := s.Get_balance(&req, &resp)
if err != nil {
t.Errorf("address: %v handler err: %v", addr, err)
}
marshalled, err := json.MarshalIndent(resp, "", " ")
if err != nil {
t.Errorf("address: %v unmarshal err: %v", addr, err)
}
t.Logf("address: %v resp: %v", addr, string(marshalled))
}
}
func TestGetHistory(t *testing.T) {
secondaryPath := "asdf"
grp := stop.NewDebug()
db, err := db.GetProdDB(regTestDBPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
s := &BlockchainAddressService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
}
for _, addr := range regTestAddrs {
req := AddressGetHistoryReq{addr}
var resp *AddressGetHistoryResp
err := s.Get_history(&req, &resp)
if err != nil {
t.Errorf("address: %v handler err: %v", addr, err)
}
marshalled, err := json.MarshalIndent(resp, "", " ")
if err != nil {
t.Errorf("address: %v unmarshal err: %v", addr, err)
}
t.Logf("address: %v resp: %v", addr, string(marshalled))
}
}
func TestListUnspent(t *testing.T) {
secondaryPath := "asdf"
grp := stop.NewDebug()
db, err := db.GetProdDB(regTestDBPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
s := &BlockchainAddressService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
}
for _, addr := range regTestAddrs {
req := AddressListUnspentReq{addr}
var resp *AddressListUnspentResp
err := s.Listunspent(&req, &resp)
if err != nil {
t.Errorf("address: %v handler err: %v", addr, err)
}
marshalled, err := json.MarshalIndent(resp, "", " ")
if err != nil {
t.Errorf("address: %v unmarshal err: %v", addr, err)
}
t.Logf("address: %v resp: %v", addr, string(marshalled))
}
}
func TestAddressSubscribe(t *testing.T) {
args := MakeDefaultTestArgs()
grp := stop.NewDebug()
secondaryPath := "asdf"
db, err := db.GetProdDB(regTestDBPath, secondaryPath, grp)
defer db.Shutdown()
if err != nil {
t.Error(err)
return
}
sm := newSessionManager(nil, db, args, grp, &chaincfg.RegressionNetParams, nil)
sm.start()
defer sm.stop()
client1, server1 := net.Pipe()
sess1 := sm.addSession(server1)
client2, server2 := net.Pipe()
sess2 := sm.addSession(server2)
// Set up logic to read a notification.
var received sync.WaitGroup
recv := func(client net.Conn) {
buf := make([]byte, 1024)
len, err := client.Read(buf)
if err != nil {
t.Errorf("read err: %v", err)
}
t.Logf("len: %v notification: %v", len, string(buf))
received.Done()
}
received.Add(2)
go recv(client1)
go recv(client2)
s1 := &BlockchainAddressService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
sessionMgr: sm,
session: sess1,
}
s2 := &BlockchainAddressService{
DB: db,
Chain: &chaincfg.RegressionNetParams,
sessionMgr: sm,
session: sess2,
}
addr1, addr2 := regTestAddrs[1], regTestAddrs[2]
// Subscribe to addr1 and addr2.
req1 := AddressSubscribeReq{addr1, addr2}
var resp1 *AddressSubscribeResp
err = s1.Subscribe(&req1, &resp1)
if err != nil {
t.Errorf("handler err: %v", err)
}
marshalled1, err := json.MarshalIndent(resp1, "", " ")
if err != nil {
t.Errorf("unmarshal err: %v", err)
}
// Subscribe to addr2 only.
t.Logf("resp: %v", string(marshalled1))
req2 := AddressSubscribeReq{addr2}
var resp2 *AddressSubscribeResp
err = s2.Subscribe(&req2, &resp2)
if err != nil {
t.Errorf("handler err: %v", err)
}
marshalled2, err := json.MarshalIndent(resp2, "", " ")
if err != nil {
t.Errorf("unmarshal err: %v", err)
}
t.Logf("resp: %v", string(marshalled2))
// Now send a notification for addr2.
address, _ := lbcutil.DecodeAddress(addr2, sm.chain)
script, _ := txscript.PayToAddrScript(address)
note := hashXNotification{}
copy(note.hashX[:], hashXScript(script, sm.chain))
status, err := hex.DecodeString((*resp1)[1])
if err != nil {
t.Errorf("decode err: %v", err)
}
note.status = append(note.status, []byte(status)...)
t.Logf("sending notification")
sm.doNotify(note)
t.Logf("waiting to receive notification(s)...")
received.Wait()
}

108
server/jsonrpc_claimtrie.go Normal file
View file

@ -0,0 +1,108 @@
package server
import (
"context"
"fmt"
"github.com/lbryio/herald.go/db"
"github.com/lbryio/herald.go/internal/metrics"
pb "github.com/lbryio/herald.go/protobuf/go"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
)
type ClaimtrieService struct {
DB *db.ReadOnlyDBColumnFamily
Server *Server
}
type ResolveData struct {
Data []string `json:"data"`
}
type Result struct {
Data string `json:"data"`
}
type GetClaimByIDData struct {
ClaimID string `json:"claim_id"`
}
// Resolve is the json rpc endpoint for 'blockchain.claimtrie.resolve'.
func (t *ClaimtrieService) Resolve(args *ResolveData, result **pb.Outputs) error {
log.Println("Resolve")
res, err := InternalResolve(args.Data, t.DB)
*result = res
return err
}
// Search is the json rpc endpoint for 'blockchain.claimtrie.search'.
func (t *ClaimtrieService) Search(args *pb.SearchRequest, result **pb.Outputs) error {
log.Println("Search")
if t.Server == nil {
log.Warnln("Server is nil in Search")
*result = nil
return nil
}
ctx := context.Background()
res, err := t.Server.Search(ctx, args)
*result = res
return err
}
// GetClaimByID is the json rpc endpoint for 'blockchain.claimtrie.getclaimbyid'.
func (t *ClaimtrieService) GetClaimByID(args *GetClaimByIDData, result **pb.Outputs) error {
log.Println("GetClaimByID")
if len(args.ClaimID) != 40 {
*result = nil
return fmt.Errorf("len(claim_id) != 40")
}
rows, extras, err := t.DB.GetClaimByID(args.ClaimID)
if err != nil {
*result = nil
return err
}
metrics.RequestsCount.With(prometheus.Labels{"method": "blockchain.claimtrie.getclaimbyid"}).Inc()
// FIXME: this has txos and extras and so does GetClaimById?
allTxos := make([]*pb.Output, 0)
allExtraTxos := make([]*pb.Output, 0)
for _, row := range rows {
txos, extraTxos, err := row.ToOutputs()
if err != nil {
*result = nil
return err
}
// TODO: there may be a more efficient way to do this.
allTxos = append(allTxos, txos...)
allExtraTxos = append(allExtraTxos, extraTxos...)
}
for _, extra := range extras {
txos, extraTxos, err := extra.ToOutputs()
if err != nil {
*result = nil
return err
}
// TODO: there may be a more efficient way to do this.
allTxos = append(allTxos, txos...)
allExtraTxos = append(allExtraTxos, extraTxos...)
}
res := &pb.Outputs{
Txos: allTxos,
ExtraTxos: allExtraTxos,
Total: uint32(len(allTxos) + len(allExtraTxos)),
Offset: 0, //TODO
Blocked: nil, //TODO
BlockedTotal: 0, //TODO
}
log.Warn(res)
*result = res
return nil
}

View file

@ -0,0 +1,39 @@
package server
import (
"errors"
log "github.com/sirupsen/logrus"
)
type PeersService struct {
Server *Server
// needed for subscribe/unsubscribe
sessionMgr *sessionManager
session *session
}
type PeersSubscribeReq struct {
Ip string `json:"ip"`
Host string `json:"host"`
Details []string `json:"details"`
}
type PeersSubscribeResp string
// Features is the json rpc endpoint for 'server.peers.subcribe'.
func (t *PeersService) Subscribe(req *PeersSubscribeReq, res **PeersSubscribeResp) error {
log.Println("PeersSubscribe")
// var port = "50001"
// FIXME: Get the actual port from the request details
if t.sessionMgr == nil || t.session == nil {
*res = nil
return errors.New("no session, rpc not supported")
}
t.sessionMgr.peersSubscribe(t.session, true /*subscribe*/)
*res = nil
return nil
}

88
server/jsonrpc_server.go Normal file
View file

@ -0,0 +1,88 @@
package server
import (
log "github.com/sirupsen/logrus"
)
type ServerService struct {
Args *Args
}
type ServerFeatureService struct {
Args *Args
}
type ServerFeaturesReq struct{}
type ServerFeaturesRes struct {
Hosts map[string]string `json:"hosts"`
Pruning string `json:"pruning"`
ServerVersion string `json:"server_version"`
ProtocolMin string `json:"protocol_min"`
ProtocolMax string `json:"protocol_max"`
GenesisHash string `json:"genesis_hash"`
Description string `json:"description"`
PaymentAddress string `json:"payment_address"`
DonationAddress string `json:"donation_address"`
DailyFee string `json:"daily_fee"`
HashFunction string `json:"hash_function"`
TrendingAlgorithm string `json:"trending_algorithm"`
}
// Features is the json rpc endpoint for 'server.features'.
func (t *ServerService) Features(req *ServerFeaturesReq, res **ServerFeaturesRes) error {
log.Println("Features")
features := &ServerFeaturesRes{
Hosts: map[string]string{},
Pruning: "",
ServerVersion: HUB_PROTOCOL_VERSION,
ProtocolMin: PROTOCOL_MIN,
ProtocolMax: PROTOCOL_MAX,
GenesisHash: t.Args.GenesisHash,
Description: t.Args.ServerDescription,
PaymentAddress: t.Args.PaymentAddress,
DonationAddress: t.Args.DonationAddress,
DailyFee: t.Args.DailyFee,
HashFunction: "sha256",
TrendingAlgorithm: "fast_ar",
}
*res = features
return nil
}
type ServerBannerService struct {
Args *Args
}
type ServerBannerReq struct{}
type ServerBannerRes string
// Banner is the json rpc endpoint for 'server.banner'.
func (t *ServerService) Banner(req *ServerBannerReq, res **ServerBannerRes) error {
log.Println("Banner")
*res = (*ServerBannerRes)(t.Args.Banner)
return nil
}
type ServerVersionService struct {
Args *Args
}
type ServerVersionReq [2]string // [client_name, client_version]
type ServerVersionRes [2]string // [version, protocol_version]
// Version is the json rpc endpoint for 'server.version'.
func (t *ServerService) Version(req *ServerVersionReq, res **ServerVersionRes) error {
// FIXME: We may need to do the computation of a negotiated version here.
// Also return an error if client is not supported?
result := [2]string{t.Args.ServerVersion, t.Args.ServerVersion}
*res = (*ServerVersionRes)(&result)
log.Printf("Version(%v) -> %v", *req, **res)
return nil
}

154
server/jsonrpc_service.go Normal file
View file

@ -0,0 +1,154 @@
package server
import (
"fmt"
"net"
"net/http"
"strconv"
"strings"
gorilla_mux "github.com/gorilla/mux"
gorilla_rpc "github.com/gorilla/rpc"
gorilla_json "github.com/gorilla/rpc/json"
log "github.com/sirupsen/logrus"
"golang.org/x/net/netutil"
)
type gorillaRpcCodec struct {
gorilla_rpc.Codec
}
func (c *gorillaRpcCodec) NewRequest(r *http.Request) gorilla_rpc.CodecRequest {
return &gorillaRpcCodecRequest{c.Codec.NewRequest(r)}
}
// gorillaRpcCodecRequest provides ability to rewrite the incoming
// request "method" field. For example:
// blockchain.block.get_header -> blockchain_block.Get_header
// blockchain.address.listunspent -> blockchain_address.Listunspent
// This makes the "method" string compatible with Gorilla/RPC
// requirements.
type gorillaRpcCodecRequest struct {
gorilla_rpc.CodecRequest
}
func (cr *gorillaRpcCodecRequest) Method() (string, error) {
rawMethod, err := cr.CodecRequest.Method()
if err != nil {
return rawMethod, err
}
parts := strings.Split(rawMethod, ".")
if len(parts) < 2 {
return rawMethod, fmt.Errorf("blockchain rpc: service/method ill-formed: %q", rawMethod)
}
service := strings.Join(parts[0:len(parts)-1], "_")
method := parts[len(parts)-1]
if len(method) < 1 {
return rawMethod, fmt.Errorf("blockchain rpc: method ill-formed: %q", method)
}
method = strings.ToUpper(string(method[0])) + string(method[1:])
return service + "." + method, err
}
// StartJsonRPC starts the json rpc server and registers the endpoints.
func (s *Server) StartJsonRPC() error {
// Set up the pure JSONRPC server with persistent connections/sessions.
if s.Args.JSONRPCPort != 0 {
port := ":" + strconv.Itoa(s.Args.JSONRPCPort)
laddr, err := net.ResolveTCPAddr("tcp4", port)
if err != nil {
log.Errorf("ResoveIPAddr: %v\n", err)
goto fail1
}
listener, err := net.ListenTCP("tcp4", laddr)
if err != nil {
log.Errorf("ListenTCP: %v\n", err)
goto fail1
}
log.Infof("JSONRPC server listening on %s", listener.Addr().String())
s.sessionManager.start()
acceptConnections := func(listener net.Listener) {
defer s.sessionManager.stop()
for {
conn, err := listener.Accept()
if err != nil {
log.Errorf("Accept: %v\n", err)
break
}
log.Infof("Accepted: %v", conn.RemoteAddr())
s.sessionManager.addSession(conn)
}
}
go acceptConnections(netutil.LimitListener(listener, s.sessionManager.sessionsMax))
}
fail1:
// Set up the JSONRPC over HTTP server.
if s.Args.JSONRPCHTTPPort != 0 {
s1 := gorilla_rpc.NewServer() // Create a new RPC server
// Register the type of data requested as JSON, with custom codec.
s1.RegisterCodec(&gorillaRpcCodec{gorilla_json.NewCodec()}, "application/json")
// Register "blockchain.claimtrie.*"" handlers.
claimtrieSvc := &ClaimtrieService{s.DB, s}
err := s1.RegisterTCPService(claimtrieSvc, "blockchain_claimtrie")
if err != nil {
log.Errorf("RegisterTCPService: %v\n", err)
goto fail2
}
// Register "blockchain.{block,address,scripthash,transaction}.*" handlers.
blockchainSvc := &BlockchainBlockService{s.DB, s.Chain}
err = s1.RegisterTCPService(blockchainSvc, "blockchain_block")
if err != nil {
log.Errorf("RegisterTCPService: %v\n", err)
goto fail2
}
err = s1.RegisterTCPService(&BlockchainHeadersService{s.DB, s.Chain, s.sessionManager, nil}, "blockchain_headers")
if err != nil {
log.Errorf("RegisterTCPService: %v\n", err)
goto fail2
}
err = s1.RegisterTCPService(&BlockchainAddressService{s.DB, s.Chain, s.sessionManager, nil}, "blockchain_address")
if err != nil {
log.Errorf("RegisterTCPService: %v\n", err)
goto fail2
}
err = s1.RegisterTCPService(&BlockchainScripthashService{s.DB, s.Chain, s.sessionManager, nil}, "blockchain_scripthash")
if err != nil {
log.Errorf("RegisterTCPService: %v\n", err)
goto fail2
}
err = s1.RegisterTCPService(&BlockchainTransactionService{s.DB, s.Chain, s.sessionManager}, "blockchain_transaction")
if err != nil {
log.Errorf("RegisterTCPService: %v\n", err)
goto fail2
}
// Register "server.{features,banner,version}" handlers.
serverSvc := &ServerService{s.Args}
err = s1.RegisterTCPService(serverSvc, "server")
if err != nil {
log.Errorf("RegisterTCPService: %v\n", err)
goto fail2
}
// Register "server.peers" handlers.
peersSvc := &PeersService{Server: s}
err = s1.RegisterTCPService(peersSvc, "server_peers")
if err != nil {
log.Errorf("RegisterTCPService: %v\n", err)
goto fail2
}
r := gorilla_mux.NewRouter()
r.Handle("/rpc", s1)
port := ":" + strconv.FormatUint(uint64(s.Args.JSONRPCHTTPPort), 10)
log.Infof("HTTP JSONRPC server listening on %s", port)
log.Fatal(http.ListenAndServe(port, r))
}
fail2:
return nil
}

126
server/notifier.go Normal file
View file

@ -0,0 +1,126 @@
package server
import (
"encoding/binary"
"fmt"
"net"
"github.com/lbryio/herald.go/internal"
"github.com/sirupsen/logrus"
)
const NotifierResponseLength = 40
// AddHeightSub adds a new height subscriber
func (s *Server) AddHeightSub(addr net.Addr, conn net.Conn) {
s.HeightSubsMut.Lock()
defer s.HeightSubsMut.Unlock()
s.HeightSubs[addr] = conn
}
// DoNotify sends a notification to all height subscribers
func (s *Server) DoNotify(heightHash *internal.HeightHash) error {
buff := make([]byte, NotifierResponseLength)
toDelete := make([]net.Addr, 0)
s.HeightSubsMut.RLock()
for addr, conn := range s.HeightSubs {
// struct.pack(b'>Q32s', height, block_hash)
binary.BigEndian.PutUint64(buff, heightHash.Height)
copy(buff[8:], heightHash.BlockHash[:32])
logrus.Tracef("notifying %s", addr)
n, err := conn.Write(buff)
if err != nil {
logrus.Warn(err)
toDelete = append(toDelete, addr)
}
if n != NotifierResponseLength {
logrus.Warn("not all bytes written")
}
}
s.HeightSubsMut.RUnlock()
if len(toDelete) > 0 {
s.HeightSubsMut.Lock()
for _, v := range toDelete {
delete(s.HeightSubs, v)
}
s.HeightSubsMut.Unlock()
}
return nil
}
// RunNotifier Runs the notfying action forever
func (s *Server) RunNotifier() error {
for notification := range s.NotifierChan {
switch note := notification.(type) {
case internal.HeightHash:
heightHash := note
s.DoNotify(&heightHash)
// Do we need this?
// case peerNotification:
// peer, _ := notification.(peerNotification)
// s.notifyPeerSubs(&Peer{Address: peer.address, Port: peer.port})
default:
logrus.Warn("unknown notification type")
}
s.sessionManager.doNotify(notification)
}
return nil
}
// NotifierServer implements the TCP protocol for height/blockheader notifications
func (s *Server) NotifierServer() error {
s.Grp.Add(1)
address := ":" + fmt.Sprintf("%d", s.Args.NotifierPort)
addr, err := net.ResolveTCPAddr("tcp", address)
if err != nil {
return err
}
listen, err := net.ListenTCP("tcp", addr)
if err != nil {
return err
}
defer listen.Close()
rdyCh := make(chan bool)
for {
var conn net.Conn
var err error
logrus.Info("Waiting for connection")
go func() {
conn, err = listen.Accept()
rdyCh <- true
}()
select {
case <-s.Grp.Ch():
s.Grp.Done()
return nil
case <-rdyCh:
logrus.Info("Connection accepted")
}
if err != nil {
logrus.Warn(err)
continue
}
addr := conn.RemoteAddr()
logrus.Println(addr)
// _, err = conn.Write([]byte(addr.String()))
// if err != nil {
// logrus.Warn(err)
// continue
// }
go s.AddHeightSub(addr, conn)
}
}

110
server/notifier_test.go Normal file
View file

@ -0,0 +1,110 @@
package server_test
import (
"encoding/hex"
"fmt"
"net"
"testing"
"time"
"github.com/lbryio/herald.go/internal"
"github.com/lbryio/herald.go/server"
"github.com/lbryio/lbry.go/v3/extras/stop"
"github.com/sirupsen/logrus"
)
const defaultBufferSize = 1024
func subReady(s *server.Server) error {
sleepTime := time.Millisecond * 100
for {
if sleepTime > time.Second {
return fmt.Errorf("timeout")
}
s.HeightSubsMut.RLock()
if len(s.HeightSubs) > 0 {
s.HeightSubsMut.RUnlock()
return nil
}
s.HeightSubsMut.RUnlock()
logrus.Warn("waiting for subscriber")
time.Sleep(sleepTime)
sleepTime = sleepTime * 2
}
}
func tcpConnReady(addr string) (net.Conn, error) {
sleepTime := time.Millisecond * 100
for {
if sleepTime > time.Second {
return nil, fmt.Errorf("timeout")
}
conn, err := net.Dial("tcp", addr)
if err != nil {
logrus.Warn(err)
time.Sleep(sleepTime)
sleepTime = sleepTime * 2
continue
}
return conn, nil
}
}
func tcpRead(conn net.Conn) ([]byte, error) {
buf := make([]byte, defaultBufferSize)
n, err := conn.Read(buf)
if err != nil {
return nil, err
}
if n != server.NotifierResponseLength {
return nil, fmt.Errorf("not all bytes read")
}
return buf[:n], nil
}
func TestNotifierServer(t *testing.T) {
args := server.MakeDefaultTestArgs()
ctx := stop.NewDebug()
hub := server.MakeHubServer(ctx, args)
go hub.NotifierServer()
go hub.RunNotifier()
addr := fmt.Sprintf(":%d", args.NotifierPort)
logrus.Info(addr)
conn, err := tcpConnReady(addr)
if err != nil {
t.Fatal(err)
}
resCh := make(chan []byte)
go func() {
logrus.Warn("waiting for response")
res, err := tcpRead(conn)
logrus.Warn("got response")
if err != nil {
logrus.Warn(err)
return
}
resCh <- res
}()
// Hacky but needed because if the reader isn't ready
// before the writer sends it won't get the data
err = subReady(hub)
if err != nil {
t.Fatal(err)
}
hash, _ := hex.DecodeString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
logrus.Warn("sending hash")
hub.NotifierChan <- internal.HeightHash{Height: 1, BlockHash: hash}
res := <-resCh
logrus.Info(string(res))
}

View file

@ -7,13 +7,12 @@ import (
"log" "log"
"math" "math"
"reflect" "reflect"
"strconv"
"strings" "strings"
"time" "time"
"github.com/lbryio/hub/internal/metrics" "github.com/lbryio/herald.go/internal"
pb "github.com/lbryio/hub/protobuf/go" "github.com/lbryio/herald.go/internal/metrics"
"github.com/lbryio/lbry.go/v2/extras/util" pb "github.com/lbryio/herald.go/protobuf/go"
"github.com/olivere/elastic/v7" "github.com/olivere/elastic/v7"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"golang.org/x/text/cases" "golang.org/x/text/cases"
@ -67,6 +66,16 @@ func StrArrToInterface(arr []string) []interface{} {
return searchVals return searchVals
} }
// Int32ArrToInterface takes an array of int32 and returns them as an array of
// interfaces.
func Int32ArrToInterface(arr []int32) []interface{} {
searchVals := make([]interface{}, len(arr))
for i := 0; i < len(arr); i++ {
searchVals[i] = arr[i]
}
return searchVals
}
// AddTermsField takes an es bool query, array of string values and a term // AddTermsField takes an es bool query, array of string values and a term
// name and adds a TermsQuery for that name matching those values to the // name and adds a TermsQuery for that name matching those values to the
// bool query. // bool query.
@ -78,6 +87,17 @@ func AddTermsField(q *elastic.BoolQuery, arr []string, name string) *elastic.Boo
return q.Must(elastic.NewTermsQuery(name, searchVals...)) return q.Must(elastic.NewTermsQuery(name, searchVals...))
} }
// AddTermsFieldInt32 takes an es bool query, array of int32 values and a term
// name and adds a TermsQuery for that name matching those values to the
// bool query.
func AddTermsFieldInt32(q *elastic.BoolQuery, arr []int32, name string) *elastic.BoolQuery {
if len(arr) == 0 {
return q
}
searchVals := Int32ArrToInterface(arr)
return q.Must(elastic.NewTermsQuery(name, searchVals...))
}
// AddTermField takes an es bool query, a string value and a term name // AddTermField takes an es bool query, a string value and a term name
// and adds a TermQuery for that name matching that value to the bool // and adds a TermQuery for that name matching that value to the bool
// query. // query.
@ -104,27 +124,33 @@ func AddIndividualTermFields(q *elastic.BoolQuery, arr []string, name string, in
// AddRangeField takes a bool query, a range field struct and a term name // AddRangeField takes a bool query, a range field struct and a term name
// and adds a term query for that name matching that range field. // and adds a term query for that name matching that range field.
func AddRangeField(q *elastic.BoolQuery, rq *pb.RangeField, name string) *elastic.BoolQuery { func AddRangeField(q *elastic.BoolQuery, rqs []*pb.RangeField, name string) *elastic.BoolQuery {
if rq == nil { if len(rqs) == 0 {
return q return q
} }
if len(rq.Value) > 1 {
if rq.Op != pb.RangeField_EQ { for _, rq := range rqs {
return q if len(rq.Value) > 1 {
if rq.Op != pb.RangeField_EQ {
continue
}
q = AddTermsFieldInt32(q, rq.Value, name)
continue
}
if rq.Op == pb.RangeField_EQ {
q = q.Must(elastic.NewTermQuery(name, rq.Value[0]))
} else if rq.Op == pb.RangeField_LT {
q = q.Must(elastic.NewRangeQuery(name).Lt(rq.Value[0]))
} else if rq.Op == pb.RangeField_LTE {
q = q.Must(elastic.NewRangeQuery(name).Lte(rq.Value[0]))
} else if rq.Op == pb.RangeField_GT {
q = q.Must(elastic.NewRangeQuery(name).Gt(rq.Value[0]))
} else { // pb.RangeField_GTE
q = q.Must(elastic.NewRangeQuery(name).Gte(rq.Value[0]))
} }
return AddTermsField(q, rq.Value, name)
}
if rq.Op == pb.RangeField_EQ {
return q.Must(elastic.NewTermQuery(name, rq.Value[0]))
} else if rq.Op == pb.RangeField_LT {
return q.Must(elastic.NewRangeQuery(name).Lt(rq.Value[0]))
} else if rq.Op == pb.RangeField_LTE {
return q.Must(elastic.NewRangeQuery(name).Lte(rq.Value[0]))
} else if rq.Op == pb.RangeField_GT {
return q.Must(elastic.NewRangeQuery(name).Gt(rq.Value[0]))
} else { // pb.RangeField_GTE
return q.Must(elastic.NewRangeQuery(name).Gte(rq.Value[0]))
} }
return q
} }
// AddInvertibleField takes a bool query, an invertible field and a term name // AddInvertibleField takes a bool query, an invertible field and a term name
@ -155,29 +181,40 @@ func (s *Server) recordErrorAndDie(err error) {
// RoundUpReleaseTime take a bool query, a range query and a term name // RoundUpReleaseTime take a bool query, a range query and a term name
// and adds a term query for that name (this is for the release time // and adds a term query for that name (this is for the release time
// field) with the value rounded up. // field) with the value rounded up.
func RoundUpReleaseTime(q *elastic.BoolQuery, rq *pb.RangeField, name string) *elastic.BoolQuery { func RoundUpReleaseTime(q *elastic.BoolQuery, rqs []*pb.RangeField, name string) *elastic.BoolQuery {
if rq == nil { if len(rqs) == 0 {
return q return q
} }
releaseTimeInt, err := strconv.ParseInt(rq.Value[0], 10, 32) for _, rq := range rqs {
if err != nil { releaseTimeInt := rq.Value[0]
return q
} if releaseTimeInt < 0 {
if releaseTimeInt < 0 { releaseTimeInt *= -1
releaseTimeInt *= - 1 }
} releaseTime := ((releaseTimeInt / 360) + 1) * 360
releaseTime := strconv.Itoa(int(((releaseTimeInt / 360) + 1) * 360)) if rq.Op == pb.RangeField_EQ {
if rq.Op == pb.RangeField_EQ { q = q.Must(elastic.NewTermQuery(name, releaseTime))
return q.Must(elastic.NewTermQuery(name, releaseTime)) } else if rq.Op == pb.RangeField_LT {
} else if rq.Op == pb.RangeField_LT { q = q.Must(elastic.NewRangeQuery(name).Lt(releaseTime))
return q.Must(elastic.NewRangeQuery(name).Lt(releaseTime)) } else if rq.Op == pb.RangeField_LTE {
} else if rq.Op == pb.RangeField_LTE { q = q.Must(elastic.NewRangeQuery(name).Lte(releaseTime))
return q.Must(elastic.NewRangeQuery(name).Lte(releaseTime)) } else if rq.Op == pb.RangeField_GT {
} else if rq.Op == pb.RangeField_GT { q = q.Must(elastic.NewRangeQuery(name).Gt(releaseTime))
return q.Must(elastic.NewRangeQuery(name).Gt(releaseTime)) } else { // pb.RangeField_GTE
} else { // pb.RangeField_GTE q = q.Must(elastic.NewRangeQuery(name).Gte(releaseTime))
return q.Must(elastic.NewRangeQuery(name).Gte(releaseTime)) }
} }
return q
}
// FieldUnsetOr takes a bool query, and a term name. A query is constructed
// such that either the field does not exist OR the field exists and matches
// the original query.
func FieldUnsetOr(q *elastic.BoolQuery, name string) *elastic.BoolQuery {
qUnset := elastic.NewBoolQuery().MustNot(elastic.NewExistsQuery(name))
qSet := elastic.NewBoolQuery().Must(elastic.NewExistsQuery(name)).Must(q)
return elastic.NewBoolQuery().Should(qUnset, qSet)
} }
// Search /* // Search /*
@ -232,6 +269,7 @@ func (s *Server) Search(ctx context.Context, in *pb.SearchRequest) (*pb.Outputs,
res, err := client.IndexStats(searchIndices[0]).Do(ctx) res, err := client.IndexStats(searchIndices[0]).Do(ctx)
if err != nil { if err != nil {
log.Printf("Error on ES index stats\n%v\n", err) log.Printf("Error on ES index stats\n%v\n", err)
return &pb.Outputs{}, nil
} }
numRefreshes := res.Indices[searchIndices[0]].Primaries.Refresh.Total numRefreshes := res.Indices[searchIndices[0]].Primaries.Refresh.Total
if numRefreshes != s.NumESRefreshes { if numRefreshes != s.NumESRefreshes {
@ -247,16 +285,16 @@ func (s *Server) Search(ctx context.Context, in *pb.SearchRequest) (*pb.Outputs,
setPageVars(in, &pageSize, &from) setPageVars(in, &pageSize, &from)
/* /*
cache based on search request params cache based on search request params
include from value and number of results. include from value and number of results.
When another search request comes in with same search params When another search request comes in with same search params
and same or increased offset (which we currently don't even use?) and same or increased offset (which we currently don't even use?)
that will be a cache hit. that will be a cache hit.
FIXME: For now the cache is turned off when in debugging mode FIXME: For now the cache is turned off when in debugging mode
(for unit tests) because it breaks on some of them. (for unit tests) because it breaks on some of them.
FIXME: Currently the cache just skips the initial search, FIXME: Currently the cache just skips the initial search,
the mgets and post processing are still done. There's probably the mgets and post processing are still done. There's probably
a more efficient way to store the final result. a more efficient way to store the final result.
*/ */
if val, err := s.QueryCache.Get(cacheKey); err != nil { if val, err := s.QueryCache.Get(cacheKey); err != nil {
@ -447,7 +485,7 @@ func (s *Server) checkQuery(in *pb.SearchRequest) error {
} }
for name, failed := range checks { for name, failed := range checks {
if failed { if failed {
time.Sleep(2) // throttle time.Sleep(time.Second * 2) // throttle
return fmt.Errorf("%s cant have more than %d items.", name, limit) return fmt.Errorf("%s cant have more than %d items.", name, limit)
} }
} }
@ -490,15 +528,15 @@ func (s *Server) setupEsQuery(
} }
replacements := map[string]string{ replacements := map[string]string{
"name": "normalized_name", "name": "normalized_name",
"normalized": "normalized_name", "normalized": "normalized_name",
"claim_name": "normalized_name", "claim_name": "normalized_name",
"txid": "tx_id", "txid": "tx_id",
"nout": "tx_nout", "nout": "tx_nout",
"reposted": "repost_count", "reposted": "repost_count",
"valid_channel_signature": "is_signature_valid", "valid_channel_signature": "is_signature_valid",
"claim_id": "_id", "claim_id": "_id",
"signature_digest": "signature", "signature_digest": "signature",
} }
textFields := map[string]bool{ textFields := map[string]bool{
@ -527,7 +565,7 @@ func (s *Server) setupEsQuery(
} }
if len(in.ClaimName) > 0 { if len(in.ClaimName) > 0 {
in.NormalizedName = util.NormalizeName(in.ClaimName) in.NormalizedName = internal.NormalizeName(in.ClaimName)
} }
if len(in.OrderBy) > 0 { if len(in.OrderBy) > 0 {
@ -567,6 +605,10 @@ func (s *Server) setupEsQuery(
q = q.Must(elastic.NewTermsQuery("stream_type", searchVals...)) q = q.Must(elastic.NewTermsQuery("stream_type", searchVals...))
} }
if in.SdHash != "" {
q.Must(elastic.NewPrefixQuery("sd_hash.keyword", in.SdHash))
}
if in.ClaimId != nil { if in.ClaimId != nil {
searchVals := StrArrToInterface(in.ClaimId.Value) searchVals := StrArrToInterface(in.ClaimId.Value)
if len(in.ClaimId.Value) == 1 && len(in.ClaimId.Value[0]) < 20 { if len(in.ClaimId.Value) == 1 && len(in.ClaimId.Value[0]) < 20 {
@ -640,12 +682,12 @@ func (s *Server) setupEsQuery(
q = AddRangeField(q, in.CreationHeight, "creation_height") q = AddRangeField(q, in.CreationHeight, "creation_height")
q = AddRangeField(q, in.ActivationHeight, "activation_height") q = AddRangeField(q, in.ActivationHeight, "activation_height")
q = AddRangeField(q, in.ExpirationHeight, "expiration_height") q = AddRangeField(q, in.ExpirationHeight, "expiration_height")
q = RoundUpReleaseTime(q, in.ReleaseTime, "release_time") qReleaseTime := RoundUpReleaseTime(elastic.NewBoolQuery(), in.ReleaseTime, "release_time")
q = q.Must(FieldUnsetOr(qReleaseTime, "release_time"))
q = AddRangeField(q, in.RepostCount, "repost_count") q = AddRangeField(q, in.RepostCount, "repost_count")
q = AddRangeField(q, in.FeeAmount, "fee_amount") q = AddRangeField(q, in.FeeAmount, "fee_amount")
q = AddRangeField(q, in.Duration, "duration") q = AddRangeField(q, in.Duration, "duration")
q = AddRangeField(q, in.CensorType, "censor_type") q = AddRangeField(q, in.CensorType, "censor_type")
q = AddRangeField(q, in.ChannelJoin, "channel_join")
q = AddRangeField(q, in.EffectiveAmount, "effective_amount") q = AddRangeField(q, in.EffectiveAmount, "effective_amount")
q = AddRangeField(q, in.SupportAmount, "support_amount") q = AddRangeField(q, in.SupportAmount, "support_amount")
q = AddRangeField(q, in.TrendingScore, "trending_score") q = AddRangeField(q, in.TrendingScore, "trending_score")
@ -845,7 +887,7 @@ func searchAhead(searchHits []*record, pageSize int, perChannelPerPage int) []*r
// struct. // struct.
func (r *record) recordToOutput() *pb.Output { func (r *record) recordToOutput() *pb.Output {
return &pb.Output{ return &pb.Output{
TxHash: util.TxIdToTxHash(r.Txid), TxHash: internal.TxIdToTxHash(r.Txid),
Nout: r.Nout, Nout: r.Nout,
Height: r.Height, Height: r.Height,
Meta: &pb.Output_Claim{ Meta: &pb.Output_Claim{
@ -940,4 +982,3 @@ func removeBlocked(searchHits []*record) ([]*record, []*record, map[string]*pb.B
return newHits, blockedHits, blockedChannels return newHits, blockedHits, blockedChannels
} }

71
server/search_test.go Normal file
View file

@ -0,0 +1,71 @@
package server_test
import (
"context"
"fmt"
"log"
"net/http"
"net/http/httptest"
"strings"
"testing"
pb "github.com/lbryio/herald.go/protobuf/go"
server "github.com/lbryio/herald.go/server"
"github.com/lbryio/lbry.go/v3/extras/stop"
"github.com/olivere/elastic/v7"
)
func TestInt32ArrToInterface(t *testing.T) {
want := []int32{0, 10, 100}
got := server.Int32ArrToInterface(want)
for i, x := range got {
if x.(int32) != want[i] {
t.Errorf("flags: got: %v, want: %v\n", x, want[i])
}
}
}
func TestStrArrToInterface(t *testing.T) {
want := []string{"asdf", "qwer", "xczv"}
got := server.StrArrToInterface(want)
for i, x := range got {
if strings.Compare(x.(string), want[i]) != 0 {
t.Errorf("flags: got: %v, want: %v\n", x, want[i])
}
}
}
func TestAddTermsField(t *testing.T) {
name := "qwer"
arr := []string{"a", "b", "c"}
var query *elastic.BoolQuery = elastic.NewBoolQuery()
query = server.AddTermsField(query, arr, name)
fmt.Printf("query: %v\n", query)
}
func TestSearch(t *testing.T) {
handler := http.NotFound
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handler(w, r)
}))
defer ts.Close()
handler = func(w http.ResponseWriter, r *http.Request) {
resp := `{}`
w.Write([]byte(resp))
}
ctx := context.Background()
stopGroup := stop.NewDebug()
args := server.MakeDefaultTestArgs()
hubServer := server.MakeHubServer(stopGroup, args)
req := &pb.SearchRequest{
Text: "asdf",
}
out, err := hubServer.Search(ctx, req)
if err != nil {
log.Println(err)
}
log.Println(out)
}

View file

@ -3,23 +3,32 @@ package server
import ( import (
"context" "context"
"crypto/sha256" "crypto/sha256"
"encoding/hex"
"errors"
"fmt" "fmt"
"hash" "hash"
"log" "io/ioutil"
golog "log"
"net" "net"
"net/http" "net/http"
"os" "os"
"regexp" "regexp"
"strconv"
"sync" "sync"
"time" "time"
"github.com/ReneKroon/ttlcache/v2" "github.com/ReneKroon/ttlcache/v2"
"github.com/lbryio/hub/internal/metrics" "github.com/lbryio/herald.go/db"
"github.com/lbryio/hub/meta" "github.com/lbryio/herald.go/internal/metrics"
pb "github.com/lbryio/hub/protobuf/go" "github.com/lbryio/herald.go/meta"
pb "github.com/lbryio/herald.go/protobuf/go"
"github.com/lbryio/lbcd/chaincfg"
lbcd "github.com/lbryio/lbcd/rpcclient"
"github.com/lbryio/lbry.go/v3/extras/stop"
"github.com/olivere/elastic/v7" "github.com/olivere/elastic/v7"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/reflection" "google.golang.org/grpc/reflection"
) )
@ -29,23 +38,31 @@ type Server struct {
Args *Args Args *Args
MultiSpaceRe *regexp.Regexp MultiSpaceRe *regexp.Regexp
WeirdCharsRe *regexp.Regexp WeirdCharsRe *regexp.Regexp
DB *db.ReadOnlyDBColumnFamily
Chain *chaincfg.Params
DaemonClient *lbcd.Client
EsClient *elastic.Client EsClient *elastic.Client
QueryCache *ttlcache.Cache QueryCache *ttlcache.Cache
S256 *hash.Hash S256 *hash.Hash
LastRefreshCheck time.Time LastRefreshCheck time.Time
RefreshDelta time.Duration RefreshDelta time.Duration
NumESRefreshes int64 NumESRefreshes int64
PeerServers map[string]*FederatedServer PeerServers map[string]*Peer
PeerServersMut sync.RWMutex PeerServersMut sync.RWMutex
NumPeerServers *int64 NumPeerServers *int64
PeerSubs map[string]*FederatedServer PeerSubs map[string]*Peer
PeerSubsMut sync.RWMutex PeerSubsMut sync.RWMutex
NumPeerSubs *int64 NumPeerSubs *int64
Subscribed bool ExternalIP net.IP
HeightSubs map[net.Addr]net.Conn
HeightSubsMut sync.RWMutex
NotifierChan chan interface{}
Grp *stop.Group
notiferListener *net.TCPListener
sessionManager *sessionManager
pb.UnimplementedHubServer pb.UnimplementedHubServer
} }
func getVersion() string { func getVersion() string {
return meta.Version return meta.Version
} }
@ -89,7 +106,8 @@ func getVersion() string {
'blockchain.address.unsubscribe' 'blockchain.address.unsubscribe'
*/ */
func (s *Server) PeerSubsLoadOrStore(peer *FederatedServer) (actual *FederatedServer, loaded bool) { // PeerSubsLoadOrStore thread safe load or store for peer subs
func (s *Server) PeerSubsLoadOrStore(peer *Peer) (actual *Peer, loaded bool) {
key := peer.peerKey() key := peer.peerKey()
s.PeerSubsMut.RLock() s.PeerSubsMut.RLock()
if actual, ok := s.PeerSubs[key]; ok { if actual, ok := s.PeerSubs[key]; ok {
@ -104,7 +122,8 @@ func (s *Server) PeerSubsLoadOrStore(peer *FederatedServer) (actual *FederatedSe
} }
} }
func (s *Server) PeerServersLoadOrStore(peer *FederatedServer) (actual *FederatedServer, loaded bool) { // PeerServersLoadOrStore thread safe load or store for peer servers
func (s *Server) PeerServersLoadOrStore(peer *Peer) (actual *Peer, loaded bool) {
key := peer.peerKey() key := peer.peerKey()
s.PeerServersMut.RLock() s.PeerServersMut.RLock()
if actual, ok := s.PeerServers[key]; ok { if actual, ok := s.PeerServers[key]; ok {
@ -119,8 +138,10 @@ func (s *Server) PeerServersLoadOrStore(peer *FederatedServer) (actual *Federate
} }
} }
// Run "main" function for starting the server. This blocks.
func (s *Server) Run() { func (s *Server) Run() {
l, err := net.Listen("tcp", ":"+s.Args.Port) address := ":" + strconv.Itoa(s.Args.Port)
l, err := net.Listen("tcp", address)
if err != nil { if err != nil {
log.Fatalf("failed to listen: %v", err) log.Fatalf("failed to listen: %v", err)
} }
@ -128,18 +149,102 @@ func (s *Server) Run() {
pb.RegisterHubServer(s.GrpcServer, s) pb.RegisterHubServer(s.GrpcServer, s)
reflection.Register(s.GrpcServer) reflection.Register(s.GrpcServer)
log.Printf("listening on %s\n", l.Addr().String()) log.Printf("Server.Run() #### listening on %s\n", l.Addr().String())
log.Println(s.Args) log.Printf("%#v\n", s.Args)
if err := s.GrpcServer.Serve(l); err != nil { if err := s.GrpcServer.Serve(l); err != nil {
log.Fatalf("failed to serve: %v", err) log.Fatalf("failed to serve: %v", err)
} }
} }
func (s *Server) Stop() {
log.Println("Shutting down server...")
if s.EsClient != nil {
log.Println("Stopping es client...")
s.EsClient.Stop()
}
if s.GrpcServer != nil {
log.Println("Stopping grpc server...")
s.GrpcServer.GracefulStop()
}
log.Println("Stopping other server threads...")
s.Grp.StopAndWait()
if s.DB != nil {
log.Println("Stopping database connection...")
s.DB.Shutdown()
}
log.Println("Returning from Stop...")
}
func LoadDatabase(args *Args, grp *stop.Group) (*db.ReadOnlyDBColumnFamily, error) {
tmpName, err := os.MkdirTemp("", "go-lbry-hub")
if err != nil {
log.Info(err)
log.Fatal(err)
}
log.Info("tmpName", tmpName)
if err != nil {
log.Info(err)
}
myDB, err := db.GetProdDB(args.DBPath, tmpName, grp)
if err != nil {
// Can't load the db, fail loudly
log.Info(err)
log.Fatalln(err)
}
if myDB.LastState != nil {
log.Infof("DB version: %v", myDB.LastState.DBVersion)
log.Infof("height: %v", myDB.LastState.Height)
log.Infof("genesis: %v", myDB.LastState.Genesis.String())
log.Infof("tip: %v", myDB.LastState.Tip.String())
log.Infof("tx count: %v", myDB.LastState.TxCount)
}
blockingChannelHashes := make([][]byte, 0, 10)
blockingIds := make([]string, 0, 10)
filteringChannelHashes := make([][]byte, 0, 10)
filteringIds := make([]string, 0, 10)
for _, id := range args.BlockingChannelIds {
hash, err := hex.DecodeString(id)
if err != nil {
log.Warn("Invalid channel id: ", id)
continue
}
blockingChannelHashes = append(blockingChannelHashes, hash)
blockingIds = append(blockingIds, id)
}
for _, id := range args.FilteringChannelIds {
hash, err := hex.DecodeString(id)
if err != nil {
log.Warn("Invalid channel id: ", id)
continue
}
filteringChannelHashes = append(filteringChannelHashes, hash)
filteringIds = append(filteringIds, id)
}
myDB.BlockingChannelHashes = blockingChannelHashes
myDB.FilteringChannelHashes = filteringChannelHashes
if len(filteringIds) > 0 {
log.Infof("filtering claims reposted by channels: %+s", filteringIds)
}
if len(blockingIds) > 0 {
log.Infof("blocking claims reposted by channels: %+s", blockingIds)
}
return myDB, nil
}
// MakeHubServer takes the arguments given to a hub when it's started and // MakeHubServer takes the arguments given to a hub when it's started and
// initializes everything. It loads information about previously known peers, // initializes everything. It loads information about previously known peers,
// creates needed internal data structures, and initializes goroutines. // creates needed internal data structures, and initializes goroutines.
func MakeHubServer(ctx context.Context, args *Args) *Server { func MakeHubServer(grp *stop.Group, args *Args) *Server {
grpcServer := grpc.NewServer(grpc.NumStreamWorkers(10)) grpcServer := grpc.NewServer(grpc.NumStreamWorkers(0))
multiSpaceRe, err := regexp.Compile(`\s{2,}`) multiSpaceRe, err := regexp.Compile(`\s{2,}`)
if err != nil { if err != nil {
@ -151,9 +256,34 @@ func MakeHubServer(ctx context.Context, args *Args) *Server {
log.Fatal(err) log.Fatal(err)
} }
var client *elastic.Client = nil var lbcdClient *lbcd.Client = nil
if args.DaemonURL != nil && args.DaemonURL.Host != "" {
var rpcCertificate []byte
if args.DaemonCAPath != "" {
rpcCertificate, err = ioutil.ReadFile(args.DaemonCAPath)
if err != nil {
log.Fatalf("failed to read SSL certificate from path: %v", args.DaemonCAPath)
}
}
log.Warnf("connecting to lbcd daemon at %v...", args.DaemonURL.Host)
password, _ := args.DaemonURL.User.Password()
cfg := &lbcd.ConnConfig{
Host: args.DaemonURL.Host,
User: args.DaemonURL.User.Username(),
Pass: password,
HTTPPostMode: true,
DisableTLS: rpcCertificate == nil,
Certificates: rpcCertificate,
}
lbcdClient, err = lbcd.New(cfg, nil)
if err != nil {
log.Fatalf("lbcd daemon connection failed: %v", err)
}
}
var esClient *elastic.Client = nil
if !args.DisableEs { if !args.DisableEs {
esUrl := args.EsHost + ":" + args.EsPort esUrl := args.EsHost + ":" + fmt.Sprintf("%d", args.EsPort)
opts := []elastic.ClientOptionFunc{ opts := []elastic.ClientOptionFunc{
elastic.SetSniff(true), elastic.SetSniff(true),
elastic.SetSnifferTimeoutStartup(time.Second * 60), elastic.SetSnifferTimeoutStartup(time.Second * 60),
@ -161,9 +291,9 @@ func MakeHubServer(ctx context.Context, args *Args) *Server {
elastic.SetURL(esUrl), elastic.SetURL(esUrl),
} }
if args.Debug { if args.Debug {
opts = append(opts, elastic.SetTraceLog(log.New(os.Stderr, "[[ELASTIC]]", 0))) opts = append(opts, elastic.SetTraceLog(golog.New(os.Stderr, "[[ELASTIC]]", 0)))
} }
client, err = elastic.NewClient(opts...) esClient, err = elastic.NewClient(opts...)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -185,44 +315,143 @@ func MakeHubServer(ctx context.Context, args *Args) *Server {
numSubs := new(int64) numSubs := new(int64)
*numSubs = 0 *numSubs = 0
//TODO: is this the right place to load the db?
var myDB *db.ReadOnlyDBColumnFamily
if !args.DisableResolve {
myDB, err = LoadDatabase(args, grp)
if err != nil {
log.Warning(err)
}
}
// Determine which chain to use based on db and cli values
dbChain := (*chaincfg.Params)(nil)
if myDB != nil && myDB.LastState != nil {
// The chain params can be inferred from DBStateValue.
switch myDB.LastState.Genesis.Hash {
case *chaincfg.MainNetParams.GenesisHash:
dbChain = &chaincfg.MainNetParams
case *chaincfg.TestNet3Params.GenesisHash:
dbChain = &chaincfg.TestNet3Params
case *chaincfg.RegressionNetParams.GenesisHash:
dbChain = &chaincfg.RegressionNetParams
}
}
cliChain := (*chaincfg.Params)(nil)
if args.Chain != nil {
switch *args.Chain {
case chaincfg.MainNetParams.Name:
cliChain = &chaincfg.MainNetParams
case chaincfg.TestNet3Params.Name, "testnet":
cliChain = &chaincfg.TestNet3Params
case chaincfg.RegressionNetParams.Name:
cliChain = &chaincfg.RegressionNetParams
}
}
chain := chaincfg.MainNetParams
if dbChain != nil && cliChain != nil {
if dbChain != cliChain {
log.Warnf("network: %v (from db) conflicts with %v (from cli)", dbChain.Name, cliChain.Name)
}
chain = *dbChain
} else if dbChain != nil {
chain = *dbChain
} else if cliChain != nil {
chain = *cliChain
}
log.Infof("network: %v", chain.Name)
args.GenesisHash = chain.GenesisHash.String()
sessionGrp := stop.New(grp)
s := &Server{ s := &Server{
GrpcServer: grpcServer, GrpcServer: grpcServer,
Args: args, Args: args,
MultiSpaceRe: multiSpaceRe, MultiSpaceRe: multiSpaceRe,
WeirdCharsRe: weirdCharsRe, WeirdCharsRe: weirdCharsRe,
EsClient: client, DB: myDB,
Chain: &chain,
DaemonClient: lbcdClient,
EsClient: esClient,
QueryCache: cache, QueryCache: cache,
S256: &s256, S256: &s256,
LastRefreshCheck: time.Now(), LastRefreshCheck: time.Now(),
RefreshDelta: refreshDelta, RefreshDelta: refreshDelta,
NumESRefreshes: 0, NumESRefreshes: 0,
PeerServers: make(map[string]*FederatedServer), PeerServers: make(map[string]*Peer),
PeerServersMut: sync.RWMutex{}, PeerServersMut: sync.RWMutex{},
NumPeerServers: numPeers, NumPeerServers: numPeers,
PeerSubs: make(map[string]*FederatedServer), PeerSubs: make(map[string]*Peer),
PeerSubsMut: sync.RWMutex{}, PeerSubsMut: sync.RWMutex{},
NumPeerSubs: numSubs, NumPeerSubs: numSubs,
Subscribed: false, ExternalIP: net.IPv4(127, 0, 0, 1),
HeightSubs: make(map[net.Addr]net.Conn),
HeightSubsMut: sync.RWMutex{},
NotifierChan: make(chan interface{}, 1),
Grp: grp,
sessionManager: nil,
} }
// FIXME: HACK
s.sessionManager = newSessionManager(s, myDB, args, sessionGrp, &chain, lbcdClient)
// Start up our background services // Start up our background services
if args.StartPrometheus { if !args.DisableResolve && !args.DisableRocksDBRefresh {
go s.prometheusEndpoint(s.Args.PrometheusPort, "metrics") log.Info("Running detect changes")
myDB.RunDetectChanges(s.NotifierChan)
} }
if args.StartUDP { if !args.DisableBlockingAndFiltering {
myDB.RunGetBlocksAndFilters()
}
if !args.DisableStartPrometheus {
go s.prometheusEndpoint(fmt.Sprintf("%d", s.Args.PrometheusPort), "metrics")
}
if !args.DisableStartUDP {
go func() { go func() {
err := UDPServer(args) err := s.UDPServer(s.Args.Port)
if err != nil { if err != nil {
log.Println("UDP Server failed!", err) log.Errorf("UDP Server (%d) failed! %v", s.Args.Port, err)
}
}()
if s.Args.JSONRPCPort != 0 {
go func() {
err := s.UDPServer(s.Args.JSONRPCPort)
if err != nil {
log.Errorf("UDP Server (%d) failed! %v", s.Args.JSONRPCPort, err)
}
}()
}
}
if !args.DisableStartNotifier {
go func() {
err := s.NotifierServer()
if err != nil {
log.Println("Notifier Server failed!", err)
}
}()
go func() {
err := s.RunNotifier()
if err != nil {
log.Println("RunNotifier failed!", err)
}
}()
}
if !args.DisableStartJSONRPC {
go func() {
err := s.StartJsonRPC()
if err != nil {
log.Println("JSONRPC Server failed!", err)
} }
}() }()
} }
// Load peers from disk and subscribe to one if there are any // Load peers from disk and subscribe to one if there are any
if args.LoadPeers { if !args.DisableLoadPeers {
err = s.loadPeers() go func() {
if err != nil { err := s.loadPeers()
log.Println(err) if err != nil {
} log.Println(err)
}
}()
} }
return s return s
@ -232,7 +461,7 @@ func MakeHubServer(ctx context.Context, args *Args) *Server {
// for this hub to allow for metric tracking. // for this hub to allow for metric tracking.
func (s *Server) prometheusEndpoint(port string, endpoint string) { func (s *Server) prometheusEndpoint(port string, endpoint string) {
http.Handle("/"+endpoint, promhttp.Handler()) http.Handle("/"+endpoint, promhttp.Handler())
log.Println(fmt.Sprintf("listening on :%s /%s", port, endpoint)) log.Printf("listening on :%s /%s\n", port, endpoint)
err := http.ListenAndServe(":"+port, nil) err := http.ListenAndServe(":"+port, nil)
log.Fatalln("Shouldn't happen??!?!", err) log.Fatalln("Shouldn't happen??!?!", err)
} }
@ -244,21 +473,21 @@ func (s *Server) Hello(ctx context.Context, args *pb.HelloMessage) (*pb.HelloMes
metrics.RequestsCount.With(prometheus.Labels{"method": "hello"}).Inc() metrics.RequestsCount.With(prometheus.Labels{"method": "hello"}).Inc()
port := args.Port port := args.Port
host := args.Host host := args.Host
server := &FederatedServer{ newPeer := &Peer{
Address: host, Address: host,
Port: port, Port: port,
Ts: time.Now(), LastSeen: time.Now(),
} }
log.Println(server) log.Println(newPeer)
err := s.addPeer(&pb.ServerMessage{Address: host, Port: port}, false) err := s.addPeer(newPeer, false, true)
// They just contacted us, so this shouldn't happen // They just contacted us, so this shouldn't happen
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
s.mergeFederatedServers(args.Servers) s.mergePeers(args.Servers)
s.writePeers() s.writePeers()
s.notifyPeerSubs(server) s.notifyPeerSubs(newPeer)
return s.makeHelloMessage(), nil return s.makeHelloMessage(), nil
} }
@ -268,10 +497,10 @@ func (s *Server) Hello(ctx context.Context, args *pb.HelloMessage) (*pb.HelloMes
func (s *Server) PeerSubscribe(ctx context.Context, in *pb.ServerMessage) (*pb.StringValue, error) { func (s *Server) PeerSubscribe(ctx context.Context, in *pb.ServerMessage) (*pb.StringValue, error) {
metrics.RequestsCount.With(prometheus.Labels{"method": "peer_subscribe"}).Inc() metrics.RequestsCount.With(prometheus.Labels{"method": "peer_subscribe"}).Inc()
var msg = "Success" var msg = "Success"
peer := &FederatedServer{ peer := &Peer{
Address: in.Address, Address: in.Address,
Port: in.Port, Port: in.Port,
Ts: time.Now(), LastSeen: time.Now(),
} }
if _, loaded := s.PeerSubsLoadOrStore(peer); !loaded { if _, loaded := s.PeerSubsLoadOrStore(peer); !loaded {
@ -288,7 +517,12 @@ func (s *Server) PeerSubscribe(ctx context.Context, in *pb.ServerMessage) (*pb.S
func (s *Server) AddPeer(ctx context.Context, args *pb.ServerMessage) (*pb.StringValue, error) { func (s *Server) AddPeer(ctx context.Context, args *pb.ServerMessage) (*pb.StringValue, error) {
metrics.RequestsCount.With(prometheus.Labels{"method": "add_peer"}).Inc() metrics.RequestsCount.With(prometheus.Labels{"method": "add_peer"}).Inc()
var msg = "Success" var msg = "Success"
err := s.addPeer(args, true) newPeer := &Peer{
Address: args.Address,
Port: args.Port,
LastSeen: time.Now(),
}
err := s.addPeer(newPeer, true, true)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
msg = "Failed" msg = "Failed"
@ -307,3 +541,85 @@ func (s *Server) Version(ctx context.Context, args *pb.EmptyMessage) (*pb.String
metrics.RequestsCount.With(prometheus.Labels{"method": "version"}).Inc() metrics.RequestsCount.With(prometheus.Labels{"method": "version"}).Inc()
return &pb.StringValue{Value: getVersion()}, nil return &pb.StringValue{Value: getVersion()}, nil
} }
func (s *Server) Height(ctx context.Context, args *pb.EmptyMessage) (*pb.UInt32Value, error) {
metrics.RequestsCount.With(prometheus.Labels{"method": "height"}).Inc()
if s.DB != nil {
return &pb.UInt32Value{Value: s.DB.LastState.Height}, nil
} else {
return &pb.UInt32Value{Value: 0}, nil
}
}
// HeightSubscribe takes a height to wait for the server to reach and waits until it reaches that
// height or higher and returns the current height. If the db is off it will return 0.
func (s *Server) HeightSubscribe(arg *pb.UInt32Value, stream pb.Hub_HeightSubscribeServer) error {
metrics.RequestsCount.With(prometheus.Labels{"method": "height"}).Inc()
if s.DB != nil {
want := arg.Value
for s.DB.LastState.Height < want {
if s.DB.LastState.Height >= want {
err := stream.Send(&pb.UInt32Value{Value: s.DB.LastState.Height})
if err != nil {
return err
}
return nil
}
time.Sleep(time.Millisecond * 100)
}
} else {
if err := stream.Send(&pb.UInt32Value{Value: 0}); err != nil {
return err
}
}
return nil
}
// HeightHashSubscribe takes a height to wait for the server to reach and waits until it reaches that
// height or higher and returns the current height. If the db is off it will return 0.
func (s *Server) HeightHashSubscribe() error {
metrics.RequestsCount.With(prometheus.Labels{"method": "height_hash"}).Inc()
return nil
}
// Resolve is the gRPC endpoint for resolve.
func (s *Server) Resolve(ctx context.Context, args *pb.StringArray) (*pb.Outputs, error) {
return InternalResolve(args.Value, s.DB)
}
// InternalResolve takes an array of urls and resolves them to their transactions.
func InternalResolve(urls []string, DB *db.ReadOnlyDBColumnFamily) (*pb.Outputs, error) {
if DB == nil {
return nil, errors.New("db is nil")
// return nil, nil
}
metrics.RequestsCount.With(prometheus.Labels{"method": "resolve"}).Inc()
allTxos := make([]*pb.Output, 0)
allExtraTxos := make([]*pb.Output, 0)
for _, url := range urls {
res := DB.Resolve(url)
txos, extraTxos, err := res.ToOutputs()
if err != nil {
return nil, err
}
// TODO: there may be a more efficient way to do this.
allTxos = append(allTxos, txos...)
allExtraTxos = append(allExtraTxos, extraTxos...)
}
res := &pb.Outputs{
Txos: allTxos,
ExtraTxos: allExtraTxos,
Total: uint32(len(allTxos) + len(allExtraTxos)),
Offset: 0, //TODO
Blocked: nil, //TODO
BlockedTotal: 0, //TODO
}
log.Warn(res)
return res, nil
}

19
server/server_test_pkg.go Normal file
View file

@ -0,0 +1,19 @@
package server
import (
"github.com/lbryio/herald.go/db"
"github.com/lbryio/lbcd/chaincfg"
"github.com/lbryio/lbry.go/v3/extras/stop"
)
func (s *Server) AddPeerExported() func(*Peer, bool, bool) error {
return s.addPeer
}
func (s *Server) GetNumPeersExported() func() int64 {
return s.getNumPeers
}
func NewSessionManagerExported(server *Server, db *db.ReadOnlyDBColumnFamily, args *Args, grp *stop.Group, chain *chaincfg.Params) *sessionManager {
return newSessionManager(server, db, args, grp, chain, nil)
}

631
server/session.go Normal file
View file

@ -0,0 +1,631 @@
package server
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"strings"
"sync"
"time"
"unsafe"
"github.com/lbryio/herald.go/db"
"github.com/lbryio/herald.go/internal"
"github.com/lbryio/lbcd/chaincfg"
"github.com/lbryio/lbcd/chaincfg/chainhash"
lbcd "github.com/lbryio/lbcd/rpcclient"
"github.com/lbryio/lbcd/wire"
"github.com/lbryio/lbry.go/v3/extras/stop"
log "github.com/sirupsen/logrus"
)
type headerNotification struct {
internal.HeightHash
blockHeaderElectrum *BlockHeaderElectrum
blockHeaderStr string
}
type hashXNotification struct {
hashX [HASHX_LEN]byte
status []byte
statusStr string
}
type peerNotification struct {
address string
port string
}
type session struct {
id uintptr
addr net.Addr
conn net.Conn
// hashXSubs maps hashX to the original subscription key (address or scripthash)
hashXSubs map[[HASHX_LEN]byte]string
// headersSub indicates header subscription
headersSub bool
// peersSub indicates peer subscription
peersSub bool
// headersSubRaw indicates the header subscription mode
headersSubRaw bool
// client provides the ability to send notifications
client rpc.ClientCodec
clientSeq uint64
// lastRecv records time of last incoming data
lastRecv time.Time
// lastSend records time of last outgoing data
lastSend time.Time
}
func (s *session) doNotify(notification interface{}) {
var method string
var params interface{}
switch note := notification.(type) {
case headerNotification:
if !s.headersSub {
return
}
heightHash := note.HeightHash
method = "blockchain.headers.subscribe"
if s.headersSubRaw {
header := note.blockHeaderStr
if len(header) == 0 {
header = hex.EncodeToString(note.BlockHeader[:])
}
params = &HeadersSubscribeRawResp{
Hex: header,
Height: uint32(heightHash.Height),
}
} else {
header := note.blockHeaderElectrum
if header == nil { // not initialized
header = newBlockHeaderElectrum((*[HEADER_SIZE]byte)(note.BlockHeader), uint32(heightHash.Height))
}
params = header
}
case hashXNotification:
orig, ok := s.hashXSubs[note.hashX]
if !ok {
return
}
if len(orig) == 64 {
method = "blockchain.scripthash.subscribe"
} else {
method = "blockchain.address.subscribe"
}
status := note.statusStr
if len(status) == 0 {
status = hex.EncodeToString(note.status)
}
params = []string{orig, status}
case peerNotification:
if !s.peersSub {
return
}
method = "server.peers.subscribe"
params = []string{note.address, note.port}
default:
log.Warnf("unknown notification type: %v", notification)
return
}
// Send the notification.
s.clientSeq += 1
req := &rpc.Request{
ServiceMethod: method,
Seq: s.clientSeq,
}
err := s.client.WriteRequest(req, params)
if err != nil {
log.Warnf("error: %v", err)
}
// Bump last send time.
s.lastSend = time.Now()
}
type sessionMap map[uintptr]*session
type sessionManager struct {
// sessionsMut protects sessions, headerSubs, hashXSubs state
sessionsMut sync.RWMutex
sessions sessionMap
// sessionsWait sync.WaitGroup
grp *stop.Group
sessionsMax int
sessionTimeout time.Duration
manageTicker *time.Ticker
db *db.ReadOnlyDBColumnFamily
args *Args
server *Server
chain *chaincfg.Params
lbcd *lbcd.Client
// peerSubs are sessions subscribed via 'blockchain.peers.subscribe'
peerSubs sessionMap
// headerSubs are sessions subscribed via 'blockchain.headers.subscribe'
headerSubs sessionMap
// hashXSubs are sessions subscribed via 'blockchain.{address,scripthash}.subscribe'
hashXSubs map[[HASHX_LEN]byte]sessionMap
}
func newSessionManager(server *Server, db *db.ReadOnlyDBColumnFamily, args *Args, grp *stop.Group, chain *chaincfg.Params, lbcd *lbcd.Client) *sessionManager {
return &sessionManager{
sessions: make(sessionMap),
grp: grp,
sessionsMax: args.MaxSessions,
sessionTimeout: time.Duration(args.SessionTimeout) * time.Second,
manageTicker: time.NewTicker(time.Duration(max(5, args.SessionTimeout/20)) * time.Second),
db: db,
args: args,
server: server,
chain: chain,
lbcd: lbcd,
peerSubs: make(sessionMap),
headerSubs: make(sessionMap),
hashXSubs: make(map[[HASHX_LEN]byte]sessionMap),
}
}
func (sm *sessionManager) start() {
sm.grp.Add(1)
go sm.manage()
}
func (sm *sessionManager) stop() {
sm.sessionsMut.Lock()
defer sm.sessionsMut.Unlock()
sm.headerSubs = make(sessionMap)
sm.hashXSubs = make(map[[HASHX_LEN]byte]sessionMap)
for _, sess := range sm.sessions {
sess.client.Close()
sess.conn.Close()
}
sm.sessions = make(sessionMap)
}
func (sm *sessionManager) manage() {
for {
sm.sessionsMut.Lock()
for _, sess := range sm.sessions {
if time.Since(sess.lastRecv) > sm.sessionTimeout {
sm.removeSessionLocked(sess)
log.Infof("session %v timed out", sess.addr.String())
}
}
sm.sessionsMut.Unlock()
// Wait for next management clock tick.
select {
case <-sm.grp.Ch():
sm.grp.Done()
return
case <-sm.manageTicker.C:
continue
}
}
}
func (sm *sessionManager) addSession(conn net.Conn) *session {
sm.sessionsMut.Lock()
sess := &session{
addr: conn.RemoteAddr(),
conn: conn,
hashXSubs: make(map[[11]byte]string),
client: jsonrpc.NewClientCodec(conn),
lastRecv: time.Now(),
}
sess.id = uintptr(unsafe.Pointer(sess))
sm.sessions[sess.id] = sess
sm.sessionsMut.Unlock()
// Create a new RPC server. These services are linked to the
// session, which allows RPC handlers to know the session for
// each request and update subscriptions.
s1 := rpc.NewServer()
// Register "server.{features,banner,version}" handlers.
serverSvc := &ServerService{sm.args}
err := s1.RegisterName("server", serverSvc)
if err != nil {
log.Errorf("RegisterName: %v\n", err)
}
// Register "server.peers" handlers.
peersSvc := &PeersService{Server: sm.server}
err = s1.RegisterName("server.peers", peersSvc)
if err != nil {
log.Errorf("RegisterName: %v\n", err)
}
// Register "blockchain.claimtrie.*"" handlers.
claimtrieSvc := &ClaimtrieService{sm.db, sm.server}
err = s1.RegisterName("blockchain.claimtrie", claimtrieSvc)
if err != nil {
log.Errorf("RegisterName: %v\n", err)
}
// Register "blockchain.{block,address,scripthash,transaction}.*" handlers.
blockchainSvc := &BlockchainBlockService{sm.db, sm.chain}
err = s1.RegisterName("blockchain.block", blockchainSvc)
if err != nil {
log.Errorf("RegisterName: %v\n", err)
goto fail
}
err = s1.RegisterName("blockchain.headers", &BlockchainHeadersService{sm.db, sm.chain, sm, sess})
if err != nil {
log.Errorf("RegisterName: %v\n", err)
goto fail
}
err = s1.RegisterName("blockchain.address", &BlockchainAddressService{sm.db, sm.chain, sm, sess})
if err != nil {
log.Errorf("RegisterName: %v\n", err)
goto fail
}
err = s1.RegisterName("blockchain.scripthash", &BlockchainScripthashService{sm.db, sm.chain, sm, sess})
if err != nil {
log.Errorf("RegisterName: %v\n", err)
goto fail
}
err = s1.RegisterName("blockchain.transaction", &BlockchainTransactionService{sm.db, sm.chain, sm})
if err != nil {
log.Errorf("RegisterName: %v\n", err)
goto fail
}
sm.grp.Add(1)
go func() {
s1.ServeCodec(&sessionServerCodec{jsonrpc.NewServerCodec(newJsonPatchingCodec(conn)), sess})
log.Infof("session %v goroutine exit", sess.addr.String())
sm.grp.Done()
}()
return sess
fail:
sm.removeSession(sess)
return nil
}
func (sm *sessionManager) removeSession(sess *session) {
sm.sessionsMut.Lock()
defer sm.sessionsMut.Unlock()
sm.removeSessionLocked(sess)
}
func (sm *sessionManager) removeSessionLocked(sess *session) {
if sess.headersSub {
delete(sm.headerSubs, sess.id)
}
for hashX := range sess.hashXSubs {
subs, ok := sm.hashXSubs[hashX]
if !ok {
continue
}
delete(subs, sess.id)
}
delete(sm.sessions, sess.id)
sess.client.Close()
sess.conn.Close()
}
func (sm *sessionManager) broadcastTx(rawTx []byte) (*chainhash.Hash, error) {
var msgTx wire.MsgTx
err := msgTx.Deserialize(bytes.NewReader(rawTx))
if err != nil {
return nil, err
}
return sm.lbcd.SendRawTransaction(&msgTx, false)
}
func (sm *sessionManager) peersSubscribe(sess *session, subscribe bool) {
sm.sessionsMut.Lock()
defer sm.sessionsMut.Unlock()
if subscribe {
sm.peerSubs[sess.id] = sess
sess.peersSub = true
return
}
delete(sm.peerSubs, sess.id)
sess.peersSub = false
}
func (sm *sessionManager) headersSubscribe(sess *session, raw bool, subscribe bool) {
sm.sessionsMut.Lock()
defer sm.sessionsMut.Unlock()
if subscribe {
sm.headerSubs[sess.id] = sess
sess.headersSub = true
sess.headersSubRaw = raw
return
}
delete(sm.headerSubs, sess.id)
sess.headersSub = false
sess.headersSubRaw = false
}
func (sm *sessionManager) hashXSubscribe(sess *session, hashX []byte, original string, subscribe bool) {
sm.sessionsMut.Lock()
defer sm.sessionsMut.Unlock()
var key [HASHX_LEN]byte
copy(key[:], hashX)
subs, ok := sm.hashXSubs[key]
if subscribe {
if !ok {
subs = make(sessionMap)
sm.hashXSubs[key] = subs
}
subs[sess.id] = sess
sess.hashXSubs[key] = original
return
}
if ok {
delete(subs, sess.id)
if len(subs) == 0 {
delete(sm.hashXSubs, key)
}
}
delete(sess.hashXSubs, key)
}
func (sm *sessionManager) doNotify(notification interface{}) {
switch note := notification.(type) {
case internal.HeightHash:
// The HeightHash notification translates to headerNotification.
notification = &headerNotification{HeightHash: note}
}
sm.sessionsMut.RLock()
var subsCopy sessionMap
switch note := notification.(type) {
case headerNotification:
log.Infof("header notification @ %#v", note)
subsCopy = sm.headerSubs
if len(subsCopy) > 0 {
hdr := [HEADER_SIZE]byte{}
copy(hdr[:], note.BlockHeader)
note.blockHeaderElectrum = newBlockHeaderElectrum(&hdr, uint32(note.Height))
note.blockHeaderStr = hex.EncodeToString(note.BlockHeader[:])
}
case hashXNotification:
log.Infof("hashX notification @ %#v", note)
hashXSubs, ok := sm.hashXSubs[note.hashX]
if ok {
subsCopy = hashXSubs
}
if len(subsCopy) > 0 {
note.statusStr = hex.EncodeToString(note.status)
}
case peerNotification:
subsCopy = sm.peerSubs
default:
log.Warnf("unknown notification type: %v", notification)
}
sm.sessionsMut.RUnlock()
// Deliver notification to relevant sessions.
for _, sess := range subsCopy {
sess.doNotify(notification)
}
// Produce secondary hashXNotification(s) corresponding to the headerNotification.
switch note := notification.(type) {
case headerNotification:
touched, err := sm.db.GetTouchedHashXs(uint32(note.Height))
if err != nil {
log.Errorf("failed to get touched hashXs at height %v, error: %v", note.Height, err)
break
}
for _, hashX := range touched {
hashXstatus, err := sm.db.GetStatus(hashX)
if err != nil {
log.Errorf("failed to get status of hashX %v, error: %v", hashX, err)
continue
}
note2 := hashXNotification{}
copy(note2.hashX[:], hashX)
note2.status = hashXstatus
sm.doNotify(note2)
}
}
}
type sessionServerCodec struct {
rpc.ServerCodec
sess *session
}
// ReadRequestHeader provides ability to rewrite the incoming
// request "method" field. For example:
//
// blockchain.block.get_header -> blockchain.block.Get_header
// blockchain.address.listunspent -> blockchain.address.Listunspent
//
// This makes the "method" string compatible with rpc.Server
// requirements.
func (c *sessionServerCodec) ReadRequestHeader(req *rpc.Request) error {
log.Infof("from %v receive header", c.sess.addr.String())
err := c.ServerCodec.ReadRequestHeader(req)
if err != nil {
log.Warnf("error: %v", err)
return err
}
log.Infof("from %v receive header: %#v", c.sess.addr.String(), *req)
rawMethod := req.ServiceMethod
parts := strings.Split(rawMethod, ".")
if len(parts) < 2 {
return fmt.Errorf("blockchain rpc: service/method ill-formed: %q", rawMethod)
}
service := strings.Join(parts[0:len(parts)-1], ".")
method := parts[len(parts)-1]
if len(method) < 1 {
return fmt.Errorf("blockchain rpc: method ill-formed: %q", method)
}
method = strings.ToUpper(string(method[0])) + string(method[1:])
req.ServiceMethod = service + "." + method
return err
}
// ReadRequestBody wraps the regular implementation, but updates session stats too.
func (c *sessionServerCodec) ReadRequestBody(params any) error {
log.Infof("from %v receive body", c.sess.addr.String())
err := c.ServerCodec.ReadRequestBody(params)
if err != nil {
log.Warnf("error: %v", err)
return err
}
log.Infof("from %v receive body: %#v", c.sess.addr.String(), params)
// Bump last receive time.
c.sess.lastRecv = time.Now()
return err
}
// WriteResponse wraps the regular implementation, but updates session stats too.
func (c *sessionServerCodec) WriteResponse(resp *rpc.Response, reply any) error {
log.Infof("respond to %v", c.sess.addr.String())
err := c.ServerCodec.WriteResponse(resp, reply)
if err != nil {
return err
}
// Bump last send time.
c.sess.lastSend = time.Now()
return err
}
// serverRequest is a duplicate of serverRequest from
// net/rpc/jsonrpc/server.go with an added Version which
// we can check.
type serverRequest struct {
Version string `json:"jsonrpc"`
Method string `json:"method"`
Params *json.RawMessage `json:"params"`
Id *json.RawMessage `json:"id"`
}
// serverResponse is a duplicate of serverResponse from
// net/rpc/jsonrpc/server.go with an added Version which
// we can set at will.
type serverResponse struct {
Version string `json:"jsonrpc"`
Id *json.RawMessage `json:"id"`
Result any `json:"result,omitempty"`
Error any `json:"error,omitempty"`
}
// jsonPatchingCodec is able to intercept the JSON requests/responses
// and tweak them. Currently, it appears we need to make several changes:
// 1) add "jsonrpc": "2.0" (or "jsonrpc": "1.0") in response
// 2) add newline to frame response
// 3) add "params": [] when "params" is missing
// 4) replace params ["arg1", "arg2", ...] with [["arg1", "arg2", ...]]
type jsonPatchingCodec struct {
conn net.Conn
inBuffer *bytes.Buffer
dec *json.Decoder
enc *json.Encoder
outBuffer *bytes.Buffer
}
func newJsonPatchingCodec(conn net.Conn) *jsonPatchingCodec {
buf1, buf2 := bytes.NewBuffer(nil), bytes.NewBuffer(nil)
return &jsonPatchingCodec{
conn: conn,
inBuffer: buf1,
dec: json.NewDecoder(buf1),
enc: json.NewEncoder(buf2),
outBuffer: buf2,
}
}
func (c *jsonPatchingCodec) Read(p []byte) (n int, err error) {
if c.outBuffer.Len() > 0 {
// Return remaining decoded bytes.
return c.outBuffer.Read(p)
}
// Buffer contents consumed. Try to decode more JSON.
// Read until framing newline. This allows us to print the raw request.
for !bytes.ContainsAny(c.inBuffer.Bytes(), "\n") {
var buf [1024]byte
n, err = c.conn.Read(buf[:])
if err != nil {
return 0, err
}
c.inBuffer.Write(buf[:n])
}
log.Infof("raw request: %v", c.inBuffer.String())
var req serverRequest
err = c.dec.Decode(&req)
if err != nil {
return 0, err
}
if req.Params != nil {
n := len(*req.Params)
if n < 2 || (*req.Params)[0] != '[' && (*req.Params)[n-1] != ']' {
// This is an error, but we're not going to try to correct it.
goto encode
}
// FIXME: The heuristics here don't cover all possibilities.
// For example: [{obj1}, {obj2}] or ["foo,bar"] would not
// be handled correctly.
bracketed := (*req.Params)[1 : n-1]
n = len(bracketed)
if n > 1 && (bracketed[0] == '{' || bracketed[0] == '[') {
// Probable single object or list argument.
goto encode
}
// The params look like ["arg1", "arg2", "arg3", ...].
// We're in trouble because our jsonrpc library does not
// handle this. So pack these args in an inner list.
// The handler method will receive ONE list argument.
params := json.RawMessage(fmt.Sprintf("[[%s]]", bracketed))
req.Params = &params
} else {
// Add empty argument list if params omitted.
params := json.RawMessage("[]")
req.Params = &params
}
encode:
// Encode the request. This allows us to print the patched request.
buf, err := json.Marshal(req)
if err != nil {
return 0, err
}
log.Infof("patched request: %v", string(buf))
err = c.enc.Encode(req)
if err != nil {
return 0, err
}
return c.outBuffer.Read(p)
}
func (c *jsonPatchingCodec) Write(p []byte) (n int, err error) {
log.Infof("raw response: %v", string(p))
var resp serverResponse
err = json.Unmarshal(p, &resp)
if err != nil {
return 0, err
}
// Add "jsonrpc": "2.0" if missing.
if len(resp.Version) == 0 {
resp.Version = "2.0"
}
buf, err := json.Marshal(resp)
if err != nil {
return 0, err
}
log.Infof("patched response: %v", string(buf))
// Add newline for framing.
return c.conn.Write(append(buf, '\n'))
}
func (c *jsonPatchingCodec) Close() error {
return c.conn.Close()
}

View file

@ -8,13 +8,19 @@ import (
"strings" "strings"
"time" "time"
"github.com/lbryio/lbry.go/v2/extras/errors" pb "github.com/lbryio/herald.go/protobuf/go"
) )
const maxBufferSize = 1024 const maxBufferSize = 1024
// genesis blocktime (which is actually wrong) // genesis blocktime (which is actually wrong)
const magic = 1446058291 // magic constant for the UDPPing protocol. The above comment is taken from
// the python code this was implemented off of.
// https://github.com/lbryio/lbry-sdk/blob/7d49b046d44a4b7067d5dc1d6cd65ff0475c71c8/lbry/wallet/server/udp.py#L12
const magic = 1446058291
const protocolVersion = 1 const protocolVersion = 1
const defaultFlags = 0b00000000
const availableFlag = 0b00000001
// SPVPing is a struct for the format of how to ping another hub over udp. // SPVPing is a struct for the format of how to ping another hub over udp.
// format b'!lB64s' // format b'!lB64s'
@ -31,7 +37,7 @@ type SPVPong struct {
flags byte flags byte
height uint32 height uint32
tip []byte // 32 tip []byte // 32
srcAddrRaw []byte // 4 srcAddrRaw []byte // 4
country uint16 country uint16
} }
@ -55,7 +61,7 @@ func decodeSPVPing(data []byte) *SPVPing {
parsedMagic := binary.BigEndian.Uint32(data) parsedMagic := binary.BigEndian.Uint32(data)
parsedProtocalVersion := data[4] parsedProtocalVersion := data[4]
return &SPVPing{ return &SPVPing{
magic: parsedMagic, magic: parsedMagic,
version: parsedProtocalVersion, version: parsedProtocalVersion,
} }
} }
@ -65,7 +71,7 @@ func decodeSPVPing(data []byte) *SPVPing {
func (pong *SPVPong) Encode() []byte { func (pong *SPVPong) Encode() []byte {
data := make([]byte, 44) data := make([]byte, 44)
data[0] = pong.protocolVersion data[0] = pong.protocolVersion
data[1] = pong.flags data[1] = pong.flags
binary.BigEndian.PutUint32(data[2:], pong.height) binary.BigEndian.PutUint32(data[2:], pong.height)
copy(data[6:], pong.tip) copy(data[6:], pong.tip)
@ -76,10 +82,13 @@ func (pong *SPVPong) Encode() []byte {
} }
// makeSPVPong creates an SPVPong struct according to given parameters. // makeSPVPong creates an SPVPong struct according to given parameters.
// FIXME: Currently, does not correctly encode the country.
func makeSPVPong(flags int, height int, tip []byte, sourceAddr string, country string) *SPVPong { func makeSPVPong(flags int, height int, tip []byte, sourceAddr string, country string) *SPVPong {
byteAddr := EncodeAddress(sourceAddr) byteAddr := EncodeAddress(sourceAddr)
countryInt := 1 var countryInt int32
var ok bool
if countryInt, ok = pb.Location_Country_value[country]; !ok {
countryInt = int32(pb.Location_UNKNOWN_COUNTRY)
}
return &SPVPong{ return &SPVPong{
protocolVersion: protocolVersion, protocolVersion: protocolVersion,
flags: byte(flags), flags: byte(flags),
@ -99,19 +108,19 @@ func decodeSPVPong(data []byte) *SPVPong {
parsedProtocalVersion := data[0] parsedProtocalVersion := data[0]
flags := data[1] flags := data[1]
height := binary.BigEndian.Uint32(data[:2]) height := binary.BigEndian.Uint32(data[2:])
tip := make([]byte, 32) tip := make([]byte, 32)
copy(tip, data[6:38]) copy(tip, data[6:38])
srcRawAddr := make([]byte, 4) srcRawAddr := make([]byte, 4)
copy(srcRawAddr, data[38:42]) copy(srcRawAddr, data[38:42])
country := binary.BigEndian.Uint16(data[:42]) country := binary.BigEndian.Uint16(data[42:])
return &SPVPong{ return &SPVPong{
protocolVersion: parsedProtocalVersion, protocolVersion: parsedProtocalVersion,
flags: flags, flags: flags,
height: height, height: height,
tip: tip, tip: tip,
srcAddrRaw: srcRawAddr, srcAddrRaw: srcRawAddr,
country: country, country: country,
} }
} }
@ -137,8 +146,8 @@ func EncodeAddress(addr string) []byte {
} }
// DecodeAddress gets the string ipv4 address from an SPVPong struct. // DecodeAddress gets the string ipv4 address from an SPVPong struct.
func (pong *SPVPong) DecodeAddress() string { func (pong *SPVPong) DecodeAddress() net.IP {
return fmt.Sprintf("%d.%d.%d.%d", return net.IPv4(
pong.srcAddrRaw[0], pong.srcAddrRaw[0],
pong.srcAddrRaw[1], pong.srcAddrRaw[1],
pong.srcAddrRaw[2], pong.srcAddrRaw[2],
@ -146,53 +155,73 @@ func (pong *SPVPong) DecodeAddress() string {
) )
} }
func (pong *SPVPong) DecodeCountry() string {
return pb.Location_Country_name[int32(pong.country)]
}
func (pong *SPVPong) DecodeProtocolVersion() int {
return int(pong.protocolVersion)
}
func (pong *SPVPong) DecodeHeight() int {
return int(pong.height)
}
func (pong *SPVPong) DecodeTip() []byte {
return pong.tip
}
func (pong *SPVPong) DecodeFlags() byte {
return pong.flags
}
// UDPPing sends a ping over udp to another hub and returns the ip address of // UDPPing sends a ping over udp to another hub and returns the ip address of
// this hub. // this hub.
func UDPPing(address string) (string, error) { func UDPPing(ip, port string) (*SPVPong, error) {
address := ip + ":" + port
addr, err := net.ResolveUDPAddr("udp", address) addr, err := net.ResolveUDPAddr("udp", address)
if err != nil { if err != nil {
return "", err return nil, err
} }
conn, err := net.DialUDP("udp", nil, addr) conn, err := net.DialUDP("udp", nil, addr)
if err != nil { if err != nil {
return "", err return nil, err
} }
defer conn.Close() defer conn.Close()
_, err = conn.Write(encodeSPVPing()) _, err = conn.Write(encodeSPVPing())
if err != nil { if err != nil {
return "", err return nil, err
} }
buffer := make([]byte, maxBufferSize) buffer := make([]byte, maxBufferSize)
deadline := time.Now().Add(time.Second) deadline := time.Now().Add(time.Second)
err = conn.SetReadDeadline(deadline) err = conn.SetReadDeadline(deadline)
if err != nil { if err != nil {
return "", err return nil, err
} }
n, _, err := conn.ReadFromUDP(buffer) n, _, err := conn.ReadFromUDP(buffer)
if err != nil { if err != nil {
return "", err return nil, err
} }
pong := decodeSPVPong(buffer[:n]) pong := decodeSPVPong(buffer[:n])
if pong == nil { if pong == nil {
return "", errors.Base("Pong decoding failed") return nil, fmt.Errorf("Pong decoding failed")
} }
myAddr := pong.DecodeAddress() return pong, nil
return myAddr, nil
} }
// UDPServer is a goroutine that starts an udp server that implements the hubs // UDPServer is a goroutine that starts an udp server that implements the hubs
// Ping/Pong protocol to find out about each other without making full TCP // Ping/Pong protocol to find out about each other without making full TCP
// connections. // connections.
func UDPServer(args *Args) error { func (s *Server) UDPServer(port int) error {
address := ":" + args.UDPPort address := ":" + strconv.Itoa(port)
tip := make([]byte, 32) tip := make([]byte, 32)
addr, err := net.ResolveUDPAddr("udp", address) addr, err := net.ResolveUDPAddr("udp", address)
if err != nil { if err != nil {
@ -215,7 +244,7 @@ func UDPServer(args *Args) error {
} }
sAddr := addr.IP.String() sAddr := addr.IP.String()
pong := makeSPVPong(0,0, tip, sAddr, args.Country) pong := makeSPVPong(defaultFlags|availableFlag, 0, tip, sAddr, s.Args.Country)
data := pong.Encode() data := pong.Encode()
_, err = conn.WriteToUDP(data, addr) _, err = conn.WriteToUDP(data, addr)

85
server/udp_test.go Normal file
View file

@ -0,0 +1,85 @@
package server_test
import (
"log"
"os/exec"
"strings"
"testing"
server "github.com/lbryio/herald.go/server"
)
// TestUDPPing tests UDPPing correctness against prod server.
func TestUDPPing(t *testing.T) {
args := server.MakeDefaultTestArgs()
args.DisableStartUDP = true
tests := []struct {
name string
wantIP string
wantCountry string
wantProtocolVersion int
wantHeightMin int
wantFlags byte
}{
{
name: "Correctly parse information from production server.",
wantIP: "SETME",
wantCountry: "US",
wantProtocolVersion: 1,
wantHeightMin: 1060000,
wantFlags: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
toAddr := "spv17.lbry.com"
toPort := "50001"
pong, err := server.UDPPing(toAddr, toPort)
if err != nil {
t.Skipf("ping failed: %v", err)
}
gotCountry := pong.DecodeCountry()
res, err := exec.Command("dig", "@resolver4.opendns.com", "myip.opendns.com", "+short").Output()
if err != nil {
log.Println(err)
}
digIP := strings.TrimSpace(string(res))
udpIP := pong.DecodeAddress().String()
tt.wantIP = digIP
log.Println("Height:", pong.DecodeHeight())
log.Printf("Flags: %x\n", pong.DecodeFlags())
log.Println("ProtocolVersion:", pong.DecodeProtocolVersion())
log.Printf("Tip: %x\n", pong.DecodeTip())
gotHeight := pong.DecodeHeight()
gotProtocolVersion := pong.DecodeProtocolVersion()
gotFlags := pong.DecodeFlags()
gotIP := udpIP
if gotIP != tt.wantIP {
t.Errorf("ip: got: '%s', want: '%s'\n", gotIP, tt.wantIP)
}
if gotCountry != tt.wantCountry {
t.Errorf("country: got: '%s', want: '%s'\n", gotCountry, tt.wantCountry)
}
if gotHeight < tt.wantHeightMin {
t.Errorf("height: got: %d, want >=: %d\n", gotHeight, tt.wantHeightMin)
}
if gotProtocolVersion != tt.wantProtocolVersion {
t.Errorf("protocolVersion: got: %d, want: %d\n", gotProtocolVersion, tt.wantProtocolVersion)
}
if gotFlags != tt.wantFlags {
t.Errorf("flags: got: %d, want: %d\n", gotFlags, tt.wantFlags)
}
})
}
}

73
signal.go Normal file
View file

@ -0,0 +1,73 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"os"
"os/signal"
"github.com/lbryio/lbry.go/v3/extras/stop"
log "github.com/sirupsen/logrus"
)
// shutdownRequestChannel is used to initiate shutdown from one of the
// subsystems using the same code paths as when an interrupt signal is received.
var shutdownRequestChannel = make(stop.Chan)
// interruptSignals defines the default signals to catch in order to do a proper
// shutdown. This may be modified during init depending on the platform.
var interruptSignals = []os.Signal{os.Interrupt}
// interruptListener listens for OS Signals such as SIGINT (Ctrl+C) and shutdown
// requests from shutdownRequestChannel. It returns a channel that is closed
// when either signal is received.
func interruptListener() <-chan struct{} {
c := make(chan struct{})
go func() {
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, interruptSignals...)
// Listen for initial shutdown signal and close the returned
// channel to notify the caller.
select {
case sig := <-interruptChannel:
log.Infof("Received signal (%s). Shutting down...",
sig)
case <-shutdownRequestChannel:
log.Info("Shutdown requested. Shutting down...")
}
close(c)
// Listen for repeated signals and display a message so the user
// knows the shutdown is in progress and the process is not
// hung.
for {
select {
case sig := <-interruptChannel:
log.Infof("Received signal (%s). Already "+
"shutting down...", sig)
case <-shutdownRequestChannel:
log.Info("Shutdown requested. Already " +
"shutting down...")
}
}
}()
return c
}
// interruptRequested returns true when the channel returned by
// interruptListener was closed. This simplifies early shutdown slightly since
// the caller can just use an if statement instead of a select.
func interruptRequested(interrupted <-chan struct{}) bool {
select {
case <-interrupted:
return true
default:
}
return false
}

18
signalsigterm.go Normal file
View file

@ -0,0 +1,18 @@
// Copyright (c) 2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package main
import (
"os"
"syscall"
)
// initsignals sets the signals to be caught by the signal handler
func initsignals() {
interruptSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}
}

11
testdata/B.csv vendored Normal file
View file

@ -0,0 +1,11 @@
B,
4200000031a2e262d60074f07330d7187907e5b02be8f9b3c60cdc03d776314912,0200000001d922db1c8020a8a101ccab3a9dff62eccd8660c9351bf91af8dc481544395821010000006a473044022047bbe0eec4931aa332ac089dfb56bac095e4e2f11258f95f8dc11f2d915399b802207cbe15f5c1ad882ef7cf7c987c5380a56613c7bbfd56e4a8e4161a46fd522a84012103e1a29d4cb998f7a6a165f1a2aaa524f319a59beb4f9336f542175ddecc6cac03feffffff0200ca9a3b000000001976a914028b4111c923a411ba165760cacea097b9b0b77588ac567d1329400000001976a914bc3826102bebb5ab7d88cb080b5234b67aac787888ac4ad50c00
420000004e91edda0f9cd3bcef9565a31e6bbbd34c731483e03ec7d8819158ac30,02000000018fb565983ddfc193da15f68414712779a7de703babef37c1b30fb8956abbd034010000006a4730440220011f6c22ecafdfc03fde256c0676b7d840e8aacd9e75a02fde40862451d97b7f022072a8093343d900d5543a00e648352a7c7cf2389d89a5863d33c34e44f3acaa590121036be4ec00fc805765e824a604fecc376cdc33cd3a8d175fad63980e16e79d8809feffffff026e39190d000000001976a9146911aebb1ec343bb432aa3edf9f08e4f110a541e88ac40787d01000000001976a914ea2faaf77fab748cddca231f9d528c43c2f3a78e88ac02340f00
420000008070865693cd82ed0f59896e34973adbff0583fb8a1293919591446075,02000000017c0ac8ee47ebdcbf4c36a0f1ff0a77347658099b734e65bd662ca510b91385b2000000006a473044022023da5a18003e6db9144b691ea77cb267b911678e15cecab4d22c52b4ced6ad3302203ba4e2cb7d1ad2c88aa89827767cea76c1805a366598fc53513807f3e5db78cd0121022c949b389ff428dc58ca8f0803dab7cde26cbc0a12b26ef758853c1266f9e543feffffff023dabb942020000001976a91440ee9a09269bdd2f3532b8bc18d792cd435de79488ac002d3101000000001976a914b4158a6a1b7505068bc5df3b0f76a02cc38014d788ac9a880f00
420000009c24d4d9187749a1f8f6d6c0f92a5e98817f5efcd427a5593344a45a2e,0200000001bda00a971cebc8727690779e84d00ba4d7393032ff0d75e11909c3ede2c7f48c010000006a473044022077d431faca3342ec9cb7701b96490cb67e5b1b3c70efb8bf6f92aa183321c71202207a9de1cb8508264f97bf2cae258571acef93d520d4743fa71b332684f99c082d0121030c29115cd040efdb5b3617e180074e78c2c9a75613c32701241ad9f8985f3abcfeffffff0240787d01000000001976a914c154d27978ee4c3002ad934e858e63c58550b1ae88acc47d6ab0070000001976a914d1ea863ff9311b80837f43732d4f26b7e183862088aca36c0d00
42000000cee66b136a85596df24ece60bbd1392f70204fd2f144f059e5195ee3c9,0100000001f5013c21bd5c858f7eed0df58e9471e148d8924962d7cadd1b775358029076f9010000006a47304402205348d7b22894f7ed8f8d44fa73a65ea9d8124e4b0b88d816b0f8b6714b897da702204093e051c09c86c33b6dcd4285670a1d442622f5b687ac028514e5b44718fd4e012103c633b45b4d49c485c67b7d96920fa6a26ae30a07c0fb9f0ae8f85c00a19b8825feffffff0273e25769000000001976a914999d5b0e3d5efcf601c711127b91841afbf5c37a88acf6268a0d090000001976a914816f8bd268c614b3ae8b564fa531ece6e1d0297188ac522f0200
42000000e0bf96accd4eda4d871c5bc8c0ebb14509b896a867095f9b419f9b04f2,0200000001d17c8a33cddad3b6a1f34248e9457b359c59e80a5541c017fd5ab1c8b21f0d2b010000006a473044022036102bd4d4e28909a17e189d23d640fd0f1111f45eb5639f68da70317e04831602201a317ae0af0930d27c280a91ced77c4c6dccda7eb297f7bf73023106762e88c8012102913606374e2b055d06fb4fb8250ca65ca66ecf4154916722e11cf3487555b2f0feffffff0240787d01000000001976a914c13e9599dafeb20e2e7cf994941d44e1e1e88ce588ace9cba36d020000001976a9149db9398216b2ad3726aa8d5a3391a20d460f494388ac7ac40e00
420000011daab2a9f45cca3d0bac6fee69324af4757b1c4e8c3a362efbb6e8a09d,02000000019161a7c7a985bd44afafebe044df38df82ecdba417ea3244235572257b201068000000006a473044022077ed0bb436d9a7bd1a83f680cef81d9b8cbd2b74de310f32859c49077e009f90022020faea4599352a9d42c50b7a4eeeda1ba31121e5a0817ae0b650f35ae79a06d6012102ad6c243af5779b873b171b79cbe65cda7cf8c5d35865ecaf796912e888150c3cfeffffff02f8c2bc74040000001976a91420195c8e1c81eb3bb03452d855591d4099db08d588ac00c2eb0b000000001976a9148bc70b6256cff9f4e645bb1431548aad757ba56388ac1ec30b00
4200000150116856ce8ce06604b9584e61afb3b613a8a5a512ab260e7f0cbe5496,010000000183843af3333d4b751745b90321b1e7c9f32b23299ac6ed160ef1d7f079a7b5f8000000006a4730440220761f40ffdc87d217f84adb43132a78f9db3a16bf9303876c6a871abb253823be02202357dadaed6701134e68f2fe41bb883da82d550c431fb1f36ef6b97ad2c3e19e012103d77e3266f1ed4533e3bd5464aebedbf0a3d2b7ec677cca6996a8548d73e50af6feffffff1147164d1f000000001976a91466f157283bbc18c616f825c2ad2d9c6108a0e96f88acd680a626000000001976a914ae0ee727bf91e4f9f70664a8a0936d7ff81d3d5888ac9b425a1e000000001976a9146fd65fe059c85fe1cb704efc80c81f0275ab7cb188ac71222212000000001976a914c2daa42a9414bc6b70b395b79d8b54556c918a2288ac0549ce27000000001976a91438b1ce845765a1a749117bbd1c6fea9adcf2784088acb420011e000000001976a914213a5c1975e979b60a24bf72809f3d7205ee925888ac58c50621000000001976a914fc6a1f55c1bb2dd382df728d75c893406bfadb5d88ac9183621e000000001976a914ad70928b870c76b899cf9d66f1ee5b258976721088ac19eb3a0c000000001976a91421ba33ab978890434c8cfd31db9789fe5af0e73c88acb755ea1e000000001976a914c2003ddf455851be985e349830dc0d7dc619205c88ac125a3f27000000001976a91411b58fd67f6b389a5bfd592db92fdcfe613214aa88acd674713f000000001976a914514f9d058cad9251b816ae609b847582e759519188ac205e3007000000001976a91417066054003dd148297c6f1922db8bcbe185ba3b88acb562931f000000001976a9149481776b356d441567601fc713e9516ccf85ebb688ace2c8184e000000001976a914dd705e9e15c0c0e0a714f6b79b8618aa840f9b2c88acc3fb0ba6050000001976a91419bef2dcb05c86662c08ef2848819a685dd0dcdc88ac5d258c06000000001976a9142479394dc4ef5e61bcb9c60ef8f3f27dffa57fdb88ac20fb0700
420000015ba8df5c3ed80d0fc364136de02e3ba9d9550cbb1ecef03b97fcdf0621,0100000001e50a9a7f3bcd2d4c0c3dc60d96b3e008a247e80e0b114d34b38399ac6ed6ea620c0000006b4830450221009b8beec62aac1e7096071b3fbbaa4cc1846ef8781ea8a9a2a8d6c1d918f359b6022074f04dd3da2c67e2e293c4a1eaf69931512a060af6665740e6084f03a6fbff22012103b5f96b51c7b567b747b9cc14e46daab5200acc37052e149e213939e103f69aa6ffffffff0134b7f505000000001976a914e75d6c70d420ffaef75def1a94469f2dc6546a3588ac00000000
42000001d4a53fc92321415631862a791f8680241ed172e579534713f68a6869ba,01000000025b09f3192aa1f9e436b4b282143f0668acd6fb7c90cdbfef0a69d84d036e702a000000006b483045022100df7687542decdaaabf1bb67b1f6809a697f54264a09c8e9dc33c5527fd6ef16302200fab534e44fad5d86a65a645a0b8ebfb9644dd3ecac65b8745706076a430c2dc0121029d7cd706d4da4a71441a1dc4a6109fdb481112d749f19174cb025e95bfafbc94ffffffff5b09f3192aa1f9e436b4b282143f0668acd6fb7c90cdbfef0a69d84d036e702a010000006a47304402204849b7a22292f1dd6af210da076b523330640e5bb31673d3dfd394067d17b23902201813fd1f1a91399b64b0cb0c309d41aa2c066f69403ca90ca046af6c609182810121029d7cd706d4da4a71441a1dc4a6109fdb481112d749f19174cb025e95bfafbc94ffffffff0240420f0000000000c6b70b406372617a7964696e676f14f9407ef9b87c18020796dd434fad2bca5c727acc4c8700125a0a583056301006072a8648ce3d020106052b8104000a03420004b906153f96ff0567979424eca1a19691df7d23be28901c248b6fe7986cf740f7631f5a36f6d03d26bd97e37e83e1320808400202dd4dbad81467f1570ff763d152282a2668747470733a2f2f737065652e63682f312f623530383737333436313932626433382e6a70676d6d76a91439ed14bafdb75ee4f06f134de8aee0b845c3bd3588ac40a01000000000001976a91439ed14bafdb75ee4f06f134de8aee0b845c3bd3588ac00000000
1 B
2 4200000031a2e262d60074f07330d7187907e5b02be8f9b3c60cdc03d776314912 0200000001d922db1c8020a8a101ccab3a9dff62eccd8660c9351bf91af8dc481544395821010000006a473044022047bbe0eec4931aa332ac089dfb56bac095e4e2f11258f95f8dc11f2d915399b802207cbe15f5c1ad882ef7cf7c987c5380a56613c7bbfd56e4a8e4161a46fd522a84012103e1a29d4cb998f7a6a165f1a2aaa524f319a59beb4f9336f542175ddecc6cac03feffffff0200ca9a3b000000001976a914028b4111c923a411ba165760cacea097b9b0b77588ac567d1329400000001976a914bc3826102bebb5ab7d88cb080b5234b67aac787888ac4ad50c00
3 420000004e91edda0f9cd3bcef9565a31e6bbbd34c731483e03ec7d8819158ac30 02000000018fb565983ddfc193da15f68414712779a7de703babef37c1b30fb8956abbd034010000006a4730440220011f6c22ecafdfc03fde256c0676b7d840e8aacd9e75a02fde40862451d97b7f022072a8093343d900d5543a00e648352a7c7cf2389d89a5863d33c34e44f3acaa590121036be4ec00fc805765e824a604fecc376cdc33cd3a8d175fad63980e16e79d8809feffffff026e39190d000000001976a9146911aebb1ec343bb432aa3edf9f08e4f110a541e88ac40787d01000000001976a914ea2faaf77fab748cddca231f9d528c43c2f3a78e88ac02340f00
4 420000008070865693cd82ed0f59896e34973adbff0583fb8a1293919591446075 02000000017c0ac8ee47ebdcbf4c36a0f1ff0a77347658099b734e65bd662ca510b91385b2000000006a473044022023da5a18003e6db9144b691ea77cb267b911678e15cecab4d22c52b4ced6ad3302203ba4e2cb7d1ad2c88aa89827767cea76c1805a366598fc53513807f3e5db78cd0121022c949b389ff428dc58ca8f0803dab7cde26cbc0a12b26ef758853c1266f9e543feffffff023dabb942020000001976a91440ee9a09269bdd2f3532b8bc18d792cd435de79488ac002d3101000000001976a914b4158a6a1b7505068bc5df3b0f76a02cc38014d788ac9a880f00
5 420000009c24d4d9187749a1f8f6d6c0f92a5e98817f5efcd427a5593344a45a2e 0200000001bda00a971cebc8727690779e84d00ba4d7393032ff0d75e11909c3ede2c7f48c010000006a473044022077d431faca3342ec9cb7701b96490cb67e5b1b3c70efb8bf6f92aa183321c71202207a9de1cb8508264f97bf2cae258571acef93d520d4743fa71b332684f99c082d0121030c29115cd040efdb5b3617e180074e78c2c9a75613c32701241ad9f8985f3abcfeffffff0240787d01000000001976a914c154d27978ee4c3002ad934e858e63c58550b1ae88acc47d6ab0070000001976a914d1ea863ff9311b80837f43732d4f26b7e183862088aca36c0d00
6 42000000cee66b136a85596df24ece60bbd1392f70204fd2f144f059e5195ee3c9 0100000001f5013c21bd5c858f7eed0df58e9471e148d8924962d7cadd1b775358029076f9010000006a47304402205348d7b22894f7ed8f8d44fa73a65ea9d8124e4b0b88d816b0f8b6714b897da702204093e051c09c86c33b6dcd4285670a1d442622f5b687ac028514e5b44718fd4e012103c633b45b4d49c485c67b7d96920fa6a26ae30a07c0fb9f0ae8f85c00a19b8825feffffff0273e25769000000001976a914999d5b0e3d5efcf601c711127b91841afbf5c37a88acf6268a0d090000001976a914816f8bd268c614b3ae8b564fa531ece6e1d0297188ac522f0200
7 42000000e0bf96accd4eda4d871c5bc8c0ebb14509b896a867095f9b419f9b04f2 0200000001d17c8a33cddad3b6a1f34248e9457b359c59e80a5541c017fd5ab1c8b21f0d2b010000006a473044022036102bd4d4e28909a17e189d23d640fd0f1111f45eb5639f68da70317e04831602201a317ae0af0930d27c280a91ced77c4c6dccda7eb297f7bf73023106762e88c8012102913606374e2b055d06fb4fb8250ca65ca66ecf4154916722e11cf3487555b2f0feffffff0240787d01000000001976a914c13e9599dafeb20e2e7cf994941d44e1e1e88ce588ace9cba36d020000001976a9149db9398216b2ad3726aa8d5a3391a20d460f494388ac7ac40e00
8 420000011daab2a9f45cca3d0bac6fee69324af4757b1c4e8c3a362efbb6e8a09d 02000000019161a7c7a985bd44afafebe044df38df82ecdba417ea3244235572257b201068000000006a473044022077ed0bb436d9a7bd1a83f680cef81d9b8cbd2b74de310f32859c49077e009f90022020faea4599352a9d42c50b7a4eeeda1ba31121e5a0817ae0b650f35ae79a06d6012102ad6c243af5779b873b171b79cbe65cda7cf8c5d35865ecaf796912e888150c3cfeffffff02f8c2bc74040000001976a91420195c8e1c81eb3bb03452d855591d4099db08d588ac00c2eb0b000000001976a9148bc70b6256cff9f4e645bb1431548aad757ba56388ac1ec30b00
9 4200000150116856ce8ce06604b9584e61afb3b613a8a5a512ab260e7f0cbe5496 010000000183843af3333d4b751745b90321b1e7c9f32b23299ac6ed160ef1d7f079a7b5f8000000006a4730440220761f40ffdc87d217f84adb43132a78f9db3a16bf9303876c6a871abb253823be02202357dadaed6701134e68f2fe41bb883da82d550c431fb1f36ef6b97ad2c3e19e012103d77e3266f1ed4533e3bd5464aebedbf0a3d2b7ec677cca6996a8548d73e50af6feffffff1147164d1f000000001976a91466f157283bbc18c616f825c2ad2d9c6108a0e96f88acd680a626000000001976a914ae0ee727bf91e4f9f70664a8a0936d7ff81d3d5888ac9b425a1e000000001976a9146fd65fe059c85fe1cb704efc80c81f0275ab7cb188ac71222212000000001976a914c2daa42a9414bc6b70b395b79d8b54556c918a2288ac0549ce27000000001976a91438b1ce845765a1a749117bbd1c6fea9adcf2784088acb420011e000000001976a914213a5c1975e979b60a24bf72809f3d7205ee925888ac58c50621000000001976a914fc6a1f55c1bb2dd382df728d75c893406bfadb5d88ac9183621e000000001976a914ad70928b870c76b899cf9d66f1ee5b258976721088ac19eb3a0c000000001976a91421ba33ab978890434c8cfd31db9789fe5af0e73c88acb755ea1e000000001976a914c2003ddf455851be985e349830dc0d7dc619205c88ac125a3f27000000001976a91411b58fd67f6b389a5bfd592db92fdcfe613214aa88acd674713f000000001976a914514f9d058cad9251b816ae609b847582e759519188ac205e3007000000001976a91417066054003dd148297c6f1922db8bcbe185ba3b88acb562931f000000001976a9149481776b356d441567601fc713e9516ccf85ebb688ace2c8184e000000001976a914dd705e9e15c0c0e0a714f6b79b8618aa840f9b2c88acc3fb0ba6050000001976a91419bef2dcb05c86662c08ef2848819a685dd0dcdc88ac5d258c06000000001976a9142479394dc4ef5e61bcb9c60ef8f3f27dffa57fdb88ac20fb0700
10 420000015ba8df5c3ed80d0fc364136de02e3ba9d9550cbb1ecef03b97fcdf0621 0100000001e50a9a7f3bcd2d4c0c3dc60d96b3e008a247e80e0b114d34b38399ac6ed6ea620c0000006b4830450221009b8beec62aac1e7096071b3fbbaa4cc1846ef8781ea8a9a2a8d6c1d918f359b6022074f04dd3da2c67e2e293c4a1eaf69931512a060af6665740e6084f03a6fbff22012103b5f96b51c7b567b747b9cc14e46daab5200acc37052e149e213939e103f69aa6ffffffff0134b7f505000000001976a914e75d6c70d420ffaef75def1a94469f2dc6546a3588ac00000000
11 42000001d4a53fc92321415631862a791f8680241ed172e579534713f68a6869ba 01000000025b09f3192aa1f9e436b4b282143f0668acd6fb7c90cdbfef0a69d84d036e702a000000006b483045022100df7687542decdaaabf1bb67b1f6809a697f54264a09c8e9dc33c5527fd6ef16302200fab534e44fad5d86a65a645a0b8ebfb9644dd3ecac65b8745706076a430c2dc0121029d7cd706d4da4a71441a1dc4a6109fdb481112d749f19174cb025e95bfafbc94ffffffff5b09f3192aa1f9e436b4b282143f0668acd6fb7c90cdbfef0a69d84d036e702a010000006a47304402204849b7a22292f1dd6af210da076b523330640e5bb31673d3dfd394067d17b23902201813fd1f1a91399b64b0cb0c309d41aa2c066f69403ca90ca046af6c609182810121029d7cd706d4da4a71441a1dc4a6109fdb481112d749f19174cb025e95bfafbc94ffffffff0240420f0000000000c6b70b406372617a7964696e676f14f9407ef9b87c18020796dd434fad2bca5c727acc4c8700125a0a583056301006072a8648ce3d020106052b8104000a03420004b906153f96ff0567979424eca1a19691df7d23be28901c248b6fe7986cf740f7631f5a36f6d03d26bd97e37e83e1320808400202dd4dbad81467f1570ff763d152282a2668747470733a2f2f737065652e63682f312f623530383737333436313932626433382e6a70676d6d76a91439ed14bafdb75ee4f06f134de8aee0b845c3bd3588ac40a01000000000001976a91439ed14bafdb75ee4f06f134de8aee0b845c3bd3588ac00000000

11
testdata/C.csv vendored Normal file
View file

@ -0,0 +1,11 @@
C,
4300000000,63f4346a4db34fdfce29a70f5e8d11f065f6b91602b7036c7f22f3a03b28899c
4300000001,246cb85843ac936d55388f2ff288b011add5b1b20cca9cfd19a403ca2c9ecbde
4300000002,0044e1258b865d262587c28ff98853bc52bb31266230c1c648cc9004047a5428
4300000003,bbf8980e3f7604896821203bf62f97f311124da1fbb95bf523fcfdb356ad19c9
4300000004,1a650b9b7b9d132e257ff6b336ba7cd96b1796357c4fc8dd7d0bd1ff1de057d5
4300000005,6d694b93a2bb5ac23a13ed6749a789ca751cf73d5982c459e0cd9d5d303da74c
4300000006,b57808c188b7315583cf120fe89de923583bc7a8ebff03189145b86bf859b21b
4300000007,a6a5b330e816242d54c8586ba9b6d63c19d921171ef3d4525b8ffc635742e83a
4300000008,b8447f415279dffe8a09afe6f6d5e335a2f6911fce8e1d1866723d5e5e8a5306
4300000009,558daee5a4a55fe03d912e35c7b6b0bc19ece82fd5bcb685bc36f2bc381babfd
1 C
2 4300000000 63f4346a4db34fdfce29a70f5e8d11f065f6b91602b7036c7f22f3a03b28899c
3 4300000001 246cb85843ac936d55388f2ff288b011add5b1b20cca9cfd19a403ca2c9ecbde
4 4300000002 0044e1258b865d262587c28ff98853bc52bb31266230c1c648cc9004047a5428
5 4300000003 bbf8980e3f7604896821203bf62f97f311124da1fbb95bf523fcfdb356ad19c9
6 4300000004 1a650b9b7b9d132e257ff6b336ba7cd96b1796357c4fc8dd7d0bd1ff1de057d5
7 4300000005 6d694b93a2bb5ac23a13ed6749a789ca751cf73d5982c459e0cd9d5d303da74c
8 4300000006 b57808c188b7315583cf120fe89de923583bc7a8ebff03189145b86bf859b21b
9 4300000007 a6a5b330e816242d54c8586ba9b6d63c19d921171ef3d4525b8ffc635742e83a
10 4300000008 b8447f415279dffe8a09afe6f6d5e335a2f6911fce8e1d1866723d5e5e8a5306
11 4300000009 558daee5a4a55fe03d912e35c7b6b0bc19ece82fd5bcb685bc36f2bc381babfd

11
testdata/D.csv vendored Normal file
View file

@ -0,0 +1,11 @@
D,
44000100ffffffffffffd8ef002741130000,a51d5c567412654e6d741114fea6fb851dec7380
44000101ffffffffffffd8ef002741140001,11158037afca9c2efabc3dff55e352bf1f5634c5
44000102ffffffffffffd8ef002741150000,a4a575934de77d8ec8589595d8cd91857e3cf5ba
44000103ffffffffffffd8ef002741160001,f595a21fb597bd030defefda3df9f8f4a3e0cb86
44000104ffffffffffffd8ef002741170000,682ccb0518a6bd00c955949d9ef330d3ac18cb80
44000105ffffffffffffd8ef002741180000,078a435851bf97c5cc36e8b03e3208a30d27679f
44000106ffffffffffffd8ef002741190000,1a4a75246a766cf21a629f619bc5bcb531de7a5a
44000107ffffffffffffd8ef0027411a0000,b144ad496b29b9c12c316f319773adcdd4c9bce2
44000108ffffffffffffd8ef0027411b0001,af2a09232fc6bf664088d65da42fe0345b458960
44000109ffffffffffffd8ef0027411c0001,7b2ab15758c519116fb9ba9331a3b9ee7530831f
1 D
2 44000100ffffffffffffd8ef002741130000 a51d5c567412654e6d741114fea6fb851dec7380
3 44000101ffffffffffffd8ef002741140001 11158037afca9c2efabc3dff55e352bf1f5634c5
4 44000102ffffffffffffd8ef002741150000 a4a575934de77d8ec8589595d8cd91857e3cf5ba
5 44000103ffffffffffffd8ef002741160001 f595a21fb597bd030defefda3df9f8f4a3e0cb86
6 44000104ffffffffffffd8ef002741170000 682ccb0518a6bd00c955949d9ef330d3ac18cb80
7 44000105ffffffffffffd8ef002741180000 078a435851bf97c5cc36e8b03e3208a30d27679f
8 44000106ffffffffffffd8ef002741190000 1a4a75246a766cf21a629f619bc5bcb531de7a5a
9 44000107ffffffffffffd8ef0027411a0000 b144ad496b29b9c12c316f319773adcdd4c9bce2
10 44000108ffffffffffffd8ef0027411b0001 af2a09232fc6bf664088d65da42fe0345b458960
11 44000109ffffffffffffd8ef0027411c0001 7b2ab15758c519116fb9ba9331a3b9ee7530831f

11
testdata/E.csv vendored Normal file
View file

@ -0,0 +1,11 @@
E,
45000000a420c44374f4f399ab4807fa1901eefc87,0297ec2100000297ec21000000000000000f42400100246167656e63652d64c3a974727569742c2d6e6f7576656175782d736b696e732d6c65616b
45000000c27eef5ea69e0d73f118826c7e326bb469,00371d66000000371d660000000000001dcd650001000e4d696e696174757265486f757365
4500000110e40894573f528c393fbcec7a472ec853,01516b32000001516b3200000000000000989680010021696f2d3137372d4e6f616d2d43686f6d736b792d6f6e2d434f494e54454c50524f
4500000324e40fcb63a0b517a3660645e9bd99244a,030bb6ba0000030bb6ba000000000000000f424001001b436f6e616e2d4578696c65732d526169642d4561726c792d457261
45000003d1538a0f19f5cd4bc1a62cc294f5c89934,011c7c990000011c7c99000000000000000f424001001130322d636172726167652d68616e646c65
45000008d47beeff8325e795a8604226145b01702b,02dbb2a2000002dbb2a2000000000000000186a001001039643336363661336161313231376138
4500000906499e073e94370ceff37cb21c28212444,0369842d00000369842d000000000000000186a001001033333465356465363139306534323466
45000009c3172e034a255f3c03566dca84bb9f046a,0225c69c000002251b0f0000000000000007a120010028617574686f722d73746f726965732d706f64636173742d657069736f64652d3734332d6b6172656e
45000009ca6e0caaaef16872b4bd4f6f1b8c2363e2,02b16956000002b16956000000000000000f4240010027554e2d504f55522d43454e542d28312d292d28536f72616c2c2d4162c3a963c3a9646169726529
4500000ad9ded2e15d18987900d09e9b29ef33d03e,02c972b3000002c972b3000000000000000186a0010006313331333333
1 E
2 45000000a420c44374f4f399ab4807fa1901eefc87 0297ec2100000297ec21000000000000000f42400100246167656e63652d64c3a974727569742c2d6e6f7576656175782d736b696e732d6c65616b
3 45000000c27eef5ea69e0d73f118826c7e326bb469 00371d66000000371d660000000000001dcd650001000e4d696e696174757265486f757365
4 4500000110e40894573f528c393fbcec7a472ec853 01516b32000001516b3200000000000000989680010021696f2d3137372d4e6f616d2d43686f6d736b792d6f6e2d434f494e54454c50524f
5 4500000324e40fcb63a0b517a3660645e9bd99244a 030bb6ba0000030bb6ba000000000000000f424001001b436f6e616e2d4578696c65732d526169642d4561726c792d457261
6 45000003d1538a0f19f5cd4bc1a62cc294f5c89934 011c7c990000011c7c99000000000000000f424001001130322d636172726167652d68616e646c65
7 45000008d47beeff8325e795a8604226145b01702b 02dbb2a2000002dbb2a2000000000000000186a001001039643336363661336161313231376138
8 4500000906499e073e94370ceff37cb21c28212444 0369842d00000369842d000000000000000186a001001033333465356465363139306534323466
9 45000009c3172e034a255f3c03566dca84bb9f046a 0225c69c000002251b0f0000000000000007a120010028617574686f722d73746f726965732d706f64636173742d657069736f64652d3734332d6b6172656e
10 45000009ca6e0caaaef16872b4bd4f6f1b8c2363e2 02b16956000002b16956000000000000000f4240010027554e2d504f55522d43454e542d28312d292d28536f72616c2c2d4162c3a963c3a9646169726529
11 4500000ad9ded2e15d18987900d09e9b29ef33d03e 02c972b3000002c972b3000000000000000186a0010006313331333333

2
testdata/E_resolve.csv vendored Normal file
View file

@ -0,0 +1,2 @@
E,,
E,452556ed1cab9d17f2a9392030a9ad7f5d138f11bd,006284e300000061ec7c0000000000000007a1200000134053747978686578656e68616d6d6572363636
1 E
2 E 452556ed1cab9d17f2a9392030a9ad7f5d138f11bd 006284e300000061ec7c0000000000000007a1200000134053747978686578656e68616d6d6572363636

11
testdata/F.csv vendored Normal file
View file

@ -0,0 +1,11 @@
F,
460001000161002741130000,002741130000
46000100026135002741130000,002741130000
4600010003613531002741130000,002741130000
460001000461353164002741130000,002741130000
46000100056135316435002741130000,002741130000
4600010006613531643563002741130000,002741130000
460001000761353164356335002741130000,002741130000
46000100086135316435633536002741130000,002741130000
4600010009613531643563353637002741130000,002741130000
460001000a61353164356335363734002741130000,002741130000
1 F
2 460001000161002741130000 002741130000
3 46000100026135002741130000 002741130000
4 4600010003613531002741130000 002741130000
5 460001000461353164002741130000 002741130000
6 46000100056135316435002741130000 002741130000
7 4600010006613531643563002741130000 002741130000
8 460001000761353164356335002741130000 002741130000
9 46000100086135316435633536002741130000 002741130000
10 4600010009613531643563353637002741130000 002741130000
11 460001000a61353164356335363734002741130000 002741130000

42
testdata/FULL_resolve.csv vendored Normal file
View file

@ -0,0 +1,42 @@
EFGIJPRSVWXZas,,
E,452556ed1cab9d17f2a9392030a9ad7f5d138f11bd,006284e300000061ec7c0000000000000007a1200000134053747978686578656e68616d6d6572363636
F,4600134073747978686578656e68616d6d657236363601320061ec7c0000,006284e30000
F,4600134073747978686578656e68616d6d6572363636013503e4d2e60000,03e4d2e60000
F,4600134073747978686578656e68616d6d657236363601630382eee90000,0382eee90000
F,4600134073747978686578656e68616d6d65723636360232350061ec7c0000,006284e30000
F,4600134073747978686578656e68616d6d657236363602356603e4d2e60000,03e4d2e60000
F,4600134073747978686578656e68616d6d65723636360263330382eee90000,0382eee90000
F,4600134073747978686578656e68616d6d6572363636033235350061ec7c0000,006284e30000
F,4600134073747978686578656e68616d6d65723636360335666103e4d2e60000,03e4d2e60000
F,4600134073747978686578656e68616d6d6572363636036333610382eee90000,0382eee90000
F,4600134073747978686578656e68616d6d657236363604323535360061ec7c0000,006284e30000
G,47006284e30000,2556ed1cab9d17f2a9392030a9ad7f5d138f11bd00134073747978686578656e68616d6d6572363636
I,499a0ed686ecdad9b6cb965c4d6681c02f0bbc66a60369e2b20000,2556ed1cab9d17f2a9392030a9ad7f5d138f11bd
J,4a2556ed1cab9d17f2a9392030a9ad7f5d138f11bd0013612d73747265616d2d696e2d7665726d6f6e740069a2570000,ee29ca5c44313a2827fd35252b72557334635749
P,5000134073747978686578656e68616d6d6572363636,2556ed1cab9d17f2a9392030a9ad7f5d138f11bd000a6471
R,5201006284e30000,000a6b652556ed1cab9d17f2a9392030a9ad7f5d138f11bd00134073747978686578656e68616d6d6572363636
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a6b67006286030000,0000007615cbad28
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a706a0063105c0000,000000000bebc200
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a73ea006367550000,0000000005f5e100
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a7d63006469750000,0000000db0b7c894
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a7ebf00648c480000,00000000b2d05e00
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a810e0064ccc00000,000000003b9aca00
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a825b006503cf0000,00000002bf52c92c
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88930066814a0000,00000000dc887a34
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88f900669d240000,0000000005f5e100
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88f900669d260000,000000001dcd6500
V,56000009ca6e0caaaef16872b4bd4f6f1b8c2363e2,dbdfb6cd5e83baf342eaab8b19662ed0c71aae9a
W,572556ed1cab9d17f2a9392030a9ad7f5d138f11bd00812cb90000,fa3a1c918fafd094083240fd54a3c8577b7f1094
W,572556ed1cab9d17f2a9392030a9ad7f5d138f11bd00812ce70000,1b845565203eca16cb6135e6fb70d4d2cec4ee9b
W,572556ed1cab9d17f2a9392030a9ad7f5d138f11bd00812ce90000,c2bfc30ebdf2511a2a9a22b463f80d1f751ee38c
W,572556ed1cab9d17f2a9392030a9ad7f5d138f11bd00812d3f0000,f9c6adebfb970aa9ab1cac21f82eb007b2421a20
W,572556ed1cab9d17f2a9392030a9ad7f5d138f11bd00e7b0800000,a3cfb4a2a4b7efda98d5f680d6dbc30b4ebb328b
W,57255761310145baa958b5587d9b5571423e5a0d3c0208ba650000,2ae0dadba7d5931105ca2e5cb1c12ec61100b9b5
W,57255761310145baa958b5587d9b5571423e5a0d3c0208dc150000,a9389febb41d9a1c63deef395273b903caf4a18d
W,57255761310145baa958b5587d9b5571423e5a0d3c0208e3eb0000,68ab6c0cdd615540062b6f6d637f8b47ab0e615b
W,57255761310145baa958b5587d9b5571423e5a0d3c0208f7210000,3d8ee0471ae8751e016b62dca9cee5cfebc9b30d
W,57255761310145baa958b5587d9b5571423e5a0d3c02090a7b0000,0a059f3e94ed2c5a9d43986f0f14cf29f02d01ce
X,58006284e3,54e14ff0c404c29b3d39ae4d249435f167d5cd4ce5a428ecb745b3df1c8e3dde
Z,5a2556ed1cab9d17f2a9392030a9ad7f5d138f11bd,00000e56
a,612556ed1cab9d17f2a9392030a9ad7f5d138f11bd,000007df178c203c
s,73,9c89283ba0f3227f6c03b70216b9f665f0118d5e0fa729cedf4fb34d6a34f46300105bec03f782718ccd27260ce980e7d3d0b5c5f7be1517027b68104109128a34d1cc562f32008e00105bef0014f734000700105befffffffffffffffff00105bec
1 EFGIJPRSVWXZas
2 E 452556ed1cab9d17f2a9392030a9ad7f5d138f11bd 006284e300000061ec7c0000000000000007a1200000134053747978686578656e68616d6d6572363636
3 F 4600134073747978686578656e68616d6d657236363601320061ec7c0000 006284e30000
4 F 4600134073747978686578656e68616d6d6572363636013503e4d2e60000 03e4d2e60000
5 F 4600134073747978686578656e68616d6d657236363601630382eee90000 0382eee90000
6 F 4600134073747978686578656e68616d6d65723636360232350061ec7c0000 006284e30000
7 F 4600134073747978686578656e68616d6d657236363602356603e4d2e60000 03e4d2e60000
8 F 4600134073747978686578656e68616d6d65723636360263330382eee90000 0382eee90000
9 F 4600134073747978686578656e68616d6d6572363636033235350061ec7c0000 006284e30000
10 F 4600134073747978686578656e68616d6d65723636360335666103e4d2e60000 03e4d2e60000
11 F 4600134073747978686578656e68616d6d6572363636036333610382eee90000 0382eee90000
12 F 4600134073747978686578656e68616d6d657236363604323535360061ec7c0000 006284e30000
13 G 47006284e30000 2556ed1cab9d17f2a9392030a9ad7f5d138f11bd00134073747978686578656e68616d6d6572363636
14 I 499a0ed686ecdad9b6cb965c4d6681c02f0bbc66a60369e2b20000 2556ed1cab9d17f2a9392030a9ad7f5d138f11bd
15 J 4a2556ed1cab9d17f2a9392030a9ad7f5d138f11bd0013612d73747265616d2d696e2d7665726d6f6e740069a2570000 ee29ca5c44313a2827fd35252b72557334635749
16 P 5000134073747978686578656e68616d6d6572363636 2556ed1cab9d17f2a9392030a9ad7f5d138f11bd000a6471
17 R 5201006284e30000 000a6b652556ed1cab9d17f2a9392030a9ad7f5d138f11bd00134073747978686578656e68616d6d6572363636
18 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a6b67006286030000 0000007615cbad28
19 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a706a0063105c0000 000000000bebc200
20 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a73ea006367550000 0000000005f5e100
21 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a7d63006469750000 0000000db0b7c894
22 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a7ebf00648c480000 00000000b2d05e00
23 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a810e0064ccc00000 000000003b9aca00
24 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a825b006503cf0000 00000002bf52c92c
25 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88930066814a0000 00000000dc887a34
26 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88f900669d240000 0000000005f5e100
27 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88f900669d260000 000000001dcd6500
28 V 56000009ca6e0caaaef16872b4bd4f6f1b8c2363e2 dbdfb6cd5e83baf342eaab8b19662ed0c71aae9a
29 W 572556ed1cab9d17f2a9392030a9ad7f5d138f11bd00812cb90000 fa3a1c918fafd094083240fd54a3c8577b7f1094
30 W 572556ed1cab9d17f2a9392030a9ad7f5d138f11bd00812ce70000 1b845565203eca16cb6135e6fb70d4d2cec4ee9b
31 W 572556ed1cab9d17f2a9392030a9ad7f5d138f11bd00812ce90000 c2bfc30ebdf2511a2a9a22b463f80d1f751ee38c
32 W 572556ed1cab9d17f2a9392030a9ad7f5d138f11bd00812d3f0000 f9c6adebfb970aa9ab1cac21f82eb007b2421a20
33 W 572556ed1cab9d17f2a9392030a9ad7f5d138f11bd00e7b0800000 a3cfb4a2a4b7efda98d5f680d6dbc30b4ebb328b
34 W 57255761310145baa958b5587d9b5571423e5a0d3c0208ba650000 2ae0dadba7d5931105ca2e5cb1c12ec61100b9b5
35 W 57255761310145baa958b5587d9b5571423e5a0d3c0208dc150000 a9389febb41d9a1c63deef395273b903caf4a18d
36 W 57255761310145baa958b5587d9b5571423e5a0d3c0208e3eb0000 68ab6c0cdd615540062b6f6d637f8b47ab0e615b
37 W 57255761310145baa958b5587d9b5571423e5a0d3c0208f7210000 3d8ee0471ae8751e016b62dca9cee5cfebc9b30d
38 W 57255761310145baa958b5587d9b5571423e5a0d3c02090a7b0000 0a059f3e94ed2c5a9d43986f0f14cf29f02d01ce
39 X 58006284e3 54e14ff0c404c29b3d39ae4d249435f167d5cd4ce5a428ecb745b3df1c8e3dde
40 Z 5a2556ed1cab9d17f2a9392030a9ad7f5d138f11bd 00000e56
41 a 612556ed1cab9d17f2a9392030a9ad7f5d138f11bd 000007df178c203c
42 s 73 9c89283ba0f3227f6c03b70216b9f665f0118d5e0fa729cedf4fb34d6a34f46300105bec03f782718ccd27260ce980e7d3d0b5c5f7be1517027b68104109128a34d1cc562f32008e00105bef0014f734000700105befffffffffffffffff00105bec

11
testdata/F_cat.csv vendored Normal file
View file

@ -0,0 +1,11 @@
F,,
F,460003636174013000201c740000,00201c740000
F,4600036361740130002d4eb10000,002d4eb10000
F,46000363617401300035f0460000,0035f0460000
F,460003636174013000817df60000,00817df60000
F,46000363617401300090d0f30000,009102470000
F,460003636174013000a009af0000,00a009af0000
F,460003636174013000a082a60000,00a082a60000
F,460003636174013000a6a0d60000,00f2c75d0000
F,460003636174013000a9111e0000,00b72e480000
F,460003636174013000ab038e0000,00ab0ba60000
1 F
2 F 460003636174013000201c740000 00201c740000
3 F 4600036361740130002d4eb10000 002d4eb10000
4 F 46000363617401300035f0460000 0035f0460000
5 F 460003636174013000817df60000 00817df60000
6 F 46000363617401300090d0f30000 009102470000
7 F 460003636174013000a009af0000 00a009af0000
8 F 460003636174013000a082a60000 00a082a60000
9 F 460003636174013000a6a0d60000 00f2c75d0000
10 F 460003636174013000a9111e0000 00b72e480000
11 F 460003636174013000ab038e0000 00ab0ba60000

11
testdata/F_resolve.csv vendored Normal file
View file

@ -0,0 +1,11 @@
F,,
F,4600134073747978686578656e68616d6d657236363601320061ec7c0000,006284e30000
F,4600134073747978686578656e68616d6d6572363636013503e4d2e60000,03e4d2e60000
F,4600134073747978686578656e68616d6d657236363601630382eee90000,0382eee90000
F,4600134073747978686578656e68616d6d65723636360232350061ec7c0000,006284e30000
F,4600134073747978686578656e68616d6d657236363602356603e4d2e60000,03e4d2e60000
F,4600134073747978686578656e68616d6d65723636360263330382eee90000,0382eee90000
F,4600134073747978686578656e68616d6d6572363636033235350061ec7c0000,006284e30000
F,4600134073747978686578656e68616d6d65723636360335666103e4d2e60000,03e4d2e60000
F,4600134073747978686578656e68616d6d6572363636036333610382eee90000,0382eee90000
F,4600134073747978686578656e68616d6d657236363604323535360061ec7c0000,006284e30000
1 F
2 F 4600134073747978686578656e68616d6d657236363601320061ec7c0000 006284e30000
3 F 4600134073747978686578656e68616d6d6572363636013503e4d2e60000 03e4d2e60000
4 F 4600134073747978686578656e68616d6d657236363601630382eee90000 0382eee90000
5 F 4600134073747978686578656e68616d6d65723636360232350061ec7c0000 006284e30000
6 F 4600134073747978686578656e68616d6d657236363602356603e4d2e60000 03e4d2e60000
7 F 4600134073747978686578656e68616d6d65723636360263330382eee90000 0382eee90000
8 F 4600134073747978686578656e68616d6d6572363636033235350061ec7c0000 006284e30000
9 F 4600134073747978686578656e68616d6d65723636360335666103e4d2e60000 03e4d2e60000
10 F 4600134073747978686578656e68616d6d6572363636036333610382eee90000 0382eee90000
11 F 4600134073747978686578656e68616d6d657236363604323535360061ec7c0000 006284e30000

11
testdata/G.csv vendored Normal file
View file

@ -0,0 +1,11 @@
G,
4700162aa70000,c78ac4c326cd43cdc0c844b7cea13659449ab3e40015746573742d70686f746f2d7374726173626f757267
4700162f600000,ebf95f7fdb89db5467bb1b88ea3b0f0f7ee5ce360003636e63
47001630960000,a6f91a86837ab84a4cf0d2dcbe94704a528cf820000f776f6e646572776f6d616e31393933
47001635e60000,9673cc2a1aac64d7b2742705abfb09fca30d7e0500056d6d61736b
47001638a80000,c39342066646dc50f1a9954b41684d157b035dac00036f6e65
47001645ef0001,4689c1ccb4420309f93ab98799b28c49fa4d3809000a65617379737472656574
470016529a0000,f1628d66ae52295590b72b9a0b3a3527642a532600137465737470756230332d32312d323031372d32
470016529d0000,a4c61ced261ab571bdb3410ae140bec6c31f14ce00117465737470756230332d32312d32303137
47001655960000,2327bcb6d7578a2669e416b5aa185fe14ee8e03e00056569676874
47001664200000,f69099600bdca9b062ba60432dba3c0ca2241167002c686973746f72792d6f662d6672696564726963682d69692d6f662d707275737369612d766f6c756d652d3137
1 G
2 4700162aa70000 c78ac4c326cd43cdc0c844b7cea13659449ab3e40015746573742d70686f746f2d7374726173626f757267
3 4700162f600000 ebf95f7fdb89db5467bb1b88ea3b0f0f7ee5ce360003636e63
4 47001630960000 a6f91a86837ab84a4cf0d2dcbe94704a528cf820000f776f6e646572776f6d616e31393933
5 47001635e60000 9673cc2a1aac64d7b2742705abfb09fca30d7e0500056d6d61736b
6 47001638a80000 c39342066646dc50f1a9954b41684d157b035dac00036f6e65
7 47001645ef0001 4689c1ccb4420309f93ab98799b28c49fa4d3809000a65617379737472656574
8 470016529a0000 f1628d66ae52295590b72b9a0b3a3527642a532600137465737470756230332d32312d323031372d32
9 470016529d0000 a4c61ced261ab571bdb3410ae140bec6c31f14ce00117465737470756230332d32312d32303137
10 47001655960000 2327bcb6d7578a2669e416b5aa185fe14ee8e03e00056569676874
11 47001664200000 f69099600bdca9b062ba60432dba3c0ca2241167002c686973746f72792d6f662d6672696564726963682d69692d6f662d707275737369612d766f6c756d652d3137

11
testdata/G_2.csv vendored Normal file
View file

@ -0,0 +1,11 @@
G,,
G,4700162aa70000,c78ac4c326cd43cdc0c844b7cea13659449ab3e40015746573742d70686f746f2d7374726173626f757267
G,4700162f600000,ebf95f7fdb89db5467bb1b88ea3b0f0f7ee5ce360003636e63
G,47001630960000,a6f91a86837ab84a4cf0d2dcbe94704a528cf820000f776f6e646572776f6d616e31393933
G,47001635e60000,9673cc2a1aac64d7b2742705abfb09fca30d7e0500056d6d61736b
G,47001638a80000,c39342066646dc50f1a9954b41684d157b035dac00036f6e65
G,47001645ef0001,4689c1ccb4420309f93ab98799b28c49fa4d3809000a65617379737472656574
G,470016529a0000,f1628d66ae52295590b72b9a0b3a3527642a532600137465737470756230332d32312d323031372d32
G,470016529d0000,a4c61ced261ab571bdb3410ae140bec6c31f14ce00117465737470756230332d32312d32303137
G,47001655960000,2327bcb6d7578a2669e416b5aa185fe14ee8e03e00056569676874
G,47001664200000,f69099600bdca9b062ba60432dba3c0ca2241167002c686973746f72792d6f662d6672696564726963682d69692d6f662d707275737369612d766f6c756d652d3137
1 G
2 G 4700162aa70000 c78ac4c326cd43cdc0c844b7cea13659449ab3e40015746573742d70686f746f2d7374726173626f757267
3 G 4700162f600000 ebf95f7fdb89db5467bb1b88ea3b0f0f7ee5ce360003636e63
4 G 47001630960000 a6f91a86837ab84a4cf0d2dcbe94704a528cf820000f776f6e646572776f6d616e31393933
5 G 47001635e60000 9673cc2a1aac64d7b2742705abfb09fca30d7e0500056d6d61736b
6 G 47001638a80000 c39342066646dc50f1a9954b41684d157b035dac00036f6e65
7 G 47001645ef0001 4689c1ccb4420309f93ab98799b28c49fa4d3809000a65617379737472656574
8 G 470016529a0000 f1628d66ae52295590b72b9a0b3a3527642a532600137465737470756230332d32312d323031372d32
9 G 470016529d0000 a4c61ced261ab571bdb3410ae140bec6c31f14ce00117465737470756230332d32312d32303137
10 G 47001655960000 2327bcb6d7578a2669e416b5aa185fe14ee8e03e00056569676874
11 G 47001664200000 f69099600bdca9b062ba60432dba3c0ca2241167002c686973746f72792d6f662d6672696564726963682d69692d6f662d707275737369612d766f6c756d652d3137

2
testdata/G_resolve.csv vendored Normal file
View file

@ -0,0 +1,2 @@
G,,
G,47006284e30000,2556ed1cab9d17f2a9392030a9ad7f5d138f11bd00134073747978686578656e68616d6d6572363636
1 G
2 G 47006284e30000 2556ed1cab9d17f2a9392030a9ad7f5d138f11bd00134073747978686578656e68616d6d6572363636

11
testdata/H.csv vendored Normal file
View file

@ -0,0 +1,11 @@
H,
4800000000,010000000000000000000000000000000000000000000000000000000000000000000000cc59e59ff97ac092b55e423aa5495151ed6fb80570a5bb78cd5bd1c3821c21b8010000000000000000000000000000000000000000000000000000000000000033193156ffff001f07050000
4800000001,0000002063f4346a4db34fdfce29a70f5e8d11f065f6b91602b7036c7f22f3a03b28899cba888e2f9c037f831046f8ad09f6d378f79c728d003b177a64d29621f481da5d01000000000000000000000000000000000000000000000000000000000000003c406b5746e1001f5b4f0000
4800000002,00000020246cb85843ac936d55388f2ff288b011add5b1b20cca9cfd19a403ca2c9ecbde09d8734d81b5f2eb1b653caf17491544ddfbc72f2f4c0c3f22a3362db5ba9d4701000000000000000000000000000000000000000000000000000000000000003d406b57ffff001f4ff20000
4800000003,000000200044e1258b865d262587c28ff98853bc52bb31266230c1c648cc9004047a5428e285dbf24334585b9a924536a717160ee185a86d1eeb7b19684538685eca761a01000000000000000000000000000000000000000000000000000000000000003d406b5746e1001fce9c0100
4800000004,00000020bbf8980e3f7604896821203bf62f97f311124da1fbb95bf523fcfdb356ad19c9d83cf1408debbd631950b7a95b0c940772119cd8a615a3d44601568713fec80c01000000000000000000000000000000000000000000000000000000000000003e406b573dc6001fec7b0000
4800000005,000000201a650b9b7b9d132e257ff6b336ba7cd96b1796357c4fc8dd7d0bd1ff1de057d547638e54178dbdddf2e81a3b7566860e5264df6066755f9760a893f5caecc57901000000000000000000000000000000000000000000000000000000000000003e406b5773ae001fcf770000
4800000006,000000206d694b93a2bb5ac23a13ed6749a789ca751cf73d5982c459e0cd9d5d303da74cec91627e0dba856b933983425d7f72958e8f974682632a0fa2acee9cfd81940101000000000000000000000000000000000000000000000000000000000000003e406b578399001f225c0100
4800000007,00000020b57808c188b7315583cf120fe89de923583bc7a8ebff03189145b86bf859b21ba3c4a19948a1263722c45c5601fd10a7aea7cf73bfa45e060508f109155e80ab01000000000000000000000000000000000000000000000000000000000000003f406b571787001f08160700
4800000008,00000020a6a5b330e816242d54c8586ba9b6d63c19d921171ef3d4525b8ffc635742e83a0fc2da46cf0de0057c1b9fc93d997105ff6cf2c8c43269b446c1dbf5ac18be8c010000000000000000000000000000000000000000000000000000000000000040406b570ae1761edd8f0300
4800000009,00000020b8447f415279dffe8a09afe6f6d5e335a2f6911fce8e1d1866723d5e5e8a53067356a733f87e592ea133328792dd9d676ed83771c8ff0f519928ce752f159ba6010000000000000000000000000000000000000000000000000000000000000040406b57139d681ed40d0000
1 H
2 4800000000 010000000000000000000000000000000000000000000000000000000000000000000000cc59e59ff97ac092b55e423aa5495151ed6fb80570a5bb78cd5bd1c3821c21b8010000000000000000000000000000000000000000000000000000000000000033193156ffff001f07050000
3 4800000001 0000002063f4346a4db34fdfce29a70f5e8d11f065f6b91602b7036c7f22f3a03b28899cba888e2f9c037f831046f8ad09f6d378f79c728d003b177a64d29621f481da5d01000000000000000000000000000000000000000000000000000000000000003c406b5746e1001f5b4f0000
4 4800000002 00000020246cb85843ac936d55388f2ff288b011add5b1b20cca9cfd19a403ca2c9ecbde09d8734d81b5f2eb1b653caf17491544ddfbc72f2f4c0c3f22a3362db5ba9d4701000000000000000000000000000000000000000000000000000000000000003d406b57ffff001f4ff20000
5 4800000003 000000200044e1258b865d262587c28ff98853bc52bb31266230c1c648cc9004047a5428e285dbf24334585b9a924536a717160ee185a86d1eeb7b19684538685eca761a01000000000000000000000000000000000000000000000000000000000000003d406b5746e1001fce9c0100
6 4800000004 00000020bbf8980e3f7604896821203bf62f97f311124da1fbb95bf523fcfdb356ad19c9d83cf1408debbd631950b7a95b0c940772119cd8a615a3d44601568713fec80c01000000000000000000000000000000000000000000000000000000000000003e406b573dc6001fec7b0000
7 4800000005 000000201a650b9b7b9d132e257ff6b336ba7cd96b1796357c4fc8dd7d0bd1ff1de057d547638e54178dbdddf2e81a3b7566860e5264df6066755f9760a893f5caecc57901000000000000000000000000000000000000000000000000000000000000003e406b5773ae001fcf770000
8 4800000006 000000206d694b93a2bb5ac23a13ed6749a789ca751cf73d5982c459e0cd9d5d303da74cec91627e0dba856b933983425d7f72958e8f974682632a0fa2acee9cfd81940101000000000000000000000000000000000000000000000000000000000000003e406b578399001f225c0100
9 4800000007 00000020b57808c188b7315583cf120fe89de923583bc7a8ebff03189145b86bf859b21ba3c4a19948a1263722c45c5601fd10a7aea7cf73bfa45e060508f109155e80ab01000000000000000000000000000000000000000000000000000000000000003f406b571787001f08160700
10 4800000008 00000020a6a5b330e816242d54c8586ba9b6d63c19d921171ef3d4525b8ffc635742e83a0fc2da46cf0de0057c1b9fc93d997105ff6cf2c8c43269b446c1dbf5ac18be8c010000000000000000000000000000000000000000000000000000000000000040406b570ae1761edd8f0300
11 4800000009 00000020b8447f415279dffe8a09afe6f6d5e335a2f6911fce8e1d1866723d5e5e8a53067356a733f87e592ea133328792dd9d676ed83771c8ff0f519928ce752f159ba6010000000000000000000000000000000000000000000000000000000000000040406b57139d681ed40d0000

11
testdata/I.csv vendored Normal file
View file

@ -0,0 +1,11 @@
I,
49000000a420c44374f4f399ab4807fa1901eefc870297ec210000,a02a093c607b6772907f923cf15014397146874b
49000000c27eef5ea69e0d73f118826c7e326bb46900371d660000,b237333ca0e44b0a91429a20420ed9bc6ea56a53
4900000110e40894573f528c393fbcec7a472ec85301516b320000,f2a31cf5c4c3cd75a5c839d6b05947dfeb1970bb
4900000324e40fcb63a0b517a3660645e9bd99244a030bb6ba0000,6b450166f56b44370d026ea2f70eb1be3454cd3f
49000003d1538a0f19f5cd4bc1a62cc294f5c89934011c7c990000,ed6c15e48a78f5113fef78847525a5f664f76362
49000008d47beeff8325e795a8604226145b01702b02dbb2a20000,f2cf43b86b9d70175dc22dbb9ff7806241d90780
4900000906499e073e94370ceff37cb21c282124440369842d0000,f2cf43b86b9d70175dc22dbb9ff7806241d90780
49000009c3172e034a255f3c03566dca84bb9f046a0225c69c0000,dd3944bc7ae2717606e8e3ae3561f83befe484ea
49000009ca6e0caaaef16872b4bd4f6f1b8c2363e202b169560000,2fdbbf06aab844bb80ee6748244cadc5d698411e
4900000ad9ded2e15d18987900d09e9b29ef33d03e02c972b30000,f6ac161e5f4100b6bb22544460b7dfc2af9b1ec3
1 I
2 49000000a420c44374f4f399ab4807fa1901eefc870297ec210000 a02a093c607b6772907f923cf15014397146874b
3 49000000c27eef5ea69e0d73f118826c7e326bb46900371d660000 b237333ca0e44b0a91429a20420ed9bc6ea56a53
4 4900000110e40894573f528c393fbcec7a472ec85301516b320000 f2a31cf5c4c3cd75a5c839d6b05947dfeb1970bb
5 4900000324e40fcb63a0b517a3660645e9bd99244a030bb6ba0000 6b450166f56b44370d026ea2f70eb1be3454cd3f
6 49000003d1538a0f19f5cd4bc1a62cc294f5c89934011c7c990000 ed6c15e48a78f5113fef78847525a5f664f76362
7 49000008d47beeff8325e795a8604226145b01702b02dbb2a20000 f2cf43b86b9d70175dc22dbb9ff7806241d90780
8 4900000906499e073e94370ceff37cb21c282124440369842d0000 f2cf43b86b9d70175dc22dbb9ff7806241d90780
9 49000009c3172e034a255f3c03566dca84bb9f046a0225c69c0000 dd3944bc7ae2717606e8e3ae3561f83befe484ea
10 49000009ca6e0caaaef16872b4bd4f6f1b8c2363e202b169560000 2fdbbf06aab844bb80ee6748244cadc5d698411e
11 4900000ad9ded2e15d18987900d09e9b29ef33d03e02c972b30000 f6ac161e5f4100b6bb22544460b7dfc2af9b1ec3

2
testdata/I_resolve.csv vendored Normal file
View file

@ -0,0 +1,2 @@
I,,
I,499a0ed686ecdad9b6cb965c4d6681c02f0bbc66a60369e2b20000,2556ed1cab9d17f2a9392030a9ad7f5d138f11bd
1 I
2 I 499a0ed686ecdad9b6cb965c4d6681c02f0bbc66a60369e2b20000 2556ed1cab9d17f2a9392030a9ad7f5d138f11bd

11
testdata/J.csv vendored Normal file
View file

@ -0,0 +1,11 @@
J,
4a00009f35397ada0476b04c67978ad081b50833ed0005676866676800201f7f0000,2febc9f39e70fac69ce7504dc7fb9523c9617c68
4a0000cbef248847373c999de142bc2d7da4d014100013646f6d656e7a61696e2d657374616661646f720358fe5f0000,2b15c03bb512d84e7450b0fbdbc4db1f9b454137
4a0000eff19123a0b3087a0f059a5e0c10d74377560010636176652d73746f72792d7974702d3102fb1edf0000,dc07a33c5e8db91e5f27ea6cfc6415677c834d34
4a0000eff19123a0b3087a0f059a5e0c10d7437756001b636c6f646f72212d67616d652d6f662d7468726f6e65732d79747002fb1ed60000,c869ff2f82cc7d28d63cfe672de46042898d6a90
4a0000eff19123a0b3087a0f059a5e0c10d74377560020636f6e636c6176652d6f662d7468652d63686f73656e2d28776f772d6266612902fb1ecf0000,2d3225c7800a8e8098aa63c9a2c5ddd6ac30f968
4a0000eff19123a0b3087a0f059a5e0c10d7437756002077686f27732d746861742d706f6b656d6f6e2d697427732d636174657270696502fb1ed00000,627769e3f4f577261c3aec4addd05890747a5c8b
4a0000eff19123a0b3087a0f059a5e0c10d7437756002077686f27732d746861742d706f6b656d6f6e2d697427732d70696b616368752102fb1ed40000,45d8a2b4f512f4eb8bf6409454975235343d4b40
4a0000eff19123a0b3087a0f059a5e0c10d7437756002162726f636b2d74726965732d746f2d736176652d736861796d696e2d7573696e670341a2c40000,2c9d219c6ce4596cffb250950c3d94938f8df91b
4a0000eff19123a0b3087a0f059a5e0c10d74377560021706d64322d6578706c6f726572732d6f662d736b792d706c61797468726f7567680315ca790000,be33e079423884c11969b3fd8f7586dee8e3a73f
4a0000eff19123a0b3087a0f059a5e0c10d74377560021706f6b656d6f6e2d756e6974652d736561736f6e2d322d286c6f6c6e6f7065212903d81d740000,236c0428226580eaffee6beed1b89fdff7c4e582
1 J
2 4a00009f35397ada0476b04c67978ad081b50833ed0005676866676800201f7f0000 2febc9f39e70fac69ce7504dc7fb9523c9617c68
3 4a0000cbef248847373c999de142bc2d7da4d014100013646f6d656e7a61696e2d657374616661646f720358fe5f0000 2b15c03bb512d84e7450b0fbdbc4db1f9b454137
4 4a0000eff19123a0b3087a0f059a5e0c10d74377560010636176652d73746f72792d7974702d3102fb1edf0000 dc07a33c5e8db91e5f27ea6cfc6415677c834d34
5 4a0000eff19123a0b3087a0f059a5e0c10d7437756001b636c6f646f72212d67616d652d6f662d7468726f6e65732d79747002fb1ed60000 c869ff2f82cc7d28d63cfe672de46042898d6a90
6 4a0000eff19123a0b3087a0f059a5e0c10d74377560020636f6e636c6176652d6f662d7468652d63686f73656e2d28776f772d6266612902fb1ecf0000 2d3225c7800a8e8098aa63c9a2c5ddd6ac30f968
7 4a0000eff19123a0b3087a0f059a5e0c10d7437756002077686f27732d746861742d706f6b656d6f6e2d697427732d636174657270696502fb1ed00000 627769e3f4f577261c3aec4addd05890747a5c8b
8 4a0000eff19123a0b3087a0f059a5e0c10d7437756002077686f27732d746861742d706f6b656d6f6e2d697427732d70696b616368752102fb1ed40000 45d8a2b4f512f4eb8bf6409454975235343d4b40
9 4a0000eff19123a0b3087a0f059a5e0c10d7437756002162726f636b2d74726965732d746f2d736176652d736861796d696e2d7573696e670341a2c40000 2c9d219c6ce4596cffb250950c3d94938f8df91b
10 4a0000eff19123a0b3087a0f059a5e0c10d74377560021706d64322d6578706c6f726572732d6f662d736b792d706c61797468726f7567680315ca790000 be33e079423884c11969b3fd8f7586dee8e3a73f
11 4a0000eff19123a0b3087a0f059a5e0c10d74377560021706f6b656d6f6e2d756e6974652d736561736f6e2d322d286c6f6c6e6f7065212903d81d740000 236c0428226580eaffee6beed1b89fdff7c4e582

2
testdata/J_resolve.csv vendored Normal file
View file

@ -0,0 +1,2 @@
J,,
J,4a2556ed1cab9d17f2a9392030a9ad7f5d138f11bd0013612d73747265616d2d696e2d7665726d6f6e740069a2570000,ee29ca5c44313a2827fd35252b72557334635749
1 J
2 J 4a2556ed1cab9d17f2a9392030a9ad7f5d138f11bd0013612d73747265616d2d696e2d7665726d6f6e740069a2570000 ee29ca5c44313a2827fd35252b72557334635749

11
testdata/K.csv vendored Normal file
View file

@ -0,0 +1,11 @@
K,
4b00000324e40fcb63a0b517a3660645e9bd99244a030bc8a50000,0000000001312d00
4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6e2aa0126,0000000001ea252a
4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6e7d20151,0000000005be6f7e
4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6eeb6014d,0000000001eac106
4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6eed00037,0000000001eac106
4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6f4f00133,0000000001eac106
4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6f4f001e5,0000000001eac106
4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6f9b6013e,0000000001eac106
4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6f9b7012f,0000000003d5820c
4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6f9b9001c,0000000003d5820c
1 K
2 4b00000324e40fcb63a0b517a3660645e9bd99244a030bc8a50000 0000000001312d00
3 4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6e2aa0126 0000000001ea252a
4 4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6e7d20151 0000000005be6f7e
5 4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6eeb6014d 0000000001eac106
6 4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6eed00037 0000000001eac106
7 4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6f4f00133 0000000001eac106
8 4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6f4f001e5 0000000001eac106
9 4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6f9b6013e 0000000001eac106
10 4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6f9b7012f 0000000003d5820c
11 4b000023415fc7ba8a470f0cdf4a66bffacd5ba97902c6f9b9001c 0000000003d5820c

11
testdata/L.csv vendored Normal file
View file

@ -0,0 +1,11 @@
L,
4c000059610000,04c7d5e2360f10ab8e28d5d831abb29b72cea3a8
4c0000596d0000,04c7d5e2360f10ab8e28d5d831abb29b72cea3a8
4c00029e100000,a467b70b0a9ddba924d0a115206fe7c1cb25a346
4c000449380000,c6ddef5e005606bd816177c7f0cba2404c719131
4c000864bb0000,32d4fc78396c239f5c1a0a041242eebb26367509
4c000c380b0000,c2ba0ad053f45d77ae569a1b5c407bc213365fda
4c001030b40000,467513d3a6eed0114964d751cd85ed49c8e3af4e
4c0011116c0000,d565af1863ccf28e9d90f8730e40b5ee1d72258d
4c001111e90000,1e81ee06e8293438fbcbe9196494a69be0426c8b
4c0016c4180001,49cb931d20a96e17348aabbc28b5838e1a650d8c
1 L
2 4c000059610000 04c7d5e2360f10ab8e28d5d831abb29b72cea3a8
3 4c0000596d0000 04c7d5e2360f10ab8e28d5d831abb29b72cea3a8
4 4c00029e100000 a467b70b0a9ddba924d0a115206fe7c1cb25a346
5 4c000449380000 c6ddef5e005606bd816177c7f0cba2404c719131
6 4c000864bb0000 32d4fc78396c239f5c1a0a041242eebb26367509
7 4c000c380b0000 c2ba0ad053f45d77ae569a1b5c407bc213365fda
8 4c001030b40000 467513d3a6eed0114964d751cd85ed49c8e3af4e
9 4c0011116c0000 d565af1863ccf28e9d90f8730e40b5ee1d72258d
10 4c001111e90000 1e81ee06e8293438fbcbe9196494a69be0426c8b
11 4c0016c4180001 49cb931d20a96e17348aabbc28b5838e1a650d8c

2
testdata/M.csv vendored Normal file

File diff suppressed because one or more lines are too long

11
testdata/N.csv vendored Normal file
View file

@ -0,0 +1,11 @@
N,
4e00000031a2e262d60074f07330d7187907e5b02be8f9b3c60cdc03d776314912,01376ce8
4e0000004e91edda0f9cd3bcef9565a31e6bbbd34c731483e03ec7d8819158ac30,030ee002
4e0000008070865693cd82ed0f59896e34973adbff0583fb8a1293919591446075,03518017
4e0000009c24d4d9187749a1f8f6d6c0f92a5e98817f5efcd427a5593344a45a2e,019436d7
4e000000cee66b136a85596df24ece60bbd1392f70204fd2f144f059e5195ee3c9,00169e07
4e000000e0bf96accd4eda4d871c5bc8c0ebb14509b896a867095f9b419f9b04f2,02bcc37a
4e0000011daab2a9f45cca3d0bac6fee69324af4757b1c4e8c3a362efbb6e8a09d,00c4c1e3
4e00000150116856ce8ce06604b9584e61afb3b613a8a5a512ab260e7f0cbe5496,003a51fa
4e0000015ba8df5c3ed80d0fc364136de02e3ba9d9550cbb1ecef03b97fcdf0621,00d725d0
4e000001d4a53fc92321415631862a791f8680241ed172e579534713f68a6869ba,025e8166
1 N
2 4e00000031a2e262d60074f07330d7187907e5b02be8f9b3c60cdc03d776314912 01376ce8
3 4e0000004e91edda0f9cd3bcef9565a31e6bbbd34c731483e03ec7d8819158ac30 030ee002
4 4e0000008070865693cd82ed0f59896e34973adbff0583fb8a1293919591446075 03518017
5 4e0000009c24d4d9187749a1f8f6d6c0f92a5e98817f5efcd427a5593344a45a2e 019436d7
6 4e000000cee66b136a85596df24ece60bbd1392f70204fd2f144f059e5195ee3c9 00169e07
7 4e000000e0bf96accd4eda4d871c5bc8c0ebb14509b896a867095f9b419f9b04f2 02bcc37a
8 4e0000011daab2a9f45cca3d0bac6fee69324af4757b1c4e8c3a362efbb6e8a09d 00c4c1e3
9 4e00000150116856ce8ce06604b9584e61afb3b613a8a5a512ab260e7f0cbe5496 003a51fa
10 4e0000015ba8df5c3ed80d0fc364136de02e3ba9d9550cbb1ecef03b97fcdf0621 00d725d0
11 4e000001d4a53fc92321415631862a791f8680241ed172e579534713f68a6869ba 025e8166

11
testdata/O.csv vendored Normal file
View file

@ -0,0 +1,11 @@
O,
4f00222fbd00162aa70000,c78ac4c326cd43cdc0c844b7cea13659449ab3e40015746573742d70686f746f2d7374726173626f757267
4f002230a700162f600000,ebf95f7fdb89db5467bb1b88ea3b0f0f7ee5ce360003636e63
4f002230e3001630960000,a6f91a86837ab84a4cf0d2dcbe94704a528cf820000f776f6e646572776f6d616e31393933
4f002231e8001635e60000,9673cc2a1aac64d7b2742705abfb09fca30d7e0500056d6d61736b
4f00223246001638a80000,c39342066646dc50f1a9954b41684d157b035dac00036f6e65
4f00223494001645ef0001,4689c1ccb4420309f93ab98799b28c49fa4d3809000a65617379737472656574
4f002236df0016529a0000,f1628d66ae52295590b72b9a0b3a3527642a532600137465737470756230332d32312d323031372d32
4f002236e10016529d0000,a4c61ced261ab571bdb3410ae140bec6c31f14ce00117465737470756230332d32312d32303137
4f00223774001655960000,2327bcb6d7578a2669e416b5aa185fe14ee8e03e00056569676874
4f00223a04001664200000,f69099600bdca9b062ba60432dba3c0ca2241167002c686973746f72792d6f662d6672696564726963682d69692d6f662d707275737369612d766f6c756d652d3137
1 O
2 4f00222fbd00162aa70000 c78ac4c326cd43cdc0c844b7cea13659449ab3e40015746573742d70686f746f2d7374726173626f757267
3 4f002230a700162f600000 ebf95f7fdb89db5467bb1b88ea3b0f0f7ee5ce360003636e63
4 4f002230e3001630960000 a6f91a86837ab84a4cf0d2dcbe94704a528cf820000f776f6e646572776f6d616e31393933
5 4f002231e8001635e60000 9673cc2a1aac64d7b2742705abfb09fca30d7e0500056d6d61736b
6 4f00223246001638a80000 c39342066646dc50f1a9954b41684d157b035dac00036f6e65
7 4f00223494001645ef0001 4689c1ccb4420309f93ab98799b28c49fa4d3809000a65617379737472656574
8 4f002236df0016529a0000 f1628d66ae52295590b72b9a0b3a3527642a532600137465737470756230332d32312d323031372d32
9 4f002236e10016529d0000 a4c61ced261ab571bdb3410ae140bec6c31f14ce00117465737470756230332d32312d32303137
10 4f00223774001655960000 2327bcb6d7578a2669e416b5aa185fe14ee8e03e00056569676874
11 4f00223a04001664200000 f69099600bdca9b062ba60432dba3c0ca2241167002c686973746f72792d6f662d6672696564726963682d69692d6f662d707275737369612d766f6c756d652d3137

11
testdata/P.csv vendored Normal file
View file

@ -0,0 +1,11 @@
P,
50000100,a51d5c567412654e6d741114fea6fb851dec73800004831f
50000101,11158037afca9c2efabc3dff55e352bf1f5634c50004831f
50000102,a4a575934de77d8ec8589595d8cd91857e3cf5ba0004831f
50000103,f595a21fb597bd030defefda3df9f8f4a3e0cb860004831f
50000104,682ccb0518a6bd00c955949d9ef330d3ac18cb800004831f
50000105,078a435851bf97c5cc36e8b03e3208a30d27679f0004831f
50000106,1a4a75246a766cf21a629f619bc5bcb531de7a5a0004831f
50000107,b144ad496b29b9c12c316f319773adcdd4c9bce20004831f
50000108,af2a09232fc6bf664088d65da42fe0345b4589600004831f
50000109,7b2ab15758c519116fb9ba9331a3b9ee7530831f0004831f
1 P
2 50000100 a51d5c567412654e6d741114fea6fb851dec73800004831f
3 50000101 11158037afca9c2efabc3dff55e352bf1f5634c50004831f
4 50000102 a4a575934de77d8ec8589595d8cd91857e3cf5ba0004831f
5 50000103 f595a21fb597bd030defefda3df9f8f4a3e0cb860004831f
6 50000104 682ccb0518a6bd00c955949d9ef330d3ac18cb800004831f
7 50000105 078a435851bf97c5cc36e8b03e3208a30d27679f0004831f
8 50000106 1a4a75246a766cf21a629f619bc5bcb531de7a5a0004831f
9 50000107 b144ad496b29b9c12c316f319773adcdd4c9bce20004831f
10 50000108 af2a09232fc6bf664088d65da42fe0345b4589600004831f
11 50000109 7b2ab15758c519116fb9ba9331a3b9ee7530831f0004831f

2
testdata/P_resolve.csv vendored Normal file
View file

@ -0,0 +1,2 @@
P,,
P,5000134073747978686578656e68616d6d6572363636,2556ed1cab9d17f2a9392030a9ad7f5d138f11bd000a6471
1 P
2 P 5000134073747978686578656e68616d6d6572363636 2556ed1cab9d17f2a9392030a9ad7f5d138f11bd000a6471

11
testdata/Q.csv vendored Normal file
View file

@ -0,0 +1,11 @@
Q,
5100002e5002000059610000,04c7d5e2360f10ab8e28d5d831abb29b72cea3a8000c697473616469736173746572
5100002e52020000596d0000,04c7d5e2360f10ab8e28d5d831abb29b72cea3a8000c697473616469736173746572
5100005d570200025e360000,32d4fc78396c239f5c1a0a041242eebb2636750900036f6e65
5100005d590200025e570001,c923ae766c269535048c06674261e546004375ab000374776f
5100005d5a0200025e6a0001,c923ae766c269535048c06674261e546004375ab000374776f
51000061e50200029e100000,a467b70b0a9ddba924d0a115206fe7c1cb25a3460003707567
51000080a002000449380000,c6ddef5e005606bd816177c7f0cba2404c7191310006757465737432
510000c3c102000864bb0000,32d4fc78396c239f5c1a0a041242eebb2636750900036f6e65
510000fd8002000c380b0000,c2ba0ad053f45d77ae569a1b5c407bc213365fda00037a6564
510001556c02001030b40000,467513d3a6eed0114964d751cd85ed49c8e3af4e000b776172616e647065616365
1 Q
2 5100002e5002000059610000 04c7d5e2360f10ab8e28d5d831abb29b72cea3a8000c697473616469736173746572
3 5100002e52020000596d0000 04c7d5e2360f10ab8e28d5d831abb29b72cea3a8000c697473616469736173746572
4 5100005d570200025e360000 32d4fc78396c239f5c1a0a041242eebb2636750900036f6e65
5 5100005d590200025e570001 c923ae766c269535048c06674261e546004375ab000374776f
6 5100005d5a0200025e6a0001 c923ae766c269535048c06674261e546004375ab000374776f
7 51000061e50200029e100000 a467b70b0a9ddba924d0a115206fe7c1cb25a3460003707567
8 51000080a002000449380000 c6ddef5e005606bd816177c7f0cba2404c7191310006757465737432
9 510000c3c102000864bb0000 32d4fc78396c239f5c1a0a041242eebb2636750900036f6e65
10 510000fd8002000c380b0000 c2ba0ad053f45d77ae569a1b5c407bc213365fda00037a6564
11 510001556c02001030b40000 467513d3a6eed0114964d751cd85ed49c8e3af4e000b776172616e647065616365

11
testdata/R.csv vendored Normal file
View file

@ -0,0 +1,11 @@
R,
520100162aa70000,00021b3dc78ac4c326cd43cdc0c844b7cea13659449ab3e40015746573742d70686f746f2d7374726173626f757267
520100162f600000,00021c27ebf95f7fdb89db5467bb1b88ea3b0f0f7ee5ce360003636e63
5201001630960000,00021c63a6f91a86837ab84a4cf0d2dcbe94704a528cf820000f776f6e646572776f6d616e31393933
5201001635e60000,00021d689673cc2a1aac64d7b2742705abfb09fca30d7e0500056d6d61736b
5201001638a80000,00021f4bc39342066646dc50f1a9954b41684d157b035dac00036f6e65
5201001645ef0001,000220144689c1ccb4420309f93ab98799b28c49fa4d3809000a65617379737472656574
52010016529a0000,0002225ff1628d66ae52295590b72b9a0b3a3527642a532600137465737470756230332d32312d323031372d32
52010016529d0000,00022261a4c61ced261ab571bdb3410ae140bec6c31f14ce00117465737470756230332d32312d32303137
5201001655960000,000222f42327bcb6d7578a2669e416b5aa185fe14ee8e03e00056569676874
5201001664200000,00022584f69099600bdca9b062ba60432dba3c0ca2241167002c686973746f72792d6f662d6672696564726963682d69692d6f662d707275737369612d766f6c756d652d3137
1 R
2 520100162aa70000 00021b3dc78ac4c326cd43cdc0c844b7cea13659449ab3e40015746573742d70686f746f2d7374726173626f757267
3 520100162f600000 00021c27ebf95f7fdb89db5467bb1b88ea3b0f0f7ee5ce360003636e63
4 5201001630960000 00021c63a6f91a86837ab84a4cf0d2dcbe94704a528cf820000f776f6e646572776f6d616e31393933
5 5201001635e60000 00021d689673cc2a1aac64d7b2742705abfb09fca30d7e0500056d6d61736b
6 5201001638a80000 00021f4bc39342066646dc50f1a9954b41684d157b035dac00036f6e65
7 5201001645ef0001 000220144689c1ccb4420309f93ab98799b28c49fa4d3809000a65617379737472656574
8 52010016529a0000 0002225ff1628d66ae52295590b72b9a0b3a3527642a532600137465737470756230332d32312d323031372d32
9 52010016529d0000 00022261a4c61ced261ab571bdb3410ae140bec6c31f14ce00117465737470756230332d32312d32303137
10 5201001655960000 000222f42327bcb6d7578a2669e416b5aa185fe14ee8e03e00056569676874
11 5201001664200000 00022584f69099600bdca9b062ba60432dba3c0ca2241167002c686973746f72792d6f662d6672696564726963682d69692d6f662d707275737369612d766f6c756d652d3137

2
testdata/R_resolve.csv vendored Normal file
View file

@ -0,0 +1,2 @@
R,,
R,5201006284e30000,000a6b652556ed1cab9d17f2a9392030a9ad7f5d138f11bd00134073747978686578656e68616d6d6572363636
1 R
2 R 5201006284e30000 000a6b652556ed1cab9d17f2a9392030a9ad7f5d138f11bd00134073747978686578656e68616d6d6572363636

11
testdata/S.csv vendored Normal file
View file

@ -0,0 +1,11 @@
S,
53000000a420c44374f4f399ab4807fa1901eefc8701000e94ad0297ec210000,00000000000f4240
53000000c27eef5ea69e0d73f118826c7e326bb46901000773de00371d660000,000000001dcd6500
5300000110e40894573f528c393fbcec7a472ec85301000d069c01516b320000,0000000000989680
5300000324e40fcb63a0b517a3660645e9bd99244a01000f2fd8030bb6ba0000,00000000000f4240
5300000324e40fcb63a0b517a3660645e9bd99244a02000f2ff4030bc8a50000,0000000001312d00
53000003d1538a0f19f5cd4bc1a62cc294f5c8993401000c816a011c7c990000,00000000000f4240
53000008d47beeff8325e795a8604226145b01702b01000ef1ed02dbb2a20000,00000000000186a0
5300000906499e073e94370ceff37cb21c2821244401000fa7c40369842d0000,00000000000186a0
53000009c3172e034a255f3c03566dca84bb9f046a01000e07020225c69c0000,000000000007a120
53000009ca6e0caaaef16872b4bd4f6f1b8c2363e201000eb5af02b169560000,00000000000f4240
1 S
2 53000000a420c44374f4f399ab4807fa1901eefc8701000e94ad0297ec210000 00000000000f4240
3 53000000c27eef5ea69e0d73f118826c7e326bb46901000773de00371d660000 000000001dcd6500
4 5300000110e40894573f528c393fbcec7a472ec85301000d069c01516b320000 0000000000989680
5 5300000324e40fcb63a0b517a3660645e9bd99244a01000f2fd8030bb6ba0000 00000000000f4240
6 5300000324e40fcb63a0b517a3660645e9bd99244a02000f2ff4030bc8a50000 0000000001312d00
7 53000003d1538a0f19f5cd4bc1a62cc294f5c8993401000c816a011c7c990000 00000000000f4240
8 53000008d47beeff8325e795a8604226145b01702b01000ef1ed02dbb2a20000 00000000000186a0
9 5300000906499e073e94370ceff37cb21c2821244401000fa7c40369842d0000 00000000000186a0
10 53000009c3172e034a255f3c03566dca84bb9f046a01000e07020225c69c0000 000000000007a120
11 53000009ca6e0caaaef16872b4bd4f6f1b8c2363e201000eb5af02b169560000 00000000000f4240

11
testdata/S_resolve.csv vendored Normal file
View file

@ -0,0 +1,11 @@
S,,
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd01000a6b67006286030000,0000007615cbad28
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd01000a706a0063105c0000,000000000bebc200
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a73ea006367550000,0000000005f5e100
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a7d63006469750000,0000000db0b7c894
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a7ebf00648c480000,00000000b2d05e00
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a810e0064ccc00000,000000003b9aca00
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a825b006503cf0000,00000002bf52c92c
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88930066814a0000,00000000dc887a34
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88f900669d240000,0000000005f5e100
S,532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88f900669d260000,000000001dcd6500
1 S
2 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd01000a6b67006286030000 0000007615cbad28
3 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd01000a706a0063105c0000 000000000bebc200
4 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a73ea006367550000 0000000005f5e100
5 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a7d63006469750000 0000000db0b7c894
6 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a7ebf00648c480000 00000000b2d05e00
7 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a810e0064ccc00000 000000003b9aca00
8 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a825b006503cf0000 00000002bf52c92c
9 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88930066814a0000 00000000dc887a34
10 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88f900669d240000 0000000005f5e100
11 S 532556ed1cab9d17f2a9392030a9ad7f5d138f11bd02000a88f900669d260000 000000001dcd6500

18
testdata/Si_resolve.csv vendored Normal file
View file

@ -0,0 +1,18 @@
Si,,
S,53000000a420c44374f4f399ab4807fa1901eefc8701000e94ad0297ec210000,00000000000f4240
S,53000000c27eef5ea69e0d73f118826c7e326bb46901000773de00371d660000,000000001dcd6500
S,5300000110e40894573f528c393fbcec7a472ec85301000d069c01516b320000,0000000000989680
S,5300000324e40fcb63a0b517a3660645e9bd99244a01000f2fd8030bb6ba0000,00000000000f4240
S,5300000324e40fcb63a0b517a3660645e9bd99244a02000f2ff4030bc8a50000,0000000001312d00
S,5300000324e40fcb63a0b517a3660645e9bd99244a02000f2ff6030bc8b00000,0000000000000003
S,5300000324e40fcb63a0b517a3660645e9bd99244a02000f2ff7030bc8b10000,0000000000000002
S,5300000324e40fcb63a0b517a3660645e9bd99244a02000f2ff9030bc8cf0000,0000000000000001
S,53000003d1538a0f19f5cd4bc1a62cc294f5c8993401000c816a011c7c990000,00000000000f4240
S,53000008d47beeff8325e795a8604226145b01702b01000ef1ed02dbb2a20000,00000000000186a0
S,5300000906499e073e94370ceff37cb21c2821244401000fa7c40369842d0000,00000000000186a0
S,5300000906499e073e94370ceff37cb21c2821244402000fa7c403698fff0000,00000000000000a1
S,5300000906499e073e94370ceff37cb21c2821244402000fa7c80369f0010000,000000000000000f
S,53000009c3172e034a255f3c03566dca84bb9f046a01000e07020225c69c0000,000000000007a120
S,53000009ca6e0caaaef16872b4bd4f6f1b8c2363e201000eb5af02b169560000,00000000000f4240
i,6900000324e40fcb63a0b517a3660645e9bd99244a,0000000001406f460000000001312d06
i,6900000906499e073e94370ceff37cb21c28212444,000000000001875000000000000000b0
1 Si
2 S 53000000a420c44374f4f399ab4807fa1901eefc8701000e94ad0297ec210000 00000000000f4240
3 S 53000000c27eef5ea69e0d73f118826c7e326bb46901000773de00371d660000 000000001dcd6500
4 S 5300000110e40894573f528c393fbcec7a472ec85301000d069c01516b320000 0000000000989680
5 S 5300000324e40fcb63a0b517a3660645e9bd99244a01000f2fd8030bb6ba0000 00000000000f4240
6 S 5300000324e40fcb63a0b517a3660645e9bd99244a02000f2ff4030bc8a50000 0000000001312d00
7 S 5300000324e40fcb63a0b517a3660645e9bd99244a02000f2ff6030bc8b00000 0000000000000003
8 S 5300000324e40fcb63a0b517a3660645e9bd99244a02000f2ff7030bc8b10000 0000000000000002
9 S 5300000324e40fcb63a0b517a3660645e9bd99244a02000f2ff9030bc8cf0000 0000000000000001
10 S 53000003d1538a0f19f5cd4bc1a62cc294f5c8993401000c816a011c7c990000 00000000000f4240
11 S 53000008d47beeff8325e795a8604226145b01702b01000ef1ed02dbb2a20000 00000000000186a0
12 S 5300000906499e073e94370ceff37cb21c2821244401000fa7c40369842d0000 00000000000186a0
13 S 5300000906499e073e94370ceff37cb21c2821244402000fa7c403698fff0000 00000000000000a1
14 S 5300000906499e073e94370ceff37cb21c2821244402000fa7c80369f0010000 000000000000000f
15 S 53000009c3172e034a255f3c03566dca84bb9f046a01000e07020225c69c0000 000000000007a120
16 S 53000009ca6e0caaaef16872b4bd4f6f1b8c2363e201000eb5af02b169560000 00000000000f4240
17 i 6900000324e40fcb63a0b517a3660645e9bd99244a 0000000001406f460000000001312d06
18 i 6900000906499e073e94370ceff37cb21c28212444 000000000001875000000000000000b0

11
testdata/T.csv vendored Normal file
View file

@ -0,0 +1,11 @@
T,
5400000000,00000001
5400000001,00000002
5400000002,00000003
5400000003,00000004
5400000004,00000005
5400000005,00000006
5400000006,00000007
5400000007,00000008
5400000008,00000009
5400000009,0000000a
1 T
2 5400000000 00000001
3 5400000001 00000002
4 5400000002 00000003
5 5400000003 00000004
6 5400000004 00000005
7 5400000005 00000006
8 5400000006 00000007
9 5400000007 00000008
10 5400000008 00000009
11 5400000009 0000000a

Some files were not shown because too many files have changed in this diff Show more