From d52828ee24c2e3fe8ffb98a3f731bec18fe92aaf Mon Sep 17 00:00:00 2001 From: =Stefano Fedeli <=> Date: Tue, 8 Oct 2019 17:16:20 +0200 Subject: [PATCH 1/5] Added Skelethon --- ots-cli.js | 46 ++++++++++++++++++++++++++++++++++++++++++ src/open-timestamps.js | 15 ++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/ots-cli.js b/ots-cli.js index 2d42277..78f766c 100644 --- a/ots-cli.js +++ b/ots-cli.js @@ -44,6 +44,20 @@ const infoCommand = program info(file, options) }) + const pruneCommand = program + .command('prune [FILE_OTS]') + .alias('p') + .description('Prune timestamp.') + .action((file, options) => { + isExecuted = true + if (!file) { + console.log(prugneCommand.helpInformation()) + return + } + options = parseCommon(options) + prune(file,options) + }) + const stampCommand = program .command('stamp [FILE...]') .alias('s') @@ -185,6 +199,38 @@ function info (argsFileOts, options) { }) } +function prune(argsFileOts,options) { + const files = [] + files.push(Utils.readFilePromise(argsFileOts, null)) + Promise.all(files).then(values => { + const fileOts = values[0] + + // Read ots file and check hash function + let detachedOts + try { + detachedOts = DetachedTimestampFile.deserialize(fileOts) + } catch (err) { + if (err instanceof Context.BadMagicError) { + throw new Error('Error! ' + argsFileOts + ' is not a timestamp file.') + } else if (err instanceof Context.DeserializationError) { + throw new Error('Invalid timestamp file ' + argsFileOts) + } else { + throw err + } + } + // Opentimestamps prune + const prunePromise = OpenTimestamps.prune(detachedOts, options) + process.exit(1) + }).catch(err => { + if (err.code === 'ENOENT') { + console.error('File not found \'' + err.path + '\'') + } else { + console.error(err.message) + } + process.exit(1) + }) +} + function stamp (argsFiles, options) { // check input params : file/hash const filePromises = [] diff --git a/src/open-timestamps.js b/src/open-timestamps.js index 3b2687d..911e095 100644 --- a/src/open-timestamps.js +++ b/src/open-timestamps.js @@ -97,6 +97,21 @@ module.exports = { return JSON.stringify(json) }, + /** + * Prune a timestamp dropping the redundant data included in the ots receipt, storing only the linear proof from the file hash to the best attestation. + */ + prune(detaches, options = {}) { + let prunable = false + + if(detaches.timestamp.getAttestations().length <= 1) { return } + detaches.timestamp.getAttestations().forEach(subStamp => { + if(! subStamp instanceof Notary.PendingAttestation ) { + prunable = true; + } + }) + console.log(prunable) + }, + /** * Create timestamp with the aid of a remote calendar for one or multiple files. * @exports OpenTimestamps/stamp From c2f5897943deb9c1f4252cf5af25755c61e43143 Mon Sep 17 00:00:00 2001 From: =Stefano Fedeli <=> Date: Tue, 8 Oct 2019 21:42:24 +0200 Subject: [PATCH 2/5] Filtering attestation --- src/open-timestamps.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/open-timestamps.js b/src/open-timestamps.js index 911e095..fa69811 100644 --- a/src/open-timestamps.js +++ b/src/open-timestamps.js @@ -110,6 +110,8 @@ module.exports = { } }) console.log(prunable) + detaches.timestamp.attestations = detaches.timestamp.attestations.filter(item => item === Notary.PendingAttestation) + console.log(detaches.timestamp) }, /** From 44001c1186297e2c2c0a7ae8b04de97b27e940f0 Mon Sep 17 00:00:00 2001 From: =Stefano Fedeli <=> Date: Fri, 18 Oct 2019 20:03:45 +0200 Subject: [PATCH 3/5] Prune scratch --- ots-cli.js | 10 ++++++++-- src/open-timestamps.js | 35 +++++++++++++++++++++++++++++------ src/timestamp.js | 10 ++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/ots-cli.js b/ots-cli.js index 78f766c..e68dbc8 100644 --- a/ots-cli.js +++ b/ots-cli.js @@ -219,8 +219,14 @@ function prune(argsFileOts,options) { } } // Opentimestamps prune - const prunePromise = OpenTimestamps.prune(detachedOts, options) - process.exit(1) + OpenTimestamps.prune(detachedOts, options) + .then(function(results){ + fs.renameSync(argsFileOts,argsFileOts+".bak") + fs.writeFile(argsFileOts, Buffer.from(detachedOts.serializeToBytes()), 'binary',err =>{}) + console.log("Recepit Pruned") + }).catch(err => { + console.log("Is not possible to prune due to " + err) + }) }).catch(err => { if (err.code === 'ENOENT') { console.error('File not found \'' + err.path + '\'') diff --git a/src/open-timestamps.js b/src/open-timestamps.js index fa69811..0382a60 100644 --- a/src/open-timestamps.js +++ b/src/open-timestamps.js @@ -97,21 +97,44 @@ module.exports = { return JSON.stringify(json) }, + + discard_attestation(timestamp) { + timestamp.getAttestations().forEach(opStamp => { + if(opStamp instanceof Notary.PendingAttestation ) { + timestamp.attestations.splice(timestamp.attestations.indexOf(opStamp),1) + } + }) + + timestamp.ops.forEach(opStamp => { + this.discard_attestation(opStamp) + }) + }, /** * Prune a timestamp dropping the redundant data included in the ots receipt, storing only the linear proof from the file hash to the best attestation. */ prune(detaches, options = {}) { let prunable = false - if(detaches.timestamp.getAttestations().length <= 1) { return } + if(detaches.timestamp.getAttestations().length <= 1) { + return new Promise((resolve, reject) => { reject(new Error('Just one attestation founded')) }) + } detaches.timestamp.getAttestations().forEach(subStamp => { - if(! subStamp instanceof Notary.PendingAttestation ) { + if(!(subStamp instanceof Notary.PendingAttestation)) { prunable = true; - } + } /*else if(! subStamp instanceof Notary.BitcoinBlockHeaderAttestation) { + return new Promise((resolve, reject) => { reject(new Error('No Bitcoin transaction founded')) }) + }*/ }) - console.log(prunable) - detaches.timestamp.attestations = detaches.timestamp.attestations.filter(item => item === Notary.PendingAttestation) - console.log(detaches.timestamp) + console.log(detaches.timestamp.strTree()) + if(prunable) { + this.discard_attestation(detaches.timestamp) + console.log(detaches.timestamp.strTree()) + process.exit(1) + return new Promise((resolve, reject) => { resolve(detaches) }) + } else { + return new Promise((resolve, reject) => { reject(new Error('No changes')) }) + } + }, /** diff --git a/src/timestamp.js b/src/timestamp.js index 175a373..1bf86a0 100644 --- a/src/timestamp.js +++ b/src/timestamp.js @@ -84,6 +84,16 @@ class Timestamp { return self } + delAttestation(clone_to_del) { + console.log("Recieved " + clone_to_del) + console.log(this.attestations) + var index = this.attestations.indexOf(clone_to_del) + console.log("I'm going to delete " + index) + this.attestations.splice(index,1) + console.log("Done! " + this.attestations) + } + + /** * Create a Serialize object. * @param {StreamSerializationContext} ctx - The stream serialization context. From 1611f1a0615302e119345cb3a0b46120298de1ff Mon Sep 17 00:00:00 2001 From: =Stefano Fedeli <=> Date: Tue, 22 Oct 2019 12:08:50 +0200 Subject: [PATCH 4/5] Working basic prune command --- src/open-timestamps.js | 48 ++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/open-timestamps.js b/src/open-timestamps.js index 0382a60..c182063 100644 --- a/src/open-timestamps.js +++ b/src/open-timestamps.js @@ -97,42 +97,64 @@ module.exports = { return JSON.stringify(json) }, + + pruneTree(timestamp) { + let prun = timestamp.attestations.length == 0 + let changed = false + + for(const element of timestamp.ops) { + var result = this.pruneTree(element[1]) + var stamp_prunable = result.prunable + var stamp_changed = result.change + changed = changed || stamp_changed || stamp_prunable + if(stamp_prunable) { + timestamp.ops.delete(element[0]) + } else { + prun = false + } + } + return {prunable : prun, change: changed} + + }, - discard_attestation(timestamp) { + discard_attestation(timestamp, saveOnly) { timestamp.getAttestations().forEach(opStamp => { - if(opStamp instanceof Notary.PendingAttestation ) { + if(!(opStamp instanceof Notary.BitcoinBlockHeaderAttestation && opStamp.height == saveOnly) ) { timestamp.attestations.splice(timestamp.attestations.indexOf(opStamp),1) } }) - - timestamp.ops.forEach(opStamp => { - this.discard_attestation(opStamp) - }) + var iter = timestamp.ops.values() + let res = iter.next() + while (!res.done) { + this.discard_attestation(res.value) + res = iter.next() + } }, /** * Prune a timestamp dropping the redundant data included in the ots receipt, storing only the linear proof from the file hash to the best attestation. */ prune(detaches, options = {}) { let prunable = false + let maxHead = 0; if(detaches.timestamp.getAttestations().length <= 1) { return new Promise((resolve, reject) => { reject(new Error('Just one attestation founded')) }) } detaches.timestamp.getAttestations().forEach(subStamp => { - if(!(subStamp instanceof Notary.PendingAttestation)) { + if(subStamp instanceof Notary.BitcoinBlockHeaderAttestation) { prunable = true; - } /*else if(! subStamp instanceof Notary.BitcoinBlockHeaderAttestation) { - return new Promise((resolve, reject) => { reject(new Error('No Bitcoin transaction founded')) }) - }*/ + maxHead = (maxHead < subStamp.height)? subStamp.height : maxHead + } }) - console.log(detaches.timestamp.strTree()) + console.log(maxHead) if(prunable) { - this.discard_attestation(detaches.timestamp) + this.discard_attestation(detaches.timestamp, maxHead) + this.pruneTree(detaches.timestamp) console.log(detaches.timestamp.strTree()) process.exit(1) return new Promise((resolve, reject) => { resolve(detaches) }) } else { - return new Promise((resolve, reject) => { reject(new Error('No changes')) }) + return new Promise((resolve, reject) => { reject(new Error('There are no Bitcoin Attestation in the file')) }) } }, From 3c67f231af7bd3d141a1049bbce2030a23b01f29 Mon Sep 17 00:00:00 2001 From: =Stefano Fedeli <=> Date: Wed, 30 Oct 2019 17:22:32 +0100 Subject: [PATCH 5/5] Final Prune Commit Fixed syntax --- ots-cli.js | 19 +++++++------- src/calendar.js | 1 + src/open-timestamps.js | 57 ++++++++++++++++++++++-------------------- src/timestamp.js | 10 -------- test/calendar.js | 1 + 5 files changed, 42 insertions(+), 46 deletions(-) diff --git a/ots-cli.js b/ots-cli.js index e68dbc8..b5958e0 100644 --- a/ots-cli.js +++ b/ots-cli.js @@ -44,18 +44,18 @@ const infoCommand = program info(file, options) }) - const pruneCommand = program +const pruneCommand = program .command('prune [FILE_OTS]') .alias('p') .description('Prune timestamp.') .action((file, options) => { isExecuted = true if (!file) { - console.log(prugneCommand.helpInformation()) + console.log(pruneCommand.helpInformation()) return } options = parseCommon(options) - prune(file,options) + prune(file, options) }) const stampCommand = program @@ -199,7 +199,7 @@ function info (argsFileOts, options) { }) } -function prune(argsFileOts,options) { +function prune (argsFileOts, options) { const files = [] files.push(Utils.readFilePromise(argsFileOts, null)) Promise.all(files).then(values => { @@ -220,12 +220,13 @@ function prune(argsFileOts,options) { } // Opentimestamps prune OpenTimestamps.prune(detachedOts, options) - .then(function(results){ - fs.renameSync(argsFileOts,argsFileOts+".bak") - fs.writeFile(argsFileOts, Buffer.from(detachedOts.serializeToBytes()), 'binary',err =>{}) - console.log("Recepit Pruned") + .then(function (results) { + // Backup the file and create the new receipt + fs.renameSync(argsFileOts, argsFileOts + '.bak') + fs.writeFile(argsFileOts, Buffer.from(detachedOts.serializeToBytes()), 'binary', err => { if (err) { console.log('Error in serialization') } }) + console.log('Recepit Pruned') }).catch(err => { - console.log("Is not possible to prune due to " + err) + console.log('Is not possible to prune due to ' + err) }) }).catch(err => { if (err.code === 'ENOENT') { diff --git a/src/calendar.js b/src/calendar.js index 8d771e8..f5e7461 100644 --- a/src/calendar.js +++ b/src/calendar.js @@ -20,6 +20,7 @@ const Message = require('bitcore-message') const Utils = require('./utils.js') const Context = require('./context.js') const Timestamp = require('./timestamp.js') +const { URL } = require('url') /* Errors */ const CommitmentNotFoundError = Error.extend('CommitmentNotFoundError') diff --git a/src/open-timestamps.js b/src/open-timestamps.js index c182063..c86a6d4 100644 --- a/src/open-timestamps.js +++ b/src/open-timestamps.js @@ -97,33 +97,37 @@ module.exports = { return JSON.stringify(json) }, - - pruneTree(timestamp) { - let prun = timestamp.attestations.length == 0 + /* + * Delete all the dead branches + */ + pruneTree (timestamp) { + let prun = timestamp.attestations.length === 0 let changed = false - for(const element of timestamp.ops) { + for (const element of timestamp.ops) { var result = this.pruneTree(element[1]) - var stamp_prunable = result.prunable - var stamp_changed = result.change - changed = changed || stamp_changed || stamp_prunable - if(stamp_prunable) { + var stampPrunable = result.prunable + var stampChanged = result.change + changed = changed || stampChanged || stampPrunable + if (stampPrunable) { timestamp.ops.delete(element[0]) } else { prun = false } } - return {prunable : prun, change: changed} - + return { prunable: prun, change: changed } }, - discard_attestation(timestamp, saveOnly) { + /* + * Delete all the leaves which didn't match the height passed + */ + discard_attestation (timestamp, heightToKeep) { timestamp.getAttestations().forEach(opStamp => { - if(!(opStamp instanceof Notary.BitcoinBlockHeaderAttestation && opStamp.height == saveOnly) ) { - timestamp.attestations.splice(timestamp.attestations.indexOf(opStamp),1) + if (!(opStamp instanceof Notary.BitcoinBlockHeaderAttestation && opStamp.height === heightToKeep)) { + timestamp.attestations.splice(timestamp.attestations.indexOf(opStamp), 1) } }) - var iter = timestamp.ops.values() + var iter = timestamp.ops.values() let res = iter.next() while (!res.done) { this.discard_attestation(res.value) @@ -131,32 +135,31 @@ module.exports = { } }, /** - * Prune a timestamp dropping the redundant data included in the ots receipt, storing only the linear proof from the file hash to the best attestation. + * Prune a timestamp dropping the redundant data included in the ots receipt, storing only the linear proof from the file hash to the best attestation. + * @exports OpenTimestamps/prune + * @param {DetachedTimestampFile[]} detaches - The array of detached file to stamp; input/output parameter. + * @param {Object} options - The option arguments. Not used yet */ - prune(detaches, options = {}) { + prune (detaches, options = {}) { let prunable = false - let maxHead = 0; + let maxHead = 0 - if(detaches.timestamp.getAttestations().length <= 1) { + if (detaches.timestamp.getAttestations().length <= 1) { return new Promise((resolve, reject) => { reject(new Error('Just one attestation founded')) }) - } + } detaches.timestamp.getAttestations().forEach(subStamp => { - if(subStamp instanceof Notary.BitcoinBlockHeaderAttestation) { - prunable = true; - maxHead = (maxHead < subStamp.height)? subStamp.height : maxHead + if (subStamp instanceof Notary.BitcoinBlockHeaderAttestation) { + prunable = true + maxHead = (maxHead < subStamp.height) ? subStamp.height : maxHead } }) - console.log(maxHead) - if(prunable) { + if (prunable) { this.discard_attestation(detaches.timestamp, maxHead) this.pruneTree(detaches.timestamp) - console.log(detaches.timestamp.strTree()) - process.exit(1) return new Promise((resolve, reject) => { resolve(detaches) }) } else { return new Promise((resolve, reject) => { reject(new Error('There are no Bitcoin Attestation in the file')) }) } - }, /** diff --git a/src/timestamp.js b/src/timestamp.js index 1bf86a0..175a373 100644 --- a/src/timestamp.js +++ b/src/timestamp.js @@ -84,16 +84,6 @@ class Timestamp { return self } - delAttestation(clone_to_del) { - console.log("Recieved " + clone_to_del) - console.log(this.attestations) - var index = this.attestations.indexOf(clone_to_del) - console.log("I'm going to delete " + index) - this.attestations.splice(index,1) - console.log("Done! " + this.attestations) - } - - /** * Create a Serialize object. * @param {StreamSerializationContext} ctx - The stream serialization context. diff --git a/test/calendar.js b/test/calendar.js index 9a34019..36e6c14 100644 --- a/test/calendar.js +++ b/test/calendar.js @@ -1,6 +1,7 @@ const test = require('tape') const Calendar = require('../src/calendar.js') const Utils = require('../src/utils.js') +const { URL } = require('url') test('Calendar.privateSubmit()', assert => { const digest = Array.from(Utils.randBytes(32))