Files
p9/cmd/wallet/genapi/genapi.go
Loki Verloren 0e2bba237a initial commit
2021-05-03 10:43:10 +02:00

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",
},
}