UNPKG

tonweb

Version:

TonWeb - JavaScript API for TON blockchain

351 lines (321 loc) 8.58 kB
const BN = require("bn.js"); const nacl = require("tweetnacl"); const ethunit = require("ethjs-unit"); const isCryptoAvailable = typeof self !== 'undefined' && self.crypto && self.crypto.subtle; let myCrypto = null; if (isCryptoAvailable) { // web // nothing to do } else { // nodejs or react-native myCrypto = require('isomorphic-webcrypto'); } /** * @param bytes {Uint8Array} * @return {Promise<ArrayBuffer>} */ function sha256(bytes) { if (isCryptoAvailable) { // web return crypto.subtle.digest("SHA-256", bytes); } else { // nodejs or react-native return myCrypto.subtle.digest({name:"SHA-256"}, bytes); } } /** * from coins to nanocoins * @param amount {BN | string} * @return {BN} */ function toNano(amount) { if (!BN.isBN(amount) && !(typeof amount === 'string')) { throw new Error('Please pass numbers as strings or BN objects to avoid precision errors.'); } return ethunit.toWei(amount, 'gwei'); } /** * from nanocoins to coins * @param amount {BN | string} * @return {string} */ function fromNano(amount) { if (!BN.isBN(amount) && !(typeof amount === 'string')) { throw new Error('Please pass numbers as strings or BN objects to avoid precision errors.'); } return ethunit.fromWei(amount, 'gwei'); } // look up tables const to_hex_array = []; const to_byte_map = {}; for (let ord = 0; ord <= 0xff; ord++) { let s = ord.toString(16); if (s.length < 2) { s = "0" + s; } to_hex_array.push(s); to_byte_map[s] = ord; } // converter using lookups /** * @param buffer {Uint8Array} * @return {string} */ function bytesToHex(buffer) { const hex_array = []; //(new Uint8Array(buffer)).forEach((v) => { hex_array.push(to_hex_array[v]) }); for (let i = 0; i < buffer.byteLength; i++) { hex_array.push(to_hex_array[buffer[i]]); } return hex_array.join(""); } // reverse conversion using lookups /** * @param s {string} * @return {Uint8Array} */ function hexToBytes(s) { s = s.toLowerCase(); const length2 = s.length; if (length2 % 2 !== 0) { throw "hex string must have length a multiple of 2"; } const length = length2 / 2; const result = new Uint8Array(length); for (let i = 0; i < length; i++) { const i2 = i * 2; const b = s.substring(i2, i2 + 2); if (!to_byte_map.hasOwnProperty(b)) throw new Error('invalid hex character ' + b); result[i] = to_byte_map[b]; } return result; } /** * @param str {string} * @param size {number} * @return {Uint8Array} */ function stringToBytes(str, size = 1) { let buf; let bufView; if (size === 1) { buf = new ArrayBuffer(str.length); bufView = new Uint8Array(buf); } if (size === 2) { buf = new ArrayBuffer(str.length * 2); bufView = new Uint16Array(buf); } if (size === 4) { buf = new ArrayBuffer(str.length * 4); bufView = new Uint32Array(buf); } for (let i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return new Uint8Array(bufView.buffer); } /** * @private * @param crc {number} * @param bytes {Uint8Array} * @return {number} */ function _crc32c(crc, bytes) { const POLY = 0x82f63b78; crc ^= 0xffffffff; for (let n = 0; n < bytes.length; n++) { crc ^= bytes[n]; crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; crc = crc & 1 ? (crc >>> 1) ^ POLY : crc >>> 1; } return crc ^ 0xffffffff; } /** * @param bytes {Uint8Array} * @return {Uint8Array} */ function crc32c(bytes) { //Version suitable for crc32-c of BOC const int_crc = _crc32c(0, bytes); const arr = new ArrayBuffer(4); const view = new DataView(arr); view.setUint32(0, int_crc, false); return new Uint8Array(arr).reverse(); } /** * @param data {ArrayLike<number>} * @return {Uint8Array} */ function crc16(data) { const poly = 0x1021; let reg = 0; const message = new Uint8Array(data.length + 2); message.set(data); for (let byte of message) { let mask = 0x80; while (mask > 0) { reg <<= 1; if (byte & mask) { reg += 1; } mask >>= 1 if (reg > 0xffff) { reg &= 0xffff; reg ^= poly; } } } return new Uint8Array([Math.floor(reg / 256), reg % 256]); } /** * @param a {Uint8Array} * @param b {Uint8Array} * @return {Uint8Array} */ function concatBytes(a, b) { const c = new Uint8Array(a.length + b.length); c.set(a); c.set(b, a.length); return c; } /** * @param a {Uint8Array} * @param b {Uint8Array} * @return {boolean} */ function compareBytes(a, b) { // TODO Make it smarter return a.toString() === b.toString(); } const base64abc = (() => { const abc = [] const A = "A".charCodeAt(0); const a = "a".charCodeAt(0); const n = "0".charCodeAt(0); for (let i = 0; i < 26; ++i) { abc.push(String.fromCharCode(A + i)); } for (let i = 0; i < 26; ++i) { abc.push(String.fromCharCode(a + i)); } for (let i = 0; i < 10; ++i) { abc.push(String.fromCharCode(n + i)); } abc.push("+"); abc.push("/"); return abc; })(); /** * @param bytes {Uint8Array} * @return {string} */ function bytesToBase64(bytes) { let result = ""; let i; const l = bytes.length; for (i = 2; i < l; i += 3) { result += base64abc[bytes[i - 2] >> 2]; result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)]; result += base64abc[((bytes[i - 1] & 0x0f) << 2) | (bytes[i] >> 6)]; result += base64abc[bytes[i] & 0x3f]; } if (i === l + 1) { // 1 octet missing result += base64abc[bytes[i - 2] >> 2]; result += base64abc[(bytes[i - 2] & 0x03) << 4]; result += "=="; } if (i === l) { // 2 octets missing result += base64abc[bytes[i - 2] >> 2]; result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)]; result += base64abc[(bytes[i - 1] & 0x0f) << 2]; result += "="; } return result; } // todo: base64 decoding process could ignore one extra character at the end of string and the byte-length check below won't be able to catch it. function base64toString(base64) { if (typeof self === 'undefined') { return Buffer.from(base64, 'base64').toString('binary'); // todo: (tolya-yanot) Buffer silently ignore incorrect base64 symbols, we need to throw error } else { return atob(base64); } } function stringToBase64(s) { if (typeof self === 'undefined') { return Buffer.from(s, 'binary').toString('base64'); // todo: (tolya-yanot) Buffer silently ignore incorrect base64 symbols, we need to throw error } else { return btoa(s); } } /** * @param base64 {string} * @return {Uint8Array} */ function base64ToBytes(base64) { const binary_string = base64toString(base64); const len = binary_string.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } return bytes; } /** * @param n {number} * @param ui8array {Uint8Array} * @return {number} */ function readNBytesUIntFromArray(n, ui8array) { let res = 0; for (let c = 0; c < n; c++) { res *= 256; res += ui8array[c]; } return res; } /** * @param seed {Uint8Array} * @returns {nacl.SignKeyPair} */ function keyPairFromSeed(seed) { return nacl.sign.keyPair.fromSeed(seed); } /** * @returns {nacl.SignKeyPair} */ function newKeyPair() { return nacl.sign.keyPair(); } /** * @returns {Uint8Array} */ function newSeed() { return nacl.sign.keyPair().secretKey.slice(0, 32); } module.exports = { BN, nacl, sha256, fromNano, toNano, bytesToHex, hexToBytes, stringToBytes, crc32c, crc16, concatBytes, bytesToBase64, base64ToBytes, base64toString, stringToBase64, compareBytes, readNBytesUIntFromArray, keyPairFromSeed, newKeyPair, newSeed };