Skip to content

Commit

Permalink
Port latest changes from go-nitro till commit 7de42286 on September…
Browse files Browse the repository at this point in the history
… 22 (#128)

* Chunked requests to query old chain logs

* Refactor params of initializeNode method

* Change type of amount to string

* Use UseDurableStore flag to enable durable store

* Use shorthand syntax for storeOpts properties

* Add useDurableStore param in setUpNode

* Change useDurableStore flag to mandatory

* Throw error if durable store folder not passed

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
  • Loading branch information
prathamesh0 and neerajvijay1997 authored Oct 10, 2023
1 parent 3a42d1d commit e0d500d
Show file tree
Hide file tree
Showing 15 changed files with 182 additions and 119 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Run relay node using v2 watcher
* Setup client

```bash
const nitro = await setupClient('charlie')
const nitro = await setupNode('charlie')
```

* Assign private keys of Bob to variables
Expand All @@ -71,7 +71,7 @@ Run relay node using v2 watcher

```bash
# In packages/server
yarn cli --pk $BOB_PK --chain-pk $BOB_CHAIN_PK --store ./out/bob-db --direct-fund --counter-party $CHARLIE_ADDRESS --get-ledger-channel --amount 1000000
yarn cli --pk $BOB_PK --chain-pk $BOB_CHAIN_PK --use-durable-store true --durable-store-folder ./out/bob-db --direct-fund --counter-party $CHARLIE_ADDRESS --get-ledger-channel --amount 1000000
# Expected output:
# ts-nitro:engine Constructed Engine +0ms
Expand All @@ -90,7 +90,7 @@ Run relay node using v2 watcher
* Run client for Bob again to create virtual payment channel:

```bash
yarn cli --pk $BOB_PK --chain-pk $BOB_CHAIN_PK --store ./out/bob-db --virtual-fund --counter-party $CHARLIE_ADDRESS --get-payment-channel --amount 1000
yarn cli --pk $BOB_PK --chain-pk $BOB_CHAIN_PK --use-durable-store true --durable-store-folder ./out/bob-db --virtual-fund --counter-party $CHARLIE_ADDRESS --get-payment-channel --amount 1000
# Final Expected output:
# ts-nitro:engine Objective VirtualFund-0xf112143060c59a6d2c5b2d429ca46eb48286e1ca1301da60b9a1a184ad1a58bb is complete & returned to API +1ms
Expand All @@ -106,7 +106,7 @@ Run relay node using v2 watcher
* Run client for Bob to make payment:

```bash
yarn cli --pk $BOB_PK --chain-pk $BOB_CHAIN_PK --store ./out/bob-db --pay --amount 50 --payment-channel $PAYMENT_CHANNEL_ID --wait
yarn cli --pk $BOB_PK --chain-pk $BOB_CHAIN_PK --use-durable-store true --durable-store-folder ./out/bob-db --pay --amount 50 --payment-channel $PAYMENT_CHANNEL_ID --wait
```

* Wait for voucher received log in client Charlie
Expand All @@ -120,7 +120,7 @@ Run relay node using v2 watcher
* Close virtual payment channel using client Bob

```bash
yarn cli --pk $BOB_PK --chain-pk $BOB_CHAIN_PK --store ./out/bob-db --virtual-defund --payment-channel $PAYMENT_CHANNEL_ID --get-payment-channel
yarn cli --pk $BOB_PK --chain-pk $BOB_CHAIN_PK --use-durable-store true --durable-store-folder ./out/bob-db --virtual-defund --payment-channel $PAYMENT_CHANNEL_ID --get-payment-channel
# Final Expected output:
# ts-nitro:engine Objective VirtualDefund-0xe613b9f1651f971473061a968823463e9570b83230c2bce734b21800f663e4aa is complete & returned to API +1ms
Expand All @@ -130,7 +130,7 @@ Run relay node using v2 watcher
* Close the ledger channel using client Bob

```bash
yarn cli --pk $BOB_PK --chain-pk $BOB_CHAIN_PK --store ./out/bob-db --direct-defund --ledger-channel $LEDGER_CHANNEL_ID --get-ledger-channel
yarn cli --pk $BOB_PK --chain-pk $BOB_CHAIN_PK --use-durable-store true --durable-store-folder ./out/bob-db --direct-defund --ledger-channel $LEDGER_CHANNEL_ID --get-ledger-channel
# Final Expected output:
# ts-nitro:engine Objective DirectDefunding-0xe29e2d7ee060fb78b279ac4c8f5cc9bf59334f3e0d25315d5e3c822ed0303d9e is complete & returned to API +1ms
Expand Down Expand Up @@ -180,5 +180,5 @@ Run relay node using v2 watcher
* In browser apps call `clearClientStorage` method to delete all indexedDBs

```bash
clearClientStorage()
clearNodeStorage()
```
8 changes: 4 additions & 4 deletions packages/example-web-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ Instructions to run two instances of `ts-nitro` clients in a browser environment
* In first browser

```bash
const nitro = await setupClient('charlie')
const nitro = await setupNode('charlie')
```

* In second browser

```bash
const nitro = await setupClient('david')
const nitro = await setupNode('david')
```

* Wait for `New peer found` log in console
Expand Down Expand Up @@ -198,7 +198,7 @@ Instructions to run instances of `ts-nitro` (browser) and `go-nitro` clients and
* Setup client

```bash
const nitro = await setupClient('david')
const nitro = await setupNode('david')
```

* Assign private keys of Erin to variables
Expand Down Expand Up @@ -349,7 +349,7 @@ Instructions to run instances of `ts-nitro` (browser) and `go-nitro` clients and
* Clear nitro client storage

```bash
clearClientStorage()
clearNodeStorage()
```

## Getting Started with Create React App
Expand Down
1 change: 1 addition & 0 deletions packages/example-web-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ window.setupNode = async (name: string): Promise<utils.Nitro> => {
actor.chainPrivateKey,
contractAddresses,
peer,
true,
`${name}-db`,
undefined,
process.env.REACT_APP_ASSET_ADDRESS
Expand Down
10 changes: 0 additions & 10 deletions packages/nitro-node/src/internal/chain/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,6 @@ import { Address } from '../../types/types';

const log = debug('ts-nitro:chain');

export interface ChainOpts {
chainUrl?: string
chainStartBlock: Uint64
chainPk?: string
provider?: providers.JsonRpcProvider,
naAddress: Address
vpaAddress: Address
caAddress: Address
}

// deployContract deploys a contract and waits for the transaction to be mined.
async function deployContract(name: string, signer: Signer, contractInterface: ContractInterface, bytecode: BytesLike): Promise<string> {
const contractFactory = new ContractFactory(contractInterface, bytecode).connect(signer);
Expand Down
18 changes: 9 additions & 9 deletions packages/nitro-node/src/internal/node/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,29 @@ import assert from 'assert';
import type { Peer } from '@cerc-io/peer';
import { NitroSigner } from '@cerc-io/nitro-util';

import { EthChainService } from '../../node/engine/chainservice/eth-chainservice';
import { EthChainService, ChainOpts } from '../../node/engine/chainservice/eth-chainservice';
import { ChainService } from '../../node/engine/chainservice/chainservice';
import { P2PMessageService } from '../../node/engine/messageservice/p2p-message-service/service';
import { newStore } from '../../node/engine/store/utils';
import { P2PMessageService, MessageOpts } from '../../node/engine/messageservice/p2p-message-service/service';
import { newStore, StoreOpts } from '../../node/engine/store/utils';
import { setupNode } from '../../utils/helpers';
import { MetricsApi } from '../../node/engine/metrics';
import { Store } from '../../node/engine/store/store';
import { Node } from '../../node/node';
import { ChainOpts } from '../chain/chain';

const log = debug('ts-nitro:node');

export async function initializeNode(
signer: NitroSigner,
peer: Peer,
chainOpts: ChainOpts,
durableStoreFolder?: string,
storeOpts: StoreOpts,
messageOpts: MessageOpts,
metricsApi?: MetricsApi,
): Promise<[Node, Store, P2PMessageService, ChainService]> {
const ourStore = await newStore(signer, durableStoreFolder);
const ourStore = await newStore(storeOpts);

log('Initializing message service...');
const msgService = await P2PMessageService.newMessageService(ourStore.getAddress(), peer);
// eslint-disable-next-line no-param-reassign
messageOpts.scAddr = ourStore.getAddress();
const msgService = await P2PMessageService.newMessageService(messageOpts);

// Compare chainOpts.ChainStartBlock to lastBlockNum seen in store. The larger of the two
// gets passed as an argument when creating NewEthChainService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,17 @@ import { assetAddressForIndex } from './eth-chain-helpers';
import { connectToChain } from './utils/utils';
import { VariablePart } from '../../../channel/state/state';
import { convertBindingsExitToExit, convertBindingsSignaturesToSignatures } from './adjudicator/reverse_typeconversions';
import { ChainOpts } from '../../../internal/chain/chain';
import { EventTracker } from './event-queue';

const log = debug('ts-nitro:eth-chain-service');

// REQUIRED_BLOCK_CONFIRMATIONS is how many blocks must be mined before an emitted event is processed
const REQUIRED_BLOCK_CONFIRMATIONS = 2;

// MAX_EPOCHS is the maximum range of old epochs we can query with a single "FilterLogs" request
// This is a restriction enforced by the rpc provider
const MAX_EPOCHS = 60480;

const naInterface = NitroAdjudicator__factory.createInterface();
const concludedTopic = ethers.utils.id(naInterface.getEvent('Concluded').format());
const allocationUpdatedTopic = ethers.utils.id(naInterface.getEvent('AllocationUpdated').format());
Expand All @@ -55,6 +58,16 @@ const topicsToWatch: string[] = [
challengeClearedTopic,
];

export interface ChainOpts {
chainUrl?: string
chainStartBlock: Uint64
chainPk?: string
provider?: providers.JsonRpcProvider,
naAddress: Address
vpaAddress: Address
caAddress: Address
}

interface EthChain {
// Following Interfaces in Go have been implemented using EthClient.provider (ethers Provider)
// bind.ContractBackend (github.com/ethereum/go-ethereum/accounts/abi/bind)
Expand Down Expand Up @@ -226,41 +239,58 @@ export class EthChainService implements ChainService {
private async checkForMissedEvents(startBlock: Uint64): Promise<void> {
// Fetch the latest block
const latestBlock = await this.chain.provider.getBlock('latest');
const latestBlockNum = latestBlock.number;

this.logger(JSONbigNative.stringify({
msg: 'checking for missed chain events',
startBlock,
currentBlock: latestBlock.number,
currentBlock: latestBlockNum,
}));

const query: ethers.providers.Filter = {
fromBlock: Number(startBlock),
toBlock: Number(latestBlock.number),
address: this.naAddress,
topics: [topicsToWatch],
};
// Loop through in chunks of MAX_EPOCHS
for (let currentStart = startBlock; currentStart <= latestBlockNum;) {
let currentEnd = currentStart + BigInt(MAX_EPOCHS);

let missedEvents;
try {
missedEvents = await this.chain.provider.getLogs(query);
} catch (err) {
this.logger(`failed to retrieve old chain logs. ${(err as Error).message}`);
if (currentEnd > latestBlockNum) {
currentEnd = BigInt(latestBlockNum);
}

let errorMsg = '*** To avoid this error, consider increasing the chainstartblock value in your configuration before restarting the node.';
errorMsg += ' Note that this may cause your node to miss chain events emitted prior to the chainstartblock.';
// Create a query for the current chunk
const query: ethers.providers.Filter = {
fromBlock: Number(currentStart),
toBlock: Number(currentEnd),
address: this.naAddress,
topics: [topicsToWatch],
};

// Fetch logs for the current chunk
let missedEvents;
try {
// eslint-disable-next-line no-await-in-loop
missedEvents = await this.chain.provider.getLogs(query);
} catch (err) {
this.logger(`failed to retrieve old chain logs. ${(err as Error).message}`);

let errorMsg = '*** To avoid this error, consider increasing the chainstartblock value in your configuration before restarting the node.';
errorMsg += ' Note that this may cause your node to miss chain events emitted prior to the chainstartblock.';

this.logger(errorMsg);
throw err;
}

this.logger(errorMsg);
throw err;
}
this.logger(JSONbigNative.stringify({
msg: 'finished checking for missed chain events in range',
fromBlock: currentStart,
toBlock: currentEnd,
numMissedEvents: missedEvents.length,
}));

this.logger(JSON.stringify({
msg: 'finished checking for missed chain events',
numMissedEvents: missedEvents.length,
}));
for (let i = 0; i < missedEvents.length; i += 1) {
const event = missedEvents[i];
this.eventTracker.push(event);
}

for (let i = 0; i < missedEvents.length; i += 1) {
const event = missedEvents[i];
this.eventTracker.push(event);
currentStart = currentEnd + BigInt(1); // Move to the next chunk
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/nitro-node/src/node/engine/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,6 @@ export class Engine {
assert(this.msg);
for await (const message of msgs) {
message.from = this.store.getAddress();
this.logMessage(message, Outgoing);

if (METRICS_ENABLED) {
this.recordMessageMetrics(message);
Expand All @@ -957,7 +956,10 @@ export class Engine {
await this.msg.send(message);
} catch (err) {
this.logger(err);
throw err;
}

this.logMessage(message, Outgoing);
}
} finally {
this.wg!.done();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ const ERR_PROTOCOL_FAIL = 'protocol selection failed';
const ERR_PEER_NOT_FOUND = 'peer info not found';
const ERR_PEER_DIAL_FAILED = 'peer dial failed';

export interface MessageOpts {
scAddr?: Address;
peer: Peer,
}

// BasicPeerInfo contains the basic information about a peer
export interface BasicPeerInfo {
id: PeerId;
Expand All @@ -59,7 +64,7 @@ async function parseBasicPeerInfo(raw: string): Promise<BasicPeerInfo> {
interface ConstructorOptions {
toEngine: ReadWriteChannel<Message>;
peers: SafeSyncMap<PeerId>;
me: Address;
scAddr: Address;
newPeerInfo: ReadWriteChannel<BasicPeerInfo>;
logger: debug.Debugger;
key?: PrivateKey;
Expand All @@ -73,9 +78,9 @@ export class P2PMessageService implements MessageService {

private peers?: SafeSyncMap<PeerId>;

private me: Address = ethers.constants.AddressZero;
private scAddr: Address = ethers.constants.AddressZero;

private key?: PrivateKey;
private privateKey?: PrivateKey;

private p2pHost?: Libp2p;

Expand All @@ -94,24 +99,29 @@ export class P2PMessageService implements MessageService {

// newMessageService returns a running P2PMessageService listening on the given ip, port and message key.
static async newMessageService(
me: Address,
peer: Peer,
logWriter?: WritableStream,
opts: MessageOpts,
): Promise<P2PMessageService> {
const ms = new P2PMessageService({
toEngine: Channel<Message>(BUFFER_SIZE),
newPeerInfo: Channel<BasicPeerInfo>(BUFFER_SIZE),
peers: new SafeSyncMap<PeerId>(),
me,
scAddr: opts.scAddr!,
logger: log,
});

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

const messageKey = await unmarshalPrivateKey(ms.peer.peerId.privateKey!);
ms.key = messageKey;
let messageKey;
try {
messageKey = await unmarshalPrivateKey(ms.peer.peerId.privateKey!);
} catch (err) {
ms.checkError(err as Error);
}

assert(messageKey);
ms.privateKey = messageKey;

assert(ms.peer.node);
ms.p2pHost = ms.peer.node;
Expand All @@ -131,8 +141,8 @@ export class P2PMessageService implements MessageService {
async id(): Promise<PeerId> {
const PeerIdFactory = await import('@libp2p/peer-id-factory');

assert(this.key);
return PeerIdFactory.createFromPrivKey(this.key);
assert(this.privateKey);
return PeerIdFactory.createFromPrivKey(this.privateKey);
}

// Custom Method to exchange info with already connected peers
Expand Down Expand Up @@ -295,7 +305,7 @@ export class P2PMessageService implements MessageService {
const peerId = await this.id();
const basicPeerInfo: BasicPeerInfo = {
id: peerId,
address: this.me,
address: this.scAddr,
};

try {
Expand Down Expand Up @@ -465,7 +475,7 @@ export class P2PMessageService implements MessageService {
// Used for adding peers that support transports other than tcp
async addPeerByMultiaddr(clientAddress: Address, multiaddrString: string) {
// Ignore ourselves
if (clientAddress === this.me) {
if (clientAddress === this.scAddr) {
return;
}

Expand Down
Loading

0 comments on commit e0d500d

Please sign in to comment.