From e1f723b3c6218e1bd922842c2400e054b2c1557d Mon Sep 17 00:00:00 2001 From: 1aerostorm Date: Wed, 1 Nov 2023 17:00:08 +0000 Subject: [PATCH] Fix register --- db/reg_pollers.lua | 49 +++++++++----- src/locales/en.json | 3 +- src/locales/ru-RU.json | 3 +- src/modules/register/TransferWaiter.jsx | 5 +- src/modules/register/UIARegister.jsx | 26 +++++++- src/pages/api/reg/[...all].js | 88 +++++++++++++++++++++++-- src/pages/sign/delegate_vs.jsx | 2 +- src/utils/{State.js => misc.js} | 4 ++ 8 files changed, 151 insertions(+), 29 deletions(-) rename src/utils/{State.js => misc.js} (90%) diff --git a/db/reg_pollers.lua b/db/reg_pollers.lua index f4a5395..f610f68 100644 --- a/db/reg_pollers.lua +++ b/db/reg_pollers.lua @@ -8,7 +8,7 @@ function reg_pollers_bootstrap() {name = 'sym', type = 'STR'}, {name = 'amount', type = 'number'}, {name = 'uid', type = 'STR'}, - {name = 'created', type = 'number'}, + {name = 'created', type = 'unsigned'}, {name = 'init_balance', type = 'number'}, } }) @@ -21,12 +21,34 @@ function reg_pollers_bootstrap() 'amount' }, unique = true }) + reg_pollers:create_index('by_created', { + type = 'tree', parts = { + 'created' + }, unique = false + }) +end + +local function wrap_rp(rp) + return { + id = rp[1], + sym = rp[2], + amount = rp[3], + uid = rp[4], + created = rp[5], + init_bal = rp[6], + } end function get_free_reg_poller(amount, sym) local rps = nil repeat if rps ~= nil then + local now = fiber.clock64() + local rp = wrap_rp(rps[1]) + if (now - rp['created']) >= 20*60*1000000 then + box.space.reg_pollers:delete(rp['id']) + break + end amount = amount + 1 end rps = box.space.reg_pollers.index.by_amount:select{sym, amount} @@ -34,19 +56,20 @@ function get_free_reg_poller(amount, sym) return amount end -local function wrap_rp(rp) - return { - id = rp[1], - sym = rp[2], - amount = rp[3], - uid = rp[4], - created = rp[5], - init_bal = rp[6], - } +local function clean_reg_pollers(now) + for i,rp in box.space.reg_pollers.index.by_created:pairs(0, {iterator = 'GT', limit = 100}) do + local rp = wrap_rp(rp) + if (now - rp['created']) > 20*1000000 then + box.space.reg_pollers:delete(rp['id']) + else + break + end + end end function upsert_reg_poller(amount, sym, uid, init_bal) local now = fiber.clock64() + clean_reg_pollers(now) local rps = box.space.reg_pollers.index.by_amount:select{sym, amount} if #rps ~= 0 then local rp = rps[1] @@ -54,11 +77,7 @@ function upsert_reg_poller(amount, sym, uid, init_bal) if rp['uid'] ~= uid then return { err = 'Someone already waits for such transfer.', res = nil } end - if (now - rp['created']) >= 15*60*1000 then - box.space.reg_pollers:delete(rp['id']) - else - return { err = nil, res = rp } - end + return { err = nil, res = rp } end local rp = box.space.reg_pollers:insert{nil, sym, amount, uid, now, init_bal} return { err = nil, res = wrap_rp(rp) } diff --git a/src/locales/en.json b/src/locales/en.json index ff84c8b..05e95d4 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -105,7 +105,8 @@ "memo_fixed": "Memo", "to": "Send tokens to address/account", "api_error": "Cannot get address. Try again later. If problem still occurs, contact the issuer of ", - "api_error_details": "and send the error details:" + "api_error_details": "and send the error details:", + "free_poller": "Cannot calculate minimal amount. Try again later, please. Or try another currency." }, "invites_jsx": { "claim_wrong_secret": "Wrong secret", diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index ba646cb..810766e 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -105,7 +105,8 @@ "memo_fixed": "Заметка/memo", "to": "Отправьте токены на адрес/аккаунт", "api_error": "Не удается получить адрес. Попробуйте позднее. Если проблема сохраняется, свяжитесь с эмитентом ", - "api_error_details": "и сообщите подробности ошибки:" + "api_error_details": "и сообщите подробности ошибки:", + "free_poller": "Не удается рассчитать минимальную сумму. Попробуйте позже, или другую валюту." }, "invites_jsx": { "claim_wrong_secret": "Неверно указан ключ", diff --git a/src/modules/register/TransferWaiter.jsx b/src/modules/register/TransferWaiter.jsx index 0df38e4..a3fc2a3 100644 --- a/src/modules/register/TransferWaiter.jsx +++ b/src/modules/register/TransferWaiter.jsx @@ -3,6 +3,7 @@ import tt from 'counterpart' import { Asset } from 'golos-lib-js/lib/utils'; import LoadingIndicator from '@/elements/LoadingIndicator' +import { delay, } from '@/utils/misc' import { callApi, } from '@/utils/RegApiClient' class TransferWaiter extends React.Component { @@ -15,7 +16,7 @@ class TransferWaiter extends React.Component { poll = async (minAmount) => { const retry = async () => { - await new Promise(resolve => setTimeout(resolve, 1000)) + await delay(1000) if (!this.state.stopped) this.poll(minAmount) } @@ -37,7 +38,7 @@ class TransferWaiter extends React.Component { start = async () => { this.setState({ - seconds: 30*60, + seconds: 15*60, stopped: false }) diff --git a/src/modules/register/UIARegister.jsx b/src/modules/register/UIARegister.jsx index 2262e69..3d712d7 100644 --- a/src/modules/register/UIARegister.jsx +++ b/src/modules/register/UIARegister.jsx @@ -13,6 +13,7 @@ import VerifyWayTabs from '@/elements/register/VerifyWayTabs' import TransferWaiter from '@/modules/register/TransferWaiter' import { apidexGetPrices, } from '@/utils/ApidexApiClient' import KeyFile from '@/utils/KeyFile' +import { delay, } from '@/utils/misc' import { emptyAuthority } from '@/utils/RecoveryUtils' import { callApi, } from '@/utils/RegApiClient' import { withRouterHelpers, } from '@/utils/routing' @@ -91,7 +92,11 @@ class UIARegister extends React.Component { let registrar = await golos.api.getAccounts([accName]) registrar = registrar[0] - const { to_type, to_api, } = deposit + const { to_type, to_api, fee, } = deposit + if (fee && (isNaN(fee) || parseFloat(fee) !== 0)) { + error = sym + tt('uia_register_jsx.transfer_not_supported') + break + } if (to_type === 'transfer') { error = sym + tt('uia_register_jsx.transfer_not_supported') break @@ -128,6 +133,23 @@ class UIARegister extends React.Component { } } + for (let i = 0; i < 3; ++i) { + try { + let fp = await callApi('/api/reg/get_free_poller/' + minAmount.toString()) + fp = await fp.json() + if (fp.error) { + throw new Error(fp.error) + } + minAmount = Asset(fp.amount) + error = null + break + } catch (err) { + console.error('get_free_poller', err) + error = tt('uia_register_jsx.free_poller') + } + await delay(2000) + } + this.setState({ rules: { ...deposit, creator: asset.creator, telegram }, minAmount, @@ -195,7 +217,7 @@ class UIARegister extends React.Component { || res.error === 'cannot_connect_gateway')) { console.error('Repeating /uia_address', res) ++retried - await new Promise(resolve => setTimeout(resolve, 1100)) + await delay(1100) await retryReq() return } diff --git a/src/pages/api/reg/[...all].js b/src/pages/api/reg/[...all].js index 97a4699..f89044d 100644 --- a/src/pages/api/reg/[...all].js +++ b/src/pages/api/reg/[...all].js @@ -1,9 +1,10 @@ import config from 'config'; import gmailSend from 'gmail-send'; -import golos, { api } from 'golos-lib-js' +import golos, { api, broadcast } from 'golos-lib-js' import { hash, } from 'golos-lib-js/lib/auth/ecc'; import { Asset } from 'golos-lib-js/lib/utils' import secureRandom from 'secure-random'; + import nextConnect from '@/server/nextConnect'; import { throwErr, } from '@/server/error'; import { initGolos, } from '@/server/initGolos'; @@ -13,6 +14,7 @@ import passport, { addModalRoutes, checkAlreadyUsed } from '@/server/passport'; import { getDailyLimit, obtainUid, getClientCfg, } from '@/server/reg'; import { regSessionMiddleware, } from '@/server/regSession'; import Tarantool from '@/server/tarantool'; +import { delay, } from '@/utils/misc' initGolos(); @@ -306,6 +308,8 @@ let handler = nextConnect({ attachParams: true, }) }) .get('/api/reg/get_free_poller/:amount', async (req, res) => { + rateLimitReq(req) + const amountStr = req.params.amount.split('%20').join(' ') let amount try { @@ -331,7 +335,7 @@ let handler = nextConnect({ attachParams: true, }) } if (freeAmount) { - const ret = Asset(0, amount.precision, amount.symbol) + const ret = await Asset(0, amount.precision, amount.symbol) ret.amountFloat = freeAmount.toString() res.json({ status: 'ok', @@ -347,10 +351,12 @@ let handler = nextConnect({ attachParams: true, }) }) .get('/api/reg/wait_for_transfer/:amount', async (req, res) => { + rateLimitReq(req) + const amountStr = req.params.amount.split('%20').join(' ') let amount try { - amount = Asset(amountStr) + amount = await Asset(amountStr) } catch (err) { res.json({ status: 'err', @@ -373,11 +379,11 @@ let handler = nextConnect({ attachParams: true, }) let bal = balances[0][amount.symbol] if (bal) { bal = bal.balance - return Asset(bal) + return await Asset(bal) } else { const assets = await api.getAssetsAsync('', [amount.symbol]) if (!assets[0]) throwErr(req, 400, ['No such asset']) - bal = Asset(assets[0].supply) + bal = await Asset(assets[0].supply) bal.amount = 0 return bal } @@ -400,6 +406,7 @@ let handler = nextConnect({ attachParams: true, }) try { initBal = await getBalance() } catch (err) { + console.error('wait_for_transfer getBalance', initBal) throwErr(req, 400, ['Blockchain unavailable']) } @@ -438,7 +445,7 @@ let handler = nextConnect({ attachParams: true, }) } console.log('wait_for_transfer', initBal.toString(), bal.toString()) - const delta = bal.amount.minus(initBal.amount) + const delta = bal.minus(initBal.amount) if (delta.gte(amount)) { let stopMe = false @@ -480,10 +487,77 @@ let handler = nextConnect({ attachParams: true, }) return } - await new Promise(resolve => setTimeout(resolve, pollMsec)) + await delay(pollMsec) } }) + .post('/api/reg/place_order/:amount', async (req, res) => { + rateLimitReq(req) + + const amountStr = req.params.amount.split('%20').join(' ') + let amount + try { + amount = await Asset(amountStr) + } catch (err) { + res.json({ + status: 'err', + error: 'Asset parse error' + }) + return + } + + if (!req.session.deposited) { + throwErr(req, 400, ['You have no deposited']) + } + const deposited = req.session.deposited[amount.symbol] + if (!deposited) { + throwErr(req, 400, ['You have no deposited in ' + amount.symbol]) + } + if (deposited !== amountStr) { + throwErr(req, 400, ['You have wrong deposited in ' + amount.symbol]) + } + + let chainProps + for (let i = 0; i < 3; ++i) { + try { + chainProps = await api.getChainPropertiesAsync() + break + } catch (err) { + console.error('/api/reg/place_order - getChainPropertiesAsync', err) + await delay(3000) + } + } + if (!chainProps) { + throwErr(req, 503, ['/api/reg/place_order - Blockchain node unavailable - cannot getChainPropertiesAsync']) + } + if (!chainProps.chain_status) { + throwErr(req, 503, ['/api/reg/place_order - Blockchain node is stopped - chain_status is false']) + } + + const signingKey = config.get('registrar.signing_key') + const orderid = Math.floor(Date.now() / 1000) + let operations = [['limit_order_create', { + owner: config.get('registrar.account'), + orderid, + amount_to_sell: amountStr, + min_to_receive: chainProps.create_account_min_golos_fee, + fill_or_kill: true, + expiration: null + }]] + try { + await broadcast.sendAsync({ + extensions: [], + operations + }, [signingKey]) + + delete req.session.deposited[amount.symbol] + await req.session.save() + } catch (err) { + console.error('/api/reg/place_order - Cannot sell tokens') + throwErr(req, 400, ['Cannot sell tokens']) + } + } + handler = addModalRoutes(handler); export default handler; diff --git a/src/pages/sign/delegate_vs.jsx b/src/pages/sign/delegate_vs.jsx index 8dc7de0..3706011 100644 --- a/src/pages/sign/delegate_vs.jsx +++ b/src/pages/sign/delegate_vs.jsx @@ -10,8 +10,8 @@ import LoginForm from '@/modules/LoginForm'; import { getOAuthCfg, getChainData, } from '@/server/oauth'; import { getOAuthSession, } from '@/server/oauthSession'; import { withSecureHeadersSSR, } from '@/server/security'; +import { steemToVests, } from '@/utils/misc' import { callApi, } from '@/utils/OAuthClient'; -import { steemToVests, } from '@/utils/State'; import validate_account_name from '@/utils/validate_account_name'; function calcMaxInterest(cprops) { diff --git a/src/utils/State.js b/src/utils/misc.js similarity index 90% rename from src/utils/State.js rename to src/utils/misc.js index 350396b..4797f65 100644 --- a/src/utils/State.js +++ b/src/utils/misc.js @@ -1,3 +1,7 @@ +export async function delay(msec) { + await new Promise(resolve => setTimeout(resolve, msec)) +} + export const toAsset = (value) => { const [ amount, symbol ] = value.split(' ') return { amount: parseFloat(amount), symbol }