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 random ordering of miners in response windows #1102

Merged
merged 12 commits into from
Dec 16, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ contract ReputationMiningCycleCommon is ReputationMiningCycleStorage, PatriciaTr
return false;
}
uint256 target = windowOpenFor * Y;
if (uint256(keccak256(abi.encodePacked(minerAddress, _stage))) > target) {
if (uint256(keccak256(abi.encodePacked(minerAddress, address(this), _stage))) > target) {
return false;
}
}
Expand Down
26 changes: 24 additions & 2 deletions helpers/test-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,18 @@ exports.currentBlock = async function currentBlock() {
return p;
};

exports.getBlock = async function getBlock(blockNumber) {
const p = new Promise((resolve, reject) => {
web3.eth.getBlock(blockNumber, (err, res) => {
if (err) {
return reject(err);
}
return resolve(res);
});
});
return p;
};

exports.getBlockTime = async function getBlockTime(blockNumber = "latest") {
const p = new Promise((resolve, reject) => {
web3.eth.getBlock(blockNumber, (err, res) => {
Expand Down Expand Up @@ -1032,8 +1044,12 @@ exports.getMiningCycleCompletePromise = async function getMiningCycleCompletePro
colonyNetworkEthers.on("ReputationMiningCycleComplete", async (_hash, _nLeaves, event) => {
const colonyNetwork = await IColonyNetwork.at(colonyNetworkEthers.address);
const newHash = await colonyNetwork.getReputationRootHash();
expect(newHash).to.not.equal(oldHash, "The old and new hashes are the same");
expect(newHash).to.equal(expectedHash, "The network root hash doens't match the one submitted");
if (oldHash) {
expect(newHash).to.not.equal(oldHash, "The old and new hashes are the same");
}
if (expectedHash) {
expect(newHash).to.equal(expectedHash, "The network root hash doesn't match the one submitted");
}
event.removeListener();
resolve();
});
Expand Down Expand Up @@ -1104,6 +1120,12 @@ exports.rolesToBytes32 = function rolesToBytes32(roles) {
return `0x${new BN(roles.map((role) => new BN(1).shln(role)).reduce((a, b) => a.or(b), new BN(0))).toString(16, 64)}`;
};

exports.sleep = function sleep(ms) {
kronosapiens marked this conversation as resolved.
Show resolved Hide resolved
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
};

class TestAdapter {
constructor() {
this.outputs = [];
Expand Down
2 changes: 2 additions & 0 deletions packages/metatransaction-broadcaster/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ COPY ./package.json ./
COPY ./package-lock.json ./
COPY ./build ./build
RUN npm ci
RUN cd ./packages/metatransaction-broadcaster/ && npm i
RUN cd ./packages/package-utils/ && npm i
EXPOSE 3000
CMD node $NODE_ARGS packages/metatransaction-broadcaster/bin/index.js --colonyNetworkAddress $COLONYNETWORK_ADDRESS --privateKey $PRIVATE_KEY --gasLimit $GASLIMIT $ARGS
3 changes: 3 additions & 0 deletions packages/reputation-miner/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
FROM node:14-bullseye
RUN apt-get update || : && apt-get install python -y
COPY ./packages ./packages
COPY ./package.json ./
COPY ./package-lock.json ./
COPY ./build ./build
RUN npm install
RUN cd ./packages/reputation-miner/ && npm i
RUN cd ./packages/package-utils/ && npm i
EXPOSE 3000
CMD node $NODE_ARGS packages/reputation-miner/bin/index.js --dbPath $REPUTATION_JSON_PATH --colonyNetworkAddress $COLONYNETWORK_ADDRESS --privateKey $PRIVATE_KEY --syncFrom $SYNC_FROM_BLOCK $ARGS
58 changes: 35 additions & 23 deletions packages/reputation-miner/ReputationMiner.js
Original file line number Diff line number Diff line change
Expand Up @@ -753,11 +753,13 @@ class ReputationMiner {
if (!entryIndex) {
entryIndex = await this.getEntryIndex(); // eslint-disable-line no-param-reassign
}
let gasEstimate = ethers.BigNumber.from(1000000);
let gasEstimate;
try {
gasEstimate = await repCycle.estimate.submitRootHash(hash, nLeaves, jrh, entryIndex);
} catch (err) { // eslint-disable-line no-empty

gasEstimate = await repCycle.estimateGas.submitRootHash(hash, nLeaves, jrh, entryIndex);
// Add some extra gas just in case the details change and a little more is needed
gasEstimate = gasEstimate.mul(11).div(10);
kronosapiens marked this conversation as resolved.
Show resolved Hide resolved
} catch (err) {
gasEstimate = ethers.BigNumber.from(1000000);
}

// Submit that entry
Expand Down Expand Up @@ -1001,11 +1003,13 @@ class ReputationMiner {
const [, siblings2] = await this.justificationTree.getProof(ReputationMiner.getHexString(totalnUpdates, 64));
const [round, index] = await this.getMySubmissionRoundAndIndex();

let gasEstimate = ethers.BigNumber.from(6000000);
let gasEstimate;
try {
gasEstimate = await repCycle.estimate.confirmJustificationRootHash(round, index, siblings1, siblings2);
} catch (err) { // eslint-disable-line no-empty

gasEstimate = await repCycle.estimateGas.confirmJustificationRootHash(round, index, siblings1, siblings2);
// Add some extra gas just in case the details change and a little more is needed
gasEstimate = gasEstimate.mul(11).div(10)
} catch (err) {
gasEstimate = ethers.BigNumber.from(6000000);
}

return repCycle.confirmJustificationRootHash(
Expand Down Expand Up @@ -1083,16 +1087,18 @@ class ReputationMiner {
);
}

let gasEstimate = ethers.BigNumber.from(1000000);
let gasEstimate;
try {
gasEstimate = await repCycle.estimate.respondToBinarySearchForChallenge(
gasEstimate = await repCycle.estimateGas.respondToBinarySearchForChallenge(
round,
index,
intermediateReputationHash,
siblings
);
} catch (err) { // eslint-disable-line no-empty

// Add some extra gas just in case the details change and a little more is needed
gasEstimate = gasEstimate.mul(11).div(10);
} catch (err) {
gasEstimate = ethers.BigNumber.from(1000000);
}
return repCycle.respondToBinarySearchForChallenge(
round,
Expand Down Expand Up @@ -1121,12 +1127,14 @@ class ReputationMiner {

const intermediateReputationHash = this.justificationHashes[targetLeafKeyAsHex].jhLeafValue;
const [, siblings] = await this.justificationTree.getProof(targetLeafKeyAsHex);
let gasEstimate = ethers.BigNumber.from(1000000);
let gasEstimate

try {
gasEstimate = await repCycle.estimate.confirmBinarySearchResult(round, index, intermediateReputationHash, siblings);
} catch (err){ // eslint-disable-line no-empty

gasEstimate = await repCycle.estimateGas.confirmBinarySearchResult(round, index, intermediateReputationHash, siblings);
// Add some extra gas just in case the details change and a little more is needed
gasEstimate = gasEstimate.mul(11).div(10);
} catch (err){
gasEstimate = ethers.BigNumber.from(1000000);
}

return repCycle.confirmBinarySearchResult(round, index, intermediateReputationHash, siblings, {
Expand Down Expand Up @@ -1225,11 +1233,13 @@ class ReputationMiner {
lastAgreeJustifications.childReputationProof.siblings,
lastAgreeJustifications.adjacentReputationProof.siblings]

let gasEstimate = ethers.BigNumber.from(4000000);
let gasEstimate;
try {
gasEstimate = await repCycle.estimate.respondToChallenge(...functionArgs);
} catch (err){ // eslint-disable-line no-empty

gasEstimate = await repCycle.estimateGas.respondToChallenge(...functionArgs);
// Add some extra gas just in case the details change and a little more is needed
gasEstimate = gasEstimate.mul(11).div(10);
} catch (err){
gasEstimate = ethers.BigNumber.from(4000000);
}

return repCycle.respondToChallenge(...functionArgs,
Expand All @@ -1245,11 +1255,13 @@ class ReputationMiner {
const repCycle = await this.getActiveRepCycle();
const [round] = await this.getMySubmissionRoundAndIndex();

let gasEstimate = ethers.BigNumber.from(4000000);
let gasEstimate;
try {
gasEstimate = await repCycle.estimateGas.confirmNewHash(round);
} catch (err){ // eslint-disable-line no-empty

// Add some extra gas just in case the details change and a little more is needed
gasEstimate = gasEstimate.mul(11).div(10);
} catch (err){
gasEstimate = ethers.BigNumber.from(4000000);
}
return repCycle.confirmNewHash(round, { gasLimit: gasEstimate, gasPrice: this.gasPrice });
}
Expand Down
17 changes: 17 additions & 0 deletions packages/reputation-miner/ReputationMinerClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ const CHALLENGE_RESPONSE_WINDOW_DURATION = 20 * 60;

const cache = apicache.middleware

const racingFunctionSignatures = [
"submitRootHash(bytes32,uint256,bytes32,uint256)",
"confirmNewHash(uint256)",
"invalidateHash(uint256,uint256)",
"respondToBinarySearchForChallenge(uint256,uint256,bytes,bytes32[])",
"confirmBinarySearchResult(uint256,uint256,bytes,bytes32[])",
"respondToChallenge(uint256[26],bytes32[7],bytes32[],bytes32[],bytes32[],bytes32[],bytes32[],bytes32[])",
"confirmJustificationRootHash(uint256,uint256,bytes32[],bytes32[])"
].map(x => ethers.utils.id(x).slice(0,10))

class ReputationMinerClient {
/**
* Constructor for ReputationMiner
Expand Down Expand Up @@ -607,6 +617,13 @@ class ReputationMinerClient {
return;
}
this._adapter.error(`Error during block checks: ${err}`);
if (racingFunctionSignatures.indexOf(err.transaction.data.slice(0, 10)) > -1){
// An error on a function that we were 'racing' to execute failed - most likely because someone else did it.
// So let's keep mining.
console.log('Sometimes-expected transaction failure - we lost a race to submit for a stage. Continuing mining')
this.endDoBlockChecks();
return;
}
if (this._exitOnError) {
this._adapter.error(`Automatically restarting`);
process.exit(1);
Expand Down
Loading