know-js
Version:
JavaScript library for sending Know transactions from the client or server
343 lines (292 loc) • 9.68 kB
JavaScript
var crypto = require("crypto");
var crypto_utils = require("../../crypto.js");
var ECPair = require("../../ecpair.js");
var ECSignature = require("../../ecsignature.js");
var networks = require("../../networks.js")
var bs58check = require('bs58check')
if (typeof Buffer === "undefined") {
Buffer = require("buffer/").Buffer;
}
var ByteBuffer = require("bytebuffer");
var fixedPoint = Math.pow(10, 8);
// default is know mainnet
var networkVersion = 0x2d;
function getBytes(transaction) {
var bb = new ByteBuffer(512, true);
bb.writeByte(0xff); // fill, to disambiguate from v1
bb.writeByte(transaction.version); // version 2
bb.writeByte(transaction.network); // know = 0x17, devnet = 0x30
bb.writeByte(transaction.type);
bb.writeInt(transaction.timestamp);
bb.append(transaction.senderPublicKey, "hex");
bb.writeLong(transaction.fee);
if(transaction.vendorFieldHex){
bb.writeByte(transaction.vendorFieldHex.length/2);
bb.append(transaction.vendorFieldHex, "hex");
}
else {
bb.writeByte(0x00);
}
switch (transaction.type) {
case 0: // Transfer
bb.writeLong(transaction.amount);
bb.writeInt(transaction.expiration);
bb.append(bs58check.decode(transaction.recipientId));
break;
case 1: // Signature
bb.append(transaction.asset.signature.publicKey,"hex");
break;
case 2: // Delegate
var delegateBytes = new Buffer(transaction.asset.delegate.username, "utf8");
bb.writeByte(delegateBytes.length/2);
bb.append(delegateBytes,"hex");
break;
case 3: // Vote
var voteBytes = transaction.asset.votes.map(function(vote){
return (vote[0] == "+" ? "01" : "00") + vote.slice(1);
}).join("");
bb.writeByte(transaction.asset.votes.length);
bb.append(voteBytes,"hex");
break;
case 4: // Multi-Signature
var keysgroupBuffer = new Buffer(transaction.asset.multisignature.keysgroup.join(""), "hex");
bb.writeByte(transaction.asset.multisignature.min);
bb.writeByte(transaction.asset.multisignature.keysgroup.length);
bb.writeByte(transaction.asset.multisignature.lifetime);
bb.append(keysgroupBuffer,"hex");
break;
case 5: // IPFS
bb.writeByte(transaction.asset.ipfs.dag.length/2);
bb.append(transaction.asset.ipfs.dag,"hex");
break;
case 6: // timelock transfer
bb.writeLong(transaction.amount);
bb.writeByte(transaction.timelocktype);
bb.writeInt(transaction.timelock);
bb.append(bs58check.decode(transaction.recipientId));
break;
case 7: // multipayment
bb.writeInt(transaction.asset.payments.length);
transaction.asset.payments.forEach(function(p) {
bb.writeLong(p.amount);
bb.append(bs58check.decode(p.recipientId));
});
break;
case 8: // delegate resignation - empty payload
break;
}
bb.flip();
return bb.toBuffer();
}
function fromBytes(hexString){
var tx={};
var buf = new Buffer(hexString, "hex");
tx.version = buf.readInt8(1) & 0xff;
tx.network = buf.readInt8(2) & 0xff;
tx.type = buf.readInt8(3) & 0xff;
tx.timestamp = buf.readUInt32LE(4);
tx.senderPublicKey = hexString.substring(16,16+33*2);
tx.fee = buf.readUInt32LE(41);
var vflength = buf.readInt8(41+8) & 0xff;
if(vflength > 0){
tx.vendorFieldHex=hexString.substring((41+8+1)*2,(41+8+1)*2+vflength*2);
}
var assetOffset = (41+8+1)*2+vflength*2;
if(tx.type == 0){ // transfer
tx.amount = buf.readUInt32LE(assetOffset/2);
tx.expiration = buf.readUInt32LE(assetOffset/2+8);
tx.recipientId = bs58check.encode(buf.slice(assetOffset/2+12,assetOffset/2+12+21));
parseSignatures(hexString, tx, assetOffset+(21+12)*2);
}
else if(tx.type == 1){ // second signature registration
tx.asset = {
signature : {
publicKey : hexString.substring(assetOffset,assetOffset+66)
}
}
parseSignatures(hexString, tx, assetOffset+66);
}
else if(tx.type == 2){ // delegate registration
var usernamelength = buf.readInt8(assetOffset/2) & 0xff;
tx.asset = {
delegate: {
username: buf.slice(assetOffset/2+1, assetOffset/2+1 + usernamelength).toString("utf8")
}
};
parseSignatures(hexString, tx, assetOffset + (usernamelength+1)*2);
}
else if(tx.type == 3){ // vote
var votelength = buf.readInt8(assetOffset/2) & 0xff;
tx.asset = {votes:[]};
var vote;
for(var i = 0; i<votelength; i++){
vote = hexString.substring(assetOffset + 2 + i*2*34, assetOffset + 2 + (i+1)*2*34);
vote = (vote[1] == "1" ? "+" : "-") + vote.slice(2);
tx.asset.votes.push(vote);
}
parseSignatures(hexString, tx, assetOffset + 2 + votelength*34*2);
}
else if(tx.type == 4){ // multisignature creation
tx.asset = {
multisignature: {}
}
tx.asset.multisignature.min = buffer.readInt8(assetOffset/2) & 0xff;
var num = buffer.readInt8(assetOffset/2+1) & 0xff;
tx.asset.multisignature.lifetime = buffer.readInt8(assetOffset/2+2) & 0xff;
tx.asset.multisignature.keysgroup = [];
for(var index = 0; index < num; index++){
var key = hexString.slice(assetOffset + 6 + index*66, assetOffset + 6 + (index+1)*66);
}
parseSignatures(hexString, tx, assetOffset + 6 + num*66);
}
else if(tx.type == 5){ // ipfs
tx.asset = {};
var l = buf.readInt8(assetOffset/2) & 0xff;
tx.asset.dag = hexString.substring(assetOffset+2,assetOffset+2+l*2);
parseSignatures(hexString, tx, assetOffset+2+l*2);
}
else if(tx.type == 6){ // timelock
tx.amount = buf.readUInt32LE(assetOffset/2);
tx.timelocktype = buf.readInt8(assetOffset/2+8) & 0xff;
tx.timelock = buf.readUInt32LE(assetOffset/2+9);
tx.recipientId = bs58check.encode(buf.slice(assetOffset/2+13,assetOffset/2+13+21));
parseSignatures(hexString, tx, assetOffset+(21+13)*2);
}
else if(tx.type == 7){ // multipayment
tx.asset = {
payments: []
}
var total = buffer.readInt8(assetOffset/2) & 0xff;
var offset = assetOffset/2 + 1;
for(var i = 0; i<total; i++){
var payment = {};
payment.amount = buf.readUInt32LE(offset);
payment.recipientId = bs58check.encode(buf.slice(offset+1,offset+1+21));
tx.asset.payments.push(payment);
offset += 22;
}
parseSignatures(hexString, tx, offset*2);
}
else if(tx.type == 8){ // delegate resignation
parseSignatures(hexString, tx, assetOffset);
}
return tx;
}
function parseSignatures(hexString, tx, startOffset){
tx.signature = hexString.substring(startOffset);
if(tx.signature.length == 0) delete tx.signature;
else {
var length = parseInt("0x" + tx.signature.substring(2,4), 16) + 2;
tx.signature = hexString.substring(startOffset, startOffset + length*2);
tx.secondSignature = hexString.substring(startOffset + length*2);
if(tx.secondSignature.length == 0) delete tx.secondSignature;
}
}
function getId(transaction) {
return getHash(transaction).toString("hex");
}
function getHash(transaction) {
return crypto.createHash("sha256").update(getBytes(transaction)).digest();
}
function getFee(transaction) {
switch (transaction.type) {
case 0: // Normal
return 0.1 * fixedPoint;
break;
case 1: // Signature
return 100 * fixedPoint;
break;
case 2: // Delegate
return 10000 * fixedPoint;
break;
case 3: // Vote
return 1 * fixedPoint;
break;
}
}
function sign(transaction, keys) {
var hash = getHash(transaction);
var signature = keys.sign(hash).toDER().toString("hex");
if (!transaction.signature) {
transaction.signature = signature;
}
return signature;
}
function secondSign(transaction, keys) {
var hash = getHash(transaction);
var signature = keys.sign(hash).toDER().toString("hex");
if (!transaction.secondSignature) {
transaction.secondSignature = signature;
}
return signature;
}
function verify(transaction, network) {
network = network || networks.know;
var hash = getHash(transaction);
var signatureBuffer = new Buffer(transaction.signature, "hex");
var senderPublicKeyBuffer = new Buffer(transaction.senderPublicKey, "hex");
var ecpair = ECPair.fromPublicKeyBuffer(senderPublicKeyBuffer, network);
var ecsignature = ECSignature.fromDER(signatureBuffer);
var res = ecpair.verify(hash, ecsignature);
return res;
}
function verifySecondSignature(transaction, publicKey, network) {
network = network || networks.know;
var hash = getHash(transaction);
var secondSignatureBuffer = new Buffer(transaction.secondSignature, "hex");
var publicKeyBuffer = new Buffer(publicKey, "hex");
var ecpair = ECPair.fromPublicKeyBuffer(publicKeyBuffer, network);
var ecsignature = ECSignature.fromDER(secondSignatureBuffer);
var res = ecpair.verify(hash, ecsignature);
return res;
}
function getKeys(secret, network) {
var ecpair = ECPair.fromSeed(secret, network || networks.know);
ecpair.publicKey = ecpair.getPublicKeyBuffer().toString("hex");
ecpair.privateKey = '';
return ecpair;
}
function getAddress(publicKey, version){
if(!version){
version = networkVersion;
}
var buffer = crypto_utils.ripemd160(new Buffer(publicKey,'hex'));
var payload = new Buffer(21);
payload.writeUInt8(version, 0);
buffer.copy(payload, 1);
return bs58check.encode(payload);
}
function setNetworkVersion(version){
networkVersion = version;
}
function getNetworkVersion(){
return networkVersion;
}
function validateAddress(address, version){
if(!version){
version = networkVersion;
}
try {
var decode = bs58check.decode(address);
return decode[0] == version;
} catch(e){
return false;
}
}
module.exports = {
getBytes: getBytes,
fromBytes: fromBytes,
getHash: getHash,
getId: getId,
getFee: getFee,
sign: sign,
secondSign: secondSign,
getKeys: getKeys,
getAddress: getAddress,
validateAddress: validateAddress,
verify: verify,
verifySecondSignature: verifySecondSignature,
fixedPoint: fixedPoint,
setNetworkVersion: setNetworkVersion,
getNetworkVersion: getNetworkVersion
}