Skip to content

Commit

Permalink
Merge pull request libp2p#60 from little-bear-labs/feature/refactor-a…
Browse files Browse the repository at this point in the history
…nd-add-tests-and-coverage

Feature/refactor and add tests and coverage
  • Loading branch information
ddimaria authored Dec 5, 2022
2 parents b2e4f60 + f254822 commit 0ea38dc
Show file tree
Hide file tree
Showing 27 changed files with 1,717 additions and 426 deletions.
51 changes: 3 additions & 48 deletions .github/workflows/js-test-and-release.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
# File managed by web3-bot. DO NOT EDIT.
# See https://github.com/protocol/.github/ for details.

name: test & maybe release
on:
push:
branches:
- master # with #262 - ${{{ github.default_branch }}}
- main # with #262 - ${{{ github.default_branch }}}
pull_request:
branches:
- master # with #262 - ${{{ github.default_branch }}}
- main # with #262 - ${{{ github.default_branch }}}
- develop

jobs:
Expand Down Expand Up @@ -38,20 +35,6 @@ jobs:
with:
flags: chrome

test-chrome-webworker:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v2
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- run: npm run --if-present test:chrome-webworker
- uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0
with:
flags: chrome-webworker

test-firefox:
needs: check
runs-on: ubuntu-latest
Expand All @@ -66,36 +49,8 @@ jobs:
with:
flags: firefox

test-firefox-webworker:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v2
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- run: npm run --if-present test:firefox-webworker
- uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0
with:
flags: firefox-webworker

