broadcast-encryption 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -0
- package/index.js +291 -0
- package/package.json +42 -0
- package/spec/broadcast-encryption/index.js +165 -0
- package/spec/broadcast-encryption/schema.json +84 -0
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# broadcast-encryption
|
|
2
|
+
|
|
3
|
+
Distribute encryption keys to a dynamic set of receivers
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
const core = new Hypercore(storage)
|
|
9
|
+
const broadcast = new BroadcastEncryption(core, { keyPair })
|
|
10
|
+
|
|
11
|
+
const encryptionKey = Buffer.alloc(32)
|
|
12
|
+
|
|
13
|
+
// distribute key to everyone
|
|
14
|
+
await broadcast.update(encryptionKey, [peer1, peer2, peer3])
|
|
15
|
+
|
|
16
|
+
encryptionKey.fill(0xff) // update encryption key
|
|
17
|
+
|
|
18
|
+
// distribute new key to everyone except peer3
|
|
19
|
+
await broadcast.update(encryptionKey, [peer1, peer2])
|
|
20
|
+
|
|
21
|
+
await broadcast.get(id) // get the encryption key corresponding to id
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## API
|
|
25
|
+
|
|
26
|
+
#### `const broadcast = new BroadcastEncryption(core, opts)`
|
|
27
|
+
|
|
28
|
+
Instantiate a new broadcast instance.
|
|
29
|
+
|
|
30
|
+
`opts` include:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
{
|
|
34
|
+
keyPair, // receiver key pair used for decryption
|
|
35
|
+
bootstrap // initial key information to use
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### `const current = broadcast.id()`
|
|
40
|
+
|
|
41
|
+
The current encryption key id.
|
|
42
|
+
|
|
43
|
+
#### `await broadcast.update(key, recipients)`
|
|
44
|
+
|
|
45
|
+
Distribute the updated `key` to all members of `recipients`.
|
|
46
|
+
|
|
47
|
+
#### `const { id, encryptionKey } = await broadcast.get(id)`
|
|
48
|
+
|
|
49
|
+
Get the encryption key corresponding to `id`.
|
|
50
|
+
|
|
51
|
+
If `id` is passed as `-1`, the latest encryption key shall be returned.
|
|
52
|
+
|
|
53
|
+
#### `const encryption = await broadcast.createEncryptionProvider(opts)`
|
|
54
|
+
|
|
55
|
+
Create a [`HypercoreEncryption`](https://github.com/holepunchto/hypercore-encryption) provider to pass to a hypercore.
|
|
56
|
+
|
|
57
|
+
#### `broadcast.on('update', (id) => {})`
|
|
58
|
+
|
|
59
|
+
An `update` event is emitted when a new encryption key is loaded.
|
|
60
|
+
|
|
61
|
+
#### `const payload = BroadcastEncryption.encrypt(data, recipients)`
|
|
62
|
+
|
|
63
|
+
Static helper to broadcast encrypt a message.
|
|
64
|
+
|
|
65
|
+
#### `const data = BroadcastEncryption.decrypt(payload, recipientSecretKey)`
|
|
66
|
+
|
|
67
|
+
Static helper to decrypt a broadcast encrypted message.
|
|
68
|
+
|
|
69
|
+
#### `const payload = BroadcastEncryption.verify(payload, data, recipients)`
|
|
70
|
+
|
|
71
|
+
Static helper to verify a `payload`. Returns true if all members on `recipients` can decrypt `data`.
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
Apache-2.0
|
package/index.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
const sodium = require('sodium-universal')
|
|
2
|
+
const ReadyResource = require('ready-resource')
|
|
3
|
+
const crypto = require('hypercore-crypto')
|
|
4
|
+
const c = require('compact-encoding')
|
|
5
|
+
const safetyCatch = require('safety-catch')
|
|
6
|
+
const b4a = require('b4a')
|
|
7
|
+
const rrp = require('resolve-reject-promise')
|
|
8
|
+
|
|
9
|
+
const schema = require('./spec/broadcast-encryption')
|
|
10
|
+
|
|
11
|
+
const [DEFAULT_NAMESPACE, GENESIS_ENTROPY, NS_NONCE, NS_KEYPAIR_SEED, NS_SYMMETRIC_NONCE] =
|
|
12
|
+
crypto.namespace('broadcast-encryption', 5)
|
|
13
|
+
|
|
14
|
+
// ephemeral state
|
|
15
|
+
const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
|
|
16
|
+
const hash = nonce.subarray(0, sodium.crypto_generichash_BYTES_MIN)
|
|
17
|
+
const secretKey = b4a.alloc(sodium.crypto_box_SECRETKEYBYTES)
|
|
18
|
+
const publicKey = b4a.alloc(sodium.crypto_box_PUBLICKEYBYTES)
|
|
19
|
+
const recipientKey = b4a.alloc(sodium.crypto_box_PUBLICKEYBYTES)
|
|
20
|
+
const symmetricKey = b4a.alloc(sodium.crypto_secretbox_KEYBYTES)
|
|
21
|
+
|
|
22
|
+
const BroadcastPayload = schema.resolveStruct('@broadcast/payload')
|
|
23
|
+
const BroadcastMessage = schema.resolveStruct('@broadcast/message')
|
|
24
|
+
|
|
25
|
+
module.exports = class BroadcastEncryption extends ReadyResource {
|
|
26
|
+
constructor(core, opts = {}) {
|
|
27
|
+
super()
|
|
28
|
+
|
|
29
|
+
this.core = core
|
|
30
|
+
this.keyPair = opts.keyPair || null
|
|
31
|
+
|
|
32
|
+
this._bootstrap = opts.bootstrap || null
|
|
33
|
+
this._latest = 0
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async _open() {
|
|
37
|
+
await this.core.ready()
|
|
38
|
+
|
|
39
|
+
this.core.on('append', this.refresh.bind(this))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
_close() {
|
|
43
|
+
return this.core.close()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
id() {
|
|
47
|
+
return this.core ? this.core.length : 0
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async refresh() {
|
|
51
|
+
let key = null
|
|
52
|
+
try {
|
|
53
|
+
key = await this._getLatestKey()
|
|
54
|
+
} catch (err) {
|
|
55
|
+
safetyCatch(err)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!key || key.id < this._latest) return
|
|
59
|
+
|
|
60
|
+
this._latest = key.id
|
|
61
|
+
this.emit('update', key.id)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async append(payload) {
|
|
65
|
+
await this._append({ payload, pointer: null })
|
|
66
|
+
const id = this.core.length
|
|
67
|
+
|
|
68
|
+
await this.point(id - 2) // point to previous key
|
|
69
|
+
|
|
70
|
+
return id
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async update(key, recipients) {
|
|
74
|
+
const payload = await BroadcastEncryption.encrypt(key, recipients)
|
|
75
|
+
return this.append(payload)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async point(to) {
|
|
79
|
+
// first pointer is null to maintain offset
|
|
80
|
+
if (to < 0) {
|
|
81
|
+
return this._append({ pointer: null, payload: null })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const [old, current] = await Promise.all([this.get(to), this._getLatestKey()])
|
|
85
|
+
|
|
86
|
+
const buffer = encryptPointer(old.encryptionKey, current.encryptionKey, nonce)
|
|
87
|
+
const pointer = { to: old.id, from: current.id, nonce, buffer }
|
|
88
|
+
|
|
89
|
+
return this._append({ payload: null, pointer })
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async _get(index, opts) {
|
|
93
|
+
return this.core.get(index, { ...opts, valueEncoding: BroadcastMessage })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async _append({ pointer, payload }) {
|
|
97
|
+
return this.core.append(c.encode(BroadcastMessage, { version: 0, pointer, payload }))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async _getLatestKey(opts) {
|
|
101
|
+
let id = this.core.length
|
|
102
|
+
|
|
103
|
+
while (id > 0) {
|
|
104
|
+
const block = await this._get(id - 1, opts)
|
|
105
|
+
|
|
106
|
+
if (block && block.payload) {
|
|
107
|
+
// use bootstrap if we have it
|
|
108
|
+
if (this._bootstrap && this._bootstrap.id === id) {
|
|
109
|
+
return this._bootstrap
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const key = {
|
|
113
|
+
id,
|
|
114
|
+
encryptionKey: this._unpack(block.payload)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return key
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
id--
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { id: 0, encryptionKey: null }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async get(id, opts) {
|
|
127
|
+
if (id === -1) {
|
|
128
|
+
return this._getLatestKey()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (id === 0) {
|
|
132
|
+
return { id: 0, encryptionKey: null }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (this._bootstrap && this._bootstrap.id === id) {
|
|
136
|
+
return this._bootstrap
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const block = await this._get(id - 1, opts)
|
|
140
|
+
if (!block) return null // no key in core
|
|
141
|
+
|
|
142
|
+
let encryptionKey = null
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
encryptionKey = this._unpack(block.payload)
|
|
146
|
+
} catch (err) {
|
|
147
|
+
encryptionKey = await this._getByPointer(id)
|
|
148
|
+
if (!encryptionKey) throw err
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const key = { id, encryptionKey }
|
|
152
|
+
|
|
153
|
+
return key
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
bootstrap(key) {
|
|
157
|
+
if (!this._bootstrap || this._bootstrap.id < key.id) {
|
|
158
|
+
this._bootstrap = key
|
|
159
|
+
this.emit('update', key.id)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async getBootstrap() {
|
|
164
|
+
return this._getLatestKey()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async _getByPointer(target) {
|
|
168
|
+
const bootstrap = await this._getLatestKey()
|
|
169
|
+
if (!bootstrap || bootstrap.id < target) return null
|
|
170
|
+
|
|
171
|
+
let id = null
|
|
172
|
+
|
|
173
|
+
let seq = bootstrap.id
|
|
174
|
+
let key = bootstrap.encryptionKey
|
|
175
|
+
|
|
176
|
+
while (seq > target) {
|
|
177
|
+
const block = await this._get(seq--)
|
|
178
|
+
|
|
179
|
+
if (block.pointer === null) continue
|
|
180
|
+
|
|
181
|
+
id = block.pointer.to
|
|
182
|
+
key = decryptPointer(block.pointer.buffer, block.pointer.nonce, key)
|
|
183
|
+
|
|
184
|
+
if (key === null) return null
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return id === null ? null : key
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
_unpack(block) {
|
|
191
|
+
if (!this.keyPair) throw new Error('No key pair provided')
|
|
192
|
+
|
|
193
|
+
const encryptionKey = BroadcastEncryption.decrypt(block, this.keyPair.secretKey)
|
|
194
|
+
if (!encryptionKey) throw new Error('Broadcast decryption failed')
|
|
195
|
+
|
|
196
|
+
return encryptionKey
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
static PayloadEncoding = BroadcastPayload
|
|
200
|
+
|
|
201
|
+
static encrypt(data, recipients) {
|
|
202
|
+
const seed = crypto.hash([NS_KEYPAIR_SEED, data])
|
|
203
|
+
|
|
204
|
+
sodium.crypto_box_seed_keypair(publicKey, secretKey, seed)
|
|
205
|
+
sodium.crypto_generichash_batch(nonce, [NS_NONCE, publicKey])
|
|
206
|
+
|
|
207
|
+
const broadcast = {
|
|
208
|
+
publicKey,
|
|
209
|
+
payload: []
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
for (const recipient of recipients) {
|
|
213
|
+
if (recipient === null) continue
|
|
214
|
+
|
|
215
|
+
const enc = b4a.alloc(data.byteLength + sodium.crypto_box_MACBYTES)
|
|
216
|
+
|
|
217
|
+
sodium.crypto_sign_ed25519_pk_to_curve25519(recipientKey, recipient)
|
|
218
|
+
sodium.crypto_box_easy(enc, data, nonce, recipientKey, secretKey)
|
|
219
|
+
|
|
220
|
+
broadcast.payload.push(enc)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return broadcast
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
static decrypt(broadcast, recipientSecretKey) {
|
|
227
|
+
const { publicKey, payload } = broadcast
|
|
228
|
+
|
|
229
|
+
const data = b4a.alloc(payload[0].byteLength - sodium.crypto_box_MACBYTES)
|
|
230
|
+
|
|
231
|
+
sodium.crypto_generichash_batch(nonce, [NS_NONCE, publicKey])
|
|
232
|
+
sodium.crypto_sign_ed25519_sk_to_curve25519(secretKey, recipientSecretKey)
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
for (const ciphertext of payload) {
|
|
236
|
+
if (sodium.crypto_box_open_easy(data, ciphertext, nonce, publicKey, secretKey)) {
|
|
237
|
+
return data
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} finally {
|
|
241
|
+
b4a.fill(secretKey, 0)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return null
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
static verify(ciphertext, data, recipients) {
|
|
248
|
+
const seed = crypto.hash([NS_KEYPAIR_SEED, data])
|
|
249
|
+
|
|
250
|
+
sodium.crypto_box_seed_keypair(publicKey, secretKey, seed)
|
|
251
|
+
sodium.crypto_generichash_batch(nonce, [NS_NONCE, publicKey])
|
|
252
|
+
|
|
253
|
+
if (!b4a.equals(publicKey, ciphertext.publicKey)) return false
|
|
254
|
+
|
|
255
|
+
const expected = b4a.alloc(data.byteLength + sodium.crypto_box_MACBYTES)
|
|
256
|
+
|
|
257
|
+
for (const target of recipients) {
|
|
258
|
+
sodium.crypto_sign_ed25519_pk_to_curve25519(recipientKey, target)
|
|
259
|
+
sodium.crypto_box_easy(expected, data, nonce, recipientKey, secretKey)
|
|
260
|
+
|
|
261
|
+
let found = false
|
|
262
|
+
|
|
263
|
+
for (const buffer of ciphertext.payload) {
|
|
264
|
+
if (b4a.equals(buffer, expected)) {
|
|
265
|
+
found = true
|
|
266
|
+
break
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!found) return false
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return true
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function encryptPointer(to, from, nonce) {
|
|
278
|
+
const buffer = b4a.alloc(to.byteLength + sodium.crypto_secretbox_MACBYTES)
|
|
279
|
+
|
|
280
|
+
sodium.crypto_generichash_batch(nonce, [NS_SYMMETRIC_NONCE, to])
|
|
281
|
+
sodium.crypto_secretbox_easy(buffer, to, nonce, from)
|
|
282
|
+
|
|
283
|
+
return buffer
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function decryptPointer(data, nonce, key) {
|
|
287
|
+
const buffer = b4a.alloc(data.byteLength - sodium.crypto_secretbox_MACBYTES)
|
|
288
|
+
if (!sodium.crypto_secretbox_open_easy(buffer, data, nonce, key)) return null
|
|
289
|
+
|
|
290
|
+
return buffer
|
|
291
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "broadcast-encryption",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "Distribute encryption keys to a dynamic set of receivers",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js",
|
|
8
|
+
"spec/*"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"format": "prettier --write .",
|
|
12
|
+
"test": "prettier --check . && node test/*.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [],
|
|
15
|
+
"author": "Holepunch Inc.",
|
|
16
|
+
"license": "Apache-2.0",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"b4a": "^1.7.3",
|
|
19
|
+
"compact-encoding": "^2.17.0",
|
|
20
|
+
"hypercore": "^11.18.2",
|
|
21
|
+
"hyperschema": "^1.16.0",
|
|
22
|
+
"ready-resource": "^1.2.0",
|
|
23
|
+
"resolve-reject-promise": "^1.1.0",
|
|
24
|
+
"safety-catch": "^1.0.2",
|
|
25
|
+
"sodium-universal": "^5.0.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"brittle": "^3.19.0",
|
|
29
|
+
"corestore": "^7.5.0",
|
|
30
|
+
"hypercore-crypto": "^3.6.1",
|
|
31
|
+
"prettier": "^3.6.2",
|
|
32
|
+
"prettier-config-holepunch": "^2.0.0"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/holepunchto/broadcast-encryption.git"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/holepunchto/broadcast-encryption/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/holepunchto/broadcast-encryption#readme"
|
|
42
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// This file is autogenerated by the hyperschema compiler
|
|
2
|
+
// Schema Version: 2
|
|
3
|
+
/* eslint-disable camelcase */
|
|
4
|
+
/* eslint-disable quotes */
|
|
5
|
+
/* eslint-disable space-before-function-paren */
|
|
6
|
+
|
|
7
|
+
const { c } = require('hyperschema/runtime')
|
|
8
|
+
|
|
9
|
+
const VERSION = 2
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line no-unused-vars
|
|
12
|
+
let version = VERSION
|
|
13
|
+
|
|
14
|
+
// @broadcast/payload.payload
|
|
15
|
+
const encoding0_1 = c.array(c.buffer)
|
|
16
|
+
|
|
17
|
+
// @broadcast/payload
|
|
18
|
+
const encoding0 = {
|
|
19
|
+
preencode(state, m) {
|
|
20
|
+
c.fixed32.preencode(state, m.publicKey)
|
|
21
|
+
encoding0_1.preencode(state, m.payload)
|
|
22
|
+
},
|
|
23
|
+
encode(state, m) {
|
|
24
|
+
c.fixed32.encode(state, m.publicKey)
|
|
25
|
+
encoding0_1.encode(state, m.payload)
|
|
26
|
+
},
|
|
27
|
+
decode(state) {
|
|
28
|
+
const r0 = c.fixed32.decode(state)
|
|
29
|
+
const r1 = encoding0_1.decode(state)
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
publicKey: r0,
|
|
33
|
+
payload: r1
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// @broadcast/pointer
|
|
39
|
+
const encoding1 = {
|
|
40
|
+
preencode(state, m) {
|
|
41
|
+
c.uint.preencode(state, m.to)
|
|
42
|
+
c.uint.preencode(state, m.from)
|
|
43
|
+
c.buffer.preencode(state, m.nonce)
|
|
44
|
+
c.buffer.preencode(state, m.buffer)
|
|
45
|
+
},
|
|
46
|
+
encode(state, m) {
|
|
47
|
+
c.uint.encode(state, m.to)
|
|
48
|
+
c.uint.encode(state, m.from)
|
|
49
|
+
c.buffer.encode(state, m.nonce)
|
|
50
|
+
c.buffer.encode(state, m.buffer)
|
|
51
|
+
},
|
|
52
|
+
decode(state) {
|
|
53
|
+
const r0 = c.uint.decode(state)
|
|
54
|
+
const r1 = c.uint.decode(state)
|
|
55
|
+
const r2 = c.buffer.decode(state)
|
|
56
|
+
const r3 = c.buffer.decode(state)
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
to: r0,
|
|
60
|
+
from: r1,
|
|
61
|
+
nonce: r2,
|
|
62
|
+
buffer: r3
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// @broadcast/message.payload
|
|
68
|
+
const encoding2_1 = c.frame(encoding0)
|
|
69
|
+
// @broadcast/message.pointer
|
|
70
|
+
const encoding2_2 = c.frame(encoding1)
|
|
71
|
+
|
|
72
|
+
// @broadcast/message
|
|
73
|
+
const encoding2 = {
|
|
74
|
+
preencode(state, m) {
|
|
75
|
+
c.uint.preencode(state, m.version)
|
|
76
|
+
state.end++ // max flag is 2 so always one byte
|
|
77
|
+
|
|
78
|
+
if (m.payload) encoding2_1.preencode(state, m.payload)
|
|
79
|
+
if (m.pointer) encoding2_2.preencode(state, m.pointer)
|
|
80
|
+
},
|
|
81
|
+
encode(state, m) {
|
|
82
|
+
const flags = (m.payload ? 1 : 0) | (m.pointer ? 2 : 0)
|
|
83
|
+
|
|
84
|
+
c.uint.encode(state, m.version)
|
|
85
|
+
c.uint.encode(state, flags)
|
|
86
|
+
|
|
87
|
+
if (m.payload) encoding2_1.encode(state, m.payload)
|
|
88
|
+
if (m.pointer) encoding2_2.encode(state, m.pointer)
|
|
89
|
+
},
|
|
90
|
+
decode(state) {
|
|
91
|
+
const r0 = c.uint.decode(state)
|
|
92
|
+
const flags = c.uint.decode(state)
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
version: r0,
|
|
96
|
+
payload: (flags & 1) !== 0 ? encoding2_1.decode(state) : null,
|
|
97
|
+
pointer: (flags & 2) !== 0 ? encoding2_2.decode(state) : null
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function setVersion(v) {
|
|
103
|
+
version = v
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function encode(name, value, v = VERSION) {
|
|
107
|
+
version = v
|
|
108
|
+
return c.encode(getEncoding(name), value)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function decode(name, buffer, v = VERSION) {
|
|
112
|
+
version = v
|
|
113
|
+
return c.decode(getEncoding(name), buffer)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getEnum(name) {
|
|
117
|
+
switch (name) {
|
|
118
|
+
default:
|
|
119
|
+
throw new Error('Enum not found ' + name)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function getEncoding(name) {
|
|
124
|
+
switch (name) {
|
|
125
|
+
case '@broadcast/payload':
|
|
126
|
+
return encoding0
|
|
127
|
+
case '@broadcast/pointer':
|
|
128
|
+
return encoding1
|
|
129
|
+
case '@broadcast/message':
|
|
130
|
+
return encoding2
|
|
131
|
+
default:
|
|
132
|
+
throw new Error('Encoder not found ' + name)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getStruct(name, v = VERSION) {
|
|
137
|
+
const enc = getEncoding(name)
|
|
138
|
+
return {
|
|
139
|
+
preencode(state, m) {
|
|
140
|
+
version = v
|
|
141
|
+
enc.preencode(state, m)
|
|
142
|
+
},
|
|
143
|
+
encode(state, m) {
|
|
144
|
+
version = v
|
|
145
|
+
enc.encode(state, m)
|
|
146
|
+
},
|
|
147
|
+
decode(state) {
|
|
148
|
+
version = v
|
|
149
|
+
return enc.decode(state)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const resolveStruct = getStruct // compat
|
|
155
|
+
|
|
156
|
+
module.exports = {
|
|
157
|
+
resolveStruct,
|
|
158
|
+
getStruct,
|
|
159
|
+
getEnum,
|
|
160
|
+
getEncoding,
|
|
161
|
+
encode,
|
|
162
|
+
decode,
|
|
163
|
+
setVersion,
|
|
164
|
+
version
|
|
165
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 2,
|
|
3
|
+
"schema": [
|
|
4
|
+
{
|
|
5
|
+
"name": "payload",
|
|
6
|
+
"namespace": "broadcast",
|
|
7
|
+
"compact": false,
|
|
8
|
+
"flagsPosition": -1,
|
|
9
|
+
"fields": [
|
|
10
|
+
{
|
|
11
|
+
"name": "publicKey",
|
|
12
|
+
"required": true,
|
|
13
|
+
"type": "fixed32",
|
|
14
|
+
"version": 1
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "payload",
|
|
18
|
+
"required": true,
|
|
19
|
+
"array": true,
|
|
20
|
+
"type": "buffer",
|
|
21
|
+
"version": 1
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "pointer",
|
|
27
|
+
"namespace": "broadcast",
|
|
28
|
+
"compact": false,
|
|
29
|
+
"flagsPosition": -1,
|
|
30
|
+
"fields": [
|
|
31
|
+
{
|
|
32
|
+
"name": "to",
|
|
33
|
+
"required": true,
|
|
34
|
+
"type": "uint",
|
|
35
|
+
"version": 1
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "from",
|
|
39
|
+
"required": true,
|
|
40
|
+
"type": "uint",
|
|
41
|
+
"version": 1
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "nonce",
|
|
45
|
+
"required": true,
|
|
46
|
+
"type": "buffer",
|
|
47
|
+
"version": 1
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "buffer",
|
|
51
|
+
"required": true,
|
|
52
|
+
"type": "buffer",
|
|
53
|
+
"version": 2
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "message",
|
|
59
|
+
"namespace": "broadcast",
|
|
60
|
+
"compact": false,
|
|
61
|
+
"flagsPosition": 1,
|
|
62
|
+
"fields": [
|
|
63
|
+
{
|
|
64
|
+
"name": "version",
|
|
65
|
+
"required": true,
|
|
66
|
+
"type": "uint",
|
|
67
|
+
"version": 1
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "payload",
|
|
71
|
+
"required": false,
|
|
72
|
+
"type": "@broadcast/payload",
|
|
73
|
+
"version": 1
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "pointer",
|
|
77
|
+
"required": false,
|
|
78
|
+
"type": "@broadcast/pointer",
|
|
79
|
+
"version": 1
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|