Add some blockchain RPC handlers and database fetching routines.
This commit is contained in:
parent
9403d84a83
commit
fe18c70bf7
7 changed files with 554 additions and 2 deletions
63
db/db.go
63
db/db.go
|
@ -406,6 +406,69 @@ func Iter(db *grocksdb.DB, opts *IterOptions) <-chan *prefixes.PrefixRowKV {
|
|||
return ch
|
||||
}
|
||||
|
||||
func (db *ReadOnlyDBColumnFamily) selectFrom(prefix []byte, startKey, stopKey prefixes.BaseKey) ([]*IterOptions, error) {
|
||||
handle, err := db.EnsureHandle(prefixes.HashXHistory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Prefix and handle
|
||||
options := NewIterateOptions().WithPrefix(prefix).WithCfHandle(handle)
|
||||
// Start and stop bounds
|
||||
options = options.WithStart(startKey.PackKey()).WithStop(stopKey.PackKey()).WithIncludeStop(true)
|
||||
// Don't include the key
|
||||
options = options.WithIncludeKey(true).WithIncludeValue(true)
|
||||
return []*IterOptions{options}, nil
|
||||
}
|
||||
|
||||
func iterate(db *grocksdb.DB, opts []*IterOptions) <-chan []*prefixes.PrefixRowKV {
|
||||
out := make(chan []*prefixes.PrefixRowKV)
|
||||
routine := func() {
|
||||
for _, o := range opts {
|
||||
for kv := range IterCF(db, o) {
|
||||
row := make([]*prefixes.PrefixRowKV, 0, 1)
|
||||
row = append(row, kv)
|
||||
out <- row
|
||||
}
|
||||
}
|
||||
close(out)
|
||||
}
|
||||
go routine()
|
||||
return out
|
||||
}
|
||||
|
||||
func innerJoin(db *grocksdb.DB, in <-chan []*prefixes.PrefixRowKV, selectFn func([]*prefixes.PrefixRowKV) ([]*IterOptions, error)) <-chan []*prefixes.PrefixRowKV {
|
||||
out := make(chan []*prefixes.PrefixRowKV)
|
||||
routine := func() {
|
||||
for kvs := range in {
|
||||
selected, err := selectFn(kvs)
|
||||
if err != nil {
|
||||
out <- []*prefixes.PrefixRowKV{{Error: err}}
|
||||
close(out)
|
||||
return
|
||||
}
|
||||
for kv := range iterate(db, selected) {
|
||||
row := make([]*prefixes.PrefixRowKV, 0, len(kvs)+1)
|
||||
row = append(row, kvs...)
|
||||
row = append(row, kv...)
|
||||
out <- row
|
||||
}
|
||||
}
|
||||
close(out)
|
||||
return
|
||||
}
|
||||
go routine()
|
||||
return out
|
||||
}
|
||||
|
||||
func checkForError(kvs []*prefixes.PrefixRowKV) error {
|
||||
for _, kv := range kvs {
|
||||
if kv.Error != nil {
|
||||
return kv.Error
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
// GetDB functions that open and return a db
|
||||
//
|
||||
|
|
183
db/db_get.go
183
db/db_get.go
|
@ -6,8 +6,10 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
|
||||
"github.com/lbryio/herald.go/db/prefixes"
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/linxGnu/grocksdb"
|
||||
)
|
||||
|
||||
|
@ -82,6 +84,187 @@ func (db *ReadOnlyDBColumnFamily) GetHeader(height uint32) ([]byte, error) {
|
|||
return rawValue, nil
|
||||
}
|
||||
|
||||
func (db *ReadOnlyDBColumnFamily) GetHeaders(height uint32, count uint32) ([][112]byte, error) {
|
||||
handle, err := db.EnsureHandle(prefixes.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
startKeyRaw := prefixes.NewHeaderKey(0).PackKey()
|
||||
endKeyRaw := prefixes.NewHeaderKey(height + count).PackKey()
|
||||
options := NewIterateOptions().WithPrefix([]byte{prefixes.Header}).WithCfHandle(handle)
|
||||
options = options.WithIncludeKey(false).WithIncludeValue(true) //.WithIncludeStop(true)
|
||||
options = options.WithStart(startKeyRaw).WithStop(endKeyRaw)
|
||||
|
||||
result := make([][112]byte, 0, count)
|
||||
for kv := range IterCF(db.DB, options) {
|
||||
h := [112]byte{}
|
||||
copy(h[:], kv.Value.(*prefixes.BlockHeaderValue).Header[:112])
|
||||
result = append(result, h)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (db *ReadOnlyDBColumnFamily) GetBalance(hashX []byte) (uint64, uint64, error) {
|
||||
handle, err := db.EnsureHandle(prefixes.UTXO)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
startKey := prefixes.UTXOKey{
|
||||
Prefix: []byte{prefixes.UTXO},
|
||||
HashX: hashX,
|
||||
TxNum: 0,
|
||||
Nout: 0,
|
||||
}
|
||||
endKey := prefixes.UTXOKey{
|
||||
Prefix: []byte{prefixes.UTXO},
|
||||
HashX: hashX,
|
||||
TxNum: math.MaxUint32,
|
||||
Nout: math.MaxUint16,
|
||||
}
|
||||
|
||||
startKeyRaw := startKey.PackKey()
|
||||
endKeyRaw := endKey.PackKey()
|
||||
// Prefix and handle
|
||||
options := NewIterateOptions().WithPrefix([]byte{prefixes.UTXO}).WithCfHandle(handle)
|
||||
// Start and stop bounds
|
||||
options = options.WithStart(startKeyRaw).WithStop(endKeyRaw).WithIncludeStop(true)
|
||||
// Don't include the key
|
||||
options = options.WithIncludeKey(false).WithIncludeValue(true)
|
||||
|
||||
ch := IterCF(db.DB, options)
|
||||
var confirmed uint64 = 0
|
||||
var unconfirmed uint64 = 0 // TODO
|
||||
for kv := range ch {
|
||||
confirmed += kv.Value.(*prefixes.UTXOValue).Amount
|
||||
}
|
||||
|
||||
return confirmed, unconfirmed, nil
|
||||
}
|
||||
|
||||
type TXOInfo struct {
|
||||
TxHash *chainhash.Hash
|
||||
TxPos uint16
|
||||
Height uint32
|
||||
Value uint64
|
||||
}
|
||||
|
||||
func (db *ReadOnlyDBColumnFamily) GetUnspent(hashX []byte) ([]TXOInfo, error) {
|
||||
startKey := &prefixes.UTXOKey{
|
||||
Prefix: []byte{prefixes.UTXO},
|
||||
HashX: hashX,
|
||||
TxNum: 0,
|
||||
Nout: 0,
|
||||
}
|
||||
endKey := &prefixes.UTXOKey{
|
||||
Prefix: []byte{prefixes.UTXO},
|
||||
HashX: hashX,
|
||||
TxNum: math.MaxUint32,
|
||||
Nout: math.MaxUint16,
|
||||
}
|
||||
selectedUTXO, err := db.selectFrom([]byte{prefixes.UTXO}, startKey, endKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectTxHashByTxNum := func(in []*prefixes.PrefixRowKV) ([]*IterOptions, error) {
|
||||
historyKey := in[0].Key.(*prefixes.UTXOKey)
|
||||
out := make([]*IterOptions, 0, 100)
|
||||
startKey := &prefixes.TxHashKey{
|
||||
Prefix: []byte{prefixes.TxHash},
|
||||
TxNum: historyKey.TxNum,
|
||||
}
|
||||
endKey := &prefixes.TxHashKey{
|
||||
Prefix: []byte{prefixes.TxHash},
|
||||
TxNum: historyKey.TxNum,
|
||||
}
|
||||
selectedTxHash, err := db.selectFrom([]byte{prefixes.TxHash}, startKey, endKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, selectedTxHash...)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
results := make([]TXOInfo, 0, 1000)
|
||||
for kvs := range innerJoin(db.DB, iterate(db.DB, selectedUTXO), selectTxHashByTxNum) {
|
||||
if err := checkForError(kvs); err != nil {
|
||||
return results, err
|
||||
}
|
||||
utxoKey := kvs[0].Key.(*prefixes.UTXOKey)
|
||||
utxoValue := kvs[0].Value.(*prefixes.UTXOValue)
|
||||
txhashValue := kvs[1].Value.(*prefixes.TxHashValue)
|
||||
results = append(results,
|
||||
TXOInfo{
|
||||
TxHash: txhashValue.TxHash,
|
||||
TxPos: utxoKey.Nout,
|
||||
Height: 0, // TODO
|
||||
Value: utxoValue.Amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
type TxInfo struct {
|
||||
TxHash *chainhash.Hash
|
||||
Height uint32
|
||||
}
|
||||
|
||||
func (db *ReadOnlyDBColumnFamily) GetHistory(hashX []byte) ([]TxInfo, error) {
|
||||
startKey := &prefixes.HashXHistoryKey{
|
||||
Prefix: []byte{prefixes.HashXHistory},
|
||||
HashX: hashX,
|
||||
Height: 0,
|
||||
}
|
||||
endKey := &prefixes.HashXHistoryKey{
|
||||
Prefix: []byte{prefixes.UTXO},
|
||||
HashX: hashX,
|
||||
Height: math.MaxUint32,
|
||||
}
|
||||
selectedHistory, err := db.selectFrom([]byte{prefixes.HashXHistory}, startKey, endKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
selectTxHashByTxNums := func(in []*prefixes.PrefixRowKV) ([]*IterOptions, error) {
|
||||
historyValue := in[0].Value.(*prefixes.HashXHistoryValue)
|
||||
out := make([]*IterOptions, 0, 100)
|
||||
for _, txnum := range historyValue.TxNums {
|
||||
startKey := &prefixes.TxHashKey{
|
||||
Prefix: []byte{prefixes.TxHash},
|
||||
TxNum: txnum,
|
||||
}
|
||||
endKey := &prefixes.TxHashKey{
|
||||
Prefix: []byte{prefixes.TxHash},
|
||||
TxNum: txnum,
|
||||
}
|
||||
selectedTxHash, err := db.selectFrom([]byte{prefixes.TxHash}, startKey, endKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, selectedTxHash...)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
results := make([]TxInfo, 0, 1000)
|
||||
for kvs := range innerJoin(db.DB, iterate(db.DB, selectedHistory), selectTxHashByTxNums) {
|
||||
if err := checkForError(kvs); err != nil {
|
||||
return results, err
|
||||
}
|
||||
historyKey := kvs[0].Key.(*prefixes.HashXHistoryKey)
|
||||
txHashValue := kvs[1].Value.(*prefixes.TxHashValue)
|
||||
results = append(results, TxInfo{
|
||||
TxHash: txHashValue.TxHash,
|
||||
Height: historyKey.Height,
|
||||
})
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetStreamsAndChannelRepostedByChannelHashes returns a map of streams and channel hashes that are reposted by the given channel hashes.
|
||||
func (db *ReadOnlyDBColumnFamily) GetStreamsAndChannelRepostedByChannelHashes(reposterChannelHashes [][]byte) (map[string][]byte, map[string][]byte, error) {
|
||||
handle, err := db.EnsureHandle(prefixes.ChannelToClaim)
|
||||
|
|
|
@ -125,6 +125,7 @@ type PrefixRowKV struct {
|
|||
Value BaseValue
|
||||
RawKey []byte
|
||||
RawValue []byte
|
||||
Error error
|
||||
}
|
||||
|
||||
type BaseKey interface {
|
||||
|
|
8
go.mod
8
go.mod
|
@ -10,6 +10,7 @@ require (
|
|||
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
|
||||
|
@ -23,15 +24,17 @@ require (
|
|||
gopkg.in/karalabe/cookiejar.v1 v1.0.0-20141109175019-e1490cae028c
|
||||
)
|
||||
|
||||
require golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b // indirect
|
||||
require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // 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-rc1
|
||||
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
|
||||
|
@ -39,6 +42,7 @@ require (
|
|||
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 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
||||
|
|
7
go.sum
7
go.sum
|
@ -63,6 +63,7 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
|
|||
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/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=
|
||||
|
@ -359,8 +360,12 @@ github.com/lbryio/lbcd v0.22.100-beta-rc5/go.mod h1:9PbFSlHYX7WlnDQwcTxHVf1W35VA
|
|||
github.com/lbryio/lbcd v0.22.200-beta/go.mod h1:kNuzGWf808ipTGB0y0WogzsGv5BVM4Qv85Z+JYwC9FA=
|
||||
github.com/lbryio/lbcd v0.22.201-beta-rc1 h1:FmzzApVj2RBXloLM2w9tLvN2xyTZjeyh+QC7GIw/wwo=
|
||||
github.com/lbryio/lbcd v0.22.201-beta-rc1/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=
|
||||
|
@ -370,6 +375,8 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b
|
|||
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/linxGnu/grocksdb v1.7.0 h1:UyFDykX0CUfxDN10cqlFho/rwt9K6KoDaLXL9Ej5z9g=
|
||||
github.com/linxGnu/grocksdb v1.7.0/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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
|
|
291
server/blockchain.go
Normal file
291
server/blockchain.go
Normal file
|
@ -0,0 +1,291 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcutil"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
type RpcReq interface {
|
||||
}
|
||||
type RpcResp interface {
|
||||
}
|
||||
type RpcHandler interface {
|
||||
Handle() (RpcResp, error)
|
||||
}
|
||||
|
||||
const CHUNK_SIZE = 96
|
||||
const MAX_CHUNK_SIZE = 40960
|
||||
const HEADER_SIZE = 112
|
||||
const HASHX_SIZE = 11
|
||||
|
||||
func min[Ord constraints.Ordered](x, y Ord) Ord {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
type blockGetChunkReq uint32
|
||||
type blockGetChunkResp string
|
||||
|
||||
// 'blockchain.block.get_chunk'
|
||||
func (req *blockGetChunkReq) Handle(s *Server) (*blockGetChunkResp, error) {
|
||||
index := uint32(*req)
|
||||
db_headers, err := s.DB.GetHeaders(index*CHUNK_SIZE, CHUNK_SIZE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw := make([]byte, 0, HEADER_SIZE*len(db_headers))
|
||||
for _, h := range db_headers {
|
||||
raw = append(raw, h[:]...)
|
||||
}
|
||||
headers := blockGetChunkResp(hex.EncodeToString(raw))
|
||||
return &headers, err
|
||||
}
|
||||
|
||||
type blockGetHeaderReq uint32
|
||||
type blockGetHeaderResp 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"`
|
||||
}
|
||||
|
||||
// 'blockchain.block.get_header'
|
||||
func (req *blockGetHeaderReq) Handle(s *Server) (*blockGetHeaderResp, error) {
|
||||
height := uint32(*req)
|
||||
headers, err := s.DB.GetHeaders(height, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(headers) < 1 {
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
decode := func(header *[HEADER_SIZE]byte, height uint32) *blockGetHeaderResp {
|
||||
return &blockGetHeaderResp{
|
||||
Version: binary.LittleEndian.Uint32(header[0:]),
|
||||
PrevBlockHash: hex.EncodeToString(header[4:46]),
|
||||
MerkleRoot: hex.EncodeToString(header[36:68]),
|
||||
ClaimTrieRoot: hex.EncodeToString(header[68:100]),
|
||||
Timestamp: binary.LittleEndian.Uint32(header[100:]),
|
||||
Bits: binary.LittleEndian.Uint32(header[104:]),
|
||||
Nonce: binary.LittleEndian.Uint32(header[108:]),
|
||||
BlockHeight: height,
|
||||
}
|
||||
}
|
||||
return decode(&headers[0], height), nil
|
||||
}
|
||||
|
||||
type blockHeadersReq struct {
|
||||
StartHeight uint32 `json:"start_height"`
|
||||
Count uint32 `json:"count"`
|
||||
CpHeight uint32 `json:"cp_height"`
|
||||
B64 bool `json:"b64"`
|
||||
}
|
||||
|
||||
type blockHeadersResp struct {
|
||||
Base64 string `json:"base64,omitempty"`
|
||||
Hex string `json:"hex,omitempty"`
|
||||
Count uint32 `json:"count"`
|
||||
Max uint32 `json:"max"`
|
||||
Branch string `json:"branch,omitempty"`
|
||||
Root string `json:"root,omitempty"`
|
||||
}
|
||||
|
||||
// 'blockchain.block.headers'
|
||||
func (req *blockHeadersReq) Handle(s *Server) (*blockHeadersResp, error) {
|
||||
count := min(req.Count, MAX_CHUNK_SIZE)
|
||||
db_headers, err := s.DB.GetHeaders(req.StartHeight, count)
|
||||
if err != nil {
|
||||
return nil, 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
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
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_SIZE]
|
||||
}
|
||||
|
||||
type addressGetBalanceReq struct {
|
||||
Address string
|
||||
}
|
||||
type addressGetBalanceResp struct {
|
||||
Confirmed uint64
|
||||
Unconfirmed uint64
|
||||
}
|
||||
|
||||
// 'blockchain.address.get_balance'
|
||||
func (req *addressGetBalanceReq) Handle(s *Server) (*addressGetBalanceResp, error) {
|
||||
address, err := lbcutil.DecodeAddress(req.Address, s.Coin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
script, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashX := hashXScript(script, s.Coin)
|
||||
confirmed, unconfirmed, err := s.DB.GetBalance(hashX)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &addressGetBalanceResp{confirmed, unconfirmed}, err
|
||||
}
|
||||
|
||||
type addressGetHistoryReq struct {
|
||||
Address string
|
||||
}
|
||||
type TxInfo struct {
|
||||
TxHash string
|
||||
Height uint32
|
||||
}
|
||||
type TxInfoFee struct {
|
||||
TxInfo
|
||||
Fee uint64
|
||||
}
|
||||
type addressGetHistoryResp struct {
|
||||
Confirmed []TxInfo
|
||||
Unconfirmed []TxInfoFee
|
||||
}
|
||||
|
||||
// 'blockchain.address.get_history'
|
||||
func (req *addressGetHistoryReq) Handle(s *Server) (*addressGetHistoryResp, error) {
|
||||
address, err := lbcutil.DecodeAddress(req.Address, s.Coin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
script, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashX := hashXScript(script, s.Coin)
|
||||
dbTXs, err := s.DB.GetHistory(hashX)
|
||||
confirmed := make([]TxInfo, 0, len(dbTXs))
|
||||
for _, tx := range dbTXs {
|
||||
confirmed = append(confirmed,
|
||||
TxInfo{
|
||||
TxHash: hex.EncodeToString(tx.TxHash[:]),
|
||||
Height: tx.Height,
|
||||
})
|
||||
}
|
||||
result := &addressGetHistoryResp{
|
||||
Confirmed: confirmed,
|
||||
Unconfirmed: []TxInfoFee{}, // TODO
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type addressGetMempoolReq struct {
|
||||
Address string
|
||||
}
|
||||
type addressGetMempoolResp []TxInfoFee
|
||||
|
||||
// 'blockchain.address.get_mempool'
|
||||
func (req *addressGetMempoolReq) Handle(s *Server) (*addressGetMempoolResp, error) {
|
||||
address, err := lbcutil.DecodeAddress(req.Address, s.Coin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
script, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO...
|
||||
hashX := hashXScript(script, s.Coin)
|
||||
dbTXs, err := s.DB.GetHistory(hashX)
|
||||
confirmed := make([]TxInfo, 0, len(dbTXs))
|
||||
for _, tx := range dbTXs {
|
||||
confirmed = append(confirmed,
|
||||
TxInfo{
|
||||
TxHash: hex.EncodeToString(tx.TxHash[:]),
|
||||
Height: tx.Height,
|
||||
})
|
||||
}
|
||||
unconfirmed := make([]TxInfoFee, 0, 100)
|
||||
result := addressGetMempoolResp(unconfirmed)
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
type addressListUnspentReq struct {
|
||||
Address string
|
||||
}
|
||||
type TXOInfo struct {
|
||||
TxHash string
|
||||
TxPos uint16
|
||||
Height uint32
|
||||
Value uint64
|
||||
}
|
||||
type addressListUnspentResp []TXOInfo
|
||||
|
||||
// 'blockchain.address.listunspent'
|
||||
func (req *addressListUnspentReq) Handle(s *Server) (*addressListUnspentResp, error) {
|
||||
address, err := lbcutil.DecodeAddress(req.Address, s.Coin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
script, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hashX := hashXScript(script, s.Coin)
|
||||
dbTXOs, err := s.DB.GetUnspent(hashX)
|
||||
unspent := make([]TXOInfo, 0, len(dbTXOs))
|
||||
for _, txo := range dbTXOs {
|
||||
unspent = append(unspent,
|
||||
TXOInfo{
|
||||
TxHash: hex.EncodeToString(txo.TxHash[:]),
|
||||
TxPos: txo.TxPos,
|
||||
Height: txo.Height,
|
||||
Value: txo.Value,
|
||||
})
|
||||
}
|
||||
result := addressListUnspentResp(unspent)
|
||||
return &result, nil
|
||||
}
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/lbryio/herald.go/internal/metrics"
|
||||
"github.com/lbryio/herald.go/meta"
|
||||
pb "github.com/lbryio/herald.go/protobuf/go"
|
||||
"github.com/lbryio/lbcd/chaincfg"
|
||||
"github.com/olivere/elastic/v7"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
@ -38,6 +39,7 @@ type Server struct {
|
|||
DB *db.ReadOnlyDBColumnFamily
|
||||
EsClient *elastic.Client
|
||||
QueryCache *ttlcache.Cache
|
||||
Coin *chaincfg.Params
|
||||
S256 *hash.Hash
|
||||
LastRefreshCheck time.Time
|
||||
RefreshDelta time.Duration
|
||||
|
@ -260,6 +262,7 @@ func MakeHubServer(ctx context.Context, args *Args) *Server {
|
|||
EsClient: client,
|
||||
QueryCache: cache,
|
||||
S256: &s256,
|
||||
Coin: &chaincfg.MainNetParams,
|
||||
LastRefreshCheck: time.Now(),
|
||||
RefreshDelta: refreshDelta,
|
||||
NumESRefreshes: 0,
|
||||
|
|
Loading…
Reference in a new issue