Skip to content

Commit

Permalink
feat(rpc/connext): deposit & openchannel calls
Browse files Browse the repository at this point in the history
This refactors the `Deposit` and `OpenChannel` methods in `SwapClient`
to make them generic and applicable to Connext. It implements this
functionality for Connext, namely retrieiving an address for sending
funds on-chain to the Connext wallet and depositing those funds into
Connext channels.

It also refactors the `CloseChannel` call similarly, however the
Connext REST client does not currently implement a `/withdraw` endpoint
for taking funds out of Connext channels. This adds dummy code to
construct a withdraw payload and make a request.

Withdrawing coins from the on-chain Connext wallet is not currently
supported by the Connext client and so the `Withdraw` rpc call remains
specific to lnd.

Closes #1472. Closes #1473.
  • Loading branch information
sangaman committed May 25, 2020
1 parent 2ecbff2 commit edad1a8
Show file tree
Hide file tree
Showing 18 changed files with 551 additions and 372 deletions.
2 changes: 2 additions & 0 deletions docs/api.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 17 additions & 8 deletions lib/cli/commands/closechannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,35 @@ import { Arguments, Argv } from 'yargs';
import { CloseChannelRequest } from '../../proto/xudrpc_pb';
import { callback, loadXudClient } from '../command';

export const command = 'closechannel <node_identifier> <currency> [--force]';
export const command = 'closechannel <currency> [node_identifier ] [--force]';

export const describe = 'close any payment channels with a peer';

export const builder = (argv: Argv) => argv
.positional('node_identifier', {
description: 'the node key or alias of the connected peer to close the channel with',
type: 'string',
})
.positional('currency', {
description: 'the ticker symbol for the currency',
type: 'string',
})
.option('node_identifier', {
description: 'the node key or alias of the connected peer to close the channel with',
type: 'string',
})
.option('force', {
type: 'boolean',
description: 'whether to force close if the peer is offline',
})
.example('$0 closechannel 028599d05b18c0c3f8028915a17d603416f7276c822b6b2d20e71a3502bd0f9e0b BTC', 'close BTC channels by node key')
.example('$0 closechannel CheeseMonkey BTC', 'close BTC channels by alias')
.example('$0 closechannel CheeseMonkey BTC --force', 'force close BTC channels by alias');
.option('destination', {
type: 'string',
description: 'the on-chain address to send funds extracted from the channel',
})
.option('amount', {
type: 'number',
description: 'for Connext only - the amount to extract from the channel',
})
.example('$0 closechannel BTC 028599d05b18c0c3f8028915a17d603416f7276c822b6b2d20e71a3502bd0f9e0b', 'close BTC channels by node key')
.example('$0 closechannel BTC CheeseMonkey', 'close BTC channels by alias')
.example('$0 closechannel BTC CheeseMonkey --force', 'force close BTC channels by alias')
.example('$0 closechannel ETH --amount 0.1', '[UNIMPLEMENTED] remove 0.1 ETH from a Connext channel');

export const handler = async (argv: Arguments<any>) => {
const request = new CloseChannelRequest();
Expand Down
3 changes: 2 additions & 1 deletion lib/cli/commands/openchannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export const builder = (argv: Argv) => argv
})
.example('$0 openchannel BTC 0.1 028599d05b18c0c3f8028915a17d603416f7276c822b6b2d20e71a3502bd0f9e0b', 'open an 0.1 BTC channel by node key')
.example('$0 openchannel BTC 0.1 CheeseMonkey', 'open an 0.1 BTC channel by alias')
.example('$0 openchannel BTC 0.1 CheeseMonkey 0.05', 'open an 0.1 BTC channel by alias and push 0.05 to remote side');
.example('$0 openchannel BTC 0.1 CheeseMonkey 0.05', 'open an 0.1 BTC channel by alias and push 0.05 to remote side')
.example('$0 openchannel ETH 0.5', 'deposit 0.5 into an ETH Connext channel without specifying a remote node');

