@enclave-e3/contracts 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json +3 -3
- package/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/artifacts.d.ts +3 -3
- package/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/ITransparentUpgradeableProxy.json +1 -1
- package/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json +3 -3
- package/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/artifacts.d.ts +4 -4
- package/artifacts/build-info/{solc-0_8_28-c77ac33099bb85c15f46d194dd2b735e036cb799.json → solc-0_8_28-e60a5d7c133605edcf61acdd5ba43ab44ee0928e.json} +27 -12
- package/artifacts/build-info/solc-0_8_28-e60a5d7c133605edcf61acdd5ba43ab44ee0928e.output.json +1 -0
- package/artifacts/contracts/E3RefundManager.sol/E3RefundManager.json +684 -0
- package/artifacts/contracts/E3RefundManager.sol/artifacts.d.ts +27 -0
- package/artifacts/contracts/Enclave.sol/Enclave.json +530 -182
- package/artifacts/contracts/Enclave.sol/artifacts.d.ts +4 -4
- package/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +14 -1
- package/artifacts/contracts/interfaces/IBondingRegistry.sol/artifacts.d.ts +2 -2
- package/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +55 -5
- package/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/artifacts.d.ts +2 -2
- package/artifacts/contracts/interfaces/IComputeProvider.sol/IComputeProvider.json +1 -1
- package/artifacts/contracts/interfaces/IComputeProvider.sol/artifacts.d.ts +1 -1
- package/artifacts/contracts/interfaces/IDecryptionVerifier.sol/IDecryptionVerifier.json +1 -1
- package/artifacts/contracts/interfaces/IDecryptionVerifier.sol/artifacts.d.ts +1 -1
- package/artifacts/contracts/interfaces/IE3Program.sol/IE3Program.json +19 -24
- package/artifacts/contracts/interfaces/IE3Program.sol/artifacts.d.ts +2 -2
- package/artifacts/contracts/interfaces/IE3RefundManager.sol/IE3RefundManager.json +470 -0
- package/artifacts/contracts/interfaces/IE3RefundManager.sol/artifacts.d.ts +27 -0
- package/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +383 -128
- package/artifacts/contracts/interfaces/IEnclave.sol/artifacts.d.ts +2 -2
- package/artifacts/contracts/interfaces/ISlashVerifier.sol/ISlashVerifier.json +1 -1
- package/artifacts/contracts/interfaces/ISlashVerifier.sol/artifacts.d.ts +1 -1
- package/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +1 -1
- package/artifacts/contracts/interfaces/ISlashingManager.sol/artifacts.d.ts +1 -1
- package/artifacts/contracts/lib/ExitQueueLib.sol/ExitQueueLib.json +1 -1
- package/artifacts/contracts/lib/ExitQueueLib.sol/artifacts.d.ts +1 -1
- package/artifacts/contracts/registry/BondingRegistry.sol/BondingRegistry.json +16 -3
- package/artifacts/contracts/registry/BondingRegistry.sol/artifacts.d.ts +4 -4
- package/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +91 -25
- package/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/artifacts.d.ts +6 -6
- package/artifacts/contracts/slashing/SlashingManager.sol/SlashingManager.json +3 -3
- package/artifacts/contracts/slashing/SlashingManager.sol/artifacts.d.ts +3 -3
- package/artifacts/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistry.json +57 -7
- package/artifacts/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistryEmptyKey.json +57 -7
- package/artifacts/contracts/test/MockCiphernodeRegistry.sol/artifacts.d.ts +8 -8
- package/artifacts/contracts/test/MockComputeProvider.sol/MockComputeProvider.json +3 -3
- package/artifacts/contracts/test/MockComputeProvider.sol/artifacts.d.ts +3 -3
- package/artifacts/contracts/test/MockDecryptionVerifier.sol/MockDecryptionVerifier.json +1 -1
- package/artifacts/contracts/test/MockDecryptionVerifier.sol/artifacts.d.ts +1 -1
- package/artifacts/contracts/test/MockE3Program.sol/MockE3Program.json +21 -26
- package/artifacts/contracts/test/MockE3Program.sol/artifacts.d.ts +4 -4
- package/artifacts/contracts/test/MockSlashingVerifier.sol/MockSlashingVerifier.json +1 -1
- package/artifacts/contracts/test/MockSlashingVerifier.sol/artifacts.d.ts +1 -1
- package/artifacts/contracts/test/MockStableToken.sol/MockUSDC.json +3 -3
- package/artifacts/contracts/test/MockStableToken.sol/artifacts.d.ts +3 -3
- package/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json +14 -22
- package/artifacts/contracts/token/EnclaveTicketToken.sol/artifacts.d.ts +4 -4
- package/artifacts/contracts/token/EnclaveToken.sol/EnclaveToken.json +10 -10
- package/artifacts/contracts/token/EnclaveToken.sol/artifacts.d.ts +4 -4
- package/artifacts/contracts/verifier/DkgPkVerifier.sol/BaseZKHonkVerifier.json +89 -0
- package/artifacts/contracts/verifier/DkgPkVerifier.sol/CommitmentSchemeLib.json +13 -0
- package/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json +186 -0
- package/artifacts/contracts/verifier/DkgPkVerifier.sol/FrLib.json +13 -0
- package/artifacts/contracts/verifier/DkgPkVerifier.sol/Honk.json +13 -0
- package/artifacts/contracts/verifier/DkgPkVerifier.sol/HonkVerificationKey.json +13 -0
- package/artifacts/contracts/verifier/DkgPkVerifier.sol/IVerifier.json +38 -0
- package/artifacts/contracts/verifier/DkgPkVerifier.sol/RelationsLib.json +13 -0
- package/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json +400 -0
- package/artifacts/contracts/verifier/DkgPkVerifier.sol/artifacts.d.ts +155 -0
- package/artifacts/poseidon-solidity/PoseidonT3.sol/PoseidonT3.json +1 -1
- package/artifacts/poseidon-solidity/PoseidonT3.sol/artifacts.d.ts +1 -1
- package/contracts/E3RefundManager.sol +365 -0
- package/contracts/Enclave.sol +426 -119
- package/contracts/interfaces/IBondingRegistry.sol +6 -0
- package/contracts/interfaces/ICiphernodeRegistry.sol +29 -7
- package/contracts/interfaces/IE3.sol +2 -6
- package/contracts/interfaces/IE3Program.sol +3 -7
- package/contracts/interfaces/IE3RefundManager.sol +150 -0
- package/contracts/interfaces/IEnclave.sol +186 -64
- package/contracts/registry/BondingRegistry.sol +9 -0
- package/contracts/registry/CiphernodeRegistryOwnable.sol +89 -42
- package/contracts/test/MockCiphernodeRegistry.sol +20 -6
- package/contracts/test/MockE3Program.sol +3 -8
- package/contracts/verifier/DkgPkVerifier.sol +3140 -0
- package/dist/hardhat.config.d.ts.map +1 -1
- package/dist/hardhat.config.js +4 -4
- package/dist/ignition/modules/dkgPkVerifier.d.ts +3 -0
- package/dist/ignition/modules/dkgPkVerifier.d.ts.map +1 -0
- package/dist/ignition/modules/dkgPkVerifier.js +10 -0
- package/dist/ignition/modules/e3RefundManager.d.ts +3 -0
- package/dist/ignition/modules/e3RefundManager.d.ts.map +1 -0
- package/dist/ignition/modules/e3RefundManager.js +23 -0
- package/dist/ignition/modules/enclave.d.ts.map +1 -1
- package/dist/ignition/modules/enclave.js +10 -0
- package/dist/scripts/deployAndSave/e3RefundManager.d.ts +20 -0
- package/dist/scripts/deployAndSave/e3RefundManager.d.ts.map +1 -0
- package/dist/scripts/deployAndSave/e3RefundManager.js +55 -0
- package/dist/scripts/deployAndSave/enclave.d.ts +13 -1
- package/dist/scripts/deployAndSave/enclave.d.ts.map +1 -1
- package/dist/scripts/deployAndSave/enclave.js +8 -1
- package/dist/scripts/deployAndSave/verifiers.d.ts +29 -0
- package/dist/scripts/deployAndSave/verifiers.d.ts.map +1 -0
- package/dist/scripts/deployAndSave/verifiers.js +109 -0
- package/dist/scripts/deployEnclave.d.ts.map +1 -1
- package/dist/scripts/deployEnclave.js +36 -0
- package/dist/scripts/deployVerifiers.d.ts +2 -0
- package/dist/scripts/deployVerifiers.d.ts.map +1 -0
- package/dist/scripts/deployVerifiers.js +33 -0
- package/dist/scripts/index.d.ts +1 -0
- package/dist/scripts/index.d.ts.map +1 -1
- package/dist/scripts/index.js +1 -0
- package/dist/tasks/enclave.d.ts +0 -2
- package/dist/tasks/enclave.d.ts.map +1 -1
- package/dist/tasks/enclave.js +8 -72
- package/dist/tasks/program.d.ts +2 -0
- package/dist/tasks/program.d.ts.map +1 -0
- package/dist/tasks/program.js +55 -0
- package/dist/test/E3Lifecycle/E3Integration.spec.d.ts +2 -0
- package/dist/test/E3Lifecycle/E3Integration.spec.d.ts.map +1 -0
- package/dist/test/E3Lifecycle/E3Integration.spec.js +757 -0
- package/dist/test/Enclave.spec.js +83 -406
- package/dist/test/Registry/CiphernodeRegistryOwnable.spec.js +133 -59
- package/dist/types/contracts/E3RefundManager.d.ts +379 -0
- package/dist/types/contracts/E3RefundManager.d.ts.map +1 -0
- package/dist/types/contracts/E3RefundManager.js +24 -0
- package/dist/types/contracts/Enclave.d.ts +325 -73
- package/dist/types/contracts/Enclave.d.ts.map +1 -1
- package/dist/types/contracts/Enclave.js +27 -3
- package/dist/types/contracts/index.d.ts +3 -0
- package/dist/types/contracts/index.d.ts.map +1 -1
- package/dist/types/contracts/interfaces/IBondingRegistry.d.ts +11 -1
- package/dist/types/contracts/interfaces/IBondingRegistry.d.ts.map +1 -1
- package/dist/types/contracts/interfaces/ICiphernodeRegistry.d.ts +35 -7
- package/dist/types/contracts/interfaces/ICiphernodeRegistry.d.ts.map +1 -1
- package/dist/types/contracts/interfaces/ICiphernodeRegistry.js +4 -0
- package/dist/types/contracts/interfaces/IE3Program.d.ts +16 -18
- package/dist/types/contracts/interfaces/IE3Program.d.ts.map +1 -1
- package/dist/types/contracts/interfaces/IE3RefundManager.d.ts +238 -0
- package/dist/types/contracts/interfaces/IE3RefundManager.d.ts.map +1 -0
- package/dist/types/contracts/interfaces/IE3RefundManager.js +16 -0
- package/dist/types/contracts/interfaces/IEnclave.d.ts +290 -62
- package/dist/types/contracts/interfaces/IEnclave.d.ts.map +1 -1
- package/dist/types/contracts/interfaces/IEnclave.js +27 -3
- package/dist/types/contracts/interfaces/index.d.ts +1 -0
- package/dist/types/contracts/interfaces/index.d.ts.map +1 -1
- package/dist/types/contracts/registry/BondingRegistry.d.ts +11 -1
- package/dist/types/contracts/registry/BondingRegistry.d.ts.map +1 -1
- package/dist/types/contracts/registry/CiphernodeRegistryOwnable.d.ts +35 -7
- package/dist/types/contracts/registry/CiphernodeRegistryOwnable.d.ts.map +1 -1
- package/dist/types/contracts/registry/CiphernodeRegistryOwnable.js +4 -0
- package/dist/types/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistry.d.ts +35 -7
- package/dist/types/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistry.d.ts.map +1 -1
- package/dist/types/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistry.js +4 -0
- package/dist/types/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistryEmptyKey.d.ts +35 -7
- package/dist/types/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistryEmptyKey.d.ts.map +1 -1
- package/dist/types/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistryEmptyKey.js +4 -0
- package/dist/types/contracts/test/MockE3Program.d.ts +16 -18
- package/dist/types/contracts/test/MockE3Program.d.ts.map +1 -1
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/BaseZKHonkVerifier.d.ts +36 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/BaseZKHonkVerifier.d.ts.map +1 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/BaseZKHonkVerifier.js +1 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.d.ts +36 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.d.ts.map +1 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.js +1 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/IVerifier.d.ts +36 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/IVerifier.d.ts.map +1 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/IVerifier.js +1 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.d.ts +138 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.d.ts.map +1 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.js +1 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/index.d.ts +5 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/index.d.ts.map +1 -0
- package/dist/types/contracts/verifier/DkgPkVerifier.sol/index.js +1 -0
- package/dist/types/contracts/verifier/index.d.ts +3 -0
- package/dist/types/contracts/verifier/index.d.ts.map +1 -0
- package/dist/types/contracts/verifier/index.js +1 -0
- package/dist/types/factories/@openzeppelin/contracts/proxy/transparent/ProxyAdmin__factory.d.ts +1 -1
- package/dist/types/factories/@openzeppelin/contracts/proxy/transparent/ProxyAdmin__factory.d.ts.map +1 -1
- package/dist/types/factories/@openzeppelin/contracts/proxy/transparent/ProxyAdmin__factory.js +1 -1
- package/dist/types/factories/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy__factory.d.ts +1 -1
- package/dist/types/factories/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy__factory.d.ts.map +1 -1
- package/dist/types/factories/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy__factory.js +1 -1
- package/dist/types/factories/contracts/E3RefundManager__factory.d.ts +540 -0
- package/dist/types/factories/contracts/E3RefundManager__factory.d.ts.map +1 -0
- package/dist/types/factories/contracts/E3RefundManager__factory.js +706 -0
- package/dist/types/factories/contracts/Enclave__factory.d.ts +412 -143
- package/dist/types/factories/contracts/Enclave__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/Enclave__factory.js +528 -180
- package/dist/types/factories/contracts/index.d.ts +2 -0
- package/dist/types/factories/contracts/index.d.ts.map +1 -1
- package/dist/types/factories/contracts/index.js +2 -0
- package/dist/types/factories/contracts/interfaces/IBondingRegistry__factory.d.ts +10 -0
- package/dist/types/factories/contracts/interfaces/IBondingRegistry__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/interfaces/IBondingRegistry__factory.js +13 -0
- package/dist/types/factories/contracts/interfaces/ICiphernodeRegistry__factory.d.ts +42 -4
- package/dist/types/factories/contracts/interfaces/ICiphernodeRegistry__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/interfaces/ICiphernodeRegistry__factory.js +54 -4
- package/dist/types/factories/contracts/interfaces/IE3Program__factory.d.ts +14 -18
- package/dist/types/factories/contracts/interfaces/IE3Program__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/interfaces/IE3Program__factory.js +18 -23
- package/dist/types/factories/contracts/interfaces/IE3RefundManager__factory.d.ts +358 -0
- package/dist/types/factories/contracts/interfaces/IE3RefundManager__factory.d.ts.map +1 -0
- package/dist/types/factories/contracts/interfaces/IE3RefundManager__factory.js +471 -0
- package/dist/types/factories/contracts/interfaces/IEnclave__factory.d.ts +303 -103
- package/dist/types/factories/contracts/interfaces/IEnclave__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/interfaces/IEnclave__factory.js +382 -127
- package/dist/types/factories/contracts/interfaces/index.d.ts +1 -0
- package/dist/types/factories/contracts/interfaces/index.d.ts.map +1 -1
- package/dist/types/factories/contracts/interfaces/index.js +1 -0
- package/dist/types/factories/contracts/registry/BondingRegistry__factory.d.ts +11 -1
- package/dist/types/factories/contracts/registry/BondingRegistry__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/registry/BondingRegistry__factory.js +14 -1
- package/dist/types/factories/contracts/registry/CiphernodeRegistryOwnable__factory.d.ts +62 -12
- package/dist/types/factories/contracts/registry/CiphernodeRegistryOwnable__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/registry/CiphernodeRegistryOwnable__factory.js +79 -13
- package/dist/types/factories/contracts/slashing/SlashingManager__factory.d.ts +1 -1
- package/dist/types/factories/contracts/slashing/SlashingManager__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/slashing/SlashingManager__factory.js +1 -1
- package/dist/types/factories/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistryEmptyKey__factory.d.ts +43 -5
- package/dist/types/factories/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistryEmptyKey__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistryEmptyKey__factory.js +55 -5
- package/dist/types/factories/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistry__factory.d.ts +43 -5
- package/dist/types/factories/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistry__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/test/MockCiphernodeRegistry.sol/MockCiphernodeRegistry__factory.js +55 -5
- package/dist/types/factories/contracts/test/MockComputeProvider__factory.d.ts +1 -1
- package/dist/types/factories/contracts/test/MockComputeProvider__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/test/MockComputeProvider__factory.js +1 -1
- package/dist/types/factories/contracts/test/MockE3Program__factory.d.ts +15 -19
- package/dist/types/factories/contracts/test/MockE3Program__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/test/MockE3Program__factory.js +19 -24
- package/dist/types/factories/contracts/test/MockStableToken.sol/MockUSDC__factory.d.ts +1 -1
- package/dist/types/factories/contracts/test/MockStableToken.sol/MockUSDC__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/test/MockStableToken.sol/MockUSDC__factory.js +1 -1
- package/dist/types/factories/contracts/token/EnclaveTicketToken__factory.d.ts +1 -1
- package/dist/types/factories/contracts/token/EnclaveTicketToken__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/token/EnclaveTicketToken__factory.js +1 -1
- package/dist/types/factories/contracts/token/EnclaveToken__factory.d.ts +1 -1
- package/dist/types/factories/contracts/token/EnclaveToken__factory.d.ts.map +1 -1
- package/dist/types/factories/contracts/token/EnclaveToken__factory.js +1 -1
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/BaseZKHonkVerifier__factory.d.ts +66 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/BaseZKHonkVerifier__factory.d.ts.map +1 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/BaseZKHonkVerifier__factory.js +90 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier__factory.d.ts +87 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier__factory.d.ts.map +1 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier__factory.js +122 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/IVerifier__factory.d.ts +26 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/IVerifier__factory.d.ts.map +1 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/IVerifier__factory.js +39 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib__factory.d.ts +323 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib__factory.d.ts.map +1 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib__factory.js +422 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/index.d.ts +5 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/index.d.ts.map +1 -0
- package/dist/types/factories/contracts/verifier/DkgPkVerifier.sol/index.js +7 -0
- package/dist/types/factories/contracts/verifier/index.d.ts +2 -0
- package/dist/types/factories/contracts/verifier/index.d.ts.map +1 -0
- package/dist/types/factories/contracts/verifier/index.js +4 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +6 -0
- package/package.json +17 -3
- package/artifacts/build-info/solc-0_8_28-c77ac33099bb85c15f46d194dd2b735e036cb799.output.json +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_format": "hh3-sol-build-info-1",
|
|
3
|
-
"id": "solc-0_8_28-
|
|
3
|
+
"id": "solc-0_8_28-e60a5d7c133605edcf61acdd5ba43ab44ee0928e",
|
|
4
4
|
"solcVersion": "0.8.28",
|
|
5
5
|
"solcLongVersion": "0.8.28+commit.7893614a",
|
|
6
6
|
"userSourceNameMap": {
|
|
7
|
+
"contracts/E3RefundManager.sol": "project/contracts/E3RefundManager.sol",
|
|
7
8
|
"contracts/Enclave.sol": "project/contracts/Enclave.sol",
|
|
8
9
|
"contracts/interfaces/IBondingRegistry.sol": "project/contracts/interfaces/IBondingRegistry.sol",
|
|
9
10
|
"contracts/interfaces/ICiphernodeRegistry.sol": "project/contracts/interfaces/ICiphernodeRegistry.sol",
|
|
@@ -11,6 +12,7 @@
|
|
|
11
12
|
"contracts/interfaces/IDecryptionVerifier.sol": "project/contracts/interfaces/IDecryptionVerifier.sol",
|
|
12
13
|
"contracts/interfaces/IE3.sol": "project/contracts/interfaces/IE3.sol",
|
|
13
14
|
"contracts/interfaces/IE3Program.sol": "project/contracts/interfaces/IE3Program.sol",
|
|
15
|
+
"contracts/interfaces/IE3RefundManager.sol": "project/contracts/interfaces/IE3RefundManager.sol",
|
|
14
16
|
"contracts/interfaces/IEnclave.sol": "project/contracts/interfaces/IEnclave.sol",
|
|
15
17
|
"contracts/interfaces/ISlashVerifier.sol": "project/contracts/interfaces/ISlashVerifier.sol",
|
|
16
18
|
"contracts/interfaces/ISlashingManager.sol": "project/contracts/interfaces/ISlashingManager.sol",
|
|
@@ -26,6 +28,7 @@
|
|
|
26
28
|
"contracts/test/MockStableToken.sol": "project/contracts/test/MockStableToken.sol",
|
|
27
29
|
"contracts/token/EnclaveTicketToken.sol": "project/contracts/token/EnclaveTicketToken.sol",
|
|
28
30
|
"contracts/token/EnclaveToken.sol": "project/contracts/token/EnclaveToken.sol",
|
|
31
|
+
"contracts/verifier/DkgPkVerifier.sol": "project/contracts/verifier/DkgPkVerifier.sol",
|
|
29
32
|
"@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol": "npm/@openzeppelin/contracts@5.3.0/proxy/transparent/ProxyAdmin.sol",
|
|
30
33
|
"@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol": "npm/@openzeppelin/contracts@5.3.0/proxy/transparent/TransparentUpgradeableProxy.sol",
|
|
31
34
|
"poseidon-solidity/PoseidonT3.sol": "npm/poseidon-solidity@0.0.5/PoseidonT3.sol"
|
|
@@ -39,7 +42,7 @@
|
|
|
39
42
|
},
|
|
40
43
|
"optimizer": {
|
|
41
44
|
"enabled": true,
|
|
42
|
-
"runs":
|
|
45
|
+
"runs": 100
|
|
43
46
|
},
|
|
44
47
|
"outputSelection": {
|
|
45
48
|
"*": {
|
|
@@ -60,6 +63,9 @@
|
|
|
60
63
|
"project/:@openzeppelin/contracts-upgradeable/=npm/@openzeppelin/contracts-upgradeable@5.4.0/",
|
|
61
64
|
"project/:@openzeppelin/contracts-upgradeable/=npm/@openzeppelin/contracts-upgradeable@5.4.0/",
|
|
62
65
|
"project/:@openzeppelin/contracts-upgradeable/=npm/@openzeppelin/contracts-upgradeable@5.4.0/",
|
|
66
|
+
"project/:@openzeppelin/contracts-upgradeable/=npm/@openzeppelin/contracts-upgradeable@5.4.0/",
|
|
67
|
+
"project/:@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.3.0/",
|
|
68
|
+
"project/:@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.3.0/",
|
|
63
69
|
"project/:@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.3.0/",
|
|
64
70
|
"project/:@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.3.0/",
|
|
65
71
|
"project/:@openzeppelin/contracts/=npm/@openzeppelin/contracts@5.3.0/",
|
|
@@ -240,14 +246,17 @@
|
|
|
240
246
|
"npm/poseidon-solidity@0.0.5/PoseidonT3.sol": {
|
|
241
247
|
"content": "/// SPDX-License-Identifier: MIT\npragma solidity >=0.7.0;\n\nlibrary PoseidonT3 {\n uint constant M00 = 0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b;\n uint constant M01 = 0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771;\n uint constant M02 = 0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7;\n uint constant M10 = 0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0;\n uint constant M11 = 0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23;\n uint constant M12 = 0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911;\n\n // See here for a simplified implementation: https://github.com/vimwitch/poseidon-solidity/blob/e57becdabb65d99fdc586fe1e1e09e7108202d53/contracts/Poseidon.sol#L40\n // Inspired by: https://github.com/iden3/circomlibjs/blob/v0.0.8/src/poseidon_slow.js\n function hash(uint[2] memory) public pure returns (uint) {\n assembly {\n let F := 21888242871839275222246405745257275088548364400416034343698204186575808495617\n let M20 := 0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d\n let M21 := 0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa\n let M22 := 0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0\n\n // load the inputs from memory\n let state1 := add(mod(mload(0x80), F), 0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864)\n let state2 := add(mod(mload(0xa0), F), 0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5)\n let scratch0 := mulmod(state1, state1, F)\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\n scratch0 := mulmod(state2, state2, F)\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\n scratch0 := add(\n 0x2f27be690fdaee46c3ce28f7532b13c856c35342c84bda6e20966310fadc01d0,\n add(add(15452833169820924772166449970675545095234312153403844297388521437673434406763, mulmod(state1, M10, F)), mulmod(state2, M20, F))\n )\n let scratch1 := add(\n 0x2b2ae1acf68b7b8d2416bebf3d4f6234b763fe04b8043ee48b8327bebca16cf2,\n add(add(18674271267752038776579386132900109523609358935013267566297499497165104279117, mulmod(state1, M11, F)), mulmod(state2, M21, F))\n )\n let scratch2 := add(\n 0x0319d062072bef7ecca5eac06f97d4d55952c175ab6b03eae64b44c7dbf11cfa,\n add(add(14817777843080276494683266178512808687156649753153012854386334860566696099579, mulmod(state1, M12, F)), mulmod(state2, M22, F))\n )\n let state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := mulmod(scratch1, scratch1, F)\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\n state0 := mulmod(scratch2, scratch2, F)\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\n state0 := add(0x28813dcaebaeaa828a376df87af4a63bc8b7bf27ad49c6298ef7b387bf28526d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x2727673b2ccbc903f181bf38e1c1d40d2033865200c352bc150928adddf9cb78, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x234ec45ca27727c2e74abd2b2a1494cd6efbd43e340587d6b8fb9e31e65cc632, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := mulmod(state1, state1, F)\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\n scratch0 := mulmod(state2, state2, F)\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\n scratch0 := add(0x15b52534031ae18f7f862cb2cf7cf760ab10a8150a337b1ccd99ff6e8797d428, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0dc8fad6d9e4b35f5ed9a3d186b79ce38e0e8a8d1b58b132d701d4eecf68d1f6, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x1bcd95ffc211fbca600f705fad3fb567ea4eb378f62e1fec97805518a47e4d9c, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := mulmod(scratch1, scratch1, F)\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\n state0 := mulmod(scratch2, scratch2, F)\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\n state0 := add(0x10520b0ab721cadfe9eff81b016fc34dc76da36c2578937817cb978d069de559, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1f6d48149b8e7f7d9b257d8ed5fbbaf42932498075fed0ace88a9eb81f5627f6, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1d9655f652309014d29e00ef35a2089bfff8dc1c816f0dc9ca34bdb5460c8705, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x04df5a56ff95bcafb051f7b1cd43a99ba731ff67e47032058fe3d4185697cc7d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0672d995f8fff640151b3d290cedaf148690a10a8c8424a7f6ec282b6e4be828, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x099952b414884454b21200d7ffafdd5f0c9a9dcc06f2708e9fc1d8209b5c75b9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x052cba2255dfd00c7c483143ba8d469448e43586a9b4cd9183fd0e843a6b9fa6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0b8badee690adb8eb0bd74712b7999af82de55707251ad7716077cb93c464ddc, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x119b1590f13307af5a1ee651020c07c749c15d60683a8050b963d0a8e4b2bdd1, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x03150b7cd6d5d17b2529d36be0f67b832c4acfc884ef4ee5ce15be0bfb4a8d09, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2cc6182c5e14546e3cf1951f173912355374efb83d80898abe69cb317c9ea565, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x005032551e6378c450cfe129a404b3764218cadedac14e2b92d2cd73111bf0f9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x233237e3289baa34bb147e972ebcb9516469c399fcc069fb88f9da2cc28276b5, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x05c8f4f4ebd4a6e3c980d31674bfbe6323037f21b34ae5a4e80c2d4c24d60280, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x0a7b1db13042d396ba05d818a319f25252bcf35ef3aeed91ee1f09b2590fc65b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2a73b71f9b210cf5b14296572c9d32dbf156e2b086ff47dc5df542365a404ec0, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1ac9b0417abcc9a1935107e9ffc91dc3ec18f2c4dbe7f22976a760bb5c50c460, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x12c0339ae08374823fabb076707ef479269f3e4d6cb104349015ee046dc93fc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x0b7475b102a165ad7f5b18db4e1e704f52900aa3253baac68246682e56e9a28e, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x037c2849e191ca3edb1c5e49f6e8b8917c843e379366f2ea32ab3aa88d7f8448, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x05a6811f8556f014e92674661e217e9bd5206c5c93a07dc145fdb176a716346f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x29a795e7d98028946e947b75d54e9f044076e87a7b2883b47b675ef5f38bd66e, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x20439a0c84b322eb45a3857afc18f5826e8c7382c8a1585c507be199981fd22f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2e0ba8d94d9ecf4a94ec2050c7371ff1bb50f27799a84b6d4a2a6f2a0982c887, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x143fd115ce08fb27ca38eb7cce822b4517822cd2109048d2e6d0ddcca17d71c8, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0c64cbecb1c734b857968dbbdcf813cdf8611659323dbcbfc84323623be9caf1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x028a305847c683f646fca925c163ff5ae74f348d62c2b670f1426cef9403da53, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2e4ef510ff0b6fda5fa940ab4c4380f26a6bcb64d89427b824d6755b5db9e30c, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0081c95bc43384e663d79270c956ce3b8925b4f6d033b078b96384f50579400e, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2ed5f0c91cbd9749187e2fade687e05ee2491b349c039a0bba8a9f4023a0bb38, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x30509991f88da3504bbf374ed5aae2f03448a22c76234c8c990f01f33a735206, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1c3f20fd55409a53221b7c4d49a356b9f0a1119fb2067b41a7529094424ec6ad, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x10b4e7f3ab5df003049514459b6e18eec46bb2213e8e131e170887b47ddcb96c, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2a1982979c3ff7f43ddd543d891c2abddd80f804c077d775039aa3502e43adef, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1c74ee64f15e1db6feddbead56d6d55dba431ebc396c9af95cad0f1315bd5c91, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x07533ec850ba7f98eab9303cace01b4b9e4f2e8b82708cfa9c2fe45a0ae146a0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x21576b438e500449a151e4eeaf17b154285c68f42d42c1808a11abf3764c0750, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x2f17c0559b8fe79608ad5ca193d62f10bce8384c815f0906743d6930836d4a9e, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x2d477e3862d07708a79e8aae946170bc9775a4201318474ae665b0b1b7e2730e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x162f5243967064c390e095577984f291afba2266c38f5abcd89be0f5b2747eab, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2b4cb233ede9ba48264ecd2c8ae50d1ad7a8596a87f29f8a7777a70092393311, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2c8fbcb2dd8573dc1dbaf8f4622854776db2eece6d85c4cf4254e7c35e03b07a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x1d6f347725e4816af2ff453f0cd56b199e1b61e9f601e9ade5e88db870949da9, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x204b0c397f4ebe71ebc2d8b3df5b913df9e6ac02b68d31324cd49af5c4565529, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x0c4cb9dc3c4fd8174f1149b3c63c3c2f9ecb827cd7dc25534ff8fb75bc79c502, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x174ad61a1448c899a25416474f4930301e5c49475279e0639a616ddc45bc7b54, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1a96177bcf4d8d89f759df4ec2f3cde2eaaa28c177cc0fa13a9816d49a38d2ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x066d04b24331d71cd0ef8054bc60c4ff05202c126a233c1a8242ace360b8a30a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x2a4c4fc6ec0b0cf52195782871c6dd3b381cc65f72e02ad527037a62aa1bd804, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x13ab2d136ccf37d447e9f2e14a7cedc95e727f8446f6d9d7e55afc01219fd649, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1121552fca26061619d24d843dc82769c1b04fcec26f55194c2e3e869acc6a9a, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x00ef653322b13d6c889bc81715c37d77a6cd267d595c4a8909a5546c7c97cff1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0e25483e45a665208b261d8ba74051e6400c776d652595d9845aca35d8a397d3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x29f536dcb9dd7682245264659e15d88e395ac3d4dde92d8c46448db979eeba89, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x2a56ef9f2c53febadfda33575dbdbd885a124e2780bbea170e456baace0fa5be, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1c8361c78eb5cf5decfb7a2d17b5c409f2ae2999a46762e8ee416240a8cb9af1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x151aff5f38b20a0fc0473089aaf0206b83e8e68a764507bfd3d0ab4be74319c5, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x04c6187e41ed881dc1b239c88f7f9d43a9f52fc8c8b6cdd1e76e47615b51f100, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x13b37bd80f4d27fb10d84331f6fb6d534b81c61ed15776449e801b7ddc9c2967, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x01a5c536273c2d9df578bfbd32c17b7a2ce3664c2a52032c9321ceb1c4e8a8e4, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x2ab3561834ca73835ad05f5d7acb950b4a9a2c666b9726da832239065b7c3b02, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1d4d8ec291e720db200fe6d686c0d613acaf6af4e95d3bf69f7ed516a597b646, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x041294d2cc484d228f5784fe7919fd2bb925351240a04b711514c9c80b65af1d, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x154ac98e01708c611c4fa715991f004898f57939d126e392042971dd90e81fc6, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0b339d8acca7d4f83eedd84093aef51050b3684c88f8b0b04524563bc6ea4da4, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x0955e49e6610c94254a4f84cfbab344598f0e71eaff4a7dd81ed95b50839c82e, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x06746a6156eba54426b9e22206f15abca9a6f41e6f535c6f3525401ea0654626, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0f18f5a0ecd1423c496f3820c549c27838e5790e2bd0a196ac917c7ff32077fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x04f6eeca1751f7308ac59eff5beb261e4bb563583ede7bc92a738223d6f76e13, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2b56973364c4c4f5c1a3ec4da3cdce038811eb116fb3e45bc1768d26fc0b3758, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x123769dd49d5b054dcd76b89804b1bcb8e1392b385716a5d83feb65d437f29ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2147b424fc48c80a88ee52b91169aacea989f6446471150994257b2fb01c63e9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x0fdc1f58548b85701a6c5505ea332a29647e6f34ad4243c2ea54ad897cebe54d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x12373a8251fea004df68abcf0f7786d4bceff28c5dbbe0c3944f685cc0a0b1f2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x21e4f4ea5f35f85bad7ea52ff742c9e8a642756b6af44203dd8a1f35c1a90035, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x16243916d69d2ca3dfb4722224d4c462b57366492f45e90d8a81934f1bc3b147, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1efbe46dd7a578b4f66f9adbc88b4378abc21566e1a0453ca13a4159cac04ac2, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x07ea5e8537cf5dd08886020e23a7f387d468d5525be66f853b672cc96a88969a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x05a8c4f9968b8aa3b7b478a30f9a5b63650f19a75e7ce11ca9fe16c0b76c00bc, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x20f057712cc21654fbfe59bd345e8dac3f7818c701b9c7882d9d57b72a32e83f, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x04a12ededa9dfd689672f8c67fee31636dcd8e88d01d49019bd90b33eb33db69, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x27e88d8c15f37dcee44f1e5425a51decbd136ce5091a6767e49ec9544ccd101a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2feed17b84285ed9b8a5c8c5e95a41f66e096619a7703223176c41ee433de4d1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x1ed7cc76edf45c7c404241420f729cf394e5942911312a0d6972b8bd53aff2b8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x15742e99b9bfa323157ff8c586f5660eac6783476144cdcadf2874be45466b1a, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1aac285387f65e82c895fc6887ddf40577107454c6ec0317284f033f27d0c785, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x25851c3c845d4790f9ddadbdb6057357832e2e7a49775f71ec75a96554d67c77, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x15a5821565cc2ec2ce78457db197edf353b7ebba2c5523370ddccc3d9f146a67, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2411d57a4813b9980efa7e31a1db5966dcf64f36044277502f15485f28c71727, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x002e6f8d6520cd4713e335b8c0b6d2e647e9a98e12f4cd2558828b5ef6cb4c9b, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x2ff7bc8f4380cde997da00b616b0fcd1af8f0e91e2fe1ed7398834609e0315d2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x00b9831b948525595ee02724471bcd182e9521f6b7bb68f1e93be4febb0d3cbe, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x0a2f53768b8ebf6a86913b0e57c04e011ca408648a4743a87d77adbf0c9c3512, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x00248156142fd0373a479f91ff239e960f599ff7e94be69b7f2a290305e1198d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x171d5620b87bfb1328cf8c02ab3f0c9a397196aa6a542c2350eb512a2b2bcda9, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x170a4f55536f7dc970087c7c10d6fad760c952172dd54dd99d1045e4ec34a808, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x29aba33f799fe66c2ef3134aea04336ecc37e38c1cd211ba482eca17e2dbfae1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1e9bc179a4fdd758fdd1bb1945088d47e70d114a03f6a0e8b5ba650369e64973, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1dd269799b660fad58f7f4892dfb0b5afeaad869a9c4b44f9c9e1c43bdaf8f09, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x22cdbc8b70117ad1401181d02e15459e7ccd426fe869c7c95d1dd2cb0f24af38, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x0ef042e454771c533a9f57a55c503fcefd3150f52ed94a7cd5ba93b9c7dacefd, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x11609e06ad6c8fe2f287f3036037e8851318e8b08a0359a03b304ffca62e8284, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x1166d9e554616dba9e753eea427c17b7fecd58c076dfe42708b08f5b783aa9af, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x2de52989431a859593413026354413db177fbf4cd2ac0b56f855a888357ee466, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x3006eb4ffc7a85819a6da492f3a8ac1df51aee5b17b8e89d74bf01cf5f71e9ad, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2af41fbb61ba8a80fdcf6fff9e3f6f422993fe8f0a4639f962344c8225145086, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x119e684de476155fe5a6b41a8ebc85db8718ab27889e85e781b214bace4827c3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x1835b786e2e8925e188bea59ae363537b51248c23828f047cff784b97b3fd800, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x28201a34c594dfa34d794996c6433a20d152bac2a7905c926c40e285ab32eeb6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x083efd7a27d1751094e80fefaf78b000864c82eb571187724a761f88c22cc4e7, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x0b6f88a3577199526158e61ceea27be811c16df7774dd8519e079564f61fd13b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x0ec868e6d15e51d9644f66e1d6471a94589511ca00d29e1014390e6ee4254f5b, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2af33e3f866771271ac0c9b3ed2e1142ecd3e74b939cd40d00d937ab84c98591, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x0b520211f904b5e7d09b5d961c6ace7734568c547dd6858b364ce5e47951f178, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x0b2d722d0919a1aad8db58f10062a92ea0c56ac4270e822cca228620188a1d40, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x1f790d4d7f8cf094d980ceb37c2453e957b54a9991ca38bbe0061d1ed6e562d4, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x0171eb95dfbf7d1eaea97cd385f780150885c16235a2a6a8da92ceb01e504233, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x0c2d0e3b5fd57549329bf6885da66b9b790b40defd2c8650762305381b168873, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1162fb28689c27154e5a8228b4e72b377cbcafa589e283c35d3803054407a18d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2f1459b65dee441b64ad386a91e8310f282c5a92a89e19921623ef8249711bc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x1e6ff3216b688c3d996d74367d5cd4c1bc489d46754eb712c243f70d1b53cfbb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x01ca8be73832b8d0681487d27d157802d741a6f36cdc2a0576881f9326478875, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1f7735706ffe9fc586f976d5bdf223dc680286080b10cea00b9b5de315f9650e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2522b60f4ea3307640a0c2dce041fba921ac10a3d5f096ef4745ca838285f019, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x23f0bee001b1029d5255075ddc957f833418cad4f52b6c3f8ce16c235572575b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2bc1ae8b8ddbb81fcaac2d44555ed5685d142633e9df905f66d9401093082d59, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x0f9406b8296564a37304507b8dba3ed162371273a07b1fc98011fcd6ad72205f, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x2360a8eb0cc7defa67b72998de90714e17e75b174a52ee4acb126c8cd995f0a8, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x15871a5cddead976804c803cbaef255eb4815a5e96df8b006dcbbc2767f88948, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x193a56766998ee9e0a8652dd2f3b1da0362f4f54f72379544f957ccdeefb420f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x2a394a43934f86982f9be56ff4fab1703b2e63c8ad334834e4309805e777ae0f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x1859954cfeb8695f3e8b635dcb345192892cd11223443ba7b4166e8876c0d142, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x04e1181763050e58013444dbcb99f1902b11bc25d90bbdca408d3819f4fed32b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0fdb253dee83869d40c335ea64de8c5bb10eb82db08b5e8b1f5e5552bfd05f23, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x058cbe8a9a5027bdaa4efb623adead6275f08686f1c08984a9d7c5bae9b4f1c0, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x1382edce9971e186497eadb1aeb1f52b23b4b83bef023ab0d15228b4cceca59a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x03464990f045c6ee0819ca51fd11b0be7f61b8eb99f14b77e1e6634601d9e8b5, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x23f7bfc8720dc296fff33b41f98ff83c6fcab4605db2eb5aaa5bc137aeb70a58, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x0a59a158e3eec2117e6e94e7f0e9decf18c3ffd5e1531a9219636158bbaf62f2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x06ec54c80381c052b58bf23b312ffd3ce2c4eba065420af8f4c23ed0075fd07b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x118872dc832e0eb5476b56648e867ec8b09340f7a7bcb1b4962f0ff9ed1f9d01, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x13d69fa127d834165ad5c7cba7ad59ed52e0b0f0e42d7fea95e1906b520921b1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x169a177f63ea681270b1c6877a73d21bde143942fb71dc55fd8a49f19f10c77b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x04ef51591c6ead97ef42f287adce40d93abeb032b922f66ffb7e9a5a7450544d, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x256e175a1dc079390ecd7ca703fb2e3b19ec61805d4f03ced5f45ee6dd0f69ec, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x30102d28636abd5fe5f2af412ff6004f75cc360d3205dd2da002813d3e2ceeb2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x10998e42dfcd3bbf1c0714bc73eb1bf40443a3fa99bef4a31fd31be182fcc792, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x193edd8e9fcf3d7625fa7d24b598a1d89f3362eaf4d582efecad76f879e36860, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x18168afd34f2d915d0368ce80b7b3347d1c7a561ce611425f2664d7aa51f0b5d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x29383c01ebd3b6ab0c017656ebe658b6a328ec77bc33626e29e2e95b33ea6111, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x10646d2f2603de39a1f4ae5e7771a64a702db6e86fb76ab600bf573f9010c711, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0beb5e07d1b27145f575f1395a55bf132f90c25b40da7b3864d0242dcb1117fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x16d685252078c133dc0d3ecad62b5c8830f95bb2e54b59abdffbf018d96fa336, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x0a6abd1d833938f33c74154e0404b4b40a555bbbec21ddfafd672dd62047f01a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1a679f5d36eb7b5c8ea12a4c2dedc8feb12dffeec450317270a6f19b34cf1860, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x0980fb233bd456c23974d50e0ebfde4726a423eada4e8f6ffbc7592e3f1b93d6, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x161b42232e61b84cbf1810af93a38fc0cece3d5628c9282003ebacb5c312c72b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0ada10a90c7f0520950f7d47a60d5e6a493f09787f1564e5d09203db47de1a0b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1a730d372310ba82320345a29ac4238ed3f07a8a2b4e121bb50ddb9af407f451, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x2c8120f268ef054f817064c369dda7ea908377feaba5c4dffbda10ef58e8c556, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1c7c8824f758753fa57c00789c684217b930e95313bcb73e6e7b8649a4968f70, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x2cd9ed31f5f8691c8e39e4077a74faa0f400ad8b491eb3f7b47b27fa3fd1cf77, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x23ff4f9d46813457cf60d92f57618399a5e022ac321ca550854ae23918a22eea, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x09945a5d147a4f66ceece6405dddd9d0af5a2c5103529407dff1ea58f180426d, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x188d9c528025d4c2b67660c6b771b90f7c7da6eaa29d3f268a6dd223ec6fc630, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x3050e37996596b7f81f68311431d8734dba7d926d3633595e0c0d8ddf4f0f47f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x15af1169396830a91600ca8102c35c426ceae5461e3f95d89d829518d30afd78, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x1da6d09885432ea9a06d9f37f873d985dae933e351466b2904284da3320d8acc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := add(0x2796ea90d269af29f5f8acf33921124e4e4fad3dbe658945e546ee411ddaa9cb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x202d7dd1da0f6b4b0325c8b3307742f01e15612ec8e9304a7cb0319e01d32d60, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x096d6790d05bb759156a952ba263d672a2d7f9c788f4c831a29dace4c0f8be5f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := add(0x054efa1f65b0fce283808965275d877b438da23ce5b13e1963798cb1447d25a4, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x1b162f83d917e93edb3308c29802deb9d8aa690113b2e14864ccf6e18e4165f1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x21e5241e12564dd6fd9f1cdd2a0de39eedfefc1466cc568ec5ceb745a0506edc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := mulmod(scratch1, scratch1, F)\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\n state0 := mulmod(scratch2, scratch2, F)\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\n state0 := add(0x1cfb5662e8cf5ac9226a80ee17b36abecb73ab5f87e161927b4349e10e4bdf08, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x0f21177e302a771bbae6d8d1ecb373b62c99af346220ac0129c53f666eb24100, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1671522374606992affb0dd7f71b12bec4236aede6290546bcef7e1f515c2320, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := mulmod(state1, state1, F)\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\n scratch0 := mulmod(state2, state2, F)\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\n scratch0 := add(0x0fa3ec5b9488259c2eb4cf24501bfad9be2ec9e42c5cc8ccd419d2a692cad870, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)))\n scratch1 := add(0x193c0e04e0bd298357cb266c1506080ed36edce85c648cc085e8c57b1ab54bba, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F)))\n scratch2 := add(0x102adf8ef74735a27e9128306dcbc3c99f6f7291cd406578ce14ea2adaba68f8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F)))\n state0 := mulmod(scratch0, scratch0, F)\n scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F)\n state0 := mulmod(scratch1, scratch1, F)\n scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F)\n state0 := mulmod(scratch2, scratch2, F)\n scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F)\n state0 := add(0x0fe0af7858e49859e2a54d6f1ad945b1316aa24bfbdd23ae40a6d0cb70c3eab1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F)))\n state1 := add(0x216f6717bbc7dedb08536a2220843f4e2da5f1daa9ebdefde8a5ea7344798d22, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F)))\n state2 := add(0x1da55cc900f0d21f4a3e694391918a1b3c23b2ac773c6b3ef88e2e4228325161, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F)))\n scratch0 := mulmod(state0, state0, F)\n state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F)\n scratch0 := mulmod(state1, state1, F)\n state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F)\n scratch0 := mulmod(state2, state2, F)\n state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F)\n\n mstore(0x0, mod(add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)), F))\n\n return(0, 0x20)\n }\n }\n}\n"
|
|
242
248
|
},
|
|
249
|
+
"project/contracts/E3RefundManager.sol": {
|
|
250
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { IE3RefundManager } from \"./interfaces/IE3RefundManager.sol\";\nimport { IEnclave } from \"./interfaces/IEnclave.sol\";\nimport { IBondingRegistry } from \"./interfaces/IBondingRegistry.sol\";\n\n/**\n * @title E3RefundManager\n * @notice Manages refund distribution for failed E3 computations\n * @dev Implements fault-attribution based refund system\n *\n */\ncontract E3RefundManager is IE3RefundManager, OwnableUpgradeable {\n using SafeERC20 for IERC20;\n ////////////////////////////////////////////////////////////\n // //\n // Storage Variables //\n // //\n ////////////////////////////////////////////////////////////\n /// @notice The Enclave contract (contains lifecycle functionality)\n IEnclave public enclave;\n /// @notice The fee token used for payments\n IERC20 public feeToken;\n /// @notice The bonding registry for node rewards\n IBondingRegistry public bondingRegistry;\n /// @notice Protocol treasury for protocol fee collection\n address public treasury;\n /// @notice Work value allocation configuration\n WorkValueAllocation internal _workAllocation;\n /// @notice Maps E3 ID to refund distribution\n mapping(uint256 e3Id => RefundDistribution) internal _distributions;\n /// @notice Tracks claims per E3 per address\n mapping(uint256 e3Id => mapping(address => bool)) internal _claimed;\n /// @notice Maps E3 ID to honest node addresses\n mapping(uint256 e3Id => address[]) internal _honestNodes;\n ////////////////////////////////////////////////////////////\n // //\n // Modifiers //\n // //\n ////////////////////////////////////////////////////////////\n /// @notice Restricts function to Enclave contract only\n modifier onlyEnclave() {\n if (msg.sender != address(enclave)) revert Unauthorized();\n _;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Initialization //\n // //\n ////////////////////////////////////////////////////////////\n /// @notice Constructor that disables initializers\n constructor() {\n _disableInitializers();\n }\n\n /// @notice Initializes the E3RefundManager contract\n /// @param _owner The owner address\n /// @param _enclave The Enclave contract address\n /// @param _treasury The protocol treasury address\n function initialize(\n address _owner,\n address _enclave,\n address _treasury\n ) public initializer {\n __Ownable_init(msg.sender);\n\n require(_enclave != address(0), \"Invalid enclave\");\n require(_treasury != address(0), \"Invalid treasury\");\n\n enclave = IEnclave(_enclave);\n feeToken = enclave.feeToken();\n bondingRegistry = enclave.bondingRegistry();\n treasury = _treasury;\n\n _workAllocation = WorkValueAllocation({\n committeeFormationBps: 1000,\n dkgBps: 3000,\n decryptionBps: 5500,\n protocolBps: 500\n });\n\n if (_owner != owner()) transferOwnership(_owner);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Refund Calculation //\n // //\n ////////////////////////////////////////////////////////////\n /// @inheritdoc IE3RefundManager\n function calculateRefund(\n uint256 e3Id,\n uint256 originalPayment,\n address[] calldata honestNodes\n ) external onlyEnclave {\n require(!_distributions[e3Id].calculated, \"Already calculated\");\n require(originalPayment > 0, \"No payment\");\n\n // Calculate work value based on stage\n IEnclave.E3Stage failedAt = _getFailedAtStage(e3Id);\n (uint16 workCompletedBps, uint16 workRemainingBps) = calculateWorkValue(\n failedAt\n );\n\n // Calculate base distribution\n uint256 honestNodeAmount = (originalPayment * workCompletedBps) / 10000;\n uint256 requesterAmount = (originalPayment * workRemainingBps) / 10000;\n uint256 protocolAmount = originalPayment -\n honestNodeAmount -\n requesterAmount;\n\n // Store distribution\n _distributions[e3Id] = RefundDistribution({\n requesterAmount: requesterAmount,\n honestNodeAmount: honestNodeAmount,\n protocolAmount: protocolAmount,\n totalSlashed: 0,\n honestNodeCount: honestNodes.length,\n calculated: true\n });\n\n // Store honest nodes\n for (uint256 i = 0; i < honestNodes.length; i++) {\n _honestNodes[e3Id].push(honestNodes[i]);\n }\n\n // Transfer protocol fee to treasury immediately\n if (protocolAmount > 0) {\n feeToken.safeTransfer(treasury, protocolAmount);\n }\n\n emit RefundDistributionCalculated(\n e3Id,\n requesterAmount,\n honestNodeAmount,\n protocolAmount,\n 0\n );\n }\n\n /// @notice Get the stage at which E3 failed (for work calculation)\n function _getFailedAtStage(\n uint256 e3Id\n ) internal view returns (IEnclave.E3Stage) {\n IEnclave.FailureReason reason = enclave.getFailureReason(e3Id);\n\n // Map failure reason to stage\n if (\n reason == IEnclave.FailureReason.CommitteeFormationTimeout ||\n reason == IEnclave.FailureReason.InsufficientCommitteeMembers\n ) {\n return IEnclave.E3Stage.Requested;\n }\n if (\n reason == IEnclave.FailureReason.DKGTimeout ||\n reason == IEnclave.FailureReason.DKGInvalidShares\n ) {\n return IEnclave.E3Stage.CommitteeFinalized;\n }\n if (reason == IEnclave.FailureReason.NoInputsReceived) {\n return IEnclave.E3Stage.KeyPublished;\n }\n if (\n reason == IEnclave.FailureReason.ComputeTimeout ||\n reason == IEnclave.FailureReason.ComputeProviderExpired ||\n reason == IEnclave.FailureReason.ComputeProviderFailed ||\n reason == IEnclave.FailureReason.RequesterCancelled\n ) {\n return IEnclave.E3Stage.KeyPublished;\n }\n if (\n reason == IEnclave.FailureReason.DecryptionTimeout ||\n reason == IEnclave.FailureReason.DecryptionInvalidShares ||\n reason == IEnclave.FailureReason.VerificationFailed\n ) {\n return IEnclave.E3Stage.CiphertextReady;\n }\n\n return IEnclave.E3Stage.None;\n }\n\n /// @inheritdoc IE3RefundManager\n function calculateWorkValue(\n IEnclave.E3Stage stage\n ) public view returns (uint16 workCompletedBps, uint16 workRemainingBps) {\n WorkValueAllocation memory alloc = _workAllocation;\n\n if (\n stage == IEnclave.E3Stage.Requested ||\n stage == IEnclave.E3Stage.None\n ) {\n // Failed at Requested = no work done\n workCompletedBps = 0;\n } else if (stage == IEnclave.E3Stage.CommitteeFinalized) {\n // Failed during DKG = sortition work done\n workCompletedBps = alloc.committeeFormationBps;\n } else if (stage == IEnclave.E3Stage.KeyPublished) {\n // Failed during input phase = sortition + DKG done (no additional work)\n workCompletedBps = alloc.committeeFormationBps + alloc.dkgBps;\n } else if (stage == IEnclave.E3Stage.CiphertextReady) {\n // Failed during decryption = sortition + DKG done (awaiting decryption shares)\n workCompletedBps = alloc.committeeFormationBps + alloc.dkgBps;\n }\n\n workRemainingBps = 10000 - workCompletedBps - alloc.protocolBps;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Claiming Functions //\n // //\n ////////////////////////////////////////////////////////////\n /// @inheritdoc IE3RefundManager\n function claimRequesterRefund(\n uint256 e3Id\n ) external returns (uint256 amount) {\n RefundDistribution storage dist = _distributions[e3Id];\n if (!dist.calculated) revert RefundNotCalculated(e3Id);\n\n address requester = enclave.getRequester(e3Id);\n if (msg.sender != requester) revert NotRequester(e3Id, msg.sender);\n\n if (_claimed[e3Id][msg.sender]) revert AlreadyClaimed(e3Id, msg.sender);\n\n amount = dist.requesterAmount;\n if (amount == 0) revert NoRefundAvailable(e3Id);\n\n _claimed[e3Id][msg.sender] = true;\n\n feeToken.safeTransfer(msg.sender, amount);\n\n emit RefundClaimed(e3Id, msg.sender, amount, \"REQUESTER\");\n }\n\n /// @inheritdoc IE3RefundManager\n function claimHonestNodeReward(\n uint256 e3Id\n ) external returns (uint256 amount) {\n RefundDistribution storage dist = _distributions[e3Id];\n require(dist.calculated, RefundNotCalculated(e3Id));\n require(!_claimed[e3Id][msg.sender], AlreadyClaimed(e3Id, msg.sender));\n\n // Check if caller is honest node\n address[] memory nodes = _honestNodes[e3Id];\n bool isHonest = false;\n for (uint256 i = 0; i < nodes.length && !isHonest; i++) {\n isHonest = (nodes[i] == msg.sender);\n }\n require(isHonest, NotHonestNode(e3Id, msg.sender));\n\n require(dist.honestNodeCount > 0, NoRefundAvailable(e3Id));\n amount = dist.honestNodeAmount / dist.honestNodeCount;\n require(amount > 0, NoRefundAvailable(e3Id));\n\n _claimed[e3Id][msg.sender] = true;\n\n // Distribute reward through bonding registry\n feeToken.approve(address(bondingRegistry), amount);\n\n address[] memory nodeArray = new address[](1);\n nodeArray[0] = msg.sender;\n uint256[] memory amountArray = new uint256[](1);\n amountArray[0] = amount;\n\n bondingRegistry.distributeRewards(feeToken, nodeArray, amountArray);\n\n emit RefundClaimed(e3Id, msg.sender, amount, \"HONEST_NODE\");\n }\n\n /// @inheritdoc IE3RefundManager\n function routeSlashedFunds(\n uint256 e3Id,\n uint256 amount\n ) external onlyEnclave {\n RefundDistribution storage dist = _distributions[e3Id];\n require(dist.calculated, \"Not calculated\");\n\n // Add slashed funds to distribution\n // Note: slashing should be finalized before claims are made.\n // 50% to requester, 50% to honest nodes for non-participation\n uint256 toRequester = amount / 2;\n uint256 toHonestNodes = amount - toRequester;\n\n dist.requesterAmount += toRequester;\n dist.honestNodeAmount += toHonestNodes;\n dist.totalSlashed += amount;\n\n emit SlashedFundsRouted(e3Id, amount);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // View Functions //\n // //\n ////////////////////////////////////////////////////////////\n /// @inheritdoc IE3RefundManager\n function getRefundDistribution(\n uint256 e3Id\n ) external view returns (RefundDistribution memory) {\n return _distributions[e3Id];\n }\n\n /// @inheritdoc IE3RefundManager\n function hasClaimed(\n uint256 e3Id,\n address claimant\n ) external view returns (bool) {\n return _claimed[e3Id][claimant];\n }\n\n /// @inheritdoc IE3RefundManager\n function getWorkAllocation()\n external\n view\n returns (WorkValueAllocation memory)\n {\n return _workAllocation;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Admin Functions //\n // //\n ////////////////////////////////////////////////////////////\n /// @inheritdoc IE3RefundManager\n function setWorkAllocation(\n WorkValueAllocation calldata allocation\n ) external onlyOwner {\n uint256 total = uint256(allocation.committeeFormationBps) +\n uint256(allocation.dkgBps) +\n uint256(allocation.decryptionBps) +\n uint256(allocation.protocolBps);\n require(total == 10000, \"Must sum to 10000\");\n\n _workAllocation = allocation;\n\n emit WorkAllocationUpdated(allocation);\n }\n\n /// @notice Set the Enclave contract address\n /// @param _enclave New Enclave address\n function setEnclave(address _enclave) external onlyOwner {\n require(_enclave != address(0), \"Invalid enclave\");\n enclave = IEnclave(_enclave);\n }\n\n /// @notice Set the treasury address\n /// @param _treasury New treasury address\n function setTreasury(address _treasury) external onlyOwner {\n require(_treasury != address(0), \"Invalid treasury\");\n treasury = _treasury;\n }\n}\n"
|
|
251
|
+
},
|
|
243
252
|
"project/contracts/Enclave.sol": {
|
|
244
|
-
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { IEnclave, E3, IE3Program } from \"./interfaces/IEnclave.sol\";\nimport { ICiphernodeRegistry } from \"./interfaces/ICiphernodeRegistry.sol\";\nimport { IBondingRegistry } from \"./interfaces/IBondingRegistry.sol\";\nimport { IDecryptionVerifier } from \"./interfaces/IDecryptionVerifier.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Enclave\n * @notice Main contract for managing Encrypted Execution Environments (E3)\n * @dev Coordinates E3 lifecycle including request, activation, input publishing, and output verification\n */\ncontract Enclave is IEnclave, OwnableUpgradeable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////////////\n // //\n // Storage Variables //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Address of the Ciphernode Registry contract.\n /// @dev Manages the pool of ciphernodes and committee selection.\n ICiphernodeRegistry public ciphernodeRegistry;\n\n /// @notice Address of the Bonding Registry contract.\n /// @dev Handles staking and reward distribution for ciphernodes.\n IBondingRegistry public bondingRegistry;\n\n /// @notice Address of the ERC20 token used for E3 fees.\n /// @dev All E3 request fees must be paid in this token.\n IERC20 public feeToken;\n\n /// @notice Maximum allowed duration for an E3 computation in seconds.\n /// @dev Requests with duration exceeding this value will be rejected.\n uint256 public maxDuration;\n\n /// @notice ID counter for the next E3 to be created.\n /// @dev Incremented after each successful E3 request.\n uint256 public nexte3Id;\n\n /// @notice Mapping of allowed E3 Programs.\n /// @dev Only enabled E3 Programs can be used in computation requests.\n mapping(IE3Program e3Program => bool allowed) public e3Programs;\n\n /// @notice Mapping storing all E3 instances by their ID.\n /// @dev Contains the full state and configuration of each E3.\n mapping(uint256 e3Id => E3 e3) public e3s;\n\n /// @notice Mapping of enabled encryption schemes to their decryption verifiers.\n /// @dev Each encryption scheme ID maps to a contract that can verify decrypted outputs.\n mapping(bytes32 encryptionSchemeId => IDecryptionVerifier decryptionVerifier)\n public decryptionVerifiers;\n\n /// @notice Mapping storing valid E3 program ABI encoded parameter sets.\n /// @dev Stores allowed encryption scheme parameters (e.g., BFV parameters).\n mapping(bytes e3ProgramParams => bool allowed) public e3ProgramsParams;\n\n /// @notice Mapping tracking fee payments for each E3.\n /// @dev Stores the amount paid for an E3, distributed to committee upon completion.\n mapping(uint256 e3Id => uint256 e3Payment) public e3Payments;\n\n ////////////////////////////////////////////////////////////\n // //\n // Errors //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Thrown when committee selection fails during E3 request or activation.\n error CommitteeSelectionFailed();\n\n /// @notice Thrown when an E3 request uses a program that is not enabled.\n /// @param e3Program The E3 program address that is not allowed.\n error E3ProgramNotAllowed(IE3Program e3Program);\n\n /// @notice Thrown when attempting to activate an E3 that is already activated.\n /// @param e3Id The ID of the E3 that is already activated.\n error E3AlreadyActivated(uint256 e3Id);\n\n /// @notice Thrown when the E3 start window or computation period has expired.\n error E3Expired();\n\n /// @notice Thrown when attempting operations on an E3 that has not been activated yet.\n /// @param e3Id The ID of the E3 that is not activated.\n error E3NotActivated(uint256 e3Id);\n\n /// @notice Thrown when attempting to activate an E3 before its start window begins.\n error E3NotReady();\n\n /// @notice Thrown when attempting to access an E3 that does not exist.\n /// @param e3Id The ID of the non-existent E3.\n error E3DoesNotExist(uint256 e3Id);\n\n /// @notice Thrown when attempting to enable a module or program that is already enabled.\n /// @param module The address of the module that is already enabled.\n error ModuleAlreadyEnabled(address module);\n\n /// @notice Thrown when attempting to disable a module or program that is not enabled.\n /// @param module The address of the module that is not enabled.\n error ModuleNotEnabled(address module);\n\n /// @notice Thrown when an invalid or disabled encryption scheme is used.\n /// @param encryptionSchemeId The ID of the invalid encryption scheme.\n error InvalidEncryptionScheme(bytes32 encryptionSchemeId);\n\n /// @notice Thrown when attempting to publish input after the computation deadline has passed.\n /// @param e3Id The ID of the E3.\n /// @param expiration The expiration timestamp that has passed.\n error InputDeadlinePassed(uint256 e3Id, uint256 expiration);\n\n /// @notice Thrown when attempting to publish output before the input deadline has passed.\n /// @param e3Id The ID of the E3.\n /// @param expiration The expiration timestamp that has not yet passed.\n error InputDeadlineNotPassed(uint256 e3Id, uint256 expiration);\n\n /// @notice Thrown when attempting to set an invalid ciphernode registry address.\n /// @param ciphernodeRegistry The invalid ciphernode registry address.\n error InvalidCiphernodeRegistry(ICiphernodeRegistry ciphernodeRegistry);\n\n /// @notice Thrown when the requested duration exceeds maxDuration or is zero.\n /// @param duration The invalid duration value.\n error InvalidDuration(uint256 duration);\n\n /// @notice Thrown when output verification fails.\n /// @param output The invalid output data.\n error InvalidOutput(bytes output);\n\n /// @notice Thrown when input data is invalid.\n error InvalidInput();\n\n /// @notice Thrown when the start window parameters are invalid.\n error InvalidStartWindow();\n\n /// @notice Thrown when the threshold parameters are invalid (e.g., M > N or M = 0).\n /// @param threshold The invalid threshold array [M, N].\n error InvalidThreshold(uint32[2] threshold);\n\n /// @notice Thrown when attempting to publish ciphertext output that has already been published.\n /// @param e3Id The ID of the E3.\n error CiphertextOutputAlreadyPublished(uint256 e3Id);\n\n /// @notice Thrown when attempting to publish plaintext output before ciphertext output.\n /// @param e3Id The ID of the E3.\n error CiphertextOutputNotPublished(uint256 e3Id);\n\n /// @notice Thrown when payment is required but not provided or insufficient.\n /// @param value The required payment amount.\n error PaymentRequired(uint256 value);\n\n /// @notice Thrown when attempting to publish plaintext output that has already been published.\n /// @param e3Id The ID of the E3.\n error PlaintextOutputAlreadyPublished(uint256 e3Id);\n\n /// @notice Thrown when attempting to set an invalid bonding registry address.\n /// @param bondingRegistry The invalid bonding registry address.\n error InvalidBondingRegistry(IBondingRegistry bondingRegistry);\n\n /// @notice Thrown when attempting to set an invalid fee token address.\n /// @param feeToken The invalid fee token address.\n error InvalidFeeToken(IERC20 feeToken);\n\n ////////////////////////////////////////////////////////////\n // //\n // Initialization //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Constructor that disables initializers.\n /// @dev Prevents the implementation contract from being initialized. Initialization is performed\n /// via the initialize() function when deployed behind a proxy.\n constructor() {\n _disableInitializers();\n }\n\n /// @notice Initializes the Enclave contract with initial configuration.\n /// @dev This function can only be called once due to the initializer modifier. Sets up core dependencies.\n /// @param _owner The owner address of this contract.\n /// @param _ciphernodeRegistry The address of the Ciphernode Registry contract.\n /// @param _bondingRegistry The address of the Bonding Registry contract.\n /// @param _feeToken The address of the ERC20 token used for E3 fees.\n /// @param _maxDuration The maximum duration of a computation in seconds.\n /// @param _e3ProgramsParams Array of ABI encoded E3 encryption scheme parameters sets (e.g., for BFV).\n function initialize(\n address _owner,\n ICiphernodeRegistry _ciphernodeRegistry,\n IBondingRegistry _bondingRegistry,\n IERC20 _feeToken,\n uint256 _maxDuration,\n bytes[] memory _e3ProgramsParams\n ) public initializer {\n __Ownable_init(msg.sender);\n setMaxDuration(_maxDuration);\n setCiphernodeRegistry(_ciphernodeRegistry);\n setBondingRegistry(_bondingRegistry);\n setFeeToken(_feeToken);\n setE3ProgramsParams(_e3ProgramsParams);\n if (_owner != owner()) transferOwnership(_owner);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Core Entrypoints //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @inheritdoc IEnclave\n function request(\n E3RequestParams calldata requestParams\n ) external returns (uint256 e3Id, E3 memory e3) {\n uint256 e3Fee = getE3Quote(requestParams);\n require(\n requestParams.threshold[1] >= requestParams.threshold[0] &&\n requestParams.threshold[0] > 0,\n InvalidThreshold(requestParams.threshold)\n );\n require(\n // TODO: do we need a minimum start window to allow time for committee selection?\n requestParams.startWindow[1] >= requestParams.startWindow[0] &&\n requestParams.startWindow[1] >= block.timestamp,\n InvalidStartWindow()\n );\n require(\n requestParams.duration > 0 && requestParams.duration <= maxDuration,\n InvalidDuration(requestParams.duration)\n );\n require(\n e3Programs[requestParams.e3Program],\n E3ProgramNotAllowed(requestParams.e3Program)\n );\n\n // TODO: should IDs be incremental or produced deterministically?\n e3Id = nexte3Id;\n nexte3Id++;\n uint256 seed = uint256(keccak256(abi.encode(block.prevrandao, e3Id)));\n\n e3.seed = seed;\n e3.threshold = requestParams.threshold;\n e3.requestBlock = block.number;\n e3.startWindow = requestParams.startWindow;\n e3.duration = requestParams.duration;\n e3.expiration = 0;\n e3.e3Program = requestParams.e3Program;\n e3.e3ProgramParams = requestParams.e3ProgramParams;\n e3.customParams = requestParams.customParams;\n e3.committeePublicKey = hex\"\";\n e3.ciphertextOutput = hex\"\";\n e3.plaintextOutput = hex\"\";\n e3.requester = msg.sender;\n\n bytes32 encryptionSchemeId = requestParams.e3Program.validate(\n e3Id,\n seed,\n requestParams.e3ProgramParams,\n requestParams.computeProviderParams,\n requestParams.customParams\n );\n IDecryptionVerifier decryptionVerifier = decryptionVerifiers[\n encryptionSchemeId\n ];\n\n require(\n decryptionVerifiers[encryptionSchemeId] !=\n IDecryptionVerifier(address(0)),\n InvalidEncryptionScheme(encryptionSchemeId)\n );\n\n e3.encryptionSchemeId = encryptionSchemeId;\n e3.decryptionVerifier = decryptionVerifier;\n\n e3s[e3Id] = e3;\n e3Payments[e3Id] = e3Fee;\n\n feeToken.safeTransferFrom(msg.sender, address(this), e3Fee);\n\n require(\n ciphernodeRegistry.requestCommittee(\n e3Id,\n seed,\n requestParams.threshold\n ),\n CommitteeSelectionFailed()\n );\n\n emit E3Requested(e3Id, e3, requestParams.e3Program);\n }\n\n /// @inheritdoc IEnclave\n function activate(uint256 e3Id) external returns (bool success) {\n E3 memory e3 = getE3(e3Id);\n\n require(e3.expiration == 0, E3AlreadyActivated(e3Id));\n require(e3.startWindow[0] <= block.timestamp, E3NotReady());\n // TODO: handle what happens to the payment if the start window has passed.\n require(e3.startWindow[1] >= block.timestamp, E3Expired());\n\n bytes32 publicKeyHash = ciphernodeRegistry.committeePublicKey(e3Id);\n\n uint256 expiresAt = block.timestamp + e3.duration;\n e3s[e3Id].expiration = expiresAt;\n e3s[e3Id].committeePublicKey = publicKeyHash;\n\n emit E3Activated(e3Id, expiresAt, publicKeyHash);\n\n return true;\n }\n\n /// @inheritdoc IEnclave\n function publishInput(\n uint256 e3Id,\n bytes calldata data\n ) external returns (bool success) {\n E3 memory e3 = getE3(e3Id);\n\n // Note: if we make 0 a no expiration, this has to be refactored\n require(e3.expiration > 0, E3NotActivated(e3Id));\n // TODO: should we have an input window, including both a start and end timestamp?\n require(\n e3.expiration > block.timestamp,\n InputDeadlinePassed(e3Id, e3.expiration)\n );\n\n e3.e3Program.validateInput(e3Id, msg.sender, data);\n\n success = true;\n }\n\n /// @inheritdoc IEnclave\n function publishCiphertextOutput(\n uint256 e3Id,\n bytes calldata ciphertextOutput,\n bytes calldata proof\n ) external returns (bool success) {\n E3 memory e3 = getE3(e3Id);\n\n // Note: if we make 0 a no expiration, this has to be refactored\n require(e3.expiration > 0, E3NotActivated(e3Id));\n require(\n e3.expiration <= block.timestamp,\n InputDeadlineNotPassed(e3Id, e3.expiration)\n );\n // TODO: should the output verifier be able to change its mind?\n //i.e. should we be able to call this multiple times?\n require(\n e3.ciphertextOutput == bytes32(0),\n CiphertextOutputAlreadyPublished(e3Id)\n );\n\n bytes32 ciphertextOutputHash = keccak256(ciphertextOutput);\n e3s[e3Id].ciphertextOutput = ciphertextOutputHash;\n\n (success) = e3.e3Program.verify(e3Id, ciphertextOutputHash, proof);\n require(success, InvalidOutput(ciphertextOutput));\n\n emit CiphertextOutputPublished(e3Id, ciphertextOutput);\n }\n\n /// @inheritdoc IEnclave\n function publishPlaintextOutput(\n uint256 e3Id,\n bytes calldata plaintextOutput,\n bytes calldata proof\n ) external returns (bool success) {\n E3 memory e3 = getE3(e3Id);\n\n // Note: if we make 0 a no expiration, this has to be refactored\n require(e3.expiration > 0, E3NotActivated(e3Id));\n require(\n e3.ciphertextOutput != bytes32(0),\n CiphertextOutputNotPublished(e3Id)\n );\n require(\n e3.plaintextOutput.length == 0,\n PlaintextOutputAlreadyPublished(e3Id)\n );\n\n e3s[e3Id].plaintextOutput = plaintextOutput;\n\n (success) = e3.decryptionVerifier.verify(\n e3Id,\n keccak256(plaintextOutput),\n proof\n );\n require(success, InvalidOutput(plaintextOutput));\n\n _distributeRewards(e3Id);\n\n emit PlaintextOutputPublished(e3Id, plaintextOutput);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Internal Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Distributes rewards to committee members after successful E3 completion.\n /// @dev Divides the E3 payment equally among all committee members and transfers via bonding registry.\n /// @dev Emits RewardsDistributed event upon successful distribution.\n /// @param e3Id The ID of the E3 for which to distribute rewards.\n function _distributeRewards(uint256 e3Id) internal {\n address[] memory committeeNodes = ciphernodeRegistry.getCommitteeNodes(\n e3Id\n );\n uint256 committeeLength = committeeNodes.length;\n uint256[] memory amounts = new uint256[](committeeLength);\n\n // TODO: do we need to pay different amounts to different nodes?\n // For now, we'll pay the same amount to all nodes.\n uint256 amount = e3Payments[e3Id] / committeeLength;\n for (uint256 i = 0; i < committeeLength; i++) {\n amounts[i] = amount;\n }\n\n uint256 totalAmount = e3Payments[e3Id];\n e3Payments[e3Id] = 0;\n\n feeToken.approve(address(bondingRegistry), totalAmount);\n\n bondingRegistry.distributeRewards(feeToken, committeeNodes, amounts);\n\n // TODO: decide where does dust go? Treasury maybe?\n feeToken.approve(address(bondingRegistry), 0);\n\n emit RewardsDistributed(e3Id, committeeNodes, amounts);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Set Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @inheritdoc IEnclave\n function setMaxDuration(\n uint256 _maxDuration\n ) public onlyOwner returns (bool success) {\n maxDuration = _maxDuration;\n success = true;\n emit MaxDurationSet(_maxDuration);\n }\n\n /// @inheritdoc IEnclave\n function setCiphernodeRegistry(\n ICiphernodeRegistry _ciphernodeRegistry\n ) public onlyOwner returns (bool success) {\n require(\n address(_ciphernodeRegistry) != address(0) &&\n _ciphernodeRegistry != ciphernodeRegistry,\n InvalidCiphernodeRegistry(_ciphernodeRegistry)\n );\n ciphernodeRegistry = _ciphernodeRegistry;\n success = true;\n emit CiphernodeRegistrySet(address(_ciphernodeRegistry));\n }\n\n /// @inheritdoc IEnclave\n function setBondingRegistry(\n IBondingRegistry _bondingRegistry\n ) public onlyOwner returns (bool success) {\n require(\n address(_bondingRegistry) != address(0) &&\n _bondingRegistry != bondingRegistry,\n InvalidBondingRegistry(_bondingRegistry)\n );\n bondingRegistry = _bondingRegistry;\n success = true;\n emit BondingRegistrySet(address(_bondingRegistry));\n }\n\n /// @inheritdoc IEnclave\n function setFeeToken(\n IERC20 _feeToken\n ) public onlyOwner returns (bool success) {\n require(\n address(_feeToken) != address(0) && _feeToken != feeToken,\n InvalidFeeToken(_feeToken)\n );\n feeToken = _feeToken;\n success = true;\n emit FeeTokenSet(address(_feeToken));\n }\n\n /// @inheritdoc IEnclave\n function enableE3Program(\n IE3Program e3Program\n ) public onlyOwner returns (bool success) {\n require(\n !e3Programs[e3Program],\n ModuleAlreadyEnabled(address(e3Program))\n );\n e3Programs[e3Program] = true;\n success = true;\n emit E3ProgramEnabled(e3Program);\n }\n\n /// @inheritdoc IEnclave\n function disableE3Program(\n IE3Program e3Program\n ) public onlyOwner returns (bool success) {\n require(e3Programs[e3Program], ModuleNotEnabled(address(e3Program)));\n delete e3Programs[e3Program];\n success = true;\n emit E3ProgramDisabled(e3Program);\n }\n\n /// @inheritdoc IEnclave\n function setDecryptionVerifier(\n bytes32 encryptionSchemeId,\n IDecryptionVerifier decryptionVerifier\n ) public onlyOwner returns (bool success) {\n require(\n decryptionVerifier != IDecryptionVerifier(address(0)) &&\n decryptionVerifiers[encryptionSchemeId] != decryptionVerifier,\n InvalidEncryptionScheme(encryptionSchemeId)\n );\n decryptionVerifiers[encryptionSchemeId] = decryptionVerifier;\n success = true;\n emit EncryptionSchemeEnabled(encryptionSchemeId);\n }\n\n /// @inheritdoc IEnclave\n function disableEncryptionScheme(\n bytes32 encryptionSchemeId\n ) public onlyOwner returns (bool success) {\n require(\n decryptionVerifiers[encryptionSchemeId] !=\n IDecryptionVerifier(address(0)),\n InvalidEncryptionScheme(encryptionSchemeId)\n );\n decryptionVerifiers[encryptionSchemeId] = IDecryptionVerifier(\n address(0)\n );\n success = true;\n emit EncryptionSchemeDisabled(encryptionSchemeId);\n }\n\n /// @inheritdoc IEnclave\n function setE3ProgramsParams(\n bytes[] memory _e3ProgramsParams\n ) public onlyOwner returns (bool success) {\n uint256 length = _e3ProgramsParams.length;\n for (uint256 i; i < length; ) {\n e3ProgramsParams[_e3ProgramsParams[i]] = true;\n unchecked {\n ++i;\n }\n }\n success = true;\n emit AllowedE3ProgramsParamsSet(_e3ProgramsParams);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Get Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @inheritdoc IEnclave\n function getE3(uint256 e3Id) public view returns (E3 memory e3) {\n e3 = e3s[e3Id];\n require(e3.e3Program != IE3Program(address(0)), E3DoesNotExist(e3Id));\n }\n\n /// @inheritdoc IEnclave\n function getE3Quote(\n E3RequestParams calldata\n ) public pure returns (uint256 fee) {\n fee = 1 * 10 ** 6;\n require(fee > 0, PaymentRequired(fee));\n }\n\n /// @inheritdoc IEnclave\n function getDecryptionVerifier(\n bytes32 encryptionSchemeId\n ) public view returns (IDecryptionVerifier) {\n return decryptionVerifiers[encryptionSchemeId];\n }\n}\n"
|
|
253
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { IEnclave, E3, IE3Program } from \"./interfaces/IEnclave.sol\";\nimport { ICiphernodeRegistry } from \"./interfaces/ICiphernodeRegistry.sol\";\nimport { IBondingRegistry } from \"./interfaces/IBondingRegistry.sol\";\nimport { IE3RefundManager } from \"./interfaces/IE3RefundManager.sol\";\nimport { IDecryptionVerifier } from \"./interfaces/IDecryptionVerifier.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\n/**\n * @title Enclave\n * @notice Main contract for managing Encrypted Execution Environments (E3)\n * @dev Coordinates E3 lifecycle including request, activation, input publishing, and output verification\n */\ncontract Enclave is IEnclave, OwnableUpgradeable {\n using SafeERC20 for IERC20;\n\n ////////////////////////////////////////////////////////////\n // //\n // Storage Variables //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Address of the Ciphernode Registry contract.\n /// @dev Manages the pool of ciphernodes and committee selection.\n ICiphernodeRegistry public ciphernodeRegistry;\n\n /// @notice Address of the Bonding Registry contract.\n /// @dev Handles staking and reward distribution for ciphernodes.\n IBondingRegistry public bondingRegistry;\n\n /// @notice E3 Refund Manager contract for handling failed E3 refunds.\n /// @dev Manages refund calculation and claiming for failed E3s.\n IE3RefundManager public e3RefundManager;\n\n /// @notice Address of the ERC20 token used for E3 fees.\n /// @dev All E3 request fees must be paid in this token.\n IERC20 public feeToken;\n\n /// @notice Maximum allowed duration for an E3 computation in seconds.\n /// @dev Requests with duration exceeding this value will be rejected.\n uint256 public maxDuration;\n\n /// @notice ID counter for the next E3 to be created.\n /// @dev Incremented after each successful E3 request.\n uint256 public nexte3Id;\n\n /// @notice Mapping of allowed E3 Programs.\n /// @dev Only enabled E3 Programs can be used in computation requests.\n mapping(IE3Program e3Program => bool allowed) public e3Programs;\n\n /// @notice Mapping storing all E3 instances by their ID.\n /// @dev Contains the full state and configuration of each E3.\n mapping(uint256 e3Id => E3 e3) public e3s;\n\n /// @notice Mapping of enabled encryption schemes to their decryption verifiers.\n /// @dev Each encryption scheme ID maps to a contract that can verify decrypted outputs.\n mapping(bytes32 encryptionSchemeId => IDecryptionVerifier decryptionVerifier)\n public decryptionVerifiers;\n\n /// @notice Mapping storing valid E3 program ABI encoded parameter sets.\n /// @dev Stores allowed encryption scheme parameters (e.g., BFV parameters).\n mapping(bytes e3ProgramParams => bool allowed) public e3ProgramsParams;\n\n /// @notice Mapping tracking fee payments for each E3.\n /// @dev Stores the amount paid for an E3, distributed to committee upon completion.\n mapping(uint256 e3Id => uint256 e3Payment) public e3Payments;\n\n /// @notice Maps E3 ID to its current stage\n mapping(uint256 e3Id => E3Stage) internal _e3Stages;\n\n /// @notice Maps E3 ID to its deadlines\n mapping(uint256 e3Id => E3Deadlines) internal _e3Deadlines;\n\n /// @notice Maps E3 ID to failure reason (if failed)\n mapping(uint256 e3Id => FailureReason) internal _e3FailureReasons;\n\n /// @notice Maps E3 ID to requester address\n mapping(uint256 e3Id => address) internal _e3Requesters;\n\n /// @notice Global timeout configuration\n E3TimeoutConfig internal _timeoutConfig;\n\n ////////////////////////////////////////////////////////////\n // //\n // Errors //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Thrown when committee selection fails during E3 request or activation.\n error CommitteeSelectionFailed();\n\n /// @notice Thrown when an E3 request uses a program that is not enabled.\n /// @param e3Program The E3 program address that is not allowed.\n error E3ProgramNotAllowed(IE3Program e3Program);\n\n /// @notice Thrown when attempting to access an E3 that does not exist.\n /// @param e3Id The ID of the non-existent E3.\n error E3DoesNotExist(uint256 e3Id);\n\n /// @notice Thrown when attempting to enable a module or program that is already enabled.\n /// @param module The address of the module that is already enabled.\n error ModuleAlreadyEnabled(address module);\n\n /// @notice Thrown when attempting to disable a module or program that is not enabled.\n /// @param module The address of the module that is not enabled.\n error ModuleNotEnabled(address module);\n\n /// @notice Thrown when an invalid or disabled encryption scheme is used.\n /// @param encryptionSchemeId The ID of the invalid encryption scheme.\n error InvalidEncryptionScheme(bytes32 encryptionSchemeId);\n\n /// @notice Thrown when attempting to set an invalid ciphernode registry address.\n /// @param ciphernodeRegistry The invalid ciphernode registry address.\n error InvalidCiphernodeRegistry(ICiphernodeRegistry ciphernodeRegistry);\n\n /// @notice Thrown when the requested duration exceeds maxDuration or is zero.\n /// @param duration The invalid duration value.\n error InvalidDuration(uint256 duration);\n\n /// @notice Thrown when output verification fails.\n /// @param output The invalid output data.\n error InvalidOutput(bytes output);\n\n /// @notice Thrown when the threshold parameters are invalid (e.g., M > N or M = 0).\n /// @param threshold The invalid threshold array [M, N].\n error InvalidThreshold(uint32[2] threshold);\n\n /// @notice Thrown when attempting to publish ciphertext output that has already been published.\n /// @param e3Id The ID of the E3.\n error CiphertextOutputAlreadyPublished(uint256 e3Id);\n\n /// @notice Thrown when attempting to publish plaintext output before ciphertext output.\n /// @param e3Id The ID of the E3.\n error CiphertextOutputNotPublished(uint256 e3Id);\n\n /// @notice Thrown when payment is required but not provided or insufficient.\n /// @param value The required payment amount.\n error PaymentRequired(uint256 value);\n\n /// @notice Thrown when attempting to publish plaintext output that has already been published.\n /// @param e3Id The ID of the E3.\n error PlaintextOutputAlreadyPublished(uint256 e3Id);\n\n /// @notice Thrown when attempting to set an invalid bonding registry address.\n /// @param bondingRegistry The invalid bonding registry address.\n error InvalidBondingRegistry(IBondingRegistry bondingRegistry);\n\n /// @notice Thrown when attempting to set an invalid fee token address.\n /// @param feeToken The invalid fee token address.\n error InvalidFeeToken(IERC20 feeToken);\n\n /// @notice E3 is not in expected stage\n error InvalidStage(uint256 e3Id, E3Stage expected, E3Stage actual);\n\n /// @notice E3 has already been marked as failed\n error E3AlreadyFailed(uint256 e3Id);\n\n /// @notice E3 has already completed\n error E3AlreadyComplete(uint256 e3Id);\n\n /// @notice Failure condition not yet met\n error FailureConditionNotMet(uint256 e3Id);\n\n /// @notice The Input deadline is invalid\n error InvalidInputDeadline(uint256 deadline);\n\n /// @notice The input deadline start is in the past\n error InvalidInputDeadlineStart(uint256 start);\n /// @notice The input deadline end is before the start\n error InvalidInputDeadlineEnd(uint256 end);\n\n /// @notice The duties are completed, and ciphernodes are not required to act anymore for this E3\n /// @param e3Id The ID of the E3\n /// @param expiration The expiration timestamp of the E3\n error CommitteeDutiesCompleted(uint256 e3Id, uint256 expiration);\n\n /// @notice The input deadline has not yet been reached\n /// @param e3Id The ID of the E3\n /// @param inputDeadline The input deadline timestamp of the E3\n error InputDeadlineNotReached(uint256 e3Id, uint256 inputDeadline);\n\n ////////////////////////////////////////////////////////////\n // //\n // Modifiers //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Restricts function to CiphernodeRegistry contract only\n modifier onlyCiphernodeRegistry() {\n require(\n msg.sender == address(ciphernodeRegistry),\n \"Only CiphernodeRegistry\"\n );\n _;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Initialization //\n ////////////////////////////////////////////////////////////\n\n /// @notice Constructor that disables initializers.\n /// @dev Prevents the implementation contract from being initialized. Initialization is performed\n /// via the initialize() function when deployed behind a proxy.\n constructor() {\n _disableInitializers();\n }\n\n /// @notice Initializes the Enclave contract with initial configuration.\n /// @dev This function can only be called once due to the initializer modifier. Sets up core dependencies.\n /// @param _owner The owner address of this contract.\n /// @param _ciphernodeRegistry The address of the Ciphernode Registry contract.\n /// @param _bondingRegistry The address of the Bonding Registry contract.\n /// @param _e3RefundManager The address of the E3 Refund Manager contract.\n /// @param _feeToken The address of the ERC20 token used for E3 fees.\n /// @param _maxDuration The maximum duration of a computation in seconds.\n /// @param config Initial timeout configuration for E3 lifecycle stages.\n /// @param _e3ProgramsParams Array of ABI encoded E3 encryption scheme parameters sets (e.g., for BFV).\n function initialize(\n address _owner,\n ICiphernodeRegistry _ciphernodeRegistry,\n IBondingRegistry _bondingRegistry,\n IE3RefundManager _e3RefundManager,\n IERC20 _feeToken,\n uint256 _maxDuration,\n E3TimeoutConfig calldata config,\n bytes[] memory _e3ProgramsParams\n ) public initializer {\n __Ownable_init(msg.sender);\n setMaxDuration(_maxDuration);\n setCiphernodeRegistry(_ciphernodeRegistry);\n setBondingRegistry(_bondingRegistry);\n setE3RefundManager(_e3RefundManager);\n setFeeToken(_feeToken);\n _setTimeoutConfig(config);\n setE3ProgramsParams(_e3ProgramsParams);\n if (_owner != owner()) transferOwnership(_owner);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Core Entrypoints //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @inheritdoc IEnclave\n function request(\n E3RequestParams calldata requestParams\n ) external returns (uint256 e3Id, E3 memory e3) {\n // check whether the threshold config is valid\n require(\n requestParams.threshold[1] >= requestParams.threshold[0] &&\n requestParams.threshold[0] > 0,\n InvalidThreshold(requestParams.threshold)\n );\n\n // input start date should be in the future\n require(\n requestParams.inputWindow[0] >= block.timestamp,\n // &&\n // requestParams.inputWindow[0] >= block.timestamp +\n // _timeoutConfig.dkgWindow,\n InvalidInputDeadlineStart(requestParams.inputWindow[0])\n );\n // the end of the input window should be after the start\n require(\n requestParams.inputWindow[1] >= requestParams.inputWindow[0],\n InvalidInputDeadlineEnd(requestParams.inputWindow[1])\n );\n\n // The total duration cannot be > maxDuration\n uint256 totalDuration = requestParams.inputWindow[1] -\n block.timestamp +\n _timeoutConfig.computeWindow +\n _timeoutConfig.decryptionWindow;\n // TODO do we actually need a max duration?\n require(totalDuration < maxDuration, InvalidDuration(totalDuration));\n\n require(\n e3Programs[requestParams.e3Program],\n E3ProgramNotAllowed(requestParams.e3Program)\n );\n\n uint256 e3Fee = getE3Quote(requestParams);\n\n e3Id = nexte3Id;\n nexte3Id++;\n uint256 seed = uint256(keccak256(abi.encode(block.prevrandao, e3Id)));\n\n e3.seed = seed;\n e3.threshold = requestParams.threshold;\n e3.requestBlock = block.number;\n e3.inputWindow = requestParams.inputWindow;\n e3.e3Program = requestParams.e3Program;\n e3.e3ProgramParams = requestParams.e3ProgramParams;\n e3.customParams = requestParams.customParams;\n e3.committeePublicKey = hex\"\";\n e3.ciphertextOutput = hex\"\";\n e3.plaintextOutput = hex\"\";\n e3.requester = msg.sender;\n\n bytes32 encryptionSchemeId = requestParams.e3Program.validate(\n e3Id,\n seed,\n requestParams.e3ProgramParams,\n requestParams.computeProviderParams,\n requestParams.customParams\n );\n IDecryptionVerifier decryptionVerifier = decryptionVerifiers[\n encryptionSchemeId\n ];\n\n require(\n decryptionVerifiers[encryptionSchemeId] !=\n IDecryptionVerifier(address(0)),\n InvalidEncryptionScheme(encryptionSchemeId)\n );\n\n e3.encryptionSchemeId = encryptionSchemeId;\n e3.decryptionVerifier = decryptionVerifier;\n\n e3s[e3Id] = e3;\n e3Payments[e3Id] = e3Fee;\n\n feeToken.safeTransferFrom(msg.sender, address(this), e3Fee);\n\n require(\n ciphernodeRegistry.requestCommittee(\n e3Id,\n seed,\n requestParams.threshold\n ),\n CommitteeSelectionFailed()\n );\n\n // Initialize E3 lifecycle\n _e3Stages[e3Id] = E3Stage.Requested;\n _e3Requesters[e3Id] = msg.sender;\n\n // the compute deadline is end of input window + compute window\n _e3Deadlines[e3Id].computeDeadline =\n e3.inputWindow[1] +\n _timeoutConfig.computeWindow;\n\n emit E3Requested(e3Id, e3, requestParams.e3Program);\n emit E3StageChanged(e3Id, E3Stage.None, E3Stage.Requested);\n }\n\n /// @inheritdoc IEnclave\n function publishCiphertextOutput(\n uint256 e3Id,\n bytes calldata ciphertextOutput,\n bytes calldata proof\n ) external returns (bool success) {\n E3 memory e3 = getE3(e3Id);\n\n E3Deadlines memory deadlines = _e3Deadlines[e3Id];\n\n // You cannot post outputs after the compute deadline\n require(\n deadlines.computeDeadline >= block.timestamp,\n CommitteeDutiesCompleted(e3Id, deadlines.computeDeadline)\n );\n\n // The program need to have stopped accepting inputs\n require(\n block.timestamp >= e3.inputWindow[1],\n InputDeadlineNotReached(e3Id, e3.inputWindow[1])\n );\n\n // For now we only accept one output\n require(\n e3.ciphertextOutput == bytes32(0),\n CiphertextOutputAlreadyPublished(e3Id)\n );\n\n bytes32 ciphertextOutputHash = keccak256(ciphertextOutput);\n e3s[e3Id].ciphertextOutput = ciphertextOutputHash;\n\n (success) = e3.e3Program.verify(e3Id, ciphertextOutputHash, proof);\n require(success, InvalidOutput(ciphertextOutput));\n\n // Update lifecycle stage\n _e3Stages[e3Id] = E3Stage.CiphertextReady;\n _e3Deadlines[e3Id].decryptionDeadline =\n block.timestamp +\n _timeoutConfig.decryptionWindow;\n\n emit CiphertextOutputPublished(e3Id, ciphertextOutput);\n emit E3StageChanged(\n e3Id,\n E3Stage.KeyPublished,\n E3Stage.CiphertextReady\n );\n }\n\n /// @inheritdoc IEnclave\n function publishPlaintextOutput(\n uint256 e3Id,\n bytes calldata plaintextOutput,\n bytes calldata proof\n ) external returns (bool success) {\n E3 memory e3 = getE3(e3Id);\n\n // Check we are in the right stage\n // no need to check if there's a ciphertext as we would not\n // be in this stage otherwise\n E3Stage current = _e3Stages[e3Id];\n require(\n current == E3Stage.CiphertextReady,\n InvalidStage(e3Id, E3Stage.CiphertextReady, current)\n );\n\n // you cannot post a decryption after the decryption deadline\n E3Deadlines memory deadlines = _e3Deadlines[e3Id];\n require(\n deadlines.decryptionDeadline >= block.timestamp,\n CommitteeDutiesCompleted(e3Id, deadlines.decryptionDeadline)\n );\n\n e3s[e3Id].plaintextOutput = plaintextOutput;\n\n (success) = e3.decryptionVerifier.verify(\n e3Id,\n keccak256(plaintextOutput),\n proof\n );\n require(success, InvalidOutput(plaintextOutput));\n\n // Update lifecycle stage to Complete\n _e3Stages[e3Id] = E3Stage.Complete;\n\n _distributeRewards(e3Id);\n\n emit PlaintextOutputPublished(e3Id, plaintextOutput);\n emit E3StageChanged(e3Id, E3Stage.CiphertextReady, E3Stage.Complete);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Internal Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Distributes rewards to committee members after successful E3 completion.\n /// @dev Divides the E3 payment equally among all committee members and transfers via bonding registry.\n /// @dev Emits RewardsDistributed event upon successful distribution.\n /// @param e3Id The ID of the E3 for which to distribute rewards.\n function _distributeRewards(uint256 e3Id) internal {\n address[] memory committeeNodes = ciphernodeRegistry.getCommitteeNodes(\n e3Id\n );\n uint256 committeeLength = committeeNodes.length;\n uint256[] memory amounts = new uint256[](committeeLength);\n\n // TODO: do we need to pay different amounts to different nodes?\n // For now, we'll pay the same amount to all nodes.\n uint256 amount = e3Payments[e3Id] / committeeLength;\n for (uint256 i = 0; i < committeeLength; i++) {\n amounts[i] = amount;\n }\n\n uint256 totalAmount = e3Payments[e3Id];\n e3Payments[e3Id] = 0;\n\n feeToken.approve(address(bondingRegistry), totalAmount);\n\n bondingRegistry.distributeRewards(feeToken, committeeNodes, amounts);\n\n // TODO: decide where does dust go? Treasury maybe?\n feeToken.approve(address(bondingRegistry), 0);\n\n emit RewardsDistributed(e3Id, committeeNodes, amounts);\n }\n\n /// @notice Retrieves the honest committee nodes for a given E3.\n /// @dev Determines honest nodes based on failure reason and committee publication status.\n /// @param e3Id The ID of the E3.\n /// @return honestNodes An array of addresses of honest committee nodes.\n function _getHonestNodes(\n uint256 e3Id\n ) private view returns (address[] memory) {\n FailureReason reason = _e3FailureReasons[e3Id];\n\n // Early failures have no committee\n if (\n reason == FailureReason.CommitteeFormationTimeout ||\n reason == FailureReason.InsufficientCommitteeMembers\n ) {\n return new address[](0);\n }\n\n // Try to get published committee nodes\n try ciphernodeRegistry.getCommitteeNodes(e3Id) returns (\n address[] memory nodes\n ) {\n // TODO: Implement fault attribution to filter honest from faulting nodes\n return nodes; // Assume all are honest for now\n } catch {\n return new address[](0); // Committee not published (DKG failed)\n }\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Set Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @inheritdoc IEnclave\n function setMaxDuration(uint256 _maxDuration) public onlyOwner {\n maxDuration = _maxDuration;\n emit MaxDurationSet(_maxDuration);\n }\n\n /// @inheritdoc IEnclave\n function setCiphernodeRegistry(\n ICiphernodeRegistry _ciphernodeRegistry\n ) public onlyOwner {\n require(\n address(_ciphernodeRegistry) != address(0) &&\n _ciphernodeRegistry != ciphernodeRegistry,\n InvalidCiphernodeRegistry(_ciphernodeRegistry)\n );\n ciphernodeRegistry = _ciphernodeRegistry;\n emit CiphernodeRegistrySet(address(_ciphernodeRegistry));\n }\n\n /// @inheritdoc IEnclave\n function setBondingRegistry(\n IBondingRegistry _bondingRegistry\n ) public onlyOwner {\n require(\n address(_bondingRegistry) != address(0) &&\n _bondingRegistry != bondingRegistry,\n InvalidBondingRegistry(_bondingRegistry)\n );\n bondingRegistry = _bondingRegistry;\n emit BondingRegistrySet(address(_bondingRegistry));\n }\n\n /// @inheritdoc IEnclave\n function setFeeToken(IERC20 _feeToken) public onlyOwner {\n require(\n address(_feeToken) != address(0) && _feeToken != feeToken,\n InvalidFeeToken(_feeToken)\n );\n feeToken = _feeToken;\n emit FeeTokenSet(address(_feeToken));\n }\n\n /// @inheritdoc IEnclave\n function enableE3Program(IE3Program e3Program) public onlyOwner {\n require(\n !e3Programs[e3Program],\n ModuleAlreadyEnabled(address(e3Program))\n );\n e3Programs[e3Program] = true;\n emit E3ProgramEnabled(e3Program);\n }\n\n /// @inheritdoc IEnclave\n function disableE3Program(IE3Program e3Program) public onlyOwner {\n require(e3Programs[e3Program], ModuleNotEnabled(address(e3Program)));\n delete e3Programs[e3Program];\n emit E3ProgramDisabled(e3Program);\n }\n\n /// @inheritdoc IEnclave\n function setDecryptionVerifier(\n bytes32 encryptionSchemeId,\n IDecryptionVerifier decryptionVerifier\n ) public onlyOwner {\n require(\n decryptionVerifier != IDecryptionVerifier(address(0)) &&\n decryptionVerifiers[encryptionSchemeId] != decryptionVerifier,\n InvalidEncryptionScheme(encryptionSchemeId)\n );\n decryptionVerifiers[encryptionSchemeId] = decryptionVerifier;\n emit EncryptionSchemeEnabled(encryptionSchemeId);\n }\n\n /// @inheritdoc IEnclave\n function disableEncryptionScheme(\n bytes32 encryptionSchemeId\n ) public onlyOwner {\n require(\n decryptionVerifiers[encryptionSchemeId] !=\n IDecryptionVerifier(address(0)),\n InvalidEncryptionScheme(encryptionSchemeId)\n );\n decryptionVerifiers[encryptionSchemeId] = IDecryptionVerifier(\n address(0)\n );\n emit EncryptionSchemeDisabled(encryptionSchemeId);\n }\n\n /// @inheritdoc IEnclave\n function setE3ProgramsParams(\n bytes[] memory _e3ProgramsParams\n ) public onlyOwner {\n uint256 length = _e3ProgramsParams.length;\n for (uint256 i; i < length; ) {\n e3ProgramsParams[_e3ProgramsParams[i]] = true;\n unchecked {\n ++i;\n }\n }\n emit AllowedE3ProgramsParamsSet(_e3ProgramsParams);\n }\n\n /// @notice Sets the E3 Refund Manager contract address\n /// @param _e3RefundManager The new E3 Refund Manager contract address\n function setE3RefundManager(\n IE3RefundManager _e3RefundManager\n ) public onlyOwner {\n require(\n address(_e3RefundManager) != address(0),\n \"Invalid E3RefundManager address\"\n );\n e3RefundManager = _e3RefundManager;\n emit E3RefundManagerSet(address(_e3RefundManager));\n }\n\n /// @notice Process a failed E3 and calculate refunds\n /// @dev Can be called by anyone once E3 is in failed state\n /// @param e3Id The ID of the failed E3\n function processE3Failure(uint256 e3Id) external {\n E3Stage stage = _e3Stages[e3Id];\n require(stage == E3Stage.Failed, \"E3 not failed\");\n\n uint256 payment = e3Payments[e3Id];\n require(payment > 0, \"No payment to refund\");\n e3Payments[e3Id] = 0; // Prevent double processing\n\n address[] memory honestNodes = _getHonestNodes(e3Id);\n\n feeToken.safeTransfer(address(e3RefundManager), payment);\n e3RefundManager.calculateRefund(e3Id, payment, honestNodes);\n\n emit E3FailureProcessed(e3Id, payment, honestNodes.length);\n }\n\n /// @inheritdoc IEnclave\n function onCommitteeFinalized(\n uint256 e3Id\n ) external onlyCiphernodeRegistry {\n // Update E3 lifecycle stage - committee finalized, DKG starting\n E3Stage current = _e3Stages[e3Id];\n if (current != E3Stage.Requested) {\n revert InvalidStage(e3Id, E3Stage.Requested, current);\n }\n _e3Stages[e3Id] = E3Stage.CommitteeFinalized;\n _e3Deadlines[e3Id].dkgDeadline =\n block.timestamp +\n _timeoutConfig.dkgWindow;\n\n emit CommitteeFinalized(e3Id);\n emit E3StageChanged(\n e3Id,\n E3Stage.Requested,\n E3Stage.CommitteeFinalized\n );\n }\n\n /// @inheritdoc IEnclave\n function onCommitteePublished(\n uint256 e3Id,\n bytes32 committeePublicKeyHash\n ) external onlyCiphernodeRegistry {\n // DKG complete, key published\n E3Stage current = _e3Stages[e3Id];\n if (current != E3Stage.CommitteeFinalized) {\n revert InvalidStage(e3Id, E3Stage.CommitteeFinalized, current);\n }\n _e3Stages[e3Id] = E3Stage.KeyPublished;\n\n e3s[e3Id].committeePublicKey = committeePublicKeyHash;\n\n emit CommitteeFormed(e3Id);\n emit E3StageChanged(\n e3Id,\n E3Stage.CommitteeFinalized,\n E3Stage.KeyPublished\n );\n }\n\n /// @inheritdoc IEnclave\n function onE3Failed(\n uint256 e3Id,\n uint8 reason\n ) external onlyCiphernodeRegistry {\n require(reason > 0 && reason <= 12, \"Invalid failure reason\");\n // Mark E3 as failed with the given reason\n _markE3FailedWithReason(e3Id, FailureReason(reason));\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Lifecycle Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Anyone can mark an E3 as failed if timeout passed\n /// @param e3Id The E3 ID\n /// @return reason The failure reason\n function markE3Failed(\n uint256 e3Id\n ) external returns (FailureReason reason) {\n E3Stage current = _e3Stages[e3Id];\n\n if (current == E3Stage.None)\n revert InvalidStage(e3Id, E3Stage.Requested, current);\n if (current == E3Stage.Complete) revert E3AlreadyComplete(e3Id);\n if (current == E3Stage.Failed) revert E3AlreadyFailed(e3Id);\n\n bool canFail;\n (canFail, reason) = _checkFailureCondition(e3Id, current);\n if (!canFail) revert FailureConditionNotMet(e3Id);\n\n _e3Stages[e3Id] = E3Stage.Failed;\n _e3FailureReasons[e3Id] = reason;\n\n emit E3StageChanged(e3Id, current, E3Stage.Failed);\n emit E3Failed(e3Id, current, reason);\n }\n\n /// @notice Internal function to mark E3 as failed with specific reason\n /// @param e3Id The E3 ID\n /// @param reason The failure reason\n function _markE3FailedWithReason(\n uint256 e3Id,\n FailureReason reason\n ) internal {\n E3Stage current = _e3Stages[e3Id];\n\n if (current == E3Stage.None)\n revert InvalidStage(e3Id, E3Stage.Requested, current);\n if (current == E3Stage.Complete) revert E3AlreadyComplete(e3Id);\n if (current == E3Stage.Failed) revert E3AlreadyFailed(e3Id);\n\n _e3Stages[e3Id] = E3Stage.Failed;\n _e3FailureReasons[e3Id] = reason;\n\n emit E3StageChanged(e3Id, current, E3Stage.Failed);\n emit E3Failed(e3Id, current, reason);\n }\n\n /// @notice Check if E3 can be marked as failed\n /// @param e3Id The E3 ID\n /// @return canFail Whether failure condition is met\n /// @return reason The failure reason if applicable\n function checkFailureCondition(\n uint256 e3Id\n ) external view returns (bool canFail, FailureReason reason) {\n E3Stage current = _e3Stages[e3Id];\n return _checkFailureCondition(e3Id, current);\n }\n\n /// @notice Internal function to check failure conditions\n function _checkFailureCondition(\n uint256 e3Id,\n E3Stage stage\n ) internal view returns (bool canFail, FailureReason reason) {\n E3Deadlines memory d = _e3Deadlines[e3Id];\n\n uint256 committeeDeadline = ciphernodeRegistry.getCommitteeDeadline(\n e3Id\n );\n\n if (stage == E3Stage.Requested && block.timestamp > committeeDeadline) {\n return (true, FailureReason.CommitteeFormationTimeout);\n }\n if (\n stage == E3Stage.CommitteeFinalized &&\n block.timestamp > d.dkgDeadline\n ) {\n return (true, FailureReason.DKGTimeout);\n }\n if (\n stage == E3Stage.KeyPublished && block.timestamp > d.computeDeadline\n ) {\n return (true, FailureReason.ComputeTimeout);\n }\n if (\n stage == E3Stage.CiphertextReady &&\n block.timestamp > d.decryptionDeadline\n ) {\n return (true, FailureReason.DecryptionTimeout);\n }\n\n return (false, FailureReason.None);\n }\n\n /// @notice Get current stage of an E3\n /// @param e3Id The E3 ID\n /// @return stage The current stage\n function getE3Stage(uint256 e3Id) external view returns (E3Stage stage) {\n return _e3Stages[e3Id];\n }\n\n /// @notice Get failure reason for an E3\n /// @param e3Id The E3 ID\n /// @return reason The failure reason\n function getFailureReason(\n uint256 e3Id\n ) external view returns (FailureReason reason) {\n return _e3FailureReasons[e3Id];\n }\n\n /// @notice Get requester address for an E3\n /// @param e3Id The E3 ID\n /// @return requester The requester address\n function getRequester(\n uint256 e3Id\n ) external view returns (address requester) {\n return _e3Requesters[e3Id];\n }\n\n /// @notice Get deadlines for an E3\n /// @param e3Id The E3 ID\n /// @return deadlines The E3 deadlines\n function getDeadlines(\n uint256 e3Id\n ) external view returns (E3Deadlines memory deadlines) {\n return _e3Deadlines[e3Id];\n }\n\n /// @notice Get timeout configuration\n /// @return config The current timeout config\n function getTimeoutConfig()\n external\n view\n returns (E3TimeoutConfig memory config)\n {\n return _timeoutConfig;\n }\n\n /// @notice Set timeout configuration\n /// @param config The new timeout config\n function setTimeoutConfig(\n E3TimeoutConfig calldata config\n ) external onlyOwner {\n _setTimeoutConfig(config);\n }\n\n /// @notice Internal function to set timeout config\n function _setTimeoutConfig(E3TimeoutConfig calldata config) internal {\n require(config.dkgWindow > 0, \"Invalid DKG window\");\n require(config.computeWindow > 0, \"Invalid compute window\");\n require(config.decryptionWindow > 0, \"Invalid decryption window\");\n require(config.gracePeriod > 0, \"Invalid grace period\");\n\n _timeoutConfig = config;\n\n emit TimeoutConfigUpdated(config);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Get Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @inheritdoc IEnclave\n function getE3(uint256 e3Id) public view returns (E3 memory e3) {\n e3 = e3s[e3Id];\n require(e3.e3Program != IE3Program(address(0)), E3DoesNotExist(e3Id));\n }\n\n /// @inheritdoc IEnclave\n function getE3Quote(\n E3RequestParams calldata\n ) public pure returns (uint256 fee) {\n fee = 1 * 10 ** 6;\n require(fee > 0, PaymentRequired(fee));\n }\n\n /// @inheritdoc IEnclave\n function getDecryptionVerifier(\n bytes32 encryptionSchemeId\n ) public view returns (IDecryptionVerifier) {\n return decryptionVerifiers[encryptionSchemeId];\n }\n}\n"
|
|
245
254
|
},
|
|
246
255
|
"project/contracts/interfaces/IBondingRegistry.sol": {
|
|
247
|
-
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\n\npragma solidity >=0.8.27;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ICiphernodeRegistry } from \"./ICiphernodeRegistry.sol\";\nimport { EnclaveTicketToken } from \"../token/EnclaveTicketToken.sol\";\n\n/**\n * @title IBondingRegistry\n * @notice Interface for the main bonding registry that holds operator balance and license bonds\n */\ninterface IBondingRegistry {\n // ======================\n // Custom Errors\n // ======================\n\n // General\n error ZeroAddress();\n error ZeroAmount();\n error CiphernodeBanned();\n error Unauthorized();\n error InsufficientBalance();\n error NotLicensed();\n error AlreadyRegistered();\n error NotRegistered();\n error ExitInProgress();\n error ExitNotReady();\n error InvalidAmount();\n error InvalidConfiguration();\n error NoPendingDeregistration();\n error OnlyRewardDistributor();\n error ArrayLengthMismatch();\n\n // ======================\n // Events (Protocol-Named)\n // ======================\n\n /**\n * @notice Emitted when operator's ticket balance changes\n * @param operator Address of the operator\n * @param delta Change in balance (positive for increase, negative for decrease)\n * @param newBalance New total balance\n * @param reason Reason for the change (e.g., \"DEPOSIT\", \"WITHDRAW\", slash reason)\n */\n event TicketBalanceUpdated(\n address indexed operator,\n int256 delta,\n uint256 newBalance,\n bytes32 indexed reason\n );\n\n /**\n * @notice Emitted when operator's license bond changes\n * @param operator Address of the operator\n * @param delta Change in bond (positive for increase, negative for decrease)\n * @param newBond New total license bond\n * @param reason Reason for the change (e.g., \"BOND\", \"UNBOND\", slash reason)\n */\n event LicenseBondUpdated(\n address indexed operator,\n int256 delta,\n uint256 newBond,\n bytes32 indexed reason\n );\n\n /**\n * @notice Emitted when operator requests deregistration from the protocol\n * @param operator Address of the operator\n * @param unlockAt Timestamp when deregistration can be finalized\n */\n event CiphernodeDeregistrationRequested(\n address indexed operator,\n uint64 unlockAt\n );\n\n /**\n * @notice Emitted when operator active status changes\n * @param operator Address of the operator\n * @param active True if active, false if inactive\n */\n event OperatorActivationChanged(address indexed operator, bool active);\n\n /**\n * @notice Emitted when configuration is updated\n * @param parameter Name of the parameter\n * @param oldValue Previous value\n * @param newValue New value\n */\n event ConfigurationUpdated(\n bytes32 indexed parameter,\n uint256 oldValue,\n uint256 newValue\n );\n\n /**\n * @notice Emitted when treasury withdraws slashed funds\n * @param to Treasury address\n * @param ticketAmount Amount of slashed ticket balance withdrawn\n * @param licenseAmount Amount of slashed license bond withdrawn\n */\n event SlashedFundsWithdrawn(\n address indexed to,\n uint256 ticketAmount,\n uint256 licenseAmount\n );\n\n // ======================\n // View Functions\n // ======================\n\n /**\n * @notice Get license token address\n * @return License token address\n */\n function getLicenseToken() external view returns (address);\n\n /**\n * @notice Get ticket token address\n * @return Ticket token address\n */\n function getTicketToken() external view returns (address);\n\n /**\n * @notice Get operator's current ticket balance\n * @param operator Address of the operator\n * @return Current collateral balance\n */\n function getTicketBalance(address operator) external view returns (uint256);\n\n /**\n * @notice Get operator's current license bond\n * @param operator Address of the operator\n * @return Current license bond\n */\n function getLicenseBond(address operator) external view returns (uint256);\n\n /**\n * @notice Get current ticket price\n * @return Price per ticket in collateral token units\n */\n function ticketPrice() external view returns (uint256);\n\n /**\n * @notice Calculate available tickets for an operator\n * @param operator Address of the operator\n * @return Number of tickets available (floor(balance / ticketPrice))\n */\n function availableTickets(address operator) external view returns (uint256);\n\n /**\n * @notice Check if operator is licensed\n * @param operator Address of the operator\n * @return True if operator has sufficient license bond\n */\n function isLicensed(address operator) external view returns (bool);\n\n /**\n * @notice Check if operator is registered\n * @param operator Address of the operator\n * @return True if operator is registered\n */\n function isRegistered(address operator) external view returns (bool);\n\n /**\n * @notice Check if operator is active\n * @param operator Address of the operator\n * @return True if operator is active (licensed, registered, and has min tickets)\n */\n function isActive(address operator) external view returns (bool);\n\n /**\n * @notice Check if operator has deregistration in progress\n * @param operator Address of the operator\n * @return True if exit requested but not finalized\n */\n function hasExitInProgress(address operator) external view returns (bool);\n\n /**\n * @notice Get license bond price required\n * @return License bond price amount\n */\n function licenseRequiredBond() external view returns (uint256);\n\n /**\n * @notice Get minimum ticket balance required for activation\n * @return Minimum number of tickets required\n */\n function minTicketBalance() external view returns (uint256);\n\n /**\n * @notice Get exit delay period\n * @return Number of seconds operators must wait after requesting exit\n */\n function exitDelay() external view returns (uint64);\n\n /**\n * @notice Get operator's ticket balance at a specific block\n * @param operator Address of the operator\n * @param blockNumber Block number to query\n * @return Ticket balance at the specified block\n */\n function getTicketBalanceAtBlock(\n address operator,\n uint256 blockNumber\n ) external view returns (uint256);\n\n /**\n * @notice Get operator's total pending exit amounts\n * @param operator Address of the operator\n * @return ticket Total pending ticket balance in exit queue\n * @return license Total pending license bond in exit queue\n */\n function pendingExits(\n address operator\n ) external view returns (uint256 ticket, uint256 license);\n\n /**\n * @notice Preview how much an operator can currently claim\n * @param operator Address of the operator\n * @return ticket Claimable ticket balance\n * @return license Claimable license bond\n */\n function previewClaimable(\n address operator\n ) external view returns (uint256 ticket, uint256 license);\n\n /**\n * @notice Get slashed funds treasury address\n * @return Address where slashed funds are sent\n */\n function slashedFundsTreasury() external view returns (address);\n\n /**\n * @notice Get total slashed ticket balance\n * @return Amount of ticket balance slashed and available for treasury withdrawal\n */\n function slashedTicketBalance() external view returns (uint256);\n\n /**\n * @notice Get total slashed license bond\n * @return Amount of license bond slashed and available for treasury withdrawal\n */\n function slashedLicenseBond() external view returns (uint256);\n\n // ======================\n // Operator Functions\n // ======================\n\n /**\n * @notice Register as an operator (callable by licensed operators)\n * @dev Requires sufficient license bond and calls registry\n */\n function registerOperator() external;\n\n /**\n * @notice Deregister as an operator and remove from IMT\n * @param siblingNodes Sibling node proofs for IMT removal\n * @dev Requires operator to provide sibling nodes for immediate IMT removal\n */\n function deregisterOperator(uint256[] calldata siblingNodes) external;\n\n /**\n * @notice Increase operator's ticket balance by depositing tokens\n * @param amount Amount of ticket tokens to deposit\n * @dev Requires approval for ticket token transfer\n */\n function addTicketBalance(uint256 amount) external;\n\n /**\n * @notice Decrease operator's ticket balance by withdrawing tokens\n * @param amount Amount of ticket tokens to withdraw\n * @dev Reverts if operator is in any active committee\n */\n function removeTicketBalance(uint256 amount) external;\n\n /**\n * @notice Bond license tokens to become eligible for registration\n * @param amount Amount of license tokens to bond\n * @dev Requires approval for license token transfer\n */\n function bondLicense(uint256 amount) external;\n\n /**\n * @notice Unbond license tokens\n * @param amount Amount of license tokens to unbond\n * @dev Reverts if operator is in any active committee or still registered\n */\n function unbondLicense(uint256 amount) external;\n\n // ======================\n // Claim Functions\n // ======================\n\n /**\n * @notice Claim operator's ticket balance and license bond\n * @param maxTicketAmount Maximum amount of ticket tokens to claim\n * @param maxLicenseAmount Maximum amount of license tokens to claim\n */\n function claimExits(\n uint256 maxTicketAmount,\n uint256 maxLicenseAmount\n ) external;\n\n // ======================\n // Slashing Functions\n // ======================\n\n /**\n * @notice Slash operator's ticket balance by absolute amount\n * @param operator Address of the operator to slash\n * @param amount Amount to slash\n * @param reason Reason for slashing (stored in event)\n * @dev Only callable by authorized slashing manager\n */\n function slashTicketBalance(\n address operator,\n uint256 amount,\n bytes32 reason\n ) external;\n\n /**\n * @notice Slash operator's license bond by absolute amount\n * @param operator Address of the operator to slash\n * @param amount Amount to slash\n * @param reason Reason for slashing (stored in event)\n * @dev Only callable by authorized slashing manager\n */\n function slashLicenseBond(\n address operator,\n uint256 amount,\n bytes32 reason\n ) external;\n\n // ======================\n // Reward Distribution Functions\n // ======================\n /**\n * @notice Distribute rewards to operators\n * @param rewardToken Reward token contract\n * @param operators Addresses of the operators to distribute rewards to\n * @param amounts Amounts of rewards to distribute to each operator\n * @dev Only callable by contract owner\n */\n function distributeRewards(\n IERC20 rewardToken,\n address[] calldata operators,\n uint256[] calldata amounts\n ) external;\n\n // ======================\n // Admin Functions\n // ======================\n\n /**\n * @notice Set ticket price\n * @param newTicketPrice New price per ticket\n * @dev Only callable by contract owner\n */\n function setTicketPrice(uint256 newTicketPrice) external;\n\n /**\n * @notice Set license bond price required\n * @param newLicenseRequiredBond New license bond price\n * @dev Only callable by contract owner\n */\n function setLicenseRequiredBond(uint256 newLicenseRequiredBond) external;\n\n /**\n * @notice Set license active BPS\n * @param newBps New license active BPS\n * @dev Only callable by contract owner\n */\n function setLicenseActiveBps(uint256 newBps) external;\n\n /**\n * @notice Set minimum ticket balance required for activation\n * @param newMinTicketBalance New minimum ticket balance\n * @dev Only callable by contract owner\n */\n function setMinTicketBalance(uint256 newMinTicketBalance) external;\n\n /**\n * @notice Set exit delay period\n * @param newExitDelay New exit delay in seconds\n * @dev Only callable by contract owner\n */\n function setExitDelay(uint64 newExitDelay) external;\n\n /**\n * @notice Set ticket token\n * @param newTicketToken New ticket token\n * @dev Only callable by contract owner\n */\n function setTicketToken(EnclaveTicketToken newTicketToken) external;\n\n /**\n * @notice Set license token\n * @param newLicenseToken New license token\n * @dev Only callable by contract owner\n */\n function setLicenseToken(IERC20 newLicenseToken) external;\n\n /**\n * @notice Set slashed funds treasury address\n * @param newSlashedFundsTreasury New slashed funds treasury address\n * @dev Only callable by contract owner\n */\n function setSlashedFundsTreasury(address newSlashedFundsTreasury) external;\n\n /**\n * @notice Set registry address\n * @param newRegistry New registry contract address\n * @dev Only callable by contract owner\n */\n function setRegistry(ICiphernodeRegistry newRegistry) external;\n\n /**\n * @notice Set slashing manager address\n * @param newSlashingManager New slashing manager contract address\n * @dev Only callable by contract owner\n */\n function setSlashingManager(address newSlashingManager) external;\n\n /**\n * @notice Set reward distributor address\n * @param newRewardDistributor New reward distributor address\n * @dev Only callable by contract owner\n */\n function setRewardDistributor(address newRewardDistributor) external;\n\n /**\n * @notice Withdraw slashed funds to treasury\n * @param ticketAmount Amount of slashed ticket balance to withdraw\n * @param licenseAmount Amount of slashed license bond to withdraw\n * @dev Only callable by contract owner, sends to treasury address\n */\n function withdrawSlashedFunds(\n uint256 ticketAmount,\n uint256 licenseAmount\n ) external;\n}\n"
|
|
256
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\n\npragma solidity >=0.8.27;\n\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport { ICiphernodeRegistry } from \"./ICiphernodeRegistry.sol\";\nimport { EnclaveTicketToken } from \"../token/EnclaveTicketToken.sol\";\n\n/**\n * @title IBondingRegistry\n * @notice Interface for the main bonding registry that holds operator balance and license bonds\n */\ninterface IBondingRegistry {\n // ======================\n // Custom Errors\n // ======================\n\n // General\n error ZeroAddress();\n error ZeroAmount();\n error CiphernodeBanned();\n error Unauthorized();\n error InsufficientBalance();\n error NotLicensed();\n error AlreadyRegistered();\n error NotRegistered();\n error ExitInProgress();\n error ExitNotReady();\n error InvalidAmount();\n error InvalidConfiguration();\n error NoPendingDeregistration();\n error OnlyRewardDistributor();\n error ArrayLengthMismatch();\n\n // ======================\n // Events (Protocol-Named)\n // ======================\n\n /**\n * @notice Emitted when operator's ticket balance changes\n * @param operator Address of the operator\n * @param delta Change in balance (positive for increase, negative for decrease)\n * @param newBalance New total balance\n * @param reason Reason for the change (e.g., \"DEPOSIT\", \"WITHDRAW\", slash reason)\n */\n event TicketBalanceUpdated(\n address indexed operator,\n int256 delta,\n uint256 newBalance,\n bytes32 indexed reason\n );\n\n /**\n * @notice Emitted when operator's license bond changes\n * @param operator Address of the operator\n * @param delta Change in bond (positive for increase, negative for decrease)\n * @param newBond New total license bond\n * @param reason Reason for the change (e.g., \"BOND\", \"UNBOND\", slash reason)\n */\n event LicenseBondUpdated(\n address indexed operator,\n int256 delta,\n uint256 newBond,\n bytes32 indexed reason\n );\n\n /**\n * @notice Emitted when operator requests deregistration from the protocol\n * @param operator Address of the operator\n * @param unlockAt Timestamp when deregistration can be finalized\n */\n event CiphernodeDeregistrationRequested(\n address indexed operator,\n uint64 unlockAt\n );\n\n /**\n * @notice Emitted when operator active status changes\n * @param operator Address of the operator\n * @param active True if active, false if inactive\n */\n event OperatorActivationChanged(address indexed operator, bool active);\n\n /**\n * @notice Emitted when configuration is updated\n * @param parameter Name of the parameter\n * @param oldValue Previous value\n * @param newValue New value\n */\n event ConfigurationUpdated(\n bytes32 indexed parameter,\n uint256 oldValue,\n uint256 newValue\n );\n\n /**\n * @notice Emitted when treasury withdraws slashed funds\n * @param to Treasury address\n * @param ticketAmount Amount of slashed ticket balance withdrawn\n * @param licenseAmount Amount of slashed license bond withdrawn\n */\n event SlashedFundsWithdrawn(\n address indexed to,\n uint256 ticketAmount,\n uint256 licenseAmount\n );\n\n // ======================\n // View Functions\n // ======================\n\n /**\n * @notice Get license token address\n * @return License token address\n */\n function getLicenseToken() external view returns (address);\n\n /**\n * @notice Get ticket token address\n * @return Ticket token address\n */\n function getTicketToken() external view returns (address);\n\n /**\n * @notice Get operator's current ticket balance\n * @param operator Address of the operator\n * @return Current collateral balance\n */\n function getTicketBalance(address operator) external view returns (uint256);\n\n /**\n * @notice Get operator's current license bond\n * @param operator Address of the operator\n * @return Current license bond\n */\n function getLicenseBond(address operator) external view returns (uint256);\n\n /**\n * @notice Get current ticket price\n * @return Price per ticket in collateral token units\n */\n function ticketPrice() external view returns (uint256);\n\n /**\n * @notice Calculate available tickets for an operator\n * @param operator Address of the operator\n * @return Number of tickets available (floor(balance / ticketPrice))\n */\n function availableTickets(address operator) external view returns (uint256);\n\n /**\n * @notice Check if operator is licensed\n * @param operator Address of the operator\n * @return True if operator has sufficient license bond\n */\n function isLicensed(address operator) external view returns (bool);\n\n /**\n * @notice Check if operator is registered\n * @param operator Address of the operator\n * @return True if operator is registered\n */\n function isRegistered(address operator) external view returns (bool);\n\n /**\n * @notice Check if operator is active\n * @param operator Address of the operator\n * @return True if operator is active (licensed, registered, and has min tickets)\n */\n function isActive(address operator) external view returns (bool);\n\n /**\n * @notice Get the number of currently active operators\n * @return Number of active operators\n */\n function numActiveOperators() external view returns (uint256);\n\n /**\n * @notice Check if operator has deregistration in progress\n * @param operator Address of the operator\n * @return True if exit requested but not finalized\n */\n function hasExitInProgress(address operator) external view returns (bool);\n\n /**\n * @notice Get license bond price required\n * @return License bond price amount\n */\n function licenseRequiredBond() external view returns (uint256);\n\n /**\n * @notice Get minimum ticket balance required for activation\n * @return Minimum number of tickets required\n */\n function minTicketBalance() external view returns (uint256);\n\n /**\n * @notice Get exit delay period\n * @return Number of seconds operators must wait after requesting exit\n */\n function exitDelay() external view returns (uint64);\n\n /**\n * @notice Get operator's ticket balance at a specific block\n * @param operator Address of the operator\n * @param blockNumber Block number to query\n * @return Ticket balance at the specified block\n */\n function getTicketBalanceAtBlock(\n address operator,\n uint256 blockNumber\n ) external view returns (uint256);\n\n /**\n * @notice Get operator's total pending exit amounts\n * @param operator Address of the operator\n * @return ticket Total pending ticket balance in exit queue\n * @return license Total pending license bond in exit queue\n */\n function pendingExits(\n address operator\n ) external view returns (uint256 ticket, uint256 license);\n\n /**\n * @notice Preview how much an operator can currently claim\n * @param operator Address of the operator\n * @return ticket Claimable ticket balance\n * @return license Claimable license bond\n */\n function previewClaimable(\n address operator\n ) external view returns (uint256 ticket, uint256 license);\n\n /**\n * @notice Get slashed funds treasury address\n * @return Address where slashed funds are sent\n */\n function slashedFundsTreasury() external view returns (address);\n\n /**\n * @notice Get total slashed ticket balance\n * @return Amount of ticket balance slashed and available for treasury withdrawal\n */\n function slashedTicketBalance() external view returns (uint256);\n\n /**\n * @notice Get total slashed license bond\n * @return Amount of license bond slashed and available for treasury withdrawal\n */\n function slashedLicenseBond() external view returns (uint256);\n\n // ======================\n // Operator Functions\n // ======================\n\n /**\n * @notice Register as an operator (callable by licensed operators)\n * @dev Requires sufficient license bond and calls registry\n */\n function registerOperator() external;\n\n /**\n * @notice Deregister as an operator and remove from IMT\n * @param siblingNodes Sibling node proofs for IMT removal\n * @dev Requires operator to provide sibling nodes for immediate IMT removal\n */\n function deregisterOperator(uint256[] calldata siblingNodes) external;\n\n /**\n * @notice Increase operator's ticket balance by depositing tokens\n * @param amount Amount of ticket tokens to deposit\n * @dev Requires approval for ticket token transfer\n */\n function addTicketBalance(uint256 amount) external;\n\n /**\n * @notice Decrease operator's ticket balance by withdrawing tokens\n * @param amount Amount of ticket tokens to withdraw\n * @dev Reverts if operator is in any active committee\n */\n function removeTicketBalance(uint256 amount) external;\n\n /**\n * @notice Bond license tokens to become eligible for registration\n * @param amount Amount of license tokens to bond\n * @dev Requires approval for license token transfer\n */\n function bondLicense(uint256 amount) external;\n\n /**\n * @notice Unbond license tokens\n * @param amount Amount of license tokens to unbond\n * @dev Reverts if operator is in any active committee or still registered\n */\n function unbondLicense(uint256 amount) external;\n\n // ======================\n // Claim Functions\n // ======================\n\n /**\n * @notice Claim operator's ticket balance and license bond\n * @param maxTicketAmount Maximum amount of ticket tokens to claim\n * @param maxLicenseAmount Maximum amount of license tokens to claim\n */\n function claimExits(\n uint256 maxTicketAmount,\n uint256 maxLicenseAmount\n ) external;\n\n // ======================\n // Slashing Functions\n // ======================\n\n /**\n * @notice Slash operator's ticket balance by absolute amount\n * @param operator Address of the operator to slash\n * @param amount Amount to slash\n * @param reason Reason for slashing (stored in event)\n * @dev Only callable by authorized slashing manager\n */\n function slashTicketBalance(\n address operator,\n uint256 amount,\n bytes32 reason\n ) external;\n\n /**\n * @notice Slash operator's license bond by absolute amount\n * @param operator Address of the operator to slash\n * @param amount Amount to slash\n * @param reason Reason for slashing (stored in event)\n * @dev Only callable by authorized slashing manager\n */\n function slashLicenseBond(\n address operator,\n uint256 amount,\n bytes32 reason\n ) external;\n\n // ======================\n // Reward Distribution Functions\n // ======================\n /**\n * @notice Distribute rewards to operators\n * @param rewardToken Reward token contract\n * @param operators Addresses of the operators to distribute rewards to\n * @param amounts Amounts of rewards to distribute to each operator\n * @dev Only callable by contract owner\n */\n function distributeRewards(\n IERC20 rewardToken,\n address[] calldata operators,\n uint256[] calldata amounts\n ) external;\n\n // ======================\n // Admin Functions\n // ======================\n\n /**\n * @notice Set ticket price\n * @param newTicketPrice New price per ticket\n * @dev Only callable by contract owner\n */\n function setTicketPrice(uint256 newTicketPrice) external;\n\n /**\n * @notice Set license bond price required\n * @param newLicenseRequiredBond New license bond price\n * @dev Only callable by contract owner\n */\n function setLicenseRequiredBond(uint256 newLicenseRequiredBond) external;\n\n /**\n * @notice Set license active BPS\n * @param newBps New license active BPS\n * @dev Only callable by contract owner\n */\n function setLicenseActiveBps(uint256 newBps) external;\n\n /**\n * @notice Set minimum ticket balance required for activation\n * @param newMinTicketBalance New minimum ticket balance\n * @dev Only callable by contract owner\n */\n function setMinTicketBalance(uint256 newMinTicketBalance) external;\n\n /**\n * @notice Set exit delay period\n * @param newExitDelay New exit delay in seconds\n * @dev Only callable by contract owner\n */\n function setExitDelay(uint64 newExitDelay) external;\n\n /**\n * @notice Set ticket token\n * @param newTicketToken New ticket token\n * @dev Only callable by contract owner\n */\n function setTicketToken(EnclaveTicketToken newTicketToken) external;\n\n /**\n * @notice Set license token\n * @param newLicenseToken New license token\n * @dev Only callable by contract owner\n */\n function setLicenseToken(IERC20 newLicenseToken) external;\n\n /**\n * @notice Set slashed funds treasury address\n * @param newSlashedFundsTreasury New slashed funds treasury address\n * @dev Only callable by contract owner\n */\n function setSlashedFundsTreasury(address newSlashedFundsTreasury) external;\n\n /**\n * @notice Set registry address\n * @param newRegistry New registry contract address\n * @dev Only callable by contract owner\n */\n function setRegistry(ICiphernodeRegistry newRegistry) external;\n\n /**\n * @notice Set slashing manager address\n * @param newSlashingManager New slashing manager contract address\n * @dev Only callable by contract owner\n */\n function setSlashingManager(address newSlashingManager) external;\n\n /**\n * @notice Set reward distributor address\n * @param newRewardDistributor New reward distributor address\n * @dev Only callable by contract owner\n */\n function setRewardDistributor(address newRewardDistributor) external;\n\n /**\n * @notice Withdraw slashed funds to treasury\n * @param ticketAmount Amount of slashed ticket balance to withdraw\n * @param licenseAmount Amount of slashed license bond to withdraw\n * @dev Only callable by contract owner, sends to treasury address\n */\n function withdrawSlashedFunds(\n uint256 ticketAmount,\n uint256 licenseAmount\n ) external;\n}\n"
|
|
248
257
|
},
|
|
249
258
|
"project/contracts/interfaces/ICiphernodeRegistry.sol": {
|
|
250
|
-
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\n/**\n * @title ICiphernodeRegistry\n * @notice Interface for managing ciphernode registration and committee selection\n * @dev This registry maintains an Incremental Merkle Tree (IMT) of registered ciphernodes\n * and coordinates committee selection for E3 computations\n */\ninterface ICiphernodeRegistry {\n /// @notice Struct representing the sortition state for an E3 round.\n /// @param initialized Whether the round has been initialized.\n /// @param finalized Whether the round has been finalized.\n /// @param requestBlock The block number when the committee was requested.\n /// @param submissionDeadline The deadline for submitting tickets.\n /// @param threshold The M/N threshold for the committee ([M, N]).\n /// @param publicKey Hash of the committee's public key.\n /// @param seed The seed for the round.\n /// @param topNodes The top nodes in the round.\n /// @param committee The committee for the round.\n /// @param submitted Mapping of nodes to their submission status.\n /// @param scoreOf Mapping of nodes to their scores.\n struct Committee {\n bool initialized;\n bool finalized;\n uint256 seed;\n uint256 requestBlock;\n uint256 submissionDeadline;\n bytes32 publicKey;\n uint32[2] threshold;\n address[] topNodes;\n address[] committee;\n mapping(address node => bool submitted) submitted;\n mapping(address node => uint256 score) scoreOf;\n }\n\n /// @notice This event MUST be emitted when a committee is selected for an E3.\n /// @param e3Id ID of the E3 for which the committee was selected.\n /// @param seed Random seed for score computation.\n /// @param threshold The M/N threshold for the committee.\n /// @param requestBlock Block number for snapshot validation.\n /// @param submissionDeadline Deadline for submitting tickets.\n event CommitteeRequested(\n uint256 indexed e3Id,\n uint256 seed,\n uint32[2] threshold,\n uint256 requestBlock,\n uint256 submissionDeadline\n );\n\n /// @notice This event MUST be emitted when a ticket is submitted for sortition\n /// @param e3Id ID of the E3 computation\n /// @param node Address of the ciphernode submitting the ticket\n /// @param ticketId The ticket number being submitted\n /// @param score The computed score for the ticket\n event TicketSubmitted(\n uint256 indexed e3Id,\n address indexed node,\n uint256 ticketId,\n uint256 score\n );\n\n /// @notice This event MUST be emitted when a committee is finalized\n /// @param e3Id ID of the E3 computation\n /// @param committee Array of selected ciphernode addresses\n event CommitteeFinalized(uint256 indexed e3Id, address[] committee);\n\n /// @notice This event MUST be emitted when a committee is selected for an E3.\n /// @param e3Id ID of the E3 for which the committee was selected.\n /// @param publicKey Public key of the committee.\n event CommitteePublished(\n uint256 indexed e3Id,\n address[] nodes,\n bytes publicKey\n );\n\n /// @notice This event MUST be emitted when a committee's active status changes.\n /// @param e3Id ID of the E3 for which the committee status changed.\n /// @param active True if committee is now active, false if completed.\n event CommitteeActivationChanged(uint256 indexed e3Id, bool active);\n\n /// @notice This event MUST be emitted when `enclave` is set.\n /// @param enclave Address of the enclave contract.\n event EnclaveSet(address indexed enclave);\n\n /// @notice This event MUST be emitted when a ciphernode is added to the registry.\n /// @param node Address of the ciphernode.\n /// @param index Index of the ciphernode in the registry.\n /// @param numNodes Number of ciphernodes in the registry.\n /// @param size Size of the registry.\n event CiphernodeAdded(\n address indexed node,\n uint256 index,\n uint256 numNodes,\n uint256 size\n );\n\n /// @notice This event MUST be emitted when a ciphernode is removed from the registry.\n /// @param node Address of the ciphernode.\n /// @param index Index of the ciphernode in the registry.\n /// @param numNodes Number of ciphernodes in the registry.\n /// @param size Size of the registry.\n event CiphernodeRemoved(\n address indexed node,\n uint256 index,\n uint256 numNodes,\n uint256 size\n );\n\n /// @notice This event MUST be emitted any time the `sortitionSubmissionWindow` is set.\n /// @param sortitionSubmissionWindow The submission window for the E3 sortition in seconds.\n event SortitionSubmissionWindowSet(uint256 sortitionSubmissionWindow);\n\n /// @notice Check if a ciphernode is eligible for committee selection\n /// @dev A ciphernode is eligible if it is enabled in the registry and meets bonding requirements\n /// @param ciphernode Address of the ciphernode to check\n /// @return eligible Whether the ciphernode is eligible for committee selection\n function isCiphernodeEligible(address ciphernode) external returns (bool);\n\n /// @notice Check if a ciphernode is enabled in the registry\n /// @param node Address of the ciphernode\n /// @return enabled Whether the ciphernode is enabled\n function isEnabled(address node) external view returns (bool enabled);\n\n /// @notice Add a ciphernode to the registry\n /// @param node Address of the ciphernode to add\n function addCiphernode(address node) external;\n\n /// @notice Remove a ciphernode from the registry\n /// @param node Address of the ciphernode to remove\n /// @param siblingNodes Array of sibling node indices for tree operations\n function removeCiphernode(\n address node,\n uint256[] calldata siblingNodes\n ) external;\n\n /// @notice Initiates the committee selection process for a specified E3.\n /// @dev This function MUST revert when not called by the Enclave contract.\n /// @param e3Id ID of the E3 for which to select the committee.\n /// @param seed Random seed for score computation.\n /// @param threshold The M/N threshold for the committee.\n /// @return success True if committee selection was successfully initiated.\n function requestCommittee(\n uint256 e3Id,\n uint256 seed,\n uint32[2] calldata threshold\n ) external returns (bool success);\n\n /// @notice Publishes the public key resulting from the committee selection process.\n /// @dev This function MUST revert if not called by the owner.\n /// @param e3Id ID of the E3 for which to select the committee.\n /// @param nodes Array of ciphernode addresses selected for the committee.\n /// @param publicKey The public key generated by the given committee.\n /// @param publicKeyHash The hash of the public key.\n function publishCommittee(\n uint256 e3Id,\n address[] calldata nodes,\n bytes calldata publicKey,\n bytes32 publicKeyHash\n ) external;\n\n /// @notice This function should be called by the Enclave contract to get the public key of a committee.\n /// @dev This function MUST revert if no committee has been requested for the given E3.\n /// @dev This function MUST revert if the committee has not yet published a public key.\n /// @param e3Id ID of the E3 for which to get the committee public key.\n /// @return publicKeyHash The hash of the public key of the given committee.\n function committeePublicKey(\n uint256 e3Id\n ) external view returns (bytes32 publicKeyHash);\n\n /// @notice This function should be called by the Enclave contract to get the committee for a given E3.\n /// @dev This function MUST revert if no committee has been requested for the given E3.\n /// @param e3Id ID of the E3 for which to get the committee.\n /// @return committeeNodes The nodes in the committee for the given E3.\n function getCommitteeNodes(\n uint256 e3Id\n ) external view returns (address[] memory committeeNodes);\n\n /// @notice Returns the current root of the ciphernode IMT\n /// @return Current IMT root\n function root() external view returns (uint256);\n\n /// @notice Returns the IMT root at the time a committee was requested\n /// @param e3Id ID of the E3\n /// @return IMT root at time of committee request\n function rootAt(uint256 e3Id) external view returns (uint256);\n\n /// @notice Returns the current size of the ciphernode IMT\n /// @return Size of the IMT\n function treeSize() external view returns (uint256);\n\n /// @notice Returns the address of the bonding registry\n /// @return Address of the bonding registry contract\n function getBondingRegistry() external view returns (address);\n\n /// @notice Sets the Enclave contract address\n /// @dev Only callable by owner\n /// @param _enclave Address of the Enclave contract\n function setEnclave(address _enclave) external;\n\n /// @notice Sets the bonding registry contract address\n /// @dev Only callable by owner\n /// @param _bondingRegistry Address of the bonding registry contract\n function setBondingRegistry(address _bondingRegistry) external;\n\n /// @notice This function should be called to set the submission window for the E3 sortition.\n /// @param _sortitionSubmissionWindow The submission window for the E3 sortition in seconds.\n function setSortitionSubmissionWindow(\n uint256 _sortitionSubmissionWindow\n ) external;\n\n /// @notice Submit a ticket for sortition\n /// @dev Validates ticket against node's balance at request block\n /// @param e3Id ID of the E3 computation\n /// @param ticketNumber The ticket number to submit\n function submitTicket(uint256 e3Id, uint256 ticketNumber) external;\n\n /// @notice Finalize the committee after submission window closes\n /// @param e3Id ID of the E3 computation\n function finalizeCommittee(uint256 e3Id) external;\n\n /// @notice Check if submission window is still open for an E3\n /// @param e3Id ID of the E3 computation\n /// @return Whether the submission window is open\n function isOpen(uint256 e3Id) external view returns (bool);\n}\n"
|
|
259
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { IEnclave } from \"./IEnclave.sol\";\nimport { IBondingRegistry } from \"./IBondingRegistry.sol\";\n\n/**\n * @title ICiphernodeRegistry\n * @notice Interface for managing ciphernode registration and committee selection\n * @dev This registry maintains an Incremental Merkle Tree (IMT) of registered ciphernodes\n * and coordinates committee selection for E3 computations\n */\ninterface ICiphernodeRegistry {\n /// @notice Struct representing the sortition state for an E3 round.\n /// @param initialized Whether the round has been initialized.\n /// @param finalized Whether the round has been finalized.\n /// @param requestBlock The block number when the committee was requested.\n /// @param committeeDeadline The deadline for committee formation (ticket submission).\n /// @param threshold The M/N threshold for the committee ([M, N]).\n /// @param publicKey Hash of the committee's public key.\n /// @param seed The seed for the round.\n /// @param topNodes The top nodes in the round.\n /// @param committee The committee for the round.\n /// @param submitted Mapping of nodes to their submission status.\n /// @param scoreOf Mapping of nodes to their scores.\n /// @param failed True if committee formation failed (threshold not met).\n struct Committee {\n bool initialized;\n bool finalized;\n bool failed;\n uint256 seed;\n uint256 requestBlock;\n uint256 committeeDeadline;\n bytes32 publicKey;\n uint32[2] threshold;\n address[] topNodes;\n address[] committee;\n mapping(address node => bool submitted) submitted;\n mapping(address node => uint256 score) scoreOf;\n }\n\n /// @notice This event MUST be emitted when a committee is selected for an E3.\n /// @param e3Id ID of the E3 for which the committee was selected.\n /// @param seed Random seed for score computation.\n /// @param threshold The M/N threshold for the committee.\n /// @param requestBlock Block number for snapshot validation.\n /// @param committeeDeadline Deadline for committee formation (ticket submission).\n event CommitteeRequested(\n uint256 indexed e3Id,\n uint256 seed,\n uint32[2] threshold,\n uint256 requestBlock,\n uint256 committeeDeadline\n );\n\n /// @notice This event MUST be emitted when a ticket is submitted for sortition\n /// @param e3Id ID of the E3 computation\n /// @param node Address of the ciphernode submitting the ticket\n /// @param ticketId The ticket number being submitted\n /// @param score The computed score for the ticket\n event TicketSubmitted(\n uint256 indexed e3Id,\n address indexed node,\n uint256 ticketId,\n uint256 score\n );\n\n /// @notice This event MUST be emitted when a committee is finalized\n /// @param e3Id ID of the E3 computation\n /// @param committee Array of selected ciphernode addresses\n event CommitteeFinalized(uint256 indexed e3Id, address[] committee);\n\n /// @notice This event MUST be emitted when committee formation fails (threshold not met)\n /// @param e3Id ID of the E3 computation\n /// @param nodesSubmitted Number of nodes that submitted tickets\n /// @param thresholdRequired Minimum number of nodes required\n event CommitteeFormationFailed(\n uint256 indexed e3Id,\n uint256 nodesSubmitted,\n uint256 thresholdRequired\n );\n\n /// @notice This event MUST be emitted when a committee is selected for an E3.\n /// @param e3Id ID of the E3 for which the committee was selected.\n /// @param publicKey Public key of the committee.\n event CommitteePublished(\n uint256 indexed e3Id,\n address[] nodes,\n bytes publicKey\n );\n\n /// @notice This event MUST be emitted when a committee's active status changes.\n /// @param e3Id ID of the E3 for which the committee status changed.\n /// @param active True if committee is now active, false if completed.\n event CommitteeActivationChanged(uint256 indexed e3Id, bool active);\n\n /// @notice This event MUST be emitted when `enclave` is set.\n /// @param enclave Address of the enclave contract.\n event EnclaveSet(address indexed enclave);\n\n /// @notice This event MUST be emitted when a ciphernode is added to the registry.\n /// @param node Address of the ciphernode.\n /// @param index Index of the ciphernode in the registry.\n /// @param numNodes Number of ciphernodes in the registry.\n /// @param size Size of the registry.\n event CiphernodeAdded(\n address indexed node,\n uint256 index,\n uint256 numNodes,\n uint256 size\n );\n\n /// @notice This event MUST be emitted when a ciphernode is removed from the registry.\n /// @param node Address of the ciphernode.\n /// @param index Index of the ciphernode in the registry.\n /// @param numNodes Number of ciphernodes in the registry.\n /// @param size Size of the registry.\n event CiphernodeRemoved(\n address indexed node,\n uint256 index,\n uint256 numNodes,\n uint256 size\n );\n\n /// @notice This event MUST be emitted any time the `sortitionSubmissionWindow` is set.\n /// @param sortitionSubmissionWindow The submission window for the E3 sortition in seconds.\n event SortitionSubmissionWindowSet(uint256 sortitionSubmissionWindow);\n\n /// @notice Check if a ciphernode is eligible for committee selection\n /// @dev A ciphernode is eligible if it is enabled in the registry and meets bonding requirements\n /// @param ciphernode Address of the ciphernode to check\n /// @return eligible Whether the ciphernode is eligible for committee selection\n function isCiphernodeEligible(address ciphernode) external returns (bool);\n\n /// @notice Check if a ciphernode is enabled in the registry\n /// @param node Address of the ciphernode\n /// @return enabled Whether the ciphernode is enabled\n function isEnabled(address node) external view returns (bool enabled);\n\n /// @notice Add a ciphernode to the registry\n /// @param node Address of the ciphernode to add\n function addCiphernode(address node) external;\n\n /// @notice Remove a ciphernode from the registry\n /// @param node Address of the ciphernode to remove\n /// @param siblingNodes Array of sibling node indices for tree operations\n function removeCiphernode(\n address node,\n uint256[] calldata siblingNodes\n ) external;\n\n /// @notice Initiates the committee selection process for a specified E3.\n /// @dev This function MUST revert when not called by the Enclave contract.\n /// @param e3Id ID of the E3 for which to select the committee.\n /// @param seed Random seed for score computation.\n /// @param threshold The M/N threshold for the committee.\n /// @return success True if committee selection was successfully initiated.\n function requestCommittee(\n uint256 e3Id,\n uint256 seed,\n uint32[2] calldata threshold\n ) external returns (bool success);\n\n /// @notice Publishes the public key resulting from the committee selection process.\n /// @dev This function MUST revert if not called by the owner.\n /// @param e3Id ID of the E3 for which to select the committee.\n /// @param nodes Array of ciphernode addresses selected for the committee.\n /// @param publicKey The public key generated by the given committee.\n /// @param publicKeyHash The hash of the public key.\n function publishCommittee(\n uint256 e3Id,\n address[] calldata nodes,\n bytes calldata publicKey,\n bytes32 publicKeyHash\n ) external;\n\n /// @notice This function should be called by the Enclave contract to get the public key of a committee.\n /// @dev This function MUST revert if no committee has been requested for the given E3.\n /// @dev This function MUST revert if the committee has not yet published a public key.\n /// @param e3Id ID of the E3 for which to get the committee public key.\n /// @return publicKeyHash The hash of the public key of the given committee.\n function committeePublicKey(\n uint256 e3Id\n ) external view returns (bytes32 publicKeyHash);\n\n /// @notice This function should be called by the Enclave contract to get the committee for a given E3.\n /// @dev This function MUST revert if no committee has been requested for the given E3.\n /// @param e3Id ID of the E3 for which to get the committee.\n /// @return committeeNodes The nodes in the committee for the given E3.\n function getCommitteeNodes(\n uint256 e3Id\n ) external view returns (address[] memory committeeNodes);\n\n /// @notice Returns the current root of the ciphernode IMT\n /// @return Current IMT root\n function root() external view returns (uint256);\n\n /// @notice Returns the IMT root at the time a committee was requested\n /// @param e3Id ID of the E3\n /// @return IMT root at time of committee request\n function rootAt(uint256 e3Id) external view returns (uint256);\n\n /// @notice Returns the current size of the ciphernode IMT\n /// @return Size of the IMT\n function treeSize() external view returns (uint256);\n\n /// @notice Returns the address of the bonding registry\n /// @return Address of the bonding registry contract\n function getBondingRegistry() external view returns (address);\n\n /// @notice Sets the Enclave contract address\n /// @dev Only callable by owner\n /// @param _enclave Address of the Enclave contract\n function setEnclave(IEnclave _enclave) external;\n\n /// @notice Sets the bonding registry contract address\n /// @dev Only callable by owner\n /// @param _bondingRegistry Address of the bonding registry contract\n function setBondingRegistry(IBondingRegistry _bondingRegistry) external;\n\n /// @notice This function should be called to set the submission window for the E3 sortition.\n /// @param _sortitionSubmissionWindow The submission window for the E3 sortition in seconds.\n function setSortitionSubmissionWindow(\n uint256 _sortitionSubmissionWindow\n ) external;\n\n /// @notice Submit a ticket for sortition\n /// @dev Validates ticket against node's balance at request block\n /// @param e3Id ID of the E3 computation\n /// @param ticketNumber The ticket number to submit\n function submitTicket(uint256 e3Id, uint256 ticketNumber) external;\n\n /// @notice Finalize the committee after submission window closes\n /// @dev If threshold not met, marks E3 as failed and returns false\n /// @param e3Id ID of the E3 computation\n /// @return success True if committee formed successfully, false if threshold not met\n function finalizeCommittee(uint256 e3Id) external returns (bool success);\n\n /// @notice Check if submission window is still open for an E3\n /// @param e3Id ID of the E3 computation\n /// @return Whether the submission window is open\n function isOpen(uint256 e3Id) external view returns (bool);\n\n /// @notice Get the committee deadline for an E3\n /// @param e3Id ID of the E3 computation\n /// @return committeeDeadline The committee deadline timestamp\n function getCommitteeDeadline(uint256 e3Id) external view returns (uint256);\n}\n"
|
|
251
260
|
},
|
|
252
261
|
"project/contracts/interfaces/IComputeProvider.sol": {
|
|
253
262
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { IDecryptionVerifier } from \"./IDecryptionVerifier.sol\";\n\n/**\n * @title IComputeProvider\n * @notice Interface for compute provider validation and configuration\n * @dev Compute providers define how computations are executed and verified in the E3 system\n */\ninterface IComputeProvider {\n /// @notice Validate compute provider parameters and return the appropriate decryption verifier\n /// @dev This function is called by the Enclave contract during E3 request to validate\n /// compute provider configuration\n /// @param e3Id ID of the E3 computation\n /// @param seed Random seed for the computation\n /// @param params ABI encoded compute provider parameters\n /// @return decryptionVerifier The decryption verifier contract to use for this computation\n function validate(\n uint256 e3Id,\n uint256 seed,\n bytes calldata params\n ) external returns (IDecryptionVerifier decryptionVerifier);\n}\n"
|
|
@@ -256,13 +265,16 @@
|
|
|
256
265
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\n/**\n * @title IDecryptionVerifier\n * @notice Interface for verifying decrypted computation outputs\n * @dev Implements cryptographic verification of plaintext outputs from encrypted computations\n */\ninterface IDecryptionVerifier {\n /// @notice Verify the decryption of a computation output\n /// @dev This function is called by the Enclave contract when plaintext output is published\n /// @param e3Id ID of the E3 computation\n /// @param plaintextOutputHash The keccak256 hash of the plaintext output to be verified\n /// @param proof ABI encoded proof of the decryption validity\n /// @return success Whether the plaintextOutputHash was successfully verified\n function verify(\n uint256 e3Id,\n bytes32 plaintextOutputHash,\n bytes memory proof\n ) external view returns (bool success);\n}\n"
|
|
257
266
|
},
|
|
258
267
|
"project/contracts/interfaces/IE3.sol": {
|
|
259
|
-
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { IE3Program } from \"./IE3Program.sol\";\nimport { IDecryptionVerifier } from \"./IDecryptionVerifier.sol\";\n\n/**\n * @title E3\n * @notice Represents a complete E3 (Encrypted Execution Environment) computation request and its lifecycle\n * @dev This struct tracks all parameters, state, and results of an encrypted computation\n * from request through completion\n * @param seed Random seed for committee selection and computation initialization\n * @param threshold M/N threshold for the committee (M required out of N total members)\n * @param requestBlock Block number when the E3 computation was requested\n * @param
|
|
268
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { IE3Program } from \"./IE3Program.sol\";\nimport { IDecryptionVerifier } from \"./IDecryptionVerifier.sol\";\n\n/**\n * @title E3\n * @notice Represents a complete E3 (Encrypted Execution Environment) computation request and its lifecycle\n * @dev This struct tracks all parameters, state, and results of an encrypted computation\n * from request through completion\n * @param seed Random seed for committee selection and computation initialization\n * @param threshold M/N threshold for the committee (M required out of N total members)\n * @param requestBlock Block number when the E3 computation was requested\n * @param inputWindow When to start and stop accepting inputs from data providers\n * @param encryptionSchemeId Identifier for the encryption scheme used in this computation\n * @param e3Program Address of the E3 Program contract that validates and verifies the computation\n * @param e3ProgramParams ABI encoded computation parameters specific to the E3 program\n * @param customParams Arbitrary ABI-encoded application-defined parameters.\n * @param decryptionVerifier Address of the output verifier contract for decryption verification\n * @param committeePublicKey Hash of the public key of the selected committee for this computation\n * @param ciphertextOutput Hash of the encrypted output data produced by the computation\n * @param plaintextOutput Decrypted output data after committee decryption\n * @param requester Address of the entity that requested the E3 computation\n */\nstruct E3 {\n uint256 seed;\n uint32[2] threshold;\n uint256 requestBlock;\n uint256[2] inputWindow;\n bytes32 encryptionSchemeId;\n IE3Program e3Program;\n bytes e3ProgramParams;\n bytes customParams;\n IDecryptionVerifier decryptionVerifier;\n bytes32 committeePublicKey;\n bytes32 ciphertextOutput;\n bytes plaintextOutput;\n address requester;\n}\n"
|
|
260
269
|
},
|
|
261
270
|
"project/contracts/interfaces/IE3Program.sol": {
|
|
262
|
-
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\n/**\n * @title IE3Program\n * @notice Interface for E3 program validation and verification\n * @dev E3 programs define the computation logic and validation rules for encrypted execution environments\n */\ninterface IE3Program {\n /// @notice Validate E3 computation parameters and return encryption scheme and input validator\n /// @dev This function is called by the Enclave contract during E3 request to configure the computation\n /// @param e3Id ID of the E3 computation\n /// @param seed Random seed for the computation\n /// @param e3ProgramParams ABI encoded E3 program parameters\n /// @param computeProviderParams ABI encoded compute provider parameters\n /// @param customParams ABI encoded custom parameters for the E3 program\n /// @return encryptionSchemeId ID of the encryption scheme to be used for the computation\n function validate(\n uint256 e3Id,\n uint256 seed,\n bytes calldata e3ProgramParams,\n bytes calldata computeProviderParams,\n bytes calldata customParams\n ) external returns (bytes32 encryptionSchemeId);\n\n /// @notice Verify the ciphertext output of an E3 computation\n /// @dev This function is called by the Enclave contract when ciphertext output is published\n /// @param e3Id ID of the E3 computation\n /// @param ciphertextOutputHash The keccak256 hash of output data to be verified\n /// @param proof ABI encoded data to verify the ciphertextOutputHash\n /// @return success Whether the output data is valid\n function verify(\n uint256 e3Id,\n bytes32 ciphertextOutputHash,\n bytes memory proof\n ) external returns (bool success);\n\n /// @notice Validate and process input data for a computation\n /// @dev This function is called by
|
|
271
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\n/**\n * @title IE3Program\n * @notice Interface for E3 program validation and verification\n * @dev E3 programs define the computation logic and validation rules for encrypted execution environments\n */\ninterface IE3Program {\n /// @notice Validate E3 computation parameters and return encryption scheme and input validator\n /// @dev This function is called by the Enclave contract during E3 request to configure the computation\n /// @param e3Id ID of the E3 computation\n /// @param seed Random seed for the computation\n /// @param e3ProgramParams ABI encoded E3 program parameters\n /// @param computeProviderParams ABI encoded compute provider parameters\n /// @param customParams ABI encoded custom parameters for the E3 program\n /// @return encryptionSchemeId ID of the encryption scheme to be used for the computation\n function validate(\n uint256 e3Id,\n uint256 seed,\n bytes calldata e3ProgramParams,\n bytes calldata computeProviderParams,\n bytes calldata customParams\n ) external returns (bytes32 encryptionSchemeId);\n\n /// @notice Verify the ciphertext output of an E3 computation\n /// @dev This function is called by the Enclave contract when ciphertext output is published\n /// @param e3Id ID of the E3 computation\n /// @param ciphertextOutputHash The keccak256 hash of output data to be verified\n /// @param proof ABI encoded data to verify the ciphertextOutputHash\n /// @return success Whether the output data is valid\n function verify(\n uint256 e3Id,\n bytes32 ciphertextOutputHash,\n bytes memory proof\n ) external returns (bool success);\n\n /// @notice Validate and process input data for a computation\n /// @dev This function is called by data providers when they want to submit their\n /// encrypted data\n /// @param e3Id ID of the E3 computation\n /// @param data The input data to be validated\n function publishInput(uint256 e3Id, bytes memory data) external;\n}\n"
|
|
272
|
+
},
|
|
273
|
+
"project/contracts/interfaces/IE3RefundManager.sol": {
|
|
274
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\nimport { IEnclave } from \"./IEnclave.sol\";\n\n/**\n * @title IE3RefundManager\n * @notice Interface for E3 refund distribution mechanism\n * @dev Handles refund calculation and claiming for failed E3s\n */\ninterface IE3RefundManager {\n ////////////////////////////////////////////////////////////\n // //\n // Structs //\n // //\n ////////////////////////////////////////////////////////////\n /// @notice Work value allocation in basis points (10000 = 100%)\n struct WorkValueAllocation {\n uint16 committeeFormationBps;\n uint16 dkgBps;\n uint16 decryptionBps;\n uint16 protocolBps;\n }\n /// @notice Refund distribution for a failed E3\n struct RefundDistribution {\n uint256 requesterAmount; // Amount for requester\n uint256 honestNodeAmount; // Total amount for honest nodes\n uint256 protocolAmount; // Amount for protocol treasury\n uint256 totalSlashed; // Slashed funds added\n uint256 honestNodeCount; // Number of honest nodes\n bool calculated; // Whether distribution is calculated\n }\n ////////////////////////////////////////////////////////////\n // //\n // Events //\n // //\n ////////////////////////////////////////////////////////////\n /// @notice Emitted when refund distribution is calculated\n event RefundDistributionCalculated(\n uint256 indexed e3Id,\n uint256 requesterAmount,\n uint256 honestNodeAmount,\n uint256 protocolAmount,\n uint256 totalSlashed\n );\n /// @notice Emitted when a refund is claimed\n event RefundClaimed(\n uint256 indexed e3Id,\n address indexed claimant,\n uint256 amount,\n bytes32 claimType\n );\n /// @notice Emitted when slashed funds are routed to E3\n event SlashedFundsRouted(uint256 indexed e3Id, uint256 amount);\n /// @notice Emitted when work allocation is updated\n event WorkAllocationUpdated(WorkValueAllocation allocation);\n ////////////////////////////////////////////////////////////\n // //\n // Errors //\n // //\n ////////////////////////////////////////////////////////////\n /// @notice E3 is not in failed state\n error E3NotFailed(uint256 e3Id);\n /// @notice Refund already claimed\n error AlreadyClaimed(uint256 e3Id, address claimant);\n /// @notice Not the requester\n error NotRequester(uint256 e3Id, address caller);\n /// @notice Not an honest node\n error NotHonestNode(uint256 e3Id, address caller);\n /// @notice Refund not calculated yet\n error RefundNotCalculated(uint256 e3Id);\n /// @notice No refund available\n error NoRefundAvailable(uint256 e3Id);\n /// @notice Caller not authorized\n error Unauthorized();\n\n ////////////////////////////////////////////////////////////\n // //\n // Functions //\n // //\n ////////////////////////////////////////////////////////////\n /// @notice Calculate refund distribution for a failed E3\n /// @param e3Id The failed E3 ID\n /// @param originalPayment The original payment amount\n /// @param honestNodes Array of honest node addresses\n function calculateRefund(\n uint256 e3Id,\n uint256 originalPayment,\n address[] calldata honestNodes\n ) external;\n\n /// @notice Requester claims their refund\n /// @param e3Id The failed E3 ID\n /// @return amount The amount claimed\n function claimRequesterRefund(\n uint256 e3Id\n ) external returns (uint256 amount);\n\n /// @notice Honest node claims their reward\n /// @param e3Id The failed E3 ID\n /// @return amount The amount claimed\n function claimHonestNodeReward(\n uint256 e3Id\n ) external returns (uint256 amount);\n\n /// @notice Route slashed funds to E3 refund pool\n /// @param e3Id The E3 ID\n /// @param amount The slashed amount\n function routeSlashedFunds(uint256 e3Id, uint256 amount) external;\n\n /// @notice Get refund distribution for an E3\n /// @param e3Id The E3 ID\n /// @return distribution The refund distribution\n function getRefundDistribution(\n uint256 e3Id\n ) external view returns (RefundDistribution memory distribution);\n\n /// @notice Check if address has claimed refund\n /// @param e3Id The E3 ID\n /// @param claimant The address to check\n /// @return claimed Whether the address has claimed\n function hasClaimed(\n uint256 e3Id,\n address claimant\n ) external view returns (bool claimed);\n\n /// @notice Calculate work value for a given stage\n /// @param stage The stage when E3 failed\n /// @return workCompletedBps Work completed in basis points\n /// @return workRemainingBps Work remaining in basis points\n function calculateWorkValue(\n IEnclave.E3Stage stage\n ) external view returns (uint16 workCompletedBps, uint16 workRemainingBps);\n\n /// @notice Set work value allocation\n /// @param allocation The new work allocation\n function setWorkAllocation(\n WorkValueAllocation calldata allocation\n ) external;\n\n /// @notice Get current work allocation\n /// @return allocation The current work allocation\n function getWorkAllocation()\n external\n view\n returns (WorkValueAllocation memory allocation);\n}\n"
|
|
263
275
|
},
|
|
264
276
|
"project/contracts/interfaces/IEnclave.sol": {
|
|
265
|
-
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { E3, IE3Program } from \"./IE3.sol\";\nimport { ICiphernodeRegistry } from \"./ICiphernodeRegistry.sol\";\nimport { IBondingRegistry } from \"./IBondingRegistry.sol\";\nimport { IDecryptionVerifier } from \"./IDecryptionVerifier.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IEnclave {\n ////////////////////////////////////////////////////////////\n // //\n // Events //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice This event MUST be emitted when an Encrypted Execution Environment (E3) is successfully requested.\n /// @param e3Id ID of the E3.\n /// @param e3 Details of the E3.\n /// @param e3Program Address of the Computation module selected.\n event E3Requested(uint256 e3Id, E3 e3, IE3Program indexed e3Program);\n\n /// @notice This event MUST be emitted when an Encrypted Execution Environment (E3) is successfully activated.\n /// @param e3Id ID of the E3.\n /// @param expiration Timestamp when committee duties expire.\n /// @param committeePublicKey Hash of the public key of the committee.\n event E3Activated(\n uint256 e3Id,\n uint256 expiration,\n bytes32 committeePublicKey\n );\n\n /// @notice This event MUST be emitted when an input to an Encrypted Execution Environment (E3) is\n /// successfully published.\n /// @param e3Id ID of the E3.\n /// @param data ABI encoded input data.\n event InputPublished(\n uint256 indexed e3Id,\n bytes data,\n uint256 inputHash,\n uint256 index\n );\n\n /// @notice This event MUST be emitted when the plaintext output of an Encrypted Execution Environment (E3)\n /// is successfully published.\n /// @param e3Id ID of the E3.\n /// @param plaintextOutput ABI encoded plaintext output.\n event PlaintextOutputPublished(uint256 indexed e3Id, bytes plaintextOutput);\n\n /// @notice This event MUST be emitted when the ciphertext output of an Encrypted Execution Environment (E3)\n /// is successfully published.\n /// @param e3Id ID of the E3.\n /// @param ciphertextOutput ABI encoded ciphertext output.\n event CiphertextOutputPublished(\n uint256 indexed e3Id,\n bytes ciphertextOutput\n );\n\n /// @notice This event MUST be emitted any time the `maxDuration` is set.\n /// @param maxDuration The maximum duration of a computation in seconds.\n event MaxDurationSet(uint256 maxDuration);\n\n /// @notice This event MUST be emitted any time the CiphernodeRegistry is set.\n /// @param ciphernodeRegistry The address of the CiphernodeRegistry contract.\n event CiphernodeRegistrySet(address ciphernodeRegistry);\n\n /// @notice This event MUST be emitted any time the BondingRegistry is set.\n /// @param bondingRegistry The address of the BondingRegistry contract.\n event BondingRegistrySet(address bondingRegistry);\n\n /// @notice This event MUST be emitted any time the fee token is set.\n /// @param feeToken The address of the fee token.\n event FeeTokenSet(address feeToken);\n\n /// @notice This event MUST be emitted when rewards are distributed to committee members.\n /// @param e3Id The ID of the E3 computation.\n /// @param nodes The addresses of the committee members receiving rewards.\n /// @param amounts The reward amounts for each committee member.\n event RewardsDistributed(\n uint256 indexed e3Id,\n address[] nodes,\n uint256[] amounts\n );\n\n /// @notice The event MUST be emitted any time an encryption scheme is enabled.\n /// @param encryptionSchemeId The ID of the encryption scheme that was enabled.\n event EncryptionSchemeEnabled(bytes32 encryptionSchemeId);\n\n /// @notice This event MUST be emitted any time an encryption scheme is disabled.\n /// @param encryptionSchemeId The ID of the encryption scheme that was disabled.\n event EncryptionSchemeDisabled(bytes32 encryptionSchemeId);\n\n /// @notice This event MUST be emitted any time a E3 Program is enabled.\n /// @param e3Program The address of the E3 Program.\n event E3ProgramEnabled(IE3Program e3Program);\n\n /// @notice This event MUST be emitted any time a E3 Program is disabled.\n /// @param e3Program The address of the E3 Program.\n event E3ProgramDisabled(IE3Program e3Program);\n\n /// @notice Emitted when the allowed E3 encryption scheme parameters are configured.\n /// @param e3ProgramParams Array of encoded encryption scheme parameters (e.g, for BFV)\n event AllowedE3ProgramsParamsSet(bytes[] e3ProgramParams);\n\n ////////////////////////////////////////////////////////////\n // //\n // Structs //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice This struct contains the parameters to submit a request to Enclave.\n /// @param threshold The M/N threshold for the committee.\n /// @param startWindow The start window for the computation.\n /// @param duration The duration of the computation in seconds.\n /// @param e3Program The address of the E3 Program.\n /// @param e3ProgramParams The ABI encoded computation parameters.\n /// @param computeProviderParams The ABI encoded compute provider parameters.\n /// @param customParams Arbitrary ABI-encoded application-defined parameters.\n struct E3RequestParams {\n uint32[2] threshold;\n uint256[2] startWindow;\n uint256 duration;\n IE3Program e3Program;\n bytes e3ProgramParams;\n bytes computeProviderParams;\n bytes customParams;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Core Entrypoints //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice This function should be called to request a computation within an Encrypted Execution Environment (E3).\n /// @dev This function MUST emit the E3Requested event.\n /// @param requestParams The parameters for the E3 request.\n /// @return e3Id ID of the E3.\n /// @return e3 The E3 struct.\n function request(\n E3RequestParams calldata requestParams\n ) external returns (uint256 e3Id, E3 memory e3);\n\n /// @notice This function should be called to activate an Encrypted Execution Environment (E3) once it has been\n /// initialized and is ready for input.\n /// @dev This function MUST emit the E3Activated event.\n /// @dev This function MUST revert if the given E3 has not yet been requested.\n /// @dev This function MUST revert if the selected node committee has not yet published a public key.\n /// @param e3Id ID of the E3.\n /// @return success True if the E3 was successfully activated.\n function activate(uint256 e3Id) external returns (bool success);\n\n /// @notice This function should be called to publish input data for Encrypted Execution Environment (E3).\n /// @dev This function MUST revert if the E3 is not yet activated.\n /// @dev This function MUST emit the InputPublished event.\n /// @param e3Id ID of the E3.\n /// @param data ABI encoded input data to publish.\n /// @return success True if the input was successfully published.\n function publishInput(\n uint256 e3Id,\n bytes calldata data\n ) external returns (bool success);\n\n /// @notice This function should be called to publish output data for an Encrypted Execution Environment (E3).\n /// @dev This function MUST emit the CiphertextOutputPublished event.\n /// @param e3Id ID of the E3.\n /// @param ciphertextOutput ABI encoded output data to verify.\n /// @param proof ABI encoded data to verify the ciphertextOutput.\n /// @return success True if the output was successfully published.\n function publishCiphertextOutput(\n uint256 e3Id,\n bytes calldata ciphertextOutput,\n bytes calldata proof\n ) external returns (bool success);\n\n /// @notice This function publishes the plaintext output of an Encrypted Execution Environment (E3).\n /// @dev This function MUST revert if the output has not been published.\n /// @dev This function MUST emit the PlaintextOutputPublished event.\n /// @param e3Id ID of the E3.\n /// @param plaintextOutput ABI encoded plaintext output.\n /// @param proof ABI encoded data to verify the plaintextOutput.\n function publishPlaintextOutput(\n uint256 e3Id,\n bytes calldata plaintextOutput,\n bytes calldata proof\n ) external returns (bool success);\n\n ////////////////////////////////////////////////////////////\n // //\n // Set Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice This function should be called to set the maximum duration of requested computations.\n /// @param _maxDuration The maximum duration of a computation in seconds.\n /// @return success True if the max duration was successfully set.\n function setMaxDuration(\n uint256 _maxDuration\n ) external returns (bool success);\n\n /// @notice Sets the Ciphernode Registry contract address.\n /// @dev This function MUST revert if the address is zero or the same as the current registry.\n /// @param _ciphernodeRegistry The address of the new Ciphernode Registry contract.\n /// @return success True if the registry was successfully set.\n function setCiphernodeRegistry(\n ICiphernodeRegistry _ciphernodeRegistry\n ) external returns (bool success);\n\n /// @notice Sets the Bonding Registry contract address.\n /// @dev This function MUST revert if the address is zero or the same as the current registry.\n /// @param _bondingRegistry The address of the new Bonding Registry contract.\n /// @return success True if the registry was successfully set.\n function setBondingRegistry(\n IBondingRegistry _bondingRegistry\n ) external returns (bool success);\n\n /// @notice Sets the fee token used for E3 payments.\n /// @dev This function MUST revert if the address is zero or the same as the current fee token.\n /// @param _feeToken The address of the new fee token.\n /// @return success True if the fee token was successfully set.\n function setFeeToken(IERC20 _feeToken) external returns (bool success);\n\n /// @notice This function should be called to enable an E3 Program.\n /// @param e3Program The address of the E3 Program.\n /// @return success True if the E3 Program was successfully enabled.\n function enableE3Program(\n IE3Program e3Program\n ) external returns (bool success);\n\n /// @notice This function should be called to disable an E3 Program.\n /// @param e3Program The address of the E3 Program.\n /// @return success True if the E3 Program was successfully disabled.\n function disableE3Program(\n IE3Program e3Program\n ) external returns (bool success);\n\n /// @notice Sets or enables a decryption verifier for a specific encryption scheme.\n /// @dev This function MUST revert if the verifier address is zero or already set to the same value.\n /// @param encryptionSchemeId The unique identifier for the encryption scheme.\n /// @param decryptionVerifier The address of the decryption verifier contract.\n /// @return success True if the verifier was successfully set.\n function setDecryptionVerifier(\n bytes32 encryptionSchemeId,\n IDecryptionVerifier decryptionVerifier\n ) external returns (bool success);\n\n /// @notice Disables a previously enabled encryption scheme.\n /// @dev This function MUST revert if the encryption scheme is not currently enabled.\n /// @param encryptionSchemeId The unique identifier for the encryption scheme to disable.\n /// @return success True if the encryption scheme was successfully disabled.\n function disableEncryptionScheme(\n bytes32 encryptionSchemeId\n ) external returns (bool success);\n\n /// @notice Sets the allowed E3 program parameters.\n /// @dev This function enables specific parameter sets for E3 programs (e.g., BFV encryption parameters).\n /// @param _e3ProgramsParams Array of ABI encoded parameter sets to allow.\n /// @return success True if the parameters were successfully set.\n function setE3ProgramsParams(\n bytes[] memory _e3ProgramsParams\n ) external returns (bool success);\n\n ////////////////////////////////////////////////////////////\n // //\n // Get Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice This function should be called to retrieve the details of an Encrypted Execution Environment (E3).\n /// @dev This function MUST revert if the E3 does not exist.\n /// @param e3Id ID of the E3.\n /// @return e3 The struct representing the requested E3.\n function getE3(uint256 e3Id) external view returns (E3 memory e3);\n\n /// @notice This function returns the fee of an E3\n /// @dev This function MUST revert if the E3 parameters are invalid.\n /// @param e3Params the struct representing the E3 request parameters\n /// @return fee the fee of the E3\n function getE3Quote(\n E3RequestParams calldata e3Params\n ) external view returns (uint256 fee);\n\n /// @notice Returns the decryption verifier for a given encryption scheme.\n /// @param encryptionSchemeId The unique identifier for the encryption scheme.\n /// @return The decryption verifier contract for the specified encryption scheme.\n function getDecryptionVerifier(\n bytes32 encryptionSchemeId\n ) external view returns (IDecryptionVerifier);\n\n /// @notice Returns the ERC20 token used to pay for E3 fees.\n function feeToken() external view returns (IERC20);\n}\n"
|
|
277
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { E3, IE3Program } from \"./IE3.sol\";\nimport { ICiphernodeRegistry } from \"./ICiphernodeRegistry.sol\";\nimport { IBondingRegistry } from \"./IBondingRegistry.sol\";\nimport { IDecryptionVerifier } from \"./IDecryptionVerifier.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\ninterface IEnclave {\n ////////////////////////////////////////////////////////////\n // //\n // Enums //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Lifecycle stages of an E3 computation\n enum E3Stage {\n None,\n Requested,\n CommitteeFinalized,\n // Once a key is published, it is possible to then accept inputs\n // as long as we are within the input deadline (start and end)\n KeyPublished,\n CiphertextReady,\n Complete,\n Failed\n }\n\n /// @notice Reasons why an E3 failed\n enum FailureReason {\n None,\n CommitteeFormationTimeout,\n InsufficientCommitteeMembers,\n DKGTimeout,\n DKGInvalidShares,\n NoInputsReceived,\n ComputeTimeout,\n ComputeProviderExpired,\n ComputeProviderFailed,\n RequesterCancelled,\n DecryptionTimeout,\n DecryptionInvalidShares,\n VerificationFailed\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Structs //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Timeout configuration for E3 stages\n struct E3TimeoutConfig {\n uint256 dkgWindow;\n uint256 computeWindow;\n uint256 decryptionWindow;\n uint256 gracePeriod;\n }\n\n /// @notice Deadlines for each E3\n struct E3Deadlines {\n uint256 dkgDeadline;\n uint256 computeDeadline;\n uint256 decryptionDeadline;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Events //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice This event MUST be emitted when an Encrypted Execution Environment (E3) is successfully requested.\n /// @param e3Id ID of the E3.\n /// @param e3 Details of the E3.\n /// @param e3Program Address of the Computation module selected.\n event E3Requested(uint256 e3Id, E3 e3, IE3Program indexed e3Program);\n\n /// @notice This event MUST be emitted when an input to an Encrypted Execution Environment (E3) is\n /// successfully published.\n /// @param e3Id ID of the E3.\n /// @param data ABI encoded input data.\n event InputPublished(\n uint256 indexed e3Id,\n bytes data,\n uint256 inputHash,\n uint256 index\n );\n\n /// @notice This event MUST be emitted when the plaintext output of an Encrypted Execution Environment (E3)\n /// is successfully published.\n /// @param e3Id ID of the E3.\n /// @param plaintextOutput ABI encoded plaintext output.\n event PlaintextOutputPublished(uint256 indexed e3Id, bytes plaintextOutput);\n\n /// @notice This event MUST be emitted when the ciphertext output of an Encrypted Execution Environment (E3)\n /// is successfully published.\n /// @param e3Id ID of the E3.\n /// @param ciphertextOutput ABI encoded ciphertext output.\n event CiphertextOutputPublished(\n uint256 indexed e3Id,\n bytes ciphertextOutput\n );\n\n /// @notice This event MUST be emitted any time the `maxDuration` is set.\n /// @param maxDuration The maximum duration of a computation in seconds.\n event MaxDurationSet(uint256 maxDuration);\n\n /// @notice This event MUST be emitted any time the CiphernodeRegistry is set.\n /// @param ciphernodeRegistry The address of the CiphernodeRegistry contract.\n event CiphernodeRegistrySet(address ciphernodeRegistry);\n\n /// @notice This event MUST be emitted any time the BondingRegistry is set.\n /// @param bondingRegistry The address of the BondingRegistry contract.\n event BondingRegistrySet(address bondingRegistry);\n\n /// @notice This event MUST be emitted any time the fee token is set.\n /// @param feeToken The address of the fee token.\n event FeeTokenSet(address feeToken);\n\n /// @notice This event MUST be emitted when rewards are distributed to committee members.\n /// @param e3Id The ID of the E3 computation.\n /// @param nodes The addresses of the committee members receiving rewards.\n /// @param amounts The reward amounts for each committee member.\n event RewardsDistributed(\n uint256 indexed e3Id,\n address[] nodes,\n uint256[] amounts\n );\n\n /// @notice The event MUST be emitted any time an encryption scheme is enabled.\n /// @param encryptionSchemeId The ID of the encryption scheme that was enabled.\n event EncryptionSchemeEnabled(bytes32 encryptionSchemeId);\n\n /// @notice This event MUST be emitted any time an encryption scheme is disabled.\n /// @param encryptionSchemeId The ID of the encryption scheme that was disabled.\n event EncryptionSchemeDisabled(bytes32 encryptionSchemeId);\n\n /// @notice This event MUST be emitted any time a E3 Program is enabled.\n /// @param e3Program The address of the E3 Program.\n event E3ProgramEnabled(IE3Program e3Program);\n\n /// @notice This event MUST be emitted any time a E3 Program is disabled.\n /// @param e3Program The address of the E3 Program.\n event E3ProgramDisabled(IE3Program e3Program);\n\n /// @notice Emitted when the allowed E3 encryption scheme parameters are configured.\n /// @param e3ProgramParams Array of encoded encryption scheme parameters (e.g, for BFV)\n event AllowedE3ProgramsParamsSet(bytes[] e3ProgramParams);\n\n /// @notice Emitted when E3RefundManager contract is set.\n /// @param e3RefundManager The address of the E3RefundManager contract.\n event E3RefundManagerSet(address indexed e3RefundManager);\n\n /// @notice Emitted when a failed E3 is processed for refunds.\n /// @param e3Id The ID of the failed E3.\n /// @param paymentAmount The original payment amount being refunded.\n /// @param honestNodeCount The number of honest nodes in the refund distribution.\n event E3FailureProcessed(\n uint256 indexed e3Id,\n uint256 paymentAmount,\n uint256 honestNodeCount\n );\n\n /// @notice Emitted when a committee is published and E3 lifecycle is updated.\n /// @param e3Id The ID of the E3.\n event CommitteeFormed(uint256 indexed e3Id);\n\n /// @notice Emitted when a committee is finalized (sortition complete, DKG starting).\n /// @param e3Id The ID of the E3.\n event CommitteeFinalized(uint256 indexed e3Id);\n\n /// @notice Emitted when E3 stage changes\n event E3StageChanged(\n uint256 indexed e3Id,\n E3Stage previousStage,\n E3Stage newStage\n );\n\n /// @notice Emitted when an E3 is marked as failed\n event E3Failed(\n uint256 indexed e3Id,\n E3Stage failedAtStage,\n FailureReason reason\n );\n\n /// @notice Emitted when timeout config is updated\n event TimeoutConfigUpdated(E3TimeoutConfig config);\n\n ////////////////////////////////////////////////////////////\n // //\n // Structs //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice This struct contains the parameters to submit a request to Enclave.\n /// @param threshold The M/N threshold for the committee.\n /// @param inputWindow When the program will start and stop accepting inputs.\n /// @param e3Program The address of the E3 Program.\n /// @param e3ProgramParams The ABI encoded computation parameters.\n /// @param computeProviderParams The ABI encoded compute provider parameters.\n /// @param customParams Arbitrary ABI-encoded application-defined parameters.\n struct E3RequestParams {\n uint32[2] threshold;\n uint256[2] inputWindow;\n IE3Program e3Program;\n bytes e3ProgramParams;\n bytes computeProviderParams;\n bytes customParams;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Core Entrypoints //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice This function should be called to request a computation within an Encrypted Execution Environment (E3).\n /// @dev This function MUST emit the E3Requested event.\n /// @param requestParams The parameters for the E3 request.\n /// @return e3Id ID of the E3.\n /// @return e3 The E3 struct.\n function request(\n E3RequestParams calldata requestParams\n ) external returns (uint256 e3Id, E3 memory e3);\n\n /// @notice This function should be called to publish output data for an Encrypted Execution Environment (E3).\n /// @dev This function MUST emit the CiphertextOutputPublished event.\n /// @param e3Id ID of the E3.\n /// @param ciphertextOutput ABI encoded output data to verify.\n /// @param proof ABI encoded data to verify the ciphertextOutput.\n /// @return success True if the output was successfully published.\n function publishCiphertextOutput(\n uint256 e3Id,\n bytes calldata ciphertextOutput,\n bytes calldata proof\n ) external returns (bool success);\n\n /// @notice This function publishes the plaintext output of an Encrypted Execution Environment (E3).\n /// @dev This function MUST revert if the output has not been published.\n /// @dev This function MUST emit the PlaintextOutputPublished event.\n /// @param e3Id ID of the E3.\n /// @param plaintextOutput ABI encoded plaintext output.\n /// @param proof ABI encoded data to verify the plaintextOutput.\n function publishPlaintextOutput(\n uint256 e3Id,\n bytes calldata plaintextOutput,\n bytes calldata proof\n ) external returns (bool success);\n\n ////////////////////////////////////////////////////////////\n // //\n // Set Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice This function should be called to set the maximum duration of requested computations.\n /// @param _maxDuration The maximum duration of a computation in seconds.\n function setMaxDuration(uint256 _maxDuration) external;\n\n /// @notice Sets the Ciphernode Registry contract address.\n /// @dev This function MUST revert if the address is zero or the same as the current registry.\n /// @param _ciphernodeRegistry The address of the new Ciphernode Registry contract.\n function setCiphernodeRegistry(\n ICiphernodeRegistry _ciphernodeRegistry\n ) external;\n\n /// @notice Sets the Bonding Registry contract address.\n /// @dev This function MUST revert if the address is zero or the same as the current registry.\n /// @param _bondingRegistry The address of the new Bonding Registry contract.\n function setBondingRegistry(IBondingRegistry _bondingRegistry) external;\n\n /// @notice Sets the fee token used for E3 payments.\n /// @dev This function MUST revert if the address is zero or the same as the current fee token.\n /// @param _feeToken The address of the new fee token.\n function setFeeToken(IERC20 _feeToken) external;\n\n /// @notice This function should be called to enable an E3 Program.\n /// @param e3Program The address of the E3 Program.\n function enableE3Program(IE3Program e3Program) external;\n\n /// @notice This function should be called to disable an E3 Program.\n /// @param e3Program The address of the E3 Program.\n function disableE3Program(IE3Program e3Program) external;\n\n /// @notice Sets or enables a decryption verifier for a specific encryption scheme.\n /// @dev This function MUST revert if the verifier address is zero or already set to the same value.\n /// @param encryptionSchemeId The unique identifier for the encryption scheme.\n /// @param decryptionVerifier The address of the decryption verifier contract.\n function setDecryptionVerifier(\n bytes32 encryptionSchemeId,\n IDecryptionVerifier decryptionVerifier\n ) external;\n\n /// @notice Disables a previously enabled encryption scheme.\n /// @dev This function MUST revert if the encryption scheme is not currently enabled.\n /// @param encryptionSchemeId The unique identifier for the encryption scheme to disable.\n function disableEncryptionScheme(bytes32 encryptionSchemeId) external;\n\n /// @notice Sets the allowed E3 program parameters.\n /// @dev This function enables specific parameter sets for E3 programs (e.g., BFV encryption parameters).\n /// @param _e3ProgramsParams Array of ABI encoded parameter sets to allow.\n function setE3ProgramsParams(bytes[] memory _e3ProgramsParams) external;\n\n ////////////////////////////////////////////////////////////\n // //\n // Get Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice This function should be called to retrieve the details of an Encrypted Execution Environment (E3).\n /// @dev This function MUST revert if the E3 does not exist.\n /// @param e3Id ID of the E3.\n /// @return e3 The struct representing the requested E3.\n function getE3(uint256 e3Id) external view returns (E3 memory e3);\n\n /// @notice This function returns the fee of an E3\n /// @dev This function MUST revert if the E3 parameters are invalid.\n /// @param e3Params the struct representing the E3 request parameters\n /// @return fee the fee of the E3\n function getE3Quote(\n E3RequestParams calldata e3Params\n ) external view returns (uint256 fee);\n\n /// @notice Returns the decryption verifier for a given encryption scheme.\n /// @param encryptionSchemeId The unique identifier for the encryption scheme.\n /// @return The decryption verifier contract for the specified encryption scheme.\n function getDecryptionVerifier(\n bytes32 encryptionSchemeId\n ) external view returns (IDecryptionVerifier);\n\n /// @notice Returns the ERC20 token used to pay for E3 fees.\n function feeToken() external view returns (IERC20);\n\n /// @notice Returns the BondingRegistry contract.\n function bondingRegistry() external view returns (IBondingRegistry);\n\n /// @notice Called by CiphernodeRegistry when committee is finalized (sortition complete).\n /// @dev Updates E3 lifecycle to CommitteeFinalized stage, starts DKG deadline.\n /// @param e3Id ID of the E3.\n function onCommitteeFinalized(uint256 e3Id) external;\n\n /// @notice Called by CiphernodeRegistry when committee public key is published (DKG complete).\n /// @dev Updates E3 lifecycle to KeyPublished stage.\n /// @param e3Id ID of the E3.\n /// @param committeePublicKeyHash Hash of the committee public key.\n function onCommitteePublished(\n uint256 e3Id,\n bytes32 committeePublicKeyHash\n ) external;\n\n /// @notice Called by authorized contracts to mark an E3 as failed with a specific reason.\n /// @dev Updates E3 lifecycle to Failed stage with the given reason.\n /// @param e3Id ID of the E3.\n /// @param reason The failure reason from FailureReason enum.\n function onE3Failed(uint256 e3Id, uint8 reason) external;\n\n ////////////////////////////////////////////////////////////\n // //\n // Lifecycle Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Anyone can mark an E3 as failed if timeout passed\n /// @param e3Id The E3 ID\n /// @return reason The failure reason\n function markE3Failed(uint256 e3Id) external returns (FailureReason reason);\n\n /// @notice Check if E3 can be marked as failed\n /// @param e3Id The E3 ID\n /// @return canFail Whether failure condition is met\n /// @return reason The failure reason if applicable\n function checkFailureCondition(\n uint256 e3Id\n ) external view returns (bool canFail, FailureReason reason);\n\n /// @notice Get current stage of an E3\n /// @param e3Id The E3 ID\n /// @return stage The current stage\n function getE3Stage(uint256 e3Id) external view returns (E3Stage stage);\n\n /// @notice Get failure reason for an E3\n /// @param e3Id The E3 ID\n /// @return reason The failure reason\n function getFailureReason(\n uint256 e3Id\n ) external view returns (FailureReason reason);\n\n /// @notice Get requester address for an E3\n /// @param e3Id The E3 ID\n /// @return requester The requester address\n function getRequester(\n uint256 e3Id\n ) external view returns (address requester);\n\n /// @notice Get deadlines for an E3\n /// @param e3Id The E3 ID\n /// @return deadlines The E3 deadlines\n function getDeadlines(\n uint256 e3Id\n ) external view returns (E3Deadlines memory deadlines);\n\n /// @notice Get timeout configuration\n /// @return config The current timeout config\n function getTimeoutConfig()\n external\n view\n returns (E3TimeoutConfig memory config);\n\n /// @notice Set timeout configuration\n /// @param config The new timeout config\n function setTimeoutConfig(E3TimeoutConfig calldata config) external;\n}\n"
|
|
266
278
|
},
|
|
267
279
|
"project/contracts/interfaces/ISlashingManager.sol": {
|
|
268
280
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\n\npragma solidity >=0.8.27;\n\nimport { IBondingRegistry } from \"./IBondingRegistry.sol\";\n\n/**\n * @title ISlashingManager\n * @notice Interface for managing slashing proposals, appeals, and execution\n * @dev Maintains policy table and handles slash workflows with appeals\n */\ninterface ISlashingManager {\n // ======================\n // Structs\n // ======================\n\n /**\n * @notice Slashing policy configuration for different slash reasons\n * @dev Defines penalties, proof requirements, and appeal mechanisms for each slash type\n * @param ticketPenalty Amount of ticket collateral to slash (in wei)\n * @param licensePenalty Amount of license bond to slash (in wei)\n * @param requiresProof Whether this slash type requires cryptographic proof verification\n * @param proofVerifier Address of the ISlashVerifier contract for proof validation\n * @param banNode Whether executing this slash will permanently ban the node\n * @param appealWindow Time window in seconds for operators to appeal (0 = immediate execution, no appeals)\n * @param enabled Whether this slash type is currently active and can be proposed\n */\n struct SlashPolicy {\n uint256 ticketPenalty;\n uint256 licensePenalty;\n bool requiresProof;\n address proofVerifier;\n bool banNode;\n uint256 appealWindow;\n bool enabled;\n }\n\n /**\n * @notice Slash proposal details tracking the full lifecycle of a slash\n * @dev Stores all state needed for proposal, appeal, and execution workflows\n * @param operator Address of the ciphernode operator being slashed\n * @param reason Hash of the slash reason (maps to SlashPolicy configuration)\n * @param ticketAmount Amount of ticket collateral to slash (copied from policy at proposal time)\n * @param licenseAmount Amount of license bond to slash (copied from policy at proposal time)\n * @param executed Whether the slashing penalties have been executed\n * @param appealed Whether the operator has filed an appeal\n * @param resolved Whether the appeal has been resolved by governance\n * @param appealUpheld Whether the appeal was approved (true = cancel slash, false = slash proceeds)\n * @param proposedAt Block timestamp when the slash was proposed\n * @param executableAt Block timestamp when execution becomes possible (proposedAt + appealWindow)\n * @param proposer Address that created this slash proposal\n * @param proofHash Keccak256 hash of the proof data submitted with the proposal\n * @param proofVerified Whether the proof was successfully verified by the proof verifier contract\n */\n struct SlashProposal {\n address operator;\n bytes32 reason;\n uint256 ticketAmount;\n uint256 licenseAmount;\n bool executed;\n bool appealed;\n bool resolved;\n bool appealUpheld;\n uint256 proposedAt;\n uint256 executableAt;\n address proposer;\n bytes32 proofHash;\n bool proofVerified;\n }\n\n // ======================\n // Errors\n // ======================\n\n /// @notice Thrown when a zero address is provided where a valid address is required\n error ZeroAddress();\n\n /// @notice Thrown when caller lacks required role permissions for the operation\n error Unauthorized();\n\n /// @notice Thrown when a slash policy configuration is invalid\n error InvalidPolicy();\n\n /// @notice Thrown when referencing a proposal ID that doesn't exist or is in invalid state\n error InvalidProposal();\n\n /// @notice Thrown when proof is required by policy but not provided\n error ProofRequired();\n\n /// @notice Thrown when provided proof fails verification\n error InvalidProof();\n\n /// @notice Thrown when attempting to execute a slash whose appeal was upheld\n error AppealUpheld();\n\n /// @notice Thrown when attempting to execute a slash with an unresolved appeal\n error AppealPending();\n\n /// @notice Thrown when attempting to file an appeal after the appeal window has closed\n error AppealWindowExpired();\n\n /// @notice Thrown when attempting to execute a slash before the appeal window has closed\n error AppealWindowActive();\n\n /// @notice Thrown when attempting to file a second appeal for the same proposal\n error AlreadyAppealed();\n\n /// @notice Thrown when attempting to execute a slash that has already been executed\n error AlreadyExecuted();\n\n /// @notice Thrown when attempting to resolve an appeal that has already been resolved\n error AlreadyResolved();\n\n /// @notice Thrown when referencing a slash reason that doesn't exist\n error SlashReasonNotFound();\n\n /// @notice Thrown when attempting to propose a slash for a disabled reason\n error SlashReasonDisabled();\n\n /// @notice Thrown when a banned ciphernode attempts a restricted operation\n error CiphernodeBanned();\n\n /// @notice Thrown when a policy requires proof but no verifier contract is configured\n error VerifierNotSet();\n\n // ======================\n // Events\n // ======================\n\n /**\n * @notice Emitted when a slash policy is created or updated\n * @param reason Hash of the slash reason being configured\n * @param policy The complete policy configuration including penalties and appeal settings\n */\n event SlashPolicyUpdated(bytes32 indexed reason, SlashPolicy policy);\n\n /**\n * @notice Emitted when a new slash proposal is created\n * @param proposalId Unique ID of the created proposal\n * @param operator Address of the ciphernode operator being slashed\n * @param reason Hash of the slash reason\n * @param ticketAmount Amount of ticket collateral to be slashed\n * @param licenseAmount Amount of license bond to be slashed\n * @param executableAt Timestamp when the slash can be executed (after appeal window)\n * @param proposer Address that created the proposal\n */\n event SlashProposed(\n uint256 indexed proposalId,\n address indexed operator,\n bytes32 indexed reason,\n uint256 ticketAmount,\n uint256 licenseAmount,\n uint256 executableAt,\n address proposer\n );\n\n /**\n * @notice Emitted when a slash proposal is executed and penalties are applied\n * @param proposalId ID of the executed proposal\n * @param operator Address of the slashed operator\n * @param reason Hash of the slash reason\n * @param ticketAmount Amount of ticket collateral slashed\n * @param licenseAmount Amount of license bond slashed\n * @param executed Execution status (should always be true)\n */\n event SlashExecuted(\n uint256 indexed proposalId,\n address indexed operator,\n bytes32 indexed reason,\n uint256 ticketAmount,\n uint256 licenseAmount,\n bool executed\n );\n\n /**\n * @notice Emitted when an operator files an appeal against a slash proposal\n * @param proposalId ID of the proposal being appealed\n * @param operator Address of the operator filing the appeal\n * @param reason Hash of the slash reason being appealed\n * @param evidence Evidence string provided by the operator supporting their appeal\n */\n event AppealFiled(\n uint256 indexed proposalId,\n address indexed operator,\n bytes32 indexed reason,\n string evidence\n );\n\n /**\n * @notice Emitted when governance resolves an appeal\n * @param proposalId ID of the proposal with the resolved appeal\n * @param operator Address of the operator who appealed\n * @param appealUpheld Whether the appeal was approved (true = slash cancelled, false = slash proceeds)\n * @param resolver Address of the governance account that resolved the appeal\n * @param resolution Explanation string for the resolution decision\n */\n event AppealResolved(\n uint256 indexed proposalId,\n address indexed operator,\n bool appealUpheld,\n address resolver,\n string resolution\n );\n\n /**\n * @notice Emitted when a node is banned or unbanned from the network\n * @param node Address of the node\n * @param status Whether the node is banned\n * @param reason Hash of the reason for banning or unbanning\n * @param updater Address that executed the ban (governance or contract)\n */\n event NodeBanUpdated(\n address indexed node,\n bool status,\n bytes32 indexed reason,\n address updater\n );\n\n // ======================\n // View Functions\n // ======================\n\n /**\n * @notice Retrieves the slash policy configuration for a given reason\n * @param reason Hash of the slash reason to query\n * @return policy The complete SlashPolicy struct (returns default empty struct if not configured)\n */\n function getSlashPolicy(\n bytes32 reason\n ) external view returns (SlashPolicy memory policy);\n\n /**\n * @notice Retrieves the details of a slash proposal\n * @param proposalId ID of the proposal to query\n * @return proposal The complete SlashProposal struct\n * @dev Reverts with InvalidProposal if proposalId >= totalProposals\n */\n function getSlashProposal(\n uint256 proposalId\n ) external view returns (SlashProposal memory proposal);\n\n /**\n * @notice Returns the total number of slash proposals ever created\n * @return count The total count of proposals (next proposalId will be this value)\n */\n function totalProposals() external view returns (uint256 count);\n\n /**\n * @notice Checks whether a node is currently banned\n * @param node Address of the node to check\n * @return isBanned True if the node is banned, false otherwise\n */\n function isBanned(address node) external view returns (bool isBanned);\n\n /**\n * @notice Returns the bonding registry contract used for executing slashes\n * @return registry The IBondingRegistry contract instance\n */\n function bondingRegistry()\n external\n view\n returns (IBondingRegistry registry);\n\n // ======================\n // Admin Functions\n // ======================\n\n /**\n * @notice Creates or updates the slash policy for a specific reason\n * @dev Only callable by GOVERNANCE_ROLE. Validates policy constraints before setting\n * @param reason Hash of the slash reason to configure (must be non-zero)\n * @param policy Complete policy configuration including penalties, proof requirements, and appeal settings\n * Requirements:\n * - reason must not be bytes32(0)\n * - policy.enabled must be true\n * - At least one of ticketPenalty or licensePenalty must be non-zero\n * - If requiresProof is true, proofVerifier must be set and appealWindow must be 0\n * - If requiresProof is false, appealWindow must be greater than 0\n */\n function setSlashPolicy(\n bytes32 reason,\n SlashPolicy calldata policy\n ) external;\n\n /**\n * @notice Updates the bonding registry contract address\n * @dev Only callable by DEFAULT_ADMIN_ROLE. Used to execute actual slashing of funds\n * @param newBondingRegistry Address of the new IBondingRegistry contract (must be non-zero)\n */\n function setBondingRegistry(address newBondingRegistry) external;\n\n /**\n * @notice Grants SLASHER_ROLE to an address\n * @dev Only callable by DEFAULT_ADMIN_ROLE. Slashers can propose and execute slashes\n * @param slasher Address to grant slashing permissions (must be non-zero)\n */\n function addSlasher(address slasher) external;\n\n /**\n * @notice Revokes SLASHER_ROLE from an address\n * @dev Only callable by DEFAULT_ADMIN_ROLE\n * @param slasher Address to revoke slashing permissions from\n */\n function removeSlasher(address slasher) external;\n\n /**\n * @notice Grants VERIFIER_ROLE to an address\n * @dev Only callable by DEFAULT_ADMIN_ROLE. Verifiers can validate proof-based slashes\n * @param verifier Address to grant verification permissions (must be non-zero)\n */\n function addVerifier(address verifier) external;\n\n /**\n * @notice Revokes VERIFIER_ROLE from an address\n * @dev Only callable by DEFAULT_ADMIN_ROLE\n * @param verifier Address to revoke verification permissions from\n */\n function removeVerifier(address verifier) external;\n\n // ======================\n // Slashing Functions\n // ======================\n\n /**\n * @notice Creates a new slash proposal against an operator\n * @dev Only callable by SLASHER_ROLE. Validates policy and proof if required\n * @param operator Address of the ciphernode operator to slash (must be non-zero)\n * @param reason Hash of the slash reason (must have an enabled policy configured)\n * @param proof Proof data to be verified (required if policy.requiresProof is true, can be empty otherwise)\n * @return proposalId Sequential ID of the created proposal\n * Requirements:\n * - operator must not be zero address\n * - reason must have an enabled policy configured\n * - If policy requires proof, proof must be non-empty and pass verification\n * - Caller must have SLASHER_ROLE\n */\n function proposeSlash(\n address operator,\n bytes32 reason,\n bytes calldata proof\n ) external returns (uint256 proposalId);\n\n /**\n * @notice Executes a slash proposal and applies penalties to the operator\n * @dev Only callable by SLASHER_ROLE. Validates execution conditions and applies slashing\n * @param proposalId ID of the proposal to execute (must exist and not be already executed)\n * Requirements:\n * - Proposal must exist and not be already executed\n * - For proof-required slashes: proof must be verified\n * - For evidence-based slashes: appeal window must have expired\n * - If appeal was filed and resolved, appeal must not have been upheld\n * - Caller must have SLASHER_ROLE\n * Effects:\n * - Marks proposal as executed\n * - Slashes ticket balance if ticketAmount > 0\n * - Slashes license bond if licenseAmount > 0\n * - Bans node if policy.banNode is true\n */\n function executeSlash(uint256 proposalId) external;\n\n // ======================\n // Appeal Functions\n // ======================\n\n /**\n * @notice Allows an operator to file an appeal against a slash proposal\n * @dev Only the operator being slashed can file an appeal, and only within the appeal window\n * @param proposalId ID of the proposal to appeal (must exist)\n * @param evidence String containing evidence and arguments supporting the appeal\n * Requirements:\n * - Proposal must exist\n * - Caller must be the operator being slashed\n * - Current timestamp must be before proposal.executableAt (within appeal window)\n * - Proposal must not already have an appeal filed\n */\n function fileAppeal(uint256 proposalId, string calldata evidence) external;\n\n /**\n * @notice Resolves an appeal by accepting or rejecting it\n * @dev Only callable by GOVERNANCE_ROLE. If appeal is upheld, the slash cannot be executed\n * @param proposalId ID of the proposal with the appeal to resolve (must exist and have an appeal)\n * @param appealUpheld True to uphold the appeal (cancel the slash), false to deny the appeal\n * (allow slash to proceed)\n * @param resolution String explaining the governance decision\n * Requirements:\n * - Proposal must exist and have an appeal filed\n * - Appeal must not already be resolved\n * - Caller must have GOVERNANCE_ROLE\n * Effects:\n * - Marks appeal as resolved\n * - Sets appealUpheld flag (true = slash cancelled, false = slash can proceed)\n */\n function resolveAppeal(\n uint256 proposalId,\n bool appealUpheld,\n string calldata resolution\n ) external;\n\n // ======================\n // Ban Management\n // ======================\n\n /**\n * @notice Bans or unbans a node from the network\n * @dev Only callable by GOVERNANCE_ROLE. Bans can also occur automatically via executeSlash\n * @param node Address of the node to ban (must be non-zero)\n * @param status Whether to ban the node\n * @param reason Hash of the reason for banning\n * Requirements:\n * - node must not be zero address\n * - Caller must have GOVERNANCE_ROLE\n * Effects:\n * - Sets banned[node] to status\n * - Emits NodeBanned event if status is true\n * - Emits NodeUnbanned event if status is false\n */\n function updateBanStatus(\n address node,\n bool status,\n bytes32 reason\n ) external;\n}\n"
|
|
@@ -274,16 +286,16 @@
|
|
|
274
286
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\n\npragma solidity >=0.8.27;\n\n/**\n * @title ExitQueueLib\n * @notice Library for managing time-locked exit queues for tickets and licenses\n * @dev Implements a queue system where assets are locked for a delay period before they can be claimed or slashed.\n * Assets are organized into tranches based on unlock timestamps, allowing efficient batch operations.\n */\nlibrary ExitQueueLib {\n /**\n * @notice Represents a single tranche of assets with a specific unlock timestamp\n * @dev Multiple assets queued at the same time are merged into the same tranche for efficiency\n * @param unlockTimestamp The timestamp when assets in this tranche become claimable\n * @param ticketAmount The amount of tickets in this tranche\n * @param licenseAmount The amount of licenses in this tranche\n */\n struct ExitTranche {\n uint64 unlockTimestamp;\n uint256 ticketAmount;\n uint256 licenseAmount;\n }\n\n /**\n * @notice Tracks total pending amounts for an operator across all tranches\n * @param ticketAmount Total pending tickets waiting in the exit queue\n * @param licenseAmount Total pending licenses waiting in the exit queue\n */\n struct PendingAmounts {\n uint256 ticketAmount;\n uint256 licenseAmount;\n }\n\n /**\n * @notice Main state structure for the exit queue system\n * @dev Contains all per-operator queue data and pending totals\n * @param operatorQueues Maps operator addresses to their arrays of exit tranches\n * @param queueHeadIndex Maps operator addresses to the current head index (for efficient cleanup)\n * @param pendingTotals Maps operator addresses to their total pending amounts\n */\n struct ExitQueueState {\n mapping(address operator => ExitTranche[] operatorQueues) operatorQueues;\n mapping(address operator => uint256 queueHeadIndex) queueHeadIndex;\n mapping(address operator => PendingAmounts operatorPendings) pendingTotals;\n }\n\n /**\n * @notice Types of assets that can be queued for exit\n * @dev Used internally to differentiate between ticket and license operations\n */\n enum AssetType {\n Ticket,\n License\n }\n\n /**\n * @notice Emitted when assets are queued for exit\n * @param operator The operator whose assets were queued\n * @param ticketAmount The amount of tickets queued\n * @param licenseAmount The amount of licenses queued\n * @param unlockTimestamp The timestamp when these assets will become claimable\n */\n event AssetsQueuedForExit(\n address indexed operator,\n uint256 ticketAmount,\n uint256 licenseAmount,\n uint64 unlockTimestamp\n );\n\n /**\n * @notice Emitted when assets are claimed from the exit queue\n * @param operator The operator who claimed the assets\n * @param ticketAmount The amount of tickets claimed\n * @param licenseAmount The amount of licenses claimed\n */\n event AssetsClaimed(\n address indexed operator,\n uint256 ticketAmount,\n uint256 licenseAmount\n );\n\n /**\n * @notice Emitted when pending assets are slashed\n * @param operator The operator whose assets were slashed\n * @param ticketAmount The amount of tickets slashed\n * @param licenseAmount The amount of licenses slashed\n * @param includedLockedAssets Whether locked (not yet unlocked) assets were included in the slash\n */\n event PendingAssetsSlashed(\n address indexed operator,\n uint256 ticketAmount,\n uint256 licenseAmount,\n bool includedLockedAssets\n );\n\n /// @notice Thrown when attempting to queue zero amount of both asset types\n error ZeroAmountNotAllowed();\n\n /// @notice Thrown when timestamp calculation would overflow uint64\n error TimestampOverflow();\n\n /// @notice Thrown when accessing an invalid queue index\n error IndexOutOfBounds();\n\n /**\n * @notice Queues both tickets and licenses for exit with a time delay\n * @dev Assets are added to the operator's queue and will be claimable after exitDelaySeconds.\n * If a tranche with the same unlock timestamp already exists, amounts are merged into it.\n * @param state The exit queue state storage\n * @param operator The operator whose assets are being queued\n * @param exitDelaySeconds The number of seconds until assets become claimable\n * @param ticketAmount The amount of tickets to queue (can be 0)\n * @param licenseAmount The amount of licenses to queue (can be 0)\n */\n function queueAssetsForExit(\n ExitQueueState storage state,\n address operator,\n uint64 exitDelaySeconds,\n uint256 ticketAmount,\n uint256 licenseAmount\n ) internal {\n if (ticketAmount == 0 && licenseAmount == 0) {\n return;\n }\n\n uint64 currentTimestamp = uint64(block.timestamp);\n require(\n currentTimestamp <= (type(uint64).max - exitDelaySeconds),\n TimestampOverflow()\n );\n uint64 unlockTimestamp = currentTimestamp + exitDelaySeconds;\n\n ExitTranche[] storage operatorQueue = state.operatorQueues[operator];\n\n uint256 len = operatorQueue.length;\n bool merged;\n if (len != 0) {\n ExitTranche storage lastTranche = operatorQueue[len - 1];\n if (lastTranche.unlockTimestamp == unlockTimestamp) {\n if (ticketAmount != 0) lastTranche.ticketAmount += ticketAmount;\n if (licenseAmount != 0) {\n lastTranche.licenseAmount += licenseAmount;\n }\n merged = true;\n }\n }\n\n if (!merged) {\n ExitTranche storage t = operatorQueue.push();\n t.unlockTimestamp = unlockTimestamp;\n t.ticketAmount = ticketAmount;\n t.licenseAmount = licenseAmount;\n }\n\n _updatePendingTotals(\n state,\n operator,\n ticketAmount,\n licenseAmount,\n true\n );\n\n emit AssetsQueuedForExit(\n operator,\n ticketAmount,\n licenseAmount,\n unlockTimestamp\n );\n }\n\n /**\n * @notice Queues only tickets for exit with a time delay\n * @dev Convenience function that calls queueAssetsForExit with licenseAmount = 0\n * @param state The exit queue state storage\n * @param operator The operator whose tickets are being queued\n * @param exitDelaySeconds The number of seconds until tickets become claimable\n * @param ticketAmount The amount of tickets to queue\n */\n function queueTicketsForExit(\n ExitQueueState storage state,\n address operator,\n uint64 exitDelaySeconds,\n uint256 ticketAmount\n ) internal {\n queueAssetsForExit(state, operator, exitDelaySeconds, ticketAmount, 0);\n }\n\n /**\n * @notice Queues only licenses for exit with a time delay\n * @dev Convenience function that calls queueAssetsForExit with ticketAmount = 0\n * @param state The exit queue state storage\n * @param operator The operator whose licenses are being queued\n * @param exitDelaySeconds The number of seconds until licenses become claimable\n * @param licenseAmount The amount of licenses to queue\n */\n function queueLicensesForExit(\n ExitQueueState storage state,\n address operator,\n uint64 exitDelaySeconds,\n uint256 licenseAmount\n ) internal {\n queueAssetsForExit(state, operator, exitDelaySeconds, 0, licenseAmount);\n }\n\n /**\n * @notice Gets the total pending amounts for an operator across all tranches\n * @dev Returns both locked (not yet claimable) and unlocked (claimable) amounts\n * @param state The exit queue state storage\n * @param operator The operator to query\n * @return ticketAmount Total pending tickets in the exit queue\n * @return licenseAmount Total pending licenses in the exit queue\n */\n function getPendingAmounts(\n ExitQueueState storage state,\n address operator\n ) internal view returns (uint256 ticketAmount, uint256 licenseAmount) {\n PendingAmounts storage pending = state.pendingTotals[operator];\n return (pending.ticketAmount, pending.licenseAmount);\n }\n\n /**\n * @notice Previews the amounts that can be claimed at the current block timestamp\n * @dev Iterates through tranches and sums up amounts where unlock timestamp has passed\n * @param state The exit queue state storage\n * @param operator The operator to query\n * @return ticketAmount Total claimable tickets at current timestamp\n * @return licenseAmount Total claimable licenses at current timestamp\n */\n function previewClaimableAmounts(\n ExitQueueState storage state,\n address operator\n ) internal view returns (uint256 ticketAmount, uint256 licenseAmount) {\n ExitTranche[] storage operatorQueue = state.operatorQueues[operator];\n uint256 currentIndex = state.queueHeadIndex[operator];\n\n for (uint256 i = currentIndex; i < operatorQueue.length; i++) {\n ExitTranche storage tranche = operatorQueue[i];\n\n if (block.timestamp < tranche.unlockTimestamp) {\n break;\n }\n\n ticketAmount += tranche.ticketAmount;\n licenseAmount += tranche.licenseAmount;\n }\n }\n\n /**\n * @notice Claims unlocked assets from the exit queue\n * @dev Only processes tranches where unlock timestamp has passed. Updates pending totals\n * and cleans up empty tranches.\n * @param state The exit queue state storage\n * @param operator The operator claiming assets\n * @param maxTicketAmount Maximum tickets to claim (actual claimed may be less if queue has fewer)\n * @param maxLicenseAmount Maximum licenses to claim (actual claimed may be less if queue has fewer)\n * @return ticketsClaimed Actual amount of tickets claimed\n * @return licensesClaimed Actual amount of licenses claimed\n */\n function claimAssets(\n ExitQueueState storage state,\n address operator,\n uint256 maxTicketAmount,\n uint256 maxLicenseAmount\n ) internal returns (uint256 ticketsClaimed, uint256 licensesClaimed) {\n if (maxTicketAmount > 0) {\n ticketsClaimed = _takeAssetsFromQueue(\n state,\n operator,\n maxTicketAmount,\n AssetType.Ticket,\n false\n );\n if (ticketsClaimed > 0) {\n state.pendingTotals[operator].ticketAmount -= ticketsClaimed;\n }\n }\n\n if (maxLicenseAmount > 0) {\n licensesClaimed = _takeAssetsFromQueue(\n state,\n operator,\n maxLicenseAmount,\n AssetType.License,\n false\n );\n if (licensesClaimed > 0) {\n state.pendingTotals[operator].licenseAmount -= licensesClaimed;\n }\n }\n\n if (ticketsClaimed > 0 || licensesClaimed > 0) {\n _cleanupEmptyTranches(state, operator);\n emit AssetsClaimed(operator, ticketsClaimed, licensesClaimed);\n }\n }\n\n /**\n * @notice Slashes pending assets from the exit queue\n * @dev Can optionally include locked (not yet unlocked) assets. Updates pending totals\n * and cleans up empty tranches.\n * @param state The exit queue state storage\n * @param operator The operator whose assets are being slashed\n * @param ticketAmountToSlash Maximum tickets to slash\n * @param licenseAmountToSlash Maximum licenses to slash\n * @param includeLockedAssets If true, slashes locked assets; if false, only slashes unlocked assets\n * @return ticketsSlashed Actual amount of tickets slashed\n * @return licensesSlashed Actual amount of licenses slashed\n */\n function slashPendingAssets(\n ExitQueueState storage state,\n address operator,\n uint256 ticketAmountToSlash,\n uint256 licenseAmountToSlash,\n bool includeLockedAssets\n ) internal returns (uint256 ticketsSlashed, uint256 licensesSlashed) {\n if (ticketAmountToSlash > 0) {\n ticketsSlashed = _takeAssetsFromQueue(\n state,\n operator,\n ticketAmountToSlash,\n AssetType.Ticket,\n includeLockedAssets\n );\n if (ticketsSlashed > 0) {\n state.pendingTotals[operator].ticketAmount -= ticketsSlashed;\n }\n }\n\n if (licenseAmountToSlash > 0) {\n licensesSlashed = _takeAssetsFromQueue(\n state,\n operator,\n licenseAmountToSlash,\n AssetType.License,\n includeLockedAssets\n );\n if (licensesSlashed > 0) {\n state.pendingTotals[operator].licenseAmount -= licensesSlashed;\n }\n }\n\n if (ticketsSlashed > 0 || licensesSlashed > 0) {\n _cleanupEmptyTranches(state, operator);\n emit PendingAssetsSlashed(\n operator,\n ticketsSlashed,\n licensesSlashed,\n includeLockedAssets\n );\n }\n }\n\n /**\n * @notice Updates the pending totals for an operator\n * @dev Internal helper to increase or decrease pending amounts. Uses bitwise OR for efficient zero check.\n * @param state The exit queue state storage\n * @param operator The operator whose pending totals are being updated\n * @param ticketAmountDelta The change in ticket amount\n * @param licenseAmountDelta The change in license amount\n * @param isIncrease If true, increases totals; if false, decreases totals\n */\n function _updatePendingTotals(\n ExitQueueState storage state,\n address operator,\n uint256 ticketAmountDelta,\n uint256 licenseAmountDelta,\n bool isIncrease\n ) private {\n if ((ticketAmountDelta | licenseAmountDelta) == 0) return;\n\n PendingAmounts storage pending = state.pendingTotals[operator];\n\n if (isIncrease) {\n if (ticketAmountDelta != 0) {\n pending.ticketAmount += ticketAmountDelta;\n }\n if (licenseAmountDelta != 0) {\n pending.licenseAmount += licenseAmountDelta;\n }\n } else {\n if (ticketAmountDelta != 0) {\n pending.ticketAmount -= ticketAmountDelta;\n }\n if (licenseAmountDelta != 0) {\n pending.licenseAmount -= licenseAmountDelta;\n }\n }\n }\n\n /**\n * @notice Cleans up empty tranches from the head of the queue\n * @dev Advances the queue head index past all tranches with zero tickets and licenses.\n * This prevents the queue from growing unbounded and reduces gas costs for future operations.\n * @param state The exit queue state storage\n * @param operator The operator whose queue is being cleaned up\n */\n function _cleanupEmptyTranches(\n ExitQueueState storage state,\n address operator\n ) private {\n ExitTranche[] storage operatorQueue = state.operatorQueues[operator];\n uint256 currentIndex = state.queueHeadIndex[operator];\n\n while (currentIndex < operatorQueue.length) {\n ExitTranche storage tranche = operatorQueue[currentIndex];\n if (tranche.ticketAmount == 0 && tranche.licenseAmount == 0) {\n currentIndex++;\n } else {\n break;\n }\n }\n\n state.queueHeadIndex[operator] = currentIndex;\n }\n\n /**\n * @notice Takes assets from the queue, either for claiming or slashing\n * @dev Iterates through tranches from head to tail, taking assets up to wantedAmount.\n * Respects unlock timestamps unless includeLockedAssets is true.\n * @param state The exit queue state storage\n * @param operator The operator whose assets are being taken\n * @param wantedAmount The maximum amount to take\n * @param assetType Whether to take tickets or licenses\n * @param includeLockedAssets If true, takes locked assets; if false, only takes unlocked assets\n * @return takenAmount The actual amount taken (may be less than wantedAmount if queue has fewer assets)\n */\n function _takeAssetsFromQueue(\n ExitQueueState storage state,\n address operator,\n uint256 wantedAmount,\n AssetType assetType,\n bool includeLockedAssets\n ) private returns (uint256 takenAmount) {\n if (wantedAmount == 0) {\n return 0;\n }\n\n ExitTranche[] storage operatorQueue = state.operatorQueues[operator];\n uint256 currentIndex = state.queueHeadIndex[operator];\n uint256 queueLength = operatorQueue.length;\n uint256 remainingWanted = wantedAmount;\n\n while (remainingWanted > 0 && currentIndex < queueLength) {\n ExitTranche storage tranche = operatorQueue[currentIndex];\n\n if (\n !includeLockedAssets &&\n block.timestamp < tranche.unlockTimestamp\n ) {\n break;\n }\n\n uint256 availableAmount;\n if (assetType == AssetType.Ticket) {\n availableAmount = tranche.ticketAmount;\n } else {\n availableAmount = tranche.licenseAmount;\n }\n\n if (availableAmount == 0) {\n currentIndex++;\n continue;\n }\n\n uint256 amountToTake = remainingWanted < availableAmount\n ? remainingWanted\n : availableAmount;\n\n if (assetType == AssetType.Ticket) {\n tranche.ticketAmount -= amountToTake;\n } else {\n tranche.licenseAmount -= amountToTake;\n }\n\n remainingWanted -= amountToTake;\n takenAmount += amountToTake;\n\n if (tranche.ticketAmount == 0 && tranche.licenseAmount == 0) {\n currentIndex++;\n }\n }\n\n state.queueHeadIndex[operator] = currentIndex;\n }\n}\n"
|
|
275
287
|
},
|
|
276
288
|
"project/contracts/registry/BondingRegistry.sol": {
|
|
277
|
-
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\n\npragma solidity >=0.8.27;\n\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { ExitQueueLib } from \"../lib/ExitQueueLib.sol\";\n\nimport { IBondingRegistry } from \"../interfaces/IBondingRegistry.sol\";\nimport { ICiphernodeRegistry } from \"../interfaces/ICiphernodeRegistry.sol\";\nimport { ISlashingManager } from \"../interfaces/ISlashingManager.sol\";\nimport { EnclaveTicketToken } from \"../token/EnclaveTicketToken.sol\";\n\n/**\n * @title BondingRegistry\n * @notice Implementation of the bonding registry managing operator ticket balances and license bonds\n * @dev Handles deposits, withdrawals, slashing, exits, and integrates with registry and slashing manager\n */\ncontract BondingRegistry is IBondingRegistry, OwnableUpgradeable {\n using SafeERC20 for IERC20;\n using ExitQueueLib for ExitQueueLib.ExitQueueState;\n\n // ======================\n // Constants\n // ======================\n\n /// @dev Reason code for ticket balance deposits\n bytes32 private constant REASON_DEPOSIT = bytes32(\"DEPOSIT\");\n\n /// @dev Reason code for ticket balance withdrawals\n bytes32 private constant REASON_WITHDRAW = bytes32(\"WITHDRAW\");\n\n /// @dev Reason code for license bond operations\n bytes32 private constant REASON_BOND = bytes32(\"BOND\");\n\n /// @dev Reason code for license unbond operations\n bytes32 private constant REASON_UNBOND = bytes32(\"UNBOND\");\n\n // ======================\n // Storage\n // ======================\n\n /// @notice Ticket token (ETK with underlying USDC) used for collateral\n EnclaveTicketToken public ticketToken;\n\n /// @notice License token (ENCL) required for operator registration\n IERC20 public licenseToken;\n\n /// @notice Registry contract for managing committee membership\n ICiphernodeRegistry public registry;\n\n /// @notice Address authorized to perform slashing operations\n address public slashingManager;\n\n /// @notice Address authorized to distribute rewards to operators\n address public rewardDistributor;\n\n /// @notice Treasury address that receives slashed funds\n address public slashedFundsTreasury;\n\n /// @notice Price per ticket in ticket token units\n uint256 public ticketPrice;\n\n /// @notice Minimum license bond required for initial registration\n uint256 public licenseRequiredBond;\n\n /// @notice Minimum number of tickets required to maintain active status\n uint256 public minTicketBalance;\n\n /// @notice Time delay in seconds before exits can be claimed\n uint64 public exitDelay;\n\n /// @notice Percentage (in basis points) of license bond that must remain bonded to stay active\n /// @dev Default 8000 = 80%. Allows operators to unbond up to 20% while remaining active\n uint256 public licenseActiveBps;\n\n /// @notice Operator state data structure\n /// @param licenseBond Amount of license tokens currently bonded\n /// @param exitUnlocksAt Timestamp when pending exit can be claimed\n /// @param registered Whether operator is registered in the protocol\n /// @param exitRequested Whether operator has requested to exit\n /// @param active Whether operator meets all requirements for active status\n struct Operator {\n uint256 licenseBond;\n uint64 exitUnlocksAt;\n bool registered;\n bool exitRequested;\n bool active;\n }\n\n /// @notice Maps operator address to their state data\n mapping(address operator => Operator data) internal operators;\n\n /// @notice Total slashed ticket balance available for treasury withdrawal\n uint256 public slashedTicketBalance;\n\n /// @notice Total slashed license bond available for treasury withdrawal\n uint256 public slashedLicenseBond;\n\n // ======================\n // Exit Queue library state\n // ======================\n\n /// @dev Internal state for managing exit queue of tickets and licenses\n ExitQueueLib.ExitQueueState private _exits;\n\n // ======================\n // Modifiers\n // ======================\n\n /// @dev Restricts function access to only the slashing manager\n modifier onlySlashingManager() {\n if (msg.sender != slashingManager) revert Unauthorized();\n _;\n }\n\n /// @dev Reverts if operator has an exit in progress that hasn't unlocked yet\n /// @param operator Address of the operator to check\n modifier noExitInProgress(address operator) {\n Operator memory op = operators[operator];\n if (op.exitRequested && block.timestamp < op.exitUnlocksAt) {\n revert ExitInProgress();\n }\n _;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Initialization //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Constructor that disables initializers.\n /// @dev Prevents the implementation contract from being initialized. Initialization is performed\n /// via the initialize() function when deployed behind a proxy.\n constructor() {\n _disableInitializers();\n }\n\n /// @notice Initializes the bonding registry contract\n /// @dev Can only be called once due to initializer modifier\n /// @param _owner Address that will own the contract\n /// @param _ticketToken Ticket token contract for collateral\n /// @param _licenseToken License token contract for bonding\n /// @param _registry Ciphernode registry contract\n /// @param _slashedFundsTreasury Address to receive slashed funds\n /// @param _ticketPrice Initial price per ticket\n /// @param _licenseRequiredBond Initial required license bond for registration\n /// @param _minTicketBalance Initial minimum ticket balance for activation\n /// @param _exitDelay Initial exit delay period in seconds\n function initialize(\n address _owner,\n EnclaveTicketToken _ticketToken,\n IERC20 _licenseToken,\n ICiphernodeRegistry _registry,\n address _slashedFundsTreasury,\n uint256 _ticketPrice,\n uint256 _licenseRequiredBond,\n uint256 _minTicketBalance,\n uint64 _exitDelay\n ) public initializer {\n __Ownable_init(msg.sender);\n setTicketToken(_ticketToken);\n setLicenseToken(_licenseToken);\n setRegistry(_registry);\n setSlashedFundsTreasury(_slashedFundsTreasury);\n setTicketPrice(_ticketPrice);\n setLicenseRequiredBond(_licenseRequiredBond);\n setMinTicketBalance(_minTicketBalance);\n setExitDelay(_exitDelay);\n setLicenseActiveBps(8_000);\n if (_owner != owner()) transferOwnership(_owner);\n }\n\n // ======================\n // View Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function getLicenseToken() external view returns (address) {\n return address(licenseToken);\n }\n\n /// @inheritdoc IBondingRegistry\n function getTicketToken() external view returns (address) {\n return address(ticketToken);\n }\n\n /// @inheritdoc IBondingRegistry\n function getTicketBalance(\n address operator\n ) external view returns (uint256) {\n return ticketToken.balanceOf(operator);\n }\n\n /// @inheritdoc IBondingRegistry\n function getLicenseBond(address operator) external view returns (uint256) {\n return operators[operator].licenseBond;\n }\n\n /// @inheritdoc IBondingRegistry\n function availableTickets(\n address operator\n ) external view returns (uint256) {\n return ticketToken.balanceOf(operator) / ticketPrice;\n }\n\n /// @notice Get operator's ticket balance at a specific block\n /// @dev Uses checkpoint mechanism from ticket token\n /// @param operator Address of the operator\n /// @param blockNumber Block number to query\n /// @return Ticket balance at the specified block\n function getTicketBalanceAtBlock(\n address operator,\n uint256 blockNumber\n ) external view returns (uint256) {\n return ticketToken.getPastVotes(operator, blockNumber);\n }\n\n /// @notice Get operator's total pending exit amounts\n /// @param operator Address of the operator\n /// @return ticket Total pending ticket balance in exit queue\n /// @return license Total pending license bond in exit queue\n function pendingExits(\n address operator\n ) external view returns (uint256 ticket, uint256 license) {\n return _exits.getPendingAmounts(operator);\n }\n\n /// @notice Preview how much an operator can currently claim\n /// @param operator Address of the operator\n /// @return ticket Claimable ticket balance\n /// @return license Claimable license bond\n function previewClaimable(\n address operator\n ) external view returns (uint256 ticket, uint256 license) {\n return _exits.previewClaimableAmounts(operator);\n }\n\n /// @inheritdoc IBondingRegistry\n function isLicensed(address operator) external view returns (bool) {\n return operators[operator].licenseBond >= _minLicenseBond();\n }\n\n /// @inheritdoc IBondingRegistry\n function isRegistered(address operator) external view returns (bool) {\n return operators[operator].registered;\n }\n\n /// @inheritdoc IBondingRegistry\n function isActive(address operator) external view returns (bool) {\n return operators[operator].active;\n }\n\n /// @inheritdoc IBondingRegistry\n function hasExitInProgress(address operator) external view returns (bool) {\n Operator memory op = operators[operator];\n return op.exitRequested && block.timestamp < op.exitUnlocksAt;\n }\n\n // ======================\n // Operator Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function registerOperator() external noExitInProgress(msg.sender) {\n // Clear previous exit request\n if (operators[msg.sender].exitRequested) {\n operators[msg.sender].exitRequested = false;\n operators[msg.sender].exitUnlocksAt = 0;\n }\n\n require(\n !ISlashingManager(slashingManager).isBanned(msg.sender),\n CiphernodeBanned()\n );\n require(!operators[msg.sender].registered, AlreadyRegistered());\n require(\n operators[msg.sender].licenseBond >= licenseRequiredBond,\n NotLicensed()\n );\n\n operators[msg.sender].registered = true;\n\n // CiphernodeRegistry already emits an event when a ciphernode is added\n registry.addCiphernode(msg.sender);\n\n _updateOperatorStatus(msg.sender);\n }\n\n /// @inheritdoc IBondingRegistry\n function deregisterOperator(\n uint256[] calldata siblingNodes\n ) external noExitInProgress(msg.sender) {\n Operator storage op = operators[msg.sender];\n require(op.registered, NotRegistered());\n\n op.registered = false;\n op.exitRequested = true;\n op.exitUnlocksAt = uint64(block.timestamp) + exitDelay;\n\n uint256 ticketOut = ticketToken.balanceOf(msg.sender);\n uint256 licenseOut = op.licenseBond;\n if (ticketOut != 0) {\n ticketToken.burnTickets(msg.sender, ticketOut);\n emit TicketBalanceUpdated(\n msg.sender,\n -int256(ticketOut),\n 0,\n REASON_WITHDRAW\n );\n }\n if (licenseOut != 0) {\n op.licenseBond = 0;\n emit LicenseBondUpdated(\n msg.sender,\n -int256(licenseOut),\n 0,\n REASON_UNBOND\n );\n }\n\n if (ticketOut != 0 || licenseOut != 0) {\n _exits.queueAssetsForExit(\n msg.sender,\n exitDelay,\n ticketOut,\n licenseOut\n );\n }\n\n // CiphernodeRegistry already emits an event when a ciphernode is removed\n registry.removeCiphernode(msg.sender, siblingNodes);\n\n emit CiphernodeDeregistrationRequested(msg.sender, op.exitUnlocksAt);\n _updateOperatorStatus(msg.sender);\n }\n\n /// @inheritdoc IBondingRegistry\n function addTicketBalance(\n uint256 amount\n ) external noExitInProgress(msg.sender) {\n require(amount != 0, ZeroAmount());\n require(operators[msg.sender].registered, NotRegistered());\n\n ticketToken.depositFrom(msg.sender, msg.sender, amount);\n\n emit TicketBalanceUpdated(\n msg.sender,\n int256(amount),\n ticketToken.balanceOf(msg.sender),\n REASON_DEPOSIT\n );\n\n _updateOperatorStatus(msg.sender);\n }\n\n /// @inheritdoc IBondingRegistry\n function removeTicketBalance(\n uint256 amount\n ) external noExitInProgress(msg.sender) {\n require(amount != 0, ZeroAmount());\n require(operators[msg.sender].registered, NotRegistered());\n require(\n ticketToken.balanceOf(msg.sender) >= amount,\n InsufficientBalance()\n );\n\n ticketToken.burnTickets(msg.sender, amount);\n _exits.queueTicketsForExit(msg.sender, exitDelay, amount);\n\n emit TicketBalanceUpdated(\n msg.sender,\n -int256(amount),\n ticketToken.balanceOf(msg.sender),\n REASON_WITHDRAW\n );\n\n _updateOperatorStatus(msg.sender);\n }\n\n /// @inheritdoc IBondingRegistry\n function bondLicense(uint256 amount) external noExitInProgress(msg.sender) {\n require(amount != 0, ZeroAmount());\n\n uint256 balanceBefore = licenseToken.balanceOf(address(this));\n licenseToken.safeTransferFrom(msg.sender, address(this), amount);\n uint256 actualReceived = licenseToken.balanceOf(address(this)) -\n balanceBefore;\n\n operators[msg.sender].licenseBond += actualReceived;\n\n emit LicenseBondUpdated(\n msg.sender,\n int256(actualReceived),\n operators[msg.sender].licenseBond,\n REASON_BOND\n );\n\n _updateOperatorStatus(msg.sender);\n }\n\n /// @inheritdoc IBondingRegistry\n function unbondLicense(\n uint256 amount\n ) external noExitInProgress(msg.sender) {\n require(amount != 0, ZeroAmount());\n require(\n operators[msg.sender].licenseBond >= amount,\n InsufficientBalance()\n );\n\n operators[msg.sender].licenseBond -= amount;\n _exits.queueLicensesForExit(msg.sender, exitDelay, amount);\n\n emit LicenseBondUpdated(\n msg.sender,\n -int256(amount),\n operators[msg.sender].licenseBond,\n REASON_UNBOND\n );\n\n _updateOperatorStatus(msg.sender);\n }\n\n // ======================\n // Claim Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function claimExits(\n uint256 maxTicketAmount,\n uint256 maxLicenseAmount\n ) external {\n (uint256 ticketClaim, uint256 licenseClaim) = _exits.claimAssets(\n msg.sender,\n maxTicketAmount,\n maxLicenseAmount\n );\n require(ticketClaim > 0 || licenseClaim > 0, ExitNotReady());\n\n if (ticketClaim > 0) ticketToken.payout(msg.sender, ticketClaim);\n if (licenseClaim > 0) {\n licenseToken.safeTransfer(msg.sender, licenseClaim);\n }\n }\n\n // ======================\n // Slashing Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function slashTicketBalance(\n address operator,\n uint256 requestedSlashAmount,\n bytes32 slashReason\n ) external onlySlashingManager {\n require(requestedSlashAmount != 0, ZeroAmount());\n\n (uint256 pendingTicketBalance, ) = _exits.getPendingAmounts(operator);\n uint256 activeBalance = ticketToken.balanceOf(operator);\n uint256 totalAvailableBalance = activeBalance + pendingTicketBalance;\n\n uint256 actualSlashAmount = Math.min(\n requestedSlashAmount,\n totalAvailableBalance\n );\n\n if (actualSlashAmount == 0) {\n return;\n }\n\n // Slash from active balance first\n uint256 slashedFromActiveBalance = Math.min(\n actualSlashAmount,\n activeBalance\n );\n if (slashedFromActiveBalance > 0) {\n ticketToken.burnTickets(operator, slashedFromActiveBalance);\n }\n\n // Slash remaining amount from pending queue\n uint256 remainingToSlash = actualSlashAmount - slashedFromActiveBalance;\n if (remainingToSlash > 0) {\n _exits.slashPendingAssets(\n operator,\n remainingToSlash,\n 0, // licenseAmount\n true\n );\n }\n\n slashedTicketBalance += actualSlashAmount;\n emit TicketBalanceUpdated(\n operator,\n -int256(actualSlashAmount),\n ticketToken.balanceOf(operator),\n slashReason\n );\n\n _updateOperatorStatus(operator);\n }\n\n /// @inheritdoc IBondingRegistry\n function slashLicenseBond(\n address operator,\n uint256 requestedSlashAmount,\n bytes32 slashReason\n ) external onlySlashingManager {\n require(requestedSlashAmount != 0, ZeroAmount());\n\n Operator storage operatorData = operators[operator];\n (, uint256 pendingLicenseBalance) = _exits.getPendingAmounts(operator);\n uint256 totalAvailableBalance = operatorData.licenseBond +\n pendingLicenseBalance;\n uint256 actualSlashAmount = Math.min(\n requestedSlashAmount,\n totalAvailableBalance\n );\n\n if (actualSlashAmount == 0) return;\n\n // Slash from active balance first\n uint256 slashedFromActiveBalance = Math.min(\n actualSlashAmount,\n operatorData.licenseBond\n );\n if (slashedFromActiveBalance > 0) {\n operatorData.licenseBond -= slashedFromActiveBalance;\n }\n\n // Slash remaining amount from pending queue\n uint256 remainingToSlash = actualSlashAmount - slashedFromActiveBalance;\n if (remainingToSlash > 0) {\n _exits.slashPendingAssets(\n operator,\n 0, // ticketAmount\n remainingToSlash,\n true\n );\n }\n\n slashedLicenseBond += actualSlashAmount;\n emit LicenseBondUpdated(\n operator,\n -int256(actualSlashAmount),\n operatorData.licenseBond,\n slashReason\n );\n\n _updateOperatorStatus(operator);\n }\n\n // ======================\n // Reward Distribution Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function distributeRewards(\n IERC20 rewardToken,\n address[] calldata recipients,\n uint256[] calldata amounts\n ) external {\n require(msg.sender == rewardDistributor, OnlyRewardDistributor());\n require(recipients.length == amounts.length, ArrayLengthMismatch());\n\n uint256 len = recipients.length;\n for (uint256 i = 0; i < len; i++) {\n if (amounts[i] > 0 && operators[recipients[i]].registered) {\n rewardToken.safeTransferFrom(\n rewardDistributor,\n recipients[i],\n amounts[i]\n );\n }\n }\n }\n\n // ======================\n // Admin Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function setTicketPrice(uint256 newTicketPrice) public onlyOwner {\n require(newTicketPrice != 0, InvalidConfiguration());\n\n uint256 oldValue = ticketPrice;\n ticketPrice = newTicketPrice;\n\n emit ConfigurationUpdated(\"ticketPrice\", oldValue, newTicketPrice);\n }\n\n /// @inheritdoc IBondingRegistry\n function setLicenseRequiredBond(\n uint256 newLicenseRequiredBond\n ) public onlyOwner {\n require(newLicenseRequiredBond != 0, InvalidConfiguration());\n\n uint256 oldValue = licenseRequiredBond;\n licenseRequiredBond = newLicenseRequiredBond;\n\n emit ConfigurationUpdated(\n \"licenseRequiredBond\",\n oldValue,\n newLicenseRequiredBond\n );\n }\n\n /// @inheritdoc IBondingRegistry\n function setLicenseActiveBps(uint256 newBps) public onlyOwner {\n require(newBps > 0 && newBps <= 10_000, InvalidConfiguration());\n\n uint256 oldValue = licenseActiveBps;\n licenseActiveBps = newBps;\n\n emit ConfigurationUpdated(\"licenseActiveBps\", oldValue, newBps);\n }\n\n /// @inheritdoc IBondingRegistry\n function setMinTicketBalance(uint256 newMinTicketBalance) public onlyOwner {\n uint256 oldValue = minTicketBalance;\n minTicketBalance = newMinTicketBalance;\n\n emit ConfigurationUpdated(\n \"minTicketBalance\",\n oldValue,\n newMinTicketBalance\n );\n }\n\n /// @inheritdoc IBondingRegistry\n function setExitDelay(uint64 newExitDelay) public onlyOwner {\n uint256 oldValue = uint256(exitDelay);\n exitDelay = newExitDelay;\n\n emit ConfigurationUpdated(\"exitDelay\", oldValue, uint256(newExitDelay));\n }\n\n /// @inheritdoc IBondingRegistry\n function setSlashedFundsTreasury(\n address newSlashedFundsTreasury\n ) public onlyOwner {\n require(newSlashedFundsTreasury != address(0), ZeroAddress());\n slashedFundsTreasury = newSlashedFundsTreasury;\n }\n\n /// @inheritdoc IBondingRegistry\n function setTicketToken(\n EnclaveTicketToken newTicketToken\n ) public onlyOwner {\n ticketToken = newTicketToken;\n }\n\n /// @inheritdoc IBondingRegistry\n function setLicenseToken(IERC20 newLicenseToken) public onlyOwner {\n licenseToken = newLicenseToken;\n }\n\n /// @inheritdoc IBondingRegistry\n function setRegistry(ICiphernodeRegistry newRegistry) public onlyOwner {\n registry = newRegistry;\n }\n\n /// @inheritdoc IBondingRegistry\n function setSlashingManager(address newSlashingManager) public onlyOwner {\n slashingManager = newSlashingManager;\n }\n\n /// @notice Sets the reward distributor address\n /// @dev Only callable by owner\n /// @param newRewardDistributor Address of the reward distributor\n function setRewardDistributor(\n address newRewardDistributor\n ) public onlyOwner {\n rewardDistributor = newRewardDistributor;\n }\n\n /// @inheritdoc IBondingRegistry\n function withdrawSlashedFunds(\n uint256 ticketAmount,\n uint256 licenseAmount\n ) public onlyOwner {\n require(ticketAmount <= slashedTicketBalance, InsufficientBalance());\n require(licenseAmount <= slashedLicenseBond, InsufficientBalance());\n\n if (ticketAmount > 0) {\n slashedTicketBalance -= ticketAmount;\n ticketToken.payout(slashedFundsTreasury, ticketAmount);\n }\n\n if (licenseAmount > 0) {\n slashedLicenseBond -= licenseAmount;\n licenseToken.safeTransfer(slashedFundsTreasury, licenseAmount);\n }\n\n emit SlashedFundsWithdrawn(\n slashedFundsTreasury,\n ticketAmount,\n licenseAmount\n );\n }\n\n // ======================\n // Internal Functions\n // ======================\n\n /// @dev Updates operator's active status based on current conditions\n /// @dev Operator is active if: registered, has minimum license bond, and has minimum tickets\n /// @param operator Address of the operator to update\n function _updateOperatorStatus(address operator) internal {\n Operator storage op = operators[operator];\n bool newActiveStatus = op.registered &&\n op.licenseBond >= _minLicenseBond() &&\n (ticketToken.balanceOf(operator) / ticketPrice >= minTicketBalance);\n\n if (op.active != newActiveStatus) {\n op.active = newActiveStatus;\n emit OperatorActivationChanged(operator, newActiveStatus);\n }\n }\n\n /// @dev Calculates the minimum license bond required to maintain active status\n /// @return Minimum license bond (licenseRequiredBond * licenseActiveBps / 10000)\n function _minLicenseBond() internal view returns (uint256) {\n return (licenseRequiredBond * licenseActiveBps) / 10_000;\n }\n}\n"
|
|
289
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\n\npragma solidity >=0.8.27;\n\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport { IERC20 } from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {\n SafeERC20\n} from \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport { Math } from \"@openzeppelin/contracts/utils/math/Math.sol\";\nimport { ExitQueueLib } from \"../lib/ExitQueueLib.sol\";\n\nimport { IBondingRegistry } from \"../interfaces/IBondingRegistry.sol\";\nimport { ICiphernodeRegistry } from \"../interfaces/ICiphernodeRegistry.sol\";\nimport { ISlashingManager } from \"../interfaces/ISlashingManager.sol\";\nimport { EnclaveTicketToken } from \"../token/EnclaveTicketToken.sol\";\n\n/**\n * @title BondingRegistry\n * @notice Implementation of the bonding registry managing operator ticket balances and license bonds\n * @dev Handles deposits, withdrawals, slashing, exits, and integrates with registry and slashing manager\n */\ncontract BondingRegistry is IBondingRegistry, OwnableUpgradeable {\n using SafeERC20 for IERC20;\n using ExitQueueLib for ExitQueueLib.ExitQueueState;\n\n // ======================\n // Constants\n // ======================\n\n /// @dev Reason code for ticket balance deposits\n bytes32 private constant REASON_DEPOSIT = bytes32(\"DEPOSIT\");\n\n /// @dev Reason code for ticket balance withdrawals\n bytes32 private constant REASON_WITHDRAW = bytes32(\"WITHDRAW\");\n\n /// @dev Reason code for license bond operations\n bytes32 private constant REASON_BOND = bytes32(\"BOND\");\n\n /// @dev Reason code for license unbond operations\n bytes32 private constant REASON_UNBOND = bytes32(\"UNBOND\");\n\n // ======================\n // Storage\n // ======================\n\n /// @notice Ticket token (ETK with underlying USDC) used for collateral\n EnclaveTicketToken public ticketToken;\n\n /// @notice License token (ENCL) required for operator registration\n IERC20 public licenseToken;\n\n /// @notice Registry contract for managing committee membership\n ICiphernodeRegistry public registry;\n\n /// @notice Address authorized to perform slashing operations\n address public slashingManager;\n\n /// @notice Address authorized to distribute rewards to operators\n address public rewardDistributor;\n\n /// @notice Treasury address that receives slashed funds\n address public slashedFundsTreasury;\n\n /// @notice Price per ticket in ticket token units\n uint256 public ticketPrice;\n\n /// @notice Minimum license bond required for initial registration\n uint256 public licenseRequiredBond;\n\n /// @notice Minimum number of tickets required to maintain active status\n uint256 public minTicketBalance;\n\n /// @notice Time delay in seconds before exits can be claimed\n uint64 public exitDelay;\n\n /// @notice Percentage (in basis points) of license bond that must remain bonded to stay active\n /// @dev Default 8000 = 80%. Allows operators to unbond up to 20% while remaining active\n uint256 public licenseActiveBps;\n\n /// @notice Number of currently active operators\n uint256 public numActiveOperators;\n\n /// @notice Operator state data structure\n /// @param licenseBond Amount of license tokens currently bonded\n /// @param exitUnlocksAt Timestamp when pending exit can be claimed\n /// @param registered Whether operator is registered in the protocol\n /// @param exitRequested Whether operator has requested to exit\n /// @param active Whether operator meets all requirements for active status\n struct Operator {\n uint256 licenseBond;\n uint64 exitUnlocksAt;\n bool registered;\n bool exitRequested;\n bool active;\n }\n\n /// @notice Maps operator address to their state data\n mapping(address operator => Operator data) internal operators;\n\n /// @notice Total slashed ticket balance available for treasury withdrawal\n uint256 public slashedTicketBalance;\n\n /// @notice Total slashed license bond available for treasury withdrawal\n uint256 public slashedLicenseBond;\n\n // ======================\n // Exit Queue library state\n // ======================\n\n /// @dev Internal state for managing exit queue of tickets and licenses\n ExitQueueLib.ExitQueueState private _exits;\n\n // ======================\n // Modifiers\n // ======================\n\n /// @dev Restricts function access to only the slashing manager\n modifier onlySlashingManager() {\n if (msg.sender != slashingManager) revert Unauthorized();\n _;\n }\n\n /// @dev Reverts if operator has an exit in progress that hasn't unlocked yet\n /// @param operator Address of the operator to check\n modifier noExitInProgress(address operator) {\n Operator memory op = operators[operator];\n if (op.exitRequested && block.timestamp < op.exitUnlocksAt) {\n revert ExitInProgress();\n }\n _;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Initialization //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Constructor that disables initializers.\n /// @dev Prevents the implementation contract from being initialized. Initialization is performed\n /// via the initialize() function when deployed behind a proxy.\n constructor() {\n _disableInitializers();\n }\n\n /// @notice Initializes the bonding registry contract\n /// @dev Can only be called once due to initializer modifier\n /// @param _owner Address that will own the contract\n /// @param _ticketToken Ticket token contract for collateral\n /// @param _licenseToken License token contract for bonding\n /// @param _registry Ciphernode registry contract\n /// @param _slashedFundsTreasury Address to receive slashed funds\n /// @param _ticketPrice Initial price per ticket\n /// @param _licenseRequiredBond Initial required license bond for registration\n /// @param _minTicketBalance Initial minimum ticket balance for activation\n /// @param _exitDelay Initial exit delay period in seconds\n function initialize(\n address _owner,\n EnclaveTicketToken _ticketToken,\n IERC20 _licenseToken,\n ICiphernodeRegistry _registry,\n address _slashedFundsTreasury,\n uint256 _ticketPrice,\n uint256 _licenseRequiredBond,\n uint256 _minTicketBalance,\n uint64 _exitDelay\n ) public initializer {\n __Ownable_init(msg.sender);\n setTicketToken(_ticketToken);\n setLicenseToken(_licenseToken);\n setRegistry(_registry);\n setSlashedFundsTreasury(_slashedFundsTreasury);\n setTicketPrice(_ticketPrice);\n setLicenseRequiredBond(_licenseRequiredBond);\n setMinTicketBalance(_minTicketBalance);\n setExitDelay(_exitDelay);\n setLicenseActiveBps(8_000);\n if (_owner != owner()) transferOwnership(_owner);\n }\n\n // ======================\n // View Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function getLicenseToken() external view returns (address) {\n return address(licenseToken);\n }\n\n /// @inheritdoc IBondingRegistry\n function getTicketToken() external view returns (address) {\n return address(ticketToken);\n }\n\n /// @inheritdoc IBondingRegistry\n function getTicketBalance(\n address operator\n ) external view returns (uint256) {\n return ticketToken.balanceOf(operator);\n }\n\n /// @inheritdoc IBondingRegistry\n function getLicenseBond(address operator) external view returns (uint256) {\n return operators[operator].licenseBond;\n }\n\n /// @inheritdoc IBondingRegistry\n function availableTickets(\n address operator\n ) external view returns (uint256) {\n return ticketToken.balanceOf(operator) / ticketPrice;\n }\n\n /// @notice Get operator's ticket balance at a specific block\n /// @dev Uses checkpoint mechanism from ticket token\n /// @param operator Address of the operator\n /// @param blockNumber Block number to query\n /// @return Ticket balance at the specified block\n function getTicketBalanceAtBlock(\n address operator,\n uint256 blockNumber\n ) external view returns (uint256) {\n return ticketToken.getPastVotes(operator, blockNumber);\n }\n\n /// @notice Get operator's total pending exit amounts\n /// @param operator Address of the operator\n /// @return ticket Total pending ticket balance in exit queue\n /// @return license Total pending license bond in exit queue\n function pendingExits(\n address operator\n ) external view returns (uint256 ticket, uint256 license) {\n return _exits.getPendingAmounts(operator);\n }\n\n /// @notice Preview how much an operator can currently claim\n /// @param operator Address of the operator\n /// @return ticket Claimable ticket balance\n /// @return license Claimable license bond\n function previewClaimable(\n address operator\n ) external view returns (uint256 ticket, uint256 license) {\n return _exits.previewClaimableAmounts(operator);\n }\n\n /// @inheritdoc IBondingRegistry\n function isLicensed(address operator) external view returns (bool) {\n return operators[operator].licenseBond >= _minLicenseBond();\n }\n\n /// @inheritdoc IBondingRegistry\n function isRegistered(address operator) external view returns (bool) {\n return operators[operator].registered;\n }\n\n /// @inheritdoc IBondingRegistry\n function isActive(address operator) external view returns (bool) {\n return operators[operator].active;\n }\n\n /// @inheritdoc IBondingRegistry\n function hasExitInProgress(address operator) external view returns (bool) {\n Operator memory op = operators[operator];\n return op.exitRequested && block.timestamp < op.exitUnlocksAt;\n }\n\n // ======================\n // Operator Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function registerOperator() external noExitInProgress(msg.sender) {\n // Clear previous exit request\n if (operators[msg.sender].exitRequested) {\n operators[msg.sender].exitRequested = false;\n operators[msg.sender].exitUnlocksAt = 0;\n }\n\n require(\n !ISlashingManager(slashingManager).isBanned(msg.sender),\n CiphernodeBanned()\n );\n require(!operators[msg.sender].registered, AlreadyRegistered());\n require(\n operators[msg.sender].licenseBond >= licenseRequiredBond,\n NotLicensed()\n );\n\n operators[msg.sender].registered = true;\n\n // CiphernodeRegistry already emits an event when a ciphernode is added\n registry.addCiphernode(msg.sender);\n\n _updateOperatorStatus(msg.sender);\n }\n\n /// @inheritdoc IBondingRegistry\n function deregisterOperator(\n uint256[] calldata siblingNodes\n ) external noExitInProgress(msg.sender) {\n Operator storage op = operators[msg.sender];\n require(op.registered, NotRegistered());\n\n op.registered = false;\n op.exitRequested = true;\n op.exitUnlocksAt = uint64(block.timestamp) + exitDelay;\n\n uint256 ticketOut = ticketToken.balanceOf(msg.sender);\n uint256 licenseOut = op.licenseBond;\n if (ticketOut != 0) {\n ticketToken.burnTickets(msg.sender, ticketOut);\n emit TicketBalanceUpdated(\n msg.sender,\n -int256(ticketOut),\n 0,\n REASON_WITHDRAW\n );\n }\n if (licenseOut != 0) {\n op.licenseBond = 0;\n emit LicenseBondUpdated(\n msg.sender,\n -int256(licenseOut),\n 0,\n REASON_UNBOND\n );\n }\n\n if (ticketOut != 0 || licenseOut != 0) {\n _exits.queueAssetsForExit(\n msg.sender,\n exitDelay,\n ticketOut,\n licenseOut\n );\n }\n\n // CiphernodeRegistry already emits an event when a ciphernode is removed\n registry.removeCiphernode(msg.sender, siblingNodes);\n\n emit CiphernodeDeregistrationRequested(msg.sender, op.exitUnlocksAt);\n _updateOperatorStatus(msg.sender);\n }\n\n /// @inheritdoc IBondingRegistry\n function addTicketBalance(\n uint256 amount\n ) external noExitInProgress(msg.sender) {\n require(amount != 0, ZeroAmount());\n require(operators[msg.sender].registered, NotRegistered());\n\n ticketToken.depositFrom(msg.sender, msg.sender, amount);\n\n emit TicketBalanceUpdated(\n msg.sender,\n int256(amount),\n ticketToken.balanceOf(msg.sender),\n REASON_DEPOSIT\n );\n\n _updateOperatorStatus(msg.sender);\n }\n\n /// @inheritdoc IBondingRegistry\n function removeTicketBalance(\n uint256 amount\n ) external noExitInProgress(msg.sender) {\n require(amount != 0, ZeroAmount());\n require(operators[msg.sender].registered, NotRegistered());\n require(\n ticketToken.balanceOf(msg.sender) >= amount,\n InsufficientBalance()\n );\n\n ticketToken.burnTickets(msg.sender, amount);\n _exits.queueTicketsForExit(msg.sender, exitDelay, amount);\n\n emit TicketBalanceUpdated(\n msg.sender,\n -int256(amount),\n ticketToken.balanceOf(msg.sender),\n REASON_WITHDRAW\n );\n\n _updateOperatorStatus(msg.sender);\n }\n\n /// @inheritdoc IBondingRegistry\n function bondLicense(uint256 amount) external noExitInProgress(msg.sender) {\n require(amount != 0, ZeroAmount());\n\n uint256 balanceBefore = licenseToken.balanceOf(address(this));\n licenseToken.safeTransferFrom(msg.sender, address(this), amount);\n uint256 actualReceived = licenseToken.balanceOf(address(this)) -\n balanceBefore;\n\n operators[msg.sender].licenseBond += actualReceived;\n\n emit LicenseBondUpdated(\n msg.sender,\n int256(actualReceived),\n operators[msg.sender].licenseBond,\n REASON_BOND\n );\n\n _updateOperatorStatus(msg.sender);\n }\n\n /// @inheritdoc IBondingRegistry\n function unbondLicense(\n uint256 amount\n ) external noExitInProgress(msg.sender) {\n require(amount != 0, ZeroAmount());\n require(\n operators[msg.sender].licenseBond >= amount,\n InsufficientBalance()\n );\n\n operators[msg.sender].licenseBond -= amount;\n _exits.queueLicensesForExit(msg.sender, exitDelay, amount);\n\n emit LicenseBondUpdated(\n msg.sender,\n -int256(amount),\n operators[msg.sender].licenseBond,\n REASON_UNBOND\n );\n\n _updateOperatorStatus(msg.sender);\n }\n\n // ======================\n // Claim Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function claimExits(\n uint256 maxTicketAmount,\n uint256 maxLicenseAmount\n ) external {\n (uint256 ticketClaim, uint256 licenseClaim) = _exits.claimAssets(\n msg.sender,\n maxTicketAmount,\n maxLicenseAmount\n );\n require(ticketClaim > 0 || licenseClaim > 0, ExitNotReady());\n\n if (ticketClaim > 0) ticketToken.payout(msg.sender, ticketClaim);\n if (licenseClaim > 0) {\n licenseToken.safeTransfer(msg.sender, licenseClaim);\n }\n }\n\n // ======================\n // Slashing Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function slashTicketBalance(\n address operator,\n uint256 requestedSlashAmount,\n bytes32 slashReason\n ) external onlySlashingManager {\n require(requestedSlashAmount != 0, ZeroAmount());\n\n (uint256 pendingTicketBalance, ) = _exits.getPendingAmounts(operator);\n uint256 activeBalance = ticketToken.balanceOf(operator);\n uint256 totalAvailableBalance = activeBalance + pendingTicketBalance;\n\n uint256 actualSlashAmount = Math.min(\n requestedSlashAmount,\n totalAvailableBalance\n );\n\n if (actualSlashAmount == 0) {\n return;\n }\n\n // Slash from active balance first\n uint256 slashedFromActiveBalance = Math.min(\n actualSlashAmount,\n activeBalance\n );\n if (slashedFromActiveBalance > 0) {\n ticketToken.burnTickets(operator, slashedFromActiveBalance);\n }\n\n // Slash remaining amount from pending queue\n uint256 remainingToSlash = actualSlashAmount - slashedFromActiveBalance;\n if (remainingToSlash > 0) {\n _exits.slashPendingAssets(\n operator,\n remainingToSlash,\n 0, // licenseAmount\n true\n );\n }\n\n slashedTicketBalance += actualSlashAmount;\n emit TicketBalanceUpdated(\n operator,\n -int256(actualSlashAmount),\n ticketToken.balanceOf(operator),\n slashReason\n );\n\n _updateOperatorStatus(operator);\n }\n\n /// @inheritdoc IBondingRegistry\n function slashLicenseBond(\n address operator,\n uint256 requestedSlashAmount,\n bytes32 slashReason\n ) external onlySlashingManager {\n require(requestedSlashAmount != 0, ZeroAmount());\n\n Operator storage operatorData = operators[operator];\n (, uint256 pendingLicenseBalance) = _exits.getPendingAmounts(operator);\n uint256 totalAvailableBalance = operatorData.licenseBond +\n pendingLicenseBalance;\n uint256 actualSlashAmount = Math.min(\n requestedSlashAmount,\n totalAvailableBalance\n );\n\n if (actualSlashAmount == 0) return;\n\n // Slash from active balance first\n uint256 slashedFromActiveBalance = Math.min(\n actualSlashAmount,\n operatorData.licenseBond\n );\n if (slashedFromActiveBalance > 0) {\n operatorData.licenseBond -= slashedFromActiveBalance;\n }\n\n // Slash remaining amount from pending queue\n uint256 remainingToSlash = actualSlashAmount - slashedFromActiveBalance;\n if (remainingToSlash > 0) {\n _exits.slashPendingAssets(\n operator,\n 0, // ticketAmount\n remainingToSlash,\n true\n );\n }\n\n slashedLicenseBond += actualSlashAmount;\n emit LicenseBondUpdated(\n operator,\n -int256(actualSlashAmount),\n operatorData.licenseBond,\n slashReason\n );\n\n _updateOperatorStatus(operator);\n }\n\n // ======================\n // Reward Distribution Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function distributeRewards(\n IERC20 rewardToken,\n address[] calldata recipients,\n uint256[] calldata amounts\n ) external {\n require(msg.sender == rewardDistributor, OnlyRewardDistributor());\n require(recipients.length == amounts.length, ArrayLengthMismatch());\n\n uint256 len = recipients.length;\n for (uint256 i = 0; i < len; i++) {\n if (amounts[i] > 0 && operators[recipients[i]].registered) {\n rewardToken.safeTransferFrom(\n rewardDistributor,\n recipients[i],\n amounts[i]\n );\n }\n }\n }\n\n // ======================\n // Admin Functions\n // ======================\n\n /// @inheritdoc IBondingRegistry\n function setTicketPrice(uint256 newTicketPrice) public onlyOwner {\n require(newTicketPrice != 0, InvalidConfiguration());\n\n uint256 oldValue = ticketPrice;\n ticketPrice = newTicketPrice;\n\n emit ConfigurationUpdated(\"ticketPrice\", oldValue, newTicketPrice);\n }\n\n /// @inheritdoc IBondingRegistry\n function setLicenseRequiredBond(\n uint256 newLicenseRequiredBond\n ) public onlyOwner {\n require(newLicenseRequiredBond != 0, InvalidConfiguration());\n\n uint256 oldValue = licenseRequiredBond;\n licenseRequiredBond = newLicenseRequiredBond;\n\n emit ConfigurationUpdated(\n \"licenseRequiredBond\",\n oldValue,\n newLicenseRequiredBond\n );\n }\n\n /// @inheritdoc IBondingRegistry\n function setLicenseActiveBps(uint256 newBps) public onlyOwner {\n require(newBps > 0 && newBps <= 10_000, InvalidConfiguration());\n\n uint256 oldValue = licenseActiveBps;\n licenseActiveBps = newBps;\n\n emit ConfigurationUpdated(\"licenseActiveBps\", oldValue, newBps);\n }\n\n /// @inheritdoc IBondingRegistry\n function setMinTicketBalance(uint256 newMinTicketBalance) public onlyOwner {\n uint256 oldValue = minTicketBalance;\n minTicketBalance = newMinTicketBalance;\n\n emit ConfigurationUpdated(\n \"minTicketBalance\",\n oldValue,\n newMinTicketBalance\n );\n }\n\n /// @inheritdoc IBondingRegistry\n function setExitDelay(uint64 newExitDelay) public onlyOwner {\n uint256 oldValue = uint256(exitDelay);\n exitDelay = newExitDelay;\n\n emit ConfigurationUpdated(\"exitDelay\", oldValue, uint256(newExitDelay));\n }\n\n /// @inheritdoc IBondingRegistry\n function setSlashedFundsTreasury(\n address newSlashedFundsTreasury\n ) public onlyOwner {\n require(newSlashedFundsTreasury != address(0), ZeroAddress());\n slashedFundsTreasury = newSlashedFundsTreasury;\n }\n\n /// @inheritdoc IBondingRegistry\n function setTicketToken(\n EnclaveTicketToken newTicketToken\n ) public onlyOwner {\n ticketToken = newTicketToken;\n }\n\n /// @inheritdoc IBondingRegistry\n function setLicenseToken(IERC20 newLicenseToken) public onlyOwner {\n licenseToken = newLicenseToken;\n }\n\n /// @inheritdoc IBondingRegistry\n function setRegistry(ICiphernodeRegistry newRegistry) public onlyOwner {\n registry = newRegistry;\n }\n\n /// @inheritdoc IBondingRegistry\n function setSlashingManager(address newSlashingManager) public onlyOwner {\n slashingManager = newSlashingManager;\n }\n\n /// @notice Sets the reward distributor address\n /// @dev Only callable by owner\n /// @param newRewardDistributor Address of the reward distributor\n function setRewardDistributor(\n address newRewardDistributor\n ) public onlyOwner {\n rewardDistributor = newRewardDistributor;\n }\n\n /// @inheritdoc IBondingRegistry\n function withdrawSlashedFunds(\n uint256 ticketAmount,\n uint256 licenseAmount\n ) public onlyOwner {\n require(ticketAmount <= slashedTicketBalance, InsufficientBalance());\n require(licenseAmount <= slashedLicenseBond, InsufficientBalance());\n\n if (ticketAmount > 0) {\n slashedTicketBalance -= ticketAmount;\n ticketToken.payout(slashedFundsTreasury, ticketAmount);\n }\n\n if (licenseAmount > 0) {\n slashedLicenseBond -= licenseAmount;\n licenseToken.safeTransfer(slashedFundsTreasury, licenseAmount);\n }\n\n emit SlashedFundsWithdrawn(\n slashedFundsTreasury,\n ticketAmount,\n licenseAmount\n );\n }\n\n // ======================\n // Internal Functions\n // ======================\n\n /// @dev Updates operator's active status based on current conditions\n /// @dev Operator is active if: registered, has minimum license bond, and has minimum tickets\n /// @param operator Address of the operator to update\n function _updateOperatorStatus(address operator) internal {\n Operator storage op = operators[operator];\n bool newActiveStatus = op.registered &&\n op.licenseBond >= _minLicenseBond() &&\n (ticketToken.balanceOf(operator) / ticketPrice >= minTicketBalance);\n\n if (op.active != newActiveStatus) {\n op.active = newActiveStatus;\n if (newActiveStatus) {\n numActiveOperators++;\n } else {\n numActiveOperators--;\n }\n\n emit OperatorActivationChanged(operator, newActiveStatus);\n }\n }\n\n /// @dev Calculates the minimum license bond required to maintain active status\n /// @return Minimum license bond (licenseRequiredBond * licenseActiveBps / 10000)\n function _minLicenseBond() internal view returns (uint256) {\n return (licenseRequiredBond * licenseActiveBps) / 10_000;\n }\n}\n"
|
|
278
290
|
},
|
|
279
291
|
"project/contracts/registry/CiphernodeRegistryOwnable.sol": {
|
|
280
|
-
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { ICiphernodeRegistry } from \"../interfaces/ICiphernodeRegistry.sol\";\nimport { IBondingRegistry } from \"../interfaces/IBondingRegistry.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n InternalLeanIMT,\n LeanIMTData\n} from \"@zk-kit/lean-imt.sol/InternalLeanIMT.sol\";\n\n/**\n * @title CiphernodeRegistryOwnable\n * @notice Ownable implementation of the ciphernode registry with IMT-based membership tracking\n * @dev Manages ciphernode registration, committee selection, and integrates with bonding registry\n */\ncontract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable {\n using InternalLeanIMT for LeanIMTData;\n\n ////////////////////////////////////////////////////////////\n // //\n // Events //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Emitted when the bonding registry address is set\n /// @param bondingRegistry Address of the bonding registry contract\n event BondingRegistrySet(address indexed bondingRegistry);\n\n ////////////////////////////////////////////////////////////\n // //\n // Storage Variables //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Address of the Enclave contract authorized to request committees\n address public enclave;\n\n /// @notice Address of the bonding registry for checking node eligibility\n address public bondingRegistry;\n\n /// @notice Current number of registered ciphernodes\n uint256 public numCiphernodes;\n\n /// @notice Submission Window for an E3 Sortition.\n /// @dev The submission window is the time period during which the ciphernodes can submit\n /// their tickets to be a part of the committee.\n uint256 public sortitionSubmissionWindow;\n\n /// @notice Incremental Merkle Tree (IMT) containing all registered ciphernodes\n LeanIMTData public ciphernodes;\n\n /// @notice Maps E3 ID to the IMT root at the time of committee request\n mapping(uint256 e3Id => uint256 root) public roots;\n\n /// @notice Maps E3 ID to the hash of the committee's public key\n mapping(uint256 e3Id => bytes32 publicKeyHash) public publicKeyHashes;\n\n /// @notice Maps E3 ID to its committee data\n mapping(uint256 e3Id => Committee committee) internal committees;\n\n ////////////////////////////////////////////////////////////\n // //\n // Errors //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Committee has already been requested for this E3\n error CommitteeAlreadyRequested();\n\n /// @notice Committee has already been published for this E3\n error CommitteeAlreadyPublished();\n\n /// @notice Committee has not been published yet for this E3\n error CommitteeNotPublished();\n\n /// @notice Committee has not been requested yet for this E3\n error CommitteeNotRequested();\n\n /// @notice Committee Not Initialized or Finalized\n error CommitteeNotInitializedOrFinalized();\n\n /// @notice Submission Window has been closed for this E3\n error SubmissionWindowClosed();\n\n /// @notice Submission deadline has been reached for this E3\n error SubmissionDeadlineReached();\n\n /// @notice Committee has already been finalized for this E3\n error CommitteeAlreadyFinalized();\n\n /// @notice Committee has not been finalized yet for this E3\n error CommitteeNotFinalized();\n\n /// @notice Node has already submitted a ticket for this E3\n error NodeAlreadySubmitted();\n\n /// @notice Node has not submitted a ticket for this E3\n error NodeNotSubmitted();\n\n /// @notice Node is not eligible for this E3\n error NodeNotEligible();\n\n /// @notice Ciphernode is not enabled in the registry\n /// @param node Address of the ciphernode\n error CiphernodeNotEnabled(address node);\n\n /// @notice Caller is not the Enclave contract\n error OnlyEnclave();\n\n /// @notice Caller is not the bonding registry\n error OnlyBondingRegistry();\n\n /// @notice Caller is neither owner nor bonding registry\n error NotOwnerOrBondingRegistry();\n\n /// @notice Node is not bonded\n /// @param node Address of the node\n error NodeNotBonded(address node);\n\n /// @notice Address cannot be zero\n error ZeroAddress();\n\n /// @notice Bonding registry has not been set\n error BondingRegistryNotSet();\n\n /// @notice Invalid ticket number\n error InvalidTicketNumber();\n\n /// @notice Submission window not closed yet\n error SubmissionWindowNotClosed();\n\n /// @notice Threshold not met for this E3\n error ThresholdNotMet();\n\n /// @notice Caller is not authorized\n error Unauthorized();\n\n ////////////////////////////////////////////////////////////\n // //\n // Modifiers //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @dev Restricts function access to only the Enclave contract\n modifier onlyEnclave() {\n require(msg.sender == enclave, OnlyEnclave());\n _;\n }\n\n /// @dev Restricts function access to only the bonding registry\n modifier onlyBondingRegistry() {\n require(msg.sender == bondingRegistry, OnlyBondingRegistry());\n _;\n }\n\n /// @dev Restricts function access to owner or bonding registry\n modifier onlyOwnerOrBondingVault() {\n require(\n msg.sender == owner() || msg.sender == bondingRegistry,\n NotOwnerOrBondingRegistry()\n );\n _;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Initialization //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Constructor that disables initializers.\n /// @dev Prevents the implementation contract from being initialized. Initialization is performed\n /// via the initialize() function when deployed behind a proxy.\n constructor() {\n _disableInitializers();\n }\n\n /// @notice Initializes the registry contract\n /// @dev Can only be called once due to initializer modifier\n /// @param _owner Address that will own the contract\n /// @param _enclave Address of the Enclave contract\n /// @param _submissionWindow The submission window for the E3 sortition in seconds\n function initialize(\n address _owner,\n address _enclave,\n uint256 _submissionWindow\n ) public initializer {\n require(_owner != address(0), ZeroAddress());\n require(_enclave != address(0), ZeroAddress());\n\n __Ownable_init(msg.sender);\n setEnclave(_enclave);\n setSortitionSubmissionWindow(_submissionWindow);\n if (_owner != owner()) transferOwnership(_owner);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Core Entrypoints //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @inheritdoc ICiphernodeRegistry\n function requestCommittee(\n uint256 e3Id,\n uint256 seed,\n uint32[2] calldata threshold\n ) external onlyEnclave returns (bool success) {\n Committee storage c = committees[e3Id];\n require(!c.initialized, CommitteeAlreadyRequested());\n\n c.initialized = true;\n c.finalized = false;\n c.seed = seed;\n c.requestBlock = block.number;\n c.submissionDeadline = block.timestamp + sortitionSubmissionWindow;\n c.threshold = threshold;\n roots[e3Id] = root();\n\n emit CommitteeRequested(\n e3Id,\n seed,\n threshold,\n c.requestBlock,\n c.submissionDeadline\n );\n success = true;\n }\n\n /// @notice Publishes a committee for an E3 computation\n /// @dev Only callable by owner. Verifies committee is finalized and matches provided nodes.\n /// @param e3Id ID of the E3 computation\n /// @param nodes Array of ciphernode addresses selected for the committee\n /// @param publicKey Aggregated public key of the committee\n /// @param publicKeyHash The hash of the public key\n function publishCommittee(\n uint256 e3Id,\n address[] calldata nodes,\n bytes calldata publicKey,\n bytes32 publicKeyHash\n ) external onlyOwner {\n Committee storage c = committees[e3Id];\n\n require(c.initialized, CommitteeNotRequested());\n require(c.finalized, CommitteeNotFinalized());\n require(c.publicKey == bytes32(0), CommitteeAlreadyPublished());\n require(nodes.length == c.committee.length, \"Node count mismatch\");\n\n // TODO: Currently we trust the owner to publish the correct committee.\n // TODO: Need a Proof that the public key is generated from the committee\n c.publicKey = publicKeyHash;\n publicKeyHashes[e3Id] = publicKeyHash;\n emit CommitteePublished(e3Id, nodes, publicKey);\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function addCiphernode(address node) external onlyOwnerOrBondingVault {\n if (isEnabled(node)) {\n return;\n }\n\n uint160 ciphernode = uint160(node);\n ciphernodes._insert(ciphernode);\n numCiphernodes++;\n emit CiphernodeAdded(\n node,\n ciphernodes._indexOf(ciphernode),\n numCiphernodes,\n ciphernodes.size\n );\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function removeCiphernode(\n address node,\n uint256[] calldata siblingNodes\n ) external onlyOwnerOrBondingVault {\n require(isEnabled(node), CiphernodeNotEnabled(node));\n\n uint160 ciphernode = uint160(node);\n uint256 index = ciphernodes._indexOf(ciphernode);\n ciphernodes._remove(ciphernode, siblingNodes);\n numCiphernodes--;\n emit CiphernodeRemoved(node, index, numCiphernodes, ciphernodes.size);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Sortition Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Submit a ticket for sortition\n /// @dev Validates ticket against node's balance at request block and inserts into top-N\n /// @param e3Id ID of the E3 computation\n /// @param ticketNumber The ticket number to submit (1 to available tickets at snapshot)\n function submitTicket(uint256 e3Id, uint256 ticketNumber) external {\n Committee storage c = committees[e3Id];\n require(c.initialized, CommitteeNotRequested());\n require(!c.finalized, CommitteeAlreadyFinalized());\n require(\n block.timestamp <= c.submissionDeadline,\n SubmissionDeadlineReached()\n );\n require(!c.submitted[msg.sender], NodeAlreadySubmitted());\n require(isCiphernodeEligible(msg.sender), NodeNotEligible());\n\n // Validate node eligibility and ticket number\n _validateNodeEligibility(msg.sender, ticketNumber, e3Id);\n\n // Compute score\n uint256 score = _computeTicketScore(\n msg.sender,\n ticketNumber,\n e3Id,\n c.seed\n );\n\n // Store submission\n c.submitted[msg.sender] = true;\n\n // Insert into top-N (ascending score)\n _insertTopN(c, msg.sender, score);\n\n emit TicketSubmitted(e3Id, msg.sender, ticketNumber, score);\n }\n\n /// @notice Finalize the committee after submission window closes\n /// @dev Can be called by anyone after the deadline. Reverts if not enough nodes submitted.\n /// @param e3Id ID of the E3 computation\n function finalizeCommittee(uint256 e3Id) external {\n Committee storage c = committees[e3Id];\n require(c.initialized, CommitteeNotRequested());\n require(!c.finalized, CommitteeAlreadyFinalized());\n require(\n block.timestamp >= c.submissionDeadline,\n SubmissionWindowNotClosed()\n );\n // TODO: Handle what happens if the threshold is not met.\n require(c.topNodes.length >= c.threshold[1], ThresholdNotMet());\n\n c.finalized = true;\n c.committee = c.topNodes;\n\n emit CommitteeFinalized(e3Id, c.topNodes);\n }\n\n /// @notice Check if submission window is still open for an E3\n /// @param e3Id ID of the E3 computation\n /// @return Whether the submission window is open\n function isOpen(uint256 e3Id) public view returns (bool) {\n Committee storage c = committees[e3Id];\n if (!c.initialized || c.finalized) return false;\n return block.timestamp <= c.submissionDeadline;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Set Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Sets the Enclave contract address\n /// @dev Only callable by owner\n /// @param _enclave Address of the Enclave contract\n function setEnclave(address _enclave) public onlyOwner {\n require(_enclave != address(0), ZeroAddress());\n enclave = _enclave;\n emit EnclaveSet(_enclave);\n }\n\n /// @notice Sets the bonding registry contract address\n /// @dev Only callable by owner\n /// @param _bondingRegistry Address of the bonding registry contract\n function setBondingRegistry(address _bondingRegistry) public onlyOwner {\n require(_bondingRegistry != address(0), ZeroAddress());\n bondingRegistry = _bondingRegistry;\n emit BondingRegistrySet(_bondingRegistry);\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function setSortitionSubmissionWindow(\n uint256 _sortitionSubmissionWindow\n ) public onlyOwner {\n sortitionSubmissionWindow = _sortitionSubmissionWindow;\n emit SortitionSubmissionWindowSet(_sortitionSubmissionWindow);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Get Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @inheritdoc ICiphernodeRegistry\n function committeePublicKey(\n uint256 e3Id\n ) external view returns (bytes32 publicKeyHash) {\n publicKeyHash = publicKeyHashes[e3Id];\n require(publicKeyHash != bytes32(0), CommitteeNotPublished());\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function isCiphernodeEligible(address node) public view returns (bool) {\n if (!isEnabled(node)) return false;\n\n require(bondingRegistry != address(0), BondingRegistryNotSet());\n return IBondingRegistry(bondingRegistry).isActive(node);\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function isEnabled(address node) public view returns (bool) {\n return ciphernodes._has(uint160(node));\n }\n\n /// @notice Returns the current root of the ciphernode IMT\n /// @return Current IMT root\n function root() public view returns (uint256) {\n return (ciphernodes._root());\n }\n\n /// @notice Returns the IMT root at the time a committee was requested\n /// @param e3Id ID of the E3\n /// @return IMT root at time of committee request\n function rootAt(uint256 e3Id) public view returns (uint256) {\n return roots[e3Id];\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function getCommitteeNodes(\n uint256 e3Id\n ) public view returns (address[] memory nodes) {\n Committee storage c = committees[e3Id];\n require(c.publicKey != bytes32(0), CommitteeNotPublished());\n nodes = c.committee;\n }\n\n /// @notice Returns the current size of the ciphernode IMT\n /// @return Size of the IMT\n function treeSize() public view returns (uint256) {\n return ciphernodes.size;\n }\n\n /// @notice Returns the address of the bonding registry\n /// @return Address of the bonding registry contract\n function getBondingRegistry() external view returns (address) {\n return bondingRegistry;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Internal Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Computes ticket score as keccak256(node || ticketNumber || e3Id || seed)\n /// @param node Address of the ciphernode\n /// @param ticketNumber The ticket number\n /// @param e3Id ID of the E3 computation\n /// @param seed Random seed for the E3\n /// @return score The computed score\n function _computeTicketScore(\n address node,\n uint256 ticketNumber,\n uint256 e3Id,\n uint256 seed\n ) internal pure returns (uint256) {\n bytes32 hash = keccak256(\n abi.encodePacked(node, ticketNumber, e3Id, seed)\n );\n return uint256(hash);\n }\n\n /// @notice Validates that a node is eligible to submit a ticket\n /// @dev Uses snapshot of ticket balance at E3 request block for deterministic validation\n /// @param node Address of the ciphernode\n /// @param ticketNumber The ticket number being submitted\n /// @param e3Id ID of the E3 computation\n function _validateNodeEligibility(\n address node,\n uint256 ticketNumber,\n uint256 e3Id\n ) internal view {\n require(ticketNumber > 0, InvalidTicketNumber());\n require(bondingRegistry != address(0), BondingRegistryNotSet());\n\n Committee storage c = committees[e3Id];\n\n // @todo Ensure we check everywhere that we use the block before the request block\n // to ensure cases where everything is done in the same block are handled correctly.\n uint256 ticketBalance = IBondingRegistry(bondingRegistry)\n .getTicketBalanceAtBlock(node, c.requestBlock - 1);\n uint256 ticketPrice = IBondingRegistry(bondingRegistry).ticketPrice();\n\n require(ticketPrice > 0, InvalidTicketNumber());\n uint256 availableTickets = ticketBalance / ticketPrice;\n\n require(availableTickets > 0, NodeNotEligible());\n require(ticketNumber <= availableTickets, InvalidTicketNumber());\n }\n\n /// @notice Inserts a node into the top-N list - Smallest scores\n /// @dev If the node is not in the top-N, it is added to the top-N.\n /// @param c Committee storage reference\n /// @param node Address of the node\n /// @param score Score of the node\n /// @return entered Whether the node was inserted into the top-N\n function _insertTopN(\n Committee storage c,\n address node,\n uint256 score\n ) internal returns (bool entered) {\n address[] storage top = c.topNodes;\n uint256 cap = c.threshold[1];\n\n if (top.length < cap) {\n top.push(node);\n c.scoreOf[node] = score;\n return true;\n }\n\n uint256 worstIdx = 0;\n uint256 worstScore = c.scoreOf[top[0]];\n unchecked {\n for (uint256 i = 1; i < top.length; ++i) {\n uint256 s = c.scoreOf[top[i]];\n if (s > worstScore) {\n worstScore = s;\n worstIdx = i;\n }\n }\n }\n\n if (score >= worstScore) return false;\n\n top[worstIdx] = node;\n c.scoreOf[node] = score;\n\n return true;\n }\n}\n"
|
|
292
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { ICiphernodeRegistry } from \"../interfaces/ICiphernodeRegistry.sol\";\nimport { IBondingRegistry } from \"../interfaces/IBondingRegistry.sol\";\nimport { IEnclave } from \"../interfaces/IEnclave.sol\";\nimport {\n OwnableUpgradeable\n} from \"@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol\";\nimport {\n InternalLeanIMT,\n LeanIMTData\n} from \"@zk-kit/lean-imt.sol/InternalLeanIMT.sol\";\n\n/**\n * @title CiphernodeRegistryOwnable\n * @notice Ownable implementation of the ciphernode registry with IMT-based membership tracking\n * @dev Manages ciphernode registration, committee selection, and integrates with bonding registry\n */\ncontract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable {\n using InternalLeanIMT for LeanIMTData;\n\n ////////////////////////////////////////////////////////////\n // //\n // Events //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Emitted when the bonding registry address is set\n /// @param bondingRegistry Address of the bonding registry contract\n event BondingRegistrySet(address indexed bondingRegistry);\n\n ////////////////////////////////////////////////////////////\n // //\n // Storage Variables //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Address of the Enclave contract authorized to request committees\n IEnclave public enclave;\n\n /// @notice Address of the bonding registry for checking node eligibility\n IBondingRegistry public bondingRegistry;\n\n /// @notice Current number of registered ciphernodes\n uint256 public numCiphernodes;\n\n /// @notice Submission Window for an E3 Sortition.\n /// @dev The submission window is the time period during which the ciphernodes can submit\n /// their tickets to be a part of the committee.\n uint256 public sortitionSubmissionWindow;\n\n /// @notice Incremental Merkle Tree (IMT) containing all registered ciphernodes\n LeanIMTData public ciphernodes;\n\n /// @notice Maps E3 ID to the IMT root at the time of committee request\n mapping(uint256 e3Id => uint256 root) public roots;\n\n /// @notice Maps E3 ID to the hash of the committee's public key\n mapping(uint256 e3Id => bytes32 publicKeyHash) public publicKeyHashes;\n\n /// @notice Maps E3 ID to its committee data\n mapping(uint256 e3Id => Committee committee) internal committees;\n\n ////////////////////////////////////////////////////////////\n // //\n // Errors //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Committee has already been requested for this E3\n error CommitteeAlreadyRequested();\n\n /// @notice Committee has already been published for this E3\n error CommitteeAlreadyPublished();\n\n /// @notice Committee has not been published yet for this E3\n error CommitteeNotPublished();\n\n /// @notice Committee has not been requested yet for this E3\n error CommitteeNotRequested();\n\n /// @notice Committee Not Initialized or Finalized\n error CommitteeNotInitializedOrFinalized();\n\n /// @notice Submission Window has been closed for this E3\n error SubmissionWindowClosed();\n\n /// @notice Committee deadline has been reached for this E3\n error CommitteeDeadlineReached();\n\n /// @notice Committee has already been finalized for this E3\n error CommitteeAlreadyFinalized();\n\n /// @notice Committee has not been finalized yet for this E3\n error CommitteeNotFinalized();\n\n /// @notice Node has already submitted a ticket for this E3\n error NodeAlreadySubmitted();\n\n /// @notice Node has not submitted a ticket for this E3\n error NodeNotSubmitted();\n\n /// @notice Node is not eligible for this E3\n error NodeNotEligible();\n\n /// @notice Ciphernode is not enabled in the registry\n /// @param node Address of the ciphernode\n error CiphernodeNotEnabled(address node);\n\n /// @notice Caller is not the Enclave contract\n error OnlyEnclave();\n\n /// @notice Caller is not the bonding registry\n error OnlyBondingRegistry();\n\n /// @notice Caller is neither owner nor bonding registry\n error NotOwnerOrBondingRegistry();\n\n /// @notice Node is not bonded\n /// @param node Address of the node\n error NodeNotBonded(address node);\n\n /// @notice Address cannot be zero\n error ZeroAddress();\n\n /// @notice Bonding registry has not been set\n error BondingRegistryNotSet();\n\n /// @notice Invalid ticket number\n error InvalidTicketNumber();\n\n /// @notice Submission window not closed yet\n error SubmissionWindowNotClosed();\n\n /// @notice Threshold not met for this E3\n error ThresholdNotMet();\n\n /// @notice Caller is not authorized\n error Unauthorized();\n\n /// @notice Not enough registered ciphernodes to meet threshold\n /// @param requested The requested committee size (N)\n /// @param available The number of registered ciphernodes\n error InsufficientCiphernodes(uint256 requested, uint256 available);\n\n ////////////////////////////////////////////////////////////\n // //\n // Modifiers //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @dev Restricts function access to only the Enclave contract\n modifier onlyEnclave() {\n require(msg.sender == address(enclave), OnlyEnclave());\n _;\n }\n\n /// @dev Restricts function access to only the bonding registry\n modifier onlyBondingRegistry() {\n require(msg.sender == address(bondingRegistry), OnlyBondingRegistry());\n _;\n }\n\n /// @dev Restricts function access to owner or bonding registry\n modifier onlyOwnerOrBondingVault() {\n require(\n msg.sender == owner() || msg.sender == address(bondingRegistry),\n NotOwnerOrBondingRegistry()\n );\n _;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Initialization //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Constructor that disables initializers.\n /// @dev Prevents the implementation contract from being initialized. Initialization is performed\n /// via the initialize() function when deployed behind a proxy.\n constructor() {\n _disableInitializers();\n }\n\n /// @notice Initializes the registry contract\n /// @dev Can only be called once due to initializer modifier\n /// @param _owner Address that will own the contract\n /// @param _enclave Address of the Enclave contract\n /// @param _submissionWindow The submission window for the E3 sortition in seconds\n function initialize(\n address _owner,\n IEnclave _enclave,\n uint256 _submissionWindow\n ) public initializer {\n require(_owner != address(0), ZeroAddress());\n\n __Ownable_init(msg.sender);\n setEnclave(_enclave);\n setSortitionSubmissionWindow(_submissionWindow);\n if (_owner != owner()) transferOwnership(_owner);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Core Entrypoints //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @inheritdoc ICiphernodeRegistry\n function requestCommittee(\n uint256 e3Id,\n uint256 seed,\n uint32[2] calldata threshold\n ) external onlyEnclave returns (bool success) {\n Committee storage c = committees[e3Id];\n require(!c.initialized, CommitteeAlreadyRequested());\n\n uint256 activeCount = bondingRegistry.numActiveOperators();\n require(\n threshold[1] <= activeCount,\n InsufficientCiphernodes(threshold[1], activeCount)\n );\n\n c.initialized = true;\n c.finalized = false;\n c.seed = seed;\n c.requestBlock = block.number;\n c.committeeDeadline = block.timestamp + sortitionSubmissionWindow;\n c.threshold = threshold;\n roots[e3Id] = root();\n\n emit CommitteeRequested(\n e3Id,\n seed,\n threshold,\n c.requestBlock,\n c.committeeDeadline\n );\n success = true;\n }\n\n /// @notice Publishes a committee for an E3 computation\n /// @dev Only callable by owner. Verifies committee is finalized and matches provided nodes.\n /// @param e3Id ID of the E3 computation\n /// @param nodes Array of ciphernode addresses selected for the committee\n /// @param publicKey Aggregated public key of the committee\n /// @param publicKeyHash The hash of the public key\n function publishCommittee(\n uint256 e3Id,\n address[] calldata nodes,\n bytes calldata publicKey,\n bytes32 publicKeyHash\n ) external onlyOwner {\n Committee storage c = committees[e3Id];\n\n require(c.initialized, CommitteeNotRequested());\n require(c.finalized, CommitteeNotFinalized());\n require(c.publicKey == bytes32(0), CommitteeAlreadyPublished());\n require(nodes.length == c.committee.length, \"Node count mismatch\");\n\n // TODO: Currently we trust the owner to publish the correct committee.\n // TODO: Need a Proof that the public key is generated from the committee\n c.publicKey = publicKeyHash;\n publicKeyHashes[e3Id] = publicKeyHash;\n // Progress E3 to KeyPublished stage\n enclave.onCommitteePublished(e3Id, publicKeyHash);\n emit CommitteePublished(e3Id, nodes, publicKey);\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function addCiphernode(address node) external onlyOwnerOrBondingVault {\n if (isEnabled(node)) {\n return;\n }\n\n uint160 ciphernode = uint160(node);\n ciphernodes._insert(ciphernode);\n numCiphernodes++;\n emit CiphernodeAdded(\n node,\n ciphernodes._indexOf(ciphernode),\n numCiphernodes,\n ciphernodes.size\n );\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function removeCiphernode(\n address node,\n uint256[] calldata siblingNodes\n ) external onlyOwnerOrBondingVault {\n require(isEnabled(node), CiphernodeNotEnabled(node));\n\n uint160 ciphernode = uint160(node);\n uint256 index = ciphernodes._indexOf(ciphernode);\n ciphernodes._remove(ciphernode, siblingNodes);\n numCiphernodes--;\n emit CiphernodeRemoved(node, index, numCiphernodes, ciphernodes.size);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Sortition Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Submit a ticket for sortition\n /// @dev Validates ticket against node's balance at request block and inserts into top-N\n /// @param e3Id ID of the E3 computation\n /// @param ticketNumber The ticket number to submit (1 to available tickets at snapshot)\n function submitTicket(uint256 e3Id, uint256 ticketNumber) external {\n Committee storage c = committees[e3Id];\n require(c.initialized, CommitteeNotRequested());\n require(!c.finalized, CommitteeAlreadyFinalized());\n require(\n block.timestamp <= c.committeeDeadline,\n CommitteeDeadlineReached()\n );\n require(!c.submitted[msg.sender], NodeAlreadySubmitted());\n require(isCiphernodeEligible(msg.sender), NodeNotEligible());\n\n // Validate node eligibility and ticket number\n _validateNodeEligibility(msg.sender, ticketNumber, e3Id);\n\n // Compute score\n uint256 score = _computeTicketScore(\n msg.sender,\n ticketNumber,\n e3Id,\n c.seed\n );\n\n // Store submission\n c.submitted[msg.sender] = true;\n\n // Insert into top-N (ascending score)\n _insertTopN(c, msg.sender, score);\n\n emit TicketSubmitted(e3Id, msg.sender, ticketNumber, score);\n }\n\n /// @notice Finalize the committee after submission window closes\n /// @dev Can be called by anyone after the deadline. If threshold not met, marks E3 as failed.\n /// @param e3Id ID of the E3 computation\n /// @return success True if committee formed successfully, false if threshold not met\n function finalizeCommittee(uint256 e3Id) external returns (bool success) {\n Committee storage c = committees[e3Id];\n require(c.initialized, CommitteeNotRequested());\n require(!c.finalized, CommitteeAlreadyFinalized());\n require(\n block.timestamp >= c.committeeDeadline,\n SubmissionWindowNotClosed()\n );\n c.finalized = true;\n bool thresholdMet = c.topNodes.length >= c.threshold[1];\n\n if (!thresholdMet) {\n c.failed = true;\n emit CommitteeFormationFailed(\n e3Id,\n c.topNodes.length,\n c.threshold[1]\n );\n enclave.onE3Failed(\n e3Id,\n uint8(IEnclave.FailureReason.InsufficientCommitteeMembers)\n );\n return false;\n }\n\n c.committee = c.topNodes;\n enclave.onCommitteeFinalized(e3Id);\n emit CommitteeFinalized(e3Id, c.topNodes);\n return true;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Set Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Sets the Enclave contract address\n /// @dev Only callable by owner\n /// @param _enclave Address of the Enclave contract\n function setEnclave(IEnclave _enclave) public onlyOwner {\n require(address(_enclave) != address(0), ZeroAddress());\n enclave = _enclave;\n emit EnclaveSet(address(_enclave));\n }\n\n /// @notice Sets the bonding registry contract address\n /// @dev Only callable by owner\n /// @param _bondingRegistry Address of the bonding registry contract\n function setBondingRegistry(\n IBondingRegistry _bondingRegistry\n ) public onlyOwner {\n require(address(_bondingRegistry) != address(0), ZeroAddress());\n bondingRegistry = _bondingRegistry;\n emit BondingRegistrySet(address(_bondingRegistry));\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function setSortitionSubmissionWindow(\n uint256 _sortitionSubmissionWindow\n ) public onlyOwner {\n sortitionSubmissionWindow = _sortitionSubmissionWindow;\n emit SortitionSubmissionWindowSet(_sortitionSubmissionWindow);\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Get Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Check if submission window is still open for an E3\n /// @param e3Id ID of the E3 computation\n /// @return Whether the submission window is open\n function isOpen(uint256 e3Id) public view returns (bool) {\n Committee storage c = committees[e3Id];\n if (!c.initialized || c.finalized) return false;\n return block.timestamp <= c.committeeDeadline;\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function committeePublicKey(\n uint256 e3Id\n ) external view returns (bytes32 publicKeyHash) {\n publicKeyHash = publicKeyHashes[e3Id];\n require(publicKeyHash != bytes32(0), CommitteeNotPublished());\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function isCiphernodeEligible(address node) public view returns (bool) {\n if (!isEnabled(node)) return false;\n\n require(\n address(bondingRegistry) != address(0),\n BondingRegistryNotSet()\n );\n return bondingRegistry.isActive(node);\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function isEnabled(address node) public view returns (bool) {\n return ciphernodes._has(uint160(node));\n }\n\n /// @notice Returns the current root of the ciphernode IMT\n /// @return Current IMT root\n function root() public view returns (uint256) {\n return (ciphernodes._root());\n }\n\n /// @notice Returns the IMT root at the time a committee was requested\n /// @param e3Id ID of the E3\n /// @return IMT root at time of committee request\n function rootAt(uint256 e3Id) public view returns (uint256) {\n return roots[e3Id];\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function getCommitteeNodes(\n uint256 e3Id\n ) public view returns (address[] memory nodes) {\n Committee storage c = committees[e3Id];\n require(c.publicKey != bytes32(0), CommitteeNotPublished());\n nodes = c.committee;\n }\n\n /// @notice Returns the current size of the ciphernode IMT\n /// @return Size of the IMT\n function treeSize() public view returns (uint256) {\n return ciphernodes.size;\n }\n\n /// @notice Returns the address of the bonding registry\n /// @return Address of the bonding registry contract\n function getBondingRegistry() external view returns (address) {\n return address(bondingRegistry);\n }\n\n /// @inheritdoc ICiphernodeRegistry\n function getCommitteeDeadline(\n uint256 e3Id\n ) external view returns (uint256) {\n Committee storage c = committees[e3Id];\n require(c.initialized, CommitteeNotRequested());\n return c.committeeDeadline;\n }\n\n ////////////////////////////////////////////////////////////\n // //\n // Internal Functions //\n // //\n ////////////////////////////////////////////////////////////\n\n /// @notice Computes ticket score as keccak256(node || ticketNumber || e3Id || seed)\n /// @param node Address of the ciphernode\n /// @param ticketNumber The ticket number\n /// @param e3Id ID of the E3 computation\n /// @param seed Random seed for the E3\n /// @return score The computed score\n function _computeTicketScore(\n address node,\n uint256 ticketNumber,\n uint256 e3Id,\n uint256 seed\n ) internal pure returns (uint256) {\n bytes32 hash = keccak256(\n abi.encodePacked(node, ticketNumber, e3Id, seed)\n );\n return uint256(hash);\n }\n\n /// @notice Validates that a node is eligible to submit a ticket\n /// @dev Uses snapshot of ticket balance at E3 request block for deterministic validation\n /// @param node Address of the ciphernode\n /// @param ticketNumber The ticket number being submitted\n /// @param e3Id ID of the E3 computation\n function _validateNodeEligibility(\n address node,\n uint256 ticketNumber,\n uint256 e3Id\n ) internal view {\n require(ticketNumber > 0, InvalidTicketNumber());\n require(\n address(bondingRegistry) != address(0),\n BondingRegistryNotSet()\n );\n\n Committee storage c = committees[e3Id];\n\n // @todo Ensure we check everywhere that we use the block before the request block\n // to ensure cases where everything is done in the same block are handled correctly.\n uint256 ticketBalance = bondingRegistry.getTicketBalanceAtBlock(\n node,\n c.requestBlock - 1\n );\n uint256 ticketPrice = bondingRegistry.ticketPrice();\n\n require(ticketPrice > 0, InvalidTicketNumber());\n uint256 availableTickets = ticketBalance / ticketPrice;\n\n require(availableTickets > 0, NodeNotEligible());\n require(ticketNumber <= availableTickets, InvalidTicketNumber());\n }\n\n /// @notice Inserts a node into the top-N list - Smallest scores\n /// @dev If the node is not in the top-N, it is added to the top-N.\n /// @param c Committee storage reference\n /// @param node Address of the node\n /// @param score Score of the node\n /// @return entered Whether the node was inserted into the top-N\n function _insertTopN(\n Committee storage c,\n address node,\n uint256 score\n ) internal returns (bool entered) {\n address[] storage top = c.topNodes;\n uint256 cap = c.threshold[1];\n\n if (top.length < cap) {\n top.push(node);\n c.scoreOf[node] = score;\n return true;\n }\n\n uint256 worstIdx = 0;\n uint256 worstScore = c.scoreOf[top[0]];\n unchecked {\n for (uint256 i = 1; i < top.length; ++i) {\n uint256 s = c.scoreOf[top[i]];\n if (s > worstScore) {\n worstScore = s;\n worstIdx = i;\n }\n }\n }\n\n if (score >= worstScore) return false;\n\n top[worstIdx] = node;\n c.scoreOf[node] = score;\n\n return true;\n }\n}\n"
|
|
281
293
|
},
|
|
282
294
|
"project/contracts/slashing/SlashingManager.sol": {
|
|
283
295
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\n\npragma solidity >=0.8.27;\n\nimport {\n AccessControl\n} from \"@openzeppelin/contracts/access/AccessControl.sol\";\nimport { ISlashingManager } from \"../interfaces/ISlashingManager.sol\";\nimport { IBondingRegistry } from \"../interfaces/IBondingRegistry.sol\";\nimport { ISlashVerifier } from \"../interfaces/ISlashVerifier.sol\";\n\n/**\n * @title SlashingManager\n * @notice Implementation of slashing management with proposal, appeal, and execution workflows\n * @dev Role-based access control for slashers, verifiers, and governance with configurable slash policies\n */\ncontract SlashingManager is ISlashingManager, AccessControl {\n // ======================\n // Constants & Roles\n // ======================\n\n /// @notice Role identifier for accounts authorized to propose and execute slashes\n bytes32 public constant SLASHER_ROLE = keccak256(\"SLASHER_ROLE\");\n\n /// @notice Role identifier for accounts authorized to verify cryptographic proofs in slash proposals\n bytes32 public constant VERIFIER_ROLE = keccak256(\"VERIFIER_ROLE\");\n\n /// @notice Role identifier for governance accounts that can configure policies, resolve appeals, and manage bans\n bytes32 public constant GOVERNANCE_ROLE = keccak256(\"GOVERNANCE_ROLE\");\n\n // ======================\n // Storage\n // ======================\n\n /// @notice Reference to the bonding registry contract where slash penalties are executed\n /// @dev Used to call slashTicketBalance() and slashLicenseBond() when executing slashes\n IBondingRegistry public bondingRegistry;\n\n /// @notice Mapping from slash reason hash to its configured policy\n /// @dev Stores penalty amounts, proof requirements, and appeal settings for each slash type\n mapping(bytes32 reason => SlashPolicy policy) public slashPolicies;\n\n /// @notice Internal storage for all slash proposals indexed by proposal ID\n /// @dev Sequentially indexed starting from 0, accessed via getSlashProposal()\n mapping(uint256 proposalId => SlashProposal proposal) internal _proposals;\n\n /// @notice Counter for total number of slash proposals ever created\n /// @dev Also serves as the next proposal ID to be assigned\n uint256 public totalProposals;\n\n /// @notice Mapping tracking which nodes are currently banned from the network\n /// @dev Set to true when a node is banned (either via executeSlash or banNode), false when unbanned\n mapping(address node => bool banned) public banned;\n\n // ======================\n // Modifiers\n // ======================\n\n /// @notice Restricts function access to accounts with SLASHER_ROLE\n /// @dev Reverts with Unauthorized() if caller lacks the role\n modifier onlySlasher() {\n if (!hasRole(SLASHER_ROLE, msg.sender)) revert Unauthorized();\n _;\n }\n\n /// @notice Restricts function access to accounts with VERIFIER_ROLE\n /// @dev Reverts with Unauthorized() if caller lacks the role\n modifier onlyVerifier() {\n if (!hasRole(VERIFIER_ROLE, msg.sender)) revert Unauthorized();\n _;\n }\n\n /// @notice Restricts function access to accounts with GOVERNANCE_ROLE\n /// @dev Reverts with Unauthorized() if caller lacks the role\n modifier onlyGovernance() {\n if (!hasRole(GOVERNANCE_ROLE, msg.sender)) revert Unauthorized();\n _;\n }\n\n // ======================\n // Constructor\n // ======================\n\n /**\n * @notice Initializes the SlashingManager contract with admin and bonding registry\n * @dev Sets up initial role assignments and bonding registry reference\n * @param admin Address to receive DEFAULT_ADMIN_ROLE and GOVERNANCE_ROLE\n * @param _bondingRegistry Address of the bonding registry contract for executing slashes\n * Requirements:\n * - admin must not be zero address\n * - _bondingRegistry must not be zero address\n */\n constructor(address admin, address _bondingRegistry) {\n require(admin != address(0), ZeroAddress());\n require(_bondingRegistry != address(0), ZeroAddress());\n\n bondingRegistry = IBondingRegistry(_bondingRegistry);\n\n _grantRole(DEFAULT_ADMIN_ROLE, admin);\n _grantRole(GOVERNANCE_ROLE, admin);\n }\n\n // ======================\n // View Functions\n // ======================\n\n /// @inheritdoc ISlashingManager\n function getSlashPolicy(\n bytes32 reason\n ) external view returns (SlashPolicy memory) {\n return slashPolicies[reason];\n }\n\n /// @inheritdoc ISlashingManager\n function getSlashProposal(\n uint256 proposalId\n ) external view returns (SlashProposal memory) {\n require(proposalId < totalProposals, InvalidProposal());\n return _proposals[proposalId];\n }\n\n /// @inheritdoc ISlashingManager\n function isBanned(address node) external view returns (bool) {\n return banned[node];\n }\n\n // ======================\n // Admin Functions\n // ======================\n\n /// @inheritdoc ISlashingManager\n function setSlashPolicy(\n bytes32 reason,\n SlashPolicy calldata policy\n ) external onlyRole(GOVERNANCE_ROLE) {\n require(reason != bytes32(0), InvalidPolicy());\n require(policy.enabled, InvalidPolicy());\n require(\n policy.ticketPenalty > 0 || policy.licensePenalty > 0,\n InvalidPolicy()\n );\n\n if (policy.requiresProof) {\n require(policy.proofVerifier != address(0), VerifierNotSet());\n // TODO: Should we allow appeal window for proof required?\n require(policy.appealWindow == 0, InvalidPolicy());\n } else {\n require(policy.appealWindow > 0, InvalidPolicy());\n }\n\n slashPolicies[reason] = policy;\n emit SlashPolicyUpdated(reason, policy);\n }\n\n /// @inheritdoc ISlashingManager\n function setBondingRegistry(\n address newBondingRegistry\n ) external onlyRole(DEFAULT_ADMIN_ROLE) {\n require(newBondingRegistry != address(0), ZeroAddress());\n bondingRegistry = IBondingRegistry(newBondingRegistry);\n }\n\n /// @inheritdoc ISlashingManager\n function addSlasher(address slasher) external onlyRole(DEFAULT_ADMIN_ROLE) {\n require(slasher != address(0), ZeroAddress());\n _grantRole(SLASHER_ROLE, slasher);\n }\n\n /// @inheritdoc ISlashingManager\n function removeSlasher(\n address slasher\n ) external onlyRole(DEFAULT_ADMIN_ROLE) {\n _revokeRole(SLASHER_ROLE, slasher);\n }\n\n /// @inheritdoc ISlashingManager\n function addVerifier(\n address verifier\n ) external onlyRole(DEFAULT_ADMIN_ROLE) {\n require(verifier != address(0), ZeroAddress());\n _grantRole(VERIFIER_ROLE, verifier);\n }\n\n /// @inheritdoc ISlashingManager\n function removeVerifier(\n address verifier\n ) external onlyRole(DEFAULT_ADMIN_ROLE) {\n _revokeRole(VERIFIER_ROLE, verifier);\n }\n\n // ======================\n // Slashing Functions\n // ======================\n\n /// @inheritdoc ISlashingManager\n function proposeSlash(\n address operator,\n bytes32 reason,\n bytes calldata proof\n )\n external\n // TODO: Do we need an onlySlasher modifier?\n // Can anyone propose a slash?\n onlySlasher\n returns (uint256 proposalId)\n {\n require(operator != address(0), ZeroAddress());\n\n SlashPolicy memory policy = slashPolicies[reason];\n require(policy.enabled, SlashReasonDisabled());\n\n proposalId = totalProposals;\n totalProposals = proposalId + 1;\n\n uint256 executableAt = block.timestamp + policy.appealWindow;\n SlashProposal storage p = _proposals[proposalId];\n\n p.operator = operator;\n p.reason = reason;\n p.ticketAmount = policy.ticketPenalty;\n p.licenseAmount = policy.licensePenalty;\n p.proposedAt = block.timestamp;\n p.executableAt = executableAt;\n p.proposer = msg.sender;\n p.proofHash = keccak256(proof);\n\n if (policy.requiresProof) {\n require(proof.length != 0, ProofRequired());\n bool ok = ISlashVerifier(policy.proofVerifier).verify(\n proposalId,\n proof\n );\n require(ok, InvalidProof());\n p.proofVerified = true;\n }\n\n emit SlashProposed(\n proposalId,\n operator,\n reason,\n policy.ticketPenalty,\n policy.licensePenalty,\n executableAt,\n msg.sender\n );\n }\n\n /// @inheritdoc ISlashingManager\n function executeSlash(uint256 proposalId) external {\n require(proposalId < totalProposals, InvalidProposal());\n SlashProposal storage p = _proposals[proposalId];\n\n // Has already been executed?\n require(!p.executed, AlreadyExecuted());\n p.executed = true;\n\n SlashPolicy memory policy = slashPolicies[p.reason];\n\n if (policy.requiresProof) {\n // Appeal window is 0 by policy validation, so we dont check for appeal gating\n require(p.proofVerified, InvalidProof());\n } else {\n // Evidence mode with appeals\n require(block.timestamp >= p.executableAt, AppealWindowActive());\n if (p.appealed) {\n require(p.resolved, AppealPending());\n require(!p.appealUpheld, AppealUpheld()); // approved = appeal upheld => cancel slash, return?\n }\n }\n\n if (p.ticketAmount > 0) {\n bondingRegistry.slashTicketBalance(\n p.operator,\n p.ticketAmount,\n p.reason\n );\n }\n\n if (p.licenseAmount > 0) {\n bondingRegistry.slashLicenseBond(\n p.operator,\n p.licenseAmount,\n p.reason\n );\n }\n\n if (policy.banNode) {\n banned[p.operator] = true;\n emit NodeBanUpdated(p.operator, true, p.reason, msg.sender);\n }\n\n emit SlashExecuted(\n proposalId,\n p.operator,\n p.reason,\n p.ticketAmount,\n p.licenseAmount,\n p.executed\n );\n }\n\n // ======================\n // Appeal Functions\n // ======================\n\n /// @inheritdoc ISlashingManager\n function fileAppeal(uint256 proposalId, string calldata evidence) external {\n require(proposalId < totalProposals, InvalidProposal());\n // TODO: Should we reject the appeal if the proposal has a cryptographic proof?\n SlashProposal storage p = _proposals[proposalId];\n\n // Only the accused can appeal\n require(msg.sender == p.operator, Unauthorized());\n // Only in the window\n require(block.timestamp < p.executableAt, AppealWindowExpired());\n // Only once\n require(!p.appealed, AlreadyAppealed());\n\n p.appealed = true;\n\n emit AppealFiled(proposalId, p.operator, p.reason, evidence);\n }\n\n /// @inheritdoc ISlashingManager\n function resolveAppeal(\n uint256 proposalId,\n bool appealUpheld,\n string calldata resolution\n ) external onlyGovernance {\n require(proposalId < totalProposals, InvalidProposal());\n SlashProposal storage p = _proposals[proposalId];\n\n require(p.appealed, InvalidProposal());\n require(!p.resolved, AlreadyResolved());\n\n p.resolved = true;\n p.appealUpheld = appealUpheld; // true => cancel slash, false => slash stands\n\n emit AppealResolved(\n proposalId,\n p.operator,\n appealUpheld,\n msg.sender,\n resolution\n );\n }\n\n // ======================\n // Ban Management\n // ======================\n\n /// @inheritdoc ISlashingManager\n function updateBanStatus(\n address node,\n bool status,\n bytes32 reason\n ) external onlyGovernance {\n require(node != address(0), ZeroAddress());\n\n banned[node] = status;\n emit NodeBanUpdated(node, status, reason, msg.sender);\n }\n}\n"
|
|
284
296
|
},
|
|
285
297
|
"project/contracts/test/MockCiphernodeRegistry.sol": {
|
|
286
|
-
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { ICiphernodeRegistry } from \"../interfaces/ICiphernodeRegistry.sol\";\n\ncontract MockCiphernodeRegistry is ICiphernodeRegistry {\n function requestCommittee(\n uint256,\n uint256,\n uint32[2] calldata\n ) external pure returns (bool success) {\n success = true;\n }\n\n function isEnabled(address) external pure returns (bool) {\n return true;\n }\n\n function committeePublicKey(uint256 e3Id) external pure returns (bytes32) {\n if (e3Id == type(uint256).max) {\n return bytes32(0);\n } else {\n return keccak256(abi.encode(e3Id));\n }\n }\n\n function isCiphernodeEligible(address) external pure returns (bool) {\n return false;\n }\n\n // solhint-disable-next-line no-empty-blocks\n function addCiphernode(address) external pure {}\n\n // solhint-disable-next-line no-empty-blocks\n function removeCiphernode(address, uint256[] calldata) external pure {}\n\n function publishCommittee(\n uint256,\n address[] calldata,\n bytes calldata,\n bytes32\n ) external pure {} // solhint-disable-line no-empty-blocks\n\n function getCommitteeNodes(\n uint256\n ) external pure returns (address[] memory) {\n address[] memory nodes = new address[](0);\n return nodes;\n }\n\n function root() external pure returns (uint256) {\n return 0;\n }\n\n function rootAt(uint256) external pure returns (uint256) {\n return 0;\n }\n\n function treeSize() external pure returns (uint256) {\n return 0;\n }\n\n function getBondingRegistry() external pure returns (address) {\n return address(0);\n }\n\n // solhint-disable-next-line no-empty-blocks\n function setEnclave(
|
|
298
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { ICiphernodeRegistry } from \"../interfaces/ICiphernodeRegistry.sol\";\nimport { IEnclave } from \"../interfaces/IEnclave.sol\";\nimport { IBondingRegistry } from \"../interfaces/IBondingRegistry.sol\";\n\ncontract MockCiphernodeRegistry is ICiphernodeRegistry {\n function requestCommittee(\n uint256,\n uint256,\n uint32[2] calldata\n ) external pure returns (bool success) {\n success = true;\n }\n\n function getCommitteeDeadline(uint256) external view returns (uint256) {\n return block.timestamp + 10;\n }\n\n function isEnabled(address) external pure returns (bool) {\n return true;\n }\n\n function committeePublicKey(uint256 e3Id) external pure returns (bytes32) {\n if (e3Id == type(uint256).max) {\n return bytes32(0);\n } else {\n return keccak256(abi.encode(e3Id));\n }\n }\n\n function isCiphernodeEligible(address) external pure returns (bool) {\n return false;\n }\n\n // solhint-disable-next-line no-empty-blocks\n function addCiphernode(address) external pure {}\n\n // solhint-disable-next-line no-empty-blocks\n function removeCiphernode(address, uint256[] calldata) external pure {}\n\n function publishCommittee(\n uint256,\n address[] calldata,\n bytes calldata,\n bytes32\n ) external pure {} // solhint-disable-line no-empty-blocks\n\n function getCommitteeNodes(\n uint256\n ) external pure returns (address[] memory) {\n address[] memory nodes = new address[](0);\n return nodes;\n }\n\n function root() external pure returns (uint256) {\n return 0;\n }\n\n function rootAt(uint256) external pure returns (uint256) {\n return 0;\n }\n\n function treeSize() external pure returns (uint256) {\n return 0;\n }\n\n function getBondingRegistry() external pure returns (address) {\n return address(0);\n }\n\n // solhint-disable-next-line no-empty-blocks\n function setEnclave(IEnclave) external pure {}\n\n // solhint-disable-next-line no-empty-blocks\n function setBondingRegistry(IBondingRegistry) external pure {}\n\n // solhint-disable-next-line no-empty-blocks\n function submitTicket(uint256, uint256) external pure {}\n\n // solhint-disable-next-line no-empty-blocks\n function finalizeCommittee(uint256) external pure returns (bool) {\n return true;\n }\n\n // solhint-disable-next-line no-empty-blocks\n function setSortitionSubmissionWindow(uint256) external pure {}\n\n function isOpen(uint256) external pure returns (bool) {\n return false;\n }\n}\n\ncontract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry {\n error CommitteeNotPublished();\n\n function requestCommittee(\n uint256,\n uint256,\n uint32[2] calldata\n ) external pure returns (bool success) {\n success = true;\n }\n\n function getCommitteeDeadline(uint256) external view returns (uint256) {\n return block.timestamp + 10;\n }\n\n function isEnabled(address) external pure returns (bool) {\n return true;\n }\n\n function committeePublicKey(uint256) external pure returns (bytes32) {\n revert CommitteeNotPublished();\n }\n\n function isCiphernodeEligible(address) external pure returns (bool) {\n return false;\n }\n\n // solhint-disable-next-line no-empty-blocks\n function addCiphernode(address) external pure {}\n\n // solhint-disable-next-line no-empty-blocks\n function removeCiphernode(address, uint256[] calldata) external pure {}\n\n function publishCommittee(\n uint256,\n address[] calldata,\n bytes calldata,\n bytes32\n ) external pure {} // solhint-disable-line no-empty-blocks\n\n function getCommitteeNodes(\n uint256\n ) external pure returns (address[] memory) {\n address[] memory nodes = new address[](0);\n return nodes;\n }\n\n function root() external pure returns (uint256) {\n return 0;\n }\n\n function rootAt(uint256) external pure returns (uint256) {\n return 0;\n }\n\n function treeSize() external pure returns (uint256) {\n return 0;\n }\n\n function getBondingRegistry() external pure returns (address) {\n return address(0);\n }\n\n // solhint-disable-next-line no-empty-blocks\n function setEnclave(IEnclave) external pure {}\n\n // solhint-disable-next-line no-empty-blocks\n function setBondingRegistry(IBondingRegistry) external pure {}\n\n // solhint-disable-next-line no-empty-blocks\n function setSortitionSubmissionWindow(uint256) external pure {}\n\n // solhint-disable-next-line no-empty-blocks\n function submitTicket(uint256, uint256) external pure {}\n\n // solhint-disable-next-line no-empty-blocks\n function finalizeCommittee(uint256) external pure returns (bool) {\n return true;\n }\n\n function isOpen(uint256) external pure returns (bool) {\n return false;\n }\n}\n"
|
|
287
299
|
},
|
|
288
300
|
"project/contracts/test/MockComputeProvider.sol": {
|
|
289
301
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport {\n IComputeProvider,\n IDecryptionVerifier\n} from \"../interfaces/IComputeProvider.sol\";\n\ncontract MockComputeProvider is IComputeProvider {\n error InvalidParams();\n\n function validate(\n uint256,\n uint256,\n bytes memory params\n ) external pure returns (IDecryptionVerifier decryptionVerifier) {\n require(params.length == 32, InvalidParams());\n // solhint-disable no-inline-assembly\n assembly {\n decryptionVerifier := mload(add(params, 32))\n }\n (decryptionVerifier) = abi.decode(params, (IDecryptionVerifier));\n }\n}\n"
|
|
@@ -292,7 +304,7 @@
|
|
|
292
304
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { IDecryptionVerifier } from \"../interfaces/IDecryptionVerifier.sol\";\n\ncontract MockDecryptionVerifier is IDecryptionVerifier {\n function verify(\n uint256,\n bytes32,\n bytes memory data\n ) external pure returns (bool success) {\n data;\n\n if (data.length > 0) success = true;\n }\n}\n"
|
|
293
305
|
},
|
|
294
306
|
"project/contracts/test/MockE3Program.sol": {
|
|
295
|
-
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { IE3Program } from \"../interfaces/IE3Program.sol\";\n\ncontract MockE3Program is IE3Program {\n error InvalidParams(bytes e3ProgramParams, bytes computeProviderParams);\n error E3AlreadyInitialized();\n\n bytes32 public constant ENCRYPTION_SCHEME_ID = keccak256(\"fhe.rs:BFV\");\n\n mapping(uint256 e3Id => bytes32 paramsHash) public paramsHashes;\n\n
|
|
307
|
+
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { IE3Program } from \"../interfaces/IE3Program.sol\";\n\ncontract MockE3Program is IE3Program {\n error InvalidParams(bytes e3ProgramParams, bytes computeProviderParams);\n error E3AlreadyInitialized();\n error InvalidInput();\n\n bytes32 public constant ENCRYPTION_SCHEME_ID = keccak256(\"fhe.rs:BFV\");\n\n mapping(uint256 e3Id => bytes32 paramsHash) public paramsHashes;\n\n function validate(\n uint256 e3Id,\n uint256,\n bytes calldata e3ProgramParams,\n bytes calldata computeProviderParams,\n bytes calldata\n ) external returns (bytes32) {\n require(\n computeProviderParams.length == 32,\n InvalidParams(e3ProgramParams, computeProviderParams)\n );\n\n require(paramsHashes[e3Id] == bytes32(0), E3AlreadyInitialized());\n paramsHashes[e3Id] = keccak256(e3ProgramParams);\n return ENCRYPTION_SCHEME_ID;\n }\n\n function publishInput(uint256, bytes memory data) external pure {\n if (data.length == 3) {\n revert InvalidInput();\n }\n }\n\n function verify(\n uint256,\n bytes32,\n bytes memory data\n ) external pure returns (bool success) {\n data;\n if (data.length > 0) success = true;\n }\n}\n"
|
|
296
308
|
},
|
|
297
309
|
"project/contracts/test/MockSlashingVerifier.sol": {
|
|
298
310
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\npragma solidity >=0.8.27;\n\nimport { ISlashVerifier } from \"../interfaces/ISlashVerifier.sol\";\n\ncontract MockSlashingVerifier is ISlashVerifier {\n function verify(\n uint256,\n bytes memory data\n ) external pure returns (bool success) {\n data;\n\n if (data.length > 0) success = true;\n }\n}\n"
|
|
@@ -305,6 +317,9 @@
|
|
|
305
317
|
},
|
|
306
318
|
"project/contracts/token/EnclaveToken.sol": {
|
|
307
319
|
"content": "// SPDX-License-Identifier: LGPL-3.0-only\n//\n// This file is provided WITHOUT ANY WARRANTY;\n// without even the implied warranty of MERCHANTABILITY\n// or FITNESS FOR A PARTICULAR PURPOSE.\n\npragma solidity >=0.8.27;\n\nimport { ERC20 } from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {\n ERC20Permit,\n Nonces\n} from \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol\";\nimport {\n ERC20Votes\n} from \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol\";\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport {\n AccessControl\n} from \"@openzeppelin/contracts/access/AccessControl.sol\";\n\n/**\n * @title EnclaveToken\n * @notice The governance and utility token for the Enclave protocol\n * @dev ERC20 token with voting capabilities, permit functionality, and controlled minting.\n * Implements transfer restrictions that can be toggled by the owner to control token\n * transferability during early phases. Supports a maximum supply cap and role-based\n * minting through the MINTER_ROLE.\n */\ncontract EnclaveToken is\n ERC20,\n ERC20Permit,\n ERC20Votes,\n Ownable,\n AccessControl\n{\n // Custom errors\n\n /// @notice Thrown when a zero address is provided where a valid address is required\n error ZeroAddress();\n\n /// @notice Thrown when attempting to mint zero tokens\n error ZeroAmount();\n\n /// @notice Thrown when minting would exceed the maximum token supply\n error ExceedsTotalSupply();\n\n /// @notice Thrown when array parameters have mismatched lengths\n error ArrayLengthMismatch();\n\n /// @notice Thrown when a transfer is attempted while restrictions are active and neither party is whitelisted\n error TransferNotAllowed();\n\n /// @notice Maximum supply of the token: 1.2 billion tokens with 18 decimals\n /// @dev Hard cap on total token supply that cannot be exceeded through minting\n uint256 public constant MAX_SUPPLY = 1_200_000_000e18;\n\n /// @notice Role identifier for accounts authorized to mint new tokens\n /// @dev Keccak256 hash of \"MINTER_ROLE\" used in AccessControl\n bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n\n /// @notice Tracks the cumulative amount of tokens minted since deployment\n /// @dev Incremented with each mint operation to enforce MAX_SUPPLY cap\n uint256 public totalMinted;\n\n /// @notice Mapping of addresses permitted to transfer tokens when restrictions are active\n /// @dev When transfersRestricted is true, only whitelisted addresses can send or receive tokens\n mapping(address account => bool allowed) public transferWhitelisted;\n\n /// @notice Indicates whether token transfers are currently restricted\n /// @dev When true, only whitelisted addresses can transfer tokens\n bool public transfersRestricted;\n\n /// @notice Emitted when tokens are minted as part of a named allocation\n /// @param recipient Address receiving the minted tokens\n /// @param amount Number of tokens minted (18 decimals)\n /// @param allocation Description of the allocation for tracking purposes\n event AllocationMinted(\n address indexed recipient,\n uint256 amount,\n string allocation\n );\n\n /// @notice Emitted when the transfer restriction setting is changed\n /// @param restricted New state of transfer restrictions (true = restricted, false = unrestricted)\n event TransferRestrictionUpdated(bool restricted);\n\n /// @notice Emitted when an address is added to or removed from the transfer whitelist\n /// @param account Address whose whitelist status changed\n /// @param whitelisted New whitelist status (true = whitelisted, false = not whitelisted)\n event TransferWhitelistUpdated(address indexed account, bool whitelisted);\n\n /**\n * @notice Initializes the Enclave token with name \"Enclave\" and symbol \"ENCL\"\n * @dev Sets up the token with voting and permit functionality. Grants admin and minter\n * roles to the owner, enables transfer restrictions, and whitelists the owner.\n * @param _owner Address that will own the contract and receive DEFAULT_ADMIN_ROLE and MINTER_ROLE\n */\n constructor(\n address _owner\n ) ERC20(\"Enclave\", \"ENCL\") ERC20Permit(\"Enclave\") Ownable(_owner) {\n // Grant the deployer all admin roles.\n _grantRole(DEFAULT_ADMIN_ROLE, _owner);\n _grantRole(MINTER_ROLE, _owner);\n\n // Initialise state variables.\n transfersRestricted = true;\n transferWhitelisted[_owner] = true;\n\n emit TransferRestrictionUpdated(true);\n emit TransferWhitelistUpdated(_owner, true);\n }\n\n /**\n * @notice Mints a named allocation of tokens to a specified recipient\n * @dev Only callable by accounts with MINTER_ROLE. Reverts if recipient is zero address,\n * amount is zero, or minting would exceed MAX_SUPPLY. Updates totalMinted tracker.\n * @param recipient Address to receive the minted tokens (cannot be zero address)\n * @param amount Number of tokens to mint in wei (18 decimals, must be greater than zero)\n * @param allocation Human-readable description of this allocation for tracking and auditing purposes\n */\n function mintAllocation(\n address recipient,\n uint256 amount,\n string memory allocation\n ) external onlyRole(MINTER_ROLE) {\n if (recipient == address(0)) revert ZeroAddress();\n if (amount == 0) revert ZeroAmount();\n // Ensure we do not exceed the total supply.\n if (totalMinted + amount > MAX_SUPPLY) revert ExceedsTotalSupply();\n\n _mint(recipient, amount);\n totalMinted += amount;\n emit AllocationMinted(recipient, amount, allocation);\n }\n\n /**\n * @notice Mints multiple named allocations to different recipients in a single transaction\n * @dev Only callable by accounts with MINTER_ROLE. All arrays must have the same length.\n * Reverts if any amount is zero or if the cumulative minting would exceed MAX_SUPPLY.\n * Gas-efficient for distributing tokens to multiple addresses.\n * @param recipients Array of addresses to receive minted tokens\n * @param amounts Array of token amounts to mint (18 decimals, must match recipients length)\n * @param allocations Array of allocation descriptions (must match recipients length)\n */\n function batchMintAllocations(\n address[] calldata recipients,\n uint256[] calldata amounts,\n string[] calldata allocations\n ) external onlyRole(MINTER_ROLE) {\n uint256 len = recipients.length;\n if (amounts.length != len || allocations.length != len) {\n revert ArrayLengthMismatch();\n }\n\n uint256 minted = totalMinted;\n\n for (uint256 i = 0; i < len; i++) {\n address recipient = recipients[i];\n uint256 amount = amounts[i];\n if (amount == 0) revert ZeroAmount();\n\n if (amount > MAX_SUPPLY - minted) revert ExceedsTotalSupply();\n minted += amount;\n\n _mint(recipient, amount);\n emit AllocationMinted(recipient, amount, allocations[i]);\n }\n\n totalMinted = minted;\n }\n\n /**\n * @notice Enables or disables transfer restrictions for the token\n * @dev Only callable by the contract owner. When restrictions are enabled, only whitelisted\n * addresses can send or receive tokens. Useful for controlling token circulation during\n * early phases before public trading.\n * @param restricted True to enable restrictions, false to allow unrestricted transfers\n */\n function setTransferRestriction(bool restricted) external onlyOwner {\n transfersRestricted = restricted;\n emit TransferRestrictionUpdated(restricted);\n }\n\n /**\n * @notice Toggles an account's transfer whitelist status between enabled and disabled\n * @dev Only callable by the contract owner. Flips the current whitelist state for the given\n * account. Whitelisted accounts can send and receive tokens even when transfer restrictions\n * are active.\n * @param account Address whose whitelist status will be toggled\n */\n function toggleTransferWhitelist(address account) external onlyOwner {\n bool newStatus = !transferWhitelisted[account];\n transferWhitelisted[account] = newStatus;\n emit TransferWhitelistUpdated(account, newStatus);\n }\n\n /**\n * @notice Whitelists key protocol contracts to allow them to transfer tokens during restricted periods\n * @dev Only callable by the contract owner. Convenience function for whitelisting multiple protocol\n * contracts in a single transaction. Zero addresses are safely ignored. Typically used to whitelist\n * contracts like bonding managers and vesting escrows that need to handle tokens on behalf of users.\n * @param bondingManager Address of the BondingManager contract (zero address skipped)\n * @param vestingEscrow Address of the VestingEscrow contract (zero address skipped)\n */\n function whitelistContracts(\n address bondingManager,\n address vestingEscrow\n ) external onlyOwner {\n if (bondingManager != address(0)) {\n transferWhitelisted[bondingManager] = true;\n emit TransferWhitelistUpdated(bondingManager, true);\n }\n if (vestingEscrow != address(0)) {\n transferWhitelisted[vestingEscrow] = true;\n emit TransferWhitelistUpdated(vestingEscrow, true);\n }\n }\n\n /**\n * @notice Internal hook that enforces transfer restrictions and updates voting power\n * @dev Overrides ERC20 and ERC20Votes to add transfer restriction logic. Reverts if transfers\n * are restricted and neither sender nor receiver is whitelisted. Minting (from == 0) and\n * burning (to == 0) are always allowed regardless of restrictions.\n * @param from Address sending tokens (zero address for minting)\n * @param to Address receiving tokens (zero address for burning)\n * @param value Amount of tokens being transferred\n */\n function _update(\n address from,\n address to,\n uint256 value\n ) internal override(ERC20, ERC20Votes) {\n // When transfers are restricted, only whitelisted addresses can send or receive.\n if (from != address(0) && to != address(0) && transfersRestricted) {\n if (!transferWhitelisted[from] && !transferWhitelisted[to]) {\n revert TransferNotAllowed();\n }\n }\n super._update(from, to, value);\n }\n\n /**\n * @notice Checks if this contract implements a given interface\n * @dev Implements ERC165 interface detection via AccessControl\n * @param interfaceId The interface identifier to check, as specified in ERC-165\n * @return bool True if the contract implements the interface, false otherwise\n */\n function supportsInterface(\n bytes4 interfaceId\n ) public view override(AccessControl) returns (bool) {\n return super.supportsInterface(interfaceId);\n }\n\n /**\n * @notice Returns the current nonce for an address, used for permit signatures\n * @dev Resolves the override conflict between ERC20Permit and Nonces by calling the parent\n * implementation. Nonces are incremented with each permit to prevent replay attacks.\n * @param owner Address to query the nonce for\n * @return uint256 The current nonce value for the given address\n */\n function nonces(\n address owner\n ) public view override(ERC20Permit, Nonces) returns (uint256) {\n return super.nonces(owner);\n }\n}\n"
|
|
320
|
+
},
|
|
321
|
+
"project/contracts/verifier/DkgPkVerifier.sol": {
|
|
322
|
+
"content": "// SPDX-License-Identifier: Apache-2.0\n// Copyright 2022 Aztec\npragma solidity >=0.8.27;\n\nuint256 constant N = 8192;\nuint256 constant LOG_N = 13;\nuint256 constant NUMBER_OF_PUBLIC_INPUTS = 17;\nuint256 constant VK_HASH = 0x170affbea8713b79ad326398ea7b49424c51fa4dca10a65185ad33dbce4d6001;\nlibrary HonkVerificationKey {\n function loadVerificationKey()\n internal\n pure\n returns (Honk.VerificationKey memory)\n {\n Honk.VerificationKey memory vk = Honk.VerificationKey({\n circuitSize: uint256(8192),\n logCircuitSize: uint256(13),\n publicInputsSize: uint256(17),\n ql: Honk.G1Point({\n x: uint256(\n 0x22a702d6d3957500783049fee5af2a27155cfdf1b6c2958942f5cde4bc63d163\n ),\n y: uint256(\n 0x003567b43861479b2347249040b9a4c4423f494e6dd8a7de9a4e3900741c4551\n )\n }),\n qr: Honk.G1Point({\n x: uint256(\n 0x1333cd6f8595ef95ab9d9744d1d00175a56c1b0bc60000ac6d4eb9ec7315c70a\n ),\n y: uint256(\n 0x1c891650f45fe25cf375ea0b322372757b89799546eed2152766130e6b99efa0\n )\n }),\n qo: Honk.G1Point({\n x: uint256(\n 0x251fdc445de2b90170e8c86c41a81303bb3de75352f2bd6f095bdab77486445a\n ),\n y: uint256(\n 0x06f20c995414f9b6bdb794492d328124e2b7676329a5a1724365076751ad2a8a\n )\n }),\n q4: Honk.G1Point({\n x: uint256(\n 0x12600bb185b845c63e4b9d5c310db2d257aa5d1d09090ef3c8bdd7334e8991d0\n ),\n y: uint256(\n 0x017ccf0bdad7381ff8a3bef25259d90c296b71c037ee58bcce51cb0b7a6e97f4\n )\n }),\n qm: Honk.G1Point({\n x: uint256(\n 0x1a5c96bf5dac44c45e28380daccedfec1d9a8919999a0d748a8686f7f42cba76\n ),\n y: uint256(\n 0x28c5876a4c88720ff49870cbc3b7f41c26a131f4d57ce95cbc6127859c08931f\n )\n }),\n qc: Honk.G1Point({\n x: uint256(\n 0x2c740230a118ad04f1482eeef2e75a14aed0f961b763120de34d0fbe63ca12b0\n ),\n y: uint256(\n 0x1b9aa7fd5bf93acbaadd615b6e0829e714539fe471bece34b359cd6a96d00294\n )\n }),\n qLookup: Honk.G1Point({\n x: uint256(\n 0x0c4032c3079594eb75a8449d3d5ce8bc3661650d53f9b24d923d8f404cb0bbc9\n ),\n y: uint256(\n 0x1084d709650356d40f0158fd6da81f54eb5fe796a0ca89441369b7c24301f851\n )\n }),\n qArith: Honk.G1Point({\n x: uint256(\n 0x1f13031b7801d2da6d5206a7da087d7661d87905cef81f24e3cbe9e7a1c51488\n ),\n y: uint256(\n 0x2d93aa6923ac15844842bd024199117f43ca14c883f37570030fdf385356a18a\n )\n }),\n qDeltaRange: Honk.G1Point({\n x: uint256(\n 0x300557c1eda8ed63912d72e051bb3431bd54f7395755b820a59ff85295eb36d9\n ),\n y: uint256(\n 0x227e5f5f62555201e96e188514b5387fb7e026a4af0ac209ec6730c4e0e6cc76\n )\n }),\n qElliptic: Honk.G1Point({\n x: uint256(\n 0x20a7925feb244b2ba41d31db4651a6c6c6be672ed0e7711f2ef4c2b89f9c4a5f\n ),\n y: uint256(\n 0x2472b531a32a7d7f80ec3c3375977ba4968f326786473e34d1fd73e6236cf661\n )\n }),\n qMemory: Honk.G1Point({\n x: uint256(\n 0x02095e5acd019e4edaf8d736615b4a159deafdb98a171db591ed499b0219fb4b\n ),\n y: uint256(\n 0x0644f4dc54ac72663d956a233412f748e74b1591553ecbca703829acf75e471a\n )\n }),\n qNnf: Honk.G1Point({\n x: uint256(\n 0x022bae55678012a184d4cb970b8f9a6d1334097d611de534bef96fc9f8d9d011\n ),\n y: uint256(\n 0x284ee2d499274885bcb7a0d77b0a261d9192ec413b0c0a86fb27ae88cdd5a72c\n )\n }),\n qPoseidon2External: Honk.G1Point({\n x: uint256(\n 0x2dd8c7e5ce5f6a7053c173bb140d46830ab962c6c45d637d66171ee540bb21d5\n ),\n y: uint256(\n 0x033b47f5f8496af8d227ef175c47b3f7cd666ebc94f0e63562ebfcf30d73f6e2\n )\n }),\n qPoseidon2Internal: Honk.G1Point({\n x: uint256(\n 0x2940b2ab034af88869e264e12ee268b16c221b5f2cdd96748cce870c1a7bb24a\n ),\n y: uint256(\n 0x04cfb9bfe08adf0e6f25c2cc9e83448d8f23d9fb3a0efba8d41768520d62d0e8\n )\n }),\n s1: Honk.G1Point({\n x: uint256(\n 0x1b300dbb752984d39a06a3278fc9048a525d0fdf14ef98f17ffb42a971fb9032\n ),\n y: uint256(\n 0x01ccd716ea4eeba47b57e3110f305af0d1e2feec11f2656816dbb234ea0b3b1b\n )\n }),\n s2: Honk.G1Point({\n x: uint256(\n 0x0d2e187f65cf5ab43eac061f54e93bdcc292b2f518ee632b030ea3290143139c\n ),\n y: uint256(\n 0x03ff9967978b924f43af1e71f6281ee36a96c1b647bbf8c17e3cb4626068b78b\n )\n }),\n s3: Honk.G1Point({\n x: uint256(\n 0x2106e82e5e59b21be7c8ac07ede04efc6445f01e31b5ce7894444cb081df43d1\n ),\n y: uint256(\n 0x0571641ab150cfa4371421f984cc897945b69ca100d0960c2ee5ca1d17cd9670\n )\n }),\n s4: Honk.G1Point({\n x: uint256(\n 0x27599a711decc65fa4aad9515655e029868cc54d495fb8b53fa44e165702a5ee\n ),\n y: uint256(\n 0x1d9db80b8419761979511454f69fa448fc50d15062e434c6994c4ccc4912f672\n )\n }),\n t1: Honk.G1Point({\n x: uint256(\n 0x099e3bd5a0a00ab7fe18040105b9b395b5d8b7b4a63b05df652b0d10ef146d26\n ),\n y: uint256(\n 0x0015b8d2515d76e2ccec99dcd194592129af3a637f5a622a32440f860d1e2a7f\n )\n }),\n t2: Honk.G1Point({\n x: uint256(\n 0x1b917517920bad3d8bc01c9595092a222b888108dc25d1aa450e0b4bc212c37e\n ),\n y: uint256(\n 0x305e8992b148eedb22e6e992077a84482141c7ebe42000a1d58ccb74381f6d19\n )\n }),\n t3: Honk.G1Point({\n x: uint256(\n 0x13567e3b915c81013ada15236ba5cfa60111b440400b2bca37e2b1085e924a77\n ),\n y: uint256(\n 0x0148d22589b91f0d8f4674af5744dedafd63caea904b434e748f9713de8cc3d7\n )\n }),\n t4: Honk.G1Point({\n x: uint256(\n 0x043d063b130adfb37342af45d0155a28edd1a7e46c840d9c943fdf45521c64ce\n ),\n y: uint256(\n 0x261522c4089330646aff96736194949330952ae74c573d1686d9cb4a00733854\n )\n }),\n id1: Honk.G1Point({\n x: uint256(\n 0x1fa05f284bf1410f37f0d556ccfeb052395ec8625620cf724d146a3b15496d4d\n ),\n y: uint256(\n 0x0c8fc17b9b81b4f93a7b9bdd1cc5f01a30ef6e808409c456f6d6d13fe7d2eb17\n )\n }),\n id2: Honk.G1Point({\n x: uint256(\n 0x05798d2cd96b3294d8cc2f0120fa58ffea3a662e32039784f0b0b051e34e8718\n ),\n y: uint256(\n 0x02d377efbb702de6d6a38e5c42c6aaf56cc11762d724a635ea4d5dc72eb9e0ca\n )\n }),\n id3: Honk.G1Point({\n x: uint256(\n 0x110bd6ab5007b97692cd6e8ea8cab85d8b039d9e8dc2a9931fa6dcd72e0adac8\n ),\n y: uint256(\n 0x2e418397e17371fcf5a1a5c614f4dfe9727b79d5aa127ab98c04a7a4c06ccb9f\n )\n }),\n id4: Honk.G1Point({\n x: uint256(\n 0x0ccf438e4b27c76e599cda77ba04ad29cbaf538ded427e979eef69a5a9884b78\n ),\n y: uint256(\n 0x09117b0cb34fc65ed20f92de70c6f8e073c755fb4acf2d74f402c6a97ee3d01a\n )\n }),\n lagrangeFirst: Honk.G1Point({\n x: uint256(\n 0x0000000000000000000000000000000000000000000000000000000000000001\n ),\n y: uint256(\n 0x0000000000000000000000000000000000000000000000000000000000000002\n )\n }),\n lagrangeLast: Honk.G1Point({\n x: uint256(\n 0x22671e023cbbeb82d51b48eb911951a7a1f9c2bb913f3f42a56f272e205d4624\n ),\n y: uint256(\n 0x139bdfe9fe99633e5abe888702fa64a75c80d62184d0a16bd999840553954bb1\n )\n })\n });\n return vk;\n }\n}\n\npragma solidity ^0.8.27;\n\ninterface IVerifier {\n function verify(\n bytes calldata _proof,\n bytes32[] calldata _publicInputs\n ) external returns (bool);\n}\n\ntype Fr is uint256;\n\nusing { add as + } for Fr global;\nusing { sub as - } for Fr global;\nusing { mul as * } for Fr global;\n\nusing { exp as ^ } for Fr global;\nusing { notEqual as != } for Fr global;\nusing { equal as == } for Fr global;\n\nuint256 constant SUBGROUP_SIZE = 256;\nuint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order\nuint256 constant P = MODULUS;\nFr constant SUBGROUP_GENERATOR = Fr.wrap(\n 0x07b0c561a6148404f086204a9f36ffb0617942546750f230c893619174a57a76\n);\nFr constant SUBGROUP_GENERATOR_INVERSE = Fr.wrap(\n 0x204bd3277422fad364751ad938e2b5e6a54cf8c68712848a692c553d0329f5d6\n);\nFr constant MINUS_ONE = Fr.wrap(MODULUS - 1);\nFr constant ONE = Fr.wrap(1);\nFr constant ZERO = Fr.wrap(0);\n// Instantiation\n\nlibrary FrLib {\n function from(uint256 value) internal pure returns (Fr) {\n unchecked {\n return Fr.wrap(value % MODULUS);\n }\n }\n\n function fromBytes32(bytes32 value) internal pure returns (Fr) {\n unchecked {\n return Fr.wrap(uint256(value) % MODULUS);\n }\n }\n\n function toBytes32(Fr value) internal pure returns (bytes32) {\n unchecked {\n return bytes32(Fr.unwrap(value));\n }\n }\n\n function invert(Fr value) internal view returns (Fr) {\n uint256 v = Fr.unwrap(value);\n uint256 result;\n\n // Call the modexp precompile to invert in the field\n assembly {\n let free := mload(0x40)\n mstore(free, 0x20)\n mstore(add(free, 0x20), 0x20)\n mstore(add(free, 0x40), 0x20)\n mstore(add(free, 0x60), v)\n mstore(add(free, 0x80), sub(MODULUS, 2))\n mstore(add(free, 0xa0), MODULUS)\n let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)\n if iszero(success) {\n revert(0, 0)\n }\n result := mload(0x00)\n mstore(0x40, add(free, 0x80))\n }\n\n return Fr.wrap(result);\n }\n\n function pow(Fr base, uint256 v) internal view returns (Fr) {\n uint256 b = Fr.unwrap(base);\n uint256 result;\n\n // Call the modexp precompile to invert in the field\n assembly {\n let free := mload(0x40)\n mstore(free, 0x20)\n mstore(add(free, 0x20), 0x20)\n mstore(add(free, 0x40), 0x20)\n mstore(add(free, 0x60), b)\n mstore(add(free, 0x80), v)\n mstore(add(free, 0xa0), MODULUS)\n let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)\n if iszero(success) {\n revert(0, 0)\n }\n result := mload(0x00)\n mstore(0x40, add(free, 0x80))\n }\n\n return Fr.wrap(result);\n }\n\n function div(Fr numerator, Fr denominator) internal view returns (Fr) {\n unchecked {\n return numerator * invert(denominator);\n }\n }\n\n function sqr(Fr value) internal pure returns (Fr) {\n unchecked {\n return value * value;\n }\n }\n\n function unwrap(Fr value) internal pure returns (uint256) {\n unchecked {\n return Fr.unwrap(value);\n }\n }\n\n function neg(Fr value) internal pure returns (Fr) {\n unchecked {\n return Fr.wrap(MODULUS - Fr.unwrap(value));\n }\n }\n}\n\n// Free functions\nfunction add(Fr a, Fr b) pure returns (Fr) {\n unchecked {\n return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS));\n }\n}\n\nfunction mul(Fr a, Fr b) pure returns (Fr) {\n unchecked {\n return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS));\n }\n}\n\nfunction sub(Fr a, Fr b) pure returns (Fr) {\n unchecked {\n return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS));\n }\n}\n\nfunction exp(Fr base, Fr exponent) pure returns (Fr) {\n if (Fr.unwrap(exponent) == 0) return Fr.wrap(1);\n // Implement exponent with a loop as we will overflow otherwise\n for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) {\n base = base * base;\n }\n return base;\n}\n\nfunction notEqual(Fr a, Fr b) pure returns (bool) {\n unchecked {\n return Fr.unwrap(a) != Fr.unwrap(b);\n }\n}\n\nfunction equal(Fr a, Fr b) pure returns (bool) {\n unchecked {\n return Fr.unwrap(a) == Fr.unwrap(b);\n }\n}\n\nuint256 constant CONST_PROOF_SIZE_LOG_N = 28;\n\nuint256 constant NUMBER_OF_SUBRELATIONS = 28;\nuint256 constant BATCHED_RELATION_PARTIAL_LENGTH = 8;\nuint256 constant ZK_BATCHED_RELATION_PARTIAL_LENGTH = 9;\nuint256 constant NUMBER_OF_ENTITIES = 41;\nuint256 constant NUMBER_UNSHIFTED = 36;\nuint256 constant NUMBER_TO_BE_SHIFTED = 5;\nuint256 constant PAIRING_POINTS_SIZE = 16;\n\nuint256 constant FIELD_ELEMENT_SIZE = 0x20;\nuint256 constant GROUP_ELEMENT_SIZE = 0x40;\n\n// Alphas are used as relation separators so there should be NUMBER_OF_SUBRELATIONS - 1\nuint256 constant NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1;\n\n// ENUM FOR WIRES\nenum WIRE {\n Q_M,\n Q_C,\n Q_L,\n Q_R,\n Q_O,\n Q_4,\n Q_LOOKUP,\n Q_ARITH,\n Q_RANGE,\n Q_ELLIPTIC,\n Q_MEMORY,\n Q_NNF,\n Q_POSEIDON2_EXTERNAL,\n Q_POSEIDON2_INTERNAL,\n SIGMA_1,\n SIGMA_2,\n SIGMA_3,\n SIGMA_4,\n ID_1,\n ID_2,\n ID_3,\n ID_4,\n TABLE_1,\n TABLE_2,\n TABLE_3,\n TABLE_4,\n LAGRANGE_FIRST,\n LAGRANGE_LAST,\n W_L,\n W_R,\n W_O,\n W_4,\n Z_PERM,\n LOOKUP_INVERSES,\n LOOKUP_READ_COUNTS,\n LOOKUP_READ_TAGS,\n W_L_SHIFT,\n W_R_SHIFT,\n W_O_SHIFT,\n W_4_SHIFT,\n Z_PERM_SHIFT\n}\n\nlibrary Honk {\n struct G1Point {\n uint256 x;\n uint256 y;\n }\n\n struct VerificationKey {\n // Misc Params\n uint256 circuitSize;\n uint256 logCircuitSize;\n uint256 publicInputsSize;\n // Selectors\n G1Point qm;\n G1Point qc;\n G1Point ql;\n G1Point qr;\n G1Point qo;\n G1Point q4;\n G1Point qLookup; // Lookup\n G1Point qArith; // Arithmetic widget\n G1Point qDeltaRange; // Delta Range sort\n G1Point qMemory; // Memory\n G1Point qNnf; // Non-native Field\n G1Point qElliptic; // Auxillary\n G1Point qPoseidon2External;\n G1Point qPoseidon2Internal;\n // Copy cnstraints\n G1Point s1;\n G1Point s2;\n G1Point s3;\n G1Point s4;\n // Copy identity\n G1Point id1;\n G1Point id2;\n G1Point id3;\n G1Point id4;\n // Precomputed lookup table\n G1Point t1;\n G1Point t2;\n G1Point t3;\n G1Point t4;\n // Fixed first and last\n G1Point lagrangeFirst;\n G1Point lagrangeLast;\n }\n\n struct RelationParameters {\n // challenges\n Fr eta;\n Fr etaTwo;\n Fr etaThree;\n Fr beta;\n Fr gamma;\n // derived\n Fr publicInputsDelta;\n }\n\n struct Proof {\n // Pairing point object\n Fr[PAIRING_POINTS_SIZE] pairingPointObject;\n // Free wires\n G1Point w1;\n G1Point w2;\n G1Point w3;\n G1Point w4;\n // Lookup helpers - Permutations\n G1Point zPerm;\n // Lookup helpers - logup\n G1Point lookupReadCounts;\n G1Point lookupReadTags;\n G1Point lookupInverses;\n // Sumcheck\n Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates;\n Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations;\n // Shplemini\n G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms;\n Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations;\n G1Point shplonkQ;\n G1Point kzgQuotient;\n }\n\n struct ZKProof {\n // Pairing point object\n Fr[PAIRING_POINTS_SIZE] pairingPointObject;\n // Commitments to wire polynomials\n G1Point w1;\n G1Point w2;\n G1Point w3;\n G1Point w4;\n // Commitments to logup witness polynomials\n G1Point lookupReadCounts;\n G1Point lookupReadTags;\n G1Point lookupInverses;\n // Commitment to grand permutation polynomial\n G1Point zPerm;\n G1Point[3] libraCommitments;\n // Sumcheck\n Fr libraSum;\n Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates;\n Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations;\n Fr libraEvaluation;\n // ZK\n G1Point geminiMaskingPoly;\n Fr geminiMaskingEval;\n // Shplemini\n G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms;\n Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations;\n Fr[4] libraPolyEvals;\n G1Point shplonkQ;\n G1Point kzgQuotient;\n }\n}\n\n// ZKTranscript library to generate fiat shamir challenges, the ZK transcript only differest\nstruct ZKTranscript {\n // Oink\n Honk.RelationParameters relationParameters;\n Fr[NUMBER_OF_ALPHAS] alphas;\n Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges;\n // Sumcheck\n Fr libraChallenge;\n Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges;\n // Shplemini\n Fr rho;\n Fr geminiR;\n Fr shplonkNu;\n Fr shplonkZ;\n // Derived\n Fr publicInputsDelta;\n}\n\nlibrary ZKTranscriptLib {\n function generateTranscript(\n Honk.ZKProof memory proof,\n bytes32[] calldata publicInputs,\n uint256 vkHash,\n uint256 publicInputsSize,\n uint256 logN\n ) external pure returns (ZKTranscript memory t) {\n Fr previousChallenge;\n (\n t.relationParameters,\n previousChallenge\n ) = generateRelationParametersChallenges(\n proof,\n publicInputs,\n vkHash,\n publicInputsSize,\n previousChallenge\n );\n\n (t.alphas, previousChallenge) = generateAlphaChallenges(\n previousChallenge,\n proof\n );\n\n (t.gateChallenges, previousChallenge) = generateGateChallenges(\n previousChallenge,\n logN\n );\n (t.libraChallenge, previousChallenge) = generateLibraChallenge(\n previousChallenge,\n proof\n );\n (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(\n proof,\n previousChallenge,\n logN\n );\n\n (t.rho, previousChallenge) = generateRhoChallenge(\n proof,\n previousChallenge\n );\n\n (t.geminiR, previousChallenge) = generateGeminiRChallenge(\n proof,\n previousChallenge,\n logN\n );\n\n (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(\n proof,\n previousChallenge,\n logN\n );\n\n (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(\n proof,\n previousChallenge\n );\n return t;\n }\n\n function splitChallenge(\n Fr challenge\n ) internal pure returns (Fr first, Fr second) {\n uint256 challengeU256 = uint256(Fr.unwrap(challenge));\n uint256 lo = challengeU256 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;\n uint256 hi = challengeU256 >> 128;\n first = FrLib.fromBytes32(bytes32(lo));\n second = FrLib.fromBytes32(bytes32(hi));\n }\n\n function generateRelationParametersChallenges(\n Honk.ZKProof memory proof,\n bytes32[] calldata publicInputs,\n uint256 vkHash,\n uint256 publicInputsSize,\n Fr previousChallenge\n )\n internal\n pure\n returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge)\n {\n (\n rp.eta,\n rp.etaTwo,\n rp.etaThree,\n previousChallenge\n ) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize);\n\n (\n rp.beta,\n rp.gamma,\n nextPreviousChallenge\n ) = generateBetaAndGammaChallenges(previousChallenge, proof);\n }\n\n function generateEtaChallenge(\n Honk.ZKProof memory proof,\n bytes32[] calldata publicInputs,\n uint256 vkHash,\n uint256 publicInputsSize\n )\n internal\n pure\n returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge)\n {\n bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 6);\n round0[0] = bytes32(vkHash);\n\n for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) {\n round0[1 + i] = bytes32(publicInputs[i]);\n }\n for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {\n round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib\n .toBytes32(proof.pairingPointObject[i]);\n }\n\n // Create the first challenge\n // Note: w4 is added to the challenge later on\n round0[1 + publicInputsSize] = bytes32(proof.w1.x);\n round0[1 + publicInputsSize + 1] = bytes32(proof.w1.y);\n round0[1 + publicInputsSize + 2] = bytes32(proof.w2.x);\n round0[1 + publicInputsSize + 3] = bytes32(proof.w2.y);\n round0[1 + publicInputsSize + 4] = bytes32(proof.w3.x);\n round0[1 + publicInputsSize + 5] = bytes32(proof.w3.y);\n\n previousChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(round0))\n );\n (eta, etaTwo) = splitChallenge(previousChallenge);\n previousChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))\n );\n\n (etaThree, ) = splitChallenge(previousChallenge);\n }\n\n function generateBetaAndGammaChallenges(\n Fr previousChallenge,\n Honk.ZKProof memory proof\n ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) {\n bytes32[7] memory round1;\n round1[0] = FrLib.toBytes32(previousChallenge);\n round1[1] = bytes32(proof.lookupReadCounts.x);\n round1[2] = bytes32(proof.lookupReadCounts.y);\n round1[3] = bytes32(proof.lookupReadTags.x);\n round1[4] = bytes32(proof.lookupReadTags.y);\n round1[5] = bytes32(proof.w4.x);\n round1[6] = bytes32(proof.w4.y);\n\n nextPreviousChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(round1))\n );\n (beta, gamma) = splitChallenge(nextPreviousChallenge);\n }\n\n // Alpha challenges non-linearise the gate contributions\n function generateAlphaChallenges(\n Fr previousChallenge,\n Honk.ZKProof memory proof\n )\n internal\n pure\n returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge)\n {\n // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup\n uint256[5] memory alpha0;\n alpha0[0] = Fr.unwrap(previousChallenge);\n alpha0[1] = proof.lookupInverses.x;\n alpha0[2] = proof.lookupInverses.y;\n alpha0[3] = proof.zPerm.x;\n alpha0[4] = proof.zPerm.y;\n\n nextPreviousChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(alpha0))\n );\n Fr alpha;\n (alpha, ) = splitChallenge(nextPreviousChallenge);\n\n // Compute powers of alpha for batching subrelations\n alphas[0] = alpha;\n for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) {\n alphas[i] = alphas[i - 1] * alpha;\n }\n }\n\n function generateGateChallenges(\n Fr previousChallenge,\n uint256 logN\n )\n internal\n pure\n returns (\n Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges,\n Fr nextPreviousChallenge\n )\n {\n previousChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))\n );\n (gateChallenges[0], ) = splitChallenge(previousChallenge);\n for (uint256 i = 1; i < logN; i++) {\n gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1];\n }\n nextPreviousChallenge = previousChallenge;\n }\n\n function generateLibraChallenge(\n Fr previousChallenge,\n Honk.ZKProof memory proof\n ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) {\n // 2 comm, 1 sum, 1 challenge\n uint256[4] memory challengeData;\n challengeData[0] = Fr.unwrap(previousChallenge);\n challengeData[1] = proof.libraCommitments[0].x;\n challengeData[2] = proof.libraCommitments[0].y;\n challengeData[3] = Fr.unwrap(proof.libraSum);\n nextPreviousChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(challengeData))\n );\n (libraChallenge, ) = splitChallenge(nextPreviousChallenge);\n }\n\n function generateSumcheckChallenges(\n Honk.ZKProof memory proof,\n Fr prevChallenge,\n uint256 logN\n )\n internal\n pure\n returns (\n Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges,\n Fr nextPreviousChallenge\n )\n {\n for (uint256 i = 0; i < logN; i++) {\n Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal;\n univariateChal[0] = prevChallenge;\n\n for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) {\n univariateChal[j + 1] = proof.sumcheckUnivariates[i][j];\n }\n prevChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(univariateChal))\n );\n\n (sumcheckChallenges[i], ) = splitChallenge(prevChallenge);\n }\n nextPreviousChallenge = prevChallenge;\n }\n\n // We add Libra claimed eval + 3 comm + 1 more eval\n function generateRhoChallenge(\n Honk.ZKProof memory proof,\n Fr prevChallenge\n ) internal pure returns (Fr rho, Fr nextPreviousChallenge) {\n uint256[NUMBER_OF_ENTITIES + 9] memory rhoChallengeElements;\n rhoChallengeElements[0] = Fr.unwrap(prevChallenge);\n uint256 i;\n for (i = 1; i <= NUMBER_OF_ENTITIES; i++) {\n rhoChallengeElements[i] = Fr.unwrap(\n proof.sumcheckEvaluations[i - 1]\n );\n }\n rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation);\n\n i += 1;\n rhoChallengeElements[i] = proof.libraCommitments[1].x;\n rhoChallengeElements[i + 1] = proof.libraCommitments[1].y;\n i += 2;\n rhoChallengeElements[i] = proof.libraCommitments[2].x;\n rhoChallengeElements[i + 1] = proof.libraCommitments[2].y;\n i += 2;\n rhoChallengeElements[i] = proof.geminiMaskingPoly.x;\n rhoChallengeElements[i + 1] = proof.geminiMaskingPoly.y;\n\n i += 2;\n rhoChallengeElements[i] = Fr.unwrap(proof.geminiMaskingEval);\n\n nextPreviousChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(rhoChallengeElements))\n );\n (rho, ) = splitChallenge(nextPreviousChallenge);\n }\n\n function generateGeminiRChallenge(\n Honk.ZKProof memory proof,\n Fr prevChallenge,\n uint256 logN\n ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) {\n uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1);\n gR[0] = Fr.unwrap(prevChallenge);\n\n for (uint256 i = 0; i < logN - 1; i++) {\n gR[1 + i * 2] = proof.geminiFoldComms[i].x;\n gR[2 + i * 2] = proof.geminiFoldComms[i].y;\n }\n\n nextPreviousChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(gR))\n );\n\n (geminiR, ) = splitChallenge(nextPreviousChallenge);\n }\n\n function generateShplonkNuChallenge(\n Honk.ZKProof memory proof,\n Fr prevChallenge,\n uint256 logN\n ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) {\n uint256[] memory shplonkNuChallengeElements = new uint256[](\n logN + 1 + 4\n );\n shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge);\n\n for (uint256 i = 1; i <= logN; i++) {\n shplonkNuChallengeElements[i] = Fr.unwrap(\n proof.geminiAEvaluations[i - 1]\n );\n }\n\n uint256 libraIdx = 0;\n for (uint256 i = logN + 1; i <= logN + 4; i++) {\n shplonkNuChallengeElements[i] = Fr.unwrap(\n proof.libraPolyEvals[libraIdx]\n );\n libraIdx++;\n }\n\n nextPreviousChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(shplonkNuChallengeElements))\n );\n (shplonkNu, ) = splitChallenge(nextPreviousChallenge);\n }\n\n function generateShplonkZChallenge(\n Honk.ZKProof memory proof,\n Fr prevChallenge\n ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) {\n uint256[3] memory shplonkZChallengeElements;\n shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge);\n\n shplonkZChallengeElements[1] = proof.shplonkQ.x;\n shplonkZChallengeElements[2] = proof.shplonkQ.y;\n\n nextPreviousChallenge = FrLib.fromBytes32(\n keccak256(abi.encodePacked(shplonkZChallengeElements))\n );\n (shplonkZ, ) = splitChallenge(nextPreviousChallenge);\n }\n\n function loadProof(\n bytes calldata proof,\n uint256 logN\n ) internal pure returns (Honk.ZKProof memory p) {\n uint256 boundary = 0x0;\n\n // Pairing point object\n for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {\n p.pairingPointObject[i] = bytesToFr(\n proof[boundary:boundary + FIELD_ELEMENT_SIZE]\n );\n boundary += FIELD_ELEMENT_SIZE;\n }\n // Commitments\n p.w1 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);\n boundary += GROUP_ELEMENT_SIZE;\n p.w2 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);\n boundary += GROUP_ELEMENT_SIZE;\n p.w3 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);\n boundary += GROUP_ELEMENT_SIZE;\n\n // Lookup / Permutation Helper Commitments\n p.lookupReadCounts = bytesToG1Point(\n proof[boundary:boundary + GROUP_ELEMENT_SIZE]\n );\n boundary += GROUP_ELEMENT_SIZE;\n p.lookupReadTags = bytesToG1Point(\n proof[boundary:boundary + GROUP_ELEMENT_SIZE]\n );\n boundary += GROUP_ELEMENT_SIZE;\n p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);\n boundary += GROUP_ELEMENT_SIZE;\n p.lookupInverses = bytesToG1Point(\n proof[boundary:boundary + GROUP_ELEMENT_SIZE]\n );\n boundary += GROUP_ELEMENT_SIZE;\n p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]);\n boundary += GROUP_ELEMENT_SIZE;\n p.libraCommitments[0] = bytesToG1Point(\n proof[boundary:boundary + GROUP_ELEMENT_SIZE]\n );\n boundary += GROUP_ELEMENT_SIZE;\n\n p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]);\n boundary += FIELD_ELEMENT_SIZE;\n // Sumcheck univariates\n for (uint256 i = 0; i < logN; i++) {\n for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) {\n p.sumcheckUnivariates[i][j] = bytesToFr(\n proof[boundary:boundary + FIELD_ELEMENT_SIZE]\n );\n boundary += FIELD_ELEMENT_SIZE;\n }\n }\n\n // Sumcheck evaluations\n for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) {\n p.sumcheckEvaluations[i] = bytesToFr(\n proof[boundary:boundary + FIELD_ELEMENT_SIZE]\n );\n boundary += FIELD_ELEMENT_SIZE;\n }\n\n p.libraEvaluation = bytesToFr(\n proof[boundary:boundary + FIELD_ELEMENT_SIZE]\n );\n boundary += FIELD_ELEMENT_SIZE;\n\n p.libraCommitments[1] = bytesToG1Point(\n proof[boundary:boundary + GROUP_ELEMENT_SIZE]\n );\n boundary += GROUP_ELEMENT_SIZE;\n p.libraCommitments[2] = bytesToG1Point(\n proof[boundary:boundary + GROUP_ELEMENT_SIZE]\n );\n boundary += GROUP_ELEMENT_SIZE;\n p.geminiMaskingPoly = bytesToG1Point(\n proof[boundary:boundary + GROUP_ELEMENT_SIZE]\n );\n boundary += GROUP_ELEMENT_SIZE;\n p.geminiMaskingEval = bytesToFr(\n proof[boundary:boundary + FIELD_ELEMENT_SIZE]\n );\n boundary += FIELD_ELEMENT_SIZE;\n\n // Gemini\n // Read gemini fold univariates\n for (uint256 i = 0; i < logN - 1; i++) {\n p.geminiFoldComms[i] = bytesToG1Point(\n proof[boundary:boundary + GROUP_ELEMENT_SIZE]\n );\n boundary += GROUP_ELEMENT_SIZE;\n }\n\n // Read gemini a evaluations\n for (uint256 i = 0; i < logN; i++) {\n p.geminiAEvaluations[i] = bytesToFr(\n proof[boundary:boundary + FIELD_ELEMENT_SIZE]\n );\n boundary += FIELD_ELEMENT_SIZE;\n }\n\n for (uint256 i = 0; i < 4; i++) {\n p.libraPolyEvals[i] = bytesToFr(\n proof[boundary:boundary + FIELD_ELEMENT_SIZE]\n );\n boundary += FIELD_ELEMENT_SIZE;\n }\n\n // Shplonk\n p.shplonkQ = bytesToG1Point(\n proof[boundary:boundary + GROUP_ELEMENT_SIZE]\n );\n boundary += GROUP_ELEMENT_SIZE;\n // KZG\n p.kzgQuotient = bytesToG1Point(\n proof[boundary:boundary + GROUP_ELEMENT_SIZE]\n );\n }\n}\n\n// Field arithmetic libraries\n\nlibrary RelationsLib {\n Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17)\n\n function accumulateRelationEvaluations(\n Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations,\n Honk.RelationParameters memory rp,\n Fr[NUMBER_OF_ALPHAS] memory alphas,\n Fr powPartialEval\n ) internal pure returns (Fr accumulator) {\n Fr[NUMBER_OF_SUBRELATIONS] memory evaluations;\n\n // Accumulate all relations in Ultra Honk - each with varying number of subrelations\n accumulateArithmeticRelation(\n purportedEvaluations,\n evaluations,\n powPartialEval\n );\n accumulatePermutationRelation(\n purportedEvaluations,\n rp,\n evaluations,\n powPartialEval\n );\n accumulateLogDerivativeLookupRelation(\n purportedEvaluations,\n rp,\n evaluations,\n powPartialEval\n );\n accumulateDeltaRangeRelation(\n purportedEvaluations,\n evaluations,\n powPartialEval\n );\n accumulateEllipticRelation(\n purportedEvaluations,\n evaluations,\n powPartialEval\n );\n accumulateMemoryRelation(\n purportedEvaluations,\n rp,\n evaluations,\n powPartialEval\n );\n accumulateNnfRelation(\n purportedEvaluations,\n evaluations,\n powPartialEval\n );\n accumulatePoseidonExternalRelation(\n purportedEvaluations,\n evaluations,\n powPartialEval\n );\n accumulatePoseidonInternalRelation(\n purportedEvaluations,\n evaluations,\n powPartialEval\n );\n\n // batch the subrelations with the alpha challenges to obtain the full honk relation\n accumulator = scaleAndBatchSubrelations(evaluations, alphas);\n }\n\n /**\n * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids\n * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code\n * editors, and thus is noisy.\n */\n function wire(\n Fr[NUMBER_OF_ENTITIES] memory p,\n WIRE _wire\n ) internal pure returns (Fr) {\n return p[uint256(_wire)];\n }\n\n uint256 internal constant NEG_HALF_MODULO_P =\n 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000;\n /**\n * Ultra Arithmetic Relation\n *\n */\n\n function accumulateArithmeticRelation(\n Fr[NUMBER_OF_ENTITIES] memory p,\n Fr[NUMBER_OF_SUBRELATIONS] memory evals,\n Fr domainSep\n ) internal pure {\n // Relation 0\n Fr q_arith = wire(p, WIRE.Q_ARITH);\n {\n Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P);\n\n Fr accum = (q_arith - Fr.wrap(3)) *\n (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) *\n neg_half;\n accum =\n accum +\n (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) +\n (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) +\n (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) +\n (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) +\n wire(p, WIRE.Q_C);\n accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT);\n accum = accum * q_arith;\n accum = accum * domainSep;\n evals[0] = accum;\n }\n\n // Relation 1\n {\n Fr accum = wire(p, WIRE.W_L) +\n wire(p, WIRE.W_4) -\n wire(p, WIRE.W_L_SHIFT) +\n wire(p, WIRE.Q_M);\n accum = accum * (q_arith - Fr.wrap(2));\n accum = accum * (q_arith - ONE);\n accum = accum * q_arith;\n accum = accum * domainSep;\n evals[1] = accum;\n }\n }\n\n function accumulatePermutationRelation(\n Fr[NUMBER_OF_ENTITIES] memory p,\n Honk.RelationParameters memory rp,\n Fr[NUMBER_OF_SUBRELATIONS] memory evals,\n Fr domainSep\n ) internal pure {\n Fr grand_product_numerator;\n Fr grand_product_denominator;\n\n {\n Fr num = wire(p, WIRE.W_L) +\n wire(p, WIRE.ID_1) *\n rp.beta +\n rp.gamma;\n num =\n num *\n (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma);\n num =\n num *\n (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma);\n num =\n num *\n (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma);\n\n grand_product_numerator = num;\n }\n {\n Fr den = wire(p, WIRE.W_L) +\n wire(p, WIRE.SIGMA_1) *\n rp.beta +\n rp.gamma;\n den =\n den *\n (wire(p, WIRE.W_R) +\n wire(p, WIRE.SIGMA_2) *\n rp.beta +\n rp.gamma);\n den =\n den *\n (wire(p, WIRE.W_O) +\n wire(p, WIRE.SIGMA_3) *\n rp.beta +\n rp.gamma);\n den =\n den *\n (wire(p, WIRE.W_4) +\n wire(p, WIRE.SIGMA_4) *\n rp.beta +\n rp.gamma);\n\n grand_product_denominator = den;\n }\n\n // Contribution 2\n {\n Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) *\n grand_product_numerator;\n\n acc =\n acc -\n ((wire(p, WIRE.Z_PERM_SHIFT) +\n (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) *\n grand_product_denominator);\n acc = acc * domainSep;\n evals[2] = acc;\n }\n\n // Contribution 3\n {\n Fr acc = (wire(p, WIRE.LAGRANGE_LAST) *\n wire(p, WIRE.Z_PERM_SHIFT)) * domainSep;\n evals[3] = acc;\n }\n }\n\n function accumulateLogDerivativeLookupRelation(\n Fr[NUMBER_OF_ENTITIES] memory p,\n Honk.RelationParameters memory rp,\n Fr[NUMBER_OF_SUBRELATIONS] memory evals,\n Fr domainSep\n ) internal pure {\n Fr write_term;\n Fr read_term;\n\n // Calculate the write term (the table accumulation)\n {\n write_term =\n wire(p, WIRE.TABLE_1) +\n rp.gamma +\n (wire(p, WIRE.TABLE_2) * rp.eta) +\n (wire(p, WIRE.TABLE_3) * rp.etaTwo) +\n (wire(p, WIRE.TABLE_4) * rp.etaThree);\n }\n\n // Calculate the write term\n {\n Fr derived_entry_1 = wire(p, WIRE.W_L) +\n rp.gamma +\n (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT));\n Fr derived_entry_2 = wire(p, WIRE.W_R) +\n wire(p, WIRE.Q_M) *\n wire(p, WIRE.W_R_SHIFT);\n Fr derived_entry_3 = wire(p, WIRE.W_O) +\n wire(p, WIRE.Q_C) *\n wire(p, WIRE.W_O_SHIFT);\n\n read_term =\n derived_entry_1 +\n (derived_entry_2 * rp.eta) +\n (derived_entry_3 * rp.etaTwo) +\n (wire(p, WIRE.Q_O) * rp.etaThree);\n }\n\n Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term;\n Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term;\n\n Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) +\n wire(p, WIRE.Q_LOOKUP) -\n (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP));\n\n // Inverse calculated correctly relation\n Fr accumulatorNone = read_term *\n write_term *\n wire(p, WIRE.LOOKUP_INVERSES) -\n inverse_exists_xor;\n accumulatorNone = accumulatorNone * domainSep;\n\n // Inverse\n Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) *\n read_inverse -\n wire(p, WIRE.LOOKUP_READ_COUNTS) *\n write_inverse;\n\n Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS);\n\n Fr read_tag_boolean_relation = read_tag * read_tag - read_tag;\n\n evals[4] = accumulatorNone;\n evals[5] = accumulatorOne;\n evals[6] = read_tag_boolean_relation * domainSep;\n }\n\n function accumulateDeltaRangeRelation(\n Fr[NUMBER_OF_ENTITIES] memory p,\n Fr[NUMBER_OF_SUBRELATIONS] memory evals,\n Fr domainSep\n ) internal pure {\n Fr minus_one = ZERO - ONE;\n Fr minus_two = ZERO - Fr.wrap(2);\n Fr minus_three = ZERO - Fr.wrap(3);\n\n // Compute wire differences\n Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L);\n Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R);\n Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O);\n Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4);\n\n // Contribution 6\n {\n Fr acc = delta_1;\n acc = acc * (delta_1 + minus_one);\n acc = acc * (delta_1 + minus_two);\n acc = acc * (delta_1 + minus_three);\n acc = acc * wire(p, WIRE.Q_RANGE);\n acc = acc * domainSep;\n evals[7] = acc;\n }\n\n // Contribution 7\n {\n Fr acc = delta_2;\n acc = acc * (delta_2 + minus_one);\n acc = acc * (delta_2 + minus_two);\n acc = acc * (delta_2 + minus_three);\n acc = acc * wire(p, WIRE.Q_RANGE);\n acc = acc * domainSep;\n evals[8] = acc;\n }\n\n // Contribution 8\n {\n Fr acc = delta_3;\n acc = acc * (delta_3 + minus_one);\n acc = acc * (delta_3 + minus_two);\n acc = acc * (delta_3 + minus_three);\n acc = acc * wire(p, WIRE.Q_RANGE);\n acc = acc * domainSep;\n evals[9] = acc;\n }\n\n // Contribution 9\n {\n Fr acc = delta_4;\n acc = acc * (delta_4 + minus_one);\n acc = acc * (delta_4 + minus_two);\n acc = acc * (delta_4 + minus_three);\n acc = acc * wire(p, WIRE.Q_RANGE);\n acc = acc * domainSep;\n evals[10] = acc;\n }\n }\n\n struct EllipticParams {\n // Points\n Fr x_1;\n Fr y_1;\n Fr x_2;\n Fr y_2;\n Fr y_3;\n Fr x_3;\n // push accumulators into memory\n Fr x_double_identity;\n }\n\n function accumulateEllipticRelation(\n Fr[NUMBER_OF_ENTITIES] memory p,\n Fr[NUMBER_OF_SUBRELATIONS] memory evals,\n Fr domainSep\n ) internal pure {\n EllipticParams memory ep;\n ep.x_1 = wire(p, WIRE.W_R);\n ep.y_1 = wire(p, WIRE.W_O);\n\n ep.x_2 = wire(p, WIRE.W_L_SHIFT);\n ep.y_2 = wire(p, WIRE.W_4_SHIFT);\n ep.y_3 = wire(p, WIRE.W_O_SHIFT);\n ep.x_3 = wire(p, WIRE.W_R_SHIFT);\n\n Fr q_sign = wire(p, WIRE.Q_L);\n Fr q_is_double = wire(p, WIRE.Q_M);\n\n // Contribution 10 point addition, x-coordinate check\n // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0\n Fr x_diff = (ep.x_2 - ep.x_1);\n Fr y1_sqr = (ep.y_1 * ep.y_1);\n {\n // Move to top\n Fr partialEval = domainSep;\n\n Fr y2_sqr = (ep.y_2 * ep.y_2);\n Fr y1y2 = ep.y_1 * ep.y_2 * q_sign;\n Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1);\n x_add_identity = x_add_identity * x_diff * x_diff;\n x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2;\n\n evals[11] =\n x_add_identity *\n partialEval *\n wire(p, WIRE.Q_ELLIPTIC) *\n (ONE - q_is_double);\n }\n\n // Contribution 11 point addition, x-coordinate check\n // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0\n {\n Fr y1_plus_y3 = ep.y_1 + ep.y_3;\n Fr y_diff = ep.y_2 * q_sign - ep.y_1;\n Fr y_add_identity = y1_plus_y3 *\n x_diff +\n (ep.x_3 - ep.x_1) *\n y_diff;\n evals[12] =\n y_add_identity *\n domainSep *\n wire(p, WIRE.Q_ELLIPTIC) *\n (ONE - q_is_double);\n }\n\n // Contribution 10 point doubling, x-coordinate check\n // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0\n // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1\n {\n Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1;\n Fr y1_sqr_mul_4 = y1_sqr + y1_sqr;\n y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4;\n Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9);\n\n // NOTE: pushed into memory (stack >:'( )\n ep.x_double_identity =\n (ep.x_3 + ep.x_1 + ep.x_1) *\n y1_sqr_mul_4 -\n x1_pow_4_mul_9;\n\n Fr acc = ep.x_double_identity *\n domainSep *\n wire(p, WIRE.Q_ELLIPTIC) *\n q_is_double;\n evals[11] = evals[11] + acc;\n }\n\n // Contribution 11 point doubling, y-coordinate check\n // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0\n {\n Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1;\n Fr y_double_identity = x1_sqr_mul_3 *\n (ep.x_1 - ep.x_3) -\n (ep.y_1 + ep.y_1) *\n (ep.y_1 + ep.y_3);\n evals[12] =\n evals[12] +\n y_double_identity *\n domainSep *\n wire(p, WIRE.Q_ELLIPTIC) *\n q_is_double;\n }\n }\n\n // Parameters used within the Memory Relation\n // A struct is used to work around stack too deep. This relation has alot of variables\n struct MemParams {\n Fr memory_record_check;\n Fr partial_record_check;\n Fr next_gate_access_type;\n Fr record_delta;\n Fr index_delta;\n Fr adjacent_values_match_if_adjacent_indices_match;\n Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation;\n Fr access_check;\n Fr next_gate_access_type_is_boolean;\n Fr ROM_consistency_check_identity;\n Fr RAM_consistency_check_identity;\n Fr timestamp_delta;\n Fr RAM_timestamp_check_identity;\n Fr memory_identity;\n Fr index_is_monotonically_increasing;\n }\n\n function accumulateMemoryRelation(\n Fr[NUMBER_OF_ENTITIES] memory p,\n Honk.RelationParameters memory rp,\n Fr[NUMBER_OF_SUBRELATIONS] memory evals,\n Fr domainSep\n ) internal pure {\n MemParams memory ap;\n\n /**\n * MEMORY\n *\n * A RAM memory record contains a tuple of the following fields:\n * * i: `index` of memory cell being accessed\n * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM)\n * * v: `value` of memory cell being accessed\n * * a: `access` type of record. read: 0 = read, 1 = write\n * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three\n *\n * A ROM memory record contains a tuple of the following fields:\n * * i: `index` of memory cell being accessed\n * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index)\n * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index)\n * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three\n *\n * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires +\n * selectors, depending on whether the gate is a RAM read/write or a ROM read\n *\n * | gate type | i | v2/t | v | a | r |\n * | --------- | -- | ----- | -- | -- | -- |\n * | ROM | w1 | w2 | w3 | -- | w4 |\n * | RAM | w1 | w2 | w3 | qc | w4 |\n *\n * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on\n * `w2` to fix its value)\n *\n *\n */\n\n /**\n * Memory Record Check\n * Partial degree: 1\n * Total degree: 4\n *\n * A ROM/ROM access gate can be evaluated with the identity:\n *\n * qc + w1 \\eta + w2 \\eta_two + w3 \\eta_three - w4 = 0\n *\n * For ROM gates, qc = 0\n */\n ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree;\n ap.memory_record_check =\n ap.memory_record_check +\n (wire(p, WIRE.W_R) * rp.etaTwo);\n ap.memory_record_check =\n ap.memory_record_check +\n (wire(p, WIRE.W_L) * rp.eta);\n ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C);\n ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4\n ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4);\n\n /**\n * Contribution 13 & 14\n * ROM Consistency Check\n * Partial degree: 1\n * Total degree: 4\n *\n * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of\n * records that are sorted.\n *\n * We apply the following checks for the sorted records:\n *\n * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4\n * 2. index values for adjacent records are monotonically increasing\n * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1}\n *\n */\n ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L);\n ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4);\n\n ap.index_is_monotonically_increasing =\n ap.index_delta *\n (ap.index_delta - Fr.wrap(1)); // deg 2\n\n ap.adjacent_values_match_if_adjacent_indices_match =\n (ap.index_delta * MINUS_ONE + ONE) *\n ap.record_delta; // deg 2\n\n evals[14] =\n ap.adjacent_values_match_if_adjacent_indices_match *\n (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) *\n (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5\n evals[15] =\n ap.index_is_monotonically_increasing *\n (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) *\n (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5\n\n ap.ROM_consistency_check_identity =\n ap.memory_record_check *\n (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7\n\n /**\n * Contributions 15,16,17\n * RAM Consistency Check\n *\n * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check`\n * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`.\n * This is validated by requiring `access` to be boolean\n *\n * For two adjacent entries in the sorted list if _both_\n * A) index values match\n * B) adjacent access value is 0 (i.e. next gate is a READ)\n * then\n * C) both values must match.\n * The gate boolean check is\n * (A && B) => C === !(A && B) || C === !A || !B || C\n *\n * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized\n * with a WRITE operation.\n */\n Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4\n ap.access_check = access_type * (access_type - Fr.wrap(1)); // check value is 0 or 1; deg 2 or 8\n\n // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta\n // deg 1 or 4\n ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree;\n ap.next_gate_access_type =\n ap.next_gate_access_type +\n (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo);\n ap.next_gate_access_type =\n ap.next_gate_access_type +\n (wire(p, WIRE.W_L_SHIFT) * rp.eta);\n ap.next_gate_access_type =\n wire(p, WIRE.W_4_SHIFT) -\n ap.next_gate_access_type;\n\n Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O);\n ap\n .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation =\n (ap.index_delta * MINUS_ONE + ONE) *\n value_delta *\n (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6\n\n // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the\n // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't\n // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access\n // type is correct, to cover this edge case\n // deg 2 or 4\n ap.next_gate_access_type_is_boolean =\n ap.next_gate_access_type *\n ap.next_gate_access_type -\n ap.next_gate_access_type;\n\n // Putting it all together...\n evals[16] =\n ap\n .adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation *\n (wire(p, WIRE.Q_O)) *\n (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8\n evals[17] =\n ap.index_is_monotonically_increasing *\n (wire(p, WIRE.Q_O)) *\n (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4\n evals[18] =\n ap.next_gate_access_type_is_boolean *\n (wire(p, WIRE.Q_O)) *\n (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6\n\n ap.RAM_consistency_check_identity =\n ap.access_check *\n (wire(p, WIRE.Q_O)); // deg 3 or 9\n\n /**\n * RAM Timestamp Consistency Check\n *\n * | w1 | w2 | w3 | w4 |\n * | index | timestamp | timestamp_check | -- |\n *\n * Let delta_index = index_{i + 1} - index_{i}\n *\n * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i\n * Else timestamp_check = 0\n */\n ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R);\n ap.RAM_timestamp_check_identity =\n (ap.index_delta * MINUS_ONE + ONE) *\n ap.timestamp_delta -\n wire(p, WIRE.W_O); // deg 3\n\n /**\n * Complete Contribution 12\n * The complete RAM/ROM memory identity\n * Partial degree:\n */\n ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6\n ap.memory_identity =\n ap.memory_identity +\n ap.RAM_timestamp_check_identity *\n (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4\n ap.memory_identity =\n ap.memory_identity +\n ap.memory_record_check *\n (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6\n ap.memory_identity =\n ap.memory_identity +\n ap.RAM_consistency_check_identity; // deg 3 or 9\n\n // (deg 3 or 9) + (deg 4) + (deg 3)\n ap.memory_identity =\n ap.memory_identity *\n (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10\n evals[13] = ap.memory_identity;\n }\n\n // Constants for the Non-native Field relation\n Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68);\n Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14);\n\n // Parameters used within the Non-Native Field Relation\n // A struct is used to work around stack too deep. This relation has alot of variables\n struct NnfParams {\n Fr limb_subproduct;\n Fr non_native_field_gate_1;\n Fr non_native_field_gate_2;\n Fr non_native_field_gate_3;\n Fr limb_accumulator_1;\n Fr limb_accumulator_2;\n Fr nnf_identity;\n }\n\n function accumulateNnfRelation(\n Fr[NUMBER_OF_ENTITIES] memory p,\n Fr[NUMBER_OF_SUBRELATIONS] memory evals,\n Fr domainSep\n ) internal pure {\n NnfParams memory ap;\n\n /**\n * Contribution 12\n * Non native field arithmetic gate 2\n * deg 4\n *\n * _ _\n * / _ _ _ 14 \\\n * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 |\n * \\_ _/\n *\n *\n */\n ap.limb_subproduct =\n wire(p, WIRE.W_L) *\n wire(p, WIRE.W_R_SHIFT) +\n wire(p, WIRE.W_L_SHIFT) *\n wire(p, WIRE.W_R);\n ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) *\n wire(p, WIRE.W_4) +\n wire(p, WIRE.W_R) *\n wire(p, WIRE.W_O) -\n wire(p, WIRE.W_O_SHIFT));\n ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE;\n ap.non_native_field_gate_2 =\n ap.non_native_field_gate_2 -\n wire(p, WIRE.W_4_SHIFT);\n ap.non_native_field_gate_2 =\n ap.non_native_field_gate_2 +\n ap.limb_subproduct;\n ap.non_native_field_gate_2 =\n ap.non_native_field_gate_2 *\n wire(p, WIRE.Q_4);\n\n ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE;\n ap.limb_subproduct =\n ap.limb_subproduct +\n (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT));\n ap.non_native_field_gate_1 = ap.limb_subproduct;\n ap.non_native_field_gate_1 =\n ap.non_native_field_gate_1 -\n (wire(p, WIRE.W_O) + wire(p, WIRE.W_4));\n ap.non_native_field_gate_1 =\n ap.non_native_field_gate_1 *\n wire(p, WIRE.Q_O);\n\n ap.non_native_field_gate_3 = ap.limb_subproduct;\n ap.non_native_field_gate_3 =\n ap.non_native_field_gate_3 +\n wire(p, WIRE.W_4);\n ap.non_native_field_gate_3 =\n ap.non_native_field_gate_3 -\n (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT));\n ap.non_native_field_gate_3 =\n ap.non_native_field_gate_3 *\n wire(p, WIRE.Q_M);\n\n Fr non_native_field_identity = ap.non_native_field_gate_1 +\n ap.non_native_field_gate_2 +\n ap.non_native_field_gate_3;\n non_native_field_identity =\n non_native_field_identity *\n wire(p, WIRE.Q_R);\n\n // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm\n // deg 2\n ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT;\n ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT);\n ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;\n ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O);\n ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;\n ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R);\n ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT;\n ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L);\n ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4);\n ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4);\n\n // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm\n // deg 2\n ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT;\n ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT);\n ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;\n ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT);\n ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;\n ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4);\n ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT;\n ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O);\n ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT);\n ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M);\n\n Fr limb_accumulator_identity = ap.limb_accumulator_1 +\n ap.limb_accumulator_2;\n limb_accumulator_identity =\n limb_accumulator_identity *\n wire(p, WIRE.Q_O); // deg 3\n\n ap.nnf_identity = non_native_field_identity + limb_accumulator_identity;\n ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep);\n evals[19] = ap.nnf_identity;\n }\n\n struct PoseidonExternalParams {\n Fr s1;\n Fr s2;\n Fr s3;\n Fr s4;\n Fr u1;\n Fr u2;\n Fr u3;\n Fr u4;\n Fr t0;\n Fr t1;\n Fr t2;\n Fr t3;\n Fr v1;\n Fr v2;\n Fr v3;\n Fr v4;\n Fr q_pos_by_scaling;\n }\n\n function accumulatePoseidonExternalRelation(\n Fr[NUMBER_OF_ENTITIES] memory p,\n Fr[NUMBER_OF_SUBRELATIONS] memory evals,\n Fr domainSep\n ) internal pure {\n PoseidonExternalParams memory ep;\n\n ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L);\n ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R);\n ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O);\n ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4);\n\n ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1;\n ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2;\n ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3;\n ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4;\n // matrix mul v = M_E * u with 14 additions\n ep.t0 = ep.u1 + ep.u2; // u_1 + u_2\n ep.t1 = ep.u3 + ep.u4; // u_3 + u_4\n ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2\n // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4\n ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4\n // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4\n ep.v4 = ep.t1 + ep.t1;\n ep.v4 = ep.v4 + ep.v4 + ep.t3;\n // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4\n ep.v2 = ep.t0 + ep.t0;\n ep.v2 = ep.v2 + ep.v2 + ep.t2;\n // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4\n ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4\n ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4\n\n ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep;\n evals[20] =\n evals[20] +\n ep.q_pos_by_scaling *\n (ep.v1 - wire(p, WIRE.W_L_SHIFT));\n\n evals[21] =\n evals[21] +\n ep.q_pos_by_scaling *\n (ep.v2 - wire(p, WIRE.W_R_SHIFT));\n\n evals[22] =\n evals[22] +\n ep.q_pos_by_scaling *\n (ep.v3 - wire(p, WIRE.W_O_SHIFT));\n\n evals[23] =\n evals[23] +\n ep.q_pos_by_scaling *\n (ep.v4 - wire(p, WIRE.W_4_SHIFT));\n }\n\n struct PoseidonInternalParams {\n Fr u1;\n Fr u2;\n Fr u3;\n Fr u4;\n Fr u_sum;\n Fr v1;\n Fr v2;\n Fr v3;\n Fr v4;\n Fr s1;\n Fr q_pos_by_scaling;\n }\n\n function accumulatePoseidonInternalRelation(\n Fr[NUMBER_OF_ENTITIES] memory p,\n Fr[NUMBER_OF_SUBRELATIONS] memory evals,\n Fr domainSep\n ) internal pure {\n PoseidonInternalParams memory ip;\n\n Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [\n FrLib.from(\n 0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7\n ),\n FrLib.from(\n 0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b\n ),\n FrLib.from(\n 0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15\n ),\n FrLib.from(\n 0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b\n )\n ];\n\n // add round constants\n ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L);\n\n // apply s-box round\n ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1;\n ip.u2 = wire(p, WIRE.W_R);\n ip.u3 = wire(p, WIRE.W_O);\n ip.u4 = wire(p, WIRE.W_4);\n\n // matrix mul with v = M_I * u 4 muls and 7 additions\n ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4;\n\n ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep;\n\n ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum;\n evals[24] =\n evals[24] +\n ip.q_pos_by_scaling *\n (ip.v1 - wire(p, WIRE.W_L_SHIFT));\n\n ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum;\n evals[25] =\n evals[25] +\n ip.q_pos_by_scaling *\n (ip.v2 - wire(p, WIRE.W_R_SHIFT));\n\n ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum;\n evals[26] =\n evals[26] +\n ip.q_pos_by_scaling *\n (ip.v3 - wire(p, WIRE.W_O_SHIFT));\n\n ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum;\n evals[27] =\n evals[27] +\n ip.q_pos_by_scaling *\n (ip.v4 - wire(p, WIRE.W_4_SHIFT));\n }\n\n function scaleAndBatchSubrelations(\n Fr[NUMBER_OF_SUBRELATIONS] memory evaluations,\n Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges\n ) internal pure returns (Fr accumulator) {\n accumulator = evaluations[0];\n\n for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) {\n accumulator =\n accumulator +\n evaluations[i] *\n subrelationChallenges[i - 1];\n }\n }\n}\n\n// Field arithmetic libraries - prevent littering the code with modmul / addmul\n\nlibrary CommitmentSchemeLib {\n using FrLib for Fr;\n\n // Avoid stack too deep\n struct ShpleminiIntermediates {\n Fr unshiftedScalar;\n Fr shiftedScalar;\n Fr unshiftedScalarNeg;\n Fr shiftedScalarNeg;\n // Scalar to be multiplied by [1]₁\n Fr constantTermAccumulator;\n // Accumulator for powers of rho\n Fr batchingChallenge;\n // Linear combination of multilinear (sumcheck) evaluations and powers of rho\n Fr batchedEvaluation;\n Fr[4] denominators;\n Fr[4] batchingScalars;\n // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated\n Fr posInvertedDenominator;\n // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated\n Fr negInvertedDenominator;\n // ν^{2i} * 1/(z - r^{2^i})\n Fr scalingFactorPos;\n // ν^{2i+1} * 1/(z + r^{2^i})\n Fr scalingFactorNeg;\n // Fold_i(r^{2^i}) reconstructed by Verifier\n Fr[] foldPosEvaluations;\n }\n\n function computeSquares(\n Fr r,\n uint256 logN\n ) internal pure returns (Fr[] memory) {\n Fr[] memory squares = new Fr[](logN);\n squares[0] = r;\n for (uint256 i = 1; i < logN; ++i) {\n squares[i] = squares[i - 1].sqr();\n }\n return squares;\n }\n // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1\n\n function computeFoldPosEvaluations(\n Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges,\n Fr batchedEvalAccumulator,\n Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations,\n Fr[] memory geminiEvalChallengePowers,\n uint256 logSize\n ) internal view returns (Fr[] memory) {\n Fr[] memory foldPosEvaluations = new Fr[](logSize);\n for (uint256 i = logSize; i > 0; --i) {\n Fr challengePower = geminiEvalChallengePowers[i - 1];\n Fr u = sumcheckUChallenges[i - 1];\n\n Fr batchedEvalRoundAcc = ((challengePower *\n batchedEvalAccumulator *\n Fr.wrap(2)) -\n geminiEvaluations[i - 1] *\n (challengePower * (ONE - u) - u));\n // Divide by the denominator\n batchedEvalRoundAcc =\n batchedEvalRoundAcc *\n (challengePower * (ONE - u) + u).invert();\n\n batchedEvalAccumulator = batchedEvalRoundAcc;\n foldPosEvaluations[i - 1] = batchedEvalRoundAcc;\n }\n return foldPosEvaluations;\n }\n}\n\nuint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q\n\nfunction bytes32ToString(bytes32 value) pure returns (string memory result) {\n bytes memory alphabet = \"0123456789abcdef\";\n\n bytes memory str = new bytes(66);\n str[0] = \"0\";\n str[1] = \"x\";\n for (uint256 i = 0; i < 32; i++) {\n str[2 + i * 2] = alphabet[uint8(value[i] >> 4)];\n str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)];\n }\n result = string(str);\n}\n\n// Fr utility\n\nfunction bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) {\n scalar = FrLib.fromBytes32(bytes32(proofSection));\n}\n\n// EC Point utilities\nfunction bytesToG1Point(\n bytes calldata proofSection\n) pure returns (Honk.G1Point memory point) {\n point = Honk.G1Point({\n x: uint256(bytes32(proofSection[0x00:0x20])) % Q,\n y: uint256(bytes32(proofSection[0x20:0x40])) % Q\n });\n}\n\nfunction negateInplace(\n Honk.G1Point memory point\n) pure returns (Honk.G1Point memory) {\n point.y = (Q - point.y) % Q;\n return point;\n}\n\n/**\n * Convert the pairing points to G1 points.\n *\n * The pairing points are serialised as an array of 68 bit limbs representing two points\n * The lhs of a pairing operation and the rhs of a pairing operation\n *\n * There are 4 fields for each group element, leaving 8 fields for each side of the pairing.\n *\n * @param pairingPoints The pairing points to convert.\n * @return lhs\n * @return rhs\n */\nfunction convertPairingPointsToG1(\n Fr[PAIRING_POINTS_SIZE] memory pairingPoints\n) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) {\n uint256 lhsX = Fr.unwrap(pairingPoints[0]);\n lhsX |= Fr.unwrap(pairingPoints[1]) << 68;\n lhsX |= Fr.unwrap(pairingPoints[2]) << 136;\n lhsX |= Fr.unwrap(pairingPoints[3]) << 204;\n lhs.x = lhsX;\n\n uint256 lhsY = Fr.unwrap(pairingPoints[4]);\n lhsY |= Fr.unwrap(pairingPoints[5]) << 68;\n lhsY |= Fr.unwrap(pairingPoints[6]) << 136;\n lhsY |= Fr.unwrap(pairingPoints[7]) << 204;\n lhs.y = lhsY;\n\n uint256 rhsX = Fr.unwrap(pairingPoints[8]);\n rhsX |= Fr.unwrap(pairingPoints[9]) << 68;\n rhsX |= Fr.unwrap(pairingPoints[10]) << 136;\n rhsX |= Fr.unwrap(pairingPoints[11]) << 204;\n rhs.x = rhsX;\n\n uint256 rhsY = Fr.unwrap(pairingPoints[12]);\n rhsY |= Fr.unwrap(pairingPoints[13]) << 68;\n rhsY |= Fr.unwrap(pairingPoints[14]) << 136;\n rhsY |= Fr.unwrap(pairingPoints[15]) << 204;\n rhs.y = rhsY;\n}\n\n/**\n * Hash the pairing inputs from the present verification context with those extracted from the public inputs.\n *\n * @param proofPairingPoints Pairing points from the proof - (public inputs).\n * @param accLhs Accumulator point for the left side - result of shplemini.\n * @param accRhs Accumulator point for the right side - result of shplemini.\n * @return recursionSeparator The recursion separator - generated from hashing the above.\n */\nfunction generateRecursionSeparator(\n Fr[PAIRING_POINTS_SIZE] memory proofPairingPoints,\n Honk.G1Point memory accLhs,\n Honk.G1Point memory accRhs\n) pure returns (Fr recursionSeparator) {\n // hash the proof aggregated X\n // hash the proof aggregated Y\n // hash the accum X\n // hash the accum Y\n\n (\n Honk.G1Point memory proofLhs,\n Honk.G1Point memory proofRhs\n ) = convertPairingPointsToG1(proofPairingPoints);\n\n uint256[8] memory recursionSeparatorElements;\n\n // Proof points\n recursionSeparatorElements[0] = proofLhs.x;\n recursionSeparatorElements[1] = proofLhs.y;\n recursionSeparatorElements[2] = proofRhs.x;\n recursionSeparatorElements[3] = proofRhs.y;\n\n // Accumulator points\n recursionSeparatorElements[4] = accLhs.x;\n recursionSeparatorElements[5] = accLhs.y;\n recursionSeparatorElements[6] = accRhs.x;\n recursionSeparatorElements[7] = accRhs.y;\n\n recursionSeparator = FrLib.fromBytes32(\n keccak256(abi.encodePacked(recursionSeparatorElements))\n );\n}\n\n/**\n * G1 Mul with Separator\n * Using the ecAdd and ecMul precompiles\n *\n * @param basePoint The point to multiply.\n * @param other The other point to add.\n * @param recursionSeperator The separator to use for the multiplication.\n * @return `(recursionSeperator * basePoint) + other`.\n */\nfunction mulWithSeperator(\n Honk.G1Point memory basePoint,\n Honk.G1Point memory other,\n Fr recursionSeperator\n) view returns (Honk.G1Point memory) {\n Honk.G1Point memory result;\n\n result = ecMul(recursionSeperator, basePoint);\n result = ecAdd(result, other);\n\n return result;\n}\n\n/**\n * G1 Mul\n * Takes a Fr value and a G1 point and uses the ecMul precompile to return the result.\n *\n * @param value The value to multiply the point by.\n * @param point The point to multiply.\n * @return result The result of the multiplication.\n */\nfunction ecMul(\n Fr value,\n Honk.G1Point memory point\n) view returns (Honk.G1Point memory) {\n Honk.G1Point memory result;\n\n assembly {\n let free := mload(0x40)\n // Write the point into memory (two 32 byte words)\n // Memory layout:\n // Address | value\n // free | point.x\n // free + 0x20| point.y\n mstore(free, mload(point))\n mstore(add(free, 0x20), mload(add(point, 0x20)))\n // Write the scalar into memory (one 32 byte word)\n // Memory layout:\n // Address | value\n // free + 0x40| value\n mstore(add(free, 0x40), value)\n\n // Call the ecMul precompile, it takes in the following\n // [point.x, point.y, scalar], and returns the result back into the free memory location.\n let success := staticcall(gas(), 0x07, free, 0x60, free, 0x40)\n if iszero(success) {\n revert(0, 0)\n }\n // Copy the result of the multiplication back into the result memory location.\n // Memory layout:\n // Address | value\n // result | result.x\n // result + 0x20| result.y\n mstore(result, mload(free))\n mstore(add(result, 0x20), mload(add(free, 0x20)))\n\n mstore(0x40, add(free, 0x60))\n }\n\n return result;\n}\n\n/**\n * G1 Add\n * Takes two G1 points and uses the ecAdd precompile to return the result.\n *\n * @param lhs The left hand side of the addition.\n * @param rhs The right hand side of the addition.\n * @return result The result of the addition.\n */\nfunction ecAdd(\n Honk.G1Point memory lhs,\n Honk.G1Point memory rhs\n) view returns (Honk.G1Point memory) {\n Honk.G1Point memory result;\n\n assembly {\n let free := mload(0x40)\n // Write lhs into memory (two 32 byte words)\n // Memory layout:\n // Address | value\n // free | lhs.x\n // free + 0x20| lhs.y\n mstore(free, mload(lhs))\n mstore(add(free, 0x20), mload(add(lhs, 0x20)))\n\n // Write rhs into memory (two 32 byte words)\n // Memory layout:\n // Address | value\n // free + 0x40| rhs.x\n // free + 0x60| rhs.y\n mstore(add(free, 0x40), mload(rhs))\n mstore(add(free, 0x60), mload(add(rhs, 0x20)))\n\n // Call the ecAdd precompile, it takes in the following\n // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location.\n let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40)\n if iszero(success) {\n revert(0, 0)\n }\n\n // Copy the result of the addition back into the result memory location.\n // Memory layout:\n // Address | value\n // result | result.x\n // result + 0x20| result.y\n mstore(result, mload(free))\n mstore(add(result, 0x20), mload(add(free, 0x20)))\n\n mstore(0x40, add(free, 0x80))\n }\n\n return result;\n}\n\nfunction validateOnCurve(Honk.G1Point memory point) pure {\n uint256 x = point.x;\n uint256 y = point.y;\n\n bool success = false;\n assembly {\n let xx := mulmod(x, x, Q)\n success := eq(mulmod(y, y, Q), addmod(mulmod(x, xx, Q), 3, Q))\n }\n\n require(success, \"point is not on the curve\");\n}\n\nfunction pairing(\n Honk.G1Point memory rhs,\n Honk.G1Point memory lhs\n) view returns (bool decodedResult) {\n bytes memory input = abi.encodePacked(\n rhs.x,\n rhs.y,\n // Fixed G2 point\n uint256(\n 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\n ),\n uint256(\n 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\n ),\n uint256(\n 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\n ),\n uint256(\n 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa\n ),\n lhs.x,\n lhs.y,\n // G2 point from VK\n uint256(\n 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1\n ),\n uint256(\n 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0\n ),\n uint256(\n 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4\n ),\n uint256(\n 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55\n )\n );\n\n (bool success, bytes memory result) = address(0x08).staticcall(input);\n decodedResult = success && abi.decode(result, (bool));\n}\n\n// Field arithmetic libraries - prevent littering the code with modmul / addmul\n\nabstract contract BaseZKHonkVerifier is IVerifier {\n using FrLib for Fr;\n\n uint256 immutable $N;\n uint256 immutable $LOG_N;\n uint256 immutable $VK_HASH;\n uint256 immutable $NUM_PUBLIC_INPUTS;\n\n constructor(\n uint256 _N,\n uint256 _logN,\n uint256 _vkHash,\n uint256 _numPublicInputs\n ) {\n $N = _N;\n $LOG_N = _logN;\n $VK_HASH = _vkHash;\n $NUM_PUBLIC_INPUTS = _numPublicInputs;\n }\n\n // Errors\n error ProofLengthWrong();\n error ProofLengthWrongWithLogN(\n uint256 logN,\n uint256 actualLength,\n uint256 expectedLength\n );\n error PublicInputsLengthWrong();\n error SumcheckFailed();\n error ShpleminiFailed();\n error GeminiChallengeInSubgroup();\n error ConsistencyCheckFailed();\n\n // Constants for proof length calculation (matching UltraKeccakZKFlavor)\n uint256 constant NUM_WITNESS_ENTITIES = 8;\n uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points\n uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements\n uint256 constant NUM_LIBRA_EVALUATIONS = 4; // libra evaluations\n\n // Calculate proof size based on log_n (matching UltraKeccakZKFlavor formula)\n function calculateProofSize(uint256 logN) internal pure returns (uint256) {\n // Witness and Libra commitments\n uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments\n proofLength += NUM_ELEMENTS_COMM * 4; // Libra concat, grand sum, quotient comms + Gemini masking\n\n // Sumcheck\n proofLength +=\n logN *\n ZK_BATCHED_RELATION_PARTIAL_LENGTH *\n NUM_ELEMENTS_FR; // sumcheck univariates\n proofLength += NUMBER_OF_ENTITIES * NUM_ELEMENTS_FR; // sumcheck evaluations\n\n // Libra and Gemini\n proofLength += NUM_ELEMENTS_FR * 3; // Libra sum, claimed eval, Gemini masking eval\n proofLength += logN * NUM_ELEMENTS_FR; // Gemini a evaluations\n proofLength += NUM_LIBRA_EVALUATIONS * NUM_ELEMENTS_FR; // libra evaluations\n\n // PCS commitments\n proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments\n proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments\n\n // Pairing points\n proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs\n\n return proofLength;\n }\n\n uint256 constant SHIFTED_COMMITMENTS_START = 30;\n\n function loadVerificationKey()\n internal\n pure\n virtual\n returns (Honk.VerificationKey memory);\n\n function verify(\n bytes calldata proof,\n bytes32[] calldata publicInputs\n ) public view override returns (bool verified) {\n // Calculate expected proof size based on $LOG_N\n uint256 expectedProofSize = calculateProofSize($LOG_N);\n\n // Check the received proof is the expected size where each field element is 32 bytes\n if (proof.length != expectedProofSize * 32) {\n revert ProofLengthWrongWithLogN(\n $LOG_N,\n proof.length,\n expectedProofSize * 32\n );\n }\n\n Honk.VerificationKey memory vk = loadVerificationKey();\n Honk.ZKProof memory p = ZKTranscriptLib.loadProof(proof, $LOG_N);\n\n if (publicInputs.length != vk.publicInputsSize - PAIRING_POINTS_SIZE) {\n revert PublicInputsLengthWrong();\n }\n\n // Generate the fiat shamir challenges for the whole protocol\n ZKTranscript memory t = ZKTranscriptLib.generateTranscript(\n p,\n publicInputs,\n $VK_HASH,\n $NUM_PUBLIC_INPUTS,\n $LOG_N\n );\n\n // Derive public input delta\n t.relationParameters.publicInputsDelta = computePublicInputDelta(\n publicInputs,\n p.pairingPointObject,\n t.relationParameters.beta,\n t.relationParameters.gamma /*pubInputsOffset=*/,\n 1\n );\n\n // Sumcheck\n if (!verifySumcheck(p, t)) revert SumcheckFailed();\n\n if (!verifyShplemini(p, vk, t)) revert ShpleminiFailed();\n\n verified = true;\n }\n\n uint256 constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28;\n\n function computePublicInputDelta(\n bytes32[] memory publicInputs,\n Fr[PAIRING_POINTS_SIZE] memory pairingPointObject,\n Fr beta,\n Fr gamma,\n uint256 offset\n ) internal view returns (Fr publicInputDelta) {\n Fr numerator = Fr.wrap(1);\n Fr denominator = Fr.wrap(1);\n\n Fr numeratorAcc = gamma +\n (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset));\n Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1));\n\n {\n for (\n uint256 i = 0;\n i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE;\n i++\n ) {\n Fr pubInput = FrLib.fromBytes32(publicInputs[i]);\n\n numerator = numerator * (numeratorAcc + pubInput);\n denominator = denominator * (denominatorAcc + pubInput);\n\n numeratorAcc = numeratorAcc + beta;\n denominatorAcc = denominatorAcc - beta;\n }\n\n for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) {\n Fr pubInput = pairingPointObject[i];\n\n numerator = numerator * (numeratorAcc + pubInput);\n denominator = denominator * (denominatorAcc + pubInput);\n\n numeratorAcc = numeratorAcc + beta;\n denominatorAcc = denominatorAcc - beta;\n }\n }\n\n // Fr delta = numerator / denominator; // TOOO: batch invert later?\n publicInputDelta = FrLib.div(numerator, denominator);\n }\n\n function verifySumcheck(\n Honk.ZKProof memory proof,\n ZKTranscript memory tp\n ) internal view returns (bool verified) {\n Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0\n Fr powPartialEvaluation = Fr.wrap(1);\n\n // We perform sumcheck reductions over log n rounds ( the multivariate degree )\n for (uint256 round; round < $LOG_N; ++round) {\n Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH]\n memory roundUnivariate = proof.sumcheckUnivariates[round];\n Fr totalSum = roundUnivariate[0] + roundUnivariate[1];\n if (totalSum != roundTargetSum) revert SumcheckFailed();\n\n Fr roundChallenge = tp.sumCheckUChallenges[round];\n\n // Update the round target for the next rounf\n roundTargetSum = computeNextTargetSum(\n roundUnivariate,\n roundChallenge\n );\n powPartialEvaluation =\n powPartialEvaluation *\n (Fr.wrap(1) +\n roundChallenge *\n (tp.gateChallenges[round] - Fr.wrap(1)));\n }\n\n // Last round\n Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations(\n proof.sumcheckEvaluations,\n tp.relationParameters,\n tp.alphas,\n powPartialEvaluation\n );\n\n Fr evaluation = Fr.wrap(1);\n for (uint256 i = 2; i < $LOG_N; i++) {\n evaluation = evaluation * tp.sumCheckUChallenges[i];\n }\n\n grandHonkRelationSum =\n grandHonkRelationSum *\n (Fr.wrap(1) - evaluation) +\n proof.libraEvaluation *\n tp.libraChallenge;\n verified = (grandHonkRelationSum == roundTargetSum);\n }\n\n // Return the new target sum for the next sumcheck round\n function computeNextTargetSum(\n Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates,\n Fr roundChallenge\n ) internal view returns (Fr targetSum) {\n Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH]\n memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [\n Fr.wrap(\n 0x0000000000000000000000000000000000000000000000000000000000009d80\n ),\n Fr.wrap(\n 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51\n ),\n Fr.wrap(\n 0x00000000000000000000000000000000000000000000000000000000000005a0\n ),\n Fr.wrap(\n 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31\n ),\n Fr.wrap(\n 0x0000000000000000000000000000000000000000000000000000000000000240\n ),\n Fr.wrap(\n 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31\n ),\n Fr.wrap(\n 0x00000000000000000000000000000000000000000000000000000000000005a0\n ),\n Fr.wrap(\n 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51\n ),\n Fr.wrap(\n 0x0000000000000000000000000000000000000000000000000000000000009d80\n )\n ];\n\n // To compute the next target sum, we evaluate the given univariate at a point u (challenge).\n\n // Performing Barycentric evaluations\n // Compute B(x)\n Fr numeratorValue = Fr.wrap(1);\n for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) {\n numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i));\n }\n\n Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses;\n for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) {\n denominatorInverses[i] = FrLib.invert(\n BARYCENTRIC_LAGRANGE_DENOMINATORS[i] *\n (roundChallenge - Fr.wrap(i))\n );\n }\n\n for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) {\n targetSum =\n targetSum +\n roundUnivariates[i] *\n denominatorInverses[i];\n }\n\n // Scale the sum by the value of B(x)\n targetSum = targetSum * numeratorValue;\n }\n\n uint256 constant LIBRA_COMMITMENTS = 3;\n uint256 constant LIBRA_EVALUATIONS = 4;\n uint256 constant LIBRA_UNIVARIATES_LENGTH = 9;\n\n struct PairingInputs {\n Honk.G1Point P_0;\n Honk.G1Point P_1;\n }\n\n function verifyShplemini(\n Honk.ZKProof memory proof,\n Honk.VerificationKey memory vk,\n ZKTranscript memory tp\n ) internal view returns (bool verified) {\n CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack\n\n // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size\n Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib\n .computeSquares(tp.geminiR, $LOG_N);\n // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings\n Fr[] memory scalars = new Fr[](\n NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3\n );\n Honk.G1Point[] memory commitments = new Honk.G1Point[](\n NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3\n );\n\n mem.posInvertedDenominator = (tp.shplonkZ -\n powers_of_evaluation_challenge[0]).invert();\n mem.negInvertedDenominator = (tp.shplonkZ +\n powers_of_evaluation_challenge[0]).invert();\n\n mem.unshiftedScalar =\n mem.posInvertedDenominator +\n (tp.shplonkNu * mem.negInvertedDenominator);\n mem.shiftedScalar =\n tp.geminiR.invert() *\n (mem.posInvertedDenominator -\n (tp.shplonkNu * mem.negInvertedDenominator));\n\n scalars[0] = Fr.wrap(1);\n commitments[0] = proof.shplonkQ;\n\n /* Batch multivariate opening claims, shifted and unshifted\n * The vector of scalars is populated as follows:\n * \\f[\n * \\left(\n * - \\left(\\frac{1}{z-r} + \\nu \\times \\frac{1}{z+r}\\right),\n * \\ldots,\n * - \\rho^{i+k-1} \\times \\left(\\frac{1}{z-r} + \\nu \\times \\frac{1}{z+r}\\right),\n * - \\rho^{i+k} \\times \\frac{1}{r} \\times \\left(\\frac{1}{z-r} - \\nu \\times \\frac{1}{z+r}\\right),\n * \\ldots,\n * - \\rho^{k+m-1} \\times \\frac{1}{r} \\times \\left(\\frac{1}{z-r} - \\nu \\times \\frac{1}{z+r}\\right)\n * \\right)\n * \\f]\n *\n * The following vector is concatenated to the vector of commitments:\n * \\f[\n * f_0, \\ldots, f_{m-1}, f_{\\text{shift}, 0}, \\ldots, f_{\\text{shift}, k-1}\n * \\f]\n *\n * Simultaneously, the evaluation of the multilinear polynomial\n * \\f[\n * \\sum \\rho^i \\cdot f_i + \\sum \\rho^{i+k} \\cdot f_{\\text{shift}, i}\n * \\f]\n * at the challenge point \\f$ (u_0,\\ldots, u_{n-1}) \\f$ is computed.\n *\n * This approach minimizes the number of iterations over the commitments to multilinear polynomials\n * and eliminates the need to store the powers of \\f$ \\rho \\f$.\n */\n mem.batchedEvaluation = proof.geminiMaskingEval;\n mem.batchingChallenge = tp.rho;\n mem.unshiftedScalarNeg = mem.unshiftedScalar.neg();\n mem.shiftedScalarNeg = mem.shiftedScalar.neg();\n\n scalars[1] = mem.unshiftedScalarNeg;\n for (uint256 i = 0; i < NUMBER_UNSHIFTED; ++i) {\n scalars[i + 2] = mem.unshiftedScalarNeg * mem.batchingChallenge;\n mem.batchedEvaluation =\n mem.batchedEvaluation +\n (proof.sumcheckEvaluations[i] * mem.batchingChallenge);\n mem.batchingChallenge = mem.batchingChallenge * tp.rho;\n }\n // g commitments are accumulated at r\n // For each of the to be shifted commitments perform the shift in place by\n // adding to the unshifted value.\n // We do so, as the values are to be used in batchMul later, and as\n // `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute.\n // Applied to w1, w2, w3, w4 and zPerm\n for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) {\n uint256 scalarOff = i + SHIFTED_COMMITMENTS_START;\n uint256 evaluationOff = i + NUMBER_UNSHIFTED;\n\n scalars[scalarOff] =\n scalars[scalarOff] +\n (mem.shiftedScalarNeg * mem.batchingChallenge);\n mem.batchedEvaluation =\n mem.batchedEvaluation +\n (proof.sumcheckEvaluations[evaluationOff] *\n mem.batchingChallenge);\n mem.batchingChallenge = mem.batchingChallenge * tp.rho;\n }\n\n commitments[1] = proof.geminiMaskingPoly;\n\n commitments[2] = vk.qm;\n commitments[3] = vk.qc;\n commitments[4] = vk.ql;\n commitments[5] = vk.qr;\n commitments[6] = vk.qo;\n commitments[7] = vk.q4;\n commitments[8] = vk.qLookup;\n commitments[9] = vk.qArith;\n commitments[10] = vk.qDeltaRange;\n commitments[11] = vk.qElliptic;\n commitments[12] = vk.qMemory;\n commitments[13] = vk.qNnf;\n commitments[14] = vk.qPoseidon2External;\n commitments[15] = vk.qPoseidon2Internal;\n commitments[16] = vk.s1;\n commitments[17] = vk.s2;\n commitments[18] = vk.s3;\n commitments[19] = vk.s4;\n commitments[20] = vk.id1;\n commitments[21] = vk.id2;\n commitments[22] = vk.id3;\n commitments[23] = vk.id4;\n commitments[24] = vk.t1;\n commitments[25] = vk.t2;\n commitments[26] = vk.t3;\n commitments[27] = vk.t4;\n commitments[28] = vk.lagrangeFirst;\n commitments[29] = vk.lagrangeLast;\n\n // Accumulate proof points\n commitments[30] = proof.w1;\n commitments[31] = proof.w2;\n commitments[32] = proof.w3;\n commitments[33] = proof.w4;\n commitments[34] = proof.zPerm;\n commitments[35] = proof.lookupInverses;\n commitments[36] = proof.lookupReadCounts;\n commitments[37] = proof.lookupReadTags;\n\n /* Batch gemini claims from the prover\n * place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from\n * aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars\n *\n * 1. Moves the vector\n * \\f[\n * \\left( \\text{com}(A_1), \\text{com}(A_2), \\ldots, \\text{com}(A_{n-1}) \\right)\n * \\f]\n * to the 'commitments' vector.\n *\n * 2. Computes the scalars:\n * \\f[\n * \\frac{\\nu^{2}}{z + r^2}, \\frac{\\nu^3}{z + r^4}, \\ldots, \\frac{\\nu^{n-1}}{z + r^{2^{n-1}}}\n * \\f]\n * and places them into the 'scalars' vector.\n *\n * 3. Accumulates the summands of the constant term:\n * \\f[\n * \\sum_{i=2}^{n-1} \\frac{\\nu^{i} \\cdot A_i(-r^{2^i})}{z + r^{2^i}}\n * \\f]\n * and adds them to the 'constant_term_accumulator'.\n */\n\n // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator:\n // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1\n Fr[] memory foldPosEvaluations = CommitmentSchemeLib\n .computeFoldPosEvaluations(\n tp.sumCheckUChallenges,\n mem.batchedEvaluation,\n proof.geminiAEvaluations,\n powers_of_evaluation_challenge,\n $LOG_N\n );\n\n mem.constantTermAccumulator =\n foldPosEvaluations[0] *\n mem.posInvertedDenominator;\n mem.constantTermAccumulator =\n mem.constantTermAccumulator +\n (proof.geminiAEvaluations[0] *\n tp.shplonkNu *\n mem.negInvertedDenominator);\n\n mem.batchingChallenge = tp.shplonkNu.sqr();\n uint256 boundary = NUMBER_UNSHIFTED + 2;\n\n // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1;\n // Compute scalar multipliers for each fold commitment\n for (uint256 i = 0; i < $LOG_N - 1; ++i) {\n bool dummy_round = i >= ($LOG_N - 1);\n\n if (!dummy_round) {\n // Update inverted denominators\n mem.posInvertedDenominator = (tp.shplonkZ -\n powers_of_evaluation_challenge[i + 1]).invert();\n mem.negInvertedDenominator = (tp.shplonkZ +\n powers_of_evaluation_challenge[i + 1]).invert();\n\n // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ]\n mem.scalingFactorPos =\n mem.batchingChallenge *\n mem.posInvertedDenominator;\n mem.scalingFactorNeg =\n mem.batchingChallenge *\n tp.shplonkNu *\n mem.negInvertedDenominator;\n scalars[boundary + i] =\n mem.scalingFactorNeg.neg() +\n mem.scalingFactorPos.neg();\n\n // Accumulate the const term contribution given by\n // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l})\n Fr accumContribution = mem.scalingFactorNeg *\n proof.geminiAEvaluations[i + 1];\n accumContribution =\n accumContribution +\n mem.scalingFactorPos *\n foldPosEvaluations[i + 1];\n mem.constantTermAccumulator =\n mem.constantTermAccumulator +\n accumContribution;\n }\n // Update the running power of v\n mem.batchingChallenge =\n mem.batchingChallenge *\n tp.shplonkNu *\n tp.shplonkNu;\n\n commitments[boundary + i] = proof.geminiFoldComms[i];\n }\n\n boundary += $LOG_N - 1;\n\n // Finalize the batch opening claim\n mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR);\n mem.denominators[1] = Fr.wrap(1).div(\n tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR\n );\n mem.denominators[2] = mem.denominators[0];\n mem.denominators[3] = mem.denominators[0];\n\n mem.batchingChallenge =\n mem.batchingChallenge *\n tp.shplonkNu *\n tp.shplonkNu;\n for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) {\n Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge;\n mem.batchingScalars[i] = scalingFactor.neg();\n mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu;\n mem.constantTermAccumulator =\n mem.constantTermAccumulator +\n scalingFactor *\n proof.libraPolyEvals[i];\n }\n scalars[boundary] = mem.batchingScalars[0];\n scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2];\n scalars[boundary + 2] = mem.batchingScalars[3];\n\n for (uint256 i = 0; i < LIBRA_COMMITMENTS; i++) {\n commitments[boundary++] = proof.libraCommitments[i];\n }\n\n commitments[boundary] = Honk.G1Point({ x: 1, y: 2 });\n scalars[boundary++] = mem.constantTermAccumulator;\n\n if (\n !checkEvalsConsistency(\n proof.libraPolyEvals,\n tp.geminiR,\n tp.sumCheckUChallenges,\n proof.libraEvaluation\n )\n ) {\n revert ConsistencyCheckFailed();\n }\n\n Honk.G1Point memory quotient_commitment = proof.kzgQuotient;\n\n commitments[boundary] = quotient_commitment;\n scalars[boundary] = tp.shplonkZ; // evaluation challenge\n\n PairingInputs memory pair;\n pair.P_0 = batchMul(commitments, scalars);\n pair.P_1 = negateInplace(quotient_commitment);\n\n // Aggregate pairing points\n Fr recursionSeparator = generateRecursionSeparator(\n proof.pairingPointObject,\n pair.P_0,\n pair.P_1\n );\n (\n Honk.G1Point memory P_0_other,\n Honk.G1Point memory P_1_other\n ) = convertPairingPointsToG1(proof.pairingPointObject);\n\n // Validate the points from the proof are on the curve\n validateOnCurve(P_0_other);\n validateOnCurve(P_1_other);\n\n // accumulate with aggregate points in proof\n pair.P_0 = mulWithSeperator(pair.P_0, P_0_other, recursionSeparator);\n pair.P_1 = mulWithSeperator(pair.P_1, P_1_other, recursionSeparator);\n\n return pairing(pair.P_0, pair.P_1);\n }\n\n struct SmallSubgroupIpaIntermediates {\n Fr[SUBGROUP_SIZE] challengePolyLagrange;\n Fr challengePolyEval;\n Fr lagrangeFirst;\n Fr lagrangeLast;\n Fr rootPower;\n Fr[SUBGROUP_SIZE] denominators; // this has to disappear\n Fr diff;\n }\n\n function checkEvalsConsistency(\n Fr[LIBRA_EVALUATIONS] memory libraPolyEvals,\n Fr geminiR,\n Fr[CONST_PROOF_SIZE_LOG_N] memory uChallenges,\n Fr libraEval\n ) internal view returns (bool check) {\n Fr one = Fr.wrap(1);\n Fr vanishingPolyEval = geminiR.pow(SUBGROUP_SIZE) - one;\n if (vanishingPolyEval == Fr.wrap(0)) {\n revert GeminiChallengeInSubgroup();\n }\n\n SmallSubgroupIpaIntermediates memory mem;\n mem.challengePolyLagrange[0] = one;\n for (uint256 round = 0; round < $LOG_N; round++) {\n uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round;\n mem.challengePolyLagrange[currIdx] = one;\n for (\n uint256 idx = currIdx + 1;\n idx < currIdx + LIBRA_UNIVARIATES_LENGTH;\n idx++\n ) {\n mem.challengePolyLagrange[idx] =\n mem.challengePolyLagrange[idx - 1] *\n uChallenges[round];\n }\n }\n\n mem.rootPower = one;\n mem.challengePolyEval = Fr.wrap(0);\n for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) {\n mem.denominators[idx] = mem.rootPower * geminiR - one;\n mem.denominators[idx] = mem.denominators[idx].invert();\n mem.challengePolyEval =\n mem.challengePolyEval +\n mem.challengePolyLagrange[idx] *\n mem.denominators[idx];\n mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE;\n }\n\n Fr numerator = vanishingPolyEval * Fr.wrap(SUBGROUP_SIZE).invert();\n mem.challengePolyEval = mem.challengePolyEval * numerator;\n mem.lagrangeFirst = mem.denominators[0] * numerator;\n mem.lagrangeLast = mem.denominators[SUBGROUP_SIZE - 1] * numerator;\n\n mem.diff = mem.lagrangeFirst * libraPolyEvals[2];\n\n mem.diff =\n mem.diff +\n (geminiR - SUBGROUP_GENERATOR_INVERSE) *\n (libraPolyEvals[1] -\n libraPolyEvals[2] -\n libraPolyEvals[0] *\n mem.challengePolyEval);\n mem.diff =\n mem.diff +\n mem.lagrangeLast *\n (libraPolyEvals[2] - libraEval) -\n vanishingPolyEval *\n libraPolyEvals[3];\n\n check = mem.diff == Fr.wrap(0);\n }\n\n // This implementation is the same as above with different constants\n function batchMul(\n Honk.G1Point[] memory base,\n Fr[] memory scalars\n ) internal view returns (Honk.G1Point memory result) {\n uint256 limit = NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3;\n\n // Validate all points are on the curve\n for (uint256 i = 0; i < limit; ++i) {\n validateOnCurve(base[i]);\n }\n\n bool success = true;\n assembly {\n let free := mload(0x40)\n\n let count := 0x01\n for {} lt(count, add(limit, 1)) {\n count := add(count, 1)\n } {\n // Get loop offsets\n let base_base := add(base, mul(count, 0x20))\n let scalar_base := add(scalars, mul(count, 0x20))\n\n mstore(add(free, 0x40), mload(mload(base_base)))\n mstore(add(free, 0x60), mload(add(0x20, mload(base_base))))\n // Add scalar\n mstore(add(free, 0x80), mload(scalar_base))\n\n success := and(\n success,\n staticcall(\n gas(),\n 7,\n add(free, 0x40),\n 0x60,\n add(free, 0x40),\n 0x40\n )\n )\n // accumulator = accumulator + accumulator_2\n success := and(\n success,\n staticcall(gas(), 6, free, 0x80, free, 0x40)\n )\n }\n\n // Return the result\n mstore(result, mload(free))\n mstore(add(result, 0x20), mload(add(free, 0x20)))\n }\n\n require(success, ShpleminiFailed());\n }\n}\n\ncontract DkgPkVerifier is\n BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS)\n{\n function loadVerificationKey()\n internal\n pure\n override\n returns (Honk.VerificationKey memory)\n {\n return HonkVerificationKey.loadVerificationKey();\n }\n}\n"
|
|
308
323
|
}
|
|
309
324
|
}
|
|
310
325
|
}
|