2017-01-25 18:51:41 +01:00
|
|
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
2016-11-15 12:43:50 +01:00
|
|
|
// This file is part of Parity.
|
|
|
|
|
|
|
|
// Parity is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
|
|
|
// Parity is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2016-12-06 16:09:02 +01:00
|
|
|
import { observable, autorun, action } from 'mobx';
|
2016-12-05 11:47:13 +01:00
|
|
|
import { sha3 } from '~/api/util/sha3';
|
2016-12-09 16:40:45 +01:00
|
|
|
import Contract from '~/api/contract';
|
2016-12-05 11:47:13 +01:00
|
|
|
import Contracts from '~/contracts';
|
2016-11-15 12:43:50 +01:00
|
|
|
|
2017-02-02 16:01:37 +01:00
|
|
|
import { checkIfVerified, findLastRequested, awaitPuzzle } from '~/contracts/verification';
|
2016-12-09 13:44:10 +01:00
|
|
|
import { checkIfTxFailed, waitForConfirmations } from '~/util/tx';
|
2016-11-15 12:43:50 +01:00
|
|
|
|
2016-11-17 14:21:05 +01:00
|
|
|
export const LOADING = 'fetching-contract';
|
|
|
|
export const QUERY_DATA = 'query-data';
|
2016-11-15 16:59:10 +01:00
|
|
|
export const POSTING_REQUEST = 'posting-request';
|
|
|
|
export const POSTED_REQUEST = 'posted-request';
|
2016-12-06 16:09:02 +01:00
|
|
|
export const REQUESTING_CODE = 'requesting-code';
|
2016-11-15 16:59:10 +01:00
|
|
|
export const QUERY_CODE = 'query-code';
|
|
|
|
export const POSTING_CONFIRMATION = 'posting-confirmation';
|
|
|
|
export const POSTED_CONFIRMATION = 'posted-confirmation';
|
|
|
|
export const DONE = 'done';
|
2016-11-15 12:43:50 +01:00
|
|
|
|
2016-11-15 16:59:10 +01:00
|
|
|
export default class VerificationStore {
|
2016-11-15 12:43:50 +01:00
|
|
|
@observable step = null;
|
|
|
|
@observable error = null;
|
|
|
|
|
2016-11-17 14:21:05 +01:00
|
|
|
@observable contract = null;
|
2016-11-15 12:43:50 +01:00
|
|
|
@observable fee = null;
|
2017-02-02 16:01:37 +01:00
|
|
|
@observable accountIsVerified = null;
|
|
|
|
@observable accountHasRequested = null;
|
|
|
|
@observable isAbleToRequest = null;
|
|
|
|
@observable lastRequestValues = null;
|
2017-01-13 09:51:25 +01:00
|
|
|
@observable isServerRunning = null;
|
2016-11-15 12:43:50 +01:00
|
|
|
@observable consentGiven = false;
|
|
|
|
@observable requestTx = null;
|
|
|
|
@observable code = '';
|
2017-03-05 10:27:18 +01:00
|
|
|
@observable isCodeValid = false;
|
2016-11-15 12:43:50 +01:00
|
|
|
@observable confirmationTx = null;
|
|
|
|
|
2017-01-19 08:45:32 +01:00
|
|
|
constructor (api, abi, certifierName, account, isTestnet) {
|
2016-11-15 12:43:50 +01:00
|
|
|
this.api = api;
|
|
|
|
this.account = account;
|
2016-11-28 17:39:55 +01:00
|
|
|
this.isTestnet = isTestnet;
|
2016-11-17 14:21:05 +01:00
|
|
|
|
|
|
|
this.step = LOADING;
|
2017-01-19 08:45:32 +01:00
|
|
|
Contracts.get().badgeReg.fetchCertifierByName(certifierName)
|
2016-12-09 16:40:45 +01:00
|
|
|
.then(({ address }) => {
|
|
|
|
this.contract = new Contract(api, abi).at(address);
|
2016-11-17 14:21:05 +01:00
|
|
|
this.load();
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
2016-12-09 16:40:45 +01:00
|
|
|
console.error('error', err);
|
2016-11-17 14:21:05 +01:00
|
|
|
this.error = 'Failed to fetch the contract: ' + err.message;
|
|
|
|
});
|
2016-11-15 12:43:50 +01:00
|
|
|
|
|
|
|
autorun(() => {
|
|
|
|
if (this.error) {
|
2016-12-06 16:09:02 +01:00
|
|
|
console.error('verification: ' + this.error);
|
2016-11-15 12:43:50 +01:00
|
|
|
}
|
|
|
|
});
|
2017-02-02 16:01:37 +01:00
|
|
|
|
|
|
|
autorun(() => {
|
|
|
|
if (this.step !== QUERY_DATA) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setIfAbleToRequest();
|
|
|
|
});
|
2016-11-15 12:43:50 +01:00
|
|
|
}
|
|
|
|
|
2016-11-17 14:21:05 +01:00
|
|
|
@action load = () => {
|
2016-11-15 12:43:50 +01:00
|
|
|
const { contract, account } = this;
|
2017-01-23 13:39:52 +01:00
|
|
|
|
2016-11-17 14:21:05 +01:00
|
|
|
this.step = LOADING;
|
2016-11-15 12:43:50 +01:00
|
|
|
|
2017-01-13 09:51:25 +01:00
|
|
|
const isServerRunning = this.isServerRunning()
|
|
|
|
.then((isRunning) => {
|
|
|
|
this.isServerRunning = isRunning;
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
this.error = 'Failed to check if server is running: ' + err.message;
|
|
|
|
});
|
|
|
|
|
2016-11-15 12:43:50 +01:00
|
|
|
const fee = contract.instance.fee.call()
|
|
|
|
.then((fee) => {
|
|
|
|
this.fee = fee;
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
this.error = 'Failed to fetch the fee: ' + err.message;
|
|
|
|
});
|
|
|
|
|
2017-02-02 16:01:37 +01:00
|
|
|
const accountIsVerified = checkIfVerified(contract, account)
|
|
|
|
.then((accountIsVerified) => {
|
|
|
|
this.accountIsVerified = accountIsVerified;
|
2016-11-15 12:43:50 +01:00
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
this.error = 'Failed to check if verified: ' + err.message;
|
|
|
|
});
|
|
|
|
|
2017-02-02 16:01:37 +01:00
|
|
|
const accountHasRequested = findLastRequested(contract, account)
|
|
|
|
.then((log) => {
|
|
|
|
this.accountHasRequested = !!log;
|
|
|
|
if (log) {
|
|
|
|
this.lastRequestValues = log.params;
|
|
|
|
this.requestTx = log.transactionHash;
|
2016-11-15 19:05:16 +01:00
|
|
|
}
|
2016-11-15 12:43:50 +01:00
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
this.error = 'Failed to check if requested: ' + err.message;
|
|
|
|
});
|
|
|
|
|
2016-11-17 12:26:59 +01:00
|
|
|
Promise
|
2017-02-02 16:01:37 +01:00
|
|
|
.all([ isServerRunning, fee, accountIsVerified, accountHasRequested ])
|
2016-11-17 12:26:59 +01:00
|
|
|
.then(() => {
|
2016-11-17 14:21:05 +01:00
|
|
|
this.step = QUERY_DATA;
|
2016-11-17 12:26:59 +01:00
|
|
|
});
|
2016-11-15 12:43:50 +01:00
|
|
|
}
|
|
|
|
|
2016-11-17 14:21:05 +01:00
|
|
|
@action setConsentGiven = (consentGiven) => {
|
|
|
|
this.consentGiven = consentGiven;
|
|
|
|
}
|
|
|
|
|
|
|
|
@action setCode = (code) => {
|
2016-11-28 17:39:55 +01:00
|
|
|
const { contract, account } = this;
|
2016-12-23 16:43:13 +01:00
|
|
|
|
|
|
|
if (!contract || !account || code.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
2016-11-28 17:39:55 +01:00
|
|
|
|
|
|
|
const confirm = contract.functions.find((fn) => fn.name === 'confirm');
|
|
|
|
const options = { from: account };
|
2017-01-10 18:56:56 +01:00
|
|
|
const values = [ sha3.text(code) ];
|
2016-11-28 17:39:55 +01:00
|
|
|
|
2016-11-17 14:21:05 +01:00
|
|
|
this.code = code;
|
2017-03-05 10:27:18 +01:00
|
|
|
this.isCodeValid = false;
|
2016-11-28 17:39:55 +01:00
|
|
|
confirm.estimateGas(options, values)
|
|
|
|
.then((gas) => {
|
|
|
|
options.gas = gas.mul(1.2).toFixed(0);
|
|
|
|
return confirm.call(options, values);
|
|
|
|
})
|
|
|
|
.then((result) => {
|
|
|
|
this.isCodeValid = result === true;
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
this.error = 'Failed to check if the code is valid: ' + err.message;
|
|
|
|
});
|
2016-11-17 14:21:05 +01:00
|
|
|
}
|
|
|
|
|
2016-12-07 19:22:13 +01:00
|
|
|
requestValues = () => []
|
|
|
|
|
2016-11-15 12:43:50 +01:00
|
|
|
@action sendRequest = () => {
|
2017-02-02 16:01:37 +01:00
|
|
|
const { api, account, contract, fee } = this;
|
2016-11-15 12:43:50 +01:00
|
|
|
|
|
|
|
const request = contract.functions.find((fn) => fn.name === 'request');
|
|
|
|
const options = { from: account, value: fee.toString() };
|
2016-12-07 19:22:13 +01:00
|
|
|
const values = this.requestValues();
|
2016-11-15 12:43:50 +01:00
|
|
|
|
2017-02-02 16:01:37 +01:00
|
|
|
this.shallSkipRequest(values)
|
|
|
|
.then((skipRequest) => {
|
|
|
|
if (skipRequest) {
|
|
|
|
return;
|
|
|
|
}
|
2016-11-15 12:43:50 +01:00
|
|
|
|
2017-02-02 16:01:37 +01:00
|
|
|
this.step = POSTING_REQUEST;
|
|
|
|
return request.estimateGas(options, values)
|
|
|
|
.then((gas) => {
|
|
|
|
options.gas = gas.mul(1.2).toFixed(0);
|
|
|
|
return request.postTransaction(options, values);
|
|
|
|
})
|
|
|
|
.then((handle) => {
|
|
|
|
// The "request rejected" error doesn't have any property to distinguish
|
|
|
|
// it from other errors, so we can't give a meaningful error here.
|
|
|
|
return api.pollMethod('parity_checkRequest', handle);
|
|
|
|
})
|
|
|
|
.then((txHash) => {
|
|
|
|
this.requestTx = txHash;
|
|
|
|
return checkIfTxFailed(api, txHash, options.gas)
|
|
|
|
.then((hasFailed) => {
|
|
|
|
if (hasFailed) {
|
|
|
|
throw new Error('Transaction failed, all gas used up.');
|
|
|
|
}
|
|
|
|
this.step = POSTED_REQUEST;
|
|
|
|
return waitForConfirmations(api, txHash, 1);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
})
|
2017-01-20 13:23:21 +01:00
|
|
|
.then(() => this.checkIfReceivedCode())
|
|
|
|
.then((hasReceived) => {
|
|
|
|
if (hasReceived) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-06 16:09:02 +01:00
|
|
|
this.step = REQUESTING_CODE;
|
2017-01-20 13:23:21 +01:00
|
|
|
return this
|
|
|
|
.requestCode()
|
|
|
|
.then(() => awaitPuzzle(api, contract, account));
|
2016-11-15 12:43:50 +01:00
|
|
|
})
|
|
|
|
.then(() => {
|
2016-11-28 17:39:55 +01:00
|
|
|
this.step = QUERY_CODE;
|
2016-11-15 12:43:50 +01:00
|
|
|
})
|
|
|
|
.catch((err) => {
|
2016-12-06 16:09:02 +01:00
|
|
|
this.error = 'Failed to request a confirmation code: ' + err.message;
|
2016-11-15 12:43:50 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-11-15 16:21:53 +01:00
|
|
|
@action queryCode = () => {
|
2016-11-15 16:59:10 +01:00
|
|
|
this.step = QUERY_CODE;
|
2016-11-15 12:43:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@action sendConfirmation = () => {
|
|
|
|
const { api, account, contract, code } = this;
|
2017-01-10 18:56:56 +01:00
|
|
|
const token = sha3.text(code);
|
2016-11-15 12:43:50 +01:00
|
|
|
|
|
|
|
const confirm = contract.functions.find((fn) => fn.name === 'confirm');
|
|
|
|
const options = { from: account };
|
|
|
|
const values = [ token ];
|
|
|
|
|
2016-11-15 16:59:10 +01:00
|
|
|
this.step = POSTING_CONFIRMATION;
|
2016-11-15 12:43:50 +01:00
|
|
|
confirm.estimateGas(options, values)
|
|
|
|
.then((gas) => {
|
|
|
|
options.gas = gas.mul(1.2).toFixed(0);
|
|
|
|
return confirm.postTransaction(options, values);
|
|
|
|
})
|
|
|
|
.then((handle) => {
|
|
|
|
// TODO: The "request rejected" error doesn't have any property to
|
|
|
|
// distinguish it from other errors, so we can't give a meaningful error here.
|
|
|
|
return api.pollMethod('parity_checkRequest', handle);
|
|
|
|
})
|
|
|
|
.then((txHash) => {
|
|
|
|
this.confirmationTx = txHash;
|
2016-11-16 13:28:51 +01:00
|
|
|
return checkIfTxFailed(api, txHash, options.gas)
|
2016-11-17 12:26:59 +01:00
|
|
|
.then((hasFailed) => {
|
|
|
|
if (hasFailed) {
|
|
|
|
throw new Error('Transaction failed, all gas used up.');
|
|
|
|
}
|
|
|
|
this.step = POSTED_CONFIRMATION;
|
|
|
|
return waitForConfirmations(api, txHash, 1);
|
|
|
|
});
|
2016-11-15 12:43:50 +01:00
|
|
|
})
|
|
|
|
.then(() => {
|
2016-11-15 16:59:10 +01:00
|
|
|
this.step = DONE;
|
2016-11-15 12:43:50 +01:00
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
this.error = 'Failed to send the verification code: ' + err.message;
|
|
|
|
});
|
|
|
|
}
|
2016-11-15 16:21:53 +01:00
|
|
|
|
|
|
|
@action done = () => {
|
2016-11-15 16:59:10 +01:00
|
|
|
this.step = DONE;
|
2016-11-15 16:21:53 +01:00
|
|
|
}
|
2016-11-15 12:43:50 +01:00
|
|
|
}
|