export const handler = async (argv: Arguments<any>) => {
const request = new OpenChannelRequest();
Expand Down
36 changes: 20 additions & 16 deletions lib/connextclient/ConnextClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import SwapClient, {
TradingLimits,
SwapClientInfo,
} from '../swaps/SwapClient';
import { SwapDeal } from '../swaps/types';
import { SwapDeal, CloseChannelParams, OpenChannelParams } from '../swaps/types';
import { UnitConverter } from '../utils/UnitConverter';
import errors, { errorCodes } from './errors';
import {
Expand Down Expand Up @@ -544,28 +544,32 @@ class ConnextClient extends SwapClient {
};
}

/**
* Deposits funds to a node
*/
public deposit = async (
{ currency, units }:
{ currency: string, units: number },
) => {
public deposit = async () => {
const clientConfig = await this.getClientConfig();
return clientConfig.signerAddress;
}

public openChannel = async ({ currency, units }: OpenChannelParams) => {
if (!currency) {
throw errors.CURRENCY_MISSING;
}
const assetId = this.getTokenAddress(currency);
await this.sendRequest('/deposit', 'POST', {
assetId,
amount: BigInt(units).toString(),
});
}

public async openChannel() {}

/**
* Closes a payment client.
* @param multisigAddress the address of the client to close
*/
public closeChannel = async (): Promise<void> => {
// not relevant for connext
public closeChannel = async ({ units, currency, destination }: CloseChannelParams): Promise<void> => {
if (!currency) {
throw errors.CURRENCY_MISSING;
}
// TODO: connext-api-client withdraw endpoint not currently implemented
await this.sendRequest('/withdraw', 'POST', {
recipient: destination,
amount: BigInt(units).toString(),
assetId: this.tokenAddresses.get(currency),
});
}

/**
Expand Down
38 changes: 31 additions & 7 deletions lib/lndclient/LndClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { LightningClient, WalletUnlockerClient } from '../proto/lndrpc_grpc_pb';
import * as lndrpc from '../proto/lndrpc_pb';
import swapErrors from '../swaps/errors';
import SwapClient, { ChannelBalance, ClientStatus, PaymentState, SwapClientInfo, TradingLimits } from '../swaps/SwapClient';
import { SwapDeal } from '../swaps/types';
import { SwapDeal, CloseChannelParams, OpenChannelParams } from '../swaps/types';
import { base64ToHex, hexToUint8Array } from '../utils/utils';
import errors from './errors';
import { Chain, ChannelCount, ClientMethods, LndClientConfig, LndInfo } from './types';
Expand Down Expand Up @@ -519,6 +519,11 @@ class LndClient extends SwapClient {
return this.unaryCall<lndrpc.ClosedChannelsRequest, lndrpc.ClosedChannelsResponse>('closedChannels', new lndrpc.ClosedChannelsRequest());
}

public deposit = async () => {
const depositAddress = await this.newAddress();
return depositAddress;
}

public withdraw = async ({ amount, destination, all = false, fee }: {
amount: number,
destination: string,
Expand Down Expand Up @@ -653,7 +658,7 @@ class LndClient extends SwapClient {
/**
* Gets a new address for the internal lnd wallet.
*/
public newAddress = async (addressType = lndrpc.AddressType.WITNESS_PUBKEY_HASH) => {
private newAddress = async (addressType = lndrpc.AddressType.WITNESS_PUBKEY_HASH) => {
const request = new lndrpc.NewAddressRequest();
request.setType(addressType);
const newAddressResponse = await this.unaryCall<lndrpc.NewAddressRequest, lndrpc.NewAddressResponse>('newAddress', request);
Expand Down Expand Up @@ -739,14 +744,17 @@ class LndClient extends SwapClient {
* Opens a channel given peerPubKey and amount.
*/
public openChannel = async (
{ peerIdentifier: peerPubKey, units, uris, pushUnits = 0 }:
{ peerIdentifier: string, units: number, uris?: string[], pushUnits?: number },
{ remoteIdentifier, units, uris, pushUnits = 0 }: OpenChannelParams,
): Promise<void> => {
if (!remoteIdentifier) {
// TODO: better handling for for unrecognized peers & force closing channels
throw new Error('peer not connected to swap client');
}
if (uris) {
await this.connectPeerAddresses(uris);
}

await this.openChannelSync(peerPubKey, units, pushUnits);
await this.openChannelSync(remoteIdentifier, units, pushUnits);
}

/**
Expand Down Expand Up @@ -1025,13 +1033,29 @@ class LndClient extends SwapClient {
}

/**
* Attempts to close an open channel.
* Closes any payment channels with a specified node.
*/
public closeChannel = (fundingTxId: string, outputIndex: number, force: boolean): Promise<void> => {
public closeChannel = async ({ remoteIdentifier, force = false }: CloseChannelParams) => {
const channels = (await this.listChannels()).getChannelsList();
const closePromises: Promise<void>[] = [];
channels.forEach((channel) => {
if (channel.getRemotePubkey() === remoteIdentifier) {
const [fundingTxId, outputIndex] = channel.getChannelPoint().split(':');
const closePromise = this.closeChannelSync(fundingTxId, Number(outputIndex), force);
closePromises.push(closePromise);
}
});
await Promise.all(closePromises);
}

/** A synchronous helper method for the closeChannel call */
public closeChannelSync = (fundingTxId: string, outputIndex: number, force: boolean): Promise<void> => {
return new Promise<void>((resolve, reject) => {
if (!this.lightning) {
throw(errors.UNAVAILABLE(this.currency, this.status));
}

// TODO: set delivery_address parameter after upgrading to 0.10+ lnd API proto definition
const request = new lndrpc.CloseChannelRequest();
const channelPoint = new lndrpc.ChannelPoint();
channelPoint.setFundingTxidStr(fundingTxId);
Expand Down
8 changes: 8 additions & 0 deletions lib/proto/xudrpc_pb.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 55 additions & 1 deletion lib/proto/xudrpc_pb.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 12 additions & 3 deletions lib/raidenclient/RaidenClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,15 +526,15 @@ class RaidenClient extends SwapClient {
* Creates a payment channel.
*/
public openChannel = async (
{ peerIdentifier: peerAddress, units, currency }:
{ peerIdentifier, units, currency }:
{ peerIdentifier: string, units: number, currency: string },
): Promise<void> => {
const tokenAddress = this.tokenAddresses.get(currency);
if (!tokenAddress) {
throw(errors.TOKEN_ADDRESS_NOT_FOUND);
}
await this.openChannelRequest({
partner_address: peerAddress,
partner_address: peerIdentifier,
token_address: tokenAddress,
total_deposit: units,
settle_timeout: 20660,
Expand All @@ -557,11 +557,15 @@ class RaidenClient extends SwapClient {
* Closes a payment channel.
* @param channel_address the address of the channel to close
*/
public closeChannel = async (channel_address: string): Promise<void> => {
public closeChannelRequest = async (channel_address: string): Promise<void> => {
const endpoint = `channels/${channel_address}`;
await this.sendRequest(endpoint, 'PATCH', { state: 'settled' });
}

public closeChannel = async () => {
// unimplemented
}

/**
* Sends a token payment through the Raiden network.
* @param target_address recipient of the payment
Expand Down Expand Up @@ -601,6 +605,11 @@ class RaidenClient extends SwapClient {
return body.our_address;
}

public deposit = async () => {
const depositAddress = await this.getAddress();
return depositAddress;
}

/** Raiden client specific cleanup. */
protected disconnect = () => {
this.setStatus(ClientStatus.Disconnected);
Expand Down
Loading

0 comments on commit edad1a8

Please sign in to comment.