-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adding recovery integration tests (#74)
* first cut * try workflow * run daemon * print logs * restart on failure * try this * try * try * try * try * cross fingers * fix * works hopefully * fix * add back * cleanup * image versions and readme
- Loading branch information
Showing
7 changed files
with
311 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
name: Recovery Signer Integration Test | ||
on: [pull_request] | ||
jobs: | ||
test-ci: | ||
name: integration test | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Start docker | ||
run: docker-compose -f test/docker/docker-compose.yml up -d | ||
- uses: actions/setup-node@v2 | ||
with: | ||
node-version: 18 | ||
- run: yarn install | ||
- run: yarn build | ||
- run: yarn test:integration:ci | ||
- name: Print Docker Logs | ||
if: always() # This ensures that the logs are printed even if the tests fail | ||
run: docker-compose -f test/docker/docker-compose.yml logs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module.exports = { | ||
rootDir: "./", | ||
preset: "ts-jest", | ||
transform: { | ||
"^.+\\.(ts|tsx)?$": "ts-jest", | ||
"^.+\\.(js|jsx)$": "babel-jest", | ||
}, | ||
testMatch: ["**/*integration.test.ts"], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Recovery Integration Tests | ||
|
||
## How it works | ||
|
||
The recovery integration tests run different recovery scenarios against recovery | ||
signer and webauth servers. 2 recovery signer and 2 webauth servers are started | ||
in a docker-compose file (see test/docker/docker-compose.yml), to simulate a | ||
wallet interacting with 2 separate recovery servers. | ||
|
||
## To run tests locally: | ||
|
||
``` | ||
// start servers using docker | ||
$ docker-compose -f test/docker/docker-compose.yml up | ||
// run tests | ||
$ yarn test:integration:ci | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
version: "3" | ||
services: | ||
recovery-signer-migrate1: | ||
image: stellar/recoverysigner:latest | ||
depends_on: | ||
postgres1: | ||
condition: service_healthy | ||
restart: on-failure | ||
command: ["db", "migrate", "up"] | ||
environment: | ||
DB_URL: "postgresql://postgres:pg_password@postgres1:5432/pg_database1?sslmode=disable" | ||
|
||
recovery-signer-migrate2: | ||
image: stellar/recoverysigner:latest | ||
depends_on: | ||
postgres2: | ||
condition: service_healthy | ||
restart: on-failure | ||
command: ["db", "migrate", "up"] | ||
environment: | ||
DB_URL: "postgresql://postgres:pg_password@postgres2:5432/pg_database2?sslmode=disable" | ||
|
||
recovery-signer1: | ||
image: stellar/recoverysigner:latest | ||
ports: | ||
- "8000:8000" | ||
depends_on: | ||
- postgres1 | ||
environment: | ||
DB_URL: "postgresql://postgres:pg_password@postgres1:5432/pg_database1?sslmode=disable" | ||
SIGNING_KEY: SAQFNCKPZ3ON5TSSEURAF4NPTZONPA37JPHQNHSLSRUNFP43MMT5LNH6 | ||
FIREBASE_PROJECT_ID: "none" | ||
SEP10_JWKS: '{"keys":[{"kty":"EC","crv":"P-256","alg":"ES256","x":"dzqvhrMYwbmv7kcZK6L1oOATMFXG9wLFlnKfHf3E7FM","y":"Vb_wmcX-Zq2Hg2LFoXCEVWMwdJ01q41pSnxC3psunUY"}]}' | ||
PORT: 8000 | ||
|
||
recovery-signer2: | ||
image: stellar/recoverysigner:latest | ||
ports: | ||
- "8002:8002" | ||
depends_on: | ||
- postgres2 | ||
environment: | ||
DB_URL: "postgresql://postgres:pg_password@postgres2:5432/pg_database2?sslmode=disable" | ||
SIGNING_KEY: SA3Y2KQCPN6RAKLUISMY252QABWPQ3A65FBMZO2JJFKJ7O7VJNQ2PRYH # Use a different key for the second recovery signer | ||
FIREBASE_PROJECT_ID: "none" | ||
SEP10_JWKS: '{"keys":[{"kty":"EC","crv":"P-256","alg":"ES256","x":"dzqvhrMYwbmv7kcZK6L1oOATMFXG9wLFlnKfHf3E7FM","y":"Vb_wmcX-Zq2Hg2LFoXCEVWMwdJ01q41pSnxC3psunUY"}]}' | ||
PORT: 8002 | ||
|
||
web-auth1: | ||
image: stellar/webauth:latest | ||
ports: | ||
- "8001:8001" | ||
environment: | ||
SIGNING_KEY: SDYHSG4V2JP5H66N2CXBFCOBTAUFWXGJVPKWY6OXSIPMYW743N62QX6U | ||
JWK: '{"kty":"EC","crv":"P-256","alg":"ES256","x":"dzqvhrMYwbmv7kcZK6L1oOATMFXG9wLFlnKfHf3E7FM","y":"Vb_wmcX-Zq2Hg2LFoXCEVWMwdJ01q41pSnxC3psunUY","d":"ivOMB4Wscz8ShvhwWDRyd-JJVfSMsjsz1oU3sNc-XJo"}' | ||
DOMAIN: test-domain | ||
AUTH_HOME_DOMAIN: test-domain | ||
JWT_ISSUER: test | ||
PORT: 8001 | ||
|
||
web-auth2: | ||
image: stellar/webauth:latest | ||
ports: | ||
- "8003:8003" | ||
environment: | ||
SIGNING_KEY: SCAS7BUKVDL44A2BAP23RVAM6XXHB24YRCANQGDTP24HP7T6LPUFIGGU # Use a different key for the second web auth server | ||
JWK: '{"kty":"EC","crv":"P-256","alg":"ES256","x":"dzqvhrMYwbmv7kcZK6L1oOATMFXG9wLFlnKfHf3E7FM","y":"Vb_wmcX-Zq2Hg2LFoXCEVWMwdJ01q41pSnxC3psunUY","d":"ivOMB4Wscz8ShvhwWDRyd-JJVfSMsjsz1oU3sNc-XJo"}' | ||
DOMAIN: test-domain | ||
AUTH_HOME_DOMAIN: test-domain | ||
JWT_ISSUER: test | ||
PORT: 8003 | ||
|
||
postgres1: | ||
image: postgres:14 | ||
environment: | ||
POSTGRES_PASSWORD: pg_password | ||
POSTGRES_DB: pg_database1 | ||
ports: | ||
- "5432:5432" | ||
healthcheck: | ||
test: ["CMD-SHELL", "pg_isready -U postgres"] | ||
interval: 10s | ||
timeout: 5s | ||
retries: 5 | ||
|
||
postgres2: | ||
image: postgres:14 | ||
environment: | ||
POSTGRES_PASSWORD: pg_password | ||
POSTGRES_DB: pg_database2 | ||
ports: | ||
- "5433:5432" | ||
healthcheck: | ||
test: ["CMD-SHELL", "pg_isready -U postgres"] | ||
interval: 10s | ||
timeout: 5s | ||
retries: 5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import axios from "axios"; | ||
import { Wallet } from "../src"; | ||
|
||
import { | ||
RecoveryServer, | ||
RecoveryServerKey, | ||
RecoveryServerMap, | ||
RecoverableWalletConfig, | ||
RecoveryRole, | ||
RecoveryAccountIdentity, | ||
RecoveryType, | ||
} from "../src/walletSdk/Types/recovery"; | ||
|
||
describe("Recovery Integration Tests", () => { | ||
it("should work", async () => { | ||
const wallet = Wallet.TestNet(); | ||
const stellar = wallet.stellar(); | ||
const accountService = stellar.account(); | ||
|
||
const server1Key: RecoveryServerKey = "server1"; | ||
const server1: RecoveryServer = { | ||
endpoint: "http://localhost:8000", | ||
authEndpoint: "http://localhost:8001", | ||
homeDomain: "test-domain", | ||
}; | ||
|
||
const server2Key: RecoveryServerKey = "server2"; | ||
const server2: RecoveryServer = { | ||
endpoint: "http://localhost:8002", | ||
authEndpoint: "http://localhost:8003", | ||
homeDomain: "test-domain", | ||
}; | ||
|
||
const servers: RecoveryServerMap = { | ||
[server1Key]: server1, | ||
[server2Key]: server2, | ||
}; | ||
|
||
const recovery = wallet.recovery({ servers }); | ||
|
||
// Create accounts | ||
|
||
const accountKp = accountService.createKeypair(); | ||
const deviceKp = accountService.createKeypair(); | ||
const recoveryKp = accountService.createKeypair(); | ||
|
||
try { | ||
await stellar.server.loadAccount(accountKp.publicKey); | ||
await stellar.server.loadAccount(deviceKp.publicKey); | ||
await stellar.server.loadAccount(recoveryKp.publicKey); | ||
} catch (e) { | ||
await axios.get( | ||
"https://friendbot.stellar.org/?addr=" + accountKp.publicKey, | ||
); | ||
await axios.get( | ||
"https://friendbot.stellar.org/?addr=" + deviceKp.publicKey, | ||
); | ||
await axios.get( | ||
"https://friendbot.stellar.org/?addr=" + recoveryKp.publicKey, | ||
); | ||
} | ||
|
||
// Create SEP-30 identities | ||
|
||
const identity1: RecoveryAccountIdentity = { | ||
role: RecoveryRole.OWNER, | ||
authMethods: [ | ||
{ | ||
type: RecoveryType.STELLAR_ADDRESS, | ||
value: recoveryKp.publicKey, | ||
}, | ||
], | ||
}; | ||
|
||
const identity2: RecoveryAccountIdentity = { | ||
role: RecoveryRole.OWNER, | ||
authMethods: [ | ||
{ | ||
type: RecoveryType.STELLAR_ADDRESS, | ||
value: recoveryKp.publicKey, | ||
}, | ||
{ | ||
type: RecoveryType.EMAIL, | ||
value: "[email protected]", | ||
}, | ||
], | ||
}; | ||
|
||
// Create recoverable wallet | ||
|
||
const config: RecoverableWalletConfig = { | ||
accountAddress: accountKp, | ||
deviceAddress: deviceKp, | ||
accountThreshold: { low: 10, medium: 10, high: 10 }, | ||
accountIdentity: { [server1Key]: [identity1], [server2Key]: [identity2] }, | ||
signerWeight: { device: 10, recoveryServer: 5 }, | ||
}; | ||
const recoverableWallet = await recovery.createRecoverableWallet(config); | ||
|
||
// Sign and submit | ||
|
||
recoverableWallet.transaction.sign(accountKp.keypair); | ||
await stellar.submitTransaction(recoverableWallet.transaction); | ||
|
||
let resp = await stellar.server.loadAccount(accountKp.publicKey); | ||
|
||
expect(resp.signers.map((obj) => obj.weight).sort((a, b) => a - b)).toEqual( | ||
[0, 5, 5, 10], | ||
); | ||
expect( | ||
resp.signers.find((obj) => obj.key === accountKp.publicKey).weight, | ||
).toBe(0); | ||
expect( | ||
resp.signers.find((obj) => obj.key === deviceKp.publicKey).weight, | ||
).toBe(10); | ||
|
||
// Get Account Info | ||
|
||
const authToken1 = await recovery | ||
.sep10Auth(server1Key) | ||
.authenticate({ accountKp: recoveryKp }); | ||
|
||
const authMap = { [server1Key]: authToken1 }; | ||
|
||
const accountResp = await recovery.getAccountInfo(accountKp, authMap); | ||
expect(accountResp[server1Key].address).toBe(accountKp.publicKey); | ||
expect(accountResp[server1Key].identities[0].role).toBe("owner"); | ||
expect(accountResp[server1Key].signers.length).toBe(1); | ||
|
||
// Recover Wallet | ||
|
||
const authToken2 = await recovery | ||
.sep10Auth(server2Key) | ||
.authenticate({ accountKp: recoveryKp }); | ||
|
||
const recoverySignerAddress1 = recoverableWallet.signers[0]; | ||
const recoverySignerAddress2 = recoverableWallet.signers[1]; | ||
const newKp = accountService.createKeypair(); | ||
const signerMap = { | ||
[server1Key]: { | ||
signerAddress: recoverySignerAddress1, | ||
authToken: authToken1, | ||
}, | ||
[server2Key]: { | ||
signerAddress: recoverySignerAddress2, | ||
authToken: authToken2, | ||
}, | ||
}; | ||
const recoverTxn = await recovery.replaceDeviceKey( | ||
accountKp, | ||
newKp, | ||
signerMap, | ||
); | ||
|
||
await stellar.submitTransaction(recoverTxn); | ||
|
||
resp = await stellar.server.loadAccount(accountKp.publicKey); | ||
|
||
expect( | ||
resp.signers.find((obj) => obj.key === deviceKp.publicKey), | ||
).toBeFalsy(); | ||
expect(resp.signers.find((obj) => obj.key === newKp.publicKey).weight).toBe( | ||
10, | ||
); | ||
}, 60000); | ||
}); |