diff --git a/.github/scripts/check_task_artifacts.py b/.github/scripts/check_task_artifacts.py new file mode 100644 index 00000000..f5ccae66 --- /dev/null +++ b/.github/scripts/check_task_artifacts.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +"""Verify task ABI bundles and deployable reference artifacts after forge build.""" +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path + +CONTRACTS = ("DINTaskCoordinator", "DINTaskAuditor") + + +def canonical_abi(abi: list) -> list[str]: + # ABI entry order is not semantically significant; preserve nested ordering. + return sorted(json.dumps(entry, sort_keys=True, separators=(",", ":")) for entry in abi) + + +def bytecode_diagnostics(bundled: object, expected: str, artifact: dict) -> str: + """Expose reproducibility differences without relaxing exact bytecode parity.""" + if not isinstance(bundled, str): + return "bundled creation bytecode is not a string" + actual = bytes.fromhex(expected.removeprefix("0x")) + try: + saved = bytes.fromhex(bundled.removeprefix("0x")) + except ValueError: + return "bundled creation bytecode is not valid hex" + if saved == actual: + difference = "decoded bytes identical; textual representation differs" + else: + first = next((i for i, (a, b) in enumerate(zip(saved, actual)) if a != b), min(len(saved), len(actual))) + difference = f"first differing byte: {first}" + metadata = artifact.get("metadata", {}) + if isinstance(metadata, str): + try: + metadata = json.loads(metadata) + except ValueError: + metadata = None + if not isinstance(metadata, dict): + details = "build metadata: unavailable" + else: + sources = metadata.get("sources", {}) + source_hashes = { + path: source.get("keccak256") + for path, source in sources.items() if isinstance(source, dict) + } if isinstance(sources, dict) else {} + details = ( + f"build compiler: {json.dumps(metadata.get('compiler', {}), sort_keys=True)}; " + f"build settings: {json.dumps(metadata.get('settings', {}), sort_keys=True)}; " + f"build source hashes: {json.dumps(source_hashes, sort_keys=True)}" + ) + return ( + f"{difference}; bundled length: {len(saved)}; build length: {len(actual)}; " + f"bundled sha256: {hashlib.sha256(saved).hexdigest()}; " + f"build sha256: {hashlib.sha256(actual).hexdigest()}; " + f"{details}" + ) + + +def check_artifacts(root: Path) -> list[str]: + errors = [] + for name in CONTRACTS: + artifact_path = root / "foundry/out" / f"{name}.sol" / f"{name}.json" + try: + artifact = json.loads(artifact_path.read_text()) + expected_abi = canonical_abi(artifact["abi"]) + expected_bytecode = artifact["bytecode"]["object"] + if not expected_bytecode or expected_bytecode == "0x": + raise ValueError("empty build bytecode") + except (OSError, ValueError, KeyError, TypeError) as exc: + errors.append(f"{name}: cannot read build artifact; run forge build first ({exc})") + continue + + for directory in ("dincli/abis", "cache_model_0/abis"): + path = root / directory / f"{name}.json" + try: + bundled = json.loads(path.read_text()) + if canonical_abi(bundled["abi"]) != expected_abi: + errors.append(f"{directory}/{name}.json: ABI differs from build") + if directory == "cache_model_0/abis" and bundled.get("bytecode") != expected_bytecode: + errors.append(f"{directory}/{name}.json: creation bytecode differs from build") + errors.append(f"{name}: {bytecode_diagnostics(bundled.get('bytecode'), expected_bytecode, artifact)}") + except (OSError, ValueError, KeyError, TypeError) as exc: + errors.append(f"{directory}/{name}.json: invalid artifact ({exc})") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[2]) + args = parser.parse_args() + errors = check_artifacts(args.root) + for error in errors: + print(error) + if errors: + return 1 + print("Task ABI bundles and reference creation bytecode match the Foundry build") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bda01be7..bc86fe13 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,6 +66,9 @@ jobs: working-directory: foundry run: forge build + - name: Verify task artifact bundles + run: python3 .github/scripts/check_task_artifacts.py + # EIP-170/EIP-3860 budget for every foundry/src contract: fails below a # 1,024 B runtime margin, warns below 2,048 B. Reads artifacts directly # because `forge build --sizes` omits DINTaskAuditor. Issue #201 Part A. diff --git a/Developer/BACK_LOG.md b/Developer/BACK_LOG.md index 8a387b02..9126d032 100644 --- a/Developer/BACK_LOG.md +++ b/Developer/BACK_LOG.md @@ -37,8 +37,8 @@ | BL-28 | dincli (model owner) | No dincli command for `releaseGIRegistrationSlots` | `DINTaskCoordinator.releaseGIRegistrationSlots(gi)` (owner-only, once per ended GI) is the only thing that decrements validators' `activeRegistrationCount`; `endGI` deliberately doesn't (BL-10). dincli ships the function in its ABI but has no command that calls it, so under a non-zero `maxConcurrentRegistrationsPerStakeUnit` every GI permanently consumes its validators' concurrent-registration slots until they hit `TC_/TA_ConcurrentRegistrationCapReached`. Harmless while the cap is 0 (the default). | P3 (before the cap is set on any network) | task_100926_12 / issue #37 fast-follow; PR No. 204 review | 🟑 Fix in review β€” [PR #302](https://github.com/InfiniteZeroFoundation/DevNet/pull/302) adds `dincli model-owner gi release-slots [--gi N]` (task_061026_21 Part C); tracked in [#223](https://github.com/InfiniteZeroFoundation/DevNet/issues/223) Part 2 | | BL-29 | Solidity/foundry (fees) | `DinFeeRouter`'s non-treasury shares accrue with no way out | `routeFeeETH`/`routeFeeDIN` pay the treasury share out immediately but only *count* the `validatorPool`, `storage` and `publicGoods` shares in `accruedEth`/`accruedDin`. No function withdraws or distributes them, so with the default 95% validator-pool split, 95% of every swept faucet/registry fee stays in the router until an upgrade adds a path. Needs a decision on who can pull each bucket and where the validator-pool share goes (reward pools? `DinEmission`?). | P3/P4 | PR No. 204 review (`DinCoordinator.md` Β§14 No. 2), Sep 30, 2026; related to issue #43's open items | πŸ†• New β€” unaddressed; tracked in [#221](https://github.com/InfiniteZeroFoundation/DevNet/issues/221) | | BL-30 | Solidity/foundry (task contracts) | No recovery from a stalled GI | If a T1/T2 batch never reaches the reveal quorum, `finalizeT1Aggregation`/`finalizeT2Aggregation` revert forever (`TC_NoSubmissions`/`TC_InsufficientSubmissions`), and there is no owner path to skip the batch or abort the GI. `endGI` needs `AggregatorsSlashed` and `startGI` needs `GIended`, so the model is stuck: its reward pool and its validators' registration slots stay locked. This is also why BL-26's contract-side re-anchor cap was deferred. Needs a GI-abort design: who may abort, when, and where the reward pool goes. | P3/P4 | PR No. 191 review (BL-26); PR No. 204 review (`DINTaskCoordinator.md` Β§10 No. 6) | πŸ†• Decided, unimplemented β€” Abraham's GI-abort policy (Oct 26, [#222](https://github.com/InfiniteZeroFoundation/DevNet/issues/222)): anyone may abort after a stall timeout, locked rewards go back to their funders, slots are released, non-revealers are still penalised; the timeout value is set with the other testnet parameters in [#155](https://github.com/InfiniteZeroFoundation/DevNet/issues/155). Adds code to `DINTaskCoordinator`, which has 55 B above the size gate ([#201](https://github.com/InfiniteZeroFoundation/DevNet/issues/201)) | -| BL-31 | Solidity/foundry (slashing) | S6 no-participation slashing is implemented but not wired | `DinValidatorStake.recordNoParticipation` (escalating 10%-per-breach slash past `s6NoParticipationThreshold`) exists and is tested, but no task contract calls it. They deliberately skip it where `slashPartial` (S1/S2, with S5 escalation) already penalises the same missed work, to avoid stacking past `MIN_STAKE`. So S6 never fires. Decide whether S6 should cover something S1/S2 don't (e.g. registered but never assigned or never active), or whether to remove it and its parameters. | P3 | PR No. 204 review (`DinValidatorStake.md` Β§14 No. 1), Sep 30, 2026; slashing taxonomy S6 (issue #38) | ⏸️ Parked for testnet β€” Abraham (Oct 26, [#224](https://github.com/InfiniteZeroFoundation/DevNet/issues/224)): S1/S2 plus S5 already cover missed work; leave S6 unwired (or remove it and its parameters) and revisit after testnet. Needs a removal PR or a "parked" note in the docs | +| BL-31 | Solidity/foundry (slashing) | S6 no-participation slashing is implemented but not wired | `DinValidatorStake.recordNoParticipation` (escalating 10%-per-breach slash past `s6NoParticipationThreshold`) exists and is tested, but no task contract calls it. They deliberately skip it where `slashPartial` (S1/S2, with S5 escalation) already penalises the same missed work, to avoid stacking past `MIN_STAKE`. So S6 never fires. Decide whether S6 should cover something S1/S2 don't (e.g. registered but never assigned or never active), or whether to remove it and its parameters. | P3 | PR No. 204 review (`DinValidatorStake.md` Β§14 No. 1), Sep 30, 2026; slashing taxonomy S6 (issue #38) | ⏸️ Parked for testnet β€” Abraham (Oct 26, [#224](https://github.com/InfiniteZeroFoundation/DevNet/issues/224)): S1/S2 plus S5 already cover missed work; leave S6 unwired (or remove it and its parameters) and revisit after testnet. Parked behavior documented in PR [#305](https://github.com/InfiniteZeroFoundation/DevNet/pull/305); storage, parameters and API remain unchanged | | BL-32 | Tooling / onboarding | Contributor environment preflight check | No single command checks a dev machine before work starts. Each of these has already cost time: (1) Python β‰₯ 3.12; (2) `forge`/`anvil` installed and solc 0.8.28 available; (3) `foundry/node_modules` present β€” without `npm ci`, `forge test`'s parallel tests race to fetch `@openzeppelin/upgrades-core` over npx (see [CLAUDE.md](../CLAUDE.md)); (4) `eth-account` and `py-multibase` import; (5) `torch` imports in the active venv (full `pytest` run); (6) `.env`/`.env.` exist, the RPC endpoint answers, and `resolve_ipfs_config()` resolves. | P3/P4 (onboarding; small, ~half a day) | `preflight.sh` pattern from [Osomudeya/devops-scripting-labs](https://github.com/Osomudeya/devops-scripting-labs) (reviewed 2026-10-02); no existing equivalent (note: the only "preflight" in `dincli/cli/system.py` is the bridge's tx preflight β€” unrelated) | 🟑 Mostly fixed β€” [PR #255](https://github.com/InfiniteZeroFoundation/DevNet/pull/255) added `scripts/preflight.py` (Python/forge/anvil/node_modules/package checks, `--network` RPC/IPFS checks, 13 tests in `tests/test_preflight.py`, a preflight section in `Documentation/public/getting-started.md`). Remaining (exit non-zero on `node_modules`, eth-account/py-multibase, `--network` RPC/IPFS failures) tracked in [#245](https://github.com/InfiniteZeroFoundation/DevNet/issues/245) | | BL-33 | Tooling / deploy | On-chain deployment drift check | Nothing verifies that `foundry/deployments/.json` (and the `din_info` that `dincli system import-deployments` writes) still matches the chain. Proposal: a read-only check, either a forge script or `dincli system verify-deployment`, that for each recorded contract compares (1) the proxy's EIP-1967 implementation and admin slots against the recorded implementation/`proxyAdmin*` addresses; (2) the deployed runtime code hash against the current `forge build` artifact (immutables masked); (3) owner / DIN-Representative addresses; (4) wiring: `DinCoordinator` ↔ `DinToken`/`DinValidatorStake`/`DinModelRegistry`/fee router/treasury links and slasher authorizations on `DinValidatorStake`. Report per-item `OK` / `DRIFT` with expected vs. actual. Caveat: it only covers what the deployments JSON records, so a clean run doesn't prove nothing else was deployed or authorized. | P3 (before the DevNet 3.0 deploy; re-run after every `UpgradePlatform.s.sol` run) | Terraform state-vs-live drift pattern from [Osomudeya/devops-scripting-labs](https://github.com/Osomudeya/devops-scripting-labs) `03-drift-detection` (reviewed 2026-10-02); no existing code reads EIP-1967 slots or code hashes in `dincli/` or `foundry/script/` | βœ… Fixed β€” PR [#254](https://github.com/InfiniteZeroFoundation/DevNet/pull/254) (issue [#244](https://github.com/InfiniteZeroFoundation/DevNet/issues/244)). `dincli system verify-deployment` added: reads the deployments JSON, checks EIP-1967 impl/admin slots, extcodehash, ProxyAdmin owner(), DinCoordinator wiring, and slasher authorizations. `DeployPlatform.s.sol` and `UpgradePlatform.s.sol` extended to write impl addresses, codehashes, deployer, and git commit. 16 tests in `tests/test_verify_deployment.py`. | -| BL-34 | Solidity/foundry (task contracts) | `DINModelRegistry.disableModel()` kill-switch doesn't reach the live task contracts | `modelDisabled[modelId]` only gates `requestModelRegistration`/`requestManifestUpdate` inside the registry itself. `DINTaskCoordinator`/`DINTaskAuditor` have no dependency on, or awareness of, the registry (confirmed: zero functional references; the auditor has two doc comments naming `DINModelRegistry` by name (L31, L434) plus two more referencing "model registry" generically without naming the type (L393, L405) β€” none are functional checks). A model the DIN-Representative has disabled β€” e.g. because its task contracts are wrongfully slashing honest validators β€” keeps running full GIs completely unaffected. The task contracts' own doc table calls this a "kill-switch," which it isn't. Needs a decision: wire the task contracts to check `modelDisabled` before state-changing calls (requires threading the model ID + registry address into contracts that don't know about the registry today), or document `disableModel` as registry-metadata-only. | P3/P4 | 2026-07 security review M-2 (`Documentation/technical/audits/foundry-src-security-review.md`) | πŸ†• Decided, unimplemented β€” Abraham (Oct 26, [#224](https://github.com/InfiniteZeroFoundation/DevNet/issues/224)): make the kill-switch real before the public testnet (at minimum stop new GIs on a disabled model's task contracts), preferably in `DINTaskAuditor` or a small external guard given `DINTaskCoordinator`'s size headroom | -| BL-35 | Solidity/foundry (registry) | `rejectModel()`'s non-refund of the registration fee was never confirmed as intentional | `feePaid` (set at `requestModelRegistration`) is never refunded on `rejectModel()`, but it's also never refunded on `approveModel()` either β€” the fee appears to be a deliberate pay-to-apply cost regardless of outcome (anti-spam), not an oversight specific to rejection. Never written down as a deliberate choice anywhere. Needs confirmation and a doc note in `DINModelRegistry.md` / the dinrep role docs, so a future reader doesn't "fix" it into an inconsistent partial-refund state. | P4 (doc-only once confirmed) | 2026-07 security review L-4 (`Documentation/technical/audits/foundry-src-security-review.md`) | βœ… Decided β€” keep as intended (Abraham, Oct 26, [#224](https://github.com/InfiniteZeroFoundation/DevNet/issues/224)): the registration fee is a pay-to-apply anti-spam cost, refunded on neither approval nor rejection. Docs pending (`DINModelRegistry.md`, dinrep role docs) | +| BL-34 | Solidity/foundry (task contracts) | `DINModelRegistry.disableModel()` kill-switch doesn't reach the live task contracts | `modelDisabled[modelId]` only gates `requestModelRegistration`/`requestManifestUpdate` inside the registry itself. `DINTaskCoordinator`/`DINTaskAuditor` have no dependency on, or awareness of, the registry (confirmed: zero functional references; the auditor has two doc comments naming `DINModelRegistry` by name (L31, L434) plus two more referencing "model registry" generically without naming the type (L393, L405) β€” none are functional checks). A model the DIN-Representative has disabled β€” e.g. because its task contracts are wrongfully slashing honest validators β€” keeps running full GIs completely unaffected. The task contracts' own doc table calls this a "kill-switch," which it isn't. Needs a decision: wire the task contracts to check `modelDisabled` before state-changing calls (requires threading the model ID + registry address into contracts that don't know about the registry today), or document `disableModel` as registry-metadata-only. | P3/P4 | 2026-07 security review M-2 (`Documentation/technical/audits/foundry-src-security-review.md`) | βœ… Resolved β€” `validateGIStart` wired in `DINTaskAuditor.sol`; both `startGI` overloads check registry approval and disabled status via `_startGI`. Compatible task pairs required; existing deployed pairs unaffected. [PR No. 305](https://github.com/InfiniteZeroFoundation/DevNet/pull/305). | +| BL-35 | Solidity/foundry (registry) | `rejectModel()`'s non-refund of the registration fee was never confirmed as intentional | `feePaid` (set at `requestModelRegistration`) is never refunded on `rejectModel()`, but it's also never refunded on `approveModel()` either β€” the fee appears to be a deliberate pay-to-apply cost regardless of outcome (anti-spam), not an oversight specific to rejection. Never written down as a deliberate choice anywhere. Needs confirmation and a doc note in `DINModelRegistry.md` / the dinrep role docs, so a future reader doesn't "fix" it into an inconsistent partial-refund state. | P4 (doc-only once confirmed) | 2026-07 security review L-4 (`Documentation/technical/audits/foundry-src-security-review.md`) | βœ… Decided β€” keep as intended (Abraham, Oct 26, [#224](https://github.com/InfiniteZeroFoundation/DevNet/issues/224)): the registration fee is a pay-to-apply anti-spam cost, refunded on neither approval nor rejection. Documented in `DINModelRegistry.md` and dinrep role docs by PR [#305](https://github.com/InfiniteZeroFoundation/DevNet/pull/305) | diff --git a/Documentation/public/roles/dinrep.md b/Documentation/public/roles/dinrep.md index 4e518960..dde46ca9 100644 --- a/Documentation/public/roles/dinrep.md +++ b/Documentation/public/roles/dinrep.md @@ -165,7 +165,7 @@ dincli dinrep registry approve-registration-request dincli dinrep registry reject-registration-request ``` -The registration fee is retained by the contract in both cases. +The required registration fee is a nonrefundable, pay-to-apply anti-spam cost: neither approval nor rejection refunds it. This is intentional (Abraham, [issue #224, 26 October 2026](https://github.com/InfiniteZeroFoundation/DevNet/issues/224#issuecomment-6406239550)). Any amount paid above the required fee is still refunded in the request transaction. --- @@ -203,7 +203,7 @@ dincli dinrep registry enable-model ``` > [!CAUTION] -> Disabling a model does not delete it. All on-chain history is preserved. Today the task contracts (`DINTaskCoordinator`, `DINTaskAuditor`) do **not** check `modelDisabled(modelId)`, so disabling only blocks manifest updates. It does not stop the model's running GIs, submissions or slashing. +> Disabling a model does not delete it. All on-chain history is preserved. With the current compatible Foundry task pair and canonical registry wiring, disabling blocks both new-GI start overloads, even when already funded, as well as manifest updates and emission funding. Registry approval is required before any GI starts. In-flight GIs, including slashing, settlement and claims, continue; this is not a GI abort. Existing deployed pairs are unchanged and require redeployment for the new guard. Review task code, pairing and canonical registry wiring before authorization/approval; immutable wiring alone does not establish trust. Reenable restores start eligibility. Pre-funded pools have no new refund or recovery path. --- diff --git a/Documentation/public/roles/model-owner.md b/Documentation/public/roles/model-owner.md index 1110d593..1a5796e9 100644 --- a/Documentation/public/roles/model-owner.md +++ b/Documentation/public/roles/model-owner.md @@ -12,13 +12,13 @@ Deploy the Task Coordinator and Task Auditor contracts specific to your task. ```bash # Deploy the Task Coordinator -dincli model-owner deploy task-coordinator --artifact +dincli model-owner deploy task-coordinator --artifact /path/to/devnet/foundry/out/DINTaskCoordinator.sol/DINTaskCoordinator.json --model-id # Deploy the Task Auditor -dincli model-owner deploy task-auditor --artifact +dincli model-owner deploy task-auditor --artifact /path/to/devnet/foundry/out/DINTaskAuditor.sol/DINTaskAuditor.json --model-id ``` -The `--artifact` flag must point to the compiled Hardhat/Foundry JSON output (contains ABI and bytecode). +The `--artifact` flag points to compiled JSON containing ABI and deployment bytecode. The coordinator defaults to the registry’s next model ID; the auditor defaults to the coordinator’s ID. Explicit IDs must match. Configure the platform `stake`, `token`, and canonical `registry` in this network's `din_info.json` before deploying the current Foundry auditor. Coordinate ID allocation with the DIN-Representative; registration approval assigns the authoritative ID later. Supported legacy Hardhat artifacts do not gain the future-GI guard. See the [model workflow](../workflows/model-workflow.md) for registration and deployment requirements. --- diff --git a/Documentation/public/workflows/model-workflow.md b/Documentation/public/workflows/model-workflow.md index affdd3c0..bf679d28 100644 --- a/Documentation/public/workflows/model-workflow.md +++ b/Documentation/public/workflows/model-workflow.md @@ -32,7 +32,16 @@ dincli system register-wallet --account > More on wallet configuration can be found in the [DIN CLI Documentation](../cli-reference.md). -In current DIN Protocol, the model owner needs to deploy a taskCoordinator contract and a taskAuditor contract specific to each model. +In current DIN Protocol, the model owner needs to deploy a taskCoordinator contract and a taskAuditor contract specific to each model. + +For current Foundry artifacts, `model-owner deploy task-coordinator` defaults `--model-id` to `DINModelRegistry.totalModels()`, the ID the next approval assigns. This assumes no other registration is approved first. `model-owner deploy task-auditor` defaults to the paired coordinator’s `modelId()`; explicit IDs must match. Both commands configure the platform `DinToken` from this network’s `din_info.json` (`token`), with auditor token setup completed before pairing. Configure the canonical platform registry address in this network's `din_info.json` (`registry`) before deploying the auditor. Its fourth constructor argument is immutable registry wiring. The coordinator reports `taskProtocolVersion() == 1`, identifying the interface that requires the auditor GI-start guard. Before deploying an auditor, the CLI checks this version as well as matching model IDs: version 1 requires the registry-aware four-argument auditor. Released unversioned coordinators remain supported with matching legacy auditors: coordinators without `modelId()` use the two-argument auditor, and those with `modelId()` use the three-argument auditor; unknown versions and mixed generations are rejected before deployment or pairing. Legacy pairs do not gain the guard described below. Unsupported custom constructor signatures are rejected. + +The checked-in `cache_model_0` artifacts and manifest describe the current reference pair. The `cache-default-artifacts` command instead downloads the manifest and ABIs selected by this network's `din_info.json` (`default_manifest` and `default_abis`); those historical bootstrap CIDs are not refreshed by this change. Verify the downloaded constructor signatures and registry wiring before using them for a new deployment. + +The DIN-Representative must approve the model before **any** GI can start. Both start overloads check the registry-assigned ID by auditor address; do not assume the anticipated constructor ID establishes registration. Coordinate ID allocation with the DIN-Representative for correct stake floors. + +Disabling a model prevents future GIs, including those with a pre-funded or privately funded pool. An in-flight GI continues through work, slashing, settlement and reward claims. There is no GI abort or new pool refund/recovery path. The guard requires a newly deployed compatible coordinator/auditor pair with canonical registry wiring and reviewed task code; existing deployments remain unchanged. See [kill-switch scope](../../technical/contracts/DINModelRegistry.md#11-kill-switch). + # Workflow @@ -40,7 +49,7 @@ In current DIN Protocol, the model owner needs to deploy a taskCoordinator contr Model Owner deploys a taskCoordinator contract specific to each model. ```bash -dincli model-owner deploy task-coordinator --artifact "/path/to/devnet/hardhat/artifacts/contracts/DINTaskCoordinator.sol/DINTaskCoordinator.json" +dincli model-owner deploy task-coordinator --artifact "/path/to/devnet/foundry/out/DINTaskCoordinator.sol/DINTaskCoordinator.json" --model-id ``` dincli will store the taskCoordinator contract address in the .env file as `SEPOLIA_OP_DEVNET_DINTaskCoordinator_Contract_Address` in your local project directory which we call `root_dir`. @@ -49,7 +58,7 @@ dincli will store the taskCoordinator contract address in the .env file as `SEPO Model Owner deploys a taskAuditor contract specific to each model. ```bash -dincli model-owner deploy task-auditor --artifact "/path/to/devnet/hardhat/artifacts/contracts/DINTaskAuditor.sol/DINTaskAuditor.json" +dincli model-owner deploy task-auditor --artifact "/path/to/devnet/foundry/out/DINTaskAuditor.sol/DINTaskAuditor.json" --model-id ``` dincli will store the taskAuditor contract address in the .env file as `SEPOLIA_OP_DEVNET__DINTaskAuditor_Contract_Address` in your local project directory which we call `root_dir`. diff --git a/Documentation/technical/audits/foundry-src-security-review.md b/Documentation/technical/audits/foundry-src-security-review.md index 6667a654..5d3d1cb4 100644 --- a/Documentation/technical/audits/foundry-src-security-review.md +++ b/Documentation/technical/audits/foundry-src-security-review.md @@ -165,7 +165,9 @@ This doesn't cause direct fund loss, but it undermines the core assumption that ### M-2. `DINModelRegistry.disableModel()` kill-switch doesn't reach the live task contracts -**Still open β€” tracked in [#224](https://github.com/InfiniteZeroFoundation/DevNet/issues/224) / BL-34** (re-confirmed against `develop` @ `740a613`, 2026-10). +**Resolution (#224, Oct 26 decision):** the current Foundry coordinator calls auditor-side registry validation from both `startGI` overloads. Approved/enabled status is required before each new GI, including pre-funded pools. New compatible task pairs with reviewed canonical registry wiring are required; old deployments and in-flight GIs are unchanged. The historical scenario and broader recommendation below are retained as audit context; GI abort is outside this fix. + +**Original finding β€” tracked in [#224](https://github.com/InfiniteZeroFoundation/DevNet/issues/224) / BL-34** (re-confirmed against `develop` @ `740a613`, 2026-10; superseded for new compatible pairs by the resolution above). **Contracts / functions:** `DINModelRegistry.sol`, `disableModel()`/`enableModel()` (L404-416); contrast with `DINTaskCoordinator.sol` and `DINTaskAuditor.sol`, neither of which references `DINModelRegistry` at all. @@ -212,7 +214,7 @@ This isn't exploitable, but it either indicates a missing feature (a `jail()` fu | L-1 | **Fixed** (current `stake()` updates `activeStake` before calling `DIN_TOKEN.safeTransferFrom` β€” correct checks-effects-interactions order). | `DinValidatorStake.sol` L113-126 | | L-2 | **Fixed β€” [PR #176](https://github.com/InfiniteZeroFoundation/DevNet/pull/176).** `depositAndMint()` has no minimum-deposit / non-zero-mint check. If the owner ever sets `dinPerEth` to a value that isn't a clean multiple of `1e18`, a small enough `msg.value` can round `mintAmount` to 0 via integer division while the ETH is still retained by the contract. *(Fix note: the zero-mint is only reachable when `dinPerEth < 1e18`, i.e. `msg.value * dinPerEth < 1e18` β€” any `dinPerEth >= 1e18` mints at least 1 wei for `msg.value >= 1`, clean multiple or not.)* | `DinCoordinator.sol` L92-101 | | L-3 | **Fixed β€” [PR #176](https://github.com/InfiniteZeroFoundation/DevNet/pull/176).** `requestModelRegistration` doesn't refund `msg.value` above the required fee β€” any overpayment is silently kept. *(Fix note: `requestManifestUpdate` had the same gap and is fixed the same way β€” `feePaid` records the required fee and the excess is refunded.)* | `DINModelRegistry.sol` L166-211 (`requestManifestUpdate` L281-320) | -| L-4 | `rejectModel()` never refunds the fee paid in the corresponding `requestModelRegistration` β€” a rejected requester loses their fee permanently. `approveModel()` doesn't touch `feePaid` either, so this looks like an intentional pay-to-apply design rather than an oversight, but it was never confirmed/documented as such. Tracked in [#224](https://github.com/InfiniteZeroFoundation/DevNet/issues/224) / BL-35. | `DINModelRegistry.sol` L244-254 | +| L-4 | **Confirmed intentional β€” [#224, Oct 26 decision](https://github.com/InfiniteZeroFoundation/DevNet/issues/224#issuecomment-6406239550) / BL-35.** The required registration fee is a nonrefundable pay-to-apply anti-spam cost on both approval and rejection. Registry and DIN-Representative docs explain the rationale and distinguish excess-payment refunds. | `DINModelRegistry.sol` L244-254 | | L-5 | **Fixed/obsolete** β€” `withdrawFees` no longer exists in `DINModelRegistry.sol`; superseded by `setFees`/`sweepFeesToRouter` (commit `06190e8`, "scope DINModelRegistry fee path to ETH-only, accumulate-then-sweep"), neither of which has this gap. | ~~`DINModelRegistry.sol` L472-477~~ | | L-6 | **Fixed β€” [PR #176](https://github.com/InfiniteZeroFoundation/DevNet/pull/176).** `DINTaskCoordinator`/`DINTaskAuditor` constructors accept `dinvalidatorStakeContract_address` / `dintaskcoordinator_contract_address` with no zero-address check. Self-inflicted misconfiguration risk only (deployer controls the args), not attacker-triggered. | `DINTaskCoordinator.sol` L256-264 (+ `setDINTaskAuditorContract` L272-283), `DINTaskAuditor.sol` L399-424 | | L-7 | **Partly fixed** (task_210726_6 Β§3) β€” the dead-code half: `totalDepositedRewards` replaced by a real per-GI `giRewardPool` mapping ("funded per-GI, settled per-GI" model), and `RewardDeposited` is now actually emitted (`DINTaskAuditor.sol:487`). The stray-ETH half still applies as originally written: the contract still has no `receive()`/`fallback()` or any ETH withdrawal path, so ETH forced into it (e.g. via `selfdestruct`) is still permanently unrecoverable (re-verified against `develop`). | `DINTaskAuditor.sol` | diff --git a/Documentation/technical/contracts/DINModelRegistry.md b/Documentation/technical/contracts/DINModelRegistry.md index 34984224..4bd4c322 100644 --- a/Documentation/technical/contracts/DINModelRegistry.md +++ b/Documentation/technical/contracts/DINModelRegistry.md @@ -18,7 +18,7 @@ Core capabilities: - **Two-phase model registration** β€” submit a request, the DIN-Representative approves or rejects. - **Two-phase manifest updates** β€” same request/approval flow. -- **Kill switch** β€” the DIN-Representative can instantly disable any model. +- **Kill switch** β€” disabling blocks future GIs on compatible task pairs, manifest updates and emission funding. - **Dynamic fee governance** β€” all four fee parameters are adjustable by the DIN-Representative. Requests keep exactly the required fee; any overpayment is refunded to the caller. - **Fee routing** β€” accumulated ETH fees are swept to `DinFeeRouter` (`sweepFeesToRouter`), which splits them across validator pool / treasury / storage / public goods. - **Transferable admin** β€” the owner role can be handed to a multisig or timelock (`transferOwnership`). @@ -294,7 +294,7 @@ function requestModelRegistration( 8. **Emit** `ModelRegistrationRequested`. 9. **Refund:** if `msg.value > requiredFee`, send the overpayment back to `msg.sender` with a low-level `call` β€” revert `RefundFailed` if it fails. This runs last (checks-effects-interactions) and the function is `nonReentrant`. -> **Note:** The required fee is held in the contract regardless of whether the request is approved or rejected. +> **Fee policy:** The required registration fee is a nonrefundable, pay-to-apply anti-spam cost: neither approval nor rejection refunds it. This is intentional (Abraham, [issue #224, 26 October 2026](https://github.com/InfiniteZeroFoundation/DevNet/issues/224#issuecomment-6406239550)). Any amount paid above the required fee is still refunded in the request transaction. --- @@ -372,18 +372,14 @@ function disableModel(uint256 modelId) external onlyOwner function enableModel(uint256 modelId) external onlyOwner ``` -- Sets / clears `modelDisabled[modelId]`. -- `modelDisabled` is `public`, so downstream contracts *could* read it (`modelRegistry.modelDisabled(modelId)`), but today neither `DINTaskCoordinator` nor `DINTaskAuditor` does. -- Emits `ModelDisabled` / `ModelEnabled`. -- **Disabled β‰  Deleted.** History, ownership, and manifest are preserved for auditability. +- Sets / clears `modelDisabled[modelId]` and emits `ModelDisabled` / `ModelEnabled`. +- Preserves history, ownership and manifest. -What disabling actually does today: +For the current Foundry task pair, both `startGI(gi)` and `startGI(gi, score)` call the auditor's `validateGIStart(gi)` before changing the pass score, GI counter or state. The auditor requires an approved registry entry for its own address, checks that entry's `modelDisabled` flag, and requires a funded pool. It uses the registry-assigned ID, including ID zero, rather than the constructor's anticipated stake-floor ID. An unregistered model or failed registry read prevents starting a GI. Pre-funding or private funding cannot bypass a disabled flag; reenabling permits starting again if the other prerequisites hold. -| Effect | Enforced? | -|--------|-----------| -| Blocks `requestManifestUpdate` (`notDisabled` modifier) | βœ… | -| Blocks `approveManifestUpdate` for that model | βœ… | -| Stops GIs, LM submissions, aggregation, audits or slashing in the task contracts | ❌ β€” not checked by `DINTaskCoordinator` / `DINTaskAuditor` (Β§19 No. 6) | +Disabling blocks subsequent GI starts, manifest updates and emission funding. It does **not** abort or freeze an in-flight GI: submissions, audits, aggregation, slashing, settlement and reward claims continue. A start ordered before disable may succeed. Existing funds remain in their pools; this change adds no cancellation, refund or recovery path. + +Both task contracts are non-upgradeable. Existing deployed pairs keep their previous behavior and need a newly deployed compatible pair to gain this protection; there is no automatic retrofit. Deploy the new coordinator together with the new auditor wired to the canonical platform registry proxy. An old coordinator can bypass the new auditor guard, and a new coordinator cannot call this guard on an old auditor. Immutable wiring prevents later reassignment but does not prove the supplied registry is canonical. The DIN-Representative must review the task code, pairing and registry address before slasher authorization and approval; arbitrary custom or malicious task code is outside this enforcement guarantee. --- @@ -464,7 +460,7 @@ DINModelRegistry └── calls β†’ DinFeeRouter.routeFeeETH{value}(this) [sweepFeesToRouter] ``` -No contract currently reads `modelDisabled`: `DINTaskCoordinator` and `DINTaskAuditor` do not check it, so the kill switch only blocks manifest updates in the registry itself (see Β§19 No. 6). +`DinEmission` checks `modelDisabled` before funding. Compatible task pairs check approval and disabled status through the auditor before starting a GI; see Β§11 for deployment and in-flight limitations. --- @@ -477,9 +473,9 @@ No contract currently reads `modelDisabled`: `DINTaskCoordinator` and `DINTaskAu | Slasher status revoked between request and approval | `isSlasherContract()` revalidated inside `approveModel()` | | Coordinator / auditor reused across models | `_modelIdByTaskCoordinator` / `_modelIdByTaskAuditor` uniqueness enforced at approval | | Silent manifest change by model owner | Manifest updates require DIN-Representative approval | -| Malicious approved model | Kill switch (`disableModel`) provides instant remediation | +| Malicious approved model | `disableModel` blocks future GIs on reviewed compatible task pairs; in-flight work continues | | Disabled model manifest still approved | `approveManifestUpdate` checks `modelDisabled` before writing | -| Fee spam on registration | Fee required at request time; retained on rejection | +| Fee spam on registration | Nonrefundable pay-to-apply anti-spam fee on approval and rejection | | Overpayment kept by the registry | Anything above the required fee is refunded in the same call; `feePaid` records only the required fee | | Re-entrancy through the refund call | The refund is the last step (checks-effects-interactions) and both request functions are `nonReentrant` (`ReentrancyGuardTransient`) | | Admin key compromise | `transferOwnership` enables migration to multisig / timelock | @@ -494,7 +490,7 @@ No contract currently reads `modelDisabled`: `DINTaskCoordinator` and `DINTaskAu - `taskCoordinator` and `taskAuditor` addresses are permanent after approval β€” no mechanism to update them. - Pending requests never expire β€” a stale request remains approvable indefinitely (a `uint256 expiresAt` field could address this). - Single admin key (the DIN-Representative) β€” no multi-sig quorum or on-chain voting yet (migrate via `transferOwnership`). -- The kill switch (`modelDisabled`) is not enforced by the task contracts (see Β§19 No. 6). +- The future-GI guard depends on compatible, reviewed task code wired to the canonical registry; it does not abort in-flight work (Β§11). --- @@ -532,4 +528,4 @@ Observations from the PR 13 review worth tracking: - **No. 3 β€” Single-step ownership transfer:** `transferOwnership` is one-step; a typoed address is unrecoverable. `Ownable2StepUpgradeable` would make handover safer. - **No. 4 β€” Repurposed error names:** `CoordinatorNoLongerSlasher` / `AuditorNoLongerSlasher` now also fire on first-time request validation, where "no longer" is a misnomer. Selector-stable but slightly misleading in traces. - **No. 5 β€” Admin revert selector changed:** unauthorized admin calls revert with `OwnableUnauthorizedAccount` instead of `NotDINDAOAdmin` β€” anything decoding revert reasons (tests, dincli error handling) must use the new selector. -- **No. 6 β€” Kill switch is not enforced downstream:** neither `DINTaskCoordinator` nor `DINTaskAuditor` reads `modelDisabled`, so disabling a model does not stop its GIs, submissions, or slashing β€” it only blocks manifest-update requests/approvals in the registry. +- **No. 6 β€” Future-GI guard implemented (#224):** compatible current task pairs reject new GIs for unregistered or disabled models. Existing deployments and in-flight work retain their behavior; abort remains outside this scope (Β§11). diff --git a/Documentation/technical/contracts/DINShared.md b/Documentation/technical/contracts/DINShared.md index d2a74807..d1b333fa 100644 --- a/Documentation/technical/contracts/DINShared.md +++ b/Documentation/technical/contracts/DINShared.md @@ -410,3 +410,8 @@ The `TA_` and `TC_` prefixes make it immediately clear in stack traces and event ### Commit-Then-Reveal Auditor Scoring `LMSevaluationStarted` and `LMSevaluationRevealStarted` split what was previously a single evaluation phase into two: auditors first commit `keccak256(abi.encode(score, vote, salt, auditor, gi, batchId, modelIndex))` (hiding their vote from other auditors until everyone has committed), then, once the model owner closes the commit window via `DINTaskCoordinator.startLMsubmissionsEvaluationReveal`, reveal the underlying `(score, vote, salt)` for it to be counted. An auditor who commits but never reveals is simply excluded from quorum/median counting, and remains slashable via the existing "missed vote" check in `slashAuditors` β€” no separate non-reveal handling needed. + + +### Future-GI registry guard (issue #224) + +`ITaskModelRegistry` exposes `getModelIdByTaskAuditor(address) -> (bool exists, uint256 modelId)` and `modelDisabled(uint256) -> bool`. `IDINTaskAuditor.validateGIStart(uint256 gi)` is the coordinator-only view gate used by both coordinator start overloads. `TA_ModelNotRegistered()` and `TA_ModelDisabled(uint256 modelId)` describe registry failures; `TC_GIRewardPoolNotFunded()` retains the existing funding selector when raised by this auditor-side gate. See [auditor deployment and scope](DINTaskAuditor.md#future-gi-guard-and-deployment). diff --git a/Documentation/technical/contracts/DINTaskAuditor.md b/Documentation/technical/contracts/DINTaskAuditor.md index 57e0913a..3223d09a 100644 --- a/Documentation/technical/contracts/DINTaskAuditor.md +++ b/Documentation/technical/contracts/DINTaskAuditor.md @@ -43,6 +43,7 @@ Phase transitions are driven by the paired [`DINTaskCoordinator`](DINTaskCoordin |----------|-------------| | `dinvalidatorStakeContract` | `DinValidatorStake` proxy (constructor) | | `dintaskcoordinatorContract` | Paired coordinator (constructor) | +| `modelRegistry` (`immutable`) | Canonical platform registry proxy (fourth constructor argument; must contain code; no setter) | | `modelId` (`immutable`) | Registry model ID for the stake floor (constructor) | | `dinToken` | DIN token (`setDinToken`); required before `depositRewards`, claims or bonded disputes | @@ -92,7 +93,7 @@ Mappings marked *(internal)* have no auto-getter; read them through the view nam | Caller | Functions | |--------|-----------| -| Paired coordinator (`onlyTaskCoordinator`) | `updatePassScore`, `createAuditorsBatches`, `setTestDataAssignedFlag`, `finalizeEvaluation`, `slashAuditors`, `settleRewards`, `decrementAuditorRegistrations` | +| Paired coordinator (`onlyTaskCoordinator`) | `validateGIStart`, `updatePassScore`, `createAuditorsBatches`, `setTestDataAssignedFlag`, `finalizeEvaluation`, `slashAuditors`, `settleRewards`, `decrementAuditorRegistrations` | | `owner()` (model owner) | `assignAuditTestDataset`, `reassignAuditTestDataset`, all setters | | Assigned auditor (`onlyAssignedAuditor`) | `commitAuditScore`, `revealAuditScore` | | Auditor of the disputed batch (`isBatchAuditor`) | `openTestDataDispute` | @@ -101,6 +102,16 @@ Mappings marked *(internal)* have no auto-getter; read them through the view nam --- +### Future-GI guard and deployment + +Constructor: `DINTaskAuditor(stake, coordinator, modelId, modelRegistry)`. A zero/EOA registry reverts `TA_InvalidAddress`; a wrong contract or unavailable registry fails closed at GI start. `validateGIStart(gi)` is coordinator-only (`TA_NotTaskCoordinator`) and reverts `TA_ModelNotRegistered`, `TA_ModelDisabled(registeredId)` or the existing `TC_GIRewardPoolNotFunded` selector. The public `giRewardPool` getter remains an unconditional accounting read. + +For the current Foundry task pair, both `startGI(gi)` and `startGI(gi, score)` call the auditor's `validateGIStart(gi)` before changing the pass score, GI counter or state. The auditor requires an approved registry entry for its own address, checks that entry's `modelDisabled` flag, and requires a funded pool. It uses the registry-assigned ID, including ID zero, rather than the constructor's anticipated stake-floor ID. An unregistered model or failed registry read prevents starting a GI. Pre-funding or private funding cannot bypass a disabled flag; reenabling permits starting again if the other prerequisites hold. + +Disabling blocks subsequent GI starts, manifest updates and emission funding. It does **not** abort or freeze an in-flight GI: submissions, audits, aggregation, slashing, settlement and reward claims continue. A start ordered before disable may succeed. Existing funds remain in their pools; this change adds no cancellation, refund or recovery path. + +Both task contracts are non-upgradeable. Existing deployed pairs keep their previous behavior and need a newly deployed compatible pair to gain this protection; there is no automatic retrofit. Deploy the new coordinator together with the new auditor wired to the canonical platform registry proxy. An old coordinator can bypass the new auditor guard, and a new coordinator cannot call this guard on an old auditor. The CLI therefore reads the deployed coordinator's `taskProtocolVersion()` before auditor deployment: version `1` requires this registry-aware four-argument auditor, while released unversioned coordinators accept only matching legacy auditor signatures (two arguments without `modelId()`, three arguments with it). Mixed or unknown generations fail before deployment, address writes or one-shot pairing. Direct deployments still require review; the version getter is a compatibility declaration, not proof of trusted task code. Immutable wiring prevents later reassignment but does not prove the supplied registry is canonical. The DIN-Representative must review the task code, pairing and registry address before slasher authorization and approval; arbitrary custom or malicious task code is outside this enforcement guarantee. + ## 5. Registration & Submissions - **`registerDINAuditor(gi)`** β€” requires the coordinator state `DINauditorsRegistrationStarted`. Checks: not already registered, not already an aggregator for the same GI (`TA_DualRoleNotAllowed`, read from the coordinator's `isDINAggregator`; issue No. 180), fewer than 300 registered (`TA_RegistrationCapReached`), `isValidatorActive` (`TA_AuditorNotActive`), stake β‰₯ the model floor (`TA_StakeBelowModelFloor`), and the concurrent-registration cap (`TA_ConcurrentRegistrationCapReached`). Then calls `incrementActiveRegistration` and emits `DINAuditorRegistered`. @@ -207,7 +218,7 @@ Read alongside the [foundry/src security review](../audits/foundry-src-security- - **No. 5 β€” Fixed: scoring can't open before test data is assigned.** `setTestDataAssignedFlag` now checks every batch has a commitment and moves the coordinator to `AuditTestDataAssigned`, which `startLMsubmissionsEvaluation` requires (task_061026_21 Part B). - **No. 6 β€” Stale NatSpec and reused errors:** `slashAuditors` says S1 and S3 are both `minStake()` (S1 is now a fraction). Several comments call parameters "DAO-settable"; they are `onlyOwner`, i.e. set by the model owner. `setDisputePenaltyBps` reuses `TA_InvalidDisputeBond`, and `closeExpiredDispute` reuses `TA_DisputeWindowClosed` for "window still open". - **No. 7 β€” `modelId` is fixed at construction**, before the registry assigns it (see [DINTaskCoordinator Β§10 No. 4](DINTaskCoordinator.md#10-review-notes--open-caveats)). -- **No. 8 β€” dincli deploy (fixed):** `dincli model-owner deploy task-auditor` calls the three-argument constructor. `--model-id` defaults to the coordinator's `modelId()`, and the command refuses a mismatch. It then calls `setDinToken` with the platform `DinToken` before linking the auditor to the coordinator. +- **No. 8 β€” Fixed: dincli supports the current four-argument constructor.** `dincli model-owner deploy task-auditor` passes the configured canonical registry and defaults `--model-id` to the paired coordinator’s `modelId()`. It rejects model-ID or protocol-version mismatches (current pairs use version `1`), then sets the platform `DinToken` before linking the auditor. Legacy constructor support does not add the guard to older contracts; the oldest two-argument artifacts skip token setup because they have no `setDinToken` function. - **No. 9 β€” Fixed: a dincli auditor commit retry no longer loses the committed salt.** Rerunning `dincli auditor lms-evaluation evaluate --submit` used to generate a new salt and overwrite the local commit cache even when the on-chain commit already existed. The later reveal then failed the hash check, and the auditor was slashed for a missed vote. dincli now skips any LM whose `hasCommittedLM` is already set, leaving its cache untouched, and writes the cache before sending the commit tx (issue No. 202 Part 1). A rerun is safe. --- diff --git a/Documentation/technical/contracts/DINTaskCoordinator.md b/Documentation/technical/contracts/DINTaskCoordinator.md index eb295ba8..8f187644 100644 --- a/Documentation/technical/contracts/DINTaskCoordinator.md +++ b/Documentation/technical/contracts/DINTaskCoordinator.md @@ -43,6 +43,7 @@ Auditor registration, client submissions, scoring and rewards live in the paired |----------|-------------| | `dinvalidatorStakeContract` | `DinValidatorStake` proxy (constructor) | | `dinTaskAuditorContract` | Paired auditor (`setDINTaskAuditorContract`) | +| `taskProtocolVersion()` | Pure getter returning `1`: requires a registry-aware auditor implementing `validateGIStart`; used by CLI deployment preflight | | `modelId` (`immutable`) | Registry model ID, used for the per-model stake floor (constructor) | | `GI` | Current GI counter (starts at 0; the first GI is 1) | | `GIstate` | Current `GIstates` value | @@ -121,6 +122,8 @@ setDinToken(dinToken) (needed for dispute bonds; dincli --- +Registry approval is required after setup and before the first GI. Both start overloads share the auditor's `validateGIStart(gi)` call after the state/index checks and before any mutation. This replaces the direct reward-pool lookup to preserve the coordinator's size budget. See [auditor wiring and future-GI guard](DINTaskAuditor.md#future-gi-guard-and-deployment) for required pair compatibility and registry trust. + ## 6. GI Lifecycle The numbers are `GIstates` ordinals (see [DINShared Β§2.1](DINShared.md)). @@ -152,7 +155,7 @@ Every owner transition after `startGI` takes the expected `_GI` and reverts `TC_ The three reveal-opening calls (`startLMsubmissionsEvaluationReveal`, `startT1AggregationReveal`, `startT2AggregationReveal`) are manual owner steps. Forgetting one stalls the GI in the commit window (finalize reverts) without corrupting state. ### 6.1 `startGI` -Requires `_GI == GI + 1` and a funded reward pool: `dinTaskAuditorContract.giRewardPool(_GI) > 0`, otherwise `TC_GIRewardPoolNotFunded`. The two-argument overload also calls `updatePassScore`. Then `GI++`. +Requires `_GI == GI + 1` and the genesis/GI-ended state, then calls `dinTaskAuditorContract.validateGIStart(_GI)`: registry approval, enabled status and a nonzero reward pool are required. The auditor preserves `TC_GIRewardPoolNotFunded` for an empty pool. Both overloads check before mutation; the two-argument overload then calls `updatePassScore`, and both increment `GI` and enter `GIstarted`. ### 6.2 `registerDINaggregator` In `DINaggregatorsRegistrationStarted` only, and `onlyCurrentGI`: `_GI` must be the current GI (`TC_WrongGI`), as for `DINTaskAuditor.registerDINAuditor` (issue #206). Checks in order: `isValidatorActive` (`TC_AggregatorNotActive`), not already registered, fewer than 300 registered (`TC_RegistrationCapReached`), stake β‰₯ `getModelStakeMin(modelId)` when non-zero (`TC_StakeBelowModelFloor`), and β€” when `maxConcurrentRegistrationsPerStakeUnit > 0` β€” `activeRegistrationCount < (stake / minStake) Γ— cap` (`TC_ConcurrentRegistrationCapReached`). Then records the aggregator, calls `incrementActiveRegistration`, emits `DINValidatorRegistered`. @@ -257,7 +260,7 @@ DINTaskCoordinator β”‚ getModelStakeMin, maxConcurrentRegistrationsPerStakeUnit, β”‚ activeRegistrationCount, increment/decrementActiveRegistration, β”‚ slashPartial (S2), slash (bad consensus, S4), slashTreasury - β”œβ”€β”€ DINTaskAuditor: giRewardPool, updatePassScore, createAuditorsBatches (with the audit seed), + β”œβ”€β”€ DINTaskAuditor: validateGIStart, updatePassScore, createAuditorsBatches (with the audit seed), β”‚ setTestDataAssignedFlag, finalizeEvaluation, approvedModelIndexes, β”‚ slashAuditors, settleRewards, decrementAuditorRegistrations └── DinToken: safeTransferFrom / safeTransfer / burn (dispute bonds) @@ -282,7 +285,7 @@ Earlier findings from the [foundry/src security review](../audits/foundry-src-se - **No. 7 β€” Stale NatSpec:** comments around the dispute scaffold still say `DinTreasury` "doesn't exist on develop yet" (forfeitures are already forwarded). Several parameters are described as "DAO-settable"; they are `onlyOwner`, i.e. set by the model owner. - **No. 8 β€” Leftovers:** `networkFeeFloor` is stored but not enforced. - **No. 9 β€” Not upgradeable:** a bug in a model's task contracts requires redeploying them and re-registering the model. -- **No. 10 β€” dincli deploy (fixed):** `dincli model-owner deploy task-coordinator` calls the two-argument constructor. `--model-id` defaults to `DINModelRegistry.totalModels()`, the ID the next approval assigns, with a warning that this only holds if no other request is approved first. It then calls `setDinToken` with the platform `DinToken`. (`dincli aggregator aggregate-t2` used to name its working directory, worker job and container after the last T1 batch id; fixed in issue No. 202 Part 2.) +- **No. 10 β€” Fixed: dincli supports the current two-argument constructor.** `dincli model-owner deploy task-coordinator` defaults `--model-id` to `DINModelRegistry.totalModels()`, with a warning that this ID only holds if no other registration is approved first. It then sets the platform `DinToken`. Legacy one-argument artifacts remain supported and skip token setup because they have no `setDinToken` function. (`dincli aggregator aggregate-t2` used to name its working directory, worker job and container after the last T1 batch id; fixed in issue No. 202 Part 2.) - **No. 11 β€” Fixed: `registerDINaggregator` now checks the GI.** It used to have no `onlyCurrentGI`, so during GI N's window a validator could register for GI N+1. Up to 300 addresses could fill that list, which locked out honest registrants and handed the attacker every T1/T2 batch. Registering for a released past GI also leaked the caller's own slot. Fixed by adding `onlyCurrentGI` (issue #206); it cost 9 bytes inside the No. 1 budget. --- diff --git a/Documentation/technical/contracts/DinValidatorStake.md b/Documentation/technical/contracts/DinValidatorStake.md index 86a9428e..63363dc2 100644 --- a/Documentation/technical/contracts/DinValidatorStake.md +++ b/Documentation/technical/contracts/DinValidatorStake.md @@ -173,7 +173,7 @@ The per-slasher ring is namespaced by the **calling task contract** because `giI Increments `s6NoParticipationCount[validator]` and emits `S6NoParticipationRecorded`. Below `s6NoParticipationThreshold` it returns `0`. At or above it, it slashes `MIN_STAKE Γ— (count βˆ’ threshold + 1) / 10`, capped at `MIN_STAKE` (10% more per breach), and emits `S6PartialSlashFired`. The count never resets. -> No contract in `foundry/src` currently calls `recordNoParticipation` β€” the task contracts deliberately skip it where `slashPartial` already applies ("No S6 recordNoParticipation here…"), so S6 is implemented but not wired (Β§14 No. 1). +> **Parked for testnet** ([Abraham, 26 October 2026, #224](https://github.com/InfiniteZeroFoundation/DevNet/issues/224#issuecomment-6406239550)): S1/S2 plus S5 already cover missed work. Retain S6 storage, parameters, API, events and tests without activating it; revisit after testnet only if a real gap appears, with a definition that avoids double penalties. No contract in `foundry/src` currently calls `recordNoParticipation` β€” the task contracts deliberately skip it where `slashPartial` already applies ("No S6 recordNoParticipation here…"), so S6 is implemented but not wired (Β§14 No. 1). ### 7.5 Jailing β€” `jailValidator(validator, duration, reason)` @@ -314,7 +314,7 @@ Until step 8, slasher management through the coordinator reverts `ValidatorStake ## 14. Review Notes & Open Caveats -- **No. 1 β€” S6 is not wired:** `recordNoParticipation` exists and is tested, but no task contract calls it, so the S6 counter never moves in practice. +- **No. 1 β€” S6 parked for testnet (intentional):** `recordNoParticipation` exists and is tested, but no task contract calls it, so the S6 counter never moves in practice. - **No. 2 β€” S5 escalation on a blacklisted validator reverts the whole slash:** escalation calls `_jailInternal`, which reverts `ValidatorIsBlacklisted`. A task contract's slashing loop hitting a blacklisted repeat offender would revert, not just skip that validator. - **No. 3 β€” Jail exit does not require `reactivate()`:** after `jailedUntil` passes, any status-syncing call (e.g. `stake`) recomputes the status, bypassing `reactivate()`'s `StakeBelowFloor` check (the later sync still requires `β‰₯ MIN_STAKE` for `Active`). - **No. 4 β€” Stale NatSpec (partly fixed, issue No. 180):** the `setModelStakeBounds` / `setMaxConcurrentRegistrationsPerStakeUnit` NatSpec now says where the task contracts enforce both (Β§8), `activeRegistrationCount` names `releaseGIRegistrationSlots` as the decrement, and `getModelStakeMin` names the `onlyOwner` (DIN-Representative) setter instead of "the model owner". Still open: `modelMinStakeBounds[].max` is never read. diff --git a/Documentation/technical/mechanisms/staking-mechanism.md b/Documentation/technical/mechanisms/staking-mechanism.md index bf38f4c5..e79d125a 100644 --- a/Documentation/technical/mechanisms/staking-mechanism.md +++ b/Documentation/technical/mechanisms/staking-mechanism.md @@ -58,7 +58,7 @@ stateDiagram-v2 | Liveness faults (S1/S2) | `slashPartial`: an audit vote that was never revealed (S1) or a T1/T2 aggregation CID that was never revealed (S2) costs `minStake Γ— s1/s2SlashFractionBps` (default 30%). Committing without revealing counts the same as not committing; whether it should cost more is open in issue #201 | | Recidivism (S5) | The same task contract recording `s5RecidivismThreshold` (3) partial slashes within `s5RecidivismWindow` (5) GIs escalates to a full `MIN_STAKE` slash plus a `s5JailDuration` (7 days) jail | | Full-severity faults | `slash(minStake)`: aggregator bad consensus, S3 audit-score deviation (behind `s3SlashingEnabled`, off by default), S4 invalid aggregation / fresh-subgroup timeout | -| No-participation (S6) | `recordNoParticipation` exists (escalating 10%-per-breach slash past a threshold of 3) but **no task contract calls it** | +| No-participation (S6) | `recordNoParticipation` exists (escalating 10%-per-breach slash past a threshold of 3) but **no task contract calls it**; parked for testnet per [#224, Oct 26 decision](https://github.com/InfiniteZeroFoundation/DevNet/issues/224#issuecomment-6406239550), because S1/S2 plus S5 cover missed work. Storage and parameters remain intact; revisit after testnet only for a demonstrated gap without double penalties | | Eligibility gates | Task contracts require `isValidatorActive()` at registration, commit/reveal and submission, and filter inactive registrants at batch creation. When configured they also enforce a per-model stake floor (`getModelStakeMin`) and a concurrent-registration cap (`(stake / minStake) Γ— maxConcurrentRegistrationsPerStakeUnit`) | | Blacklist | `blacklistValidator`/`unblacklistValidator` are direct `owner()` (DIN-Representative) actions; blacklist freezes `stake`/`unstake`/`claimUnstaked` while funds remain slashable | diff --git a/cache_model_0/abis/DINTaskAuditor.json b/cache_model_0/abis/DINTaskAuditor.json index 26bcfa5c..9a3d007a 100644 --- a/cache_model_0/abis/DINTaskAuditor.json +++ b/cache_model_0/abis/DINTaskAuditor.json @@ -17,6 +17,11 @@ "name": "modelId_", "type": "uint256", "internalType": "uint256" + }, + { + "name": "modelRegistry_", + "type": "address", + "internalType": "address" } ], "stateMutability": "nonpayable" @@ -977,6 +982,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "modelRegistry", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract ITaskModelRegistry" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "openTestDataDispute", @@ -1568,6 +1586,19 @@ "outputs": [], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "validateGIStart", + "inputs": [ + { + "name": "gi", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "view" + }, { "type": "event", "name": "AuditScoreCommitted", @@ -2483,11 +2514,6 @@ "name": "TA_EncryptedKeyCountMismatch", "inputs": [] }, - { - "type": "error", - "name": "TA_FlagAlreadySet", - "inputs": [] - }, { "type": "error", "name": "TA_FlagMustBeTrue", @@ -2543,6 +2569,22 @@ "name": "TA_MaxLMSubmissionsReached", "inputs": [] }, + { + "type": "error", + "name": "TA_ModelDisabled", + "inputs": [ + { + "name": "modelId", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "TA_ModelNotRegistered", + "inputs": [] + }, { "type": "error", "name": "TA_NoActiveDispute", @@ -2623,11 +2665,21 @@ "name": "TA_StakeBelowModelFloor", "inputs": [] }, + { + "type": "error", + "name": "TA_TestDataNotAssigned", + "inputs": [] + }, { "type": "error", "name": "TA_WrongGI", "inputs": [] + }, + { + "type": "error", + "name": "TC_GIRewardPoolNotFunded", + "inputs": [] } ], - "bytecode": "0x60a06040523461024257604051601f61558538819003918201601f19168301916001600160401b038311848410176101fb578084926060946040528339810103126102425761004d81610246565b90604061005c60208301610246565b91015191331561022f575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3604051608081016001600160401b038111828210176101fb57604090815261177082526107d060208301526105dc908201526101f4606090910152600580546001600160401b0319166701f405dc07d01770179055612710600d55602860168190556017805460ff19169055610bb860185568056bc75e2d631000009055611c206029556109c4602a556001600160a01b03168015801561021e575b61020f57600180546001600160a01b0319908116929092179055600280549091166001600160a01b039290921691909117905560805260405160c081016001600160401b038111828210176101fb5760029160a091604052600381526003602082015282604082015282606082015260326080820152015260036010556003601155600260125560026013556032601455600260155560405161532a908161025b82396080518181816135cf01526138a80152f35b634e487b7160e01b5f52604160045260245ffd5b632a86629560e21b5f5260045ffd5b506001600160a01b03821615610146565b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b51906001600160a01b03821682036102425756fe60806040526004361015610011575f80fd5b5f3560e01c8063097511f21461418c5780630e28f82b146141205780630f68a9ea146140cd5780631a54d32b1461407f578063224c19bb14613fce578063246116e614613e7657806324e168af14613dff57806333fb6a4b14613dad5780633670202714613d74578063372500ab14613cd65780633743887c14613cb95780633ae117b1146137665780633b6fbf851461371c5780633e20697c146136da5780633e35e48a14613670578063402914f51461363857806340ce01d21461361c578063426c87e9146135f257806343cdaad2146135b857806346635d0c146134eb5780634a7154ef1461346f5780634bfecdfa1461341c57806353fb6c5c146133d357806358aed7bc146133ab5780635cbc26a01461336257806362cc53c714613338578063715018a6146132e1578063773174cc146132935780637fec6535146132765780638747214f1461310257806389a29883146130d35780638da5cb5b146130ac5780638eace699146130415780639724e13814612fac57806397f03f1c14612f8f578063982231b014612f495780639ae8d14014612e4a5780639d05ab3414612d7e5780639ea8414014612d56578063a068399514612d34578063a2e26ef214612a63578063abe32a3a14612a1f578063ad5ee392146125ed578063ae169a501461238c578063b323e22f1461234a578063b658d4cd1461232d578063b8c88a2814612110578063bdd071fb14611fff578063be8768b914611fe2578063c299d8a014611e74578063c7b1389414611e4a578063c7faf422146117f6578063c8264bac146116ab578063cb23533b14611415578063cb404a7514610efe578063cc27900914610ee1578063cff0ab9614610e9a578063d358841314610e51578063d5eb897c14610cf1578063d6f353a114610cc9578063dbee7f6314610ca8578063e13b7b3114610ae6578063e4f679c414610a41578063e6f9f65314610591578063e7e948f21461054a578063f07a72b2146104dd578063f2fde38b14610458578063f76e4eaa14610437578063fabf7aa5146103a55763fc8e9edc1461031b575f80fd5b346103a15760203660031901126103a157600254600435906001600160a01b0316330361039257606481116103835760407fc15f800daae9273e961b7ab52ded9fbe213e65775b7e3ebe617f30f0c22f589d91601454908060145582519182526020820152a1005b63645bfacf60e11b5f5260045ffd5b63b701ef7360e01b5f5260045ffd5b5f80fd5b346103a1576103b3366143d3565b905f52601960205260405f2080548210156103a15760e0916103d491614505565b5060018060a01b038154169060ff6001820154916003600282015491015492604051948552602085015264ffffffffff81166040850152818160281c1615156060850152818160301c161515608085015260381c16151560a083015260c0820152f35b346103a15760203660031901126103a15761045061471e565b600435602955005b346103a15760203660031901126103a1576104716141e1565b61047961471e565b6001600160a01b031680156104ca575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b346103a15760203660031901126103a1576004358015158091036103a15760407f3f2db80a4ed2b1368e37435d4e3ed368e5aa688ee6aa6df9cba71e8d36a09de09161052761471e565b6017549060ff811660ff1983161760175560ff83519216151582526020820152a1005b346103a15760603660031901126103a1576004355f52601e60205260405f206024355f5260205260405f206044355f52602052602060ff60405f2054166040519015158152f35b346103a15760c03660031901126103a157600435602435604435906064359260843593841515948581036103a157825f52601c60205260405f2054841015610a32575f838152601d60209081526040808320878452825280832033845290915290205460ff1615610a2357825f52601e60205260405f20845f5260205260405f20855f5260205260ff60405f20541615610a1457600254604051633b0ff6bb60e01b81526001600160a01b0390911690602081600481855afa908115610976575f916109e2575b5084036109d3576020600491604051928380926374e45da960e11b82525afa908115610976575f916109a4575b50601a81101561099057600e03610981576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610976575f91610947575b50156109385760648211610929575f8381526023602090815260408083208784528252808320338452825280832088845290915290205460ff161561091a575f8381526021602090815260408083208784528252808320338452825280832088845290915290205460ff1661090b57604051602081019083825287604082015260a43560608201523360808201528460a08201528560c08201528660e082015260e08152610780610100826142c0565b5190205f84815260226020908152604080832088845282528083203384528252808320898452909152902054036108fc57827fceb4d4eba5a8d7a52a7de69669091f419fd48ad8b6aedfe9f65f4c0593074bf260406108fa98889561084e8996865f52601f602052845f20885f52602052845f2060018060a01b0333165f52602052845f20895f5260205282855f2055865f5260208052845f20885f52602052845f2060018060a01b0333165f52602052845f20895f52602052845f209060ff801983541691151516179055565b5f858152602160209081528482208883528152848220338084529082528583208a84528252858320805460ff19166001179055878352600b82528583209083529052839020805461089e9061463a565b9055845f52600a602052825f206108b5815461463a565b9055825190878252602082015285857fd419687a143d402bddbccfea67ace3394b824dc870e853cc3fc76849da2427ef853394a48151903382526020820152a4614c57565b005b63389e443b60e11b5f5260045ffd5b6304aba2b560e21b5f5260045ffd5b63b82710dd60e01b5f5260045ffd5b6357d5d01760e11b5f5260045ffd5b6335d7e52560e01b5f5260045ffd5b610969915060203d60201161096f575b61096181836142c0565b81019061455f565b876106d0565b503d610957565b6040513d5f823e3d90fd5b6369f1a16560e01b5f5260045ffd5b634e487b7160e01b5f52602160045260245ffd5b6109c6915060203d6020116109cc575b6109be81836142c0565b810190614547565b87610685565b503d6109b4565b63c8c8f12360e01b5f5260045ffd5b90506020813d602011610a0c575b816109fd602093836142c0565b810103126103a1575188610658565b3d91506109f0565b6301695e1960e11b5f5260045ffd5b6350f67e6b60e01b5f5260045ffd5b635de291e760e11b5f5260045ffd5b346103a157610a4f366143d3565b610a57614744565b815f52602760205260405f20815f5260205260405f209060ff60038301541615610ad7576002820154431115610ac8578281610ab6947fb80257bd2788328d63afd027b3e298fe6a1808ad2bb105b3e1302ad0dcc739055f80a3614f57565b5f5f5160206152d55f395f51905f525d005b630ea6b21360e01b5f5260045ffd5b63bf09dff560e01b5f5260045ffd5b346103a157610af4366143d3565b6002546001600160a01b03163381900361039257602060049160405192838092633b0ff6bb60e01b82525afa908115610976575f91610c76575b5082036109d357608061ffff7f775b575ecc1d51193be35fb0d537bc28414f551a09db15d77e74ad4391eef08292845f52600360205260405f205482610c5e610bec60405193610b7d8561426f565b610be7600554610be7612710610bde81610bd281610bc68d60208e8a1691828152019e8f8f8b60101c16905260608f604083019b818160201c168d5260301c169101528a614595565b049d8e9c511688614595565b04988994511686614595565b049788946146b2565b6146b2565b93610bf6856151a5565b600460405191610c05836142a5565b878352602083019085825260408401878152606085019182528a850192600184528d5f52600860205260405f209551865551600186015551600285015551600384015551151591019060ff801983541691151516179055565b604051938452602084015260408301526060820152a2005b90506020813d602011610ca0575b81610c91602093836142c0565b810103126103a1575183610b2e565b3d9150610c84565b346103a15760203660031901126103a157610cc161471e565b600435602855005b346103a1575f3660031901126103a1576001546040516001600160a01b039091168152602090f35b346103a15760203660031901126103a1576004355f52601960205260405f20805490610d1c82614648565b91610d2a60405193846142c0565b8083526020830180925f5260205f205f915b838310610dda578486604051918291602083019060208452518091526040830191905f5b818110610d6e575050500390f35b91935091602060e060019260c08751858060a01b038151168352848101518584015264ffffffffff604082015116604084015260608101511515606084015260808101511515608084015260a0810151151560a0840152015160c0820152019401910191849392610d60565b60046020600192604051610ded8161428a565b848060a01b038654168152848601548382015260ff600287015464ffffffffff81166040840152818160281c1615156060840152818160301c161515608084015260381c16151560a0820152600386015460c0820152815201920192019190610d3c565b346103a15760403660031901126103a157610e6a6141f7565b6004355f52601a60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346103a1575f3660031901126103a15760c06010546011546012546013546014549160155493604051958652602086015260408501526060840152608083015260a0820152f35b346103a1575f3660031901126103a1576020600654604051908152f35b346103a15760203660031901126103a157600254600435906001600160a01b03163381900361039257604051633b0ff6bb60e01b8152602081600481855afa908115610976575f916113e3575b5082036109d3576020600491604051928380926374e45da960e11b82525afa908115610976575f916113c4575b50601a811015610990576016036113b557600154604051631bad9e0560e11b81529190602090839060049082906001600160a01b03165afa918215610976575f92611381575b50612710610fce60185484614595565b0491815f52601c60205260405f205490825f52601960205260405f20925f915b83831061100057602060405160018152f35b815f52601c6020526110158360405f20614505565b50945f94876002600189019801965b885481101561136f57611037818a614532565b9054895460175460039390931b9190911c6001600160a01b0316915f918291849060ff168d8c8e865b86811061126d575b505050505050505f14611179575087878415611160575050600154604051630b6f9cd360e11b815260048101839052602481018d90526a4155445f4e4f5f564f544560a81b6044820152606481018990529350602090849060849082905f906001600160a01b03165af18015610976578789918d955f91611123575b5060606001965f5160206152b55f395f51905f52925b604051916a4155445f4e4f5f564f544560a81b835260208301526040820152a45b018890611024565b95505050506020833d8211611158575b81611140602093836142c0565b810103126103a15791518a92908890889060606110e4565b3d9150611133565b5f5160206152b55f395f51905f5260606001965f6110fa565b90925061118a575b6001915061111b565b5f91602060018060a01b0360015416606460405180968193637dd1602d60e11b83528660048401528b60248401527220aaa22fa9a1a7a922afa222ab24a0aa24a7a760691b60448401525af1928315610976578789915f95611229575b505f5160206152b55f395f51905f526060600196604051907220aaa22fa9a1a7a922afa222ab24a0aa24a7a760691b82528b60208301526040820152a4611181565b945050506020833d8211611265575b81611245602093836142c0565b810103126103a15791519187875f5160206152b55f395f51905f526111e7565b3d9150611238565b8d6112788286614532565b90549060031b1c845f52602160205260405f20845f5260205260405f20885f5260205260405f20815f5260205260ff60405f2054161561135757868061133c575b6112c8575b5050600101611060565b6112ff600391865f52601f60205260405f20865f5260205260405f208a5f5260205260405f20815f5260205260405f205493614505565b500154908181111561132e5790611315916146b2565b60165410611325575b8d5f6112be565b6001985061131e565b611337916146b2565b611315565b5060ff600261134b8385614505565b50015460301c166112b9565b5050505050505050505060018d80808d8c8e83611068565b50919650945050600190920191610fee565b9091506020813d6020116113ad575b8161139d602093836142c0565b810103126103a157519082610fbe565b3d9150611390565b633c727ea360e11b5f5260045ffd5b6113dd915060203d6020116109cc576109be81836142c0565b82610f78565b90506020813d60201161140d575b816113fe602093836142c0565b810103126103a1575183610f4b565b3d91506113f1565b346103a15760803660031901126103a15760043560243560443591606435815f52601c60205260405f2054831015610a32575f828152601d60209081526040808320868452825280832033845290915290205460ff1615610a2357815f52601e60205260405f20835f5260205260405f20845f5260205260ff60405f20541615610a1457600254604051633b0ff6bb60e01b81526001600160a01b0390911690602081600481855afa908115610976575f91611679575b5083036109d3576020600491604051928380926374e45da960e11b82525afa908115610976575f9161165a575b50601a81101561099057600d0361164b576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610976575f9161162c575b501561093857801561161d575f8281526023602090815260408083208684528252808320338452825280832087845290915290205460ff1661160e575f82815260226020908152604080832086845282528083203380855290835281842088855283528184208590558584526023835281842087855283528184208185528352818420888552835292819020805460ff19166001179055805196875290860192909252937f41e011092ee86e0527dccd7793e354593cdb6869d5049a7b20f59f76e4a9c0849190a4005b6303bbddc160e41b5f5260045ffd5b63a4bcca0960e01b5f5260045ffd5b611645915060203d60201161096f5761096181836142c0565b85611544565b6329bfa12d60e01b5f5260045ffd5b611673915060203d6020116109cc576109be81836142c0565b856114f9565b90506020813d6020116116a3575b81611694602093836142c0565b810103126103a15751866114cc565b3d9150611687565b346103a15760803660031901126103a1576116c461471e565b6127106117046116f96116ee61ffff6116db6146da565b1661ffff6116e76146eb565b16906146a5565b61ffff6116e76146fc565b61ffff6116e761470d565b036117e75761ffff6117146146da565b1660055463ffff00006117256146eb565b60101b1665ffff000000006117386146fc565b60201b169167ffff00000000000061174e61470d565b60301b169367ffff000000000000199165ffffffffffff1916171617171760055560405160043561ffff81168091036103a157815260243561ffff81168091036103a157602082015260443561ffff81168091036103a157604082015260643561ffff81168091036103a1578160809160607ffa57d62d8e4143dd5783720cdd7ad606d15892c507177d88495b8d544620a3f0940152a1005b6387c966e360e01b5f5260045ffd5b346103a15760403660031901126103a15760025460043590602435906001600160a01b03163381900361039257604051633b0ff6bb60e01b8152602081600481855afa908115610976575f91611e18575b5083036109d3576020600491604051928380926374e45da960e11b82525afa908115610976575f91611df9575b50601a81101561099057600b03611dea578015611ddb575f828152600e602052604081208054600154919290916001600160a01b031690805b838110611d5657506118be9061465f565b925f905f5b848110611cc557878787805160105411611cb65761190e60405160208101908482526720aaa22fa0a2222960c11b6040820152602881526119056048826142c0565b519020826150f5565b825f52601960205260405f2054916119258361465f565b925f5b818110611ca15750506040516020810191825266082aa88be9288b60cb1b60408201526027815261195a6047826142c0565b51902082515f198101908111611c8d57805b611c015750505f5f925f945b611984601054846146a5565b8451101580611bad575b15611b7757805f52601c60205260405f208054600160401b811015611b50576119bc91600182018155614505565b50916119c78761463a565b96835560018301965f5b601054811015611a5057600190611a046001600160a01b036119fc6119f6848b6146a5565b8b614691565b51168b6145a8565b845f52601d60205260405f2086545f5260205260405f20828060a01b03611a2e6119f6848b6146a5565b5116838060a01b03165f5260205260405f208260ff19825416179055016119d1565b509291965094926011549182611a678689516146b2565b10611b64575b600281015f5b848110611ac2575050611aba9291611ab49154897fc7303d804f645ce5daea12e226f3bab498c9c6f97beca0db122209bef4b3da8c5f80a3601054906146a5565b936146a5565b939094611978565b611ad5611acf82896146a5565b8a614691565b5190825491600160401b831015611b5057611af7836001809501865585614532565b819291549060031b91821b915f19901b19161790558a5f52601e60205260405f2084545f5260205260405f20611b36611b30838b6146a5565b8c614691565b515f5260205260405f208260ff1982541617905501611a73565b634e487b7160e01b5f52604160045260245ffd5b9150611b718487516146b2565b91611a6d565b7fcfa1c723e12abd91d3583fb69d67fbde07903c41e311030508dc89f0714cb4ae602087604051908152a2602060405160018152f35b50601154611bbb81876146a5565b83511090811591611bcd575b5061198e565b9050611bdb601554876146a5565b835110159081611bed575b5087611bc7565b611bf89150866146a5565b82511087611be6565b8351604051611c3881611c2a602082019486888791606093918352602083015260408201520190565b03601f1981018352826142c0565b519020906001810191828211611c8d57611c8792611c5591614df5565b611c5f8187614691565b5190611c76611c6e8489614691565b519188614691565b52611c818287614691565b52614de9565b8061196c565b634e487b7160e01b5f52601160045260245ffd5b8080611caf60019388614691565b5201611928565b6310ef86ed60e21b5f5260045ffd5b611ccf8183614532565b90546040516310ab556b60e21b815260039290921b1c6001600160a01b031660048201819052909190602083602481895afa8015610976576001935f91611d38575b50611d1e575b50016118c3565b611d31611d2a8661463a565b9589614691565b5289611d17565b611d50915060203d811161096f5761096181836142c0565b8b611d11565b611d608186614532565b90546040516310ab556b60e21b815260039290921b1c6001600160a01b03166004820152602081602481875afa908115610976575f91611dbd575b50611da9575b6001016118ad565b90611db560019161463a565b919050611da1565b611dd5915060203d811161096f5761096181836142c0565b88611d9b565b6370d56aed60e01b5f5260045ffd5b6315c6fd2560e11b5f5260045ffd5b611e12915060203d6020116109cc576109be81836142c0565b83611874565b90506020813d602011611e42575b81611e33602093836142c0565b810103126103a1575184611847565b3d9150611e26565b346103a15760203660031901126103a1576004355f526003602052602060405f2054604051908152f35b346103a15760803660031901126103a1576004356024356044356001600160401b0381116103a157611eaa9036906004016143e9565b9290611eb461471e565b611ebc614744565b815f52602760205260405f20835f5260205260405f2090600382019460ff86541615610ad75760028301544311610ac857611ef6816146bf565b90611f0460405192836142c0565b808252602082019236828201116103a157815f9260209286378301015251902060405160208101918483528560408301526060820152606435608082015260808152611f5160a0826142c0565b519020825f52602660205260405f20845f5260205260405f2054145f14611fd15760208160017f2d7b75e83725d13c472b5ea1bc153d9e3f52ec0e7ff57919c18741843a06ceb79301549560ff198154169055611fad86615077565b546040519586526001600160a01b031694a45f5f5160206152d55f395f51905f525d005b9190611fdd9350614f57565b610ab6565b346103a1575f3660031901126103a1576020602954604051908152f35b346103a15761200d366143d3565b9081156121015780158015612092575b6120835760045461203c908390309033906001600160a01b0316614eee565b805f52600360205260405f206120538382546146a5565b90556040519182527f4e3c672324bfa4c8193ded7cce75b1e608180241cf3d89ca45bc08101f3594b360203393a3005b63388ecdff60e21b5f5260045ffd5b50600254604051633b0ff6bb60e01b815290602090829060049082906001600160a01b03165afa908115610976575f916120cf575b50811061201d565b90506020813d6020116120f9575b816120ea602093836142c0565b810103126103a15751836120c7565b3d91506120dd565b63445512df60e01b5f5260045ffd5b346103a15761211e366143d3565b612126614744565b815f52601c60205260405f2054811015610a32575f828152601d60209081526040808320848452825280832033845290915290205460ff1615610a2357815f52600860205260ff600460405f2001541661231e57815f52602660205260405f20815f5260205260405f20541561230f57815f52602760205260405f20815f52602052600360405f20015460ff81166123005760081c60ff166122f157602854806122d1575b506121d8602954436146a5565b91602854604051906121e9826142a5565b338252602082019081526122746003604084019387855260608101946001865260808201945f8652875f52602760205260405f20895f5260205260405f209260018060a01b039060018060a01b03905116166bffffffffffffffffffffffff60a01b8454161783555160018301555160028201550192511515839060ff801983541691151516179055565b51151561ff0082549160081b169061ff0019161790556028549260405193845260208401527f65ae6dac38bbdf2d8e9903b5f7f16daa039874c6edc0ad5c65084468423f80fb60403394a45f5f5160206152d55f395f51905f525d005b6004546122eb9190309033906001600160a01b0316614eee565b826121cb565b631fa9208960e21b5f5260045ffd5b6305dd195560e11b5f5260045ffd5b6308aae87d60e21b5f5260045ffd5b630c8d37bf60e31b5f5260045ffd5b346103a1575f3660031901126103a1576020602a54604051908152f35b346103a15760203660031901126103a1576004355f52600e60205261238861237460405f206145e9565b604051918291602083526020830190614496565b0390f35b346103a15760203660031901126103a157600435805f52600860205260405f2060ff600482015416156125de575f828152600c6020908152604080832033845290915290205460ff166125cf575f828152600c6020908152604080832033808552908352818420805460ff191660011790558584526009835281842054601a8452828520918552925282205491939160ff16806125c6575b612566575b50805f52600a60205260405f205480612522575b506003820154918261247d575b505050801561246e57335f52600760205261246a60405f209182546146a5565b9055005b634855efe360e01b5f5260045ffd5b600254604051630f5985ff60e11b81526004810193909352336024840152602090839060449082906001600160a01b03165afa918215610976575f926124ee575b50811561244a57916124db6124e09260026124e696950154614595565b614577565b906146a5565b81808061244a565b9091506020813d60201161251a575b8161250a602093836142c0565b810103126103a1575190846124be565b3d91506124fd565b5f828152600b6020908152604080832033845290915290205480612547575b5061243d565b61255e9294916124db6124e0926001870154614595565b918380612541565b5f828152601960209081526040808320601b83528184203385529092529091205461259091614505565b5060ff600282015460381c166125a7575b50612429565b6125be9294506124db906003855491015490614595565b9183806125a1565b50801515612424565b637f0966f960e01b5f5260045ffd5b63187132e160e21b5f5260045ffd5b346103a15760203660031901126103a157600254600435906001600160a01b03163381900361039257604051633b0ff6bb60e01b8152602081600481855afa908115610976575f916129ed575b5082036109d3576020600491604051928380926374e45da960e11b82525afa908115610976575f916129ce575b50601a81101561099057600e036129bf57805f52601960205260405f20815f52601c60205260405f2054915f915f5b8481106126aa576020846040519015158152f35b825f94939452601c6020526126c28160405f20614505565b506002810194906001015f5b86548110156129b1576126e18188614532565b90549060031b1c85548110156129a8576126fb8187614505565b50600281019160ff835460281c1615612998575b84549161271b8361465f565b936127258461465f565b945f945f5b8181106128d857505060135485101561274e575b5050505050506001905b016126ce565b9060ff612763866127c0959697989e94614dff565b94856003845492015581660100000000000066ff0000000000001983161780855560281c16806128cc575b835467ff00000000000000191690151560381b60ff60381b1617835560301c161590816128bd575b5061289f5761463a565b60165490995f5b8581101561273e576001906001600160a01b036127e48285614691565b51168a5f52601f60205260405f208c5f5260205260405f20838060a01b0382165f5260205260405f20875f526020528b8b7f76543dcb2ee57f6ed82a780c59bc9720db389ef1c39a8fdf85e80323dfe464db60405f205488818c8c8083115f1461288d5761285590612884936146b2565b6040805192835260208301959095529381018d90526060810184905291909211608082015290819060a0820190565b0390a4016127c7565b6128849261289a916146b2565b612855565b875f52600960205260405f206128b68482546146a5565b905561463a565b60ff91505460381c168e6127b6565b5060145486101561278e565b6128e2818b614532565b60018060a01b0391549060031b1c168b5f52602160205260405f208d5f5260205260405f2060018060a01b0382165f5260205260405f20875f526020528c8c60ff60405f205416612939575b50505060010161272a565b91600193999161298f935f52601f60205260405f20905f5260205260405f20848060a01b0382165f5260205260405f20895f5260205260405f205461297e8388614691565b52612989828c614691565b5261463a565b96908c8c61292e565b6129a3818888614c57565b61270f565b50600190612748565b509394509250600101612696565b63c85f922b60e01b5f5260045ffd5b6129e7915060203d6020116109cc576109be81836142c0565b82612667565b90506020813d602011612a17575b81612a08602093836142c0565b810103126103a157518361263a565b3d91506129fb565b346103a15760403660031901126103a157612a386141f7565b6004355f52601b60205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b346103a15760403660031901126103a15760043560243560018060a01b0360025416604051633b0ff6bb60e01b8152602081600481855afa908115610976575f91612d02575b5082036109d3576020600491604051928380926374e45da960e11b82525afa908115610976575f91612ce3575b50601a81101561099057600a03612cd4575f818152601a6020908152604080832033845290915290205460ff16612cc557805f52601960205260405f2054600d541115612cb6575f8181526019602081815260408084208054601b8452828620338752845282862081905594869052929091525191929190612b578261428a565b3382526020820190858252604083019164ffffffffff4216835260608401925f8452608085015f815260a08601905f825260c08701945f86528054600160401b811015611b5057612bad91600182018155614505565b979097612ca3575187546001600160a01b039091166001600160a01b0319909116178755925160018701559051600286018054955165ff000000000090151560281b1664ffffffffff90921665ffffffffffff1990961695909517178455600393612c4892909190518254915167ffff0000000000001990921690151560301b66ff000000000000161790151560381b60ff60381b16179055565b51910155805f52601a60205260405f2060018060a01b0333165f5260205260405f20600160ff198254161790556040519283527f252ff0ddaf7e8904aaf91e8733a7cdc0fd737b41f4db319c377582046fbf549c60203394a4005b634e487b7160e01b5f525f60045260245ffd5b636cf178c360e11b5f5260045ffd5b63169ae8e160e11b5f5260045ffd5b6353d9694b60e01b5f5260045ffd5b612cfc915060203d6020116109cc576109be81836142c0565b83612ad6565b90506020813d602011612d2c575b81612d1d602093836142c0565b810103126103a1575184612aa9565b3d9150612d10565b346103a1575f3660031901126103a157602060ff601754166040519015158152f35b346103a1575f3660031901126103a1576002546040516001600160a01b039091168152602090f35b346103a15760203660031901126103a1576004355f52601960205260405f205f5f908254915b828110612e145750612db59061465f565b915f905f5b838110612dd75760405160208082528190612388908201886144d2565b8060ff6002612de860019486614505565b50015460381c16612dfa575b01612dba565b80612e0e612e078661463a565b9588614691565b52612df4565b60ff6002612e228387614505565b50015460381c16612e36575b600101612da4565b90612e4260019161463a565b919050612e2e565b346103a157612e5836614416565b94612e6794919493929361471e565b600254604051633b0ff6bb60e01b815290602090829060049082906001600160a01b03165afa908115610976575f91612f17575b5087036109d357612eac81886147fa565b90875f52602760205260405f20815f5260205260ff600360405f20015460081c1615610ad75760018201548603612f0857876108fa985f52602760205260405f20825f52602052600360405f200161ff0019815416905561483f565b630edcfc0760e41b5f5260045ffd5b90506020813d602011612f41575b81612f32602093836142c0565b810103126103a1575188612e9b565b3d9150612f25565b346103a157612f573661420d565b915f52601d60205260405f20905f5260205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346103a1575f3660031901126103a1576020602854604051908152f35b346103a15760203660031901126103a157600254604051633b0ff6bb60e01b81526004803592602091839182906001600160a01b03165afa908115610976575f9161300f575b5081116109d3575f52601c602052602060405f2054604051908152f35b90506020813d602011613039575b8161302a602093836142c0565b810103126103a1575182612ff2565b3d915061301d565b346103a15760203660031901126103a1576004355f52600860205260405f20805461238860018301549260028101549060ff6004600383015492015416916040519586958693909594919260809360a086019786526020860152604085015260608401521515910152565b346103a1575f3660031901126103a1575f546040516001600160a01b039091168152602090f35b346103a1576130e1366143d3565b905f52602660205260405f20905f52602052602060405f2054604051908152f35b346103a157613110366143d3565b600254604051633b0ff6bb60e01b815290602090829060049082906001600160a01b03165afa908115610976575f91613244575b5082116109d357815f52601c60205260405f2054811015610a3257613173915f52601c60205260405f20614505565b506040516131808161426f565b81548152613190600183016145e9565b9060208101918252600283019260405180948591602082549182815201915f5260205f20905f5b81811061322b57505050600361238894936131da61321d946131e79403896142c0565b60408501978852016142e1565b91826060820152519351945161320f6040519687968752608060208801526080870190614496565b9085820360408701526144d2565b908382036060850152614381565b82548452889450602090930192600192830192016131b7565b90506020813d60201161326e575b8161325f602093836142c0565b810103126103a1575183613144565b3d9150613252565b346103a1575f3660031901126103a1576020601854604051908152f35b346103a1576132a1366143a5565b925f92919252602260205260405f20905f5260205260405f209060018060a01b03165f5260205260405f20905f52602052602060405f2054604051908152f35b346103a1575f3660031901126103a1576132f961471e565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346103a15760203660031901126103a1576004355f526009602052602060405f2054604051908152f35b346103a15760403660031901126103a15761337b6141f7565b6004355f52600f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346103a1575f3660031901126103a1576004546040516001600160a01b039091168152602090f35b346103a15760403660031901126103a1576133ec6141f7565b6004355f52600c60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346103a15761342a366143a5565b925f92919252602360205260405f20905f5260205260405f209060018060a01b03165f5260205260405f20905f52602052602060ff60405f2054166040519015158152f35b346103a15760203660031901126103a15760043561348b61471e565b801580156134e0575b6134d15760407fabaff455f95ee810233f80ebf495dba4360e49a2a3353ca5521be34c79ffc8a491601854908060185582519182526020820152a1005b63044fb52760e01b5f5260045ffd5b506127108111613494565b346103a1576134f936614416565b9461350894919493929361471e565b600254604051633b0ff6bb60e01b815290602090829060049082906001600160a01b03165afa908115610976575f91613586575b5087036109d35761354d81886147fa565b9060018201548603612f0857875f52602760205260405f20815f5260205260ff600360405f20015460081c166122f1576108fa9761483f565b90506020813d6020116135b0575b816135a1602093836142c0565b810103126103a157518861353c565b3d9150613594565b346103a1575f3660031901126103a15760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346103a15760203660031901126103a1576004355f52600a602052602060405f2054604051908152f35b346103a1575f3660031901126103a157602060405161012c8152f35b346103a15760203660031901126103a1576001600160a01b036136596141e1565b165f526007602052602060405f2054604051908152f35b346103a15761367e366143d3565b905f52602760205260405f20905f5260205260a060405f2060ff600180841b03825416916001810154906003600282015491015491604051948552602085015260408401528181161515606084015260081c1615156080820152f35b346103a1575f3660031901126103a157608060055461ffff604051918181168352818160101c166020840152818160201c16604084015260301c166060820152f35b346103a15761372a3661420d565b915f52602560205260405f20905f5260205260405f209060018060a01b03165f52602052602061375d60405f2054614237565b15604051908152f35b346103a15760203660031901126103a157600254604051633b0ff6bb60e01b815260048035926001600160a01b03169190602090829081855afa908115610976575f91613c87575b5082036109d3576040516374e45da960e11b8152602081600481855afa908115610976575f91613c68575b50601a81101561099057600803613c59575f828152600f6020908152604080832033845290915290205460ff16613c4a576040516372be4a1f60e11b81526004810183905233602482015290602090829060449082905afa908115610976575f91613c2b575b50613c1c57805f52600e60205261012c60405f20541015613c0d576001546040516310ab556b60e21b81523360048201526001600160a01b0390911690602081602481855afa908115610976575f91613bee575b501561093857604051634ab989d560e01b81527f00000000000000000000000000000000000000000000000000000000000000006004820152602081602481855afa908115610976575f91613bbc575b508015159081613b55575b50613b465760405163f2d679bf60e01b8152602081600481855afa908115610976575f91613b14575b50806139ce575b5050805f52600e6020526139353360405f206145a8565b5f818152600f602090815260408083203384529091529020805460ff19166001908117909155546001600160a01b0316803b156103a1575f80916024604051809481936347e91db160e01b83523360048401525af18015610976576139be575b5033907f059cacf06f742fac570d6cd3760c2ffe4aebcdecd73804a6a9a4beb0eae079c85f80a3005b5f6139c8916142c0565b81613995565b6040516303d3b32360e51b8152336004820152602081602481865afa908115610976575f91613ae2575b50604051631bad9e0560e11b815290602082600481875afa918215610976575f92613aad575b50602492613a31602093613a3693614577565b614595565b926040519283809263b998328360e01b82523360048301525afa908115610976575f91613a7b575b501015613a6c57818061391e565b632530883760e11b5f5260045ffd5b90506020813d602011613aa5575b81613a96602093836142c0565b810103126103a1575183613a5e565b3d9150613a89565b91506020823d602011613ada575b81613ac8602093836142c0565b810103126103a1579051906024613a1e565b3d9150613abb565b90506020813d602011613b0c575b81613afd602093836142c0565b810103126103a15751846139f8565b3d9150613af0565b90506020813d602011613b3e575b81613b2f602093836142c0565b810103126103a1575183613917565b3d9150613b22565b63fb2002c760e01b5f5260045ffd5b6040516303d3b32360e51b8152336004820152909150602081602481865afa908115610976575f91613b8a575b5010836138ee565b90506020813d602011613bb4575b81613ba5602093836142c0565b810103126103a1575184613b82565b3d9150613b98565b90506020813d602011613be6575b81613bd7602093836142c0565b810103126103a15751836138e3565b3d9150613bca565b613c07915060203d60201161096f5761096181836142c0565b83613893565b630350bbb360e31b5f5260045ffd5b631d47600f60e11b5f5260045ffd5b613c44915060203d60201161096f5761096181836142c0565b8261383f565b6335ced03760e11b5f5260045ffd5b634ff4ea3360e11b5f5260045ffd5b613c81915060203d6020116109cc576109be81836142c0565b836137d9565b90506020813d602011613cb1575b81613ca2602093836142c0565b810103126103a15751836137ae565b3d9150613c95565b346103a1575f3660031901126103a1576020601654604051908152f35b346103a1575f3660031901126103a157613cee614744565b335f52600760205260405f20548015613d6557335f81815260076020526040812055600454613d279183916001600160a01b0316614779565b6040519081527ffc30cddea38e2bf4d6ea7d3f9ed3b6ad7f176419f4963bd81318067a4aee73fe60203392a25f5f5160206152d55f395f51905f525d005b639677fa6360e01b5f5260045ffd5b346103a15760203660031901126103a157600435613d9061471e565b6127108111613d9e57602a55005b637956bd3560e11b5f5260045ffd5b346103a157613dbb366143a5565b925f929192526020805260405f20905f5260205260405f209060018060a01b03165f5260205260405f20905f52602052602060ff60405f2054166040519015158152f35b346103a15760203660031901126103a157613e186141e1565b613e2061471e565b6001600160a01b03168015613e6757600480546001600160a01b031916821790557f3cceaedf044d3b5af0dbf9cf80be3d5a7be17bdc9ee87aa77868aa3b6ad54e805f80a2005b632a86629560e21b5f5260045ffd5b346103a15760403660031901126103a1576004356024358015801582036103a1576002546001600160a01b03163381900361039257604051633b0ff6bb60e01b8152602081600481855afa908115610976575f91613f9c575b5084036109d3576020600491604051928380926374e45da960e11b82525afa908115610976575f91613f7d575b50601a81101561099057600c03613f6e57613f5f57815f52602460205260ff60405f205416613f5057613f45915f52602460205260405f209060ff801983541691151516179055565b602060405160018152f35b630d65d35760e11b5f5260045ffd5b63ac5e0cbb60e01b5f5260045ffd5b634d818d4960e11b5f5260045ffd5b613f96915060203d6020116109cc576109be81836142c0565b84613efc565b90506020813d602011613fc6575b81613fb7602093836142c0565b810103126103a1575185613ecf565b3d9150613faa565b346103a15760203660031901126103a1576002546001600160a01b03163303610392576004355f52600e60205260405f205f5b81548110156108fa576001546001600160a01b0316906140218184614532565b905460039190911b1c6001600160a01b0316823b156103a1575f92602484926040519586938492631bf8593960e31b845260048401525af19182156109765760019261406f575b5001614001565b5f614079916142c0565b83614068565b346103a15761408d366143a5565b925f92919252601f60205260405f20905f5260205260405f209060018060a01b03165f5260205260405f20905f52602052602060405f2054604051908152f35b346103a1576140db366143a5565b925f92919252602160205260405f20905f5260205260405f209060018060a01b03165f5260205260405f20905f52602052602060ff60405f2054166040519015158152f35b346103a15760203660031901126103a15760043561413c61471e565b6064811161417d5760407f8219a9db2aac1ea83dbd262fc148ad39af63c144f8caf76b4dc5ff5d250b004c91601654908060165582519182526020820152a1005b633ca0ab7b60e01b5f5260045ffd5b346103a15761419a3661420d565b915f52602560205260405f20905f5260205260405f209060018060a01b03165f526020526123886141cd60405f206142e1565b604051918291602083526020830190614381565b600435906001600160a01b03821682036103a157565b602435906001600160a01b03821682036103a157565b60609060031901126103a15760043590602435906044356001600160a01b03811681036103a15790565b90600182811c92168015614265575b602083101461425157565b634e487b7160e01b5f52602260045260245ffd5b91607f1691614246565b608081019081106001600160401b03821117611b5057604052565b60e081019081106001600160401b03821117611b5057604052565b60a081019081106001600160401b03821117611b5057604052565b90601f801991011681019081106001600160401b03821117611b5057604052565b9060405191825f8254926142f484614237565b808452936001811690811561435f575060011461431b575b50614319925003836142c0565b565b90505f9291925260205f20905f915b818310614343575050906020614319928201015f61430c565b602091935080600191548385890101520191019091849261432a565b90506020925061431994915060ff191682840152151560051b8201015f61430c565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60809060031901126103a15760043590602435906044356001600160a01b03811681036103a1579060643590565b60409060031901126103a1576004359060243590565b9181601f840112156103a1578235916001600160401b0383116103a157602083818601950101116103a157565b9060a06003198301126103a15760043591602435916044356001600160401b0381116103a15782614449916004016143e9565b929092916064356001600160401b0381116103a157826023820112156103a1578060040135926001600160401b0384116103a15760248460051b830101116103a157602401919060843590565b90602080835192838152019201905f5b8181106144b35750505090565b82516001600160a01b03168452602093840193909201916001016144a6565b90602080835192838152019201905f5b8181106144ef5750505090565b82518452602093840193909201916001016144e2565b805482101561451e575f5260205f209060021b01905f90565b634e487b7160e01b5f52603260045260245ffd5b805482101561451e575f5260205f2001905f90565b908160209103126103a15751601a8110156103a15790565b908160209103126103a1575180151581036103a15790565b8115614581570490565b634e487b7160e01b5f52601260045260245ffd5b81810292918115918404141715611c8d57565b8054600160401b811015611b50576145c591600182018155614532565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b90604051918281549182825260208201905f5260205f20925f5b818110614618575050614319925003836142c0565b84546001600160a01b0316835260019485019487945060209093019201614603565b5f198114611c8d5760010190565b6001600160401b038111611b505760051b60200190565b9061466982614648565b61467660405191826142c0565b8281528092614687601f1991614648565b0190602036910137565b805182101561451e5760209160051b010190565b91908201809211611c8d57565b91908203918211611c8d57565b6001600160401b038111611b5057601f01601f191660200190565b60043561ffff811681036103a15790565b60243561ffff811681036103a15790565b60443561ffff811681036103a15790565b60643561ffff811681036103a15790565b5f546001600160a01b0316330361473157565b63118cdaa760e01b5f523360045260245ffd5b5f5160206152d55f395f51905f525c61476a5760015f5160206152d55f395f51905f525d565b633ee5aeb560e01b5f5260045ffd5b916040519163a9059cbb60e01b5f5260018060a01b031660045260245260205f60448180865af19060015f51148216156147d9575b604052156147b95750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b9060018115166147f157823b15153d151616906147ae565b503d5f823e3d90fd5b919091805f52601c60205260405f2054831015610a32575f52601c6020526148258260405f20614505565b509182540361483057565b637d7d0e5f60e01b5f5260045ffd5b9596919493909792600386016001600160401b038311611b50576148638154614237565b601f8111614c12575b50825f601f8211600114614bae575f91614ba3575b508360011b905f198560031b1c19161790555b5f5b88811015614b035760018054908801906001600160a01b03165f6148ba8484614532565b9054604051632942541760e11b815260039290921b1c6001600160a01b0316600482015291829060249082905afa908115610976575f91614a8a575b505115614a7b578160051b870135601e19883603018112156103a1578701908135906001600160401b0382116103a15781360360208401136103a1578a5f52602560205260405f208d5f526020526149528460405f2092614532565b60018060a01b0391549060031b1c1660018060a01b03165f5260205260405f209161497d8354614237565b601f8111614a36575b505f90601f83116001146149c9576001949392915f91836149bb575b50505f19600383901b1c191690841b1790555b01614896565b602092500101355f806149a2565b601f19831691845f5260205f20925f5b818110614a1c575091600196959492918388959310614a00575b505050811b0190556149b5565b01602001355f19600384901b60f8161c191690555f80806149f3565b9193602060018192828888010135815501950192016149d9565b835f5260205f20601f840160051c81019160208510614a71575b601f0160051c01905b818110614a665750614986565b5f8155600101614a59565b9091508190614a50565b6316f7535160e11b5f5260045ffd5b90503d805f833e614a9b81836142c0565b8101906020818303126103a1578051906001600160401b0382116103a1570181601f820112156103a157805190614ad1826146bf565b92614adf60405194856142c0565b828452602083830101116103a157815f9260208093018386015e830101525f6148f6565b507fe20a8926756681a7b5a0012bbc9d06a1cef062fc77fd91f3a3e6e68d6cc7e39a9550602094508160607f19a47fa24c43060a3e1f39e943807c130a264b31560486108d3db2ea273202859289948c9b9c96979c865f5260268a5260405f20885f528a528860405f205560405194859360408552816040860152858501375f83830185015289830152601f01601f19168101030190a3604051908152a3565b90508201355f614881565b5f8381526020812092508590601f198216905b818110614bf7575010614bde575b5050600183811b019055614894565b8301355f19600386901b60f8161c191690555f80614bcf565b86840135855560019094019360209384019388935001614bc1565b815f5260205f20601f850160051c81019160208610614c4d575b601f0160051c01905b818110614c42575061486c565b5f8155600101614c35565b9091508190614c2c565b805f52601c60205260405f2054821015610a3257805f52601c602052614c808260405f20614505565b50815f5260196020526002614c988560405f20614505565b50019060ff825460281c16614de2575f915f9160015f91018054915b828110614d2b5750505060125491828110614d2257614d156040937f43f39e4e350f276d138c2bd9e8df24df2957632fd8151c885d7a2a148c795c8695101580939065ff0000000000825491151560281b169065ff00000000001916179055565b82519182526020820152a4565b50505050505050565b614d358183614532565b90545f8981526021602090815260408083208d8452825280832060039590951b9390931c6001600160a01b03168083529381528282208d83529052205460ff16614d83575b50600101614cb4565b94614d8d9061463a565b94875f526020805260405f20895f5260205260405f209060018060a01b03165f5260205260405f20895f5260205260ff60405f205416614dce575b5f614d7a565b94614dda60019161463a565b959050614dc8565b5050505050565b8015611c8d575f190190565b8115614581570690565b908015614edf5760015b818110614e615750600180821614614e4f5760011c905f198201828111611c8d57614e4992614e3b614e429284614691565b5192614691565b51906146a5565b60011c90565b90614e5d9160011c90614691565b5190565b90614e6e82849594614691565b5192825b80151580614ec1575b15614ea9575f19810190808211611c8d57614e99614ea49288614691565b51611c818289614691565b614e72565b93614eba6001939492969586614691565b5201614e09565b505f198101818111611c8d57614ed8869188614691565b5111614e7b565b6311298f6d60e11b5f5260045ffd5b6040516323b872dd60e01b5f9081526001600160a01b039384166004529290931660245260449390935260209060648180865af19060015f5114821615614f3f575b6040525f606052156147b95750565b9060018115166147f157823b15153d15161690614f30565b909182827f8faad919cbe9703e1bfaea79ce255b90cd7fdc25d976e997f2cf26a0ba8538b460406001850154600360018060a01b03875416960161010061ffff1982541617905580615059575b835f52600860205260ff6004835f200154165f14615038575f5b80151580615023575b614ffd575b82519182526020820152a47fe725be1a9d589008aa523bb5095c4ff62c7a8e4e9269b2850724c5f3c2298ba65f80a3565b845f526003602052825f206150138282546146b2565b905561501e81615077565b614fcc565b50845f52600360205280835f20541015614fc7565b835f526003602052612710615053835f2054602a5490614595565b04614fbe565b60045461507290829088906001600160a01b0316614779565b614fa4565b80156150f2576004546001600160a01b031690600181901c823b156103a1575f8093602460405180968193630852cd8d60e31b83528660048401525af191821561097657614319936150dd936150e2575b506150d5826006546146a5565b6006556146b2565b6151a5565b5f6150ec916142c0565b5f6150c8565b50565b908151600281106151a0575f198101908111611c8d57805b61511657505050565b825160405161513f81611c2a602082019486888791606093918352602083015260408201520190565b519020906001810191828211611c8d5761519a9261515c91614df5565b6001600160a01b0361516e8287614691565b51169061518f6001600160a01b036151868589614691565b51169187614691565b52611c818286614691565b8061510d565b505050565b80156150f257600154604051639958065160e01b815290602090829060049082906001600160a01b03165afa908115610976575f91615272575b506001600160a01b0381161561521957600454615214929161520c918391906001600160a01b0316614779565b6006546146a5565b600655565b506004546001600160a01b031690813b156103a1575f8092602460405180958193630852cd8d60e31b83528660048401525af19182156109765761521492615262575b5061520c565b5f61526c916142c0565b5f61525c565b90506020813d6020116152ac575b8161528d602093836142c0565b810103126103a157516001600160a01b03811681036103a1575f6151df565b3d915061528056fe3a922abd76b9b7c1ea4bce9b738c36db4a39b390754c59a980916ffb688792c79b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220dade069f02e8701a737f25b7123ece007a1afa25fd48cb4692e5ecab7473356564736f6c634300081c0033" + "bytecode": "0x60c06040523461027157604051601f61580938819003918201601f19168301916001600160401b03831184841017610221578084926080946040528339810103126102715761004d81610275565b9061005a60208201610275565b61006b606060408401519301610275565b92331561025e575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3604051608081016001600160401b0381118282101761022157604090815261177082526107d060208301526105dc908201526101f4606090910152600580546001600160401b0319166701f405dc07d01770179055612710600d55602860168190556017805460ff19169055610bb860185568056bc75e2d63100000602755611c2090556109c46029556001600160a01b03168015801561024d575b8015610244575b61023557600180546001600160a01b0319908116929092179055600280549091166001600160a01b0392831617905560a0919091521660805260405160c081016001600160401b038111828210176102215760029160a091604052600381526003602082015282604082015282606082015260326080820152015260036010556003601155600260125560026013556032601455600260155560405161557f908161028a823960805181818161348b0152614029015260a0518181816137e70152613ac00152f35b634e487b7160e01b5f52604160045260245ffd5b632a86629560e21b5f5260045ffd5b50833b15610159565b506001600160a01b03821615610152565b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b51906001600160a01b03821682036102715756fe60c06040526004361015610011575f80fd5b5f3560e01c8063097511f2146143ec5780630e28f82b146143805780630f68a9ea1461432d5780631a54d32b146142df578063224c19bb1461422e578063246116e6146140cf57806324e168af146140585780632ba36f781461401457806333fb6a4b14613fc25780633670202714613f89578063372500ab14613eeb5780633743887c14613ece5780633ae117b11461397e5780633b6fbf85146139345780633e20697c146138f25780633e35e48a14613888578063402914f51461385057806340ce01d214613834578063426c87e91461380a57806343cdaad2146137d057806346635d0c146137035780634a7154ef146136875780634bfecdfa1461363457806353fb6c5c146135eb57806358aed7bc146135c35780635a6f53a7146134515780635cbc26a01461340857806362cc53c7146133de578063715018a614613387578063773174cc146133395780637fec65351461331c5780638747214f146131a857806389a29883146131795780638da5cb5b146131525780638eace699146130e75780639724e1381461305257806397f03f1c14613035578063982231b014612fef5780639ae8d14014612ef05780639d05ab3414612e245780639ea8414014612dfc578063a068399514612dda578063a2e26ef214612b09578063abe32a3a14612ac5578063ad5ee3921461263f578063ae169a50146123de578063b323e22f1461239c578063b658d4cd1461237f578063b8c88a2814612162578063bdd071fb14612051578063be8768b914612034578063c299d8a014611ec6578063c7b1389414611e9c578063c7faf4221461183c578063c8264bac146116f1578063cb23533b14611440578063cb404a7514610f1f578063cc27900914610f02578063cff0ab9614610ebb578063d358841314610e72578063d5eb897c14610d12578063d6f353a114610cea578063dbee7f6314610cc9578063e13b7b3114610b07578063e4f679c414610a62578063e6f9f653146105a7578063e7e948f214610560578063f07a72b2146104f3578063f2fde38b1461046e578063f76e4eaa1461044d578063fabf7aa5146103bb5763fc8e9edc14610331575f80fd5b346103b75760203660031901126103b757600254600435906001600160a01b031633036103a857606481116103995760407fc15f800daae9273e961b7ab52ded9fbe213e65775b7e3ebe617f30f0c22f589d91601454908060145582519182526020820152a1005b63645bfacf60e11b5f5260045ffd5b63b701ef7360e01b5f5260045ffd5b5f80fd5b346103b7576103c936614633565b905f52601960205260405f2080548210156103b75760e0916103ea91614765565b5060018060a01b038154169060ff6001820154916003600282015491015492604051948552602085015264ffffffffff81166040850152818160281c1615156060850152818160301c161515608085015260381c16151560a083015260c0820152f35b346103b75760203660031901126103b757610466614973565b600435602855005b346103b75760203660031901126103b757610487614441565b61048f614973565b6001600160a01b031680156104e0575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b346103b75760203660031901126103b7576004358015158091036103b75760407f3f2db80a4ed2b1368e37435d4e3ed368e5aa688ee6aa6df9cba71e8d36a09de09161053d614973565b6017549060ff811660ff1983161760175560ff83519216151582526020820152a1005b346103b75760603660031901126103b7576004355f52601e60205260405f206024355f5260205260405f206044355f52602052602060ff60405f2054166040519015158152f35b346103b75760c03660031901126103b757600435602435604435906064359260843593841515948581036103b757825f52601c60205260405f2054841015610a53575f838152601d60209081526040808320878452825280832033845290915290205460ff1615610a4457825f52601e60205260405f20845f5260205260405f20855f5260205260ff60405f20541615610a3557600254604051633b0ff6bb60e01b81526001600160a01b0390911690602081600481855afa908115610997575f91610a03575b5084036109f4576020600491604051928380926374e45da960e11b82525afa908115610997575f916109c5575b50601d8110156109b1576010036109a2576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610997575f9161095d575b501561094e576064821161093f575f8381526023602090815260408083208784528252808320338452825280832088845290915290205460ff1615610930575f8381526021602090815260408083208784528252808320338452825280832088845290915290205460ff1661092157604051602081019083825287604082015260a43560608201523360808201528460a08201528560c08201528660e082015260e0815261079661010082614520565b5190205f848152602260209081526040808320888452825280832033845282528083208984529091529020540361091257827fceb4d4eba5a8d7a52a7de69669091f419fd48ad8b6aedfe9f65f4c0593074bf260406109109888956108648996865f52601f602052845f20885f52602052845f2060018060a01b0333165f52602052845f20895f5260205282855f2055865f5260208052845f20885f52602052845f2060018060a01b0333165f52602052845f20895f52602052845f209060ff801983541691151516179055565b5f858152602160209081528482208883528152848220338084529082528583208a84528252858320805460ff19166001179055878352600b8252858320908352905283902080546108b49061488f565b9055845f52600a602052825f206108cb815461488f565b9055825190878252602082015285857fd419687a143d402bddbccfea67ace3394b824dc870e853cc3fc76849da2427ef853394a48151903382526020820152a4614eac565b005b63389e443b60e11b5f5260045ffd5b6304aba2b560e21b5f5260045ffd5b63b82710dd60e01b5f5260045ffd5b6357d5d01760e11b5f5260045ffd5b6335d7e52560e01b5f5260045ffd5b90506020813d60201161098f575b8161097860209383614520565b810103126103b757610989906147bf565b876106e6565b3d915061096b565b6040513d5f823e3d90fd5b6369f1a16560e01b5f5260045ffd5b634e487b7160e01b5f52602160045260245ffd5b6109e7915060203d6020116109ed575b6109df8183614520565b8101906147a7565b8761069b565b503d6109d5565b63c8c8f12360e01b5f5260045ffd5b90506020813d602011610a2d575b81610a1e60209383614520565b810103126103b757518861066e565b3d9150610a11565b6301695e1960e11b5f5260045ffd5b6350f67e6b60e01b5f5260045ffd5b635de291e760e11b5f5260045ffd5b346103b757610a7036614633565b610a78614999565b815f52602660205260405f20815f5260205260405f209060ff60038301541615610af8576002820154431115610ae9578281610ad7947fb80257bd2788328d63afd027b3e298fe6a1808ad2bb105b3e1302ad0dcc739055f80a36151ac565b5f5f51602061552a5f395f51905f525d005b630ea6b21360e01b5f5260045ffd5b63bf09dff560e01b5f5260045ffd5b346103b757610b1536614633565b6002546001600160a01b0316338190036103a857602060049160405192838092633b0ff6bb60e01b82525afa908115610997575f91610c97575b5082036109f457608061ffff7f775b575ecc1d51193be35fb0d537bc28414f551a09db15d77e74ad4391eef08292845f52600360205260405f205482610c7f610c0d60405193610b9e856144cf565b610c08600554610c08612710610bff81610bf381610be78d60208e8a1691828152019e8f8f8b60101c16905260608f604083019b818160201c168d5260301c169101528a6147ea565b049d8e9c5116886147ea565b049889945116866147ea565b04978894614907565b614907565b93610c17856153fa565b600460405191610c2683614505565b878352602083019085825260408401878152606085019182528a850192600184528d5f52600860205260405f209551865551600186015551600285015551600384015551151591019060ff801983541691151516179055565b604051938452602084015260408301526060820152a2005b90506020813d602011610cc1575b81610cb260209383614520565b810103126103b7575183610b4f565b3d9150610ca5565b346103b75760203660031901126103b757610ce2614973565b600435602755005b346103b7575f3660031901126103b7576001546040516001600160a01b039091168152602090f35b346103b75760203660031901126103b7576004355f52601960205260405f20805490610d3d8261489d565b91610d4b6040519384614520565b8083526020830180925f5260205f205f915b838310610dfb578486604051918291602083019060208452518091526040830191905f5b818110610d8f575050500390f35b91935091602060e060019260c08751858060a01b038151168352848101518584015264ffffffffff604082015116604084015260608101511515606084015260808101511515608084015260a0810151151560a0840152015160c0820152019401910191849392610d81565b60046020600192604051610e0e816144ea565b848060a01b038654168152848601548382015260ff600287015464ffffffffff81166040840152818160281c1615156060840152818160301c161515608084015260381c16151560a0820152600386015460c0820152815201920192019190610d5d565b346103b75760403660031901126103b757610e8b614457565b6004355f52601a60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346103b7575f3660031901126103b75760c06010546011546012546013546014549160155493604051958652602086015260408501526060840152608083015260a0820152f35b346103b7575f3660031901126103b7576020600654604051908152f35b346103b75760203660031901126103b757600254600435906001600160a01b0316338190036103a857604051633b0ff6bb60e01b8152602081600481855afa908115610997575f9161140e575b5082036109f4576020600491604051928380926374e45da960e11b82525afa908115610997575f916113ef575b50601d8110156109b1576019036113e057600154604051631bad9e0560e11b81529190602090839060049082906001600160a01b03165afa918215610997575f926113ac575b50612710610fef601854846147ea565b0491815f52601c60205260405f205491805f52601960205260405f20905f5b84811061102057602060405160018152f35b815f52601c6020526110358160405f20614765565b5060018101600281549201908154905f5b84811061105a57505050505060010161100e565b8a6110658284614792565b905460175460039290921b1c6001600160a01b031692905f908190859060ff16828e8e8e5b8d8d85106112a3575b505050505050505f146111a85750811561118c57602089608460018060a01b0360015416945f6040519687948593630b6f9cd360e11b85528a600486015260248501526a4155445f4e4f5f564f544560a81b604485015260648401525af192831561099757888a8f945f96611143575b505f51602061550a5f395f51905f526060600196975b604051906a4155445f4e4f5f564f544560a81b82528960208301526040820152a45b019050611046565b9450505092506020823d8211611184575b8161116160209383614520565b810103126103b7579051918c9190888a5f51602061550a5f395f51905f52611103565b3d9150611154565b60019192888a5f51602061550a5f395f51905f5260605f611119565b929091926111ba575b5060019061113b565b91505f90602060018060a01b036001541660648d6040519586938492637dd1602d60e11b845289600485015260248401527220aaa22fa9a1a7a922afa222ab24a0aa24a7a760691b60448401525af192831561099757888a8f94958e965f91611262575b506060600196975f51602061550a5f395f51905f5292604051917220aaa22fa9a1a7a922afa222ab24a0aa24a7a760691b835260208301526040820152a4906111b1565b955050505092506020823d821161129b575b8161128160209383614520565b810103126103b75790518a928d929189908b90606061121e565b3d9150611274565b846112ad91614792565b90549060031b1c91805f52602160205260405f20825f5260205260405f20875f5260205260405f20835f5260205260ff60405f2054161561139657858061137b575b611303575b505050506001018e8e8e61108a565b9161133b916003935f52601f60205260405f20905f5260205260405f20875f5260205260405f20815f5260205260405f205493614765565b500154908181111561136d579061135191614907565b60165410611364575b8e5f8f8f906112f4565b6001945061135a565b61137691614907565b611351565b5060ff600261138a8587614765565b50015460301c166112ef565b505050505050505060018f80808e8e8e8d611093565b9091506020813d6020116113d8575b816113c860209383614520565b810103126103b757519082610fdf565b3d91506113bb565b633c727ea360e11b5f5260045ffd5b611408915060203d6020116109ed576109df8183614520565b82610f99565b90506020813d602011611438575b8161142960209383614520565b810103126103b7575183610f6c565b3d915061141c565b346103b75760803660031901126103b75760043560243560443591606435815f52601c60205260405f2054831015610a53575f828152601d60209081526040808320868452825280832033845290915290205460ff1615610a4457815f52601e60205260405f20835f5260205260405f20845f5260205260ff60405f20541615610a3557600254604051633b0ff6bb60e01b81526001600160a01b0390911690602081600481855afa908115610997575f916116bf575b5083036109f4576020600491604051928380926374e45da960e11b82525afa908115610997575f916116a0575b50601d8110156109b157600f03611691576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610997575f91611657575b501561094e578015611648575f8281526023602090815260408083208684528252808320338452825280832087845290915290205460ff16611639575f82815260226020908152604080832086845282528083203380855290835281842088855283528184208590558584526023835281842087855283528184208185528352818420888552835292819020805460ff19166001179055805196875290860192909252937f41e011092ee86e0527dccd7793e354593cdb6869d5049a7b20f59f76e4a9c0849190a4005b6303bbddc160e41b5f5260045ffd5b63a4bcca0960e01b5f5260045ffd5b90506020813d602011611689575b8161167260209383614520565b810103126103b757611683906147bf565b8561156f565b3d9150611665565b6329bfa12d60e01b5f5260045ffd5b6116b9915060203d6020116109ed576109df8183614520565b85611524565b90506020813d6020116116e9575b816116da60209383614520565b810103126103b75751866114f7565b3d91506116cd565b346103b75760803660031901126103b75761170a614973565b61271061174a61173f61173461ffff61172161492f565b1661ffff61172d614940565b16906148fa565b61ffff61172d614951565b61ffff61172d614962565b0361182d5761ffff61175a61492f565b1660055463ffff000061176b614940565b60101b1665ffff0000000061177e614951565b60201b169167ffff000000000000611794614962565b60301b169367ffff000000000000199165ffffffffffff1916171617171760055560405160043561ffff81168091036103b757815260243561ffff81168091036103b757602082015260443561ffff81168091036103b757604082015260643561ffff81168091036103b7578160809160607ffa57d62d8e4143dd5783720cdd7ad606d15892c507177d88495b8d544620a3f0940152a1005b6387c966e360e01b5f5260045ffd5b346103b75760403660031901126103b75760025460043590602435906001600160a01b0316338190036103a857604051633b0ff6bb60e01b8152602081600481855afa908115610997575f91611e6a575b5083036109f4576020600491604051928380926374e45da960e11b82525afa908115610997575f91611e4b575b50601d8110156109b157600c03611e3c578015611e2d57815f52600e60205260405f2080546118e88161489d565b6118f56040519182614520565b818152601f196119048361489d565b013660208301376001545f906001600160a01b0316815b848110611d7e57505061192d906148b4565b925f915f5b848110611d3457878787805160105411611d255761197d60405160208101908482526720aaa22fa0a2222960c11b604082015260288152611974604882614520565b5190208261534a565b825f52601960205260405f205491611994836148b4565b925f5b818110611d105750506040516020810191825266082aa88be9288b60cb1b6040820152602781526119c9604782614520565b51902082515f198101908111611cfc57805b611c705750505f5f925f945b6119f3601054846148fa565b8451101580611c1c575b15611be657805f52601c60205260405f208054600160401b811015611bbf57611a2b91600182018155614765565b5091611a368761488f565b96835560018301965f5b601054811015611abf57600190611a736001600160a01b03611a6b611a65848b6148fa565b8b6148e6565b51168b6147fd565b845f52601d60205260405f2086545f5260205260405f20828060a01b03611a9d611a65848b6148fa565b5116838060a01b03165f5260205260405f208260ff1982541617905501611a40565b509291965094926011549182611ad6868951614907565b10611bd3575b600281015f5b848110611b31575050611b299291611b239154897fc7303d804f645ce5daea12e226f3bab498c9c6f97beca0db122209bef4b3da8c5f80a3601054906148fa565b936148fa565b9390946119e7565b611b44611b3e82896148fa565b8a6148e6565b5190825491600160401b831015611bbf57611b66836001809501865585614792565b819291549060031b91821b915f19901b19161790558a5f52601e60205260405f2084545f5260205260405f20611ba5611b9f838b6148fa565b8c6148e6565b515f5260205260405f208260ff1982541617905501611ae2565b634e487b7160e01b5f52604160045260245ffd5b9150611be0848751614907565b91611adc565b7fcfa1c723e12abd91d3583fb69d67fbde07903c41e311030508dc89f0714cb4ae602087604051908152a2602060405160018152f35b50601154611c2a81876148fa565b83511090811591611c3c575b506119fd565b9050611c4a601554876148fa565b835110159081611c5c575b5087611c36565b611c679150866148fa565b82511087611c55565b8351604051611ca781611c99602082019486888791606093918352602083015260408201520190565b03601f198101835282614520565b519020906001810191828211611cfc57611cf692611cc49161504a565b611cce81876148e6565b5190611ce5611cdd84896148e6565b5191886148e6565b52611cf082876148e6565b5261503e565b806119db565b634e487b7160e01b5f52601160045260245ffd5b8080611d1e600193886148e6565b5201611997565b6310ef86ed60e21b5f5260045ffd5b80611d41600192846148e6565b51611d4d575b01611932565b611d578185614792565b838060a01b0391549060031b1c16611d78611d718761488f565b96896148e6565b52611d47565b611d888187614792565b90546040516310ab556b60e21b815260039290921b1c6001600160a01b03166004820152602081602481865afa908115610997575f91611df4575b50611dce82866148e6565b8115159052611de0575b60010161191b565b91611dec60019161488f565b929050611dd8565b90506020813d8211611e25575b81611e0e60209383614520565b810103126103b757611e1f906147bf565b89611dc3565b3d9150611e01565b6370d56aed60e01b5f5260045ffd5b6315c6fd2560e11b5f5260045ffd5b611e64915060203d6020116109ed576109df8183614520565b836118ba565b90506020813d602011611e94575b81611e8560209383614520565b810103126103b757518461188d565b3d9150611e78565b346103b75760203660031901126103b7576004355f526003602052602060405f2054604051908152f35b346103b75760803660031901126103b7576004356024356044356001600160401b0381116103b757611efc903690600401614649565b9290611f06614973565b611f0e614999565b815f52602660205260405f20835f5260205260405f2090600382019460ff86541615610af85760028301544311610ae957611f4881614914565b90611f566040519283614520565b808252602082019236828201116103b757815f9260209286378301015251902060405160208101918483528560408301526060820152606435608082015260808152611fa360a082614520565b519020825f52602560205260405f20845f5260205260405f2054145f146120235760208160017f2d7b75e83725d13c472b5ea1bc153d9e3f52ec0e7ff57919c18741843a06ceb79301549560ff198154169055611fff866152cc565b546040519586526001600160a01b031694a45f5f51602061552a5f395f51905f525d005b919061202f93506151ac565b610ad7565b346103b7575f3660031901126103b7576020602854604051908152f35b346103b75761205f36614633565b90811561215357801580156120e4575b6120d55760045461208e908390309033906001600160a01b0316615143565b805f52600360205260405f206120a58382546148fa565b90556040519182527f4e3c672324bfa4c8193ded7cce75b1e608180241cf3d89ca45bc08101f3594b360203393a3005b63388ecdff60e21b5f5260045ffd5b50600254604051633b0ff6bb60e01b815290602090829060049082906001600160a01b03165afa908115610997575f91612121575b50811061206f565b90506020813d60201161214b575b8161213c60209383614520565b810103126103b7575183612119565b3d915061212f565b63445512df60e01b5f5260045ffd5b346103b75761217036614633565b612178614999565b815f52601c60205260405f2054811015610a53575f828152601d60209081526040808320848452825280832033845290915290205460ff1615610a4457815f52600860205260ff600460405f2001541661237057815f52602560205260405f20815f5260205260405f20541561236157815f52602660205260405f20815f52602052600360405f20015460ff81166123525760081c60ff166123435760275480612323575b5061222a602854436148fa565b916027546040519061223b82614505565b338252602082019081526122c66003604084019387855260608101946001865260808201945f8652875f52602660205260405f20895f5260205260405f209260018060a01b039060018060a01b03905116166bffffffffffffffffffffffff60a01b8454161783555160018301555160028201550192511515839060ff801983541691151516179055565b51151561ff0082549160081b169061ff0019161790556027549260405193845260208401527f65ae6dac38bbdf2d8e9903b5f7f16daa039874c6edc0ad5c65084468423f80fb60403394a45f5f51602061552a5f395f51905f525d005b60045461233d9190309033906001600160a01b0316615143565b8261221d565b631fa9208960e21b5f5260045ffd5b6305dd195560e11b5f5260045ffd5b6308aae87d60e21b5f5260045ffd5b630c8d37bf60e31b5f5260045ffd5b346103b7575f3660031901126103b7576020602954604051908152f35b346103b75760203660031901126103b7576004355f52600e6020526123da6123c660405f2061483e565b6040519182916020835260208301906146f6565b0390f35b346103b75760203660031901126103b757600435805f52600860205260405f2060ff60048201541615612630575f828152600c6020908152604080832033845290915290205460ff16612621575f828152600c6020908152604080832033808552908352818420805460ff191660011790558584526009835281842054601a8452828520918552925282205491939160ff1680612618575b6125b8575b50805f52600a60205260405f205480612574575b50600382015491826124cf575b50505080156124c057335f5260076020526124bc60405f209182546148fa565b9055005b634855efe360e01b5f5260045ffd5b600254604051630f5985ff60e11b81526004810193909352336024840152602090839060449082906001600160a01b03165afa918215610997575f92612540575b50811561249c579161252d612532926002612538969501546147ea565b6147cc565b906148fa565b81808061249c565b9091506020813d60201161256c575b8161255c60209383614520565b810103126103b757519084612510565b3d915061254f565b5f828152600b6020908152604080832033845290915290205480612599575b5061248f565b6125b092949161252d6125329260018701546147ea565b918380612593565b5f828152601960209081526040808320601b8352818420338552909252909120546125e291614765565b5060ff600282015460381c166125f9575b5061247b565b61261092945061252d9060038554910154906147ea565b9183806125f3565b50801515612476565b637f0966f960e01b5f5260045ffd5b63187132e160e21b5f5260045ffd5b346103b75760203660031901126103b7576002546001600160a01b0316338190036103a857604051633b0ff6bb60e01b8152602081600481855afa908115610997575f91612a93575b50600435036109f4576020600491604051928380926374e45da960e11b82525afa908115610997575f91612a74575b50601d8110156109b157601003612a65576004355f908152601960209081526040808320601c909252822054608052815b60805181106126fe576020836040519015158152f35b6004355f52601c6020526127158160405f20614765565b506002810154906001810154905f5b83811061273757505050506001016126e8565b958661274d600198600285999697989901614792565b90549060031b1c8454811015612a5d576127678186614765565b509060ff600283015460281c1615612a4b575b612783876148b4565b9061278d886148b4565b925f925f5b8a811061298a57506013548410156127bb575b505050505087905b019096509493929194612724565b906127cd8461283d9495969893615054565b60a05260ff600282015460a051600384015581660100000000000066ff0000000000001983161780600286015560281c168061297c575b60028401805467ff00000000000000191691151560381b60ff60381b1691909117905560301c1615908161296a575b506129485761488f565b60165490945f5b848110156127a5578c90818060a01b0361285e82856148e6565b51166004355f52601f6020528d60405f20905f5260205260405f20838060a01b0382165f5260205260405f20865f526020528d60405f205460a05181115f14612912577f76543dcb2ee57f6ed82a780c59bc9720db389ef1c39a8fdf85e80323dfe464db6129096128d160a05184614907565b925b6040805160a080518e835260208301949094529181019290925260608201859052938a1060808201526004359390918291820190565b0390a401612844565b7f76543dcb2ee57f6ed82a780c59bc9720db389ef1c39a8fdf85e80323dfe464db6129096129428360a051614907565b926128d3565b6004355f52600960205260405f2061296360a05182546148fa565b905561488f565b60ff91506002015460381c168d612833565b5060145460a0511015612804565b612996818f8b01614792565b8f8060a01b0391549060031b1c166004355f5260216020528d60405f20905f5260205260405f208f8060a01b0382165f5260205260405f20855f526020528d60ff60405f2054166129eb575b50508d01612792565b958f929691612a43926004355f52601f60205260405f20905f5260205260405f20848060a01b0382165f5260205260405f20875f5260205260405f2054612a3283876148e6565b52612a3d828a6148e6565b5261488f565b94908d6129e2565b612a58818a600435614eac565b61277a565b5087906127ad565b63c85f922b60e01b5f5260045ffd5b612a8d915060203d6020116109ed576109df8183614520565b816126b7565b90506020813d602011612abd575b81612aae60209383614520565b810103126103b7575182612688565b3d9150612aa1565b346103b75760403660031901126103b757612ade614457565b6004355f52601b60205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b346103b75760403660031901126103b75760043560243560018060a01b0360025416604051633b0ff6bb60e01b8152602081600481855afa908115610997575f91612da8575b5082036109f4576020600491604051928380926374e45da960e11b82525afa908115610997575f91612d89575b50601d8110156109b157600a03612d7a575f818152601a6020908152604080832033845290915290205460ff16612d6b57805f52601960205260405f2054600d541115612d5c575f8181526019602081815260408084208054601b8452828620338752845282862081905594869052929091525191929190612bfd826144ea565b3382526020820190858252604083019164ffffffffff4216835260608401925f8452608085015f815260a08601905f825260c08701945f86528054600160401b811015611bbf57612c5391600182018155614765565b979097612d49575187546001600160a01b039091166001600160a01b0319909116178755925160018701559051600286018054955165ff000000000090151560281b1664ffffffffff90921665ffffffffffff1990961695909517178455600393612cee92909190518254915167ffff0000000000001990921690151560301b66ff000000000000161790151560381b60ff60381b16179055565b51910155805f52601a60205260405f2060018060a01b0333165f5260205260405f20600160ff198254161790556040519283527f252ff0ddaf7e8904aaf91e8733a7cdc0fd737b41f4db319c377582046fbf549c60203394a4005b634e487b7160e01b5f525f60045260245ffd5b636cf178c360e11b5f5260045ffd5b63169ae8e160e11b5f5260045ffd5b6353d9694b60e01b5f5260045ffd5b612da2915060203d6020116109ed576109df8183614520565b83612b7c565b90506020813d602011612dd2575b81612dc360209383614520565b810103126103b7575184612b4f565b3d9150612db6565b346103b7575f3660031901126103b757602060ff601754166040519015158152f35b346103b7575f3660031901126103b7576002546040516001600160a01b039091168152602090f35b346103b75760203660031901126103b7576004355f52601960205260405f205f5f908254915b828110612eba5750612e5b906148b4565b915f905f5b838110612e7d57604051602080825281906123da90820188614732565b8060ff6002612e8e60019486614765565b50015460381c16612ea0575b01612e60565b80612eb4612ead8661488f565b95886148e6565b52612e9a565b60ff6002612ec88387614765565b50015460381c16612edc575b600101612e4a565b90612ee860019161488f565b919050612ed4565b346103b757612efe36614676565b94612f0d949194939293614973565b600254604051633b0ff6bb60e01b815290602090829060049082906001600160a01b03165afa908115610997575f91612fbd575b5087036109f457612f528188614a4f565b90875f52602660205260405f20815f5260205260ff600360405f20015460081c1615610af85760018201548603612fae5787610910985f52602660205260405f20825f52602052600360405f200161ff00198154169055614a94565b630edcfc0760e41b5f5260045ffd5b90506020813d602011612fe7575b81612fd860209383614520565b810103126103b7575188612f41565b3d9150612fcb565b346103b757612ffd3661446d565b915f52601d60205260405f20905f5260205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346103b7575f3660031901126103b7576020602754604051908152f35b346103b75760203660031901126103b757600254604051633b0ff6bb60e01b81526004803592602091839182906001600160a01b03165afa908115610997575f916130b5575b5081116109f4575f52601c602052602060405f2054604051908152f35b90506020813d6020116130df575b816130d060209383614520565b810103126103b7575182613098565b3d91506130c3565b346103b75760203660031901126103b7576004355f52600860205260405f2080546123da60018301549260028101549060ff6004600383015492015416916040519586958693909594919260809360a086019786526020860152604085015260608401521515910152565b346103b7575f3660031901126103b7575f546040516001600160a01b039091168152602090f35b346103b75761318736614633565b905f52602560205260405f20905f52602052602060405f2054604051908152f35b346103b7576131b636614633565b600254604051633b0ff6bb60e01b815290602090829060049082906001600160a01b03165afa908115610997575f916132ea575b5082116109f457815f52601c60205260405f2054811015610a5357613219915f52601c60205260405f20614765565b50604051613226816144cf565b815481526132366001830161483e565b9060208101918252600283019260405180948591602082549182815201915f5260205f20905f5b8181106132d15750505060036123da94936132806132c39461328d940389614520565b6040850197885201614541565b9182606082015251935194516132b560405196879687526080602088015260808701906146f6565b908582036040870152614732565b9083820360608501526145e1565b825484528894506020909301926001928301920161325d565b90506020813d602011613314575b8161330560209383614520565b810103126103b75751836131ea565b3d91506132f8565b346103b7575f3660031901126103b7576020601854604051908152f35b346103b75761334736614605565b925f92919252602260205260405f20905f5260205260405f209060018060a01b03165f5260205260405f20905f52602052602060405f2054604051908152f35b346103b7575f3660031901126103b75761339f614973565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346103b75760203660031901126103b7576004355f526009602052602060405f2054604051908152f35b346103b75760403660031901126103b757613421614457565b6004355f52600f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346103b75760203660031901126103b7576002546001600160a01b031633036103a85760408051638ab84b0560e01b8152306004820152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169082602481845afa918215610997575f905f93613582575b501561357357602060249160405192838092630813d4c560e11b82528660048301525afa908115610997575f91613539575b50613527576004355f52600360205260405f20541561351857005b6333b8921f60e21b5f5260045ffd5b6304db93cb60e21b5f5260045260245ffd5b90506020813d60201161356b575b8161355460209383614520565b810103126103b757613565906147bf565b826134fd565b3d9150613547565b6324fe126160e01b5f5260045ffd5b9250506040823d6040116135bb575b8161359e60409383614520565b810103126103b75760206135b1836147bf565b92015191836134cb565b3d9150613591565b346103b7575f3660031901126103b7576004546040516001600160a01b039091168152602090f35b346103b75760403660031901126103b757613604614457565b6004355f52600c60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346103b75761364236614605565b925f92919252602360205260405f20905f5260205260405f209060018060a01b03165f5260205260405f20905f52602052602060ff60405f2054166040519015158152f35b346103b75760203660031901126103b7576004356136a3614973565b801580156136f8575b6136e95760407fabaff455f95ee810233f80ebf495dba4360e49a2a3353ca5521be34c79ffc8a491601854908060185582519182526020820152a1005b63044fb52760e01b5f5260045ffd5b5061271081116136ac565b346103b75761371136614676565b94613720949194939293614973565b600254604051633b0ff6bb60e01b815290602090829060049082906001600160a01b03165afa908115610997575f9161379e575b5087036109f4576137658188614a4f565b9060018201548603612fae57875f52602660205260405f20815f5260205260ff600360405f20015460081c166123435761091097614a94565b90506020813d6020116137c8575b816137b960209383614520565b810103126103b7575188613754565b3d91506137ac565b346103b7575f3660031901126103b75760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346103b75760203660031901126103b7576004355f52600a602052602060405f2054604051908152f35b346103b7575f3660031901126103b757602060405161012c8152f35b346103b75760203660031901126103b7576001600160a01b03613871614441565b165f526007602052602060405f2054604051908152f35b346103b75761389636614633565b905f52602660205260405f20905f5260205260a060405f2060ff600180841b03825416916001810154906003600282015491015491604051948552602085015260408401528181161515606084015260081c1615156080820152f35b346103b7575f3660031901126103b757608060055461ffff604051918181168352818160101c166020840152818160201c16604084015260301c166060820152f35b346103b7576139423661446d565b915f52602460205260405f20905f5260205260405f209060018060a01b03165f52602052602061397560405f2054614497565b15604051908152f35b346103b75760203660031901126103b757600254604051633b0ff6bb60e01b815260048035926001600160a01b03169190602090829081855afa908115610997575f91613e9c575b5082036109f4576040516374e45da960e11b8152602081600481855afa908115610997575f91613e7d575b50601d8110156109b157600803613e6e575f828152600f6020908152604080832033845290915290205460ff16613e5f576040516372be4a1f60e11b81526004810183905233602482015290602090829060449082905afa908115610997575f91613e25575b50613e1657805f52600e60205261012c60405f20541015613e07576001546040516310ab556b60e21b81523360048201526001600160a01b0390911690602081602481855afa908115610997575f91613dcd575b501561094e57604051634ab989d560e01b81527f00000000000000000000000000000000000000000000000000000000000000006004820152602081602481855afa908115610997575f91613d9b575b5060405163f2d679bf60e01b815290602082600481865afa918215610997575f92613d67575b50801580158091613d5e575b613be4575b50505050805f52600e602052613b4b3360405f206147fd565b5f818152600f602090815260408083203384529091529020805460ff19166001908117909155546001600160a01b0316803b156103b7575f80916024604051809481936347e91db160e01b83523360048401525af1801561099757613bd4575b5033907f059cacf06f742fac570d6cd3760c2ffe4aebcdecd73804a6a9a4beb0eae079c85f80a3005b5f613bde91614520565b81613bab565b6040516303d3b32360e51b815233600482015291602083602481885afa928315610997575f93613d2a575b5081613d20575b50613d115781613c27575b80613b32565b604051631bad9e0560e11b815290602082600481875afa918215610997575f92613cdc575b50602492613c5f602093613c64936147cc565b6147ea565b926040519283809263b998328360e01b82523360048301525afa908115610997575f91613caa575b501015613c9b57818080613c21565b632530883760e11b5f5260045ffd5b90506020813d602011613cd4575b81613cc560209383614520565b810103126103b7575183613c8c565b3d9150613cb8565b91506020823d602011613d09575b81613cf760209383614520565b810103126103b7579051906024613c4c565b3d9150613cea565b63fb2002c760e01b5f5260045ffd5b9050811085613c16565b9092506020813d602011613d56575b81613d4660209383614520565b810103126103b757519186613c0f565b3d9150613d39565b50821515613b2d565b9091506020813d602011613d93575b81613d8360209383614520565b810103126103b757519084613b21565b3d9150613d76565b90506020813d602011613dc5575b81613db660209383614520565b810103126103b7575183613afb565b3d9150613da9565b90506020813d602011613dff575b81613de860209383614520565b810103126103b757613df9906147bf565b83613aab565b3d9150613ddb565b630350bbb360e31b5f5260045ffd5b631d47600f60e11b5f5260045ffd5b90506020813d602011613e57575b81613e4060209383614520565b810103126103b757613e51906147bf565b82613a57565b3d9150613e33565b6335ced03760e11b5f5260045ffd5b634ff4ea3360e11b5f5260045ffd5b613e96915060203d6020116109ed576109df8183614520565b836139f1565b90506020813d602011613ec6575b81613eb760209383614520565b810103126103b75751836139c6565b3d9150613eaa565b346103b7575f3660031901126103b7576020601654604051908152f35b346103b7575f3660031901126103b757613f03614999565b335f52600760205260405f20548015613f7a57335f81815260076020526040812055600454613f3c9183916001600160a01b03166149ce565b6040519081527ffc30cddea38e2bf4d6ea7d3f9ed3b6ad7f176419f4963bd81318067a4aee73fe60203392a25f5f51602061552a5f395f51905f525d005b639677fa6360e01b5f5260045ffd5b346103b75760203660031901126103b757600435613fa5614973565b6127108111613fb357602955005b637956bd3560e11b5f5260045ffd5b346103b757613fd036614605565b925f929192526020805260405f20905f5260205260405f209060018060a01b03165f5260205260405f20905f52602052602060ff60405f2054166040519015158152f35b346103b7575f3660031901126103b7576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346103b75760203660031901126103b757614071614441565b614079614973565b6001600160a01b031680156140c057600480546001600160a01b031916821790557f3cceaedf044d3b5af0dbf9cf80be3d5a7be17bdc9ee87aa77868aa3b6ad54e805f80a2005b632a86629560e21b5f5260045ffd5b346103b75760403660031901126103b7576004356024358015908115036103b7576002546001600160a01b0316338190036103a857604051633b0ff6bb60e01b8152602081600481855afa908115610997575f916141fc575b5083036109f4576020600491604051928380926374e45da960e11b82525afa908115610997575f916141dd575b50601d8110156109b157600d036141ce576141bf57805f52601c60205260405f2054905f5b82811061418c57602060405160018152f35b815f52602560205260405f20815f5260205260405f2054156141b05760010161417a565b63439399e560e01b5f5260045ffd5b63ac5e0cbb60e01b5f5260045ffd5b634d818d4960e11b5f5260045ffd5b6141f6915060203d6020116109ed576109df8183614520565b83614155565b90506020813d602011614226575b8161421760209383614520565b810103126103b7575184614128565b3d915061420a565b346103b75760203660031901126103b7576002546001600160a01b031633036103a8576004355f52600e60205260405f205f5b8154811015610910576001546001600160a01b0316906142818184614792565b905460039190911b1c6001600160a01b0316823b156103b7575f92602484926040519586938492631bf8593960e31b845260048401525af1918215610997576001926142cf575b5001614261565b5f6142d991614520565b836142c8565b346103b7576142ed36614605565b925f92919252601f60205260405f20905f5260205260405f209060018060a01b03165f5260205260405f20905f52602052602060405f2054604051908152f35b346103b75761433b36614605565b925f92919252602160205260405f20905f5260205260405f209060018060a01b03165f5260205260405f20905f52602052602060ff60405f2054166040519015158152f35b346103b75760203660031901126103b75760043561439c614973565b606481116143dd5760407f8219a9db2aac1ea83dbd262fc148ad39af63c144f8caf76b4dc5ff5d250b004c91601654908060165582519182526020820152a1005b633ca0ab7b60e01b5f5260045ffd5b346103b7576143fa3661446d565b915f52602460205260405f20905f5260205260405f209060018060a01b03165f526020526123da61442d60405f20614541565b6040519182916020835260208301906145e1565b600435906001600160a01b03821682036103b757565b602435906001600160a01b03821682036103b757565b60609060031901126103b75760043590602435906044356001600160a01b03811681036103b75790565b90600182811c921680156144c5575b60208310146144b157565b634e487b7160e01b5f52602260045260245ffd5b91607f16916144a6565b608081019081106001600160401b03821117611bbf57604052565b60e081019081106001600160401b03821117611bbf57604052565b60a081019081106001600160401b03821117611bbf57604052565b90601f801991011681019081106001600160401b03821117611bbf57604052565b9060405191825f82549261455484614497565b80845293600181169081156145bf575060011461457b575b5061457992500383614520565b565b90505f9291925260205f20905f915b8183106145a3575050906020614579928201015f61456c565b602091935080600191548385890101520191019091849261458a565b90506020925061457994915060ff191682840152151560051b8201015f61456c565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60809060031901126103b75760043590602435906044356001600160a01b03811681036103b7579060643590565b60409060031901126103b7576004359060243590565b9181601f840112156103b7578235916001600160401b0383116103b757602083818601950101116103b757565b9060a06003198301126103b75760043591602435916044356001600160401b0381116103b757826146a991600401614649565b929092916064356001600160401b0381116103b757826023820112156103b7578060040135926001600160401b0384116103b75760248460051b830101116103b757602401919060843590565b90602080835192838152019201905f5b8181106147135750505090565b82516001600160a01b0316845260209384019390920191600101614706565b90602080835192838152019201905f5b81811061474f5750505090565b8251845260209384019390920191600101614742565b805482101561477e575f5260205f209060021b01905f90565b634e487b7160e01b5f52603260045260245ffd5b805482101561477e575f5260205f2001905f90565b908160209103126103b75751601d8110156103b75790565b519081151582036103b757565b81156147d6570490565b634e487b7160e01b5f52601260045260245ffd5b81810292918115918404141715611cfc57565b8054600160401b811015611bbf5761481a91600182018155614792565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b90604051918281549182825260208201905f5260205f20925f5b81811061486d57505061457992500383614520565b84546001600160a01b0316835260019485019487945060209093019201614858565b5f198114611cfc5760010190565b6001600160401b038111611bbf5760051b60200190565b906148be8261489d565b6148cb6040519182614520565b82815280926148dc601f199161489d565b0190602036910137565b805182101561477e5760209160051b010190565b91908201809211611cfc57565b91908203918211611cfc57565b6001600160401b038111611bbf57601f01601f191660200190565b60043561ffff811681036103b75790565b60243561ffff811681036103b75790565b60443561ffff811681036103b75790565b60643561ffff811681036103b75790565b5f546001600160a01b0316330361498657565b63118cdaa760e01b5f523360045260245ffd5b5f51602061552a5f395f51905f525c6149bf5760015f51602061552a5f395f51905f525d565b633ee5aeb560e01b5f5260045ffd5b916040519163a9059cbb60e01b5f5260018060a01b031660045260245260205f60448180865af19060015f5114821615614a2e575b60405215614a0e5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b906001811516614a4657823b15153d15161690614a03565b503d5f823e3d90fd5b919091805f52601c60205260405f2054831015610a53575f52601c602052614a7a8260405f20614765565b5091825403614a8557565b637d7d0e5f60e01b5f5260045ffd5b9596919493909792600386016001600160401b038311611bbf57614ab88154614497565b601f8111614e67575b50825f601f8211600114614e03575f91614df8575b508360011b905f198560031b1c19161790555b5f5b88811015614d585760018054908801906001600160a01b03165f614b0f8484614792565b9054604051632942541760e11b815260039290921b1c6001600160a01b0316600482015291829060249082905afa908115610997575f91614cdf575b505115614cd0578160051b870135601e19883603018112156103b7578701908135906001600160401b0382116103b75781360360208401136103b7578a5f52602460205260405f208d5f52602052614ba78460405f2092614792565b60018060a01b0391549060031b1c1660018060a01b03165f5260205260405f2091614bd28354614497565b601f8111614c8b575b505f90601f8311600114614c1e576001949392915f9183614c10575b50505f19600383901b1c191690841b1790555b01614aeb565b602092500101355f80614bf7565b601f19831691845f5260205f20925f5b818110614c71575091600196959492918388959310614c55575b505050811b019055614c0a565b01602001355f19600384901b60f8161c191690555f8080614c48565b919360206001819282888801013581550195019201614c2e565b835f5260205f20601f840160051c81019160208510614cc6575b601f0160051c01905b818110614cbb5750614bdb565b5f8155600101614cae565b9091508190614ca5565b6316f7535160e11b5f5260045ffd5b90503d805f833e614cf08183614520565b8101906020818303126103b7578051906001600160401b0382116103b7570181601f820112156103b757805190614d2682614914565b92614d346040519485614520565b828452602083830101116103b757815f9260208093018386015e830101525f614b4b565b507fe20a8926756681a7b5a0012bbc9d06a1cef062fc77fd91f3a3e6e68d6cc7e39a9550602094508160607f19a47fa24c43060a3e1f39e943807c130a264b31560486108d3db2ea273202859289948c9b9c96979c865f5260258a5260405f20885f528a528860405f205560405194859360408552816040860152858501375f83830185015289830152601f01601f19168101030190a3604051908152a3565b90508201355f614ad6565b5f8381526020812092508590601f198216905b818110614e4c575010614e33575b5050600183811b019055614ae9565b8301355f19600386901b60f8161c191690555f80614e24565b86840135855560019094019360209384019388935001614e16565b815f5260205f20601f850160051c81019160208610614ea2575b601f0160051c01905b818110614e975750614ac1565b5f8155600101614e8a565b9091508190614e81565b805f52601c60205260405f2054821015610a5357805f52601c602052614ed58260405f20614765565b50815f5260196020526002614eed8560405f20614765565b50019060ff825460281c16615037575f915f9160015f91018054915b828110614f805750505060125491828110614f7757614f6a6040937f43f39e4e350f276d138c2bd9e8df24df2957632fd8151c885d7a2a148c795c8695101580939065ff0000000000825491151560281b169065ff00000000001916179055565b82519182526020820152a4565b50505050505050565b614f8a8183614792565b90545f8981526021602090815260408083208d8452825280832060039590951b9390931c6001600160a01b03168083529381528282208d83529052205460ff16614fd8575b50600101614f09565b94614fe29061488f565b94875f526020805260405f20895f5260205260405f209060018060a01b03165f5260205260405f20895f5260205260ff60405f205416615023575b5f614fcf565b9461502f60019161488f565b95905061501d565b5050505050565b8015611cfc575f190190565b81156147d6570690565b9080156151345760015b8181106150b657506001808216146150a45760011c905f198201828111611cfc5761509e9261509061509792846148e6565b51926148e6565b51906148fa565b60011c90565b906150b29160011c906148e6565b5190565b906150c3828495946148e6565b5192825b80151580615116575b156150fe575f19810190808211611cfc576150ee6150f992886148e6565b51611cf082896148e6565b6150c7565b9361510f60019394929695866148e6565b520161505e565b505f198101818111611cfc5761512d8691886148e6565b51116150d0565b6311298f6d60e11b5f5260045ffd5b6040516323b872dd60e01b5f9081526001600160a01b039384166004529290931660245260449390935260209060648180865af19060015f5114821615615194575b6040525f60605215614a0e5750565b906001811516614a4657823b15153d15161690615185565b909182827f8faad919cbe9703e1bfaea79ce255b90cd7fdc25d976e997f2cf26a0ba8538b460406001850154600360018060a01b03875416960161010061ffff19825416179055806152ae575b835f52600860205260ff6004835f200154165f1461528d575f5b80151580615278575b615252575b82519182526020820152a47fe725be1a9d589008aa523bb5095c4ff62c7a8e4e9269b2850724c5f3c2298ba65f80a3565b845f526003602052825f20615268828254614907565b9055615273816152cc565b615221565b50845f52600360205280835f2054101561521c565b835f5260036020526127106152a8835f2054602954906147ea565b04615213565b6004546152c790829088906001600160a01b03166149ce565b6151f9565b8015615347576004546001600160a01b031690600181901c823b156103b7575f8093602460405180968193630852cd8d60e31b83528660048401525af1918215610997576145799361533293615337575b5061532a826006546148fa565b600655614907565b6153fa565b5f61534191614520565b5f61531d565b50565b908151600281106153f5575f198101908111611cfc57805b61536b57505050565b825160405161539481611c99602082019486888791606093918352602083015260408201520190565b519020906001810191828211611cfc576153ef926153b19161504a565b6001600160a01b036153c382876148e6565b5116906153e46001600160a01b036153db85896148e6565b511691876148e6565b52611cf082866148e6565b80615362565b505050565b801561534757600154604051639958065160e01b815290602090829060049082906001600160a01b03165afa908115610997575f916154c7575b506001600160a01b0381161561546e576004546154699291615461918391906001600160a01b03166149ce565b6006546148fa565b600655565b506004546001600160a01b031690813b156103b7575f8092602460405180958193630852cd8d60e31b83528660048401525af191821561099757615469926154b7575b50615461565b5f6154c191614520565b5f6154b1565b90506020813d602011615501575b816154e260209383614520565b810103126103b757516001600160a01b03811681036103b7575f615434565b3d91506154d556fe3a922abd76b9b7c1ea4bce9b738c36db4a39b390754c59a980916ffb688792c79b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220a8471fd3183c2aa0ef558f9332f3e00af83500b48ea4ad976e39a1302b31c15c64736f6c634300081c0033" } \ No newline at end of file diff --git a/cache_model_0/abis/DINTaskCoordinator.json b/cache_model_0/abis/DINTaskCoordinator.json index 32eb49ef..f61c75ad 100644 --- a/cache_model_0/abis/DINTaskCoordinator.json +++ b/cache_model_0/abis/DINTaskCoordinator.json @@ -1441,6 +1441,19 @@ "outputs": [], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "taskProtocolVersion", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "pure" + }, { "type": "function", "name": "tier1BatchCount", @@ -2412,11 +2425,6 @@ "name": "TC_DisputeWindowClosed", "inputs": [] }, - { - "type": "error", - "name": "TC_EvalPhaseNotClosed", - "inputs": [] - }, { "type": "error", "name": "TC_FailedToCreateAuditorsBatches", @@ -2439,7 +2447,7 @@ }, { "type": "error", - "name": "TC_GIRewardPoolNotFunded", + "name": "TC_GINotEnded", "inputs": [] }, { @@ -2577,6 +2585,11 @@ "name": "TC_RevealCannotBeStarted", "inputs": [] }, + { + "type": "error", + "name": "TC_SlotsAlreadyReleased", + "inputs": [] + }, { "type": "error", "name": "TC_StakeBelowModelFloor", @@ -2668,5 +2681,5 @@ "inputs": [] } ], - "bytecode": "0x60a03461016957601f615d4b38819003918201601f19168301916001600160401b0383118484101761016d5780849260409485528339810103126101695780516001600160a01b038116919082900361016957602001513315610156575f8054336001600160a01b0319821681178355604051949290916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a35f60035568056bc75e2d63100000601b55601c80546001600160c01b0319167007000000000002a3000000000000015180179055610bb8602355801561014757600180546001600160a01b0319169190911790556080526004805460ff191690555f807fa8f52d7b4405100a39ccc552ccc9426d860b1e822c71e97675d1623b49db6bab8180a3615bc990816101828239608051818181611f0c0152612cfb0152f35b63f70949a160e01b5f5260045ffd5b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c90816304501eec1461504a5750806309adabf514614c3e5780630a2b338214614b185780630ac043401461447f5780630e84af231461422c578063117f5f92146142065780631511c3a61461413357806317baaf2014614116578063199efdf4146140ec5780631a336bfc14613a015780631c2c56bc146139d75780631d9e72951461359e5780631dfa1ac21461353f5780631eb30bfe146134fb5780631f1761d01461348d5780631f83192c1461345e5780632059a899146133a55780632450922014613388578063246116e6146132ed57806324e168af146132a15780632a9cafbe146132805780632b143f03146132125780632d8d2d631461311057806331823a22146130f557806332280eed14612f2557806337d274c914612ef25780633a0ea70414612ec95780633b0ff6bb14612eac5780633b6dffe514612e595780633ce58bba14612deb57806342f7750214612d1e57806343cdaad214612ce457806346bf820a14612c765780634774b52414612bfa5780634e649ca71461281b578063530a9fb114612794578063576a91b31461266357806358aed7bc1461263b578063595e3411146125cd5780635b0283ef146123655780635bfd7d3a146122b05780635fec207c14611e555780636336894114611de7578063715018a614611d90578063747a21fe14611d7457806378737a3914611d4c5780637c4596c714611ba1578063868276ab14611b8457806388c2978514611aaa578063894f4dd114611a725780638b1de7be14611a045780638cfbd515146119de5780638da5cb5b146119b7578063914b92cd146116ec5780639de2f2a01461159c578063a120c41a14611576578063a7e10e281461153e578063aafebd501461146e578063ac0c7d5114611445578063ac9fc1001461142c578063acaf042a1461049a578063adabd801146113f9578063b4b9db4114610f89578063b593d2d514611394578063b61998b61461129d578063b6f104a814611273578063b759977014611244578063b7696bd6146111d3578063b7a0e41c14610f8e578063bee3fe1714610f89578063c0aeb10d14610f1b578063c6e8fc6f14610d61578063c9ad03c814610d29578063cad783b214610aca578063cb404a75146109cf578063cc279009146109b2578063d6f353a11461098a578063d86795ac1461091c578063e2852e3f146108f3578063e547118e14610885578063e57c943e1461083c578063e6f5e11914610689578063e9c8bb5214610660578063ead1df17146105c3578063f0f95f6414610549578063f2fde38b146104c4578063f34db55e1461049a5763f4eed253146103e4575f80fd5b34610496576020366003190112610496576103fd6150eb565b610405615769565b60ff60045416601a81101561048257610473576001600160a01b03168015610464576bffffffffffffffffffffffff60a01b6002541617600255600160ff19600454161760045560016003545f516020615b345f395f51905f525f80a3005b63f70949a160e01b5f5260045ffd5b631f85a85960e31b5f5260045ffd5b634e487b7160e01b5f52602160045260245ffd5b5f80fd5b34610496576020366003190112610496576004355f526012602052602060405f2054604051908152f35b34610496576020366003190112610496576104dd6150eb565b6104e5615769565b6001600160a01b03168015610536575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b346104965761055736615153565b90610560615769565b60035481036105b45760ff60045416601a81101561048257601681141590816105a8575b50610599575f52601260205260405f20555f80f35b63c7a0e6e160e01b5f5260045ffd5b60049150141583610584565b6347ca2dcf60e01b5f5260045ffd5b34610496575f366003190112610496576105db615a38565b335f52602860205260405f2054801561065257335f81815260286020526040812055601a546106149183916001600160a01b0316615a6d565b6040519081527fa09dd3797281514bc434a54ae8b69a6d02ae2fdbfab7c86dc8975163fd0045d060203392a25f5f516020615b745f395f51905f525d005b621db6b160e21b5f5260045ffd5b34610496575f3660031901126104965760ff60045416604051601a821015610482576020918152f35b346104965760803660031901126104965760243560043560028210156104965760643590604435906001600160a01b03831683036104965760a093610790575f818152600f6020908152604080832085845282528083205f196001891b019690961680845295825280832054848452600e83528184208685528352818420878552835281842054858552600c84528285208786528452828520888652845282852054868652600b8552838620888752855283862098865297845282852054958552600d84528285209685529583528184208585529092529091205460ff9182169490939116905b604051941515855260208501521515604084015260608301526080820152f35b5f81815260176020908152604080832085845282528083205f196001891b01969096168084529582528083205484845260168352818420868552835281842087855283528184205485855260148452828520878652845282852088865284528285205486865260138552838620888752855283862098865297845282852054958552601584528285209685529583528184208585529092529091205460ff918216949093911690610770565b34610496576040366003190112610496576108556150d5565b6004355f52600760205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346104965760203660031901126104965761089e615769565b600354600435036105b45760ff60045416601a811015610482576005036108e457600660ff19600454161760045560066003545f516020615b345f395f51905f525f80a3005b6321012b6760e01b5f5260045ffd5b34610496576020366003190112610496576004355f5260208052602060405f2054604051908152f35b3461049657602036600319011261049657610935615769565b600354600435036105b45760ff60045416601a8110156104825760130361097b57601460ff19600454161760045560146003545f516020615b345f395f51905f525f80a3005b631416b1a360e01b5f5260045ffd5b34610496575f366003190112610496576001546040516001600160a01b039091168152602090f35b34610496575f366003190112610496576020602154604051908152f35b34610496576020366003190112610496576004356109eb615769565b60035481036105b45760ff60045416601a81101561048257601603610abb5760025460405163cb404a7560e01b81526004810192909252602090829060249082905f906001600160a01b03165af1908115610ab0575f91610a81575b5015610a7257601760ff19600454161760045560176003545f516020615b345f395f51905f525f80a3005b634b058d6560e11b5f5260045ffd5b610aa3915060203d602011610aa9575b610a9b8183615222565b810190615243565b81610a47565b503d610a91565b6040513d5f823e3d90fd5b63b52ae37160e01b5f5260045ffd5b3461049657602036600319011261049657600435610ae6615769565b60035481036105b45760ff60045416601a81101561048257601503610d1a575f81815260106020526040812091426001600160401b0316915b8354811015610cf557610b328185615274565b505f5f905f935f9760018501985b8954811015610c5257610b53818b61519e565b60018060a01b0391549060031b1c16885f52601460205260405f20875490815f5260205260405f2060018060a01b0383165f5260205260ff60405f205416610be2575b5090600191895f52601860205260405f2090838060a01b03165f5260205260405f20610bc2815461528d565b9055885f52601960205260405f20610bda815461528d565b905501610b40565b97610bec9061528d565b97895f52601360205260405f20815f5260205260405f2060018060a01b0383165f5260205260405f2054908a5f52601560205260405f20905f5260205260405f20815f5260205260405f2054878111610c46575b50610b96565b9650945060018c610c40565b50969750929150928315610ce757600211610cd8577f9bca595da6d2d1ae0f0ef7abe316afdd467890adf8eb5a2234a8af80f8d151b760206001948360028895018760ff19825416179055816003820155845f526025835260405f2090545f52825260405f20896001600160401b0319825416179055604051908152a201929192610b1f565b6380c2216560e01b5f5260045ffd5b62b5305b60e51b5f5260045ffd5b601660ff19600454161760045560166003545f516020615b345f395f51905f525f80a3005b63c4d5525560e01b5f5260045ffd5b34610496576020366003190112610496576001600160a01b03610d4a6150eb565b165f526028602052602060405f2054604051908152f35b3461049657610d6f36615169565b9160035481036105b45760ff60045416601a81101561048257601403610f0c5781610efd575f818152601160209081526040808320858452825280832033845290915290205460ff1615610eee576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610ab0575f91610ecf575b5015610ec0578215610eb1575f818152601760209081526040808320858452825280832033845290915290205460ff16610ea2575f818152601660209081526040808320858452825280832033808552908352818420879055848452601783528184208685528352818420818552835292819020805460ff191660011790555194855290937f430cf27dfb66aa7260b606dbba6d3d2600983f065b1e30ffcab0e6457f3776c39190a4005b6343a311af60e01b5f5260045ffd5b630e660de560e11b5f5260045ffd5b636936304560e01b5f5260045ffd5b610ee8915060203d602011610aa957610a9b8183615222565b84610df7565b6328e28e8360e01b5f5260045ffd5b6327fda01d60e11b5f5260045ffd5b636582261960e11b5f5260045ffd5b3461049657602036600319011261049657610f34615769565b600354600435036105b45760ff60045416601a81101561048257600603610f7a57600760ff19600454161760045560076003545f516020615b345f395f51905f525f80a3005b6301876fed60e21b5f5260045ffd5b615183565b3461049657602036600319011261049657600435610faa615769565b60035481036105b45760ff60045416601a811015610482576012036111c4575f81815260096020526040812091426001600160401b0316915b835481101561119f57610ff6818561525b565b5060018101945f9485938492835b895481101561111757611017818b61519e565b60018060a01b0391549060031b1c16885f52600c60205260405f20875490815f5260205260405f2060018060a01b0383165f5260205260ff60405f2054166110a6575b5090600191895f52601860205260405f2090838060a01b03165f5260205260405f20611086815461528d565b9055885f52601960205260405f2061109e815461528d565b905501611004565b976110b09061528d565b97895f52600b60205260405f20815f5260205260405f2060018060a01b0383165f5260205260405f2054908a5f52600d60205260405f20905f5260205260405f20815f5260205260405f205487811161110a575b5061105a565b909a50955060018c611104565b5096925092939096508115610ce757600211610cd857837fb1becb4dbd2440a1dbd1bac657cc9dbbe112f85f7a99012f71aff4bab4acc5a86020856003600197018760ff19825416179055846004820155835f526024825260405f2081545f52825260405f208a6001600160401b03198254161790555493604051908152a301929192610fe3565b601360ff19600454161760045560136003545f516020615b345f395f51905f525f80a3005b638f5431c960e01b5f5260045ffd5b346104965760803660031901126104965760243560028110156104965761120a606435916004355f52602760205260405f20615101565b6044355f5260205260405f2080548210156104965760209161122b9161519e565b905460405160039290921b1c6001600160a01b03168152f35b346104965761125236615153565b905f52600660205260405f2080548210156104965760209161122b9161519e565b34610496576020366003190112610496576004355f526019602052602060405f2054604051908152f35b3461049657608036600319011261049657600435602435906001600160401b03821680920361049657604435906001600160401b0382169283830361049657606435936001600160401b038516808603610496576112f9615769565b831591821561138b575b8215611382575b508115611379575b811561136d575b5061135e5767ffffffffffffffff60401b91601b55601c54936001600160401b0360801b9060801b16936001600160401b0360c01b16179160401b161717601c555f80f35b631218659f60e01b5f5260045ffd5b61010091501185611319565b80159150611312565b1591508661130a565b83159250611303565b34610496576020366003190112610496576113ad615769565b60ff60045416601a811015610482576003036113ea5760048035600555805460ff1916811781556003545f516020615b345f395f51905f525f80a3005b631529e88760e31b5f5260045ffd5b34610496576020366003190112610496576004355f52601f60205260206001600160401b0360405f205416604051908152f35b346104965761144361143d366150b2565b916155e7565b005b34610496575f3660031901126104965760206001600160401b03601c5460801c16604051908152f35b34610496575f36600319011261049657611486615769565b60ff60045416601a8110156104825760010361152f57600154604051633c1a477960e11b815230600482015290602090829060249082906001600160a01b03165afa908115610ab0575f91611510575b501561150157600260ff19600454161760045560026003545f516020615b345f395f51905f525f80a3005b634f3572c560e01b5f5260045ffd5b611529915060203d602011610aa957610a9b8183615222565b816114d6565b632748c52f60e21b5f5260045ffd5b346104965761154c36615153565b905f52602560205260405f20905f5260205260206001600160401b0360405f205416604051908152f35b346104965760203660031901126104965760043560035481036105b45761144390615497565b34610496576115aa36615153565b6115b2615769565b60ff60045416601a81101561048257600481141590816116e0575b506116d157600354600181018091116116bd5782036105b4576002546040516331ec4e2560e21b815260048101939093526001600160a01b031691602081602481865afa908115610ab0575f9161168b575b501561167c57813b15610496575f91602483926040519485938492633f23a7b760e21b845260048401525af18015610ab05761166c575b61166160035461528d565b60035561144361594d565b5f61167691615222565b80611656565b6333b8921f60e21b5f5260045ffd5b90506020813d6020116116b5575b816116a660209383615222565b8101031261049657518361161f565b3d9150611699565b634e487b7160e01b5f52601160045260245ffd5b63205400e160e21b5f5260045ffd5b601991501415836115cd565b34610496576116fa366150b2565b825f9392935260266020526117128360405f20615101565b815f5260205260405f2090600282019283549260ff8460c01c16806119aa575b1561199b5760ff8460d01c1661198c576001600160401b038460401c1642111561197d5760ff60d01b19909316600160d01b17909355600182015491546001600160a01b03165f90815260286020526040902080546117cb9460049490929161179b919061529b565b9055805f5260276020526117b28560405f20615101565b825f526020526117d260405f206040519586809261530a565b0385615222565b600154604051631bad9e0560e11b81529360209185919082906001600160a01b03165afa928315610ab0575f93611949575b505f5b8451811015611914576001545f91906020906001600160a01b0390811690606490611832858b6152a8565b51166040519586938492637dd1602d60e11b845260048401528a60248401527814cd17d1949154d217d4d55091d493d55417d512535153d555603a1b60448401525af18015610ab0575f906118e2575b6001925084845f516020615b545f395f51905f5260606001600160a01b036118aa878d6152a8565b511694604051907814cd17d1949154d217d4d55091d493d55417d512535153d555603a1b82528b60208301526040820152a401611807565b506020823d821161190c575b816118fb60209383615222565b810103126104965760019151611882565b3d91506118ee565b507ff42fd7a6ed54d90eb785299babf26fd989424f01778ddc07a710b65b7effc0ca60208661194660405180926151fa565ba3005b9092506020813d602011611975575b8161196560209383615222565b8101031261049657519185611804565b3d9150611958565b63aa161f2160e01b5f5260045ffd5b631a3b0bef60e01b5f5260045ffd5b630a18626760e21b5f5260045ffd5b5060ff8460c81c16611732565b34610496575f366003190112610496575f546040516001600160a01b039091168152602090f35b346104965760203660031901126104965760043560035481036105b4576114439061534a565b3461049657602036600319011261049657611a1d615769565b600354600435036105b45760ff60045416601a81101561048257600703611a6357600860ff19600454161760045560086003545f516020615b345f395f51905f525f80a3005b63ca5a3b5560e01b5f5260045ffd5b3461049657611a8036615153565b905f52602460205260405f20905f5260205260206001600160401b0360405f205416604051908152f35b3461049657602036600319011261049657600435611ac6615769565b60ff60045416601a8110156104825760048114159081611b78575b506116d157600354600181018091116116bd5781036105b4576002546040516331ec4e2560e21b81526004810192909252602090829060249082906001600160a01b03165afa908115610ab0575f91611b46575b501561167c5761166160035461528d565b90506020813d602011611b70575b81611b6160209383615222565b81010312610496575181611b35565b3d9150611b54565b60199150141582611ae1565b34610496575f366003190112610496576020602254604051908152f35b3461049657611baf36615169565b9160035481036105b45760ff60045416601a81101561048257601103611d3d57805f52600960205260405f2054821015611d2e575f818152600a60209081526040808320858452825280832033845290915290205460ff1615610eee576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610ab0575f91611d0f575b5015610ec0578215611d00575f818152600f60209081526040808320858452825280832033845290915290205460ff16611cf1575f818152600e60209081526040808320858452825280832033808552908352818420879055848452600f83528184208685528352818420818552835292819020805460ff191660011790555194855290937fc33cb04355b8f36a3e3860fb823d630cc37a5833cf62828f742e70bdd3751eac9190a4005b63432cb74f60e11b5f5260045ffd5b63d5d0c92560e01b5f5260045ffd5b611d28915060203d602011610aa957610a9b8183615222565b84611c46565b633b554d2360e11b5f5260045ffd5b637100347160e01b5f5260045ffd5b34610496575f366003190112610496576002546040516001600160a01b039091168152602090f35b34610496575f36600319011261049657602060405161012c8152f35b34610496575f36600319011261049657611da8615769565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b3461049657602036600319011261049657611e00615769565b600354600435036105b45760ff60045416601a81101561048257601003611e4657601160ff19600454161760045560116003545f516020615b345f395f51905f525f80a3005b63076b0ff760e01b5f5260045ffd5b346104965760203660031901126104965760043560035481036105b45760ff60045416601a811015610482576006036122a1576001546040516310ab556b60e21b81523360048201526001600160a01b0390911690602081602481855afa908115610ab0575f91612282575b5015610ec0575f82815260076020908152604080832033845290915290205460ff1661227357815f52600660205261012c60405f2054101561226457604051634ab989d560e01b81527f00000000000000000000000000000000000000000000000000000000000000006004820152602081602481855afa908115610ab0575f91612232575b5080151590816121cb575b506121bc5760405163f2d679bf60e01b8152602081600481855afa908115610ab0575f9161218a575b5080612032575b5050805f526006602052611f993360405f206152bc565b5f8181526007602090815260408083203384529091529020805460ff19166001908117909155546001600160a01b0316803b15610496575f80916024604051809481936347e91db160e01b83523360048401525af18015610ab057612022575b5033907f88f3e7b00fb6a3104c7d2c8d3ff669b012e5724bd38e50559cb41d177a3a8ba15f80a3005b5f61202c91615222565b81611ff9565b6040516303d3b32360e51b8152336004820152602081602481865afa908115610ab0575f91612158575b50604051631bad9e0560e11b815290602082600481875afa918215610ab0575f92612124575b5081156121105760249260209261209992046151e7565b926040519283809263b998328360e01b82523360048301525afa908115610ab0575f916120de575b5010156120cf578180611f82565b634f21e5ed60e11b5f5260045ffd5b90506020813d602011612108575b816120f960209383615222565b810103126104965751836120c1565b3d91506120ec565b634e487b7160e01b5f52601260045260245ffd5b9091506020813d602011612150575b8161214060209383615222565b8101031261049657519085612082565b3d9150612133565b90506020813d602011612182575b8161217360209383615222565b8101031261049657518461205c565b3d9150612166565b90506020813d6020116121b4575b816121a560209383615222565b81010312610496575183611f7b565b3d9150612198565b631931d5eb60e01b5f5260045ffd5b6040516303d3b32360e51b8152336004820152909150602081602481865afa908115610ab0575f91612200575b501083611f52565b90506020813d60201161222a575b8161221b60209383615222565b810103126104965751846121f8565b3d915061220e565b90506020813d60201161225c575b8161224d60209383615222565b81010312610496575183611f47565b3d9150612240565b63083a2c5d60e01b5f5260045ffd5b6306d4010b60e21b5f5260045ffd5b61229b915060203d602011610aa957610a9b8183615222565b83611ec1565b6331fe69e760e21b5f5260045ffd5b34610496576020366003190112610496576004356122cc615769565b60035481036105b45760ff60045416601a81101561048257600a036123565761230c6001600160401b03601c5460801c166001600160401b0343166151c7565b905f52601f6020526001600160401b0360405f2091166001600160401b0319825416179055600b60ff196004541617600455600b6003545f516020615b345f395f51905f525f80a3005b630d1f756d60e31b5f5260045ffd5b346104965761237336615094565b60039391935482036105b45760ff60045416601a811015610482576015036125be5782610efd575f828152601160209081526040808320868452825280832033845290915290205460ff1615610eee576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610ab0575f9161259f575b5015610ec0575f828152601760209081526040808320868452825280832033845290915290205460ff1615612590575f828152601460209081526040808320868452825280832033845290915290205460ff16612581578315612572576040805160208101868152918101929092523360608301526080820183905290600160a082015260c0808201859052815261249660e082615222565b5190205f828152601660209081526040808320868452825280832033845290915290205403612563575f818152601460209081526040808320858452825280832033808552908352818420805460ff1916600117905584845260138352818420868552835281842081855283529281902086905551858152849184917ff381116b332b24a3860f90e240a69b31a4eb09c27b59cc54e049aad99897e97c9190a45f52601560205260405f20905f5260205260405f20905f5260205260405f2061255f815461528d565b9055005b634407f1db60e01b5f5260045ffd5b63010e92c160e11b5f5260045ffd5b636e479ac960e01b5f5260045ffd5b6343d466b760e11b5f5260045ffd5b6125b8915060203d602011610aa957610a9b8183615222565b856123fd565b63e396ca2760e01b5f5260045ffd5b34610496576020366003190112610496576125e6615769565b600354600435036105b45760ff60045416601a8110156104825760110361262c57601260ff19600454161760045560126003545f516020615b345f395f51905f525f80a3005b63e89736d560e01b5f5260045ffd5b34610496575f36600319011261049657601a546040516001600160a01b039091168152602090f35b346104965761267136615153565b9060035481116105b457805f52600960205260405f2054821015612785576001916126a6915f52600960205260405f2061525b565b50805490600281019060ff6003820154166126d76004830154926126d0604051809881930161530a565b0386615222565b604051948580966020865491828152019081965f5260205f20905f5b81811061276c575050509161270f612725926020940389615222565b60405196875260a08388015260a0870190615117565b958587036040870152519586815201925f955b8087106127545750508394501515606084015260808301520390f35b90936020806001928751815201950196019590612738565b825484528a9450602090930192600192830192016126f3565b631935ca8d60e01b5f5260045ffd5b34610496576127a236615153565b9081610efd5760035481116105b4576001916127c8915f52601060205260405f20615274565b5080549060ff600282015416906127ee6003820154916117cb604051809781930161530a565b61280a6040519485948552608060208601526080850190615117565b911515604084015260608301520390f35b346104965761282936615064565b92612832615769565b805f5260266020526128478360405f20615101565b825f5260205260405f2060028101805460ff8160c01c1680612bed575b1561199b5760ff8160d01c1661198c5760ff60d01b1916600160d01b1790558415612bb657600181015490546001600160a01b03165f908152602860205260409020805490916128b39161529b565b905560028310156104825760046128f6948415805f14612b9557835f5260096020526128fd60016128e78760405f2061525b565b50015b6040519889809261530a565b0388615222565b8015612b7557835f526009602052826129198660405f2061525b565b500154915b600154604051631bad9e0560e11b81529460209186919082906001600160a01b03165afa938415610ab0575f94612b41575b505f5b8851811015612b01578215612ac057855f52600b60205260405f20875f5260205260405f2060018060a01b03612989838c6152a8565b511660018060a01b03165f526020528360405f20545b03612ab8576001545f91906020906001600160a01b03908116906064906129c6858f6152a8565b51166040519586938492637dd1602d60e11b845260048401528b602484015275299a2fa4a72b20a624a22fa0a3a3a922a3a0aa24a7a760511b60448401525af18015610ab05788888c945f93612a71575b505f516020615b545f395f51905f526060600196612a3b87898060a01b03926152a8565b5116946040519075299a2fa4a72b20a624a22fa0a3a3a922a3a0aa24a7a760511b82528c60208301526040820152a45b01612953565b94505050506020823d8211612ab0575b81612a8e60209383615222565b8101031261049657905189919088885f516020615b545f395f51905f52612a17565b3d9150612a81565b600190612a6b565b855f52601360205260405f20875f5260205260405f2060018060a01b03612ae7838c6152a8565b511660018060a01b03165f526020528360405f205461299f565b509395925050507f9086299cfbdc88752f050229a9e7b3563c343b687b7fa5f7ace411c049301d1a9294505b612b3c60405192839283615207565b0390a3005b9093506020813d602011612b6d575b81612b5d60209383615222565b8101031261049657519288612950565b3d9150612b50565b835f5260106020526003612b8c8660405f20615274565b5001549161291e565b835f5260106020526128fd6001612baf8760405f20615274565b50016128ea565b92612be860017f9086299cfbdc88752f050229a9e7b3563c343b687b7fa5f7ace411c049301d1a94969395015461578f565b612b2d565b5060ff8160c81c16612864565b3461049657602036600319011261049657600435612c16615769565b80158015612c6b575b612c5c5760407f6aa2abf97a85127b2d7e0a4a90f5bf57c815685cdf8a07985043f5298fd6190c91602354908060235582519182526020820152a1005b631e0e5cfb60e11b5f5260045ffd5b506127108111612c1f565b3461049657602036600319011261049657612c8f615769565b600354600435036105b45760ff60045416601a81101561048257601403612cd557601560ff19600454161760045560156003545f516020615b345f395f51905f525f80a3005b63106ff71f60e01b5f5260045ffd5b34610496575f3660031901126104965760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461049657602036600319011261049657600435612d3a615769565b60035481036105b45760ff60045416601a81101561048257601803612ddc5760018060a01b036002541690805f52601960205260405f2054823b156104965760445f9283604051958694859363e13b7b3160e01b8552600485015260248401525af18015610ab057612dcc575b601960ff19600454161760045560196003545f516020615b345f395f51905f525f80a3005b5f612dd691615222565b80612da7565b6303858c7f60e01b5f5260045ffd5b3461049657602036600319011261049657612e04615769565b600354600435036105b45760ff60045416601a81101561048257600d03612e4a57600e60ff196004541617600455600e6003545f516020615b345f395f51905f525f80a3005b636487147760e11b5f5260045ffd5b34610496576020366003190112610496576004355f526006602052612ea8612e8d612e9460405f206040519283809261530a565b0382615222565b604051918291602083526020830190615117565b0390f35b34610496575f366003190112610496576020600354604051908152f35b34610496575f3660031901126104965760206001600160401b03601c5460401c16604051908152f35b34610496576020366003190112610496576004355f52601d60205260206001600160401b0360405f205416604051908152f35b3461049657602036600319011261049657600435612f41615769565b6003548082109081156130cd575b501561309557805f52600860205260ff60405f20541661305757805f52600860205260405f20600160ff19825416179055805f52600660205260405f20905f5b825481101561300d576001546001600160a01b031690612faf818561519e565b905460039190911b1c6001600160a01b0316823b15610496575f92602484926040519586938492631bf8593960e31b845260048401525af1918215610ab057600192612ffd575b5001612f8f565b5f61300791615222565b84612ff6565b506002546001600160a01b031690813b15610496575f9160248392604051948593849263224c19bb60e01b845260048401525af18015610ab05761304d57005b5f61144391615222565b60405162461bcd60e51b81526020600482015260166024820152751cdb1bdd1cc8185b1c9958591e481c995b19585cd95960521b6044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f11d2481a185cc81b9bdd08195b99195960821b6044820152606490fd5b90508114806130dd575b82612f4f565b5060ff60045416601a811015610482576019146130d7565b34610496575f36600319011261049657602060405160028152f35b346104965760203660031901126104965760043561312c615769565b60035481036105b45760ff60045416601a81101561048257600b0361320357805f526020805260405f205480156131f457602090604460018060a01b0360025416935f60405195869485936363fd7a1160e11b8552600485015260248401525af1908115610ab0575f916131d5575b50156131c657600c60ff196004541617600455600c6003545f516020615b345f395f51905f525f80a3005b63611d8f8760e01b5f5260045ffd5b6131ee915060203d602011610aa957610a9b8183615222565b8161319b565b63dbbb808560e01b5f5260045ffd5b63c827e84760e01b5f5260045ffd5b346104965760203660031901126104965761322b615769565b600354600435036105b45760ff60045416601a8110156104825760080361327157600960ff19600454161760045560096003545f516020615b345f395f51905f525f80a3005b63768c7a4d60e11b5f5260045ffd5b3461049657602036600319011261049657613299615769565b600435602255005b34610496576020366003190112610496576132ba6150eb565b6132c2615769565b6001600160a01b03168015610464576bffffffffffffffffffffffff60a01b601a541617601a555f80f35b34610496576040366003190112610496576004356024359081151580920361049657613317615769565b60035481036105b45760ff60045416601a81101561048257600c03613379576002546001600160a01b031691823b156104965760445f928360405195869485936312308b7360e11b8552600485015260248401525af18015610ab05761304d57005b630c14724760e31b5f5260045ffd5b34610496575f366003190112610496576020602354604051908152f35b34610496576133c86133b6366150b2565b92915f52602660205260405f20615101565b905f5260205261012060405f2060018060a01b038154169060ff600182015491600360028201549101549260405194855260208501526001600160401b03811660408501526001600160401b038160401c1660608501526001600160401b038160801c166080850152818160c01c16151560a0850152818160c81c16151560c085015260d01c16151560e0830152610100820152f35b34610496576020366003190112610496576004355f526008602052602060ff60405f2054166040519015158152f35b34610496576020366003190112610496576134a6615769565b600354600435036105b45760ff60045416601a811015610482576009036134ec57600a60ff196004541617600455600a6003545f516020615b345f395f51905f525f80a3005b63379ffc8d60e11b5f5260045ffd5b34610496576040366003190112610496576135146150d5565b6004355f52601860205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b3461049657602036600319011261049657613558615769565b600354600435036105b45760ff60045416601a81101561048257600c0361320357600d60ff196004541617600455600d6003545f516020615b345f395f51905f525f80a3005b34610496576135ac366150b2565b90916135b6615a38565b801580156139cc575b6105b4576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610ab0575f916139ad575b5015610ec0576002831015610482578261395657805f52600960205260405f2054821015611d2e57805f52600960205260ff600361363e8460405f2061525b565b50015416815f52602460205260405f20835f526020526001600160401b0360405f205416905b15613947576001600160401b03613681601c5492828416906151c7565b16421161393857815f52602660205261369d8460405f20615101565b835f5260205260018060a01b0360405f205416613929576136d66001600160401b03601b549260801c166001600160401b0343166151c7565b906040519161012083018381106001600160401b0382111761391557604052338352602083019182526040830190426001600160401b0316825260608401905f825260808501906001600160401b0316815260a085015f815260c08601915f835260e08701935f85526101008801965f8852895f5260266020528b60405f209061375f91615101565b5f8c815260209190915260409081902099518a546001600160a01b0319166001600160a01b0391909116178a55905160018a0155955160028901805492516fffffffffffffffffffffffffffffffff199093166001600160401b03909216919091179190961b67ffffffffffffffff60401b1617855551845467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b16178455518354915161ffff60c01b1990921690151560c01b60ff60c01b161790151560c81b60ff60c81b1617825551815460ff60d01b191690151560d01b60ff60d01b1617905551600390910155601a54601b546040516323b872dd60e01b5f52336004523060245260449190915293906001600160a01b0316805a60645f806020948194f15f516001148116156138f6575b856040525f606052156138e457506138a784601b54926151fa565b60208401527f4c0dd383a6eb2a2f4b8f2a967e4365ca69b2d0012887044c0c509fa0ce676f2360403394a45f5f516020615b745f395f51905f525d005b635274afe760e01b5f5260045260245ffd5b600181151661390c57813b15153d15161661388c565b853d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b633624f00960e21b5f5260045ffd5b63a4872ddd60e01b5f5260045ffd5b6355bc0ebf60e11b5f5260045ffd5b805f52601060205260405f2054821015611d2e57805f52601060205260ff60026139838460405f20615274565b50015416815f52602560205260405f20835f526020526001600160401b0360405f20541690613664565b6139c6915060203d602011610aa957610a9b8183615222565b846135fd565b5060035481116135bf565b34610496576020366003190112610496576004355f526009602052602060405f2054604051908152f35b3461049657602036600319011261049657600435613a1d615769565b60035481036105b45760ff60045416601a81101561048257600f036140dd57805f52601e60205260405f20549081156140ce57805f52600660205260405f20905f915f81549160018060a01b0360015416915b8381106140495750613a8185615936565b94613a8f6040519687615222565b808652613a9e601f1991615936565b013660208701375f905f5b848110613fb8578787876003825110613fa957602492613af660405160208101908382526720a3a3afa0a2222960c11b604082015260288152613aed604882615222565b51902084615988565b6002546040516327416acd60e21b815260048101849052945f9186919082906001600160a01b03165afa938415610ab0575f94613f17575b506003845110613f085760405160208101918252660828e8ebe9288b60cb1b604082015260278152613b61604782615222565b51902083515f1981019081116116bd57805b613e7c5750505f915f915f935b600381018082116116bd57835181111580613e33575b15613d1257825f52600960205260405f208054600160401b81101561391557613bc49160018201815561525b565b5091613bcf8761528d565b96835560018301905f5b60038110613ca35750505060039182613bf3878a516152fd565b10613c90575b600281015f5b848110613c4057505090613c3a929154847fb9202018de9f7eba7cdf3f805001edb29b6629a2fd89f5bd9c667a9d99450f655f80a39461529b565b92613b80565b613c53613c4d828a61529b565b8b6152a8565b5190825491600160401b83101561391557613c7583600180950186558561519e565b819291549060031b91821b915f19901b191617905501613bff565b9150613c9d8588516152fd565b91613bf9565b600190613cc66001600160a01b03613cbe613c4d848761529b565b5116856152bc565b865f52600a60205260405f2086545f5260205260405f20828060a01b03613cf0613c4d848761529b565b5116838060a01b03165f5260205260405f208260ff1982541617905501613bd9565b5082906003613d228284516152fd565b1015613d4e575b601060ff19600454161760045560106003545f516020615b345f395f51905f525f80a3005b825f52601060205260405f208054600160401b81101561391557613d7791600182018155615274565b50915f83555f9160018401925b60038110613dbe575050505054907fcba6d1415af496acc567dab38af44a786626bc3e3c6d0057a8b34329bb31e6ef5f80a3808080613d29565b600190613de76001600160a01b03613ddf613dd9848761529b565b876152a8565b5116866152bc565b865f52601160205260405f2086545f5260205260405f20828060a01b03613e11613dd9848761529b565b5116838060a01b03165f5260205260405f208260ff1982541617905501613d84565b50600385018086116116bd578751811190811591613e52575b50613b96565b9050600286018087116116bd57885110159081613e71575b5088613e4c565b905087511088613e6a565b8451604051613eb381613ea5602082019486888791606093918352602083015260408201520190565b03601f198101835282615222565b5190209060018101918282116116bd57613f0292613ed09161597e565b613eda81886152a8565b5190613ef1613ee9848a6152a8565b5191896152a8565b52613efc82886152a8565b52615972565b80613b73565b634607f5d560e01b5f5260045ffd5b9093503d805f833e613f298183615222565b810190602081830312610496578051906001600160401b03821161049657019080601f83011215610496578151613f5f81615936565b92613f6d6040519485615222565b81845260208085019260051b82010192831161049657602001905b828210613f99575050509284613b2e565b8151815260209182019101613f88565b631571768360e31b5f5260045ffd5b613fc2818361519e565b90546040516310ab556b60e21b815260039290921b1c6001600160a01b031660048201819052909190602083602481895afa8015610ab0576001935f9161402b575b50614011575b5001613aa9565b61402461401d8661528d565b958a6152a8565b528961400a565b614043915060203d8111610aa957610a9b8183615222565b8b614004565b614053818361519e565b90546040516310ab556b60e21b815260039290921b1c6001600160a01b03166004820152602081602481875afa908115610ab0575f916140b0575b5061409c575b600101613a70565b946140a860019161528d565b959050614094565b6140c8915060203d8111610aa957610a9b8183615222565b8861408e565b6366d7bd4760e01b5f5260045ffd5b636be945dd60e01b5f5260045ffd5b34610496576020366003190112610496576004355f52601e602052602060405f2054604051908152f35b34610496575f366003190112610496576020601b54604051908152f35b34610496575f3660031901126104965761414b615769565b60ff60045416601a811015610482576002036141f857600154600254604051633c1a477960e11b81526001600160a01b0391821660048201529160209183916024918391165afa908115610ab0575f916141d9575b50156141ca57600360ff196004541617600455600380545f516020615b345f395f51905f525f80a3005b63f831404160e01b5f5260045ffd5b6141f2915060203d602011610aa957610a9b8183615222565b816141a0565b62bf1abb60e31b5f5260045ffd5b34610496575f3660031901126104965760206001600160401b03601c5416604051908152f35b346104965761423a36615094565b60039391935482036105b45760ff60045416601a8110156104825760120361447057815f52600960205260405f2054831015611d2e575f828152600a60209081526040808320868452825280832033845290915290205460ff1615610eee576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610ab0575f91614451575b5015610ec0575f828152600f60209081526040808320868452825280832033845290915290205460ff1615614442575f828152600c60209081526040808320868452825280832033845290915290205460ff166125815783156125725760408051602081018681529181019290925233606083015260808201839052905f60a082015260c0808201859052815261436b60e082615222565b5190205f828152600e60209081526040808320868452825280832033845290915290205403614433575f818152600c60209081526040808320858452825280832033808552908352818420805460ff19166001179055848452600b8352818420868552835281842081855283529281902086905551858152849184917e4ff64f8cc2f88940c535cfe6face7248dc5ebf1faba271d2f8052017e747c79190a45f52600d60205260405f20905f5260205260405f20905f5260205260405f2061255f815461528d565b635130b96560e11b5f5260045ffd5b632ccbe95160e01b5f5260045ffd5b61446a915060203d602011610aa957610a9b8183615222565b856142d3565b636f5ecfff60e11b5f5260045ffd5b346104965760203660031901126104965760043561449b615769565b60035481036105b45760ff60045416601a81101561048257601703614b0957600154604051631bad9e0560e11b815290602090829060049082906001600160a01b03165afa908115610ab0575f91614ad7575b50906127106144ff602354846151e7565b0491815f52600960205260405f20905f925b82548410156147e357614524848461525b565b5091825492600460018201910154945f5b82548110156147d057614548818461519e565b90545f878152600c602090815260408083208b8452825280832060039590951b9390931c6001600160a01b0316808352939052205490919060ff166146a657891561468c57600154604051630b6f9cd360e11b815260048101849052602481018c90527320a3a3afaa18afa727afa9aaa126a4a9a9a4a7a760611b6044820152606481018890529290602090849060849082905f906001600160a01b03165af18015610ab0578789918d955f91614642575b506001955f516020615b545f395f51905f52915b604080517320a3a3afaa18afa727afa9aaa126a4a9a9a4a7a760611b81526020810193909352820152606090a45b01614535565b95505050506020833d8211614684575b8161465f60209383615222565b810103126104965787875f516020615b545f395f51905f528d600196519150956145fa565b3d9150614652565b60019187875f516020615b545f395f51905f528d5f61460e565b855f52600b60205260405f20875f5260205260405f2060018060a01b0383165f526020528760405f2054036146df575b6001915061463c565b600154604051637dd1602d60e11b81526004810184905260248101879052734147475f54315f4241445f434f4e53454e53555360601b60448201529290602090849060649082905f906001600160a01b03165af1928315610ab0578789915f9561478c575b5060408051734147475f54315f4241445f434f4e53454e53555360601b8152602081018a9052908101959095526001945f516020615b545f395f51905f5290606090a46146d6565b945050506020833d82116147c8575b816147a860209383615222565b810103126104965791519187875f516020615b545f395f51905f52614744565b3d915061479b565b5095919350506001919350019291614511565b8491815f52601060205260405f20905f925b8254841015614ab2576148088484615274565b50805460038201549490937320a3a3afaa192fa727afa9aaa126a4a9a9a4a7a760611b929091734147475f54325f4241445f434f4e53454e53555360601b91600101905f905b8254821015614a9d578690614863838561519e565b60018060a01b0391549060031b1c1692825f52601460205260405f208a5f5260205260405f2060018060a01b0385165f526020528c60ff60405f205416155f146149935750878d8015614977579060846020925f60018060a01b03600154166040519889958694630b6f9cd360e11b86528c60048701526024860152604485015260648401525af1938415610ab0578a8a8f95968b975f91614934575b5060019596975f516020615b545f395f51905f52915b60408051928352602083018b9052820152606090a45b01915061484e565b95965050505093506020823d821161496f575b8161495460209383615222565b8101031261049657905187938d9392918b908b906001614900565b3d9150614947565b9490600193945f516020615b545f395f51905f528d925f614916565b9390919250885f52601360205260405f208a5f5260205260405f2060018060a01b0382165f526020528a60405f2054036149d1575b5060019061492c565b9250905f91602060018060a01b0360015416606460405180968193637dd1602d60e11b83528960048401528c60248401528b60448401525af1938415610ab0578a8a8f955f97614a52575b505f516020615b545f395f51905f5260019596978b8b60405192839260608401928452602084015260408301520390a4906149c8565b949550505093506020823d8211614a95575b81614a7160209383615222565b81010312610496579051928c9291908a8a5f516020615b545f395f51905f52614a1c565b3d9150614a64565b60019099019897509295506147f59350505050565b601860ff19600454161760045560186003545f516020615b345f395f51905f525f80a3005b90506020813d602011614b01575b81614af260209383615222565b810103126104965751826144ee565b3d9150614ae5565b6360cd0e9960e01b5f5260045ffd5b3461049657602036600319011261049657600435614b34615769565b60035481036105b45760ff60045416601a81101561048257600e03614c2f576002546040516356af71c960e11b81526004810183905290602090829060249082905f906001600160a01b03165af1908115610ab0575f91614c10575b5015614c0157614bb76001600160401b03601c5460801c166001600160401b0343166151c7565b905f52601d6020526001600160401b0360405f2091166001600160401b0319825416179055600f60ff196004541617600455600f6003545f516020615b345f395f51905f525f80a3005b6307fb097360e41b5f5260045ffd5b614c29915060203d602011610aa957610a9b8183615222565b82614b90565b63183dd1d160e21b5f5260045ffd5b3461049657614c4c36615064565b92614c55615769565b805f526026602052614c6a8360405f20615101565b825f5260205260405f2060018060a01b038154161561503b576002810190815460ff8160c01c1661502c5761ffff60c01b191686151560c81b60ff60c81b1617600160c01b1782558515614fe75760030190815415614fd857614d0f90614ce86001600160401b03601c5460401c166001600160401b0342166151c7565b67ffffffffffffffff60401b82549160401b169067ffffffffffffffff60401b1916179055565b546002841015610482576126d091614d766117cb9286155f14614fb757825f526009602052614d556001614d468860405f2061525b565b50015b6040519586809261530a565b825f526006602052614d7060405f206040519687809261530a565b84615988565b60405193614d85608086615222565b60038552602085019160603684375f975f5b8651811080614fad575b15614e7c57614dc3866001600160a01b03614dbc848b6152a8565b5116615aee565b1580614e0a575b90614dd891614ddd5761528d565b614d97565b6001600160a01b03614def828a6152a8565b5116614e04614dfd8d61528d565b9c8b6152a8565b5261528d565b506001546001600160a01b039081169190602090614e28838b6152a8565b51166024604051809581936310ab556b60e21b835260048301525afa8015610ab057614dd8925f91614e5e575b50909150614dca565b614e76915060203d8111610aa957610a9b8183615222565b8c614e55565b50945094919660039196935010613fa957835f526027602052614ea28260405f20615101565b855f5260205260405f20958151906001600160401b03821161391557600160401b8211613915578754828955808310614f87575b50965f5260205f20965f5b828110614f6a5750505084955093837f1aa282aabded606592d2cccbd78439ed03820b25543d6161b213b4b8c9651993614f587f5f2ed0b8a76b3e4bf8076e2e31d83602fef08c30bdb2b069d1db259c176e63ef9697604051918291614f4783896151fa565b604060208401526040830190615117565b0390a3612b3c60405192839283615207565b81516001600160a01b0316818a0155602090910190600101614ee1565b885f528260205f2091820191015b818110614fa25750614ed6565b5f8155600101614f95565b5060038a10614da1565b825f526010602052614d556001614fd18860405f20615274565b5001614d49565b632d5720c760e11b5f5260045ffd5b815460ff60d01b1916600160d01b1790915560010154919390927f5f2ed0b8a76b3e4bf8076e2e31d83602fef08c30bdb2b069d1db259c176e63ef92612be89061578f565b63450cfd7f60e11b5f5260045ffd5b63167f349d60e11b5f5260045ffd5b34610496575f366003190112610496576020906005548152f35b60809060031901126104965760043590602435600281101561049657906044359060643580151581036104965790565b60809060031901126104965760043590602435906044359060643590565b606090600319011261049657600435906024356002811015610496579060443590565b602435906001600160a01b038216820361049657565b600435906001600160a01b038216820361049657565b906002811015610482575f5260205260405f2090565b90602080835192838152019201905f5b8181106151345750505090565b82516001600160a01b0316845260209384019390920191600101615127565b6040906003190112610496576004359060243590565b606090600319011261049657600435906024359060443590565b34610496575f36600319011261049657602060405160038152f35b80548210156151b3575f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b906001600160401b03809116911601906001600160401b0382116116bd57565b818102929181159184041417156116bd57565b9060028210156104825752565b90929160209061521b8360408101966151fa565b1515910152565b90601f801991011681019081106001600160401b0382111761391557604052565b90816020910312610496575180151581036104965790565b80548210156151b3575f52600560205f20910201905f90565b80548210156151b3575f5260205f209060021b01905f90565b5f1981146116bd5760010190565b919082018092116116bd57565b80518210156151b35760209160051b010190565b8054600160401b811015613915576152d99160018201815561519e565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b919082039182116116bd57565b90602082549182815201915f5260205f20905f5b81811061532b5750505090565b82546001600160a01b031684526020909301926001928301920161531e565b805f52601f6020526001600160401b0360405f205416801561548857815f526020805260405f2054615479578043111561546a574080156153ef57604051602081019182528260408201526210555160ea1b6060820152604381526153b0606382615222565b5190205f828152602080805260409182902083905590519182527fe53c25b43758bf910ba120923c7295303dcbc2369273c7985227b00293f3b56991a2565b506154116001600160401b03601c5460801c166001600160401b0343166151c7565b5f828152601f6020908152604091829020805467ffffffffffffffff19166001600160401b0394909416938417905590519182527f7e22e73285f299179a84bb3388a9dd94b3eac27a6de162455a42ee6ce6706f9491a2565b63de3f681d60e01b5f5260045ffd5b638d10dfef60e01b5f5260045ffd5b63906135ab60e01b5f5260045ffd5b805f52601d6020526001600160401b0360405f20541680156155d857815f52601e60205260405f20546155c957804311156155ba5740801561553f57604051602081019182528260408201526241474760e81b6060820152604381526154fe606382615222565b5190205f828152601e602090815260409182902083905590519182527f9dcaca5280e612f442cef14eea003bbece818a3fde40db4dc4a3887682aa07b691a2565b506155616001600160401b03601c5460801c166001600160401b0343166151c7565b5f828152601d6020908152604091829020805467ffffffffffffffff19166001600160401b0394909416938417905590519182527fbed5c9fb50b85a70b1ea0ab5bf64bbf34ba76521149788c3873abaf955d545c291a2565b632f9cb5e160e21b5f5260045ffd5b634653993f60e01b5f5260045ffd5b631c237c4160e31b5f5260045ffd5b90815f5260266020526155fd8160405f20615101565b835f5260205260405f209060018060a01b038254161561503b57600282019081549260ff8460c01c1661502c5760030192835461575a576001600160401b039060801c168043111561574b57409182156156cc57506002811015610482577ff1ac2abd9bfa2c470babc8a68c69061d1661d798dedc05ba6f20a5fdb6bfe51592604092835160208101918252868582015260ff60f81b8460f81b166060820152876061820152606181526156b2608182615222565b5190208091556156c4835180936151fa565b6020820152a3565b7f2bee25e87a4aab24a6e926580a5c7026c624938721cc23acc05733fe7a4deebd9350604092506001600160401b039061573a61571283601c5460801c168443166151c7565b825467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b16178255565b5460801c166156c4835180936151fa565b63221198d360e11b5f5260045ffd5b63d6e8b91560e01b5f5260045ffd5b5f546001600160a01b0316330361577c57565b63118cdaa760e01b5f523360045260245ffd5b5f908015615932578060011c916157a683836152fd565b601a546001600160a01b031693909190843b15610496575f94602486926040519788938492630852cd8d60e31b845260048401525af1938415610ab05760049461591f575b50600154604051639958065160e01b81529460209186919082906001600160a01b03165afa9384156159125781946158ca575b506001600160a01b038416156158565750601a546158519361584992916001600160a01b0316615a6d565b60215461529b565b602155565b601a54929350916001600160a01b031690813b156158c6578291602483926040519485938492630852cd8d60e31b845260048401525af180156158bb576158a3575b505061585190615849565b6158ae828092615222565b6158b85780615898565b80fd5b6040513d84823e3d90fd5b8280fd5b9093506020813d60201161590a575b816158e660209383615222565b8101031261590657516001600160a01b038116810361590657925f61581e565b8380fd5b3d91506158d9565b50604051903d90823e3d90fd5b61592b91505f90615222565b5f5f6157eb565b5050565b6001600160401b0381116139155760051b60200190565b600560ff19600454161760045560056003545f516020615b345f395f51905f525f80a3565b80156116bd575f190190565b8115612110570690565b90815160028110615a33575f1981019081116116bd57805b6159a957505050565b82516040516159d281613ea5602082019486888791606093918352602083015260408201520190565b5190209060018101918282116116bd57615a2d926159ef9161597e565b6001600160a01b03615a0182876152a8565b511690615a226001600160a01b03615a1985896152a8565b511691876152a8565b52613efc82866152a8565b806159a0565b505050565b5f516020615b745f395f51905f525c615a5e5760015f516020615b745f395f51905f525d565b633ee5aeb560e01b5f5260045ffd5b916040519163a9059cbb60e01b5f5260018060a01b031660045260245260205f60448180865af19060015f5114821615615acd575b60405215615aad5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b906001811516615ae557823b15153d15161690615aa2565b503d5f823e3d90fd5b5f5b8251811015615b2c576001600160a01b03615b0b82856152a8565b51166001600160a01b03831614615b2457600101615af0565b505050600190565b5050505f9056fea8f52d7b4405100a39ccc552ccc9426d860b1e822c71e97675d1623b49db6babd31b1ab070aba73480b7f863f37fefd98c16862672290e5cadbf1212bf1630cd9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a264697066735822122000a241a2c46c062389f65f399665216c325230efce0e45a4053d88b85f212bfb64736f6c634300081c0033" + "bytecode": "0x60a03461016957601f615c9b38819003918201601f19168301916001600160401b0383118484101761016d5780849260409485528339810103126101695780516001600160a01b038116919082900361016957602001513315610156575f8054336001600160a01b0319821681178355604051949290916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a35f60035568056bc75e2d63100000601b55601c80546001600160c01b0319167007000000000002a3000000000000015180179055610bb8602355801561014757600180546001600160a01b0319169190911790556080526004805460ff191690555f807fa8f52d7b4405100a39ccc552ccc9426d860b1e822c71e97675d1623b49db6bab8180a3615b1990816101828239608051818181611eb30152612c690152f35b63f70949a160e01b5f5260045ffd5b631e4fbdf760e01b5f525f60045260245ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c90816304501eec14614f545750806309adabf514614b485780630a2b338214614a225780630ac04340146143a45780630e84af2314614151578063117f5f921461412b5780631511c3a61461405857806317baaf201461403b578063199efdf4146140115780631a336bfc146139465780631c2c56bc1461391c5780631d9e7295146134e35780631dfa1ac2146134755780631eb30bfe146134315780631f1761d0146133c35780631f83192c146133945780632059a899146132db57806324509220146132be578063246116e6146131ef57806324e168af146131a35780632a9cafbe146131825780632b143f03146131145780632d8d2d631461302457806331823a221461300957806332280eed14612e9357806337d274c914612e605780633a0ea70414612e375780633b0ff6bb14612e1a5780633b6dffe514612dc75780633ce58bba14612d5957806342f7750214612c8c57806343cdaad214612c5257806346bf820a14612be45780634774b52414612b685780634e649ca714612789578063530a9fb114612702578063576a91b3146125d157806358aed7bc146125a9578063595e34111461253b5780635b0283ef146122d35780635bfd7d3a1461221e5780635fec207c14611dfc5780636336894114611d8e578063715018a614611d37578063747a21fe14611d1b57806378737a3914611cf35780637c4596c714611b48578063868276ab14611b2b57806388c2978514611a8a578063894f4dd114611a525780638b1de7be146119e45780638cfbd515146119be5780638da5cb5b14611997578063914b92cd146116cc5780639de2f2a0146115b1578063a120c41a1461158b578063a5031f6414611570578063a7e10e2814611538578063aafebd5014611468578063ac0c7d511461143f578063ac9fc10014611426578063acaf042a146104a5578063adabd801146113f3578063b4b9db4114610f8d578063b593d2d51461138e578063b61998b614611297578063b6f104a81461126d578063b75997701461123e578063b7696bd6146111cd578063b7a0e41c14610f92578063bee3fe1714610f8d578063c0aeb10d14610f1f578063c6e8fc6f14610d65578063c9ad03c814610d2d578063cad783b214610ad5578063cb404a75146109da578063cc279009146109bd578063d6f353a114610995578063d86795ac14610927578063e2852e3f146108fe578063e547118e14610890578063e57c943e14610847578063e6f5e11914610694578063e9c8bb521461066b578063ead1df17146105ce578063f0f95f6414610554578063f2fde38b146104cf578063f34db55e146104a55763f4eed253146103ef575f80fd5b346104a15760203660031901126104a157610408614ff5565b6104106156b9565b60ff60045416601d81101561048d5761047e576001600160a01b0316801561046f576bffffffffffffffffffffffff60a01b6002541617600255600160ff19600454161760045560016003545f516020615a845f395f51905f525f80a3005b63f70949a160e01b5f5260045ffd5b631f85a85960e31b5f5260045ffd5b634e487b7160e01b5f52602160045260245ffd5b5f80fd5b346104a15760203660031901126104a1576004355f526012602052602060405f2054604051908152f35b346104a15760203660031901126104a1576104e8614ff5565b6104f06156b9565b6001600160a01b03168015610541575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b346104a1576105623661505d565b9061056b6156b9565b60035481036105bf5760ff60045416601d81101561048d57601981141590816105b3575b506105a4575f52601260205260405f20555f80f35b63c7a0e6e160e01b5f5260045ffd5b6004915014158361058f565b6347ca2dcf60e01b5f5260045ffd5b346104a1575f3660031901126104a1576105e6615988565b335f52602860205260405f2054801561065d57335f81815260286020526040812055601a5461061f9183916001600160a01b03166159bd565b6040519081527fa09dd3797281514bc434a54ae8b69a6d02ae2fdbfab7c86dc8975163fd0045d060203392a25f5f516020615ac45f395f51905f525d005b621db6b160e21b5f5260045ffd5b346104a1575f3660031901126104a15760ff60045416604051601d82101561048d576020918152f35b346104a15760803660031901126104a15760243560043560028210156104a15760643590604435906001600160a01b03831683036104a15760a09361079b575f818152600f6020908152604080832085845282528083205f196001891b019690961680845295825280832054848452600e83528184208685528352818420878552835281842054858552600c84528285208786528452828520888652845282852054868652600b8552838620888752855283862098865297845282852054958552600d84528285209685529583528184208585529092529091205460ff9182169490939116905b604051941515855260208501521515604084015260608301526080820152f35b5f81815260176020908152604080832085845282528083205f196001891b01969096168084529582528083205484845260168352818420868552835281842087855283528184205485855260148452828520878652845282852088865284528285205486865260138552838620888752855283862098865297845282852054958552601584528285209685529583528184208585529092529091205460ff91821694909391169061077b565b346104a15760403660031901126104a157610860614fdf565b6004355f52600760205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346104a15760203660031901126104a1576108a96156b9565b600354600435036105bf5760ff60045416601d81101561048d576005036108ef57600660ff19600454161760045560066003545f516020615a845f395f51905f525f80a3005b6321012b6760e01b5f5260045ffd5b346104a15760203660031901126104a1576004355f5260208052602060405f2054604051908152f35b346104a15760203660031901126104a1576109406156b9565b600354600435036105bf5760ff60045416601d81101561048d5760160361098657601760ff19600454161760045560176003545f516020615a845f395f51905f525f80a3005b631416b1a360e01b5f5260045ffd5b346104a1575f3660031901126104a1576001546040516001600160a01b039091168152602090f35b346104a1575f3660031901126104a1576020602154604051908152f35b346104a15760203660031901126104a1576004356109f66156b9565b60035481036105bf5760ff60045416601d81101561048d57601903610ac65760025460405163cb404a7560e01b81526004810192909252602090829060249082905f906001600160a01b03165af1908115610abb575f91610a8c575b5015610a7d57601a60ff196004541617600455601a6003545f516020615a845f395f51905f525f80a3005b634b058d6560e11b5f5260045ffd5b610aae915060203d602011610ab4575b610aa6818361512c565b81019061514d565b81610a52565b503d610a9c565b6040513d5f823e3d90fd5b63b52ae37160e01b5f5260045ffd5b346104a15760203660031901126104a157600435610af16156b9565b60035481036105bf5760ff60045416601d81101561048d57601803610d1e57805f52601060205260405f20908154905f926001600160401b0380421616935b838110610b5c57601960ff19600454161760045560196003545f516020615a845f395f51905f525f80a3005b610b66818361517e565b5090815460018301908154915f925f5f925f5b838110610c1b57505050508215610c0d57600211610bfe576020827f9bca595da6d2d1ae0f0ef7abe316afdd467890adf8eb5a2234a8af80f8d151b7928894600388600260019a018a60ff198254161790550155845f526025835260405f20905f52825260405f208a6001600160401b0319825416179055604051908152a201610b30565b6380c2216560e01b5f5260045ffd5b62b5305b60e51b5f5260045ffd5b610c2581836150a8565b90545f8d81526014602090815260408083208b8452825280832060039590951b9390931c6001600160a01b0316808352939052205460ff16610cad575b906001918c5f52601860205260405f2090838060a01b03165f5260205260405f20610c8d8154615197565b90558b5f52601960205260405f20610ca58154615197565b905501610b79565b94610cb790615197565b948b5f52601360205260405f20875f5260205260405f2060018060a01b0382165f5260205260405f20548c5f52601560205260405f20885f5260205260405f20815f5260205260405f2054858111610d11575b5050610c62565b909850935060018f610d0a565b63c4d5525560e01b5f5260045ffd5b346104a15760203660031901126104a1576001600160a01b03610d4e614ff5565b165f526028602052602060405f2054604051908152f35b346104a157610d7336615073565b9160035481036105bf5760ff60045416601d81101561048d57601703610f105781610f01575f818152601160209081526040808320858452825280832033845290915290205460ff1615610ef2576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610abb575f91610ed3575b5015610ec4578215610eb5575f818152601760209081526040808320858452825280832033845290915290205460ff16610ea6575f818152601660209081526040808320858452825280832033808552908352818420879055848452601783528184208685528352818420818552835292819020805460ff191660011790555194855290937f430cf27dfb66aa7260b606dbba6d3d2600983f065b1e30ffcab0e6457f3776c39190a4005b6343a311af60e01b5f5260045ffd5b630e660de560e11b5f5260045ffd5b636936304560e01b5f5260045ffd5b610eec915060203d602011610ab457610aa6818361512c565b84610dfb565b6328e28e8360e01b5f5260045ffd5b6327fda01d60e11b5f5260045ffd5b636582261960e11b5f5260045ffd5b346104a15760203660031901126104a157610f386156b9565b600354600435036105bf5760ff60045416601d81101561048d57600603610f7e57600760ff19600454161760045560076003545f516020615a845f395f51905f525f80a3005b6301876fed60e21b5f5260045ffd5b61508d565b346104a15760203660031901126104a157600435610fae6156b9565b60035481036105bf5760ff60045416601d81101561048d576015036111be575f818152600960205260408120805491426001600160401b031691905b83811061101657601660ff19600454161760045560166003545f516020615a845f395f51905f525f80a3005b6110208183615165565b5090815460018301928354935f945f5f925f5b8381106110b757505050508415610c0d57600211610bfe577fb1becb4dbd2440a1dbd1bac657cc9dbbe112f85f7a99012f71aff4bab4acc5a860208560019660048560038e97018a60ff198254161790550155835f526024825260405f20855f52825260405f20896001600160401b0319825416179055604051908152a301610fea565b6110c181836150a8565b90545f8f8152600c602090815260408083208c8452825280832060039590951b9390931c6001600160a01b031680835293905220548e9060ff1661114b575b90600192915f52601860205260405f2090838060a01b03165f5260205260405f2061112b8154615197565b90558d5f52601960205260405f206111438154615197565b905501611033565b509461115690615197565b948d5f52600b60205260405f20885f5260205260405f2060018060a01b0382165f526020528d60405f2054815f52600d60205260405f208a5f5260205260405f20815f5260205260405f20548681116111b1575b5050611100565b909b50945060015f6111aa565b638f5431c960e01b5f5260045ffd5b346104a15760803660031901126104a15760243560028110156104a157611204606435916004355f52602760205260405f2061500b565b6044355f5260205260405f2080548210156104a157602091611225916150a8565b905460405160039290921b1c6001600160a01b03168152f35b346104a15761124c3661505d565b905f52600660205260405f2080548210156104a157602091611225916150a8565b346104a15760203660031901126104a1576004355f526019602052602060405f2054604051908152f35b346104a15760803660031901126104a157600435602435906001600160401b0382168092036104a157604435906001600160401b038216928383036104a157606435936001600160401b0385168086036104a1576112f36156b9565b8315918215611385575b821561137c575b508115611373575b8115611367575b506113585767ffffffffffffffff60401b91601b55601c54936001600160401b0360801b9060801b16936001600160401b0360c01b16179160401b161717601c555f80f35b631218659f60e01b5f5260045ffd5b61010091501185611313565b8015915061130c565b15915086611304565b831592506112fd565b346104a15760203660031901126104a1576113a76156b9565b60ff60045416601d81101561048d576003036113e45760048035600555805460ff1916811781556003545f516020615a845f395f51905f525f80a3005b631529e88760e31b5f5260045ffd5b346104a15760203660031901126104a1576004355f52601f60205260206001600160401b0360405f205416604051908152f35b346104a15761143d61143736614fbc565b91615537565b005b346104a1575f3660031901126104a15760206001600160401b03601c5460801c16604051908152f35b346104a1575f3660031901126104a1576114806156b9565b60ff60045416601d81101561048d5760010361152957600154604051633c1a477960e11b815230600482015290602090829060249082906001600160a01b03165afa908115610abb575f9161150a575b50156114fb57600260ff19600454161760045560026003545f516020615a845f395f51905f525f80a3005b634f3572c560e01b5f5260045ffd5b611523915060203d602011610ab457610aa6818361512c565b816114d0565b632748c52f60e21b5f5260045ffd5b346104a1576115463661505d565b905f52602560205260405f20905f5260205260206001600160401b0360405f205416604051908152f35b346104a1575f3660031901126104a157602060405160018152f35b346104a15760203660031901126104a15760043560035481036105bf5761143d906153c4565b346104a1576115bf3661505d565b6115c76156b9565b60ff60045416601d81101561048d57600481141590816116c0575b506116b1576003546001810180911161169d5782036105bf576002546001600160a01b031691823b156104a15760405190635a6f53a760e01b825260048201525f81602481865afa8015610abb5761168d575b50813b156104a1575f91602483926040519485938492633f23a7b760e21b845260048401525af18015610abb5761167d575b611672600354615197565b60035561143d61589d565b5f6116879161512c565b80611667565b5f6116979161512c565b82611635565b634e487b7160e01b5f52601160045260245ffd5b63205400e160e21b5f5260045ffd5b601c91501415836115e2565b346104a1576116da36614fbc565b825f9392935260266020526116f28360405f2061500b565b815f5260205260405f2090600282019283549260ff8460c01c168061198a575b1561197b5760ff8460d01c1661196c576001600160401b038460401c1642111561195d5760ff60d01b19909316600160d01b17909355600182015491546001600160a01b03165f90815260286020526040902080546117ab9460049490929161177b91906151a5565b9055805f5260276020526117928560405f2061500b565b825f526020526117b260405f2060405195868092615214565b038561512c565b600154604051631bad9e0560e11b81529360209185919082906001600160a01b03165afa928315610abb575f93611929575b505f5b84518110156118f4576001545f91906020906001600160a01b0390811690606490611812858b6151b2565b51166040519586938492637dd1602d60e11b845260048401528a60248401527814cd17d1949154d217d4d55091d493d55417d512535153d555603a1b60448401525af18015610abb575f906118c2575b6001925084845f516020615aa45f395f51905f5260606001600160a01b0361188a878d6151b2565b511694604051907814cd17d1949154d217d4d55091d493d55417d512535153d555603a1b82528b60208301526040820152a4016117e7565b506020823d82116118ec575b816118db6020938361512c565b810103126104a15760019151611862565b3d91506118ce565b507ff42fd7a6ed54d90eb785299babf26fd989424f01778ddc07a710b65b7effc0ca6020866119266040518092615104565ba3005b9092506020813d602011611955575b816119456020938361512c565b810103126104a1575191856117e4565b3d9150611938565b63aa161f2160e01b5f5260045ffd5b631a3b0bef60e01b5f5260045ffd5b630a18626760e21b5f5260045ffd5b5060ff8460c81c16611712565b346104a1575f3660031901126104a1575f546040516001600160a01b039091168152602090f35b346104a15760203660031901126104a15760043560035481036105bf5761143d90615254565b346104a15760203660031901126104a1576119fd6156b9565b600354600435036105bf5760ff60045416601d81101561048d57600703611a4357600860ff19600454161760045560086003545f516020615a845f395f51905f525f80a3005b63ca5a3b5560e01b5f5260045ffd5b346104a157611a603661505d565b905f52602460205260405f20905f5260205260206001600160401b0360405f205416604051908152f35b346104a15760203660031901126104a157600435611aa66156b9565b60ff60045416601d81101561048d5760048114159081611b1f575b506116b1576003546001810180911161169d5781036105bf576002546001600160a01b031690813b156104a1575f90602460405180948193635a6f53a760e01b835260048301525afa8015610abb5761167d57611672600354615197565b601c9150141582611ac1565b346104a1575f3660031901126104a1576020602254604051908152f35b346104a157611b5636615073565b9160035481036105bf5760ff60045416601d81101561048d57601403611ce457805f52600960205260405f2054821015611cd5575f818152600a60209081526040808320858452825280832033845290915290205460ff1615610ef2576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610abb575f91611cb6575b5015610ec4578215611ca7575f818152600f60209081526040808320858452825280832033845290915290205460ff16611c98575f818152600e60209081526040808320858452825280832033808552908352818420879055848452600f83528184208685528352818420818552835292819020805460ff191660011790555194855290937fc33cb04355b8f36a3e3860fb823d630cc37a5833cf62828f742e70bdd3751eac9190a4005b63432cb74f60e11b5f5260045ffd5b63d5d0c92560e01b5f5260045ffd5b611ccf915060203d602011610ab457610aa6818361512c565b84611bed565b633b554d2360e11b5f5260045ffd5b637100347160e01b5f5260045ffd5b346104a1575f3660031901126104a1576002546040516001600160a01b039091168152602090f35b346104a1575f3660031901126104a157602060405161012c8152f35b346104a1575f3660031901126104a157611d4f6156b9565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346104a15760203660031901126104a157611da76156b9565b600354600435036105bf5760ff60045416601d81101561048d57601303611ded57601460ff19600454161760045560146003545f516020615a845f395f51905f525f80a3005b63076b0ff760e01b5f5260045ffd5b346104a15760203660031901126104a15760043560035481036105bf5760ff60045416601d81101561048d5760060361220f576001546040516310ab556b60e21b81523360048201526001600160a01b0390911690602081602481855afa908115610abb575f916121f0575b5015610ec4575f82815260076020908152604080832033845290915290205460ff166121e157815f52600660205261012c60405f205410156121d257604051634ab989d560e01b81527f00000000000000000000000000000000000000000000000000000000000000006004820152602081602481855afa908115610abb575f916121a0575b5060405163f2d679bf60e01b815290602082600481865afa918215610abb575f9261216c575b50801580158091612163575b611fd7575b50505050805f526006602052611f3e3360405f206151c6565b5f8181526007602090815260408083203384529091529020805460ff19166001908117909155546001600160a01b0316803b156104a1575f80916024604051809481936347e91db160e01b83523360048401525af18015610abb57611fc7575b5033907f88f3e7b00fb6a3104c7d2c8d3ff669b012e5724bd38e50559cb41d177a3a8ba15f80a3005b5f611fd19161512c565b81611f9e565b6040516303d3b32360e51b815233600482015291602083602481885afa928315610abb575f9361212f575b5081612125575b50612116578161201a575b80611f25565b604051631bad9e0560e11b815290602082600481875afa918215610abb575f926120e2575b5081156120ce5760249260209261205692046150f1565b926040519283809263b998328360e01b82523360048301525afa908115610abb575f9161209c575b50101561208d57818080612014565b634f21e5ed60e11b5f5260045ffd5b90506020813d6020116120c6575b816120b76020938361512c565b810103126104a157518361207e565b3d91506120aa565b634e487b7160e01b5f52601260045260245ffd5b9091506020813d60201161210e575b816120fe6020938361512c565b810103126104a15751908561203f565b3d91506120f1565b631931d5eb60e01b5f5260045ffd5b9050811085612009565b9092506020813d60201161215b575b8161214b6020938361512c565b810103126104a157519186612002565b3d915061213e565b50821515611f20565b9091506020813d602011612198575b816121886020938361512c565b810103126104a157519084611f14565b3d915061217b565b90506020813d6020116121ca575b816121bb6020938361512c565b810103126104a1575183611eee565b3d91506121ae565b63083a2c5d60e01b5f5260045ffd5b6306d4010b60e21b5f5260045ffd5b612209915060203d602011610ab457610aa6818361512c565b83611e68565b6331fe69e760e21b5f5260045ffd5b346104a15760203660031901126104a15760043561223a6156b9565b60035481036105bf5760ff60045416601d81101561048d57600a036122c45761227a6001600160401b03601c5460801c166001600160401b0343166150d1565b905f52601f6020526001600160401b0360405f2091166001600160401b0319825416179055600b60ff196004541617600455600b6003545f516020615a845f395f51905f525f80a3005b630d1f756d60e31b5f5260045ffd5b346104a1576122e136614f9e565b60039391935482036105bf5760ff60045416601d81101561048d5760180361252c5782610f01575f828152601160209081526040808320868452825280832033845290915290205460ff1615610ef2576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610abb575f9161250d575b5015610ec4575f828152601760209081526040808320868452825280832033845290915290205460ff16156124fe575f828152601460209081526040808320868452825280832033845290915290205460ff166124ef5783156124e0576040805160208101868152918101929092523360608301526080820183905290600160a082015260c0808201859052815261240460e08261512c565b5190205f8281526016602090815260408083208684528252808320338452909152902054036124d1575f818152601460209081526040808320858452825280832033808552908352818420805460ff1916600117905584845260138352818420868552835281842081855283529281902086905551858152849184917ff381116b332b24a3860f90e240a69b31a4eb09c27b59cc54e049aad99897e97c9190a45f52601560205260405f20905f5260205260405f20905f5260205260405f206124cd8154615197565b9055005b634407f1db60e01b5f5260045ffd5b63010e92c160e11b5f5260045ffd5b636e479ac960e01b5f5260045ffd5b6343d466b760e11b5f5260045ffd5b612526915060203d602011610ab457610aa6818361512c565b8561236b565b63e396ca2760e01b5f5260045ffd5b346104a15760203660031901126104a1576125546156b9565b600354600435036105bf5760ff60045416601d81101561048d5760140361259a57601560ff19600454161760045560156003545f516020615a845f395f51905f525f80a3005b63e89736d560e01b5f5260045ffd5b346104a1575f3660031901126104a157601a546040516001600160a01b039091168152602090f35b346104a1576125df3661505d565b9060035481116105bf57805f52600960205260405f20548210156126f357600191612614915f52600960205260405f20615165565b50805490600281019060ff60038201541661264560048301549261263e6040518098819301615214565b038661512c565b604051948580966020865491828152019081965f5260205f20905f5b8181106126da575050509161267d61269392602094038961512c565b60405196875260a08388015260a0870190615021565b958587036040870152519586815201925f955b8087106126c25750508394501515606084015260808301520390f35b909360208060019287518152019501960195906126a6565b825484528a945060209093019260019283019201612661565b631935ca8d60e01b5f5260045ffd5b346104a1576127103661505d565b9081610f015760035481116105bf57600191612736915f52601060205260405f2061517e565b5080549060ff6002820154169061275c6003820154916117ab6040518097819301615214565b6127786040519485948552608060208601526080850190615021565b911515604084015260608301520390f35b346104a15761279736614f6e565b926127a06156b9565b805f5260266020526127b58360405f2061500b565b825f5260205260405f2060028101805460ff8160c01c1680612b5b575b1561197b5760ff8160d01c1661196c5760ff60d01b1916600160d01b1790558415612b2457600181015490546001600160a01b03165f90815260286020526040902080549091612821916151a5565b9055600283101561048d576004612864948415805f14612b0357835f52600960205261286b60016128558760405f20615165565b50015b60405198898092615214565b038861512c565b8015612ae357835f526009602052826128878660405f20615165565b500154915b600154604051631bad9e0560e11b81529460209186919082906001600160a01b03165afa938415610abb575f94612aaf575b505f5b8851811015612a6f578215612a2e57855f52600b60205260405f20875f5260205260405f2060018060a01b036128f7838c6151b2565b511660018060a01b03165f526020528360405f20545b03612a26576001545f91906020906001600160a01b0390811690606490612934858f6151b2565b51166040519586938492637dd1602d60e11b845260048401528b602484015275299a2fa4a72b20a624a22fa0a3a3a922a3a0aa24a7a760511b60448401525af18015610abb5788888c945f936129df575b505f516020615aa45f395f51905f5260606001966129a987898060a01b03926151b2565b5116946040519075299a2fa4a72b20a624a22fa0a3a3a922a3a0aa24a7a760511b82528c60208301526040820152a45b016128c1565b94505050506020823d8211612a1e575b816129fc6020938361512c565b810103126104a157905189919088885f516020615aa45f395f51905f52612985565b3d91506129ef565b6001906129d9565b855f52601360205260405f20875f5260205260405f2060018060a01b03612a55838c6151b2565b511660018060a01b03165f526020528360405f205461290d565b509395925050507f9086299cfbdc88752f050229a9e7b3563c343b687b7fa5f7ace411c049301d1a9294505b612aaa60405192839283615111565b0390a3005b9093506020813d602011612adb575b81612acb6020938361512c565b810103126104a1575192886128be565b3d9150612abe565b835f5260106020526003612afa8660405f2061517e565b5001549161288c565b835f52601060205261286b6001612b1d8760405f2061517e565b5001612858565b92612b5660017f9086299cfbdc88752f050229a9e7b3563c343b687b7fa5f7ace411c049301d1a9496939501546156df565b612a9b565b5060ff8160c81c166127d2565b346104a15760203660031901126104a157600435612b846156b9565b80158015612bd9575b612bca5760407f6aa2abf97a85127b2d7e0a4a90f5bf57c815685cdf8a07985043f5298fd6190c91602354908060235582519182526020820152a1005b631e0e5cfb60e11b5f5260045ffd5b506127108111612b8d565b346104a15760203660031901126104a157612bfd6156b9565b600354600435036105bf5760ff60045416601d81101561048d57601703612c4357601860ff19600454161760045560186003545f516020615a845f395f51905f525f80a3005b63106ff71f60e01b5f5260045ffd5b346104a1575f3660031901126104a15760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346104a15760203660031901126104a157600435612ca86156b9565b60035481036105bf5760ff60045416601d81101561048d57601b03612d4a5760018060a01b036002541690805f52601960205260405f2054823b156104a15760445f9283604051958694859363e13b7b3160e01b8552600485015260248401525af18015610abb57612d3a575b601c60ff196004541617600455601c6003545f516020615a845f395f51905f525f80a3005b5f612d449161512c565b80612d15565b6303858c7f60e01b5f5260045ffd5b346104a15760203660031901126104a157612d726156b9565b600354600435036105bf5760ff60045416601d81101561048d57600f03612db857601060ff19600454161760045560106003545f516020615a845f395f51905f525f80a3005b636487147760e11b5f5260045ffd5b346104a15760203660031901126104a1576004355f526006602052612e16612dfb612e0260405f2060405192838092615214565b038261512c565b604051918291602083526020830190615021565b0390f35b346104a1575f3660031901126104a1576020600354604051908152f35b346104a1575f3660031901126104a15760206001600160401b03601c5460401c16604051908152f35b346104a15760203660031901126104a1576004355f52601d60205260206001600160401b0360405f205416604051908152f35b346104a15760203660031901126104a157600435612eaf6156b9565b600354808210908115612fe1575b5015612fd257805f52600860205260ff60405f205416612fc357805f52600860205260405f20600160ff19825416179055805f52600660205260405f2080545f5b818110612f4e576002546001600160a01b031684813b156104a1575f9160248392604051948593849263224c19bb60e01b845260048401525af18015610abb57612f4457005b5f61143d9161512c565b6001546001600160a01b031690612f6581856150a8565b905460039190911b1c6001600160a01b0316823b156104a1575f92602484926040519586938492631bf8593960e31b845260048401525af1918215610abb57600192612fb3575b5001612efe565b5f612fbd9161512c565b85612fac565b630c3dc41b60e01b5f5260045ffd5b631e5b133160e21b5f5260045ffd5b9050811480612ff1575b82612ebd565b5060ff60045416601d81101561048d57601c14612feb565b346104a1575f3660031901126104a157602060405160028152f35b346104a15760203660031901126104a1576004356130406156b9565b60035481036105bf5760ff60045416601d81101561048d57600c03613105576002545f828152602080805260408083205490516363fd7a1160e11b8152600481019590955260248501529183916044918391906001600160a01b03165af1908115610abb575f916130e6575b50156130d757600d60ff196004541617600455600d6003545f516020615a845f395f51905f525f80a3005b63611d8f8760e01b5f5260045ffd5b6130ff915060203d602011610ab457610aa6818361512c565b816130ac565b63dbbb808560e01b5f5260045ffd5b346104a15760203660031901126104a15761312d6156b9565b600354600435036105bf5760ff60045416601d81101561048d5760080361317357600960ff19600454161760045560096003545f516020615a845f395f51905f525f80a3005b63768c7a4d60e11b5f5260045ffd5b346104a15760203660031901126104a15761319b6156b9565b600435602255005b346104a15760203660031901126104a1576131bc614ff5565b6131c46156b9565b6001600160a01b0316801561046f576bffffffffffffffffffffffff60a01b601a541617601a555f80f35b346104a15760403660031901126104a157600435602435908115158092036104a1576132196156b9565b60035481036105bf5760ff60045416601d81101561048d57600d036132af576002546001600160a01b031691823b156104a15760445f928360405195869485936312308b7360e11b8552600485015260248401525af18015610abb5761329f575b600e60ff196004541617600455600e6003545f516020615a845f395f51905f525f80a3005b5f6132a99161512c565b8061327a565b630c14724760e31b5f5260045ffd5b346104a1575f3660031901126104a1576020602354604051908152f35b346104a1576132fe6132ec36614fbc565b92915f52602660205260405f2061500b565b905f5260205261012060405f2060018060a01b038154169060ff600182015491600360028201549101549260405194855260208501526001600160401b03811660408501526001600160401b038160401c1660608501526001600160401b038160801c166080850152818160c01c16151560a0850152818160c81c16151560c085015260d01c16151560e0830152610100820152f35b346104a15760203660031901126104a1576004355f526008602052602060ff60405f2054166040519015158152f35b346104a15760203660031901126104a1576133dc6156b9565b600354600435036105bf5760ff60045416601d81101561048d5760090361342257600a60ff196004541617600455600a6003545f516020615a845f395f51905f525f80a3005b63379ffc8d60e11b5f5260045ffd5b346104a15760403660031901126104a15761344a614fdf565b6004355f52601860205260405f209060018060a01b03165f52602052602060405f2054604051908152f35b346104a15760203660031901126104a15761348e6156b9565b600354600435036105bf5760ff60045416601d81101561048d57600e036134d457600f60ff196004541617600455600f6003545f516020615a845f395f51905f525f80a3005b63c827e84760e01b5f5260045ffd5b346104a1576134f136614fbc565b90916134fb615988565b80158015613911575b6105bf576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610abb575f916138f2575b5015610ec457600283101561048d578261389b57805f52600960205260405f2054821015611cd557805f52600960205260ff60036135838460405f20615165565b50015416815f52602460205260405f20835f526020526001600160401b0360405f205416905b1561388c576001600160401b036135c6601c5492828416906150d1565b16421161387d57815f5260266020526135e28460405f2061500b565b835f5260205260018060a01b0360405f20541661386e5761361b6001600160401b03601b549260801c166001600160401b0343166150d1565b906040519161012083018381106001600160401b0382111761385a57604052338352602083019182526040830190426001600160401b0316825260608401905f825260808501906001600160401b0316815260a085015f815260c08601915f835260e08701935f85526101008801965f8852895f5260266020528b60405f20906136a49161500b565b5f8c815260209190915260409081902099518a546001600160a01b0319166001600160a01b0391909116178a55905160018a0155955160028901805492516fffffffffffffffffffffffffffffffff199093166001600160401b03909216919091179190961b67ffffffffffffffff60401b1617855551845467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b16178455518354915161ffff60c01b1990921690151560c01b60ff60c01b161790151560c81b60ff60c81b1617825551815460ff60d01b191690151560d01b60ff60d01b1617905551600390910155601a54601b546040516323b872dd60e01b5f52336004523060245260449190915293906001600160a01b0316805a60645f806020948194f15f5160011481161561383b575b856040525f6060521561382957506137ec84601b5492615104565b60208401527f4c0dd383a6eb2a2f4b8f2a967e4365ca69b2d0012887044c0c509fa0ce676f2360403394a45f5f516020615ac45f395f51905f525d005b635274afe760e01b5f5260045260245ffd5b600181151661385157813b15153d1516166137d1565b853d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b633624f00960e21b5f5260045ffd5b63a4872ddd60e01b5f5260045ffd5b6355bc0ebf60e11b5f5260045ffd5b805f52601060205260405f2054821015611cd557805f52601060205260ff60026138c88460405f2061517e565b50015416815f52602560205260405f20835f526020526001600160401b0360405f205416906135a9565b61390b915060203d602011610ab457610aa6818361512c565b84613542565b506003548111613504565b346104a15760203660031901126104a1576004355f526009602052602060405f2054604051908152f35b346104a15760203660031901126104a1576004356139626156b9565b60035481036105bf5760ff60045416601d81101561048d5760120361400257805f52601e60205260405f205490805f52600660205260405f209081546139a781615886565b6139b4604051918261512c565b818152601f196139c383615886565b013660208301376001545f94906001600160a01b0316855b848110613f6e5750506139ed85615886565b946139fb604051968761512c565b808652613a0a601f1991615886565b013660208701375f915f5b848110613f24578787876003825110613f1557602492613a6260405160208101908382526720a3a3afa0a2222960c11b604082015260288152613a5960488261512c565b519020846158d8565b6002546040516327416acd60e21b815260048101849052945f9186919082906001600160a01b03165afa938415610abb575f94613e83575b506003845110613e745760405160208101918252660828e8ebe9288b60cb1b604082015260278152613acd60478261512c565b51902083515f19810190811161169d57805b613de85750505f915f915f935b6003810180821161169d57835181111580613d9f575b15613c7e57825f52600960205260405f208054600160401b81101561385a57613b3091600182018155615165565b5091613b3b87615197565b96835560018301905f5b60038110613c0f5750505060039182613b5f878a51615207565b10613bfc575b600281015f5b848110613bac57505090613ba6929154847fb9202018de9f7eba7cdf3f805001edb29b6629a2fd89f5bd9c667a9d99450f655f80a3946151a5565b92613aec565b613bbf613bb9828a6151a5565b8b6151b2565b5190825491600160401b83101561385a57613be18360018095018655856150a8565b819291549060031b91821b915f19901b191617905501613b6b565b9150613c09858851615207565b91613b65565b600190613c326001600160a01b03613c2a613bb984876151a5565b5116856151c6565b865f52600a60205260405f2086545f5260205260405f20828060a01b03613c5c613bb984876151a5565b5116838060a01b03165f5260205260405f208260ff1982541617905501613b45565b5082906003613c8e828451615207565b1015613cba575b601360ff19600454161760045560136003545f516020615a845f395f51905f525f80a3005b825f52601060205260405f208054600160401b81101561385a57613ce39160018201815561517e565b50915f83555f9160018401925b60038110613d2a575050505054907fcba6d1415af496acc567dab38af44a786626bc3e3c6d0057a8b34329bb31e6ef5f80a3808080613c95565b600190613d536001600160a01b03613d4b613d4584876151a5565b876151b2565b5116866151c6565b865f52601160205260405f2086545f5260205260405f20828060a01b03613d7d613d4584876151a5565b5116838060a01b03165f5260205260405f208260ff1982541617905501613cf0565b506003850180861161169d578751811190811591613dbe575b50613b02565b90506002860180871161169d57885110159081613ddd575b5088613db8565b905087511088613dd6565b8451604051613e1f81613e11602082019486888791606093918352602083015260408201520190565b03601f19810183528261512c565b51902090600181019182821161169d57613e6e92613e3c916158ce565b613e4681886151b2565b5190613e5d613e55848a6151b2565b5191896151b2565b52613e6882886151b2565b526158c2565b80613adf565b634607f5d560e01b5f5260045ffd5b9093503d805f833e613e95818361512c565b8101906020818303126104a1578051906001600160401b0382116104a157019080601f830112156104a1578151613ecb81615886565b92613ed9604051948561512c565b81845260208085019260051b8201019283116104a157602001905b828210613f05575050509284613a9a565b8151815260209182019101613ef4565b631571768360e31b5f5260045ffd5b80613f31600192846151b2565b51613f3d575b01613a15565b613f4781856150a8565b838060a01b0391549060031b1c16613f68613f6187615197565b968a6151b2565b52613f37565b613f7881846150a8565b90546040516310ab556b60e21b815260039290921b1c6001600160a01b03166004820152602081602481865afa908115610abb575f91613fe4575b50613fbe82866151b2565b8115159052613fd0575b6001016139db565b95613fdc600191615197565b969050613fc8565b613ffc915060203d8111610ab457610aa6818361512c565b89613fb3565b6366d7bd4760e01b5f5260045ffd5b346104a15760203660031901126104a1576004355f52601e602052602060405f2054604051908152f35b346104a1575f3660031901126104a1576020601b54604051908152f35b346104a1575f3660031901126104a1576140706156b9565b60ff60045416601d81101561048d5760020361411d57600154600254604051633c1a477960e11b81526001600160a01b0391821660048201529160209183916024918391165afa908115610abb575f916140fe575b50156140ef57600360ff196004541617600455600380545f516020615a845f395f51905f525f80a3005b63f831404160e01b5f5260045ffd5b614117915060203d602011610ab457610aa6818361512c565b816140c5565b62bf1abb60e31b5f5260045ffd5b346104a1575f3660031901126104a15760206001600160401b03601c5416604051908152f35b346104a15761415f36614f9e565b60039391935482036105bf5760ff60045416601d81101561048d5760150361439557815f52600960205260405f2054831015611cd5575f828152600a60209081526040808320868452825280832033845290915290205460ff1615610ef2576001546040516310ab556b60e21b815233600482015290602090829060249082906001600160a01b03165afa908115610abb575f91614376575b5015610ec4575f828152600f60209081526040808320868452825280832033845290915290205460ff1615614367575f828152600c60209081526040808320868452825280832033845290915290205460ff166124ef5783156124e05760408051602081018681529181019290925233606083015260808201839052905f60a082015260c0808201859052815261429060e08261512c565b5190205f828152600e60209081526040808320868452825280832033845290915290205403614358575f818152600c60209081526040808320858452825280832033808552908352818420805460ff19166001179055848452600b8352818420868552835281842081855283529281902086905551858152849184917e4ff64f8cc2f88940c535cfe6face7248dc5ebf1faba271d2f8052017e747c79190a45f52600d60205260405f20905f5260205260405f20905f5260205260405f206124cd8154615197565b635130b96560e11b5f5260045ffd5b632ccbe95160e01b5f5260045ffd5b61438f915060203d602011610ab457610aa6818361512c565b856141f8565b636f5ecfff60e11b5f5260045ffd5b346104a15760203660031901126104a1576004356143c06156b9565b60035481036105bf5760ff60045416601d81101561048d57601a03614a1357600154604051631bad9e0560e11b815290602090829060049082906001600160a01b03165afa908115610abb575f916149e1575b50612710614423602354836150f1565b0491805f52600960205260405f208054905f5b82811061471857505050805f52601060205260405f20918254925f5b84811061447e57601b60ff196004541617600455601b6003545f516020615a845f395f51905f525f80a3005b614488818361517e565b508054600360018301920154907320a3a3afaa192fa727afa9aaa126a4a9a9a4a7a760611b734147475f54325f4241445f434f4e53454e53555360601b8454945f5b8681106144e05750505050505050600101614452565b8c6144eb82846150a8565b90545f8e81526014602090815260408083208b8452825280832060039590951b9390931c6001600160a01b0316808352939052205490929060ff1661461b57868d83156146015760209150608460018060a01b0360015416945f6040519687948593630b6f9cd360e11b85528a600486015260248501528c604485015260648401525af1928315610abb578f928e89915f966145b8575b505f516020615aa45f395f51905f52600195965b604080518c8152602081018a905290810191909152606090a45b0190506144ca565b9450505092506020823d82116145f9575b816145d66020938361512c565b810103126104a1579051918e9190878e5f516020615aa45f395f51905f52614582565b3d91506145c9565b9060019394915f516020615aa45f395f51905f525f614596565b90918c5f52601360205260405f20875f5260205260405f2060018060a01b0382165f526020528760405f205403614656575b506001906145b0565b91505f9060208c606460018060a01b0360015416916040519586938492637dd1602d60e11b845289600485015260248401528a60448401525af1928315610abb578f928e8e958a925f916146d7575b50604080518a815260208101989098528701529394600194905f516020615aa45f395f51905f5290606090a49061464d565b955050505092506020823d8211614710575b816146f66020938361512c565b810103126104a15790518b928f929188908f9060016146a5565b3d91506146e9565b6147228183615165565b50805460046001830192015490825492895f5b85811061474a57505050505050600101614436565b61475481846150a8565b90545f8c8152600c60209081526040808320898452825280832060039590951b9390931c6001600160a01b0316808352939052205460ff166148b157848b841561489a57600154604051630b6f9cd360e11b81526004810185905260248101969096527320a3a3afaa18afa727afa9aaa126a4a9a9a4a7a760611b6044870152606486019190915260209150849060849082905f906001600160a01b03165af18015610abb578b86918f955f91614850575b506001955f516020615aa45f395f51905f52915b604080517320a3a3afaa18afa727afa9aaa126a4a9a9a4a7a760611b81526020810193909352820152606090a45b018a90614735565b95505050506020833d8211614892575b8161486d6020938361512c565b810103126104a157848b5f516020615aa45f395f51905f528f60019651915095614806565b3d9150614860565b5f516020615aa45f395f51905f526001955f61481a565b9150895f52600b60205260405f20845f5260205260405f2060018060a01b0383165f526020528460405f2054036148ec575b60019150614848565b600154604051637dd1602d60e11b815260048101849052602481018d9052734147475f54315f4241445f434f4e53454e53555360601b60448201529290602090849060649082905f906001600160a01b03165af18015610abb578b86918e955f91614997575b5060408051734147475f54315f4241445f434f4e53454e53555360601b815260208101979097528601526001945f516020615aa45f395f51905f5290606090a46148e3565b95505050506020833d82116149d9575b816149b46020938361512c565b810103126104a157848b5f516020615aa45f395f51905f528e60019651915095614952565b3d91506149a7565b90506020813d602011614a0b575b816149fc6020938361512c565b810103126104a1575182614413565b3d91506149ef565b6360cd0e9960e01b5f5260045ffd5b346104a15760203660031901126104a157600435614a3e6156b9565b60035481036105bf5760ff60045416601d81101561048d57601003614b39576002546040516356af71c960e11b81526004810183905290602090829060249082905f906001600160a01b03165af1908115610abb575f91614b1a575b5015614b0b57614ac16001600160401b03601c5460801c166001600160401b0343166150d1565b905f52601d6020526001600160401b0360405f2091166001600160401b0319825416179055601160ff19600454161760045560116003545f516020615a845f395f51905f525f80a3005b6307fb097360e41b5f5260045ffd5b614b33915060203d602011610ab457610aa6818361512c565b82614a9a565b63183dd1d160e21b5f5260045ffd5b346104a157614b5636614f6e565b92614b5f6156b9565b805f526026602052614b748360405f2061500b565b825f5260205260405f2060018060a01b0381541615614f45576002810190815460ff8160c01c16614f365761ffff60c01b191686151560c81b60ff60c81b1617600160c01b1782558515614ef15760030190815415614ee257614c1990614bf26001600160401b03601c5460401c166001600160401b0342166150d1565b67ffffffffffffffff60401b82549160401b169067ffffffffffffffff60401b1916179055565b54600284101561048d5761263e91614c806117ab9286155f14614ec157825f526009602052614c5f6001614c508860405f20615165565b50015b60405195868092615214565b825f526006602052614c7a60405f2060405196878092615214565b846158d8565b60405193614c8f60808661512c565b60038552602085019160603684375f975f5b8651811080614eb7575b15614d8657614ccd866001600160a01b03614cc6848b6151b2565b5116615a3e565b1580614d14575b90614ce291614ce757615197565b614ca1565b6001600160a01b03614cf9828a6151b2565b5116614d0e614d078d615197565b9c8b6151b2565b52615197565b506001546001600160a01b039081169190602090614d32838b6151b2565b51166024604051809581936310ab556b60e21b835260048301525afa8015610abb57614ce2925f91614d68575b50909150614cd4565b614d80915060203d8111610ab457610aa6818361512c565b8c614d5f565b50945094919660039196935010613f1557835f526027602052614dac8260405f2061500b565b855f5260205260405f20958151906001600160401b03821161385a57600160401b821161385a578754828955808310614e91575b50965f5260205f20965f5b828110614e745750505084955093837f1aa282aabded606592d2cccbd78439ed03820b25543d6161b213b4b8c9651993614e627f5f2ed0b8a76b3e4bf8076e2e31d83602fef08c30bdb2b069d1db259c176e63ef9697604051918291614e518389615104565b604060208401526040830190615021565b0390a3612aaa60405192839283615111565b81516001600160a01b0316818a0155602090910190600101614deb565b885f528260205f2091820191015b818110614eac5750614de0565b5f8155600101614e9f565b5060038a10614cab565b825f526010602052614c5f6001614edb8860405f2061517e565b5001614c53565b632d5720c760e11b5f5260045ffd5b815460ff60d01b1916600160d01b1790915560010154919390927f5f2ed0b8a76b3e4bf8076e2e31d83602fef08c30bdb2b069d1db259c176e63ef92612b56906156df565b63450cfd7f60e11b5f5260045ffd5b63167f349d60e11b5f5260045ffd5b346104a1575f3660031901126104a1576020906005548152f35b60809060031901126104a1576004359060243560028110156104a157906044359060643580151581036104a15790565b60809060031901126104a15760043590602435906044359060643590565b60609060031901126104a1576004359060243560028110156104a1579060443590565b602435906001600160a01b03821682036104a157565b600435906001600160a01b03821682036104a157565b90600281101561048d575f5260205260405f2090565b90602080835192838152019201905f5b81811061503e5750505090565b82516001600160a01b0316845260209384019390920191600101615031565b60409060031901126104a1576004359060243590565b60609060031901126104a157600435906024359060443590565b346104a1575f3660031901126104a157602060405160038152f35b80548210156150bd575f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b906001600160401b03809116911601906001600160401b03821161169d57565b8181029291811591840414171561169d57565b90600282101561048d5752565b909291602090615125836040810196615104565b1515910152565b90601f801991011681019081106001600160401b0382111761385a57604052565b908160209103126104a1575180151581036104a15790565b80548210156150bd575f52600560205f20910201905f90565b80548210156150bd575f5260205f209060021b01905f90565b5f19811461169d5760010190565b9190820180921161169d57565b80518210156150bd5760209160051b010190565b8054600160401b81101561385a576151e3916001820181556150a8565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b9190820391821161169d57565b90602082549182815201915f5260205f20905f5b8181106152355750505090565b82546001600160a01b0316845260209093019260019283019201615228565b805f52601f6020526001600160401b0360405f20541680156153b557815f526020805260405f20546153a657804311156153975740801561531c57604051602081019182528260408201526210555160ea1b6060820152604381526152ba60638261512c565b5190205f828152602080805260409182902083905590519182527fe53c25b43758bf910ba120923c7295303dcbc2369273c7985227b00293f3b56991a2600c60ff196004541617600455600c6003545f516020615a845f395f51905f525f80a3565b5061533e6001600160401b03601c5460801c166001600160401b0343166150d1565b5f828152601f6020908152604091829020805467ffffffffffffffff19166001600160401b0394909416938417905590519182527f7e22e73285f299179a84bb3388a9dd94b3eac27a6de162455a42ee6ce6706f9491a2565b63de3f681d60e01b5f5260045ffd5b638d10dfef60e01b5f5260045ffd5b63906135ab60e01b5f5260045ffd5b805f52601d6020526001600160401b0360405f205416801561552857815f52601e60205260405f2054615519578043111561550a5740801561548f57604051602081019182528260408201526241474760e81b60608201526043815261542b60638261512c565b5190205f828152601e602090815260409182902083905590519182527f9dcaca5280e612f442cef14eea003bbece818a3fde40db4dc4a3887682aa07b691a2601260ff19600454161760045560126003545f516020615a845f395f51905f525f80a3565b506154b16001600160401b03601c5460801c166001600160401b0343166150d1565b5f828152601d6020908152604091829020805467ffffffffffffffff19166001600160401b0394909416938417905590519182527fbed5c9fb50b85a70b1ea0ab5bf64bbf34ba76521149788c3873abaf955d545c291a2565b632f9cb5e160e21b5f5260045ffd5b634653993f60e01b5f5260045ffd5b631c237c4160e31b5f5260045ffd5b90815f52602660205261554d8160405f2061500b565b835f5260205260405f209060018060a01b0382541615614f4557600282019081549260ff8460c01c16614f36576003019283546156aa576001600160401b039060801c168043111561569b574091821561561c5750600281101561048d577ff1ac2abd9bfa2c470babc8a68c69061d1661d798dedc05ba6f20a5fdb6bfe51592604092835160208101918252868582015260ff60f81b8460f81b1660608201528760618201526061815261560260818261512c565b51902080915561561483518093615104565b6020820152a3565b7f2bee25e87a4aab24a6e926580a5c7026c624938721cc23acc05733fe7a4deebd9350604092506001600160401b039061568a61566283601c5460801c168443166150d1565b825467ffffffffffffffff60801b191660809190911b67ffffffffffffffff60801b16178255565b5460801c1661561483518093615104565b63221198d360e11b5f5260045ffd5b63d6e8b91560e01b5f5260045ffd5b5f546001600160a01b031633036156cc57565b63118cdaa760e01b5f523360045260245ffd5b5f908015615882578060011c916156f68383615207565b601a546001600160a01b031693909190843b156104a1575f94602486926040519788938492630852cd8d60e31b845260048401525af1938415610abb5760049461586f575b50600154604051639958065160e01b81529460209186919082906001600160a01b03165afa93841561586257819461581a575b506001600160a01b038416156157a65750601a546157a19361579992916001600160a01b03166159bd565b6021546151a5565b602155565b601a54929350916001600160a01b031690813b15615816578291602483926040519485938492630852cd8d60e31b845260048401525af1801561580b576157f3575b50506157a190615799565b6157fe82809261512c565b61580857806157e8565b80fd5b6040513d84823e3d90fd5b8280fd5b9093506020813d60201161585a575b816158366020938361512c565b8101031261585657516001600160a01b038116810361585657925f61576e565b8380fd5b3d9150615829565b50604051903d90823e3d90fd5b61587b91505f9061512c565b5f5f61573b565b5050565b6001600160401b03811161385a5760051b60200190565b600560ff19600454161760045560056003545f516020615a845f395f51905f525f80a3565b801561169d575f190190565b81156120ce570690565b90815160028110615983575f19810190811161169d57805b6158f957505050565b825160405161592281613e11602082019486888791606093918352602083015260408201520190565b51902090600181019182821161169d5761597d9261593f916158ce565b6001600160a01b0361595182876151b2565b5116906159726001600160a01b0361596985896151b2565b511691876151b2565b52613e6882866151b2565b806158f0565b505050565b5f516020615ac45f395f51905f525c6159ae5760015f516020615ac45f395f51905f525d565b633ee5aeb560e01b5f5260045ffd5b916040519163a9059cbb60e01b5f5260018060a01b031660045260245260205f60448180865af19060015f5114821615615a1d575b604052156159fd5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b906001811516615a3557823b15153d151616906159f2565b503d5f823e3d90fd5b5f5b8251811015615a7c576001600160a01b03615a5b82856151b2565b51166001600160a01b03831614615a7457600101615a40565b505050600190565b5050505f9056fea8f52d7b4405100a39ccc552ccc9426d860b1e822c71e97675d1623b49db6babd31b1ab070aba73480b7f863f37fefd98c16862672290e5cadbf1212bf1630cd9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a26469706673582212209f9b4f1a4023b83a764e4decc5869ba87d1ea69af08f4140c5ab4d5261f9c98064736f6c634300081c0033" } \ No newline at end of file diff --git a/cache_model_0/manifest.json b/cache_model_0/manifest.json index 7b50439f..a951f053 100644 --- a/cache_model_0/manifest.json +++ b/cache_model_0/manifest.json @@ -168,7 +168,7 @@ "artifact": { "type": "foundry-artifact", "path": "contracts/abis/DINTaskCoordinator.json", - "ipfs": "bafybeie4fki5rhunywzk5rybd6cfrqf4oce4u6xyzgq3uyxobsmlml43hi", + "ipfs": "bafkreigjv7cxzpakuvq5v37qxeepwgj5aqyhq7vr46l7wagp7sdh2ziovq", "stakeholders": [ "modelowner", "auditors", @@ -218,7 +218,7 @@ "artifact": { "type": "foundry-artifact", "path": "contracts/abis/DINTaskAuditor.json", - "ipfs": "bafybeic5e3e4b5ydtoicquggczta2ywxwoir4j53mutprfji5w4bjuzpj4", + "ipfs": "bafkreidgp65wdj2yjwkvbc5iyxb6hckg2pmp46rt3q3tetgk62z3obwlny", "stakeholders": [ "modelowner", "auditors", @@ -263,4 +263,4 @@ } } } -} \ No newline at end of file +} diff --git a/dincli/abis/DINTaskAuditor.json b/dincli/abis/DINTaskAuditor.json index 92ce5452..99a65ed4 100644 --- a/dincli/abis/DINTaskAuditor.json +++ b/dincli/abis/DINTaskAuditor.json @@ -17,6 +17,11 @@ "name": "modelId_", "type": "uint256", "internalType": "uint256" + }, + { + "name": "modelRegistry_", + "type": "address", + "internalType": "address" } ], "stateMutability": "nonpayable" @@ -977,6 +982,19 @@ ], "stateMutability": "view" }, + { + "type": "function", + "name": "modelRegistry", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract ITaskModelRegistry" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "openTestDataDispute", @@ -1568,6 +1586,19 @@ "outputs": [], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "validateGIStart", + "inputs": [ + { + "name": "gi", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "view" + }, { "type": "event", "name": "AuditScoreCommitted", @@ -2538,6 +2569,22 @@ "name": "TA_MaxLMSubmissionsReached", "inputs": [] }, + { + "type": "error", + "name": "TA_ModelDisabled", + "inputs": [ + { + "name": "modelId", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "type": "error", + "name": "TA_ModelNotRegistered", + "inputs": [] + }, { "type": "error", "name": "TA_NoActiveDispute", @@ -2627,6 +2674,11 @@ "type": "error", "name": "TA_WrongGI", "inputs": [] + }, + { + "type": "error", + "name": "TC_GIRewardPoolNotFunded", + "inputs": [] } ] } \ No newline at end of file diff --git a/dincli/abis/DINTaskCoordinator.json b/dincli/abis/DINTaskCoordinator.json index 66c9ee7b..ed2941b8 100644 --- a/dincli/abis/DINTaskCoordinator.json +++ b/dincli/abis/DINTaskCoordinator.json @@ -1441,6 +1441,19 @@ "outputs": [], "stateMutability": "nonpayable" }, + { + "type": "function", + "name": "taskProtocolVersion", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "pure" + }, { "type": "function", "name": "tier1BatchCount", @@ -2437,11 +2450,6 @@ "name": "TC_GINotEnded", "inputs": [] }, - { - "type": "error", - "name": "TC_GIRewardPoolNotFunded", - "inputs": [] - }, { "type": "error", "name": "TC_GenesisModelHashCannotBeSet", diff --git a/dincli/cli/modelownerd/deploy.py b/dincli/cli/modelownerd/deploy.py index e8da08e8..997ccc7c 100644 --- a/dincli/cli/modelownerd/deploy.py +++ b/dincli/cli/modelownerd/deploy.py @@ -1,20 +1,51 @@ import os import time from typing import Optional - import typer +from web3.exceptions import BadFunctionCallOutput, ContractLogicError from dincli.cli.contract_utils import get_contract_instance -from dincli.cli.utils import (build_and_send_tx, get_env_key, load_din_info, +from dincli.cli.utils import (build_and_send_tx, load_din_info, resolve_task_coordinator_address, set_env_key) -_MODEL_ID_ABI = [ - {"inputs": [], "name": "modelId", "outputs": [{"name": "", "type": "uint256"}], - "stateMutability": "view", "type": "function"}, +_COORDINATOR_DEPLOYMENT_ABI = [ + {"type": "function", "name": "modelId", "inputs": [], + "outputs": [{"type": "uint256"}], "stateMutability": "view"}, + {"type": "function", "name": "taskProtocolVersion", "inputs": [], + "outputs": [{"type": "uint256"}], "stateMutability": "pure"}, ] -# Deploy sub-app (for 'dincli modelowner deploy ...') -deploy_app = typer.Typer(help="Deploy task-level smart contracts") + +def _task_constructor_signature(contract, role): + """Support explicit released task signatures; refuse unknown custom shapes.""" + constructors = [item for item in contract.abi if item.get("type") == "constructor"] + signature = ( + tuple(item["type"] for item in constructors[0]["inputs"]) + if len(constructors) == 1 else () + ) + supported = { + "coordinator": {("address",), ("address", "uint256")}, + "auditor": {("address", "address"), ("address", "address", "uint256"), + ("address", "address", "uint256", "address")}, + } + if signature not in supported[role]: + raise typer.BadParameter(f"Unsupported {role} constructor signature: {signature}") + return signature + + +def _task_constructor_args(contract, role, stake, *, coordinator=None, + model_id=None, registry=None): + signature = _task_constructor_signature(contract, role) + args = [stake] if role == "coordinator" else [stake, coordinator] + if "uint256" in signature: + if model_id is None or model_id < 0 or model_id >= 2**256: + raise typer.BadParameter("This artifact requires --model-id in the uint256 range") + args.append(model_id) + if role == "auditor" and len(signature) == 4: + if not registry: + raise typer.BadParameter("Configure the canonical platform registry for this network before deployment") + args.append(registry) + return args def _require_din_info_address(effective_network, key, label, console): @@ -50,117 +81,172 @@ def _set_din_token(ctx, contract, din_token_address, label): f"Failed to set DinToken on {label}", ) + +# Deploy sub-app (for 'dincli modelowner deploy ...') +deploy_app = typer.Typer(help="Deploy task-level smart contracts") + @deploy_app.command() def task_coordinator( ctx: typer.Context, - artifact_path: str = typer.Option(..., "--artifact", help="Path to DINTaskCoordinator contract artifact JSON (Foundry format)."), - model_id: Optional[int] = typer.Option( - None, "--model-id", - help="Model ID DinModelRegistry will assign to this model (default: DINModelRegistry.totalModels()).", - ), + artifact_path: str = typer.Option(..., "--artifact", help="Task coordinator artifact JSON (Foundry or Hardhat)."), + model_id: Optional[int] = typer.Option(None, "--model-id", help="Anticipated registry ID (default: DINModelRegistry.totalModels())."), ): """ - Deploy the DINTaskCoordinator contract and set its DinToken. + Deploy the DINTaskCoordinator contract. """ effective_network, w3, account, console = ctx.obj.get_en_w3_account_console() # Load contract instance DINTaskCoordinator_contract = get_contract_instance(artifact_path, effective_network) - din_validator_stake_address = _require_din_info_address(effective_network, "stake", "DinValidatorStake", console) - din_token_address = _require_din_info_address(effective_network, "token", "DinToken", console) - if model_id is None: - model_id = _default_model_id_from_registry(ctx, console) + # Resolve DINValidatorStake address + din_validator_stake_address = _require_din_info_address( + effective_network, "stake", "DinValidatorStake", console + ) + + signature = _task_constructor_signature(DINTaskCoordinator_contract, "coordinator") + din_token_address = None + if "uint256" in signature: + din_token_address = _require_din_info_address(effective_network, "token", "DinToken", console) + if model_id is None: + model_id = _default_model_id_from_registry(ctx, console) console.print(f"[bold green]Deploying DINTaskCoordinator on network:[/bold green] {effective_network}") console.print(f"[cyan]Using DINValidatorStake:[/cyan] {din_validator_stake_address}") - console.print(f"[cyan]Model ID:[/cyan] {model_id}") tx_receipt = build_and_send_tx( ctx, - DINTaskCoordinator_contract.constructor(din_validator_stake_address, model_id), + DINTaskCoordinator_contract.constructor(*_task_constructor_args( + DINTaskCoordinator_contract, "coordinator", din_validator_stake_address, + model_id=model_id, + )), "Deploying DINTaskCoordinator", "DINTaskCoordinator contract deployed successfully", "Failed to deploy DINTaskCoordinator" ) - + dintaskcoordinator_contract_address = tx_receipt.contractAddress - + console.print(f"[bold green]βœ“ DINTaskCoordinator contract deployed at:[/bold green] {dintaskcoordinator_contract_address}") - + set_env_key(effective_network.upper()+"_DINTaskCoordinator_Contract_Address", dintaskcoordinator_contract_address) console.print(f"[green]βœ“ Saved DINTaskCoordinator address to {os.getcwd()}/.env as {effective_network.upper()}_DINTaskCoordinator_Contract_Address[/green]") - deployed_coordinator = get_contract_instance(artifact_path, effective_network, dintaskcoordinator_contract_address) - _set_din_token(ctx, deployed_coordinator, din_token_address, "DINTaskCoordinator") + if din_token_address: + deployed = get_contract_instance(artifact_path, effective_network, dintaskcoordinator_contract_address) + _set_din_token(ctx, deployed, din_token_address, "DINTaskCoordinator") @deploy_app.command() def task_auditor( ctx: typer.Context, - artifact_path: str = typer.Option(..., "--artifact", help="Path to DINTaskAuditor contract artifact JSON (Foundry format)."), + artifact_path: str = typer.Option(..., "--artifact", help="Task auditor artifact JSON (Foundry or Hardhat)."), + model_id: Optional[int] = typer.Option(None, "--model-id", help="Model ID (default: paired coordinator modelId()); must match paired coordinator."), task_coordinator: str = typer.Option(None, "--taskCoordinator", help="DINTaskCoordinator contract address"), - model_id: Optional[int] = typer.Option( - None, "--model-id", - help="Model ID DinModelRegistry will assign to this model (default: the coordinator's modelId()).", - ), ): """ - Deploy the DINTaskAuditor contract, link it to the coordinator and set its DinToken. + Deploy the DINTaskAuditor contract. """ effective_network, w3, account, console = ctx.obj.get_en_w3_account_console() # Load contract instance DINTaskAuditor_contract = get_contract_instance(artifact_path, effective_network) - din_validator_stake_address = _require_din_info_address(effective_network, "stake", "DinValidatorStake", console) - din_token_address = _require_din_info_address(effective_network, "token", "DinToken", console) + din_info = load_din_info() + din_validator_stake_address = _require_din_info_address( + effective_network, "stake", "DinValidatorStake", console + ) # Resolve DINTaskCoordinator address task_coordinator_address = resolve_task_coordinator_address( effective_network, task_coordinator, console ) - # Guard: --model-id must match the coordinator's immutable modelId(). - # Both IDs select per-model stake floors; pairing is one-shot and irreversible. - coord_view = w3.eth.contract(address=task_coordinator_address, abi=_MODEL_ID_ABI) - coord_model_id = coord_view.functions.modelId().call() - if model_id is None: - model_id = coord_model_id - console.print(f"[cyan]--model-id not given; using the coordinator's modelId() = {model_id}[/cyan]") - if coord_model_id != model_id: - console.print( - f"[bold red]Error:[/bold red] --model-id {model_id} does not match " - f"coordinator modelId() {coord_model_id}. " - "Aborting β€” pairing is one-shot and the IDs are immutable." + signature = _task_constructor_signature(DINTaskAuditor_contract, "auditor") + registry_address = din_info.get(effective_network, {}).get("registry") + if len(signature) == 4 and not registry_address: + raise typer.BadParameter("Configure the canonical platform registry for this network before deployment") + din_token_address = None + if "uint256" in signature: + if model_id is not None and (model_id < 0 or model_id >= 2**256): + raise typer.BadParameter("This artifact requires --model-id in the uint256 range") + din_token_address = _require_din_info_address(effective_network, "token", "DinToken", console) + + paired = w3.eth.contract(address=task_coordinator_address, abi=_COORDINATOR_DEPLOYMENT_ABI) + if "uint256" in signature: + # IDs select stake floors; both IDs and the one-shot pairing are immutable. + try: + paired_model_id = paired.functions.modelId().call() + except (ContractLogicError, BadFunctionCallOutput) as exc: + raise typer.BadParameter( + "Cannot read modelId() from the paired coordinator; verify the address " + "and that it is a compatible task coordinator." + ) from exc + if model_id is None: + model_id = paired_model_id + console.print(f"[cyan]--model-id not given; using the coordinator's modelId() = {model_id}[/cyan]") + if paired_model_id != model_id: + raise typer.BadParameter("--model-id must match the paired coordinator modelId()") + + constructor_args = _task_constructor_args( + DINTaskAuditor_contract, "auditor", din_validator_stake_address, + coordinator=task_coordinator_address, model_id=model_id, registry=registry_address, + ) + + if not w3.eth.get_code(task_coordinator_address): + raise typer.BadParameter("The paired coordinator has no deployed code; verify --taskCoordinator") + try: + coordinator_version = paired.functions.taskProtocolVersion().call() + except (ContractLogicError, BadFunctionCallOutput): + # Released legacy coordinators have no version getter. Transport/RPC + # failures still propagate, rather than silently taking the legacy path. + coordinator_version = 0 + auditor_version = 1 if len(constructor_args) == 4 else 0 + if coordinator_version not in (0, 1) or coordinator_version != auditor_version: + raise typer.BadParameter( + "Incompatible task contract generations: coordinator protocol version " + f"{coordinator_version}, auditor protocol version {auditor_version}. " + "Deploy a matching task pair; auditor pairing is one-shot." ) - raise typer.Exit(1) + + if len(constructor_args) == 2: + # The pre-model-ID auditor also lacks the reward-pool interface used by + # later coordinators. Keep the two released legacy families separate. + try: + paired.functions.modelId().call() + except (ContractLogicError, BadFunctionCallOutput): + pass + else: + raise typer.BadParameter( + "Incompatible task contract generations: a coordinator with modelId() " + "requires an auditor with a model ID. Deploy a matching task pair." + ) console.print(f"[bold green]Deploying DINTaskAuditor on network:[/bold green] {effective_network}") console.print(f"[cyan]Using DINValidatorStake address:[/cyan] {din_validator_stake_address}") console.print(f"[cyan]Using DINTaskCoordinator address:[/cyan] {task_coordinator_address}") - console.print(f"[cyan]Model ID:[/cyan] {model_id}") tx_receipt = build_and_send_tx( ctx, - DINTaskAuditor_contract.constructor(din_validator_stake_address, task_coordinator_address, model_id), + DINTaskAuditor_contract.constructor(*constructor_args), "Deploying DINTaskAuditor", "DINTaskAuditor contract deployed successfully", "Failed to deploy DINTaskAuditor" ) - + dintaskauditor_contract_address = tx_receipt.contractAddress - + console.print(f"[bold green]βœ“ DINTaskAuditor contract deployed at:[/bold green] {dintaskauditor_contract_address}") - + set_env_key(effective_network.upper() + "_"+task_coordinator_address+"_DINTaskAuditor_Contract_Address", dintaskauditor_contract_address) console.print(f"[green]βœ“ Saved DINTaskAuditor address to {os.getcwd()}/.env as {effective_network.upper()}_"+task_coordinator_address+"_DINTaskAuditor_Contract_Address[/green]") - deployed_auditor = get_contract_instance(artifact_path, effective_network, dintaskauditor_contract_address) - _set_din_token(ctx, deployed_auditor, din_token_address, "DINTaskAuditor") + if din_token_address: + deployed = get_contract_instance(artifact_path, effective_network, dintaskauditor_contract_address) + _set_din_token(ctx, deployed, din_token_address, "DINTaskAuditor") # Set DINTaskAuditor in DINTaskCoordinator console.print(f"[cyan]Setting DINTaskAuditor in DINTaskCoordinator...[/cyan]") - + DINTaskCoordinator_contract = ctx.obj.get_deployed_din_task_coordinator_contract(verbose=True, model_id=None, taskCoordinator_address=task_coordinator_address) time.sleep(10) diff --git a/foundry/remappings.txt b/foundry/remappings.txt index 11a7c6c2..4cce724f 100644 --- a/foundry/remappings.txt +++ b/foundry/remappings.txt @@ -2,3 +2,5 @@ forge-std/=lib/forge-std/src/ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ @openzeppelin/foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/ +erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/ +halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/ diff --git a/foundry/src/DINModelRegistry.sol b/foundry/src/DINModelRegistry.sol index 0b0f1291..48e2f327 100644 --- a/foundry/src/DINModelRegistry.sol +++ b/foundry/src/DINModelRegistry.sol @@ -427,7 +427,8 @@ contract DINModelRegistry is Initializable, OwnableUpgradeable, ReentrancyGuardT return (true, val - 1); } - /// @notice Disables a model, blocking manifest updates and new GI registrations. + /// @notice Disables a model, blocking manifest updates and emission funding. + /// @dev Compatible task pairs also check this flag before new GIs; in-flight GIs continue. /// @param modelId ID of the model to disable. function disableModel(uint256 modelId) external onlyOwner { if (modelId >= models.length) revert InvalidModelId(); diff --git a/foundry/src/DINShared.sol b/foundry/src/DINShared.sol index 0d3b3610..0a190cba 100644 --- a/foundry/src/DINShared.sol +++ b/foundry/src/DINShared.sol @@ -136,7 +136,15 @@ interface IDINTaskCoordinator { ) external view returns (uint256); } +/// @notice Registry reads required by the task's future-GI kill switch. +interface ITaskModelRegistry { + function getModelIdByTaskAuditor(address auditor) external view returns (bool exists, uint256 modelId); + function modelDisabled(uint256 modelId) external view returns (bool); +} + interface IDINTaskAuditor { + function validateGIStart(uint256 gi) external view; + function createAuditorsBatches( uint _GI, bytes32 seed @@ -487,3 +495,8 @@ error TC_T2EmptyCommitHash(); error TC_T2NoCommitFound(); /// @dev The revealed (cid, salt) does not hash to the stored T2 commitment. error TC_T2RevealHashMismatch(); + +/// @notice This auditor has not been approved in its configured registry. +error TA_ModelNotRegistered(); +/// @notice The registry has disabled the model; no new GI may start. +error TA_ModelDisabled(uint256 modelId); diff --git a/foundry/src/DINTaskAuditor.sol b/foundry/src/DINTaskAuditor.sol index 833b7939..0a60204d 100644 --- a/foundry/src/DINTaskAuditor.sol +++ b/foundry/src/DINTaskAuditor.sol @@ -18,6 +18,9 @@ interface IBurnableDinToken { contract DINTaskAuditor is Ownable, ReentrancyGuardTransient { using SafeERC20 for IERC20; + /// @notice Canonical platform registry, fixed at deployment; review wiring before approval. + ITaskModelRegistry public immutable modelRegistry; + IDinValidatorStake public dinvalidatorStakeContract; IDINTaskCoordinator public dintaskcoordinatorContract; @@ -401,14 +404,17 @@ contract DINTaskAuditor is Ownable, ReentrancyGuardTransient { /// @param _dinvalidatorStakeContract_address Address of the DinValidatorStake proxy. /// @param _dintaskcoordinator_contract_address Address of the paired DINTaskCoordinator. /// @param modelId_ Model registry ID for this deployment, used for per-model stake enforcement. + /// @param modelRegistry_ Canonical DINModelRegistry proxy; cannot be changed after deployment. constructor( address _dinvalidatorStakeContract_address, address _dintaskcoordinator_contract_address, - uint256 modelId_ + uint256 modelId_, + address modelRegistry_ ) Ownable(msg.sender) { if ( _dinvalidatorStakeContract_address == address(0) || - _dintaskcoordinator_contract_address == address(0) + _dintaskcoordinator_contract_address == address(0) || + modelRegistry_.code.length == 0 ) revert TA_InvalidAddress(); dinvalidatorStakeContract = IDinValidatorStake( _dinvalidatorStakeContract_address @@ -417,6 +423,7 @@ contract DINTaskAuditor is Ownable, ReentrancyGuardTransient { _dintaskcoordinator_contract_address ); modelId = modelId_; + modelRegistry = ITaskModelRegistry(modelRegistry_); params = Params({ auditorsPerBatch: 3, @@ -428,6 +435,19 @@ contract DINTaskAuditor is Ownable, ReentrancyGuardTransient { }); } + /// @notice Checks registry approval, enabled status and funding before a new GI. + /// @dev Only the paired coordinator calls this, from both startGI overloads, + /// before any mutation. Reverse lookup uses the approved ID rather than + /// the constructor's anticipated stake-floor ID. Registry failures revert + /// closed. In-flight phases, settlement and claims remain available. + /// @param gi Next GI whose reward pool must be funded. + function validateGIStart(uint256 gi) external view onlyTaskCoordinator { + (bool exists, uint256 registeredId) = modelRegistry.getModelIdByTaskAuditor(address(this)); + if (!exists) revert TA_ModelNotRegistered(); + if (modelRegistry.modelDisabled(registeredId)) revert TA_ModelDisabled(registeredId); + if (giRewardPool[gi] == 0) revert TC_GIRewardPoolNotFunded(); + } + /// @notice Wires the DIN token used for reward deposits and payouts. /// @dev onlyOwner, freely re-settable (mirrors DINModelRegistry.setDinToken /// / the existing setFees-style setters elsewhere in this codebase -- @@ -463,7 +483,7 @@ contract DINTaskAuditor is Ownable, ReentrancyGuardTransient { /// pool (typically the model owner, but not restricted to them -- /// matches the "market-set pool" framing in MECHANISM_DESIGN Β§5, /// nothing stops a third party topping up a pool they care about). - /// DINTaskCoordinator._startGI checks giRewardPool(_GI) > 0 as a + /// DINTaskCoordinator._startGI calls validateGIStart(_GI) as a /// precondition before that GI can start. /// /// `gi` must be the current GI or a future one: GI only ever moves diff --git a/foundry/src/DINTaskCoordinator.sol b/foundry/src/DINTaskCoordinator.sol index fee3720b..e3fdce61 100644 --- a/foundry/src/DINTaskCoordinator.sol +++ b/foundry/src/DINTaskCoordinator.sol @@ -19,6 +19,13 @@ interface IBurnableDinToken { contract DINTaskCoordinator is Ownable, ReentrancyGuardTransient { using SafeERC20 for IERC20; + /// @notice Task-pair interface generation: 1 requires the auditor GI-start guard. + /// @dev Deployment tooling uses this before the irreversible auditor pairing. + /// Legacy coordinators expose no version getter. + function taskProtocolVersion() external pure returns (uint256) { + return 1; + } + IDinValidatorStake public dinvalidatorStakeContract; IDINTaskAuditor public dinTaskAuditorContract; @@ -376,8 +383,9 @@ contract DINTaskCoordinator is Ownable, ReentrancyGuardTransient { // task_210726_6 Β§3: a GI cannot start unless its reward pool has // been funded via DINTaskAuditor.depositRewards(_GI, ...) first. // Prevents a GI running to completion with nothing to settle/claim. - if (dinTaskAuditorContract.giRewardPool(_GI) == 0) - revert TC_GIRewardPoolNotFunded(); + // The auditor also checks authoritative registry approval/enabled status. + // Keeping these reads there preserves this contract's runtime size budget. + dinTaskAuditorContract.validateGIStart(_GI); if (updatePassScore) { dinTaskAuditorContract.updatePassScore(score); } diff --git a/foundry/test/AggregatorCommitReveal.t.sol b/foundry/test/AggregatorCommitReveal.t.sol index 892954de..04e2f289 100644 --- a/foundry/test/AggregatorCommitReveal.t.sol +++ b/foundry/test/AggregatorCommitReveal.t.sol @@ -19,6 +19,7 @@ import {DinCoordinator} from "../src/DinCoordinator.sol"; import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import { GIstates, @@ -143,7 +144,7 @@ contract AggregatorCommitRevealTest is Test { function _deployTaskPair() internal { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(new EnabledModelRegistry())); tc.setDINTaskAuditorContract(address(ta)); vm.stopPrank(); diff --git a/foundry/test/AuditorCommitReveal.t.sol b/foundry/test/AuditorCommitReveal.t.sol index b682ae30..f82e0caf 100644 --- a/foundry/test/AuditorCommitReveal.t.sol +++ b/foundry/test/AuditorCommitReveal.t.sol @@ -16,6 +16,7 @@ import {DinCoordinator} from "../src/DinCoordinator.sol"; import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import {GIstates, TA_RevealHashMismatch} from "../src/DINShared.sol"; import {auditCommitHash} from "./utils/AuditCommitHash.sol"; @@ -124,7 +125,7 @@ contract AuditorCommitRevealTest is Test { function _deployTaskPair() internal { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(new EnabledModelRegistry())); tc.setDINTaskAuditorContract(address(ta)); vm.stopPrank(); diff --git a/foundry/test/DeployPlatform.t.sol b/foundry/test/DeployPlatform.t.sol index c51b16c9..c4841cda 100644 --- a/foundry/test/DeployPlatform.t.sol +++ b/foundry/test/DeployPlatform.t.sol @@ -10,6 +10,7 @@ import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DinTreasury} from "../src/DinTreasury.sol"; import {DinFeeRouter} from "../src/DinFeeRouter.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; import {TA_InvalidSlashFraction, TC_InvalidSlashFraction} from "../src/DINShared.sol"; @@ -706,7 +707,7 @@ contract SlashFractionZeroBpsTest is Test { function setUp() public { // Deploy a stub stake contract address β€” the setter guards don't call it. tc = new DINTaskCoordinator(address(0x1), 1); - ta = new DINTaskAuditor(address(0x1), address(tc), 1); + ta = new DINTaskAuditor(address(0x1), address(tc), 1, address(new EnabledModelRegistry())); } function test_setS1SlashFractionBps_rejectsZero() public { diff --git a/foundry/test/DisputeResolution.t.sol b/foundry/test/DisputeResolution.t.sol index 904eaf7d..bd018d8f 100644 --- a/foundry/test/DisputeResolution.t.sol +++ b/foundry/test/DisputeResolution.t.sol @@ -18,6 +18,7 @@ import {DinCoordinator} from "../src/DinCoordinator.sol"; import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import "../src/DINShared.sol" as Shared; import {auditCommitHash} from "./utils/AuditCommitHash.sol"; @@ -130,7 +131,7 @@ contract DisputeResolutionTest is Test { function _deployTaskPair() internal { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(new EnabledModelRegistry())); tc.setDINTaskAuditorContract(address(ta)); vm.stopPrank(); diff --git a/foundry/test/EncryptedTestData.t.sol b/foundry/test/EncryptedTestData.t.sol index 586537fd..d8b66139 100644 --- a/foundry/test/EncryptedTestData.t.sol +++ b/foundry/test/EncryptedTestData.t.sol @@ -30,6 +30,7 @@ import {DinCoordinator} from "../src/DinCoordinator.sol"; import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import {GIstates, TA_NoCommitmentStored, TA_DisputeAlreadyActive, TA_NoActiveDispute, TA_DisputeWindowClosed, TA_NotAssignedAuditor, TA_RewardsAlreadySettled} from "../src/DINShared.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; @@ -126,7 +127,7 @@ contract EncryptedTestDataTest is Test { function _deployTaskPair() internal { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(new EnabledModelRegistry())); tc.setDINTaskAuditorContract(address(ta)); vm.stopPrank(); diff --git a/foundry/test/GasSimulation.t.sol b/foundry/test/GasSimulation.t.sol index d7d9dc26..0268ddc8 100644 --- a/foundry/test/GasSimulation.t.sol +++ b/foundry/test/GasSimulation.t.sol @@ -10,6 +10,7 @@ import {DinCoordinator} from "../src/DinCoordinator.sol"; import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DinTreasury} from "../src/DinTreasury.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import {auditCommitHash} from "./utils/AuditCommitHash.sol"; import {assignPlaceholderTestData} from "./utils/PlaceholderTestData.sol"; @@ -80,7 +81,7 @@ contract GasSimulationTest is Test { // Task contracts tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(new EnabledModelRegistry())); // Wire state machine: auditor β†’ coordinator slasher β†’ auditor slasher β†’ genesis tc.setDINTaskAuditorContract(address(ta)); diff --git a/foundry/test/LifecycleEvents.t.sol b/foundry/test/LifecycleEvents.t.sol index 1d72cc5c..46ea3520 100644 --- a/foundry/test/LifecycleEvents.t.sol +++ b/foundry/test/LifecycleEvents.t.sol @@ -23,6 +23,7 @@ import {DinToken} from "../src/DinToken.sol"; import {DinCoordinator} from "../src/DinCoordinator.sol"; import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import { GIstates, @@ -123,7 +124,7 @@ contract LifecycleEventsTest is Test { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(new EnabledModelRegistry())); tc.setDINTaskAuditorContract(address(ta)); tc.setDinToken(address(token)); ta.setDinToken(address(token)); diff --git a/foundry/test/PR146SlashingRegression.t.sol b/foundry/test/PR146SlashingRegression.t.sol index bfcafe17..6fb17ca4 100644 --- a/foundry/test/PR146SlashingRegression.t.sol +++ b/foundry/test/PR146SlashingRegression.t.sol @@ -20,6 +20,7 @@ import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DinTreasury} from "../src/DinTreasury.sol"; import {DinFeeRouter} from "../src/DinFeeRouter.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import {auditCommitHash} from "./utils/AuditCommitHash.sol"; import {assignPlaceholderTestData} from "./utils/PlaceholderTestData.sol"; @@ -83,7 +84,7 @@ contract PR146SlashingRegressionTest is Test { function _deployTaskPair() internal { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(new EnabledModelRegistry())); tc.setDINTaskAuditorContract(address(ta)); vm.stopPrank(); diff --git a/foundry/test/RewardEngine.t.sol b/foundry/test/RewardEngine.t.sol index 6efc2f5d..e64fb779 100644 --- a/foundry/test/RewardEngine.t.sol +++ b/foundry/test/RewardEngine.t.sol @@ -18,7 +18,8 @@ import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; -import {GIstates} from "../src/DINShared.sol"; +import {GIstates, TA_ModelDisabled, TA_ModelNotRegistered, TA_InvalidAddress, + TA_NotTaskCoordinator, TC_GIRewardPoolNotFunded} from "../src/DINShared.sol"; import {auditCommitHash} from "./utils/AuditCommitHash.sol"; import {assignPlaceholderTestData} from "./utils/PlaceholderTestData.sol"; @@ -37,6 +38,8 @@ contract RewardEngineTest is Test { DINTaskCoordinator tc; DINTaskAuditor ta; + bool skipRegistration; + bool disableDuringGI; address admin = makeAddr("admin"); address modelOwner = makeAddr("modelOwner"); @@ -127,7 +130,7 @@ contract RewardEngineTest is Test { function _deployTaskPair() internal { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(registry)); tc.setDINTaskAuditorContract(address(ta)); vm.stopPrank(); @@ -141,7 +144,17 @@ contract RewardEngineTest is Test { tc.setDINTaskAuditorAsSlasher(); tc.setGenesisModelIpfsHash(bytes32(uint256(1))); ta.setDinToken(address(token)); + if (skipRegistration) { + vm.stopPrank(); + return; + } + vm.deal(modelOwner, 1 ether); + uint256 requestId = registry.requestModelRegistration{value: registry.openSourceFee()}( + bytes32(uint256(1)), address(tc), address(ta), true + ); vm.stopPrank(); + vm.prank(admin); + registry.approveModel(requestId); } /// @dev Drives an entire honest GI from a funded pool through to @@ -169,6 +182,10 @@ contract RewardEngineTest is Test { vm.prank(modelOwner); tc.startGI(1); + if (disableDuringGI) { + vm.prank(admin); + registry.disableModel(0); + } vm.startPrank(modelOwner); tc.startDINaggregatorsRegistration(1); @@ -412,13 +429,154 @@ contract RewardEngineTest is Test { ta.depositRewards(1, 1 ether); } + // Issue #224: real registry integration, including model ID zero. The + // fixture's anticipated constructor ID is deliberately 1, not approved ID 0. + function _killSwitchFixture() internal { + _deployPlatform(); + _deployTaskPair(); + _fundDinBalance(modelOwner, 3 ether); + vm.prank(modelOwner); + ta.depositRewards(1, 1 ether); + } + + function test_disabledModel_blocksBothOverloadsAtomically_andReenable() public { + _killSwitchFixture(); + assertEq(tc.taskProtocolVersion(), 1); + (bool exists, uint256 approvedId) = registry.getModelIdByTaskAuditor(address(ta)); + assertTrue(exists); + assertEq(approvedId, 0); + assertEq(ta.modelId(), 1); + vm.prank(admin); + registry.disableModel(0); + (, bytes memory beforeParams) = address(ta).staticcall(abi.encodeWithSignature("params()")); + vm.startPrank(modelOwner); + vm.expectRevert(abi.encodeWithSelector(TA_ModelDisabled.selector, 0)); + tc.startGI(1); + vm.expectRevert(abi.encodeWithSelector(TA_ModelDisabled.selector, 0)); + tc.startGI(1, 77); + vm.stopPrank(); + (, bytes memory afterParams) = address(ta).staticcall(abi.encodeWithSignature("params()")); + assertEq(afterParams, beforeParams); + assertEq(tc.GI(), 0); + assertEq(uint256(tc.GIstate()), uint256(GIstates.GenesisModelCreated)); + assertEq(ta.giRewardPool(1), 1 ether); + vm.prank(admin); + registry.enableModel(0); + vm.prank(modelOwner); + tc.startGI(1, 77); + assertEq(tc.GI(), 1); + } + + function test_disabledModel_blocksPrivateFundingAfterDisable() public { + _killSwitchFixture(); + vm.prank(admin); + registry.disableModel(0); + vm.startPrank(modelOwner); + ta.depositRewards(1, 1 ether); + vm.expectRevert(abi.encodeWithSelector(TA_ModelDisabled.selector, 0)); + tc.startGI(1); + vm.stopPrank(); + assertEq(ta.giRewardPool(1), 2 ether); + } + + function test_unregisteredModel_blocksBothOverloads() public { + _deployPlatform(); + skipRegistration = true; + _deployTaskPair(); + _fundDinBalance(modelOwner, 1 ether); + vm.startPrank(modelOwner); + ta.depositRewards(1, 1 ether); + vm.expectRevert(TA_ModelNotRegistered.selector); + tc.startGI(1); + vm.expectRevert(TA_ModelNotRegistered.selector); + tc.startGI(1, 77); + vm.stopPrank(); + assertEq(tc.GI(), 0); + } + + function test_registryReadFailure_failsClosed() public { + _assertRegistryReadFailsClosed(false); + } + + function test_disabledStatusReadFailure_failsClosed() public { + _assertRegistryReadFailsClosed(true); + } + + function _assertRegistryReadFailsClosed(bool disabledStatus) internal { + _killSwitchFixture(); + bytes memory callData = disabledStatus + ? abi.encodeWithSignature("modelDisabled(uint256)", 0) + : abi.encodeWithSignature("getModelIdByTaskAuditor(address)", address(ta)); + (, bytes memory beforeParams) = address(ta).staticcall(abi.encodeWithSignature("params()")); + uint256 beforeGI = tc.GI(); + GIstates beforeState = tc.GIstate(); + vm.mockCallRevert(address(registry), callData, bytes("registry unavailable")); + vm.startPrank(modelOwner); + vm.expectRevert(bytes("registry unavailable")); + tc.startGI(1); + vm.expectRevert(bytes("registry unavailable")); + tc.startGI(1, 77); + vm.stopPrank(); + (, bytes memory afterParams) = address(ta).staticcall(abi.encodeWithSignature("params()")); + assertEq(afterParams, beforeParams); + assertEq(tc.GI(), beforeGI); + assertEq(uint256(tc.GIstate()), uint256(beforeState)); + assertEq(ta.giRewardPool(1), 1 ether); + } + + function test_registryWiring_rejectsZeroAndEOA() public { + vm.expectRevert(TA_InvalidAddress.selector); + new DINTaskAuditor(address(1), address(2), 0, address(0)); + vm.expectRevert(TA_InvalidAddress.selector); + new DINTaskAuditor(address(1), address(2), 0, makeAddr("notRegistry")); + } + + function test_wrongRegistryContract_failsClosed() public { + _deployPlatform(); + DINTaskAuditor wrong = new DINTaskAuditor(address(stake), address(this), 0, address(token)); + vm.expectRevert(); // token has no registry selector + wrong.validateGIStart(1); + } + + function test_validateGIStart_onlyPairedCoordinator() public { + _killSwitchFixture(); + vm.expectRevert(TA_NotTaskCoordinator.selector); + ta.validateGIStart(1); + } + + function test_disabledInFlight_completesSettlesClaims_butCannotStartNext() public { + disableDuringGI = true; + _runFullHonestGI(1000 ether); + assertTrue(registry.modelDisabled(0)); + vm.prank(modelOwner); + tc.endGI(1); + vm.prank(client1); + ta.claimReward(1); + uint256 amount = ta.claimable(client1); + assertGt(amount, 0); + uint256 balance = token.balanceOf(client1); + vm.prank(client1); + ta.claimRewards(); + assertEq(token.balanceOf(client1), balance + amount); + _fundDinBalance(modelOwner, 1 ether); + vm.startPrank(modelOwner); + ta.depositRewards(2, 1 ether); + vm.expectRevert(abi.encodeWithSelector(TA_ModelDisabled.selector, 0)); + tc.startGI(2); + vm.expectRevert(abi.encodeWithSelector(TA_ModelDisabled.selector, 0)); + tc.startGI(2, 77); + vm.stopPrank(); + assertEq(tc.GI(), 1); + assertEq(uint256(tc.GIstate()), uint256(GIstates.GIended)); + } + function test_startGI_revertsWithoutFundedPool() public { _deployPlatform(); _deployTaskPair(); // No depositRewards call at all. vm.prank(modelOwner); - vm.expectRevert(); // TC_GIRewardPoolNotFunded + vm.expectRevert(TC_GIRewardPoolNotFunded.selector); tc.startGI(1); } diff --git a/foundry/test/ScoringValidation.t.sol b/foundry/test/ScoringValidation.t.sol index 4d089011..ec425606 100644 --- a/foundry/test/ScoringValidation.t.sol +++ b/foundry/test/ScoringValidation.t.sol @@ -16,6 +16,7 @@ import {DinCoordinator} from "../src/DinCoordinator.sol"; import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import {GIstates} from "../src/DINShared.sol"; import {auditCommitHash} from "./utils/AuditCommitHash.sol"; @@ -120,7 +121,7 @@ contract ScoringValidationTest is Test { function _deployTaskPair() internal { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(new EnabledModelRegistry())); tc.setDINTaskAuditorContract(address(ta)); vm.stopPrank(); diff --git a/foundry/test/SecurityFindings.t.sol b/foundry/test/SecurityFindings.t.sol index 655c7f43..b3e5f0d3 100644 --- a/foundry/test/SecurityFindings.t.sol +++ b/foundry/test/SecurityFindings.t.sol @@ -18,6 +18,7 @@ import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DinTreasury} from "../src/DinTreasury.sol"; import {DinFeeRouter} from "../src/DinFeeRouter.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import { GIstates, @@ -215,7 +216,7 @@ contract SecurityFindingsTest is Test { function _deployTaskPair() internal { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(new EnabledModelRegistry())); tc.setDINTaskAuditorContract(address(ta)); vm.stopPrank(); @@ -624,14 +625,16 @@ contract SecurityFindingsTest is Test { function test_DINTaskAuditor_constructor_rejectsZeroStakeAddress() public { _deployPlatform(); DINTaskCoordinator tcLocal = new DINTaskCoordinator(address(stake), 1); + address enabledRegistry = address(new EnabledModelRegistry()); vm.expectRevert(TA_InvalidAddress.selector); - new DINTaskAuditor(address(0), address(tcLocal), 1); + new DINTaskAuditor(address(0), address(tcLocal), 1, enabledRegistry); } function test_DINTaskAuditor_constructor_rejectsZeroCoordinatorAddress() public { _deployPlatform(); + address enabledRegistry = address(new EnabledModelRegistry()); vm.expectRevert(TA_InvalidAddress.selector); - new DINTaskAuditor(address(stake), address(0), 1); + new DINTaskAuditor(address(stake), address(0), 1, enabledRegistry); } function test_setDINTaskAuditorContract_rejectsZeroAddress() public { diff --git a/foundry/test/StakingEnforcement.t.sol b/foundry/test/StakingEnforcement.t.sol index aa45ea9a..cffc2b98 100644 --- a/foundry/test/StakingEnforcement.t.sol +++ b/foundry/test/StakingEnforcement.t.sol @@ -16,6 +16,7 @@ import {DinCoordinator} from "../src/DinCoordinator.sol"; import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import {GIstates} from "../src/DINShared.sol"; import { @@ -85,7 +86,7 @@ contract StakingEnforcementTest is Test { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), MODEL_ID); - ta = new DINTaskAuditor(address(stake), address(tc), MODEL_ID); + ta = new DINTaskAuditor(address(stake), address(tc), MODEL_ID, address(new EnabledModelRegistry())); tc.setDINTaskAuditorContract(address(ta)); ta.setDinToken(address(token)); vm.stopPrank(); @@ -235,7 +236,7 @@ contract StakingEnforcementTest is Test { // Deploy a second coordinator for the same stake contract vm.startPrank(modelOwner); DINTaskCoordinator tc2 = new DINTaskCoordinator(address(stake), 2); - DINTaskAuditor ta2 = new DINTaskAuditor(address(stake), address(tc2), 2); + DINTaskAuditor ta2 = new DINTaskAuditor(address(stake), address(tc2), 2, address(new EnabledModelRegistry())); tc2.setDINTaskAuditorContract(address(ta2)); ta2.setDinToken(address(token)); vm.stopPrank(); diff --git a/foundry/test/TreasuryForwarding.t.sol b/foundry/test/TreasuryForwarding.t.sol index 783175d0..90f8d66a 100644 --- a/foundry/test/TreasuryForwarding.t.sol +++ b/foundry/test/TreasuryForwarding.t.sol @@ -19,6 +19,7 @@ import {DinCoordinator} from "../src/DinCoordinator.sol"; import {DinValidatorStake} from "../src/DinValidatorStake.sol"; import {DINModelRegistry} from "../src/DINModelRegistry.sol"; import {DINTaskCoordinator} from "../src/DINTaskCoordinator.sol"; +import {EnabledModelRegistry} from "./utils/EnabledModelRegistry.sol"; import {DINTaskAuditor} from "../src/DINTaskAuditor.sol"; import {GIstates} from "../src/DINShared.sol"; import {auditCommitHash} from "./utils/AuditCommitHash.sol"; @@ -80,7 +81,7 @@ contract TreasuryForwardingTest is Test { vm.startPrank(modelOwner); tc = new DINTaskCoordinator(address(stake), 1); - ta = new DINTaskAuditor(address(stake), address(tc), 1); + ta = new DINTaskAuditor(address(stake), address(tc), 1, address(new EnabledModelRegistry())); tc.setDINTaskAuditorContract(address(ta)); tc.setDinToken(address(token)); ta.setDinToken(address(token)); diff --git a/foundry/test/utils/EnabledModelRegistry.sol b/foundry/test/utils/EnabledModelRegistry.sol new file mode 100644 index 00000000..3133a3f2 --- /dev/null +++ b/foundry/test/utils/EnabledModelRegistry.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +/// @dev Enabled/approved registry fixture for isolated task tests. Kill-switch +/// integration tests in RewardEngine use the actual registry proxy instead. +contract EnabledModelRegistry { + function getModelIdByTaskAuditor(address) external pure returns (bool, uint256) { + return (true, 0); + } + + function modelDisabled(uint256) external pure returns (bool) { + return false; + } +} diff --git a/tests/test_deploy_task_contracts.py b/tests/test_deploy_task_contracts.py index 14d04d15..5098b558 100644 --- a/tests/test_deploy_task_contracts.py +++ b/tests/test_deploy_task_contracts.py @@ -36,9 +36,15 @@ class Call(tuple): """A contract call as (name, *args); doubles as the tx passed to build_and_send_tx.""" -def _contract(address=None): +def _contract(address=None, role="coordinator", guarded=False): return SimpleNamespace( address=address, + abi=[{"type": "constructor", "inputs": [ + {"type": t} for t in ( + ("address", "uint256") if role == "coordinator" else + ("address", "address", "uint256") + (("address",) if guarded else ()) + ) + ]}], constructor=lambda *a: Call(("constructor", *a)), functions=SimpleNamespace( setDinToken=lambda addr: Call(("setDinToken", address, addr)), @@ -48,11 +54,13 @@ def _contract(address=None): class DummyCtxObj: - def __init__(self, total_models=3, coord_model_id=3): + def __init__(self, total_models=3, coord_model_id=3, guarded=False): self.console = DummyConsole() self._total_models = total_models self.w3 = SimpleNamespace(eth=SimpleNamespace( + get_code=lambda address: b"\x60", contract=lambda address, abi: SimpleNamespace(functions=SimpleNamespace( + taskProtocolVersion=lambda: SimpleNamespace(call=lambda: int(guarded)), modelId=lambda: SimpleNamespace(call=lambda: coord_model_id), )), )) @@ -71,7 +79,7 @@ def get_deployed_din_task_coordinator_contract(self, verbose=False, model_id=Non @pytest.fixture def din_info(monkeypatch): - info = {"local": {"stake": STAKE_ADDR, "token": TOKEN_ADDR}} + info = {"local": {"stake": STAKE_ADDR, "token": TOKEN_ADDR, "registry": "registry"}} monkeypatch.setattr(deploy, "load_din_info", lambda: info) return info["local"] @@ -88,7 +96,7 @@ def fake_build_and_send_tx(ctx, fn, *args, **kwargs): return SimpleNamespace(status=1) monkeypatch.setattr(deploy, "build_and_send_tx", fake_build_and_send_tx) - monkeypatch.setattr(deploy, "get_contract_instance", lambda path, net, address=None: _contract(address)) + monkeypatch.setattr(deploy, "get_contract_instance", lambda path, net, address=None: _contract(address, "coordinator" if path == "c.json" else "auditor", path == "guarded.json")) monkeypatch.setattr(deploy, "resolve_task_coordinator_address", lambda net, addr, console: COORD_ADDR) monkeypatch.setattr(deploy, "set_env_key", lambda *a, **kw: None) monkeypatch.setattr(time, "sleep", lambda _: None) @@ -152,3 +160,20 @@ def test_missing_stake_exits_before_any_tx(self, sent, din_info): with pytest.raises(typer.Exit): deploy.task_auditor(_ctx(coord_model_id=0), artifact_path="a.json", task_coordinator=COORD_ADDR, model_id=0) assert sent == [] + +@pytest.mark.parametrize("model_id", [None, 4]) +def test_guarded_auditor_defaults_and_sets_token_before_pairing(sent, model_id): + deploy.task_auditor(_ctx(coord_model_id=4, guarded=True), + artifact_path="guarded.json", task_coordinator=COORD_ADDR, model_id=model_id) + assert sent == [ + ("constructor", STAKE_ADDR, COORD_ADDR, 4, "registry"), + ("setDinToken", AUDITOR_ADDR, TOKEN_ADDR), + ("setDINTaskAuditorContract", AUDITOR_ADDR), + ] + + +def test_missing_coordinator_token_sends_no_transaction(sent, din_info): + del din_info["token"] + with pytest.raises(typer.Exit): + deploy.task_coordinator(_ctx(), artifact_path="c.json", model_id=None) + assert sent == [] diff --git a/tests/test_model_disable_deployment.py b/tests/test_model_disable_deployment.py new file mode 100644 index 00000000..a55bfa5e --- /dev/null +++ b/tests/test_model_disable_deployment.py @@ -0,0 +1,376 @@ +"""Local constructor compatibility checks; no chain or wallet used.""" +from types import SimpleNamespace +from unittest.mock import Mock + +import pytest +import typer +from web3.exceptions import BadFunctionCallOutput, ContractLogicError + +from dincli.cli.modelownerd import deploy + + +def artifact(*types): + return SimpleNamespace(abi=[{"type": "constructor", "inputs": [ + {"type": kind} for kind in types + ]}]) + + +@pytest.mark.parametrize("role,types,expected", [ + ("coordinator", ("address",), ["stake"]), + ("coordinator", ("address", "uint256"), ["stake", 0]), + ("auditor", ("address", "address"), ["stake", "coordinator"]), + ("auditor", ("address", "address", "uint256"), ["stake", "coordinator", 0]), + ("auditor", ("address", "address", "uint256", "address"), + ["stake", "coordinator", 0, "registry"]), +]) +def test_supported_constructor_shapes(role, types, expected): + assert deploy._task_constructor_args( + artifact(*types), role, "stake", coordinator="coordinator", + model_id=0, registry="registry", + ) == expected + + +@pytest.mark.parametrize("model_id", [None, -1, 2**256]) +def test_current_artifact_requires_valid_model_id(model_id): + with pytest.raises(typer.BadParameter, match="--model-id"): + deploy._task_constructor_args(artifact("address", "uint256"), + "coordinator", "stake", model_id=model_id) + + +def test_new_auditor_requires_configured_registry(): + with pytest.raises(typer.BadParameter, match="canonical platform registry"): + deploy._task_constructor_args(artifact("address", "address", "uint256", "address"), + "auditor", "stake", model_id=0) + + +@pytest.mark.parametrize("types", [(), ("uint256",), ("address", "address", "address")]) +def test_unknown_signature_rejected(types): + with pytest.raises(typer.BadParameter, match="Unsupported"): + deploy._task_constructor_args(artifact(*types), "auditor", "stake", model_id=0) + + +@pytest.fixture +def auditor_command(monkeypatch): + contract = Mock() + paired = Mock() + paired.functions.modelId.return_value.call.return_value = 7 + w3 = Mock() + w3.eth.contract.return_value = paired + w3.eth.get_code.return_value = b"\x60" + paired.functions.taskProtocolVersion.return_value.call.side_effect = ( + lambda: 1 if len(contract.abi[0]["inputs"]) == 4 else 0 + ) + obj = Mock() + obj.get_en_w3_account_console.return_value = ("local", w3, Mock(), Mock()) + ctx = SimpleNamespace(obj=obj) + monkeypatch.setattr(deploy, "get_contract_instance", lambda *a: contract) + monkeypatch.setattr(deploy, "load_din_info", lambda: {"local": {"stake": "stake", "registry": "registry", "token": "token"}}) + monkeypatch.setattr(deploy, "resolve_task_coordinator_address", lambda *a: "coordinator") + send = Mock(return_value=SimpleNamespace(contractAddress="auditor")) + monkeypatch.setattr(deploy, "build_and_send_tx", send) + write_env = Mock() + sleep = Mock() + monkeypatch.setattr(deploy, "set_env_key", write_env) + monkeypatch.setattr(deploy.time, "sleep", sleep) + return SimpleNamespace( + contract=contract, paired=paired, w3=w3, ctx=ctx, send=send, + write_env=write_env, sleep=sleep, + ) + + +def assert_no_deployment(command): + command.contract.constructor.assert_not_called() + command.send.assert_not_called() + command.write_env.assert_not_called() + command.ctx.obj.get_deployed_din_task_coordinator_contract.assert_not_called() + command.sleep.assert_not_called() + + +@pytest.mark.parametrize("with_registry", [False, True]) +def test_auditor_command_checks_pair_and_deploys(auditor_command, with_registry): + command = auditor_command + types = ("address", "address", "uint256") + (("address",) if with_registry else ()) + command.contract.abi = artifact(*types).abi + + deploy.task_auditor(command.ctx, "artifact.json", 7, "coordinator") + + expected = ["stake", "coordinator", 7] + (["registry"] if with_registry else []) + command.contract.constructor.assert_called_once_with(*expected) + command.paired.functions.modelId.assert_called_once_with() + command.paired.functions.modelId.return_value.call.assert_called_once_with() + assert command.w3.eth.contract.call_args.kwargs["address"] == "coordinator" + command.paired.functions.taskProtocolVersion.return_value.call.assert_called_once_with() + assert command.send.call_count == 3 + assert command.send.call_args_list[0].args[1] is command.contract.constructor.return_value + setter = command.ctx.obj.get_deployed_din_task_coordinator_contract.return_value + setter.functions.setDINTaskAuditorContract.assert_called_once_with("auditor") + command.contract.functions.setDinToken.assert_called_once_with("token") + assert command.send.call_args_list[1].args[1] is command.contract.functions.setDinToken.return_value + assert command.send.call_args_list[2].args[1] is setter.functions.setDINTaskAuditorContract.return_value + command.write_env.assert_called_once_with( + "LOCAL_coordinator_DINTaskAuditor_Contract_Address", "auditor", + ) + command.sleep.assert_called_once_with(10) + + +@pytest.mark.parametrize("with_registry", [False, True]) +def test_auditor_command_rejects_model_id_mismatch(auditor_command, with_registry): + command = auditor_command + types = ("address", "address", "uint256") + (("address",) if with_registry else ()) + command.contract.abi = artifact(*types).abi + + with pytest.raises(typer.BadParameter, match="match the paired"): + deploy.task_auditor(command.ctx, "artifact.json", 8, "coordinator") + + assert_no_deployment(command) + + +@pytest.mark.parametrize("with_registry", [False, True]) +@pytest.mark.parametrize("error_type", [ContractLogicError, BadFunctionCallOutput]) +def test_auditor_command_reports_model_id_revert(auditor_command, with_registry, error_type): + command = auditor_command + types = ("address", "address", "uint256") + (("address",) if with_registry else ()) + command.contract.abi = artifact(*types).abi + revert = error_type("incompatible getter") + command.paired.functions.modelId.return_value.call.side_effect = revert + + with pytest.raises(typer.BadParameter) as caught: + deploy.task_auditor(command.ctx, "artifact.json", 7, "coordinator") + + assert str(caught.value) == ( + "Cannot read modelId() from the paired coordinator; verify the address " + "and that it is a compatible task coordinator." + ) + assert caught.value.__cause__ is revert + assert_no_deployment(command) + + +@pytest.mark.parametrize("with_registry", [False, True]) +@pytest.mark.parametrize("error_type", [ConnectionError, ValueError]) +def test_auditor_command_preserves_unrelated_read_errors(auditor_command, with_registry, error_type): + command = auditor_command + types = ("address", "address", "uint256") + (("address",) if with_registry else ()) + command.contract.abi = artifact(*types).abi + error = error_type("modelId read failed") + command.paired.functions.modelId.return_value.call.side_effect = error + + with pytest.raises(error_type) as caught: + deploy.task_auditor(command.ctx, "artifact.json", 7, "coordinator") + + assert caught.value is error + assert_no_deployment(command) + + +def test_auditor_command_does_not_wrap_contract_creation_error(auditor_command): + command = auditor_command + command.contract.abi = artifact("address", "address", "uint256").abi + error = ContractLogicError("contract creation failed") + command.w3.eth.contract.side_effect = error + + with pytest.raises(ContractLogicError) as caught: + deploy.task_auditor(command.ctx, "artifact.json", 7, "coordinator") + + assert caught.value is error + assert_no_deployment(command) + + +def test_legacy_auditor_deploys_when_model_id_getter_is_absent(auditor_command): + command = auditor_command + command.contract.abi = artifact("address", "address").abi + command.paired.functions.modelId.return_value.call.side_effect = ContractLogicError("no getter") + + deploy.task_auditor(command.ctx, "artifact.json", None, "coordinator") + + command.contract.constructor.assert_called_once_with("stake", "coordinator") + command.w3.eth.contract.assert_called_once() + command.paired.functions.taskProtocolVersion.return_value.call.assert_called_once_with() + command.paired.functions.modelId.return_value.call.assert_called_once_with() + assert command.send.call_count == 2 + command.write_env.assert_called_once_with( + "LOCAL_coordinator_DINTaskAuditor_Contract_Address", "auditor", + ) + + +@pytest.mark.parametrize("role", ["coordinator", "auditor"]) +@pytest.mark.parametrize("config", [{}, {"local": {}}]) +def test_missing_stake_sends_no_transaction(auditor_command, monkeypatch, role, config): + command = auditor_command + monkeypatch.setattr(deploy, "load_din_info", lambda: config) + command.contract.abi = artifact("address", "uint256").abi + + with pytest.raises(typer.Exit): + if role == "coordinator": + deploy.task_coordinator(command.ctx, artifact_path="artifact.json", model_id=7) + else: + deploy.task_auditor(command.ctx, artifact_path="artifact.json", model_id=7, + task_coordinator="coordinator") + + assert_no_deployment(command) + + +def test_missing_registry_sends_no_transaction(auditor_command, monkeypatch): + command = auditor_command + command.contract.abi = artifact("address", "address", "uint256", "address").abi + monkeypatch.setattr(deploy, "load_din_info", lambda: {"local": {"stake": "stake"}}) + + with pytest.raises(typer.BadParameter, match="canonical platform registry"): + deploy.task_auditor(command.ctx, artifact_path="artifact.json", model_id=7, + task_coordinator="coordinator") + + command.w3.eth.contract.assert_not_called() + assert_no_deployment(command) + + +@pytest.mark.parametrize("types,model_id,expected", [ + (("address",), None, ("stake",)), + (("address", "uint256"), 0, ("stake", 0)), + (("address", "uint256"), 2**256 - 1, ("stake", 2**256 - 1)), +]) +def test_coordinator_command_deploys_supported_shapes(auditor_command, types, model_id, expected): + command = auditor_command + command.contract.abi = artifact(*types).abi + + deploy.task_coordinator(command.ctx, artifact_path="artifact.json", model_id=model_id) + + command.contract.constructor.assert_called_once_with(*expected) + assert command.send.call_count == (2 if "uint256" in types else 1) + assert command.send.call_args_list[0].args[1] is command.contract.constructor.return_value + command.write_env.assert_called_once_with("LOCAL_DINTaskCoordinator_Contract_Address", "auditor") + command.w3.eth.contract.assert_not_called() + + +@pytest.mark.parametrize("model_id", [-1, 2**256]) +@pytest.mark.parametrize("role", ["coordinator", "auditor"]) +def test_invalid_id_sends_no_transaction(auditor_command, role, model_id): + command = auditor_command + types = ("address", "uint256") if role == "coordinator" else ("address", "address", "uint256", "address") + command.contract.abi = artifact(*types).abi + + with pytest.raises(typer.BadParameter, match="--model-id"): + if role == "coordinator": + deploy.task_coordinator(command.ctx, artifact_path="artifact.json", model_id=model_id) + else: + deploy.task_auditor(command.ctx, artifact_path="artifact.json", model_id=model_id, + task_coordinator="coordinator") + + command.w3.eth.contract.assert_not_called() + assert_no_deployment(command) + + +@pytest.mark.parametrize("types,version", [ + (("address", "address"), 1), + (("address", "address", "uint256"), 1), + (("address", "address", "uint256", "address"), 0), + (("address", "address", "uint256", "address"), 2), + (("address", "address", "uint256"), 2), +]) +def test_incompatible_generations_send_no_transaction(auditor_command, types, version): + command = auditor_command + command.contract.abi = artifact(*types).abi + command.paired.functions.taskProtocolVersion.return_value.call.side_effect = None + command.paired.functions.taskProtocolVersion.return_value.call.return_value = version + + with pytest.raises(typer.BadParameter, match="Incompatible task contract generations"): + deploy.task_auditor(command.ctx, artifact_path="artifact.json", model_id=7, + task_coordinator="coordinator") + + assert_no_deployment(command) + + +@pytest.mark.parametrize("error_type", [ContractLogicError, BadFunctionCallOutput]) +@pytest.mark.parametrize("guarded", [False, True]) +def test_missing_version_getter_only_allows_legacy_pairs(auditor_command, error_type, guarded): + command = auditor_command + types = ("address", "address", "uint256") + (("address",) if guarded else ()) + command.contract.abi = artifact(*types).abi + command.paired.functions.taskProtocolVersion.return_value.call.side_effect = error_type("no getter") + + if guarded: + with pytest.raises(typer.BadParameter, match="Incompatible task contract generations"): + deploy.task_auditor(command.ctx, artifact_path="artifact.json", model_id=7, + task_coordinator="coordinator") + assert_no_deployment(command) + else: + deploy.task_auditor(command.ctx, artifact_path="artifact.json", model_id=7, + task_coordinator="coordinator") + command.contract.constructor.assert_called_once_with("stake", "coordinator", 7) + assert command.send.call_count == 3 + + +@pytest.mark.parametrize("error_type", [ConnectionError, ValueError]) +def test_version_read_rpc_errors_do_not_fall_back_to_legacy(auditor_command, error_type): + command = auditor_command + command.contract.abi = artifact("address", "address", "uint256").abi + error = error_type("RPC failure") + command.paired.functions.taskProtocolVersion.return_value.call.side_effect = error + + with pytest.raises(error_type) as caught: + deploy.task_auditor(command.ctx, artifact_path="artifact.json", model_id=7, + task_coordinator="coordinator") + + assert caught.value is error + assert_no_deployment(command) + + +def test_legacy_auditor_rejects_coordinator_without_code(auditor_command): + command = auditor_command + command.contract.abi = artifact("address", "address").abi + command.w3.eth.get_code.return_value = b"" + + with pytest.raises(typer.BadParameter, match="no deployed code"): + deploy.task_auditor(command.ctx, artifact_path="artifact.json", model_id=None, + task_coordinator="coordinator") + + command.paired.functions.taskProtocolVersion.assert_not_called() + assert_no_deployment(command) + + +def test_two_argument_auditor_rejects_previous_model_id_coordinator(auditor_command): + command = auditor_command + command.contract.abi = artifact("address", "address").abi + command.paired.functions.modelId.return_value.call.return_value = 7 + + with pytest.raises(typer.BadParameter, match="requires an auditor with a model ID"): + deploy.task_auditor(command.ctx, artifact_path="artifact.json", model_id=None, + task_coordinator="coordinator") + + assert_no_deployment(command) + + +def test_legacy_capability_rpc_error_does_not_deploy(auditor_command): + command = auditor_command + command.contract.abi = artifact("address", "address").abi + command.paired.functions.modelId.return_value.call.side_effect = ConnectionError("RPC failure") + + with pytest.raises(ConnectionError): + deploy.task_auditor(command.ctx, artifact_path="artifact.json", model_id=None, + task_coordinator="coordinator") + + assert_no_deployment(command) + +@pytest.mark.parametrize("types,version", [ + (("address", "address", "uint256"), 1), + (("address", "address", "uint256", "address"), 0), +]) +def test_default_model_id_does_not_bypass_generation_guard(auditor_command, types, version): + command = auditor_command + command.contract.abi = artifact(*types).abi + command.paired.functions.taskProtocolVersion.return_value.call.side_effect = None + command.paired.functions.taskProtocolVersion.return_value.call.return_value = version + with pytest.raises(typer.BadParameter, match="Incompatible task contract generations"): + deploy.task_auditor(command.ctx, "artifact.json", None, "coordinator") + assert_no_deployment(command) + + +@pytest.mark.parametrize("role", ["coordinator", "auditor"]) +def test_oldest_artifacts_need_neither_token_nor_registry(auditor_command, monkeypatch, role): + command = auditor_command + monkeypatch.setattr(deploy, "load_din_info", lambda: {"local": {"stake": "stake"}}) + command.contract.abi = artifact(*(("address",) if role == "coordinator" else ("address", "address"))).abi + command.paired.functions.modelId.return_value.call.side_effect = ContractLogicError("no getter") + if role == "coordinator": + deploy.task_coordinator(command.ctx, "artifact.json", None) + else: + deploy.task_auditor(command.ctx, "artifact.json", None, "coordinator") + command.contract.functions.setDinToken.assert_not_called() + command.ctx.obj.get_deployed_din_registry_contract.assert_not_called() diff --git a/tests/test_task_artifact_check.py b/tests/test_task_artifact_check.py new file mode 100644 index 00000000..cf88c2b4 --- /dev/null +++ b/tests/test_task_artifact_check.py @@ -0,0 +1,133 @@ +"""The post-build gate must reject ABI drift and stale deployable bytecode.""" +import importlib.util +import hashlib +import json +from pathlib import Path + +import pytest + +spec = importlib.util.spec_from_file_location( + "check_task_artifacts", Path(__file__).resolve().parents[1] / ".github/scripts/check_task_artifacts.py", +) +checker = importlib.util.module_from_spec(spec) +spec.loader.exec_module(checker) + + +@pytest.fixture +def artifacts(tmp_path): + abi = [{"type": "constructor", "inputs": []}, {"type": "function", "name": "modelId"}] + for name in checker.CONTRACTS: + for directory, data in [ + (f"foundry/out/{name}.sol", {"abi": abi, "bytecode": {"object": "0x6001"}}), + ("dincli/abis", {"abi": list(reversed(abi))}), + ("cache_model_0/abis", {"abi": abi, "bytecode": "0x6001"}), + ]: + path = tmp_path / directory / f"{name}.json" + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(data)) + return tmp_path + + +def test_matching_artifacts_ignore_abi_entry_order(artifacts): + assert checker.check_artifacts(artifacts) == [] + + +@pytest.mark.parametrize("directory,field,value,error", [ + ("dincli/abis", "abi", [], "ABI differs"), + ("cache_model_0/abis", "abi", [], "ABI differs"), + ("cache_model_0/abis", "bytecode", "0x6002", "creation bytecode differs"), +]) +def test_stale_bundle_fails_gate(artifacts, directory, field, value, error): + path = artifacts / directory / "DINTaskAuditor.json" + data = json.loads(path.read_text()) + data[field] = value + path.write_text(json.dumps(data)) + assert any(error in result for result in checker.check_artifacts(artifacts)) + + +def test_missing_build_fails_with_build_instruction(tmp_path): + errors = checker.check_artifacts(tmp_path) + assert len(errors) == 2 + assert all("run forge build first" in error for error in errors) + + +def test_empty_build_bytecode_fails(artifacts): + path = artifacts / "foundry/out/DINTaskAuditor.sol/DINTaskAuditor.json" + data = json.loads(path.read_text()) + data["bytecode"]["object"] = "0x" + path.write_text(json.dumps(data)) + assert any("empty build bytecode" in error for error in checker.check_artifacts(artifacts)) + + +def test_bytecode_mismatch_reports_build_settings_without_passing(artifacts): + path = artifacts / "foundry/out/DINTaskAuditor.sol/DINTaskAuditor.json" + data = json.loads(path.read_text()) + data["bytecode"]["object"] = "0x6002" + data["metadata"] = {"compiler": {"version": "0.8.28"}, "settings": {"viaIR": True}} + path.write_text(json.dumps(data)) + errors = checker.check_artifacts(artifacts) + assert any("creation bytecode differs from build" in error for error in errors) + diagnostic = next(error for error in errors if "first differing byte:" in error) + assert "first differing byte: 1" in diagnostic + assert "bundled sha256:" in diagnostic and "build sha256:" in diagnostic + assert '"version": "0.8.28"' in diagnostic + assert '"viaIR": true' in diagnostic + + +@pytest.mark.parametrize("metadata", [None, [], "null", "[]", "invalid json"]) +def test_unusable_metadata_preserves_mismatch_reporting(artifacts, metadata, monkeypatch, capsys): + path = artifacts / "foundry/out/DINTaskAuditor.sol/DINTaskAuditor.json" + data = json.loads(path.read_text()) + data.update(metadata=metadata, bytecode={"object": "0x6002"}) + path.write_text(json.dumps(data)) + monkeypatch.setattr("sys.argv", ["check_task_artifacts.py", "--root", str(artifacts)]) + assert checker.main() == 1 + output = capsys.readouterr().out + assert "creation bytecode differs from build" in output + assert "first differing byte: 1" in output + assert "build metadata: unavailable" in output + + +def test_formatting_only_difference_still_fails_gate(artifacts): + path = artifacts / "cache_model_0/abis/DINTaskAuditor.json" + data = json.loads(path.read_text()) + data["bytecode"] = "6001" + path.write_text(json.dumps(data)) + errors = checker.check_artifacts(artifacts) + assert any("creation bytecode differs from build" in error for error in errors) + assert any("decoded bytes identical; textual representation differs" in error for error in errors) + assert not any("first differing byte:" in error for error in errors) + + +@pytest.mark.parametrize("saved,built,offset", [ + ("0x6001", "0x6101", 0), + ("0x60", "0x6001", 1), + ("0x6001", "0x60", 1), +]) +def test_diagnostic_byte_offsets_lengths_and_hashes(saved, built, offset): + result = checker.bytecode_diagnostics(saved, built, {}) + assert f"first differing byte: {offset}" in result + for label, code in (("bundled", saved), ("build", built)): + raw = bytes.fromhex(code[2:]) + assert f"{label} length: {len(raw)};" in result + assert f"{label} sha256: {hashlib.sha256(raw).hexdigest()};" in result + + +@pytest.mark.parametrize("as_string", [False, True]) +def test_diagnostics_report_sorted_source_hashes_and_metadata(as_string): + metadata = { + "compiler": {"version": "0.8.28"}, "settings": {"viaIR": True}, + "sources": {"z.sol": {"keccak256": "0x22"}, "a.sol": {"keccak256": "0x11"}}, + } + result = checker.bytecode_diagnostics("0x6001", "0x6002", { + "metadata": json.dumps(metadata) if as_string else metadata, + }) + assert 'build source hashes: {"a.sol": "0x11", "z.sol": "0x22"}' in result + assert '"version": "0.8.28"' in result + assert '"viaIR": true' in result + + +def test_hex_case_difference_has_no_differing_byte(): + result = checker.bytecode_diagnostics("0x60AA", "0x60aa", {}) + assert "decoded bytes identical; textual representation differs" in result + assert "first differing byte:" not in result diff --git a/tests/test_task_auditor_model_id_guard.py b/tests/test_task_auditor_model_id_guard.py index 7d1e5cb7..63b6009c 100644 --- a/tests/test_task_auditor_model_id_guard.py +++ b/tests/test_task_auditor_model_id_guard.py @@ -7,6 +7,7 @@ redeployment. """ import time +from unittest.mock import Mock from types import SimpleNamespace import pytest @@ -37,9 +38,10 @@ def _contract(address, abi): return SimpleNamespace( functions=SimpleNamespace( modelId=lambda: SimpleNamespace(call=lambda: coord_model_id), + taskProtocolVersion=lambda: SimpleNamespace(call=lambda: 0), ) ) - return SimpleNamespace(eth=SimpleNamespace(contract=_contract)) + return SimpleNamespace(eth=SimpleNamespace(contract=_contract, get_code=lambda address: b"\x60")) class DummyCtxObj: @@ -61,13 +63,14 @@ def get_deployed_din_task_coordinator_contract(self, verbose=False, model_id=Non @pytest.fixture(autouse=True) def _patch_deploy(monkeypatch): """Stub out I/O so tests run without real artifacts, chain, or filesystem.""" - monkeypatch.setattr( - deploy, "get_contract_instance", - lambda path, net, address=None: SimpleNamespace( - constructor=lambda *a: "ctor", - functions=SimpleNamespace(setDinToken=lambda addr: ("setDinToken", addr)), - ), + contract = SimpleNamespace( + abi=[{"type": "constructor", "inputs": [ + {"type": kind} for kind in ("address", "address", "uint256") + ]}], + constructor=Mock(return_value="ctor"), + functions=SimpleNamespace(setDinToken=lambda addr: ("setDinToken", addr)), ) + monkeypatch.setattr(deploy, "get_contract_instance", lambda path, net, address=None: contract) monkeypatch.setattr( deploy, "load_din_info", lambda: {"local": {"stake": STAKE_ADDR, "token": TOKEN_ADDR}}, @@ -78,6 +81,7 @@ def _patch_deploy(monkeypatch): ) monkeypatch.setattr(deploy, "set_env_key", lambda *a, **kw: None) monkeypatch.setattr(time, "sleep", lambda _: None) + return contract @pytest.fixture @@ -93,10 +97,10 @@ def fake_build_and_send_tx(*args, **kwargs): class TestModelIdGuard: - def test_mismatch_sends_no_transaction(self, sent): + def test_mismatch_sends_no_transaction(self, sent, _patch_deploy): ctx = SimpleNamespace(obj=DummyCtxObj(coord_model_id=0)) - with pytest.raises(typer.Exit) as exc: + with pytest.raises(typer.BadParameter, match="match the paired coordinator") as exc: deploy.task_auditor( ctx, artifact_path="dummy.json", @@ -104,14 +108,12 @@ def test_mismatch_sends_no_transaction(self, sent): model_id=1, ) - assert exc.value.exit_code == 1 + assert "--model-id" in str(exc.value) assert sent == [], "No transaction must be sent on a model-ID mismatch" - assert any( - "does not match" in m or "mismatch" in m.lower() - for m in ctx.obj.console.messages - ), f"Expected mismatch error in output, got: {ctx.obj.console.messages}" + _patch_deploy.constructor.assert_not_called() + - def test_match_sends_deployment_transaction(self, sent): + def test_match_sends_deployment_transaction(self, sent, _patch_deploy): ctx = SimpleNamespace(obj=DummyCtxObj(coord_model_id=1)) deploy.task_auditor( @@ -121,4 +123,8 @@ def test_match_sends_deployment_transaction(self, sent): model_id=1, ) - assert len(sent) >= 1, "Deployment transaction must be sent when model IDs match" + _patch_deploy.constructor.assert_called_once_with(STAKE_ADDR, COORD_ADDR, 1) + assert len(sent) == 3, "Matching IDs deploy, configure the token, then pair" + assert sent[0][1] == "ctor" + assert sent[1][1] == ("setDinToken", TOKEN_ADDR) + assert sent[2][1] == ("set", AUDITOR_ADDR)