NODECOIN SOURCE CODE GITHUB
EARLIER VERSION
function run(appDir) {
EARLIER VERSION
function run(appDir) {
var http = require("http"),
querystring = require("querystring"),
events = require("events"),
util = require("util"),
Step = require("step"),
async = require("async"),
long = require("long"),
Map = require("collections/map"),
SortedArrayMap = require("collections/sorted-array-map"),
crypto = require("crypto"),
extend = require("node.extend"),
curve = require("node-curve25519"),
Datastore = require("nedb"),
db = new Datastore(),
net = require("net"),
swig = require("swig"),
path = require("path"),
fs = require("fs"),
mime = require("mime"),
url = require("url"),
longInt = require("long"),
request = require("request"),
bigint = require("biginteger").BigInteger;
var logger = function() {
var winston = require("winston");
var loggingLevels = {
levels: {
netdbg: 1,
bchdbg: 0,
rpcdbg: 0,
scrdbg: 0,
DBdbg: 1,
transdbg: 1,
debug: 1,
info: 10,
notice: 20,
warn: 30,
error: 40,
crit: 50,
alert: 60,
emerg: 70
},
colors: {
netdbg: "blue",
bchdbg: "blue",
rpcdbg: "blue",
scrdbg: "blue",
transdbg: "blue",
debug: "blue",
info: "green",
notice: "cyan",
warn: "yellow",
error: "red",
crit: "red",
alert: "yellow",
emerg: "red",
DBdbg: "blue"
}
};
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var pad = function(n) {
return n < 10 ? "0" + n.toString(10) : n.toString(10)
};
var pad3 = function(n) {
var num = n.toString(10);
while (num.length < 3) num = "0" + num;
return num
};
var timestamp = function() {
var d = new Date();
var time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join(":");
time += "." + pad3(d.getMilliseconds());
return [d.getDate(), months[d.getMonth()], time].join(" ")
};
var exports = {};
var logger = exports.logger = new winston.Logger({
transports: [new winston.transports.Console({
colorize: true,
level: "debug",
timestamp: timestamp
}), new winston.transports.File({
level: "debug",
timestamp: timestamp,
filename: process.cwd() + "/logs/logs.log",
maxsize: 1048576,
maxFiles: 2
})],
levels: loggingLevels.levels,
level: "debug"
});
winston.addColors(loggingLevels.colors);
exports.loggerOn = true;
exports.disable = function() {
if (this.logger.loggerOn === false) {
return
}
this.logger.loggerOn = false;
this.logger.remove(winston.transports.Console);
this.logger.remove(winston.transports.File)
};
return logger.extend(exports)
}();
bigint.fromBuffer = function(buf) {
var strHex = buf.toString("hex");
return bigint.parse(strHex.toLocaleUpperCase(), 16)
};
bigint.prototype.toBuffer = function() {
var hexStr = this.toString(16);
if (hexStr.length % 2 === 0) {
return new Buffer(hexStr, "hex")
}
return new Buffer("0" + hexStr, "hex")
};
var Crypto = function() {};
Crypto.getPublicKey = function(secretWord) {
try {
var secretWordHash = curve.sha256(secretWord);
return curve.derivePublicKeyWithClamp(secretWordHash)
} catch (e) {
logger.error(e.stack ? e.stack : e.toString());
return false
}
};
var Utils = function() {
var clearBuffer = function(buf) {
for (var i = 0; i < buf.length; i++) {
buf[i] = 0
}
return buf
};
var two64 = new bigint("18446744073709551616");
var getDateTime = function() {
var date = new Date();
var hour = date.getHours();
hour = (hour < 10 ? "0" : "") + hour;
var min = date.getMinutes();
min = (min < 10 ? "0" : "") + min;
var sec = date.getSeconds();
sec = (sec < 10 ? "0" : "") + sec;
var year = date.getFullYear();
var month = date.getMonth() + 1;
month = (month < 10 ? "0" : "") + month;
var day = date.getDate();
day = (day < 10 ? "0" : "") + day;
return year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec
};
var decodeHex = function(hex) {
return new Buffer(hex, "hex").slice(0)
};
var longToBigInt = function(long) {
return bigint(long.toString())
};
var bigIntToLong = function(bigIntVal) {
var buf = bigIntVal.toBuffer();
return bufferToLongLE(buf)
};
var bigIntToLongBE = function(bigIntVal) {
var buf = bigIntVal.toBuffer();
return bufferToLongBE(buf)
};
var stringToLong = function(str) {
var bi = bigint(str);
return bigIntToLongBE(bi)
};
var bufferToLongLE = function(buf) {
if (buf.length < 8) {
var addLength = 8 - buf.length;
var addBuffer = new Buffer(addLength);
addBuffer = clearBuffer(addBuffer);
buf = Buffer.concat([addBuffer, buf], 8)
}
var lowBits = buf.slice(buf.length - 4, buf.length);
var higthBits = buf.slice(buf.length - 8, buf.length - 4);
return new longInt(lowBits.readInt32LE(0), higthBits.readInt32LE(0), true)
};
var bufferToLongBE = function(buf) {
if (buf.length < 8) {
var addLength = 8 - buf.length;
var addBuffer = new Buffer(addLength);
addBuffer = clearBuffer(addBuffer);
buf = Buffer.concat([addBuffer, buf], 8)
}
var lowBits = buf.slice(buf.length - 4, buf.length);
var higthBits = buf.slice(buf.length - 8, buf.length - 4);
return new longInt(lowBits.readInt32BE(0), higthBits.readInt32BE(0), true)
};
var getBEBigIntFromLENumber = function(num) {
if (typeof num !== "string") {
num = mun.toString()
}
var bi = bigint(num);
var buf = bi.toBuffer();
var buffArr = [];
for (var i = 0; i < buf.length; i++) {
buffArr.push(buf[buf.length - i - 1])
}
buf = new Buffer(buffArr);
return bigint.fromBuffer(buf)
};
var sha256 = function(message) {
var shasum = crypto.createHash("sha256");
shasum.update(message);
return shasum.digest()
};
var roundTo5Float = function(num) {
var numF = parseFloat(nullToNumber(num));
return Math.round(numF * 1e5) / 1e5
};
var isEmptyObj = function(obj) {
if (obj == null) return true;
if (obj.length && obj.length > 0) return false;
if (obj.length === 0) return true;
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) return false
}
return true
};
var nullToNumber = function(val) {
if (val === null) {
return 0
}
return val
};
return {
clearBuffer: clearBuffer,
two64: two64,
getDateTime: getDateTime,
decodeHex: decodeHex,
longToBigInt: longToBigInt,
bigIntToLong: bigIntToLong,
bigIntToLongBE: bigIntToLongBE,
stringToLong: stringToLong,
bufferToLongLE: bufferToLongLE,
bufferToLongBE: bufferToLongBE,
getBEBigIntFromLENumber: getBEBigIntFromLENumber,
sha256: sha256,
roundTo5Float: roundTo5Float,
isEmptyObj: isEmptyObj,
nullToNumber: nullToNumber
}
}();
var Config = function() {
var Config = function() {
return {
port: 19775,
host: "127.0.0.1",
starPeers: ["5.45.124.65:19776", "5.45.114.108:19776"],
peerPort: 19776,
serverKey: "qwerty123456",
devMod: false
}
}();
try {
Config.GEN_BLOCK_WAIT_MS = 6e4;
Config.two64 = new bigint("18446744073709551616");
Config.NULL_HASH = Utils.clearBuffer(new Buffer(32));
Config.EMPTY_BUFFER = new Buffer(0);
Config.ZERO_VALUE = Utils.clearBuffer(new Buffer(8));
Config.INT64_MAX = Utils.decodeHex("ffffffffffffffff");
Config.MAX_BALANCE = 1e9;
Config.COIN = 1e9;
Config.INITIAL_BASE_TARGET = 153722867;
Config.TRANSPARENT_FORGING_BLOCK = 3e4;
Config.MAX_BASE_TARGET = Config.MAX_BALANCE * Config.INITIAL_BASE_TARGET
} catch (e) {
logger.error("Error while generating utility constants:\n" + (e.stack ? e.stack : e.toString()));
process.exit(1)
}
return Config
}();
function NodeServer(createNew) {
events.EventEmitter.call(this);
this.state = null;
this.running = false;
this.synced = false;
this.intervalCheckSynced = null;
this.netStatus = Peer.prototype.statuses.DISABLE;
try {
this.blockGenerator = new BlockGenerator(this);
this.peerProcessor = new PeerProcessor(this);
this.addListener("stateChange", this.handleStateChange.bind(this));
this.setupStateTransitions()
} catch (err) {
logger.error("Initialization " + (err.stack ? err.stack : "error: " + err.toString()))
}
if (typeof createNew !== "undefined" && createNew) {
return this
}
if (typeof NodeServer.instance === "undefined") {
NodeServer.instance = this
}
return NodeServer.instance
}
util.inherits(NodeServer, events.EventEmitter);
NodeServer.prototype.start = function() {
this.setState("init")
};
NodeServer.prototype.setState = function(newState) {
var oldState = this.state;
if (newState == "init" && oldState !== null) {
return
}
this.state = newState;
this.emit("stateChange", {
oldState: oldState,
newState: newState
})
};
NodeServer.prototype.setupStateTransitions = function() {
var self = this;
this.addListener("initComplete", function() {
if (self.state == "init") {
self.setState("netConnect")
}
});
this.peerProcessor.addListener("netConnected", function() {
if (self.state == "netConnect") {
self.setState("blockDownload")
}
})
};
NodeServer.prototype.handleStateChange = function(e) {
var self = this;
this.running = !!~["netConnect", "blockDownload", "default"].indexOf(e.newState);
switch (e.oldState) {}
switch (e.newState) {
case "init":
BlockchainProcessor.run(function() {
setInterval(function() {
if (Account.currentAccount) {
logger.info("Account.currentAccount.accountSecret", Account.currentAccount.accountSecret)
} else {
logger.info("You not logged to generate block.")
}
}, 6e4);
self.emit("initComplete")
});
break;
case "netConnect":
this.peerProcessor.run();
this.startFrontendServer(Router.route, handle);
break;
case "blockDownload":
this.broadcastPeerStatus(Peer.prototype.statuses.CHECK);
this.startCheckSynced();
this.peerProcessor.syncTransaction();
break;
case "synced":
break
}
};
NodeServer.prototype.startCheckSynced = function() {
this.intervalCheckSynced = setInterval(this.checkSynced.bind(this), 1e4)
};
NodeServer.prototype.checkSynced = function() {
if (this.peerProcessor.synced && Account.currentAccount) {
clearInterval(this.intervalCheckSynced);
this.broadcastPeerStatus(Peer.prototype.statuses.ACTIVE)
}
};
NodeServer.prototype.addConnection = function(conn) {
this.blockGenerator.addConnection(conn)
};
NodeServer.prototype.broadcast = function(data, command, options) {
var conns = this.peerProcessor.getActiveConnections();
var _options = options || {};
var f = null;
if (typeof command === "string") {
f = function(conn, data, command) {
return conn.sendMessage(command, data)
}
} else {
f = function(conn, data) {
return conn.broadcast(data)
}
}
var checkOptions;
if (_options["peerKey"]) {
checkOptions = function(data, key) {
if (key == _options["peerKey"]) {
data["broadcastSelectPeer"] = true
}
return data
}
} else {
checkOptions = function(data, key) {
return data
}
}
conns.forEach(function(conn, key) {
f(conn, checkOptions(data, key), command)
})
};
NodeServer.prototype.broadcastPeerStatus = function(status) {
this.netStatus = status;
this.broadcast({
status: status
}, Connection.prototype.commands.PEER_STATUS)
};
NodeServer.prototype.broadcastNewTransaction = function(transaction) {
var data = transaction.getData();
this.broadcast(data, Connection.prototype.commands.NEW_TRANSACTION)
};
NodeServer.prototype.broadcastNewBlock = function(block) {
var data = block.getDataWithTransactions();
data.broadcasted = true;
this.broadcast(data, Connection.prototype.commands.BLOCK)
};
NodeServer.prototype.startFrontendServer = function(route, handle) {
function onRequest(request, response) {
route(handle, request, response)
}
http.createServer(onRequest).listen(Config.port);
logger.info("UI Server has started at http://" + Config.host + ":" + Config.port)
};
function string_to_array(str) {
var len = str.length;
var res = new Array(len);
for (var i = 0; i < len; i++) res[i] = str.charCodeAt(i);
return res
}
function array_to_hex_string(ary) {
var res = "";
for (var i = 0; i < ary.length; i++) res += SHA256_hexchars[ary[i] >> 4] + SHA256_hexchars[ary[i] & 15];
return res
}
function SHA256_init() {
SHA256_H = new Array(1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225);
SHA256_buf = new Array();
SHA256_len = 0
}
function SHA256_write(msg) {
if (typeof msg == "string") SHA256_buf = SHA256_buf.concat(string_to_array(msg));
else SHA256_buf = SHA256_buf.concat(msg);
for (var i = 0; i + 64 <= SHA256_buf.length; i += 64) SHA256_Hash_Byte_Block(SHA256_H, SHA256_buf.slice(i, i + 64));
SHA256_buf = SHA256_buf.slice(i);
SHA256_len += msg.length
}
function SHA256_finalize() {
SHA256_buf[SHA256_buf.length] = 128;
if (SHA256_buf.length > 64 - 8) {
for (var i = SHA256_buf.length; i < 64; i++) SHA256_buf[i] = 0;
SHA256_Hash_Byte_Block(SHA256_H, SHA256_buf);
SHA256_buf.length = 0
}
for (var i = SHA256_buf.length; i < 64 - 5; i++) SHA256_buf[i] = 0;
SHA256_buf[59] = SHA256_len >>> 29 & 255;
SHA256_buf[60] = SHA256_len >>> 21 & 255;
SHA256_buf[61] = SHA256_len >>> 13 & 255;
SHA256_buf[62] = SHA256_len >>> 5 & 255;
SHA256_buf[63] = SHA256_len << 3 & 255;
SHA256_Hash_Byte_Block(SHA256_H, SHA256_buf);
var res = new Array(32);
for (var i = 0; i < 8; i++) {
res[4 * i + 0] = SHA256_H[i] >>> 24;
res[4 * i + 1] = SHA256_H[i] >> 16 & 255;
res[4 * i + 2] = SHA256_H[i] >> 8 & 255;
res[4 * i + 3] = SHA256_H[i] & 255
}
delete SHA256_H;
delete SHA256_buf;
delete SHA256_len;
return res
}
function SHA256_hash(msg) {
var res;
SHA256_init();
SHA256_write(msg);
res = SHA256_finalize();
return array_to_hex_string(res)
}
function HMAC_SHA256_init(key) {
if (typeof key == "string") HMAC_SHA256_key = string_to_array(key);
else HMAC_SHA256_key = new Array().concat(key); if (HMAC_SHA256_key.length > 64) {
SHA256_init();
SHA256_write(HMAC_SHA256_key);
HMAC_SHA256_key = SHA256_finalize()
}
for (var i = HMAC_SHA256_key.length; i < 64; i++) HMAC_SHA256_key[i] = 0;
for (var i = 0; i < 64; i++) HMAC_SHA256_key[i] ^= 54;
SHA256_init();
SHA256_write(HMAC_SHA256_key)
}
function HMAC_SHA256_write(msg) {
SHA256_write(msg)
}
function HMAC_SHA256_finalize() {
var md = SHA256_finalize();
for (var i = 0; i < 64; i++) HMAC_SHA256_key[i] ^= 54 ^ 92;
SHA256_init();
SHA256_write(HMAC_SHA256_key);
SHA256_write(md);
for (var i = 0; i < 64; i++) HMAC_SHA256_key[i] = 0;
delete HMAC_SHA256_key;
return SHA256_finalize()
}
function HMAC_SHA256_MAC(key, msg) {
var res;
HMAC_SHA256_init(key);
HMAC_SHA256_write(msg);
res = HMAC_SHA256_finalize();
return array_to_hex_string(res)
}
SHA256_hexchars = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f");
SHA256_K = new Array(1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298);
function SHA256_sigma0(x) {
return (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ x >>> 3
}
function SHA256_sigma1(x) {
return (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ x >>> 10
}
function SHA256_Sigma0(x) {
return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10)
}
function SHA256_Sigma1(x) {
return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7)
}
function SHA256_Ch(x, y, z) {
return z ^ x & (y ^ z)
}
function SHA256_Maj(x, y, z) {
return x & y ^ z & (x ^ y)
}
function SHA256_Hash_Word_Block(H, W) {
for (var i = 16; i < 64; i++) W[i] = SHA256_sigma1(W[i - 2]) + W[i - 7] + SHA256_sigma0(W[i - 15]) + W[i - 16] & 4294967295;
var state = new Array().concat(H);
for (var i = 0; i < 64; i++) {
var T1 = state[7] + SHA256_Sigma1(state[4]) + SHA256_Ch(state[4], state[5], state[6]) + SHA256_K[i] + W[i];
var T2 = SHA256_Sigma0(state[0]) + SHA256_Maj(state[0], state[1], state[2]);
state.pop();
state.unshift(T1 + T2 & 4294967295);
state[4] = state[4] + T1 & 4294967295
}
for (var i = 0; i < 8; i++) H[i] = H[i] + state[i] & 4294967295
}
function SHA256_Hash_Byte_Block(H, w) {
var W = new Array(16);
for (var i = 0; i < 16; i++) W[i] = w[4 * i + 0] << 24 | w[4 * i + 1] << 16 | w[4 * i + 2] << 8 | w[4 * i + 3];
SHA256_Hash_Word_Block(H, W)
}
var converters = function() {
var charToNibble = {};
var nibbleToChar = [];
var i;
for (i = 0; i <= 9; ++i) {
var char = i.toString();
charToNibble[char] = i;
nibbleToChar.push(char)
}
for (i = 10; i <= 15; ++i) {
var lowerChar = String.fromCharCode("a".charCodeAt(0) + i - 10);
var upperChar = String.fromCharCode("A".charCodeAt(0) + i - 10);
charToNibble[lowerChar] = i;
charToNibble[upperChar] = i;
nibbleToChar.push(lowerChar)
}
return {
byteArrayToHexString: function(bytes) {
var str = "";
for (var i = 0; i < bytes.length; ++i) str += nibbleToChar[bytes[i] >> 4] + nibbleToChar[bytes[i] & 15];
return str
},
stringToByteArray: function(str) {
var bytes = new Array(str.length);
for (var i = 0; i < str.length; ++i) bytes[i] = str.charCodeAt(i);
return bytes
},
hexStringToByteArray: function(str) {
var bytes = [];
var i = 0;
if (0 !== str.length % 2) {
bytes.push(charToNibble[str.charAt(0)]);
++i
}
for (; i < str.length - 1; i += 2) bytes.push((charToNibble[str.charAt(i)] << 4) + charToNibble[str.charAt(i + 1)]);
return bytes
},
stringToHexString: function(str) {
return this.byteArrayToHexString(this.stringToByteArray(str))
}
}
}();
var curve25519 = function() {
var KEY_SIZE = 32;
var UNPACKED_SIZE = 16;
var ORDER = [237, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16];
var ORDER_TIMES_8 = [104, 159, 174, 231, 210, 24, 147, 192, 178, 230, 188, 23, 245, 206, 247, 166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128];
var BASE_2Y = [22587, 610, 29883, 44076, 15515, 9479, 25859, 56197, 23910, 4462, 17831, 16322, 62102, 36542, 52412, 16035];
var BASE_R2Y = [5744, 16384, 61977, 54121, 8776, 18501, 26522, 34893, 23833, 5823, 55924, 58749, 24147, 14085, 13606, 6080];
var C1 = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var C9 = [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var C486671 = [27919, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var C39420360 = [33224, 601, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
var P25 = 33554431;
var P26 = 67108863;
function clamp(k) {
k[31] &= 127;
k[31] |= 64;
k[0] &= 248
}
function cpy32(d, s) {
for (var i = 0; i < 32; i++) d[i] = s[i]
}
function mula_small(p, q, m, x, n, z) {
m = m | 0;
n = n | 0;
z = z | 0;
var v = 0;
for (var i = 0; i < n; ++i) {
v += (q[i + m] & 255) + z * (x[i] & 255);
p[i + m] = v & 255;
v >>= 8
}
return v
}
function mula32(p, x, y, t, z) {
t = t | 0;
z = z | 0;
var n = 31;
var w = 0;
var i = 0;
for (; i < t; i++) {
var zy = z * (y[i] & 255);
w += mula_small(p, p, i, x, n, zy) + (p[i + n] & 255) + zy * (x[n] & 255);
p[i + n] = w & 255;
w >>= 8
}
p[i + n] = w + (p[i + n] & 255) & 255;
return w >> 8
}
function divmod(q, r, n, d, t) {
n = n | 0;
t = t | 0;
var rn = 0;
var dt = (d[t - 1] & 255) << 8;
if (t > 1) dt |= d[t - 2] & 255;
while (n-- >= t) {
var z = rn << 16 | (r[n] & 255) << 8;
if (n > 0) z |= r[n - 1] & 255;
var i = n - t + 1;
z /= dt;
rn += mula_small(r, r, i, d, t, -z);
q[i] = z + rn & 255;
mula_small(r, r, i, d, t, -rn);
rn = r[n] & 255;
r[n] = 0
}
r[t - 1] = rn & 255
}
function numsize(x, n) {
while (n-- !== 0 && x[n] === 0) {}
return n + 1
}
function egcd32(x, y, a, b) {
var an, bn = 32,
qn, i;
for (i = 0; i < 32; i++) x[i] = y[i] = 0;
x[0] = 1;
an = numsize(a, 32);
if (an === 0) return y;
var temp = new Array(32);
while (true) {
qn = bn - an + 1;
divmod(temp, b, bn, a, an);
bn = numsize(b, bn);
if (bn === 0) return x;
mula32(y, x, temp, qn, -1);
qn = an - bn + 1;
divmod(temp, a, an, b, bn);
an = numsize(a, an);
if (an === 0) return y;
mula32(x, y, temp, qn, -1)
}
}
function unpack(x, m) {
for (var i = 0; i < KEY_SIZE; i += 2) x[i / 2] = m[i] & 255 | (m[i + 1] & 255) << 8
}
function is_overflow(x) {
return x[0] > P26 - 19 && (x[1] & x[3] & x[5] & x[7] & x[9]) === P25 && (x[2] & x[4] & x[6] & x[8]) === P26 || x[9] > P25
}
function pack(x, m) {
for (var i = 0; i < UNPACKED_SIZE; ++i) {
m[2 * i] = x[i] & 255;
m[2 * i + 1] = (x[i] & 65280) >> 8
}
}
function createUnpackedArray() {
return new Uint16Array(UNPACKED_SIZE)
}
function cpy(d, s) {
for (var i = 0; i < UNPACKED_SIZE; ++i) d[i] = s[i]
}
function set(d, s) {
d[0] = s;
for (var i = 1; i < UNPACKED_SIZE; ++i) d[i] = 0
}
var add = c255laddmodp;
var sub = c255lsubmodp;
var mul_small = c255lmulasmall;
var mul = c255lmulmodp;
var sqr = c255lsqrmodp;
function recip(y, x, sqrtassist) {
var t0 = createUnpackedArray();
var t1 = createUnpackedArray();
var t2 = createUnpackedArray();
var t3 = createUnpackedArray();
var t4 = createUnpackedArray();
var i;
sqr(t1, x);
sqr(t2, t1);
sqr(t0, t2);
mul(t2, t0, x);
mul(t0, t2, t1);
sqr(t1, t0);
mul(t3, t1, t2);
sqr(t1, t3);
sqr(t2, t1);
sqr(t1, t2);
sqr(t2, t1);
sqr(t1, t2);
mul(t2, t1, t3);
sqr(t1, t2);
sqr(t3, t1);
for (i = 1; i < 5; i++) {
sqr(t1, t3);
sqr(t3, t1)
}
mul(t1, t3, t2);
sqr(t3, t1);
sqr(t4, t3);
for (i = 1; i < 10; i++) {
sqr(t3, t4);
sqr(t4, t3)
}
mul(t3, t4, t1);
for (i = 0; i < 5; i++) {
sqr(t1, t3);
sqr(t3, t1)
}
mul(t1, t3, t2);
sqr(t2, t1);
sqr(t3, t2);
for (i = 1; i < 25; i++) {
sqr(t2, t3);
sqr(t3, t2)
}
mul(t2, t3, t1);
sqr(t3, t2);
sqr(t4, t3);
for (i = 1; i < 50; i++) {
sqr(t3, t4);
sqr(t4, t3)
}
mul(t3, t4, t2);
for (i = 0; i < 25; i++) {
sqr(t4, t3);
sqr(t3, t4)
}
mul(t2, t3, t1);
sqr(t1, t2);
sqr(t2, t1);
if (sqrtassist !== 0) {
mul(y, x, t2)
} else {
sqr(t1, t2);
sqr(t2, t1);
sqr(t1, t2);
mul(y, t1, t0)
}
}
function is_negative(x) {
var isOverflowOrNegative = is_overflow(x) || x[9] < 0;
var leastSignificantBit = x[0] & 1;
return ((isOverflowOrNegative ? 1 : 0) ^ leastSignificantBit) & 4294967295
}
function sqrt(x, u) {
var v = createUnpackedArray();
var t1 = createUnpackedArray();
var t2 = createUnpackedArray();
add(t1, u, u);
recip(v, t1, 1);
sqr(x, v);
mul(t2, t1, x);
sub(t2, t2, C1);
mul(t1, v, t2);
mul(x, u, t1)
}
function c255lsqr8h(a7, a6, a5, a4, a3, a2, a1, a0) {
var r = [];
var v;
r[0] = (v = a0 * a0) & 65535;
r[1] = (v = (v / 65536 | 0) + 2 * a0 * a1) & 65535;
r[2] = (v = (v / 65536 | 0) + 2 * a0 * a2 + a1 * a1) & 65535;
r[3] = (v = (v / 65536 | 0) + 2 * a0 * a3 + 2 * a1 * a2) & 65535;
r[4] = (v = (v / 65536 | 0) + 2 * a0 * a4 + 2 * a1 * a3 + a2 * a2) & 65535;
r[5] = (v = (v / 65536 | 0) + 2 * a0 * a5 + 2 * a1 * a4 + 2 * a2 * a3) & 65535;
r[6] = (v = (v / 65536 | 0) + 2 * a0 * a6 + 2 * a1 * a5 + 2 * a2 * a4 + a3 * a3) & 65535;
r[7] = (v = (v / 65536 | 0) + 2 * a0 * a7 + 2 * a1 * a6 + 2 * a2 * a5 + 2 * a3 * a4) & 65535;
r[8] = (v = (v / 65536 | 0) + 2 * a1 * a7 + 2 * a2 * a6 + 2 * a3 * a5 + a4 * a4) & 65535;
r[9] = (v = (v / 65536 | 0) + 2 * a2 * a7 + 2 * a3 * a6 + 2 * a4 * a5) & 65535;
r[10] = (v = (v / 65536 | 0) + 2 * a3 * a7 + 2 * a4 * a6 + a5 * a5) & 65535;
r[11] = (v = (v / 65536 | 0) + 2 * a4 * a7 + 2 * a5 * a6) & 65535;
r[12] = (v = (v / 65536 | 0) + 2 * a5 * a7 + a6 * a6) & 65535;
r[13] = (v = (v / 65536 | 0) + 2 * a6 * a7) & 65535;
r[14] = (v = (v / 65536 | 0) + a7 * a7) & 65535;
r[15] = v / 65536 | 0;
return r
}
function c255lsqrmodp(r, a) {
var x = c255lsqr8h(a[15], a[14], a[13], a[12], a[11], a[10], a[9], a[8]);
var z = c255lsqr8h(a[7], a[6], a[5], a[4], a[3], a[2], a[1], a[0]);
var y = c255lsqr8h(a[15] + a[7], a[14] + a[6], a[13] + a[5], a[12] + a[4], a[11] + a[3], a[10] + a[2], a[9] + a[1], a[8] + a[0]);
var v;
r[0] = (v = 8388608 + z[0] + (y[8] - x[8] - z[8] + x[0] - 128) * 38) & 65535;
r[1] = (v = 8388480 + (v / 65536 | 0) + z[1] + (y[9] - x[9] - z[9] + x[1]) * 38) & 65535;
r[2] = (v = 8388480 + (v / 65536 | 0) + z[2] + (y[10] - x[10] - z[10] + x[2]) * 38) & 65535;
r[3] = (v = 8388480 + (v / 65536 | 0) + z[3] + (y[11] - x[11] - z[11] + x[3]) * 38) & 65535;
r[4] = (v = 8388480 + (v / 65536 | 0) + z[4] + (y[12] - x[12] - z[12] + x[4]) * 38) & 65535;
r[5] = (v = 8388480 + (v / 65536 | 0) + z[5] + (y[13] - x[13] - z[13] + x[5]) * 38) & 65535;
r[6] = (v = 8388480 + (v / 65536 | 0) + z[6] + (y[14] - x[14] - z[14] + x[6]) * 38) & 65535;
r[7] = (v = 8388480 + (v / 65536 | 0) + z[7] + (y[15] - x[15] - z[15] + x[7]) * 38) & 65535;
r[8] = (v = 8388480 + (v / 65536 | 0) + z[8] + y[0] - x[0] - z[0] + x[8] * 38) & 65535;
r[9] = (v = 8388480 + (v / 65536 | 0) + z[9] + y[1] - x[1] - z[1] + x[9] * 38) & 65535;
r[10] = (v = 8388480 + (v / 65536 | 0) + z[10] + y[2] - x[2] - z[2] + x[10] * 38) & 65535;
r[11] = (v = 8388480 + (v / 65536 | 0) + z[11] + y[3] - x[3] - z[3] + x[11] * 38) & 65535;
r[12] = (v = 8388480 + (v / 65536 | 0) + z[12] + y[4] - x[4] - z[4] + x[12] * 38) & 65535;
r[13] = (v = 8388480 + (v / 65536 | 0) + z[13] + y[5] - x[5] - z[5] + x[13] * 38) & 65535;
r[14] = (v = 8388480 + (v / 65536 | 0) + z[14] + y[6] - x[6] - z[6] + x[14] * 38) & 65535;
var r15 = 8388480 + (v / 65536 | 0) + z[15] + y[7] - x[7] - z[7] + x[15] * 38;
c255lreduce(r, r15)
}
function c255lmul8h(a7, a6, a5, a4, a3, a2, a1, a0, b7, b6, b5, b4, b3, b2, b1, b0) {
var r = [];
var v;
r[0] = (v = a0 * b0) & 65535;
r[1] = (v = (v / 65536 | 0) + a0 * b1 + a1 * b0) & 65535;
r[2] = (v = (v / 65536 | 0) + a0 * b2 + a1 * b1 + a2 * b0) & 65535;
r[3] = (v = (v / 65536 | 0) + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0) & 65535;
r[4] = (v = (v / 65536 | 0) + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0) & 65535;
r[5] = (v = (v / 65536 | 0) + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0) & 65535;
r[6] = (v = (v / 65536 | 0) + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0) & 65535;
r[7] = (v = (v / 65536 | 0) + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0) & 65535;
r[8] = (v = (v / 65536 | 0) + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1) & 65535;
r[9] = (v = (v / 65536 | 0) + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2) & 65535;
r[10] = (v = (v / 65536 | 0) + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3) & 65535;
r[11] = (v = (v / 65536 | 0) + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4) & 65535;
r[12] = (v = (v / 65536 | 0) + a5 * b7 + a6 * b6 + a7 * b5) & 65535;
r[13] = (v = (v / 65536 | 0) + a6 * b7 + a7 * b6) & 65535;
r[14] = (v = (v / 65536 | 0) + a7 * b7) & 65535;
r[15] = v / 65536 | 0;
return r
}
function c255lmulmodp(r, a, b) {
var x = c255lmul8h(a[15], a[14], a[13], a[12], a[11], a[10], a[9], a[8], b[15], b[14], b[13], b[12], b[11], b[10], b[9], b[8]);
var z = c255lmul8h(a[7], a[6], a[5], a[4], a[3], a[2], a[1], a[0], b[7], b[6], b[5], b[4], b[3], b[2], b[1], b[0]);
var y = c255lmul8h(a[15] + a[7], a[14] + a[6], a[13] + a[5], a[12] + a[4], a[11] + a[3], a[10] + a[2], a[9] + a[1], a[8] + a[0], b[15] + b[7], b[14] + b[6], b[13] + b[5], b[12] + b[4], b[11] + b[3], b[10] + b[2], b[9] + b[1], b[8] + b[0]);
var v;
r[0] = (v = 8388608 + z[0] + (y[8] - x[8] - z[8] + x[0] - 128) * 38) & 65535;
r[1] = (v = 8388480 + (v / 65536 | 0) + z[1] + (y[9] - x[9] - z[9] + x[1]) * 38) & 65535;
r[2] = (v = 8388480 + (v / 65536 | 0) + z[2] + (y[10] - x[10] - z[10] + x[2]) * 38) & 65535;
r[3] = (v = 8388480 + (v / 65536 | 0) + z[3] + (y[11] - x[11] - z[11] + x[3]) * 38) & 65535;
r[4] = (v = 8388480 + (v / 65536 | 0) + z[4] + (y[12] - x[12] - z[12] + x[4]) * 38) & 65535;
r[5] = (v = 8388480 + (v / 65536 | 0) + z[5] + (y[13] - x[13] - z[13] + x[5]) * 38) & 65535;
r[6] = (v = 8388480 + (v / 65536 | 0) + z[6] + (y[14] - x[14] - z[14] + x[6]) * 38) & 65535;
r[7] = (v = 8388480 + (v / 65536 | 0) + z[7] + (y[15] - x[15] - z[15] + x[7]) * 38) & 65535;
r[8] = (v = 8388480 + (v / 65536 | 0) + z[8] + y[0] - x[0] - z[0] + x[8] * 38) & 65535;
r[9] = (v = 8388480 + (v / 65536 | 0) + z[9] + y[1] - x[1] - z[1] + x[9] * 38) & 65535;
r[10] = (v = 8388480 + (v / 65536 | 0) + z[10] + y[2] - x[2] - z[2] + x[10] * 38) & 65535;
r[11] = (v = 8388480 + (v / 65536 | 0) + z[11] + y[3] - x[3] - z[3] + x[11] * 38) & 65535;
r[12] = (v = 8388480 + (v / 65536 | 0) + z[12] + y[4] - x[4] - z[4] + x[12] * 38) & 65535;
r[13] = (v = 8388480 + (v / 65536 | 0) + z[13] + y[5] - x[5] - z[5] + x[13] * 38) & 65535;
r[14] = (v = 8388480 + (v / 65536 | 0) + z[14] + y[6] - x[6] - z[6] + x[14] * 38) & 65535;
var r15 = 8388480 + (v / 65536 | 0) + z[15] + y[7] - x[7] - z[7] + x[15] * 38;
c255lreduce(r, r15)
}
function c255lreduce(a, a15) {
var v = a15;
a[15] = v & 32767;
v = (v / 32768 | 0) * 19;
for (var i = 0; i <= 14; ++i) {
a[i] = (v += a[i]) & 65535;
v = v / 65536 | 0
}
a[15] += v
}
function c255laddmodp(r, a, b) {
var v;
r[0] = (v = ((a[15] / 32768 | 0) + (b[15] / 32768 | 0)) * 19 + a[0] + b[0]) & 65535;
for (var i = 1; i <= 14; ++i) r[i] = (v = (v / 65536 | 0) + a[i] + b[i]) & 65535;
r[15] = (v / 65536 | 0) + (a[15] & 32767) + (b[15] & 32767)
}
function c255lsubmodp(r, a, b) {
var v;
r[0] = (v = 524288 + ((a[15] / 32768 | 0) - (b[15] / 32768 | 0) - 1) * 19 + a[0] - b[0]) & 65535;
for (var i = 1; i <= 14; ++i) r[i] = (v = (v / 65536 | 0) + 524280 + a[i] - b[i]) & 65535;
r[15] = (v / 65536 | 0) + 32760 + (a[15] & 32767) - (b[15] & 32767)
}
function c255lmulasmall(r, a, m) {
var v;
r[0] = (v = a[0] * m) & 65535;
for (var i = 1; i <= 14; ++i) r[i] = (v = (v / 65536 | 0) + a[i] * m) & 65535;
var r15 = (v / 65536 | 0) + a[15] * m;
c255lreduce(r, r15)
}
function mont_prep(t1, t2, ax, az) {
add(t1, ax, az);
sub(t2, ax, az)
}
function mont_add(t1, t2, t3, t4, ax, az, dx) {
mul(ax, t2, t3);
mul(az, t1, t4);
add(t1, ax, az);
sub(t2, ax, az);
sqr(ax, t1);
sqr(t1, t2);
mul(az, t1, dx)
}
function mont_dbl(t1, t2, t3, t4, bx, bz) {
sqr(t1, t3);
sqr(t2, t4);
mul(bx, t1, t2);
sub(t2, t1, t2);
mul_small(bz, t2, 121665);
add(t1, t1, bz);
mul(bz, t1, t2)
}
function x_to_y2(t, y2, x) {
sqr(t, x);
mul_small(y2, x, 486662);
add(t, t, y2);
add(t, t, C1);
mul(y2, t, x)
}
function core(Px, s, k, Gx) {
var dx = createUnpackedArray();
var t1 = createUnpackedArray();
var t2 = createUnpackedArray();
var t3 = createUnpackedArray();
var t4 = createUnpackedArray();
var x = [createUnpackedArray(), createUnpackedArray()];
var z = [createUnpackedArray(), createUnpackedArray()];
var i, j;
if (Gx !== null) unpack(dx, Gx);
else set(dx, 9);
set(x[0], 1);
set(z[0], 0);
cpy(x[1], dx);
set(z[1], 1);
for (i = 32; i-- !== 0;) {
for (j = 8; j-- !== 0;) {
var bit1 = (k[i] & 255) >> j & 1;
var bit0 = ~(k[i] & 255) >> j & 1;
var ax = x[bit0];
var az = z[bit0];
var bx = x[bit1];
var bz = z[bit1];
mont_prep(t1, t2, ax, az);
mont_prep(t3, t4, bx, bz);
mont_add(t1, t2, t3, t4, ax, az, dx);
mont_dbl(t1, t2, t3, t4, bx, bz)
}
}
recip(t1, z[0], 0);
mul(dx, x[0], t1);
pack(dx, Px);
if (s !== null) {
x_to_y2(t2, t1, dx);
recip(t3, z[1], 0);
mul(t2, x[1], t3);
add(t2, t2, dx);
add(t2, t2, C486671);
sub(dx, dx, C9);
sqr(t3, dx);
mul(dx, t2, t3);
sub(dx, dx, t1);
sub(dx, dx, C39420360);
mul(t1, dx, BASE_R2Y);
if (is_negative(t1) !== 0) cpy32(s, k);
else mula_small(s, ORDER_TIMES_8, 0, k, 32, -1);
var temp1 = new Array(32);
var temp2 = new Array(64);
var temp3 = new Array(64);
cpy32(temp1, ORDER);
cpy32(s, egcd32(temp2, temp3, s, temp1));
if ((s[31] & 128) !== 0) mula_small(s, s, 0, ORDER, 32, 1)
}
}
function sign(h, x, s) {
var w, i;
var h1 = new Array(32);
var x1 = new Array(32);
var tmp1 = new Array(64);
var tmp2 = new Array(64);
cpy32(h1, h);
cpy32(x1, x);
var tmp3 = new Array(32);
divmod(tmp3, h1, 32, ORDER, 32);
divmod(tmp3, x1, 32, ORDER, 32);
var v = new Array(32);
mula_small(v, x1, 0, h1, 32, -1);
mula_small(v, v, 0, ORDER, 32, 1);
mula32(tmp1, v, s, 32, 1);
divmod(tmp2, tmp1, 64, ORDER, 32);
for (w = 0, i = 0; i < 32; i++) w |= v[i] = tmp1[i];
return w !== 0 ? v : undefined
}
function verify(v, h, P) {
var d = new Array(32);
var p = [createUnpackedArray(), createUnpackedArray()];
var s = [createUnpackedArray(), createUnpackedArray()];
var yx = [createUnpackedArray(), createUnpackedArray(), createUnpackedArray()];
var yz = [createUnpackedArray(), createUnpackedArray(), createUnpackedArray()];
var t1 = [createUnpackedArray(), createUnpackedArray(), createUnpackedArray()];
var t2 = [createUnpackedArray(), createUnpackedArray(), createUnpackedArray()];
var vi = 0,
hi = 0,
di = 0,
nvh = 0,
i, j, k;
set(p[0], 9);
unpack(p[1], P);
x_to_y2(t1[0], t2[0], p[1]);
sqrt(t1[0], t2[0]);
j = is_negative(t1[0]);
add(t2[0], t2[0], C39420360);
mul(t2[1], BASE_2Y, t1[0]);
sub(t1[j], t2[0], t2[1]);
add(t1[1 - j], t2[0], t2[1]);
cpy(t2[0], p[1]);
sub(t2[0], t2[0], C9);
sqr(t2[1], t2[0]);
recip(t2[0], t2[1], 0);
mul(s[0], t1[0], t2[0]);
sub(s[0], s[0], p[1]);
sub(s[0], s[0], C486671);
mul(s[1], t1[1], t2[0]);
sub(s[1], s[1], p[1]);
sub(s[1], s[1], C486671);
mul_small(s[0], s[0], 1);
mul_small(s[1], s[1], 1);
for (i = 0; i < 32; i++) {
vi = vi >> 8 ^ v[i] & 255 ^ (v[i] & 255) << 1;
hi = hi >> 8 ^ h[i] & 255 ^ (h[i] & 255) << 1;
nvh = ~(vi ^ hi);
di = nvh & (di & 128) >> 7 ^ vi;
di ^= nvh & (di & 1) << 1;
di ^= nvh & (di & 2) << 1;
di ^= nvh & (di & 4) << 1;
di ^= nvh & (di & 8) << 1;
di ^= nvh & (di & 16) << 1;
di ^= nvh & (di & 32) << 1;
di ^= nvh & (di & 64) << 1;
d[i] = di & 255
}
di = (nvh & (di & 128) << 1 ^ vi) >> 8;
set(yx[0], 1);
cpy(yx[1], p[di]);
cpy(yx[2], s[0]);
set(yz[0], 0);
set(yz[1], 1);
set(yz[2], 1);
vi = 0;
hi = 0;
for (i = 32; i-- !== 0;) {
vi = vi << 8 | v[i] & 255;
hi = hi << 8 | h[i] & 255;
di = di << 8 | d[i] & 255;
for (j = 8; j-- !== 0;) {
mont_prep(t1[0], t2[0], yx[0], yz[0]);
mont_prep(t1[1], t2[1], yx[1], yz[1]);
mont_prep(t1[2], t2[2], yx[2], yz[2]);
k = ((vi ^ vi >> 1) >> j & 1) + ((hi ^ hi >> 1) >> j & 1);
mont_dbl(yx[2], yz[2], t1[k], t2[k], yx[0], yz[0]);
k = di >> j & 2 ^ (di >> j & 1) << 1;
mont_add(t1[1], t2[1], t1[k], t2[k], yx[1], yz[1], p[di >> j & 1]);
mont_add(t1[2], t2[2], t1[0], t2[0], yx[2], yz[2], s[((vi ^ hi) >> j & 2) >> 1])
}
}
k = (vi & 1) + (hi & 1);
recip(t1[0], yz[k], 0);
mul(t1[1], yx[k], t1[0]);
var Y = [];
pack(t1[1], Y);
return Y
}
function keygen(k) {
var P = [];
var s = [];
k = k || [];
clamp(k);
core(P, s, k, null);
return {
p: P,
s: s,
k: k
}
}
return {
sign: sign,
verify: verify,
keygen: keygen
}
}();
var hash = {
init: SHA256_init,
update: SHA256_write,
getBytes: SHA256_finalize
};
var nxtCrypto = function(curve25519, hash, converters) {
function simpleHash(message) {
hash.init();
hash.update(message);
return hash.getBytes()
}
function areByteArraysEqual(bytes1, bytes2) {
if (bytes1.length !== bytes2.length) return false;
for (var i = 0; i < bytes1.length; ++i) {
if (bytes1[i] !== bytes2[i]) return false
}
return true
}
function getPublicKey(secretPhrase) {
var secretPhraseBytes = converters.hexStringToByteArray(secretPhrase);
var digest = simpleHash(secretPhraseBytes);
return converters.byteArrayToHexString(curve25519.keygen(digest).p)
}
function sign(message, secretPhrase) {
var messageBytes = converters.hexStringToByteArray(message);
var secretPhraseBytes = converters.hexStringToByteArray(secretPhrase);
var digest = simpleHash(secretPhraseBytes);
var s = curve25519.keygen(digest).s;
var m = simpleHash(messageBytes);
hash.init();
hash.update(m);
hash.update(s);
var x = hash.getBytes();
var y = curve25519.keygen(x).p;
hash.init();
hash.update(m);
hash.update(y);
var h = hash.getBytes();
var v = curve25519.sign(h, x, s);
return converters.byteArrayToHexString(v.concat(h))
}
function verify(signature, message, publicKey) {
var signatureBytes = converters.hexStringToByteArray(signature);
var messageBytes = converters.hexStringToByteArray(message);
var publicKeyBytes = converters.hexStringToByteArray(publicKey);
var v = signatureBytes.slice(0, 32);
var h = signatureBytes.slice(32);
var y = curve25519.verify(v, h, publicKeyBytes);
var m = simpleHash(messageBytes);
hash.init();
hash.update(m);
hash.update(y);
var h2 = hash.getBytes();
return areByteArraysEqual(h, h2)
}
return {
getPublicKey: getPublicKey,
sign: sign,
verify: verify
}
}(curve25519, hash, converters);
var BlockGenerator = function BlockGenerator(node) {
events.EventEmitter.call(this);
this.node = node;
this.timestamp = null;
this.running = false;
this.whoSayGenBlock = null;
this.selectedPeer = null;
this.collectedPeerResponse = new Map();
this.verifiedPeerResponse = null
};
util.inherits(BlockGenerator, events.EventEmitter);
BlockGenerator.prototype.flag = function(action) {
switch (action) {
case "start":
this.timestamp = new Date().getTime();
this.running = true;
break;
case "done":
case "error":
this.whoSayGenBlock = null;
this.selectedPeer = null;
case "stop":
this.timestamp = null;
this.running = false;
break
}
};
BlockGenerator.prototype.broadcastGenerateBlock = function() {
if (this.whoSayGenBlock !== null) {
logger.notice("Already generate block: " + this.whoSayGenBlock);
return
}
if (this.running) {
logger.notice("BlockGenerator already running.");
return
}
this.flag("start");
this.node.broadcast({
timestamp: this.timestamp
}, Connection.prototype.commands.BROADCAST_GENERATE_BLOCK);
this.initCheckPeers()
};
BlockGenerator.prototype.initCheckPeers = function() {
setTimeout(this.selectPeer.bind(this), 3e4)
};
BlockGenerator.prototype.stopNotice = function(message) {
logger.notice("Generating unit is stopped due to: " + message)
};
BlockGenerator.prototype.addConnection = function(conn) {
conn.addListener(Connection.prototype.commands.BROADCAST_GENERATE_BLOCK, this.handleBroadcastGenerateBlock.bind(this));
conn.addListener(Connection.prototype.commands.STOP_GENERATE_BLOCK, this.handleStopGenerateBlock.bind(this));
conn.addListener(Connection.prototype.commands.NEW_BLOCK, this.handleNewBlock.bind(this));
conn.addListener(Connection.prototype.commands.ANSWER_ON_GENERATE_BLOCK, this.handleAnswerOnGenerateBlock.bind(this));
conn.addListener(Connection.prototype.commands.VERIFIED_PEER, this.handleVerifiedPeer.bind(this));
conn.addListener(Connection.prototype.commands.VERIFIED_PEER_RESPONSE, this.handleVerifiedPeerResponse.bind(this));
conn.addListener(Connection.prototype.commands.PEER_NOT_VERIFIED, this.handlePeerNotVerified.bind(this))
};
BlockGenerator.prototype.handleBroadcastGenerateBlock = function(e) {
logger.netdbg("Handle Broadcast Generate Block\n" + e.conn.toString());
if (this.running) {
if (this.timestamp < e.message.data.timestamp) {
e.conn.sendMessage(Connection.prototype.commands.STOP_GENERATE_BLOCK, {
message: "My query generation was earlier in time."
});
return
} else {
this.stopNotice("Execution time is more than requesting " + e.peer.toString());
this.flag("stop")
}
}
if (this.whoSayGenBlock !== null) {
e.conn.sendMessage(Connection.prototype.commands.STOP_GENERATE_BLOCK, {
message: "Already generate by " + this.whoSayGenBlock
});
return
}
this.whoSayGenBlock = e.peer.toString();
this.createResponse(e)
};
BlockGenerator.prototype.createResponse = function(e) {
var accountId = Account.currentAccount.accountId,
data = {
accountId: accountId
};
Step(function getMyTransactions() {
TransactionDb.getMyTransactions(accountId, this)
}, function receiveTransactions(transactions) {
data["transactionsCount"] = transactions.length;
this(null)
}, function sendMessage() {
e.conn.sendMessage(Connection.prototype.commands.ANSWER_ON_GENERATE_BLOCK, data)
})
};
BlockGenerator.prototype.handleStopGenerateBlock = function(e) {
logger.netdbg("Handle Stop Generate Block\n" + e.conn.toString());
this.stopNotice(e.message.data.message);
this.flag(e.message.data.fullStop ? "done" : "stop")
};
BlockGenerator.prototype.handleAnswerOnGenerateBlock = function(e) {
logger.netdbg("Handle Answer On Generate Block\n" + e.conn.toString());
if (!this.running) {
logger.notice("Whe are not running");
return
}
this.collectedPeerResponse.set(e.peer.toString(), {
peer: e.peer,
account: e.message.data
})
};
BlockGenerator.prototype.handleNewBlock = function(e) {
logger.netdbg("Handle New Block\n" + e.conn.toString());
var conn = this.node.peerProcessor.connections.get(this.whoSayGenBlock);
if (conn) {
conn.sendMessage(Connection.prototype.commands.STOP_GENERATE_BLOCK, {
message: "Work done!!!",
fullStop: true
})
} else {
this.node.broadcast({
message: "Work done!!!",
fullStop: true
}, Connection.prototype.commands.STOP_GENERATE_BLOCK)
}
};
BlockGenerator.prototype.handlePeerNotVerified = function(e) {
logger.netdbg("Handle NPeerNotVerified\n" + e.conn.toString());
logger.error(e.message.data.message);
this.flag("error")
};
BlockGenerator.prototype.handleVerifiedPeer = function(e) {
logger.netdbg("Handle Verified Peer\n" + e.conn.toString());
var data = e.message.data;
if (typeof data.broadcastSelectPeer !== "undefined" && data.broadcastSelectPeer) {
this.verifiedPeerResponse = new Map();
setTimeout(this.verifiedPeer.bind(this), 3e4)
} else {
var peer = new Peer(data.peer);
var connection = this.node.peerProcessor.connections.get(peer.toString());
if (!connection) {
this.node.broadcast({
message: "Work done!!!",
fullStop: true
}, Connection.prototype.commands.STOP_GENERATE_BLOCK);
this.flag("done");
return
}
this.selectedPeer = peer.toString();
this.validatePeer({
peer: peer,
account: data.account
}, function(result) {
connection.sendMessage(Connection.prototype.commands.VERIFIED_PEER_RESPONSE, {
valid: result.valid
})
})
}
};
BlockGenerator.prototype.handleVerifiedPeerResponse = function(e) {
logger.netdbg("Handle Verified Peer Response\n" + e.conn.toString());
this.verifiedPeerResponse.set(e.peer.toString(), e.message.data)
};
BlockGenerator.prototype.verifiedPeer = function() {
var approved = 0,
notApproved = 0,
countPeerVerified = this.verifiedPeerResponse.length;
this.verifiedPeerResponse.forEach(function(data, key) {
if (data.valid) {
approved++
} else {
notApproved++
}
});
if (approved * 100 / countPeerVerified > 50) {
this.broadcastSendBlock(this.generateBlock())
} else {
this.node.broadcast({
message: "Approved less then 50%",
notValid: true
}, Connection.prototype.commands.PEER_NOT_VERIFIED)
}
};
BlockGenerator.prototype.generateBlock = function() {
return new Block({
id: 1
})
};
BlockGenerator.prototype.validatePeer = function(data, callback) {
var peer = data.peer,
account = data.account;
var _data = {};
Step(function getMyTransactions() {
TransactionDb.getMyTransactions(account.accountId, this)
}, function receiveTransactions(transactions) {
_data = data;
_data["valid"] = account.transactionsCount === transactions.length;
callback(_data)
})
};
BlockGenerator.prototype.comparePeers = function(oldPeer, newPeer) {
if (oldPeer === null) {
return true
}
var now = new Date().getTime(),
oldDiff = now - oldPeer.timestamp,
newDiff = now - newPeer.timestamp;
if (oldDiff > newDiff) {
return false
}
var oldPeerResponse = this.collectedPeerResponse.get(oldPeer.toString()),
newPeerResponse = this.collectedPeerResponse.get(newPeer.toString());
return oldPeerResponse.account.transactionsCount < newPeerResponse.account.transactionsCount
};
BlockGenerator.prototype.selectPeer = function() {
if (!this.running) {
logger.notice("Whe are not running");
return
}
var peerProc = this.node.peerProcessor;
var filtered = peerProc.connections.filter(function(conn, key) {
return conn.peer.status === Peer.prototype.statuses.ACTIVE && this.collectedPeerResponse.has(key)
}.bind(this));
var self = this;
async.eachSeries(filtered.toArray(), function(conn, _callback) {
var key = conn.peer.toString(),
peerResponse = self.collectedPeerResponse.get(key);
self.validatePeer(peerResponse, function(data) {
self.collectedPeerResponse.set(key, data);
_callback()
})
}, function(err) {
if (err) {
logger.error("No select peer!!!");
self.node.broadcast({
message: "No select peer!!!",
fullStop: true
}, Connection.prototype.commands.STOP_GENERATE_BLOCK);
return
}
var curPeer = null,
currPeerAccount = null;
filtered.forEach(function(conn, key) {
var peerResponse = self.collectedPeerResponse.get(key);
if (curPeer === null || peerResponse.valid && self.comparePeers(curPeer, peerResponse.peer)) {
curPeer = peerResponse.peer;
currPeerAccount = peerResponse.account
}
});
if (curPeer) {
self.selectedPeer = curPeer;
self.node.broadcast({
peer: curPeer.getData(),
account: currPeerAccount
}, Connection.prototype.commands.VERIFIED_PEER, {
peerKey: curPeer.toString()
})
} else {
logger.error("No select peer!!!");
self.node.broadcast({
message: "No select peer!!!",
fullStop: true
}, Connection.prototype.commands.STOP_GENERATE_BLOCK)
}
})
};
BlockGenerator.prototype.broadcastSendBlock = function(block) {
if (block instanceof Block) {
this.node.broadcast({
block: block.getData()
}, Connection.prototype.commands.NEW_BLOCK)
} else {
logger.error("'block' must be instance of 'models/Block'")
}
};
var DB = function() {
return {
db: new Datastore({
filename: appDir + "/db/nxtl.db",
autoload: true
}),
dbTx: new Datastore({
filename: appDir + "/db/nxtlTx.db",
autoload: true
}),
dbPeers: new Datastore({
filename: appDir + "/db/peers.db",
autoload: true
})
}
}();
var Genesis = {
genesisBlockId: "9971962888809300594",
creatorId: "7684489094182123925",
creatorPublicKey: new Buffer("03362fbbe6611243d853507a82dbe59844d169157fcda08deb171ed238fa3e19", "hex"),
genesisRecipients: ["15007997926042764084", "14302536936654097014", "4768052738330486459", "3216421205779526334", "17591617551191551229", "17629803831991308677", "17982531019448340817", "1092564458935116408", "1797803473310418787"],
genesisAmounts: [6e8, 15e7, 5e7, 5e7, 1e7, 4e7, 5e7, 3e7, 2e7],
genesisSignatures: ["d10e619bf8a9e31cd3c0ef1234d1efa40e7ab19f8a66e1ce63d065b8a992ae0f3ab0bbe032584229b9b64bbf2a1a19368a139cf52b76544fc9f7c4cfa6524475", "c687b6d33fbe1a8f4175d107e4d6bdbe3cffb5bd89aca5496c3fe0ae2e3d1b0a63278702131d59404eb6c5ca642f27a5eb8a8c68f5f70f64afe869f9fd81da2a", "d7d7b05dea2fc4371029d3d2e7a4250dd29f4a99d9eeb624c0df5155bd147e0c8597148beb1aa370c5b2dfeeaa28c9abfe0762ece014a24bb7c09370690ef963", "7520150c9d1741212d7748cc6841e554f1c89372d488026c1887b62b70a3860f5293f72274b57a64d77aad7eac220e322a0fa36f11b4f6ef58764d8b6e88229c", "2e456ec235841c8ac301e288f20b53ec2dc91ee24b94c2316c1b143a605550096d4192d7bea120a45509f23ae9aa92b16de094ddadf39d9411072e1f0045c75c", "fb0b87dbcf493a226f438ae27725237d7d828638d0a259c2e64996b8140f610f8d7cf644f51e97bc558a45adac66d57954abf7ad5eaf3e490ec6ea2da6e15800", "38af290d4a8cf743ebe14174173658cb08905827b0b68fc38aae257836d58b06b09a1bbd856c2d377f282e065951c886f607f1d3e01d970ef2c2dd9023c2f0ea", "eede7ced4d51b186bdb447e02d14e83fa1cf6ccf8e0752c2064b6d76cdca0f0b64edf1cdeedd14bcd9d4d59cfb70034cb1f28601f8ae0ef30ecc5b44aa1f9bd0", "5c4caafcca29e86b7659bcb56c93dfabb28d682d4ca1bff23e3ec25cc9d6230f86c71a36f4f74617b0beec5a5cb274e1f6577ac52b9e6d8fb7daceef3bd0dba3"],
genesisBlockSignature: "e0580c1f3feb66a9aa09ba2649f6726dca87b6db14f2dbcda368f7a2269c5604d16866925c706318ff0fdc79e1455c9b4ee88a969eb96899f54b6603a68f319f"
};
var TransactionProcessor = function() {
events.EventEmitter.call(this)
};
util.inherits(TransactionProcessor, events.EventEmitter);
TransactionProcessor.prototype.verifiyTransaction = function(transaction, unconfTransArr, callback) {
if (typeof unconfTransArr === "function") {
callback = unconfTransArr;
unconfTransArr = []
}
TransactionDb.hasTransaction(transaction.getId(), function(res) {
if (res) {
callback("Transaction " + transaction.getStringId() + " is already in the blockchain")
}
function validContinue() {
if (!transaction.verify()) {
callback("Signature verification failed for transaction " + transaction.getStringId())
}
if (transaction.getId().equals(new long(0))) {
callback("Invalid transaction id")
}
try {
transaction.validateAttachment()
} catch (e) {
callback(e)
}
Account.getAccounts(transaction.senderId.toString(), function(accounts) {
if (accounts == null || transaction.amount + transaction.fee > parseFloat(accounts.nxtlAccount.unconfirmedAmount)) {
callback("Not Enough money accId:" + transaction.senderId.toString())
}
callback(null)
})
}
TransactionDb.hasTransaction(transaction.referencedTransactionId.toString(), function(res) {
if (!res && transaction.referencedTransactionId != null) {
var isInUnconfTx = false;
for (var index = 0; index < unconfTransArr.length; ++index) {
var tx = unconfTransArr[index];
if (tx.id.toString() == transaction.referencedTransactionId.toString()) {
isInUnconfTx = true;
break
}
}
UnconfirmedTransactions.hasTransaction(transaction.referencedTransactionId.toString(), function(res) {
if (!res && !isInUnconfTx) {
callback("Missing referenced transaction " + transaction.referencedTransactionId.toString() + " for transaction " + transaction.getStringId())
} else {
validContinue()
}
})
} else {
validContinue()
}
})
})
};
TransactionProcessor.prototype.addTransactionOrConfirmation = function(transaction, withConfirmation) {
if (typeof withConfirmation === "undefined") {
withConfirmation = false
}
UnconfirmedTransactions.findTransaction(transaction.getId().toString(), function(resTx) {
if (resTx) {
if (resTx.confirmations < 1440) {
logger.info("Add TX confirmation txID: " + transaction.getId().toString());
UnconfirmedTransactions.addConfirmation(transaction.getId().toString())
}
} else {
logger.info("Add new TX from broadcast txID: " + transaction.getId().toString());
if (withConfirmation === true) {
transaction.confirmations += 1
} else {
transaction.confirmations = 1
}
UnconfirmedTransactions.addTransactions([transaction]);
var senderAccount = Account.addOrGetAccount(transaction.getSenderId().toString());
senderAccount.addToUnconfirmedBalance(-Utils.roundTo5Float(transaction.amount) - Utils.roundTo5Float(transaction.fee));
var recipientAccount = Account.addOrGetAccount(transaction.recipientId.toString());
recipientAccount.addToUnconfirmedBalance(Utils.roundTo5Float(transaction.amount));
var blockchain = new Blockchain();
blockchain.setLastTransaction(transaction);
logger.info("Added transaction", transaction.getId().toString());
NodeServer.broadcastNewTransaction(transaction)
}
})
};
var TransactionType = function() {};
TransactionType.TYPE_PAYMENT = 0;
TransactionType.TYPE_MESSAGING = 1;
TransactionType.TYPE_COLORED_COINS = 2;
TransactionType.SUBTYPE_PAYMENT_ORDINARY_PAYMENT = 0;
TransactionType.SUBTYPE_MESSAGING_ARBITRARY_MESSAGE = 0;
TransactionType.SUBTYPE_MESSAGING_ALIAS_ASSIGNMENT = 1;
TransactionType.SUBTYPE_MESSAGING_POLL_CREATION = 2;
TransactionType.SUBTYPE_MESSAGING_VOTE_CASTING = 3;
TransactionType.SUBTYPE_COLORED_COINS_ASSET_ISSUANCE = 0;
TransactionType.SUBTYPE_COLORED_COINS_ASSET_TRANSFER = 1;
TransactionType.SUBTYPE_COLORED_COINS_ASK_ORDER_PLACEMENT = 2;
TransactionType.SUBTYPE_COLORED_COINS_BID_ORDER_PLACEMENT = 3;
TransactionType.SUBTYPE_COLORED_COINS_ASK_ORDER_CANCELLATION = 4;
TransactionType.SUBTYPE_COLORED_COINS_BID_ORDER_CANCELLATION = 5;
TransactionType.findTransactionType = function(type, subtype) {
switch (type) {
case TransactionType.TYPE_PAYMENT:
switch (subtype) {
case TransactionType.SUBTYPE_PAYMENT_ORDINARY_PAYMENT:
return TransactionType.Payment;
default:
return null
}
default:
return null
}
};
TransactionType.Payment = function() {};
TransactionType.Payment.getType = function() {
return TransactionType.TYPE_PAYMENT
};
TransactionType.Payment.getSubtype = function() {
return TransactionType.SUBTYPE_PAYMENT_ORDINARY_PAYMENT
};
TransactionType.Payment.loadAttachment = function(transaction, buffer) {
TransactionType.Payment.validateAttachment(transaction)
};
TransactionType.Payment.loadAttachment = function(transaction, attachmentData) {
TransactionType.Payment.validateAttachment(transaction)
};
TransactionType.Payment.applyAttachmentUnconfirmed = function(transaction, senderAccount) {
return true
};
TransactionType.Payment.applyAttachment = function(transaction, senderAccount, recipientAccount) {
recipientAccount.addToBalanceAndUnconfirmedBalance(transaction.amount)
};
TransactionType.Payment.undoAttachment = function(transaction, senderAccount, recipientAccount) {
recipientAccount.addToBalanceAndUnconfirmedBalance(-transaction.amount)
};
TransactionType.Payment.undoAttachmentUnconfirmed = function(transaction, senderAccount) {};
TransactionType.Payment.isDuplicate = function(transaction, duplicates) {
return false
};
TransactionType.Payment.updateTotals = function(transaction, accumulatedAmounts, accumulatedAssetQuantities, accumulatedAmount) {};
TransactionType.Payment.validateAttachment = function(transaction) {
if (transaction.amount <= 0 || transaction.amount >= Config.MAX_BALANCE) {
throw new Error("Invalid ordinary payment: " + transaction.attachment.getJSON())
}
};
var Blockchain = function() {
var lastBlock = null;
var lastTransaction = null;
this.getBlock = function(blockId) {};
this.setLastTransaction = function(transaction) {
lastTransaction = transaction
};
this.getLastTransaction = function() {
return lastTransaction
};
this.setLastBlock = function(block) {
lastBlock = block
};
this.getLastBlock = function() {
return lastBlock
};
this.getAllBlocks = function(callback) {};
this.getAllTransactions = function(callback) {};
if (Blockchain.instance) {
return Blockchain.instance
}
Blockchain.instance = this
};
var BlockchainProcessor = function() {
events.EventEmitter.call(this)
};
util.inherits(BlockchainProcessor, events.EventEmitter);
BlockchainProcessor.run = function(callback) {
Account.addOrGetAccount("7684489094182123925");
BlockchainProcessor.addGenesisBlock(function() {
scan(function() {
logger.info("blockchain run end");
if (typeof callback == "function") {
callback()
}
})
})
};
BlockchainProcessor.addGenesisBlock = function(callback) {
BlockDb.hasBlock(Genesis.genesisBlockId, function(has) {
if (has) {
logger.info("Genesis block already in database");
if (typeof callback === "function") {
callback()
}
} else {
logger.info("Genesis block not in database, starting from scratch");
try {
var transactionsMap = {
count: 0
};
for (var i = 0; i < Genesis.genesisRecipients.length; i++) {
var transaction = new Transaction({
type: TransactionType.Payment,
timestamp: 0,
deadline: 0,
senderPublicKey: Genesis.creatorPublicKey,
recipientId: Genesis.genesisRecipients[i],
amount: Genesis.genesisAmounts[i],
fee: 0,
referencedTransactionId: 0,
signature: Genesis.genesisSignatures[i]
});
transactionsMap[transaction.getId().toString()] = transaction;
transactionsMap.count++
}
var digest = crypto.createHash("sha256");
for (var transactionId in transactionsMap) {
if (transactionsMap.hasOwnProperty(transactionId) && transactionId != "count") {
transaction = transactionsMap[transactionId];
digest.update(transaction.getBytes())
}
}
var genesisBlock = new Block({
version: -1,
timestamp: 0,
previousBlockId: null,
totalAmount: 1e9,
totalFee: 0,
payloadLength: transactionsMap.count * 128,
payloadHash: digest.digest(),
generatorPublicKey: Genesis.creatorPublicKey,
generationSignature: Config.NULL_HASH,
blockSignature: Genesis.genesisBlockSignature,
previousBlockHash: null,
blockTransactions: transactionsMap
});
genesisBlock.setPrevious(null);
logger.info("genesisBlock.verifyBlockSignature()", genesisBlock.verifyBlockSignature());
BlockchainProcessor.addBlock(genesisBlock, true);
if (typeof callback === "function") {
callback()
}
} catch (e) {
logger.error(e.stack ? e.stack : e.toString());
throw new Error(e)
}
}
})
};
BlockchainProcessor.generateBlock = function(secretPhrase) {
try {
UnconfirmedTransactions.getAll(function(transactionsArr) {
if (transactionsArr.count > 0) {
var totalFee = 0;
var totalAmount = 0;
var digest = crypto.createHash("sha256");
for (var transactionId in transactionsArr) {
if (transactionsArr.hasOwnProperty(transactionId) && transactionId != "count") {
var transaction = transactionsArr[transactionId];
digest.update(transaction.getBytes());
totalAmount += Utils.roundTo5Float(transaction.amount);
totalFee += Utils.roundTo5Float(transaction.fee)
}
}
var blockchain = new Blockchain();
var previousBlock = blockchain.getLastBlock();
var previousBlockHash = Utils.sha256(previousBlock.getBytes());
var generatorPublicKey = nxtCrypto.getPublicKey(secretPhrase);
var generationSignature = null;
if (previousBlock.height < Config.TRANSPARENT_FORGING_BLOCK) {
generationSignature = nxtCrypto.sign(previousBlock.generationSignature.toString("hex"), secretPhrase)
} else {
digest.update(previousBlock.generationSignature);
digest.update(generatorPublicKey);
generationSignature = digest.digest()
}
var block = new Block({
version: 1,
timestamp: new Date().getTime(),
previousBlockId: previousBlock.id,
totalAmount: Utils.roundTo5Float(totalAmount),
totalFee: Utils.roundTo5Float(totalFee),
payloadLength: transactionsArr.length * 128,
payloadHash: digest.digest(),
generatorPublicKey: generatorPublicKey,
generationSignature: generationSignature,
blockSignature: null,
previousBlockHash: previousBlockHash,
blockTransactions: transactionsArr
});
block.sign(secretPhrase);
block.setPrevious(previousBlock);
logger.info("Generating block", block.getId().toString());
try {
logger.info(block.verifyBlockSignature());
block.verifyGenerationSignature(function(res) {
logger.info(res)
});
if (block.verifyBlockSignature()) {
block.verifyGenerationSignature(function(res) {
if (!res) {
logger.error("Account " + block.getGeneratorId() + " generated an incorrect block.")
}
BlockchainProcessor.pushBlock(block, function() {
BlockDb.setNextBlockId(previousBlock.getId().toString(), block.getId().toString(), function() {
BlockchainProcessor.addBlock(block, false)
})
});
logger.info("Account " + block.getGeneratorId() + " generated block " + block.getStringId())
})
} else {
logger.error("Account " + block.getGeneratorId() + " generated an incorrect block.")
}
} catch (err) {
logger.error("BlockchainProcessor.generateBlock error");
logger.error(err.toString())
}
} else {
logger.info("No new transactions to generate block.")
}
})
} catch (e) {
logger.error("Create block ERROR:", e);
throw new Error(e)
}
};
BlockchainProcessor.pushBlock = function(block, _callback) {
var curTime = new Date().getTime();
var blockchain = new Blockchain();
var previousLastBlock = blockchain.getLastBlock();
if (previousLastBlock.getId().toString() != block.previousBlockId.toString()) {
throw new Error("Previous block id doesn't match")
}
if (Utils.sha256(previousLastBlock.getBytes()).toString("hex") != block.previousBlockHash.toString("hex")) {
throw new Error("Previous block hash doesn't match")
}
if (parseInt(block.timestamp) > curTime + 15 || parseInt(block.timestamp) <= parseInt(previousLastBlock.timestamp)) {
throw new Error("Invalid timestamp: " + block.timestamp + " current time is " + curTime + ", previous block timestamp is " + previousLastBlock.timestamp)
}
if (block.getId().equals(long.fromInt(0))) {
throw new Error("Invalid id")
}
BlockDb.hasBlock(block.getId(), function(res) {
if (res) {
throw new Error("Duplicate ID")
}
if (!block.verifyBlockSignature()) {
throw new Error("Signature verifyBlockSignature verification failed")
}
block.verifyGenerationSignature(function(res) {
if (!res) {
throw new Error("Signature verifyGenerationSignature verification failed")
}
var calculatedTotalAmount = 0,
calculatedTotalFee = 0,
duplicates = null,
accumulatedAmounts = {},
accumulatedAssetQuantities = null,
digest = crypto.createHash("sha256");
var transactionsArr = block.getTransactionsAsArray();
async.eachSeries(transactionsArr, function(transaction, callback) {
if (transaction.getExpiration() < block.timestamp) {
callback("Invalid transaction timestamp " + transaction.timestamp + " for transaction " + transaction.getStringId() + ", current time is " + curTime + ", block timestamp is " + block.timestamp)
}
TransactionDb.hasTransaction(transaction.getId(), function(res) {
if (res) {
callback("Transaction " + transaction.getStringId() + " is already in the blockchain")
}
TransactionDb.hasTransaction(transaction.referencedTransactionId.toString(), function(res) {
if (!res && transaction.referencedTransactionId != null && block.getTransactionIds().indexOf(transaction.referencedTransactionId.toString()) === -1) {
callback("Missing referenced transaction " + transaction.referencedTransactionId.toString() + " for transaction " + transaction.getStringId())
}
if (!transaction.verify()) {
callback("Signature verification failed for transaction " + transaction.getStringId())
}
if (transaction.getId().equals(new long(0))) {
callback("Invalid transaction id")
}
if (transaction.isDuplicate(duplicates)) {
callback("Transaction is a duplicate: " + transaction.getStringId())
}
try {
transaction.validateAttachment()
} catch (e) {
callback(e)
}
calculatedTotalAmount += Utils.roundTo5Float(transaction.amount);
transaction.updateTotals(accumulatedAmounts, accumulatedAssetQuantities);
calculatedTotalFee += Utils.roundTo5Float(transaction.fee);
digest.update(transaction.getBytes());
callback()
})
})
}, function(err) {
if (err) {
logger.error(err);
throw new Error(err)
} else {
logger.info("All transactions passed async verification");
if (Utils.roundTo5Float(calculatedTotalAmount) != Utils.roundTo5Float(block.totalAmount) || Utils.roundTo5Float(calculatedTotalFee) != Utils.roundTo5Float(block.totalFee)) {
throw new Error("Total amount or fee don't match transaction totals")
}
var digestStr = digest.digest().toString("hex");
if (digestStr != block.payloadHash.toString("hex")) {
logger.error("Payload hash doesn't match block ID: " + block.id.toString());
throw new Error("Payload hash doesn't match")
}
var accumulatedAmountsArr = [];
for (var accumulatedAmountEntryId in accumulatedAmounts) {
if (accumulatedAmounts.hasOwnProperty(accumulatedAmountEntryId)) {
accumulatedAmountsArr.push({
key: accumulatedAmountEntryId,
amount: accumulatedAmounts[accumulatedAmountEntryId]
})
}
}
async.each(accumulatedAmountsArr, function(accumulatedAmountEntry, callback) {
var senderAccount = Account.getAccounts(accumulatedAmountEntry.key, function(senderAccauntNums) {
if (senderAccauntNums.nxtlAccount.amount < accumulatedAmountEntry.amount) {
callback("Not enough funds in sender account: " + Convert.toUnsignedLong(senderAccount.getId()))
} else {
callback()
}
})
}, function(err) {
if (err) {
throw new Error(err)
}
if (typeof _callback === "function") {
_callback()
}
})
}
})
})
})
};
BlockchainProcessor.checkExistBlock = function(block, _callback) {
function blockTransactionsCheck() {
var txs = block.getTransactionsAsArray();
async.each(txs, function(transaction, callback) {
BlockchainProcessor.checkExistTransaction(transaction, callback)
}, function(err) {
if (err) {
_callback(err)
} else {
_callback(null)
}
})
}
if (!block.verifyBlockSignature()) {
_callback("Signature verifyBlockSignature verification failed: block height " + block.height);
return
}
if (parseInt(block.version) > 0) {
var prevHeight = block.height - 1;
BlockDb.findBlockIdAtHeight(prevHeight, function(prevBlock) {
if (!prevBlock) {
_callback("Error no prev block find: block height " + block.height)
} else {
if (prevBlock.getId().toString() != block.previousBlockId.toString()) {
_callback("Previous block id doesn't match: block height " + block.height);
return
}
if (Utils.sha256(prevBlock.getBytes()).toString("hex") != block.previousBlockHash.toString("hex")) {
_callback("Previous block hash doesn't match: block height " + block.height);
return
}
block.verifyGenerationSignature(function(res) {
if (!res) {
_callback("Signature verifyGenerationSignature verification failed: block height " + block.height)
} else {
blockTransactionsCheck()
}
})
}
})
} else {
blockTransactionsCheck()
}
};
BlockchainProcessor.checkExistTransaction = function(tx, _callback) {
function txVerify(tx, _callback) {
if (!tx.verify()) {
_callback("Signature verification failed for transaction " + transaction.getStringId());
return
}
if (tx.getId().equals(new long(0))) {
_callback("Invalid transaction id");
return
}
_callback()
}
if (tx.blockId.toString() == Genesis.genesisBlockId) {
txVerify(tx, _callback);
return
}
TransactionDb.hasTransaction(tx.referencedTransactionId.toString(), function(res) {
if (!res) {
_callback("Wrong referenced transaction ID");
return
} else {
txVerify(tx, _callback)
}
})
};
function scan(_callback) {
logger.info("Scanning blockchain...");
var blockchain = new Blockchain();
async.waterfall([
function(callback) {
BlockDb.getLastBlock(function(res) {
blockchain.setLastBlock(res);
logger.info("Last block set on height " + res.height);
callback()
})
},
function(callback) {
TransactionDb.getLastTransaction(function(res) {
blockchain.setLastTransaction(res);
logger.info("Last Transaction id is " + res.id)
});
callback()
},
function(callback) {
var curHeight = 0;
var lastHeight = blockchain.getLastBlock().height;
BlockDb.getAllBlockList(function(blocks) {
if (!blocks) {
throw Error("No block finded at height " + curHeight)
}
async.eachSeries(blocks, function(blockData, _callback) {
var block = new Block(blockData);
BlockDb.findRelatedTransactions(block, function(block) {
BlockchainProcessor.checkExistBlock(block, function(err) {
if (err) {
_callback(err)
} else {
block.addConfirmedAndUnconfirmedAmounts();
setImmediate(function() {
_callback()
})
}
})
})
}, function(err) {
if (err) {
callback(err)
} else {
callback(null)
}
})
})
}
], function(err, result) {
if (err) {
logger.error("Scan blockchain ERROR", err);
throw new Error(err)
}
if (typeof _callback == "function") {
_callback()
}
});
logger.info("...Scanning blockchain done")
}
BlockchainProcessor.addBlock = function(block, withoutBalanceChange, callback) {
BlockDb.saveBlock(block, function() {
if (typeof withoutBalanceChange === "undefined" || !withoutBalanceChange) {
block.addConfirmedAmounts();
block.addUnconfirmedFee()
}
var blockchain = new Blockchain();
blockchain.setLastBlock(block);
block.removeUnconfirmedTxs();
NodeServer.broadcastNewBlock(block);
if (typeof callback === "function") {
callback()
}
})
};
var Nxtl = function() {};
Nxtl.getBlockchain = function() {
return new Blockchain()
};
var BlockDb = function() {};
BlockDb.getLastBlock = function(callback) {
var q = {
tbl: "block"
};
DB.dbTx.find(q).limit(1).sort({
height: -1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
if (docs.length > 0) {
var block = new Block(docs[0]);
BlockDb.findRelatedTransactions(block, callback)
} else {
callback(null)
}
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err)
}
})
};
BlockDb.getAllBlockList = function(callback) {
var q = {
tbl: "block"
};
DB.dbTx.find(q).sort({
height: 1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
callback(docs)
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err);
callback(false)
}
})
};
BlockDb.getLastBlocksList = function(n, callback) {
var q = {
tbl: "block"
};
DB.dbTx.find(q).limit(n).sort({
height: -1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
callback(docs)
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err);
callback(false)
}
})
};
BlockDb.findRelatedTransactions = function(block, callback) {
TransactionDb.findBlockTransactions(block.id.toString(), function(txs) {
if (txs === null) {
txs = {
count: 0
}
}
block.blockTransactions = txs;
callback(block)
})
};
BlockDb.findBlock = function(blockId, callback) {
var q = {
id: blockId,
tbl: "block"
};
DB.dbTx.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var block = false;
if (docs.length > 0) {
block = new Block(docs[0]);
BlockDb.findRelatedTransactions(block, callback)
} else {
callback(block)
}
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err)
}
})
};
BlockDb.hasBlock = function(blockId, callback) {
var q = {
id: blockId,
tbl: "block"
};
DB.dbTx.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
if (docs.length > 0) {
callback(true)
} else {
callback(false)
}
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
BlockDb.findBlockIdAtHeight = function(height, callback) {
var q = {
height: height,
tbl: "block"
};
DB.dbTx.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var block = false;
if (docs.length > 0) {
block = new Block(docs[0]);
BlockDb.findRelatedTransactions(block, callback)
} else {
callback(block)
}
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err);
callback(false)
}
})
};
BlockDb.findBlockByRs = function(rs, callback) {
rs.tbl = "block";
DB.dbTx.find(rs, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var block = false;
if (docs.length > 0) {
block = new Block(docs[0]);
BlockDb.findRelatedTransactions(block, callback)
} else {
callback(block)
}
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
BlockDb.saveBlock = function(block, callback) {
if (block instanceof Block) {
var tmpBlock = block.getData();
tmpBlock.tbl = "block";
if (block.blockTransactions.count > 0 || block.blockTransactions.length > 0) {
TransactionDb.deleteTransactions(block.blockTransactions);
UnconfirmedTransactions.deleteTransactions(block.blockTransactions);
TransactionDb.saveTransactions(block.blockTransactions)
}
DB.dbTx.insert(tmpBlock, function(err, newDoc) {
if (err) {
logger.info("Transaction insert ERROR", err)
} else {
if (typeof callback === "function") {
callback()
}
}
})
}
};
BlockDb.setNextBlockId = function(blockId, nextBlockId, callback) {
if (blockId === 0) {
callback()
} else {
DB.dbTx.update({
id: blockId,
tbl: "block"
}, {
$set: {
nextBlockId: nextBlockId
}
}, {}, function(err, numReplaced) {
if (err) {
logger.info("setNextBlockId error")
} else {
if (typeof callback === "function") {
callback(numReplaced)
}
}
})
}
};
BlockDb.deleteBlockAtHeight = function(height, callback) {
height = parseInt(height);
DB.dbTx.remove({
height: height
}, {
multi: true
}, function(err, numRemoved) {
if (typeof callback === "function") {
if (err) {
callback(err)
} else {
callback(numRemoved)
}
}
})
};
BlockDb.deleteAll = function() {
DB.dbTx.remove({}, {}, function(err, numRemoved) {
if (err) {
logger.info("Error drop DB", err)
}
})
};
BlockDb.addConfirmation = function(blockId, callback) {
var q = {
id: blockId,
tbl: "block"
};
DB.dbTx.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var block = false;
if (docs.length > 0) {
block = docs[0];
var confirmations = block.confirmations + 1;
DB.dbTx.update(q, {
$set: {
confirmations: confirmations
}
}, {}, function() {
callback()
})
} else {
callback(false)
}
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err);
callback(err)
}
})
};
var PeersDb = function() {};
PeersDb.addPeer = function(peer, callback) {
var peerTmp = peer.getData();
peerTmp.id = peer.host + ":" + peer.port;
DB.dbPeers.insert(peerTmp, function(err, newDoc) {
if (err) {
logger.error("Peer insert ERROR", err)
} else {
logger.DBdbg("PeersDb.addPeer ok: " + peerTmp.id);
if (typeof callback === "function") {
callback(newDoc)
}
}
})
};
PeersDb.addReplacePeer = function(peer, callback) {
var peerTmp = peer.getData();
peerTmp.id = peer.host + ":" + peer.port;
DB.dbPeers.update({
id: peerTmp.id
}, peerTmp, {}, function(err, numReplaced) {
if (err) {
logger.error("Peer insert ERROR", err)
} else {
logger.DBdbg("PeersDb.addReplacePeer " + peerTmp.id + " numReplaced " + numReplaced);
if (numReplaced > 0) {
if (typeof callback === "function") {
callback(numReplaced)
}
} else {
PeersDb.addPeer(peer, callback)
}
}
})
};
PeersDb.getAllPeersList = function(callback) {
var q = {};
PeersDb.getPeersListByRs(q, callback)
};
PeersDb.getPeersListByRs = function(q, callback) {
DB.dbPeers.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
callback(docs)
}
} else {
logger.error("Find Peer ERROR!!!", err)
}
})
};
var TransactionDb = function() {};
TransactionDb.getLastTransaction = function(callback) {
var q = {
tbl: "transaction"
};
DB.dbTx.find(q).limit(1).sort({
timestamp: -1,
id: -1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var transaction = false;
if (docs.length > 0) {
transaction = new Transaction(docs[0])
}
callback(transaction)
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
TransactionDb.getLastTransactions = function(n, callback) {
var q = {
tbl: "transaction"
};
DB.dbTx.find(q).limit(n).sort({
timestamp: -1,
id: -1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
if (docs.length > 0) {
callback(docs)
} else {
callback(null)
}
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
TransactionDb.getUnconfirmedTransactions = function(callback) {
var q = {
blockId: null,
tbl: "transaction"
};
DB.dbTx.find(q).sort({
timestamp: 1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var transactionsMap = false;
if (docs.length > 0) {
transactionsMap = {
count: 0
};
for (var i in docs) {
transactionsMap[docs[i].id] = new Transaction(docs[i]);
transactionsMap.count++
}
}
callback(transactionsMap)
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err)
}
})
};
TransactionDb.getMyTransactions = function(_accountId, callback) {
var q = {
$or: [{
recipientId: _accountId
}, {
senderId: _accountId
}],
$not: {
blockId: null
},
type: TransactionType.TYPE_PAYMENT,
tbl: "transaction"
};
TransactionDb.getTransactionsListByRs(q, callback)
};
TransactionDb.getMyAllTransactions = function(_accountId, callback) {
var q = {
$or: [{
recipientId: _accountId
}, {
senderId: _accountId
}],
type: TransactionType.TYPE_PAYMENT,
tbl: "transaction"
};
DB.dbTx.find(q).sort({
timestamp: -1,
id: -1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
callback(docs)
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err)
}
})
};
TransactionDb.getAllTransactionsList = function(callback) {
var q = {
tbl: "transaction"
};
TransactionDb.getTransactionsListByRs(q, callback)
};
TransactionDb.getTransactionsListByRs = function(q, callback) {
DB.dbTx.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
callback(docs)
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err)
}
})
};
TransactionDb.findTransaction = function(transactionId, callback) {
var q = {
id: transactionId,
tbl: "transaction"
};
DB.dbTx.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var transaction = false;
if (docs.length > 0) {
transaction = new Transaction(docs[0])
}
callback(transaction)
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
TransactionDb.hasTransaction = function(transactionId, callback) {
var q = {
id: transactionId,
tbl: "transaction"
};
DB.dbTx.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
if (docs.length > 0) {
callback(true)
} else {
callback(false)
}
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
TransactionDb.findTransactionByRs = function(rs, callback) {
rs.tbl = "transaction";
DB.dbTx.find(rs, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var transaction = false;
if (docs.length > 0) {
transaction = new Transaction(docs[0])
}
callback(transaction)
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
TransactionDb.findBlockTransactions = function(blockId, callback) {
var q = {
blockId: blockId,
tbl: "transaction"
};
DB.dbTx.find(q).sort({
timestamp: 1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var transactionsMap = {
count: 0
};
if (docs.length > 0) {
for (var i in docs) {
transactionsMap[docs[i].id] = new Transaction(docs[i]);
transactionsMap.count++
}
}
callback(transactionsMap)
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err);
callback(null)
}
})
};
TransactionDb.saveTransactions = function(transactions) {
for (var i in transactions) {
if (!transactions.hasOwnProperty(i) || i == "count") {
continue
}
var transaction = transactions[i];
var transactionTmp = transaction.getData();
transactionTmp.tbl = "transaction";
DB.dbTx.insert(transactionTmp, function(err, newDoc) {
if (err) {
logger.info("Transaction insert ERROR", err)
}
})
}
};
TransactionDb.deleteTransactions = function(transactions) {
for (var i in transactions) {
if (!transactions.hasOwnProperty(i) || i == "count") {
continue
}
var transaction = transactions[i];
var transactionTmp = transaction.getData();
transactionTmp.tbl = "transaction";
DB.dbTx.remove({
id: transactionTmp.id,
tbl: "transaction"
}, {}, function(err, numRemoved) {})
}
};
var Block = function(data) {
if ("object" !== typeof data) {
data = {}
}
this.version = data.version || null;
this.timestamp = data.timestamp || null;
this.previousBlockId = data.previousBlockId || 0;
this.generatorPublicKey = data.generatorPublicKey || Config.NULL_HASH;
this.previousBlockHash = data.previousBlockHash || Config.NULL_HASH;
this.totalAmount = data.totalAmount || 0;
this.totalFee = data.totalFee || 0;
this.payloadLength = data.payloadLength || 0;
this.generationSignature = data.generationSignature || Config.NULL_HASH;
this.payloadHash = data.payloadHash || Config.NULL_HASH;
this.transactionIds = data.transactionIds || [];
this.blockTransactions = data.blockTransactions || [];
this.blockSignature = data.blockSignature || null;
this.cumulativeDifficulty = data.cumulativeDifficulty || new bigint("0");
this.baseTarget = data.baseTarget || Config.INITIAL_BASE_TARGET;
this.nextBlockId = data.nextBlockId || null;
this.height = typeof data.height !== "undefined" ? data.height : -1;
this.id = data.id || null;
this.stringId = data.stringId || null;
this.generatorId = data.generatorId || null;
this.confirmations = data.confirmations || 0;
if (typeof this.previousBlockId == "string") {
this.previousBlockId = Utils.stringToLong(this.previousBlockId)
}
if (typeof this.generatorPublicKey == "string") {
this.generatorPublicKey = new Buffer(this.generatorPublicKey, "hex")
}
if (typeof this.previousBlockHash == "string") {
this.previousBlockHash = new Buffer(this.previousBlockHash, "hex")
}
if (typeof this.generationSignature == "string") {
this.generationSignature = new Buffer(this.generationSignature, "hex")
}
if (typeof this.payloadHash == "string") {
this.payloadHash = new Buffer(this.payloadHash, "hex")
}
if (typeof this.blockSignature == "string") {
this.blockSignature = new Buffer(this.blockSignature, "hex")
}
if (typeof this.nextBlockId == "string") {
this.nextBlockId = Utils.stringToLong(this.nextBlockId)
}
if (typeof this.id == "string") {
this.id = Utils.stringToLong(this.id)
}
if (typeof this.cumulativeDifficulty == "string") {
this.cumulativeDifficulty = new bigint(this.cumulativeDifficulty)
}
};
Block.prototype.getData = function() {
return {
version: this.version,
timestamp: this.timestamp,
previousBlockId: this.previousBlockId.toString(),
generatorPublicKey: this.generatorPublicKey.toString("hex"),
previousBlockHash: this.previousBlockHash.toString("hex"),
totalAmount: this.totalAmount,
totalFee: this.totalFee,
payloadLength: this.payloadLength,
generationSignature: this.generationSignature.toString("hex"),
payloadHash: this.payloadHash.toString("hex"),
blockSignature: this.blockSignature ? this.blockSignature.toString("hex") : null,
cumulativeDifficulty: this.cumulativeDifficulty.toString(),
baseTarget: this.baseTarget,
nextBlockId: this.nextBlockId ? this.nextBlockId.toString() : null,
height: this.height,
id: this.id.toString(),
stringId: this.stringId,
generatorId: this.generatorId ? this.generatorId.toString() : null,
confirmations: this.confirmations
}
};
Block.prototype.getDataWithTransactions = function() {
var data = this.getData();
data.blockTransactions = this.getTransactionsDataAsArray().slice(0);
return data
};
Block.prototype.getId = function() {
if (this.id == null) {
if (this.blockSignature == null) {
throw new Error("Block is not signed yet")
}
var hash = curve.sha256(this.getBytes());
this.id = Utils.bufferToLongBE(hash);
this.stringId = this.id.toString()
}
return this.id
};
Block.prototype.getStringId = function() {
if (this.stringId == null) {
this.getId();
if (this.stringId == null) {
this.stringId = this.id.toString()
}
}
return this.stringId
};
Block.prototype.getGeneratorId = function() {
if (this.generatorId == null) {
this.generatorId = Account.getId(this.generatorPublicKey)
}
return this.generatorId
};
Block.prototype.hashCode = function() {
var id = this.getId();
id.toString("16")
};
Block.prototype.getBytes = function() {
var self = this;
var obj = {
version: this.version,
timestamp: this.timestamp,
previousBlockId: this.previousBlockId.toString(),
blockTransactions: this.blockTransactions.count,
totalAmount: this.totalAmount,
totalFee: this.totalFee,
payloadLength: this.payloadLength,
payloadHash: this.payloadHash,
generatorPublicKey: this.generatorPublicKey,
generationSignature: this.generationSignature
};
if (this.version > 1) {
obj.previousBlockHash = this.previousBlockHash
}
return JSON.stringify(obj)
};
Block.prototype.sign = function(secretPhrase) {
if (this.blockSignature != null) {
return this.blockSignature
}
if (!secretPhrase instanceof Buffer) {
secretPhrase = curve.sha256(secretPhrase)
}
this.blockSignature = nxtCrypto.sign(curve.sha256(this.getBytes()).toString("hex"), secretPhrase.toString("hex"));
return this.blockSignature
};
Block.prototype.verifyBlockSignature = function() {
var account = Account.addOrGetAccount(this.getGeneratorId());
if (account == null) {
return false
}
var data = curve.sha256(this.getBytes());
var isSignVerified = nxtCrypto.verify(this.blockSignature.toString("hex"), data.toString("hex"), this.generatorPublicKey.toString("hex"));
return isSignVerified && account.setOrVerify(this.generatorPublicKey, this.height)
};
Block.prototype.verifyGenerationSignature = function(__callback) {
var self = this;
BlockDb.findBlock(this.previousBlockId.toString(), function(previousBlock) {
try {
if (previousBlock == null && self.height != 0) {
__callback(false)
}
var isSignVerified = nxtCrypto.verify(self.generationSignature.toString("hex"), previousBlock.generationSignature.toString("hex"), self.generatorPublicKey.toString("hex"));
if (self.version == 1 && !isSignVerified) {
__callback(false)
}
var account = Account.getAccount(self.getGeneratorId());
__callback(true)
} catch (e) {
logger.error("Error verifying block generation signature", e);
__callback(false)
}
})
};
Block.prototype.apply = function() {
var generatorAccount = Account.addOrGetAccount(this.getGeneratorId());
generatorAccount.apply(this.generatorPublicKey, this.height);
generatorAccount.addToBalanceAndUnconfirmedBalance(Utils.roundTo5Float(this.totalFee))
};
Block.prototype.setPrevious = function(previousBlock) {
if (previousBlock != null) {
if (!previousBlock.getId() == this.previousBlockId) {
throw new Error("Previous block id doesn't match")
}
this.height = previousBlock.height + 1;
this.calculateBaseTarget(previousBlock)
} else {
this.height = 0
}
for (var transactionId in this.blockTransactions) {
var transaction;
if (this.blockTransactions.hasOwnProperty(transactionId) && transactionId != "count") {
transaction = this.blockTransactions[transactionId];
transaction.setBlock(this)
}
}
};
Block.prototype.calculateBaseTarget = function(previousBlock) {
if (this.getId() == Genesis.genesisBlockId && this.previousBlockId == null) {
this.baseTarget = Config.INITIAL_BASE_TARGET;
this.cumulativeDifficulty = 0
} else {
var curBaseTarget = previousBlock.baseTarget;
var newBaseTarget = new bigint(curBaseTarget).multiply(this.timestamp - previousBlock.timestamp).divide(60);
newBaseTarget = Utils.bigIntToLongBE(newBaseTarget);
if (newBaseTarget < 0 || newBaseTarget > Config.MAX_BASE_TARGET) {
newBaseTarget = Config.MAX_BASE_TARGET
}
if (newBaseTarget < curBaseTarget / 2) {
newBaseTarget = curBaseTarget / 2
}
if (newBaseTarget == 0) {
newBaseTarget = 1
}
var twofoldCurBaseTarget = curBaseTarget * 2;
if (twofoldCurBaseTarget < 0) {
twofoldCurBaseTarget = Config.MAX_BASE_TARGET
}
if (newBaseTarget > twofoldCurBaseTarget) {
newBaseTarget = twofoldCurBaseTarget
}
this.baseTarget = newBaseTarget;
this.cumulativeDifficulty = previousBlock.cumulativeDifficulty.add(Config.two64.divide(this.baseTarget.toString()))
}
};
Block.prototype.getTransactionIds = function() {
if (!this.transactionIds || this.transactionIds.length == 0) {
this.transactionIds = [];
for (var transactionId in this.blockTransactions) {
var transaction;
if (this.blockTransactions.hasOwnProperty(transactionId) && transactionId != "count") {
transaction = this.blockTransactions[transactionId];
this.transactionIds.push(transaction.id.toString())
}
}
}
return this.transactionIds
};
Block.prototype.getTransactionsAsArray = function() {
var transactionsArr = [];
for (var transactionId in this.blockTransactions) {
if (this.blockTransactions.hasOwnProperty(transactionId) && transactionId != "count") {
transactionsArr.push(this.blockTransactions[transactionId])
}
}
return transactionsArr
};
Block.prototype.getTransactionsDataAsArray = function() {
var transactionsArr = [];
for (var transactionId in this.blockTransactions) {
if (this.blockTransactions.hasOwnProperty(transactionId) && transactionId != "count") {
transactionsArr.push(this.blockTransactions[transactionId].getData())
}
}
return transactionsArr
};
Block.prototype.addConfirmedAndUnconfirmedAmounts = function() {
for (var transactionId in this.blockTransactions) {
if (this.blockTransactions.hasOwnProperty(transactionId) && transactionId != "count") {
var tx = this.blockTransactions[transactionId];
var recipientAccount = Account.addOrGetAccount(tx.recipientId.toString());
recipientAccount.addToBalanceAndUnconfirmedBalance(Utils.roundTo5Float(Utils.nullToNumber(tx.amount)));
var senderAccount = Account.addOrGetAccount(tx.getSenderId().toString());
senderAccount.addToBalanceAndUnconfirmedBalance(-Utils.roundTo5Float(Utils.nullToNumber(tx.amount)) - Utils.roundTo5Float(tx.fee))
}
}
var _genereatorAccount = Account.addOrGetAccount(this.generatorId.toString());
_genereatorAccount.addToBalanceAndUnconfirmedBalance(Utils.roundTo5Float(this.totalFee))
};
Block.prototype.addConfirmedAmounts = function() {
for (var transactionId in this.blockTransactions) {
if (this.blockTransactions.hasOwnProperty(transactionId) && transactionId != "count") {
var tx = this.blockTransactions[transactionId];
var recipientAccount = Account.addOrGetAccount(tx.recipientId.toString());
recipientAccount.addToBalance(Utils.roundTo5Float(Utils.nullToNumber(tx.amount)));
var senderAccount = Account.addOrGetAccount(tx.senderId.toString());
senderAccount.addToBalance(-Utils.roundTo5Float(Utils.nullToNumber(tx.amount)) - Utils.roundTo5Float(tx.fee))
}
}
var _genereatorAccount = Account.addOrGetAccount(this.generatorId.toString());
_genereatorAccount.addToBalance(Utils.roundTo5Float(this.totalFee))
};
Block.prototype.addUnconfirmedFee = function() {
var _genereatorAccount = Account.addOrGetAccount(this.generatorId.toString());
_genereatorAccount.addToUnconfirmedBalance(Utils.roundTo5Float(this.totalFee))
};
Block.prototype.addUnconfirmedAmounts = function() {
for (var transactionId in this.blockTransactions) {
if (this.blockTransactions.hasOwnProperty(transactionId) && transactionId != "count") {
var tx = this.blockTransactions[transactionId];
var recipientAccount = Account.addOrGetAccount(tx.recipientId.toString());
recipientAccount.addToUnconfirmedBalance(Utils.roundTo5Float(Utils.nullToNumber(tx.amount)));
var senderAccount = Account.addOrGetAccount(tx.senderId.toString());
senderAccount.addToUnconfirmedBalance(-Utils.roundTo5Float(Utils.nullToNumber(tx.amount)) - Utils.roundTo5Float(tx.fee))
}
}
var _genereatorAccount = Account.addOrGetAccount(this.generatorId.toString());
_genereatorAccount.addToUnconfirmedBalance(Utils.roundTo5Float(this.totalFee))
};
Block.prototype.removeUnconfirmedTxs = function(callback) {
var txArr = this.getTransactionsAsArray();
UnconfirmedTransactions.deleteTransactions(txArr, callback)
};
var Transaction = function(data) {
if ("object" !== typeof data) {
data = {}
}
this.deadline = data.deadline || null;
this.senderPublicKey = data.senderPublicKey || null;
this.recipientId = data.recipientId || null;
this.amount = data.amount || 0;
this.fee = data.fee || null;
this.referencedTransactionId = data.referencedTransactionId || null;
this.type = typeof data.type !== "undefined" ? data.type : null;
this.height = data.height || null;
this.blockId = data.blockId || null;
this.block = data.block || null;
this.signature = data.signature || null;
this.timestamp = data.timestamp || null;
this.attachment = data.attachment || null;
this.id = data.id || null;
this.null = null;
this.senderId = data.senderId || null;
this.hash = data.hash || null;
this.confirmations = data.confirmations || 0;
if (typeof this.senderPublicKey == "string") {
this.senderPublicKey = new Buffer(this.senderPublicKey, "hex")
}
if (typeof this.recipientId == "string") {
this.recipientId = Utils.stringToLong(this.recipientId)
}
if (typeof this.referencedTransactionId == "string") {
this.referencedTransactionId = Utils.stringToLong(this.referencedTransactionId)
}
if (typeof this.type == "string" || typeof this.type == "number") {
this.type = TransactionType.findTransactionType(this.type, 0)
}
if (typeof this.blockId == "string") {
this.blockId = Utils.stringToLong(this.blockId)
}
if (typeof this.signature == "string") {
this.signature = new Buffer(this.signature, "hex")
}
if (typeof this.id == "string") {
this.id = Utils.stringToLong(this.id)
}
if (typeof this.senderId == "string") {
this.senderId = Utils.stringToLong(this.senderId)
}
if (typeof this.hash == "string") {
this.hash = new Buffer(this.hash, "hex")
}
};
Transaction.prototype.getData = function() {
var type = null;
if (this.type) {
type = this.type.getType()
}
return {
deadline: this.deadline,
senderPublicKey: this.senderPublicKey.toString("hex"),
recipientId: this.recipientId.toString(),
amount: this.amount,
fee: this.fee,
referencedTransactionId: this.referencedTransactionId ? this.referencedTransactionId.toString() : this.referencedTransactionId,
type: type,
height: this.height,
blockId: this.blockId ? this.blockId.toString() : null,
signature: this.signature ? this.signature.toString("hex") : null,
timestamp: this.timestamp,
attachment: this.attachment,
id: this.getId().toString(),
"null": null,
senderId: this.getSenderId().toString(),
hash: this.hash.toString("hex"),
confirmations: this.confirmations
}
};
Transaction.prototype.getBlock = function() {
if (this.block == null) {
var self = this;
BlockDb.findBlock(self.blockId, function(block) {
self.block = block
})
}
return this.block
};
Transaction.prototype.setBlock = function(block) {
this.block = block;
this.blockId = block.getId();
this.height = block.height
};
Transaction.prototype.getExpiration = function() {
return this.timestamp + this.deadline * 60 * 60 * 1e3
};
Transaction.prototype.getId = function() {
if (this.id == null) {
if (this.signature == null) {
return false
}
this.hash = curve.sha256(this.getBytes());
this.id = Utils.bufferToLongBE(this.hash);
this.stringId = this.id.toString()
}
return this.id
};
Transaction.prototype.getStringId = function() {
if (this.stringId == null) {
this.getId();
if (this.stringId == null) {
this.stringId = this.id.toString()
}
}
return this.stringId
};
Transaction.prototype.getSenderId = function() {
if (this.senderId == null) {
this.senderId = Account.getId(this.senderPublicKey)
}
return this.senderId
};
Transaction.prototype.getStringId = function() {
if (this.stringId == null) {
this.getId();
if (this.stringId == null) {
this.stringId = this.id.toString()
}
}
return this.stringId
};
Transaction.prototype.compareTo = function(o) {
if (this.height < o.height) {
return -1
}
if (this.height > o.height) {
return 1
}
if (Utils.nullToNumber(this.fee) * o.getSize() > Utils.nullToNumber(o.fee) * this.getSize() || this.fee === null && o.fee !== null) {
return -1
}
if (Utils.nullToNumber(this.fee) * o.getSize() < Utils.nullToNumber(o.fee) * this.getSize()) {
return 1
}
if (this.timestamp < o.timestamp) {
return -1
}
if (this.timestamp > o.timestamp) {
return 1
}
return 0
};
Transaction.prototype.TRANSACTION_BYTES_LENGTH = 1 + 1 + 4 + 2 + 32 + 8 + 4 + 4 + 8 + 64;
Transaction.prototype.getSize = function() {
return this.TRANSACTION_BYTES_LENGTH + (this.attachment == null ? 0 : this.attachment.getSize())
};
Transaction.prototype.getBytes = function() {
var self = this;
var obj = {
type: self.type.getType(),
subtype: self.type.getSubtype(),
timestamp: self.timestamp,
deadline: self.deadline,
senderPublicKey: self.senderPublicKey,
recipientId: self.recipientId,
amount: self.amount,
fee: self.fee,
referencedTransactionId: self.referencedTransactionId
};
if (self.attachment != null) {
obj.attachment = self.attachment.getBytes()
}
return JSON.stringify(obj)
};
Transaction.prototype.sign = function(secretPhrase) {
if (this.signature != null) {
return this.signature
}
console.log("Transaction sign", curve.sha256(this.getBytes()));
this.signature = nxtCrypto.sign(curve.sha256(this.getBytes()).toString("hex"), secretPhrase.toString("hex"));
try {
while (!this.verify()) {
this.timestamp++;
this.signature = nxtCrypto.sign(curve.sha256(this.getBytes()).toString("hex"), secretPhrase.toString("hex"))
}
return this.signature
} catch (e) {
console.log("Error signing transaction", e);
return false
}
};
Transaction.prototype.getHash = function() {
if (this.hash == null) {
var data = this.getBytes();
var hash = curve.sha256(data);
this.hash = hash.toString("hex")
}
return this.hash
};
Transaction.prototype.hashCode = function() {
var id = this.getId();
id.toString("16")
};
Transaction.prototype.verify = function() {
var account = Account.addOrGetAccount(this.getSenderId().toString());
if (account == null) {
return false
}
var data = curve.sha256(this.getBytes());
console.log("Transaction data virify", data);
var isSignVerified = nxtCrypto.verify(this.signature.toString("hex"), data.toString("hex"), this.senderPublicKey.toString("hex"));
return isSignVerified && account.setOrVerify(this.senderPublicKey, this.height)
};
Transaction.prototype.applyUnconfirmed = function() {
var senderAccount = Account.getAccount(this.getSenderId());
if (senderAccount == null) {
return false
}
return this.type.applyUnconfirmed(this, senderAccount)
};
Transaction.prototype.apply = function() {
var senderAccount = Account.getAccount(this.getSenderId());
senderAccount.apply(this.senderPublicKey, this.height);
var recipientAccount = Account.getAccount(this.recipientId);
if (recipientAccount == null) {
recipientAccount = Account.addOrGetAccount(recipientId)
}
this.type.apply(this, senderAccount, recipientAccount)
};
Transaction.prototype.undoUnconfirmed = function() {
var senderAccount = Account.getAccount(this.getSenderId());
this.type.undoUnconfirmed(this, senderAccount)
};
Transaction.prototype.undo = function() {
var senderAccount = Account.getAccount(this.senderId);
senderAccount.undo(this.height);
var recipientAccount = Account.getAccount(this.recipientId);
this.type.undo(this, senderAccount, recipientAccount)
};
Transaction.prototype.updateTotals = function(accumulatedAmounts, accumulatedAssetQuantities) {
var senderId = this.getSenderId();
var accumulatedAmount = accumulatedAmounts === null || typeof accumulatedAmounts[senderId.toString()] === "undefined" ? null : accumulatedAmounts[senderId.toString()];
if (accumulatedAmount == null) {
accumulatedAmount = 0
}
accumulatedAmounts[senderId.toString()] = Utils.roundTo5Float(accumulatedAmount) + (Utils.roundTo5Float(this.amount) + Utils.roundTo5Float(this.fee));
this.type.updateTotals(this, accumulatedAmounts, accumulatedAssetQuantities, accumulatedAmount)
};
Transaction.prototype.isDuplicate = function(duplicates) {
return this.type.isDuplicate(this, duplicates)
};
Transaction.prototype.validateAttachment = function() {
this.type.validateAttachment(this)
};
var UnconfirmedTransactions = function() {};
UnconfirmedTransactions.addConfirmation = function(id, callback) {
var q = {
id: id,
tbl: "transaction"
};
db.update(q, {
$inc: {
confirmations: 1
}
}, {}, function() {
if (typeof callback === "function") {
callback()
}
})
};
UnconfirmedTransactions.getLastTransaction = function(callback) {
var q = {
tbl: "transaction"
};
db.find(q).limit(1).sort({
timestamp: -1,
id: -1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var transaction = false;
if (docs.length > 0) {
transaction = new Transaction(docs[0])
}
callback(transaction)
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
UnconfirmedTransactions.getLastTransactions = function(n, callback) {
var q = {
tbl: "transaction"
};
db.find(q).limit(n).sort({
timestamp: -1,
id: -1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
if (docs.length > 0) {
callback(docs)
} else {
callback(null)
}
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
UnconfirmedTransactions.getAll = function(callback) {
var q = {
blockId: null,
tbl: "transaction"
};
db.find(q).sort({
timestamp: 1,
id: 1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var transactionsMap = false;
if (docs.length > 0) {
transactionsMap = {
count: 0
};
for (var i in docs) {
transactionsMap[docs[i].id] = new Transaction(docs[i]);
transactionsMap.count++
}
}
callback(transactionsMap)
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err)
}
})
};
UnconfirmedTransactions.getMyTransactions = function(_accountId, callback) {
var q = {
$or: [{
recipientId: _accountId
}, {
senderId: _accountId
}],
$not: {
blockId: null
},
type: TransactionType.TYPE_PAYMENT,
tbl: "transaction"
};
UnconfirmedTransactions.getTransactionsListByRs(q, callback)
};
UnconfirmedTransactions.getMyAllTransactions = function(_accountId, callback) {
var q = {
$or: [{
recipientId: _accountId
}, {
senderId: _accountId
}],
type: TransactionType.TYPE_PAYMENT,
tbl: "transaction"
};
db.find(q).sort({
timestamp: -1,
id: -1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
callback(docs)
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err)
}
})
};
UnconfirmedTransactions.getAllTransactionsList = function(callback) {
var q = {
tbl: "transaction"
};
db.find(q).sort({
timestamp: -1,
id: -1
}).exec(function(err, docs) {
if (!err) {
if (typeof callback === "function") {
callback(docs)
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err)
}
})
};
UnconfirmedTransactions.getTransactionsListByRs = function(q, callback) {
db.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
callback(docs)
}
} else {
logger.info("Find BlockTransactions ERROR!!!", err)
}
})
};
UnconfirmedTransactions.findTransaction = function(transactionId, callback) {
var q = {
id: transactionId,
tbl: "transaction"
};
db.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var transaction = false;
if (docs.length > 0) {
transaction = new Transaction(docs[0])
}
callback(transaction)
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
UnconfirmedTransactions.hasTransaction = function(transactionId, callback) {
var q = {
id: transactionId,
tbl: "transaction"
};
db.find(q, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
if (docs.length > 0) {
callback(true)
} else {
callback(false)
}
}
} else {
logger.error("Find transaction ERROR!!!", err)
}
})
};
UnconfirmedTransactions.findTransactionByRs = function(rs, callback) {
rs.tbl = "transaction";
db.find(rs, function(err, docs) {
if (!err) {
if (typeof callback === "function") {
var transaction = false;
if (docs.length > 0) {
transaction = new Transaction(docs[0])
}
callback(transaction)
}
} else {
logger.info("Find transaction ERROR!!!", err)
}
})
};
UnconfirmedTransactions.addTransactions = function(transactions) {
for (var i in transactions) {
if (!transactions.hasOwnProperty(i) || i == "count") {
continue
}
var transaction = transactions[i];
var transactionTmp = transaction.getData();
transactionTmp.tbl = "transaction";
db.insert(transactionTmp, function(err, newDoc) {
if (err) {
logger.info("Transaction insert ERROR", err)
}
})
}
};
UnconfirmedTransactions.addTransactionsData = function(transactionsData, callback) {
for (var i in transactionsData) {
if (!transactionsData.hasOwnProperty(i) || i == "count") {
continue
}
var transactionTmp = transactionsData[i];
transactionTmp.tbl = "transaction";
db.insert(transactionTmp, function(err, newDoc) {
if (err) {
logger.info("Transaction insert ERROR", err);
callback(false)
} else {
callback(true)
}
})
}
};
UnconfirmedTransactions.addTransactionsData = function(transactionsData, callback) {
for (var i in transactionsData) {
if (!transactionsData.hasOwnProperty(i) || i == "count") {
continue
}
var transactionTmp = transactionsData[i];
transactionTmp.tbl = "transaction";
db.insert(transactionTmp, function(err, newDoc) {
if (err) {
logger.info("Transaction insert ERROR", err);
callback(false)
} else {
callback(true)
}
})
}
};
UnconfirmedTransactions.deleteTransactions = function(transactions, callback) {
for (var i in transactions) {
if (!transactions.hasOwnProperty(i) || i == "count") {
continue
}
var transaction = transactions[i];
var transactionTmp = transaction.getData();
transactionTmp.tbl = "transaction";
db.remove({
id: transactionTmp.id,
tbl: "transaction"
}, {}, function(err, numRemoved) {
if (typeof callback === "function") {
callback()
}
})
}
};
function Connection(socket, peer) {
events.EventEmitter.call(this);
this.socket = socket;
this.peer = peer;
this.buffer = "";
this.active = false;
this.inbound = !!socket.server;
this.setupHandlers()
}
util.inherits(Connection, events.EventEmitter);
Connection.prototype.commands = {
VERSION: "version",
READY: "verack",
ADDRESSES: "addr",
GET_ADDRESSES: "getaddr",
GET_TRANSACTIONS: "gettrans",
GET_LAST_TRANSACTION: "getlasttrans",
LAST_TRANSACTION: "lasttrans",
GET_NEXT_BLOCK: "getnextblock",
NEW_TRANSACTION: "newtrans",
BLOCK: "block",
GET_PREV_TRANSACTION: "getprevtrans",
BROADCAST: "broadcast",
PEER_STATUS: "peerstatus",
BROADCAST_GENERATE_BLOCK: "bcgenblk",
STOP_GENERATE_BLOCK: "stopgenblk",
ANSWER_ON_GENERATE_BLOCK: "answergenblk",
NEW_BLOCK: "newblk",
VERIFIED_PEER: "verifpeer",
VERIFIED_PEER_RESPONSE: "verifpeerresp",
PEER_NOT_VERIFIED: "peernotverif",
UNCONFIRMED_TRANSACTIONS: "unconftrans"
};
Connection.prototype.toString = function() {
return "Connection: '" + this.peer.toString() + "',\n" + "inbound(isServer): " + this.inbound
};
Connection.prototype.setupHandlers = function() {
this.socket.addListener("connect", this.handleConnect.bind(this));
this.socket.addListener("error", this.handleError.bind(this));
this.socket.addListener("end", this.handleDisconnect.bind(this));
this.socket.addListener("data", function(data) {
var _data = data.toString(),
DATA_LENGTH_SHOW = 31,
dataMsg = _data.length > DATA_LENGTH_SHOW ? _data.substring(0, DATA_LENGTH_SHOW) + "..." : _data;
logger.netdbg("[" + this.peer + "] " + "Received " + dataMsg + " bytes of data: " + Buffer.byteLength(_data, "utf8"));
logger.netdbg(this.toString())
}.bind(this));
this.socket.addListener("data", this.handleData.bind(this))
};
Connection.prototype.handleConnect = function() {
if (!this.inbound) {
this.sendVersion()
}
this.emit("connect", {
conn: this,
socket: this.socket,
peer: this.peer
})
};
Connection.prototype.handleError = function(err) {
if (err.errno == 110 || err.errno == "ETIMEDOUT") {
logger.netdbg("Connection timed out for " + this.peer)
} else if (err.errno == 111 || err.errno == "ECONNREFUSED") {
logger.netdbg("Connection refused for " + this.peer)
} else {
logger.warn("Connection with " + this.peer + " " + err.toString())
}
this.emit("error", {
conn: this,
socket: this.socket,
peer: this.peer,
err: err
})
};
Connection.prototype.handleDisconnect = function() {
this.emit("disconnect", {
conn: this,
socket: this.socket,
peer: this.peer
})
};
Connection.prototype.handleData = function(data) {
this.buffer += data.toString();
if (this.buffer.indexOf("___|||___") == -1) {
return
}
var datas = this.buffer.split("___|||___");
this.buffer = datas[datas.length - 1];
delete datas[datas.length - 1];
var self = this;
datas.forEach(function(_data) {
var message;
try {
message = self.parseMessage(_data)
} catch (e) {
logger.error("Error while parsing message " + _data + "\n from [" + self.peer + "]\nmessage: " + e.toString())
}
if (message) {
self.peer.uploadBytes += Buffer.byteLength(_data, "utf8");
self.handleMessage(message)
}
})
};
Connection.prototype.handleMessage = function(message) {
logger.netdbg("Handle Message (message command: '" + message.command + "')\n" + this.toString());
switch (message.command) {
case Connection.prototype.commands.VERSION:
if (this.inbound) {
this.sendVersion()
}
this.sendMessage(Connection.prototype.commands.READY);
break;
case Connection.prototype.commands.READY:
this.active = true;
break
}
this.emit(message.command, {
conn: this,
socket: this.socket,
peer: this.peer,
message: message
})
};
Connection.prototype.parseMessage = function(message) {
message = JSON.parse(message);
var notExist = true;
for (var command in Connection.prototype.commands) {
if (Connection.prototype.commands[command] == message.command) {
notExist = false;
break
}
}
if (notExist) {
logger.error("Connection.parseMessage(): Command not implemented", {
cmd: message.command
});
return null
}
switch (message.command) {
case Connection.prototype.commands.ADDRESSES:
message.addrs = message.data.addrs;
message.netStatus = message.data.netStatus;
break
}
return message
};
Connection.prototype.sendVersion = function() {
var data = {
version: 1,
timestamp: new Date().getTime()
};
this.sendMessage(Connection.prototype.commands.VERSION, data)
};
Connection.prototype.sendGetAddr = function() {
this.sendMessage(Connection.prototype.commands.GET_ADDRESSES)
};
Connection.prototype.sendLastTransaction = function(data) {
this.sendMessage(Connection.prototype.commands.LAST_TRANSACTION, data)
};
Connection.prototype.sendGetLastTransaction = function() {
this.sendMessage(Connection.prototype.commands.GET_LAST_TRANSACTION)
};
Connection.prototype.sendGetNextBlock = function(data) {
this.sendMessage(Connection.prototype.commands.GET_NEXT_BLOCK, data)
};
Connection.prototype.sendNewTransaction = function(data) {
this.sendMessage(Connection.prototype.commands.NEW_TRANSACTION, data)
};
Connection.prototype.sendBlock = function(data) {
this.sendMessage(Connection.prototype.commands.BLOCK, data)
};
Connection.prototype.sendUnconfirmedTransactions = function(data) {
this.sendMessage(Connection.prototype.commands.UNCONFIRMED_TRANSACTIONS, data)
};
Connection.prototype.sendMessage = function(command, data) {
try {
if (typeof data === "undefined") {
data = null
}
var message = {
command: command,
data: data
};
logger.netdbg("[" + this.peer + "] Sending message " + message.command);
message = JSON.stringify(message) + "___|||___";
this.peer.downloadBytes += Buffer.byteLength(message, "utf8");
this.socket.write(message)
} catch (err) {
logger.error("Error while sending message to peer " + this.peer + ": " + (err.stack ? err.stack : err.toString()))
}
};
Connection.prototype.broadcast = function(data) {
this.sendMessage(Connection.prototype.commands.broadcast, data)
};
function Peer(host, port, services) {
if ("string" === typeof host) {
if (host.indexOf(":") && !port) {
var parts = host.split(":");
host = parts[0];
port = parts[1]
}
this.host = host;
this.port = +port || 8333
} else if (host instanceof Peer) {
this.host = host.host;
this.port = host.port
} else if (Buffer.isBuffer(host)) {
this.host = host.toString("hex").match(/(.{1,4})/g).join(":");
this.port = +port || 8333
} else if (host instanceof Object) {
return this.fromData(host)
} else {
throw new Error("Could not instantiate peer, invalid parameter type: " + typeof host)
}
this.services = services ? services : null;
this.MAX_LOST_CONNECTION = 10;
this.lostConnection = 0;
this.connection = null;
this.uploadBytes = 0;
this.downloadBytes = 0;
this.timestamp = null;
this.lastSeen = null;
this.status = Peer.prototype.statuses.DISABLE;
this.oldStatus = null
}
Peer.prototype.statuses = {
ACTIVE: "active",
CHECK: "check",
PENDING: "pending",
SYNCED: "synced",
DISABLE: "disable"
};
Peer.prototype.getData = function() {
return {
port: this.port,
host: this.host,
uploadBytes: this.uploadBytes,
downloadBytes: this.downloadBytes,
services: this.services,
MAX_LOST_CONNECTION: this.MAX_LOST_CONNECTION,
lostConnection: this.lostConnection,
status: this.status
}
};
Peer.prototype.fromData = function(data) {
this.port = data.port || null;
this.host = data.host || null;
this.uploadBytes = data.uploadBytes || 0;
this.downloadBytes = data.downloadBytes || 0;
this.services = data.services || null;
this.MAX_LOST_CONNECTION = data.MAX_LOST_CONNECTION || 0;
this.lostConnection = data.lostConnection || 0;
this.status = data.status || Peer.prototype.statuses.DISABLE;
return this
};
Peer.prototype.isLost = function() {
if (this.lostConnection >= this.MAX_LOST_CONNECTION) {
this.status = Peer.prototype.statuses.DISABLE;
return true
}
return false
};
Peer.prototype.connectionLost = function() {
if (this.lostConnection === 0) {
this.updateLastSeen()
}
this.lostConnection++;
this.connection = null;
this.timestamp = null;
this.oldStatus = this.status;
this.status = Peer.prototype.statuses.PENDING
};
Peer.prototype.updateLastSeen = function() {
this.lastSeen = new Date().getTime()
};
Peer.prototype.updateUpTime = function() {
this.timestamp = new Date().getTime()
};
Peer.prototype.getConnection = function() {
if (this.connection === null) {
this.updateUpTime();
this.connection = net.createConnection(this.port, this.host);
this.status = this.oldStatus === null ? Peer.prototype.statuses.DISABLE : this.oldStatus
}
return this.connection
};
Peer.prototype.getHostAsBuffer = function() {
return new Buffer(this.host.split("."))
};
Peer.prototype.toString = function() {
return this.host + ":" + this.port
};
Peer.prototype.toBuffer = function() {
return ""
};
function PeerProcessor(node) {
events.EventEmitter.call(this);
var self = this;
var client, server;
this.node = node;
this.synced = false;
this.enabled = false;
this.timer = null;
this.peers = new Map();
this.forcePeers = new Map();
this.connections = new Map();
this.isConnected = false;
this.peerDiscovery = true;
this.interval = 5e3;
this.minConnections = 8;
this.MAX_ADDR_COUNT = 1e3
}
util.inherits(PeerProcessor, events.EventEmitter);
PeerProcessor.prototype.getActivePeersArr = function() {
var activePeers = [];
this.connections.map(function(connection, key) {
activePeers.push(connection.peer)
}.bind(this));
return activePeers
};
PeerProcessor.prototype.run = function() {
this.enabled = true;
var initialPeers = Config.starPeers;
var forcePeers = Config.starPeers;
initialPeers.forEach(function(peer) {
if ("string" !== typeof peer) {
throw new Error("PeerManager.enable(): Invalid Configuration for initial" + "peers.")
}
this.addPeer(peer)
}.bind(this));
forcePeers.forEach(function(peer) {
if ("string" !== typeof peer) {
throw new Error("PeerManager.enable(): Invalid Configuration for initial" + "peers.")
}
var _peer = new Peer(peer);
this.forcePeers.set(_peer.toString(), _peer)
}.bind(this));
this.server = net.createServer(function(socketConn) {
try {
var peer = new Peer(socketConn.remoteAddress, Config.peerPort);
this.addConnection(socketConn, peer)
} catch (e) {
logger.error("Add peer errror: " + JSON.stringify(e))
}
}.bind(this));
logger.netdbg("this.server.listen on port: " + Config.peerPort);
this.server.listen(Config.peerPort);
if (!this.timer) {
this.pingStatus()
}
};
PeerProcessor.prototype.addPeer = function(peer, port) {
if (peer instanceof Peer) {
logger.netdbg("Add peer: " + peer.toString());
var defStatus = Peer.prototype.statuses.DISABLE;
if (!this.peers.has(peer.toString())) {
PeersDb.addReplacePeer(peer);
this.peers.set(peer.toString(), peer)
} else if (peer.status != defStatus) {
var _peer;
if (this.connections.has(peer.toString()) && (_peer = this.connections.get(peer.toString()).peer).status == defStatus) {
_peer.status = peer.status
}
if ((_peer = this.peers.get(peer.toString())).status == defStatus) {
_peer.status = peer.status
}
}
} else if ("string" == typeof peer) {
this.addPeer(new Peer(peer, port))
} else {
logger.log("error", "Node.addPeer(): Invalid value provided for peer", {
val: peer
});
throw "Node.addPeer(): Invalid value provided for peer."
}
};
PeerProcessor.prototype.connectTo = function(peer) {
logger.netdbg("Connecting to peer " + peer);
try {
return this.addConnection(peer.getConnection(), peer)
} catch (e) {
logger.error("Error creating connection", e);
return null
}
};
PeerProcessor.prototype.addConnection = function(socketConn, peer) {
var conn = new Connection(socketConn, peer);
this.connections.set(conn.peer.toString(), conn);
this.node.addConnection(conn);
conn.addListener("connect", this.handleConnect.bind(this));
conn.addListener("error", this.handleError.bind(this));
conn.addListener("disconnect", this.handleDisconnect.bind(this));
conn.addListener(Connection.prototype.commands.VERSION, this.handleVersion.bind(this));
conn.addListener(Connection.prototype.commands.READY, this.handleReady.bind(this));
conn.addListener(Connection.prototype.commands.ADDRESSES, this.handleAddr.bind(this));
conn.addListener(Connection.prototype.commands.GET_ADDRESSES, this.handleGetAddr.bind(this));
conn.addListener(Connection.prototype.commands.GET_LAST_TRANSACTION, this.handleGetLastTransaction.bind(this));
conn.addListener(Connection.prototype.commands.LAST_TRANSACTION, this.handleLastTransaction.bind(this));
conn.addListener(Connection.prototype.commands.BLOCK, this.handleBlock.bind(this));
conn.addListener(Connection.prototype.commands.GET_NEXT_BLOCK, this.handleGetNextBlock.bind(this));
conn.addListener(Connection.prototype.commands.NEW_TRANSACTION, this.handleNewTransaction.bind(this));
conn.addListener(Connection.prototype.commands.BROADCAST, this.handleBroadcast.bind(this));
conn.addListener(Connection.prototype.commands.PEER_STATUS, this.handlePeerStatus.bind(this));
conn.addListener(Connection.prototype.commands.UNCONFIRMED_TRANSACTIONS, this.handleUnconfirmedTransactions.bind(this));
return conn
};
PeerProcessor.prototype.getActiveConnections = function() {
return this.connections
};
PeerProcessor.prototype.handleConnect = function(e) {
logger.netdbg("Handle Connect\n" + e.conn.toString());
this.addPeer(e.peer)
};
PeerProcessor.prototype.handleVersion = function(e) {
logger.netdbg("Handle Version\n" + e.conn.toString());
if (!e.conn.inbound) {
this.addPeer(e.peer)
}
if (this.peerDiscovery) {
e.conn.sendGetAddr();
e.conn.getaddr = true
}
};
PeerProcessor.prototype.handleReady = function(e) {
logger.netdbg("Handle Ready\n" + e.conn.toString());
this.addPeer(e.peer);
this.emit("connect", {
pm: this,
conn: e.conn,
socket: e.socket,
peer: e.peer
});
if (this.isConnected == false) {
this.emit("netConnected");
this.isConnected = true
}
};
PeerProcessor.prototype.handleAddr = function(e) {
if (!this.peerDiscovery) {
return
}
logger.netdbg("Handle Addr\n" + e.conn.toString());
if (typeof e.message.addrs !== "undefined") {
var peer, defStatus = Peer.prototype.statuses.DISABLE;
e.peer.status = e.message.netStatus || defStatus;
e.message.addrs.forEach(function(addr) {
try {
peer = new Peer(addr.ip, addr.port, addr.services);
peer.status = addr.status || defStatus;
this.addPeer(peer)
} catch (e) {
logger.warn("Invalid addr received: " + e.message)
}
}.bind(this));
if (e.message.addrs.length < 1e3) {
e.conn.getaddr = false
}
}
};
PeerProcessor.prototype.handleGetAddr = function(e) {
logger.netdbg("Handle GetAddr\n", e.conn.toString());
var addressesCount = this.peers.length;
if (addressesCount > this.MAX_ADDR_COUNT) {
addressesCount = this.MAX_ADDR_COUNT
}
var peers = this.peers.values();
var addrs = [],
connection = null,
status, defStatus = Peer.prototype.statuses.DISABLE;
for (var i = 0; i < addressesCount; i++) {
if (e.peer.host === peers[i].host) {
continue
}
connection = this.connections.get(peers[i].toString());
status = defStatus;
if (typeof connection !== "undefined") {
status = connection.peer.status
}
addrs.push({
services: peers[i].services,
ip: peers[i].host,
port: peers[i].port,
status: status
})
}
e.conn.sendMessage(Connection.prototype.commands.ADDRESSES, {
addrs: addrs,
netStatus: this.node.netStatus
})
};
PeerProcessor.prototype.handleError = function(e) {
logger.netdbg("Handle Error\n" + e.conn.toString());
this.handleDisconnect.apply(this, [].slice.call(arguments))
};
PeerProcessor.prototype.handleDisconnect = function(e) {
logger.netdbg("Handle Disconnect\n" + e.conn.toString());
logger.netdbg("Disconnected from peer " + e.peer);
var key = e.peer.toString();
if (this.connections.has(key)) {
this.connections.delete(key)
}
if (this.peers.has(key)) {
this.peers.get(key).connectionLost()
}
e.peer.connectionLost();
if (!this.connections.length) {
this.emit("netDisconnected");
this.isConnected = false
}
};
PeerProcessor.prototype.pingStatus = function pingStatus() {
if (!this.enabled) {
return
}
this.checkStatus();
this.clearLostPeers();
this.timer = setTimeout(this.pingStatus.bind(this), this.interval)
};
PeerProcessor.prototype.checkStatus = function checkStatus() {
if (this.forcePeers.length) {
this.forcePeers.map(function(peer, key) {
if (!this.connections.has(key)) {
this.connectTo(peer)
}
}.bind(this))
}
var connectablePeers = [];
this.peers.map(function(peer, key) {
if (!this.connections.has(key)) {
connectablePeers.push(peer)
}
}.bind(this));
while (this.connections.length < this.minConnections && connectablePeers.length) {
var peer = connectablePeers.splice(Math.random() * connectablePeers.length, 1);
this.connectTo(peer[0])
}
};
PeerProcessor.prototype.clearLostPeers = function() {
var lostPeers = this.peers.filter(function(peer, key) {
if (!this.connections.has(key)) {
peer.connectionLost()
}
if (peer.isLost()) {
logger.netdbg("Removed peer " + key);
return true
}
return false
}.bind(this));
if (lostPeers.length) {
this.peers.deleteEach(lostPeers.keys())
}
};
PeerProcessor.prototype.sendTransaction = function(trans) {
if (this.forcePeers.length) {
var connectionCount = 0;
var connection = null;
this.forcePeers.map(function(peer, key) {
if (this.connections.has(key)) {
connection = this.connections.get(key);
connection.sendNewTransaction();
connectionCount++
}
}.bind(this));
if (connectionCount === 0) {
logger.netdbg("Error: no connection to force peers.")
}
} else {
logger.netdbg("Error: forcePeers is empty.")
}
};
PeerProcessor.prototype.syncTransaction = function() {
if (this.forcePeers.length) {
var connectionCount = 0;
var connection = null;
this.forcePeers.some(function(peer, key) {
if (this.connections.has(key)) {
connection = this.connections.get(key);
connection.sendGetLastTransaction();
connectionCount++
}
}.bind(this));
if (connectionCount === 0) {
logger.netdbg("Error: no connection to force peers.")
}
} else {
logger.netdbg("Error: forcePeers is empty.")
}
};
PeerProcessor.prototype.handleLastTransaction = function(e) {
logger.netdbg("Handle Last Transaction \n", e.conn.toString());
if (!e.message.data) {
logger.error('Error: no data for "handleLastTransaction"');
return
}
var blockchain = new Blockchain();
var transaction = new Transaction(e.message.data);
var result = blockchain.getLastTransaction().compareTo(transaction);
var nextHeight = blockchain.getLastBlock().height + 1;
e.conn.sendGetNextBlock(nextHeight)
};
PeerProcessor.prototype.handleGetLastTransaction = function(e) {
logger.netdbg("Handle Get Last Transaction \n", e.conn.toString());
var blockchain = new Blockchain();
e.conn.sendLastTransaction(blockchain.getLastTransaction().getData())
};
var blockSyncBuffer = new Map();
var syncBlockRunning = false;
var unconfTxsSyncBuffer = new Map();
var syncUnconfTxsRunning = false;
PeerProcessor.prototype.handleBlock = function(e) {
var self = this;
logger.netdbg("Handle Block\n", e.conn.toString());
var blockData = e.message.data;
var tyransactionsArr = e.message.data.blockTransactions;
txArr = {
count: 0
};
tyransactionsArr.forEach(function(transactionData) {
var tx = new Transaction(transactionData);
txArr[tx.getId().toString()] = tx;
txArr.count += 1
});
blockData.blockTransactions = txArr;
var block = new Block(blockData);
var broadcasted = false;
if (typeof e.message.data.broadcasted !== "undefined" && e.message.data.broadcasted) {
broadcasted = true
}
blockSyncBuffer.set(blockData.id.toString(), {
block: block,
conn: e.conn,
broadcasted: broadcasted
});
this.processSyncBlock()
};
PeerProcessor.prototype.processSyncBlock = function() {
var self = this;
if (syncBlockRunning) {
return
}
syncBlockRunning = true;
var blockSyncBufferArr = blockSyncBuffer.toArray();
async.eachSeries(blockSyncBufferArr, function(data, callback) {
var conn = data.conn;
var block = data.block;
var broadcasted = data.broadcasted;
blockSyncBuffer.delete(block.id.toString());
BlockDb.hasBlock(block.id.toString(), function(exist) {
if (!exist) {
BlockchainProcessor.pushBlock(block, function() {
logger.warn("block pushed OK id:" + block.id.toString());
BlockDb.setNextBlockId(block.previousBlockId.toString(), block.getId().toString(), function() {
block.confirmations = block.confirmations + 1;
BlockchainProcessor.addBlock(block, false, function() {
logger.warn("Block saved OK id:" + block.id.toString());
if (!broadcasted) {
block.addUnconfirmedAmounts();
conn.sendGetLastTransaction()
}
callback()
})
})
})
} else {
logger.info("Block exist, continue on height: " + block.height);
BlockDb.addConfirmation(block.id.toString(), function() {
callback()
})
}
})
}, function(err, callback) {
if (err) {
logger.error(err)
}
syncBlockRunning = false
})
};
PeerProcessor.prototype.handleGetNextBlock = function(e) {
logger.netdbg("Handle Get Next Block \n", e.conn.toString());
var height = e.message.data || null;
var blockchain = new Blockchain();
if (parseInt(height) > blockchain.getLastBlock().height) {
UnconfirmedTransactions.getAllTransactionsList(function(unconfTxsData) {
e.conn.sendUnconfirmedTransactions(unconfTxsData)
})
} else if (height) {
BlockDb.findBlockIdAtHeight(height, function(block) {
if (block) {
logger.transdbg("Send block at heifght - " + e.height);
e.conn.sendBlock(block.getDataWithTransactions())
} else {
logger.warn("No block at heifght - " + e.height)
}
})
} else {
e.conn.sendBlock({})
}
};
var newTxsSyncBuffer = [];
var newTxsRunning = false;
PeerProcessor.prototype.handleNewTransaction = function(e) {
var transactionData = e.message.data || null;
logger.netdbg("Handle New Transaction \n", e.conn.toString());
if (!transactionData) {
return
}
newTxsSyncBuffer.push(transactionData);
this.processNewTransaction()
};
PeerProcessor.prototype.processNewTransaction = function() {
if (newTxsRunning) {
return
}
newTxsRunning = true;
async.eachSeries(newTxsSyncBuffer, function(transactionData, callback) {
var transaction = new Transaction(transactionData);
var transactionProcessor = new TransactionProcessor();
transactionProcessor.verifiyTransaction(transaction, function(err) {
if (err) {
logger.warn(err);
callback()
} else {
transactionProcessor.addTransactionOrConfirmation(transaction, false);
callback()
}
})
}, function(err) {
newTxsRunning = false;
newTxsSyncBuffer = []
})
};
PeerProcessor.prototype.handleUnconfirmedTransactions = function(e) {
var self = this;
logger.netdbg("Handle Unconfirmed Transactions\n", e.conn.toString());
var unconfTxsData = e.message.data || null;
if (syncUnconfTxsRunning) {
return
}
syncUnconfTxsRunning = true;
if (unconfTxsData.length) {
async.eachSeries(unconfTxsData, function(txData, callback) {
UnconfirmedTransactions.hasTransaction(txData.id, function(exist) {
if (!exist) {
var transaction = new Transaction(txData);
var transactionProcessor = new TransactionProcessor();
transactionProcessor.verifiyTransaction(transaction, unconfTxsData, function(err) {
if (err) {
logger.warn(err);
callback(err)
} else {
transactionProcessor.addTransactionOrConfirmation(transaction, true);
callback()
}
})
}
})
}, function(err) {
if (err) {
logger.error("ERROR PeerProcessor.prototype.handleUnconfirmedTransactions " + err);
syncUnconfTxsRunning = false
} else {
syncUnconfTxsRunning = false;
self.synced = true;
self.node.setState("synced");
self.node.broadcastPeerStatus(Peer.prototype.statuses.SYNCED)
}
})
} else {
this.synced = true;
self.node.setState("synced");
this.node.broadcastPeerStatus(Peer.prototype.statuses.SYNCED)
}
};
PeerProcessor.prototype.handleBroadcast = function(e) {
logger.netdbg("Handle Broadcast\n", e.conn.toString())
};
PeerProcessor.prototype.handlePeerStatus = function(e) {
logger.netdbg("Handle Peer Status\n", e.conn.toString());
e.peer.status = e.message.data.status || Peer.prototype.statuses.DISABLE
};
var AccountHandlers = function() {};
AccountHandlers.loginAccount = function loginAccount(response, params, request) {
if (!NodeServer.peerProcessor.synced) {
ResponseHelper.end500(response, "Not synced yet!!")
}
var user = new User(params.userId);
var accountId = user.loginAccount(params.walletKey);
var publicKey = user.getPublicKey().toString("hex");
logger.info("user.getPublicKey()", publicKey);
Account.insertNewAccount({
accountId: accountId,
publicKey: publicKey
}, request);
Account.addOrGetAccount(accountId.toString());
Account.setCurrentAccount({
accountId: accountId,
accountSecret: user.getSecretWord().toString("hex")
});
Account.getAccounts(accountId, function(accounts) {
TransactionDb.getLastTransactions(10, function(recentTransactions) {
TransactionDb.getMyAllTransactions(accountId, function(myTransactions) {
UnconfirmedTransactions.getMyAllTransactions(accountId, function(unconfTrans) {
myTransactions = unconfTrans.concat(myTransactions);
ResponseHelper.end200Text(response, JSON.stringify({
accountId: accountId,
accountSecret: user.getSecretWord().toString("hex"),
accountTypes: accounts,
recentTransactions: recentTransactions || [],
myTransactions: myTransactions
}))
})
})
})
})
};
AccountHandlers.getAccountTransactions = function getAccountTransactions(response, params, request) {
TransactionDb.getLastTransactions(10, function(recentTransactions) {
TransactionDb.getMyAllTransactions(params.accountId, function(myTransactions) {
UnconfirmedTransactions.getMyAllTransactions(params.accountId, function(unconfTrans) {
UnconfirmedTransactions.getAllTransactionsList(function(unconfTransAll) {
myTransactions = unconfTrans.concat(myTransactions);
recentTransactions = unconfTransAll.concat(recentTransactions);
ResponseHelper.end200Text(response, JSON.stringify({
recentTransactions: recentTransactions || [],
myTransactions: myTransactions
}))
})
})
})
})
};
AccountHandlers.getMyTransactions = function getMyTransactions(response, params, request) {
TransactionDb.getMyAllTransactions(params.accountId, function(myTransactions) {
ResponseHelper.end200Text(response, JSON.stringify({
myTransactions: myTransactions
}))
})
};
AccountHandlers.logoutAccount = function logoutAccount(response) {
Account.currentAccount = null;
NodeServer.broadcastPeerStatus(Peer.prototype.statuses.DISABLE);
NodeServer.startCheckSynced();
ResponseHelper.end200Text(response, JSON.stringify({
logout: true
}))
};
AccountHandlers.getAccountByHash = function getAccountByHash(response, params, request) {
if (typeof params.secret === "undefined") {
ResponseHelper.end403(response);
return
}
var account = Account.createFromSecretWord(params.secret);
Account.getAccounts(account.id, function(accountNums) {
account.accountTypes = accountNums;
TransactionDb.getLastTransactions(10, function(recentTransactions) {
account.recentTransactions = recentTransactions;
ResponseHelper.end200Text(response, JSON.stringify(account))
})
})
};
AccountHandlers.showAccounts = function showAccounts(response, params) {
if (params.key === Config.serverKey) {
Account.getRegisteredAccounts(function(err, docs) {
response.writeHead(200, {
"Content-Type": "text/html"
});
var tplStr = swig.renderFile(appDir + "/frontend/showAcc.html", {
accounts: docs
});
response.end(tplStr)
})
} else {
ResponseHelper.end403(response)
}
};
AccountHandlers.getAccounts = function getAccounts(response, params, request) {
ResponseHelper.end200Text(response, JSON.stringify(Account.accounts))
};
var BlockHandlers = function() {};
BlockHandlers.showAllBlocks = function(response, params, request) {
BlockDb.getLastBlocksList(25, function(res) {
var data = [];
async.eachSeries(res, function(blockData, _callback) {
var block = new Block(blockData);
BlockDb.findRelatedTransactions(block, function(block) {
data.push(block.getDataWithTransactions());
_callback()
})
}, function(err) {
if (err) {
ResponseHelper.end500(response, err)
} else {
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write(JSON.stringify(data));
response.end()
}
})
})
};
var BlockchainexplHandlers = function() {};
BlockchainexplHandlers.index = function(response, params, request) {
response.writeHead(200, {
"Content-Type": "text/html"
});
var tplStr = swig.renderFile(appDir + "/frontend/blockchainExpl.html", {});
response.end(tplStr)
};
var PeersHandlers = function() {};
PeersHandlers.getAllPeersText = function(response, params, request) {
var peersObj = NodeServer.peerProcessor;
var peers = peersObj.getActivePeersArr();
var activePeersStr = "";
for (var id in peers) {
if (peers.hasOwnProperty(id)) {
activePeersStr += JSON.stringify(peers[id].getData()) + "\n<br/>"
}
}
PeersDb.getAllPeersList(function(archivedPeers) {
ResponseHelper.end200Text(response, "Active peers:\n" + JSON.stringify(activePeersStr + "\n<br/><br/><br/>\n\nArchived Peers:\n" + JSON.stringify(archivedPeers)))
})
};
PeersHandlers.getAllPeersJSON = function(response, params, request) {
var peersObj = NodeServer.peerProcessor;
var peersData = [];
var peers = peersObj.getActivePeersArr();
for (var id in peers) {
if (peers.hasOwnProperty(id)) {
peersData.push(peers[id].getData())
}
}
ResponseHelper.end200Text(response, JSON.stringify(peersData))
};
RequestHandlers = function() {};
swig.setDefaults({
locals: {
host: Config.host,
port: Config.port
}
});
RequestHandlers.start = function(response) {
response.writeHead(200, {
"Content-Type": "text/html"
});
var tplStr = swig.renderFile(appDir + "/frontend/index.html", {});
response.end(tplStr)
};
RequestHandlers.broadcast = function(response, params) {
response.writeHead(200, {
"Content-Type": "text/plain"
});
NodeServer.broadcast({
message: params.message
});
response.write("sending");
response.end()
};
RequestHandlers.checkLoading = function(response, prams) {
ResponseHelper.end200Text(response, JSON.stringify({
loading: !NodeServer.peerProcessor.synced
}))
};
var TransactionHandlers = function() {};
TransactionHandlers.showAllTransactions = function(response, params, request) {
UnconfirmedTransactions.getAllTransactionsList(function(res) {
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write(JSON.stringify(res));
response.end()
})
};
TransactionHandlers.validateTransactionParams = function(response, params) {
var result = {
err: false,
result: null
};
logger.info(params);
var errs = {};
if (typeof params.recipientId == "undefined" || params.recipientId == "") {
errs.recipientId = true
}
var account = Account.createFromSecretWord(params.accountSecret);
if (account.id.toString() == params.recipientId) {
errs.recipientId = true
}
logger.info("parseFloat(params.amount)", parseFloat(params.amount));
var regEx = /^([0-9])*\.{0,1}([0-9])*$/g;
if (typeof params.amount == "undefined" || params.amount == 0 || parseFloat(params.amount) < 1e-5 || !params.amount.match(regEx)) {
errs.amount = true
}
if (typeof params.accountSecret == "undefined" || params.accountSecret == "") {
errs.accountSecret = true
}
if (!Utils.isEmptyObj(errs)) {
result.err = errs;
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write(JSON.stringify(result));
response.end();
return false
}
return result
};
TransactionHandlers.validateTransactionAmount = function(response, params, result, account, accounts) {
if (accounts == null || params.amount + params.fee > parseFloat(accounts.nxtlAccount.unconfirmedAmount)) {
logger.info("Not Enough money accId:", account.accountId);
result.err = {
amount: true
};
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write(JSON.stringify(result));
response.end();
return false
}
return true
};
TransactionHandlers.validateTransaction = function(response, params, request) {
var result = TransactionHandlers.validateTransactionParams(response, params);
if (result === false) {
return false
}
params.amount = Utils.roundTo5Float(params.amount);
params.fee = Utils.roundTo5Float(params.amount * .0035) > 1e-5 ? Utils.roundTo5Float(params.amount * .0035) : 1e-5;
var account = Account.createFromSecretWord(params.accountSecret);
Account.getAccounts(account.id, function(accounts) {
if (TransactionHandlers.validateTransactionAmount(response, params, result, account, accounts) === false) {
return false
} else {
result.result = true;
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write(JSON.stringify(result));
response.end();
return true
}
});
return true
};
TransactionHandlers.addTransaction = function(response, params, request) {
var result = TransactionHandlers.validateTransactionParams(response, params);
if (result === false) {
return false
}
var blockchain = new Blockchain();
params.amount = Utils.roundTo5Float(params.amount);
params.fee = Utils.roundTo5Float(params.amount * .0035) > 1e-5 ? Utils.roundTo5Float(params.amount * .0035) : 1e-5;
var referencedTransactionId = blockchain.getLastTransaction();
var account = Account.createFromSecretWord(params.accountSecret);
Account.getAccounts(account.id, function(accounts) {
if (TransactionHandlers.validateTransactionAmount(response, params, result, account, accounts) === false) {
return false
}
var transaction = new Transaction({
type: TransactionType.Payment,
timestamp: new Date().getTime(),
deadline: params.deadline,
senderPublicKey: account.publicKey,
recipientId: params.recipientId,
amount: params.amount,
fee: params.fee,
referencedTransactionId: referencedTransactionId.id,
signature: null
});
transaction.sign(params.accountSecret);
var transactionProcessor = new TransactionProcessor();
transactionProcessor.addTransactionOrConfirmation(transaction);
result.result = {
transactionId: transaction.getId().toString()
};
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write(JSON.stringify(result));
response.end();
return true
});
return true
};
TransactionHandlers.getLastTransaction = function(response, params, request) {
TransactionDb.getLastTransaction(function(res) {
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write(JSON.stringify(res));
response.end()
})
};
TransactionHandlers.getTransactionByUser = function(response, params, request) {
TransactionDb.getMyAllTransactions(params.userId, function(res) {
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write(JSON.stringify(res));
response.end()
})
};
var handle = {};
handle["/"] = RequestHandlers.start;
handle["/app/checkLoading"] = RequestHandlers.checkLoading;
handle["/account/login"] = AccountHandlers.loginAccount;
handle["/account/getAccountTransactions"] = AccountHandlers.getAccountTransactions;
handle["/account/getMyTransactions"] = AccountHandlers.getMyTransactions;
handle["/account/getAccounts"] = AccountHandlers.getAccounts;
handle["/account/getAccountByHash"] = AccountHandlers.getAccountByHash;
handle["/account/logoutAccount"] = AccountHandlers.logoutAccount;
handle["/showAccounts"] = AccountHandlers.showAccounts;
handle["/block/showAllBlocks"] = BlockHandlers.showAllBlocks;
handle["/transaction/showAllTransactions"] = TransactionHandlers.showAllTransactions;
handle["/transaction/addTransaction"] = TransactionHandlers.addTransaction;
handle["/transaction/validateTransaction"] = TransactionHandlers.validateTransaction;
handle["/transaction/getLastTransaction"] = TransactionHandlers.getLastTransaction;
handle["/transaction/getTransactionByUser"] = TransactionHandlers.getTransactionByUser;
handle["/blockchainexpl/"] = BlockchainexplHandlers.index;
handle["/getAllPeersText"] = PeersHandlers.getAllPeersText;
handle["/getAllPeersJSON"] = PeersHandlers.getAllPeersJSON;
handle["/api/broadcast"] = RequestHandlers.broadcast;
var PostRequestProcess = function() {};
PostRequestProcess.processPost = function processPost(request, response, callback) {
var queryData = "";
if (typeof callback !== "function") return null;
if (request.method == "POST") {
request.on("data", function(data) {
queryData += data;
if (queryData.length > 1e6) {
queryData = "";
response.writeHead(413, {
"Content-Type": "text/plain"
}).end();
request.connection.destroy()
}
});
request.on("end", function() {
response.post = querystring.parse(queryData);
callback()
})
} else {
response.writeHead(405, {
"Content-Type": "text/plain"
});
response.end()
}
};
var ResponseHelper = function() {};
ResponseHelper.end404 = function(response) {
response.writeHead(404, {
"Content-Type": "text/plain"
});
response.write("404 Not found");
response.end()
};
ResponseHelper.end403 = function(response) {
response.writeHead(403, {
"Content-Type": "text/plain"
});
response.write("You can't access this area.");
response.end()
};
ResponseHelper.end500 = function(response, err) {
response.writeHead(500, {
"Content-Type": "text/plain"
});
response.write(err + "\n");
response.end()
};
ResponseHelper.end200Text = function(response, data) {
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write(data);
response.end()
};
var Router = function() {};
Router.route = function route(handle, request, response) {
var urlParts = url.parse(request.url, true);
var pathname = urlParts.pathname;
if (typeof handle[pathname] === "function") {
if (request.method === "POST") {
PostRequestProcess.processPost(request, response, function() {
handle[pathname](response, response.post, request)
})
} else {
handle[pathname](response, urlParts.query, request)
}
} else {
var staticFilesPath = appDir + "/frontend/";
var filename = path.join(staticFilesPath, pathname);
logger.info("filename", filename);
fs.exists(filename, function(exists) {
if (!exists) {
logger.info("No request handler found for " + pathname);
ResponseHelper.end404(response);
return
}
if (fs.statSync(filename).isDirectory()) {
ResponseHelper.end403(response);
return
}
fs.readFile(filename, "binary", function(err, file) {
if (err) {
ResponseHelper.end500(response, err);
return
}
var type = mime.lookup(filename);
response.writeHead(200, {
"Content-Type": type
});
response.write(file, "binary");
response.end()
})
})
}
};
var User = function(_userId) {
var userId;
var secretWord;
var publicKey;
var isInactive = false;
userId = _userId;
this.getId = function() {
return userId
};
this.getPublicKey = function() {
return publicKey
};
this.getSecretWord = function() {
return secretWord
};
this.getIsInactive = function() {
return isInactive
};
this.setIsInactive = function(_isInactive) {
if (typeof _isInactive === "boolean") isInactive = _isInactive
};
this.loginAccount = function(_secretWord) {
secretWord = curve.sha256(_secretWord);
var nxtlPublicKeyHex = nxtCrypto.getPublicKey(secretWord.toString("hex"));
console.log("nxtCrypto", nxtlPublicKeyHex);
publicKey = new Buffer(nxtlPublicKeyHex, "hex");
return Account.getId(publicKey)
};
this.logoutAccount = function() {
secretWord = null
}
};
var Account = function(id) {
this.id = id;
this.height = 0;
this.publicKey = null;
this.keyHeight = null;
this.balance = 0;
this.unconfirmedBalance = 0;
this.guaranteedBalances = {};
this.assetBalances = {};
this.unconfirmedAssetBalances = {};
this.accountTypes = {}
};
Account.accounts = {};
Account.currentAccount = null;
Account.setCurrentAccount = function(account) {
if (Account.currentAccount !== null) {
logger.warn("Current account already set");
return false
}
Account.currentAccount = account
};
Account.createFromSecretWord = function(secret) {
if (secret instanceof Buffer) {
secret = secret.toString("hex")
}
var publicKey = nxtCrypto.getPublicKey(secret);
var accountId = Account.getId(new Buffer(publicKey, "hex"));
var account = Account.addOrGetAccount(accountId.toString());
account.publicKey = new Buffer(publicKey, "hex");
return account
};
Account.getId = function(publicKey) {
var publicKeyHash = curve.sha256(publicKey);
var longVal = Utils.bufferToLongLE(publicKeyHash);
return longVal.toString()
};
Account.getAccounts = function(accountId, _callback) {
var nxtlAccount = {};
var dollarAccount = {};
var euroAccount = {};
var btcAccount = {};
var accountNums;
async.waterfall([
function(callback) {
NxtlAccount.init(accountId, function(res) {
nxtlAccount = res;
callback(null)
})
},
function(callback) {
DollarAccount.init(accountId, function(res) {
dollarAccount = res;
callback(null)
})
},
function(callback) {
EuroAccount.init(accountId, function(res) {
euroAccount = res;
callback(null)
})
},
function(callback) {
BtcAccount.init(accountId, function(res) {
btcAccount = res;
callback(null)
})
}
], function(err, result) {
if (err) {
console.log("Account.getAccounts Error", err)
}
accountNums = {
nxtlAccount: nxtlAccount.getSettings(),
dollarAccount: dollarAccount.getSettings(),
euroAccount: euroAccount.getSettings(),
btcAccount: btcAccount.getSettings()
};
if (typeof _callback === "function") {
_callback(accountNums)
}
})
};
Account.getAccount = function(_id) {
return Account.accounts[_id]
};
Account.addOrGetAccount = function(id) {
var account = new Account(id);
if (typeof Account.accounts[id] === "undefined") {
Account.accounts[id] = account
}
return Account.accounts[id]
};
Account.insertNewAccount = function(params, request, callback) {
if (typeof params === "undefined") {
console.log("empty params for new account insert.");
return false
}
var doc = {
type: "account",
accountId: params.accountId,
publicKeyStr: params.publicKey,
ip: request.connection.remoteAddress,
time: Utils.getDateTime()
};
logger.info("user doc", doc);
DB.db.insert(doc, function(err, newDocs) {
if (!err) {
if (typeof callback === "function") {
callback(newDocs)
}
} else {
console.log("insert_callback error", err)
}
});
return true
};
Account.getRegisteredAccounts = function(callback) {
if (typeof callback !== "function") {
callback = function(err, docs) {}
}
DB.db.find({
type: "account"
}).sort({
time: 1
}).exec(callback)
};
Account.prototype.setOrVerify = function(key, height) {
if (this.publicKey == null) {
this.publicKey = key;
this.keyHeight = -1;
return true
} else if (this.publicKey.toString("hex") == key.toString("hex")) {
return true
} else if (this.keyHeight == -1) {
console.log("DUPLICATE KEY!!!");
console.log("Account key for " + this.id + " was already set to a different one at the same height " + ", current height is " + height + ", rejecting new key");
return false
} else if (this.keyHeight >= height) {
console.log("DUPLICATE KEY!!!");
console.log("Changing key for account " + this.id + " at height " + height + ", was previously set to a different one at height " + this.keyHeight);
this.publicKey = key;
this.keyHeight = height;
return true
}
console.log("DUPLICATE KEY!!!");
console.log("Invalid key for account " + this.id + " at height " + height + ", was already set to a different one at height " + this.keyHeight);
return false
};
Account.prototype.apply = function(key, height) {};
Account.prototype.addToBalanceAndUnconfirmedBalance = function(amount) {
this.balance += amount;
this.unconfirmedBalance += amount
};
Account.prototype.addToBalance = function(amount) {
this.balance += amount
};
Account.prototype.addToUnconfirmedBalance = function(amount) {
this.unconfirmedBalance += amount
};
Account.isLogginedForForge = function() {
return !!Account.currentAccount
};
var AccountClass = function() {
var settings = {
name: "",
id: "",
postfix: "",
amount: 0,
unconfirmedAmount: 0
};
this.setId = function(_id) {
settings.id = _id
};
this.setName = function(_name) {
settings.name = _name
};
this.setAmount = function(_ammount) {
settings.amount = _ammount
};
this.setUnconfirmedAmount = function(_ammount) {
settings.unconfirmedAmount = _ammount
};
this.setPostfix = function(_postfix) {
settings.postfix = _postfix
};
this.getPostfix = function() {
return settings.postfix
};
this.getSettings = function() {
return settings
}
};
var BtcAccount = function() {
AccountClass.apply(this, Array.prototype.slice.call(arguments))
};
BtcAccount.prototype = new AccountClass();
BtcAccount.init = function(_id, _callback) {
var self = new BtcAccount();
self.setName("btc");
self.setPostfix("BTC");
self.setId("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W");
_callback(self)
};
var DollarAccount = function() {
AccountClass.apply(this, Array.prototype.slice.call(arguments))
};
DollarAccount.prototype = new AccountClass();
DollarAccount.init = function(_id, _callback) {
var self = new DollarAccount();
self.setName("dollar");
self.setPostfix("USD");
self.setId(_id + self.getPostfix());
_callback(self)
};
var EuroAccount = function() {
AccountClass.apply(this, Array.prototype.slice.call(arguments))
};
EuroAccount.prototype = new AccountClass();
EuroAccount.init = function(_id, _callback) {
var self = new EuroAccount();
self.setName("euro");
self.setPostfix("EUR");
self.setId(_id + self.getPostfix());
_callback(self)
};
var NxtlAccount = function() {
AccountClass.apply(this, Array.prototype.slice.call(arguments))
};
NxtlAccount.prototype = new AccountClass();
NxtlAccount.init = function(_id, _callback) {
var self = new NxtlAccount(_id);
async.waterfall([
function(callback) {
self.setName("nxtl");
self.setPostfix("NODE");
self.setId(_id + self.getPostfix());
callback(null)
},
function(callback) {
var account = Account.getAccount(_id.toString());
self.setAmount(Utils.roundTo5Float(account.balance));
self.setUnconfirmedAmount(Utils.roundTo5Float(account.unconfirmedBalance));
callback(null, "ok")
}
], function(err, result) {
if (typeof _callback === "function") {
_callback(self)
}
})
};
NodeServer = new NodeServer();
NodeServer.start()
}