From 3a4e806c1b65a1b2a261e09c132ea88ab0a9fabf Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Wed, 10 Apr 2024 17:06:06 -0500 Subject: [PATCH 01/35] ts-nitro version of demo auth app --- packages/example-web-app/src/App.css | 117 +++++-- packages/example-web-app/src/App.tsx | 455 ++++++++++++++++++++++--- packages/example-web-app/tsconfig.json | 2 +- 3 files changed, 497 insertions(+), 77 deletions(-) diff --git a/packages/example-web-app/src/App.css b/packages/example-web-app/src/App.css index 74b5e053..a9345119 100644 --- a/packages/example-web-app/src/App.css +++ b/packages/example-web-app/src/App.css @@ -1,38 +1,103 @@ -.App { - text-align: center; +html { + margin: 0 auto; + padding: 2rem; + font-family: "Roboto", sans-serif; + background-color: #eeeff2; } -.App-logo { - height: 40vmin; - pointer-events: none; +#root { + background-color: #eeeff2; } -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } +#top-group { + background-color: white; + border-radius: 3px; + border: 3px solid #04AA6D; + padding: 0.7em; } -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); +#mid-group { + margin-top: 2em; + background-color: white; + border-radius: 3px; + border: 3px solid #04AADD; + padding: .7em; +} + +textarea { + width: 100%; + height: 20em; +} + +h2 { + margin-top: 0; + margin-bottom: 0.2em; +} + +ul { + list-style: none; +} + +.info-line { + padding: .2em; +} + +input { + border: 0; + font-size: 15px; + font-weight: 400; + letter-spacing: 1px; +} + +button { + border: none; + border-radius: 3px; + position: relative; + padding: 0.75em; + margin: 10px 1px; + font-size: 12px; + font-weight: 400; + text-transform: uppercase; + letter-spacing: 0; + will-change: box-shadow, transform; + transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), + background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 0.4s; + background-color: #04AADD; /* Green */ + color: white; +} + +button:disabled { + background-color: #DDD; /* Green */ + color: white; +} + +button:hover { + background-color: #f8881e; /* Green */ + color: white; +} + +button:hover:disabled { + background-color: #DDD; /* Green */ + color: white; +} + +.empty { + background-color: #ec3535; /* Green */ color: white; } -.App-link { - color: #61dafb; +.key { + padding-left: 1em; + font-size: 12px; + font-weight: 400; + text-transform: uppercase; + letter-spacing: 0; } -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } +.value { + padding-left: 2em; + font-size: 15px; + font-weight: 400; + letter-spacing: 1px; } diff --git a/packages/example-web-app/src/App.tsx b/packages/example-web-app/src/App.tsx index 09baa138..03a293d5 100644 --- a/packages/example-web-app/src/App.tsx +++ b/packages/example-web-app/src/App.tsx @@ -1,50 +1,40 @@ -import React, { useEffect } from 'react'; -import assert from 'assert'; +import { useEffect, useState } from 'react'; +import { + utils, + LedgerChannelInfo, + PaymentChannelInfo +} from '@cerc-io/nitro-node'; +import { + JSONbigNative, + hex2Bytes +} from '@cerc-io/nitro-util'; -import { utils } from '@cerc-io/nitro-node'; -import { JSONbigNative, hex2Bytes, DEFAULT_CHAIN_URL_WEBSOCKET } from '@cerc-io/nitro-util'; - -import contractAddresses from './nitro-addresses.json'; -import logo from './logo.svg'; import './App.css'; -const { - ACTORS, - createPeerIdFromKey, - createPeerAndInit, - subscribeVoucherLogs -} = utils; - -declare global { - interface Window { - setupNode: (name: string) => Promise - clearNodeStorage: () => Promise - out: (jsonObject: any) => void - } -} +const { createPeerIdFromKey, createPeerAndInit, subscribeVoucherLogs } = utils; -window.clearNodeStorage = utils.Nitro.clearNodeStorage; - -// Method to setup nitro node with test actors -window.setupNode = async (name: string): Promise => { - const actor = ACTORS[name]; - assert(actor, `Actor with name ${name} does not exists`); - assert(process.env.REACT_APP_RELAY_MULTIADDR); +(BigInt.prototype as any).toJSON = function () { + return Number(this.toString()); +}; +const setupNode = async ( + websocketUrl: string, + privateKey: string, + bootNodeMultiAddr: string, + contractAddresses: { [key: string]: string } +): Promise => { // Create peer instance - const peerIdObj = await createPeerIdFromKey(hex2Bytes(actor.privateKey)); - const peer = await createPeerAndInit(process.env.REACT_APP_RELAY_MULTIADDR, {}, peerIdObj); + const peerIdObj = await createPeerIdFromKey(hex2Bytes(privateKey)); + const peer = await createPeerAndInit(bootNodeMultiAddr, {}, peerIdObj); const nitro = await utils.Nitro.setupNode( - actor.privateKey, - DEFAULT_CHAIN_URL_WEBSOCKET, - actor.chainPrivateKey, + privateKey, + websocketUrl, + privateKey, contractAddresses, peer, true, - `${name}-db`, - undefined, - process.env.REACT_APP_ASSET_ADDRESS + 'nitro-db' ); // Subscribe to vouchers and log them @@ -53,25 +43,390 @@ window.setupNode = async (name: string): Promise => { return nitro; }; -window.out = (jsonObject) => { - console.log(JSONbigNative.stringify(jsonObject, null, 2)); -}; +async function updateChannels ( + nitro: utils.Nitro, + setFocusedLedgerChannel: (l: LedgerChannelInfo | null) => void, + setFocusedPaymentChannel: (p: PaymentChannelInfo | null) => void, + setCreatingLedgerChannel: (v: boolean) => void, + setCreatingPaymentChannel: (v: boolean) => void +) { + if (!nitro) { + return; + } + const ledgerChannels = (await nitro.getAllLedgerChannels()).filter( + (lc) => lc.status === 'Open' + ); + const paymentChannels = new Map(); + + let focusedLedgerChannel: LedgerChannelInfo | null = null; + let focusedPaymentChannel: PaymentChannelInfo | null = null; + + for (const lc of ledgerChannels) { + const pcs = (await nitro.getPaymentChannelsByLedger(lc.iD.string())).filter( + (pc) => pc.status === 'Open' + ); + paymentChannels.set(lc.iD.string(), pcs); + for (const pc of pcs) { + if ( + focusedPaymentChannel == null || + pc.balance.remainingFunds!.valueOf() > + focusedPaymentChannel.balance.remainingFunds!.valueOf() + ) { + focusedLedgerChannel = lc; + focusedPaymentChannel = pc; + } + } + } + + if (!focusedLedgerChannel && ledgerChannels.length) { + focusedLedgerChannel = ledgerChannels[0]; + } + + setFocusedPaymentChannel(focusedPaymentChannel); + if (focusedPaymentChannel) { + setCreatingPaymentChannel(false); + } + setFocusedLedgerChannel(focusedLedgerChannel); + if (focusedLedgerChannel) { + setCreatingLedgerChannel(false); + } +} + +async function pay ( + nitro: utils.Nitro | null, + targetUrl: string, + paymentChannel: PaymentChannelInfo | null, + amount: number, + setToken: (p: any | null) => void +) { + if (nitro && paymentChannel) { + const voucher = await nitro.pay(paymentChannel.iD.string(), `${amount}`); + const response = await fetch(`${targetUrl}/pay/receive`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(voucher) + }); + const token = await response.json(); + setToken(token); + } +} + +function getRpcUrl (rpcUrl?: string): string { + if (rpcUrl) { + return rpcUrl ?? ''; + } + return 'ws://localhost:8545'; +} + +function getTargetUrl (targetUrl?: string): string { + if (targetUrl) { + return targetUrl ?? ''; + } + + return 'http://localhost:5678'; +} + +async function send (url: string): Promise { + try { + const fromEl = document.getElementById('api-send'); + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + // @ts-ignore + body: fromEl!.value + }); + + const text = await response.text(); + const recvEl = document.getElementById('api-recv'); + // @ts-ignore + recvEl.value = text; + } catch (e) { + const recvEl = document.getElementById('api-recv'); + // @ts-ignore + recvEl.value = e; + } +} function App () { + const [nitro, setNitro] = useState(null); + const [targetServerUrl, setTargetServerUrl] = useState( + getTargetUrl() + ); + const [myEthWebSocketUrl, setMyEthWebSocketUrl] = useState(getRpcUrl()); + const [myNitroAddress, setMyNitroAddress] = useState(''); + const [theirNitroAddress, setTheirNitroAddress] = useState(''); + const [targetMultiAddr, setTargetMultiAddr] = useState('/ip4/127.0.0.1/tcp/5007/ws/p2p/16Uiu2HAmHWMQivwbNvkzaAwnSkFxYjxvcbe55oPKWPXdKz3NwPbi'); + const [focusedLedgerChannel, setFocusedLedgerChannel] = + useState(null); + const [focusedPaymentChannel, setFocusedPaymentChannel] = + useState(null); + const [token, setToken] = useState(null); + const [creatingLedgerChannel, setCreatingLedgerChannel] = + useState(false); + const [creatingPaymentChannel, setCreatingPaymentChannel] = + useState(false); + + let updateEverything = async () => {}; + let updateInterval: NodeJS.Timeout | undefined; + + useEffect(() => { + const delayDebounceFn = setTimeout(() => { + setFocusedPaymentChannel(null); + setFocusedLedgerChannel(null); + setMyNitroAddress(''); + setupNode( + myEthWebSocketUrl, + '888814df89c4358d7ddb3fa4b0213e7331239a80e1f013eaa7b2deca2a41a218', + targetMultiAddr, + { + nitroAdjudicatorAddress: '0x2B6AFbd4F479cE4101Df722cF4E05F941523EaD9', + virtualPaymentAppAddress: '0xBca48057Da826cB2eb1258E2C679678b269dC262', + consensusAppAddress: '0xCf5207018766587b8cBad4B8B1a1a38c225ebA7A' + }).then((c) => { + setNitro(c); + setMyNitroAddress(c.node.address); + updateEverything = async () => + updateChannels( + c, + setFocusedLedgerChannel, + setFocusedPaymentChannel, + setCreatingLedgerChannel, + setCreatingPaymentChannel + ); + if (updateInterval) { + clearInterval(updateInterval); + } + updateInterval = setInterval(updateEverything, 1000); + }); + }, 1000); + + return () => clearTimeout(delayDebounceFn); + }, [myEthWebSocketUrl]); + useEffect(() => { - window.onunhandledrejection = (err) => { - // Log unhandled errors instead of stopping application - console.log(err); - }; - }, []); + if (nitro) { + setMyNitroAddress(nitro.store.getAddress()); + nitro.addPeerByMultiaddr(theirNitroAddress, targetMultiAddr); + updateEverything(); + // nitro.notifications.on('objective_completed', updateEverything); + } + }, [nitro, targetMultiAddr, theirNitroAddress]); + + useEffect(() => { + const delayDebounceFn = setTimeout(() => { + setFocusedPaymentChannel(null); + setFocusedLedgerChannel(null); + setTheirNitroAddress(''); + fetch(targetServerUrl + '/pay/address').then((response) => { + response.text().then((v) => { + setTheirNitroAddress(v); + if (nitro) { + updateEverything(); + } + }); + }); + }, 1000); + + return () => clearTimeout(delayDebounceFn); + }, [targetServerUrl, targetMultiAddr]); return ( -
-
- logo -

ts-nitro

-
-
+ <> +
+

Nitro Details

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Consumer Nitro Node + setMyEthWebSocketUrl(e.target.value)} + value={myEthWebSocketUrl?.toString()} + /> +
Consumer Address{myNitroAddress}
Provider Endpoint + setTargetServerUrl(e.target.value)} + value={targetServerUrl?.toString()} + /> +
Provider Address{theirNitroAddress}
Ledger Channel + {focusedLedgerChannel + ? ( + + {focusedLedgerChannel.iD.string()}{' '} + + + ) + : ( + + )} +
Ledger Balance + {focusedLedgerChannel + ? `${focusedLedgerChannel.balance.theirBalance} / ${focusedLedgerChannel.balance.myBalance}` + : ''} +
Payment Channel + {focusedPaymentChannel + ? ( + + {focusedPaymentChannel.iD.string()}{' '} + + + ) + : focusedLedgerChannel + ? ( + + ) + : ( + '' + )} +
Channel Balance + {focusedPaymentChannel + ? `${focusedPaymentChannel.balance.paidSoFar} / ${focusedPaymentChannel.balance.remainingFunds}` + : ''} +
API Token + {token && `${token.token}`}{' '} + {focusedPaymentChannel && ( + + )} +
+
+
+

