Compare commits
4 commits
Author | SHA1 | Date | |
---|---|---|---|
a0ff51b84a | |||
4c39a9842c | |||
f513fca6a7 | |||
6728bf4b08 |
|
@ -977,8 +977,8 @@ func NewImportMultiCmd(requests []ImportMultiRequest, options *ImportMultiOption
|
||||||
|
|
||||||
// RescanBlockchainCmd defines the RescanBlockchain JSON-RPC command.
|
// RescanBlockchainCmd defines the RescanBlockchain JSON-RPC command.
|
||||||
type RescanBlockchainCmd struct {
|
type RescanBlockchainCmd struct {
|
||||||
StartHeight *int64 `jsonrpcdefault:"0"`
|
StartHeight *int32 `jsonrpcdefault:"0"`
|
||||||
StopHeight *int64 `jsonrpcdefault:"0"`
|
StopHeight *int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRescanBlockchainCmd returns a new instance which can be used to issue
|
// NewRescanBlockchainCmd returns a new instance which can be used to issue
|
||||||
|
@ -986,7 +986,7 @@ type RescanBlockchainCmd struct {
|
||||||
//
|
//
|
||||||
// The parameters which are pointers indicate they are optional. Passing nil
|
// The parameters which are pointers indicate they are optional. Passing nil
|
||||||
// for optional parameters will use the default value.
|
// for optional parameters will use the default value.
|
||||||
func NewRescanBlockchainCmd(startHeight *int64, stopHeight *int64) *RescanBlockchainCmd {
|
func NewRescanBlockchainCmd(startHeight *int32, stopHeight *int32) *RescanBlockchainCmd {
|
||||||
return &RescanBlockchainCmd{
|
return &RescanBlockchainCmd{
|
||||||
StartHeight: startHeight,
|
StartHeight: startHeight,
|
||||||
StopHeight: stopHeight,
|
StopHeight: stopHeight,
|
||||||
|
|
|
@ -319,8 +319,8 @@ type ListUnspentResult struct {
|
||||||
|
|
||||||
// RescanBlockchainResult models the data returned from the rescanblockchain command.
|
// RescanBlockchainResult models the data returned from the rescanblockchain command.
|
||||||
type RescanBlockchainResult struct {
|
type RescanBlockchainResult struct {
|
||||||
StartHeight int64 `json:"start_height"`
|
StartHeight int32 `json:"start_height"`
|
||||||
StoptHeight int64 `json:"stop_height"`
|
StoptHeight int32 `json:"stop_height"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignRawTransactionError models the data that contains script verification
|
// SignRawTransactionError models the data that contains script verification
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
# lbcd Websockets Example
|
# lbcdbloknotify
|
||||||
|
|
||||||
This example shows how to use the rpcclient package to connect to a btcd RPC
|
This bridge program subscribes to lbcd's notifications over websockets using the rpcclient package.
|
||||||
server using TLS-secured websockets, register for block connected and block
|
Users can specify supported actions upon receiving this notifications.
|
||||||
disconnected notifications, and get the current block count.
|
|
||||||
|
|
||||||
## Running the Example
|
## Building(or Running) the Program
|
||||||
|
|
||||||
The first step is to clone the lbcd package:
|
Clone the lbcd package:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone github.com/lbryio/lbcd
|
$ git clone github.com/lbryio/lbcd
|
||||||
|
$ cd lbcd/rpcclient/examples
|
||||||
|
|
||||||
|
# build the program
|
||||||
|
$ go build .
|
||||||
|
|
||||||
|
# or directly run it (build implicitly behind the scene)
|
||||||
|
$ go run .
|
||||||
```
|
```
|
||||||
|
|
||||||
Display available options:
|
Display available options:
|
||||||
|
@ -30,19 +36,30 @@ $ go run . -h
|
||||||
-stratumpass string
|
-stratumpass string
|
||||||
Stratum server password (default "password")
|
Stratum server password (default "password")
|
||||||
-quiet
|
-quiet
|
||||||
Do not print logs
|
Do not print periodic logs
|
||||||
```
|
```
|
||||||
|
|
||||||
Start the program:
|
Running the program:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ go run . -stratumpass <STRATUM PASSWD> -rpcuser <RPC USERNAME> -rpcpass <RPC PASSWD>
|
# Send stratum mining.update_block mesage upon receving block connected notifiations.
|
||||||
|
$ go run . -rpcuser <RPC USERNAME> -rpcpass <RPC PASSWD> --notls -stratum <STRATUM SERVER> -stratumpass <STRATUM PASSWD>
|
||||||
|
|
||||||
2022/01/10 23:16:21 NotifyBlocks: Registration Complete
|
2022/01/10 23:16:21 Current block count: 1093112
|
||||||
2022/01/10 23:16:21 Block count: 1093112
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
# Execute a custome command (with blockhash) upon receving block connected notifiations.
|
||||||
|
$ go run . -rpcuser <RPC USERNAME> -rpcpass <RPC PASSWD> --notls -run "echo %s"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
* Stratum TCP connection is persisted with auto-reconnect. (retry backoff increases from 1s to 60s maximum)
|
||||||
|
|
||||||
|
* Stratum update_block jobs on previous notifications are canceled when a new notification arrives.
|
||||||
|
Usually, the jobs are so short and completed immediately. However, if the Stratum connection is broken, this
|
||||||
|
prevents the bridge from accumulating stale jobs.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This example is licensed under the [copyfree](http://copyfree.org) ISC License.
|
This example is licensed under the [copyfree](http://copyfree.org) ISC License.
|
||||||
|
|
20
rpcclient/examples/lbcdblocknotify/adapter.go
Normal file
20
rpcclient/examples/lbcdblocknotify/adapter.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/lbryio/lbcd/wire"
|
||||||
|
"github.com/lbryio/lbcutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type eventBlockConected struct {
|
||||||
|
height int32
|
||||||
|
header *wire.BlockHeader
|
||||||
|
txns []*lbcutil.Tx
|
||||||
|
}
|
||||||
|
|
||||||
|
type adapter struct {
|
||||||
|
*bridge
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *adapter) onFilteredBlockConnected(height int32, header *wire.BlockHeader, txns []*lbcutil.Tx) {
|
||||||
|
a.eventCh <- &eventBlockConected{height, header, txns}
|
||||||
|
}
|
172
rpcclient/examples/lbcdblocknotify/bridge.go
Normal file
172
rpcclient/examples/lbcdblocknotify/bridge.go
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type bridge struct {
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
prevJobContext context.Context
|
||||||
|
prevJobCancel context.CancelFunc
|
||||||
|
|
||||||
|
eventCh chan interface{}
|
||||||
|
errorc chan error
|
||||||
|
wg sync.WaitGroup
|
||||||
|
|
||||||
|
stratum *stratumClient
|
||||||
|
|
||||||
|
customCmd string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBridge(stratumServer, stratumPass, coinid string) *bridge {
|
||||||
|
|
||||||
|
s := &bridge{
|
||||||
|
ctx: context.Background(),
|
||||||
|
eventCh: make(chan interface{}),
|
||||||
|
errorc: make(chan error),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(stratumServer) > 0 {
|
||||||
|
s.stratum = newStratumClient(stratumServer, stratumPass, coinid)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) start() {
|
||||||
|
|
||||||
|
if b.stratum != nil {
|
||||||
|
backoff := time.Second
|
||||||
|
for {
|
||||||
|
err := b.stratum.dial()
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Printf("WARN: stratum.dial() error: %s, retry in %s", err, backoff)
|
||||||
|
time.Sleep(backoff)
|
||||||
|
if backoff < 60*time.Second {
|
||||||
|
backoff += time.Second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for e := range b.eventCh {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *eventBlockConected:
|
||||||
|
b.handleFilteredBlockConnected(e)
|
||||||
|
default:
|
||||||
|
b.errorc <- fmt.Errorf("unknown event type: %T", e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) handleFilteredBlockConnected(e *eventBlockConected) {
|
||||||
|
|
||||||
|
if !*quiet {
|
||||||
|
log.Printf("Block connected: %s (%d) %v", e.header.BlockHash(), e.height, e.header.Timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := e.header.BlockHash().String()
|
||||||
|
height := e.height
|
||||||
|
|
||||||
|
// Cancel jobs on previous block. It's safe if they are already done.
|
||||||
|
if b.prevJobContext != nil {
|
||||||
|
select {
|
||||||
|
case <-b.prevJobContext.Done():
|
||||||
|
log.Printf("prev one canceled")
|
||||||
|
default:
|
||||||
|
b.prevJobCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until all previous jobs are done or canceled.
|
||||||
|
b.wg.Wait()
|
||||||
|
|
||||||
|
// Create and save cancelable subcontext for new jobs.
|
||||||
|
ctx, cancel := context.WithCancel(b.ctx)
|
||||||
|
b.prevJobContext, b.prevJobCancel = ctx, cancel
|
||||||
|
|
||||||
|
if len(b.customCmd) > 0 {
|
||||||
|
go b.execCustomCommand(ctx, hash, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send stratum update block message
|
||||||
|
if b.stratum != nil {
|
||||||
|
go b.stratumUpdateBlock(ctx, hash, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *bridge) stratumUpdateBlock(ctx context.Context, hash string, height int32) {
|
||||||
|
s.wg.Add(1)
|
||||||
|
defer s.wg.Done()
|
||||||
|
|
||||||
|
backoff := time.Second
|
||||||
|
retry := func(err error) {
|
||||||
|
if backoff < 60*time.Second {
|
||||||
|
backoff += time.Second
|
||||||
|
}
|
||||||
|
log.Printf("WARN: stratum.send() on block %d error: %s", height, err)
|
||||||
|
time.Sleep(backoff)
|
||||||
|
s.stratum.dial()
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := stratumUpdateBlockMsg(*stratumPass, *coinid, hash)
|
||||||
|
|
||||||
|
for {
|
||||||
|
switch err := s.stratum.send(ctx, msg); {
|
||||||
|
case err == nil:
|
||||||
|
return
|
||||||
|
case errors.Is(err, context.Canceled):
|
||||||
|
log.Printf("INFO: stratum.send() on block %d: %s.", height, err)
|
||||||
|
return
|
||||||
|
case errors.Is(err, syscall.EPIPE):
|
||||||
|
errClose := s.stratum.conn.Close()
|
||||||
|
if errClose != nil {
|
||||||
|
log.Printf("WARN: stratum.conn.Close() on block %d: %s.", height, errClose)
|
||||||
|
}
|
||||||
|
retry(err)
|
||||||
|
case errors.Is(err, net.ErrClosed):
|
||||||
|
retry(err)
|
||||||
|
default:
|
||||||
|
retry(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *bridge) execCustomCommand(ctx context.Context, hash string, height int32) {
|
||||||
|
s.wg.Add(1)
|
||||||
|
defer s.wg.Done()
|
||||||
|
|
||||||
|
cmd := strings.ReplaceAll(s.customCmd, "%s", hash)
|
||||||
|
err := doExecCustomCommand(ctx, cmd)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ERROR: execCustomCommand on block %s(%d): %s", hash, height, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doExecCustomCommand(ctx context.Context, cmd string) error {
|
||||||
|
strs := strings.Split(cmd, " ")
|
||||||
|
path, err := exec.LookPath(strs[0])
|
||||||
|
if errors.Is(err, exec.ErrDot) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c := exec.CommandContext(ctx, path, strs[1:]...)
|
||||||
|
c.Stdout = os.Stdout
|
||||||
|
return c.Run()
|
||||||
|
}
|
53
rpcclient/examples/lbcdblocknotify/lbcdclient.go
Normal file
53
rpcclient/examples/lbcdblocknotify/lbcdclient.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/lbryio/lbcd/rpcclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newLbcdClient(server, user, pass string, notls bool, adpt adapter) *rpcclient.Client {
|
||||||
|
|
||||||
|
ntfnHandlers := rpcclient.NotificationHandlers{
|
||||||
|
OnFilteredBlockConnected: adpt.onFilteredBlockConnected,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config lbcd RPC client with websockets.
|
||||||
|
connCfg := &rpcclient.ConnConfig{
|
||||||
|
Host: server,
|
||||||
|
Endpoint: "ws",
|
||||||
|
User: user,
|
||||||
|
Pass: pass,
|
||||||
|
DisableTLS: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !notls {
|
||||||
|
cert, err := ioutil.ReadFile(filepath.Join(lbcdHomeDir, "rpc.cert"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("can't read lbcd certificate: %s", err)
|
||||||
|
}
|
||||||
|
connCfg.Certificates = cert
|
||||||
|
connCfg.DisableTLS = false
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := rpcclient.New(connCfg, &ntfnHandlers)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("can't create rpc client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register for block connect and disconnect notifications.
|
||||||
|
if err = client.NotifyBlocks(); err != nil {
|
||||||
|
log.Fatalf("can't register block notification: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current block count.
|
||||||
|
blockCount, err := client.GetBlockCount()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("can't get block count: %s", err)
|
||||||
|
}
|
||||||
|
log.Printf("Current block count: %d", blockCount)
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
|
@ -1,136 +1,63 @@
|
||||||
// Copyright (c) 2014-2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/lbryio/lbcd/rpcclient"
|
|
||||||
"github.com/lbryio/lbcd/wire"
|
|
||||||
"github.com/lbryio/lbcutil"
|
"github.com/lbryio/lbcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
coinid = flag.String("coinid", "1425", "Coin ID")
|
lbcdHomeDir = lbcutil.AppDataDir("lbcd", false)
|
||||||
stratum = flag.String("stratum", "", "Stratum server")
|
defaultCert = filepath.Join(lbcdHomeDir, "rpc.cert")
|
||||||
stratumPass = flag.String("stratumpass", "", "Stratum server password")
|
)
|
||||||
rpcserver = flag.String("rpcserver", "localhost:9245", "LBCD RPC server")
|
var (
|
||||||
rpcuser = flag.String("rpcuser", "rpcuser", "LBCD RPC username")
|
coinid = flag.String("coinid", "1425", "Coin ID")
|
||||||
rpcpass = flag.String("rpcpass", "rpcpass", "LBCD RPC password")
|
stratumServer = flag.String("stratum", "", "Stratum server")
|
||||||
notls = flag.Bool("notls", false, "Connect to LBCD with TLS disabled")
|
stratumPass = flag.String("stratumpass", "", "Stratum server password")
|
||||||
run = flag.String("run", "", "Run custom shell command")
|
rpcserver = flag.String("rpcserver", "localhost:9245", "LBCD RPC server")
|
||||||
quiet = flag.Bool("quiet", false, "Do not print logs")
|
rpcuser = flag.String("rpcuser", "rpcuser", "LBCD RPC username")
|
||||||
|
rpcpass = flag.String("rpcpass", "rpcpass", "LBCD RPC password")
|
||||||
|
rpccert = flag.String("rpccert", defaultCert, "LBCD RPC certificate")
|
||||||
|
notls = flag.Bool("notls", false, "Connect to LBCD with TLS disabled")
|
||||||
|
run = flag.String("run", "", "Run custom shell command")
|
||||||
|
quiet = flag.Bool("quiet", false, "Do not print logs")
|
||||||
)
|
)
|
||||||
|
|
||||||
func onFilteredBlockConnected(height int32, header *wire.BlockHeader, txns []*lbcutil.Tx) {
|
|
||||||
|
|
||||||
blockHash := header.BlockHash().String()
|
|
||||||
|
|
||||||
if !*quiet {
|
|
||||||
log.Printf("Block connected: %v (%d) %v", blockHash, height, header.Timestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmd := *run; len(cmd) != 0 {
|
|
||||||
cmd = strings.ReplaceAll(cmd, "%s", blockHash)
|
|
||||||
err := execCustomCommand(cmd)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR: execCustomCommand: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(*stratum) > 0 && len(*stratumPass) > 0 {
|
|
||||||
err := stratumUpdateBlock(*stratum, *stratumPass, *coinid, blockHash)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR: stratumUpdateBlock: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func execCustomCommand(cmd string) error {
|
|
||||||
strs := strings.Split(cmd, " ")
|
|
||||||
path, err := exec.LookPath(strs[0])
|
|
||||||
if errors.Is(err, exec.ErrDot) {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c := exec.Command(path, strs[1:]...)
|
|
||||||
c.Stdout = os.Stdout
|
|
||||||
return c.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func stratumUpdateBlock(stratum, stratumPass, coinid, blockHash string) error {
|
|
||||||
addr, err := net.ResolveTCPAddr("tcp", stratum)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't resolve addr: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := net.DialTCP("tcp", nil, addr)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't dial tcp: %w", err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
msg := fmt.Sprintf(`{"id":1,"method":"mining.update_block","params":[%q,%s,%q]}`,
|
|
||||||
stratumPass, coinid, blockHash)
|
|
||||||
|
|
||||||
_, err = conn.Write([]byte(msg))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can't write message: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
ntfnHandlers := rpcclient.NotificationHandlers{
|
// Setup notification handler
|
||||||
OnFilteredBlockConnected: onFilteredBlockConnected,
|
b := newBridge(*stratumServer, *stratumPass, *coinid)
|
||||||
|
|
||||||
|
if len(*run) > 0 {
|
||||||
|
// Check if ccommand exists.
|
||||||
|
strs := strings.Split(*run, " ")
|
||||||
|
cmd := strs[0]
|
||||||
|
_, err := exec.LookPath(cmd)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERROR: %s not found: %s", cmd, err)
|
||||||
|
}
|
||||||
|
b.customCmd = *run
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to local lbcd RPC server using websockets.
|
// Start the eventt handler.
|
||||||
lbcdHomeDir := lbcutil.AppDataDir("lbcd", false)
|
go b.start()
|
||||||
certs, err := ioutil.ReadFile(filepath.Join(lbcdHomeDir, "rpc.cert"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("can't read lbcd certificate: %s", err)
|
|
||||||
}
|
|
||||||
connCfg := &rpcclient.ConnConfig{
|
|
||||||
Host: *rpcserver,
|
|
||||||
Endpoint: "ws",
|
|
||||||
User: *rpcuser,
|
|
||||||
Pass: *rpcpass,
|
|
||||||
Certificates: certs,
|
|
||||||
DisableTLS: *notls,
|
|
||||||
}
|
|
||||||
client, err := rpcclient.New(connCfg, &ntfnHandlers)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("can't create rpc client: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register for block connect and disconnect notifications.
|
// Adaptater receives lbcd notifications, and emit events.
|
||||||
if err = client.NotifyBlocks(); err != nil {
|
adpt := adapter{b}
|
||||||
log.Fatalf("can't register block notification: %s", err)
|
|
||||||
}
|
|
||||||
log.Printf("NotifyBlocks: Registration Complete")
|
|
||||||
|
|
||||||
// Get the current block count.
|
client := newLbcdClient(*rpcserver, *rpcuser, *rpcpass, *notls, adpt)
|
||||||
blockCount, err := client.GetBlockCount()
|
|
||||||
if err != nil {
|
go func() {
|
||||||
log.Fatalf("can't get block count: %s", err)
|
err := <-b.errorc
|
||||||
}
|
log.Fatalf("ERROR: %s", err)
|
||||||
log.Printf("Block count: %d", blockCount)
|
client.Shutdown()
|
||||||
|
}()
|
||||||
|
|
||||||
// Wait until the client either shuts down gracefully (or the user
|
// Wait until the client either shuts down gracefully (or the user
|
||||||
// terminates the process with Ctrl+C).
|
// terminates the process with Ctrl+C).
|
||||||
|
|
56
rpcclient/examples/lbcdblocknotify/stratumclient.go
Normal file
56
rpcclient/examples/lbcdblocknotify/stratumclient.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stratumClient struct {
|
||||||
|
server string
|
||||||
|
passwd string
|
||||||
|
coinid string
|
||||||
|
conn *net.TCPConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStratumClient(server, passwd, coinid string) *stratumClient {
|
||||||
|
|
||||||
|
return &stratumClient{
|
||||||
|
server: server,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *stratumClient) dial() error {
|
||||||
|
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", c.server)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("resolve tcp addr: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.DialTCP("tcp", nil, addr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("dial tcp: %w", err)
|
||||||
|
}
|
||||||
|
c.conn = conn
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *stratumClient) send(ctx context.Context, msg string) error {
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := c.conn.Write([]byte(msg))
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func stratumUpdateBlockMsg(stratumPass, coinid, blockHash string) string {
|
||||||
|
|
||||||
|
return fmt.Sprintf(`{"id":1,"method":"mining.update_block","params":[%q,%s,%q]}`,
|
||||||
|
stratumPass, coinid, blockHash)
|
||||||
|
}
|
|
@ -774,7 +774,8 @@ func (c *Client) handleSendPostMessage(jReq *jsonRequest) {
|
||||||
tries := 10
|
tries := 10
|
||||||
for i := 0; tries == 0 || i < tries; i++ {
|
for i := 0; tries == 0 || i < tries; i++ {
|
||||||
bodyReader := bytes.NewReader(jReq.marshalledJSON)
|
bodyReader := bytes.NewReader(jReq.marshalledJSON)
|
||||||
httpReq, err := http.NewRequest("POST", url, bodyReader)
|
var httpReq *http.Request
|
||||||
|
httpReq, err = http.NewRequest("POST", url, bodyReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jReq.responseChan <- &Response{result: nil, err: err}
|
jReq.responseChan <- &Response{result: nil, err: err}
|
||||||
return
|
return
|
||||||
|
@ -786,7 +787,8 @@ func (c *Client) handleSendPostMessage(jReq *jsonRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure basic access authorization.
|
// Configure basic access authorization.
|
||||||
user, pass, err := c.config.getAuth()
|
var user, pass string
|
||||||
|
user, pass, err = c.config.getAuth()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jReq.responseChan <- &Response{result: nil, err: err}
|
jReq.responseChan <- &Response{result: nil, err: err}
|
||||||
return
|
return
|
||||||
|
|
|
@ -2062,14 +2062,14 @@ func (r FutureRescanBlockchainResult) Receive() (*btcjson.RescanBlockchainResult
|
||||||
// returned instance.
|
// returned instance.
|
||||||
//
|
//
|
||||||
// See RescanBlockchain for the blocking version and more details.
|
// See RescanBlockchain for the blocking version and more details.
|
||||||
func (c *Client) RescanBlockchainAsync(startHeight *int64, stopHeight *int64) FutureRescanBlockchainResult {
|
func (c *Client) RescanBlockchainAsync(startHeight *int32, stopHeight *int32) FutureRescanBlockchainResult {
|
||||||
cmd := btcjson.NewRescanBlockchainCmd(startHeight, stopHeight)
|
cmd := btcjson.NewRescanBlockchainCmd(startHeight, stopHeight)
|
||||||
return c.SendCmd(cmd)
|
return c.SendCmd(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RescanBlockchain rescans the local blockchain for wallet related
|
// RescanBlockchain rescans the local blockchain for wallet related
|
||||||
// transactions from the startHeight to the the inclusive stopHeight.
|
// transactions from the startHeight to the the inclusive stopHeight.
|
||||||
func (c *Client) RescanBlockchain(startHeight *int64, stopHeight *int64) (*btcjson.RescanBlockchainResult, error) {
|
func (c *Client) RescanBlockchain(startHeight *int32, stopHeight *int32) (*btcjson.RescanBlockchainResult, error) {
|
||||||
return c.RescanBlockchainAsync(startHeight, stopHeight).Receive()
|
return c.RescanBlockchainAsync(startHeight, stopHeight).Receive()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -206,7 +206,7 @@ func StripClaimScriptPrefix(script []byte) []byte {
|
||||||
return script[cs.Size:]
|
return script[cs.Size:]
|
||||||
}
|
}
|
||||||
|
|
||||||
const illegalChars = "=&#:*$%?/;\\\b\n\t\r\x00"
|
const illegalChars = "=&#:$%?/;\\\b\n\t\r\x00"
|
||||||
|
|
||||||
func AllClaimsAreSane(script []byte, enforceSoftFork bool) error {
|
func AllClaimsAreSane(script []byte, enforceSoftFork bool) error {
|
||||||
cs, err := ExtractClaimScript(script)
|
cs, err := ExtractClaimScript(script)
|
||||||
|
|
Loading…
Reference in a new issue