上次实验中我们通过在项目文件中定位并修改eth 端口访问方式,实现了将eth服务与truffle合约服务分离的目的。通过上次实验,我们可以在tru-host本机实现webpack的转账服务。但截至目前,我们发现浏览器中只能通过http://localhost:8080实现合约服务的访问,不仅其他主机无法通过IP地址来访问tru-host:8080,甚至在tru-host主机本身直接使用192.168.3.103:8080访问合约服务都会招到拒绝(返回“ERR_CONNECTION_REFUSED”)。
今天我们这个实验的目的就是解决上述问题,实现tru-host主机可以对外服务。
假设大家和我一样对nodejs的工作机制不太熟悉(如果熟悉的话,其实直接可以看跳过以下比较绕的一段内容,直接看最后一步...),延续之前的研究思路,由表及里的来分析问题。
0. 提前规划
本实验希望达成以下目的:
(1) 希望局域网内的所有人可以通过tru-host虚机的IP地址(192.168.3.103)访问合约服务(MetaCoin转账服务);
(2) 希望合约服务的端口由原来的8080变更为8088。
(3) 给局域网内任何一台处于eth测试网络(eth-host:8545)的账户(并非在testrpc自动生成的账号)上转2000个MetaCoin!
1. 了解truffle项目的启动方式
truffle项目在完成migrate之后,需执行npm run dev实现本地8080端口的监听。
在此,我们需要对nodejs项目结构和npm执行机制要做一个简单的了解。npm是随同NodeJS一起安装的包管理工具,方便用户在nodejs下实现包的安装、执行和卸载。npm在之前如何安装ethereumjs-testrpc和truffle时,大家应该有所接触了,但今天重点涉及的是npm的脚本执行的配置机制。
在nodejs项目框架中,往往存在这么几个目录和配置文件:
(1) app:存放javascript(js脚本)、stylesheets(css脚本)以及index.html,熟悉网站结构的朋友应该对上述结构很清楚了。
(2) node_modules:存放项目中依赖的包。
(3) package.json:项目包文件定义、依赖关系的定义以及npm可供加载的执行脚本指令的定义,可以让我们非常方便的使用NPM进行项目的发布安装。
刚才提及的npm run dev,实际执行的就是package.json文件中定义在scripts中的"dev"的指令。
也就是说,当我们执行npm run dev时,就相当于等同执行了 webpack-dev-server这么一行指令....但这个”webpack-dev-server“貌似和可执行的脚本貌似不太搭界啊。
2. 深入了解npm run
为此我们有必要进一步了解下npm run的工作机制。npm 允许在package.json文件里面,使用scripts字段定义脚本命令。scripts字段定义的脚本命令,可以是清晰指定的具体的脚本加行参,也可以是默认指定目录下的脚本link。npm run dev时,会npm自动访问package.json并在scripts中解析获取dev具体的脚本命令"webpack-dev-server",并在临时创建的path=./node_modules/.bin/目录下找到"webpack-dev-server" link指向的脚本并加载执行。
我们顺藤摸瓜,可以找到./node_modules/.bin目录下,可以尝试直接执行下该指令:
cd ./node_modules/.bin ./webpack-dev-server
完成到这一步,我们算是找到了在执行 npm run dev真正的执行脚本了。按这个思路,大家还可以摸索下package.json中scripts中的其他命令。
更多有关npm scripts的说明,详见:http://www.ruanyifeng.com/blog/2016/10/npm_scripts.html
3. 了解webpack启动参数
./webpack-dev-server --help在linux下要习惯使用 --help查询指令的使用方法...看看是否是否有办法实现域名和服务端口的配置:
webpack-dev-server 2.9.1 webpack 2.7.0 Usage: https://webpack.js.org/configuration/dev-server/ Config options: --config Path to the config file [string] [default: webpack.config.js or webpackfile.js] --env Environment passed to the config, when it is a function Basic options: --context The root directory for resolving entry point and stats [string] [default: The current directory] --entry The entry point [string] --watch, -w Watch the filesystem for changes [boolean] --debug Switch loaders to debug mode [boolean] --devtool Enable devtool for better debugging experience (Example: --devtool eval-cheap-module-source-map) [string] -d shortcut for --debug --devtool eval-cheap-module-source-map --output-pathinfo [boolean] -p shortcut for --optimize-minimize --define process.env.NODE_ENV="production" [boolean] --progress Print compilation progress in percentage [boolean] Module options: --module-bind Bind an extension to a loader [string] --module-bind-post [string] --module-bind-pre [string] Output options: --output-path The output path for compilation assets [string] [default: The current directory] --output-filename The output filename of the bundle [string] [default: [name].js] --output-chunk-filename The output filename for additional chunks [string] [default: filename with [id] instead of [name] or [id] prefixed] --output-source-map-filename The output filename for the SourceMap [string] --output-public-path The public path for the assets [string] --output-jsonp-function The name of the jsonp function used for chunk loading [string] --output-pathinfo Include a comment with the request for every dependency (require, import, etc.) [boolean] --output-library Expose the exports of the entry point as library [string] --output-library-target The type for exposing the exports of the entry point as library [string] Advanced options: --records-input-path Path to the records file (reading) [string] --records-output-path Path to the records file (writing) [string] --records-path Path to the records file [string] --define Define any free var in the bundle [string] --target The targeted execution environment [string] --cache Enable in memory caching [boolean] [default: It's enabled by default when watching] --watch-stdin, --stdin close when stdin ends [boolean] --watch-aggregate-timeout Timeout for gathering changes while watching --watch-poll The polling interval for watching (also enable polling) [boolean] --hot Enables Hot Module Replacement [boolean] --prefetch Prefetch this request (Example: --prefetch ./file.js) [string] --provide Provide these modules as free vars in all modules (Example: --provide jQuery=jquery) [string] --labeled-modules Enables labeled modules [boolean] --plugin Load this plugin [string] --bail Abort the compilation on first error [boolean] --profile Profile the compilation and include information in stats [boolean] --hot-only Do not refresh page if HMR fails [boolean] Resolving options: --resolve-alias Setup a module alias for resolving (Example: jquery-plugin=jquery.plugin) [string] --resolve-extensions Setup extensions that should be used to resolve modules (Example: --resolve-extensions .es6 .js) [array] --resolve-loader-alias Setup a loader alias for resolving [string] Optimizing options: --optimize-max-chunks Try to keep the chunk count below a limit --optimize-min-chunk-size Try to keep the chunk size above a limit --optimize-minimize Minimize javascript and switches loaders to minimizing [boolean] Stats options: --color, --colors Enables/Disables colors on the console [boolean] [default: (supports-color)] --info Info [boolean] [default: true] --quiet Quiet [boolean] --client-log-level Log level in the browser (info, warning, error or none) [string] [default: "info"] SSL options: --https HTTPS [boolean] --key Path to a SSL key. [string] --cert Path to a SSL certificate. [string] --cacert Path to a SSL CA certificate. [string] --pfx Path to a SSL pfx file. [string] --pfx-passphrase Passphrase for pfx file. [string] Response options: --content-base A directory or URL to serve HTML content from.[string] --watch-content-base Enable live-reloading of the content-base. [boolean] --history-api-fallback Fallback to /index.html for Single Page Applications. [boolean] --compress Enable gzip compression [boolean] Connection options: --port The port --disable-host-check Will not check the host [boolean] --socket Socket to listen --public The public hostname/ip address of the server [string] --host The hostname/ip address the server will bind to [string] [default: "localhost"] --allowed-hosts A comma-delimited string of hosts that are allowed to access the dev server [string] Options: --help, -h Show help [boolean] --version, -v Show version number [boolean] --bonjour Broadcasts the server via ZeroConf networking on start[boolean] --lazy Lazy [boolean] --inline Inline mode (set to false to disable including client scripts like livereload) [boolean] [default: true] --open Open the default browser, or optionally specify a browser name [string] --useLocalIp Open default browser with local IP [boolean] --open-page Open default browser with the specified page [string]
留意108~116行是有关连接的选项,简单翻译一下:
连接选项: --port 端口 --disable-host-check 禁止检查主机端口 [布尔值:true/false] --socket 用于监听的Socket --public 用于公共访问的域名或IP地址 [字符串] --host 绑定到本机的域名或IP地址 [字符串] [缺省: "localhost"] --allowed-hosts 使用西文逗号分割的允许访问的主机域名或IP列表 [字符串]
由此,我们总算不用像实验一一样需要跑到代码中去修改eth服务的主机名那样麻烦了,直接可以通过webpack-dev-server指令的传参即可完成合约服务器的ip与端口的变更。
4. 修改package.json
回到项目目录,编辑package.json,将scriptes/dev的值修改为:"webpack-dev-server --host tru-host --public 192.168.3.103 --port 8088 --disable-host-check true",如下图:
5. 重新启动webpack合约服务器
truffle migrate npm run dev
说明合约服务已正常启动。
(1) 验证本机服务访问:回到tru-host虚机,在浏览器上输入localhost:8080,可以发现访问失败了。原因是我们已把绑定的域名端口变为了http://tru-host:8088,尝试新的域名发现可以正常访问。
(2) 验证局域网其他机器的访问:在IP:192.168.3.36主机上访问http://192.168.3.103:8088,发现并没有出现像tru-host本机访问时出现的10000个MetaCoin币。
(3) 安装MetaMask插件
为了进一步验证该服务是OK的,我们安装一个chrome插件:MetaMask,然后按指引完成账户的注册,ethereum网络会给大家生成一个免费的账户。
在登录的状态下点击下这个插件,左上角可以看到网络切换,选择”Custom RPC“并填入之前eth-host主机的IP地址和端口(http://192.168.3.102:8545),点击”save“保存eth网络设置;回到网络选择页面,选择刚才配置的eth网络。
然后回到插件主页面,选择”...“菜单,找到”copy Address to clipboard“
OK,现在我们已经在本地(192.168.3.36)通过已接入到我们的eth-host的eth网络可以查询账号余额了,并且我们通过MetaMask插件拷贝出了当前插件钱包中的eth账号了。
接下来,我们就往这个账号里面转2000个MetaCoin吧!
(4) 回到tru-host主机(当前仅仅这个主机账号上有10000个MetaCoin),在浏览器中”Amount“框里面填:2000;在下面的”To Address“的框里,粘贴刚才复制的账号,点击按键”Send MetaCoin“,可以看到在tru-host主机上执行转账成功了!
(5) 回到192.168.3.36主机上,在安装有MetaMask插件的浏览器上重新访问http://192.168.3.103:8088,可以发现我们已经有2000个MetaCoin了!
今天的实验就到此为止。总结下:
1. 回顾之前的truffle migrate操作,我们可以发现不仅仅做了eth网络的绑定还从eth网络中取回了第一个账本(上面有10000个MetaCoin)并将此账本写入到了项目中;
2. 可以通过package.json文件的scripts配置的指令,在./node_modules/.bin目录下找到执行的指令,并通过 --help 来获取传参并加以应用;
3. 智能合约的使用,需要账本的支持;而账本的支持,又要需要本地钱包的支撑。目前,钱包的形态也有很多种,像浏览器插件的方式可能是最轻量级的钱包应用了。
4. 其实完成了最后一步操作后,细心的朋友会发现我们的插件钱包MetaMask里面的ETH币值并未变化,还是零。我想可能是测试环境下并非产生真实币值是原因之一,此外,测试环境中缺乏挖矿节点形成51%的记账效果估计也是重要因素。
OK,就到这里,休息,休息一下!
祝大家国庆快乐!