2017-11-03 13:46:27 +01:00
|
|
|
package lbrycrd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
|
2018-02-22 20:17:22 +01:00
|
|
|
"github.com/lbryio/lbry.go/errors"
|
|
|
|
|
2017-11-03 13:46:27 +01:00
|
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
2018-03-07 12:27:03 +01:00
|
|
|
"github.com/btcsuite/btcd/rpcclient"
|
2017-11-03 13:46:27 +01:00
|
|
|
"github.com/btcsuite/btcutil"
|
|
|
|
"github.com/go-ini/ini"
|
|
|
|
)
|
|
|
|
|
|
|
|
const DefaultPort = 9245
|
|
|
|
|
2018-06-27 21:41:21 +02:00
|
|
|
var GenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
|
|
|
0x9c, 0x89, 0x28, 0x3b, 0xa0, 0xf3, 0x22, 0x7f,
|
|
|
|
0x6c, 0x03, 0xb7, 0x02, 0x16, 0xb9, 0xf6, 0x65,
|
|
|
|
0xf0, 0x11, 0x8d, 0x5e, 0x0f, 0xa7, 0x29, 0xce,
|
|
|
|
0xdf, 0x4f, 0xb3, 0x4d, 0x6a, 0x34, 0xf4, 0x63,
|
|
|
|
})
|
|
|
|
|
2017-11-03 13:46:27 +01:00
|
|
|
// MainNetParams define the lbrycrd network. See https://github.com/lbryio/lbrycrd/blob/master/src/chainparams.cpp
|
|
|
|
var MainNetParams = chaincfg.Params{
|
|
|
|
PubKeyHashAddrID: 0x55,
|
|
|
|
ScriptHashAddrID: 0x7a,
|
|
|
|
PrivateKeyID: 0x1c,
|
2018-06-27 21:41:21 +02:00
|
|
|
Bech32HRPSegwit: "not-used", // we don't have this (yet)
|
|
|
|
GenesisHash: &GenesisHash,
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
// Register lbrycrd network
|
|
|
|
err := chaincfg.Register(&MainNetParams)
|
|
|
|
if err != nil {
|
|
|
|
panic("failed to register lbrycrd network: " + err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Client connects to a lbrycrd instance
|
|
|
|
type Client struct {
|
2018-03-07 12:27:03 +01:00
|
|
|
*rpcclient.Client
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// New initializes a new Client
|
|
|
|
func New(lbrycrdURL string) (*Client, error) {
|
|
|
|
// Connect to local bitcoin core RPC server using HTTP POST mode.
|
|
|
|
|
|
|
|
u, err := url.Parse(lbrycrdURL)
|
|
|
|
if err != nil {
|
2018-02-22 20:17:22 +01:00
|
|
|
return nil, errors.Err(err)
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if u.User == nil {
|
2018-02-22 20:17:22 +01:00
|
|
|
return nil, errors.Err("no userinfo")
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
password, _ := u.User.Password()
|
|
|
|
|
2018-03-07 12:27:03 +01:00
|
|
|
connCfg := &rpcclient.ConnConfig{
|
2017-11-03 13:46:27 +01:00
|
|
|
Host: u.Host,
|
|
|
|
User: u.User.Username(),
|
|
|
|
Pass: password,
|
|
|
|
HTTPPostMode: true, // Bitcoin core only supports HTTP POST mode
|
|
|
|
DisableTLS: true, // Bitcoin core does not provide TLS by default
|
|
|
|
}
|
|
|
|
// Notice the notification parameter is nil since notifications are not supported in HTTP POST mode.
|
2018-03-07 12:27:03 +01:00
|
|
|
client, err := rpcclient.New(connCfg, nil)
|
2017-11-03 13:46:27 +01:00
|
|
|
if err != nil {
|
2018-02-22 20:17:22 +01:00
|
|
|
return nil, errors.Err(err)
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
2017-11-21 19:53:56 +01:00
|
|
|
// make sure lbrycrd is running and responsive
|
|
|
|
_, err = client.GetInfo()
|
|
|
|
if err != nil {
|
2018-02-22 20:17:22 +01:00
|
|
|
return nil, errors.Err(err)
|
2017-11-21 19:53:56 +01:00
|
|
|
}
|
|
|
|
|
2017-11-03 13:46:27 +01:00
|
|
|
return &Client{client}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewWithDefaultURL() (*Client, error) {
|
2018-03-09 01:42:42 +01:00
|
|
|
url, err := getLbrycrdURLFromConfFile()
|
2017-11-03 13:46:27 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-03-09 01:42:42 +01:00
|
|
|
return New(url)
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
2018-02-22 20:17:22 +01:00
|
|
|
var errInsufficientFunds = errors.Base("insufficient funds")
|
2017-11-03 13:46:27 +01:00
|
|
|
|
|
|
|
// SimpleSend is a convenience function to send credits to an address (0 min confirmations)
|
|
|
|
func (c *Client) SimpleSend(toAddress string, amount float64) (*chainhash.Hash, error) {
|
2018-06-27 21:41:21 +02:00
|
|
|
decodedAddress, err := DecodeAddress(toAddress, &MainNetParams)
|
2017-11-03 13:46:27 +01:00
|
|
|
if err != nil {
|
2018-02-22 20:17:22 +01:00
|
|
|
return nil, errors.Err(err)
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
lbcAmount, err := btcutil.NewAmount(amount)
|
|
|
|
if err != nil {
|
2018-02-22 20:17:22 +01:00
|
|
|
return nil, errors.Err(err)
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
hash, err := c.Client.SendFromMinConf("", decodedAddress, lbcAmount, 0)
|
2017-12-30 00:51:38 +01:00
|
|
|
if err != nil {
|
|
|
|
if err.Error() == "-6: Insufficient funds" {
|
2018-02-22 20:17:22 +01:00
|
|
|
err = errors.Err(errInsufficientFunds)
|
2017-12-30 00:51:38 +01:00
|
|
|
}
|
2018-02-22 20:17:22 +01:00
|
|
|
return nil, errors.Err(err)
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
2017-12-30 00:51:38 +01:00
|
|
|
return hash, nil
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//func (c *Client) SendWithSplit(toAddress string, amount float64, numUTXOs int) (*chainhash.Hash, error) {
|
2018-06-27 21:41:21 +02:00
|
|
|
// decodedAddress, err := DecodeAddress(toAddress, &MainNetParams)
|
2017-11-03 13:46:27 +01:00
|
|
|
// if err != nil {
|
|
|
|
// return nil, errors.Wrap(err, 0)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// amountPerAddress, err := btcutil.NewAmount(amount / float64(numUTXOs))
|
|
|
|
// if err != nil {
|
|
|
|
// return nil, errors.Wrap(err, 0)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// amounts := map[btcutil.Address]btcutil.Amount{}
|
|
|
|
// for i := 0; i < numUTXOs; i++ {
|
|
|
|
// addr := decodedAddress // to give it a new address, so
|
|
|
|
// amounts[addr] = amountPerAddress
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// hash, err := c.Client.SendManyMinConf("", amounts, 0)
|
|
|
|
// if err != nil && err.Error() == "-6: Insufficient funds" {
|
|
|
|
// err = errors.Wrap(errInsufficientFunds, 0)
|
|
|
|
// }
|
|
|
|
// return hash, errors.Wrap(err, 0)
|
|
|
|
//}
|
|
|
|
|
|
|
|
func getLbrycrdURLFromConfFile() (string, error) {
|
|
|
|
if os.Getenv("HOME") == "" {
|
2018-02-22 20:17:22 +01:00
|
|
|
return "", errors.Err("no $HOME var found")
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
defaultConfFile := os.Getenv("HOME") + "/.lbrycrd/lbrycrd.conf"
|
|
|
|
if _, err := os.Stat(defaultConfFile); os.IsNotExist(err) {
|
2018-02-22 20:17:22 +01:00
|
|
|
return "", errors.Err("default lbrycrd conf file not found")
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cfg, err := ini.Load(defaultConfFile)
|
|
|
|
if err != nil {
|
2018-02-22 20:17:22 +01:00
|
|
|
return "", errors.Err(err)
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
section, err := cfg.GetSection("")
|
|
|
|
if err != nil {
|
2018-02-22 20:17:22 +01:00
|
|
|
return "", errors.Err(err)
|
2017-11-03 13:46:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
username := section.Key("rpcuser").String()
|
|
|
|
password := section.Key("rpcpassword").String()
|
|
|
|
host := section.Key("rpchost").String()
|
|
|
|
if host == "" {
|
|
|
|
host = "localhost"
|
|
|
|
}
|
|
|
|
port := section.Key("rpcport").String()
|
|
|
|
if port == "" {
|
|
|
|
port = strconv.Itoa(DefaultPort)
|
|
|
|
}
|
|
|
|
|
|
|
|
userpass := ""
|
|
|
|
if username != "" || password != "" {
|
|
|
|
userpass = username + ":" + password + "@"
|
|
|
|
}
|
|
|
|
|
|
|
|
return "rpc://" + userpass + host + ":" + port, nil
|
|
|
|
}
|