diff --git a/src/helpers/alias.ts b/src/helpers/alias.ts index a32a19eb..af1175bc 100644 --- a/src/helpers/alias.ts +++ b/src/helpers/alias.ts @@ -1,7 +1,7 @@ import { uniq } from 'lodash'; import db from './mysql'; -const DEFAULT_ALIAS_EXPIRY_DAYS = 30; +export const DEFAULT_ALIAS_EXPIRY_DAYS = 30; // These types can always be executed with an alias const TYPES_EXECUTABLE_BY_ALIAS = [ @@ -38,25 +38,14 @@ type ExecutableType = | (typeof OPTIONAL_TYPES_EXECUTABLE_BY_ALIAS)[number] | (typeof TYPES_EXECUTABLE_BY_STARKNET_ALIAS)[number]; -/** - * Checks if an alias relationship exists and is not expired - * @param address - The original address - * @param alias - The alias address to check - * @param expiryDays - Number of days after which alias expires (default: 30) - * @returns Promise - True if valid alias exists - */ -export async function isExistingAlias( - address: string, - alias: string, - expiryDays = DEFAULT_ALIAS_EXPIRY_DAYS -): Promise { +export async function isExistingAlias(address: string, alias: string): Promise { const query = `SELECT 1 FROM aliases WHERE address = ? AND alias = ? - AND created > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL ? DAY)) + AND expiration > UNIX_TIMESTAMP(NOW()) LIMIT 1`; - const results = await db.queryAsync(query, [address, alias, expiryDays]); + const results = await db.queryAsync(query, [address, alias]); return results.length > 0; } diff --git a/src/writer/alias.ts b/src/writer/alias.ts index 4a075538..30f8f928 100644 --- a/src/writer/alias.ts +++ b/src/writer/alias.ts @@ -1,4 +1,5 @@ import snapshot from '@snapshot-labs/snapshot.js'; +import { DEFAULT_ALIAS_EXPIRY_DAYS } from '../helpers/alias'; import log from '../helpers/log'; import db from '../helpers/mysql'; import { jsonParse } from '../helpers/utils'; @@ -14,12 +15,16 @@ export async function verify(message): Promise { return Promise.reject('alias cannot be the same as the address'); } - const existing = await db.queryAsync( - 'SELECT 1 FROM aliases WHERE address = ? AND alias = ? LIMIT 1', - [message.address, msg.payload.alias] - ); - if (existing.length > 0) { - return Promise.reject('alias already exists'); + const results = await db.queryAsync('SELECT address FROM aliases WHERE alias = ?', [ + msg.payload.alias + ]); + for (const row of results) { + if (row.address === message.address) { + return Promise.reject('alias already exists'); + } + } + if (results.length > 0) { + return Promise.reject('alias is already linked to another address'); } return true; @@ -32,7 +37,8 @@ export async function action(message, ipfs, receipt, id): Promise { ipfs, address: message.address, alias: msg.payload.alias, - created: msg.timestamp + created: msg.timestamp, + expiration: msg.timestamp + DEFAULT_ALIAS_EXPIRY_DAYS * 86400 }; await db.queryAsync('INSERT INTO aliases SET ?', params); } diff --git a/test/fixtures/alias.ts b/test/fixtures/alias.ts index cf3ca83f..0b6c0456 100644 --- a/test/fixtures/alias.ts +++ b/test/fixtures/alias.ts @@ -1,23 +1,31 @@ +import { DEFAULT_ALIAS_EXPIRY_DAYS } from '../../src/helpers/alias'; + +const now = Math.floor(Date.now() / 1000); +const EXPIRY_SECONDS = DEFAULT_ALIAS_EXPIRY_DAYS * 86400; + export const aliasesSqlFixtures: Record[] = [ { id: '1', ipfs: 'Qm...', address: '0x0000000000000000000000000000000000000000', alias: '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3', - created: Math.floor(Date.now() / 1000) + created: now, + expiration: now + EXPIRY_SECONDS }, { id: '2', ipfs: 'Qm...', address: '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0', alias: '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f3', - created: Math.floor(Date.now() / 1000) + created: now, + expiration: now + EXPIRY_SECONDS }, { id: '3', ipfs: 'Qm...', address: '0x02a0a8f3b6097e7a6bd7649deb30715323072a159c0e6b71b689bd245c146cc0', alias: '0x91FD2c8d24767db4Ece7069AA27832ffaf8590f4', - created: 1 + created: 1, + expiration: 1 + EXPIRY_SECONDS } ]; diff --git a/test/integration/helpers/alias.test.ts b/test/integration/helpers/alias.test.ts index 48f91117..cf1be008 100644 --- a/test/integration/helpers/alias.test.ts +++ b/test/integration/helpers/alias.test.ts @@ -41,6 +41,22 @@ describe('alias', () => { ).rejects.toMatch('alias already exists'); }); + it('should reject when alias is already linked to another address', async () => { + const alias = aliasesSqlFixtures[0].alias; + const differentAddress = '0x0000000000000000000000000000000000000099'; + const msg = { + version: '0.1.4', + timestamp: Math.floor(Date.now() / 1000), + type: 'alias', + from: differentAddress, + payload: { alias } + }; + + await expect( + verify({ address: differentAddress, from: differentAddress, msg: JSON.stringify(msg) }) + ).rejects.toMatch('alias is already linked to another address'); + }); + it('should pass when alias does not exist', async () => { const msg = { version: '0.1.4', diff --git a/test/schema.sql b/test/schema.sql index f7e64be4..8e86dfc2 100644 --- a/test/schema.sql +++ b/test/schema.sql @@ -138,6 +138,7 @@ CREATE TABLE aliases ( address VARCHAR(100) NOT NULL, alias VARCHAR(100) NOT NULL, created INT(11) NOT NULL, + expiration INT(11) NOT NULL, PRIMARY KEY (address, alias), INDEX ipfs (ipfs) );