decdn-incentive 0.0.2

Payment channels, bonding, and voucher management for deCDN
Documentation
//! `ContentBlacklist` contract ABI bindings (ADR 011 § Content Takedown).
//!
//! Generated by the `alloy::sol!` macro from the Solidity surface in
//! `contracts/src/ContentBlacklist.sol`. The node's blacklist compliance
//! watcher ([`crate`] consumer `decdn-node::blacklist_watcher`) consumes the
//! read path (`isHashBlacklistedForOperator`) plus the `HashBlacklisted` /
//! `HashRemoved` membership events: each event is a *signal to re-read* the
//! authoritative in-scope predicate for the hashes the node holds, then evict
//! any that are blacklisted for this operator (global ∪ current-region ∪
//! ripening-prev-region — the region logic lives entirely inside the contract
//! view, so the watcher never repacks a region).
//!
//! The write + `getHashEntry` surface is mirrored for the cross-layer e2e
//! harness (`decdn-e2e`), which drives governance blacklist adds and reads
//! `addedAt` to time the compliance / slashability windows.

// The `sol!`-generated bindings include macro-emitted code that uses patterns
// workspace clippy denies (raw indexing into ABI fixed-size byte arrays,
// `unwrap` on infallible conversions). These allows scope the relaxation to
// this module only.
#[allow(
    clippy::expect_used,
    clippy::indexing_slicing,
    clippy::panic,
    clippy::unwrap_used,
    clippy::pub_underscore_fields,
    clippy::missing_docs_in_private_items,
    clippy::too_many_arguments,
    missing_debug_implementations,
    missing_docs,
    non_snake_case,
    non_camel_case_types
)]
mod sol_types {
    alloy::sol! {
        #[sol(rpc)]
        contract ContentBlacklist {
            /// Per-(region, hash) entry. `addedAt == 0` means "not blacklisted".
            ///
            /// MUST match `ContentBlacklist.HashEntry` field-for-field and in
            /// order — a static struct return is ABI-identical to the flattened
            /// tuple, so a missing field silently mis-decodes rather than
            /// failing loudly.
            ///
            /// `effectiveAt` (`addedAt + complianceWindow`, ADR 011 § Compliance
            /// Window) is the slashability boundary, NOT `addedAt`: an entry is
            /// unenforceable during the grace period a node needs to learn of it.
            /// `emergency` + `category` carry the auto-expiry term for entries
            /// added by the emergency multisig.
            struct HashEntry {
                uint64 addedAt;
                uint64 effectiveAt;
                bool emergency;
                uint8 category;
            }

            /// Region-scoped regional-body record (ADR 011 § Regional Governance
            /// Bodies). `body == address(0)` means no body is registered.
            struct RegionalBody {
                address body;
                uint64 suspendedAt;
                bool suspensionRatified;
            }

            // -----------------------------------------------------------------
            // View functions (consumed by the node runtime)
            // -----------------------------------------------------------------

            /// True iff `hash` is an enforceable blacklist entry in scope for
            /// `operator`: global ∪ current-region ∪ (ripening-window)
            /// previous-region, per ADR 030. The single source of truth for the
            /// watcher's scope decision — the region packing / ripening math
            /// stays on-chain.
            function isHashBlacklistedForOperator(bytes32 hash, address operator)
                external
                view
                returns (bool);

            /// Raw entry for `(region, hash)`. Used by the e2e harness to time
            /// windows; the node relies on the operator-scope predicate above
            /// instead.
            function getHashEntry(bytes32 region, bytes32 hash)
                external
                view
                returns (HashEntry memory);

            /// True while `origin` is denied at the origin level, honouring
            /// emergency auto-expiry (a raw mapping read would not).
            ///
            /// The per-address boot reconcile relies on this predicate, which is usable
            /// because the deny-set is enumerable and has something to iterate over.
            ///
            /// This alone is NOT the address-liveness predicate: it reads only
            /// `_isOriginBlacklisted` and returns `false` for an OPERATOR-only
            /// entry (`addOperator` sets `isOperatorBlacklisted` and never touches
            /// the origin mapping). A boot reconcile of the `blacklistedAddresses`
            /// union MUST OR this with `isOperatorBlacklisted` — the same
            /// disjunction `ContentBlacklist._syncAddr` / `OriginAssignment` use —
            /// or it drops every voted-out operator (#1499).
            function isOriginBlacklisted(address origin) external view returns (bool);

            /// The operator half of the address deny-set (`addOperator` /
            /// `removeOperator`). Public auto-getter for the `mapping(address =>
            /// bool)`; there is no emergency auto-expiry on this leg, so — unlike
            /// [`isOriginBlacklisted`] — a raw read IS the live value. The boot
            /// reconcile ORs the two: `isOriginBlacklisted(a) ||
            /// isOperatorBlacklisted(a)`.
            function isOperatorBlacklisted(address operator) external view returns (bool);

            /// Every region key whose entries are in scope for `operator` right
            /// now: `GLOBAL` (always first), the declared region, and — while the
            /// ADR 030 ripening window is open — the previous one. One to three
            /// keys. Resolved on-chain through the same internal the slash-
            /// eligibility predicate uses, so the region packing and ripening
            /// math cannot drift between the two.
            function getScopeRegions(address operator) external view returns (bytes32[] memory);

            /// Number of entries indexed under `region`. Companion to
            /// `blacklistedHashes`; RAW membership, matching that view.
            function blacklistedHashCount(bytes32 region) external view returns (uint256);

            /// A page of `region`'s entry set. A page shorter than `limit` is the
            /// end of the set.
            ///
            /// Returns RAW membership — an emergency entry past its deadline is
            /// still listed, because filtering would put holes in a paginated
            /// view and dropping entries is the fail-open direction. Apply
            /// `isHashBlacklistedForOperator` (or `getHashEntry`) for liveness.
            ///
            /// Order is NOT stable across mutations (swap-and-pop removal), so
            /// every page — and the count it is checked against — MUST be read at
            /// one pinned block height, or a concurrent removal can move an
            /// unread element into an already-read slot and skip it.
            function blacklistedHashes(bytes32 region, uint256 offset, uint256 limit)
                external
                view
                returns (bytes32[] memory);

            /// Number of addresses denied at the origin or operator level.
            function blacklistedAddressCount() external view returns (uint256);

            /// A page of the UNION of the origin and operator deny lists — the
            /// same disjunction `OriginAssignment` evaluates per address, and the
            /// only readable source for that set. Same raw-membership and
            /// pinned-block caveats as `blacklistedHashes`.
            function blacklistedAddresses(uint256 offset, uint256 limit)
                external
                view
                returns (address[] memory);

            // -----------------------------------------------------------------
            // Write functions (governance — driven by the e2e harness)
            // -----------------------------------------------------------------

            /// Add `hash` to the global blacklist (`GOVERNANCE_ROLE`). `reason`
            /// is the free-form on-chain audit-trail note (DMCA/DSA notice id).
            function addHashGlobal(bytes32 hash, string reason) external;

            /// Add `hash` to `region`'s blacklist (`REGIONAL_BODY_ROLE`). `reason`
            /// is the free-form on-chain audit-trail note (DMCA/DSA notice id).
            function addHashRegional(bytes32 region, bytes32 hash, string reason) external;

            /// Remove `hash` from the global blacklist (`GOVERNANCE_ROLE`).
            function removeHashGlobal(bytes32 hash) external;

            /// Bind `body` to `region` and grant it `REGIONAL_BODY_ROLE`
            /// (`GOVERNANCE_ROLE`). `emergencyMultisig` is the
            /// `EMERGENCY_MULTISIG_ROLE` holder to check signer-disjointness
            /// against; it is verified to hold the role.
            function registerRegionalBody(bytes32 region, address body, address emergencyMultisig)
                external;

            /// The body registered for `region`, plus its suspension state.
            function getRegionalBody(bytes32 region) external view returns (RegionalBody memory);

            /// Grace (seconds) between a standard/regional add and the moment the
            /// entry becomes slashable (ADR 011 § Compliance Window).
            function complianceWindow() external view returns (uint64);

            // -----------------------------------------------------------------
            // Membership events (watcher re-read signals)
            // -----------------------------------------------------------------

            /// A `(region, hash)` entry was added (or re-added). `region` is
            /// `bytes32("GLOBAL")` for the global scope.
            ///
            /// These declarations MUST match `ContentBlacklist.sol` field-for-field,
            /// including parameters this watcher never reads. `topic0` is the keccak of
            /// the event signature, and the non-indexed params are the ABI-encoded data
            /// payload — so a drift in either breaks the watcher twice over: it matches
            /// no logs at all, and would mis-decode any it did match. It fails SILENTLY,
            /// as an empty deny-set, which reads exactly like "nothing is blacklisted"
            /// while the node serves content it is slashable for serving.
            ///
            /// `reason` (the audit-trail note) is unused here for that reason alone:
            /// it is part of the signature, so it is part of this declaration.
            event HashBlacklisted(bytes32 indexed region, bytes32 indexed hash, string reason);

            /// A `(region, hash)` entry was removed.
            event HashRemoved(bytes32 indexed region, bytes32 indexed hash);

            /// An origin/operator address entered or left the origin blacklist
            /// (ADR 011 § Hash Evasion and Origin Blacklisting).
            ///
            /// Like the hash events, it carries no version counter: a consumer
            /// detects a missed one by re-enumerating, not from the event.
            ///
            /// The set is enumerable via [`blacklistedAddresses`], so a consumer holds
            /// the deny-set by enumeration and treats this event as a low-latency signal
            /// to re-read rather than the source of truth; the boot reconcile draws on
            /// the same enumeration.
            event OriginBlacklistUpdated(address indexed origin, bool blacklisted);

            /// Governance blacklisted an OPERATOR address (`addOperator`), which
            /// also ejects it from `CapacityBond`. This is a SECOND, independent
            /// origin-level list: `addOperator` writes `isOperatorBlacklisted`
            /// and never touches `_isOriginBlacklisted`, so a consumer that
            /// watches only `OriginBlacklistUpdated` misses the primary
            /// governance path entirely. `OriginAssignment` treats the two as a
            /// union (`isOriginBlacklisted(op) || isOperatorBlacklisted(op)`);
            /// so must the node.
            event OperatorBlacklisted(address indexed operator);

            /// `removeOperator` — the inverse of `OperatorBlacklisted`.
            event OperatorBlacklistCleared(address indexed operator);
        }
    }
}

pub use sol_types::ContentBlacklist;