2018-10-31,MOAC推出官方公共节点,https://gateway.moac.io。
本文测试环境:Windows 10 + node Ver8.11.1 + chain3 Ver0.1.8。
1.介绍
moac gateway是一个托管的墨客节点集群,为用户提供公开的墨客区块链主网和测试网节点。
出于安全原因,gateway不管理用户的私钥,这意味着gateway不能代表用户签署交易。
因此,在使用chain3的过程中,不能通过sendTransaction、而只能使用sendRawTransaction发出交易。
1.1 主要特性(优势):
1.2 使用方法:
通过以下脚本,可以使用http://gateway.moac.io代替自己的Vnode节点。
之前使用代码连接本地Vnode节点方式:
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://localhost:8545'));
改为通过以下方式连接公共Vnode节点:
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://gateway.moac.io/testnet')); //For testnet
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://gateway.moac.io/mainnet')); //For mainnet
1.3 不足:
使用gateway跟使用本地Vnode节点相比,也有一些不足:
2.账号管理
moac gateway提供Vnode功能的时候,从安全角度考虑,没有启动personal,因此不能通过persoanl.newAccount()创建账号。
鉴于moac gateway需要私钥签名才能发送交易,这里给出管理墨客账号的部分代码。
2.1 安装依赖模块ethereumjs-wallet
在具有管理员权限的控制台中,运行以下命令安装ethereumjs-wallet。
C:\>npm install -g ethereumjs-wallet
此处注意:如果是在Windows上安装并且遇到错误,则可能需要安装Windows构建工具。
在具有管理员权限的控制台中,运行以下命令安装Windows构建工具,然后再尝试安装依赖模块。
C:\>npm install -g windows-build-tools
2.2 离线生成账号privateKey
方法一:node环境下运行以下代码,可以离线生成账号,分别得到私钥、公钥和地址。
var crypto = require('crypto'); //npm install -g crypto
var secp256k1 = require('secp256k1'); //npm install -g secp256k1
var keccak = require('keccak'); //npm install -g keccak
//获得随机的32个字节作为私钥,在使用中,请注意随机数来源的安全
var privateKey = crypto.randomBytes(32);
//获得公钥
var publicKey = secp256k1.publicKeyCreate(privateKey, false).slice(1);
//获得地址
var address = keccak('keccak256').update(publicKey).digest().slice(-20);
console.log('public key', publicKey.toString('hex'));
console.log('private key', privateKey.toString('hex'));
console.log('address', '0x' + address.toString('hex'));
方法二:node环境下运行以下代码,可以离线生成账号,得到私钥和地址。
var Wallet = require('ethereumjs-wallet'); //npm install -g ethereumjs-wallet
const wallet = Wallet.generate();
console.log("privateKey: " + wallet.getPrivateKeyString());
console.log("address: " + wallet.getAddressString());
运行结果:
2.3 从privateKey得到keystore
node环境下运行以下代码,可以从privateKey得到keystore文件内容:
var Wallet = require('ethereumjs-wallet');
var key = Buffer.from('6bee6f2a97fc1e2e9d9aa041dbdcebbd50c2ae3488070e01e47dcd38e5840ff8', 'hex'); //填入privateKey,没有0x
var wallet = Wallet.fromPrivateKey(key);
var keystore = wallet.toV3String('123456'); //填入要设置的密码,该密码可以从keystore得到privateKey
console.log("wallet: " + JSON.stringify(keystore));
运行结果:
将输出内容(wallet后面双引号中的内容),去掉所有“\”符号,新建一个文件,保存到D盘keystore目录下。
2.4 从keystore得到privateKey
从keystore得到privateKey需要提供账号密码。
node环境下运行以下代码,可以从keystore文件得到privateKey:
var keythereum = require("keythereum"); //npm install -g keythereum
var datadir = "D:"; //此处需默认保存在keystore目录下,也就是keystore文件实际保存在D:\keystore目录下
var address= "0xd3ab5cdba1d540fb049b88e05241b3393e52e230";
const password = "123456";
var keyObject = keythereum.importFromFile(address, datadir);
var privateKey = keythereum.recover(password, keyObject);
console.log(privateKey.toString('hex'));
运行结果:
正确地从keystore得到privateKey。
3.基础使用测试
使用公共节点,用户通常有些信息需要拿到,而不能像本地节点那样是当然知道的,包括节点版本信息及当前区块高度等。
3.1 版本信息
使用代码如下:
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('https://gateway.moac.io/mainnet'));
var version = chain3.version.api;
console.log(version);
var version = chain3.version.node;
console.log(version);
var version = chain3.version.network;
console.log(version);
输出结果:
前面是mainnet信息,后面是testnet信息,通常测试网的Vnode版本跟主网一样或比主网高,得到的结果均为当前最新发布版本。
3.2 区块高度
使用代码如下:
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://gateway.moac.io/mainnet'));
if(chain3.isConnected()){
console.log(chain3.mc.blockNumber);
}
输出结果:与浏览器同步较好。
以上测试,及部分chain3的基础使用(如获取账号balance等),使用方式与本地节点相同,均输出正确结果。
该步骤测试运行多次,快的时候1秒返回结果,慢的时候需要30多秒。
4.发送签名交易
此处测试过程使用本文第二节生成的账号私钥、地址。
先向该地址发送0.5个mc;然后运行以下代码,使用sendRawTransaction发送一个签名交易,该笔交易发送0.2个mc。
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://gateway.moac.io/mainnet'));
var address = "0xd3ab5cdba1d540fb049b88e05241b3393e52e230";
var account = {address:"0xd3ab5cdba1d540fb049b88e05241b3393e52e230",secret:"6bee6f2a97fc1e2e9d9aa041dbdcebbd50c2ae3488070e01e47dcd38e5840ff8"};
var toAddress = "0x68986c1bcd54ae5dae69310fc64ea544ff1d56c4";
var amount = 0.2;
send(chain3, account.address, account.secret, toAddress, amount, txCount = -1)
function send(chain3, fromAddress, fromSecret, toAddress, amount, txCount = -1){
var mc = chain3.mc;
var txcount = txCount >= 0 ? txCount : chain3.mc.getTransactionCount(fromAddress);
console.log("Get tx account", txcount);
var gasPrice = 25000000000;
var gasLimit = 100000;
var value = chain3.toSha(amount, 'mc');
var gasTotal = gasPrice * gasLimit + Number(value);
console.log(gasPrice, gasLimit, value, chain3.fromSha(gasTotal, 'mc'));
var rawTx = {
from: fromAddress,
to: toAddress,
nonce: chain3.intToHex(txcount),
gasPrice: chain3.intToHex(gasPrice),
gasLimit: chain3.intToHex(gasLimit),
value: chain3.intToHex(value),
shardingFlag: 0, //default is global contract
chainId: chain3.version.network
};
var signedTx = chain3.signTransaction(rawTx, fromSecret);
mc.sendRawTransaction(signedTx, function(err, hash) {
if (!err){
console.log("succeed: ", hash);
return hash;
}else{
console.log("error:", err);
console.log('raw tx:', rawTx);
}
});
}
输出结果:
该步骤测试运行多次,均成功返回结果。
到浏览器查询签名交易发送情况:
5.调用合约
为了测试的方便,该步骤使用一个已经部署好的erc20合约。
在对合约的调用中,有很多调用不需要使用私钥签名,比如获取标准erc20合约的基本属性和balance的调用。
5.1 给新账号发送token
给新账号0xd3ab5cdba1d540fb049b88e05241b3393e52e230发送100个KFZT(标准erc20 token,合约地址看代码)。
5.2 查询账号里的token余额
使用gateway节点,node环境运行以下代码,实现对合约的调用,该调用会查询到账号的token余额,不需要签名。
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://gateway.moac.io/mainnet'));
var contractAddress = "0xA2580D58A58998ca06e6f5b2A96A95D8d48f1679";
var address = "0xd3ab5cdba1d540fb049b88e05241b3393e52e230";
var abiString = '[ { "constant": true, "inputs": [], "name": "name",.......... { "indexed": true, "name": "_spender", "type": "address" }, { "indexed": false, "name": "_value", "type": "uint256" } ], "name": "Approval", "type": "event" } ]';
//调用erc20合约
//基本属性
callContract2(chain3, contractAddress, address, abiString);
function callContract2(chain3, contractAddress, address, abiString){
var abi = JSON.parse(abiString);
var contract = chain3.mc.contract(abi);
var token = contract.at(contractAddress);
console.log(JSON.stringify(token.totalSupply()));
console.log(JSON.stringify(token.name()));
console.log(JSON.stringify(token.decimals()));
console.log(JSON.stringify(token.symbol()));
}
//调用erc20合约
//查询余额
callContract(chain3, contractAddress, address, abiString);
function callContract(chain3, contractAddress, address, abiString){
var abi = JSON.parse(abiString);
var contract = chain3.mc.contract(abi);
var token = contract.at(contractAddress);
token.balanceOf.call(address, function(err, result){
console.log(err, JSON.stringify(result));
});
}
输出结果:
该步骤测试运行多次,合约的四个属性都在1秒内返回,而账号token的balance值返回结果均在30多秒。
5.2 发送token
使用gateway节点,node环境运行以下代码,实现对合约的调用,将10个KFZT发回原账号,该调用需要私钥签名。
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://gateway.moac.io/mainnet'));
var contractAddress = "0xA2580D58A58998ca06e6f5b2A96A95D8d48f1679";
var address = "0xd3ab5cdba1d540fb049b88e05241b3393e52e230";
var account = {address:"0xd3ab5cdba1d540fb049b88e05241b3393e52e230",secret:"6bee6f2a97fc1e2e9d9aa041dbdcebbd50c2ae3488070e01e47dcd38e5840ff8"};
var abiString = '[ { "constant": true, "inputs": [], "name": "name",...... { "indexed": true, "name": "_spender", "type": "address" }, { "indexed": false, "name": "_value", "type": "uint256" } ], "name": "Approval", "type": "event" } ]';
//调用erc20合约
//发送token
var amount = 10; //token的最小单位
var anotherAddress = "0x68986c1BCD54Ae5dAe69310fC64Ea544FF1D56C4";
callContract1(chain3, contractAddress, account, abiString, anotherAddress, amount);
function callContract1(chain3, contractAddress, account, abiString, anotherAddress, amount){
var address = account.address;
var abi = JSON.parse(abiString);
var contract = chain3.mc.contract(abi);
var token = contract.at(contractAddress);
var data = token.transfer.getData(anotherAddress, amount);
console.log('data', data);
var txCount = chain3.mc.getTransactionCount(account.address);
var rawTx = {
nonce: chain3.intToHex(txCount),
gasPrice: chain3.intToHex(25000000000),
gasLimit: chain3.intToHex(100000),
to: contractAddress,
data: data,
chainId: chain3.version.network
};
var signedTx = chain3.signTransaction(rawTx, account.secret);
chain3.mc.sendRawTransaction(signedTx, function(err, hash) {
if (!err){
console.log("succeed: ", hash);
var filter = chain3.mc.filter('latest');
filter.watch(function(error, result) {
var receipt = chain3.mc.getTransaction(hash);
if (!error && receipt && receipt.blockNumber != null) {
console.log("done.");
filter.stopWatching();
process.exit(0);
}
});
}else{
console.log("error:", err.message);
}
});
}
输出结果:
该步骤测试运行多次,输出结果至succeed在2秒左右,至done均在60秒以上。
6.使用RPC
此处测试简单的RPC调用:
var request = require('request');
var url = "http://gateway.moac.io/mainnet";
var requestData = {"jsonrpc":"2.0","method":"mc_gasPrice","params":[],"id":99};
httprequest(url,requestData);
function httprequest(url,data){
request({
url: url,
method: "POST",
json: true,
body: data
}, function(error, response, result) {
if (!error && response.statusCode == 200) {
console.log(result) // 请求成功的处理逻辑
}
});
};
输出结果:
该步骤测试运行多次,返回结果在2秒内。
其他json rpc的使用参考:https://github.com/MOACChain/moac-core/wiki/JSON-RPC。
本次测试结论:
1.对于许可的功能(主要是RPC和Chain3的调用),可以提供最新版的、稳定的墨客节点功能;
2.实测中,所有查询及签名交易均成功;
3.实测中,与本地节点比较,有延迟现象存在,但没有出现无法连接Vnode的情况。