上次实验中我们通过在项目文件中定位并修改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]
连接选项:
--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,就到这里,休息,休息一下!
祝大家国庆快乐!