Skip to content

Commit

Permalink
Merge pull request #1102 from JoinColony/fix/randomise-miner
Browse files Browse the repository at this point in the history
Fix random ordering of miners in response windows
  • Loading branch information
kronosapiens authored Dec 16, 2022
2 parents bae2749 + 8bcf179 commit ce1cdb1
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 26 deletions.
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) {
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);
} 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

0 comments on commit ce1cdb1

Please sign in to comment.