From 430fc530f21953d65a1882bff9ecb1fd88272213 Mon Sep 17 00:00:00 2001 From: Alex Grintsvayg Date: Wed, 20 Oct 2021 12:23:55 -0400 Subject: [PATCH] uncommitted stuff --- electrum/client.go | 149 ++++++++++++++++++++ electrum/network.go | 279 +++++++++++++++++++++++++++++++++++++ electrum/transport.go | 136 ++++++++++++++++++ go.sum | 2 - lbrycrd/blockchain_test.go | 2 +- lbrycrd/script.go | 108 +++++++++++++- schema/stake/sign.go | 4 +- schema/stake/stake.go | 7 +- 8 files changed, 672 insertions(+), 15 deletions(-) create mode 100644 electrum/client.go create mode 100644 electrum/network.go create mode 100644 electrum/transport.go diff --git a/electrum/client.go b/electrum/client.go new file mode 100644 index 0000000..c7f47af --- /dev/null +++ b/electrum/client.go @@ -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 +} diff --git a/electrum/network.go b/electrum/network.go new file mode 100644 index 0000000..ae9310b --- /dev/null +++ b/electrum/network.go @@ -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)) +} diff --git a/electrum/transport.go b/electrum/transport.go new file mode 100644 index 0000000..55a67ea --- /dev/null +++ b/electrum/transport.go @@ -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) + } +} diff --git a/go.sum b/go.sum index 43125d2..f2b2006 100644 --- a/go.sum +++ b/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= diff --git a/lbrycrd/blockchain_test.go b/lbrycrd/blockchain_test.go index e4b0be9..1cbd1c8 100644 --- a/lbrycrd/blockchain_test.go +++ b/lbrycrd/blockchain_test.go @@ -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 { diff --git a/lbrycrd/script.go b/lbrycrd/script.go index 430c185..4378d90 100644 --- a/lbrycrd/script.go +++ b/lbrycrd/script.go @@ -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
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 +} diff --git a/schema/stake/sign.go b/schema/stake/sign.go index 21b6d52..cf62079 100644 --- a/schema/stake/sign.go +++ b/schema/stake/sign.go @@ -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) diff --git a/schema/stake/stake.go b/schema/stake/stake.go index d15171d..3c851cf 100644 --- a/schema/stake/stake.go +++ b/schema/stake/stake.go @@ -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 } }