uncommitted stuff
This commit is contained in:
parent
0945466653
commit
430fc530f2
8 changed files with 672 additions and 15 deletions
149
electrum/client.go
Normal file
149
electrum/client.go
Normal file
|
@ -0,0 +1,149 @@
|
|||
package electrum
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/lbryio/lbry.go/v3/lbrycrd"
|
||||
"github.com/lbryio/lbry.go/v3/schema/stake"
|
||||
types "github.com/lbryio/types/v2/go"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/lbryio/lbcutil"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// Raw makes a raw wallet server request
|
||||
func (n *Node) Raw(method string, params []string, v interface{}) error {
|
||||
return n.request(method, params, v)
|
||||
}
|
||||
|
||||
// ServerVersion returns the server's version.
|
||||
// https://electrumx.readthedocs.io/en/latest/protocol-methods.html#server-version
|
||||
func (n *Node) ServerVersion() (string, error) {
|
||||
resp := &struct {
|
||||
Result []string `json:"result"`
|
||||
}{}
|
||||
err := n.request("server.version", []string{"reflector.go", ProtocolVersion}, resp)
|
||||
|
||||
var v string
|
||||
if len(resp.Result) >= 2 {
|
||||
v = resp.Result[1]
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (n *Node) Resolve(url string) (*types.Output, error) {
|
||||
outputs := &types.Outputs{}
|
||||
resp := &struct {
|
||||
Result string `json:"result"`
|
||||
}{}
|
||||
|
||||
err := n.request("blockchain.claimtrie.resolve", []string{url}, resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(resp.Result)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
err = proto.Unmarshal(b, outputs)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if len(outputs.GetTxos()) != 1 {
|
||||
return nil, errors.New("expected 1 output, got " + cast.ToString(len(outputs.GetTxos())))
|
||||
}
|
||||
|
||||
if e := outputs.GetTxos()[0].GetError(); e != nil {
|
||||
return nil, errors.Newf("%s: %s", e.GetCode(), e.GetText())
|
||||
}
|
||||
|
||||
return outputs.GetTxos()[0], nil
|
||||
}
|
||||
|
||||
type GetClaimsInTxResp struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
ID int `json:"id"`
|
||||
Result []struct {
|
||||
Name string `json:"name"`
|
||||
ClaimID string `json:"claim_id"`
|
||||
Txid string `json:"txid"`
|
||||
Nout int `json:"nout"`
|
||||
Amount int `json:"amount"`
|
||||
Depth int `json:"depth"`
|
||||
Height int `json:"height"`
|
||||
Value string `json:"value"`
|
||||
ClaimSequence int `json:"claim_sequence"`
|
||||
Address string `json:"address"`
|
||||
Supports []interface{} `json:"supports"` // TODO: finish me
|
||||
EffectiveAmount int `json:"effective_amount"`
|
||||
ValidAtHeight int `json:"valid_at_height"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
func (n *Node) GetClaimsInTx(txid string) (*GetClaimsInTxResp, error) {
|
||||
var resp GetClaimsInTxResp
|
||||
err := n.request("blockchain.claimtrie.getclaimsintx", []string{txid}, &resp)
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
func (n *Node) GetTx(txid string) (string, error) {
|
||||
resp := &struct {
|
||||
Result string `json:"result"`
|
||||
}{}
|
||||
|
||||
err := n.request("blockchain.transaction.get", []string{txid}, resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.Result, nil
|
||||
}
|
||||
|
||||
func (n *Node) GetClaimInTx(txid string, nout int) (*types.Claim, error) {
|
||||
hexTx, err := n.GetTx(txid)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
rawTx, err := hex.DecodeString(hexTx)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
tx, err := lbcutil.NewTxFromBytes(rawTx)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if len(tx.MsgTx().TxOut) <= nout {
|
||||
return nil, errors.WithStack(errors.New("nout not found"))
|
||||
}
|
||||
|
||||
script := tx.MsgTx().TxOut[nout].PkScript
|
||||
|
||||
var value []byte
|
||||
if lbrycrd.IsClaimNameScript(script) {
|
||||
_, value, _, err = lbrycrd.ParseClaimNameScript(script)
|
||||
} else if lbrycrd.IsClaimUpdateScript(script) {
|
||||
_, _, value, _, err = lbrycrd.ParseClaimUpdateScript(script)
|
||||
} else {
|
||||
err = errors.New("no claim found in output")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ch, err := stake.DecodeClaimBytes(value, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ch.Claim, nil
|
||||
}
|
279
electrum/network.go
Normal file
279
electrum/network.go
Normal file
|
@ -0,0 +1,279 @@
|
|||
package electrum
|
||||
|
||||
// copied from https://github.com/d4l3k/go-electrum
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbry.go/v3/extras/stop"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
ClientVersion = "0.0.1"
|
||||
ProtocolVersion = "1.0"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotImplemented = errors.New("not implemented")
|
||||
ErrNodeConnected = errors.New("node already connected")
|
||||
ErrConnectFailed = errors.New("failed to connect")
|
||||
ErrTimeout = errors.New("timeout")
|
||||
)
|
||||
|
||||
type response struct {
|
||||
data []byte
|
||||
err error
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
transport *TCPTransport
|
||||
nextId atomic.Uint32
|
||||
grp *stop.Group
|
||||
|
||||
handlersMu *sync.RWMutex
|
||||
handlers map[uint32]chan response
|
||||
|
||||
pushHandlersMu *sync.RWMutex
|
||||
pushHandlers map[string][]chan response
|
||||
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewNode creates a new node.
|
||||
func NewNode() *Node {
|
||||
return &Node{
|
||||
handlers: make(map[uint32]chan response),
|
||||
pushHandlers: make(map[string][]chan response),
|
||||
handlersMu: &sync.RWMutex{},
|
||||
pushHandlersMu: &sync.RWMutex{},
|
||||
grp: stop.New(),
|
||||
timeout: 1 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// Connect creates a new connection to the specified address.
|
||||
func (n *Node) Connect(addrs []string, config *tls.Config) error {
|
||||
if n.transport != nil {
|
||||
return errors.WithStack(ErrNodeConnected)
|
||||
}
|
||||
|
||||
// shuffle addresses for load balancing
|
||||
rand.Shuffle(len(addrs), func(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] })
|
||||
|
||||
var err error
|
||||
|
||||
for _, addr := range addrs {
|
||||
n.transport, err = NewTransport(addr, config)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if errors.Is(err, ErrTimeout) {
|
||||
continue
|
||||
}
|
||||
if e, ok := err.(*net.OpError); ok && e.Err.Error() == "no such host" {
|
||||
// net.errNoSuchHost is not exported, so we have to string-match
|
||||
continue
|
||||
}
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if n.transport == nil {
|
||||
return errors.WithStack(ErrConnectFailed)
|
||||
}
|
||||
|
||||
log.Debugf("wallet connected to %s", n.transport.conn.RemoteAddr())
|
||||
|
||||
n.grp.Add(1)
|
||||
go func() {
|
||||
defer n.grp.Done()
|
||||
<-n.grp.Ch()
|
||||
n.transport.Shutdown()
|
||||
}()
|
||||
|
||||
n.grp.Add(1)
|
||||
go func() {
|
||||
defer n.grp.Done()
|
||||
n.handleErrors()
|
||||
}()
|
||||
|
||||
n.grp.Add(1)
|
||||
go func() {
|
||||
defer n.grp.Done()
|
||||
n.listen()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Node) Shutdown() {
|
||||
var addr net.Addr
|
||||
if n.transport != nil {
|
||||
addr = n.transport.conn.RemoteAddr()
|
||||
}
|
||||
log.Debugf("shutting down wallet %s", addr)
|
||||
n.grp.StopAndWait()
|
||||
log.Debugf("wallet stopped")
|
||||
}
|
||||
|
||||
func (n *Node) handleErrors() {
|
||||
for {
|
||||
select {
|
||||
case <-n.grp.Ch():
|
||||
return
|
||||
case err := <-n.transport.Errors():
|
||||
n.err(errors.WithStack(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// err handles errors produced by the foreign node.
|
||||
func (n *Node) err(err error) {
|
||||
// TODO: Better error handling.
|
||||
log.Error(errors.WithStack(err))
|
||||
}
|
||||
|
||||
// listen processes messages from the server.
|
||||
func (n *Node) listen() {
|
||||
for {
|
||||
select {
|
||||
case <-n.grp.Ch():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
select {
|
||||
case <-n.grp.Ch():
|
||||
return
|
||||
case bytes := <-n.transport.Responses():
|
||||
msg := &struct {
|
||||
Id uint32 `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Error struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
} `json:"error"`
|
||||
}{}
|
||||
msg2 := &struct {
|
||||
Id uint32 `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Error struct {
|
||||
Code int `json:"code"`
|
||||
Message struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
} `json:"message"`
|
||||
} `json:"error"`
|
||||
}{}
|
||||
r := response{}
|
||||
|
||||
err := json.Unmarshal(bytes, msg)
|
||||
if err != nil {
|
||||
// try msg2, a hack around the weird error-in-error response we sometimes get from wallet server
|
||||
// maybe that happens because the wallet server passes a lbrycrd error through to us?
|
||||
if err2 := json.Unmarshal(bytes, msg2); err2 == nil {
|
||||
err = nil
|
||||
msg.Id = msg2.Id
|
||||
msg.Method = msg2.Method
|
||||
msg.Error = msg2.Error.Message
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
r.err = errors.WithStack(err)
|
||||
n.err(r.err)
|
||||
} else if len(msg.Error.Message) > 0 {
|
||||
r.err = errors.WithStack(errors.Newf("%d: %s", msg.Error.Code, msg.Error.Message))
|
||||
} else {
|
||||
r.data = bytes
|
||||
}
|
||||
|
||||
if len(msg.Method) > 0 {
|
||||
n.pushHandlersMu.RLock()
|
||||
handlers := n.pushHandlers[msg.Method]
|
||||
n.pushHandlersMu.RUnlock()
|
||||
|
||||
for _, handler := range handlers {
|
||||
select {
|
||||
case handler <- r:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n.handlersMu.RLock()
|
||||
c, ok := n.handlers[msg.Id]
|
||||
n.handlersMu.RUnlock()
|
||||
if ok {
|
||||
c <- r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// listenPush returns a channel of messages matching the method.
|
||||
//func (n *Node) listenPush(method string) <-chan []byte {
|
||||
// c := make(chan []byte, 1)
|
||||
// n.pushHandlersMu.Lock()
|
||||
// defer n.pushHandlersMu.Unlock()
|
||||
// n.pushHandlers[method] = append(n.pushHandlers[method], c)
|
||||
// return c
|
||||
//}
|
||||
|
||||
// request makes a request to the server and unmarshals the response into v.
|
||||
func (n *Node) request(method string, params []string, v interface{}) error {
|
||||
msg := struct {
|
||||
Id uint32 `json:"id"`
|
||||
Method string `json:"method"`
|
||||
Params []string `json:"params"`
|
||||
}{
|
||||
Id: n.nextId.Load(),
|
||||
Method: method,
|
||||
Params: params,
|
||||
}
|
||||
n.nextId.Inc()
|
||||
|
||||
bytes, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
bytes = append(bytes, delimiter)
|
||||
|
||||
c := make(chan response, 1)
|
||||
|
||||
n.handlersMu.Lock()
|
||||
n.handlers[msg.Id] = c
|
||||
n.handlersMu.Unlock()
|
||||
|
||||
err = n.transport.Send(bytes)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
var r response
|
||||
select {
|
||||
case <-n.grp.Ch():
|
||||
return nil
|
||||
case r = <-c:
|
||||
case <-time.After(n.timeout):
|
||||
r = response{err: ErrTimeout}
|
||||
}
|
||||
|
||||
n.handlersMu.Lock()
|
||||
delete(n.handlers, msg.Id)
|
||||
n.handlersMu.Unlock()
|
||||
|
||||
if r.err != nil {
|
||||
return errors.WithStack(r.err)
|
||||
}
|
||||
|
||||
return errors.WithStack(json.Unmarshal(r.data, v))
|
||||
}
|
136
electrum/transport.go
Normal file
136
electrum/transport.go
Normal file
|
@ -0,0 +1,136 @@
|
|||
package electrum
|
||||
|
||||
// copied from https://github.com/d4l3k/go-electrum
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/lbryio/lbry.go/v3/extras/stop"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type TCPTransport struct {
|
||||
conn net.Conn
|
||||
responses chan []byte
|
||||
errors chan error
|
||||
grp *stop.Group
|
||||
}
|
||||
|
||||
func NewTransport(addr string, config *tls.Config) (*TCPTransport, error) {
|
||||
var conn net.Conn
|
||||
var err error
|
||||
|
||||
timeout := 5 * time.Second
|
||||
if config != nil {
|
||||
conn, err = tls.DialWithDialer(&net.Dialer{Timeout: timeout}, "tcp", addr, config)
|
||||
} else {
|
||||
conn, err = net.DialTimeout("tcp", addr, timeout)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := &TCPTransport{
|
||||
conn: conn,
|
||||
responses: make(chan []byte),
|
||||
errors: make(chan error),
|
||||
grp: stop.New(),
|
||||
}
|
||||
|
||||
t.grp.Add(1)
|
||||
go func() {
|
||||
defer t.grp.Done()
|
||||
<-t.grp.Ch()
|
||||
t.close()
|
||||
}()
|
||||
|
||||
t.grp.Add(1)
|
||||
go func() {
|
||||
defer t.grp.Done()
|
||||
t.listen()
|
||||
}()
|
||||
|
||||
err = t.test()
|
||||
if err != nil {
|
||||
t.grp.StopAndWait()
|
||||
return nil, errors.WithMessage(err, addr)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
const delimiter = byte('\n')
|
||||
|
||||
func (t *TCPTransport) Send(body []byte) error {
|
||||
log.Debugf("%s <- %s", t.conn.RemoteAddr(), body)
|
||||
_, err := t.conn.Write(body)
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *TCPTransport) Responses() <-chan []byte { return t.responses }
|
||||
func (t *TCPTransport) Errors() <-chan error { return t.errors }
|
||||
func (t *TCPTransport) Shutdown() { t.grp.StopAndWait() }
|
||||
|
||||
func (t *TCPTransport) listen() {
|
||||
reader := bufio.NewReader(t.conn)
|
||||
for {
|
||||
line, err := reader.ReadBytes(delimiter)
|
||||
if err != nil {
|
||||
t.error(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("%s -> %s", t.conn.RemoteAddr(), line)
|
||||
|
||||
t.responses <- line
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TCPTransport) error(err error) {
|
||||
select {
|
||||
case t.errors <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TCPTransport) test() error {
|
||||
err := t.Send([]byte(`{"id":1,"method":"server.version"}` + "\n"))
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
var data []byte
|
||||
select {
|
||||
case data = <-t.Responses():
|
||||
case <-time.Tick(1 * time.Second):
|
||||
return errors.WithStack(ErrTimeout)
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Error struct {
|
||||
Message string `json:"message"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, &response)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
if response.Error.Message != "" {
|
||||
return errors.WithStack(errors.New(response.Error.Message))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TCPTransport) close() {
|
||||
err := t.conn.Close()
|
||||
if err != nil {
|
||||
t.error(err)
|
||||
}
|
||||
}
|
2
go.sum
2
go.sum
|
@ -39,7 +39,6 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm
|
|||
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-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ=
|
||||
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=
|
||||
|
@ -265,7 +264,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||
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.0.0-20200203050410-e1076f12bf19 h1:Cm9IuRnz5Pj1v/RsdkZ6UrDALn7wyfNtsGTpi/aVf2w=
|
||||
github.com/lbryio/lbcd v0.0.0-20200203050410-e1076f12bf19/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/lbryio/lbcd v0.22.100-beta/go.mod h1:u8SaFX4xdGMMR5xasBGfgApC8pvD4rnK2OujZnrq5gs=
|
||||
github.com/lbryio/lbcd v0.22.101-beta h1:umLIxhyRwPdi91vtsDfgW85viK0AV8RPtIF9zQYtXw0=
|
||||
|
|
|
@ -3,7 +3,7 @@ package lbrycrd_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/lbryio/lbry.go/v2/lbrycrd"
|
||||
"github.com/lbryio/lbry.go/v3/lbrycrd"
|
||||
)
|
||||
|
||||
var claimIdTests = []struct {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package lbrycrd
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/lbryio/lbry.go/v2/extras/errors"
|
||||
|
||||
"github.com/lbryio/lbcd/txscript"
|
||||
"github.com/lbryio/lbcutil"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
)
|
||||
|
||||
func GetClaimSupportPayoutScript(name, claimid string, address lbcutil.Address) ([]byte, error) {
|
||||
|
@ -14,12 +15,12 @@ func GetClaimSupportPayoutScript(name, claimid string, address lbcutil.Address)
|
|||
|
||||
pkscript, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
return nil, errors.Err(err)
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
bytes, err := hex.DecodeString(claimid)
|
||||
if err != nil {
|
||||
return nil, errors.Err(err)
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return txscript.NewScriptBuilder().
|
||||
|
@ -38,7 +39,7 @@ func GetClaimNamePayoutScript(name string, value []byte, address lbcutil.Address
|
|||
|
||||
pkscript, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
return nil, errors.Err(err)
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return txscript.NewScriptBuilder().
|
||||
|
@ -56,12 +57,12 @@ func GetUpdateClaimPayoutScript(name, claimid string, value []byte, address lbcu
|
|||
|
||||
pkscript, err := txscript.PayToAddrScript(address)
|
||||
if err != nil {
|
||||
return nil, errors.Err(err)
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
bytes, err := hex.DecodeString(claimid)
|
||||
if err != nil {
|
||||
return nil, errors.Err(err)
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return txscript.NewScriptBuilder().
|
||||
|
@ -74,3 +75,96 @@ func GetUpdateClaimPayoutScript(name, claimid string, value []byte, address lbcu
|
|||
AddOps(pkscript). //OP_DUP OP_HASH160 <address> OP_EQUALVERIFY OP_CHECKSIG
|
||||
Script()
|
||||
}
|
||||
|
||||
// IsClaimNameScript returns true if the script for the vout contains the OP_CLAIM_NAME code.
|
||||
func IsClaimNameScript(script []byte) bool {
|
||||
return len(script) > 0 && script[0] == txscript.OP_CLAIMNAME
|
||||
}
|
||||
|
||||
// IsClaimUpdateScript returns true if the script for the vout contains the OP_CLAIM_UPDATE code.
|
||||
func IsClaimUpdateScript(script []byte) bool {
|
||||
return len(script) > 0 && script[0] == txscript.OP_UPDATECLAIM
|
||||
}
|
||||
|
||||
// ParseClaimNameScript parses a script for the claim of a name.
|
||||
func ParseClaimNameScript(script []byte) (name string, value []byte, pubkeyscript []byte, err error) {
|
||||
// Already validated by blockchain so can be assumed
|
||||
// opClaimName Name Value OP_2DROP OP_DROP pubkeyscript
|
||||
nameBytesToRead := int(script[1])
|
||||
nameStart := 2
|
||||
if nameBytesToRead == txscript.OP_PUSHDATA1 {
|
||||
nameBytesToRead = int(script[2])
|
||||
nameStart = 3
|
||||
} else if nameBytesToRead > txscript.OP_PUSHDATA1 {
|
||||
return "", nil, nil, errors.WithStack(errors.New("bytes to read is more than next byte"))
|
||||
}
|
||||
nameEnd := nameStart + nameBytesToRead
|
||||
name = string(script[nameStart:nameEnd])
|
||||
dataPushType := int(script[nameEnd])
|
||||
valueBytesToRead := int(script[nameEnd])
|
||||
valueStart := nameEnd + 1
|
||||
if dataPushType == txscript.OP_PUSHDATA1 {
|
||||
valueBytesToRead = int(script[nameEnd+1])
|
||||
valueStart = nameEnd + 2
|
||||
} else if dataPushType == txscript.OP_PUSHDATA2 {
|
||||
valueStart = nameEnd + 3
|
||||
valueBytesToRead = int(binary.LittleEndian.Uint16(script[nameEnd+1 : valueStart]))
|
||||
} else if dataPushType == txscript.OP_PUSHDATA4 {
|
||||
valueStart = nameEnd + 5
|
||||
valueBytesToRead = int(binary.LittleEndian.Uint32(script[nameEnd+2 : valueStart]))
|
||||
}
|
||||
valueEnd := valueStart + valueBytesToRead
|
||||
value = script[valueStart:valueEnd]
|
||||
pksStart := valueEnd + 2 // +2 to ignore OP_2DROP and OP_DROP
|
||||
pubkeyscript = script[pksStart:] //Remainder is always pubkeyscript
|
||||
|
||||
return name, value, pubkeyscript, err
|
||||
}
|
||||
|
||||
// ParseClaimUpdateScript parses a script for an update of a claim.
|
||||
func ParseClaimUpdateScript(script []byte) (name string, claimid string, value []byte, pubkeyscript []byte, err error) {
|
||||
// opUpdateClaim Name ClaimID Value OP_2DROP OP_2DROP pubkeyscript
|
||||
|
||||
//Name
|
||||
nameBytesToRead := int(script[1])
|
||||
nameStart := 2
|
||||
if nameBytesToRead == txscript.OP_PUSHDATA1 {
|
||||
nameBytesToRead = int(script[2])
|
||||
nameStart = 3
|
||||
} else if nameBytesToRead > txscript.OP_PUSHDATA1 {
|
||||
err = errors.WithStack(errors.New("ParseClaimUpdateScript: Bytes to read is more than next byte! "))
|
||||
return
|
||||
}
|
||||
nameEnd := nameStart + nameBytesToRead
|
||||
name = string(script[nameStart:nameEnd])
|
||||
|
||||
//ClaimID
|
||||
claimidBytesToRead := int(script[nameEnd])
|
||||
claimidStart := nameEnd + 1
|
||||
claimidEnd := claimidStart + claimidBytesToRead
|
||||
bytes := rev(script[claimidStart:claimidEnd])
|
||||
claimid = hex.EncodeToString(bytes)
|
||||
|
||||
//Value
|
||||
dataPushType := int(script[claimidEnd])
|
||||
valueBytesToRead := int(script[claimidEnd])
|
||||
valueStart := claimidEnd + 1
|
||||
if dataPushType == txscript.OP_PUSHDATA1 {
|
||||
valueBytesToRead = int(script[claimidEnd+1])
|
||||
valueStart = claimidEnd + 2
|
||||
} else if dataPushType == txscript.OP_PUSHDATA2 {
|
||||
valueStart = claimidEnd + 3
|
||||
valueBytesToRead = int(binary.LittleEndian.Uint16(script[claimidEnd+1 : valueStart]))
|
||||
} else if dataPushType == txscript.OP_PUSHDATA4 {
|
||||
valueStart = claimidEnd + 5
|
||||
valueBytesToRead = int(binary.LittleEndian.Uint32(script[claimidEnd+2 : valueStart]))
|
||||
}
|
||||
valueEnd := valueStart + valueBytesToRead
|
||||
value = script[valueStart:valueEnd]
|
||||
|
||||
//PublicKeyScript
|
||||
pksStart := valueEnd + 2 // +2 to ignore OP_2DROP and OP_DROP
|
||||
pubkeyscript = script[pksStart:] //Remainder is always pubkeyscript
|
||||
|
||||
return name, claimid, value, pubkeyscript, err
|
||||
}
|
||||
|
|
|
@ -4,11 +4,12 @@ import (
|
|||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/lbryio/lbry.go/v3/schema/address"
|
||||
"github.com/lbryio/lbry.go/v3/schema/keys"
|
||||
|
||||
"github.com/lbryio/lbcd/btcec"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
)
|
||||
|
||||
func Sign(privKey btcec.PrivateKey, channel Helper, claim Helper, k string) (*keys.Signature, error) {
|
||||
|
@ -23,7 +24,6 @@ func Sign(privKey btcec.PrivateKey, channel Helper, claim Helper, k string) (*ke
|
|||
}
|
||||
|
||||
func (c *Helper) sign(privKey btcec.PrivateKey, channel Helper, firstInputTxID string) (*keys.Signature, error) {
|
||||
|
||||
txidBytes, err := hex.DecodeString(firstInputTxID)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
|
|
|
@ -44,10 +44,11 @@ func (c *Helper) ValidateAddresses(blockchainName string) error {
|
|||
fee := c.GetStream().GetFee()
|
||||
if fee != nil {
|
||||
return validateAddress(fee.GetAddress(), blockchainName)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if c.Claim.GetChannel() != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.Claim.GetChannel() != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue