interledger / rfcs

Specifications for Interledger and related protocols
https://interledger.org
Other
453 stars 111 forks source link

Proposal: ILP Address Mapping #31

Closed justmoon closed 7 years ago

justmoon commented 8 years ago

So far we haven't explained how ILP addresses would actually map to local ledger addresses. We need a scheme similar to the Address Resolution Protocol (ARP) in the IP stack.

Flow

  1. When an Interledger module wants to send a payment, it must first determine whether the payment is local for any of its ledger plugins. Local means that the address starts with the ledger plugin's address prefix.
  2. If yes, send through that plugin.
  3. If no, resolve using ILP routing table to a connector's ILP address and send to that address. (All next hop addresses in the routing table MUST be local.) Typically, clients' routing tables would contain a single entry with a catch-all (gateway).

    Examples

We are us.fed.wf.alice.

Note: The send() API is simplified in this example.

core.isLocalAddress("us.fed.wf.bob") -> true
plugin.send("us.fed.wf.bob", "30.00")
  - Is local? Yes (otherwise throw exception)
  - My prefix is "us.fed.wf", so local identifier is "bob"

core.isLocalAddress("uk.gov.bofe.rbs.john") -> false
(not local, => look up in routing table - returns gateway ilp address)
plugin.send("us.fed.wf.connie", "30.00")
  - Is local? Yes (otherwise throw exception)
  - My prefix is "us.fed.wf", so local identifier is "connie"

core.isLocalAddress("us.fed.wf.nerd.frednoob") -> true
plugin.send("us.fed.wf.nerd.frednoob", "30.00")
  - Is local? Yes (otherwise throw exception)
  - My prefix is "us.fed.wf", so local identifier is "nerd"

Address Format Change

Before:

us.fed.wf/bob

After:

us.fed.wf.bob

Previously, we thought that being able to differentiate an account address from a ledger address was a useful feature. But as we investigated the details of the address resolution process, we discovered that this distinction makes it impossible for us.fed.wf/bob to be a ledger with subaccounts.

We were able to find the scheme outlined in this issue which properly allows for local address resolution without distinguishing between ledger addresses and account addresses.

Ledger Plugin API Changes

We want to add a new method to the ledger plugin:

getPrefix() -> Promise.<String>

getPrefix returns the ledger plugin's ILP address prefix (which may be configured, automatically detected, hard-coded, etc.)

If an ILP address begins with a ledger plugin's prefix, the address is considered local with respect to that plugin.

The call getAddress returns the ILP address. (Instead of the local account identifier.)

Ledger plugin configuration is custom per plugin (custom settings are no longer wrapped in an auth property.)

The account property in the send() call contains a ILP address (not a local address).

Some properties (such as _store) MAY be injected by the application hosting the plugin, however these are guaranteed to start with an underscore. (_)

Configuration Examples

new BellsPlugin({
  prefix: 'us.fed.wf', // defaults to the ledger's self-reported prefix
  account: 'https://red.ilpdemo.org/ledger/accounts/nerd',
  password: 'alice',
  _store: { ... }
})

// nerd
new VirtualPlugin({
  prefix: 'us.fed.wf.nerd.frednoob',
  token: '1234-1234',
  account: 'wootmeister',
  balance: '300',
  limit: '0',
  mqttHost: 'http://mqtt.example',
  secret: '245t',
  _store: { ... }
})

// noob
new VirtualPlugin({
  account: 'blahmeister',
  token: '1234-1234',
  mqttHost: 'http://mqtt.example'
})

with @bensharaf and @pedrorechez

clark800 commented 8 years ago

What are the thoughts on bob.wf.fed.us vs. us.fed.wf.bob? The former is more similar to internet addresses, whereas the latter is more like Java package naming.

emschwartz commented 8 years ago

Could you explain a bit more the change from us.fed.wf/bob to us.fed.wf.bob?

How does the last connector know whether it's supposed to send a local transfer to an account called bob or whether it's being asked whether it knows about a ledger called bob?

justmoon commented 8 years ago

What are the thoughts on bob.wf.fed.us vs. us.fed.wf.bob? The former is more similar to internet addresses, whereas the latter is more like Java package naming.

