Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Prune command #69

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions ots-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(pruneCommand.helpInformation())
return
}
options = parseCommon(options)
prune(file, options)
})

const stampCommand = program
.command('stamp [FILE...]')
.alias('s')
Expand Down Expand Up @@ -185,6 +199,45 @@ 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
OpenTimestamps.prune(detachedOts, options)
.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)
})
}).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 = []
Expand Down
1 change: 1 addition & 0 deletions src/calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
65 changes: 65 additions & 0 deletions src/open-timestamps.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,71 @@ module.exports = {
return JSON.stringify(json)
},

/*
* Delete all the dead branches
*/
pruneTree (timestamp) {
let prun = timestamp.attestations.length === 0
let changed = false

for (const element of timestamp.ops) {
var result = this.pruneTree(element[1])
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 }
},

/*
* Delete all the leaves which didn't match the height passed
*/
discard_attestation (timestamp, heightToKeep) {
timestamp.getAttestations().forEach(opStamp => {
Copy link
Member

@lvaccaro lvaccaro Jan 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAttestations() returns a Set of every attestations walking the timestamp tree until the leaf. I think in this case, you need to check only the current attestations as: timestamp.attestations

if (!(opStamp instanceof Notary.BitcoinBlockHeaderAttestation && opStamp.height === heightToKeep)) {
timestamp.attestations.splice(timestamp.attestations.indexOf(opStamp), 1)
}
})
var iter = timestamp.ops.values()
let res = iter.next()
while (!res.done) {
this.discard_attestation(res.value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add the heightToKeep param

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.
* @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 = {}) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to update the prune prototype in order to take one DetachedTimestampFile at time, instead an array of file (following the PR's code you are just handle 1 file at time).

let prunable = false
let maxHead = 0

if (detaches.timestamp.getAttestations().length <= 1) {
Copy link
Member

@lvaccaro lvaccaro Jan 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

count only BitcoinBlockHeaderAttestation, because usually there is at least one PendingAttestation that shouldn't be counted

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 (prunable) {
this.discard_attestation(detaches.timestamp, maxHead)
this.pruneTree(detaches.timestamp)
return new Promise((resolve, reject) => { resolve(detaches) })
} else {
return new Promise((resolve, reject) => { reject(new Error('There are no Bitcoin Attestation in the file')) })
}
},

/**
* Create timestamp with the aid of a remote calendar for one or multiple files.
* @exports OpenTimestamps/stamp
Expand Down
1 change: 1 addition & 0 deletions test/calendar.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const test = require('tape')
const Calendar = require('../src/calendar.js')
const Utils = require('../src/utils.js')
const { URL } = require('url')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is auto merging stuff that add URL here and in the src/calendar.js file


test('Calendar.privateSubmit()', assert => {
const digest = Array.from(Utils.randBytes(32))
Expand Down