scryinfo
Version:
A real data storage library on ethereum
292 lines (263 loc) • 10.9 kB
JavaScript
/**
en: normalized struct data for callback:
zh: callback或事件回调的参数, 是统一的格式:
{
st: ScryStatus.xx, // @see{ScryStatus}
msg: "", // error message. 错误或提示信息.
error: error object, // error object. 错误时,返回的error对象.
data: object // the business object. 返回的一些具体结果对象.
}
@author Danny Yan
@date 2017-08
*/
var path = require("path");
var fs = require("fs");
var Web3 = require("web3");
var util = require('util');
var events = require('events');
var base58 = require('base-58'); // npm install base-58
var concat = require('concat-stream');
var keythereum = require("keythereum");
var Tx = require('ethereumjs-tx');
var Status = require("./scryStatus.js");
/**
constructor
*/
function Scry(providerURL) {
var web3 = this.web3 = new Web3();
this.eth = web3.eth;
this.personal = web3.personal;
this.admin = web3.admin;
this.providerURL = providerURL;
this._accountsDirty = true;
this.accounts = this.getAccounts();
}
util.inherits(Scry, events.EventEmitter);
/**
en: Start scry system.
zh: 开始启动
@param callback function.
*/
Scry.prototype.start = function(callback) {
this.web3.setProvider(new Web3.providers.HttpProvider(this.providerURL, 0));
var isOK = this.web3.isConnected(); // 这里是同步阻塞
console.log("scry start successfully.");
callback(isOK);
};
/**
en: Get all accounts from keystore directory.
zh: 从keystore目录获取所有账号信息
@param strict boolean. If strict is true, will throw error when results are empty. Default value is false
@retrun Array<Ojbect>. Return a array.
*/
Scry.prototype.getAccounts = function(strict) {
if (this._accountsDirty == false) {
if (strict && this.accounts.length < 1) {
throw new Error("accounts is empty.");
}
return this.accounts;
}
var dir = path.resolve(__dirname + "/keystore/");
if (fs.existsSync(dir) == false) {
fs.mkdir(dir);
}
console.log(); // ? install by npm, it will be wrong on first run!!
var files = fs.readdirSync(dir);
var list = [];
for (var i = 0, len = files.length; i < len; i++) {
var data = fs.readFileSync(dir + "\\" + files[i]);
var keyObject = JSON.parse(data.toString());
list.push("0x" + keyObject.address);
}
this._accountsDirty = false;
this.accounts = list;
if (strict && list.length < 1) {
throw new Error("accounts is empty.");
}
return list;
};
/**
en: Get eth balance.
zh: 获取eth余额.
@param from string. account address.
@param toWei boolean. Default value is true
*/
Scry.prototype.getBalance = function(from, toWei) {
if (toWei == null) toWei = true;
var val = this.eth.getBalance(from).toNumber();
return toWei ? val : this.web3.fromWei(val);
};
/**
en: Get a instance of contract by address and abi.
zh: 通过地址和abi获取一个合约实例
@param address string.
@param abi string.
@return object.
*/
Scry.prototype.getContractInstance = function(address, abi) {
if (typeof(address) != "string" || typeof(abi) != "string") {
throw new Error("address or abi is null.");
}
var abiObj = typeof(abi) == "string" ? JSON.parse(abi) : abi;
var _contract = this.eth.contract(abiObj);
var _contractInst = _contract.at(address);
return _contractInst;
};
/**
en: watch event
zh: 监听事件
@param eventFunc object. web3 function object.
@param txHash string. 如果指定此值,则只有事件返回的transactionHash与其相等时才会触发回调
@param callback function.
*/
Scry.prototype.watchEvent = function(eventFunc, txHash, callback) {
if (eventFunc == null || typeof(eventFunc) != "function") {
throw new Error("eventFunc must be a function type.");
return;
}
var myEvent = eventFunc();
myEvent.watch(function(err, result) {
if (err == null) {
if (txHash == null) {
typeof(callback) == "function" && callback(Status.returnOK( result ));
} else if (result.transactionHash == txHash) {
typeof(callback) == "function" && callback(Status.returnOK( result ));
}
// console.log("myEvent:",result);
} else {
// console.log("myEvent err: ", err);
typeof(callback) == "function" && callback(Status.returnError( err.toString(), err ));
}
myEvent.stopWatching();
});
};
/**
en: Get the address logs.
zh: 获取一个指定地址上的日志(事件结果).但不包含事件的详细信息(合约中的事件参数)
@param address string.
@param fromBlock number|string. Start block number. Default value is 'latest'. 指定的起始区块号. 可不填,默认为"latest"
@param toBlock number|string. End block number. Default value is 'latest'. 指定的结束区块号. 可不填,默认为"latest"
@param callback function.
*/
Scry.prototype.getLogs = function(address, fromBlock, toBlock, callback) {
var filter = this.eth.filter({ address: address, fromBlock: fromBlock || "latest", toBlock: toBlock || 'latest' });
var myResults = filter.get(function(err, rs) {
if (err != null) {
typeof(callback) == "function" && callback(Status.returnError( err.toString(), err ));
return;
}
typeof(callback) == "function" && callback(Status.returnOK( rs ));
});
};
/**
en: Get contract's all events.
zh: 获取一个合约的所有日志. 包含事件的详细信息.
@param contractInst object. contract object.
@param fromBlock number|string. Start block number. Default value is 'latest'. 指定的起始区块号. 可不填,默认为"latest"
@param toBlock number|string. End block number. Default value is 'latest'. 指定的结束区块号. 可不填,默认为"latest"
@param callback function.
*/
Scry.prototype.getEvents = function(contractInst, fromBlock, toBlock, callback) {
var events = null;
events = contractInst.allEvents({ address: contractInst.address, fromBlock: fromBlock || "latest", toBlock: toBlock || 'latest' }, function(err, rs) {
if (err != null) {
typeof(callback) == "function" && callback(Status.returnError( err.toString(), err ));
return;
}
typeof(callback) == "function" && callback(Status.returnOK( rs ));
events.stopWatching();
});
};
/**
en: Read contents from file
zh: 从指定路径的文件中读取内容
@param p string. File path
*/
Scry.prototype.loadContentFromFile = function(p) {
p = path.resolve(p);
if (fs.existsSync(p) == false) return null;
var cnt = fs.readFileSync(p);
return cnt.toString();
};
/**
en: Call contract's function, use 'call'. It won't produce a transaction.
zh: 调用合约的非交易方法(同call). 一定会处理为指定一个from, 默认的from是accoutns[0]
@param options object. 格式为:
{
address:"", // Contract address. Don't have to fill in when set 'func'. 合约地址. func指定时,可不填
abi:"", // Contract abi code. Don't have to fill in when set 'func'. 合约abi. func指定时,可不填
funcName:"", // Contract function name. Don't have to fill in when set 'func'. 调用的合约方法名. func指定时,可不填
func: ojbect, // Contract function object. If you set this value, the aforementioned three values will invalid. contract的function对象,如果存在此值,则不用填写上述3个值
from:"", // Caller address. Default value is accounts[0]. 可不填, 默认accounts[0]
params:[] // The contract function's parameters array. 调用方法所需的参数. 可不填
}
@return
*/
Scry.prototype.contractCall = function(options) {
if (options == null) {
throw new Error("invalid options parameters.");
}
var func = options.func;
if (func == null) {
var address = options.address;
var abi = options.abi;
var funcName = options.funcName;
if (address == null || abi == null || funcName == null) {
throw new Error("invalid options parameters.");
}
var inst = this.getContractInstance(address, abi);
if (inst == null) {
throw new Error("contract is null.");
}
func = inst[funcName];
if (func == null) {
throw new Error("contract function '" + funcName + "' is null.");
}
}
params = options.params;
if (params != null && !(params instanceof Array)) {
throw new Error("params must be an instance of Array.");
}
params = params || [];
// 调用合约非交易方法时,必须指定from为当前账号,否则默认使用远程代理节点的账号
var from = options.from || this.getAccounts(true)[0];
params.push({ from: from });
return func.call.apply(func, params);
};
/**
en: Get contract function's data for payload.
zh: 生成执行合约方法所需的payloadData
@param object. Contract function object. 合约的方法对象
@param params Array. The contract function's parameters array. 合约方法所需的参数数组
*/
Scry.prototype.getPayloadData = function(funcObj, params) {
if (params != null && !(params instanceof Array)) {
throw new Error("deployed fail: options.params must be an instance of Arry.");
}
var payloadData = funcObj.getData.apply(funcObj, params);
return payloadData;
};
/**
en: Verify address's password is correct.
zh: 验证密码是否正确
@param address string.
@param password string.
@param callback function.
*/
Scry.prototype.verifyPass = function(address, password, callback) {
if (address == null || password == null) {
throw new Error("invalid parameters.");
}
var keyObject = keythereum.importFromFile(address, __dirname);
if (keyObject == null) {
typeof(callback) == "function" && callback(Status.returnError("account '" + address + "'s privatekey is not exist." ));
return;
}
var privateKey = keythereum.recover(password, keyObject);
if (privateKey == null) {
typeof(callback) == "function" && callback(Status.returnError("the address and password is mismatching." ));
} else {
typeof(callback) == "function" && callback(Status.returnOK("verify correctly."));
}
};
module.exports = Scry;