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

[WIP] Bulk append validation #17

Closed
wants to merge 7 commits into from
Closed
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
129 changes: 129 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,32 @@ exports.checkInvalid = function (state, hmac_key, msg) {
return false //not invalid
}

exports.checkInvalidBulk = function (state, hmac_key, messages) {

// todo: validate all authors are ourself

for (var i = 0; i < messages.length; i++) {
var message = messages[i].message

if(!ref.isFeedId(message.author))
return new Error('invalid message: must have author')
if(!isSigMatchesCurve(message))
return new Error('invalid message: signature type must match author type')

if(!isValidOrder(message, true))
return fatal(new Error('message must have keys in allowed order'))

var invalidShape = isInvalidShape(message)

if (invalidShape) return invalidShape

if(!ssbKeys.verifyObj({public: message.author.substring(1)}, hmac_key, message))
return fatal(new Error('invalid signature'))

}

}

/*
{
//an array of messages which have been validated, but not written to the database yet.
Expand Down Expand Up @@ -240,6 +266,53 @@ exports.append = function (state, hmac_key, msg) {
return exports.appendKVT(state, hmac_key, exports.toKeyValueTimestamp(msg))
}

exports.appendBulk = function(state, hmac_key, messages) {
var err = exports.checkInvalidBulk(state, hmac_key, messages)

if (err) throw err;

var kvtMessages = messages.map(function (msg) {
return exports.toKeyValueTimestamp(msg.message, msg.id)
});

var firstMessage = kvtMessages[0];
var lastMessage = kvtMessages[kvtMessages.length - 1]

var msgAuthor = firstMessage.value.author;
var lowestSequence = firstMessage.value.sequence

var highestSequence = lastMessage.value.sequence
var lastMessageId = lastMessage.value.key

var timestamp = lastMessage.value.timestamp

// Dequeue anything on the per-feed queue to the main queue before making the new write
if (state.feeds[msgAuthor]) {
var a = state.feeds[msgAuthor]
a.id = lastMessageId
a.sequence = highestSequence
a.timestamp = timestamp
var q = state.feeds[msgAuthor].queue
state.validated += q.length
state.queued -= q.length
for (var i = 0; i < q.length; ++i)
state.queue.push(q[i])
q = []
} else if (lowestSequence === 1) {
state.feeds[msgAuthor] = {
id: lastMessageId,
sequence: highestSequence,
timestamp: timestamp,
queue: []
}
}

state.queue.push(kvtMessages)
state.validated += 1

return state
}

exports.validate = function (state, hmac_key, feed) {
if(!state.feeds[feed] || !state.feeds[feed].queue.length) {
return state
Expand All @@ -251,6 +324,7 @@ exports.validate = function (state, hmac_key, feed) {

//pass in your own timestamp, so it's completely deterministic
exports.create = function (state, keys, hmac_key, content, timestamp) {

if(timestamp == null || isNaN(+timestamp)) throw new Error('timestamp must be provided')

if(!isObject(content) && !isEncrypted(content))
Expand All @@ -274,6 +348,61 @@ exports.create = function (state, keys, hmac_key, content, timestamp) {
return ssbKeys.signObj(keys, hmac_key, msg)
}

exports.createAll = function (state, keys, hmac_key, messages) {

// The 'previous' for the first message in the bulk append will be the
// message currently at the head of the log or null if this is the first
// append
var previous = state ? state.id : null

// The first sequence number is the head of the log's sequence number + 1, or 1 if this is the first
// append
var nextSequenceNumber = state ? state.sequence + 1 : 1
var result = [];

messages.forEach(function (message, idx) {
var content = message.content
var timestamp = message.timestamp

// todo: Validate timestamps are increasing

if(!isObject(content) && !isEncrypted(content)) {
throw new Error('invalid message content, must be object or encrypted string')
}

var msg = {
previous: previous,
sequence: nextSequenceNumber,
author: keys.id,
timestamp: +timestamp,
hash: 'sha256',
content: content
}

var err = isInvalidShape(msg)
if(err) throw err

// We need to sign the message this stage to know what its ID is to use it as the 'previous'
// for the next message
var signedMsg = ssbKeys.signObj(keys, hmac_key, msg);

var msgId = exports.id(signedMsg)
nextSequenceNumber = nextSequenceNumber + 1

// The next 'previous' for the next message in the bulk append will be this message
// once the message has been appended
previous = msgId

// We return the ID with the signed message as well as the ID so we don't have to compute it again
result.push({
message: signedMsg,
id: msgId
})
})

return result;
}

exports.id = function (msg) {
return '%'+ssbKeys.hash(JSON.stringify(msg, null, 2))
}
Expand Down