test-electron-main:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v2
with:
node-version: lts/*
- uses: ipfs/aegir/actions/cache-node-modules@master
- run: npx xvfb-maybe npm run --if-present test:electron-main
- uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0
with:
flags: electron-main

release:
needs: [test-chrome, test-chrome-webworker, test-firefox, test-firefox-webworker, test-electron-main]
needs: [test-chrome, test-firefox]
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/master' # with #262 - 'refs/heads/${{{ github.default_branch }}}'
steps:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public/css/main.css

# Coverage reports
coverage
.coverage
.nyc_output

# API keys and secrets
.env
Expand Down
123 changes: 89 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@
[![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![IRC](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
[![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io)
[![codecov](https://img.shields.io/codecov/c/github/little-bear-labs//js-libp2p-webrtc.svg?style=flat-square)](https://codecov.io/gh/little-bear-labs//js-libp2p-webrtc)
[![CI](https://img.shields.io/github/workflow/status/libp2p/js-libp2p-interfaces/test%20&%20maybe%20release/master?style=flat-square)](https://github.com/little-bear-labs//js-libp2p-webrtc/actions/workflows/js-test-and-release.yml)
[![codecov](https://img.shields.io/codecov/c/github/little-bear-labs/js-libp2p-webrtc.svg?style=flat-square)](https://codecov.io/gh/little-bear-labs/js-libp2p-webrtc)
[![CI](https://img.shields.io/github/workflow/status/libp2p/js-libp2p-interfaces/test%20&%20maybe%20release/master?style=flat-square)](https://github.com/little-bear-labs/js-libp2p-webrtc/actions/workflows/js-test-and-release.yml)

> The browser implementation of the WebRTC module for libp2p.
## Table of contents <!-- omit in toc -->

- [Install](#install)
- [Usage](#usage)
- [API](#api)
- [Examples](#examples)
- [Interfaces](#interfaces)
- [Transport](#transport)
- [Connection](#connection)
- [Hacking](#hacking)
- [Contribute](#contribute)
- [Development](#development)
- [Build](#build)
- [Protocol Buffers](#protocol-buffers)
- [Test](#test)
- [Lint](#lint)
- [Clean](#clean)
- [Check Dependencies](#check-dependencies)
Expand All @@ -35,49 +36,81 @@ npm i @libp2p/webrtc
## Usage

```js
import { createLibp2pNode } from 'libp2p'
import { webRTC } from '@libp2p/webrtc'
import { noise } from '@chainsafe/libp2p-noise'
import { createLibp2p } from 'libp2p'
import { Noise } from '@chainsafe/libp2p-noise'
import { multiaddr } from '@multiformats/multiaddr'
import { pipe } from 'it-pipe'
import all from 'it-all'

const node = await createLibp2pNode({
transports: [
webRTC()
],
connectionEncryption: [
noise()
]
})

const addr = multiaddr('/ip4/0.0.0.0/udp/56093/webrtc/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ')
const { stream } = await node.dialProtocol(addr, '/my-protocol/1.0.0')
const values = await pipe(stream, all)
import first from "it-first";
import { pipe } from "it-pipe";
import { fromString, toString } from "uint8arrays";
import { webRTC } from 'js-libp2p-webrtc'

const node = await createLibp2p({
transports: [webRTC()],
connectionEncryption: [() => new Noise()],
});

await node.start()

const ma = multiaddr('/ip4/0.0.0.0/udp/56093/webrtc/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ')
const stream = await node.dialProtocol(ma, ['/my-protocol/1.0.0'])
const message = `Hello js-libp2p-webrtc\n`
const response = await pipe([fromString(message)], stream, async (source) => await first(source))
const responseDecoded = toString(response.slice(0, response.length))
```
## API

## Examples
Examples can be found in the [examples folder](examples/README.md).

## Interfaces

### Transport

[![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/libp2p-interfaces/src/transport/img/badge.png)](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/transport)
![https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/interface-transport](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/interface-transport/img/badge.png)

`libp2p-webrtc` accepts WebRTC encapsulated addresses: `/ip4/0.0.0.0/udp/56093/webrtc/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ`
Browsers can only `dial`, so `listen` is not supported.

### Connection
```js
interface Transport {
[Symbol.toStringTag]: string
[symbol]: true
dial: (ma: Multiaddr, options: DialOptions) => Promise<Connection>
createListener: (options: CreateListenerOptions) => Listener
filter: MultiaddrFilter
}

class WebRTCTransport implements Transport {

async dial (ma: Multiaddr, options: WebRTCDialOptions): Promise<Connection> {
const rawConn = await this._connect(ma, options)
log(`dialing address - ${ma.toString()}`)
return rawConn
}

createListener (options: CreateListenerOptions): Listener {
throw unimplemented('WebRTCTransport.createListener')
}
}
```

[![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/libp2p-interfaces/src/connection/img/badge.png)](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/connection)
### Connection

## Hacking
![https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/interface-connection](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/interface-connection/img/badge.png)

Besides the usual `npm install` to get dependencies, `npm run build` to invoke tsc, and `npm run test` to execute unit tests...
```js
interface MultiaddrConnection extends Duplex<Uint8Array> {
close: (err?: Error) => Promise<void>
remoteAddr: Multiaddr
timeline: MultiaddrConnectionTimeline
}

There is also `npm run autogen` which uses ProtoBuf's protoc to populate the generated code directory `proto_ts` based on `*.proto` files in src. Don't forget to run this step before `build` any time you make a change to any of the `*.proto` files.
class WebRTCMultiaddrConnection implements MultiaddrConnection { }
```

## Contribute

Contributions are welcome! The libp2p implementation in JavaScript is a work in progress. As such, there's a few things you can do right now to help out:

- [Check out the existing issues](//github.com/little-bear-labs//js-libp2p-webrtc/issues).
- [Check out the existing issues](//github.com/little-bear-labs/js-libp2p-webrtc/issues).
- **Perform code reviews**.
- **Add tests**. There can never be enough tests.
- Go through the modules and **check out existing issues**. This is especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically.
Expand All @@ -86,8 +119,6 @@ Please be aware that all interactions related to libp2p are subject to the IPFS

Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.

## Development

This module leans heavily on (Aegir)[https://github.com/ipfs/aegir] for most of the `package.json` scripts.

### Build
Expand All @@ -99,6 +130,30 @@ npm run build

The build will be located in the `/dist` folder.

### Protocol Buffers

There is also `npm run generate:proto` script that uses protoc to populate the generated code directory `proto_ts` based on `*.proto` files in src. Don't forget to run this step before `build` any time you make a change to any of the `*.proto` files.

### Test

To run all tests:

```shell
npm test
```

To run tests for Chome only:

```shell
npm run test:chrome
```

To run tests for Firefox only:

```shell
npm run test:firefox
```

### Lint
Aegir is also used to lint the code, which follows the [Standard](https://github.com/standard/standard) JS linter.
The VS Code plugin for this standard is located at https://marketplace.visualstudio.com/items?itemName=standard.vscode-standard.
Expand Down
4 changes: 4 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Examples

* [Browser to Server Echo](browser-to-server/README.md): connect to a go-libp2p-webrtc server with a browser

34 changes: 34 additions & 0 deletions examples/browser-to-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# js-libp2p-webrtc Browser to Server

This example leverages the [vite bundler](https://vitejs.dev/) to compile and serve the libp2p code in the browser. You can use other bundlers such as Webpack, but we will not be covering them here.

## Running the Go Server

To run the Go LibP2P WebRTC server:

```shell
npm run go-libp2p-server
```

Copy the multiaddress in the output.

## Running the Example

In a separate console tab, install dependencies and start the Vite server:

```shell
npm i && npm run start
```

The browser window will automatically open.
Using the copied multiaddress from the Go server, paste it into the `Server MultiAddress` input and click the `Connect` button.
Once the peer is connected, click the message section will appear. Enter a message and click the `Send` button.

The output should look like:

```text
Dialing /ip4/10.0.1.5/udp/54375/webrtc/certhash/uEiADy8JubdWrAzseyzfXFyCpdRN02eWZg86tjCrTCA5dbQ/p2p/12D3KooWEG7N4bnZfFBNZE7WG6xm2P4Sr6sonMwyD4HCAqApEthb
Peer connected '/ip4/10.0.1.5/udp/54375/webrtc/certhash/uEiADy8JubdWrAzseyzfXFyCpdRN02eWZg86tjCrTCA5dbQ/p2p/12D3KooWEG7N4bnZfFBNZE7WG6xm2P4Sr6sonMwyD4HCAqApEthb'
Sending message 'hello'
Received message 'hello'
```
41 changes: 41 additions & 0 deletions examples/browser-to-server/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>js-libp2p WebRTC</title>
<style>
label,
button {
display: block;
font-weight: bold;
margin: 5px 0;
}
div {
margin-bottom: 20px;
}
#send-section {
display: none;
}
input[type='text'] {
width: 800px;
}
</style>
</head>
<body>
<div id="app">
<div>
<label for="peer">Server MultiAddress:</label>
<input type="text" id="peer" />
<button id="connect">Connect</button>
</div>
<div id="send-section">
<label for="message">Message:</label>
<input type="text" id="message" value="hello" />
<button id="send">Send</button>
</div>
<div id="output"></div>
</div>
<script type="module" src="./index.js"></script>
</body>
</html>
39 changes: 39 additions & 0 deletions examples/browser-to-server/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { createLibp2p } from 'libp2p'
import { Noise } from '@chainsafe/libp2p-noise'
import { multiaddr } from '@multiformats/multiaddr'
import first from "it-first";
import { pipe } from "it-pipe";
import { fromString, toString } from "uint8arrays";
import { webRTC } from 'js-libp2p-webrtc'

let stream;
const output = document.getElementById('output')
const sendSection = document.getElementById('send-section')
const appendOutput = (line) => output.innerText += `${line}\n`
const clean = (line) => line.replaceAll('\n', '')

const node = await createLibp2p({
transports: [webRTC()],
connectionEncryption: [() => new Noise()],
});

await node.start()

node.connectionManager.addEventListener('peer:connect', (connection) => {
appendOutput(`Peer connected '${node.getConnections().map(c => c.remoteAddr.toString())}'`)
sendSection.style.display = 'block'
})

window.connect.onclick = async () => {
const ma = multiaddr(window.peer.value)
appendOutput(`Dialing ${ma}`)
stream = await node.dialProtocol(ma, ['/echo/1.0.0'])
}

window.send.onclick = async () => {
const message = `${window.message.value}\n`
appendOutput(`Sending message '${clean(message)}'`)
const response = await pipe([fromString(message)], stream, async (source) => await first(source))
const responseDecoded = toString(response.slice(0, response.length));
appendOutput(`Received message '${clean(responseDecoded)}'`)
}
Loading

0 comments on commit 0ea38dc

Please sign in to comment.