• Stars
    star
    120
  • Rank 295,983 (Top 6 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created over 7 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

bandwidth efficient broadcast gossip

Epidemic Broadcast Trees

This module is loosely based on plumtree Epidemic Broadcast Trees EBT paper, but adapted to also replicate logs, and optimized to achive a minimal overhead (the cost of the protocol is linear with the number of messages to be sent)

It's a algorithm that combines the robustness of a flooding epidemic gossip broadcast, with the efficiency of a tree model. It's intended for implementing realtime protocols (such as chat, scuttlebutt, also radio/video) over networks with random topology - or networks where otherwise peers may be unable to all connect to each other or to a central hub.

Although the primary motivation for this module is to use it in secure scuttlebutt, it's intended to be decoupled sufficiently to use for other applications.

Example

implement a simple in memory log replicator.

var clocks = {}
var logs = {}

function append (msg, cb) {
  var log = logs[msg.author] || {}
  //check that this is the next expected message.
  if(msg.sequence != Object.keys(log).length + 1)
    cb(new Error('out of order, found:'+msg.sequence+', expected:'+log.length))
  else {
    log[msg.sequence] = msg
    ebt.onAppend(msg)
    cb()
  }
}

var ebt = EBT({
  //NOTE: in this example, we are using readable strings for clarity
  //but ideally you'd use cryptographic ids, like public keys.
  id: 'alice',
  getClock: function (id, cb) {
    //load the peer clock for id.
    cb(null, clocks[id] || {})
  },
  setClock: function (id, clock) {
    //set clock doesn't have take a cb, but it's okay to be async.
    clocks[id] = clock
  },
  getAt: function (pair, cb) {
    //load a message particular message, by id:sequence
    if(!logs[pair.id] || !logs[pair.id][pair.sequence])
      cb(new Error('not found'))
    else
      cb(null, logs[pair.id][pair.sequence])
  },
  append: append
})

ebt.append({
  author: 'alice', sequence: 1, content: {}
}, function () {})

//must explicitly say we are replicating which peers.
ebt.request('alice', true)
ebt.request('bob', true)

//create a stream and pipe it to another instance
//isClient and version are required.
var stream = ebt.createStream('bob', version=3, isClient = true)
stream.pipe(remote_stream).pipe(stream)

note about push-stream: push-stream is only new, so you'll probably need to convert this to a pull-stream to connect stream to a network io stream and serialization

var pushToPull = require('push-stream-to-pull-stream')
var stream = pushToPull(ebt.createStream(remote_id, 3, isCient = true))
pull(stream, remote_pull_stream, stream)

API

EBT(opts) => ebt

where opts provides the necessary things to connect ebt to your system.

opts = {
  id: string,
  timeout: 3000, //default,
  getClock: function (id, cb),
  setClock: function (id, clock),
  getAt: function ({id:string, sequence:number}, cb),
  append: function (msg, cb),
  isFeed: function (id),
  isMsg: function(data),
  getMsgAuthor: function(msg),
  getMsgSequence: function(msg)
}

Create a new EBT instance. id is a unique identifier of the current peer. In secure-scuttlebutt this is a ed25519 public key.

getClock(id, cb) and setClock(id, clock) save a peer's clock object. This is used to save bandwidth when reconnecting to a peer again.

getAt({id, sequence}, cb) retrives a message in a feed and an sequence. messages must have {author, sequence, content} fields.

append(msg, cb) append a particular message to the log.

timeout is used to decide when to switch a feed to another peer. This is essential to detecting when a peer may have stalled.

isFeed(id) is a validation function that returns true if id is a valid feed identifier. If not, it is ignored'

optional for backwards compatibility

isMsg(data) is a validation function used to distinguish between data messages and status messages. A message must contain an author field that corresponds to the feed identifier and a sequence field.

getMsgAuthor(msg) is a function that given a message returns the author.

getMsgSequence(msg) is a function that given a message returns the sequence.

ebt.onAppend (msg)

When a message is appended to the database, tell ebt about it. this must be called whenever a message is successfully appended to the database.

ebt.createStream(id, version, isClient) => PushStream

Create a stream for replication. returns a push-stream. The current version is 3, and isClient must be either true or false. On the client side stream, it will wait for the server to send their vector clock, before replying. This means that if the server doesn't actually support this api, you give them a change to send back an error before sending a potentially large vector clock.

ebt.request(id, follow)

Tell ebt to replicate a particular feed. id is a feed id, and follow is a boolean. If follow is false, but previously was called with true, ebt will stop replicating that feed.

ebt.progress()

returns an object which represents the current replication progress.

an example object output looks like this, all values are integers >= 0.

{
  start: S, //where we where at when we started
  current: C, //operations done
  total: T //operations expected
}

this follows a common pattern used across ssbc modules for representing progress, used for example here: https://github.com/ssbc/scuttlebot/blob/master/lib/progress.js

ebt.state

The state of the replication is available at ebt.state. Read only access is okay, but updating should only be done via ebt methods.

{
  id: <id>, //our id,
  clock: {<id>: <seq>}, //our local clock,
  follows: {<id>: <boolean>}, //who we replicate, true if we replicate.
  blocks: {<id>: {<id>: <boolean>}}, //who blocks who, true if they are blocked.
  peers: { //currently connected peers
    <id>: {
      clock: {<id>: <seq|-1>}, //feeds that we KNOW the peer is up to. -1 if they do not replicate that feed.
      msgs: [<msg>], //queue of messages waiting to be sent.
      retrive: [<id>], //ids of feeds ready for the next message to be retrived.
      notes: null || {<id>: <encoded_seq>}, //notes object (encoded vector clock to be sent)
      replicating: { //feeds being replicated to peer.
        <id>: {
          rx: <boolean>, //true if we have asked to recieve this feed
          tx: <boolean>, //true if we have been asked to send this feed
          sent: <seq|-1|null>, //sequence number of message we have sent.
          requested: <seq|-1|null> //sequence number the remote peer asked for, and thus we know they have.
        }
      }
    }
  },
  receive: [<msg>] //queue of incoming messages
}

notes: <X> is a value type.

<id> is a "feed id" value that opts.isFeed(id) === true. (note, this doesn't actually need to be an ssb feed id, this module can be used for other things too)

<seq> is an positive integer or zero. -1 is used to represent if the are explicitly not replicating that feed.

<msg> is a message where opts.isMsg(id) === true.

Replication overview

The state of other peers are stored outside this module in the SSB-EBT module. See getClock & setClock.

Notes (aka the vector clock) are stored as { feed: (seq === -1 ? -1 : seq << 1 | !rx) } (= * 2 + 1?). The sequence can be extracted using getSequence and rx/tx using getReceive (is even). -1 means do not replicate.

two peers connect, one is the client (who initiated the connection), and the other is the server (that received the request). The protocol is mostly the same for clients and servers, but one exception is that the server starts by sending their vector clock (notes first).

It's assumed that data changes following a long tail pattern, a small number of peers are highly active, but many peers only add data slowly. Connections between peers may be short lived, and data may change more slowly than subsequent connections. If we sent the whole vector clock on each connection that would add up to a significant overhead. Request Skipping is a way to avoid a great deal of bandwidth overhead. Each peer remembers the vector clock sent by the other peer. And, on a new connection, when they send their vector clock they first check it against the vector clock that the remote peer sent last time. If the sequence in the peer's clock is the same as the one in the saved record of the remote peer, then that feed is left out of the request (hence the name "request skipping") The saved record of the remote peer's vector clock may be different to their actual vector clock, but if they have a new sequence for that feed, they will include that feed in their request, and the local peer will respond by sending an additional partial vector clock including their sequence for that feed, once both sides have exchanged their sequence for a particular feed, replication of messages in that feed may occur.

When connecting to multiple peers, only request new messages using rx for a feed from one of the nodes. See test/multiple.js.

Following and blocking are handled in EBT. Following acts as the signal of what feeds to replicate. EBT won't connect to someone that has been blocked. It will not send messages of a peer (including self) to another peer if the first peer blocks the second.

The tests are very readable because they use a simulator where a trace of the run is saved and pretty printed. See test/two.js for a good example.

Comparison to plumtree

I had an idea for a gossip protocol that avoided retransmitting messages by putting unneeded connections into standby mode (which can be brought back into service when necessary) and then was pleasantly surprised to discover it was not a new idea, but had already been described in a paper - and there is an EBT implementation in erlang of that paper.

There are some small differences, mainly because I want to send messages in order, which makes it easy to represent what messages have not been seen using just a incrementing sequence number per feed.

But plumbtree is solely a broadcast protocol, not an eventually consistent replication protocol. Since we are replicating logs it's also necessary to send a handshake to request the feeds from the right points. If you are replicating thousands of feeds the size of the handshake is significant, so we introduce an algorithm for "request skipping" that avoids sending unnecessary requests, and saves a lot of bandwidth compared to just requesting all feeds each connection.

Related work

Brisa also describes a broadcast protocal that at first glace looks very close to the EBT paper. It is modelled using two components: tree construction/maintenance and peer sampling. Peer sampling is in SSB terminology where SSB conn is used. Brisa uses HyParView, written by the same authors as the EBT paper for peer sampling. Compared to EBT, Brisa does not depend on lazy mode between peers where only the sequence information is maintained, instead it depends on HyParView to detect failures. This has the advantage that it does not need a timer, that is highly latency sensitive. It also has a nice property in how messages are disseminated in that they are piggybacked with information about the tree that allows the parent selection to make better choices as it has a better view of the network. The sequence numbers are an important part of the protocol implemented here because they, as described earlier, are used to ensure that messages are disseminated in a eventually consistent manor.

TODO

License

MIT

More Repositories

1

patchwork

A decentralized messaging and sharing app built on top of Secure Scuttlebutt (SSB).
JavaScript
3,582
star
2

ssb-server

The gossip and replication server for Secure Scuttlebutt - a distributed social network
JavaScript
1,677
star
3

ssb-db

A database of unforgeable append-only feeds, optimized for efficient replication for peer to peer protocols
JavaScript
1,175
star
4

patchbay

An alternative Secure Scuttlebutt client interface that is fully compatible with Patchwork
JavaScript
387
star
5

scuttlebutt-protocol-guide

Protocol documentation for Secure Scuttlebutt
HTML
213
star
6

go-ssb-room

Room server implemented in Go
Go
187
star
7

handbook.scuttlebutt.nz

ssb handbook: A guide to the Secure Scuttlebutt key concepts and influences (see also, new website: https://gitlab.com/ssbc/scuttlebutt.nz/)
CSS
165
star
8

go-ssb

Go implementation of ssb (work in progress!)
Go
155
star
9

multiserver

A single interface that can work with multiple protocols, and multiple transforms of those protocols (eg, security layer)
JavaScript
103
star
10

muxrpc

lightweight multiplexed rpc
JavaScript
98
star
11

chloride

JavaScript
90
star
12

secret-stack

connect peers to each other using secret-handshakes
JavaScript
89
star
13

patchcore

A shared library of depject modules to build Secure Scuttlebutt social network apps
JavaScript
75
star
14

docs

Documentation repo
Shell
61
star
15

patchfoo

Plain SSB web UI. Uses HTML forms instead of client-side JS. Designed for use on low-power and low-resource computers.
54
star
16

scuttle-shell

A system tray app for running Secure Scuttlebutt and providing sbot features to your local system
JavaScript
54
star
17

jitdb

A database on top of a log with automatic index generation and maintenance
JavaScript
49
star
18

ssb-db2

A new database for secure-scuttlebutt
JavaScript
47
star
19

ssb-client

client library to scuttlebot
JavaScript
46
star
20

bipf

Binary json codec optimized for in-place access
JavaScript
45
star
21

go-secretstream

Go port of secret-handshake
Go
44
star
22

ssb-keys

keyfile operations for ssb
JavaScript
36
star
23

dgram-broadcast

Formerly known as "broadcast-stream" on npm
JavaScript
34
star
24

dynamic-dijkstra

JavaScript
31
star
25

tinySSB

tinySSB is "Secure Scuttlebutt over LoRa and BLE"
C
29
star
26

ssb-peer-invites

A new ssb invite system to create invites without having a pub
JavaScript
26
star
27

ssb-tribes

JavaScript
26
star
28

go-muxrpc

js/muxrpc in golang (for interfacing ssb/scuttlebot)
Go
23
star
29

ssb-friends

Manages the SSB social graph
JavaScript
23
star
30

ssb-config

standard configuration for ssb
JavaScript
23
star
31

packet-stream

JavaScript
23
star
32

ssb-tunnel

create a p2p link tunneled through a pub server
JavaScript
22
star
33

ssb-ahoy

An onboarding mini-app - gets you all set up, and caught up on the gossip before you set out on your adventure
JavaScript
22
star
34

sips

Secure Scuttlebutt Implementation Protocols
HTML
22
star
35

netsim

secure scuttlebutt network simulator
Go
21
star
36

rooms2

Design doc for the next edition of SSB Room servers
HTML
21
star
37

dev.scuttlebutt.nz

The go-to place for updated developer information on the SSB stack in different languages
20
star
38

ssb-threads

Scuttlebot plugin for fetching messages as threads
JavaScript
20
star
39

multiblob

A content-addressable-store that supports multiple hashing algorithms, and pull-streams
JavaScript
20
star
40

envelope-spec

JavaScript
18
star
41

patchlite

[WIP] A browser lite client for the Scuttlebutt network
JavaScript
18
star
42

ssb-ebt

secure scuttlebutt replication with epidemic-broadcast-trees
JavaScript
18
star
43

ssb-identities

manage multiple identities as sbot plugin
JavaScript
18
star
44

ssb2-discussion-forum

not quite tiny, also not quite large
17
star
45

ssb-uri-spec

Specification of SSB URIs
16
star
46

async-append-only-log

A new append-only-log for SSB purposes
JavaScript
16
star
47

visual-docs

Diagrams and animations documenting Secure Scuttlebutt (scuttlebutt.nz) and Āhau (ahau.io)
JavaScript
16
star
48

ssb-conn

SSB plugin for establishing and managing peer connections
TypeScript
16
star
49

ssb-typescript

Contains type definitions for common SSB concepts
TypeScript
16
star
50

margaret

a flume-like persisted append-only log implementation
Go
16
star
51

ssb-spec-drafts

A collection of protocol specifications for Secure Scuttlebutt
HTML
15
star
52

ssb-first-aid-kit

A user-friendly app for diagnosing and fixing problems with your Scuttlebutt installation
Vue
15
star
53

fusion-identity-spec

14
star
54

ssb-ooo

retrive ssb messages Out Of Order
JavaScript
14
star
55

ssb-subset-replication-spec

14
star
56

private-group-spec

JavaScript
13
star
57

ssb-graphviz

visualize your ssb network graph
JavaScript
12
star
58

ssb-viewer

JavaScript
12
star
59

ssb-validate

better ssb validator
JavaScript
12
star
60

ssb-group-exclusion-spec

12
star
61

ssb-query

JavaScript
12
star
62

ssb-hello-ws

simplest example to get a ssb-client working over websockets
HTML
12
star
63

envelope-js

new message encryption for ssb
JavaScript
12
star
64

layered-graph

JavaScript
11
star
65

ssb-blobs

blob gossiping ssb-subprotocol
JavaScript
11
star
66

ssb-buttwoo-spec

Spec for a new binary feed format for SSB
11
star
67

ssb-backup-tool

A backup tool for the Scuttleverse
JavaScript
11
star
68

scuttlebot.io

Source repo for https://scuttlebot.io
JavaScript
11
star
69

ssb-meta-feeds-spec

Design doc for subfeeds in SSB
HTML
11
star
70

ssb-usage-stats

Generate statistics about network use (number of users, growth, retention, etc)
JavaScript
11
star
71

ssb-meta-feeds

JavaScript
10
star
72

ssb-ws

ssb-ws & http server for ssb
JavaScript
10
star
73

ssb-uri2

Utilities for recognizing and converting SSB URIs
JavaScript
10
star
74

ssb-fixtures

Generate a simulated .ssb folder
TypeScript
10
star
75

ssb-gossip

Schedule connections randomly with a peerlist constructed from config, multicast UDP announcements, feed announcements, and API-calls
JavaScript
10
star
76

scuttle-testbot

JavaScript
10
star
77

ssb-bfe-rs

Binary Field Encodings (BFE) for Secure Scuttlebutt (SSB)
Rust
10
star
78

ssb-http-auth-spec

Specification of SSB HTTP Authentication
HTML
10
star
79

ssb-backlinks

scuttlebot plugin for indexing all link mentions of messages
JavaScript
10
star
80

ssb-markdown

patchwork's markdown parser
JavaScript
9
star
81

bipf-spec

Spec for bipf
9
star
82

ssb-bfe-spec

JavaScript
9
star
83

ssb-links

ssb-plugin that indexes all the links!
JavaScript
9
star
84

ssb-replicate

ssb legacy replication, previously built into ssb-server
JavaScript
8
star
85

ssb-conn-hub

Module that manages active connections to SSB peers
TypeScript
8
star
86

ssb-tribes2

SSB private groups with ssb-db2
JavaScript
8
star
87

ssb-device-address

announce a public address for yourself
JavaScript
8
star
88

stack-diagram

A diagram detailing the core components utilized in the SSB stack
8
star
89

ssb-sort

JavaScript
8
star
90

ssb-serve-blobs

Sbot plugin to serve blobs from a local http server
JavaScript
8
star
91

packet-stream-codec

JavaScript
8
star
92

ssb-search2

Full-text search in SSB using ssb-db2
TypeScript
8
star
93

ssb-replication-scheduler

Plugin to trigger replication of feeds identified as friendly in the social graph
JavaScript
8
star
94

ssb-tangle

JavaScript
8
star
95

ssb-private

scuttlebot plugin for indexed private messages
JavaScript
8
star
96

ssb-invite

"followbot" style invite codes for ssb
JavaScript
8
star
97

ssb-meta-feeds-migration-spec

How to migrate from classic SSB to metafeeds
7
star
98

ssb-caps

The default "Caps" keys for accessing the SSB protocol using secret handshake
7
star
99

go-metafeed

A go implementation of the bendy-butt format spec to support ssb metafeeds
Go
7
star
100

ssb-meme

JavaScript
7
star