Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix QA issues with KYC oracle #1000

Draft
wants to merge 12 commits into
base: develop
Choose a base branch
from
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"flatten:contracts": "mkdir -p ./build/flattened/ && steamroller contracts/colonyNetwork/IColonyNetwork.sol > build/flattened/flatIColonyNetwork.sol && steamroller contracts/colony/IColony.sol > build/flattened/flatIColony.sol && steamroller contracts/reputationMiningCycle/IReputationMiningCycle.sol > build/flattened/flatIReputationMiningCycle.sol && steamroller contracts/colony/IMetaColony.sol > build/flattened/flatIMetaColony.sol && steamroller contracts/common/IRecovery.sol > build/flattened/flatIRecovery.sol && steamroller contracts/common/IEtherRouter.sol > build/flattened/flatIEtherRouter.sol",
"test:reputation": "npm run start:blockchain:client & truffle migrate --reset --compile-all && nyc truffle test ./test/reputation-system/* ./test/reputation-system/reputation-mining-client/* --network development",
"test:reputation:coverage": "SOLIDITY_COVERAGE=1 truffle run coverage --solcoverjs ./.solcover.reputation.js --network coverage --temp build --file='./test/reputation-system/**/*'",
"test:contracts": "npm run start:blockchain:client & truffle migrate --reset --compile-all && truffle test ./test/contracts-network/* --network development",
"test:contracts": "npm run start:blockchain:client & truffle migrate --reset --compile-all && truffle test ./test/contracts-network/* ./test/packages/* --network development",
"test:contracts:extensions": "npm run start:blockchain:client & truffle migrate --reset --compile-all && truffle test ./test/extensions/* --network development",
"test:contracts:chainid": "npm run stop:blockchain:client && npm run start:blockchain:client & truffle migrate --reset --compile-all && truffle test ./test-chainid/*",
"test:contracts:chainid:coverage": "SOLIDITY_COVERAGE=1 truffle run coverage --solcoverjs ./.solcover.chainid.js --network coverage --temp build --file='./test-chainid/**/*'",
Expand Down Expand Up @@ -83,6 +83,7 @@
"@codechecks/client": "^0.1.5",
"@colony/eslint-config-colony": "8.0.1",
"async-request": "^1.2.0",
"axios": "^0.21.1",
"babel-plugin-istanbul": "^6.0.0",
"bignumber.js": "^9.0.0",
"bluebird": "^3.7.2",
Expand All @@ -94,6 +95,7 @@
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-eslint-comments": "^3.1.2",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-no-only-tests": "^2.6.0",
"eslint-plugin-prettier": "^3.1.2",
"eth-ens-namehash": "^2.0.8",
"eth-gas-reporter": "^0.2.15",
Expand Down Expand Up @@ -129,7 +131,5 @@
"workspaces": [
"packages/*"
],
"dependencies": {
"eslint-plugin-no-only-tests": "^2.4.0"
}
"dependencies": {}
}
8 changes: 8 additions & 0 deletions packages/kyc-oracle/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM node:12.18.2
COPY ./packages ./packages
COPY ./package.json ./
COPY ./yarn.lock ./
COPY ./build ./build
RUN yarn
EXPOSE 3000
CMD node $NODE_ARGS packages/kyc-oracle/bin/index.js --whitelistAddress $WHITELIST_ADDRESS --apiKey $API_KEY --privateKey $PRIVATE_KEY $ARGS
173 changes: 112 additions & 61 deletions packages/kyc-oracle/KycOracle.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const { getChallenge, verifyEthSignature } = etherpass;
const sqlite3 = require("sqlite3");
const sqlite = require("sqlite");
const bodyParser = require("body-parser");
const { colonyIOCors, ConsoleAdapter, updateGasEstimate } = require("../package-utils");

