diff --git a/agent/flow-trace/00_INDEX.md b/agent/flow-trace/00_INDEX.md index 9c51134355..d7299a2802 100644 --- a/agent/flow-trace/00_INDEX.md +++ b/agent/flow-trace/00_INDEX.md @@ -39,7 +39,8 @@ 7. SORTITION Ciphernodes compute scores, submit tickets on-chain → Top N lowest scores selected -8. FINALIZE finalizeCommittee() → committee locked in +8. FINALIZE Committee members schedule staggered finalizeCommittee() calls + → first successful call locks committee in canonical on-chain order 9. DKG Selected nodes perform distributed key generation: a. BFV keygen → C0 proof (proves keypair valid) @@ -49,23 +50,26 @@ e. Collect shares → verify C2/C3 proofs (2-phase) f. Decrypt shares → calc decryption key → C4a/C4b proofs g. Exchange DecryptionKeyShared → verify C4 proofs - h. Publish KeyshareCreated → aggregator + h. Publish KeyshareCreated → all committee members buffer it -10. PK AGG Aggregator aggregates pk_shares → aggregate PK - → C5 proof (proves aggregation correct) - → publishCommittee() on-chain → KeyPublished stage +10. PK AGG All committee members buffer keyshares + → Rust normalizes finalized committee into ascending ticket-score order + → active aggregator = lowest non-expelled party_id in that normalized order + → active aggregator aggregates pk_shares → C5 proof + → permissionless publishCommittee() on-chain → KeyPublished stage 11. COMPUTE Data encrypted with aggregate PK, computation runs → Ciphertext output published on-chain 12. DECRYPT Committee members produce decryption shares → C6 proof per share (proves share correctly derived) - → Broadcast to aggregator + → broadcast to all committee members for buffering -13. AGGREGATE Aggregator combines M+1 shares → plaintext +13. AGGREGATE Active aggregator combines M+1 shares → plaintext → C7 proof (proves reconstruction correct) -14. COMPLETE publishPlaintextOutput() → rewards distributed +14. COMPLETE Active aggregator permissionlessly calls publishPlaintextOutput() + → rewards distributed → Each active committee member gets fee / N → Any escrowed slashed funds split: nodes (successSlashedNodeBps) + treasury @@ -172,7 +176,7 @@ _Found during source-code cross-referencing of these trace documents._ | 4 | `activate()` calls `register()` → `registerOperator()` which has `require(!registered, AlreadyRegistered())`. So activate **reverts** for already-registered operators. It only works for re-registration after deregistration. | BondingRegistry.sol:308 | 01_REGISTRATION | | 5 | `E3Requested` event is `(uint256 e3Id, E3 e3, IE3Program indexed e3Program)` — seed and params are inside the E3 struct, not separate parameters. | IEnclave.sol:82 | 03_E3_REQUEST | | 6 | `finalizeCommittee()` checks `>=` deadline, not `>`. | CiphernodeRegistryOwnable.sol | 03_E3_REQUEST | -| 7 | `publishCommittee()` is `onlyOwner` restricted — centralized trust assumption acknowledged in contract TODOs. | CiphernodeRegistryOwnable.sol | 04_DKG | +| 7 | `publishCommittee()` is now permissionless. The effective access control is the C5 proof verification plus the single-publish guard `publicKeyHashes[e3Id] == 0`; the old `onlyOwner` note is obsolete. | CiphernodeRegistryOwnable.sol | 04_DKG | | 8 | `CommitteePublished` event emits `(e3Id, nodes, publicKey, proof)` — full PK bytes and C5 proof, not just pkHash. | CiphernodeRegistryOwnable.sol | 04_DKG | | 9 | `_validateNodeEligibility` calls `bondingRegistry.getTicketBalanceAtBlock()` (not `ticketToken.getPastVotes()` directly). | CiphernodeRegistryOwnable.sol:668 | 03_E3_REQUEST | | 10 | Lane A slashing uses **attestation-based** verification (committee quorum votes), not direct ZK proof re-verification on-chain. `proposeSlash()` decodes voter addresses, agrees, data hashes, and ECDSA signatures — not ZK proofs. | SlashingManager.sol | 05_FAILURE | @@ -182,7 +186,7 @@ _Found during source-code cross-referencing of these trace documents._ | # | Concern | Severity | Detail | | --- | ---------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | **Deregister-before-slash race** | Accepted | SlashingManager Lane B (evidence+appeal) has a window during which the operator can deregister and claim their exit. If they do, the slash executes against 0 funds. The contract comments acknowledge this as an accepted tradeoff for the appeal window design. | -| 2 | **`publishCommittee()` is centralized** | High | Only the contract owner can publish the committee public key. A malicious or compromised owner could publish a fake key. The contract has `// TODO` and `// SECURITY` comments acknowledging this. | +| 2 | **Committee publication decentralized** | Resolved | `publishCommittee()` is permissionless. Off-chain role selection chooses the active aggregator, while on-chain C5 proof verification and the single-publish guard prevent invalid or duplicate committee publication. | | 3 | **`gracePeriod` is dead code** | Medium | `gracePeriod` is stored and validated during config updates but never actually used in any timeout check. Either the deadlines already bake in sufficient buffer, or this is a missing feature. | | 4 | **`activate` CLI command is misleading** | Low | Named "activate" but actually calls "register" — will fail for already-registered operators. There's no standalone way to trigger re-evaluation of active status; instead, `_updateOperatorStatus()` runs automatically inside `addTicketBalance()`, `bondLicense()`, etc. | | 5 | **Active-job load balancing bug fixed** | Info | The Rust `NodeStateStore.available_tickets()` subtracts `active_jobs` from total tickets, reducing the chance of busy nodes being selected for new E3s. Previously, the `Sortition` actor's `Handler` was missing match arms for `E3Failed` and `E3StageChanged`, causing these events to fall to the default `_ => ()` — the typed handlers for decrementing jobs were dead code. This has been fixed: E3Failed and E3StageChanged are now routed to their handlers, and `finalized_committees` is cleaned up in `decrement_jobs_for_e3` to prevent unbounded memory growth. | diff --git a/agent/flow-trace/03_E3_REQUEST_AND_COMMITTEE.md b/agent/flow-trace/03_E3_REQUEST_AND_COMMITTEE.md index 56be89b2f4..b160e15a2e 100644 --- a/agent/flow-trace/03_E3_REQUEST_AND_COMMITTEE.md +++ b/agent/flow-trace/03_E3_REQUEST_AND_COMMITTEE.md @@ -127,9 +127,9 @@ EnclaveSolReader decodes IEnclave::E3Requested log │ └─ Creates Fhe instance from BFV params │ └─ Stores as dependency in E3Context │ -├─ PublicKeyAggregatorExtension.on_event(): (aggregator only) -│ └─ Spins up PublicKeyAggregator actor -│ └─ State: Collecting (waiting for N keyshares) +├─ PublicKeyAggregatorExtension.on_event(): +│ └─ Spins up the per-E3 public-key aggregation pipeline +│ └─ KeyshareCreatedFilterBuffer buffers until this node becomes the active aggregator │ └─ Sortition actor receives E3Requested: │ @@ -171,7 +171,8 @@ CiphernodeSelector receives WithSortitionTicket │ ├─ Caches E3Meta { e3_id, threshold_m, threshold_n, seed, ... } │ ├─ Publishes TicketGenerated { │ │ e3_id, -│ │ ticket_id: TicketId::Score(ticket_number) +│ │ ticket_id: TicketId::Score(ticket_number), +│ │ party_index: index_in_local_score_ranking │ │ } │ └─ This event triggers on-chain ticket submission │ @@ -233,20 +234,26 @@ CiphernodeRegistrySolWriter receives TicketGenerated event ## Step 3: Committee Finalization -### 3a. Deadline Timer (Rust-Side, Aggregator) +### 3a. Deadline Timer (Rust-Side, Committee Members) ``` CommitteeFinalizer actor receives CommitteeRequested event │ +├─ Stores the request during replay and waits until ALL of: +│ ├─ local TicketGenerated.party_index is known +│ └─ EffectsEnabled has fired +│ ├─ Calculates wait time: -│ wait = committeeDeadline - currentTimestamp + buffer +│ wait = max(committeeDeadline - currentTimestamp, 0) +│ + 1 second +│ + party_index * 5 seconds │ -├─ Schedules timer +├─ Schedules a staggered timer │ ├─ When timer fires: │ └─ Publishes CommitteeFinalizeRequested { e3_id } │ -└─ On E3Failed / E3StageChanged(Complete|Failed): +└─ On E3Failed / E3RequestComplete / E3StageChanged(Complete|Failed): └─ Cancels pending timer for this e3_id (if any) → Prevents stale finalization attempt after E3 is already terminal ``` @@ -292,7 +299,7 @@ CiphernodeRegistrySolWriter receives CommitteeFinalizeRequested │ │ │ │ } │ │ │ │ │ └──────────────────────────────────────────┘ │ │ │ │ - │ │ 6. Emit CommitteeFinalized(e3Id, committee, scores) │ + │ │ 6. Emit CommitteeFinalized(e3Id, committee) │ │ │ } │ │ └─────────────────────────────────────────────────────────┘ ``` @@ -309,6 +316,7 @@ CiphernodeRegistrySolReader decodes CommitteeFinalized event ├─ Sortition actor: │ └─ Stores finalized committee as a `Committee` struct in persistent map │ → Provides O(1) address→party_id lookup for later expulsion handling +│ → `CommitteeFinalized` is normalized into ascending ticket-score order before storage │ ├─ CiphernodeSelector: │ ├─ Checks if this node's address is in the committee list @@ -318,12 +326,17 @@ CiphernodeRegistrySolReader decodes CommitteeFinalized event │ │ e3_id, threshold_m, threshold_n, │ │ seed, party_id, ...all E3 metadata │ │ } +│ │ Publishes AggregatorChanged { +│ │ e3_id, +│ │ is_aggregator = (my node has the lowest non-expelled party_id in the +│ │ score-sorted finalized committee) +│ │ } │ └─ If NO: does nothing for this E3 │ └─ KeyshareCreatedFilterBuffer: └─ Stores committee set - └─ Flushes any buffered KeyshareCreated events - └─ Only forwards events from verified committee members + └─ Keeps buffering until AggregatorChanged(is_aggregator=true) + └─ Then flushes buffered KeyshareCreated events from verified committee members ``` --- @@ -358,8 +371,17 @@ If any deadline is missed → anyone can call markE3Failed() 2. **Snapshot-based eligibility**: Ticket balances are checked at `requestBlock - 1`, preventing front-running manipulation. -3. **Permissionless finalization**: Anyone can call `finalizeCommittee()` after the deadline — no +3. **Runtime committee order**: the Rust runtime normalizes `CommitteeFinalized` into ascending + ticket-score order before `Sortition` and `CiphernodeSelector` derive `party_id`. That makes + `party_id` in the runtime equivalent to score order, even though the raw on-chain `topNodes` + array is not itself score-sorted. + +4. **Active aggregator selection**: `CiphernodeSelector` derives `AggregatorChanged` from the + finalized committee plus enriched `CommitteeMemberExpelled` events. The active aggregator is the + lowest non-expelled `party_id` in the score-sorted runtime committee. + +5. **Permissionless finalization**: Anyone can call `finalizeCommittee()` after the deadline — no single point of failure. -4. **IMT root snapshot**: The Merkle tree root is captured at request time. Nodes that join/leave +6. **IMT root snapshot**: The Merkle tree root is captured at request time. Nodes that join/leave after the request don't affect this E3's committee. diff --git a/agent/flow-trace/04_DKG_AND_COMPUTATION.md b/agent/flow-trace/04_DKG_AND_COMPUTATION.md index f84608d22f..696116f4eb 100644 --- a/agent/flow-trace/04_DKG_AND_COMPUTATION.md +++ b/agent/flow-trace/04_DKG_AND_COMPUTATION.md @@ -4,8 +4,10 @@ After committee finalization, the selected ciphernodes perform Distributed Key Generation (DKG) using threshold BFV (TrBFV) cryptography. This produces a collective public key without any single -party knowing the full secret key. Later, the committee produces decryption shares that the -aggregator combines. +party knowing the full secret key. Later, the committee produces decryption shares; all committee +members buffer them, and the active aggregator combines them. The runtime first normalizes the +finalized committee into ascending ticket-score order, and the active aggregator is then the lowest +non-expelled `party_id` in that normalized order. --- @@ -39,6 +41,8 @@ CiphernodeSelected event arrives at ThresholdKeyshare │ ├─ 5. Create child actors: │ │ ├─ EncryptionKeyCollector (waits for all N parties' keys) │ │ └─ ThresholdShareCollector (waits for all N parties' shares) +│ │ → These collectors start immediately so early peer keys/shares can +│ │ be buffered while this node is still finishing earlier DKG phases │ │ │ └─ Each collector has a timeout (60s for keys, 120s for shares) ``` @@ -448,21 +452,24 @@ ThresholdKeyshare receives AllThresholdSharesCollected pk_share, // public key share signed_proof // ZK proof of correct generation } - → Broadcast to aggregator via P2P + → Broadcast to committee members via P2P ``` --- -## Phase 2: Public Key Aggregation (Aggregator Only, with C5 Proof) +## Phase 2: Public Key Aggregation (Committee-Buffered, Active Aggregator Submits) ``` -PublicKeyAggregator (AGGREGATOR) collects KeyshareCreated events + All committee members receive KeyshareCreated events │ ├─ KeyshareCreatedFilterBuffer gates events: -│ └─ Only forwards KeyshareCreated from verified committee members -│ └─ Buffers until CommitteeFinalized is known + │ └─ Only accepts KeyshareCreated from verified committee members + │ └─ Buffers until BOTH CommitteeFinalized and AggregatorChanged(is_aggregator=true) + │ └─ On expulsion-driven handoff, the next active aggregator flushes its existing buffer │ -├─ When threshold_n keyshares collected: + ├─ Only the active aggregator's buffer flushes into PublicKeyAggregator + │ + ├─ When threshold_n keyshares collected: │ │ │ ├─ 1. Aggregate public key shares: │ │ aggregate_pk = Fhe::get_aggregate_public_key( @@ -497,16 +504,15 @@ PublicKeyAggregator (AGGREGATOR) collects KeyshareCreated events │ e3_id, aggregate_pk, pk_hash, node_list │ } │ -└─ CiphernodeRegistrySolWriter (AGGREGATOR) receives PublicKeyAggregated: - └─ Calls contract.publishCommittee(e3_id, nodes, publicKey, pkHash) +└─ CiphernodeRegistrySolWriter receives PublicKeyAggregated: + ├─ Requires EffectsEnabled + ├─ Requires active_aggregators[e3_id] == true + ├─ Reads chain state to confirm committee public key is still unset + └─ Calls contract.publishCommittee(e3_id, nodes, publicKey, pkHash) │ │ ┌─── ON-CHAIN (CiphernodeRegistryOwnable) ──────────┐ │ │ │ │ │ publishCommittee(e3Id, nodes, pk, pkHash) { │ - │ │ ⚠️ **onlyOwner** — this is centralized! │ - │ │ (acknowledged in contract with a TODO/SECURITY │ - │ │ comment; the owner can publish any key) │ - │ │ │ │ │ 1. require(initialized && finalized) │ │ │ 2. require(publicKeyHashes[e3Id] == 0) │ │ │ → Can only publish once │ @@ -625,25 +631,29 @@ EnclaveSolReader decodes CiphertextOutputPublished event │ signed_proof: SignedProofPayload(C6), │ node: address │ } - │ → Broadcast via P2P to aggregator + │ → Broadcast via P2P to committee members for buffering │ └─ State: Decrypting → Completed ``` --- -## Phase 5: Plaintext Aggregation (Aggregator Only, with C6 Verification & C7 Proof) +## Phase 5: Plaintext Aggregation (Committee-Buffered, Active Aggregator Submits) ``` -ThresholdPlaintextAggregator receives DecryptionshareCreated events + All committee members receive DecryptionshareCreated events │ -├─ For each share: -│ ├─ Verify sender is in committee: -│ │ └─ Query Sortition via E3CommitteeContainsRequest -│ ├─ If verified: add_share(party_id, decryption_share) -│ └─ If not: ignore + ├─ DecryptionshareCreatedBuffer gates events: + │ ├─ Tracks expelled parties + │ ├─ Buffers until AggregatorChanged(is_aggregator=true) + │ └─ Flushes verified shares to ThresholdPlaintextAggregator when this node is active + │ + ├─ ThresholdPlaintextAggregator receives flushed shares + │ ├─ Verifies sender is in committee + │ ├─ Adds the share if verified + │ └─ Ignores non-members or expelled parties │ -├─ C6 VERIFICATION (per-share, on aggregator): + ├─ C6 VERIFICATION (per-share, on active aggregator): │ ShareVerificationActor receives C6 signed proofs │ ├─ ECDSA recovery + ZK verification (same 2-phase as C2/C3) │ ├─ On failure: SignedProofFailed → accusation pipeline @@ -692,8 +702,11 @@ ThresholdPlaintextAggregator receives DecryptionshareCreated events │ │ │ └─ Publish PlaintextAggregated { e3_id, decrypted_output } │ -└─ EnclaveSolWriter (AGGREGATOR) receives PlaintextAggregated: - └─ Calls contract.publishPlaintextOutput(e3Id, output, proof) +└─ EnclaveSolWriter receives PlaintextAggregated: + ├─ Requires EffectsEnabled + ├─ Requires active_aggregators[e3_id] == true + ├─ Reads chain state to confirm plaintextOutput is still empty + └─ Calls contract.publishPlaintextOutput(e3Id, output, proof) │ │ ┌─── ON-CHAIN (Enclave.sol) ─────────────────────────┐ │ │ │ @@ -860,7 +873,7 @@ Each party now has dk_i (decryption key portion) No party knows the full secret key Any M+1 parties can collaboratively decrypt -AGGREGATOR collects PK_share₁ + PK_share₂ + PK_share₃ +ACTIVE AGGREGATOR collects PK_share₁ + PK_share₂ + PK_share₃ → Produces aggregate_PK (public, published on-chain) → Anyone can encrypt, only committee can decrypt ``` diff --git a/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md b/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md index 9d88c27b10..b7a357e627 100644 --- a/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md +++ b/agent/flow-trace/05_FAILURE_REFUND_SLASHING.md @@ -75,6 +75,11 @@ Specific triggers: ### Step 1: Process Failure +Runtime note: `processE3Failure()` is a permissionless cleanup path. The Rust `EnclaveSolWriter` may +auto-submit it from any effects-enabled node on the same chain, and it must not depend on +active-aggregator designation because failures can happen before committee finalization or while the +current aggregator is offline. + ``` Anyone calls: Enclave.processE3Failure(e3Id) │ @@ -85,8 +90,8 @@ Anyone calls: Enclave.processE3Failure(e3Id) ├─ 2. e3Payments[e3Id] = 0 (prevent double-processing) │ ├─ 3. Get honest nodes: -│ honestNodes = ciphernodeRegistry.getActiveCommitteeNodes(e3Id) -│ → Returns committee members NOT expelled by slashing +│ (honestNodes, _) = ciphernodeRegistry.getActiveCommitteeNodes(e3Id) +│ → Returns committee members NOT expelled by slashing plus their ticket scores │ ├─ 4. Transfer payment to E3RefundManager: │ paymentToken = _e3FeeTokens[e3Id] (per-E3 token, not current global) diff --git a/agent/flow-trace/06_DEACTIVATION_AND_COMPLETION.md b/agent/flow-trace/06_DEACTIVATION_AND_COMPLETION.md index 477b460263..7cc1f8024a 100644 --- a/agent/flow-trace/06_DEACTIVATION_AND_COMPLETION.md +++ b/agent/flow-trace/06_DEACTIVATION_AND_COMPLETION.md @@ -149,7 +149,7 @@ publishPlaintextOutput() succeeds ├─ ON-CHAIN: │ ├─ stage = Complete │ ├─ _distributeRewards(e3Id) -│ │ ├─ activeNodes = ciphernodeRegistry.getActiveCommitteeNodes(e3Id) +│ │ ├─ (activeNodes, _) = ciphernodeRegistry.getActiveCommitteeNodes(e3Id) │ │ ├─ perNode = payment / activeNodes.length │ │ ├─ dust → last member │ │ ├─ if activeNodes.length == 0: refund payment to requester @@ -177,13 +177,15 @@ publishPlaintextOutput() succeeds │ → Node becomes available for future E3s │ → Removes e3_id from node_state.e3_committees map │ - ├─ CiphernodeSelector: removes e3_id from e3_cache + ├─ CiphernodeSelector: removes e3_id from e3_cache, committee, expelled set, + │ and persisted aggregator designation for the E3 │ ├─ Per-E3 actors receive Die / shutdown on completion: │ ├─ ThresholdKeyshare: state = Completed, actor stops │ ├─ PublicKeyAggregator: actor stops │ ├─ ThresholdPlaintextAggregator: actor stops - │ └─ KeyshareCreatedFilterBuffer: no new E3 events after context teardown + │ ├─ KeyshareCreatedFilterBuffer: no new E3 events after context teardown + │ └─ DecryptionshareCreatedBuffer: no new E3 events after context teardown │ └─ E3Router: removes E3Context for this e3_id → All per-E3 state fully cleaned up @@ -206,12 +208,17 @@ enclave start → running node On restart: ├─ Sync module replays: -│ 1. Load snapshot metadata -│ 2. Replay EventStore events since last snapshot -│ 3. Fetch historical EVM events from last known block -│ 4. Sort & publish merged events by HLC timestamp -│ 5. Enable effects (writers activated) -│ 6. SyncEnded → live operations begin +│ 1. Load snapshot metadata and hydrate persisted per-E3 state +│ → Extensions must preserve hydrated recipients; replayed committee events +│ must not replace a restored per-E3 actor with a fresh instance +│ 2. CiphernodeSelector emits persisted AggregatorChanged state before replay +│ 3. Replay EventStore events since last snapshot (effects still disabled) +│ 4. Fetch historical EVM events from last known block +│ 5. Historical libp2p sync retries failed aggregate fetches after reconnects +│ and also on bounded retry intervals even without a new connection event +│ 6. Sort & publish merged events by HLC timestamp +│ 7. Enable effects (writers may submit only after this point) +│ 8. SyncEnded → live operations begin └─ Node resumes from where it left off ``` diff --git a/circuits/README.md b/circuits/README.md index 5c1dc4205d..756ba5e5e6 100644 --- a/circuits/README.md +++ b/circuits/README.md @@ -52,7 +52,7 @@ not a single crate. | ------------------------------- | -------- | ---------------------------- | --------------------------------------------- | | `pk` | C0 | `PkBfv` | Commit to individual BFV public key | | `sk_share_computation_base` | C2 inner | `SkShareComputationBase` | Shamir shares (`y`) for secret contribution | -| `e_sm_share_computation_base` | C2 inner | `ESmShareComputationBase` | Shamir shares (`y`) for smudging noise | +| `e_sm_share_computation_base` | C2 inner | `ESmShareComputationBase` | Shamir shares (`y`) for smudging noise | | `share_computation_chunk` | C2 inner | `ShareComputationChunk` | Reed–Solomon parity on a coefficient slice | | `share_computation_chunk_batch` | C2 inner | `ShareComputationChunkBatch` | Binds base proof to a batch of chunk proofs | | `share_computation` | **C2** | `ShareComputation` | Final C2 step; aggregates inner proofs | @@ -106,9 +106,9 @@ produces. Install options and CLI flags are on the ## Related documentation -| Topic | Location | -| ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| Cryptographic model (PV-TBFV, phases P1–P4, circuit identifiers C0–C7) | [Cryptography](https://docs.theinterfold.com/cryptography) · [source](../docs/pages/cryptography.mdx) | -| Toolchain, repository layout, `enclave noir`, compilation | [Noir Circuits](https://docs.theinterfold.com/noir-circuits) · [source](../docs/pages/noir-circuits.mdx) | +| Topic | Location | +| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| Cryptographic model (PV-TBFV, phases P1–P4, circuit identifiers C0–C7) | [Cryptography](https://docs.theinterfold.com/cryptography) · [source](../docs/pages/cryptography.mdx) | +| Toolchain, repository layout, `enclave noir`, compilation | [Noir Circuits](https://docs.theinterfold.com/noir-circuits) · [source](../docs/pages/noir-circuits.mdx) | | Rust types (`ProofType`, `CircuitName`) | [`signed_proof.rs`](../crates/events/src/enclave_event/signed_proof.rs) · [`proof.rs`](../crates/events/src/enclave_event/proof.rs) | -| Protocol execution (actors, events, proof ordering) | [`agent/flow-trace/04_DKG_AND_COMPUTATION.md`](../agent/flow-trace/04_DKG_AND_COMPUTATION.md) | +| Protocol execution (actors, events, proof ordering) | [`agent/flow-trace/04_DKG_AND_COMPUTATION.md`](../agent/flow-trace/04_DKG_AND_COMPUTATION.md) | diff --git a/circuits/bin/dkg/share_computation_chunk/README.md b/circuits/bin/dkg/share_computation_chunk/README.md index 8f7e30542d..a01eb0ac74 100644 --- a/circuits/bin/dkg/share_computation_chunk/README.md +++ b/circuits/bin/dkg/share_computation_chunk/README.md @@ -1,6 +1,7 @@ # `share_computation_chunk` — C2 (inner) -Reed-Solomon parity checks on a **slice** of the public share array `y` (see `PARITY_MATRIX` in configs). +Reed-Solomon parity checks on a **slice** of the public share array `y` (see `PARITY_MATRIX` in +configs). | | | | --------- | ----------------------------------------------------------------------------------------------------- | diff --git a/circuits/bin/dkg/sk_share_computation_base/README.md b/circuits/bin/dkg/sk_share_computation_base/README.md index 0b0150ce32..bd64673d9a 100644 --- a/circuits/bin/dkg/sk_share_computation_base/README.md +++ b/circuits/bin/dkg/sk_share_computation_base/README.md @@ -1,6 +1,7 @@ # `sk_share_computation_base` — C2 (inner) -Base proof for the **secret key contribution** Shamir share array `y`, bound to C1’s `sk` commitment. +Base proof for the **secret key contribution** Shamir share array `y`, bound to C1’s `sk` +commitment. | | | | --------- | --------------------------------------------------------------------------------------------------- | diff --git a/crates/aggregator/src/committee_finalizer.rs b/crates/aggregator/src/committee_finalizer.rs index cbbabc5255..cf22de885d 100644 --- a/crates/aggregator/src/committee_finalizer.rs +++ b/crates/aggregator/src/committee_finalizer.rs @@ -6,20 +6,34 @@ use actix::prelude::*; use e3_events::{ - prelude::*, run_once, trap, BusHandle, CommitteeFinalizeRequested, CommitteeRequested, - E3Failed, E3Stage, E3StageChanged, EType, EffectsEnabled, EnclaveEvent, EnclaveEventData, - EventType, Shutdown, TypedEvent, + prelude::*, trap, BusHandle, CommitteeFinalizeRequested, CommitteeRequested, E3Failed, + E3RequestComplete, E3Stage, E3StageChanged, EType, EffectsEnabled, EnclaveEvent, + EnclaveEventData, EventType, Shutdown, TicketGenerated, TypedEvent, }; +use e3_events::{E3id, EventContext, Sequenced}; use e3_utils::{NotifySync, MAILBOX_LIMIT}; use std::collections::HashMap; use std::time::Duration; use tracing::{error, info}; +const FINALIZATION_BUFFER_SECONDS: u64 = 1; +const FINALIZE_INTERVAL_SECONDS: u64 = 5; + +#[derive(Clone)] +struct PendingCommitteeRequest { + e3_id: E3id, + committee_deadline: u64, + ec: EventContext, +} + /// CommitteeFinalizer is an actor that listens to CommitteeRequested events and dispatches /// CommitteeFinalizeRequested events after the submission deadline has passed. pub struct CommitteeFinalizer { bus: BusHandle, pending_committees: HashMap, + pending_requests: HashMap, + party_indexes: HashMap, + effects_enabled: bool, } impl CommitteeFinalizer { @@ -27,6 +41,9 @@ impl CommitteeFinalizer { Self { bus: bus.clone(), pending_committees: HashMap::new(), + pending_requests: HashMap::new(), + party_indexes: HashMap::new(), + effects_enabled: false, } } @@ -39,80 +56,39 @@ impl CommitteeFinalizer { EventType::Shutdown, EventType::E3Failed, EventType::E3StageChanged, + EventType::E3RequestComplete, + EventType::TicketGenerated, + EventType::CommitteeRequested, + EventType::EffectsEnabled, ], addr.clone().recipient(), ); - // Gate CommitteeRequested behind EffectsEnabled — finalization should not - // be scheduled during historical event replay. - bus.subscribe( - EventType::EffectsEnabled, - run_once::({ - let bus = bus.clone(); - let addr = addr.clone(); - move |_| { - bus.subscribe(EventType::CommitteeRequested, addr.into()); - Ok(()) - } - }) - .recipient(), - ); - addr } -} -impl Actor for CommitteeFinalizer { - type Context = Context; - fn started(&mut self, ctx: &mut Self::Context) { - ctx.set_mailbox_capacity(MAILBOX_LIMIT); - } -} - -impl Handler for CommitteeFinalizer { - type Result = (); - fn handle(&mut self, msg: EnclaveEvent, ctx: &mut Self::Context) -> Self::Result { - let (msg, ec) = msg.into_components(); - match msg { - EnclaveEventData::CommitteeRequested(data) => { - self.notify_sync(ctx, TypedEvent::new(data, ec)) - } - EnclaveEventData::Shutdown(data) => self.notify_sync(ctx, data), - EnclaveEventData::E3Failed(data) => self.notify_sync(ctx, TypedEvent::new(data, ec)), - EnclaveEventData::E3StageChanged(data) => { - self.notify_sync(ctx, TypedEvent::new(data, ec)) - } - _ => (), + fn schedule_committee( + &mut self, + e3_id: String, + request: PendingCommitteeRequest, + party_index: u64, + ctx: &mut Context, + ) { + if self.pending_committees.contains_key(&e3_id) { + return; } - } -} -impl Handler> for CommitteeFinalizer { - type Result = (); - - // TODO: Remove all async from this function. Remove reliance on e3_evm package. Add unit test. - fn handle( - &mut self, - msg: TypedEvent, - ctx: &mut Self::Context, - ) -> Self::Result { - let ec = msg.get_ctx().clone(); - let e3_id = msg.e3_id.clone(); - let committee_deadline = msg.committee_deadline; + let committee_deadline = request.committee_deadline; + let request_e3_id = request.e3_id.clone(); + let ec = request.ec.clone(); + let e3_id_for_async = e3_id.clone(); - const FINALIZATION_BUFFER_SECONDS: u64 = 1; - let e3_id_for_log = e3_id.clone(); let fut = async move { - // TODO: we should have no dependencies on e3_evm here. Reason being that this is core - // functionality and evm is shell. Shell can depend on core but core MUST not depend on - // shell. This means we should not hold an address to a shell actor even and should use - // the eventbus to communicate. - // see https://github.com/gnosisguild/enclave/issues/989 match e3_evm::helpers::get_current_timestamp().await { Ok(timestamp) => Some(timestamp), Err(e) => { error!( - e3_id = %e3_id_for_log, + e3_id = %e3_id_for_async, error = %e, "Failed to get current timestamp from RPC" ); @@ -121,63 +97,149 @@ impl Handler> for CommitteeFinalizer { } }; - let e3_id_for_async = e3_id; ctx.spawn( fut.into_actor(self) .then(move |current_timestamp, act, ctx| { if let Some(current_timestamp) = current_timestamp { let seconds_until_deadline = if committee_deadline > current_timestamp { - (committee_deadline - current_timestamp) + FINALIZATION_BUFFER_SECONDS + committee_deadline - current_timestamp } else { - info!( - e3_id = %e3_id_for_async, - committee_deadline = committee_deadline, - current_timestamp = current_timestamp, - "Submission deadline already passed, finalizing with buffer" - ); - FINALIZATION_BUFFER_SECONDS - }; + 0 + } + FINALIZATION_BUFFER_SECONDS + + (party_index * FINALIZE_INTERVAL_SECONDS); info!( - e3_id = %e3_id_for_async, - committee_deadline = committee_deadline, - current_timestamp = current_timestamp, + e3_id = %e3_id, + party_index, + committee_deadline, + current_timestamp, seconds_to_wait = seconds_until_deadline, "Scheduling committee finalization" ); let bus = act.bus.clone(); - let e3_id_clone = e3_id_for_async.clone(); + let e3_id_clone = e3_id.clone(); + let ec_clone = ec.clone(); let handle = ctx.run_later( Duration::from_secs(seconds_until_deadline), move |act, _ctx| { - info!(e3_id = %e3_id_clone, "Dispatching CommitteeFinalizeRequested event"); + info!(e3_id = %e3_id_clone, party_index, "Dispatching CommitteeFinalizeRequested event"); - trap(EType::Sortition, &act.bus.with_ec(&ec), || { - bus.publish(CommitteeFinalizeRequested { - e3_id: e3_id_clone.clone(), - },ec)?; + trap(EType::Sortition, &act.bus.with_ec(&ec_clone), || { + bus.publish( + CommitteeFinalizeRequested { + e3_id: request_e3_id.clone(), + }, + ec_clone.clone(), + )?; Ok(()) }); - act.pending_committees.remove(&e3_id_clone.to_string()); + act.pending_committees.remove(&e3_id_clone); }, ); - act.pending_committees - .insert(e3_id_for_async.to_string(), handle); - } else { - error!( - e3_id = %e3_id_for_async, - "Skipping committee finalization due to timestamp fetch failure" - ); + act.pending_committees.insert(e3_id.clone(), handle); } async {}.into_actor(act) }), ); } + + fn schedule_if_ready(&mut self, e3_id: &str, ctx: &mut Context) { + if !self.effects_enabled { + return; + } + + let Some(request) = self.pending_requests.get(e3_id).cloned() else { + return; + }; + let Some(party_index) = self.party_indexes.get(e3_id).copied() else { + return; + }; + + self.schedule_committee(e3_id.to_owned(), request, party_index, ctx); + } +} + +impl Actor for CommitteeFinalizer { + type Context = Context; + fn started(&mut self, ctx: &mut Self::Context) { + ctx.set_mailbox_capacity(MAILBOX_LIMIT); + } +} + +impl Handler for CommitteeFinalizer { + type Result = (); + fn handle(&mut self, msg: EnclaveEvent, ctx: &mut Self::Context) -> Self::Result { + let (msg, ec) = msg.into_components(); + match msg { + EnclaveEventData::CommitteeRequested(data) => { + self.notify_sync(ctx, TypedEvent::new(data, ec)) + } + EnclaveEventData::EffectsEnabled(data) => self.notify_sync(ctx, data), + EnclaveEventData::TicketGenerated(data) => self.notify_sync(ctx, data), + EnclaveEventData::Shutdown(data) => self.notify_sync(ctx, data), + EnclaveEventData::E3Failed(data) => self.notify_sync(ctx, TypedEvent::new(data, ec)), + EnclaveEventData::E3RequestComplete(data) => { + self.notify_sync(ctx, TypedEvent::new(data, ec)) + } + EnclaveEventData::E3StageChanged(data) => { + self.notify_sync(ctx, TypedEvent::new(data, ec)) + } + _ => (), + } + } +} + +impl Handler> for CommitteeFinalizer { + type Result = (); + + // TODO: Remove all async from this function. Remove reliance on e3_evm package. Add unit test. + fn handle( + &mut self, + msg: TypedEvent, + ctx: &mut Self::Context, + ) -> Self::Result { + let e3_id = msg.e3_id.to_string(); + self.pending_requests.insert( + e3_id.clone(), + PendingCommitteeRequest { + e3_id: msg.e3_id.clone(), + committee_deadline: msg.committee_deadline, + ec: msg.get_ctx().clone(), + }, + ); + self.schedule_if_ready(&e3_id, ctx); + } +} + +impl Handler for CommitteeFinalizer { + type Result = (); + + fn handle(&mut self, msg: TicketGenerated, ctx: &mut Self::Context) -> Self::Result { + let Some(party_index) = msg.party_index else { + return; + }; + + let e3_id = msg.e3_id.to_string(); + self.party_indexes.insert(e3_id.clone(), party_index); + self.schedule_if_ready(&e3_id, ctx); + } +} + +impl Handler for CommitteeFinalizer { + type Result = (); + + fn handle(&mut self, _msg: EffectsEnabled, ctx: &mut Self::Context) -> Self::Result { + self.effects_enabled = true; + let e3_ids: Vec = self.pending_requests.keys().cloned().collect(); + for e3_id in e3_ids { + self.schedule_if_ready(&e3_id, ctx); + } + } } impl Handler for CommitteeFinalizer { @@ -204,6 +266,8 @@ impl Handler> for CommitteeFinalizer { ); ctx.cancel_future(handle); } + self.pending_requests.remove(&e3_id_str); + self.party_indexes.remove(&e3_id_str); } } @@ -221,8 +285,27 @@ impl Handler> for CommitteeFinalizer { ); ctx.cancel_future(handle); } + self.pending_requests.remove(&e3_id_str); + self.party_indexes.remove(&e3_id_str); } _ => {} } } } + +impl Handler> for CommitteeFinalizer { + type Result = (); + + fn handle( + &mut self, + msg: TypedEvent, + ctx: &mut Self::Context, + ) -> Self::Result { + let e3_id_str = msg.e3_id.to_string(); + if let Some(handle) = self.pending_committees.remove(&e3_id_str) { + ctx.cancel_future(handle); + } + self.pending_requests.remove(&e3_id_str); + self.party_indexes.remove(&e3_id_str); + } +} diff --git a/crates/aggregator/src/decryptionshare_created_buffer.rs b/crates/aggregator/src/decryptionshare_created_buffer.rs new file mode 100644 index 0000000000..ec689115d5 --- /dev/null +++ b/crates/aggregator/src/decryptionshare_created_buffer.rs @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use actix::prelude::*; +use e3_events::{prelude::*, AggregatorChanged, Die, EnclaveEvent, EnclaveEventData}; +use e3_utils::MAILBOX_LIMIT; +use std::collections::HashSet; + +use crate::ThresholdPlaintextAggregator; + +pub struct DecryptionshareCreatedBuffer { + dest: Addr, + buffer: Vec, + expelled_parties: HashSet, + is_aggregator: bool, +} + +impl DecryptionshareCreatedBuffer { + pub fn new(dest: Addr) -> Self { + Self { + dest, + buffer: Vec::new(), + expelled_parties: HashSet::new(), + is_aggregator: false, + } + } + + fn flush(&mut self) { + if !self.is_aggregator { + return; + } + + for event in self.buffer.drain(..) { + match event.get_data() { + EnclaveEventData::DecryptionshareCreated(data) + if !self.expelled_parties.contains(&data.party_id) => + { + self.dest.do_send(event); + } + EnclaveEventData::CommitteeMemberExpelled(data) if data.party_id.is_some() => { + self.dest.do_send(event); + } + EnclaveEventData::E3RequestComplete(_) | EnclaveEventData::Shutdown(_) => { + self.dest.do_send(event); + } + _ => {} + } + } + } +} + +impl Actor for DecryptionshareCreatedBuffer { + type Context = Context; + + fn started(&mut self, ctx: &mut Self::Context) { + ctx.set_mailbox_capacity(MAILBOX_LIMIT); + } +} + +impl Handler for DecryptionshareCreatedBuffer { + type Result = (); + + fn handle(&mut self, msg: EnclaveEvent, _ctx: &mut Self::Context) -> Self::Result { + match msg.get_data() { + EnclaveEventData::DecryptionshareCreated(data) => { + if self.expelled_parties.contains(&data.party_id) { + return; + } + + if self.is_aggregator { + self.dest.do_send(msg); + } else { + self.buffer.push(msg); + } + } + EnclaveEventData::CommitteeMemberExpelled(data) => { + let Some(party_id) = data.party_id else { + return; + }; + + self.expelled_parties.insert(party_id); + self.buffer.retain(|event| { + !matches!( + event.get_data(), + EnclaveEventData::DecryptionshareCreated(share) + if share.party_id == party_id + ) + }); + + if self.is_aggregator { + self.dest.do_send(msg); + } else { + self.buffer.push(msg); + } + } + EnclaveEventData::AggregatorChanged(AggregatorChanged { is_aggregator, .. }) => { + self.is_aggregator = *is_aggregator; + self.flush(); + } + EnclaveEventData::E3RequestComplete(_) | EnclaveEventData::Shutdown(_) => { + self.dest.do_send(msg); + } + _ => { + if self.is_aggregator { + self.dest.do_send(msg); + } + } + } + } +} + +impl Handler for DecryptionshareCreatedBuffer { + type Result = (); + + fn handle(&mut self, _: Die, ctx: &mut Self::Context) -> Self::Result { + ctx.stop(); + } +} diff --git a/crates/aggregator/src/ext.rs b/crates/aggregator/src/ext.rs index 90c3ee9657..fd2b9cfbaa 100644 --- a/crates/aggregator/src/ext.rs +++ b/crates/aggregator/src/ext.rs @@ -6,6 +6,7 @@ use std::sync::Arc; +use crate::decryptionshare_created_buffer::DecryptionshareCreatedBuffer; use crate::keyshare_created_filter_buffer::KeyshareCreatedFilterBuffer; use crate::{ PublicKeyAggregator, PublicKeyAggregatorParams, PublicKeyAggregatorState, @@ -16,7 +17,7 @@ use actix::{Actor, Addr, Recipient}; use anyhow::{anyhow, Result}; use async_trait::async_trait; use e3_data::{AutoPersist, Persistable, RepositoriesFactory}; -use e3_events::{prelude::*, E3id}; +use e3_events::{prelude::*, CiphernodeSelected, E3id}; use e3_events::{BusHandle, EType, EnclaveEvent, EnclaveEventData}; use e3_fhe::ext::FHE_KEY; use e3_fhe::Fhe; @@ -44,11 +45,15 @@ const ERROR_PUBKEY_META_MISSING:&str = "Could not create PublicKeyAggregator bec #[async_trait] impl E3Extension for PublicKeyAggregatorExtension { fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { - // Saving the publickey aggregator with deps on E3Requested - let EnclaveEventData::E3Requested(data) = evt.get_data() else { + // Create the public-key aggregation pipeline only for finalized committee members. + let EnclaveEventData::CiphernodeSelected(data) = evt.get_data() else { return; }; + if ctx.get_event_recipient("publickey").is_some() { + return; + } + let Some(fhe) = ctx.get_dependency(FHE_KEY) else { self.bus.err( EType::PublickeyAggregation, @@ -56,19 +61,18 @@ impl E3Extension for PublicKeyAggregatorExtension { ); return; }; - let Some(ref meta) = ctx.get_dependency(META_KEY) else { - self.bus.err( - EType::PublickeyAggregation, - anyhow!(ERROR_PUBKEY_META_MISSING), - ); - return; - }; - let e3_id = data.e3_id.clone(); + let CiphernodeSelected { + e3_id, + threshold_n, + threshold_m, + seed, + .. + } = data.clone(); let repo = ctx.repositories().publickey(&e3_id); let sync_state = repo.send(Some(PublicKeyAggregatorState::init( - meta.threshold_n, - meta.threshold_m, - meta.seed, + threshold_n, + threshold_m, + seed, ))); let value = create_publickey_aggregator( @@ -169,7 +173,15 @@ const ERROR_TRBFV_PLAINTEXT_META_MISSING:&str = "Could not create ThresholdPlain #[async_trait] impl E3Extension for ThresholdPlaintextAggregatorExtension { fn on_event(&self, ctx: &mut E3Context, evt: &EnclaveEvent) { - // Save plaintext aggregator + if ctx.get_event_recipient("threshold_keyshare").is_none() { + return; + } + + if ctx.get_event_recipient("plaintext").is_some() { + return; + } + + // Save plaintext aggregator for finalized committee members. let EnclaveEventData::CiphertextOutputPublished(data) = evt.get_data() else { return; }; @@ -195,14 +207,17 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { ctx.set_event_recipient( "plaintext", Some( - ThresholdPlaintextAggregator::new( - ThresholdPlaintextAggregatorParams { - bus: self.bus.clone(), - sortition: self.sortition.clone(), - e3_id: e3_id.clone(), - params_preset: self.params_preset.clone(), - }, - sync_state, + DecryptionshareCreatedBuffer::new( + ThresholdPlaintextAggregator::new( + ThresholdPlaintextAggregatorParams { + bus: self.bus.clone(), + sortition: self.sortition.clone(), + e3_id: e3_id.clone(), + params_preset: self.params_preset.clone(), + }, + sync_state, + ) + .start(), ) .start() .into(), @@ -233,11 +248,13 @@ impl E3Extension for ThresholdPlaintextAggregatorExtension { }, sync_state, ) - .start() - .into(); + .start(); // send to context - ctx.set_event_recipient("plaintext", Some(value)); + ctx.set_event_recipient( + "plaintext", + Some(DecryptionshareCreatedBuffer::new(value).start().into()), + ); Ok(()) } diff --git a/crates/aggregator/src/keyshare_created_filter_buffer.rs b/crates/aggregator/src/keyshare_created_filter_buffer.rs index 283f5112b1..ee79d39f0d 100644 --- a/crates/aggregator/src/keyshare_created_filter_buffer.rs +++ b/crates/aggregator/src/keyshare_created_filter_buffer.rs @@ -6,7 +6,7 @@ use actix::prelude::*; -use e3_events::{prelude::*, Die, EnclaveEvent, EnclaveEventData}; +use e3_events::{prelude::*, AggregatorChanged, Die, EnclaveEvent, EnclaveEventData}; use e3_utils::MAILBOX_LIMIT; use std::collections::HashSet; use tracing::info; @@ -18,9 +18,8 @@ pub struct KeyshareCreatedFilterBuffer { dest: Addr, committee: Option>, buffer: Vec, - /// Nodes expelled before CommitteeFinalized arrived. - /// Tracked separately so they are filtered during `process_buffered_events()`. - expelled_before_finalization: HashSet, + expelled_nodes: HashSet, + is_aggregator: bool, } impl KeyshareCreatedFilterBuffer { @@ -29,19 +28,32 @@ impl KeyshareCreatedFilterBuffer { dest, committee: None, buffer: Vec::new(), - expelled_before_finalization: HashSet::new(), + expelled_nodes: HashSet::new(), + is_aggregator: false, } } fn process_buffered_events(&mut self) { + if !self.is_aggregator { + return; + } + if let Some(ref committee) = self.committee { for event in self.buffer.drain(..) { - if let EnclaveEventData::KeyshareCreated(data) = event.get_data() { - if committee.contains(&data.node) - && !self.expelled_before_finalization.contains(&data.node) + match event.get_data() { + EnclaveEventData::KeyshareCreated(data) + if committee.contains(&data.node) + && !self.expelled_nodes.contains(&data.node) => { self.dest.do_send(event); } + EnclaveEventData::CommitteeMemberExpelled(data) if data.party_id.is_none() => { + self.dest.do_send(event); + } + EnclaveEventData::E3RequestComplete(_) | EnclaveEventData::Shutdown(_) => { + self.dest.do_send(event); + } + _ => {} } } } @@ -61,49 +73,67 @@ impl Handler for KeyshareCreatedFilterBuffer { fn handle(&mut self, msg: EnclaveEvent, _ctx: &mut Self::Context) -> Self::Result { match msg.get_data() { EnclaveEventData::KeyshareCreated(data) => match &self.committee { - Some(committee) if committee.contains(&data.node) => { - // if the committee is ready then process + Some(committee) + if self.is_aggregator + && committee.contains(&data.node) + && !self.expelled_nodes.contains(&data.node) => + { self.dest.do_send(msg); } None => { - // if not buffer + self.buffer.push(msg); + } + Some(committee) + if committee.contains(&data.node) + && !self.expelled_nodes.contains(&data.node) => + { self.buffer.push(msg); } _ => {} }, EnclaveEventData::CommitteeFinalized(data) => { - self.dest.do_send(msg.clone()); self.committee = Some(data.committee.iter().cloned().collect()); self.process_buffered_events(); } EnclaveEventData::CommitteeMemberExpelled(data) => { - // Only process raw events from chain (party_id not yet resolved). if data.party_id.is_some() { return; } let node_addr = data.node.to_string(); + self.expelled_nodes.insert(node_addr.clone()); + self.buffer.retain(|event| { + !matches!( + event.get_data(), + EnclaveEventData::KeyshareCreated(share) if share.node == node_addr + ) + }); - // Remove expelled node so we don't forward late KeyshareCreated events from them if let Some(ref mut committee) = self.committee { info!( "KeyshareCreatedFilterBuffer: removing expelled node {} from committee filter (e3_id={})", node_addr, data.e3_id ); committee.remove(&node_addr); + } + + if self.is_aggregator { + self.dest.do_send(msg); } else { - // Committee not yet finalized — track for filtering during process_buffered_events - info!( - "KeyshareCreatedFilterBuffer: tracking expelled node {} before finalization (e3_id={})", - node_addr, data.e3_id - ); - self.expelled_before_finalization.insert(node_addr); + self.buffer.push(msg); } - // Forward to PublicKeyAggregator for threshold_n adjustment + } + EnclaveEventData::AggregatorChanged(AggregatorChanged { is_aggregator, .. }) => { + self.is_aggregator = *is_aggregator; + self.process_buffered_events(); + } + EnclaveEventData::E3RequestComplete(_) | EnclaveEventData::Shutdown(_) => { self.dest.do_send(msg); } _ => { - self.dest.do_send(msg); + if self.is_aggregator { + self.dest.do_send(msg); + } } } } diff --git a/crates/aggregator/src/lib.rs b/crates/aggregator/src/lib.rs index e6ba05955d..348ac8976d 100644 --- a/crates/aggregator/src/lib.rs +++ b/crates/aggregator/src/lib.rs @@ -5,6 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. mod committee_finalizer; +mod decryptionshare_created_buffer; pub mod ext; mod keyshare_created_filter_buffer; mod proof_fold; diff --git a/crates/aggregator/src/threshold_plaintext_aggregator.rs b/crates/aggregator/src/threshold_plaintext_aggregator.rs index 60b2967243..695822208a 100644 --- a/crates/aggregator/src/threshold_plaintext_aggregator.rs +++ b/crates/aggregator/src/threshold_plaintext_aggregator.rs @@ -11,11 +11,12 @@ use actix::prelude::*; use anyhow::{anyhow, bail, ensure, Result}; use e3_data::Persistable; use e3_events::{ - prelude::*, trap, AggregationProofPending, AggregationProofSigned, BusHandle, ComputeRequest, - ComputeResponse, ComputeResponseKind, CorrelationId, DecryptedSharesAggregationProofRequest, - DecryptionshareCreated, Die, E3id, EType, EnclaveEvent, EnclaveEventData, EventContext, - PartyProofsToVerify, PlaintextAggregated, Proof, Seed, Sequenced, ShareVerificationComplete, - ShareVerificationDispatched, SignedProofPayload, TypedEvent, VerificationKind, ZkResponse, + prelude::*, trap, AggregationProofPending, AggregationProofSigned, BusHandle, + CommitteeMemberExpelled, ComputeRequest, ComputeResponse, ComputeResponseKind, CorrelationId, + DecryptedSharesAggregationProofRequest, DecryptionshareCreated, Die, E3id, EType, EnclaveEvent, + EnclaveEventData, EventContext, PartyProofsToVerify, PlaintextAggregated, Proof, Seed, + Sequenced, ShareVerificationComplete, ShareVerificationDispatched, SignedProofPayload, + TypedEvent, VerificationKind, ZkResponse, }; use e3_fhe_params::BfvPreset; use e3_sortition::{E3CommitteeContainsRequest, E3CommitteeContainsResponse, Sortition}; @@ -262,6 +263,71 @@ impl ThresholdPlaintextAggregator { }) } + pub fn handle_member_expelled( + &mut self, + party_id: u64, + ec: &EventContext, + ) -> Result<()> { + self.state.try_mutate(ec, |state| { + let ThresholdPlaintextAggregatorState::Collecting(current) = state else { + return Ok(state); + }; + + let mut shares = current.shares; + let mut c6_proofs = current.c6_proofs; + let mut c6_wrapped_proofs = current.c6_wrapped_proofs; + let mut threshold_n = current.threshold_n; + + shares.remove(&party_id); + c6_proofs.remove(&party_id); + c6_wrapped_proofs.remove(&party_id); + + if threshold_n > 0 { + threshold_n -= 1; + } + + if threshold_n < current.threshold_m { + warn!( + "ThresholdPlaintextAggregator: threshold_n ({}) < threshold_m ({}) after expulsion", + threshold_n, current.threshold_m + ); + return Ok(ThresholdPlaintextAggregatorState::Collecting(Collecting { + threshold_m: current.threshold_m, + threshold_n, + shares, + c6_proofs, + c6_wrapped_proofs, + seed: current.seed, + ciphertext_output: current.ciphertext_output, + params: current.params, + })); + } + + if (shares.len() as u64) < threshold_n || threshold_n == 0 { + return Ok(ThresholdPlaintextAggregatorState::Collecting(Collecting { + threshold_m: current.threshold_m, + threshold_n, + shares, + c6_proofs, + c6_wrapped_proofs, + seed: current.seed, + ciphertext_output: current.ciphertext_output, + params: current.params, + })); + } + + Ok(ThresholdPlaintextAggregatorState::VerifyingC6(VerifyingC6 { + threshold_m: current.threshold_m, + threshold_n, + shares, + c6_proofs, + c6_wrapped_proofs, + ciphertext_output: current.ciphertext_output, + params: current.params, + })) + }) + } + /// Dispatch C6 proof verification through ShareVerificationActor. pub fn dispatch_c6_verification( &mut self, @@ -612,6 +678,9 @@ impl Handler for ThresholdPlaintextAggregator { EnclaveEventData::ComputeResponse(data) => { self.notify_sync(ctx, TypedEvent::new(data, ec)) } + EnclaveEventData::CommitteeMemberExpelled(data) => { + self.notify_sync(ctx, TypedEvent::new(data, ec)) + } EnclaveEventData::ShareVerificationComplete(data) => { self.notify_sync(ctx, TypedEvent::new(data, ec)) } @@ -719,6 +788,37 @@ impl Handler> for ThresholdPlaintextAggregator { } } +impl Handler> for ThresholdPlaintextAggregator { + type Result = (); + + fn handle( + &mut self, + msg: TypedEvent, + _ctx: &mut Self::Context, + ) -> Self::Result { + trap( + EType::PlaintextAggregation, + &self.bus.with_ec(msg.get_ctx()), + || { + let (msg, ec) = msg.into_components(); + let Some(party_id) = msg.party_id else { + return Ok(()); + }; + + self.handle_member_expelled(party_id, &ec)?; + + if let Some(ThresholdPlaintextAggregatorState::VerifyingC6(ref state)) = + self.state.get() + { + self.dispatch_c6_verification(state.c6_proofs.clone(), ec)?; + } + + Ok(()) + }, + ) + } +} + impl Handler> for ThresholdPlaintextAggregator { type Result = (); fn handle( diff --git a/crates/ciphernode-builder/src/ciphernode_builder.rs b/crates/ciphernode-builder/src/ciphernode_builder.rs index 44c7b44899..054965d17c 100644 --- a/crates/ciphernode-builder/src/ciphernode_builder.rs +++ b/crates/ciphernode-builder/src/ciphernode_builder.rs @@ -32,8 +32,9 @@ use e3_net::{ }; use e3_request::E3Router; use e3_sortition::{ - CiphernodeSelector, CiphernodeSelectorFactory, FinalizedCommitteesRepositoryFactory, - NodeStateRepositoryFactory, Sortition, SortitionBackend, SortitionRepositoryFactory, + CiphernodeSelector, CiphernodeSelectorFactory, EmitPersistedAggregatorState, + FinalizedCommitteesRepositoryFactory, NodeStateRepositoryFactory, Sortition, SortitionBackend, + SortitionRepositoryFactory, }; use e3_sync::sync; use e3_utils::SharedRng; @@ -443,7 +444,7 @@ impl CiphernodeBuilder { repositories.node_state(), repositories.finalized_committees(), default_backend, - ciphernode_selector, + ciphernode_selector.clone(), &addr, ) .await?; @@ -548,6 +549,7 @@ impl CiphernodeBuilder { info!("E3Router building..."); e3_builder.build().await?; + ciphernode_selector.do_send(EmitPersistedAggregatorState); let topic = "enclave-gossip"; let (peer_id, interface, channel_bridge) = if let Some(net_config) = self.net_config { @@ -735,7 +737,6 @@ async fn setup_evm_system( &bus, write_provider.clone(), contract.address()?, - pubkey_agg, ); info!("CiphernodeRegistrySolWriter attached for publishing committees"); diff --git a/crates/ciphernode-entrypoint.sh b/crates/ciphernode-entrypoint.sh index 01679d4326..b7b1640180 100644 --- a/crates/ciphernode-entrypoint.sh +++ b/crates/ciphernode-entrypoint.sh @@ -4,7 +4,6 @@ set -e # Paths to config and secrets CONFIG_FILE="$CONFIG_DIR/config.yaml" SECRETS_FILE="/run/secrets/secrets.json" -AGGREGATOR="$AGGREGATOR" # Ensure required files exist if [ ! -f "$CONFIG_FILE" ]; then @@ -27,7 +26,7 @@ if [ -z "$PRIVATE_KEY" ] || [ -z "$PASSWORD" ] || [ -z "$NETWORK_PRIVATE_KEY" ]; exit 1 fi -# Set password and private key +# Set password echo "Setting password" enclave password set --config "$CONFIG_FILE" --password "$PASSWORD" @@ -35,15 +34,10 @@ enclave password set --config "$CONFIG_FILE" --password "$PASSWORD" echo "Setting network private key" enclave net set-key --config "$CONFIG_FILE" --net-keypair "$NETWORK_PRIVATE_KEY" -if [ "$AGGREGATOR" = "true" ]; then - echo "Setting private key" - enclave wallet set --config "$CONFIG_FILE" --private-key "$PRIVATE_KEY" +echo "Setting wallet key" +enclave wallet set --config "$CONFIG_FILE" --private-key "$PRIVATE_KEY" - echo "Starting aggregator" - exec enclave start -v --config "$CONFIG_FILE" -else - echo "Starting Ciphernode" - exec enclave start -v --config "$CONFIG_FILE" -fi +echo "Starting ciphernode" +exec enclave start -v --config "$CONFIG_FILE" diff --git a/crates/cli/src/start.rs b/crates/cli/src/start.rs index 3f5c54416e..ae7d6c13f6 100644 --- a/crates/cli/src/start.rs +++ b/crates/cli/src/start.rs @@ -12,7 +12,7 @@ use crate::{ }; use anyhow::Result; use e3_ciphernode_builder::CiphernodeHandle; -use e3_config::{AppConfig, NodeRole}; +use e3_config::AppConfig; use e3_console::Console; use e3_events::{prelude::*, Shutdown}; use e3_socket_server::start_socket_server; @@ -84,23 +84,7 @@ pub async fn build_ciphernode( // add cli peers to the config config.add_peers(peers); - let node = match config.role() { - // Launch in aggregator configuration - NodeRole::Aggregator { - pubkey_write_path, - plaintext_write_path, - } => { - e3_entrypoint::start::aggregator_start::execute( - &config, - pubkey_write_path, - plaintext_write_path, - ) - .await? - } - - // Launch in ciphernode configuration - NodeRole::Ciphernode => e3_entrypoint::start::start::execute(&config).await?, - }; + let node = e3_entrypoint::start::start::execute(&config).await?; Ok(node) } diff --git a/crates/config/src/app_config.rs b/crates/config/src/app_config.rs index 7e027c5a0c..d5d88328e2 100644 --- a/crates/config/src/app_config.rs +++ b/crates/config/src/app_config.rs @@ -22,26 +22,6 @@ use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::{collections::HashMap, env, path::PathBuf}; -/// Either "aggregator" or "ciphernode" -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] -#[serde(rename_all = "lowercase")] -#[serde(tag = "type")] -pub enum NodeRole { - /// Aggregator role - Aggregator { - pubkey_write_path: Option, - plaintext_write_path: Option, - }, - /// Ciphernode role - Ciphernode, -} - -impl Default for NodeRole { - fn default() -> Self { - NodeRole::Ciphernode - } -} - /// The structure within the app configuration #[derive(Debug, Clone, Deserialize, Serialize)] #[serde(default)] @@ -65,9 +45,6 @@ pub struct NodeDefinition { pub data_dir: PathBuf, /// Override the base folder for enclave configuration defaults to `~/.config/enclave/{name}` on linux pub config_dir: PathBuf, - /// The node role eg. "ciphernode" or "aggregator" - #[serde(default)] - pub role: NodeRole, /// If a net key has not been set autogenerate one on start pub autonetkey: bool, /// If a password has not been set autogenerate one on start @@ -88,7 +65,6 @@ impl Default for NodeDefinition { log_file: PathBuf::from("log"), // ~/.config/enclave/log config_dir: std::path::PathBuf::new(), // ~/.config/enclave data_dir: std::path::PathBuf::new(), // ~/.config/enclave - role: NodeRole::Ciphernode, autonetkey: false, autopassword: false, autowallet: false, @@ -377,26 +353,6 @@ impl AppConfig { &self.nodes } - /// Get the node's role and enriched relevant provided configuration - pub fn role(&self) -> NodeRole { - match self.node_def().role.clone() { - NodeRole::Aggregator { - pubkey_write_path, - plaintext_write_path, - } => NodeRole::Aggregator { - // Normalize paths so that these paths are based on the config dir if they are - // relative - pubkey_write_path: pubkey_write_path - .as_ref() - .map(|p| self.paths.relative_to_config(p)), - plaintext_write_path: plaintext_write_path - .as_ref() - .map(|p| self.paths.relative_to_config(p)), - }, - NodeRole::Ciphernode => NodeRole::Ciphernode, - } - } - /// Get the value of autonetkey pub fn autonetkey(&self) -> bool { self.autonetkey @@ -599,10 +555,6 @@ nodes: peers: - "one" - "two" - role: - type: aggregator - pubkey_write_path: "./output/pubkey.bin" - plaintext_write_path: "./output/plaintext.txt" "#; { @@ -658,17 +610,6 @@ nodes: ); assert_eq!(config.db_file(), PathBuf::from("/mydata/enclave/ag/db")); assert_eq!(config.key_file(), PathBuf::from("/myconfig/enclave/ag/key")); - - // Write paths should be relative to config file if they are relative - assert_eq!( - config.role(), - NodeRole::Aggregator { - pubkey_write_path: Some(PathBuf::from("/default/config/output/pubkey.bin")), - plaintext_write_path: Some(PathBuf::from( - "/default/config/output/plaintext.txt" - )) - } - ); }; Ok(()) } @@ -702,8 +643,6 @@ nodes: expected_config_dir.join("enclave.config.yaml") ); - assert_eq!(config.role(), NodeRole::Ciphernode); - Ok(()) }); } diff --git a/crates/entrypoint/src/start/aggregator_start.rs b/crates/entrypoint/src/start/aggregator_start.rs deleted file mode 100644 index 32b428aa85..0000000000 --- a/crates/entrypoint/src/start/aggregator_start.rs +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use anyhow::Result; -use e3_ciphernode_builder::{CiphernodeBuilder, CiphernodeHandle}; -use e3_config::AppConfig; -use e3_crypto::Cipher; -use e3_test_helpers::{PlaintextWriter, PublicKeyWriter}; -use e3_zk_prover::ZkBackend; -use rand::SeedableRng; -use rand_chacha::{rand_core::OsRng, ChaCha20Rng}; -use std::{ - path::PathBuf, - sync::{Arc, Mutex}, -}; - -pub async fn execute( - config: &AppConfig, - pubkey_write_path: Option, - plaintext_write_path: Option, -) -> Result { - let rng = Arc::new(Mutex::new(ChaCha20Rng::from_rng(OsRng)?)); - let cipher = Arc::new(Cipher::from_file(config.key_file()).await?); - let backend = ZkBackend::new(config.bb_binary(), config.circuits_dir(), config.work_dir()); - - let node = CiphernodeBuilder::new(rng.clone(), cipher.clone()) - .with_persistence(&config.log_file(), &config.db_file()) - .with_chains(&config.chains()) - .with_sortition_score() - .with_contract_enclave_full() - .with_contract_bonding_registry() - .with_contract_ciphernode_registry() - .with_contract_slashing_manager() - .with_max_threads() - .with_zkproof(backend) - .with_pubkey_aggregation() - .with_threshold_plaintext_aggregation() - .with_net(config.peers(), config.quic_port()) - .with_shared_store() - .with_shared_eventstore() - .build() - .await?; - - // These are here purely for our integration test so leaving out of the builder - if let Some(path) = pubkey_write_path { - PublicKeyWriter::attach(&path, node.bus().clone()); - } - - if let Some(path) = plaintext_write_path { - PlaintextWriter::attach(&path, node.bus().clone()); - } - - Ok(node) -} diff --git a/crates/entrypoint/src/start/mod.rs b/crates/entrypoint/src/start/mod.rs index 53458bcd64..1ec7e86a99 100644 --- a/crates/entrypoint/src/start/mod.rs +++ b/crates/entrypoint/src/start/mod.rs @@ -4,5 +4,4 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -pub mod aggregator_start; pub mod start; diff --git a/crates/entrypoint/src/start/start.rs b/crates/entrypoint/src/start/start.rs index f1db8bd827..8c35c90633 100644 --- a/crates/entrypoint/src/start/start.rs +++ b/crates/entrypoint/src/start/start.rs @@ -24,13 +24,15 @@ pub async fn execute(config: &AppConfig) -> Result { .with_persistence(&config.log_file(), &config.db_file()) .with_sortition_score() .with_chains(&config.chains()) - .with_contract_enclave_reader() + .with_contract_enclave_full() .with_contract_bonding_registry() .with_max_threads() .with_contract_ciphernode_registry() .with_contract_slashing_manager() .with_trbfv() .with_zkproof(backend) + .with_pubkey_aggregation() + .with_threshold_plaintext_aggregation() .with_net(config.peers(), config.quic_port()) .with_shared_store() .with_shared_eventstore() diff --git a/crates/events/src/committee.rs b/crates/events/src/committee.rs index b55a2a04e9..76096cfddf 100644 --- a/crates/events/src/committee.rs +++ b/crates/events/src/committee.rs @@ -1,80 +1,109 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// Ordered list of committee members where index == party_id. -/// -/// Provides O(1) address→party_id lookups via an internal index. -/// The index is eagerly rebuilt when the struct is deserialized. -/// -/// `PartialEq` compares only the `members` vec (the canonical data); -/// the `index` is a derived cache. -#[derive(Clone, Debug, Serialize)] -pub struct Committee { - /// Ordered member list — index == party_id. - members: Vec, - /// Lowercased-address → party_id for O(1) lookup. - #[serde(skip)] - index: HashMap, -} - -impl PartialEq for Committee { - fn eq(&self, other: &Self) -> bool { - self.members == other.members - } -} - -impl Eq for Committee {} - -impl<'de> Deserialize<'de> for Committee { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - #[derive(Deserialize)] - struct Inner { - members: Vec, - } - let inner = Inner::deserialize(deserializer)?; - Ok(Committee::new(inner.members)) - } -} - -impl Committee { - pub fn new(members: Vec) -> Self { - let index = members - .iter() - .enumerate() - .map(|(i, addr)| (addr.to_lowercase(), i as u64)) - .collect(); - Self { members, index } - } - - /// Resolve an address to its party_id (position in the committee list). - pub fn party_id_for(&self, addr: &str) -> Option { - self.index.get(&addr.to_lowercase()).copied() - } - - /// Check if an address is a committee member. - pub fn contains(&self, addr: &str) -> bool { - self.party_id_for(addr).is_some() - } - - /// The ordered member list (index == party_id). - pub fn members(&self) -> &[String] { - &self.members - } - - pub fn len(&self) -> usize { - self.members.len() - } - - pub fn is_empty(&self) -> bool { - self.members.is_empty() - } -} +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// Ordered list of committee members where index == party_id. +/// +/// Provides O(1) address→party_id lookups via an internal index. +/// The index is eagerly rebuilt when the struct is deserialized. +/// +/// `PartialEq` compares only the `members` vec (the canonical data); +/// the `index` is a derived cache. +#[derive(Clone, Debug, Serialize)] +pub struct Committee { + /// Ordered member list — index == party_id. + members: Vec, + /// Lowercased-address → party_id for O(1) lookup. + #[serde(skip)] + index: HashMap, +} + +impl PartialEq for Committee { + fn eq(&self, other: &Self) -> bool { + self.members == other.members + } +} + +impl Eq for Committee {} + +impl<'de> Deserialize<'de> for Committee { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Inner { + members: Vec, + } + let inner = Inner::deserialize(deserializer)?; + Ok(Committee::new(inner.members)) + } +} + +impl Committee { + pub fn new(members: Vec) -> Self { + let index = members + .iter() + .enumerate() + .map(|(i, addr)| (addr.to_lowercase(), i as u64)) + .collect(); + + Self { members, index } + } + + /// Resolve an address to its party_id (position in the committee list). + pub fn party_id_for(&self, addr: &str) -> Option { + self.index.get(&addr.to_lowercase()).copied() + } + + /// Check if an address is a committee member. + pub fn contains(&self, addr: &str) -> bool { + self.party_id_for(addr).is_some() + } + + /// The ordered member list (index == party_id). + pub fn members(&self) -> &[String] { + &self.members + } + + pub fn len(&self) -> usize { + self.members.len() + } + + pub fn is_empty(&self) -> bool { + self.members.is_empty() + } + + pub fn is_active_aggregator(&self, my_addr: &str, expelled: &[u64]) -> bool { + (0..self.members.len() as u64) + .find(|party_id| !expelled.contains(party_id)) + .and_then(|party_id| self.members.get(party_id as usize)) + .map(|addr| addr.eq_ignore_ascii_case(my_addr)) + .unwrap_or(false) + } +} + +#[cfg(test)] +mod tests { + use super::Committee; + + #[test] + fn picks_lowest_non_expelled_party_in_sorted_committee_as_aggregator() { + // Committee order is already score-sorted before it is stored. + let committee = Committee::new(vec![ + "0xbbb".to_string(), + "0xccc".to_string(), + "0xaaa".to_string(), + ]); + + assert!(committee.is_active_aggregator("0xBbB", &[])); + assert!(committee.is_active_aggregator("0xccc", &[0])); + assert!(committee.is_active_aggregator("0xaaa", &[0, 1])); + assert!(!committee.is_active_aggregator("0xaaa", &[0, 1, 2])); + } +} diff --git a/crates/events/src/enclave_event/aggregator_changed.rs b/crates/events/src/enclave_event/aggregator_changed.rs new file mode 100644 index 0000000000..bcdd4d606d --- /dev/null +++ b/crates/events/src/enclave_event/aggregator_changed.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use crate::E3id; +use actix::Message; +use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display}; + +#[derive(Message, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[rtype(result = "()")] +pub struct AggregatorChanged { + pub e3_id: E3id, + pub is_aggregator: bool, +} + +impl Display for AggregatorChanged { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "AggregatorChanged {{ e3_id: {}, is_aggregator: {} }}", + self.e3_id, self.is_aggregator + ) + } +} diff --git a/crates/events/src/enclave_event/mod.rs b/crates/events/src/enclave_event/mod.rs index c931eec527..065517d96e 100644 --- a/crates/events/src/enclave_event/mod.rs +++ b/crates/events/src/enclave_event/mod.rs @@ -8,6 +8,7 @@ mod accusation_quorum_reached; mod accusation_vote; mod aggregation_proof_pending; mod aggregation_proof_signed; +mod aggregator_changed; mod ciphernode_added; mod ciphernode_removed; mod ciphernode_selected; @@ -73,6 +74,7 @@ pub use accusation_quorum_reached::*; pub use accusation_vote::*; pub use aggregation_proof_pending::*; pub use aggregation_proof_signed::*; +pub use aggregator_changed::*; pub use ciphernode_added::*; pub use ciphernode_removed::*; pub use ciphernode_selected::*; @@ -234,6 +236,7 @@ macro_rules! impl_event_types { pub enum EnclaveEventData { AccusationQuorumReached(AccusationQuorumReached), AccusationVote(AccusationVote), + AggregatorChanged(AggregatorChanged), ProofFailureAccusation(ProofFailureAccusation), ProofVerificationFailed(ProofVerificationFailed), ProofVerificationPassed(ProofVerificationPassed), @@ -533,6 +536,7 @@ impl EnclaveEventData { match self { EnclaveEventData::AccusationQuorumReached(ref data) => Some(data.e3_id.clone()), EnclaveEventData::AccusationVote(ref data) => Some(data.e3_id.clone()), + EnclaveEventData::AggregatorChanged(ref data) => Some(data.e3_id.clone()), EnclaveEventData::ProofFailureAccusation(ref data) => Some(data.e3_id.clone()), EnclaveEventData::ProofVerificationFailed(ref data) => Some(data.e3_id.clone()), EnclaveEventData::ProofVerificationPassed(ref data) => Some(data.e3_id.clone()), @@ -610,6 +614,7 @@ impl WithAggregateId for EnclaveEvent { impl_event_types!( AccusationQuorumReached, AccusationVote, + AggregatorChanged, ProofFailureAccusation, ProofVerificationFailed, ProofVerificationPassed, diff --git a/crates/events/src/enclave_event/ticket_generated.rs b/crates/events/src/enclave_event/ticket_generated.rs index 1530240705..6d513a82aa 100644 --- a/crates/events/src/enclave_event/ticket_generated.rs +++ b/crates/events/src/enclave_event/ticket_generated.rs @@ -20,14 +20,15 @@ pub struct TicketGenerated { pub e3_id: E3id, pub ticket_id: TicketId, pub node: String, + pub party_index: Option, } impl Display for TicketGenerated { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "e3_id: {}, ticket_id: {:?}, node: {}", - self.e3_id, self.ticket_id, self.node + "e3_id: {}, ticket_id: {:?}, node: {}, party_index: {:?}", + self.e3_id, self.ticket_id, self.node, self.party_index ) } } diff --git a/crates/evm/src/ciphernode_registry_sol.rs b/crates/evm/src/ciphernode_registry_sol.rs index d9e06e27a9..7c6cebae72 100644 --- a/crates/evm/src/ciphernode_registry_sol.rs +++ b/crates/evm/src/ciphernode_registry_sol.rs @@ -5,7 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::{ - error_decoder::format_evm_error, + error_decoder::{decode_error_from_str, format_evm_error}, events::{EnclaveEvmEvent, EvmEventProcessor}, evm_parser::EvmParser, helpers::{encode_zk_proof, send_tx_with_retry, EthProvider}, @@ -20,11 +20,13 @@ use alloy::{ }; use anyhow::Result; use e3_events::{ - prelude::*, run_once, BusHandle, CommitteeFinalizeRequested, CommitteeFinalized, E3id, EType, - EffectsEnabled, EnclaveEvent, EnclaveEventData, EventSubscriber, EventType, OrderedSet, Proof, - PublicKeyAggregated, Seed, Shutdown, TicketGenerated, TicketId, + prelude::*, AggregatorChanged, BusHandle, CommitteeFinalizeRequested, CommitteeFinalized, + E3RequestComplete, E3id, EType, EffectsEnabled, EnclaveEvent, EnclaveEventData, + EventSubscriber, EventType, OrderedSet, Proof, PublicKeyAggregated, Seed, Shutdown, + TicketGenerated, TicketId, }; use e3_utils::{ArcBytes, NotifySync, MAILBOX_LIMIT}; +use std::collections::HashMap; use tracing::{error, info, trace}; sol!( @@ -270,6 +272,8 @@ pub struct CiphernodeRegistrySolWriter

{ provider: EthProvider

, contract_address: Address, bus: BusHandle, + effects_enabled: bool, + active_aggregators: HashMap, } impl CiphernodeRegistrySolWriter

{ @@ -282,46 +286,32 @@ impl CiphernodeRegistrySolWriter provider, contract_address, bus: bus.clone(), + effects_enabled: false, + active_aggregators: HashMap::new(), }) } - pub fn attach( - bus: &BusHandle, - provider: EthProvider

, - contract_address: Address, - is_aggregator: bool, - ) { - let runner = run_once::({ - let bus = bus.clone(); - move |_| { - let addr = - CiphernodeRegistrySolWriter::new(&bus, provider, contract_address)?.start(); - - if is_aggregator { - bus.subscribe_all( - &[ - EventType::PublicKeyAggregated, - EventType::CommitteeFinalizeRequested, - ], - addr.clone().into(), - ) - } - - bus.subscribe_all( - &[ - // Subscribe to TicketGenerated for ticket submission - EventType::TicketGenerated, - // Stop gracefully on shutdown - EventType::Shutdown, - ], - addr.clone().into(), - ); - - Ok(()) - } - }); + pub fn attach(bus: &BusHandle, provider: EthProvider

, contract_address: Address) { + let addr = CiphernodeRegistrySolWriter::new(bus, provider, contract_address) + .expect("failed to create CiphernodeRegistrySolWriter") + .start(); + + bus.subscribe_all( + &[ + EventType::EffectsEnabled, + EventType::AggregatorChanged, + EventType::PublicKeyAggregated, + EventType::CommitteeFinalizeRequested, + EventType::TicketGenerated, + EventType::E3RequestComplete, + EventType::Shutdown, + ], + addr.into(), + ); + } - bus.subscribe(EventType::EffectsEnabled, runner.recipient()); + fn is_active_aggregator_for(&self, e3_id: &E3id) -> bool { + self.active_aggregators.get(e3_id).copied().unwrap_or(false) } } @@ -340,6 +330,8 @@ impl Handler fn handle(&mut self, msg: EnclaveEvent, ctx: &mut Self::Context) -> Self::Result { match msg.into_data() { + EnclaveEventData::EffectsEnabled(data) => self.notify_sync(ctx, data), + EnclaveEventData::AggregatorChanged(data) => self.notify_sync(ctx, data), EnclaveEventData::PublicKeyAggregated(data) => { // Only publish if the src and destination chains match if self.provider.chain_id() == data.e3_id.chain_id() { @@ -357,18 +349,53 @@ impl Handler ctx.notify(data); } } + EnclaveEventData::E3RequestComplete(data) => self.notify_sync(ctx, data), EnclaveEventData::Shutdown(data) => self.notify_sync(ctx, data), _ => (), } } } +impl Handler + for CiphernodeRegistrySolWriter

+{ + type Result = (); + + fn handle(&mut self, _: EffectsEnabled, _: &mut Self::Context) -> Self::Result { + self.effects_enabled = true; + } +} + +impl Handler + for CiphernodeRegistrySolWriter

+{ + type Result = (); + + fn handle(&mut self, msg: AggregatorChanged, _: &mut Self::Context) -> Self::Result { + self.active_aggregators.insert(msg.e3_id, msg.is_aggregator); + } +} + +impl Handler + for CiphernodeRegistrySolWriter

+{ + type Result = (); + + fn handle(&mut self, msg: E3RequestComplete, _: &mut Self::Context) -> Self::Result { + self.active_aggregators.remove(&msg.e3_id); + } +} + impl Handler for CiphernodeRegistrySolWriter

{ type Result = ResponseFuture<()>; fn handle(&mut self, msg: TicketGenerated, _: &mut Self::Context) -> Self::Result { + if !self.effects_enabled { + return Box::pin(async {}); + } + match msg.ticket_id { TicketId::Score(ticket_id) => { info!( @@ -408,12 +435,32 @@ impl Handler; fn handle(&mut self, msg: CommitteeFinalizeRequested, _: &mut Self::Context) -> Self::Result { + if !self.effects_enabled { + return Box::pin(async {}); + } + let e3_id = msg.e3_id.clone(); let contract_address = self.contract_address; let provider = self.provider.clone(); let bus = self.bus.clone(); Box::pin(async move { + match should_finalize_committee(provider.clone(), contract_address, e3_id.clone()).await + { + Ok(false) => { + info!(e3_id = %e3_id, "Skipping finalizeCommittee; on-chain state is not finalizable"); + return; + } + Err(err) => { + error!( + "Failed to preflight finalizeCommittee: {}", + format_evm_error(&err) + ); + return; + } + Ok(true) => {} + } + info!("Finalizing committee for E3 {:?}", e3_id); let result = finalize_committee_on_registry(provider, contract_address, e3_id).await; @@ -436,6 +483,10 @@ impl Handler; fn handle(&mut self, msg: PublicKeyAggregated, _: &mut Self::Context) -> Self::Result { + if !self.effects_enabled || !self.is_active_aggregator_for(&msg.e3_id) { + return Box::pin(async {}); + } + let e3_id = msg.e3_id.clone(); let pubkey = msg.pubkey.clone(); let nodes = msg.nodes.clone(); @@ -445,6 +496,22 @@ impl Handler { + info!(e3_id = %e3_id, "Skipping publishCommittee; committee public key already published"); + return; + } + Err(err) => { + error!( + "Failed to preflight publishCommittee: {}", + format_evm_error(&err) + ); + return; + } + Ok(true) => {} + } + let result = publish_committee_to_registry( provider, contract_address, @@ -542,6 +609,62 @@ pub async fn finalize_committee_on_registry( + provider: EthProvider

, + contract_address: Address, + e3_id: E3id, +) -> Result { + let e3_id_u256: U256 = e3_id.try_into()?; + let contract = ICiphernodeRegistry::new(contract_address, provider.provider()); + if contract.isOpen(e3_id_u256).call().await? { + return Ok(false); + } + + match contract.finalizeCommittee(e3_id_u256).call().await { + Ok(_) => Ok(true), + Err(err) => { + let err = anyhow::Error::from(err); + let decoded = decode_error_from_str(&format!("{err:?}")); + + if decoded.as_deref().is_some_and(|message| { + message.contains("CommitteeAlreadyFinalized") + || message.contains("CommitteeNotRequested") + || message.contains("SubmissionWindowNotClosed") + || message.contains("ThresholdNotMet") + }) { + return Ok(false); + } + + Err(err) + } + } +} + +async fn should_publish_committee( + provider: EthProvider

, + contract_address: Address, + e3_id: E3id, +) -> Result { + let e3_id_u256: U256 = e3_id.try_into()?; + let contract = ICiphernodeRegistry::new(contract_address, provider.provider()); + match contract.committeePublicKey(e3_id_u256).call().await { + Ok(_) => Ok(false), + Err(err) => { + let err = anyhow::Error::from(err); + let decoded = decode_error_from_str(&format!("{err:?}")); + + if decoded + .as_deref() + .is_some_and(|message| message.contains("CommitteeNotPublished")) + { + return Ok(true); + } + + Err(err) + } + } +} + pub async fn publish_committee_to_registry( provider: EthProvider

, contract_address: Address, @@ -601,14 +724,10 @@ impl CiphernodeRegistrySol { CiphernodeRegistrySolReader::setup(processor) } - pub fn attach_writer

( - bus: &BusHandle, - provider: EthProvider

, - contract_address: Address, - is_aggregator: bool, - ) where + pub fn attach_writer

(bus: &BusHandle, provider: EthProvider

, contract_address: Address) + where P: Provider + WalletProvider + Clone + 'static, { - CiphernodeRegistrySolWriter::attach(bus, provider, contract_address, is_aggregator); + CiphernodeRegistrySolWriter::attach(bus, provider, contract_address); } } diff --git a/crates/evm/src/enclave_sol_writer.rs b/crates/evm/src/enclave_sol_writer.rs index f35ab1c4fa..fb00726456 100644 --- a/crates/evm/src/enclave_sol_writer.rs +++ b/crates/evm/src/enclave_sol_writer.rs @@ -19,15 +19,17 @@ use alloy::{ }; use anyhow::Result; use e3_events::BusHandle; +use e3_events::E3RequestComplete; +use e3_events::EnclaveEvent; use e3_events::EnclaveEventData; use e3_events::EventType; use e3_events::Shutdown; -use e3_events::{prelude::*, EffectsEnabled}; -use e3_events::{run_once, EnclaveEvent}; +use e3_events::{prelude::*, AggregatorChanged, EffectsEnabled}; use e3_events::{E3Stage, E3StageChanged}; use e3_events::{E3id, EType, PlaintextAggregated, Proof}; use e3_utils::NotifySync; use e3_utils::MAILBOX_LIMIT; +use std::collections::HashMap; use tracing::info; sol!( @@ -41,6 +43,8 @@ pub struct EnclaveSolWriter

{ provider: EthProvider

, contract_address: Address, bus: BusHandle, + effects_enabled: bool, + active_aggregators: HashMap, } impl EnclaveSolWriter

{ @@ -53,27 +57,30 @@ impl EnclaveSolWriter

{ provider, contract_address, bus: bus.clone(), + effects_enabled: false, + active_aggregators: HashMap::new(), }) } pub fn attach(bus: &BusHandle, provider: EthProvider

, contract_address: Address) { - let addr = run_once::({ - let bus = bus.clone(); - move |_| { - let addr = EnclaveSolWriter::new(&bus, provider, contract_address)?.start(); - bus.subscribe_all( - &[ - EventType::PlaintextAggregated, - EventType::E3StageChanged, - EventType::Shutdown, - ], - addr.clone().into(), - ); - Ok(()) - } - }); + let addr = EnclaveSolWriter::new(bus, provider, contract_address) + .expect("failed to create EnclaveSolWriter") + .start(); + bus.subscribe_all( + &[ + EventType::EffectsEnabled, + EventType::AggregatorChanged, + EventType::PlaintextAggregated, + EventType::E3StageChanged, + EventType::E3RequestComplete, + EventType::Shutdown, + ], + addr.into(), + ); + } - bus.subscribe(EventType::EffectsEnabled, addr.recipient()); + fn is_active_aggregator_for(&self, e3_id: &E3id) -> bool { + self.active_aggregators.get(e3_id).copied().unwrap_or(false) } } @@ -89,6 +96,8 @@ impl Handler for E fn handle(&mut self, msg: EnclaveEvent, ctx: &mut Self::Context) -> Self::Result { match msg.into_data() { + EnclaveEventData::EffectsEnabled(data) => self.notify_sync(ctx, data), + EnclaveEventData::AggregatorChanged(data) => self.notify_sync(ctx, data), EnclaveEventData::PlaintextAggregated(data) => { // Only publish if the src and destination chains match if self.provider.chain_id() == data.e3_id.chain_id() { @@ -104,18 +113,53 @@ impl Handler for E ctx.notify(data); } } + EnclaveEventData::E3RequestComplete(data) => self.notify_sync(ctx, data), EnclaveEventData::Shutdown(data) => self.notify_sync(ctx, data), _ => (), } } } +impl Handler + for EnclaveSolWriter

+{ + type Result = (); + + fn handle(&mut self, _: EffectsEnabled, _: &mut Self::Context) -> Self::Result { + self.effects_enabled = true; + } +} + +impl Handler + for EnclaveSolWriter

+{ + type Result = (); + + fn handle(&mut self, msg: AggregatorChanged, _: &mut Self::Context) -> Self::Result { + self.active_aggregators.insert(msg.e3_id, msg.is_aggregator); + } +} + +impl Handler + for EnclaveSolWriter

+{ + type Result = (); + + fn handle(&mut self, msg: E3RequestComplete, _: &mut Self::Context) -> Self::Result { + self.active_aggregators.remove(&msg.e3_id); + } +} + impl Handler for EnclaveSolWriter

{ type Result = ResponseFuture<()>; fn handle(&mut self, msg: PlaintextAggregated, _: &mut Self::Context) -> Self::Result { + if !self.effects_enabled || !self.is_active_aggregator_for(&msg.e3_id) { + return Box::pin(async {}); + } + Box::pin({ let e3_id = msg.e3_id.clone(); let decrypted_output = msg.decrypted_output.clone(); @@ -154,6 +198,26 @@ impl Handler { + info!(e3_id = %e3_id, "Skipping publishPlaintextOutput; plaintext already published"); + return; + } + Err(err) => { + bus.err( + EType::Evm, + anyhow::anyhow!( + "Error preflighting plaintext publication: {}", + format_evm_error(&err) + ), + ); + return; + } + Ok(true) => {} + } + let result = publish_plaintext_output( provider, contract_address, @@ -196,21 +260,29 @@ impl Handler type Result = ResponseFuture<()>; fn handle(&mut self, msg: E3StageChanged, _: &mut Self::Context) -> Self::Result { + if !self.effects_enabled { + return Box::pin(async {}); + } + Box::pin({ + let e3_id = msg.e3_id.clone(); let contract_address = self.contract_address; let provider = self.provider.clone(); async move { - let result = - process_e3_failure(provider, contract_address, msg.e3_id.clone()).await; + let result = process_e3_failure(provider, contract_address, e3_id.clone()).await; match result { Ok(receipt) => { - info!(tx=%receipt.transaction_hash, "Called processE3Failure for E3 {}", msg.e3_id); + info!( + tx=%receipt.transaction_hash, + e3_id = %e3_id, + "Called processE3Failure" + ); } Err(err) => { - // Non-fatal: may revert if already processed or no payment info!( - "processE3Failure for E3 {} did not succeed (may already be processed): {:?}", - msg.e3_id, err + e3_id = %e3_id, + "processE3Failure did not succeed (may already be processed): {}", + format_evm_error(&err) ); } } @@ -266,6 +338,17 @@ async fn publish_plaintext_output( .await } +async fn should_publish_plaintext( + provider: EthProvider

, + contract_address: Address, + e3_id: E3id, +) -> Result { + let e3_id: U256 = e3_id.try_into()?; + let contract = IEnclave::new(contract_address, provider.provider()); + let e3 = contract.getE3(e3_id).call().await?; + Ok(e3.plaintextOutput.is_empty()) +} + async fn process_e3_failure( provider: EthProvider

, contract_address: Address, @@ -273,22 +356,16 @@ async fn process_e3_failure( ) -> Result { let e3_id: U256 = e3_id.try_into()?; - send_tx_with_retry("processE3Failure", &[], || { - info!("processE3Failure() e3_id={:?}", e3_id); - let provider = provider.clone(); - - async move { - let from_address = provider.provider().default_signer_address(); - let current_nonce = provider - .provider() - .get_transaction_count(from_address) - .pending() - .await?; - let contract = IEnclave::new(contract_address, provider.provider()); - let builder = contract.processE3Failure(e3_id).nonce(current_nonce); - let receipt = builder.send().await?.get_receipt().await?; - Ok(receipt) - } - }) - .await + info!("processE3Failure() e3_id={:?}", e3_id); + + let from_address = provider.provider().default_signer_address(); + let current_nonce = provider + .provider() + .get_transaction_count(from_address) + .pending() + .await?; + let contract = IEnclave::new(contract_address, provider.provider()); + let builder = contract.processE3Failure(e3_id).nonce(current_nonce); + let receipt = builder.send().await?.get_receipt().await?; + Ok(receipt) } diff --git a/crates/keyshare/src/ext.rs b/crates/keyshare/src/ext.rs index e2c67e22d8..4446f8203b 100644 --- a/crates/keyshare/src/ext.rs +++ b/crates/keyshare/src/ext.rs @@ -53,6 +53,10 @@ impl E3Extension for ThresholdKeyshareExtension { return; }; + if ctx.get_event_recipient("threshold_keyshare").is_some() { + return; + } + let e3_id = data.clone().e3_id; let party_id = data.clone().party_id; let Some(meta) = ctx.get_dependency(META_KEY) else { diff --git a/crates/keyshare/src/threshold_keyshare.rs b/crates/keyshare/src/threshold_keyshare.rs index f1f4f14b3a..9292f38bbf 100644 --- a/crates/keyshare/src/threshold_keyshare.rs +++ b/crates/keyshare/src/threshold_keyshare.rs @@ -577,6 +577,21 @@ impl ThresholdKeyshare { self_addr: Addr, ) -> Result<()> { let state = self.state.try_get()?; + if !matches!( + state.state, + KeyshareState::CollectingEncryptionKeys(_) + | KeyshareState::GeneratingThresholdShare(_) + | KeyshareState::AggregatingDecryptionKey(_) + ) { + trace!( + e3_id = %state.e3_id, + state = state.variant_name(), + sender_party_id = msg.share.party_id, + "Ignoring ThresholdShareCreated outside share collection" + ); + return Ok(()); + } + let my_party_id = state.party_id; // Filter: only process shares intended for this party @@ -609,6 +624,19 @@ impl ThresholdKeyshare { self_addr: Addr, ) -> Result<()> { let state = self.state.try_get()?; + if !matches!( + state.state, + KeyshareState::Init | KeyshareState::CollectingEncryptionKeys(_) + ) { + trace!( + e3_id = %state.e3_id, + state = state.variant_name(), + sender_party_id = msg.key.party_id, + "Ignoring EncryptionKeyCreated outside key collection" + ); + return Ok(()); + } + // Reject keys from expelled parties if state.expelled_parties.contains(&msg.key.party_id) { info!( @@ -741,6 +769,16 @@ impl ThresholdKeyshare { address: Addr, ) -> Result<()> { let (msg, ec) = msg.into_components(); + let state = self.state.try_get()?; + if !matches!(state.state, KeyshareState::Init) { + info!( + e3_id = %state.e3_id, + state = state.variant_name(), + "Ignoring replayed CiphernodeSelected; keyshare already initialized" + ); + return Ok(()); + } + info!("CiphernodeSelected received."); // Ensure the collectors are created let _ = self.ensure_collector(address.clone()); @@ -755,7 +793,6 @@ impl ThresholdKeyshare { let sk_bfv_encrypted = SensitiveBytes::new(sk_bytes, &self.cipher)?; let pk_bfv_bytes = ArcBytes::from_bytes(&pk_bfv.to_bytes()); - let state = self.state.try_get()?; let e3_id = state.e3_id.clone(); self.state.try_mutate(&ec, |s| { diff --git a/crates/net/src/net_sync_manager.rs b/crates/net/src/net_sync_manager.rs index 93f1ce003c..a555249644 100644 --- a/crates/net/src/net_sync_manager.rs +++ b/crates/net/src/net_sync_manager.rs @@ -30,13 +30,16 @@ use crate::{ /// failed before publishing `NetReady` anyway. const NET_READY_CONNECT_TIMEOUT: Duration = Duration::from_secs(60); -/// Maximum time to wait for the `AllPeersDialed` event before giving up. -const ALL_PEERS_DIALED_TIMEOUT: Duration = Duration::from_secs(120); +/// Direct-request retry settings for a single historical sync fetch attempt. +const SYNC_FETCH_MAX_RETRIES: u32 = 3; +const SYNC_FETCH_RETRY_TIMEOUT: Duration = Duration::from_secs(5); -/// Maximum time to wait for a peer to reconnect after sync fetch fails. -/// On restart, peers may briefly connect then disconnect (the remote side still -/// holds the old connection). Kademlia rediscovery can take up to ~120s. -const SYNC_RECONNECT_TIMEOUT: Duration = Duration::from_secs(120); +/// If a historical sync fetch fails, wait this long for a fresh connection +/// before retrying anyway against currently connected peers. +const SYNC_RECOVERY_RETRY_INTERVAL: Duration = Duration::from_secs(15); + +/// Number of recovery rounds to try for failed aggregates after the initial fetch pass. +const SYNC_RECOVERY_MAX_ATTEMPTS: usize = 3; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SyncResponseValue { @@ -345,6 +348,27 @@ struct AllPeersDialed { #[rtype(result = "()")] struct PeerConnected; +async fn fetch_historical_events_for_aggregate( + net_cmds: &mpsc::Sender, + net_events: &Arc>, + aggregate_id: AggregateId, + since: u128, +) -> Result>> { + let requester = DirectRequester::builder(net_cmds.clone(), net_events.clone()) + .max_retries(SYNC_FETCH_MAX_RETRIES) + .retry_timeout(SYNC_FETCH_RETRY_TIMEOUT) + .build(); + + fetch_all_batched_events::>( + requester, + PeerTarget::Random, + aggregate_id, + since, + 100, + ) + .await +} + async fn handle_sync_request_event( net_cmds: mpsc::Sender, net_events: Arc>, @@ -398,18 +422,8 @@ async fn handle_sync_request_event( "Requesting batched events for aggregate_id={} since={}", aggregate_id, since ); - let requester = DirectRequester::builder(net_cmds.clone(), net_events.clone()) - .max_retries(10) - .retry_timeout(Duration::from_secs(5)) - .build(); - match fetch_all_batched_events::>( - requester, - PeerTarget::Random, - *aggregate_id, - *since, - 100, - ) - .await + match fetch_historical_events_for_aggregate(&net_cmds, &net_events, *aggregate_id, *since) + .await { Ok(events) => { info!( @@ -435,79 +449,92 @@ async fn handle_sync_request_event( } } - // If any aggregate failed (likely "no connected peers"), wait for a peer - // to reconnect and retry. This handles the restart scenario where peers - // briefly connect then immediately disconnect (the remote side still holds - // the old connection and rejects the new one). Reconnection via Kademlia - // can take up to ~120s. + // If any aggregate failed, retry a few recovery rounds. Prefer a fresh + // ConnectionEstablished signal when one arrives, but do not depend on it: + // a connected peer may simply be slow or temporarily stalled. if !failed_aggregates.is_empty() { info!( - "Sync fetch failed for {} aggregates — waiting for peer reconnection...", + "Sync fetch failed for {} aggregates — starting recovery retries...", failed_aggregates.len() ); - match await_event( - &net_events, - |e| { - if matches!(e, NetEvent::ConnectionEstablished { .. }) { - Some(()) - } else { - None - } - }, - SYNC_RECONNECT_TIMEOUT, - ) - .await - { - Ok(()) => { - info!("Peer reconnected, retrying failed aggregates"); - let mut still_failed = Vec::new(); - for aggregate_id in failed_aggregates { - let since = event.since.get(&aggregate_id).copied().unwrap_or(0); - let requester = - DirectRequester::builder(net_cmds.clone(), net_events.clone()).build(); - match fetch_all_batched_events::>( - requester, - PeerTarget::Random, - aggregate_id, - since, - 100, - ) - .await - { - Ok(events) => { - info!( - "Retry succeeded: {} events for aggregate_id={}", - events.len(), - aggregate_id - ); - for enclave_event in events { - let ts = enclave_event.ts(); - if ts > latest_timestamp { - latest_timestamp = ts; - } - all_events.push(enclave_event); - } - } - Err(e) => { - warn!("Retry also failed for aggregate_id={}: {e}", aggregate_id); - still_failed.push(aggregate_id); - } + let mut recovery_attempt = 0; + + while !failed_aggregates.is_empty() && recovery_attempt < SYNC_RECOVERY_MAX_ATTEMPTS { + recovery_attempt += 1; + + match await_event( + &net_events, + |e| { + if matches!(e, NetEvent::ConnectionEstablished { .. }) { + Some(()) + } else { + None } + }, + SYNC_RECOVERY_RETRY_INTERVAL, + ) + .await + { + Ok(()) => { + info!( + attempt = recovery_attempt, + "Peer reconnected, retrying failed aggregates" + ); } - if !still_failed.is_empty() { - bail!( - "failed to fetch historical net events for aggregates: {:?}", - still_failed + Err(_) => { + info!( + attempt = recovery_attempt, + retry_after = ?SYNC_RECOVERY_RETRY_INTERVAL, + "No new peer connection observed; retrying failed aggregates against current peers" ); } } - Err(_) => { - bail!( - "failed to fetch historical net events for aggregates: {:?} (no peers reconnected within {:?})", - failed_aggregates, - SYNC_RECONNECT_TIMEOUT - ); + + let mut still_failed = Vec::new(); + for aggregate_id in failed_aggregates { + let since = event.since.get(&aggregate_id).copied().unwrap_or(0); + match fetch_historical_events_for_aggregate( + &net_cmds, + &net_events, + aggregate_id, + since, + ) + .await + { + Ok(events) => { + info!( + attempt = recovery_attempt, + "Retry succeeded: {} events for aggregate_id={}", + events.len(), + aggregate_id + ); + for enclave_event in events { + let ts = enclave_event.ts(); + if ts > latest_timestamp { + latest_timestamp = ts; + } + all_events.push(enclave_event); + } + } + Err(e) => { + warn!( + attempt = recovery_attempt, + "Retry failed for aggregate_id={}: {e}", aggregate_id + ); + still_failed.push(aggregate_id); + } + } } + + failed_aggregates = still_failed; + } + + if !failed_aggregates.is_empty() { + bail!( + "failed to fetch historical net events for aggregates: {:?} after {} recovery attempts", + failed_aggregates, + SYNC_RECOVERY_MAX_ATTEMPTS + ); } } diff --git a/crates/sortition/src/ciphernode_selector.rs b/crates/sortition/src/ciphernode_selector.rs index 481e0cee35..3c2cf247dd 100644 --- a/crates/sortition/src/ciphernode_selector.rs +++ b/crates/sortition/src/ciphernode_selector.rs @@ -10,14 +10,18 @@ use anyhow::bail; use anyhow::Result; use e3_data::{AutoPersist, Persistable, Repository}; use e3_events::E3RequestComplete; +use e3_events::EventContext; +use e3_events::Sequenced; use e3_events::TypedEvent; use e3_events::{ - prelude::*, trap, BusHandle, CiphernodeSelected, CommitteeFinalized, E3Requested, E3id, EType, - EnclaveEvent, EnclaveEventData, EventType, Shutdown, TicketGenerated, TicketId, + prelude::*, trap, AggregatorChanged, BusHandle, CiphernodeSelected, Committee, + CommitteeFinalized, CommitteeMemberExpelled, E3Requested, E3id, EType, EnclaveEvent, + EnclaveEventData, EventType, Shutdown, TicketGenerated, TicketId, }; use e3_request::E3Meta; use e3_utils::NotifySync; use e3_utils::MAILBOX_LIMIT; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use tracing::info; @@ -34,12 +38,24 @@ fn e3_meta_from(req: &E3Requested) -> E3Meta { } } +#[derive(Clone, Debug, Serialize, Deserialize, Default)] +pub struct CiphernodeSelectorState { + pub e3_cache: HashMap, + pub committees: HashMap, + pub expelled: HashMap>, + pub is_aggregator: HashMap, +} + +#[derive(Message, Debug, Clone, Copy)] +#[rtype(result = "()")] +pub struct EmitPersistedAggregatorState; + /// CiphernodeSelector is an actor that determines if a ciphernode is part of a committee and if so /// emits a TicketGenerated event (score sortition) to the event bus pub struct CiphernodeSelector { bus: BusHandle, address: String, - e3_cache: Persistable>, + state: Persistable, } impl Actor for CiphernodeSelector { @@ -52,32 +68,74 @@ impl Actor for CiphernodeSelector { impl CiphernodeSelector { pub fn new( bus: &BusHandle, - e3_cache: Persistable>, + state: Persistable, address: &str, ) -> Self { Self { bus: bus.clone(), - e3_cache, + state, address: address.to_owned(), } } pub async fn attach( bus: &BusHandle, - selector_store: Repository>, + selector_store: Repository, address: &str, ) -> Result> { - let e3_cache = selector_store.load_or_default(HashMap::new()).await?; - let addr = CiphernodeSelector::new(bus, e3_cache, address).start(); + let state = selector_store + .load_or_default(CiphernodeSelectorState::default()) + .await?; + let addr = CiphernodeSelector::new(bus, state, address).start(); bus.subscribe(EventType::E3Requested, addr.clone().recipient()); bus.subscribe(EventType::E3RequestComplete, addr.clone().recipient()); bus.subscribe(EventType::CommitteeFinalized, addr.clone().recipient()); + bus.subscribe(EventType::CommitteeMemberExpelled, addr.clone().recipient()); bus.subscribe(EventType::Shutdown, addr.clone().recipient()); info!("CiphernodeSelector listening!"); Ok(addr) } + + fn update_aggregator_status( + &mut self, + e3_id: &E3id, + ec: &EventContext, + force_emit: bool, + ) -> Result<()> { + let Some(state) = self.state.get() else { + bail!("Could not get selector state"); + }; + + let committee = state + .committees + .get(e3_id) + .cloned() + .ok_or_else(|| anyhow::anyhow!("Missing finalized committee for {}", e3_id))?; + let expelled = state.expelled.get(e3_id).cloned().unwrap_or_default(); + let is_aggregator = committee.is_active_aggregator(&self.address, &expelled); + let previous = state.is_aggregator.get(e3_id).copied(); + + self.state.try_mutate(ec, |mut selector_state| { + selector_state + .is_aggregator + .insert(e3_id.clone(), is_aggregator); + Ok(selector_state) + })?; + + if force_emit || previous != Some(is_aggregator) { + self.bus.publish( + AggregatorChanged { + e3_id: e3_id.clone(), + is_aggregator, + }, + ec.clone(), + )?; + } + + Ok(()) + } } impl Handler for CiphernodeSelector { @@ -92,6 +150,9 @@ impl Handler for CiphernodeSelector { EnclaveEventData::CommitteeFinalized(data) => { self.notify_sync(ctx, TypedEvent::new(data, ec)) } + EnclaveEventData::CommitteeMemberExpelled(data) => { + self.notify_sync(ctx, TypedEvent::new(data, ec)) + } EnclaveEventData::Shutdown(data) => self.notify_sync(ctx, data), _ => (), } @@ -113,11 +174,12 @@ impl Handler> for CiphernodeSelector { fn handle(&mut self, msg: TypedEvent, _: &mut Self::Context) -> Self::Result { trap(EType::Sortition, &self.bus.with_ec(msg.get_ctx()), || { - self.e3_cache.try_mutate(msg.get_ctx(), |mut cache| { - cache + self.state.try_mutate(msg.get_ctx(), |mut state| { + state + .e3_cache .entry(msg.e3_id.clone()) .or_insert_with(|| e3_meta_from(&msg)); - Ok(cache) + Ok(state) }) }) } @@ -132,13 +194,15 @@ impl Handler>> for CiphernodeSelecto _: &mut Self::Context, ) -> Self::Result { trap(EType::Sortition, &self.bus.with_ec(data.get_ctx()), || { - self.e3_cache.try_mutate(data.get_ctx(), |mut cache| { + self.state.try_mutate(data.get_ctx(), |mut state| { info!( - "Mutating e3_cache: appending data: {:?}", + "Mutating selector state: appending data: {:?}", data.e3_id.clone() ); - cache.insert(data.e3_id.clone(), e3_meta_from(&data)); - Ok(cache) + state + .e3_cache + .insert(data.e3_id.clone(), e3_meta_from(&data)); + Ok(state) })?; if !data.is_selected() { @@ -156,6 +220,7 @@ impl Handler>> for CiphernodeSelecto e3_id: data.e3_id.clone(), ticket_id: TicketId::Score(tid), node: data.address().to_owned(), + party_index: data.party_id(), }, data.get_ctx().to_owned(), )?; @@ -177,9 +242,12 @@ impl Handler> for CiphernodeSelector { EType::Sortition, &self.bus.with_ec(msg.get_ctx()), move || { - self.e3_cache.try_mutate(msg.get_ctx(), |mut cache| { - cache.remove(&msg.e3_id); - Ok(cache) + self.state.try_mutate(msg.get_ctx(), |mut state| { + state.e3_cache.remove(&msg.e3_id); + state.committees.remove(&msg.e3_id); + state.expelled.remove(&msg.e3_id); + state.is_aggregator.remove(&msg.e3_id); + Ok(state) }) }, ) @@ -198,57 +266,61 @@ impl Handler> for CiphernodeSelector { EType::Sortition, &self.bus.with_ec(msg.get_ctx()), move || { - let (msg, ec) = msg.into_components(); + let (mut msg, ec) = msg.into_components(); + msg.sort_by_score(); info!("CiphernodeSelector received CommitteeFinalized."); let bus = self.bus.clone(); - info!("Getting e3_cache..."); - let Some(e3_cache) = self.e3_cache.get() else { - bail!("Could not get cache"); + info!("Getting selector state..."); + let Some(state) = self.state.get() else { + bail!("Could not get selector state"); }; info!("Getting e3_meta..."); - let Some(e3_meta) = e3_cache.get(&msg.e3_id) else { + let Some(e3_meta) = state.e3_cache.get(&msg.e3_id) else { bail!( "Could not find E3Meta on CiphernodeSelector for {}", msg.e3_id ); }; - // Check if this node is in the finalized committee - if !msg.committee.contains(&self.address) { - info!(node = self.address, "Node not in finalized committee"); - return Ok(()); - } + self.state.try_mutate(&ec, |mut selector_state| { + selector_state + .committees + .insert(msg.e3_id.clone(), Committee::new(msg.committee.clone())); + selector_state + .expelled + .entry(msg.e3_id.clone()) + .or_default(); + Ok(selector_state) + })?; - // Retrieve E3 metadata from repository - let Some(party_id) = msg.committee.iter().position(|addr| addr == &self.address) - else { + // Check if this node is in the finalized committee + if let Some(party_id) = msg.committee.iter().position(|addr| addr == &self.address) + { info!( node = self.address, - "Node address not found in committee list (should not happen)" + party_id = party_id, + "Node is in finalized committee, emitting CiphernodeSelected" ); - return Ok(()); - }; - info!( - node = self.address, - party_id = party_id, - "Node is in finalized committee, emitting CiphernodeSelected" - ); + bus.publish( + CiphernodeSelected { + party_id: party_id as u64, + e3_id: msg.e3_id.clone(), + threshold_m: e3_meta.threshold_m, + threshold_n: e3_meta.threshold_n, + esi_per_ct: e3_meta.esi_per_ct, + error_size: e3_meta.error_size.clone(), + params: e3_meta.params.clone(), + seed: e3_meta.seed, + }, + ec.clone(), + )?; + } else { + info!(node = self.address, "Node not in finalized committee"); + } - bus.publish( - CiphernodeSelected { - party_id: party_id as u64, - e3_id: msg.e3_id, - threshold_m: e3_meta.threshold_m, - threshold_n: e3_meta.threshold_n, - esi_per_ct: e3_meta.esi_per_ct, - error_size: e3_meta.error_size.clone(), - params: e3_meta.params.clone(), - seed: e3_meta.seed, - }, - ec, - )?; + self.update_aggregator_status(&msg.e3_id, &ec, true)?; Ok(()) }, @@ -256,6 +328,57 @@ impl Handler> for CiphernodeSelector { } } +impl Handler> for CiphernodeSelector { + type Result = (); + + fn handle( + &mut self, + msg: TypedEvent, + _ctx: &mut Self::Context, + ) -> Self::Result { + trap(EType::Sortition, &self.bus.with_ec(msg.get_ctx()), || { + let (msg, ec) = msg.into_components(); + let Some(party_id) = msg.party_id else { + return Ok(()); + }; + + self.state.try_mutate(&ec, |mut state| { + let expelled = state.expelled.entry(msg.e3_id.clone()).or_default(); + if !expelled.contains(&party_id) { + expelled.push(party_id); + expelled.sort_unstable(); + } + Ok(state) + })?; + + self.update_aggregator_status(&msg.e3_id, &ec, false) + }) + } +} + +impl Handler for CiphernodeSelector { + type Result = (); + + fn handle( + &mut self, + _: EmitPersistedAggregatorState, + _ctx: &mut Self::Context, + ) -> Self::Result { + let Some(state) = self.state.get() else { + return; + }; + + for (e3_id, is_aggregator) in state.is_aggregator { + if let Err(err) = self.bus.publish_without_context(AggregatorChanged { + e3_id, + is_aggregator, + }) { + self.bus.err(EType::Sortition, err); + } + } + } +} + impl Handler for CiphernodeSelector { type Result = (); fn handle(&mut self, _msg: Shutdown, ctx: &mut Self::Context) -> Self::Result { diff --git a/crates/sortition/src/repo.rs b/crates/sortition/src/repo.rs index 0b25aad99b..7553c99e2a 100644 --- a/crates/sortition/src/repo.rs +++ b/crates/sortition/src/repo.rs @@ -6,9 +6,9 @@ use crate::backends::SortitionBackend; use crate::sortition::NodeStateStore; +use crate::CiphernodeSelectorState; use e3_data::{Repositories, Repository}; use e3_events::{Committee, E3id, StoreKeys}; -use e3_request::E3Meta; use std::collections::HashMap; pub trait SortitionRepositoryFactory { @@ -22,11 +22,11 @@ impl SortitionRepositoryFactory for Repositories { } pub trait CiphernodeSelectorFactory { - fn ciphernode_selector(&self) -> Repository>; + fn ciphernode_selector(&self) -> Repository; } impl CiphernodeSelectorFactory for Repositories { - fn ciphernode_selector(&self) -> Repository> { + fn ciphernode_selector(&self) -> Repository { Repository::new(self.store.scope(StoreKeys::ciphernode_selector())) } } diff --git a/crates/sortition/src/sortition.rs b/crates/sortition/src/sortition.rs index d46805e6bc..6a924bedf3 100644 --- a/crates/sortition/src/sortition.rs +++ b/crates/sortition/src/sortition.rs @@ -796,7 +796,8 @@ impl Handler> for Sortition { msg: TypedEvent, _ctx: &mut Self::Context, ) -> Self::Result { - let (msg, ec) = msg.into_components(); + let (mut msg, ec) = msg.into_components(); + msg.sort_by_score(); trap(EType::Sortition, &self.bus.with_ec(&ec), || { info!( e3_id = %msg.e3_id, diff --git a/crates/tests/tests/integration.rs b/crates/tests/tests/integration.rs index f746c899c3..360a98447a 100644 --- a/crates/tests/tests/integration.rs +++ b/crates/tests/tests/integration.rs @@ -13,10 +13,11 @@ use e3_ciphernode_builder::{CiphernodeBuilder, EventSystem}; use e3_config::BBPath; use e3_crypto::Cipher; use e3_events::{ - prelude::*, BusHandle, CiphertextOutputPublished, CommitteeFinalized, ConfigurationUpdated, - E3Requested, E3id, EffectsEnabled, EnclaveEvent, EnclaveEventData, EventType, GetEvents, - HistoryCollector, OperatorActivationChanged, OrderedSet, PkAggregationProofPending, - PkAggregationProofRequest, PlaintextAggregated, Seed, TakeEvents, TicketBalanceUpdated, + prelude::*, BusHandle, CiphertextOutputPublished, CommitteeFinalized, ComputeRequestKind, + ComputeResponseKind, ConfigurationUpdated, E3Requested, E3id, EffectsEnabled, EnclaveEvent, + EnclaveEventData, EventType, GetEvents, HistoryCollector, OperatorActivationChanged, + OrderedSet, PkAggregationProofPending, PkAggregationProofRequest, PlaintextAggregated, + ProofType, Seed, TakeEvents, TicketBalanceUpdated, VerificationKind, ZkRequest, ZkResponse, }; use e3_fhe_params::DEFAULT_BFV_PRESET; use e3_fhe_params::{build_pair_for_preset, create_deterministic_crp_from_default_seed}; @@ -26,11 +27,14 @@ use e3_net::events::{GossipData, NetEvent}; use e3_net::NetEventTranslator; use e3_polynomial::CrtPolynomial; use e3_sortition::{calculate_buffer_size, RegisteredNode, ScoreSortition, Ticket}; -use e3_test_helpers::ciphernode_system::CiphernodeSystemBuilder; +use e3_test_helpers::ciphernode_system::{ + CiphernodeHistory, CiphernodeSystem, CiphernodeSystemBuilder, +}; use e3_test_helpers::{ create_seed_from_u64, create_shared_rng_from_u64, find_bb, with_tracing, AddToCommittee, }; use e3_trbfv::helpers::calculate_error_size; +use e3_trbfv::{TrBFVRequest, TrBFVResponse}; use e3_utils::utility_types::ArcBytes; use e3_utils::{colorize, rand_eth_addr, Color}; use e3_zk_helpers::{compute_modulus_bit, compute_threshold_pk_commitment}; @@ -499,6 +503,176 @@ fn determine_committee( Ok((committee, committee_scores, buffer_nodes)) } +fn find_node_index_by_address(nodes: &CiphernodeSystem, address: &str) -> Result { + for (index, node) in nodes.iter().enumerate() { + if node.address().eq_ignore_ascii_case(address) { + return Ok(index); + } + } + + bail!("Could not find node index for address {address}"); +} + +async fn expect_node_events_with_timeouts( + nodes: &CiphernodeSystem, + index: usize, + expected: &[&str], + total_to: Duration, + per_evt_to: Duration, +) -> Result { + let h = nodes + .take_history_with_timeouts(index, expected.len(), Some(total_to), Some(per_evt_to)) + .await + .map_err(|e| anyhow::anyhow!("FAILURE on node {index}: {expected:?} : {e}"))?; + + println!( + "node {index} >> {:?} == {:?}", + h.event_types(), + expected.to_vec() + ); + h.expect(expected.to_vec()); + Ok(h) +} + +fn project_history(history: &[EnclaveEvent], mut projector: F) -> Vec<&'static str> +where + F: FnMut(&EnclaveEventData) -> Option<&'static str>, +{ + history + .iter() + .filter_map(|event| projector(event.get_data())) + .collect() +} + +fn count_projected_events(projected: &[&str], event_type: &str) -> usize { + projected.iter().filter(|seen| **seen == event_type).count() +} + +fn publickey_aggregator_marker(data: &EnclaveEventData, e3_id: &E3id) -> Option<&'static str> { + match data { + EnclaveEventData::CommitteeFinalized(data) if data.e3_id == *e3_id => { + Some("CommitteeFinalized") + } + EnclaveEventData::CiphernodeSelected(data) if data.e3_id == *e3_id => { + Some("CiphernodeSelected") + } + EnclaveEventData::AggregatorChanged(data) if data.e3_id == *e3_id && data.is_aggregator => { + Some("AggregatorChanged") + } + EnclaveEventData::KeyshareCreated(data) if data.e3_id == *e3_id => Some("KeyshareCreated"), + EnclaveEventData::ShareVerificationDispatched(data) + if data.e3_id == *e3_id && data.kind == VerificationKind::PkGenerationProofs => + { + Some("ShareVerificationDispatched") + } + EnclaveEventData::CommitmentConsistencyCheckRequested(data) + if data.e3_id == *e3_id && data.kind == VerificationKind::PkGenerationProofs => + { + Some("CommitmentConsistencyCheckRequested") + } + EnclaveEventData::CommitmentConsistencyCheckComplete(data) + if data.e3_id == *e3_id && data.kind == VerificationKind::PkGenerationProofs => + { + Some("CommitmentConsistencyCheckComplete") + } + EnclaveEventData::ProofVerificationPassed(data) + if data.e3_id == *e3_id && data.proof_type == ProofType::C1PkGeneration => + { + Some("ProofVerificationPassed") + } + EnclaveEventData::ShareVerificationComplete(data) + if data.e3_id == *e3_id && data.kind == VerificationKind::PkGenerationProofs => + { + Some("ShareVerificationComplete") + } + EnclaveEventData::PkAggregationProofPending(data) if data.e3_id == *e3_id => { + Some("PkAggregationProofPending") + } + EnclaveEventData::PkAggregationProofSigned(data) if data.e3_id == *e3_id => { + Some("PkAggregationProofSigned") + } + EnclaveEventData::DKGRecursiveAggregationComplete(data) if data.e3_id == *e3_id => { + Some("DKGRecursiveAggregationComplete") + } + EnclaveEventData::PublicKeyAggregated(data) if data.e3_id == *e3_id => { + Some("PublicKeyAggregated") + } + _ => None, + } +} + +fn plaintext_aggregator_marker(data: &EnclaveEventData, e3_id: &E3id) -> Option<&'static str> { + match data { + EnclaveEventData::CiphertextOutputPublished(data) if data.e3_id == *e3_id => { + Some("CiphertextOutputPublished") + } + EnclaveEventData::DecryptionshareCreated(data) if data.e3_id == *e3_id => { + Some("DecryptionshareCreated") + } + EnclaveEventData::ShareVerificationDispatched(data) + if data.e3_id == *e3_id && data.kind == VerificationKind::ThresholdDecryptionProofs => + { + Some("ShareVerificationDispatched") + } + EnclaveEventData::CommitmentConsistencyCheckRequested(data) + if data.e3_id == *e3_id && data.kind == VerificationKind::ThresholdDecryptionProofs => + { + Some("CommitmentConsistencyCheckRequested") + } + EnclaveEventData::CommitmentConsistencyCheckComplete(data) + if data.e3_id == *e3_id && data.kind == VerificationKind::ThresholdDecryptionProofs => + { + Some("CommitmentConsistencyCheckComplete") + } + EnclaveEventData::ComputeRequest(data) + if data.e3_id == *e3_id + && matches!( + &data.request, + ComputeRequestKind::Zk(ZkRequest::VerifyShareProofs(_)) + | ComputeRequestKind::TrBFV(TrBFVRequest::CalculateThresholdDecryption(_)) + | ComputeRequestKind::Zk(ZkRequest::DecryptedSharesAggregation(_)) + | ComputeRequestKind::Zk(ZkRequest::FoldProofs { .. }) + ) => + { + Some("ComputeRequest") + } + EnclaveEventData::ComputeResponse(data) + if data.e3_id == *e3_id + && matches!( + &data.response, + ComputeResponseKind::Zk(ZkResponse::VerifyShareProofs(_)) + | ComputeResponseKind::TrBFV(TrBFVResponse::CalculateThresholdDecryption( + _ + )) + | ComputeResponseKind::Zk(ZkResponse::DecryptedSharesAggregation(_)) + | ComputeResponseKind::Zk(ZkResponse::FoldProofs(_)) + ) => + { + Some("ComputeResponse") + } + EnclaveEventData::ProofVerificationPassed(data) + if data.e3_id == *e3_id && data.proof_type == ProofType::C6ThresholdShareDecryption => + { + Some("ProofVerificationPassed") + } + EnclaveEventData::ShareVerificationComplete(data) + if data.e3_id == *e3_id && data.kind == VerificationKind::ThresholdDecryptionProofs => + { + Some("ShareVerificationComplete") + } + EnclaveEventData::AggregationProofPending(data) if data.e3_id == *e3_id => { + Some("AggregationProofPending") + } + EnclaveEventData::AggregationProofSigned(data) if data.e3_id == *e3_id => { + Some("AggregationProofSigned") + } + EnclaveEventData::PlaintextAggregated(data) if data.e3_id == *e3_id => { + Some("PlaintextAggregated") + } + _ => None, + } +} + async fn setup_score_sortition_environment( bus: &BusHandle, eth_addrs: &Vec, @@ -653,6 +827,8 @@ async fn test_trbfv_actor() -> Result<()> { let (zk_backend, _zk_temp) = setup_test_zk_backend().await; let nodes = CiphernodeSystemBuilder::new() + // All nodes run the same binary under the aggregator-committee model. + // Node 0 stays an observer only because it is excluded from sortition registration. // Adding 20 total nodes: 3 for committee + 3 buffer = 6 selected, 14 unselected .add_group(1, || async { let addr = rand_eth_addr(&rng); @@ -678,13 +854,16 @@ async fn test_trbfv_actor() -> Result<()> { let addr = rand_eth_addr(&rng); println!("Building normal {}", &addr); CiphernodeBuilder::new(rng.clone(), cipher.clone()) + .testmode_with_history() .with_shared_taskpool(&task_pool) .with_multithread_concurrent_jobs(concurrent_jobs) .with_shared_multithread_report(&multithread_report) .with_trbfv() .with_zkproof(zk_backend.clone()) .testmode_with_signer(PrivateKeySigner::random()) + .with_pubkey_aggregation() .with_sortition_score() + .with_threshold_plaintext_aggregation() .testmode_with_forked_bus(bus.event_bus()) .testmode_ignore_address_check() .with_logging() @@ -771,6 +950,17 @@ async fn test_trbfv_actor() -> Result<()> { buffer_nodes.len() )); + let active_aggregator_addr = committee + .first() + .cloned() + .expect("committee should have an active aggregator"); + let active_aggregator_index = find_node_index_by_address(&nodes, &active_aggregator_addr)?; + + println!( + "Resolved active aggregator: node index {} ({})", + active_aggregator_index, active_aggregator_addr + ); + nodes.expect_events(&["E3Requested"]).await?; bus.publish_without_context(CommitteeFinalized { @@ -789,54 +979,70 @@ async fn test_trbfv_actor() -> Result<()> { committee_finalized_timer.elapsed(), )); - // Collector (node 0): prefix order differs by `proof_aggregation_enabled` (see flow-trace DKG). - // Then C1/C5 verification, then if aggregation: DKGRecursive ×3 + cross-node fold ZK, then - // PublicKeyAggregated. + // Node 0 is a non-committee observer. It only sees bus-global events and the forwardable + // gossip events from the active aggregator flow. let shares_to_pubkey_agg_timer = Instant::now(); const KS3: [&str; 3] = ["KeyshareCreated"; 3]; const DKG3: [&str; 3] = ["DKGRecursiveAggregationComplete"; 3]; - const C1_C5: [&str; 13] = [ + const ACTIVE_AGGREGATOR_C1_C5: [&str; 9] = [ "ShareVerificationDispatched", "CommitmentConsistencyCheckRequested", "CommitmentConsistencyCheckComplete", - "ComputeRequest", - "ComputeResponse", "ProofVerificationPassed", "ProofVerificationPassed", "ProofVerificationPassed", "ShareVerificationComplete", "PkAggregationProofPending", - "ComputeRequest", - "ComputeResponse", "PkAggregationProofSigned", ]; - const DKG_FOLD: [&str; 4] = [ - "ComputeRequest", - "ComputeResponse", - "ComputeRequest", - "ComputeResponse", - ]; - let mut expected_events: Vec<&'static str> = Vec::new(); + let mut expected_events: Vec<&'static str> = vec!["AggregatorChanged"]; if proof_aggregation_enabled { expected_events.extend_from_slice(&KS3); + expected_events.extend_from_slice(&DKG3); } else { expected_events.extend_from_slice(&DKG3); expected_events.extend_from_slice(&KS3); } - expected_events.extend_from_slice(&C1_C5); + expected_events.push("PublicKeyAggregated"); + let h = expect_node_events_with_timeouts( + &nodes, + 0, + &expected_events, + Duration::from_secs(5000), + Duration::from_secs(5000), + ) + .await?; + + let active_aggregator_history = nodes.get_history(active_aggregator_index).await?; + let active_aggregator_pubkey_history_len = active_aggregator_history.len(); + let mut expected_active_aggregator_pubkey_events = vec![ + "CommitteeFinalized", + "CiphernodeSelected", + "AggregatorChanged", + ]; + if proof_aggregation_enabled { + expected_active_aggregator_pubkey_events.extend_from_slice(&KS3); + } else { + expected_active_aggregator_pubkey_events.extend_from_slice(&DKG3); + expected_active_aggregator_pubkey_events.extend_from_slice(&KS3); + } + expected_active_aggregator_pubkey_events.extend_from_slice(&ACTIVE_AGGREGATOR_C1_C5); if proof_aggregation_enabled { - expected_events.extend_from_slice(&DKG3); - expected_events.extend_from_slice(&DKG_FOLD); + expected_active_aggregator_pubkey_events.extend_from_slice(&DKG3); } - expected_events.push("PublicKeyAggregated"); - let h = nodes - .expect_events_with_timeouts( - &expected_events, - Duration::from_secs(5000), - Duration::from_secs(5000), - ) - .await?; + expected_active_aggregator_pubkey_events.push("PublicKeyAggregated"); + + // The active aggregator is also a selected committee member, so its node history contains + // local ThresholdKeyshare DKG work in addition to the public-key aggregation stage. Project + // only the deterministic pubkey-aggregation signals instead of comparing the whole raw node bus. + let active_aggregator_pubkey_events = project_history(&active_aggregator_history, |data| { + publickey_aggregator_marker(data, &e3_id) + }); + assert_eq!( + active_aggregator_pubkey_events, expected_active_aggregator_pubkey_events, + "Unexpected active aggregator public-key flow" + ); report.push(( "ThresholdShares -> PublicKeyAggregated", @@ -849,13 +1055,14 @@ async fn test_trbfv_actor() -> Result<()> { )); let app_gen_timer = Instant::now(); - // Verify we got the expected events (last should be PublicKeyAggregated) - // First we get the public key + // First we get the public key from the collector-visible gossip event. println!("Getting public key"); - let Some(EnclaveEventData::PublicKeyAggregated(pubkey_event)) = h.last().map(|e| e.get_data()) - else { + let Some(pubkey_event) = h.iter().rev().find_map(|event| match event.get_data() { + EnclaveEventData::PublicKeyAggregated(data) => Some(data.clone()), + _ => None, + }) else { panic!( - "Was expecting last event to be PublicKeyAggregated, got: {:?}", + "Was expecting collector history to contain PublicKeyAggregated, got: {:?}", h.event_types() ); }; @@ -904,59 +1111,129 @@ async fn test_trbfv_actor() -> Result<()> { println!("CiphertextOutputPublished event has been dispatched!"); - // Lets grab decryption share events - // The collector sees: - // - 1 CiphertextOutputPublished (from shared bus) - // - 3 DecryptionshareCreated (from simulate_libp2p, passes is_forwardable_event) - // - 1 ShareVerificationDispatched (C6 verification dispatched by ThresholdPlaintextAggregator) - // - 1 CommitmentConsistencyCheckRequested (pre-ZK consistency check) - // - 1 CommitmentConsistencyCheckComplete (consistency check result) - // - 1 ComputeRequest (C6 ZK verification) - // - 1 ComputeResponse (C6 ZK verification result) - // - 9 ProofVerificationPassed (3 parties × 3 C6 proofs per ciphertext) - // - 1 ShareVerificationComplete (C6 verification done) - // - 1 ComputeRequest (TrBFV CalculateThresholdDecryption) - // - 1 ComputeResponse (TrBFV CalculateThresholdDecryption) - // - 1 AggregationProofPending (C7 proof requested by ThresholdPlaintextAggregator) - // - 1 ComputeRequest (C7 proof generation) - // - 1 ComputeResponse (C7 proof result) - // - 1 AggregationProofSigned (C7 proof signed by ProofRequestActor) - // - If proof aggregation: ComputeRequest + ComputeResponse per C6 fold step (9 proofs -> 8 steps) - // - 1 PlaintextAggregated (with C7 + C6 proofs) - // - 1 E3RequestComplete (published after PlaintextAggregated by request router) - // Internal events from committee nodes (ComputeRequest/Response for CalculateDecryptionShare) - // stay on their local buses. + // The collector only sees the shared ciphertext event, gossiped decryption shares, and the + // final gossiped plaintext output. + const DS3: [&str; 3] = ["DecryptionshareCreated"; 3]; + let mut expected_events: Vec<&'static str> = vec!["CiphertextOutputPublished"]; + expected_events.extend_from_slice(&DS3); + expected_events.push("PlaintextAggregated"); + + let h = expect_node_events_with_timeouts( + &nodes, + 0, + &expected_events, + Duration::from_secs(1000), + Duration::from_secs(1000), + ) + .await?; + + let active_aggregator_history = nodes.get_history(active_aggregator_index).await?; + const C6_VERIFY_PREFIX: [&str; 19] = [ + "CiphertextOutputPublished", + "DecryptionshareCreated", + "DecryptionshareCreated", + "DecryptionshareCreated", + "ShareVerificationDispatched", + "CommitmentConsistencyCheckRequested", + "CommitmentConsistencyCheckComplete", + "ComputeRequest", + "ComputeResponse", + "ProofVerificationPassed", + "ProofVerificationPassed", + "ProofVerificationPassed", + "ProofVerificationPassed", + "ProofVerificationPassed", + "ProofVerificationPassed", + "ProofVerificationPassed", + "ProofVerificationPassed", + "ProofVerificationPassed", + "ShareVerificationComplete", + ]; + let active_aggregator_plaintext_events = project_history( + &active_aggregator_history[active_aggregator_pubkey_history_len..], + |data| plaintext_aggregator_marker(data, &e3_id), + ); + assert_eq!( + &active_aggregator_plaintext_events[..C6_VERIFY_PREFIX.len()], + C6_VERIFY_PREFIX, + "Unexpected active aggregator C6 verification prefix" + ); + + let completion_events = &active_aggregator_plaintext_events[C6_VERIFY_PREFIX.len()..]; let c6_proof_count = threshold_n as usize * num_votes_per_voter; - let c6_fold_steps = c6_proof_count.saturating_sub(1); - let c6_fold_events = if proof_aggregation_enabled { - 2 * c6_fold_steps + let c6_fold_steps = if proof_aggregation_enabled { + c6_proof_count.saturating_sub(1) } else { 0 }; - // Sum matches the comment above through AggregationProofSigned, then fold, then PlaintextAggregated. - // E3RequestComplete is not included (arrives after; not needed for take). - let expected_count = 1 // CiphertextOutputPublished - + 3 // DecryptionshareCreated - + 1 // ShareVerificationDispatched - + 2 // CommitmentConsistencyCheck (Requested + Complete) - + 2 // C6 ZK verification (ComputeRequest + ComputeResponse) - + 9 // ProofVerificationPassed (3 parties × 3 proofs) - + 1 // ShareVerificationComplete - + 2 // TrBFV computation (ComputeRequest + ComputeResponse) - + 1 // AggregationProofPending - + 2 // C7 proof (ComputeRequest + ComputeResponse) - + 1 // AggregationProofSigned - + c6_fold_events // C6 fold steps - + 1; // PlaintextAggregated - let h = nodes - .take_history_with_timeouts( - 0, - expected_count, - Some(Duration::from_secs(1000)), - Some(Duration::from_secs(1000)), - ) - .await?; + if proof_aggregation_enabled { + let aggregation_pending_index = completion_events + .iter() + .position(|event| *event == "AggregationProofPending") + .expect("AggregationProofPending should be present"); + let aggregation_signed_index = completion_events + .iter() + .position(|event| *event == "AggregationProofSigned") + .expect("AggregationProofSigned should be present"); + let plaintext_aggregated_index = completion_events + .iter() + .position(|event| *event == "PlaintextAggregated") + .expect("PlaintextAggregated should be present"); + + assert_eq!( + completion_events.len(), + 7 + (2 * c6_fold_steps), + "Unexpected active aggregator C6/C7 completion event count" + ); + assert_eq!( + &completion_events[..2], + ["ComputeRequest", "ComputeRequest"] + ); + assert_eq!( + count_projected_events(completion_events, "ComputeRequest"), + 2 + c6_fold_steps + ); + assert_eq!( + count_projected_events(completion_events, "ComputeResponse"), + 2 + c6_fold_steps + ); + assert_eq!( + count_projected_events(completion_events, "AggregationProofPending"), + 1 + ); + assert_eq!( + count_projected_events(completion_events, "AggregationProofSigned"), + 1 + ); + assert_eq!( + count_projected_events(completion_events, "PlaintextAggregated"), + 1 + ); + assert!( + aggregation_pending_index < aggregation_signed_index, + "AggregationProofPending must precede AggregationProofSigned" + ); + assert_eq!( + plaintext_aggregated_index, + completion_events.len() - 1, + "PlaintextAggregated must be the last active aggregator completion event" + ); + } else { + assert_eq!( + completion_events, + [ + "ComputeRequest", + "ComputeResponse", + "AggregationProofPending", + "ComputeRequest", + "ComputeResponse", + "AggregationProofSigned", + "PlaintextAggregated", + ], + "Unexpected active aggregator plaintext completion flow" + ); + } report.push(( "Ciphertext published -> PlaintextAggregated", diff --git a/crates/zk-prover/src/actors/commitment_links/mod.rs b/crates/zk-prover/src/actors/commitment_links/mod.rs index 1741b70a66..d69874d43d 100644 --- a/crates/zk-prover/src/actors/commitment_links/mod.rs +++ b/crates/zk-prover/src/actors/commitment_links/mod.rs @@ -70,13 +70,20 @@ pub trait CommitmentLink: Send + Sync { } /// Returns the default set of commitment links to register. +/// +/// C4→C6 links are disabled: the C4 Noir circuit and C6 Rust code compute +/// `compute_aggregated_shares_commitment` from incompatible representations +/// of the same aggregated polynomial (unreduced/uncentered/non-reversed vs +/// reduced/centered/reversed, plus different BIT parameters). See test +/// `c4_c6_commitment_mismatch_due_to_modular_reduction` in `c4a_to_c6.rs`. +/// Re-enable after aligning the commitment computation (circuit change needed). pub fn default_links() -> Vec> { vec![ Box::new(c0_to_c3::C3aToC0PkCommitmentLink), Box::new(c0_to_c3::C3bToC0PkCommitmentLink), Box::new(c1_to_c5::C1ToC5PkCommitmentLink), - Box::new(c4a_to_c6::C4aToC6SkCommitmentLink), - Box::new(c4b_to_c6::C4bToC6ESmCommitmentLink), Box::new(c6_to_c7::C6ToC7DCommitmentLink), + // Box::new(c4a_to_c6::C4aToC6SkCommitmentLink), + // Box::new(c4b_to_c6::C4bToC6ESmCommitmentLink), ] } diff --git a/dappnode/README.md b/dappnode/README.md index 370ba5c532..64cad8e183 100644 --- a/dappnode/README.md +++ b/dappnode/README.md @@ -49,7 +49,6 @@ Once this package is published to the DAppStore: - `RPC_URL` – WebSocket RPC endpoint (e.g. `wss://ethereum-sepolia-rpc.publicnode.com`) - `NETWORK` – e.g. `sepolia`, `mainnet`, `localhost` - Contract addresses + deploy blocks - - Node role (`ciphernode` or `aggregator`) - Optional keys and peers 4. Confirm and finish the installation. @@ -126,12 +125,8 @@ All runtime configuration is done via environment variables. They are: - **`NETWORK`** Logical network name written into the Enclave config (e.g. `sepolia`, `mainnet`, `localhost`). -- **`NODE_ROLE`** - - `ciphernode` – participate in threshold decryption. - - `aggregator` – coordinate operations, requires a wallet key. - -- **`ETH_ADDRESS`** Optional Ethereum address to bind the node to. Leave empty to let Enclave handle - it. +- **`NODE_ADDRESS`** Optional Ethereum address to bind the node to. Leave empty to let Enclave + handle it. - **`QUIC_PORT`** Internal UDP port used for QUIC [Quick UDP Internet Connections] P2P networking. Default in this package: `37173`. @@ -163,8 +158,7 @@ block heights. - **`NETWORK_PRIVATE_KEY`** Optional libp2p network key. If set, `entrypoint.sh` calls: - `enclave net set-key --config /data/config.yaml --net-keypair "$NETWORK_PRIVATE_KEY"` -- **`PRIVATE_KEY`** Optional Ethereum private key (hex). Only needed for aggregator mode. If set, - `entrypoint.sh` calls: +- **`PRIVATE_KEY`** Optional Ethereum private key (hex). If set, `entrypoint.sh` calls: - `enclave wallet set --config /data/config.yaml --private-key "$PRIVATE_KEY"` ### Peers @@ -189,11 +183,12 @@ screen after installation, as per DAppNode’s env behavior. At container startup, `entrypoint.sh`: 1. Validates `RPC_URL` is non-empty and starts with `ws://` or `wss://`. -2. Applies sensible defaults for `NETWORK`, `QUIC_PORT`, `NODE_ROLE`, and `LOG_LEVEL`. +2. Applies sensible defaults for `NETWORK`, `QUIC_PORT`, and `LOG_LEVEL`. 3. Uses `envsubst` to render `config.template.yaml` into `/data/config.yaml`, substituting: - - node address, role, ports - - network name and RPC URL - - contract addresses and deploy blocks + +- node address and ports +- network name and RPC URL +- contract addresses and deploy blocks 4. Optionally programs password, network key, and wallet key via the `enclave` CLI. 5. Builds CLI args, including verbosity and `--peer` flags from `PEERS`. diff --git a/dappnode/config.template.yaml b/dappnode/config.template.yaml index a5f5703542..254a34145c 100644 --- a/dappnode/config.template.yaml +++ b/dappnode/config.template.yaml @@ -4,8 +4,6 @@ node: address: '${NODE_ADDRESS}' quic_port: ${QUIC_PORT} - role: - type: ${NODE_ROLE} autonetkey: true autopassword: true autowallet: true diff --git a/dappnode/docker-compose.yml b/dappnode/docker-compose.yml index 512f8abd4d..9f6a05602c 100644 --- a/dappnode/docker-compose.yml +++ b/dappnode/docker-compose.yml @@ -16,7 +16,6 @@ services: RPC_URL: '' # Network & node config NETWORK: 'sepolia' - NODE_ROLE: 'ciphernode' NODE_ADDRESS: '' QUIC_PORT: '37173' PEERS: '' diff --git a/dappnode/entrypoint.sh b/dappnode/entrypoint.sh index e3ecdb7568..e4ae969d36 100644 --- a/dappnode/entrypoint.sh +++ b/dappnode/entrypoint.sh @@ -27,7 +27,6 @@ fi # Set defaults export NETWORK="${NETWORK:-sepolia}" export QUIC_PORT="${QUIC_PORT:-37173}" -export NODE_ROLE="${NODE_ROLE:-ciphernode}" export NODE_ADDRESS="${NODE_ADDRESS:-}" export LOG_LEVEL="${LOG_LEVEL:-info}" diff --git a/dappnode/setup-wizard.yml b/dappnode/setup-wizard.yml index 183fb8b259..286cb2d571 100644 --- a/dappnode/setup-wizard.yml +++ b/dappnode/setup-wizard.yml @@ -53,22 +53,6 @@ fields: required: false pattern: '^(0x[a-fA-F0-9]{64})?$' - # Node settings - - id: NODE_ROLE - target: - type: environment - name: NODE_ROLE - service: ciphernode - title: 'Node Role' - default: 'ciphernode' - description: | - - **ciphernode**: Participate in threshold decryption - - **aggregator**: Coordinate operations - enum: - - ciphernode - - aggregator - required: false - # Contracts - id: ENCLAVE_CONTRACT target: diff --git a/deploy/cn2.yaml b/deploy/cn2.yaml index 3445012c76..311bca41da 100644 --- a/deploy/cn2.yaml +++ b/deploy/cn2.yaml @@ -4,7 +4,7 @@ node: peers: - '/dns4/cn1/udp/9091/quic-v1' - '/dns4/cn3/udp/9093/quic-v1' - - '/dns4/aggregator/udp/9094/quic-v1' + - '/dns4/cn4/udp/9094/quic-v1' chains: - name: 'sepolia' rpc_url: '${RPC_URL}' diff --git a/deploy/cn3.yaml b/deploy/cn3.yaml index e12592eb8f..043d858e6b 100644 --- a/deploy/cn3.yaml +++ b/deploy/cn3.yaml @@ -4,7 +4,7 @@ node: peers: - '/dns4/cn1/udp/9091/quic-v1' - '/dns4/cn2/udp/9092/quic-v1' - - '/dns4/aggregator/udp/9094/quic-v1' + - '/dns4/cn4/udp/9094/quic-v1' chains: - name: 'sepolia' rpc_url: '${RPC_URL}' diff --git a/deploy/agg.yaml b/deploy/cn4.yaml similarity index 93% rename from deploy/agg.yaml rename to deploy/cn4.yaml index e767a2b2f0..d19a4841dc 100644 --- a/deploy/agg.yaml +++ b/deploy/cn4.yaml @@ -1,8 +1,6 @@ node: address: '${ADDRESS}' quic_port: ${QUIC_PORT} - role: - type: aggregator peers: - '/dns4/cn1/udp/9091/quic-v1' - '/dns4/cn2/udp/9092/quic-v1' diff --git a/deploy/copy-secrets.sh b/deploy/copy-secrets.sh index 2bfc6132b6..c8010794e3 100755 --- a/deploy/copy-secrets.sh +++ b/deploy/copy-secrets.sh @@ -17,14 +17,14 @@ YELLOW='\033[1;33m' NC='\033[0m' # No Color # List of target files -TARGETS=("cn1" "cn2" "cn3" "agg") +TARGETS=("cn1" "cn2" "cn3" "cn4") # Sample network private keys NETWORK_KEY_CN1="0x11a1e500a548b70d88184a1e042900c0ed6c57f8710bcc35dc8c85fa33d3f580" NETWORK_KEY_CN2="0x21a1e500a548b70d88184a1e042900c0ed6c57f8710bcc35dc8c85fa33d3f580" NETWORK_KEY_CN3="0x31a1e500a548b70d88184a1e042900c0ed6c57f8710bcc35dc8c85fa33d3f580" -NETWORK_KEY_AGG="0x41a1e500a548b70d88184a1e042900c0ed6c57f8710bcc35dc8c85fa33d3f580" -NET_KEYS=($NETWORK_KEY_CN1 $NETWORK_KEY_CN2 $NETWORK_KEY_CN3 $NETWORK_KEY_AGG) +NETWORK_KEY_CN4="0x41a1e500a548b70d88184a1e042900c0ed6c57f8710bcc35dc8c85fa33d3f580" +NET_KEYS=($NETWORK_KEY_CN1 $NETWORK_KEY_CN2 $NETWORK_KEY_CN3 $NETWORK_KEY_CN4) # Check if source file exists if [ ! -f "$SOURCE" ]; then diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 7fc5b3445f..32ed7b0c70 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -9,7 +9,6 @@ services: target: secrets.json env_file: .env environment: - AGGREGATOR: 'false' ADDRESS: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8' QUIC_PORT: 9091 deploy: @@ -28,7 +27,6 @@ services: target: secrets.json env_file: .env environment: - AGGREGATOR: 'false' ADDRESS: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC' QUIC_PORT: 9092 deploy: @@ -47,7 +45,6 @@ services: target: secrets.json env_file: .env environment: - AGGREGATOR: 'false' ADDRESS: '0x90F79bf6EB2c4f870365E785982E1f101E93b906' QUIC_PORT: 9093 deploy: @@ -56,19 +53,18 @@ services: networks: - global-network - aggregator: + cn4: image: { { IMAGE } } depends_on: - cn1 volumes: - - ./agg.yaml:/home/ciphernode/.config/enclave/config.yaml:ro - - agg-data:/home/ciphernode/.local/share/enclave + - ./cn4.yaml:/home/ciphernode/.config/enclave/config.yaml:ro + - cn4-data:/home/ciphernode/.local/share/enclave secrets: - - source: secrets_agg + - source: secrets_cn4 target: secrets.json env_file: .env environment: - AGGREGATOR: 'true' ADDRESS: '0x8626a6940E2eb28930eFb4CeF49B2d1F2C9C1199' QUIC_PORT: 9094 deploy: @@ -84,14 +80,14 @@ secrets: file: cn2.secrets.json secrets_cn3: file: cn3.secrets.json - secrets_agg: - file: agg.secrets.json + secrets_cn4: + file: cn4.secrets.json volumes: cn1-data: cn2-data: cn3-data: - agg-data: + cn4-data: networks: global-network: diff --git a/deploy/inspect.sh b/deploy/inspect.sh index 20959b5a2b..50a09afddd 100755 --- a/deploy/inspect.sh +++ b/deploy/inspect.sh @@ -42,7 +42,7 @@ get_logs_by_version enclave_cn3 echo "" echo "=================================" -echo " AGG " +echo " CN4 " echo "=================================" -get_logs_by_version enclave_aggregator +get_logs_by_version enclave_cn4 diff --git a/deploy/local/nodes.sh b/deploy/local/nodes.sh index 7b4e091fdc..a344192a27 100755 --- a/deploy/local/nodes.sh +++ b/deploy/local/nodes.sh @@ -8,7 +8,6 @@ concurrently \ --prefix-colors "blue,yellow" \ "anvil" \ "cd examples/CRISP && \ - enclave wallet set --name ag --private-key "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" && enclave wallet set --name cn1 --private-key "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" && enclave wallet set --name cn2 --private-key "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" && enclave wallet set --name cn3 --private-key "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6" && diff --git a/deploy/local/start.sh b/deploy/local/start.sh index 20beab7df5..0990164938 100755 --- a/deploy/local/start.sh +++ b/deploy/local/start.sh @@ -100,7 +100,7 @@ concurrently \ --names "ANVIL,NODES" \ --prefix-colors "blue,yellow" \ "anvil" \ - "cd examples/CRISP && enclave wallet set --name ag --private-key '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' && enclave nodes up -v" & + "cd examples/CRISP && enclave nodes up -v" & INFRA_PID=$! diff --git a/deploy/swarm_deployment.md b/deploy/swarm_deployment.md index 62555ed683..866ae5ccd5 100644 --- a/deploy/swarm_deployment.md +++ b/deploy/swarm_deployment.md @@ -106,7 +106,7 @@ To deploy with swarm we need to set up the secrets file for our cluster. ## What it does -- Copies `example.secrets.json` to create `cn1/2/3` and `agg.secrets.json` files +- Copies `example.secrets.json` to create `cn1/2/3/4.secrets.json` files - Skips existing files - Warns with yellow arrows (==>) if any files are identical to the example @@ -134,7 +134,7 @@ This will deploy the following services: ``` ❯ docker service ls ID NAME MODE REPLICAS IMAGE PORTS -tr44go8vevh1 enclave_aggregator replicated 1/1 ghcr.io/gnosisguild/ciphernode:latest +tr44go8vevh1 enclave_cn4 replicated 1/1 ghcr.io/gnosisguild/ciphernode:latest kdqktv85xcuv enclave_cn1 replicated 1/1 ghcr.io/gnosisguild/ciphernode:latest nguul381w6mu enclave_cn2 replicated 1/1 ghcr.io/gnosisguild/ciphernode:latest zgmwmv7cd63j enclave_cn3 replicated 1/1 ghcr.io/gnosisguild/ciphernode:latest diff --git a/docs/pages/ciphernode-operators/running.mdx b/docs/pages/ciphernode-operators/running.mdx index 157ce2720a..6cba1c8ccb 100644 --- a/docs/pages/ciphernode-operators/running.mdx +++ b/docs/pages/ciphernode-operators/running.mdx @@ -21,7 +21,6 @@ DappNode provides a user-friendly interface for running a ciphernode with minima - `RPC_URL` - WebSocket RPC endpoint (e.g., `wss://ethereum-sepolia-rpc.publicnode.com`) - `NETWORK` - Network name (e.g., `sepolia`, `mainnet`) - Contract addresses and deploy blocks - - Node role (`ciphernode` or `aggregator`) - Optional: encryption password, network key, private key 4. Confirm and complete the installation @@ -33,12 +32,11 @@ DappNode provides a user-friendly interface for running a ciphernode with minima | --------------------- | -------------------------------------------- | -------- | | `RPC_URL` | WebSocket RPC endpoint | Yes | | `NETWORK` | Network name (sepolia, mainnet, etc.) | No | -| `NODE_ROLE` | `ciphernode` or `aggregator` | No | | `NODE_ADDRESS` | Your Ethereum address | No | | `QUIC_PORT` | UDP port for P2P networking (default: 37173) | No | | `ENCRYPTION_PASSWORD` | Password to encrypt local data | No | | `NETWORK_PRIVATE_KEY` | libp2p network key (ed25519) | No | -| `PRIVATE_KEY` | Ethereum private key (for aggregator) | No | +| `PRIVATE_KEY` | Ethereum private key for node transactions | No | | `PEERS` | Comma-separated peer multiaddresses | No | --- diff --git a/docs/pages/introduction.mdx b/docs/pages/introduction.mdx index 83c20596a0..a27e59e504 100644 --- a/docs/pages/introduction.mdx +++ b/docs/pages/introduction.mdx @@ -56,8 +56,8 @@ These components enable computation on encrypted inputs, verification of correct distributed control of decryption through the network of ciphernodes. For a dedicated walkthrough of threshold BFV, PV-TBFV, and the circuit phases (C0–C7), see -[Cryptography](/cryptography). To compile and lint the Noir workspace, run `enclave noir`, and wire the -prover from Rust, see [Noir Circuits](/noir-circuits). +[Cryptography](/cryptography). To compile and lint the Noir workspace, run `enclave noir`, and wire +the prover from Rust, see [Noir Circuits](/noir-circuits). ### What You'll Learn diff --git a/docs/pages/project-template.mdx b/docs/pages/project-template.mdx index 42c6d6bd8a..ba18a5fca2 100644 --- a/docs/pages/project-template.mdx +++ b/docs/pages/project-template.mdx @@ -51,8 +51,8 @@ image is ready; skip this by exporting `SKIP_PROGRAM_COMPILE=1`. flags. - `server/.env`: holds the operator wallet key (never commit this file), RPC endpoints, and cron API tokens. -- `enclave.config.yaml`: describes your Ciphernodes and aggregator profile. The default file mirrors - the template you saw earlier in this repo. +- `enclave.config.yaml`: describes your local ciphernode network. The default file mirrors the + template you saw earlier in this repo. ## Integrating the SDK @@ -82,10 +82,10 @@ contracts. ## Customizing Ciphernodes -`enclave.config.yaml` includes multiple node profiles (`cn1`, `cn2`, `cn3`, `ag`). Update the -addresses, peers, and QUIC ports to match your deployment. For production, replace -`autonetkey/autopassword` with explicit secrets and run `enclave nodes up --detach` from a systemd -service or container entrypoint. +`enclave.config.yaml` includes multiple node profiles (`cn1` through `cn5`). Update the addresses, +peers, and QUIC ports to match your deployment. For production, replace `autonetkey/autopassword` +with explicit secrets and run `enclave nodes up --detach` from a systemd service or container +entrypoint. ## Common tweaks diff --git a/examples/CRISP/.env.example b/examples/CRISP/.env.example index 39a8d1ac53..47c8b80c52 100644 --- a/examples/CRISP/.env.example +++ b/examples/CRISP/.env.example @@ -1,7 +1,7 @@ # This is mostly for testing if we want to setup multiple ciphernodes on a single machine for a testnet such as sepolia # These are hardhat's default accounts' private keys -PRIVATE_KEY_AG="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +PRIVATE_KEY_ADMIN="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" PRIVATE_KEY_CN1="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" PRIVATE_KEY_CN2="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" PRIVATE_KEY_CN3="0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6" diff --git a/examples/CRISP/Readme.md b/examples/CRISP/Readme.md index efa8abb953..666b8cdaea 100644 --- a/examples/CRISP/Readme.md +++ b/examples/CRISP/Readme.md @@ -24,7 +24,7 @@ CRISP/ ├── crates/ # Rust libraries used by the server ├── circuits/ # Noir zero-knowledge circuits ├── scripts/ # Development scripts for running, testing, and deployment -├── enclave.config.yaml # Ciphernodes + aggregator config +├── enclave.config.yaml # Local ciphernode network config └── docker-compose.yaml # Optional multi-node deployment ``` diff --git a/examples/CRISP/enclave.config.yaml b/examples/CRISP/enclave.config.yaml index 04dc00d664..221ee71ca3 100644 --- a/examples/CRISP/enclave.config.yaml +++ b/examples/CRISP/enclave.config.yaml @@ -61,11 +61,3 @@ nodes: ctrl_port: 50505 autonetkey: true autopassword: true - ag: - address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' - quic_port: 9206 - ctrl_port: 50506 - autonetkey: true - autopassword: true - role: - type: "aggregator" diff --git a/examples/CRISP/scripts/dev_cipher.sh b/examples/CRISP/scripts/dev_cipher.sh index 7760f6e8b2..9be991388f 100755 --- a/examples/CRISP/scripts/dev_cipher.sh +++ b/examples/CRISP/scripts/dev_cipher.sh @@ -8,14 +8,12 @@ rm -rf ./.enclave/data rm -rf ./.enclave/config rm -rf $READYFILE -PRIVATE_KEY_AG="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" PRIVATE_KEY_CN1="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" PRIVATE_KEY_CN2="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" PRIVATE_KEY_CN3="0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6" PRIVATE_KEY_CN4="0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a" PRIVATE_KEY_CN5="0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba" -enclave wallet set --name ag --private-key "$PRIVATE_KEY_AG" enclave wallet set --name cn1 --private-key "$PRIVATE_KEY_CN1" enclave wallet set --name cn2 --private-key "$PRIVATE_KEY_CN2" enclave wallet set --name cn3 --private-key "$PRIVATE_KEY_CN3" diff --git a/examples/CRISP/scripts/setup_testnet.sh b/examples/CRISP/scripts/setup_testnet.sh index c7578aff97..15312c64bc 100644 --- a/examples/CRISP/scripts/setup_testnet.sh +++ b/examples/CRISP/scripts/setup_testnet.sh @@ -35,7 +35,13 @@ if [ ! -f .env ]; then fi source .env -enclave wallet set --name ag --private-key "$PRIVATE_KEY_AG" +PRIVATE_KEY_ADMIN="${PRIVATE_KEY_ADMIN:-${PRIVATE_KEY_AG:-}}" + +if [ -z "$PRIVATE_KEY_ADMIN" ]; then + echo "Error: PRIVATE_KEY_ADMIN (or legacy PRIVATE_KEY_AG) must be set in .env" + exit 1 +fi + enclave wallet set --name cn1 --private-key "$PRIVATE_KEY_CN1" enclave wallet set --name cn2 --private-key "$PRIVATE_KEY_CN2" enclave wallet set --name cn3 --private-key "$PRIVATE_KEY_CN3" @@ -55,8 +61,8 @@ CN5=$(yq -r '.nodes.cn5.address' ./enclave.config.yaml) echo "Minting tokens" -# The aggregator is supposed to be the contract owner for testing -export PRIVATE_KEY="$PRIVATE_KEY_AG" +# The admin/deployer account performs owner-only setup tasks during testnet bootstrap +export PRIVATE_KEY="$PRIVATE_KEY_ADMIN" pnpm ciphernode:mint:tokens --ciphernode-address "$CN1" --network "sepolia" pnpm ciphernode:mint:tokens --ciphernode-address "$CN2" --network "sepolia" @@ -79,8 +85,8 @@ pnpm ciphernode:add:self --network "sepolia" echo "CIPHERNODES HAVE BEEN ADDED." -# Reset the private key to the aggregator for further operations (owner of contracts) -export PRIVATE_KEY="$PRIVATE_KEY_AG" +# Reset the private key to the admin account for any subsequent contract operations +export PRIVATE_KEY="$PRIVATE_KEY_ADMIN" # wait diff --git a/package.json b/package.json index fbe950e735..cd916302b1 100644 --- a/package.json +++ b/package.json @@ -39,10 +39,13 @@ "rust:build": "cargo build --locked --release", "prerust:build": "pnpm evm:build && pnpm fixtures:build", "committee:new": "cd packages/enclave-contracts && pnpm committee:new", + "committee:get-public-key": "cd packages/enclave-contracts && pnpm committee:get-public-key", + "committee:get-active-aggregator": "cd packages/enclave-contracts && pnpm committee:get-active-aggregator", "committee:publish": "cd packages/enclave-contracts && pnpm hardhat committee:publish", "e3:activate": "cd packages/enclave-contracts && pnpm e3:activate", "e3-program:publishInput": "cd packages/enclave-contracts && pnpm hardhat e3-program:publishInput", "e3:publishCiphertext": "cd packages/enclave-contracts && pnpm hardhat e3:publishCiphertext", + "e3:get-plaintext": "cd packages/enclave-contracts && pnpm e3:get-plaintext", "evm:install": "cd packages/enclave-contracts && pnpm install", "evm:node": "cd packages/enclave-contracts && pnpm hardhat node", "evm:build": "cd packages/enclave-contracts && pnpm compile", diff --git a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json index c7b335ec71..19883b9329 100644 --- a/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json +++ b/packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json @@ -2559,11 +2559,11 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b615a72806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106102d9575f3560e01c80639117173c11610182578063cb649617116100e0578063ea71aa571161008f578063ea71aa57146108c0578063f0691cba146108d3578063f2fde38b146108e6578063f81b8ef6146108f9578063fad8e1111461090c578063fbdb32371461091f578063fd2f3d0114610947575f5ffd5b8063cb64961714610829578063cbd1687214610832578063cf0f34c414610845578063cfbdc98d14610858578063d8afed3e14610887578063da16fb2f1461089a578063e59e4695146108ad575f5ffd5b8063ac3d2f421161013c578063ac3d2f4214610780578063b27392d5146107a8578063b68fd1be146107bb578063bb2d1b82146107ce578063bff232c1146107e1578063c1ab0f1f146107f4578063c4ccafa214610807575f5ffd5b80639117173c146105ac57806392312386146105bf578063929a8faf146105d257806399c6679d146105f35780639c8570c81461061b578063a87f4ab91461062e575f5ffd5b80634fc772641161023a5780637cfa9d74116101e95780637cfa9d741461053157806381476ec21461054457806385814243146105575780638da5cb5b1461056a5780638dcdd86b146105725780638e5ce3ad1461058457806390173a4114610597575f5ffd5b80634fc77264146104b357806364226409146104c6578063647846a5146104e75780636db5c8fd146104fa578063715018a61461050357806377868ae41461050b5780637c8c3b4d1461051e575f5ffd5b80631ba72945116102965780631ba72945146103a657806336c5d38a146103b95780634017daf0146103e8578063406ed35c146104155780634147a36014610435578063459d9294146104625780634e92ec63146104a0575f5ffd5b806301d12f1c146102dd57806302a3a9c9146102f25780630ef81b2f1461030557806310bc62811461034357806311bd61d91461036b57806315cce22414610393575b5f5ffd5b6102f06102eb36600461472f565b61095a565b005b6102f06103003660046147df565b610ba1565b61032d6103133660046147fa565b5f908152600960205260409020546001600160a01b031690565b60405161033a919061481e565b60405180910390f35b61032d6103513660046147fa565b60096020525f90815260409020546001600160a01b031681565b61037e610379366004614840565b610c4d565b60405163ffffffff909116815260200161033a565b6102f06103a13660046147df565b610c89565b6102f06103b4366004614868565b610d2e565b6103db6103c73660046147fa565b5f908152600f602052604090205460ff1690565b60405161033a91906148aa565b6103fb6103f63660046147fa565b610d42565b60405161033a9e9d9c9b9a999897969594939291906148f6565b6104286104233660046147fa565b610f6c565b60405161033a9190614ae6565b6104546104433660046147fa565b600c6020525f908152604090205481565b60405190815260200161033a565b610490610470366004614af8565b8051602081830181018051600b8252928201919093012091525460ff1681565b604051901515815260200161033a565b6102f06104ae3660046147fa565b611269565b6102f06104c13660046147df565b6112f8565b6104d96104d4366004614b31565b61138b565b60405161033a929190614b68565b60045461032d906001600160a01b031681565b61045460055481565b6102f0611bd3565b6102f0610519366004614b80565b611be6565b6102f061052c366004614bb1565b611c7e565b6102f061053f3660046147fa565b611d07565b6102f0610552366004614bdf565b611e06565b60015461032d906001600160a01b031681565b61032d611efa565b5f5461032d906001600160a01b031681565b60035461032d906001600160a01b031681565b61059f611f28565b60405161033a9190614bff565b6102f06105ba3660046147fa565b611f6e565b61059f6105cd3660046147fa565b6120dc565b6105e56105e03660046147fa565b612135565b60405161033a929190614c20565b61032d6106013660046147fa565b5f908152601060205260409020546001600160a01b031690565b610490610629366004614c73565b61215c565b610773604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161033a9190614cea565b61032d61078e3660046147fa565b5f908152600a60205260409020546001600160a01b031690565b6104906107b6366004614df7565b6123f4565b6102f06107c9366004614b80565b612624565b6102f06107dc366004614e9c565b6126bb565b6102f06107ef3660046147df565b612777565b6102f0610802366004614bdf565b61281e565b6104906108153660046147df565b60076020525f908152604090205460ff1681565b61045460065481565b6102f0610840366004614bb1565b6128db565b6102f06108533660046147fa565b61298e565b61087a6108663660046147fa565b5f908152600d602052604090205460ff1690565b60405161033a9190614ed4565b6102f0610895366004614ee2565b6129cb565b6104546108a8366004614b31565b612c58565b6102f06108bb3660046147df565b6131dc565b6102f06108ce366004614efc565b613276565b60025461032d906001600160a01b031681565b6102f06108f43660046147df565b613523565b6103db6109073660046147fa565b61355d565b6102f061091a3660046147df565b6136f7565b61032d61092d3660046147fa565b600a6020525f90815260409020546001600160a01b031681565b6102f06109553660046147df565b61378f565b5f61096361381e565b805490915060ff600160401b82041615906001600160401b03165f811580156109895750825b90505f826001600160401b031660011480156109a45750303b155b9050811580156109b2575080155b156109d05760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156109fa57845460ff60401b1916600160401b1785555b610a0333613848565b610a0c8861298e565b610a158c6136f7565b610a1e8b6131dc565b610a278a610ba1565b610a3089610c89565b610a3987613859565b610a4286612624565b604080516101e08101825261c3508082526161a86020808401829052611388948401859052601460608501819052620249f0608086018190526207a12060a087018190526107d060c088018190525f60e089018190526103e86101008a015261012089018190526109c46101408a018190526101608a018b90526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b031916690138827101388000007d60a31b179055805467ffffffffffffffff19169055610b2b611efa565b6001600160a01b03168d6001600160a01b031614610b4c57610b4c8d613523565b8315610b9257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050505050565b610ba9613916565b6001600160a01b038116610c045760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f208160028110610c66575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610c91613916565b6001600160a01b03811615801590610cb757506004546001600160a01b03828116911614155b8190610cd75760405163eddf07f560e01b8152600401610bfb919061481e565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610d2390839061481e565b60405180910390a150565b610d36613916565b610d3f81613859565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff90951695939492936001600160a01b039092169291610d8d90614f33565b80601f0160208091040260200160405190810160405280929190818152602001828054610db990614f33565b8015610e045780601f10610ddb57610100808354040283529160200191610e04565b820191905f5260205f20905b815481529060010190602001808311610de757829003601f168201915b505050505090806008018054610e1990614f33565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4590614f33565b8015610e905780601f10610e6757610100808354040283529160200191610e90565b820191905f5260205f20905b815481529060010190602001808311610e7357829003601f168201915b5050506009840154600a850154600b860154600c870154600d8801805497986001600160a01b03958616989490951696509194509291610ecf90614f33565b80601f0160208091040260200160405190810160405280929190818152602001828054610efb90614f33565b8015610f465780601f10610f1d57610100808354040283529160200191610f46565b820191905f5260205f20905b815481529060010190602001808311610f2957829003601f168201915b505050600e90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610f74614414565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610fb157610fb1614882565b6003811115610fc257610fc2614882565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610fe75750505091835250506005820154602082015260068201546001600160a01b0316604082015260078201805460609092019161103490614f33565b80601f016020809104026020016040519081016040528092919081815260200182805461106090614f33565b80156110ab5780601f10611082576101008083540402835291602001916110ab565b820191905f5260205f20905b81548152906001019060200180831161108e57829003601f168201915b505050505081526020016008820180546110c490614f33565b80601f01602080910402602001604051908101604052809291908181526020018280546110f090614f33565b801561113b5780601f106111125761010080835404028352916020019161113b565b820191905f5260205f20905b81548152906001019060200180831161111e57829003601f168201915b505050918352505060098201546001600160a01b039081166020830152600a830154166040820152600b8201546060820152600c8201546080820152600d8201805460a09092019161118c90614f33565b80601f01602080910402602001604051908101604052809291908181526020018280546111b890614f33565b80156112035780601f106111da57610100808354040283529160200191611203565b820191905f5260205f20905b8154815290600101906020018083116111e657829003601f168201915b5050509183525050600e91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a08201519192508391166112635760405163cd6f4a4f60e01b8152600401610bfb91815260200190565b50919050565b611271613916565b5f8181526009602052604090205481906001600160a01b03166112aa576040516381c4951960e01b8152600401610bfb91815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610d239083815260200190565b611300613916565b6001600160a01b0381165f90815260076020526040902054819060ff1661133b576040516321ac7c5f60e01b8152600401610bfb919061481e565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610d2390839061481e565b5f611394614414565b5f6012816113a56020870187614f65565b60038111156113b6576113b6614882565b60038111156113c7576113c7614882565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116113ee579050505050505090505f8160016002811061144557611445614f7e565b602002015163ffffffff1611845f0160208101906114639190614f65565b906114825760405163286c068d60e11b8152600401610bfb9190614f92565b506020840135428110156114ac57604051630b99e87960e01b8152600401610bfb91815260200190565b50604084013560208501358110156114da5760405163174b5a0760e21b8152600401610bfb91815260200190565b506017546016545f91906114f2426040890135614fb4565b6114fc9190614fc7565b6115069190614fc7565b90506005548110819061152f576040516313b783af60e21b8152600401610bfb91815260200190565b5060075f61154360808801606089016147df565b6001600160a01b0316815260208101919091526040015f205460ff1661156f60808701606088016147df565b9061158e5760405163295a6a6f60e11b8152600401610bfb919061481e565b505f61159986612c58565b60068054965090915085905f6115ae83614fda565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff1916600117905560109094528290208054339416939093179092556016549192506116899190890135614fc7565b5f878152600e60209081526040909120600101919091558186526116af90880188614f65565b856020019060038111156116c5576116c5614882565b908160038111156116d8576116d8614882565b905250436040808701919091528051808201825290602089019060029083908390808284375f92019190915250505060608087019190915261172090608089019089016147df565b6001600160a01b031660a086015261173b6080880188614ff2565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060c08087019190915261178390880188614ff2565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e0808701919091526117d0906101008901908901615041565b15156101c08601525f610140860181905261016086018190526040805160208101909152908152610180860152336101a08601819052600454611820916001600160a01b03909116903085613948565b5f6118316080890160608a016147df565b6001600160a01b031663fefd9a8b888461184e60808d018d614ff2565b61185b60a08f018f614ff2565b8f8060c0019061186b9190614ff2565b6040518963ffffffff1660e01b815260040161188e989796959493929190615084565b6020604051808303815f875af11580156118aa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118ce91906150da565b5f818152600960205260409020549091506001600160a01b0316818161190a576040516381c4951960e01b8152600401610bfb91815260200190565b505f828152600a60205260409020546001600160a01b03168281611944576040516381c4951960e01b8152600401610bfb91815260200190565b50608088018390526001600160a01b038083166101008a015281166101208901525f8981526008602090815260409091208951815590890151600180830180548c94939260ff1991909116908360038111156119a2576119a2614882565b0217905550604082015181600201556060820151816003019060026119c8929190614491565b506080820151600582015560a08201516006820180546001600160a01b0319166001600160a01b0390921691909117905560c08201516007820190611a0d9082615150565b5060e08201516008820190611a229082615150565b506101008201516009820180546001600160a01b039283166001600160a01b031991821617909155610120840151600a84018054919093169116179055610140820151600b820155610160820151600c820155610180820151600d820190611a8a9082615150565b506101a0820151600e90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091555f5460405163291a691b60e01b815291169063291a691b90611af0908c9088908c90600401615205565b6020604051808303815f875af1158015611b0c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b30919061524a565b611b4d57604051630d8dbe2560e01b815260040160405180910390fd5b611b5d60808b0160608c016147df565b6001600160a01b03167f5904e83d57e704fba4d92c5617f6b50ae993cd42d78babfcb239d0a6775f30708a8a604051611b97929190614b68565b60405180910390a2885f516020615a465f395f51905f525f6001604051611bbf929190615265565b60405180910390a250505050505050915091565b611bdb613916565b611be45f6139b5565b565b611bee613916565b80515f5b81811015611c4257600b838281518110611c0e57611c0e614f7e565b6020026020010151604051611c239190615280565b908152604051908190036020019020805460ff19169055600101611bf2565b507fd1b46e030b48add7bc03225cc5a6f403970976b36983f99ec31d535d627fc7db82604051611c729190615296565b60405180910390a15050565b611c86613916565b6001600160a01b03811615801590611cb757505f828152600a60205260409020546001600160a01b03828116911614155b8290611cd9576040516381c4951960e01b8152600401610bfb91815260200190565b505f918252600a602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b5f546001600160a01b03163314611d315760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff166001816006811115611d5657611d56614882565b14611d7b57816001826040516337e1404160e01b8152600401610bfb939291906152f9565b5f828152600d60205260409020805460ff19166002179055601554611da09042614fc7565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f516020615a465f395f51905f5260016002604051611dfa929190615265565b60405180910390a25050565b5f546001600160a01b03163314611e305760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff166002816006811115611e6057611e60614882565b14611e8557836002826040516337e1404160e01b8152600401610bfb939291906152f9565b5f848152600d6020526040808220805460ff19166003179055600b84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f516020615a465f395f51905f5260026003604051611eec929190615265565b60405180910390a250505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b611f4960405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff166006816006811115611f9357611f93614882565b148290611fb657604051637cb2d48360e11b8152600401610bfb91815260200190565b505f828152600c60205260409020548281611fe7576040516345ba89d560e11b8152600401610bfb91815260200190565b505f838152600c6020526040812081905561200184613a25565b5f858152601160205260409020546002549192506001600160a01b039081169161202e9183911685613b19565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b6979061206490889087908790879060040161535d565b5f604051808303815f87803b15801561207b575f5ffd5b505af115801561208d573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a073178484516040516120cd929190918252602082015260400190565b60405180910390a25050505050565b6120fd60405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff166121528482613b44565b9250925050915091565b5f5f61216787610f6c565b5f888152600d602052604090205490915060ff16600381600681111561218f5761218f614882565b14886003839091926121b7576040516337e1404160e01b8152600401610bfb939291906152f9565b5050505f888152600e602090815260409182902082516060810184528154815260018201549281018390526002909101549281019290925289904281101561221b576040516308f3034360e31b815260048101929092526024820152604401610bfb565b50506060830151602001518990428111156122525760405163017e35e560e71b815260048101929092526024820152604401610bfb565b505061016083015189901561227d57604051637eb9cea960e11b8152600401610bfb91815260200190565b505f888860405161228f929190615394565b60408051918290039091205f8c815260086020908152838220600c01839055600d905291909120805460ff191660041790556017549091506122d19042614fc7565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf9061231e908d9085908c908c906004016153a3565b6020604051808303815f875af115801561233a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061235e919061524a565b945088888661238257604051632f9f8ab960e01b8152600401610bfb9291906153c2565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a6040516123b69291906153c2565b60405180910390a2895f516020615a465f395f51905f52600360046040516123df929190615265565b60405180910390a25050505095945050505050565b5f5f6123ff89610f6c565b5f8a8152600d602052604090205490915060ff16600481600681111561242757612427614882565b148a60048390919261244f576040516337e1404160e01b8152600401610bfb939291906152f9565b5050505f8a8152600e602090815260409182902082516060810184528154815260018201549281019290925260020154918101829052908b90428110156124b2576040516308f3034360e31b815260048101929092526024820152604401610bfb565b50505f8b8152600860205260409020600d016124cf8a8c836153d5565b505f8b8152600d6020526040902080546005919060ff191660018302179055508261010001516001600160a01b0316635bf48e3a8b8b604051612513929190615394565b6040519081900381206001600160e01b031960e084901b168252612541918c908c908c908c90600401615489565b602060405180830381865afa15801561255c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612580919061524a565b93508989856125a457604051632f9f8ab960e01b8152600401610bfb9291906153c2565b50506125af8b613ccc565b8a7f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b8b8b8b8b6040516125e594939291906154c1565b60405180910390a28a5f516020615a465f395f51905f526004600560405161260e929190615265565b60405180910390a2505050979650505050505050565b61262c613916565b80515f5b8181101561268b576001600b84838151811061264e5761264e614f7e565b60200260200101516040516126639190615280565b908152604051908190036020019020805491151560ff19909216919091179055600101612630565b507f027b83cad653f54850fef6faa8c705f73a53e7f8de50a3a33ac1e0e3a5c0be8182604051611c729190615296565b5f546001600160a01b03163314806126dd57506003546001600160a01b031633145b6126fa57604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156127105750600d60ff821611155b6127555760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610bfb565b612773828260ff16600d81111561276e5761276e614882565b614137565b5050565b61277f613916565b6001600160a01b0381166127d55760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610bfb565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b03163314612849576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b158015612893575f5ffd5b505af11580156128a5573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee54282604051611dfa91815260200190565b6128e3613916565b6001600160a01b0381161580159061291457505f828152600960205260409020546001600160a01b03828116911614155b8290612936576040516381c4951960e01b8152600401610bfb91815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb699101611c72565b612996613916565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610d23565b6129d3613916565b6127106129e86101208301610100840161550c565b61ffff161115612a006101208301610100840161550c565b90612a25576040516301027fc160e21b815261ffff9091166004820152602401610bfb565b50612710612a3b6101408301610120840161550c565b61ffff161115612a536101408301610120840161550c565b90612a78576040516301027fc160e21b815261ffff9091166004820152602401610bfb565b50612710612a8e6101608301610140840161550c565b61ffff161115612aa66101608301610140840161550c565b90612acb57604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612710612ae16101808301610160840161550c565b61ffff161115612af96101808301610160840161550c565b90612b1e57604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612710612b346101a08301610180840161550c565b61ffff161115612b4c6101a08301610180840161550c565b90612b7157604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612b846101408201610120830161550c565b61ffff161580612bad57505f612ba1610100830160e084016147df565b6001600160a01b031614155b612bca5760405163015f92ff60e51b815260040160405180910390fd5b612bdc6101e082016101c08301615543565b63ffffffff16612bf46101c083016101a08401615543565b63ffffffff161015612c19576040516392f55c6560e01b815260040160405180910390fd5b806018612c268282615582565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610d239190615740565b5f80601281612c6a6020860186614f65565b6003811115612c7b57612c7b614882565b6003811115612c8c57612c8c614882565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612cb3579050505050505090505f81600160028110612d0a57612d0a614f7e565b602002015163ffffffff1611835f016020810190612d289190614f65565b90612d475760405163286c068d60e11b8152600401610bfb9190614f92565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c086015292831693919092169115612e72576101a081015163ffffffff16846001602002015163ffffffff161015865f016020810190612e519190614f65565b90612e705760405163010b971d60e31b8152600401610bfb9190614f92565b505b6101c081015163ffffffff1615612ec1576101c081015184519063ffffffff9081169082161015612ebf57604051630a4b6b6360e11b815263ffffffff9091166004820152602401610bfb565b505b60408601356020870135811015612eee5760405163174b5a0760e21b8152600401610bfb91815260200190565b506101808101516017545f9161271091612f0c9161ffff169061584a565b612f169190615861565b61271061ffff1683610160015161ffff16601560010154612f37919061584a565b612f419190615861565b61271061ffff1684610140015161ffff1660155f0154612f61919061584a565b612f6b9190615861565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa158015612fb7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fdb91906150da565b612fe59190614fc7565b612fef9190614fb4565b612ff99190614fc7565b6130039190614fc7565b61300d9190614fc7565b90505f61301b600186614fb4565b61302690600261584a565b61303190600261584a565b61303c906006614fc7565b90505f85845f015161304e919061584a565b905081868560200151613061919061584a565b61306b919061584a565b6130759082614fc7565b905060018611156130bd57600261308d600188614fb4565b613097908861584a565b85604001516130a6919061584a565b6130b09190615861565b6130ba9082614fc7565b90505b81868560c001516130ce919061584a565b6130d8919061584a565b6130e29082614fc7565b9050828685606001516130f5919061584a565b6130ff919061584a565b6131099082614fc7565b905084846080015161311b919061584a565b6131259082614fc7565b9050600185111561316d57600261313d600187614fb4565b613147908761584a565b8560400151613156919061584a565b6131609190615861565b61316a9082614fc7565b90505b60a084015161317c9082614fc7565b610100850151909150612710906131979061ffff1682614fc7565b6131a1908361584a565b6131ab9190615861565b975087806131cf57604051638c4fcd9360e01b8152600401610bfb91815260200190565b5050505050505050919050565b6131e4613916565b6001600160a01b0381161580159061320a57506001546001600160a01b03828116911614155b819061322a576040516320252f0b60e01b8152600401610bfb919061481e565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610d2390839061481e565b61327e613916565b61328b6020820182615543565b63ffffffff166132a16040830160208401615543565b63ffffffff16101580156132c657505f6132be6020830183615543565b63ffffffff16115b6132e357604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c08301521561341c576101a081015163ffffffff166133cc6040840160208501615543565b63ffffffff1610156133e46040840160208501615543565b826101a00151909161341957604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610bfb565b50505b6101c081015163ffffffff1615613493576101c081015163ffffffff166134466020840184615543565b63ffffffff16101561345b6020840184615543565b826101c0015190916134905760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610bfb565b50505b8160125f8560038111156134a9576134a9614882565b60038111156134ba576134ba614882565b815260208101919091526040015f206134d49160026144cf565b508260038111156134e7576134e7614882565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa836040516135169190615880565b60405180910390a2505050565b61352b613916565b6001600160a01b038116613554575f604051631e4fbdf760e01b8152600401610bfb919061481e565b610d3f816139b5565b5f818152600d602052604081205460ff168181600681111561358157613581614882565b036135a657826001826040516337e1404160e01b8152600401610bfb939291906152f9565b60058160068111156135ba576135ba614882565b036135db5760405163462c7bed60e01b815260048101849052602401610bfb565b60068160068111156135ef576135ef614882565b0361361057604051633de16e3560e11b815260048101849052602401610bfb565b5f61361b8483613b44565b935090508061364057604051639f65d93560e01b815260048101859052602401610bfb565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d81111561368957613689614882565b0217905550835f516020615a465f395f51905f528360066040516136ae929190615265565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb83856040516136e89291906158c0565b60405180910390a25050919050565b6136ff613916565b6001600160a01b0381161580159061372457505f546001600160a01b03828116911614155b8190613744576040516375ac4eb760e11b8152600401610bfb919061481e565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610d2390839061481e565b6001600160a01b0381165f90815260076020526040902054819060ff16156137cb5760405163b29d459560e01b8152600401610bfb919061481e565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610d2390839061481e565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b613850614292565b610d3f816142b7565b80356138785760405163055f269d60e01b815260040160405180910390fd5b5f81602001351161389c5760405163055f269d60e01b815260040160405180910390fd5b5f8160400135116138c05760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610d23565b3361391f611efa565b6001600160a01b031614611be4573360405163118cdaa760e01b8152600401610bfb919061481e565b6040516001600160a01b0384811660248301528381166044830152606482018390526139af9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506142bf565b50505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f818152600f602052604090205460609060ff16600181600d811115613a4d57613a4d614882565b1480613a6a5750600281600d811115613a6857613a68614882565b145b15613aa2575f5b604051908082528060200260200182016040528015613a9a578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa925050508015613b0857506040513d5f823e601f3d908101601f19168201604052613b0591908101906158db565b60015b613b12575f613a71565b9392505050565b613b3f83846001600160a01b031663a9059cbb858560405160240161397d92919061596a565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa158015613bbe573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613be291906150da565b90506001856006811115613bf857613bf8614882565b148015613c0457508042115b15613c1757600180935093505050613cc5565b6002856006811115613c2b57613c2b614882565b148015613c385750815142115b15613c4c5760016003935093505050613cc5565b6003856006811115613c6057613c60614882565b148015613c705750816020015142115b15613c845760016006935093505050613cc5565b6004856006811115613c9857613c98614882565b148015613ca85750816040015142115b15613cbc576001600a935093505050613cc5565b5f5f9350935050505b9250929050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613d11573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613d3891908101906158db565b80515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613dd6576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613da290889088908690600401615983565b5f604051808303815f87803b158015613db9575f5ffd5b505af1158015613dcb573d5f5f3e3d5ffd5b505050505050505050565b825f03613e77575f858152601060205260409020546001600160a01b03168015613e0e57613e0e6001600160a01b0383168285613b19565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613e4290899089908790600401615983565b5f604051808303815f87803b158015613e59575f5ffd5b505af1158015613e6b573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613eb757506001600160a01b03811615155b15613ef357612710613ecd61ffff84168761584a565b613ed79190615861565b92508215613ef357613ef36001600160a01b0385168285613b19565b5f613efe8487614fb4565b90505f876001600160401b03811115613f1957613f196145d1565b604051908082528060200260200182016040528015613f42578160200160208202803683370190505b5090505f613f508984615861565b90505f805b8a811015613f8f5782848281518110613f7057613f70614f7e565b6020908102919091010152613f858383614fc7565b9150600101613f55565b505f613f9b8286614fb4565b90508015613fd8578084613fb060018e614fb4565b81518110613fc057613fc0614f7e565b60200260200101818151613fd49190614fc7565b9052505b600154613ff2906001600160a01b038b8116911687614322565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b8152600401614036939291906159e3565b5f604051808303815f87803b15801561404d575f5ffd5b505af115801561405f573d5f5f3e3d5ffd5b505060015461407d92506001600160a01b038c81169250165f614322565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d866040516140af929190615a18565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b81526004016140fb93929190615983565b5f604051808303815f87803b158015614112575f5ffd5b505af1158015614124573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f828152600d602052604081205460ff169081600681111561415b5761415b614882565b0361418057826001826040516337e1404160e01b8152600401610bfb939291906152f9565b600581600681111561419457614194614882565b036141b55760405163462c7bed60e01b815260048101849052602401610bfb565b60068160068111156141c9576141c9614882565b036141ea57604051633de16e3560e11b815260048101849052602401610bfb565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d81111561423357614233614882565b0217905550825f516020615a465f395f51905f52826006604051614258929190615265565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb82846040516135169291906158c0565b61429a6143b2565b611be457604051631afcd79f60e31b815260040160405180910390fd5b61352b614292565b5f5f60205f8451602086015f885af1806142de576040513d5f823e3d81fd5b50505f513d915081156142f5578060011415614302565b6001600160a01b0384163b155b156139af5783604051635274afe760e01b8152600401610bfb919061481e565b5f836001600160a01b031663095ea7b3848460405160240161434592919061596a565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050905061437e84826143cb565b6139af576143a884856001600160a01b031663095ea7b3865f60405160240161397d92919061596a565b6139af84826142bf565b5f6143bb61381e565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f51905082801561440a575081156143fc578060011461440a565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f815260200161443c61456b565b81525f602082018190526040820181905260608083018190526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b82600281019282156144bf579160200282015b828111156144bf5782518255916020019190600101906144a4565b506144cb929150614589565b5090565b6001830191839082156144bf579160200282015f5b8382111561452e57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026144e4565b801561455e5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261452e565b50506144cb929150614589565b60405180604001604052806002906020820280368337509192915050565b5b808211156144cb575f815560010161458a565b6001600160a01b0381168114610d3f575f5ffd5b80356145bc8161459d565b919050565b5f60608284031215611263575f5ffd5b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561460d5761460d6145d1565b604052919050565b5f6001600160401b0382111561462d5761462d6145d1565b5060051b60200190565b5f82601f830112614646575f5ffd5b81356001600160401b0381111561465f5761465f6145d1565b614672601f8201601f19166020016145e5565b818152846020838601011115614686575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f82601f8301126146b1575f5ffd5b81356146c46146bf82614615565b6145e5565b8082825260208201915060208360051b8601019250858311156146e5575f5ffd5b602085015b838110156147255780356001600160401b03811115614707575f5ffd5b614716886020838a0101614637565b845250602092830192016146ea565b5095945050505050565b5f5f5f5f5f5f5f5f610140898b031215614747575f5ffd5b88356147528161459d565b975060208901356147628161459d565b965060408901356147728161459d565b955060608901356147828161459d565b945060808901356147928161459d565b935060a089013592506147a88a60c08b016145c1565b91506101208901356001600160401b038111156147c3575f5ffd5b6147cf8b828c016146a2565b9150509295985092959890939650565b5f602082840312156147ef575f5ffd5b8135613b128161459d565b5f6020828403121561480a575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b8035600481106145bc575f5ffd5b5f5f60408385031215614851575f5ffd5b61485a83614832565b946020939093013593505050565b5f60608284031215614878575f5ffd5b613b1283836145c1565b634e487b7160e01b5f52602160045260245ffd5b600e81106148a6576148a6614882565b9052565b602081016138428284614896565b600481106148a6576148a6614882565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e8152614906602082018f6148b8565b8c60408201528b606082015261491f608082018c614811565b6101c060a08201525f6149366101c083018c6148c8565b82810360c0840152614948818c6148c8565b905061495760e084018b614811565b61496561010084018a614811565b876101208401528661014084015282810361016084015261498681876148c8565b915050614997610180830185614811565b8215156101a08301529f9e505050505050505050505050505050565b805f5b60028110156139af5781518452602093840193909101906001016149b6565b805182525f60208201516149ec60208501826148b8565b50604082015160408401526060820151614a0960608501826149b3565b50608082015160a084015260a0820151614a2660c0850182614811565b5060c082015161020060e0850152614a426102008501826148c8565b905060e0830151848203610100860152614a5c82826148c8565b915050610100830151614a73610120860182614811565b50610120830151614a88610140860182614811565b506101408301516101608501526101608301516101808501526101808301518482036101a0860152614aba82826148c8565b9150506101a0830151614ad16101c0860182614811565b506101c08301518015156101e0860152613a9a565b602081525f613b1260208301846149d5565b5f60208284031215614b08575f5ffd5b81356001600160401b03811115614b1d575f5ffd5b614b2984828501614637565b949350505050565b5f60208284031215614b41575f5ffd5b81356001600160401b03811115614b56575f5ffd5b82016101008185031215613b12575f5ffd5b828152604060208201525f614b2960408301846149d5565b5f60208284031215614b90575f5ffd5b81356001600160401b03811115614ba5575f5ffd5b614b29848285016146a2565b5f5f60408385031215614bc2575f5ffd5b823591506020830135614bd48161459d565b809150509250929050565b5f5f60408385031215614bf0575f5ffd5b50508035926020909101359150565b81518152602080830151908201526040808301519082015260608101613842565b821515815260408101613b126020830184614896565b5f5f83601f840112614c46575f5ffd5b5081356001600160401b03811115614c5c575f5ffd5b602083019150836020828501011115613cc5575f5ffd5b5f5f5f5f5f60608688031215614c87575f5ffd5b8535945060208601356001600160401b03811115614ca3575f5ffd5b614caf88828901614c36565b90955093505060408601356001600160401b03811115614ccd575f5ffd5b614cd988828901614c36565b969995985093965092949392505050565b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614d4460e0840182614811565b50610100830151614d5c61010084018261ffff169052565b50610120830151614d7461012084018261ffff169052565b50610140830151614d8c61014084018261ffff169052565b50610160830151614da461016084018261ffff169052565b50610180830151614dbc61018084018261ffff169052565b506101a0830151614dd66101a084018263ffffffff169052565b506101c0830151614df06101c084018263ffffffff169052565b5092915050565b5f5f5f5f5f5f5f6080888a031215614e0d575f5ffd5b8735965060208801356001600160401b03811115614e29575f5ffd5b614e358a828b01614c36565b90975095505060408801356001600160401b03811115614e53575f5ffd5b614e5f8a828b01614c36565b90955093505060608801356001600160401b03811115614e7d575f5ffd5b614e898a828b01614c36565b989b979a50959850939692959293505050565b5f5f60408385031215614ead575f5ffd5b82359150602083013560ff81168114614bd4575f5ffd5b600781106148a6576148a6614882565b602081016138428284614ec4565b5f6101e0828403128015614ef4575f5ffd5b509092915050565b5f5f60608385031215614f0d575f5ffd5b614f1683614832565b915083606084011115614f27575f5ffd5b50926020919091019150565b600181811c90821680614f4757607f821691505b60208210810361126357634e487b7160e01b5f52602260045260245ffd5b5f60208284031215614f75575f5ffd5b613b1282614832565b634e487b7160e01b5f52603260045260245ffd5b6020810161384282846148b8565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561384257613842614fa0565b8082018082111561384257613842614fa0565b5f60018201614feb57614feb614fa0565b5060010190565b5f5f8335601e19843603018112615007575f5ffd5b8301803591506001600160401b03821115615020575f5ffd5b602001915036819003821315613cc5575f5ffd5b8015158114610d3f575f5ffd5b5f60208284031215615051575f5ffd5b8135613b1281615034565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b88815287602082015260a060408201525f6150a360a08301888a61505c565b82810360608401526150b681878961505c565b905082810360808401526150cb81858761505c565b9b9a5050505050505050505050565b5f602082840312156150ea575f5ffd5b5051919050565b601f821115613b3f57805f5260205f20601f840160051c810160208510156151165750805b601f840160051c820191505b81811015615135575f8155600101615122565b5050505050565b5f19600383901b1c191660019190911b1790565b81516001600160401b03811115615169576151696145d1565b61517d816151778454614f33565b846150f1565b6020601f8211600181146151aa575f83156151985750848201515b6151a2848261513c565b855550615135565b5f84815260208120601f198516915b828110156151d957878501518255602094850194600190920191016151b9565b50848210156151f657868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b600281101561523f57815163ffffffff1683526020928301929091019060010161521a565b505050949350505050565b5f6020828403121561525a575f5ffd5b8151613b1281615034565b604081016152738285614ec4565b613b126020830184614ec4565b5f82518060208501845e5f920191825250919050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b828110156152ed57603f198786030184526152d88583516148c8565b945060209384019391909101906001016152bc565b50929695505050505050565b8381526060810161530d6020830185614ec4565b614b296040830184614ec4565b5f8151808452602084019350602083015f5b828110156153535781516001600160a01b031686526020958601959091019060010161532c565b5093949350505050565b848152836020820152608060408201525f61537b608083018561531a565b905060018060a01b038316606083015295945050505050565b818382375f9101908152919050565b848152836020820152606060408201525f61440a60608301848661505c565b602081525f614b2960208301848661505c565b6001600160401b038311156153ec576153ec6145d1565b615400836153fa8354614f33565b836150f1565b5f601f84116001811461542c575f851561541a5750838201355b615424868261513c565b845550615135565b5f83815260208120601f198716915b8281101561545b578685013582556020948501946001909201910161543b565b5086821015615477575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b858152606060208201525f6154a260608301868861505c565b82810360408401526154b581858761505c565b98975050505050505050565b604081525f6154d460408301868861505c565b82810360208401526154e781858761505c565b979650505050505050565b61ffff81168114610d3f575f5ffd5b80356145bc816154f2565b5f6020828403121561551c575f5ffd5b8135613b12816154f2565b63ffffffff81168114610d3f575f5ffd5b80356145bc81615527565b5f60208284031215615553575f5ffd5b8135613b1281615527565b5f81356138428161459d565b5f8135613842816154f2565b5f813561384281615527565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c08201356006820155600781016155f26155d560e0850161555e565b82546001600160a01b0319166001600160a01b0391909116178255565b615622615602610100850161556a565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b615652615632610120850161556a565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b615682615662610140850161556a565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b6156b2615692610160850161556a565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b6156e26156c2610180850161556a565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b506008810161570e6156f76101a08501615576565b825463ffffffff191663ffffffff91909116178255565b613b3f61571e6101c08501615576565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e0810161579160e084016145b1565b61579e60e0840182614811565b506157ac6101008401615501565b61ffff166101008301526157c36101208401615501565b61ffff166101208301526157da6101408401615501565b61ffff166101408301526157f16101608401615501565b61ffff166101608301526158086101808401615501565b61ffff1661018083015261581f6101a08401615538565b63ffffffff166101a08301526158386101c08401615538565b63ffffffff81166101c0840152614df0565b808202811582820484141761384257613842614fa0565b5f8261587b57634e487b7160e01b5f52601260045260245ffd5b500490565b6040810181835f5b60028110156158b757813561589c81615527565b63ffffffff1683526020928301929190910190600101615888565b50505092915050565b604081016158ce8285614ec4565b613b126020830184614896565b5f602082840312156158eb575f5ffd5b81516001600160401b03811115615900575f5ffd5b8201601f81018413615910575f5ffd5b805161591e6146bf82614615565b8082825260208201915060208360051b85010192508683111561593f575f5ffd5b6020840193505b8284101561440a5783516159598161459d565b825260209384019390910190615946565b6001600160a01b03929092168252602082015260400190565b838152606060208201525f61599b606083018561531a565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b828110156153535781518652602095860195909101906001016159c5565b6001600160a01b03841681526060602082018190525f90615a069083018561531a565b828103604084015261440a81856159b3565b604081525f615a2a604083018561531a565b8281036020840152615a3c81856159b3565b9594505050505056fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106102d9575f3560e01c80639117173c11610182578063cb649617116100e0578063ea71aa571161008f578063ea71aa57146108c0578063f0691cba146108d3578063f2fde38b146108e6578063f81b8ef6146108f9578063fad8e1111461090c578063fbdb32371461091f578063fd2f3d0114610947575f5ffd5b8063cb64961714610829578063cbd1687214610832578063cf0f34c414610845578063cfbdc98d14610858578063d8afed3e14610887578063da16fb2f1461089a578063e59e4695146108ad575f5ffd5b8063ac3d2f421161013c578063ac3d2f4214610780578063b27392d5146107a8578063b68fd1be146107bb578063bb2d1b82146107ce578063bff232c1146107e1578063c1ab0f1f146107f4578063c4ccafa214610807575f5ffd5b80639117173c146105ac57806392312386146105bf578063929a8faf146105d257806399c6679d146105f35780639c8570c81461061b578063a87f4ab91461062e575f5ffd5b80634fc772641161023a5780637cfa9d74116101e95780637cfa9d741461053157806381476ec21461054457806385814243146105575780638da5cb5b1461056a5780638dcdd86b146105725780638e5ce3ad1461058457806390173a4114610597575f5ffd5b80634fc77264146104b357806364226409146104c6578063647846a5146104e75780636db5c8fd146104fa578063715018a61461050357806377868ae41461050b5780637c8c3b4d1461051e575f5ffd5b80631ba72945116102965780631ba72945146103a657806336c5d38a146103b95780634017daf0146103e8578063406ed35c146104155780634147a36014610435578063459d9294146104625780634e92ec63146104a0575f5ffd5b806301d12f1c146102dd57806302a3a9c9146102f25780630ef81b2f1461030557806310bc62811461034357806311bd61d91461036b57806315cce22414610393575b5f5ffd5b6102f06102eb36600461472f565b61095a565b005b6102f06103003660046147df565b610ba1565b61032d6103133660046147fa565b5f908152600960205260409020546001600160a01b031690565b60405161033a919061481e565b60405180910390f35b61032d6103513660046147fa565b60096020525f90815260409020546001600160a01b031681565b61037e610379366004614840565b610c4d565b60405163ffffffff909116815260200161033a565b6102f06103a13660046147df565b610c89565b6102f06103b4366004614868565b610d2e565b6103db6103c73660046147fa565b5f908152600f602052604090205460ff1690565b60405161033a91906148aa565b6103fb6103f63660046147fa565b610d42565b60405161033a9e9d9c9b9a999897969594939291906148f6565b6104286104233660046147fa565b610f6c565b60405161033a9190614ae6565b6104546104433660046147fa565b600c6020525f908152604090205481565b60405190815260200161033a565b610490610470366004614af8565b8051602081830181018051600b8252928201919093012091525460ff1681565b604051901515815260200161033a565b6102f06104ae3660046147fa565b611269565b6102f06104c13660046147df565b6112f8565b6104d96104d4366004614b31565b61138b565b60405161033a929190614b68565b60045461032d906001600160a01b031681565b61045460055481565b6102f0611bd3565b6102f0610519366004614b80565b611be6565b6102f061052c366004614bb1565b611c7e565b6102f061053f3660046147fa565b611d07565b6102f0610552366004614bdf565b611e06565b60015461032d906001600160a01b031681565b61032d611efa565b5f5461032d906001600160a01b031681565b60035461032d906001600160a01b031681565b61059f611f28565b60405161033a9190614bff565b6102f06105ba3660046147fa565b611f6e565b61059f6105cd3660046147fa565b6120dc565b6105e56105e03660046147fa565b612135565b60405161033a929190614c20565b61032d6106013660046147fa565b5f908152601060205260409020546001600160a01b031690565b610490610629366004614c73565b61215c565b610773604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161033a9190614cea565b61032d61078e3660046147fa565b5f908152600a60205260409020546001600160a01b031690565b6104906107b6366004614df7565b6123f4565b6102f06107c9366004614b80565b612624565b6102f06107dc366004614e9c565b6126bb565b6102f06107ef3660046147df565b612777565b6102f0610802366004614bdf565b61281e565b6104906108153660046147df565b60076020525f908152604090205460ff1681565b61045460065481565b6102f0610840366004614bb1565b6128db565b6102f06108533660046147fa565b61298e565b61087a6108663660046147fa565b5f908152600d602052604090205460ff1690565b60405161033a9190614ed4565b6102f0610895366004614ee2565b6129cb565b6104546108a8366004614b31565b612c58565b6102f06108bb3660046147df565b6131dc565b6102f06108ce366004614efc565b613276565b60025461032d906001600160a01b031681565b6102f06108f43660046147df565b613523565b6103db6109073660046147fa565b61355d565b6102f061091a3660046147df565b6136f7565b61032d61092d3660046147fa565b600a6020525f90815260409020546001600160a01b031681565b6102f06109553660046147df565b61378f565b5f61096361381e565b805490915060ff600160401b82041615906001600160401b03165f811580156109895750825b90505f826001600160401b031660011480156109a45750303b155b9050811580156109b2575080155b156109d05760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156109fa57845460ff60401b1916600160401b1785555b610a0333613848565b610a0c8861298e565b610a158c6136f7565b610a1e8b6131dc565b610a278a610ba1565b610a3089610c89565b610a3987613859565b610a4286612624565b604080516101e08101825261c3508082526161a86020808401829052611388948401859052601460608501819052620249f0608086018190526207a12060a087018190526107d060c088018190525f60e089018190526103e86101008a015261012089018190526109c46101408a018190526101608a018b90526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b031916690138827101388000007d60a31b179055805467ffffffffffffffff19169055610b2b611efa565b6001600160a01b03168d6001600160a01b031614610b4c57610b4c8d613523565b8315610b9257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050505050565b610ba9613916565b6001600160a01b038116610c045760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f208160028110610c66575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610c91613916565b6001600160a01b03811615801590610cb757506004546001600160a01b03828116911614155b8190610cd75760405163eddf07f560e01b8152600401610bfb919061481e565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610d2390839061481e565b60405180910390a150565b610d36613916565b610d3f81613859565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff90951695939492936001600160a01b039092169291610d8d90614f33565b80601f0160208091040260200160405190810160405280929190818152602001828054610db990614f33565b8015610e045780601f10610ddb57610100808354040283529160200191610e04565b820191905f5260205f20905b815481529060010190602001808311610de757829003601f168201915b505050505090806008018054610e1990614f33565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4590614f33565b8015610e905780601f10610e6757610100808354040283529160200191610e90565b820191905f5260205f20905b815481529060010190602001808311610e7357829003601f168201915b5050506009840154600a850154600b860154600c870154600d8801805497986001600160a01b03958616989490951696509194509291610ecf90614f33565b80601f0160208091040260200160405190810160405280929190818152602001828054610efb90614f33565b8015610f465780601f10610f1d57610100808354040283529160200191610f46565b820191905f5260205f20905b815481529060010190602001808311610f2957829003601f168201915b505050600e90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610f74614414565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610fb157610fb1614882565b6003811115610fc257610fc2614882565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610fe75750505091835250506005820154602082015260068201546001600160a01b0316604082015260078201805460609092019161103490614f33565b80601f016020809104026020016040519081016040528092919081815260200182805461106090614f33565b80156110ab5780601f10611082576101008083540402835291602001916110ab565b820191905f5260205f20905b81548152906001019060200180831161108e57829003601f168201915b505050505081526020016008820180546110c490614f33565b80601f01602080910402602001604051908101604052809291908181526020018280546110f090614f33565b801561113b5780601f106111125761010080835404028352916020019161113b565b820191905f5260205f20905b81548152906001019060200180831161111e57829003601f168201915b505050918352505060098201546001600160a01b039081166020830152600a830154166040820152600b8201546060820152600c8201546080820152600d8201805460a09092019161118c90614f33565b80601f01602080910402602001604051908101604052809291908181526020018280546111b890614f33565b80156112035780601f106111da57610100808354040283529160200191611203565b820191905f5260205f20905b8154815290600101906020018083116111e657829003601f168201915b5050509183525050600e91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a08201519192508391166112635760405163cd6f4a4f60e01b8152600401610bfb91815260200190565b50919050565b611271613916565b5f8181526009602052604090205481906001600160a01b03166112aa576040516381c4951960e01b8152600401610bfb91815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610d239083815260200190565b611300613916565b6001600160a01b0381165f90815260076020526040902054819060ff1661133b576040516321ac7c5f60e01b8152600401610bfb919061481e565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610d2390839061481e565b5f611394614414565b5f6012816113a56020870187614f65565b60038111156113b6576113b6614882565b60038111156113c7576113c7614882565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116113ee579050505050505090505f8160016002811061144557611445614f7e565b602002015163ffffffff1611845f0160208101906114639190614f65565b906114825760405163286c068d60e11b8152600401610bfb9190614f92565b506020840135428110156114ac57604051630b99e87960e01b8152600401610bfb91815260200190565b50604084013560208501358110156114da5760405163174b5a0760e21b8152600401610bfb91815260200190565b506017546016545f91906114f2426040890135614fb4565b6114fc9190614fc7565b6115069190614fc7565b90506005548110819061152f576040516313b783af60e21b8152600401610bfb91815260200190565b5060075f61154360808801606089016147df565b6001600160a01b0316815260208101919091526040015f205460ff1661156f60808701606088016147df565b9061158e5760405163295a6a6f60e11b8152600401610bfb919061481e565b505f61159986612c58565b60068054965090915085905f6115ae83614fda565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff1916600117905560109094528290208054339416939093179092556016549192506116899190890135614fc7565b5f878152600e60209081526040909120600101919091558186526116af90880188614f65565b856020019060038111156116c5576116c5614882565b908160038111156116d8576116d8614882565b905250436040808701919091528051808201825290602089019060029083908390808284375f92019190915250505060608087019190915261172090608089019089016147df565b6001600160a01b031660a086015261173b6080880188614ff2565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060c08087019190915261178390880188614ff2565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e0808701919091526117d0906101008901908901615041565b15156101c08601525f610140860181905261016086018190526040805160208101909152908152610180860152336101a08601819052600454611820916001600160a01b03909116903085613948565b5f6118316080890160608a016147df565b6001600160a01b031663fefd9a8b888461184e60808d018d614ff2565b61185b60a08f018f614ff2565b8f8060c0019061186b9190614ff2565b6040518963ffffffff1660e01b815260040161188e989796959493929190615084565b6020604051808303815f875af11580156118aa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118ce91906150da565b5f818152600960205260409020549091506001600160a01b0316818161190a576040516381c4951960e01b8152600401610bfb91815260200190565b505f828152600a60205260409020546001600160a01b03168281611944576040516381c4951960e01b8152600401610bfb91815260200190565b50608088018390526001600160a01b038083166101008a015281166101208901525f8981526008602090815260409091208951815590890151600180830180548c94939260ff1991909116908360038111156119a2576119a2614882565b0217905550604082015181600201556060820151816003019060026119c8929190614491565b506080820151600582015560a08201516006820180546001600160a01b0319166001600160a01b0390921691909117905560c08201516007820190611a0d9082615150565b5060e08201516008820190611a229082615150565b506101008201516009820180546001600160a01b039283166001600160a01b031991821617909155610120840151600a84018054919093169116179055610140820151600b820155610160820151600c820155610180820151600d820190611a8a9082615150565b506101a0820151600e90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091555f5460405163291a691b60e01b815291169063291a691b90611af0908c9088908c90600401615205565b6020604051808303815f875af1158015611b0c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b30919061524a565b611b4d57604051630d8dbe2560e01b815260040160405180910390fd5b611b5d60808b0160608c016147df565b6001600160a01b03167f5904e83d57e704fba4d92c5617f6b50ae993cd42d78babfcb239d0a6775f30708a8a604051611b97929190614b68565b60405180910390a2885f516020615a465f395f51905f525f6001604051611bbf929190615265565b60405180910390a250505050505050915091565b611bdb613916565b611be45f6139b5565b565b611bee613916565b80515f5b81811015611c4257600b838281518110611c0e57611c0e614f7e565b6020026020010151604051611c239190615280565b908152604051908190036020019020805460ff19169055600101611bf2565b507fd1b46e030b48add7bc03225cc5a6f403970976b36983f99ec31d535d627fc7db82604051611c729190615296565b60405180910390a15050565b611c86613916565b6001600160a01b03811615801590611cb757505f828152600a60205260409020546001600160a01b03828116911614155b8290611cd9576040516381c4951960e01b8152600401610bfb91815260200190565b505f918252600a602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b5f546001600160a01b03163314611d315760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff166001816006811115611d5657611d56614882565b14611d7b57816001826040516337e1404160e01b8152600401610bfb939291906152f9565b5f828152600d60205260409020805460ff19166002179055601554611da09042614fc7565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f516020615a465f395f51905f5260016002604051611dfa929190615265565b60405180910390a25050565b5f546001600160a01b03163314611e305760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff166002816006811115611e6057611e60614882565b14611e8557836002826040516337e1404160e01b8152600401610bfb939291906152f9565b5f848152600d6020526040808220805460ff19166003179055600b84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f516020615a465f395f51905f5260026003604051611eec929190615265565b60405180910390a250505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b611f4960405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff166006816006811115611f9357611f93614882565b148290611fb657604051637cb2d48360e11b8152600401610bfb91815260200190565b505f828152600c60205260409020548281611fe7576040516345ba89d560e11b8152600401610bfb91815260200190565b505f838152600c6020526040812081905561200184613a25565b5f858152601160205260409020546002549192506001600160a01b039081169161202e9183911685613b19565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b6979061206490889087908790879060040161535d565b5f604051808303815f87803b15801561207b575f5ffd5b505af115801561208d573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a073178484516040516120cd929190918252602082015260400190565b60405180910390a25050505050565b6120fd60405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff166121528482613b44565b9250925050915091565b5f5f61216787610f6c565b5f888152600d602052604090205490915060ff16600381600681111561218f5761218f614882565b14886003839091926121b7576040516337e1404160e01b8152600401610bfb939291906152f9565b5050505f888152600e602090815260409182902082516060810184528154815260018201549281018390526002909101549281019290925289904281101561221b576040516308f3034360e31b815260048101929092526024820152604401610bfb565b50506060830151602001518990428111156122525760405163017e35e560e71b815260048101929092526024820152604401610bfb565b505061016083015189901561227d57604051637eb9cea960e11b8152600401610bfb91815260200190565b505f888860405161228f929190615394565b60408051918290039091205f8c815260086020908152838220600c01839055600d905291909120805460ff191660041790556017549091506122d19042614fc7565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf9061231e908d9085908c908c906004016153a3565b6020604051808303815f875af115801561233a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061235e919061524a565b945088888661238257604051632f9f8ab960e01b8152600401610bfb9291906153c2565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a6040516123b69291906153c2565b60405180910390a2895f516020615a465f395f51905f52600360046040516123df929190615265565b60405180910390a25050505095945050505050565b5f5f6123ff89610f6c565b5f8a8152600d602052604090205490915060ff16600481600681111561242757612427614882565b148a60048390919261244f576040516337e1404160e01b8152600401610bfb939291906152f9565b5050505f8a8152600e602090815260409182902082516060810184528154815260018201549281019290925260020154918101829052908b90428110156124b2576040516308f3034360e31b815260048101929092526024820152604401610bfb565b50505f8b8152600860205260409020600d016124cf8a8c836153d5565b505f8b8152600d6020526040902080546005919060ff191660018302179055508261010001516001600160a01b0316635bf48e3a8b8b604051612513929190615394565b6040519081900381206001600160e01b031960e084901b168252612541918c908c908c908c90600401615489565b602060405180830381865afa15801561255c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612580919061524a565b93508989856125a457604051632f9f8ab960e01b8152600401610bfb9291906153c2565b50506125af8b613ccc565b8a7f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b8b8b8b8b6040516125e594939291906154c1565b60405180910390a28a5f516020615a465f395f51905f526004600560405161260e929190615265565b60405180910390a2505050979650505050505050565b61262c613916565b80515f5b8181101561268b576001600b84838151811061264e5761264e614f7e565b60200260200101516040516126639190615280565b908152604051908190036020019020805491151560ff19909216919091179055600101612630565b507f027b83cad653f54850fef6faa8c705f73a53e7f8de50a3a33ac1e0e3a5c0be8182604051611c729190615296565b5f546001600160a01b03163314806126dd57506003546001600160a01b031633145b6126fa57604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156127105750600d60ff821611155b6127555760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610bfb565b612773828260ff16600d81111561276e5761276e614882565b614137565b5050565b61277f613916565b6001600160a01b0381166127d55760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610bfb565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b03163314612849576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b158015612893575f5ffd5b505af11580156128a5573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee54282604051611dfa91815260200190565b6128e3613916565b6001600160a01b0381161580159061291457505f828152600960205260409020546001600160a01b03828116911614155b8290612936576040516381c4951960e01b8152600401610bfb91815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb699101611c72565b612996613916565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610d23565b6129d3613916565b6127106129e86101208301610100840161550c565b61ffff161115612a006101208301610100840161550c565b90612a25576040516301027fc160e21b815261ffff9091166004820152602401610bfb565b50612710612a3b6101408301610120840161550c565b61ffff161115612a536101408301610120840161550c565b90612a78576040516301027fc160e21b815261ffff9091166004820152602401610bfb565b50612710612a8e6101608301610140840161550c565b61ffff161115612aa66101608301610140840161550c565b90612acb57604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612710612ae16101808301610160840161550c565b61ffff161115612af96101808301610160840161550c565b90612b1e57604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612710612b346101a08301610180840161550c565b61ffff161115612b4c6101a08301610180840161550c565b90612b7157604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612b846101408201610120830161550c565b61ffff161580612bad57505f612ba1610100830160e084016147df565b6001600160a01b031614155b612bca5760405163015f92ff60e51b815260040160405180910390fd5b612bdc6101e082016101c08301615543565b63ffffffff16612bf46101c083016101a08401615543565b63ffffffff161015612c19576040516392f55c6560e01b815260040160405180910390fd5b806018612c268282615582565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610d239190615740565b5f80601281612c6a6020860186614f65565b6003811115612c7b57612c7b614882565b6003811115612c8c57612c8c614882565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612cb3579050505050505090505f81600160028110612d0a57612d0a614f7e565b602002015163ffffffff1611835f016020810190612d289190614f65565b90612d475760405163286c068d60e11b8152600401610bfb9190614f92565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c086015292831693919092169115612e72576101a081015163ffffffff16846001602002015163ffffffff161015865f016020810190612e519190614f65565b90612e705760405163010b971d60e31b8152600401610bfb9190614f92565b505b6101c081015163ffffffff1615612ec1576101c081015184519063ffffffff9081169082161015612ebf57604051630a4b6b6360e11b815263ffffffff9091166004820152602401610bfb565b505b60408601356020870135811015612eee5760405163174b5a0760e21b8152600401610bfb91815260200190565b506101808101516017545f9161271091612f0c9161ffff169061584a565b612f169190615861565b61271061ffff1683610160015161ffff16601560010154612f37919061584a565b612f419190615861565b61271061ffff1684610140015161ffff1660155f0154612f61919061584a565b612f6b9190615861565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa158015612fb7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fdb91906150da565b612fe59190614fc7565b612fef9190614fb4565b612ff99190614fc7565b6130039190614fc7565b61300d9190614fc7565b90505f61301b600186614fb4565b61302690600261584a565b61303190600261584a565b61303c906006614fc7565b90505f85845f015161304e919061584a565b905081868560200151613061919061584a565b61306b919061584a565b6130759082614fc7565b905060018611156130bd57600261308d600188614fb4565b613097908861584a565b85604001516130a6919061584a565b6130b09190615861565b6130ba9082614fc7565b90505b81868560c001516130ce919061584a565b6130d8919061584a565b6130e29082614fc7565b9050828685606001516130f5919061584a565b6130ff919061584a565b6131099082614fc7565b905084846080015161311b919061584a565b6131259082614fc7565b9050600185111561316d57600261313d600187614fb4565b613147908761584a565b8560400151613156919061584a565b6131609190615861565b61316a9082614fc7565b90505b60a084015161317c9082614fc7565b610100850151909150612710906131979061ffff1682614fc7565b6131a1908361584a565b6131ab9190615861565b975087806131cf57604051638c4fcd9360e01b8152600401610bfb91815260200190565b5050505050505050919050565b6131e4613916565b6001600160a01b0381161580159061320a57506001546001600160a01b03828116911614155b819061322a576040516320252f0b60e01b8152600401610bfb919061481e565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610d2390839061481e565b61327e613916565b61328b6020820182615543565b63ffffffff166132a16040830160208401615543565b63ffffffff16101580156132c657505f6132be6020830183615543565b63ffffffff16115b6132e357604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c08301521561341c576101a081015163ffffffff166133cc6040840160208501615543565b63ffffffff1610156133e46040840160208501615543565b826101a00151909161341957604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610bfb565b50505b6101c081015163ffffffff1615613493576101c081015163ffffffff166134466020840184615543565b63ffffffff16101561345b6020840184615543565b826101c0015190916134905760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610bfb565b50505b8160125f8560038111156134a9576134a9614882565b60038111156134ba576134ba614882565b815260208101919091526040015f206134d49160026144cf565b508260038111156134e7576134e7614882565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa836040516135169190615880565b60405180910390a2505050565b61352b613916565b6001600160a01b038116613554575f604051631e4fbdf760e01b8152600401610bfb919061481e565b610d3f816139b5565b5f818152600d602052604081205460ff168181600681111561358157613581614882565b036135a657826001826040516337e1404160e01b8152600401610bfb939291906152f9565b60058160068111156135ba576135ba614882565b036135db5760405163462c7bed60e01b815260048101849052602401610bfb565b60068160068111156135ef576135ef614882565b0361361057604051633de16e3560e11b815260048101849052602401610bfb565b5f61361b8483613b44565b935090508061364057604051639f65d93560e01b815260048101859052602401610bfb565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d81111561368957613689614882565b0217905550835f516020615a465f395f51905f528360066040516136ae929190615265565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb83856040516136e89291906158c0565b60405180910390a25050919050565b6136ff613916565b6001600160a01b0381161580159061372457505f546001600160a01b03828116911614155b8190613744576040516375ac4eb760e11b8152600401610bfb919061481e565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610d2390839061481e565b6001600160a01b0381165f90815260076020526040902054819060ff16156137cb5760405163b29d459560e01b8152600401610bfb919061481e565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610d2390839061481e565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b613850614292565b610d3f816142b7565b80356138785760405163055f269d60e01b815260040160405180910390fd5b5f81602001351161389c5760405163055f269d60e01b815260040160405180910390fd5b5f8160400135116138c05760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610d23565b3361391f611efa565b6001600160a01b031614611be4573360405163118cdaa760e01b8152600401610bfb919061481e565b6040516001600160a01b0384811660248301528381166044830152606482018390526139af9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506142bf565b50505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f818152600f602052604090205460609060ff16600181600d811115613a4d57613a4d614882565b1480613a6a5750600281600d811115613a6857613a68614882565b145b15613aa2575f5b604051908082528060200260200182016040528015613a9a578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa925050508015613b0857506040513d5f823e601f3d908101601f19168201604052613b0591908101906158db565b60015b613b12575f613a71565b9392505050565b613b3f83846001600160a01b031663a9059cbb858560405160240161397d92919061596a565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa158015613bbe573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613be291906150da565b90506001856006811115613bf857613bf8614882565b148015613c0457508042115b15613c1757600180935093505050613cc5565b6002856006811115613c2b57613c2b614882565b148015613c385750815142115b15613c4c5760016003935093505050613cc5565b6003856006811115613c6057613c60614882565b148015613c705750816020015142115b15613c845760016006935093505050613cc5565b6004856006811115613c9857613c98614882565b148015613ca85750816040015142115b15613cbc576001600a935093505050613cc5565b5f5f9350935050505b9250929050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613d11573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613d3891908101906158db565b80515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613dd6576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613da290889088908690600401615983565b5f604051808303815f87803b158015613db9575f5ffd5b505af1158015613dcb573d5f5f3e3d5ffd5b505050505050505050565b825f03613e77575f858152601060205260409020546001600160a01b03168015613e0e57613e0e6001600160a01b0383168285613b19565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613e4290899089908790600401615983565b5f604051808303815f87803b158015613e59575f5ffd5b505af1158015613e6b573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613eb757506001600160a01b03811615155b15613ef357612710613ecd61ffff84168761584a565b613ed79190615861565b92508215613ef357613ef36001600160a01b0385168285613b19565b5f613efe8487614fb4565b90505f876001600160401b03811115613f1957613f196145d1565b604051908082528060200260200182016040528015613f42578160200160208202803683370190505b5090505f613f508984615861565b90505f805b8a811015613f8f5782848281518110613f7057613f70614f7e565b6020908102919091010152613f858383614fc7565b9150600101613f55565b505f613f9b8286614fb4565b90508015613fd8578084613fb060018e614fb4565b81518110613fc057613fc0614f7e565b60200260200101818151613fd49190614fc7565b9052505b600154613ff2906001600160a01b038b8116911687614322565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b8152600401614036939291906159e3565b5f604051808303815f87803b15801561404d575f5ffd5b505af115801561405f573d5f5f3e3d5ffd5b505060015461407d92506001600160a01b038c81169250165f614322565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d866040516140af929190615a18565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b81526004016140fb93929190615983565b5f604051808303815f87803b158015614112575f5ffd5b505af1158015614124573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f828152600d602052604081205460ff169081600681111561415b5761415b614882565b0361418057826001826040516337e1404160e01b8152600401610bfb939291906152f9565b600581600681111561419457614194614882565b036141b55760405163462c7bed60e01b815260048101849052602401610bfb565b60068160068111156141c9576141c9614882565b036141ea57604051633de16e3560e11b815260048101849052602401610bfb565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d81111561423357614233614882565b0217905550825f516020615a465f395f51905f52826006604051614258929190615265565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb82846040516135169291906158c0565b61429a6143b2565b611be457604051631afcd79f60e31b815260040160405180910390fd5b61352b614292565b5f5f60205f8451602086015f885af1806142de576040513d5f823e3d81fd5b50505f513d915081156142f5578060011415614302565b6001600160a01b0384163b155b156139af5783604051635274afe760e01b8152600401610bfb919061481e565b5f836001600160a01b031663095ea7b3848460405160240161434592919061596a565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050905061437e84826143cb565b6139af576143a884856001600160a01b031663095ea7b3865f60405160240161397d92919061596a565b6139af84826142bf565b5f6143bb61381e565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f51905082801561440a575081156143fc578060011461440a565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f815260200161443c61456b565b81525f602082018190526040820181905260608083018190526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b82600281019282156144bf579160200282015b828111156144bf5782518255916020019190600101906144a4565b506144cb929150614589565b5090565b6001830191839082156144bf579160200282015f5b8382111561452e57833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026144e4565b801561455e5782816101000a81549063ffffffff021916905560040160208160030104928301926001030261452e565b50506144cb929150614589565b60405180604001604052806002906020820280368337509192915050565b5b808211156144cb575f815560010161458a565b6001600160a01b0381168114610d3f575f5ffd5b80356145bc8161459d565b919050565b5f60608284031215611263575f5ffd5b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561460d5761460d6145d1565b604052919050565b5f6001600160401b0382111561462d5761462d6145d1565b5060051b60200190565b5f82601f830112614646575f5ffd5b81356001600160401b0381111561465f5761465f6145d1565b614672601f8201601f19166020016145e5565b818152846020838601011115614686575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f82601f8301126146b1575f5ffd5b81356146c46146bf82614615565b6145e5565b8082825260208201915060208360051b8601019250858311156146e5575f5ffd5b602085015b838110156147255780356001600160401b03811115614707575f5ffd5b614716886020838a0101614637565b845250602092830192016146ea565b5095945050505050565b5f5f5f5f5f5f5f5f610140898b031215614747575f5ffd5b88356147528161459d565b975060208901356147628161459d565b965060408901356147728161459d565b955060608901356147828161459d565b945060808901356147928161459d565b935060a089013592506147a88a60c08b016145c1565b91506101208901356001600160401b038111156147c3575f5ffd5b6147cf8b828c016146a2565b9150509295985092959890939650565b5f602082840312156147ef575f5ffd5b8135613b128161459d565b5f6020828403121561480a575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b8035600481106145bc575f5ffd5b5f5f60408385031215614851575f5ffd5b61485a83614832565b946020939093013593505050565b5f60608284031215614878575f5ffd5b613b1283836145c1565b634e487b7160e01b5f52602160045260245ffd5b600e81106148a6576148a6614882565b9052565b602081016138428284614896565b600481106148a6576148a6614882565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e8152614906602082018f6148b8565b8c60408201528b606082015261491f608082018c614811565b6101c060a08201525f6149366101c083018c6148c8565b82810360c0840152614948818c6148c8565b905061495760e084018b614811565b61496561010084018a614811565b876101208401528661014084015282810361016084015261498681876148c8565b915050614997610180830185614811565b8215156101a08301529f9e505050505050505050505050505050565b805f5b60028110156139af5781518452602093840193909101906001016149b6565b805182525f60208201516149ec60208501826148b8565b50604082015160408401526060820151614a0960608501826149b3565b50608082015160a084015260a0820151614a2660c0850182614811565b5060c082015161020060e0850152614a426102008501826148c8565b905060e0830151848203610100860152614a5c82826148c8565b915050610100830151614a73610120860182614811565b50610120830151614a88610140860182614811565b506101408301516101608501526101608301516101808501526101808301518482036101a0860152614aba82826148c8565b9150506101a0830151614ad16101c0860182614811565b506101c08301518015156101e0860152613a9a565b602081525f613b1260208301846149d5565b5f60208284031215614b08575f5ffd5b81356001600160401b03811115614b1d575f5ffd5b614b2984828501614637565b949350505050565b5f60208284031215614b41575f5ffd5b81356001600160401b03811115614b56575f5ffd5b82016101008185031215613b12575f5ffd5b828152604060208201525f614b2960408301846149d5565b5f60208284031215614b90575f5ffd5b81356001600160401b03811115614ba5575f5ffd5b614b29848285016146a2565b5f5f60408385031215614bc2575f5ffd5b823591506020830135614bd48161459d565b809150509250929050565b5f5f60408385031215614bf0575f5ffd5b50508035926020909101359150565b81518152602080830151908201526040808301519082015260608101613842565b821515815260408101613b126020830184614896565b5f5f83601f840112614c46575f5ffd5b5081356001600160401b03811115614c5c575f5ffd5b602083019150836020828501011115613cc5575f5ffd5b5f5f5f5f5f60608688031215614c87575f5ffd5b8535945060208601356001600160401b03811115614ca3575f5ffd5b614caf88828901614c36565b90955093505060408601356001600160401b03811115614ccd575f5ffd5b614cd988828901614c36565b969995985093965092949392505050565b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614d4460e0840182614811565b50610100830151614d5c61010084018261ffff169052565b50610120830151614d7461012084018261ffff169052565b50610140830151614d8c61014084018261ffff169052565b50610160830151614da461016084018261ffff169052565b50610180830151614dbc61018084018261ffff169052565b506101a0830151614dd66101a084018263ffffffff169052565b506101c0830151614df06101c084018263ffffffff169052565b5092915050565b5f5f5f5f5f5f5f6080888a031215614e0d575f5ffd5b8735965060208801356001600160401b03811115614e29575f5ffd5b614e358a828b01614c36565b90975095505060408801356001600160401b03811115614e53575f5ffd5b614e5f8a828b01614c36565b90955093505060608801356001600160401b03811115614e7d575f5ffd5b614e898a828b01614c36565b989b979a50959850939692959293505050565b5f5f60408385031215614ead575f5ffd5b82359150602083013560ff81168114614bd4575f5ffd5b600781106148a6576148a6614882565b602081016138428284614ec4565b5f6101e0828403128015614ef4575f5ffd5b509092915050565b5f5f60608385031215614f0d575f5ffd5b614f1683614832565b915083606084011115614f27575f5ffd5b50926020919091019150565b600181811c90821680614f4757607f821691505b60208210810361126357634e487b7160e01b5f52602260045260245ffd5b5f60208284031215614f75575f5ffd5b613b1282614832565b634e487b7160e01b5f52603260045260245ffd5b6020810161384282846148b8565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561384257613842614fa0565b8082018082111561384257613842614fa0565b5f60018201614feb57614feb614fa0565b5060010190565b5f5f8335601e19843603018112615007575f5ffd5b8301803591506001600160401b03821115615020575f5ffd5b602001915036819003821315613cc5575f5ffd5b8015158114610d3f575f5ffd5b5f60208284031215615051575f5ffd5b8135613b1281615034565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b88815287602082015260a060408201525f6150a360a08301888a61505c565b82810360608401526150b681878961505c565b905082810360808401526150cb81858761505c565b9b9a5050505050505050505050565b5f602082840312156150ea575f5ffd5b5051919050565b601f821115613b3f57805f5260205f20601f840160051c810160208510156151165750805b601f840160051c820191505b81811015615135575f8155600101615122565b5050505050565b5f19600383901b1c191660019190911b1790565b81516001600160401b03811115615169576151696145d1565b61517d816151778454614f33565b846150f1565b6020601f8211600181146151aa575f83156151985750848201515b6151a2848261513c565b855550615135565b5f84815260208120601f198516915b828110156151d957878501518255602094850194600190920191016151b9565b50848210156151f657868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b600281101561523f57815163ffffffff1683526020928301929091019060010161521a565b505050949350505050565b5f6020828403121561525a575f5ffd5b8151613b1281615034565b604081016152738285614ec4565b613b126020830184614ec4565b5f82518060208501845e5f920191825250919050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b828110156152ed57603f198786030184526152d88583516148c8565b945060209384019391909101906001016152bc565b50929695505050505050565b8381526060810161530d6020830185614ec4565b614b296040830184614ec4565b5f8151808452602084019350602083015f5b828110156153535781516001600160a01b031686526020958601959091019060010161532c565b5093949350505050565b848152836020820152608060408201525f61537b608083018561531a565b905060018060a01b038316606083015295945050505050565b818382375f9101908152919050565b848152836020820152606060408201525f61440a60608301848661505c565b602081525f614b2960208301848661505c565b6001600160401b038311156153ec576153ec6145d1565b615400836153fa8354614f33565b836150f1565b5f601f84116001811461542c575f851561541a5750838201355b615424868261513c565b845550615135565b5f83815260208120601f198716915b8281101561545b578685013582556020948501946001909201910161543b565b5086821015615477575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b858152606060208201525f6154a260608301868861505c565b82810360408401526154b581858761505c565b98975050505050505050565b604081525f6154d460408301868861505c565b82810360208401526154e781858761505c565b979650505050505050565b61ffff81168114610d3f575f5ffd5b80356145bc816154f2565b5f6020828403121561551c575f5ffd5b8135613b12816154f2565b63ffffffff81168114610d3f575f5ffd5b80356145bc81615527565b5f60208284031215615553575f5ffd5b8135613b1281615527565b5f81356138428161459d565b5f8135613842816154f2565b5f813561384281615527565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c08201356006820155600781016155f26155d560e0850161555e565b82546001600160a01b0319166001600160a01b0391909116178255565b615622615602610100850161556a565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b615652615632610120850161556a565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b615682615662610140850161556a565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b6156b2615692610160850161556a565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b6156e26156c2610180850161556a565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b506008810161570e6156f76101a08501615576565b825463ffffffff191663ffffffff91909116178255565b613b3f61571e6101c08501615576565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e0810161579160e084016145b1565b61579e60e0840182614811565b506157ac6101008401615501565b61ffff166101008301526157c36101208401615501565b61ffff166101208301526157da6101408401615501565b61ffff166101408301526157f16101608401615501565b61ffff166101608301526158086101808401615501565b61ffff1661018083015261581f6101a08401615538565b63ffffffff166101a08301526158386101c08401615538565b63ffffffff81166101c0840152614df0565b808202811582820484141761384257613842614fa0565b5f8261587b57634e487b7160e01b5f52601260045260245ffd5b500490565b6040810181835f5b60028110156158b757813561589c81615527565b63ffffffff1683526020928301929190910190600101615888565b50505092915050565b604081016158ce8285614ec4565b613b126020830184614896565b5f602082840312156158eb575f5ffd5b81516001600160401b03811115615900575f5ffd5b8201601f81018413615910575f5ffd5b805161591e6146bf82614615565b8082825260208201915060208360051b85010192508683111561593f575f5ffd5b6020840193505b8284101561440a5783516159598161459d565b825260209384019390910190615946565b6001600160a01b03929092168252602082015260400190565b838152606060208201525f61599b606083018561531a565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b828110156153535781518652602095860195909101906001016159c5565b6001600160a01b03841681526060602082018190525f90615a069083018561531a565b828103604084015261440a81856159b3565b604081525f615a2a604083018561531a565b8281036020840152615a3c81856159b3565b9594505050505056fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", + "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b615b04806100d65f395ff3fe608060405234801561000f575f5ffd5b50600436106102d9575f3560e01c80639117173c11610182578063cb649617116100e0578063ea71aa571161008f578063ea71aa57146108c0578063f0691cba146108d3578063f2fde38b146108e6578063f81b8ef6146108f9578063fad8e1111461090c578063fbdb32371461091f578063fd2f3d0114610947575f5ffd5b8063cb64961714610829578063cbd1687214610832578063cf0f34c414610845578063cfbdc98d14610858578063d8afed3e14610887578063da16fb2f1461089a578063e59e4695146108ad575f5ffd5b8063ac3d2f421161013c578063ac3d2f4214610780578063b27392d5146107a8578063b68fd1be146107bb578063bb2d1b82146107ce578063bff232c1146107e1578063c1ab0f1f146107f4578063c4ccafa214610807575f5ffd5b80639117173c146105ac57806392312386146105bf578063929a8faf146105d257806399c6679d146105f35780639c8570c81461061b578063a87f4ab91461062e575f5ffd5b80634fc772641161023a5780637cfa9d74116101e95780637cfa9d741461053157806381476ec21461054457806385814243146105575780638da5cb5b1461056a5780638dcdd86b146105725780638e5ce3ad1461058457806390173a4114610597575f5ffd5b80634fc77264146104b357806364226409146104c6578063647846a5146104e75780636db5c8fd146104fa578063715018a61461050357806377868ae41461050b5780637c8c3b4d1461051e575f5ffd5b80631ba72945116102965780631ba72945146103a657806336c5d38a146103b95780634017daf0146103e8578063406ed35c146104155780634147a36014610435578063459d9294146104625780634e92ec63146104a0575f5ffd5b806301d12f1c146102dd57806302a3a9c9146102f25780630ef81b2f1461030557806310bc62811461034357806311bd61d91461036b57806315cce22414610393575b5f5ffd5b6102f06102eb366004614729565b61095a565b005b6102f06103003660046147d9565b610ba1565b61032d6103133660046147fb565b5f908152600960205260409020546001600160a01b031690565b60405161033a919061481f565b60405180910390f35b61032d6103513660046147fb565b60096020525f90815260409020546001600160a01b031681565b61037e610379366004614841565b610c4d565b60405163ffffffff909116815260200161033a565b6102f06103a13660046147d9565b610c89565b6102f06103b4366004614869565b610d2e565b6103db6103c73660046147fb565b5f908152600f602052604090205460ff1690565b60405161033a91906148ab565b6103fb6103f63660046147fb565b610d42565b60405161033a9e9d9c9b9a999897969594939291906148f7565b6104286104233660046147fb565b610f6c565b60405161033a9190614ae7565b6104546104433660046147fb565b600c6020525f908152604090205481565b60405190815260200161033a565b610490610470366004614af9565b8051602081830181018051600b8252928201919093012091525460ff1681565b604051901515815260200161033a565b6102f06104ae3660046147fb565b611269565b6102f06104c13660046147d9565b6112f8565b6104d96104d4366004614b32565b61138b565b60405161033a929190614b69565b60045461032d906001600160a01b031681565b61045460055481565b6102f0611bd3565b6102f0610519366004614b81565b611be6565b6102f061052c366004614bb2565b611c7e565b6102f061053f3660046147fb565b611d07565b6102f0610552366004614be0565b611e06565b60015461032d906001600160a01b031681565b61032d611efa565b5f5461032d906001600160a01b031681565b60035461032d906001600160a01b031681565b61059f611f28565b60405161033a9190614c00565b6102f06105ba3660046147fb565b611f6e565b61059f6105cd3660046147fb565b6120dc565b6105e56105e03660046147fb565b612135565b60405161033a929190614c21565b61032d6106013660046147fb565b5f908152601060205260409020546001600160a01b031690565b610490610629366004614c74565b61215c565b610773604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161033a9190614ceb565b61032d61078e3660046147fb565b5f908152600a60205260409020546001600160a01b031690565b6104906107b6366004614df8565b6123f4565b6102f06107c9366004614b81565b612624565b6102f06107dc366004614e9d565b6126bb565b6102f06107ef3660046147d9565b612777565b6102f0610802366004614be0565b61281e565b6104906108153660046147d9565b60076020525f908152604090205460ff1681565b61045460065481565b6102f0610840366004614bb2565b6128db565b6102f06108533660046147fb565b61298e565b61087a6108663660046147fb565b5f908152600d602052604090205460ff1690565b60405161033a9190614ed5565b6102f0610895366004614ee3565b6129cb565b6104546108a8366004614b32565b612c58565b6102f06108bb3660046147d9565b6131dc565b6102f06108ce366004614efd565b613276565b60025461032d906001600160a01b031681565b6102f06108f43660046147d9565b613523565b6103db6109073660046147fb565b61355d565b6102f061091a3660046147d9565b6136f7565b61032d61092d3660046147fb565b600a6020525f90815260409020546001600160a01b031681565b6102f06109553660046147d9565b61378f565b5f61096361381e565b805490915060ff600160401b82041615906001600160401b03165f811580156109895750825b90505f826001600160401b031660011480156109a45750303b155b9050811580156109b2575080155b156109d05760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156109fa57845460ff60401b1916600160401b1785555b610a0333613848565b610a0c8861298e565b610a158c6136f7565b610a1e8b6131dc565b610a278a610ba1565b610a3089610c89565b610a3987613859565b610a4286612624565b604080516101e08101825261c3508082526161a86020808401829052611388948401859052601460608501819052620249f0608086018190526207a12060a087018190526107d060c088018190525f60e089018190526103e86101008a015261012089018190526109c46101408a018190526101608a018b90526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b031916690138827101388000007d60a31b179055805467ffffffffffffffff19169055610b2b611efa565b6001600160a01b03168d6001600160a01b031614610b4c57610b4c8d613523565b8315610b9257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050505050565b610ba9613916565b6001600160a01b038116610c045760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f208160028110610c66575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610c91613916565b6001600160a01b03811615801590610cb757506004546001600160a01b03828116911614155b8190610cd75760405163eddf07f560e01b8152600401610bfb919061481f565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610d2390839061481f565b60405180910390a150565b610d36613916565b610d3f81613859565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff90951695939492936001600160a01b039092169291610d8d90614f34565b80601f0160208091040260200160405190810160405280929190818152602001828054610db990614f34565b8015610e045780601f10610ddb57610100808354040283529160200191610e04565b820191905f5260205f20905b815481529060010190602001808311610de757829003601f168201915b505050505090806008018054610e1990614f34565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4590614f34565b8015610e905780601f10610e6757610100808354040283529160200191610e90565b820191905f5260205f20905b815481529060010190602001808311610e7357829003601f168201915b5050506009840154600a850154600b860154600c870154600d8801805497986001600160a01b03958616989490951696509194509291610ecf90614f34565b80601f0160208091040260200160405190810160405280929190818152602001828054610efb90614f34565b8015610f465780601f10610f1d57610100808354040283529160200191610f46565b820191905f5260205f20905b815481529060010190602001808311610f2957829003601f168201915b505050600e90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610f7461440e565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610fb157610fb1614883565b6003811115610fc257610fc2614883565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610fe75750505091835250506005820154602082015260068201546001600160a01b0316604082015260078201805460609092019161103490614f34565b80601f016020809104026020016040519081016040528092919081815260200182805461106090614f34565b80156110ab5780601f10611082576101008083540402835291602001916110ab565b820191905f5260205f20905b81548152906001019060200180831161108e57829003601f168201915b505050505081526020016008820180546110c490614f34565b80601f01602080910402602001604051908101604052809291908181526020018280546110f090614f34565b801561113b5780601f106111125761010080835404028352916020019161113b565b820191905f5260205f20905b81548152906001019060200180831161111e57829003601f168201915b505050918352505060098201546001600160a01b039081166020830152600a830154166040820152600b8201546060820152600c8201546080820152600d8201805460a09092019161118c90614f34565b80601f01602080910402602001604051908101604052809291908181526020018280546111b890614f34565b80156112035780601f106111da57610100808354040283529160200191611203565b820191905f5260205f20905b8154815290600101906020018083116111e657829003601f168201915b5050509183525050600e91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a08201519192508391166112635760405163cd6f4a4f60e01b8152600401610bfb91815260200190565b50919050565b611271613916565b5f8181526009602052604090205481906001600160a01b03166112aa576040516381c4951960e01b8152600401610bfb91815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610d239083815260200190565b611300613916565b6001600160a01b0381165f90815260076020526040902054819060ff1661133b576040516321ac7c5f60e01b8152600401610bfb919061481f565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610d2390839061481f565b5f61139461440e565b5f6012816113a56020870187614f66565b60038111156113b6576113b6614883565b60038111156113c7576113c7614883565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116113ee579050505050505090505f8160016002811061144557611445614f7f565b602002015163ffffffff1611845f0160208101906114639190614f66565b906114825760405163286c068d60e11b8152600401610bfb9190614f93565b506020840135428110156114ac57604051630b99e87960e01b8152600401610bfb91815260200190565b50604084013560208501358110156114da5760405163174b5a0760e21b8152600401610bfb91815260200190565b506017546016545f91906114f2426040890135614fb5565b6114fc9190614fc8565b6115069190614fc8565b90506005548110819061152f576040516313b783af60e21b8152600401610bfb91815260200190565b5060075f61154360808801606089016147d9565b6001600160a01b0316815260208101919091526040015f205460ff1661156f60808701606088016147d9565b9061158e5760405163295a6a6f60e11b8152600401610bfb919061481f565b505f61159986612c58565b60068054965090915085905f6115ae83614fdb565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff1916600117905560109094528290208054339416939093179092556016549192506116899190890135614fc8565b5f878152600e60209081526040909120600101919091558186526116af90880188614f66565b856020019060038111156116c5576116c5614883565b908160038111156116d8576116d8614883565b905250436040808701919091528051808201825290602089019060029083908390808284375f92019190915250505060608087019190915261172090608089019089016147d9565b6001600160a01b031660a086015261173b6080880188614ff3565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060c08087019190915261178390880188614ff3565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e0808701919091526117d0906101008901908901615042565b15156101c08601525f610140860181905261016086018190526040805160208101909152908152610180860152336101a08601819052600454611820916001600160a01b03909116903085613948565b5f6118316080890160608a016147d9565b6001600160a01b031663fefd9a8b888461184e60808d018d614ff3565b61185b60a08f018f614ff3565b8f8060c0019061186b9190614ff3565b6040518963ffffffff1660e01b815260040161188e989796959493929190615085565b6020604051808303815f875af11580156118aa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118ce91906150db565b5f818152600960205260409020549091506001600160a01b0316818161190a576040516381c4951960e01b8152600401610bfb91815260200190565b505f828152600a60205260409020546001600160a01b03168281611944576040516381c4951960e01b8152600401610bfb91815260200190565b50608088018390526001600160a01b038083166101008a015281166101208901525f8981526008602090815260409091208951815590890151600180830180548c94939260ff1991909116908360038111156119a2576119a2614883565b0217905550604082015181600201556060820151816003019060026119c892919061448b565b506080820151600582015560a08201516006820180546001600160a01b0319166001600160a01b0390921691909117905560c08201516007820190611a0d9082615151565b5060e08201516008820190611a229082615151565b506101008201516009820180546001600160a01b039283166001600160a01b031991821617909155610120840151600a84018054919093169116179055610140820151600b820155610160820151600c820155610180820151600d820190611a8a9082615151565b506101a0820151600e90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091555f5460405163291a691b60e01b815291169063291a691b90611af0908c9088908c90600401615206565b6020604051808303815f875af1158015611b0c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b30919061524b565b611b4d57604051630d8dbe2560e01b815260040160405180910390fd5b611b5d60808b0160608c016147d9565b6001600160a01b03167f5904e83d57e704fba4d92c5617f6b50ae993cd42d78babfcb239d0a6775f30708a8a604051611b97929190614b69565b60405180910390a2885f516020615ad85f395f51905f525f6001604051611bbf929190615266565b60405180910390a250505050505050915091565b611bdb613916565b611be45f6139b5565b565b611bee613916565b80515f5b81811015611c4257600b838281518110611c0e57611c0e614f7f565b6020026020010151604051611c239190615281565b908152604051908190036020019020805460ff19169055600101611bf2565b507fd1b46e030b48add7bc03225cc5a6f403970976b36983f99ec31d535d627fc7db82604051611c729190615297565b60405180910390a15050565b611c86613916565b6001600160a01b03811615801590611cb757505f828152600a60205260409020546001600160a01b03828116911614155b8290611cd9576040516381c4951960e01b8152600401610bfb91815260200190565b505f918252600a602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b5f546001600160a01b03163314611d315760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff166001816006811115611d5657611d56614883565b14611d7b57816001826040516337e1404160e01b8152600401610bfb939291906152fa565b5f828152600d60205260409020805460ff19166002179055601554611da09042614fc8565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f516020615ad85f395f51905f5260016002604051611dfa929190615266565b60405180910390a25050565b5f546001600160a01b03163314611e305760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff166002816006811115611e6057611e60614883565b14611e8557836002826040516337e1404160e01b8152600401610bfb939291906152fa565b5f848152600d6020526040808220805460ff19166003179055600b84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f516020615ad85f395f51905f5260026003604051611eec929190615266565b60405180910390a250505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b611f4960405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff166006816006811115611f9357611f93614883565b148290611fb657604051637cb2d48360e11b8152600401610bfb91815260200190565b505f828152600c60205260409020548281611fe7576040516345ba89d560e11b8152600401610bfb91815260200190565b505f838152600c6020526040812081905561200184613a25565b5f858152601160205260409020546002549192506001600160a01b039081169161202e9183911685613b12565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b6979061206490889087908790879060040161535e565b5f604051808303815f87803b15801561207b575f5ffd5b505af115801561208d573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a073178484516040516120cd929190918252602082015260400190565b60405180910390a25050505050565b6120fd60405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff166121528482613b3d565b9250925050915091565b5f5f61216787610f6c565b5f888152600d602052604090205490915060ff16600381600681111561218f5761218f614883565b14886003839091926121b7576040516337e1404160e01b8152600401610bfb939291906152fa565b5050505f888152600e602090815260409182902082516060810184528154815260018201549281018390526002909101549281019290925289904281101561221b576040516308f3034360e31b815260048101929092526024820152604401610bfb565b50506060830151602001518990428111156122525760405163017e35e560e71b815260048101929092526024820152604401610bfb565b505061016083015189901561227d57604051637eb9cea960e11b8152600401610bfb91815260200190565b505f888860405161228f929190615395565b60408051918290039091205f8c815260086020908152838220600c01839055600d905291909120805460ff191660041790556017549091506122d19042614fc8565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf9061231e908d9085908c908c906004016153a4565b6020604051808303815f875af115801561233a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061235e919061524b565b945088888661238257604051632f9f8ab960e01b8152600401610bfb9291906153c3565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a6040516123b69291906153c3565b60405180910390a2895f516020615ad85f395f51905f52600360046040516123df929190615266565b60405180910390a25050505095945050505050565b5f5f6123ff89610f6c565b5f8a8152600d602052604090205490915060ff16600481600681111561242757612427614883565b148a60048390919261244f576040516337e1404160e01b8152600401610bfb939291906152fa565b5050505f8a8152600e602090815260409182902082516060810184528154815260018201549281019290925260020154918101829052908b90428110156124b2576040516308f3034360e31b815260048101929092526024820152604401610bfb565b50505f8b8152600860205260409020600d016124cf8a8c836153d6565b505f8b8152600d6020526040902080546005919060ff191660018302179055508261010001516001600160a01b0316635bf48e3a8b8b604051612513929190615395565b6040519081900381206001600160e01b031960e084901b168252612541918c908c908c908c9060040161548a565b602060405180830381865afa15801561255c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612580919061524b565b93508989856125a457604051632f9f8ab960e01b8152600401610bfb9291906153c3565b50506125af8b613cc5565b8a7f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b8b8b8b8b6040516125e594939291906154c2565b60405180910390a28a5f516020615ad85f395f51905f526004600560405161260e929190615266565b60405180910390a2505050979650505050505050565b61262c613916565b80515f5b8181101561268b576001600b84838151811061264e5761264e614f7f565b60200260200101516040516126639190615281565b908152604051908190036020019020805491151560ff19909216919091179055600101612630565b507f027b83cad653f54850fef6faa8c705f73a53e7f8de50a3a33ac1e0e3a5c0be8182604051611c729190615297565b5f546001600160a01b03163314806126dd57506003546001600160a01b031633145b6126fa57604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156127105750600d60ff821611155b6127555760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610bfb565b612773828260ff16600d81111561276e5761276e614883565b614131565b5050565b61277f613916565b6001600160a01b0381166127d55760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610bfb565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b03163314612849576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b158015612893575f5ffd5b505af11580156128a5573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee54282604051611dfa91815260200190565b6128e3613916565b6001600160a01b0381161580159061291457505f828152600960205260409020546001600160a01b03828116911614155b8290612936576040516381c4951960e01b8152600401610bfb91815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb699101611c72565b612996613916565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610d23565b6129d3613916565b6127106129e86101208301610100840161550d565b61ffff161115612a006101208301610100840161550d565b90612a25576040516301027fc160e21b815261ffff9091166004820152602401610bfb565b50612710612a3b6101408301610120840161550d565b61ffff161115612a536101408301610120840161550d565b90612a78576040516301027fc160e21b815261ffff9091166004820152602401610bfb565b50612710612a8e6101608301610140840161550d565b61ffff161115612aa66101608301610140840161550d565b90612acb57604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612710612ae16101808301610160840161550d565b61ffff161115612af96101808301610160840161550d565b90612b1e57604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612710612b346101a08301610180840161550d565b61ffff161115612b4c6101a08301610180840161550d565b90612b7157604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612b846101408201610120830161550d565b61ffff161580612bad57505f612ba1610100830160e084016147d9565b6001600160a01b031614155b612bca5760405163015f92ff60e51b815260040160405180910390fd5b612bdc6101e082016101c08301615544565b63ffffffff16612bf46101c083016101a08401615544565b63ffffffff161015612c19576040516392f55c6560e01b815260040160405180910390fd5b806018612c268282615583565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610d239190615741565b5f80601281612c6a6020860186614f66565b6003811115612c7b57612c7b614883565b6003811115612c8c57612c8c614883565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612cb3579050505050505090505f81600160028110612d0a57612d0a614f7f565b602002015163ffffffff1611835f016020810190612d289190614f66565b90612d475760405163286c068d60e11b8152600401610bfb9190614f93565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c086015292831693919092169115612e72576101a081015163ffffffff16846001602002015163ffffffff161015865f016020810190612e519190614f66565b90612e705760405163010b971d60e31b8152600401610bfb9190614f93565b505b6101c081015163ffffffff1615612ec1576101c081015184519063ffffffff9081169082161015612ebf57604051630a4b6b6360e11b815263ffffffff9091166004820152602401610bfb565b505b60408601356020870135811015612eee5760405163174b5a0760e21b8152600401610bfb91815260200190565b506101808101516017545f9161271091612f0c9161ffff169061584b565b612f169190615862565b61271061ffff1683610160015161ffff16601560010154612f37919061584b565b612f419190615862565b61271061ffff1684610140015161ffff1660155f0154612f61919061584b565b612f6b9190615862565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa158015612fb7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fdb91906150db565b612fe59190614fc8565b612fef9190614fb5565b612ff99190614fc8565b6130039190614fc8565b61300d9190614fc8565b90505f61301b600186614fb5565b61302690600261584b565b61303190600261584b565b61303c906006614fc8565b90505f85845f015161304e919061584b565b905081868560200151613061919061584b565b61306b919061584b565b6130759082614fc8565b905060018611156130bd57600261308d600188614fb5565b613097908861584b565b85604001516130a6919061584b565b6130b09190615862565b6130ba9082614fc8565b90505b81868560c001516130ce919061584b565b6130d8919061584b565b6130e29082614fc8565b9050828685606001516130f5919061584b565b6130ff919061584b565b6131099082614fc8565b905084846080015161311b919061584b565b6131259082614fc8565b9050600185111561316d57600261313d600187614fb5565b613147908761584b565b8560400151613156919061584b565b6131609190615862565b61316a9082614fc8565b90505b60a084015161317c9082614fc8565b610100850151909150612710906131979061ffff1682614fc8565b6131a1908361584b565b6131ab9190615862565b975087806131cf57604051638c4fcd9360e01b8152600401610bfb91815260200190565b5050505050505050919050565b6131e4613916565b6001600160a01b0381161580159061320a57506001546001600160a01b03828116911614155b819061322a576040516320252f0b60e01b8152600401610bfb919061481f565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610d2390839061481f565b61327e613916565b61328b6020820182615544565b63ffffffff166132a16040830160208401615544565b63ffffffff16101580156132c657505f6132be6020830183615544565b63ffffffff16115b6132e357604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c08301521561341c576101a081015163ffffffff166133cc6040840160208501615544565b63ffffffff1610156133e46040840160208501615544565b826101a00151909161341957604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610bfb565b50505b6101c081015163ffffffff1615613493576101c081015163ffffffff166134466020840184615544565b63ffffffff16101561345b6020840184615544565b826101c0015190916134905760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610bfb565b50505b8160125f8560038111156134a9576134a9614883565b60038111156134ba576134ba614883565b815260208101919091526040015f206134d49160026144c9565b508260038111156134e7576134e7614883565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa836040516135169190615881565b60405180910390a2505050565b61352b613916565b6001600160a01b038116613554575f604051631e4fbdf760e01b8152600401610bfb919061481f565b610d3f816139b5565b5f818152600d602052604081205460ff168181600681111561358157613581614883565b036135a657826001826040516337e1404160e01b8152600401610bfb939291906152fa565b60058160068111156135ba576135ba614883565b036135db5760405163462c7bed60e01b815260048101849052602401610bfb565b60068160068111156135ef576135ef614883565b0361361057604051633de16e3560e11b815260048101849052602401610bfb565b5f61361b8483613b3d565b935090508061364057604051639f65d93560e01b815260048101859052602401610bfb565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d81111561368957613689614883565b0217905550835f516020615ad85f395f51905f528360066040516136ae929190615266565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb83856040516136e89291906158c1565b60405180910390a25050919050565b6136ff613916565b6001600160a01b0381161580159061372457505f546001600160a01b03828116911614155b8190613744576040516375ac4eb760e11b8152600401610bfb919061481f565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610d2390839061481f565b6001600160a01b0381165f90815260076020526040902054819060ff16156137cb5760405163b29d459560e01b8152600401610bfb919061481f565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610d2390839061481f565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b61385061428c565b610d3f816142b1565b80356138785760405163055f269d60e01b815260040160405180910390fd5b5f81602001351161389c5760405163055f269d60e01b815260040160405180910390fd5b5f8160400135116138c05760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610d23565b3361391f611efa565b6001600160a01b031614611be4573360405163118cdaa760e01b8152600401610bfb919061481f565b6040516001600160a01b0384811660248301528381166044830152606482018390526139af9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506142b9565b50505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f818152600f602052604090205460609060ff16600181600d811115613a4d57613a4d614883565b1480613a6a5750600281600d811115613a6857613a68614883565b145b15613aa2575f5b604051908082528060200260200182016040528015613a9a578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa925050508015613b0857506040513d5f823e601f3d908101601f19168201604052613b059190810190615937565b60015b613a9a575f613a71565b613b3883846001600160a01b031663a9059cbb858560405160240161397d9291906159fc565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa158015613bb7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bdb91906150db565b90506001856006811115613bf157613bf1614883565b148015613bfd57508042115b15613c1057600180935093505050613cbe565b6002856006811115613c2457613c24614883565b148015613c315750815142115b15613c455760016003935093505050613cbe565b6003856006811115613c5957613c59614883565b148015613c695750816020015142115b15613c7d5760016006935093505050613cbe565b6004856006811115613c9157613c91614883565b148015613ca15750816040015142115b15613cb5576001600a935093505050613cbe565b5f5f9350935050505b9250929050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613d0a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613d319190810190615937565b5080515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613dd0576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613d9c90889088908690600401615a15565b5f604051808303815f87803b158015613db3575f5ffd5b505af1158015613dc5573d5f5f3e3d5ffd5b505050505050505050565b825f03613e71575f858152601060205260409020546001600160a01b03168015613e0857613e086001600160a01b0383168285613b12565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613e3c90899089908790600401615a15565b5f604051808303815f87803b158015613e53575f5ffd5b505af1158015613e65573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613eb157506001600160a01b03811615155b15613eed57612710613ec761ffff84168761584b565b613ed19190615862565b92508215613eed57613eed6001600160a01b0385168285613b12565b5f613ef88487614fb5565b90505f876001600160401b03811115613f1357613f136145cb565b604051908082528060200260200182016040528015613f3c578160200160208202803683370190505b5090505f613f4a8984615862565b90505f805b8a811015613f895782848281518110613f6a57613f6a614f7f565b6020908102919091010152613f7f8383614fc8565b9150600101613f4f565b505f613f958286614fb5565b90508015613fd2578084613faa60018e614fb5565b81518110613fba57613fba614f7f565b60200260200101818151613fce9190614fc8565b9052505b600154613fec906001600160a01b038b811691168761431c565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b815260040161403093929190615a75565b5f604051808303815f87803b158015614047575f5ffd5b505af1158015614059573d5f5f3e3d5ffd5b505060015461407792506001600160a01b038c81169250165f61431c565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d866040516140a9929190615aaa565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b81526004016140f593929190615a15565b5f604051808303815f87803b15801561410c575f5ffd5b505af115801561411e573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f828152600d602052604081205460ff169081600681111561415557614155614883565b0361417a57826001826040516337e1404160e01b8152600401610bfb939291906152fa565b600581600681111561418e5761418e614883565b036141af5760405163462c7bed60e01b815260048101849052602401610bfb565b60068160068111156141c3576141c3614883565b036141e457604051633de16e3560e11b815260048101849052602401610bfb565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d81111561422d5761422d614883565b0217905550825f516020615ad85f395f51905f52826006604051614252929190615266565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb82846040516135169291906158c1565b6142946143ac565b611be457604051631afcd79f60e31b815260040160405180910390fd5b61352b61428c565b5f5f60205f8451602086015f885af1806142d8576040513d5f823e3d81fd5b50505f513d915081156142ef5780600114156142fc565b6001600160a01b0384163b155b156139af5783604051635274afe760e01b8152600401610bfb919061481f565b5f836001600160a01b031663095ea7b3848460405160240161433f9291906159fc565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050905061437884826143c5565b6139af576143a284856001600160a01b031663095ea7b3865f60405160240161397d9291906159fc565b6139af84826142b9565b5f6143b561381e565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f519050828015614404575081156143f65780600114614404565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f8152602001614436614565565b81525f602082018190526040820181905260608083018190526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b82600281019282156144b9579160200282015b828111156144b957825182559160200191906001019061449e565b506144c5929150614583565b5090565b6001830191839082156144b9579160200282015f5b8382111561452857833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026144de565b80156145585782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614528565b50506144c5929150614583565b60405180604001604052806002906020820280368337509192915050565b5b808211156144c5575f8155600101614584565b6001600160a01b0381168114610d3f575f5ffd5b80356145b681614597565b919050565b5f60608284031215611263575f5ffd5b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715614607576146076145cb565b604052919050565b5f6001600160401b03821115614627576146276145cb565b5060051b60200190565b5f82601f830112614640575f5ffd5b81356001600160401b03811115614659576146596145cb565b61466c601f8201601f19166020016145df565b818152846020838601011115614680575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f82601f8301126146ab575f5ffd5b81356146be6146b98261460f565b6145df565b8082825260208201915060208360051b8601019250858311156146df575f5ffd5b602085015b8381101561471f5780356001600160401b03811115614701575f5ffd5b614710886020838a0101614631565b845250602092830192016146e4565b5095945050505050565b5f5f5f5f5f5f5f5f610140898b031215614741575f5ffd5b883561474c81614597565b9750602089013561475c81614597565b9650604089013561476c81614597565b9550606089013561477c81614597565b9450608089013561478c81614597565b935060a089013592506147a28a60c08b016145bb565b91506101208901356001600160401b038111156147bd575f5ffd5b6147c98b828c0161469c565b9150509295985092959890939650565b5f602082840312156147e9575f5ffd5b81356147f481614597565b9392505050565b5f6020828403121561480b575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b8035600481106145b6575f5ffd5b5f5f60408385031215614852575f5ffd5b61485b83614833565b946020939093013593505050565b5f60608284031215614879575f5ffd5b6147f483836145bb565b634e487b7160e01b5f52602160045260245ffd5b600e81106148a7576148a7614883565b9052565b602081016138428284614897565b600481106148a7576148a7614883565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e8152614907602082018f6148b9565b8c60408201528b6060820152614920608082018c614812565b6101c060a08201525f6149376101c083018c6148c9565b82810360c0840152614949818c6148c9565b905061495860e084018b614812565b61496661010084018a614812565b876101208401528661014084015282810361016084015261498781876148c9565b915050614998610180830185614812565b8215156101a08301529f9e505050505050505050505050505050565b805f5b60028110156139af5781518452602093840193909101906001016149b7565b805182525f60208201516149ed60208501826148b9565b50604082015160408401526060820151614a0a60608501826149b4565b50608082015160a084015260a0820151614a2760c0850182614812565b5060c082015161020060e0850152614a436102008501826148c9565b905060e0830151848203610100860152614a5d82826148c9565b915050610100830151614a74610120860182614812565b50610120830151614a89610140860182614812565b506101408301516101608501526101608301516101808501526101808301518482036101a0860152614abb82826148c9565b9150506101a0830151614ad26101c0860182614812565b506101c08301518015156101e0860152613a9a565b602081525f6147f460208301846149d6565b5f60208284031215614b09575f5ffd5b81356001600160401b03811115614b1e575f5ffd5b614b2a84828501614631565b949350505050565b5f60208284031215614b42575f5ffd5b81356001600160401b03811115614b57575f5ffd5b820161010081850312156147f4575f5ffd5b828152604060208201525f614b2a60408301846149d6565b5f60208284031215614b91575f5ffd5b81356001600160401b03811115614ba6575f5ffd5b614b2a8482850161469c565b5f5f60408385031215614bc3575f5ffd5b823591506020830135614bd581614597565b809150509250929050565b5f5f60408385031215614bf1575f5ffd5b50508035926020909101359150565b81518152602080830151908201526040808301519082015260608101613842565b8215158152604081016147f46020830184614897565b5f5f83601f840112614c47575f5ffd5b5081356001600160401b03811115614c5d575f5ffd5b602083019150836020828501011115613cbe575f5ffd5b5f5f5f5f5f60608688031215614c88575f5ffd5b8535945060208601356001600160401b03811115614ca4575f5ffd5b614cb088828901614c37565b90955093505060408601356001600160401b03811115614cce575f5ffd5b614cda88828901614c37565b969995985093965092949392505050565b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614d4560e0840182614812565b50610100830151614d5d61010084018261ffff169052565b50610120830151614d7561012084018261ffff169052565b50610140830151614d8d61014084018261ffff169052565b50610160830151614da561016084018261ffff169052565b50610180830151614dbd61018084018261ffff169052565b506101a0830151614dd76101a084018263ffffffff169052565b506101c0830151614df16101c084018263ffffffff169052565b5092915050565b5f5f5f5f5f5f5f6080888a031215614e0e575f5ffd5b8735965060208801356001600160401b03811115614e2a575f5ffd5b614e368a828b01614c37565b90975095505060408801356001600160401b03811115614e54575f5ffd5b614e608a828b01614c37565b90955093505060608801356001600160401b03811115614e7e575f5ffd5b614e8a8a828b01614c37565b989b979a50959850939692959293505050565b5f5f60408385031215614eae575f5ffd5b82359150602083013560ff81168114614bd5575f5ffd5b600781106148a7576148a7614883565b602081016138428284614ec5565b5f6101e0828403128015614ef5575f5ffd5b509092915050565b5f5f60608385031215614f0e575f5ffd5b614f1783614833565b915083606084011115614f28575f5ffd5b50926020919091019150565b600181811c90821680614f4857607f821691505b60208210810361126357634e487b7160e01b5f52602260045260245ffd5b5f60208284031215614f76575f5ffd5b6147f482614833565b634e487b7160e01b5f52603260045260245ffd5b6020810161384282846148b9565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561384257613842614fa1565b8082018082111561384257613842614fa1565b5f60018201614fec57614fec614fa1565b5060010190565b5f5f8335601e19843603018112615008575f5ffd5b8301803591506001600160401b03821115615021575f5ffd5b602001915036819003821315613cbe575f5ffd5b8015158114610d3f575f5ffd5b5f60208284031215615052575f5ffd5b81356147f481615035565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b88815287602082015260a060408201525f6150a460a08301888a61505d565b82810360608401526150b781878961505d565b905082810360808401526150cc81858761505d565b9b9a5050505050505050505050565b5f602082840312156150eb575f5ffd5b5051919050565b601f821115613b3857805f5260205f20601f840160051c810160208510156151175750805b601f840160051c820191505b81811015615136575f8155600101615123565b5050505050565b5f19600383901b1c191660019190911b1790565b81516001600160401b0381111561516a5761516a6145cb565b61517e816151788454614f34565b846150f2565b6020601f8211600181146151ab575f83156151995750848201515b6151a3848261513d565b855550615136565b5f84815260208120601f198516915b828110156151da57878501518255602094850194600190920191016151ba565b50848210156151f757868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b600281101561524057815163ffffffff1683526020928301929091019060010161521b565b505050949350505050565b5f6020828403121561525b575f5ffd5b81516147f481615035565b604081016152748285614ec5565b6147f46020830184614ec5565b5f82518060208501845e5f920191825250919050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b828110156152ee57603f198786030184526152d98583516148c9565b945060209384019391909101906001016152bd565b50929695505050505050565b8381526060810161530e6020830185614ec5565b614b2a6040830184614ec5565b5f8151808452602084019350602083015f5b828110156153545781516001600160a01b031686526020958601959091019060010161532d565b5093949350505050565b848152836020820152608060408201525f61537c608083018561531b565b905060018060a01b038316606083015295945050505050565b818382375f9101908152919050565b848152836020820152606060408201525f61440460608301848661505d565b602081525f614b2a60208301848661505d565b6001600160401b038311156153ed576153ed6145cb565b615401836153fb8354614f34565b836150f2565b5f601f84116001811461542d575f851561541b5750838201355b615425868261513d565b845550615136565b5f83815260208120601f198716915b8281101561545c578685013582556020948501946001909201910161543c565b5086821015615478575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b858152606060208201525f6154a360608301868861505d565b82810360408401526154b681858761505d565b98975050505050505050565b604081525f6154d560408301868861505d565b82810360208401526154e881858761505d565b979650505050505050565b61ffff81168114610d3f575f5ffd5b80356145b6816154f3565b5f6020828403121561551d575f5ffd5b81356147f4816154f3565b63ffffffff81168114610d3f575f5ffd5b80356145b681615528565b5f60208284031215615554575f5ffd5b81356147f481615528565b5f813561384281614597565b5f8135613842816154f3565b5f813561384281615528565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c08201356006820155600781016155f36155d660e0850161555f565b82546001600160a01b0319166001600160a01b0391909116178255565b615623615603610100850161556b565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b615653615633610120850161556b565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b615683615663610140850161556b565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b6156b3615693610160850161556b565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b6156e36156c3610180850161556b565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b506008810161570f6156f86101a08501615577565b825463ffffffff191663ffffffff91909116178255565b613b3861571f6101c08501615577565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e0810161579260e084016145ab565b61579f60e0840182614812565b506157ad6101008401615502565b61ffff166101008301526157c46101208401615502565b61ffff166101208301526157db6101408401615502565b61ffff166101408301526157f26101608401615502565b61ffff166101608301526158096101808401615502565b61ffff166101808301526158206101a08401615539565b63ffffffff166101a08301526158396101c08401615539565b63ffffffff81166101c0840152614df1565b808202811582820484141761384257613842614fa1565b5f8261587c57634e487b7160e01b5f52601260045260245ffd5b500490565b6040810181835f5b60028110156158b857813561589d81615528565b63ffffffff1683526020928301929190910190600101615889565b50505092915050565b604081016158cf8285614ec5565b6147f46020830184614897565b5f82601f8301126158eb575f5ffd5b81516158f96146b98261460f565b8082825260208201915060208360051b86010192508583111561591a575f5ffd5b602085015b8381101561471f57805183526020928301920161591f565b5f5f60408385031215615948575f5ffd5b82516001600160401b0381111561595d575f5ffd5b8301601f8101851361596d575f5ffd5b805161597b6146b98261460f565b8082825260208201915060208360051b85010192508783111561599c575f5ffd5b6020840193505b828410156159c75783516159b681614597565b8252602093840193909101906159a3565b8095505050505060208301516001600160401b038111156159e6575f5ffd5b6159f2858286016158dc565b9150509250929050565b6001600160a01b03929092168252602082015260400190565b838152606060208201525f615a2d606083018561531b565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b82811015615354578151865260209586019590910190600101615a57565b6001600160a01b03841681526060602082018190525f90615a989083018561531b565b82810360408401526144048185615a45565b604081525f615abc604083018561531b565b8281036020840152615ace8185615a45565b9594505050505056fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b50600436106102d9575f3560e01c80639117173c11610182578063cb649617116100e0578063ea71aa571161008f578063ea71aa57146108c0578063f0691cba146108d3578063f2fde38b146108e6578063f81b8ef6146108f9578063fad8e1111461090c578063fbdb32371461091f578063fd2f3d0114610947575f5ffd5b8063cb64961714610829578063cbd1687214610832578063cf0f34c414610845578063cfbdc98d14610858578063d8afed3e14610887578063da16fb2f1461089a578063e59e4695146108ad575f5ffd5b8063ac3d2f421161013c578063ac3d2f4214610780578063b27392d5146107a8578063b68fd1be146107bb578063bb2d1b82146107ce578063bff232c1146107e1578063c1ab0f1f146107f4578063c4ccafa214610807575f5ffd5b80639117173c146105ac57806392312386146105bf578063929a8faf146105d257806399c6679d146105f35780639c8570c81461061b578063a87f4ab91461062e575f5ffd5b80634fc772641161023a5780637cfa9d74116101e95780637cfa9d741461053157806381476ec21461054457806385814243146105575780638da5cb5b1461056a5780638dcdd86b146105725780638e5ce3ad1461058457806390173a4114610597575f5ffd5b80634fc77264146104b357806364226409146104c6578063647846a5146104e75780636db5c8fd146104fa578063715018a61461050357806377868ae41461050b5780637c8c3b4d1461051e575f5ffd5b80631ba72945116102965780631ba72945146103a657806336c5d38a146103b95780634017daf0146103e8578063406ed35c146104155780634147a36014610435578063459d9294146104625780634e92ec63146104a0575f5ffd5b806301d12f1c146102dd57806302a3a9c9146102f25780630ef81b2f1461030557806310bc62811461034357806311bd61d91461036b57806315cce22414610393575b5f5ffd5b6102f06102eb366004614729565b61095a565b005b6102f06103003660046147d9565b610ba1565b61032d6103133660046147fb565b5f908152600960205260409020546001600160a01b031690565b60405161033a919061481f565b60405180910390f35b61032d6103513660046147fb565b60096020525f90815260409020546001600160a01b031681565b61037e610379366004614841565b610c4d565b60405163ffffffff909116815260200161033a565b6102f06103a13660046147d9565b610c89565b6102f06103b4366004614869565b610d2e565b6103db6103c73660046147fb565b5f908152600f602052604090205460ff1690565b60405161033a91906148ab565b6103fb6103f63660046147fb565b610d42565b60405161033a9e9d9c9b9a999897969594939291906148f7565b6104286104233660046147fb565b610f6c565b60405161033a9190614ae7565b6104546104433660046147fb565b600c6020525f908152604090205481565b60405190815260200161033a565b610490610470366004614af9565b8051602081830181018051600b8252928201919093012091525460ff1681565b604051901515815260200161033a565b6102f06104ae3660046147fb565b611269565b6102f06104c13660046147d9565b6112f8565b6104d96104d4366004614b32565b61138b565b60405161033a929190614b69565b60045461032d906001600160a01b031681565b61045460055481565b6102f0611bd3565b6102f0610519366004614b81565b611be6565b6102f061052c366004614bb2565b611c7e565b6102f061053f3660046147fb565b611d07565b6102f0610552366004614be0565b611e06565b60015461032d906001600160a01b031681565b61032d611efa565b5f5461032d906001600160a01b031681565b60035461032d906001600160a01b031681565b61059f611f28565b60405161033a9190614c00565b6102f06105ba3660046147fb565b611f6e565b61059f6105cd3660046147fb565b6120dc565b6105e56105e03660046147fb565b612135565b60405161033a929190614c21565b61032d6106013660046147fb565b5f908152601060205260409020546001600160a01b031690565b610490610629366004614c74565b61215c565b610773604080516101e0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081019190915250604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a0840152640100000000909104166101c082015290565b60405161033a9190614ceb565b61032d61078e3660046147fb565b5f908152600a60205260409020546001600160a01b031690565b6104906107b6366004614df8565b6123f4565b6102f06107c9366004614b81565b612624565b6102f06107dc366004614e9d565b6126bb565b6102f06107ef3660046147d9565b612777565b6102f0610802366004614be0565b61281e565b6104906108153660046147d9565b60076020525f908152604090205460ff1681565b61045460065481565b6102f0610840366004614bb2565b6128db565b6102f06108533660046147fb565b61298e565b61087a6108663660046147fb565b5f908152600d602052604090205460ff1690565b60405161033a9190614ed5565b6102f0610895366004614ee3565b6129cb565b6104546108a8366004614b32565b612c58565b6102f06108bb3660046147d9565b6131dc565b6102f06108ce366004614efd565b613276565b60025461032d906001600160a01b031681565b6102f06108f43660046147d9565b613523565b6103db6109073660046147fb565b61355d565b6102f061091a3660046147d9565b6136f7565b61032d61092d3660046147fb565b600a6020525f90815260409020546001600160a01b031681565b6102f06109553660046147d9565b61378f565b5f61096361381e565b805490915060ff600160401b82041615906001600160401b03165f811580156109895750825b90505f826001600160401b031660011480156109a45750303b155b9050811580156109b2575080155b156109d05760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156109fa57845460ff60401b1916600160401b1785555b610a0333613848565b610a0c8861298e565b610a158c6136f7565b610a1e8b6131dc565b610a278a610ba1565b610a3089610c89565b610a3987613859565b610a4286612624565b604080516101e08101825261c3508082526161a86020808401829052611388948401859052601460608501819052620249f0608086018190526207a12060a087018190526107d060c088018190525f60e089018190526103e86101008a015261012089018190526109c46101408a018190526101608a018b90526101808a01526101a089018190526101c090980197909752601895909555601993909355601a95909555601b94909455601c55601d55601e55601f80546001600160f01b031916690138827101388000007d60a31b179055805467ffffffffffffffff19169055610b2b611efa565b6001600160a01b03168d6001600160a01b031614610b4c57610b4c8d613523565b8315610b9257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050505050565b610ba9613916565b6001600160a01b038116610c045760405162461bcd60e51b815260206004820152601f60248201527f496e76616c6964204533526566756e644d616e6167657220616464726573730060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040517f9557d04c1c0b16f93f13b69aed23b3b6ab935bff3c53ac81d17896d3583542ed905f90a250565b6012602052815f5260405f208160028110610c66575f80fd5b60089182820401919006600402915091509054906101000a900463ffffffff1681565b610c91613916565b6001600160a01b03811615801590610cb757506004546001600160a01b03828116911614155b8190610cd75760405163eddf07f560e01b8152600401610bfb919061481f565b50600480546001600160a01b0319166001600160a01b0383161790556040517f722ff84c1234b2482061def5c82c6b5080c117b3cbb69d686844a051e4b8e7f390610d2390839061481f565b60405180910390a150565b610d36613916565b610d3f81613859565b50565b60086020525f9081526040902080546001820154600283015460058401546006850154600786018054959660ff90951695939492936001600160a01b039092169291610d8d90614f34565b80601f0160208091040260200160405190810160405280929190818152602001828054610db990614f34565b8015610e045780601f10610ddb57610100808354040283529160200191610e04565b820191905f5260205f20905b815481529060010190602001808311610de757829003601f168201915b505050505090806008018054610e1990614f34565b80601f0160208091040260200160405190810160405280929190818152602001828054610e4590614f34565b8015610e905780601f10610e6757610100808354040283529160200191610e90565b820191905f5260205f20905b815481529060010190602001808311610e7357829003601f168201915b5050506009840154600a850154600b860154600c870154600d8801805497986001600160a01b03958616989490951696509194509291610ecf90614f34565b80601f0160208091040260200160405190810160405280929190818152602001828054610efb90614f34565b8015610f465780601f10610f1d57610100808354040283529160200191610f46565b820191905f5260205f20905b815481529060010190602001808311610f2957829003601f168201915b505050600e90930154919250506001600160a01b0381169060ff600160a01b909104168e565b610f7461440e565b5f8281526008602090815260409182902082516101e08101909352805483526001810154909183019060ff166003811115610fb157610fb1614883565b6003811115610fc257610fc2614883565b8152600282810154602083015260408051808201808352919093019291600385019182845b815481526020019060010190808311610fe75750505091835250506005820154602082015260068201546001600160a01b0316604082015260078201805460609092019161103490614f34565b80601f016020809104026020016040519081016040528092919081815260200182805461106090614f34565b80156110ab5780601f10611082576101008083540402835291602001916110ab565b820191905f5260205f20905b81548152906001019060200180831161108e57829003601f168201915b505050505081526020016008820180546110c490614f34565b80601f01602080910402602001604051908101604052809291908181526020018280546110f090614f34565b801561113b5780601f106111125761010080835404028352916020019161113b565b820191905f5260205f20905b81548152906001019060200180831161111e57829003601f168201915b505050918352505060098201546001600160a01b039081166020830152600a830154166040820152600b8201546060820152600c8201546080820152600d8201805460a09092019161118c90614f34565b80601f01602080910402602001604051908101604052809291908181526020018280546111b890614f34565b80156112035780601f106111da57610100808354040283529160200191611203565b820191905f5260205f20905b8154815290600101906020018083116111e657829003601f168201915b5050509183525050600e91909101546001600160a01b038082166020840152600160a01b90910460ff16151560409092019190915260a08201519192508391166112635760405163cd6f4a4f60e01b8152600401610bfb91815260200190565b50919050565b611271613916565b5f8181526009602052604090205481906001600160a01b03166112aa576040516381c4951960e01b8152600401610bfb91815260200190565b505f818152600960205260409081902080546001600160a01b0319169055517f104eb329a192aef26eddea07c2af5ad2587792e62b37ed4045b6ba59bc5540fc90610d239083815260200190565b611300613916565b6001600160a01b0381165f90815260076020526040902054819060ff1661133b576040516321ac7c5f60e01b8152600401610bfb919061481f565b506001600160a01b0381165f9081526007602052604090819020805460ff19169055517f56070b80bd617fcd2f7a284861edb488830a38f9dedcd77b2cb2f4eac17743e790610d2390839061481f565b5f61139461440e565b5f6012816113a56020870187614f66565b60038111156113b6576113b6614883565b60038111156113c7576113c7614883565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff16815260200190600401906020826003010492830192600103820291508084116113ee579050505050505090505f8160016002811061144557611445614f7f565b602002015163ffffffff1611845f0160208101906114639190614f66565b906114825760405163286c068d60e11b8152600401610bfb9190614f93565b506020840135428110156114ac57604051630b99e87960e01b8152600401610bfb91815260200190565b50604084013560208501358110156114da5760405163174b5a0760e21b8152600401610bfb91815260200190565b506017546016545f91906114f2426040890135614fb5565b6114fc9190614fc8565b6115069190614fc8565b90506005548110819061152f576040516313b783af60e21b8152600401610bfb91815260200190565b5060075f61154360808801606089016147d9565b6001600160a01b0316815260208101919091526040015f205460ff1661156f60808701606088016147d9565b9061158e5760405163295a6a6f60e11b8152600401610bfb919061481f565b505f61159986612c58565b60068054965090915085905f6115ae83614fdb565b9091555050604080514460208201529081018690525f9060600160408051601f1981840301815291815281516020928301205f898152600c84528281208690556004546011855283822080546001600160a01b03199081166001600160a01b0393841617909155601f805460138852868520805461ffff191661ffff600160b01b909304929092169190911790555460148752858420805483169190931617909155600d8552838220805460ff1916600117905560109094528290208054339416939093179092556016549192506116899190890135614fc8565b5f878152600e60209081526040909120600101919091558186526116af90880188614f66565b856020019060038111156116c5576116c5614883565b908160038111156116d8576116d8614883565b905250436040808701919091528051808201825290602089019060029083908390808284375f92019190915250505060608087019190915261172090608089019089016147d9565b6001600160a01b031660a086015261173b6080880188614ff3565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060c08087019190915261178390880188614ff3565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525050505060e0808701919091526117d0906101008901908901615042565b15156101c08601525f610140860181905261016086018190526040805160208101909152908152610180860152336101a08601819052600454611820916001600160a01b03909116903085613948565b5f6118316080890160608a016147d9565b6001600160a01b031663fefd9a8b888461184e60808d018d614ff3565b61185b60a08f018f614ff3565b8f8060c0019061186b9190614ff3565b6040518963ffffffff1660e01b815260040161188e989796959493929190615085565b6020604051808303815f875af11580156118aa573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118ce91906150db565b5f818152600960205260409020549091506001600160a01b0316818161190a576040516381c4951960e01b8152600401610bfb91815260200190565b505f828152600a60205260409020546001600160a01b03168281611944576040516381c4951960e01b8152600401610bfb91815260200190565b50608088018390526001600160a01b038083166101008a015281166101208901525f8981526008602090815260409091208951815590890151600180830180548c94939260ff1991909116908360038111156119a2576119a2614883565b0217905550604082015181600201556060820151816003019060026119c892919061448b565b506080820151600582015560a08201516006820180546001600160a01b0319166001600160a01b0390921691909117905560c08201516007820190611a0d9082615151565b5060e08201516008820190611a229082615151565b506101008201516009820180546001600160a01b039283166001600160a01b031991821617909155610120840151600a84018054919093169116179055610140820151600b820155610160820151600c820155610180820151600d820190611a8a9082615151565b506101a0820151600e90910180546101c0909301511515600160a01b026001600160a81b03199093166001600160a01b0392831617929092179091555f5460405163291a691b60e01b815291169063291a691b90611af0908c9088908c90600401615206565b6020604051808303815f875af1158015611b0c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b30919061524b565b611b4d57604051630d8dbe2560e01b815260040160405180910390fd5b611b5d60808b0160608c016147d9565b6001600160a01b03167f5904e83d57e704fba4d92c5617f6b50ae993cd42d78babfcb239d0a6775f30708a8a604051611b97929190614b69565b60405180910390a2885f516020615ad85f395f51905f525f6001604051611bbf929190615266565b60405180910390a250505050505050915091565b611bdb613916565b611be45f6139b5565b565b611bee613916565b80515f5b81811015611c4257600b838281518110611c0e57611c0e614f7f565b6020026020010151604051611c239190615281565b908152604051908190036020019020805460ff19169055600101611bf2565b507fd1b46e030b48add7bc03225cc5a6f403970976b36983f99ec31d535d627fc7db82604051611c729190615297565b60405180910390a15050565b611c86613916565b6001600160a01b03811615801590611cb757505f828152600a60205260409020546001600160a01b03828116911614155b8290611cd9576040516381c4951960e01b8152600401610bfb91815260200190565b505f918252600a602052604090912080546001600160a01b0319166001600160a01b03909216919091179055565b5f546001600160a01b03163314611d315760405163b56831db60e01b815260040160405180910390fd5b5f818152600d602052604090205460ff166001816006811115611d5657611d56614883565b14611d7b57816001826040516337e1404160e01b8152600401610bfb939291906152fa565b5f828152600d60205260409020805460ff19166002179055601554611da09042614fc8565b5f838152600e602052604080822092909255905183917fc44405af9078047712501f519e1fb900c2896c62b488336f84529c72ae16e6f191a2815f516020615ad85f395f51905f5260016002604051611dfa929190615266565b60405180910390a25050565b5f546001600160a01b03163314611e305760405163b56831db60e01b815260040160405180910390fd5b5f828152600860209081526040808320600d9092529091205460ff166002816006811115611e6057611e60614883565b14611e8557836002826040516337e1404160e01b8152600401610bfb939291906152fa565b5f848152600d6020526040808220805460ff19166003179055600b84018590555185917f11df18edb9bc9cd90a79068e0e208b630202148643d797d6150e7bacb733e63c91a2835f516020615ad85f395f51905f5260026003604051611eec929190615266565b60405180910390a250505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b611f4960405180606001604052805f81526020015f81526020015f81525090565b5060408051606081018252601554815260165460208201526017549181019190915290565b5f818152600d602052604090205460ff166006816006811115611f9357611f93614883565b148290611fb657604051637cb2d48360e11b8152600401610bfb91815260200190565b505f828152600c60205260409020548281611fe7576040516345ba89d560e11b8152600401610bfb91815260200190565b505f838152600c6020526040812081905561200184613a25565b5f858152601160205260409020546002549192506001600160a01b039081169161202e9183911685613b12565b60025460405163da19b69760e01b81526001600160a01b039091169063da19b6979061206490889087908790879060040161535e565b5f604051808303815f87803b15801561207b575f5ffd5b505af115801561208d573d5f5f3e3d5ffd5b50505050847f5297818f48a66292b8b3e2caab83eec531b669bb20807fd38cf006adb2a073178484516040516120cd929190918252602082015260400190565b60405180910390a25050505050565b6120fd60405180606001604052805f81526020015f81526020015f81525090565b505f908152600e6020908152604091829020825160608101845281548152600182015492810192909252600201549181019190915290565b5f818152600d6020526040812054819060ff166121528482613b3d565b9250925050915091565b5f5f61216787610f6c565b5f888152600d602052604090205490915060ff16600381600681111561218f5761218f614883565b14886003839091926121b7576040516337e1404160e01b8152600401610bfb939291906152fa565b5050505f888152600e602090815260409182902082516060810184528154815260018201549281018390526002909101549281019290925289904281101561221b576040516308f3034360e31b815260048101929092526024820152604401610bfb565b50506060830151602001518990428111156122525760405163017e35e560e71b815260048101929092526024820152604401610bfb565b505061016083015189901561227d57604051637eb9cea960e11b8152600401610bfb91815260200190565b505f888860405161228f929190615395565b60408051918290039091205f8c815260086020908152838220600c01839055600d905291909120805460ff191660041790556017549091506122d19042614fc8565b5f8b8152600e6020526040908190206002019190915560a08501519051632f0e1bbf60e01b81526001600160a01b0390911690632f0e1bbf9061231e908d9085908c908c906004016153a4565b6020604051808303815f875af115801561233a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061235e919061524b565b945088888661238257604051632f9f8ab960e01b8152600401610bfb9291906153c3565b5050897f7cc27e4a5626cbc4f8ba1a927b0448de55e6a114bc87660331270c5109ade0718a8a6040516123b69291906153c3565b60405180910390a2895f516020615ad85f395f51905f52600360046040516123df929190615266565b60405180910390a25050505095945050505050565b5f5f6123ff89610f6c565b5f8a8152600d602052604090205490915060ff16600481600681111561242757612427614883565b148a60048390919261244f576040516337e1404160e01b8152600401610bfb939291906152fa565b5050505f8a8152600e602090815260409182902082516060810184528154815260018201549281019290925260020154918101829052908b90428110156124b2576040516308f3034360e31b815260048101929092526024820152604401610bfb565b50505f8b8152600860205260409020600d016124cf8a8c836153d6565b505f8b8152600d6020526040902080546005919060ff191660018302179055508261010001516001600160a01b0316635bf48e3a8b8b604051612513929190615395565b6040519081900381206001600160e01b031960e084901b168252612541918c908c908c908c9060040161548a565b602060405180830381865afa15801561255c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612580919061524b565b93508989856125a457604051632f9f8ab960e01b8152600401610bfb9291906153c3565b50506125af8b613cc5565b8a7f3a140076c461ebc41d74833ae0ee8bbc8079a135a63392098cd381e84350b69b8b8b8b8b6040516125e594939291906154c2565b60405180910390a28a5f516020615ad85f395f51905f526004600560405161260e929190615266565b60405180910390a2505050979650505050505050565b61262c613916565b80515f5b8181101561268b576001600b84838151811061264e5761264e614f7f565b60200260200101516040516126639190615281565b908152604051908190036020019020805491151560ff19909216919091179055600101612630565b507f027b83cad653f54850fef6faa8c705f73a53e7f8de50a3a33ac1e0e3a5c0be8182604051611c729190615297565b5f546001600160a01b03163314806126dd57506003546001600160a01b031633145b6126fa57604051639e75a8b560e01b815260040160405180910390fd5b5f8160ff161180156127105750600d60ff821611155b6127555760405162461bcd60e51b815260206004820152601660248201527524b73b30b634b2103330b4b63ab932903932b0b9b7b760511b6044820152606401610bfb565b612773828260ff16600d81111561276e5761276e614883565b614131565b5050565b61277f613916565b6001600160a01b0381166127d55760405162461bcd60e51b815260206004820152601f60248201527f496e76616c696420536c617368696e674d616e616765722061646472657373006044820152606401610bfb565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4ccc8ed483c7c44c3602c3c38afc2c014a8f1d2dc210dfe58ebeeeead230f8e0905f90a250565b6003546001600160a01b03163314612849576040516357d6948d60e11b815260040160405180910390fd5b60025460405163c1ab0f1f60e01b815260048101849052602481018390526001600160a01b039091169063c1ab0f1f906044015f604051808303815f87803b158015612893575f5ffd5b505af11580156128a5573d5f5f3e3d5ffd5b50505050817f4f41a3b0a032ebcae925f2ace77d507435840ca4b2dbaffdd7723fa8d72ee54282604051611dfa91815260200190565b6128e3613916565b6001600160a01b0381161580159061291457505f828152600960205260409020546001600160a01b03828116911614155b8290612936576040516381c4951960e01b8152600401610bfb91815260200190565b505f8281526009602090815260409182902080546001600160a01b0319166001600160a01b03851617905590518381527ff4041a3f914dac3bc9bf5f003ba41f28dbb84abe42f4e07c76266f5c8ceecb699101611c72565b612996613916565b60058190556040518181527fba0716ba1ee2ea8ecc4c64119b4537cdb42a99d82acf92af5b87607b8b52355290602001610d23565b6129d3613916565b6127106129e86101208301610100840161550d565b61ffff161115612a006101208301610100840161550d565b90612a25576040516301027fc160e21b815261ffff9091166004820152602401610bfb565b50612710612a3b6101408301610120840161550d565b61ffff161115612a536101408301610120840161550d565b90612a78576040516301027fc160e21b815261ffff9091166004820152602401610bfb565b50612710612a8e6101608301610140840161550d565b61ffff161115612aa66101608301610140840161550d565b90612acb57604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612710612ae16101808301610160840161550d565b61ffff161115612af96101808301610160840161550d565b90612b1e57604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612710612b346101a08301610180840161550d565b61ffff161115612b4c6101a08301610180840161550d565b90612b7157604051633239953960e01b815261ffff9091166004820152602401610bfb565b50612b846101408201610120830161550d565b61ffff161580612bad57505f612ba1610100830160e084016147d9565b6001600160a01b031614155b612bca5760405163015f92ff60e51b815260040160405180910390fd5b612bdc6101e082016101c08301615544565b63ffffffff16612bf46101c083016101a08401615544565b63ffffffff161015612c19576040516392f55c6560e01b815260040160405180910390fd5b806018612c268282615583565b9050507fbf3951313e980027eb48ce363fdb707286195ec6a0f802ac153927cf929c3fc681604051610d239190615741565b5f80601281612c6a6020860186614f66565b6003811115612c7b57612c7b614883565b6003811115612c8c57612c8c614883565b8152602081019190915260409081015f20815180830190925260028282826020028201915f905b82829054906101000a900463ffffffff1663ffffffff1681526020019060040190602082600301049283019260010382029150808411612cb3579050505050505090505f81600160028110612d0a57612d0a614f7f565b602002015163ffffffff1611835f016020810190612d289190614f66565b90612d475760405163286c068d60e11b8152600401610bfb9190614f93565b506020808201518251604080516101e081018252601854815260195481860152601a5491810191909152601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e0830152600160a01b810461ffff908116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152925463ffffffff8181166101a0860181905264010000000090920481166101c086015292831693919092169115612e72576101a081015163ffffffff16846001602002015163ffffffff161015865f016020810190612e519190614f66565b90612e705760405163010b971d60e31b8152600401610bfb9190614f93565b505b6101c081015163ffffffff1615612ec1576101c081015184519063ffffffff9081169082161015612ebf57604051630a4b6b6360e11b815263ffffffff9091166004820152602401610bfb565b505b60408601356020870135811015612eee5760405163174b5a0760e21b8152600401610bfb91815260200190565b506101808101516017545f9161271091612f0c9161ffff169061584b565b612f169190615862565b61271061ffff1683610160015161ffff16601560010154612f37919061584b565b612f419190615862565b61271061ffff1684610140015161ffff1660155f0154612f61919061584b565b612f6b9190615862565b5f5460408051634f87c3a560e11b8152815160208e81013594938f0135936001600160a01b031692639f0f874a92600480830193928290030181865afa158015612fb7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fdb91906150db565b612fe59190614fc8565b612fef9190614fb5565b612ff99190614fc8565b6130039190614fc8565b61300d9190614fc8565b90505f61301b600186614fb5565b61302690600261584b565b61303190600261584b565b61303c906006614fc8565b90505f85845f015161304e919061584b565b905081868560200151613061919061584b565b61306b919061584b565b6130759082614fc8565b905060018611156130bd57600261308d600188614fb5565b613097908861584b565b85604001516130a6919061584b565b6130b09190615862565b6130ba9082614fc8565b90505b81868560c001516130ce919061584b565b6130d8919061584b565b6130e29082614fc8565b9050828685606001516130f5919061584b565b6130ff919061584b565b6131099082614fc8565b905084846080015161311b919061584b565b6131259082614fc8565b9050600185111561316d57600261313d600187614fb5565b613147908761584b565b8560400151613156919061584b565b6131609190615862565b61316a9082614fc8565b90505b60a084015161317c9082614fc8565b610100850151909150612710906131979061ffff1682614fc8565b6131a1908361584b565b6131ab9190615862565b975087806131cf57604051638c4fcd9360e01b8152600401610bfb91815260200190565b5050505050505050919050565b6131e4613916565b6001600160a01b0381161580159061320a57506001546001600160a01b03828116911614155b819061322a576040516320252f0b60e01b8152600401610bfb919061481f565b50600180546001600160a01b0319166001600160a01b0383161790556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a790610d2390839061481f565b61327e613916565b61328b6020820182615544565b63ffffffff166132a16040830160208401615544565b63ffffffff16101580156132c657505f6132be6020830183615544565b63ffffffff16115b6132e357604051634564ab9b60e01b815260040160405180910390fd5b604080516101e0810182526018548152601954602080830191909152601a5492820192909252601b546060820152601c546080820152601d5460a0820152601e5460c0820152601f546001600160a01b03811660e083015261ffff600160a01b82048116610100840152600160b01b82048116610120840152600160c01b82048116610140840152600160d01b82048116610160840152600160e01b90910416610180820152905463ffffffff8082166101a08401819052640100000000909204166101c08301521561341c576101a081015163ffffffff166133cc6040840160208501615544565b63ffffffff1610156133e46040840160208501615544565b826101a00151909161341957604051633ccc4c2160e21b815263ffffffff928316600482015291166024820152604401610bfb565b50505b6101c081015163ffffffff1615613493576101c081015163ffffffff166134466020840184615544565b63ffffffff16101561345b6020840184615544565b826101c0015190916134905760405163156c4e5b60e11b815263ffffffff928316600482015291166024820152604401610bfb565b50505b8160125f8560038111156134a9576134a9614883565b60038111156134ba576134ba614883565b815260208101919091526040015f206134d49160026144c9565b508260038111156134e7576134e7614883565b7f8b56fae526eee054f0849759a99fc7d4ff3823824ebf097a56f7d78adb6b34fa836040516135169190615881565b60405180910390a2505050565b61352b613916565b6001600160a01b038116613554575f604051631e4fbdf760e01b8152600401610bfb919061481f565b610d3f816139b5565b5f818152600d602052604081205460ff168181600681111561358157613581614883565b036135a657826001826040516337e1404160e01b8152600401610bfb939291906152fa565b60058160068111156135ba576135ba614883565b036135db5760405163462c7bed60e01b815260048101849052602401610bfb565b60068160068111156135ef576135ef614883565b0361361057604051633de16e3560e11b815260048101849052602401610bfb565b5f61361b8483613b3d565b935090508061364057604051639f65d93560e01b815260048101859052602401610bfb565b5f848152600d6020526040902080546006919060ff191660018302179055505f848152600f60205260409020805484919060ff1916600183600d81111561368957613689614883565b0217905550835f516020615ad85f395f51905f528360066040516136ae929190615266565b60405180910390a2837fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb83856040516136e89291906158c1565b60405180910390a25050919050565b6136ff613916565b6001600160a01b0381161580159061372457505f546001600160a01b03828116911614155b8190613744576040516375ac4eb760e11b8152600401610bfb919061481f565b505f80546001600160a01b0319166001600160a01b0383161790556040517f80052b810d39120cf6c976cca504a21703f585521dc7a41c6d241090e6c579b690610d2390839061481f565b6001600160a01b0381165f90815260076020526040902054819060ff16156137cb5760405163b29d459560e01b8152600401610bfb919061481f565b506001600160a01b0381165f9081526007602052604090819020805460ff19166001179055517fb8d368517268f297fff00825d67d098763117d061360d31027be5b2e1a59d46790610d2390839061481f565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005b92915050565b61385061428c565b610d3f816142b1565b80356138785760405163055f269d60e01b815260040160405180910390fd5b5f81602001351161389c5760405163055f269d60e01b815260040160405180910390fd5b5f8160400135116138c05760405163055f269d60e01b815260040160405180910390fd5b80356015819055602080830135601681905560408085013560178190558151948552928401919091528201527f7e86ba16b805e2835af5c5b7aa5a942ced8bcc1fb95a05fbe42dae3862350a1690606001610d23565b3361391f611efa565b6001600160a01b031614611be4573360405163118cdaa760e01b8152600401610bfb919061481f565b6040516001600160a01b0384811660248301528381166044830152606482018390526139af9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506142b9565b50505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f818152600f602052604090205460609060ff16600181600d811115613a4d57613a4d614883565b1480613a6a5750600281600d811115613a6857613a68614883565b145b15613aa2575f5b604051908082528060200260200182016040528015613a9a578160200160208202803683370190505b509392505050565b5f5460405162beb08960e51b8152600481018590526001600160a01b03909116906317d61120906024015f60405180830381865afa925050508015613b0857506040513d5f823e601f3d908101601f19168201604052613b059190810190615937565b60015b613a9a575f613a71565b613b3883846001600160a01b031663a9059cbb858560405160240161397d9291906159fc565b505050565b5f828152600e60209081526040808320815160608101835281548152600182015493810193909352600201548282015282549051632800d82960e01b81526004810186905283929183916001600160a01b0390911690632800d82990602401602060405180830381865afa158015613bb7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bdb91906150db565b90506001856006811115613bf157613bf1614883565b148015613bfd57508042115b15613c1057600180935093505050613cbe565b6002856006811115613c2457613c24614883565b148015613c315750815142115b15613c455760016003935093505050613cbe565b6003856006811115613c5957613c59614883565b148015613c695750816020015142115b15613c7d5760016006935093505050613cbe565b6004856006811115613c9157613c91614883565b148015613ca15750816040015142115b15613cb5576001600a935093505050613cbe565b5f5f9350935050505b9250929050565b5f805460405162beb08960e51b8152600481018490526001600160a01b03909116906317d61120906024015f60405180830381865afa158015613d0a573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613d319190810190615937565b5080515f848152600c60209081526040808320805490849055601190925282205493945091926001600160a01b031690829003613dd0576002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613d9c90889088908690600401615a15565b5f604051808303815f87803b158015613db3575f5ffd5b505af1158015613dc5573d5f5f3e3d5ffd5b505050505050505050565b825f03613e71575f858152601060205260409020546001600160a01b03168015613e0857613e086001600160a01b0383168285613b12565b6002546040516341489f1560e01b81526001600160a01b03909116906341489f1590613e3c90899089908790600401615a15565b5f604051808303815f87803b158015613e53575f5ffd5b505af1158015613e65573d5f5f3e3d5ffd5b50505050505050505050565b5f85815260136020908152604080832054601490925282205461ffff909116906001600160a01b03168115801590613eb157506001600160a01b03811615155b15613eed57612710613ec761ffff84168761584b565b613ed19190615862565b92508215613eed57613eed6001600160a01b0385168285613b12565b5f613ef88487614fb5565b90505f876001600160401b03811115613f1357613f136145cb565b604051908082528060200260200182016040528015613f3c578160200160208202803683370190505b5090505f613f4a8984615862565b90505f805b8a811015613f895782848281518110613f6a57613f6a614f7f565b6020908102919091010152613f7f8383614fc8565b9150600101613f4f565b505f613f958286614fb5565b90508015613fd2578084613faa60018e614fb5565b81518110613fba57613fba614f7f565b60200260200101818151613fce9190614fc8565b9052505b600154613fec906001600160a01b038b811691168761431c565b60015f9054906101000a90046001600160a01b03166001600160a01b031663dd8c818e8a8e876040518463ffffffff1660e01b815260040161403093929190615a75565b5f604051808303815f87803b158015614047575f5ffd5b505af1158015614059573d5f5f3e3d5ffd5b505060015461407792506001600160a01b038c81169250165f61431c565b8c7fac9fe8ad7f55eac03284399116ecafc104f10459773f4cdf47063c46e5be335a8d866040516140a9929190615aaa565b60405180910390a260025f9054906101000a90046001600160a01b03166001600160a01b03166341489f158e8e8c6040518463ffffffff1660e01b81526004016140f593929190615a15565b5f604051808303815f87803b15801561410c575f5ffd5b505af115801561411e573d5f5f3e3d5ffd5b5050505050505050505050505050505050565b5f828152600d602052604081205460ff169081600681111561415557614155614883565b0361417a57826001826040516337e1404160e01b8152600401610bfb939291906152fa565b600581600681111561418e5761418e614883565b036141af5760405163462c7bed60e01b815260048101849052602401610bfb565b60068160068111156141c3576141c3614883565b036141e457604051633de16e3560e11b815260048101849052602401610bfb565b5f838152600d6020526040902080546006919060ff191660018302179055505f838152600f60205260409020805483919060ff1916600183600d81111561422d5761422d614883565b0217905550825f516020615ad85f395f51905f52826006604051614252929190615266565b60405180910390a2827fe20209be7caae6e76291267cfa711353981274bf127e94f16eb9ec44b68582bb82846040516135169291906158c1565b6142946143ac565b611be457604051631afcd79f60e31b815260040160405180910390fd5b61352b61428c565b5f5f60205f8451602086015f885af1806142d8576040513d5f823e3d81fd5b50505f513d915081156142ef5780600114156142fc565b6001600160a01b0384163b155b156139af5783604051635274afe760e01b8152600401610bfb919061481f565b5f836001600160a01b031663095ea7b3848460405160240161433f9291906159fc565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050905061437884826143c5565b6139af576143a284856001600160a01b031663095ea7b3865f60405160240161397d9291906159fc565b6139af84826142b9565b5f6143b561381e565b54600160401b900460ff16919050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f519050828015614404575081156143f65780600114614404565b5f866001600160a01b03163b115b9695505050505050565b604080516101e081019091525f808252602082019081526020015f8152602001614436614565565b81525f602082018190526040820181905260608083018190526080830181905260a0830182905260c0830182905260e08301829052610100830182905261012083015261014082018190526101609091015290565b82600281019282156144b9579160200282015b828111156144b957825182559160200191906001019061449e565b506144c5929150614583565b5090565b6001830191839082156144b9579160200282015f5b8382111561452857833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026144de565b80156145585782816101000a81549063ffffffff0219169055600401602081600301049283019260010302614528565b50506144c5929150614583565b60405180604001604052806002906020820280368337509192915050565b5b808211156144c5575f8155600101614584565b6001600160a01b0381168114610d3f575f5ffd5b80356145b681614597565b919050565b5f60608284031215611263575f5ffd5b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715614607576146076145cb565b604052919050565b5f6001600160401b03821115614627576146276145cb565b5060051b60200190565b5f82601f830112614640575f5ffd5b81356001600160401b03811115614659576146596145cb565b61466c601f8201601f19166020016145df565b818152846020838601011115614680575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f82601f8301126146ab575f5ffd5b81356146be6146b98261460f565b6145df565b8082825260208201915060208360051b8601019250858311156146df575f5ffd5b602085015b8381101561471f5780356001600160401b03811115614701575f5ffd5b614710886020838a0101614631565b845250602092830192016146e4565b5095945050505050565b5f5f5f5f5f5f5f5f610140898b031215614741575f5ffd5b883561474c81614597565b9750602089013561475c81614597565b9650604089013561476c81614597565b9550606089013561477c81614597565b9450608089013561478c81614597565b935060a089013592506147a28a60c08b016145bb565b91506101208901356001600160401b038111156147bd575f5ffd5b6147c98b828c0161469c565b9150509295985092959890939650565b5f602082840312156147e9575f5ffd5b81356147f481614597565b9392505050565b5f6020828403121561480b575f5ffd5b5035919050565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b8035600481106145b6575f5ffd5b5f5f60408385031215614852575f5ffd5b61485b83614833565b946020939093013593505050565b5f60608284031215614879575f5ffd5b6147f483836145bb565b634e487b7160e01b5f52602160045260245ffd5b600e81106148a7576148a7614883565b9052565b602081016138428284614897565b600481106148a7576148a7614883565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8e8152614907602082018f6148b9565b8c60408201528b6060820152614920608082018c614812565b6101c060a08201525f6149376101c083018c6148c9565b82810360c0840152614949818c6148c9565b905061495860e084018b614812565b61496661010084018a614812565b876101208401528661014084015282810361016084015261498781876148c9565b915050614998610180830185614812565b8215156101a08301529f9e505050505050505050505050505050565b805f5b60028110156139af5781518452602093840193909101906001016149b7565b805182525f60208201516149ed60208501826148b9565b50604082015160408401526060820151614a0a60608501826149b4565b50608082015160a084015260a0820151614a2760c0850182614812565b5060c082015161020060e0850152614a436102008501826148c9565b905060e0830151848203610100860152614a5d82826148c9565b915050610100830151614a74610120860182614812565b50610120830151614a89610140860182614812565b506101408301516101608501526101608301516101808501526101808301518482036101a0860152614abb82826148c9565b9150506101a0830151614ad26101c0860182614812565b506101c08301518015156101e0860152613a9a565b602081525f6147f460208301846149d6565b5f60208284031215614b09575f5ffd5b81356001600160401b03811115614b1e575f5ffd5b614b2a84828501614631565b949350505050565b5f60208284031215614b42575f5ffd5b81356001600160401b03811115614b57575f5ffd5b820161010081850312156147f4575f5ffd5b828152604060208201525f614b2a60408301846149d6565b5f60208284031215614b91575f5ffd5b81356001600160401b03811115614ba6575f5ffd5b614b2a8482850161469c565b5f5f60408385031215614bc3575f5ffd5b823591506020830135614bd581614597565b809150509250929050565b5f5f60408385031215614bf1575f5ffd5b50508035926020909101359150565b81518152602080830151908201526040808301519082015260608101613842565b8215158152604081016147f46020830184614897565b5f5f83601f840112614c47575f5ffd5b5081356001600160401b03811115614c5d575f5ffd5b602083019150836020828501011115613cbe575f5ffd5b5f5f5f5f5f60608688031215614c88575f5ffd5b8535945060208601356001600160401b03811115614ca4575f5ffd5b614cb088828901614c37565b90955093505060408601356001600160401b03811115614cce575f5ffd5b614cda88828901614c37565b969995985093965092949392505050565b5f6101e082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e0830151614d4560e0840182614812565b50610100830151614d5d61010084018261ffff169052565b50610120830151614d7561012084018261ffff169052565b50610140830151614d8d61014084018261ffff169052565b50610160830151614da561016084018261ffff169052565b50610180830151614dbd61018084018261ffff169052565b506101a0830151614dd76101a084018263ffffffff169052565b506101c0830151614df16101c084018263ffffffff169052565b5092915050565b5f5f5f5f5f5f5f6080888a031215614e0e575f5ffd5b8735965060208801356001600160401b03811115614e2a575f5ffd5b614e368a828b01614c37565b90975095505060408801356001600160401b03811115614e54575f5ffd5b614e608a828b01614c37565b90955093505060608801356001600160401b03811115614e7e575f5ffd5b614e8a8a828b01614c37565b989b979a50959850939692959293505050565b5f5f60408385031215614eae575f5ffd5b82359150602083013560ff81168114614bd5575f5ffd5b600781106148a7576148a7614883565b602081016138428284614ec5565b5f6101e0828403128015614ef5575f5ffd5b509092915050565b5f5f60608385031215614f0e575f5ffd5b614f1783614833565b915083606084011115614f28575f5ffd5b50926020919091019150565b600181811c90821680614f4857607f821691505b60208210810361126357634e487b7160e01b5f52602260045260245ffd5b5f60208284031215614f76575f5ffd5b6147f482614833565b634e487b7160e01b5f52603260045260245ffd5b6020810161384282846148b9565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561384257613842614fa1565b8082018082111561384257613842614fa1565b5f60018201614fec57614fec614fa1565b5060010190565b5f5f8335601e19843603018112615008575f5ffd5b8301803591506001600160401b03821115615021575f5ffd5b602001915036819003821315613cbe575f5ffd5b8015158114610d3f575f5ffd5b5f60208284031215615052575f5ffd5b81356147f481615035565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b88815287602082015260a060408201525f6150a460a08301888a61505d565b82810360608401526150b781878961505d565b905082810360808401526150cc81858761505d565b9b9a5050505050505050505050565b5f602082840312156150eb575f5ffd5b5051919050565b601f821115613b3857805f5260205f20601f840160051c810160208510156151175750805b601f840160051c820191505b81811015615136575f8155600101615123565b5050505050565b5f19600383901b1c191660019190911b1790565b81516001600160401b0381111561516a5761516a6145cb565b61517e816151788454614f34565b846150f2565b6020601f8211600181146151ab575f83156151995750848201515b6151a3848261513d565b855550615136565b5f84815260208120601f198516915b828110156151da57878501518255602094850194600190920191016151ba565b50848210156151f757868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b838152602081018390526080810160408201835f5b600281101561524057815163ffffffff1683526020928301929091019060010161521b565b505050949350505050565b5f6020828403121561525b575f5ffd5b81516147f481615035565b604081016152748285614ec5565b6147f46020830184614ec5565b5f82518060208501845e5f920191825250919050565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b828110156152ee57603f198786030184526152d98583516148c9565b945060209384019391909101906001016152bd565b50929695505050505050565b8381526060810161530e6020830185614ec5565b614b2a6040830184614ec5565b5f8151808452602084019350602083015f5b828110156153545781516001600160a01b031686526020958601959091019060010161532d565b5093949350505050565b848152836020820152608060408201525f61537c608083018561531b565b905060018060a01b038316606083015295945050505050565b818382375f9101908152919050565b848152836020820152606060408201525f61440460608301848661505d565b602081525f614b2a60208301848661505d565b6001600160401b038311156153ed576153ed6145cb565b615401836153fb8354614f34565b836150f2565b5f601f84116001811461542d575f851561541b5750838201355b615425868261513d565b845550615136565b5f83815260208120601f198716915b8281101561545c578685013582556020948501946001909201910161543c565b5086821015615478575f1960f88860031b161c19848701351681555b505060018560011b0183555050505050565b858152606060208201525f6154a360608301868861505d565b82810360408401526154b681858761505d565b98975050505050505050565b604081525f6154d560408301868861505d565b82810360208401526154e881858761505d565b979650505050505050565b61ffff81168114610d3f575f5ffd5b80356145b6816154f3565b5f6020828403121561551d575f5ffd5b81356147f4816154f3565b63ffffffff81168114610d3f575f5ffd5b80356145b681615528565b5f60208284031215615554575f5ffd5b81356147f481615528565b5f813561384281614597565b5f8135613842816154f3565b5f813561384281615528565b813581556020820135600182015560408201356002820155606082013560038201556080820135600482015560a0820135600582015560c08201356006820155600781016155f36155d660e0850161555f565b82546001600160a01b0319166001600160a01b0391909116178255565b615623615603610100850161556b565b82805461ffff60a01b191660a09290921b61ffff60a01b16919091179055565b615653615633610120850161556b565b82805461ffff60b01b191660b09290921b61ffff60b01b16919091179055565b615683615663610140850161556b565b82805461ffff60c01b191660c09290921b61ffff60c01b16919091179055565b6156b3615693610160850161556b565b82805461ffff60d01b191660d09290921b61ffff60d01b16919091179055565b6156e36156c3610180850161556b565b82805461ffff60e01b191660e09290921b61ffff60e01b16919091179055565b506008810161570f6156f86101a08501615577565b825463ffffffff191663ffffffff91909116178255565b613b3861571f6101c08501615577565b825467ffffffff00000000191660209190911b67ffffffff0000000016178255565b813581526020808301359082015260408083013590820152606080830135908201526080808301359082015260a0808301359082015260c080830135908201526101e0810161579260e084016145ab565b61579f60e0840182614812565b506157ad6101008401615502565b61ffff166101008301526157c46101208401615502565b61ffff166101208301526157db6101408401615502565b61ffff166101408301526157f26101608401615502565b61ffff166101608301526158096101808401615502565b61ffff166101808301526158206101a08401615539565b63ffffffff166101a08301526158396101c08401615539565b63ffffffff81166101c0840152614df1565b808202811582820484141761384257613842614fa1565b5f8261587c57634e487b7160e01b5f52601260045260245ffd5b500490565b6040810181835f5b60028110156158b857813561589d81615528565b63ffffffff1683526020928301929190910190600101615889565b50505092915050565b604081016158cf8285614ec5565b6147f46020830184614897565b5f82601f8301126158eb575f5ffd5b81516158f96146b98261460f565b8082825260208201915060208360051b86010192508583111561591a575f5ffd5b602085015b8381101561471f57805183526020928301920161591f565b5f5f60408385031215615948575f5ffd5b82516001600160401b0381111561595d575f5ffd5b8301601f8101851361596d575f5ffd5b805161597b6146b98261460f565b8082825260208201915060208360051b85010192508783111561599c575f5ffd5b6020840193505b828410156159c75783516159b681614597565b8252602093840193909101906159a3565b8095505050505060208301516001600160401b038111156159e6575f5ffd5b6159f2858286016158dc565b9150509250929050565b6001600160a01b03929092168252602082015260400190565b838152606060208201525f615a2d606083018561531b565b905060018060a01b0383166040830152949350505050565b5f8151808452602084019350602083015f5b82811015615354578151865260209586019590910190600101615a57565b6001600160a01b03841681526060602082018190525f90615a989083018561531b565b82810360408401526144048185615a45565b604081525f615abc604083018561531b565b8281036020840152615ace8185615a45565b9594505050505056fe1b418a230a21d37a078bf8f16decbde8ccceacd77159371f62f0d4ea00d19967a164736f6c634300081c000a", "linkReferences": {}, "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/Enclave.sol", - "buildInfoId": "solc-0_8_28-d3db7173950582d145915864c28ba15e975a7c98" + "buildInfoId": "solc-0_8_28-361d4ed525b2a26ac9b3313fd80fb3038fddef8a" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json index 5300c1d0ae..185e63b330 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json @@ -940,5 +940,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IBondingRegistry.sol", - "buildInfoId": "solc-0_8_28-d3db7173950582d145915864c28ba15e975a7c98" + "buildInfoId": "solc-0_8_28-67d7bf420ed8ac25001394841682e83cf4523ea2" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json index 7563739e37..4e550a8b39 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json @@ -568,6 +568,11 @@ "internalType": "address[]", "name": "nodes", "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "scores", + "type": "uint256[]" } ], "stateMutability": "view", @@ -960,5 +965,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ICiphernodeRegistry.sol", - "buildInfoId": "solc-0_8_28-d3db7173950582d145915864c28ba15e975a7c98" + "buildInfoId": "solc-0_8_28-67d7bf420ed8ac25001394841682e83cf4523ea2" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json index 15f9047249..fa7cc31b58 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json @@ -2093,5 +2093,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/IEnclave.sol", - "buildInfoId": "solc-0_8_28-d3db7173950582d145915864c28ba15e975a7c98" + "buildInfoId": "solc-0_8_28-67d7bf420ed8ac25001394841682e83cf4523ea2" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json index 480e8ce6c7..ae582e7c29 100644 --- a/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json +++ b/packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json @@ -954,5 +954,5 @@ "deployedLinkReferences": {}, "immutableReferences": {}, "inputSourceName": "project/contracts/interfaces/ISlashingManager.sol", - "buildInfoId": "solc-0_8_28-d3db7173950582d145915864c28ba15e975a7c98" + "buildInfoId": "solc-0_8_28-67d7bf420ed8ac25001394841682e83cf4523ea2" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json index c9b682e2dc..6a70931d0a 100644 --- a/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json +++ b/packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json @@ -743,8 +743,13 @@ "outputs": [ { "internalType": "address[]", - "name": "", + "name": "nodes", "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "scores", + "type": "uint256[]" } ], "stateMutability": "view", @@ -1259,30 +1264,30 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b613e2d806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061023f575f3560e01c80639f0f874a11610135578063e59e4695116100b4578063f26ef74e11610079578063f26ef74e14610571578063f2fde38b14610584578063f379b0df14610597578063f52fd803146105d1578063f6fc05d514610643575f5ffd5b8063e59e469514610516578063e6745e1314610529578063e82f3b701461053c578063ebf0c7171461054f578063f165053614610557575f5ffd5b8063c3a0ec30116100fa578063c3a0ec30146104ae578063ca2869a0146104bf578063cd6dc687146104de578063da881e5a146104f1578063dbb06c9314610504575f5ffd5b80639f0f874a1461044d578063a016493014610456578063a8a4d69b14610469578063bff232c11461047c578063c2b40ae41461048f575f5ffd5b8063715018a6116101c15780638d1ddfb1116101865780638d1ddfb1146103cd5780638da5cb5b146103e35780638e5ce3ad146103eb5780639015d371146103fe5780639a7a2ffc14610411575f5ffd5b8063715018a6146103465780637c92f5241461034e578063858142431461037b5780638a78bb151461039b5780638cb89ecb146103ae575f5ffd5b80632e7b716d116102075780632e7b716d146102d85780634d6861a6146102eb57806350e6d94c146102fe5780635d5047761461032057806370e36bbe14610333575f5ffd5b8063096b810a146102435780630f3e34121461025857806317d611201461026b5780632800d82914610294578063291a691b146102b5575b5f5ffd5b61025661025136600461323c565b61064c565b005b610256610266366004613257565b610798565b61027e610279366004613257565b6107db565b60405161028b919061326e565b60405180910390f35b6102a76102a2366004613257565b61091d565b60405190815260200161028b565b6102c86102c33660046132b9565b610969565b604051901515815260200161028b565b6102c86102e636600461323c565b610b43565b6102c86102f9366004613257565b610bf6565b6102c861030c36600461323c565b60066020525f908152604090205460ff1681565b6102c861032e3660046132f2565b610c35565b61025661034136600461323c565b610c79565b610256610cef565b61036161035c366004613320565b610d02565b6040805192835263ffffffff90911660208301520161028b565b60015461038e906001600160a01b031681565b60405161028b9190613355565b6102566103a936600461323c565b610ea9565b6102a76103bc366004613257565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102a7565b61038e610fe7565b600b5461038e906001600160a01b031681565b6102c861040c36600461323c565b611015565b61043761041f36600461323c565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161028b565b6102a760035481565b61027e610464366004613257565b611032565b6102c86104773660046132f2565b6110c8565b61025661048a36600461323c565b61110c565b6102a761049d366004613257565b60086020525f908152604090205481565b6001546001600160a01b031661038e565b6102a76104cd366004613257565b5f9081526008602052604090205490565b6102566104ec366004613369565b61115d565b6102c86104ff366004613257565b6112ba565b5f5461038e906001600160a01b031681565b61025661052436600461323c565b611594565b610256610537366004613393565b61160c565b6102a761054a366004613257565b6117cf565b6102a7611800565b61055f601481565b60405160ff909116815260200161028b565b61025661057f3660046133f7565b611812565b61025661059236600461323c565b611b0c565b6004546105b39064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161028b565b6106146105df366004613257565b5f908152600a6020819052604090912090810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161028b949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102a760025481565b610654610fe7565b6001600160a01b0316336001600160a01b0316148061067d57506001546001600160a01b031633145b61069a57604051632864c4e160e01b815260040160405180910390fd5b6106a381611015565b81906106cc576040516381e5828960e01b81526004016106c39190613355565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906106fa9060049083611b46565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161072783613511565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b6107a0611de8565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602081905260408220600681015491810154606093919291816001600160401b0381111561081157610811613526565b60405190808252806020026020018201604052801561083a578160200160208202803683370190505b5090505f805b84811015610911576001866009015f8860060184815481106108645761086461354e565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205460ff16600281111561089c5761089c61353a565b03610909578560060181815481106108b6576108b661354e565b905f5260205f20015f9054906101000a90046001600160a01b03168383815181106108e3576108e361354e565b6001600160a01b03909216602092830291909101909101528161090581613562565b9250505b600101610840565b50909695505050505050565b5f818152600a6020526040812081815460ff1660038111156109415761094161353a565b0361095f57604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b031633146109945760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff1660038111156109b8576109b861353a565b146109d6576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610a1d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a41919061357a565b905080610a5460408601602087016135a4565b63ffffffff161115610a6c60408601602087016135a4565b829091610a9a576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106c3565b5050815460ff1916600190811783558201859055436002830155600354610ac190426135bd565b6003830155610ad560058301856002613173565b50610ade611800565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610b2f928a928a92916135d0565b60405180910390a250600195945050505050565b5f610b4d82611015565b610b5857505f919050565b6001546001600160a01b0316610b81576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610bb1908590600401613355565b602060405180830381865afa158015610bcc573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bf0919061362f565b92915050565b5f818152600a602052604081206001815460ff166003811115610c1b57610c1b61353a565b14610c2857505f92915050565b6003015442111592915050565b5f60015f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff166002811115610c7157610c7161353a565b149392505050565b610c81611de8565b6001600160a01b038116610ca85760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610cf7611de8565b610d005f611e1a565b565b600b545f9081906001600160a01b03163314610d315760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff166003811115610d5657610d5661353a565b14610d7457604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f90815260098301602052604090205463ffffffff909116925060019060ff166002811115610db457610db461353a565b14610dc457600a01549150610ea1565b6001600160a01b0385165f9081526009820160205260408120805460ff19166002179055600a8201805491610df883613511565b919050555080600a01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610e49929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b610eb1610fe7565b6001600160a01b0316336001600160a01b03161480610eda57506001546001600160a01b031633145b610ef757604051632864c4e160e01b815260040160405180910390fd5b610f0081611015565b610fe45760048054600160281b900464ffffffffff1690610f2a906001600160a01b038416611e8a565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff199091161790556002805491610f7b83613562565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161078c565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a60205260409020600481015460609190611065576040516322e679e360e11b815260040160405180910390fd5b806006018054806020026020016040519081016040528092919081815260200182805480156110bb57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161109d575b5050505050915050919050565b5f805f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff1660028111156111035761110361353a565b14159392505050565b611114611de8565b6001600160a01b03811661113b5760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b5f611166612060565b805490915060ff600160401b82041615906001600160401b03165f8115801561118c5750825b90505f826001600160401b031660011480156111a75750303b155b9050811580156111b5575080155b156111d35760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156111fd57845460ff60401b1916600160401b1785555b6001600160a01b0387166112245760405163d92e233d60e01b815260040160405180910390fd5b61122d33612088565b61123960046014612099565b61124286610798565b61124a610fe7565b6001600160a01b0316876001600160a01b03161461126b5761126b87611b0c565b83156112b157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff1660038111156112de576112de61353a565b036112fc57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156113145761131461353a565b1461133257604051631860f69960e31b815260040160405180910390fd5b8060030154421161135657604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061143b578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b15801561141c575f5ffd5b505af115801561142e573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600a83018190555f816001600160401b0381111561146b5761146b613526565b604051908082528060200260200182016040528015611494578160200160208202803683370190505b5090505f5b8281101561150657846008015f8660060183815481106114bb576114bb61354e565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106114f3576114f361354e565b6020908102919091010152600101611499565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611549575f5ffd5b505af115801561155b573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610b2f929190613648565b61159c611de8565b6001600160a01b0381166115c35760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff1660038111156116305761163061353a565b0361164e57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116665761166661353a565b1461168457604051631860f69960e31b815260040160405180910390fd5b80600301544211156116a957604051639a19114d60e01b815260040160405180910390fd5b335f90815260078201602052604090205460ff16156116db5760405163257309f160e11b815260040160405180910390fd5b6116e433610b43565b6117015760405163149fbcfd60e11b815260040160405180910390fd5b61170c338385612118565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526007850160205260409020805460ff1916600117905590915061178b908390836122e9565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f81815260096020526040902054806117fb576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61180d600460146124ea565b905090565b61181a611de8565b5f898152600a602052604090206002815460ff16600381111561183f5761183f61353a565b1461185d57604051634f4b461f60e11b815260040160405180910390fd5b6004810154156118805760405163632a22bb60e01b815260040160405180910390fd5b600681015488146118c95760405162461bcd60e51b815260206004820152601360248201527209cdec8ca40c6deeadce840dad2e6dac2e8c6d606b1b60448201526064016106c3565b5f6118d6858701876137d2565b9150505f8151116119205760405162461bcd60e51b815260206004820152601460248201527343353a206e6f207075626c696320696e7075747360601b60448201526064016106c3565b5f81600183516119309190613875565b815181106119405761194061354e565b602002602001015190505f5f5f9054906101000a90046001600160a01b03166001600160a01b031663406ed35c8e6040518263ffffffff1660e01b815260040161198c91815260200190565b5f60405180830381865afa1580156119a6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526119cd919081019061395a565b610120810151604051637bf41d7760e11b81529192506001600160a01b03169063f7e83aee90611a07908b908b908b908b90600401613af9565b602060405180830381865afa158015611a22573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a46919061357a565b5060048085018390555f8e815260096020526040808220859055905490516340a3b76160e11b81529182018f9052602482018490526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015611aa5575f5ffd5b505af1158015611ab7573d5f5f3e3d5ffd5b505050508c7f49ac1dd411942113d1c5e6799c6379ce341afe85a4175fb562cf2a5fb886c27d8d8d8d8d8d8d604051611af596959493929190613b2a565b60405180910390a250505050505050505050505050565b611b14611de8565b6001600160a01b038116611b3d575f604051631e4fbdf760e01b81526004016106c39190613355565b610fe481611e1a565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611b855760405162461bcd60e51b81526004016106c390613ba3565b825464ffffffffff600160281b90910481169082168111611be35760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106c3565b825f5b81866001015f611bf684886125e3565b64ffffffffff1681526020019081526020015f20819055505f816001611c1c9190613bed565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611c515750611de0565b600185165f03611d18575f611c7083611c6b886001613c06565b6125e3565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611cd191600401613c23565b602060405180830381865af4158015611cec573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d10919061357a565b935050611dcc565b5f611d2883611c6b600189613c53565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611d8991600401613c23565b602060405180830381865af4158015611da4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dc8919061357a565b9350505b50647fffffffff600194851c169301611be6565b505050505050565b33611df1610fe7565b6001600160a01b031614610d00573360405163118cdaa760e01b81526004016106c39190613355565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611ed95760405162461bcd60e51b81526004016106c390613ba3565b825464ffffffffff90811690821610611f2c5760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106c3565b611f37816001613c06565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f611f6e84876125e3565b64ffffffffff16815260208101919091526040015f20556001831615612059575f611f9e82611c6b600187613c53565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611fff91600401613c23565b602060405180830381865af415801561201a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061203e919061357a565b647fffffffff600195861c1694909350919091019050611f5e565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610bf0565b612090612600565b610fe481612625565b602060ff821611156120e75760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106c3565b6120f8600160ff831681901b613875565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116121385760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612161576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161219791613875565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156121de573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612202919061357a565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612255573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612279919061357a565b90505f811161229b5760405163aeaddff160e01b815260040160405180910390fd5b5f6122a68284613c70565b90505f81116122c85760405163149fbcfd60e11b815260040160405180910390fd5b808611156112b15760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff169081111561236757508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526008870182526040808420869055600988019092529120805460ff19168217905590506124e3565b5f5f90505f876008015f855f815481106123835761238361354e565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b845481101561240b575f896008015f8784815481106123cd576123cd61354e565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612402578092508193505b506001016123ac565b5080861061241f575f9450505050506124e3565b5f886009015f8685815481106124375761243761354e565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156124745761247461353a565b02179055508684838154811061248c5761248c61354e565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260088a018252604080822089905560098b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff161161253d5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106c3565b602060ff831611156125615760405162461bcd60e51b81526004016106c390613c8f565b8254600160281b900464ffffffffff168061258060ff85166002613de0565b64ffffffffff1610156125d05760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106c3565b6125db84828561262d565b949350505050565b5f816125f660ff851663ffffffff613df9565b6124e39190613c06565b6126086126f5565b610d0057604051631afcd79f60e31b815260040160405180910390fd5b611b14612600565b5f602060ff831611156126525760405162461bcd60e51b81526004016106c390613c8f565b8264ffffffffff165f03612670576126698261270e565b90506124e3565b5f61267c836001613bed565b60ff166001600160401b0381111561269657612696613526565b6040519080825280602002602001820160405280156126bf578160200160208202803683370190505b5090506126ce85858584612da8565b808360ff16815181106126e3576126e361354e565b60200260200101519150509392505050565b5f6126fe612060565b54600160401b900460ff16919050565b5f8160ff165f0361272057505f919050565b8160ff1660010361275257507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361278457507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff166003036127b657507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036127e857507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361281a57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361284c57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361287e57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff166008036128b057507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036128e257507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361291457507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361294657507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361297857507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036129aa57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036129dc57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612a0e57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612a4057507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612a7257507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612aa457507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612ad657507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612b0857507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612b3a57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612b6c57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612b9e57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612bd057507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612c0257507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612c3457507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612c6657507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612c9857507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612cca57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612cfc57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612d2e57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612d6057507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106c3565b602060ff83161115612dcc5760405162461bcd60e51b81526004016106c390613c8f565b5f8364ffffffffff1611612e305760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106c3565b5f612e3c600185613c53565b9050600181165f03612e8f57846001015f612e575f846125e3565b64ffffffffff1681526020019081526020015f2054825f81518110612e7e57612e7e61354e565b602002602001018181525050612eb7565b612e985f61270e565b825f81518110612eaa57612eaa61354e565b6020026020010181815250505b5f5b8360ff168160ff161015611de057600182165f03612faf5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612f0b57612f0b61354e565b60200260200101518152602001612f218561270e565b8152506040518263ffffffff1660e01b8152600401612f409190613c23565b602060405180830381865af4158015612f5b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f7f919061357a565b83612f8b836001613bed565b60ff1681518110612f9e57612f9e61354e565b602002602001018181525050613160565b5f612fbb826001613bed565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff1681111561305d575f876001015f6130128560016130019190613bed565b60018864ffffffffff16901c6125e3565b64ffffffffff1681526020019081526020015f2054905080858460016130389190613bed565b60ff168151811061304b5761304b61354e565b6020026020010181815250505061315e565b5f876001015f61307485600188611c6b9190613c53565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106130cb576130cb61354e565b60200260200101518152506040518263ffffffff1660e01b81526004016130f29190613c23565b602060405180830381865af415801561310d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613131919061357a565b8561313d856001613bed565b60ff16815181106131505761315061354e565b602002602001018181525050505b505b647fffffffff600192831c169101612eb9565b600183019183908215613204579160200282015f5b838211156131d257833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613188565b80156132025782816101000a81549063ffffffff02191690556004016020816003010492830192600103026131d2565b505b50613210929150613214565b5090565b5b80821115613210575f8155600101613215565b6001600160a01b0381168114610fe4575f5ffd5b5f6020828403121561324c575f5ffd5b81356124e381613228565b5f60208284031215613267575f5ffd5b5035919050565b602080825282518282018190525f918401906040840190835b818110156132ae5783516001600160a01b0316835260209384019390920191600101613287565b509095945050505050565b5f5f5f608084860312156132cb575f5ffd5b8335925060208401359150608084018510156132e5575f5ffd5b6040840190509250925092565b5f5f60408385031215613303575f5ffd5b82359150602083013561331581613228565b809150509250929050565b5f5f5f60608486031215613332575f5ffd5b83359250602084013561334481613228565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b5f5f6040838503121561337a575f5ffd5b823561338581613228565b946020939093013593505050565b5f5f604083850312156133a4575f5ffd5b50508035926020909101359150565b5f5f83601f8401126133c3575f5ffd5b5081356001600160401b038111156133d9575f5ffd5b6020830191508360208285010111156133f0575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f60a08a8c03121561340f575f5ffd5b8935985060208a01356001600160401b0381111561342b575f5ffd5b8a01601f81018c1361343b575f5ffd5b80356001600160401b03811115613450575f5ffd5b8c60208260051b8401011115613464575f5ffd5b6020919091019850965060408a01356001600160401b03811115613486575f5ffd5b6134928c828d016133b3565b90975095505060608a01356001600160401b038111156134b0575f5ffd5b6134bc8c828d016133b3565b90955093505060808a01356001600160401b038111156134da575f5ffd5b6134e68c828d016133b3565b915080935050809150509295985092959850929598565b634e487b7160e01b5f52601160045260245ffd5b5f8161351f5761351f6134fd565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f60018201613573576135736134fd565b5060010190565b5f6020828403121561358a575f5ffd5b5051919050565b803563ffffffff811681146117fb575f5ffd5b5f602082840312156135b4575f5ffd5b6124e382613591565b80820180821115610bf057610bf06134fd565b84815260a0810160208201855f5b600281101561360b5763ffffffff6135f583613591565b16835260209283019291909101906001016135de565b50505060608201939093526080015292915050565b805180151581146117fb575f5ffd5b5f6020828403121561363f575f5ffd5b6124e382613620565b604080825283549082018190525f8481526020812090916060840190835b8181101561368d5783546001600160a01b0316835260019384019360209093019201613666565b5050838103602080860191909152855180835291810192508501905f5b818110156136c85782518452602093840193909201916001016136aa565b50919695505050505050565b6040516101e081016001600160401b03811182821017156136f7576136f7613526565b60405290565b604051601f8201601f191681016001600160401b038111828210171561372557613725613526565b604052919050565b5f6001600160401b0382111561374557613745613526565b50601f01601f191660200190565b5f82601f830112613762575f5ffd5b81356001600160401b0381111561377b5761377b613526565b8060051b61378b602082016136fd565b918252602081850181019290810190868411156137a6575f5ffd5b6020860192505b838310156137c85782358252602092830192909101906137ad565b9695505050505050565b5f5f604083850312156137e3575f5ffd5b82356001600160401b038111156137f8575f5ffd5b8301601f81018513613808575f5ffd5b803561381b6138168261372d565b6136fd565b81815286602083850101111561382f575f5ffd5b816020840160208301375f6020838301015280945050505060208301356001600160401b0381111561385f575f5ffd5b61386b85828601613753565b9150509250929050565b81810381811115610bf057610bf06134fd565b8051600481106117fb575f5ffd5b5f82601f8301126138a5575f5ffd5b604080519081016001600160401b03811182821017156138c7576138c7613526565b80604052508060408401858111156138dd575f5ffd5b845b818110156138f75780518352602092830192016138df565b509195945050505050565b80516117fb81613228565b5f82601f83011261391c575f5ffd5b815161392a6138168261372d565b81815284602083860101111561393e575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f6020828403121561396a575f5ffd5b81516001600160401b0381111561397f575f5ffd5b82016102008185031215613991575f5ffd5b6139996136d4565b815181526139a960208301613888565b6020820152604082810151908201526139c58560608401613896565b606082015260a082015160808201526139e060c08301613902565b60a082015260e08201516001600160401b038111156139fd575f5ffd5b613a098682850161390d565b60c0830152506101008201516001600160401b03811115613a28575f5ffd5b613a348682850161390d565b60e083015250613a476101208301613902565b610100820152613a5a6101408301613902565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613a90575f5ffd5b613a9c8682850161390d565b61018083015250613ab06101c08301613902565b6101a0820152613ac36101e08301613620565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f613b0c604083018688613ad1565b8281036020840152613b1f818587613ad1565b979650505050505050565b606080825281018690525f8760808301825b89811015613b6c578235613b4f81613228565b6001600160a01b0316825260209283019290910190600101613b3c565b508381036020850152613b8081888a613ad1565b9150508281036040840152613b96818587613ad1565b9998505050505050505050565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610bf057610bf06134fd565b64ffffffffff8181168382160190811115610bf057610bf06134fd565b6040810181835f5b6002811015613c4a578151835260209283019290910190600101613c2b565b50505092915050565b64ffffffffff8281168282160390811115610bf057610bf06134fd565b5f82613c8a57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115610ea157808504811115613cf157613cf16134fd565b6001841615613cff57908102905b60019390931c928002613cd6565b5f82613d1b57506001610bf0565b81613d2757505f610bf0565b8160018114613d3d5760028114613d4757613d79565b6001915050610bf0565b60ff841115613d5857613d586134fd565b6001841b915064ffffffffff821115613d7357613d736134fd565b50610bf0565b5060208310610133831016604e8410600b8410161715613db1575081810a64ffffffffff811115613dac57613dac6134fd565b610bf0565b613dc164ffffffffff8484613cd2565b8064ffffffffff04821115613dd857613dd86134fd565b029392505050565b5f6124e364ffffffffff841664ffffffffff8416613d0d565b64ffffffffff8181168382160290811690818114613e1957613e196134fd565b509291505056fea164736f6c634300081c000a", - "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061023f575f3560e01c80639f0f874a11610135578063e59e4695116100b4578063f26ef74e11610079578063f26ef74e14610571578063f2fde38b14610584578063f379b0df14610597578063f52fd803146105d1578063f6fc05d514610643575f5ffd5b8063e59e469514610516578063e6745e1314610529578063e82f3b701461053c578063ebf0c7171461054f578063f165053614610557575f5ffd5b8063c3a0ec30116100fa578063c3a0ec30146104ae578063ca2869a0146104bf578063cd6dc687146104de578063da881e5a146104f1578063dbb06c9314610504575f5ffd5b80639f0f874a1461044d578063a016493014610456578063a8a4d69b14610469578063bff232c11461047c578063c2b40ae41461048f575f5ffd5b8063715018a6116101c15780638d1ddfb1116101865780638d1ddfb1146103cd5780638da5cb5b146103e35780638e5ce3ad146103eb5780639015d371146103fe5780639a7a2ffc14610411575f5ffd5b8063715018a6146103465780637c92f5241461034e578063858142431461037b5780638a78bb151461039b5780638cb89ecb146103ae575f5ffd5b80632e7b716d116102075780632e7b716d146102d85780634d6861a6146102eb57806350e6d94c146102fe5780635d5047761461032057806370e36bbe14610333575f5ffd5b8063096b810a146102435780630f3e34121461025857806317d611201461026b5780632800d82914610294578063291a691b146102b5575b5f5ffd5b61025661025136600461323c565b61064c565b005b610256610266366004613257565b610798565b61027e610279366004613257565b6107db565b60405161028b919061326e565b60405180910390f35b6102a76102a2366004613257565b61091d565b60405190815260200161028b565b6102c86102c33660046132b9565b610969565b604051901515815260200161028b565b6102c86102e636600461323c565b610b43565b6102c86102f9366004613257565b610bf6565b6102c861030c36600461323c565b60066020525f908152604090205460ff1681565b6102c861032e3660046132f2565b610c35565b61025661034136600461323c565b610c79565b610256610cef565b61036161035c366004613320565b610d02565b6040805192835263ffffffff90911660208301520161028b565b60015461038e906001600160a01b031681565b60405161028b9190613355565b6102566103a936600461323c565b610ea9565b6102a76103bc366004613257565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102a7565b61038e610fe7565b600b5461038e906001600160a01b031681565b6102c861040c36600461323c565b611015565b61043761041f36600461323c565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161028b565b6102a760035481565b61027e610464366004613257565b611032565b6102c86104773660046132f2565b6110c8565b61025661048a36600461323c565b61110c565b6102a761049d366004613257565b60086020525f908152604090205481565b6001546001600160a01b031661038e565b6102a76104cd366004613257565b5f9081526008602052604090205490565b6102566104ec366004613369565b61115d565b6102c86104ff366004613257565b6112ba565b5f5461038e906001600160a01b031681565b61025661052436600461323c565b611594565b610256610537366004613393565b61160c565b6102a761054a366004613257565b6117cf565b6102a7611800565b61055f601481565b60405160ff909116815260200161028b565b61025661057f3660046133f7565b611812565b61025661059236600461323c565b611b0c565b6004546105b39064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161028b565b6106146105df366004613257565b5f908152600a6020819052604090912090810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161028b949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102a760025481565b610654610fe7565b6001600160a01b0316336001600160a01b0316148061067d57506001546001600160a01b031633145b61069a57604051632864c4e160e01b815260040160405180910390fd5b6106a381611015565b81906106cc576040516381e5828960e01b81526004016106c39190613355565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906106fa9060049083611b46565b6001600160a01b0382165f908152600660205260408120805460ff19169055600280549161072783613511565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b6107a0611de8565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a602081905260408220600681015491810154606093919291816001600160401b0381111561081157610811613526565b60405190808252806020026020018201604052801561083a578160200160208202803683370190505b5090505f805b84811015610911576001866009015f8860060184815481106108645761086461354e565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205460ff16600281111561089c5761089c61353a565b03610909578560060181815481106108b6576108b661354e565b905f5260205f20015f9054906101000a90046001600160a01b03168383815181106108e3576108e361354e565b6001600160a01b03909216602092830291909101909101528161090581613562565b9250505b600101610840565b50909695505050505050565b5f818152600a6020526040812081815460ff1660038111156109415761094161353a565b0361095f57604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b031633146109945760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff1660038111156109b8576109b861353a565b146109d6576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610a1d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a41919061357a565b905080610a5460408601602087016135a4565b63ffffffff161115610a6c60408601602087016135a4565b829091610a9a576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106c3565b5050815460ff1916600190811783558201859055436002830155600354610ac190426135bd565b6003830155610ad560058301856002613173565b50610ade611800565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610b2f928a928a92916135d0565b60405180910390a250600195945050505050565b5f610b4d82611015565b610b5857505f919050565b6001546001600160a01b0316610b81576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610bb1908590600401613355565b602060405180830381865afa158015610bcc573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bf0919061362f565b92915050565b5f818152600a602052604081206001815460ff166003811115610c1b57610c1b61353a565b14610c2857505f92915050565b6003015442111592915050565b5f60015f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff166002811115610c7157610c7161353a565b149392505050565b610c81611de8565b6001600160a01b038116610ca85760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610cf7611de8565b610d005f611e1a565b565b600b545f9081906001600160a01b03163314610d315760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff166003811115610d5657610d5661353a565b14610d7457604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f90815260098301602052604090205463ffffffff909116925060019060ff166002811115610db457610db461353a565b14610dc457600a01549150610ea1565b6001600160a01b0385165f9081526009820160205260408120805460ff19166002179055600a8201805491610df883613511565b919050555080600a01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610e49929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b610eb1610fe7565b6001600160a01b0316336001600160a01b03161480610eda57506001546001600160a01b031633145b610ef757604051632864c4e160e01b815260040160405180910390fd5b610f0081611015565b610fe45760048054600160281b900464ffffffffff1690610f2a906001600160a01b038416611e8a565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff199091161790556002805491610f7b83613562565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161078c565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a60205260409020600481015460609190611065576040516322e679e360e11b815260040160405180910390fd5b806006018054806020026020016040519081016040528092919081815260200182805480156110bb57602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161109d575b5050505050915050919050565b5f805f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff1660028111156111035761110361353a565b14159392505050565b611114611de8565b6001600160a01b03811661113b5760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b5f611166612060565b805490915060ff600160401b82041615906001600160401b03165f8115801561118c5750825b90505f826001600160401b031660011480156111a75750303b155b9050811580156111b5575080155b156111d35760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156111fd57845460ff60401b1916600160401b1785555b6001600160a01b0387166112245760405163d92e233d60e01b815260040160405180910390fd5b61122d33612088565b61123960046014612099565b61124286610798565b61124a610fe7565b6001600160a01b0316876001600160a01b03161461126b5761126b87611b0c565b83156112b157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff1660038111156112de576112de61353a565b036112fc57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156113145761131461353a565b1461133257604051631860f69960e31b815260040160405180910390fd5b8060030154421161135657604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff1611158061143b578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b15801561141c575f5ffd5b505af115801561142e573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600a83018190555f816001600160401b0381111561146b5761146b613526565b604051908082528060200260200182016040528015611494578160200160208202803683370190505b5090505f5b8281101561150657846008015f8660060183815481106114bb576114bb61354e565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106114f3576114f361354e565b6020908102919091010152600101611499565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b158015611549575f5ffd5b505af115801561155b573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610b2f929190613648565b61159c611de8565b6001600160a01b0381166115c35760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff1660038111156116305761163061353a565b0361164e57604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116665761166661353a565b1461168457604051631860f69960e31b815260040160405180910390fd5b80600301544211156116a957604051639a19114d60e01b815260040160405180910390fd5b335f90815260078201602052604090205460ff16156116db5760405163257309f160e11b815260040160405180910390fd5b6116e433610b43565b6117015760405163149fbcfd60e11b815260040160405180910390fd5b61170c338385612118565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526007850160205260409020805460ff1916600117905590915061178b908390836122e9565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f81815260096020526040902054806117fb576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61180d600460146124ea565b905090565b61181a611de8565b5f898152600a602052604090206002815460ff16600381111561183f5761183f61353a565b1461185d57604051634f4b461f60e11b815260040160405180910390fd5b6004810154156118805760405163632a22bb60e01b815260040160405180910390fd5b600681015488146118c95760405162461bcd60e51b815260206004820152601360248201527209cdec8ca40c6deeadce840dad2e6dac2e8c6d606b1b60448201526064016106c3565b5f6118d6858701876137d2565b9150505f8151116119205760405162461bcd60e51b815260206004820152601460248201527343353a206e6f207075626c696320696e7075747360601b60448201526064016106c3565b5f81600183516119309190613875565b815181106119405761194061354e565b602002602001015190505f5f5f9054906101000a90046001600160a01b03166001600160a01b031663406ed35c8e6040518263ffffffff1660e01b815260040161198c91815260200190565b5f60405180830381865afa1580156119a6573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526119cd919081019061395a565b610120810151604051637bf41d7760e11b81529192506001600160a01b03169063f7e83aee90611a07908b908b908b908b90600401613af9565b602060405180830381865afa158015611a22573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a46919061357a565b5060048085018390555f8e815260096020526040808220859055905490516340a3b76160e11b81529182018f9052602482018490526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015611aa5575f5ffd5b505af1158015611ab7573d5f5f3e3d5ffd5b505050508c7f49ac1dd411942113d1c5e6799c6379ce341afe85a4175fb562cf2a5fb886c27d8d8d8d8d8d8d604051611af596959493929190613b2a565b60405180910390a250505050505050505050505050565b611b14611de8565b6001600160a01b038116611b3d575f604051631e4fbdf760e01b81526004016106c39190613355565b610fe481611e1a565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611b855760405162461bcd60e51b81526004016106c390613ba3565b825464ffffffffff600160281b90910481169082168111611be35760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106c3565b825f5b81866001015f611bf684886125e3565b64ffffffffff1681526020019081526020015f20819055505f816001611c1c9190613bed565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611c515750611de0565b600185165f03611d18575f611c7083611c6b886001613c06565b6125e3565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611cd191600401613c23565b602060405180830381865af4158015611cec573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d10919061357a565b935050611dcc565b5f611d2883611c6b600189613c53565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611d8991600401613c23565b602060405180830381865af4158015611da4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dc8919061357a565b9350505b50647fffffffff600194851c169301611be6565b505050505050565b33611df1610fe7565b6001600160a01b031614610d00573360405163118cdaa760e01b81526004016106c39190613355565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611ed95760405162461bcd60e51b81526004016106c390613ba3565b825464ffffffffff90811690821610611f2c5760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106c3565b611f37816001613c06565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f611f6e84876125e3565b64ffffffffff16815260208101919091526040015f20556001831615612059575f611f9e82611c6b600187613c53565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611fff91600401613c23565b602060405180830381865af415801561201a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061203e919061357a565b647fffffffff600195861c1694909350919091019050611f5e565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610bf0565b612090612600565b610fe481612625565b602060ff821611156120e75760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106c3565b6120f8600160ff831681901b613875565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116121385760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b0316612161576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161219791613875565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156121de573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612202919061357a565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa158015612255573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612279919061357a565b90505f811161229b5760405163aeaddff160e01b815260040160405180910390fd5b5f6122a68284613c70565b90505f81116122c85760405163149fbcfd60e11b815260040160405180910390fd5b808611156112b15760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff169081111561236757508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526008870182526040808420869055600988019092529120805460ff19168217905590506124e3565b5f5f90505f876008015f855f815481106123835761238361354e565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b845481101561240b575f896008015f8784815481106123cd576123cd61354e565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612402578092508193505b506001016123ac565b5080861061241f575f9450505050506124e3565b5f886009015f8685815481106124375761243761354e565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156124745761247461353a565b02179055508684838154811061248c5761248c61354e565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260088a018252604080822089905560098b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff161161253d5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106c3565b602060ff831611156125615760405162461bcd60e51b81526004016106c390613c8f565b8254600160281b900464ffffffffff168061258060ff85166002613de0565b64ffffffffff1610156125d05760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106c3565b6125db84828561262d565b949350505050565b5f816125f660ff851663ffffffff613df9565b6124e39190613c06565b6126086126f5565b610d0057604051631afcd79f60e31b815260040160405180910390fd5b611b14612600565b5f602060ff831611156126525760405162461bcd60e51b81526004016106c390613c8f565b8264ffffffffff165f03612670576126698261270e565b90506124e3565b5f61267c836001613bed565b60ff166001600160401b0381111561269657612696613526565b6040519080825280602002602001820160405280156126bf578160200160208202803683370190505b5090506126ce85858584612da8565b808360ff16815181106126e3576126e361354e565b60200260200101519150509392505050565b5f6126fe612060565b54600160401b900460ff16919050565b5f8160ff165f0361272057505f919050565b8160ff1660010361275257507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff1660020361278457507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff166003036127b657507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff166004036127e857507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361281a57507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff1660060361284c57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff1660070361287e57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff166008036128b057507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff166009036128e257507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361291457507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b0361294657507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c0361297857507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d036129aa57507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e036129dc57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612a0e57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612a4057507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612a7257507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612aa457507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612ad657507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612b0857507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612b3a57507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612b6c57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612b9e57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612bd057507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612c0257507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612c3457507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612c6657507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612c9857507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612cca57507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612cfc57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612d2e57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612d6057507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106c3565b602060ff83161115612dcc5760405162461bcd60e51b81526004016106c390613c8f565b5f8364ffffffffff1611612e305760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106c3565b5f612e3c600185613c53565b9050600181165f03612e8f57846001015f612e575f846125e3565b64ffffffffff1681526020019081526020015f2054825f81518110612e7e57612e7e61354e565b602002602001018181525050612eb7565b612e985f61270e565b825f81518110612eaa57612eaa61354e565b6020026020010181815250505b5f5b8360ff168160ff161015611de057600182165f03612faf5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612f0b57612f0b61354e565b60200260200101518152602001612f218561270e565b8152506040518263ffffffff1660e01b8152600401612f409190613c23565b602060405180830381865af4158015612f5b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f7f919061357a565b83612f8b836001613bed565b60ff1681518110612f9e57612f9e61354e565b602002602001018181525050613160565b5f612fbb826001613bed565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff1681111561305d575f876001015f6130128560016130019190613bed565b60018864ffffffffff16901c6125e3565b64ffffffffff1681526020019081526020015f2054905080858460016130389190613bed565b60ff168151811061304b5761304b61354e565b6020026020010181815250505061315e565b5f876001015f61307485600188611c6b9190613c53565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106130cb576130cb61354e565b60200260200101518152506040518263ffffffff1660e01b81526004016130f29190613c23565b602060405180830381865af415801561310d573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613131919061357a565b8561313d856001613bed565b60ff16815181106131505761315061354e565b602002602001018181525050505b505b647fffffffff600192831c169101612eb9565b600183019183908215613204579160200282015f5b838211156131d257833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff1602179055509260200192600401602081600301049283019260010302613188565b80156132025782816101000a81549063ffffffff02191690556004016020816003010492830192600103026131d2565b505b50613210929150613214565b5090565b5b80821115613210575f8155600101613215565b6001600160a01b0381168114610fe4575f5ffd5b5f6020828403121561324c575f5ffd5b81356124e381613228565b5f60208284031215613267575f5ffd5b5035919050565b602080825282518282018190525f918401906040840190835b818110156132ae5783516001600160a01b0316835260209384019390920191600101613287565b509095945050505050565b5f5f5f608084860312156132cb575f5ffd5b8335925060208401359150608084018510156132e5575f5ffd5b6040840190509250925092565b5f5f60408385031215613303575f5ffd5b82359150602083013561331581613228565b809150509250929050565b5f5f5f60608486031215613332575f5ffd5b83359250602084013561334481613228565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b5f5f6040838503121561337a575f5ffd5b823561338581613228565b946020939093013593505050565b5f5f604083850312156133a4575f5ffd5b50508035926020909101359150565b5f5f83601f8401126133c3575f5ffd5b5081356001600160401b038111156133d9575f5ffd5b6020830191508360208285010111156133f0575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f60a08a8c03121561340f575f5ffd5b8935985060208a01356001600160401b0381111561342b575f5ffd5b8a01601f81018c1361343b575f5ffd5b80356001600160401b03811115613450575f5ffd5b8c60208260051b8401011115613464575f5ffd5b6020919091019850965060408a01356001600160401b03811115613486575f5ffd5b6134928c828d016133b3565b90975095505060608a01356001600160401b038111156134b0575f5ffd5b6134bc8c828d016133b3565b90955093505060808a01356001600160401b038111156134da575f5ffd5b6134e68c828d016133b3565b915080935050809150509295985092959850929598565b634e487b7160e01b5f52601160045260245ffd5b5f8161351f5761351f6134fd565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f60018201613573576135736134fd565b5060010190565b5f6020828403121561358a575f5ffd5b5051919050565b803563ffffffff811681146117fb575f5ffd5b5f602082840312156135b4575f5ffd5b6124e382613591565b80820180821115610bf057610bf06134fd565b84815260a0810160208201855f5b600281101561360b5763ffffffff6135f583613591565b16835260209283019291909101906001016135de565b50505060608201939093526080015292915050565b805180151581146117fb575f5ffd5b5f6020828403121561363f575f5ffd5b6124e382613620565b604080825283549082018190525f8481526020812090916060840190835b8181101561368d5783546001600160a01b0316835260019384019360209093019201613666565b5050838103602080860191909152855180835291810192508501905f5b818110156136c85782518452602093840193909201916001016136aa565b50919695505050505050565b6040516101e081016001600160401b03811182821017156136f7576136f7613526565b60405290565b604051601f8201601f191681016001600160401b038111828210171561372557613725613526565b604052919050565b5f6001600160401b0382111561374557613745613526565b50601f01601f191660200190565b5f82601f830112613762575f5ffd5b81356001600160401b0381111561377b5761377b613526565b8060051b61378b602082016136fd565b918252602081850181019290810190868411156137a6575f5ffd5b6020860192505b838310156137c85782358252602092830192909101906137ad565b9695505050505050565b5f5f604083850312156137e3575f5ffd5b82356001600160401b038111156137f8575f5ffd5b8301601f81018513613808575f5ffd5b803561381b6138168261372d565b6136fd565b81815286602083850101111561382f575f5ffd5b816020840160208301375f6020838301015280945050505060208301356001600160401b0381111561385f575f5ffd5b61386b85828601613753565b9150509250929050565b81810381811115610bf057610bf06134fd565b8051600481106117fb575f5ffd5b5f82601f8301126138a5575f5ffd5b604080519081016001600160401b03811182821017156138c7576138c7613526565b80604052508060408401858111156138dd575f5ffd5b845b818110156138f75780518352602092830192016138df565b509195945050505050565b80516117fb81613228565b5f82601f83011261391c575f5ffd5b815161392a6138168261372d565b81815284602083860101111561393e575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f6020828403121561396a575f5ffd5b81516001600160401b0381111561397f575f5ffd5b82016102008185031215613991575f5ffd5b6139996136d4565b815181526139a960208301613888565b6020820152604082810151908201526139c58560608401613896565b606082015260a082015160808201526139e060c08301613902565b60a082015260e08201516001600160401b038111156139fd575f5ffd5b613a098682850161390d565b60c0830152506101008201516001600160401b03811115613a28575f5ffd5b613a348682850161390d565b60e083015250613a476101208301613902565b610100820152613a5a6101408301613902565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613a90575f5ffd5b613a9c8682850161390d565b61018083015250613ab06101c08301613902565b6101a0820152613ac36101e08301613620565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f613b0c604083018688613ad1565b8281036020840152613b1f818587613ad1565b979650505050505050565b606080825281018690525f8760808301825b89811015613b6c578235613b4f81613228565b6001600160a01b0316825260209283019290910190600101613b3c565b508381036020850152613b8081888a613ad1565b9150508281036040840152613b96818587613ad1565b9998505050505050505050565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610bf057610bf06134fd565b64ffffffffff8181168382160190811115610bf057610bf06134fd565b6040810181835f5b6002811015613c4a578151835260209283019290910190600101613c2b565b50505092915050565b64ffffffffff8281168282160390811115610bf057610bf06134fd565b5f82613c8a57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115610ea157808504811115613cf157613cf16134fd565b6001841615613cff57908102905b60019390931c928002613cd6565b5f82613d1b57506001610bf0565b81613d2757505f610bf0565b8160018114613d3d5760028114613d4757613d79565b6001915050610bf0565b60ff841115613d5857613d586134fd565b6001841b915064ffffffffff821115613d7357613d736134fd565b50610bf0565b5060208310610133831016604e8410600b8410161715613db1575081810a64ffffffffff811115613dac57613dac6134fd565b610bf0565b613dc164ffffffffff8484613cd2565b8064ffffffffff04821115613dd857613dd86134fd565b029392505050565b5f6124e364ffffffffff841664ffffffffff8416613d0d565b64ffffffffff8181168382160290811690818114613e1957613e196134fd565b509291505056fea164736f6c634300081c000a", + "bytecode": "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b613ecf806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061023f575f3560e01c80639f0f874a11610135578063e59e4695116100b4578063f26ef74e11610079578063f26ef74e1461057f578063f2fde38b14610592578063f379b0df146105a5578063f52fd803146105df578063f6fc05d514610651575f5ffd5b8063e59e469514610524578063e6745e1314610537578063e82f3b701461054a578063ebf0c7171461055d578063f165053614610565575f5ffd5b8063c3a0ec30116100fa578063c3a0ec30146104bc578063ca2869a0146104cd578063cd6dc687146104ec578063da881e5a146104ff578063dbb06c9314610512575f5ffd5b80639f0f874a1461044e578063a016493014610457578063a8a4d69b14610477578063bff232c11461048a578063c2b40ae41461049d575f5ffd5b8063715018a6116101c15780638d1ddfb1116101865780638d1ddfb1146103ce5780638da5cb5b146103e45780638e5ce3ad146103ec5780639015d371146103ff5780639a7a2ffc14610412575f5ffd5b8063715018a6146103475780637c92f5241461034f578063858142431461037c5780638a78bb151461039c5780638cb89ecb146103af575f5ffd5b80632e7b716d116102075780632e7b716d146102d95780634d6861a6146102ec57806350e6d94c146102ff5780635d5047761461032157806370e36bbe14610334575f5ffd5b8063096b810a146102435780630f3e34121461025857806317d611201461026b5780632800d82914610295578063291a691b146102b6575b5f5ffd5b6102566102513660046132aa565b61065a565b005b6102566102663660046132c5565b6107a6565b61027e6102793660046132c5565b6107e9565b60405161028c92919061334f565b60405180910390f35b6102a86102a33660046132c5565b610993565b60405190815260200161028c565b6102c96102c436600461337c565b6109df565b604051901515815260200161028c565b6102c96102e73660046132aa565b610bb9565b6102c96102fa3660046132c5565b610c6c565b6102c961030d3660046132aa565b60066020525f908152604090205460ff1681565b6102c961032f3660046133b5565b610cab565b6102566103423660046132aa565b610cef565b610256610d65565b61036261035d3660046133e3565b610d78565b6040805192835263ffffffff90911660208301520161028c565b60015461038f906001600160a01b031681565b60405161028c9190613418565b6102566103aa3660046132aa565b610f1f565b6102a86103bd3660046132c5565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102a8565b61038f61105d565b600b5461038f906001600160a01b031681565b6102c961040d3660046132aa565b61108b565b6104386104203660046132aa565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161028c565b6102a860035481565b61046a6104653660046132c5565b6110a8565b60405161028c919061342c565b6102c96104853660046133b5565b61113e565b6102566104983660046132aa565b611182565b6102a86104ab3660046132c5565b60086020525f908152604090205481565b6001546001600160a01b031661038f565b6102a86104db3660046132c5565b5f9081526008602052604090205490565b6102566104fa36600461343e565b6111d3565b6102c961050d3660046132c5565b611330565b5f5461038f906001600160a01b031681565b6102566105323660046132aa565b61160a565b610256610545366004613468565b611682565b6102a86105583660046132c5565b611845565b6102a8611876565b61056d601481565b60405160ff909116815260200161028c565b61025661058d3660046134cc565b611888565b6102566105a03660046132aa565b611b7a565b6004546105c19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161028c565b6106226105ed3660046132c5565b5f908152600a6020819052604090912090810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161028c949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102a860025481565b61066261105d565b6001600160a01b0316336001600160a01b0316148061068b57506001546001600160a01b031633145b6106a857604051632864c4e160e01b815260040160405180910390fd5b6106b18161108b565b81906106da576040516381e5828960e01b81526004016106d19190613418565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107089060049083611bb4565b6001600160a01b0382165f908152600660205260408120805460ff191690556002805491610735836135e6565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b6107ae611e56565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a60208190526040909120600681015491810154606092839291806001600160401b03811115610820576108206135fb565b604051908082528060200260200182016040528015610849578160200160208202803683370190505b509450806001600160401b03811115610864576108646135fb565b60405190808252806020026020018201604052801561088d578160200160208202803683370190505b5093505f805b83811015610989575f8560060182815481106108b1576108b161360f565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f90815260098801602052604090205460ff1660028111156108f7576108f7613623565b03610980578088848151811061090f5761090f61360f565b60200260200101906001600160a01b031690816001600160a01b031681525050856008015f826001600160a01b03166001600160a01b031681526020019081526020015f20548784815181106109675761096761360f565b60209081029190910101528261097c81613637565b9350505b50600101610893565b5050505050915091565b5f818152600a6020526040812081815460ff1660038111156109b7576109b7613623565b036109d557604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610a0a5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610a2e57610a2e613623565b14610a4c576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610a93573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab7919061364f565b905080610aca6040860160208701613679565b63ffffffff161115610ae26040860160208701613679565b829091610b10576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106d1565b5050815460ff1916600190811783558201859055436002830155600354610b379042613692565b6003830155610b4b600583018560026131e1565b50610b54611876565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ba5928a928a92916136a5565b60405180910390a250600195945050505050565b5f610bc38261108b565b610bce57505f919050565b6001546001600160a01b0316610bf7576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610c27908590600401613418565b602060405180830381865afa158015610c42573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c669190613704565b92915050565b5f818152600a602052604081206001815460ff166003811115610c9157610c91613623565b14610c9e57505f92915050565b6003015442111592915050565b5f60015f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff166002811115610ce757610ce7613623565b149392505050565b610cf7611e56565b6001600160a01b038116610d1e5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610d6d611e56565b610d765f611e88565b565b600b545f9081906001600160a01b03163314610da75760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff166003811115610dcc57610dcc613623565b14610dea57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f90815260098301602052604090205463ffffffff909116925060019060ff166002811115610e2a57610e2a613623565b14610e3a57600a01549150610f17565b6001600160a01b0385165f9081526009820160205260408120805460ff19166002179055600a8201805491610e6e836135e6565b919050555080600a01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610ebf929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b610f2761105d565b6001600160a01b0316336001600160a01b03161480610f5057506001546001600160a01b031633145b610f6d57604051632864c4e160e01b815260040160405180910390fd5b610f768161108b565b61105a5760048054600160281b900464ffffffffff1690610fa0906001600160a01b038416611ef8565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff199091161790556002805491610ff183613637565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161079a565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906110db576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561113157602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611113575b5050505050915050919050565b5f805f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff16600281111561117957611179613623565b14159392505050565b61118a611e56565b6001600160a01b0381166111b15760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b5f6111dc6120ce565b805490915060ff600160401b82041615906001600160401b03165f811580156112025750825b90505f826001600160401b0316600114801561121d5750303b155b90508115801561122b575080155b156112495760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561127357845460ff60401b1916600160401b1785555b6001600160a01b03871661129a5760405163d92e233d60e01b815260040160405180910390fd5b6112a3336120f6565b6112af60046014612107565b6112b8866107a6565b6112c061105d565b6001600160a01b0316876001600160a01b0316146112e1576112e187611b7a565b831561132757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561135457611354613623565b0361137257604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561138a5761138a613623565b146113a857604051631860f69960e31b815260040160405180910390fd5b806003015442116113cc57604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806114b1578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611492575f5ffd5b505af11580156114a4573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600a83018190555f816001600160401b038111156114e1576114e16135fb565b60405190808252806020026020018201604052801561150a578160200160208202803683370190505b5090505f5b8281101561157c57846008015f8660060183815481106115315761153161360f565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106115695761156961360f565b602090810291909101015260010161150f565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b1580156115bf575f5ffd5b505af11580156115d1573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ba592919061371d565b611612611e56565b6001600160a01b0381166116395760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff1660038111156116a6576116a6613623565b036116c457604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116dc576116dc613623565b146116fa57604051631860f69960e31b815260040160405180910390fd5b806003015442111561171f57604051639a19114d60e01b815260040160405180910390fd5b335f90815260078201602052604090205460ff16156117515760405163257309f160e11b815260040160405180910390fd5b61175a33610bb9565b6117775760405163149fbcfd60e11b815260040160405180910390fd5b611782338385612186565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526007850160205260409020805460ff1916600117905590915061180190839083612357565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611871576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61188360046014612558565b905090565b5f898152600a602052604090206002815460ff1660038111156118ad576118ad613623565b146118cb57604051634f4b461f60e11b815260040160405180910390fd5b6004810154156118ee5760405163632a22bb60e01b815260040160405180910390fd5b600681015488146119375760405162461bcd60e51b815260206004820152601360248201527209cdec8ca40c6deeadce840dad2e6dac2e8c6d606b1b60448201526064016106d1565b5f61194485870187613874565b9150505f81511161198e5760405162461bcd60e51b815260206004820152601460248201527343353a206e6f207075626c696320696e7075747360601b60448201526064016106d1565b5f816001835161199e9190613917565b815181106119ae576119ae61360f565b602002602001015190505f5f5f9054906101000a90046001600160a01b03166001600160a01b031663406ed35c8e6040518263ffffffff1660e01b81526004016119fa91815260200190565b5f60405180830381865afa158015611a14573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611a3b91908101906139fc565b610120810151604051637bf41d7760e11b81529192506001600160a01b03169063f7e83aee90611a75908b908b908b908b90600401613b9b565b602060405180830381865afa158015611a90573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ab4919061364f565b5060048085018390555f8e815260096020526040808220859055905490516340a3b76160e11b81529182018f9052602482018490526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015611b13575f5ffd5b505af1158015611b25573d5f5f3e3d5ffd5b505050508c7f49ac1dd411942113d1c5e6799c6379ce341afe85a4175fb562cf2a5fb886c27d8d8d8d8d8d8d604051611b6396959493929190613bcc565b60405180910390a250505050505050505050505050565b611b82611e56565b6001600160a01b038116611bab575f604051631e4fbdf760e01b81526004016106d19190613418565b61105a81611e88565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611bf35760405162461bcd60e51b81526004016106d190613c45565b825464ffffffffff600160281b90910481169082168111611c515760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106d1565b825f5b81866001015f611c648488612651565b64ffffffffff1681526020019081526020015f20819055505f816001611c8a9190613c8f565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611cbf5750611e4e565b600185165f03611d86575f611cde83611cd9886001613ca8565b612651565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611d3f91600401613cc5565b602060405180830381865af4158015611d5a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d7e919061364f565b935050611e3a565b5f611d9683611cd9600189613cf5565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611df791600401613cc5565b602060405180830381865af4158015611e12573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e36919061364f565b9350505b50647fffffffff600194851c169301611c54565b505050505050565b33611e5f61105d565b6001600160a01b031614610d76573360405163118cdaa760e01b81526004016106d19190613418565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611f475760405162461bcd60e51b81526004016106d190613c45565b825464ffffffffff90811690821610611f9a5760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106d1565b611fa5816001613ca8565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f611fdc8487612651565b64ffffffffff16815260208101919091526040015f205560018316156120c7575f61200c82611cd9600187613cf5565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161206d91600401613cc5565b602060405180830381865af4158015612088573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120ac919061364f565b647fffffffff600195861c1694909350919091019050611fcc565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610c66565b6120fe61266e565b61105a81612693565b602060ff821611156121555760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106d1565b612166600160ff831681901b613917565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116121a65760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166121cf576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161220591613917565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa15801561224c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612270919061364f565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122c3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122e7919061364f565b90505f81116123095760405163aeaddff160e01b815260040160405180910390fd5b5f6123148284613d12565b90505f81116123365760405163149fbcfd60e11b815260040160405180910390fd5b808611156113275760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156123d557508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526008870182526040808420869055600988019092529120805460ff1916821790559050612551565b5f5f90505f876008015f855f815481106123f1576123f161360f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612479575f896008015f87848154811061243b5761243b61360f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612470578092508193505b5060010161241a565b5080861061248d575f945050505050612551565b5f886009015f8685815481106124a5576124a561360f565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156124e2576124e2613623565b0217905550868483815481106124fa576124fa61360f565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260088a018252604080822089905560098b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116125ab5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106d1565b602060ff831611156125cf5760405162461bcd60e51b81526004016106d190613d31565b8254600160281b900464ffffffffff16806125ee60ff85166002613e82565b64ffffffffff16101561263e5760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106d1565b61264984828561269b565b949350505050565b5f8161266460ff851663ffffffff613e9b565b6125519190613ca8565b612676612763565b610d7657604051631afcd79f60e31b815260040160405180910390fd5b611b8261266e565b5f602060ff831611156126c05760405162461bcd60e51b81526004016106d190613d31565b8264ffffffffff165f036126de576126d78261277c565b9050612551565b5f6126ea836001613c8f565b60ff166001600160401b03811115612704576127046135fb565b60405190808252806020026020018201604052801561272d578160200160208202803683370190505b50905061273c85858584612e16565b808360ff16815181106127515761275161360f565b60200260200101519150509392505050565b5f61276c6120ce565b54600160401b900460ff16919050565b5f8160ff165f0361278e57505f919050565b8160ff166001036127c057507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036127f257507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361282457507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361285657507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361288857507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036128ba57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036128ec57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361291e57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361295057507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361298257507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036129b457507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c036129e657507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612a1857507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612a4a57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612a7c57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612aae57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612ae057507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612b1257507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612b4457507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612b7657507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612ba857507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612bda57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612c0c57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612c3e57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612c7057507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612ca257507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612cd457507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612d0657507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612d3857507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612d6a57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612d9c57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612dce57507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106d1565b602060ff83161115612e3a5760405162461bcd60e51b81526004016106d190613d31565b5f8364ffffffffff1611612e9e5760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106d1565b5f612eaa600185613cf5565b9050600181165f03612efd57846001015f612ec55f84612651565b64ffffffffff1681526020019081526020015f2054825f81518110612eec57612eec61360f565b602002602001018181525050612f25565b612f065f61277c565b825f81518110612f1857612f1861360f565b6020026020010181815250505b5f5b8360ff168160ff161015611e4e57600182165f0361301d5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612f7957612f7961360f565b60200260200101518152602001612f8f8561277c565b8152506040518263ffffffff1660e01b8152600401612fae9190613cc5565b602060405180830381865af4158015612fc9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fed919061364f565b83612ff9836001613c8f565b60ff168151811061300c5761300c61360f565b6020026020010181815250506131ce565b5f613029826001613c8f565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156130cb575f876001015f61308085600161306f9190613c8f565b60018864ffffffffff16901c612651565b64ffffffffff1681526020019081526020015f2054905080858460016130a69190613c8f565b60ff16815181106130b9576130b961360f565b602002602001018181525050506131cc565b5f876001015f6130e285600188611cd99190613cf5565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106131395761313961360f565b60200260200101518152506040518263ffffffff1660e01b81526004016131609190613cc5565b602060405180830381865af415801561317b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061319f919061364f565b856131ab856001613c8f565b60ff16815181106131be576131be61360f565b602002602001018181525050505b505b647fffffffff600192831c169101612f27565b600183019183908215613272579160200282015f5b8382111561324057833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026131f6565b80156132705782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613240565b505b5061327e929150613282565b5090565b5b8082111561327e575f8155600101613283565b6001600160a01b038116811461105a575f5ffd5b5f602082840312156132ba575f5ffd5b813561255181613296565b5f602082840312156132d5575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b828110156133155781516001600160a01b03168652602095860195909101906001016132ee565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613315578151865260209586019590910190600101613331565b604081525f61336160408301856132dc565b8281036020840152613373818561331f565b95945050505050565b5f5f5f6080848603121561338e575f5ffd5b8335925060208401359150608084018510156133a8575f5ffd5b6040840190509250925092565b5f5f604083850312156133c6575f5ffd5b8235915060208301356133d881613296565b809150509250929050565b5f5f5f606084860312156133f5575f5ffd5b83359250602084013561340781613296565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f61255160208301846132dc565b5f5f6040838503121561344f575f5ffd5b823561345a81613296565b946020939093013593505050565b5f5f60408385031215613479575f5ffd5b50508035926020909101359150565b5f5f83601f840112613498575f5ffd5b5081356001600160401b038111156134ae575f5ffd5b6020830191508360208285010111156134c5575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f60a08a8c0312156134e4575f5ffd5b8935985060208a01356001600160401b03811115613500575f5ffd5b8a01601f81018c13613510575f5ffd5b80356001600160401b03811115613525575f5ffd5b8c60208260051b8401011115613539575f5ffd5b6020919091019850965060408a01356001600160401b0381111561355b575f5ffd5b6135678c828d01613488565b90975095505060608a01356001600160401b03811115613585575f5ffd5b6135918c828d01613488565b90955093505060808a01356001600160401b038111156135af575f5ffd5b6135bb8c828d01613488565b915080935050809150509295985092959850929598565b634e487b7160e01b5f52601160045260245ffd5b5f816135f4576135f46135d2565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f60018201613648576136486135d2565b5060010190565b5f6020828403121561365f575f5ffd5b5051919050565b803563ffffffff81168114611871575f5ffd5b5f60208284031215613689575f5ffd5b61255182613666565b80820180821115610c6657610c666135d2565b84815260a0810160208201855f5b60028110156136e05763ffffffff6136ca83613666565b16835260209283019291909101906001016136b3565b50505060608201939093526080015292915050565b80518015158114611871575f5ffd5b5f60208284031215613714575f5ffd5b612551826136f5565b604080825283549082018190525f8481526020812090916060840190835b818110156137625783546001600160a01b031683526001938401936020909301920161373b565b50508381036020850152613776818661331f565b9695505050505050565b6040516101e081016001600160401b03811182821017156137a3576137a36135fb565b60405290565b604051601f8201601f191681016001600160401b03811182821017156137d1576137d16135fb565b604052919050565b5f6001600160401b038211156137f1576137f16135fb565b50601f01601f191660200190565b5f82601f83011261380e575f5ffd5b81356001600160401b03811115613827576138276135fb565b8060051b613837602082016137a9565b91825260208185018101929081019086841115613852575f5ffd5b6020860192505b83831015613776578235825260209283019290910190613859565b5f5f60408385031215613885575f5ffd5b82356001600160401b0381111561389a575f5ffd5b8301601f810185136138aa575f5ffd5b80356138bd6138b8826137d9565b6137a9565b8181528660208385010111156138d1575f5ffd5b816020840160208301375f6020838301015280945050505060208301356001600160401b03811115613901575f5ffd5b61390d858286016137ff565b9150509250929050565b81810381811115610c6657610c666135d2565b805160048110611871575f5ffd5b5f82601f830112613947575f5ffd5b604080519081016001600160401b0381118282101715613969576139696135fb565b806040525080604084018581111561397f575f5ffd5b845b81811015613999578051835260209283019201613981565b509195945050505050565b805161187181613296565b5f82601f8301126139be575f5ffd5b81516139cc6138b8826137d9565b8181528460208386010111156139e0575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f60208284031215613a0c575f5ffd5b81516001600160401b03811115613a21575f5ffd5b82016102008185031215613a33575f5ffd5b613a3b613780565b81518152613a4b6020830161392a565b602082015260408281015190820152613a678560608401613938565b606082015260a08201516080820152613a8260c083016139a4565b60a082015260e08201516001600160401b03811115613a9f575f5ffd5b613aab868285016139af565b60c0830152506101008201516001600160401b03811115613aca575f5ffd5b613ad6868285016139af565b60e083015250613ae961012083016139a4565b610100820152613afc61014083016139a4565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613b32575f5ffd5b613b3e868285016139af565b61018083015250613b526101c083016139a4565b6101a0820152613b656101e083016136f5565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f613bae604083018688613b73565b8281036020840152613bc1818587613b73565b979650505050505050565b606080825281018690525f8760808301825b89811015613c0e578235613bf181613296565b6001600160a01b0316825260209283019290910190600101613bde565b508381036020850152613c2281888a613b73565b9150508281036040840152613c38818587613b73565b9998505050505050505050565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610c6657610c666135d2565b64ffffffffff8181168382160190811115610c6657610c666135d2565b6040810181835f5b6002811015613cec578151835260209283019290910190600101613ccd565b50505092915050565b64ffffffffff8281168282160390811115610c6657610c666135d2565b5f82613d2c57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115610f1757808504811115613d9357613d936135d2565b6001841615613da157908102905b60019390931c928002613d78565b5f82613dbd57506001610c66565b81613dc957505f610c66565b8160018114613ddf5760028114613de957613e1b565b6001915050610c66565b60ff841115613dfa57613dfa6135d2565b6001841b915064ffffffffff821115613e1557613e156135d2565b50610c66565b5060208310610133831016604e8410600b8410161715613e53575081810a64ffffffffff811115613e4e57613e4e6135d2565b610c66565b613e6364ffffffffff8484613d74565b8064ffffffffff04821115613e7a57613e7a6135d2565b029392505050565b5f61255164ffffffffff841664ffffffffff8416613daf565b64ffffffffff8181168382160290811690818114613ebb57613ebb6135d2565b509291505056fea164736f6c634300081c000a", + "deployedBytecode": "0x608060405234801561000f575f5ffd5b506004361061023f575f3560e01c80639f0f874a11610135578063e59e4695116100b4578063f26ef74e11610079578063f26ef74e1461057f578063f2fde38b14610592578063f379b0df146105a5578063f52fd803146105df578063f6fc05d514610651575f5ffd5b8063e59e469514610524578063e6745e1314610537578063e82f3b701461054a578063ebf0c7171461055d578063f165053614610565575f5ffd5b8063c3a0ec30116100fa578063c3a0ec30146104bc578063ca2869a0146104cd578063cd6dc687146104ec578063da881e5a146104ff578063dbb06c9314610512575f5ffd5b80639f0f874a1461044e578063a016493014610457578063a8a4d69b14610477578063bff232c11461048a578063c2b40ae41461049d575f5ffd5b8063715018a6116101c15780638d1ddfb1116101865780638d1ddfb1146103ce5780638da5cb5b146103e45780638e5ce3ad146103ec5780639015d371146103ff5780639a7a2ffc14610412575f5ffd5b8063715018a6146103475780637c92f5241461034f578063858142431461037c5780638a78bb151461039c5780638cb89ecb146103af575f5ffd5b80632e7b716d116102075780632e7b716d146102d95780634d6861a6146102ec57806350e6d94c146102ff5780635d5047761461032157806370e36bbe14610334575f5ffd5b8063096b810a146102435780630f3e34121461025857806317d611201461026b5780632800d82914610295578063291a691b146102b6575b5f5ffd5b6102566102513660046132aa565b61065a565b005b6102566102663660046132c5565b6107a6565b61027e6102793660046132c5565b6107e9565b60405161028c92919061334f565b60405180910390f35b6102a86102a33660046132c5565b610993565b60405190815260200161028c565b6102c96102c436600461337c565b6109df565b604051901515815260200161028c565b6102c96102e73660046132aa565b610bb9565b6102c96102fa3660046132c5565b610c6c565b6102c961030d3660046132aa565b60066020525f908152604090205460ff1681565b6102c961032f3660046133b5565b610cab565b6102566103423660046132aa565b610cef565b610256610d65565b61036261035d3660046133e3565b610d78565b6040805192835263ffffffff90911660208301520161028c565b60015461038f906001600160a01b031681565b60405161028c9190613418565b6102566103aa3660046132aa565b610f1f565b6102a86103bd3660046132c5565b60096020525f908152604090205481565b600454600160281b900464ffffffffff166102a8565b61038f61105d565b600b5461038f906001600160a01b031681565b6102c961040d3660046132aa565b61108b565b6104386104203660046132aa565b60076020525f908152604090205464ffffffffff1681565b60405164ffffffffff909116815260200161028c565b6102a860035481565b61046a6104653660046132c5565b6110a8565b60405161028c919061342c565b6102c96104853660046133b5565b61113e565b6102566104983660046132aa565b611182565b6102a86104ab3660046132c5565b60086020525f908152604090205481565b6001546001600160a01b031661038f565b6102a86104db3660046132c5565b5f9081526008602052604090205490565b6102566104fa36600461343e565b6111d3565b6102c961050d3660046132c5565b611330565b5f5461038f906001600160a01b031681565b6102566105323660046132aa565b61160a565b610256610545366004613468565b611682565b6102a86105583660046132c5565b611845565b6102a8611876565b61056d601481565b60405160ff909116815260200161028c565b61025661058d3660046134cc565b611888565b6102566105a03660046132aa565b611b7a565b6004546105c19064ffffffffff80821691600160281b90041682565b6040805164ffffffffff93841681529290911660208301520161028c565b6106226105ed3660046132c5565b5f908152600a6020819052604090912090810154600590910154909163ffffffff80831692600160201b900416908284101590565b60405161028c949392919093845263ffffffff9283166020850152911660408301521515606082015260800190565b6102a860025481565b61066261105d565b6001600160a01b0316336001600160a01b0316148061068b57506001546001600160a01b031633145b6106a857604051632864c4e160e01b815260040160405180910390fd5b6106b18161108b565b81906106da576040516381e5828960e01b81526004016106d19190613418565b60405180910390fd5b506001600160a01b0381165f9081526007602052604081205464ffffffffff16906107089060049083611bb4565b6001600160a01b0382165f908152600660205260408120805460ff191690556002805491610735836135e6565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f8c008e3835f6c79bfcdb89f0f6ca8705e0b01049ee84a90b0e4da1c7ba9405d5906060015b60405180910390a25050565b6107ae611e56565b60038190556040518181527fbe772dc189863d512fa01e489c8eac204975aef1a8662d8b5a333804b5207ab79060200160405180910390a150565b5f818152600a60208190526040909120600681015491810154606092839291806001600160401b03811115610820576108206135fb565b604051908082528060200260200182016040528015610849578160200160208202803683370190505b509450806001600160401b03811115610864576108646135fb565b60405190808252806020026020018201604052801561088d578160200160208202803683370190505b5093505f805b83811015610989575f8560060182815481106108b1576108b161360f565b5f918252602090912001546001600160a01b0316905060016001600160a01b0382165f90815260098801602052604090205460ff1660028111156108f7576108f7613623565b03610980578088848151811061090f5761090f61360f565b60200260200101906001600160a01b031690816001600160a01b031681525050856008015f826001600160a01b03166001600160a01b031681526020019081526020015f20548784815181106109675761096761360f565b60209081029190910101528261097c81613637565b9350505b50600101610893565b5050505050915091565b5f818152600a6020526040812081815460ff1660038111156109b7576109b7613623565b036109d557604051630d4c1d9760e41b815260040160405180910390fd5b6003015492915050565b5f80546001600160a01b03163314610a0a5760405163e4c2a7eb60e01b815260040160405180910390fd5b5f848152600a6020526040812090815460ff166003811115610a2e57610a2e613623565b14610a4c576040516374ff462560e11b815260040160405180910390fd5b60015460408051630cc37d8f60e11b815290515f926001600160a01b031691631986fb1e9160048083019260209291908290030181865afa158015610a93573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab7919061364f565b905080610aca6040860160208701613679565b63ffffffff161115610ae26040860160208701613679565b829091610b10576040516344ec930f60e01b815263ffffffff909216600483015260248201526044016106d1565b5050815460ff1916600190811783558201859055436002830155600354610b379042613692565b6003830155610b4b600583018560026131e1565b50610b54611876565b5f87815260086020526040908190209190915560028301546003840154915188927f381d281d32f95ef8fe4e5f3b263ea6a32d03d331e1a141ae1da996dc02a7a17092610ba5928a928a92916136a5565b60405180910390a250600195945050505050565b5f610bc38261108b565b610bce57505f919050565b6001546001600160a01b0316610bf7576040516350ca893360e01b815260040160405180910390fd5b600154604051639f8a13d760e01b81526001600160a01b0390911690639f8a13d790610c27908590600401613418565b602060405180830381865afa158015610c42573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c669190613704565b92915050565b5f818152600a602052604081206001815460ff166003811115610c9157610c91613623565b14610c9e57505f92915050565b6003015442111592915050565b5f60015f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff166002811115610ce757610ce7613623565b149392505050565b610cf7611e56565b6001600160a01b038116610d1e5760405163d92e233d60e01b815260040160405180910390fd5b5f80546001600160a01b0319166001600160a01b038316908117825560405190917f2c8267accd82e977550ed2349c73311183cd22e306347be4453c8d130995e3c991a250565b610d6d611e56565b610d765f611e88565b565b600b545f9081906001600160a01b03163314610da75760405163fcef374960e01b815260040160405180910390fd5b5f858152600a602052604090206002815460ff166003811115610dcc57610dcc613623565b14610dea57604051634f4b461f60e11b815260040160405180910390fd5b60058101546001600160a01b0386165f90815260098301602052604090205463ffffffff909116925060019060ff166002811115610e2a57610e2a613623565b14610e3a57600a01549150610f17565b6001600160a01b0385165f9081526009820160205260408120805460ff19166002179055600a8201805491610e6e836135e6565b919050555080600a01549250846001600160a01b0316867f6c783b92374361b4d6efaf29673b89437ee969bb3c9d2d5d86b143ad5447b8498686604051610ebf929190918252602082015260400190565b60405180910390a36040805184815263ffffffff84166020820181905285101591810182905287907f119cb11dd0a68c257d6dc9b06dcb37dd422ce276eb8bf3cd0b7079a116b8e2989060600160405180910390a250505b935093915050565b610f2761105d565b6001600160a01b0316336001600160a01b03161480610f5057506001546001600160a01b031633145b610f6d57604051632864c4e160e01b815260040160405180910390fd5b610f768161108b565b61105a5760048054600160281b900464ffffffffff1690610fa0906001600160a01b038416611ef8565b6001600160a01b0382165f908152600660209081526040808320805460ff1916600117905560079091528120805464ffffffffff841664ffffffffff199091161790556002805491610ff183613637565b90915550506002546004546040805164ffffffffff80861682526020820194909452600160281b909204909216918101919091526001600160a01b038316907f3318d261fe14a5761d2d1e21555652f623d2134c430a9883c9ad6e958bb0db539060600161079a565b50565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03165f9081526006602052604090205460ff1690565b5f818152600a602052604090206004810154606091906110db576040516322e679e360e11b815260040160405180910390fd5b8060060180548060200260200160405190810160405280929190818152602001828054801561113157602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611113575b5050505050915050919050565b5f805f848152600a602090815260408083206001600160a01b038716845260090190915290205460ff16600281111561117957611179613623565b14159392505050565b61118a611e56565b6001600160a01b0381166111b15760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b5f6111dc6120ce565b805490915060ff600160401b82041615906001600160401b03165f811580156112025750825b90505f826001600160401b0316600114801561121d5750303b155b90508115801561122b575080155b156112495760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561127357845460ff60401b1916600160401b1785555b6001600160a01b03871661129a5760405163d92e233d60e01b815260040160405180910390fd5b6112a3336120f6565b6112af60046014612107565b6112b8866107a6565b6112c061105d565b6001600160a01b0316876001600160a01b0316146112e1576112e187611b7a565b831561132757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f818152600a6020526040812081815460ff16600381111561135457611354613623565b0361137257604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff16600381111561138a5761138a613623565b146113a857604051631860f69960e31b815260040160405180910390fd5b806003015442116113cc57604051632f021e8d60e11b815260040160405180910390fd5b60058101546006820154600160201b90910463ffffffff161115806114b1578154600360ff199091161782556006820154600583015460408051928352600160201b90910463ffffffff16602083015285917fecc4a9fb7e28d074cba7f5b227e9b5827823c850a385539b9a2f98a08f8c203d910160405180910390a25f54604051635d968dc160e11b815260048101869052600260248201526001600160a01b039091169063bb2d1b82906044015f604051808303815f87803b158015611492575f5ffd5b505af11580156114a4573d5f5f3e3d5ffd5b505f979650505050505050565b815460ff191660021782556006820154600a83018190555f816001600160401b038111156114e1576114e16135fb565b60405190808252806020026020018201604052801561150a578160200160208202803683370190505b5090505f5b8281101561157c57846008015f8660060183815481106115315761153161360f565b5f9182526020808320909101546001600160a01b0316835282019290925260400190205482518390839081106115695761156961360f565b602090810291909101015260010161150f565b505f54604051631f3ea75d60e21b8152600481018890526001600160a01b0390911690637cfa9d74906024015f604051808303815f87803b1580156115bf575f5ffd5b505af11580156115d1573d5f5f3e3d5ffd5b50505050857f4f1f5b329c741a8ba15e9645e301061294d0c1fdd455448ffd5e76ff255929d78560060183604051610ba592919061371d565b611612611e56565b6001600160a01b0381166116395760405163d92e233d60e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040517fad4055f18cdad6f4bdd71afe3a72cbeee964217943e1bde38f138289e981a9a7905f90a250565b5f828152600a6020526040812090815460ff1660038111156116a6576116a6613623565b036116c457604051630d4c1d9760e41b815260040160405180910390fd5b6001815460ff1660038111156116dc576116dc613623565b146116fa57604051631860f69960e31b815260040160405180910390fd5b806003015442111561171f57604051639a19114d60e01b815260040160405180910390fd5b335f90815260078201602052604090205460ff16156117515760405163257309f160e11b815260040160405180910390fd5b61175a33610bb9565b6117775760405163149fbcfd60e11b815260040160405180910390fd5b611782338385612186565b6001810154604080516bffffffffffffffffffffffff193360601b16602080830191909152603482018690526054820187905260748083019490945282518083039094018452609490910190915281519101205f90335f8181526007850160205260409020805460ff1916600117905590915061180190839083612357565b506040805184815260208101839052339186917f52999628fb1cb05707e842278833b22e511f11746202cecdf221968b0b89e8bd910160405180910390a350505050565b5f8181526009602052604090205480611871576040516322e679e360e11b815260040160405180910390fd5b919050565b5f61188360046014612558565b905090565b5f898152600a602052604090206002815460ff1660038111156118ad576118ad613623565b146118cb57604051634f4b461f60e11b815260040160405180910390fd5b6004810154156118ee5760405163632a22bb60e01b815260040160405180910390fd5b600681015488146119375760405162461bcd60e51b815260206004820152601360248201527209cdec8ca40c6deeadce840dad2e6dac2e8c6d606b1b60448201526064016106d1565b5f61194485870187613874565b9150505f81511161198e5760405162461bcd60e51b815260206004820152601460248201527343353a206e6f207075626c696320696e7075747360601b60448201526064016106d1565b5f816001835161199e9190613917565b815181106119ae576119ae61360f565b602002602001015190505f5f5f9054906101000a90046001600160a01b03166001600160a01b031663406ed35c8e6040518263ffffffff1660e01b81526004016119fa91815260200190565b5f60405180830381865afa158015611a14573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611a3b91908101906139fc565b610120810151604051637bf41d7760e11b81529192506001600160a01b03169063f7e83aee90611a75908b908b908b908b90600401613b9b565b602060405180830381865afa158015611a90573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ab4919061364f565b5060048085018390555f8e815260096020526040808220859055905490516340a3b76160e11b81529182018f9052602482018490526001600160a01b0316906381476ec2906044015f604051808303815f87803b158015611b13575f5ffd5b505af1158015611b25573d5f5f3e3d5ffd5b505050508c7f49ac1dd411942113d1c5e6799c6379ce341afe85a4175fb562cf2a5fb886c27d8d8d8d8d8d8d604051611b6396959493929190613bcc565b60405180910390a250505050505050505050505050565b611b82611e56565b6001600160a01b038116611bab575f604051631e4fbdf760e01b81526004016106d19190613418565b61105a81611e88565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611bf35760405162461bcd60e51b81526004016106d190613c45565b825464ffffffffff600160281b90910481169082168111611c515760405162461bcd60e51b815260206004820152601860248201527713185e9e5253550e881b195859881b5d5cdd08195e1a5cdd60421b60448201526064016106d1565b825f5b81866001015f611c648488612651565b64ffffffffff1681526020019081526020015f20819055505f816001611c8a9190613c8f565b60ff168464ffffffffff16901c64ffffffffff16905060018564ffffffffff16901c64ffffffffff168111611cbf5750611e4e565b600185165f03611d86575f611cde83611cd9886001613ca8565b612651565b60408051808201825286815264ffffffffff83165f90815260018c0160209081529083902054908201529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611d3f91600401613cc5565b602060405180830381865af4158015611d5a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d7e919061364f565b935050611e3a565b5f611d9683611cd9600189613cf5565b60408051808201825264ffffffffff83165f90815260018c0160209081529083902054825281018790529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe91611df791600401613cc5565b602060405180830381865af4158015611e12573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e36919061364f565b9350505b50647fffffffff600194851c169301611c54565b505050505050565b33611e5f61105d565b6001600160a01b031614610d76573360405163118cdaa760e01b81526004016106d19190613418565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b8154600160281b900464ffffffffff167f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018210611f475760405162461bcd60e51b81526004016106d190613c45565b825464ffffffffff90811690821610611f9a5760405162461bcd60e51b815260206004820152601560248201527413185e9e5253550e881d1c9959481a5cc8199d5b1b605a1b60448201526064016106d1565b611fa5816001613ca8565b835464ffffffffff91909116600160281b0269ffffffffff000000000019909116178355815f5b81856001015f611fdc8487612651565b64ffffffffff16815260208101919091526040015f205560018316156120c7575f61200c82611cd9600187613cf5565b60408051808201825264ffffffffff83165f90815260018a0160209081529083902054825281018690529051632b0aac7f60e11b815291925073__$078c82ddf6c95d34ea184ef1dd6130d136$__9163561558fe9161206d91600401613cc5565b602060405180830381865af4158015612088573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120ac919061364f565b647fffffffff600195861c1694909350919091019050611fcc565b5050505050565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610c66565b6120fe61266e565b61105a81612693565b602060ff821611156121555760405162461bcd60e51b81526020600482015260176024820152764c617a79494d543a205472656520746f6f206c6172676560481b60448201526064016106d1565b612166600160ff831681901b613917565b825469ffffffffffffffffffff191664ffffffffff919091161790915550565b5f82116121a65760405163aeaddff160e01b815260040160405180910390fd5b6001546001600160a01b03166121cf576040516350ca893360e01b815260040160405180910390fd5b5f818152600a602052604081206001805460028301549293926001600160a01b039091169163bb03bd7191889161220591613917565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa15801561224c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612270919061364f565b90505f60015f9054906101000a90046001600160a01b03166001600160a01b0316631209b1f66040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122c3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122e7919061364f565b90505f81116123095760405163aeaddff160e01b815260040160405180910390fd5b5f6123148284613d12565b90505f81116123365760405163149fbcfd60e11b815260040160405180910390fd5b808611156113275760405163aeaddff160e01b815260040160405180910390fd5b60058301546006840180545f92600160201b900463ffffffff16908111156123d557508054600180820183555f928352602080842090920180546001600160a01b0319166001600160a01b03881690811790915583526008870182526040808420869055600988019092529120805460ff1916821790559050612551565b5f5f90505f876008015f855f815481106123f1576123f161360f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905060015b8454811015612479575f896008015f87848154811061243b5761243b61360f565b5f9182526020808320909101546001600160a01b03168352820192909252604001902054905082811115612470578092508193505b5060010161241a565b5080861061248d575f945050505050612551565b5f886009015f8685815481106124a5576124a561360f565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff191660018360028111156124e2576124e2613623565b0217905550868483815481106124fa576124fa61360f565b5f91825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918916815260088a018252604080822089905560098b0190925220805460ff191660019081179091559450505050505b9392505050565b5f5f8260ff16116125ab5760405162461bcd60e51b815260206004820152601a60248201527f4c617a79494d543a206465707468206d757374206265203e203000000000000060448201526064016106d1565b602060ff831611156125cf5760405162461bcd60e51b81526004016106d190613d31565b8254600160281b900464ffffffffff16806125ee60ff85166002613e82565b64ffffffffff16101561263e5760405162461bcd60e51b8152602060048201526018602482015277098c2f4f2929aa87440c2dac4d2ceeadeeae640c8cae0e8d60431b60448201526064016106d1565b61264984828561269b565b949350505050565b5f8161266460ff851663ffffffff613e9b565b6125519190613ca8565b612676612763565b610d7657604051631afcd79f60e31b815260040160405180910390fd5b611b8261266e565b5f602060ff831611156126c05760405162461bcd60e51b81526004016106d190613d31565b8264ffffffffff165f036126de576126d78261277c565b9050612551565b5f6126ea836001613c8f565b60ff166001600160401b03811115612704576127046135fb565b60405190808252806020026020018201604052801561272d578160200160208202803683370190505b50905061273c85858584612e16565b808360ff16815181106127515761275161360f565b60200260200101519150509392505050565b5f61276c6120ce565b54600160401b900460ff16919050565b5f8160ff165f0361278e57505f919050565b8160ff166001036127c057507f2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864919050565b8160ff166002036127f257507f1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1919050565b8160ff1660030361282457507f18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238919050565b8160ff1660040361285657507f07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a919050565b8160ff1660050361288857507f2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55919050565b8160ff166006036128ba57507f2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78919050565b8160ff166007036128ec57507f078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d919050565b8160ff1660080361291e57507f2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61919050565b8160ff1660090361295057507f0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747919050565b8160ff16600a0361298257507f1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2919050565b8160ff16600b036129b457507f1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636919050565b8160ff16600c036129e657507f2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a919050565b8160ff16600d03612a1857507f14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0919050565b8160ff16600e03612a4a57507f190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c919050565b8160ff16600f03612a7c57507f22f98aa9ce704152ac17354914ad73ed1167ae6596af510aa5b3649325e06c92919050565b8160ff16601003612aae57507f2a7c7c9b6ce5880b9f6f228d72bf6a575a526f29c66ecceef8b753d38bba7323919050565b8160ff16601103612ae057507f2e8186e558698ec1c67af9c14d463ffc470043c9c2988b954d75dd643f36b992919050565b8160ff16601203612b1257507f0f57c5571e9a4eab49e2c8cf050dae948aef6ead647392273546249d1c1ff10f919050565b8160ff16601303612b4457507f1830ee67b5fb554ad5f63d4388800e1cfe78e310697d46e43c9ce36134f72cca919050565b8160ff16601403612b7657507f2134e76ac5d21aab186c2be1dd8f84ee880a1e46eaf712f9d371b6df22191f3e919050565b8160ff16601503612ba857507f19df90ec844ebc4ffeebd866f33859b0c051d8c958ee3aa88f8f8df3db91a5b1919050565b8160ff16601603612bda57507f18cca2a66b5c0787981e69aefd84852d74af0e93ef4912b4648c05f722efe52b919050565b8160ff16601703612c0c57507f2388909415230d1b4d1304d2d54f473a628338f2efad83fadf05644549d2538d919050565b8160ff16601803612c3e57507f27171fb4a97b6cc0e9e8f543b5294de866a2af2c9c8d0b1d96e673e4529ed540919050565b8160ff16601903612c7057507f2ff6650540f629fd5711a0bc74fc0d28dcb230b9392583e5f8d59696dde6ae21919050565b8160ff16601a03612ca257507f120c58f143d491e95902f7f5277778a2e0ad5168f6add75669932630ce611518919050565b8160ff16601b03612cd457507f1f21feb70d3f21b07bf853d5e5db03071ec495a0a565a21da2d665d279483795919050565b8160ff16601c03612d0657507f24be905fa71335e14c638cc0f66a8623a826e768068a9e968bb1a1dde18a72d2919050565b8160ff16601d03612d3857507f0f8666b62ed17491c50ceadead57d4cd597ef3821d65c328744c74e553dac26d919050565b8160ff16601e03612d6a57507f0918d46bf52d98b034413f4a1a1c41594e7a7a3f6ae08cb43d1a2a230e1959ef919050565b8160ff16601f03612d9c57507f1bbeb01b4c479ecde76917645e404dfa2e26f90d0afc5a65128513ad375c5ff2919050565b8160ff16602003612dce57507f2f68a1c58e257e42a17a6c61dff5551ed560b9922ab119d5ac8e184c9734ead9919050565b60405162461bcd60e51b815260206004820152601e60248201527f4c617a79494d543a2064656661756c745a65726f2062616420696e646578000060448201526064016106d1565b602060ff83161115612e3a5760405162461bcd60e51b81526004016106d190613d31565b5f8364ffffffffff1611612e9e5760405162461bcd60e51b815260206004820152602560248201527f4c617a79494d543a206e756d626572206f66206c6561766573206d7573742062604482015264065203e20360dc1b60648201526084016106d1565b5f612eaa600185613cf5565b9050600181165f03612efd57846001015f612ec55f84612651565b64ffffffffff1681526020019081526020015f2054825f81518110612eec57612eec61360f565b602002602001018181525050612f25565b612f065f61277c565b825f81518110612f1857612f1861360f565b6020026020010181815250505b5f5b8360ff168160ff161015611e4e57600182165f0361301d5773__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280868560ff1681518110612f7957612f7961360f565b60200260200101518152602001612f8f8561277c565b8152506040518263ffffffff1660e01b8152600401612fae9190613cc5565b602060405180830381865af4158015612fc9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fed919061364f565b83612ff9836001613c8f565b60ff168151811061300c5761300c61360f565b6020026020010181815250506131ce565b5f613029826001613c8f565b60ff168664ffffffffff16901c64ffffffffff16905060018364ffffffffff16901c64ffffffffff168111156130cb575f876001015f61308085600161306f9190613c8f565b60018864ffffffffff16901c612651565b64ffffffffff1681526020019081526020015f2054905080858460016130a69190613c8f565b60ff16815181106130b9576130b961360f565b602002602001018181525050506131cc565b5f876001015f6130e285600188611cd99190613cf5565b64ffffffffff1681526020019081526020015f2054905073__$078c82ddf6c95d34ea184ef1dd6130d136$__63561558fe6040518060400160405280848152602001888760ff16815181106131395761313961360f565b60200260200101518152506040518263ffffffff1660e01b81526004016131609190613cc5565b602060405180830381865af415801561317b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061319f919061364f565b856131ab856001613c8f565b60ff16815181106131be576131be61360f565b602002602001018181525050505b505b647fffffffff600192831c169101612f27565b600183019183908215613272579160200282015f5b8382111561324057833563ffffffff1683826101000a81548163ffffffff021916908363ffffffff16021790555092602001926004016020816003010492830192600103026131f6565b80156132705782816101000a81549063ffffffff0219169055600401602081600301049283019260010302613240565b505b5061327e929150613282565b5090565b5b8082111561327e575f8155600101613283565b6001600160a01b038116811461105a575f5ffd5b5f602082840312156132ba575f5ffd5b813561255181613296565b5f602082840312156132d5575f5ffd5b5035919050565b5f8151808452602084019350602083015f5b828110156133155781516001600160a01b03168652602095860195909101906001016132ee565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613315578151865260209586019590910190600101613331565b604081525f61336160408301856132dc565b8281036020840152613373818561331f565b95945050505050565b5f5f5f6080848603121561338e575f5ffd5b8335925060208401359150608084018510156133a8575f5ffd5b6040840190509250925092565b5f5f604083850312156133c6575f5ffd5b8235915060208301356133d881613296565b809150509250929050565b5f5f5f606084860312156133f5575f5ffd5b83359250602084013561340781613296565b929592945050506040919091013590565b6001600160a01b0391909116815260200190565b602081525f61255160208301846132dc565b5f5f6040838503121561344f575f5ffd5b823561345a81613296565b946020939093013593505050565b5f5f60408385031215613479575f5ffd5b50508035926020909101359150565b5f5f83601f840112613498575f5ffd5b5081356001600160401b038111156134ae575f5ffd5b6020830191508360208285010111156134c5575f5ffd5b9250929050565b5f5f5f5f5f5f5f5f5f60a08a8c0312156134e4575f5ffd5b8935985060208a01356001600160401b03811115613500575f5ffd5b8a01601f81018c13613510575f5ffd5b80356001600160401b03811115613525575f5ffd5b8c60208260051b8401011115613539575f5ffd5b6020919091019850965060408a01356001600160401b0381111561355b575f5ffd5b6135678c828d01613488565b90975095505060608a01356001600160401b03811115613585575f5ffd5b6135918c828d01613488565b90955093505060808a01356001600160401b038111156135af575f5ffd5b6135bb8c828d01613488565b915080935050809150509295985092959850929598565b634e487b7160e01b5f52601160045260245ffd5b5f816135f4576135f46135d2565b505f190190565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f60018201613648576136486135d2565b5060010190565b5f6020828403121561365f575f5ffd5b5051919050565b803563ffffffff81168114611871575f5ffd5b5f60208284031215613689575f5ffd5b61255182613666565b80820180821115610c6657610c666135d2565b84815260a0810160208201855f5b60028110156136e05763ffffffff6136ca83613666565b16835260209283019291909101906001016136b3565b50505060608201939093526080015292915050565b80518015158114611871575f5ffd5b5f60208284031215613714575f5ffd5b612551826136f5565b604080825283549082018190525f8481526020812090916060840190835b818110156137625783546001600160a01b031683526001938401936020909301920161373b565b50508381036020850152613776818661331f565b9695505050505050565b6040516101e081016001600160401b03811182821017156137a3576137a36135fb565b60405290565b604051601f8201601f191681016001600160401b03811182821017156137d1576137d16135fb565b604052919050565b5f6001600160401b038211156137f1576137f16135fb565b50601f01601f191660200190565b5f82601f83011261380e575f5ffd5b81356001600160401b03811115613827576138276135fb565b8060051b613837602082016137a9565b91825260208185018101929081019086841115613852575f5ffd5b6020860192505b83831015613776578235825260209283019290910190613859565b5f5f60408385031215613885575f5ffd5b82356001600160401b0381111561389a575f5ffd5b8301601f810185136138aa575f5ffd5b80356138bd6138b8826137d9565b6137a9565b8181528660208385010111156138d1575f5ffd5b816020840160208301375f6020838301015280945050505060208301356001600160401b03811115613901575f5ffd5b61390d858286016137ff565b9150509250929050565b81810381811115610c6657610c666135d2565b805160048110611871575f5ffd5b5f82601f830112613947575f5ffd5b604080519081016001600160401b0381118282101715613969576139696135fb565b806040525080604084018581111561397f575f5ffd5b845b81811015613999578051835260209283019201613981565b509195945050505050565b805161187181613296565b5f82601f8301126139be575f5ffd5b81516139cc6138b8826137d9565b8181528460208386010111156139e0575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f60208284031215613a0c575f5ffd5b81516001600160401b03811115613a21575f5ffd5b82016102008185031215613a33575f5ffd5b613a3b613780565b81518152613a4b6020830161392a565b602082015260408281015190820152613a678560608401613938565b606082015260a08201516080820152613a8260c083016139a4565b60a082015260e08201516001600160401b03811115613a9f575f5ffd5b613aab868285016139af565b60c0830152506101008201516001600160401b03811115613aca575f5ffd5b613ad6868285016139af565b60e083015250613ae961012083016139a4565b610100820152613afc61014083016139a4565b61012082015261016082810151610140830152610180830151908201526101a08201516001600160401b03811115613b32575f5ffd5b613b3e868285016139af565b61018083015250613b526101c083016139a4565b6101a0820152613b656101e083016136f5565b6101c0820152949350505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f613bae604083018688613b73565b8281036020840152613bc1818587613b73565b979650505050505050565b606080825281018690525f8760808301825b89811015613c0e578235613bf181613296565b6001600160a01b0316825260209283019290910190600101613bde565b508381036020850152613c2281888a613b73565b9150508281036040840152613c38818587613b73565b9998505050505050505050565b6020808252602a908201527f4c617a79494d543a206c656166206d757374206265203c20534e41524b5f53436040820152691053105497d19251531160b21b606082015260800190565b60ff8181168382160190811115610c6657610c666135d2565b64ffffffffff8181168382160190811115610c6657610c666135d2565b6040810181835f5b6002811015613cec578151835260209283019290910190600101613ccd565b50505092915050565b64ffffffffff8281168282160390811115610c6657610c666135d2565b5f82613d2c57634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526023908201527f4c617a79494d543a206465707468206d757374206265203c3d204d41585f44456040820152620a0a8960eb1b606082015260800190565b6001815b6001841115610f1757808504811115613d9357613d936135d2565b6001841615613da157908102905b60019390931c928002613d78565b5f82613dbd57506001610c66565b81613dc957505f610c66565b8160018114613ddf5760028114613de957613e1b565b6001915050610c66565b60ff841115613dfa57613dfa6135d2565b6001841b915064ffffffffff821115613e1557613e156135d2565b50610c66565b5060208310610133831016604e8410600b8410161715613e53575081810a64ffffffffff811115613e4e57613e4e6135d2565b610c66565b613e6364ffffffffff8484613d74565b8064ffffffffff04821115613e7a57613e7a6135d2565b029392505050565b5f61255164ffffffffff841664ffffffffff8416613daf565b64ffffffffff8181168382160290811690818114613ebb57613ebb6135d2565b509291505056fea164736f6c634300081c000a", "linkReferences": { "npm/poseidon-solidity@0.0.5/PoseidonT3.sol": { "PoseidonT3": [ { "length": 20, - "start": 7553 + "start": 7663 }, { "length": 20, - "start": 7737 + "start": 7847 }, { "length": 20, - "start": 8367 + "start": 8477 }, { "length": 20, - "start": 12201 + "start": 12311 }, { "length": 20, - "start": 12643 + "start": 12753 } ] } @@ -1292,28 +1297,28 @@ "PoseidonT3": [ { "length": 20, - "start": 7339 + "start": 7449 }, { "length": 20, - "start": 7523 + "start": 7633 }, { "length": 20, - "start": 8153 + "start": 8263 }, { "length": 20, - "start": 11987 + "start": 12097 }, { "length": 20, - "start": 12429 + "start": 12539 } ] } }, "immutableReferences": {}, "inputSourceName": "project/contracts/registry/CiphernodeRegistryOwnable.sol", - "buildInfoId": "solc-0_8_28-fd853fea4a7c18ebe6d1db07eac4a0f86f797607" + "buildInfoId": "solc-0_8_28-67d7bf420ed8ac25001394841682e83cf4523ea2" } \ No newline at end of file diff --git a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json index 65becc23d6..295b45fae6 100644 --- a/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json +++ b/packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json @@ -1223,5 +1223,5 @@ ] }, "inputSourceName": "project/contracts/token/EnclaveTicketToken.sol", - "buildInfoId": "solc-0_8_28-3f0df73226c7ff72a6c756321d5f230fe39ed6a0" + "buildInfoId": "solc-0_8_28-67d7bf420ed8ac25001394841682e83cf4523ea2" } \ No newline at end of file diff --git a/packages/enclave-contracts/contracts/Enclave.sol b/packages/enclave-contracts/contracts/Enclave.sol index 0945f27aaa..f0edca06fd 100644 --- a/packages/enclave-contracts/contracts/Enclave.sol +++ b/packages/enclave-contracts/contracts/Enclave.sol @@ -437,7 +437,7 @@ contract Enclave is IEnclave, OwnableUpgradeable { /// Any division dust is sent to the last member rather than being lost. /// @param e3Id The ID of the E3 for which to distribute rewards. function _distributeRewards(uint256 e3Id) internal { - address[] memory activeNodes = ciphernodeRegistry + (address[] memory activeNodes, ) = ciphernodeRegistry .getActiveCommitteeNodes(e3Id); uint256 activeLength = activeNodes.length; @@ -535,7 +535,8 @@ contract Enclave is IEnclave, OwnableUpgradeable { // Use active committee nodes (already filtered by expulsion) try ciphernodeRegistry.getActiveCommitteeNodes(e3Id) returns ( - address[] memory nodes + address[] memory nodes, + uint256[] memory ) { return nodes; } catch { diff --git a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol index 6298a4df90..d6a38193a5 100644 --- a/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol @@ -422,9 +422,10 @@ interface ICiphernodeRegistry { /// @notice Get active (non-expelled) committee nodes for an E3 /// @param e3Id ID of the E3 computation /// @return nodes Array of active committee member addresses + /// @return scores Array of active committee member ticket scores aligned with `nodes` function getActiveCommitteeNodes( uint256 e3Id - ) external view returns (address[] memory nodes); + ) external view returns (address[] memory nodes, uint256[] memory scores); /// @notice Consolidated committee viability check — avoids two separate view calls. /// @param e3Id ID of the E3 computation diff --git a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol index e67ae0c1af..d5aeff1f90 100644 --- a/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol +++ b/packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol @@ -189,7 +189,8 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { } /// @notice Publishes a committee for an E3 computation - /// @dev Only callable by owner. Verification of C5 proof is done in Enclave.onCommitteePublished. + /// @dev Permissionless once the committee is finalized. Verification of C5 proof is + /// done in Enclave.onCommitteePublished. /// @param e3Id ID of the E3 computation /// @param nodes Array of ciphernode addresses selected for the committee /// @param publicKey Aggregated public key of the committee @@ -201,7 +202,7 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { bytes calldata publicKey, bytes calldata proof, bytes calldata foldProof - ) external onlyOwner { + ) external { Committee storage c = committees[e3Id]; require( @@ -552,23 +553,24 @@ contract CiphernodeRegistryOwnable is ICiphernodeRegistry, OwnableUpgradeable { /// @inheritdoc ICiphernodeRegistry function getActiveCommitteeNodes( uint256 e3Id - ) external view returns (address[] memory) { + ) external view returns (address[] memory nodes, uint256[] memory scores) { Committee storage c = committees[e3Id]; uint256 total = c.topNodes.length; uint256 actCount = c.activeCount; - address[] memory activeNodes = new address[](actCount); + nodes = new address[](actCount); + scores = new uint256[](actCount); uint256 idx = 0; for (uint256 i = 0; i < total; ++i) { + address node = c.topNodes[i]; if ( - c.memberStatus[c.topNodes[i]] == - ICiphernodeRegistry.MemberStatus.Active + c.memberStatus[node] == ICiphernodeRegistry.MemberStatus.Active ) { - activeNodes[idx] = c.topNodes[i]; + nodes[idx] = node; + scores[idx] = c.scoreOf[node]; idx++; } } - return activeNodes; } /// @inheritdoc ICiphernodeRegistry diff --git a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol index ee3a608b60..545d61770e 100644 --- a/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol +++ b/packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol @@ -162,8 +162,9 @@ contract MockCiphernodeRegistry is ICiphernodeRegistry { function getActiveCommitteeNodes( uint256 - ) external pure returns (address[] memory) { - return new address[](0); + ) external pure returns (address[] memory nodes, uint256[] memory scores) { + nodes = new address[](0); + scores = new uint256[](0); } function getCommitteeViability( @@ -284,8 +285,9 @@ contract MockCiphernodeRegistryEmptyKey is ICiphernodeRegistry { function getActiveCommitteeNodes( uint256 - ) external pure returns (address[] memory) { - return new address[](0); + ) external pure returns (address[] memory nodes, uint256[] memory scores) { + nodes = new address[](0); + scores = new uint256[](0); } function getCommitteeViability( diff --git a/packages/enclave-contracts/deployed_contracts.json b/packages/enclave-contracts/deployed_contracts.json index dfd3ccc67a..358966c089 100644 --- a/packages/enclave-contracts/deployed_contracts.json +++ b/packages/enclave-contracts/deployed_contracts.json @@ -151,21 +151,21 @@ }, "localhost": { "PoseidonT3": { - "blockNumber": 4, + "blockNumber": 6, "address": "0x3333333C0A88F9BE4fd23ed0536F9B6c427e3B93" }, "MockUSDC": { "constructorArgs": { "initialSupply": "1000000" }, - "blockNumber": 5, + "blockNumber": 7, "address": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512" }, "EnclaveToken": { "constructorArgs": { "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 6, + "blockNumber": 8, "address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" }, "EnclaveTicketToken": { @@ -174,14 +174,14 @@ "registry": "0x0000000000000000000000000000000000000001", "owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 8, + "blockNumber": 10, "address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" }, "SlashingManager": { "constructorArgs": { "admin": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, - "blockNumber": 9, + "blockNumber": 11, "address": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707" }, "CiphernodeRegistryOwnable": { @@ -196,7 +196,7 @@ "proxyAdminAddress": "0x9bd03768a7DCc129555dE410FF8E85528A4F88b5", "implementationAddress": "0x0165878A594ca255338adfa4d48449f69242Eb8F" }, - "blockNumber": 10, + "blockNumber": 12, "address": "0xa513E6E4b8f2a923D98304ec87F64353C4D5C853" }, "BondingRegistry": { @@ -218,7 +218,7 @@ "proxyAdminAddress": "0x8aCd85898458400f7Db866d53FCFF6f0D49741FF", "implementationAddress": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" }, - "blockNumber": 11, + "blockNumber": 13, "address": "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318" }, "Enclave": { @@ -241,7 +241,7 @@ "proxyAdminAddress": "0x8dAF17A20c9DBA35f005b6324F493785D239719d", "implementationAddress": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" }, - "blockNumber": 14, + "blockNumber": 16, "address": "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e" }, "E3RefundManager": { @@ -257,24 +257,24 @@ "proxyAdminAddress": "0x32467b43BFa67273FC7dDda0999Ee9A12F2AaA08", "implementationAddress": "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0" }, - "blockNumber": 16, + "blockNumber": 18, "address": "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82" }, "MockComputeProvider": { - "blockNumber": 18, - "address": "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E" + "blockNumber": 20, + "address": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" }, "MockDecryptionVerifier": { - "blockNumber": 19, - "address": "0xc3e53F4d16Ae77Db1c982e75a937B9f60FE63690" + "blockNumber": 21, + "address": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB" }, "MockPkVerifier": { - "blockNumber": 20, - "address": "0x84eA74d481Ee0A5332c457a4d796187F6Ba67fEB" + "blockNumber": 22, + "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" }, "MockE3Program": { - "blockNumber": 21, - "address": "0x9E545E3C0baAB3E08CdfD552C960A1050f373042" + "blockNumber": 23, + "address": "0xa82fF9aFd8f496c3d6ac40E2a0F282E47488CFc9" } } } \ No newline at end of file diff --git a/packages/enclave-contracts/hardhat.config.ts b/packages/enclave-contracts/hardhat.config.ts index b0826c6d9a..549ffb0a61 100644 --- a/packages/enclave-contracts/hardhat.config.ts +++ b/packages/enclave-contracts/hardhat.config.ts @@ -21,6 +21,9 @@ import { } from "./tasks/ciphernode"; import { enableE3, + getActiveAggregator, + getCommitteePublicKey, + getPlaintextOutput, publishCiphertext, publishCommittee, publishPlaintext, @@ -92,9 +95,12 @@ const config: HardhatUserConfig = { ciphernodeMintTokens, ciphernodeRemove, requestCommittee, + getCommitteePublicKey, + getActiveAggregator, publishPlaintext, publishCiphertext, publishCommittee, + getPlaintextOutput, publishInput, enableE3, cleanDeploymentsTask, diff --git a/packages/enclave-contracts/package.json b/packages/enclave-contracts/package.json index c294358eee..d0795e61c9 100644 --- a/packages/enclave-contracts/package.json +++ b/packages/enclave-contracts/package.json @@ -170,6 +170,9 @@ "ciphernode:mint-tokens": "hardhat ciphernode:mint-tokens", "ciphernode:remove": "hardhat ciphernode:remove", "committee:new": "hardhat committee:new", + "committee:get-public-key": "hardhat committee:getPublicKey", + "committee:get-active-aggregator": "hardhat committee:getActiveAggregator", + "e3:get-plaintext": "hardhat e3:getPlaintext", "lint": "solhint --disc --max-warnings 10 \"contracts/**/*.sol\"", "prettier:check": "prettier --check \"**/*.{js,json,md,sol,ts,yml}\"", "prettier:write": "prettier --write \"**/*.{js,json,md,sol,ts,yml}\"", diff --git a/packages/enclave-contracts/tasks/enclave.ts b/packages/enclave-contracts/tasks/enclave.ts index 5274de08c6..3e234e5b2a 100644 --- a/packages/enclave-contracts/tasks/enclave.ts +++ b/packages/enclave-contracts/tasks/enclave.ts @@ -7,9 +7,68 @@ import { BigNumberish, ZeroAddress, zeroPadValue } from "ethers"; import fs from "fs"; import { task } from "hardhat/config"; import { ArgumentType } from "hardhat/types/arguments"; +import path from "path"; import { readDeploymentArgs } from "../scripts/utils"; +function ensureParentDir(filePath: string): void { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); +} + +function decodePlaintextBytesToCsv(bytes: Uint8Array): string { + if (bytes.length % 8 !== 0) { + throw new Error("Plaintext output length must be a multiple of 8 bytes"); + } + + const values: string[] = []; + for (let index = 0; index < bytes.length; index += 8) { + let value = 0n; + for (let offset = 0; offset < 8; offset++) { + value |= BigInt(bytes[index + offset]!) << BigInt(offset * 8); + } + values.push(value.toString()); + } + + return values.join(","); +} + +async function getRegistryConnection(hre: any) { + const { ethers } = await hre.network.connect(); + const [signer] = await ethers.getSigners(); + const chain = hre.globalOptions.network; + const deployment = readDeploymentArgs("CiphernodeRegistryOwnable", chain); + + if (!deployment?.address) { + throw new Error("CiphernodeRegistryOwnable deployment not found"); + } + + return { + ethers, + deployment, + registry: await ethers.getContractAt( + "CiphernodeRegistryOwnable", + deployment.address, + signer, + ), + }; +} + +async function getEnclaveConnection(hre: any) { + const { ethers } = await hre.network.connect(); + const [signer] = await ethers.getSigners(); + const chain = hre.globalOptions.network; + const deployment = readDeploymentArgs("Enclave", chain); + + if (!deployment?.address) { + throw new Error("Enclave deployment not found"); + } + + return { + ethers, + enclave: await ethers.getContractAt("Enclave", deployment.address, signer), + }; +} + export const requestCommittee = task( "committee:new", "Request a new ciphernode committee, will use E3 mock contracts by default", @@ -315,6 +374,95 @@ export const publishCommittee = task( })) .build(); +export const getCommitteePublicKey = task( + "committee:getPublicKey", + "Read the latest published committee public key for an E3", +) + .addOption({ + name: "e3Id", + description: "Id of the E3 program", + defaultValue: 0, + type: ArgumentType.INT, + }) + .addOption({ + name: "outFile", + description: "file to write the raw committee public key bytes to", + defaultValue: "", + type: ArgumentType.STRING, + }) + .setAction(async () => ({ + default: async ({ e3Id, outFile }, hre) => { + const { ethers, deployment, registry } = await getRegistryConnection(hre); + const filter = registry.filters.CommitteePublished(e3Id); + const logs = await registry.queryFilter( + filter, + deployment.blockNumber ?? 0, + "latest", + ); + const event = logs.at(-1) as any; + + if (!event) { + throw new Error(`CommitteePublished event not found for e3Id=${e3Id}`); + } + + const publicKey = (event.args.publicKey ?? event.args[2]) as string; + if (!publicKey || publicKey === "0x") { + throw new Error(`Committee public key is empty for e3Id=${e3Id}`); + } + + if (outFile) { + ensureParentDir(outFile); + fs.writeFileSync(outFile, Buffer.from(ethers.getBytes(publicKey))); + } + + console.log(publicKey); + }, + })) + .build(); + +export const getActiveAggregator = task( + "committee:getActiveAggregator", + "Read the active aggregator address for an E3", +) + .addOption({ + name: "e3Id", + description: "Id of the E3 program", + defaultValue: 0, + type: ArgumentType.INT, + }) + .setAction(async () => ({ + default: async ({ e3Id }, hre) => { + const { registry } = await getRegistryConnection(hre); + const [activeNodes, activeScores]: [string[], bigint[]] = + await registry.getActiveCommitteeNodes(e3Id); + + if (activeNodes.length !== activeScores.length) { + throw new Error( + `Mismatched active committee data for e3Id=${e3Id}: nodes=${activeNodes.length}, scores=${activeScores.length}`, + ); + } + + if (activeNodes.length === 0) { + throw new Error(`No active committee nodes found for e3Id=${e3Id}`); + } + + const [activeAggregator] = activeNodes + .map((node, index) => ({ + node, + score: activeScores[index], + index, + })) + .sort((left, right) => { + if (left.score < right.score) return -1; + if (left.score > right.score) return 1; + return left.index - right.index; + }); + + console.log(activeAggregator.node); + }, + })) + .build(); + export const publishCiphertext = task( "e3:publishCiphertext", "Publish ciphertext output for an E3 program", @@ -480,3 +628,42 @@ export const publishPlaintext = task( }, })) .build(); + +export const getPlaintextOutput = task( + "e3:getPlaintext", + "Read the published plaintext output for an E3", +) + .addOption({ + name: "e3Id", + description: "Id of the E3 program", + defaultValue: 0, + type: ArgumentType.INT, + }) + .addOption({ + name: "outFile", + description: "file to write the decoded plaintext CSV output to", + defaultValue: "", + type: ArgumentType.STRING, + }) + .setAction(async () => ({ + default: async ({ e3Id, outFile }, hre) => { + const { ethers, enclave } = await getEnclaveConnection(hre); + const e3 = await enclave.getE3(e3Id); + + if (!e3.plaintextOutput || e3.plaintextOutput === "0x") { + throw new Error(`Plaintext output not published for e3Id=${e3Id}`); + } + + const decoded = decodePlaintextBytesToCsv( + ethers.getBytes(e3.plaintextOutput), + ); + + if (outFile) { + ensureParentDir(outFile); + fs.writeFileSync(outFile, decoded); + } + + console.log(decoded); + }, + })) + .build(); diff --git a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts index 858ce78925..1af2dc7c51 100644 --- a/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts +++ b/packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts @@ -408,7 +408,7 @@ describe("CiphernodeRegistryOwnable", function () { }); describe("publishCommittee()", function () { - it("reverts if the caller is not the owner", async function () { + it("allows any caller to publish a finalized committee", async function () { const { registry, enclave, @@ -446,7 +446,18 @@ describe("CiphernodeRegistryOwnable", function () { c5Proof, "0x", ), - ).to.be.revertedWithCustomError(registry, "OwnableUnauthorizedAccount"); + ) + .to.emit(registry, "CommitteePublished") + .withArgs( + 0, + [ + await operator1.getAddress(), + await operator2.getAddress(), + await operator3.getAddress(), + ], + data, + c5Proof, + ); }); it("stores the public key of the committee", async function () { const { @@ -535,6 +546,44 @@ describe("CiphernodeRegistryOwnable", function () { }); }); + describe("getActiveCommitteeNodes()", function () { + it("returns active committee nodes with their scores", async function () { + const { + registry, + enclave, + usdcToken, + mockE3Program, + mockDecryptionVerifier, + operator1, + operator2, + operator3, + } = await loadFixture(setup); + await makeRequest( + enclave, + usdcToken, + mockE3Program, + mockDecryptionVerifier, + ); + + await registry.connect(operator1).submitTicket(0, 1); + await registry.connect(operator2).submitTicket(0, 1); + await registry.connect(operator3).submitTicket(0, 1); + await finalizeCommitteeAfterWindow(registry, 0); + + const finalizedEvents = await registry.queryFilter( + registry.filters.CommitteeFinalized(0), + ); + expect(finalizedEvents.length).to.equal(1); + + const finalizedEvent = finalizedEvents[0]; + const [activeNodes, activeScores] = + await registry.getActiveCommitteeNodes(0); + + expect(activeNodes).to.deep.equal(finalizedEvent.args.committee); + expect(activeScores).to.deep.equal(finalizedEvent.args.scores); + }); + }); + describe("addCiphernode()", function () { it("reverts if the caller is not the owner", async function () { const { registry, notTheOwner } = await loadFixture(setup); diff --git a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts index fe26883230..00357d35bc 100644 --- a/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts +++ b/packages/enclave-contracts/test/Slashing/CommitteeExpulsion.spec.ts @@ -647,10 +647,19 @@ describe("Committee Expulsion & Fault Tolerance", function () { ]); // Before expulsion: all 3 should be in active nodes - const nodesBefore = await registry.getActiveCommitteeNodes(0); + const [nodesBefore, scoresBefore] = + await registry.getActiveCommitteeNodes(0); expect(nodesBefore.length).to.equal(3); + expect(scoresBefore.length).to.equal(3); expect(nodesBefore).to.include(await operator1.getAddress()); + const scoreByNode = new Map( + nodesBefore.map((node, index) => [ + node.toLowerCase(), + scoresBefore[index], + ]), + ); + // Expel operator1 const proof = await signAndEncodeAttestation( [operator2, operator3], @@ -664,11 +673,18 @@ describe("Committee Expulsion & Fault Tolerance", function () { ); // After expulsion: only 2 should be active - const nodesAfter = await registry.getActiveCommitteeNodes(0); + const [nodesAfter, scoresAfter] = + await registry.getActiveCommitteeNodes(0); expect(nodesAfter.length).to.equal(2); + expect(scoresAfter.length).to.equal(2); expect(nodesAfter).to.not.include(await operator1.getAddress()); expect(nodesAfter).to.include(await operator2.getAddress()); expect(nodesAfter).to.include(await operator3.getAddress()); + scoresAfter.forEach((score, index) => { + expect(score).to.equal( + scoreByNode.get(nodesAfter[index].toLowerCase()), + ); + }); }); }); diff --git a/templates/default/enclave.config.yaml b/templates/default/enclave.config.yaml index 143048a50a..6d6ce9fe18 100644 --- a/templates/default/enclave.config.yaml +++ b/templates/default/enclave.config.yaml @@ -53,11 +53,3 @@ nodes: ctrl_port: 50505 autonetkey: true autopassword: true - ag: - address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" - quic_port: 9206 - ctrl_port: 50506 - autonetkey: true - autopassword: true - role: - type: "aggregator" diff --git a/templates/default/scripts/dev_ciphernodes.sh b/templates/default/scripts/dev_ciphernodes.sh index 97339c349f..37133c1dab 100755 --- a/templates/default/scripts/dev_ciphernodes.sh +++ b/templates/default/scripts/dev_ciphernodes.sh @@ -24,14 +24,12 @@ pnpm wait-on tcp:localhost:8545 rm -rf .enclave/data rm -rf .enclave/config -PRIVATE_KEY_AG="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" PRIVATE_KEY_CN1="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" PRIVATE_KEY_CN2="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" PRIVATE_KEY_CN3="0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6" PRIVATE_KEY_CN4="0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a" PRIVATE_KEY_CN5="0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba" -enclave wallet set --name ag --private-key "$PRIVATE_KEY_AG" enclave wallet set --name cn1 --private-key "$PRIVATE_KEY_CN1" enclave wallet set --name cn2 --private-key "$PRIVATE_KEY_CN2" enclave wallet set --name cn3 --private-key "$PRIVATE_KEY_CN3" diff --git a/tests/integration/base.sh b/tests/integration/base.sh index 16b9c22548..5134ab0748 100755 --- a/tests/integration/base.sh +++ b/tests/integration/base.sh @@ -20,8 +20,6 @@ done pnpm evm:clean pnpm evm:deploy --network localhost -# set wallet to ag specifically -enclave_wallet_set ag "$PRIVATE_KEY_AG" enclave_wallet_set cn1 "$PRIVATE_KEY_CN1" enclave_wallet_set cn2 "$PRIVATE_KEY_CN2" enclave_wallet_set cn3 "$PRIVATE_KEY_CN3" @@ -76,7 +74,7 @@ pnpm committee:new \ --e3-params "$ENCODED_PARAMS" \ --committee-size 0 -waiton "$SCRIPT_DIR/output/pubkey.bin" +wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" heading "Mock encrypted plaintext" $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" @@ -91,7 +89,7 @@ waiton "$SCRIPT_DIR/output/output.bin" heading "Publish ciphertext to EVM" pnpm e3:publishCiphertext --e3-id 0 --network localhost --data-file "$SCRIPT_DIR/output/output.bin" --proof 0x12345678 -waiton "$SCRIPT_DIR/output/plaintext.txt" +wait_for_plaintext_output 0 "$SCRIPT_DIR/output/plaintext.txt" ACTUAL=$(cut -d',' -f1,2 $SCRIPT_DIR/output/plaintext.txt) diff --git a/tests/integration/enclave.config.yaml b/tests/integration/enclave.config.yaml index 3dc7463498..142f2ca87b 100644 --- a/tests/integration/enclave.config.yaml +++ b/tests/integration/enclave.config.yaml @@ -63,13 +63,3 @@ nodes: ctrl_port: 50505 autonetkey: true autopassword: true - ag: - address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" - quic_port: 9206 - ctrl_port: 50506 - autonetkey: true - autopassword: true - role: - type: aggregator - pubkey_write_path: "./output/pubkey.bin" - plaintext_write_path: "./output/plaintext.txt" diff --git a/tests/integration/fns.sh b/tests/integration/fns.sh index 2b24b9c1de..f4594e5741 100644 --- a/tests/integration/fns.sh +++ b/tests/integration/fns.sh @@ -15,7 +15,6 @@ fi # Environment variables RPC_URL="ws://localhost:8545" -PRIVATE_KEY_AG="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" PRIVATE_KEY_CN1="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" PRIVATE_KEY_CN2="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" PRIVATE_KEY_CN3="0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6" @@ -99,6 +98,83 @@ waiton-files() { done } + wait_for_committee_pubkey() { + local e3_id="$1" + local out_file="$2" + local timeout="${3:-1300}" + local start_time=$(date +%s) + + while true; do + if pnpm committee:get-public-key --network localhost --e3-id "$e3_id" --out-file "$out_file" >/dev/null 2>&1; then + return 0 + fi + + if [ $(($(date +%s) - start_time)) -ge "$timeout" ]; then + echo "Timeout after ${timeout}s waiting for committee public key for e3=${e3_id}" >&2 + return 1 + fi + + sleep 1 + done + } + + wait_for_plaintext_output() { + local e3_id="$1" + local out_file="$2" + local timeout="${3:-1300}" + local start_time=$(date +%s) + + while true; do + if pnpm e3:get-plaintext --network localhost --e3-id "$e3_id" --out-file "$out_file" >/dev/null 2>&1; then + return 0 + fi + + if [ $(($(date +%s) - start_time)) -ge "$timeout" ]; then + echo "Timeout after ${timeout}s waiting for plaintext output for e3=${e3_id}" >&2 + return 1 + fi + + sleep 1 + done + } + + wait_for_active_aggregator_address() { + local e3_id="$1" + local timeout="${2:-1300}" + local start_time=$(date +%s) + + while true; do + local output="" + if output=$(pnpm committee:get-active-aggregator --network localhost --e3-id "$e3_id" 2>/dev/null); then + echo "$output" | tail -n 1 + return 0 + fi + + if [ $(($(date +%s) - start_time)) -ge "$timeout" ]; then + echo "Timeout after ${timeout}s waiting for active aggregator for e3=${e3_id}" >&2 + return 1 + fi + + sleep 1 + done + } + + node_name_for_address() { + local address="${1,,}" + + case "$address" in + "${CIPHERNODE_ADDRESS_1,,}") echo "cn1" ;; + "${CIPHERNODE_ADDRESS_2,,}") echo "cn2" ;; + "${CIPHERNODE_ADDRESS_3,,}") echo "cn3" ;; + "${CIPHERNODE_ADDRESS_4,,}") echo "cn4" ;; + "${CIPHERNODE_ADDRESS_5,,}") echo "cn5" ;; + *) + echo "Unknown ciphernode address: $1" >&2 + return 1 + ;; + esac + } + enclave_password_set() { local name="$1" local password="$2" diff --git a/tests/integration/persist.sh b/tests/integration/persist.sh index 20fce089cd..c7311fa327 100755 --- a/tests/integration/persist.sh +++ b/tests/integration/persist.sh @@ -20,8 +20,6 @@ done pnpm evm:clean pnpm evm:deploy --network localhost -# set wallet to ag specifically -enclave_wallet_set ag "$PRIVATE_KEY_AG" enclave_wallet_set cn1 "$PRIVATE_KEY_CN1" enclave_wallet_set cn2 "$PRIVATE_KEY_CN2" enclave_wallet_set cn3 "$PRIVATE_KEY_CN3" @@ -71,17 +69,28 @@ pnpm committee:new \ --committee-size 0 \ --proof-aggregation-enabled true -waiton "$SCRIPT_DIR/output/pubkey.bin" +wait_for_committee_pubkey 0 "$SCRIPT_DIR/output/pubkey.bin" -# kill aggregator -enclave_nodes_stop ag +ACTIVE_AGG_ADDRESS=$(wait_for_active_aggregator_address 0) +if ! ACTIVE_AGG=$(node_name_for_address "$ACTIVE_AGG_ADDRESS"); then + echo "Failed to resolve active aggregator node name for address: $ACTIVE_AGG_ADDRESS" >&2 + exit 1 +fi + +if [[ -z "$ACTIVE_AGG" ]]; then + echo "Resolved empty active aggregator node name for address: $ACTIVE_AGG_ADDRESS" >&2 + exit 1 +fi + +# kill active aggregator +enclave_nodes_stop "$ACTIVE_AGG" -sleep 8 +sleep 15 -# relaunch the aggregator -enclave_nodes_start ag +# relaunch the active aggregator +enclave_nodes_start "$ACTIVE_AGG" -sleep 8 +sleep 5 heading "Mock encrypted plaintext" $SCRIPT_DIR/lib/fake_encrypt.sh --input "$SCRIPT_DIR/output/pubkey.bin" --output "$SCRIPT_DIR/output/output.bin" --plaintext $PLAINTEXT --params "$ENCODED_PARAMS" @@ -96,7 +105,7 @@ waiton "$SCRIPT_DIR/output/output.bin" heading "Publish ciphertext to EVM" pnpm e3:publishCiphertext --e3-id 0 --network localhost --data-file "$SCRIPT_DIR/output/output.bin" --proof 0x12345678 -waiton "$SCRIPT_DIR/output/plaintext.txt" +wait_for_plaintext_output 0 "$SCRIPT_DIR/output/plaintext.txt" ACTUAL=$(cut -d',' -f1,2 $SCRIPT_DIR/output/plaintext.txt)