-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
380 additions
and
9 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,9 @@ | ||
# Editor configuration, see http://editorconfig.org | ||
root = true | ||
|
||
[*] | ||
charset = utf-8 | ||
indent_style = space | ||
indent_size = 2 | ||
insert_final_newline = true | ||
trim_trailing_whitespace = true |
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 |
---|---|---|
|
@@ -6,6 +6,11 @@ | |
"author": "Inokentii Mazhara <[email protected]>", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@ethersproject/address": "^5.7.0", | ||
"@ethersproject/hash": "^5.7.0", | ||
"@ethersproject/strings": "^5.7.0", | ||
"@ethersproject/transactions": "^5.7.0", | ||
"@stablelib/random": "^1.0.2", | ||
"@taquito/rpc": "14.0.0", | ||
"@taquito/taquito": "14.0.0", | ||
"@taquito/tzip12": "14.0.0", | ||
|
@@ -37,6 +42,7 @@ | |
"ts": "tsc --pretty", | ||
"lint": "eslint ./src --ext .js,.ts", | ||
"lint:fix": "eslint ./src --ext .js,.ts --fix", | ||
"lint:rules": "eslint --print-config", | ||
"clean": "rimraf dist/", | ||
"db-migration": "cd migrations/notifications && npx ts-node index.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,82 @@ | ||
import * as ethersAddressUtils from '@ethersproject/address'; | ||
import * as ethersHashUtils from '@ethersproject/hash'; | ||
import * as ethersStringsUtils from '@ethersproject/strings'; | ||
import * as ethersTxUtils from '@ethersproject/transactions'; | ||
import { STATUS_CODE } from '@taquito/http-utils'; | ||
import { validateAddress, ValidationResult, verifySignature, getPkhfromPk } from '@taquito/utils'; | ||
|
||
import { redisClient } from './redis'; | ||
import { CodedError } from './utils/errors'; | ||
import { safeCheck } from './utils/helpers'; | ||
import { getSigningNonce } from './utils/signing-nonce'; | ||
|
||
const REDIS_DB_KEY = 'magic_square_quest'; | ||
|
||
export function getMagicSquareQuestParticipants() { | ||
return redisClient.lrange(REDIS_DB_KEY, 0, -1).then(records => records.map(r => JSON.parse(r))); | ||
} | ||
|
||
interface StartQuestPayload { | ||
pkh: string; | ||
publicKey: string; | ||
messageBytes: string; | ||
signature: string; | ||
evm: { | ||
pkh: string; | ||
messageBytes: string; | ||
signature: string; | ||
}; | ||
} | ||
|
||
export async function startMagicSquareQuest({ pkh, publicKey, messageBytes, signature, evm }: StartQuestPayload) { | ||
// Public Key Hashes | ||
|
||
if (!safeCheck(() => validateAddress(pkh) === ValidationResult.VALID && getPkhfromPk(publicKey) === pkh)) | ||
throw new CodedError(STATUS_CODE.BAD_REQUEST, 'Invalid Tezos public key (hash)'); | ||
|
||
let evmPkh: string; | ||
try { | ||
// Corrects lower-cased addresses. Throws if invalid. | ||
evmPkh = ethersAddressUtils.getAddress(evm.pkh); | ||
} catch (err) { | ||
console.error(err); | ||
throw new CodedError(STATUS_CODE.BAD_REQUEST, 'Invalid EVM public key hash'); | ||
} | ||
|
||
// Signatures | ||
|
||
if (!safeCheck(() => verifySignature(messageBytes, publicKey, signature))) | ||
throw new CodedError(STATUS_CODE.UNAUTHORIZED, 'Invalid Tezos signature or message'); | ||
|
||
if ( | ||
!safeCheck(() => { | ||
const messageBytes = ethersStringsUtils.toUtf8String(evm.messageBytes); | ||
const messageHash = ethersHashUtils.hashMessage(messageBytes); | ||
|
||
return ethersTxUtils.recoverAddress(messageHash, evm.signature) === evmPkh; | ||
}) | ||
) | ||
throw new CodedError(STATUS_CODE.UNAUTHORIZED, 'Invalid EVM signature or message'); | ||
|
||
// Presence check | ||
|
||
const exists = await redisClient | ||
.lrange(REDIS_DB_KEY, 0, -1) | ||
.then(items => items.some(item => item.includes(pkh) && item.includes(evmPkh))); | ||
|
||
if (exists) | ||
throw new CodedError(STATUS_CODE.CONFLICT, 'Quest already started for the given credentials', 'QUEST_STARTED'); | ||
|
||
// Auth nonce | ||
getSigningNonce.delete(pkh); | ||
|
||
// Registering | ||
|
||
const item = { | ||
pkh, | ||
evmPkh, | ||
ts: new Date().toISOString() | ||
}; | ||
|
||
await redisClient.lpush(REDIS_DB_KEY, JSON.stringify(item)); | ||
} |
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,17 @@ | ||
interface CodedErrorForResponse { | ||
message: string; | ||
code?: string; | ||
} | ||
|
||
export class CodedError extends Error { | ||
constructor(public code: number, message: string, public errorCode?: string) { | ||
super(message); | ||
} | ||
|
||
buildResponse() { | ||
const res: CodedErrorForResponse = { message: this.message }; | ||
if (this.errorCode) res.code = this.errorCode; | ||
|
||
return res; | ||
} | ||
} |
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,23 @@ | ||
import { randomStringForEntropy } from '@stablelib/random'; | ||
import { validateAddress, ValidationResult } from '@taquito/utils'; | ||
import memoizee from 'memoizee'; | ||
|
||
import { CodedError } from './errors'; | ||
|
||
export const SIGNING_NONCE_TTL = 5 * 60_000; | ||
|
||
const MEMOIZE_OPTIONS = { | ||
max: 500, | ||
maxAge: SIGNING_NONCE_TTL | ||
}; | ||
|
||
export const getSigningNonce = memoizee((pkh: string) => { | ||
if (validateAddress(pkh) !== ValidationResult.VALID) throw new CodedError(400, 'Invalid address'); | ||
|
||
return buildNonce(); | ||
}, MEMOIZE_OPTIONS); | ||
|
||
function buildNonce() { | ||
// The way it is done in SIWE.generateNonce() | ||
return randomStringForEntropy(96); | ||
} |
Oops, something went wrong.