class KycOracle {
/**
Expand All @@ -19,14 +20,22 @@ class KycOracle {
* @param {Object} provider Ethers provider that allows access to an ethereum node.
* @param {Number} port The port the oracle will serve on
*/
constructor({ privateKey, adminAddress, apiKey, loader, provider, dbPath, port = 3003 }) {
constructor({ privateKey, adminAddress, apiKey, loader, provider, adapter, dbPath, port = 3003 }) {
this.provider = provider;

if (privateKey) {
this.wallet = new ethers.Wallet(privateKey, this.provider);
this.adminAddress = this.wallet.address;
} else {
this.wallet = provider.getSigner(adminAddress);
this.adminAddress = adminAddress;
}

this.adapter = adapter;
if (typeof this.adapter === "undefined") {
this.adapter = new ConsoleAdapter();
}

console.log("Transactions will be signed from ", this.adminAddress);

this.apiKey = apiKey;
Expand All @@ -37,28 +46,10 @@ class KycOracle {
}

this.loader = loader;
this.provider = provider;

this.app = express();

this.app.use(function (req, res, next) {
const origin = req.get("origin");

const colonyRegex = /.*colony\.io/;
const colonyMatches = colonyRegex.exec(origin);

const localRegex = /http:\/\/(127(\.\d+){1,3}|[0:]+1|localhost)/;

const localMatches = localRegex.exec(origin);

if (colonyMatches) {
res.header("Access-Control-Allow-Origin", colonyMatches[0]);
} else if (localMatches) {
res.header("Access-Control-Allow-Origin", "*");
}
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Synaps-Session-Id");
next();
});
this.app.use(colonyIOCors);

this.app.use(bodyParser.json());

Expand Down Expand Up @@ -91,12 +82,11 @@ class KycOracle {
if (!sessionId) {
// Create a session for them
const { data } = await axios.post(
"https://workflow-api.synaps.io/v2/session/init",
`https://workflow-api.synaps.io/v2/session/init?alias=${address}`,
{}, // No data
{
headers: {
"Api-Key": this.apiKey,
alias: address,
},
}
);
Expand Down Expand Up @@ -135,7 +125,7 @@ class KycOracle {
if (validated) {
const alreadyApproved = await this.whitelist.getApproval(userAddress);
if (!alreadyApproved) {
await this.updateGasEstimate("safeLow");
await updateGasEstimate("safeLow", this.chainId, this.adapter);
const gasEstimate = await this.whitelist.estimateGas.approveUsers([userAddress], true);
this.whitelist.approveUsers([userAddress], true, { gasLimit: gasEstimate, gasPrice: this.gasPrice });
}
Expand Down Expand Up @@ -187,36 +177,10 @@ class KycOracle {
this.whitelistContractDef = await this.loader.load({ contractName: "Whitelist" }, { abi: true, address: false });
this.whitelist = new ethers.Contract(whitelistAddress, this.whitelistContractDef.abi, this.wallet);

await this.updateGasEstimate("safeLow");
await updateGasEstimate("safeLow", this.chainId, this.adapter);
await this.createDB();
}

/**
* Update the gas estimate
* @param {string} Transaction speed (fastest, fast, safeLow)
* @return {Promise}
*/
async updateGasEstimate(type) {
if (this.chainId === 100) {
this.gasPrice = ethers.utils.hexlify(1000000000);
return;
}

try {
// Get latest from ethGasStation
const { data } = await axios.get("https://ethgasstation.info/json/ethgasAPI.json");

if (data[type]) {
this.gasPrice = ethers.utils.hexlify((data[type] / 10) * 1e9);
} else {
this.gasPrice = ethers.utils.hexlify(20000000000);
}
} catch (err) {
console.log(`Error during gas estimation: ${err}`);
this.gasPrice = ethers.utils.hexlify(20000000000);
}
}

async createDB() {
const db = await sqlite.open({ filename: this.dbPath, driver: sqlite3.Database });
await db.run(
Expand All @@ -228,45 +192,132 @@ class KycOracle {
await db.close();
}

// async getSessionForAddress(address) {
// const db = await sqlite.open({ filename: this.dbPath, driver: sqlite3.Database });
// const res = await db.all(
// `SELECT DISTINCT users.session_id as session_id
// FROM users
// WHERE users.address="${address}"`
// );
// await db.close();
// const sessionIds = res.map((x) => x.session_id);
// if (sessionIds.length > 1) {
// throw new Error("More than one matching address");
// }
// return sessionIds[0];
// }

async getSessionForAddress(address) {
const db = await sqlite.open({ filename: this.dbPath, driver: sqlite3.Database });
const res = await db.all(
await this.checkDBOpen();
let res = await this.db.all(
`SELECT DISTINCT users.session_id as session_id
FROM users
WHERE users.address="${address}"`
);
await db.close();
const sessionIds = res.map((x) => x.session_id);
if (sessionIds.length > 1) {
throw new Error("More than one matching address");
}
return sessionIds[0];
if (sessionIds.length === 1) {
return sessionIds[0];
}

let { data } = await axios.get(`https://workflow-api.synaps.io/v2/session/list/FINISHED?alias=${address}`, {
headers: {
"Api-Key": this.apiKey,
},
});
if (data.length === 1) {
return data[0].session_id;
}

// Is it pending?
res = await axios.get(`https://workflow-api.synaps.io/v2/session/list/PENDING?alias=${address}`, {
headers: {
"Api-Key": this.apiKey,
},
});

data = res.data;

if (data.length === 1) {
return data[0].session_id;
}

res = await axios.get(`https://workflow-api.synaps.io/v2/session/list/CANCELLED?alias=${address}`, {
headers: {
"Api-Key": this.apiKey,
},
});
data = res.data;
if (data.length === 1) {
await this.setSessionForAddress(address, data[0].session_id);
return data[0].session_id;
}

return undefined;
}

async setSessionForAddress(address, session) {
const db = await sqlite.open({ filename: this.dbPath, driver: sqlite3.Database });
await db.run(
await this.checkDBOpen();
await this.db.run(
`INSERT INTO users (address, session_id)
VALUES ("${address}", "${session}")
ON CONFLICT(address) DO
UPDATE SET session_id = "${session}"`
);
await db.close();
}

// async getAddressForSession(sessionId) {
// const db = await sqlite.open({ filename: this.dbPath, driver: sqlite3.Database });
// const res = await db.all(
// `SELECT DISTINCT users.address as address
// FROM users
// WHERE users.session_id="${sessionId}"`
// );
// await db.close();
// const addresses = res.map((x) => x.address);
// if (addresses.length > 1) {
// throw new Error("More than one matching address");
// }
// return addresses[0];
// }

async getAddressForSession(sessionId) {
const db = await sqlite.open({ filename: this.dbPath, driver: sqlite3.Database });
const res = await db.all(
await this.checkDBOpen();
const res = await this.db.all(
`SELECT DISTINCT users.address as address
FROM users
WHERE users.session_id="${sessionId}"`
);
await db.close();
const addresses = res.map((x) => x.address);
if (addresses.length > 1) {
throw new Error("More than one matching address");
}
return addresses[0];
if (addresses.length === 1) {
return addresses[0];
}

const { data } = await axios.get("https://workflow-api.synaps.io/v2/session/info", {
headers: {
"Api-Key": this.apiKey,
"Session-Id": sessionId,
},
});

if (data.alias) {
await this.setSessionForAddress(data.alias, sessionId);
return data.alias;
}

return undefined;
}

async checkDBOpen() {
if (this.db) {
return;
}
this.db = await sqlite.open({ filename: this.dbPath, driver: sqlite3.Database });
}
}

Expand Down
9 changes: 6 additions & 3 deletions packages/kyc-oracle/bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ const { argv } = require("yargs")
.option("privateKey", { string: true })
.option("whitelistAddress", { string: true })
.option("apiKey", { string: true })
.option("adminAddress", { string: true });
.option("adminAddress", { string: true })
.option("providerAddress", { type: "array", default: [] });

const KycOracle = require("../KycOracle");
const TruffleLoader = require("../TruffleLoader").default;

const { adminAddress, privateKey, whitelistAddress, apiKey, network, providerPort, providerAddress, dbPath, port } = argv;
const { adminAddress, privateKey, whitelistAddress, apiKey, network, localProviderPort, localProviderAddress, providerAddress, dbPath, port } = argv;
const supportedInfuraNetworks = ["mainnet"];

if ((!adminAddress && !privateKey) || !whitelistAddress || !apiKey) {
Expand All @@ -34,8 +35,10 @@ if (network) {
process.exit();
}
provider = new ethers.providers.InfuraProvider(network);
} else if (providerAddress.length === 0) {
provider = new ethers.providers.JsonRpcProvider(`http://${localProviderAddress || "localhost"}:${localProviderPort || "8545"}`);
} else {
provider = new ethers.providers.JsonRpcProvider(`http://${providerAddress || "localhost"}:${providerPort || "8545"}`);
provider = new ethers.providers.JsonRpcProvider(providerAddress[0]);
}

const client = new KycOracle({ privateKey, adminAddress, apiKey, loader, provider, dbPath, port });
Expand Down
8 changes: 8 additions & 0 deletions packages/metatransaction-broadcaster/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM node:12.18.2
COPY ./packages ./packages
COPY ./package.json ./
COPY ./yarn.lock ./
COPY ./build ./build
RUN yarn
EXPOSE 3000
CMD node $NODE_ARGS packages/metatransaction-broadcaster/bin/index.js --colonyNetworkAddress $COLONYNETWORK_ADDRESS --privateKey $PRIVATE_KEY $ARGS
Loading