diff --git a/backend/src/api/import-from-matrix.js b/backend/src/api/import-from-matrix.js index c61aeeb..5c08fe3 100644 --- a/backend/src/api/import-from-matrix.js +++ b/backend/src/api/import-from-matrix.js @@ -1,13 +1,17 @@ -const { getClient } = require('../matrix-bot'); +const ethers = require('ethers'); +const { getClient } = require('../matrix-bot'); const { matrixUserToAuthorAddress } = require('../util/db'); const write = require('../util/forum/write'); -const { dao } = require('../util/contracts'); +const { dao, getProvider } = require('../util/contracts'); const { ETH_NETWORK, + ETH_PRIVATE_KEY, } = process.env; +const wallet = new ethers.Wallet(ETH_PRIVATE_KEY, getProvider()); + const addPostWithRetry = async (authors, hash, citations, retryDelay = 5000) => { try { await dao.addPost(authors, hash, citations); @@ -58,8 +62,6 @@ module.exports = async (req, res) => { } // We want to add a post representing this matrix message. - // We can't sign it on behalf of the author. - // That means we need to support posts without signatures. const authors = [{ authorAddress, weightPPM: 1000000 }]; // TODO: Take citations as input to this API call, referencing other posts or matrix events const citations = []; @@ -68,9 +70,12 @@ module.exports = async (req, res) => { roomId, eventId, }; + // We can't sign it on behalf of the author, but we can sign it with our own key + const sender = wallet.getAddress(); + const signature = await wallet.signMessage(); const { hash } = await write({ - authors, citations, content, embeddedData, + sender, authors, citations, content, embeddedData, signature, }); // Now we want to add a post on-chain diff --git a/backend/src/contract-listeners/validation-pools.js b/backend/src/contract-listeners/validation-pools.js index 67ea451..2aa0f4b 100644 --- a/backend/src/contract-listeners/validation-pools.js +++ b/backend/src/contract-listeners/validation-pools.js @@ -21,7 +21,7 @@ const start = async () => { if (proposal.stage === BigInt(5)) { // Proposal is accepted enableStaking = false; - console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal accepted. Not starting.`); + console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is accepted. Not starting.`); } else if (proposal.stage === BigInt(4)) { // Proposal is failed console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is failed. No effect.`); @@ -30,7 +30,7 @@ const start = async () => { console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is stage ${proposal.stage.toString()}. Registering listener.`); const proposalAcceptedHandler = (event) => { if (event.returnValues.proposalIndex === STOP_PROPOSAL_ID) { - console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal accepted. Stopping.`); + console.log(`STOP_PROPOSAL_ID ${STOP_PROPOSAL_ID} proposal is accepted. Stopping.`); enableStaking = false; proposals.off('ProposalAccepted', proposalAcceptedHandler); } @@ -53,7 +53,7 @@ const start = async () => { console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is stage ${proposal.stage.toString()}. Registering listener.`); const proposalAcceptedHandler = (event) => { if (event.returnValues.proposalIndex === START_PROPOSAL_ID) { - console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal accepted. Starting.`); + console.log(`START_PROPOSAL_ID ${START_PROPOSAL_ID} proposal is accepted. Starting.`); enableStaking = true; proposals.off('ProposalAccepted', proposalAcceptedHandler); } diff --git a/backend/src/util/contracts.js b/backend/src/util/contracts.js index 28e35ea..228724b 100644 --- a/backend/src/util/contracts.js +++ b/backend/src/util/contracts.js @@ -4,34 +4,39 @@ const { getContractAddressByNetworkName } = require('./contract-config'); const DAOArtifact = require('../../contractArtifacts/DAO.json'); const ProposalsArtifact = require('../../contractArtifacts/Proposals.json'); -const network = process.env.ETH_NETWORK; +const { + ETH_NETWORK, + ETH_PRIVATE_KEY, + INFURA_API_KEY, +} = process.env; -console.log('network:', network); +console.log('network:', ETH_NETWORK); const getProvider = () => { - switch (network) { + switch (ETH_NETWORK) { case 'localhost': return ethers.getDefaultProvider('http://localhost:8545'); case 'sepolia': return new ethers.InfuraProvider( - network, - process.env.INFURA_API_KEY, + ETH_NETWORK, + INFURA_API_KEY, ); default: throw new Error('Unknown network'); } }; -const wallet = new ethers.Wallet(process.env.ETH_PRIVATE_KEY, getProvider()); +const wallet = new ethers.Wallet(ETH_PRIVATE_KEY, getProvider()); module.exports = { + getProvider, dao: new ethers.Contract( - getContractAddressByNetworkName(process.env.ETH_NETWORK, 'DAO'), + getContractAddressByNetworkName(ETH_NETWORK, 'DAO'), DAOArtifact.abi, wallet, ), proposals: new ethers.Contract( - getContractAddressByNetworkName(process.env.ETH_NETWORK, 'Proposals'), + getContractAddressByNetworkName(ETH_NETWORK, 'Proposals'), ProposalsArtifact.abi, wallet, ), diff --git a/backend/src/util/forum/read.js b/backend/src/util/forum/read.js index 819a18b..988fd13 100644 --- a/backend/src/util/forum/read.js +++ b/backend/src/util/forum/read.js @@ -10,12 +10,12 @@ const read = async (hash) => { data.embeddedData = data.embeddedData || undefined; const { - authors, content, signature, embeddedData, citations, + sender, authors, content, signature, embeddedData, citations, } = data; // Verify hash const derivedHash = objectHash({ - authors, content, signature, embeddedData, + sender, authors, content, signature, embeddedData, }); if (derivedHash !== hash) { throw new Error('hash mismatch'); @@ -29,7 +29,7 @@ const read = async (hash) => { } return { - authors, content, signature, embeddedData, citations, + sender, authors, content, signature, embeddedData, citations, }; }; diff --git a/backend/src/util/forum/write.js b/backend/src/util/forum/write.js index b6df2b6..03fe768 100644 --- a/backend/src/util/forum/write.js +++ b/backend/src/util/forum/write.js @@ -4,26 +4,24 @@ const verifySignature = require('../verify-signature'); const { forum } = require('../db'); const write = async ({ - authors, content, citations, embeddedData, signature, + sender, authors, content, citations, embeddedData, signature, }) => { - if (signature) { // Check author signature - if (!verifySignature({ - authors, content, signature, embeddedData, - })) { - const err = new Error(); - err.status = 403; - err.message = 'Signature verification failed'; - throw err; - } + if (!verifySignature({ + sender, authors, content, signature, embeddedData, + })) { + const err = new Error(); + err.status = 403; + err.message = 'Signature verification failed'; + throw err; } // Compute content hash const data = { - authors, content, signature, embeddedData, citations, + sender, authors, content, signature, embeddedData, citations, }; const hash = objectHash({ - authors, content, signature, embeddedData, + sender, authors, content, signature, embeddedData, }); // Store content diff --git a/backend/src/util/verify-signature.js b/backend/src/util/verify-signature.js index d1454f8..89bc81f 100644 --- a/backend/src/util/verify-signature.js +++ b/backend/src/util/verify-signature.js @@ -1,7 +1,7 @@ const { recoverPersonalSignature } = require('@metamask/eth-sig-util'); const verifySignature = ({ - authors, content, signature, embeddedData, + sender, authors, content, signature, embeddedData, }) => { let contentToVerify = content; if (embeddedData && Object.entries(embeddedData).length) { @@ -9,9 +9,9 @@ const verifySignature = ({ } try { const account = recoverPersonalSignature({ data: contentToVerify, signature }); - const authorAddresses = authors.map((author) => author.authorAddress.toLowerCase()); - if (!authorAddresses.includes(account.toLowerCase())) { - console.log('error: signer is not among the authors'); + const addresses = authors.concat(sender).map((author) => author.authorAddress.toLowerCase()); + if (!addresses.includes(account.toLowerCase())) { + console.log('error: signer is not among the authors or sender'); return false; } } catch (e) {