From 95ccfeceefa6063f40127ed589b735b30f66d58a Mon Sep 17 00:00:00 2001 From: Danial Date: Fri, 16 Aug 2024 15:01:58 +0900 Subject: [PATCH] Add the ability to register a point provider --- packages/relay/config/config.yaml | 1 + packages/relay/config/config_test.yaml | 1 + packages/relay/src/common/Config.ts | 6 +++ packages/relay/src/routers/ProviderRouter.ts | 50 +++++++++++++++++++ packages/relay/src/storage/RelayStorage.ts | 15 ++++++ .../src/storage/mapper/register_provider.xml | 30 +++++++++++ packages/relay/src/storage/mapper/table.xml | 16 ++++++ packages/relay/src/utils/ContractUtils.ts | 5 ++ packages/relay/src/utils/Errors.ts | 4 +- 9 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 packages/relay/src/storage/mapper/register_provider.xml diff --git a/packages/relay/config/config.yaml b/packages/relay/config/config.yaml index d4586e3e..9ba6a53d 100644 --- a/packages/relay/config/config.yaml +++ b/packages/relay/config/config.yaml @@ -96,6 +96,7 @@ relay: testMode: ${RELAY_TEST_MODE} bridgeActiveStatus: true allowedShopIdPrefix: "0x0001" + initialBalanceOfProvider: 50000 contracts: sideChain: diff --git a/packages/relay/config/config_test.yaml b/packages/relay/config/config_test.yaml index e84c4dff..b986fc79 100644 --- a/packages/relay/config/config_test.yaml +++ b/packages/relay/config/config_test.yaml @@ -96,6 +96,7 @@ relay: testMode: ${RELAY_TEST_MODE} bridgeActiveStatus: true allowedShopIdPrefix: "0x0001" + initialBalanceOfProvider: 50000 contracts: sideChain: diff --git a/packages/relay/src/common/Config.ts b/packages/relay/src/common/Config.ts index ae617925..33120326 100644 --- a/packages/relay/src/common/Config.ts +++ b/packages/relay/src/common/Config.ts @@ -211,6 +211,7 @@ export class RelayConfig implements IRelayConfig { public testMode: boolean; public bridgeActiveStatus: boolean; public allowedShopIdPrefix: string; + public initialBalanceOfProvider: number; constructor() { const defaults = RelayConfig.defaultValue(); @@ -228,6 +229,7 @@ export class RelayConfig implements IRelayConfig { this.testMode = defaults.testMode; this.bridgeActiveStatus = defaults.bridgeActiveStatus; this.allowedShopIdPrefix = defaults.allowedShopIdPrefix; + this.initialBalanceOfProvider = defaults.initialBalanceOfProvider; } public static defaultValue(): IRelayConfig { @@ -251,6 +253,7 @@ export class RelayConfig implements IRelayConfig { testMode: false, bridgeActiveStatus: true, allowedShopIdPrefix: "0x0001", + initialBalanceOfProvider: 50000, }; } @@ -269,6 +272,8 @@ export class RelayConfig implements IRelayConfig { if (config.bridgeActiveStatus !== undefined) this.bridgeActiveStatus = config.bridgeActiveStatus.toString().toLowerCase() === "true"; if (config.allowedShopIdPrefix !== undefined) this.allowedShopIdPrefix = config.allowedShopIdPrefix; + if (config.initialBalanceOfProvider !== undefined) + this.initialBalanceOfProvider = config.initialBalanceOfProvider; } } @@ -493,6 +498,7 @@ export interface IRelayConfig { testMode: boolean; bridgeActiveStatus: boolean; allowedShopIdPrefix: string; + initialBalanceOfProvider: number; } export interface IContractsConfig { diff --git a/packages/relay/src/routers/ProviderRouter.ts b/packages/relay/src/routers/ProviderRouter.ts index 00999675..48a68429 100644 --- a/packages/relay/src/routers/ProviderRouter.ts +++ b/packages/relay/src/routers/ProviderRouter.ts @@ -85,6 +85,18 @@ export class ProviderRouter { } public registerRoutes() { + this.app.post( + "/v1/provider/register", + [ + body("provider").exists().trim().isEthereumAddress(), + body("signature") + .exists() + .trim() + .matches(/^(0x)[0-9a-f]{130}$/i), + ], + this.provider_register.bind(this) + ); + this.app.get( "/v1/provider/balance/:provider", [param("provider").exists().trim().isEthereumAddress()], @@ -148,6 +160,44 @@ export class ProviderRouter { ); } + private async provider_register(req: express.Request, res: express.Response) { + logger.http(`POST /v1/provider/register ${req.ip}:${JSON.stringify(req.body)}`); + + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(200).json(ResponseMessage.getErrorMessage("2001", { validation: errors.array() })); + } + + const signerItem = await this.getRelaySigner(this.contractManager.sideChainProvider); + try { + const provider: string = String(req.body.provider).trim(); + const signature: string = String(req.body.signature).trim(); + + const nonce = await this.contractManager.sideLedgerContract.nonceOf(provider); + const message = ContractUtils.getRegisterProviderMessage(provider, nonce, this.contractManager.sideChainId); + if (!ContractUtils.verifyMessage(provider, message, signature)) + return res.status(200).json(ResponseMessage.getErrorMessage("1501")); + + const balance = await this.contractManager.sideLedgerContract.tokenBalanceOf(provider); + const minimum = BOACoin.make(this.config.relay.initialBalanceOfProvider).value; + if (balance.lt(minimum)) { + return res.status(200).json(ResponseMessage.getErrorMessage("1511")); + } + + await this.storage.postNewProvider(provider); + + this.metrics.add("success", 1); + return res.status(200).json(this.makeResponseData(0, { provider })); + } catch (error: any) { + const msg = ResponseMessage.getEVMErrorMessage(error); + logger.error(`POST /v1/provider/register : ${msg.error.message}`); + this.metrics.add("failure", 1); + return res.status(200).json(this.makeResponseData(msg.code, undefined, msg.error)); + } finally { + this.releaseRelaySigner(signerItem); + } + } + private async provider_balance(req: express.Request, res: express.Response) { logger.http(`GET /v1/provider/balance/:provider ${req.ip}:${JSON.stringify(req.params)}`); diff --git a/packages/relay/src/storage/RelayStorage.ts b/packages/relay/src/storage/RelayStorage.ts index 6144f069..192c30b5 100644 --- a/packages/relay/src/storage/RelayStorage.ts +++ b/packages/relay/src/storage/RelayStorage.ts @@ -980,4 +980,19 @@ export class RelayStorage extends Storage { }); } /// endregion + + public postNewProvider(provider: string): Promise { + return new Promise(async (resolve, reject) => { + this.queryForMapper("register_provider", "postProvider", { + provider, + }) + .then(() => { + return resolve(); + }) + .catch((reason) => { + if (reason instanceof Error) return reject(reason); + return reject(new Error(reason)); + }); + }); + } } diff --git a/packages/relay/src/storage/mapper/register_provider.xml b/packages/relay/src/storage/mapper/register_provider.xml new file mode 100644 index 00000000..85c8194e --- /dev/null +++ b/packages/relay/src/storage/mapper/register_provider.xml @@ -0,0 +1,30 @@ + + + + + + INSERT INTO register_provider + ( + "provider" , + "timestamp" + ) + VALUES + ( + #{provider} , + now() + ) + ON CONFLICT ("sequence") + DO UPDATE + SET + "provider" = EXCLUDED."provider", + "timestamp" = EXCLUDED."timestamp"; + + + + + + diff --git a/packages/relay/src/storage/mapper/table.xml b/packages/relay/src/storage/mapper/table.xml index c3ab4d3e..3bdfde05 100644 --- a/packages/relay/src/storage/mapper/table.xml +++ b/packages/relay/src/storage/mapper/table.xml @@ -113,6 +113,20 @@ + + CREATE TABLE IF NOT EXISTS register_provider + ( + "sequence" BIGINT generated always as identity, + "provider" VARCHAR(42) NOT NULL, + "timestamp" TIMESTAMP, + PRIMARY KEY ("sequence") + ); + CREATE SEQUENCE IF NOT EXISTS register_provider_sequence_seq; + CREATE INDEX IF NOT EXISTS register_provider_provider_index + on register_provider (provider); + + + diff --git a/packages/relay/src/utils/ContractUtils.ts b/packages/relay/src/utils/ContractUtils.ts index 8e4f98a8..bb4ee668 100644 --- a/packages/relay/src/utils/ContractUtils.ts +++ b/packages/relay/src/utils/ContractUtils.ts @@ -784,6 +784,11 @@ export class ContractUtils { return arrayify(keccak256(encodedResult)); } + public static getRegisterProviderMessage(provider: string, nonce: BigNumberish, chainId: BigNumberish): Uint8Array { + const encodedResult = defaultAbiCoder.encode(["address", "uint256", "uint256"], [provider, chainId, nonce]); + return arrayify(keccak256(encodedResult)); + } + public static async signMessage(signer: Signer, message: Uint8Array): Promise { return signer.signMessage(message); } diff --git a/packages/relay/src/utils/Errors.ts b/packages/relay/src/utils/Errors.ts index 72c5276f..58708734 100644 --- a/packages/relay/src/utils/Errors.ts +++ b/packages/relay/src/utils/Errors.ts @@ -62,11 +62,11 @@ export class ResponseMessage { ["2003", "The payment ID is not exist"], ["2004", "Temporary address that does not exist"], ["2005", "Mobile notification not allowed"], - ["2006", "Can not found delegator"], + ["2006", "Can not find delegator"], ["2007", "The phone number format is invalid."], ["2008", "Mobile information not found."], ["2020", "The status code for this payment cannot be approved"], - ["2022", "The status code for this payment cannot be cancel"], + ["2022", "The status code for this payment cannot be cancelled"], ["2024", "The status code for this payment cannot process closing"], ["2025", "This payment has already been approved"], ["2026", "This payment has already been closed"],