UNPKG

bitcoin-p2p-messages

Version:
295 lines (252 loc) 8.44 kB
"use strict"; import crypto from 'crypto' import BN from 'bn.js' export const VERSION = require('../package.json').version; export const Network = { mainnet : 0xD9B4BEF9 , testnet : 0xDAB5BFFA , testnet3 : 0x0709110B }; export const BLOCK_HEADER_LENGTH = 81; export const PROTOCOL_HEADER_LENGTH = 24; // Bytes export const PROTOCOL_INV_LENGTH = 36; export const PROTOCOL_VERSION = 70001; export const PROTOCOL_NODE_NETWORK = new BN('0100000000000000',16,'le'); // TODO: new BN(1) export const PROTOCOL_USER_AGENT = '/BitPocket:'+VERSION+'/'; export const PROTOCOL_ADDR_LENGTH = 30; // including timestamp export const PROTOCOL_REJECT_CCODE_MALFORMED = 0x01; export const PROTOCOL_REJECT_CCODE_INVALID = 0x10; export const PROTOCOL_REJECT_CCODE_OBSOLETE = 0x11; export const PROTOCOL_REJECT_CCODE_DUPLICATE = 0x12; export const PROTOCOL_REJECT_CCODE_NONSTANDARD = 0x40; export const PROTOCOL_REJECT_CCODE_DUST = 0x41; export const PROTOCOL_REJECT_CCODE_INSUFFICIENTEFEE = 0x42; export const PROTOCOL_REJECT_CCODE_CHECKPOINT = 0x43; export function sha256(input) { let hash = crypto.createHash('sha256'); hash.update(input); return hash.digest(); } export function sha256x2(input) { return sha256(sha256(input)); } export function generateRandomBuffer(size=8) { return crypto.randomBytes(size); } export function generateRandomBN(size=8) { return new BN(generateRandomBuffer(size),'16','le'); } export function isNonce(buffer) { return Buffer.isBuffer(buffer) && buffer.length === 8; } export function getNetwork(magic) { let network = ''; for (let key in Network) { if (Network[key] === magic) { network = key; break; } } if (network.length > 0) { return network; } throw new Error('Incorrect magic number / network'); } // TODO: reomve instead of varintLength export function varintSize(buffer,start=0) { switch (buffer.readUInt8(start)) { case 0xFD: // UInt8 UInt16LE return 3; case 0xFE: // UInt8 UInt32LE return 5; case 0xFF:// UInt8 UInt64LE return 9; default: return 1; } } export function varintLength(buffer,start=0) { switch (buffer.readUInt8(start)) { case 0xFD: // UInt8 UInt16LE return 3; case 0xFE: // UInt8 UInt32LE return 5; case 0xFF:// UInt8 UInt64LE return 9; default: return 1; } } export function readVarint(buffer,start=0) { var precede = buffer.readUInt8(start); switch (precede) { case 0xFD: return buffer.readUInt16LE(start+1); case 0xFE: return buffer.readUInt32LE(start+1); case 0xFF: let second = buffer.readUInt32LE(start+1); let first = buffer.readUInt32LE(start+5); let combined = (first * 0x100000000) + second; if (combined <= 0x1fffffffffffff) { return combined; } else { throw new Error('TODO: BigInteger needs to be parsed'); } break; default: return precede; } } export function writeVarint(n) { let buf = undefined; if (n < 253) { buf = new Buffer(1); buf.writeUInt8(n, 0); } else if (n < 0x10000) { buf = new Buffer(1 + 2); buf.writeUInt8(253, 0); buf.writeUInt16LE(n, 1); } else if (n < 0x100000000) { buf = new Buffer(1 + 4); buf.writeUInt8(254, 0); buf.writeUInt32LE(n, 1); } else { buf = new Buffer(1 + 8); buf.writeUInt8(255, 0); buf.writeInt32LE(n & -1, 1); buf.writeUInt32LE(Math.floor(n / 0x100000000), 5); } return buf; } export function varstringLength(input) { let stringStart = varintLength(input); let stringLength = readVarint(input); return stringStart + stringLength; } export function readVarstring(input,encoding='ascii') { let stringStart = varintLength(input); let stringLength = readVarint(input); return input.slice(stringStart,stringLength+stringStart).toString(encoding); } export function writeVarstring(input,encoding='ascii') { let varIntBuffer = writeVarint(input.length); let varIntLength = varintLength(varIntBuffer); return Buffer.concat([varIntBuffer,new Buffer(input,encoding)],varIntLength+input.length); } export function readUInt64LE(input) { if (Buffer.isBuffer(input)) { input = input.slice(0,8).toString('hex'); } if (typeof input === 'string' && input.length === 16) { return new BN(input,16,'le'); } throw new Error('Incorrect input for reading 64Bit Integer'); } export function read64BitTimestamp(input) { return new Date(readUInt64LE(input).toNumber() * 1000); } export function readIP(input) { let ipv6 = []; let ipv4 = []; for (let a = 0; a < 16; a+=2) { var twoBytes = input.slice(a,a+2); ipv6.push(twoBytes.toString('hex')); if (a >= 12) { ipv4.push(twoBytes[0]); ipv4.push(twoBytes[1]); } } ipv6 = ipv6.join(':'); ipv4 = ipv4.join('.'); return { v6: ipv6, v4: ipv4 }; } export function readNetworkAddress(input) { let start = 0, address = {}; if (input.length === PROTOCOL_ADDR_LENGTH) { // timestamp included address.timestamp = new Date(input.readUInt32LE(0) * 1000); start = 4; } address.services = readUInt64LE(input.slice(start,start+8)); address.ip = readIP(input.slice(start+8,start+24)); address.port = input.readUInt16BE(start+24); return address; } export function writeIP(ip) { if (typeof ip.v6 === 'string' && ip.v6.length === 39) { // TODO: maybe support all possible ipv6 variants return new Buffer(ip.v6.replace(/:/g,''),'hex'); } else if (typeof ip.v4 === 'string' && (ip.v4.split('.').length === 4)) { let buffer = new Buffer(16); // see dual-stack: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses buffer[10] = 255; buffer[11] = 255; ip.v4.split('.').forEach(function(byte,index) { buffer[index+12] = parseInt(byte); }); return buffer; } } export function writeNetworkAddress(address) { let totalLength = PROTOCOL_ADDR_LENGTH - 4; let buffers = []; if (address.timestamp && (address.timestamp instanceof Date)) { buffers.push(new Buffer(4)); buffers[buffers.length-1].writeUInt32LE(Math.round(address.timestamp.getTime() / 1000)); totalLength = PROTOCOL_ADDR_LENGTH; } buffers.push(address.services.toArrayLike(Buffer,'le',8)); // BN dependency buffers.push(writeIP(address.ip)); let buffer = Buffer.concat(buffers,totalLength); buffer.writeUInt16BE(address.port,totalLength-2); return buffer; } export function serializeInv(inv) { if (Buffer.isBuffer(inv.hash) && (typeof inv.type === 'number')) { let typeBuffer = new Buffer(4); typeBuffer.writeUInt32LE(inv.type,0); return Buffer.concat([typeBuffer,inv.hash],PROTOCOL_INV_LENGTH); } throw new Error('Incorrect inv inputs'); } export function checkNetworkAddressInput(input,defaultValue) { input = input || {}; input.services = checkBigNumberInput(input.services,new BN(1)); input.ip = checkIpInput(input.ip); input.port = input.port || 8333; return input; } export function checkIpInput(input,defaultValue) { defaultValue = defaultValue || { v4 : '127.0.0.1' , v6 : '0000:0000:0000:0000:0000:ffff:7f00:0001' }; if (input && (input.v4 || input.v6)) { return input; } return defaultValue; } export function checkBigNumberInput(input,defaultValue) { if (input instanceof BN) { return input; } else if (typeof input === 'number') { return new BN(input); } return defaultValue || new BN(0); } export function checkBufferInput(input,defaultValue) { if (typeof input === 'string') { return new Buffer(input,'hex'); } else if (Buffer.isBuffer(input)) { return input; } return Buffer.isBuffer(defaultValue) ? defaultValue : new Buffer(0); } export function checkArrayInput(input,defaultValue) { if (!Array.isArray(input)) { return defaultValue || []; } return input; }