const crypto = require("crypto");
const { hexToBase64 } = require("./util");
const privacyProxyAbi = require("../solidity/PrivacyProxy.json").output.abi;
/**
*
* @module flexiblePrivacyGroup
*/
function FlexiblePrivacyGroup(web3) {
web3.eth.extend({
property: "flexiblePrivacyGroup",
methods: [
/**
* @function find
* @param {String[]} public enclave keys
* @return {Object}
*/
{
name: "find",
call: "privx_findFlexiblePrivacyGroup",
params: 1,
},
],
});
/**
* Create an on chain privacy group
* @function create
* @param {Object} options Map to add the members
* @param {string} options.privacyGroupId Privacy group ID to add to
* @param {string} options.privateKey Private Key used to sign transaction with
* @param {string} options.enclaveKey Enclave public key
* @param {string[]} options.participants list of enclaveKey to pass to the contract to add to the group
* @return {Promise<T>}
*/
const create = (options) => {
const contract = new web3.eth.Contract(privacyProxyAbi);
const functionAbi = contract._jsonInterface.find((e) => {
return e.name === "addParticipants";
});
const functionArgs = web3.eth.abi
.encodeParameters(functionAbi.inputs, [
options.participants.map((e) => {
return Buffer.from(e, "base64");
}),
])
.slice(2);
// Generate a random ID if one was not passed in
const privacyGroupId =
options.privacyGroupId || crypto.randomBytes(32).toString("base64");
const functionCall = {
to: "0x000000000000000000000000000000000000007c",
data: functionAbi.signature + functionArgs,
privateFrom: options.enclaveKey,
privacyGroupId,
privateKey: options.privateKey,
};
return web3.priv
.generateAndSendRawTransaction(functionCall)
.then((transactionHash) => {
return web3.priv.waitForTransactionReceipt(transactionHash);
});
};
/**
* Get Participants of a specific privacy group
* @function getParticipants
* @param {Object} options
* @param {string} options.privacyGroupId Privacy group ID
* @return {Promise<T>}
*/
const getParticipants = (options) => {
const contract = new web3.eth.Contract(privacyProxyAbi);
const functionAbi = contract._jsonInterface.find((e) => {
return e.name === "getParticipants";
});
return web3.priv
.call(options.privacyGroupId, {
to: "0x000000000000000000000000000000000000007c",
data: functionAbi.signature,
})
.then((result) => {
const functionArgs = web3.eth.abi.decodeParameters(
functionAbi.outputs,
result
)[0];
return functionArgs.map((hash) => {
return hexToBase64(hash.slice(2));
});
});
};
/**
* Remove a member from an on-chain privacy group
* @function removeFrom
* @param {Object} options Map to add the members
* @param {string} options.privacyGroupId Privacy group ID to add to
* @param {string} options.privateKey Private Key used to sign transaction with
* @param {string} options.enclaveKey Enclave public key
* @param {string} options.participant single enclaveKey to pass to the contract to add to the group
* @returns {Promise<T>}
*/
const removeFrom = (options) => {
const contract = new web3.eth.Contract(privacyProxyAbi);
const functionAbi = contract._jsonInterface.find((e) => {
return e.name === "removeParticipant";
});
const functionArgs = web3.eth.abi
.encodeParameters(functionAbi.inputs, [
Buffer.from(options.participant, "base64"),
])
.slice(2);
const functionCall = {
to: "0x000000000000000000000000000000000000007c",
data: functionAbi.signature + functionArgs,
privateFrom: options.enclaveKey,
privacyGroupId: options.privacyGroupId,
privateKey: options.privateKey,
};
return web3.priv
.generateAndSendRawTransaction(functionCall)
.then((transactionHash) => {
return web3.priv.waitForTransactionReceipt(transactionHash);
});
};
/**
* Either lock or unlock the privacy group for member adding
* @function setLockState
* @param {Object} options Map to lock the group
* @param {string} options.privacyGroupId Privacy group ID to lock/unlock
* @param {string} options.privateKey Private Key used to sign transaction with
* @param {string} options.enclaveKey Enclave public key
* @param {boolean} options.lock boolean indicating whether to lock or unlock
* @returns {Promise<T>}
*/
const setLockState = (options) => {
const contract = new web3.eth.Contract(privacyProxyAbi);
const functionAbi = contract._jsonInterface.find((e) => {
return e.name === (options.lock ? "lock" : "unlock");
});
const functionCall = {
to: "0x000000000000000000000000000000000000007c",
data: functionAbi.signature,
privateFrom: options.enclaveKey,
privacyGroupId: options.privacyGroupId,
privateKey: options.privateKey,
};
return web3.priv
.generateAndSendRawTransaction(functionCall)
.then(async (transactionHash) => {
return web3.priv.waitForTransactionReceipt(transactionHash);
});
};
/**
* Add to an existing on-chain privacy group
* @function addTo
* @param {Object} options Map to add the members
* @param {string} options.privacyGroupId Privacy group ID to add to
* @param {string} options.privateKey Private Key used to sign transaction with
* @param {string} options.enclaveKey Enclave public key
* @param {string[]} options.participants list of enclaveKey to pass to the contract to add to the group
* @returns {Promise<T>}
*/
const addTo = (options) => {
return setLockState(Object.assign(options, { lock: true })).then(
(receipt) => {
if (receipt.status === "0x1") {
return create(options);
}
throw Error(
`Locking the privacy group failed, receipt: ${JSON.stringify(
receipt
)}`
);
}
);
};
Object.assign(web3.eth.flexiblePrivacyGroup, {
create,
getParticipants,
removeFrom,
setLockState,
addTo,
});
return web3;
}
module.exports = FlexiblePrivacyGroup;