func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	c := cmd.(*soterjson.GetAddedNodeInfoCmd)
	peers := s.cfg.ConnMgr.PersistentPeers()
	if c.Node != nil {
		node := *c.Node
		found := false
		for i, peer := range peers {
			if peer.ToPeer().Addr() == node {
				peers = peers[i : i+1]
				found = true
			}
		}
		if !found {
			return nil, &soterjson.RPCError{
				Code:    soterjson.ErrRPCClientNodeNotAdded,
				Message: "Node has not been added",
			}
		}
	}
	if !c.DNS {
		results := make([]string, 0, len(peers))
		for _, peer := range peers {
			results = append(results, peer.ToPeer().Addr())
		}
		return results, nil
	}
	results := make([]*soterjson.GetAddedNodeInfoResult, 0, len(peers))
	for _, rpcPeer := range peers {
		peer := rpcPeer.ToPeer()
		var result soterjson.GetAddedNodeInfoResult
		result.AddedNode = peer.Addr()
		result.Connected = soterjson.Bool(peer.Connected())
		host, _, err := net.SplitHostPort(peer.Addr())
		if err != nil {
			host = peer.Addr()
		}
		var ipList []string
		switch {
		case net.ParseIP(host) != nil, strings.HasSuffix(host, ".onion"):
			ipList = make([]string, 1)
			ipList[0] = host
		default:
			ips, err := soterdLookup(host)
			if err != nil {
				ipList = make([]string, 1)
				ipList[0] = host
				break
			}
			ipList = make([]string, 0, len(ips))
			for _, ip := range ips {
				ipList = append(ipList, ip.String())
			}
		}
		addrs := make([]soterjson.GetAddedNodeInfoResultAddr, 0, len(ipList))
		for _, ip := range ipList {
			var addr soterjson.GetAddedNodeInfoResultAddr
			addr.Address = ip
			addr.Connected = "false"
			if ip == host && peer.Connected() {
				addr.Connected = directionString(peer.Inbound())
			}
			addrs = append(addrs, addr)
		}
		result.Addresses = &addrs
		results = append(results, &result)
	}
	return results, nil
}
handleGetAddedNodeInfo handles getaddednodeinfo commands.
package main
import (
	"bytes"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"math/big"
	"math/rand"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"github.com/btcsuite/websocket"
	"github.com/soteria-dag/soterd/blockdag"
	"github.com/soteria-dag/soterd/blockdag/indexers"
	"github.com/soteria-dag/soterd/chaincfg"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/database"
	"github.com/soteria-dag/soterd/mempool"
	"github.com/soteria-dag/soterd/mining/cpuminer"
	"github.com/soteria-dag/soterd/miningdag"
	"github.com/soteria-dag/soterd/peer"
	"github.com/soteria-dag/soterd/soterec"
	"github.com/soteria-dag/soterd/soterjson"
	"github.com/soteria-dag/soterd/soterutil"
	"github.com/soteria-dag/soterd/txscript"
	"github.com/soteria-dag/soterd/wire"
)
const (
	jsonrpcSemverString = "1.3.0"
	jsonrpcSemverMajor  = 1
	jsonrpcSemverMinor  = 3
	jsonrpcSemverPatch  = 0
)
const (
	rpcAuthTimeoutSeconds = 10
	uint256Size = 32
	gbtNonceRange = "00000000ffffffff"
	gbtRegenerateSeconds = 60
	maxProtocolVersion = 70002
)
var (
	gbtMutableFields = []string{
		"time", "transactions/add", "prevblock", "coinbase/append",
	}
	gbtCoinbaseAux = &soterjson.GetBlockTemplateResultAux{
		Flags: hex.EncodeToString(builderScript(txscript.
			NewScriptBuilder().
			AddData([]byte(miningdag.CoinbaseFlags)))),
	}
	gbtCapabilities = []string{"proposal"}
)
var (
	ErrRPCUnimplemented = &soterjson.RPCError{
		Code:    soterjson.ErrRPCUnimplemented,
		Message: "Command unimplemented",
	}
	ErrRPCNoWallet = &soterjson.RPCError{
		Code:    soterjson.ErrRPCNoWallet,
		Message: "This implementation does not implement wallet commands",
	}
)
type commandHandler func(*rpcServer, interface{}, <-chan struct{}) (interface{}, error)
var rpcHandlers map[string]commandHandler
var rpcHandlersBeforeInit = map[string]commandHandler{
	"addnode":               handleAddNode,
	"createrawtransaction":  handleCreateRawTransaction,
	"debuglevel":            handleDebugLevel,
	"decoderawtransaction":  handleDecodeRawTransaction,
	"decodescript":          handleDecodeScript,
	"estimatefee":        handleEstimateFee,
	"generate":           handleGenerate,
	"getaddednodeinfo":   handleGetAddedNodeInfo,
	"getaddrcache":       handleGetAddrCache,
	"getbestblock":       handleGetBestBlock,
	"getbestblockhash":   handleGetBestBlockHash,
	"getblock":           handleGetBlock,
	"getblockchaininfo":  handleGetBlockChainInfo,
	"getblockcount":      handleGetBlockCount,
	"getblockhash":       handleGetBlockHash,
	"getblockheader":     handleGetBlockHeader,
	"getblocktemplate":   handleGetBlockTemplate,
	"getblockmetrics":    handleGetBlockMetrics,
	"getcfilter":         handleGetCFilter,
	"getcfilterheader":   handleGetCFilterHeader,
	"getconnectioncount": handleGetConnectionCount,
	"getcurrentnet":      handleGetCurrentNet,
	"getdagcoloring":     handleGetDAGColoring,
	"getdagtips":         handleGetDAGTips,
	"getdifficulty":      handleGetDifficulty,
	"getgenerate":        handleGetGenerate,
	"gethashespersec":    handleGetHashesPerSec,
	"getheaders":         handleGetHeaders,
	"getinfo":            handleGetInfo,
	"getlistenaddrs":     handleGetListenAddrs,
	"getmempoolinfo":     handleGetMempoolInfo,
	"getmininginfo":         handleGetMiningInfo,
	"getnettotals":          handleGetNetTotals,
	"getnetworkhashps":      handleGetNetworkHashPS,
	"getpeerinfo":           handleGetPeerInfo,
	"getrawmempool":         handleGetRawMempool,
	"getrawtransaction":     handleGetRawTransaction,
	"gettxout":              handleGetTxOut,
	"help":                  handleHelp,
	"node":                  handleNode,
	"ping":                  handlePing,
	"renderdag":             handleRenderDag,
	"searchrawtransactions": handleSearchRawTransactions,
	"sendrawtransaction":    handleSendRawTransaction,
	"setgenerate":           handleSetGenerate,
	"stop":                  handleStop,
	"submitblock":           handleSubmitBlock,
	"uptime":                handleUptime,
	"validateaddress":       handleValidateAddress,
	"verifychain":           handleVerifyChain,
	"verifymessage":         handleVerifyMessage,
	"version":               handleVersion,
}
var rpcAskWallet = map[string]struct{}{
	"addmultisigaddress":     {},
	"backupwallet":           {},
	"createencryptedwallet":  {},
	"createmultisig":         {},
	"dumpprivkey":            {},
	"dumpwallet":             {},
	"encryptwallet":          {},
	"getaccount":             {},
	"getaccountaddress":      {},
	"getaddressesbyaccount":  {},
	"getbalance":             {},
	"getnewaddress":          {},
	"getrawchangeaddress":    {},
	"getreceivedbyaccount":   {},
	"getreceivedbyaddress":   {},
	"gettransaction":         {},
	"gettxoutsetinfo":        {},
	"getunconfirmedbalance":  {},
	"getwalletinfo":          {},
	"importprivkey":          {},
	"importwallet":           {},
	"keypoolrefill":          {},
	"listaccounts":           {},
	"listaddressgroupings":   {},
	"listlockunspent":        {},
	"listreceivedbyaccount":  {},
	"listreceivedbyaddress":  {},
	"listsinceblock":         {},
	"listtransactions":       {},
	"listunspent":            {},
	"lockunspent":            {},
	"move":                   {},
	"sendfrom":               {},
	"sendmany":               {},
	"sendtoaddress":          {},
	"setaccount":             {},
	"settxfee":               {},
	"signmessage":            {},
	"signrawtransaction":     {},
	"walletlock":             {},
	"walletpassphrase":       {},
	"walletpassphrasechange": {},
}
var rpcUnimplemented = map[string]struct{}{
	"estimatepriority": {},
	"getchaintips":     {},
	"getmempoolentry":  {},
	"getnetworkinfo":   {},
	"getwork":          {},
	"invalidateblock":  {},
	"preciousblock":    {},
	"reconsiderblock":  {},
}
var rpcLimited = map[string]struct{}{
	"loadtxfilter":          {},
	"notifyblocks":          {},
	"notifynewtransactions": {},
	"notifyreceived":        {},
	"notifyspent":           {},
	"rescan":                {},
	"rescanblocks":          {},
	"session":               {},
	"help": {},
	"createrawtransaction":  {},
	"decoderawtransaction":  {},
	"decodescript":          {},
	"estimatefee":           {},
	"getbestblock":          {},
	"getbestblockhash":      {},
	"getblock":              {},
	"getblockcount":         {},
	"getblockhash":          {},
	"getblockheader":        {},
	"getcfilter":            {},
	"getcfilterheader":      {},
	"getcurrentnet":         {},
	"getdifficulty":         {},
	"getheaders":            {},
	"getinfo":               {},
	"getnettotals":          {},
	"getnetworkhashps":      {},
	"getrawmempool":         {},
	"getrawtransaction":     {},
	"gettxout":              {},
	"searchrawtransactions": {},
	"sendrawtransaction":    {},
	"submitblock":           {},
	"uptime":                {},
	"validateaddress":       {},
	"verifymessage":         {},
	"version":               {},
}
func builderScript(builder *txscript.ScriptBuilder) []byte {
	script, err := builder.Script()
	if err != nil {
		panic(err)
	}
	return script
}
func internalRPCError(errStr, context string) *soterjson.RPCError {
	logStr := errStr
	if context != "" {
		logStr = context + ": " + errStr
	}
	rpcsLog.Error(logStr)
	return soterjson.NewRPCError(soterjson.ErrRPCInternal.Code, errStr)
}
func rpcDecodeHexError(gotHex string) *soterjson.RPCError {
	return soterjson.NewRPCError(soterjson.ErrRPCDecodeHexString,
		fmt.Sprintf("Argument must be hexadecimal string (not %q)",
			gotHex))
}
func rpcNoTxInfoError(txHash *chainhash.Hash) *soterjson.RPCError {
	return soterjson.NewRPCError(soterjson.ErrRPCNoTxInfo,
		fmt.Sprintf("No information available about transaction %v",
			txHash))
}
type gbtWorkState struct {
	sync.Mutex
	lastTxUpdate  time.Time
	lastGenerated time.Time
	prevHash      *chainhash.Hash
	minTimestamp  time.Time
	template      *miningdag.BlockTemplate
	notifyMap     map[chainhash.Hash]map[int64]chan struct{}
	timeSource    blockdag.MedianTimeSource
}
func newGbtWorkState(timeSource blockdag.MedianTimeSource) *gbtWorkState {
	return &gbtWorkState{
		notifyMap:  make(map[chainhash.Hash]map[int64]chan struct{}),
		timeSource: timeSource,
	}
}
func handleUnimplemented(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	return nil, ErrRPCUnimplemented
}
func handleAskWallet(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	return nil, ErrRPCNoWallet
}
func handleAddNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	c := cmd.(*soterjson.AddNodeCmd)
	addr := normalizeAddress(c.Addr, s.cfg.ChainParams.DefaultPort)
	var err error
	switch c.SubCmd {
	case "add":
		err = s.cfg.ConnMgr.Connect(addr, true)
	case "remove":
		err = s.cfg.ConnMgr.RemoveByAddr(addr)
	case "onetry":
		err = s.cfg.ConnMgr.Connect(addr, false)
	default:
		return nil, &soterjson.RPCError{
			Code:    soterjson.ErrRPCInvalidParameter,
			Message: "invalid subcommand for addnode",
		}
	}
	if err != nil {
		return nil, &soterjson.RPCError{
			Code:    soterjson.ErrRPCInvalidParameter,
			Message: err.Error(),
		}
	}
	return nil, nil
}
func handleNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	c := cmd.(*soterjson.NodeCmd)
	var addr string
	var nodeID uint64
	var errN, err error
	params := s.cfg.ChainParams
	switch c.SubCmd {
	case "disconnect":
		if nodeID, errN = strconv.ParseUint(c.Target, 10, 32); errN == nil {
			err = s.cfg.ConnMgr.DisconnectByID(int32(nodeID))
		} else {
			if _, _, errP := net.SplitHostPort(c.Target); errP == nil || net.ParseIP(c.Target) != nil {
				addr = normalizeAddress(c.Target, params.DefaultPort)
				err = s.cfg.ConnMgr.DisconnectByAddr(addr)
			} else {
				return nil, &soterjson.RPCError{
					Code:    soterjson.ErrRPCInvalidParameter,
					Message: "invalid address or node ID",
				}
			}
		}
		if err != nil && peerExists(s.cfg.ConnMgr, addr, int32(nodeID)) {
			return nil, &soterjson.RPCError{
				Code:    soterjson.ErrRPCMisc,
				Message: "can't disconnect a permanent peer, use remove",
			}
		}
	case "remove":
		if nodeID, errN = strconv.ParseUint(c.Target, 10, 32); errN == nil {
			err = s.cfg.ConnMgr.RemoveByID(int32(nodeID))
		} else {
			if _, _, errP := net.SplitHostPort(c.Target); errP == nil || net.ParseIP(c.Target) != nil {
				addr = normalizeAddress(c.Target, params.DefaultPort)
				err = s.cfg.ConnMgr.RemoveByAddr(addr)
			} else {
				return nil, &soterjson.RPCError{
					Code:    soterjson.ErrRPCInvalidParameter,
					Message: "invalid address or node ID",
				}
			}
		}
		if err != nil && peerExists(s.cfg.ConnMgr, addr, int32(nodeID)) {
			return nil, &soterjson.RPCError{
				Code:    soterjson.ErrRPCMisc,
				Message: "can't remove a temporary peer, use disconnect",
			}
		}
	case "connect":
		addr = normalizeAddress(c.Target, params.DefaultPort)
		subCmd := "temp"
		if c.ConnectSubCmd != nil {
			subCmd = *c.ConnectSubCmd
		}
		switch subCmd {
		case "perm", "temp":
			err = s.cfg.ConnMgr.Connect(addr, subCmd == "perm")
		default:
			return nil, &soterjson.RPCError{
				Code:    soterjson.ErrRPCInvalidParameter,
				Message: "invalid subcommand for node connect",
			}
		}
	default:
		return nil, &soterjson.RPCError{
			Code:    soterjson.ErrRPCInvalidParameter,
			Message: "invalid subcommand for node",
		}
	}
	if err != nil {
		return nil, &soterjson.RPCError{
			Code:    soterjson.ErrRPCInvalidParameter,
			Message: err.Error(),
		}
	}
	return nil, nil
}
func peerExists(connMgr rpcserverConnManager, addr string, nodeID int32) bool {
	for _, p := range connMgr.ConnectedPeers() {
		if p.ToPeer().ID() == nodeID || p.ToPeer().Addr() == addr {
			return true
		}
	}
	return false
}
func messageToHex(msg wire.Message) (string, error) {
	var buf bytes.Buffer
	if err := msg.SotoEncode(&buf, maxProtocolVersion, wire.WitnessEncoding); err != nil {
		context := fmt.Sprintf("Failed to encode msg of type %T", msg)
		return "", internalRPCError(err.Error(), context)
	}
	return hex.EncodeToString(buf.Bytes()), nil
}
func handleCreateRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	c := cmd.(*soterjson.CreateRawTransactionCmd)
	if c.LockTime != nil &&
		(*c.LockTime < 0 || *c.LockTime > int64(wire.MaxTxInSequenceNum)) {
		return nil, &soterjson.RPCError{
			Code:    soterjson.ErrRPCInvalidParameter,
			Message: "Locktime out of range",
		}
	}
	mtx := wire.NewMsgTx(wire.TxVersion)
	for _, input := range c.Inputs {
		txHash, err := chainhash.NewHashFromStr(input.Txid)
		if err != nil {
			return nil, rpcDecodeHexError(input.Txid)
		}
		prevOut := wire.NewOutPoint(txHash, input.Vout)
		txIn := wire.NewTxIn(prevOut, []byte{}, nil)
		if c.LockTime != nil && *c.LockTime != 0 {
			txIn.Sequence = wire.MaxTxInSequenceNum - 1
		}
		mtx.AddTxIn(txIn)
	}
	params := s.cfg.ChainParams
	for encodedAddr, amount := range c.Amounts {
		if amount <= 0 || amount > soterutil.MaxNanoSoter {
			return nil, &soterjson.RPCError{
				Code:    soterjson.ErrRPCType,
				Message: "Invalid amount",
			}
		}
		addr, err := soterutil.DecodeAddress(encodedAddr, params)
		if err != nil {
			return nil, &soterjson.RPCError{
				Code:    soterjson.ErrRPCInvalidAddressOrKey,
				Message: "Invalid address or key: " + err.Error(),
			}
		}
		switch addr.(type) {
		case *soterutil.AddressPubKeyHash:
		case *soterutil.AddressScriptHash:
		default:
			return nil, &soterjson.RPCError{
				Code:    soterjson.ErrRPCInvalidAddressOrKey,
				Message: "Invalid address or key",
			}
		}
		if !addr.IsForNet(params) {
			return nil, &soterjson.RPCError{
				Code: soterjson.ErrRPCInvalidAddressOrKey,
				Message: "Invalid address: " + encodedAddr +
					" is for the wrong network",
			}
		}
		pkScript, err := txscript.PayToAddrScript(addr)
		if err != nil {
			context := "Failed to generate pay-to-address script"
			return nil, internalRPCError(err.Error(), context)
		}
		nanoSoter, err := soterutil.NewAmount(amount)
		if err != nil {
			context := "Failed to convert amount"
			return nil, internalRPCError(err.Error(), context)
		}
		txOut := wire.NewTxOut(int64(nanoSoter), pkScript)
		mtx.AddTxOut(txOut)
	}
	if c.LockTime != nil {
		mtx.LockTime = uint32(*c.LockTime)
	}
	mtxHex, err := messageToHex(mtx)
	if err != nil {
		return nil, err
	}
	return mtxHex, nil
}
func handleDebugLevel(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	c := cmd.(*soterjson.DebugLevelCmd)
	if c.LevelSpec == "show" {
		return fmt.Sprintf("Supported subsystems %v",
			supportedSubsystems()), nil
	}
	err := parseAndSetDebugLevels(c.LevelSpec)
	if err != nil {
		return nil, &soterjson.RPCError{
			Code:    soterjson.ErrRPCInvalidParams.Code,
			Message: err.Error(),
		}
	}
	return "Done.", nil
}
func witnessToHex(witness wire.TxWitness) []string {
	if len(witness) == 0 {
		return nil
	}
	result := make([]string, 0, len(witness))
	for _, wit := range witness {
		result = append(result, hex.EncodeToString(wit))
	}
	return result
}
func createVinList(mtx *wire.MsgTx) []soterjson.Vin {
	vinList := make([]soterjson.Vin, len(mtx.TxIn))
	if blockdag.IsCoinBaseTx(mtx) {
		txIn := mtx.TxIn[0]
		vinList[0].Coinbase = hex.EncodeToString(txIn.SignatureScript)
		vinList[0].Sequence = txIn.Sequence
		vinList[0].Witness = witnessToHex(txIn.Witness)
		return vinList
	}
	for i, txIn := range mtx.TxIn {
		disbuf, _ := txscript.DisasmString(txIn.SignatureScript)
		vinEntry := &vinList[i]
		vinEntry.Txid = txIn.PreviousOutPoint.Hash.String()
		vinEntry.Vout = txIn.PreviousOutPoint.Index
		vinEntry.Sequence = txIn.Sequence
		vinEntry.ScriptSig = &soterjson.ScriptSig{
			Asm: disbuf,
			Hex: hex.EncodeToString(txIn.SignatureScript),
		}
		if mtx.HasWitness() {
			vinEntry.Witness = witnessToHex(txIn.Witness)
		}
	}
	return vinList
}
func createVoutList(mtx *wire.MsgTx, chainParams *chaincfg.Params, filterAddrMap map[string]struct{}) []soterjson.Vout {
	voutList := make([]soterjson.Vout, 0, len(mtx.TxOut))
	for i, v := range mtx.TxOut {
		disbuf, _ := txscript.DisasmString(v.PkScript)
		scriptClass, addrs, reqSigs, _ := txscript.ExtractPkScriptAddrs(
			v.PkScript, chainParams)
		passesFilter := len(filterAddrMap) == 0
		encodedAddrs := make([]string, len(addrs))
		for j, addr := range addrs {
			encodedAddr := addr.EncodeAddress()
			encodedAddrs[j] = encodedAddr
			if passesFilter {
				continue
			}
			if _, exists := filterAddrMap[encodedAddr]; exists {
				passesFilter = true
			}
		}
		if !passesFilter {
			continue
		}
		var vout soterjson.Vout
		vout.N = uint32(i)
		vout.Value = soterutil.Amount(v.Value).ToSOTER()
		vout.ScriptPubKey.Addresses = encodedAddrs
		vout.ScriptPubKey.Asm = disbuf
		vout.ScriptPubKey.Hex = hex.EncodeToString(v.PkScript)
		vout.ScriptPubKey.Type = scriptClass.String()
		vout.ScriptPubKey.ReqSigs = int32(reqSigs)
		voutList = append(voutList, vout)
	}
	return voutList
}
func createTxRawResult(chainParams *chaincfg.Params, mtx *wire.MsgTx,
	txHash string, blkHeader *wire.BlockHeader, blkHash string,
	blkHeight int32, chainHeight int32) (*soterjson.TxRawResult, error) {
	mtxHex, err := messageToHex(mtx)
	if err != nil {
		return nil, err
	}
	txReply := &soterjson.TxRawResult{
		Hex:      mtxHex,
		Txid:     txHash,
		Hash:     mtx.WitnessHash().String(),
		Size:     int32(mtx.SerializeSize()),
		Vsize:    int32(mempool.GetTxVirtualSize(soterutil.NewTx(mtx))),
		Vin:      createVinList(mtx),
		Vout:     createVoutList(mtx, chainParams, nil),
		Version:  mtx.Version,
		LockTime: mtx.LockTime,
	}
	if blkHeader != nil {
		txReply.Time = blkHeader.Timestamp.Unix()
		txReply.Blocktime = blkHeader.Timestamp.Unix()
		txReply.BlockHash = blkHash
		txReply.Confirmations = uint64(1 + chainHeight - blkHeight)
	}
	return txReply, nil
}
func handleDecodeRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	c := cmd.(*soterjson.DecodeRawTransactionCmd)
	hexStr := c.HexTx
	if len(hexStr)%2 != 0 {
		hexStr = "0" + hexStr
	}
	serializedTx, err := hex.DecodeString(hexStr)
	if err != nil {
		return nil, rpcDecodeHexError(hexStr)
	}
	var mtx wire.MsgTx
	err = mtx.Deserialize(bytes.NewReader(serializedTx))
	if err != nil {
		return nil, &soterjson.RPCError{
			Code:    soterjson.ErrRPCDeserialization,
			Message: "TX decode failed: " + err.Error(),
		}
	}
	txReply := soterjson.TxRawDecodeResult{
		Txid:     mtx.TxHash().String(),
		Version:  mtx.Version,
		Locktime: mtx.LockTime,
		Vin:      createVinList(&mtx),
		Vout:     createVoutList(&mtx, s.cfg.ChainParams, nil),
	}
	return txReply, nil
}
func handleDecodeScript(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	c := cmd.(*soterjson.DecodeScriptCmd)
	hexStr := c.HexScript
	if len(hexStr)%2 != 0 {
		hexStr = "0" + hexStr
	}
	script, err := hex.DecodeString(hexStr)
	if err != nil {
		return nil, rpcDecodeHexError(hexStr)
	}
	disbuf, _ := txscript.DisasmString(script)
	scriptClass, addrs, reqSigs, _ := txscript.ExtractPkScriptAddrs(script,
		s.cfg.ChainParams)
	addresses := make([]string, len(addrs))
	for i, addr := range addrs {
		addresses[i] = addr.EncodeAddress()
	}
	p2sh, err := soterutil.NewAddressScriptHash(script, s.cfg.ChainParams)
	if err != nil {
		context := "Failed to convert script to pay-to-script-hash"
		return nil, internalRPCError(err.Error(), context)
	}
	reply := soterjson.DecodeScriptResult{
		Asm:       disbuf,
		ReqSigs:   int32(reqSigs),
		Type:      scriptClass.String(),
		Addresses: addresses,
	}
	if scriptClass != txscript.ScriptHashTy {
		reply.P2sh = p2sh.EncodeAddress()
	}
	return reply, nil
}
func handleEstimateFee(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	c := cmd.(*soterjson.EstimateFeeCmd)
	if s.cfg.FeeEstimator == nil {
		return nil, errors.New("Fee estimation disabled")
	}
	if c.NumBlocks <= 0 {
		return -1.0, errors.New("Parameter NumBlocks must be positive")
	}
	feeRate, err := s.cfg.FeeEstimator.EstimateFee(uint32(c.NumBlocks))
	if err != nil {
		return -1.0, err
	}
	return float64(feeRate), nil
}
func handleGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
	if len(cfg.miningAddrs) == 0 {
		return nil, &soterjson.RPCError{
			Code: soterjson.ErrRPCInternal.Code,
			Message: "No payment addresses specified " +
				"via --miningaddr",
		}
	}
	if !s.cfg.ChainParams.GenerateSupported {
		return nil, &soterjson.RPCError{
			Code: soterjson.ErrRPCDifficulty,
			Message: fmt.Sprintf("No support for `generate` on "+
				"the current network, %s, as it's unlikely to "+
				"be possible to mine a block with the CPU.",
				s.cfg.ChainParams.Net),
		}
	}
	c := cmd.(*soterjson.GenerateCmd)
	if c.NumBlocks == 0 {
		return nil, &soterjson.RPCError{
			Code:    soterjson.ErrRPCInternal.Code,
			Message: "Please request a nonzero number of blocks to generate.",
		}
	}
	reply := make([]string, c.NumBlocks)
	blockHashes, err := s.cfg.CPUMiner.GenerateNBlocks(c.NumBlocks)
	if err != nil {
		return nil, &soterjson.RPCError{
			Code:    soterjson.ErrRPCInternal.Code,
			Message: err.Error(),
		}
	}
	for i, hash := range blockHashes {
		reply[i] = hash.String()
	}
	return reply, nil
}