Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CIP-0045? | Decentralized WebRTC dApp-Wallet Communication #395

Merged
merged 21 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5196c78
feat: add cip-0078 including a demo folder
fabianbormann Nov 30, 2022
bca3632
fix: adjust the structure as described in CIP-0001
fabianbormann Dec 1, 2022
dfd00b9
fix: structure and incorrectly formatted to-do list
fabianbormann Dec 1, 2022
4ee0d27
fix: reformat the structure to match the cip-0001 specs
fabianbormann Dec 1, 2022
7c7b5b7
fix: wrong formatted todo list item
fabianbormann Dec 1, 2022
f4e9739
fix: add the changes from Roberto manually due to the changed file st…
fabianbormann Dec 1, 2022
bfdb646
fix: relocate the demo implementation to a new repository
fabianbormann Dec 1, 2022
4c3344d
caught spelling mistake (not proofread whole doc)
rphair Dec 6, 2022
1b11eda
Update README.md
NetWalker108 Apr 7, 2023
0058914
Merge branch 'cardano-foundation:master' into master
fabianbormann Jul 6, 2023
a9be0a5
Apply suggestions from code review
fabianbormann Jul 6, 2023
e7ccca1
chore: add updates section, change wording to signaling server, updat…
fabianbormann Jul 6, 2023
57e0ad4
CIP-0045? | Removed top level title from old CIP template
Ryun1 Jul 27, 2023
059f0ee
feat: add identifier information with regards to cip-0013
fabianbormann Aug 17, 2023
1cfe9d5
chore: pull remote changes
fabianbormann Aug 17, 2023
c2c3b61
chore: cleanup README.md
fabianbormann Aug 17, 2023
ffceb9c
feat: add more links and references to close open points
fabianbormann Jan 4, 2024
8e03458
Update CIP-????/README.md
KtorZ Jan 4, 2024
97cc4af
chore: renaming the folder to the actual CIP number
fabianbormann Jan 4, 2024
3332377
Merge branch 'patch-1' of github.com:NetWalker108/CIPs into NetWalker…
fabianbormann Jan 4, 2024
dc7ac64
Merge branch 'NetWalker108-patch-1'
fabianbormann Jan 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 247 additions & 0 deletions CIP-????/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
---
CIP: ?
Title: Decentralized WebRTC dApp/Wallet Communication Using WebTorrent Tracker for Peer Discovery
Status: Proposed
Category: Wallets
Authors:
- Fabian Bormann <fabian.bormann@cardanofoundation.org>
- Jaime Caso <jaime.caso@cardanofoundation.org>
Implementors:
- Fabian Bormann <fabian.bormann@cardanofoundation.org>
- Jaime Caso <jaime.caso@cardanofoundation.org>
Discussions:
- https://github.com/cardano-foundation/CIPs/pull/395
Created: 2022-11-29
License: CC-BY-4.0
---

# CIP-????: Decentralized WebRTC dApp/Wallet Communication Using WebTorrent Tracker for Peer Discovery

## Abstract

We want to introduce a decentralized communication method between dApps and wallets based on WebTorrent trackers and WebRTC. This CIP also contains a proof of concept implementation injecting the wallet rpc methods into the dApps global window object similar to [CIP-0030](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0030).

## Motivation

In a decentralized ecosystem a communication between wallet apps and dApps is still challanging. The inter-app communication on mobile devices does not directly allow remote procedure calls and is mostly restricted to Universal Links (iOS) or Deeplinks (Android). State-of-the-art solutions like WalletConnect tackle these problems using WebRTC communication which also works across devices, but requires a central signaling server (STUN or TURN server) to estalblish a WebRTC connection. In this CIP we want to introduce an architecture which uses WebTorrent trackers for peer discovery to remove the need of this central component.

## Specification

### Establish a WebRTC Connection Using a STUN/TURN Server (State-of-the-art approach)

