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