diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml index 8b4429b79..8557d7b6b 100644 --- a/.github/workflows/integration_test.yml +++ b/.github/workflows/integration_test.yml @@ -1,16 +1,23 @@ -name: Integration Test -on: +name: Integration Tests +on: pull_request: branches: - master - + jobs: - build: - runs-on: ubuntu-18.04 + integration-test: + runs-on: ubuntu-18.04 strategy: fail-fast: false matrix: - test-name: ['boostPayment', 'botCreation', 'chatPayment', 'cleanup', 'clearAllChats', 'clearAllContacts', 'contacts', 'images', 'latestTest', 'lsats', 'paidMeet', 'paidTribeImages', 'queryRoutes', 'self', 'sphinxPeople', 'streamPayment', 'tribe', 'tribe3Escrow', 'tribe3Messages', 'tribe3Private', 'tribe3Profile', 'tribeEdit', 'tribeImages', 'messageLength', 'transportToken', 'pinnedMsg', 'hmac', 'socketIO', 'tribeMember'] + test-name: [ + 'boostPayment', 'botCreation', 'chatPayment', 'cleanup', 'clearAllChats', + 'clearAllContacts', 'contacts', 'images', 'latestTest', 'lsats', 'paidMeet', + 'paidTribeImages', 'queryRoutes', 'self', 'sphinxPeople', 'streamPayment', + 'tribe', 'tribe3Escrow', 'tribe3Messages', 'tribe3Private', 'tribe3Profile', + 'tribeEdit', 'tribeImages', 'messageLength', 'transportToken', 'pinnedMsg', + 'hmac', 'socketIO', 'tribeMember', 'ampMessage' + ] steps: - name: Enable docker.host.internal for Ubuntu run: | @@ -25,35 +32,36 @@ jobs: - name: Build Relay working-directory: ./relay run: | - npm install && npm run build && docker build -t sphinxlightning/sphinx-relay . + npm install && npm run build && docker build -t sphinxlightning/sphinx-relay . - name: Checkout stack run: | - git clone https://github.com/stakwork/sphinx-stack.git stack - - name: give permissions + git clone -b amp https://github.com/antonilol/sphinx-stack.git stack + - name: give permissions working-directory: ./stack run: | - chmod 777 ./bitcoind; - chmod 777 -R ./relay; - chmod 777 -R ./lnd; - chmod 777 -R ./proxy; - - name: Turn on Stack + chmod 777 ./bitcoind + chmod 777 -R ./relay + chmod 777 -R ./lnd + chmod 777 -R ./proxy + - name: Turn on Stack working-directory: ./stack run: | - GITACTION_ENV=gitactionenv docker-compose -f alts/proxy.yml --project-dir . up -d + GITACTION_ENV=gitactionenv docker-compose -f alts/proxy.yml --project-dir . up -d - name: Check for NODES.json working-directory: ./stack run: | - sleep 240; + sleep 4m docker-compose ps docker logs meme.sphinx docker logs dave.sphinx - docker wait stack_relaysetup_1; - - name: copy file - uses: canastro/copy-file-action@master - with: - source: "stack/relay/NODES.json" - target: "relay/src/tests/configs/nodes.json" + docker logs -f stack_relaysetup_1 + docker logs -f stack_lndsetup_1 + - name: copy nodes.json + run: | + mkdir relay/src/tests/configs + cp stack/relay/NODES.json relay/src/tests/configs/nodes.json - name: Run tests working-directory: ./relay - run: | - npx ava src/tests/controllers/${{matrix.test-name}}.test.ts --verbose --serial --timeout=2m +# run: npx ava src/tests/controllers/${{matrix.test-name}}.test.ts --verbose --serial --timeout=2m +# a script that runs the same test but allows retries when failed + a lot of logs + run: .github/workflows/test.sh ${{matrix.test-name}} diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh new file mode 100755 index 000000000..cdf5b9221 --- /dev/null +++ b/.github/workflows/test.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +delay=10 +retries=3 + +i=0 + +while true; do + if (npx ava src/tests/controllers/"$1".test.ts --verbose --serial --timeout=2m); then + break + else + ((i++)) + if [ $i -gt $retries ]; then + echo DEBUG LOGS + + echo -e "\n\n== alice chans ==\n\n" + docker exec alice-lnd.sphinx lncli -n regtest listchannels + + echo -e "\n\n=== bob chans ===\n\n" + docker exec bob-lnd.sphinx lncli -n regtest --rpcserver=localhost:10010 listchannels + + echo -e "\n\n== carol chans ==\n\n" + docker exec carol-lnd.sphinx lncli -n regtest --rpcserver=localhost:10011 listchannels + + echo -e "\n\n===== alice =====\n\n" + docker logs alice.sphinx + + echo -e "\n\n====== bob ======\n\n" + docker logs bob.sphinx + + echo -e "\n\n===== carol =====\n\n" + docker logs carol.sphinx + + exit 1 + fi + echo Test failed, retrying in "$delay"s "($i/$retries)" + sleep $delay + fi +done diff --git a/.gitignore b/.gitignore index a3dde2780..c809c56e0 100644 --- a/.gitignore +++ b/.gitignore @@ -41,5 +41,4 @@ creds/* !creds/scheduler_creds hsm_secret -src/tests/configs/* -src/tests/configs/nodes.json \ No newline at end of file +src/tests/configs/ diff --git a/src/grpc/interfaces.ts b/src/grpc/interfaces.ts index 6368dc94b..624d576a0 100644 --- a/src/grpc/interfaces.ts +++ b/src/grpc/interfaces.ts @@ -357,12 +357,14 @@ export interface KeysendRequest { final_cltv_delta: number dest: Buf dest_custom_records: DestCustomRecords - payment_hash: Buf + payment_hash?: Buf dest_features: number[] route_hints?: RouteHint[] fee_limit?: { [k: string]: number } fee_limit_sat?: number timeout_seconds?: number + // to use amp, unset payment_hash and dest_custom_records[`${LND_KEYSEND_KEY}`] + amp?: boolean } interface GreenlightHop { node_id: Buf @@ -530,6 +532,7 @@ export interface Invoice { htlcs: InvoiceHTLC[] features: { [k: string]: any } is_keysend: boolean + is_amp: boolean } // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface Payment { diff --git a/src/grpc/lightning.ts b/src/grpc/lightning.ts index 29607d0a0..7f60dff9c 100644 --- a/src/grpc/lightning.ts +++ b/src/grpc/lightning.ts @@ -20,7 +20,7 @@ import { Req } from '../types' const config = loadConfig() const LND_IP = config.lnd_ip || 'localhost' // const IS_LND = config.lightning_provider === "LND"; -const IS_GREENLIGHT = config.lightning_provider === 'GREENLIGHT' +export const IS_GREENLIGHT = config.lightning_provider === 'GREENLIGHT' export const LND_KEYSEND_KEY = 5482373484 export const SPHINX_CUSTOM_RECORD_KEY = 133773310 @@ -289,22 +289,29 @@ export function keysend( sphinxLogger.info('keysend', logging.Lightning) return new Promise(async function (resolve, reject) { try { - const preimage = ByteBuffer.wrap(crypto.randomBytes(32)) + const preimage = crypto.randomBytes(32) + const payment_hash = sha.sha256.arrayBuffer(preimage) const dest_custom_records = { [`${LND_KEYSEND_KEY}`]: preimage, } if (opts.extra_tlv) { Object.entries(opts.extra_tlv).forEach(([k, v]) => { - dest_custom_records[k] = ByteBuffer.fromUTF8(v) + dest_custom_records[k] = Buffer.from(v, 'utf8') }) } + // feature bits: + // 9: tlv-onion + // 15: payment-addr + // 30: amp (required) + const keysend_features = [ 9 ] + const amp_features = [ 9, 15, 30 ] const options: interfaces.KeysendRequest = { amt: Math.max(opts.amt, constants.min_sat_amount || 3), final_cltv_delta: 10, dest: ByteBuffer.fromHex(opts.dest), dest_custom_records, - payment_hash: sha.sha256.arrayBuffer(preimage.toBuffer()), - dest_features: [9], + payment_hash, + dest_features: keysend_features } if (opts.data) { options.dest_custom_records[`${SPHINX_CUSTOM_RECORD_KEY}`] = @@ -350,25 +357,46 @@ export function keysend( } }) } else { - // console.log("SEND sendPaymentV2", options) - // new sendPayment (with optional route hints) + delete options.payment_hash + delete dest_custom_records[`${LND_KEYSEND_KEY}`] + options.dest_features = amp_features + options.amp = true + options.fee_limit_sat = FEE_LIMIT_SAT options.timeout_seconds = 16 const router = await loadRouter() const call = router.sendPaymentV2(options) call.on('data', function (payment) { - const state = payment.status || payment.state + const ampState: string = payment.status || payment.state if (payment.payment_error) { reject(payment.payment_error) } else { - if (state === 'IN_FLIGHT') { - // do nothing - } else if (state === 'FAILED_NO_ROUTE') { - reject(payment.failure_reason || payment) - } else if (state === 'FAILED') { - reject(payment.failure_reason || payment) - } else if (state === 'SUCCEEDED') { + if (ampState === 'SUCCEEDED') { resolve(payment) + } else if (ampState !== 'IN_FLIGHT') { + sphinxLogger.debug(`AMP ${ampState}, trying keysend`, logging.Lightning) + // restore options + options.payment_hash = payment_hash + options.dest_custom_records[`${LND_KEYSEND_KEY}`] = preimage + options.dest_features = keysend_features + delete options.amp + const call = router.sendPaymentV2(options) + call.on('data', function (payment) { + const keysendState: string = payment.status || payment.state + if (payment.payment_error) { + reject(payment.payment_error) + } else { + if (keysendState === 'SUCCEEDED') { + resolve(payment) + } else if (keysendState !== 'IN_FLIGHT') { + sphinxLogger.debug(`AMP ${ampState} and keysend ${keysendState}`, logging.Lightning) + reject(payment.failure_reason || payment) + } + } + }) + call.on('error', function (err) { + reject(err) + }) } } }) @@ -859,7 +887,12 @@ export async function complexBalances( 0 ) const spendableBalance = channels.reduce( - (a, chan) => a + Math.max(0, parseInt(chan.local_balance) - parseInt(chan.local_chan_reserve_sat)), + (a, chan) => + a + + Math.max( + 0, + parseInt(chan.local_balance) - parseInt(chan.local_chan_reserve_sat) + ), 0 ) const response = await channelBalance(ownerPubkey) diff --git a/src/grpc/subscribe.ts b/src/grpc/subscribe.ts index 7450b4ec4..57f2751e9 100644 --- a/src/grpc/subscribe.ts +++ b/src/grpc/subscribe.ts @@ -31,7 +31,7 @@ export function subscribeInvoices( return } // console.log("IS KEYSEND", inv.is_keysend) - if (inv.is_keysend) { + if (inv.is_keysend || inv.is_amp) { parseKeysendInvoice(inv) } else { receiveNonKeysend(inv) diff --git a/src/network/receive.ts b/src/network/receive.ts index c1d93daa2..e1d14b2d3 100644 --- a/src/network/receive.ts +++ b/src/network/receive.ts @@ -31,6 +31,7 @@ import * as bolt11 from '@boltz/bolt11' import { loadConfig } from '../utils/config' import { sphinxLogger, logging } from '../utils/logger' import { Payload, AdminPayload } from './interfaces' +import * as sha from 'js-sha256' const config = loadConfig() /* @@ -514,12 +515,14 @@ const hashCache: { [k: string]: boolean } = {} export async function parseKeysendInvoice( i: interfaces.Invoice ): Promise { - try { - const hash = i.r_hash.toString('base64') - if (hashCache[hash]) return - hashCache[hash] = true - } catch (e) { - sphinxLogger.error('failed hash cache in parseKeysendInvoice') + if (Lightning.IS_GREENLIGHT) { + try { + const hash = sha.sha256.hex(JSON.stringify(i)) + if (hashCache[hash]) return + hashCache[hash] = true + } catch (e) { + sphinxLogger.error('failed hash cache in parseKeysendInvoice') + } } const recs = i.htlcs && i.htlcs[0] && i.htlcs[0].custom_records diff --git a/src/tests/configs/nodes.json b/src/tests/configs/nodes.json deleted file mode 100644 index db3c17c6f..000000000 --- a/src/tests/configs/nodes.json +++ /dev/null @@ -1,35 +0,0 @@ -[ - { - "alias": "alice", - "pubkey": "03a394d0ebf0d003124ab130c6b12b8b990a50a30a464354800a51981ba745bb07", - "ip": "http://alice.sphinx:3001", - "external_ip": "http://localhost:3001", - "authToken": "/3S3tM7OZxsgpgGcWghn", - "contact_key": "MIIBCgKCAQEA4qi6zHioHi3fnLOO5s/9YykchQI/DWGnRut5fmt1TaYyigd+zivgXrSndPoGp+J2rvzcavjIlOfbi+omxNNOMv4I9RY+pstcs+qMZ+l4WgtldxJbFGDlyXEBMQUh2AF1xT5arl13PzERvcCMeFViP17WeCvX31Zv4A9bFiXhREsaCQgdONxg5Cv7i9UNjAQnwiwXJerAH7XTL6xyRG2pu0mdyOf5GruGawn/Ux8Bdct/Y73b0DFPg47fBe2HZ8+OjwhIpRyFLKyUltjRYwqa3LVuX7grWXLTgiqWXRbQqj9UwwBevLWqf07qsXRk+EmoQJAuZCom0262W51CXobdQQIDAQAB", - "privkey": "MIIEogIBAAKCAQEA4qi6zHioHi3fnLOO5s/9YykchQI/DWGnRut5fmt1TaYyigd+zivgXrSndPoGp+J2rvzcavjIlOfbi+omxNNOMv4I9RY+pstcs+qMZ+l4WgtldxJbFGDlyXEBMQUh2AF1xT5arl13PzERvcCMeFViP17WeCvX31Zv4A9bFiXhREsaCQgdONxg5Cv7i9UNjAQnwiwXJerAH7XTL6xyRG2pu0mdyOf5GruGawn/Ux8Bdct/Y73b0DFPg47fBe2HZ8+OjwhIpRyFLKyUltjRYwqa3LVuX7grWXLTgiqWXRbQqj9UwwBevLWqf07qsXRk+EmoQJAuZCom0262W51CXobdQQIDAQABAoIBAH2a/5mcK5490bNnFgP2svamq7VR31stCKQWjgduyVy0g0FWkgDy863H8xyyCcozh43FduiY4Z8TNek9Yyqj5qv/dztKjoGkJMVXb2OOUDGmKx5nP631XKHbS8u8ilrHxFoACgSiqxiLuscq0y1EM+PmTHff5AtSPUEvgUyuxT0wTdhLv75SfSsigqQitzMT9pjZheMy0ZC+JG9OwWdD+7tejk/NcmaGnzy/t1F/nbfY7cEBzZ3qQZRU9I2iBoNEky6bJbsAn3sk+l8p1ncb+Ha6DLmRdCjZ9eYxYuIiJEZvY7YP3CJOiupVcGzTahgUOs7OjPEOfzffBFBrE73R+aECgYEA/1a2pY3pW4m2O9OuR1p/2RIrg0pXjjbie09lxV8+P5Qgc38fKgBe3H/c1rzJ6mnr9HzA+PC9Qm8ligXg6pAR1z7i2PoxmUvlS+IXWzTUgM4SeqG/lQ4k1Ra/9MG29HHz3lpkv4aT2mqjb+LQDKQTD5vcCZFPuEZK2PTl8OYgq4UCgYEA4z8AgQ5rxW/kBctfA7s3AxNrGDOnAsxO2EggGVzdZr7y2VnQsCw+psPkvPwufv4RJ7m5KBD6utubqyzcm+c2cg6Puv2Vg6CTBvtMkPIC2HhEVQ+m3VvFGZWzVu9RqTXbtYch5qvmQWuF8kFMtREwyCGV5/PcIr7VMsuBOWnPYY0CgYBggqMIkkoIylVx6YGvAJrgLkj6F+heHYqfNxRtdKFvoIpffUYHNxqj10Reef8Ltjt0Lo81yLCQYrhaofwwTIIhzGgrwO1sVs51TyCq3Z4QjVNYvLhQG4d6b13gojOH02ZsvdfkuHY8ESBzUtR2YrG9rZiOmTj7mr4BoRckZacljQKBgEF4CCwmPHRQTRBCTChmCm5PbN43G1maBOkYtm64K20LfCmUT6RByvb+nuq3eKnLhqO8RRS+941X8Zw6O4CK90ua2FvV7PpUC6fKRH/JyXoWBBHBhaBmbP12/IkCP7ORObAts12ijp6wXJGzsGbGAhjWXDrMLdKwyfltWdhxnHaNAoGAdACiH6mVQpPFA2eM3x87K/CTYQebZAwzu/IbLgEyqUn/Tc7Th1GhchsG1MdPnqIwIe2rCnbOzABY4eLcWOhxcK976taWjDNExjLFyYWHnVeVpjVKGaTzfI0TGRwLX3n3wmr53yBnCZWDWTVJYkqvRstFIcLpltGDn65vR2DiTBU=", - "exported_keys": "a2V5czo6QXdGTEpjTk5vNXFhSStyU29YY3JkcUtMay9iWEgvdjEzb1dOUUhvcjhjLyt1ZVJvYnppZktKSC9nV0VDL2xub3B5RytCNFBMaU82NHBhNHIzRyt4STk3NnB0a3ZYZWE2SGVFODVQbGV0eGIrb0x0eGdmSGlUTHFma29Wc1BwMnc2QlFwaWliMXdJT2ZTRzVmUmcxdy80Q2xBcmJ5VGdjRjJUd3NhZEZlWXJNRXFDcUFadGFsamVWS1RYVFYrL3cyUm9FZ214VFdtNHpiNjFkTHIxWG5hcXM5VWpqczd5NEJYdlNXT3VIZlVaekFjdTJ0U2d5R25VczlwbEJwU0p6RGFFWUVzeDFjMTdHcG9aeXFwRGVGRFNFNzltcXFRM3JvQU9OUFNaa1YrbkhWZHMxdFpWd1Z0UkFlWW1RWUpjbjFSSXlHTk4xZXUybWt0VkZ0M0IzYmNqZ1NPdHRSbzFNWlZRdFpiYXVSOVY0NWxpTW5vZE1DMEpUNnVWV0Y1TStlVTBVb1lOSDh5aHYzajdqTURCd280dnF0eW5ROURUUTRmT0pINi9wazhRdHVCSHNHT1VPdEFhTEsvQjFIZ2t0S281Mk1ZL1Z1b0tJTHlKVnI3SzlRc1QwbW5CMmRrYlJ2ci85UjJ3cWNEQnlFVWRDVFRQM3BqbVVReEdNb090bEZKVUFRd2NMVmN5U1dFdVE5bjlLUnBlQ2FiOGIxdDlaNWZ4eDgzbDUrUzQzZjFvMUZ4dnhkWlgySmZnMkx4NExMZUd4emp2a2I2T0JnYndwL1FqTnk4YU13RW44N3BBUDJVQ0IzQitReVdBM2ZFZStYMnJKZFN5Mkt4dzhWTFBwWG1QQWovQkxKWmZFT0dYZi9JNmUxOTNtQWxrRGJ5Z2pFNmRsUCs0VExqVTNOS0ZTK0JLRzU3VUhSM095L0dndm1SVkdpMXVDT0NISi91T0FOUFhXbUFCS2c5ZUw1TllUeEgxSmhkVG5ZT2RyeEdYTHArcERwMnc4U0hNTTIzcnY0VlBpWG9CbnNZS25mT2hSS1NPTDFLNU9XVzVMRFlVOWtvUTVqbWNTdnYvWEk5STBqWmlxOUF4WjZHKzU5Y2lZR0VBYU5KS3REL1Z2YStCYUJseExHaTJRb0pjWDJSbnpkbWQrSUZod2hWdFg4dEowUW5ZRFZXc0hFTGxpaUtnKzFsT1krdGVoNzdEbHE5UW4vUUpqQzVGUTJXZU04SjFESTIvb2xkelhaODM2QmhWUlZMZzJSTWZLcTBHTEYxZUV3cVJra1J0ZEFWQzFVdTIwdDJsYi9WdVRETWo0MDJmTmpKT05yd1ZlQXJ1WkJyVFY2OHI5RXdXaUZHd2JDVkM1NFlzY2MrZnAwbFh4V1ZXU1BkY0dzWXh4WHBVQitJTHdMNm9HdWZMRUVjbnJuYmQ0eVRjMWRVY05RSVBZUWRIaXRJbDRUTjhNQkc1SmVCLzJNT1VXS0xXUXVOa21PRXNIMXp6UWJLbmtwc3dVYTUyd1dKNnBzeTlrOGE1eENJZjBqNW1ZdHJWZUNwZTlKdWFQRDdyeUxjc016L29MWkx3TlBtdFlnUXZMM1NscFY2VmNSdmp0bmJNanc3SERWQ1BtY3NkZlllTnkySFRESDVzZGM0RUFJakJOT05LTTJNUFFKSnhTaWRQaHcrVWsyeDhiVmVMYWhjSWtIZU00Yy9nVVlyUnhoS3hIOEN4RTBwYVh6YkpGMDliNmQzU3FtR0JMZGVjOE9STGVGeEZDRjBsc1h2RUJ6d1UwdjVlWFFaRmxMbWc0UmUyKzF5REJoV1VIRUNsSjVxeHNQWWNpUUY2ZGs4ckgrb3AwNW9mbTFHM1FWYko4MGh0cVkyOG9GNnVQNGdqSUU4WEpaRWkyL0ZLb2lXTXFlbEdmNktDbGxhTzF2NVh4SFROSDc3Y1hoSnRURWEvSit3L3AzYVhqTlE0VmQ5TWtJbm16L0VvTjBHZUdCUnhSL2g5TnJ5V0RHNCt0UnhlOEk5RjRsMDZGL3RRWmJtR1NUZFhIWUw5d1o0Rjllb0p2TmpTQ2JRZUVqVkYyemNIcCtQYjNycDY3ZjVTZWVSSGNvdmFnbFVQRzdKTW4wR2hOdDJvMTlSbXVoL1Nnc2c5QlphWjlvUUhGbnIzTC9ZVCtja3dKUDJvM2Y0ZlB0VGlTY00wUmRRbmN5YkNWb2IzU3JZVVV3NzNxajZqVjFqQS9FWDhBdEliUU05cmxhYVVLRG8xQWtvZnNCR1Y1SlNzVktKTWFZN3B0Y0RuZzZadC93V1paV25mVEtaUHQ2cTlDeEU1Q0VGNjRNYk5ZQk9LL2hjV2hZQTFBZGsxc0Y3UG9CMmR1L1pLL3FmUFRNenhCQWVzZDg5NEZtYWI3Uk9hcm0zTEJmaGJvUjJDNlNEcHhCOEdvMUc3alFHNzIrNWFjcnlxVlBuVGI4b2J0ajRTTDJyaEc5d2RaMmpKK291KzVyeHN6dWMrNWp6U3J6S0hvdHJhV2V0SnFXc28xTitPWXRneXI2S2tTMzgwbXdNaDlYQ1l4VTFCejlFcWd0QmhSVEZGZ1Yvei9nNytCaVdYTklUTzdMNDIzbWRnK3pkT1MzN1U5Njg3dFNCa0c3Y0FUVU9Nd05IeVJRZm82aEY0OENrZDNJWGoyeXRGM1hIdFBHbTNSSUw2Z1BPc1VQVXM3ZlY2YnYraE5mZVVONC81SFpLcE14UWNuTWd3TjhmOFpVZWZIQWtKMENoTTZMK1grZnhDdkcvK290NVF2bTdwSnBlZjNSK0Z0UXRML1l6NmZFV1VEUUNEeENZd0d0WmlqMkJTMEJFanNsRFE0OGorUGpUcTdFNDFaYU5KNkNsbEZBSTRpeDZpYmxUbnFQcnN5TzlzaWQvZDQ3aTEzZGVLNVZwaTRaM1R6dk14V1A1NkRZQkNWdk1Ic0xDREZWaFd4bFd5c1FORjk0czQ0MDdZVWhtQjJwQmhocmg2dGt1eGVjY2xMZ3RDTWJhVCt6WjB5K2lYeEh4NFc4bnovTmxHTklKeFhMNTA3L3owVFJvaFFoZmlHS0s1VEVTZGtoTlZnME12c0hVZHJrMm5MdzRHbnpyNXZoQmhIa2RCbjFwendJdXV3eWNIaWxHZCtrczRTcTE2K2JaVmRuNmVyc2k3aWVJVU1PR3BDUTkyYUYwK0RZOEozVTY3ZE5DL3ljcWhkdmg5cldXR2duZHhOT0VmRVMvTlhjeTdOeDYrYnhwMEtlb3dzUFl0ejdiSjZJTXRCekE1L3hQNGR2OUd1Z3ZXbWY1aVErY3EzWjJBUWpIYndxVENKUUhpdXJGcFJRbXB5YnFISEs1bVcyMmxzRUwxRWo0WjZybWwzVEthOUlQTDZLcHZ3RlF4amF6bHA0RHNjM0pTYThaMThLak1zQ0FxM0xRYlpMNWdkeU40NEk2SVFNV0FYRzdWenJkbEtMeHNySU0rRmFtc3M0Z1ZaTkNDd255OGtCekt0S0lQKzVHSTdjbEQzS1FwSG5RN3ZOcjEvTDBta0VFUWN0bFZMaE5mcWFrbmlmc29uMzZweFFubkFlaHJRZmVTalJEL1M0ZnB1L2pvYnhxNWRxT2VxQXRGU1RzM0djcUg3RHVWcFBvY2xFYVBuTTVlQmtVdmZvZDBHQi9FUU5ac24xT3RyMFVUaz0=", - "pin": "111111" - }, - { - "alias": "bob", - "pubkey": "02a38857848aca6b32ebcc3c85d07ee41354988f4f1e0b4e6ccd255eee6ed75b8d", - "ip": "http://bob.sphinx:3002", - "external_ip": "http://localhost:3002", - "authToken": "BbANspHdDk5umWHei/0t", - "contact_key": "MIIBCgKCAQEAoSzSQ6XsQTi0SpO0ljU7F1ZM5LIW2dtYU4rR79KbK/EttDmJ7CPISIznyybwS5ANUocC1BWrJTezhSu7hOd1YoW8wU48J3aNn9I3eGzDV4F2hF1pw3iZ36oIiEvaPv2CYswXGocldRgGEurkTXU/4zcxYUYBqo4/ZCGWXm5M76QASggq2rp8auBMPU7jgoG5DXifsdiXKVQkYjCtD2SBBhvEwvHXVaBs2xJEr1aFBjGRqeoyqMbFfECM7RKe2JeeEz0y686/dAscxIgIQp18OEyd3aS92i+lrBv/MzdJHGoST6VQ1nNDKI8AIDPium2R4tp04TtOS7X3ytZet7RdZQIDAQAB", - "privkey": "MIIEowIBAAKCAQEAoSzSQ6XsQTi0SpO0ljU7F1ZM5LIW2dtYU4rR79KbK/EttDmJ7CPISIznyybwS5ANUocC1BWrJTezhSu7hOd1YoW8wU48J3aNn9I3eGzDV4F2hF1pw3iZ36oIiEvaPv2CYswXGocldRgGEurkTXU/4zcxYUYBqo4/ZCGWXm5M76QASggq2rp8auBMPU7jgoG5DXifsdiXKVQkYjCtD2SBBhvEwvHXVaBs2xJEr1aFBjGRqeoyqMbFfECM7RKe2JeeEz0y686/dAscxIgIQp18OEyd3aS92i+lrBv/MzdJHGoST6VQ1nNDKI8AIDPium2R4tp04TtOS7X3ytZet7RdZQIDAQABAoIBAEnWht2G/h1DJRWlZW4LJO8kEkYTcHSOEID1cAg2q/ANpaqTUaRlIDRcmTRwulVjwTa1s44F/0olopC34Eia4SQpsLe+Z8CMh4VYgSEicrt8DBfH09RBQ/07oH0QgwOokDtC1LLKh1aFkwLHqZ/yvzoQ5wZxVAIW+OOen4zTa1u9RGIigPKEsAos24sNYli8mtYghYdExwTu09oPwcH/AibgVwtkwsUh6DHqLLyFAde656iVfm1xkmHP18oupopL2rAAn/9VfXg+vB7/sKVoOxx1JEA+bm6tsrSPxfmOFlnMPw0SHk4rU4Va9X8PtAFfSYIR3lwHnPNHCy6JdH7lXmECgYEAzefwPrS+o1voTSbeAbG1lmTFYgFAHoODsN5qiwWGRbVqJw8/qH7NBK5Sv6KFzbVbf3DQdgzzVYRfOrSbq5bCuWdFxcMTgMTFIwYv+UW7+N7E033hrpcFk9RiX66iqke5ZzRtwZ3dHnVX9Smo2q813yv3hbwleM7cYPNy7NvgOAMCgYEAyGL9XJZZab77fqUh+52ls5YeKpecPM+ligRkJdfuKHBBOGAJGdBOR5rj9Nxere4pOhjtv5XBS2C0A8JnHm1mEwXo9ycMK/IV2Us82E50gXfQf6LKpHeEGQ3L0Gol2EQx2tfzLqFYq3s50LFU6b+cSVCtCv5KNmR21MxvWyQeHHcCgYEAon00g+eSwcuXqLrRW7jnugVQFqUx+mKCnCMJJSh4663cVXKdI9Tc6aK/vmb8/Hbv+8QFXgOeS5AormU0q9ZPWIx87TGixG2DvL33QdmcGyuBW/ztCiA30I88CpH/4Cy1zRH8w1xW0OB9RwVXMSwvcSx7KthtIcbs9r1npi9l5A0CgYBu94Jed1PkX0JeC4waDl7oaEUkSPjYJBUDrRLYcg8Mjb1QPr5LcTMoK/n4auBzaOOjrJJb+c4ks9R8EHeSPfq8phUwaIsw+spmnmd/UVLrLzJiu/+DvDuGtEEdlOqILJt/GP8t32N+8qo9izX+i4E8iFSLNe5a2J+FkABKdGHIWQKBgHgQ+dVIs9zakLbQCt62tGGYMR+8kgzQf2iZWFhz8ALnXD/LYChHLSiZWW9HcBjmObhCQs+ciCoSt0rWRUN+pjv/93+y+c8WHK9RAfAX1Hjxfo7C9OXtnHmbiabZuuLPQjGeohmkRw0e8ngqsZAbiRGTeN7acBhtf6IytgSG2yk7", - "exported_keys": "a2V5czo6QXdGc0tSVkZVZU1idCtnSENZWXV4Y2IxSTZoOVhiY0F3TlkxaE1qR0p0UmlIRDJXb00vdERyT3pheXJ2WGNyK2R5b2F3aElXcEI1dTZHV2hNRE1uaU1naVpLamlJTDlnSW00ZmFKb0VHbTMrYkNEbWNRbDMzWlplRUgxbEV2WDAzYnNyUkViNFZtYXZ2WUdDL3pRaGVQUWZGWUhRMGVNbzdsWGJuZExVUkUxWENUVDd1cUcrKzd5WDMrRXlDY2d4VHRuS0lwUXRwNHZIempjZXNMNDRRbU01bkc0K09vdlNidFd1RmhNSlR5QlFPemg0R1ZwUC9qUGF0eDlIYmRiUzhnb0dZaEIrQklzVGpjbFZMMCtFUUwxTHUySWdxWFU4bklhS2t4ZVlBQTFrRUEzTktMZ21tK0pNWkFZcFprcFhFelduL1R0ZnJkTVpYL1RNRStIV2dpN0xOa2lHS2VxR1hEQzBtYk1UMXlycHU3UUMrZkdTdzJZTjcybkZnZ25VMW9vdkRnaERyZTQvN0VXclE1NU5BNGFhYUdyT2hCaHFIOFVBTGc1WHFISGpkTzJxUjhidXJqcktCY0xvMS9vQnZjWnJaQ1lWTExXVzRqNjZ2QzdjTkdpb3psLy9EMzJLNmxtMm8xaUkxemo5UU5ZNWxvRmFFb1RsbVVWb290Q1g1RDk0bUFhYlEzMHNlcDlYN292ZCtvMUtyOFozWHBMS3lGWURuSmpCZ0hid1NwY2xkT3U1UG8xSjZmUFlNODFJRjVMY1MweWFNWGRjNFN2dldNeGxFcWNmMThLNkZSNCtrT1JhWlh3VlZWLy9zWUE5WUtTRlZISlJ1bHE0SHNOaGJucW5yUjNpVEJXeVRtQlU4ZjU1L0RWSWxQeDZNSmJkYlFpelQ5SzJuR3R0Y090OXRsdThFUE1aSUZkczBJUW5mQjZNdEwzR3pyQTFyaTF5akJVcHNCcmxUZkZHTHBReVhyeG1jTGNOOWpsaXVhQTgraytFYVVjYTlFUG1IbHNZekluQkFXOTFwRGZySmE4RWdKUG9odjhKQUxBTDliRWtKY3Rob0kvYnpzNGx6R2FTWXBEd3FxUXFhRHpBZk9EWWlvL3pSV3Vta2luNjZSRC9VeXgzU1lTV0IvQUxYNU9ZMHczL1JpQkpyNE04clJUaFJKMXR1dkxqanFrSWxRZVNnTWVoVGVaTkg1UFJCbzVrYmVnT2VOR0lKbDlXa1R5QlRRVHlrSnd5KysvbFJDY3l4czY4NENWQWNTZmZzSFhqQXhCRDdRYUZsRndWc05HSmxzNHpiR3l3VlVLV3gwVTQ4eTN1WUtoVHl1RjJEeDdMNU1ZVlRiekFINVVra09oYVNMdWdoOTIwREdOazlGRFFHUDBMR0dmU0dqZHF3Z1A3OFFZaFBFSjFTNkgyeHNSSTBTbnhwZFVubTB2Zmh2eHNKb051VFhjNUxmc0xkTWpiV21vSktwZ0FhMitvdVpYVmxiVVZValJaQjNmU1VVM1VCNGVGTlNCWm94TjI5TXpJVVNEVVRoOHlGekFrOFVSUzM0cG0vd0ZzSXpyYSthTVR5aHNLNmxyMm5WRlR6M1dnV2lYdEhkdFpYaFNuM0xYV0JySy9ZeXlBSzBpRnFKeCtDNExraWlMWjcyUzBqVWpIdjdHR2ErVmNLaWtQZGkxeVkvZkdwUGk3bXA2Ly9oRVYvQWRQd1g4Y3FzN2JiTGRoZkpzdWt3ZmMwL3o3VDJ3VmRpVHZlT1hQWjdzRld3amVaQnEyUDY0VUNDRG5Mbytla2FGMUVsZ1gvdWNtY0x5eVRheGN6WlNsSFFlUFo4SzNwQ1FvSU1Nek5ZZERWSHU0M09QUXlrRzRaSFJ6SzNLckQyOExBbTNaYWxaNlZXOFVicGJYSTFUZEhidndtR25DdVJsWndQQm52NmF6Ujk0TkI1b3pCNnpuaHhpVUpWdFVjYVNKVUQzUDhnRFFtY2FhbUNmUFpUQTUraW5uYkVCcy9PN1NZamZKdTVXMXNLNG9hcDNZSGpzdm1tcnRNWm5FWE1RYi85VVIveEJhOWlNOTgyRXZsYlQyK21MYkVxNElvZmpKbExON21JWi9TQUZIUHdMZjh5d1JZOHQrZmttQlpIT3cvbEV0cXFYMUQyUFlNc2cycDJHVEd4T0NVbFNBVE90UXVkVjk2T1FiTS9pVU5YVmhuK0JsZjVUYVpMLzZRN2ZUaGJ1b3BwKzFQeENmLzFGUTEwaDAvOEt2aU5SNHl1a3Y5ZGdFcDBzNFlyVlY2UmNYT3pxOVovS2FxR2lvL3hhZ081Nmw2MlNxSGRWOGFuUWZCbFM0NVpPc2M4VTRhczh5Q0JmQmppcG0xZy8wVlVMU3BmaHIxenpEdFYvSDd4WjgxL1pxK2liUmpVZHRVaCtCRUpCMlVVbWV5Y3R6cjVyWjZkdzJ3UFFXNmhsSXhxR0NPbTR3NTJmR0ExemlLZFAxeFZmQkx2b2FTaXNpNDFIcU5VdGU3U05ZWFVjRnRCTW9qbTY4ZHYvb1BzL2pMQXY1VEI2WXlQQ3h3WmVyRTY0V0xDcGYwRUR4ejA4blUxV3pZWWNwMXNVa1ZvMlMzS2x1Mlk2NSswZWV2aGM4aWwrcGFMK1JybkFvWnpEallxbFgvemZsSG8wL1hUY0FRNll1dzRMR2hEZUhma3J6alZnUjI1WFpncmlqZ2I2OVY1STROZkxvMit3RjgxOXlvTTJMTjk2VURLaG5EZitDTll1dWhGbjhmUThwQU9nOENoWWxDOVpEbXd0VjA5T2lZWDJVVU1hSFFBOGVOZ1dRVlpYUXRpbExodERMWSsra2k3alhDaVBnVFNwbnVVWjdUSVRMMWxHRlJBZ3ozN2ZZRUR4dzdnTE9yeU10NlU2aHVPd0lwMml1dnZYYmt1NXl1VTVuUmJDR2hNTlBZMkhrVkp5SW5pcFcvUlVIMENXaEYxcmdNU0NFVCs0MnlRMnEyQTdxdFJIVDBjVDFzeXpTaU1KZDlQMTlSSUtjaWZWcFdpb1ZqLzBuWlpwVWZNdktDWmpDdTVndHB2dk1FSnZSRVlDeHIvMXppSzJ2R3hBaUliNXJES3U0NWV6TGxPcmRkWXdOV2FpNFpJaVBPeHZhcjFGN2tpVm1uelp3WVN6dVorUTdoaFRaQnArMU9rc3hzanI1YS9UQXZ3RGxiRDU0VXl6Zklyc2JtN0VWbTFDczBZNmZuc0ZVcHFBeTcvVEtxcnIvc2h1bUNvWVloejBDQVE3RlF1YlJ4N0lzTS95VEEwUElGaHAyWFhMa25qV3RHbVRyQVNIQ1VsTTBkTUJlR0s5V3pDRzE1WUFESlpPOTlKdEVMWU9wMTJTSzdlMGIyQVkvTHNoWkZMU2VyblRGMU4zRFdZS0ozeXc5NHZMek11d0ZCcUlKY2VQeXpqdEdBVjdHMHpPaUt1RGF2QTk4MFRmOTc4cC9PRndwNVFHcFhVSHRVMUJvbUNPcWdPcDNSTW9RT2lLT2tnYmhnMHZFMVphQ2RMVmVRMG05YVFMOWwwQmNpTk4vRlV6RXRRUXFELzRBK2RKT05BZUlXUFFJeEZOSmZXZE1iQkppTlZsS0FLNGVZOExBcXRpSmEzWklDL2kwSDVUaHZ3VnJ0UzJUZmM1ZXJQOWR4eEg4d3p3b2tBST0=", - "pin": "111111" - }, - { - "alias": "carol", - "pubkey": "0364c05cbcbb9612036cc66297445a88bcfc21941fd816e17a56b54b0b52ff02b9", - "ip": "http://carol.sphinx:3003", - "external_ip": "http://localhost:3003", - "authToken": "zdcZoHGIMddIA5xO6I/Z", - "contact_key": "MIIBCgKCAQEAtAvyMIBFrRc9p14gvgn9znFkI0XeEnvUU7NtW0yOuvvWVpWsjI0bOaPRWL9N4GsNnHr2LJwqxBsdk5TaPd1fDkZiJWXQEYfPe3nQwRgIS10EzbKgF/XP1RSAuyvvP7lotalpDEyMPz9oCLtrtBBKoy4YbQhgtVZkNWo8z26jSlvQZR0QIa63tc+p0NzRq5NV0nKieqJidValahAuk3zb13gEnuoWh97QP71itez0EmQB9RugLTakMklHdbJAj5vrjFa0XJpZPqL+a9sXFC27UGMgJ79X8Gyoc8F1Htg52ma+FuETCKFc3YCVZiPo7uFhbcIosmTqeehpIWgoc1t9UQIDAQAB", - "privkey": "MIIEpAIBAAKCAQEAtAvyMIBFrRc9p14gvgn9znFkI0XeEnvUU7NtW0yOuvvWVpWsjI0bOaPRWL9N4GsNnHr2LJwqxBsdk5TaPd1fDkZiJWXQEYfPe3nQwRgIS10EzbKgF/XP1RSAuyvvP7lotalpDEyMPz9oCLtrtBBKoy4YbQhgtVZkNWo8z26jSlvQZR0QIa63tc+p0NzRq5NV0nKieqJidValahAuk3zb13gEnuoWh97QP71itez0EmQB9RugLTakMklHdbJAj5vrjFa0XJpZPqL+a9sXFC27UGMgJ79X8Gyoc8F1Htg52ma+FuETCKFc3YCVZiPo7uFhbcIosmTqeehpIWgoc1t9UQIDAQABAoIBAEQaBKitzC0Vjl+BLhdTxinRIBRrd/0702s5ghK3mYFGgqNLh/G96d9ahHPyIG3TcFmQ9Q6Yn2B/9Sb+dbJSyoKmwrbslNAcs7E7uWdgRLrk201G1GFbvBoX4D1r99n9soqMIszhI2dE/3/WDH4Wb3TbmaecNKBCpjLCFXBMo/0arKPbsvU/+aFlhLfbp2b2U/cu4oPT7KQXSjkvz61+vHGF1TPo1HvK4Ktpr/jB4Br2WQZpXma9rOw8lBnBQMLZvuXPLzPq+y0K5yRBQyBQ5foNIpXVieD2crdSyEAgshSJn+EiFoPzUj5hQihi14UlTlEoIUrieOnOk1+eYuqNqnUCgYEA5fFxlWBRiu4Nx7DIvjQ/9Q2e2j/Dorq0SyQ9aiT2GdsSNMxAYOfw2UgAIvdwNVapaMN3+QImlCOVFbsZkyHtEh4AuxC7rmrTIeOqX5K9RxPDLye3GLJGVSzRKJ+ddt+LrfbjOplIJzqASXYeP2oXXEr3IGH2hZd35XDovoqR6K8CgYEAyHMGq5UqFnL660Vy1du1X4jei9GsUQrYkSV3y/eoEEC4JzF/NYp0pZONQTS5ewJsiRiqBDb9ZQphx6+oAgmuC+I/Pl0FdIRALYFxRz5P/iVwa2iFakqhwkP1lJTdkTNZLImY8+T8ooThyN5x/r0S73RU7Sn3COsUCwuodWoWef8CgYBQYOcAfWvjsbVVcaGqg188DSfTSOjdMICnp6JkQUlZ3m9tz1xNw6Y4fee2UWKC8qlDetisTakorYuGziFxgjF53M0Klx3UKXlmZkv/Hq9Vcj4PdMWXN3JIgeMXXTxQ+nG8PQpPSJelRtjjVQvbxP0ngPkD89qV9k2uAZsKSCwa9wKBgQC/pMnhzlFY+UFRIyrxsIet+SvcvfBj41y/GPV6OyOhtl7aigY3aFKx42qPxxr2spCWWtBiCzklG1H1GsokeKDAl+UjeMg4aCD74OAu3zKXp6/d4SZ9QzF4jIUFM3bK1i8R19Q2it3HfAhqhdKzXI34Bl8UCKU5D5gwH0tGRtGKfQKBgQDgA//TrE/2rMHfl6VQzImeVkPvxd6wS18m2AyZQJ1XqJJ+R0JrINqiftmRusiFxRSBZ0jL3I5siNx3C8c3XFpWfYhA0jtHF0dRdlOvWxI0sWd4ZfD1MQKd2306c2SRXH9VYshyVt6mhKiYWb4syNIs6zvI/6HURKX2EsLPkkyhXg==", - "exported_keys": "a2V5czo6QXdFd1RObHQ3NXBaVDVhaEFvMU5oVnJVM1IrUW4xTnZTdUdnVEM1c1lHb0NUUlZnYndmdlVoSU9Dbkl6YkRFRzU2bnZXNElaSFB1UTJIVFZCNlNVVldBNFMySDg3NkRtdjFiSzBjbkJKbUdIbnZNaWVlVkVWZkVLQldkR2RLeHltOWErRDNkdjFSWnhmQWlhVE5hOWZ2VlpYTmhDTXFTSDlmRm4xSEdKMEpBVWk1eHhFYllUdnBCb1B5MGVJakhMbU1TcnIyV2NRMzNQcklGV2dZL2NZS3ZWUVRuMEszNlBlbzF4MjhydGl0Zmp5MzJjM1d5YU5MNS9QSUQ5bi9JLy8xV084bjZPVVliRWhkaW1nZmYyS3lQaG5QajZvOFc3a1BqNU9LSlpZbFR0STFKQllQK3ZCOEcxbGJlbGkrV0tWVmVvMnptL0graEZRN3MyZDAvWlBDU21YeXFvcWlLVGR2Z1FYOUJRaE5DUWxEQkF2WnBOWWc0VTlDcjhiV3NrT0pCcWJ6NExXWUtoRHRpeHVxSGpmUTh0bXd4aFBhdXp5V2diV2NNSUJiTEtkbHQrSm9qb0RIWFhXT3FidFptUC9FMFlQUCtlT1I4eG9KZlphN3BIT0U1WTlFYUY0STJmeGtRS01QdURsMWMzTWswQlRCbVdjZDg1c3hkbDVhRThyeHUxL3c5T00wSCtNakYyNVRCOGFzUC9oQW0zSkFKRFBNSk9wdVpKcVZuUDQxeUNQbGVEWjdoaVFXN05icGZoS041YklZVW9aQXIxTjZVZytQbW1PTkJOLzJaUTcwYTdUN2Y1MlJXTWdOcDQ4d0l3dlRtREtsdnJZSUJ3d1l1UnMxWm1CMHdFQWk5YlBHWnNzUUFRNW0reVliZjNqWm8xNlBVcDhNVmVMQ0ErTzNZNlFWVzFTaWM0MHNLcXJIZlpReWFKcWd3V2pWTDVOMUhScDJVdUE5bndRVmlJemtTdWxreEEzTTNBUjJ4NDNXelB0bWRENzh0STNPbHlXcmZmSmdRc29ocmRPZXJ4cDhhcTVvbG9vMUhxeGhHTWFTSEY3ZWYycGd6NGJjRXRzVHVSRFBhZ2Q5S05FdTY4dVQ0V1RIbkNXZC8zcit2VDQzb1lBYWxMaHBuNmRxNGE4aWVGZS9xVm5KSU0zQVozQURCdy9wWUhodFBjVTQ0b0ZLTmVHL2thSTFNU25CRlV6TE9WOFZOTkJnYzB4ZlJSbjVxcVZUb01LTDZyRm1yNGxPakNWYVp2V0cxQmw1YjlKVGtkZDN3NmpnK3VlSzJwdldiMWprRTR5THRmYktBWWdjSkpuUG5SeVBCanJkRW1iTVFXRDJaT2tLSmVaQ3FqYVQ2Y3ZGcnNwTGVERHhzaHI4NjdlcnQ2cVlUd2pxaDkycEYrRzEySVJ0bkxrc01kSGpZYTcreEVHNnlsWTNGSDVrUmpmWW01bXpsUUM3bXJZUDh1ZlpMbW93d2lUamVCd0ZzUlhUSS9mMXgvZzJCNHUrQTc5VzlIeitVV05VTWdVZU52cVlraE44OHdnY1FMSkxzRHdRNGNUSURjQ3dlYkw0bDBrZ1FkNDZnakpTR3BPb2k5QlJobFY4WlVqaVRkRjFObGpuMFR6d1crVFRoakJ2dmljTk10VlF0Uk93RnpEREtPd09KTC9vclMyTm10RkVxR1JyNnR1Z2RSZ2xQVGdvYnFuZldIbnlJaE1uZUhjT2NLemVDdUlhZ1o2NlVLVFhhb1BMUWpFN1pMWk5yWllTRDMxeEQ4UTJZdzdTVk85S3lvRW52Y0NuTFZoSHYzUGRzYnB5VGJRN3N1ai9WZzA2ZTJLWkRjUHFvRjRxTU1KT05RQkN5b21OOStkZ2VXZTFHUmpqZGM2eGhlSFJ3Nm95NndIYlpkOVp5RDlBeERENG1tV1JFY2poeE9ZdWhhbjl0YjcrdkpuUzJRbTUxSjdNSUVwSFpxSkZCeWVMcWVlb0pmOHI1elRTUWkxUVBIZklQV2tNRnRFclZOU29nZjhOUDZDRjZJQzU5d29uYjlkL2xzMmpSU01Vd0dXNnhzdGR3RHNBQlBWTE1ZVTIvc2RDS0htWkVlYlRQTTVxV2lNNEcxdTZsaFQxbDdvS3J6YkYvenc1MXMvZlAwZDlSclkwVUpHQ3B0L0xJYitPcmlXR0dxZ3hwY0l1dzV0NGJUUklNbmlxRkVxWWZ0MGJ5MEtZNjllSERSS1ExQWVBY2hhN25zOTk5MmIzZkVNaUhlUTNveHdqNVhPeHR5MysxWU4wQVlycWFmZnUzTXZsTVVpK1VwdkU5UU1oMmJsNi80dVVYN2VqQ2lJa1lhTktTZzl2Q0k3Uzh4RXJKYVBJTmdPcFNFdFF1eEErOHBQUmxsTWJxQnVVcDlodUFUSGNaeFpsaXVQRk1BcytESFNRR0FoVDN6ZGFZMElIQnNUZmpMb2tic0VLN3orWlU5cUREOHprMDJTOGJmNjRZOGllSHV1N0lISGJTS1NuaG52RTJsY0hYcUFCSFIxdk1VeTJKd0lYYUhUUXJkMmJLdVpQVjN4dDBCTWQ3ajlrMEdHdlhnOUlWS29BMFVweDQ1ZmgxZXk2eGQ2QTVnUlM3Vk90TzhCODgyZTExQ2ZDaUdLamNtVVJva1c3NTY4WnpjWVVqVG5WY1d3TU5MZUJXZVhUd1RTaFRxSThRandqYzNzRzM2SHdTaytER3gydXhhamdsNnZpVkJrYjdsMno2NjlDdFd5dTdOUUcxL1BXcU9lSVIzZ1hES0JyVUpBUDVKbzBSNzZHVXk1eXBoa0NIZ3hVUzdBOHJUUDV0KzRxOTVVaGpuUUp6c2VsSG9zbHN5WGJlZEtWZnBlUUQyTzV2ZWpDSmluU3VhQzYwekdTbjF4WEdjL0dGZVkrTXhNaDVZZExMSHN6UFNFWGs0NUROSGJvaEJhMEs3MU83NWp5U3p0RlFUNWQ0RWZLUW44Z3NDTFY2S1p0Wk90NlVZWXVmdmloSlpBV0hHQXpVVTBDYkFnVUJlSGNySXBnaUd6bitZVHlWYm9HVitDTGpEOFJmOWhkeVNsYjVhNzd2SG9FTkNCbm96dUt1VTBXaWY3US9sWC9iT0pabVRZRXYxd2hoaSs2amZwL3o2Y0UveitmdllEZ2xkRUFqNVdRbCtEbW5hTGZmK0hYdkphOWNWaGVJUzZkQ3VZejRuU3JIY0hZOUJ4bEhRcFhHVkFuTnBiVGw5QjB4VFhrT2tuZkMxeU5WYytYV1h6YzA0dkRzdWZpNC9oRytpTXRMM1BLdzd2aFFBRStBYWw0NWI5NG9PZ29EU3k2MmhmNDVjVU8yRVBFdEEvM256eFJ0WjNOcGQ5bzlzd29oT3ZnemV3akt5MjBJVlV5VTAzNHpVYUhJZ3JHc2JqbS92Mld4b3JWRzlkS3pyL1lSK1FKSTNuN3Vjc3lOYjFGd3MyYmtWM0N1ZXk1YldTV1pxSkIxcmxGek50K0k0eWoxVVhrSk8xSDBmRzRVeFoyN2Y3ekZsMzlSakNLU3ZOY2RMSXpJNjBlYkxHbUdaU3BpajhBSDBRdzdUazBIK3RZOHR3cTl2ZE1iOUJpREJsVTR1K3BzL2dsM0trSHhrZmhQTU1ZS3d2QnBaYjJuTWpad1dLTjFnelZSYlBKVFBJY1VEWkI5QXVpcz0=", - "pin": "111111" - } -] diff --git a/src/tests/controllers/ampMessage.test.ts b/src/tests/controllers/ampMessage.test.ts new file mode 100644 index 000000000..fbde19cc0 --- /dev/null +++ b/src/tests/controllers/ampMessage.test.ts @@ -0,0 +1,99 @@ +import test, { ExecutionContext } from 'ava' +import { sendPayment } from '../utils/msg' +import nodes from '../nodes' +import { addContact } from '../utils/save' +import { NodeConfig } from '../types' + +/* + npx ava src/tests/controllers/ampMessage.test.ts --verbose --serial --timeout=2m +*/ + +interface Context {} + +test.serial( + 'ampMessage: send more sats than one channel can handle to test AMP', + async (t: ExecutionContext) => { + t.true(Array.isArray(nodes)) + await ampMessage(t, nodes) + } +) + +async function ampMessage(t: ExecutionContext, nodes: NodeConfig[]) { + //TWO NODES SEND PAYMENTS TO EACH OTHER IN A CHAT USING AMP ===> + + //Alice and Bob both keysend and amp enabled + //Carol only keysend enabled + + //Test that we can send a payment with an amount larger than largest channel size + //from Alice -> Bob and using Carol as liquidity as the second shard of the amp payment + //Alice -> 1.5mil sats -> Bob (since Alice has two channels with 1 mil each) + + { + const node1 = nodes[0] + const node2 = nodes[1] + + console.log(`amp payment from ${node1.alias} to ${node2.alias}`) + + console.log('adding contact') + const added = await addContact(t, node1, node2) + t.true(added, 'n1 should add n2 as contact') + console.log('contact added') + + console.log(`sending payment ${node1.alias} -> ${node2.alias}`) + //NODE1 SENDS PAYMENT TO NODE2 + const amount = 1500000 + const paymentText = 'AMP test 1' + const payment = await sendPayment(t, node1, node2, amount, paymentText) + console.log(payment) + t.true(payment, 'payment should be sent') + console.log(`payment sent ${node1.alias} -> ${node2.alias}`) + } + + //Test that Carol still can receive keysends + + { + const node1 = nodes[1] + const node2 = nodes[2] + + console.log(`amp payment from ${node1.alias} to ${node2.alias}`) + + console.log('adding contact') + const added = await addContact(t, node1, node2) + t.true(added, 'n1 should add n2 as contact') + console.log('contact added') + + console.log(`sending payment ${node1.alias} -> ${node2.alias}`) + //NODE1 SENDS PAYMENT TO NODE2 + const amount = 100000 + const paymentText = 'AMP test 2' + const payment = await sendPayment(t, node1, node2, amount, paymentText) + console.log(payment) + t.true(payment, 'payment should be sent') + console.log(`payment sent ${node1.alias} -> ${node2.alias}`) + } + + //Test that Carol with only keysend enabled can send to amp node (Alice) + + { + const node1 = nodes[2] + const node2 = nodes[0] + + console.log(`amp payment from ${node1.alias} to ${node2.alias}`) + + console.log('adding contact') + const added = await addContact(t, node1, node2) + t.true(added, 'n1 should add n2 as contact') + console.log('contact added') + + console.log(`sending payment ${node1.alias} -> ${node2.alias}`) + //NODE1 SENDS PAYMENT TO NODE2 + const amount = 1500000 + const paymentText = 'AMP test 3' + const payment = await sendPayment(t, node1, node2, amount, paymentText) + console.log(payment) + t.true(payment, 'payment should be sent') + console.log(`payment sent ${node1.alias} -> ${node2.alias}`) + } +} + +module.exports = ampMessage