Ethereum API

+ + + + + + + + + +
+ +
+ +
+
+ ); } diff --git a/packages/example-web-app/tsconfig.json b/packages/example-web-app/tsconfig.json index a273b0cf..1bdae4cf 100644 --- a/packages/example-web-app/tsconfig.json +++ b/packages/example-web-app/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "esnext", "lib": [ "dom", "dom.iterable", From c1ed737634455be82008e55b98dc0e093827e749 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Fri, 19 Apr 2024 16:59:21 -0500 Subject: [PATCH 02/35] env --- packages/example-web-app/src/App.tsx | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/example-web-app/src/App.tsx b/packages/example-web-app/src/App.tsx index 03a293d5..8de87e2d 100644 --- a/packages/example-web-app/src/App.tsx +++ b/packages/example-web-app/src/App.tsx @@ -13,10 +13,6 @@ import './App.css'; const { createPeerIdFromKey, createPeerAndInit, subscribeVoucherLogs } = utils; -(BigInt.prototype as any).toJSON = function () { - return Number(this.toString()); -}; - const setupNode = async ( websocketUrl: string, privateKey: string, @@ -106,7 +102,7 @@ async function pay ( headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(voucher) + body: JSONbigNative.stringify(voucher) }); const token = await response.json(); setToken(token); @@ -117,7 +113,7 @@ function getRpcUrl (rpcUrl?: string): string { if (rpcUrl) { return rpcUrl ?? ''; } - return 'ws://localhost:8545'; + return process.env.REACT_APP_RPC_URL ?? 'ws://localhost:8546'; } function getTargetUrl (targetUrl?: string): string { @@ -125,7 +121,7 @@ function getTargetUrl (targetUrl?: string): string { return targetUrl ?? ''; } - return 'http://localhost:5678'; + return process.env.REACT_APP_TARGET_URL ?? 'http://localhost:5678'; } async function send (url: string): Promise { @@ -180,12 +176,12 @@ function App () { setMyNitroAddress(''); setupNode( myEthWebSocketUrl, - '888814df89c4358d7ddb3fa4b0213e7331239a80e1f013eaa7b2deca2a41a218', + process.env.REACT_APP_NITRO_PK!, targetMultiAddr, { - nitroAdjudicatorAddress: '0x2B6AFbd4F479cE4101Df722cF4E05F941523EaD9', - virtualPaymentAppAddress: '0xBca48057Da826cB2eb1258E2C679678b269dC262', - consensusAppAddress: '0xCf5207018766587b8cBad4B8B1a1a38c225ebA7A' + nitroAdjudicatorAddress: process.env.REACT_APP_NA_ADDRESS!, + virtualPaymentAppAddress: process.env.REACT_APP_VPA_ADDRESS!, + consensusAppAddress: process.env.REACT_APP_CA_ADDRESS! }).then((c) => { setNitro(c); setMyNitroAddress(c.node.address); @@ -241,7 +237,7 @@ function App () { - +
Consumer Nitro NodeETH WebSocket URL