genapi.go raw
1 package main
2
3 import (
4 "os"
5 "sort"
6 "text/template"
7
8 "github.com/p9c/p9/pkg/log"
9 )
10
11 type handler struct {
12 Method, Handler, HandlerWithChain, Cmd, ResType string
13 }
14
15 type handlersT []handler
16
17 func (h handlersT) Len() int {
18 return len(h)
19 }
20
21 func (h handlersT) Less(i, j int) bool {
22 return h[i].Method < h[j].Method
23 }
24
25 func (h handlersT) Swap(i, j int) {
26 h[i], h[j] = h[j], h[i]
27 }
28
29 func main() {
30 log.SetLogLevel("trace")
31 if fd, e := os.Create("rpchandlers.go"); E.Chk(e) {
32 } else {
33 defer fd.Close()
34 t := template.Must(template.New("noderpc").Parse(NodeRPCHandlerTpl))
35 sort.Sort(handlers)
36 if e = t.Execute(fd, handlers); E.Chk(e) {
37 }
38 }
39 }
40
41 const (
42 RPCMapName = "RPCHandlers"
43 Worker = "CAPI"
44 )
45
46 var NodeRPCHandlerTpl = `// generated by go run ./genapi/.; DO NOT EDIT
47 //
48 `+`//go:generate go run ./genapi/.
49
50 package wallet
51
52 import (
53 "io"
54 "net/rpc"
55 "time"
56
57 "github.com/p9c/p9/pkg/qu"
58
59 "github.com/p9c/p9/pkg/btcjson"
60 "github.com/p9c/p9/pkg/chainclient"
61 )
62
63 // API stores the channel, parameters and result values from calls via the channel
64 type API struct {
65 Ch interface{}
66 Params interface{}
67 Result interface{}
68 }
69
70 // CAPI is the central structure for configuration and access to a net/rpc API access endpoint for this RPC API
71 type CAPI struct {
72 Timeout time.Duration
73 quit qu.C
74 }
75
76 // NewCAPI returns a new CAPI
77 func NewCAPI(quit qu.C, timeout ...time.Duration) (c *CAPI) {
78 c = &CAPI{quit: quit}
79 if len(timeout)>0 {
80 c.Timeout = timeout[0]
81 } else {
82 c.Timeout = time.Second * 5
83 }
84 return
85 }
86
87 // CAPIClient is a wrapper around RPC calls
88 type CAPIClient struct {
89 *rpc.Client
90 }
91
92 // NewCAPIClient creates a new client for a kopach_worker. Note that any kind of connection can be used here,
93 // other than the StdConn
94 func NewCAPIClient(conn io.ReadWriteCloser) *CAPIClient {
95 return &CAPIClient{rpc.NewClient(conn)}
96 }
97
98 type (
99 // None means no parameters it is not checked so it can be nil
100 None struct{} {{range .}}
101 // {{.Handler}}Res is the result from a call to {{.Handler}}
102 {{.Handler}}Res struct { Res *{{.ResType}}; e error }{{end}}
103 )
104
105 // RequestHandler is a handler function to handle an unmarshaled and parsed request into a marshalable response. If the
106 // error is a *json.RPCError or any of the above special error classes, the server will respond with the JSON-RPC
107 // appropriate error code. All other errors use the wallet catch-all error code, json.ErrRPCWallet.
108 type RequestHandler func(interface{}, *Wallet,
109 ...*chainclient.RPCClient) (interface{}, error)
110
111 // ` + RPCMapName + ` is all of the RPC calls available
112 //
113 // - Handler is the handler function
114 //
115 // - Call is a channel carrying a struct containing parameters and error that is listened to in RunAPI to dispatch the
116 // calls
117 //
118 // - Result is a bundle of command parameters and a channel that the result will be sent back on
119 //
120 // Get and save the Result function's return, and you can then call the call functions check, result and wait functions
121 // for asynchronous and synchronous calls to RPC functions
122 var ` + RPCMapName + ` = map[string]struct {
123 Handler RequestHandler
124 // Function variables cannot be compared against anything but nil, so use a boolean to record whether help
125 // generation is necessary. This is used by the tests to ensure that help can be generated for every implemented
126 // method.
127 //
128 // A single map and this bool is here is used rather than several maps for the unimplemented handlers so every
129 // method has exactly one handler function.
130 //
131 // The Return field returns a new channel of the type returned by this function. This makes it possible to use this
132 // for callers to receive a response in the cpc library which implements the functions as channel pipes
133 NoHelp bool
134 Call chan API
135 Params interface{}
136 Result func() API
137 }{
138 {{range .}} "{{.Method}}":{
139 Handler: {{.Handler}}, Call: make(chan API, 32),
140 Result: func() API { return API{Ch: make(chan {{.Handler}}Res)} }},
141 {{end}}
142 }
143
144 // API functions
145 //
146 // The functions here provide access to the RPC through a convenient set of functions generated for each call in the RPC
147 // API to request, check for, access the results and wait on results
148
149 {{range .}}
150 // {{.Handler}} calls the method with the given parameters
151 func (a API) {{.Handler}}(cmd {{.Cmd}}) (e error) {
152 ` + RPCMapName + `["{{.Method}}"].Call <- API{a.Ch, cmd, nil}
153 return
154 }
155
156 // {{.Handler}}Check checks if a new message arrived on the result channel and returns true if it does, as well as
157 // storing the value in the Result field
158 func (a API) {{.Handler}}Check() (isNew bool) {
159 select {
160 case o := <- a.Ch.(chan {{.Handler}}Res):
161 if o.e != nil {
162 a.Result = o.e
163 } else {
164 a.Result = o.Res
165 }
166 isNew = true
167 default:
168 }
169 return
170 }
171
172 // {{.Handler}}GetRes returns a pointer to the value in the Result field
173 func (a API) {{.Handler}}GetRes() (out *{{.ResType}}, e error) {
174 out, _ = a.Result.(*{{.ResType}})
175 e, _ = a.Result.(error)
176 return
177 }
178
179 // {{.Handler}}Wait calls the method and blocks until it returns or 5 seconds passes
180 func (a API) {{.Handler}}Wait(cmd {{.Cmd}}) (out *{{.ResType}}, e error) {
181 ` + RPCMapName + `["{{.Method}}"].Call <- API{a.Ch, cmd, nil}
182 select {
183 case <-time.After(time.Second*5):
184 break
185 case o := <- a.Ch.(chan {{.Handler}}Res):
186 out, e = o.Res, o.e
187 }
188 return
189 }
190 {{end}}
191
192 // RunAPI starts up the api handler server that receives rpc.API messages and runs the handler and returns the result
193 // Note that the parameters are type asserted to prevent the consumer of the API from sending wrong message types not
194 // because it's necessary since they are interfaces end to end
195 func RunAPI(chainRPC *chainclient.RPCClient, wallet *Wallet,
196 quit qu.C) {
197 nrh := ` + RPCMapName + `
198 go func() {
199 D.Ln("starting up wallet cAPI")
200 var e error
201 var res interface{}
202 for {
203 select { {{range .}}
204 case msg := <-nrh["{{.Method}}"].Call:
205 if res, e = nrh["{{.Method}}"].
206 Handler(msg.Params.({{.Cmd}}), wallet,
207 chainRPC); E.Chk(e) {
208 }
209 if r, ok := res.({{.ResType}}); ok {
210 msg.Ch.(chan {{.Handler}}Res) <- {{.Handler}}Res{&r, e} } {{end}}
211 case <-quit.Wait():
212 D.Ln("stopping wallet cAPI")
213 return
214 }
215 }
216 }()
217 }
218
219 // RPC API functions to use with net/rpc
220 {{range .}}
221 func (c *CAPI) {{.Handler}}(req {{.Cmd}}, resp {{.ResType}}) (e error) {
222 nrh := ` + RPCMapName + `
223 res := nrh["{{.Method}}"].Result()
224 res.Params = req
225 nrh["{{.Method}}"].Call <- res
226 select {
227 case resp = <-res.Ch.(chan {{.ResType}}):
228 case <-time.After(c.Timeout):
229 case <-c.quit.Wait():
230 }
231 return
232 }
233 {{end}}
234 // Client call wrappers for a CAPI client with a given Conn
235 {{range .}}
236 func (r *CAPIClient) {{.Handler}}(cmd ...{{.Cmd}}) (res {{.ResType}}, e error) {
237 var c {{.Cmd}}
238 if len(cmd) > 0 {
239 c = cmd[0]
240 }
241 if e = r.Call("` + Worker + `.{{.Handler}}", c, &res); E.Chk(e) {
242 }
243 return
244 }
245 {{end}}
246 `
247
248 var handlers = handlersT{
249 {
250 Method: "addmultisigaddress",
251 Handler: "AddMultiSigAddress",
252 Cmd: "*btcjson.AddMultisigAddressCmd",
253 ResType: "string",
254 },
255 {
256 Method: "createmultisig",
257 Handler: "CreateMultiSig",
258 Cmd: "*btcjson.CreateMultisigCmd",
259 ResType: "btcjson.CreateMultiSigResult",
260 },
261 {
262 Method: "dumpprivkey",
263 Handler: "DumpPrivKey",
264 Cmd: "*btcjson.DumpPrivKeyCmd",
265 ResType: "string",
266 },
267 {
268 Method: "getaccount",
269 Handler: "GetAccount",
270 Cmd: "*btcjson.GetAccountCmd",
271 ResType: "string",
272 },
273 {
274 Method: "getaccountaddress",
275 Handler: "GetAccountAddress",
276 Cmd: "*btcjson.GetAccountAddressCmd",
277 ResType: "string",
278 },
279 {
280 Method: "getaddressesbyaccount",
281 Handler: "GetAddressesByAccount",
282 Cmd: "*btcjson.GetAddressesByAccountCmd",
283 ResType: "[]string",
284 },
285 {
286 Method: "getbalance",
287 Handler: "GetBalance",
288 Cmd: "*btcjson.GetBalanceCmd",
289 ResType: "float64",
290 },
291 {
292 Method: "getbestblockhash",
293 Handler: "GetBestBlockHash",
294 Cmd: "*None",
295 ResType: "string",
296 },
297 {
298 Method: "getblockcount",
299 Handler: "GetBlockCount",
300 Cmd: "*None",
301 ResType: "int32",
302 },
303 {
304 Method: "getinfo",
305 Handler: "GetInfo",
306 Cmd: "*None",
307 ResType: "btcjson.InfoWalletResult",
308 },
309 {
310 Method: "getnewaddress",
311 Handler: "GetNewAddress",
312 Cmd: "*btcjson.GetNewAddressCmd",
313 ResType: "string",
314 },
315 {
316 Method: "getrawchangeaddress",
317 Handler: "GetRawChangeAddress",
318 Cmd: "*btcjson.GetRawChangeAddressCmd",
319 ResType: "string",
320 },
321 {
322 Method: "getreceivedbyaccount",
323 Handler: "GetReceivedByAccount",
324 Cmd: "*btcjson.GetReceivedByAccountCmd",
325 ResType: "float64",
326 },
327 {
328 Method: "getreceivedbyaddress",
329 Handler: "GetReceivedByAddress",
330 Cmd: "*btcjson.GetReceivedByAddressCmd",
331 ResType: "float64",
332 },
333 {
334 Method: "gettransaction",
335 Handler: "GetTransaction",
336 Cmd: "*btcjson.GetTransactionCmd",
337 ResType: "btcjson.GetTransactionResult",
338 },
339 {
340 Method: "help",
341 Handler: "HelpNoChainRPC",
342 HandlerWithChain: "HelpWithChainRPC",
343 Cmd: "btcjson.HelpCmd",
344 ResType: "string",
345 },
346 {
347 Method: "importprivkey",
348 Handler: "ImportPrivKey",
349 Cmd: "*btcjson.ImportPrivKeyCmd",
350 ResType: "None",
351 },
352 {
353 Method: "keypoolrefill",
354 Handler: "KeypoolRefill",
355 Cmd: "*None",
356 ResType: "None",
357 },
358 {
359 Method: "listaccounts",
360 Handler: "ListAccounts",
361 Cmd: "*btcjson.ListAccountsCmd",
362 ResType: "map[string]float64",
363 },
364 {
365 Method: "listlockunspent",
366 Handler: "ListLockUnspent",
367 Cmd: "*None",
368 ResType: "[]btcjson.TransactionInput",
369 },
370 {
371 Method: "listreceivedbyaccount",
372 Handler: "ListReceivedByAccount",
373 Cmd: "*btcjson.ListReceivedByAccountCmd",
374 ResType: "[]btcjson.ListReceivedByAccountResult",
375 },
376 {
377 Method: "listreceivedbyaddress",
378 Handler: "ListReceivedByAddress",
379 Cmd: "*btcjson.ListReceivedByAddressCmd",
380 ResType: "btcjson.ListReceivedByAddressResult",
381 },
382 {
383 Method: "listsinceblock",
384 Handler: "ListSinceBlock",
385 HandlerWithChain: "ListSinceBlock",
386 Cmd: "btcjson.ListSinceBlockCmd",
387 ResType: "btcjson.ListSinceBlockResult",
388 },
389 {
390 Method: "listtransactions",
391 Handler: "ListTransactions",
392 Cmd: "*btcjson.ListTransactionsCmd",
393 ResType: "[]btcjson.ListTransactionsResult",
394 },
395 {
396 Method: "listunspent",
397 Handler: "ListUnspent",
398 Cmd: "*btcjson.ListUnspentCmd",
399 ResType: "[]btcjson.ListUnspentResult",
400 },
401 {
402 Method: "sendfrom",
403 Handler: "LockUnspent",
404 HandlerWithChain: "LockUnspent",
405 Cmd: "btcjson.LockUnspentCmd",
406 ResType: "bool",
407 },
408 {
409 Method: "sendmany",
410 Handler: "SendMany",
411 Cmd: "*btcjson.SendManyCmd",
412 ResType: "string",
413 },
414 {
415 Method: "sendtoaddress",
416 Handler: "SendToAddress",
417 Cmd: "*btcjson.SendToAddressCmd",
418 ResType: "string",
419 },
420 {
421 Method: "settxfee",
422 Handler: "SetTxFee",
423 Cmd: "*btcjson.SetTxFeeCmd",
424 ResType: "bool",
425 },
426 {
427 Method: "signmessage",
428 Handler: "SignMessage",
429 Cmd: "*btcjson.SignMessageCmd",
430 ResType: "string",
431 },
432 {
433 Method: "signrawtransaction",
434 Handler: "SignRawTransaction",
435 HandlerWithChain: "SignRawTransaction",
436 Cmd: "btcjson.SignRawTransactionCmd",
437 ResType: "btcjson.SignRawTransactionResult",
438 },
439 {
440 Method: "validateaddress",
441 Handler: "ValidateAddress",
442 Cmd: "*btcjson.ValidateAddressCmd",
443 ResType: "btcjson.ValidateAddressWalletResult",
444 },
445 {
446 Method: "verifymessage",
447 Handler: "VerifyMessage",
448 Cmd: "*btcjson.VerifyMessageCmd",
449 ResType: "bool",
450 },
451 {
452 Method: "walletlock",
453 Handler: "WalletLock",
454 Cmd: "*None",
455 ResType: "None",
456 },
457 {
458 Method: "walletpassphrase",
459 Handler: "WalletPassphrase",
460 Cmd: "*btcjson.WalletPassphraseCmd",
461 ResType: "None",
462 },
463 {
464 Method: "walletpassphrasechange",
465 Handler: "WalletPassphraseChange",
466 Cmd: "*btcjson.WalletPassphraseChangeCmd",
467 ResType: "None",
468 },
469 {
470 Method: "createnewaccount",
471 Handler: "CreateNewAccount",
472 Cmd: "*btcjson.CreateNewAccountCmd",
473 ResType: "None",
474 },
475 {
476 Method: "getbestblock",
477 Handler: "GetBestBlock",
478 Cmd: "*None",
479 ResType: "btcjson.GetBestBlockResult",
480 },
481 {
482 Method: "getunconfirmedbalance",
483 Handler: "GetUnconfirmedBalance",
484 Cmd: "*btcjson.GetUnconfirmedBalanceCmd",
485 ResType: "float64",
486 },
487 {
488 Method: "listaddresstransactions",
489 Handler: "ListAddressTransactions",
490 Cmd: "*btcjson.ListAddressTransactionsCmd",
491 ResType: "[]btcjson.ListTransactionsResult",
492 },
493 {
494 Method: "listalltransactions",
495 Handler: "ListAllTransactions",
496 Cmd: "*btcjson.ListAllTransactionsCmd",
497 ResType: "[]btcjson.ListTransactionsResult",
498 },
499 {
500 Method: "renameaccount",
501 Handler: "RenameAccount",
502 Cmd: "*btcjson.RenameAccountCmd",
503 ResType: "None",
504 },
505 {
506 Method: "walletislocked",
507 Handler: "WalletIsLocked",
508 Cmd: "*None",
509 ResType: "bool",
510 },
511 {
512 Method: "dropwallethistory",
513 Handler: "HandleDropWalletHistory",
514 Cmd: "*None",
515 ResType: "string",
516 },
517 }
518