Skip to content

Commit

Permalink
Pass peer instance as argument to message service (#78)
Browse files Browse the repository at this point in the history
* Pass peer instance to message service

* Instantiate and pass peer instance in browser app

* Check protocols before dialing for peer info exchange

* Exchange nitro info with already connected peers

* Remove util package and move code to nitro-client

* Fix nitro import in react-app

* Add publish workflows

---------

Co-authored-by: prathamesh0 <prathamesh.musale0@gmail.com>
  • Loading branch information
nikugogoi and prathamesh0 authored Jul 13, 2023
1 parent 7734207 commit 49d1bd7
Show file tree
Hide file tree
Showing 40 changed files with 320 additions and 281 deletions.
68 changes: 68 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Publish package
on:
release:
types: [published]
jobs:
npm_publish_node:
name: Push packages to gitea
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 18.x ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
registry-url: 'https://npm.pkg.github.com'
- name: "Install dependencies"
- run: yarn
- name: Run yarn build
run: |
yarn build:node
- name: Configure git.vdb.to npm registry
run: |
npm config set @cerc-io:registry https://git.vdb.to/api/packages/cerc-io/npm/
- name: Authenticate to git.vdb.to registry
run: |
npm config set -- '//git.vdb.to/api/packages/cerc-io/npm/:_authToken' "${{ secrets.GITEA_PUBLISH_TOKEN }}"
- name: lerna publish
run: |
yarn lerna publish from-package --no-git-tag-version --yes
npm_publish_browser:
name: Push nitro-client-browser package to gitea
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 18.x ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
registry-url: 'https://npm.pkg.github.com'
- name: "Install dependencies"
run: yarn
- name: Run yarn build
run: |
yarn build:browser --ignore @cerc-io/example-web-app
- name: Rename nitro-client package
run: |
cd packages/nitro-client
yarn build:update-package-name
cd ../../
- name: Configure git.vdb.to npm registry
run: |
npm config set @cerc-io:registry https://git.vdb.to/api/packages/cerc-io/npm/
- name: Authenticate to git.vdb.to registry
run: |
npm config set -- '//git.vdb.to/api/packages/cerc-io/npm/:_authToken' "${{ secrets.GITEA_PUBLISH_TOKEN }}"
- name: npm publish nitro-client-browser
run: |
cd packages/nitro-client
npm publish
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Run relay node using v2 watcher
* Copy the deployed contract addresses from 1st repo

```bash
cp <PATH_TO_FIRST_REPO>/packages/util/src/addresses.json <PATH_TO_SECOND_REPO>/packages/util/src/
cp <PATH_TO_FIRST_REPO>/packages/nitro-util/nitro-addresses.json <PATH_TO_SECOND_REPO>/packages/example-web-app/src/nitro-addresses.json
```

* Build packages for browser environment
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"test:node": "lerna run test --stream --parallel --ignore @cerc-io/example-web-app",
"prepare": "husky install",
"chain": "lerna run chain --scope=@cerc-io/server",
"test:deploy-contracts": "lerna run test:deploy-contracts --scope=@cerc-io/util"
"test:deploy-contracts": "lerna run test:deploy-contracts --scope=@cerc-io/nitro-util",
"test:copy-addresses": "lerna run test:copy-addresses --scope=@cerc-io/nitro-util"
}
}
3 changes: 3 additions & 0 deletions packages/example-web-app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Deployed nitro contract addresses json
src/nitro-addresses.json
1 change: 0 additions & 1 deletion packages/example-web-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"private": true,
"dependencies": {
"@cerc-io/nitro-client": "^0.1.0",
"@cerc-io/util": "^0.1.0",
"@libp2p/crypto": "^1.0.4",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
Expand Down
31 changes: 20 additions & 11 deletions packages/example-web-app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,46 @@
import React, { useEffect } from 'react';
import assert from 'assert';

import {
ACTORS,
DEFAULT_CHAIN_URL,
Nitro
} from '@cerc-io/util';
import { JSONbigNative } from '@cerc-io/nitro-util';
import { utils } from '@cerc-io/nitro-client';
import { JSONbigNative, hex2Bytes } from '@cerc-io/nitro-util';

import contractAddresses from './nitro-addresses.json';
import logo from './logo.svg';
import './App.css';

const {
ACTORS,
DEFAULT_CHAIN_URL,
createPeerIdFromKey,
createPeerAndInit
} = utils;

declare global {
interface Window {
setupClient: (name: string) => Promise<Nitro>
setupClient: (name: string) => Promise<utils.Nitro>
clearClientStorage: () => Promise<boolean>
out: (jsonObject: any) => void
}
}

window.clearClientStorage = Nitro.clearClientStorage;
window.clearClientStorage = utils.Nitro.clearClientStorage;

// Method to setup nitro client with test actors
window.setupClient = async (name: string): Promise<Nitro> => {
window.setupClient = async (name: string): Promise<utils.Nitro> => {
const actor = ACTORS[name];
assert(actor, `Actor with name ${name} does not exists`);
assert(process.env.REACT_APP_RELAY_MULTIADDR);

return Nitro.setupClient(
// Create peer instance
const peerIdObj = await createPeerIdFromKey(hex2Bytes(actor.privateKey));
const peer = await createPeerAndInit(process.env.REACT_APP_RELAY_MULTIADDR, {}, peerIdObj);

return utils.Nitro.setupClient(
actor.privateKey,
DEFAULT_CHAIN_URL,
actor.chainPrivateKey,
process.env.REACT_APP_RELAY_MULTIADDR,
contractAddresses,
peer,
`${name}-db`
);
};
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion packages/nitro-client/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": ["webpack.*.ts", "test/**/*.test.ts"]
"devDependencies": ["webpack.*.ts", "test/**/*.test.ts", "scripts/**/*.ts"]
}
],
"max-classes-per-file": "off",
Expand Down
2 changes: 2 additions & 0 deletions packages/nitro-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
"build:node": "yarn index:node && webpack --config webpack.prod.ts --env target=node",
"build:dev:browser": "yarn index:browser && webpack --config webpack.dev.ts --env target=browser",
"build:dev:node": "yarn index:node && webpack --config webpack.dev.ts --env target=node",
"build:update-package-name": "yarn ts-node scripts/update-package-name.ts",
"dev": "webpack --watch --config webpack.dev.ts",
"lint": "eslint .",
"test": "mocha"
},
"devDependencies": {
"@libp2p/interface-peer-store": "1.2.9",
"@npmcli/package-json": "^3.1.1",
"@typechain/ethers-v5": "^11.0.0",
"@types/chai": "^4.3.5",
"@types/debug": "^4.1.7",
Expand Down
13 changes: 13 additions & 0 deletions packages/nitro-client/scripts/update-package-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import PackageJson from '@npmcli/package-json';

async function main() {
const pkgJson = await PackageJson.load('.');
const { content } = pkgJson;

content.name = '@cerc-io/nitro-client-browser';
pkgJson.update(content);

await pkgJson.save();
}

main();
2 changes: 2 additions & 0 deletions packages/nitro-client/src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export { Destination } from './types/destination';
export { Metrics, GetMetrics } from './client/engine/metrics';
export { LedgerChannelInfo, PaymentChannelInfo } from './client/query/types';

export * as utils from './utils';

export const test = (): string => {
// eslint-disable-next-line no-console
console.log('Test from nitro-client');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import type { IncomingStreamData } from '@libp2p/interface-registrar';
// @ts-expect-error
import type { PeerId } from '@libp2p/interface-peer-id';
// @ts-expect-error
import { PeerProtocolsChangeData } from '@libp2p/interface-peer-store';
// @ts-expect-error
import type { Peer } from '@cerc-io/peer';

import { SafeSyncMap } from '../../../../internal/safesync/safesync';
Expand Down Expand Up @@ -103,10 +105,8 @@ export class P2PMessageService implements MessageService {
// If useMdnsPeerDiscovery is true, the message service will use mDNS to discover peers.
// Otherwise, peers must be added manually via `AddPeers`.
static async newMessageService(
relayMultiAddr: string,
me: Address,
pk: Buffer,
initOptions: PeerInitConfig = {},
peer: Peer,
logWriter?: WritableStream,
): Promise<P2PMessageService> {
const ms = new P2PMessageService({
Expand All @@ -117,43 +117,23 @@ export class P2PMessageService implements MessageService {
logger: log,
});

const {
unmarshalPrivateKey, marshalPrivateKey, marshalPublicKey, generateKeyPairFromSeed,
} = await import('@libp2p/crypto/keys');
ms.peer = peer;
assert(ms.peer.peerId);
const { unmarshalPrivateKey } = await import('@libp2p/crypto/keys');

try {
// TODO: Unmarshall private key similar to go-nitro
// const messageKey = await unmarshalPrivateKey(pk);
// Workaround to get a libp2p private key from `pk` passed to message service
const messageKey = await generateKeyPairFromSeed('Ed25519', pk);
const messageKey = await unmarshalPrivateKey(ms.peer.peerId.privateKey!);

ms.key = messageKey;
} catch (err) {
ms.checkError(err as Error);
}

assert(ms.key);
const PeerIdFactory = await import('@libp2p/peer-id-factory');

const { Peer } = await import('@cerc-io/peer');
// TODO: Debug connection issue with webrtc enabled
// Disabled by setting nodejs option to true below
ms.peer = new Peer(relayMultiAddr, true);
const peerId = await PeerIdFactory.createFromPrivKey(ms.key);

await ms.peer.init(
initOptions,
{
id: peerId.toString(),
privKey: Buffer.from(marshalPrivateKey(ms.key)).toString('base64'),
pubKey: Buffer.from(marshalPublicKey(ms.key.public)).toString('base64'),
},
);

assert(ms.peer.node);
ms.p2pHost = ms.peer.node;
assert(ms.p2pHost);
ms.p2pHost.addEventListener('peer:connect', ms.handlePeerConnect.bind(ms));
ms.p2pHost.peerStore.addEventListener('change:protocols', ms.handleChangeProtocols.bind(ms));

ms.p2pHost.handle(PROTOCOL_ID, ms.msgStreamHandler.bind(ms));

Expand All @@ -163,6 +143,8 @@ export class P2PMessageService implements MessageService {
});
});

await ms.exchangeInfoWithConnectedPeers();

return ms;
}

Expand All @@ -174,24 +156,57 @@ export class P2PMessageService implements MessageService {
return PeerIdFactory.createFromPrivKey(this.key);
}

// Method to exchange info with already connected peers
private async exchangeInfoWithConnectedPeers() {
const peerIds = this.p2pHost.getPeers();

await Promise.all(peerIds.map(async (peerId: PeerId) => {
const connection: Connection = await this.p2pHost.dial(peerId);

await this.handlePeerConnect({ detail: connection } as CustomEvent<Connection>);
}));
}

private async handleChangeProtocols({ detail: data }: CustomEvent<PeerProtocolsChangeData>) {
// Ignore self protocol changes
if (data.peerId.equals(this.p2pHost.peerId)) {
return;
}

// Ignore if PEER_EXCHANGE_PROTOCOL_ID is not handled by remote peer
if (!data.protocols.includes(PEER_EXCHANGE_PROTOCOL_ID)) {
return;
}

// Returns existing connection
const connection = await this.p2pHost.dial(data.peerId);
await this.exchangePeerInfo(connection);
}

// handlePeerProtocols is called by the libp2p node when a peer protocols are updated.
private async handlePeerConnect({ detail: data }: CustomEvent<Connection>) {
assert(this.p2pHost);
assert(this.p2pHost);

// Ignore for connection with relay node
if (this.peer!.relayNodeMultiaddr.equals(data.remoteAddr)) {
// Get protocols supported by remote peer
const protocols = await this.p2pHost.peerStore.protoBook.get(data.remotePeer);

// The protocol may not be updated in the list and will be handled later on change:protocols event
if (!protocols.includes(PEER_EXCHANGE_PROTOCOL_ID)) {
return;
}

await this.exchangePeerInfo(data);
}

private async exchangePeerInfo(connection: Connection) {
try {
const stream = await data.newStream(PEER_EXCHANGE_PROTOCOL_ID);
const stream = await connection.newStream(PEER_EXCHANGE_PROTOCOL_ID);

await this.sendPeerInfo(stream);
stream.close();

// Use a non-blocking channel send in case no one is listening
this.sentInfoToPeer.push(data.remotePeer);
this.sentInfoToPeer.push(connection.remotePeer);
} catch (err) {
this.checkError(err as Error);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/nitro-client/src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export { Allocation, AllocationType, Allocations } from './channel/state/outcome
export { Destination } from './types/destination';
export { Metrics, GetMetrics } from './client/engine/metrics';
export { LedgerChannelInfo, PaymentChannelInfo } from './client/query/types';

export * as utils from './utils';
File renamed without changes.
Loading

0 comments on commit 49d1bd7

Please sign in to comment.