mirror of
https://github.com/logos-storage/logos-storage-contracts-eth.git
synced 2026-01-02 13:23:10 +00:00
feat: move to hardhat ignition for deployments (#231)
* Move to ethers 6 and use hardhat ignition for deployments * Update prettier to the last version * Remove comment * Remove npx call in package.json * Remove useless comment * Add localhost configuration for hardhat ignition * Use contract initial balance instead of const value * Update dependencies and use extract mining to a script in order to use hardhat ignition deploy command * Fix deployment modules for local env * Remove unused function * Export contract deployment custom error assert into a function * Refactoring * Remove old deployments folder * Add process names when running concurrently * Remove conditional allowBlocksWithSameTimestamp, set true everytime * Update dependencies * Update Vault tests for ignition * Update token description * Add vault ignition module * Remove old .tool-versions * Fix formatting * Remove deployments folder and add README for previous files references * Put back the comment related to hardhat automine * Set hardhat gas limit to auto and restore manual mining for Vault tests * Apply prettier formatting and bug test with ignition syntax * Add deployments artifacts * Fix build-info ignore * Use HARDHAT_NETWORK env variable to deploy marketplace contract * Add guard to check that configs has tags * Add testnet deployment addresses * Add TOKEN_ADDRESS to reuse the token contract deployed * Fix token deployment with contractAt * Remove localhost deployment artifacts * Add section in README for deployments * Ignore localhost deployments in git * Set mine script for localhost deployment only and add deploy reset command * Remove previous deployment scripts * Fix typo in documentation * Add log when reusing token address * Update testnet artifact reference * Remove HARDHAT_NETWORK and update documentation * fix md format * Npm audit fix * Update dependencies * Remove default deployer * Update commit for last testnet artifacts * Remove deployments files from linea and testnet and update the last commit hashes to those artifacts
This commit is contained in:
parent
2dddc26015
commit
dee3d7b654
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@ -10,7 +10,6 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
formatting:
|
formatting:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -26,10 +25,9 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
# workaround for https://github.com/NomicFoundation/hardhat/issues/3877
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 18.15
|
node-version: 22
|
||||||
- run: npm install
|
- run: npm install
|
||||||
- run: npm test
|
- run: npm test
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v4
|
||||||
@ -53,9 +51,9 @@ jobs:
|
|||||||
- name: Install Java
|
- name: Install Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: "zulu"
|
||||||
java-version: '11'
|
java-version: "11"
|
||||||
java-package: 'jre'
|
java-package: "jre"
|
||||||
|
|
||||||
- name: Install Certora CLI
|
- name: Install Certora CLI
|
||||||
run: pip3 install certora-cli==7.10.2
|
run: pip3 install certora-cli==7.10.2
|
||||||
@ -88,4 +86,3 @@ jobs:
|
|||||||
rule:
|
rule:
|
||||||
- verify:marketplace
|
- verify:marketplace
|
||||||
- verify:state_changes
|
- verify:state_changes
|
||||||
|
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@ -4,3 +4,10 @@ artifacts
|
|||||||
deployment-localhost.json
|
deployment-localhost.json
|
||||||
crytic-export
|
crytic-export
|
||||||
.certora_internal
|
.certora_internal
|
||||||
|
coverage
|
||||||
|
coverage.json
|
||||||
|
|
||||||
|
# Ignore localhost deployments files
|
||||||
|
ignition/deployments/chain-31337
|
||||||
|
|
||||||
|
ignition/deployments/**/build-info
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
nodejs 18.15.0
|
|
||||||
23
Readme.md
23
Readme.md
@ -22,9 +22,28 @@ To start a local Ethereum node with the contracts deployed, execute:
|
|||||||
|
|
||||||
npm start
|
npm start
|
||||||
|
|
||||||
This will create a `deployment-localhost.json` file containing the addresses of
|
Deployment
|
||||||
the deployed contracts.
|
----------
|
||||||
|
|
||||||
|
To deploy the marketplace, you need to specify the network using `--network MY_NETWORK`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run deploy -- --network localhost
|
||||||
|
```
|
||||||
|
|
||||||
|
Hardhat uses [reconciliation](https://hardhat.org/ignition/docs/advanced/reconciliation) to recover from
|
||||||
|
errors or resume a previous deployment. In our case, we will likely redeploy a new contract every time,
|
||||||
|
so we will need to [clear the previous deployment](https://hardhat.org/ignition/docs/guides/modifications#clearing-an-existing-deployment-with-reset):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run deploy -- --network testnet --reset
|
||||||
|
```
|
||||||
|
|
||||||
|
To reuse a previously deployed `Token` contract, define the environment variable `TOKEN_ADDRESS`.
|
||||||
|
The deployment script will use `contractAt` from Hardhat Ignition to retrieve the existing contract
|
||||||
|
instead of deploying a new one.
|
||||||
|
|
||||||
|
The deployment files are kept under version control [as recommended by Hardhat](https://hardhat.org/ignition/docs/advanced/versioning), except the build files, which are 18 MB.
|
||||||
|
|
||||||
Running the prover
|
Running the prover
|
||||||
------------------
|
------------------
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
const { loadZkeyHash } = require("../verifier/verifier.js")
|
|
||||||
const { loadConfiguration } = require("../configuration/configuration.js")
|
|
||||||
|
|
||||||
async function mine256blocks({ network, ethers }) {
|
|
||||||
if (network.tags.local) {
|
|
||||||
await ethers.provider.send("hardhat_mine", ["0x100"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// deploys a marketplace with a real Groth16 verifier
|
|
||||||
async function deployMarketplace({ deployments, getNamedAccounts }) {
|
|
||||||
const token = await deployments.get("TestToken")
|
|
||||||
const verifier = await deployments.get("Groth16Verifier")
|
|
||||||
const zkeyHash = loadZkeyHash(network.name)
|
|
||||||
let configuration = loadConfiguration(network.name)
|
|
||||||
configuration.proofs.zkeyHash = zkeyHash
|
|
||||||
const args = [configuration, token.address, verifier.address]
|
|
||||||
const { deployer: from } = await getNamedAccounts()
|
|
||||||
const marketplace = await deployments.deploy("Marketplace", { args, from })
|
|
||||||
console.log("Deployed Marketplace with Groth16 Verifier at:")
|
|
||||||
console.log(marketplace.address)
|
|
||||||
console.log()
|
|
||||||
}
|
|
||||||
|
|
||||||
// deploys a marketplace with a testing verifier
|
|
||||||
async function deployTestMarketplace({
|
|
||||||
network,
|
|
||||||
deployments,
|
|
||||||
getNamedAccounts,
|
|
||||||
}) {
|
|
||||||
if (network.tags.local) {
|
|
||||||
const token = await deployments.get("TestToken")
|
|
||||||
const verifier = await deployments.get("TestVerifier")
|
|
||||||
const zkeyHash = loadZkeyHash(network.name)
|
|
||||||
let configuration = loadConfiguration(network.name)
|
|
||||||
configuration.proofs.zkeyHash = zkeyHash
|
|
||||||
const args = [configuration, token.address, verifier.address]
|
|
||||||
const { deployer: from } = await getNamedAccounts()
|
|
||||||
const marketplace = await deployments.deploy("Marketplace", { args, from })
|
|
||||||
console.log("Deployed Marketplace with Test Verifier at:")
|
|
||||||
console.log(marketplace.address)
|
|
||||||
console.log()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = async (environment) => {
|
|
||||||
await mine256blocks(environment)
|
|
||||||
await deployMarketplace(environment)
|
|
||||||
await deployTestMarketplace(environment)
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.tags = ["Marketplace"]
|
|
||||||
module.exports.dependencies = ["TestToken", "Verifier"]
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
const MINTED_TOKENS = 1_000_000_000_000_000
|
|
||||||
|
|
||||||
module.exports = async ({
|
|
||||||
deployments,
|
|
||||||
getNamedAccounts,
|
|
||||||
getUnnamedAccounts,
|
|
||||||
network,
|
|
||||||
}) => {
|
|
||||||
const { deployer } = await getNamedAccounts()
|
|
||||||
const tokenDeployment = await deployments.deploy("TestToken", {
|
|
||||||
from: deployer,
|
|
||||||
skipIfAlreadyDeployed: true,
|
|
||||||
})
|
|
||||||
const token = await hre.ethers.getContractAt(
|
|
||||||
"TestToken",
|
|
||||||
tokenDeployment.address
|
|
||||||
)
|
|
||||||
|
|
||||||
const accounts = [
|
|
||||||
...Object.values(await getNamedAccounts()),
|
|
||||||
...(await getUnnamedAccounts()),
|
|
||||||
]
|
|
||||||
if (network.tags.local) {
|
|
||||||
for (const account of accounts) {
|
|
||||||
console.log(`Minting ${MINTED_TOKENS} tokens to address ${account}`)
|
|
||||||
|
|
||||||
const transaction = await token.mint(account, MINTED_TOKENS, {
|
|
||||||
from: deployer,
|
|
||||||
})
|
|
||||||
await transaction.wait()
|
|
||||||
}
|
|
||||||
console.log()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.tags = ["TestToken"]
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
const { loadVerificationKey } = require("../verifier/verifier.js")
|
|
||||||
|
|
||||||
async function deployVerifier({ deployments, getNamedAccounts }) {
|
|
||||||
const { deployer } = await getNamedAccounts()
|
|
||||||
const verificationKey = loadVerificationKey(network.name)
|
|
||||||
await deployments.deploy("Groth16Verifier", {
|
|
||||||
args: [verificationKey],
|
|
||||||
from: deployer,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deployTestVerifier({ network, deployments, getNamedAccounts }) {
|
|
||||||
if (network.tags.local) {
|
|
||||||
const { deployer } = await getNamedAccounts()
|
|
||||||
await deployments.deploy("TestVerifier", { from: deployer })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = async (environment) => {
|
|
||||||
await deployVerifier(environment)
|
|
||||||
await deployTestVerifier(environment)
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.tags = ["Verifier"]
|
|
||||||
2
deployments/.gitignore
vendored
2
deployments/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
localhost
|
|
||||||
codexdisttestnetwork
|
|
||||||
@ -1 +0,0 @@
|
|||||||
789987
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
1660990954
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
167005
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,6 +1,5 @@
|
|||||||
require("@nomiclabs/hardhat-waffle")
|
require("@nomicfoundation/hardhat-toolbox")
|
||||||
require("hardhat-deploy")
|
require("@nomicfoundation/hardhat-ignition-ethers")
|
||||||
require("hardhat-deploy-ethers")
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
solidity: {
|
solidity: {
|
||||||
@ -25,6 +24,10 @@ module.exports = {
|
|||||||
hardhat: {
|
hardhat: {
|
||||||
tags: ["local"],
|
tags: ["local"],
|
||||||
allowBlocksWithSameTimestamp: true,
|
allowBlocksWithSameTimestamp: true,
|
||||||
|
gas: "auto",
|
||||||
|
},
|
||||||
|
localhost: {
|
||||||
|
tags: ["local"],
|
||||||
},
|
},
|
||||||
codexdisttestnetwork: {
|
codexdisttestnetwork: {
|
||||||
url: `${process.env.DISTTEST_NETWORK_URL}`,
|
url: `${process.env.DISTTEST_NETWORK_URL}`,
|
||||||
|
|||||||
33
ignition/README.md
Normal file
33
ignition/README.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Deployment
|
||||||
|
|
||||||
|
Hardhat Ignition is now used to deploy the contracts, so the old
|
||||||
|
deployment files are no longer relevant.
|
||||||
|
|
||||||
|
However, the ABI of the contracts has changed due to an OpenZeppelin update.
|
||||||
|
If we ever need to recreate the artifacts from the previous ABI contracts (for any reason),
|
||||||
|
we can do so using a small script that imports the previously generated files.
|
||||||
|
Here is an example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
module.exports = buildModule("Token", (m) => {
|
||||||
|
const previousJsonFile = path.join(__dirname, "./TestToken.json");
|
||||||
|
const artifact = JSON.parse(fs.readFileSync(previousJsonFile, "utf8"));
|
||||||
|
const address = artifact.address;
|
||||||
|
const token = m.contractAt("TestToken", address, {});
|
||||||
|
return { token };
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we can run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx hardhat ignition deploy ignition/modules/migration/token.js --network taiko_test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** Check [this comment](https://github.com/codex-storage/codex-contracts-eth/pull/231#issuecomment-2808996517) for more context.
|
||||||
|
|
||||||
|
Here is the list of previous commits containing the ABI contracts that were deployed:
|
||||||
|
|
||||||
|
- [Taiko](https://github.com/codex-storage/codex-contracts-eth/commit/1854dfba9991a25532de5f6a53cf50e66afb3c8b)
|
||||||
|
- [Testnet](https://github.com/codex-storage/codex-contracts-eth/commit/449d64ffc0dc1478d0690d36f037358084a17b09)
|
||||||
|
- [Linea](https://github.com/codex-storage/codex-contracts-eth/pull/226/commits/2dddc260152b6e9c24ae372397f9b9b2d27ce8e4)
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"Token#TestToken": "0x34a22f3911De437307c6f4485931779670f78764",
|
||||||
|
"Verifier#Groth16Verifier": "0x1f60B2329775545AaeF743dbC3571e699405263e",
|
||||||
|
"Marketplace#Marketplace": "0xDB2908d724a15d05c0B6B8e8441a8b36E67476d3"
|
||||||
|
}
|
||||||
9
ignition/modules/endian.js
Normal file
9
ignition/modules/endian.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
|
||||||
|
module.exports = buildModule("Endian", (m) => {
|
||||||
|
const endian = m.contract("Endian", [], {})
|
||||||
|
|
||||||
|
const testEndian = m.contract("TestEndian", [], {})
|
||||||
|
|
||||||
|
return { endian, testEndian }
|
||||||
|
})
|
||||||
43
ignition/modules/marketplace.js
Normal file
43
ignition/modules/marketplace.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
const { loadZkeyHash } = require("../../verifier/verifier.js")
|
||||||
|
const { loadConfiguration } = require("../../configuration/configuration.js")
|
||||||
|
const TokenModule = require("./token.js")
|
||||||
|
const VerifierModule = require("./verifier.js")
|
||||||
|
|
||||||
|
function getDefaultConfig() {
|
||||||
|
const zkeyHash = loadZkeyHash(hre.network.name)
|
||||||
|
const config = loadConfiguration(hre.network.name)
|
||||||
|
config.proofs.zkeyHash = zkeyHash
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = buildModule("Marketplace", (m) => {
|
||||||
|
const { token } = m.useModule(TokenModule)
|
||||||
|
const { verifier } = m.useModule(VerifierModule)
|
||||||
|
const configuration = m.getParameter("configuration", getDefaultConfig())
|
||||||
|
|
||||||
|
const marketplace = m.contract(
|
||||||
|
"Marketplace",
|
||||||
|
[configuration, token, verifier],
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
|
let testMarketplace
|
||||||
|
const config = hre.network.config
|
||||||
|
|
||||||
|
if (config && config.tags && config.tags.includes("local")) {
|
||||||
|
const { testVerifier } = m.useModule(VerifierModule)
|
||||||
|
|
||||||
|
testMarketplace = m.contract(
|
||||||
|
"TestMarketplace",
|
||||||
|
[configuration, token, testVerifier],
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
marketplace,
|
||||||
|
testMarketplace,
|
||||||
|
token,
|
||||||
|
}
|
||||||
|
})
|
||||||
9
ignition/modules/periods.js
Normal file
9
ignition/modules/periods.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
|
||||||
|
module.exports = buildModule("Periods", (m) => {
|
||||||
|
const secondsPerPeriod = m.getParameter("secondsPerPeriod", 0)
|
||||||
|
|
||||||
|
const periods = m.contract("Periods", [secondsPerPeriod], {})
|
||||||
|
|
||||||
|
return { periods }
|
||||||
|
})
|
||||||
11
ignition/modules/proofs.js
Normal file
11
ignition/modules/proofs.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
const VerifierModule = require("./verifier.js")
|
||||||
|
|
||||||
|
module.exports = buildModule("Proofs", (m) => {
|
||||||
|
const { verifier } = m.useModule(VerifierModule)
|
||||||
|
const configuration = m.getParameter("configuration", null)
|
||||||
|
|
||||||
|
const testProofs = m.contract("TestProofs", [configuration, verifier], {})
|
||||||
|
|
||||||
|
return { testProofs }
|
||||||
|
})
|
||||||
13
ignition/modules/slot-reservations.js
Normal file
13
ignition/modules/slot-reservations.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
|
||||||
|
module.exports = buildModule("SlotReservations", (m) => {
|
||||||
|
const configuration = m.getParameter("configuration", null)
|
||||||
|
|
||||||
|
const testSlotReservations = m.contract(
|
||||||
|
"TestSlotReservations",
|
||||||
|
[configuration],
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
|
||||||
|
return { testSlotReservations }
|
||||||
|
})
|
||||||
31
ignition/modules/token.js
Normal file
31
ignition/modules/token.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
|
||||||
|
const MAX_ACCOUNTS = 20
|
||||||
|
const MINTED_TOKENS = 1_000_000_000_000_000n
|
||||||
|
|
||||||
|
module.exports = buildModule("Token", (m) => {
|
||||||
|
let token
|
||||||
|
|
||||||
|
if (process.env.TOKEN_ADDRESS) {
|
||||||
|
console.log(
|
||||||
|
"Using existing TestToken on address: ",
|
||||||
|
process.env.TOKEN_ADDRESS,
|
||||||
|
)
|
||||||
|
token = m.contractAt("TestToken", process.env.TOKEN_ADDRESS, {})
|
||||||
|
} else {
|
||||||
|
token = m.contract("TestToken", [], {})
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = hre.network.config
|
||||||
|
|
||||||
|
if (config && config.tags && config.tags.includes("local")) {
|
||||||
|
for (let i = 0; i < MAX_ACCOUNTS; i++) {
|
||||||
|
const account = m.getAccount(i)
|
||||||
|
m.call(token, "mint", [account, MINTED_TOKENS], {
|
||||||
|
id: `SendingTestTokens_${i}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { token }
|
||||||
|
})
|
||||||
10
ignition/modules/vault.js
Normal file
10
ignition/modules/vault.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
const TokenModule = require("./token.js")
|
||||||
|
|
||||||
|
module.exports = buildModule("Vault", (m) => {
|
||||||
|
const { token } = m.useModule(TokenModule)
|
||||||
|
|
||||||
|
const vault = m.contract("Vault", [token], {})
|
||||||
|
|
||||||
|
return { vault, token }
|
||||||
|
})
|
||||||
11
ignition/modules/verifier.js
Normal file
11
ignition/modules/verifier.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules")
|
||||||
|
const { loadVerificationKey } = require("../../verifier/verifier.js")
|
||||||
|
|
||||||
|
module.exports = buildModule("Verifier", (m) => {
|
||||||
|
const verificationKey = loadVerificationKey(hre.network.name)
|
||||||
|
const verifier = m.contract("Groth16Verifier", [verificationKey], {})
|
||||||
|
|
||||||
|
const testVerifier = m.contract("TestVerifier", [], {})
|
||||||
|
|
||||||
|
return { verifier, testVerifier }
|
||||||
|
})
|
||||||
36014
package-lock.json
generated
36014
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
36
package.json
36
package.json
@ -4,29 +4,31 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run lint && hardhat test",
|
"test": "npm run lint && hardhat test",
|
||||||
"fuzz": "hardhat compile && fuzzing/fuzz.sh",
|
"fuzz": "hardhat compile && fuzzing/fuzz.sh",
|
||||||
"start": "hardhat node --export deployment-localhost.json",
|
"start": "concurrently --names \"hardhat,deployment\" --prefix \"[{time} {name}]\" \"hardhat node\" \"sleep 2 && npm run mine && npm run deploy -- --network localhost\"",
|
||||||
"compile": "hardhat compile",
|
"compile": "hardhat compile",
|
||||||
"format": "prettier --write contracts/*.sol contracts/**/*.sol test/**/*.js",
|
"format": "prettier --write test/**/*.js --plugin=prettier-plugin-solidity contracts/**/*.sol ",
|
||||||
"format:check": "prettier --check contracts/*.sol contracts/**/*.sol test/**/*.js",
|
"format:check": "prettier --check test/**/*.js --plugin=prettier-plugin-solidity contracts/**/*.sol",
|
||||||
"lint": "solhint contracts/**.sol",
|
"lint": "solhint contracts/**.sol",
|
||||||
"deploy": "hardhat deploy",
|
"deploy": "hardhat ignition deploy ignition/modules/marketplace.js",
|
||||||
|
"mine": "hardhat run scripts/mine.js --network localhost",
|
||||||
"verify": "npm run verify:marketplace && npm run verify:state_changes",
|
"verify": "npm run verify:marketplace && npm run verify:state_changes",
|
||||||
"verify:marketplace": "certoraRun certora/confs/Marketplace.conf",
|
"verify:marketplace": "certoraRun certora/confs/Marketplace.conf",
|
||||||
"verify:state_changes": "certoraRun certora/confs/StateChanges.conf"
|
"verify:state_changes": "certoraRun certora/confs/StateChanges.conf",
|
||||||
|
"coverage": "hardhat coverage",
|
||||||
|
"gas:report": "REPORT_GAS=true hardhat test"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nomiclabs/hardhat-ethers": "^2.2.1",
|
|
||||||
"@nomiclabs/hardhat-waffle": "^2.0.3",
|
|
||||||
"@openzeppelin/contracts": "^5.3.0",
|
"@openzeppelin/contracts": "^5.3.0",
|
||||||
"@stdlib/stats-binomial-test": "^0.0.7",
|
"@nomicfoundation/hardhat-chai-matchers": "^2.0.8",
|
||||||
"chai": "^4.3.7",
|
"@nomicfoundation/hardhat-ignition-ethers": "^0.15.11",
|
||||||
"ethereum-waffle": "^3.4.4",
|
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
|
||||||
"ethers": "^5.7.2",
|
"@stdlib/stats-binomial-test": "^0.2.2",
|
||||||
"hardhat": "^2.24.2",
|
"chai": "^4.5.0",
|
||||||
"hardhat-deploy": "^0.11.34",
|
"ethers": "6.14.4",
|
||||||
"hardhat-deploy-ethers": "^0.3.0-beta.13",
|
"hardhat": "^2.24.3",
|
||||||
"prettier": "^2.8.2",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-solidity": "^1.4.2",
|
"prettier-plugin-solidity": "^1.4.3",
|
||||||
"solhint": "^5.0.5"
|
"solhint": "^5.1.0",
|
||||||
|
"concurrently": "^9.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
scripts/mine.js
Normal file
7
scripts/mine.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const { mine } = require("@nomicfoundation/hardhat-network-helpers")
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
await mine(256)
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
const { expect } = require("chai")
|
const { expect } = require("chai")
|
||||||
const { ethers } = require("hardhat")
|
const EndianModule = require("../ignition/modules/endian")
|
||||||
|
|
||||||
describe("Endian", function () {
|
describe("Endian", function () {
|
||||||
const big =
|
const big =
|
||||||
@ -10,8 +10,8 @@ describe("Endian", function () {
|
|||||||
let endian
|
let endian
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
let Endian = await ethers.getContractFactory("TestEndian")
|
const { testEndian } = await ignition.deploy(EndianModule)
|
||||||
endian = await Endian.deploy()
|
endian = testEndian
|
||||||
})
|
})
|
||||||
|
|
||||||
it("converts from little endian to big endian", async function () {
|
it("converts from little endian to big endian", async function () {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,32 @@
|
|||||||
const { expect } = require("chai")
|
const { expect } = require("chai")
|
||||||
const { ethers } = require("hardhat")
|
const PeriodsModule = require("../ignition/modules/periods")
|
||||||
|
const { assertDeploymentRejectedWithCustomError } = require("./helpers")
|
||||||
|
|
||||||
describe("Periods", function () {
|
describe("Periods", function () {
|
||||||
it("should revert when secondsPerPeriod is 0", async function () {
|
it("should revert when secondsPerPeriod is 0", async function () {
|
||||||
const PeriodsContract = await ethers.getContractFactory("Periods")
|
const promise = ignition.deploy(PeriodsModule, {
|
||||||
await expect(PeriodsContract.deploy(0)).to.be.revertedWith(
|
parameters: {
|
||||||
"Periods_InvalidSecondsPerPeriod"
|
Periods: {
|
||||||
|
secondsPerPeriod: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
assertDeploymentRejectedWithCustomError(
|
||||||
|
"Periods_InvalidSecondsPerPeriod",
|
||||||
|
promise,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not revert when secondsPerPeriod more than 0", async function () {
|
it("should not revert when secondsPerPeriod more than 0", async function () {
|
||||||
const PeriodsContract = await ethers.getContractFactory("Periods")
|
const promise = ignition.deploy(PeriodsModule, {
|
||||||
await expect(PeriodsContract.deploy(10)).not.to.be.reverted
|
parameters: {
|
||||||
|
Periods: {
|
||||||
|
secondsPerPeriod: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(promise).not.to.be.rejected
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,20 +1,21 @@
|
|||||||
const { expect } = require("chai")
|
const { expect } = require("chai")
|
||||||
const { ethers, deployments } = require("hardhat")
|
const { ethers } = require("hardhat")
|
||||||
const { hexlify, randomBytes } = ethers.utils
|
const { hexlify, randomBytes } = ethers
|
||||||
const {
|
const {
|
||||||
snapshot,
|
snapshot,
|
||||||
revert,
|
revert,
|
||||||
mine,
|
|
||||||
ensureMinimumBlockHeight,
|
ensureMinimumBlockHeight,
|
||||||
currentTime,
|
currentTime,
|
||||||
advanceTime,
|
advanceTime,
|
||||||
advanceTimeTo,
|
advanceTimeTo,
|
||||||
|
mine,
|
||||||
} = require("./evm")
|
} = require("./evm")
|
||||||
const { periodic } = require("./time")
|
const { periodic } = require("./time")
|
||||||
const { loadProof, loadPublicInput } = require("../verifier/verifier")
|
const { loadProof, loadPublicInput } = require("../verifier/verifier")
|
||||||
const { SlotState } = require("./requests")
|
const { SlotState } = require("./requests")
|
||||||
const binomialTest = require("@stdlib/stats-binomial-test")
|
const binomialTest = require("@stdlib/stats-binomial-test")
|
||||||
const { exampleProof } = require("./examples")
|
const { exampleProof } = require("./examples")
|
||||||
|
const ProofsModule = require("../ignition/modules/proofs")
|
||||||
|
|
||||||
describe("Proofs", function () {
|
describe("Proofs", function () {
|
||||||
const slotId = hexlify(randomBytes(32))
|
const slotId = hexlify(randomBytes(32))
|
||||||
@ -30,13 +31,22 @@ describe("Proofs", function () {
|
|||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
await snapshot()
|
await snapshot()
|
||||||
await ensureMinimumBlockHeight(256)
|
await ensureMinimumBlockHeight(256)
|
||||||
const Proofs = await ethers.getContractFactory("TestProofs")
|
|
||||||
await deployments.fixture(["Verifier"])
|
const { testProofs } = await ignition.deploy(ProofsModule, {
|
||||||
const verifier = await deployments.get("Groth16Verifier")
|
parameters: {
|
||||||
proofs = await Proofs.deploy(
|
Proofs: {
|
||||||
{ period, timeout, downtime, zkeyHash: "", downtimeProduct },
|
configuration: {
|
||||||
verifier.address
|
period,
|
||||||
)
|
timeout,
|
||||||
|
downtime,
|
||||||
|
zkeyHash: "",
|
||||||
|
downtimeProduct,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
proofs = testProofs
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async function () {
|
afterEach(async function () {
|
||||||
@ -113,7 +123,7 @@ describe("Proofs", function () {
|
|||||||
let previous = await proofs.getPointer(slotId)
|
let previous = await proofs.getPointer(slotId)
|
||||||
await mine()
|
await mine()
|
||||||
let current = await proofs.getPointer(slotId)
|
let current = await proofs.getPointer(slotId)
|
||||||
expect(current).to.equal((previous + 1) % 256)
|
expect(current).to.equal((previous + 1n) % 256n)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -202,15 +212,15 @@ describe("Proofs", function () {
|
|||||||
it("fails proof submission when proof is incorrect", async function () {
|
it("fails proof submission when proof is incorrect", async function () {
|
||||||
let invalid = exampleProof()
|
let invalid = exampleProof()
|
||||||
await expect(
|
await expect(
|
||||||
proofs.proofReceived(slotId, invalid, pubSignals)
|
proofs.proofReceived(slotId, invalid, pubSignals),
|
||||||
).to.be.revertedWith("Proofs_InvalidProof")
|
).to.be.revertedWithCustomError(proofs, "Proofs_InvalidProof")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("fails proof submission when public input is incorrect", async function () {
|
it("fails proof submission when public input is incorrect", async function () {
|
||||||
let invalid = [1, 2, 3]
|
let invalid = [1, 2, 3]
|
||||||
await expect(
|
await expect(
|
||||||
proofs.proofReceived(slotId, proof, invalid)
|
proofs.proofReceived(slotId, proof, invalid),
|
||||||
).to.be.revertedWith("Proofs_InvalidProof")
|
).to.be.revertedWithCustomError(proofs, "Proofs_InvalidProof")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("emits an event when proof was submitted", async function () {
|
it("emits an event when proof was submitted", async function () {
|
||||||
@ -222,8 +232,8 @@ describe("Proofs", function () {
|
|||||||
it("fails proof submission when already submitted", async function () {
|
it("fails proof submission when already submitted", async function () {
|
||||||
await proofs.proofReceived(slotId, proof, pubSignals)
|
await proofs.proofReceived(slotId, proof, pubSignals)
|
||||||
await expect(
|
await expect(
|
||||||
proofs.proofReceived(slotId, proof, pubSignals)
|
proofs.proofReceived(slotId, proof, pubSignals),
|
||||||
).to.be.revertedWith("Proofs_ProofAlreadySubmitted")
|
).to.be.revertedWithCustomError(proofs, "Proofs_ProofAlreadySubmitted")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("marks a proof as missing", async function () {
|
it("marks a proof as missing", async function () {
|
||||||
@ -239,8 +249,8 @@ describe("Proofs", function () {
|
|||||||
await waitUntilProofIsRequired(slotId)
|
await waitUntilProofIsRequired(slotId)
|
||||||
let currentPeriod = periodOf(await currentTime())
|
let currentPeriod = periodOf(await currentTime())
|
||||||
await expect(
|
await expect(
|
||||||
proofs.markProofAsMissing(slotId, currentPeriod)
|
proofs.markProofAsMissing(slotId, currentPeriod),
|
||||||
).to.be.revertedWith("Proofs_PeriodNotEnded")
|
).to.be.revertedWithCustomError(proofs, "Proofs_PeriodNotEnded")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not mark a proof as missing after timeout", async function () {
|
it("does not mark a proof as missing after timeout", async function () {
|
||||||
@ -248,8 +258,8 @@ describe("Proofs", function () {
|
|||||||
let currentPeriod = periodOf(await currentTime())
|
let currentPeriod = periodOf(await currentTime())
|
||||||
await advanceTimeTo(periodEnd(currentPeriod) + timeout + 1)
|
await advanceTimeTo(periodEnd(currentPeriod) + timeout + 1)
|
||||||
await expect(
|
await expect(
|
||||||
proofs.markProofAsMissing(slotId, currentPeriod)
|
proofs.markProofAsMissing(slotId, currentPeriod),
|
||||||
).to.be.revertedWith("Proofs_ValidationTimedOut")
|
).to.be.revertedWithCustomError(proofs, "Proofs_ValidationTimedOut")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not mark a received proof as missing", async function () {
|
it("does not mark a received proof as missing", async function () {
|
||||||
@ -258,8 +268,8 @@ describe("Proofs", function () {
|
|||||||
await proofs.proofReceived(slotId, proof, pubSignals)
|
await proofs.proofReceived(slotId, proof, pubSignals)
|
||||||
await advanceTimeTo(periodEnd(receivedPeriod) + 1)
|
await advanceTimeTo(periodEnd(receivedPeriod) + 1)
|
||||||
await expect(
|
await expect(
|
||||||
proofs.markProofAsMissing(slotId, receivedPeriod)
|
proofs.markProofAsMissing(slotId, receivedPeriod),
|
||||||
).to.be.revertedWith("Proofs_ProofNotMissing")
|
).to.be.revertedWithCustomError(proofs, "Proofs_ProofNotMissing")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not mark proof as missing when not required", async function () {
|
it("does not mark proof as missing when not required", async function () {
|
||||||
@ -269,8 +279,8 @@ describe("Proofs", function () {
|
|||||||
let currentPeriod = periodOf(await currentTime())
|
let currentPeriod = periodOf(await currentTime())
|
||||||
await advanceTimeTo(periodEnd(currentPeriod) + 1)
|
await advanceTimeTo(periodEnd(currentPeriod) + 1)
|
||||||
await expect(
|
await expect(
|
||||||
proofs.markProofAsMissing(slotId, currentPeriod)
|
proofs.markProofAsMissing(slotId, currentPeriod),
|
||||||
).to.be.revertedWith("Proofs_ProofNotRequired")
|
).to.be.revertedWithCustomError(proofs, "Proofs_ProofNotRequired")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not mark proof as missing twice", async function () {
|
it("does not mark proof as missing twice", async function () {
|
||||||
@ -279,8 +289,11 @@ describe("Proofs", function () {
|
|||||||
await advanceTimeTo(periodEnd(missedPeriod) + 1)
|
await advanceTimeTo(periodEnd(missedPeriod) + 1)
|
||||||
await proofs.markProofAsMissing(slotId, missedPeriod)
|
await proofs.markProofAsMissing(slotId, missedPeriod)
|
||||||
await expect(
|
await expect(
|
||||||
proofs.markProofAsMissing(slotId, missedPeriod)
|
proofs.markProofAsMissing(slotId, missedPeriod),
|
||||||
).to.be.revertedWith("Proofs_ProofAlreadyMarkedMissing")
|
).to.be.revertedWithCustomError(
|
||||||
|
proofs,
|
||||||
|
"Proofs_ProofAlreadyMarkedMissing",
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("requires no proofs when slot is finished", async function () {
|
it("requires no proofs when slot is finished", async function () {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ const { ethers } = require("hardhat")
|
|||||||
const { exampleRequest, exampleConfiguration } = require("./examples")
|
const { exampleRequest, exampleConfiguration } = require("./examples")
|
||||||
const { requestId, slotId } = require("./ids")
|
const { requestId, slotId } = require("./ids")
|
||||||
const { SlotState } = require("./requests")
|
const { SlotState } = require("./requests")
|
||||||
|
const SlotReservationsModule = require("../ignition/modules/slot-reservations")
|
||||||
|
|
||||||
describe("SlotReservations", function () {
|
describe("SlotReservations", function () {
|
||||||
let reservations
|
let reservations
|
||||||
@ -15,10 +16,18 @@ describe("SlotReservations", function () {
|
|||||||
const config = exampleConfiguration()
|
const config = exampleConfiguration()
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
let SlotReservations = await ethers.getContractFactory(
|
const { testSlotReservations } = await ignition.deploy(
|
||||||
"TestSlotReservations"
|
SlotReservationsModule,
|
||||||
|
{
|
||||||
|
parameters: {
|
||||||
|
SlotReservations: {
|
||||||
|
configuration: config.reservations,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
reservations = await SlotReservations.deploy(config.reservations)
|
|
||||||
|
reservations = testSlotReservations
|
||||||
;[provider, address1, address2, address3] = await ethers.getSigners()
|
;[provider, address1, address2, address3] = await ethers.getSigners()
|
||||||
|
|
||||||
request = await exampleRequest()
|
request = await exampleRequest()
|
||||||
@ -76,8 +85,11 @@ describe("SlotReservations", function () {
|
|||||||
|
|
||||||
it("cannot reserve a slot more than once", async function () {
|
it("cannot reserve a slot more than once", async function () {
|
||||||
await reservations.reserveSlot(reqId, slotIndex)
|
await reservations.reserveSlot(reqId, slotIndex)
|
||||||
await expect(reservations.reserveSlot(reqId, slotIndex)).to.be.revertedWith(
|
await expect(
|
||||||
"SlotReservations_ReservationNotAllowed"
|
reservations.reserveSlot(reqId, slotIndex),
|
||||||
|
).to.be.revertedWithCustomError(
|
||||||
|
reservations,
|
||||||
|
"SlotReservations_ReservationNotAllowed",
|
||||||
)
|
)
|
||||||
expect(await reservations.length(id)).to.equal(1)
|
expect(await reservations.length(id)).to.equal(1)
|
||||||
})
|
})
|
||||||
@ -95,8 +107,11 @@ describe("SlotReservations", function () {
|
|||||||
switchAccount(address3)
|
switchAccount(address3)
|
||||||
await reservations.reserveSlot(reqId, slotIndex)
|
await reservations.reserveSlot(reqId, slotIndex)
|
||||||
switchAccount(provider)
|
switchAccount(provider)
|
||||||
await expect(reservations.reserveSlot(reqId, slotIndex)).to.be.revertedWith(
|
await expect(
|
||||||
"SlotReservations_ReservationNotAllowed"
|
reservations.reserveSlot(reqId, slotIndex),
|
||||||
|
).to.be.revertedWithCustomError(
|
||||||
|
reservations,
|
||||||
|
"SlotReservations_ReservationNotAllowed",
|
||||||
)
|
)
|
||||||
expect(await reservations.length(id)).to.equal(3)
|
expect(await reservations.length(id)).to.equal(3)
|
||||||
expect(await reservations.contains(id, provider.address)).to.be.false
|
expect(await reservations.contains(id, provider.address)).to.be.false
|
||||||
@ -115,8 +130,11 @@ describe("SlotReservations", function () {
|
|||||||
|
|
||||||
it("cannot reserve a slot if not free or not in repair", async function () {
|
it("cannot reserve a slot if not free or not in repair", async function () {
|
||||||
await reservations.setSlotState(id, SlotState.Filled)
|
await reservations.setSlotState(id, SlotState.Filled)
|
||||||
await expect(reservations.reserveSlot(reqId, slotIndex)).to.be.revertedWith(
|
await expect(
|
||||||
"SlotReservations_ReservationNotAllowed"
|
reservations.reserveSlot(reqId, slotIndex),
|
||||||
|
).to.be.revertedWithCustomError(
|
||||||
|
reservations,
|
||||||
|
"SlotReservations_ReservationNotAllowed",
|
||||||
)
|
)
|
||||||
expect(await reservations.length(id)).to.equal(0)
|
expect(await reservations.length(id)).to.equal(0)
|
||||||
})
|
})
|
||||||
@ -139,7 +157,7 @@ describe("SlotReservations", function () {
|
|||||||
it("should not emit an event when reservations are not full", async function () {
|
it("should not emit an event when reservations are not full", async function () {
|
||||||
await expect(reservations.reserveSlot(reqId, slotIndex)).to.not.emit(
|
await expect(reservations.reserveSlot(reqId, slotIndex)).to.not.emit(
|
||||||
reservations,
|
reservations,
|
||||||
"SlotReservationsFull"
|
"SlotReservationsFull",
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
53
test/evm.js
53
test/evm.js
@ -1,23 +1,27 @@
|
|||||||
const { ethers } = require("hardhat")
|
const {
|
||||||
|
time,
|
||||||
|
mine,
|
||||||
|
takeSnapshot,
|
||||||
|
} = require("@nomicfoundation/hardhat-network-helpers")
|
||||||
|
const hre = require("hardhat")
|
||||||
|
const provider = hre.network.provider
|
||||||
|
|
||||||
let snapshots = []
|
const snapshots = []
|
||||||
|
|
||||||
async function snapshot() {
|
async function snapshot() {
|
||||||
const id = await ethers.provider.send("evm_snapshot")
|
const snapshot = await takeSnapshot()
|
||||||
const time = await currentTime()
|
const time = await currentTime()
|
||||||
const automine = await ethers.provider.send("hardhat_getAutomine")
|
const automine = await provider.send("hardhat_getAutomine")
|
||||||
snapshots.push({ id, time, automine })
|
snapshots.push({ snapshot, automine, time })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function revert() {
|
async function revert() {
|
||||||
const { id, time, automine } = snapshots.pop()
|
const { snapshot, time, automine } = snapshots.pop()
|
||||||
await ethers.provider.send("evm_revert", [id])
|
if (snapshot) {
|
||||||
|
await snapshot.restore()
|
||||||
const current = await currentTime()
|
await setNextBlockTimestamp(time)
|
||||||
const nextTime = Math.max(time + 1, current + 1)
|
await provider.send("evm_setAutomine", [automine])
|
||||||
|
}
|
||||||
await ethers.provider.send("evm_setNextBlockTimestamp", [nextTime])
|
|
||||||
await ethers.provider.send("evm_setAutomine", [automine])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,38 +30,33 @@ async function revert() {
|
|||||||
* When automine mode is disabled, transactions that revert are silently ignored!
|
* When automine mode is disabled, transactions that revert are silently ignored!
|
||||||
*/
|
*/
|
||||||
async function setAutomine(enabled) {
|
async function setAutomine(enabled) {
|
||||||
await ethers.provider.send("evm_setAutomine", [enabled])
|
await provider.send("evm_setAutomine", [enabled])
|
||||||
}
|
|
||||||
|
|
||||||
async function mine() {
|
|
||||||
await ethers.provider.send("evm_mine")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureMinimumBlockHeight(height) {
|
async function ensureMinimumBlockHeight(height) {
|
||||||
while ((await ethers.provider.getBlockNumber()) < height) {
|
while ((await time.latestBlock()) < height) {
|
||||||
await mine()
|
await mine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setNextBlockTimestamp(timestamp) {
|
||||||
|
return time.setNextBlockTimestamp(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
async function currentTime() {
|
async function currentTime() {
|
||||||
let block = await ethers.provider.getBlock("latest")
|
return time.latest()
|
||||||
return block.timestamp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function advanceTime(seconds) {
|
async function advanceTime(seconds) {
|
||||||
await ethers.provider.send("evm_increaseTime", [seconds])
|
await time.increase(seconds)
|
||||||
await mine()
|
await mine()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function advanceTimeTo(timestamp) {
|
async function advanceTimeTo(timestamp) {
|
||||||
await setNextBlockTimestamp(timestamp)
|
await time.setNextBlockTimestamp(timestamp)
|
||||||
await mine()
|
await mine()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setNextBlockTimestamp(timestamp) {
|
|
||||||
await ethers.provider.send("evm_setNextBlockTimestamp", [timestamp])
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
snapshot,
|
snapshot,
|
||||||
revert,
|
revert,
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
const { ethers } = require("hardhat")
|
|
||||||
const { hours } = require("./time")
|
const { hours } = require("./time")
|
||||||
const { hexlify, randomBytes } = ethers.utils
|
const { hexlify, randomBytes } = require("hardhat").ethers
|
||||||
|
|
||||||
const exampleConfiguration = () => ({
|
const exampleConfiguration = () => ({
|
||||||
collateral: {
|
collateral: {
|
||||||
@ -36,7 +35,7 @@ const exampleRequest = async () => {
|
|||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
cid: Buffer.from("zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob"),
|
cid: Buffer.from("zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob"),
|
||||||
merkleRoot: Array.from(randomBytes(32)),
|
merkleRoot: randomBytes(32),
|
||||||
},
|
},
|
||||||
expiry: hours(1),
|
expiry: hours(1),
|
||||||
nonce: hexlify(randomBytes(32)),
|
nonce: hexlify(randomBytes(32)),
|
||||||
|
|||||||
15
test/helpers.js
Normal file
15
test/helpers.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
module.exports = {
|
||||||
|
assertDeploymentRejectedWithCustomError: async function (
|
||||||
|
customError,
|
||||||
|
deploymentPromise,
|
||||||
|
) {
|
||||||
|
const error = await expect(deploymentPromise).to.be.rejected
|
||||||
|
|
||||||
|
expect(error)
|
||||||
|
.to.have.property("message")
|
||||||
|
.that.contains(
|
||||||
|
customError,
|
||||||
|
`Expected error ${expectedError}, but got ${error.message}`,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -1,12 +1,11 @@
|
|||||||
const { ethers } = require("hardhat")
|
const { keccak256, AbiCoder } = require("hardhat").ethers
|
||||||
const { keccak256, defaultAbiCoder } = ethers.utils
|
|
||||||
|
|
||||||
function requestId(request) {
|
function requestId(request) {
|
||||||
const Ask = "tuple(uint256, uint256, uint256, uint64, uint64, uint64, int64)"
|
const Ask = "tuple(uint256, uint256, uint256, uint64, uint64, uint64, int64)"
|
||||||
const Content = "tuple(bytes, bytes32)"
|
const Content = "tuple(bytes, bytes32)"
|
||||||
const Request =
|
const Request =
|
||||||
"tuple(address, " + Ask + ", " + Content + ", uint64, bytes32)"
|
"tuple(address, " + Ask + ", " + Content + ", uint64, bytes32)"
|
||||||
return keccak256(defaultAbiCoder.encode([Request], requestToArray(request)))
|
return keccak256(new AbiCoder().encode([Request], requestToArray(request)))
|
||||||
}
|
}
|
||||||
|
|
||||||
function askToArray(ask) {
|
function askToArray(ask) {
|
||||||
@ -40,7 +39,7 @@ function requestToArray(request) {
|
|||||||
function slotId(slot) {
|
function slotId(slot) {
|
||||||
const types = "tuple(bytes32, uint256)"
|
const types = "tuple(bytes32, uint256)"
|
||||||
const values = [slot.request, slot.index]
|
const values = [slot.request, slot.index]
|
||||||
const encoding = defaultAbiCoder.encode([types], [values])
|
const encoding = new AbiCoder().encode([types], [values])
|
||||||
return keccak256(encoding)
|
return keccak256(encoding)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,16 +4,16 @@ const { payoutForDuration } = require("./price")
|
|||||||
const { collateralPerSlot } = require("./collateral")
|
const { collateralPerSlot } = require("./collateral")
|
||||||
|
|
||||||
async function waitUntilCancelled(contract, request) {
|
async function waitUntilCancelled(contract, request) {
|
||||||
const expiry = (await contract.requestExpiry(requestId(request))).toNumber()
|
const expiry = await contract.requestExpiry(requestId(request))
|
||||||
// We do +1, because the expiry check in contract is done as `>` and not `>=`.
|
// We do +1, because the expiry check in contract is done as `>` and not `>=`.
|
||||||
await advanceTimeTo(expiry + 1)
|
return advanceTimeTo(expiry + 1n)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitUntilSlotsFilled(contract, request, proof, token, slots) {
|
async function waitUntilSlotsFilled(contract, request, proof, token, slots) {
|
||||||
let collateral = collateralPerSlot(request)
|
let collateral = collateralPerSlot(request)
|
||||||
await token.approve(contract.address, collateral * slots.length)
|
await token.approve(await contract.getAddress(), collateral * slots.length)
|
||||||
|
|
||||||
let requestEnd = (await contract.requestEnd(requestId(request))).toNumber()
|
let requestEnd = await contract.requestEnd(requestId(request))
|
||||||
const payouts = []
|
const payouts = []
|
||||||
for (let slotIndex of slots) {
|
for (let slotIndex of slots) {
|
||||||
await contract.reserveSlot(requestId(request), slotIndex)
|
await contract.reserveSlot(requestId(request), slotIndex)
|
||||||
@ -22,7 +22,7 @@ async function waitUntilSlotsFilled(contract, request, proof, token, slots) {
|
|||||||
payouts[slotIndex] = payoutForDuration(
|
payouts[slotIndex] = payoutForDuration(
|
||||||
request,
|
request,
|
||||||
await currentTime(),
|
await currentTime(),
|
||||||
requestEnd
|
requestEnd,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,14 +35,14 @@ async function waitUntilStarted(contract, request, proof, token) {
|
|||||||
request,
|
request,
|
||||||
proof,
|
proof,
|
||||||
token,
|
token,
|
||||||
Array.from({ length: request.ask.slots }, (_, i) => i)
|
Array.from({ length: request.ask.slots }, (_, i) => i),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitUntilFinished(contract, requestId) {
|
async function waitUntilFinished(contract, requestId) {
|
||||||
const end = (await contract.requestEnd(requestId)).toNumber()
|
const end = await contract.requestEnd(requestId)
|
||||||
// We do +1, because the end check in contract is done as `>` and not `>=`.
|
// We do +1, because the end check in contract is done as `>` and not `>=`.
|
||||||
await advanceTimeTo(end + 1)
|
await advanceTimeTo(end + 1n)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitUntilFailed(contract, request) {
|
async function waitUntilFailed(contract, request) {
|
||||||
@ -72,7 +72,7 @@ function patchOverloads(contract) {
|
|||||||
if (logicalXor(rewardRecipient, collateralRecipient)) {
|
if (logicalXor(rewardRecipient, collateralRecipient)) {
|
||||||
// XOR, if exactly one is truthy
|
// XOR, if exactly one is truthy
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Invalid freeSlot overload, you must specify both `rewardRecipient` and `collateralRecipient` or neither."
|
"Invalid freeSlot overload, you must specify both `rewardRecipient` and `collateralRecipient` or neither.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +96,11 @@ function patchOverloads(contract) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function littleEndianToBigInt(littleEndian) {
|
||||||
|
const buffer = Buffer.from(littleEndian)
|
||||||
|
return BigInt(`0x${buffer.toString("hex")}`)
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
waitUntilCancelled,
|
waitUntilCancelled,
|
||||||
waitUntilStarted,
|
waitUntilStarted,
|
||||||
@ -104,4 +109,5 @@ module.exports = {
|
|||||||
waitUntilFailed,
|
waitUntilFailed,
|
||||||
waitUntilSlotFailed,
|
waitUntilSlotFailed,
|
||||||
patchOverloads,
|
patchOverloads,
|
||||||
|
littleEndianToBigInt,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,21 @@ function maxPrice(request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function payoutForDuration(request, start, end) {
|
function payoutForDuration(request, start, end) {
|
||||||
return (end - start) * pricePerSlotPerSecond(request)
|
return (Number(end) - Number(start)) * pricePerSlotPerSecond(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { maxPrice, pricePerSlotPerSecond, payoutForDuration }
|
function calculatePartialPayout(request, expiresAt, filledAt) {
|
||||||
|
return (Number(expiresAt) - Number(filledAt)) * pricePerSlotPerSecond(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateBalance(balance, reward) {
|
||||||
|
return BigInt(balance) + BigInt(reward)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
maxPrice,
|
||||||
|
pricePerSlotPerSecond,
|
||||||
|
payoutForDuration,
|
||||||
|
calculatePartialPayout,
|
||||||
|
calculateBalance,
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
const { Assertion } = require("chai")
|
const { Assertion } = require("chai")
|
||||||
const { currentTime } = require("./evm")
|
|
||||||
|
|
||||||
const RequestState = {
|
const RequestState = {
|
||||||
New: 0,
|
New: 0,
|
||||||
@ -10,13 +9,13 @@ const RequestState = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SlotState = {
|
const SlotState = {
|
||||||
Free: 0,
|
Free: 0n,
|
||||||
Filled: 1,
|
Filled: 1n,
|
||||||
Finished: 2,
|
Finished: 2n,
|
||||||
Failed: 3,
|
Failed: 3n,
|
||||||
Paid: 4,
|
Paid: 4n,
|
||||||
Cancelled: 5,
|
Cancelled: 5n,
|
||||||
Repair: 6,
|
Repair: 6n,
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableRequestAssertions() {
|
function enableRequestAssertions() {
|
||||||
@ -29,21 +28,21 @@ function enableRequestAssertions() {
|
|||||||
"expected request #{this} to have client #{exp} but got #{act}",
|
"expected request #{this} to have client #{exp} but got #{act}",
|
||||||
"expected request #{this} to not have client #{act}, expected #{exp}",
|
"expected request #{this} to not have client #{act}, expected #{exp}",
|
||||||
request.client, // expected
|
request.client, // expected
|
||||||
actual.client // actual
|
actual.client, // actual
|
||||||
)
|
)
|
||||||
this.assert(
|
this.assert(
|
||||||
actual.expiry == request.expiry,
|
actual.expiry == request.expiry,
|
||||||
"expected request #{this} to have expiry #{exp} but got #{act}",
|
"expected request #{this} to have expiry #{exp} but got #{act}",
|
||||||
"expected request #{this} to not have expiry #{act}, expected #{exp}",
|
"expected request #{this} to not have expiry #{act}, expected #{exp}",
|
||||||
request.expiry, // expected
|
request.expiry, // expected
|
||||||
actual.expiry // actual
|
actual.expiry, // actual
|
||||||
)
|
)
|
||||||
this.assert(
|
this.assert(
|
||||||
actual.nonce === request.nonce,
|
actual.nonce === request.nonce,
|
||||||
"expected request #{this} to have nonce #{exp} but got #{act}",
|
"expected request #{this} to have nonce #{exp} but got #{act}",
|
||||||
"expected request #{this} to not have nonce #{act}, expected #{exp}",
|
"expected request #{this} to not have nonce #{act}, expected #{exp}",
|
||||||
request.nonce, // expected
|
request.nonce, // expected
|
||||||
actual.nonce // actual
|
actual.nonce, // actual
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
28
verifier/networks/localhost/example-proof/proof.json
Normal file
28
verifier/networks/localhost/example-proof/proof.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"pi_a": [
|
||||||
|
"6534256117371673392078914918983405561950705741555607896616281548993166767050",
|
||||||
|
"4683704071819826577549511087725295557628226569657977399743888281350488008160",
|
||||||
|
"1"
|
||||||
|
],
|
||||||
|
"pi_b": [
|
||||||
|
[
|
||||||
|
"5214601005301447749408976517903523691026761713935248596363525026084207179898",
|
||||||
|
"12928862018496028019248584699701543976607635004280021323367162829172676142063"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"6802359022534661924490571652741723715650845080843101596088257121470226373081",
|
||||||
|
"13026065833109947056007317243769626467054545801812125911606045634450472294131"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"1",
|
||||||
|
"0"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"pi_c": [
|
||||||
|
"4064922718155280187684708354118623661512911161238889435233679092743470899962",
|
||||||
|
"16109406845320108508342512724551388830826795687369147885401665396548555026064",
|
||||||
|
"1"
|
||||||
|
],
|
||||||
|
"protocol": "groth16",
|
||||||
|
"curve": "bn128"
|
||||||
|
}
|
||||||
5
verifier/networks/localhost/example-proof/public.json
Normal file
5
verifier/networks/localhost/example-proof/public.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
"10941033813",
|
||||||
|
"16074246370508166450132968585287196391860062495017081813239200574579640171677",
|
||||||
|
"3"
|
||||||
|
]
|
||||||
4
verifier/networks/localhost/proof_main.circom
Normal file
4
verifier/networks/localhost/proof_main.circom
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pragma circom 2.0.0;
|
||||||
|
include "sample_cells.circom";
|
||||||
|
// SampleAndProven( maxDepth, maxLog2NSlots, blockTreeDepth, nFieldElemsPerCell, nSamples )
|
||||||
|
component main {public [entropy,dataSetRoot,slotIndex]} = SampleAndProve(32, 8, 5, 67, 5);
|
||||||
BIN
verifier/networks/localhost/proof_main.r1cs
Normal file
BIN
verifier/networks/localhost/proof_main.r1cs
Normal file
Binary file not shown.
BIN
verifier/networks/localhost/proof_main.wasm
Normal file
BIN
verifier/networks/localhost/proof_main.wasm
Normal file
Binary file not shown.
BIN
verifier/networks/localhost/proof_main.zkey
Normal file
BIN
verifier/networks/localhost/proof_main.zkey
Normal file
Binary file not shown.
104
verifier/networks/localhost/proof_main_verification_key.json
Normal file
104
verifier/networks/localhost/proof_main_verification_key.json
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
{
|
||||||
|
"protocol": "groth16",
|
||||||
|
"curve": "bn128",
|
||||||
|
"nPublic": 3,
|
||||||
|
"vk_alpha_1": [
|
||||||
|
"20491192805390485299153009773594534940189261866228447918068658471970481763042",
|
||||||
|
"9383485363053290200918347156157836566562967994039712273449902621266178545958",
|
||||||
|
"1"
|
||||||
|
],
|
||||||
|
"vk_beta_2": [
|
||||||
|
[
|
||||||
|
"6375614351688725206403948262868962793625744043794305715222011528459656738731",
|
||||||
|
"4252822878758300859123897981450591353533073413197771768651442665752259397132"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"10505242626370262277552901082094356697409835680220590971873171140371331206856",
|
||||||
|
"21847035105528745403288232691147584728191162732299865338377159692350059136679"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"1",
|
||||||
|
"0"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"vk_gamma_2": [
|
||||||
|
[
|
||||||
|
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
|
||||||
|
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
|
||||||
|
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"1",
|
||||||
|
"0"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"vk_delta_2": [
|
||||||
|
[
|
||||||
|
"3431611495862121747865613070227194870328323956412989443038622984801922101560",
|
||||||
|
"1839332345901382467668376261123176838724068493614256702976446713083234015641"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"4267694917739299737209021244200092851467599237028086467255985366439671917679",
|
||||||
|
"14005704598922119245150389324584568926199987543485693236347173598336567030859"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"1",
|
||||||
|
"0"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"vk_alphabeta_12": [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"2029413683389138792403550203267699914886160938906632433982220835551125967885",
|
||||||
|
"21072700047562757817161031222997517981543347628379360635925549008442030252106"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"5940354580057074848093997050200682056184807770593307860589430076672439820312",
|
||||||
|
"12156638873931618554171829126792193045421052652279363021382169897324752428276"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"7898200236362823042373859371574133993780991612861777490112507062703164551277",
|
||||||
|
"7074218545237549455313236346927434013100842096812539264420499035217050630853"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"7077479683546002997211712695946002074877511277312570035766170199895071832130",
|
||||||
|
"10093483419865920389913245021038182291233451549023025229112148274109565435465"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"4595479056700221319381530156280926371456704509942304414423590385166031118820",
|
||||||
|
"19831328484489333784475432780421641293929726139240675179672856274388269393268"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"11934129596455521040620786944827826205713621633706285934057045369193958244500",
|
||||||
|
"8037395052364110730298837004334506829870972346962140206007064471173334027475"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"IC": [
|
||||||
|
[
|
||||||
|
"11919420103024546168896650006162652130022732573970705849225139177428442519914",
|
||||||
|
"17747753383929265689844293401689552935018333420134132157824903795680624926572",
|
||||||
|
"1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"13158415194355348546090070151711085027834066488127676886518524272551654481129",
|
||||||
|
"18831701962118195025265682681702066674741422770850028135520336928884612556978",
|
||||||
|
"1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"20882269691461568155321689204947751047717828445545223718893788782534717197527",
|
||||||
|
"11996193054822748526485644723594571195813487505803351159052936325857690315211",
|
||||||
|
"1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"18155166643053044822201627105588517913195535693446564472247126736722594445000",
|
||||||
|
"13816319482622393060406816684195314200198627617641073470088058848129378231754",
|
||||||
|
"1"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
1
verifier/networks/localhost/zkey_hash.json
Normal file
1
verifier/networks/localhost/zkey_hash.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
"65dfa139e1c6dae5f50102cca2a856b759723c6ec6029343c2c6e926b855a6ca"
|
||||||
Loading…
x
Reference in New Issue
Block a user