Skip to content

Commit

Permalink
Merge 9aac171 into d05367f
Browse files Browse the repository at this point in the history
  • Loading branch information
dignifiedquire committed Apr 26, 2016
2 parents d05367f + 9aac171 commit 05f87b2
Show file tree
Hide file tree
Showing 24 changed files with 1,337 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

dist
lib
29 changes: 29 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

test
24 changes: 24 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
sudo: false
language: node_js
node_js:
- 4
- 5

# Make sure we have new NPM.
before_install:
- npm install -g npm

script:
- npm run lint
- npm test
- npm run coverage

addons:
firefox: 'latest'

before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start

after_success:
- npm run coverage-publish
46 changes: 44 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,44 @@
# js-bitswap
Node.js implementation of the Bitswap 'data exchange' protocol used by IPFS
# ipfs-bitswap

[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs)
[![Coverage Status](https://coveralls.io/repos/github/ipfs/js-ipfs-bitswap/badge.svg?branch=master)](https://coveralls.io/github/ipfs/js-ipfs-bitswap?branch=master)
[![Travis CI](https://travis-ci.org/ipfs/js-ipfs-bitswap.svg?branch=master)](https://travis-ci.org/ipfs/js-ipfs-bitswap)
[![Circle CI](https://circleci.com/gh/ipfs/js-ipfs-bitswap.svg?style=svg)](https://circleci.com/gh/ipfs/js-ipfs-bitswap)
[![Dependency Status](https://david-dm.org/ipfs/js-ipfs-bitswap.svg?style=flat-square)](https://david-dm.org/ipfs/js-ipfs-bitswap) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)


> Node.js implementation of the Bitswap 'data exchange' protocol used by IPFS
# Installation

## npm

```sh
> npm i ipfs-bitswap
```

## Use in Node.js

```js
const bitswap = require('ipfs-bitswap')
```

## Use in a browser with browserify, webpack or any other bundler

The code published to npm that gets loaded on require is in fact a ES5 transpiled version with the right shims added. This means that you can require it and use with your favourite bundler without having to adjust asset management process.

```js
const bitswap = require('ipfs-bitswap')
```

## Use in a browser Using a script tag

Loading this module through a script tag will make the `Unixfs` obj available in the global namespace.

```html
<script src="https://npmcdn.com/ipfs-bitswap/dist/index.min.js"></script>
<!-- OR -->
<script src="https://npmcdn.com/ipfs-bitswap/dist/index.js"></script>
```
12 changes: 12 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
machine:
node:
version: stable

dependencies:
pre:
- google-chrome --version
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
- sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
- sudo apt-get update
- sudo apt-get --only-upgrade install google-chrome-stable
- google-chrome --version
55 changes: 55 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "ipfs-bitswap",
"version": "1.0.0",
"description": "Node.js implementation of the Bitswap data exchange protocol used by IPFS",
"main": "lib/index.js",
"jsnext:main": "src/index.js",
"scripts": {
"test": "aegir-test",
"test:browser": "aegir-test browser",
"test:node": "aegir-test node",
"lint": "aegir-lint",
"release": "aegir-release",
"build": "aegir-build",
"coverage": "aegir-coverage",
"coverage-publish": "aegir-coverage publish"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ipfs/js-ipfs-bitswap.git"
},
"keywords": [
"ipfs",
"libp2p",
"p2p",
"exchange"
],
"author": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/ipfs/js-ipfs-bitswap/issues"
},
"homepage": "https://github.com/ipfs/js-ipfs-bitswap#readme",
"devDependencies": {
"aegir": "^2.1.2",
"chai": "^3.5.0",
"lodash": "^4.11.1",
"peer-id": "^0.6.6"
},
"dependencies": {
"debug": "^2.2.0",
"heap": "^0.2.6",
"ipfs-blocks": "^0.2.3",
"lodash.isundefined": "^3.0.1",
"protocol-buffers": "^3.1.6"
},
"aegir": {
"webpack": {
"resolve": {
"alias": {
"node-forge": "../../../node_modules/peer-id/vendor/forge.bundle.js"
}
}
}
}
}
11 changes: 11 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict'

const second = 1000

module.exports = {
maxProvidersPerRequest: 3,
provierRequestTimeout: 10 * second,
hasBlockTimeout: 15 * second,
provideTimeout: 15 * second,
kMaxPriority: Math.pow(2, 31) - 1
}
123 changes: 123 additions & 0 deletions src/decision/engine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
'use strict'

const debug = require('debug')
const log = debug('engine')
log.error = debug('engine:error')

const Wantlist = require('../wantlist')
const PeerRequestQueue = require('./peer-request-queue')
const Ledger = require('./ledger')

module.exports = class Engine {
constructor (blockStore) {
this.blockStore = blockStore

// A list of of ledgers by their partner id
this.ledgerMap = new Map()

// A priority queue of requests received from different
// peers.
this.peerRequestQueue = new PeerRequestQueue()

// Can't declare generator functions regularly
this.outbox = function * () {
// eslint-disable-next-line
while (true) {
const nextTask = this.peerRequestQueue.pop()
if (!nextTask) break

const block = this.blockStore.get(nextTask.entry.key)
if (!block) {
nextTask.done()
continue
}

yield {
peer: nextTask.target,
block: block,
sent: () => {
nextTask.done()
}
}
}
}
}

peers () {
return Array.from(this.ledgerMap.values()).map((l) => l.partner)
}

// Handle incoming messages
messageReceived (peerId, msg) {
if (msg.empty) {
log('received empty message from %s', peerId)
}

const ledger = this._findOrCreate(peerId)

// If the message was a full wantlist clear the current one
if (msg.full) {
ledger.wantlist = new Wantlist()
}

for (let entry of msg.wantlist.values()) {
const key = entry.entry.key
if (entry.cancel) {
log('cancel %s', key)
ledger.cancelWant(key)
this.peerRequestQueue.remove(key, peerId)
} else {
log('wants %s - %s', key, entry.entry.priority)
ledger.wants(key, entry.entry.priority)

// If we already have the block, serve it
if (this.blockStore.has(key)) {
this.peerRequestQueue.push(entry.entry, peerId)
}
}
}

for (let block of msg.blocks.values()) {
log('got block %s %s bytes', block.key, block.data.length)
ledger.receivedBytes(block.data.length)

// Check all connected peers if they want the block we received
for (let l of this.ledgerMap.values()) {
const entry = l.wantlistContains(block.key)

if (entry) {
this.peerRequestQueue.push(entry, ledger.partner)
}
}
}
}

// Clear up all accounting things after message was sent
messageSent (peerId, msg) {
const ledger = this._findOrCreate(peerId)
for (let block of msg.blocks.values()) {
ledger.sentBytes(block.data.length)
ledger.wantlist.remove(block.key)
this.peerRequestQueue.remove(block.key, peerId)
}
}

numBytesSentTo (peerId) {
return this._findOrCreate(peerId).accounting.bytesSent
}

numBytesReceivedFrom (peerId) {
return this._findOrCreate(peerId).accounting.bytesRecv
}

_findOrCreate (peerId) {
if (this.ledgerMap.has(peerId)) {
return this.ledgerMap.get(peerId)
}

const l = new Ledger(peerId)
this.ledgerMap.set(peerId, l)

return l
}
}
5 changes: 5 additions & 0 deletions src/decision/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict'

const Engine = require('./engine')

exports.Engine = Engine
42 changes: 42 additions & 0 deletions src/decision/ledger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict'

const Wantlist = require('../wantlist')

module.exports = class Ledger {
constructor (peerId) {
this.partner = peerId
this.wantlist = new Wantlist()

this.exchangeCount = 0
this.sentToPeer = new Map()

this.accounting = {
bytesSent: 0,
bytesRecv: 0
}
}

sentBytes (n) {
this.exchangeCount ++
this.lastExchange = (new Date()).getTime()
this.accounting.bytesSent += n
}

receivedBytes (n) {
this.exchangeCount ++
this.lastExchange = (new Date()).getTime()
this.accounting.bytesRecv += n
}

wants (key, priority) {
this.wantlist.add(key, priority)
}

cancelWant (key) {
this.wantlist.remove(key)
}

wantlistContains (key) {
return this.wantlist.contains(key)
}
}
Loading

0 comments on commit 05f87b2

Please sign in to comment.