From 0affd45df6bc1d99438275797bb192eceae276a8 Mon Sep 17 00:00:00 2001 From: Vasco Santos Date: Mon, 15 Jun 2020 17:47:58 +0200 Subject: [PATCH] feat: record interface --- src/record/README.md | 75 +++++++++++++++++++++++++++++++++++++++ src/record/index.js | 35 ++++++++++++++++++ src/record/tests/index.js | 35 ++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 src/record/README.md create mode 100644 src/record/index.js create mode 100644 src/record/tests/index.js diff --git a/src/record/README.md b/src/record/README.md new file mode 100644 index 000000000..4f13d27f9 --- /dev/null +++ b/src/record/README.md @@ -0,0 +1,75 @@ +interface-record +================== + +A libp2p node needs to store data in a public location (e.g. a DHT), or rely on potentially untrustworthy intermediaries to relay information. Libp2p provides an all-purpose data container called **envelope**, which includes a signature of the data, so that it its authenticity can be verified. + +The record represents the data that will be stored inside the **envelope** when distributing records across the network. The `interface-record` aims to guarantee that any type of record created is compliant with the libp2p **envelope**. + +Taking into account that a record might be used in different contexts, an **envelope** signature made for a specific purpose **must not** be considered valid for a different purpose. Accordingly, each record has a short and descriptive string representing the record use case, known as **domain**. The data to be signed will be prepended with the domain string, in order to create a domain signature. + +A record can also contain a Buffer codec (ideally registered as a [multicodec](https://github.com/multiformats/multicodec)). This codec will prefix the record data in the **envelope** , so that it can be deserialized deterministically. + +## Usage + +```js +const tests = require('libp2p-interfaces/src/record/tests') +describe('your record', () => { + tests({ + async setup () { + return YourRecord + }, + async teardown () { + // cleanup resources created by setup() + } + }) +}) +``` + +## Create Record + +```js +const multicodec = require('multicodec') +const Record = require('libp2p-interfaces/src/record') +// const Protobuf = require('./record.proto') + +const ENVELOPE_DOMAIN_PEER_RECORD = 'libp2p-peer-record' +const ENVELOPE_PAYLOAD_TYPE_PEER_RECORD = Buffer.from('0301', 'hex') + +class PeerRecord extends Record { + constructor (peerId, multiaddrs, seqNumber) { + super (ENVELOPE_DOMAIN_PEER_RECORD, ENVELOPE_PAYLOAD_TYPE_PEER_RECORD) + } + + marshal () { + // Implement and return using Protobuf + } + + isEqual (other) { + // Verify + } +} +``` + +## API + +### marshal + +- `record.marshal()` + +Marshal a record to be used in a libp2p envelope. + +**Returns** + +It returns a `Protobuf` containing the record data. + +### isEqual + +- `record.isEqual(other)` + +Verifies if the other Record is identical to this one. + +**Parameters** +- other is a `Record` to compare with the current instance. + +**Returns** +- `boolean` diff --git a/src/record/index.js b/src/record/index.js new file mode 100644 index 000000000..463c8613d --- /dev/null +++ b/src/record/index.js @@ -0,0 +1,35 @@ +'use strict' + +const errcode = require('err-code') + +/** + * Record is the base implementation of a record that can be used as the payload of a libp2p envelope. + */ +class Record { + /** + * @constructor + * @param {String} domain signature domain + * @param {Buffer} codec identifier of the type of record + */ + constructor(domain, codec) { + this.domain = domain + this.codec = codec + } + + /** + * Marshal a record to be used in an envelope. + */ + marshal () { + throw errcode(new Error('marshal must be implemented by the subclass'), 'ERR_NOT_IMPLEMENTED') + } + + /** + * Verifies if the other provided Record is identical to this one. + * @param {Record} other + */ + isEqual (other) { + throw errcode(new Error('isEqual must be implemented by the subclass'), 'ERR_NOT_IMPLEMENTED') + } +} + +module.exports = Record diff --git a/src/record/tests/index.js b/src/record/tests/index.js new file mode 100644 index 000000000..6f70954b6 --- /dev/null +++ b/src/record/tests/index.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ + +'use strict' + +const chai = require('chai') +const expect = chai.expect +chai.use(require('dirty-chai')) + +module.exports = (test) => { + describe('record', () => { + let record + + beforeEach(async () => { + record = await test.setup() + if (!record) throw new Error('missing record') + }) + + afterEach(() => test.teardown()) + + it('has domain and codec', () => { + expect(record.domain).to.exist() + expect(record.codec).to.exist() + }) + + it('is able to marshal', () => { + const rawData = record.marshal() + expect(Buffer.isBuffer(rawData)).to.eql(true) + }) + + it('is able to compare two records', () => { + const isEqual = record.isEqual(record) + expect(isEqual).to.eql(true) + }) + }) +} \ No newline at end of file