Web3.js是一套用Javascript实现的API,用于与以太坊节点进行通信,并通过以太坊节点操作以太坊网络。Web.js内部使用JSON-RPC协议与以太坊节点(geth和其他类型的节点)进行通信。
JSON-RPC是一个无状态且轻量级的远程过程调用(RPC)协议。该协议主要定义了一些数据结构及其相关的处理规则。允许运行在基于Socket、HTTP等诸多不同消息传输环境的同一进程中使用JSON作为数据格式。
Web3.js将所有的JSON-RPC封装成Javascript API。Web3.js可以与所有种类的、支持JSON-RPC协议的以太坊节点通信。
首先需要安装Node.js。
brew install node
其次安装Web3.js。
brew install web3
安装完Web3.js,在终端执行node命令进入Node.js交互环境(REPL),然后输入:
require("web3")
Web3.js不仅可以通过HTTP与以太坊节点连接,还可以通过IPC方式与以太坊节点连接。HTTP连接方式通过HttpProvider对象指定连接信息,IPC方式需要通过IPCProvider对象指定连接信息。
IPCProvider类的构造方法需要一个IPC文件,在启动geth节点后,从日志输出中可以找到这个IPC文件,本例是geth.ipc。
关于以太坊私有链的搭建参见【干货】Mac上搭建以太坊私有网络
从日志信息可以看出,笔者的geth.ipc文件路径是/Users/andrewfeng/Documents/Ethereum/myeth/chaindata/geth.ipc,读者可将其替换为自己的目录。
接下来我们测试同时使用HTTP和IPC方式与geth节点连接,并调用getAccount函数,用异步的方式获取geth节点的账户。
编写javascript脚本文件connect_http_ipc.js如下:
var Web3 = require("web3");
var net = require('net');
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
web3.eth.getAccounts(
function(error, response)
{
console.log(response)
}
)
web3 = new Web3(new Web3.providers.IpcProvider('/Users/andrewfeng/Documents/Ethereum/myeth/chaindata/geth.ipc',net));
web3.eth.getAccounts(
function(error, response)
{
console.log(response)
}
)
如果通过geth节点或其他节点发布智能合约,不能通过Remix直接部署,而是需要使用相应的API发布智能合约。发布智能合约需要使用contract.new方法,而且必须先将智能合约源码文件(.sol文件)编译成二进制问价(.bin)才能发布。
将.sol文件编译成EVM字节码的工作是在客户端完成的,而不是在以太坊节点或以太坊网络中完成的。
用Solidity语言编写一个简单的计算阶乘的智能合约程序Factorial.sol.
pragma solidity ^0.6.3;
contract Factorial {
function factorial(uint n) public returns(uint){
if (n == 0 || n == 1)
return 1;
else
return n * factorial(n-1);
}
}
这里可以使用强大的IDEA作为开发工具,安装插件Intellij-Solidity,即可编写Solidity代码。但还需要安装Solidity编译器。
Solidity编译器是用于编译Solidity源代码文件的,可以将其编译为多种目标文件。因此需要安装solc,但命令行工具是solcjs。使用如下命令安装:
npm install -g solc
例如,使用如下命令可将Factorial.sol文件编译生成abi文件。
solcjs --abi Factorial.sol
但我们可以将其集成进IDEA的扩展工具中。打开Preferences,找到Tools > External Tools,点击”+“按钮,填入如下参数。
通过执行Tools > External Tools > solidity即可编译执行。在项目的out/product目录下生成编译文件。
使用Web3.js API发布智能合约,首先要使用fs模块中的API装载Factorial_sol_Factorial.abi和Factorial_sol_Factorial.bin文件。
装载完.bin和.abi文件后,在使用contract.new方法发布智能合约之前,要先使用web3.personal.unlockAccount函数解锁用于发布智能合约的账户,否则该账户不能用来发布智能合约,也不能用来转账。最后通过contract.new方法异步方式发布智能合约。
编写javascript脚本文件install_contract.js如下:
var Web3 = require("web3");
var fs = require("fs");
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
var code = '0x' + fs.readFileSync("Factorial_sol_Factorial.bin").toString();
var abi = JSON.parse(fs.readFileSync("Factorial_sol_Factorial.abi").toString());
var contract = web3.eth.contract(abi);
console.log('account balance:' + web3.eth.getBalance(web3.eth.accounts[0]))
web3.personal.unlockAccount(web3.eth.accounts[0],"3336515")//第二个参数为账户密码
var contract = contract.new({from: web3.eth.accounts[0], data: code, gas: 500000},
function(e, contract){
if(!contract.address) {
console.log("已经发起交易,交易地址:" + contract.transactionHash + "\n正在等待挖矿");
} else {
console.log("智能合约部署成功,地址:" + contract.address);
}
}
)
核心是contract.new方法。该方法通过异步方式发布智能合约,通过第一个参数传递与智能合约相关的信息。尤其要指明的是gas属性,该属性需要指定发布智能合约需要付出的代价,单位是gas,这时以太坊的内部计量单位。
要想知道发布某个智能合约大概需要多少gas,可以使用web3.eth.estimateGas函数预估。
var gasValue = web3.eth.estimateGas({data:code})
console.log('gas:' + gasValue)
要想将智能合约发布在以太坊上,还需要挖矿,挖出一个区块才执行。
miner.start(2);admin.sleepBlocks(1);miner.stop()
调用智能合约并不需要装载Factorial_sol_Factorial.bin文件,秩序装载Factorial_sol_Factorial.abi文件即可。.abi文件是普通的文本文件,用于描述智能合约的名字、函数、参数类型等信息。
调用智能合约有两种方式:本地方式和以太坊网络方式。通过本地方式调用智能合约API,会直接返回计算结果;通过以太坊网络方式调用智能合约,结果都保存在以太坊网络中。
编写javascript脚本文件install_contract.js如下:
var Web3 = require("web3");
var fs = require("fs");
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
var abi = JSON.parse(fs.readFileSync("Factorial_sol_Factorial.abi").toString());
var contract = web3.eth.contract(abi);
var instance = contract.at('0x5c2a7ba57cd87fcf0918ab262a67caaf4bc9135e')
web3.personal.unlockAccount(web3.eth.accounts[0],"3336515")
console.log(instance.factorial.call(10).toString())
console.log(instance.factorial(10,{from:web3.eth.accounts[0],gas:8000000}))
可以直接使用Node.js的solc模块将编译智能合约的功能嵌入到程序中。
首先安装solc模块:
npm install solc
然后可以使用下面的代码引用solc模块:
var solc = require('solc');
编写Greeter智能合约:
pragma solidity ^0.6.3;
contract Greeter {
string public greeting;
function Greeter(){
greeting = "Hello World!";
}
function setGreeting(string _greeting) public {
greeting = _greeting;
}
function getGreeting() constant returns (string) {
return greeting;
}
}
编写javascript脚本文件greeter.js如下:
var Web3 = require("web3");
var solc = require('solc');
var fs = require("fs");
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
var code = fs.readFileSync("Greeter.sol").toString();
var input = {
'Greeter.sol': code,
}
var output = solc.compile({ sources: input });
var abi = JSON.parse(output.contracts['Greeter.sol:Greeter'].metadata).output.abi;
web3.personal.unlockAccount(web3.eth.accounts[0],"3336515")
var contract = web3.eth.contract(abi);
contract.new({from: web3.eth.accounts[0], data: '0x' + output.contracts['Greeter.sol:Greeter'].bytecode, gas: 200000},
function(e, contractData){
if(!contractData.address) {
console.log("已经发起交易,交易地址:" + contractData.transactionHash + "\n正在等待挖矿");
} else {
console.log("智能合约部署成功,地址:" + contractData.address);
var instance = contract.at(contractData.address);
console.log(instance.greet())
instance.setGreeting('你好', transact={'from': web3.eth.accounts[0]},
function(e,data)
{
console.log(instance.greet())
}
)
}
}
)