UNPKG

34.9 kBMarkdownView Raw
1# ILP Connector [![npm][npm-image]][npm-url] [![circle][circle-image]][circle-url] [![codecov][codecov-image]][codecov-url]
2
3[npm-image]: https://img.shields.io/npm/v/ilp-connector.svg?style=flat
4
5[npm-url]: https://npmjs.org/package/ilp-connector
6
7[circle-image]: https://circleci.com/gh/interledgerjs/ilp-connector.svg?style=shield
8
9[circle-url]: https://circleci.com/gh/interledgerjs/ilp-connector
10
11[codecov-image]: https://codecov.io/gh/interledgerjs/ilp-connector/branch/master/graph/badge.svg
12
13[codecov-url]: https://codecov.io/gh/interledgerjs/ilp-connector
14
15> A reference implementation of the ILP Connector
16
17## Table of Contents
18
19* [Overview](#overview)
20
21 * [What is this?](#what-is-this)
22 * [Who is this for?](#who-is-this-for)
23
24* [Quickstart](#quickstart)
25
26* [Guides](#guides)
27
28 * [Connect your connector to the Interledger](#connect-your-connector-to-the-interledger)
29 * [Allow your apps to connect to your connector](#allow-your-apps-to-connect-to-your-connector)
30 * [Embed a connector in another JavaScript app](#embed-a-connector-in-another-javascript-app)
31 * [Create a tier-1 connector](#create-a-tier-1-connector)
32 * [Run the connector in Docker](#run-the-connector-in-docker)
33
34* [Reference](#reference)
35
36 * [Configuration Variables](#configuration-variables)
37 * [API Reference](#api-reference)
38 * [Extensibility: Plugins](#extensibility-plugins)
39 * [Extensibility: Stores](#extensibility-stores)
40 * [Extensibility: Middlewares](#extensibility-middlewares)
41 * [Extensibility: Backends](#extensibility-backends)
42
43* [Development](#development)
44
45## Overview
46
47### What is this?
48
49This is a JavaScript reference implementation of an [Interledger](https://interledger.org) connector. Find out more about the [Interledger architecture](https://interledger.org/rfcs/0001-interledger-architecture/) and [Interledger protocol](https://interledger.org/rfcs/0003-interledger-protocol/).
50
51An Interledger connector forwards Interledger packets, just like an Internet router forward Internet packets. The difference is that Interledger packets represent value in addition to data. Interledger connectors do not actually move the money, they rely on [plugins](https://interledger.org/rfcs/0004-ledger-plugin-interface/) for settlement. Plugins may settle by making a payment on an external payment system like ACH or they may use payments channels over a digital asset ledger like XRP Ledger or Bitcoin. Some plugins may not settle at all - this is useful for example when the plugin connects two hosts owned by the same person.
52
53### Who is this for?
54
55Just like IP routers can be found anywhere from your home wifi router to a small business network to the large Internet backbones, an Interledger connector is a versatile component that appears in a lot of different context. Here are some example use cases:
56
57#### Your personal connector
58
59You could be a developer who runs an Interledger connector so you can point all of your apps to it. This would allow you to change Interledger providers just by reconfiguring your connector without having to update the credentials in every single one of your apps.
60
61It also gives you a single place to manage your money. This version of the connector is pretty rudimentary, but in the future it will be able to tell you which app spent how much and when they sent it, how much each app earned etc.
62
63#### The heart of an Interledger Service Provider (ILSP)
64
65An Interledger Service Provider (ILSP) is the Interledger equivalent of an Internet Service Provider (ISP). It's an entity that provides access to the Interledger network for its users.
66
67Each ILSP needs to have one or more connectors to route the ILP packets from its customers to the Interledger and vice versa.
68
69Some ILSPs are simply customers of a larger ILSP. Others are so-called tier-1 ILSPs. Tier-1 ILSPs have a special responsibility, they provide routing services for the network.
70
71This implementation of the connector contains a routing protocol implementation for tier-1 connectors. Please note that in order to become a tier-1 connector you need to have a relationship with one or more existing tier-1 connectors and they need to trust you not to overwhelm them with traffic or harbor malicious customers on your network.
72
73## Timekeeping
74
75Timekeeping is an important part of processing transactions. Your node must have the right time set to make sure it can handle packets from peers correctly. If you drift too far from the current time, your node will have a different time to your peers and might start to experience strange issues and/or accept/reject packets incorrectly.
76
77It is highly recommended you run some kind of time synchronisation service on your server. If you need help to install tools for keeping your clock in sync, this article describes how to do it:
78https://www.techrepublic.com/blog/data-center/syncing-time-in-linux-and-windows-with-ntp/
79
80## Quickstart
81
82```sh
83npm install -g ilp-connector ilp-plugin-btp
84CONNECTOR_STORE_PATH=~/.connector-data CONNECTOR_ACCOUNTS='{}' CONNECTOR_ADMIN_API=true CONNECTOR_ILP_ADDRESS=test.quickstart ilp-connector
85```
86
87You are now running a connector!
88
89##### What's next?
90
91* [Connect your connector to the Interledger](#connect-your-connector-to-the-interledger)
92* [Allow your apps to connect to your connector](#allow-your-apps-to-connect-to-your-connector)
93* [Embed a connector in another JavaScript app](#embed-a-connector-in-another-javascript-app)
94* [Create a tier-1 connector](#create-a-tier-1-connector)
95* [Run the connector in Docker](#run-the-connector-in-docker)
96
97## Guides
98
99### Connect your connector to the Interledger
100
101In order to connect your connector to the Interledger, you need to configure a plugin representing your account with an Interledger Service Provider (ILSP).
102
103You will configure this plugin as a `parent` plugin, which means that your connector will automatically fetch its ILP address from this ILSP and send all its traffic through it. Your ILSP will tell you the other settings you need to use.
104
105From here on your configuration will get more complicated. So let's use the [PM2](https://github.com/Unitech/pm2) process manager, which allows us to specify our configuration in one tidy JavaScript config file.
106
107##### launch.config.js
108
109```js
110'use strict'
111
112const path = require('path')
113
114const parentConnector = {
115 // This tells our connector that this is our main upstream link which will
116 // automatically make it our default route and load our ILP address from it.
117 relation: 'parent',
118 assetScale: 6,
119 assetCode: 'XRP',
120 plugin: 'ilp-plugin-xrp-asym',
121 options: {
122
123 }
124}
125
126const connectorApp = {
127 name: 'connector',
128 env: {
129 // The one-to-one backend will use an exchange rate of 1:1 for everything
130 CONNECTOR_BACKEND: 'one-to-one',
131
132 // We don't want to charge any fee
133 CONNECTOR_SPREAD: '0',
134
135 // Where is our database stored
136 CONNECTOR_STORE_PATH: '/home/bob/connector',
137
138 // Configure our plugins
139 CONNECTOR_ACCOUNTS: JSON.stringify({
140 // `up` is an arbitrary name we give to our parent connector
141 up: parentConnector
142 })
143 },
144 script: path.resolve(process.execPath, '../../lib/node_modules/ilp-connector/src/index.js')
145}
146
147module.exports = { apps: [ connectorApp ] }
148```
149
150Now we can run our connector with:
151
152```sh
153npm install -g pm2
154pm2 start launch.config.js
155```
156
157### Allow your apps to connect to your connector
158
159**TODO**
160
161### Embed a connector in another JavaScript app
162
163**TODO**
164
165### Create a tier-1 connector
166
167**TODO**
168
169### Run the connector in Docker
170
171This project can be run in a [Docker](https://www.docker.com/) container.
172
173```sh
174docker run -it --rm -e CONNECTOR_SPREAD='0.005' interledgerjs/ilp-connector
175```
176
177Breaking down that command:
178
179* `-it` Run ILP Connector in an interactive terminal.
180* `--rm` Delete container when it's done running.
181* `-e CONNECTOR_SPREAD='0.005'` Set the connector's spread to 0.5%. This is an example for how to pass configuration to the connector.
182
183## Reference
184
185### Configuration Variables
186
187<!-- WARNING: This section is auto-generated. Please do not edit in README.md -->
188
189#### `env`
190
191* Environment: `CONNECTOR_ENV`
192* Type: `string`
193* Default: `"test"`
194
195Determines what type of network the connector is a part of. Can be: 'production', 'test'. Default: 'test'
196
197#### `ilpAddress`
198
199* Environment: `CONNECTOR_ILP_ADDRESS`
200* Type: `string`
201* Default: `"unknown"`
202
203ILP address of the connector. This property can be omitted if an account with `relation=parent` is configured under `accounts`.
204
205#### `ilpAddressInheritFrom`
206
207* Environment: `CONNECTOR_ILP_ADDRESS_INHERIT_FROM`
208* Type: `string`
209* Default: `""`
210
211If there are multiple parents, and `ilpAddress` is not set explicit, specify the account ID of the parent that we should load our address from. Defaults to the first parent in the `accounts` map.
212
213#### `accounts`
214
215* Environment: `CONNECTOR_ACCOUNTS`
216* Type: `object`
217* Default: `{}`
218
219| Name | Type | Description |
220| ------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
221| `*` | object | Description of individual account. |
222| `*.relation` | string | Relationship between the connector and the counterparty that the account is with. |
223| `*.plugin` | string | Name or instance of the ILP plugin that should be used for this account. A plugin instance can only be passed when instantiating the connector from JavaScript. |
224| `*.assetCode` | string | Currency code or other asset identifier that will be passed to the backend to select the correct rate for this account. |
225| `*.assetScale` | integer | Interledger amounts are integers, but most currencies are typically represented as fractional units, e.g. cents. This property defines how many Interledger units make up one regular units. For dollars, this would usually be set to 9, so that Interledger amounts are expressed in nanodollars. |
226| `*.balance` | object | _Optional_ Defines whether the connector should maintain and enforce a balance for this account. The balance is always from the connector's perspective. Therefore, a negative balance implies the connector owes money to the counterparty and a positive balance implies the counterparty owes money to the connector. This setting is enforced by the built-in `balance` middleware. |
227| `*.balance.maximum` | string | Maximum balance (in this account's indivisible base units) the connector will allow. The connector will reject incoming packets if they would put it above this balance. The format is a string containing an integer (which may be prefixed with `-` to indicate a negative value), `"-Infinity"` or `"Infinity"`. |
228| `*.balance.minimum` | string | _Optional_ Minimum balance (in this account's indivisible base units) the connector must maintain. The connector will reject outgoing packets if they would put it below this balance. The format is a string containing an integer (which may be prefixed with `-` to indicate a negative value), `"-Infinity"` or `"Infinity"`. |
229| `*.balance.settleThreshold` | string | _Optional_ Balance (in this account's indivisible base units) numerically below which the connector will automatically initiate a settlement. The format is a string containing an integer (which may be prefixed with `-` to indicate a negative value) or `"-Infinity"`. |
230| `*.balance.settleTo` | string | _Optional_ Balance (in this account's indivisible base units) the connector will attempt to reach when settling. The format is an integer (which may be prefixed with `-` to indicate a negative value) as a string. |
231| `*.deduplicate.cleanupInterval` | integer | _Optional_ Frequency at which the connector removes old deduplicate records. (in milliseconds; defaults to 30 seconds) |
232| `*.deduplicate.packetLifetime` | integer | _Optional_ Lifetime of a cache record. (in milliseconds; defaults to 30 seconds) |
233| `*.ilpAddressSegment` | string | _Optional_ What segment will be appended to the connector's ILP address to form this account's ILP address. Only applicable to accounts with `relation=child`. Defaults to the id of the account, i.e. the key used in the `accounts` config object. |
234| `*.maxPacketAmount` | string | _Optional_ Maximum amount per packet for incoming prepare packets. Connector will reject any incoming prepare packets from this account with a higher amount. Amount should be provided as an integer in a string (in atomic units). This setting is enforced by the built-in `maxPacketAmount` middleware. |
235| `*.options.*` | object | _Optional_ |
236| `*.rateLimit` | object | _Optional_ Maximum rate of incoming packets. Limit is implemented as a token bucket with a constant refill rate. When the token bucket is empty, all requests are immediately rejected. This setting is enforced by the built-in `rateLimit` middleware. |
237| `*.rateLimit.capacity` | integer | _Optional_ Maximum number of tokens in the bucket. |
238| `*.rateLimit.refillCount` | integer | _Optional_ How many tokens are refilled per period. The default refill period is one second, so this would be the average number of requests per second. |
239| `*.rateLimit.refillPeriod` | integer | _Optional_ Length of time (in milliseconds) during which the token balance increases by `refillCount` tokens. Defaults to one second. |
240| `*.receiveRoutes` | boolean | _Optional_ Whether we should receive and process route broadcasts from this peer. Defaults to `false` for `relation=child` and `true` otherwise. |
241| `*.sendRoutes` | boolean | _Optional_ Whether we should broadcast routes to this peer. Defaults to `false` for `relation=child` and `true` otherwise. |
242| `*.throughput` | object | _Optional_ Configuration to limit the total amount sent via Interledger per unit of time. This setting is enforced by the built-in `throughput` middleware. |
243| `*.throughput.incomingAmount` | string | _Optional_ Maximum incoming throughput amount (in atomic units; per second) for incoming packets. If this setting is not set, the incoming throughput limit is disabled. |
244| `*.throughput.outgoingAmount` | string | _Optional_ Maximum throughput amount (in atomic units; per second) for outgoing packets. If this setting is not set, the outgoing throughput limit is disabled. |
245| `*.throughput.refillPeriod` | integer | _Optional_ Length of time (in milliseconds) during which the token balance increases by `incomingAmount`/`outgoingAmount` tokens. Defaults to one second. |
246
247#### `defaultRoute`
248
249* Environment: `CONNECTOR_DEFAULT_ROUTE`
250* Type: `string`
251* Default: `"auto"`
252
253Which account should be used as the default route for all other traffic. Can be set to empty string to disable the default route or 'auto' to automatically use the first parent in the `accounts` map. Default: 'auto'
254
255#### `routes`
256
257* Environment: `CONNECTOR_ROUTES`
258* Type: `array`
259* Default: `[]`
260
261| Name | Type | Description |
262| ----------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
263| `[]` | object | Description of a route entry. |
264| `[].targetPrefix` | string | ILP address prefix that this route applies to. Configured routes take precedence over the same or shorter prefixes that are local or published by peers. More specific prefixes will still take precedence. Prefixes should NOT include a trailing period. |
265| `[].peerId` | string | ID of the account that destinations matching `targetPrefix` should be forwarded to. Must be one of the accounts in `accounts`. |
266
267#### `spread`
268
269* Environment: `CONNECTOR_SPREAD`
270* Type: `number`
271* Default: `0.002`
272
273How much of a spread to add on top of the reference exchange rate. Determines the connector's margin.
274
275#### `minMessageWindow`
276
277* Environment: `CONNECTOR_MIN_MESSAGE_WINDOW`
278* Type: `integer`
279* Default: `1000`
280
281Minimum time the connector wants to budget for getting a message to the accounts its trading on. In milliseconds.
282
283#### `maxHoldTime`
284
285* Environment: `CONNECTOR_MAX_HOLD_TIME`
286* Type: `integer`
287* Default: `30000`
288
289Maximum duration (in milliseconds) the connector is willing to place funds on hold while waiting for the outcome of a transaction.
290
291#### `routeBroadcastEnabled`
292
293* Environment: `CONNECTOR_ROUTE_BROADCAST_ENABLED`
294* Type: `boolean`
295* Default: `true`
296
297Whether to broadcast known routes.
298
299#### `routeBroadcastInterval`
300
301* Environment: `CONNECTOR_ROUTE_BROADCAST_INTERVAL`
302* Type: `integer`
303* Default: `30000`
304
305Frequency at which the connector broadcasts its routes to adjacent connectors. (in milliseconds)
306
307#### `routeCleanupInterval`
308
309* Environment: `CONNECTOR_ROUTE_CLEANUP_INTERVAL`
310* Type: `integer`
311* Default: `1000`
312
313The frequency at which the connector checks for expired routes. (in milliseconds)
314
315#### `routeExpiry`
316
317* Environment: `CONNECTOR_ROUTE_EXPIRY`
318* Type: `integer`
319* Default: `45000`
320
321The maximum age of a route provided by this connector. (in milliseconds)
322
323#### `routingSecret`
324
325* Environment: `CONNECTOR_ROUTING_SECRET`
326* Type: `string`
327* Default: `""`
328
329Seed used for generating routing table auth values.
330
331#### `backend`
332
333* Environment: `CONNECTOR_BACKEND`
334* Type: `string`
335* Default: `"ecb"`
336
337Name of the backend (can be built-in or a require-able module name). Built-in modules are: ecb, ecb-plus-xrp, ecb-plus-coinmarketcap, one-to-one
338
339#### `backendConfig`
340
341* Environment: `CONNECTOR_BACKEND_CONFIG`
342* Type: `object`
343* Default: `{}`
344
345Additional configuration for the backend.
346
347#### `store`
348
349* Environment: `CONNECTOR_STORE`
350* Type: `string`
351* Default: `"memory"`
352
353Name of the store (can be built-in or a require-able module name). Built-in modules are: memory
354
355#### `storePath`
356
357* Environment: `CONNECTOR_STORE_PATH`
358* Type: `string`
359* Default: `""`
360
361Shorthand for `config.storeConfig.path`.
362
363#### `storeConfig`
364
365* Environment: `CONNECTOR_STORE_CONFIG`
366* Type: `object`
367* Default: `{}`
368
369Additional options to be passed to the `store`'s constructor.
370
371#### `middlewares`
372
373* Environment: `CONNECTOR_MIDDLEWARES`
374* Type: `object`
375* Default: `{}`
376
377| Name | Type | Description |
378| ------------- | ------ | ------------------------------------------------------------------------ |
379| `*` | object | Object describing middleware instance. |
380| `*.type` | string | NPM module that should be `require`d to load the middleware constructor. |
381| `*.options.*` | object | _Optional_ |
382
383#### `disableMiddleware`
384
385* Environment: `CONNECTOR_DISABLE_MIDDLEWARE`
386* Type: `array`
387* Default: `[]`
388
389| Name | Type | Description |
390| ---- | ------ | ------------------------------------- |
391| `[]` | string | Name of the middleware to be removed. |
392
393#### `reflectPayments`
394
395* Environment: `CONNECTOR_REFLECT_PAYMENTS`
396* Type: `boolean`
397* Default: `true`
398
399Whether to allow routing payments back to the account that sent them.
400
401#### `initialConnectTimeout`
402
403* Environment: `CONNECTOR_INITIAL_CONNECT_TIMEOUT`
404* Type: `integer`
405* Default: `10000`
406
407How long the connector should wait for account plugins to connect before launching other subsystems. (in milliseconds)
408
409#### `adminApi`
410
411* Environment: `CONNECTOR_ADMIN_API`
412* Type: `boolean`
413* Default: `false`
414
415Whether the admin API is enabled or not. Default: false (disabled)
416
417#### `adminApiPort`
418
419* Environment: `CONNECTOR_ADMIN_API_PORT`
420* Type: `integer`
421* Default: `7780`
422
423Which port the admin API should listen on. Default: 7780
424
425#### `adminApiHost`
426
427* Environment: `CONNECTOR_ADMIN_API_HOST`
428* Type: `string`
429* Default: `"127.0.0.1"`
430
431Host to bind to. Warning: The admin API interface should never be made public! Default: '127.0.0.1'
432
433#### `collectDefaultMetrics`
434
435* Environment: `CONNECTOR_COLLECT_DEFAULT_METRICS`
436* Type: `boolean`
437* Default: `false`
438
439Whether the Prometheus exporter should include system metrics or not. Default: false (no)
440
441### API Reference
442
443### Extensibility: Plugins
444
445Plugins represent different ways to link senders, receivers and connectors together. Most plugins use [Bilateral Transfer Protocol (BTP)](https://github.com/interledger/rfcs/blob/master/0023-bilateral-transfer-protocol/0023-bilateral-transfer-protocol.md) in order to communicate. The main differences between plugins are whether they are **multi-user** and which **settlement ledger** they use.
446
447Multi-user plugins are plugins which connect to multiple counterparties, rather than just one. They are usually used as server-side plugins to serve a large number of clients. An example is [**ilp-plugin-mini-accounts**](https://github.com/interledgerjs/ilp-plugin-mini-accounts). Multi-user plugins actually contain a little mini connector internally which knows how to route packets to the correct client.
448
449Plugins implement the [Ledger Plugin Interface (LPI)](https://github.com/interledger/rfcs/pull/347). To write your own plugin, consider extending [ilp-plugin-btp](https://github.com/interledgerjs/ilp-plugin-btp) for single-user plugins and [ilp-plugin-mini-accounts](https://github.com/interledgerjs/ilp-plugin-mini-accounts) for multi-user plugins. Check the list below for plugins you can copy as a starting point.
450
451#### ilp-plugin-btp
452
453* Multi-user: No
454* Settlement: None
455* Github: [interledgerjs/ilp-plugin-btp](https://github.com/interledgerjs/ilp-plugin-btp)
456* NPM: [ilp-plugin-btp](https://www.npmjs.com/package/ilp-plugin-btp)
457
458Plain BTP plugin, used to connect two parties without settling. Often used as a client for [ilp-plugin-mini-accounts](#ilp-plugin-mini-accounts).
459
460#### ilp-plugin-mini-accounts
461
462* Multi-user: Yes
463* Settlement: None
464* Github: [interledgerjs/ilp-plugin-mini-accounts](https://github.com/interledgerjs/ilp-plugin-mini-accounts)
465* NPM: [ilp-plugin-mini-accounts](https://www.npmjs.com/package/ilp-plugin-mini-accounts)
466
467Plain BTP multi-user plugin. You could run mini-accounts on your connector and then connect all of your own clients to it.
468
469#### ilp-plugin-xrp-paychan
470
471* Multi-user: No
472* Settlement: [XRP Payment Channels](https://ripple.com/build/payment-channels-tutorial/)
473* Github: [interledgerjs/ilp-plugin-xrp-paychan](https://github.com/interledgerjs/ilp-plugin-xrp-paychan)
474* NPM: [ilp-plugin-xrp-paychan](https://www.npmjs.com/package/ilp-plugin-xrp-paychan)
475
476Basic plugin for peering with settlement over XRP payment channels.
477
478#### ilp-plugin-lightning
479
480* Multi-user: No
481* Settlement: [Lightning Network](https://lightning.network/)
482* Github: [interledgerjs/ilp-plugin-lightning](https://github.com/interledgerjs/ilp-plugin-lightning)
483* NPM: [ilp-plugin-lightning](https://www.npmjs.com/package/ilp-plugin-lightning)
484
485ILP peering using settlement over Lightning.
486
487### Extensibility: Stores
488
489Stores represent different means for persistence for the connector.
490
491#### Built-in: memory
492
493Pure in-memory store. Resets every time the connector is run. Useful for development and testing.
494
495### Extensibility: Middlewares
496
497#### Built-in: errorHandler
498
499* Pipelines: `incomingData`, `incomingMoney`
500
501First middleware in the pipeline. Handles any errors that occur anywhere else and converts them into ILP rejections.
502
503The `errorHandler` middleware will check the thrown error for a field called `ilpErrorCode` which should contain a three-character ILP error code. Otherwise it uses `'F00'` by default. For the `message` it uses the error message and for `triggeredBy` the connector's address. If the error object has a field `ilpErrorData` which is a `Buffer`, it will also attach the provided data to the error. Otherwise, it will attach an empty `data` buffer.
504
505#### Built-in: deduplicate
506
507* Pipelines: `startup`, `teardown`, `outgoingData`
508
509Prevents sending duplicate packets which helps reduce the impact of routing loops.
510
511This middleware keeps track of all prepared transfers. If there is a transfer with the same `destination`, `executionCondition`, `data` and an equal or greater `amount` and `expiresAt` already prepared, then we simply link the new packet to the existing packet's outcome. If a packet is being routed in a loop, it will fulfill these requirements, the loop will be terminated, and the packet will time out.
512
513See also: <https://github.com/interledger/rfcs/issues/330#issuecomment-348750488>
514
515#### Built-in: rateLimit
516
517* Pipelines: `incomingData`, `incomingMoney`, `outgoingData`, `outgoingMoney`
518
519Reduces the maximum number of total requests incoming from or outgoing to any account.
520
521Used for basic rate limiting and to help with DoS.
522
523#### Built-in: maxPacketAmount
524
525* Pipelines: `incomingData`, `outgoingData`
526
527Rejects packets with an amount greater than the specified value.
528
529#### Built-in: throughput
530
531* Pipelines: `incomingData`, `outgoingData`
532
533Limits the throughput for a given account. Throughput is the amount of money transferred per time.
534
535#### Built-in: balance
536
537* Pipelines: `startup`, `incomingData`, `incomingMoney`, `outgoingData`, `outgoingMoney`
538
539Tracks the balance of a given account from the perspective of the connector. This is also the subsystem that triggers settlements.
540
541#### Built-in: validateFulfillment
542
543* Pipelines: `outgoingData`
544
545Validates fulfillments in incoming ILP fulfill responses. If the fulfillment is invalid, it converts the fulfillment into a rejection.
546
547#### Built-in: expire
548
549* Pipelines: `outgoingData`
550
551Expires outgoing ILP packets at their designated `expiresAt` time. Returns a rejection when this occurs.
552
553#### Built-in: stats
554
555* Pipelines: `incomingData`, `incomingMoney`, `outgoingData`, `outgoingMoney`
556
557Tracks throughput by account. Results are accessible through the admin API.
558
559### Extensibility: Backends
560
561Backends provide fee policies and exchange rates. For a professionally run connector, just should create your own backend, using exchange rates that come directly from the exchange or broker where you plan to trade to re-balance your accounts.
562
563#### Built-in: one-to-one
564
565* Supported currencies: _any_
566
567The `one-to-one` backend applies the `CONNECTOR_SPREAD` setting, the `assetScale` settings, and otherwise uses a 1:1 exchange rate for all assets. This is the simplest backend, recommended for connectors that deal in only one currency.
568
569#### Built-in: ecb
570
571* Supported currencies: see [Euro foreign exchange reference rates](http://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html)
572
573The `ecb` backend loads fiat exchange rates from [Euro foreign exchange reference rates](http://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html). **Suitable for development and experimental use only.**
574
575#### Built-in: ecb-plus-xrp
576
577* Supported currencies: see [Euro foreign exchange reference rates](http://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html), XRP
578
579The `ecb-plus-xrp` backend loads fiat exchange rates from [Euro foreign exchange reference rates](http://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html) and XRP exchange rates from the [Ripple Data API](https://ripple.com/build/data-api-v2/). **Suitable for development and experimental use only.**
580
581#### Built-in: ecb-plus-coinmarketcap
582
583* Supported currencies: see [Euro foreign exchange reference rates](http://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html), see [CoinMarketCap](https://coinmarketcap.com/)
584
585The `ecb-plus-coinmarketcap` backend loads fiat exchange rates from [Euro foreign exchange reference rates](http://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html) and crypto-currency exchange rates from [CoinMarketCap](https://coinmarketcap.com/). **Suitable for development and experimental use only.**
586
587## Development
588
589If you would like to contribute back to this project, please follow these steps:
590
591#### Step 1: Clone repo
592
593```sh
594git clone https://github.com/interledgerjs/ilp-connector.git
595cd ilp-connector
596```
597
598#### Step 2: Install dependencies
599
600```sh
601npm install
602```
603
604#### Step 3: Run it!
605
606```sh
607CONNECTOR_STORE_PATH=~/.connector-data CONNECTOR_ACCOUNTS='{}' CONNECTOR_ILP_ADDRESS=test.quickstart npm start
608```
609
610#### Step 4: Read the contributor guidelines
611
612See [CONTRIBUTING.md](/CONTRIBUTING.md).