func newOutPointFromWire(op *wire.OutPoint) soterjson.OutPoint {
	return soterjson.OutPoint{
		Hash:  op.Hash.String(),
		Index: op.Index,
	}
}
newOutPointFromWire constructs the soterjson representation of a transaction
outpoint from the wire type.
package rpcclient
import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"time"
	"github.com/soteria-dag/soterd/soterjson"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/wire"
	"github.com/soteria-dag/soterd/soterutil"
)
var (
	ErrWebsocketsRequired = errors.New("a websocket connection is required " +
		"to use this feature")
)
type notificationState struct {
	notifyBlocks       bool
	notifyNewTx        bool
	notifyNewTxVerbose bool
	notifyReceived     map[string]struct{}
	notifySpent        map[soterjson.OutPoint]struct{}
}
func (s *notificationState) Copy() *notificationState {
	var stateCopy notificationState
	stateCopy.notifyBlocks = s.notifyBlocks
	stateCopy.notifyNewTx = s.notifyNewTx
	stateCopy.notifyNewTxVerbose = s.notifyNewTxVerbose
	stateCopy.notifyReceived = make(map[string]struct{})
	for addr := range s.notifyReceived {
		stateCopy.notifyReceived[addr] = struct{}{}
	}
	stateCopy.notifySpent = make(map[soterjson.OutPoint]struct{})
	for op := range s.notifySpent {
		stateCopy.notifySpent[op] = struct{}{}
	}
	return &stateCopy
}
func newNotificationState() *notificationState {
	return &notificationState{
		notifyReceived: make(map[string]struct{}),
		notifySpent:    make(map[soterjson.OutPoint]struct{}),
	}
}
func newNilFutureResult() chan *response {
	responseChan := make(chan *response, 1)
	responseChan <- &response{result: nil, err: nil}
	return responseChan
}
type NotificationHandlers struct {
	OnClientConnected func()
	OnBlockConnected func(hash *chainhash.Hash, height int32, t time.Time)
	OnFilteredBlockConnected func(height int32, header *wire.BlockHeader,
		txs []*soterutil.Tx)
	OnBlockDisconnected func(hash *chainhash.Hash, height int32, t time.Time)
	OnFilteredBlockDisconnected func(height int32, header *wire.BlockHeader)
	OnRecvTx func(transaction *soterutil.Tx, details *soterjson.BlockDetails)
	OnRedeemingTx func(transaction *soterutil.Tx, details *soterjson.BlockDetails)
	OnRelevantTxAccepted func(transaction []byte)
	OnRescanFinished func(hash *chainhash.Hash, height int32, blkTime time.Time)
	OnRescanProgress func(hash *chainhash.Hash, height int32, blkTime time.Time)
	OnTxAccepted func(hash *chainhash.Hash, amount soterutil.Amount)
	OnTxAcceptedVerbose func(txDetails *soterjson.TxRawResult)
	OnSoterdConnected func(connected bool)
	OnAccountBalance func(account string, balance soterutil.Amount, confirmed bool)
	OnWalletLockState func(locked bool)
	OnUnknownNotification func(method string, params []json.RawMessage)
}
func (c *Client) handleNotification(ntfn *rawNotification) {
	if c.ntfnHandlers == nil {
		return
	}
	switch ntfn.Method {
	case soterjson.BlockConnectedNtfnMethod:
		if c.ntfnHandlers.OnBlockConnected == nil {
			return
		}
		blockHash, blockHeight, blockTime, err := parseChainNtfnParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid block connected "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnBlockConnected(blockHash, blockHeight, blockTime)
	case soterjson.FilteredBlockConnectedNtfnMethod:
		if c.ntfnHandlers.OnFilteredBlockConnected == nil {
			return
		}
		blockHeight, blockHeader, transactions, err :=
			parseFilteredBlockConnectedParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid filtered block "+
				"connected notification: %v", err)
			return
		}
		c.ntfnHandlers.OnFilteredBlockConnected(blockHeight,
			blockHeader, transactions)
	case soterjson.BlockDisconnectedNtfnMethod:
		if c.ntfnHandlers.OnBlockDisconnected == nil {
			return
		}
		blockHash, blockHeight, blockTime, err := parseChainNtfnParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid block connected "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnBlockDisconnected(blockHash, blockHeight, blockTime)
	case soterjson.FilteredBlockDisconnectedNtfnMethod:
		if c.ntfnHandlers.OnFilteredBlockDisconnected == nil {
			return
		}
		blockHeight, blockHeader, err :=
			parseFilteredBlockDisconnectedParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid filtered block "+
				"disconnected notification: %v", err)
			return
		}
		c.ntfnHandlers.OnFilteredBlockDisconnected(blockHeight,
			blockHeader)
	case soterjson.RecvTxNtfnMethod:
		if c.ntfnHandlers.OnRecvTx == nil {
			return
		}
		tx, block, err := parseChainTxNtfnParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid recvtx notification: %v",
				err)
			return
		}
		c.ntfnHandlers.OnRecvTx(tx, block)
	case soterjson.RedeemingTxNtfnMethod:
		if c.ntfnHandlers.OnRedeemingTx == nil {
			return
		}
		tx, block, err := parseChainTxNtfnParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid redeemingtx "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnRedeemingTx(tx, block)
	case soterjson.RelevantTxAcceptedNtfnMethod:
		if c.ntfnHandlers.OnRelevantTxAccepted == nil {
			return
		}
		transaction, err := parseRelevantTxAcceptedParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid relevanttxaccepted "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnRelevantTxAccepted(transaction)
	case soterjson.RescanFinishedNtfnMethod:
		if c.ntfnHandlers.OnRescanFinished == nil {
			return
		}
		hash, height, blkTime, err := parseRescanProgressParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid rescanfinished "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnRescanFinished(hash, height, blkTime)
	case soterjson.RescanProgressNtfnMethod:
		if c.ntfnHandlers.OnRescanProgress == nil {
			return
		}
		hash, height, blkTime, err := parseRescanProgressParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid rescanprogress "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnRescanProgress(hash, height, blkTime)
	case soterjson.TxAcceptedNtfnMethod:
		if c.ntfnHandlers.OnTxAccepted == nil {
			return
		}
		hash, amt, err := parseTxAcceptedNtfnParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid tx accepted "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnTxAccepted(hash, amt)
	case soterjson.TxAcceptedVerboseNtfnMethod:
		if c.ntfnHandlers.OnTxAcceptedVerbose == nil {
			return
		}
		rawTx, err := parseTxAcceptedVerboseNtfnParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid tx accepted verbose "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnTxAcceptedVerbose(rawTx)
	case soterjson.SoterdConnectedNtfnMethod:
		if c.ntfnHandlers.OnSoterdConnected == nil {
			return
		}
		connected, err := parseSoterdConnectedNtfnParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid soterd connected "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnSoterdConnected(connected)
	case soterjson.AccountBalanceNtfnMethod:
		if c.ntfnHandlers.OnAccountBalance == nil {
			return
		}
		account, bal, conf, err := parseAccountBalanceNtfnParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid account balance "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnAccountBalance(account, bal, conf)
	case soterjson.WalletLockStateNtfnMethod:
		if c.ntfnHandlers.OnWalletLockState == nil {
			return
		}
		_, locked, err := parseWalletLockStateNtfnParams(ntfn.Params)
		if err != nil {
			log.Warnf("Received invalid wallet lock state "+
				"notification: %v", err)
			return
		}
		c.ntfnHandlers.OnWalletLockState(locked)
	default:
		if c.ntfnHandlers.OnUnknownNotification == nil {
			return
		}
		c.ntfnHandlers.OnUnknownNotification(ntfn.Method, ntfn.Params)
	}
}
type wrongNumParams int
func (e wrongNumParams) Error() string {
	return fmt.Sprintf("wrong number of parameters (%d)", e)
}
func parseChainNtfnParams(params []json.RawMessage) (*chainhash.Hash,
	int32, time.Time, error) {
	if len(params) != 3 {
		return nil, 0, time.Time{}, wrongNumParams(len(params))
	}
	var blockHashStr string
	err := json.Unmarshal(params[0], &blockHashStr)
	if err != nil {
		return nil, 0, time.Time{}, err
	}
	var blockHeight int32
	err = json.Unmarshal(params[1], &blockHeight)
	if err != nil {
		return nil, 0, time.Time{}, err
	}
	var blockTimeUnix int64
	err = json.Unmarshal(params[2], &blockTimeUnix)
	if err != nil {
		return nil, 0, time.Time{}, err
	}
	blockHash, err := chainhash.NewHashFromStr(blockHashStr)
	if err != nil {
		return nil, 0, time.Time{}, err
	}
	blockTime := time.Unix(blockTimeUnix, 0)
	return blockHash, blockHeight, blockTime, nil
}
func parseFilteredBlockConnectedParams(params []json.RawMessage) (int32,
	*wire.BlockHeader, []*soterutil.Tx, error) {
	if len(params) < 3 {
		return 0, nil, nil, wrongNumParams(len(params))
	}
	var blockHeight int32
	err := json.Unmarshal(params[0], &blockHeight)
	if err != nil {
		return 0, nil, nil, err
	}
	blockHeaderBytes, err := parseHexParam(params[1])
	if err != nil {
		return 0, nil, nil, err
	}
	var blockHeader wire.BlockHeader
	err = blockHeader.Deserialize(bytes.NewReader(blockHeaderBytes))
	if err != nil {
		return 0, nil, nil, err
	}
	var hexTransactions []string
	err = json.Unmarshal(params[2], &hexTransactions)
	if err != nil {
		return 0, nil, nil, err
	}
	transactions := make([]*soterutil.Tx, len(hexTransactions))
	for i, hexTx := range hexTransactions {
		transaction, err := hex.DecodeString(hexTx)
		if err != nil {
			return 0, nil, nil, err
		}
		transactions[i], err = soterutil.NewTxFromBytes(transaction)
		if err != nil {
			return 0, nil, nil, err
		}
	}
	return blockHeight, &blockHeader, transactions, nil
}
func parseFilteredBlockDisconnectedParams(params []json.RawMessage) (int32,
	*wire.BlockHeader, error) {
	if len(params) < 2 {
		return 0, nil, wrongNumParams(len(params))
	}
	var blockHeight int32
	err := json.Unmarshal(params[0], &blockHeight)
	if err != nil {
		return 0, nil, err
	}
	blockHeaderBytes, err := parseHexParam(params[1])
	if err != nil {
		return 0, nil, err
	}
	var blockHeader wire.BlockHeader
	err = blockHeader.Deserialize(bytes.NewReader(blockHeaderBytes))
	if err != nil {
		return 0, nil, err
	}
	return blockHeight, &blockHeader, nil
}
func parseHexParam(param json.RawMessage) ([]byte, error) {
	var s string
	err := json.Unmarshal(param, &s)
	if err != nil {
		return nil, err
	}
	return hex.DecodeString(s)
}
func parseRelevantTxAcceptedParams(params []json.RawMessage) (transaction []byte, err error) {
	if len(params) < 1 {
		return nil, wrongNumParams(len(params))
	}
	return parseHexParam(params[0])
}
func parseChainTxNtfnParams(params []json.RawMessage) (*soterutil.Tx,
	*soterjson.BlockDetails, error) {
	if len(params) == 0 || len(params) > 2 {
		return nil, nil, wrongNumParams(len(params))
	}
	var txHex string
	err := json.Unmarshal(params[0], &txHex)
	if err != nil {
		return nil, nil, err
	}
	var block *soterjson.BlockDetails
	if len(params) > 1 {
		err = json.Unmarshal(params[1], &block)
		if err != nil {
			return nil, nil, err
		}
	}
	serializedTx, err := hex.DecodeString(txHex)
	if err != nil {
		return nil, nil, err
	}
	var msgTx wire.MsgTx
	err = msgTx.Deserialize(bytes.NewReader(serializedTx))
	if err != nil {
		return nil, nil, err
	}
	return soterutil.NewTx(&msgTx), block, nil
}
func parseRescanProgressParams(params []json.RawMessage) (*chainhash.Hash, int32, time.Time, error) {
	if len(params) != 3 {
		return nil, 0, time.Time{}, wrongNumParams(len(params))
	}
	var hashStr string
	err := json.Unmarshal(params[0], &hashStr)
	if err != nil {
		return nil, 0, time.Time{}, err
	}
	var height int32
	err = json.Unmarshal(params[1], &height)
	if err != nil {
		return nil, 0, time.Time{}, err
	}
	var blkTime int64
	err = json.Unmarshal(params[2], &blkTime)
	if err != nil {
		return nil, 0, time.Time{}, err
	}
	hash, err := chainhash.NewHashFromStr(hashStr)
	if err != nil {
		return nil, 0, time.Time{}, err
	}
	return hash, height, time.Unix(blkTime, 0), nil
}
func parseTxAcceptedNtfnParams(params []json.RawMessage) (*chainhash.Hash,
	soterutil.Amount, error) {
	if len(params) != 2 {
		return nil, 0, wrongNumParams(len(params))
	}
	var txHashStr string
	err := json.Unmarshal(params[0], &txHashStr)
	if err != nil {
		return nil, 0, err
	}
	var famt float64
	err = json.Unmarshal(params[1], &famt)
	if err != nil {
		return nil, 0, err
	}
	amt, err := soterutil.NewAmount(famt)
	if err != nil {
		return nil, 0, err
	}
	txHash, err := chainhash.NewHashFromStr(txHashStr)
	if err != nil {
		return nil, 0, err
	}
	return txHash, amt, nil
}
func parseTxAcceptedVerboseNtfnParams(params []json.RawMessage) (*soterjson.TxRawResult,
	error) {
	if len(params) != 1 {
		return nil, wrongNumParams(len(params))
	}
	var rawTx soterjson.TxRawResult
	err := json.Unmarshal(params[0], &rawTx)
	if err != nil {
		return nil, err
	}
	return &rawTx, nil
}
func parseSoterdConnectedNtfnParams(params []json.RawMessage) (bool, error) {
	if len(params) != 1 {
		return false, wrongNumParams(len(params))
	}
	var connected bool
	err := json.Unmarshal(params[0], &connected)
	if err != nil {
		return false, err
	}
	return connected, nil
}
func parseAccountBalanceNtfnParams(params []json.RawMessage) (account string,
	balance soterutil.Amount, confirmed bool, err error) {
	if len(params) != 3 {
		return "", 0, false, wrongNumParams(len(params))
	}
	err = json.Unmarshal(params[0], &account)
	if err != nil {
		return "", 0, false, err
	}
	var fbal float64
	err = json.Unmarshal(params[1], &fbal)
	if err != nil {
		return "", 0, false, err
	}
	err = json.Unmarshal(params[2], &confirmed)
	if err != nil {
		return "", 0, false, err
	}
	bal, err := soterutil.NewAmount(fbal)
	if err != nil {
		return "", 0, false, err
	}
	return account, bal, confirmed, nil
}
func parseWalletLockStateNtfnParams(params []json.RawMessage) (account string,
	locked bool, err error) {
	if len(params) != 2 {
		return "", false, wrongNumParams(len(params))
	}
	err = json.Unmarshal(params[0], &account)
	if err != nil {
		return "", false, err
	}
	err = json.Unmarshal(params[1], &locked)
	if err != nil {
		return "", false, err
	}
	return account, locked, nil
}
type FutureNotifyBlocksResult chan *response
func (r FutureNotifyBlocksResult) Receive() error {
	_, err := receiveFuture(r)
	return err
}
func (c *Client) NotifyBlocksAsync() FutureNotifyBlocksResult {
	if c.config.HTTPPostMode {
		return newFutureError(ErrWebsocketsRequired)
	}
	if c.ntfnHandlers == nil {
		return newNilFutureResult()
	}
	cmd := soterjson.NewNotifyBlocksCmd()
	return c.sendCmd(cmd)
}
func (c *Client) NotifyBlocks() error {
	return c.NotifyBlocksAsync().Receive()
}
type FutureNotifySpentResult chan *response
func (r FutureNotifySpentResult) Receive() error {
	_, err := receiveFuture(r)
	return err
}
func (c *Client) notifySpentInternal(outpoints []soterjson.OutPoint) FutureNotifySpentResult {
	if c.config.HTTPPostMode {
		return newFutureError(ErrWebsocketsRequired)
	}
	if c.ntfnHandlers == nil {
		return newNilFutureResult()
	}
	cmd := soterjson.NewNotifySpentCmd(outpoints)
	return c.sendCmd(cmd)
}