Skip to content

Commit

Permalink
increase complexity on successful registration
Browse files Browse the repository at this point in the history
  • Loading branch information
finn-block committed Nov 15, 2023
1 parent 040069c commit b0941a7
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 22 deletions.
34 changes: 27 additions & 7 deletions src/pow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Kysely } from 'kysely';

const recentChallenges: { [challenge: string]: number } = {};
const CHALLENGE_TIMEOUT = 60 * 1000;
const COMPLEXITY_LOOKBACK = 60 * 1000; // complexity is based on number of successful registrations in this timeframe
const COMPLEXITY_MINIMUM = 5;

export class ProofOfWork {
#db: Kysely<PowDatabase>;
Expand All @@ -30,6 +32,7 @@ export class ProofOfWork {
.createTable('authorizedTenants')
.ifNotExists()
.addColumn('did', 'text', (column) => column.primaryKey())
.addColumn('timeadded', 'timestamp', (column) => column.notNull())
.execute();
}

Expand All @@ -55,7 +58,7 @@ export class ProofOfWork {
async authorizeTenant(tenant: string): Promise<void> {
await this.#db
.insertInto('authorizedTenants')
.values({ did: tenant })
.values({ did: tenant, timeadded: Date.now() })
.executeTakeFirst();
}

Expand All @@ -64,7 +67,7 @@ export class ProofOfWork {
recentChallenges[challenge] = Date.now();
res.json({
challenge: challenge,
complexity: getComplexity(),
complexity: await this.getComplexity(),
});
}

Expand All @@ -79,7 +82,7 @@ export class ProofOfWork {
hash.update(body.challenge);
hash.update(body.response);

const complexity = getComplexity();
const complexity = await this.getComplexity();
const digest = hash.digest('hex');
if (!digest.startsWith('0'.repeat(complexity))) {
res.status(401).json({ success: false });
Expand All @@ -89,7 +92,7 @@ export class ProofOfWork {
try {
await this.#db
.insertInto('authorizedTenants')
.values({ did: body.did })
.values({ did: body.did, timeadded: Date.now() })
.executeTakeFirst();
} catch (e) {
console.log('error inserting did', e);
Expand All @@ -98,6 +101,25 @@ export class ProofOfWork {
}
res.json({ success: true });
}

private async getComplexity(): Promise<number> {
const result = await this.#db
.selectFrom('authorizedTenants')
.where('timeadded', '>', Date.now() - COMPLEXITY_LOOKBACK)
.select((eb) => eb.fn.countAll().as('recent_reg_count'))
.executeTakeFirstOrThrow();
const recent = result.recent_reg_count as number;
if (recent == 0) {
return COMPLEXITY_MINIMUM;
}

const complexity = Math.floor(recent / 10);
if (complexity < COMPLEXITY_MINIMUM) {
return COMPLEXITY_MINIMUM;
}

return complexity;
}
}

const challengeCharacters =
Expand All @@ -113,11 +135,9 @@ function generateChallenge(): string {
return challenge;
}

function getComplexity(): number {
return Object.keys(recentChallenges).length;
}
interface AuthorizedTenants {
did: string;
timeadded: number;
}

interface PowDatabase {
Expand Down
80 changes: 65 additions & 15 deletions tests/http-api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ describe('http api', function () {
let httpApi: HttpApi;
let server: Server;
let profile: Profile;
let pow: ProofOfWork;

before(async function () {
config.powRegistration = true;
const pow = new ProofOfWork(getDialectFromURI(new URL('sqlite://')));
pow = new ProofOfWork(getDialectFromURI(new URL('sqlite://')));
profile = await createProfile();
httpApi = new HttpApi(dwn, pow);
});
Expand All @@ -72,7 +73,7 @@ describe('http api', function () {
complexity: number;
};
expect(body.challenge.length).to.equal(10);
expect(body.complexity).to.equal(1);
expect(body.complexity).to.equal(5);
});

it('accepts a correct registration challenge', async function () {
Expand All @@ -83,7 +84,7 @@ describe('http api', function () {
complexity: number;
};
expect(body.challenge.length).to.equal(10);
expect(body.complexity).to.equal(2);
expect(body.complexity).to.equal(5);

// solve the challenge
let response = '';
Expand All @@ -102,18 +103,67 @@ describe('http api', function () {
});

expect(submitResponse.status).to.equal(200);
});

it('increase complexity as more challenges are issued', async function () {
const challengeResponse = await fetch('http://localhost:3000/register');
expect(challengeResponse.status).to.equal(200);
const body = (await challengeResponse.json()) as {
challenge: string;
complexity: number;
};
expect(body.challenge.length).to.equal(10);
expect(body.complexity).to.equal(3);
});
}).timeout(30000);

it('increase complexity as more challenges are completed', async function () {
for (let i = 1; i <= 60; i++) {
const p = await createProfile();
if (i < 59) {
pow.authorizeTenant(p.did);
continue;
}

const challengeResponse = await fetch('http://localhost:3000/register');
expect(challengeResponse.status).to.equal(200);
const body = (await challengeResponse.json()) as {
challenge: string;
complexity: number;
};
expect(body.challenge.length).to.equal(10);

// solve the challenge
let response = '';
let iterations = 0;
const start = Date.now();
while (!checkNonce(body.challenge, response, body.complexity)) {
response = generateNonce(5);
iterations++;
if (iterations % 10000000 == 0) {
console.log(
'complexity:',
body.complexity,
'iteration count:',
iterations,
'duration:',
Date.now() - start,
'ms',
);
}
}

console.log(
'complexity:',
body.complexity,
'iteration count:',
iterations,
'duration:',
Date.now() - start,
'ms',
);

const submitResponse = await fetch('http://localhost:3000/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
challenge: body.challenge,
response: response,
did: p.did,
}),
});

expect(submitResponse.status).to.equal(200);
}
}).timeout(120000);

it('rejects an invalid nonce', async function () {
const challengeResponse = await fetch('http://localhost:3000/register');
Expand Down

0 comments on commit b0941a7

Please sign in to comment.