-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update to ILPv4 + LPI2 #401
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM, but I think @sentientwaffle should also review this because he's more familiar with a lot of the code this PR touches
package.json
Outdated
"five-bells-shared": "^25.1.0", | ||
"ilp": "~11.2.0", | ||
"ilp-packet": "~1.3.0", | ||
"ilp-packet": "^2.0.0", | ||
"ilp-plugin-bells": "^15.0.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we stop including plugin bells?
const fulfillPromise = promiseRetry(retryOpts, function (retry, number) { | ||
return Promise.resolve(plugin.fulfillCondition(sourceTransferID, fulfillment, fulfillmentData)) | ||
.catch(function (err) { | ||
if (shouldRetry(err)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add fulfillment retry logic to the ilp-compat-plugin
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do think that fulfillment retries should be in the plugin because it depends on the ledger protocol if and how you want to retry.
Should it be in ilp-plugin-compat? I don't think it's worth the effort. Retry logic is an optimization, it's only to minimize unnecessary connector losses. I'd say we migrate to LPI2 and then add retry logic per plugin. Different plugins may want to handle retries quite differently, depending on whether they're TCP or UDP based, session or request based or to deal with some other ledger/transport-specific quirks.
src/lib/utils.js
Outdated
@@ -22,40 +20,6 @@ function getPairs (arr) { | |||
}, []) | |||
} | |||
|
|||
/** | |||
* Deterministically generate a UUID from a secret and a public input. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now Mojaloop depends on having the same transfer ID across all of the ledgers. Taking the transfer id out of the LPI will make it more difficult to upgrade the connector used in that project. Do we have any alternative way of attaching the transfer ID from the previous ledger and making the connector use the same one on the outgoing transfer, or do we need to just make Mojaloop not depend on having the same transfer IDs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM except for a couple minor notes
src/lib/config.js
Outdated
@@ -149,6 +149,8 @@ function getLocalConfig () { | |||
features.debugReplyNotifications = | |||
Config.castBool(Config.getEnv(envPrefix, 'DEBUG_REPLY_NOTIFICATIONS')) | |||
|
|||
const account = Config.getEnv(envPrefix, 'ILP_ADDRESS') || 'unknown' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add ILP_ADDRESS
to the README.
src/lib/route-broadcaster.js
Outdated
@@ -39,9 +39,10 @@ class RouteBroadcaster { | |||
|
|||
this.autoloadPeers = config.autoloadPeers | |||
this.defaultPeers = config.peers | |||
// peersByLedger is stored in the form { ledgerPrefix ⇒ { connectorAddress ⇒ true } } | |||
// peersByLedger is stored in the form { ledgerPrefix ⇒ true } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/peersByLedger/peers
src/lib/route-broadcaster.js
Outdated
// timeout the plugin.sendRequest Promise just so we don't have it hanging around forever | ||
timeout: this.routeBroadcastInterval | ||
}) | ||
const broadcastPromise = this.ledgers.getPlugin(adjacentLedger).sendRequest({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this.ledgers.getPlugin(adjacentLedger)
return an LPI1 or an LPI2 interface? Because the sendRequest
method, needed for broadcasting routes, seems to be absent in LPI2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not in the spec but it is in the ilp-compat-plugin
as a deprecated feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sendRequest
won't exist when compat()
returns the original LPI2 plugin (https://github.com/interledgerjs/ilp-compat-plugin/blob/d8331126a92ad2b65a7e7c4513ce44c13d485340/index.js#L31-L33).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So then this PR will break route broadcasts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LPI1 plugins will work thanks to compat
; LPI2 plugins will be broken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They should be switched to use either interledger payments or the internet
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in a similar way to @justmoon 's DHCP on ILP proposal (for asking your peer to assign you an ILP address), it would be easy to send a payment to your peer for which the destination is something like peer.broadcast
, and the condition is either trivially fulfillable (if you're paying to send broadcasts) or unfulfillable (if you're not paying to send broadcasts). I don't see why that's worse/harder than using messaging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, that's what plugin-xrp-escrow already does. And if a BTP connection is available between the connectors then they can just use a BTP Message packet. But that's what plugin.sendRequest
already nicely abstracts, and we agree that ilp-connector needs it, so then instead of removing code from the stack, we have just split the ledger plugin into a separate transfer plugin and messaging plugin, which is just moving code around.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't actually need a messaging plugin anymore though, because transfers are expected to be just as fast as messages. And setting the amount to 1 means that the impact on your liquidity is negligible. This just lets us keep plugins as simple as possible, which should make them less buggy and more secure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ben and I discussed this on Slack and reduced this question to interledger/rfcs#357
4cc5e35
to
6279cac
Compare
Codecov Report
@@ Coverage Diff @@
## master #401 +/- ##
===========================================
- Coverage 84.82% 74.54% -10.28%
===========================================
Files 22 51 +29
Lines 336 1756 +1420
Branches 49 278 +229
===========================================
+ Hits 285 1309 +1024
- Misses 51 447 +396
Continue to review full report at Codecov.
|
9d17bb5
to
b23fe4a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks awesome to me. @sentientwaffle should definitely review this again
src/controllers/ildcp-host.js
Outdated
writer.writeVarOctetString(Buffer.from(sourceAccount, 'ascii')) | ||
writer.writeUInt8(info.currencyScale) | ||
writer.writeVarOctetString(Buffer.from(info.currency, 'utf8')) | ||
return writer.getBuffer() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This encoding should probably be added to the ilp-packet
module
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I wasn't sure where to put it. It's not ilp-packet layer, so I don't think ilp-packet is actually the right place unless we turn that into ilp-formats. Plus - is it really worth modularizing four lines of code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough
src/controllers/ilp-prepare.js
Outdated
const { nextHop, nextHopPacket } = await this.routeBuilder.getNextHopPacket(sourceAccount, parsedPacket) | ||
|
||
log.debug('sending outbound ilp prepare. destination=%s amount=%s', destination, nextHopPacket.amount) | ||
const result = await this.sendDataToPeer(nextHop, IlpPacket.serializeIlpPrepare(nextHopPacket)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we modify the ILP packet in place rather than re-encoding? (That could also be a separate PR though)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imho, that's premature optimization. Not long ago, we were ok going with a text-based format that would have been an order of magnitude slower. It's just not worth worrying about trying to get zero-copy performance at this stage. There are much lower hanging fruit.
src/errors/remote-quote-error.js
Outdated
|
||
// TODO: Is this the right error code? Maybe we should pass on the error we | ||
// we received where possible? | ||
this.ilpErrorCode = codes.T00_INTERNAL_ERROR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure these errors will be temporary if other connectors just stop supporting ILQP
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point - what's the right error code here? Or should we add one?
(I ran into a few cases where I was like: might be good to add a code for this - or make an existing code less specific.)
src/lib/plugin-store.js
Outdated
logging: log.debug, | ||
omitNull: true, | ||
// All transactions should be done with isolation level SERIALIZABLE | ||
// TOOD: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this TODO for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This "TOOD" was going to say: So we still need SERIALIZABLE isolation level. (My guess is no.)
src/services/balances.js
Outdated
} | ||
|
||
handleMoney (amount) { | ||
// TODO: Implement balance logic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be implemented ASAP after this PR is merged
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I think that's a job for @sharafian - he's written that code about 15 times already since it used to be in each plugin.
src/services/route-builder.js
Outdated
|
||
// Make sure that neither amount exceeds 15 significant digits. | ||
if (rate.gt(1)) { | ||
return new LiquidityCurve([ [0, 0], [ PROBE_AMOUNT / rate, PROBE_AMOUNT ] ]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be using bignumber.js' math functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copied the existing code to make sure the results are exactly the same - I still like that idea so we don't have to update the tests as much. But we'll see once we get to updating the integration tests how it'll play out.
README.md
Outdated
}, { | ||
"targetPrefix": "cny.", | ||
"connectorLedger": "ilpdemo.red." | ||
"connectorAccount": "lpdemo.red.cny_connector" | ||
"peerAddress": "ilpdemo.red." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the peerAddress
for this route should be ilpdemo.blue.
(or something other than ilpdemo.red.
), otherwise it will be caught by the targetPrefix: ""
route anyway.
src/services/route-broadcaster.js
Outdated
|
||
async start () { | ||
try { | ||
await this.reloadLocalRoutes() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.reloadLocalRoutes
is sync, no need to await
.
src/services/route-broadcaster.js
Outdated
await new Promise(resolve => setTimeout(resolve, this.config.routeBroadcastInterval)) | ||
|
||
try { | ||
await this.reloadLocalRoutes() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.reloadLocalRoutes
is sync, no need to await
.
src/backends/fixerio/index.js
Outdated
* @param {String} params.destination_ledger The URI of the destination ledger | ||
* @param {String} params.source_currency The source currency | ||
* @param {String} params.destination_currency The destination currency | ||
* @param {String} sourceAccount The URI of the source ledger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are ILP addresses, not ledger URIs (the code is correct; the comment is wrong).
src/backends/fixerio/index.js
Outdated
* @param {String} params.destination_ledger The URI of the destination ledger | ||
* @param {String} params.source_amount The amount of the source asset we want to send | ||
* @param {String} params.destination_amount The amount of the destination asset we want to send | ||
* @param {String} params.sourceAccount The URI of the source ledger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sourceAccount
and destinationAccount
are ILP addresses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not anymore after config refactor.
src/controllers/ilp-prepare.js
Outdated
}) | ||
} | ||
|
||
this.backend.submitPayment({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the submitPayment
call be inside of the result[0] === TYPE_ILP_FULFILL
block? Otherwise it might be submitted even if sendDataToPeer
replied with a rejection.
src/lib/utils.js
Outdated
|
||
if (nextSegmentEnd === -1) { | ||
prefix = address | ||
return false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to return false
, just return
.
src/routing/peer.js
Outdated
const PrefixMap = require('./prefix-map') | ||
const log = require('../common').log.create('routing-peer') | ||
|
||
const PEER_PROTOCOL_PREFIX = 'peer' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ilp-prepare.js
declares this as 'peer.'
.
src/routing/peer.js
Outdated
const newRoutes = routes.filter(route => route.epoch > this.epoch && route.nextHop !== this.address).map(route => ({ | ||
source_ledger: this.address, | ||
destination_ledger: route.prefix, | ||
min_message_window: 1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should reference the configured minMessageWindow
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed for now.
src/routing/peer.js
Outdated
data: { | ||
new_routes: newRoutes, | ||
hold_down_time: holdDownTime, | ||
unreachable_through_me: unreachableAccounts, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unreachableThroughMe
, not unreachableAccounts
.
src/services/accounts.js
Outdated
|
||
const store = this.store.getPluginStore(accountAddress) | ||
|
||
creds.options.prefix = accountAddress |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this line necessary (see line 93 prefix: accountAddress + '.',
)? If it is, shouldn't the prefix be set with a + '.'
suffix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
src/services/config.js
Outdated
features.debugReplyNotifications = | ||
Config.castBool(Config.getEnv(envPrefix, 'DEBUG_REPLY_NOTIFICATIONS')) | ||
|
||
this.address = Config.getEnv(envPrefix, 'ILP_ADDRESS') || '' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This parameter should be documented in the README.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, README needs a major rewrite...
4bc732c
to
5ced0f2
Compare
5ced0f2
to
913ca83
Compare
this._verifyLedgerIsConnected(nextHop.destinationLedger) | ||
|
||
// As long as the fxSpread > slippage, the connector won't lose money. | ||
const expectedSourceAmount = new BigNumber(nextHop.sourceAmount).times(1 - this.slippage) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This slippage computation wasn't added back in the refactor (the config option still exists, though).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
f7cf20e
to
3a3cc48
Compare
3a3cc48
to
5d0867d
Compare
README.md
Outdated
| -------------- | ------- | ------------------------------------------------------------------------ | | ||
| `[]` | object | Object describing middleware instance. | | ||
| `[].type` | string | NPM module that should be `require`d to load the middleware constructor. | | ||
| `[].priority` | integer | Priority at which this middleware should be inserted. Must be unique. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this field implemented? I don't see it used in the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, it was from a previous version, I'll take it out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
Both of these changes are needed to get the integration tests passing.
README.md
Outdated
| `*.assetCode` | string | Currency code or other asset identifier that will be passed to the backend to select the correct rate for this account. | | ||
| `*.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. | | ||
| `*.balance` | object | _Optional_ Defines whether the connector should maintain and enforce a balance for this account. This setting is enforced by the built-in `balance` middleware. | | ||
| `*.balance.maximumDebt` | string | How much money (in atomic units) this counterparty is allowed to owe us before we start rejecting their packets. Each incoming ILP prepare packet increases the amount owed, each incoming settlement lowers it. Provided as an integer in a string. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be balance.maximum
(or the balance middleware needs to change).
9030e87
to
28c6873
Compare
28c6873
to
b33434a
Compare
8a8a992
to
bb8f737
Compare
Woohoo! What a monster PR. Nicely work @justmoon |
Depends on interledger-deprecated/ilp-compat-plugin#7