518 lines
13 KiB
Go
518 lines
13 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"sort"
|
|
"text/template"
|
|
|
|
"github.com/p9c/p9/pkg/log"
|
|
)
|
|
|
|
type handler struct {
|
|
Method, Handler, HandlerWithChain, Cmd, ResType string
|
|
}
|
|
|
|
type handlersT []handler
|
|
|
|
func (h handlersT) Len() int {
|
|
return len(h)
|
|
}
|
|
|
|
func (h handlersT) Less(i, j int) bool {
|
|
return h[i].Method < h[j].Method
|
|
}
|
|
|
|
func (h handlersT) Swap(i, j int) {
|
|
h[i], h[j] = h[j], h[i]
|
|
}
|
|
|
|
func main() {
|
|
log.SetLogLevel("trace")
|
|
if fd, e := os.Create("rpchandlers.go"); E.Chk(e) {
|
|
} else {
|
|
defer fd.Close()
|
|
t := template.Must(template.New("noderpc").Parse(NodeRPCHandlerTpl))
|
|
sort.Sort(handlers)
|
|
if e = t.Execute(fd, handlers); E.Chk(e) {
|
|
}
|
|
}
|
|
}
|
|
|
|
const (
|
|
RPCMapName = "RPCHandlers"
|
|
Worker = "CAPI"
|
|
)
|
|
|
|
var NodeRPCHandlerTpl = `// generated by go run ./genapi/.; DO NOT EDIT
|
|
//
|
|
`+`//go:generate go run ./genapi/.
|
|
|
|
package wallet
|
|
|
|
import (
|
|
"io"
|
|
"net/rpc"
|
|
"time"
|
|
|
|
"github.com/p9c/p9/pkg/qu"
|
|
|
|
"github.com/p9c/p9/pkg/btcjson"
|
|
"github.com/p9c/p9/pkg/chainclient"
|
|
)
|
|
|
|
// API stores the channel, parameters and result values from calls via the channel
|
|
type API struct {
|
|
Ch interface{}
|
|
Params interface{}
|
|
Result interface{}
|
|
}
|
|
|
|
// CAPI is the central structure for configuration and access to a net/rpc API access endpoint for this RPC API
|
|
type CAPI struct {
|
|
Timeout time.Duration
|
|
quit qu.C
|
|
}
|
|
|
|
// NewCAPI returns a new CAPI
|
|
func NewCAPI(quit qu.C, timeout ...time.Duration) (c *CAPI) {
|
|
c = &CAPI{quit: quit}
|
|
if len(timeout)>0 {
|
|
c.Timeout = timeout[0]
|
|
} else {
|
|
c.Timeout = time.Second * 5
|
|
}
|
|
return
|
|
}
|
|
|
|
// CAPIClient is a wrapper around RPC calls
|
|
type CAPIClient struct {
|
|
*rpc.Client
|
|
}
|
|
|
|
// NewCAPIClient creates a new client for a kopach_worker. Note that any kind of connection can be used here,
|
|
// other than the StdConn
|
|
func NewCAPIClient(conn io.ReadWriteCloser) *CAPIClient {
|
|
return &CAPIClient{rpc.NewClient(conn)}
|
|
}
|
|
|
|
type (
|
|
// None means no parameters it is not checked so it can be nil
|
|
None struct{} {{range .}}
|
|
// {{.Handler}}Res is the result from a call to {{.Handler}}
|
|
{{.Handler}}Res struct { Res *{{.ResType}}; e error }{{end}}
|
|
)
|
|
|
|
// RequestHandler is a handler function to handle an unmarshaled and parsed request into a marshalable response. If the
|
|
// error is a *json.RPCError or any of the above special error classes, the server will respond with the JSON-RPC
|
|
// appropriate error code. All other errors use the wallet catch-all error code, json.ErrRPCWallet.
|
|
type RequestHandler func(interface{}, *Wallet,
|
|
...*chainclient.RPCClient) (interface{}, error)
|
|
|
|
// ` + RPCMapName + ` is all of the RPC calls available
|
|
//
|
|
// - Handler is the handler function
|
|
//
|
|
// - Call is a channel carrying a struct containing parameters and error that is listened to in RunAPI to dispatch the
|
|
// calls
|
|
//
|
|
// - Result is a bundle of command parameters and a channel that the result will be sent back on
|
|
//
|
|
// Get and save the Result function's return, and you can then call the call functions check, result and wait functions
|
|
// for asynchronous and synchronous calls to RPC functions
|
|
var ` + RPCMapName + ` = map[string]struct {
|
|
Handler RequestHandler
|
|
// Function variables cannot be compared against anything but nil, so use a boolean to record whether help
|
|
// generation is necessary. This is used by the tests to ensure that help can be generated for every implemented
|
|
// method.
|
|
//
|
|
// A single map and this bool is here is used rather than several maps for the unimplemented handlers so every
|
|
// method has exactly one handler function.
|
|
//
|
|
// The Return field returns a new channel of the type returned by this function. This makes it possible to use this
|
|
// for callers to receive a response in the cpc library which implements the functions as channel pipes
|
|
NoHelp bool
|
|
Call chan API
|
|
Params interface{}
|
|
Result func() API
|
|
}{
|
|
{{range .}} "{{.Method}}":{
|
|
Handler: {{.Handler}}, Call: make(chan API, 32),
|
|
Result: func() API { return API{Ch: make(chan {{.Handler}}Res)} }},
|
|
{{end}}
|
|
}
|
|
|
|
// API functions
|
|
//
|
|
// The functions here provide access to the RPC through a convenient set of functions generated for each call in the RPC
|
|
// API to request, check for, access the results and wait on results
|
|
|
|
{{range .}}
|
|
// {{.Handler}} calls the method with the given parameters
|
|
func (a API) {{.Handler}}(cmd {{.Cmd}}) (e error) {
|
|
` + RPCMapName + `["{{.Method}}"].Call <- API{a.Ch, cmd, nil}
|
|
return
|
|
}
|
|
|
|
// {{.Handler}}Check checks if a new message arrived on the result channel and returns true if it does, as well as
|
|
// storing the value in the Result field
|
|
func (a API) {{.Handler}}Check() (isNew bool) {
|
|
select {
|
|
case o := <- a.Ch.(chan {{.Handler}}Res):
|
|
if o.e != nil {
|
|
a.Result = o.e
|
|
} else {
|
|
a.Result = o.Res
|
|
}
|
|
isNew = true
|
|
default:
|
|
}
|
|
return
|
|
}
|
|
|
|
// {{.Handler}}GetRes returns a pointer to the value in the Result field
|
|
func (a API) {{.Handler}}GetRes() (out *{{.ResType}}, e error) {
|
|
out, _ = a.Result.(*{{.ResType}})
|
|
e, _ = a.Result.(error)
|
|
return
|
|
}
|
|
|
|
// {{.Handler}}Wait calls the method and blocks until it returns or 5 seconds passes
|
|
func (a API) {{.Handler}}Wait(cmd {{.Cmd}}) (out *{{.ResType}}, e error) {
|
|
` + RPCMapName + `["{{.Method}}"].Call <- API{a.Ch, cmd, nil}
|
|
select {
|
|
case <-time.After(time.Second*5):
|
|
break
|
|
case o := <- a.Ch.(chan {{.Handler}}Res):
|
|
out, e = o.Res, o.e
|
|
}
|
|
return
|
|
}
|
|
{{end}}
|
|
|
|
// RunAPI starts up the api handler server that receives rpc.API messages and runs the handler and returns the result
|
|
// Note that the parameters are type asserted to prevent the consumer of the API from sending wrong message types not
|
|
// because it's necessary since they are interfaces end to end
|
|
func RunAPI(chainRPC *chainclient.RPCClient, wallet *Wallet,
|
|
quit qu.C) {
|
|
nrh := ` + RPCMapName + `
|
|
go func() {
|
|
D.Ln("starting up wallet cAPI")
|
|
var e error
|
|
var res interface{}
|
|
for {
|
|
select { {{range .}}
|
|
case msg := <-nrh["{{.Method}}"].Call:
|
|
if res, e = nrh["{{.Method}}"].
|
|
Handler(msg.Params.({{.Cmd}}), wallet,
|
|
chainRPC); E.Chk(e) {
|
|
}
|
|
if r, ok := res.({{.ResType}}); ok {
|
|
msg.Ch.(chan {{.Handler}}Res) <- {{.Handler}}Res{&r, e} } {{end}}
|
|
case <-quit.Wait():
|
|
D.Ln("stopping wallet cAPI")
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// RPC API functions to use with net/rpc
|
|
{{range .}}
|
|
func (c *CAPI) {{.Handler}}(req {{.Cmd}}, resp {{.ResType}}) (e error) {
|
|
nrh := ` + RPCMapName + `
|
|
res := nrh["{{.Method}}"].Result()
|
|
res.Params = req
|
|
nrh["{{.Method}}"].Call <- res
|
|
select {
|
|
case resp = <-res.Ch.(chan {{.ResType}}):
|
|
case <-time.After(c.Timeout):
|
|
case <-c.quit.Wait():
|
|
}
|
|
return
|
|
}
|
|
{{end}}
|
|
// Client call wrappers for a CAPI client with a given Conn
|
|
{{range .}}
|
|
func (r *CAPIClient) {{.Handler}}(cmd ...{{.Cmd}}) (res {{.ResType}}, e error) {
|
|
var c {{.Cmd}}
|
|
if len(cmd) > 0 {
|
|
c = cmd[0]
|
|
}
|
|
if e = r.Call("` + Worker + `.{{.Handler}}", c, &res); E.Chk(e) {
|
|
}
|
|
return
|
|
}
|
|
{{end}}
|
|
`
|
|
|
|
var handlers = handlersT{
|
|
{
|
|
Method: "addmultisigaddress",
|
|
Handler: "AddMultiSigAddress",
|
|
Cmd: "*btcjson.AddMultisigAddressCmd",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "createmultisig",
|
|
Handler: "CreateMultiSig",
|
|
Cmd: "*btcjson.CreateMultisigCmd",
|
|
ResType: "btcjson.CreateMultiSigResult",
|
|
},
|
|
{
|
|
Method: "dumpprivkey",
|
|
Handler: "DumpPrivKey",
|
|
Cmd: "*btcjson.DumpPrivKeyCmd",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "getaccount",
|
|
Handler: "GetAccount",
|
|
Cmd: "*btcjson.GetAccountCmd",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "getaccountaddress",
|
|
Handler: "GetAccountAddress",
|
|
Cmd: "*btcjson.GetAccountAddressCmd",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "getaddressesbyaccount",
|
|
Handler: "GetAddressesByAccount",
|
|
Cmd: "*btcjson.GetAddressesByAccountCmd",
|
|
ResType: "[]string",
|
|
},
|
|
{
|
|
Method: "getbalance",
|
|
Handler: "GetBalance",
|
|
Cmd: "*btcjson.GetBalanceCmd",
|
|
ResType: "float64",
|
|
},
|
|
{
|
|
Method: "getbestblockhash",
|
|
Handler: "GetBestBlockHash",
|
|
Cmd: "*None",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "getblockcount",
|
|
Handler: "GetBlockCount",
|
|
Cmd: "*None",
|
|
ResType: "int32",
|
|
},
|
|
{
|
|
Method: "getinfo",
|
|
Handler: "GetInfo",
|
|
Cmd: "*None",
|
|
ResType: "btcjson.InfoWalletResult",
|
|
},
|
|
{
|
|
Method: "getnewaddress",
|
|
Handler: "GetNewAddress",
|
|
Cmd: "*btcjson.GetNewAddressCmd",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "getrawchangeaddress",
|
|
Handler: "GetRawChangeAddress",
|
|
Cmd: "*btcjson.GetRawChangeAddressCmd",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "getreceivedbyaccount",
|
|
Handler: "GetReceivedByAccount",
|
|
Cmd: "*btcjson.GetReceivedByAccountCmd",
|
|
ResType: "float64",
|
|
},
|
|
{
|
|
Method: "getreceivedbyaddress",
|
|
Handler: "GetReceivedByAddress",
|
|
Cmd: "*btcjson.GetReceivedByAddressCmd",
|
|
ResType: "float64",
|
|
},
|
|
{
|
|
Method: "gettransaction",
|
|
Handler: "GetTransaction",
|
|
Cmd: "*btcjson.GetTransactionCmd",
|
|
ResType: "btcjson.GetTransactionResult",
|
|
},
|
|
{
|
|
Method: "help",
|
|
Handler: "HelpNoChainRPC",
|
|
HandlerWithChain: "HelpWithChainRPC",
|
|
Cmd: "btcjson.HelpCmd",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "importprivkey",
|
|
Handler: "ImportPrivKey",
|
|
Cmd: "*btcjson.ImportPrivKeyCmd",
|
|
ResType: "None",
|
|
},
|
|
{
|
|
Method: "keypoolrefill",
|
|
Handler: "KeypoolRefill",
|
|
Cmd: "*None",
|
|
ResType: "None",
|
|
},
|
|
{
|
|
Method: "listaccounts",
|
|
Handler: "ListAccounts",
|
|
Cmd: "*btcjson.ListAccountsCmd",
|
|
ResType: "map[string]float64",
|
|
},
|
|
{
|
|
Method: "listlockunspent",
|
|
Handler: "ListLockUnspent",
|
|
Cmd: "*None",
|
|
ResType: "[]btcjson.TransactionInput",
|
|
},
|
|
{
|
|
Method: "listreceivedbyaccount",
|
|
Handler: "ListReceivedByAccount",
|
|
Cmd: "*btcjson.ListReceivedByAccountCmd",
|
|
ResType: "[]btcjson.ListReceivedByAccountResult",
|
|
},
|
|
{
|
|
Method: "listreceivedbyaddress",
|
|
Handler: "ListReceivedByAddress",
|
|
Cmd: "*btcjson.ListReceivedByAddressCmd",
|
|
ResType: "btcjson.ListReceivedByAddressResult",
|
|
},
|
|
{
|
|
Method: "listsinceblock",
|
|
Handler: "ListSinceBlock",
|
|
HandlerWithChain: "ListSinceBlock",
|
|
Cmd: "btcjson.ListSinceBlockCmd",
|
|
ResType: "btcjson.ListSinceBlockResult",
|
|
},
|
|
{
|
|
Method: "listtransactions",
|
|
Handler: "ListTransactions",
|
|
Cmd: "*btcjson.ListTransactionsCmd",
|
|
ResType: "[]btcjson.ListTransactionsResult",
|
|
},
|
|
{
|
|
Method: "listunspent",
|
|
Handler: "ListUnspent",
|
|
Cmd: "*btcjson.ListUnspentCmd",
|
|
ResType: "[]btcjson.ListUnspentResult",
|
|
},
|
|
{
|
|
Method: "sendfrom",
|
|
Handler: "LockUnspent",
|
|
HandlerWithChain: "LockUnspent",
|
|
Cmd: "btcjson.LockUnspentCmd",
|
|
ResType: "bool",
|
|
},
|
|
{
|
|
Method: "sendmany",
|
|
Handler: "SendMany",
|
|
Cmd: "*btcjson.SendManyCmd",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "sendtoaddress",
|
|
Handler: "SendToAddress",
|
|
Cmd: "*btcjson.SendToAddressCmd",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "settxfee",
|
|
Handler: "SetTxFee",
|
|
Cmd: "*btcjson.SetTxFeeCmd",
|
|
ResType: "bool",
|
|
},
|
|
{
|
|
Method: "signmessage",
|
|
Handler: "SignMessage",
|
|
Cmd: "*btcjson.SignMessageCmd",
|
|
ResType: "string",
|
|
},
|
|
{
|
|
Method: "signrawtransaction",
|
|
Handler: "SignRawTransaction",
|
|
HandlerWithChain: "SignRawTransaction",
|
|
Cmd: "btcjson.SignRawTransactionCmd",
|
|
ResType: "btcjson.SignRawTransactionResult",
|
|
},
|
|
{
|
|
Method: "validateaddress",
|
|
Handler: "ValidateAddress",
|
|
Cmd: "*btcjson.ValidateAddressCmd",
|
|
ResType: "btcjson.ValidateAddressWalletResult",
|
|
},
|
|
{
|
|
Method: "verifymessage",
|
|
Handler: "VerifyMessage",
|
|
Cmd: "*btcjson.VerifyMessageCmd",
|
|
ResType: "bool",
|
|
},
|
|
{
|
|
Method: "walletlock",
|
|
Handler: "WalletLock",
|
|
Cmd: "*None",
|
|
ResType: "None",
|
|
},
|
|
{
|
|
Method: "walletpassphrase",
|
|
Handler: "WalletPassphrase",
|
|
Cmd: "*btcjson.WalletPassphraseCmd",
|
|
ResType: "None",
|
|
},
|
|
{
|
|
Method: "walletpassphrasechange",
|
|
Handler: "WalletPassphraseChange",
|
|
Cmd: "*btcjson.WalletPassphraseChangeCmd",
|
|
ResType: "None",
|
|
},
|
|
{
|
|
Method: "createnewaccount",
|
|
Handler: "CreateNewAccount",
|
|
Cmd: "*btcjson.CreateNewAccountCmd",
|
|
ResType: "None",
|
|
},
|
|
{
|
|
Method: "getbestblock",
|
|
Handler: "GetBestBlock",
|
|
Cmd: "*None",
|
|
ResType: "btcjson.GetBestBlockResult",
|
|
},
|
|
{
|
|
Method: "getunconfirmedbalance",
|
|
Handler: "GetUnconfirmedBalance",
|
|
Cmd: "*btcjson.GetUnconfirmedBalanceCmd",
|
|
ResType: "float64",
|
|
},
|
|
{
|
|
Method: "listaddresstransactions",
|
|
Handler: "ListAddressTransactions",
|
|
Cmd: "*btcjson.ListAddressTransactionsCmd",
|
|
ResType: "[]btcjson.ListTransactionsResult",
|
|
},
|
|
{
|
|
Method: "listalltransactions",
|
|
Handler: "ListAllTransactions",
|
|
Cmd: "*btcjson.ListAllTransactionsCmd",
|
|
ResType: "[]btcjson.ListTransactionsResult",
|
|
},
|
|
{
|
|
Method: "renameaccount",
|
|
Handler: "RenameAccount",
|
|
Cmd: "*btcjson.RenameAccountCmd",
|
|
ResType: "None",
|
|
},
|
|
{
|
|
Method: "walletislocked",
|
|
Handler: "WalletIsLocked",
|
|
Cmd: "*None",
|
|
ResType: "bool",
|
|
},
|
|
{
|
|
Method: "dropwallethistory",
|
|
Handler: "HandleDropWalletHistory",
|
|
Cmd: "*None",
|
|
ResType: "string",
|
|
},
|
|
}
|