UNPKG

wanchain-util

Version:

wanchain util package

536 lines (479 loc) 17.6 kB
const createKeccakHash = require('keccak'); const ethTx = require("ethereumjs-tx"); const ethUtil = require('ethereumjs-util'); const crypto = require('crypto'); const BN = require('bn.js'); const secp256k1 = require('secp256k1'); const secp256k1_N = new BN("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16); const wanchainTx = function (data) { // Define Properties const fields = [{ name : 'Txtype', length:32, allowLess:true, default: new Buffer([]) },{ name: 'nonce', length: 32, allowLess: true, default: new Buffer([]) }, { name: 'gasPrice', length: 32, allowLess: true, default: new Buffer([]) }, { name: 'gasLimit', alias: 'gas', length: 32, allowLess: true, default: new Buffer([]) }, { name: 'to', allowZero: true, length: 20, default: new Buffer([]) }, { name: 'value', length: 32, allowLess: true, default: new Buffer([]) }, { name: 'data', alias: 'input', allowZero: true, default: new Buffer([]) }, { name: 'v', length: 1, default: new Buffer([0x1c]) }, { name: 'r', length: 32, allowLess: true, default: new Buffer([]) }, { name: 's', length: 32, allowLess: true, default: new Buffer([]) }] /** * Returns the rlp encoding of the transaction * @method serialize * @return {Buffer} */ // attached serialize ethUtil.defineProperties(this, fields, data) /** * @prop {Buffer} from (read only) sender address of this transaction, mathematically derived from other parameters. */ Object.defineProperty(this, 'from', { enumerable: true, configurable: true, get: this.getSenderAddress.bind(this) }) // calculate chainId from signature let sigV = ethUtil.bufferToInt(this.v) let chainId = Math.floor((sigV - 35) / 2) if (chainId < 0) chainId = 0 // set chainId this._chainId = chainId || data.chainId || 0 this._homestead = true } function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; } extend(wanchainTx, ethTx); /** * Computes a sha3-256 hash of the serialized tx * @method hash * @param {Boolean} [signature=true] whether or not to inculde the signature * @return {Buffer} */ wanchainTx.prototype.hash = function (includeSignature){ if (includeSignature === undefined) includeSignature = true // EIP155 spec: // when computing the hash of a transaction for purposes of signing or recovering, // instead of hashing only the first six elements (ie. nonce, gasprice, startgas, to, value, data), // hash nine elements, with v replaced by CHAIN_ID, r = 0 and s = 0 let items if (includeSignature) { items = this.raw } else { if (this._chainId > 0) { const raw = this.raw.slice() this.v = this._chainId this.r = 0 this.s = 0 items = this.raw this.raw = raw } else { items = this.raw.slice(0, 7) } } // create hash return ethUtil.rlphash(items) } exports.wanchainTx = wanchainTx; //x * hash(P)P exports.xScalarHashP = function(x, P) { let hashPub = ethUtil.sha3(P); let iP = secp256k1.publicKeyTweakMul(P, hashPub); let I = secp256k1.publicKeyTweakMul(iP, x); return I; } exports.waddressLength = 66*2; exports.isValidWAddress = function (address) { return /^0x[0-9a-fA-F]{132}$/i.test(address) } exports.toChecksumOTAddress = function (address) { address = exports.stripHexPrefix(address).toLowerCase(); if(address.length != exports.waddressLength){ return ""; } let abx = address.slice(2,66)+address.slice(68) let Cabx = "" var hash = ethUtil.sha3(address,512).toString('hex') for (var i = 0; i < abx.length; i++) { if (parseInt(hash[i], 16) >= 8) { Cabx += abx[i].toUpperCase(); }else{ Cabx += abx[i]; } } return "0x"+address.slice(0,2)+Cabx.slice(0,64)+address.slice(66,68)+Cabx.slice(64); } exports.isValidChecksumOTAddress = function (address) { return exports.isValidWAddress(address) && (exports.toChecksumOTAddress(address) === address) } exports.getDataForSendWanCoin = function(fromWaddr){ if (!exports.isValidChecksumOTAddress(fromWaddr)){ return ""; } let Pubkey = exports.stripHexPrefix(fromWaddr).toLowerCase(); return "0x00"+Pubkey; } exports.verifyRinSign = function(ringArgs){ let sumC = new BN('0'); for (let i=0; i<ringArgs.w.length;i++){ sumC = sumC.add(new BN(ringArgs.w[i])); } sumC = sumC.umod(secp256k1_N); let h = createKeccakHash('keccak256'); h.update(ringArgs.m); for (let i=0; i<ringArgs.w.length;i++){ let Li = secp256k1.publicKeyCreate(ringArgs.q[i], false);//[qi]G let tP = secp256k1.publicKeyTweakMul(ringArgs.PubKeys[i], ringArgs.w[i]);//[wi]Pi Li = secp256k1.publicKeyCombine([Li, tP], false); // [qi]G + [wi]Pi h.update(Li); } for (let i=0; i<ringArgs.q.length;i++){ let Ric = exports.xScalarHashP(ringArgs.q[i], ringArgs.PubKeys[i]); let Ri = secp256k1.publicKeyConvert(Ric, false); let wiI = secp256k1.publicKeyTweakMul(ringArgs.I, ringArgs.w[i]); Ri = secp256k1.publicKeyCombine([Ri, wiI], false); h.update(Ri); } let hash = h.digest(); return hash.toString('hex') == sumC.toArrayLike(Buffer, 'be', 32).toString('hex'); } exports.getRingSign = function(m,otaSk,otaPubK,ringPubKs){ let rklen = ringPubKs.length; let s = Math.floor(Math.random()*(rklen+1)); ringPubKs.splice(s, 0, otaPubK); let Ic = exports.xScalarHashP(otaSk, otaPubK); //otaSk * hash(otaPubK)otaPubK let I = secp256k1.publicKeyConvert(Ic, false); let q = []; let w = []; let sumC = new BN('0'); let h = createKeccakHash('keccak256'); h.update(m); for(let i=0; i<rklen+1; i++) { q.push(_generatePrivateKey()); w.push(_generatePrivateKey()); let Li = secp256k1.publicKeyCreate(q[i], false);//[qi]G if(i != s){ let tP = secp256k1.publicKeyTweakMul(ringPubKs[i], w[i]);//[wi]Pi Li = secp256k1.publicKeyCombine([Li, tP], false); // [qi]G + [wi]Pi sumC = sumC.add(new BN(w[i])); sumC = sumC.umod(secp256k1_N); } h.update(Li); } for(let i=0; i<rklen+1; i++) { let Ric = exports.xScalarHashP(q[i], ringPubKs[i]); let Ri = secp256k1.publicKeyConvert(Ric, false); if(i != s){ let wiI = secp256k1.publicKeyTweakMul(I, w[i]); Ri = secp256k1.publicKeyCombine([Ri, wiI], false); } h.update(Ri); } let cd = h.digest('hex'); let c = new BN(cd,16).umod(secp256k1_N); let cs = c.sub(sumC).umod(secp256k1_N); let Qs = new BN(q[s]); let bnx = new BN(otaSk).umod(secp256k1_N); let csx = cs.mul(bnx).umod(secp256k1_N)//; let rs = Qs.sub(csx).umod(secp256k1_N);; w[s] = cs.toArrayLike(Buffer, 'be', 32); qs_old = q[s]; q[s] = rs.toArrayLike(Buffer,'be', 32); // let qs_oldXG = secp256k1.publicKeyCreate(qs_old, false); // let qs_newXG_1 = secp256k1.publicKeyCreate(q[s], false); // let qs_newXG_2 = secp256k1.publicKeyTweakMul(ringPubKs[s], w[s]); // let qs_newXG = secp256k1.publicKeyCombine([qs_newXG_1, qs_newXG_2], false); return { q:q, w:w, PubKeys:ringPubKs, I: I, m: m }; } exports.convertWaddrtoRaw = function(fromWaddr){ let address = exports.stripHexPrefix(fromWaddr).toLowerCase(); let pubKeyA = secp256k1.publicKeyConvert(new Buffer(address.slice(0,66), 'hex'), false); let pubKeyB = secp256k1.publicKeyConvert(new Buffer(address.slice(66), 'hex'), false); let PubKey = secp256k1.publicKeyConvert(pubKeyA,false).toString('hex').slice(2)+secp256k1.publicKeyConvert(pubKeyB,false).toString('hex').slice(2); return PubKey; } exports.convertRawtoWaddr = function(fromRawaddr){ let addr = exports.recoverPubkeyFromRaw(fromRawaddr); let pubKeyA = addr.A; let pubKeyB = addr.B; let PubKey = secp256k1.publicKeyConvert(pubKeyA,true).toString('hex')+secp256k1.publicKeyConvert(pubKeyB,true).toString('hex'); return exports.toChecksumOTAddress(PubKey); } exports.generateWaddrFromPriv = function(privA, privB){ let pubkeyA = secp256k1.publicKeyCreate(privA, true); let pubkeyB = secp256k1.publicKeyCreate(privB, true); return exports.convertPubKeytoWaddr(pubkeyA, pubkeyB); } exports.convertPubKeytoWaddr = function(pubKeyA, pubKeyB){ let PubKey = secp256k1.publicKeyConvert(pubKeyA,true).toString('hex')+secp256k1.publicKeyConvert(pubKeyB,true).toString('hex'); return exports.toChecksumOTAddress(PubKey); } exports.generateA1 = function(RPrivateKeyDBytes, pubKeyA, pubKeyB){ let A1 = secp256k1.publicKeyTweakMul(pubKeyB, RPrivateKeyDBytes, false); let A1Bytes = ethUtil.sha3(A1); A1 = secp256k1.publicKeyTweakAdd(pubKeyA, A1Bytes, false); return A1; } exports.recoverPubkeyFromWaddress = function(fromWaddr){ let address = exports.stripHexPrefix(fromWaddr).toLowerCase(); let pubKeyA = secp256k1.publicKeyConvert(new Buffer(address.slice(0,66), 'hex'), false); let pubKeyB = secp256k1.publicKeyConvert(new Buffer(address.slice(66), 'hex'), false); return {A:pubKeyA, B:pubKeyB} } exports.recoverPubkeyFromRaw = function(fromRaw){ let rawA = "04"+fromRaw.slice(0,128); let rawB = "04"+fromRaw.slice(128); let pubKeyA = secp256k1.publicKeyConvert(new Buffer(rawA, 'hex'), false); let pubKeyB = secp256k1.publicKeyConvert(new Buffer(rawB, 'hex'), false); return {A:pubKeyA, B:pubKeyB} } exports.generateOTAWaddress = function (fromWaddr) { let PubKey = exports.recoverPubkeyFromWaddress(fromWaddr); let pubKeyA = PubKey.A; let pubKeyB = PubKey.B; let RPrivateKey = _generatePrivateKey(); let A1 = exports.generateA1(RPrivateKey, pubKeyA, pubKeyB) let S1 = secp256k1.publicKeyCreate(new Buffer(RPrivateKey, 'hex'), false); let OTAPubKey = secp256k1.publicKeyConvert(A1,true).toString('hex')+secp256k1.publicKeyConvert(S1,true).toString('hex'); return exports.toChecksumOTAddress(OTAPubKey); } /** * Returns a `Boolean` on whether or not the a `String` starts with "0x" * @param {String} str * @return {Boolean} */ exports.isHexPrefixed = function (str) { return str.slice(0, 2) === '0x' } /** * Removes "0x" from a given `String` * @param {String} str * @return {String} */ exports.stripHexPrefix = function (str) { if (typeof str !== 'string') { return str } return exports.isHexPrefixed(str) ? str.slice(2) : str } /** * Pads a `String` to have an even length * @param {String} a * @return {String} */ exports.padToEven = function (a) { if (a.length % 2) a = '0' + a return a } exports.otaHash = function(){ if(arguments.length < 1){ throw "invalid parameters"; } var buf = new Buffer([]); for (i = 0; i < arguments.length; i++){ item = exports.toBuffer(arguments[i]); buf = Buffer.concat([buf, item]); } return ethUtil.sha3(buf); } //strstrPrivateKey shouldn't have 0x prefix exports.otaSign = function(hashSrc, strPrivateKey){ var privateKey = new Buffer(strPrivateKey, 'hex') return exports.ecsign(hashSrc, privateKey); } exports.ascii_to_hexa = function (str) { var arr1 = []; for (var n = 0, l = str.length; n < l; n ++) { var hex = Number(str.charCodeAt(n)).toString(16); arr1.push(hex); } return arr1.join(''); } //convert number to bytes32 for compatible with contract evm hash implements //TODO: validate input exports.numberToBytes32 = function(input){ if(!input){ return ''; } var inputStr = input.toString(); var a2hStr = exports.ascii_to_hexa(inputStr); var padding = ""; for (var i = 0; i < 64 - a2hStr.length; i++){ padding += "0" } return '0x' + a2hStr + padding; } /** * get public key string from private key string * @param private key string * @return {String|null} */ exports.publicKeyFromPrivateKey = function (privateKey) { if(!privateKey.startsWith('0x')){ privateKey = '0x' + privateKey; } return exports.bufferToHex(exports.privateToPublic(privateKey), 'hex'); } function _generatePrivateKey(){ var randomBuf = crypto.randomBytes(32); if (secp256k1.privateKeyVerify(randomBuf)){ return randomBuf; } else { return _generatePrivateKey(); } } function _generateA1(RPrivateKeyDBytes, pubKeyA, pubKeyB){ A1 = secp256k1.publicKeyTweakMul(pubKeyA, RPrivateKeyDBytes, false); A1Bytes = ethUtil.sha3(A1); A1 = secp256k1.publicKeyTweakAdd(pubKeyB, A1Bytes, false); return A1; } function _generateOTAPublicKey(pubKeyA, pubKeyB){ RPrivateKey = _generatePrivateKey(); A1 = _generateA1(RPrivateKey, pubKeyA, pubKeyB); return { OtaA1: exports.bufferToHex(A1).slice(4), OtaS1: exports.bufferToHex(exports.privateToPublic(RPrivateKey)).slice(2) }; } //input is 128 or 130 byte function _utilPubkey2SecpFormat(utilPubKeyStr) { if(utilPubKeyStr.startsWith('0x')){ utilPubKeyStr = utilPubKeyStr.slice(2); } utilPubKeyStr = '04' + utilPubKeyStr; return secp256k1.publicKeyConvert(new Buffer(utilPubKeyStr, 'hex')); } exports.pubkeyStrCompressed = function(pubStr){ buf = _utilPubkey2SecpFormat(pubStr); return exports.bufferToHex(buf); } //get secp256k1 format public key buf function _secpPUBKBufFromPrivate(privateKey) { var pubStr = exports.pulicKeyFromPrivateKey(privateKey); return _utilPubkey2SecpFormat(pubStr); } exports.generateOTAPublicKey = function (A, B) { var pubKeyA = _utilPubkey2SecpFormat(A); var pubKeyB = _utilPubkey2SecpFormat(B); return _generateOTAPublicKey(pubKeyA, pubKeyB); } function _privateKeyStr2Buf(s) { if(s.startsWith('0x')){ s = s.slice(2); } return new Buffer(s, 'hex'); } exports.computeOTAPrivateKey = function(A, S, a, b){ var otaPubS1 = _utilPubkey2SecpFormat(S); var privatekey_a =_privateKeyStr2Buf(a); var privatekey_b = _privateKeyStr2Buf(b); var pub = secp256k1.publicKeyTweakMul(otaPubS1, privatekey_b, false); k = ethUtil.sha3(pub); k = secp256k1.privateKeyTweakAdd(k, privatekey_a); return k; } /* otaPubS1 is secpFormat bufa, bufb is privatekey Buffer. */ exports.computeWaddrPrivateKey = function(waddr, bufa, bufb){ let ota = exports.recoverPubkeyFromWaddress(waddr); var pub = secp256k1.publicKeyTweakMul(ota.B, bufb, false); k = ethUtil.sha3(pub); k = secp256k1.privateKeyTweakAdd(k, bufa); return k; } /** * Checks if the address is a valid. Accepts checksummed addresses too * @param {String} address * @return {Boolean} */ exports.isValidAddress = function (address) { return /^0x[0-9a-fA-F]{40}$/i.test(address) } /** * Returns a checksummed address * @param {String} address * @return {String} */ exports.toChecksumAddress = function (address) { address = exports.stripHexPrefix(address).toLowerCase() var hash = exports.sha3(address).toString('hex') var ret = '0x' for (var i = 0; i < address.length; i++) { if (parseInt(hash[i], 16) < 8) { ret += address[i].toUpperCase() } else { ret += address[i] } } return ret } /** * Checks if the address is a valid checksummed address * @param {Buffer} address * @return {Boolean} */ exports.isValidChecksumAddress = function (address) { return exports.isValidAddress(address) && (exports.toChecksumAddress(address) === address) } exports.sha3 = ethUtil.sha3; exports.web3Wan = require("./web3_wan.js"); exports.coinSCAbi = [{"constant":false,"type":"function","stateMutability":"nonpayable","inputs":[{"name":"OtaAddr","type":"string"},{"name":"Value","type":"uint256"}],"name":"buyCoinNote","outputs":[{"name":"OtaAddr","type":"string"},{"name":"Value","type":"uint256"}]},{"constant":false,"type":"function","inputs":[{"name":"RingSignedData","type":"string"},{"name":"Value","type":"uint256"}],"name":"refundCoin","outputs":[{"name":"RingSignedData","type":"string"},{"name":"Value","type":"uint256"}]},{"constant":false,"inputs":[],"name":"getCoins","outputs":[{"name":"Value","type":"uint256"}]}]; exports.stampSCAbi = [{"constant":false,"type":"function","stateMutability":"nonpayable","inputs":[{"name":"OtaAddr","type":"string"},{"name":"Value","type":"uint256"}],"name":"buyStamp","outputs":[{"name":"OtaAddr","type":"string"},{"name":"Value","type":"uint256"}]},{"constant":false,"type":"function","inputs":[{"name":"RingSignedData","type":"string"},{"name":"Value","type":"uint256"}],"name":"refundCoin","outputs":[{"name":"RingSignedData","type":"string"},{"name":"Value","type":"uint256"}]},{"constant":false,"type":"function","stateMutability":"nonpayable","inputs":[],"name":"getCoins","outputs":[{"name":"Value","type":"uint256"}]}]; exports.contractCoinAddress = '0x0000000000000000000000000000000000000064'; exports.contractStampAddress = '0x00000000000000000000000000000000000000c8';