History stream errors for encrypted hypercores
Closed this issue · 6 comments
This one is puzzling, to say the least. at the end of this issue, you can find an isolated test to replicate it.
NOTE: the same errors occur when using this PR of Hypercore.
none of the following issues occur if you comment line 9
to disable encryption.
What I am trying to achieve
Listen to a Hyperbee's feed, and log the changed key whenever the feed emits append
event:
clone.on('append', async () => {
// Check that the clone is setup correctly
console.log({
name,
valid:
cloneDB.feed.peers.length > 0 &&
(encryptionKey ? !!cloneDB.feed.encryption : true),
});
const stream = cloneDB.createHistoryStream({
reverse: true,
limit: 1,
});
stream?.on('data', (data) => console.log('Data at: ' + name, data));
stream?.on('error', (err) =>
console.log('Error at: ' + name + ':\n', err),
);
});
What happens
There are two cases:
1- Replicating an encrypted Hyperbee and creating a HistroyStream whenever the core
emits append
.
Two errors are thrown then it works fine :
{ name: 'ONE', valid: true }
{ name: 'ONE', valid: true }
Error at: ONE:
TypeError: Cannot read properties of undefined (reading 'keys')
at BlockEntry.getTreeNode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:253:37)
at HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:50)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HistoryIterator.open (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/iterators/history.js:16:5)
Error at: ONE:
Error: Groups are not supported
at exports.skip (/Users/ward/code/play/hyperbee-tests/node_modules/protocol-buffers-encodings/index.js:29:13)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/lib/messages.js:469:18)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:416:21)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:432:14)
at Hypercore._decode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:789:23)
at Hypercore._decryptAndDecode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:630:24)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HyperBee.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:354:21)
at async Batch.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:525:9)
at async HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:13)
{ name: 'ONE', valid: true }
Data at: ONE { type: 'put', seq: 2, key: 'foo', value: 'update number: 1' }
{ name: 'ONE', valid: true }
Data at: ONE { type: 'put', seq: 3, key: 'foo', value: 'update number: 2' }
2- It gets worse if I try to do the same for two Hyperbees from the same Hypercore (literally the same object or two sessions of the same core), all logs are errors, here is an example:
{ name: 'ONE', valid: true }
{ name: 'TWO', valid: true }
{ name: 'ONE', valid: true }
{ name: 'TWO', valid: true }
Error at: TWO:
RangeError: Could not decode varint
at Object.read [as decode] (/Users/ward/code/play/hyperbee-tests/node_modules/varint/decode.js:17:13)
at exports.skip (/Users/ward/code/play/hyperbee-tests/node_modules/protocol-buffers-encodings/index.js:24:24)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/lib/messages.js:232:18)
at new Pointers (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:36:29)
at inflate (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:67:10)
at BlockEntry.getTreeNode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:249:20)
at HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:50)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HistoryIterator.open (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/iterators/history.js:16:5)
Error at: ONE:
Error: Groups are not supported
at exports.skip (/Users/ward/code/play/hyperbee-tests/node_modules/protocol-buffers-encodings/index.js:29:13)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/lib/messages.js:469:18)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:416:21)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:432:14)
at Hypercore._decode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:789:23)
at Hypercore._decryptAndDecode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:630:24)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HyperBee.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:354:21)
at async Batch.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:525:9)
at async HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:13)
Error at: ONE:
RangeError: Could not decode varint
at Object.read [as decode] (/Users/ward/code/play/hyperbee-tests/node_modules/varint/decode.js:17:13)
at exports.skip (/Users/ward/code/play/hyperbee-tests/node_modules/protocol-buffers-encodings/index.js:24:24)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/lib/messages.js:232:18)
at new Pointers (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:36:29)
at inflate (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:67:10)
at BlockEntry.getTreeNode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:249:20)
at HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:50)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HistoryIterator.open (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/iterators/history.js:16:5)
Error at: TWO:
Error: Groups are not supported
at exports.skip (/Users/ward/code/play/hyperbee-tests/node_modules/protocol-buffers-encodings/index.js:29:13)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/lib/messages.js:469:18)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:416:21)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:432:14)
at Hypercore._decode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:789:23)
at Hypercore._decryptAndDecode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:630:24)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HyperBee.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:354:21)
at async Batch.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:525:9)
at async HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:13)
{ name: 'ONE', valid: true }
{ name: 'TWO', valid: true }
Error at: TWO:
TypeError: Cannot read properties of undefined (reading 'keys')
at BlockEntry.getTreeNode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:253:37)
at HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:50)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HistoryIterator.open (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/iterators/history.js:16:5)
Error at: ONE:
Error: Groups are not supported
at exports.skip (/Users/ward/code/play/hyperbee-tests/node_modules/protocol-buffers-encodings/index.js:29:13)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/lib/messages.js:469:18)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:416:21)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:432:14)
at Hypercore._decode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:789:23)
at Hypercore._decryptAndDecode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:630:24)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HyperBee.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:354:21)
at async Batch.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:525:9)
at async HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:13)
{ name: 'ONE', valid: true }
{ name: 'TWO', valid: true }
Error at: TWO:
TypeError: Cannot read properties of undefined (reading 'keys')
at BlockEntry.getTreeNode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:253:37)
at HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:50)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HistoryIterator.open (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/iterators/history.js:16:5)
Error at: ONE:
Error: Decoded message is not valid
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/lib/messages.js:446:39)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:416:21)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:432:14)
at Hypercore._decode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:789:23)
at Hypercore._decryptAndDecode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:630:24)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HyperBee.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:354:21)
at async Batch.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:525:9)
at async HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:13)
at async HistoryIterator.open (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/iterators/history.js:16:5)
{ name: 'ONE', valid: true }
{ name: 'TWO', valid: true }
Error at: TWO:
Error: Unknown wire type: 7
at exports.skip (/Users/ward/code/play/hyperbee-tests/node_modules/protocol-buffers-encodings/index.js:35:9)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/lib/messages.js:232:18)
at new Pointers (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:36:29)
at inflate (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:67:10)
at BlockEntry.getTreeNode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:249:20)
at HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:50)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HistoryIterator.open (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/iterators/history.js:16:5)
Error at: ONE:
Error: Unknown wire type: 6
at exports.skip (/Users/ward/code/play/hyperbee-tests/node_modules/protocol-buffers-encodings/index.js:35:9)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/lib/messages.js:469:18)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:416:21)
at Object.decode (/Users/ward/code/play/hyperbee-tests/node_modules/compact-encoding/index.js:432:14)
at Hypercore._decode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:789:23)
at Hypercore._decryptAndDecode (/Users/ward/code/play/hyperbee-tests/node_modules/hypercore/index.js:630:24)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async HyperBee.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:354:21)
at async Batch.getBlock (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:525:9)
at async HyperBee.getRoot (/Users/ward/code/play/hyperbee-tests/node_modules/hyperbee/index.js:342:13)
I am happy to offer any more context if needed.
Here is the code to replicate this bug:
import Corestore from 'corestore';
import Hyperbee from 'hyperbee';
import RAM from 'random-access-memory';
let key;
let originStore;
let encryptionKey;
// Commenting next line will resolve all issues
encryptionKey = Buffer.alloc(32, 'hello world');
{
const store = new Corestore(RAM);
const origin = store.get({
name: 'origin',
encryptionKey,
});
await origin.ready();
const originDB = new Hyperbee(origin, {
keyEncoding: 'utf8',
valueEncoding: 'utf8',
});
let count = 0;
setInterval(() => originDB.put('foo', 'update number: ' + count++), 1000);
key = origin.key;
originStore = store;
}
{
const store = new Corestore(RAM);
const s = store.replicate(true);
s.pipe(originStore.replicate(false)).pipe(s);
await test('ONE');
// Try commenting next line
await test('TWO');
async function test(name) {
const clone = store.get({
key: key,
encryptionKey,
});
await clone.update();
const cloneDB = new Hyperbee(clone, {
keyEncoding: 'utf8',
valueEncoding: 'utf8',
});
clone.on('append', async () => {
// Check that the clone is setup correctly
console.log({
name,
valid:
cloneDB.feed.peers.length > 0 &&
(encryptionKey ? !!cloneDB.feed.encryption : true),
});
const stream = cloneDB.createHistoryStream({
reverse: true,
limit: 1,
});
stream?.on('data', (data) => console.log('Data at: ' + name, data));
stream?.on('error', (err) =>
console.log('Error at: ' + name + ':\n', err),
);
});
}
}
package.json
{
"type": "module",
"dependencies": {
"corestore": "^6.0.1-alpha.17",
"hyperbee": "^1.8.3",
"random-access-memory": "^4.1.0"
}
}
Doesn't look like a Hyperbee bug, seems it's just getting encrypted blocks (due to some bug) which makes Hyperbee throw (as it should)
That makes sense, but I made sure that I check the validity of Hypercores every time in on('append')
and the encryption seems to be fine. Moreover, catching these errors at #53, resulted in the correct behavior, meaning, that it didn't stop the decrypted blocks and valid history to be fetched correctly.
If you know where should I look next I would be happy to follow that lead instead of #53
Having multiple, inflight replicator requests for the same encrypted block results in a bug as the block is then decrypted multiple times: https://github.com/hypercore-protocol/hypercore-next/blob/b113b082e087f0ea796e7dc163862ddf3a5d61dc/test/encryption.js#L173-L186
@kasperisager nice catch, can you capture that in a hypercore test?
@mafintosh That is a Hypercore test 😄 Opened a draft PR as well: hypercore-protocol/hypercore-next#105
The example code works on my end with the changes from the above PR ☝️