From 02347d134aaa12793691f334ea844d578fe287c3 Mon Sep 17 00:00:00 2001 From: ansuz Date: Thu, 30 Apr 2020 18:18:05 -0400 Subject: [PATCH] queue signature validation per-channel to ensure correct ordering --- lib/historyKeeper.js | 1 + lib/hk-util.js | 37 ++++++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/lib/historyKeeper.js b/lib/historyKeeper.js index aa320f4b6..7e45faf08 100644 --- a/lib/historyKeeper.js +++ b/lib/historyKeeper.js @@ -38,6 +38,7 @@ module.exports.create = function (config, cb) { channel_cache: {}, queueStorage: WriteQueue(), queueDeletes: WriteQueue(), + queueValidation: WriteQueue(), batchIndexReads: BatchRead("HK_GET_INDEX"), batchMetadata: BatchRead('GET_METADATA'), diff --git a/lib/hk-util.js b/lib/hk-util.js index 562114ec7..81984b300 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -859,23 +859,30 @@ HK.onChannelMessage = function (Env, Server, channel, msgStruct) { // trim the checkpoint indicator off the message if it's present let signedMsg = (isCp) ? msgStruct[4].replace(CHECKPOINT_PATTERN, '') : msgStruct[4]; - // convert the message from a base64 string into a Uint8Array - - //const txid = Util.uid(); // Listen for messages - //console.log(+new Date(), "Send verification request"); - Env.validateMessage(signedMsg, metadata.validateKey, w(function (err) { - // no errors means success - if (!err) { return; } - // validation can fail in multiple ways - if (err === 'FAILED') { - // we log this case, but not others for some reason - Log.info("HK_SIGNED_MESSAGE_REJECTED", 'Channel '+channel.id); - } - // always abort if there was an error... - return void w.abort(); - })); + /* queueing this helps avoid race conditions in which workers + validate and write messages in a different order than they were received. + For best effect the validate and store should actually be queued atomically, + but this is a step in the right direction. + */ + var proceed = w(); + Env.queueValidation(channel.id, function (next) { + Env.validateMessage(signedMsg, metadata.validateKey, function (err) { + // always go on to the next item in the queue regardless of the outcome + next(); + + // no errors means success + if (!err) { return proceed(); } + // validation can fail in multiple ways + if (err === 'FAILED') { + // we log this case, but not others for some reason + Log.info("HK_SIGNED_MESSAGE_REJECTED", 'Channel '+channel.id); + } + // always abort if there was an error... + return void w.abort(); + }); + }); }).nThen(function () { // do checkpoint stuff...