github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/console/console.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 //版权所有2016 Go Ethereum作者 10 //此文件是Go以太坊库的一部分。 11 // 12 //Go-Ethereum库是免费软件:您可以重新分发它和/或修改 13 //根据GNU发布的较低通用公共许可证的条款 14 //自由软件基金会,或者许可证的第3版,或者 15 //(由您选择)任何更高版本。 16 // 17 //Go以太坊图书馆的发行目的是希望它会有用, 18 //但没有任何保证;甚至没有 19 //适销性或特定用途的适用性。见 20 //GNU较低的通用公共许可证,了解更多详细信息。 21 // 22 //你应该收到一份GNU较低级别的公共许可证副本 23 //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。 24 25 package console 26 27 import ( 28 "fmt" 29 "io" 30 "io/ioutil" 31 "os" 32 "os/signal" 33 "path/filepath" 34 "regexp" 35 "sort" 36 "strings" 37 "syscall" 38 39 "github.com/ethereum/go-ethereum/internal/jsre" 40 "github.com/ethereum/go-ethereum/internal/web3ext" 41 "github.com/ethereum/go-ethereum/rpc" 42 "github.com/mattn/go-colorable" 43 "github.com/peterh/liner" 44 "github.com/robertkrimen/otto" 45 ) 46 47 var ( 48 passwordRegexp = regexp.MustCompile(`personal.[nus]`) 49 onlyWhitespace = regexp.MustCompile(`^\s*$`) 50 exit = regexp.MustCompile(`^\s*exit\s*;*\s*$`) 51 ) 52 53 //HistoryFile是数据目录中用于存储输入回滚的文件。 54 const HistoryFile = "history" 55 56 //默认提示是用于用户输入查询的默认提示行前缀。 57 const DefaultPrompt = "> " 58 59 //config是配置的集合,用于微调 60 // 61 type Config struct { 62 DataDir string //存储控制台历史记录的数据目录 63 DocRoot string //从何处加载javascript文件的文件系统路径 64 Client *rpc.Client //通过RPC客户端执行以太坊请求 65 Prompt string //输入提示前缀字符串(默认为默认提示) 66 Prompter UserPrompter //输入提示以允许交互式用户反馈(默认为TerminalPrompter) 67 Printer io.Writer //要序列化任何显示字符串的输出编写器(默认为os.stdout) 68 Preload []string //要预加载的javascript文件的绝对路径 69 } 70 71 //控制台是一个JavaScript解释的运行时环境。它是一个成熟的 72 //通过外部或进程内RPC连接到运行节点的javascript控制台 73 //客户端。 74 type Console struct { 75 client *rpc.Client //通过RPC客户端执行以太坊请求 76 jsre *jsre.JSRE //运行解释器的javascript运行时环境 77 prompt string //输入提示前缀字符串 78 prompter UserPrompter //输入提示以允许交互式用户反馈 79 histPath string //控制台回滚历史记录的绝对路径 80 history []string //控制台维护的滚动历史记录 81 printer io.Writer //要将任何显示字符串序列化到的输出编写器 82 } 83 84 //new初始化javascript解释的运行时环境并设置默认值 85 //使用config结构。 86 func New(config Config) (*Console, error) { 87 //优雅地处理未设置的配置值 88 if config.Prompter == nil { 89 config.Prompter = Stdin 90 } 91 if config.Prompt == "" { 92 config.Prompt = DefaultPrompt 93 } 94 if config.Printer == nil { 95 config.Printer = colorable.NewColorableStdout() 96 } 97 //初始化控制台并返回 98 console := &Console{ 99 client: config.Client, 100 jsre: jsre.New(config.DocRoot, config.Printer), 101 prompt: config.Prompt, 102 prompter: config.Prompter, 103 printer: config.Printer, 104 histPath: filepath.Join(config.DataDir, HistoryFile), 105 } 106 if err := os.MkdirAll(config.DataDir, 0700); err != nil { 107 return nil, err 108 } 109 if err := console.init(config.Preload); err != nil { 110 return nil, err 111 } 112 return console, nil 113 } 114 115 //init从远程RPC提供程序检索可用的API并初始化 116 //控制台的javascript名称空间基于公开的模块。 117 func (c *Console) init(preload []string) error { 118 //初始化javascript<->go-rpc桥 119 bridge := newBridge(c.client, c.prompter, c.printer) 120 c.jsre.Set("jeth", struct{}{}) 121 122 jethObj, _ := c.jsre.Get("jeth") 123 jethObj.Object().Set("send", bridge.Send) 124 jethObj.Object().Set("sendAsync", bridge.Send) 125 126 consoleObj, _ := c.jsre.Get("console") 127 consoleObj.Object().Set("log", c.consoleOutput) 128 consoleObj.Object().Set("error", c.consoleOutput) 129 130 //加载所有内部实用程序javascript库 131 if err := c.jsre.Compile("bignumber.js", jsre.BigNumber_JS); err != nil { 132 return fmt.Errorf("bignumber.js: %v", err) 133 } 134 if err := c.jsre.Compile("web3.js", jsre.Web3_JS); err != nil { 135 return fmt.Errorf("web3.js: %v", err) 136 } 137 if _, err := c.jsre.Run("var Web3 = require('web3');"); err != nil { 138 return fmt.Errorf("web3 require: %v", err) 139 } 140 if _, err := c.jsre.Run("var web3 = new Web3(jeth);"); err != nil { 141 return fmt.Errorf("web3 provider: %v", err) 142 } 143 //将支持的API加载到JavaScript运行时环境中 144 apis, err := c.client.SupportedModules() 145 if err != nil { 146 return fmt.Errorf("api modules: %v", err) 147 } 148 flatten := "var eth = web3.eth; var personal = web3.personal; " 149 for api := range apis { 150 if api == "web3" { 151 continue //手动映射或忽略 152 } 153 if file, ok := web3ext.Modules[api]; ok { 154 //加载模块的扩展。 155 if err = c.jsre.Compile(fmt.Sprintf("%s.js", api), file); err != nil { 156 return fmt.Errorf("%s.js: %v", api, err) 157 } 158 flatten += fmt.Sprintf("var %s = web3.%s; ", api, api) 159 } else if obj, err := c.jsre.Run("web3." + api); err == nil && obj.IsObject() { 160 //启用web3.js内置扩展(如果可用)。 161 flatten += fmt.Sprintf("var %s = web3.%s; ", api, api) 162 } 163 } 164 if _, err = c.jsre.Run(flatten); err != nil { 165 return fmt.Errorf("namespace flattening: %v", err) 166 } 167 //初始化全局名称寄存器(暂时禁用) 168 //c.jsre.run(`var globalregistrar=eth.contract(`+registrar.globalregistrabi+`);registrar=globalregistrar.at(`+registrar.globalregistraradr+`);`) 169 170 // 171 if c.prompter != nil { 172 //检索要检测的帐户管理对象 173 personal, err := c.jsre.Get("personal") 174 if err != nil { 175 return err 176 } 177 //重写openwallet、unlockaccount、newaccount和sign方法,因为 178 //这些需要用户交互。在控制台中分配这些方法 179 //原始Web3回调。这些将由jeth.*方法在 180 //他们从用户那里获得了密码,并将原始Web3请求发送到 181 //后端。 182 if obj := personal.Object(); obj != nil { //确保通过接口启用个人API 183 if _, err = c.jsre.Run(`jeth.openWallet = personal.openWallet;`); err != nil { 184 return fmt.Errorf("personal.openWallet: %v", err) 185 } 186 if _, err = c.jsre.Run(`jeth.unlockAccount = personal.unlockAccount;`); err != nil { 187 return fmt.Errorf("personal.unlockAccount: %v", err) 188 } 189 if _, err = c.jsre.Run(`jeth.newAccount = personal.newAccount;`); err != nil { 190 return fmt.Errorf("personal.newAccount: %v", err) 191 } 192 if _, err = c.jsre.Run(`jeth.sign = personal.sign;`); err != nil { 193 return fmt.Errorf("personal.sign: %v", err) 194 } 195 obj.Set("openWallet", bridge.OpenWallet) 196 obj.Set("unlockAccount", bridge.UnlockAccount) 197 obj.Set("newAccount", bridge.NewAccount) 198 obj.Set("sign", bridge.Sign) 199 } 200 } 201 //admin.sleep和admin.sleepBlocks由控制台提供,而不是由rpc层提供。 202 admin, err := c.jsre.Get("admin") 203 if err != nil { 204 return err 205 } 206 if obj := admin.Object(); obj != nil { //确保通过接口启用管理API 207 obj.Set("sleepBlocks", bridge.SleepBlocks) 208 obj.Set("sleep", bridge.Sleep) 209 obj.Set("clearHistory", c.clearHistory) 210 } 211 //在启动控制台之前预加载任何javascript文件 212 for _, path := range preload { 213 if err := c.jsre.Exec(path); err != nil { 214 failure := err.Error() 215 if ottoErr, ok := err.(*otto.Error); ok { 216 failure = ottoErr.String() 217 } 218 return fmt.Errorf("%s: %v", path, failure) 219 } 220 } 221 //配置控制台的输入Prompter以完成回滚和选项卡。 222 if c.prompter != nil { 223 if content, err := ioutil.ReadFile(c.histPath); err != nil { 224 c.prompter.SetHistory(nil) 225 } else { 226 c.history = strings.Split(string(content), "\n") 227 c.prompter.SetHistory(c.history) 228 } 229 c.prompter.SetWordCompleter(c.AutoCompleteInput) 230 } 231 return nil 232 } 233 234 func (c *Console) clearHistory() { 235 c.history = nil 236 c.prompter.ClearHistory() 237 if err := os.Remove(c.histPath); err != nil { 238 fmt.Fprintln(c.printer, "can't delete history file:", err) 239 } else { 240 fmt.Fprintln(c.printer, "history file deleted") 241 } 242 } 243 244 //consoleoutput是console.log和console.error方法的重写,用于 245 //将输出流传输到配置的输出流,而不是stdout。 246 func (c *Console) consoleOutput(call otto.FunctionCall) otto.Value { 247 output := []string{} 248 for _, argument := range call.ArgumentList { 249 output = append(output, fmt.Sprintf("%v", argument)) 250 } 251 fmt.Fprintln(c.printer, strings.Join(output, " ")) 252 return otto.Value{} 253 } 254 255 //autocompleteinput是一个预先组装好的单词完成器,供用户使用。 256 //输入prompter以向用户提供有关可用方法的提示。 257 func (c *Console) AutoCompleteInput(line string, pos int) (string, []string, string) { 258 //不能为空输入提供完成 259 if len(line) == 0 || pos == 0 { 260 return "", nil, "" 261 } 262 //将数据分块到相关部分进行自动完成 263 //例如,如果是嵌套行eth.getbalance(eth.coinb<tab><tab> 264 start := pos - 1 265 for ; start > 0; start-- { 266 //跳过所有方法和名称空间(即包括点) 267 if line[start] == '.' || (line[start] >= 'a' && line[start] <= 'z') || (line[start] >= 'A' && line[start] <= 'Z') { 268 continue 269 } 270 //以特殊方式处理Web3(即其他数字不会自动完成) 271 if start >= 3 && line[start-3:start] == "web3" { 272 start -= 3 273 continue 274 } 275 //我们碰到了一个意想不到的字符,这里是自动完成表单 276 start++ 277 break 278 } 279 return line[:start], c.jsre.CompleteKeywords(line[start:pos]), line[pos:] 280 } 281 282 //欢迎显示当前geth实例的摘要和有关 283 //控制台的可用模块。 284 func (c *Console) Welcome() { 285 //打印一些通用的geth元数据 286 fmt.Fprintf(c.printer, "Welcome to the Geth JavaScript console!\n\n") 287 c.jsre.Run(` 288 console.log("instance: " + web3.version.node); 289 console.log("coinbase: " + eth.coinbase); 290 console.log("at block: " + eth.blockNumber + " (" + new Date(1000 * eth.getBlock(eth.blockNumber).timestamp) + ")"); 291 console.log(" datadir: " + admin.datadir); 292 `) 293 //列出用户可以调用的所有支持模块 294 if apis, err := c.client.SupportedModules(); err == nil { 295 modules := make([]string, 0, len(apis)) 296 for api, version := range apis { 297 modules = append(modules, fmt.Sprintf("%s:%s", api, version)) 298 } 299 sort.Strings(modules) 300 fmt.Fprintln(c.printer, " modules:", strings.Join(modules, " ")) 301 } 302 fmt.Fprintln(c.printer) 303 } 304 305 //evaluate执行代码并将结果漂亮地打印到指定的输出 306 //溪流。 307 func (c *Console) Evaluate(statement string) error { 308 defer func() { 309 if r := recover(); r != nil { 310 fmt.Fprintf(c.printer, "[native] error: %v\n", r) 311 } 312 }() 313 return c.jsre.Evaluate(statement, c.printer) 314 } 315 316 //Interactive启动一个交互式用户会话,在该会话中输入来自 317 // 318 func (c *Console) Interactive() { 319 var ( 320 prompt = c.prompt //当前提示行(用于多行输入) 321 indents = 0 //当前输入缩进数(用于多行输入) 322 input = "" //当前用户输入 323 scheduler = make(chan string) //发送下一个提示并接收输入的通道 324 ) 325 //启动Goroutine以侦听提示请求并返回输入 326 go func() { 327 for { 328 //读取下一个用户输入 329 line, err := c.prompter.PromptInput(<-scheduler) 330 if err != nil { 331 //如果出现错误,请清除提示或失败。 332 if err == liner.ErrPromptAborted { //Ctrl—C 333 prompt, indents, input = c.prompt, 0, "" 334 scheduler <- "" 335 continue 336 } 337 close(scheduler) 338 return 339 } 340 //检索用户输入、发送解释和循环 341 scheduler <- line 342 } 343 }() 344 //如果输入为空,我们需要退出时也监视ctrl-c 345 abort := make(chan os.Signal, 1) 346 signal.Notify(abort, syscall.SIGINT, syscall.SIGTERM) 347 348 //开始向用户发送提示并读取输入 349 for { 350 //发送下一个提示,触发输入读取并处理结果 351 scheduler <- prompt 352 select { 353 case <-abort: 354 //用户强制相当于控制台 355 fmt.Fprintln(c.printer, "caught interrupt, exiting") 356 return 357 358 case line, ok := <-scheduler: 359 //提示器返回用户输入,处理特殊情况 360 if !ok || (indents <= 0 && exit.MatchString(line)) { 361 return 362 } 363 if onlyWhitespace.MatchString(line) { 364 continue 365 } 366 //将行附加到输入并检查多行解释 367 input += line + "\n" 368 369 indents = countIndents(input) 370 if indents <= 0 { 371 prompt = c.prompt 372 } else { 373 prompt = strings.Repeat(".", indents*3) + " " 374 } 375 //如果所有需要的行都存在,请保存命令并运行 376 if indents <= 0 { 377 if len(input) > 0 && input[0] != ' ' && !passwordRegexp.MatchString(input) { 378 if command := strings.TrimSpace(input); len(c.history) == 0 || command != c.history[len(c.history)-1] { 379 c.history = append(c.history, command) 380 if c.prompter != nil { 381 c.prompter.AppendHistory(command) 382 } 383 } 384 } 385 c.Evaluate(input) 386 input = "" 387 } 388 } 389 } 390 } 391 392 //countindents返回给定输入的标识数。 393 //如果输入无效,例如var a=,结果可以是负数。 394 func countIndents(input string) int { 395 var ( 396 indents = 0 397 inString = false 398 strOpenChar = ' ' //跟踪字符串open char以允许var str=“i'm…..”; 399 charEscaped = false //如果前一个字符是'\'字符,则允许var str=“abc\”def; 400 ) 401 402 for _, c := range input { 403 switch c { 404 case '\\': 405 //在字符串中指示下一个字符为转义字符,而上一个字符不转义此反斜杠 406 if !charEscaped && inString { 407 charEscaped = true 408 } 409 case '\'', '"': 410 if inString && !charEscaped && strOpenChar == c { //结束字符串 411 inString = false 412 } else if !inString && !charEscaped { //开始字符串 413 inString = true 414 strOpenChar = c 415 } 416 charEscaped = false 417 case '{', '(': 418 if !inString { //在字符串中忽略括号,允许var str=“a”;而不缩进 419 indents++ 420 } 421 charEscaped = false 422 case '}', ')': 423 if !inString { 424 indents-- 425 } 426 charEscaped = false 427 default: 428 charEscaped = false 429 } 430 } 431 432 return indents 433 } 434 435 //执行运行指定为参数的javascript文件。 436 func (c *Console) Execute(path string) error { 437 return c.jsre.Exec(path) 438 } 439 440 //停止清理控制台并终止运行时环境。 441 func (c *Console) Stop(graceful bool) error { 442 if err := ioutil.WriteFile(c.histPath, []byte(strings.Join(c.history, "\n")), 0600); err != nil { 443 return err 444 } 445 if err := os.Chmod(c.histPath, 0600); err != nil { //强制0600,即使以前不同 446 return err 447 } 448 c.jsre.Stop(graceful) 449 return nil 450 }