Skip to content

Commit

Permalink
Add a class FakerValidator
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelKim20 committed Sep 13, 2023
1 parent 8d40a32 commit da6cea0
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 2 deletions.
3 changes: 3 additions & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,12 @@
}
],
"devDependencies": {
"@ethersproject/experimental": "^5.7.0",
"@size-limit/preset-small-lib": "^7.0.8",
"@types/express": "^4.17.8",
"bigint-buffer": "^1.1.5",
"express": "^4.17.1",
"express-validator": "^6.14.0",
"ganache": "^7.9.1",
"glob": "^8.0.3",
"husky": "^7.0.4",
Expand Down
223 changes: 223 additions & 0 deletions packages/client/test/helper/FakerValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import { ContractUtils } from "../../src";

import * as bodyParser from "body-parser";
import * as http from "http";
// @ts-ignore
import * as express from "express";
import { body, validationResult } from "express-validator";

import { LinkCollection, LinkCollection__factory } from "del-osx-lib";

import { AddressZero, HashZero } from "@ethersproject/constants";
import { NonceManager } from "@ethersproject/experimental";
import { BigNumber, BigNumberish, Signer } from "ethers";

import { GasPriceManager } from "./GasPriceManager";
import { GanacheServer } from "./GanacheServer";
import { Deployment } from "./ContractDeployer";

export class FakerValidator {
private readonly port: number;

protected _app: express.Application;

protected _server: http.Server | null = null;

protected _deployment: Deployment;

private _accounts: Signer[];

constructor(port: number | string, deployment: Deployment) {
if (typeof port === "string") this.port = parseInt(port, 10);
else this.port = port;

this._app = express();
this._deployment = deployment;
this._accounts = GanacheServer.accounts();
}

public start(): Promise<void> {
this._app.use(bodyParser.urlencoded({ extended: false }));
this._app.use(bodyParser.json());

this._app.get("/", [], this.getHealthStatus.bind(this));
this._app.post(
"/request",
[
body("email")
.exists()
.trim()
.isEmail(),
body("address")
.exists()
.trim()
.isEthereumAddress(),
body("signature")
.exists()
.trim()
.matches(/^(0x)[0-9a-f]{130}$/i),
],
this.postRequest.bind(this)
);

// Listen on provided this.port on this.address.
return new Promise<void>((resolve, reject) => {
// Create HTTP _server.
this._server = http.createServer(this._app);
this._server.on("error", reject);
this._server.listen(this.port, async () => {
await this.onStart();
resolve();
});
});
}

private async onStart() {
await this.getContract()
.connect(this.validator1)
.updateEndpoint(`http://127.0.0.1:${this.port}`);
await this.getContract()
.connect(this.validator2)
.updateEndpoint(`http://127.0.0.1:${this.port}`);
await this.getContract()
.connect(this.validator3)
.updateEndpoint(`http://127.0.0.1:${this.port}`);
}

public stop(): Promise<void> {
return new Promise<void>(async (resolve, reject) => {
if (this._server != null) {
this._server.close((err?) => {
if (err) reject(err);
else resolve();
});
} else resolve();
});
}

private makeResponseData(code: number, data: any, error?: any): any {
return {
code,
data,
error,
};
}

private getContract(): LinkCollection {
const contract = LinkCollection__factory.connect(this._deployment.linkCollection.address, this.validator1);
return contract;
}

private get validator1(): Signer {
return new NonceManager(new GasPriceManager(this._accounts[2]));
}

private get validator2(): Signer {
return new NonceManager(new GasPriceManager(this._accounts[3]));
}

private get validator3(): Signer {
return new NonceManager(new GasPriceManager(this._accounts[4]));
}

private async getRequestId(emailHash: string, address: string, nonce: BigNumberish): Promise<string> {
while (true) {
const id = ContractUtils.getRequestId(emailHash, address, nonce);
if (await (await this.getContract()).isAvailable(id)) return id;
}
}

private async getHealthStatus(req: express.Request, res: express.Response) {
return res.json("OK");
}

private async postRequest(req: express.Request, res: express.Response) {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.json(
this.makeResponseData(400, undefined, {
message: "Failed to check the validity of parameters.",
validation: errors.array(),
})
);
}

try {
const email: string = String(req.body.email).trim(); // 이메일 해시
const address: string = String(req.body.address).trim(); // 주소
const signature: string = String(req.body.signature).trim(); // 서명
const nonce = await (await this.getContract()).nonceOf(address);
const emailHash = ContractUtils.sha256String(email);
if (!ContractUtils.verifyRequestData(address, email, nonce, signature)) {
return res.json(
this.makeResponseData(401, undefined, {
message: "The signature value entered is not valid.",
})
);
}

const emailToAddress: string = await this.getContract().toAddress(emailHash);
if (emailToAddress !== AddressZero) {
return res.json(
this.makeResponseData(402, undefined, {
message: "This email is already registered.",
})
);
}

const addressToEmail: string = await this.getContract().toEmail(address);
if (addressToEmail !== HashZero) {
return res.json(
this.makeResponseData(403, undefined, {
message: "This address is already registered.",
})
);
}

const requestId = await this.getRequestId(emailHash, address, nonce);
setTimeout(async () => {
await (await this.getContract())
.connect(this.validator1)
.addRequest(requestId, emailHash, address, signature);
}, 1000);

setTimeout(async () => {
await this.getContract()
.connect(this.validator1)
.voteRequest(requestId, BigNumber.from(1));
await this.getContract()
.connect(this.validator2)
.voteRequest(requestId, BigNumber.from(1));
}, 2000);

setTimeout(async () => {
await this.getContract()
.connect(this.validator1)
.countVote(requestId);
}, 3000);

try {
return res.json(
this.makeResponseData(200, {
requestId,
})
);
} catch (error) {
const message = error.message !== undefined ? error.message : "Failed save request";
return res.json(
this.makeResponseData(800, undefined, {
message,
})
);
}
} catch (error) {
const message = error.message !== undefined ? error.message : "Failed save request";
console.error(message);
return res.json(
this.makeResponseData(500, undefined, {
message,
})
);
}
}
}
6 changes: 6 additions & 0 deletions packages/client/test/methods.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Server } from "ganache";
import { GanacheServer } from "./helper/GanacheServer";
import { contextParamsLocalChain } from "./helper/constants";
import { FakerValidator } from "./helper/FakerValidator";
import { Client, Context, ContractUtils } from "../src";
import { AddRequestSteps } from "../src/interfaces";
import { BigNumber } from "ethers";
Expand All @@ -9,6 +10,7 @@ import { ContractDeployer, Deployment } from "./helper/ContractDeployer";
describe("SDK Client", () => {
let deployment: Deployment;
const [, , validator1, validator2, , user1] = GanacheServer.accounts();
let fakerValidator: FakerValidator;

describe("SDK Client", () => {
let server: Server;
Expand All @@ -19,10 +21,14 @@ describe("SDK Client", () => {
deployment = await ContractDeployer.deploy();

GanacheServer.setTestWeb3Signer(user1);

fakerValidator = new FakerValidator(7070, deployment);
await fakerValidator.start();
});

afterAll(async () => {
await server.close();
await fakerValidator.stop();
});

describe("Method Check", () => {
Expand Down
26 changes: 24 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,15 @@
"@ethersproject/properties" "^5.7.0"
"@ethersproject/transactions" "^5.7.0"

"@ethersproject/experimental@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/experimental/-/experimental-5.7.0.tgz#9759639434d37beaedfd8acab6f3af7db246b92d"
integrity sha512-DWvhuw7Dg8JPyhMbh/CNYOwsTLjXRx/HGkacIL5rBocG8jJC0kmixwoK/J3YblO4vtcyBLMa+sV74RJZK2iyHg==
dependencies:
"@ethersproject/web" "^5.7.0"
ethers "^5.7.0"
scrypt-js "3.0.1"

"@ethersproject/[email protected]", "@ethersproject/hash@^5.7.0":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7"
Expand Down Expand Up @@ -4366,7 +4375,15 @@ expect@^25.5.0:
jest-message-util "^25.5.0"
jest-regex-util "^25.2.6"

express@^4.14.0:
express-validator@^6.14.0:
version "6.15.0"
resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-6.15.0.tgz#5e4601428960b0d66f5f4ae09cb32ed2077374a4"
integrity sha512-r05VYoBL3i2pswuehoFSy+uM8NBuVaY7avp5qrYjQBDzagx2Z5A77FZqPT8/gNLF3HopWkIzaTFaC4JysWXLqg==
dependencies:
lodash "^4.17.21"
validator "^13.9.0"

express@^4.14.0, express@^4.17.1:
version "4.18.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
Expand Down Expand Up @@ -6430,7 +6447,7 @@ lodash.sortby@^4.7.0:
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==

lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19:
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
Expand Down Expand Up @@ -9186,6 +9203,11 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"

validator@^13.9.0:
version "13.11.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b"
integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==

varint@^5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4"
Expand Down

0 comments on commit da6cea0

Please sign in to comment.