UNPKG

dagcoin-core

Version:
280 lines (215 loc) 10.8 kB
'use strict'; var instance = null; // My module function ProofManager() { this.ecdsaSig = require('byteballcore/signature'); this.db = require('byteballcore/db'); this.hasher = require('byteballcore/object_hash'); this.crypto = require('crypto'); this.deviceManager = require('dagcoin-core/lib/deviceManager').getInstance(); this.active = false; } ProofManager.prototype.proofAddressBatch = function (proofAddressBatch, deviceAddress) { var self = this; if (!proofAddressBatch || proofAddressBatch.length === 0) { console.log('NOTHING TO PROOF'); return Promise.resolve({ validBatch: [], invalidBatch: [] }); } var proof = proofAddressBatch.pop(); return self.proofAddressBatch(proofAddressBatch, deviceAddress).then(function (result) { return self.proofAddressAndSaveToDB(proof, deviceAddress).then(function (valid) { if (valid) { result.validBatch.push(proof); } else { result.invalidBatch.push(proof); } return Promise.resolve(result); }); }); }; ProofManager.prototype.proofAddress = function (proof, proofDeviceAddress) { var self = this; var deviceAddress = proofDeviceAddress; if (!deviceAddress) { deviceAddress = proof.device_address; if (!deviceAddress) { throw Error('NO DEVICE ADDRESS AVAILABLE FOR PROOFING'); } } if (!proof) { throw Error('PARAMETER proof IS NOT SET'); } if (!proof.address) { throw Error('PARAMETER proof.address IS NOT SET'); } if (!proof.address_definition) { throw Error('PARAMETER proof.address_definition IS NOT SET'); } var definition = JSON.parse(proof.address_definition); var definitionHash = self.hasher.getChash160(definition); if (definitionHash !== proof.address) { console.log('DEFINITION VALIDATION FAILED: ' + proof.address + ' IS NOT THE HASH OF ' + proof.address_definition + ' (THI IS: ' + definitionHash + ')'); return Promise.resolve(false); } console.log('HASH 160 VALIDATED: ' + proof.address + ' IS THE HASH OF ' + proof.address_definition); console.log('PROOFING ' + deviceAddress + ' AGAINST ' + proof.device_address_signature); var valid = self.proof(deviceAddress, proof.device_address_signature, definition); // IT MEANS IT DOES NOT HAVE A MASTER ADDRESS (SO IT IS ONE) OR IS ALREADY INVALID if (!proof.master_address || !valid) { console.log('PROOF RESULT: ' + valid); return Promise.resolve(valid); } //FURTHER PROOF IN CASE OF LINK TO A MASTER ADDRESS return new Promise(function (resolve, reject) { self.db.query('SELECT device_address, address_definition\n FROM dagcoin_proofs \n WHERE proofed = 1 AND address = ? AND device_address = ?', [proof.master_address, deviceAddress], function (rows) { if (!rows || rows.length === 0) { console.log('COULD NOT FIND PROOF FOR MASTER ADDRESS ' + proof.master_address + ' OF ' + proof.address + ' WITH DEVICE\n ' + deviceAddress); resolve(false); return; } if (rows.length > 1) { reject('TOO MANY PROOF FOR MASTER ADDRESS ' + proof.master_address + ' OF ' + proof.address + ' WITH DEVICE\n ' + deviceAddress); return; } var masterDeviceAddress = rows[0].device_address; if (!masterDeviceAddress) { reject('NO DEVICE ADDRESS FOR MASTER ADDRESS ' + proof.master_address + ' OF ' + proof.address + ' WITH DEVICE\n ' + deviceAddress); return; } if (masterDeviceAddress !== deviceAddress) { console.log('DEVICE ADDRESSES OF MASTER ADDRESS ' + proof.master_address + ' AND ' + proof.address + ' DO NOT MATCH:\n ' + deviceAddress + ' \u2243 ' + masterDeviceAddress); resolve(false); return; } var masterAddressDefinition = JSON.parse(rows[0].address_definition); if (!masterAddressDefinition) { reject('NO ADDRESS DEFINITION FOR MASTER ADDRESS ' + proof.master_address + ' OF ' + proof.address + ' WITH DEVICE\n ' + deviceAddress); return; } resolve(self.proof(proof.address, proof.master_address_signature, masterAddressDefinition)); }); }); }; ProofManager.prototype.proof = function (textToProve, signature, definition) { if (!textToProve) { throw Error('PARAMETER textToProve IS NOT SET'); } if (!signature) { throw Error('PARAMETER signature IS NOT SET'); } if (!definition) { throw Error('PARAMETER definition IS NOT SET'); } if (!definition[0] === 'sig' || !definition[1].pubkey) { throw Error('DEFINITION DOES NOT CONTAIN A PUBLIC KEY. CHECK THE DEFINITION: ' + JSON.stringify(definition)); } var publicKey = definition[1].pubkey; var publicKeyBase64 = new Buffer(publicKey, 'base64'); var bufferOfTextToProve = this.crypto.createHash('sha256').update(textToProve, 'utf8').digest(); return this.ecdsaSig.verify(bufferOfTextToProve, signature, publicKeyBase64); }; ProofManager.prototype.proofText = function (proof, text) { var self = this; if (!proof) { throw Error('PARAMETER proof IS NOT SET'); } if (!proof.address) { throw Error('PARAMETER proof.address IS NOT SET'); } if (!proof.address_definition) { throw Error('PARAMETER proof.address_definition IS NOT SET'); } var definition = JSON.parse(proof.address_definition); var definitionHash = self.hasher.getChash160(definition); if (definitionHash !== proof.address) { console.log('DEFINITION VALIDATION FAILED: ' + proof.address + ' IS NOT THE HASH OF ' + proof.address_definition + ' (THI IS: ' + definitionHash + ')'); return Promise.resolve(false); } console.log('HASH 160 VALIDATED: ' + proof.address + ' IS THE HASH OF ' + proof.address_definition); console.log('PROOFING ' + text + ' AGAINST ' + proof.text_signature); return self.proof(text, proof.text_signature, definition); }; ProofManager.prototype.hasAddressProofInDb = function (address, deviceAddress) { var self = this; return new Promise(function (resolve, reject) { self.db.query('SELECT proofed FROM dagcoin_proofs WHERE address = ? AND device_address = ? ' + 'UNION SELECT 1 FROM shared_addresses WHERE shared_address = ? ' + 'UNION SELECT 1 FROM dagcoin_funding_addresses WHERE master_address = ? AND STATUS = \'LEGACY\'', [address, deviceAddress, address], function (rows) { if (!rows || rows.length === 0) { resolve(false); return; } if (rows.length > 1) { reject('MORE THAN A PROOF FOR ' + address + ', ' + deviceAddress + '. THIS IS UNEXPECTED'); return; } resolve(rows[0].proofed === 1); }); }); }; /** * * @param proof * @returns {Promise} Resolves to true when the address proof is stored into the db */ ProofManager.prototype.proofAddressAndSaveToDB = function (proof, deviceAddress) { var self = this; return new Promise(function (resolve, reject) { self.db.query('SELECT address, proofed FROM dagcoin_proofs WHERE address = ? AND device_address = ?', [proof.address, deviceAddress], function (rows) { if (!rows || rows.length === 0) { console.log('NO PROOF AVAILABLE FOR ' + proof.address + ' YET'); resolve(false); return; } if (rows.length > 1) { reject('TOO MANY PROOFS AVAILABLE FOR ' + proof.address + ': ' + rows.length + '. CHECK THE DATABASE STATE'); return; } var previousProof = rows[0]; if (!previousProof.proofed) { console.log('ALREADY DETECTED A FAILED ATTEMPT TO PROOF ' + proof.address + ' OWNERSHIP TO ' + deviceAddress); self.db.query('DELETE FROM dagcoin_proofs WHERE address = ? AND device_address = ?', [proof.address, deviceAddress], function (result) { console.log('DB DELETE OF OLD PROOF OF ' + proof.address + ' OWNERSHIP TO ' + deviceAddress + ' : ' + result); resolve(false); }); return; } resolve(true); }); }).then(function (alreadyProofed) { if (alreadyProofed) { return Promise.resolve(true); } return self.proofAddress(proof, deviceAddress).then(function (proofed) { return new Promise(function (resolve) { self.db.query('\n INSERT INTO dagcoin_proofs (\n address,\n address_definition,\n device_address_signature,\n master_address,\n master_address_signature,\n device_address,\n proofed\n ) VALUES(?, ?, ?, ?, ?, ?, ?)', [proof.address, proof.address_definition, proof.device_address_signature, proof.master_address, proof.master_address_signature, deviceAddress, proofed], function (result) { console.log('DB DELETE OF OLD PROOF OF ' + proof.address + ' OWNERSHIP TO ' + deviceAddress + ' : ' + result); resolve(proofed); }); }); }); }); }; ProofManager.prototype.askForProofs = function (deviceAddress, addresses) { if (!addresses || addresses.length === 0) { console.log('ProofManager.askForProofs: CALLED WITH AN EMPTY LIST OF addresses. NOTHING TO BE DONE'); return Promise.resolve(null); } var self = this; return self.deviceManager.sendRequestAndListen(deviceAddress, 'proofing', { addresses: addresses }).then(function (messageBody) { var proofs = messageBody.proofs; console.log('PROOFS: ' + JSON.stringify(proofs)); if (!proofs || proofs.length === 0) { return Promise.reject('NO PROOFS PROVIDED IN THE CLIENT RESPONSE FOR ' + JSON.stringify(addresses)); } else { return Promise.resolve(proofs); } }).then(function (proofs) { return self.proofAddressBatch(proofs, deviceAddress); }); }; module.exports = ProofManager; module.exports.getInstance = function () { if (!instance) { instance = new ProofManager(); } return instance; };