genapi.go raw
1 // +build generate
2
3 package main
4
5 import (
6 "github.com/p9c/p9/pkg/log"
7 "os"
8 "sort"
9 "text/template"
10 )
11
12 const (
13 RPCMapName = "RPCHandlers"
14 Worker = "CAPI"
15 )
16
17 type handler struct {
18 Method, Handler, Cmd, ResType string
19 }
20
21 type handlersT []handler
22
23 func (h handlersT) Len() int { return len(h) }
24 func (h handlersT) Less(i, j int) bool { return h[i].Method < h[j].Method }
25 func (h handlersT) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
26
27 var handlers = handlersT{
28 {
29 Method: "addnode",
30 Handler: "AddNode",
31 Cmd: "*btcjson.AddNodeCmd",
32 ResType: "None",
33 },
34 {
35 Method: "createrawtransaction",
36 Handler: "CreateRawTransaction",
37 Cmd: "*btcjson.CreateRawTransactionCmd",
38 ResType: "string",
39 },
40 {
41 Method: "decoderawtransaction",
42 Handler: "DecodeRawTransaction",
43 Cmd: "*btcjson.DecodeRawTransactionCmd",
44 ResType: "btcjson.TxRawDecodeResult",
45 },
46 {
47 Method: "decodescript",
48 Handler: "DecodeScript",
49 Cmd: "*btcjson.DecodeScriptCmd",
50 ResType: "btcjson.DecodeScriptResult",
51 },
52 {
53 Method: "estimatefee",
54 Handler: "EstimateFee",
55 Cmd: "*btcjson.EstimateFeeCmd",
56 ResType: "float64",
57 },
58 {
59 Method: "generate",
60 Handler: "Generate",
61 Cmd: "*None",
62 ResType: "[]string",
63 },
64 {
65 Method: "getaddednodeinfo",
66 Handler: "GetAddedNodeInfo",
67 Cmd: "*btcjson.GetAddedNodeInfoCmd",
68 ResType: "[]btcjson.GetAddedNodeInfoResultAddr",
69 },
70 {
71 Method: "getbestblock",
72 Handler: "GetBestBlock",
73 Cmd: "*None",
74 ResType: "btcjson.GetBestBlockResult",
75 },
76 {
77 Method: "getbestblockhash",
78 Handler: "GetBestBlockHash",
79 Cmd: "*None",
80 ResType: "string",
81 },
82 {
83 Method: "getblock",
84 Handler: "GetBlock",
85 Cmd: "*btcjson.GetBlockCmd",
86 ResType: "btcjson.GetBlockVerboseResult",
87 },
88 {
89 Method: "getblockchaininfo",
90 Handler: "GetBlockChainInfo",
91 Cmd: "*None",
92 ResType: "btcjson.GetBlockChainInfoResult",
93 },
94 {
95 Method: "getblockcount",
96 Handler: "GetBlockCount",
97 Cmd: "*None",
98 ResType: "int64",
99 },
100 {
101 Method: "getblockhash",
102 Handler: "GetBlockHash",
103 Cmd: "*btcjson.GetBlockHashCmd",
104 ResType: "string",
105 },
106 {
107 Method: "getblockheader",
108 Handler: "GetBlockHeader",
109 Cmd: "*btcjson.GetBlockHeaderCmd",
110 ResType: "btcjson.GetBlockHeaderVerboseResult",
111 },
112 {
113 Method: "getblocktemplate",
114 Handler: "GetBlockTemplate",
115 Cmd: "*btcjson.GetBlockTemplateCmd",
116 ResType: "string",
117 },
118 {
119 Method: "getcfilter",
120 Handler: "GetCFilter",
121 Cmd: "*btcjson.GetCFilterCmd",
122 ResType: "string",
123 },
124 {
125 Method: "getcfilterheader",
126 Handler: "GetCFilterHeader",
127 Cmd: "*btcjson.GetCFilterHeaderCmd",
128 ResType: "string",
129 },
130 {
131 Method: "getconnectioncount",
132 Handler: "GetConnectionCount",
133 Cmd: "*None",
134 ResType: "int32",
135 },
136 {
137 Method: "getcurrentnet",
138 Handler: "GetCurrentNet",
139 Cmd: "*None",
140 ResType: "string",
141 },
142 {
143 Method: "getdifficulty",
144 Handler: "GetDifficulty",
145 Cmd: "*btcjson.GetDifficultyCmd",
146 ResType: "float64",
147 },
148 {
149 Method: "getgenerate",
150 Handler: "GetGenerate",
151 Cmd: "*btcjson.GetHeadersCmd",
152 ResType: "bool",
153 },
154 {
155 Method: "gethashespersec",
156 Handler: "GetHashesPerSec",
157 Cmd: "*None",
158 ResType: "float64",
159 },
160 {
161 Method: "getheaders",
162 Handler: "GetHeaders",
163 Cmd: "*btcjson.GetHeadersCmd",
164 ResType: "[]string",
165 },
166 {
167 Method: "getinfo",
168 Handler: "GetInfo",
169 Cmd: "*None",
170 ResType: "btcjson.InfoChainResult0",
171 },
172 {
173 Method: "getmempoolinfo",
174 Handler: "GetMempoolInfo",
175 Cmd: "*None",
176 ResType: "btcjson.GetMempoolInfoResult",
177 },
178 {
179 Method: "getmininginfo",
180 Handler: "GetMiningInfo",
181 Cmd: "*None",
182 ResType: "btcjson.GetMiningInfoResult",
183 },
184 {
185 Method: "getnettotals",
186 Handler: "GetNetTotals",
187 Cmd: "*None",
188 ResType: "btcjson.GetNetTotalsResult",
189 },
190 {
191 Method: "getnetworkhashps",
192 Handler: "GetNetworkHashPS",
193 Cmd: "*btcjson.GetNetworkHashPSCmd",
194 ResType: "[]btcjson.GetPeerInfoResult",
195 },
196 {
197 Method: "getpeerinfo",
198 Handler: "GetPeerInfo",
199 Cmd: "*None",
200 ResType: "[]btcjson.GetPeerInfoResult",
201 },
202 {
203 Method: "getrawmempool",
204 Handler: "GetRawMempool",
205 Cmd: "*btcjson.GetRawMempoolCmd",
206 ResType: "[]string",
207 },
208 {
209 Method: "getrawtransaction",
210 Handler: "GetRawTransaction",
211 Cmd: "*btcjson.GetRawTransactionCmd",
212 ResType: "string",
213 },
214 {
215 Method: "gettxout",
216 Handler: "GetTxOut",
217 Cmd: "*btcjson.GetTxOutCmd",
218 ResType: "string",
219 },
220 {
221 Method: "help",
222 Handler: "Help",
223 Cmd: "*btcjson.HelpCmd",
224 ResType: "string",
225 },
226 {
227 Method: "node",
228 Handler: "Node",
229 Cmd: "*btcjson.NodeCmd",
230 ResType: "None",
231 },
232 {
233 Method: "ping",
234 Handler: "Ping",
235 Cmd: "*None",
236 ResType: "None",
237 },
238 {
239 Method: "searchrawtransactions",
240 Handler: "SearchRawTransactions",
241 Cmd: "*btcjson.SearchRawTransactionsCmd",
242 ResType: "[]btcjson.SearchRawTransactionsResult",
243 },
244 {
245 Method: "sendrawtransaction",
246 Handler: "SendRawTransaction",
247 Cmd: "*btcjson.SendRawTransactionCmd",
248 ResType: "None",
249 },
250 {
251 Method: "setgenerate",
252 Handler: "SetGenerate",
253 Cmd: "*btcjson.SetGenerateCmd",
254 ResType: "None",
255 },
256 {
257 Method: "stop",
258 Handler: "Stop",
259 Cmd: "*None",
260 ResType: "None",
261 },
262 {
263 Method: "restart",
264 Handler: "Restart",
265 Cmd: "*None",
266 ResType: "None",
267 },
268 {
269 Method: "resetchain",
270 Handler: "ResetChain",
271 Cmd: "*None",
272 ResType: "None",
273 },
274 {
275 Method: "submitblock",
276 Handler: "SubmitBlock",
277 Cmd: "*btcjson.SubmitBlockCmd",
278 ResType: "string",
279 },
280 {
281 Method: "uptime",
282 Handler: "Uptime",
283 Cmd: "*None",
284 ResType: "btcjson.GetMempoolInfoResult",
285 },
286 {
287 Method: "validateaddress",
288 Handler: "ValidateAddress",
289 Cmd: "*btcjson.ValidateAddressCmd",
290 ResType: "btcjson.ValidateAddressChainResult",
291 },
292 {
293 Method: "verifychain",
294 Handler: "VerifyChain",
295 Cmd: "*btcjson.VerifyChainCmd",
296 ResType: "bool",
297 },
298 {
299 Method: "verifymessage",
300 Handler: "VerifyMessage",
301 Cmd: "*btcjson.VerifyMessageCmd",
302 ResType: "bool",
303 },
304 {
305 Method: "version",
306 Handler: "Version",
307 Cmd: "*btcjson.VersionCmd",
308 ResType: "map[string]btcjson.VersionResult",
309 },
310 }
311
312 func main() {
313 var NodeRPCHandlerTpl = `//go:generate go run -tags generate ./genapi/.
314 // generated; DO NOT EDIT
315
316 package chainrpc
317
318 import (
319 "io"
320 "net/rpc"
321 "time"
322
323 "github.com/p9c/p9/pkg/qu"
324
325 "github.com/p9c/p9/pkg/btcjson"
326 )
327
328 // API stores the channel, parameters and result values from calls via
329 // the channel
330 type API struct {
331 Ch interface{}
332 Params interface{}
333 Result interface{}
334 }
335
336 // CAPI is the central structure for configuration and access to a
337 // net/rpc API access endpoint for this RPC API
338 type CAPI struct {
339 Timeout time.Duration
340 quit qu.C
341 }
342
343 // NewCAPI returns a new CAPI
344 func NewCAPI(quit qu.C, timeout ...time.Duration) (c *CAPI) {
345 c = &CAPI{quit: quit}
346 if len(timeout)>0 {
347 c.Timeout = timeout[0]
348 } else {
349 c.Timeout = time.Second * 5
350 }
351 return
352 }
353
354 // Wrappers around RPC calls
355 type CAPIClient struct {
356 *rpc.Client
357 }
358
359 // New creates a new client for a kopach_worker.
360 // Note that any kind of connection can be used here, other than the StdConn
361 func NewCAPIClient(conn io.ReadWriteCloser) *CAPIClient {
362 return &CAPIClient{rpc.NewClient(conn)}
363 }
364
365 type (
366 // None means no parameters it is not checked so it can be nil
367 None struct{} {{range .}}
368 // {{.Handler}}Res is the result from a call to {{.Handler}}
369 {{.Handler}}Res struct { Res *{{.ResType}}; Err error }{{end}}
370 )
371
372 // ` + RPCMapName + `BeforeInit are created first and are added to the main list
373 // when the init runs.
374 //
375 // - Fn is the handler function
376 //
377 // - Call is a channel carrying a struct containing parameters and error that is
378 // listened to in RunAPI to dispatch the calls
379 //
380 // - Result is a bundle of command parameters and a channel that the result will be sent
381 // back on
382 //
383 // Get and save the Result function's return, and you can then call the call functions
384 // check, result and wait functions for asynchronous and synchronous calls to RPC functions
385 var ` + RPCMapName + `BeforeInit = map[string]CommandHandler{
386 {{range .}} "{{.Method}}":{
387 Fn: Handle{{.Handler}}, Call: make(chan API, 32),
388 Result: func() API { return API{Ch: make(chan {{.Handler}}Res)} }},
389 {{end}}
390 }
391
392 // API functions
393 //
394 // The functions here provide access to the RPC through a convenient set of functions
395 // generated for each call in the RPC API to request, check for, access the results and
396 // wait on results
397
398 {{range .}}
399 // {{.Handler}} calls the method with the given parameters
400 func (a API) {{.Handler}}(cmd {{.Cmd}}) (e error) {
401 ` + RPCMapName + `["{{.Method}}"].Call <-API{a.Ch, cmd, nil}
402 return
403 }
404
405 // {{.Handler}}Chk checks if a new message arrived on the result channel and
406 // returns true if it does, as well as storing the value in the Result field
407 func (a API) {{.Handler}}Chk() (isNew bool) {
408 select {
409 case o := <-a.Ch.(chan {{.Handler}}Res):
410 if o.Err != nil {
411 a.Result = o.Err
412 } else {
413 a.Result = o.Res
414 }
415 isNew = true
416 default:
417 }
418 return
419 }
420
421 // {{.Handler}}GetRes returns a pointer to the value in the Result field
422 func (a API) {{.Handler}}GetRes() (out *{{.ResType}}, e error) {
423 out, _ = a.Result.(*{{.ResType}})
424 e, _ = a.Result.(error)
425 return
426 }
427
428 // {{.Handler}}Wait calls the method and blocks until it returns or 5 seconds passes
429 func (a API) {{.Handler}}Wait(cmd {{.Cmd}}) (out *{{.ResType}}, e error) {
430 ` + RPCMapName + `["{{.Method}}"].Call <-API{a.Ch, cmd, nil}
431 select {
432 case <-time.After(time.Second*5):
433 break
434 case o := <-a.Ch.(chan {{.Handler}}Res):
435 out, e = o.Res, o.Err
436 }
437 return
438 }
439 {{end}}
440
441 // RunAPI starts up the api handler server that receives rpc.API messages and runs the handler and returns the result
442 // Note that the parameters are type asserted to prevent the consumer of the API from sending wrong message types not
443 // because it's necessary since they are interfaces end to end
444 func RunAPI(server *Server, quit qu.C) {
445 nrh := ` + RPCMapName + `
446 go func() {
447 D.Ln("starting up node cAPI")
448 var e error
449 var res interface{}
450 for {
451 select { {{range .}}
452 case msg := <-nrh["{{.Method}}"].Call:
453 if res, e = nrh["{{.Method}}"].
454 Fn(server, msg.Params.({{.Cmd}}), nil); E.Chk(e) {
455 }
456 if r, ok := res.({{.ResType}}); ok {
457 msg.Ch.(chan {{.Handler}}Res) <-{{.Handler}}Res{&r, e} } {{end}}
458 case <-quit.Wait():
459 D.Ln("stopping wallet cAPI")
460 return
461 }
462 }
463 }()
464 }
465
466 // RPC API functions to use with net/rpc
467 {{range .}}
468 func (c *CAPI) {{.Handler}}(req {{.Cmd}}, resp {{.ResType}}) (e error) {
469 nrh := ` + RPCMapName + `
470 res := nrh["{{.Method}}"].Result()
471 res.Params = req
472 nrh["{{.Method}}"].Call <- res
473 select {
474 case resp = <-res.Ch.(chan {{.ResType}}):
475 case <-time.After(c.Timeout):
476 case <-c.quit.Wait():
477 }
478 return
479 }
480 {{end}}
481 // Client call wrappers for a CAPI client with a given Conn
482 {{range .}}
483 func (r *CAPIClient) {{.Handler}}(cmd ...{{.Cmd}}) (res {{.ResType}}, e error) {
484 var c {{.Cmd}}
485 if len(cmd) > 0 {
486 c = cmd[0]
487 }
488 if e = r.Call("` + Worker + `.{{.Handler}}", c, &res); E.Chk(e) {
489 }
490 return
491 }
492 {{end}}
493 `
494 log.SetLogLevel("trace")
495 if fd, e := os.Create("rpchandlers.go"); E.Chk(e) {
496 if fd, e := os.OpenFile("rpchandlers.go", os.O_RDWR|os.O_CREATE, 0755); E.Chk(e) {
497 } else {
498 defer fd.Close()
499 t := template.Must(template.New("noderpc").Parse(NodeRPCHandlerTpl))
500 sort.Sort(handlers)
501 if e = t.Execute(fd, handlers); E.Chk(e) {
502 }
503 }
504 } else {
505 defer fd.Close()
506 t := template.Must(template.New("noderpc").Parse(NodeRPCHandlerTpl))
507 sort.Sort(handlers)
508 if e = t.Execute(fd, handlers); E.Chk(e) {
509 }
510 }
511 }
512