Left-to-right is more natural in English. URIs use it (scheme comes first) and paths use it (Windows, Linux and HTTP). DNS was initially used for email primarily, which is right-to-left, so that's presumably why DNS ended up right-to-left. This is one of the strange artifacts of history - you read a URI left-to-right until you get to the domain, which is right-to-left, then you continue reading the path left-to-right.

I assume Java packages, Android apps and other instances of reverse DNS notation are attempts to correct this odd choice.

Note that there is unfortunately no culturally neutral choice, we have to pick one or the other. We are using big-endian (most significant first) encodings for our binary protocols. Arabic numerals are written most significant first. So it makes sense to choose most significant first for our addressing scheme as well. But of course I'm interested to hear counter-arguments if there are any.

How does the last connector know whether it's supposed to send a local transfer to an account called bob or whether it's being asked whether it knows about a ledger called bob?

ilp-core will check whether a given address is local to any of the ledgers it is directly connected to and if not it will resolve it to an address that is local using its routing table.

One important note is that the connector doesn't need to know whether or not it is the last connector. It only needs to know what the next hop is.

So what would happen in your example is if I'm a connector and I get a payment with a destinationAccount of us.fed.wf.bob and I have a local ledger plugin with prefix us.fed.wf, then I would tell that plugin to deliver to bob. The plugin MAY resolve that name however it wants, in the case of ilp-plugin-bells it would simply send to the literal account bob.

If the address was, say, us.fed.wf.bob.clarice, then ilp-plugin-bells would still deliver to bob. The account bob may belong to a connector which may forward it to someone else or it may belong to a receiver who will handle the payment. Doesn't matter to me, the connector.

And if I don't have a local ledger plugin with prefix us.fed.wf, then I will forward the payment to whoever is my route for us.fed.wf.bob, e.g. us.fed.bofa.domestic.

emschwartz commented 8 years ago

Hmm. I still don't see how this mixing of accounts and ledgers would work. What if there is an account on the local ledger with the same name as another ledger? How would my local ledger plugin be able to resolve that and know whether to send it to that account or a connector that connects to that ledger?

Also, specifying the accounts you should send to within a path sounds like a kind of source routing. That's not necessarily a bad thing but it seems like accounts and ledgers should be denoted separately.

clark800 commented 8 years ago

Left-to-right is more natural in English.

This isn't obvious to me. Physical mailing addresses are ordered most-specific first, just like domain names.

I assume Java packages, Android apps and other instances of reverse DNS notation are attempts to correct this odd choice.

The linked articles says "A characteristic of reverse-DNS strings is that they are based on registered domain names, and are only reversed for sorting purposes."

I also was wondering the same things as Evan about how to tell if an address component refers to a ledger or an account. Why not just use email address syntax?

Also before we were using actual DNS addresses for ledgers, but here it looks like something else. Are we no longer using DNS addresses for ledgers, and if so why?

justmoon commented 8 years ago

The linked articles says "A characteristic of reverse-DNS strings is that they are based on registered domain names, and are only reversed for sorting purposes."

(Emphasis added.) Actually, that just made me realize a very strong technical reason why we would write addresses this way. We are routing first in the general direction of the destination and then get more and more specific. If we write addresses left-to-right, that means that we will be looking up prefixes in the routing table. If we write addresses right-to-left, we would be looking up suffixes.

Since most implementations of key/value stores allow you to look up keys by prefix, not suffix, we would have to reverse addresses all the time if we go with domain name (little endian) ordering.

What if there is an account on the local ledger with the same name as another ledger?

Then presumably that account is the omnibus account for that subledger. Avoiding name collisions is up to the ledger or ledger plugin.

How would my local ledger plugin be able to resolve that?

  1. Check if the address is me. If so, I'm done.
  2. Check if the address is a local ledger prefix. If so, send using the corresponding ledger plugin.
  3. Check if the address is a prefix in my routing table. If so, change the address to the next hop address and go to step 2.
  4. Else chop off the last segment of the address and go to step 2. If there is nothing more to chop off, stop and emit an error no route to account.

In other words longer prefixes trump shorter prefixes. When prefixes are the same length, local ledger prefixes trump routing table prefixes.

Suppose we have an address like us.wf.bob.mary.

We start in China. The connector in China has ledger plugins with the local prefixes cn.alipay and cn.unionpay and a routing table containing us => cn.alipay.yuhao. The longest known prefix in us.wf.bob.mary is us which points to a connector with the address cn.alipay.yuhao. So it sends the payment onwards to cn.alipay.yuhao.

