From 8745c9df54bb3b960e8635b0b002dfdcea1dc3f8 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Thu, 8 Sep 2022 01:50:40 +0100 Subject: [PATCH 1/2] chore: scripts to populate the subcourts --- contracts/README.md | 5 +- contracts/scripts/getCourtsV1.ts | 56 +++++++++++++++++ contracts/scripts/populateCourts.ts | 73 +++++++++++++++++++++++ contracts/src/kleros-v1/IKlerosLiquid.sol | 24 ++++++++ 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 contracts/scripts/getCourtsV1.ts create mode 100644 contracts/scripts/populateCourts.ts diff --git a/contracts/README.md b/contracts/README.md index f93e90d22..600f2512d 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -163,12 +163,13 @@ yarn hardhat --network + ({ + id: courtId, + parent: result.parent.toString(), + hiddenVotes: result.hiddenVotes, + minStake: result.minStake.toString(), + alpha: result.alpha.toString(), + feeForJuror: result.feeForJuror.toString(), + jurorsForCourtJump: result.feeForJuror.toString(), + timesPerPeriod: [], + } as unknown as Court) + ); + + court.timesPerPeriod = await courtsV1.getSubcourt(courtId).then((result) => { + return result.timesPerPeriod.map((bn) => bn.toNumber()); + }); + + court.id = courtId; + + // console.log("CourtId %d -> %O", courtId, court); + + courts.push(court); + } + console.log(JSON.stringify(courts, null, "\t")); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/contracts/scripts/populateCourts.ts b/contracts/scripts/populateCourts.ts new file mode 100644 index 000000000..3aad024e1 --- /dev/null +++ b/contracts/scripts/populateCourts.ts @@ -0,0 +1,73 @@ +import { deployments, getNamedAccounts, getChainId, ethers } from "hardhat"; +import { KlerosCore } from "../typechain-types"; +import courtsV1 from "../courts.v1.json"; +import { BigNumber } from "ethers"; + +enum HomeChains { + ARBITRUM_ONE = 42161, + ARBITRUM_RINKEBY = 421611, + ARBITRUM_GOERLI = 421613, + HARDHAT = 31337, +} + +const GENERAL_COURT = BigNumber.from(1); +const DISPUTE_KIT_CLASSIC = BigNumber.from(1); + +async function main() { + // fallback to hardhat node signers on local network + const deployer = (await getNamedAccounts()).deployer ?? (await ethers.getSigners())[0].address; + + const chainId = Number(await getChainId()); + if (!HomeChains[chainId]) { + console.error(`Aborting: script is not compatible with ${chainId}`); + return; + } else { + console.log("deploying to %s with deployer %s", HomeChains[chainId], deployer); + } + + // WARNING: skip the Forking court at id 0, so the v1 courts are shifted by 1 + const courtsV2 = courtsV1.map((court) => ({ + ...court, + id: BigNumber.from(court.id).add(1), + parent: BigNumber.from(court.parent).add(1), + })); + + console.log("courtsV2 = %O", courtsV2); + + const klerosCoreDeployment = await deployments.get("KlerosCore"); + const core = (await ethers.getContractAt("KlerosCore", klerosCoreDeployment.address)) as KlerosCore; + + for (const court of courtsV2) { + // TODO: detect if the court already exist, if so, modify instead of create. + if (court.id.eq(GENERAL_COURT)) { + // General court, it already exist. + // TODO: compare the court parameters before attempting to modify it. + console.log("Skipping subcourt %d", court.id); + } else { + // Other courts, create them. + console.log("Creating subcourt %d with %O", court.id, court); + await core.createSubcourt( + court.parent, + court.hiddenVotes, + court.minStake, + court.alpha, + court.feeForJuror, + court.jurorsForCourtJump, + [court.timesPerPeriod[0], court.timesPerPeriod[1], court.timesPerPeriod[2], court.timesPerPeriod[3]], + 5, // Not accessible on-chain, but has always been set to the same value so far. + [DISPUTE_KIT_CLASSIC] + ); + + // console.log("Created: %O", await core.courts(court.id)); + } + + await new Promise((resolve) => setTimeout(resolve, 100)); + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/contracts/src/kleros-v1/IKlerosLiquid.sol b/contracts/src/kleros-v1/IKlerosLiquid.sol index 79532045f..f18396839 100644 --- a/contracts/src/kleros-v1/IKlerosLiquid.sol +++ b/contracts/src/kleros-v1/IKlerosLiquid.sol @@ -19,6 +19,18 @@ interface IKlerosLiquid is IArbitrator { drawing // Jurors can be drawn. Pass after all disputes have jurors or `maxDrawingTime` passes. } + struct Court { + uint96 parent; // The parent court. + uint256[] children; // List of child courts. + bool hiddenVotes; // Whether to use commit and reveal or not. + uint256 minStake; // Minimum tokens needed to stake in the court. + uint256 alpha; // Basis point of tokens that are lost when incoherent. + uint256 feeForJuror; // Arbitration fee paid per juror. + // The appeal after the one that reaches this number of jurors will go to the parent court if any, otherwise, no more appeals are possible. + uint256 jurorsForCourtJump; + uint256[4] timesPerPeriod; // The time allotted to each dispute period in the form `timesPerPeriod[period]`. + } + struct Dispute { // Note that appeal `0` is equivalent to the first round of the dispute. uint96 subcourtID; // The ID of the subcourt the dispute is in. @@ -37,6 +49,18 @@ interface IKlerosLiquid is IArbitrator { uint256 lockedTokens; // The juror's total amount of tokens locked in disputes. } + function courts(uint256 _index) + external + view + returns ( + uint96 parent, + bool hiddenVotes, + uint256 minStake, + uint256 alpha, + uint256 feeForJuror, + uint256 jurorsForCourtJump + ); + function phase() external view returns (Phase); function lockInsolventTransfers() external view returns (bool); From fe1d7cca5a7d99eb2759222f874d34b56467d580 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Thu, 8 Sep 2022 21:38:33 +0100 Subject: [PATCH 2/2] feat: subcourt initialization script can update existing subcourts --- contracts/scripts/populateCourts.ts | 74 ++++++++++++++++++++---- contracts/src/arbitration/KlerosCore.sol | 2 +- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/contracts/scripts/populateCourts.ts b/contracts/scripts/populateCourts.ts index 3aad024e1..c9568939d 100644 --- a/contracts/scripts/populateCourts.ts +++ b/contracts/scripts/populateCourts.ts @@ -1,7 +1,7 @@ import { deployments, getNamedAccounts, getChainId, ethers } from "hardhat"; import { KlerosCore } from "../typechain-types"; -import courtsV1 from "../courts.v1.json"; import { BigNumber } from "ethers"; +import courtsV1 from "../courts.v1.json"; enum HomeChains { ARBITRUM_ONE = 42161, @@ -10,7 +10,6 @@ enum HomeChains { HARDHAT = 31337, } -const GENERAL_COURT = BigNumber.from(1); const DISPUTE_KIT_CLASSIC = BigNumber.from(1); async function main() { @@ -38,14 +37,69 @@ async function main() { const core = (await ethers.getContractAt("KlerosCore", klerosCoreDeployment.address)) as KlerosCore; for (const court of courtsV2) { - // TODO: detect if the court already exist, if so, modify instead of create. - if (court.id.eq(GENERAL_COURT)) { - // General court, it already exist. - // TODO: compare the court parameters before attempting to modify it. - console.log("Skipping subcourt %d", court.id); + const subcourtPresent = await core.courts(court.id).catch(() => {}); + if (subcourtPresent) { + console.log("Subcourt %d found: %O", court.id, subcourtPresent); + + // Subcourt.parent and sortitionSumTreeK cannot be changed. + + if (subcourtPresent.hiddenVotes !== court.hiddenVotes) { + console.log( + "Subcourt %d: changing hiddenVotes from %d to %d", + court.id, + subcourtPresent.hiddenVotes, + court.hiddenVotes + ); + await core.changeSubcourtHiddenVotes(court.id, court.hiddenVotes); + } + + if (!subcourtPresent.minStake.eq(court.minStake)) { + console.log("Subcourt %d: changing minStake from %d to %d", court.id, subcourtPresent.minStake, court.minStake); + await core.changeSubcourtMinStake(court.id, court.minStake); + } + + if (!subcourtPresent.alpha.eq(court.alpha)) { + console.log("Subcourt %d: changing alpha from %d to %d", court.id, subcourtPresent.alpha, court.alpha); + await core.changeSubcourtAlpha(court.id, court.alpha); + } + + if (!subcourtPresent.feeForJuror.eq(court.feeForJuror)) { + console.log( + "Subcourt %d: changing feeForJuror from %d to %d", + court.id, + subcourtPresent.feeForJuror, + court.feeForJuror + ); + await core.changeSubcourtJurorFee(court.id, court.feeForJuror); + } + + if (!subcourtPresent.jurorsForCourtJump.eq(court.jurorsForCourtJump)) { + console.log( + "Subcourt %d: changing jurorsForCourtJump from %d to %d", + court.id, + subcourtPresent.jurorsForCourtJump, + court.jurorsForCourtJump + ); + await core.changeSubcourtJurorsForJump(court.id, court.jurorsForCourtJump); + } + + const timesPerPeriodPresent = (await core.getTimesPerPeriod(court.id)).map((bn) => bn.toNumber()); + if (!timesPerPeriodPresent.every((val, index) => val === court.timesPerPeriod[index])) { + console.log( + "Subcourt %d: changing timesPerPeriod from %O to %O", + court.id, + timesPerPeriodPresent, + court.timesPerPeriod + ); + await core.changeSubcourtTimesPerPeriod(court.id, [ + court.timesPerPeriod[0], + court.timesPerPeriod[1], + court.timesPerPeriod[2], + court.timesPerPeriod[3], + ]); + } } else { - // Other courts, create them. - console.log("Creating subcourt %d with %O", court.id, court); + console.log("Subcourt %d not found, creating it with", court.id, court); await core.createSubcourt( court.parent, court.hiddenVotes, @@ -57,8 +111,6 @@ async function main() { 5, // Not accessible on-chain, but has always been set to the same value so far. [DISPUTE_KIT_CLASSIC] ); - - // console.log("Created: %O", await core.courts(court.id)); } await new Promise((resolve) => setTimeout(resolve, 100)); diff --git a/contracts/src/arbitration/KlerosCore.sol b/contracts/src/arbitration/KlerosCore.sol index b1746129e..de3e793cc 100644 --- a/contracts/src/arbitration/KlerosCore.sol +++ b/contracts/src/arbitration/KlerosCore.sol @@ -453,7 +453,7 @@ contract KlerosCore is IArbitrator { * @param _subcourtID The ID of the subcourt. * @param _hiddenVotes The new value for the `hiddenVotes` property value. */ - function changeHiddenVotes(uint96 _subcourtID, bool _hiddenVotes) external onlyByGovernor { + function changeSubcourtHiddenVotes(uint96 _subcourtID, bool _hiddenVotes) external onlyByGovernor { courts[_subcourtID].hiddenVotes = _hiddenVotes; emit SubcourtModified(_subcourtID, "hiddenVotes"); }