Skip to content
This repository has been archived by the owner on Jul 13, 2022. It is now read-only.

Feature: DNS TXT record support for PayID lookup #589

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,17 @@ const payId = 'alice$dev.payid.xpring.money'
// Send XRP to the given PayID.
const transactionHash = await xpringClient.send(amount, payId, wallet)
```
# PayID-DNS extension

This version of Xpring-JS will attempt to look up PayID information in a DNS TXT record before reverting to http/s protocol.

For example requesting PayID `richard$payid.link` for `xrpl-mainnet` will first attempt to retreive a TXT record at `xrpl-mainnet.richard._payid.payid.link`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

retrieve


If a JSON payload is available on that TXT record it will be processed using [PayID-Sign](https://github.com/codetsunami/payid-sign) to check authenticity, and, if verified, will be returned in place of the regular PayID http/s lookup.

This provides an alternative to running a http/s server for seldom changed crypto addresses while maintaining the benefits of PayID as a universal cryptocurrency address-book. It also provides a DDoS resistant way to ensure a PayID is always available.

To generate a JSON payload for your own DNS TXT record PayID please use the tool here: [PayID-Sign Commandline Tool](https://github.com/codetsunami/payid-sign-cmdline)

# Contributing

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xpring-js",
"version": "5.0.0",
"version": "5.1.0",
"description": "XpringJS provides a Javascript based SDK for interacting with the Ripple Ledger.",
"main": "build/index.js",
"repository": {
Expand Down Expand Up @@ -51,6 +51,7 @@
"typescript": "^3.9.3"
},
"dependencies": {
"payid-sign": ">=1.0.2",
"@grpc/grpc-js": "^1.1.1",
"axios": "^0.19.2",
"big-integer": "^1.6.48",
Expand Down
42 changes: 41 additions & 1 deletion src/PayID/pay-id-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import { PayIdUtils } from 'xpring-common-js'
import PayIdError, { PayIdErrorType } from './pay-id-error'
import { Address, DefaultApi, CryptoAddressDetails } from './Generated/api'
import * as PayIDSign from 'payid-sign'
import * as dns from 'dns'

const PAYID_API_VERSION = '1.0'

Expand Down Expand Up @@ -66,13 +68,33 @@ export default class PayIdClient {
return this.addressesForPayIdAndNetwork(payId, 'payid')
}

/**
* Look up a DNS txt record and return as a promise
*
* @param domain the domain name to look up for example arthur._payid.mydomain.com
*/
private async lookupDnsTxt(
domain:string
): Promise<string> {
return new Promise((resolve, reject) => {
dns.resolveTxt(domain, (err: any | null, addresses: string[][]) => {
if(err) reject(err)
try {
resolve(addresses[0].join(''))
} catch (error) {
reject('no txt record')
}
})
})
}

/**
* Return an array of {@link Address}es that match the inputs.
*
* @param payId The PayID to resolve.
* @param network The network to resolve on.
*/
private async addressesForPayIdAndNetwork(
public async addressesForPayIdAndNetwork(
payId: string,
network: string,
): Promise<Array<Address>> {
Expand All @@ -84,6 +106,24 @@ export default class PayIdClient {
// Swagger API adds the leading '/' in path automatically because it is part of the endpoint.
const path = payIdComponents.path.substring(1)

// First check if there is a DNS entry for this PayID, and use it preferentially if there is
const identifier = path.replace(/\+.+$/,'')
const domain = network + "." + identifier + "._payid." + payIdComponents.host
try {
const payload = await this.lookupDnsTxt(domain);
// execution to here is a DNS hit so process the DNS txt payload
if (PayIDSign.verify_signed_payid( identifier + '$' + payIdComponents.host, payload )) {
// if the DNS payload is not verified we will fall through to regular protocol,
// however if it is verified we will now return based on the DNS payload
var ret = JSON.parse(payload)
delete ret['signature']
delete ret['publicKey']
return [ret]
}
} catch (error) {
// this is a DNS miss, so continue executing along the normal path
}

const api = new DefaultApi(undefined, basePath)

const options = PayIdClient.makeOptionsWithAcceptTypes(
Expand Down