1 // Command corectl provides miscellaneous control functions for a Chain Core.
17 "github.com/bytom/blockchain"
18 "github.com/bytom/blockchain/rpc"
19 "github.com/bytom/crypto/ed25519"
20 "github.com/bytom/env"
21 "github.com/bytom/errors"
22 "github.com/bytom/log"
23 "github.com/bytom/crypto/ed25519/chainkd"
28 home = blockchain.HomeDirFromEnvironment()
29 coreURL = env.String("BYTOM_URL", "http://localhost:1999")
31 // build vars; initialized by the linker
37 // We collect log output in this buffer,
38 // and display it only when there's an error.
39 var logbuf bytes.Buffer
42 f func(*rpc.Client, []string)
45 type grantReq struct {
46 Policy string `json:"policy"`
47 GuardType string `json:"guard_type"`
48 GuardData interface{} `json:"guard_data"`
51 var commands = map[string]*command{
52 "create-block-keypair": {createBlockKeyPair},
57 "create-account": {createAccount},
58 "update-account-tags": {updateAccountTags},
59 "create-asset": {createAsset},
60 "update-asset-tags": {updateAssetTags},
61 "build-transaction": {buildTransaction},
65 log.SetOutput(&logbuf)
68 if len(os.Args) >= 2 && os.Args[1] == "-version" {
71 // build tag with bytom- prefix indicates official release
72 version = strings.TrimPrefix(buildTag, "bytom-")
74 // version of the form rev123 indicates non-release build
77 fmt.Printf("bytomcli %s\n", version)
78 fmt.Printf("build-commit: %v\n", buildCommit)
79 fmt.Printf("build-date: %v\n", buildDate)
87 cmd := commands[os.Args[1]]
89 fmt.Fprintln(os.Stderr, "unknown command:", os.Args[1])
93 cmd.f(mustRPCClient(), os.Args[2:])
97 func createBlockKeyPair(client *rpc.Client, args []string) {
99 fatalln("error: create-block-keypair takes no args")
102 Pub ed25519.PublicKey
104 err := client.Call(context.Background(), "/mockhsm/create-block-key", nil, &pub)
106 fmt.Printf("%x\n", pub.Pub)
109 // reset will attempt a reset rpc call on a remote core. If the
110 // core is not configured with reset capabilities an error is returned.
111 func reset(client *rpc.Client, args []string) {
113 fatalln("error: reset takes no args")
116 req := map[string]bool{
120 err := client.Call(context.Background(), "/reset", req, nil)
124 func grant(client *rpc.Client, args []string) {
125 editAuthz(client, args, "grant")
128 func revoke(client *rpc.Client, args []string) {
129 editAuthz(client, args, "revoke")
132 func editAuthz(client *rpc.Client, args []string, action string) {
133 usage := "usage: corectl " + action + " [policy] [guard]"
134 var flags flag.FlagSet
136 flags.Usage = func() {
137 fmt.Fprintln(os.Stderr, usage)
138 fmt.Fprintln(os.Stderr, `
139 Where guard is one of:
140 token=[id] to affect an access token
141 CN=[name] to affect an X.509 Common Name
142 OU=[name] to affect an X.509 Organizational Unit
144 The type of guard (before the = sign) is case-insensitive.
154 req := grantReq{Policy: args[0]}
156 switch typ, data := splitAfter2(args[1], "="); strings.ToUpper(typ) {
158 req.GuardType = "access_token"
159 req.GuardData = map[string]interface{}{"id": data}
161 req.GuardType = "x509"
162 req.GuardData = map[string]interface{}{"subject": map[string]string{"CN": data}}
164 req.GuardType = "x509"
165 req.GuardData = map[string]interface{}{"subject": map[string]string{"OU": data}}
167 fmt.Fprintln(os.Stderr, "unknown guard type", typ)
171 path := map[string]string{
172 "grant": "/create-authorization-grant",
173 "revoke": "/delete-authorization-grant",
175 err := client.Call(context.Background(), path, req, nil)
179 func mustRPCClient() *rpc.Client {
180 // TODO(kr): refactor some of this cert-loading logic into bytom/blockchain
181 // and use it from cored as well.
182 // Note that this function, unlike maybeUseTLS in cored,
183 // does not load the cert and key from env vars,
184 // only from the filesystem.
185 certFile := filepath.Join(home, "tls.crt")
186 keyFile := filepath.Join(home, "tls.key")
187 config, err := blockchain.TLSConfig(certFile, keyFile, "")
188 if err == blockchain.ErrNoTLS {
189 return &rpc.Client{BaseURL: *coreURL}
190 } else if err != nil {
191 fatalln("error: loading TLS cert:", err)
194 t := &http.Transport{
195 DialContext: (&net.Dialer{
196 Timeout: 30 * time.Second,
197 KeepAlive: 30 * time.Second,
201 IdleConnTimeout: 90 * time.Second,
202 TLSClientConfig: config,
203 TLSHandshakeTimeout: 10 * time.Second,
204 ExpectContinueTimeout: 1 * time.Second,
208 if strings.HasPrefix(url, "http:") {
209 url = "https:" + url[5:]
214 Client: &http.Client{Transport: t},
218 func fatalln(v ...interface{}) {
219 io.Copy(os.Stderr, &logbuf)
220 fmt.Fprintln(os.Stderr, v...)
224 func dieOnRPCError(err error, prefixes ...interface{}) {
229 io.Copy(os.Stderr, &logbuf)
231 if len(prefixes) > 0 {
232 fmt.Fprintln(os.Stderr, prefixes...)
235 if msgErr, ok := errors.Root(err).(rpc.ErrStatusCode); ok && msgErr.ErrorData != nil {
236 fmt.Fprintln(os.Stderr, "RPC error:", msgErr.ErrorData.ChainCode, msgErr.ErrorData.Message)
237 if msgErr.ErrorData.Detail != "" {
238 fmt.Fprintln(os.Stderr, "Detail:", msgErr.ErrorData.Detail)
241 fmt.Fprintln(os.Stderr, "RPC error:", err)
247 func help(w io.Writer) {
248 fmt.Fprintln(w, "usage: corectl [-version] [command] [arguments]")
249 fmt.Fprint(w, "\nThe commands are:\n\n")
250 for name := range commands {
251 fmt.Fprintln(w, "\t", name)
253 fmt.Fprint(w, "\nFlags:\n")
254 fmt.Fprintln(w, "\t-version print version information")
258 // splitAfter2 is like strings.SplitAfterN with n=2.
259 // If sep is not in s, it returns a="" and b=s.
260 func splitAfter2(s, sep string) (a, b string) {
261 i := strings.Index(s, sep)
266 func wait(client *rpc.Client, args []string) {
268 fatalln("error: wait takes no args")
272 err := client.Call(context.Background(), "/info", nil, nil)
277 if statusErr, ok := errors.Root(err).(rpc.ErrStatusCode); ok && statusErr.StatusCode/100 != 5 {
281 time.Sleep(500 * time.Millisecond)
285 func createAccount(client *rpc.Client, args []string) {
287 fatalln("error: createAccount takes no args")
289 xprv, err := chainkd.NewXPrv(nil)
291 fatalln("NewXprv error.")
294 fmt.Printf("xprv:%v\n", xprv)
295 fmt.Printf("xpub:%v\n", xpub)
297 RootXPubs []chainkd.XPub `json:"root_xpubs"`
300 Tags map[string]interface{}
301 ClientToken string `json:"client_token"`
304 ins.RootXPubs = []chainkd.XPub{xpub}
307 ins.Tags = map[string]interface{}{"test_tag": "v0",}
308 ins.ClientToken = args[0]
309 responses := make([]interface{}, 50)
310 client.Call(context.Background(), "/create-account", &[]Ins{ins,}, &responses)
312 fmt.Printf("responses:%v\n", responses)
315 func createAsset(client *rpc.Client, args []string) {
317 fatalln("error: createAsset takes no args")
319 xprv, err := chainkd.NewXPrv(nil)
321 fatalln("NewXprv error.")
324 fmt.Printf("xprv:%v\n", xprv)
325 fmt.Printf("xpub:%v\n", xpub)
327 RootXPubs []chainkd.XPub `json:"root_xpubs"`
330 Tags map[string]interface{}
331 Definition map[string]interface{}
332 ClientToken string `json:"client_token"`
335 ins.RootXPubs = []chainkd.XPub{xpub}
338 ins.Tags = map[string]interface{}{"test_tag": "v0",}
339 ins.Definition = map[string]interface{}{"test_definition": "v0"}
340 ins.ClientToken = args[0]
341 responses := make([]interface{}, 50)
342 client.Call(context.Background(), "/create-asset", &[]Ins{ins,}, &responses)
344 fmt.Printf("responses:%v\n", responses)
347 func updateAccountTags(client *rpc.Client,args []string){
349 fatalln("error:updateAccountTags not use args")
354 Tags map[string]interface{} `json:"tags"`
361 ins.Tags = map[string]interface{}{"test_tag": "v0",}
362 responses := make([]interface{}, 50)
363 client.Call(context.Background(), "/update-account-tags", &[]Ins{ins,}, &responses)
364 fmt.Printf("responses:%v\n", responses)
367 func updateAssetTags(client *rpc.Client, args []string){
369 fatalln("error:updateAccountTags not use args")
374 Tags map[string]interface{} `json:"tags"`
381 ins.Tags = map[string]interface{}{"test_tag": "v0",}
382 responses := make([]interface{}, 50)
383 client.Call(context.Background(), "/update-asset-tags", &[]Ins{ins,}, &responses)
384 fmt.Printf("responses:%v\n", responses)
387 func buildTransaction(client *rpc.Client, args []string) {
389 fatalln("error:updateAccountTags not use args")