lbcd/cmds.go
2013-11-08 14:07:57 -05:00

468 lines
12 KiB
Go

// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package btcws
import (
"encoding/json"
"errors"
"github.com/conformal/btcdb"
"github.com/conformal/btcjson"
"github.com/conformal/btcwire"
)
func init() {
btcjson.RegisterCustomCmd("getcurrentnet", parseGetCurrentNetCmd)
btcjson.RegisterCustomCmd("getbestblock", parseGetBestBlockCmd)
btcjson.RegisterCustomCmd("rescan", parseRescanCmd)
btcjson.RegisterCustomCmd("notifynewtxs", parseNotifyNewTXsCmd)
btcjson.RegisterCustomCmd("notifyspent", parseNotifySpentCmd)
}
// GetCurrentNetCmd is a type handling custom marshaling and
// unmarshaling of getcurrentnet JSON websocket extension
// commands.
type GetCurrentNetCmd struct {
id interface{}
}
// Enforce that GetCurrentNetCmd satisifies the btcjson.Cmd interface.
var _ btcjson.Cmd = &GetCurrentNetCmd{}
// NewGetCurrentNetCmd creates a new GetCurrentNetCmd.
func NewGetCurrentNetCmd(id interface{}) *GetCurrentNetCmd {
return &GetCurrentNetCmd{id: id}
}
// parseGetCurrentNetCmd parses a RawCmd into a concrete type satisifying
// the btcjson.Cmd interface. This is used when registering the custom
// command with the btcjson parser.
func parseGetCurrentNetCmd(r *btcjson.RawCmd) (btcjson.Cmd, error) {
if len(r.Params) != 0 {
return nil, btcjson.ErrWrongNumberOfParams
}
return NewGetCurrentNetCmd(r.Id), nil
}
// Id satisifies the Cmd interface by returning the ID of the command.
func (cmd *GetCurrentNetCmd) Id() interface{} {
return cmd.id
}
// Method satisfies the Cmd interface by returning the RPC method.
func (cmd *GetCurrentNetCmd) Method() string {
return "getcurrentnet"
}
// MarshalJSON returns the JSON encoding of cmd. Part of the Cmd interface.
func (cmd *GetCurrentNetCmd) MarshalJSON() ([]byte, error) {
// Fill a RawCmd and marshal.
raw := btcjson.RawCmd{
Jsonrpc: "1.0",
Method: "getcurrentnet",
Id: cmd.id,
}
return json.Marshal(raw)
}
// UnmarshalJSON unmarshals the JSON encoding of cmd into cmd. Part of
// the Cmd interface.
func (cmd *GetCurrentNetCmd) UnmarshalJSON(b []byte) error {
// Unmarshal into a RawCmd.
var r btcjson.RawCmd
if err := json.Unmarshal(b, &r); err != nil {
return err
}
newCmd, err := parseGetCurrentNetCmd(&r)
if err != nil {
return err
}
concreteCmd, ok := newCmd.(*GetCurrentNetCmd)
if !ok {
return btcjson.ErrInternal
}
*cmd = *concreteCmd
return nil
}
// GetBestBlockCmd is a type handling custom marshaling and
// unmarshaling of getbestblock JSON websocket extension
// commands.
type GetBestBlockCmd struct {
id interface{}
}
// Enforce that GetBestBlockCmd satisifies the btcjson.Cmd interface.
var _ btcjson.Cmd = &GetBestBlockCmd{}
// NewGetBestBlockCmd creates a new GetBestBlock.
func NewGetBestBlockCmd(id interface{}) *GetBestBlockCmd {
return &GetBestBlockCmd{id: id}
}
// parseGetBestBlockCmd parses a RawCmd into a concrete type satisifying
// the btcjson.Cmd interface. This is used when registering the custom
// command with the btcjson parser.
func parseGetBestBlockCmd(r *btcjson.RawCmd) (btcjson.Cmd, error) {
if len(r.Params) != 0 {
return nil, btcjson.ErrWrongNumberOfParams
}
return NewGetBestBlockCmd(r.Id), nil
}
// Id satisifies the Cmd interface by returning the ID of the command.
func (cmd *GetBestBlockCmd) Id() interface{} {
return cmd.id
}
// Method satisfies the Cmd interface by returning the RPC method.
func (cmd *GetBestBlockCmd) Method() string {
return "getbestblock"
}
// MarshalJSON returns the JSON encoding of cmd. Part of the Cmd interface.
func (cmd *GetBestBlockCmd) MarshalJSON() ([]byte, error) {
// Fill a RawCmd and marshal.
raw := btcjson.RawCmd{
Jsonrpc: "1.0",
Method: "getbestblock",
Id: cmd.id,
}
return json.Marshal(raw)
}
// UnmarshalJSON unmarshals the JSON encoding of cmd into cmd. Part of
// the Cmd interface.
func (cmd *GetBestBlockCmd) UnmarshalJSON(b []byte) error {
// Unmarshal into a RawCmd.
var r btcjson.RawCmd
if err := json.Unmarshal(b, &r); err != nil {
return err
}
newCmd, err := parseGetBestBlockCmd(&r)
if err != nil {
return err
}
concreteCmd, ok := newCmd.(*GetBestBlockCmd)
if !ok {
return btcjson.ErrInternal
}
*cmd = *concreteCmd
return nil
}
// RescanCmd is a type handling custom marshaling and
// unmarshaling of rescan JSON websocket extension
// commands.
type RescanCmd struct {
id interface{}
BeginBlock int32
Addresses map[string]struct{}
EndBlock int64 // TODO: switch this and btcdb.AllShas to int32
}
// Enforce that RescanCmd satisifies the btcjson.Cmd interface.
var _ btcjson.Cmd = &RescanCmd{}
// NewRescanCmd creates a new RescanCmd, parsing the optional
// arguments optArgs which may either be empty or a single upper
// block height.
func NewRescanCmd(id interface{}, begin int32, addresses map[string]struct{},
optArgs ...int64) (*RescanCmd, error) {
// Optional parameters set to their defaults.
end := btcdb.AllShas
if len(optArgs) > 0 {
if len(optArgs) > 1 {
return nil, btcjson.ErrTooManyOptArgs
}
end = optArgs[0]
}
return &RescanCmd{
id: id,
BeginBlock: begin,
Addresses: addresses,
EndBlock: end,
}, nil
}
// parseRescanCmd parses a RawCmd into a concrete type satisifying
// the btcjson.Cmd interface. This is used when registering the custom
// command with the btcjson parser.
func parseRescanCmd(r *btcjson.RawCmd) (btcjson.Cmd, error) {
if len(r.Params) < 2 {
return nil, btcjson.ErrWrongNumberOfParams
}
begin, ok := r.Params[0].(float64)
if !ok {
return nil, errors.New("first parameter must be a number")
}
iaddrs, ok := r.Params[1].(map[string]interface{})
if !ok {
return nil, errors.New("second parameter must be a JSON object")
}
addresses := make(map[string]struct{}, len(iaddrs))
for addr := range iaddrs {
addresses[addr] = struct{}{}
}
params := make([]int64, len(r.Params[2:]))
for i, val := range r.Params[2:] {
fval, ok := val.(float64)
if !ok {
return nil, errors.New("optional parameters must " +
"be be numbers")
}
params[i] = int64(fval)
}
return NewRescanCmd(r.Id, int32(begin), addresses, params...)
}
// Id satisifies the Cmd interface by returning the ID of the command.
func (cmd *RescanCmd) Id() interface{} {
return cmd.id
}
// Method satisfies the Cmd interface by returning the RPC method.
func (cmd *RescanCmd) Method() string {
return "rescan"
}
// MarshalJSON returns the JSON encoding of cmd. Part of the Cmd interface.
func (cmd *RescanCmd) MarshalJSON() ([]byte, error) {
// Fill a RawCmd and marshal.
raw := btcjson.RawCmd{
Jsonrpc: "1.0",
Method: "rescan",
Id: cmd.id,
Params: []interface{}{
cmd.BeginBlock,
cmd.Addresses,
},
}
if cmd.EndBlock != btcdb.AllShas {
raw.Params = append(raw.Params, cmd.EndBlock)
}
return json.Marshal(raw)
}
// UnmarshalJSON unmarshals the JSON encoding of cmd into cmd. Part of
// the Cmd interface.
func (cmd *RescanCmd) UnmarshalJSON(b []byte) error {
// Unmarshal into a RawCmd.
var r btcjson.RawCmd
if err := json.Unmarshal(b, &r); err != nil {
return err
}
newCmd, err := parseRescanCmd(&r)
if err != nil {
return err
}
concreteCmd, ok := newCmd.(*RescanCmd)
if !ok {
return btcjson.ErrInternal
}
*cmd = *concreteCmd
return nil
}
// NotifyNewTXsCmd is a type handling custom marshaling and
// unmarshaling of notifynewtxs JSON websocket extension
// commands.
type NotifyNewTXsCmd struct {
id interface{}
Addresses []string
}
// Enforce that NotifyNewTXsCmd satisifies the btcjson.Cmd interface.
var _ btcjson.Cmd = &NotifyNewTXsCmd{}
// NewNotifyNewTXsCmd creates a new RescanCmd.
func NewNotifyNewTXsCmd(id interface{}, addresses []string) *NotifyNewTXsCmd {
return &NotifyNewTXsCmd{
id: id,
Addresses: addresses,
}
}
// parseNotifyNewTXsCmd parses a NotifyNewTXsCmd into a concrete type
// satisifying the btcjson.Cmd interface. This is used when registering
// the custom command with the btcjson parser.
func parseNotifyNewTXsCmd(r *btcjson.RawCmd) (btcjson.Cmd, error) {
if len(r.Params) != 1 {
return nil, btcjson.ErrWrongNumberOfParams
}
iaddrs, ok := r.Params[0].([]interface{})
if !ok {
return nil, errors.New("first parameter must be a JSON array")
}
addresses := make([]string, len(iaddrs))
for i := range iaddrs {
addr, ok := iaddrs[i].(string)
if !ok {
return nil, errors.New("first parameter must be an " +
"array of strings")
}
addresses[i] = addr
}
return NewNotifyNewTXsCmd(r.Id, addresses), nil
}
// Id satisifies the Cmd interface by returning the ID of the command.
func (cmd *NotifyNewTXsCmd) Id() interface{} {
return cmd.id
}
// Method satisfies the Cmd interface by returning the RPC method.
func (cmd *NotifyNewTXsCmd) Method() string {
return "notifynewtxs"
}
// MarshalJSON returns the JSON encoding of cmd. Part of the Cmd interface.
func (cmd *NotifyNewTXsCmd) MarshalJSON() ([]byte, error) {
// Fill a RawCmd and marshal.
raw := btcjson.RawCmd{
Jsonrpc: "1.0",
Method: "notifynewtxs",
Id: cmd.id,
Params: []interface{}{
cmd.Addresses,
},
}
return json.Marshal(raw)
}
// UnmarshalJSON unmarshals the JSON encoding of cmd into cmd. Part of
// the Cmd interface.
func (cmd *NotifyNewTXsCmd) UnmarshalJSON(b []byte) error {
// Unmarshal into a RawCmd.
var r btcjson.RawCmd
if err := json.Unmarshal(b, &r); err != nil {
return err
}
newCmd, err := parseNotifyNewTXsCmd(&r)
if err != nil {
return err
}
concreteCmd, ok := newCmd.(*NotifyNewTXsCmd)
if !ok {
return btcjson.ErrInternal
}
*cmd = *concreteCmd
return nil
}
// NotifySpentCmd is a type handling custom marshaling and
// unmarshaling of notifyspent JSON websocket extension
// commands.
type NotifySpentCmd struct {
id interface{}
*btcwire.OutPoint
}
// Enforce that NotifySpentCmd satisifies the btcjson.Cmd interface.
var _ btcjson.Cmd = &NotifySpentCmd{}
// NewNotifySpentCmd creates a new RescanCmd.
func NewNotifySpentCmd(id interface{}, op *btcwire.OutPoint) *NotifySpentCmd {
return &NotifySpentCmd{
id: id,
OutPoint: op,
}
}
// parseNotifySpentCmd parses a NotifySpentCmd into a concrete type
// satisifying the btcjson.Cmd interface. This is used when registering
// the custom command with the btcjson parser.
func parseNotifySpentCmd(r *btcjson.RawCmd) (btcjson.Cmd, error) {
if len(r.Params) != 2 {
return nil, btcjson.ErrWrongNumberOfParams
}
hashStr, ok := r.Params[0].(string)
if !ok {
return nil, errors.New("first parameter must be a string")
}
hash, err := btcwire.NewShaHashFromStr(hashStr)
if err != nil {
return nil, errors.New("first parameter is not a valid " +
"hash string")
}
idx, ok := r.Params[1].(float64)
if !ok {
return nil, errors.New("second parameter is not a number")
}
if idx < 0 {
return nil, errors.New("second parameter cannot be negative")
}
cmd := NewNotifySpentCmd(r.Id, btcwire.NewOutPoint(hash, uint32(idx)))
return cmd, nil
}
// Id satisifies the Cmd interface by returning the ID of the command.
func (cmd *NotifySpentCmd) Id() interface{} {
return cmd.id
}
// Method satisfies the Cmd interface by returning the RPC method.
func (cmd *NotifySpentCmd) Method() string {
return "notifyspent"
}
// MarshalJSON returns the JSON encoding of cmd. Part of the Cmd interface.
func (cmd *NotifySpentCmd) MarshalJSON() ([]byte, error) {
// Fill a RawCmd and marshal.
raw := btcjson.RawCmd{
Jsonrpc: "1.0",
Method: "notifyspent",
Id: cmd.id,
Params: []interface{}{
cmd.OutPoint.Hash.String(),
cmd.OutPoint.Index,
},
}
return json.Marshal(raw)
}
// UnmarshalJSON unmarshals the JSON encoding of cmd into cmd. Part of
// the Cmd interface.
func (cmd *NotifySpentCmd) UnmarshalJSON(b []byte) error {
// Unmarshal into a RawCmd.
var r btcjson.RawCmd
if err := json.Unmarshal(b, &r); err != nil {
return err
}
newCmd, err := parseNotifySpentCmd(&r)
if err != nil {
return err
}
concreteCmd, ok := newCmd.(*NotifySpentCmd)
if !ok {
return btcjson.ErrInternal
}
*cmd = *concreteCmd
return nil
}