The connector cn.alipay.yuhao also has an account in the US called us.bofa.yuhao. It recognizes local prefixes cn.alipay and us.bofa and has a routing entry us.wf => us.bofa.connie. The longest prefix it recognizes in us.wf.bob.mary is us.wf which according to its routing table should be forwarded to us.bofa.connie. So it sends the payment onward to us.bofa.connie.

The connector us.bofa.connie also has an account at WF called us.wf.connie. It recognizes local prefixes us.bofa and us.wf. The longest prefix it recognizes in us.wf.bob.mary is us.wf which is a local ledger prefix. So it sends the payment locally to us.wf.bob.mary. However, note that the ledger plugin only uses the first non-prefix element as the local account identifier, so it actually sends the payment to us.wf.bob.

The connector us.wf.bob is the gateway for a subledger by the same name. It recognizes local prefixes us.wf and us.wf.bob. The longest prefix it recognizes in us.wf.bob.mary is us.wf.bob which is the prefix of its local subledger. So it sends the payment to us.wf.bob.mary.


This does introduce a limitation - it assumes that subledgers have one gateway connector (as opposed to multiple.) I think this is a defensible assumption. It doesn't prohibit lateral routes - if there is a route in my routing table for the prefix us.wf.bob I will prefer that over the local prefix us.wf, because it is longer. You can also have multiple gateway connectors corresponding to different ledgers because those would be different addresses i.e. us.wf.bob vs us.acme.bob. And you can have multiple upstream gateways in general, because the routing is only used going to a ledger, not from it.

Also before we were using actual DNS addresses for ledgers, but here it looks like something else. Are we no longer using DNS addresses for ledgers, and if so why?

We were previously using opaque strings as ledger names, e.g. https://red.ilpdemo.org/ledger. This means that all ledgers must be in all routing tables, which doesn't scale to more than a few thousand ledgers. If we want to be able to support billions of ledgers in the Internet of Value we need to be able to route to prefixes. That way we can know how to route towards us.* without knowing every single ledger in the US. We could use something like fed.gov;acme.com;bob.example but it's simpler and more readable to just introduce new identifiers specifically designed for ILP routing, like us.acme.bob.

clark800 commented 8 years ago

So if I understand correctly, the dotted addresses are basically "partial path hints": they are "partial" in the sense that the path only goes to a well-known connector, not all the way to the sender, and they are "hints" in the sense that there may be a shorter/better path.

It may be possible to get these hints dynamically if ledgers and connectors provide sorted lists of next-hop ledgers and connectors in order of size/liquidity. So using the original DNS-based address format, you could query the ledger for it's connectors, and the first connector in the array would be the recommended gateway (for ledgers that don't support this query I assume the address would point to a connector with a virtual ledger plugin?). The sender could then query that connector for it's "biggest" ledger and proceed recursively until a known connector is found. If a cycle is detected, it can switch to the second biggest and continue searching the whole graph. In this way, the information that was statically-encoded in the dotted address could be obtained dynamically, which would be more robust (it can route around connectors and ledgers that are down if there is an alternative path).

Also, in any case, I'd like to suggest avoiding using countries as top-level names as it would be better to not support artificial boundaries in any way.

justmoon commented 8 years ago

It may be possible to get these hints dynamically if ledgers and connectors provide sorted lists of next-hop ledgers and connectors in order of size/liquidity.

Good observation! This would fall under auto-configuration (think DHCP) which we decided to keep out of the initial PR, but it is definitely planned for the future. For now, people running a connector just have to define their ILP addresses manually.

Also, in any case, I'd like to suggest avoiding using countries as top-level names as it would be better to not support artificial boundaries in any way.

Good point. ILP itself won't specify what it should be and it'll likely be different for different types of ledgers. (For a cryptocurrency for instance, a country doesn't really make sense, so it may start with the name of the crypto-currency instead.)

emschwartz commented 7 years ago

@justmoon can this issue be closed now?

justmoon commented 7 years ago

Yes. Let's make a mental note that every time we formally specify a ledger protocol/integration (e.g. Common Ledger API), one of the things to be addressed is how ILP addresses map to local ones.