```mermaid
sequenceDiagram
dApp-->>+TURN/STUN: Who am I?
TURN/STUN-->>dApp: You are <ip:port>
Wallet-->>+TURN/STUN: Who am I?
TURN/STUN-->>Wallet: You are <ip:port>
dApp->>Wallet: messages
Wallet->>dApp: messages
```

The data will be send peer to peer via a WebRTC data channel once the ip discovery has been finished. E.g. WalletConnect expects/provides a relay server URL to initialize the connection. While this method allows dApps to communitcate peer-to-peer with wallets it also leads to a [possible SPOF](https://twitter.com/walletconnect/status/1407279853943001088?lang=en).

### Establish a WebRTC Connection Using WebTorrent Tracker (Our approach)

```mermaid
flowchart LR
subgraph dApp
subgraph .torrent server
end
end

subgraph Wallet[Wallet App]
end

dApp-->|.torrent| TrackerA[Tracker 1]
dApp-->|.torrent| TrackerB[Tracker 2]
dApp-->|.torrent| TrackerC[...]
dApp-->|.torrent| TrackerD[Tracker n]

dApp-->|.torrent| TrackerA[Tracker 1]
dApp-->|.torrent| TrackerB[Tracker 2]
dApp-->|.torrent| TrackerC[...]
dApp-->|.torrent| TrackerD[Tracker n]

dApp-->|Share public key| Wallet
```

Deeplinks, Universal Links or even a QR code could be used to share the public key. The wallet app would query a list of trackers using the public key to establish the WebRTC connection. Once this is done, the data is sent peer-to-peer via the WebRTC standard (e.g. to invoke RPC calls).

```mermaid
flowchart LR
Wallet-->|queries| TrackerA[Tracker 1]
Wallet-->|queries| TrackerB[Tracker 2]
Wallet-->|queries| TrackerC[...]
Wallet-->|queries| TrackerD[Tracker n]

Wallet-->|queries| TrackerA[Tracker 1]
Wallet-->|queries| TrackerB[Tracker 2]
Wallet-->|queries| TrackerC[...]
Wallet-->|queries| TrackerD[Tracker n]

subgraph dApp
subgraph .torrent server
end
end

Wallet<--Establish WebRTC data channel\n for peer to peer communication-->dApp
```

### Proof of Concept

The idea of using WebTorrent trackers instead of STUN/TURN servers for peer discovery was already mentioned in [Aug 2018 by Chris McCormick](https://mccormick.cx/news/entries/on-self-hosting-and-decentralized-software):
"I've also been tinkering with WebTorrent. [...]
Working with this technology made me realise something the other day: it's now possible to host back-end services, or "servers" inside browser tabs. [...] So anyway, I've made this weird thing to enable developers to build "backend" services which run in browser tabs"

McCormick's idea has been developed and open sourced as a library called [bugout](https://github.com/chr15m/bugout/) (MIT).

For this proof of concept we wrote two small pieces of software:

- A html page aka the dApp
- An ionic react app (to target mutliple devices) aka the wallet app

The whole code is provided within this [demo folder](demo) that also contains a [step-by-step guide](demo/README.md).

#### dApp

The dApp consists of a standard HTML5 template including the following lines of code:

```html
<script src="https://chr15m.github.io/bugout/bugout.min.js"></script>
<script>
var bugout = new Bugout({
seed: localStorage["poc-server-seed"],
announce: [
'udp://tracker.opentrackr.org:1337/announce',
'udp://open.tracker.cl:1337/announce',
'udp://opentracker.i2p.rocks:6969/announce',
'https://opentracker.i2p.rocks:443/announce',
'wss://tracker.files.fm:7073/announce',
'wss://spacetradersapi-chatbox.herokuapp.com:443/announce',
'ws://tracker.files.fm:7072/announce'
]
});
localStorage["poc-server-seed"] = bugout.seed;

var connected = false;
bugout.on("connections", function (clients) {
if (clients == 0 && connected == false) {
connected = true;
console.log("[info]: server ready");
console.log(`[info]: share this address with your wallet app -> ${bugout.address()}`);
}
console.log(`[info]: ${clients} clients connected`);
});

bugout.register("api", function (address, args, callback) {
const api = { version: args.api.version, address: address }

for (method of args.api.methods) {
api[method] = () => new Promise((resolve, reject) => {
bugout.rpc(address, method, {}, (result) => resolve(result));
});
}

window.cardano = window.cardano || {};
window.cardano[args.api.name] = api;
console.log(`[info]: injected api of ${args.api.name} into window.cardano`);
});
</script>
```

#### Wallet App

The wallet app is a standard ionic react app built by the ionic cli:

```zsh
ionic start WalletApp blank --type=react
cd WalletApp
npm i bugout
```

The following lines of code were added to the index.tsx file:

```js
const bugout = new Bugout(
"<HASH provided by the dAPP>", {
announce: [
'udp://tracker.opentrackr.org:1337/announce',
'udp://open.tracker.cl:1337/announce',
'udp://opentracker.i2p.rocks:6969/announce',
'https://opentracker.i2p.rocks:443/announce',
'wss://tracker.files.fm:7073/announce',
'wss://spacetradersapi-chatbox.herokuapp.com:443/announce',
'ws://tracker.files.fm:7072/announce'
]
});

bugout.on("server", function() {
console.log("[info]: connected to server")
bugout.rpc("<HASH provided by the dAPP>", "api", {"api": {
version: "1.0.3",
name: 'boostwallet',
methods: ["getRewardAddresses"]
}});
});

bugout.register("getRewardAddresses", (address:string, args:any, callback:Function) => {
callback(["e1820506cb0ce54ae755b2512b6cf31856d7265e8792cb86afc94e0872"]);
});
```

This example has a few restrictions:

1. bugout is currently not compatible with Webpack 5, so polyfills are not automatically included and a react-scripts eject is needed to add them to the webpack.config.js file

2. bugout does not directly provide type declarations. There are some declarations within a [PR](https://github.com/chr15m/bugout/pull/45), but they need to be adjusted (a few parameters are not mandatory) and added to a bugout.d.ts file.

### User Flow

```mermaid
sequenceDiagram
dApp-->>Wallet: Share address using Deeplink /Universal Link / QR
Wallet-->>Tracker List: Accept connection and start quering using the address
Wallet-->>dApp: Once connected register RPC functions and share the API
dApp-->>dApp: Inject the API to window.cardano[walletName]
dApp-->>Wallet: Call RPC functions (e.g. getRewardAddresses)
Wallet-->>Wallet: If needed bring app to foregrund (e.g. request signing)
Wallet-->>dApp: Send response
```

### Security Aspects

We decided to spwan the server within the dApp to force the user to manually scan a QR code (using a wallet app) or accept an "Open with `<WalletAppName>`" ui dialog (in case of Universal Links or Deeplinks). This prevents the user from connecting the wallet to an unwanted dApp. Additionally we need to add a few security checks to prevent a misusage of this method.

- The wallet app needs to verifiy the origin (address) of the RPC call
- dApps should ask the user for permission to inject the wallet names into the window.cardano object to prevent XSS attack (Maybe using a graphical representation of the wallet app address e.g. blockies)

## Rationale
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me this rationale section is a bit light, id like to see answers too:

  • Were there any alternate designs considered? Why did the current design succeed?
  • What are the benefits and drawbacks of this approach when compared to the connection via CIP-30?

Copy link
Collaborator

@rphair rphair Jul 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I remember @fabianbormann you & we talked about some alternative design considerations in the CIP meetings around the time this was originally presented... so perhaps you could recall & include an inventory of those that came up?


The purpose of this CIP mainly consists of two parts. It addresses the current lack of dApp mobile support, but at the same time provides an even more decentralized alternative to state-of-the-art communication methods. To archive this goal we have introduced a WebTorrent and WebRTC based architecture. To demonstrate a viable implementation, we have implemented a proof of concept which also shows how a rpc method injection like CIP-0030 might look like.

## Path to Active

### Acceptance Criteria

- [ ] A library should be build to make it easy from dAPP and wallet side to implement the proposed communication method
- [ ] The library target should be browser to avoid the need of manual polyfills
- [ ] Mobile testing on different devices and operating systems needs to be done with a special focus to the wallet app running in background mode
- [ ] Potential security issues and attack vectors need to be discussed in detail
- [ ] A full reference implementation is needed to test if the entire user flow and at the same time provide this as a how-to for developers

### Implementation Plan

- [ ] Fork/Extend bugout to add webpack 5 and typescript support
- [ ] Povide a general intermediate cardano-connect typescript library to provide
1. A check for mobile/desktop environment
2. Depending on the environment provide interfaces for CIP-0030 / and / or CIP-?
3. Add a full implementation of the server/client side code above to define a communication standard similar to CIP-0030 (getRewardAddresses, signData, signTx, ...)
- [ ] Start discussions about security gaps within the proposed method with various developers and also look for research papers
- [x] Check if the wallet app also reacts to rpc calls in background mode on Android
- [ ] Check if the wallet app also reacts to rpc calls in background mode on iOS
- [ ] Implement the library within an example dApp and [boost wallet](https://github.com/boost-pool/boost-wallet)

## Copyright

This CIP is licensed under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/legalcode)
40 changes: 40 additions & 0 deletions CIP-????/demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Run the demo

## Prerequirements

* node>=16.x.x, npm>=8.x.x

## Getting Started

```zsh
cd ionic-app
npm i
```

## Run the server (aka dApp)

Open the dApp.html file & your dev tools to see the console log outputs.

## Run the client (aka Wallet App)

```zsh
cd ionic-app
npm start
```

## Testing (PoC)

Once you have the server and client running you should see something like

```js
"[info]: injected api of boostwallet into window.cardano"
```

in your dApp logs. Now you can issue

```js
window.cardano.boostwallet.getRewardAddresses().then(result => console.log(result))
```

to execute the remote call and get the reward address from your Wallet App.

51 changes: 51 additions & 0 deletions CIP-????/demo/dApp.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://chr15m.github.io/bugout/bugout.min.js"></script>
<script>
var bugout = new Bugout({
seed: localStorage["bugout-demo-server-seed"],
announce: [
'udp://tracker.opentrackr.org:1337/announce',
'udp://open.tracker.cl:1337/announce',
'udp://opentracker.i2p.rocks:6969/announce',
'https://opentracker.i2p.rocks:443/announce',
'wss://tracker.files.fm:7073/announce',
'wss://spacetradersapi-chatbox.herokuapp.com:443/announce',
'ws://tracker.files.fm:7072/announce'
]
});
localStorage["bugout-demo-server-seed"] = bugout.seed;

var connected = false;
bugout.on("connections", function (clients) {
if (clients == 0 && connected == false) {
connected = true;
console.log("[info]: server ready");
}
console.log(`[info]: ${clients} clients connected`);
});

bugout.register("api", function (address, args, callback) {
const api = { version: args.api.version, address: address }

for (method of args.api.methods) {
api[method] = () => new Promise((resolve, reject) => {
bugout.rpc(address, method, {}, (result) => resolve(result));
});
}

window.cardano = window.cardano || {};
window.cardano[args.api.name] = api;
console.log(`[info]: injected api of ${args.api.name} into window.cardano`);
});
</script>
<title>DApp</title>
</head>
<body>
<h1>Simple Example Dapp</h1>
</body>
</html>
31 changes: 31 additions & 0 deletions CIP-????/demo/ionic-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
/.vscode/*
!/.vscode/extensions.json
.idea

npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Optional eslint cache
.eslintcache

.vscode/
10 changes: 10 additions & 0 deletions CIP-????/demo/ionic-app/capacitor.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
appId: 'io.ionic.starter',
appName: 'WalletApp',
webDir: 'build',
bundledWebRuntime: false
};

export default config;
Loading