From 3d9450749fe20a49bb1bae8de85580f13b0641e6 Mon Sep 17 00:00:00 2001 From: Josh Burroughs Date: Mon, 28 Sep 2026 13:31:38 -0400 Subject: [PATCH 1/9] libfc(ethereum): let a confirmed write fund itself to a gas-limit floor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `create_default_tx` sizes every transaction from `eth_estimateGas` plus a buffer. That is right for a call that either does its work or reverts, and wrong for one that stops on a `gasleft()` watchdog and records where it stopped: the estimator converges on the least gas at which such a call SUCCEEDS — the smallest amount of work it can do and still return cleanly — so the estimate systematically under-funds it. `ethereum_confirm_options::gas_limit_floor` lets the caller of a `create_tx_and_confirm` wrapper fund the call to a floor instead. The floor is applied after the buffered estimate and is subject to the same policy ceiling: a floor above `max_gas_limit` is a policy rejection (`gas_limit_cap_exceeded`, field `gas_limit_floor`), never a silently clamped limit. Co-Authored-By: Claude Fable 5.1 Change-Id: I72d029cda7d08eb19b921a971af7af8c7f4c922a --- .../fc/network/ethereum/ethereum_client.hpp | 19 ++++++++++++++-- .../src/network/ethereum/ethereum_client.cpp | 22 +++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp b/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp index ed1ba05f3a..509f7a904c 100644 --- a/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp +++ b/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp @@ -39,6 +39,19 @@ struct ethereum_confirm_options { /// Retry / backoff / deadline envelope — shared with the Solana client. /// See `fc::task::retry_options` for per-field semantics. fc::task::retry_options retry = fc::task::retry_option_defaults; + + /// Least gas limit the transaction is funded with, applied AFTER the + /// buffered estimate; `0` leaves the estimate alone. + /// + /// For a call whose execution stops on a `gasleft()` watchdog and records + /// where it stopped rather than reverting, `eth_estimateGas` converges on + /// the least gas at which the call SUCCEEDS — the smallest amount of work it + /// can do and still return cleanly — so the estimate systematically + /// under-funds it. A floor lets the caller fund such a call to what the + /// chain allows instead. The floor is still subject to the client's + /// `max_gas_limit`: a floor above the policy ceiling is a policy rejection, + /// never a silently clamped limit. + uint64_t gas_limit_floor = 0; }; inline constexpr ethereum_confirm_options ethereum_confirm_option_defaults{}; @@ -569,10 +582,12 @@ class ethereum_client : public std::enable_shared_from_this { * @param to Recipient address (contract address for contract calls) * @param contract ABI contract definition for encoding the call data * @param params Parameters to pass to the contract function + * @param gas_limit_floor Least gas limit to fund the transaction with, or 0 + * to use the buffered estimate; see `ethereum_confirm_options::gas_limit_floor` * @return Configured eip1559_tx ready for signing and submission */ eip1559_tx create_default_tx(const address_compat_type& to, const abi::contract& contract, - const fc::variants& params = {}); + const fc::variants& params = {}, uint64_t gas_limit_floor = 0); /** * @brief Gets or creates a typed contract client instance @@ -700,7 +715,7 @@ ethereum_contract_tx_fn ethereum_contract_client::create_tx_and_con abi::contract& abi = abi_map[contract.name]; return [this, &abi, opts](const Args&... args) -> RT { contract_invoke_data_items params = {args...}; - auto tx = client->create_default_tx(contract_address, abi, params); + auto tx = client->create_default_tx(contract_address, abi, params, opts.gas_limit_floor); auto res_var = client->execute_contract_tx_fn(tx, abi, params); // `execute_contract_tx_fn` returns the submitted tx hash. Await diff --git a/libraries/libfc/src/network/ethereum/ethereum_client.cpp b/libraries/libfc/src/network/ethereum/ethereum_client.cpp index eded061d17..d5eafe8a3f 100644 --- a/libraries/libfc/src/network/ethereum/ethereum_client.cpp +++ b/libraries/libfc/src/network/ethereum/ethereum_client.cpp @@ -28,6 +28,7 @@ constexpr std::string_view base_fee_per_gas = "base_fee_per_gas"; constexpr std::string_view max_priority_fee_per_gas = "max_priority_fee_per_gas"; constexpr std::string_view estimated_gas = "estimated_gas"; constexpr std::string_view gas_price = "gas_price"; +constexpr std::string_view gas_limit_floor = "gas_limit_floor"; } // namespace transaction_policy_field namespace ethereum_rpc_field { @@ -286,23 +287,40 @@ fc::variant ethereum_client::get_syncing_status() { * Constructs a complete EIP-1559 transaction by: * - Fetching current gas configuration (base fee, priority fee) * - Encoding the contract call data according to the ABI - * - Estimating gas usage and adding a 20% buffer + * - Estimating gas usage and adding a 20% buffer, then raising the result to + * `gas_limit_floor` when the caller funds the call to a floor * - Setting the nonce from the pending transaction count * * @param to The recipient address (contract address for contract calls) * @param contract The ABI contract definition for encoding the call data * @param params The parameters to pass to the contract function + * @param gas_limit_floor Least gas limit to fund with, or 0 for the buffered estimate * @return A configured eip1559_tx ready for signing and submission * @throws fc::network::json_rpc::json_rpc_exception if any RPC call fails + * @throws ethereum_transaction_policy_exception if the estimate or the floor + * breaches the client's `max_gas_limit` */ eip1559_tx ethereum_client::create_default_tx(const address_compat_type& to, const abi::contract& contract, - const fc::variants& params) { + const fc::variants& params, uint64_t gas_limit_floor) { try { auto gc = get_gas_config_unlogged(); auto data = contract_encode_data(contract, params); auto estimated_gas = estimate_gas(to, contract, data, gc); auto gas_limit = derive_buffered_gas_limit(_transaction_policy, estimated_gas); + if (gas_limit_floor != 0) { + // The floor is bounded by the same ceiling as the estimate: a caller + // asking for more than the policy allows is refused, not clamped, so + // an under-funded call is never silently sent. + const fc::uint256 floor{gas_limit_floor}; + if (floor > _transaction_policy.max_gas_limit) { + throw_transaction_policy_exception(ethereum_transaction_policy_reason::gas_limit_cap_exceeded, + transaction_policy_field::gas_limit_floor, + floor.str(), + _transaction_policy.max_gas_limit.str()); + } + if (gas_limit < floor) gas_limit = floor; + } return eip1559_tx{.chain_id = get_chain_id(), .nonce = get_transaction_count(get_signer_address(), block_tag_t::pending), From a35902d295258aa65e1b55b95d95944b0cd9aa33 Mon Sep 17 00:00:00 2001 From: Josh Burroughs Date: Mon, 28 Sep 2026 13:31:38 -0400 Subject: [PATCH 2/9] outpost-eth: deliver whole envelopes as calldata and continue them from the outpost's cursor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Ethereum outpost no longer takes an envelope in 8 KiB chunks staged on chain. `OPPInbound.epochIn(uint32 epochIndex, bytes envelopeData)` takes the WHOLE envelope in one call — Ethereum bounds a transaction by gas, not size, and a full-cap envelope is ~1.3 M gas of calldata — and what may not fit is dispatch, which the contract spills across continuations of the same call. The outpost never stores the bytes; every continuation re-supplies them. THE RELAY DELIVERS OR CONTINUES. Each tick reads the outpost's own cursor (`nextEpochIndex`, `dispatchSpill(epoch)`) at `latest` and decides: - finalized, or the epoch cursor is past it -> nothing to send - tipped on the digest THIS relay delivered -> re-supply the envelope - tipped on a digest it did not deliver -> its deliverers continue - not tipped -> deliver `decide_delivery` is the pure table; the loop around it continues a spilling epoch within the tick until the outpost reports it finalized, bounded by the deadline and `MAX_CONTINUATIONS_PER_TICK`. A delivery that records without tipping stops the tick rather than re-sending a paid no-op; the job's boundary-gated retry still covers path-2 consensus. Whether this relay's delivery is the settled one is read from `epochDeliveries(epoch, self)` against `pendingEpochHash()`, because the contract admits a continuation only from a deliverer of the settled digest. FUNDED TO THE CEILING, NOT THE ESTIMATE. `epochIn` stops dispatching on a `gasleft()` watchdog instead of reverting, so `eth_estimateGas` converges on the tip plus ONE attestation and would spill every call after one attestation. The wrapper is built with `gas_limit_floor` = the client's policy ceiling bounded by EIP-7825's cap (`delivery_gas_limit_floor`), so one call carries as much dispatch as the chain allows; the unused remainder is refunded. Retired with the chunk loop: `ETHEREUM_MAX_CHUNK_BYTES`, `chunk_count_for`, the staging-header resume read, `discardEnvelopeChunks`. The ABI fixtures are refreshed from the rebuilt wire-ethereum artifacts (`epochIn(uint32,bytes)`, selector 004e356a), and the plugin tests exercise every branch of the decision table plus deadline abandonment and resumption against a stubbed OPPInbound. Co-Authored-By: Claude Fable 5.1 Change-Id: I8543310904e320d13101aee4c7c2bca6936ffc73 --- .../src/batch_operator_plugin.cpp | 2 +- .../sysio/outpost_ethereum_client_plugin.hpp | 94 +- .../outpost_ethereum_client.hpp | 186 ++-- .../src/outpost_ethereum_client.cpp | 520 +++++------ .../test/test_ethereum_transaction_policy.cpp | 66 +- .../test_outpost_ethereum_client_plugin.cpp | 851 +++++++----------- tests/fixtures/ethereum-abi-opp-current.json | 399 +++++++- .../ethereum-abi-opp-inbound-current.json | 750 ++++++++------- 8 files changed, 1510 insertions(+), 1358 deletions(-) diff --git a/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp b/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp index 025435226c..6220093a33 100644 --- a/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp +++ b/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp @@ -1943,7 +1943,7 @@ void batch_operator_plugin::set_program_options(options_description& cli, "How often to check epoch state (ms)"); // SIZING RULE for batch-delivery-timeout-ms: it bounds the WHOLE outbound // delivery, and an Ethereum delivery is now one transaction PER CHUNK - // (ETHEREUM_MAX_CHUNK_BYTES = 8192; Solana chunks at 672). Size it as + // (whole-envelope on Ethereum, continued while it spills; Solana chunks at 672). Size it as // total chunks x (target block time + confirmation margin) // for the largest envelope the outpost is expected to carry. At the 32768 // byte platform envelope cap that is 4 Ethereum chunks, so the 15000 ms diff --git a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp index a8e4aec9ee..2bf46328a2 100644 --- a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp +++ b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp @@ -56,36 +56,74 @@ struct opp_contract_client : ethereum_contract_client { : ethereum_contract_call_fn{}) {} }; +/// EIP-7825's per-transaction gas cap (2^24) — the most any single `epochIn` +/// can be funded with, whatever the client's policy allows. +inline constexpr uint64_t EIP_7825_TX_GAS_CAP = 16'777'216; + +/** + * @brief Gas an `epochIn` transaction is funded with: the client's policy + * ceiling, bounded by EIP-7825's cap. + * + * Delivery is funded to a FLOOR rather than to the node's estimate because the + * estimate is systematically wrong for this call. `OPPInbound.epochIn` stops + * dispatching on a `gasleft()` watchdog and records where it stopped instead + * of reverting, so `eth_estimateGas` converges on the least gas at which the + * call SUCCEEDS — the consensus tip plus ONE attestation. Funded to that + * figure every call would spill after one attestation and an envelope would + * cost one transaction per attestation. Funded to the ceiling, one call + * carries as much dispatch as the chain allows and the unused remainder is + * refunded. + * + * @param policy The client's local expenditure policy. + * @return The floor, in gas. + */ +inline uint64_t delivery_gas_limit_floor(const ethereum_transaction_policy& policy) { + const fc::uint256 cap{EIP_7825_TX_GAS_CAP}; + return policy.max_gas_limit < cap ? policy.max_gas_limit.convert_to() : EIP_7825_TX_GAS_CAP; +} + +/** + * @brief Confirmation options for `epochIn`: the defaults, funded to + * `delivery_gas_limit_floor`. + * @param client The client whose policy sets the floor; null (an ABI-only + * construction) leaves the floor at zero. + * @return The options the `epochIn` wrapper is built with. + */ +inline ethereum_confirm_options delivery_confirm_options(const ethereum_client_ptr& client) { + ethereum_confirm_options options = ethereum_confirm_option_defaults; + if (client) options.gas_limit_floor = delivery_gas_limit_floor(client->transaction_policy()); + return options; +} + /// Typed contract client for OPPInbound.sol. Same confirmed-default -/// policy as `opp_contract_client` for write paths. +/// policy as `opp_contract_client` for the write path. struct opp_inbound_contract_client : ethereum_contract_client { - /// `epochIn(uint32 epochIndex, uint16 chunkIndex, uint16 totalChunks, - /// uint32 totalBytes, bytes chunkData)` — ONE chunk of the - /// envelope. The contract stages every chunk but the last and finalizes - /// inline on the chunk that completes the envelope; there is no terminal - /// call and no crank. `chunkData` rides as a hex-encoded string because the - /// libfc ABI encoder takes `dt::bytes` that way (see + /// `epochIn(uint32 epochIndex, bytes envelopeData)` — the WHOLE envelope in + /// ONE call, addressed to its epoch. The call that reaches consensus + /// dispatches as many attestations as its gas allows and records where it + /// stopped; a continuation is the SAME call with the same arguments, which + /// the contract resumes from its cursor. Funded to the policy ceiling — see + /// `delivery_gas_limit_floor`. `envelopeData` rides as a hex-encoded string + /// because the libfc ABI encoder takes `dt::bytes` that way (see /// `ethereum_abi::encode_dynamic_data`). /// /// `ethereum_contract_tx_fn` binds every argument as a non-const lvalue - /// reference, so callers must materialize named locals for all five. - ethereum_contract_tx_fn epoch_in; - /// `discardEnvelopeChunks()` — staged-owner-only recovery, resetting every - /// header this signer owns. Invoked by the relay when it finds a - /// CURRENT-epoch staging header whose shape belongs to a superseded - /// envelope; reverts (`OPP_ChunkBufferMissing`) when nothing is staged, - /// which the relay treats as "already clear". - ethereum_contract_tx_fn discard_envelope_chunks; + /// reference, so callers must materialize named locals for both. + ethereum_contract_tx_fn epoch_in; /// `nextEpochIndex()` view — the epoch the outpost is currently accepting. ethereum_contract_call_fn next_epoch_index; - /// `envelopeChunkState(address operator_)` view — the OWNER-BOUND staging - /// header, the relay's resume read. Block tag rides first per - /// `ethereum_contract_call_fn`; the address argument is a hex string. - /// Returns the raw `eth_call` hex — `create_call` does not - /// auto-decode, so the caller pushes it back through `contract_decode_data` - /// against this ABI entry (the same shape `read_inbound_envelope` uses for - /// `getLatestOutboundEnvelope`). - ethereum_contract_call_fn envelope_chunk_state; + /// `dispatchSpill(uint32 epochIndex)` view — where a tipped epoch's dispatch + /// stands: `(tipped, dispatched, complete, finalized)`. Block tag rides + /// first per `ethereum_contract_call_fn`. Returns the raw `eth_call` hex — + /// `create_call` does not auto-decode, so the caller pushes it + /// back through `contract_decode_data` against this ABI entry. + ethereum_contract_call_fn dispatch_spill; + /// `epochDeliveries(uint32 epochIndex, address operator_)` view — the digest + /// `operator_` delivered for the epoch, or zero when it has not delivered. + ethereum_contract_call_fn epoch_deliveries; + /// `pendingEpochHash()` view — the digest consensus settled on for the epoch + /// the outpost is processing. + ethereum_contract_call_fn pending_epoch_hash; /// `attestationHandlers(uint16 attestationType)` view — the outpost's own /// inbound routing table: the `IOPPReceiver` registered for one attestation /// type, `address(0)` when none is and `ATTESTATION_BLACKHOLE` when governance @@ -99,12 +137,12 @@ struct opp_inbound_contract_client : ethereum_contract_client { const address_compat_type& contract_address, const std::vector& contracts) : ethereum_contract_client(client, contract_address, contracts) - , epoch_in(create_tx_and_confirm( - get_abi("epochIn"))) - , discard_envelope_chunks( - create_tx_and_confirm(get_abi("discardEnvelopeChunks"))) + , epoch_in(create_tx_and_confirm( + get_abi("epochIn"), delivery_confirm_options(client))) , next_epoch_index(create_call(get_abi("nextEpochIndex"))) - , envelope_chunk_state(create_call(get_abi("envelopeChunkState"))) + , dispatch_spill(create_call(get_abi("dispatchSpill"))) + , epoch_deliveries(create_call(get_abi("epochDeliveries"))) + , pending_epoch_hash(create_call(get_abi("pendingEpochHash"))) , attestation_handlers(create_call(get_abi("attestationHandlers"))) {} }; diff --git a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp index cfa7009121..03d686db2f 100644 --- a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp +++ b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp @@ -14,115 +14,57 @@ namespace sysio { -/// Per-`epochIn` chunk payload limit on Ethereum. -/// -/// MIRROR DUTY — compiled into BOTH sides and never configured: this value must -/// equal `MAX_CHUNK_BYTES` in wire-ethereum's `contracts/outpost/OPPCommon.sol` -/// (re-declared as the public constant `OPPInbound.MAX_CHUNK_BYTES` so the -/// hardhat drift-guard suite can pin it). Deliveries carrying more than this in -/// a single transaction were demonstrated to break the Ethereum envelope -/// exchange, so `deliver_outbound_envelope` splits an envelope into -/// `ceil(size / ETHEREUM_MAX_CHUNK_BYTES)` transactions: every non-final chunk -/// is EXACTLY this many bytes and the final chunk carries the remainder. -/// -/// A multiple of 32 so the contract's staging-cell writes stay word-aligned. -/// Against `OPP_MAX_ENVELOPE_BYTES` this yields at most -/// `ceil(32'768 / 8'192) == 4` chunks — and there is no terminal call: the -/// contract finalizes inline on the chunk that completes the envelope. This is -/// the Ethereum analogue of `SOLANA_MAX_CHUNK_BYTES` (672). -inline constexpr size_t ETHEREUM_MAX_CHUNK_BYTES = 8'192; - namespace outpost_ethereum_client_detail { -/// Number of `epochIn` transactions one envelope of `total_bytes` costs. -/// Ceil-division, matching `OPPInbound`'s own `totalChunks` validation — the -/// contract rejects any delivery whose declared `totalChunks` differs. -constexpr uint16_t chunk_count_for(size_t total_bytes) { - return static_cast((total_bytes + ETHEREUM_MAX_CHUNK_BYTES - 1) / - ETHEREUM_MAX_CHUNK_BYTES); -} - -/// Decoded `OPPInbound.envelopeChunkState(address)` view result — the -/// owner-bound staging header this operator has (or has not) left on chain. -/// -/// A never-staged / fully-reclaimed header decodes as all-zero, which the -/// contract spells `totalChunks == SLOT_EMPTY (0)`; `decide_chunk_resume` -/// treats that as "nothing to adopt". -struct envelope_chunk_state { - /// WIRE epoch the staged header belongs to. - uint32_t epoch_index = 0; - /// Staging owner, as the ABI decoder returns it (lower-case `0x`-hex), or - /// empty when the read failed / no header exists. - std::string owner; - /// Declared chunk count of the staged envelope; 0 == empty header. - uint16_t total_chunks = 0; - /// Chunks actually STORED on chain. The final chunk is never stored, so - /// this never reaches `total_chunks` for a well-formed header. - uint16_t received_chunks = 0; - /// Declared total envelope size of the staged envelope. - uint32_t total_bytes = 0; - /// Sum of the stored cells' lengths — diagnostics only. - uint64_t stored_bytes = 0; -}; - -/// What the relay should do about the staging header it just read. -enum class chunk_resume_action { - /// Nothing adoptable on chain — send every chunk from index 0. - start_fresh, - /// Our own header for this exact epoch and shape — continue from its - /// high-water mark. - resume, - /// Our own CURRENT-epoch header with a different shape (a superseded - /// envelope for the same epoch) — `discardEnvelopeChunks()` first, then - /// send from index 0. - discard_and_restart, - /// Our own header for a DIFFERENT epoch — read `nextEpochIndex()` to find - /// out whether the epoch under delivery has already been consumed; if it - /// has, the delivery is skipped entirely (it would only buy late-no-op gas). - confirm_epoch_advanced +/// Decoded `OPPInbound.dispatchSpill(uint32)` view result — where a tipped +/// epoch's processing stands. All-false/zero for an epoch that has not tipped, +/// which is also what an unreadable response decodes to. +struct dispatch_spill { + /// Consensus settled the epoch's digest and dispatch began. + bool tipped = false; + /// Attestations routed so far — also where the next continuation resumes. + uint16_t dispatched = 0; + /// Every attestation has been routed. + bool complete = false; + /// The outbound envelope has been emitted and `nextEpochIndex` advanced. + bool finalized = false; }; -/// `decide_chunk_resume`'s verdict. -struct chunk_resume_decision { - chunk_resume_action action = chunk_resume_action::start_fresh; - /// First chunk index to send. Meaningful for `resume`; 0 for every other - /// action. - uint16_t start_chunk = 0; +/// What the relay should do about the epoch it is holding, given the outpost's +/// view of that epoch. +enum class delivery_action { + /// The epoch has not tipped: deliver the envelope — a first delivery, or + /// the idempotent same-digest re-delivery a path-2 consensus retry is. + deliver, + /// The epoch tipped on THIS relay's digest and is not finished: re-supply + /// the envelope so the outpost dispatches the next stretch. + continue_dispatch, + /// The epoch tipped on a digest this relay did not deliver. Only a relay + /// whose recorded delivery matches the settled digest may continue it, so + /// there is nothing for this one to send. + wait_for_deliverer, + /// The outpost has already finalized this epoch: nothing to send. + already_finalized }; /// Compare two EVM addresses for identity, tolerating an absent `0x` prefix /// and EIP-55 checksum casing on either side. bool same_evm_address(std::string_view lhs, std::string_view rhs); -/// True when `revert_data` is exactly `OPP_ChunkBufferMissing(owner_address)`. +/// Decide the relay's next move for `epoch_index`. /// -/// `discardEnvelopeChunks()` raises that error, and only that error, when it -/// finds nothing of ours to clear — the one revert meaning the staging header -/// is already in the state the caller wanted. Every other revert (a wrong -/// implementation behind the proxy, a reentrancy guard, an unrecognised -/// selector) must not be mistaken for it. +/// Pure and side-effect free so the table is unit-testable without an EVM +/// node; the caller performs whichever RPC the verdict names. /// -/// @param revert_data `json_rpc_error::data`, the node's revert bytes as -/// `0x`-hex; empty or truncated data returns false. -/// @param owner_address the signer this relay expects the contract to name. -bool is_chunk_buffer_missing_revert(std::string_view revert_data, std::string_view owner_address); - -/// Decide how to resume (or abandon) a chunked delivery given the on-chain -/// staging header. -/// -/// Pure and side-effect free so the decision table is unit-testable without an -/// EVM node; the caller performs whichever RPC the returned action names. -/// -/// @param staged header read back from `envelopeChunkState(self)`. -/// @param self_address this relay's own signer address. -/// @param epoch_index WIRE epoch being delivered. -/// @param total_chunks chunk count of the envelope being delivered. -/// @param total_bytes size of the envelope being delivered. -chunk_resume_decision decide_chunk_resume(const envelope_chunk_state& staged, - std::string_view self_address, - uint32_t epoch_index, - uint16_t total_chunks, - uint32_t total_bytes); +/// @param next_epoch_index the outpost's `nextEpochIndex()`. +/// @param epoch_index WIRE epoch being delivered. +/// @param spill the outpost's `dispatchSpill(epoch_index)`. +/// @param own_delivery_settled whether this relay's recorded delivery digest +/// equals the settled `pendingEpochHash`. +delivery_action decide_delivery(uint32_t next_epoch_index, + uint32_t epoch_index, + const dispatch_spill& spill, + bool own_delivery_settled); /// Why `SyndicationPool.realizeYield()` refused, read from the node's revert bytes. enum class realize_yield_refusal { @@ -211,10 +153,9 @@ class outpost_ethereum_client : public outpost_client { const ethereum_client_entry_ptr& entry() const { return _entry; } const std::string& opp_address() const { return _opp_addr; } const std::string& opp_inbound_address() const { return _opp_inbound_addr; } - /// This relay's own signer address in `0x`-hex — the identity every staging - /// header is bound to. Derived once at construction; the chunk resume path - /// compares it against `envelopeChunkState`'s `owner` on every multi-chunk - /// delivery, so it is cached rather than re-derived per tick. + /// This relay's own signer address in `0x`-hex — the identity the outpost + /// records deliveries under. Derived once at construction; the continuation + /// check compares the digest recorded under it against the settled one. const std::string& signer_address_hex() const { return _signer_address_hex; } /// The liq syndication pool the crank drives, `0x`-hex, or empty until the /// outpost's `DESYNDICATE_LIQ` handler has been discovered. @@ -233,32 +174,31 @@ class outpost_ethereum_client : public outpost_client { /// @throws fc::exception on transport failure. std::optional discover_syndication_pool(); - /// Read `OPPInbound.envelopeChunkState(self)` at `latest` and decode it. - /// - /// `latest` is correct here (unlike `read_inbound_envelope`, which reads at - /// `finalized`): this is our OWN staging high-water mark, not delivered - /// content WIRE consensus commits against. Reading it at `finalized` would - /// re-send chunks already staged in unfinalized blocks every tick. + /// Read `OPPInbound.nextEpochIndex()` at `latest`. + /// @throws fc::exception on transport failure, deadline expiry, or an + /// unparsable response. + uint32_t read_next_epoch_index(); + + /// Read `OPPInbound.dispatchSpill(epoch_index)` at `latest` and decode it. /// - /// A malformed / unreadable response yields an all-zero state, which - /// degrades to `start_fresh` — the contract absorbs the replayed chunks as - /// idempotent no-ops. + /// `latest`, deliberately, where `read_inbound_envelope` reads at + /// `finalized`: the cursor is this outpost's own bookkeeping about work + /// this relay is doing right now, and the worst a reorg can cost is a + /// re-sent transaction the contract already treats as an idempotent no-op. + /// Finality lags head by roughly 64 blocks, so a continuation gated on it + /// would wait that out between every stretch of dispatch. /// - /// @throws fc::exception on transport failure or deadline expiry. - outpost_ethereum_client_detail::envelope_chunk_state read_envelope_chunk_state(); + /// @throws fc::exception on transport failure, deadline expiry, or an + /// unparsable response. + outpost_ethereum_client_detail::dispatch_spill read_dispatch_spill(uint32_t epoch_index); - /// First chunk index to send for this delivery, or `std::nullopt` when the - /// epoch has already advanced past `epoch_index` and the delivery must be - /// skipped outright. - /// - /// Multi-chunk deliveries only — a single-chunk envelope stages nothing, so - /// the dominant case pays no extra RPC. + /// Whether the digest this relay delivered for `epoch_index` is the one + /// consensus settled on (`epochDeliveries(epoch, self) == pendingEpochHash`). + /// False when this relay has not delivered, or delivered a minority digest. /// - /// @throws fc::exception on transport failure or deadline expiry. - std::optional resume_chunk_index(uint32_t epoch_index, - uint16_t total_chunks, - uint32_t total_bytes, - fc::time_point deadline_abs); + /// @throws fc::exception on transport failure, deadline expiry, or an + /// unparsable response. + bool own_delivery_settled(uint32_t epoch_index); ethereum_client_entry_ptr _entry; std::string _opp_addr; diff --git a/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp b/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp index b2b139bf2f..efd11ebd09 100644 --- a/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp +++ b/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp @@ -42,7 +42,6 @@ constexpr int ethereum_execution_reverted_code = 3; /// ours to clear. Hashed rather than written out as its selector so a reader can check this /// against the ABI directly; `chunk_buffer_missing_selector_is_pinned` holds the hash to the /// four bytes the contract actually emits. -constexpr auto chunk_buffer_missing_signature = "OPP_ChunkBufferMissing(address)"; /// The three refusals `SyndicationPool.realizeYield()` raises for its own reasons /// (Wire-Network/wire-ethereum#207), hashed the same way; @@ -78,18 +77,31 @@ constexpr auto tx_realize_yield = "realizeYield"; } // namespace syndication_pool_abi namespace opp_inbound_abi { -constexpr auto view_envelope_chunk_state = "envelopeChunkState"; -constexpr auto view_next_epoch_index = "nextEpochIndex"; +constexpr auto view_next_epoch_index = "nextEpochIndex"; +constexpr auto view_dispatch_spill = "dispatchSpill"; +constexpr auto view_epoch_deliveries = "epochDeliveries"; +constexpr auto view_pending_epoch_hash = "pendingEpochHash"; namespace field { -constexpr auto epoch_index = "epochIndex"; -constexpr auto owner = "owner"; -constexpr auto total_chunks = "totalChunks"; -constexpr auto received_chunks = "receivedChunks"; -constexpr auto total_bytes = "totalBytes"; -constexpr auto stored_bytes = "storedBytes"; +constexpr auto tipped = "tipped"; +constexpr auto dispatched = "dispatched"; +constexpr auto complete = "complete"; +constexpr auto finalized = "finalized"; } } // namespace opp_inbound_abi +/// Most `epochIn` continuations one tick sends for one epoch before handing +/// the rest to the next tick. A bound, not a budget: a full-cap envelope +/// spills a handful of times at most, and a cursor still advancing past this +/// many is worth a log line and a fresh tick rather than an unbounded loop +/// inside one. +constexpr uint32_t MAX_CONTINUATIONS_PER_TICK = 32; + +/// Block tag for reading this outpost's own delivery bookkeeping +/// (`nextEpochIndex`, `dispatchSpill`, `epochDeliveries`, `pendingEpochHash`). +/// `latest`, deliberately, where the inbound-envelope read uses `finalized`: +/// see `outpost_ethereum_client::read_dispatch_spill`. +constexpr auto PROGRESS_READ_TAG = eth::block_tag_t::latest; + constexpr size_t EVM_ABI_WORD_BYTES = 32; constexpr size_t EVM_ADDRESS_BYTES = 20; constexpr size_t EVM_SELECTOR_BYTES = 4; @@ -155,6 +167,59 @@ bool has_function_abi(const std::vector& abis, std::string_v }); } +/// Interpret one decoded ABI output word as a boolean. +/// +/// Tolerates every shape the decoder may hand back for a Solidity `bool` — a +/// native bool, the decimal strings the numeric path normalises to, or a raw +/// number. Returns `std::nullopt` for anything else so callers fail closed. +std::optional abi_bool_output(const fc::variant& value) { + if (value.is_bool()) return value.as_bool(); + if (value.is_string()) { + const auto text = value.as_string(); + if (text == "true") return true; + if (text == "false") return false; + } + const auto numeric = abi_uint_output(value); + if (numeric) return *numeric != 0; + return std::nullopt; +} + +/// Pull the single value out of a decoded view result. +/// +/// libfc picks one of three shapes: a BARE value when the function has one +/// output, a named object when every output is named, and a positional array +/// otherwise. A public state-variable getter takes the first path because its +/// output carries no name; a struct getter takes the second. Reading a scalar +/// through this keeps a caller indifferent to which one the decoder chose. +fc::variant decoded_scalar(const fc::variant& decoded) { + if (decoded.is_array()) { + const auto& items = decoded.get_array(); + return items.empty() ? fc::variant() : items[0]; + } + if (decoded.is_object()) { + const auto& object = decoded.get_object(); + return object.size() == 0 ? fc::variant() : object.begin()->value(); + } + return decoded; +} + +/// A 32-byte word as the decoder renders it, normalised for comparison: +/// lower-case hex with no `0x` prefix. Empty when the value is not a string. +std::string normalized_word(const fc::variant& value) { + if (!value.is_string()) return {}; + std::string word = value.as_string(); + if (word.size() >= HEX_PREFIX_CHARS && word[0] == '0' && (word[1] == 'x' || word[1] == 'X')) { + word.erase(0, HEX_PREFIX_CHARS); + } + std::ranges::transform(word, word.begin(), [](unsigned char c) { return std::tolower(c); }); + return word; +} + +/// Whether a normalised word is the zero word (or empty — nothing recorded). +bool is_zero_word(std::string_view word) { + return std::ranges::all_of(word, [](char c) { return c == '0'; }); +} + } // namespace namespace outpost_ethereum_client_detail { @@ -165,65 +230,25 @@ bool same_evm_address(std::string_view lhs, std::string_view rhs) { return same_hex(left, strip_hex_prefix(rhs)); } -bool is_chunk_buffer_missing_revert(std::string_view revert_data, std::string_view owner_address) { - constexpr size_t selector_chars = EVM_SELECTOR_BYTES * HEX_CHARS_PER_BYTE; - constexpr size_t word_chars = EVM_ABI_WORD_BYTES * HEX_CHARS_PER_BYTE; - constexpr size_t address_chars = EVM_ADDRESS_BYTES * HEX_CHARS_PER_BYTE; - - const auto data = strip_hex_prefix(revert_data); - // Selector plus exactly one address word. Requiring the exact length is no more brittle - // than the selector test: an error taking different arguments hashes differently. - if (data.size() != selector_chars + word_chars) return false; - - if (!same_hex(data.substr(0, selector_chars), selector_hex(chunk_buffer_missing_signature))) - return false; - - // The error's sole argument is the contract's `msg.sender`, so the word must be this - // relay's own signer, left-padded into 32 bytes. - const auto word = data.substr(selector_chars); - if (word.find_first_not_of('0') < word_chars - address_chars) return false; - return same_evm_address(word.substr(word_chars - address_chars), owner_address); -} - -chunk_resume_decision decide_chunk_resume(const envelope_chunk_state& staged, - std::string_view self_address, - uint32_t epoch_index, - uint16_t total_chunks, - uint32_t total_bytes) { - // An empty header (`totalChunks == SLOT_EMPTY`) is the reclaimable state the - // contract hands back for "never staged" and "already reset". Nothing to - // adopt; chunk 0 stages it. - if (staged.total_chunks == 0) { - return {chunk_resume_action::start_fresh, 0}; - } - - // A header owned by someone else is not ours to continue or discard: the - // view is owner-bound, so this can only mean the read was answered for a - // different signer. Start from 0 and let the contract's ownership guard - // arbitrate. - if (!same_evm_address(staged.owner, self_address)) { - return {chunk_resume_action::start_fresh, 0}; - } - - // Our own header, but for a different epoch. On a path-2 consensus retry - // this is the signature of an epoch that advanced underneath us, so the - // caller confirms against `nextEpochIndex()` before spending gas on late - // no-op transactions. - if (staged.epoch_index != epoch_index) { - return {chunk_resume_action::confirm_epoch_advanced, 0}; - } - - // Our own CURRENT-epoch header whose declared shape belongs to a different - // envelope, or one claiming more stored chunks than a well-formed upload can - // hold (the final chunk is never stored, so `receivedChunks` tops out at - // `totalChunks - 1`). Either way the staged cells are unusable: discard, - // then upload from 0. - if (staged.total_chunks != total_chunks || staged.total_bytes != total_bytes || - staged.received_chunks >= total_chunks) { - return {chunk_resume_action::discard_and_restart, 0}; - } - - return {chunk_resume_action::resume, staged.received_chunks}; +delivery_action decide_delivery(uint32_t next_epoch_index, + uint32_t epoch_index, + const dispatch_spill& spill, + bool own_delivery_settled) { + // The outpost is past this epoch, or has closed it: every transaction + // would be a paid late no-op. `finalized` is checked as well as the epoch + // cursor because the two are written in one call and reads that race a + // just-mined block may see one before the other. + if (next_epoch_index > epoch_index || spill.finalized) { + return delivery_action::already_finalized; + } + // Tipped but unfinished. The contract admits a continuation only from a + // relay whose recorded delivery IS the settled digest, so a relay that has + // not delivered, or delivered a minority envelope, has nothing to send. + if (spill.tipped) { + return own_delivery_settled ? delivery_action::continue_dispatch + : delivery_action::wait_for_deliverer; + } + return delivery_action::deliver; } std::optional classify_realize_yield_revert(std::string_view revert_data) { @@ -297,9 +322,10 @@ outpost_ethereum_client::outpost_ethereum_client( _entry->client->get_contract(_opp_inbound_addr, _abis); } - // Every OPPInbound staging header is bound to the delivering signer, so the - // chunk-resume read needs this relay's own address on every multi-chunk - // delivery. Derive it once here rather than per tick. + // OPPInbound records every delivery under the delivering signer, and a + // continuation is admitted only from a signer whose recorded digest is the + // settled one, so the continuation check needs this relay's own address. + // Derive it once here rather than per tick. _signer_address_hex = fc::to_hex(_entry->client->get_signer_address(), /*add_prefix=*/true); } @@ -313,179 +339,88 @@ outpost_ethereum_client::authenticated_caller_address() const { return {address.begin(), address.end()}; } -detail::envelope_chunk_state outpost_ethereum_client::read_envelope_chunk_state() { - detail::envelope_chunk_state state; - - const auto& abi = _opp_inbound_client->get_abi(opp_inbound_abi::view_envelope_chunk_state); - - // Read at `latest`. This is our OWN staging high-water mark, not delivered - // content WIRE commits consensus against, so the `finalized`-tag reasoning - // that governs `read_inbound_envelope` does not apply — reading it at - // `finalized` would re-send every chunk staged in an unfinalized block. - std::string operator_address = _signer_address_hex; - const auto raw_hex_var = - _opp_inbound_client->envelope_chunk_state(eth::block_tag_t::latest, operator_address); - if (!raw_hex_var.is_string()) { - wlog("outpost_ethereum_client[{}]: envelopeChunkState returned non-string variant", - to_string()); - return state; - } - const std::string raw_hex = raw_hex_var.as_string(); - if (raw_hex.empty() || raw_hex == "0x") { - dlog("outpost_ethereum_client[{}]: envelopeChunkState returned empty hex", to_string()); - return state; - } - - const auto decoded = eth::contract_decode_data(abi, raw_hex); - dlog("outpost_ethereum_client[{}]: envelopeChunkState decoded={}", - to_string(), fc::json::to_string(decoded, fc::json::yield_function_t{})); - if (!decoded.is_object()) { - wlog("outpost_ethereum_client[{}]: envelopeChunkState result was not a variant object", - to_string()); - return state; - } - - const auto& obj = decoded.get_object(); - for (const auto* key : {opp_inbound_abi::field::epoch_index, - opp_inbound_abi::field::owner, - opp_inbound_abi::field::total_chunks, - opp_inbound_abi::field::received_chunks, - opp_inbound_abi::field::total_bytes, - opp_inbound_abi::field::stored_bytes}) { - if (!obj.contains(key)) { - wlog("outpost_ethereum_client[{}]: envelopeChunkState result missing '{}' key", - to_string(), key); - return state; - } - } - - const auto& owner_var = obj[opp_inbound_abi::field::owner]; - if (!owner_var.is_string()) { - wlog("outpost_ethereum_client[{}]: envelopeChunkState owner was not a string", to_string()); - return state; - } +uint32_t outpost_ethereum_client::read_next_epoch_index() { + const auto raw = _opp_inbound_client->next_epoch_index(PROGRESS_READ_TAG); + FC_ASSERT(raw.is_string(), + "outpost_ethereum_client[{}]: nextEpochIndex returned a non-string variant", + to_string()); + const auto decoded = decoded_scalar(eth::contract_decode_data( + _opp_inbound_client->get_abi(opp_inbound_abi::view_next_epoch_index), raw.as_string())); + const auto value = abi_uint_output(decoded); + FC_ASSERT(value.has_value(), + "outpost_ethereum_client[{}]: nextEpochIndex was not a parsable numeric output", + to_string()); + return static_cast(*value); +} - // Fail closed on the fields the resume DECISION reads: an unparsable one - // must not be silently read as 0, which would fabricate an empty header. - const auto epoch_index = abi_uint_output(obj[opp_inbound_abi::field::epoch_index]); - const auto total_chunks = abi_uint_output(obj[opp_inbound_abi::field::total_chunks]); - const auto received_chunks = abi_uint_output(obj[opp_inbound_abi::field::received_chunks]); - const auto total_bytes = abi_uint_output(obj[opp_inbound_abi::field::total_bytes]); - if (!epoch_index || !total_chunks || !received_chunks || !total_bytes) { - wlog("outpost_ethereum_client[{}]: envelopeChunkState carried an unparsable numeric field", - to_string()); - return state; - } +detail::dispatch_spill outpost_ethereum_client::read_dispatch_spill(uint32_t epoch_index) { + // `ethereum_contract_call_fn` binds its arguments as non-const lvalue + // references, so the epoch needs a named local. + uint32_t epoch_arg = epoch_index; + const auto raw = _opp_inbound_client->dispatch_spill(PROGRESS_READ_TAG, epoch_arg); + FC_ASSERT(raw.is_string(), + "outpost_ethereum_client[{}]: dispatchSpill returned a non-string variant", + to_string()); + const auto decoded = eth::contract_decode_data( + _opp_inbound_client->get_abi(opp_inbound_abi::view_dispatch_spill), raw.as_string()); + dlog("outpost_ethereum_client[{}]: dispatchSpill({}) decoded={}", + to_string(), epoch_index, fc::json::to_string(decoded, fc::json::yield_function_t{})); + FC_ASSERT(decoded.is_object(), + "outpost_ethereum_client[{}]: dispatchSpill result was not a variant object", + to_string()); - state.owner = owner_var.as_string(); - state.epoch_index = static_cast(*epoch_index); - state.total_chunks = static_cast(*total_chunks); - state.received_chunks = static_cast(*received_chunks); - state.total_bytes = static_cast(*total_bytes); - // `storedBytes` is a uint256 on chain and feeds diagnostics ONLY — it is not - // an input to `decide_chunk_resume`. Parse it separately with a 0 default so - // a value above UINT64_MAX cannot discard an otherwise-valid header and - // force a needless re-upload of chunks the outpost already holds. - state.stored_bytes = abi_uint_output(obj[opp_inbound_abi::field::stored_bytes]).value_or(0); - return state; + // Fail closed on every field: an unparsable one must not be silently read + // as "not tipped", which would send a fresh delivery into an epoch that + // has already settled and be refused as a non-deliverer. + const auto& object = decoded.get_object(); + const auto read_bool = [&](const char* key) { + FC_ASSERT(object.contains(key), + "outpost_ethereum_client[{}]: dispatchSpill result missing '{}'", to_string(), key); + const auto value = abi_bool_output(object[key]); + FC_ASSERT(value.has_value(), + "outpost_ethereum_client[{}]: dispatchSpill '{}' was not a boolean output", + to_string(), key); + return *value; + }; + FC_ASSERT(object.contains(opp_inbound_abi::field::dispatched), + "outpost_ethereum_client[{}]: dispatchSpill result missing '{}'", + to_string(), opp_inbound_abi::field::dispatched); + const auto dispatched = abi_uint_output(object[opp_inbound_abi::field::dispatched]); + FC_ASSERT(dispatched.has_value(), + "outpost_ethereum_client[{}]: dispatchSpill '{}' was not a numeric output", + to_string(), opp_inbound_abi::field::dispatched); + + detail::dispatch_spill spill; + spill.tipped = read_bool(opp_inbound_abi::field::tipped); + spill.dispatched = static_cast(*dispatched); + spill.complete = read_bool(opp_inbound_abi::field::complete); + spill.finalized = read_bool(opp_inbound_abi::field::finalized); + return spill; } -std::optional outpost_ethereum_client::resume_chunk_index( - uint32_t epoch_index, - uint16_t total_chunks, - uint32_t total_bytes, - fc::time_point deadline_abs) { - throw_if_past_deadline(deadline_abs, OP_DELIVER_OUTBOUND); - const auto staged = read_envelope_chunk_state(); - const auto decision = detail::decide_chunk_resume( - staged, _signer_address_hex, epoch_index, total_chunks, total_bytes); - - dlog("outpost_ethereum_client[{}]: chunk resume epoch={} total_chunks={} action={} " - "staged(epoch={} owner={} chunks={} received={} bytes={})", - to_string(), epoch_index, total_chunks, - magic_enum::enum_name(decision.action), - staged.epoch_index, staged.owner, staged.total_chunks, - staged.received_chunks, staged.total_bytes); - - switch (decision.action) { - case detail::chunk_resume_action::start_fresh: - return uint16_t{0}; - - case detail::chunk_resume_action::resume: - ilog("outpost_ethereum_client[{}]: resuming epoch={} delivery at chunk {}/{} " - "from the on-chain staging high-water mark", - to_string(), epoch_index, decision.start_chunk, total_chunks); - return decision.start_chunk; - - case detail::chunk_resume_action::discard_and_restart: { - throw_if_past_deadline(deadline_abs, OP_DELIVER_OUTBOUND); - try { - const auto result = _opp_inbound_client->discard_envelope_chunks(); - ilog("outpost_ethereum_client[{}]: discarded a superseded epoch={} staging header " - "(staged chunks={} bytes={}) tx={}", - to_string(), epoch_index, staged.total_chunks, staged.total_bytes, - result.as_string()); - } catch (const fc::network::json_rpc::json_rpc_error& e) { - // Only one revert means the header is already clear, and it has to be identified by - // what the node returned, not by the exception's type or its code alone. A - // protocol-level JSON-RPC error says the node never ran the call at all; a code-3 - // revert carrying anything else — a wrong implementation behind the proxy, a - // reentrancy guard, an unrecognised selector — says it ran and failed for a reason - // this path cannot interpret. Either way the discard may not have happened, and - // tolerating it would upload against a staging header that is still stale. - if (e.code != ethereum_execution_reverted_code || - !detail::is_chunk_buffer_missing_revert(e.data.is_string() ? e.data.as_string() : std::string{}, - _signer_address_hex)) - throw; - // `OPP_ChunkBufferMissing(self)` from `eth_estimateGas` means the header is already - // clear, which is exactly the state we wanted. Tolerate it and upload from chunk 0. - // - // Deliberately NOT widened: a revert first observed at RECEIPT time - // (status=0) is raised by `wait_for_confirmation` as a generic - // `fc::exception` (`ethereum_client.cpp` "Ethereum tx {} reverted - // (status=0)"), and so are transport failures and deadline expiry. - // All three propagate and abandon the tick, which is benign: the next - // cron tick re-reads the staging header and resumes from whatever the - // chain actually holds. - dlog("outpost_ethereum_client[{}]: discardEnvelopeChunks reverted at estimate time, " - "treating the staging header as already clear: code={} message='{}'", - to_string(), e.code, e.top_message()); - } - return uint16_t{0}; - } - - case detail::chunk_resume_action::confirm_epoch_advanced: { - throw_if_past_deadline(deadline_abs, OP_DELIVER_OUTBOUND); - const auto next_var = _opp_inbound_client->next_epoch_index(eth::block_tag_t::latest); - // `nextEpochIndex()` declares a single output, and `contract_decode_data` - // returns a lone component directly rather than wrapping it. - const auto next_epoch = - next_var.is_string() - ? abi_uint_output(eth::contract_decode_data( - _opp_inbound_client->get_abi(opp_inbound_abi::view_next_epoch_index), - next_var.as_string())) - : std::optional{}; - if (!next_epoch) { - wlog("outpost_ethereum_client[{}]: could not read nextEpochIndex while resolving a " - "stale staging header; delivering epoch={} from chunk 0", - to_string(), epoch_index); - return uint16_t{0}; - } - if (*next_epoch > epoch_index) { - ilog("outpost_ethereum_client[{}]: skipping epoch={} delivery — the outpost has " - "already advanced to epoch {}", - to_string(), epoch_index, *next_epoch); - return std::nullopt; - } - return uint16_t{0}; - } - } +bool outpost_ethereum_client::own_delivery_settled(uint32_t epoch_index) { + uint32_t epoch_arg = epoch_index; + std::string operator_arg = _signer_address_hex; + const auto delivered_raw = + _opp_inbound_client->epoch_deliveries(PROGRESS_READ_TAG, epoch_arg, operator_arg); + const auto settled_raw = _opp_inbound_client->pending_epoch_hash(PROGRESS_READ_TAG); + FC_ASSERT(delivered_raw.is_string() && settled_raw.is_string(), + "outpost_ethereum_client[{}]: epochDeliveries / pendingEpochHash returned a " + "non-string variant", + to_string()); - // Unreachable: the switch above covers every `chunk_resume_action`. An enum - // can still hold an out-of-range value, so the compiler requires a fallback; - // uploading from chunk 0 is the conservative one (the contract absorbs any - // replayed chunk as an idempotent no-op). - return uint16_t{0}; + const auto delivered = normalized_word(decoded_scalar(eth::contract_decode_data( + _opp_inbound_client->get_abi(opp_inbound_abi::view_epoch_deliveries), delivered_raw.as_string()))); + const auto settled = normalized_word(decoded_scalar(eth::contract_decode_data( + _opp_inbound_client->get_abi(opp_inbound_abi::view_pending_epoch_hash), settled_raw.as_string()))); + dlog("outpost_ethereum_client[{}]: epoch={} own delivery={} settled={}", + to_string(), epoch_index, delivered, settled); + + // A zero digest is "nothing recorded" on either side and never a match: an + // epoch cannot settle on the zero digest, and a relay that has not + // delivered has no claim on the continuation. + if (is_zero_word(delivered) || is_zero_word(settled)) return false; + return delivered == settled; } std::string outpost_ethereum_client::deliver_outbound_envelope( @@ -510,52 +445,87 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( "of {} bytes; OPPInbound will reject it", to_string(), total, OPP_MAX_ENVELOPE_BYTES); - const uint16_t total_chunks = detail::chunk_count_for(total); - - // RESUME — multi-chunk deliveries only. A single-chunk envelope stages - // nothing on chain (OPPInbound's fast path assembles straight from - // calldata), so the dominant case pays no extra round trip. - uint16_t start_chunk = 0; - if (total_chunks > 1) { - const auto resume = - resume_chunk_index(epoch_index, total_chunks, static_cast(total), deadline_abs); - if (!resume) { - // The outpost advanced past this epoch; every chunk would be a paid - // late no-op. Returning empty marks the epoch handled for this tick. - return {}; - } - start_chunk = *resume; - } + // ONE call carries the whole envelope. Ethereum bounds a transaction by + // gas, not size, and a full-cap envelope is ~1.3 M gas of calldata against + // EIP-7825's 16.7 M cap; what may not fit is DISPATCH, which the contract + // spills across continuations of this same call. The envelope is + // re-supplied on every continuation — the outpost never stores its bytes. + const std::string envelope_hex = fc::to_hex(envelope_bytes.data(), static_cast(total)); // Sequential, receipt-confirmed submission — one transaction in flight at a // time, so the signer's nonce advances in lock-step and a mid-sequence - // failure simply abandons the tick. The next cron tick restarts from the - // on-chain high-water mark; the contract absorbs any replayed chunk as an - // idempotent no-op. + // failure simply abandons the tick. The next cron tick re-reads the + // outpost's cursor and picks up wherever the chain actually is. std::string last_tx; - for (uint16_t chunk = start_chunk; chunk < total_chunks; ++chunk) { + bool delivered_this_tick = false; + for (uint32_t call = 0; call <= MAX_CONTINUATIONS_PER_TICK; ++call) { throw_if_past_deadline(deadline_abs, OP_DELIVER_OUTBOUND); - const size_t offset = static_cast(chunk) * ETHEREUM_MAX_CHUNK_BYTES; - const size_t length = std::min(ETHEREUM_MAX_CHUNK_BYTES, total - offset); + const auto next_epoch = read_next_epoch_index(); + const auto spill = read_dispatch_spill(epoch_index); + // The two settlement reads are only meaningful — and only paid for — + // when the epoch is mid-flight. + const bool settled = spill.tipped && !spill.finalized && own_delivery_settled(epoch_index); + const auto action = detail::decide_delivery(next_epoch, epoch_index, spill, settled); + dlog("outpost_ethereum_client[{}]: epoch={} action={} next_epoch={} " + "spill(tipped={} dispatched={} complete={} finalized={}) settled={}", + to_string(), epoch_index, magic_enum::enum_name(action), next_epoch, + spill.tipped, spill.dispatched, spill.complete, spill.finalized, settled); + + switch (action) { + case detail::delivery_action::already_finalized: + if (last_tx.empty()) { + // Nothing sent: the outpost was already past this epoch when the + // tick began. An EMPTY tx id tells the job the epoch is handled. + ilog("outpost_ethereum_client[{}]: skipping epoch={} delivery — the outpost has " + "already finalized it (nextEpochIndex={})", + to_string(), epoch_index, next_epoch); + } + return last_tx; + + case detail::delivery_action::wait_for_deliverer: + ilog("outpost_ethereum_client[{}]: epoch={} tipped on a digest this relay did not " + "deliver (dispatched={} complete={}); its deliverers carry the continuation", + to_string(), epoch_index, spill.dispatched, spill.complete); + return last_tx; + + case detail::delivery_action::deliver: + if (delivered_this_tick) { + // Our delivery is recorded and consensus has not tipped: the + // remaining group members have yet to deliver. Re-sending would + // be a paid no-op; the job's boundary-gated retry covers path 2. + dlog("outpost_ethereum_client[{}]: epoch={} delivery recorded; consensus not yet tipped", + to_string(), epoch_index); + return last_tx; + } + break; + + case detail::delivery_action::continue_dispatch: + break; + } + throw_if_past_deadline(deadline_abs, OP_DELIVER_OUTBOUND); // `ethereum_contract_tx_fn` binds every argument as a non-const lvalue - // reference, so each one needs a named local (the same constraint that - // preserves the RPC's chain-native byte encoding). - uint32_t epoch_arg = epoch_index; - uint16_t chunk_arg = chunk; - uint16_t chunks_arg = total_chunks; - uint32_t bytes_arg = static_cast(total); - std::string chunk_hex = - fc::to_hex(envelope_bytes.data() + offset, static_cast(length)); - - const auto result = - _opp_inbound_client->epoch_in(epoch_arg, chunk_arg, chunks_arg, bytes_arg, chunk_hex); - last_tx = result.as_string(); - ilog("outpost_ethereum_client[{}]: epochIn chunk sent epoch={} chunk={}/{} bytes={} tx={}", - to_string(), epoch_index, chunk, total_chunks, length, last_tx); + // reference, so each one needs a named local. + uint32_t epoch_arg = epoch_index; + std::string payload_hex = envelope_hex; + const auto result = _opp_inbound_client->epoch_in(epoch_arg, payload_hex); + last_tx = result.as_string(); + + if (action == detail::delivery_action::deliver) { + delivered_this_tick = true; + ilog("outpost_ethereum_client[{}]: epochIn delivered epoch={} bytes={} tx={}", + to_string(), epoch_index, total, last_tx); + } else { + ilog("outpost_ethereum_client[{}]: epochIn continued epoch={} from attestation {} " + "(complete={}) tx={}", + to_string(), epoch_index, spill.dispatched, spill.complete, last_tx); + } } + wlog("outpost_ethereum_client[{}]: epoch={} still not finalized after {} continuations in " + "one tick; the next tick resumes from the outpost's cursor", + to_string(), epoch_index, MAX_CONTINUATIONS_PER_TICK); return last_tx; } diff --git a/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp b/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp index 0f8eced462..e66c5b1f33 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp @@ -136,26 +136,24 @@ ethabi::contract bytes_argument_function(std::string name) { }; } -/** Build the chunked `epochIn` function ABI. */ -ethabi::contract chunked_epoch_in_function(std::string name) { +/** Build the whole-envelope `epochIn(uint32,bytes)` function ABI. */ +ethabi::contract whole_envelope_epoch_in_function(std::string name) { return ethabi::contract{ .name = std::move(name), .type = ethabi::invoke_target_type::function, .inputs = {ethabi::component_type{"epochIndex", ethabi::data_type::uint32}, - ethabi::component_type{"chunkIndex", ethabi::data_type::uint16}, - ethabi::component_type{"totalChunks", ethabi::data_type::uint16}, - ethabi::component_type{"totalBytes", ethabi::data_type::uint32}, - ethabi::component_type{"chunkData", ethabi::data_type::bytes}}, + ethabi::component_type{"envelopeData", ethabi::data_type::bytes}}, .outputs = {}, }; } -/** Build a function ABI with one address argument. */ -ethabi::contract address_argument_function(std::string name) { +/** Build the `epochDeliveries(uint32,address)` view ABI. */ +ethabi::contract epoch_deliveries_function(std::string name) { return ethabi::contract{ .name = std::move(name), .type = ethabi::invoke_target_type::function, - .inputs = {ethabi::component_type{"operator_", ethabi::data_type::address}}, + .inputs = {ethabi::component_type{"epochIndex", ethabi::data_type::uint32}, + ethabi::component_type{"operator_", ethabi::data_type::address}}, .outputs = {}, }; } @@ -481,6 +479,36 @@ BOOST_AUTO_TEST_CASE(default_transaction_uses_priority_fee_in_estimate_payload_a BOOST_CHECK_EQUAL(sign_count.load(), 0u); } +/// A gas-limit floor raises an under-estimate to the floor, leaves a larger +/// buffered estimate alone, and is refused — never clamped — above the policy +/// ceiling. +BOOST_AUTO_TEST_CASE(gas_limit_floor_raises_an_under_estimate_and_is_policy_bounded) { + std::atomic sign_count = 0; + const auto provider = make_recording_signer(sign_count); + auto policy = bounded_policy(); + policy.max_gas_limit = 2000; + auto client = std::make_shared(provider, policy); + + // The fake estimate is 834 -> buffered 1000. + BOOST_CHECK_EQUAL( + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 0).gas_limit, + 1000); + BOOST_CHECK_EQUAL( + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 900).gas_limit, + 1000); + BOOST_CHECK_EQUAL( + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 1500).gas_limit, + 1500); + BOOST_CHECK_EQUAL( + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 2000).gas_limit, + 2000); + expect_policy_rejection([&] { + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 2001); + }); + BOOST_CHECK_EQUAL(sign_count.load(), 0u); + BOOST_CHECK_EQUAL(client->broadcast_count, 0u); +} + BOOST_AUTO_TEST_CASE(all_typed_write_wrappers_share_the_policy_enforced_path) { std::atomic sign_count = 0; const auto provider = make_recording_signer(sign_count); @@ -491,21 +519,17 @@ BOOST_AUTO_TEST_CASE(all_typed_write_wrappers_share_the_policy_enforced_path) { sysio::opp_inbound_contract_client inbound{ client, std::string(contract_address), - {chunked_epoch_in_function("epochIn"), no_argument_function("nextEpochIndex"), - no_argument_function("discardEnvelopeChunks"), - address_argument_function("envelopeChunkState"), + {whole_envelope_epoch_in_function("epochIn"), no_argument_function("nextEpochIndex"), + uint32_argument_function("dispatchSpill"), epoch_deliveries_function("epochDeliveries"), + no_argument_function("pendingEpochHash"), uint16_argument_function("attestationHandlers")}, }; - // Both OPPInbound write wrappers — the per-chunk delivery and the staged - // recovery — must be rejected by the policy before signing. + // OPPInbound's one write wrapper — the whole-envelope delivery — must be + // rejected by the policy before signing. It is funded to the policy ceiling + // (999 here), which the ×1.2-buffered estimate of 834 already breaches. uint32_t epoch_index = 1; - uint16_t chunk_index = 0; - uint16_t total_chunks = 1; - uint32_t total_bytes = 1; - std::string chunk = "01"; - expect_policy_rejection( - [&] { inbound.epoch_in(epoch_index, chunk_index, total_chunks, total_bytes, chunk); }); - expect_policy_rejection([&] { inbound.discard_envelope_chunks(); }); + std::string envelope = "01"; + expect_policy_rejection([&] { inbound.epoch_in(epoch_index, envelope); }); sysio::opp_contract_client opp{ client, diff --git a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp index 93d5520281..8928536d2c 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp @@ -199,39 +199,41 @@ constexpr size_t rpc_length_oversized_envelope_bytes = sysio::OPP_MAX_ENVELOPE_B constexpr char malformed_envelope_byte = static_cast(0xff); constexpr char oversized_envelope_fill_byte = static_cast(0x01); -// ── Chunked `epochIn` fixtures ─────────────────────────────────────────── +// ── Whole-envelope `epochIn` fixtures ──────────────────────────────────── constexpr std::string_view test_opp_inbound_address = "e7f1725E7734CE288F8367e1Bb143E90bb3F0512"; -/// keccak256("epochIn(uint32,uint16,uint16,uint32,bytes)")[0..4). -constexpr std::string_view epoch_in_selector = "c3e558bc"; +/// keccak256("epochIn(uint32,bytes)")[0..4). +constexpr std::string_view epoch_in_selector = "004e356a"; +/// keccak256("epochIn(uint32,uint16,uint16,uint32,bytes)")[0..4) — the retired chunked form. +constexpr std::string_view retired_chunked_epoch_in_selector = "c3e558bc"; /// keccak256("epochIn(bytes)")[0..4) — the retired single-transaction form. constexpr std::string_view retired_single_bytes_epoch_in_selector = "cfae3118"; -constexpr size_t evm_address_bytes = 20; -constexpr size_t evm_address_hex_chars = evm_address_bytes * hex_chars_per_byte; -constexpr size_t epoch_in_input_count = 5; -/// A peer operator's address, used to prove an unowned staging header is ignored. -constexpr std::string_view foreign_operator_address = - "0x90F79bf6EB2c4f870365E785982E1f101E93b906"; -/// Envelope sizes exercising the chunk loop: three full chunks + a remainder, -/// and the exact platform cap (four full chunks). -constexpr size_t three_chunk_envelope_bytes = 2 * sysio::ETHEREUM_MAX_CHUNK_BYTES + 3'616; +constexpr size_t epoch_in_input_count = 2; +/// The digest consensus settled on, a digest that diverges from it, and the +/// zero word the outpost reports for a relay that never delivered. +constexpr std::string_view settled_digest_word = + "1111111111111111111111111111111111111111111111111111111111111111"; +constexpr std::string_view divergent_digest_word = + "2222222222222222222222222222222222222222222222222222222222222222"; +constexpr std::string_view zero_digest_word = + "0000000000000000000000000000000000000000000000000000000000000000"; +/// A mid-size envelope — large enough that a chunked relay would have split it. +constexpr size_t mid_envelope_bytes = 20'000; /// `derive_buffered_gas_limit` applies a ×1.2 buffer, so a policy pinned at /// EIP-7825's per-transaction cap rejects any estimate above 13 981 013. -constexpr uint64_t eip_7825_tx_gas_cap = 16'777'216; constexpr uint64_t under_cap_gas_estimate = 13'000'000; constexpr uint64_t under_cap_buffered_gas_limit = 15'600'000; constexpr uint64_t over_cap_gas_estimate = 14'000'000; +/// A policy ceiling below EIP-7825's cap, to prove the floor follows the policy. +constexpr uint64_t below_cap_policy_gas_limit = 9'000'000; + /// JSON-RPC error code anvil/geth report for a reverted call. constexpr int contract_revert_rpc_code = 3; - /// JSON-RPC parse error: the node never executed the call, so nothing on chain changed. constexpr int json_rpc_parse_error_code = -32700; -/// `keccak256("OPP_ChunkBufferMissing(address)")[0..4]`, written out rather than recomputed so -/// these tests pin the client's own hashing of that signature instead of restating it. -constexpr std::string_view chunk_buffer_missing_selector = "dca69e67"; -/// A selector the client must not accept. `OPP_NoChunkSlot(address)` is a real neighbouring -/// error of the same shape, so a length or padding check alone would let it through. -constexpr std::string_view no_chunk_slot_selector = "9ecbc704"; +/// A one-address custom error the client has no reading for — the same shape as +/// `AccessManagedUnauthorized(address)`, a different selector. +constexpr std::string_view unrelated_address_error_selector = "dca69e67"; /// Any address that is not this relay's signer, for the revert naming a different operator. constexpr std::string_view test_other_operator_address = "0x00000000000000000000000000000000deadbeef"; @@ -374,34 +376,20 @@ std::string encode_latest_outbound_result(uint32_t epoch, const std::vector inbound; std::unique_ptr outpost; - std::vector chunk_calls; - size_t chunk_state_reads = 0; - size_t discard_calls = 0; - size_t next_epoch_reads = 0; + std::vector delivery_calls; + size_t next_epoch_reads = 0; + size_t spill_reads = 0; + size_t settlement_reads = 0; - /// Response the stubbed `envelopeChunkState` view returns; the default is - /// the all-zero (never staged) header. - std::string chunk_state_response = - encode_envelope_chunk_state_result(0, std::string(evm_address_hex_chars, '0'), 0, 0, 0, 0); /// Response the stubbed `nextEpochIndex` view returns. std::string next_epoch_index_response = encode_next_epoch_index_result(0); - /// When set, the stubbed `discardEnvelopeChunks` write throws it. - std::optional discard_failure; + /// Responses the stubbed `dispatchSpill` view returns, one per read in + /// order; the last one repeats. The default is the never-tipped cursor. + std::vector spill_responses{encode_dispatch_spill_result(false, 0, false, false)}; + /// Response the stubbed `epochDeliveries(epoch, self)` view returns. + std::string own_delivery_response = encode_word_result(zero_digest_word); + /// Response the stubbed `pendingEpochHash` view returns. + std::string pending_hash_response = encode_word_result(settled_digest_word); /// Wall-clock the FIRST stubbed `epochIn` burns before returning, standing in /// for a slow chain. Applied only to the first call so a deadline set below - /// it expires deterministically at the SECOND chunk's pre-flight check. - std::chrono::milliseconds first_chunk_delay{0}; + /// it expires deterministically at the next pre-flight check. + std::chrono::milliseconds first_call_delay{0}; + + /// The spill response for the `n`-th read. + const std::string& spill_response_at(size_t n) const { + return spill_responses[std::min(n, spill_responses.size() - 1)]; + } }; /// Build an envelope of exactly `size` bytes whose content varies per index, so -/// a mis-sliced chunk cannot accidentally compare equal to the right one. -std::vector make_chunked_envelope(size_t size) { +/// a mis-sliced payload cannot accidentally compare equal to the right one. +std::vector make_envelope(size_t size) { std::vector envelope(size); for (size_t i = 0; i < size; ++i) { envelope[i] = static_cast((i * 31 + 7) & 0xff); @@ -475,10 +466,6 @@ std::vector serialize_envelope(uint32_t epoch) { return {serialized.begin(), serialized.end()}; } -/// Stand up a `chunked_delivery_fixture`: a real `outpost_ethereum_client` -/// whose OPPInbound wrapper has every typed callable replaced by a recording -/// stub. The caller owns the returned fixture; the stubs capture it by -/// reference, so it must not be moved after this returns. /// The app, signer, chain connection and client entry every relay fixture /// stands on. The connection points at a port nothing listens on, so a wrapper /// a case forgot to stub fails loudly instead of dialing anything. @@ -521,8 +508,12 @@ relay_test_stack create_relay_test_stack() { return stack; } -std::unique_ptr create_chunked_delivery_fixture() { - auto fixture = std::make_unique(); +/// Stand up a `whole_envelope_delivery_fixture`: a real `outpost_ethereum_client` +/// whose OPPInbound wrapper has every typed callable replaced by a recording +/// stub. The caller owns the returned fixture; the stubs capture it by +/// reference, so it must not be moved after this returns. +std::unique_ptr create_whole_envelope_delivery_fixture() { + auto fixture = std::make_unique(); auto stack = create_relay_test_stack(); fixture->tester = std::move(stack.tester); auto eth_client = stack.eth_client; @@ -534,40 +525,44 @@ std::unique_ptr create_chunked_delivery_fixture() { BOOST_REQUIRE(fixture->inbound); auto* raw = fixture.get(); - raw->inbound->epoch_in = [raw](uint32_t& epoch_index, - uint16_t& chunk_index, - uint16_t& total_chunks, - uint32_t& total_bytes, - std::string& chunk_hex) -> fc::variant { - raw->chunk_calls.push_back( - observed_chunk_call{epoch_index, chunk_index, total_chunks, total_bytes, chunk_hex}); - if (raw->chunk_calls.size() == 1 && raw->first_chunk_delay.count() > 0) { - std::this_thread::sleep_for(raw->first_chunk_delay); + raw->inbound->epoch_in = [raw](uint32_t& epoch_index, std::string& envelope_hex) -> fc::variant { + raw->delivery_calls.push_back(observed_delivery_call{epoch_index, envelope_hex}); + if (raw->delivery_calls.size() == 1 && raw->first_call_delay.count() > 0) { + std::this_thread::sleep_for(raw->first_call_delay); } - return fc::variant(std::string(hex_prefix) + abi_word(raw->chunk_calls.size())); - }; - raw->inbound->envelope_chunk_state = - [raw](const block_number_or_tag_t& block, std::string& operator_address) -> fc::variant { - // The resume read is our OWN staging high-water mark, so it must be - // taken at `latest` — reading it at `finalized` would replay chunks - // staged in unfinalized blocks on every tick. - BOOST_CHECK(std::holds_alternative(block)); - BOOST_CHECK(std::get(block) == block_tag_t::latest); - BOOST_CHECK(!operator_address.empty()); - ++raw->chunk_state_reads; - return fc::variant(raw->chunk_state_response); - }; - raw->inbound->discard_envelope_chunks = [raw]() -> fc::variant { - ++raw->discard_calls; - if (raw->discard_failure) throw *raw->discard_failure; - return fc::variant(std::string(hex_prefix) + abi_word(0)); + return fc::variant(std::string(hex_prefix) + abi_word(raw->delivery_calls.size())); }; - raw->inbound->next_epoch_index = [raw](const block_number_or_tag_t& block) -> fc::variant { + // Every bookkeeping read is the outpost's OWN cursor, so it is taken at + // `latest` — at `finalized` a continuation would wait out finality between + // every stretch of dispatch. + const auto expect_latest = [](const block_number_or_tag_t& block) { BOOST_CHECK(std::holds_alternative(block)); BOOST_CHECK(std::get(block) == block_tag_t::latest); + }; + raw->inbound->next_epoch_index = [raw, expect_latest](const block_number_or_tag_t& block) -> fc::variant { + expect_latest(block); ++raw->next_epoch_reads; return fc::variant(raw->next_epoch_index_response); }; + raw->inbound->dispatch_spill = + [raw, expect_latest](const block_number_or_tag_t& block, uint32_t& epoch_index) -> fc::variant { + expect_latest(block); + BOOST_CHECK_EQUAL(epoch_index, test_wire_epoch); + return fc::variant(raw->spill_response_at(raw->spill_reads++)); + }; + raw->inbound->epoch_deliveries = + [raw, expect_latest](const block_number_or_tag_t& block, uint32_t& epoch_index, + std::string& operator_address) -> fc::variant { + expect_latest(block); + BOOST_CHECK_EQUAL(epoch_index, test_wire_epoch); + BOOST_CHECK_EQUAL(operator_address, raw->outpost->signer_address_hex()); + ++raw->settlement_reads; + return fc::variant(raw->own_delivery_response); + }; + raw->inbound->pending_epoch_hash = [raw, expect_latest](const block_number_or_tag_t& block) -> fc::variant { + expect_latest(block); + return fc::variant(raw->pending_hash_response); + }; fixture->outpost = std::make_unique( stack.entry, @@ -579,32 +574,17 @@ std::unique_ptr create_chunked_delivery_fixture() { return fixture; } -/// Assert that `calls` covers `[first_chunk, total_chunks)` of `envelope` with -/// exact slices: every non-final chunk is exactly `ETHEREUM_MAX_CHUNK_BYTES` -/// and the final one carries the remainder. -void check_chunk_call_sequence(const std::vector& calls, - const std::vector& envelope, - uint32_t epoch_index, - uint16_t first_chunk) { - const auto total_chunks = sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()); - BOOST_REQUIRE_EQUAL(calls.size(), static_cast(total_chunks - first_chunk)); - - for (size_t i = 0; i < calls.size(); ++i) { - const auto& call = calls[i]; - const auto chunk = static_cast(first_chunk + i); +/// Assert that every observed `epochIn` carried `envelope` whole, addressed to +/// `epoch_index`. +void check_delivery_calls(const std::vector& calls, + const std::vector& envelope, + uint32_t epoch_index, + size_t expected_calls) { + BOOST_REQUIRE_EQUAL(calls.size(), expected_calls); + const auto expected_hex = fc::to_hex(envelope.data(), static_cast(envelope.size())); + for (const auto& call : calls) { BOOST_CHECK_EQUAL(call.epoch_index, epoch_index); - BOOST_CHECK_EQUAL(call.chunk_index, chunk); - BOOST_CHECK_EQUAL(call.total_chunks, total_chunks); - BOOST_CHECK_EQUAL(call.total_bytes, static_cast(envelope.size())); - - const size_t offset = static_cast(chunk) * sysio::ETHEREUM_MAX_CHUNK_BYTES; - const size_t length = - std::min(sysio::ETHEREUM_MAX_CHUNK_BYTES, envelope.size() - offset); - if (chunk + 1 < total_chunks) { - BOOST_CHECK_EQUAL(length, sysio::ETHEREUM_MAX_CHUNK_BYTES); - } - BOOST_CHECK_EQUAL(call.chunk_hex, - fc::to_hex(envelope.data() + offset, static_cast(length))); + BOOST_CHECK_EQUAL(call.envelope_hex, expected_hex); } } @@ -616,7 +596,7 @@ constexpr std::string_view attestation_blackhole_address = "0x000000000000000000 constexpr std::string_view test_syndication_pool_address = "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"; constexpr std::string_view test_moved_syndication_pool_address = "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9"; /// `keccak256("WIRE_NoYield()")[0..4]` and the pool's other two refusals, written out so these -/// tests pin the client's own hashing of the signatures (as `chunk_buffer_missing_selector` does). +/// tests pin the client's own hashing of the signatures rather than restating it. constexpr std::string_view no_yield_selector = "053716d1"; constexpr std::string_view yield_below_deadband_selector = "45441430"; constexpr std::string_view pool_underbacked_selector = "358cc7e9"; @@ -959,23 +939,30 @@ BOOST_AUTO_TEST_CASE(opp_inbound_contract_client_construction) try { BOOST_CHECK(!abis.empty()); // Every ABI entry the typed `opp_inbound_contract_client` binds at - // construction: the chunked write, the owner-bound resume read, the - // recovery write, and the epoch view the resume path falls back on. - bool has_epoch_in = false, has_next_epoch = false; + // construction: the whole-envelope write and the four cursor views the + // deliver-or-continue decision reads. The staged-chunk surface is gone. + bool has_epoch_in = false, has_next_epoch = false, has_spill = false; + bool has_deliveries = false, has_pending_hash = false; bool has_discard = false, has_chunk_state = false; for (auto& c : abis) { if (c.name == "epochIn") has_epoch_in = true; if (c.name == "nextEpochIndex") has_next_epoch = true; + if (c.name == "dispatchSpill") has_spill = true; + if (c.name == "epochDeliveries") has_deliveries = true; + if (c.name == "pendingEpochHash") has_pending_hash = true; if (c.name == "discardEnvelopeChunks") has_discard = true; if (c.name == "envelopeChunkState") has_chunk_state = true; } BOOST_CHECK(has_epoch_in); BOOST_CHECK(has_next_epoch); - BOOST_CHECK(has_discard); - BOOST_CHECK(has_chunk_state); + BOOST_CHECK(has_spill); + BOOST_CHECK(has_deliveries); + BOOST_CHECK(has_pending_hash); + BOOST_CHECK(!has_discard); + BOOST_CHECK(!has_chunk_state); } FC_LOG_AND_RETHROW(); -BOOST_AUTO_TEST_CASE(epoch_in_abi_encoding_with_chunk_params) try { +BOOST_AUTO_TEST_CASE(epoch_in_abi_encoding_whole_envelope) try { auto abis = load_abi_fixture(opp_inbound_abi_fixture); // Find the epochIn ABI entry @@ -985,30 +972,24 @@ BOOST_AUTO_TEST_CASE(epoch_in_abi_encoding_with_chunk_params) try { } BOOST_REQUIRE(epoch_in_abi != nullptr); - // Chunked delivery: (uint32 epochIndex, uint16 chunkIndex, uint16 totalChunks, - // uint32 totalBytes, bytes chunkData) + // Whole-envelope delivery: (uint32 epochIndex, bytes envelopeData). BOOST_REQUIRE_EQUAL(epoch_in_abi->inputs.size(), epoch_in_input_count); BOOST_CHECK(epoch_in_abi->inputs[0].type == eth::abi::data_type::uint32); - BOOST_CHECK(epoch_in_abi->inputs[1].type == eth::abi::data_type::uint16); - BOOST_CHECK(epoch_in_abi->inputs[2].type == eth::abi::data_type::uint16); - BOOST_CHECK(epoch_in_abi->inputs[3].type == eth::abi::data_type::uint32); - BOOST_CHECK(epoch_in_abi->inputs[4].type == eth::abi::data_type::bytes); + BOOST_CHECK(epoch_in_abi->inputs[1].type == eth::abi::data_type::bytes); - // Encode the five chunk params — this is what the batch operator now does. - std::string test_chunk_hex = "120c0a040800100012040800100028deeef5ce06300138"; + // Encode the two params — this is what the batch operator does. + std::string test_envelope_hex = "120c0a040800100012040800100028deeef5ce06300138"; auto encoded = contract_encode_data( *epoch_in_abi, - std::vector{fc::variant(uint64_t{test_wire_epoch}), - fc::variant(uint64_t{0}), - fc::variant(uint64_t{1}), - fc::variant(uint64_t{test_chunk_hex.size() / hex_chars_per_byte}), - fc::variant(test_chunk_hex)}); + std::vector{fc::variant(uint64_t{test_wire_epoch}), fc::variant(test_envelope_hex)}); BOOST_CHECK(!encoded.empty()); - // keccak256("epochIn(uint32,uint16,uint16,uint32,bytes)")[0..4) — the old - // single-`bytes` selector (0xcfae3118) is gone with the old signature. + // keccak256("epochIn(uint32,bytes)")[0..4) — neither retired form's + // selector survives. BOOST_CHECK_EQUAL(encoded.substr(0, evm_function_selector_hex_chars), std::string(epoch_in_selector)); + BOOST_CHECK(encoded.substr(0, evm_function_selector_hex_chars) != + std::string(retired_chunked_epoch_in_selector)); BOOST_CHECK(encoded.substr(0, evm_function_selector_hex_chars) != std::string(retired_single_bytes_epoch_in_selector)); @@ -1258,135 +1239,63 @@ BOOST_AUTO_TEST_CASE(read_emitted_envelope_digest_reads_the_outpost_record) try } FC_LOG_AND_RETHROW(); // --------------------------------------------------------------------------- -// Chunked WIRE -> Ethereum envelope delivery +// Whole-envelope WIRE -> Ethereum delivery // --------------------------------------------------------------------------- -/// Pin the compiled chunk constants and the ceil-division that derives -/// `totalChunks`. These values are mirrored in wire-ethereum's -/// `OPPCommon.sol`; a silent drift on either side breaks every delivery, so -/// both sides carry an equivalent pin. -BOOST_AUTO_TEST_CASE(envelope_chunk_count_math) try { - namespace chunking = sysio::outpost_ethereum_client_detail; - - BOOST_CHECK_EQUAL(sysio::ETHEREUM_MAX_CHUNK_BYTES, 8'192u); - // Word-aligned so OPPInbound's staging-cell writes stay whole-word. - BOOST_CHECK_EQUAL(sysio::ETHEREUM_MAX_CHUNK_BYTES % evm_abi_word_bytes, 0u); - BOOST_CHECK_EQUAL(sysio::OPP_MAX_ENVELOPE_BYTES, 32'768u); - - BOOST_CHECK_EQUAL(chunking::chunk_count_for(1), 1u); - BOOST_CHECK_EQUAL(chunking::chunk_count_for(sysio::ETHEREUM_MAX_CHUNK_BYTES), 1u); - BOOST_CHECK_EQUAL(chunking::chunk_count_for(sysio::ETHEREUM_MAX_CHUNK_BYTES + 1), 2u); - BOOST_CHECK_EQUAL(chunking::chunk_count_for(2 * sysio::ETHEREUM_MAX_CHUNK_BYTES), 2u); - BOOST_CHECK_EQUAL(chunking::chunk_count_for(2 * sysio::ETHEREUM_MAX_CHUNK_BYTES + 1), 3u); - BOOST_CHECK_EQUAL(chunking::chunk_count_for(3 * sysio::ETHEREUM_MAX_CHUNK_BYTES), 3u); - // The platform cap is an exact multiple: four full chunks, no remainder. - BOOST_CHECK_EQUAL(chunking::chunk_count_for(sysio::OPP_MAX_ENVELOPE_BYTES), 4u); - BOOST_CHECK_EQUAL(sysio::OPP_MAX_ENVELOPE_BYTES % sysio::ETHEREUM_MAX_CHUNK_BYTES, 0u); - - // Ragged tail: the first chunks are exactly MAX_CHUNK_BYTES, the last is - // the remainder. - BOOST_CHECK_EQUAL(chunking::chunk_count_for(three_chunk_envelope_bytes), 3u); - BOOST_CHECK_EQUAL(three_chunk_envelope_bytes - 2 * sysio::ETHEREUM_MAX_CHUNK_BYTES, 3'616u); - - // Unlike Solana — whose relay sends `chunks + 1` transactions because a - // zero-data terminal call triggers finalization — Ethereum finalizes inline - // on the chunk that completes the envelope, so the transaction count equals - // the chunk count exactly. The delivery cases below pin that by counting - // observed `epochIn` calls against `chunk_count_for`. -} FC_LOG_AND_RETHROW(); - -/// The client hashes `OPP_ChunkBufferMissing(address)` to recognise the one tolerable discard -/// revert. This pins that hash against the four bytes the deployed contract actually emits, so -/// a wrong signature string cannot quietly make every revert unrecognisable — which would look -/// like nothing more than a few extra abandoned ticks. -BOOST_AUTO_TEST_CASE(chunk_buffer_missing_selector_is_pinned) try { - namespace chunking = sysio::outpost_ethereum_client_detail; - const std::string self{"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"}; - - BOOST_CHECK(chunking::is_chunk_buffer_missing_revert( - encode_address_revert(chunk_buffer_missing_selector, self), self)); - // The node lower-cases what it returns; the configured signer may be checksummed. - BOOST_CHECK(chunking::is_chunk_buffer_missing_revert( - encode_address_revert(chunk_buffer_missing_selector, self), fc::to_lower(self))); - - // Right selector, wrong operator. - BOOST_CHECK(!chunking::is_chunk_buffer_missing_revert( - encode_address_revert(chunk_buffer_missing_selector, foreign_operator_address), self)); - // Right shape, different error. - BOOST_CHECK(!chunking::is_chunk_buffer_missing_revert( - encode_address_revert(no_chunk_slot_selector, self), self)); - // Nothing to identify it by. - BOOST_CHECK(!chunking::is_chunk_buffer_missing_revert("", self)); - BOOST_CHECK(!chunking::is_chunk_buffer_missing_revert("0x", self)); - - // A non-zero byte in the padding is not our address however it decodes. - auto corrupt_padding = encode_address_revert(chunk_buffer_missing_selector, self); - corrupt_padding[hex_prefix.size() + chunk_buffer_missing_selector.size()] = '1'; - BOOST_CHECK(!chunking::is_chunk_buffer_missing_revert(corrupt_padding, self)); -} FC_LOG_AND_RETHROW(); +/// The gas an `epochIn` is funded with follows the client's policy ceiling and +/// never exceeds EIP-7825's cap — estimation cannot size a watchdog-bounded +/// call, so the floor is what carries a full envelope's dispatch. +BOOST_AUTO_TEST_CASE(delivery_gas_floor_is_the_policy_ceiling_bounded_by_the_cap) try { + ethereum_transaction_policy policy{ + .client_id = std::string(latest_slot_test_entry_id), + .chain_id = test_evm_chain_id, + .max_priority_fee_per_gas = maximum_ethereum_transaction_policy_value(), + .max_fee_per_gas = maximum_ethereum_transaction_policy_value(), + .max_gas_limit = maximum_ethereum_transaction_policy_value(), + .max_total_native_cost = maximum_ethereum_transaction_policy_value(), + }; + BOOST_CHECK_EQUAL(sysio::delivery_gas_limit_floor(policy), sysio::EIP_7825_TX_GAS_CAP); -/// The resume decision table, exercised without an EVM node. -BOOST_AUTO_TEST_CASE(chunk_resume_decision_table) try { - namespace chunking = sysio::outpost_ethereum_client_detail; - using action = chunking::chunk_resume_action; + policy.max_gas_limit = fc::uint256{sysio::EIP_7825_TX_GAS_CAP}; + BOOST_CHECK_EQUAL(sysio::delivery_gas_limit_floor(policy), sysio::EIP_7825_TX_GAS_CAP); - // Same address, EIP-55 checksummed and all-lower-case — the ABI decoder - // returns the latter, operators and tooling quote the former. - const std::string self{"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"}; - const std::string self_lower{"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"}; - const uint16_t total_chunks = 4; - const uint32_t total_bytes = sysio::OPP_MAX_ENVELOPE_BYTES; + policy.max_gas_limit = fc::uint256{below_cap_policy_gas_limit}; + BOOST_CHECK_EQUAL(sysio::delivery_gas_limit_floor(policy), below_cap_policy_gas_limit); - const auto decide = [&](const chunking::envelope_chunk_state& staged) { - return chunking::decide_chunk_resume(staged, self, test_wire_epoch, total_chunks, total_bytes); - }; + // An ABI-only construction has no client to read a policy from: no floor. + BOOST_CHECK_EQUAL(sysio::delivery_confirm_options(ethereum_client_ptr{}).gas_limit_floor, 0u); +} FC_LOG_AND_RETHROW(); - // Address comparison tolerates prefix and EIP-55 casing differences. - BOOST_CHECK(chunking::same_evm_address(self, self_lower)); - BOOST_CHECK(chunking::same_evm_address(self, self.substr(hex_prefix.size()))); - BOOST_CHECK(!chunking::same_evm_address(self, foreign_operator_address)); - BOOST_CHECK(!chunking::same_evm_address("", self)); - - // No header at all -> start at chunk 0. - BOOST_CHECK(decide({}).action == action::start_fresh); - BOOST_CHECK_EQUAL(decide({}).start_chunk, 0u); - - // A peer's header is not ours to continue or discard. - chunking::envelope_chunk_state foreign{ - test_wire_epoch, std::string(foreign_operator_address), total_chunks, 2, total_bytes, 0}; - BOOST_CHECK(decide(foreign).action == action::start_fresh); - BOOST_CHECK_EQUAL(decide(foreign).start_chunk, 0u); - - // Our own header, same epoch and shape -> resume at the high-water mark. - chunking::envelope_chunk_state matching{test_wire_epoch, self, total_chunks, 2, total_bytes, 0}; - BOOST_CHECK(decide(matching).action == action::resume); - BOOST_CHECK_EQUAL(decide(matching).start_chunk, 2u); - - // Same epoch, different shape -> discard the superseded cells, restart. - chunking::envelope_chunk_state wrong_chunk_count = matching; - wrong_chunk_count.total_chunks = total_chunks - 1; - BOOST_CHECK(decide(wrong_chunk_count).action == action::discard_and_restart); - - chunking::envelope_chunk_state wrong_size = matching; - wrong_size.total_bytes = total_bytes - 1; - BOOST_CHECK(decide(wrong_size).action == action::discard_and_restart); - - // The final chunk is never stored, so receivedChunks can never reach - // totalChunks; a header claiming otherwise is unusable. - chunking::envelope_chunk_state over_received = matching; - over_received.received_chunks = total_chunks; - BOOST_CHECK(decide(over_received).action == action::discard_and_restart); - - // Our own header for a different epoch -> confirm against nextEpochIndex. - chunking::envelope_chunk_state stale = matching; - stale.epoch_index = test_stale_wire_epoch; - BOOST_CHECK(decide(stale).action == action::confirm_epoch_advanced); - BOOST_CHECK_EQUAL(decide(stale).start_chunk, 0u); +/// The deliver-or-continue decision table, exercised without an EVM node. +BOOST_AUTO_TEST_CASE(delivery_decision_table) try { + namespace detail = sysio::outpost_ethereum_client_detail; + using action = detail::delivery_action; + + const detail::dispatch_spill untipped{}; + const detail::dispatch_spill mid_flight{.tipped = true, .dispatched = 3, .complete = false, .finalized = false}; + const detail::dispatch_spill emit_pending{.tipped = true, .dispatched = 9, .complete = true, .finalized = false}; + const detail::dispatch_spill finalized{.tipped = true, .dispatched = 9, .complete = true, .finalized = true}; + + // Not tipped: deliver, whatever this relay's own record says. + BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, untipped, false) == action::deliver); + BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, untipped, true) == action::deliver); + // A stale outpost cursor is still "this epoch": deliver. + BOOST_CHECK(detail::decide_delivery(test_stale_wire_epoch, test_wire_epoch, untipped, false) == action::deliver); + + // Tipped and unfinished: only the settled digest's deliverers continue. + BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, mid_flight, true) == action::continue_dispatch); + BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, emit_pending, true) == action::continue_dispatch); + BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, mid_flight, false) == action::wait_for_deliverer); + BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, emit_pending, false) == action::wait_for_deliverer); + + // Closed, by either signal: nothing to send. + BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, finalized, true) == action::already_finalized); + BOOST_CHECK(detail::decide_delivery(test_different_wire_epoch, test_wire_epoch, untipped, false) == action::already_finalized); + BOOST_CHECK(detail::decide_delivery(test_different_wire_epoch, test_wire_epoch, mid_flight, true) == action::already_finalized); } FC_LOG_AND_RETHROW(); -/// Guard rails before any transaction is signed. BOOST_AUTO_TEST_CASE(delivery_rejects_empty_and_over_cap_envelopes) try { - auto fixture = create_chunked_delivery_fixture(); + auto fixture = create_whole_envelope_delivery_fixture(); BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( test_wire_epoch, {}, fc::seconds(test_rpc_deadline_seconds)), @@ -1397,333 +1306,177 @@ BOOST_AUTO_TEST_CASE(delivery_rejects_empty_and_over_cap_envelopes) try { test_wire_epoch, over_cap, fc::seconds(test_rpc_deadline_seconds)), fc::assert_exception); - BOOST_CHECK_EQUAL(fixture->chunk_calls.size(), 0u); - BOOST_CHECK_EQUAL(fixture->chunk_state_reads, 0u); + BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); + BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 0u); + BOOST_CHECK_EQUAL(fixture->spill_reads, 0u); } FC_LOG_AND_RETHROW(); -/// The dominant case: an envelope at or below one chunk is a single -/// transaction that stages nothing, so the resume read is skipped entirely. -BOOST_AUTO_TEST_CASE(single_chunk_delivery_skips_the_resume_read) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(sysio::ETHEREUM_MAX_CHUNK_BYTES); +/// The dominant case: the whole envelope goes in ONE call, and once the +/// delivery is recorded without tipping (the rest of the group has yet to +/// deliver) the tick stops rather than re-sending a paid no-op. +BOOST_AUTO_TEST_CASE(delivery_sends_the_whole_envelope_in_one_call) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(sysio::OPP_MAX_ENVELOPE_BYTES); const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); BOOST_CHECK(!tx.empty()); - BOOST_CHECK_EQUAL(fixture->chunk_state_reads, 0u); - BOOST_CHECK_EQUAL(fixture->discard_calls, 0u); - BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 0u); - check_chunk_call_sequence(fixture->chunk_calls, envelope, test_wire_epoch, 0); -} FC_LOG_AND_RETHROW(); - -/// A multi-chunk delivery with nothing staged sends every chunk in order, each -/// exactly MAX_CHUNK_BYTES but the last. -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_sends_every_chunk_in_order) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); - - fixture->outpost->deliver_outbound_envelope( - test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); - - BOOST_CHECK_EQUAL(fixture->chunk_state_reads, 1u); - BOOST_CHECK_EQUAL(fixture->discard_calls, 0u); - check_chunk_call_sequence(fixture->chunk_calls, envelope, test_wire_epoch, 0); + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); + // Read before the delivery, and once more after it to learn it did not tip. + BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 2u); + BOOST_CHECK_EQUAL(fixture->spill_reads, 2u); + // Settlement is only consulted for a tipped epoch. + BOOST_CHECK_EQUAL(fixture->settlement_reads, 0u); } FC_LOG_AND_RETHROW(); -/// Resume: an own, matching header advances the start index to the on-chain -/// high-water mark. Without this a tick whose budget covers k transactions -/// re-sends chunks 0..k-1 forever and never reaches chunk k. -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_resumes_from_the_staged_high_water_mark) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); - - constexpr uint16_t staged_chunks = 2; - fixture->chunk_state_response = encode_envelope_chunk_state_result( - test_wire_epoch, - fixture->outpost->signer_address_hex(), - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), - staged_chunks, - static_cast(envelope.size()), - static_cast(staged_chunks) * sysio::ETHEREUM_MAX_CHUNK_BYTES); +/// A consensus retry against an epoch the outpost has already moved past is +/// abandoned before a single transaction is signed — it would only buy a late +/// no-op — and the EMPTY tx id marks the epoch handled. +BOOST_AUTO_TEST_CASE(delivery_skips_when_the_outpost_epoch_advanced) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->next_epoch_index_response = encode_next_epoch_index_result(test_different_wire_epoch); - fixture->outpost->deliver_outbound_envelope( + const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); - BOOST_CHECK_EQUAL(fixture->chunk_state_reads, 1u); - BOOST_CHECK_EQUAL(fixture->discard_calls, 0u); - check_chunk_call_sequence(fixture->chunk_calls, envelope, test_wire_epoch, staged_chunks); + BOOST_CHECK(tx.empty()); + BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); + BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 1u); } FC_LOG_AND_RETHROW(); -/// `storedBytes` is a uint256 the relay only logs. A value beyond the uint64 -/// range must NOT invalidate the header: doing so would drop back to chunk 0 -/// and re-upload cells the outpost already holds. -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_resumes_despite_an_oversized_stored_bytes_field) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); +/// The cursor's own `finalized` flag closes the epoch even when the epoch +/// cursor read has not caught up with it. +BOOST_AUTO_TEST_CASE(delivery_skips_an_epoch_the_cursor_reports_finalized) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->spill_responses = {encode_dispatch_spill_result(true, 5, true, true)}; - constexpr uint16_t staged_chunks = 2; - fixture->chunk_state_response = encode_envelope_chunk_state_result( - test_wire_epoch, - fixture->outpost->signer_address_hex(), - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), - staged_chunks, - static_cast(envelope.size()), - /*stored_bytes=*/0, - stored_bytes_above_uint64_word()); - - fixture->outpost->deliver_outbound_envelope( + const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); - BOOST_CHECK_EQUAL(fixture->discard_calls, 0u); - check_chunk_call_sequence(fixture->chunk_calls, envelope, test_wire_epoch, staged_chunks); + BOOST_CHECK(tx.empty()); + BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); + BOOST_CHECK_EQUAL(fixture->settlement_reads, 0u); } FC_LOG_AND_RETHROW(); -/// Mid-sequence deadline expiry — the scenario resume exists for. -/// -/// The first chunk burns more wall clock than the whole delivery budget, so the -/// SECOND chunk's pre-flight `throw_if_past_deadline` fires: the tick abandons -/// after a PARTIAL, correctly-ordered prefix rather than rolling back. The next -/// cron tick then resumes from the on-chain high-water mark instead of -/// re-sending chunk 0 forever (the livelock this design removes). -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_abandons_on_deadline_then_resumes_next_tick) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); - BOOST_REQUIRE_EQUAL( - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), 3u); - - constexpr int64_t delivery_budget_ms = 50; - fixture->first_chunk_delay = std::chrono::milliseconds{delivery_budget_ms * 4}; - - BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( - test_wire_epoch, envelope, fc::milliseconds(delivery_budget_ms)), - fc::timeout_exception); - - // Exactly one chunk landed, and it is a correct prefix of the envelope. - BOOST_REQUIRE_EQUAL(fixture->chunk_calls.size(), 1u); - BOOST_CHECK_EQUAL(fixture->chunk_calls[0].chunk_index, 0u); - BOOST_CHECK_EQUAL(fixture->chunk_calls[0].chunk_hex, - fc::to_hex(envelope.data(), sysio::ETHEREUM_MAX_CHUNK_BYTES)); - - // Next tick: the outpost reports the one staged chunk, and the relay picks - // up at chunk 1 rather than restarting. - fixture->chunk_calls.clear(); - fixture->first_chunk_delay = std::chrono::milliseconds{0}; - fixture->chunk_state_response = encode_envelope_chunk_state_result( - test_wire_epoch, - fixture->outpost->signer_address_hex(), - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), - 1, - static_cast(envelope.size()), - sysio::ETHEREUM_MAX_CHUNK_BYTES); +/// A delivery that tips and spills is continued in the SAME tick: the relay +/// re-supplies the envelope until the outpost reports the epoch finalized. +BOOST_AUTO_TEST_CASE(delivery_that_tips_and_spills_continues_in_the_same_tick) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(sysio::OPP_MAX_ENVELOPE_BYTES); + fixture->spill_responses = { + encode_dispatch_spill_result(false, 0, false, false), // before the delivery + encode_dispatch_spill_result(true, 4, false, false), // tipped, spilled after 4 + encode_dispatch_spill_result(true, 11, true, false), // dispatched, emit outstanding + encode_dispatch_spill_result(true, 11, true, true), // finalized + }; + fixture->own_delivery_response = encode_word_result(settled_digest_word); - fixture->outpost->deliver_outbound_envelope( + const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); - BOOST_CHECK_EQUAL(fixture->discard_calls, 0u); - check_chunk_call_sequence(fixture->chunk_calls, envelope, test_wire_epoch, 1); + BOOST_CHECK(!tx.empty()); + // The delivery, then two continuations — every one carrying the whole envelope. + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 3); + BOOST_CHECK_EQUAL(fixture->spill_reads, 4u); + // Settlement is checked for each of the two mid-flight reads. + BOOST_CHECK_EQUAL(fixture->settlement_reads, 2u); } FC_LOG_AND_RETHROW(); -/// A staging header owned by a peer is never adopted and never discarded — the -/// delivery simply starts at chunk 0 and lets the contract's ownership guard -/// arbitrate. -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_ignores_a_peer_owned_header) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); - - fixture->chunk_state_response = encode_envelope_chunk_state_result( - test_wire_epoch, - foreign_operator_address, - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), - 2, - static_cast(envelope.size()), - 0); +/// Resume: a tick that finds the epoch already tipped on this relay's digest +/// picks the continuation up without a fresh delivery — the contract would +/// refuse one, and the relay does not pay to find that out. +BOOST_AUTO_TEST_CASE(delivery_continues_a_tipped_epoch_it_delivered_until_it_finalizes) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->spill_responses = { + encode_dispatch_spill_result(true, 3, false, false), + encode_dispatch_spill_result(true, 3, true, false), + encode_dispatch_spill_result(true, 3, true, true), + }; + fixture->own_delivery_response = encode_word_result(settled_digest_word); - fixture->outpost->deliver_outbound_envelope( + const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); - BOOST_CHECK_EQUAL(fixture->discard_calls, 0u); - BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 0u); - check_chunk_call_sequence(fixture->chunk_calls, envelope, test_wire_epoch, 0); + BOOST_CHECK(!tx.empty()); + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 2); + BOOST_CHECK_EQUAL(fixture->settlement_reads, 2u); } FC_LOG_AND_RETHROW(); -/// A CURRENT-epoch header describing a different envelope is discarded first, -/// then the upload restarts from chunk 0. -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_discards_a_superseded_current_epoch_header) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); - - fixture->chunk_state_response = encode_envelope_chunk_state_result( - test_wire_epoch, - fixture->outpost->signer_address_hex(), - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), - 1, - static_cast(envelope.size()) - 1, // a different envelope, same epoch - sysio::ETHEREUM_MAX_CHUNK_BYTES); +/// A relay that never delivered has no claim on a tipped epoch's +/// continuation: its deliverers carry it, and this relay sends nothing. +BOOST_AUTO_TEST_CASE(delivery_leaves_a_tipped_epoch_it_did_not_deliver_to_its_deliverers) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->spill_responses = {encode_dispatch_spill_result(true, 2, false, false)}; + fixture->own_delivery_response = encode_word_result(zero_digest_word); - fixture->outpost->deliver_outbound_envelope( + const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); - BOOST_CHECK_EQUAL(fixture->discard_calls, 1u); - check_chunk_call_sequence(fixture->chunk_calls, envelope, test_wire_epoch, 0); + BOOST_CHECK(tx.empty()); + BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); + BOOST_CHECK_EQUAL(fixture->settlement_reads, 1u); } FC_LOG_AND_RETHROW(); -/// A reverting discard means the header is ALREADY clear (the contract's -/// `OPP_ChunkBufferMissing`), which is exactly the state the relay wanted: -/// log it and upload from chunk 0. Only transport failures abandon the tick. -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_treats_a_reverting_discard_as_already_clear) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); +/// A relay whose delivery settled on a DIFFERENT digest — a minority envelope +/// — must not resume the epoch from its own divergent bytes. +BOOST_AUTO_TEST_CASE(delivery_does_not_continue_from_a_minority_digest) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->spill_responses = {encode_dispatch_spill_result(true, 2, false, false)}; + fixture->own_delivery_response = encode_word_result(divergent_digest_word); - fixture->chunk_state_response = encode_envelope_chunk_state_result( - test_wire_epoch, - fixture->outpost->signer_address_hex(), - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), - 1, - static_cast(envelope.size()) - 1, - sysio::ETHEREUM_MAX_CHUNK_BYTES); - fixture->discard_failure = fc::network::json_rpc::json_rpc_error( - contract_revert_rpc_code, "execution reverted", - fc::variant{encode_address_revert(chunk_buffer_missing_selector, - fixture->outpost->signer_address_hex())}); - - fixture->outpost->deliver_outbound_envelope( + const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); - BOOST_CHECK_EQUAL(fixture->discard_calls, 1u); - check_chunk_call_sequence(fixture->chunk_calls, envelope, test_wire_epoch, 0); -} FC_LOG_AND_RETHROW(); - -/// A revert the client cannot identify is NOT proof the staging header is clear. -/// -/// Code 3 says only that the EVM executed and reverted. A node whose proxy points at a -/// mismatched implementation, or that returns no revert bytes at all, produces the same code -/// for a completely different reason — and the superseded header may still be staged. Each of -/// these must abandon the tick rather than upload chunk 0 against it. -/// -/// Abandoning is self-healing rather than terminal: this path is only reached when a header -/// WAS read, so the next cron tick re-reads it and either resumes or starts fresh. -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_abandons_on_an_unrecognized_discard_revert) try { - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); - // Built against a throwaway fixture so the cases can name this relay's real signer. - const auto signer = create_chunked_delivery_fixture()->outpost->signer_address_hex(); - - const std::vector> unrecognized{ - {"no revert bytes at all", ""}, - {"an empty revert payload", "0x"}, - {"a neighbouring error of identical shape", - encode_address_revert(no_chunk_slot_selector, signer)}, - {"our error naming a different operator", - encode_address_revert(chunk_buffer_missing_selector, test_other_operator_address)}, - {"our selector with the argument truncated away", - "0x" + std::string(chunk_buffer_missing_selector)}, - }; - - for (const auto& [description, revert_data] : unrecognized) { - BOOST_TEST_CONTEXT(description) { - auto fixture = create_chunked_delivery_fixture(); - fixture->chunk_state_response = encode_envelope_chunk_state_result( - test_wire_epoch, - fixture->outpost->signer_address_hex(), - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), - 1, - static_cast(envelope.size()) - 1, - sysio::ETHEREUM_MAX_CHUNK_BYTES); - fixture->discard_failure = fc::network::json_rpc::json_rpc_error( - contract_revert_rpc_code, "execution reverted", fc::variant{revert_data}); - - BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( - test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), - fc::exception); - - BOOST_CHECK_EQUAL(fixture->discard_calls, 1u); - BOOST_CHECK(fixture->chunk_calls.empty()); - } - } + BOOST_CHECK(tx.empty()); + BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); } FC_LOG_AND_RETHROW(); -/// A JSON-RPC protocol error is NOT a revert, and must abandon the tick rather than be tolerated. +/// Mid-sequence deadline expiry — the scenario the cursor exists for. /// -/// A parse error means the node never executed the call, so the staging header may still hold -/// the superseded envelope. Treating it as "already clear" would upload chunk 0 against that -/// stale header. Such a response is required by the JSON-RPC specification to carry a null id, -/// which the transport now decodes as json_rpc_error rather than a bare fc::exception, so this -/// catch discriminates on the code instead of on the exception type. -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_abandons_on_a_protocol_error_during_discard) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); - - fixture->chunk_state_response = encode_envelope_chunk_state_result( - test_wire_epoch, - fixture->outpost->signer_address_hex(), - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), - 1, - static_cast(envelope.size()) - 1, - sysio::ETHEREUM_MAX_CHUNK_BYTES); - // The payload is a well-formed OPP_ChunkBufferMissing naming this signer, so only the code - // distinguishes it. A JSON-RPC error object may carry `data`, and with an empty one here the - // selector check would reject it first and the code guard would never be exercised. - fixture->discard_failure = fc::network::json_rpc::json_rpc_error( - json_rpc_parse_error_code, "Parse error", - fc::variant{encode_address_revert(chunk_buffer_missing_selector, - fixture->outpost->signer_address_hex())}); +/// The delivery burns more wall clock than the whole budget, so the +/// continuation's pre-flight `throw_if_past_deadline` fires: the tick abandons +/// after the delivery landed rather than rolling anything back. The next tick +/// reads the cursor and continues from it instead of re-delivering. +BOOST_AUTO_TEST_CASE(delivery_abandons_on_deadline_then_continues_next_tick) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(sysio::OPP_MAX_ENVELOPE_BYTES); - BOOST_CHECK_THROW( - fixture->outpost->deliver_outbound_envelope(test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), - fc::exception); + constexpr int64_t delivery_budget_ms = 50; + fixture->first_call_delay = std::chrono::milliseconds{delivery_budget_ms * 4}; + fixture->spill_responses = { + encode_dispatch_spill_result(false, 0, false, false), + encode_dispatch_spill_result(true, 6, false, false), + }; + fixture->own_delivery_response = encode_word_result(settled_digest_word); - BOOST_CHECK_EQUAL(fixture->discard_calls, 1u); - BOOST_CHECK(fixture->chunk_calls.empty()); -} FC_LOG_AND_RETHROW(); + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::milliseconds(delivery_budget_ms)), + fc::timeout_exception); -/// A stale OWN header on a consensus retry is the signature of an epoch that -/// advanced underneath us. One `nextEpochIndex()` read confirms it, and the -/// delivery is abandoned before a single transaction is signed — otherwise the -/// retry pays for up to `totalChunks` late no-ops. -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_skips_when_the_outpost_epoch_advanced) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); - - // A header epoch differing from the delivery epoch triggers the - // confirmation read. - fixture->chunk_state_response = encode_envelope_chunk_state_result( - test_stale_wire_epoch, - fixture->outpost->signer_address_hex(), - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), - 1, - static_cast(envelope.size()), - sysio::ETHEREUM_MAX_CHUNK_BYTES); - fixture->next_epoch_index_response = encode_next_epoch_index_result(test_different_wire_epoch); + // Exactly the delivery landed, carrying the whole envelope. + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); + + // Next tick: the outpost reports the epoch tipped on our digest with six + // attestations routed, and the relay continues rather than re-delivering. + fixture->delivery_calls.clear(); + fixture->first_call_delay = std::chrono::milliseconds{0}; + fixture->spill_reads = 0; + fixture->spill_responses = { + encode_dispatch_spill_result(true, 6, false, false), + encode_dispatch_spill_result(true, 6, true, true), + }; const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); - BOOST_CHECK(tx.empty()); - BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 1u); - BOOST_CHECK_EQUAL(fixture->chunk_calls.size(), 0u); - BOOST_CHECK_EQUAL(fixture->discard_calls, 0u); -} FC_LOG_AND_RETHROW(); - -/// The same stale-header shape when the outpost has NOT advanced: the epoch is -/// still deliverable, so the upload proceeds from chunk 0. -BOOST_AUTO_TEST_CASE(multi_chunk_delivery_proceeds_when_the_outpost_epoch_has_not_advanced) try { - auto fixture = create_chunked_delivery_fixture(); - auto envelope = make_chunked_envelope(three_chunk_envelope_bytes); - - fixture->chunk_state_response = encode_envelope_chunk_state_result( - test_stale_wire_epoch, - fixture->outpost->signer_address_hex(), - sysio::outpost_ethereum_client_detail::chunk_count_for(envelope.size()), - 1, - static_cast(envelope.size()), - sysio::ETHEREUM_MAX_CHUNK_BYTES); - fixture->next_epoch_index_response = encode_next_epoch_index_result(test_wire_epoch); - - fixture->outpost->deliver_outbound_envelope( - test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); - - BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 1u); - check_chunk_call_sequence(fixture->chunk_calls, envelope, test_wire_epoch, 0); + BOOST_CHECK(!tx.empty()); + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); } FC_LOG_AND_RETHROW(); // ── `crank_outpost` ────────────────────────────────────────────────────── @@ -1756,7 +1509,7 @@ BOOST_AUTO_TEST_CASE(realize_yield_refusal_selectors_are_pinned) try { BOOST_CHECK(!crank::classify_realize_yield_revert( encode_address_revert(access_managed_unauthorized_selector, test_other_operator_address))); BOOST_CHECK(!crank::classify_realize_yield_revert( - encode_address_revert(chunk_buffer_missing_selector, test_other_operator_address))); + encode_address_revert(unrelated_address_error_selector, test_other_operator_address))); BOOST_CHECK(!crank::classify_realize_yield_revert("")); BOOST_CHECK(!crank::classify_realize_yield_revert("0x")); } FC_LOG_AND_RETHROW(); @@ -1938,7 +1691,7 @@ BOOST_AUTO_TEST_CASE(gas_limit_policy_bounds_the_buffered_limit_at_the_eip_7825_ .chain_id = test_evm_chain_id, .max_priority_fee_per_gas = maximum_ethereum_transaction_policy_value(), .max_fee_per_gas = maximum_ethereum_transaction_policy_value(), - .max_gas_limit = fc::uint256{eip_7825_tx_gas_cap}, + .max_gas_limit = fc::uint256{sysio::EIP_7825_TX_GAS_CAP}, .max_total_native_cost = maximum_ethereum_transaction_policy_value(), }; diff --git a/tests/fixtures/ethereum-abi-opp-current.json b/tests/fixtures/ethereum-abi-opp-current.json index ab1e98df9e..66811df72d 100644 --- a/tests/fixtures/ethereum-abi-opp-current.json +++ b/tests/fixtures/ethereum-abi-opp-current.json @@ -78,11 +78,54 @@ "name": "InvalidInitialization", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "position", + "type": "uint256" + } + ], + "name": "MalformedVarint", + "type": "error" + }, { "inputs": [], "name": "NotInitializing", "type": "error" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "expected", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "found", + "type": "bytes32" + } + ], + "name": "OPP_DigestMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "expected", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "actual", + "type": "uint32" + } + ], + "name": "OPP_EnvelopeEpochMismatch", + "type": "error" + }, { "inputs": [ { @@ -115,6 +158,27 @@ "name": "OPP_EpochHashMismatch", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "handler", + "type": "address" + }, + { + "internalType": "AttestationType", + "name": "attestationType", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "gasBound", + "type": "uint256" + } + ], + "name": "OPP_HandlerGasExhausted", + "type": "error" + }, { "inputs": [ { @@ -145,17 +209,12 @@ { "inputs": [ { - "internalType": "uint256", - "name": "required", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "provided", - "type": "uint256" + "internalType": "string", + "name": "reason", + "type": "string" } ], - "name": "OPP_InsufficientSignatureWeight", + "name": "OPP_InvalidInitialRoster", "type": "error" }, { @@ -179,6 +238,27 @@ "name": "OPP_InvalidRetentionConfig", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "expected", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "actual", + "type": "bytes32" + } + ], + "name": "OPP_MessageChainMismatch", + "type": "error" + }, { "inputs": [ { @@ -205,6 +285,22 @@ "name": "OPP_NoPendingAttestations", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "messageId", + "type": "bytes" + } + ], + "name": "OPP_NonCanonicalMessageId", + "type": "error" + }, { "inputs": [ { @@ -216,6 +312,22 @@ "name": "OPP_NonCanonicalPreviousEpochHash", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "previousMessageId", + "type": "bytes" + } + ], + "name": "OPP_NonCanonicalPreviousMessageId", + "type": "error" + }, { "inputs": [ { @@ -232,6 +344,27 @@ "name": "OPP_NonSequentialEpoch", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expected", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actual", + "type": "uint256" + } + ], + "name": "OPP_NonSequentialMessage", + "type": "error" + }, { "inputs": [ { @@ -269,6 +402,38 @@ "name": "OPP_OperatorAlreadyDelivered", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "projectedEnvelopeBytes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxEnvelopeBytes", + "type": "uint256" + } + ], + "name": "OPP_OutboundMessageUnpackable", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "projectedBytes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxBytes", + "type": "uint256" + } + ], + "name": "OPP_OutboundQueueFull", + "type": "error" + }, { "inputs": [ { @@ -315,12 +480,27 @@ { "inputs": [ { - "internalType": "AttestationType", - "name": "attestationType", - "type": "uint16" + "internalType": "uint32", + "name": "epochIndex", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "rosterEpochIndex", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "activeGroupIndex", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "groupCount", + "type": "uint32" } ], - "name": "OPP_UnhandledAttestationType", + "name": "OPP_UnmappedEpochGroup", "type": "error" }, { @@ -378,6 +558,38 @@ "name": "AuthorityUpdated", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "epochIndex", + "type": "uint32" + } + ], + "name": "EnvelopeRetentionCatchUpPruned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "previousRetentionEpochs", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "retentionEpochs", + "type": "uint32" + } + ], + "name": "EnvelopeRetentionConfigUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -417,6 +629,32 @@ "name": "Upgraded", "type": "event" }, + { + "inputs": [], + "name": "ENVELOPE_BASE_BYTES", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_CHUNK_BYTES", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "MAX_ENVELOPE_BYTES", @@ -430,6 +668,32 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "MESSAGE_ESTIMATE_OVERHEAD_BYTES", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OUTBOUND_WORD_COUNT", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "UPGRADE_INTERFACE_VERSION", @@ -631,6 +895,13 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "initializeOutboundQueueBytes", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "isConsumingScheduledOp", @@ -672,12 +943,12 @@ }, { "inputs": [], - "name": "latestOutboundEnvelope", + "name": "latestOutboundEpoch", "outputs": [ { - "internalType": "bytes", + "internalType": "uint32", "name": "", - "type": "bytes" + "type": "uint32" } ], "stateMutability": "view", @@ -685,12 +956,12 @@ }, { "inputs": [], - "name": "latestOutboundEpoch", + "name": "oppInboundAddress", "outputs": [ { - "internalType": "uint32", + "internalType": "address", "name": "", - "type": "uint32" + "type": "address" } ], "stateMutability": "view", @@ -725,6 +996,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "outboundLen", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "outboundRetentionConfig", @@ -751,6 +1035,24 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "fromWord", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "toWord", + "type": "uint256" + } + ], + "name": "prewarmOutbound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "proxiableUUID", @@ -777,6 +1079,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "queuedEstimatedBytes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "queuedMessageCount", @@ -1028,6 +1343,32 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bool", + "name": "active", + "type": "bool" + } + ], + "name": "setInboundDispatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "oppInbound_", + "type": "address" + } + ], + "name": "setOPPInbound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1047,26 +1388,26 @@ "type": "function" } ], - "bytecode": "0x60a0806040523460c857306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1660b957506001600160401b036002600160401b0319828216016075575b6040516142ba90816100cd82396080518181816106a2015261370a0152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80806056565b63f92ee8a960e01b8152600490fd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806304676e38146101e45780630d6f1e9d146101df57806327e935a1146101da57806347453a2a146101d55780634f1ef286146101d05780634fca79cb146101cb57806352d1902d146101c6578063621fbd59146101c15780637a9e5e4b146101bc5780638fb36037146101b757806393ec06be146101b257806395fabcf0146101ad578063a3ad9cc3146101a8578063a593aec2146101a3578063a9e50c101461019e578063ad3cb1cc14610199578063b244542b14610194578063b8f8d7e51461018f578063bf7e214f1461018a578063c16a8a5614610185578063c4d66de814610180578063c94c93e71461017b578063d5b5272a14610176578063e006490014610171578063e0c14aa81461016c578063e2fc04ae14610167578063e36f9a0714610162578063e44efb5a1461015d5763f5786b4b14610158575f80fd5b6115f5565b6115d6565b6115b9565b61149c565b6110e9565b6110ac565b611010565b610fe1565b610eb5565b610e92565b610e5e565b610e3b565b610e0c565b610c07565b610bcf565b610af3565b610819565b6107fc565b6107df565b61078f565b610715565b6106f9565b610690565b6105e4565b6104d9565b61033b565b6102e4565b610290565b610208565b63ffffffff8116036101f757565b5f80fd5b3590610206826101e9565b565b346101f75760203660031901126101f7576060600435610227816101e9565b63ffffffff8091165f52600960205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b5f9103126101f757565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346101f7575f3660031901126101f75763ffffffff600c54166102b1610c97565b906102ce604051928392835260406020840152604083019061026c565b0390f35b60209060031901126101f75760043590565b346101f7576102f2366102d2565b6102fc3633612905565b8015610315576004541561030c57005b6004555f600355005b604051636ad43cc360e11b8152600490fd5b634e487b7160e01b5f52603260045260245ffd5b346101f757610349366102d2565b6006548110156101f75760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01546040516001600160a01b039091168152602090f35b600435906001600160a01b03821682036101f757565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b038211176103d457604052565b6103a5565b602081019081106001600160401b038211176103d457604052565b604081019081106001600160401b038211176103d457604052565b60e081019081106001600160401b038211176103d457604052565b6001600160401b0381116103d457604052565b90601f801991011681019081106001600160401b038211176103d457604052565b604051906102068261040f565b60405190610206826103b9565b6001600160401b0381116103d457601f01601f191660200190565b81601f820112156101f7578035906104aa82610478565b926104b8604051948561043d565b828452602083830101116101f757815f926020809301838601378301015290565b60403660031901126101f7576104ed61038f565b6024356001600160401b0381116101f75761050c903690600401610493565b90610515613700565b61051f3633612905565b610527613700565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f93816105b3575b5061057957604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f80516020614245833981519152830361059a576105989250613cea565b005b604051632a87526960e21b815260048101849052602490fd5b6105d691945060203d6020116105dd575b6105ce818361043d565b810190612496565b925f61054f565b503d6105c4565b346101f75760203660031901126101f7576102ce600435610604816101e9565b60405f818051610613816103b9565b828152826020820152015263ffffffff8092165f5260096020526001815f206001600160401b03835194610646866103b9565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b346101f7575f3660031901126101f7577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630036106e75760206040515f805160206142458339815191528152f35b60405163703e46dd60e11b8152600490fd5b346101f7575f3660031901126101f75760205f54604051908152f35b346101f75760203660031901126101f75761072e61038f565b5f80516020614225833981519152546001600160a01b0391908216330361077857803b1561075f5761059890612a21565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b346101f7575f3660031901126101f7575f805160206142258339815191525460a01c60ff16156107d7576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f6107c4565b346101f7575f3660031901126101f7576020604051620100008152f35b346101f7575f3660031901126101f7576020600354604051908152f35b346101f75760203660031901126101f757600435610836816101e9565b6108403633612905565b600c5463ffffffff1661085e610855826118a4565b63ffffffff1690565b63ffffffff831603610ac457506108736118f8565b63ffffffff821660608201526108a46108946001600160401b034216611935565b6001600160401b03166040830152565b6108ac612a79565b60a08201525f60085480151580610952575b7fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d88761094d866109416108ef88612b56565b9161090062010000845111156121f7565b610922835160208501206109148184612f19565b61091d83612f7f565b600d55565b61092b83612269565b63ffffffff1663ffffffff19600c541617600c55565b60405191829182610df8565b0390a1005b5f6101005b838210610a6e575b50506109759083159081610a66575b5015611b50565b8161097f81611c2b565b60c085019081525f5b828110610a2e57505061099a91611c8e565b905f5b828110610a0057505f5b8181106109ef575050916109416108ef61094d936109e57fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d88796600855565b93508294506108be565b6001906109fa612125565b016109a7565b80610a28610a18610a1360019486611b35565b611951565b50610a2283611951565b90611fb6565b0161099d565b6001919250610a5c81610a4081611951565b50610a4c865191611a57565b610a568383611c7a565b52611c7a565b5001908391610988565b90505f61096e565b610a8a610a85610a7f849794611951565b50611a57565b612ae8565b9062010000610a998383611b35565b11610abb57610aad600192610ab392611b35565b92611b42565b940190610957565b5081945061095f565b610acd906118a4565b6040516307d3e83d60e51b815263ffffffff918216600482015291166024820152604490fd5b346101f75760203660031901126101f757600435610b10816101e9565b610b1a3633612905565b63ffffffff80821690815f52600960205260405f20541615610b8a5761059891610b6b610b7f92610b64610855610b56600a5463ffffffff1690565b600c5463ffffffff16612fd7565b101561240b565b63ffffffff165f52600960205260405f2090565b60015f918281550155565b60405162461bcd60e51b815260206004820152601760248201527f4f50503a207265636f7264206e6f742070726573656e740000000000000000006044820152606490fd5b346101f7575f3660031901126101f75760206001600160401b0360015416604051908152f35b60405190610c02826103d9565b5f8252565b346101f7575f3660031901126101f7576102ce604051610c26816103f4565b60058152640352e302e360dc1b602082015260405191829160208352602083019061026c565b634e487b7160e01b5f525f60045260245ffd5b90600182811c92168015610c8d575b6020831014610c7957565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610c6e565b604051905f82600b5491610caa83610c5f565b80835292602090600190818116908115610d345750600114610cd5575b50506102069250038361043d565b915092600b5f527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9935f925b828410610d1c57506102069450505081016020015f80610cc7565b85548885018301529485019487945092810192610d01565b9150506020925061020694915060ff191682840152151560051b8201015f80610cc7565b9060405191825f8254610d6a81610c5f565b908184526020946001916001811690815f14610dd65750600114610d98575b5050506102069250038361043d565b5f90815285812095935091905b818310610dbe57505061020693508201015f8080610d89565b85548884018501529485019487945091830191610da5565b9250505061020694925060ff191682840152151560051b8201015f8080610d89565b906020610e0992818152019061026c565b90565b346101f7575f3660031901126101f7576102ce610e27610c97565b60405191829160208352602083019061026c565b346101f7575f3660031901126101f757602063ffffffff600c5416604051908152f35b346101f7575f3660031901126101f7575f80516020614225833981519152546040516001600160a01b039091168152602090f35b346101f7575f3660031901126101f757602063ffffffff600a5416604051908152f35b346101f75760203660031901126101f757610ece61038f565b5f8051602061426583398151915254906001600160401b0360ff8360401c1615921680159081610fd9575b6001149081610fcf575b159081610fc6575b50610fb4575f80516020614265833981519152805467ffffffffffffffff19166001179055610f3e9082610f9057612ff6565b610f4457005b5f80516020614265833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290806020810161094d565b5f80516020614265833981519152805460ff60401b1916600160401b179055612ff6565b60405163f92ee8a960e01b8152600490fd5b9050155f610f0b565b303b159150610f03565b839150610ef9565b346101f75760203660031901126101f7576004355f526005602052602060ff60405f2054166040519015158152f35b346101f75760203660031901126101f75760043561102d816101e9565b6110373633612905565b604051611043816103d9565b63ffffffff8092168082521561106757511663ffffffff19600a541617600a555f80f35b60405162461bcd60e51b815260206004820152601d60248201527f4f50503a20696e76616c696420726574656e74696f6e20636f6e6669670000006044820152606490fd5b346101f7575f3660031901126101f7576020600854604051908152f35b6004359061ffff821682036101f757565b359061ffff821682036101f757565b346101f7576040806003193601126101f7576111036110c9565b6024356001600160401b0381116101f757611122903690600401610493565b9160045415611205576111353633612905565b5f805160206142258339815191525461116490611158906001600160a01b031681565b6001600160a01b031690565b81516368fc2b7760e11b815261ffff841662010000176004820152336024820152908290829060449082905afa908115611200575f916111d2575b50156111af576105988383613157565b51639a28d60760e01b815233600482015261ffff91909116602482015260449150fd5b6111f29150823d84116111f9575b6111ea818361043d565b810190612465565b505f61119f565b503d6111e0565b61248b565b5163738c481160e01b8152600490fd5b91908260409103126101f75760405161122d816103f4565b8092803560ff811681036101f75782526020908101359161124d836101e9565b0152565b91906080838203126101f75760206112856040519261126f846103f4565b6040849661127d8382611215565b865201611215565b910152565b35906001600160401b03821682036101f757565b6001600160401b0381116103d45760051b60200190565b91909160409081818503126101f75781516112cf816103f4565b809482356112dc816101e9565b8252602092838101356001600160401b03918282116101f757019180601f840112156101f75782359061130e8261129e565b9661131b8151988961043d565b828852868089019360051b860101948286116101f757878101935b86851061134857505050505050500152565b84358681116101f75782019060609081601f1984880301126101f757845191611370836103b9565b61137b8c85016110da565b83528584013561138a816101e9565b8c840152830135918883116101f7576113aa878d80969581960101610493565b86820152815201940193611336565b9060406020610206936113e084825163ffffffff6020809260ff8151168552015116910152565b015191019063ffffffff6020809260ff8151168552015116910152565b90610e0991602081526114146020820183516113b9565b602082015190611432610140928360a084015261016083019061026c565b9160c0611478611453604087015195601f199687878303018588015261026c565b606087015163ffffffff1660e08601526080870151868683030161010087015261026c565b60a08601516001600160401b0316610120850152940151928285030191015261026c565b346101f7576003196040368201126101f757600480356001600160401b03928382116101f7576101409082360301126101f7576114d761045e565b906114e436828501611251565b825260848101358481116101f75761150190843691840101610493565b602083015260a48101358481116101f75761152190843691840101610493565b604083015261153260c482016101fb565b606083015260e48101358481116101f75761155290843691840101610493565b6080830152611564610104820161128a565b60a0830152610124810135908482116101f757836115859236920101610493565b60c08201526024359283116101f7576115a76115ad926102ce943691016112b5565b906125d7565b604051918291826113fd565b346101f7575f3660031901126101f7576020600454604051908152f35b346101f7575f3660031901126101f75760206004541515604051908152f35b346101f757611603366102d2565b600490815415611880576116173633612905565b80156118705781541461162657005b5f81556003549081156118615761163b611bb3565b915f5480155f146117f5575061164f6132f9565b604090604085015261167c61166c6001600160401b034216611935565b6001600160401b031660a0860152565b611684611bf0565b600181529161169281613343565b916020928385019081525f5b8381106117825750505050806116b7836116d6966125d7565b93818501519060405196879283926347f5343160e01b84528301610df8565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4801561120057611749945f91611765575b505f5561173661171a60a08501516001600160401b031690565b6001600160401b03166001600160401b03196001541617600155565b61173e611c09565b92835282015261351d565b61175c611757600854611b42565b600855565b6105985f600355565b61177c9150823d84116105dd576105ce818361043d565b5f611700565b806117ee8161179b611795600195613050565b506133a7565b886117a8825161ffff1690565b9101516117dd6117bc825163ffffffff1690565b6117d16117c761046b565b61ffff9095168552565b63ffffffff16838c0152565b87820152855190610a568383611c7a565b500161169e565b604051630c9b3d8760e21b8152838101918252905f9082908190602001038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611200575f9161183f575b5061164f565b61185b91503d805f833e611853818361043d565b810190612550565b5f611839565b60405163783bc0f160e11b8152fd5b50604051636ad43cc360e11b8152fd5b5060405163738c481160e01b8152fd5b634e487b7160e01b5f52601160045260245ffd5b90600163ffffffff809316019182116118b957565b611890565b604051906118cb826103f4565b5f6020838281520152565b604051906118e3826103f4565b816118ec6118be565b815260206112856118be565b604051906119058261040f565b606060c0838281526119156118d6565b60208201525f60408201525f838201525f60808201528260a08201520152565b906103e86001600160401b03809316029182169182036118b957565b60075481101561198a57600a9060075f52027fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801905f90565b610327565b9060405161199c816103f4565b915460ff8116835260081c63ffffffff166020830152565b90604080516119c2816103f4565b809363ffffffff80825416835260018092018054916119e08361129e565b956119ee604051978861043d565b83875260209160208801935f5260205f20915f945b868610611a1857505050505050505060200152565b60028589928451611a28816103b9565b86885461ffff8116835260101c1683820152611a45858901610d58565b86820152815201940195019492611a03565b90604051611a64816103f4565b602061128560088395604051611a798161040f565b604051611a85816103f4565b611a8e8361198f565b8152611a9c6001840161198f565b868201528152611aae60028301610d58565b85820152611abe60038301610d58565b6040820152611ae4611ad7600484015463ffffffff1690565b63ffffffff166060830152565b611af060058301610d58565b6080820152611b1c611b0c60068401546001600160401b031690565b6001600160401b031660a0830152565b611b2860078301610d58565b60c08201528552016119b4565b919082018092116118b957565b5f1981146118b95760010190565b15611b5757565b60405162461bcd60e51b815260206004820152602e60248201527f4f50503a20717565756564206d6573736167652065786365656473204d41585f60448201526d454e56454c4f50455f425954455360901b6064820152608490fd5b60405190611bc08261040f565b606060c083611bcd6118d6565b81528260208201528260408201525f838201528260808201525f60a08201520152565b60405190611bfd826103f4565b60606020835f81520152565b60405190611c16826103f4565b81611c1f611bb3565b81526020611285611bf0565b90611c358261129e565b611c42604051918261043d565b8281528092611c53601f199161129e565b01905f5b828110611c6357505050565b602090611c6e611c09565b82828501015201611c57565b805182101561198a5760209160051b010190565b919082039182116118b957565b818110611ca6575050565b5f8155600101611c9b565b90601f8211611cbe575050565b61020691600b5f527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9906020601f840160051c83019310611d07575b601f0160051c0190611c9b565b9091508190611cfa565b9190601f8111611d2057505050565b610206925f5260205f20906020601f840160051c83019310611d0757601f0160051c0190611c9b565b919091828114611e3057611d5d8354610c5f565b6001600160401b0381116103d457611d7f81611d798454610c5f565b84611d11565b5f93601f8211600114611dbe57611daf92939482915f92611db3575b50508160011b915f199060031b1c19161790565b9055565b015490505f80611d9b565b611dd0601f198316915f5260205f2090565b94611dde845f5260205f2090565b915f5b818110611e1857509583600195969710611e00575b505050811b019055565b01545f1960f88460031b161c191690555f8080611df6565b87830154845560019384019390920191602001611de1565b509050565b611e3f8154610c5f565b9081611e49575050565b81601f5f9311600114611e5a575055565b908083918252611e79601f60208420940160051c840160018501611c9b565b5555565b600160401b82116103d4578054828255808310611e9957505050565b6001916001600160ff1b0382811683036118b957841684036118b9575f5260205f209060011b81019260011b015b828110611ed357505050565b805f60029255611ee4838201611e35565b01611ec7565b818114611f7757815491611efe8383611e7d565b5f5260205f20905f5260205f20905f905b838210611f1c5750505050565b806001918403611f37575b6002809101930191019091611f0f565b8054845461ffff191661ffff919091161784558054845465ffffffff0000191665ffffffff0000909116178455611f72818301858401611d49565b611f27565b5050565b818103611f86575050565b60018083611fae63ffffffff610206965416859063ffffffff1663ffffffff19825416179055565b019101611eea565b9061212057818114159182611fca57505050565b600880918461020695611fe1575b50019101611f7b565b80612091575b50611ff86002820160028601611d49565b6120086003820160038601611d49565b61203561201c600483015463ffffffff1690565b600486019063ffffffff1663ffffffff19825416179055565b6120456005820160058601611d49565b61207b61205c60068301546001600160401b031690565b60068601906001600160401b03166001600160401b0319825416179055565b61208b6007820160078601611d49565b5f611fd8565b6120ee575b6001840160018201908181036120ad575b50611fe7565b8154815460ff191660ff919091161781556120e79163ffffffff9054851c1664ffffffff0082549160081b169064ffffffff001916179055565b5f806120a7565b8054845460ff191660ff919091161784558054845464ffffffff00191690831c60081b64ffffffff0016178455612096565b610c4c565b60075480156121e3575f190161213a81611951565b612120575f815560015f6001830155600960029261215a60028201611e35565b61216660038201611e35565b5f600482015561217860058201611e35565b5f600682015561218a60078201611e35565b5f600882015501908154915f8155826121a7575b50505050600755565b6001600160ff1b03831683036118b9575f5260205f209160011b8201915b8281101561219e57805f8592556121dd838201611e35565b016121c5565b634e487b7160e01b5f52603160045260245ffd5b156121fe57565b60405162461bcd60e51b815260206004820152603a60248201527f4f50503a20656e76656c6f70652065786365656473204d41585f454e56454c4f60448201527f50455f42595445532028657374696d61746f72206472696674290000000000006064820152608490fd5b9081516001600160401b0381116103d45761228e81612289600b54610c5f565b611cb1565b602080601f83116001146122ce575081906122be93945f926122c35750508160011b915f199060031b1c19161790565b600b55565b015190505f80611d9b565b90601f19831694612300600b5f527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db990565b925f905b87821061233b575050836001959610612323575b505050811b01600b55565b01515f1960f88460031b161c191690555f8080612318565b80600185968294968601518155019501930190612304565b91909182516001600160401b0381116103d45761237481611d798454610c5f565b602080601f83116001146123a557508190611daf9394955f926122c35750508160011b915f199060031b1c19161790565b90601f198316956123b9855f5260205f2090565b925f905b8882106123f3575050836001959697106123db57505050811b019055565b01515f1960f88460031b161c191690555f8080611df6565b806001859682949686015181550195019301906123bd565b1561241257565b60405162461bcd60e51b815260206004820152602560248201527f4f50503a207265636f7264207374696c6c20696e20726574656e74696f6e2077604482015264696e646f7760d81b6064820152608490fd5b91908260409103126101f757815180151581036101f757602090920151610e09816101e9565b6040513d5f823e3d90fd5b908160209103126101f7575190565b90602080835260609060608401908063ffffffff94858151168288015201519460408080830152865180945260808201908360808660051b8501019801955f935b8685106124f95750505050505050505090565b90919293949596979998868061253b600193868f89908f607f198a8603018d52519061ffff8251168552868201511686850152015191818a820152019061026c565b9b9c9a019897969190910194019291906124e6565b6020818303126101f7578051906001600160401b0382116101f7570181601f820112156101f75780519061258382610478565b92612591604051948561043d565b828452602083830101116101f757815f9260208093018386015e8301015290565b90916125c9610e099360408452604084019061026c565b91602081840391015261026c565b906125e0611bb3565b506040805163765809ab60e11b81526004926020929173__$b91d2ad766fd07dc99f56c1a0b020615bb$__919084818061261c858a83016124a5565b0381865af4908115611200575f91612877575b5060808701908582518051155f146127f35750508451630c9b3d8760e21b8152878101918252905f90829081906020010381875af49283156112005761269c9361268f93612685935f926127d7575b50526131ef565b5163ffffffff1690565b63ffffffff166060870152565b815162409a0960e81b81525f81806126b6898983016113fd565b0381855af4908115611200575f916127bd575b50858401805180518061278257505052815163b93612a760e01b8152938385806126f5898583016113fd565b0381855af49283156112005761272e955f958695612763575b505051630c9b3d8760e21b81529081019283529384928391829160200190565b03915af4908115611200575f91612749575b5060c082015290565b61275d91503d805f833e611853818361043d565b5f612740565b61277a929550803d106105dd576105ce818361043d565b925f8061270e565b9150939250848496959296012090855190860120036127a2575050505090565b51630ca2e06d60e11b815292839261057592919084016125b2565b6127d191503d805f833e611853818361043d565b5f6126c9565b6127ec9192503d805f833e611853818361043d565b905f61267e565b9091935061281492508551809381926347f5343160e01b83528a8301610df8565b0381865af4908115611200575f9161285a575b5081810361283657505061269c565b8351631d97afef60e31b815280870192835260208301919091529081906040010390fd5b6128719150853d87116105dd576105ce818361043d565b5f612827565b61288e9150853d87116105dd576105ce818361043d565b5f61262f565b6004116101f7575f90600490565b6001600160e01b031990358181169392600481106128bf57505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f80516020614225833981519152805461293d906001600160a01b031661293461292e86612894565b906128a2565b9084309161367f565b901561294a575b50505050565b63ffffffff1615612a01575f80516020614225833981519152805460ff60a01b1916600160a01b17905561298b90611158908190546001600160a01b031690565b91823b156101f7576129b6925f9283604051809681958294634a63ebf760e11b8452600484016128ce565b03925af18015611200576129e8575b505f80516020614225833981519152805460ff60a01b191690555f808080612944565b806129f56129fb9261042a565b80610262565b5f6129c5565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f8051602061422583398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b600d5480612a945750604051612a8e816103d9565b5f815290565b60405190630c9b3d8760e21b825260048201525f8160248173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611200575f91612ad4575090565b610e0991503d805f833e611853818361043d565b61010090602080910190602082510151515f925b818410612b0a575050505090565b909192936018808201918281116118b9576040612b2b888887510151611c7a565b5101515101018091116118b95793600101929190612afc565b805191908290602001825e015f815290565b612b5e610bf5565b612b66613911565b92604080518095602094858301612b7c91612b44565b612b8591612b44565b0394601f19958681018252612b9a908261043d565b8451612ba590613759565b908251918291868301612bb791612b44565b612bc091612b44565b038681018252612bd0908261043d565b612bd8613928565b908251918291868301612bea91612b44565b612bf391612b44565b038681018252612c03908261043d565b83850151612c1090613795565b908151612c23906001600160401b031690565b612c2c90613a38565b908351918291878301612c3e91612b44565b612c4791612b44565b038781018252612c57908261043d565b8251918291868301612c6891612b44565b612c7191612b44565b038681018252612c81908261043d565b612c8961393b565b908251918291868301612c9b91612b44565b612ca491612b44565b038681018252612cb4908261043d565b848201516001600160401b0316612cca90613a38565b908251918291868301612cdc91612b44565b612ce591612b44565b038681018252612cf5908261043d565b612cfd61394e565b908251918291868301612d0f91612b44565b612d1891612b44565b038681018252612d28908261043d565b6060850151612d3c9063ffffffff16613a38565b908251918291868301612d4e91612b44565b612d5791612b44565b038681018252612d67908261043d565b612d6f613961565b908251918291868301612d8191612b44565b612d8a91612b44565b038681018252612d9a908261043d565b6080850151612dae9063ffffffff16613a38565b908251918291868301612dc091612b44565b612dc991612b44565b038681018252612dd9908261043d565b612de1613974565b908251918291868301612df391612b44565b612dfc91612b44565b038681018252612e0c908261043d565b60a0850151612e1a90613759565b908251918291868301612e2c91612b44565b612e3591612b44565b038681018252612e45908261043d565b915f925b60c08601805151851015612f0f57612e8f612f0560c093612ef9612ead612ea78a6001978f612ea1908f612e958f612e8f612e826139b9565b91519b8c94850190612b44565b90612b44565b0390810188528761043d565b51611c7a565b51613ab7565b612eeb8d8c612edf8c612e8f612ed2612ecd88516001600160401b031690565b613a38565b9151988994850190612b44565b0390810185528461043d565b88519485938c850190612b44565b038a810183528261043d565b9401939050612e49565b5095509350505050565b63ffffffff8082165f52600960205260405f2090815416612f7a57612f50600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b612f969063ffffffff91829182600a541690612fd7565b16908115159081612fbf575b50612faa5750565b5f5260096020525f6001604082208281550155565b9050815f52600960205260405f20541615155f612fa2565b63ffffffff91908282168382161115612fef57031690565b5050505f90565b61301f90613002613bb7565b61300a613bb7565b613012613bb7565b61301a613bb7565b612a21565b613027613bb7565b61302f613bb7565b60c860405161303d816103d9565b5260c863ffffffff19600a541617600a55565b60025481101561198a5760025f5260011b7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01905f90565b929190612120578051835461ffff191661ffff91909116178355600180930190602080910151938451916001600160401b0383116103d4576130d4836130ce8654610c5f565b86611d11565b602091601f84116001146131065750508190611daf9394955f926122c35750508160011b915f199060031b1c19161790565b95601f9291921984169661311d865f5260205f2090565b935f915b89831061314057505050836001959697106123db57505050811b019055565b838501518655948501949381019391810191613121565b600254916003548084115f146131ac575061020692506131856040519261317d846103f4565b61ffff168352565b60208201526131a66003546131a161319c82611b42565b600355565b613050565b90613088565b6131b590611b42565b60035561ffff604051926131c8846103f4565b1682526020820152600160401b8210156103d4576131a68260016102069401600255613050565b613222916131fb610bf5565b906132046139d9565b612e8f613268604092604051978891612e8f60209889850190612b44565b0396613236601f199889810183528261043d565b61325c61324d612ecd6108558a5163ffffffff1690565b60405194859389850190612b44565b0387810183528261043d565b915f925b848601805151851015612f0f57612e8f6132ef612e8f612ef989956132e18d6132bb6132b58d60019a612ea18f6132a98f91612e8f612e82613928565b0386810189528861043d565b51613be5565b92612edf6132d3612ecd86516001600160401b031690565b8d519788938d850190612b44565b885194859388850190612b44565b940193905061326c565b604051613305816103d9565b5f8152905f368137565b6040519061331c826103f4565b6001825260203681840137565b60405190613336826103f4565b600a825260203681840137565b9061334d8261129e565b60409061335d604051918261043d565b838152809361336e601f199161129e565b01915f5b83811061337f5750505050565b602090825161338d816103b9565b5f8152825f81830152606085830152828601015201613372565b906040516133b4816103f4565b60206112856001839561ffff815416855201610d58565b8151815463ffffffff191663ffffffff91821617825591600180920160208092015191808351936133fc8585611e7d565b01915f52805f205f925b8484106134165750505050505050565b8051805183548583015161ffff90921665ffffffffffff199091161790891660101b65ffffffff0000161783556040015180519187840191906001600160401b0384116103d45782869261346f86611d798d9754610c5f565b8390601f87116001146134af5795806134a092600297985f926122c35750508160011b915f199060031b1c19161790565b90555b01920193019290613406565b9590601f198216966134c4845f5260205f2090565b975f5b818110613504575091879891846002999594106134ec575b505050811b0190556134a3565b01515f1960f88460031b161c191690555f80806134df565b838301518a5597909801978d978b9793840193016134c7565b600754600160401b8110156103d45780600161353c9201600755611951565b61212057600860208361367660c061020696516135d6815186613598825161357260ff8251168d9060ff1660ff19825416179055565b909101518a5464ffffffff00191660089190911b64ffffffff0016178a5563ffffffff90565b878060018c019301516135b960ff825116859060ff1660ff19825416179055565b0151825464ffffffff001916911660081b64ffffffff0016179055565b6135e68582015160028901612353565b6135f7604082015160038901612353565b61362461360b606083015163ffffffff1690565b600489019063ffffffff1663ffffffff19825416179055565b613635608082015160058901612353565b61366b61364c60a08301516001600160401b031690565b60068901906001600160401b03166001600160401b0319825416179055565b015160078601612353565b015191016133cb565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b038411176103d4576040935f9385528380528360205251915afa6136ee57565b9150505f51906020518060201c150290565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811630811491821561373e575b50506106e757565b5f805160206142458339815191525416141590505f80613736565b610e09612e8f916137876137766001600160401b03835116613a38565b916040519485936020850190612b44565b03601f19810183528261043d565b61379d610bf5565b906137a6613911565b60408051938460209384928383016137bd91612b44565b6137c691612b44565b0393601f199485810187526137db908761043d565b80516137e690613d8f565b9586516137f9906001600160401b031690565b61380290613a38565b90845191829185830161381491612b44565b61381d91612b44565b03868101825261382d908261043d565b835196879184830161383e91612b44565b61384791612b44565b038581018752613857908761043d565b61385f613928565b95835196879184830161387191612b44565b61387a91612b44565b03858101875261388a908761043d565b015161389590613d8f565b9081516138a8906001600160401b031690565b6138b190613a38565b9481519586918583016138c391612b44565b6138cc91612b44565b0384810186526138dc908661043d565b5193849283016138eb91612b44565b6138f491612b44565b039081018252610e09908261043d565b80511561198a5760200190565b61391961330f565b600a61392482613904565b5390565b61393061330f565b601261392482613904565b61394361330f565b602861392482613904565b61395661330f565b603061392482613904565b61396961330f565b603861392482613904565b61397c613329565b60a25f5b607f808311156139a55790608060019284161760208286010153019060071c90613980565b50607f600192166020828501015301815290565b6139c1613329565b61014260015f60c26020850153019060071c90613980565b6139e161330f565b600861392482613904565b6139f461330f565b601061392482613904565b613a0761330f565b601a61392482613904565b613a1a61330f565b603261392482613904565b613a2d61330f565b604261392482613904565b9060809160806001600160401b03821610613a9357613a55613329565b5f5b607f80841115613a7b57908560019285161760208285010153019160071c91613a57565b5090929350607f600192166020828501015301815290565b909150613a9e61330f565b9060f81b6001600160f81b0319165f1a61392482613904565b613abf610bf5565b90613ac8613911565b6040805193846020938492838301613adf91612b44565b613ae891612b44565b0393601f19948581018752613afd908761043d565b8051613b0890613e47565b958651613b1b906001600160401b031690565b613b2490613a38565b908451918291858301613b3691612b44565b613b3f91612b44565b038681018252613b4f908261043d565b8351968791848301613b6091612b44565b613b6991612b44565b038581018752613b79908761043d565b613b81613928565b958351968791848301613b9391612b44565b613b9c91612b44565b038581018752613bac908761043d565b0151613895906131ef565b60ff5f805160206142658339815191525460401c1615613bd357565b604051631afcd79f60e31b8152600490fd5b610e09613c26612e8f92613cde613cca612e8f613cd1613c03610bf5565b94613c0c6139d9565b9060409485928351998a91612e8f60209b8c850190612b44565b0398613c3a601f199a8b810183528261043d565b613c7989613c6d613c5d612ecd613c56613c56895161ffff1690565b61ffff1690565b612e8f8851958694850190612b44565b038b810183528261043d565b613c8889613c6d613c5d6139ec565b613ca589613c6d613c5d612ecd6108558489015163ffffffff1690565b613cbe613cb06139ff565b85519d8e938c850190612b44565b038981018c528b61043d565b0151613759565b9151968794850190612b44565b0390810183528261043d565b90813b15613d6e575f8051602061424583398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2805115613d5357613d5091614181565b50565b505034613d5c57565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b610e09612e8f612e8f613cde613e37612ecd6108556020612e8f98613e2c612e8f613e06613dbb610bf5565b613dd5613dc66139d9565b6040519d8e9389850190612b44565b039a613de9601f199c8d810183528261043d565b613c6d61324d612ecd613e00613e00895160ff1690565b60ff1690565b613e20613e116139ec565b6040519a8b9387850190612b44565b0389810189528861043d565b015163ffffffff1690565b6040519586936020850190612b44565b613e4f610bf5565b90613e58613911565b91604080518094602093848301613e6e91612b44565b613e7791612b44565b0392601f19938481018652613e8c908661043d565b8051613e9790613795565b948551613eaa906001600160401b031690565b613eb390613a38565b908351918291868301613ec591612b44565b613ece91612b44565b038581018252613ede908261043d565b8251958691858301613eef91612b44565b613ef891612b44565b038481018652613f08908661043d565b613f10613928565b948251958691858301613f2291612b44565b613f2b91612b44565b038481018652613f3b908661043d565b82810151613f4890613759565b948251958691858301613f5a91612b44565b613f6391612b44565b038481018652613f73908661043d565b613f7b6139ff565b948251958691858301613f8d91612b44565b613f9691612b44565b038481018652613fa6908661043d565b81810151613fb390613759565b948251958691858301613fc591612b44565b613fce91612b44565b038481018652613fde908661043d565b613fe661393b565b948251958691858301613ff891612b44565b61400191612b44565b038481018652614011908661043d565b60608101516140259063ffffffff16613a38565b94825195869185830161403791612b44565b61404091612b44565b038481018652614050908661043d565b614058613a12565b94825195869185830161406a91612b44565b61407391612b44565b038481018652614083908661043d565b608081015161409190613759565b9482519586918583016140a391612b44565b6140ac91612b44565b0384810186526140bc908661043d565b6140c4613961565b9482519586918583016140d691612b44565b6140df91612b44565b0384810186526140ef908661043d565b60a08101516001600160401b031661410690613a38565b94825195869185830161411891612b44565b61412191612b44565b038481018652614131908661043d565b614139613a25565b94825195869185830161414b91612b44565b61415491612b44565b038481018652614164908661043d565b60c0015161417190613759565b905193849283016138eb91612b44565b5f80610e0993602081519101845af43d156141bd573d916141a183610478565b926141af604051948561043d565b83523d5f602085013e6141c1565b6060915b906141e857508051156141d657602081519101fd5b60405163d6bda27560e01b8152600490fd5b8151158061421b575b6141f9575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156141f156fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212201dd53cc2caf86754ed88247c9b779f72471445dd4bcf6d516a7bf3bb29c0dce864736f6c63430008190033", - "deployedBytecode": "0x60806040526004361015610011575f80fd5b5f3560e01c806304676e38146101e45780630d6f1e9d146101df57806327e935a1146101da57806347453a2a146101d55780634f1ef286146101d05780634fca79cb146101cb57806352d1902d146101c6578063621fbd59146101c15780637a9e5e4b146101bc5780638fb36037146101b757806393ec06be146101b257806395fabcf0146101ad578063a3ad9cc3146101a8578063a593aec2146101a3578063a9e50c101461019e578063ad3cb1cc14610199578063b244542b14610194578063b8f8d7e51461018f578063bf7e214f1461018a578063c16a8a5614610185578063c4d66de814610180578063c94c93e71461017b578063d5b5272a14610176578063e006490014610171578063e0c14aa81461016c578063e2fc04ae14610167578063e36f9a0714610162578063e44efb5a1461015d5763f5786b4b14610158575f80fd5b6115f5565b6115d6565b6115b9565b61149c565b6110e9565b6110ac565b611010565b610fe1565b610eb5565b610e92565b610e5e565b610e3b565b610e0c565b610c07565b610bcf565b610af3565b610819565b6107fc565b6107df565b61078f565b610715565b6106f9565b610690565b6105e4565b6104d9565b61033b565b6102e4565b610290565b610208565b63ffffffff8116036101f757565b5f80fd5b3590610206826101e9565b565b346101f75760203660031901126101f7576060600435610227816101e9565b63ffffffff8091165f52600960205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b5f9103126101f757565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346101f7575f3660031901126101f75763ffffffff600c54166102b1610c97565b906102ce604051928392835260406020840152604083019061026c565b0390f35b60209060031901126101f75760043590565b346101f7576102f2366102d2565b6102fc3633612905565b8015610315576004541561030c57005b6004555f600355005b604051636ad43cc360e11b8152600490fd5b634e487b7160e01b5f52603260045260245ffd5b346101f757610349366102d2565b6006548110156101f75760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01546040516001600160a01b039091168152602090f35b600435906001600160a01b03821682036101f757565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b038211176103d457604052565b6103a5565b602081019081106001600160401b038211176103d457604052565b604081019081106001600160401b038211176103d457604052565b60e081019081106001600160401b038211176103d457604052565b6001600160401b0381116103d457604052565b90601f801991011681019081106001600160401b038211176103d457604052565b604051906102068261040f565b60405190610206826103b9565b6001600160401b0381116103d457601f01601f191660200190565b81601f820112156101f7578035906104aa82610478565b926104b8604051948561043d565b828452602083830101116101f757815f926020809301838601378301015290565b60403660031901126101f7576104ed61038f565b6024356001600160401b0381116101f75761050c903690600401610493565b90610515613700565b61051f3633612905565b610527613700565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f93816105b3575b5061057957604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f80516020614245833981519152830361059a576105989250613cea565b005b604051632a87526960e21b815260048101849052602490fd5b6105d691945060203d6020116105dd575b6105ce818361043d565b810190612496565b925f61054f565b503d6105c4565b346101f75760203660031901126101f7576102ce600435610604816101e9565b60405f818051610613816103b9565b828152826020820152015263ffffffff8092165f5260096020526001815f206001600160401b03835194610646866103b9565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b346101f7575f3660031901126101f7577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630036106e75760206040515f805160206142458339815191528152f35b60405163703e46dd60e11b8152600490fd5b346101f7575f3660031901126101f75760205f54604051908152f35b346101f75760203660031901126101f75761072e61038f565b5f80516020614225833981519152546001600160a01b0391908216330361077857803b1561075f5761059890612a21565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b346101f7575f3660031901126101f7575f805160206142258339815191525460a01c60ff16156107d7576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f6107c4565b346101f7575f3660031901126101f7576020604051620100008152f35b346101f7575f3660031901126101f7576020600354604051908152f35b346101f75760203660031901126101f757600435610836816101e9565b6108403633612905565b600c5463ffffffff1661085e610855826118a4565b63ffffffff1690565b63ffffffff831603610ac457506108736118f8565b63ffffffff821660608201526108a46108946001600160401b034216611935565b6001600160401b03166040830152565b6108ac612a79565b60a08201525f60085480151580610952575b7fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d88761094d866109416108ef88612b56565b9161090062010000845111156121f7565b610922835160208501206109148184612f19565b61091d83612f7f565b600d55565b61092b83612269565b63ffffffff1663ffffffff19600c541617600c55565b60405191829182610df8565b0390a1005b5f6101005b838210610a6e575b50506109759083159081610a66575b5015611b50565b8161097f81611c2b565b60c085019081525f5b828110610a2e57505061099a91611c8e565b905f5b828110610a0057505f5b8181106109ef575050916109416108ef61094d936109e57fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d88796600855565b93508294506108be565b6001906109fa612125565b016109a7565b80610a28610a18610a1360019486611b35565b611951565b50610a2283611951565b90611fb6565b0161099d565b6001919250610a5c81610a4081611951565b50610a4c865191611a57565b610a568383611c7a565b52611c7a565b5001908391610988565b90505f61096e565b610a8a610a85610a7f849794611951565b50611a57565b612ae8565b9062010000610a998383611b35565b11610abb57610aad600192610ab392611b35565b92611b42565b940190610957565b5081945061095f565b610acd906118a4565b6040516307d3e83d60e51b815263ffffffff918216600482015291166024820152604490fd5b346101f75760203660031901126101f757600435610b10816101e9565b610b1a3633612905565b63ffffffff80821690815f52600960205260405f20541615610b8a5761059891610b6b610b7f92610b64610855610b56600a5463ffffffff1690565b600c5463ffffffff16612fd7565b101561240b565b63ffffffff165f52600960205260405f2090565b60015f918281550155565b60405162461bcd60e51b815260206004820152601760248201527f4f50503a207265636f7264206e6f742070726573656e740000000000000000006044820152606490fd5b346101f7575f3660031901126101f75760206001600160401b0360015416604051908152f35b60405190610c02826103d9565b5f8252565b346101f7575f3660031901126101f7576102ce604051610c26816103f4565b60058152640352e302e360dc1b602082015260405191829160208352602083019061026c565b634e487b7160e01b5f525f60045260245ffd5b90600182811c92168015610c8d575b6020831014610c7957565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610c6e565b604051905f82600b5491610caa83610c5f565b80835292602090600190818116908115610d345750600114610cd5575b50506102069250038361043d565b915092600b5f527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9935f925b828410610d1c57506102069450505081016020015f80610cc7565b85548885018301529485019487945092810192610d01565b9150506020925061020694915060ff191682840152151560051b8201015f80610cc7565b9060405191825f8254610d6a81610c5f565b908184526020946001916001811690815f14610dd65750600114610d98575b5050506102069250038361043d565b5f90815285812095935091905b818310610dbe57505061020693508201015f8080610d89565b85548884018501529485019487945091830191610da5565b9250505061020694925060ff191682840152151560051b8201015f8080610d89565b906020610e0992818152019061026c565b90565b346101f7575f3660031901126101f7576102ce610e27610c97565b60405191829160208352602083019061026c565b346101f7575f3660031901126101f757602063ffffffff600c5416604051908152f35b346101f7575f3660031901126101f7575f80516020614225833981519152546040516001600160a01b039091168152602090f35b346101f7575f3660031901126101f757602063ffffffff600a5416604051908152f35b346101f75760203660031901126101f757610ece61038f565b5f8051602061426583398151915254906001600160401b0360ff8360401c1615921680159081610fd9575b6001149081610fcf575b159081610fc6575b50610fb4575f80516020614265833981519152805467ffffffffffffffff19166001179055610f3e9082610f9057612ff6565b610f4457005b5f80516020614265833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290806020810161094d565b5f80516020614265833981519152805460ff60401b1916600160401b179055612ff6565b60405163f92ee8a960e01b8152600490fd5b9050155f610f0b565b303b159150610f03565b839150610ef9565b346101f75760203660031901126101f7576004355f526005602052602060ff60405f2054166040519015158152f35b346101f75760203660031901126101f75760043561102d816101e9565b6110373633612905565b604051611043816103d9565b63ffffffff8092168082521561106757511663ffffffff19600a541617600a555f80f35b60405162461bcd60e51b815260206004820152601d60248201527f4f50503a20696e76616c696420726574656e74696f6e20636f6e6669670000006044820152606490fd5b346101f7575f3660031901126101f7576020600854604051908152f35b6004359061ffff821682036101f757565b359061ffff821682036101f757565b346101f7576040806003193601126101f7576111036110c9565b6024356001600160401b0381116101f757611122903690600401610493565b9160045415611205576111353633612905565b5f805160206142258339815191525461116490611158906001600160a01b031681565b6001600160a01b031690565b81516368fc2b7760e11b815261ffff841662010000176004820152336024820152908290829060449082905afa908115611200575f916111d2575b50156111af576105988383613157565b51639a28d60760e01b815233600482015261ffff91909116602482015260449150fd5b6111f29150823d84116111f9575b6111ea818361043d565b810190612465565b505f61119f565b503d6111e0565b61248b565b5163738c481160e01b8152600490fd5b91908260409103126101f75760405161122d816103f4565b8092803560ff811681036101f75782526020908101359161124d836101e9565b0152565b91906080838203126101f75760206112856040519261126f846103f4565b6040849661127d8382611215565b865201611215565b910152565b35906001600160401b03821682036101f757565b6001600160401b0381116103d45760051b60200190565b91909160409081818503126101f75781516112cf816103f4565b809482356112dc816101e9565b8252602092838101356001600160401b03918282116101f757019180601f840112156101f75782359061130e8261129e565b9661131b8151988961043d565b828852868089019360051b860101948286116101f757878101935b86851061134857505050505050500152565b84358681116101f75782019060609081601f1984880301126101f757845191611370836103b9565b61137b8c85016110da565b83528584013561138a816101e9565b8c840152830135918883116101f7576113aa878d80969581960101610493565b86820152815201940193611336565b9060406020610206936113e084825163ffffffff6020809260ff8151168552015116910152565b015191019063ffffffff6020809260ff8151168552015116910152565b90610e0991602081526114146020820183516113b9565b602082015190611432610140928360a084015261016083019061026c565b9160c0611478611453604087015195601f199687878303018588015261026c565b606087015163ffffffff1660e08601526080870151868683030161010087015261026c565b60a08601516001600160401b0316610120850152940151928285030191015261026c565b346101f7576003196040368201126101f757600480356001600160401b03928382116101f7576101409082360301126101f7576114d761045e565b906114e436828501611251565b825260848101358481116101f75761150190843691840101610493565b602083015260a48101358481116101f75761152190843691840101610493565b604083015261153260c482016101fb565b606083015260e48101358481116101f75761155290843691840101610493565b6080830152611564610104820161128a565b60a0830152610124810135908482116101f757836115859236920101610493565b60c08201526024359283116101f7576115a76115ad926102ce943691016112b5565b906125d7565b604051918291826113fd565b346101f7575f3660031901126101f7576020600454604051908152f35b346101f7575f3660031901126101f75760206004541515604051908152f35b346101f757611603366102d2565b600490815415611880576116173633612905565b80156118705781541461162657005b5f81556003549081156118615761163b611bb3565b915f5480155f146117f5575061164f6132f9565b604090604085015261167c61166c6001600160401b034216611935565b6001600160401b031660a0860152565b611684611bf0565b600181529161169281613343565b916020928385019081525f5b8381106117825750505050806116b7836116d6966125d7565b93818501519060405196879283926347f5343160e01b84528301610df8565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4801561120057611749945f91611765575b505f5561173661171a60a08501516001600160401b031690565b6001600160401b03166001600160401b03196001541617600155565b61173e611c09565b92835282015261351d565b61175c611757600854611b42565b600855565b6105985f600355565b61177c9150823d84116105dd576105ce818361043d565b5f611700565b806117ee8161179b611795600195613050565b506133a7565b886117a8825161ffff1690565b9101516117dd6117bc825163ffffffff1690565b6117d16117c761046b565b61ffff9095168552565b63ffffffff16838c0152565b87820152855190610a568383611c7a565b500161169e565b604051630c9b3d8760e21b8152838101918252905f9082908190602001038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611200575f9161183f575b5061164f565b61185b91503d805f833e611853818361043d565b810190612550565b5f611839565b60405163783bc0f160e11b8152fd5b50604051636ad43cc360e11b8152fd5b5060405163738c481160e01b8152fd5b634e487b7160e01b5f52601160045260245ffd5b90600163ffffffff809316019182116118b957565b611890565b604051906118cb826103f4565b5f6020838281520152565b604051906118e3826103f4565b816118ec6118be565b815260206112856118be565b604051906119058261040f565b606060c0838281526119156118d6565b60208201525f60408201525f838201525f60808201528260a08201520152565b906103e86001600160401b03809316029182169182036118b957565b60075481101561198a57600a9060075f52027fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801905f90565b610327565b9060405161199c816103f4565b915460ff8116835260081c63ffffffff166020830152565b90604080516119c2816103f4565b809363ffffffff80825416835260018092018054916119e08361129e565b956119ee604051978861043d565b83875260209160208801935f5260205f20915f945b868610611a1857505050505050505060200152565b60028589928451611a28816103b9565b86885461ffff8116835260101c1683820152611a45858901610d58565b86820152815201940195019492611a03565b90604051611a64816103f4565b602061128560088395604051611a798161040f565b604051611a85816103f4565b611a8e8361198f565b8152611a9c6001840161198f565b868201528152611aae60028301610d58565b85820152611abe60038301610d58565b6040820152611ae4611ad7600484015463ffffffff1690565b63ffffffff166060830152565b611af060058301610d58565b6080820152611b1c611b0c60068401546001600160401b031690565b6001600160401b031660a0830152565b611b2860078301610d58565b60c08201528552016119b4565b919082018092116118b957565b5f1981146118b95760010190565b15611b5757565b60405162461bcd60e51b815260206004820152602e60248201527f4f50503a20717565756564206d6573736167652065786365656473204d41585f60448201526d454e56454c4f50455f425954455360901b6064820152608490fd5b60405190611bc08261040f565b606060c083611bcd6118d6565b81528260208201528260408201525f838201528260808201525f60a08201520152565b60405190611bfd826103f4565b60606020835f81520152565b60405190611c16826103f4565b81611c1f611bb3565b81526020611285611bf0565b90611c358261129e565b611c42604051918261043d565b8281528092611c53601f199161129e565b01905f5b828110611c6357505050565b602090611c6e611c09565b82828501015201611c57565b805182101561198a5760209160051b010190565b919082039182116118b957565b818110611ca6575050565b5f8155600101611c9b565b90601f8211611cbe575050565b61020691600b5f527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9906020601f840160051c83019310611d07575b601f0160051c0190611c9b565b9091508190611cfa565b9190601f8111611d2057505050565b610206925f5260205f20906020601f840160051c83019310611d0757601f0160051c0190611c9b565b919091828114611e3057611d5d8354610c5f565b6001600160401b0381116103d457611d7f81611d798454610c5f565b84611d11565b5f93601f8211600114611dbe57611daf92939482915f92611db3575b50508160011b915f199060031b1c19161790565b9055565b015490505f80611d9b565b611dd0601f198316915f5260205f2090565b94611dde845f5260205f2090565b915f5b818110611e1857509583600195969710611e00575b505050811b019055565b01545f1960f88460031b161c191690555f8080611df6565b87830154845560019384019390920191602001611de1565b509050565b611e3f8154610c5f565b9081611e49575050565b81601f5f9311600114611e5a575055565b908083918252611e79601f60208420940160051c840160018501611c9b565b5555565b600160401b82116103d4578054828255808310611e9957505050565b6001916001600160ff1b0382811683036118b957841684036118b9575f5260205f209060011b81019260011b015b828110611ed357505050565b805f60029255611ee4838201611e35565b01611ec7565b818114611f7757815491611efe8383611e7d565b5f5260205f20905f5260205f20905f905b838210611f1c5750505050565b806001918403611f37575b6002809101930191019091611f0f565b8054845461ffff191661ffff919091161784558054845465ffffffff0000191665ffffffff0000909116178455611f72818301858401611d49565b611f27565b5050565b818103611f86575050565b60018083611fae63ffffffff610206965416859063ffffffff1663ffffffff19825416179055565b019101611eea565b9061212057818114159182611fca57505050565b600880918461020695611fe1575b50019101611f7b565b80612091575b50611ff86002820160028601611d49565b6120086003820160038601611d49565b61203561201c600483015463ffffffff1690565b600486019063ffffffff1663ffffffff19825416179055565b6120456005820160058601611d49565b61207b61205c60068301546001600160401b031690565b60068601906001600160401b03166001600160401b0319825416179055565b61208b6007820160078601611d49565b5f611fd8565b6120ee575b6001840160018201908181036120ad575b50611fe7565b8154815460ff191660ff919091161781556120e79163ffffffff9054851c1664ffffffff0082549160081b169064ffffffff001916179055565b5f806120a7565b8054845460ff191660ff919091161784558054845464ffffffff00191690831c60081b64ffffffff0016178455612096565b610c4c565b60075480156121e3575f190161213a81611951565b612120575f815560015f6001830155600960029261215a60028201611e35565b61216660038201611e35565b5f600482015561217860058201611e35565b5f600682015561218a60078201611e35565b5f600882015501908154915f8155826121a7575b50505050600755565b6001600160ff1b03831683036118b9575f5260205f209160011b8201915b8281101561219e57805f8592556121dd838201611e35565b016121c5565b634e487b7160e01b5f52603160045260245ffd5b156121fe57565b60405162461bcd60e51b815260206004820152603a60248201527f4f50503a20656e76656c6f70652065786365656473204d41585f454e56454c4f60448201527f50455f42595445532028657374696d61746f72206472696674290000000000006064820152608490fd5b9081516001600160401b0381116103d45761228e81612289600b54610c5f565b611cb1565b602080601f83116001146122ce575081906122be93945f926122c35750508160011b915f199060031b1c19161790565b600b55565b015190505f80611d9b565b90601f19831694612300600b5f527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db990565b925f905b87821061233b575050836001959610612323575b505050811b01600b55565b01515f1960f88460031b161c191690555f8080612318565b80600185968294968601518155019501930190612304565b91909182516001600160401b0381116103d45761237481611d798454610c5f565b602080601f83116001146123a557508190611daf9394955f926122c35750508160011b915f199060031b1c19161790565b90601f198316956123b9855f5260205f2090565b925f905b8882106123f3575050836001959697106123db57505050811b019055565b01515f1960f88460031b161c191690555f8080611df6565b806001859682949686015181550195019301906123bd565b1561241257565b60405162461bcd60e51b815260206004820152602560248201527f4f50503a207265636f7264207374696c6c20696e20726574656e74696f6e2077604482015264696e646f7760d81b6064820152608490fd5b91908260409103126101f757815180151581036101f757602090920151610e09816101e9565b6040513d5f823e3d90fd5b908160209103126101f7575190565b90602080835260609060608401908063ffffffff94858151168288015201519460408080830152865180945260808201908360808660051b8501019801955f935b8685106124f95750505050505050505090565b90919293949596979998868061253b600193868f89908f607f198a8603018d52519061ffff8251168552868201511686850152015191818a820152019061026c565b9b9c9a019897969190910194019291906124e6565b6020818303126101f7578051906001600160401b0382116101f7570181601f820112156101f75780519061258382610478565b92612591604051948561043d565b828452602083830101116101f757815f9260208093018386015e8301015290565b90916125c9610e099360408452604084019061026c565b91602081840391015261026c565b906125e0611bb3565b506040805163765809ab60e11b81526004926020929173__$b91d2ad766fd07dc99f56c1a0b020615bb$__919084818061261c858a83016124a5565b0381865af4908115611200575f91612877575b5060808701908582518051155f146127f35750508451630c9b3d8760e21b8152878101918252905f90829081906020010381875af49283156112005761269c9361268f93612685935f926127d7575b50526131ef565b5163ffffffff1690565b63ffffffff166060870152565b815162409a0960e81b81525f81806126b6898983016113fd565b0381855af4908115611200575f916127bd575b50858401805180518061278257505052815163b93612a760e01b8152938385806126f5898583016113fd565b0381855af49283156112005761272e955f958695612763575b505051630c9b3d8760e21b81529081019283529384928391829160200190565b03915af4908115611200575f91612749575b5060c082015290565b61275d91503d805f833e611853818361043d565b5f612740565b61277a929550803d106105dd576105ce818361043d565b925f8061270e565b9150939250848496959296012090855190860120036127a2575050505090565b51630ca2e06d60e11b815292839261057592919084016125b2565b6127d191503d805f833e611853818361043d565b5f6126c9565b6127ec9192503d805f833e611853818361043d565b905f61267e565b9091935061281492508551809381926347f5343160e01b83528a8301610df8565b0381865af4908115611200575f9161285a575b5081810361283657505061269c565b8351631d97afef60e31b815280870192835260208301919091529081906040010390fd5b6128719150853d87116105dd576105ce818361043d565b5f612827565b61288e9150853d87116105dd576105ce818361043d565b5f61262f565b6004116101f7575f90600490565b6001600160e01b031990358181169392600481106128bf57505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f80516020614225833981519152805461293d906001600160a01b031661293461292e86612894565b906128a2565b9084309161367f565b901561294a575b50505050565b63ffffffff1615612a01575f80516020614225833981519152805460ff60a01b1916600160a01b17905561298b90611158908190546001600160a01b031690565b91823b156101f7576129b6925f9283604051809681958294634a63ebf760e11b8452600484016128ce565b03925af18015611200576129e8575b505f80516020614225833981519152805460ff60a01b191690555f808080612944565b806129f56129fb9261042a565b80610262565b5f6129c5565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f8051602061422583398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b600d5480612a945750604051612a8e816103d9565b5f815290565b60405190630c9b3d8760e21b825260048201525f8160248173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611200575f91612ad4575090565b610e0991503d805f833e611853818361043d565b61010090602080910190602082510151515f925b818410612b0a575050505090565b909192936018808201918281116118b9576040612b2b888887510151611c7a565b5101515101018091116118b95793600101929190612afc565b805191908290602001825e015f815290565b612b5e610bf5565b612b66613911565b92604080518095602094858301612b7c91612b44565b612b8591612b44565b0394601f19958681018252612b9a908261043d565b8451612ba590613759565b908251918291868301612bb791612b44565b612bc091612b44565b038681018252612bd0908261043d565b612bd8613928565b908251918291868301612bea91612b44565b612bf391612b44565b038681018252612c03908261043d565b83850151612c1090613795565b908151612c23906001600160401b031690565b612c2c90613a38565b908351918291878301612c3e91612b44565b612c4791612b44565b038781018252612c57908261043d565b8251918291868301612c6891612b44565b612c7191612b44565b038681018252612c81908261043d565b612c8961393b565b908251918291868301612c9b91612b44565b612ca491612b44565b038681018252612cb4908261043d565b848201516001600160401b0316612cca90613a38565b908251918291868301612cdc91612b44565b612ce591612b44565b038681018252612cf5908261043d565b612cfd61394e565b908251918291868301612d0f91612b44565b612d1891612b44565b038681018252612d28908261043d565b6060850151612d3c9063ffffffff16613a38565b908251918291868301612d4e91612b44565b612d5791612b44565b038681018252612d67908261043d565b612d6f613961565b908251918291868301612d8191612b44565b612d8a91612b44565b038681018252612d9a908261043d565b6080850151612dae9063ffffffff16613a38565b908251918291868301612dc091612b44565b612dc991612b44565b038681018252612dd9908261043d565b612de1613974565b908251918291868301612df391612b44565b612dfc91612b44565b038681018252612e0c908261043d565b60a0850151612e1a90613759565b908251918291868301612e2c91612b44565b612e3591612b44565b038681018252612e45908261043d565b915f925b60c08601805151851015612f0f57612e8f612f0560c093612ef9612ead612ea78a6001978f612ea1908f612e958f612e8f612e826139b9565b91519b8c94850190612b44565b90612b44565b0390810188528761043d565b51611c7a565b51613ab7565b612eeb8d8c612edf8c612e8f612ed2612ecd88516001600160401b031690565b613a38565b9151988994850190612b44565b0390810185528461043d565b88519485938c850190612b44565b038a810183528261043d565b9401939050612e49565b5095509350505050565b63ffffffff8082165f52600960205260405f2090815416612f7a57612f50600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b612f969063ffffffff91829182600a541690612fd7565b16908115159081612fbf575b50612faa5750565b5f5260096020525f6001604082208281550155565b9050815f52600960205260405f20541615155f612fa2565b63ffffffff91908282168382161115612fef57031690565b5050505f90565b61301f90613002613bb7565b61300a613bb7565b613012613bb7565b61301a613bb7565b612a21565b613027613bb7565b61302f613bb7565b60c860405161303d816103d9565b5260c863ffffffff19600a541617600a55565b60025481101561198a5760025f5260011b7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01905f90565b929190612120578051835461ffff191661ffff91909116178355600180930190602080910151938451916001600160401b0383116103d4576130d4836130ce8654610c5f565b86611d11565b602091601f84116001146131065750508190611daf9394955f926122c35750508160011b915f199060031b1c19161790565b95601f9291921984169661311d865f5260205f2090565b935f915b89831061314057505050836001959697106123db57505050811b019055565b838501518655948501949381019391810191613121565b600254916003548084115f146131ac575061020692506131856040519261317d846103f4565b61ffff168352565b60208201526131a66003546131a161319c82611b42565b600355565b613050565b90613088565b6131b590611b42565b60035561ffff604051926131c8846103f4565b1682526020820152600160401b8210156103d4576131a68260016102069401600255613050565b613222916131fb610bf5565b906132046139d9565b612e8f613268604092604051978891612e8f60209889850190612b44565b0396613236601f199889810183528261043d565b61325c61324d612ecd6108558a5163ffffffff1690565b60405194859389850190612b44565b0387810183528261043d565b915f925b848601805151851015612f0f57612e8f6132ef612e8f612ef989956132e18d6132bb6132b58d60019a612ea18f6132a98f91612e8f612e82613928565b0386810189528861043d565b51613be5565b92612edf6132d3612ecd86516001600160401b031690565b8d519788938d850190612b44565b885194859388850190612b44565b940193905061326c565b604051613305816103d9565b5f8152905f368137565b6040519061331c826103f4565b6001825260203681840137565b60405190613336826103f4565b600a825260203681840137565b9061334d8261129e565b60409061335d604051918261043d565b838152809361336e601f199161129e565b01915f5b83811061337f5750505050565b602090825161338d816103b9565b5f8152825f81830152606085830152828601015201613372565b906040516133b4816103f4565b60206112856001839561ffff815416855201610d58565b8151815463ffffffff191663ffffffff91821617825591600180920160208092015191808351936133fc8585611e7d565b01915f52805f205f925b8484106134165750505050505050565b8051805183548583015161ffff90921665ffffffffffff199091161790891660101b65ffffffff0000161783556040015180519187840191906001600160401b0384116103d45782869261346f86611d798d9754610c5f565b8390601f87116001146134af5795806134a092600297985f926122c35750508160011b915f199060031b1c19161790565b90555b01920193019290613406565b9590601f198216966134c4845f5260205f2090565b975f5b818110613504575091879891846002999594106134ec575b505050811b0190556134a3565b01515f1960f88460031b161c191690555f80806134df565b838301518a5597909801978d978b9793840193016134c7565b600754600160401b8110156103d45780600161353c9201600755611951565b61212057600860208361367660c061020696516135d6815186613598825161357260ff8251168d9060ff1660ff19825416179055565b909101518a5464ffffffff00191660089190911b64ffffffff0016178a5563ffffffff90565b878060018c019301516135b960ff825116859060ff1660ff19825416179055565b0151825464ffffffff001916911660081b64ffffffff0016179055565b6135e68582015160028901612353565b6135f7604082015160038901612353565b61362461360b606083015163ffffffff1690565b600489019063ffffffff1663ffffffff19825416179055565b613635608082015160058901612353565b61366b61364c60a08301516001600160401b031690565b60068901906001600160401b03166001600160401b0319825416179055565b015160078601612353565b015191016133cb565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b038411176103d4576040935f9385528380528360205251915afa6136ee57565b9150505f51906020518060201c150290565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811630811491821561373e575b50506106e757565b5f805160206142458339815191525416141590505f80613736565b610e09612e8f916137876137766001600160401b03835116613a38565b916040519485936020850190612b44565b03601f19810183528261043d565b61379d610bf5565b906137a6613911565b60408051938460209384928383016137bd91612b44565b6137c691612b44565b0393601f199485810187526137db908761043d565b80516137e690613d8f565b9586516137f9906001600160401b031690565b61380290613a38565b90845191829185830161381491612b44565b61381d91612b44565b03868101825261382d908261043d565b835196879184830161383e91612b44565b61384791612b44565b038581018752613857908761043d565b61385f613928565b95835196879184830161387191612b44565b61387a91612b44565b03858101875261388a908761043d565b015161389590613d8f565b9081516138a8906001600160401b031690565b6138b190613a38565b9481519586918583016138c391612b44565b6138cc91612b44565b0384810186526138dc908661043d565b5193849283016138eb91612b44565b6138f491612b44565b039081018252610e09908261043d565b80511561198a5760200190565b61391961330f565b600a61392482613904565b5390565b61393061330f565b601261392482613904565b61394361330f565b602861392482613904565b61395661330f565b603061392482613904565b61396961330f565b603861392482613904565b61397c613329565b60a25f5b607f808311156139a55790608060019284161760208286010153019060071c90613980565b50607f600192166020828501015301815290565b6139c1613329565b61014260015f60c26020850153019060071c90613980565b6139e161330f565b600861392482613904565b6139f461330f565b601061392482613904565b613a0761330f565b601a61392482613904565b613a1a61330f565b603261392482613904565b613a2d61330f565b604261392482613904565b9060809160806001600160401b03821610613a9357613a55613329565b5f5b607f80841115613a7b57908560019285161760208285010153019160071c91613a57565b5090929350607f600192166020828501015301815290565b909150613a9e61330f565b9060f81b6001600160f81b0319165f1a61392482613904565b613abf610bf5565b90613ac8613911565b6040805193846020938492838301613adf91612b44565b613ae891612b44565b0393601f19948581018752613afd908761043d565b8051613b0890613e47565b958651613b1b906001600160401b031690565b613b2490613a38565b908451918291858301613b3691612b44565b613b3f91612b44565b038681018252613b4f908261043d565b8351968791848301613b6091612b44565b613b6991612b44565b038581018752613b79908761043d565b613b81613928565b958351968791848301613b9391612b44565b613b9c91612b44565b038581018752613bac908761043d565b0151613895906131ef565b60ff5f805160206142658339815191525460401c1615613bd357565b604051631afcd79f60e31b8152600490fd5b610e09613c26612e8f92613cde613cca612e8f613cd1613c03610bf5565b94613c0c6139d9565b9060409485928351998a91612e8f60209b8c850190612b44565b0398613c3a601f199a8b810183528261043d565b613c7989613c6d613c5d612ecd613c56613c56895161ffff1690565b61ffff1690565b612e8f8851958694850190612b44565b038b810183528261043d565b613c8889613c6d613c5d6139ec565b613ca589613c6d613c5d612ecd6108558489015163ffffffff1690565b613cbe613cb06139ff565b85519d8e938c850190612b44565b038981018c528b61043d565b0151613759565b9151968794850190612b44565b0390810183528261043d565b90813b15613d6e575f8051602061424583398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2805115613d5357613d5091614181565b50565b505034613d5c57565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b610e09612e8f612e8f613cde613e37612ecd6108556020612e8f98613e2c612e8f613e06613dbb610bf5565b613dd5613dc66139d9565b6040519d8e9389850190612b44565b039a613de9601f199c8d810183528261043d565b613c6d61324d612ecd613e00613e00895160ff1690565b60ff1690565b613e20613e116139ec565b6040519a8b9387850190612b44565b0389810189528861043d565b015163ffffffff1690565b6040519586936020850190612b44565b613e4f610bf5565b90613e58613911565b91604080518094602093848301613e6e91612b44565b613e7791612b44565b0392601f19938481018652613e8c908661043d565b8051613e9790613795565b948551613eaa906001600160401b031690565b613eb390613a38565b908351918291868301613ec591612b44565b613ece91612b44565b038581018252613ede908261043d565b8251958691858301613eef91612b44565b613ef891612b44565b038481018652613f08908661043d565b613f10613928565b948251958691858301613f2291612b44565b613f2b91612b44565b038481018652613f3b908661043d565b82810151613f4890613759565b948251958691858301613f5a91612b44565b613f6391612b44565b038481018652613f73908661043d565b613f7b6139ff565b948251958691858301613f8d91612b44565b613f9691612b44565b038481018652613fa6908661043d565b81810151613fb390613759565b948251958691858301613fc591612b44565b613fce91612b44565b038481018652613fde908661043d565b613fe661393b565b948251958691858301613ff891612b44565b61400191612b44565b038481018652614011908661043d565b60608101516140259063ffffffff16613a38565b94825195869185830161403791612b44565b61404091612b44565b038481018652614050908661043d565b614058613a12565b94825195869185830161406a91612b44565b61407391612b44565b038481018652614083908661043d565b608081015161409190613759565b9482519586918583016140a391612b44565b6140ac91612b44565b0384810186526140bc908661043d565b6140c4613961565b9482519586918583016140d691612b44565b6140df91612b44565b0384810186526140ef908661043d565b60a08101516001600160401b031661410690613a38565b94825195869185830161411891612b44565b61412191612b44565b038481018652614131908661043d565b614139613a25565b94825195869185830161414b91612b44565b61415491612b44565b038481018652614164908661043d565b60c0015161417190613759565b905193849283016138eb91612b44565b5f80610e0993602081519101845af43d156141bd573d916141a183610478565b926141af604051948561043d565b83523d5f602085013e6141c1565b6060915b906141e857508051156141d657602081519101fd5b60405163d6bda27560e01b8152600490fd5b8151158061421b575b6141f9575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156141f156fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212201dd53cc2caf86754ed88247c9b779f72471445dd4bcf6d516a7bf3bb29c0dce864736f6c63430008190033", + "bytecode": "0x60a0806040523460c857306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1660b957506001600160401b036002600160401b0319828216016075575b6040516147a190816100cd82396080518181816109570152613bd20152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80806056565b63f92ee8a960e01b8152600490fd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806302ddde7b1461028457806304676e381461027f5780630d6f1e9d1461027a57806316a91b741461027557806327e935a11461027057806347453a2a1461026b5780634b3254fb146102665780634ed891d0146102615780634f1ef2861461025c5780634fca79cb1461025757806352d1902d14610252578063621fbd591461024d5780636744c79f146102485780637a9e5e4b146102435780638cbc18391461023e5780638fb360371461023957806393ec06be1461023457806395fabcf01461022f578063a3ad9cc31461022a578063a593aec214610225578063a9e50c1014610220578063ad3cb1cc1461021b578063b632ab5714610216578063b8f8d7e514610211578063bf7e214f1461020c578063c16a8a5614610207578063c4d66de814610202578063c8c14d17146101fd578063c94c93e7146101f8578063cde78421146101f3578063d5b5272a146101ee578063de152649146101e9578063e0064900146101e4578063e0c14aa8146101df578063e0c1c0b4146101da578063e2fc04ae146101d5578063e36f9a07146101d0578063e44efb5a146101cb5763f5786b4b146101c6575f80fd5b61193a565b61191b565b6118fe565b6117e1565b611515565b6113e9565b6113ac565b61138f565b61129a565b611272565b611243565b611227565b61110d565b6110ea565b6110b6565b611093565b611077565b611032565b610ffa565b610f00565b610c12565b610bf5565b610bd9565b610b89565b610ac2565b610a48565b6109d4565b6109ae565b610945565b610899565b61078e565b610628565b6105c8565b61056e565b610511565b6104ed565b610431565b610393565b610297565b5f91031261029357565b5f80fd5b34610293575f366003190112610293575f8051602061474c833981519152805460ff8160401c16908115610363575b50610351575f8051602061474c833981519152805467ffffffffffffffff19166002179055805460ff60401b1916600160401b179055610304611f66565b5f8051602061474c833981519152805460ff60401b19169055604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b60405163f92ee8a960e01b8152600490fd5b600291506001600160401b031610155f6102c6565b63ffffffff81160361029357565b359061039182610378565b565b346102935760203660031901126102935760606004356103b281610378565b63ffffffff8091165f52600960205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60409063ffffffff61042e949316815281602082015201906103ed565b90565b34610293575f3660031901126102935761045d6104546104105463ffffffff1690565b63ffffffff1690565b61046681611fe8565b90610479610473826105f8565b60051c90565b5f5b8181106104d5575050601f811690816104b2575b8261049f600c5463ffffffff1690565b6104ae60405192839283610411565b0390f35b81602091038301019060015f199160200360031b1b011981511690525f8061048f565b806001916010015460208260051b870101520161047b565b34610293575f36600319011261029357602063ffffffff6104105416604051908152f35b346102935760203660031901126102935760043561052f3633612d6b565b8015610548576004541561053f57005b6004555f600355005b604051636ad43cc360e11b8152600490fd5b634e487b7160e01b5f52603260045260245ffd5b34610293576020366003190112610293576004356006548110156102935760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01546040516001600160a01b039091168152602090f35b34610293575f3660031901126102935760206040516104008152f35b634e487b7160e01b5f52601160045260245ffd5b90601f820180921161060657565b6105e4565b9061010091820180921161060657565b9190820180921161060657565b34610293575f3660031901126102935760206040516101188152f35b600435906001600160a01b038216820361029357565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761068957604052565b61065a565b602081019081106001600160401b0382111761068957604052565b604081019081106001600160401b0382111761068957604052565b60e081019081106001600160401b0382111761068957604052565b6001600160401b03811161068957604052565b90601f801991011681019081106001600160401b0382111761068957604052565b60405190610391826106c4565b604051906103918261066e565b6001600160401b03811161068957601f01601f191660200190565b81601f820112156102935780359061075f8261072d565b9261076d60405194856106f2565b8284526020838301011161029357815f926020809301838601378301015290565b6040366003190112610293576107a2610644565b6024356001600160401b038111610293576107c1903690600401610748565b906107ca613bc8565b6107d43633612d6b565b6107dc613bc8565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610868575b5061082e57604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f8051602061472c833981519152830361084f5761084d92506141b2565b005b604051632a87526960e21b815260048101849052602490fd5b61088b91945060203d602011610892575b61088381836106f2565b8101906128a0565b925f610804565b503d610879565b34610293576020366003190112610293576104ae6004356108b981610378565b60405f8180516108c88161066e565b828152826020820152015263ffffffff8092165f5260096020526001815f206001600160401b038351946108fb8661066e565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610293575f366003190112610293577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316300361099c5760206040515f8051602061472c8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610293575f3660031901126102935760205f54604051908152f35b8015150361029357565b34610293576020366003190112610293576004356109f1816109ca565b600f546001600160a01b031633819003610a2a57507fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755d005b60449060405190636e01d1ef60e11b82523360048301526024820152fd5b3461029357602036600319011261029357610a61610644565b5f8051602061470c833981519152546001600160a01b03919082163303610aab57803b15610a925761084d90612e87565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b3461029357604036600319011261029357602435600435610ae33633612d6b565b63ffffffff6104105416610b445781811080610b38575b610b039061201a565b818110610b0c57005b80610b32610b1b600193612066565b81549060031b906001821b915f19901b1916179055565b01610b03565b50610400821115610afa565b60405162461bcd60e51b815260206004820152601f60248201527f4f50503a206f7574626f756e642062616e6b20616c7265616479206c697665006044820152606490fd5b34610293575f366003190112610293575f8051602061470c8339815191525460a01c60ff1615610bd1576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610bbe565b34610293575f3660031901126102935760206040516180008152f35b34610293575f366003190112610293576020600354604051908152f35b3461029357602036600319011261029357600435610c2f81610378565b610c393633612d6b565b600c5463ffffffff16610c4e61045482612078565b63ffffffff831603610ed15750610c636120c7565b63ffffffff82166060820152610c94610c846001600160401b034216612104565b6001600160401b03166040830152565b610c9c612edf565b60a08201525f600854908115159182610d3e575b7fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d88761034c86610d32610ce188612f60565b91610cf161800084511115612793565b610d1383516020850120610d058184613323565b610d0e83613389565b600d55565b610d1c836133e1565b63ffffffff1663ffffffff19600c541617600c55565b60405191829182612805565b5f92836101005b838210610e70575b5050610d639083159081610e68575b501561212e565b81610d6d81612209565b60c086019081525f5b828110610e30575050610d889161226c565b905f5b828110610e0257505f5b818110610df1575050610ce161034c93610de7610de27fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d8879795610dda610d3296600855565b600e5461226c565b600e55565b9350829450610cb0565b600190610dfc6126c1565b01610d95565b80610e2a610e1a610e156001948661061b565b611caa565b50610e2483611caa565b90612552565b01610d8b565b6001919250610e5e81610e4281611caa565b50610e4e865191611e88565b610e588383612258565b52612258565b5001908391610d76565b90505f610d5c565b610e8d610e88610e8284979894611caa565b50611e88565b612c9e565b90618000610e9b838361061b565b11610ec757610eb982610eb3600194610ebf9461061b565b9461061b565b96612120565b940190610d45565b5081959450610d4d565b610eda90612078565b6040516307d3e83d60e51b815263ffffffff918216600482015291166024820152604490fd5b3461029357602036600319011261029357600435610f1d81610378565b610f273633612d6b565b63ffffffff9081811691825f52600960205260405f20541615610fb557610f84610f8f91610f70610f68610454610f63600c5463ffffffff1690565b613432565b851115612816565b63ffffffff165f52600960205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b60405162461bcd60e51b815260206004820152601760248201527f4f50503a207265636f7264206e6f742070726573656e740000000000000000006044820152606490fd5b34610293575f3660031901126102935760206001600160401b0360015416604051908152f35b6040519061102d8261068e565b5f8252565b34610293575f366003190112610293576104ae604051611051816106a9565b60058152640352e302e360dc1b60208201526040519182916020835260208301906103ed565b34610293575f3660031901126102935760206040516120008152f35b34610293575f36600319011261029357602063ffffffff600c5416604051908152f35b34610293575f366003190112610293575f8051602061470c833981519152546040516001600160a01b039091168152602090f35b34610293575f36600319011261029357602063ffffffff600a5416604051908152f35b3461029357602036600319011261029357611126610644565b5f8051602061474c83398151915254906001600160401b0360ff8360401c161592168015908161121f575b6001149081611215575b15908161120c575b50610351575f8051602061474c833981519152805467ffffffffffffffff1916600117905561119690826111e857613445565b61119c57005b5f8051602061474c833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290806020810161034c565b5f8051602061474c833981519152805460ff60401b1916600160401b179055613445565b9050155f611163565b303b15915061115b565b839150611151565b34610293575f3660031901126102935760206040516101008152f35b34610293576020366003190112610293576004355f526005602052602060ff60405f2054166040519015158152f35b34610293575f36600319011261029357600f546040516001600160a01b039091168152602090f35b34610293576020366003190112610293576004356112b781610378565b6112c13633612d6b565b604051906112ce8261068e565b63ffffffff81168083521561134a577f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69161132a611311600a5463ffffffff1690565b9163ffffffff90511663ffffffff19600a541617600a55565b6040805163ffffffff92831681529290911660208301528190810161034c565b60405162461bcd60e51b815260206004820152601d60248201527f4f50503a20696e76616c696420726574656e74696f6e20636f6e6669670000006044820152606490fd5b34610293575f366003190112610293576020600e54604051908152f35b34610293575f366003190112610293576020600854604051908152f35b6004359061ffff8216820361029357565b359061ffff8216820361029357565b3461029357604080600319360112610293576114036113c9565b6024356001600160401b03811161029357611422903690600401610748565b9160045415611505576114353633612d6b565b5f8051602061470c8339815191525461146490611458906001600160a01b031681565b6001600160a01b031690565b81516368fc2b7760e11b815261ffff841662010000176004820152336024820152908290829060449082905afa908115611500575f916114d2575b50156114af5761084d8383613669565b51639a28d60760e01b815233600482015261ffff91909116602482015260449150fd5b6114f29150823d84116114f9575b6114ea81836106f2565b810190612870565b505f61149f565b503d6114e0565b612895565b5163738c481160e01b8152600490fd5b346102935760203660031901126102935761152e610644565b6115383633612d6b565b600f80546001600160a01b0319166001600160a01b0392909216919091179055005b919082604091031261029357604051611572816106a9565b8092803560ff811681036102935782526020908101359161159283610378565b0152565b91906080838203126102935760206115ca604051926115b4846106a9565b604084966115c2838261155a565b86520161155a565b910152565b35906001600160401b038216820361029357565b6001600160401b0381116106895760051b60200190565b9190916040908181850312610293578151611614816106a9565b8094823561162181610378565b8252602092838101356001600160401b039182821161029357019180601f8401121561029357823590611653826115e3565b96611660815198896106f2565b828852868089019360051b8601019482861161029357878101935b86851061168d57505050505050500152565b84358681116102935782019060609081601f198488030112610293578451916116b58361066e565b6116c08c85016113da565b8352858401356116cf81610378565b8c84015283013591888311610293576116ef878d80969581960101610748565b8682015281520194019361167b565b90604060206103919361172584825163ffffffff6020809260ff8151168552015116910152565b015191019063ffffffff6020809260ff8151168552015116910152565b9061042e91602081526117596020820183516116fe565b602082015190611777610140928360a08401526101608301906103ed565b9160c06117bd611798604087015195601f19968787830301858801526103ed565b606087015163ffffffff1660e0860152608087015186868303016101008701526103ed565b60a08601516001600160401b031661012085015294015192828503019101526103ed565b346102935760031960403682011261029357600480356001600160401b0392838211610293576101409082360301126102935761181c610713565b9061182936828501611596565b825260848101358481116102935761184690843691840101610748565b602083015260a48101358481116102935761186690843691840101610748565b604083015261187760c48201610386565b606083015260e48101358481116102935761189790843691840101610748565b60808301526118a961010482016115cf565b60a08301526101248101359084821161029357836118ca9236920101610748565b60c0820152602435928311610293576118ec6118f2926104ae943691016115fa565b906129e1565b60405191829182611742565b34610293575f366003190112610293576020600454604051908152f35b34610293575f3660031901126102935760206004541515604051908152f35b3461029357602080600319360112610293576004908135825415611c99576119623633612d6b565b8015611c885782541461197157005b5f82556003548015611c7757611985612191565b5f5480611c0b5750611995611f9e565b60409060408301526119c26119b26001600160401b034216612104565b6001600160401b031660a0840152565b6119ca6121ce565b600181529283916119da8261380b565b8684019081525f5b838110611b8f57505050506119f6916129e1565b91611a188180850151604051809381926347f5343160e01b8352898301612805565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91611b72575b505f55611a75611a5960a08501516001600160401b031690565b6001600160401b03166001600160401b03196001541617600155565b611a7d6121e7565b928352820152611a8c81612c9e565b917fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755c611b2457611abf83600e5461061b565b906120008211611aff575050611af691611adb610de2926139e5565b611aee611ae9600854612120565b600855565b600e5461061b565b61084d5f600355565b604080516306f8bcbb60e51b8152918201928352612000602084015290918291010390fd5b618000611b308461060b565b11611b465750611af691611adb610de2926139e5565b611b4f8361060b565b6040805163048c985b60e51b815292830191825261800060208301528291010390fd5b611b899150823d84116108925761088381836106f2565b5f611a3f565b60019192939450611bff81611bac611ba68261349f565b5061386f565b8a611bb9825161ffff1690565b910151611bee611bcd825163ffffffff1690565b611be2611bd8610720565b61ffff9095168552565b63ffffffff16838e0152565b87820152855190610e588383612258565b500190859392916119e2565b604051630c9b3d8760e21b8152858101918252905f9082908190602001038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91611c55575b50611995565b611c7191503d805f833e611c6981836106f2565b81019061295a565b5f611c4f565b60405163783bc0f160e11b81528390fd5b604051636ad43cc360e11b81528390fd5b60405163738c481160e01b81528390fd5b600754811015611ce357600a9060075f52027fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801905f90565b61055a565b90604051611cf5816106a9565b915460ff8116835260081c63ffffffff166020830152565b90600182811c92168015611d3b575b6020831014611d2757565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611d1c565b9060405191825f8254611d5781611d0d565b908184526020946001916001811690815f14611dc35750600114611d85575b505050610391925003836106f2565b5f90815285812095935091905b818310611dab57505061039193508201015f8080611d76565b85548884018501529485019487945091830191611d92565b9250505061039194925060ff191682840152151560051b8201015f8080611d76565b9060408051611df3816106a9565b809363ffffffff8082541683526001809201805491611e11836115e3565b95611e1f60405197886106f2565b83875260209160208801935f5260205f20915f945b868610611e4957505050505050505060200152565b60028589928451611e598161066e565b86885461ffff8116835260101c1683820152611e76858901611d45565b86820152815201940195019492611e34565b90604051611e95816106a9565b60206115ca60088395604051611eaa816106c4565b604051611eb6816106a9565b611ebf83611ce8565b8152611ecd60018401611ce8565b868201528152611edf60028301611d45565b85820152611eef60038301611d45565b6040820152611f15611f08600484015463ffffffff1690565b63ffffffff166060830152565b611f2160058301611d45565b6080820152611f4d611f3d60068401546001600160401b031690565b6001600160401b031660a0830152565b611f5960078301611d45565b60c0820152855201611de5565b5f806008545b808210611f7a575050600e55565b9091611f8b610e88610e8285611caa565b8101809111610606579160010190611f6c565b604051611faa8161068e565b5f8152905f368137565b60405190611fc1826106a9565b6001825260203681840137565b60405190611fdb826106a9565b600a825260203681840137565b90611ff28261072d565b611fff60405191826106f2565b8281528092612010601f199161072d565b0190602036910137565b1561202157565b60405162461bcd60e51b815260206004820152601a60248201527f4f50503a20696e76616c6964207072657761726d2072616e67650000000000006044820152606490fd5b610400811015611ce357601001905f90565b90600163ffffffff8093160191821161060657565b6040519061209a826106a9565b5f6020838281520152565b604051906120b2826106a9565b816120bb61208d565b815260206115ca61208d565b604051906120d4826106c4565b606060c0838281526120e46120a5565b60208201525f60408201525f838201525f60808201528260a08201520152565b906103e86001600160401b038093160291821691820361060657565b5f1981146106065760010190565b1561213557565b60405162461bcd60e51b815260206004820152602e60248201527f4f50503a20717565756564206d6573736167652065786365656473204d41585f60448201526d454e56454c4f50455f425954455360901b6064820152608490fd5b6040519061219e826106c4565b606060c0836121ab6120a5565b81528260208201528260408201525f838201528260808201525f60a08201520152565b604051906121db826106a9565b60606020835f81520152565b604051906121f4826106a9565b816121fd612191565b815260206115ca6121ce565b90612213826115e3565b61222060405191826106f2565b8281528092612231601f19916115e3565b01905f5b82811061224157505050565b60209061224c6121e7565b82828501015201612235565b8051821015611ce35760209160051b010190565b9190820391821161060657565b634e487b7160e01b5f525f60045260245ffd5b818110612297575050565b5f815560010161228c565b9190601f81116122b157505050565b610391925f5260205f20906020601f840160051c830193106122db575b601f0160051c019061228c565b90915081906122ce565b9190918281146123cc576122f98354611d0d565b6001600160401b0381116106895761231b816123158454611d0d565b846122a2565b5f93601f821160011461235a5761234b92939482915f9261234f575b50508160011b915f199060031b1c19161790565b9055565b015490505f80612337565b61236c601f198316915f5260205f2090565b9461237a845f5260205f2090565b915f5b8181106123b45750958360019596971061239c575b505050811b019055565b01545f1960f88460031b161c191690555f8080612392565b8783015484556001938401939092019160200161237d565b509050565b6123db8154611d0d565b90816123e5575050565b81601f5f93116001146123f6575055565b908083918252612415601f60208420940160051c84016001850161228c565b5555565b600160401b821161068957805482825580831061243557505050565b6001916001600160ff1b0382811683036106065784168403610606575f5260205f209060011b81019260011b015b82811061246f57505050565b805f600292556124808382016123d1565b01612463565b8181146125135781549161249a8383612419565b5f5260205f20905f5260205f20905f905b8382106124b85750505050565b8060019184036124d3575b60028091019301910190916124ab565b8054845461ffff191661ffff919091161784558054845465ffffffff0000191665ffffffff000090911617845561250e8183018584016122e5565b6124c3565b5050565b818103612522575050565b6001808361254a63ffffffff610391965416859063ffffffff1663ffffffff19825416179055565b019101612486565b906126bc5781811415918261256657505050565b60088091846103919561257d575b50019101612517565b8061262d575b5061259460028201600286016122e5565b6125a460038201600386016122e5565b6125d16125b8600483015463ffffffff1690565b600486019063ffffffff1663ffffffff19825416179055565b6125e160058201600586016122e5565b6126176125f860068301546001600160401b031690565b60068601906001600160401b03166001600160401b0319825416179055565b61262760078201600786016122e5565b5f612574565b61268a575b600184016001820190818103612649575b50612583565b8154815460ff191660ff919091161781556126839163ffffffff9054851c1664ffffffff0082549160081b169064ffffffff001916179055565b5f80612643565b8054845460ff191660ff919091161784558054845464ffffffff00191690831c60081b64ffffffff0016178455612632565b612279565b600754801561277f575f19016126d681611caa565b6126bc575f815560015f600183015560096002926126f6600282016123d1565b612702600382016123d1565b5f6004820155612714600582016123d1565b5f6006820155612726600782016123d1565b5f600882015501908154915f815582612743575b50505050600755565b6001600160ff1b0383168303610606575f5260205f209160011b8201915b8281101561273a57805f8592556127798382016123d1565b01612761565b634e487b7160e01b5f52603160045260245ffd5b1561279a57565b60405162461bcd60e51b815260206004820152603a60248201527f4f50503a20656e76656c6f70652065786365656473204d41585f454e56454c4f60448201527f50455f42595445532028657374696d61746f72206472696674290000000000006064820152608490fd5b90602061042e9281815201906103ed565b1561281d57565b60405162461bcd60e51b815260206004820152602560248201527f4f50503a207265636f7264207374696c6c20696e20726574656e74696f6e2077604482015264696e646f7760d81b6064820152608490fd5b91908260409103126102935760208251612889816109ca565b92015161042e81610378565b6040513d5f823e3d90fd5b90816020910312610293575190565b90602080835260609060608401908063ffffffff94858151168288015201519460408080830152865180945260808201908360808660051b8501019801955f935b8685106129035750505050505050505090565b909192939495969799988680612945600193868f89908f607f198a8603018d52519061ffff8251168552868201511686850152015191818a82015201906103ed565b9b9c9a019897969190910194019291906128f0565b602081830312610293578051906001600160401b038211610293570181601f820112156102935780519061298d8261072d565b9261299b60405194856106f2565b8284526020838301011161029357815f9260208093018386015e8301015290565b90916129d361042e936040845260408401906103ed565b9160208184039101526103ed565b906129ea612191565b506040805163765809ab60e11b81526004926020929173__$b91d2ad766fd07dc99f56c1a0b020615bb$__9190848180612a26858a83016128af565b0381865af4908115611500575f91612c81575b5060808701908582518051155f14612bfd5750508451630c9b3d8760e21b8152878101918252905f90829081906020010381875af492831561150057612aa693612a9993612a8f935f92612be1575b5052613701565b5163ffffffff1690565b63ffffffff166060870152565b815162409a0960e81b81525f8180612ac089898301611742565b0381855af4908115611500575f91612bc7575b508584018051805180612b8c57505052815163b93612a760e01b815293838580612aff89858301611742565b0381855af492831561150057612b38955f958695612b6d575b505051630c9b3d8760e21b81529081019283529384928391829160200190565b03915af4908115611500575f91612b53575b5060c082015290565b612b6791503d805f833e611c6981836106f2565b5f612b4a565b612b84929550803d106108925761088381836106f2565b925f80612b18565b915093925084849695929601209085519086012003612bac575050505090565b51630ca2e06d60e11b815292839261082a92919084016129bc565b612bdb91503d805f833e611c6981836106f2565b5f612ad3565b612bf69192503d805f833e611c6981836106f2565b905f612a88565b90919350612c1e92508551809381926347f5343160e01b83528a8301612805565b0381865af4908115611500575f91612c64575b50818103612c40575050612aa6565b8351631d97afef60e31b815280870192835260208301919091529081906040010390fd5b612c7b9150853d87116108925761088381836106f2565b5f612c31565b612c989150853d87116108925761088381836106f2565b5f612a39565b61010090602080910190602082510151515f925b818410612cc0575050505090565b90919293601880820191828111610606576040612ce1888887510151612258565b5101515101018091116106065793600101929190612cb2565b600411610293575f90600490565b6001600160e01b03199035818116939260048110612d2557505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f8051602061470c8339815191528054612da3906001600160a01b0316612d9a612d9486612cfa565b90612d08565b90843091613b47565b9015612db0575b50505050565b63ffffffff1615612e67575f8051602061470c833981519152805460ff60a01b1916600160a01b179055612df190611458908190546001600160a01b031690565b91823b1561029357612e1c925f9283604051809681958294634a63ebf760e11b845260048401612d34565b03925af1801561150057612e4e575b505f8051602061470c833981519152805460ff60a01b191690555f808080612daa565b80612e5b612e61926106df565b80610289565b5f612e2b565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f8051602061470c83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b600d5480612efa5750604051612ef48161068e565b5f815290565b60405190630c9b3d8760e21b825260048201525f8160248173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91612f3a575090565b61042e91503d805f833e611c6981836106f2565b805191908290602001825e015f815290565b612f68611020565b612f70613dd9565b92604080518095602094858301612f8691612f4e565b612f8f91612f4e565b0394601f19958681018252612fa490826106f2565b8451612faf90613c21565b908251918291868301612fc191612f4e565b612fca91612f4e565b038681018252612fda90826106f2565b612fe2613df0565b908251918291868301612ff491612f4e565b612ffd91612f4e565b03868101825261300d90826106f2565b8385015161301a90613c5d565b90815161302d906001600160401b031690565b61303690613f00565b90835191829187830161304891612f4e565b61305191612f4e565b03878101825261306190826106f2565b825191829186830161307291612f4e565b61307b91612f4e565b03868101825261308b90826106f2565b613093613e03565b9082519182918683016130a591612f4e565b6130ae91612f4e565b0386810182526130be90826106f2565b848201516001600160401b03166130d490613f00565b9082519182918683016130e691612f4e565b6130ef91612f4e565b0386810182526130ff90826106f2565b613107613e16565b90825191829186830161311991612f4e565b61312291612f4e565b03868101825261313290826106f2565b60608501516131469063ffffffff16613f00565b90825191829186830161315891612f4e565b61316191612f4e565b03868101825261317190826106f2565b613179613e29565b90825191829186830161318b91612f4e565b61319491612f4e565b0386810182526131a490826106f2565b60808501516131b89063ffffffff16613f00565b9082519182918683016131ca91612f4e565b6131d391612f4e565b0386810182526131e390826106f2565b6131eb613e3c565b9082519182918683016131fd91612f4e565b61320691612f4e565b03868101825261321690826106f2565b60a085015161322490613c21565b90825191829186830161323691612f4e565b61323f91612f4e565b03868101825261324f90826106f2565b915f925b60c086018051518510156133195761329961330f60c0936133036132b76132b18a6001978f6132ab908f61329f8f61329961328c613e81565b91519b8c94850190612f4e565b90612f4e565b039081018852876106f2565b51612258565b51613f7f565b6132f58d8c6132e98c6132996132dc6132d788516001600160401b031690565b613f00565b9151988994850190612f4e565b039081018552846106f2565b88519485938c850190612f4e565b038a81018352826106f2565b9401939050613253565b5095509350505050565b63ffffffff8082165f52600960205260405f20908154166133845761335a600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b6133a09063ffffffff91829182600a541690614649565b169081151590816133c9575b506133b45750565b5f5260096020525f6001604082208281550155565b9050815f52600960205260405f20541615155f6133ac565b805190601f8201908183116106065760059160051c915f5b83811061341c575050505063ffffffff610410911663ffffffff19825416179055565b80821b83016020015160108201556001016133f9565b61042e9063ffffffff600a541690614649565b61346e9061345161407f565b61345961407f565b61346161407f565b61346961407f565b612e87565b61347661407f565b61347e61407f565b60c860405161348c8161068e565b5260c863ffffffff19600a541617600a55565b600254811015611ce35760025f5260011b7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01905f90565b91909182516001600160401b038111610689576134f8816123158454611d0d565b602080601f83116001146135345750819061234b9394955f926135295750508160011b915f199060031b1c19161790565b015190505f80612337565b90601f19831695613548855f5260205f2090565b925f905b8882106135825750508360019596971061356a57505050811b019055565b01515f1960f88460031b161c191690555f8080612392565b8060018596829496860151815501950193019061354c565b9291906126bc578051835461ffff191661ffff91909116178355600180930190602080910151938451916001600160401b038311610689576135e6836135e08654611d0d565b866122a2565b602091601f8411600114613618575050819061234b9394955f926135295750508160011b915f199060031b1c19161790565b95601f9291921984169661362f865f5260205f2090565b935f915b898310613652575050508360019596971061356a57505050811b019055565b838501518655948501949381019391810191613633565b600254916003548084115f146136be575061039192506136976040519261368f846106a9565b61ffff168352565b60208201526136b86003546136b36136ae82612120565b600355565b61349f565b9061359a565b6136c790612120565b60035561ffff604051926136da846106a9565b1682526020820152600160401b821015610689576136b8826001610391940160025561349f565b6137349161370d611020565b90613716613ea1565b61329961377a60409260405197889161329960209889850190612f4e565b0396613748601f19988981018352826106f2565b61376e61375f6132d76104548a5163ffffffff1690565b60405194859389850190612f4e565b038781018352826106f2565b915f925b8486018051518510156133195761329961380161329961330389956137f38d6137cd6137c78d60019a6132ab8f6137bb8f9161329961328c613df0565b038681018952886106f2565b516140ad565b926132e96137e56132d786516001600160401b031690565b8d519788938d850190612f4e565b885194859388850190612f4e565b940193905061377e565b90613815826115e3565b60409061382560405191826106f2565b8381528093613836601f19916115e3565b01915f5b8381106138475750505050565b60209082516138558161066e565b5f8152825f8183015260608583015282860101520161383a565b9060405161387c816106a9565b60206115ca6001839561ffff815416855201611d45565b8151815463ffffffff191663ffffffff91821617825591600180920160208092015191808351936138c48585612419565b01915f52805f205f925b8484106138de5750505050505050565b8051805183548583015161ffff90921665ffffffffffff199091161790891660101b65ffffffff0000161783556040015180519187840191906001600160401b03841161068957828692613937866123158d9754611d0d565b8390601f871160011461397757958061396892600297985f926135295750508160011b915f199060031b1c19161790565b90555b019201930192906138ce565b9590601f1982169661398c845f5260205f2090565b975f5b8181106139cc575091879891846002999594106139b4575b505050811b01905561396b565b01515f1960f88460031b161c191690555f80806139a7565b838301518a5597909801978d978b97938401930161398f565b600754600160401b81101561068957806001613a049201600755611caa565b6126bc576008602083613b3e60c06103919651613a9e815186613a608251613a3a60ff8251168d9060ff1660ff19825416179055565b909101518a5464ffffffff00191660089190911b64ffffffff0016178a5563ffffffff90565b878060018c01930151613a8160ff825116859060ff1660ff19825416179055565b0151825464ffffffff001916911660081b64ffffffff0016179055565b613aae85820151600289016134d7565b613abf6040820151600389016134d7565b613aec613ad3606083015163ffffffff1690565b600489019063ffffffff1663ffffffff19825416179055565b613afd6080820151600589016134d7565b613b33613b1460a08301516001600160401b031690565b60068901906001600160401b03166001600160401b0319825416179055565b0151600786016134d7565b01519101613893565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b03841117610689576040935f9385528380528360205251915afa613bb657565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613c06575b505061099c57565b5f8051602061472c8339815191525416141590505f80613bfe565b61042e61329991613c4f613c3e6001600160401b03835116613f00565b916040519485936020850190612f4e565b03601f1981018352826106f2565b613c65611020565b90613c6e613dd9565b6040805193846020938492838301613c8591612f4e565b613c8e91612f4e565b0393601f19948581018752613ca390876106f2565b8051613cae90614257565b958651613cc1906001600160401b031690565b613cca90613f00565b908451918291858301613cdc91612f4e565b613ce591612f4e565b038681018252613cf590826106f2565b8351968791848301613d0691612f4e565b613d0f91612f4e565b038581018752613d1f90876106f2565b613d27613df0565b958351968791848301613d3991612f4e565b613d4291612f4e565b038581018752613d5290876106f2565b0151613d5d90614257565b908151613d70906001600160401b031690565b613d7990613f00565b948151958691858301613d8b91612f4e565b613d9491612f4e565b038481018652613da490866106f2565b519384928301613db391612f4e565b613dbc91612f4e565b03908101825261042e90826106f2565b805115611ce35760200190565b613de1611fb4565b600a613dec82613dcc565b5390565b613df8611fb4565b6012613dec82613dcc565b613e0b611fb4565b6028613dec82613dcc565b613e1e611fb4565b6030613dec82613dcc565b613e31611fb4565b6038613dec82613dcc565b613e44611fce565b60a25f5b607f80831115613e6d5790608060019284161760208286010153019060071c90613e48565b50607f600192166020828501015301815290565b613e89611fce565b61014260015f60c26020850153019060071c90613e48565b613ea9611fb4565b6008613dec82613dcc565b613ebc611fb4565b6010613dec82613dcc565b613ecf611fb4565b601a613dec82613dcc565b613ee2611fb4565b6032613dec82613dcc565b613ef5611fb4565b6042613dec82613dcc565b9060809160806001600160401b03821610613f5b57613f1d611fce565b5f5b607f80841115613f4357908560019285161760208285010153019160071c91613f1f565b5090929350607f600192166020828501015301815290565b909150613f66611fb4565b9060f81b6001600160f81b0319165f1a613dec82613dcc565b613f87611020565b90613f90613dd9565b6040805193846020938492838301613fa791612f4e565b613fb091612f4e565b0393601f19948581018752613fc590876106f2565b8051613fd09061430f565b958651613fe3906001600160401b031690565b613fec90613f00565b908451918291858301613ffe91612f4e565b61400791612f4e565b03868101825261401790826106f2565b835196879184830161402891612f4e565b61403191612f4e565b03858101875261404190876106f2565b614049613df0565b95835196879184830161405b91612f4e565b61406491612f4e565b03858101875261407490876106f2565b0151613d5d90613701565b60ff5f8051602061474c8339815191525460401c161561409b57565b604051631afcd79f60e31b8152600490fd5b61042e6140ee613299926141a66141926132996141996140cb611020565b946140d4613ea1565b9060409485928351998a9161329960209b8c850190612f4e565b0398614102601f199a8b81018352826106f2565b614141896141356141256132d761411e61411e895161ffff1690565b61ffff1690565b6132998851958694850190612f4e565b038b81018352826106f2565b61415089614135614125613eb4565b61416d896141356141256132d76104548489015163ffffffff1690565b614186614178613ec7565b85519d8e938c850190612f4e565b038981018c528b6106f2565b0151613c21565b9151968794850190612f4e565b039081018352826106f2565b90813b15614236575f8051602061472c83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561421b5761421891614668565b50565b50503461422457565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b61042e6132996132996141a66142ff6132d76104546020613299986142f46132996142ce614283611020565b61429d61428e613ea1565b6040519d8e9389850190612f4e565b039a6142b1601f199c8d81018352826106f2565b61413561375f6132d76142c86142c8895160ff1690565b60ff1690565b6142e86142d9613eb4565b6040519a8b9387850190612f4e565b038981018952886106f2565b015163ffffffff1690565b6040519586936020850190612f4e565b614317611020565b90614320613dd9565b9160408051809460209384830161433691612f4e565b61433f91612f4e565b0392601f1993848101865261435490866106f2565b805161435f90613c5d565b948551614372906001600160401b031690565b61437b90613f00565b90835191829186830161438d91612f4e565b61439691612f4e565b0385810182526143a690826106f2565b82519586918583016143b791612f4e565b6143c091612f4e565b0384810186526143d090866106f2565b6143d8613df0565b9482519586918583016143ea91612f4e565b6143f391612f4e565b03848101865261440390866106f2565b8281015161441090613c21565b94825195869185830161442291612f4e565b61442b91612f4e565b03848101865261443b90866106f2565b614443613ec7565b94825195869185830161445591612f4e565b61445e91612f4e565b03848101865261446e90866106f2565b8181015161447b90613c21565b94825195869185830161448d91612f4e565b61449691612f4e565b0384810186526144a690866106f2565b6144ae613e03565b9482519586918583016144c091612f4e565b6144c991612f4e565b0384810186526144d990866106f2565b60608101516144ed9063ffffffff16613f00565b9482519586918583016144ff91612f4e565b61450891612f4e565b03848101865261451890866106f2565b614520613eda565b94825195869185830161453291612f4e565b61453b91612f4e565b03848101865261454b90866106f2565b608081015161455990613c21565b94825195869185830161456b91612f4e565b61457491612f4e565b03848101865261458490866106f2565b61458c613e29565b94825195869185830161459e91612f4e565b6145a791612f4e565b0384810186526145b790866106f2565b60a08101516001600160401b03166145ce90613f00565b9482519586918583016145e091612f4e565b6145e991612f4e565b0384810186526145f990866106f2565b614601613eed565b94825195869185830161461391612f4e565b61461c91612f4e565b03848101865261462c90866106f2565b60c0015161463990613c21565b90519384928301613db391612f4e565b63ffffffff9190828216838216111561466157031690565b5050505f90565b5f8061042e93602081519101845af43d156146a4573d916146888361072d565b9261469660405194856106f2565b83523d5f602085013e6146a8565b6060915b906146cf57508051156146bd57602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614702575b6146e0575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156146d856fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220f5b6d4a44ad194936330b5fb973c2a2aea0de6de3f7d1ea8e8573cef058ba4d864736f6c63430008190033", + "deployedBytecode": "0x60806040526004361015610011575f80fd5b5f3560e01c806302ddde7b1461028457806304676e381461027f5780630d6f1e9d1461027a57806316a91b741461027557806327e935a11461027057806347453a2a1461026b5780634b3254fb146102665780634ed891d0146102615780634f1ef2861461025c5780634fca79cb1461025757806352d1902d14610252578063621fbd591461024d5780636744c79f146102485780637a9e5e4b146102435780638cbc18391461023e5780638fb360371461023957806393ec06be1461023457806395fabcf01461022f578063a3ad9cc31461022a578063a593aec214610225578063a9e50c1014610220578063ad3cb1cc1461021b578063b632ab5714610216578063b8f8d7e514610211578063bf7e214f1461020c578063c16a8a5614610207578063c4d66de814610202578063c8c14d17146101fd578063c94c93e7146101f8578063cde78421146101f3578063d5b5272a146101ee578063de152649146101e9578063e0064900146101e4578063e0c14aa8146101df578063e0c1c0b4146101da578063e2fc04ae146101d5578063e36f9a07146101d0578063e44efb5a146101cb5763f5786b4b146101c6575f80fd5b61193a565b61191b565b6118fe565b6117e1565b611515565b6113e9565b6113ac565b61138f565b61129a565b611272565b611243565b611227565b61110d565b6110ea565b6110b6565b611093565b611077565b611032565b610ffa565b610f00565b610c12565b610bf5565b610bd9565b610b89565b610ac2565b610a48565b6109d4565b6109ae565b610945565b610899565b61078e565b610628565b6105c8565b61056e565b610511565b6104ed565b610431565b610393565b610297565b5f91031261029357565b5f80fd5b34610293575f366003190112610293575f8051602061474c833981519152805460ff8160401c16908115610363575b50610351575f8051602061474c833981519152805467ffffffffffffffff19166002179055805460ff60401b1916600160401b179055610304611f66565b5f8051602061474c833981519152805460ff60401b19169055604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b60405163f92ee8a960e01b8152600490fd5b600291506001600160401b031610155f6102c6565b63ffffffff81160361029357565b359061039182610378565b565b346102935760203660031901126102935760606004356103b281610378565b63ffffffff8091165f52600960205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60409063ffffffff61042e949316815281602082015201906103ed565b90565b34610293575f3660031901126102935761045d6104546104105463ffffffff1690565b63ffffffff1690565b61046681611fe8565b90610479610473826105f8565b60051c90565b5f5b8181106104d5575050601f811690816104b2575b8261049f600c5463ffffffff1690565b6104ae60405192839283610411565b0390f35b81602091038301019060015f199160200360031b1b011981511690525f8061048f565b806001916010015460208260051b870101520161047b565b34610293575f36600319011261029357602063ffffffff6104105416604051908152f35b346102935760203660031901126102935760043561052f3633612d6b565b8015610548576004541561053f57005b6004555f600355005b604051636ad43cc360e11b8152600490fd5b634e487b7160e01b5f52603260045260245ffd5b34610293576020366003190112610293576004356006548110156102935760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01546040516001600160a01b039091168152602090f35b34610293575f3660031901126102935760206040516104008152f35b634e487b7160e01b5f52601160045260245ffd5b90601f820180921161060657565b6105e4565b9061010091820180921161060657565b9190820180921161060657565b34610293575f3660031901126102935760206040516101188152f35b600435906001600160a01b038216820361029357565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761068957604052565b61065a565b602081019081106001600160401b0382111761068957604052565b604081019081106001600160401b0382111761068957604052565b60e081019081106001600160401b0382111761068957604052565b6001600160401b03811161068957604052565b90601f801991011681019081106001600160401b0382111761068957604052565b60405190610391826106c4565b604051906103918261066e565b6001600160401b03811161068957601f01601f191660200190565b81601f820112156102935780359061075f8261072d565b9261076d60405194856106f2565b8284526020838301011161029357815f926020809301838601378301015290565b6040366003190112610293576107a2610644565b6024356001600160401b038111610293576107c1903690600401610748565b906107ca613bc8565b6107d43633612d6b565b6107dc613bc8565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610868575b5061082e57604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f8051602061472c833981519152830361084f5761084d92506141b2565b005b604051632a87526960e21b815260048101849052602490fd5b61088b91945060203d602011610892575b61088381836106f2565b8101906128a0565b925f610804565b503d610879565b34610293576020366003190112610293576104ae6004356108b981610378565b60405f8180516108c88161066e565b828152826020820152015263ffffffff8092165f5260096020526001815f206001600160401b038351946108fb8661066e565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610293575f366003190112610293577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316300361099c5760206040515f8051602061472c8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610293575f3660031901126102935760205f54604051908152f35b8015150361029357565b34610293576020366003190112610293576004356109f1816109ca565b600f546001600160a01b031633819003610a2a57507fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755d005b60449060405190636e01d1ef60e11b82523360048301526024820152fd5b3461029357602036600319011261029357610a61610644565b5f8051602061470c833981519152546001600160a01b03919082163303610aab57803b15610a925761084d90612e87565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b3461029357604036600319011261029357602435600435610ae33633612d6b565b63ffffffff6104105416610b445781811080610b38575b610b039061201a565b818110610b0c57005b80610b32610b1b600193612066565b81549060031b906001821b915f19901b1916179055565b01610b03565b50610400821115610afa565b60405162461bcd60e51b815260206004820152601f60248201527f4f50503a206f7574626f756e642062616e6b20616c7265616479206c697665006044820152606490fd5b34610293575f366003190112610293575f8051602061470c8339815191525460a01c60ff1615610bd1576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610bbe565b34610293575f3660031901126102935760206040516180008152f35b34610293575f366003190112610293576020600354604051908152f35b3461029357602036600319011261029357600435610c2f81610378565b610c393633612d6b565b600c5463ffffffff16610c4e61045482612078565b63ffffffff831603610ed15750610c636120c7565b63ffffffff82166060820152610c94610c846001600160401b034216612104565b6001600160401b03166040830152565b610c9c612edf565b60a08201525f600854908115159182610d3e575b7fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d88761034c86610d32610ce188612f60565b91610cf161800084511115612793565b610d1383516020850120610d058184613323565b610d0e83613389565b600d55565b610d1c836133e1565b63ffffffff1663ffffffff19600c541617600c55565b60405191829182612805565b5f92836101005b838210610e70575b5050610d639083159081610e68575b501561212e565b81610d6d81612209565b60c086019081525f5b828110610e30575050610d889161226c565b905f5b828110610e0257505f5b818110610df1575050610ce161034c93610de7610de27fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d8879795610dda610d3296600855565b600e5461226c565b600e55565b9350829450610cb0565b600190610dfc6126c1565b01610d95565b80610e2a610e1a610e156001948661061b565b611caa565b50610e2483611caa565b90612552565b01610d8b565b6001919250610e5e81610e4281611caa565b50610e4e865191611e88565b610e588383612258565b52612258565b5001908391610d76565b90505f610d5c565b610e8d610e88610e8284979894611caa565b50611e88565b612c9e565b90618000610e9b838361061b565b11610ec757610eb982610eb3600194610ebf9461061b565b9461061b565b96612120565b940190610d45565b5081959450610d4d565b610eda90612078565b6040516307d3e83d60e51b815263ffffffff918216600482015291166024820152604490fd5b3461029357602036600319011261029357600435610f1d81610378565b610f273633612d6b565b63ffffffff9081811691825f52600960205260405f20541615610fb557610f84610f8f91610f70610f68610454610f63600c5463ffffffff1690565b613432565b851115612816565b63ffffffff165f52600960205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b60405162461bcd60e51b815260206004820152601760248201527f4f50503a207265636f7264206e6f742070726573656e740000000000000000006044820152606490fd5b34610293575f3660031901126102935760206001600160401b0360015416604051908152f35b6040519061102d8261068e565b5f8252565b34610293575f366003190112610293576104ae604051611051816106a9565b60058152640352e302e360dc1b60208201526040519182916020835260208301906103ed565b34610293575f3660031901126102935760206040516120008152f35b34610293575f36600319011261029357602063ffffffff600c5416604051908152f35b34610293575f366003190112610293575f8051602061470c833981519152546040516001600160a01b039091168152602090f35b34610293575f36600319011261029357602063ffffffff600a5416604051908152f35b3461029357602036600319011261029357611126610644565b5f8051602061474c83398151915254906001600160401b0360ff8360401c161592168015908161121f575b6001149081611215575b15908161120c575b50610351575f8051602061474c833981519152805467ffffffffffffffff1916600117905561119690826111e857613445565b61119c57005b5f8051602061474c833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290806020810161034c565b5f8051602061474c833981519152805460ff60401b1916600160401b179055613445565b9050155f611163565b303b15915061115b565b839150611151565b34610293575f3660031901126102935760206040516101008152f35b34610293576020366003190112610293576004355f526005602052602060ff60405f2054166040519015158152f35b34610293575f36600319011261029357600f546040516001600160a01b039091168152602090f35b34610293576020366003190112610293576004356112b781610378565b6112c13633612d6b565b604051906112ce8261068e565b63ffffffff81168083521561134a577f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69161132a611311600a5463ffffffff1690565b9163ffffffff90511663ffffffff19600a541617600a55565b6040805163ffffffff92831681529290911660208301528190810161034c565b60405162461bcd60e51b815260206004820152601d60248201527f4f50503a20696e76616c696420726574656e74696f6e20636f6e6669670000006044820152606490fd5b34610293575f366003190112610293576020600e54604051908152f35b34610293575f366003190112610293576020600854604051908152f35b6004359061ffff8216820361029357565b359061ffff8216820361029357565b3461029357604080600319360112610293576114036113c9565b6024356001600160401b03811161029357611422903690600401610748565b9160045415611505576114353633612d6b565b5f8051602061470c8339815191525461146490611458906001600160a01b031681565b6001600160a01b031690565b81516368fc2b7760e11b815261ffff841662010000176004820152336024820152908290829060449082905afa908115611500575f916114d2575b50156114af5761084d8383613669565b51639a28d60760e01b815233600482015261ffff91909116602482015260449150fd5b6114f29150823d84116114f9575b6114ea81836106f2565b810190612870565b505f61149f565b503d6114e0565b612895565b5163738c481160e01b8152600490fd5b346102935760203660031901126102935761152e610644565b6115383633612d6b565b600f80546001600160a01b0319166001600160a01b0392909216919091179055005b919082604091031261029357604051611572816106a9565b8092803560ff811681036102935782526020908101359161159283610378565b0152565b91906080838203126102935760206115ca604051926115b4846106a9565b604084966115c2838261155a565b86520161155a565b910152565b35906001600160401b038216820361029357565b6001600160401b0381116106895760051b60200190565b9190916040908181850312610293578151611614816106a9565b8094823561162181610378565b8252602092838101356001600160401b039182821161029357019180601f8401121561029357823590611653826115e3565b96611660815198896106f2565b828852868089019360051b8601019482861161029357878101935b86851061168d57505050505050500152565b84358681116102935782019060609081601f198488030112610293578451916116b58361066e565b6116c08c85016113da565b8352858401356116cf81610378565b8c84015283013591888311610293576116ef878d80969581960101610748565b8682015281520194019361167b565b90604060206103919361172584825163ffffffff6020809260ff8151168552015116910152565b015191019063ffffffff6020809260ff8151168552015116910152565b9061042e91602081526117596020820183516116fe565b602082015190611777610140928360a08401526101608301906103ed565b9160c06117bd611798604087015195601f19968787830301858801526103ed565b606087015163ffffffff1660e0860152608087015186868303016101008701526103ed565b60a08601516001600160401b031661012085015294015192828503019101526103ed565b346102935760031960403682011261029357600480356001600160401b0392838211610293576101409082360301126102935761181c610713565b9061182936828501611596565b825260848101358481116102935761184690843691840101610748565b602083015260a48101358481116102935761186690843691840101610748565b604083015261187760c48201610386565b606083015260e48101358481116102935761189790843691840101610748565b60808301526118a961010482016115cf565b60a08301526101248101359084821161029357836118ca9236920101610748565b60c0820152602435928311610293576118ec6118f2926104ae943691016115fa565b906129e1565b60405191829182611742565b34610293575f366003190112610293576020600454604051908152f35b34610293575f3660031901126102935760206004541515604051908152f35b3461029357602080600319360112610293576004908135825415611c99576119623633612d6b565b8015611c885782541461197157005b5f82556003548015611c7757611985612191565b5f5480611c0b5750611995611f9e565b60409060408301526119c26119b26001600160401b034216612104565b6001600160401b031660a0840152565b6119ca6121ce565b600181529283916119da8261380b565b8684019081525f5b838110611b8f57505050506119f6916129e1565b91611a188180850151604051809381926347f5343160e01b8352898301612805565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91611b72575b505f55611a75611a5960a08501516001600160401b031690565b6001600160401b03166001600160401b03196001541617600155565b611a7d6121e7565b928352820152611a8c81612c9e565b917fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755c611b2457611abf83600e5461061b565b906120008211611aff575050611af691611adb610de2926139e5565b611aee611ae9600854612120565b600855565b600e5461061b565b61084d5f600355565b604080516306f8bcbb60e51b8152918201928352612000602084015290918291010390fd5b618000611b308461060b565b11611b465750611af691611adb610de2926139e5565b611b4f8361060b565b6040805163048c985b60e51b815292830191825261800060208301528291010390fd5b611b899150823d84116108925761088381836106f2565b5f611a3f565b60019192939450611bff81611bac611ba68261349f565b5061386f565b8a611bb9825161ffff1690565b910151611bee611bcd825163ffffffff1690565b611be2611bd8610720565b61ffff9095168552565b63ffffffff16838e0152565b87820152855190610e588383612258565b500190859392916119e2565b604051630c9b3d8760e21b8152858101918252905f9082908190602001038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91611c55575b50611995565b611c7191503d805f833e611c6981836106f2565b81019061295a565b5f611c4f565b60405163783bc0f160e11b81528390fd5b604051636ad43cc360e11b81528390fd5b60405163738c481160e01b81528390fd5b600754811015611ce357600a9060075f52027fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801905f90565b61055a565b90604051611cf5816106a9565b915460ff8116835260081c63ffffffff166020830152565b90600182811c92168015611d3b575b6020831014611d2757565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611d1c565b9060405191825f8254611d5781611d0d565b908184526020946001916001811690815f14611dc35750600114611d85575b505050610391925003836106f2565b5f90815285812095935091905b818310611dab57505061039193508201015f8080611d76565b85548884018501529485019487945091830191611d92565b9250505061039194925060ff191682840152151560051b8201015f8080611d76565b9060408051611df3816106a9565b809363ffffffff8082541683526001809201805491611e11836115e3565b95611e1f60405197886106f2565b83875260209160208801935f5260205f20915f945b868610611e4957505050505050505060200152565b60028589928451611e598161066e565b86885461ffff8116835260101c1683820152611e76858901611d45565b86820152815201940195019492611e34565b90604051611e95816106a9565b60206115ca60088395604051611eaa816106c4565b604051611eb6816106a9565b611ebf83611ce8565b8152611ecd60018401611ce8565b868201528152611edf60028301611d45565b85820152611eef60038301611d45565b6040820152611f15611f08600484015463ffffffff1690565b63ffffffff166060830152565b611f2160058301611d45565b6080820152611f4d611f3d60068401546001600160401b031690565b6001600160401b031660a0830152565b611f5960078301611d45565b60c0820152855201611de5565b5f806008545b808210611f7a575050600e55565b9091611f8b610e88610e8285611caa565b8101809111610606579160010190611f6c565b604051611faa8161068e565b5f8152905f368137565b60405190611fc1826106a9565b6001825260203681840137565b60405190611fdb826106a9565b600a825260203681840137565b90611ff28261072d565b611fff60405191826106f2565b8281528092612010601f199161072d565b0190602036910137565b1561202157565b60405162461bcd60e51b815260206004820152601a60248201527f4f50503a20696e76616c6964207072657761726d2072616e67650000000000006044820152606490fd5b610400811015611ce357601001905f90565b90600163ffffffff8093160191821161060657565b6040519061209a826106a9565b5f6020838281520152565b604051906120b2826106a9565b816120bb61208d565b815260206115ca61208d565b604051906120d4826106c4565b606060c0838281526120e46120a5565b60208201525f60408201525f838201525f60808201528260a08201520152565b906103e86001600160401b038093160291821691820361060657565b5f1981146106065760010190565b1561213557565b60405162461bcd60e51b815260206004820152602e60248201527f4f50503a20717565756564206d6573736167652065786365656473204d41585f60448201526d454e56454c4f50455f425954455360901b6064820152608490fd5b6040519061219e826106c4565b606060c0836121ab6120a5565b81528260208201528260408201525f838201528260808201525f60a08201520152565b604051906121db826106a9565b60606020835f81520152565b604051906121f4826106a9565b816121fd612191565b815260206115ca6121ce565b90612213826115e3565b61222060405191826106f2565b8281528092612231601f19916115e3565b01905f5b82811061224157505050565b60209061224c6121e7565b82828501015201612235565b8051821015611ce35760209160051b010190565b9190820391821161060657565b634e487b7160e01b5f525f60045260245ffd5b818110612297575050565b5f815560010161228c565b9190601f81116122b157505050565b610391925f5260205f20906020601f840160051c830193106122db575b601f0160051c019061228c565b90915081906122ce565b9190918281146123cc576122f98354611d0d565b6001600160401b0381116106895761231b816123158454611d0d565b846122a2565b5f93601f821160011461235a5761234b92939482915f9261234f575b50508160011b915f199060031b1c19161790565b9055565b015490505f80612337565b61236c601f198316915f5260205f2090565b9461237a845f5260205f2090565b915f5b8181106123b45750958360019596971061239c575b505050811b019055565b01545f1960f88460031b161c191690555f8080612392565b8783015484556001938401939092019160200161237d565b509050565b6123db8154611d0d565b90816123e5575050565b81601f5f93116001146123f6575055565b908083918252612415601f60208420940160051c84016001850161228c565b5555565b600160401b821161068957805482825580831061243557505050565b6001916001600160ff1b0382811683036106065784168403610606575f5260205f209060011b81019260011b015b82811061246f57505050565b805f600292556124808382016123d1565b01612463565b8181146125135781549161249a8383612419565b5f5260205f20905f5260205f20905f905b8382106124b85750505050565b8060019184036124d3575b60028091019301910190916124ab565b8054845461ffff191661ffff919091161784558054845465ffffffff0000191665ffffffff000090911617845561250e8183018584016122e5565b6124c3565b5050565b818103612522575050565b6001808361254a63ffffffff610391965416859063ffffffff1663ffffffff19825416179055565b019101612486565b906126bc5781811415918261256657505050565b60088091846103919561257d575b50019101612517565b8061262d575b5061259460028201600286016122e5565b6125a460038201600386016122e5565b6125d16125b8600483015463ffffffff1690565b600486019063ffffffff1663ffffffff19825416179055565b6125e160058201600586016122e5565b6126176125f860068301546001600160401b031690565b60068601906001600160401b03166001600160401b0319825416179055565b61262760078201600786016122e5565b5f612574565b61268a575b600184016001820190818103612649575b50612583565b8154815460ff191660ff919091161781556126839163ffffffff9054851c1664ffffffff0082549160081b169064ffffffff001916179055565b5f80612643565b8054845460ff191660ff919091161784558054845464ffffffff00191690831c60081b64ffffffff0016178455612632565b612279565b600754801561277f575f19016126d681611caa565b6126bc575f815560015f600183015560096002926126f6600282016123d1565b612702600382016123d1565b5f6004820155612714600582016123d1565b5f6006820155612726600782016123d1565b5f600882015501908154915f815582612743575b50505050600755565b6001600160ff1b0383168303610606575f5260205f209160011b8201915b8281101561273a57805f8592556127798382016123d1565b01612761565b634e487b7160e01b5f52603160045260245ffd5b1561279a57565b60405162461bcd60e51b815260206004820152603a60248201527f4f50503a20656e76656c6f70652065786365656473204d41585f454e56454c4f60448201527f50455f42595445532028657374696d61746f72206472696674290000000000006064820152608490fd5b90602061042e9281815201906103ed565b1561281d57565b60405162461bcd60e51b815260206004820152602560248201527f4f50503a207265636f7264207374696c6c20696e20726574656e74696f6e2077604482015264696e646f7760d81b6064820152608490fd5b91908260409103126102935760208251612889816109ca565b92015161042e81610378565b6040513d5f823e3d90fd5b90816020910312610293575190565b90602080835260609060608401908063ffffffff94858151168288015201519460408080830152865180945260808201908360808660051b8501019801955f935b8685106129035750505050505050505090565b909192939495969799988680612945600193868f89908f607f198a8603018d52519061ffff8251168552868201511686850152015191818a82015201906103ed565b9b9c9a019897969190910194019291906128f0565b602081830312610293578051906001600160401b038211610293570181601f820112156102935780519061298d8261072d565b9261299b60405194856106f2565b8284526020838301011161029357815f9260208093018386015e8301015290565b90916129d361042e936040845260408401906103ed565b9160208184039101526103ed565b906129ea612191565b506040805163765809ab60e11b81526004926020929173__$b91d2ad766fd07dc99f56c1a0b020615bb$__9190848180612a26858a83016128af565b0381865af4908115611500575f91612c81575b5060808701908582518051155f14612bfd5750508451630c9b3d8760e21b8152878101918252905f90829081906020010381875af492831561150057612aa693612a9993612a8f935f92612be1575b5052613701565b5163ffffffff1690565b63ffffffff166060870152565b815162409a0960e81b81525f8180612ac089898301611742565b0381855af4908115611500575f91612bc7575b508584018051805180612b8c57505052815163b93612a760e01b815293838580612aff89858301611742565b0381855af492831561150057612b38955f958695612b6d575b505051630c9b3d8760e21b81529081019283529384928391829160200190565b03915af4908115611500575f91612b53575b5060c082015290565b612b6791503d805f833e611c6981836106f2565b5f612b4a565b612b84929550803d106108925761088381836106f2565b925f80612b18565b915093925084849695929601209085519086012003612bac575050505090565b51630ca2e06d60e11b815292839261082a92919084016129bc565b612bdb91503d805f833e611c6981836106f2565b5f612ad3565b612bf69192503d805f833e611c6981836106f2565b905f612a88565b90919350612c1e92508551809381926347f5343160e01b83528a8301612805565b0381865af4908115611500575f91612c64575b50818103612c40575050612aa6565b8351631d97afef60e31b815280870192835260208301919091529081906040010390fd5b612c7b9150853d87116108925761088381836106f2565b5f612c31565b612c989150853d87116108925761088381836106f2565b5f612a39565b61010090602080910190602082510151515f925b818410612cc0575050505090565b90919293601880820191828111610606576040612ce1888887510151612258565b5101515101018091116106065793600101929190612cb2565b600411610293575f90600490565b6001600160e01b03199035818116939260048110612d2557505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f8051602061470c8339815191528054612da3906001600160a01b0316612d9a612d9486612cfa565b90612d08565b90843091613b47565b9015612db0575b50505050565b63ffffffff1615612e67575f8051602061470c833981519152805460ff60a01b1916600160a01b179055612df190611458908190546001600160a01b031690565b91823b1561029357612e1c925f9283604051809681958294634a63ebf760e11b845260048401612d34565b03925af1801561150057612e4e575b505f8051602061470c833981519152805460ff60a01b191690555f808080612daa565b80612e5b612e61926106df565b80610289565b5f612e2b565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f8051602061470c83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b600d5480612efa5750604051612ef48161068e565b5f815290565b60405190630c9b3d8760e21b825260048201525f8160248173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91612f3a575090565b61042e91503d805f833e611c6981836106f2565b805191908290602001825e015f815290565b612f68611020565b612f70613dd9565b92604080518095602094858301612f8691612f4e565b612f8f91612f4e565b0394601f19958681018252612fa490826106f2565b8451612faf90613c21565b908251918291868301612fc191612f4e565b612fca91612f4e565b038681018252612fda90826106f2565b612fe2613df0565b908251918291868301612ff491612f4e565b612ffd91612f4e565b03868101825261300d90826106f2565b8385015161301a90613c5d565b90815161302d906001600160401b031690565b61303690613f00565b90835191829187830161304891612f4e565b61305191612f4e565b03878101825261306190826106f2565b825191829186830161307291612f4e565b61307b91612f4e565b03868101825261308b90826106f2565b613093613e03565b9082519182918683016130a591612f4e565b6130ae91612f4e565b0386810182526130be90826106f2565b848201516001600160401b03166130d490613f00565b9082519182918683016130e691612f4e565b6130ef91612f4e565b0386810182526130ff90826106f2565b613107613e16565b90825191829186830161311991612f4e565b61312291612f4e565b03868101825261313290826106f2565b60608501516131469063ffffffff16613f00565b90825191829186830161315891612f4e565b61316191612f4e565b03868101825261317190826106f2565b613179613e29565b90825191829186830161318b91612f4e565b61319491612f4e565b0386810182526131a490826106f2565b60808501516131b89063ffffffff16613f00565b9082519182918683016131ca91612f4e565b6131d391612f4e565b0386810182526131e390826106f2565b6131eb613e3c565b9082519182918683016131fd91612f4e565b61320691612f4e565b03868101825261321690826106f2565b60a085015161322490613c21565b90825191829186830161323691612f4e565b61323f91612f4e565b03868101825261324f90826106f2565b915f925b60c086018051518510156133195761329961330f60c0936133036132b76132b18a6001978f6132ab908f61329f8f61329961328c613e81565b91519b8c94850190612f4e565b90612f4e565b039081018852876106f2565b51612258565b51613f7f565b6132f58d8c6132e98c6132996132dc6132d788516001600160401b031690565b613f00565b9151988994850190612f4e565b039081018552846106f2565b88519485938c850190612f4e565b038a81018352826106f2565b9401939050613253565b5095509350505050565b63ffffffff8082165f52600960205260405f20908154166133845761335a600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b6133a09063ffffffff91829182600a541690614649565b169081151590816133c9575b506133b45750565b5f5260096020525f6001604082208281550155565b9050815f52600960205260405f20541615155f6133ac565b805190601f8201908183116106065760059160051c915f5b83811061341c575050505063ffffffff610410911663ffffffff19825416179055565b80821b83016020015160108201556001016133f9565b61042e9063ffffffff600a541690614649565b61346e9061345161407f565b61345961407f565b61346161407f565b61346961407f565b612e87565b61347661407f565b61347e61407f565b60c860405161348c8161068e565b5260c863ffffffff19600a541617600a55565b600254811015611ce35760025f5260011b7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01905f90565b91909182516001600160401b038111610689576134f8816123158454611d0d565b602080601f83116001146135345750819061234b9394955f926135295750508160011b915f199060031b1c19161790565b015190505f80612337565b90601f19831695613548855f5260205f2090565b925f905b8882106135825750508360019596971061356a57505050811b019055565b01515f1960f88460031b161c191690555f8080612392565b8060018596829496860151815501950193019061354c565b9291906126bc578051835461ffff191661ffff91909116178355600180930190602080910151938451916001600160401b038311610689576135e6836135e08654611d0d565b866122a2565b602091601f8411600114613618575050819061234b9394955f926135295750508160011b915f199060031b1c19161790565b95601f9291921984169661362f865f5260205f2090565b935f915b898310613652575050508360019596971061356a57505050811b019055565b838501518655948501949381019391810191613633565b600254916003548084115f146136be575061039192506136976040519261368f846106a9565b61ffff168352565b60208201526136b86003546136b36136ae82612120565b600355565b61349f565b9061359a565b6136c790612120565b60035561ffff604051926136da846106a9565b1682526020820152600160401b821015610689576136b8826001610391940160025561349f565b6137349161370d611020565b90613716613ea1565b61329961377a60409260405197889161329960209889850190612f4e565b0396613748601f19988981018352826106f2565b61376e61375f6132d76104548a5163ffffffff1690565b60405194859389850190612f4e565b038781018352826106f2565b915f925b8486018051518510156133195761329961380161329961330389956137f38d6137cd6137c78d60019a6132ab8f6137bb8f9161329961328c613df0565b038681018952886106f2565b516140ad565b926132e96137e56132d786516001600160401b031690565b8d519788938d850190612f4e565b885194859388850190612f4e565b940193905061377e565b90613815826115e3565b60409061382560405191826106f2565b8381528093613836601f19916115e3565b01915f5b8381106138475750505050565b60209082516138558161066e565b5f8152825f8183015260608583015282860101520161383a565b9060405161387c816106a9565b60206115ca6001839561ffff815416855201611d45565b8151815463ffffffff191663ffffffff91821617825591600180920160208092015191808351936138c48585612419565b01915f52805f205f925b8484106138de5750505050505050565b8051805183548583015161ffff90921665ffffffffffff199091161790891660101b65ffffffff0000161783556040015180519187840191906001600160401b03841161068957828692613937866123158d9754611d0d565b8390601f871160011461397757958061396892600297985f926135295750508160011b915f199060031b1c19161790565b90555b019201930192906138ce565b9590601f1982169661398c845f5260205f2090565b975f5b8181106139cc575091879891846002999594106139b4575b505050811b01905561396b565b01515f1960f88460031b161c191690555f80806139a7565b838301518a5597909801978d978b97938401930161398f565b600754600160401b81101561068957806001613a049201600755611caa565b6126bc576008602083613b3e60c06103919651613a9e815186613a608251613a3a60ff8251168d9060ff1660ff19825416179055565b909101518a5464ffffffff00191660089190911b64ffffffff0016178a5563ffffffff90565b878060018c01930151613a8160ff825116859060ff1660ff19825416179055565b0151825464ffffffff001916911660081b64ffffffff0016179055565b613aae85820151600289016134d7565b613abf6040820151600389016134d7565b613aec613ad3606083015163ffffffff1690565b600489019063ffffffff1663ffffffff19825416179055565b613afd6080820151600589016134d7565b613b33613b1460a08301516001600160401b031690565b60068901906001600160401b03166001600160401b0319825416179055565b0151600786016134d7565b01519101613893565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b03841117610689576040935f9385528380528360205251915afa613bb657565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613c06575b505061099c57565b5f8051602061472c8339815191525416141590505f80613bfe565b61042e61329991613c4f613c3e6001600160401b03835116613f00565b916040519485936020850190612f4e565b03601f1981018352826106f2565b613c65611020565b90613c6e613dd9565b6040805193846020938492838301613c8591612f4e565b613c8e91612f4e565b0393601f19948581018752613ca390876106f2565b8051613cae90614257565b958651613cc1906001600160401b031690565b613cca90613f00565b908451918291858301613cdc91612f4e565b613ce591612f4e565b038681018252613cf590826106f2565b8351968791848301613d0691612f4e565b613d0f91612f4e565b038581018752613d1f90876106f2565b613d27613df0565b958351968791848301613d3991612f4e565b613d4291612f4e565b038581018752613d5290876106f2565b0151613d5d90614257565b908151613d70906001600160401b031690565b613d7990613f00565b948151958691858301613d8b91612f4e565b613d9491612f4e565b038481018652613da490866106f2565b519384928301613db391612f4e565b613dbc91612f4e565b03908101825261042e90826106f2565b805115611ce35760200190565b613de1611fb4565b600a613dec82613dcc565b5390565b613df8611fb4565b6012613dec82613dcc565b613e0b611fb4565b6028613dec82613dcc565b613e1e611fb4565b6030613dec82613dcc565b613e31611fb4565b6038613dec82613dcc565b613e44611fce565b60a25f5b607f80831115613e6d5790608060019284161760208286010153019060071c90613e48565b50607f600192166020828501015301815290565b613e89611fce565b61014260015f60c26020850153019060071c90613e48565b613ea9611fb4565b6008613dec82613dcc565b613ebc611fb4565b6010613dec82613dcc565b613ecf611fb4565b601a613dec82613dcc565b613ee2611fb4565b6032613dec82613dcc565b613ef5611fb4565b6042613dec82613dcc565b9060809160806001600160401b03821610613f5b57613f1d611fce565b5f5b607f80841115613f4357908560019285161760208285010153019160071c91613f1f565b5090929350607f600192166020828501015301815290565b909150613f66611fb4565b9060f81b6001600160f81b0319165f1a613dec82613dcc565b613f87611020565b90613f90613dd9565b6040805193846020938492838301613fa791612f4e565b613fb091612f4e565b0393601f19948581018752613fc590876106f2565b8051613fd09061430f565b958651613fe3906001600160401b031690565b613fec90613f00565b908451918291858301613ffe91612f4e565b61400791612f4e565b03868101825261401790826106f2565b835196879184830161402891612f4e565b61403191612f4e565b03858101875261404190876106f2565b614049613df0565b95835196879184830161405b91612f4e565b61406491612f4e565b03858101875261407490876106f2565b0151613d5d90613701565b60ff5f8051602061474c8339815191525460401c161561409b57565b604051631afcd79f60e31b8152600490fd5b61042e6140ee613299926141a66141926132996141996140cb611020565b946140d4613ea1565b9060409485928351998a9161329960209b8c850190612f4e565b0398614102601f199a8b81018352826106f2565b614141896141356141256132d761411e61411e895161ffff1690565b61ffff1690565b6132998851958694850190612f4e565b038b81018352826106f2565b61415089614135614125613eb4565b61416d896141356141256132d76104548489015163ffffffff1690565b614186614178613ec7565b85519d8e938c850190612f4e565b038981018c528b6106f2565b0151613c21565b9151968794850190612f4e565b039081018352826106f2565b90813b15614236575f8051602061472c83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561421b5761421891614668565b50565b50503461422457565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b61042e6132996132996141a66142ff6132d76104546020613299986142f46132996142ce614283611020565b61429d61428e613ea1565b6040519d8e9389850190612f4e565b039a6142b1601f199c8d81018352826106f2565b61413561375f6132d76142c86142c8895160ff1690565b60ff1690565b6142e86142d9613eb4565b6040519a8b9387850190612f4e565b038981018952886106f2565b015163ffffffff1690565b6040519586936020850190612f4e565b614317611020565b90614320613dd9565b9160408051809460209384830161433691612f4e565b61433f91612f4e565b0392601f1993848101865261435490866106f2565b805161435f90613c5d565b948551614372906001600160401b031690565b61437b90613f00565b90835191829186830161438d91612f4e565b61439691612f4e565b0385810182526143a690826106f2565b82519586918583016143b791612f4e565b6143c091612f4e565b0384810186526143d090866106f2565b6143d8613df0565b9482519586918583016143ea91612f4e565b6143f391612f4e565b03848101865261440390866106f2565b8281015161441090613c21565b94825195869185830161442291612f4e565b61442b91612f4e565b03848101865261443b90866106f2565b614443613ec7565b94825195869185830161445591612f4e565b61445e91612f4e565b03848101865261446e90866106f2565b8181015161447b90613c21565b94825195869185830161448d91612f4e565b61449691612f4e565b0384810186526144a690866106f2565b6144ae613e03565b9482519586918583016144c091612f4e565b6144c991612f4e565b0384810186526144d990866106f2565b60608101516144ed9063ffffffff16613f00565b9482519586918583016144ff91612f4e565b61450891612f4e565b03848101865261451890866106f2565b614520613eda565b94825195869185830161453291612f4e565b61453b91612f4e565b03848101865261454b90866106f2565b608081015161455990613c21565b94825195869185830161456b91612f4e565b61457491612f4e565b03848101865261458490866106f2565b61458c613e29565b94825195869185830161459e91612f4e565b6145a791612f4e565b0384810186526145b790866106f2565b60a08101516001600160401b03166145ce90613f00565b9482519586918583016145e091612f4e565b6145e991612f4e565b0384810186526145f990866106f2565b614601613eed565b94825195869185830161461391612f4e565b61461c91612f4e565b03848101865261462c90866106f2565b60c0015161463990613c21565b90519384928301613db391612f4e565b63ffffffff9190828216838216111561466157031690565b5050505f90565b5f8061042e93602081519101845af43d156146a4573d916146888361072d565b9261469660405194856106f2565b83523d5f602085013e6146a8565b6060915b906146cf57508051156146bd57602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614702575b6146e0575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156146d856fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220f5b6d4a44ad194936330b5fb973c2a2aea0de6de3f7d1ea8e8573cef058ba4d864736f6c63430008190033", "linkReferences": { "contracts/outpost/OPPCommon.sol": { "OPPCommon": [ { "length": 20, - "start": 6055 + "start": 6889 }, { "length": 20, - "start": 6371 + "start": 7417 }, { "length": 20, - "start": 9925 + "start": 10959 }, { "length": 20, - "start": 11131 + "start": 12257 } ] } @@ -1076,19 +1417,19 @@ "OPPCommon": [ { "length": 20, - "start": 5850 + "start": 6684 }, { "length": 20, - "start": 6166 + "start": 7212 }, { "length": 20, - "start": 9720 + "start": 10754 }, { "length": 20, - "start": 10926 + "start": 12052 } ] } diff --git a/tests/fixtures/ethereum-abi-opp-inbound-current.json b/tests/fixtures/ethereum-abi-opp-inbound-current.json index a2b784677b..7476b2fd11 100644 --- a/tests/fixtures/ethereum-abi-opp-inbound-current.json +++ b/tests/fixtures/ethereum-abi-opp-inbound-current.json @@ -100,84 +100,36 @@ "name": "InvalidInitialization", "type": "error" }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "operator_", - "type": "address" - } - ], - "name": "OPP_ChunkBufferMissing", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint32", - "name": "epochIndex", - "type": "uint32" - } - ], - "name": "OPP_ChunkBufferShapeMismatch", - "type": "error" - }, { "inputs": [ { - "internalType": "uint16", - "name": "chunkIndex", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "totalChunks", - "type": "uint16" + "internalType": "uint256", + "name": "position", + "type": "uint256" } ], - "name": "OPP_ChunkIndexOutOfRange", + "name": "MalformedVarint", "type": "error" }, { - "inputs": [ - { - "internalType": "uint16", - "name": "expected", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "actual", - "type": "uint16" - } - ], - "name": "OPP_ChunkOutOfOrder", + "inputs": [], + "name": "NotInitializing", "type": "error" }, { "inputs": [ { - "internalType": "uint16", - "name": "chunkIndex", - "type": "uint16" - }, - { - "internalType": "uint256", + "internalType": "bytes32", "name": "expected", - "type": "uint256" + "type": "bytes32" }, { - "internalType": "uint256", - "name": "actual", - "type": "uint256" + "internalType": "bytes32", + "name": "found", + "type": "bytes32" } ], - "name": "OPP_ChunkSizeMismatch", + "name": "OPP_DigestMismatch", "type": "error" }, { @@ -228,6 +180,27 @@ "name": "OPP_EpochHashMismatch", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "handler", + "type": "address" + }, + { + "internalType": "AttestationType", + "name": "attestationType", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "gasBound", + "type": "uint256" + } + ], + "name": "OPP_HandlerGasExhausted", + "type": "error" + }, { "inputs": [ { @@ -258,33 +231,12 @@ { "inputs": [ { - "internalType": "uint256", - "name": "required", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "provided", - "type": "uint256" - } - ], - "name": "OPP_InsufficientSignatureWeight", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "expected", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "actual", - "type": "uint16" + "internalType": "string", + "name": "reason", + "type": "string" } ], - "name": "OPP_InvalidChunkCount", + "name": "OPP_InvalidInitialRoster", "type": "error" }, { @@ -308,6 +260,27 @@ "name": "OPP_InvalidRetentionConfig", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "expected", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "actual", + "type": "bytes32" + } + ], + "name": "OPP_MessageChainMismatch", + "type": "error" + }, { "inputs": [ { @@ -329,31 +302,52 @@ "name": "OPP_NoAttestationsSent", "type": "error" }, + { + "inputs": [], + "name": "OPP_NoPendingAttestations", + "type": "error" + }, { "inputs": [ { - "internalType": "address", - "name": "operator_", - "type": "address" + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "messageId", + "type": "bytes" } ], - "name": "OPP_NoChunkSlot", + "name": "OPP_NonCanonicalMessageId", "type": "error" }, { - "inputs": [], - "name": "OPP_NoPendingAttestations", + "inputs": [ + { + "internalType": "bytes", + "name": "previousEnvelopeHash", + "type": "bytes" + } + ], + "name": "OPP_NonCanonicalPreviousEpochHash", "type": "error" }, { "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, { "internalType": "bytes", - "name": "previousEnvelopeHash", + "name": "previousMessageId", "type": "bytes" } ], - "name": "OPP_NonCanonicalPreviousEpochHash", + "name": "OPP_NonCanonicalPreviousMessageId", "type": "error" }, { @@ -372,6 +366,27 @@ "name": "OPP_NonSequentialEpoch", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "expected", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "actual", + "type": "uint256" + } + ], + "name": "OPP_NonSequentialMessage", + "type": "error" + }, { "inputs": [ { @@ -409,6 +424,22 @@ "name": "OPP_OperatorAlreadyDelivered", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "projectedEnvelopeBytes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxEnvelopeBytes", + "type": "uint256" + } + ], + "name": "OPP_OutboundMessageUnpackable", + "type": "error" + }, { "inputs": [ { @@ -468,6 +499,32 @@ "name": "OPP_UnauthorizedAttestationType", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "epochIndex", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "rosterEpochIndex", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "activeGroupIndex", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "groupCount", + "type": "uint32" + } + ], + "name": "OPP_UnmappedEpochGroup", + "type": "error" + }, { "inputs": [ { @@ -571,6 +628,43 @@ "name": "AttestationDelivered", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "handler", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "messageID", + "type": "bytes" + }, + { + "indexed": false, + "internalType": "AttestationType", + "name": "attestationType", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "sequenceNumber", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasConsumed", + "type": "uint256" + } + ], + "name": "AttestationFailed", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -638,19 +732,25 @@ "anonymous": false, "inputs": [ { - "indexed": true, + "indexed": false, "internalType": "uint32", "name": "epochIndex", "type": "uint32" }, { - "indexed": true, - "internalType": "address", - "name": "operator_", - "type": "address" + "indexed": false, + "internalType": "uint32", + "name": "attestedEpochIndex", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "enum OPPInboundLib.GroupsRejection", + "name": "rejection", + "type": "uint8" } ], - "name": "EnvelopeChunksDiscarded", + "name": "BatchOperatorGroupsRejected", "type": "event" }, { @@ -685,37 +785,6 @@ "name": "EnvelopeRetentionConfigUpdated", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint32", - "name": "epochIndex", - "type": "uint32" - }, - { - "indexed": true, - "internalType": "address", - "name": "operator_", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint16", - "name": "chunkIndex", - "type": "uint16" - }, - { - "indexed": false, - "internalType": "uint16", - "name": "totalChunks", - "type": "uint16" - } - ], - "name": "EpochChunk", - "type": "event" - }, { "anonymous": false, "inputs": [ @@ -779,6 +848,25 @@ "name": "EpochDelivery", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint32", + "name": "epochIndex", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "dispatched", + "type": "uint16" + } + ], + "name": "EpochDispatchProgressed", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -809,101 +897,68 @@ "inputs": [ { "indexed": false, - "internalType": "uint64", - "name": "version", - "type": "uint64" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "newReserveManager", - "type": "address" + "internalType": "uint32", + "name": "groupCount", + "type": "uint32" }, { "indexed": false, - "internalType": "address", - "name": "oldReserveManager", - "type": "address" + "internalType": "uint32", + "name": "operatorCount", + "type": "uint32" } ], - "name": "ReserveManagerAddressSet", + "name": "InitialRosterInstalled", "type": "event" }, { "anonymous": false, "inputs": [ { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" - } - ], - "name": "Upgraded", - "type": "event" - }, - { - "inputs": [], - "name": "MAX_CHUNKS", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "MAX_CHUNK_BYTES", - "outputs": [ - { - "internalType": "uint32", - "name": "", - "type": "uint32" + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" } ], - "stateMutability": "view", - "type": "function" + "name": "Initialized", + "type": "event" }, { - "inputs": [], - "name": "MAX_CHUNK_SLOTS", - "outputs": [ + "anonymous": false, + "inputs": [ { - "internalType": "uint16", - "name": "", - "type": "uint16" + "indexed": false, + "internalType": "address", + "name": "newReserveManager", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "oldReserveManager", + "type": "address" } ], - "stateMutability": "view", - "type": "function" + "name": "ReserveManagerAddressSet", + "type": "event" }, { - "inputs": [], - "name": "MAX_ENVELOPE_BYTES", - "outputs": [ + "anonymous": false, + "inputs": [ { - "internalType": "uint256", - "name": "", - "type": "uint256" + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" } ], - "stateMutability": "view", - "type": "function" + "name": "Upgraded", + "type": "event" }, { "inputs": [], - "name": "MIN_SIG_WEIGHT", + "name": "MAX_ENVELOPE_BYTES", "outputs": [ { "internalType": "uint256", @@ -927,19 +982,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "SLOT_EMPTY", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "UPGRADE_INTERFACE_VERSION", @@ -1001,59 +1043,46 @@ { "inputs": [ { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" + "internalType": "uint32", + "name": "epochIndex_", + "type": "uint32" } ], - "name": "batchOpGroups", + "name": "authorizedGroupIndexFor", "outputs": [ { - "internalType": "address", - "name": "", - "type": "address" + "internalType": "uint32", + "name": "groupIndex", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "ok", + "type": "bool" } ], "stateMutability": "view", "type": "function" }, { - "inputs": [], - "name": "chunkCellCount", - "outputs": [ + "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ + }, { - "internalType": "address", - "name": "operator_", - "type": "address" + "internalType": "uint256", + "name": "", + "type": "uint256" } ], - "name": "chunkHeaderOf", + "name": "batchOpGroups", "outputs": [ { - "internalType": "uint256", - "name": "position", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "found", - "type": "bool" + "internalType": "address", + "name": "", + "type": "address" } ], "stateMutability": "view", @@ -1085,52 +1114,35 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "discardEnvelopeChunks", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { - "internalType": "address", - "name": "operator_", - "type": "address" + "internalType": "uint32", + "name": "", + "type": "uint32" } ], - "name": "envelopeChunkState", + "name": "dispatchSpill", "outputs": [ { - "internalType": "uint32", - "name": "epochIndex", - "type": "uint32" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "uint16", - "name": "totalChunks", - "type": "uint16" + "internalType": "bool", + "name": "tipped", + "type": "bool" }, { "internalType": "uint16", - "name": "receivedChunks", + "name": "dispatched", "type": "uint16" }, { - "internalType": "uint32", - "name": "totalBytes", - "type": "uint32" + "internalType": "bool", + "name": "complete", + "type": "bool" }, { - "internalType": "uint256", - "name": "storedBytes", - "type": "uint256" + "internalType": "bool", + "name": "finalized", + "type": "bool" } ], "stateMutability": "view", @@ -1223,24 +1235,9 @@ "name": "epochIndex", "type": "uint32" }, - { - "internalType": "uint16", - "name": "chunkIndex", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "totalChunks", - "type": "uint16" - }, - { - "internalType": "uint32", - "name": "totalBytes", - "type": "uint32" - }, { "internalType": "bytes", - "name": "chunkData", + "name": "envelopeData", "type": "bytes" } ], @@ -1314,6 +1311,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "inboundMessageTip", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "inboundRetentionConfig", @@ -1333,6 +1343,52 @@ "internalType": "address", "name": "oppManager", "type": "address" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "activeGroupIndex", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "epochIndex", + "type": "uint32" + }, + { + "components": [ + { + "components": [ + { + "internalType": "ChainKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "address_", + "type": "bytes" + } + ], + "internalType": "struct ChainAddress[]", + "name": "operators", + "type": "tuple[]" + } + ], + "internalType": "struct BatchOperatorGroup[]", + "name": "groups", + "type": "tuple[]" + }, + { + "internalType": "uint32", + "name": "epochDurationSec", + "type": "uint32" + } + ], + "internalType": "struct BatchOperatorGroups", + "name": "initialGroups", + "type": "tuple" } ], "name": "initialize", @@ -1341,10 +1397,22 @@ "type": "function" }, { - "inputs": [], - "name": "initializeChunkSlots", - "outputs": [], - "stateMutability": "nonpayable", + "inputs": [ + { + "internalType": "address", + "name": "operator_", + "type": "address" + } + ], + "name": "isActiveOperator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", "type": "function" }, { @@ -1353,9 +1421,14 @@ "internalType": "address", "name": "operator_", "type": "address" + }, + { + "internalType": "uint32", + "name": "epochIndex_", + "type": "uint32" } ], - "name": "isActiveOperator", + "name": "isActiveOperatorForEpoch", "outputs": [ { "internalType": "bool", @@ -1750,6 +1823,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "rosterEpochIndex", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "rosterInitialized", @@ -1865,88 +1951,88 @@ "type": "function" } ], - "bytecode": "0x60a0806040523460da57306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1660cb57506001600160401b036002600160401b0319828216016087575b5f805463ffffffff19166001178155600c556040516149b490816100df8239608051818181610a9f0152612f890152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80806056565b63f92ee8a960e01b8152600490fd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80630fa92c531461038f578063135a70ef1461038a578063151b8f4e146103855780631bdf0a721461038057806320857e651461037b57806322fe388b14610376578063239e1d67146103715780632945698b1461036c5780632c92892f146103675780633367cca514610362578063360511351461035d5780633915ba631461035857806343df05c9146102d65780634ab47f43146103535780634f1ef2861461034e5780635126a52d1461034957806352d1902d146103445780635701a93f1461033f5780635c0c8f441461033a5780635d4e25ce146103355780635ed25346146103305780635ff8195e1461032b578063621fbd59146103265780637a9e5e4b146103215780637b7781221461031c5780637e283cea146103175780637e5db78f1461031257806380e643b61461030d5780638bdebf21146103085780638d9f616b146103035780638fb36037146102fe57806393ec06be146102f957806399b1a19c146102f4578063ad3cb1cc146102ef578063af243e89146102ea578063b41565b8146102e5578063b632ab57146102e0578063b8262d86146102db578063bee323cd146102d6578063bf7e214f146102d1578063bfa9980c146102cc578063c0d0373e146102c7578063c1e8b5e3146102c2578063c268863f146102bd578063c3e558bc146102b8578063c45fdcb7146102b3578063c4d66de8146102ae578063d0841f22146102a9578063d1ad8d01146102a4578063d5b5272a1461029f578063e3dff2af1461029a578063e55ba57614610295578063ea8dbafe14610290578063f2c905941461028b578063f552501a146102865763fdfd07df14610281575f80fd5b611a4a565b611908565b61170a565b6116e2565b6116a3565b611686565b6115d8565b61157e565b611556565b6114c9565b611466565b611341565b611319565b6112fc565b6111f0565b61118f565b61115b565b6107ee565b61107b565b61105f565b610fb3565b610f80565b610f3b565b610ef2565b610ed6565b610e86565b610e47565b610e21565b610dfd565b610d25565b610ca9565b610c50565b610bd6565b610bb9565b610b96565b610b7b565b610b5e565b610b19565b610af6565b610a8d565b610a5b565b61093f565b610808565b6107bc565b610715565b6106e8565b6106cb565b61069e565b61067c565b610661565b6105b3565b610495565b610472565b610457565b6103a6565b63ffffffff8116036103a257565b5f80fd5b346103a25760203660031901126103a25760206004356103c581610394565b63ffffffff8091165f52600e825260405f205416604051908152f35b5f9103126103a257565b634e487b7160e01b5f52601160045260245ffd5b90612000820180921161040e57565b6103eb565b906004820180921161040e57565b906008820180921161040e57565b9190820180921161040e57565b5f1981019190821161040e57565b9190820391821161040e57565b346103a2575f3660031901126103a2576020604051601c8152f35b346103a2575f3660031901126103a257602063ffffffff600f5416604051908152f35b346103a2575f3660031901126103a25763ffffffff805f541690815f52600e6020528060405f2054169160ff60115460201c1680610570575b1561055d5760105415610558576001600160401b03926105549160105f526105075f8051602061493f8339815191525463ffffffff1690565b600f549260405196879685169460201c169286936001600160401b039060809497969260a087019863ffffffff8096818094168a5216602089015216604087015216606085015216910152565b0390f35b6113c7565b82610554916001600160401b0394610507565b5060105415156104ce565b61ffff8116036103a257565b602435906001600160a01b03821682036103a257565b600435906001600160a01b03821682036103a257565b346103a25760403660031901126103a2576004356105d08161057b565b61ffff6105db610587565b916105e6363361213b565b165f818152600460209081526040918290205482516001600160a01b03868116825291821692810192909252929182917f46d568db0216a0215fe7f56360f9fa1a002c4bf0773add9cd62cd06eff1601899190a25f52600460205260405f2091166bffffffffffffffffffffffff60a01b8254161790555f80f35b346103a2575f3660031901126103a257602060405160078152f35b346103a2575f3660031901126103a257602063ffffffff5f5416604051908152f35b346103a25760203660031901126103a2576004355f52601b602052602060ff60405f205416604051908152f35b346103a2575f3660031901126103a2576020600c54604051908152f35b346103a25760203660031901126103a257602061070b61070661059d565b611a90565b6040519015158152f35b346103a25760203660031901126103a2576001600160401b0363ffffffff610554815f5416805f52601760205260405f206004355f526020528260405f2054169061076660115460ff9060201c1690565b806107b1575b156107875761050761077c6113db565b505463ffffffff1690565b6107ac6107a28263ffffffff165f52600e60205260405f2090565b5463ffffffff1690565b610507565b50601054151561076c565b346103a25760203660031901126103a2576004355f526012602052602060018060a01b0360405f205416604051908152f35b346103a2575f3660031901126103a25760206040515f8152f35b346103a25760203660031901126103a2576004355f52601a602052602060ff60405f205416604051908152f35b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761086457604052565b610835565b602081019081106001600160401b0382111761086457604052565b604081019081106001600160401b0382111761086457604052565b6001600160401b03811161086457604052565b60e081019081106001600160401b0382111761086457604052565b90601f801991011681019081106001600160401b0382111761086457604052565b6001600160401b03811161086457601f01601f191660200190565b929192610915826108ee565b9161092360405193846108cd565b8294818452818301116103a2578281602093845f960137010152565b60403660031901126103a25761095361059d565b6024356001600160401b0381116103a257366023820112156103a257610983903690602481600401359101610909565b9061098c612f7f565b610996363361213b565b61099e612f7f565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610a2a575b506109f057604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f8051602061491f8339815191528303610a1157610a0f925061377d565b005b604051632a87526960e21b815260048101849052602490fd5b610a4d91945060203d602011610a54575b610a4581836108cd565b81019061288a565b925f6109c6565b503d610a3b565b346103a25760203660031901126103a2576004355f526013602052602060018060a01b0360405f205416604051908152f35b346103a2575f3660031901126103a2577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610ae45760206040515f8051602061491f8339815191528152f35b60405163703e46dd60e11b8152600490fd5b346103a2575f3660031901126103a257602063ffffffff60155416604051908152f35b346103a25760203660031901126103a257610b3261059d565b610b3c363361213b565b600380546001600160a01b0319166001600160a01b0392909216919091179055005b346103a2575f3660031901126103a2576020600254604051908152f35b346103a2575f3660031901126103a257602060405160048152f35b346103a2575f3660031901126103a257602063ffffffff60115416604051908152f35b346103a2575f3660031901126103a2576020600154604051908152f35b346103a25760203660031901126103a257610bef61059d565b5f805160206148ff833981519152546001600160a01b03919082163303610c3957803b15610c2057610a0f9061225e565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b346103a25760403660031901126103a2576020610ca0600435610c7281610394565b63ffffffff610c7f610587565b91165f52600d835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b346103a25760203660031901126103a2577f2b797207c3f71ed55e7ee9f751f9d4e23795bff49d40a0d3d05e160e1257798d610ce361059d565b610ced363361213b565b601680546001600160a01b039283166001600160a01b031982168117909255604080519283529216602082015290819081015b0390a1005b346103a2575f3660031901126103a257610d3d6122b6565b601c545f905f5b818110610d71578215610d5957610a0f611c42565b60405163dca69e6760e01b8152336004820152602490fd5b610d7a816113fa565b50549261ffff8460c01c16158015610de6575b610ddc575080610da7610da16001936113fa565b90611b5a565b819363ffffffff3391167f77018e8d096d16121cecd1c24da099cbde586f6f93c2f1352a90eac66e5a45455f80a35b01610d44565b9250600190610dd6565b50602084901c6001600160a01b0316331415610d8d565b346103a2575f3660031901126103a257602060115460ff60405191831c1615158152f35b346103a2575f3660031901126103a25760206001600160401b0360185416604051908152f35b346103a25760203660031901126103a25761ffff600435610e678161057b565b165f526004602052602060018060a01b0360405f205416604051908152f35b346103a2575f3660031901126103a2575f805160206148ff8339815191525460a01c60ff1615610ece576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610ebb565b346103a2575f3660031901126103a25760206040516180008152f35b346103a2575f3660031901126103a257602060ff600f5460601c166040519015158152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346103a2575f3660031901126103a257610554604051610f5a81610884565b60058152640352e302e360dc1b6020820152604051918291602083526020830190610f17565b346103a25760203660031901126103a2576004355f52601960205260206001600160401b0360405f205416604051908152f35b346103a25760203660031901126103a257610554600435610fd381610394565b60405f818051610fe281610849565b828152826020820152015263ffffffff8092165f5260146020526001815f206001600160401b0383519461101586610849565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b346103a2575f3660031901126103a25760206040516120008152f35b346103a2575f3660031901126103a2575f8051602061495f833981519152805460ff8160401c16908115611146575b50611134575f8051602061495f833981519152805467ffffffffffffffff19166002179055805460ff60401b1916600160401b1790556110e86122f5565b5f8051602061495f833981519152805460ff60401b19169055604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2908060208101610d20565b60405163f92ee8a960e01b8152600490fd5b600291506001600160401b031610155f6110aa565b346103a2575f3660031901126103a2575f805160206148ff833981519152546040516001600160a01b039091168152602090f35b346103a25760203660031901126103a25760c06111b26111ad61059d565b611ba4565b93949290916040519563ffffffff809516875260018060a01b0316602087015261ffff809216604087015216606085015216608083015260a0820152f35b346103a25760203660031901126103a25760043561120d81610394565b611217363361213b565b63ffffffff90816112396107a28363ffffffff165f52601460205260405f2090565b16156112dc5761125d6112586112535f5463ffffffff1690565b612430565b612466565b82821692811683116112b657506112856112909163ffffffff165f52601460205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b604051631cc9a6c760e21b815263ffffffff928316600482015291166024820152604490fd5b604051630529520560e31b815263ffffffff919091166004820152602490fd5b346103a2575f3660031901126103a2576020601d54604051908152f35b346103a2575f3660031901126103a2576003546040516001600160a01b039091168152602090f35b346103a25760a03660031901126103a25760043561135e81610394565b60243561136a8161057b565b604435916113778361057b565b60643561138381610394565b608435936001600160401b03938486116103a257366023870112156103a25785600401359485116103a25736602486880101116103a2576024610a0f960193611c2c565b634e487b7160e01b5f52603260045260245ffd5b601054156105585760105f525f8051602061493f833981519152905f90565b601c5481101561055857601c5f527f0e4562a10381dec21b205ed72637e6b1b523bdd0e4d4d50af5cd23dd4500a21101905f90565b6010548110156105585760105f525f8051602061493f83398151915201905f90565b8054821015610558575f5260205f2001905f90565b346103a25760403660031901126103a257600435602435906010548110156103a25760105f525f8051602061493f8339815191520180548210156103a2576020916114b091611451565b905460405160039290921b1c6001600160a01b03168152f35b346103a25760203660031901126103a2576114e261059d565b5f8051602061495f83398151915290815460ff8160401c16908115611541575b50611134575f8051602061495f833981519152805467ffffffffffffffff191660021790556110e891805460ff60401b1916600160401b179055612044565b600291506001600160401b031610155f611502565b346103a2575f3660031901126103a2576020600f546001600160401b0360405191831c168152f35b346103a25760203660031901126103a257606060043561159d81610394565b63ffffffff8091165f52601460205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b346103a25760203660031901126103a2576004356115f581610394565b6115ff363361213b565b60405161160b81610869565b63ffffffff908183168082521561167457601580549151831663ffffffff1983161790556040805163ffffffff93909216831682529290911660208201527f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69181908101610d20565b604051639ca50c6160e01b8152600490fd5b346103a2575f3660031901126103a2576020600b54604051908152f35b346103a25760203660031901126103a25763ffffffff6004356116c581610394565b6116cf363361213b565b1663ffffffff19600f541617600f555f80f35b346103a2575f3660031901126103a2576016546040516001600160a01b039091168152602090f35b346103a25760203660031901126103a257604061172d61172861059d565b612362565b825191825215156020820152f35b604051905f6009546001918160011c9260018316908115611822575b60209060208610831461180e57858852879460208601939081156117ef575060011461178f575b50505061178d925003836108cd565b565b9250936117bd60095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af90565b945f935b8285106117d95750505061178d9350015f808061177e565b86548585015295860195889550938101936117c1565b9250505061178d9491925060ff19168252151560051b015f808061177e565b634e487b7160e01b5f52602260045260245ffd5b93607f1693611757565b6040519061183982610884565b60065460ff8116835260081c63ffffffff166020830152565b6040519061185f82610884565b60075460ff8116835260081c63ffffffff166020830152565b926001600160401b03906118de60206118a06119059a98969995610120808a52890190610f17565b986118c1828901825163ffffffff6020809260ff8151168552015116910152565b0151805160ff1660608801526020015163ffffffff166080870152565b1660a084015263ffffffff80921660c08401521660e0820152610100818403910152610f17565b90565b346103a2575f3660031901126103a2576040515f6005546001918160011c9260018316908115611a40575b60209060208610831461180e5785875286946020860193908115611a2157506001146119c1575b84611967818603826108cd565b60405161197381610884565b61197b61182c565b8152611985611852565b602082015261055460085463ffffffff61199d61173b565b916040519586956001600160401b03848460601c16948460401c1693169187611878565b9250936119ef60055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db090565b945f935b828510611a0b575050506119679350015f808061195a565b86548585015295860195879550938101936119f3565b925050506119679491925060ff19168252151560051b015f808061195a565b93607f1693611933565b346103a25760403660031901126103a2576020600435611a6981610394565b63ffffffff8091165f526017825260405f206024355f52825260405f205416604051908152f35b601154611aa59060201c60ff161590565b1590565b611b545763ffffffff80611abe60105463ffffffff1690565b168015611b4d575f5b8183821610611ad857505050505f90565b80611ae3849261142f565b5080545f5b818110611afb5750505060010116611ac7565b9091929350611b22611b0d8285611451565b905460039190911b1c6001600160a01b031690565b6001600160a01b03888116911614611b41576001019085939291611ae8565b50505050505050600190565b5050505f90565b50600190565b90611b64575f9055565b634e487b7160e01b5f525f60045260245ffd5b9081600d1b91808304612000149015171561040e57565b908160021b918083046004149015171561040e57565b611bad90612362565b15611c1d57601c5481101561055857601c5f527f0e4562a10381dec21b205ed72637e6b1b523bdd0e4d4d50af5cd23dd4500a211015461ffff90818160d01c1690611bf782611b77565b63ffffffff821694602083901c6001600160a01b03169460c084901c16939260e01c9190565b505f905f905f905f905f905f90565b90611c429594939291611c3d6122b6565b611cbc565b5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d565b90600161ffff8093160191821161040e57565b909280606093602095601d855286850152846040850152818585015260808401375f8282018401850152601f01601f191601010190565b6040513d5f823e3d90fd5b91939093611cce5f5463ffffffff1690565b63ffffffff948582169286861692848410612038578484036120115750611cf7611aa133611a90565b611ff957611d078983838b612479565b5f965f9861ffff9485841693600185145f14611d3a575050505050505050611d349061178d953691610909565b90612b3f565b91939599509193959950611d4d336125b2565b999095869a15611fe15760019b611d63886113fa565b5096875491848360c01c16908115908115611fd5575b5015611f2357505050818516611f015760e088901b6001600160e01b031916640100000000600160c01b033360201b1663ffffffff8c161761ffff60c01b60c086901b16171786555b611dd5611dce86611c67565b61ffff1690565b03611dfe575050505091611d349391611df861178d99945461ffff9060d01c1690565b90612723565b929a9495509596509750611e35919650611e2c73__$a62ffa76e12530392778037a67b422bd63$__94611b8e565b9089169061042f565b91803b156103a257611e60945f9360405196879485938493633535237960e21b855260048501611c7a565b03915af48015611efc577fbe287004f9392c9ea6ab7463d51604830991743d8d3364ae2566b5bc8607110c93611ede93611ebf92611ee3575b50611ea387611c67565b815461ffff60d01b191660d09190911b61ffff60d01b16179055565b6040805161ffff96871681529590911660208601523394918291820190565b0390a3565b80611ef0611ef69261089f565b806103e1565b5f611e99565b611cb1565b604051635d551ad560e01b81525f600482015261ffff86166024820152604490fd5b602083901c6001600160a01b031633148015918591611fca575b50508015611fbb575b611f9c575060d01c8216858316818110611f8b578103611f665750611dc2565b604051635d551ad560e01b815261ffff91821660048201529086166024820152604490fd5b505050505050505050505050505050565b604051633b52f0fb60e01b8152911663ffffffff166004820152602490fd5b50808a168260e01c1415611f46565b14159050835f611f3d565b9050828416105f611d79565b6040516327b2f1c160e21b8152336004820152602490fd5b604051632af0051560e21b8152336004820152602490fd5b6040516307d3e83d60e51b815263ffffffff91821660048201529087166024820152604490fd5b50505050505050505050565b6120659061205061374f565b61205861374f565b61206061374f565b61225e565b61206d61374f565b6120756122f5565b5f805463ffffffff19908116600117909155600f80546bffffffffffffffffffffffff19164260201b6bffffffffffffffff000000001617905560c890816040516120bf81610869565b526015541617601555565b6004116103a2575f90600490565b6001600160e01b031990358181169392600481106120f557505050565b60040360031b82901b16169150565b60209260409260018060a01b0316825282848301528083830152805f60608401375f8282018401850152601f01601f191601010190565b5f805160206148ff8339815191528054612173906001600160a01b031661216a612164866120ca565b906120d8565b90843091612efe565b9015612180575b50505050565b63ffffffff161561223e575f805160206148ff833981519152805460ff60a01b1916600160a01b1790556121ce906121c29081905b546001600160a01b031690565b6001600160a01b031690565b91823b156103a2576121f9925f9283604051809681958294634a63ebf760e11b845260048401612104565b03925af18015611efc5761222b575b505f805160206148ff833981519152805460ff60a01b191690555f80808061217a565b80611ef06122389261089f565b5f612208565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f805160206148ff83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00805c6122e3576001905d565b604051633ee5aeb560e01b8152600490fd5b5f5b6007811061233757505f5b601c811061230d5750565b601d908154600160401b8110156108645760018101908184551015610558576001915f5201612302565b601c90815491600160401b831015610864578261235a91600180950190556113fa565b5050016122f7565b5f915f91601c54905f809161238761237e5f5463ffffffff1690565b63ffffffff1690565b925b848110612397575050505050565b6123a0816113fa565b505461ffff8160c01c16158015612415575b61240c5763ffffffff809116908582146123fd5788159081156123f1575b506123e1575b506001905b01612389565b90975060019650915086866123d6565b9050841681115f6123d0565b50909750600196505050505050565b506001906123db565b50602081901c6001600160a01b0390811690841614156123b2565b63ffffffff9060018183161115612448575f19011690565b50505f90565b63ffffffff91908282168382161115611b4d57031690565b6119059063ffffffff601554169061244e565b9163ffffffff811690811580156125a7575b61257f57506124ad611dce6124a76124a2846103ff565b61043c565b600d1c90565b61ffff80841691818116830361255a57508416928184101561253457506124d6611dce85611c67565b03612529576124e76124ed92611b77565b9061044a565b915b8281036124fb57505050565b604051632ac20c8f60e21b815261ffff92909216600483015260248201929092526044810191909152606490fd5b5050612000916124ef565b6040516385d12c3160e01b815261ffff8681166004830152919091166024820152604490fd5b6040516328e2405760e01b815261ffff91821660048201529085166024820152604490fd5b604051630c6f042160e31b815263ffffffff9190911660048201526180006024820152604490fd5b50618000821161248b565b601c54906125c660115460ff9060201c1690565b612672575f19905f5b8381106125ef57505090505f1981146125e85790600190565b505f905f90565b6125f8816113fa565b505461ffff8160c01c16158015612653575b1561262b57505f198314612623575b6001905b016125cf565b915081612619565b60201c6001600160a01b03908116908316146126495760019061261d565b9250505090600190565b5061266561237e5f5463ffffffff1690565b63ffffffff82161061260a565b601054915f5b83811061268957505050505f905f90565b6126928161142f565b505f9080545b8083106126aa57505050600101612678565b6126bd611b0d8484999798969599611451565b6001600160a01b038581169116036126e15750505050508110156125e85790600190565b9091929493956001019190612698565b906126fb826108ee565b61270860405191826108cd565b8281528092612719601f19916108ee565b0190602036910137565b9493929161273963ffffffff61273f92166126f1565b95611b8e565b905f905f92601d545b61ffff83168510612760575050506020915085010137565b90919284830180841161040e578281101561055857601d5f527f6d4407e7be21f808e6509aa9fa9143369579dd7d760fe20a2c09680fc146134f01906040515f83549360019085821c82871691821561286f575b602097888310841461180e5782875260208701939081156128505750600114612812575b5050505092600192826127f36128059461ffff9703826108cd565b8d83838351958694019201015e61042f565b9501949392919050612748565b61282391929394505f5260205f2090565b925f935b82851061283f575050500182826127f36128056127d8565b805484860152938701938101612827565b60ff191684525050151560051b019150839050826127f36128056127d8565b90607f16906127b4565b906020611905928181520190610f17565b908160209103126103a2575190565b906040602061178d936128c084825163ffffffff6020809260ff8151168552015116910152565b015191019063ffffffff6020809260ff8151168552015116910152565b604080830163ffffffff9283815116855260208091015194604082820152855180935260609360608201908360608660051b8501019801955f935b86851061292b5750505050505050505090565b90919293949596979998868061296d600193868f89908f605f198a8603018d52519061ffff8251168552868201511686850152015191818a8201520190610f17565b9b9c9a01989796919091019401929190612918565b908082519081815260208091019281808460051b8301019501935f915b8483106129af5750505050505090565b9091929394958480612a80600193601f198682030187528a519083612a7060408451908085526129e28186018351612899565b8382015161014092612a5e612a47612a21612a0960c09588878d01526101808c0190610f17565b9584015195603f1996878c83030160e08d0152610f17565b606084015163ffffffff166101008b01526080840151868b8303016101208c0152610f17565b9460a0830151908901906001600160401b03169052565b01519085830301610160860152610f17565b92015190848184039101526128dd565b980193019301919493929061299f565b906119059160208152815190612ab461014092836020840152610160830190610f17565b91612ac760208501516040840190612899565b60408401516001600160401b031660c0830152606084015163ffffffff1660e0830152608084015163ffffffff1661010083015260c0612b1a60a086015194601f19958686830301610120870152610f17565b9401519282850301910152612982565b63ffffffff80911690811461040e5760010190565b93929190612b4c90613154565b60209081808201510151612b61815160ff1690565b90600260ff831614801590612ee5575b612eb45750506060810195612b8a875163ffffffff1690565b9063ffffffff80821690831603612e8e57505060a081015182815180155f14612dcd5750505060025480612dad57505b60405163791b84ad60e11b8152828180612bd78560048301612a90565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4928315611efc575f93612d8e575b5050612c3a612c28612c14885163ffffffff1690565b63ffffffff165f52600d60205260405f2090565b335f9081526020919091526040902090565b548015612c8d578203612c525761178d949550613391565b6109ec612c63875163ffffffff1690565b60405163082085ab60e21b815263ffffffff90911660048201523360248201529081906044820190565b50612d5a61237e61178d969784612cc433612caf612c14855163ffffffff1690565b9060018060a01b03165f5260205260405f2090565b55612d17612ced612cd9835163ffffffff1690565b63ffffffff165f52600e60205260405f2090565b612d03612cfe825463ffffffff1690565b612b2a565b63ffffffff1663ffffffff19825416179055565b612d50612ced86612d43612d2f855163ffffffff1690565b63ffffffff165f52601760205260405f2090565b905f5260205260405f2090565b5163ffffffff1690565b6040518381523391907fcafc00e7c35f462868d78e8228850e885bd0870978967c446c6bb67122a3fb2890602090a3613391565b612da5929350803d10610a5457610a4581836108cd565b905f80612bfe565b604051631d734b9160e01b815260048101919091525f6024820152604490fd5b03612e52576040516347f5343160e01b815290838280612df08460048301612879565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4918215611efc575f92612e6f575b508115612e525750600254808203612e30575050612bba565b604051631d734b9160e01b815260048101919091526024810191909152604490fd5b604051630ca3ddf160e01b81529081906109ec9060048301612879565b612e87919250843d8611610a5457610a4581836108cd565b905f612e17565b604051635070175360e01b815263ffffffff918216600482015291166024820152604490fd5b8301516040516385a89b3360e01b815246600482015260ff92909216602483015263ffffffff166044820152606490fd5b508084015163ffffffff1663ffffffff16461415612b71565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b03841117610864576040935f9385528380528360205251915afa612f6d57565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215612fbd575b5050610ae457565b5f8051602061491f8339815191525416141590505f80612fb5565b60405190612fe582610884565b5f6020838281520152565b60405190612ffd82610884565b81613006612fd8565b81526020613012612fd8565b910152565b60405190613024826108b2565b606060c083828152613034612ff0565b60208201525f60408201525f838201525f60808201528260a08201520152565b5f19811461040e5760010190565b6001600160401b0381116108645760051b60200190565b60405190613086826108b2565b606060c083613093612ff0565b81528260208201528260408201525f838201528260808201525f60a08201520152565b604051906130c382610884565b60606020835f81520152565b604051906130dc82610884565b816130e5613079565b815260206130126130b6565b906130fb82613062565b61310860405191826108cd565b8281528092613119601f1991613062565b01905f5b82811061312957505050565b6020906131346130cf565b8282850101520161311d565b80518210156105585760209160051b010190565b9061315d613017565b915f8151905f805b83811061332d5750613176906130f1565b9160c086019283525f915b81811061318f575050505050565b61319990856138dc565b90816001600160401b0391828116600a81145f146131ca57505050506131bf9085613a95565b919087525b90613181565b60128103613212575050506131e261320b92876138dc565b9116906132016131fc6131f5848461042f565b838a613920565b613ac4565b60208a015261042f565b905b6131c4565b6028810361323f57505050509061322c61320d92866138dc565b6001600160401b03909116604089015291565b6030810361326f57505050509061320d61325e63ffffffff93876138dc565b9316606089019063ffffffff169052565b6038810361329f57505050509061320d61328e63ffffffff93876138dc565b9316608089019063ffffffff169052565b60a281036132c157505050506132b59085613a95565b919060a08801526131c4565b6101420361331d5750506132d861320b92876138dc565b9116906133176132fa6132f56132ee858561042f565b848b613920565b6139cc565b95875161330682613054565b976133118383613140565b52613140565b5061042f565b61320b9350600716915086613822565b61333a90859493946138dc565b6101426001600160401b038394931614613366575b600761335e9293169086613822565b929192613165565b600761337461335e93613054565b92505061334f565b90600163ffffffff8093160191821161040e57565b929391909360608401946133af6107a2612cd9885163ffffffff1690565b60115460201c60ff1680613744575b1561373e576133ce61077c6113db565b905b819063ffffffff80931615613736575b506133f96107a284612d43612d2f8c5163ffffffff1690565b916134126134068361337c565b60011c637fffffff1690565b90600f54926134316001600160401b0394858482169160201c1661042f565b4210159180808716921682109383613729575b505050811591613721575b501561371757906134cc839261346761352f95600b55565b61349461348061347b8c5163ffffffff1690565b61337c565b63ffffffff1663ffffffff195f5416175f55565b600f80546cffffffffffffffffff000000001916429290921660201b6bffffffffffffffff000000001691909117600160601b179055565b7fa77ed82a4a8c2f7b412630e92b1c8f59e6645b05820a8b0ed43ae5fac990491c61351f61350161237e8b5163ffffffff1690565b6040805186815263ffffffff90951660208601529093918291820190565b0390a2865163ffffffff16613b70565b613545613540865163ffffffff1690565b613bd6565b613705575b50600354613560906001600160a01b03166121c2565b92833b156103a257604051636744c79f60e01b80825260016004830152945f908290602490829084905af18015611efc576136f2575b505f5b60c084015180518210156135c757816135bf6135b960c094600194613140565b51613ca1565b019050613599565b50506003549193909250906135e4906001600160a01b03166121c2565b90813b156103a2575f90604051928391825281838161360a60048201905f602083019252565b03925af18015611efc576136df575b50613625600b54600255565b7fe784b22f2061de501b77364cab02ee109528afe4ddc8d423d076f6b49021b58e61366e613657835163ffffffff1690565b60405163ffffffff90911681529081906020820190565b0390a16003546136959061368a906001600160a01b03166121c2565b915163ffffffff1690565b90803b156103a25760405163a3ad9cc360e01b815263ffffffff9290921660048301525f908290602490829084905af18015611efc576136d25750565b80611ef061178d9261089f565b80611ef06136ec9261089f565b5f613619565b80611ef06136ff9261089f565b5f613596565b610da1613711916113fa565b5f61354a565b5050505050915050565b90505f61344f565b16111590505f8080613444565b90505f6133e0565b806133d0565b5060105415156133be565b60ff5f8051602061495f8339815191525460401c161561376b57565b604051631afcd79f60e31b8152600490fd5b90813b15613801575f8051602061491f83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28051156137e6576137e391614028565b50565b5050346137ef57565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b6001600160401b039283169283613843575061383e92506138dc565b905090565b9192600181036138595750505061190590610421565b6002810361387757506119059261386f916138dc565b91169061042f565b60051415915061388c90505761190590610413565b60405162461bcd60e51b815260206004820152602260248201527f50726f746f62756652756e74696d653a20756e6b6e6f776e2077697265207479604482015261706560f01b6064820152608490fd5b91908201602001905f805b60806007600186515f1a94607f8616851b179601920192161561391357603f82116103a25792906138e7565b9293909203601f19019150565b9181811015806139c1575b15613970578161393a9161044a565b90613944826126f1565b9282613951575b50505090565b015f5b8281101561394b57602081830181015185830182015201613954565b60405162461bcd60e51b8152602060048201526024808201527f50726f746f62756652756e74696d653a20736c696365206f7574206f6620626f604482015263756e647360e01b6064820152608490fd5b50825181111561392b565b906139d56130cf565b915f8151905b8181106139e757505050565b6139f190836138dc565b90816001600160401b0391828116600a81145f14613a4757505050613a19613a3f92856138dc565b911690613a38613a33613a2c848461042f565b8388613920565b6141f7565b875261042f565b905b906139db565b601203613a85575050613a5d613a7f92856138dc565b911690613a75613a70613a2c848461042f565b6140da565b602088015261042f565b90613a41565b613a7f9350600716915084613822565b9190613aa96001600160401b0391846138dc565b911681019283821161040e57613ac0918491613920565b9190565b90613acd612ff0565b915f8151905b818110613adf57505050565b613ae990836138dc565b90816001600160401b0391828116600a81145f14613b3157505050613b11613b2992856138dc565b911690613a38613b24613a2c848461042f565b61435a565b905b90613ad3565b601203613b60575050613b47613b5a92856138dc565b911690613a75613b24613a2c848461042f565b90613b2b565b613b5a9350600716915084613822565b63ffffffff8082165f52601460205260405f2090815416613bd157613ba7600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b613bed9063ffffffff91829182601554169061244e565b16908115159081613c16575b50613c015750565b5f5260146020525f6001604082208281550155565b9050815f52601460205260405f20541615155f613bf9565b90613c46602091949394604084526040840190610f17565b6001600160a01b03909416910152565b9193926001600160401b039061ffff613c79604094606087526060870190610f17565b9616602085015216910152565b60409061ffff61190594931681528160208201520190610f17565b905f5b6020908180850151015180518210156140215781613cc191613140565b51805161ffff16908161ee0f811461400e5761ee138114613ff65761ee218114613f9f5761ee208114613f485761ee1b14613e6a57613d126121c26121b58461ffff165f52600460205260405f2090565b6001600160a01b038116929091908315613e225761dead8414613dd25760408092015192803b156103a2578251633bbe5e5b60e21b8152935f918591829084908290613d62908860048401613c86565b03925af18015611efc576001967f6615e715ef29da98af4d4b50f6beb57ec1a6a61bb207b117e5fb58632755e93794613db592613dbf575b508951015192519283926001600160401b0388169184613c56565b0390a25b01613ca4565b80611ef0613dcc9261089f565b5f613d9a565b9150507f06a84e8c796b3ecc778e5832b8ba6869cc1bf4822409bfd7f852021f87256d0c91506001938651015190613e1a6040519283926001600160401b0387169184613c56565b0390a1613db9565b9150507ff1a904444e5437715f6092bdea001e8e9fa7124d1b19aed02311ac2553da0dea91506001938651015190613e1a6040519283926001600160401b0387169184613c56565b6040908101516016805473__$a62ffa76e12530392778037a67b422bd63$__96919392906001600160a01b0316873b156103a257835163d85349a560e01b8152975f92899283918291613ec09160048401613c2e565b03915af4958615611efc57613f0a7f6615e715ef29da98af4d4b50f6beb57ec1a6a61bb207b117e5fb58632755e93794613f2d92600199613f35575b50546001600160a01b031690565b91895101519251928392888060a01b0316956001600160401b0388169184613c56565b0390a2613db9565b80611ef0613f429261089f565b5f613efc565b506040908101516016805473__$a62ffa76e12530392778037a67b422bd63$__96919392906001600160a01b0316873b156103a257835163462fd82d60e01b8152975f92899283918291613ec09160048401613c2e565b506040908101516016805473__$a62ffa76e12530392778037a67b422bd63$__96919392906001600160a01b0316873b156103a2578351634f6b2fdf60e01b8152975f92899283918291613ec09160048401613c2e565b506001935061400991506040015161455c565b613db9565b506001935061400991506040015161443f565b5050509050565b5f8061190593602081519101845af43d15614064573d91614048836108ee565b9261405660405194856108cd565b83523d5f602085013e61462b565b60609161462b565b6040519061407982610849565b60606040835f81525f60208201520152565b9061409582613062565b6140a260405191826108cd565b82815280926140b3601f1991613062565b01905f5b8281106140c357505050565b6020906140ce61406c565b828285010152016140b7565b906140e36130b6565b915f8151905f805b8381106141a957506140fc9061408b565b91602086019283525f915b818110614115575050505050565b61411f90856138dc565b90816001600160401b0391828116600881145f1461416257505050509061415c61414e63ffffffff93876138dc565b9316889063ffffffff169052565b90614107565b60120361419957505061417861419392876138dc565b9116906133176132fa61418e6132ee858561042f565b61468e565b9061415c565b6141939350600716915086613822565b6141b690859493946138dc565b60126001600160401b0383949316146141e1575b60076141d99293169086613822565b9291926140eb565b60076141ef6141d993613054565b9250506141ca565b90614200613079565b915f8151905b81811061421257505050565b61421c90836138dc565b90816001600160401b0391828116600a81145f1461425f5750505061424461425792856138dc565b911690613a386131fc613a2c848461042f565b905b90614206565b9192509060128103614284575050506142789083613a95565b91906020860152614259565b601a81036142a5575050506142999083613a95565b91906040860152614259565b602881036142d957505050906142d46142c363ffffffff93856138dc565b9316606087019063ffffffff169052565b614259565b603281036142fa575050506142ee9083613a95565b91906080860152614259565b6038810361432657505050906143136142d492846138dc565b6001600160401b0390911660a087015291565b6042036143455750506143399083613a95565b919060c0860152614259565b61435492506007169084613822565b90614259565b90614363612fd8565b915f8151905b81811061437557505050565b61437f90836138dc565b80916001600160401b038116600881145f146143b9575050509060ff6143a86143af93856138dc565b939061475a565b1685525b90614369565b6010036143de575050906143d263ffffffff92846138dc565b921660208601526143b3565b6143ed92506007169084613822565b906143b3565b908160609103126103a257604080519161440c83610849565b805161441781610394565b8352602081015161442781610394565b6020840152015180151581036103a257604082015290565b606061447163ffffffff600f54169260405193849283926374f9c0eb60e01b8452608060048501526084840190610f17565b9060106024840152601260448401526064830152038173__$a62ffa76e12530392778037a67b422bd63$__5af48015611efc57604061450e9161178d935f9161452d575b506144dd6144c7825163ffffffff1690565b63ffffffff1663ffffffff196011541617601155565b6145076144f1602083015163ffffffff1690565b63ffffffff1663ffffffff19600f541617600f55565b0151151590565b64ff0000000060115491151560201b169064ff00000000191617601155565b61454f915060603d606011614555575b61454781836108cd565b8101906143f3565b5f6144b5565b503d61453d565b6001600160401b039060018260185416019082821161040e5773__$a62ffa76e12530392778037a67b422bd63$__90813b156103a2575f916145bc91604051809681948293637401af5d60e01b845260e0600485015260e4840190610f17565b90601260248401526013604484015260196064840152601a6084840152601b60a4840152871660c483015203915af4918215611efc5761178d92614618575b506001600160401b03166001600160401b03196018541617601855565b80611ef06146259261089f565b5f6145fb565b90614652575080511561464057602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614685575b614663575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561465b565b9061469761406c565b915f8151905b8181106146a957505050565b6146b390836138dc565b80916001600160401b038116600881145f146146f257505050906146ec6146dd6146e493856138dc565b93906147aa565b61ffff168652565b9061469d565b60108103614726575050509061472161471063ffffffff93856138dc565b9316602087019063ffffffff169052565b6146ec565b601a036147455750506147399083613a95565b919060408601526146ec565b61475492506007169084613822565b906146ec565b6001600160401b031680156147a55760018114611b54576002811461479f57600381146147995760249060405190632cd3748760e21b82526004820152fd5b50600390565b50600290565b505f90565b6001600160401b0381168015612448576107d18082146148f85750610bb98082146148f85750610bba8082146148f85750610bbc8082146148f85750610bbe8082146148f8575061aa008082146148f8575061ee008082146148f8575061ee028082146148f8575061ee048082146148f8575061ee068082146148f8575061ee108082146148f8575061ee118082146148f8575061ee138082146148f8575061ee0f8082146148f8575061ee158082146148f8575061ee168082146148f8575061ee178082146148f8575061ee188082146148f8575061ee198082146148f8575061ee1b8082146148f8575061ee1c8082146148f8575061ee1e8082146148f8575061ee1f8082146148f8575061ee208082146148f8575061ee218082146148f8575061ee2280911461383e57604051632cd3748760e21b81526001600160401b0383166004820152602490fd5b9150509056fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220c83eea418b14c7847807a669a42dd27a5506963582b408019c06d10beba85c8264736f6c63430008190033", - "deployedBytecode": "0x60806040526004361015610011575f80fd5b5f3560e01c80630fa92c531461038f578063135a70ef1461038a578063151b8f4e146103855780631bdf0a721461038057806320857e651461037b57806322fe388b14610376578063239e1d67146103715780632945698b1461036c5780632c92892f146103675780633367cca514610362578063360511351461035d5780633915ba631461035857806343df05c9146102d65780634ab47f43146103535780634f1ef2861461034e5780635126a52d1461034957806352d1902d146103445780635701a93f1461033f5780635c0c8f441461033a5780635d4e25ce146103355780635ed25346146103305780635ff8195e1461032b578063621fbd59146103265780637a9e5e4b146103215780637b7781221461031c5780637e283cea146103175780637e5db78f1461031257806380e643b61461030d5780638bdebf21146103085780638d9f616b146103035780638fb36037146102fe57806393ec06be146102f957806399b1a19c146102f4578063ad3cb1cc146102ef578063af243e89146102ea578063b41565b8146102e5578063b632ab57146102e0578063b8262d86146102db578063bee323cd146102d6578063bf7e214f146102d1578063bfa9980c146102cc578063c0d0373e146102c7578063c1e8b5e3146102c2578063c268863f146102bd578063c3e558bc146102b8578063c45fdcb7146102b3578063c4d66de8146102ae578063d0841f22146102a9578063d1ad8d01146102a4578063d5b5272a1461029f578063e3dff2af1461029a578063e55ba57614610295578063ea8dbafe14610290578063f2c905941461028b578063f552501a146102865763fdfd07df14610281575f80fd5b611a4a565b611908565b61170a565b6116e2565b6116a3565b611686565b6115d8565b61157e565b611556565b6114c9565b611466565b611341565b611319565b6112fc565b6111f0565b61118f565b61115b565b6107ee565b61107b565b61105f565b610fb3565b610f80565b610f3b565b610ef2565b610ed6565b610e86565b610e47565b610e21565b610dfd565b610d25565b610ca9565b610c50565b610bd6565b610bb9565b610b96565b610b7b565b610b5e565b610b19565b610af6565b610a8d565b610a5b565b61093f565b610808565b6107bc565b610715565b6106e8565b6106cb565b61069e565b61067c565b610661565b6105b3565b610495565b610472565b610457565b6103a6565b63ffffffff8116036103a257565b5f80fd5b346103a25760203660031901126103a25760206004356103c581610394565b63ffffffff8091165f52600e825260405f205416604051908152f35b5f9103126103a257565b634e487b7160e01b5f52601160045260245ffd5b90612000820180921161040e57565b6103eb565b906004820180921161040e57565b906008820180921161040e57565b9190820180921161040e57565b5f1981019190821161040e57565b9190820391821161040e57565b346103a2575f3660031901126103a2576020604051601c8152f35b346103a2575f3660031901126103a257602063ffffffff600f5416604051908152f35b346103a2575f3660031901126103a25763ffffffff805f541690815f52600e6020528060405f2054169160ff60115460201c1680610570575b1561055d5760105415610558576001600160401b03926105549160105f526105075f8051602061493f8339815191525463ffffffff1690565b600f549260405196879685169460201c169286936001600160401b039060809497969260a087019863ffffffff8096818094168a5216602089015216604087015216606085015216910152565b0390f35b6113c7565b82610554916001600160401b0394610507565b5060105415156104ce565b61ffff8116036103a257565b602435906001600160a01b03821682036103a257565b600435906001600160a01b03821682036103a257565b346103a25760403660031901126103a2576004356105d08161057b565b61ffff6105db610587565b916105e6363361213b565b165f818152600460209081526040918290205482516001600160a01b03868116825291821692810192909252929182917f46d568db0216a0215fe7f56360f9fa1a002c4bf0773add9cd62cd06eff1601899190a25f52600460205260405f2091166bffffffffffffffffffffffff60a01b8254161790555f80f35b346103a2575f3660031901126103a257602060405160078152f35b346103a2575f3660031901126103a257602063ffffffff5f5416604051908152f35b346103a25760203660031901126103a2576004355f52601b602052602060ff60405f205416604051908152f35b346103a2575f3660031901126103a2576020600c54604051908152f35b346103a25760203660031901126103a257602061070b61070661059d565b611a90565b6040519015158152f35b346103a25760203660031901126103a2576001600160401b0363ffffffff610554815f5416805f52601760205260405f206004355f526020528260405f2054169061076660115460ff9060201c1690565b806107b1575b156107875761050761077c6113db565b505463ffffffff1690565b6107ac6107a28263ffffffff165f52600e60205260405f2090565b5463ffffffff1690565b610507565b50601054151561076c565b346103a25760203660031901126103a2576004355f526012602052602060018060a01b0360405f205416604051908152f35b346103a2575f3660031901126103a25760206040515f8152f35b346103a25760203660031901126103a2576004355f52601a602052602060ff60405f205416604051908152f35b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761086457604052565b610835565b602081019081106001600160401b0382111761086457604052565b604081019081106001600160401b0382111761086457604052565b6001600160401b03811161086457604052565b60e081019081106001600160401b0382111761086457604052565b90601f801991011681019081106001600160401b0382111761086457604052565b6001600160401b03811161086457601f01601f191660200190565b929192610915826108ee565b9161092360405193846108cd565b8294818452818301116103a2578281602093845f960137010152565b60403660031901126103a25761095361059d565b6024356001600160401b0381116103a257366023820112156103a257610983903690602481600401359101610909565b9061098c612f7f565b610996363361213b565b61099e612f7f565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610a2a575b506109f057604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f8051602061491f8339815191528303610a1157610a0f925061377d565b005b604051632a87526960e21b815260048101849052602490fd5b610a4d91945060203d602011610a54575b610a4581836108cd565b81019061288a565b925f6109c6565b503d610a3b565b346103a25760203660031901126103a2576004355f526013602052602060018060a01b0360405f205416604051908152f35b346103a2575f3660031901126103a2577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610ae45760206040515f8051602061491f8339815191528152f35b60405163703e46dd60e11b8152600490fd5b346103a2575f3660031901126103a257602063ffffffff60155416604051908152f35b346103a25760203660031901126103a257610b3261059d565b610b3c363361213b565b600380546001600160a01b0319166001600160a01b0392909216919091179055005b346103a2575f3660031901126103a2576020600254604051908152f35b346103a2575f3660031901126103a257602060405160048152f35b346103a2575f3660031901126103a257602063ffffffff60115416604051908152f35b346103a2575f3660031901126103a2576020600154604051908152f35b346103a25760203660031901126103a257610bef61059d565b5f805160206148ff833981519152546001600160a01b03919082163303610c3957803b15610c2057610a0f9061225e565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b346103a25760403660031901126103a2576020610ca0600435610c7281610394565b63ffffffff610c7f610587565b91165f52600d835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b346103a25760203660031901126103a2577f2b797207c3f71ed55e7ee9f751f9d4e23795bff49d40a0d3d05e160e1257798d610ce361059d565b610ced363361213b565b601680546001600160a01b039283166001600160a01b031982168117909255604080519283529216602082015290819081015b0390a1005b346103a2575f3660031901126103a257610d3d6122b6565b601c545f905f5b818110610d71578215610d5957610a0f611c42565b60405163dca69e6760e01b8152336004820152602490fd5b610d7a816113fa565b50549261ffff8460c01c16158015610de6575b610ddc575080610da7610da16001936113fa565b90611b5a565b819363ffffffff3391167f77018e8d096d16121cecd1c24da099cbde586f6f93c2f1352a90eac66e5a45455f80a35b01610d44565b9250600190610dd6565b50602084901c6001600160a01b0316331415610d8d565b346103a2575f3660031901126103a257602060115460ff60405191831c1615158152f35b346103a2575f3660031901126103a25760206001600160401b0360185416604051908152f35b346103a25760203660031901126103a25761ffff600435610e678161057b565b165f526004602052602060018060a01b0360405f205416604051908152f35b346103a2575f3660031901126103a2575f805160206148ff8339815191525460a01c60ff1615610ece576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610ebb565b346103a2575f3660031901126103a25760206040516180008152f35b346103a2575f3660031901126103a257602060ff600f5460601c166040519015158152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b346103a2575f3660031901126103a257610554604051610f5a81610884565b60058152640352e302e360dc1b6020820152604051918291602083526020830190610f17565b346103a25760203660031901126103a2576004355f52601960205260206001600160401b0360405f205416604051908152f35b346103a25760203660031901126103a257610554600435610fd381610394565b60405f818051610fe281610849565b828152826020820152015263ffffffff8092165f5260146020526001815f206001600160401b0383519461101586610849565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b346103a2575f3660031901126103a25760206040516120008152f35b346103a2575f3660031901126103a2575f8051602061495f833981519152805460ff8160401c16908115611146575b50611134575f8051602061495f833981519152805467ffffffffffffffff19166002179055805460ff60401b1916600160401b1790556110e86122f5565b5f8051602061495f833981519152805460ff60401b19169055604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2908060208101610d20565b60405163f92ee8a960e01b8152600490fd5b600291506001600160401b031610155f6110aa565b346103a2575f3660031901126103a2575f805160206148ff833981519152546040516001600160a01b039091168152602090f35b346103a25760203660031901126103a25760c06111b26111ad61059d565b611ba4565b93949290916040519563ffffffff809516875260018060a01b0316602087015261ffff809216604087015216606085015216608083015260a0820152f35b346103a25760203660031901126103a25760043561120d81610394565b611217363361213b565b63ffffffff90816112396107a28363ffffffff165f52601460205260405f2090565b16156112dc5761125d6112586112535f5463ffffffff1690565b612430565b612466565b82821692811683116112b657506112856112909163ffffffff165f52601460205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b604051631cc9a6c760e21b815263ffffffff928316600482015291166024820152604490fd5b604051630529520560e31b815263ffffffff919091166004820152602490fd5b346103a2575f3660031901126103a2576020601d54604051908152f35b346103a2575f3660031901126103a2576003546040516001600160a01b039091168152602090f35b346103a25760a03660031901126103a25760043561135e81610394565b60243561136a8161057b565b604435916113778361057b565b60643561138381610394565b608435936001600160401b03938486116103a257366023870112156103a25785600401359485116103a25736602486880101116103a2576024610a0f960193611c2c565b634e487b7160e01b5f52603260045260245ffd5b601054156105585760105f525f8051602061493f833981519152905f90565b601c5481101561055857601c5f527f0e4562a10381dec21b205ed72637e6b1b523bdd0e4d4d50af5cd23dd4500a21101905f90565b6010548110156105585760105f525f8051602061493f83398151915201905f90565b8054821015610558575f5260205f2001905f90565b346103a25760403660031901126103a257600435602435906010548110156103a25760105f525f8051602061493f8339815191520180548210156103a2576020916114b091611451565b905460405160039290921b1c6001600160a01b03168152f35b346103a25760203660031901126103a2576114e261059d565b5f8051602061495f83398151915290815460ff8160401c16908115611541575b50611134575f8051602061495f833981519152805467ffffffffffffffff191660021790556110e891805460ff60401b1916600160401b179055612044565b600291506001600160401b031610155f611502565b346103a2575f3660031901126103a2576020600f546001600160401b0360405191831c168152f35b346103a25760203660031901126103a257606060043561159d81610394565b63ffffffff8091165f52601460205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b346103a25760203660031901126103a2576004356115f581610394565b6115ff363361213b565b60405161160b81610869565b63ffffffff908183168082521561167457601580549151831663ffffffff1983161790556040805163ffffffff93909216831682529290911660208201527f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69181908101610d20565b604051639ca50c6160e01b8152600490fd5b346103a2575f3660031901126103a2576020600b54604051908152f35b346103a25760203660031901126103a25763ffffffff6004356116c581610394565b6116cf363361213b565b1663ffffffff19600f541617600f555f80f35b346103a2575f3660031901126103a2576016546040516001600160a01b039091168152602090f35b346103a25760203660031901126103a257604061172d61172861059d565b612362565b825191825215156020820152f35b604051905f6009546001918160011c9260018316908115611822575b60209060208610831461180e57858852879460208601939081156117ef575060011461178f575b50505061178d925003836108cd565b565b9250936117bd60095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af90565b945f935b8285106117d95750505061178d9350015f808061177e565b86548585015295860195889550938101936117c1565b9250505061178d9491925060ff19168252151560051b015f808061177e565b634e487b7160e01b5f52602260045260245ffd5b93607f1693611757565b6040519061183982610884565b60065460ff8116835260081c63ffffffff166020830152565b6040519061185f82610884565b60075460ff8116835260081c63ffffffff166020830152565b926001600160401b03906118de60206118a06119059a98969995610120808a52890190610f17565b986118c1828901825163ffffffff6020809260ff8151168552015116910152565b0151805160ff1660608801526020015163ffffffff166080870152565b1660a084015263ffffffff80921660c08401521660e0820152610100818403910152610f17565b90565b346103a2575f3660031901126103a2576040515f6005546001918160011c9260018316908115611a40575b60209060208610831461180e5785875286946020860193908115611a2157506001146119c1575b84611967818603826108cd565b60405161197381610884565b61197b61182c565b8152611985611852565b602082015261055460085463ffffffff61199d61173b565b916040519586956001600160401b03848460601c16948460401c1693169187611878565b9250936119ef60055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db090565b945f935b828510611a0b575050506119679350015f808061195a565b86548585015295860195879550938101936119f3565b925050506119679491925060ff19168252151560051b015f808061195a565b93607f1693611933565b346103a25760403660031901126103a2576020600435611a6981610394565b63ffffffff8091165f526017825260405f206024355f52825260405f205416604051908152f35b601154611aa59060201c60ff161590565b1590565b611b545763ffffffff80611abe60105463ffffffff1690565b168015611b4d575f5b8183821610611ad857505050505f90565b80611ae3849261142f565b5080545f5b818110611afb5750505060010116611ac7565b9091929350611b22611b0d8285611451565b905460039190911b1c6001600160a01b031690565b6001600160a01b03888116911614611b41576001019085939291611ae8565b50505050505050600190565b5050505f90565b50600190565b90611b64575f9055565b634e487b7160e01b5f525f60045260245ffd5b9081600d1b91808304612000149015171561040e57565b908160021b918083046004149015171561040e57565b611bad90612362565b15611c1d57601c5481101561055857601c5f527f0e4562a10381dec21b205ed72637e6b1b523bdd0e4d4d50af5cd23dd4500a211015461ffff90818160d01c1690611bf782611b77565b63ffffffff821694602083901c6001600160a01b03169460c084901c16939260e01c9190565b505f905f905f905f905f905f90565b90611c429594939291611c3d6122b6565b611cbc565b5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d565b90600161ffff8093160191821161040e57565b909280606093602095601d855286850152846040850152818585015260808401375f8282018401850152601f01601f191601010190565b6040513d5f823e3d90fd5b91939093611cce5f5463ffffffff1690565b63ffffffff948582169286861692848410612038578484036120115750611cf7611aa133611a90565b611ff957611d078983838b612479565b5f965f9861ffff9485841693600185145f14611d3a575050505050505050611d349061178d953691610909565b90612b3f565b91939599509193959950611d4d336125b2565b999095869a15611fe15760019b611d63886113fa565b5096875491848360c01c16908115908115611fd5575b5015611f2357505050818516611f015760e088901b6001600160e01b031916640100000000600160c01b033360201b1663ffffffff8c161761ffff60c01b60c086901b16171786555b611dd5611dce86611c67565b61ffff1690565b03611dfe575050505091611d349391611df861178d99945461ffff9060d01c1690565b90612723565b929a9495509596509750611e35919650611e2c73__$a62ffa76e12530392778037a67b422bd63$__94611b8e565b9089169061042f565b91803b156103a257611e60945f9360405196879485938493633535237960e21b855260048501611c7a565b03915af48015611efc577fbe287004f9392c9ea6ab7463d51604830991743d8d3364ae2566b5bc8607110c93611ede93611ebf92611ee3575b50611ea387611c67565b815461ffff60d01b191660d09190911b61ffff60d01b16179055565b6040805161ffff96871681529590911660208601523394918291820190565b0390a3565b80611ef0611ef69261089f565b806103e1565b5f611e99565b611cb1565b604051635d551ad560e01b81525f600482015261ffff86166024820152604490fd5b602083901c6001600160a01b031633148015918591611fca575b50508015611fbb575b611f9c575060d01c8216858316818110611f8b578103611f665750611dc2565b604051635d551ad560e01b815261ffff91821660048201529086166024820152604490fd5b505050505050505050505050505050565b604051633b52f0fb60e01b8152911663ffffffff166004820152602490fd5b50808a168260e01c1415611f46565b14159050835f611f3d565b9050828416105f611d79565b6040516327b2f1c160e21b8152336004820152602490fd5b604051632af0051560e21b8152336004820152602490fd5b6040516307d3e83d60e51b815263ffffffff91821660048201529087166024820152604490fd5b50505050505050505050565b6120659061205061374f565b61205861374f565b61206061374f565b61225e565b61206d61374f565b6120756122f5565b5f805463ffffffff19908116600117909155600f80546bffffffffffffffffffffffff19164260201b6bffffffffffffffff000000001617905560c890816040516120bf81610869565b526015541617601555565b6004116103a2575f90600490565b6001600160e01b031990358181169392600481106120f557505050565b60040360031b82901b16169150565b60209260409260018060a01b0316825282848301528083830152805f60608401375f8282018401850152601f01601f191601010190565b5f805160206148ff8339815191528054612173906001600160a01b031661216a612164866120ca565b906120d8565b90843091612efe565b9015612180575b50505050565b63ffffffff161561223e575f805160206148ff833981519152805460ff60a01b1916600160a01b1790556121ce906121c29081905b546001600160a01b031690565b6001600160a01b031690565b91823b156103a2576121f9925f9283604051809681958294634a63ebf760e11b845260048401612104565b03925af18015611efc5761222b575b505f805160206148ff833981519152805460ff60a01b191690555f80808061217a565b80611ef06122389261089f565b5f612208565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f805160206148ff83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00805c6122e3576001905d565b604051633ee5aeb560e01b8152600490fd5b5f5b6007811061233757505f5b601c811061230d5750565b601d908154600160401b8110156108645760018101908184551015610558576001915f5201612302565b601c90815491600160401b831015610864578261235a91600180950190556113fa565b5050016122f7565b5f915f91601c54905f809161238761237e5f5463ffffffff1690565b63ffffffff1690565b925b848110612397575050505050565b6123a0816113fa565b505461ffff8160c01c16158015612415575b61240c5763ffffffff809116908582146123fd5788159081156123f1575b506123e1575b506001905b01612389565b90975060019650915086866123d6565b9050841681115f6123d0565b50909750600196505050505050565b506001906123db565b50602081901c6001600160a01b0390811690841614156123b2565b63ffffffff9060018183161115612448575f19011690565b50505f90565b63ffffffff91908282168382161115611b4d57031690565b6119059063ffffffff601554169061244e565b9163ffffffff811690811580156125a7575b61257f57506124ad611dce6124a76124a2846103ff565b61043c565b600d1c90565b61ffff80841691818116830361255a57508416928184101561253457506124d6611dce85611c67565b03612529576124e76124ed92611b77565b9061044a565b915b8281036124fb57505050565b604051632ac20c8f60e21b815261ffff92909216600483015260248201929092526044810191909152606490fd5b5050612000916124ef565b6040516385d12c3160e01b815261ffff8681166004830152919091166024820152604490fd5b6040516328e2405760e01b815261ffff91821660048201529085166024820152604490fd5b604051630c6f042160e31b815263ffffffff9190911660048201526180006024820152604490fd5b50618000821161248b565b601c54906125c660115460ff9060201c1690565b612672575f19905f5b8381106125ef57505090505f1981146125e85790600190565b505f905f90565b6125f8816113fa565b505461ffff8160c01c16158015612653575b1561262b57505f198314612623575b6001905b016125cf565b915081612619565b60201c6001600160a01b03908116908316146126495760019061261d565b9250505090600190565b5061266561237e5f5463ffffffff1690565b63ffffffff82161061260a565b601054915f5b83811061268957505050505f905f90565b6126928161142f565b505f9080545b8083106126aa57505050600101612678565b6126bd611b0d8484999798969599611451565b6001600160a01b038581169116036126e15750505050508110156125e85790600190565b9091929493956001019190612698565b906126fb826108ee565b61270860405191826108cd565b8281528092612719601f19916108ee565b0190602036910137565b9493929161273963ffffffff61273f92166126f1565b95611b8e565b905f905f92601d545b61ffff83168510612760575050506020915085010137565b90919284830180841161040e578281101561055857601d5f527f6d4407e7be21f808e6509aa9fa9143369579dd7d760fe20a2c09680fc146134f01906040515f83549360019085821c82871691821561286f575b602097888310841461180e5782875260208701939081156128505750600114612812575b5050505092600192826127f36128059461ffff9703826108cd565b8d83838351958694019201015e61042f565b9501949392919050612748565b61282391929394505f5260205f2090565b925f935b82851061283f575050500182826127f36128056127d8565b805484860152938701938101612827565b60ff191684525050151560051b019150839050826127f36128056127d8565b90607f16906127b4565b906020611905928181520190610f17565b908160209103126103a2575190565b906040602061178d936128c084825163ffffffff6020809260ff8151168552015116910152565b015191019063ffffffff6020809260ff8151168552015116910152565b604080830163ffffffff9283815116855260208091015194604082820152855180935260609360608201908360608660051b8501019801955f935b86851061292b5750505050505050505090565b90919293949596979998868061296d600193868f89908f605f198a8603018d52519061ffff8251168552868201511686850152015191818a8201520190610f17565b9b9c9a01989796919091019401929190612918565b908082519081815260208091019281808460051b8301019501935f915b8483106129af5750505050505090565b9091929394958480612a80600193601f198682030187528a519083612a7060408451908085526129e28186018351612899565b8382015161014092612a5e612a47612a21612a0960c09588878d01526101808c0190610f17565b9584015195603f1996878c83030160e08d0152610f17565b606084015163ffffffff166101008b01526080840151868b8303016101208c0152610f17565b9460a0830151908901906001600160401b03169052565b01519085830301610160860152610f17565b92015190848184039101526128dd565b980193019301919493929061299f565b906119059160208152815190612ab461014092836020840152610160830190610f17565b91612ac760208501516040840190612899565b60408401516001600160401b031660c0830152606084015163ffffffff1660e0830152608084015163ffffffff1661010083015260c0612b1a60a086015194601f19958686830301610120870152610f17565b9401519282850301910152612982565b63ffffffff80911690811461040e5760010190565b93929190612b4c90613154565b60209081808201510151612b61815160ff1690565b90600260ff831614801590612ee5575b612eb45750506060810195612b8a875163ffffffff1690565b9063ffffffff80821690831603612e8e57505060a081015182815180155f14612dcd5750505060025480612dad57505b60405163791b84ad60e11b8152828180612bd78560048301612a90565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4928315611efc575f93612d8e575b5050612c3a612c28612c14885163ffffffff1690565b63ffffffff165f52600d60205260405f2090565b335f9081526020919091526040902090565b548015612c8d578203612c525761178d949550613391565b6109ec612c63875163ffffffff1690565b60405163082085ab60e21b815263ffffffff90911660048201523360248201529081906044820190565b50612d5a61237e61178d969784612cc433612caf612c14855163ffffffff1690565b9060018060a01b03165f5260205260405f2090565b55612d17612ced612cd9835163ffffffff1690565b63ffffffff165f52600e60205260405f2090565b612d03612cfe825463ffffffff1690565b612b2a565b63ffffffff1663ffffffff19825416179055565b612d50612ced86612d43612d2f855163ffffffff1690565b63ffffffff165f52601760205260405f2090565b905f5260205260405f2090565b5163ffffffff1690565b6040518381523391907fcafc00e7c35f462868d78e8228850e885bd0870978967c446c6bb67122a3fb2890602090a3613391565b612da5929350803d10610a5457610a4581836108cd565b905f80612bfe565b604051631d734b9160e01b815260048101919091525f6024820152604490fd5b03612e52576040516347f5343160e01b815290838280612df08460048301612879565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4918215611efc575f92612e6f575b508115612e525750600254808203612e30575050612bba565b604051631d734b9160e01b815260048101919091526024810191909152604490fd5b604051630ca3ddf160e01b81529081906109ec9060048301612879565b612e87919250843d8611610a5457610a4581836108cd565b905f612e17565b604051635070175360e01b815263ffffffff918216600482015291166024820152604490fd5b8301516040516385a89b3360e01b815246600482015260ff92909216602483015263ffffffff166044820152606490fd5b508084015163ffffffff1663ffffffff16461415612b71565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b03841117610864576040935f9385528380528360205251915afa612f6d57565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215612fbd575b5050610ae457565b5f8051602061491f8339815191525416141590505f80612fb5565b60405190612fe582610884565b5f6020838281520152565b60405190612ffd82610884565b81613006612fd8565b81526020613012612fd8565b910152565b60405190613024826108b2565b606060c083828152613034612ff0565b60208201525f60408201525f838201525f60808201528260a08201520152565b5f19811461040e5760010190565b6001600160401b0381116108645760051b60200190565b60405190613086826108b2565b606060c083613093612ff0565b81528260208201528260408201525f838201528260808201525f60a08201520152565b604051906130c382610884565b60606020835f81520152565b604051906130dc82610884565b816130e5613079565b815260206130126130b6565b906130fb82613062565b61310860405191826108cd565b8281528092613119601f1991613062565b01905f5b82811061312957505050565b6020906131346130cf565b8282850101520161311d565b80518210156105585760209160051b010190565b9061315d613017565b915f8151905f805b83811061332d5750613176906130f1565b9160c086019283525f915b81811061318f575050505050565b61319990856138dc565b90816001600160401b0391828116600a81145f146131ca57505050506131bf9085613a95565b919087525b90613181565b60128103613212575050506131e261320b92876138dc565b9116906132016131fc6131f5848461042f565b838a613920565b613ac4565b60208a015261042f565b905b6131c4565b6028810361323f57505050509061322c61320d92866138dc565b6001600160401b03909116604089015291565b6030810361326f57505050509061320d61325e63ffffffff93876138dc565b9316606089019063ffffffff169052565b6038810361329f57505050509061320d61328e63ffffffff93876138dc565b9316608089019063ffffffff169052565b60a281036132c157505050506132b59085613a95565b919060a08801526131c4565b6101420361331d5750506132d861320b92876138dc565b9116906133176132fa6132f56132ee858561042f565b848b613920565b6139cc565b95875161330682613054565b976133118383613140565b52613140565b5061042f565b61320b9350600716915086613822565b61333a90859493946138dc565b6101426001600160401b038394931614613366575b600761335e9293169086613822565b929192613165565b600761337461335e93613054565b92505061334f565b90600163ffffffff8093160191821161040e57565b929391909360608401946133af6107a2612cd9885163ffffffff1690565b60115460201c60ff1680613744575b1561373e576133ce61077c6113db565b905b819063ffffffff80931615613736575b506133f96107a284612d43612d2f8c5163ffffffff1690565b916134126134068361337c565b60011c637fffffff1690565b90600f54926134316001600160401b0394858482169160201c1661042f565b4210159180808716921682109383613729575b505050811591613721575b501561371757906134cc839261346761352f95600b55565b61349461348061347b8c5163ffffffff1690565b61337c565b63ffffffff1663ffffffff195f5416175f55565b600f80546cffffffffffffffffff000000001916429290921660201b6bffffffffffffffff000000001691909117600160601b179055565b7fa77ed82a4a8c2f7b412630e92b1c8f59e6645b05820a8b0ed43ae5fac990491c61351f61350161237e8b5163ffffffff1690565b6040805186815263ffffffff90951660208601529093918291820190565b0390a2865163ffffffff16613b70565b613545613540865163ffffffff1690565b613bd6565b613705575b50600354613560906001600160a01b03166121c2565b92833b156103a257604051636744c79f60e01b80825260016004830152945f908290602490829084905af18015611efc576136f2575b505f5b60c084015180518210156135c757816135bf6135b960c094600194613140565b51613ca1565b019050613599565b50506003549193909250906135e4906001600160a01b03166121c2565b90813b156103a2575f90604051928391825281838161360a60048201905f602083019252565b03925af18015611efc576136df575b50613625600b54600255565b7fe784b22f2061de501b77364cab02ee109528afe4ddc8d423d076f6b49021b58e61366e613657835163ffffffff1690565b60405163ffffffff90911681529081906020820190565b0390a16003546136959061368a906001600160a01b03166121c2565b915163ffffffff1690565b90803b156103a25760405163a3ad9cc360e01b815263ffffffff9290921660048301525f908290602490829084905af18015611efc576136d25750565b80611ef061178d9261089f565b80611ef06136ec9261089f565b5f613619565b80611ef06136ff9261089f565b5f613596565b610da1613711916113fa565b5f61354a565b5050505050915050565b90505f61344f565b16111590505f8080613444565b90505f6133e0565b806133d0565b5060105415156133be565b60ff5f8051602061495f8339815191525460401c161561376b57565b604051631afcd79f60e31b8152600490fd5b90813b15613801575f8051602061491f83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28051156137e6576137e391614028565b50565b5050346137ef57565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b6001600160401b039283169283613843575061383e92506138dc565b905090565b9192600181036138595750505061190590610421565b6002810361387757506119059261386f916138dc565b91169061042f565b60051415915061388c90505761190590610413565b60405162461bcd60e51b815260206004820152602260248201527f50726f746f62756652756e74696d653a20756e6b6e6f776e2077697265207479604482015261706560f01b6064820152608490fd5b91908201602001905f805b60806007600186515f1a94607f8616851b179601920192161561391357603f82116103a25792906138e7565b9293909203601f19019150565b9181811015806139c1575b15613970578161393a9161044a565b90613944826126f1565b9282613951575b50505090565b015f5b8281101561394b57602081830181015185830182015201613954565b60405162461bcd60e51b8152602060048201526024808201527f50726f746f62756652756e74696d653a20736c696365206f7574206f6620626f604482015263756e647360e01b6064820152608490fd5b50825181111561392b565b906139d56130cf565b915f8151905b8181106139e757505050565b6139f190836138dc565b90816001600160401b0391828116600a81145f14613a4757505050613a19613a3f92856138dc565b911690613a38613a33613a2c848461042f565b8388613920565b6141f7565b875261042f565b905b906139db565b601203613a85575050613a5d613a7f92856138dc565b911690613a75613a70613a2c848461042f565b6140da565b602088015261042f565b90613a41565b613a7f9350600716915084613822565b9190613aa96001600160401b0391846138dc565b911681019283821161040e57613ac0918491613920565b9190565b90613acd612ff0565b915f8151905b818110613adf57505050565b613ae990836138dc565b90816001600160401b0391828116600a81145f14613b3157505050613b11613b2992856138dc565b911690613a38613b24613a2c848461042f565b61435a565b905b90613ad3565b601203613b60575050613b47613b5a92856138dc565b911690613a75613b24613a2c848461042f565b90613b2b565b613b5a9350600716915084613822565b63ffffffff8082165f52601460205260405f2090815416613bd157613ba7600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b613bed9063ffffffff91829182601554169061244e565b16908115159081613c16575b50613c015750565b5f5260146020525f6001604082208281550155565b9050815f52601460205260405f20541615155f613bf9565b90613c46602091949394604084526040840190610f17565b6001600160a01b03909416910152565b9193926001600160401b039061ffff613c79604094606087526060870190610f17565b9616602085015216910152565b60409061ffff61190594931681528160208201520190610f17565b905f5b6020908180850151015180518210156140215781613cc191613140565b51805161ffff16908161ee0f811461400e5761ee138114613ff65761ee218114613f9f5761ee208114613f485761ee1b14613e6a57613d126121c26121b58461ffff165f52600460205260405f2090565b6001600160a01b038116929091908315613e225761dead8414613dd25760408092015192803b156103a2578251633bbe5e5b60e21b8152935f918591829084908290613d62908860048401613c86565b03925af18015611efc576001967f6615e715ef29da98af4d4b50f6beb57ec1a6a61bb207b117e5fb58632755e93794613db592613dbf575b508951015192519283926001600160401b0388169184613c56565b0390a25b01613ca4565b80611ef0613dcc9261089f565b5f613d9a565b9150507f06a84e8c796b3ecc778e5832b8ba6869cc1bf4822409bfd7f852021f87256d0c91506001938651015190613e1a6040519283926001600160401b0387169184613c56565b0390a1613db9565b9150507ff1a904444e5437715f6092bdea001e8e9fa7124d1b19aed02311ac2553da0dea91506001938651015190613e1a6040519283926001600160401b0387169184613c56565b6040908101516016805473__$a62ffa76e12530392778037a67b422bd63$__96919392906001600160a01b0316873b156103a257835163d85349a560e01b8152975f92899283918291613ec09160048401613c2e565b03915af4958615611efc57613f0a7f6615e715ef29da98af4d4b50f6beb57ec1a6a61bb207b117e5fb58632755e93794613f2d92600199613f35575b50546001600160a01b031690565b91895101519251928392888060a01b0316956001600160401b0388169184613c56565b0390a2613db9565b80611ef0613f429261089f565b5f613efc565b506040908101516016805473__$a62ffa76e12530392778037a67b422bd63$__96919392906001600160a01b0316873b156103a257835163462fd82d60e01b8152975f92899283918291613ec09160048401613c2e565b506040908101516016805473__$a62ffa76e12530392778037a67b422bd63$__96919392906001600160a01b0316873b156103a2578351634f6b2fdf60e01b8152975f92899283918291613ec09160048401613c2e565b506001935061400991506040015161455c565b613db9565b506001935061400991506040015161443f565b5050509050565b5f8061190593602081519101845af43d15614064573d91614048836108ee565b9261405660405194856108cd565b83523d5f602085013e61462b565b60609161462b565b6040519061407982610849565b60606040835f81525f60208201520152565b9061409582613062565b6140a260405191826108cd565b82815280926140b3601f1991613062565b01905f5b8281106140c357505050565b6020906140ce61406c565b828285010152016140b7565b906140e36130b6565b915f8151905f805b8381106141a957506140fc9061408b565b91602086019283525f915b818110614115575050505050565b61411f90856138dc565b90816001600160401b0391828116600881145f1461416257505050509061415c61414e63ffffffff93876138dc565b9316889063ffffffff169052565b90614107565b60120361419957505061417861419392876138dc565b9116906133176132fa61418e6132ee858561042f565b61468e565b9061415c565b6141939350600716915086613822565b6141b690859493946138dc565b60126001600160401b0383949316146141e1575b60076141d99293169086613822565b9291926140eb565b60076141ef6141d993613054565b9250506141ca565b90614200613079565b915f8151905b81811061421257505050565b61421c90836138dc565b90816001600160401b0391828116600a81145f1461425f5750505061424461425792856138dc565b911690613a386131fc613a2c848461042f565b905b90614206565b9192509060128103614284575050506142789083613a95565b91906020860152614259565b601a81036142a5575050506142999083613a95565b91906040860152614259565b602881036142d957505050906142d46142c363ffffffff93856138dc565b9316606087019063ffffffff169052565b614259565b603281036142fa575050506142ee9083613a95565b91906080860152614259565b6038810361432657505050906143136142d492846138dc565b6001600160401b0390911660a087015291565b6042036143455750506143399083613a95565b919060c0860152614259565b61435492506007169084613822565b90614259565b90614363612fd8565b915f8151905b81811061437557505050565b61437f90836138dc565b80916001600160401b038116600881145f146143b9575050509060ff6143a86143af93856138dc565b939061475a565b1685525b90614369565b6010036143de575050906143d263ffffffff92846138dc565b921660208601526143b3565b6143ed92506007169084613822565b906143b3565b908160609103126103a257604080519161440c83610849565b805161441781610394565b8352602081015161442781610394565b6020840152015180151581036103a257604082015290565b606061447163ffffffff600f54169260405193849283926374f9c0eb60e01b8452608060048501526084840190610f17565b9060106024840152601260448401526064830152038173__$a62ffa76e12530392778037a67b422bd63$__5af48015611efc57604061450e9161178d935f9161452d575b506144dd6144c7825163ffffffff1690565b63ffffffff1663ffffffff196011541617601155565b6145076144f1602083015163ffffffff1690565b63ffffffff1663ffffffff19600f541617600f55565b0151151590565b64ff0000000060115491151560201b169064ff00000000191617601155565b61454f915060603d606011614555575b61454781836108cd565b8101906143f3565b5f6144b5565b503d61453d565b6001600160401b039060018260185416019082821161040e5773__$a62ffa76e12530392778037a67b422bd63$__90813b156103a2575f916145bc91604051809681948293637401af5d60e01b845260e0600485015260e4840190610f17565b90601260248401526013604484015260196064840152601a6084840152601b60a4840152871660c483015203915af4918215611efc5761178d92614618575b506001600160401b03166001600160401b03196018541617601855565b80611ef06146259261089f565b5f6145fb565b90614652575080511561464057602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614685575b614663575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561465b565b9061469761406c565b915f8151905b8181106146a957505050565b6146b390836138dc565b80916001600160401b038116600881145f146146f257505050906146ec6146dd6146e493856138dc565b93906147aa565b61ffff168652565b9061469d565b60108103614726575050509061472161471063ffffffff93856138dc565b9316602087019063ffffffff169052565b6146ec565b601a036147455750506147399083613a95565b919060408601526146ec565b61475492506007169084613822565b906146ec565b6001600160401b031680156147a55760018114611b54576002811461479f57600381146147995760249060405190632cd3748760e21b82526004820152fd5b50600390565b50600290565b505f90565b6001600160401b0381168015612448576107d18082146148f85750610bb98082146148f85750610bba8082146148f85750610bbc8082146148f85750610bbe8082146148f8575061aa008082146148f8575061ee008082146148f8575061ee028082146148f8575061ee048082146148f8575061ee068082146148f8575061ee108082146148f8575061ee118082146148f8575061ee138082146148f8575061ee0f8082146148f8575061ee158082146148f8575061ee168082146148f8575061ee178082146148f8575061ee188082146148f8575061ee198082146148f8575061ee1b8082146148f8575061ee1c8082146148f8575061ee1e8082146148f8575061ee1f8082146148f8575061ee208082146148f8575061ee218082146148f8575061ee2280911461383e57604051632cd3748760e21b81526001600160401b0383166004820152602490fd5b9150509056fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220c83eea418b14c7847807a669a42dd27a5506963582b408019c06d10beba85c8264736f6c63430008190033", + "bytecode": "0x60a0806040523460da57306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1660cb57506001600160401b036002600160401b0319828216016087575b5f805463ffffffff19166001178155600c55604051614cb090816100df8239608051818181610c6b01526139d40152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80806056565b63f92ee8a960e01b8152600490fd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80624e356a146103435780630fa92c531461033e578063151b8f4e146103395780631bdf0a72146103345780631d7a88271461032f57806320857e651461032a57806322fe388b14610325578063239e1d67146103205780632945698b1461031b5780632c92892f146103165780632f4d9fa9146103115780633367cca51461030c57806336051135146103075780633915ba63146103025780634ab47f43146102fd5780634f1ef286146102f85780635126a52d146102f357806352d1902d146102ee578063533ae1cc146102e95780635701a93f146102e45780635c0c8f44146102df5780635d4e25ce146102da5780635ff8195e146102d5578063621fbd59146102d05780637a9e5e4b146102cb5780637b778122146102c65780637e283cea146102c157806380e643b6146102bc5780638b75e929146102b75780638bdebf21146102b25780638d9f616b146102ad5780638fb36037146102a857806393ec06be146102a357806399b1a19c1461029e578063ad3cb1cc14610299578063af243e8914610294578063b41565b81461028f578063bf7e214f1461028a578063c0d0373e14610285578063c268863f14610280578063c45fdcb71461027b578063cd2ad3fb14610276578063d0841f2214610271578063d1ad8d011461026c578063d3f0730614610267578063d5b5272a14610262578063e3dff2af1461025d578063e55ba57614610258578063ea8dbafe14610253578063f552501a1461024e5763fdfd07df14610249575f80fd5b611910565b6117ce565b6115de565b61159f565b611582565b6114d4565b6114a4565b61144a565b611422565b6113be565b61135b565b6112e3565b6111cd565b611199565b6110ed565b6110ba565b611075565b61102c565b611010565b610fc0565b610f81565b610f5b565b610f20565b610efc565b610e80565b610e27565b610dad565b610d90565b610d6d565b610d50565b610d0b565b610ce8565b610cc2565b610c59565b610c27565b610b1e565b610af1565b610abf565b610a75565b610a3f565b610a22565b610a05565b6109d8565b6109b6565b61099b565b6108ed565b6106ad565b610471565b61044e565b610409565b610367565b63ffffffff81160361035657565b5f80fd5b359061036582610348565b565b346103565760403660031901126103565760043561038481610348565b6001600160401b036024358181116103565736602382011215610356578060040135918211610356573660248383010111610356577f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0091825c6103f7575f9360246103f4936001865d0190611956565b5d005b604051633ee5aeb560e01b8152600490fd5b3461035657602036600319011261035657602060043561042881610348565b63ffffffff8091165f52600e825260405f205416604051908152f35b5f91031261035657565b34610356575f36600319011261035657602063ffffffff600f5416604051908152f35b34610356575f366003190112610356576001600160401b0363ffffffff6104fb815f5416805f52600e6020528260405f205416906104ae8161291f565b600f549260405196879685169460201c169286936001600160401b039060809497969260a087019863ffffffff8096818094168a5216602089015216604087015216606085015216910152565b0390f35b600435906001600160a01b038216820361035657565b602435906001600160a01b038216820361035657565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b0382111761055a57604052565b61052b565b602081019081106001600160401b0382111761055a57604052565b604081019081106001600160401b0382111761055a57604052565b606081019081106001600160401b0382111761055a57604052565b6001600160401b03811161055a57604052565b60c081019081106001600160401b0382111761055a57604052565b60e081019081106001600160401b0382111761055a57604052565b90601f801991011681019081106001600160401b0382111761055a57604052565b604051906103658261055f565b6001600160401b03811161055a5760051b60200190565b6001600160401b03811161055a57601f01601f191660200190565b9291926106658261063e565b9161067360405193846105f9565b829481845281830111610356578281602093845f960137010152565b9080601f83011215610356578160206106aa93359101610659565b90565b3461035657600319604036820112610356576106c76104ff565b602435916001600160401b03831161035657608090833603011261035657604051906106f28261053f565b826004013561070081610348565b8252602483013561071081610348565b60208301526044926044810135936001600160401b03851161035657366023868401011215610356576004858301013561074981610627565b9561075760405197886105f9565b81875260208701903660248460051b83880101011161035657602481860101915b60248460051b838801010183106107ac576107aa88886107a060648a8e60408601520161035a565b6060830152611b82565b005b82356001600160401b03811161035657828701016020602319823603011261035657604051906107db8261055f565b60248101356001600160401b0381116103565736604382840101121561035657602481830101359061080c82610627565b9261081a60405194856105f9565b8284526020840190368b8560051b858401010111610356578a83820101915b8b8560051b8584010101831061085f575050509183525050815260209283019201610778565b82356001600160401b038111610356576040838601820136036043190112610356576040519161088e8361057a565b8d8287860101013560ff81168103610356578352606482878601010135926001600160401b038411610356576108d28f9360209594869536928b8a0101010161068f565b83820152815201920191610839565b61ffff81160361035657565b346103565760403660031901126103565760043561090a816108e1565b61ffff610915610515565b916109203633612ac6565b165f818152600460209081526040918290205482516001600160a01b03868116825291821692810192909252929182917f46d568db0216a0215fe7f56360f9fa1a002c4bf0773add9cd62cd06eff1601899190a25f52600460205260405f2091166bffffffffffffffffffffffff60a01b8254161790555f80f35b34610356575f36600319011261035657602060405160078152f35b34610356575f36600319011261035657602063ffffffff5f5416604051908152f35b34610356576020366003190112610356576004355f52601b602052602060ff60405f205416604051908152f35b34610356575f366003190112610356576020600c54604051908152f35b34610356575f366003190112610356576020601f54604051908152f35b34610356576020366003190112610356576020610a6b610a5d6104ff565b63ffffffff5f541690612326565b6040519015158152f35b34610356576020366003190112610356576001600160401b0363ffffffff6104fb815f5416805f52601760205260405f206004355f526020528260405f205416906104ae8161291f565b34610356576020366003190112610356576004355f526012602052602060018060a01b0360405f205416604051908152f35b34610356576020366003190112610356576004355f52601a602052602060ff60405f205416604051908152f35b604036600319011261035657610b326104ff565b6024356001600160401b03811161035657610b5190369060040161068f565b90610b5a6139ca565b610b643633612ac6565b610b6c6139ca565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610bf6575b50610bbe57604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f80516020614c1b8339815191528303610bdd576107aa9250614166565b604051632a87526960e21b815260048101849052602490fd5b610c1991945060203d602011610c20575b610c1181836105f9565b8101906125ae565b925f610b94565b503d610c07565b34610356576020366003190112610356576004355f526013602052602060018060a01b0360405f205416604051908152f35b34610356575f366003190112610356577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610cb05760206040515f80516020614c1b8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610356575f36600319011261035657602063ffffffff601e5460081c16604051908152f35b34610356575f36600319011261035657602063ffffffff60155416604051908152f35b3461035657602036600319011261035657610d246104ff565b610d2e3633612ac6565b600380546001600160a01b0319166001600160a01b0392909216919091179055005b34610356575f366003190112610356576020600254604051908152f35b34610356575f36600319011261035657602063ffffffff60115416604051908152f35b34610356575f366003190112610356576020600154604051908152f35b3461035657602036600319011261035657610dc66104ff565b5f80516020614bfb833981519152546001600160a01b03919082163303610e1057803b15610df7576107aa90612bdc565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b34610356576040366003190112610356576020610e77600435610e4981610348565b63ffffffff610e56610515565b91165f52600d835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b34610356576020366003190112610356577f2b797207c3f71ed55e7ee9f751f9d4e23795bff49d40a0d3d05e160e1257798d610eba6104ff565b610ec43633612ac6565b601680546001600160a01b039283166001600160a01b031982168117909255604080519283529216602082015290819081015b0390a1005b34610356575f36600319011261035657602060115460ff60405191831c1615158152f35b34610356576020366003190112610356576040610f47600435610f4281610348565b61228f565b63ffffffff83519216825215156020820152f35b34610356575f3660031901126103565760206001600160401b0360185416604051908152f35b346103565760203660031901126103565761ffff600435610fa1816108e1565b165f526004602052602060018060a01b0360405f205416604051908152f35b34610356575f366003190112610356575f80516020614bfb8339815191525460a01c60ff1615611008576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610ff5565b34610356575f3660031901126103565760206040516180008152f35b34610356575f36600319011261035657602060ff600f5460601c166040519015158152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b34610356575f366003190112610356576104fb6040516110948161057a565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611051565b34610356576020366003190112610356576004355f52601960205260206001600160401b0360405f205416604051908152f35b34610356576020366003190112610356576104fb60043561110d81610348565b60405f81805161111c81610595565b828152826020820152015263ffffffff8092165f5260146020526001815f206001600160401b0383519461114f86610595565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610356575f366003190112610356575f80516020614bfb833981519152546040516001600160a01b039091168152602090f35b34610356576020366003190112610356576004356111ea81610348565b6111f43633612ac6565b63ffffffff90816112206112168363ffffffff165f52601460205260405f2090565b5463ffffffff1690565b16156112c35761124461123f61123a5f5463ffffffff1690565b612c34565b612c6b565b828216928116831161129d575061126c6112779163ffffffff165f52601460205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b604051631cc9a6c760e21b815263ffffffff928316600482015291166024820152604490fd5b604051630529520560e31b815263ffffffff919091166004820152602490fd5b34610356575f366003190112610356576003546040516001600160a01b039091168152602090f35b634e487b7160e01b5f52603260045260245ffd5b6010548110156113415760105f525f80516020614c3b83398151915201905f90565b61130b565b8054821015611341575f5260205f2001905f90565b3461035657604036600319011261035657600435602435906010548110156103565760105f525f80516020614c3b833981519152018054821015610356576020916113a591611346565b905460405160039290921b1c6001600160a01b03168152f35b346103565760203660031901126103565763ffffffff6004356113e081610348565b165f5260208052608060405f205460ff604051918181161515835261ffff8160081c166020840152818160181c161515604084015260201c1615156060820152f35b34610356575f366003190112610356576020600f546001600160401b0360405191831c168152f35b3461035657602036600319011261035657606060043561146981610348565b63ffffffff8091165f52601460205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b34610356576040366003190112610356576020610a6b6114c26104ff565b602435906114cf82610348565b612326565b34610356576020366003190112610356576004356114f181610348565b6114fb3633612ac6565b6040516115078161055f565b63ffffffff908183168082521561157057601580549151831663ffffffff1983161790556040805163ffffffff93909216831682529290911660208201527f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69181908101610ef7565b604051639ca50c6160e01b8152600490fd5b34610356575f366003190112610356576020600b54604051908152f35b346103565760203660031901126103565763ffffffff6004356115c181610348565b6115cb3633612ac6565b1663ffffffff19600f541617600f555f80f35b34610356575f366003190112610356576016546040516001600160a01b039091168152602090f35b604051905f6009546001918160011c92600183169081156116eb575b6020906020861083146116d757858852879460208601939081156116b85750600114611658575b505050610365925003836105f9565b92509361168660095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af90565b945f935b8285106116a2575050506103659350015f8080611649565b865485850152958601958895509381019361168a565b925050506103659491925060ff19168252151560051b015f8080611649565b634e487b7160e01b5f52602260045260245ffd5b93607f1693611622565b604051906117028261057a565b60065460ff8116835260081c63ffffffff166020830152565b604051906117288261057a565b60075460ff8116835260081c63ffffffff166020830152565b926001600160401b03906117a760206117696106aa9a98969995610120808a52890190611051565b9861178a828901825163ffffffff6020809260ff8151168552015116910152565b0151805160ff1660608801526020015163ffffffff166080870152565b1660a084015263ffffffff80921660c08401521660e0820152610100818403910152611051565b34610356575f366003190112610356576040515f6005546001918160011c9260018316908115611906575b6020906020861083146116d757858752869460208601939081156118e75750600114611887575b8461182d818603826105f9565b6040516118398161057a565b6118416116f5565b815261184b61171b565b60208201526104fb60085463ffffffff611863611606565b916040519586956001600160401b03848460601c16948460401c1693169187611741565b9250936118b560055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db090565b945f935b8285106118d15750505061182d9350015f8080611820565b86548585015295860195879550938101936118b9565b9250505061182d9491925060ff19168252151560051b015f8080611820565b93607f16936117f9565b3461035657604036600319011261035657602060043561192f81610348565b63ffffffff8091165f526017825260405f206024355f52825260405f205416604051908152f35b9092919261197461196b5f5463ffffffff1690565b63ffffffff1690565b63ffffffff90818416908110611b7a5761199b8463ffffffff165f526020805260405f2090565b5460ff81169081611b6b575b50611ae7575f5463ffffffff169082821603611ac0575060115460ff8160201c16611a4b575b50506119e06119dc8333612326565b1590565b611a335783158015611a28575b611a0757611a019061036593943691610659565b906125d2565b604051630c6f042160e31b8152600481018590526180006024820152604490fd5b5061800084116119ed565b604051632af0051560e21b8152336004820152602490fd5b611a548461228f565b90506119cd57610bba84611a71601e5463ffffffff9060081c1690565b90611a8160105463ffffffff1690565b604051631bf926d360e11b815263ffffffff92831660048201529282166024840152939094168416604482015292909116606483015281906084820190565b6040516307d3e83d60e51b815263ffffffff91821660048201529084166024820152604490fd5b505092611b17611b058363ffffffff165f52600d60205260405f2090565b335f9081526020919091526040902090565b5493600b54809503611a3357611b2e913691610659565b908151602083012093808503611b4a57506103659293506123d8565b6040516340d6b4cd60e01b8152600481019190915260248101859052604490fd5b60ff915060201c16155f6119a7565b505050509050565b905f80516020614c5b8339815191529081549260ff8460401c1615936001600160401b03811680159081611c83575b6001149081611c79575b159081611c70575b50611c5e5767ffffffffffffffff1916600117909255611be79183611c3a57611c8b565b611bed57565b5f80516020614c5b833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1565b5f80516020614c5b833981519152805460ff60401b1916600160401b179055611c8b565b60405163f92ee8a960e01b8152600490fd5b9050155f611bc3565b303b159150611bbb565b869150611bb1565b611caf90929192611c9a61391b565b611ca261391b565b611caa61391b565b612bdc565b611cb761391b565b5f805463ffffffff19166001908117909155600f80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161790556040928381018051519182156121f357606081019063ffffffff9485611d1b845163ffffffff1690565b16156121ae5784611d3361196b845163ffffffff1690565b1015612169575f945f5b81811061210e5750611d4e8661296d565b5f805b838110611ed85750505f905f5b838110611e65575050505050611e3b92611de2611dcc7ff485fcf1271cbcd2319ebf860c827016cef79959673d605215dcd74ed0629ea4989994611dc2611dac611e1a965163ffffffff1690565b63ffffffff1663ffffffff196011541617601155565b5163ffffffff1690565b63ffffffff1663ffffffff19600f541617600f55565b611dfc64010000000064ff00000000196011541617601155565b611e0f64ffffffff0019601e5416601e55565b515163ffffffff1690565b905163ffffffff918216815293909216909116602083015281906040820190565b0390a1610365611e4961061a565b60c8815263ffffffff90511663ffffffff196015541617601555565b80611e7286928a51612959565b515151611e7d6129e4565b5050611e888261131f565b505f5b828110611e9b5750505001611d5e565b611ecd611ec7611eba611eb38a98959697989a6129d6565b9989612959565b516001600160a01b031690565b84612a1a565b019087939291611e8b565b80611ee586928a51612959565b515183905f845b611efa575b50505001611d51565b90919293825182101561210757506002611f2b611f25611f25611f1d8587612959565b515160ff1690565b60ff1690565b036120c2576020601481611f3f8486612959565b510151510361207d57611f61611f6791611f598486612959565b51015161299f565b60601c90565b6001600160a01b03811680156120375790849392918a955b888110611fb957505090611fb08592611fa1611f9a8a6129d6565b998b612959565b6001600160a01b039091169052565b01839493611eec565b9080929394959650611fdd611fd1611eba848d612959565b6001600160a01b031690565b14611ff0578a01908a9594939291611f7f565b50508f5163098d772360e21b815260206004820152601a60248201527f6475706c6963617465206f70657261746f7220616464726573730000000000006044820152606490fd5b508f5163098d772360e21b815260206004820152601860248201527f6f70657261746f722061646472657373206973207a65726f00000000000000006044820152606490fd5b8f5163098d772360e21b815260206004820181905260248201527f6f70657261746f722061646472657373206973206e6f742032302062797465736044820152606490fd5b8e5163098d772360e21b815260206004820152601e60248201527f6f70657261746f72206973206e6f7420616e2045564d206164647265737300006044820152606490fd5b9392611ef1565b9561211a878751612959565b51515190811561213657839161212f91612282565b9601611d3d565b8a5163098d772360e21b815260206004820152600b60248201526a0656d7074792067726f75760ac1b6044820152606490fd5b875163098d772360e21b815260206004820152601d60248201527f61637469766547726f7570496e646578206f7574206f662072616e67650000006044820152606490fd5b875163098d772360e21b815260206004820152601860248201527f65706f63684475726174696f6e536563206973207a65726f00000000000000006044820152606490fd5b855163098d772360e21b81526020600482015260096024820152686e6f2067726f75707360b81b6044820152606490fd5b634e487b7160e01b5f52601160045260245ffd5b5f1981019190821161224657565b612224565b9190820391821161224657565b906001820180921161224657565b906004820180921161224657565b906008820180921161224657565b9190820180921161224657565b60115460ff8160201c161561230a5760105490811561231d57601e546122bd9060081c63ffffffff1661196b565b9063ffffffff80941682811115612312576122e46122df6122eb94879361224b565b612238565b9116612282565b9081101561230a576122fc8161131f565b50541561230a571690600190565b50505f905f90565b50505050505f905f90565b5050505f905f90565b906123309061228f565b1561239e576010548110156113415760105f525f80516020614c3b83398151915201908154915f5b83811061236757505050505f90565b6123718183611346565b905460039190911b1c6001600160a01b039081169084161461239557600101612358565b50505050600190565b50505f90565b6040513d5f823e3d90fd5b90600161ffff8093160191821161224657565b91909161ffff8080941691160191821161224657565b906123f08263ffffffff165f526020805260405f2090565b90612400825460ff9060181c1690565b61259057600354612419906001600160a01b0316611fd1565b90813b1561035657604051905f82636744c79f60e01b9485825281838161244860048201906001602083019252565b03925af19081156125785761246e92869261257d575b50845460081c61ffff1690612f42565b60035490929190612487906001600160a01b0316611fd1565b91823b15610356575f9060405193849182528183816124ad60048201905f602083019252565b03925af1908115612578576124ec926124d49261255f575b50845460081c61ffff166123c2565b835462ffff00191660089190911b62ffff0016178355565b1561251657805463ff0000001916630100000017815561250e6119dc83612c93565b612516575050565b547f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc95009063ffffffff9060081c61ffff1660405161ffff909116815292169180602081015b0390a2565b8061256c612572926105b0565b80610444565b5f6124c5565b6123a4565b8061256c61258a926105b0565b5f61245e565b5061250e6119dc83612c93565b9060206106aa928181520190611051565b90816020910312610356575190565b63ffffffff8091169081146122465760010190565b91906125dd816131ab565b602091828083015101516125f2815160ff1690565b90600260ff831614801590612906575b6128d5575050606082019461261b865163ffffffff1690565b9063ffffffff808216908316036128af57505060a082015183815180155f146127ee57505050600254806127ce57505b612654826133ea565b9281519082012091612684611b05612670885163ffffffff1690565b63ffffffff165f52600d60205260405f2090565b5480156126d757830361269c576103659495506135dd565b610bba6126ad875163ffffffff1690565b60405163082085ab60e21b815263ffffffff90911660048201523360248201529081906044820190565b5061279a61196b61036596978561270e336126f9612670855163ffffffff1690565b9060018060a01b03165f5260205260405f2090565b55612761612737612723835163ffffffff1690565b63ffffffff165f52600e60205260405f2090565b61274d612748825463ffffffff1690565b6125bd565b63ffffffff1663ffffffff19825416179055565b611dc26127378761278d612779855163ffffffff1690565b63ffffffff165f52601760205260405f2090565b905f5260205260405f2090565b6040518481523391907fcafc00e7c35f462868d78e8228850e885bd0870978967c446c6bb67122a3fb2890602090a36135dd565b604051631d734b9160e01b815260048101919091525f6024820152604490fd5b03612873576040516347f5343160e01b815290848280612811846004830161259d565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4918215612578575f92612890575b508115612873575060025480820361285157505061264b565b604051631d734b9160e01b815260048101919091526024810191909152604490fd5b604051630ca3ddf160e01b8152908190610bba906004830161259d565b6128a8919250853d8711610c2057610c1181836105f9565b905f612838565b604051635070175360e01b815263ffffffff918216600482015291166024820152604490fd5b8401516040516385a89b3360e01b815246600482015260ff92909216602483015263ffffffff166044820152606490fd5b508085015163ffffffff1663ffffffff16461415612602565b6129289061228f565b15612954576010548110156113415760105f525f80516020614c3b833981519152015463ffffffff1690565b505f90565b80518210156113415760209160051b010190565b9061297782610627565b61298460405191826105f9565b8281528092612995601f1991610627565b0190602036910137565b90602082519201516bffffffffffffffffffffffff199081811693601481106129c757505050565b60140360031b82901b16169150565b5f1981146122465760010190565b601054600160401b81101561055a5760018101806010558110156113415760105f525f80516020614c3b83398151915201905f90565b8054600160401b81101561055a57612a3791600182018155611346565b60018060a09493941b038084549260031b9316831b921b1916179055565b600411610356575f90600490565b6001600160e01b03199035818116939260048110612a8057505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f80516020614bfb8339815191528054612afe906001600160a01b0316612af5612aef86612a55565b90612a63565b90843091613949565b9015612b0b575b50505050565b63ffffffff1615612bbc575f80516020614bfb833981519152805460ff60a01b1916600160a01b179055612b4c90611fd1908190546001600160a01b031690565b91823b1561035657612b77925f9283604051809681958294634a63ebf760e11b845260048401612a8f565b03925af1801561257857612ba9575b505f80516020614bfb833981519152805460ff60a01b191690555f808080612b05565b8061256c612bb6926105b0565b5f612b86565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f80516020614bfb83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b63ffffffff906001818316111561239e575f19011690565b63ffffffff91908282168382161115612c6457031690565b5050505f90565b6106aa9063ffffffff6015541690612c4c565b90600163ffffffff8093160191821161224657565b612c9c81613a52565b156129545760207fe784b22f2061de501b77364cab02ee109528afe4ddc8d423d076f6b49021b58e9163ffffffff612cf981831692835f5284805260405f2064010000000064ff0000000019825416179055600b54600255612c7e565b1663ffffffff195f5416175f55604051908152a1600190565b6020908181840312610356578051906001600160401b038211610356570182601f8201121561035657805191612d4783610627565b936040612d5760405196876105f9565b848652828601918360c080970286010194818611610356578401925b858410612d84575050505050505090565b8684830312610356578487918451612d9b816105c3565b8651612da6816108e1565b815282870151838201528587015186820152606080880151908201526080808801519082015260a08088015190820152815201930192612d73565b905f916040805191637d3b06bb60e11b83525f8380612e03846004830161259d565b038173__$583c08fff37be670b82ce66a4a046aa62c$__5af4928315612578575f93612f1e575b505f945b8351861015612f125761ffff811615801590612f0b575b80612eff575b612ef457612eec600191612ee78789612e7a612e73612e6a838c612959565b515161ffff1690565b61ffff1690565b612ea28a8a612e98856020612e8f8286612959565b51015193612959565b510151908a613b0a565b612ec28b60a0612eb8866080612e8f8286612959565b510151908b613b0a565b906001600160401b03612edf5a612ed95a60031c90565b9061224b565b941692613bff565b6123af565b950194612e2e565b945050505050905f90565b50620f42405a10612e4b565b505f612e45565b94505050505090600190565b612f3b9193503d805f833e612f3381836105f9565b810190612d12565b915f612e2a565b60408051637d3b06bb60e11b815292945f94939290858480612f67856004830161259d565b038173__$583c08fff37be670b82ce66a4a046aa62c$__5af4938415612578575f9461304c575b5061ffff8097169586905b855188101561303d5788811615801590613034575b80613028575b61301a57613012600191612ee7898b612fd3612e73612e6a838e612959565b612ff28c8c612fe8856020612e8f8286612959565b510151908c613b0a565b612ec28d60a0613008866080612e8f8286612959565b510151908d613b0a565b970196612f99565b965050955050505050905f90565b50620f42405a10612fb4565b50811515612fae565b96505095505050505090600190565b6130619194503d805f833e612f3381836105f9565b925f612f8e565b604051906130758261057a565b5f6020838281520152565b6040519061308d8261057a565b81613096613068565b815260206130a2613068565b910152565b604051906130b4826105de565b606060c0838281526130c4613080565b60208201525f60408201525f838201525f60808201528260a08201520152565b604051906130f1826105de565b606060c0836130fe613080565b81528260208201528260408201525f838201528260808201525f60a08201520152565b6040519061312e8261057a565b60606020835f81520152565b604051906131478261057a565b816131506130e4565b815260206130a2613121565b9061316682610627565b61317360405191826105f9565b8281528092613184601f1991610627565b01905f5b82811061319457505050565b60209061319f61313a565b82828501015201613188565b906131b46130a7565b915f8151905f805b83811061338457506131cd9061315c565b9160c086019283525f915b8181106131e6575050505050565b6131f09085613e14565b90816001600160401b0391828116600a81145f1461322157505050506132169085613fcd565b919087525b906131d8565b60128103613269575050506132396132629287613e14565b91169061325861325361324c8484612282565b838a613e58565b613ffc565b60208a0152612282565b905b61321b565b602881036132965750505050906132836132649286613e14565b6001600160401b03909116604089015291565b603081036132c65750505050906132646132b563ffffffff9387613e14565b9316606089019063ffffffff169052565b603881036132f65750505050906132646132e563ffffffff9387613e14565b9316608089019063ffffffff169052565b60a28103613318575050505061330c9085613fcd565b919060a088015261321b565b6101420361337457505061332f6132629287613e14565b91169061336e61335161334c6133458585612282565b848b613e58565b613f04565b95875161335d826129d6565b976133688383612959565b52612959565b50612282565b6132629350600716915086613d5a565b6133919085949394613e14565b6101426001600160401b0383949316146133bd575b60076133b59293169086613d5a565b9291926131bc565b60076133cb6133b5936129d6565b9250506133a6565b6040906106aa939281528160208201520190611051565b90601f54918215159060c080910191825151915f9073__$b91d2ad766fd07dc99f56c1a0b020615bb$__925b84831061342557505050505050565b613436838799939495969751612959565b5151602092838201519060408093015193858351036135c25784518681151591826135b7575b505061359c5783516347f5343160e01b9384825287828c818061348360049c8d830161259d565b03915af49485156125785788925f96613575575b50906134af92918751938492839283528a830161259d565b03818d5af4968715612578575f97613556575b50508061354c575b61352357506134da90851c612258565b83851c918183036134f957505050509560018093019190949392613416565b5163eb9ba7ad60e01b81529283018681526020810191909152604081019190915281906060010390fd5b9151630b296d8760e31b8152928301868152602081019290925260408201528190036060019150fd5b50808214156134ca565b61356d929750803d10610c2057610c1181836105f9565b945f806134c2565b8392919650613593906134af943d8511610c2057610c1181836105f9565b95909192613497565b8351632d9a492360e21b815280610bba878b600484016133d3565b14159050865f61345c565b8351635812b67560e01b815280610bba858b600484016133d3565b60609093919301916135fb6135f6845163ffffffff1690565b61291f565b63ffffffff80821690811561391257613634637fffffff61362a6112168761278d6127798c5163ffffffff1690565b9460011c16612c7e565b90600f54926136536001600160401b0394858482169160201c16612282565b421015918086169182109383613905575b5050508115916138fd575b50156138f557613729936136c16136c69261368986600b55565b600f80546cffffffffffffffffff000000001916429290921660201b6bffffffffffffffff000000001691909117600160601b179055565b601f55565b7fa77ed82a4a8c2f7b412630e92b1c8f59e6645b05820a8b0ed43ae5fac990491c6137196136fb61196b875163ffffffff1690565b6040805186815263ffffffff90951660208601529093918291820190565b0390a2825163ffffffff166140a8565b61373f61373a825163ffffffff1690565b61410e565b613763613750825163ffffffff1690565b63ffffffff165f526020805260405f2090565b805460ff1916600117815591600354613784906001600160a01b0316611fd1565b90813b1561035657604051905f82636744c79f60e01b948582528183816137b360048201906001602083019252565b03925af1918215612578576137d6926138e2575b50835163ffffffff1690612de1565b60035491949092916137f0906001600160a01b0316611fd1565b91823b15610356575f90604051938491825281838161381660048201905f602083019252565b03925af1908115612578576138559284926138cf575b50805463ffffff00191662ffff00600888901b161791151560181b63ff00000016919091179055565b1580156138b0575b613865575050565b61255a61389a61196b7f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc9500935163ffffffff1690565b60405161ffff9094168452929081906020820190565b506138ca6119dc6138c5835163ffffffff1690565b612c93565b61385d565b8061256c6138dc926105b0565b5f61382c565b8061256c6138ef926105b0565b5f6137c7565b505050505050565b90505f61366f565b16111590505f8080613664565b50505050505050565b60ff5f80516020614c5b8339815191525460401c161561393757565b604051631afcd79f60e31b8152600490fd5b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b0384111761055a576040935f9385528380528360205251915afa6139b857565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613a08575b5050610cb057565b5f80516020614c1b8339815191525416141590505f80613a00565b3d15613a4d573d90613a348261063e565b91613a4260405193846105f9565b82523d5f602084013e565b606090565b623d09005a1061295457600354613a71906001600160a01b0316611fd1565b803b156103565760405163a3ad9cc360e01b815263ffffffff9290921660048301525f908290602490829084905af19081613acf575b50613aca57613ab4613a23565b8051918215613ac35750602001fd5b9150505f90565b600190565b8061256c613adc926105b0565b5f613aa7565b90613aec8261063e565b613af960405191826105f9565b8281528092612995601f199161063e565b919080820391821161224657602090613b2283613ae2565b930101602083015e90565b5190811515820361035657565b90816020910312610356576106aa90613b2d565b91613b709061ffff604093969596168452606060208501526060840190611051565b6001600160a01b03909416910152565b9193926001600160401b039061ffff613ba3604094606087526060870190611051565b9616602085015216910152565b9160a093613be6613bf49261ffff6001600160401b03959a99989a6004885216602087015260c0604087015260c0860190611051565b908482036060860152611051565b951660808201520152565b91929461ffff83169061ee0f8214613d4a575061ee1314613d3c576016546040516359109f8560e01b815273__$a62ffa76e12530392778037a67b422bd63$__939160209082908190613c60906001600160a01b0316878760048501613b4e565b0381875af4908115612578575f91613d0d575b50613cbf57823b1561035657613ca15f956040519788968795869563173d424160e21b875260048701613bb0565b03915af4801561257857613cb25750565b8061256c610365926105b0565b6016547f6615e715ef29da98af4d4b50f6beb57ec1a6a61bb207b117e5fb58632755e93795509093925061255a9150613d00906001600160a01b0316611fd1565b9460405193849384613b80565b613d2f915060203d602011613d35575b613d2781836105f9565b810190613b3a565b5f613c73565b503d613d1d565b925050506103659150614492565b945050915050610365925061430d565b6001600160401b039283169283613d7b5750613d769250613e14565b905090565b919260018103613d91575050506106aa90612274565b60028103613daf57506106aa92613da791613e14565b911690612282565b600514159150613dc49050576106aa90612266565b60405162461bcd60e51b815260206004820152602260248201527f50726f746f62756652756e74696d653a20756e6b6e6f776e2077697265207479604482015261706560f01b6064820152608490fd5b91908201602001905f805b60806007600186515f1a94607f8616851b1796019201921615613e4b57603f8211610356579290613e1f565b9293909203601f19019150565b918181101580613ef9575b15613ea85781613e729161224b565b90613e7c82613ae2565b9282613e89575b50505090565b015f5b82811015613e8357602081830181015185830182015201613e8c565b60405162461bcd60e51b8152602060048201526024808201527f50726f746f62756652756e74696d653a20736c696365206f7574206f6620626f604482015263756e647360e01b6064820152608490fd5b508251811115613e63565b90613f0d61313a565b915f8151905b818110613f1f57505050565b613f299083613e14565b90816001600160401b0391828116600a81145f14613f7f57505050613f51613f779285613e14565b911690613f70613f6b613f648484612282565b8388613e58565b6146ec565b8752612282565b905b90613f13565b601203613fbd575050613f95613fb79285613e14565b911690613fad613fa8613f648484612282565b6145cf565b6020880152612282565b90613f79565b613fb79350600716915084613d5a565b9190613fe16001600160401b039184613e14565b911681019283821161224657613ff8918491613e58565b9190565b90614005613080565b915f8151905b81811061401757505050565b6140219083613e14565b90816001600160401b0391828116600a81145f14614069575050506140496140619285613e14565b911690613f7061405c613f648484612282565b61484f565b905b9061400b565b60120361409857505061407f6140929285613e14565b911690613fad61405c613f648484612282565b90614063565b6140929350600716915084613d5a565b63ffffffff8082165f52601460205260405f2090815416614109576140df600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b6141259063ffffffff918291826015541690612c4c565b1690811515908161414e575b506141395750565b5f5260146020525f6001604082208281550155565b9050815f52601460205260405f20541615155f614131565b90813b156141ea575f80516020614c1b83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28051156141cf576141cc916148e8565b50565b5050346141d857565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b908160a0910312610356576040519060a082018281106001600160401b0382111761055a576040528051906006821015610356576080918352602081015161425281610348565b6020840152604081015161426581610348565b604084015261427660608201613b2d565b6060840152015161428681610348565b608082015290565b916142a660809295949560a0855260a0850190611051565b94601060208501526012604085015263ffffffff809216606085015216910152565b600611156142d257565b634e487b7160e01b5f52602160045260245ffd5b63ffffffff9182168152911660208201526060810192919060068210156142d25760400152565b9060a061431f600f5463ffffffff1690565b601e5460081c63ffffffff169361434a6040519586938493632b6e75a560e11b85526004850161428e565b038173__$a62ffa76e12530392778037a67b422bd63$__5af4918215612578575f92614461575b50815161437d816142c8565b614386816142c8565b61441357506143f66080826143a8611dac602061036596015163ffffffff1690565b6143bc611dcc604083015163ffffffff1690565b6143eb6143cc6060830151151590565b64ff0000000060115491151560201b169064ff00000000191617601155565b015163ffffffff1690565b64ffffffff00601e549160081b169064ffffffff00191617601e55565b907f06bc5809fcff2cb3c29e300b687d3444b42c7836482312d880b48c4bcfbec92291611c3561444a608084015163ffffffff1690565b9251614455816142c8565b604051938493846142e6565b61448491925060a03d60a01161448b575b61447c81836105f9565b81019061420b565b905f614371565b503d614472565b6001600160401b03906001826018541601908282116122465773__$a62ffa76e12530392778037a67b422bd63$__90813b15610356575f916144f291604051809681948293637401af5d60e01b845260e0600485015260e4840190611051565b90601260248401526013604484015260196064840152601a6084840152601b60a4840152871660c483015203915af4918215612578576103659261454e575b506001600160401b03166001600160401b03196018541617601855565b8061256c61455b926105b0565b5f614531565b6040519061456e82610595565b60606040835f81525f60208201520152565b9061458a82610627565b61459760405191826105f9565b82815280926145a8601f1991610627565b01905f5b8281106145b857505050565b6020906145c3614561565b828285010152016145ac565b906145d8613121565b915f8151905f805b83811061469e57506145f190614580565b91602086019283525f915b81811061460a575050505050565b6146149085613e14565b90816001600160401b0391828116600881145f1461465757505050509061465161464363ffffffff9387613e14565b9316889063ffffffff169052565b906145fc565b60120361468e57505061466d6146889287613e14565b91169061336e6133516146836133458585612282565b614905565b90614651565b6146889350600716915086613d5a565b6146ab9085949394613e14565b60126001600160401b0383949316146146d6575b60076146ce9293169086613d5a565b9291926145e0565b60076146e46146ce936129d6565b9250506146bf565b906146f56130e4565b915f8151905b81811061470757505050565b6147119083613e14565b90816001600160401b0391828116600a81145f146147545750505061473961474c9285613e14565b911690613f70613253613f648484612282565b905b906146fb565b91925090601281036147795750505061476d9083613fcd565b9190602086015261474e565b601a810361479a5750505061478e9083613fcd565b9190604086015261474e565b602881036147ce57505050906147c96147b863ffffffff9385613e14565b9316606087019063ffffffff169052565b61474e565b603281036147ef575050506147e39083613fcd565b9190608086015261474e565b6038810361481b57505050906148086147c99284613e14565b6001600160401b0390911660a087015291565b60420361483a57505061482e9083613fcd565b919060c086015261474e565b61484992506007169084613d5a565b9061474e565b90614858613068565b915f8151905b81811061486a57505050565b6148749083613e14565b80916001600160401b038116600881145f146148ae575050509060ff61489d6148a49385613e14565b93906149d1565b1685525b9061485e565b6010036148d3575050906148c763ffffffff9284613e14565b921660208601526148a8565b6148e292506007169084613d5a565b906148a8565b5f806106aa93602081519101845af46148ff613a23565b91614a22565b9061490e614561565b915f8151905b81811061492057505050565b61492a9083613e14565b80916001600160401b038116600881145f14614969575050509061496361495461495b9385613e14565b9390614a85565b61ffff168652565b90614914565b6010810361499d575050509061499861498763ffffffff9385613e14565b9316602087019063ffffffff169052565b614963565b601a036149bc5750506149b09083613fcd565b91906040860152614963565b6149cb92506007169084613d5a565b90614963565b6001600160401b031680156129545760018114614a1c5760028114614a165760038114614a105760249060405190632cd3748760e21b82526004820152fd5b50600390565b50600290565b50600190565b90614a495750805115614a3757602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614a7c575b614a5a575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15614a52565b6001600160401b038116801561239e576107d1808214614bf45750610bb9808214614bf45750610bba808214614bf45750610bbc808214614bf45750610bbe808214614bf4575061aa00808214614bf4575061ee00808214614bf4575061ee02808214614bf4575061ee04808214614bf4575061ee06808214614bf4575061ee10808214614bf4575061ee11808214614bf4575061ee13808214614bf4575061ee0f808214614bf4575061ee15808214614bf4575061ee16808214614bf4575061ee17808214614bf4575061ee18808214614bf4575061ee19808214614bf4575061ee1b808214614bf4575061ee1c808214614bf4575061ee1e808214614bf4575061ee1f808214614bf4575061ee20808214614bf4575061ee21808214614bf4575061ee22808214614bf4575061ee23808214614bf4575061ee24808214614bf4575061ee25809114613d7657604051632cd3748760e21b81526001600160401b0383166004820152602490fd5b9150509056fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a264697066735822122010a68012febf8fe1754f31c152149d058698cd9a133878ab2a1ddcfd7b362bf164736f6c63430008190033", + "deployedBytecode": "0x60806040526004361015610011575f80fd5b5f3560e01c80624e356a146103435780630fa92c531461033e578063151b8f4e146103395780631bdf0a72146103345780631d7a88271461032f57806320857e651461032a57806322fe388b14610325578063239e1d67146103205780632945698b1461031b5780632c92892f146103165780632f4d9fa9146103115780633367cca51461030c57806336051135146103075780633915ba63146103025780634ab47f43146102fd5780634f1ef286146102f85780635126a52d146102f357806352d1902d146102ee578063533ae1cc146102e95780635701a93f146102e45780635c0c8f44146102df5780635d4e25ce146102da5780635ff8195e146102d5578063621fbd59146102d05780637a9e5e4b146102cb5780637b778122146102c65780637e283cea146102c157806380e643b6146102bc5780638b75e929146102b75780638bdebf21146102b25780638d9f616b146102ad5780638fb36037146102a857806393ec06be146102a357806399b1a19c1461029e578063ad3cb1cc14610299578063af243e8914610294578063b41565b81461028f578063bf7e214f1461028a578063c0d0373e14610285578063c268863f14610280578063c45fdcb71461027b578063cd2ad3fb14610276578063d0841f2214610271578063d1ad8d011461026c578063d3f0730614610267578063d5b5272a14610262578063e3dff2af1461025d578063e55ba57614610258578063ea8dbafe14610253578063f552501a1461024e5763fdfd07df14610249575f80fd5b611910565b6117ce565b6115de565b61159f565b611582565b6114d4565b6114a4565b61144a565b611422565b6113be565b61135b565b6112e3565b6111cd565b611199565b6110ed565b6110ba565b611075565b61102c565b611010565b610fc0565b610f81565b610f5b565b610f20565b610efc565b610e80565b610e27565b610dad565b610d90565b610d6d565b610d50565b610d0b565b610ce8565b610cc2565b610c59565b610c27565b610b1e565b610af1565b610abf565b610a75565b610a3f565b610a22565b610a05565b6109d8565b6109b6565b61099b565b6108ed565b6106ad565b610471565b61044e565b610409565b610367565b63ffffffff81160361035657565b5f80fd5b359061036582610348565b565b346103565760403660031901126103565760043561038481610348565b6001600160401b036024358181116103565736602382011215610356578060040135918211610356573660248383010111610356577f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0091825c6103f7575f9360246103f4936001865d0190611956565b5d005b604051633ee5aeb560e01b8152600490fd5b3461035657602036600319011261035657602060043561042881610348565b63ffffffff8091165f52600e825260405f205416604051908152f35b5f91031261035657565b34610356575f36600319011261035657602063ffffffff600f5416604051908152f35b34610356575f366003190112610356576001600160401b0363ffffffff6104fb815f5416805f52600e6020528260405f205416906104ae8161291f565b600f549260405196879685169460201c169286936001600160401b039060809497969260a087019863ffffffff8096818094168a5216602089015216604087015216606085015216910152565b0390f35b600435906001600160a01b038216820361035657565b602435906001600160a01b038216820361035657565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b0382111761055a57604052565b61052b565b602081019081106001600160401b0382111761055a57604052565b604081019081106001600160401b0382111761055a57604052565b606081019081106001600160401b0382111761055a57604052565b6001600160401b03811161055a57604052565b60c081019081106001600160401b0382111761055a57604052565b60e081019081106001600160401b0382111761055a57604052565b90601f801991011681019081106001600160401b0382111761055a57604052565b604051906103658261055f565b6001600160401b03811161055a5760051b60200190565b6001600160401b03811161055a57601f01601f191660200190565b9291926106658261063e565b9161067360405193846105f9565b829481845281830111610356578281602093845f960137010152565b9080601f83011215610356578160206106aa93359101610659565b90565b3461035657600319604036820112610356576106c76104ff565b602435916001600160401b03831161035657608090833603011261035657604051906106f28261053f565b826004013561070081610348565b8252602483013561071081610348565b60208301526044926044810135936001600160401b03851161035657366023868401011215610356576004858301013561074981610627565b9561075760405197886105f9565b81875260208701903660248460051b83880101011161035657602481860101915b60248460051b838801010183106107ac576107aa88886107a060648a8e60408601520161035a565b6060830152611b82565b005b82356001600160401b03811161035657828701016020602319823603011261035657604051906107db8261055f565b60248101356001600160401b0381116103565736604382840101121561035657602481830101359061080c82610627565b9261081a60405194856105f9565b8284526020840190368b8560051b858401010111610356578a83820101915b8b8560051b8584010101831061085f575050509183525050815260209283019201610778565b82356001600160401b038111610356576040838601820136036043190112610356576040519161088e8361057a565b8d8287860101013560ff81168103610356578352606482878601010135926001600160401b038411610356576108d28f9360209594869536928b8a0101010161068f565b83820152815201920191610839565b61ffff81160361035657565b346103565760403660031901126103565760043561090a816108e1565b61ffff610915610515565b916109203633612ac6565b165f818152600460209081526040918290205482516001600160a01b03868116825291821692810192909252929182917f46d568db0216a0215fe7f56360f9fa1a002c4bf0773add9cd62cd06eff1601899190a25f52600460205260405f2091166bffffffffffffffffffffffff60a01b8254161790555f80f35b34610356575f36600319011261035657602060405160078152f35b34610356575f36600319011261035657602063ffffffff5f5416604051908152f35b34610356576020366003190112610356576004355f52601b602052602060ff60405f205416604051908152f35b34610356575f366003190112610356576020600c54604051908152f35b34610356575f366003190112610356576020601f54604051908152f35b34610356576020366003190112610356576020610a6b610a5d6104ff565b63ffffffff5f541690612326565b6040519015158152f35b34610356576020366003190112610356576001600160401b0363ffffffff6104fb815f5416805f52601760205260405f206004355f526020528260405f205416906104ae8161291f565b34610356576020366003190112610356576004355f526012602052602060018060a01b0360405f205416604051908152f35b34610356576020366003190112610356576004355f52601a602052602060ff60405f205416604051908152f35b604036600319011261035657610b326104ff565b6024356001600160401b03811161035657610b5190369060040161068f565b90610b5a6139ca565b610b643633612ac6565b610b6c6139ca565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610bf6575b50610bbe57604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f80516020614c1b8339815191528303610bdd576107aa9250614166565b604051632a87526960e21b815260048101849052602490fd5b610c1991945060203d602011610c20575b610c1181836105f9565b8101906125ae565b925f610b94565b503d610c07565b34610356576020366003190112610356576004355f526013602052602060018060a01b0360405f205416604051908152f35b34610356575f366003190112610356577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610cb05760206040515f80516020614c1b8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610356575f36600319011261035657602063ffffffff601e5460081c16604051908152f35b34610356575f36600319011261035657602063ffffffff60155416604051908152f35b3461035657602036600319011261035657610d246104ff565b610d2e3633612ac6565b600380546001600160a01b0319166001600160a01b0392909216919091179055005b34610356575f366003190112610356576020600254604051908152f35b34610356575f36600319011261035657602063ffffffff60115416604051908152f35b34610356575f366003190112610356576020600154604051908152f35b3461035657602036600319011261035657610dc66104ff565b5f80516020614bfb833981519152546001600160a01b03919082163303610e1057803b15610df7576107aa90612bdc565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b34610356576040366003190112610356576020610e77600435610e4981610348565b63ffffffff610e56610515565b91165f52600d835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b34610356576020366003190112610356577f2b797207c3f71ed55e7ee9f751f9d4e23795bff49d40a0d3d05e160e1257798d610eba6104ff565b610ec43633612ac6565b601680546001600160a01b039283166001600160a01b031982168117909255604080519283529216602082015290819081015b0390a1005b34610356575f36600319011261035657602060115460ff60405191831c1615158152f35b34610356576020366003190112610356576040610f47600435610f4281610348565b61228f565b63ffffffff83519216825215156020820152f35b34610356575f3660031901126103565760206001600160401b0360185416604051908152f35b346103565760203660031901126103565761ffff600435610fa1816108e1565b165f526004602052602060018060a01b0360405f205416604051908152f35b34610356575f366003190112610356575f80516020614bfb8339815191525460a01c60ff1615611008576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610ff5565b34610356575f3660031901126103565760206040516180008152f35b34610356575f36600319011261035657602060ff600f5460601c166040519015158152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b34610356575f366003190112610356576104fb6040516110948161057a565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611051565b34610356576020366003190112610356576004355f52601960205260206001600160401b0360405f205416604051908152f35b34610356576020366003190112610356576104fb60043561110d81610348565b60405f81805161111c81610595565b828152826020820152015263ffffffff8092165f5260146020526001815f206001600160401b0383519461114f86610595565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610356575f366003190112610356575f80516020614bfb833981519152546040516001600160a01b039091168152602090f35b34610356576020366003190112610356576004356111ea81610348565b6111f43633612ac6565b63ffffffff90816112206112168363ffffffff165f52601460205260405f2090565b5463ffffffff1690565b16156112c35761124461123f61123a5f5463ffffffff1690565b612c34565b612c6b565b828216928116831161129d575061126c6112779163ffffffff165f52601460205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b604051631cc9a6c760e21b815263ffffffff928316600482015291166024820152604490fd5b604051630529520560e31b815263ffffffff919091166004820152602490fd5b34610356575f366003190112610356576003546040516001600160a01b039091168152602090f35b634e487b7160e01b5f52603260045260245ffd5b6010548110156113415760105f525f80516020614c3b83398151915201905f90565b61130b565b8054821015611341575f5260205f2001905f90565b3461035657604036600319011261035657600435602435906010548110156103565760105f525f80516020614c3b833981519152018054821015610356576020916113a591611346565b905460405160039290921b1c6001600160a01b03168152f35b346103565760203660031901126103565763ffffffff6004356113e081610348565b165f5260208052608060405f205460ff604051918181161515835261ffff8160081c166020840152818160181c161515604084015260201c1615156060820152f35b34610356575f366003190112610356576020600f546001600160401b0360405191831c168152f35b3461035657602036600319011261035657606060043561146981610348565b63ffffffff8091165f52601460205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b34610356576040366003190112610356576020610a6b6114c26104ff565b602435906114cf82610348565b612326565b34610356576020366003190112610356576004356114f181610348565b6114fb3633612ac6565b6040516115078161055f565b63ffffffff908183168082521561157057601580549151831663ffffffff1983161790556040805163ffffffff93909216831682529290911660208201527f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69181908101610ef7565b604051639ca50c6160e01b8152600490fd5b34610356575f366003190112610356576020600b54604051908152f35b346103565760203660031901126103565763ffffffff6004356115c181610348565b6115cb3633612ac6565b1663ffffffff19600f541617600f555f80f35b34610356575f366003190112610356576016546040516001600160a01b039091168152602090f35b604051905f6009546001918160011c92600183169081156116eb575b6020906020861083146116d757858852879460208601939081156116b85750600114611658575b505050610365925003836105f9565b92509361168660095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af90565b945f935b8285106116a2575050506103659350015f8080611649565b865485850152958601958895509381019361168a565b925050506103659491925060ff19168252151560051b015f8080611649565b634e487b7160e01b5f52602260045260245ffd5b93607f1693611622565b604051906117028261057a565b60065460ff8116835260081c63ffffffff166020830152565b604051906117288261057a565b60075460ff8116835260081c63ffffffff166020830152565b926001600160401b03906117a760206117696106aa9a98969995610120808a52890190611051565b9861178a828901825163ffffffff6020809260ff8151168552015116910152565b0151805160ff1660608801526020015163ffffffff166080870152565b1660a084015263ffffffff80921660c08401521660e0820152610100818403910152611051565b34610356575f366003190112610356576040515f6005546001918160011c9260018316908115611906575b6020906020861083146116d757858752869460208601939081156118e75750600114611887575b8461182d818603826105f9565b6040516118398161057a565b6118416116f5565b815261184b61171b565b60208201526104fb60085463ffffffff611863611606565b916040519586956001600160401b03848460601c16948460401c1693169187611741565b9250936118b560055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db090565b945f935b8285106118d15750505061182d9350015f8080611820565b86548585015295860195879550938101936118b9565b9250505061182d9491925060ff19168252151560051b015f8080611820565b93607f16936117f9565b3461035657604036600319011261035657602060043561192f81610348565b63ffffffff8091165f526017825260405f206024355f52825260405f205416604051908152f35b9092919261197461196b5f5463ffffffff1690565b63ffffffff1690565b63ffffffff90818416908110611b7a5761199b8463ffffffff165f526020805260405f2090565b5460ff81169081611b6b575b50611ae7575f5463ffffffff169082821603611ac0575060115460ff8160201c16611a4b575b50506119e06119dc8333612326565b1590565b611a335783158015611a28575b611a0757611a019061036593943691610659565b906125d2565b604051630c6f042160e31b8152600481018590526180006024820152604490fd5b5061800084116119ed565b604051632af0051560e21b8152336004820152602490fd5b611a548461228f565b90506119cd57610bba84611a71601e5463ffffffff9060081c1690565b90611a8160105463ffffffff1690565b604051631bf926d360e11b815263ffffffff92831660048201529282166024840152939094168416604482015292909116606483015281906084820190565b6040516307d3e83d60e51b815263ffffffff91821660048201529084166024820152604490fd5b505092611b17611b058363ffffffff165f52600d60205260405f2090565b335f9081526020919091526040902090565b5493600b54809503611a3357611b2e913691610659565b908151602083012093808503611b4a57506103659293506123d8565b6040516340d6b4cd60e01b8152600481019190915260248101859052604490fd5b60ff915060201c16155f6119a7565b505050509050565b905f80516020614c5b8339815191529081549260ff8460401c1615936001600160401b03811680159081611c83575b6001149081611c79575b159081611c70575b50611c5e5767ffffffffffffffff1916600117909255611be79183611c3a57611c8b565b611bed57565b5f80516020614c5b833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1565b5f80516020614c5b833981519152805460ff60401b1916600160401b179055611c8b565b60405163f92ee8a960e01b8152600490fd5b9050155f611bc3565b303b159150611bbb565b869150611bb1565b611caf90929192611c9a61391b565b611ca261391b565b611caa61391b565b612bdc565b611cb761391b565b5f805463ffffffff19166001908117909155600f80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161790556040928381018051519182156121f357606081019063ffffffff9485611d1b845163ffffffff1690565b16156121ae5784611d3361196b845163ffffffff1690565b1015612169575f945f5b81811061210e5750611d4e8661296d565b5f805b838110611ed85750505f905f5b838110611e65575050505050611e3b92611de2611dcc7ff485fcf1271cbcd2319ebf860c827016cef79959673d605215dcd74ed0629ea4989994611dc2611dac611e1a965163ffffffff1690565b63ffffffff1663ffffffff196011541617601155565b5163ffffffff1690565b63ffffffff1663ffffffff19600f541617600f55565b611dfc64010000000064ff00000000196011541617601155565b611e0f64ffffffff0019601e5416601e55565b515163ffffffff1690565b905163ffffffff918216815293909216909116602083015281906040820190565b0390a1610365611e4961061a565b60c8815263ffffffff90511663ffffffff196015541617601555565b80611e7286928a51612959565b515151611e7d6129e4565b5050611e888261131f565b505f5b828110611e9b5750505001611d5e565b611ecd611ec7611eba611eb38a98959697989a6129d6565b9989612959565b516001600160a01b031690565b84612a1a565b019087939291611e8b565b80611ee586928a51612959565b515183905f845b611efa575b50505001611d51565b90919293825182101561210757506002611f2b611f25611f25611f1d8587612959565b515160ff1690565b60ff1690565b036120c2576020601481611f3f8486612959565b510151510361207d57611f61611f6791611f598486612959565b51015161299f565b60601c90565b6001600160a01b03811680156120375790849392918a955b888110611fb957505090611fb08592611fa1611f9a8a6129d6565b998b612959565b6001600160a01b039091169052565b01839493611eec565b9080929394959650611fdd611fd1611eba848d612959565b6001600160a01b031690565b14611ff0578a01908a9594939291611f7f565b50508f5163098d772360e21b815260206004820152601a60248201527f6475706c6963617465206f70657261746f7220616464726573730000000000006044820152606490fd5b508f5163098d772360e21b815260206004820152601860248201527f6f70657261746f722061646472657373206973207a65726f00000000000000006044820152606490fd5b8f5163098d772360e21b815260206004820181905260248201527f6f70657261746f722061646472657373206973206e6f742032302062797465736044820152606490fd5b8e5163098d772360e21b815260206004820152601e60248201527f6f70657261746f72206973206e6f7420616e2045564d206164647265737300006044820152606490fd5b9392611ef1565b9561211a878751612959565b51515190811561213657839161212f91612282565b9601611d3d565b8a5163098d772360e21b815260206004820152600b60248201526a0656d7074792067726f75760ac1b6044820152606490fd5b875163098d772360e21b815260206004820152601d60248201527f61637469766547726f7570496e646578206f7574206f662072616e67650000006044820152606490fd5b875163098d772360e21b815260206004820152601860248201527f65706f63684475726174696f6e536563206973207a65726f00000000000000006044820152606490fd5b855163098d772360e21b81526020600482015260096024820152686e6f2067726f75707360b81b6044820152606490fd5b634e487b7160e01b5f52601160045260245ffd5b5f1981019190821161224657565b612224565b9190820391821161224657565b906001820180921161224657565b906004820180921161224657565b906008820180921161224657565b9190820180921161224657565b60115460ff8160201c161561230a5760105490811561231d57601e546122bd9060081c63ffffffff1661196b565b9063ffffffff80941682811115612312576122e46122df6122eb94879361224b565b612238565b9116612282565b9081101561230a576122fc8161131f565b50541561230a571690600190565b50505f905f90565b50505050505f905f90565b5050505f905f90565b906123309061228f565b1561239e576010548110156113415760105f525f80516020614c3b83398151915201908154915f5b83811061236757505050505f90565b6123718183611346565b905460039190911b1c6001600160a01b039081169084161461239557600101612358565b50505050600190565b50505f90565b6040513d5f823e3d90fd5b90600161ffff8093160191821161224657565b91909161ffff8080941691160191821161224657565b906123f08263ffffffff165f526020805260405f2090565b90612400825460ff9060181c1690565b61259057600354612419906001600160a01b0316611fd1565b90813b1561035657604051905f82636744c79f60e01b9485825281838161244860048201906001602083019252565b03925af19081156125785761246e92869261257d575b50845460081c61ffff1690612f42565b60035490929190612487906001600160a01b0316611fd1565b91823b15610356575f9060405193849182528183816124ad60048201905f602083019252565b03925af1908115612578576124ec926124d49261255f575b50845460081c61ffff166123c2565b835462ffff00191660089190911b62ffff0016178355565b1561251657805463ff0000001916630100000017815561250e6119dc83612c93565b612516575050565b547f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc95009063ffffffff9060081c61ffff1660405161ffff909116815292169180602081015b0390a2565b8061256c612572926105b0565b80610444565b5f6124c5565b6123a4565b8061256c61258a926105b0565b5f61245e565b5061250e6119dc83612c93565b9060206106aa928181520190611051565b90816020910312610356575190565b63ffffffff8091169081146122465760010190565b91906125dd816131ab565b602091828083015101516125f2815160ff1690565b90600260ff831614801590612906575b6128d5575050606082019461261b865163ffffffff1690565b9063ffffffff808216908316036128af57505060a082015183815180155f146127ee57505050600254806127ce57505b612654826133ea565b9281519082012091612684611b05612670885163ffffffff1690565b63ffffffff165f52600d60205260405f2090565b5480156126d757830361269c576103659495506135dd565b610bba6126ad875163ffffffff1690565b60405163082085ab60e21b815263ffffffff90911660048201523360248201529081906044820190565b5061279a61196b61036596978561270e336126f9612670855163ffffffff1690565b9060018060a01b03165f5260205260405f2090565b55612761612737612723835163ffffffff1690565b63ffffffff165f52600e60205260405f2090565b61274d612748825463ffffffff1690565b6125bd565b63ffffffff1663ffffffff19825416179055565b611dc26127378761278d612779855163ffffffff1690565b63ffffffff165f52601760205260405f2090565b905f5260205260405f2090565b6040518481523391907fcafc00e7c35f462868d78e8228850e885bd0870978967c446c6bb67122a3fb2890602090a36135dd565b604051631d734b9160e01b815260048101919091525f6024820152604490fd5b03612873576040516347f5343160e01b815290848280612811846004830161259d565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4918215612578575f92612890575b508115612873575060025480820361285157505061264b565b604051631d734b9160e01b815260048101919091526024810191909152604490fd5b604051630ca3ddf160e01b8152908190610bba906004830161259d565b6128a8919250853d8711610c2057610c1181836105f9565b905f612838565b604051635070175360e01b815263ffffffff918216600482015291166024820152604490fd5b8401516040516385a89b3360e01b815246600482015260ff92909216602483015263ffffffff166044820152606490fd5b508085015163ffffffff1663ffffffff16461415612602565b6129289061228f565b15612954576010548110156113415760105f525f80516020614c3b833981519152015463ffffffff1690565b505f90565b80518210156113415760209160051b010190565b9061297782610627565b61298460405191826105f9565b8281528092612995601f1991610627565b0190602036910137565b90602082519201516bffffffffffffffffffffffff199081811693601481106129c757505050565b60140360031b82901b16169150565b5f1981146122465760010190565b601054600160401b81101561055a5760018101806010558110156113415760105f525f80516020614c3b83398151915201905f90565b8054600160401b81101561055a57612a3791600182018155611346565b60018060a09493941b038084549260031b9316831b921b1916179055565b600411610356575f90600490565b6001600160e01b03199035818116939260048110612a8057505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f80516020614bfb8339815191528054612afe906001600160a01b0316612af5612aef86612a55565b90612a63565b90843091613949565b9015612b0b575b50505050565b63ffffffff1615612bbc575f80516020614bfb833981519152805460ff60a01b1916600160a01b179055612b4c90611fd1908190546001600160a01b031690565b91823b1561035657612b77925f9283604051809681958294634a63ebf760e11b845260048401612a8f565b03925af1801561257857612ba9575b505f80516020614bfb833981519152805460ff60a01b191690555f808080612b05565b8061256c612bb6926105b0565b5f612b86565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f80516020614bfb83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b63ffffffff906001818316111561239e575f19011690565b63ffffffff91908282168382161115612c6457031690565b5050505f90565b6106aa9063ffffffff6015541690612c4c565b90600163ffffffff8093160191821161224657565b612c9c81613a52565b156129545760207fe784b22f2061de501b77364cab02ee109528afe4ddc8d423d076f6b49021b58e9163ffffffff612cf981831692835f5284805260405f2064010000000064ff0000000019825416179055600b54600255612c7e565b1663ffffffff195f5416175f55604051908152a1600190565b6020908181840312610356578051906001600160401b038211610356570182601f8201121561035657805191612d4783610627565b936040612d5760405196876105f9565b848652828601918360c080970286010194818611610356578401925b858410612d84575050505050505090565b8684830312610356578487918451612d9b816105c3565b8651612da6816108e1565b815282870151838201528587015186820152606080880151908201526080808801519082015260a08088015190820152815201930192612d73565b905f916040805191637d3b06bb60e11b83525f8380612e03846004830161259d565b038173__$583c08fff37be670b82ce66a4a046aa62c$__5af4928315612578575f93612f1e575b505f945b8351861015612f125761ffff811615801590612f0b575b80612eff575b612ef457612eec600191612ee78789612e7a612e73612e6a838c612959565b515161ffff1690565b61ffff1690565b612ea28a8a612e98856020612e8f8286612959565b51015193612959565b510151908a613b0a565b612ec28b60a0612eb8866080612e8f8286612959565b510151908b613b0a565b906001600160401b03612edf5a612ed95a60031c90565b9061224b565b941692613bff565b6123af565b950194612e2e565b945050505050905f90565b50620f42405a10612e4b565b505f612e45565b94505050505090600190565b612f3b9193503d805f833e612f3381836105f9565b810190612d12565b915f612e2a565b60408051637d3b06bb60e11b815292945f94939290858480612f67856004830161259d565b038173__$583c08fff37be670b82ce66a4a046aa62c$__5af4938415612578575f9461304c575b5061ffff8097169586905b855188101561303d5788811615801590613034575b80613028575b61301a57613012600191612ee7898b612fd3612e73612e6a838e612959565b612ff28c8c612fe8856020612e8f8286612959565b510151908c613b0a565b612ec28d60a0613008866080612e8f8286612959565b510151908d613b0a565b970196612f99565b965050955050505050905f90565b50620f42405a10612fb4565b50811515612fae565b96505095505050505090600190565b6130619194503d805f833e612f3381836105f9565b925f612f8e565b604051906130758261057a565b5f6020838281520152565b6040519061308d8261057a565b81613096613068565b815260206130a2613068565b910152565b604051906130b4826105de565b606060c0838281526130c4613080565b60208201525f60408201525f838201525f60808201528260a08201520152565b604051906130f1826105de565b606060c0836130fe613080565b81528260208201528260408201525f838201528260808201525f60a08201520152565b6040519061312e8261057a565b60606020835f81520152565b604051906131478261057a565b816131506130e4565b815260206130a2613121565b9061316682610627565b61317360405191826105f9565b8281528092613184601f1991610627565b01905f5b82811061319457505050565b60209061319f61313a565b82828501015201613188565b906131b46130a7565b915f8151905f805b83811061338457506131cd9061315c565b9160c086019283525f915b8181106131e6575050505050565b6131f09085613e14565b90816001600160401b0391828116600a81145f1461322157505050506132169085613fcd565b919087525b906131d8565b60128103613269575050506132396132629287613e14565b91169061325861325361324c8484612282565b838a613e58565b613ffc565b60208a0152612282565b905b61321b565b602881036132965750505050906132836132649286613e14565b6001600160401b03909116604089015291565b603081036132c65750505050906132646132b563ffffffff9387613e14565b9316606089019063ffffffff169052565b603881036132f65750505050906132646132e563ffffffff9387613e14565b9316608089019063ffffffff169052565b60a28103613318575050505061330c9085613fcd565b919060a088015261321b565b6101420361337457505061332f6132629287613e14565b91169061336e61335161334c6133458585612282565b848b613e58565b613f04565b95875161335d826129d6565b976133688383612959565b52612959565b50612282565b6132629350600716915086613d5a565b6133919085949394613e14565b6101426001600160401b0383949316146133bd575b60076133b59293169086613d5a565b9291926131bc565b60076133cb6133b5936129d6565b9250506133a6565b6040906106aa939281528160208201520190611051565b90601f54918215159060c080910191825151915f9073__$b91d2ad766fd07dc99f56c1a0b020615bb$__925b84831061342557505050505050565b613436838799939495969751612959565b5151602092838201519060408093015193858351036135c25784518681151591826135b7575b505061359c5783516347f5343160e01b9384825287828c818061348360049c8d830161259d565b03915af49485156125785788925f96613575575b50906134af92918751938492839283528a830161259d565b03818d5af4968715612578575f97613556575b50508061354c575b61352357506134da90851c612258565b83851c918183036134f957505050509560018093019190949392613416565b5163eb9ba7ad60e01b81529283018681526020810191909152604081019190915281906060010390fd5b9151630b296d8760e31b8152928301868152602081019290925260408201528190036060019150fd5b50808214156134ca565b61356d929750803d10610c2057610c1181836105f9565b945f806134c2565b8392919650613593906134af943d8511610c2057610c1181836105f9565b95909192613497565b8351632d9a492360e21b815280610bba878b600484016133d3565b14159050865f61345c565b8351635812b67560e01b815280610bba858b600484016133d3565b60609093919301916135fb6135f6845163ffffffff1690565b61291f565b63ffffffff80821690811561391257613634637fffffff61362a6112168761278d6127798c5163ffffffff1690565b9460011c16612c7e565b90600f54926136536001600160401b0394858482169160201c16612282565b421015918086169182109383613905575b5050508115916138fd575b50156138f557613729936136c16136c69261368986600b55565b600f80546cffffffffffffffffff000000001916429290921660201b6bffffffffffffffff000000001691909117600160601b179055565b601f55565b7fa77ed82a4a8c2f7b412630e92b1c8f59e6645b05820a8b0ed43ae5fac990491c6137196136fb61196b875163ffffffff1690565b6040805186815263ffffffff90951660208601529093918291820190565b0390a2825163ffffffff166140a8565b61373f61373a825163ffffffff1690565b61410e565b613763613750825163ffffffff1690565b63ffffffff165f526020805260405f2090565b805460ff1916600117815591600354613784906001600160a01b0316611fd1565b90813b1561035657604051905f82636744c79f60e01b948582528183816137b360048201906001602083019252565b03925af1918215612578576137d6926138e2575b50835163ffffffff1690612de1565b60035491949092916137f0906001600160a01b0316611fd1565b91823b15610356575f90604051938491825281838161381660048201905f602083019252565b03925af1908115612578576138559284926138cf575b50805463ffffff00191662ffff00600888901b161791151560181b63ff00000016919091179055565b1580156138b0575b613865575050565b61255a61389a61196b7f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc9500935163ffffffff1690565b60405161ffff9094168452929081906020820190565b506138ca6119dc6138c5835163ffffffff1690565b612c93565b61385d565b8061256c6138dc926105b0565b5f61382c565b8061256c6138ef926105b0565b5f6137c7565b505050505050565b90505f61366f565b16111590505f8080613664565b50505050505050565b60ff5f80516020614c5b8339815191525460401c161561393757565b604051631afcd79f60e31b8152600490fd5b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b0384111761055a576040935f9385528380528360205251915afa6139b857565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613a08575b5050610cb057565b5f80516020614c1b8339815191525416141590505f80613a00565b3d15613a4d573d90613a348261063e565b91613a4260405193846105f9565b82523d5f602084013e565b606090565b623d09005a1061295457600354613a71906001600160a01b0316611fd1565b803b156103565760405163a3ad9cc360e01b815263ffffffff9290921660048301525f908290602490829084905af19081613acf575b50613aca57613ab4613a23565b8051918215613ac35750602001fd5b9150505f90565b600190565b8061256c613adc926105b0565b5f613aa7565b90613aec8261063e565b613af960405191826105f9565b8281528092612995601f199161063e565b919080820391821161224657602090613b2283613ae2565b930101602083015e90565b5190811515820361035657565b90816020910312610356576106aa90613b2d565b91613b709061ffff604093969596168452606060208501526060840190611051565b6001600160a01b03909416910152565b9193926001600160401b039061ffff613ba3604094606087526060870190611051565b9616602085015216910152565b9160a093613be6613bf49261ffff6001600160401b03959a99989a6004885216602087015260c0604087015260c0860190611051565b908482036060860152611051565b951660808201520152565b91929461ffff83169061ee0f8214613d4a575061ee1314613d3c576016546040516359109f8560e01b815273__$a62ffa76e12530392778037a67b422bd63$__939160209082908190613c60906001600160a01b0316878760048501613b4e565b0381875af4908115612578575f91613d0d575b50613cbf57823b1561035657613ca15f956040519788968795869563173d424160e21b875260048701613bb0565b03915af4801561257857613cb25750565b8061256c610365926105b0565b6016547f6615e715ef29da98af4d4b50f6beb57ec1a6a61bb207b117e5fb58632755e93795509093925061255a9150613d00906001600160a01b0316611fd1565b9460405193849384613b80565b613d2f915060203d602011613d35575b613d2781836105f9565b810190613b3a565b5f613c73565b503d613d1d565b925050506103659150614492565b945050915050610365925061430d565b6001600160401b039283169283613d7b5750613d769250613e14565b905090565b919260018103613d91575050506106aa90612274565b60028103613daf57506106aa92613da791613e14565b911690612282565b600514159150613dc49050576106aa90612266565b60405162461bcd60e51b815260206004820152602260248201527f50726f746f62756652756e74696d653a20756e6b6e6f776e2077697265207479604482015261706560f01b6064820152608490fd5b91908201602001905f805b60806007600186515f1a94607f8616851b1796019201921615613e4b57603f8211610356579290613e1f565b9293909203601f19019150565b918181101580613ef9575b15613ea85781613e729161224b565b90613e7c82613ae2565b9282613e89575b50505090565b015f5b82811015613e8357602081830181015185830182015201613e8c565b60405162461bcd60e51b8152602060048201526024808201527f50726f746f62756652756e74696d653a20736c696365206f7574206f6620626f604482015263756e647360e01b6064820152608490fd5b508251811115613e63565b90613f0d61313a565b915f8151905b818110613f1f57505050565b613f299083613e14565b90816001600160401b0391828116600a81145f14613f7f57505050613f51613f779285613e14565b911690613f70613f6b613f648484612282565b8388613e58565b6146ec565b8752612282565b905b90613f13565b601203613fbd575050613f95613fb79285613e14565b911690613fad613fa8613f648484612282565b6145cf565b6020880152612282565b90613f79565b613fb79350600716915084613d5a565b9190613fe16001600160401b039184613e14565b911681019283821161224657613ff8918491613e58565b9190565b90614005613080565b915f8151905b81811061401757505050565b6140219083613e14565b90816001600160401b0391828116600a81145f14614069575050506140496140619285613e14565b911690613f7061405c613f648484612282565b61484f565b905b9061400b565b60120361409857505061407f6140929285613e14565b911690613fad61405c613f648484612282565b90614063565b6140929350600716915084613d5a565b63ffffffff8082165f52601460205260405f2090815416614109576140df600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b6141259063ffffffff918291826015541690612c4c565b1690811515908161414e575b506141395750565b5f5260146020525f6001604082208281550155565b9050815f52601460205260405f20541615155f614131565b90813b156141ea575f80516020614c1b83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28051156141cf576141cc916148e8565b50565b5050346141d857565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b908160a0910312610356576040519060a082018281106001600160401b0382111761055a576040528051906006821015610356576080918352602081015161425281610348565b6020840152604081015161426581610348565b604084015261427660608201613b2d565b6060840152015161428681610348565b608082015290565b916142a660809295949560a0855260a0850190611051565b94601060208501526012604085015263ffffffff809216606085015216910152565b600611156142d257565b634e487b7160e01b5f52602160045260245ffd5b63ffffffff9182168152911660208201526060810192919060068210156142d25760400152565b9060a061431f600f5463ffffffff1690565b601e5460081c63ffffffff169361434a6040519586938493632b6e75a560e11b85526004850161428e565b038173__$a62ffa76e12530392778037a67b422bd63$__5af4918215612578575f92614461575b50815161437d816142c8565b614386816142c8565b61441357506143f66080826143a8611dac602061036596015163ffffffff1690565b6143bc611dcc604083015163ffffffff1690565b6143eb6143cc6060830151151590565b64ff0000000060115491151560201b169064ff00000000191617601155565b015163ffffffff1690565b64ffffffff00601e549160081b169064ffffffff00191617601e55565b907f06bc5809fcff2cb3c29e300b687d3444b42c7836482312d880b48c4bcfbec92291611c3561444a608084015163ffffffff1690565b9251614455816142c8565b604051938493846142e6565b61448491925060a03d60a01161448b575b61447c81836105f9565b81019061420b565b905f614371565b503d614472565b6001600160401b03906001826018541601908282116122465773__$a62ffa76e12530392778037a67b422bd63$__90813b15610356575f916144f291604051809681948293637401af5d60e01b845260e0600485015260e4840190611051565b90601260248401526013604484015260196064840152601a6084840152601b60a4840152871660c483015203915af4918215612578576103659261454e575b506001600160401b03166001600160401b03196018541617601855565b8061256c61455b926105b0565b5f614531565b6040519061456e82610595565b60606040835f81525f60208201520152565b9061458a82610627565b61459760405191826105f9565b82815280926145a8601f1991610627565b01905f5b8281106145b857505050565b6020906145c3614561565b828285010152016145ac565b906145d8613121565b915f8151905f805b83811061469e57506145f190614580565b91602086019283525f915b81811061460a575050505050565b6146149085613e14565b90816001600160401b0391828116600881145f1461465757505050509061465161464363ffffffff9387613e14565b9316889063ffffffff169052565b906145fc565b60120361468e57505061466d6146889287613e14565b91169061336e6133516146836133458585612282565b614905565b90614651565b6146889350600716915086613d5a565b6146ab9085949394613e14565b60126001600160401b0383949316146146d6575b60076146ce9293169086613d5a565b9291926145e0565b60076146e46146ce936129d6565b9250506146bf565b906146f56130e4565b915f8151905b81811061470757505050565b6147119083613e14565b90816001600160401b0391828116600a81145f146147545750505061473961474c9285613e14565b911690613f70613253613f648484612282565b905b906146fb565b91925090601281036147795750505061476d9083613fcd565b9190602086015261474e565b601a810361479a5750505061478e9083613fcd565b9190604086015261474e565b602881036147ce57505050906147c96147b863ffffffff9385613e14565b9316606087019063ffffffff169052565b61474e565b603281036147ef575050506147e39083613fcd565b9190608086015261474e565b6038810361481b57505050906148086147c99284613e14565b6001600160401b0390911660a087015291565b60420361483a57505061482e9083613fcd565b919060c086015261474e565b61484992506007169084613d5a565b9061474e565b90614858613068565b915f8151905b81811061486a57505050565b6148749083613e14565b80916001600160401b038116600881145f146148ae575050509060ff61489d6148a49385613e14565b93906149d1565b1685525b9061485e565b6010036148d3575050906148c763ffffffff9284613e14565b921660208601526148a8565b6148e292506007169084613d5a565b906148a8565b5f806106aa93602081519101845af46148ff613a23565b91614a22565b9061490e614561565b915f8151905b81811061492057505050565b61492a9083613e14565b80916001600160401b038116600881145f14614969575050509061496361495461495b9385613e14565b9390614a85565b61ffff168652565b90614914565b6010810361499d575050509061499861498763ffffffff9385613e14565b9316602087019063ffffffff169052565b614963565b601a036149bc5750506149b09083613fcd565b91906040860152614963565b6149cb92506007169084613d5a565b90614963565b6001600160401b031680156129545760018114614a1c5760028114614a165760038114614a105760249060405190632cd3748760e21b82526004820152fd5b50600390565b50600290565b50600190565b90614a495750805115614a3757602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614a7c575b614a5a575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15614a52565b6001600160401b038116801561239e576107d1808214614bf45750610bb9808214614bf45750610bba808214614bf45750610bbc808214614bf45750610bbe808214614bf4575061aa00808214614bf4575061ee00808214614bf4575061ee02808214614bf4575061ee04808214614bf4575061ee06808214614bf4575061ee10808214614bf4575061ee11808214614bf4575061ee13808214614bf4575061ee0f808214614bf4575061ee15808214614bf4575061ee16808214614bf4575061ee17808214614bf4575061ee18808214614bf4575061ee19808214614bf4575061ee1b808214614bf4575061ee1c808214614bf4575061ee1e808214614bf4575061ee1f808214614bf4575061ee20808214614bf4575061ee21808214614bf4575061ee22808214614bf4575061ee23808214614bf4575061ee24808214614bf4575061ee25809114613d7657604051632cd3748760e21b81526001600160401b0383166004820152602490fd5b9150509056fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a264697066735822122010a68012febf8fe1754f31c152149d058698cd9a133878ab2a1ddcfd7b362bf164736f6c63430008190033", "linkReferences": { - "contracts/outpost/OPPCommon.sol": { - "OPPCommon": [ + "contracts/outpost/OPPChunkedLib.sol": { + "OPPChunkedLib": [ { "length": 20, - "start": 11450 + "start": 12006 }, { "length": 20, - "start": 11987 + "start": 12362 } ] }, - "contracts/outpost/OPPInboundLib.sol": { - "OPPInboundLib": [ - { - "length": 20, - "start": 7922 - }, + "contracts/outpost/OPPCommon.sol": { + "OPPCommon": [ { "length": 20, - "start": 16213 + "start": 10484 }, { "length": 20, - "start": 16436 - }, + "start": 13536 + } + ] + }, + "contracts/outpost/OPPInboundLib.sol": { + "OPPInboundLib": [ { "length": 20, - "start": 16523 + "start": 15627 }, { "length": 20, - "start": 17768 + "start": 17453 }, { "length": 20, - "start": 18006 + "start": 17804 } ] } }, "deployedLinkReferences": { - "contracts/outpost/OPPCommon.sol": { - "OPPCommon": [ + "contracts/outpost/OPPChunkedLib.sol": { + "OPPChunkedLib": [ { "length": 20, - "start": 11227 + "start": 11783 }, { "length": 20, - "start": 11764 + "start": 12139 } ] }, - "contracts/outpost/OPPInboundLib.sol": { - "OPPInboundLib": [ - { - "length": 20, - "start": 7699 - }, + "contracts/outpost/OPPCommon.sol": { + "OPPCommon": [ { "length": 20, - "start": 15990 + "start": 10261 }, { "length": 20, - "start": 16213 - }, + "start": 13313 + } + ] + }, + "contracts/outpost/OPPInboundLib.sol": { + "OPPInboundLib": [ { "length": 20, - "start": 16300 + "start": 15404 }, { "length": 20, - "start": 17545 + "start": 17230 }, { "length": 20, - "start": 17783 + "start": 17581 } ] } From e988028c8454d1c499a245120e76f51f6d437529 Mon Sep 17 00:00:00 2001 From: Josh Burroughs Date: Wed, 7 Oct 2026 09:34:00 -0400 Subject: [PATCH 3/9] outpost-eth: fund each epochIn to what it has left to carry The cranker funded every `epochIn` to the policy ceiling. A node reserves `gas_limit * max_fee_per_gas` of the signer's balance whatever the call uses, so a small envelope funded to the cap was refused for balance at exactly the fee levels where delivery matters most. The node's estimate is wrong the other way: the contract stops dispatching on a gas watchdog and records where it stopped, so `eth_estimateGas` converges on the tip plus one attestation and funding to it costs one transaction per attestation. `delivery_gas_budget` sizes each call from what it has to do: a fixed cost, a per-byte cost for calldata and the decode, an allowance per attestation the outpost has not dispatched yet, and the emit the finishing call attempts. A spill met earlier in the same tick doubles the next budget, so a run of attestations dearer than the allowance costs a logarithmic number of extra calls; the ceiling (policy max, bounded by EIP-7825) still caps it. An envelope the cranker cannot read is funded to the ceiling. libfc gains `ethereum_confirm_options::gas_limit_cap`: the transaction carries at most the cap, and `eth_estimateGas` is sent with `gas` set to it, so a call the budget cannot carry is refused by the node before anything is signed. The delivery options set floor and cap to the same budget. A JSON-RPC refusal of the pre-flight is retried once at the ceiling when the budget was below it. The epoch-in callback takes the budget per call, since a plain `ethereum_contract_tx_fn` binds its options once at construction. Co-Authored-By: Claude Fable 5.1 Change-Id: Iddd9d1349e000d7fe0133efdf2b053c3151e4612 --- .../fc/network/ethereum/ethereum_client.hpp | 21 ++- .../src/network/ethereum/ethereum_client.cpp | 42 ++++-- .../sysio/outpost_ethereum_client_plugin.hpp | 123 +++++++++++++----- .../outpost_ethereum_client.hpp | 6 + .../src/outpost_ethereum_client.cpp | 80 ++++++++++-- .../test/test_ethereum_transaction_policy.cpp | 44 ++++++- .../test_outpost_ethereum_client_plugin.cpp | 110 ++++++++++++++-- 7 files changed, 360 insertions(+), 66 deletions(-) diff --git a/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp b/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp index 509f7a904c..fa76cd6883 100644 --- a/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp +++ b/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp @@ -52,6 +52,15 @@ struct ethereum_confirm_options { /// `max_gas_limit`: a floor above the policy ceiling is a policy rejection, /// never a silently clamped limit. uint64_t gas_limit_floor = 0; + + /// Most gas the transaction is funded with, and the gas the pre-flight + /// estimate is asked to fit in; `0` leaves both unbounded (the policy + /// ceiling still applies). With a cap the estimate runs under exactly the + /// budget the transaction will carry, so a call that cannot succeed inside + /// it is refused by the node before anything is signed, instead of being + /// sent and reverting on chain for the whole budget. Must be at least the + /// floor; a cap above the policy ceiling is a policy rejection. + uint64_t gas_limit_cap = 0; }; inline constexpr ethereum_confirm_options ethereum_confirm_option_defaults{}; @@ -427,7 +436,9 @@ class ethereum_client : public std::enable_shared_from_this { fc::uint256 estimate_gas(const address_compat_type& to, const std::optional& value = {0}, const std::optional& gas_config = std::nullopt); - fc::uint256 estimate_gas(const address_compat_type& to, const abi::contract& contract, const data_or_params_t& params, const std::optional& gas_config = std::nullopt); + /// @param gas_limit_cap When non-zero, the `gas` the estimate is asked to + /// fit in: the node refuses a call that cannot succeed inside it. + fc::uint256 estimate_gas(const address_compat_type& to, const abi::contract& contract, const data_or_params_t& params, const std::optional& gas_config = std::nullopt, uint64_t gas_limit_cap = 0); /** * @brief Retrieves the current gas price. @@ -584,10 +595,13 @@ class ethereum_client : public std::enable_shared_from_this { * @param params Parameters to pass to the contract function * @param gas_limit_floor Least gas limit to fund the transaction with, or 0 * to use the buffered estimate; see `ethereum_confirm_options::gas_limit_floor` + * @param gas_limit_cap Most gas to fund it with, and the bound the estimate + * runs under, or 0 for none; see `ethereum_confirm_options::gas_limit_cap` * @return Configured eip1559_tx ready for signing and submission */ eip1559_tx create_default_tx(const address_compat_type& to, const abi::contract& contract, - const fc::variants& params = {}, uint64_t gas_limit_floor = 0); + const fc::variants& params = {}, uint64_t gas_limit_floor = 0, + uint64_t gas_limit_cap = 0); /** * @brief Gets or creates a typed contract client instance @@ -715,7 +729,8 @@ ethereum_contract_tx_fn ethereum_contract_client::create_tx_and_con abi::contract& abi = abi_map[contract.name]; return [this, &abi, opts](const Args&... args) -> RT { contract_invoke_data_items params = {args...}; - auto tx = client->create_default_tx(contract_address, abi, params, opts.gas_limit_floor); + auto tx = client->create_default_tx(contract_address, abi, params, opts.gas_limit_floor, + opts.gas_limit_cap); auto res_var = client->execute_contract_tx_fn(tx, abi, params); // `execute_contract_tx_fn` returns the submitted tx hash. Await diff --git a/libraries/libfc/src/network/ethereum/ethereum_client.cpp b/libraries/libfc/src/network/ethereum/ethereum_client.cpp index d5eafe8a3f..6c9436ae53 100644 --- a/libraries/libfc/src/network/ethereum/ethereum_client.cpp +++ b/libraries/libfc/src/network/ethereum/ethereum_client.cpp @@ -29,12 +29,14 @@ constexpr std::string_view max_priority_fee_per_gas = "max_priority_fee_per_gas" constexpr std::string_view estimated_gas = "estimated_gas"; constexpr std::string_view gas_price = "gas_price"; constexpr std::string_view gas_limit_floor = "gas_limit_floor"; +constexpr std::string_view gas_limit_cap = "gas_limit_cap"; } // namespace transaction_policy_field namespace ethereum_rpc_field { constexpr std::string_view base_fee_per_gas = "baseFeePerGas"; constexpr std::string_view max_priority_fee_per_gas = "maxPriorityFeePerGas"; constexpr std::string_view max_fee_per_gas = "maxFeePerGas"; +constexpr std::string_view gas = "gas"; } // namespace ethereum_rpc_field constexpr std::string_view gas_configuration_operation = "get_gas_config"; @@ -301,26 +303,37 @@ fc::variant ethereum_client::get_syncing_status() { * breaches the client's `max_gas_limit` */ eip1559_tx ethereum_client::create_default_tx(const address_compat_type& to, const abi::contract& contract, - const fc::variants& params, uint64_t gas_limit_floor) { + const fc::variants& params, uint64_t gas_limit_floor, + uint64_t gas_limit_cap) { + FC_ASSERT(gas_limit_cap == 0 || gas_limit_cap >= gas_limit_floor, + "gas_limit_cap ({}) is below gas_limit_floor ({})", gas_limit_cap, gas_limit_floor); try { auto gc = get_gas_config_unlogged(); auto data = contract_encode_data(contract, params); - auto estimated_gas = estimate_gas(to, contract, data, gc); + // The floor and the cap are bounded by the same ceiling as the estimate: + // a caller asking for more than the policy allows is refused, not + // clamped, so an under-funded call is never silently sent. + const auto require_within_policy = [&](uint64_t limit, std::string_view field) { + const fc::uint256 wide{limit}; + if (wide > _transaction_policy.max_gas_limit) { + throw_transaction_policy_exception(ethereum_transaction_policy_reason::gas_limit_cap_exceeded, + field, wide.str(), _transaction_policy.max_gas_limit.str()); + } + }; + if (gas_limit_cap != 0) require_within_policy(gas_limit_cap, transaction_policy_field::gas_limit_cap); + + auto estimated_gas = estimate_gas(to, contract, data, gc, gas_limit_cap); auto gas_limit = derive_buffered_gas_limit(_transaction_policy, estimated_gas); if (gas_limit_floor != 0) { - // The floor is bounded by the same ceiling as the estimate: a caller - // asking for more than the policy allows is refused, not clamped, so - // an under-funded call is never silently sent. + require_within_policy(gas_limit_floor, transaction_policy_field::gas_limit_floor); const fc::uint256 floor{gas_limit_floor}; - if (floor > _transaction_policy.max_gas_limit) { - throw_transaction_policy_exception(ethereum_transaction_policy_reason::gas_limit_cap_exceeded, - transaction_policy_field::gas_limit_floor, - floor.str(), - _transaction_policy.max_gas_limit.str()); - } if (gas_limit < floor) gas_limit = floor; } + if (gas_limit_cap != 0) { + const fc::uint256 cap{gas_limit_cap}; + if (gas_limit > cap) gas_limit = cap; + } return eip1559_tx{.chain_id = get_chain_id(), .nonce = get_transaction_count(get_signer_address(), block_tag_t::pending), @@ -528,7 +541,8 @@ ethereum_client::gas_config_t ethereum_client::get_gas_config_unlogged() { * @throws fc::network::json_rpc::json_rpc_exception if the RPC call fails */ fc::uint256 ethereum_client::estimate_gas(const address_compat_type& to, const abi::contract& contract, - const data_or_params_t& data_or_params, const std::optional& gas_config_opt) { + const data_or_params_t& data_or_params, const std::optional& gas_config_opt, + uint64_t gas_limit_cap) { fc::mutable_variant_object tx; gas_config_t gc = gas_config_opt ? *gas_config_opt : get_gas_config(); @@ -541,6 +555,10 @@ fc::uint256 ethereum_client::estimate_gas(const address_compat_type& to, const a (std::string(ethereum_rpc_field::max_fee_per_gas), format_rpc_quantity(gc.max_fee_per_gas)) ("data", data) ("input", data); + // A capped estimate is a pre-flight at the budget the transaction will + // carry: the node searches only up to `gas` and reports a revert when the + // call cannot succeed inside it. + if (gas_limit_cap != 0) tx(std::string(ethereum_rpc_field::gas), format_rpc_quantity(fc::uint256{gas_limit_cap})); auto resp = execute_idempotent("eth_estimateGas", fc::variants{tx}); return parse_rpc_quantity(resp, transaction_policy_field::estimated_gas); diff --git a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp index 2bf46328a2..9bff80d200 100644 --- a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp +++ b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + #include #include #include @@ -61,37 +65,94 @@ struct opp_contract_client : ethereum_contract_client { inline constexpr uint64_t EIP_7825_TX_GAS_CAP = 16'777'216; /** - * @brief Gas an `epochIn` transaction is funded with: the client's policy + * @brief The most gas any `epochIn` may be funded with: the client's policy * ceiling, bounded by EIP-7825's cap. - * - * Delivery is funded to a FLOOR rather than to the node's estimate because the - * estimate is systematically wrong for this call. `OPPInbound.epochIn` stops - * dispatching on a `gasleft()` watchdog and records where it stopped instead - * of reverting, so `eth_estimateGas` converges on the least gas at which the - * call SUCCEEDS — the consensus tip plus ONE attestation. Funded to that - * figure every call would spill after one attestation and an envelope would - * cost one transaction per attestation. Funded to the ceiling, one call - * carries as much dispatch as the chain allows and the unused remainder is - * refunded. - * * @param policy The client's local expenditure policy. - * @return The floor, in gas. + * @return The ceiling, in gas. */ -inline uint64_t delivery_gas_limit_floor(const ethereum_transaction_policy& policy) { +inline uint64_t delivery_gas_ceiling(const ethereum_transaction_policy& policy) { const fc::uint256 cap{EIP_7825_TX_GAS_CAP}; return policy.max_gas_limit < cap ? policy.max_gas_limit.convert_to() : EIP_7825_TX_GAS_CAP; } +/// Gas an `epochIn` pays before its dispatch loop, independent of size: the +/// transaction base, the delivery record, and the consensus bookkeeping a tip +/// adds. A ~700-byte tip reaches the loop at ~320k (wire-ethereum +/// `OPPCapacity`, the under-funded probe); rounded up. +inline constexpr uint64_t DELIVERY_FIXED_GAS = 500'000; +/// Gas an `epochIn` pays per envelope byte: calldata (EIP-7623 floors a +/// calldata-heavy transaction at 40 gas a byte), the linear decode (~50 gas +/// a byte, measured at the cap) and the dispatch walk; rounded up. +inline constexpr uint64_t DELIVERY_GAS_PER_BYTE = 120; +/// Gas allowed per attestation still to dispatch. A remit is tens of +/// thousands; a routine rotation seating seven unseen operators is ~1.15M. +/// An attestation that costs more spills, and the next call escalates. +inline constexpr uint64_t ATTESTATION_GAS_ALLOWANCE = 1'500'000; +/// Gas left for the outbound emit the finishing call attempts: +/// `OPPInbound.EmitAttemptGasFloor`, below which the contract defers it. +inline constexpr uint64_t EMIT_GAS_ALLOWANCE = 4'000'000; + +/// What one `epochIn` has to carry, from which its gas budget is sized. +struct delivery_gas_request { + /// Encoded envelope size. + uint64_t envelope_bytes = 0; + /// Attestations the outpost has not dispatched yet, or `std::nullopt` + /// when the envelope could not be read: the budget is then the ceiling. + std::optional remaining_attestations; + /// Calls already sent for this epoch in this tick that spilled: each one + /// doubles the budget, so a run of attestations dearer than the allowance + /// costs at most a logarithmic number of extra calls. + uint32_t escalations = 0; + /// `delivery_gas_ceiling`. + uint64_t ceiling = 0; +}; + /** - * @brief Confirmation options for `epochIn`: the defaults, funded to - * `delivery_gas_limit_floor`. - * @param client The client whose policy sets the floor; null (an ABI-only - * construction) leaves the floor at zero. - * @return The options the `epochIn` wrapper is built with. + * @brief Gas one `epochIn` is funded with. + * + * Sized to what the call has left to do rather than to the node's estimate or + * to the ceiling. The estimate is systematically wrong for this call: + * `OPPInbound.epochIn` stops dispatching on a `gasleft()` watchdog and records + * where it stopped, so `eth_estimateGas` converges on the least gas at which + * the call SUCCEEDS — the tip plus one attestation — and funding to it would + * cost one transaction per attestation. The ceiling is wrong the other way: + * a node reserves `gas_limit * max_fee_per_gas` of the signer's balance + * whatever the call uses, so a small envelope funded to the cap is refused for + * balance at exactly the fee levels where delivery matters most. + * + * The budget is the fixed and per-byte cost, an allowance per attestation + * still to dispatch, and the emit; doubled per spill already met this tick; + * never above the ceiling. The contract spills what does not fit and the + * next call carries on, so an under-estimate costs a continuation, never the + * epoch. An envelope that cannot be read is funded to the ceiling. + * + * @param request What the call has to carry. + * @return The budget, in gas. + */ +inline uint64_t delivery_gas_budget(const delivery_gas_request& request) { + if (!request.remaining_attestations) return request.ceiling; + uint64_t budget = DELIVERY_FIXED_GAS + request.envelope_bytes * DELIVERY_GAS_PER_BYTE + + uint64_t{*request.remaining_attestations} * ATTESTATION_GAS_ALLOWANCE + + EMIT_GAS_ALLOWANCE; + for (uint32_t i = 0; i < request.escalations && budget < request.ceiling; ++i) budget *= 2; + return std::min(budget, request.ceiling); +} + +/** + * @brief Confirmation options for one `epochIn`: the defaults, funded to + * exactly `gas_budget`. + * + * Floor and cap are the same figure, so the transaction carries the budget + * and the pre-flight estimate runs under it: a call the budget cannot carry + * is refused by the node before it is signed. + * + * @param gas_budget The budget from `delivery_gas_budget`. + * @return The options the call is sent with. */ -inline ethereum_confirm_options delivery_confirm_options(const ethereum_client_ptr& client) { +inline ethereum_confirm_options delivery_confirm_options(uint64_t gas_budget) { ethereum_confirm_options options = ethereum_confirm_option_defaults; - if (client) options.gas_limit_floor = delivery_gas_limit_floor(client->transaction_policy()); + options.gas_limit_floor = gas_budget; + options.gas_limit_cap = gas_budget; return options; } @@ -102,14 +163,16 @@ struct opp_inbound_contract_client : ethereum_contract_client { /// ONE call, addressed to its epoch. The call that reaches consensus /// dispatches as many attestations as its gas allows and records where it /// stopped; a continuation is the SAME call with the same arguments, which - /// the contract resumes from its cursor. Funded to the policy ceiling — see - /// `delivery_gas_limit_floor`. `envelopeData` rides as a hex-encoded string - /// because the libfc ABI encoder takes `dt::bytes` that way (see + /// the contract resumes from its cursor. Funded to exactly `gas_budget` + /// (see `delivery_gas_budget`), which is why this is not a plain + /// `ethereum_contract_tx_fn`: that binds its options once at construction, + /// and the budget differs per call. `envelopeData` rides as a hex-encoded + /// string because the libfc ABI encoder takes `dt::bytes` that way (see /// `ethereum_abi::encode_dynamic_data`). /// - /// `ethereum_contract_tx_fn` binds every argument as a non-const lvalue - /// reference, so callers must materialize named locals for both. - ethereum_contract_tx_fn epoch_in; + /// The ABI arguments are bound as non-const lvalue references, so callers + /// must materialize named locals for both. + std::function epoch_in; /// `nextEpochIndex()` view — the epoch the outpost is currently accepting. ethereum_contract_call_fn next_epoch_index; /// `dispatchSpill(uint32 epochIndex)` view — where a tipped epoch's dispatch @@ -137,8 +200,10 @@ struct opp_inbound_contract_client : ethereum_contract_client { const address_compat_type& contract_address, const std::vector& contracts) : ethereum_contract_client(client, contract_address, contracts) - , epoch_in(create_tx_and_confirm( - get_abi("epochIn"), delivery_confirm_options(client))) + , epoch_in([this](uint32_t& epoch_index, std::string& envelope_hex, uint64_t gas_budget) -> fc::variant { + return create_tx_and_confirm( + get_abi("epochIn"), delivery_confirm_options(gas_budget))(epoch_index, envelope_hex); + }) , next_epoch_index(create_call(get_abi("nextEpochIndex"))) , dispatch_spill(create_call(get_abi("dispatchSpill"))) , epoch_deliveries(create_call(get_abi("epochDeliveries"))) diff --git a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp index 03d686db2f..af9db544e8 100644 --- a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp +++ b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp @@ -96,6 +96,12 @@ std::optional classify_realize_yield_revert(std::string_v /// the hex is not exactly one word of hex digits with a zero 12-byte pad. std::optional address_from_word(std::string_view raw_hex); +/// How many attestations `envelope_bytes` carries across its messages, or +/// `std::nullopt` when the bytes do not decode as an OPP envelope. The relay +/// sizes a delivery's gas budget from it; an unreadable envelope is funded to +/// the ceiling and left to the contract to judge. +std::optional count_envelope_attestations(const std::vector& envelope_bytes); + /// True when `handler_address` names a contract the outpost routes to: neither /// `address(0)` (nothing registered) nor `ATTESTATION_BLACKHOLE` (governance /// dropped the type). diff --git a/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp b/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp index efd11ebd09..74fd89f872 100644 --- a/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp +++ b/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -271,6 +272,17 @@ std::optional classify_realize_yield_revert(std::string_v return std::nullopt; } +std::optional count_envelope_attestations(const std::vector& envelope_bytes) { + sysio::opp::Envelope envelope; + if (!envelope.ParseFromArray(envelope_bytes.data(), static_cast(envelope_bytes.size()))) { + return std::nullopt; + } + uint64_t count = 0; + for (const auto& message : envelope.messages()) count += message.payload().attestations_size(); + if (count > std::numeric_limits::max()) return std::nullopt; + return static_cast(count); +} + std::optional address_from_word(std::string_view raw_hex) { constexpr size_t word_chars = EVM_ABI_WORD_BYTES * HEX_CHARS_PER_BYTE; constexpr size_t address_chars = EVM_ADDRESS_BYTES * HEX_CHARS_PER_BYTE; @@ -452,12 +464,25 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( // re-supplied on every continuation — the outpost never stores its bytes. const std::string envelope_hex = fc::to_hex(envelope_bytes.data(), static_cast(total)); + // Each call is funded to what it has left to carry (`delivery_gas_budget`), + // never to a fixed figure: the attestations the outpost's cursor says are + // still to dispatch, doubled for every spill already met this tick. An + // envelope the relay cannot read is funded to the ceiling. + const auto attestation_count = detail::count_envelope_attestations(envelope_bytes); + const uint64_t gas_ceiling = delivery_gas_ceiling(_entry->client->transaction_policy()); + if (!attestation_count) { + wlog("outpost_ethereum_client[{}]: epoch={} envelope ({} bytes) does not decode as an OPP " + "envelope; funding delivery to the ceiling ({} gas)", + to_string(), epoch_index, total, gas_ceiling); + } + // Sequential, receipt-confirmed submission — one transaction in flight at a // time, so the signer's nonce advances in lock-step and a mid-sequence // failure simply abandons the tick. The next cron tick re-reads the // outpost's cursor and picks up wherever the chain actually is. std::string last_tx; bool delivered_this_tick = false; + uint32_t spills_this_tick = 0; for (uint32_t call = 0; call <= MAX_CONTINUATIONS_PER_TICK; ++call) { throw_if_past_deadline(deadline_abs, OP_DELIVER_OUTBOUND); @@ -501,25 +526,64 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( break; case detail::delivery_action::continue_dispatch: + // A continuation behind a call this tick already sent means that + // call spilled: what it was funded with did not carry its work, so + // the next one is funded to more. A continuation that OPENS the tick + // resumes a spill from an earlier tick at the sized budget. + if (!last_tx.empty()) ++spills_this_tick; break; } throw_if_past_deadline(deadline_abs, OP_DELIVER_OUTBOUND); - // `ethereum_contract_tx_fn` binds every argument as a non-const lvalue - // reference, so each one needs a named local. + // Before the tip nothing is dispatched, so every attestation is still + // to carry; after it the outpost's cursor says how many remain, and a + // complete dispatch leaves only the emit. + std::optional remaining; + if (attestation_count) { + const uint32_t done = spill.tipped ? spill.dispatched : 0; + remaining = spill.complete ? 0 : (*attestation_count > done ? *attestation_count - done : 0); + } + uint64_t gas_budget = delivery_gas_budget(delivery_gas_request{ + .envelope_bytes = total, + .remaining_attestations = remaining, + .escalations = spills_this_tick, + .ceiling = gas_ceiling, + }); + dlog("outpost_ethereum_client[{}]: epoch={} funding epochIn with {} gas (remaining={} " + "escalations={} ceiling={})", + to_string(), epoch_index, gas_budget, + remaining ? std::to_string(*remaining) : "unknown", spills_this_tick, gas_ceiling); + + // The ABI arguments are bound as non-const lvalue references, so each + // one needs a named local. uint32_t epoch_arg = epoch_index; std::string payload_hex = envelope_hex; - const auto result = _opp_inbound_client->epoch_in(epoch_arg, payload_hex); - last_tx = result.as_string(); + fc::variant result; + try { + result = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget); + } catch (const fc::network::json_rpc::json_rpc_error& refusal) { + // The node refused the call at this budget before anything was + // signed — the pre-flight estimate runs under the budget. Below the + // ceiling that is the budget's fault: retry once with everything the + // policy allows. At the ceiling it is the envelope's, and the tick + // reports it rather than spending the ceiling on finding out again. + if (gas_budget >= gas_ceiling) throw; + wlog("outpost_ethereum_client[{}]: epoch={} refused at {} gas ({}); retrying at the " + "ceiling ({} gas)", + to_string(), epoch_index, gas_budget, refusal.to_string(), gas_ceiling); + gas_budget = gas_ceiling; + result = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget); + } + last_tx = result.as_string(); if (action == detail::delivery_action::deliver) { delivered_this_tick = true; - ilog("outpost_ethereum_client[{}]: epochIn delivered epoch={} bytes={} tx={}", - to_string(), epoch_index, total, last_tx); + ilog("outpost_ethereum_client[{}]: epochIn delivered epoch={} bytes={} gas={} tx={}", + to_string(), epoch_index, total, gas_budget, last_tx); } else { ilog("outpost_ethereum_client[{}]: epochIn continued epoch={} from attestation {} " - "(complete={}) tx={}", - to_string(), epoch_index, spill.dispatched, spill.complete, last_tx); + "(complete={}) gas={} tx={}", + to_string(), epoch_index, spill.dispatched, spill.complete, gas_budget, last_tx); } } diff --git a/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp b/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp index e66c5b1f33..8df97ecd62 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp @@ -509,6 +509,42 @@ BOOST_AUTO_TEST_CASE(gas_limit_floor_raises_an_under_estimate_and_is_policy_boun BOOST_CHECK_EQUAL(client->broadcast_count, 0u); } +/// A cap bounds the limit from above, is handed to the estimate as the gas +/// the call must fit in, cannot sit below the floor, and is policy-bounded +/// exactly like the floor. +BOOST_AUTO_TEST_CASE(gas_limit_cap_bounds_the_limit_and_runs_the_estimate_under_it) { + std::atomic sign_count = 0; + const auto provider = make_recording_signer(sign_count); + auto policy = bounded_policy(); + policy.max_gas_limit = 2000; + auto client = std::make_shared(provider, policy); + + // No cap: the estimate payload carries no `gas`. + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 0, 0); + BOOST_CHECK(!client->estimate_gas_params.get_array().front().get_object().contains("gas")); + + // The fake estimate is 834 -> buffered 1000; a cap of 900 wins. + BOOST_CHECK_EQUAL( + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 0, 900).gas_limit, + 900); + BOOST_CHECK_EQUAL( + client->estimate_gas_params.get_array().front().get_object()["gas"].as_string(), "0x384"); + // Floor and cap equal: the transaction carries exactly that. + BOOST_CHECK_EQUAL( + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 1500, 1500).gas_limit, + 1500); + // A cap above the policy ceiling is a rejection, not a clamp. + expect_policy_rejection([&] { + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 0, 2001); + }); + // A cap below the floor is a caller error. + BOOST_CHECK_THROW( + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 1500, 900), + fc::exception); + BOOST_CHECK_EQUAL(sign_count.load(), 0u); + BOOST_CHECK_EQUAL(client->broadcast_count, 0u); +} + BOOST_AUTO_TEST_CASE(all_typed_write_wrappers_share_the_policy_enforced_path) { std::atomic sign_count = 0; const auto provider = make_recording_signer(sign_count); @@ -525,11 +561,13 @@ BOOST_AUTO_TEST_CASE(all_typed_write_wrappers_share_the_policy_enforced_path) { uint16_argument_function("attestationHandlers")}, }; // OPPInbound's one write wrapper — the whole-envelope delivery — must be - // rejected by the policy before signing. It is funded to the policy ceiling - // (999 here), which the ×1.2-buffered estimate of 834 already breaches. + // rejected by the policy before signing. A budget above the policy ceiling + // (999 here) is refused outright; one within it still loses to the + // ×1.2-buffered estimate of 834, which breaches the ceiling on its own. uint32_t epoch_index = 1; std::string envelope = "01"; - expect_policy_rejection([&] { inbound.epoch_in(epoch_index, envelope); }); + expect_policy_rejection([&] { inbound.epoch_in(epoch_index, envelope, 1000); }); + expect_policy_rejection([&] { inbound.epoch_in(epoch_index, envelope, 999); }); sysio::opp_contract_client opp{ client, diff --git a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp index 8928536d2c..888916b037 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp @@ -401,6 +401,7 @@ std::string encode_next_epoch_index_result(uint32_t next_epoch_index) { struct observed_delivery_call { uint32_t epoch_index; std::string envelope_hex; + uint64_t gas_budget; }; /// Harness binding a real `outpost_ethereum_client` to a stubbed OPPInbound @@ -466,6 +467,23 @@ std::vector serialize_envelope(uint32_t epoch) { return {serialized.begin(), serialized.end()}; } +/// Serialize a protobuf envelope whose one message carries `attestations` +/// attestations of `payload_bytes` each — the shape the delivery budget is +/// sized from. +std::vector serialize_envelope_with_attestations(uint32_t epoch, uint32_t attestations, + size_t payload_bytes) { + sysio::opp::Envelope envelope; + envelope.set_epoch_index(epoch); + auto* payload = envelope.add_messages()->mutable_payload(); + for (uint32_t i = 0; i < attestations; ++i) { + auto* entry = payload->add_attestations(); + entry->set_data(std::string(payload_bytes, static_cast('a' + (i % 26)))); + entry->set_data_size(static_cast(payload_bytes)); + } + const auto serialized = envelope.SerializeAsString(); + return {serialized.begin(), serialized.end()}; +} + /// The app, signer, chain connection and client entry every relay fixture /// stands on. The connection points at a port nothing listens on, so a wrapper /// a case forgot to stub fails loudly instead of dialing anything. @@ -525,8 +543,8 @@ std::unique_ptr create_whole_envelope_delivery_ BOOST_REQUIRE(fixture->inbound); auto* raw = fixture.get(); - raw->inbound->epoch_in = [raw](uint32_t& epoch_index, std::string& envelope_hex) -> fc::variant { - raw->delivery_calls.push_back(observed_delivery_call{epoch_index, envelope_hex}); + raw->inbound->epoch_in = [raw](uint32_t& epoch_index, std::string& envelope_hex, uint64_t gas_budget) -> fc::variant { + raw->delivery_calls.push_back(observed_delivery_call{epoch_index, envelope_hex, gas_budget}); if (raw->delivery_calls.size() == 1 && raw->first_call_delay.count() > 0) { std::this_thread::sleep_for(raw->first_call_delay); } @@ -1242,10 +1260,10 @@ BOOST_AUTO_TEST_CASE(read_emitted_envelope_digest_reads_the_outpost_record) try // Whole-envelope WIRE -> Ethereum delivery // --------------------------------------------------------------------------- -/// The gas an `epochIn` is funded with follows the client's policy ceiling and -/// never exceeds EIP-7825's cap — estimation cannot size a watchdog-bounded -/// call, so the floor is what carries a full envelope's dispatch. -BOOST_AUTO_TEST_CASE(delivery_gas_floor_is_the_policy_ceiling_bounded_by_the_cap) try { +/// The most an `epochIn` may be funded with follows the client's policy +/// ceiling and never exceeds EIP-7825's cap; the options a call is sent with +/// pin floor and cap to the one budget, so the pre-flight runs under it. +BOOST_AUTO_TEST_CASE(delivery_gas_ceiling_is_the_policy_ceiling_bounded_by_the_cap) try { ethereum_transaction_policy policy{ .client_id = std::string(latest_slot_test_entry_id), .chain_id = test_evm_chain_id, @@ -1254,16 +1272,83 @@ BOOST_AUTO_TEST_CASE(delivery_gas_floor_is_the_policy_ceiling_bounded_by_the_cap .max_gas_limit = maximum_ethereum_transaction_policy_value(), .max_total_native_cost = maximum_ethereum_transaction_policy_value(), }; - BOOST_CHECK_EQUAL(sysio::delivery_gas_limit_floor(policy), sysio::EIP_7825_TX_GAS_CAP); + BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy), sysio::EIP_7825_TX_GAS_CAP); policy.max_gas_limit = fc::uint256{sysio::EIP_7825_TX_GAS_CAP}; - BOOST_CHECK_EQUAL(sysio::delivery_gas_limit_floor(policy), sysio::EIP_7825_TX_GAS_CAP); + BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy), sysio::EIP_7825_TX_GAS_CAP); policy.max_gas_limit = fc::uint256{below_cap_policy_gas_limit}; - BOOST_CHECK_EQUAL(sysio::delivery_gas_limit_floor(policy), below_cap_policy_gas_limit); + BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy), below_cap_policy_gas_limit); + + const auto options = sysio::delivery_confirm_options(below_cap_policy_gas_limit); + BOOST_CHECK_EQUAL(options.gas_limit_floor, below_cap_policy_gas_limit); + BOOST_CHECK_EQUAL(options.gas_limit_cap, below_cap_policy_gas_limit); +} FC_LOG_AND_RETHROW(); + +/// A delivery is funded to what it has left to carry — fixed cost, bytes, an +/// allowance per attestation still to dispatch, the emit — doubled per spill +/// already met this tick and never past the ceiling; an envelope the relay +/// cannot read is funded to the ceiling outright. +BOOST_AUTO_TEST_CASE(delivery_gas_budget_follows_the_remaining_work_and_escalates_to_the_ceiling) try { + constexpr uint64_t bytes = 1'000; + constexpr uint64_t ceiling = sysio::EIP_7825_TX_GAS_CAP; + const auto budget = [&](uint32_t remaining, uint32_t escalations) { + return sysio::delivery_gas_budget(sysio::delivery_gas_request{ + .envelope_bytes = bytes, .remaining_attestations = remaining, .escalations = escalations, .ceiling = ceiling}); + }; + const uint64_t base = sysio::DELIVERY_FIXED_GAS + bytes * sysio::DELIVERY_GAS_PER_BYTE + sysio::EMIT_GAS_ALLOWANCE; + + BOOST_CHECK_EQUAL(budget(0, 0), base); + BOOST_CHECK_EQUAL(budget(1, 0), base + sysio::ATTESTATION_GAS_ALLOWANCE); + BOOST_CHECK_EQUAL(budget(3, 0), base + 3 * sysio::ATTESTATION_GAS_ALLOWANCE); + // Each spill doubles, and the ceiling is where doubling stops. + BOOST_CHECK_EQUAL(budget(1, 1), 2 * (base + sysio::ATTESTATION_GAS_ALLOWANCE)); + BOOST_CHECK_EQUAL(budget(1, 2), ceiling); + BOOST_CHECK_EQUAL(budget(1, 40), ceiling); + // A full envelope's worth of attestations is above the ceiling on its own. + BOOST_CHECK_EQUAL(budget(70, 0), ceiling); + // Unreadable: the ceiling, whatever else is known. + BOOST_CHECK_EQUAL(sysio::delivery_gas_budget(sysio::delivery_gas_request{ + .envelope_bytes = bytes, .remaining_attestations = std::nullopt, .escalations = 0, .ceiling = ceiling}), + ceiling); + // Counting is the relay's own read of the envelope. + BOOST_CHECK_EQUAL(*sysio::outpost_ethereum_client_detail::count_envelope_attestations( + serialize_envelope_with_attestations(test_wire_epoch, 3, 16)), + 3u); + BOOST_CHECK(!sysio::outpost_ethereum_client_detail::count_envelope_attestations(make_envelope(mid_envelope_bytes))); +} FC_LOG_AND_RETHROW(); - // An ABI-only construction has no client to read a policy from: no floor. - BOOST_CHECK_EQUAL(sysio::delivery_confirm_options(ethereum_client_ptr{}).gas_limit_floor, 0u); +/// Across a tipped-and-spilled epoch the budget tracks the outpost's cursor: +/// everything before the tip, what remains after it, only the emit once +/// dispatch is complete — each continuation doubled for the spill before it. +BOOST_AUTO_TEST_CASE(delivery_funds_each_call_to_its_remaining_work) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = serialize_envelope_with_attestations(test_wire_epoch, 2, 64); + fixture->spill_responses = { + encode_dispatch_spill_result(false, 0, false, false), // before the delivery + encode_dispatch_spill_result(true, 1, false, false), // tipped, spilled after 1 + encode_dispatch_spill_result(true, 2, true, false), // dispatched, emit outstanding + encode_dispatch_spill_result(true, 2, true, true), // finalized + }; + fixture->own_delivery_response = encode_word_result(settled_digest_word); + + const auto tx = fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); + BOOST_CHECK(!tx.empty()); + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 3); + + const auto expected = [&](uint32_t remaining, uint32_t escalations) { + return sysio::delivery_gas_budget(sysio::delivery_gas_request{ + .envelope_bytes = envelope.size(), + .remaining_attestations = remaining, + .escalations = escalations, + .ceiling = sysio::EIP_7825_TX_GAS_CAP}); + }; + BOOST_CHECK_EQUAL(fixture->delivery_calls[0].gas_budget, expected(2, 0)); + BOOST_CHECK_EQUAL(fixture->delivery_calls[1].gas_budget, expected(1, 1)); + BOOST_CHECK_EQUAL(fixture->delivery_calls[2].gas_budget, expected(0, 2)); + BOOST_CHECK(fixture->delivery_calls[0].gas_budget < fixture->delivery_calls[1].gas_budget); + BOOST_CHECK(fixture->delivery_calls[0].gas_budget < sysio::EIP_7825_TX_GAS_CAP); } FC_LOG_AND_RETHROW(); /// The deliver-or-continue decision table, exercised without an EVM node. @@ -1323,6 +1408,9 @@ BOOST_AUTO_TEST_CASE(delivery_sends_the_whole_envelope_in_one_call) try { BOOST_CHECK(!tx.empty()); check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); + // Opaque bytes carry no attestation count to size a budget from: the + // ceiling, and the contract judges them. + BOOST_CHECK_EQUAL(fixture->delivery_calls[0].gas_budget, sysio::EIP_7825_TX_GAS_CAP); // Read before the delivery, and once more after it to learn it did not tip. BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 2u); BOOST_CHECK_EQUAL(fixture->spill_reads, 2u); From 3a68c61b4b81afe37e94a6eee57eb85c26c8284a Mon Sep 17 00:00:00 2001 From: Josh Burroughs Date: Thu, 8 Oct 2026 15:39:48 -0400 Subject: [PATCH 4/9] libfc: a capped Ethereum transaction is sent with its cap; expose the confirmed receipt `create_default_tx` with a `gas_limit_cap` now sends the transaction with exactly the cap. The pre-flight estimate still runs under it, so a call that cannot succeed inside the cap is refused by the node before anything is signed, but the estimate's VALUE is no longer an input to the limit: neither the x1.2 headroom buffer nor the buffered-estimate policy check applies, since what is simulated is exactly what is sent. Before, a capped call whose estimate used most of its cap was rejected by `derive_buffered_gas_limit` (buffered estimate above `max_gas_limit`) for a transaction the cap already carried -- the review's #13 on PR #660. `wait_for_receipt` returns the `eth_getTransactionReceipt` object after the same two-phase wait `wait_for_confirmation` performs (which now delegates to it). A caller deciding its next move from the chain reads the receipt's `blockNumber` and `logs` instead of re-reading state at `latest`, which a lagging backend may serve from before the transaction landed. Tests: the cap case now pins that a cap of 1200 is sent as 1200 (not the buffered 1000), that a capped call fits a policy its buffered estimate would breach, and that `wait_for_receipt` hands back the receipt's block and logs, honours the depth wait, and rejects a reverted receipt. Co-Authored-By: Claude Fable 5.1 Change-Id: I855041d6ebb93bac73044faea674a509f46ecdbd --- .../fc/network/ethereum/ethereum_client.hpp | 44 ++++++++++++--- .../src/network/ethereum/ethereum_client.cpp | 55 +++++++++++++------ 2 files changed, 72 insertions(+), 27 deletions(-) diff --git a/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp b/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp index fa76cd6883..999ff99b47 100644 --- a/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp +++ b/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp @@ -50,16 +50,20 @@ struct ethereum_confirm_options { /// under-funds it. A floor lets the caller fund such a call to what the /// chain allows instead. The floor is still subject to the client's /// `max_gas_limit`: a floor above the policy ceiling is a policy rejection, - /// never a silently clamped limit. + /// never a silently clamped limit. Ignored when `gas_limit_cap` is set: the + /// cap is then the limit outright. uint64_t gas_limit_floor = 0; - /// Most gas the transaction is funded with, and the gas the pre-flight - /// estimate is asked to fit in; `0` leaves both unbounded (the policy - /// ceiling still applies). With a cap the estimate runs under exactly the - /// budget the transaction will carry, so a call that cannot succeed inside - /// it is refused by the node before anything is signed, instead of being - /// sent and reverting on chain for the whole budget. Must be at least the - /// floor; a cap above the policy ceiling is a policy rejection. + /// The gas limit the transaction is sent with, and the gas the pre-flight + /// estimate runs under; `0` leaves the limit to the buffered estimate (and + /// the floor). A capped call is sent with EXACTLY this limit: the estimate + /// is a pre-flight at that gas and nothing more, so a call that cannot + /// succeed inside it is refused by the node before anything is signed, + /// instead of being sent and reverting on chain for the whole budget. The + /// estimate's value is not an input to the limit -- neither the headroom + /// buffer nor the buffered-estimate ceiling check applies, since what is + /// simulated is exactly what is sent. Must be at least the floor; a cap + /// above the policy ceiling is a policy rejection. uint64_t gas_limit_cap = 0; }; @@ -505,6 +509,28 @@ class ethereum_client : public std::enable_shared_from_this { const ethereum_confirm_options& opts = ethereum_confirm_option_defaults); + /** + * @brief Wait for a previously-submitted tx hash to be included and + * confirmed at `opts.confirmations` depth, and return its receipt. + * + * The receipt is the authoritative record of what the transaction did: + * its `blockNumber` pins every follow-up read to the block that holds the + * effect, and its `logs` carry the events the contract emitted. A caller + * that decides its next move from those reads the receipt rather than + * re-reading state at `latest`, which a lagging backend may serve from a + * block before the transaction landed. + * + * Same two-phase wait as `wait_for_confirmation`. + * + * @param tx_hash Transaction hash returned by an earlier submit. + * @param opts Confirmation depth + retry/backoff envelope. + * @return The `eth_getTransactionReceipt` object, as the node returned it. + * @throws fc::timeout_exception on deadline expiry, fc::exception on an + * on-chain revert (`status == 0`). + */ + fc::variant wait_for_receipt(const std::string& tx_hash, + const ethereum_confirm_options& opts = ethereum_confirm_option_defaults); + /** * @brief Retrieves logs based on filter parameters. * @param params The filter parameters for fetching logs. @@ -595,7 +621,7 @@ class ethereum_client : public std::enable_shared_from_this { * @param params Parameters to pass to the contract function * @param gas_limit_floor Least gas limit to fund the transaction with, or 0 * to use the buffered estimate; see `ethereum_confirm_options::gas_limit_floor` - * @param gas_limit_cap Most gas to fund it with, and the bound the estimate + * @param gas_limit_cap The gas limit to send with, and the bound the estimate * runs under, or 0 for none; see `ethereum_confirm_options::gas_limit_cap` * @return Configured eip1559_tx ready for signing and submission */ diff --git a/libraries/libfc/src/network/ethereum/ethereum_client.cpp b/libraries/libfc/src/network/ethereum/ethereum_client.cpp index 6c9436ae53..ac96d07d3c 100644 --- a/libraries/libfc/src/network/ethereum/ethereum_client.cpp +++ b/libraries/libfc/src/network/ethereum/ethereum_client.cpp @@ -289,18 +289,22 @@ fc::variant ethereum_client::get_syncing_status() { * Constructs a complete EIP-1559 transaction by: * - Fetching current gas configuration (base fee, priority fee) * - Encoding the contract call data according to the ABI - * - Estimating gas usage and adding a 20% buffer, then raising the result to - * `gas_limit_floor` when the caller funds the call to a floor + * - Sizing the gas limit: with a cap, the cap IS the limit and the estimate + * is a pre-flight at that gas; without one, the estimate plus a 20% buffer, + * raised to `gas_limit_floor` when the caller funds the call to a floor * - Setting the nonce from the pending transaction count * * @param to The recipient address (contract address for contract calls) * @param contract The ABI contract definition for encoding the call data * @param params The parameters to pass to the contract function * @param gas_limit_floor Least gas limit to fund with, or 0 for the buffered estimate + * @param gas_limit_cap The limit to send with, or 0 for the buffered estimate * @return A configured eip1559_tx ready for signing and submission - * @throws fc::network::json_rpc::json_rpc_exception if any RPC call fails - * @throws ethereum_transaction_policy_exception if the estimate or the floor - * breaches the client's `max_gas_limit` + * @throws fc::network::json_rpc::json_rpc_exception if any RPC call fails -- + * including the node refusing a capped pre-flight that cannot succeed + * inside the cap + * @throws ethereum_transaction_policy_exception if the estimate, the floor or + * the cap breaches the client's `max_gas_limit` */ eip1559_tx ethereum_client::create_default_tx(const address_compat_type& to, const abi::contract& contract, const fc::variants& params, uint64_t gas_limit_floor, @@ -321,18 +325,27 @@ eip1559_tx ethereum_client::create_default_tx(const address_compat_type& to, con field, wide.str(), _transaction_policy.max_gas_limit.str()); } }; - if (gas_limit_cap != 0) require_within_policy(gas_limit_cap, transaction_policy_field::gas_limit_cap); - - auto estimated_gas = estimate_gas(to, contract, data, gc, gas_limit_cap); - auto gas_limit = derive_buffered_gas_limit(_transaction_policy, estimated_gas); - if (gas_limit_floor != 0) { - require_within_policy(gas_limit_floor, transaction_policy_field::gas_limit_floor); - const fc::uint256 floor{gas_limit_floor}; - if (gas_limit < floor) gas_limit = floor; - } + + fc::uint256 gas_limit; if (gas_limit_cap != 0) { - const fc::uint256 cap{gas_limit_cap}; - if (gas_limit > cap) gas_limit = cap; + // A capped call is sent with exactly the cap. The estimate runs under + // it as a pre-flight -- the node refuses a call that cannot succeed + // inside the cap -- and its VALUE is discarded: what is simulated is + // what is sent, so neither the headroom buffer nor the buffered + // ceiling check has anything to add. (Buffering a call that uses most + // of its cap would only manufacture a policy rejection for a + // transaction the cap already carries.) + require_within_policy(gas_limit_cap, transaction_policy_field::gas_limit_cap); + (void) estimate_gas(to, contract, data, gc, gas_limit_cap); + gas_limit = fc::uint256{gas_limit_cap}; + } else { + auto estimated_gas = estimate_gas(to, contract, data, gc, 0); + gas_limit = derive_buffered_gas_limit(_transaction_policy, estimated_gas); + if (gas_limit_floor != 0) { + require_within_policy(gas_limit_floor, transaction_policy_field::gas_limit_floor); + const fc::uint256 floor{gas_limit_floor}; + if (gas_limit < floor) gas_limit = floor; + } } return eip1559_tx{.chain_id = get_chain_id(), @@ -614,6 +627,12 @@ std::string ethereum_client::send_raw_transaction(const std::string& raw_tx_data std::string ethereum_client::wait_for_confirmation(const std::string& tx_hash, const ethereum_confirm_options& opts) { + (void) wait_for_receipt(tx_hash, opts); + return tx_hash; +} + +fc::variant ethereum_client::wait_for_receipt(const std::string& tx_hash, + const ethereum_confirm_options& opts) { // Phase 1: wait for the receipt to exist. A null result means the tx // hasn't been included yet — retry with backoff. A `status == 0` means // the EVM executed the tx and reverted — propagate as a fatal error so @@ -640,7 +659,7 @@ std::string ethereum_client::wait_for_confirmation(const std::string& tx_hash, if (opts.confirmations <= 1) { // Receipt exists: tx is in the head block. That's one confirmation by // definition; no further wait required. - return tx_hash; + return receipt; } // Phase 2: wait for `opts.confirmations - 1` more blocks on top of the @@ -663,7 +682,7 @@ std::string ethereum_client::wait_for_confirmation(const std::string& tx_hash, return std::nullopt; }); - return tx_hash; + return receipt; } std::string ethereum_client::send_transaction_and_confirm(const std::string& raw_tx_data, From 1705ec9947a2f18d2928a785f42defc8b9282295 Mon Sep 17 00:00:00 2001 From: Josh Burroughs Date: Thu, 8 Oct 2026 15:39:48 -0400 Subject: [PATCH 5/9] outpost_ethereum_client: receipt-pinned reads, a progress invariant per call, and a fee-bounded gas ceiling The relay's `deliver_outbound_envelope` now takes every decision from reads pinned to one block -- the head when the tick starts, then the block each confirmed `epochIn` landed in (from its receipt) -- never `latest`, which a backend behind the block that just confirmed could serve from before the call. A backend that does not know the block yet is retried for a bounded time. `epoch_in` returns an `epoch_in_receipt` (hash, block, logs) built from `create_default_tx` + `execute_contract_tx_fn` + `wait_for_receipt`, and the `OPPInbound` events in the receipt are summarised into the per-call log line. Every call paid for must move the outpost's cursor (`advanced`): a confirmed call that left every field where it was was under-funded for the attestation at the cursor, so the next is funded double; one at the ceiling that still moves nothing is logged once at error level per cursor position and ends the tick in the new `outpost_delivery_incomplete_exception` (3110009), which the job logs at info and does NOT mark handled, so the next tick resumes. The same exception ends a tick that reaches `MAX_CONTINUATIONS_PER_TICK` with the epoch still open (the loop no longer sends 33 calls and returns normally), and a tick whose outpost is still on an earlier epoch (`outpost_behind`, instead of a non-sequential revert). Before a continuation the relay checks that `keccak256` of the envelope it holds IS the settled digest, and sends nothing when it is not. A recorded, untipped delivery is re-sent only when the outpost's own `pendingConsensusForDigest(own digest)` says the contract's path-2 predicate holds (boundary elapsed AND a strict majority agreeing); otherwise the relay waits for the group. Settlement is a four-way classification (`never_delivered` / `recorded` / `divergent` / `settled`) that asserts both words are 32 bytes instead of failing open. Gas: `delivery_gas_ceiling(policy, max_fee_per_gas)` bounds the ceiling by what `max_total_native_cost` pays for at the fee the call is sent at, so a budget the ceiling admits is never refused by the policy at signing. A client handed an OPPInbound address is refused at construction when its static ceiling is below `DELIVERY_MINIMUM_GAS_CEILING` (one full-cap delivery: 9 932 160 gas), and warned when its bounded total-cost term is. Estimate-time reverts are classified: `OPP_DispatchUnderfunded` / `OPP_HandlerGasExhausted` below the ceiling retry once at the ceiling; `OPP_NonSequentialEpoch`, `OPP_OperatorAlreadyDelivered`, `OPP_NotActiveOperator` and `OPP_DigestMismatch` re-read at the head a bounded number of times. Also: chunk-era comments and the stale `discardEnvelopeChunks` / `ETHEREUM_MAX_CHUNK_BYTES` mentions removed; README delivery, policy, diagnostics and test sections rewritten; `docs/ethereum-client-config.example.json` raised from a 2M `max_gas_limit` (below the emit floor) to the EIP-7825 cap; the delivery-timeout sizing comment and the `outpost_client` SPI doc updated; `tests/fixtures/ethereum-abi-opp{,-inbound}-current.json` regenerated from the current wire-ethereum artifacts (hardhat JSON, 117 entries, incl. the events and `pendingConsensusForDigest`). Tests: the delivery fixture now runs over a scripted chain (block number, fee RPCs) with receipt-returning `epochIn`, pinned-block view stubs and scripted refusals. New cases: fee-bounded ceiling, refusal of an undeliverable policy, settlement classification, the path-2 predicate, the progress invariant, pinned revert selectors and event topics, receipt summaries, consensus retry gated on the outpost's majority view, escalation after a no-op call and the stall report at the ceiling, gas refusals retried at the ceiling, stale refusals re-read at the head, outpost behind, the continuation bound, and a continuation refused with a non-settled envelope. The job test covers the incomplete-delivery retry. Co-Authored-By: Claude Fable 5.1 Change-Id: I67e24428e4e82717e0f7a7025ce064826fe11e5a --- docs/ethereum-client-config.example.json | 4 +- .../chain/include/sysio/chain/exceptions.hpp | 2 + plugins/batch_operator_plugin/README.md | 3 +- .../src/batch_operator_plugin.cpp | 28 +- .../src/outpost_opp_job.cpp | 21 +- .../test/test_outpost_opp_job.cpp | 32 + .../sysio/outpost_client/outpost_client.hpp | 12 +- .../outpost_ethereum_client_plugin/README.md | 121 ++- .../sysio/outpost_ethereum_client_plugin.hpp | 131 ++- .../outpost_ethereum_client.hpp | 227 ++++- .../src/outpost_ethereum_client.cpp | 842 +++++++++++----- .../test/test_ethereum_transaction_policy.cpp | 117 ++- .../test_outpost_ethereum_client_plugin.cpp | 908 ++++++++++++++++-- tests/fixtures/ethereum-abi-opp-current.json | 62 +- .../ethereum-abi-opp-inbound-current.json | 344 +++---- 15 files changed, 2233 insertions(+), 621 deletions(-) diff --git a/docs/ethereum-client-config.example.json b/docs/ethereum-client-config.example.json index 968093ef24..d45bc29da9 100644 --- a/docs/ethereum-client-config.example.json +++ b/docs/ethereum-client-config.example.json @@ -11,8 +11,8 @@ "transaction_policy": { "max_priority_fee_per_gas_wei": "2000000000", "max_fee_per_gas_wei": "100000000000", - "max_gas_limit": "2000000", - "max_total_native_cost_wei": "250000000000000000" + "max_gas_limit": "16777216", + "max_total_native_cost_wei": "1700000000000000000" } } ] diff --git a/libraries/chain/include/sysio/chain/exceptions.hpp b/libraries/chain/include/sysio/chain/exceptions.hpp index e2a02ce5d8..02dcfbe5a9 100644 --- a/libraries/chain/include/sysio/chain/exceptions.hpp +++ b/libraries/chain/include/sysio/chain/exceptions.hpp @@ -438,6 +438,8 @@ namespace sysio { namespace chain { 3110007, "Missing Trace API Plugin" ) FC_DECLARE_DERIVED_EXCEPTION( signing_transient_exception, plugin_exception, 3110008, "Transient signing-provider failure; the operation should be retried" ) + FC_DECLARE_DERIVED_EXCEPTION( outpost_delivery_incomplete_exception, plugin_exception, + 3110009, "Outpost delivery left the epoch open; the next tick resumes it" ) FC_DECLARE_DERIVED_EXCEPTION( wallet_exception, chain_exception, diff --git a/plugins/batch_operator_plugin/README.md b/plugins/batch_operator_plugin/README.md index 5930f639ca..36fbccfcac 100644 --- a/plugins/batch_operator_plugin/README.md +++ b/plugins/batch_operator_plugin/README.md @@ -17,7 +17,8 @@ Run by every member of the elected group, for every active outpost chain: **Outbound (WIRE → Outposts):** 1. Read the outpost's outbound envelope that `sysio.epoch::advance` built (`sysio.msgch::buildenv`) from the depot's `outenvelopes` table -2. Deliver it to the Outpost contract, chunked (ETH: `OPPInbound.epochIn()`, SOL: `epoch_in`) +2. Deliver it to the Outpost contract (ETH: one `OPPInbound.epochIn()` carrying the whole envelope, + continued while its dispatch spills; SOL: `epoch_in`, chunked) 3. The outpost reaches consensus once enough group members have delivered identical bytes **Inbound (Outposts → WIRE):** diff --git a/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp b/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp index 6220093a33..672448bbca 100644 --- a/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp +++ b/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp @@ -1942,19 +1942,21 @@ void batch_operator_plugin::set_program_options(options_description& cli, opts("batch-epoch-poll-ms", bpo::value()->default_value(whole_milliseconds(EPOCH_POLL)), "How often to check epoch state (ms)"); // SIZING RULE for batch-delivery-timeout-ms: it bounds the WHOLE outbound - // delivery, and an Ethereum delivery is now one transaction PER CHUNK - // (whole-envelope on Ethereum, continued while it spills; Solana chunks at 672). Size it as - // total chunks x (target block time + confirmation margin) - // for the largest envelope the outpost is expected to carry. At the 32768 - // byte platform envelope cap that is 4 Ethereum chunks, so the 15000 ms - // default covers a 1 s block time with wide margin. Undersizing is not fatal: - // the relay resumes from the on-chain per-operator high-water mark on the - // next cron tick, so a truncated tick converges rather than restarting from - // chunk zero. SHARED KNOB: this same value also bounds the depot - // push_action completion wait (see push_action above); post-WIRE-331 a - // timeout there is safe (the async_action_completion callback retains its - // own state), but raising this option lengthens BOTH waits, so raise it for - // chunking deliberately rather than as a blanket tuning step. + // delivery, which is a sequence of confirmed transactions. On Ethereum the + // whole envelope goes in ONE `epochIn`, continued by a further call for each + // time the outpost's dispatch spills (a full-cap envelope spills a handful + // of times at most); on Solana the envelope is chunked at 672 bytes. Size it + // as + // expected transactions x (target block time + confirmation margin) + // for the largest envelope the outpost is expected to carry; the 15000 ms + // default covers a 1 s block time with wide margin. Undersizing is not + // fatal: the relay resumes from the outpost's on-chain cursor on the next + // cron tick, so a truncated tick converges rather than restarting. SHARED + // KNOB: this same value also bounds the depot push_action completion wait + // (see push_action above); post-WIRE-331 a timeout there is safe (the + // async_action_completion callback retains its own state), but raising this + // option lengthens BOTH waits, so raise it for delivery deliberately rather + // than as a blanket tuning step. // Help text below must stay ASCII with no " --" sequence (PerformanceHarness // splits nodeop --help output on that token). opts("batch-delivery-timeout-ms", bpo::value()->default_value(whole_milliseconds(DELIVERY_TIMEOUT)), diff --git a/plugins/batch_operator_plugin/src/outpost_opp_job.cpp b/plugins/batch_operator_plugin/src/outpost_opp_job.cpp index 35aa82ca92..3b19f1a1a0 100644 --- a/plugins/batch_operator_plugin/src/outpost_opp_job.cpp +++ b/plugins/batch_operator_plugin/src/outpost_opp_job.cpp @@ -5,6 +5,8 @@ #include #include +#include + namespace sysio { namespace { @@ -117,11 +119,15 @@ void outpost_opp_job::run_outbound() { } FC_LOG_AND_DROP("outpost_opp_job[{}]: emit_debug_envelope threw", _client->to_string()); try { - // An EMPTY tx id is a defined outcome, not a failure: the relay skipped a - // delivery whose epoch the outpost has already advanced past (chunked - // Ethereum deliveries read that back before spending gas on late no-ops). - // The epoch is correctly marked handled below; the relay's own - // "skipping epoch=..." ilog is the truthful record of what happened. + // An EMPTY tx id is a defined outcome, not a failure: the relay found + // nothing to send — the outpost had already finalized the epoch, or + // (Ethereum) it tipped on another relay's digest, or this relay's + // delivery is recorded and waits on the rest of the group. The epoch is + // correctly marked handled below; the relay's own ilog is the truthful + // record of what happened. An epoch the relay could NOT finish with — + // the outpost behind, a continuation bound reached, a call at the gas + // ceiling that advanced nothing — arrives as + // `outpost_delivery_incomplete_exception` and is retried next tick. auto tx_id = _client->deliver_outbound_envelope(epoch, pending->raw_envelope, _outpost_deadline); ilog("outpost_opp_job[{}]: {} outbound envelope ({} bytes) tx={}", _client->to_string(), @@ -135,6 +141,11 @@ void outpost_opp_job::run_outbound() { } else { _last_outbound_epoch = epoch; } + } catch (const chain::outpost_delivery_incomplete_exception& e) { + // Expected while an epoch spans ticks: not a failure, and not marked + // handled, so the next tick resumes it from the outpost's cursor. + ilog("outpost_opp_job[{}]: outbound delivery for epoch {} continues next tick: {}", + _client->to_string(), epoch, e.top_message()); } catch (const fc::network::json_rpc::json_rpc_error& e) { wlog("outpost_opp_job[{}]: outbound delivery failed: code={} message='{}' data={} detail='{}'", _client->to_string(), e.code, e.top_message(), diff --git a/plugins/batch_operator_plugin/test/test_outpost_opp_job.cpp b/plugins/batch_operator_plugin/test/test_outpost_opp_job.cpp index d1dabc7397..4f5a617d19 100644 --- a/plugins/batch_operator_plugin/test/test_outpost_opp_job.cpp +++ b/plugins/batch_operator_plugin/test/test_outpost_opp_job.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "mocks/mock_depot_ops.hpp" #include "mocks/mock_outpost_client.hpp" @@ -286,6 +287,37 @@ BOOST_AUTO_TEST_CASE(run_outbound_swallows_exceptions_and_does_not_mark_epoch) { BOOST_REQUIRE_EQUAL(depot.emitted_events.size(), 2u); } +/// A delivery the relay could not finish with — the outpost behind, a +/// continuation bound reached, a stalled call at the gas ceiling — is not a +/// failure and not a handled epoch: the job neither cranks nor marks it, and +/// the next tick delivers again. +BOOST_AUTO_TEST_CASE(run_outbound_retries_an_incomplete_delivery_next_tick_without_cranking) { + auto client = make_client(CHAIN_KIND_EVM, 0, 31337); + mock_depot_ops depot; + depot.epoch = 5; + outbound_envelope_record rec; + rec.raw_envelope = {'x'}; + depot.pending_response = [rec](uint64_t, uint32_t) -> std::optional { + return rec; + }; + client->deliver_response = [](const auto&) -> std::string { + SYS_THROW(sysio::chain::outpost_delivery_incomplete_exception, "still open"); + }; + + outpost_opp_job job(client, depot, kDeadline); + BOOST_CHECK_NO_THROW(job.run_outbound()); + BOOST_CHECK_EQUAL(client->outbound_calls.size(), 1u); + BOOST_CHECK_EQUAL(client->crank_calls.size(), 0u); + + // Not marked: the next tick resumes it, and the crank rides the delivery that lands. + client->deliver_response = [](const auto&) { return std::string{"0xfinished"}; }; + job.run_outbound(); + BOOST_CHECK_EQUAL(client->outbound_calls.size(), 2u); + BOOST_CHECK_EQUAL(client->crank_calls.size(), 1u); + job.run_outbound(); + BOOST_CHECK_EQUAL(client->outbound_calls.size(), 2u); +} + BOOST_AUTO_TEST_CASE(run_outbound_emits_event_on_failure_and_retries_next_tick) { // Reproduces the dev-026 Solana groups-of-7 symptom: delivery throws a // json_rpc_error with -32602/empty body, the cron tick swallows it, and diff --git a/plugins/outpost_client_plugin/include/sysio/outpost_client/outpost_client.hpp b/plugins/outpost_client_plugin/include/sysio/outpost_client/outpost_client.hpp index fbe9cb1f7f..9f50e12041 100644 --- a/plugins/outpost_client_plugin/include/sysio/outpost_client/outpost_client.hpp +++ b/plugins/outpost_client_plugin/include/sysio/outpost_client/outpost_client.hpp @@ -93,14 +93,22 @@ class outpost_client { * * Must enforce `deadline` internally; a hung chain RPC must not block the * caller beyond this duration. Solana may chunk one envelope across - * multiple `epoch_in` transactions; the consensus-reaching transaction + * multiple `epoch_in` transactions; Ethereum sends the whole envelope in + * one `epochIn` and re-sends it while the outpost reports the epoch tipped + * on this relay's digest and unfinished. The consensus-reaching transaction * performs the outpost's outbound emit internally. * * @param epoch_index The current WIRE epoch this envelope belongs to. * @param envelope_bytes Raw protobuf `opp::Envelope` bytes. * @param deadline Upper bound on the total time spent talking to the * remote chain for this call. - * @return Chain-native transaction id / signature suitable for logs. + * @return Chain-native transaction id / signature suitable for logs, or + * EMPTY when the relay found nothing to send and the epoch counts + * as handled (the outpost had already finalized it, or it settled + * on another relay's envelope). + * @throws chain::outpost_delivery_incomplete_exception when the epoch is + * left open for THIS relay to finish on a later tick; the job does + * not mark it handled. * @throws fc::exception on RPC failure or deadline expiry. */ virtual std::string deliver_outbound_envelope(uint32_t epoch_index, diff --git a/plugins/outpost_ethereum_client_plugin/README.md b/plugins/outpost_ethereum_client_plugin/README.md index 65b6f47c29..49c4b8b4cf 100644 --- a/plugins/outpost_ethereum_client_plugin/README.md +++ b/plugins/outpost_ethereum_client_plugin/README.md @@ -59,6 +59,23 @@ optional, but when present all four fields must be set, each amount must be a ca (digits only, non-zero, no leading zero), and `max_priority_fee_per_gas_wei` must not exceed `max_fee_per_gas_wei`. File-configured chain ids are always verified against `eth_chainId`. +A client that delivers OPP envelopes (one handed an `OPPInbound` address by `create_outpost_client`) must +also fund a delivery: its `max_gas_limit`, bounded by EIP-7825's 16 777 216 per-transaction cap, has to +reach `DELIVERY_MINIMUM_GAS_CEILING` (9 932 160 gas — what an envelope at the 32 768-byte platform cap +costs to deliver and tip, dispatch one attestation at its allowance, and emit). The relay refuses to be +built on a smaller ceiling, since the largest envelope the platform allows could never complete on it. The +total-cost term binds at the fee a call is sent at: when `max_total_native_cost_wei / max_fee_per_gas_wei` +is below that minimum the relay starts with a warning, and deliveries at fees near the policy maximum spill +into more continuations than their budgets size for. + +What the signer needs follows from the same arithmetic. Every `epochIn` is sent with its budget as the gas +limit — a full-cap first delivery at the EIP-7825 cap, less for a continuation — and a node reserves +`gas_limit x max_fee_per_gas` of the signer's balance for it whatever the call uses (about 0.7 ETH at +16 777 216 gas and a 20 gwei base fee, returned on inclusion minus the gas burnt). A default geth also +refuses a submission whose `gas_limit x maxFeePerGas` exceeds its 1 ETH `--rpc.txfeecap` (about 59.6 gwei at +the cap); raise that cap on the operator's own node or accept that deliveries at higher fees are refused by +the node rather than by the policy. + Chain-id resolution is retried before it gives up: initial backoff 200 ms, doubling to a 1 s ceiling, under a 5 s total budget. Failure raises `plugin_config_exception` naming the client, the sanitized endpoint, and a stable `last_failure` category rather than the response body. @@ -97,34 +114,64 @@ confirmations — OPP writes are consensus-critical and must not silently drop. | Wrapper | Contract | Members | |---|---|---| | `opp_contract_client` | `OPP.sol` | `emitOutboundEnvelope(uint32)` (recovery-only write; no in-tree steady-state caller), `getLatestOutboundEnvelope()` view | -| `opp_inbound_contract_client` | `OPPInbound.sol` | `epochIn(uint32,uint16,uint16,uint32,bytes)`, `discardEnvelopeChunks()`, `nextEpochIndex()` view, `envelopeChunkState(address)` view, `attestationHandlers(uint16)` view | +| `opp_inbound_contract_client` | `OPPInbound.sol` | `epochIn(uint32,bytes)` (funded per call, returns the confirmed receipt), `nextEpochIndex()`, `dispatchSpill(uint32)`, `epochDeliveries(uint32,address)`, `pendingEpochHash()`, `pendingConsensusForDigest(bytes32)` and `attestationHandlers(uint16)` views | | `syndication_pool_contract_client` | `SyndicationPool.sol` (Wire-Network/wire-ethereum#207) | `realizeYield()` | -### Outbound delivery and chunking +### Outbound delivery `deliver_outbound_envelope` refuses an empty envelope and one larger than `OPP_MAX_ENVELOPE_BYTES` (32 768), -then splits the rest into `ceil(size / ETHEREUM_MAX_CHUNK_BYTES)` `epochIn` transactions. -`ETHEREUM_MAX_CHUNK_BYTES` is 8 192 — a compiled-in mirror of `MAX_CHUNK_BYTES` in wire-ethereum's -`OPPCommon.sol`, never configured — so an envelope costs at most four transactions. Every non-final chunk is -exactly that size and the final chunk carries the remainder; there is no terminal call and no crank, because -the contract finalizes inline on the chunk that completes the envelope. Chunks are submitted sequentially -with one transaction in flight at a time, so the signer's nonce advances in lock-step. - -A single-chunk envelope stages nothing on chain and pays no extra round trip. A multi-chunk delivery first -reads `envelopeChunkState(self)` at the `latest` block tag — this is the relay's own staging high-water mark, -not content WIRE commits consensus against — and decides what to do from the header alone: - -| Staged header | Action | +then sends the WHOLE envelope in ONE `epochIn(uint32,bytes)`. Ethereum bounds a transaction by gas, not +size — a full-cap envelope is ~1.3 M gas of calldata against EIP-7825's 16.7 M cap — and what may not fit +in one call is dispatch, which the contract spills: the call that reaches consensus routes as many +attestations as its gas allows, records where it stopped in `dispatchSpill(epoch)`, and a continuation is +the SAME call with the same arguments, resumed from that cursor. The outpost never stores the envelope's +bytes; every continuation re-supplies them. + +**Reads are pinned to one block.** Every decision is taken from `nextEpochIndex()`, `dispatchSpill(epoch)`, +`epochDeliveries(epoch, self)` and `pendingEpochHash()` read at ONE block number — the head when the tick +starts, then the block each confirmed call landed in (from its receipt) — never at `latest`, which a +backend behind the block that just confirmed could serve from before the call. A backend that does not +know the block yet is retried for a bounded time. The reads are not at `finalized` either: the cursor is +the outpost's own bookkeeping about work this relay is doing now, a reorg costs at most a re-sent call the +contract treats as an idempotent no-op, and a continuation gated on finality would wait ~64 blocks between +every stretch of dispatch. + +From those reads the relay decides, per `outpost_ethereum_client_detail::decide_delivery`: + +| Outpost state | Action | |---|---| -| Empty (`totalChunks == 0`), or owned by another signer | Send every chunk from index 0 | -| Ours, same epoch, same chunk count and total size, plausible progress | Resume from `receivedChunks` | -| Ours, same epoch, different shape, or more stored chunks than a well-formed upload can hold | `discardEnvelopeChunks()`, then send from index 0 | -| Ours, a different epoch | Read `nextEpochIndex()`; if the outpost has already consumed this epoch, skip the delivery entirely rather than pay for late no-ops | - -A `discardEnvelopeChunks()` that reverts at estimate time is treated as "already clear". An unreadable or -malformed state decodes as all-zero and degrades to starting fresh, which the contract absorbs as idempotent -no-ops. A mid-sequence failure simply abandons the tick; the next one restarts from the on-chain high-water -mark. +| `nextEpochIndex > epoch`, or the spill cursor reports `finalized` | Nothing to send; an EMPTY tx id marks the epoch handled | +| `nextEpochIndex < epoch` | The outpost is behind: `outpost_delivery_incomplete_exception`, retried next tick | +| Tipped and unfinished, and this relay's recorded digest IS `pendingEpochHash` | Continue: re-send the envelope (after checking its `keccak256` is the settled digest) | +| Tipped and unfinished on a digest this relay did not deliver, or delivered a minority of | Nothing to send; its deliverers carry the continuation | +| Untipped, nothing recorded for this relay | Deliver | +| Untipped, this relay recorded, and `pendingConsensusForDigest(own digest)` says the boundary has elapsed and a strict majority agrees | Re-deliver, so the outpost re-runs its path-2 tip | +| Untipped, this relay recorded, otherwise | Nothing to send until more of the group delivers | + +**Each call is funded to what it has left to carry** (`delivery_gas_budget`): a fixed cost, a per-byte cost, +an allowance per attestation the cursor says is still to dispatch, and the emit — doubled for each call this +tick that fell short, never above the ceiling. The ceiling (`delivery_gas_ceiling`) is the policy's +`max_gas_limit` bounded by EIP-7825's cap AND by what `max_total_native_cost` pays for at the current fee, +so a budget it admits is never refused by the policy at signing. The node's estimate is deliberately not +the budget: `epochIn` stops on a `gasleft()` watchdog and records where it stopped, so `eth_estimateGas` +converges on the least gas at which the call succeeds — the tip plus one attestation. Instead the budget is +both the pre-flight estimate's gas and the limit the transaction is sent with, so a call that cannot run +inside it is refused before anything is signed, and one that can is sent with exactly it. + +**Every call paid for must move the cursor.** After each confirmed call the relay reads the outpost again at +the receipt's block; a call that advanced nothing (no delivery recorded, no tip, no dispatch progress, no +finalization) was under-funded for the attestation at the cursor, and the next one is funded higher. A +node refusal at estimate time is classified by its revert: `OPP_DispatchUnderfunded` and +`OPP_HandlerGasExhausted` below the ceiling retry once at the ceiling; `OPP_NonSequentialEpoch`, +`OPP_OperatorAlreadyDelivered`, `OPP_NotActiveOperator` and `OPP_DigestMismatch` mean the outpost moved +under the read (another relay's call landed), so the relay re-reads at the head and decides again, a +bounded number of times. A call at the ceiling that advances nothing, or is refused for gas at the ceiling, +is an attestation no funding carries: logged once at error level per cursor position, then the tick ends in +`outpost_delivery_incomplete_exception` so the job retries rather than marks the epoch handled. The same +exception ends a tick that reaches `MAX_CONTINUATIONS_PER_TICK` (32) calls with the epoch still open. + +Calls are submitted sequentially, one in flight at a time, so the signer's nonce advances in lock-step; a +mid-sequence failure or deadline abandons the tick, and the next one resumes from the outpost's cursor. ### Inbound reads @@ -254,13 +301,20 @@ Startup and configuration: Per-client runtime lines are prefixed with the SPI label `outpost_ethereum_client[{chain_code}:{ChainKind}:{chain_id}]`: -- `epochIn chunk sent epoch=... chunk=.../... bytes=... tx=...` for each delivered chunk. -- `resuming epoch=... delivery at chunk ...`, `discarded a superseded epoch=... staging header`, and - `skipping epoch=... delivery — the outpost has ...` on the chunk-resume paths. +- `epochIn delivered|continued epoch=... bytes=... gas=... block=... tx=... — recorded=... tipped=... + dispatched=... finalized=...` for each confirmed call, summarising the `OPPInbound` events in its receipt. +- `skipping epoch=... delivery — the outpost has already finalized it`, `tipped on a digest this relay did + not deliver`, `delivery already recorded; consensus not tipped`, `re-delivering to run the boundary tip` + and `not delivered — the outpost is still on epoch ...` for each decision that sends nothing (or + re-sends). +- `refused at ... gas (...); retrying at the ceiling` at warning level for a gas refusal below the ceiling, + and `cannot advance past attestation N at the ceiling` at error level — once per cursor position — when + no funding carries the attestation there; `still open after N calls in one tick` at warning level when + the per-tick bound is reached. - `read inbound envelope epoch=... bytes=...` after a successful inbound read. -- Warning-level lines for every malformed view result (`envelopeChunkState returned non-string variant`, - `latestOutboundEnvelope data_ not a string`, and siblings); an epoch mismatch on the inbound slot stays at - debug level so steady-state polling is not noisy. +- Warning-level lines for every malformed view result (`latestOutboundEnvelope data_ not a string` and + siblings); an epoch mismatch on the inbound slot stays at debug level so steady-state polling is not + noisy. ## Tests @@ -272,9 +326,12 @@ ninja -C build/debug test_outpost_ethereum_client_plugin Two suites run in that binary. `outpost_ethereum_client_plugin` covers option registration, chain-id resolution and verification (explicit, RPC-resolved, mismatched, malformed, out-of-range, and transient transport failure), signature-provider rejection cases, contract-client construction and ABI encoding, the -chunk-count and chunk-resume decision tables, and every delivery path — single-chunk, multi-chunk in order, -resume from a staged high-water mark, peer-owned header, superseded header, reverting discard, epoch -advanced, deadline abandonment, and the empty and over-cap rejections — and the outpost crank: idle without +delivery decision table, settlement and receipt classification, the gas budget and ceilings, and every +delivery path — one call, tip-and-spill continued in the same tick, resume from a tipped cursor, minority +and never-delivered digests, consensus retry gated on the outpost's own majority view, pinned-block reads, +escalation after a call that advanced nothing and the stall report at the ceiling, revert classification, +outpost behind, the per-tick bound, epoch advanced, deadline abandonment, and the empty and over-cap +rejections — and the outpost crank: idle without the pool ABI or a registered handler, binding the registered pool and re-binding on a change, the pool's own three refusals, unrecognised reverts and protocol errors, and deadline abandonment. `outpost_ethereum_transaction_policy_tests` covers the expenditure-policy boundary: that a rejection happens diff --git a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp index 9bff80d200..1e26343870 100644 --- a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp +++ b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -65,8 +66,8 @@ struct opp_contract_client : ethereum_contract_client { inline constexpr uint64_t EIP_7825_TX_GAS_CAP = 16'777'216; /** - * @brief The most gas any `epochIn` may be funded with: the client's policy - * ceiling, bounded by EIP-7825's cap. + * @brief The most gas any `epochIn` may be funded with under the policy's + * static limit: `max_gas_limit`, bounded by EIP-7825's cap. * @param policy The client's local expenditure policy. * @return The ceiling, in gas. */ @@ -75,6 +76,28 @@ inline uint64_t delivery_gas_ceiling(const ethereum_transaction_policy& policy) return policy.max_gas_limit < cap ? policy.max_gas_limit.convert_to() : EIP_7825_TX_GAS_CAP; } +/** + * @brief The most gas any `epochIn` may be funded with at a given fee: the + * static ceiling, further bounded by what `max_total_native_cost` + * pays for at `max_fee_per_gas`. + * + * The policy refuses a transaction whose `gas_limit * max_fee_per_gas` + * exceeds `max_total_native_cost`, so a limit the static ceiling admits can + * still be a rejection at the fee the call is sent at. Sizing to this + * ceiling turns that late rejection into a smaller budget the contract + * spills across continuations instead. + * + * @param policy The client's local expenditure policy. + * @param max_fee_per_gas The fee the call will be sent at. + * @return The ceiling, in gas; the static ceiling when the fee is zero. + */ +inline uint64_t delivery_gas_ceiling(const ethereum_transaction_policy& policy, const fc::uint256& max_fee_per_gas) { + const uint64_t static_ceiling = delivery_gas_ceiling(policy); + if (max_fee_per_gas == 0) return static_ceiling; + const fc::uint256 affordable = policy.max_total_native_cost / max_fee_per_gas; + return affordable < fc::uint256{static_ceiling} ? affordable.convert_to() : static_ceiling; +} + /// Gas an `epochIn` pays before its dispatch loop, independent of size: the /// transaction base, the delivery record, and the consensus bookkeeping a tip /// adds. A ~700-byte tip reaches the loop at ~320k (wire-ethereum @@ -92,6 +115,15 @@ inline constexpr uint64_t ATTESTATION_GAS_ALLOWANCE = 1'500'000; /// `OPPInbound.EmitAttemptGasFloor`, below which the contract defers it. inline constexpr uint64_t EMIT_GAS_ALLOWANCE = 4'000'000; +/// The least static ceiling the relay accepts: what a full-cap envelope costs +/// to deliver and tip, dispatch one attestation at its allowance, and emit. +/// Under less, the largest envelope the platform allows cannot complete on +/// this client, so the relay refuses to be built on it rather than discover +/// that one epoch at a time. +inline constexpr uint64_t DELIVERY_MINIMUM_GAS_CEILING = DELIVERY_FIXED_GAS + + OPP_MAX_ENVELOPE_BYTES * DELIVERY_GAS_PER_BYTE + + ATTESTATION_GAS_ALLOWANCE + EMIT_GAS_ALLOWANCE; + /// What one `epochIn` has to carry, from which its gas budget is sized. struct delivery_gas_request { /// Encoded envelope size. @@ -142,9 +174,10 @@ inline uint64_t delivery_gas_budget(const delivery_gas_request& request) { * @brief Confirmation options for one `epochIn`: the defaults, funded to * exactly `gas_budget`. * - * Floor and cap are the same figure, so the transaction carries the budget - * and the pre-flight estimate runs under it: a call the budget cannot carry - * is refused by the node before it is signed. + * The cap is the limit the transaction is sent with and the gas its + * pre-flight estimate runs under: a call the budget cannot carry is refused + * by the node before it is signed, and a call that can is sent with the + * budget, never with a buffered estimate. * * @param gas_budget The budget from `delivery_gas_budget`. * @return The options the call is sent with. @@ -156,6 +189,56 @@ inline ethereum_confirm_options delivery_confirm_options(uint64_t gas_budget) { return options; } +/// `eth_getTransactionReceipt` field names the relay reads. +namespace ethereum_receipt_field { +inline constexpr auto block_number = "blockNumber"; +inline constexpr auto logs = "logs"; +} // namespace ethereum_receipt_field + +/// What one `epochIn` left on chain, read from its receipt once it confirmed. +struct epoch_in_receipt { + /// The transaction's hash, for logs. + std::string tx_hash; + /// The block that holds the transaction. Every read that decides the + /// relay's next move is pinned to it, so a backend lagging behind the block + /// that just confirmed cannot report the state from before the call. + fc::uint256 block_number; + /// The receipt's `logs`, as the node returned them: what the call did. + fc::variants logs; +}; + +/** + * @brief Read an `epoch_in_receipt` out of an `eth_getTransactionReceipt` result. + * @param tx_hash The confirmed transaction's hash. + * @param receipt The receipt object as `ethereum_client::wait_for_receipt` returned it. + * @return The block number and logs the relay acts on. + * @throws fc::exception when the receipt is not an object or carries no block number. + */ +inline epoch_in_receipt parse_epoch_in_receipt(std::string tx_hash, const fc::variant& receipt) { + FC_ASSERT(receipt.is_object(), "transaction receipt for {} is not an object", tx_hash); + const auto& object = receipt.get_object(); + FC_ASSERT(object.contains(ethereum_receipt_field::block_number), + "transaction receipt for {} carries no blockNumber", tx_hash); + epoch_in_receipt parsed{.tx_hash = std::move(tx_hash), + .block_number = fc::to_uint256(object[ethereum_receipt_field::block_number]), + .logs = {}}; + if (object.contains(ethereum_receipt_field::logs) && object[ethereum_receipt_field::logs].is_array()) { + parsed.logs = object[ethereum_receipt_field::logs].get_array(); + } + return parsed; +} + +/// ABI entry names of `OPPInbound.sol` the typed wrapper binds. +namespace opp_inbound_abi_name { +inline constexpr auto epoch_in = "epochIn"; +inline constexpr auto next_epoch_index = "nextEpochIndex"; +inline constexpr auto dispatch_spill = "dispatchSpill"; +inline constexpr auto epoch_deliveries = "epochDeliveries"; +inline constexpr auto pending_epoch_hash = "pendingEpochHash"; +inline constexpr auto pending_consensus_for_digest = "pendingConsensusForDigest"; +inline constexpr auto attestation_handlers = "attestationHandlers"; +} // namespace opp_inbound_abi_name + /// Typed contract client for OPPInbound.sol. Same confirmed-default /// policy as `opp_contract_client` for the write path. struct opp_inbound_contract_client : ethereum_contract_client { @@ -166,13 +249,15 @@ struct opp_inbound_contract_client : ethereum_contract_client { /// the contract resumes from its cursor. Funded to exactly `gas_budget` /// (see `delivery_gas_budget`), which is why this is not a plain /// `ethereum_contract_tx_fn`: that binds its options once at construction, - /// and the budget differs per call. `envelopeData` rides as a hex-encoded - /// string because the libfc ABI encoder takes `dt::bytes` that way (see + /// and the budget differs per call. Returns the confirmed receipt rather + /// than the hash alone, because the relay's next move is decided from the + /// block the call landed in. `envelopeData` rides as a hex-encoded string + /// because the libfc ABI encoder takes `dt::bytes` that way (see /// `ethereum_abi::encode_dynamic_data`). /// /// The ABI arguments are bound as non-const lvalue references, so callers /// must materialize named locals for both. - std::function epoch_in; + std::function epoch_in; /// `nextEpochIndex()` view — the epoch the outpost is currently accepting. ethereum_contract_call_fn next_epoch_index; /// `dispatchSpill(uint32 epochIndex)` view — where a tipped epoch's dispatch @@ -187,6 +272,12 @@ struct opp_inbound_contract_client : ethereum_contract_client { /// `pendingEpochHash()` view — the digest consensus settled on for the epoch /// the outpost is processing. ethereum_contract_call_fn pending_epoch_hash; + /// `pendingConsensusForDigest(bytes32 digest)` view — how close the current + /// epoch is to tipping on `digest`: `(nextEpoch, agreeing, groupSize, + /// currentEpochStartedAtTs, epochDurationSec_)`. The relay reads it before a + /// consensus retry, so a re-delivery is sent only when the contract's own + /// path-2 predicate would fire. The digest rides as `0x`-hex. + ethereum_contract_call_fn pending_consensus_for_digest; /// `attestationHandlers(uint16 attestationType)` view — the outpost's own /// inbound routing table: the `IOPPReceiver` registered for one attestation /// type, `address(0)` when none is and `ATTESTATION_BLACKHOLE` when governance @@ -200,15 +291,23 @@ struct opp_inbound_contract_client : ethereum_contract_client { const address_compat_type& contract_address, const std::vector& contracts) : ethereum_contract_client(client, contract_address, contracts) - , epoch_in([this](uint32_t& epoch_index, std::string& envelope_hex, uint64_t gas_budget) -> fc::variant { - return create_tx_and_confirm( - get_abi("epochIn"), delivery_confirm_options(gas_budget))(epoch_index, envelope_hex); + , epoch_in([this](uint32_t& epoch_index, std::string& envelope_hex, uint64_t gas_budget) -> epoch_in_receipt { + const auto& abi = get_abi(opp_inbound_abi_name::epoch_in); + const auto options = delivery_confirm_options(gas_budget); + contract_invoke_data_items params = {epoch_index, envelope_hex}; + auto tx = this->client->create_default_tx(this->contract_address, abi, params, options.gas_limit_floor, + options.gas_limit_cap); + const auto tx_hash = this->client->execute_contract_tx_fn(tx, abi, params).as_string(); + return parse_epoch_in_receipt(tx_hash, this->client->wait_for_receipt(tx_hash, options)); }) - , next_epoch_index(create_call(get_abi("nextEpochIndex"))) - , dispatch_spill(create_call(get_abi("dispatchSpill"))) - , epoch_deliveries(create_call(get_abi("epochDeliveries"))) - , pending_epoch_hash(create_call(get_abi("pendingEpochHash"))) - , attestation_handlers(create_call(get_abi("attestationHandlers"))) {} + , next_epoch_index(create_call(get_abi(opp_inbound_abi_name::next_epoch_index))) + , dispatch_spill(create_call(get_abi(opp_inbound_abi_name::dispatch_spill))) + , epoch_deliveries( + create_call(get_abi(opp_inbound_abi_name::epoch_deliveries))) + , pending_epoch_hash(create_call(get_abi(opp_inbound_abi_name::pending_epoch_hash))) + , pending_consensus_for_digest( + create_call(get_abi(opp_inbound_abi_name::pending_consensus_for_digest))) + , attestation_handlers(create_call(get_abi(opp_inbound_abi_name::attestation_handlers))) {} }; /// Typed contract client for wire-ethereum's `SyndicationPool.sol`, the liq diff --git a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp index af9db544e8..392a782361 100644 --- a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp +++ b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -17,8 +18,8 @@ namespace sysio { namespace outpost_ethereum_client_detail { /// Decoded `OPPInbound.dispatchSpill(uint32)` view result — where a tipped -/// epoch's processing stands. All-false/zero for an epoch that has not tipped, -/// which is also what an unreadable response decodes to. +/// epoch's processing stands. All-false/zero for an epoch that has not tipped; +/// an unreadable response is an error, never this. struct dispatch_spill { /// Consensus settled the epoch's digest and dispatch began. bool tipped = false; @@ -30,12 +31,69 @@ struct dispatch_spill { bool finalized = false; }; +/// How this relay's recorded delivery for an epoch relates to what the +/// outpost settled on. +enum class delivery_settlement { + /// Nothing is recorded under this relay for the epoch. + never_delivered, + /// Recorded, and the epoch has not tipped: the settled digest is not yet + /// this epoch's, so there is nothing to compare against. + recorded, + /// Recorded on a digest other than the one consensus settled: a minority + /// envelope, which must not be used to resume the epoch. + divergent, + /// Recorded on the settled digest: this relay may continue the epoch. + settled +}; + +/// Classify this relay's delivery record for an epoch. +/// +/// @param own_digest `epochDeliveries(epoch, self)` as a normalised word +/// (lower-case hex, no prefix); the zero word records nothing. +/// @param settled_digest `pendingEpochHash()` normalised the same way. +/// @param tipped `dispatchSpill(epoch).tipped`; until the tip the +/// settled digest belongs to an earlier epoch. +/// @throws fc::exception when either word is not exactly 32 bytes of hex: a +/// malformed read must not pass as "never delivered". +delivery_settlement classify_settlement(std::string_view own_digest, std::string_view settled_digest, + bool tipped); + +/// Decoded `OPPInbound.pendingConsensusForDigest(bytes32)` view result. +struct pending_consensus { + /// The epoch the outpost is accepting. + uint32_t next_epoch = 0; + /// Deliveries recorded for the digest in that epoch. + uint32_t agreeing = 0; + /// Members of the group scheduled for that epoch. + uint32_t group_size = 0; + /// `block.timestamp` at which the outpost's current epoch began. + uint64_t epoch_started_at = 0; + /// The outpost's mirror of the depot's `epoch_duration_sec`. + uint32_t epoch_duration_sec = 0; +}; + +/// The contract's path-2 predicate (`.claude/rules/opp-consensus.md`): the +/// boundary has elapsed and a strict majority of the group agrees. A +/// re-delivery sent while this is false is a paid no-op; one sent while it is +/// true re-runs the tip and settles the epoch. +/// +/// @param consensus the view, read for the digest this relay delivered. +/// @param now_sec wall clock, seconds since the epoch. +bool majority_tip_reachable(const pending_consensus& consensus, uint64_t now_sec); + /// What the relay should do about the epoch it is holding, given the outpost's /// view of that epoch. enum class delivery_action { - /// The epoch has not tipped: deliver the envelope — a first delivery, or - /// the idempotent same-digest re-delivery a path-2 consensus retry is. + /// Nothing recorded and the epoch has not tipped: deliver the envelope. deliver, + /// This relay's delivery is recorded, the epoch has not tipped, and the + /// contract's path-2 predicate holds: re-send the same envelope so the + /// outpost re-runs the tip (idempotent on a same-digest re-delivery). + retry_consensus, + /// This relay's delivery is recorded and the epoch has not tipped; a + /// re-delivery would change nothing until more of the group delivers (or, + /// before the outpost's own boundary, until that boundary passes). + await_peers, /// The epoch tipped on THIS relay's digest and is not finished: re-supply /// the envelope so the outpost dispatches the next stretch. continue_dispatch, @@ -44,7 +102,11 @@ enum class delivery_action { /// there is nothing for this one to send. wait_for_deliverer, /// The outpost has already finalized this epoch: nothing to send. - already_finalized + already_finalized, + /// The outpost is still on an earlier epoch: a delivery for this one would + /// be refused as non-sequential, and the epoch must be retried once the + /// outpost catches up. + outpost_behind }; /// Compare two EVM addresses for identity, tolerating an absent `0x` prefix @@ -56,15 +118,96 @@ bool same_evm_address(std::string_view lhs, std::string_view rhs); /// Pure and side-effect free so the table is unit-testable without an EVM /// node; the caller performs whichever RPC the verdict names. /// -/// @param next_epoch_index the outpost's `nextEpochIndex()`. -/// @param epoch_index WIRE epoch being delivered. -/// @param spill the outpost's `dispatchSpill(epoch_index)`. -/// @param own_delivery_settled whether this relay's recorded delivery digest -/// equals the settled `pendingEpochHash`. +/// @param next_epoch_index the outpost's `nextEpochIndex()`. +/// @param epoch_index WIRE epoch being delivered. +/// @param spill the outpost's `dispatchSpill(epoch_index)`. +/// @param settlement this relay's delivery record, see `classify_settlement`. +/// @param majority_reachable `majority_tip_reachable` for the digest this relay +/// delivered; consulted only for a recorded, untipped epoch. delivery_action decide_delivery(uint32_t next_epoch_index, uint32_t epoch_index, const dispatch_spill& spill, - bool own_delivery_settled); + delivery_settlement settlement, + bool majority_reachable); + +/// Everything the relay reads about one epoch at ONE block, so the pieces +/// cannot disagree with each other. +struct delivery_progress { + /// `nextEpochIndex()`. + uint32_t next_epoch_index = 0; + /// `dispatchSpill(epoch)`. + dispatch_spill spill; + /// `epochDeliveries(epoch, self)`, normalised; the zero word when none. + std::string own_digest; + /// `pendingEpochHash()`, normalised. + std::string settled_digest; +}; + +/// Whether the outpost moved the epoch forward between two reads: the +/// invariant every call the relay pays for must satisfy. A call that leaves +/// every field where it was did nothing, and sending it again at the same +/// budget would do nothing again. +bool advanced(const delivery_progress& before, const delivery_progress& after); + +/// `OPPInbound.epochIn` refusals the relay tells apart in the node's revert +/// bytes. Every one is raised at estimate time, before anything is signed. +enum class epoch_in_revert { + /// `OPP_DispatchUnderfunded(uint256,uint256)`: the budget does not reach + /// the contract's dispatch floor. + dispatch_underfunded, + /// `OPP_HandlerGasExhausted(address,uint16,uint256)`: the first handler of + /// the call ran out of its bounded gas — the budget is too small for the + /// attestation at the cursor, or no budget is. + handler_gas_exhausted, + /// `OPP_NonSequentialEpoch(uint32,uint32)`: the outpost is on another epoch. + non_sequential_epoch, + /// `OPP_OperatorAlreadyDelivered(uint32,address)`: a re-delivery the + /// contract treats as already counted. + operator_already_delivered, + /// `OPP_NotActiveOperator(address)`: this signer is not in the epoch's + /// group, or is not the settled digest's deliverer for a continuation. + not_active_operator, + /// `OPP_DigestMismatch(bytes32,bytes32)`: the envelope supplied for a + /// continuation is not the one consensus settled on. + digest_mismatch +}; + +/// Classify an `epochIn` revert. `std::nullopt` for a protocol error (the node +/// never ran the call) and for any revert that is not one of the refusals +/// above, exactly shaped: an unknown revert must not pass as a known one. +/// +/// @param rpc_code `json_rpc_error::code`. +/// @param revert_data `json_rpc_error::data`, the node's revert bytes as `0x`-hex. +std::optional classify_epoch_in_revert(int rpc_code, std::string_view revert_data); + +/// What one confirmed `epochIn` did, read from the `OPPInbound` events in its +/// receipt. Absent events mean the call recorded nothing new, which is the +/// contract's benign no-op for a late or redundant call. +struct delivery_receipt_summary { + /// `EpochDelivery(epoch, self, digest)`: this relay's delivery was recorded. + bool recorded = false; + /// `EpochConsensus(epoch, digest, count)`: the call tipped the epoch. + bool tipped = false; + /// `EpochDispatchProgressed(epoch, dispatched)`: where the call's dispatch + /// stopped, when it dispatched at all. + std::optional dispatched; + /// `EpochComplete(epoch)`: the call emitted and finalized the epoch. + bool finalized = false; +}; + +/// Summarise the `OPPInbound` events in one receipt's `logs`. +/// +/// @param logs the receipt's `logs` array, as the node returned it. +/// @param inbound_address the `OPPInbound` the relay delivers to; logs from +/// any other address (a handler's, a pool's) are ignored. +/// @param epoch_index the epoch delivered; events for another are ignored. +/// @param operator_address this relay's signer, for `EpochDelivery`. +delivery_receipt_summary summarize_delivery_receipt(const fc::variants& logs, std::string_view inbound_address, + uint32_t epoch_index, std::string_view operator_address); + +/// `keccak256` of the envelope bytes as a normalised word — the digest the +/// outpost records for a delivery and settles consensus on. +std::string envelope_digest_word(const std::vector& envelope_bytes); /// Why `SyndicationPool.realizeYield()` refused, read from the node's revert bytes. enum class realize_yield_refusal { @@ -122,6 +265,10 @@ bool is_routable_handler(std::string_view handler_address); */ class outpost_ethereum_client : public outpost_client { public: + /// @throws chain::plugin_config_exception when an OPPInbound address is + /// given and the client's static gas ceiling is below + /// `DELIVERY_MINIMUM_GAS_CEILING`: no envelope at the platform cap + /// could complete on it. outpost_ethereum_client(ethereum_client_entry_ptr entry, std::string opp_addr, std::string opp_inbound_addr, @@ -136,6 +283,22 @@ class outpost_ethereum_client : public outpost_client { std::vector authenticated_caller_address() const override; // to_string() inherits the base-class default: "{chain_code}:{ChainKind}:{chain_id}". + /// Deliver the envelope in ONE `epochIn`, then continue it while the + /// outpost reports it tipped on this relay's digest and unfinished. Every + /// decision is taken from a read pinned to one block — the head at the + /// start of the tick, then the block each confirmed call landed in — and + /// every call paid for must move the outpost's cursor, or the next one is + /// funded higher; a call at the ceiling that moves nothing is reported. + /// + /// @return The last transaction hash sent, or EMPTY when nothing was sent + /// because the outpost had already finalized the epoch or there is + /// nothing for this relay to send (the epoch tipped on another + /// digest, or this relay's delivery is recorded and awaits peers). + /// @throws chain::outpost_delivery_incomplete_exception when the tick ends + /// with the epoch still open for THIS relay to act on: the outpost + /// is behind, the per-tick continuation bound was reached, or a + /// call at the ceiling advanced nothing. The job retries next tick. + /// @throws fc::exception on transport failure or deadline expiry. std::string deliver_outbound_envelope(uint32_t epoch_index, const std::vector& envelope_bytes, fc::microseconds deadline) override; @@ -180,31 +343,33 @@ class outpost_ethereum_client : public outpost_client { /// @throws fc::exception on transport failure. std::optional discover_syndication_pool(); - /// Read `OPPInbound.nextEpochIndex()` at `latest`. - /// @throws fc::exception on transport failure, deadline expiry, or an - /// unparsable response. - uint32_t read_next_epoch_index(); - - /// Read `OPPInbound.dispatchSpill(epoch_index)` at `latest` and decode it. + /// Read everything `deliver_outbound_envelope` decides from — the epoch + /// cursor, the spill cursor, this relay's delivery record and the settled + /// digest — at ONE block. /// - /// `latest`, deliberately, where `read_inbound_envelope` reads at - /// `finalized`: the cursor is this outpost's own bookkeeping about work - /// this relay is doing right now, and the worst a reorg can cost is a - /// re-sent transaction the contract already treats as an idempotent no-op. - /// Finality lags head by roughly 64 blocks, so a continuation gated on it - /// would wait that out between every stretch of dispatch. + /// Pinned to a block number, never `latest`: the block is the head read at + /// the start of the tick, or the block a call this tick confirmed in, so a + /// backend serving `latest` from behind that block cannot hand back the + /// state from before the call and have the relay pay for it twice. A + /// backend that does not know the block yet is retried for a bounded time. + /// Not `finalized`, where `read_inbound_envelope` reads at `finalized`: the + /// cursor is this outpost's own bookkeeping about work this relay is doing + /// right now. A reorg under it costs a paid re-delivery before the tip or a + /// refused continuation after it, and the dearer direction — a send not made + /// because the read was behind — is what the per-call progress invariant and + /// the next tick cover; finality lags head by roughly 64 blocks, and a + /// continuation gated on it would wait that out between every stretch of + /// dispatch. /// /// @throws fc::exception on transport failure, deadline expiry, or an /// unparsable response. - outpost_ethereum_client_detail::dispatch_spill read_dispatch_spill(uint32_t epoch_index); + outpost_ethereum_client_detail::delivery_progress read_progress(uint32_t epoch_index, const fc::uint256& block); - /// Whether the digest this relay delivered for `epoch_index` is the one - /// consensus settled on (`epochDeliveries(epoch, self) == pendingEpochHash`). - /// False when this relay has not delivered, or delivered a minority digest. - /// + /// Read `OPPInbound.pendingConsensusForDigest(digest)` at `block` and decode it. /// @throws fc::exception on transport failure, deadline expiry, or an /// unparsable response. - bool own_delivery_settled(uint32_t epoch_index); + outpost_ethereum_client_detail::pending_consensus read_pending_consensus(const std::string& digest_word, + const fc::uint256& block); ethereum_client_entry_ptr _entry; std::string _opp_addr; @@ -220,6 +385,10 @@ class outpost_ethereum_client : public outpost_client { std::shared_ptr _syndication_pool_client; // nullable /// Cached `0x`-hex signer address — see `signer_address_hex()`. std::string _signer_address_hex; + /// The (epoch, cursor) a ceiling-funded call last failed to advance, so the + /// error is reported once and repeats stay at debug level until the cursor + /// moves. + std::optional> _reported_stall; uint64_t _outpost_id; uint32_t _chain_id; }; diff --git a/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp b/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp index 74fd89f872..5c2f7c625b 100644 --- a/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp +++ b/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp @@ -15,7 +15,10 @@ #include #include #include +#include +#include +#include #include #include @@ -33,16 +36,33 @@ constexpr std::string_view OP_READ_EMITTED_DIGEST = "read_emitted_envelope_diges constexpr std::string_view OP_REALIZE_YIELD = "crank_outpost:realizeYield"; +/// Retry label for the pinned progress read, see `PINNED_READ_RETRY`. +constexpr std::string_view OP_READ_PROGRESS = "ethereum:read_progress"; + /// Execution APIs code for a call the node executed and that reverted, as distinct from a /// protocol error such as a parse failure, where the node never ran the call at all. Not an /// EIP-1474 code — that spec only defines the negative range; 3 is geth's convention, which /// `ethereum/execution-apis` standardised. constexpr int ethereum_execution_reverted_code = 3; -/// Solidity signature of the revert `discardEnvelopeChunks()` raises when it has nothing of -/// ours to clear. Hashed rather than written out as its selector so a reader can check this -/// against the ABI directly; `chunk_buffer_missing_selector_is_pinned` holds the hash to the -/// four bytes the contract actually emits. +/// Solidity signatures of the `OPPInbound.epochIn` refusals the relay tells apart +/// (wire-ethereum `OPPErrors.sol`). Hashed rather than written out as selectors so a reader +/// can check them against the ABI directly; `epoch_in_revert_selectors_are_pinned` holds +/// each to the four bytes the contract actually emits. `OPP_HandlerGasExhausted` carries the +/// attestation type as the `uint16` the enum's ABI encoding is. +constexpr auto dispatch_underfunded_signature = "OPP_DispatchUnderfunded(uint256,uint256)"; +constexpr auto handler_gas_exhausted_signature = "OPP_HandlerGasExhausted(address,uint16,uint256)"; +constexpr auto non_sequential_epoch_signature = "OPP_NonSequentialEpoch(uint32,uint32)"; +constexpr auto operator_already_delivered_signature = "OPP_OperatorAlreadyDelivered(uint32,address)"; +constexpr auto not_active_operator_signature = "OPP_NotActiveOperator(address)"; +constexpr auto digest_mismatch_signature = "OPP_DigestMismatch(bytes32,bytes32)"; + +/// Solidity signatures of the `OPPInbound` events a delivery's receipt carries, hashed to +/// the topic the node reports; `delivery_event_topics_are_pinned` holds each to the bytes. +constexpr auto epoch_delivery_event_signature = "EpochDelivery(uint32,address,bytes32)"; +constexpr auto epoch_consensus_event_signature = "EpochConsensus(uint32,bytes32,uint32)"; +constexpr auto epoch_dispatch_progressed_event_signature = "EpochDispatchProgressed(uint32,uint16)"; +constexpr auto epoch_complete_event_signature = "EpochComplete(uint32)"; /// The three refusals `SyndicationPool.realizeYield()` raises for its own reasons /// (Wire-Network/wire-ethereum#207), hashed the same way; @@ -55,13 +75,15 @@ constexpr auto pool_underbacked_signature = "WIRE_PoolUnderbacked(uint64,uin constexpr auto enforced_pause_signature = "EnforcedPause()"; /// `ATTESTATION_BLACKHOLE` in wire-ethereum's `OPPCommon.sol`: the handler governance -/// registers to drop an attestation type on purpose. Mirror duty, like -/// `ETHEREUM_MAX_CHUNK_BYTES`. +/// registers to drop an attestation type on purpose. A compiled-in mirror of the contract +/// constant, never configured. constexpr auto attestation_blackhole_address = "0x000000000000000000000000000000000000dead"; /// ABI entry names and decoded-output field keys of the outpost contracts this /// client drives. Grouped per contract so a Solidity rename is one edit here -/// rather than a scatter of string literals. +/// rather than a scatter of string literals. The OPPInbound entry names live +/// with the wrapper (`opp_inbound_abi_name`); only its decoded field keys are +/// here. namespace opp_abi { constexpr auto view_latest_outbound_envelope = "getLatestOutboundEnvelope"; namespace field { @@ -78,36 +100,59 @@ constexpr auto tx_realize_yield = "realizeYield"; } // namespace syndication_pool_abi namespace opp_inbound_abi { -constexpr auto view_next_epoch_index = "nextEpochIndex"; -constexpr auto view_dispatch_spill = "dispatchSpill"; -constexpr auto view_epoch_deliveries = "epochDeliveries"; -constexpr auto view_pending_epoch_hash = "pendingEpochHash"; namespace field { -constexpr auto tipped = "tipped"; -constexpr auto dispatched = "dispatched"; -constexpr auto complete = "complete"; -constexpr auto finalized = "finalized"; +constexpr auto tipped = "tipped"; +constexpr auto dispatched = "dispatched"; +constexpr auto complete = "complete"; +constexpr auto finalized = "finalized"; +constexpr auto next_epoch = "nextEpoch"; +constexpr auto agreeing = "agreeing"; +constexpr auto group_size = "groupSize"; +constexpr auto current_epoch_started_at = "currentEpochStartedAtTs"; +constexpr auto epoch_duration_sec = "epochDurationSec_"; } } // namespace opp_inbound_abi -/// Most `epochIn` continuations one tick sends for one epoch before handing -/// the rest to the next tick. A bound, not a budget: a full-cap envelope -/// spills a handful of times at most, and a cursor still advancing past this -/// many is worth a log line and a fresh tick rather than an unbounded loop -/// inside one. +/// `eth_getTransactionReceipt` log entry field names. +namespace receipt_log_field { +constexpr auto address = "address"; +constexpr auto topics = "topics"; +constexpr auto data = "data"; +} // namespace receipt_log_field + +/// Most `epochIn` calls one tick sends for one epoch before handing the rest +/// to the next tick. A bound, not a budget: a full-cap envelope spills a +/// handful of times at most, and a cursor still advancing past this many is +/// worth a log line and a fresh tick rather than an unbounded loop inside one. constexpr uint32_t MAX_CONTINUATIONS_PER_TICK = 32; -/// Block tag for reading this outpost's own delivery bookkeeping -/// (`nextEpochIndex`, `dispatchSpill`, `epochDeliveries`, `pendingEpochHash`). -/// `latest`, deliberately, where the inbound-envelope read uses `finalized`: -/// see `outpost_ethereum_client::read_dispatch_spill`. -constexpr auto PROGRESS_READ_TAG = eth::block_tag_t::latest; +/// Most times one tick re-reads the outpost after the node refused a call for +/// a reason that says the state moved under the read (`OPP_NonSequentialEpoch` +/// and its siblings). A refusal that survives this many fresh reads is not a +/// race, and the tick reports it instead of reading again. +constexpr uint32_t MAX_STALE_REFUSALS_PER_TICK = 2; + +/// How the pinned progress read retries a backend that does not know the +/// block yet (a load-balanced endpoint behind the one that confirmed the +/// transaction). Short, because the block exists: the backend is behind by +/// seconds, not minutes, and the tick's own deadline bounds it anyway. +constexpr fc::task::retry_options PINNED_READ_RETRY{ + .initial_backoff = fc::milliseconds(250), + .max_backoff = fc::seconds(1), + .total_timeout = fc::seconds(10), +}; constexpr size_t EVM_ABI_WORD_BYTES = 32; constexpr size_t EVM_ADDRESS_BYTES = 20; constexpr size_t EVM_SELECTOR_BYTES = 4; constexpr size_t HEX_PREFIX_CHARS = 2; constexpr size_t HEX_CHARS_PER_BYTE = 2; +constexpr size_t EVM_ABI_WORD_HEX_CHARS = EVM_ABI_WORD_BYTES * HEX_CHARS_PER_BYTE; +constexpr size_t EVM_SELECTOR_HEX_CHARS = EVM_SELECTOR_BYTES * HEX_CHARS_PER_BYTE; +/// Hex digits a `uint64` occupies at the low end of a word; a word with a +/// non-zero digit above them does not fit a `uint64`. +constexpr size_t UINT64_HEX_CHARS = sizeof(uint64_t) * HEX_CHARS_PER_BYTE; +constexpr int HEX_RADIX = 16; constexpr size_t MAX_ENVELOPE_HEX_CHARS = HEX_PREFIX_CHARS + OPP_MAX_ENVELOPE_BYTES * HEX_CHARS_PER_BYTE; constexpr size_t MAX_LATEST_OUTBOUND_RPC_BYTES = @@ -116,6 +161,11 @@ constexpr size_t MAX_LATEST_OUTBOUND_RPC_BYTES = constexpr size_t MAX_LATEST_OUTBOUND_RPC_HEX_CHARS = HEX_PREFIX_CHARS + MAX_LATEST_OUTBOUND_RPC_BYTES * HEX_CHARS_PER_BYTE; +/// Topic index of the first indexed argument in a receipt log (`topics[0]` is +/// the event signature). +constexpr size_t FIRST_INDEXED_TOPIC = 1; +constexpr size_t SECOND_INDEXED_TOPIC = 2; + /// Drop a leading `0x` / `0X` if present. Hex is not self-delimiting, so every /// comparison below works on the prefix-free form. std::string_view strip_hex_prefix(std::string_view value) { @@ -154,11 +204,15 @@ std::optional abi_uint_output(const fc::variant& value) { return std::nullopt; } +/// The keccak256 of a Solidity signature as prefix-free lower-case hex: the +/// event topic, or (its first four bytes) the function / error selector. +std::string signature_hash_hex(const char* signature) { + return fc::crypto::keccak256::hash(std::string(signature)).str(); +} + /// The four-byte selector of a Solidity function or error `signature`, as hex. std::string selector_hex(const char* signature) { - return fc::crypto::keccak256::hash(std::string(signature)) - .str() - .substr(0, EVM_SELECTOR_BYTES * HEX_CHARS_PER_BYTE); + return signature_hash_hex(signature).substr(0, EVM_SELECTOR_HEX_CHARS); } /// True when `abis` declares a function named `name`. @@ -168,52 +222,18 @@ bool has_function_abi(const std::vector& abis, std::string_v }); } -/// Interpret one decoded ABI output word as a boolean. -/// -/// Tolerates every shape the decoder may hand back for a Solidity `bool` — a -/// native bool, the decimal strings the numeric path normalises to, or a raw -/// number. Returns `std::nullopt` for anything else so callers fail closed. -std::optional abi_bool_output(const fc::variant& value) { - if (value.is_bool()) return value.as_bool(); - if (value.is_string()) { - const auto text = value.as_string(); - if (text == "true") return true; - if (text == "false") return false; - } - const auto numeric = abi_uint_output(value); - if (numeric) return *numeric != 0; - return std::nullopt; -} - -/// Pull the single value out of a decoded view result. -/// -/// libfc picks one of three shapes: a BARE value when the function has one -/// output, a named object when every output is named, and a positional array -/// otherwise. A public state-variable getter takes the first path because its -/// output carries no name; a struct getter takes the second. Reading a scalar -/// through this keeps a caller indifferent to which one the decoder chose. -fc::variant decoded_scalar(const fc::variant& decoded) { - if (decoded.is_array()) { - const auto& items = decoded.get_array(); - return items.empty() ? fc::variant() : items[0]; - } - if (decoded.is_object()) { - const auto& object = decoded.get_object(); - return object.size() == 0 ? fc::variant() : object.begin()->value(); - } - return decoded; +/// Hex normalised for comparison: lower-case with no `0x` prefix. +std::string normalized_hex(std::string_view value) { + std::string hex{strip_hex_prefix(value)}; + std::ranges::transform(hex, hex.begin(), [](unsigned char c) { return std::tolower(c); }); + return hex; } /// A 32-byte word as the decoder renders it, normalised for comparison: /// lower-case hex with no `0x` prefix. Empty when the value is not a string. std::string normalized_word(const fc::variant& value) { if (!value.is_string()) return {}; - std::string word = value.as_string(); - if (word.size() >= HEX_PREFIX_CHARS && word[0] == '0' && (word[1] == 'x' || word[1] == 'X')) { - word.erase(0, HEX_PREFIX_CHARS); - } - std::ranges::transform(word, word.begin(), [](unsigned char c) { return std::tolower(c); }); - return word; + return normalized_hex(value.as_string()); } /// Whether a normalised word is the zero word (or empty — nothing recorded). @@ -221,6 +241,121 @@ bool is_zero_word(std::string_view word) { return std::ranges::all_of(word, [](char c) { return c == '0'; }); } +/// Whether `word` is exactly one ABI word of hex digits. +bool is_hex_word(std::string_view word) { + return word.size() == EVM_ABI_WORD_HEX_CHARS && + std::ranges::all_of(word, [](unsigned char c) { return std::isxdigit(c) != 0; }); +} + +/// The unsigned integer one normalised ABI word holds, or `std::nullopt` when +/// the word is malformed or does not fit a `uint64`. +std::optional word_to_uint(std::string_view word) { + if (!is_hex_word(word)) return std::nullopt; + const auto high = word.substr(0, EVM_ABI_WORD_HEX_CHARS - UINT64_HEX_CHARS); + if (!is_zero_word(high)) return std::nullopt; + try { + return std::stoull(std::string(word.substr(high.size())), nullptr, HEX_RADIX); + } catch (const std::exception&) { + return std::nullopt; + } +} + +/// The `n`-th ABI word of a receipt log's normalised `data`, or `std::nullopt` +/// when the data is shorter than that. +std::optional data_word(std::string_view data, size_t n) { + const size_t offset = n * EVM_ABI_WORD_HEX_CHARS; + if (data.size() < offset + EVM_ABI_WORD_HEX_CHARS) return std::nullopt; + return data.substr(offset, EVM_ABI_WORD_HEX_CHARS); +} + +/// The raw `eth_call` hex of one OPPInbound view, asserted to be a string. +std::string view_hex(const fc::variant& raw, std::string_view client_label, const char* view) { + FC_ASSERT(raw.is_string(), "outpost_ethereum_client[{}]: {} returned a non-string variant", client_label, view); + return raw.as_string(); +} + +/// `OPPInbound.nextEpochIndex()` at `block`. +uint32_t read_next_epoch_index(opp_inbound_contract_client& inbound, const eth::block_number_or_tag_t& block, + std::string_view client_label) { + const auto raw = view_hex(inbound.next_epoch_index(block), client_label, opp_inbound_abi_name::next_epoch_index); + // A single-output view decodes to the bare value. + const auto value = + abi_uint_output(eth::contract_decode_data(inbound.get_abi(opp_inbound_abi_name::next_epoch_index), raw)); + FC_ASSERT(value.has_value(), "outpost_ethereum_client[{}]: nextEpochIndex was not a parsable numeric output", + client_label); + return static_cast(*value); +} + +/// `OPPInbound.dispatchSpill(epoch_index)` at `block`, decoded. +/// +/// Fails closed on every field: an unparsable one must not be silently read +/// as "not tipped", which would send a fresh delivery into an epoch that has +/// already settled and be refused as a non-deliverer. +detail::dispatch_spill read_dispatch_spill(opp_inbound_contract_client& inbound, uint32_t epoch_index, + const eth::block_number_or_tag_t& block, std::string_view client_label) { + // `ethereum_contract_call_fn` binds its arguments as non-const lvalue + // references, so the epoch needs a named local. + uint32_t epoch_arg = epoch_index; + const auto raw = view_hex(inbound.dispatch_spill(block, epoch_arg), client_label, opp_inbound_abi_name::dispatch_spill); + const auto decoded = eth::contract_decode_data(inbound.get_abi(opp_inbound_abi_name::dispatch_spill), raw); + dlog("outpost_ethereum_client[{}]: dispatchSpill({}) decoded={}", client_label, epoch_index, + fc::json::to_string(decoded, fc::json::yield_function_t{})); + FC_ASSERT(decoded.is_object(), "outpost_ethereum_client[{}]: dispatchSpill result was not a variant object", + client_label); + + const auto& object = decoded.get_object(); + // The decoder hands a Solidity `bool` back as a native bool; anything else is a malformed read. + const auto read_bool = [&](const char* key) { + FC_ASSERT(object.contains(key), "outpost_ethereum_client[{}]: dispatchSpill result missing '{}'", client_label, + key); + FC_ASSERT(object[key].is_bool(), "outpost_ethereum_client[{}]: dispatchSpill '{}' was not a boolean output", + client_label, key); + return object[key].as_bool(); + }; + FC_ASSERT(object.contains(opp_inbound_abi::field::dispatched), + "outpost_ethereum_client[{}]: dispatchSpill result missing '{}'", client_label, + opp_inbound_abi::field::dispatched); + const auto dispatched = abi_uint_output(object[opp_inbound_abi::field::dispatched]); + FC_ASSERT(dispatched.has_value(), "outpost_ethereum_client[{}]: dispatchSpill '{}' was not a numeric output", + client_label, opp_inbound_abi::field::dispatched); + + detail::dispatch_spill spill; + spill.tipped = read_bool(opp_inbound_abi::field::tipped); + spill.dispatched = static_cast(*dispatched); + spill.complete = read_bool(opp_inbound_abi::field::complete); + spill.finalized = read_bool(opp_inbound_abi::field::finalized); + return spill; +} + +/// `OPPInbound.epochDeliveries(epoch_index, operator)` at `block`, normalised. +std::string read_own_digest(opp_inbound_contract_client& inbound, uint32_t epoch_index, + const std::string& operator_address, const eth::block_number_or_tag_t& block, + std::string_view client_label) { + uint32_t epoch_arg = epoch_index; + std::string operator_arg = operator_address; + const auto raw = view_hex(inbound.epoch_deliveries(block, epoch_arg, operator_arg), client_label, + opp_inbound_abi_name::epoch_deliveries); + return normalized_word(eth::contract_decode_data(inbound.get_abi(opp_inbound_abi_name::epoch_deliveries), raw)); +} + +/// `OPPInbound.pendingEpochHash()` at `block`, normalised. +std::string read_settled_digest(opp_inbound_contract_client& inbound, const eth::block_number_or_tag_t& block, + std::string_view client_label) { + const auto raw = view_hex(inbound.pending_epoch_hash(block), client_label, opp_inbound_abi_name::pending_epoch_hash); + return normalized_word(eth::contract_decode_data(inbound.get_abi(opp_inbound_abi_name::pending_epoch_hash), raw)); +} + +/// The block parameter for a read pinned to `block`. +eth::block_number_or_tag_t pinned(const fc::uint256& block) { + return eth::format_rpc_quantity(block); +} + +/// `json_rpc_error::data` as the revert hex it carries, or empty when the +/// node attached none. +std::string revert_hex(const fc::network::json_rpc::json_rpc_error& refusal) { + return refusal.data.is_string() ? refusal.data.as_string() : std::string{}; +} + } // namespace namespace outpost_ethereum_client_detail { @@ -231,10 +366,30 @@ bool same_evm_address(std::string_view lhs, std::string_view rhs) { return same_hex(left, strip_hex_prefix(rhs)); } +delivery_settlement classify_settlement(std::string_view own_digest, std::string_view settled_digest, + bool tipped) { + FC_ASSERT(is_hex_word(own_digest), "epochDeliveries did not return one 32-byte word: '{}'", own_digest); + FC_ASSERT(is_hex_word(settled_digest), "pendingEpochHash did not return one 32-byte word: '{}'", settled_digest); + if (is_zero_word(own_digest)) return delivery_settlement::never_delivered; + // Until the tip, `pendingEpochHash` is an earlier epoch's digest (or the + // zero word on a fresh outpost): nothing to compare this epoch's record to. + if (!tipped) return delivery_settlement::recorded; + return same_hex(own_digest, settled_digest) ? delivery_settlement::settled : delivery_settlement::divergent; +} + +bool majority_tip_reachable(const pending_consensus& consensus, uint64_t now_sec) { + if (consensus.group_size == 0) return false; + const uint64_t boundary = consensus.epoch_started_at + consensus.epoch_duration_sec; + // STRICT majority, as the contract computes it (`groupSize / 2 + 1`). + const uint32_t majority = consensus.group_size / 2 + 1; + return now_sec >= boundary && consensus.agreeing >= majority; +} + delivery_action decide_delivery(uint32_t next_epoch_index, uint32_t epoch_index, const dispatch_spill& spill, - bool own_delivery_settled) { + delivery_settlement settlement, + bool majority_reachable) { // The outpost is past this epoch, or has closed it: every transaction // would be a paid late no-op. `finalized` is checked as well as the epoch // cursor because the two are written in one call and reads that race a @@ -242,28 +397,115 @@ delivery_action decide_delivery(uint32_t next_epoch_index, if (next_epoch_index > epoch_index || spill.finalized) { return delivery_action::already_finalized; } + // The outpost has not reached this epoch: `epochIn` refuses it as + // non-sequential until the earlier one finalizes. + if (next_epoch_index < epoch_index) { + return delivery_action::outpost_behind; + } // Tipped but unfinished. The contract admits a continuation only from a // relay whose recorded delivery IS the settled digest, so a relay that has // not delivered, or delivered a minority envelope, has nothing to send. if (spill.tipped) { - return own_delivery_settled ? delivery_action::continue_dispatch - : delivery_action::wait_for_deliverer; + return settlement == delivery_settlement::settled ? delivery_action::continue_dispatch + : delivery_action::wait_for_deliverer; } - return delivery_action::deliver; + if (settlement == delivery_settlement::never_delivered) return delivery_action::deliver; + // Recorded and untipped: the rest of the group has yet to deliver. A + // re-delivery re-runs the tip, which only settles anything once the + // contract's path-2 predicate holds. + return majority_reachable ? delivery_action::retry_consensus : delivery_action::await_peers; } -std::optional classify_realize_yield_revert(std::string_view revert_data) { - constexpr size_t selector_chars = EVM_SELECTOR_BYTES * HEX_CHARS_PER_BYTE; - constexpr size_t word_chars = EVM_ABI_WORD_BYTES * HEX_CHARS_PER_BYTE; +bool advanced(const delivery_progress& before, const delivery_progress& after) { + if (after.next_epoch_index > before.next_epoch_index) return true; + if (after.spill.finalized && !before.spill.finalized) return true; + if (after.spill.complete && !before.spill.complete) return true; + if (after.spill.tipped && !before.spill.tipped) return true; + if (after.spill.dispatched > before.spill.dispatched) return true; + return is_zero_word(before.own_digest) && !is_zero_word(after.own_digest); +} +std::optional classify_epoch_in_revert(int rpc_code, std::string_view revert_data) { + if (rpc_code != ethereum_execution_reverted_code) return std::nullopt; const auto data = strip_hex_prefix(revert_data); - if (data.size() < selector_chars) return std::nullopt; - const auto selector = data.substr(0, selector_chars); - const auto argument_chars = data.size() - selector_chars; - // Exact shape, as for `OPP_ChunkBufferMissing`: an error with other arguments hashes + if (data.size() < EVM_SELECTOR_HEX_CHARS) return std::nullopt; + const auto selector = data.substr(0, EVM_SELECTOR_HEX_CHARS); + const auto argument_chars = data.size() - EVM_SELECTOR_HEX_CHARS; + // Exact shape: an error with other arguments hashes differently, and a + // payload that does not fit the error is not that error. + const auto is = [&](const char* signature, size_t words) { + return argument_chars == words * EVM_ABI_WORD_HEX_CHARS && same_hex(selector, selector_hex(signature)); + }; + if (is(dispatch_underfunded_signature, 2)) return epoch_in_revert::dispatch_underfunded; + if (is(handler_gas_exhausted_signature, 3)) return epoch_in_revert::handler_gas_exhausted; + if (is(non_sequential_epoch_signature, 2)) return epoch_in_revert::non_sequential_epoch; + if (is(operator_already_delivered_signature, 2)) return epoch_in_revert::operator_already_delivered; + if (is(not_active_operator_signature, 1)) return epoch_in_revert::not_active_operator; + if (is(digest_mismatch_signature, 2)) return epoch_in_revert::digest_mismatch; + return std::nullopt; +} + +delivery_receipt_summary summarize_delivery_receipt(const fc::variants& logs, std::string_view inbound_address, + uint32_t epoch_index, std::string_view operator_address) { + const auto delivery_topic = signature_hash_hex(epoch_delivery_event_signature); + const auto consensus_topic = signature_hash_hex(epoch_consensus_event_signature); + const auto progressed_topic = signature_hash_hex(epoch_dispatch_progressed_event_signature); + const auto complete_topic = signature_hash_hex(epoch_complete_event_signature); + + delivery_receipt_summary summary; + for (const auto& entry : logs) { + if (!entry.is_object()) continue; + const auto& log = entry.get_object(); + if (!log.contains(receipt_log_field::address) || !log[receipt_log_field::address].is_string()) continue; + if (!same_evm_address(log[receipt_log_field::address].as_string(), inbound_address)) continue; + if (!log.contains(receipt_log_field::topics) || !log[receipt_log_field::topics].is_array()) continue; + const auto& topics = log[receipt_log_field::topics].get_array(); + if (topics.empty() || !topics.front().is_string()) continue; + const auto topic = normalized_word(topics.front()); + const auto data = log.contains(receipt_log_field::data) ? normalized_word(log[receipt_log_field::data]) + : std::string{}; + // The epoch of an event that indexes it; `EpochComplete` carries its + // epoch in the data instead. + const auto indexed_epoch = [&]() -> std::optional { + if (topics.size() <= FIRST_INDEXED_TOPIC) return std::nullopt; + return word_to_uint(normalized_word(topics[FIRST_INDEXED_TOPIC])); + }; + + if (same_hex(topic, delivery_topic)) { + if (topics.size() <= SECOND_INDEXED_TOPIC) continue; + const auto operator_word = address_from_word(normalized_word(topics[SECOND_INDEXED_TOPIC])); + if (indexed_epoch() == epoch_index && operator_word && same_evm_address(*operator_word, operator_address)) { + summary.recorded = true; + } + } else if (same_hex(topic, consensus_topic)) { + if (indexed_epoch() == epoch_index) summary.tipped = true; + } else if (same_hex(topic, progressed_topic)) { + const auto dispatched = data_word(data, 0).and_then(word_to_uint); + if (indexed_epoch() == epoch_index && dispatched && *dispatched <= std::numeric_limits::max()) { + summary.dispatched = static_cast(*dispatched); + } + } else if (same_hex(topic, complete_topic)) { + if (data_word(data, 0).and_then(word_to_uint) == epoch_index) summary.finalized = true; + } + } + return summary; +} + +std::string envelope_digest_word(const std::vector& envelope_bytes) { + const std::span bytes{reinterpret_cast(envelope_bytes.data()), + envelope_bytes.size()}; + return normalized_hex(fc::crypto::keccak256::hash(bytes).str()); +} + +std::optional classify_realize_yield_revert(std::string_view revert_data) { + const auto data = strip_hex_prefix(revert_data); + if (data.size() < EVM_SELECTOR_HEX_CHARS) return std::nullopt; + const auto selector = data.substr(0, EVM_SELECTOR_HEX_CHARS); + const auto argument_chars = data.size() - EVM_SELECTOR_HEX_CHARS; + // Exact shape, as for the `epochIn` refusals: an error with other arguments hashes // differently, and a payload that does not fit the error is not that error. const auto is = [&](const char* signature, size_t words) { - return argument_chars == words * word_chars && same_hex(selector, selector_hex(signature)); + return argument_chars == words * EVM_ABI_WORD_HEX_CHARS && same_hex(selector, selector_hex(signature)); }; if (is(no_yield_signature, 0)) return realize_yield_refusal::no_yield; if (is(yield_below_deadband_signature, 2)) return realize_yield_refusal::below_deadband; @@ -284,15 +526,13 @@ std::optional count_envelope_attestations(const std::vector& env } std::optional address_from_word(std::string_view raw_hex) { - constexpr size_t word_chars = EVM_ABI_WORD_BYTES * HEX_CHARS_PER_BYTE; constexpr size_t address_chars = EVM_ADDRESS_BYTES * HEX_CHARS_PER_BYTE; const auto word = strip_hex_prefix(raw_hex); - if (word.size() != word_chars) return std::nullopt; - if (!std::ranges::all_of(word, [](unsigned char c) { return std::isxdigit(c) != 0; })) return std::nullopt; + if (!is_hex_word(word)) return std::nullopt; // An address word is left-padded with zeros; anything else in the pad is not an address. - if (word.find_first_not_of('0') < word_chars - address_chars) return std::nullopt; - return "0x" + std::string(word.substr(word_chars - address_chars)); + if (word.find_first_not_of('0') < EVM_ABI_WORD_HEX_CHARS - address_chars) return std::nullopt; + return "0x" + std::string(word.substr(EVM_ABI_WORD_HEX_CHARS - address_chars)); } bool is_routable_handler(std::string_view handler_address) { @@ -332,6 +572,32 @@ outpost_ethereum_client::outpost_ethereum_client( if (!_opp_inbound_addr.empty()) { _opp_inbound_client = _entry->client->get_contract(_opp_inbound_addr, _abis); + + // A delivery is funded to what it has to carry, up to the policy's + // ceiling. A ceiling under what the largest envelope the platform + // allows needs is a configuration that cannot deliver, and it is + // refused here rather than discovered one stuck epoch at a time. The + // total-cost term is a warning, not a refusal: it binds at the fee a + // call is sent at, and only the policy's own maximum is known here; an + // unbounded total cost (the maximum policy) has nothing to warn about. + const auto& policy = _entry->client->transaction_policy(); + const uint64_t ceiling = delivery_gas_ceiling(policy); + SYS_ASSERT(ceiling >= DELIVERY_MINIMUM_GAS_CEILING, chain::plugin_config_exception, + "outpost_ethereum_client[{}]: transaction policy max_gas_limit {} funds at most {} gas per " + "epochIn, below the {} a full-cap envelope needs to deliver, dispatch one attestation and " + "emit; raise max_gas_limit for client {}", + to_string(), policy.max_gas_limit.str(), ceiling, DELIVERY_MINIMUM_GAS_CEILING, + policy.client_id); + const uint64_t affordable_at_max_fee = delivery_gas_ceiling(policy, policy.max_fee_per_gas); + const bool total_cost_bounded = + policy.max_total_native_cost != eth::maximum_ethereum_transaction_policy_value(); + if (total_cost_bounded && affordable_at_max_fee < DELIVERY_MINIMUM_GAS_CEILING) { + wlog("outpost_ethereum_client[{}]: transaction policy max_total_native_cost {} wei funds only {} gas " + "per epochIn at max_fee_per_gas {} wei, below the {} a full-cap envelope needs; deliveries at " + "fees near the policy maximum will spill into more continuations than the budget sizes for", + to_string(), policy.max_total_native_cost.str(), affordable_at_max_fee, policy.max_fee_per_gas.str(), + DELIVERY_MINIMUM_GAS_CEILING); + } } // OPPInbound records every delivery under the delivering signer, and a @@ -351,88 +617,59 @@ outpost_ethereum_client::authenticated_caller_address() const { return {address.begin(), address.end()}; } -uint32_t outpost_ethereum_client::read_next_epoch_index() { - const auto raw = _opp_inbound_client->next_epoch_index(PROGRESS_READ_TAG); - FC_ASSERT(raw.is_string(), - "outpost_ethereum_client[{}]: nextEpochIndex returned a non-string variant", - to_string()); - const auto decoded = decoded_scalar(eth::contract_decode_data( - _opp_inbound_client->get_abi(opp_inbound_abi::view_next_epoch_index), raw.as_string())); - const auto value = abi_uint_output(decoded); - FC_ASSERT(value.has_value(), - "outpost_ethereum_client[{}]: nextEpochIndex was not a parsable numeric output", - to_string()); - return static_cast(*value); +detail::delivery_progress outpost_ethereum_client::read_progress(uint32_t epoch_index, const fc::uint256& block) { + const auto at = pinned(block); + // A backend that has not seen `block` yet answers with a JSON-RPC error + // rather than stale state; that is the one failure worth waiting out. + // Everything else (a malformed response, the deadline) propagates. + return fc::task::retry_until( + OP_READ_PROGRESS, PINNED_READ_RETRY, [&]() -> std::optional { + try { + detail::delivery_progress progress; + progress.next_epoch_index = read_next_epoch_index(*_opp_inbound_client, at, to_string()); + progress.spill = read_dispatch_spill(*_opp_inbound_client, epoch_index, at, to_string()); + progress.own_digest = read_own_digest(*_opp_inbound_client, epoch_index, _signer_address_hex, at, to_string()); + progress.settled_digest = read_settled_digest(*_opp_inbound_client, at, to_string()); + dlog("outpost_ethereum_client[{}]: epoch={} at block {}: next_epoch={} spill(tipped={} dispatched={} " + "complete={} finalized={}) own={} settled={}", + to_string(), epoch_index, block.str(), progress.next_epoch_index, progress.spill.tipped, + progress.spill.dispatched, progress.spill.complete, progress.spill.finalized, progress.own_digest, + progress.settled_digest); + return progress; + } catch (const fc::network::json_rpc::json_rpc_error& refusal) { + dlog("outpost_ethereum_client[{}]: progress read at block {} refused ({}); retrying", to_string(), + block.str(), refusal.to_string()); + return std::nullopt; + } + }); } -detail::dispatch_spill outpost_ethereum_client::read_dispatch_spill(uint32_t epoch_index) { - // `ethereum_contract_call_fn` binds its arguments as non-const lvalue - // references, so the epoch needs a named local. - uint32_t epoch_arg = epoch_index; - const auto raw = _opp_inbound_client->dispatch_spill(PROGRESS_READ_TAG, epoch_arg); - FC_ASSERT(raw.is_string(), - "outpost_ethereum_client[{}]: dispatchSpill returned a non-string variant", - to_string()); +detail::pending_consensus outpost_ethereum_client::read_pending_consensus(const std::string& digest_word, + const fc::uint256& block) { + std::string digest_arg = "0x" + digest_word; + const auto raw = view_hex(_opp_inbound_client->pending_consensus_for_digest(pinned(block), digest_arg), to_string(), + opp_inbound_abi_name::pending_consensus_for_digest); const auto decoded = eth::contract_decode_data( - _opp_inbound_client->get_abi(opp_inbound_abi::view_dispatch_spill), raw.as_string()); - dlog("outpost_ethereum_client[{}]: dispatchSpill({}) decoded={}", - to_string(), epoch_index, fc::json::to_string(decoded, fc::json::yield_function_t{})); + _opp_inbound_client->get_abi(opp_inbound_abi_name::pending_consensus_for_digest), raw); FC_ASSERT(decoded.is_object(), - "outpost_ethereum_client[{}]: dispatchSpill result was not a variant object", - to_string()); - - // Fail closed on every field: an unparsable one must not be silently read - // as "not tipped", which would send a fresh delivery into an epoch that - // has already settled and be refused as a non-deliverer. - const auto& object = decoded.get_object(); - const auto read_bool = [&](const char* key) { - FC_ASSERT(object.contains(key), - "outpost_ethereum_client[{}]: dispatchSpill result missing '{}'", to_string(), key); - const auto value = abi_bool_output(object[key]); - FC_ASSERT(value.has_value(), - "outpost_ethereum_client[{}]: dispatchSpill '{}' was not a boolean output", + "outpost_ethereum_client[{}]: pendingConsensusForDigest result was not a variant object", to_string()); + const auto& object = decoded.get_object(); + const auto read = [&](const char* key) -> uint64_t { + FC_ASSERT(object.contains(key), "outpost_ethereum_client[{}]: pendingConsensusForDigest result missing '{}'", to_string(), key); + const auto value = abi_uint_output(object[key]); + FC_ASSERT(value.has_value(), + "outpost_ethereum_client[{}]: pendingConsensusForDigest '{}' was not a numeric output", to_string(), + key); return *value; }; - FC_ASSERT(object.contains(opp_inbound_abi::field::dispatched), - "outpost_ethereum_client[{}]: dispatchSpill result missing '{}'", - to_string(), opp_inbound_abi::field::dispatched); - const auto dispatched = abi_uint_output(object[opp_inbound_abi::field::dispatched]); - FC_ASSERT(dispatched.has_value(), - "outpost_ethereum_client[{}]: dispatchSpill '{}' was not a numeric output", - to_string(), opp_inbound_abi::field::dispatched); - - detail::dispatch_spill spill; - spill.tipped = read_bool(opp_inbound_abi::field::tipped); - spill.dispatched = static_cast(*dispatched); - spill.complete = read_bool(opp_inbound_abi::field::complete); - spill.finalized = read_bool(opp_inbound_abi::field::finalized); - return spill; -} - -bool outpost_ethereum_client::own_delivery_settled(uint32_t epoch_index) { - uint32_t epoch_arg = epoch_index; - std::string operator_arg = _signer_address_hex; - const auto delivered_raw = - _opp_inbound_client->epoch_deliveries(PROGRESS_READ_TAG, epoch_arg, operator_arg); - const auto settled_raw = _opp_inbound_client->pending_epoch_hash(PROGRESS_READ_TAG); - FC_ASSERT(delivered_raw.is_string() && settled_raw.is_string(), - "outpost_ethereum_client[{}]: epochDeliveries / pendingEpochHash returned a " - "non-string variant", - to_string()); - - const auto delivered = normalized_word(decoded_scalar(eth::contract_decode_data( - _opp_inbound_client->get_abi(opp_inbound_abi::view_epoch_deliveries), delivered_raw.as_string()))); - const auto settled = normalized_word(decoded_scalar(eth::contract_decode_data( - _opp_inbound_client->get_abi(opp_inbound_abi::view_pending_epoch_hash), settled_raw.as_string()))); - dlog("outpost_ethereum_client[{}]: epoch={} own delivery={} settled={}", - to_string(), epoch_index, delivered, settled); - - // A zero digest is "nothing recorded" on either side and never a match: an - // epoch cannot settle on the zero digest, and a relay that has not - // delivered has no claim on the continuation. - if (is_zero_word(delivered) || is_zero_word(settled)) return false; - return delivered == settled; + detail::pending_consensus consensus; + consensus.next_epoch = static_cast(read(opp_inbound_abi::field::next_epoch)); + consensus.agreeing = static_cast(read(opp_inbound_abi::field::agreeing)); + consensus.group_size = static_cast(read(opp_inbound_abi::field::group_size)); + consensus.epoch_started_at = read(opp_inbound_abi::field::current_epoch_started_at); + consensus.epoch_duration_sec = static_cast(read(opp_inbound_abi::field::epoch_duration_sec)); + return consensus; } std::string outpost_ethereum_client::deliver_outbound_envelope( @@ -462,40 +699,89 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( // EIP-7825's 16.7 M cap; what may not fit is DISPATCH, which the contract // spills across continuations of this same call. The envelope is // re-supplied on every continuation — the outpost never stores its bytes. - const std::string envelope_hex = fc::to_hex(envelope_bytes.data(), static_cast(total)); + const std::string envelope_hex = fc::to_hex(envelope_bytes.data(), static_cast(total)); + // The digest the outpost records for this delivery and settles consensus + // on: a continuation is only valid with the bytes that hash to the settled + // digest, which the relay checks before paying to find out. + const std::string local_digest = detail::envelope_digest_word(envelope_bytes); // Each call is funded to what it has left to carry (`delivery_gas_budget`), // never to a fixed figure: the attestations the outpost's cursor says are - // still to dispatch, doubled for every spill already met this tick. An - // envelope the relay cannot read is funded to the ceiling. + // still to dispatch, doubled for every call this tick that fell short. An + // envelope the relay cannot read is funded to the ceiling. The ceiling is + // what the policy allows AT THE CURRENT FEE, so a budget it admits is a + // budget the policy's total-cost term will not refuse at signing. const auto attestation_count = detail::count_envelope_attestations(envelope_bytes); - const uint64_t gas_ceiling = delivery_gas_ceiling(_entry->client->transaction_policy()); + const auto gas_config = _entry->client->get_gas_config(); + const uint64_t gas_ceiling = + delivery_gas_ceiling(_entry->client->transaction_policy(), gas_config.max_fee_per_gas); if (!attestation_count) { wlog("outpost_ethereum_client[{}]: epoch={} envelope ({} bytes) does not decode as an OPP " "envelope; funding delivery to the ceiling ({} gas)", to_string(), epoch_index, total, gas_ceiling); } + // Every decision is taken from the outpost's state at ONE block: the head + // when the tick starts, then the block each confirmed call landed in. A + // read at `latest` could be served by a backend behind the block that just + // confirmed and report the state from before the call. + fc::uint256 pinned_block = _entry->client->get_block_number(); + + // A call that cannot be sent at its budget, or that was sent and moved + // nothing, is reported as a stalled epoch: once at error level for each + // place the cursor stalls, then at debug until the cursor moves. The tick + // ends in `outpost_delivery_incomplete_exception` so the job retries it. + const auto report_stall = [&](const detail::dispatch_spill& spill, const std::string& reason) { + const std::pair where{epoch_index, spill.dispatched}; + if (_reported_stall != where) { + _reported_stall = where; + elog("outpost_ethereum_client[{}]: epoch={} cannot advance past attestation {} at the ceiling ({} gas): " + "{}; the next tick retries, and the epoch stays open until the outpost accepts the call", + to_string(), epoch_index, spill.dispatched, gas_ceiling, reason); + } else { + dlog("outpost_ethereum_client[{}]: epoch={} still stalled at attestation {}: {}", to_string(), epoch_index, + spill.dispatched, reason); + } + SYS_THROW(chain::outpost_delivery_incomplete_exception, + "outpost_ethereum_client[{}]: epoch={} stalled at attestation {} at the ceiling ({} gas): {}", + to_string(), epoch_index, spill.dispatched, gas_ceiling, reason); + }; + // Sequential, receipt-confirmed submission — one transaction in flight at a // time, so the signer's nonce advances in lock-step and a mid-sequence // failure simply abandons the tick. The next cron tick re-reads the // outpost's cursor and picks up wherever the chain actually is. - std::string last_tx; - bool delivered_this_tick = false; - uint32_t spills_this_tick = 0; - for (uint32_t call = 0; call <= MAX_CONTINUATIONS_PER_TICK; ++call) { + std::string last_tx; + uint32_t sends = 0; + uint32_t escalations = 0; + uint32_t stale_refusals = 0; + uint64_t last_budget = 0; + std::optional before_last_send; + for (;;) { throw_if_past_deadline(deadline_abs, OP_DELIVER_OUTBOUND); - const auto next_epoch = read_next_epoch_index(); - const auto spill = read_dispatch_spill(epoch_index); - // The two settlement reads are only meaningful — and only paid for — - // when the epoch is mid-flight. - const bool settled = spill.tipped && !spill.finalized && own_delivery_settled(epoch_index); - const auto action = detail::decide_delivery(next_epoch, epoch_index, spill, settled); + const auto progress = read_progress(epoch_index, pinned_block); + const auto settlement = + detail::classify_settlement(progress.own_digest, progress.settled_digest, progress.spill.tipped); + + // A recorded, untipped delivery is re-sent only when the outpost's own + // numbers say the re-send would tip it — and only when this tick has + // not just sent one, whose tip check ran in that very transaction. + bool majority_reachable = false; + std::optional consensus; + if (sends == 0 && settlement == detail::delivery_settlement::recorded && + progress.next_epoch_index == epoch_index) { + consensus = read_pending_consensus(progress.own_digest, pinned_block); + majority_reachable = + detail::majority_tip_reachable(*consensus, uint64_t{fc::time_point::now().sec_since_epoch()}); + } + const auto action = detail::decide_delivery(progress.next_epoch_index, epoch_index, progress.spill, + settlement, majority_reachable); dlog("outpost_ethereum_client[{}]: epoch={} action={} next_epoch={} " - "spill(tipped={} dispatched={} complete={} finalized={}) settled={}", - to_string(), epoch_index, magic_enum::enum_name(action), next_epoch, - spill.tipped, spill.dispatched, spill.complete, spill.finalized, settled); + "spill(tipped={} dispatched={} complete={} finalized={}) settlement={} sends={}", + to_string(), epoch_index, magic_enum::enum_name(action), progress.next_epoch_index, + progress.spill.tipped, progress.spill.dispatched, progress.spill.complete, progress.spill.finalized, + magic_enum::enum_name(settlement), sends); switch (action) { case detail::delivery_action::already_finalized: @@ -504,93 +790,187 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( // tick began. An EMPTY tx id tells the job the epoch is handled. ilog("outpost_ethereum_client[{}]: skipping epoch={} delivery — the outpost has " "already finalized it (nextEpochIndex={})", - to_string(), epoch_index, next_epoch); + to_string(), epoch_index, progress.next_epoch_index); } return last_tx; + case detail::delivery_action::outpost_behind: + // Not a handled epoch: the outpost will accept this delivery once it + // finalizes the earlier one, and the next tick must try again. + ilog("outpost_ethereum_client[{}]: epoch={} not delivered — the outpost is still on epoch {}; " + "retrying next tick", + to_string(), epoch_index, progress.next_epoch_index); + SYS_THROW(chain::outpost_delivery_incomplete_exception, + "outpost_ethereum_client[{}]: epoch={} not delivered — the outpost is still on epoch {}", + to_string(), epoch_index, progress.next_epoch_index); + case detail::delivery_action::wait_for_deliverer: - ilog("outpost_ethereum_client[{}]: epoch={} tipped on a digest this relay did not " - "deliver (dispatched={} complete={}); its deliverers carry the continuation", - to_string(), epoch_index, spill.dispatched, spill.complete); + if (settlement == detail::delivery_settlement::divergent) { + // This relay's envelope disagreed with the majority's: worth a + // warning with both digests, since it means the depot handed this + // relay different bytes than its peers. + wlog("outpost_ethereum_client[{}]: epoch={} tipped on digest {} but this relay delivered {}; its " + "deliverers carry the continuation (dispatched={} complete={})", + to_string(), epoch_index, progress.settled_digest, progress.own_digest, progress.spill.dispatched, + progress.spill.complete); + } else { + ilog("outpost_ethereum_client[{}]: epoch={} tipped on a digest this relay did not deliver " + "(dispatched={} complete={}); its deliverers carry the continuation", + to_string(), epoch_index, progress.spill.dispatched, progress.spill.complete); + } return last_tx; - case detail::delivery_action::deliver: - if (delivered_this_tick) { - // Our delivery is recorded and consensus has not tipped: the - // remaining group members have yet to deliver. Re-sending would - // be a paid no-op; the job's boundary-gated retry covers path 2. - dlog("outpost_ethereum_client[{}]: epoch={} delivery recorded; consensus not yet tipped", - to_string(), epoch_index); - return last_tx; + case detail::delivery_action::await_peers: + if (sends == 0) { + // A consensus retry that arrives before the OUTPOST's boundary (the + // job gates it on the depot's, which leads the outpost's) is not a + // used-up retry: the epoch stays open so the job tries again next + // tick, until the boundary passes and the majority view decides. + const uint64_t boundary = consensus->epoch_started_at + consensus->epoch_duration_sec; + if (uint64_t{fc::time_point::now().sec_since_epoch()} < boundary) { + ilog("outpost_ethereum_client[{}]: epoch={} delivery recorded; the outpost's boundary ({}) has not " + "passed, so a re-delivery could not tip it yet; retrying next tick", + to_string(), epoch_index, boundary); + SYS_THROW(chain::outpost_delivery_incomplete_exception, + "outpost_ethereum_client[{}]: epoch={} recorded; outpost boundary {} not reached", + to_string(), epoch_index, boundary); + } + ilog("outpost_ethereum_client[{}]: epoch={} delivery already recorded; consensus not tipped " + "(agreeing={} group={} boundary={}); nothing to send until more of the group delivers", + to_string(), epoch_index, consensus->agreeing, consensus->group_size, boundary); + } else { + dlog("outpost_ethereum_client[{}]: epoch={} delivery recorded; consensus not yet tipped", to_string(), + epoch_index); } + return last_tx; + + case detail::delivery_action::retry_consensus: + ilog("outpost_ethereum_client[{}]: epoch={} re-delivering to run the boundary tip (agreeing={} " + "group={} boundary={})", + to_string(), epoch_index, consensus->agreeing, consensus->group_size, + consensus->epoch_started_at + consensus->epoch_duration_sec); break; case detail::delivery_action::continue_dispatch: - // A continuation behind a call this tick already sent means that - // call spilled: what it was funded with did not carry its work, so - // the next one is funded to more. A continuation that OPENS the tick - // resumes a spill from an earlier tick at the sized budget. - if (!last_tx.empty()) ++spills_this_tick; + // The contract resumes only from the bytes that hash to the settled + // digest. An envelope that does not is not this epoch's — nothing + // to continue with, and no transaction to pay for finding that out. + if (!same_hex(local_digest, progress.settled_digest)) { + wlog("outpost_ethereum_client[{}]: epoch={} is tipped on digest {} but the envelope held hashes to " + "{}; this relay cannot continue it", + to_string(), epoch_index, progress.settled_digest, local_digest); + return last_tx; + } + break; + + case detail::delivery_action::deliver: break; } + // The call this tick last paid for must have moved the outpost's cursor; + // one that did not was under-funded for the attestation at the cursor + // (the contract records a spill only when it dispatched at least one), + // and the next is funded higher. At the ceiling there is nothing higher. + if (before_last_send) { + if (!detail::advanced(*before_last_send, progress)) { + if (last_budget >= gas_ceiling) { + report_stall(progress.spill, "the last call advanced nothing"); + } + ++escalations; + } else if (action == detail::delivery_action::continue_dispatch) { + // A continuation behind a call this tick already sent means that + // call spilled: what it was funded with did not carry its work. + ++escalations; + } + } + if (sends >= MAX_CONTINUATIONS_PER_TICK) { + wlog("outpost_ethereum_client[{}]: epoch={} still open after {} calls in one tick (dispatched={} " + "complete={}); the next tick resumes from the outpost's cursor", + to_string(), epoch_index, sends, progress.spill.dispatched, progress.spill.complete); + SYS_THROW(chain::outpost_delivery_incomplete_exception, + "outpost_ethereum_client[{}]: epoch={} still open after {} calls in one tick", to_string(), + epoch_index, sends); + } + throw_if_past_deadline(deadline_abs, OP_DELIVER_OUTBOUND); // Before the tip nothing is dispatched, so every attestation is still // to carry; after it the outpost's cursor says how many remain, and a // complete dispatch leaves only the emit. std::optional remaining; if (attestation_count) { - const uint32_t done = spill.tipped ? spill.dispatched : 0; - remaining = spill.complete ? 0 : (*attestation_count > done ? *attestation_count - done : 0); + const uint32_t done = progress.spill.tipped ? progress.spill.dispatched : 0; + remaining = progress.spill.complete ? 0 : (*attestation_count > done ? *attestation_count - done : 0); } uint64_t gas_budget = delivery_gas_budget(delivery_gas_request{ .envelope_bytes = total, .remaining_attestations = remaining, - .escalations = spills_this_tick, + .escalations = escalations, .ceiling = gas_ceiling, }); dlog("outpost_ethereum_client[{}]: epoch={} funding epochIn with {} gas (remaining={} " "escalations={} ceiling={})", to_string(), epoch_index, gas_budget, - remaining ? std::to_string(*remaining) : "unknown", spills_this_tick, gas_ceiling); + remaining ? std::to_string(*remaining) : "unknown", escalations, gas_ceiling); // The ABI arguments are bound as non-const lvalue references, so each // one needs a named local. - uint32_t epoch_arg = epoch_index; - std::string payload_hex = envelope_hex; - fc::variant result; + uint32_t epoch_arg = epoch_index; + std::string payload_hex = envelope_hex; + epoch_in_receipt receipt; try { - result = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget); + receipt = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget); } catch (const fc::network::json_rpc::json_rpc_error& refusal) { - // The node refused the call at this budget before anything was - // signed — the pre-flight estimate runs under the budget. Below the - // ceiling that is the budget's fault: retry once with everything the - // policy allows. At the ceiling it is the envelope's, and the tick - // reports it rather than spending the ceiling on finding out again. - if (gas_budget >= gas_ceiling) throw; - wlog("outpost_ethereum_client[{}]: epoch={} refused at {} gas ({}); retrying at the " - "ceiling ({} gas)", - to_string(), epoch_index, gas_budget, refusal.to_string(), gas_ceiling); - gas_budget = gas_ceiling; - result = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget); + // The node refused the call before anything was signed: the + // pre-flight estimate runs under the budget. Which refusal decides + // what the relay does about it. + const auto revert = detail::classify_epoch_in_revert(refusal.code, revert_hex(refusal)); + if (!revert) throw; + switch (*revert) { + case detail::epoch_in_revert::dispatch_underfunded: + case detail::epoch_in_revert::handler_gas_exhausted: + // The budget's fault below the ceiling: retry once with everything + // the policy allows. At the ceiling it is the envelope's — an + // attestation no funding carries — and the tick reports it rather + // than spend the ceiling on finding out again. + if (gas_budget >= gas_ceiling) report_stall(progress.spill, refusal.to_string()); + wlog("outpost_ethereum_client[{}]: epoch={} refused at {} gas ({}); retrying at the " + "ceiling ({} gas)", + to_string(), epoch_index, gas_budget, magic_enum::enum_name(*revert), gas_ceiling); + gas_budget = gas_ceiling; + receipt = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget); + break; + + case detail::epoch_in_revert::non_sequential_epoch: + case detail::epoch_in_revert::operator_already_delivered: + case detail::epoch_in_revert::not_active_operator: + case detail::epoch_in_revert::digest_mismatch: + // The outpost's state is not what the pinned read said — another + // relay's call landed since — or this relay has no standing for + // the call. Read again at the head and decide from that rather + // than guess; a refusal that survives fresh reads is reported. + if (++stale_refusals > MAX_STALE_REFUSALS_PER_TICK) throw; + ilog("outpost_ethereum_client[{}]: epoch={} refused ({}); re-reading the outpost at the head", + to_string(), epoch_index, magic_enum::enum_name(*revert)); + pinned_block = _entry->client->get_block_number(); + before_last_send.reset(); + continue; + } } - last_tx = result.as_string(); - if (action == detail::delivery_action::deliver) { - delivered_this_tick = true; - ilog("outpost_ethereum_client[{}]: epochIn delivered epoch={} bytes={} gas={} tx={}", - to_string(), epoch_index, total, gas_budget, last_tx); - } else { - ilog("outpost_ethereum_client[{}]: epochIn continued epoch={} from attestation {} " - "(complete={}) gas={} tx={}", - to_string(), epoch_index, spill.dispatched, spill.complete, gas_budget, last_tx); - } + ++sends; + last_tx = receipt.tx_hash; + last_budget = gas_budget; + pinned_block = receipt.block_number; + before_last_send = progress; + + const auto summary = + detail::summarize_delivery_receipt(receipt.logs, _opp_inbound_addr, epoch_index, _signer_address_hex); + ilog("outpost_ethereum_client[{}]: epochIn {} epoch={} bytes={} gas={} block={} tx={} — recorded={} " + "tipped={} dispatched={} finalized={}", + to_string(), action == detail::delivery_action::continue_dispatch ? "continued" : "delivered", + epoch_index, total, gas_budget, receipt.block_number.str(), receipt.tx_hash, summary.recorded, + summary.tipped, summary.dispatched ? std::to_string(*summary.dispatched) : "-", summary.finalized); } - - wlog("outpost_ethereum_client[{}]: epoch={} still not finalized after {} continuations in " - "one tick; the next tick resumes from the outpost's cursor", - to_string(), epoch_index, MAX_CONTINUATIONS_PER_TICK); - return last_tx; } std::vector outpost_ethereum_client::read_inbound_envelope( @@ -770,8 +1150,8 @@ void outpost_ethereum_client::bind_syndication_pool( } std::optional outpost_ethereum_client::discover_syndication_pool() { - // The routing table is configuration, not delivered content, so `latest` is right for the - // same reason it is for the staging-header read. + // The routing table is configuration, not delivered content, so `latest` is right: it is + // not state this relay is mid-way through changing, and a stale read costs one idle crank. uint16_t attestation_type = static_cast( magic_enum::enum_integer(sysio::opp::types::ATTESTATION_TYPE_DESYNDICATE_LIQ)); const auto raw = _opp_inbound_client->attestation_handlers(eth::block_tag_t::latest, attestation_type); @@ -829,7 +1209,7 @@ void outpost_ethereum_client::crank_outpost(uint32_t epoch_index, fc::microsecon // is the job's to log as a failed crank, exactly like a transport failure. const auto refusal = e.code == ethereum_execution_reverted_code - ? detail::classify_realize_yield_revert(e.data.is_string() ? e.data.as_string() : std::string{}) + ? detail::classify_realize_yield_revert(revert_hex(e)) : std::nullopt; if (!refusal) throw; switch (*refusal) { diff --git a/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp b/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp index 8df97ecd62..e81a3556f6 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp @@ -126,16 +126,6 @@ ethabi::contract no_argument_function(std::string name) { }; } -/** Build a function ABI with one dynamic-bytes argument. */ -ethabi::contract bytes_argument_function(std::string name) { - return ethabi::contract{ - .name = std::move(name), - .type = ethabi::invoke_target_type::function, - .inputs = {ethabi::component_type{"data", ethabi::data_type::bytes}}, - .outputs = {}, - }; -} - /** Build the whole-envelope `epochIn(uint32,bytes)` function ABI. */ ethabi::contract whole_envelope_epoch_in_function(std::string name) { return ethabi::contract{ @@ -158,6 +148,16 @@ ethabi::contract epoch_deliveries_function(std::string name) { }; } +/** Build the `pendingConsensusForDigest(bytes32)` view ABI. */ +ethabi::contract bytes32_argument_function(std::string name) { + return ethabi::contract{ + .name = std::move(name), + .type = ethabi::invoke_target_type::function, + .inputs = {ethabi::component_type{"digest", ethabi::data_type::bytes32}}, + .outputs = {}, + }; +} + /** Build a function ABI with one uint16 argument. */ ethabi::contract uint16_argument_function(std::string name) { return ethabi::contract{ @@ -213,13 +213,18 @@ class recording_ethereum_client final : public ethereum_client { } if (method == "eth_estimateGas") { estimate_gas_params = params; - return fc::variant("0x342"); + return fc::variant(estimate_response); } if (method == "eth_getTransactionCount") return fc::variant("0x0"); if (method == "eth_sendRawTransaction") { ++broadcast_count; return fc::variant(std::string(transaction_hash)); } + if (method == "eth_getTransactionReceipt") { + ++receipt_reads; + return receipt_response; + } + if (method == "eth_blockNumber") return fc::variant(block_number_response); FC_THROW_EXCEPTION(fc::invalid_arg_exception, "unexpected fake RPC method {}", method); } @@ -230,6 +235,13 @@ class recording_ethereum_client final : public ethereum_client { std::vector methods; fc::variant estimate_gas_params; + /** What `eth_estimateGas` answers; 834 by default, which buffers to 1000. */ + std::string estimate_response = "0x342"; + /** What `eth_getTransactionReceipt` answers; null (not yet included) by default. */ + fc::variant receipt_response; + /** What `eth_blockNumber` answers. */ + std::string block_number_response = "0x10"; + size_t receipt_reads = 0; size_t broadcast_count = 0; }; @@ -509,10 +521,11 @@ BOOST_AUTO_TEST_CASE(gas_limit_floor_raises_an_under_estimate_and_is_policy_boun BOOST_CHECK_EQUAL(client->broadcast_count, 0u); } -/// A cap bounds the limit from above, is handed to the estimate as the gas -/// the call must fit in, cannot sit below the floor, and is policy-bounded -/// exactly like the floor. -BOOST_AUTO_TEST_CASE(gas_limit_cap_bounds_the_limit_and_runs_the_estimate_under_it) { +/// A cap IS the limit: the transaction is sent with exactly it, the estimate +/// runs under it as a pre-flight only (its value neither raises nor lowers +/// the limit, and the buffered-estimate ceiling check does not apply), it +/// cannot sit below the floor, and it is policy-bounded exactly like the floor. +BOOST_AUTO_TEST_CASE(gas_limit_cap_is_the_limit_and_runs_the_estimate_under_it) { std::atomic sign_count = 0; const auto provider = make_recording_signer(sign_count); auto policy = bounded_policy(); @@ -523,16 +536,35 @@ BOOST_AUTO_TEST_CASE(gas_limit_cap_bounds_the_limit_and_runs_the_estimate_under_ client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 0, 0); BOOST_CHECK(!client->estimate_gas_params.get_array().front().get_object().contains("gas")); - // The fake estimate is 834 -> buffered 1000; a cap of 900 wins. + // The fake estimate is 834 -> buffered 1000; a cap of 900 is sent as 900 ... BOOST_CHECK_EQUAL( client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 0, 900).gas_limit, 900); BOOST_CHECK_EQUAL( client->estimate_gas_params.get_array().front().get_object()["gas"].as_string(), "0x384"); + // ... and a cap of 1200 as 1200: the estimate's value is not an input to the limit. + BOOST_CHECK_EQUAL( + client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 0, 1200).gas_limit, + 1200); + BOOST_CHECK_EQUAL( + client->estimate_gas_params.get_array().front().get_object()["gas"].as_string(), "0x4b0"); // Floor and cap equal: the transaction carries exactly that. BOOST_CHECK_EQUAL( client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 1500, 1500).gas_limit, 1500); + // An estimate whose buffer would breach the policy is a rejection without a + // cap, and irrelevant with one: 1000 buffers to 1200 > 1000, but a capped + // call that fits in 1000 is sent with 1000. + client->estimate_response = "0x3e8"; + policy.max_gas_limit = 1000; + auto tight_client = std::make_shared(provider, policy); + tight_client->estimate_response = "0x3e8"; + expect_policy_rejection([&] { + tight_client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 0, 0); + }); + BOOST_CHECK_EQUAL( + tight_client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 0, 1000).gas_limit, + 1000); // A cap above the policy ceiling is a rejection, not a clamp. expect_policy_rejection([&] { client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 0, 2001); @@ -543,6 +575,37 @@ BOOST_AUTO_TEST_CASE(gas_limit_cap_bounds_the_limit_and_runs_the_estimate_under_ fc::exception); BOOST_CHECK_EQUAL(sign_count.load(), 0u); BOOST_CHECK_EQUAL(client->broadcast_count, 0u); + BOOST_CHECK_EQUAL(tight_client->broadcast_count, 0u); +} + +/// The confirmation wait hands back the receipt itself — block number and +/// logs — so a caller can pin its next reads to the block that holds the +/// effect; a reverted receipt is an error, and the depth wait still applies. +BOOST_AUTO_TEST_CASE(wait_for_receipt_returns_the_confirmed_receipt) { + std::atomic sign_count = 0; + const auto provider = make_recording_signer(sign_count); + auto client = std::make_shared(provider, bounded_policy()); + const std::string hash{transaction_hash}; + const fc::variants logs{fc::mutable_variant_object("address", std::string(contract_address))}; + client->receipt_response = fc::mutable_variant_object("status", "0x1")("blockNumber", "0x10")("logs", logs); + + const auto receipt = client->wait_for_receipt(hash); + BOOST_REQUIRE(receipt.is_object()); + BOOST_CHECK_EQUAL(receipt.get_object()["blockNumber"].as_string(), "0x10"); + BOOST_CHECK_EQUAL(receipt.get_object()["logs"].get_array().size(), 1u); + BOOST_CHECK_EQUAL(client->receipt_reads, 1u); + BOOST_CHECK_EQUAL(client->wait_for_confirmation(hash), hash); + + // Two confirmations: the receipt's block plus one, which the fake chain has reached. + ethereum_confirm_options two_deep = ethereum_confirm_option_defaults; + two_deep.confirmations = 2; + client->block_number_response = "0x11"; + BOOST_CHECK(client->wait_for_receipt(hash, two_deep).is_object()); + BOOST_CHECK(std::ranges::count(client->methods, "eth_blockNumber") == 1); + + client->receipt_response = fc::mutable_variant_object("status", "0x0")("blockNumber", "0x10"); + BOOST_CHECK_THROW(client->wait_for_receipt(hash), fc::exception); + BOOST_CHECK_EQUAL(sign_count.load(), 0u); } BOOST_AUTO_TEST_CASE(all_typed_write_wrappers_share_the_policy_enforced_path) { @@ -557,17 +620,26 @@ BOOST_AUTO_TEST_CASE(all_typed_write_wrappers_share_the_policy_enforced_path) { std::string(contract_address), {whole_envelope_epoch_in_function("epochIn"), no_argument_function("nextEpochIndex"), uint32_argument_function("dispatchSpill"), epoch_deliveries_function("epochDeliveries"), - no_argument_function("pendingEpochHash"), + no_argument_function("pendingEpochHash"), bytes32_argument_function("pendingConsensusForDigest"), uint16_argument_function("attestationHandlers")}, }; // OPPInbound's one write wrapper — the whole-envelope delivery — must be // rejected by the policy before signing. A budget above the policy ceiling - // (999 here) is refused outright; one within it still loses to the - // ×1.2-buffered estimate of 834, which breaches the ceiling on its own. + // (999 here) is refused outright; one at the ceiling is sent with exactly + // it (the estimate of 834 is a pre-flight under the cap, not an input to + // the limit) and comes back as the confirmed receipt. uint32_t epoch_index = 1; std::string envelope = "01"; expect_policy_rejection([&] { inbound.epoch_in(epoch_index, envelope, 1000); }); - expect_policy_rejection([&] { inbound.epoch_in(epoch_index, envelope, 999); }); + client->receipt_response = fc::mutable_variant_object("status", "0x1")("blockNumber", "0x10")("logs", fc::variants{}); + const auto receipt = inbound.epoch_in(epoch_index, envelope, 999); + BOOST_CHECK_EQUAL(receipt.block_number, fc::uint256{16}); + BOOST_CHECK(receipt.logs.empty()); + // The budget reached the pre-flight as the gas the call must fit in — the + // wrapper hands `delivery_confirm_options(budget)` to `create_default_tx`. + BOOST_CHECK_EQUAL(client->estimate_gas_params.get_array().front().get_object()["gas"].as_string(), "0x3e7"); + BOOST_CHECK_EQUAL(sign_count.load(), 1u); + BOOST_CHECK_EQUAL(client->broadcast_count, 1u); sysio::opp_contract_client opp{ client, @@ -584,8 +656,9 @@ BOOST_AUTO_TEST_CASE(all_typed_write_wrappers_share_the_policy_enforced_path) { }; expect_policy_rejection([&] { pool.realize_yield(); }); - BOOST_CHECK_EQUAL(sign_count.load(), 0u); - BOOST_CHECK_EQUAL(client->broadcast_count, 0u); + // Only the in-policy delivery reached the signer and the wire. + BOOST_CHECK_EQUAL(sign_count.load(), 1u); + BOOST_CHECK_EQUAL(client->broadcast_count, 1u); } BOOST_AUTO_TEST_CASE(plugin_startup_attaches_unified_client_policies) { diff --git a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp index 888916b037..3d18d67731 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -397,6 +398,14 @@ std::string encode_next_epoch_index_result(uint32_t next_epoch_index) { return std::string(hex_prefix) + abi_word(next_epoch_index); } +/// ABI-encode one address as the single word an `address` getter returns. +std::string encode_address_word(std::string_view address_hex) { + std::string_view address = address_hex; + if (address.starts_with("0x") || address.starts_with("0X")) address.remove_prefix(2); + return std::string(hex_prefix) + std::string(evm_abi_word_hex_chars - address.size(), '0') + + std::string(address); +} + /// One `epochIn` invocation as the stubbed typed wrapper observed it. struct observed_delivery_call { uint32_t epoch_index; @@ -404,14 +413,142 @@ struct observed_delivery_call { uint64_t gas_budget; }; +/// The head block the scripted chain reports when a tick starts; each +/// confirmed call lands one block later. +constexpr uint64_t test_head_block = 100; +/// Fee RPC answers for the scripted chain: a 10 wei tip over a 45 wei base +/// fee, so the derived `max_fee_per_gas` is 100 wei and the policy's +/// total-cost term never binds under the maximum policy. +constexpr std::string_view test_priority_fee_hex = "0xa"; +constexpr std::string_view test_base_fee_hex = "0x2d"; +/// Event topics of the `OPPInbound` events a delivery receipt carries — +/// `keccak256` of each signature, written out so the tests pin the client's own +/// hashing rather than restate it. +constexpr std::string_view epoch_delivery_topic = + "cafc00e7c35f462868d78e8228850e885bd0870978967c446c6bb67122a3fb28"; +constexpr std::string_view epoch_consensus_topic = + "a77ed82a4a8c2f7b412630e92b1c8f59e6645b05820a8b0ed43ae5fac990491c"; +constexpr std::string_view epoch_dispatch_progressed_topic = + "1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc9500"; +constexpr std::string_view epoch_complete_topic = + "e784b22f2061de501b77364cab02ee109528afe4ddc8d423d076f6b49021b58e"; +/// Selectors of the `epochIn` refusals the relay classifies, written out the +/// same way (`keccak256(signature)[0..4]`). +constexpr std::string_view dispatch_underfunded_selector = "e76ff350"; +constexpr std::string_view handler_gas_exhausted_selector = "a117d554"; +constexpr std::string_view non_sequential_epoch_selector = "fa7d07a0"; +constexpr std::string_view operator_already_delivered_selector = "208216ac"; +constexpr std::string_view not_active_operator_selector = "abc01454"; +constexpr std::string_view digest_mismatch_selector = "40d6b4cd"; +/// `keccak256` of the empty byte string, pinning `envelope_digest_word`. +constexpr std::string_view keccak256_of_empty = + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; +/// A group of three: a strict majority is two. +constexpr uint32_t test_group_size = 3; +constexpr uint32_t test_majority = test_group_size / 2 + 1; +/// A policy ceiling under what a full-cap envelope needs, for the refusal to build on it. +constexpr uint64_t undeliverable_policy_gas_limit = 2'000'000; +/// One ether, for the total-cost ceiling. +constexpr uint64_t one_ether_wei = 1'000'000'000'000'000'000ULL; +constexpr uint64_t hundred_gwei = 100'000'000'000ULL; + +/// The chain-level RPCs the relay issues outside the typed wrappers +/// (`eth_blockNumber`, the fee pair), answered from a script so the real +/// client never dials its dead endpoint for them. Anything else still does, +/// and fails loudly. +struct scripted_rpc { + fc::uint256 head_block{test_head_block}; + size_t block_number_reads = 0; +}; + +class scripted_ethereum_client final : public ethereum_client { +public: + scripted_ethereum_client(const fc::crypto::signature_provider_ptr& provider, ethereum_transaction_policy policy, + std::shared_ptr script) + : ethereum_client(provider, std::variant{std::string(latest_slot_test_rpc_url)}, + std::move(policy)) + , _script(std::move(script)) {} + + fc::variant execute(const std::string& method, const fc::variant& params) override { + if (const auto scripted = answer(method)) return *scripted; + return ethereum_client::execute(method, params); + } + fc::variant execute_idempotent(const std::string& method, const fc::variant& params) override { + if (const auto scripted = answer(method)) return *scripted; + return ethereum_client::execute_idempotent(method, params); + } + +private: + std::optional answer(const std::string& method) { + if (method == "eth_blockNumber") { + ++_script->block_number_reads; + return fc::variant(format_rpc_quantity(_script->head_block)); + } + if (method == "eth_maxPriorityFeePerGas") return fc::variant(std::string(test_priority_fee_hex)); + if (method == "eth_getBlockByNumber") { + return fc::variant(fc::mutable_variant_object("baseFeePerGas", std::string(test_base_fee_hex))); + } + return std::nullopt; + } + + std::shared_ptr _script; +}; + +/// Encode the raw return bytes for the `pendingConsensusForDigest(bytes32)` +/// view — five static outputs, concatenated. +std::string encode_pending_consensus_result(uint32_t next_epoch, uint32_t agreeing, uint32_t group_size, + uint64_t epoch_started_at, uint32_t epoch_duration_sec) { + return std::string(hex_prefix) + abi_word(next_epoch) + abi_word(agreeing) + abi_word(group_size) + + abi_word(epoch_started_at) + abi_word(epoch_duration_sec); +} + +/// One receipt log entry as a node returns it. +fc::variant receipt_log(std::string_view address, const std::vector& topics, std::string data_hex) { + fc::variants topic_variants; + for (const auto& topic : topics) topic_variants.emplace_back(topic); + return fc::mutable_variant_object("address", std::string(address))("topics", std::move(topic_variants))( + "data", std::string(hex_prefix) + std::move(data_hex)); +} + +std::string prefixed(std::string_view word) { return std::string(hex_prefix) + std::string(word); } + +fc::variant epoch_delivery_log(std::string_view address, uint32_t epoch, std::string_view operator_hex, + std::string_view digest_word) { + return receipt_log(address, {prefixed(epoch_delivery_topic), prefixed(abi_word(epoch)), + encode_address_word(operator_hex)}, + std::string(digest_word)); +} +fc::variant epoch_consensus_log(std::string_view address, uint32_t epoch, std::string_view digest_word, + uint32_t count) { + return receipt_log(address, {prefixed(epoch_consensus_topic), prefixed(abi_word(epoch))}, + std::string(digest_word) + abi_word(count)); +} +fc::variant epoch_dispatch_progressed_log(std::string_view address, uint32_t epoch, uint16_t dispatched) { + return receipt_log(address, {prefixed(epoch_dispatch_progressed_topic), prefixed(abi_word(epoch))}, + abi_word(dispatched)); +} +fc::variant epoch_complete_log(std::string_view address, uint32_t epoch) { + return receipt_log(address, {prefixed(epoch_complete_topic)}, abi_word(epoch)); +} + +/// A node's refusal of `epochIn` with one of the contract's custom errors: the +/// execution-reverted code and the selector followed by `words` zero words. +fc::network::json_rpc::json_rpc_error epoch_in_refusal(std::string_view selector, size_t words) { + std::string data = prefixed(selector); + for (size_t i = 0; i < words; ++i) data += abi_word(0); + return fc::network::json_rpc::json_rpc_error(contract_revert_rpc_code, "execution reverted", fc::variant(data)); +} + /// Harness binding a real `outpost_ethereum_client` to a stubbed OPPInbound -/// wrapper. +/// wrapper over a scripted chain. /// /// `ethereum_client::get_contract` caches one typed wrapper per address, so a /// wrapper materialized here is the SAME object the client resolves in its /// constructor — replacing its `std::function` members intercepts every RPC at /// the typed callable boundary, exactly as the `getLatestOutboundEnvelope` -/// coverage above does. +/// coverage above does. The stubbed `epochIn` confirms in a fresh block each +/// time and answers with a receipt the case scripts; every view records the +/// block it was pinned to. struct whole_envelope_delivery_fixture { ~whole_envelope_delivery_fixture() { outpost.reset(); @@ -421,31 +558,73 @@ struct whole_envelope_delivery_fixture { } std::unique_ptr tester; + std::shared_ptr rpc; std::shared_ptr inbound; std::unique_ptr outpost; + /// Every `epochIn` that confirmed. std::vector delivery_calls; + /// The budget of every `epochIn` attempt, refused ones included. + std::vector attempted_budgets; + /// Per attempt, in order, a refusal the stub throws instead of confirming; + /// attempts past the end confirm. + std::vector> refusals; size_t next_epoch_reads = 0; size_t spill_reads = 0; size_t settlement_reads = 0; - - /// Response the stubbed `nextEpochIndex` view returns. - std::string next_epoch_index_response = encode_next_epoch_index_result(0); + size_t consensus_reads = 0; + /// The block parameter of every stubbed view read, in order. + std::vector read_blocks; + /// The digest every `pendingConsensusForDigest` read asked about. + std::vector consensus_digests; + + /// Responses the stubbed `nextEpochIndex` view returns, one per read; the + /// last one repeats. The default is an outpost on the delivered epoch. + std::vector next_epoch_index_responses{encode_next_epoch_index_result(test_wire_epoch)}; /// Responses the stubbed `dispatchSpill` view returns, one per read in /// order; the last one repeats. The default is the never-tipped cursor. std::vector spill_responses{encode_dispatch_spill_result(false, 0, false, false)}; - /// Response the stubbed `epochDeliveries(epoch, self)` view returns. - std::string own_delivery_response = encode_word_result(zero_digest_word); + /// Responses the stubbed `epochDeliveries(epoch, self)` view returns, one + /// per read; the last repeats. The default: nothing recorded on the first + /// read, a record on every read after it (the delivery landed). + std::vector own_delivery_responses{encode_word_result(zero_digest_word), + encode_word_result(settled_digest_word)}; /// Response the stubbed `pendingEpochHash` view returns. std::string pending_hash_response = encode_word_result(settled_digest_word); + /// Response the stubbed `pendingConsensusForDigest` view returns. The + /// default: this relay alone has delivered, the boundary long past. + std::string consensus_response = encode_pending_consensus_result(test_wire_epoch, 1, test_group_size, 0, 0); + /// Receipt logs per confirmed call, in order; the last repeats. The default + /// is the delivery record alone. `std::nullopt` entries use the default. + std::vector> receipt_logs; /// Wall-clock the FIRST stubbed `epochIn` burns before returning, standing in /// for a slow chain. Applied only to the first call so a deadline set below /// it expires deterministically at the next pre-flight check. std::chrono::milliseconds first_call_delay{0}; - /// The spill response for the `n`-th read. - const std::string& spill_response_at(size_t n) const { - return spill_responses[std::min(n, spill_responses.size() - 1)]; + template + static const T& response_at(const std::vector& responses, size_t n) { + return responses[std::min(n, responses.size() - 1)]; + } + + /// Script the outpost as settled on `envelope`'s own digest with this relay + /// as its deliverer — the shape every continuation case needs. The record + /// appears from the `recorded_from_read`-th read on: 0 when the tick finds + /// it already there (a resume), 1 when the tick's own delivery lands it. + void settle_on(const std::vector& envelope, size_t recorded_from_read = 0) { + const auto digest = sysio::outpost_ethereum_client_detail::envelope_digest_word(envelope); + own_delivery_responses.assign(recorded_from_read, encode_word_result(zero_digest_word)); + own_delivery_responses.push_back(encode_word_result(digest)); + pending_hash_response = encode_word_result(digest); + } + + /// The distinct blocks the view reads were pinned to, adjacent repeats collapsed. + std::vector blocks_read() const { + std::vector blocks; + for (const auto& block : read_blocks) { + if (blocks.empty() || blocks.back() != block) blocks.push_back(block); + } + return blocks; } }; @@ -486,15 +665,29 @@ std::vector serialize_envelope_with_attestations(uint32_t epoch, uint32_t /// The app, signer, chain connection and client entry every relay fixture /// stands on. The connection points at a port nothing listens on, so a wrapper -/// a case forgot to stub fails loudly instead of dialing anything. +/// a case forgot to stub fails loudly instead of dialing anything; the few +/// chain-level RPCs the relay issues itself are answered by `rpc`. struct relay_test_stack { std::unique_ptr tester; fc::crypto::signature_provider_ptr sig_provider; + std::shared_ptr rpc; ethereum_client_ptr eth_client; std::shared_ptr entry; }; -relay_test_stack create_relay_test_stack() { +/// The maximum policy, with `max_gas_limit` overridden when a case needs a ceiling. +ethereum_transaction_policy relay_test_policy(fc::uint256 max_gas_limit = maximum_ethereum_transaction_policy_value()) { + return ethereum_transaction_policy{ + .client_id = std::string(latest_slot_test_entry_id), + .chain_id = test_evm_chain_id, + .max_priority_fee_per_gas = maximum_ethereum_transaction_policy_value(), + .max_fee_per_gas = maximum_ethereum_transaction_policy_value(), + .max_gas_limit = max_gas_limit, + .max_total_native_cost = maximum_ethereum_transaction_policy_value(), + }; +} + +relay_test_stack create_relay_test_stack(ethereum_transaction_policy transaction_policy = relay_test_policy()) { relay_test_stack stack; stack.tester = create_app(); @@ -505,18 +698,9 @@ relay_test_stack create_relay_test_stack() { std::string(latest_slot_test_public_key), to_private_key_spec(std::string(latest_slot_test_private_key))); - ethereum_transaction_policy transaction_policy{ - .client_id = std::string(latest_slot_test_entry_id), - .chain_id = test_evm_chain_id, - .max_priority_fee_per_gas = maximum_ethereum_transaction_policy_value(), - .max_fee_per_gas = maximum_ethereum_transaction_policy_value(), - .max_gas_limit = maximum_ethereum_transaction_policy_value(), - .max_total_native_cost = maximum_ethereum_transaction_policy_value(), - }; - stack.eth_client = std::make_shared( - stack.sig_provider, - std::variant{std::string(latest_slot_test_rpc_url)}, - std::move(transaction_policy)); + stack.rpc = std::make_shared(); + stack.eth_client = std::make_shared(stack.sig_provider, std::move(transaction_policy), + stack.rpc); stack.entry = std::make_shared(); stack.entry->id = latest_slot_test_entry_id; @@ -534,6 +718,7 @@ std::unique_ptr create_whole_envelope_delivery_ auto fixture = std::make_unique(); auto stack = create_relay_test_stack(); fixture->tester = std::move(stack.tester); + fixture->rpc = stack.rpc; auto eth_client = stack.eth_client; auto abis = load_abi_fixture(opp_inbound_abi_fixture); @@ -543,44 +728,66 @@ std::unique_ptr create_whole_envelope_delivery_ BOOST_REQUIRE(fixture->inbound); auto* raw = fixture.get(); - raw->inbound->epoch_in = [raw](uint32_t& epoch_index, std::string& envelope_hex, uint64_t gas_budget) -> fc::variant { + raw->inbound->epoch_in = [raw, inbound_address](uint32_t& epoch_index, std::string& envelope_hex, + uint64_t gas_budget) -> sysio::epoch_in_receipt { + const size_t attempt = raw->attempted_budgets.size(); + raw->attempted_budgets.push_back(gas_budget); + if (attempt < raw->refusals.size() && raw->refusals[attempt]) throw *raw->refusals[attempt]; + raw->delivery_calls.push_back(observed_delivery_call{epoch_index, envelope_hex, gas_budget}); if (raw->delivery_calls.size() == 1 && raw->first_call_delay.count() > 0) { std::this_thread::sleep_for(raw->first_call_delay); } - return fc::variant(std::string(hex_prefix) + abi_word(raw->delivery_calls.size())); + const size_t call = raw->delivery_calls.size(); + sysio::epoch_in_receipt receipt; + receipt.tx_hash = std::string(hex_prefix) + abi_word(call); + receipt.block_number = fc::uint256{test_head_block + call}; + const auto scripted = raw->receipt_logs.empty() + ? std::nullopt + : whole_envelope_delivery_fixture::response_at(raw->receipt_logs, call - 1); + receipt.logs = scripted ? *scripted + : fc::variants{epoch_delivery_log(inbound_address, epoch_index, + raw->outpost->signer_address_hex(), + settled_digest_word)}; + return receipt; }; - // Every bookkeeping read is the outpost's OWN cursor, so it is taken at - // `latest` — at `finalized` a continuation would wait out finality between - // every stretch of dispatch. - const auto expect_latest = [](const block_number_or_tag_t& block) { - BOOST_CHECK(std::holds_alternative(block)); - BOOST_CHECK(std::get(block) == block_tag_t::latest); + // Every bookkeeping read is pinned to a block number — the head the tick + // started at, or the block the last call confirmed in — never a tag. + const auto note_pinned = [raw](const block_number_or_tag_t& block) { + BOOST_REQUIRE(std::holds_alternative(block)); + raw->read_blocks.push_back(std::get(block)); }; - raw->inbound->next_epoch_index = [raw, expect_latest](const block_number_or_tag_t& block) -> fc::variant { - expect_latest(block); - ++raw->next_epoch_reads; - return fc::variant(raw->next_epoch_index_response); + raw->inbound->next_epoch_index = [raw, note_pinned](const block_number_or_tag_t& block) -> fc::variant { + note_pinned(block); + return fc::variant( + whole_envelope_delivery_fixture::response_at(raw->next_epoch_index_responses, raw->next_epoch_reads++)); }; raw->inbound->dispatch_spill = - [raw, expect_latest](const block_number_or_tag_t& block, uint32_t& epoch_index) -> fc::variant { - expect_latest(block); + [raw, note_pinned](const block_number_or_tag_t& block, uint32_t& epoch_index) -> fc::variant { + note_pinned(block); BOOST_CHECK_EQUAL(epoch_index, test_wire_epoch); - return fc::variant(raw->spill_response_at(raw->spill_reads++)); + return fc::variant(whole_envelope_delivery_fixture::response_at(raw->spill_responses, raw->spill_reads++)); }; raw->inbound->epoch_deliveries = - [raw, expect_latest](const block_number_or_tag_t& block, uint32_t& epoch_index, - std::string& operator_address) -> fc::variant { - expect_latest(block); + [raw, note_pinned](const block_number_or_tag_t& block, uint32_t& epoch_index, + std::string& operator_address) -> fc::variant { + note_pinned(block); BOOST_CHECK_EQUAL(epoch_index, test_wire_epoch); BOOST_CHECK_EQUAL(operator_address, raw->outpost->signer_address_hex()); - ++raw->settlement_reads; - return fc::variant(raw->own_delivery_response); + return fc::variant( + whole_envelope_delivery_fixture::response_at(raw->own_delivery_responses, raw->settlement_reads++)); }; - raw->inbound->pending_epoch_hash = [raw, expect_latest](const block_number_or_tag_t& block) -> fc::variant { - expect_latest(block); + raw->inbound->pending_epoch_hash = [raw, note_pinned](const block_number_or_tag_t& block) -> fc::variant { + note_pinned(block); return fc::variant(raw->pending_hash_response); }; + raw->inbound->pending_consensus_for_digest = + [raw, note_pinned](const block_number_or_tag_t& block, std::string& digest) -> fc::variant { + note_pinned(block); + ++raw->consensus_reads; + raw->consensus_digests.push_back(digest); + return fc::variant(raw->consensus_response); + }; fixture->outpost = std::make_unique( stack.entry, @@ -606,6 +813,9 @@ void check_delivery_calls(const std::vector& calls, } } +/// The block parameter a read pinned to `block` carries. +std::string pinned_block_hex(uint64_t block) { return format_rpc_quantity(fc::uint256{block}); } + // ── `crank_outpost` fixtures ───────────────────────────────────────────── constexpr std::string_view syndication_pool_abi_fixture = "ethereum-abi-syndication-pool.json"; constexpr std::string_view zero_evm_address = "0x0000000000000000000000000000000000000000"; @@ -626,14 +836,6 @@ constexpr std::string_view access_managed_unauthorized_selector = "068ca9d8"; constexpr uint64_t test_yield_delta = 5; constexpr uint64_t test_yield_deadband = 10; -/// ABI-encode one address as the single word an `address` getter returns. -std::string encode_address_word(std::string_view address_hex) { - std::string_view address = address_hex; - if (address.starts_with("0x") || address.starts_with("0X")) address.remove_prefix(2); - return std::string(hex_prefix) + std::string(evm_abi_word_hex_chars - address.size(), '0') + - std::string(address); -} - /// ABI-encode a two-`uint64` custom error the way a node returns it in `error.data`. std::string encode_two_word_revert(std::string_view selector, uint64_t first, uint64_t second) { return std::string(hex_prefix) + std::string(selector) + abi_word(first) + abi_word(second); @@ -957,10 +1159,10 @@ BOOST_AUTO_TEST_CASE(opp_inbound_contract_client_construction) try { BOOST_CHECK(!abis.empty()); // Every ABI entry the typed `opp_inbound_contract_client` binds at - // construction: the whole-envelope write and the four cursor views the + // construction: the whole-envelope write and the five views the // deliver-or-continue decision reads. The staged-chunk surface is gone. bool has_epoch_in = false, has_next_epoch = false, has_spill = false; - bool has_deliveries = false, has_pending_hash = false; + bool has_deliveries = false, has_pending_hash = false, has_pending_consensus = false; bool has_discard = false, has_chunk_state = false; for (auto& c : abis) { if (c.name == "epochIn") has_epoch_in = true; @@ -968,6 +1170,7 @@ BOOST_AUTO_TEST_CASE(opp_inbound_contract_client_construction) try { if (c.name == "dispatchSpill") has_spill = true; if (c.name == "epochDeliveries") has_deliveries = true; if (c.name == "pendingEpochHash") has_pending_hash = true; + if (c.name == "pendingConsensusForDigest") has_pending_consensus = true; if (c.name == "discardEnvelopeChunks") has_discard = true; if (c.name == "envelopeChunkState") has_chunk_state = true; } @@ -976,6 +1179,7 @@ BOOST_AUTO_TEST_CASE(opp_inbound_contract_client_construction) try { BOOST_CHECK(has_spill); BOOST_CHECK(has_deliveries); BOOST_CHECK(has_pending_hash); + BOOST_CHECK(has_pending_consensus); BOOST_CHECK(!has_discard); BOOST_CHECK(!has_chunk_state); } FC_LOG_AND_RETHROW(); @@ -1264,14 +1468,7 @@ BOOST_AUTO_TEST_CASE(read_emitted_envelope_digest_reads_the_outpost_record) try /// ceiling and never exceeds EIP-7825's cap; the options a call is sent with /// pin floor and cap to the one budget, so the pre-flight runs under it. BOOST_AUTO_TEST_CASE(delivery_gas_ceiling_is_the_policy_ceiling_bounded_by_the_cap) try { - ethereum_transaction_policy policy{ - .client_id = std::string(latest_slot_test_entry_id), - .chain_id = test_evm_chain_id, - .max_priority_fee_per_gas = maximum_ethereum_transaction_policy_value(), - .max_fee_per_gas = maximum_ethereum_transaction_policy_value(), - .max_gas_limit = maximum_ethereum_transaction_policy_value(), - .max_total_native_cost = maximum_ethereum_transaction_policy_value(), - }; + auto policy = relay_test_policy(); BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy), sysio::EIP_7825_TX_GAS_CAP); policy.max_gas_limit = fc::uint256{sysio::EIP_7825_TX_GAS_CAP}; @@ -1285,6 +1482,25 @@ BOOST_AUTO_TEST_CASE(delivery_gas_ceiling_is_the_policy_ceiling_bounded_by_the_c BOOST_CHECK_EQUAL(options.gas_limit_cap, below_cap_policy_gas_limit); } FC_LOG_AND_RETHROW(); +/// At a fee, the ceiling is also what the policy's total-cost term pays for: +/// one ether at a hundred gwei is ten million gas, under the cap; a zero fee +/// or an ample budget leaves the static ceiling in force. +BOOST_AUTO_TEST_CASE(delivery_gas_ceiling_is_bounded_by_the_total_cost_at_the_fee) try { + auto policy = relay_test_policy(); + policy.max_total_native_cost = fc::uint256{one_ether_wei}; + constexpr uint64_t affordable_at_hundred_gwei = one_ether_wei / hundred_gwei; + static_assert(affordable_at_hundred_gwei < sysio::EIP_7825_TX_GAS_CAP); + + BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy, fc::uint256{hundred_gwei}), affordable_at_hundred_gwei); + BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy, fc::uint256{0}), sysio::EIP_7825_TX_GAS_CAP); + BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy, fc::uint256{1}), sysio::EIP_7825_TX_GAS_CAP); + // The static ceiling still binds when it is the lower of the two. + policy.max_gas_limit = fc::uint256{below_cap_policy_gas_limit}; + BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy, fc::uint256{1}), below_cap_policy_gas_limit); + BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy, fc::uint256{hundred_gwei}), + std::min(affordable_at_hundred_gwei, below_cap_policy_gas_limit)); +} FC_LOG_AND_RETHROW(); + /// A delivery is funded to what it has left to carry — fixed cost, bytes, an /// allowance per attestation still to dispatch, the emit — doubled per spill /// already met this tick and never past the ceiling; an envelope the relay @@ -1316,11 +1532,38 @@ BOOST_AUTO_TEST_CASE(delivery_gas_budget_follows_the_remaining_work_and_escalate serialize_envelope_with_attestations(test_wire_epoch, 3, 16)), 3u); BOOST_CHECK(!sysio::outpost_ethereum_client_detail::count_envelope_attestations(make_envelope(mid_envelope_bytes))); + // The least ceiling the relay accepts is one full-cap delivery's worth. + BOOST_CHECK_EQUAL(sysio::DELIVERY_MINIMUM_GAS_CEILING, + sysio::DELIVERY_FIXED_GAS + sysio::OPP_MAX_ENVELOPE_BYTES * sysio::DELIVERY_GAS_PER_BYTE + + sysio::ATTESTATION_GAS_ALLOWANCE + sysio::EMIT_GAS_ALLOWANCE); + BOOST_CHECK(sysio::DELIVERY_MINIMUM_GAS_CEILING < sysio::EIP_7825_TX_GAS_CAP); +} FC_LOG_AND_RETHROW(); + +/// A relay that delivers is refused a policy whose ceiling cannot complete a +/// full-cap envelope; one that only reads the outpost (no OPPInbound address) +/// is not, since it never funds a delivery. +BOOST_AUTO_TEST_CASE(relay_refuses_a_policy_ceiling_below_one_full_delivery) try { + static_assert(undeliverable_policy_gas_limit < sysio::DELIVERY_MINIMUM_GAS_CEILING); + auto stack = create_relay_test_stack(relay_test_policy(fc::uint256{undeliverable_policy_gas_limit})); + auto abis = load_abi_fixture(opp_inbound_abi_fixture); + const auto opp_abis = load_abi_fixture(opp_abi_fixture); + abis.insert(abis.end(), opp_abis.begin(), opp_abis.end()); + + BOOST_CHECK_THROW(sysio::outpost_ethereum_client(stack.entry, std::string{}, std::string(test_opp_inbound_address), + abis, test_outpost_chain_code, test_evm_chain_id), + sysio::chain::plugin_config_exception); + BOOST_CHECK_NO_THROW(sysio::outpost_ethereum_client(stack.entry, std::string(test_opp_address), std::string{}, + abis, test_outpost_chain_code, test_evm_chain_id)); + + stack.tester.reset(); + appbase::application::reset_app_singleton(); } FC_LOG_AND_RETHROW(); /// Across a tipped-and-spilled epoch the budget tracks the outpost's cursor: /// everything before the tip, what remains after it, only the emit once /// dispatch is complete — each continuation doubled for the spill before it. +/// Every read is pinned: the head when the tick starts, then the block each +/// confirmed call landed in. BOOST_AUTO_TEST_CASE(delivery_funds_each_call_to_its_remaining_work) try { auto fixture = create_whole_envelope_delivery_fixture(); auto envelope = serialize_envelope_with_attestations(test_wire_epoch, 2, 64); @@ -1330,7 +1573,7 @@ BOOST_AUTO_TEST_CASE(delivery_funds_each_call_to_its_remaining_work) try { encode_dispatch_spill_result(true, 2, true, false), // dispatched, emit outstanding encode_dispatch_spill_result(true, 2, true, true), // finalized }; - fixture->own_delivery_response = encode_word_result(settled_digest_word); + fixture->settle_on(envelope, /*recorded_from_read=*/1); const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); @@ -1349,34 +1592,216 @@ BOOST_AUTO_TEST_CASE(delivery_funds_each_call_to_its_remaining_work) try { BOOST_CHECK_EQUAL(fixture->delivery_calls[2].gas_budget, expected(0, 2)); BOOST_CHECK(fixture->delivery_calls[0].gas_budget < fixture->delivery_calls[1].gas_budget); BOOST_CHECK(fixture->delivery_calls[0].gas_budget < sysio::EIP_7825_TX_GAS_CAP); + + // One head read, then a read at each of the three receipt blocks. + BOOST_CHECK_EQUAL(fixture->rpc->block_number_reads, 1u); + const std::vector expected_blocks{pinned_block_hex(test_head_block), pinned_block_hex(test_head_block + 1), + pinned_block_hex(test_head_block + 2), pinned_block_hex(test_head_block + 3)}; + const auto blocks = fixture->blocks_read(); + BOOST_CHECK_EQUAL_COLLECTIONS(blocks.begin(), blocks.end(), expected_blocks.begin(), expected_blocks.end()); } FC_LOG_AND_RETHROW(); /// The deliver-or-continue decision table, exercised without an EVM node. BOOST_AUTO_TEST_CASE(delivery_decision_table) try { namespace detail = sysio::outpost_ethereum_client_detail; - using action = detail::delivery_action; + using action = detail::delivery_action; + using settlement = detail::delivery_settlement; const detail::dispatch_spill untipped{}; const detail::dispatch_spill mid_flight{.tipped = true, .dispatched = 3, .complete = false, .finalized = false}; const detail::dispatch_spill emit_pending{.tipped = true, .dispatched = 9, .complete = true, .finalized = false}; const detail::dispatch_spill finalized{.tipped = true, .dispatched = 9, .complete = true, .finalized = true}; + const auto decide = [](uint32_t next, const detail::dispatch_spill& spill, settlement s, bool majority) { + return detail::decide_delivery(next, test_wire_epoch, spill, s, majority); + }; - // Not tipped: deliver, whatever this relay's own record says. - BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, untipped, false) == action::deliver); - BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, untipped, true) == action::deliver); - // A stale outpost cursor is still "this epoch": deliver. - BOOST_CHECK(detail::decide_delivery(test_stale_wire_epoch, test_wire_epoch, untipped, false) == action::deliver); + // Not tipped, nothing recorded: deliver. + BOOST_CHECK(decide(test_wire_epoch, untipped, settlement::never_delivered, false) == action::deliver); + BOOST_CHECK(decide(test_wire_epoch, untipped, settlement::never_delivered, true) == action::deliver); + // Not tipped, recorded: re-deliver only when the outpost's own majority view says it would tip. + BOOST_CHECK(decide(test_wire_epoch, untipped, settlement::recorded, true) == action::retry_consensus); + BOOST_CHECK(decide(test_wire_epoch, untipped, settlement::recorded, false) == action::await_peers); - // Tipped and unfinished: only the settled digest's deliverers continue. - BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, mid_flight, true) == action::continue_dispatch); - BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, emit_pending, true) == action::continue_dispatch); - BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, mid_flight, false) == action::wait_for_deliverer); - BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, emit_pending, false) == action::wait_for_deliverer); + // Tipped and unfinished: only the settled digest's deliverer continues. + BOOST_CHECK(decide(test_wire_epoch, mid_flight, settlement::settled, false) == action::continue_dispatch); + BOOST_CHECK(decide(test_wire_epoch, emit_pending, settlement::settled, false) == action::continue_dispatch); + BOOST_CHECK(decide(test_wire_epoch, mid_flight, settlement::never_delivered, false) == action::wait_for_deliverer); + BOOST_CHECK(decide(test_wire_epoch, mid_flight, settlement::divergent, false) == action::wait_for_deliverer); + BOOST_CHECK(decide(test_wire_epoch, emit_pending, settlement::divergent, true) == action::wait_for_deliverer); // Closed, by either signal: nothing to send. - BOOST_CHECK(detail::decide_delivery(test_wire_epoch, test_wire_epoch, finalized, true) == action::already_finalized); - BOOST_CHECK(detail::decide_delivery(test_different_wire_epoch, test_wire_epoch, untipped, false) == action::already_finalized); - BOOST_CHECK(detail::decide_delivery(test_different_wire_epoch, test_wire_epoch, mid_flight, true) == action::already_finalized); + BOOST_CHECK(decide(test_wire_epoch, finalized, settlement::settled, false) == action::already_finalized); + BOOST_CHECK(decide(test_different_wire_epoch, untipped, settlement::never_delivered, false) == action::already_finalized); + BOOST_CHECK(decide(test_different_wire_epoch, mid_flight, settlement::settled, false) == action::already_finalized); + + // The outpost has not reached this epoch: a delivery would be refused as non-sequential. + BOOST_CHECK(decide(test_stale_wire_epoch, untipped, settlement::never_delivered, false) == action::outpost_behind); + BOOST_CHECK(decide(test_stale_wire_epoch, mid_flight, settlement::settled, false) == action::outpost_behind); +} FC_LOG_AND_RETHROW(); + +/// This relay's record is classified against the settled digest only once the +/// epoch tipped; a malformed word is an error, never "nothing recorded". +BOOST_AUTO_TEST_CASE(delivery_settlement_classification) try { + namespace detail = sysio::outpost_ethereum_client_detail; + using settlement = detail::delivery_settlement; + + BOOST_CHECK(detail::classify_settlement(zero_digest_word, settled_digest_word, false) == settlement::never_delivered); + BOOST_CHECK(detail::classify_settlement(zero_digest_word, settled_digest_word, true) == settlement::never_delivered); + BOOST_CHECK(detail::classify_settlement(settled_digest_word, settled_digest_word, false) == settlement::recorded); + BOOST_CHECK(detail::classify_settlement(divergent_digest_word, settled_digest_word, false) == settlement::recorded); + BOOST_CHECK(detail::classify_settlement(settled_digest_word, settled_digest_word, true) == settlement::settled); + BOOST_CHECK(detail::classify_settlement(divergent_digest_word, settled_digest_word, true) == settlement::divergent); + // Checksum casing on either side does not split a match. + const std::string upper{"1111111111111111111111111111111111111111111111111111111111111111"}; + BOOST_CHECK(detail::classify_settlement(upper, settled_digest_word, true) == settlement::settled); + + BOOST_CHECK_THROW(detail::classify_settlement("", settled_digest_word, true), fc::exception); + BOOST_CHECK_THROW(detail::classify_settlement(settled_digest_word, "0x", true), fc::exception); + BOOST_CHECK_THROW(detail::classify_settlement(std::string(settled_digest_word) + "0", settled_digest_word, true), + fc::exception); +} FC_LOG_AND_RETHROW(); + +/// The contract's path-2 predicate, mirrored: the boundary elapsed AND a +/// strict majority agreeing; an empty group never tips. +BOOST_AUTO_TEST_CASE(majority_tip_is_the_contracts_path_2_predicate) try { + namespace detail = sysio::outpost_ethereum_client_detail; + constexpr uint64_t started = 1'000; + constexpr uint32_t duration = 60; + constexpr uint64_t boundary = started + duration; + const auto view = [](uint32_t agreeing, uint32_t group) { + return detail::pending_consensus{.next_epoch = test_wire_epoch, .agreeing = agreeing, .group_size = group, + .epoch_started_at = started, .epoch_duration_sec = duration}; + }; + + BOOST_CHECK(detail::majority_tip_reachable(view(test_majority, test_group_size), boundary)); + BOOST_CHECK(detail::majority_tip_reachable(view(test_group_size, test_group_size), boundary + 1)); + BOOST_CHECK(!detail::majority_tip_reachable(view(test_majority, test_group_size), boundary - 1)); + BOOST_CHECK(!detail::majority_tip_reachable(view(test_majority - 1, test_group_size), boundary)); + BOOST_CHECK(!detail::majority_tip_reachable(view(0, 0), boundary)); + // A group of one tips on its own delivery; the strict majority of five is three. + BOOST_CHECK(detail::majority_tip_reachable(view(1, 1), boundary)); + BOOST_CHECK(detail::majority_tip_reachable(view(3, 5), boundary)); + BOOST_CHECK(!detail::majority_tip_reachable(view(2, 5), boundary)); +} FC_LOG_AND_RETHROW(); + +/// The progress invariant: every field the outpost moves counts as progress, +/// and nothing else does. +BOOST_AUTO_TEST_CASE(advanced_tracks_every_cursor_field) try { + namespace detail = sysio::outpost_ethereum_client_detail; + const detail::delivery_progress before{.next_epoch_index = test_wire_epoch, + .spill = {}, + .own_digest = std::string(zero_digest_word), + .settled_digest = std::string(zero_digest_word)}; + BOOST_CHECK(!detail::advanced(before, before)); + + auto recorded = before; + recorded.own_digest = std::string(settled_digest_word); + BOOST_CHECK(detail::advanced(before, recorded)); + auto tipped = before; + tipped.spill.tipped = true; + BOOST_CHECK(detail::advanced(before, tipped)); + auto dispatched = tipped; + dispatched.spill.dispatched = 1; + BOOST_CHECK(detail::advanced(tipped, dispatched)); + BOOST_CHECK(!detail::advanced(dispatched, dispatched)); + auto complete = dispatched; + complete.spill.complete = true; + BOOST_CHECK(detail::advanced(dispatched, complete)); + auto finalized = complete; + finalized.spill.finalized = true; + BOOST_CHECK(detail::advanced(complete, finalized)); + auto next_epoch = before; + next_epoch.next_epoch_index = test_different_wire_epoch; + BOOST_CHECK(detail::advanced(before, next_epoch)); + // The settled digest changing on its own is another epoch's business, not progress here. + auto resettled = before; + resettled.settled_digest = std::string(settled_digest_word); + BOOST_CHECK(!detail::advanced(before, resettled)); +} FC_LOG_AND_RETHROW(); + +/// The `epochIn` refusals are identified by selector AND shape, exactly as the +/// pool's are, and only for a call the node executed. +BOOST_AUTO_TEST_CASE(epoch_in_revert_selectors_are_pinned) try { + namespace detail = sysio::outpost_ethereum_client_detail; + using revert = detail::epoch_in_revert; + const auto data = [](std::string_view selector, size_t words) { + return epoch_in_refusal(selector, words).data.as_string(); + }; + + BOOST_CHECK(detail::classify_epoch_in_revert(contract_revert_rpc_code, data(dispatch_underfunded_selector, 2)) == + revert::dispatch_underfunded); + BOOST_CHECK(detail::classify_epoch_in_revert(contract_revert_rpc_code, data(handler_gas_exhausted_selector, 3)) == + revert::handler_gas_exhausted); + BOOST_CHECK(detail::classify_epoch_in_revert(contract_revert_rpc_code, data(non_sequential_epoch_selector, 2)) == + revert::non_sequential_epoch); + BOOST_CHECK(detail::classify_epoch_in_revert(contract_revert_rpc_code, data(operator_already_delivered_selector, 2)) == + revert::operator_already_delivered); + BOOST_CHECK(detail::classify_epoch_in_revert(contract_revert_rpc_code, data(not_active_operator_selector, 1)) == + revert::not_active_operator); + BOOST_CHECK(detail::classify_epoch_in_revert(contract_revert_rpc_code, data(digest_mismatch_selector, 2)) == + revert::digest_mismatch); + + // Wrong shape, wrong selector, a protocol error, or no bytes: not a known refusal. + BOOST_CHECK(!detail::classify_epoch_in_revert(contract_revert_rpc_code, data(dispatch_underfunded_selector, 1))); + BOOST_CHECK(!detail::classify_epoch_in_revert(contract_revert_rpc_code, data(not_active_operator_selector, 2))); + BOOST_CHECK(!detail::classify_epoch_in_revert(contract_revert_rpc_code, data(no_yield_selector, 0))); + BOOST_CHECK(!detail::classify_epoch_in_revert(json_rpc_parse_error_code, data(dispatch_underfunded_selector, 2))); + BOOST_CHECK(!detail::classify_epoch_in_revert(contract_revert_rpc_code, "")); + BOOST_CHECK(!detail::classify_epoch_in_revert(contract_revert_rpc_code, "0x")); +} FC_LOG_AND_RETHROW(); + +/// A confirmed call's receipt is summarised from the `OPPInbound` events in it, +/// for this epoch and this relay only; logs from other contracts, other epochs +/// and other operators are ignored. +BOOST_AUTO_TEST_CASE(delivery_receipts_summarise_the_outposts_events) try { + namespace detail = sysio::outpost_ethereum_client_detail; + const std::string inbound{test_opp_inbound_address}; + const std::string self{test_other_operator_address}; + const std::string other{test_syndication_pool_address}; + + const auto nothing = detail::summarize_delivery_receipt({}, inbound, test_wire_epoch, self); + BOOST_CHECK(!nothing.recorded && !nothing.tipped && !nothing.dispatched && !nothing.finalized); + + const fc::variants full{ + epoch_delivery_log(inbound, test_wire_epoch, self, settled_digest_word), + epoch_consensus_log(inbound, test_wire_epoch, settled_digest_word, test_group_size), + epoch_dispatch_progressed_log(inbound, test_wire_epoch, 4), + epoch_complete_log(inbound, test_wire_epoch), + }; + const auto summary = detail::summarize_delivery_receipt(full, inbound, test_wire_epoch, self); + BOOST_CHECK(summary.recorded); + BOOST_CHECK(summary.tipped); + BOOST_REQUIRE(summary.dispatched.has_value()); + BOOST_CHECK_EQUAL(*summary.dispatched, 4u); + BOOST_CHECK(summary.finalized); + // Addresses compare without their checksum casing. + std::string upper_inbound = inbound; + std::ranges::transform(upper_inbound, upper_inbound.begin(), [](unsigned char c) { return std::toupper(c); }); + BOOST_CHECK(detail::summarize_delivery_receipt(full, upper_inbound, test_wire_epoch, self).recorded); + + const fc::variants foreign{ + epoch_delivery_log(other, test_wire_epoch, self, settled_digest_word), // another contract + epoch_delivery_log(inbound, test_wire_epoch, other, settled_digest_word), // another operator + epoch_consensus_log(inbound, test_different_wire_epoch, settled_digest_word, 1), // another epoch + epoch_dispatch_progressed_log(inbound, test_different_wire_epoch, 4), + epoch_complete_log(inbound, test_different_wire_epoch), + fc::variant("not a log object"), + }; + const auto ignored = detail::summarize_delivery_receipt(foreign, inbound, test_wire_epoch, self); + BOOST_CHECK(!ignored.recorded && !ignored.tipped && !ignored.dispatched && !ignored.finalized); + + // The receipt parser keeps the block and the logs, and refuses a receipt without a block. + const auto parsed = sysio::parse_epoch_in_receipt( + "0x1", fc::mutable_variant_object("blockNumber", pinned_block_hex(test_head_block))("logs", full)); + BOOST_CHECK_EQUAL(parsed.tx_hash, "0x1"); + BOOST_CHECK_EQUAL(parsed.block_number, fc::uint256{test_head_block}); + BOOST_CHECK_EQUAL(parsed.logs.size(), full.size()); + BOOST_CHECK(sysio::parse_epoch_in_receipt("0x1", fc::mutable_variant_object("blockNumber", "0x1")).logs.empty()); + BOOST_CHECK_THROW(sysio::parse_epoch_in_receipt("0x1", fc::mutable_variant_object("logs", full)), fc::exception); + BOOST_CHECK_THROW(sysio::parse_epoch_in_receipt("0x1", fc::variant("0x1")), fc::exception); + + // The digest the relay compares against the settled one is keccak256 of the bytes. + BOOST_CHECK_EQUAL(detail::envelope_digest_word({}), keccak256_of_empty); } FC_LOG_AND_RETHROW(); BOOST_AUTO_TEST_CASE(delivery_rejects_empty_and_over_cap_envelopes) try { @@ -1411,11 +1836,16 @@ BOOST_AUTO_TEST_CASE(delivery_sends_the_whole_envelope_in_one_call) try { // Opaque bytes carry no attestation count to size a budget from: the // ceiling, and the contract judges them. BOOST_CHECK_EQUAL(fixture->delivery_calls[0].gas_budget, sysio::EIP_7825_TX_GAS_CAP); - // Read before the delivery, and once more after it to learn it did not tip. + // Read at the head before the delivery, and once more at the receipt's + // block after it to learn it was recorded and did not tip. BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 2u); BOOST_CHECK_EQUAL(fixture->spill_reads, 2u); - // Settlement is only consulted for a tipped epoch. - BOOST_CHECK_EQUAL(fixture->settlement_reads, 0u); + BOOST_CHECK_EQUAL(fixture->settlement_reads, 2u); + const std::vector expected_blocks{pinned_block_hex(test_head_block), pinned_block_hex(test_head_block + 1)}; + const auto blocks = fixture->blocks_read(); + BOOST_CHECK_EQUAL_COLLECTIONS(blocks.begin(), blocks.end(), expected_blocks.begin(), expected_blocks.end()); + // The tip check ran in the delivery itself: no consensus read follows a send. + BOOST_CHECK_EQUAL(fixture->consensus_reads, 0u); } FC_LOG_AND_RETHROW(); /// A consensus retry against an epoch the outpost has already moved past is @@ -1424,7 +1854,7 @@ BOOST_AUTO_TEST_CASE(delivery_sends_the_whole_envelope_in_one_call) try { BOOST_AUTO_TEST_CASE(delivery_skips_when_the_outpost_epoch_advanced) try { auto fixture = create_whole_envelope_delivery_fixture(); auto envelope = make_envelope(mid_envelope_bytes); - fixture->next_epoch_index_response = encode_next_epoch_index_result(test_different_wire_epoch); + fixture->next_epoch_index_responses = {encode_next_epoch_index_result(test_different_wire_epoch)}; const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); @@ -1446,7 +1876,22 @@ BOOST_AUTO_TEST_CASE(delivery_skips_an_epoch_the_cursor_reports_finalized) try { BOOST_CHECK(tx.empty()); BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); - BOOST_CHECK_EQUAL(fixture->settlement_reads, 0u); + BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 1u); +} FC_LOG_AND_RETHROW(); + +/// An outpost still on an earlier epoch cannot take this delivery yet: nothing +/// is sent, and the tick ends in the retry exception rather than marking the +/// epoch handled. +BOOST_AUTO_TEST_CASE(delivery_waits_for_an_outpost_that_is_behind) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->next_epoch_index_responses = {encode_next_epoch_index_result(test_stale_wire_epoch)}; + + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + sysio::chain::outpost_delivery_incomplete_exception); + BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 0u); } FC_LOG_AND_RETHROW(); /// A delivery that tips and spills is continued in the SAME tick: the relay @@ -1460,7 +1905,7 @@ BOOST_AUTO_TEST_CASE(delivery_that_tips_and_spills_continues_in_the_same_tick) t encode_dispatch_spill_result(true, 11, true, false), // dispatched, emit outstanding encode_dispatch_spill_result(true, 11, true, true), // finalized }; - fixture->own_delivery_response = encode_word_result(settled_digest_word); + fixture->settle_on(envelope, /*recorded_from_read=*/1); const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); @@ -1469,8 +1914,7 @@ BOOST_AUTO_TEST_CASE(delivery_that_tips_and_spills_continues_in_the_same_tick) t // The delivery, then two continuations — every one carrying the whole envelope. check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 3); BOOST_CHECK_EQUAL(fixture->spill_reads, 4u); - // Settlement is checked for each of the two mid-flight reads. - BOOST_CHECK_EQUAL(fixture->settlement_reads, 2u); + BOOST_CHECK_EQUAL(fixture->settlement_reads, 4u); } FC_LOG_AND_RETHROW(); /// Resume: a tick that finds the epoch already tipped on this relay's digest @@ -1484,14 +1928,31 @@ BOOST_AUTO_TEST_CASE(delivery_continues_a_tipped_epoch_it_delivered_until_it_fin encode_dispatch_spill_result(true, 3, true, false), encode_dispatch_spill_result(true, 3, true, true), }; - fixture->own_delivery_response = encode_word_result(settled_digest_word); + fixture->settle_on(envelope); const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); BOOST_CHECK(!tx.empty()); check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 2); - BOOST_CHECK_EQUAL(fixture->settlement_reads, 2u); + BOOST_CHECK_EQUAL(fixture->settlement_reads, 3u); +} FC_LOG_AND_RETHROW(); + +/// A continuation is sent only with the bytes consensus settled on: an +/// envelope whose digest is not the settled one is this relay's problem to +/// report, not the outpost's to refuse for a fee. +BOOST_AUTO_TEST_CASE(delivery_does_not_continue_with_an_envelope_that_is_not_the_settled_digest) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->spill_responses = {encode_dispatch_spill_result(true, 3, false, false)}; + fixture->own_delivery_responses = {encode_word_result(settled_digest_word)}; + fixture->pending_hash_response = encode_word_result(settled_digest_word); + + const auto tx = fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); + + BOOST_CHECK(tx.empty()); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 0u); } FC_LOG_AND_RETHROW(); /// A relay that never delivered has no claim on a tipped epoch's @@ -1499,8 +1960,8 @@ BOOST_AUTO_TEST_CASE(delivery_continues_a_tipped_epoch_it_delivered_until_it_fin BOOST_AUTO_TEST_CASE(delivery_leaves_a_tipped_epoch_it_did_not_deliver_to_its_deliverers) try { auto fixture = create_whole_envelope_delivery_fixture(); auto envelope = make_envelope(mid_envelope_bytes); - fixture->spill_responses = {encode_dispatch_spill_result(true, 2, false, false)}; - fixture->own_delivery_response = encode_word_result(zero_digest_word); + fixture->spill_responses = {encode_dispatch_spill_result(true, 2, false, false)}; + fixture->own_delivery_responses = {encode_word_result(zero_digest_word)}; const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); @@ -1515,8 +1976,8 @@ BOOST_AUTO_TEST_CASE(delivery_leaves_a_tipped_epoch_it_did_not_deliver_to_its_de BOOST_AUTO_TEST_CASE(delivery_does_not_continue_from_a_minority_digest) try { auto fixture = create_whole_envelope_delivery_fixture(); auto envelope = make_envelope(mid_envelope_bytes); - fixture->spill_responses = {encode_dispatch_spill_result(true, 2, false, false)}; - fixture->own_delivery_response = encode_word_result(divergent_digest_word); + fixture->spill_responses = {encode_dispatch_spill_result(true, 2, false, false)}; + fixture->own_delivery_responses = {encode_word_result(divergent_digest_word)}; const auto tx = fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); @@ -1525,6 +1986,259 @@ BOOST_AUTO_TEST_CASE(delivery_does_not_continue_from_a_minority_digest) try { BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); } FC_LOG_AND_RETHROW(); +/// A recorded, untipped delivery is re-sent only when the outpost's own +/// consensus view says the re-send would tip it — the boundary elapsed and a +/// strict majority agreeing — and only once per tick. The ordinary first +/// delivery of a tick (`delivery_sends_the_whole_envelope_in_one_call`) is the +/// recorded-untipped-after-a-send case, which returns normally. +BOOST_AUTO_TEST_CASE(delivery_retries_consensus_only_when_the_outposts_majority_view_allows) try { + const uint64_t now = fc::time_point::now().sec_since_epoch(); + constexpr uint32_t duration = 60; + const auto consensus = [&](uint32_t agreeing, uint64_t started_at) { + return encode_pending_consensus_result(test_wire_epoch, agreeing, test_group_size, started_at, duration); + }; + + // Boundary elapsed, majority agreeing: one re-delivery, then the tick stops. + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->own_delivery_responses = {encode_word_result(settled_digest_word)}; + fixture->consensus_response = consensus(test_majority, now - duration - 1); + + const auto tx = fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); + + BOOST_CHECK(!tx.empty()); + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); + BOOST_CHECK_EQUAL(fixture->consensus_reads, 1u); + BOOST_REQUIRE_EQUAL(fixture->consensus_digests.size(), 1u); + BOOST_CHECK_EQUAL(fixture->consensus_digests.front(), prefixed(settled_digest_word)); + } + // Majority not reached: nothing to send. + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->own_delivery_responses = {encode_word_result(settled_digest_word)}; + fixture->consensus_response = consensus(test_majority - 1, now - duration - 1); + + const auto tx = fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); + + BOOST_CHECK(tx.empty()); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 0u); + BOOST_CHECK_EQUAL(fixture->consensus_reads, 1u); + } + // Boundary not elapsed on the outpost: nothing to send, however many agree + // — and not a used-up retry either: the epoch stays open for the next tick. + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->own_delivery_responses = {encode_word_result(settled_digest_word)}; + fixture->consensus_response = consensus(test_group_size, now + duration); + + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + sysio::chain::outpost_delivery_incomplete_exception); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 0u); + BOOST_CHECK_EQUAL(fixture->consensus_reads, 1u); + } +} FC_LOG_AND_RETHROW(); + +/// A call that confirms but moves nothing — no record, no tip, no dispatch +/// progress — was under-funded for the attestation at the cursor: the next is +/// funded double, and a call at the ceiling that still moves nothing ends the +/// tick in the retry exception instead of a fourth paid no-op. +BOOST_AUTO_TEST_CASE(delivery_escalates_after_a_call_that_advanced_nothing_and_stops_at_the_ceiling) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = serialize_envelope_with_attestations(test_wire_epoch, 1, 16); + // The outpost never records the delivery, whatever is sent. + fixture->own_delivery_responses = {encode_word_result(zero_digest_word)}; + fixture->receipt_logs = {fc::variants{}}; + + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + sysio::chain::outpost_delivery_incomplete_exception); + + const auto expected = [&](uint32_t escalations) { + return sysio::delivery_gas_budget(sysio::delivery_gas_request{ + .envelope_bytes = envelope.size(), + .remaining_attestations = 1, + .escalations = escalations, + .ceiling = sysio::EIP_7825_TX_GAS_CAP}); + }; + BOOST_REQUIRE_EQUAL(expected(2), sysio::EIP_7825_TX_GAS_CAP); + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 3); + BOOST_CHECK_EQUAL(fixture->delivery_calls[0].gas_budget, expected(0)); + BOOST_CHECK_EQUAL(fixture->delivery_calls[1].gas_budget, expected(1)); + BOOST_CHECK_EQUAL(fixture->delivery_calls[2].gas_budget, sysio::EIP_7825_TX_GAS_CAP); + // Each read after a send was pinned to that send's block. + BOOST_CHECK_EQUAL(fixture->blocks_read().size(), 4u); + + // The next tick finds the same stall and reports it the same way, without + // paying for the under-funded calls again: straight to the ceiling. + fixture->delivery_calls.clear(); + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + sysio::chain::outpost_delivery_incomplete_exception); + BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 3u); +} FC_LOG_AND_RETHROW(); + +/// A node refusal for gas below the ceiling is retried once at the ceiling; +/// one at the ceiling is the stalled-epoch report, with nothing sent. +BOOST_AUTO_TEST_CASE(delivery_retries_a_gas_refusal_at_the_ceiling_and_reports_one_there) try { + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = serialize_envelope_with_attestations(test_wire_epoch, 2, 64); + fixture->refusals = {epoch_in_refusal(dispatch_underfunded_selector, 2)}; + + const auto tx = fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); + + BOOST_CHECK(!tx.empty()); + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); + BOOST_REQUIRE_EQUAL(fixture->attempted_budgets.size(), 2u); + BOOST_CHECK(fixture->attempted_budgets[0] < sysio::EIP_7825_TX_GAS_CAP); + BOOST_CHECK_EQUAL(fixture->attempted_budgets[1], sysio::EIP_7825_TX_GAS_CAP); + BOOST_CHECK_EQUAL(fixture->delivery_calls[0].gas_budget, sysio::EIP_7825_TX_GAS_CAP); + } + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); // unreadable: funded to the ceiling outright + fixture->refusals = {epoch_in_refusal(handler_gas_exhausted_selector, 3)}; + + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + sysio::chain::outpost_delivery_incomplete_exception); + BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); + BOOST_REQUIRE_EQUAL(fixture->attempted_budgets.size(), 1u); + BOOST_CHECK_EQUAL(fixture->attempted_budgets[0], sysio::EIP_7825_TX_GAS_CAP); + } + // Any other refusal is the job's to see. + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->refusals = {fc::network::json_rpc::json_rpc_error(json_rpc_parse_error_code, "parse error")}; + + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + fc::network::json_rpc::json_rpc_error); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 1u); + } +} FC_LOG_AND_RETHROW(); + +/// A refusal that says the outpost moved under the read — here, the epoch +/// finalized between the read and the send — is answered by a fresh read at +/// the head, not a guess; one that survives fresh reads is reported. +BOOST_AUTO_TEST_CASE(delivery_rereads_at_the_head_after_a_stale_refusal) try { + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->refusals = {epoch_in_refusal(non_sequential_epoch_selector, 2)}; + fixture->next_epoch_index_responses = {encode_next_epoch_index_result(test_wire_epoch), + encode_next_epoch_index_result(test_different_wire_epoch)}; + + const auto tx = fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); + + BOOST_CHECK(tx.empty()); + BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 1u); + BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 2u); + BOOST_CHECK_EQUAL(fixture->rpc->block_number_reads, 2u); + } + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->refusals = {epoch_in_refusal(not_active_operator_selector, 1), + epoch_in_refusal(not_active_operator_selector, 1), + epoch_in_refusal(not_active_operator_selector, 1)}; + fixture->own_delivery_responses = {encode_word_result(zero_digest_word)}; + + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + fc::network::json_rpc::json_rpc_error); + BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 0u); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 3u); + } +} FC_LOG_AND_RETHROW(); + +/// A tick sends at most `MAX_CONTINUATIONS_PER_TICK` calls; an epoch still +/// open after that is handed to the next tick through the retry exception, +/// never marked handled. +BOOST_AUTO_TEST_CASE(delivery_ends_the_tick_at_the_continuation_bound) try { + constexpr uint16_t continuation_bound = 32; + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + // Every call dispatches one more attestation and spills again. + for (uint16_t dispatched = 1; dispatched <= continuation_bound + 1; ++dispatched) { + fixture->spill_responses.push_back(encode_dispatch_spill_result(true, dispatched, false, false)); + } + fixture->spill_responses.erase(fixture->spill_responses.begin()); // drop the never-tipped default + fixture->settle_on(envelope); + + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + sysio::chain::outpost_delivery_incomplete_exception); + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, continuation_bound); + // The bound was decided from a read AFTER the last send, at its block. + BOOST_CHECK_EQUAL(fixture->spill_reads, continuation_bound + 1u); +} FC_LOG_AND_RETHROW(); + +/// The cursor readers fail closed: a spill word that does not decode, or a +/// digest that is not one 32-byte word, is an error, never "not tipped" or +/// "never delivered" — either of which would send a fresh delivery into an +/// epoch that may already have settled. +BOOST_AUTO_TEST_CASE(delivery_fails_closed_on_malformed_cursor_reads) try { + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->spill_responses = {std::string(hex_prefix) + abi_word(1)}; // one word of four + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + fc::exception); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 0u); + } + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->own_delivery_responses = {std::string(hex_prefix)}; // no word at all + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + fc::exception); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 0u); + } + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->next_epoch_index_responses = {"0xzz"}; + // The ABI decoder's own refusal of a non-hex response, not an fc::exception. + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + std::exception); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 0u); + } +} FC_LOG_AND_RETHROW(); + +/// A transport failure on a continuation propagates after the delivery that +/// landed stands: the tick abandons, nothing is rolled back, and the next tick +/// resumes from the outpost's cursor. +BOOST_AUTO_TEST_CASE(delivery_propagates_a_transport_failure_on_a_continuation) try { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(sysio::OPP_MAX_ENVELOPE_BYTES); + fixture->spill_responses = { + encode_dispatch_spill_result(false, 0, false, false), + encode_dispatch_spill_result(true, 4, false, false), + }; + fixture->settle_on(envelope, /*recorded_from_read=*/1); + fixture->refusals = {std::nullopt, fc::network::json_rpc::json_rpc_error(json_rpc_parse_error_code, "gateway timeout")}; + + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + fc::network::json_rpc::json_rpc_error); + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); + BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 2u); +} FC_LOG_AND_RETHROW(); + /// Mid-sequence deadline expiry — the scenario the cursor exists for. /// /// The delivery burns more wall clock than the whole budget, so the @@ -1541,7 +2255,7 @@ BOOST_AUTO_TEST_CASE(delivery_abandons_on_deadline_then_continues_next_tick) try encode_dispatch_spill_result(false, 0, false, false), encode_dispatch_spill_result(true, 6, false, false), }; - fixture->own_delivery_response = encode_word_result(settled_digest_word); + fixture->settle_on(envelope, /*recorded_from_read=*/1); BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( test_wire_epoch, envelope, fc::milliseconds(delivery_budget_ms)), @@ -1569,9 +2283,9 @@ BOOST_AUTO_TEST_CASE(delivery_abandons_on_deadline_then_continues_next_tick) try // ── `crank_outpost` ────────────────────────────────────────────────────── -/// The pool's three refusals are identified by selector AND shape, exactly as -/// `OPP_ChunkBufferMissing` is: a neighbouring error, a role error, a refusal with the wrong -/// argument count, and no bytes at all are none of them. +/// The pool's three refusals are identified by selector AND shape, exactly as the `epochIn` +/// refusals are: a neighbouring error, a role error, a refusal with the wrong argument count, +/// and no bytes at all are none of them. BOOST_AUTO_TEST_CASE(realize_yield_refusal_selectors_are_pinned) try { namespace crank = sysio::outpost_ethereum_client_detail; using refusal = crank::realize_yield_refusal; diff --git a/tests/fixtures/ethereum-abi-opp-current.json b/tests/fixtures/ethereum-abi-opp-current.json index 66811df72d..b1e243e52e 100644 --- a/tests/fixtures/ethereum-abi-opp-current.json +++ b/tests/fixtures/ethereum-abi-opp-current.json @@ -78,6 +78,17 @@ "name": "InvalidInitialization", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "position", + "type": "uint256" + } + ], + "name": "MalformedEnvelope", + "type": "error" + }, { "inputs": [ { @@ -94,6 +105,22 @@ "name": "NotInitializing", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "nextEpochIndex", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "deliveries", + "type": "uint32" + } + ], + "name": "OPP_BootstrapWindowClosed", + "type": "error" + }, { "inputs": [ { @@ -110,6 +137,22 @@ "name": "OPP_DigestMismatch", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLeft", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "floor", + "type": "uint256" + } + ], + "name": "OPP_DispatchUnderfunded", + "type": "error" + }, { "inputs": [ { @@ -206,6 +249,17 @@ "name": "OPP_InboundEnvelopeStillInRetention", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "OPP_InternalDispatchOnly", + "type": "error" + }, { "inputs": [ { @@ -1388,11 +1442,11 @@ "type": "function" } ], - "bytecode": "0x60a0806040523460c857306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1660b957506001600160401b036002600160401b0319828216016075575b6040516147a190816100cd82396080518181816109570152613bd20152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80806056565b63f92ee8a960e01b8152600490fd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806302ddde7b1461028457806304676e381461027f5780630d6f1e9d1461027a57806316a91b741461027557806327e935a11461027057806347453a2a1461026b5780634b3254fb146102665780634ed891d0146102615780634f1ef2861461025c5780634fca79cb1461025757806352d1902d14610252578063621fbd591461024d5780636744c79f146102485780637a9e5e4b146102435780638cbc18391461023e5780638fb360371461023957806393ec06be1461023457806395fabcf01461022f578063a3ad9cc31461022a578063a593aec214610225578063a9e50c1014610220578063ad3cb1cc1461021b578063b632ab5714610216578063b8f8d7e514610211578063bf7e214f1461020c578063c16a8a5614610207578063c4d66de814610202578063c8c14d17146101fd578063c94c93e7146101f8578063cde78421146101f3578063d5b5272a146101ee578063de152649146101e9578063e0064900146101e4578063e0c14aa8146101df578063e0c1c0b4146101da578063e2fc04ae146101d5578063e36f9a07146101d0578063e44efb5a146101cb5763f5786b4b146101c6575f80fd5b61193a565b61191b565b6118fe565b6117e1565b611515565b6113e9565b6113ac565b61138f565b61129a565b611272565b611243565b611227565b61110d565b6110ea565b6110b6565b611093565b611077565b611032565b610ffa565b610f00565b610c12565b610bf5565b610bd9565b610b89565b610ac2565b610a48565b6109d4565b6109ae565b610945565b610899565b61078e565b610628565b6105c8565b61056e565b610511565b6104ed565b610431565b610393565b610297565b5f91031261029357565b5f80fd5b34610293575f366003190112610293575f8051602061474c833981519152805460ff8160401c16908115610363575b50610351575f8051602061474c833981519152805467ffffffffffffffff19166002179055805460ff60401b1916600160401b179055610304611f66565b5f8051602061474c833981519152805460ff60401b19169055604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b60405163f92ee8a960e01b8152600490fd5b600291506001600160401b031610155f6102c6565b63ffffffff81160361029357565b359061039182610378565b565b346102935760203660031901126102935760606004356103b281610378565b63ffffffff8091165f52600960205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60409063ffffffff61042e949316815281602082015201906103ed565b90565b34610293575f3660031901126102935761045d6104546104105463ffffffff1690565b63ffffffff1690565b61046681611fe8565b90610479610473826105f8565b60051c90565b5f5b8181106104d5575050601f811690816104b2575b8261049f600c5463ffffffff1690565b6104ae60405192839283610411565b0390f35b81602091038301019060015f199160200360031b1b011981511690525f8061048f565b806001916010015460208260051b870101520161047b565b34610293575f36600319011261029357602063ffffffff6104105416604051908152f35b346102935760203660031901126102935760043561052f3633612d6b565b8015610548576004541561053f57005b6004555f600355005b604051636ad43cc360e11b8152600490fd5b634e487b7160e01b5f52603260045260245ffd5b34610293576020366003190112610293576004356006548110156102935760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01546040516001600160a01b039091168152602090f35b34610293575f3660031901126102935760206040516104008152f35b634e487b7160e01b5f52601160045260245ffd5b90601f820180921161060657565b6105e4565b9061010091820180921161060657565b9190820180921161060657565b34610293575f3660031901126102935760206040516101188152f35b600435906001600160a01b038216820361029357565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761068957604052565b61065a565b602081019081106001600160401b0382111761068957604052565b604081019081106001600160401b0382111761068957604052565b60e081019081106001600160401b0382111761068957604052565b6001600160401b03811161068957604052565b90601f801991011681019081106001600160401b0382111761068957604052565b60405190610391826106c4565b604051906103918261066e565b6001600160401b03811161068957601f01601f191660200190565b81601f820112156102935780359061075f8261072d565b9261076d60405194856106f2565b8284526020838301011161029357815f926020809301838601378301015290565b6040366003190112610293576107a2610644565b6024356001600160401b038111610293576107c1903690600401610748565b906107ca613bc8565b6107d43633612d6b565b6107dc613bc8565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610868575b5061082e57604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f8051602061472c833981519152830361084f5761084d92506141b2565b005b604051632a87526960e21b815260048101849052602490fd5b61088b91945060203d602011610892575b61088381836106f2565b8101906128a0565b925f610804565b503d610879565b34610293576020366003190112610293576104ae6004356108b981610378565b60405f8180516108c88161066e565b828152826020820152015263ffffffff8092165f5260096020526001815f206001600160401b038351946108fb8661066e565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610293575f366003190112610293577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316300361099c5760206040515f8051602061472c8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610293575f3660031901126102935760205f54604051908152f35b8015150361029357565b34610293576020366003190112610293576004356109f1816109ca565b600f546001600160a01b031633819003610a2a57507fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755d005b60449060405190636e01d1ef60e11b82523360048301526024820152fd5b3461029357602036600319011261029357610a61610644565b5f8051602061470c833981519152546001600160a01b03919082163303610aab57803b15610a925761084d90612e87565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b3461029357604036600319011261029357602435600435610ae33633612d6b565b63ffffffff6104105416610b445781811080610b38575b610b039061201a565b818110610b0c57005b80610b32610b1b600193612066565b81549060031b906001821b915f19901b1916179055565b01610b03565b50610400821115610afa565b60405162461bcd60e51b815260206004820152601f60248201527f4f50503a206f7574626f756e642062616e6b20616c7265616479206c697665006044820152606490fd5b34610293575f366003190112610293575f8051602061470c8339815191525460a01c60ff1615610bd1576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610bbe565b34610293575f3660031901126102935760206040516180008152f35b34610293575f366003190112610293576020600354604051908152f35b3461029357602036600319011261029357600435610c2f81610378565b610c393633612d6b565b600c5463ffffffff16610c4e61045482612078565b63ffffffff831603610ed15750610c636120c7565b63ffffffff82166060820152610c94610c846001600160401b034216612104565b6001600160401b03166040830152565b610c9c612edf565b60a08201525f600854908115159182610d3e575b7fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d88761034c86610d32610ce188612f60565b91610cf161800084511115612793565b610d1383516020850120610d058184613323565b610d0e83613389565b600d55565b610d1c836133e1565b63ffffffff1663ffffffff19600c541617600c55565b60405191829182612805565b5f92836101005b838210610e70575b5050610d639083159081610e68575b501561212e565b81610d6d81612209565b60c086019081525f5b828110610e30575050610d889161226c565b905f5b828110610e0257505f5b818110610df1575050610ce161034c93610de7610de27fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d8879795610dda610d3296600855565b600e5461226c565b600e55565b9350829450610cb0565b600190610dfc6126c1565b01610d95565b80610e2a610e1a610e156001948661061b565b611caa565b50610e2483611caa565b90612552565b01610d8b565b6001919250610e5e81610e4281611caa565b50610e4e865191611e88565b610e588383612258565b52612258565b5001908391610d76565b90505f610d5c565b610e8d610e88610e8284979894611caa565b50611e88565b612c9e565b90618000610e9b838361061b565b11610ec757610eb982610eb3600194610ebf9461061b565b9461061b565b96612120565b940190610d45565b5081959450610d4d565b610eda90612078565b6040516307d3e83d60e51b815263ffffffff918216600482015291166024820152604490fd5b3461029357602036600319011261029357600435610f1d81610378565b610f273633612d6b565b63ffffffff9081811691825f52600960205260405f20541615610fb557610f84610f8f91610f70610f68610454610f63600c5463ffffffff1690565b613432565b851115612816565b63ffffffff165f52600960205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b60405162461bcd60e51b815260206004820152601760248201527f4f50503a207265636f7264206e6f742070726573656e740000000000000000006044820152606490fd5b34610293575f3660031901126102935760206001600160401b0360015416604051908152f35b6040519061102d8261068e565b5f8252565b34610293575f366003190112610293576104ae604051611051816106a9565b60058152640352e302e360dc1b60208201526040519182916020835260208301906103ed565b34610293575f3660031901126102935760206040516120008152f35b34610293575f36600319011261029357602063ffffffff600c5416604051908152f35b34610293575f366003190112610293575f8051602061470c833981519152546040516001600160a01b039091168152602090f35b34610293575f36600319011261029357602063ffffffff600a5416604051908152f35b3461029357602036600319011261029357611126610644565b5f8051602061474c83398151915254906001600160401b0360ff8360401c161592168015908161121f575b6001149081611215575b15908161120c575b50610351575f8051602061474c833981519152805467ffffffffffffffff1916600117905561119690826111e857613445565b61119c57005b5f8051602061474c833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290806020810161034c565b5f8051602061474c833981519152805460ff60401b1916600160401b179055613445565b9050155f611163565b303b15915061115b565b839150611151565b34610293575f3660031901126102935760206040516101008152f35b34610293576020366003190112610293576004355f526005602052602060ff60405f2054166040519015158152f35b34610293575f36600319011261029357600f546040516001600160a01b039091168152602090f35b34610293576020366003190112610293576004356112b781610378565b6112c13633612d6b565b604051906112ce8261068e565b63ffffffff81168083521561134a577f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69161132a611311600a5463ffffffff1690565b9163ffffffff90511663ffffffff19600a541617600a55565b6040805163ffffffff92831681529290911660208301528190810161034c565b60405162461bcd60e51b815260206004820152601d60248201527f4f50503a20696e76616c696420726574656e74696f6e20636f6e6669670000006044820152606490fd5b34610293575f366003190112610293576020600e54604051908152f35b34610293575f366003190112610293576020600854604051908152f35b6004359061ffff8216820361029357565b359061ffff8216820361029357565b3461029357604080600319360112610293576114036113c9565b6024356001600160401b03811161029357611422903690600401610748565b9160045415611505576114353633612d6b565b5f8051602061470c8339815191525461146490611458906001600160a01b031681565b6001600160a01b031690565b81516368fc2b7760e11b815261ffff841662010000176004820152336024820152908290829060449082905afa908115611500575f916114d2575b50156114af5761084d8383613669565b51639a28d60760e01b815233600482015261ffff91909116602482015260449150fd5b6114f29150823d84116114f9575b6114ea81836106f2565b810190612870565b505f61149f565b503d6114e0565b612895565b5163738c481160e01b8152600490fd5b346102935760203660031901126102935761152e610644565b6115383633612d6b565b600f80546001600160a01b0319166001600160a01b0392909216919091179055005b919082604091031261029357604051611572816106a9565b8092803560ff811681036102935782526020908101359161159283610378565b0152565b91906080838203126102935760206115ca604051926115b4846106a9565b604084966115c2838261155a565b86520161155a565b910152565b35906001600160401b038216820361029357565b6001600160401b0381116106895760051b60200190565b9190916040908181850312610293578151611614816106a9565b8094823561162181610378565b8252602092838101356001600160401b039182821161029357019180601f8401121561029357823590611653826115e3565b96611660815198896106f2565b828852868089019360051b8601019482861161029357878101935b86851061168d57505050505050500152565b84358681116102935782019060609081601f198488030112610293578451916116b58361066e565b6116c08c85016113da565b8352858401356116cf81610378565b8c84015283013591888311610293576116ef878d80969581960101610748565b8682015281520194019361167b565b90604060206103919361172584825163ffffffff6020809260ff8151168552015116910152565b015191019063ffffffff6020809260ff8151168552015116910152565b9061042e91602081526117596020820183516116fe565b602082015190611777610140928360a08401526101608301906103ed565b9160c06117bd611798604087015195601f19968787830301858801526103ed565b606087015163ffffffff1660e0860152608087015186868303016101008701526103ed565b60a08601516001600160401b031661012085015294015192828503019101526103ed565b346102935760031960403682011261029357600480356001600160401b0392838211610293576101409082360301126102935761181c610713565b9061182936828501611596565b825260848101358481116102935761184690843691840101610748565b602083015260a48101358481116102935761186690843691840101610748565b604083015261187760c48201610386565b606083015260e48101358481116102935761189790843691840101610748565b60808301526118a961010482016115cf565b60a08301526101248101359084821161029357836118ca9236920101610748565b60c0820152602435928311610293576118ec6118f2926104ae943691016115fa565b906129e1565b60405191829182611742565b34610293575f366003190112610293576020600454604051908152f35b34610293575f3660031901126102935760206004541515604051908152f35b3461029357602080600319360112610293576004908135825415611c99576119623633612d6b565b8015611c885782541461197157005b5f82556003548015611c7757611985612191565b5f5480611c0b5750611995611f9e565b60409060408301526119c26119b26001600160401b034216612104565b6001600160401b031660a0840152565b6119ca6121ce565b600181529283916119da8261380b565b8684019081525f5b838110611b8f57505050506119f6916129e1565b91611a188180850151604051809381926347f5343160e01b8352898301612805565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91611b72575b505f55611a75611a5960a08501516001600160401b031690565b6001600160401b03166001600160401b03196001541617600155565b611a7d6121e7565b928352820152611a8c81612c9e565b917fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755c611b2457611abf83600e5461061b565b906120008211611aff575050611af691611adb610de2926139e5565b611aee611ae9600854612120565b600855565b600e5461061b565b61084d5f600355565b604080516306f8bcbb60e51b8152918201928352612000602084015290918291010390fd5b618000611b308461060b565b11611b465750611af691611adb610de2926139e5565b611b4f8361060b565b6040805163048c985b60e51b815292830191825261800060208301528291010390fd5b611b899150823d84116108925761088381836106f2565b5f611a3f565b60019192939450611bff81611bac611ba68261349f565b5061386f565b8a611bb9825161ffff1690565b910151611bee611bcd825163ffffffff1690565b611be2611bd8610720565b61ffff9095168552565b63ffffffff16838e0152565b87820152855190610e588383612258565b500190859392916119e2565b604051630c9b3d8760e21b8152858101918252905f9082908190602001038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91611c55575b50611995565b611c7191503d805f833e611c6981836106f2565b81019061295a565b5f611c4f565b60405163783bc0f160e11b81528390fd5b604051636ad43cc360e11b81528390fd5b60405163738c481160e01b81528390fd5b600754811015611ce357600a9060075f52027fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801905f90565b61055a565b90604051611cf5816106a9565b915460ff8116835260081c63ffffffff166020830152565b90600182811c92168015611d3b575b6020831014611d2757565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611d1c565b9060405191825f8254611d5781611d0d565b908184526020946001916001811690815f14611dc35750600114611d85575b505050610391925003836106f2565b5f90815285812095935091905b818310611dab57505061039193508201015f8080611d76565b85548884018501529485019487945091830191611d92565b9250505061039194925060ff191682840152151560051b8201015f8080611d76565b9060408051611df3816106a9565b809363ffffffff8082541683526001809201805491611e11836115e3565b95611e1f60405197886106f2565b83875260209160208801935f5260205f20915f945b868610611e4957505050505050505060200152565b60028589928451611e598161066e565b86885461ffff8116835260101c1683820152611e76858901611d45565b86820152815201940195019492611e34565b90604051611e95816106a9565b60206115ca60088395604051611eaa816106c4565b604051611eb6816106a9565b611ebf83611ce8565b8152611ecd60018401611ce8565b868201528152611edf60028301611d45565b85820152611eef60038301611d45565b6040820152611f15611f08600484015463ffffffff1690565b63ffffffff166060830152565b611f2160058301611d45565b6080820152611f4d611f3d60068401546001600160401b031690565b6001600160401b031660a0830152565b611f5960078301611d45565b60c0820152855201611de5565b5f806008545b808210611f7a575050600e55565b9091611f8b610e88610e8285611caa565b8101809111610606579160010190611f6c565b604051611faa8161068e565b5f8152905f368137565b60405190611fc1826106a9565b6001825260203681840137565b60405190611fdb826106a9565b600a825260203681840137565b90611ff28261072d565b611fff60405191826106f2565b8281528092612010601f199161072d565b0190602036910137565b1561202157565b60405162461bcd60e51b815260206004820152601a60248201527f4f50503a20696e76616c6964207072657761726d2072616e67650000000000006044820152606490fd5b610400811015611ce357601001905f90565b90600163ffffffff8093160191821161060657565b6040519061209a826106a9565b5f6020838281520152565b604051906120b2826106a9565b816120bb61208d565b815260206115ca61208d565b604051906120d4826106c4565b606060c0838281526120e46120a5565b60208201525f60408201525f838201525f60808201528260a08201520152565b906103e86001600160401b038093160291821691820361060657565b5f1981146106065760010190565b1561213557565b60405162461bcd60e51b815260206004820152602e60248201527f4f50503a20717565756564206d6573736167652065786365656473204d41585f60448201526d454e56454c4f50455f425954455360901b6064820152608490fd5b6040519061219e826106c4565b606060c0836121ab6120a5565b81528260208201528260408201525f838201528260808201525f60a08201520152565b604051906121db826106a9565b60606020835f81520152565b604051906121f4826106a9565b816121fd612191565b815260206115ca6121ce565b90612213826115e3565b61222060405191826106f2565b8281528092612231601f19916115e3565b01905f5b82811061224157505050565b60209061224c6121e7565b82828501015201612235565b8051821015611ce35760209160051b010190565b9190820391821161060657565b634e487b7160e01b5f525f60045260245ffd5b818110612297575050565b5f815560010161228c565b9190601f81116122b157505050565b610391925f5260205f20906020601f840160051c830193106122db575b601f0160051c019061228c565b90915081906122ce565b9190918281146123cc576122f98354611d0d565b6001600160401b0381116106895761231b816123158454611d0d565b846122a2565b5f93601f821160011461235a5761234b92939482915f9261234f575b50508160011b915f199060031b1c19161790565b9055565b015490505f80612337565b61236c601f198316915f5260205f2090565b9461237a845f5260205f2090565b915f5b8181106123b45750958360019596971061239c575b505050811b019055565b01545f1960f88460031b161c191690555f8080612392565b8783015484556001938401939092019160200161237d565b509050565b6123db8154611d0d565b90816123e5575050565b81601f5f93116001146123f6575055565b908083918252612415601f60208420940160051c84016001850161228c565b5555565b600160401b821161068957805482825580831061243557505050565b6001916001600160ff1b0382811683036106065784168403610606575f5260205f209060011b81019260011b015b82811061246f57505050565b805f600292556124808382016123d1565b01612463565b8181146125135781549161249a8383612419565b5f5260205f20905f5260205f20905f905b8382106124b85750505050565b8060019184036124d3575b60028091019301910190916124ab565b8054845461ffff191661ffff919091161784558054845465ffffffff0000191665ffffffff000090911617845561250e8183018584016122e5565b6124c3565b5050565b818103612522575050565b6001808361254a63ffffffff610391965416859063ffffffff1663ffffffff19825416179055565b019101612486565b906126bc5781811415918261256657505050565b60088091846103919561257d575b50019101612517565b8061262d575b5061259460028201600286016122e5565b6125a460038201600386016122e5565b6125d16125b8600483015463ffffffff1690565b600486019063ffffffff1663ffffffff19825416179055565b6125e160058201600586016122e5565b6126176125f860068301546001600160401b031690565b60068601906001600160401b03166001600160401b0319825416179055565b61262760078201600786016122e5565b5f612574565b61268a575b600184016001820190818103612649575b50612583565b8154815460ff191660ff919091161781556126839163ffffffff9054851c1664ffffffff0082549160081b169064ffffffff001916179055565b5f80612643565b8054845460ff191660ff919091161784558054845464ffffffff00191690831c60081b64ffffffff0016178455612632565b612279565b600754801561277f575f19016126d681611caa565b6126bc575f815560015f600183015560096002926126f6600282016123d1565b612702600382016123d1565b5f6004820155612714600582016123d1565b5f6006820155612726600782016123d1565b5f600882015501908154915f815582612743575b50505050600755565b6001600160ff1b0383168303610606575f5260205f209160011b8201915b8281101561273a57805f8592556127798382016123d1565b01612761565b634e487b7160e01b5f52603160045260245ffd5b1561279a57565b60405162461bcd60e51b815260206004820152603a60248201527f4f50503a20656e76656c6f70652065786365656473204d41585f454e56454c4f60448201527f50455f42595445532028657374696d61746f72206472696674290000000000006064820152608490fd5b90602061042e9281815201906103ed565b1561281d57565b60405162461bcd60e51b815260206004820152602560248201527f4f50503a207265636f7264207374696c6c20696e20726574656e74696f6e2077604482015264696e646f7760d81b6064820152608490fd5b91908260409103126102935760208251612889816109ca565b92015161042e81610378565b6040513d5f823e3d90fd5b90816020910312610293575190565b90602080835260609060608401908063ffffffff94858151168288015201519460408080830152865180945260808201908360808660051b8501019801955f935b8685106129035750505050505050505090565b909192939495969799988680612945600193868f89908f607f198a8603018d52519061ffff8251168552868201511686850152015191818a82015201906103ed565b9b9c9a019897969190910194019291906128f0565b602081830312610293578051906001600160401b038211610293570181601f820112156102935780519061298d8261072d565b9261299b60405194856106f2565b8284526020838301011161029357815f9260208093018386015e8301015290565b90916129d361042e936040845260408401906103ed565b9160208184039101526103ed565b906129ea612191565b506040805163765809ab60e11b81526004926020929173__$b91d2ad766fd07dc99f56c1a0b020615bb$__9190848180612a26858a83016128af565b0381865af4908115611500575f91612c81575b5060808701908582518051155f14612bfd5750508451630c9b3d8760e21b8152878101918252905f90829081906020010381875af492831561150057612aa693612a9993612a8f935f92612be1575b5052613701565b5163ffffffff1690565b63ffffffff166060870152565b815162409a0960e81b81525f8180612ac089898301611742565b0381855af4908115611500575f91612bc7575b508584018051805180612b8c57505052815163b93612a760e01b815293838580612aff89858301611742565b0381855af492831561150057612b38955f958695612b6d575b505051630c9b3d8760e21b81529081019283529384928391829160200190565b03915af4908115611500575f91612b53575b5060c082015290565b612b6791503d805f833e611c6981836106f2565b5f612b4a565b612b84929550803d106108925761088381836106f2565b925f80612b18565b915093925084849695929601209085519086012003612bac575050505090565b51630ca2e06d60e11b815292839261082a92919084016129bc565b612bdb91503d805f833e611c6981836106f2565b5f612ad3565b612bf69192503d805f833e611c6981836106f2565b905f612a88565b90919350612c1e92508551809381926347f5343160e01b83528a8301612805565b0381865af4908115611500575f91612c64575b50818103612c40575050612aa6565b8351631d97afef60e31b815280870192835260208301919091529081906040010390fd5b612c7b9150853d87116108925761088381836106f2565b5f612c31565b612c989150853d87116108925761088381836106f2565b5f612a39565b61010090602080910190602082510151515f925b818410612cc0575050505090565b90919293601880820191828111610606576040612ce1888887510151612258565b5101515101018091116106065793600101929190612cb2565b600411610293575f90600490565b6001600160e01b03199035818116939260048110612d2557505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f8051602061470c8339815191528054612da3906001600160a01b0316612d9a612d9486612cfa565b90612d08565b90843091613b47565b9015612db0575b50505050565b63ffffffff1615612e67575f8051602061470c833981519152805460ff60a01b1916600160a01b179055612df190611458908190546001600160a01b031690565b91823b1561029357612e1c925f9283604051809681958294634a63ebf760e11b845260048401612d34565b03925af1801561150057612e4e575b505f8051602061470c833981519152805460ff60a01b191690555f808080612daa565b80612e5b612e61926106df565b80610289565b5f612e2b565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f8051602061470c83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b600d5480612efa5750604051612ef48161068e565b5f815290565b60405190630c9b3d8760e21b825260048201525f8160248173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91612f3a575090565b61042e91503d805f833e611c6981836106f2565b805191908290602001825e015f815290565b612f68611020565b612f70613dd9565b92604080518095602094858301612f8691612f4e565b612f8f91612f4e565b0394601f19958681018252612fa490826106f2565b8451612faf90613c21565b908251918291868301612fc191612f4e565b612fca91612f4e565b038681018252612fda90826106f2565b612fe2613df0565b908251918291868301612ff491612f4e565b612ffd91612f4e565b03868101825261300d90826106f2565b8385015161301a90613c5d565b90815161302d906001600160401b031690565b61303690613f00565b90835191829187830161304891612f4e565b61305191612f4e565b03878101825261306190826106f2565b825191829186830161307291612f4e565b61307b91612f4e565b03868101825261308b90826106f2565b613093613e03565b9082519182918683016130a591612f4e565b6130ae91612f4e565b0386810182526130be90826106f2565b848201516001600160401b03166130d490613f00565b9082519182918683016130e691612f4e565b6130ef91612f4e565b0386810182526130ff90826106f2565b613107613e16565b90825191829186830161311991612f4e565b61312291612f4e565b03868101825261313290826106f2565b60608501516131469063ffffffff16613f00565b90825191829186830161315891612f4e565b61316191612f4e565b03868101825261317190826106f2565b613179613e29565b90825191829186830161318b91612f4e565b61319491612f4e565b0386810182526131a490826106f2565b60808501516131b89063ffffffff16613f00565b9082519182918683016131ca91612f4e565b6131d391612f4e565b0386810182526131e390826106f2565b6131eb613e3c565b9082519182918683016131fd91612f4e565b61320691612f4e565b03868101825261321690826106f2565b60a085015161322490613c21565b90825191829186830161323691612f4e565b61323f91612f4e565b03868101825261324f90826106f2565b915f925b60c086018051518510156133195761329961330f60c0936133036132b76132b18a6001978f6132ab908f61329f8f61329961328c613e81565b91519b8c94850190612f4e565b90612f4e565b039081018852876106f2565b51612258565b51613f7f565b6132f58d8c6132e98c6132996132dc6132d788516001600160401b031690565b613f00565b9151988994850190612f4e565b039081018552846106f2565b88519485938c850190612f4e565b038a81018352826106f2565b9401939050613253565b5095509350505050565b63ffffffff8082165f52600960205260405f20908154166133845761335a600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b6133a09063ffffffff91829182600a541690614649565b169081151590816133c9575b506133b45750565b5f5260096020525f6001604082208281550155565b9050815f52600960205260405f20541615155f6133ac565b805190601f8201908183116106065760059160051c915f5b83811061341c575050505063ffffffff610410911663ffffffff19825416179055565b80821b83016020015160108201556001016133f9565b61042e9063ffffffff600a541690614649565b61346e9061345161407f565b61345961407f565b61346161407f565b61346961407f565b612e87565b61347661407f565b61347e61407f565b60c860405161348c8161068e565b5260c863ffffffff19600a541617600a55565b600254811015611ce35760025f5260011b7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01905f90565b91909182516001600160401b038111610689576134f8816123158454611d0d565b602080601f83116001146135345750819061234b9394955f926135295750508160011b915f199060031b1c19161790565b015190505f80612337565b90601f19831695613548855f5260205f2090565b925f905b8882106135825750508360019596971061356a57505050811b019055565b01515f1960f88460031b161c191690555f8080612392565b8060018596829496860151815501950193019061354c565b9291906126bc578051835461ffff191661ffff91909116178355600180930190602080910151938451916001600160401b038311610689576135e6836135e08654611d0d565b866122a2565b602091601f8411600114613618575050819061234b9394955f926135295750508160011b915f199060031b1c19161790565b95601f9291921984169661362f865f5260205f2090565b935f915b898310613652575050508360019596971061356a57505050811b019055565b838501518655948501949381019391810191613633565b600254916003548084115f146136be575061039192506136976040519261368f846106a9565b61ffff168352565b60208201526136b86003546136b36136ae82612120565b600355565b61349f565b9061359a565b6136c790612120565b60035561ffff604051926136da846106a9565b1682526020820152600160401b821015610689576136b8826001610391940160025561349f565b6137349161370d611020565b90613716613ea1565b61329961377a60409260405197889161329960209889850190612f4e565b0396613748601f19988981018352826106f2565b61376e61375f6132d76104548a5163ffffffff1690565b60405194859389850190612f4e565b038781018352826106f2565b915f925b8486018051518510156133195761329961380161329961330389956137f38d6137cd6137c78d60019a6132ab8f6137bb8f9161329961328c613df0565b038681018952886106f2565b516140ad565b926132e96137e56132d786516001600160401b031690565b8d519788938d850190612f4e565b885194859388850190612f4e565b940193905061377e565b90613815826115e3565b60409061382560405191826106f2565b8381528093613836601f19916115e3565b01915f5b8381106138475750505050565b60209082516138558161066e565b5f8152825f8183015260608583015282860101520161383a565b9060405161387c816106a9565b60206115ca6001839561ffff815416855201611d45565b8151815463ffffffff191663ffffffff91821617825591600180920160208092015191808351936138c48585612419565b01915f52805f205f925b8484106138de5750505050505050565b8051805183548583015161ffff90921665ffffffffffff199091161790891660101b65ffffffff0000161783556040015180519187840191906001600160401b03841161068957828692613937866123158d9754611d0d565b8390601f871160011461397757958061396892600297985f926135295750508160011b915f199060031b1c19161790565b90555b019201930192906138ce565b9590601f1982169661398c845f5260205f2090565b975f5b8181106139cc575091879891846002999594106139b4575b505050811b01905561396b565b01515f1960f88460031b161c191690555f80806139a7565b838301518a5597909801978d978b97938401930161398f565b600754600160401b81101561068957806001613a049201600755611caa565b6126bc576008602083613b3e60c06103919651613a9e815186613a608251613a3a60ff8251168d9060ff1660ff19825416179055565b909101518a5464ffffffff00191660089190911b64ffffffff0016178a5563ffffffff90565b878060018c01930151613a8160ff825116859060ff1660ff19825416179055565b0151825464ffffffff001916911660081b64ffffffff0016179055565b613aae85820151600289016134d7565b613abf6040820151600389016134d7565b613aec613ad3606083015163ffffffff1690565b600489019063ffffffff1663ffffffff19825416179055565b613afd6080820151600589016134d7565b613b33613b1460a08301516001600160401b031690565b60068901906001600160401b03166001600160401b0319825416179055565b0151600786016134d7565b01519101613893565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b03841117610689576040935f9385528380528360205251915afa613bb657565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613c06575b505061099c57565b5f8051602061472c8339815191525416141590505f80613bfe565b61042e61329991613c4f613c3e6001600160401b03835116613f00565b916040519485936020850190612f4e565b03601f1981018352826106f2565b613c65611020565b90613c6e613dd9565b6040805193846020938492838301613c8591612f4e565b613c8e91612f4e565b0393601f19948581018752613ca390876106f2565b8051613cae90614257565b958651613cc1906001600160401b031690565b613cca90613f00565b908451918291858301613cdc91612f4e565b613ce591612f4e565b038681018252613cf590826106f2565b8351968791848301613d0691612f4e565b613d0f91612f4e565b038581018752613d1f90876106f2565b613d27613df0565b958351968791848301613d3991612f4e565b613d4291612f4e565b038581018752613d5290876106f2565b0151613d5d90614257565b908151613d70906001600160401b031690565b613d7990613f00565b948151958691858301613d8b91612f4e565b613d9491612f4e565b038481018652613da490866106f2565b519384928301613db391612f4e565b613dbc91612f4e565b03908101825261042e90826106f2565b805115611ce35760200190565b613de1611fb4565b600a613dec82613dcc565b5390565b613df8611fb4565b6012613dec82613dcc565b613e0b611fb4565b6028613dec82613dcc565b613e1e611fb4565b6030613dec82613dcc565b613e31611fb4565b6038613dec82613dcc565b613e44611fce565b60a25f5b607f80831115613e6d5790608060019284161760208286010153019060071c90613e48565b50607f600192166020828501015301815290565b613e89611fce565b61014260015f60c26020850153019060071c90613e48565b613ea9611fb4565b6008613dec82613dcc565b613ebc611fb4565b6010613dec82613dcc565b613ecf611fb4565b601a613dec82613dcc565b613ee2611fb4565b6032613dec82613dcc565b613ef5611fb4565b6042613dec82613dcc565b9060809160806001600160401b03821610613f5b57613f1d611fce565b5f5b607f80841115613f4357908560019285161760208285010153019160071c91613f1f565b5090929350607f600192166020828501015301815290565b909150613f66611fb4565b9060f81b6001600160f81b0319165f1a613dec82613dcc565b613f87611020565b90613f90613dd9565b6040805193846020938492838301613fa791612f4e565b613fb091612f4e565b0393601f19948581018752613fc590876106f2565b8051613fd09061430f565b958651613fe3906001600160401b031690565b613fec90613f00565b908451918291858301613ffe91612f4e565b61400791612f4e565b03868101825261401790826106f2565b835196879184830161402891612f4e565b61403191612f4e565b03858101875261404190876106f2565b614049613df0565b95835196879184830161405b91612f4e565b61406491612f4e565b03858101875261407490876106f2565b0151613d5d90613701565b60ff5f8051602061474c8339815191525460401c161561409b57565b604051631afcd79f60e31b8152600490fd5b61042e6140ee613299926141a66141926132996141996140cb611020565b946140d4613ea1565b9060409485928351998a9161329960209b8c850190612f4e565b0398614102601f199a8b81018352826106f2565b614141896141356141256132d761411e61411e895161ffff1690565b61ffff1690565b6132998851958694850190612f4e565b038b81018352826106f2565b61415089614135614125613eb4565b61416d896141356141256132d76104548489015163ffffffff1690565b614186614178613ec7565b85519d8e938c850190612f4e565b038981018c528b6106f2565b0151613c21565b9151968794850190612f4e565b039081018352826106f2565b90813b15614236575f8051602061472c83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561421b5761421891614668565b50565b50503461422457565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b61042e6132996132996141a66142ff6132d76104546020613299986142f46132996142ce614283611020565b61429d61428e613ea1565b6040519d8e9389850190612f4e565b039a6142b1601f199c8d81018352826106f2565b61413561375f6132d76142c86142c8895160ff1690565b60ff1690565b6142e86142d9613eb4565b6040519a8b9387850190612f4e565b038981018952886106f2565b015163ffffffff1690565b6040519586936020850190612f4e565b614317611020565b90614320613dd9565b9160408051809460209384830161433691612f4e565b61433f91612f4e565b0392601f1993848101865261435490866106f2565b805161435f90613c5d565b948551614372906001600160401b031690565b61437b90613f00565b90835191829186830161438d91612f4e565b61439691612f4e565b0385810182526143a690826106f2565b82519586918583016143b791612f4e565b6143c091612f4e565b0384810186526143d090866106f2565b6143d8613df0565b9482519586918583016143ea91612f4e565b6143f391612f4e565b03848101865261440390866106f2565b8281015161441090613c21565b94825195869185830161442291612f4e565b61442b91612f4e565b03848101865261443b90866106f2565b614443613ec7565b94825195869185830161445591612f4e565b61445e91612f4e565b03848101865261446e90866106f2565b8181015161447b90613c21565b94825195869185830161448d91612f4e565b61449691612f4e565b0384810186526144a690866106f2565b6144ae613e03565b9482519586918583016144c091612f4e565b6144c991612f4e565b0384810186526144d990866106f2565b60608101516144ed9063ffffffff16613f00565b9482519586918583016144ff91612f4e565b61450891612f4e565b03848101865261451890866106f2565b614520613eda565b94825195869185830161453291612f4e565b61453b91612f4e565b03848101865261454b90866106f2565b608081015161455990613c21565b94825195869185830161456b91612f4e565b61457491612f4e565b03848101865261458490866106f2565b61458c613e29565b94825195869185830161459e91612f4e565b6145a791612f4e565b0384810186526145b790866106f2565b60a08101516001600160401b03166145ce90613f00565b9482519586918583016145e091612f4e565b6145e991612f4e565b0384810186526145f990866106f2565b614601613eed565b94825195869185830161461391612f4e565b61461c91612f4e565b03848101865261462c90866106f2565b60c0015161463990613c21565b90519384928301613db391612f4e565b63ffffffff9190828216838216111561466157031690565b5050505f90565b5f8061042e93602081519101845af43d156146a4573d916146888361072d565b9261469660405194856106f2565b83523d5f602085013e6146a8565b6060915b906146cf57508051156146bd57602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614702575b6146e0575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156146d856fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220f5b6d4a44ad194936330b5fb973c2a2aea0de6de3f7d1ea8e8573cef058ba4d864736f6c63430008190033", - "deployedBytecode": "0x60806040526004361015610011575f80fd5b5f3560e01c806302ddde7b1461028457806304676e381461027f5780630d6f1e9d1461027a57806316a91b741461027557806327e935a11461027057806347453a2a1461026b5780634b3254fb146102665780634ed891d0146102615780634f1ef2861461025c5780634fca79cb1461025757806352d1902d14610252578063621fbd591461024d5780636744c79f146102485780637a9e5e4b146102435780638cbc18391461023e5780638fb360371461023957806393ec06be1461023457806395fabcf01461022f578063a3ad9cc31461022a578063a593aec214610225578063a9e50c1014610220578063ad3cb1cc1461021b578063b632ab5714610216578063b8f8d7e514610211578063bf7e214f1461020c578063c16a8a5614610207578063c4d66de814610202578063c8c14d17146101fd578063c94c93e7146101f8578063cde78421146101f3578063d5b5272a146101ee578063de152649146101e9578063e0064900146101e4578063e0c14aa8146101df578063e0c1c0b4146101da578063e2fc04ae146101d5578063e36f9a07146101d0578063e44efb5a146101cb5763f5786b4b146101c6575f80fd5b61193a565b61191b565b6118fe565b6117e1565b611515565b6113e9565b6113ac565b61138f565b61129a565b611272565b611243565b611227565b61110d565b6110ea565b6110b6565b611093565b611077565b611032565b610ffa565b610f00565b610c12565b610bf5565b610bd9565b610b89565b610ac2565b610a48565b6109d4565b6109ae565b610945565b610899565b61078e565b610628565b6105c8565b61056e565b610511565b6104ed565b610431565b610393565b610297565b5f91031261029357565b5f80fd5b34610293575f366003190112610293575f8051602061474c833981519152805460ff8160401c16908115610363575b50610351575f8051602061474c833981519152805467ffffffffffffffff19166002179055805460ff60401b1916600160401b179055610304611f66565b5f8051602061474c833981519152805460ff60401b19169055604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b60405163f92ee8a960e01b8152600490fd5b600291506001600160401b031610155f6102c6565b63ffffffff81160361029357565b359061039182610378565b565b346102935760203660031901126102935760606004356103b281610378565b63ffffffff8091165f52600960205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60409063ffffffff61042e949316815281602082015201906103ed565b90565b34610293575f3660031901126102935761045d6104546104105463ffffffff1690565b63ffffffff1690565b61046681611fe8565b90610479610473826105f8565b60051c90565b5f5b8181106104d5575050601f811690816104b2575b8261049f600c5463ffffffff1690565b6104ae60405192839283610411565b0390f35b81602091038301019060015f199160200360031b1b011981511690525f8061048f565b806001916010015460208260051b870101520161047b565b34610293575f36600319011261029357602063ffffffff6104105416604051908152f35b346102935760203660031901126102935760043561052f3633612d6b565b8015610548576004541561053f57005b6004555f600355005b604051636ad43cc360e11b8152600490fd5b634e487b7160e01b5f52603260045260245ffd5b34610293576020366003190112610293576004356006548110156102935760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01546040516001600160a01b039091168152602090f35b34610293575f3660031901126102935760206040516104008152f35b634e487b7160e01b5f52601160045260245ffd5b90601f820180921161060657565b6105e4565b9061010091820180921161060657565b9190820180921161060657565b34610293575f3660031901126102935760206040516101188152f35b600435906001600160a01b038216820361029357565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761068957604052565b61065a565b602081019081106001600160401b0382111761068957604052565b604081019081106001600160401b0382111761068957604052565b60e081019081106001600160401b0382111761068957604052565b6001600160401b03811161068957604052565b90601f801991011681019081106001600160401b0382111761068957604052565b60405190610391826106c4565b604051906103918261066e565b6001600160401b03811161068957601f01601f191660200190565b81601f820112156102935780359061075f8261072d565b9261076d60405194856106f2565b8284526020838301011161029357815f926020809301838601378301015290565b6040366003190112610293576107a2610644565b6024356001600160401b038111610293576107c1903690600401610748565b906107ca613bc8565b6107d43633612d6b565b6107dc613bc8565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610868575b5061082e57604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f8051602061472c833981519152830361084f5761084d92506141b2565b005b604051632a87526960e21b815260048101849052602490fd5b61088b91945060203d602011610892575b61088381836106f2565b8101906128a0565b925f610804565b503d610879565b34610293576020366003190112610293576104ae6004356108b981610378565b60405f8180516108c88161066e565b828152826020820152015263ffffffff8092165f5260096020526001815f206001600160401b038351946108fb8661066e565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610293575f366003190112610293577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316300361099c5760206040515f8051602061472c8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610293575f3660031901126102935760205f54604051908152f35b8015150361029357565b34610293576020366003190112610293576004356109f1816109ca565b600f546001600160a01b031633819003610a2a57507fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755d005b60449060405190636e01d1ef60e11b82523360048301526024820152fd5b3461029357602036600319011261029357610a61610644565b5f8051602061470c833981519152546001600160a01b03919082163303610aab57803b15610a925761084d90612e87565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b3461029357604036600319011261029357602435600435610ae33633612d6b565b63ffffffff6104105416610b445781811080610b38575b610b039061201a565b818110610b0c57005b80610b32610b1b600193612066565b81549060031b906001821b915f19901b1916179055565b01610b03565b50610400821115610afa565b60405162461bcd60e51b815260206004820152601f60248201527f4f50503a206f7574626f756e642062616e6b20616c7265616479206c697665006044820152606490fd5b34610293575f366003190112610293575f8051602061470c8339815191525460a01c60ff1615610bd1576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610bbe565b34610293575f3660031901126102935760206040516180008152f35b34610293575f366003190112610293576020600354604051908152f35b3461029357602036600319011261029357600435610c2f81610378565b610c393633612d6b565b600c5463ffffffff16610c4e61045482612078565b63ffffffff831603610ed15750610c636120c7565b63ffffffff82166060820152610c94610c846001600160401b034216612104565b6001600160401b03166040830152565b610c9c612edf565b60a08201525f600854908115159182610d3e575b7fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d88761034c86610d32610ce188612f60565b91610cf161800084511115612793565b610d1383516020850120610d058184613323565b610d0e83613389565b600d55565b610d1c836133e1565b63ffffffff1663ffffffff19600c541617600c55565b60405191829182612805565b5f92836101005b838210610e70575b5050610d639083159081610e68575b501561212e565b81610d6d81612209565b60c086019081525f5b828110610e30575050610d889161226c565b905f5b828110610e0257505f5b818110610df1575050610ce161034c93610de7610de27fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d8879795610dda610d3296600855565b600e5461226c565b600e55565b9350829450610cb0565b600190610dfc6126c1565b01610d95565b80610e2a610e1a610e156001948661061b565b611caa565b50610e2483611caa565b90612552565b01610d8b565b6001919250610e5e81610e4281611caa565b50610e4e865191611e88565b610e588383612258565b52612258565b5001908391610d76565b90505f610d5c565b610e8d610e88610e8284979894611caa565b50611e88565b612c9e565b90618000610e9b838361061b565b11610ec757610eb982610eb3600194610ebf9461061b565b9461061b565b96612120565b940190610d45565b5081959450610d4d565b610eda90612078565b6040516307d3e83d60e51b815263ffffffff918216600482015291166024820152604490fd5b3461029357602036600319011261029357600435610f1d81610378565b610f273633612d6b565b63ffffffff9081811691825f52600960205260405f20541615610fb557610f84610f8f91610f70610f68610454610f63600c5463ffffffff1690565b613432565b851115612816565b63ffffffff165f52600960205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b60405162461bcd60e51b815260206004820152601760248201527f4f50503a207265636f7264206e6f742070726573656e740000000000000000006044820152606490fd5b34610293575f3660031901126102935760206001600160401b0360015416604051908152f35b6040519061102d8261068e565b5f8252565b34610293575f366003190112610293576104ae604051611051816106a9565b60058152640352e302e360dc1b60208201526040519182916020835260208301906103ed565b34610293575f3660031901126102935760206040516120008152f35b34610293575f36600319011261029357602063ffffffff600c5416604051908152f35b34610293575f366003190112610293575f8051602061470c833981519152546040516001600160a01b039091168152602090f35b34610293575f36600319011261029357602063ffffffff600a5416604051908152f35b3461029357602036600319011261029357611126610644565b5f8051602061474c83398151915254906001600160401b0360ff8360401c161592168015908161121f575b6001149081611215575b15908161120c575b50610351575f8051602061474c833981519152805467ffffffffffffffff1916600117905561119690826111e857613445565b61119c57005b5f8051602061474c833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290806020810161034c565b5f8051602061474c833981519152805460ff60401b1916600160401b179055613445565b9050155f611163565b303b15915061115b565b839150611151565b34610293575f3660031901126102935760206040516101008152f35b34610293576020366003190112610293576004355f526005602052602060ff60405f2054166040519015158152f35b34610293575f36600319011261029357600f546040516001600160a01b039091168152602090f35b34610293576020366003190112610293576004356112b781610378565b6112c13633612d6b565b604051906112ce8261068e565b63ffffffff81168083521561134a577f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69161132a611311600a5463ffffffff1690565b9163ffffffff90511663ffffffff19600a541617600a55565b6040805163ffffffff92831681529290911660208301528190810161034c565b60405162461bcd60e51b815260206004820152601d60248201527f4f50503a20696e76616c696420726574656e74696f6e20636f6e6669670000006044820152606490fd5b34610293575f366003190112610293576020600e54604051908152f35b34610293575f366003190112610293576020600854604051908152f35b6004359061ffff8216820361029357565b359061ffff8216820361029357565b3461029357604080600319360112610293576114036113c9565b6024356001600160401b03811161029357611422903690600401610748565b9160045415611505576114353633612d6b565b5f8051602061470c8339815191525461146490611458906001600160a01b031681565b6001600160a01b031690565b81516368fc2b7760e11b815261ffff841662010000176004820152336024820152908290829060449082905afa908115611500575f916114d2575b50156114af5761084d8383613669565b51639a28d60760e01b815233600482015261ffff91909116602482015260449150fd5b6114f29150823d84116114f9575b6114ea81836106f2565b810190612870565b505f61149f565b503d6114e0565b612895565b5163738c481160e01b8152600490fd5b346102935760203660031901126102935761152e610644565b6115383633612d6b565b600f80546001600160a01b0319166001600160a01b0392909216919091179055005b919082604091031261029357604051611572816106a9565b8092803560ff811681036102935782526020908101359161159283610378565b0152565b91906080838203126102935760206115ca604051926115b4846106a9565b604084966115c2838261155a565b86520161155a565b910152565b35906001600160401b038216820361029357565b6001600160401b0381116106895760051b60200190565b9190916040908181850312610293578151611614816106a9565b8094823561162181610378565b8252602092838101356001600160401b039182821161029357019180601f8401121561029357823590611653826115e3565b96611660815198896106f2565b828852868089019360051b8601019482861161029357878101935b86851061168d57505050505050500152565b84358681116102935782019060609081601f198488030112610293578451916116b58361066e565b6116c08c85016113da565b8352858401356116cf81610378565b8c84015283013591888311610293576116ef878d80969581960101610748565b8682015281520194019361167b565b90604060206103919361172584825163ffffffff6020809260ff8151168552015116910152565b015191019063ffffffff6020809260ff8151168552015116910152565b9061042e91602081526117596020820183516116fe565b602082015190611777610140928360a08401526101608301906103ed565b9160c06117bd611798604087015195601f19968787830301858801526103ed565b606087015163ffffffff1660e0860152608087015186868303016101008701526103ed565b60a08601516001600160401b031661012085015294015192828503019101526103ed565b346102935760031960403682011261029357600480356001600160401b0392838211610293576101409082360301126102935761181c610713565b9061182936828501611596565b825260848101358481116102935761184690843691840101610748565b602083015260a48101358481116102935761186690843691840101610748565b604083015261187760c48201610386565b606083015260e48101358481116102935761189790843691840101610748565b60808301526118a961010482016115cf565b60a08301526101248101359084821161029357836118ca9236920101610748565b60c0820152602435928311610293576118ec6118f2926104ae943691016115fa565b906129e1565b60405191829182611742565b34610293575f366003190112610293576020600454604051908152f35b34610293575f3660031901126102935760206004541515604051908152f35b3461029357602080600319360112610293576004908135825415611c99576119623633612d6b565b8015611c885782541461197157005b5f82556003548015611c7757611985612191565b5f5480611c0b5750611995611f9e565b60409060408301526119c26119b26001600160401b034216612104565b6001600160401b031660a0840152565b6119ca6121ce565b600181529283916119da8261380b565b8684019081525f5b838110611b8f57505050506119f6916129e1565b91611a188180850151604051809381926347f5343160e01b8352898301612805565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91611b72575b505f55611a75611a5960a08501516001600160401b031690565b6001600160401b03166001600160401b03196001541617600155565b611a7d6121e7565b928352820152611a8c81612c9e565b917fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755c611b2457611abf83600e5461061b565b906120008211611aff575050611af691611adb610de2926139e5565b611aee611ae9600854612120565b600855565b600e5461061b565b61084d5f600355565b604080516306f8bcbb60e51b8152918201928352612000602084015290918291010390fd5b618000611b308461060b565b11611b465750611af691611adb610de2926139e5565b611b4f8361060b565b6040805163048c985b60e51b815292830191825261800060208301528291010390fd5b611b899150823d84116108925761088381836106f2565b5f611a3f565b60019192939450611bff81611bac611ba68261349f565b5061386f565b8a611bb9825161ffff1690565b910151611bee611bcd825163ffffffff1690565b611be2611bd8610720565b61ffff9095168552565b63ffffffff16838e0152565b87820152855190610e588383612258565b500190859392916119e2565b604051630c9b3d8760e21b8152858101918252905f9082908190602001038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91611c55575b50611995565b611c7191503d805f833e611c6981836106f2565b81019061295a565b5f611c4f565b60405163783bc0f160e11b81528390fd5b604051636ad43cc360e11b81528390fd5b60405163738c481160e01b81528390fd5b600754811015611ce357600a9060075f52027fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801905f90565b61055a565b90604051611cf5816106a9565b915460ff8116835260081c63ffffffff166020830152565b90600182811c92168015611d3b575b6020831014611d2757565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611d1c565b9060405191825f8254611d5781611d0d565b908184526020946001916001811690815f14611dc35750600114611d85575b505050610391925003836106f2565b5f90815285812095935091905b818310611dab57505061039193508201015f8080611d76565b85548884018501529485019487945091830191611d92565b9250505061039194925060ff191682840152151560051b8201015f8080611d76565b9060408051611df3816106a9565b809363ffffffff8082541683526001809201805491611e11836115e3565b95611e1f60405197886106f2565b83875260209160208801935f5260205f20915f945b868610611e4957505050505050505060200152565b60028589928451611e598161066e565b86885461ffff8116835260101c1683820152611e76858901611d45565b86820152815201940195019492611e34565b90604051611e95816106a9565b60206115ca60088395604051611eaa816106c4565b604051611eb6816106a9565b611ebf83611ce8565b8152611ecd60018401611ce8565b868201528152611edf60028301611d45565b85820152611eef60038301611d45565b6040820152611f15611f08600484015463ffffffff1690565b63ffffffff166060830152565b611f2160058301611d45565b6080820152611f4d611f3d60068401546001600160401b031690565b6001600160401b031660a0830152565b611f5960078301611d45565b60c0820152855201611de5565b5f806008545b808210611f7a575050600e55565b9091611f8b610e88610e8285611caa565b8101809111610606579160010190611f6c565b604051611faa8161068e565b5f8152905f368137565b60405190611fc1826106a9565b6001825260203681840137565b60405190611fdb826106a9565b600a825260203681840137565b90611ff28261072d565b611fff60405191826106f2565b8281528092612010601f199161072d565b0190602036910137565b1561202157565b60405162461bcd60e51b815260206004820152601a60248201527f4f50503a20696e76616c6964207072657761726d2072616e67650000000000006044820152606490fd5b610400811015611ce357601001905f90565b90600163ffffffff8093160191821161060657565b6040519061209a826106a9565b5f6020838281520152565b604051906120b2826106a9565b816120bb61208d565b815260206115ca61208d565b604051906120d4826106c4565b606060c0838281526120e46120a5565b60208201525f60408201525f838201525f60808201528260a08201520152565b906103e86001600160401b038093160291821691820361060657565b5f1981146106065760010190565b1561213557565b60405162461bcd60e51b815260206004820152602e60248201527f4f50503a20717565756564206d6573736167652065786365656473204d41585f60448201526d454e56454c4f50455f425954455360901b6064820152608490fd5b6040519061219e826106c4565b606060c0836121ab6120a5565b81528260208201528260408201525f838201528260808201525f60a08201520152565b604051906121db826106a9565b60606020835f81520152565b604051906121f4826106a9565b816121fd612191565b815260206115ca6121ce565b90612213826115e3565b61222060405191826106f2565b8281528092612231601f19916115e3565b01905f5b82811061224157505050565b60209061224c6121e7565b82828501015201612235565b8051821015611ce35760209160051b010190565b9190820391821161060657565b634e487b7160e01b5f525f60045260245ffd5b818110612297575050565b5f815560010161228c565b9190601f81116122b157505050565b610391925f5260205f20906020601f840160051c830193106122db575b601f0160051c019061228c565b90915081906122ce565b9190918281146123cc576122f98354611d0d565b6001600160401b0381116106895761231b816123158454611d0d565b846122a2565b5f93601f821160011461235a5761234b92939482915f9261234f575b50508160011b915f199060031b1c19161790565b9055565b015490505f80612337565b61236c601f198316915f5260205f2090565b9461237a845f5260205f2090565b915f5b8181106123b45750958360019596971061239c575b505050811b019055565b01545f1960f88460031b161c191690555f8080612392565b8783015484556001938401939092019160200161237d565b509050565b6123db8154611d0d565b90816123e5575050565b81601f5f93116001146123f6575055565b908083918252612415601f60208420940160051c84016001850161228c565b5555565b600160401b821161068957805482825580831061243557505050565b6001916001600160ff1b0382811683036106065784168403610606575f5260205f209060011b81019260011b015b82811061246f57505050565b805f600292556124808382016123d1565b01612463565b8181146125135781549161249a8383612419565b5f5260205f20905f5260205f20905f905b8382106124b85750505050565b8060019184036124d3575b60028091019301910190916124ab565b8054845461ffff191661ffff919091161784558054845465ffffffff0000191665ffffffff000090911617845561250e8183018584016122e5565b6124c3565b5050565b818103612522575050565b6001808361254a63ffffffff610391965416859063ffffffff1663ffffffff19825416179055565b019101612486565b906126bc5781811415918261256657505050565b60088091846103919561257d575b50019101612517565b8061262d575b5061259460028201600286016122e5565b6125a460038201600386016122e5565b6125d16125b8600483015463ffffffff1690565b600486019063ffffffff1663ffffffff19825416179055565b6125e160058201600586016122e5565b6126176125f860068301546001600160401b031690565b60068601906001600160401b03166001600160401b0319825416179055565b61262760078201600786016122e5565b5f612574565b61268a575b600184016001820190818103612649575b50612583565b8154815460ff191660ff919091161781556126839163ffffffff9054851c1664ffffffff0082549160081b169064ffffffff001916179055565b5f80612643565b8054845460ff191660ff919091161784558054845464ffffffff00191690831c60081b64ffffffff0016178455612632565b612279565b600754801561277f575f19016126d681611caa565b6126bc575f815560015f600183015560096002926126f6600282016123d1565b612702600382016123d1565b5f6004820155612714600582016123d1565b5f6006820155612726600782016123d1565b5f600882015501908154915f815582612743575b50505050600755565b6001600160ff1b0383168303610606575f5260205f209160011b8201915b8281101561273a57805f8592556127798382016123d1565b01612761565b634e487b7160e01b5f52603160045260245ffd5b1561279a57565b60405162461bcd60e51b815260206004820152603a60248201527f4f50503a20656e76656c6f70652065786365656473204d41585f454e56454c4f60448201527f50455f42595445532028657374696d61746f72206472696674290000000000006064820152608490fd5b90602061042e9281815201906103ed565b1561281d57565b60405162461bcd60e51b815260206004820152602560248201527f4f50503a207265636f7264207374696c6c20696e20726574656e74696f6e2077604482015264696e646f7760d81b6064820152608490fd5b91908260409103126102935760208251612889816109ca565b92015161042e81610378565b6040513d5f823e3d90fd5b90816020910312610293575190565b90602080835260609060608401908063ffffffff94858151168288015201519460408080830152865180945260808201908360808660051b8501019801955f935b8685106129035750505050505050505090565b909192939495969799988680612945600193868f89908f607f198a8603018d52519061ffff8251168552868201511686850152015191818a82015201906103ed565b9b9c9a019897969190910194019291906128f0565b602081830312610293578051906001600160401b038211610293570181601f820112156102935780519061298d8261072d565b9261299b60405194856106f2565b8284526020838301011161029357815f9260208093018386015e8301015290565b90916129d361042e936040845260408401906103ed565b9160208184039101526103ed565b906129ea612191565b506040805163765809ab60e11b81526004926020929173__$b91d2ad766fd07dc99f56c1a0b020615bb$__9190848180612a26858a83016128af565b0381865af4908115611500575f91612c81575b5060808701908582518051155f14612bfd5750508451630c9b3d8760e21b8152878101918252905f90829081906020010381875af492831561150057612aa693612a9993612a8f935f92612be1575b5052613701565b5163ffffffff1690565b63ffffffff166060870152565b815162409a0960e81b81525f8180612ac089898301611742565b0381855af4908115611500575f91612bc7575b508584018051805180612b8c57505052815163b93612a760e01b815293838580612aff89858301611742565b0381855af492831561150057612b38955f958695612b6d575b505051630c9b3d8760e21b81529081019283529384928391829160200190565b03915af4908115611500575f91612b53575b5060c082015290565b612b6791503d805f833e611c6981836106f2565b5f612b4a565b612b84929550803d106108925761088381836106f2565b925f80612b18565b915093925084849695929601209085519086012003612bac575050505090565b51630ca2e06d60e11b815292839261082a92919084016129bc565b612bdb91503d805f833e611c6981836106f2565b5f612ad3565b612bf69192503d805f833e611c6981836106f2565b905f612a88565b90919350612c1e92508551809381926347f5343160e01b83528a8301612805565b0381865af4908115611500575f91612c64575b50818103612c40575050612aa6565b8351631d97afef60e31b815280870192835260208301919091529081906040010390fd5b612c7b9150853d87116108925761088381836106f2565b5f612c31565b612c989150853d87116108925761088381836106f2565b5f612a39565b61010090602080910190602082510151515f925b818410612cc0575050505090565b90919293601880820191828111610606576040612ce1888887510151612258565b5101515101018091116106065793600101929190612cb2565b600411610293575f90600490565b6001600160e01b03199035818116939260048110612d2557505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f8051602061470c8339815191528054612da3906001600160a01b0316612d9a612d9486612cfa565b90612d08565b90843091613b47565b9015612db0575b50505050565b63ffffffff1615612e67575f8051602061470c833981519152805460ff60a01b1916600160a01b179055612df190611458908190546001600160a01b031690565b91823b1561029357612e1c925f9283604051809681958294634a63ebf760e11b845260048401612d34565b03925af1801561150057612e4e575b505f8051602061470c833981519152805460ff60a01b191690555f808080612daa565b80612e5b612e61926106df565b80610289565b5f612e2b565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f8051602061470c83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b600d5480612efa5750604051612ef48161068e565b5f815290565b60405190630c9b3d8760e21b825260048201525f8160248173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4908115611500575f91612f3a575090565b61042e91503d805f833e611c6981836106f2565b805191908290602001825e015f815290565b612f68611020565b612f70613dd9565b92604080518095602094858301612f8691612f4e565b612f8f91612f4e565b0394601f19958681018252612fa490826106f2565b8451612faf90613c21565b908251918291868301612fc191612f4e565b612fca91612f4e565b038681018252612fda90826106f2565b612fe2613df0565b908251918291868301612ff491612f4e565b612ffd91612f4e565b03868101825261300d90826106f2565b8385015161301a90613c5d565b90815161302d906001600160401b031690565b61303690613f00565b90835191829187830161304891612f4e565b61305191612f4e565b03878101825261306190826106f2565b825191829186830161307291612f4e565b61307b91612f4e565b03868101825261308b90826106f2565b613093613e03565b9082519182918683016130a591612f4e565b6130ae91612f4e565b0386810182526130be90826106f2565b848201516001600160401b03166130d490613f00565b9082519182918683016130e691612f4e565b6130ef91612f4e565b0386810182526130ff90826106f2565b613107613e16565b90825191829186830161311991612f4e565b61312291612f4e565b03868101825261313290826106f2565b60608501516131469063ffffffff16613f00565b90825191829186830161315891612f4e565b61316191612f4e565b03868101825261317190826106f2565b613179613e29565b90825191829186830161318b91612f4e565b61319491612f4e565b0386810182526131a490826106f2565b60808501516131b89063ffffffff16613f00565b9082519182918683016131ca91612f4e565b6131d391612f4e565b0386810182526131e390826106f2565b6131eb613e3c565b9082519182918683016131fd91612f4e565b61320691612f4e565b03868101825261321690826106f2565b60a085015161322490613c21565b90825191829186830161323691612f4e565b61323f91612f4e565b03868101825261324f90826106f2565b915f925b60c086018051518510156133195761329961330f60c0936133036132b76132b18a6001978f6132ab908f61329f8f61329961328c613e81565b91519b8c94850190612f4e565b90612f4e565b039081018852876106f2565b51612258565b51613f7f565b6132f58d8c6132e98c6132996132dc6132d788516001600160401b031690565b613f00565b9151988994850190612f4e565b039081018552846106f2565b88519485938c850190612f4e565b038a81018352826106f2565b9401939050613253565b5095509350505050565b63ffffffff8082165f52600960205260405f20908154166133845761335a600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b6133a09063ffffffff91829182600a541690614649565b169081151590816133c9575b506133b45750565b5f5260096020525f6001604082208281550155565b9050815f52600960205260405f20541615155f6133ac565b805190601f8201908183116106065760059160051c915f5b83811061341c575050505063ffffffff610410911663ffffffff19825416179055565b80821b83016020015160108201556001016133f9565b61042e9063ffffffff600a541690614649565b61346e9061345161407f565b61345961407f565b61346161407f565b61346961407f565b612e87565b61347661407f565b61347e61407f565b60c860405161348c8161068e565b5260c863ffffffff19600a541617600a55565b600254811015611ce35760025f5260011b7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01905f90565b91909182516001600160401b038111610689576134f8816123158454611d0d565b602080601f83116001146135345750819061234b9394955f926135295750508160011b915f199060031b1c19161790565b015190505f80612337565b90601f19831695613548855f5260205f2090565b925f905b8882106135825750508360019596971061356a57505050811b019055565b01515f1960f88460031b161c191690555f8080612392565b8060018596829496860151815501950193019061354c565b9291906126bc578051835461ffff191661ffff91909116178355600180930190602080910151938451916001600160401b038311610689576135e6836135e08654611d0d565b866122a2565b602091601f8411600114613618575050819061234b9394955f926135295750508160011b915f199060031b1c19161790565b95601f9291921984169661362f865f5260205f2090565b935f915b898310613652575050508360019596971061356a57505050811b019055565b838501518655948501949381019391810191613633565b600254916003548084115f146136be575061039192506136976040519261368f846106a9565b61ffff168352565b60208201526136b86003546136b36136ae82612120565b600355565b61349f565b9061359a565b6136c790612120565b60035561ffff604051926136da846106a9565b1682526020820152600160401b821015610689576136b8826001610391940160025561349f565b6137349161370d611020565b90613716613ea1565b61329961377a60409260405197889161329960209889850190612f4e565b0396613748601f19988981018352826106f2565b61376e61375f6132d76104548a5163ffffffff1690565b60405194859389850190612f4e565b038781018352826106f2565b915f925b8486018051518510156133195761329961380161329961330389956137f38d6137cd6137c78d60019a6132ab8f6137bb8f9161329961328c613df0565b038681018952886106f2565b516140ad565b926132e96137e56132d786516001600160401b031690565b8d519788938d850190612f4e565b885194859388850190612f4e565b940193905061377e565b90613815826115e3565b60409061382560405191826106f2565b8381528093613836601f19916115e3565b01915f5b8381106138475750505050565b60209082516138558161066e565b5f8152825f8183015260608583015282860101520161383a565b9060405161387c816106a9565b60206115ca6001839561ffff815416855201611d45565b8151815463ffffffff191663ffffffff91821617825591600180920160208092015191808351936138c48585612419565b01915f52805f205f925b8484106138de5750505050505050565b8051805183548583015161ffff90921665ffffffffffff199091161790891660101b65ffffffff0000161783556040015180519187840191906001600160401b03841161068957828692613937866123158d9754611d0d565b8390601f871160011461397757958061396892600297985f926135295750508160011b915f199060031b1c19161790565b90555b019201930192906138ce565b9590601f1982169661398c845f5260205f2090565b975f5b8181106139cc575091879891846002999594106139b4575b505050811b01905561396b565b01515f1960f88460031b161c191690555f80806139a7565b838301518a5597909801978d978b97938401930161398f565b600754600160401b81101561068957806001613a049201600755611caa565b6126bc576008602083613b3e60c06103919651613a9e815186613a608251613a3a60ff8251168d9060ff1660ff19825416179055565b909101518a5464ffffffff00191660089190911b64ffffffff0016178a5563ffffffff90565b878060018c01930151613a8160ff825116859060ff1660ff19825416179055565b0151825464ffffffff001916911660081b64ffffffff0016179055565b613aae85820151600289016134d7565b613abf6040820151600389016134d7565b613aec613ad3606083015163ffffffff1690565b600489019063ffffffff1663ffffffff19825416179055565b613afd6080820151600589016134d7565b613b33613b1460a08301516001600160401b031690565b60068901906001600160401b03166001600160401b0319825416179055565b0151600786016134d7565b01519101613893565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b03841117610689576040935f9385528380528360205251915afa613bb657565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613c06575b505061099c57565b5f8051602061472c8339815191525416141590505f80613bfe565b61042e61329991613c4f613c3e6001600160401b03835116613f00565b916040519485936020850190612f4e565b03601f1981018352826106f2565b613c65611020565b90613c6e613dd9565b6040805193846020938492838301613c8591612f4e565b613c8e91612f4e565b0393601f19948581018752613ca390876106f2565b8051613cae90614257565b958651613cc1906001600160401b031690565b613cca90613f00565b908451918291858301613cdc91612f4e565b613ce591612f4e565b038681018252613cf590826106f2565b8351968791848301613d0691612f4e565b613d0f91612f4e565b038581018752613d1f90876106f2565b613d27613df0565b958351968791848301613d3991612f4e565b613d4291612f4e565b038581018752613d5290876106f2565b0151613d5d90614257565b908151613d70906001600160401b031690565b613d7990613f00565b948151958691858301613d8b91612f4e565b613d9491612f4e565b038481018652613da490866106f2565b519384928301613db391612f4e565b613dbc91612f4e565b03908101825261042e90826106f2565b805115611ce35760200190565b613de1611fb4565b600a613dec82613dcc565b5390565b613df8611fb4565b6012613dec82613dcc565b613e0b611fb4565b6028613dec82613dcc565b613e1e611fb4565b6030613dec82613dcc565b613e31611fb4565b6038613dec82613dcc565b613e44611fce565b60a25f5b607f80831115613e6d5790608060019284161760208286010153019060071c90613e48565b50607f600192166020828501015301815290565b613e89611fce565b61014260015f60c26020850153019060071c90613e48565b613ea9611fb4565b6008613dec82613dcc565b613ebc611fb4565b6010613dec82613dcc565b613ecf611fb4565b601a613dec82613dcc565b613ee2611fb4565b6032613dec82613dcc565b613ef5611fb4565b6042613dec82613dcc565b9060809160806001600160401b03821610613f5b57613f1d611fce565b5f5b607f80841115613f4357908560019285161760208285010153019160071c91613f1f565b5090929350607f600192166020828501015301815290565b909150613f66611fb4565b9060f81b6001600160f81b0319165f1a613dec82613dcc565b613f87611020565b90613f90613dd9565b6040805193846020938492838301613fa791612f4e565b613fb091612f4e565b0393601f19948581018752613fc590876106f2565b8051613fd09061430f565b958651613fe3906001600160401b031690565b613fec90613f00565b908451918291858301613ffe91612f4e565b61400791612f4e565b03868101825261401790826106f2565b835196879184830161402891612f4e565b61403191612f4e565b03858101875261404190876106f2565b614049613df0565b95835196879184830161405b91612f4e565b61406491612f4e565b03858101875261407490876106f2565b0151613d5d90613701565b60ff5f8051602061474c8339815191525460401c161561409b57565b604051631afcd79f60e31b8152600490fd5b61042e6140ee613299926141a66141926132996141996140cb611020565b946140d4613ea1565b9060409485928351998a9161329960209b8c850190612f4e565b0398614102601f199a8b81018352826106f2565b614141896141356141256132d761411e61411e895161ffff1690565b61ffff1690565b6132998851958694850190612f4e565b038b81018352826106f2565b61415089614135614125613eb4565b61416d896141356141256132d76104548489015163ffffffff1690565b614186614178613ec7565b85519d8e938c850190612f4e565b038981018c528b6106f2565b0151613c21565b9151968794850190612f4e565b039081018352826106f2565b90813b15614236575f8051602061472c83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561421b5761421891614668565b50565b50503461422457565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b61042e6132996132996141a66142ff6132d76104546020613299986142f46132996142ce614283611020565b61429d61428e613ea1565b6040519d8e9389850190612f4e565b039a6142b1601f199c8d81018352826106f2565b61413561375f6132d76142c86142c8895160ff1690565b60ff1690565b6142e86142d9613eb4565b6040519a8b9387850190612f4e565b038981018952886106f2565b015163ffffffff1690565b6040519586936020850190612f4e565b614317611020565b90614320613dd9565b9160408051809460209384830161433691612f4e565b61433f91612f4e565b0392601f1993848101865261435490866106f2565b805161435f90613c5d565b948551614372906001600160401b031690565b61437b90613f00565b90835191829186830161438d91612f4e565b61439691612f4e565b0385810182526143a690826106f2565b82519586918583016143b791612f4e565b6143c091612f4e565b0384810186526143d090866106f2565b6143d8613df0565b9482519586918583016143ea91612f4e565b6143f391612f4e565b03848101865261440390866106f2565b8281015161441090613c21565b94825195869185830161442291612f4e565b61442b91612f4e565b03848101865261443b90866106f2565b614443613ec7565b94825195869185830161445591612f4e565b61445e91612f4e565b03848101865261446e90866106f2565b8181015161447b90613c21565b94825195869185830161448d91612f4e565b61449691612f4e565b0384810186526144a690866106f2565b6144ae613e03565b9482519586918583016144c091612f4e565b6144c991612f4e565b0384810186526144d990866106f2565b60608101516144ed9063ffffffff16613f00565b9482519586918583016144ff91612f4e565b61450891612f4e565b03848101865261451890866106f2565b614520613eda565b94825195869185830161453291612f4e565b61453b91612f4e565b03848101865261454b90866106f2565b608081015161455990613c21565b94825195869185830161456b91612f4e565b61457491612f4e565b03848101865261458490866106f2565b61458c613e29565b94825195869185830161459e91612f4e565b6145a791612f4e565b0384810186526145b790866106f2565b60a08101516001600160401b03166145ce90613f00565b9482519586918583016145e091612f4e565b6145e991612f4e565b0384810186526145f990866106f2565b614601613eed565b94825195869185830161461391612f4e565b61461c91612f4e565b03848101865261462c90866106f2565b60c0015161463990613c21565b90519384928301613db391612f4e565b63ffffffff9190828216838216111561466157031690565b5050505f90565b5f8061042e93602081519101845af43d156146a4573d916146888361072d565b9261469660405194856106f2565b83523d5f602085013e6146a8565b6060915b906146cf57508051156146bd57602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614702575b6146e0575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156146d856fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a2646970667358221220f5b6d4a44ad194936330b5fb973c2a2aea0de6de3f7d1ea8e8573cef058ba4d864736f6c63430008190033", + "bytecode": "0x60a0806040523460c857306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1660b957506001600160401b036002600160401b0319828216016075575b6040516147a190816100cd82396080518181816109570152613bd20152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80806056565b63f92ee8a960e01b8152600490fd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806302ddde7b1461028457806304676e381461027f5780630d6f1e9d1461027a57806316a91b741461027557806327e935a11461027057806347453a2a1461026b5780634b3254fb146102665780634ed891d0146102615780634f1ef2861461025c5780634fca79cb1461025757806352d1902d14610252578063621fbd591461024d5780636744c79f146102485780637a9e5e4b146102435780638cbc18391461023e5780638fb360371461023957806393ec06be1461023457806395fabcf01461022f578063a3ad9cc31461022a578063a593aec214610225578063a9e50c1014610220578063ad3cb1cc1461021b578063b632ab5714610216578063b8f8d7e514610211578063bf7e214f1461020c578063c16a8a5614610207578063c4d66de814610202578063c8c14d17146101fd578063c94c93e7146101f8578063cde78421146101f3578063d5b5272a146101ee578063de152649146101e9578063e0064900146101e4578063e0c14aa8146101df578063e0c1c0b4146101da578063e2fc04ae146101d5578063e36f9a07146101d0578063e44efb5a146101cb5763f5786b4b146101c6575f80fd5b61193a565b61191b565b6118fe565b6117e1565b611515565b6113e9565b6113ac565b61138f565b61129a565b611272565b611243565b611227565b61110d565b6110ea565b6110b6565b611093565b611077565b611032565b610ffa565b610f00565b610c12565b610bf5565b610bd9565b610b89565b610ac2565b610a48565b6109d4565b6109ae565b610945565b610899565b61078e565b610628565b6105c8565b61056e565b610511565b6104ed565b610431565b610393565b610297565b5f91031261029357565b5f80fd5b34610293575f366003190112610293575f8051602061474c833981519152805460ff8160401c16908115610363575b50610351575f8051602061474c833981519152805467ffffffffffffffff19166002179055805460ff60401b1916600160401b179055610304611f66565b5f8051602061474c833981519152805460ff60401b19169055604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b60405163f92ee8a960e01b8152600490fd5b600291506001600160401b031610155f6102c6565b63ffffffff81160361029357565b359061039182610378565b565b346102935760203660031901126102935760606004356103b281610378565b63ffffffff8091165f52600960205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60409063ffffffff61042e949316815281602082015201906103ed565b90565b34610293575f3660031901126102935761045d6104546104105463ffffffff1690565b63ffffffff1690565b61046681611fe8565b90610479610473826105f8565b60051c90565b5f5b8181106104d5575050601f811690816104b2575b8261049f600c5463ffffffff1690565b6104ae60405192839283610411565b0390f35b81602091038301019060015f199160200360031b1b011981511690525f8061048f565b806001916010015460208260051b870101520161047b565b34610293575f36600319011261029357602063ffffffff6104105416604051908152f35b346102935760203660031901126102935760043561052f3633612d6b565b8015610548576004541561053f57005b6004555f600355005b604051636ad43cc360e11b8152600490fd5b634e487b7160e01b5f52603260045260245ffd5b34610293576020366003190112610293576004356006548110156102935760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01546040516001600160a01b039091168152602090f35b34610293575f3660031901126102935760206040516104008152f35b634e487b7160e01b5f52601160045260245ffd5b90601f820180921161060657565b6105e4565b9061010091820180921161060657565b9190820180921161060657565b34610293575f3660031901126102935760206040516101188152f35b600435906001600160a01b038216820361029357565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761068957604052565b61065a565b602081019081106001600160401b0382111761068957604052565b604081019081106001600160401b0382111761068957604052565b60e081019081106001600160401b0382111761068957604052565b6001600160401b03811161068957604052565b90601f801991011681019081106001600160401b0382111761068957604052565b60405190610391826106c4565b604051906103918261066e565b6001600160401b03811161068957601f01601f191660200190565b81601f820112156102935780359061075f8261072d565b9261076d60405194856106f2565b8284526020838301011161029357815f926020809301838601378301015290565b6040366003190112610293576107a2610644565b6024356001600160401b038111610293576107c1903690600401610748565b906107ca613bc8565b6107d43633612d6b565b6107dc613bc8565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610868575b5061082e57604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f8051602061472c833981519152830361084f5761084d92506141b2565b005b604051632a87526960e21b815260048101849052602490fd5b61088b91945060203d602011610892575b61088381836106f2565b8101906128a0565b925f610804565b503d610879565b34610293576020366003190112610293576104ae6004356108b981610378565b60405f8180516108c88161066e565b828152826020820152015263ffffffff8092165f5260096020526001815f206001600160401b038351946108fb8661066e565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610293575f366003190112610293577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316300361099c5760206040515f8051602061472c8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610293575f3660031901126102935760205f54604051908152f35b8015150361029357565b34610293576020366003190112610293576004356109f1816109ca565b600f546001600160a01b031633819003610a2a57507fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755d005b60449060405190636e01d1ef60e11b82523360048301526024820152fd5b3461029357602036600319011261029357610a61610644565b5f8051602061470c833981519152546001600160a01b03919082163303610aab57803b15610a925761084d90612e87565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b3461029357604036600319011261029357602435600435610ae33633612d6b565b63ffffffff6104105416610b445781811080610b38575b610b039061201a565b818110610b0c57005b80610b32610b1b600193612066565b81549060031b906001821b915f19901b1916179055565b01610b03565b50610400821115610afa565b60405162461bcd60e51b815260206004820152601f60248201527f4f50503a206f7574626f756e642062616e6b20616c7265616479206c697665006044820152606490fd5b34610293575f366003190112610293575f8051602061470c8339815191525460a01c60ff1615610bd1576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610bbe565b34610293575f3660031901126102935760206040516180008152f35b34610293575f366003190112610293576020600354604051908152f35b3461029357602036600319011261029357600435610c2f81610378565b610c393633612d6b565b600c5463ffffffff16610c4e61045482612078565b63ffffffff831603610ed15750610c636120c7565b63ffffffff82166060820152610c94610c846001600160401b034216612104565b6001600160401b03166040830152565b610c9c612edf565b60a08201525f600854908115159182610d3e575b7fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d88761034c86610d32610ce188612f60565b91610cf161800084511115612793565b610d1383516020850120610d058184613323565b610d0e83613389565b600d55565b610d1c836133e1565b63ffffffff1663ffffffff19600c541617600c55565b60405191829182612805565b5f92836101005b838210610e70575b5050610d639083159081610e68575b501561212e565b81610d6d81612209565b60c086019081525f5b828110610e30575050610d889161226c565b905f5b828110610e0257505f5b818110610df1575050610ce161034c93610de7610de27fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d8879795610dda610d3296600855565b600e5461226c565b600e55565b9350829450610cb0565b600190610dfc6126c1565b01610d95565b80610e2a610e1a610e156001948661061b565b611caa565b50610e2483611caa565b90612552565b01610d8b565b6001919250610e5e81610e4281611caa565b50610e4e865191611e88565b610e588383612258565b52612258565b5001908391610d76565b90505f610d5c565b610e8d610e88610e8284979894611caa565b50611e88565b612c9e565b90618000610e9b838361061b565b11610ec757610eb982610eb3600194610ebf9461061b565b9461061b565b96612120565b940190610d45565b5081959450610d4d565b610eda90612078565b6040516307d3e83d60e51b815263ffffffff918216600482015291166024820152604490fd5b3461029357602036600319011261029357600435610f1d81610378565b610f273633612d6b565b63ffffffff9081811691825f52600960205260405f20541615610fb557610f84610f8f91610f70610f68610454610f63600c5463ffffffff1690565b613432565b851115612816565b63ffffffff165f52600960205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b60405162461bcd60e51b815260206004820152601760248201527f4f50503a207265636f7264206e6f742070726573656e740000000000000000006044820152606490fd5b34610293575f3660031901126102935760206001600160401b0360015416604051908152f35b6040519061102d8261068e565b5f8252565b34610293575f366003190112610293576104ae604051611051816106a9565b60058152640352e302e360dc1b60208201526040519182916020835260208301906103ed565b34610293575f3660031901126102935760206040516120008152f35b34610293575f36600319011261029357602063ffffffff600c5416604051908152f35b34610293575f366003190112610293575f8051602061470c833981519152546040516001600160a01b039091168152602090f35b34610293575f36600319011261029357602063ffffffff600a5416604051908152f35b3461029357602036600319011261029357611126610644565b5f8051602061474c83398151915254906001600160401b0360ff8360401c161592168015908161121f575b6001149081611215575b15908161120c575b50610351575f8051602061474c833981519152805467ffffffffffffffff1916600117905561119690826111e857613445565b61119c57005b5f8051602061474c833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290806020810161034c565b5f8051602061474c833981519152805460ff60401b1916600160401b179055613445565b9050155f611163565b303b15915061115b565b839150611151565b34610293575f3660031901126102935760206040516101008152f35b34610293576020366003190112610293576004355f526005602052602060ff60405f2054166040519015158152f35b34610293575f36600319011261029357600f546040516001600160a01b039091168152602090f35b34610293576020366003190112610293576004356112b781610378565b6112c13633612d6b565b604051906112ce8261068e565b63ffffffff81168083521561134a577f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69161132a611311600a5463ffffffff1690565b9163ffffffff90511663ffffffff19600a541617600a55565b6040805163ffffffff92831681529290911660208301528190810161034c565b60405162461bcd60e51b815260206004820152601d60248201527f4f50503a20696e76616c696420726574656e74696f6e20636f6e6669670000006044820152606490fd5b34610293575f366003190112610293576020600e54604051908152f35b34610293575f366003190112610293576020600854604051908152f35b6004359061ffff8216820361029357565b359061ffff8216820361029357565b3461029357604080600319360112610293576114036113c9565b6024356001600160401b03811161029357611422903690600401610748565b9160045415611505576114353633612d6b565b5f8051602061470c8339815191525461146490611458906001600160a01b031681565b6001600160a01b031690565b81516368fc2b7760e11b815261ffff841662010000176004820152336024820152908290829060449082905afa908115611500575f916114d2575b50156114af5761084d8383613669565b51639a28d60760e01b815233600482015261ffff91909116602482015260449150fd5b6114f29150823d84116114f9575b6114ea81836106f2565b810190612870565b505f61149f565b503d6114e0565b612895565b5163738c481160e01b8152600490fd5b346102935760203660031901126102935761152e610644565b6115383633612d6b565b600f80546001600160a01b0319166001600160a01b0392909216919091179055005b919082604091031261029357604051611572816106a9565b8092803560ff811681036102935782526020908101359161159283610378565b0152565b91906080838203126102935760206115ca604051926115b4846106a9565b604084966115c2838261155a565b86520161155a565b910152565b35906001600160401b038216820361029357565b6001600160401b0381116106895760051b60200190565b9190916040908181850312610293578151611614816106a9565b8094823561162181610378565b8252602092838101356001600160401b039182821161029357019180601f8401121561029357823590611653826115e3565b96611660815198896106f2565b828852868089019360051b8601019482861161029357878101935b86851061168d57505050505050500152565b84358681116102935782019060609081601f198488030112610293578451916116b58361066e565b6116c08c85016113da565b8352858401356116cf81610378565b8c84015283013591888311610293576116ef878d80969581960101610748565b8682015281520194019361167b565b90604060206103919361172584825163ffffffff6020809260ff8151168552015116910152565b015191019063ffffffff6020809260ff8151168552015116910152565b9061042e91602081526117596020820183516116fe565b602082015190611777610140928360a08401526101608301906103ed565b9160c06117bd611798604087015195601f19968787830301858801526103ed565b606087015163ffffffff1660e0860152608087015186868303016101008701526103ed565b60a08601516001600160401b031661012085015294015192828503019101526103ed565b346102935760031960403682011261029357600480356001600160401b0392838211610293576101409082360301126102935761181c610713565b9061182936828501611596565b825260848101358481116102935761184690843691840101610748565b602083015260a48101358481116102935761186690843691840101610748565b604083015261187760c48201610386565b606083015260e48101358481116102935761189790843691840101610748565b60808301526118a961010482016115cf565b60a08301526101248101359084821161029357836118ca9236920101610748565b60c0820152602435928311610293576118ec6118f2926104ae943691016115fa565b906129e1565b60405191829182611742565b34610293575f366003190112610293576020600454604051908152f35b34610293575f3660031901126102935760206004541515604051908152f35b3461029357602080600319360112610293576004908135825415611c99576119623633612d6b565b8015611c885782541461197157005b5f82556003548015611c7757611985612191565b5f5480611c0b5750611995611f9e565b60409060408301526119c26119b26001600160401b034216612104565b6001600160401b031660a0840152565b6119ca6121ce565b600181529283916119da8261380b565b8684019081525f5b838110611b8f57505050506119f6916129e1565b91611a188180850151604051809381926347f5343160e01b8352898301612805565b038173__$6c94be45b3cad4e4a0bccaa807f5821f2b$__5af4908115611500575f91611b72575b505f55611a75611a5960a08501516001600160401b031690565b6001600160401b03166001600160401b03196001541617600155565b611a7d6121e7565b928352820152611a8c81612c9e565b917fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755c611b2457611abf83600e5461061b565b906120008211611aff575050611af691611adb610de2926139e5565b611aee611ae9600854612120565b600855565b600e5461061b565b61084d5f600355565b604080516306f8bcbb60e51b8152918201928352612000602084015290918291010390fd5b618000611b308461060b565b11611b465750611af691611adb610de2926139e5565b611b4f8361060b565b6040805163048c985b60e51b815292830191825261800060208301528291010390fd5b611b899150823d84116108925761088381836106f2565b5f611a3f565b60019192939450611bff81611bac611ba68261349f565b5061386f565b8a611bb9825161ffff1690565b910151611bee611bcd825163ffffffff1690565b611be2611bd8610720565b61ffff9095168552565b63ffffffff16838e0152565b87820152855190610e588383612258565b500190859392916119e2565b604051630c9b3d8760e21b8152858101918252905f9082908190602001038173__$6c94be45b3cad4e4a0bccaa807f5821f2b$__5af4908115611500575f91611c55575b50611995565b611c7191503d805f833e611c6981836106f2565b81019061295a565b5f611c4f565b60405163783bc0f160e11b81528390fd5b604051636ad43cc360e11b81528390fd5b60405163738c481160e01b81528390fd5b600754811015611ce357600a9060075f52027fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801905f90565b61055a565b90604051611cf5816106a9565b915460ff8116835260081c63ffffffff166020830152565b90600182811c92168015611d3b575b6020831014611d2757565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611d1c565b9060405191825f8254611d5781611d0d565b908184526020946001916001811690815f14611dc35750600114611d85575b505050610391925003836106f2565b5f90815285812095935091905b818310611dab57505061039193508201015f8080611d76565b85548884018501529485019487945091830191611d92565b9250505061039194925060ff191682840152151560051b8201015f8080611d76565b9060408051611df3816106a9565b809363ffffffff8082541683526001809201805491611e11836115e3565b95611e1f60405197886106f2565b83875260209160208801935f5260205f20915f945b868610611e4957505050505050505060200152565b60028589928451611e598161066e565b86885461ffff8116835260101c1683820152611e76858901611d45565b86820152815201940195019492611e34565b90604051611e95816106a9565b60206115ca60088395604051611eaa816106c4565b604051611eb6816106a9565b611ebf83611ce8565b8152611ecd60018401611ce8565b868201528152611edf60028301611d45565b85820152611eef60038301611d45565b6040820152611f15611f08600484015463ffffffff1690565b63ffffffff166060830152565b611f2160058301611d45565b6080820152611f4d611f3d60068401546001600160401b031690565b6001600160401b031660a0830152565b611f5960078301611d45565b60c0820152855201611de5565b5f806008545b808210611f7a575050600e55565b9091611f8b610e88610e8285611caa565b8101809111610606579160010190611f6c565b604051611faa8161068e565b5f8152905f368137565b60405190611fc1826106a9565b6001825260203681840137565b60405190611fdb826106a9565b600a825260203681840137565b90611ff28261072d565b611fff60405191826106f2565b8281528092612010601f199161072d565b0190602036910137565b1561202157565b60405162461bcd60e51b815260206004820152601a60248201527f4f50503a20696e76616c6964207072657761726d2072616e67650000000000006044820152606490fd5b610400811015611ce357601001905f90565b90600163ffffffff8093160191821161060657565b6040519061209a826106a9565b5f6020838281520152565b604051906120b2826106a9565b816120bb61208d565b815260206115ca61208d565b604051906120d4826106c4565b606060c0838281526120e46120a5565b60208201525f60408201525f838201525f60808201528260a08201520152565b906103e86001600160401b038093160291821691820361060657565b5f1981146106065760010190565b1561213557565b60405162461bcd60e51b815260206004820152602e60248201527f4f50503a20717565756564206d6573736167652065786365656473204d41585f60448201526d454e56454c4f50455f425954455360901b6064820152608490fd5b6040519061219e826106c4565b606060c0836121ab6120a5565b81528260208201528260408201525f838201528260808201525f60a08201520152565b604051906121db826106a9565b60606020835f81520152565b604051906121f4826106a9565b816121fd612191565b815260206115ca6121ce565b90612213826115e3565b61222060405191826106f2565b8281528092612231601f19916115e3565b01905f5b82811061224157505050565b60209061224c6121e7565b82828501015201612235565b8051821015611ce35760209160051b010190565b9190820391821161060657565b634e487b7160e01b5f525f60045260245ffd5b818110612297575050565b5f815560010161228c565b9190601f81116122b157505050565b610391925f5260205f20906020601f840160051c830193106122db575b601f0160051c019061228c565b90915081906122ce565b9190918281146123cc576122f98354611d0d565b6001600160401b0381116106895761231b816123158454611d0d565b846122a2565b5f93601f821160011461235a5761234b92939482915f9261234f575b50508160011b915f199060031b1c19161790565b9055565b015490505f80612337565b61236c601f198316915f5260205f2090565b9461237a845f5260205f2090565b915f5b8181106123b45750958360019596971061239c575b505050811b019055565b01545f1960f88460031b161c191690555f8080612392565b8783015484556001938401939092019160200161237d565b509050565b6123db8154611d0d565b90816123e5575050565b81601f5f93116001146123f6575055565b908083918252612415601f60208420940160051c84016001850161228c565b5555565b600160401b821161068957805482825580831061243557505050565b6001916001600160ff1b0382811683036106065784168403610606575f5260205f209060011b81019260011b015b82811061246f57505050565b805f600292556124808382016123d1565b01612463565b8181146125135781549161249a8383612419565b5f5260205f20905f5260205f20905f905b8382106124b85750505050565b8060019184036124d3575b60028091019301910190916124ab565b8054845461ffff191661ffff919091161784558054845465ffffffff0000191665ffffffff000090911617845561250e8183018584016122e5565b6124c3565b5050565b818103612522575050565b6001808361254a63ffffffff610391965416859063ffffffff1663ffffffff19825416179055565b019101612486565b906126bc5781811415918261256657505050565b60088091846103919561257d575b50019101612517565b8061262d575b5061259460028201600286016122e5565b6125a460038201600386016122e5565b6125d16125b8600483015463ffffffff1690565b600486019063ffffffff1663ffffffff19825416179055565b6125e160058201600586016122e5565b6126176125f860068301546001600160401b031690565b60068601906001600160401b03166001600160401b0319825416179055565b61262760078201600786016122e5565b5f612574565b61268a575b600184016001820190818103612649575b50612583565b8154815460ff191660ff919091161781556126839163ffffffff9054851c1664ffffffff0082549160081b169064ffffffff001916179055565b5f80612643565b8054845460ff191660ff919091161784558054845464ffffffff00191690831c60081b64ffffffff0016178455612632565b612279565b600754801561277f575f19016126d681611caa565b6126bc575f815560015f600183015560096002926126f6600282016123d1565b612702600382016123d1565b5f6004820155612714600582016123d1565b5f6006820155612726600782016123d1565b5f600882015501908154915f815582612743575b50505050600755565b6001600160ff1b0383168303610606575f5260205f209160011b8201915b8281101561273a57805f8592556127798382016123d1565b01612761565b634e487b7160e01b5f52603160045260245ffd5b1561279a57565b60405162461bcd60e51b815260206004820152603a60248201527f4f50503a20656e76656c6f70652065786365656473204d41585f454e56454c4f60448201527f50455f42595445532028657374696d61746f72206472696674290000000000006064820152608490fd5b90602061042e9281815201906103ed565b1561281d57565b60405162461bcd60e51b815260206004820152602560248201527f4f50503a207265636f7264207374696c6c20696e20726574656e74696f6e2077604482015264696e646f7760d81b6064820152608490fd5b91908260409103126102935760208251612889816109ca565b92015161042e81610378565b6040513d5f823e3d90fd5b90816020910312610293575190565b90602080835260609060608401908063ffffffff94858151168288015201519460408080830152865180945260808201908360808660051b8501019801955f935b8685106129035750505050505050505090565b909192939495969799988680612945600193868f89908f607f198a8603018d52519061ffff8251168552868201511686850152015191818a82015201906103ed565b9b9c9a019897969190910194019291906128f0565b602081830312610293578051906001600160401b038211610293570181601f820112156102935780519061298d8261072d565b9261299b60405194856106f2565b8284526020838301011161029357815f9260208093018386015e8301015290565b90916129d361042e936040845260408401906103ed565b9160208184039101526103ed565b906129ea612191565b506040805163765809ab60e11b81526004926020929173__$6c94be45b3cad4e4a0bccaa807f5821f2b$__9190848180612a26858a83016128af565b0381865af4908115611500575f91612c81575b5060808701908582518051155f14612bfd5750508451630c9b3d8760e21b8152878101918252905f90829081906020010381875af492831561150057612aa693612a9993612a8f935f92612be1575b5052613701565b5163ffffffff1690565b63ffffffff166060870152565b815162409a0960e81b81525f8180612ac089898301611742565b0381855af4908115611500575f91612bc7575b508584018051805180612b8c57505052815163b93612a760e01b815293838580612aff89858301611742565b0381855af492831561150057612b38955f958695612b6d575b505051630c9b3d8760e21b81529081019283529384928391829160200190565b03915af4908115611500575f91612b53575b5060c082015290565b612b6791503d805f833e611c6981836106f2565b5f612b4a565b612b84929550803d106108925761088381836106f2565b925f80612b18565b915093925084849695929601209085519086012003612bac575050505090565b51630ca2e06d60e11b815292839261082a92919084016129bc565b612bdb91503d805f833e611c6981836106f2565b5f612ad3565b612bf69192503d805f833e611c6981836106f2565b905f612a88565b90919350612c1e92508551809381926347f5343160e01b83528a8301612805565b0381865af4908115611500575f91612c64575b50818103612c40575050612aa6565b8351631d97afef60e31b815280870192835260208301919091529081906040010390fd5b612c7b9150853d87116108925761088381836106f2565b5f612c31565b612c989150853d87116108925761088381836106f2565b5f612a39565b61010090602080910190602082510151515f925b818410612cc0575050505090565b90919293601880820191828111610606576040612ce1888887510151612258565b5101515101018091116106065793600101929190612cb2565b600411610293575f90600490565b6001600160e01b03199035818116939260048110612d2557505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f8051602061470c8339815191528054612da3906001600160a01b0316612d9a612d9486612cfa565b90612d08565b90843091613b47565b9015612db0575b50505050565b63ffffffff1615612e67575f8051602061470c833981519152805460ff60a01b1916600160a01b179055612df190611458908190546001600160a01b031690565b91823b1561029357612e1c925f9283604051809681958294634a63ebf760e11b845260048401612d34565b03925af1801561150057612e4e575b505f8051602061470c833981519152805460ff60a01b191690555f808080612daa565b80612e5b612e61926106df565b80610289565b5f612e2b565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f8051602061470c83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b600d5480612efa5750604051612ef48161068e565b5f815290565b60405190630c9b3d8760e21b825260048201525f8160248173__$6c94be45b3cad4e4a0bccaa807f5821f2b$__5af4908115611500575f91612f3a575090565b61042e91503d805f833e611c6981836106f2565b805191908290602001825e015f815290565b612f68611020565b612f70613dd9565b92604080518095602094858301612f8691612f4e565b612f8f91612f4e565b0394601f19958681018252612fa490826106f2565b8451612faf90613c21565b908251918291868301612fc191612f4e565b612fca91612f4e565b038681018252612fda90826106f2565b612fe2613df0565b908251918291868301612ff491612f4e565b612ffd91612f4e565b03868101825261300d90826106f2565b8385015161301a90613c5d565b90815161302d906001600160401b031690565b61303690613f00565b90835191829187830161304891612f4e565b61305191612f4e565b03878101825261306190826106f2565b825191829186830161307291612f4e565b61307b91612f4e565b03868101825261308b90826106f2565b613093613e03565b9082519182918683016130a591612f4e565b6130ae91612f4e565b0386810182526130be90826106f2565b848201516001600160401b03166130d490613f00565b9082519182918683016130e691612f4e565b6130ef91612f4e565b0386810182526130ff90826106f2565b613107613e16565b90825191829186830161311991612f4e565b61312291612f4e565b03868101825261313290826106f2565b60608501516131469063ffffffff16613f00565b90825191829186830161315891612f4e565b61316191612f4e565b03868101825261317190826106f2565b613179613e29565b90825191829186830161318b91612f4e565b61319491612f4e565b0386810182526131a490826106f2565b60808501516131b89063ffffffff16613f00565b9082519182918683016131ca91612f4e565b6131d391612f4e565b0386810182526131e390826106f2565b6131eb613e3c565b9082519182918683016131fd91612f4e565b61320691612f4e565b03868101825261321690826106f2565b60a085015161322490613c21565b90825191829186830161323691612f4e565b61323f91612f4e565b03868101825261324f90826106f2565b915f925b60c086018051518510156133195761329961330f60c0936133036132b76132b18a6001978f6132ab908f61329f8f61329961328c613e81565b91519b8c94850190612f4e565b90612f4e565b039081018852876106f2565b51612258565b51613f7f565b6132f58d8c6132e98c6132996132dc6132d788516001600160401b031690565b613f00565b9151988994850190612f4e565b039081018552846106f2565b88519485938c850190612f4e565b038a81018352826106f2565b9401939050613253565b5095509350505050565b63ffffffff8082165f52600960205260405f20908154166133845761335a600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b6133a09063ffffffff91829182600a541690614649565b169081151590816133c9575b506133b45750565b5f5260096020525f6001604082208281550155565b9050815f52600960205260405f20541615155f6133ac565b805190601f8201908183116106065760059160051c915f5b83811061341c575050505063ffffffff610410911663ffffffff19825416179055565b80821b83016020015160108201556001016133f9565b61042e9063ffffffff600a541690614649565b61346e9061345161407f565b61345961407f565b61346161407f565b61346961407f565b612e87565b61347661407f565b61347e61407f565b60c860405161348c8161068e565b5260c863ffffffff19600a541617600a55565b600254811015611ce35760025f5260011b7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01905f90565b91909182516001600160401b038111610689576134f8816123158454611d0d565b602080601f83116001146135345750819061234b9394955f926135295750508160011b915f199060031b1c19161790565b015190505f80612337565b90601f19831695613548855f5260205f2090565b925f905b8882106135825750508360019596971061356a57505050811b019055565b01515f1960f88460031b161c191690555f8080612392565b8060018596829496860151815501950193019061354c565b9291906126bc578051835461ffff191661ffff91909116178355600180930190602080910151938451916001600160401b038311610689576135e6836135e08654611d0d565b866122a2565b602091601f8411600114613618575050819061234b9394955f926135295750508160011b915f199060031b1c19161790565b95601f9291921984169661362f865f5260205f2090565b935f915b898310613652575050508360019596971061356a57505050811b019055565b838501518655948501949381019391810191613633565b600254916003548084115f146136be575061039192506136976040519261368f846106a9565b61ffff168352565b60208201526136b86003546136b36136ae82612120565b600355565b61349f565b9061359a565b6136c790612120565b60035561ffff604051926136da846106a9565b1682526020820152600160401b821015610689576136b8826001610391940160025561349f565b6137349161370d611020565b90613716613ea1565b61329961377a60409260405197889161329960209889850190612f4e565b0396613748601f19988981018352826106f2565b61376e61375f6132d76104548a5163ffffffff1690565b60405194859389850190612f4e565b038781018352826106f2565b915f925b8486018051518510156133195761329961380161329961330389956137f38d6137cd6137c78d60019a6132ab8f6137bb8f9161329961328c613df0565b038681018952886106f2565b516140ad565b926132e96137e56132d786516001600160401b031690565b8d519788938d850190612f4e565b885194859388850190612f4e565b940193905061377e565b90613815826115e3565b60409061382560405191826106f2565b8381528093613836601f19916115e3565b01915f5b8381106138475750505050565b60209082516138558161066e565b5f8152825f8183015260608583015282860101520161383a565b9060405161387c816106a9565b60206115ca6001839561ffff815416855201611d45565b8151815463ffffffff191663ffffffff91821617825591600180920160208092015191808351936138c48585612419565b01915f52805f205f925b8484106138de5750505050505050565b8051805183548583015161ffff90921665ffffffffffff199091161790891660101b65ffffffff0000161783556040015180519187840191906001600160401b03841161068957828692613937866123158d9754611d0d565b8390601f871160011461397757958061396892600297985f926135295750508160011b915f199060031b1c19161790565b90555b019201930192906138ce565b9590601f1982169661398c845f5260205f2090565b975f5b8181106139cc575091879891846002999594106139b4575b505050811b01905561396b565b01515f1960f88460031b161c191690555f80806139a7565b838301518a5597909801978d978b97938401930161398f565b600754600160401b81101561068957806001613a049201600755611caa565b6126bc576008602083613b3e60c06103919651613a9e815186613a608251613a3a60ff8251168d9060ff1660ff19825416179055565b909101518a5464ffffffff00191660089190911b64ffffffff0016178a5563ffffffff90565b878060018c01930151613a8160ff825116859060ff1660ff19825416179055565b0151825464ffffffff001916911660081b64ffffffff0016179055565b613aae85820151600289016134d7565b613abf6040820151600389016134d7565b613aec613ad3606083015163ffffffff1690565b600489019063ffffffff1663ffffffff19825416179055565b613afd6080820151600589016134d7565b613b33613b1460a08301516001600160401b031690565b60068901906001600160401b03166001600160401b0319825416179055565b0151600786016134d7565b01519101613893565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b03841117610689576040935f9385528380528360205251915afa613bb657565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613c06575b505061099c57565b5f8051602061472c8339815191525416141590505f80613bfe565b61042e61329991613c4f613c3e6001600160401b03835116613f00565b916040519485936020850190612f4e565b03601f1981018352826106f2565b613c65611020565b90613c6e613dd9565b6040805193846020938492838301613c8591612f4e565b613c8e91612f4e565b0393601f19948581018752613ca390876106f2565b8051613cae90614257565b958651613cc1906001600160401b031690565b613cca90613f00565b908451918291858301613cdc91612f4e565b613ce591612f4e565b038681018252613cf590826106f2565b8351968791848301613d0691612f4e565b613d0f91612f4e565b038581018752613d1f90876106f2565b613d27613df0565b958351968791848301613d3991612f4e565b613d4291612f4e565b038581018752613d5290876106f2565b0151613d5d90614257565b908151613d70906001600160401b031690565b613d7990613f00565b948151958691858301613d8b91612f4e565b613d9491612f4e565b038481018652613da490866106f2565b519384928301613db391612f4e565b613dbc91612f4e565b03908101825261042e90826106f2565b805115611ce35760200190565b613de1611fb4565b600a613dec82613dcc565b5390565b613df8611fb4565b6012613dec82613dcc565b613e0b611fb4565b6028613dec82613dcc565b613e1e611fb4565b6030613dec82613dcc565b613e31611fb4565b6038613dec82613dcc565b613e44611fce565b60a25f5b607f80831115613e6d5790608060019284161760208286010153019060071c90613e48565b50607f600192166020828501015301815290565b613e89611fce565b61014260015f60c26020850153019060071c90613e48565b613ea9611fb4565b6008613dec82613dcc565b613ebc611fb4565b6010613dec82613dcc565b613ecf611fb4565b601a613dec82613dcc565b613ee2611fb4565b6032613dec82613dcc565b613ef5611fb4565b6042613dec82613dcc565b9060809160806001600160401b03821610613f5b57613f1d611fce565b5f5b607f80841115613f4357908560019285161760208285010153019160071c91613f1f565b5090929350607f600192166020828501015301815290565b909150613f66611fb4565b9060f81b6001600160f81b0319165f1a613dec82613dcc565b613f87611020565b90613f90613dd9565b6040805193846020938492838301613fa791612f4e565b613fb091612f4e565b0393601f19948581018752613fc590876106f2565b8051613fd09061430f565b958651613fe3906001600160401b031690565b613fec90613f00565b908451918291858301613ffe91612f4e565b61400791612f4e565b03868101825261401790826106f2565b835196879184830161402891612f4e565b61403191612f4e565b03858101875261404190876106f2565b614049613df0565b95835196879184830161405b91612f4e565b61406491612f4e565b03858101875261407490876106f2565b0151613d5d90613701565b60ff5f8051602061474c8339815191525460401c161561409b57565b604051631afcd79f60e31b8152600490fd5b61042e6140ee613299926141a66141926132996141996140cb611020565b946140d4613ea1565b9060409485928351998a9161329960209b8c850190612f4e565b0398614102601f199a8b81018352826106f2565b614141896141356141256132d761411e61411e895161ffff1690565b61ffff1690565b6132998851958694850190612f4e565b038b81018352826106f2565b61415089614135614125613eb4565b61416d896141356141256132d76104548489015163ffffffff1690565b614186614178613ec7565b85519d8e938c850190612f4e565b038981018c528b6106f2565b0151613c21565b9151968794850190612f4e565b039081018352826106f2565b90813b15614236575f8051602061472c83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561421b5761421891614668565b50565b50503461422457565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b61042e6132996132996141a66142ff6132d76104546020613299986142f46132996142ce614283611020565b61429d61428e613ea1565b6040519d8e9389850190612f4e565b039a6142b1601f199c8d81018352826106f2565b61413561375f6132d76142c86142c8895160ff1690565b60ff1690565b6142e86142d9613eb4565b6040519a8b9387850190612f4e565b038981018952886106f2565b015163ffffffff1690565b6040519586936020850190612f4e565b614317611020565b90614320613dd9565b9160408051809460209384830161433691612f4e565b61433f91612f4e565b0392601f1993848101865261435490866106f2565b805161435f90613c5d565b948551614372906001600160401b031690565b61437b90613f00565b90835191829186830161438d91612f4e565b61439691612f4e565b0385810182526143a690826106f2565b82519586918583016143b791612f4e565b6143c091612f4e565b0384810186526143d090866106f2565b6143d8613df0565b9482519586918583016143ea91612f4e565b6143f391612f4e565b03848101865261440390866106f2565b8281015161441090613c21565b94825195869185830161442291612f4e565b61442b91612f4e565b03848101865261443b90866106f2565b614443613ec7565b94825195869185830161445591612f4e565b61445e91612f4e565b03848101865261446e90866106f2565b8181015161447b90613c21565b94825195869185830161448d91612f4e565b61449691612f4e565b0384810186526144a690866106f2565b6144ae613e03565b9482519586918583016144c091612f4e565b6144c991612f4e565b0384810186526144d990866106f2565b60608101516144ed9063ffffffff16613f00565b9482519586918583016144ff91612f4e565b61450891612f4e565b03848101865261451890866106f2565b614520613eda565b94825195869185830161453291612f4e565b61453b91612f4e565b03848101865261454b90866106f2565b608081015161455990613c21565b94825195869185830161456b91612f4e565b61457491612f4e565b03848101865261458490866106f2565b61458c613e29565b94825195869185830161459e91612f4e565b6145a791612f4e565b0384810186526145b790866106f2565b60a08101516001600160401b03166145ce90613f00565b9482519586918583016145e091612f4e565b6145e991612f4e565b0384810186526145f990866106f2565b614601613eed565b94825195869185830161461391612f4e565b61461c91612f4e565b03848101865261462c90866106f2565b60c0015161463990613c21565b90519384928301613db391612f4e565b63ffffffff9190828216838216111561466157031690565b5050505f90565b5f8061042e93602081519101845af43d156146a4573d916146888361072d565b9261469660405194856106f2565b83523d5f602085013e6146a8565b6060915b906146cf57508051156146bd57602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614702575b6146e0575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156146d856fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212206d0b8bf89bf266756ee2e2b3eefbac5d0ed3b41f57b9ea9047b840c51040157264736f6c63430008190033", + "deployedBytecode": "0x60806040526004361015610011575f80fd5b5f3560e01c806302ddde7b1461028457806304676e381461027f5780630d6f1e9d1461027a57806316a91b741461027557806327e935a11461027057806347453a2a1461026b5780634b3254fb146102665780634ed891d0146102615780634f1ef2861461025c5780634fca79cb1461025757806352d1902d14610252578063621fbd591461024d5780636744c79f146102485780637a9e5e4b146102435780638cbc18391461023e5780638fb360371461023957806393ec06be1461023457806395fabcf01461022f578063a3ad9cc31461022a578063a593aec214610225578063a9e50c1014610220578063ad3cb1cc1461021b578063b632ab5714610216578063b8f8d7e514610211578063bf7e214f1461020c578063c16a8a5614610207578063c4d66de814610202578063c8c14d17146101fd578063c94c93e7146101f8578063cde78421146101f3578063d5b5272a146101ee578063de152649146101e9578063e0064900146101e4578063e0c14aa8146101df578063e0c1c0b4146101da578063e2fc04ae146101d5578063e36f9a07146101d0578063e44efb5a146101cb5763f5786b4b146101c6575f80fd5b61193a565b61191b565b6118fe565b6117e1565b611515565b6113e9565b6113ac565b61138f565b61129a565b611272565b611243565b611227565b61110d565b6110ea565b6110b6565b611093565b611077565b611032565b610ffa565b610f00565b610c12565b610bf5565b610bd9565b610b89565b610ac2565b610a48565b6109d4565b6109ae565b610945565b610899565b61078e565b610628565b6105c8565b61056e565b610511565b6104ed565b610431565b610393565b610297565b5f91031261029357565b5f80fd5b34610293575f366003190112610293575f8051602061474c833981519152805460ff8160401c16908115610363575b50610351575f8051602061474c833981519152805467ffffffffffffffff19166002179055805460ff60401b1916600160401b179055610304611f66565b5f8051602061474c833981519152805460ff60401b19169055604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b60405163f92ee8a960e01b8152600490fd5b600291506001600160401b031610155f6102c6565b63ffffffff81160361029357565b359061039182610378565b565b346102935760203660031901126102935760606004356103b281610378565b63ffffffff8091165f52600960205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b60409063ffffffff61042e949316815281602082015201906103ed565b90565b34610293575f3660031901126102935761045d6104546104105463ffffffff1690565b63ffffffff1690565b61046681611fe8565b90610479610473826105f8565b60051c90565b5f5b8181106104d5575050601f811690816104b2575b8261049f600c5463ffffffff1690565b6104ae60405192839283610411565b0390f35b81602091038301019060015f199160200360031b1b011981511690525f8061048f565b806001916010015460208260051b870101520161047b565b34610293575f36600319011261029357602063ffffffff6104105416604051908152f35b346102935760203660031901126102935760043561052f3633612d6b565b8015610548576004541561053f57005b6004555f600355005b604051636ad43cc360e11b8152600490fd5b634e487b7160e01b5f52603260045260245ffd5b34610293576020366003190112610293576004356006548110156102935760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01546040516001600160a01b039091168152602090f35b34610293575f3660031901126102935760206040516104008152f35b634e487b7160e01b5f52601160045260245ffd5b90601f820180921161060657565b6105e4565b9061010091820180921161060657565b9190820180921161060657565b34610293575f3660031901126102935760206040516101188152f35b600435906001600160a01b038216820361029357565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b0382111761068957604052565b61065a565b602081019081106001600160401b0382111761068957604052565b604081019081106001600160401b0382111761068957604052565b60e081019081106001600160401b0382111761068957604052565b6001600160401b03811161068957604052565b90601f801991011681019081106001600160401b0382111761068957604052565b60405190610391826106c4565b604051906103918261066e565b6001600160401b03811161068957601f01601f191660200190565b81601f820112156102935780359061075f8261072d565b9261076d60405194856106f2565b8284526020838301011161029357815f926020809301838601378301015290565b6040366003190112610293576107a2610644565b6024356001600160401b038111610293576107c1903690600401610748565b906107ca613bc8565b6107d43633612d6b565b6107dc613bc8565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610868575b5061082e57604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f8051602061472c833981519152830361084f5761084d92506141b2565b005b604051632a87526960e21b815260048101849052602490fd5b61088b91945060203d602011610892575b61088381836106f2565b8101906128a0565b925f610804565b503d610879565b34610293576020366003190112610293576104ae6004356108b981610378565b60405f8180516108c88161066e565b828152826020820152015263ffffffff8092165f5260096020526001815f206001600160401b038351946108fb8661066e565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610293575f366003190112610293577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316300361099c5760206040515f8051602061472c8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610293575f3660031901126102935760205f54604051908152f35b8015150361029357565b34610293576020366003190112610293576004356109f1816109ca565b600f546001600160a01b031633819003610a2a57507fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755d005b60449060405190636e01d1ef60e11b82523360048301526024820152fd5b3461029357602036600319011261029357610a61610644565b5f8051602061470c833981519152546001600160a01b03919082163303610aab57803b15610a925761084d90612e87565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b3461029357604036600319011261029357602435600435610ae33633612d6b565b63ffffffff6104105416610b445781811080610b38575b610b039061201a565b818110610b0c57005b80610b32610b1b600193612066565b81549060031b906001821b915f19901b1916179055565b01610b03565b50610400821115610afa565b60405162461bcd60e51b815260206004820152601f60248201527f4f50503a206f7574626f756e642062616e6b20616c7265616479206c697665006044820152606490fd5b34610293575f366003190112610293575f8051602061470c8339815191525460a01c60ff1615610bd1576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610bbe565b34610293575f3660031901126102935760206040516180008152f35b34610293575f366003190112610293576020600354604051908152f35b3461029357602036600319011261029357600435610c2f81610378565b610c393633612d6b565b600c5463ffffffff16610c4e61045482612078565b63ffffffff831603610ed15750610c636120c7565b63ffffffff82166060820152610c94610c846001600160401b034216612104565b6001600160401b03166040830152565b610c9c612edf565b60a08201525f600854908115159182610d3e575b7fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d88761034c86610d32610ce188612f60565b91610cf161800084511115612793565b610d1383516020850120610d058184613323565b610d0e83613389565b600d55565b610d1c836133e1565b63ffffffff1663ffffffff19600c541617600c55565b60405191829182612805565b5f92836101005b838210610e70575b5050610d639083159081610e68575b501561212e565b81610d6d81612209565b60c086019081525f5b828110610e30575050610d889161226c565b905f5b828110610e0257505f5b818110610df1575050610ce161034c93610de7610de27fe93b186dfb145614c1bb2c64e844f7211236c8c919656e5e745d6cdb6260d8879795610dda610d3296600855565b600e5461226c565b600e55565b9350829450610cb0565b600190610dfc6126c1565b01610d95565b80610e2a610e1a610e156001948661061b565b611caa565b50610e2483611caa565b90612552565b01610d8b565b6001919250610e5e81610e4281611caa565b50610e4e865191611e88565b610e588383612258565b52612258565b5001908391610d76565b90505f610d5c565b610e8d610e88610e8284979894611caa565b50611e88565b612c9e565b90618000610e9b838361061b565b11610ec757610eb982610eb3600194610ebf9461061b565b9461061b565b96612120565b940190610d45565b5081959450610d4d565b610eda90612078565b6040516307d3e83d60e51b815263ffffffff918216600482015291166024820152604490fd5b3461029357602036600319011261029357600435610f1d81610378565b610f273633612d6b565b63ffffffff9081811691825f52600960205260405f20541615610fb557610f84610f8f91610f70610f68610454610f63600c5463ffffffff1690565b613432565b851115612816565b63ffffffff165f52600960205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b60405162461bcd60e51b815260206004820152601760248201527f4f50503a207265636f7264206e6f742070726573656e740000000000000000006044820152606490fd5b34610293575f3660031901126102935760206001600160401b0360015416604051908152f35b6040519061102d8261068e565b5f8252565b34610293575f366003190112610293576104ae604051611051816106a9565b60058152640352e302e360dc1b60208201526040519182916020835260208301906103ed565b34610293575f3660031901126102935760206040516120008152f35b34610293575f36600319011261029357602063ffffffff600c5416604051908152f35b34610293575f366003190112610293575f8051602061470c833981519152546040516001600160a01b039091168152602090f35b34610293575f36600319011261029357602063ffffffff600a5416604051908152f35b3461029357602036600319011261029357611126610644565b5f8051602061474c83398151915254906001600160401b0360ff8360401c161592168015908161121f575b6001149081611215575b15908161120c575b50610351575f8051602061474c833981519152805467ffffffffffffffff1916600117905561119690826111e857613445565b61119c57005b5f8051602061474c833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290806020810161034c565b5f8051602061474c833981519152805460ff60401b1916600160401b179055613445565b9050155f611163565b303b15915061115b565b839150611151565b34610293575f3660031901126102935760206040516101008152f35b34610293576020366003190112610293576004355f526005602052602060ff60405f2054166040519015158152f35b34610293575f36600319011261029357600f546040516001600160a01b039091168152602090f35b34610293576020366003190112610293576004356112b781610378565b6112c13633612d6b565b604051906112ce8261068e565b63ffffffff81168083521561134a577f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69161132a611311600a5463ffffffff1690565b9163ffffffff90511663ffffffff19600a541617600a55565b6040805163ffffffff92831681529290911660208301528190810161034c565b60405162461bcd60e51b815260206004820152601d60248201527f4f50503a20696e76616c696420726574656e74696f6e20636f6e6669670000006044820152606490fd5b34610293575f366003190112610293576020600e54604051908152f35b34610293575f366003190112610293576020600854604051908152f35b6004359061ffff8216820361029357565b359061ffff8216820361029357565b3461029357604080600319360112610293576114036113c9565b6024356001600160401b03811161029357611422903690600401610748565b9160045415611505576114353633612d6b565b5f8051602061470c8339815191525461146490611458906001600160a01b031681565b6001600160a01b031690565b81516368fc2b7760e11b815261ffff841662010000176004820152336024820152908290829060449082905afa908115611500575f916114d2575b50156114af5761084d8383613669565b51639a28d60760e01b815233600482015261ffff91909116602482015260449150fd5b6114f29150823d84116114f9575b6114ea81836106f2565b810190612870565b505f61149f565b503d6114e0565b612895565b5163738c481160e01b8152600490fd5b346102935760203660031901126102935761152e610644565b6115383633612d6b565b600f80546001600160a01b0319166001600160a01b0392909216919091179055005b919082604091031261029357604051611572816106a9565b8092803560ff811681036102935782526020908101359161159283610378565b0152565b91906080838203126102935760206115ca604051926115b4846106a9565b604084966115c2838261155a565b86520161155a565b910152565b35906001600160401b038216820361029357565b6001600160401b0381116106895760051b60200190565b9190916040908181850312610293578151611614816106a9565b8094823561162181610378565b8252602092838101356001600160401b039182821161029357019180601f8401121561029357823590611653826115e3565b96611660815198896106f2565b828852868089019360051b8601019482861161029357878101935b86851061168d57505050505050500152565b84358681116102935782019060609081601f198488030112610293578451916116b58361066e565b6116c08c85016113da565b8352858401356116cf81610378565b8c84015283013591888311610293576116ef878d80969581960101610748565b8682015281520194019361167b565b90604060206103919361172584825163ffffffff6020809260ff8151168552015116910152565b015191019063ffffffff6020809260ff8151168552015116910152565b9061042e91602081526117596020820183516116fe565b602082015190611777610140928360a08401526101608301906103ed565b9160c06117bd611798604087015195601f19968787830301858801526103ed565b606087015163ffffffff1660e0860152608087015186868303016101008701526103ed565b60a08601516001600160401b031661012085015294015192828503019101526103ed565b346102935760031960403682011261029357600480356001600160401b0392838211610293576101409082360301126102935761181c610713565b9061182936828501611596565b825260848101358481116102935761184690843691840101610748565b602083015260a48101358481116102935761186690843691840101610748565b604083015261187760c48201610386565b606083015260e48101358481116102935761189790843691840101610748565b60808301526118a961010482016115cf565b60a08301526101248101359084821161029357836118ca9236920101610748565b60c0820152602435928311610293576118ec6118f2926104ae943691016115fa565b906129e1565b60405191829182611742565b34610293575f366003190112610293576020600454604051908152f35b34610293575f3660031901126102935760206004541515604051908152f35b3461029357602080600319360112610293576004908135825415611c99576119623633612d6b565b8015611c885782541461197157005b5f82556003548015611c7757611985612191565b5f5480611c0b5750611995611f9e565b60409060408301526119c26119b26001600160401b034216612104565b6001600160401b031660a0840152565b6119ca6121ce565b600181529283916119da8261380b565b8684019081525f5b838110611b8f57505050506119f6916129e1565b91611a188180850151604051809381926347f5343160e01b8352898301612805565b038173__$6c94be45b3cad4e4a0bccaa807f5821f2b$__5af4908115611500575f91611b72575b505f55611a75611a5960a08501516001600160401b031690565b6001600160401b03166001600160401b03196001541617600155565b611a7d6121e7565b928352820152611a8c81612c9e565b917fcf8be8503aed7d1346a99aa4bcf2fadebd86e033736ff955ae30798c1fa172755c611b2457611abf83600e5461061b565b906120008211611aff575050611af691611adb610de2926139e5565b611aee611ae9600854612120565b600855565b600e5461061b565b61084d5f600355565b604080516306f8bcbb60e51b8152918201928352612000602084015290918291010390fd5b618000611b308461060b565b11611b465750611af691611adb610de2926139e5565b611b4f8361060b565b6040805163048c985b60e51b815292830191825261800060208301528291010390fd5b611b899150823d84116108925761088381836106f2565b5f611a3f565b60019192939450611bff81611bac611ba68261349f565b5061386f565b8a611bb9825161ffff1690565b910151611bee611bcd825163ffffffff1690565b611be2611bd8610720565b61ffff9095168552565b63ffffffff16838e0152565b87820152855190610e588383612258565b500190859392916119e2565b604051630c9b3d8760e21b8152858101918252905f9082908190602001038173__$6c94be45b3cad4e4a0bccaa807f5821f2b$__5af4908115611500575f91611c55575b50611995565b611c7191503d805f833e611c6981836106f2565b81019061295a565b5f611c4f565b60405163783bc0f160e11b81528390fd5b604051636ad43cc360e11b81528390fd5b60405163738c481160e01b81528390fd5b600754811015611ce357600a9060075f52027fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68801905f90565b61055a565b90604051611cf5816106a9565b915460ff8116835260081c63ffffffff166020830152565b90600182811c92168015611d3b575b6020831014611d2757565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611d1c565b9060405191825f8254611d5781611d0d565b908184526020946001916001811690815f14611dc35750600114611d85575b505050610391925003836106f2565b5f90815285812095935091905b818310611dab57505061039193508201015f8080611d76565b85548884018501529485019487945091830191611d92565b9250505061039194925060ff191682840152151560051b8201015f8080611d76565b9060408051611df3816106a9565b809363ffffffff8082541683526001809201805491611e11836115e3565b95611e1f60405197886106f2565b83875260209160208801935f5260205f20915f945b868610611e4957505050505050505060200152565b60028589928451611e598161066e565b86885461ffff8116835260101c1683820152611e76858901611d45565b86820152815201940195019492611e34565b90604051611e95816106a9565b60206115ca60088395604051611eaa816106c4565b604051611eb6816106a9565b611ebf83611ce8565b8152611ecd60018401611ce8565b868201528152611edf60028301611d45565b85820152611eef60038301611d45565b6040820152611f15611f08600484015463ffffffff1690565b63ffffffff166060830152565b611f2160058301611d45565b6080820152611f4d611f3d60068401546001600160401b031690565b6001600160401b031660a0830152565b611f5960078301611d45565b60c0820152855201611de5565b5f806008545b808210611f7a575050600e55565b9091611f8b610e88610e8285611caa565b8101809111610606579160010190611f6c565b604051611faa8161068e565b5f8152905f368137565b60405190611fc1826106a9565b6001825260203681840137565b60405190611fdb826106a9565b600a825260203681840137565b90611ff28261072d565b611fff60405191826106f2565b8281528092612010601f199161072d565b0190602036910137565b1561202157565b60405162461bcd60e51b815260206004820152601a60248201527f4f50503a20696e76616c6964207072657761726d2072616e67650000000000006044820152606490fd5b610400811015611ce357601001905f90565b90600163ffffffff8093160191821161060657565b6040519061209a826106a9565b5f6020838281520152565b604051906120b2826106a9565b816120bb61208d565b815260206115ca61208d565b604051906120d4826106c4565b606060c0838281526120e46120a5565b60208201525f60408201525f838201525f60808201528260a08201520152565b906103e86001600160401b038093160291821691820361060657565b5f1981146106065760010190565b1561213557565b60405162461bcd60e51b815260206004820152602e60248201527f4f50503a20717565756564206d6573736167652065786365656473204d41585f60448201526d454e56454c4f50455f425954455360901b6064820152608490fd5b6040519061219e826106c4565b606060c0836121ab6120a5565b81528260208201528260408201525f838201528260808201525f60a08201520152565b604051906121db826106a9565b60606020835f81520152565b604051906121f4826106a9565b816121fd612191565b815260206115ca6121ce565b90612213826115e3565b61222060405191826106f2565b8281528092612231601f19916115e3565b01905f5b82811061224157505050565b60209061224c6121e7565b82828501015201612235565b8051821015611ce35760209160051b010190565b9190820391821161060657565b634e487b7160e01b5f525f60045260245ffd5b818110612297575050565b5f815560010161228c565b9190601f81116122b157505050565b610391925f5260205f20906020601f840160051c830193106122db575b601f0160051c019061228c565b90915081906122ce565b9190918281146123cc576122f98354611d0d565b6001600160401b0381116106895761231b816123158454611d0d565b846122a2565b5f93601f821160011461235a5761234b92939482915f9261234f575b50508160011b915f199060031b1c19161790565b9055565b015490505f80612337565b61236c601f198316915f5260205f2090565b9461237a845f5260205f2090565b915f5b8181106123b45750958360019596971061239c575b505050811b019055565b01545f1960f88460031b161c191690555f8080612392565b8783015484556001938401939092019160200161237d565b509050565b6123db8154611d0d565b90816123e5575050565b81601f5f93116001146123f6575055565b908083918252612415601f60208420940160051c84016001850161228c565b5555565b600160401b821161068957805482825580831061243557505050565b6001916001600160ff1b0382811683036106065784168403610606575f5260205f209060011b81019260011b015b82811061246f57505050565b805f600292556124808382016123d1565b01612463565b8181146125135781549161249a8383612419565b5f5260205f20905f5260205f20905f905b8382106124b85750505050565b8060019184036124d3575b60028091019301910190916124ab565b8054845461ffff191661ffff919091161784558054845465ffffffff0000191665ffffffff000090911617845561250e8183018584016122e5565b6124c3565b5050565b818103612522575050565b6001808361254a63ffffffff610391965416859063ffffffff1663ffffffff19825416179055565b019101612486565b906126bc5781811415918261256657505050565b60088091846103919561257d575b50019101612517565b8061262d575b5061259460028201600286016122e5565b6125a460038201600386016122e5565b6125d16125b8600483015463ffffffff1690565b600486019063ffffffff1663ffffffff19825416179055565b6125e160058201600586016122e5565b6126176125f860068301546001600160401b031690565b60068601906001600160401b03166001600160401b0319825416179055565b61262760078201600786016122e5565b5f612574565b61268a575b600184016001820190818103612649575b50612583565b8154815460ff191660ff919091161781556126839163ffffffff9054851c1664ffffffff0082549160081b169064ffffffff001916179055565b5f80612643565b8054845460ff191660ff919091161784558054845464ffffffff00191690831c60081b64ffffffff0016178455612632565b612279565b600754801561277f575f19016126d681611caa565b6126bc575f815560015f600183015560096002926126f6600282016123d1565b612702600382016123d1565b5f6004820155612714600582016123d1565b5f6006820155612726600782016123d1565b5f600882015501908154915f815582612743575b50505050600755565b6001600160ff1b0383168303610606575f5260205f209160011b8201915b8281101561273a57805f8592556127798382016123d1565b01612761565b634e487b7160e01b5f52603160045260245ffd5b1561279a57565b60405162461bcd60e51b815260206004820152603a60248201527f4f50503a20656e76656c6f70652065786365656473204d41585f454e56454c4f60448201527f50455f42595445532028657374696d61746f72206472696674290000000000006064820152608490fd5b90602061042e9281815201906103ed565b1561281d57565b60405162461bcd60e51b815260206004820152602560248201527f4f50503a207265636f7264207374696c6c20696e20726574656e74696f6e2077604482015264696e646f7760d81b6064820152608490fd5b91908260409103126102935760208251612889816109ca565b92015161042e81610378565b6040513d5f823e3d90fd5b90816020910312610293575190565b90602080835260609060608401908063ffffffff94858151168288015201519460408080830152865180945260808201908360808660051b8501019801955f935b8685106129035750505050505050505090565b909192939495969799988680612945600193868f89908f607f198a8603018d52519061ffff8251168552868201511686850152015191818a82015201906103ed565b9b9c9a019897969190910194019291906128f0565b602081830312610293578051906001600160401b038211610293570181601f820112156102935780519061298d8261072d565b9261299b60405194856106f2565b8284526020838301011161029357815f9260208093018386015e8301015290565b90916129d361042e936040845260408401906103ed565b9160208184039101526103ed565b906129ea612191565b506040805163765809ab60e11b81526004926020929173__$6c94be45b3cad4e4a0bccaa807f5821f2b$__9190848180612a26858a83016128af565b0381865af4908115611500575f91612c81575b5060808701908582518051155f14612bfd5750508451630c9b3d8760e21b8152878101918252905f90829081906020010381875af492831561150057612aa693612a9993612a8f935f92612be1575b5052613701565b5163ffffffff1690565b63ffffffff166060870152565b815162409a0960e81b81525f8180612ac089898301611742565b0381855af4908115611500575f91612bc7575b508584018051805180612b8c57505052815163b93612a760e01b815293838580612aff89858301611742565b0381855af492831561150057612b38955f958695612b6d575b505051630c9b3d8760e21b81529081019283529384928391829160200190565b03915af4908115611500575f91612b53575b5060c082015290565b612b6791503d805f833e611c6981836106f2565b5f612b4a565b612b84929550803d106108925761088381836106f2565b925f80612b18565b915093925084849695929601209085519086012003612bac575050505090565b51630ca2e06d60e11b815292839261082a92919084016129bc565b612bdb91503d805f833e611c6981836106f2565b5f612ad3565b612bf69192503d805f833e611c6981836106f2565b905f612a88565b90919350612c1e92508551809381926347f5343160e01b83528a8301612805565b0381865af4908115611500575f91612c64575b50818103612c40575050612aa6565b8351631d97afef60e31b815280870192835260208301919091529081906040010390fd5b612c7b9150853d87116108925761088381836106f2565b5f612c31565b612c989150853d87116108925761088381836106f2565b5f612a39565b61010090602080910190602082510151515f925b818410612cc0575050505090565b90919293601880820191828111610606576040612ce1888887510151612258565b5101515101018091116106065793600101929190612cb2565b600411610293575f90600490565b6001600160e01b03199035818116939260048110612d2557505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f8051602061470c8339815191528054612da3906001600160a01b0316612d9a612d9486612cfa565b90612d08565b90843091613b47565b9015612db0575b50505050565b63ffffffff1615612e67575f8051602061470c833981519152805460ff60a01b1916600160a01b179055612df190611458908190546001600160a01b031690565b91823b1561029357612e1c925f9283604051809681958294634a63ebf760e11b845260048401612d34565b03925af1801561150057612e4e575b505f8051602061470c833981519152805460ff60a01b191690555f808080612daa565b80612e5b612e61926106df565b80610289565b5f612e2b565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f8051602061470c83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b600d5480612efa5750604051612ef48161068e565b5f815290565b60405190630c9b3d8760e21b825260048201525f8160248173__$6c94be45b3cad4e4a0bccaa807f5821f2b$__5af4908115611500575f91612f3a575090565b61042e91503d805f833e611c6981836106f2565b805191908290602001825e015f815290565b612f68611020565b612f70613dd9565b92604080518095602094858301612f8691612f4e565b612f8f91612f4e565b0394601f19958681018252612fa490826106f2565b8451612faf90613c21565b908251918291868301612fc191612f4e565b612fca91612f4e565b038681018252612fda90826106f2565b612fe2613df0565b908251918291868301612ff491612f4e565b612ffd91612f4e565b03868101825261300d90826106f2565b8385015161301a90613c5d565b90815161302d906001600160401b031690565b61303690613f00565b90835191829187830161304891612f4e565b61305191612f4e565b03878101825261306190826106f2565b825191829186830161307291612f4e565b61307b91612f4e565b03868101825261308b90826106f2565b613093613e03565b9082519182918683016130a591612f4e565b6130ae91612f4e565b0386810182526130be90826106f2565b848201516001600160401b03166130d490613f00565b9082519182918683016130e691612f4e565b6130ef91612f4e565b0386810182526130ff90826106f2565b613107613e16565b90825191829186830161311991612f4e565b61312291612f4e565b03868101825261313290826106f2565b60608501516131469063ffffffff16613f00565b90825191829186830161315891612f4e565b61316191612f4e565b03868101825261317190826106f2565b613179613e29565b90825191829186830161318b91612f4e565b61319491612f4e565b0386810182526131a490826106f2565b60808501516131b89063ffffffff16613f00565b9082519182918683016131ca91612f4e565b6131d391612f4e565b0386810182526131e390826106f2565b6131eb613e3c565b9082519182918683016131fd91612f4e565b61320691612f4e565b03868101825261321690826106f2565b60a085015161322490613c21565b90825191829186830161323691612f4e565b61323f91612f4e565b03868101825261324f90826106f2565b915f925b60c086018051518510156133195761329961330f60c0936133036132b76132b18a6001978f6132ab908f61329f8f61329961328c613e81565b91519b8c94850190612f4e565b90612f4e565b039081018852876106f2565b51612258565b51613f7f565b6132f58d8c6132e98c6132996132dc6132d788516001600160401b031690565b613f00565b9151988994850190612f4e565b039081018552846106f2565b88519485938c850190612f4e565b038a81018352826106f2565b9401939050613253565b5095509350505050565b63ffffffff8082165f52600960205260405f20908154166133845761335a600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b6133a09063ffffffff91829182600a541690614649565b169081151590816133c9575b506133b45750565b5f5260096020525f6001604082208281550155565b9050815f52600960205260405f20541615155f6133ac565b805190601f8201908183116106065760059160051c915f5b83811061341c575050505063ffffffff610410911663ffffffff19825416179055565b80821b83016020015160108201556001016133f9565b61042e9063ffffffff600a541690614649565b61346e9061345161407f565b61345961407f565b61346161407f565b61346961407f565b612e87565b61347661407f565b61347e61407f565b60c860405161348c8161068e565b5260c863ffffffff19600a541617600a55565b600254811015611ce35760025f5260011b7f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01905f90565b91909182516001600160401b038111610689576134f8816123158454611d0d565b602080601f83116001146135345750819061234b9394955f926135295750508160011b915f199060031b1c19161790565b015190505f80612337565b90601f19831695613548855f5260205f2090565b925f905b8882106135825750508360019596971061356a57505050811b019055565b01515f1960f88460031b161c191690555f8080612392565b8060018596829496860151815501950193019061354c565b9291906126bc578051835461ffff191661ffff91909116178355600180930190602080910151938451916001600160401b038311610689576135e6836135e08654611d0d565b866122a2565b602091601f8411600114613618575050819061234b9394955f926135295750508160011b915f199060031b1c19161790565b95601f9291921984169661362f865f5260205f2090565b935f915b898310613652575050508360019596971061356a57505050811b019055565b838501518655948501949381019391810191613633565b600254916003548084115f146136be575061039192506136976040519261368f846106a9565b61ffff168352565b60208201526136b86003546136b36136ae82612120565b600355565b61349f565b9061359a565b6136c790612120565b60035561ffff604051926136da846106a9565b1682526020820152600160401b821015610689576136b8826001610391940160025561349f565b6137349161370d611020565b90613716613ea1565b61329961377a60409260405197889161329960209889850190612f4e565b0396613748601f19988981018352826106f2565b61376e61375f6132d76104548a5163ffffffff1690565b60405194859389850190612f4e565b038781018352826106f2565b915f925b8486018051518510156133195761329961380161329961330389956137f38d6137cd6137c78d60019a6132ab8f6137bb8f9161329961328c613df0565b038681018952886106f2565b516140ad565b926132e96137e56132d786516001600160401b031690565b8d519788938d850190612f4e565b885194859388850190612f4e565b940193905061377e565b90613815826115e3565b60409061382560405191826106f2565b8381528093613836601f19916115e3565b01915f5b8381106138475750505050565b60209082516138558161066e565b5f8152825f8183015260608583015282860101520161383a565b9060405161387c816106a9565b60206115ca6001839561ffff815416855201611d45565b8151815463ffffffff191663ffffffff91821617825591600180920160208092015191808351936138c48585612419565b01915f52805f205f925b8484106138de5750505050505050565b8051805183548583015161ffff90921665ffffffffffff199091161790891660101b65ffffffff0000161783556040015180519187840191906001600160401b03841161068957828692613937866123158d9754611d0d565b8390601f871160011461397757958061396892600297985f926135295750508160011b915f199060031b1c19161790565b90555b019201930192906138ce565b9590601f1982169661398c845f5260205f2090565b975f5b8181106139cc575091879891846002999594106139b4575b505050811b01905561396b565b01515f1960f88460031b161c191690555f80806139a7565b838301518a5597909801978d978b97938401930161398f565b600754600160401b81101561068957806001613a049201600755611caa565b6126bc576008602083613b3e60c06103919651613a9e815186613a608251613a3a60ff8251168d9060ff1660ff19825416179055565b909101518a5464ffffffff00191660089190911b64ffffffff0016178a5563ffffffff90565b878060018c01930151613a8160ff825116859060ff1660ff19825416179055565b0151825464ffffffff001916911660081b64ffffffff0016179055565b613aae85820151600289016134d7565b613abf6040820151600389016134d7565b613aec613ad3606083015163ffffffff1690565b600489019063ffffffff1663ffffffff19825416179055565b613afd6080820151600589016134d7565b613b33613b1460a08301516001600160401b031690565b60068901906001600160401b03166001600160401b0319825416179055565b0151600786016134d7565b01519101613893565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b03841117610689576040935f9385528380528360205251915afa613bb657565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613c06575b505061099c57565b5f8051602061472c8339815191525416141590505f80613bfe565b61042e61329991613c4f613c3e6001600160401b03835116613f00565b916040519485936020850190612f4e565b03601f1981018352826106f2565b613c65611020565b90613c6e613dd9565b6040805193846020938492838301613c8591612f4e565b613c8e91612f4e565b0393601f19948581018752613ca390876106f2565b8051613cae90614257565b958651613cc1906001600160401b031690565b613cca90613f00565b908451918291858301613cdc91612f4e565b613ce591612f4e565b038681018252613cf590826106f2565b8351968791848301613d0691612f4e565b613d0f91612f4e565b038581018752613d1f90876106f2565b613d27613df0565b958351968791848301613d3991612f4e565b613d4291612f4e565b038581018752613d5290876106f2565b0151613d5d90614257565b908151613d70906001600160401b031690565b613d7990613f00565b948151958691858301613d8b91612f4e565b613d9491612f4e565b038481018652613da490866106f2565b519384928301613db391612f4e565b613dbc91612f4e565b03908101825261042e90826106f2565b805115611ce35760200190565b613de1611fb4565b600a613dec82613dcc565b5390565b613df8611fb4565b6012613dec82613dcc565b613e0b611fb4565b6028613dec82613dcc565b613e1e611fb4565b6030613dec82613dcc565b613e31611fb4565b6038613dec82613dcc565b613e44611fce565b60a25f5b607f80831115613e6d5790608060019284161760208286010153019060071c90613e48565b50607f600192166020828501015301815290565b613e89611fce565b61014260015f60c26020850153019060071c90613e48565b613ea9611fb4565b6008613dec82613dcc565b613ebc611fb4565b6010613dec82613dcc565b613ecf611fb4565b601a613dec82613dcc565b613ee2611fb4565b6032613dec82613dcc565b613ef5611fb4565b6042613dec82613dcc565b9060809160806001600160401b03821610613f5b57613f1d611fce565b5f5b607f80841115613f4357908560019285161760208285010153019160071c91613f1f565b5090929350607f600192166020828501015301815290565b909150613f66611fb4565b9060f81b6001600160f81b0319165f1a613dec82613dcc565b613f87611020565b90613f90613dd9565b6040805193846020938492838301613fa791612f4e565b613fb091612f4e565b0393601f19948581018752613fc590876106f2565b8051613fd09061430f565b958651613fe3906001600160401b031690565b613fec90613f00565b908451918291858301613ffe91612f4e565b61400791612f4e565b03868101825261401790826106f2565b835196879184830161402891612f4e565b61403191612f4e565b03858101875261404190876106f2565b614049613df0565b95835196879184830161405b91612f4e565b61406491612f4e565b03858101875261407490876106f2565b0151613d5d90613701565b60ff5f8051602061474c8339815191525460401c161561409b57565b604051631afcd79f60e31b8152600490fd5b61042e6140ee613299926141a66141926132996141996140cb611020565b946140d4613ea1565b9060409485928351998a9161329960209b8c850190612f4e565b0398614102601f199a8b81018352826106f2565b614141896141356141256132d761411e61411e895161ffff1690565b61ffff1690565b6132998851958694850190612f4e565b038b81018352826106f2565b61415089614135614125613eb4565b61416d896141356141256132d76104548489015163ffffffff1690565b614186614178613ec7565b85519d8e938c850190612f4e565b038981018c528b6106f2565b0151613c21565b9151968794850190612f4e565b039081018352826106f2565b90813b15614236575f8051602061472c83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a280511561421b5761421891614668565b50565b50503461422457565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b61042e6132996132996141a66142ff6132d76104546020613299986142f46132996142ce614283611020565b61429d61428e613ea1565b6040519d8e9389850190612f4e565b039a6142b1601f199c8d81018352826106f2565b61413561375f6132d76142c86142c8895160ff1690565b60ff1690565b6142e86142d9613eb4565b6040519a8b9387850190612f4e565b038981018952886106f2565b015163ffffffff1690565b6040519586936020850190612f4e565b614317611020565b90614320613dd9565b9160408051809460209384830161433691612f4e565b61433f91612f4e565b0392601f1993848101865261435490866106f2565b805161435f90613c5d565b948551614372906001600160401b031690565b61437b90613f00565b90835191829186830161438d91612f4e565b61439691612f4e565b0385810182526143a690826106f2565b82519586918583016143b791612f4e565b6143c091612f4e565b0384810186526143d090866106f2565b6143d8613df0565b9482519586918583016143ea91612f4e565b6143f391612f4e565b03848101865261440390866106f2565b8281015161441090613c21565b94825195869185830161442291612f4e565b61442b91612f4e565b03848101865261443b90866106f2565b614443613ec7565b94825195869185830161445591612f4e565b61445e91612f4e565b03848101865261446e90866106f2565b8181015161447b90613c21565b94825195869185830161448d91612f4e565b61449691612f4e565b0384810186526144a690866106f2565b6144ae613e03565b9482519586918583016144c091612f4e565b6144c991612f4e565b0384810186526144d990866106f2565b60608101516144ed9063ffffffff16613f00565b9482519586918583016144ff91612f4e565b61450891612f4e565b03848101865261451890866106f2565b614520613eda565b94825195869185830161453291612f4e565b61453b91612f4e565b03848101865261454b90866106f2565b608081015161455990613c21565b94825195869185830161456b91612f4e565b61457491612f4e565b03848101865261458490866106f2565b61458c613e29565b94825195869185830161459e91612f4e565b6145a791612f4e565b0384810186526145b790866106f2565b60a08101516001600160401b03166145ce90613f00565b9482519586918583016145e091612f4e565b6145e991612f4e565b0384810186526145f990866106f2565b614601613eed565b94825195869185830161461391612f4e565b61461c91612f4e565b03848101865261462c90866106f2565b60c0015161463990613c21565b90519384928301613db391612f4e565b63ffffffff9190828216838216111561466157031690565b5050505f90565b5f8061042e93602081519101845af43d156146a4573d916146888361072d565b9261469660405194856106f2565b83523d5f602085013e6146a8565b6060915b906146cf57508051156146bd57602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614702575b6146e0575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156146d856fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212206d0b8bf89bf266756ee2e2b3eefbac5d0ed3b41f57b9ea9047b840c51040157264736f6c63430008190033", "linkReferences": { "contracts/outpost/OPPCommon.sol": { - "OPPCommon": [ + "OPPCommonV2": [ { "length": 20, "start": 6889 @@ -1414,7 +1468,7 @@ }, "deployedLinkReferences": { "contracts/outpost/OPPCommon.sol": { - "OPPCommon": [ + "OPPCommonV2": [ { "length": 20, "start": 6684 diff --git a/tests/fixtures/ethereum-abi-opp-inbound-current.json b/tests/fixtures/ethereum-abi-opp-inbound-current.json index 7476b2fd11..9a41bfecb4 100644 --- a/tests/fixtures/ethereum-abi-opp-inbound-current.json +++ b/tests/fixtures/ethereum-abi-opp-inbound-current.json @@ -100,6 +100,28 @@ "name": "InvalidInitialization", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "position", + "type": "uint256" + } + ], + "name": "MalformedEnvelope", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "position", + "type": "uint256" + } + ], + "name": "MalformedEnvelope", + "type": "error" + }, { "inputs": [ { @@ -116,6 +138,22 @@ "name": "NotInitializing", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "nextEpochIndex", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "deliveries", + "type": "uint32" + } + ], + "name": "OPP_BootstrapWindowClosed", + "type": "error" + }, { "inputs": [ { @@ -132,6 +170,22 @@ "name": "OPP_DigestMismatch", "type": "error" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "gasLeft", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "floor", + "type": "uint256" + } + ], + "name": "OPP_DispatchUnderfunded", + "type": "error" + }, { "inputs": [ { @@ -228,6 +282,17 @@ "name": "OPP_InboundEnvelopeStillInRetention", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "OPP_InternalDispatchOnly", + "type": "error" + }, { "inputs": [ { @@ -928,33 +993,27 @@ "anonymous": false, "inputs": [ { - "indexed": false, - "internalType": "address", - "name": "newReserveManager", - "type": "address" - }, - { - "indexed": false, + "indexed": true, "internalType": "address", - "name": "oldReserveManager", + "name": "implementation", "type": "address" } ], - "name": "ReserveManagerAddressSet", + "name": "Upgraded", "type": "event" }, { - "anonymous": false, - "inputs": [ + "inputs": [], + "name": "FIRST_INBOUND_EPOCH_INDEX", + "outputs": [ { - "indexed": true, - "internalType": "address", - "name": "implementation", - "type": "address" + "internalType": "uint32", + "name": "", + "type": "uint32" } ], - "name": "Upgraded", - "type": "event" + "stateMutability": "view", + "type": "function" }, { "inputs": [], @@ -1282,6 +1341,29 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "AttestationType", + "name": "attestationType", + "type": "uint16" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "uint32", + "name": "epochIndex_", + "type": "uint32" + } + ], + "name": "handleInternalAttestation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1396,6 +1478,60 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint32", + "name": "activeGroupIndex", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "epochIndex", + "type": "uint32" + }, + { + "components": [ + { + "components": [ + { + "internalType": "ChainKind", + "name": "kind", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "address_", + "type": "bytes" + } + ], + "internalType": "struct ChainAddress[]", + "name": "operators", + "type": "tuple[]" + } + ], + "internalType": "struct BatchOperatorGroup[]", + "name": "groups", + "type": "tuple[]" + }, + { + "internalType": "uint32", + "name": "epochDurationSec", + "type": "uint32" + } + ], + "internalType": "struct BatchOperatorGroups", + "name": "initialGroups", + "type": "tuple" + } + ], + "name": "installInitialRoster", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -1452,19 +1588,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "lastMessageID", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "nextEpochIndex", @@ -1652,80 +1775,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "pendingEpoch", - "outputs": [ - { - "internalType": "bytes", - "name": "envelopeHash", - "type": "bytes" - }, - { - "components": [ - { - "components": [ - { - "internalType": "ChainKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "id", - "type": "uint32" - } - ], - "internalType": "struct ChainId", - "name": "start", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "ChainKind", - "name": "kind", - "type": "uint8" - }, - { - "internalType": "uint32", - "name": "id", - "type": "uint32" - } - ], - "internalType": "struct ChainId", - "name": "end", - "type": "tuple" - } - ], - "internalType": "struct Endpoints", - "name": "endpoints", - "type": "tuple" - }, - { - "internalType": "uint64", - "name": "epochTimestamp", - "type": "uint64" - }, - { - "internalType": "uint32", - "name": "epochIndex", - "type": "uint32" - }, - { - "internalType": "uint32", - "name": "epochEnvelopeIndex", - "type": "uint32" - }, - { - "internalType": "bytes", - "name": "previousEnvelopeHash", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "pendingEpochHash", @@ -1739,19 +1788,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "pendingMessageCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "previousEpochHash", @@ -1810,19 +1846,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "reserveManagerAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "rosterEpochIndex", @@ -1919,19 +1942,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "address", - "name": "newReserveManager", - "type": "address" - } - ], - "name": "setReserveManagerAddress", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -1951,30 +1961,30 @@ "type": "function" } ], - "bytecode": "0x60a0806040523460da57306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1660cb57506001600160401b036002600160401b0319828216016087575b5f805463ffffffff19166001178155600c55604051614cb090816100df8239608051818181610c6b01526139d40152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80806056565b63f92ee8a960e01b8152600490fd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80624e356a146103435780630fa92c531461033e578063151b8f4e146103395780631bdf0a72146103345780631d7a88271461032f57806320857e651461032a57806322fe388b14610325578063239e1d67146103205780632945698b1461031b5780632c92892f146103165780632f4d9fa9146103115780633367cca51461030c57806336051135146103075780633915ba63146103025780634ab47f43146102fd5780634f1ef286146102f85780635126a52d146102f357806352d1902d146102ee578063533ae1cc146102e95780635701a93f146102e45780635c0c8f44146102df5780635d4e25ce146102da5780635ff8195e146102d5578063621fbd59146102d05780637a9e5e4b146102cb5780637b778122146102c65780637e283cea146102c157806380e643b6146102bc5780638b75e929146102b75780638bdebf21146102b25780638d9f616b146102ad5780638fb36037146102a857806393ec06be146102a357806399b1a19c1461029e578063ad3cb1cc14610299578063af243e8914610294578063b41565b81461028f578063bf7e214f1461028a578063c0d0373e14610285578063c268863f14610280578063c45fdcb71461027b578063cd2ad3fb14610276578063d0841f2214610271578063d1ad8d011461026c578063d3f0730614610267578063d5b5272a14610262578063e3dff2af1461025d578063e55ba57614610258578063ea8dbafe14610253578063f552501a1461024e5763fdfd07df14610249575f80fd5b611910565b6117ce565b6115de565b61159f565b611582565b6114d4565b6114a4565b61144a565b611422565b6113be565b61135b565b6112e3565b6111cd565b611199565b6110ed565b6110ba565b611075565b61102c565b611010565b610fc0565b610f81565b610f5b565b610f20565b610efc565b610e80565b610e27565b610dad565b610d90565b610d6d565b610d50565b610d0b565b610ce8565b610cc2565b610c59565b610c27565b610b1e565b610af1565b610abf565b610a75565b610a3f565b610a22565b610a05565b6109d8565b6109b6565b61099b565b6108ed565b6106ad565b610471565b61044e565b610409565b610367565b63ffffffff81160361035657565b5f80fd5b359061036582610348565b565b346103565760403660031901126103565760043561038481610348565b6001600160401b036024358181116103565736602382011215610356578060040135918211610356573660248383010111610356577f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0091825c6103f7575f9360246103f4936001865d0190611956565b5d005b604051633ee5aeb560e01b8152600490fd5b3461035657602036600319011261035657602060043561042881610348565b63ffffffff8091165f52600e825260405f205416604051908152f35b5f91031261035657565b34610356575f36600319011261035657602063ffffffff600f5416604051908152f35b34610356575f366003190112610356576001600160401b0363ffffffff6104fb815f5416805f52600e6020528260405f205416906104ae8161291f565b600f549260405196879685169460201c169286936001600160401b039060809497969260a087019863ffffffff8096818094168a5216602089015216604087015216606085015216910152565b0390f35b600435906001600160a01b038216820361035657565b602435906001600160a01b038216820361035657565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b0382111761055a57604052565b61052b565b602081019081106001600160401b0382111761055a57604052565b604081019081106001600160401b0382111761055a57604052565b606081019081106001600160401b0382111761055a57604052565b6001600160401b03811161055a57604052565b60c081019081106001600160401b0382111761055a57604052565b60e081019081106001600160401b0382111761055a57604052565b90601f801991011681019081106001600160401b0382111761055a57604052565b604051906103658261055f565b6001600160401b03811161055a5760051b60200190565b6001600160401b03811161055a57601f01601f191660200190565b9291926106658261063e565b9161067360405193846105f9565b829481845281830111610356578281602093845f960137010152565b9080601f83011215610356578160206106aa93359101610659565b90565b3461035657600319604036820112610356576106c76104ff565b602435916001600160401b03831161035657608090833603011261035657604051906106f28261053f565b826004013561070081610348565b8252602483013561071081610348565b60208301526044926044810135936001600160401b03851161035657366023868401011215610356576004858301013561074981610627565b9561075760405197886105f9565b81875260208701903660248460051b83880101011161035657602481860101915b60248460051b838801010183106107ac576107aa88886107a060648a8e60408601520161035a565b6060830152611b82565b005b82356001600160401b03811161035657828701016020602319823603011261035657604051906107db8261055f565b60248101356001600160401b0381116103565736604382840101121561035657602481830101359061080c82610627565b9261081a60405194856105f9565b8284526020840190368b8560051b858401010111610356578a83820101915b8b8560051b8584010101831061085f575050509183525050815260209283019201610778565b82356001600160401b038111610356576040838601820136036043190112610356576040519161088e8361057a565b8d8287860101013560ff81168103610356578352606482878601010135926001600160401b038411610356576108d28f9360209594869536928b8a0101010161068f565b83820152815201920191610839565b61ffff81160361035657565b346103565760403660031901126103565760043561090a816108e1565b61ffff610915610515565b916109203633612ac6565b165f818152600460209081526040918290205482516001600160a01b03868116825291821692810192909252929182917f46d568db0216a0215fe7f56360f9fa1a002c4bf0773add9cd62cd06eff1601899190a25f52600460205260405f2091166bffffffffffffffffffffffff60a01b8254161790555f80f35b34610356575f36600319011261035657602060405160078152f35b34610356575f36600319011261035657602063ffffffff5f5416604051908152f35b34610356576020366003190112610356576004355f52601b602052602060ff60405f205416604051908152f35b34610356575f366003190112610356576020600c54604051908152f35b34610356575f366003190112610356576020601f54604051908152f35b34610356576020366003190112610356576020610a6b610a5d6104ff565b63ffffffff5f541690612326565b6040519015158152f35b34610356576020366003190112610356576001600160401b0363ffffffff6104fb815f5416805f52601760205260405f206004355f526020528260405f205416906104ae8161291f565b34610356576020366003190112610356576004355f526012602052602060018060a01b0360405f205416604051908152f35b34610356576020366003190112610356576004355f52601a602052602060ff60405f205416604051908152f35b604036600319011261035657610b326104ff565b6024356001600160401b03811161035657610b5190369060040161068f565b90610b5a6139ca565b610b643633612ac6565b610b6c6139ca565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610bf6575b50610bbe57604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f80516020614c1b8339815191528303610bdd576107aa9250614166565b604051632a87526960e21b815260048101849052602490fd5b610c1991945060203d602011610c20575b610c1181836105f9565b8101906125ae565b925f610b94565b503d610c07565b34610356576020366003190112610356576004355f526013602052602060018060a01b0360405f205416604051908152f35b34610356575f366003190112610356577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610cb05760206040515f80516020614c1b8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610356575f36600319011261035657602063ffffffff601e5460081c16604051908152f35b34610356575f36600319011261035657602063ffffffff60155416604051908152f35b3461035657602036600319011261035657610d246104ff565b610d2e3633612ac6565b600380546001600160a01b0319166001600160a01b0392909216919091179055005b34610356575f366003190112610356576020600254604051908152f35b34610356575f36600319011261035657602063ffffffff60115416604051908152f35b34610356575f366003190112610356576020600154604051908152f35b3461035657602036600319011261035657610dc66104ff565b5f80516020614bfb833981519152546001600160a01b03919082163303610e1057803b15610df7576107aa90612bdc565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b34610356576040366003190112610356576020610e77600435610e4981610348565b63ffffffff610e56610515565b91165f52600d835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b34610356576020366003190112610356577f2b797207c3f71ed55e7ee9f751f9d4e23795bff49d40a0d3d05e160e1257798d610eba6104ff565b610ec43633612ac6565b601680546001600160a01b039283166001600160a01b031982168117909255604080519283529216602082015290819081015b0390a1005b34610356575f36600319011261035657602060115460ff60405191831c1615158152f35b34610356576020366003190112610356576040610f47600435610f4281610348565b61228f565b63ffffffff83519216825215156020820152f35b34610356575f3660031901126103565760206001600160401b0360185416604051908152f35b346103565760203660031901126103565761ffff600435610fa1816108e1565b165f526004602052602060018060a01b0360405f205416604051908152f35b34610356575f366003190112610356575f80516020614bfb8339815191525460a01c60ff1615611008576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610ff5565b34610356575f3660031901126103565760206040516180008152f35b34610356575f36600319011261035657602060ff600f5460601c166040519015158152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b34610356575f366003190112610356576104fb6040516110948161057a565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611051565b34610356576020366003190112610356576004355f52601960205260206001600160401b0360405f205416604051908152f35b34610356576020366003190112610356576104fb60043561110d81610348565b60405f81805161111c81610595565b828152826020820152015263ffffffff8092165f5260146020526001815f206001600160401b0383519461114f86610595565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610356575f366003190112610356575f80516020614bfb833981519152546040516001600160a01b039091168152602090f35b34610356576020366003190112610356576004356111ea81610348565b6111f43633612ac6565b63ffffffff90816112206112168363ffffffff165f52601460205260405f2090565b5463ffffffff1690565b16156112c35761124461123f61123a5f5463ffffffff1690565b612c34565b612c6b565b828216928116831161129d575061126c6112779163ffffffff165f52601460205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b604051631cc9a6c760e21b815263ffffffff928316600482015291166024820152604490fd5b604051630529520560e31b815263ffffffff919091166004820152602490fd5b34610356575f366003190112610356576003546040516001600160a01b039091168152602090f35b634e487b7160e01b5f52603260045260245ffd5b6010548110156113415760105f525f80516020614c3b83398151915201905f90565b61130b565b8054821015611341575f5260205f2001905f90565b3461035657604036600319011261035657600435602435906010548110156103565760105f525f80516020614c3b833981519152018054821015610356576020916113a591611346565b905460405160039290921b1c6001600160a01b03168152f35b346103565760203660031901126103565763ffffffff6004356113e081610348565b165f5260208052608060405f205460ff604051918181161515835261ffff8160081c166020840152818160181c161515604084015260201c1615156060820152f35b34610356575f366003190112610356576020600f546001600160401b0360405191831c168152f35b3461035657602036600319011261035657606060043561146981610348565b63ffffffff8091165f52601460205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b34610356576040366003190112610356576020610a6b6114c26104ff565b602435906114cf82610348565b612326565b34610356576020366003190112610356576004356114f181610348565b6114fb3633612ac6565b6040516115078161055f565b63ffffffff908183168082521561157057601580549151831663ffffffff1983161790556040805163ffffffff93909216831682529290911660208201527f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69181908101610ef7565b604051639ca50c6160e01b8152600490fd5b34610356575f366003190112610356576020600b54604051908152f35b346103565760203660031901126103565763ffffffff6004356115c181610348565b6115cb3633612ac6565b1663ffffffff19600f541617600f555f80f35b34610356575f366003190112610356576016546040516001600160a01b039091168152602090f35b604051905f6009546001918160011c92600183169081156116eb575b6020906020861083146116d757858852879460208601939081156116b85750600114611658575b505050610365925003836105f9565b92509361168660095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af90565b945f935b8285106116a2575050506103659350015f8080611649565b865485850152958601958895509381019361168a565b925050506103659491925060ff19168252151560051b015f8080611649565b634e487b7160e01b5f52602260045260245ffd5b93607f1693611622565b604051906117028261057a565b60065460ff8116835260081c63ffffffff166020830152565b604051906117288261057a565b60075460ff8116835260081c63ffffffff166020830152565b926001600160401b03906117a760206117696106aa9a98969995610120808a52890190611051565b9861178a828901825163ffffffff6020809260ff8151168552015116910152565b0151805160ff1660608801526020015163ffffffff166080870152565b1660a084015263ffffffff80921660c08401521660e0820152610100818403910152611051565b34610356575f366003190112610356576040515f6005546001918160011c9260018316908115611906575b6020906020861083146116d757858752869460208601939081156118e75750600114611887575b8461182d818603826105f9565b6040516118398161057a565b6118416116f5565b815261184b61171b565b60208201526104fb60085463ffffffff611863611606565b916040519586956001600160401b03848460601c16948460401c1693169187611741565b9250936118b560055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db090565b945f935b8285106118d15750505061182d9350015f8080611820565b86548585015295860195879550938101936118b9565b9250505061182d9491925060ff19168252151560051b015f8080611820565b93607f16936117f9565b3461035657604036600319011261035657602060043561192f81610348565b63ffffffff8091165f526017825260405f206024355f52825260405f205416604051908152f35b9092919261197461196b5f5463ffffffff1690565b63ffffffff1690565b63ffffffff90818416908110611b7a5761199b8463ffffffff165f526020805260405f2090565b5460ff81169081611b6b575b50611ae7575f5463ffffffff169082821603611ac0575060115460ff8160201c16611a4b575b50506119e06119dc8333612326565b1590565b611a335783158015611a28575b611a0757611a019061036593943691610659565b906125d2565b604051630c6f042160e31b8152600481018590526180006024820152604490fd5b5061800084116119ed565b604051632af0051560e21b8152336004820152602490fd5b611a548461228f565b90506119cd57610bba84611a71601e5463ffffffff9060081c1690565b90611a8160105463ffffffff1690565b604051631bf926d360e11b815263ffffffff92831660048201529282166024840152939094168416604482015292909116606483015281906084820190565b6040516307d3e83d60e51b815263ffffffff91821660048201529084166024820152604490fd5b505092611b17611b058363ffffffff165f52600d60205260405f2090565b335f9081526020919091526040902090565b5493600b54809503611a3357611b2e913691610659565b908151602083012093808503611b4a57506103659293506123d8565b6040516340d6b4cd60e01b8152600481019190915260248101859052604490fd5b60ff915060201c16155f6119a7565b505050509050565b905f80516020614c5b8339815191529081549260ff8460401c1615936001600160401b03811680159081611c83575b6001149081611c79575b159081611c70575b50611c5e5767ffffffffffffffff1916600117909255611be79183611c3a57611c8b565b611bed57565b5f80516020614c5b833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1565b5f80516020614c5b833981519152805460ff60401b1916600160401b179055611c8b565b60405163f92ee8a960e01b8152600490fd5b9050155f611bc3565b303b159150611bbb565b869150611bb1565b611caf90929192611c9a61391b565b611ca261391b565b611caa61391b565b612bdc565b611cb761391b565b5f805463ffffffff19166001908117909155600f80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161790556040928381018051519182156121f357606081019063ffffffff9485611d1b845163ffffffff1690565b16156121ae5784611d3361196b845163ffffffff1690565b1015612169575f945f5b81811061210e5750611d4e8661296d565b5f805b838110611ed85750505f905f5b838110611e65575050505050611e3b92611de2611dcc7ff485fcf1271cbcd2319ebf860c827016cef79959673d605215dcd74ed0629ea4989994611dc2611dac611e1a965163ffffffff1690565b63ffffffff1663ffffffff196011541617601155565b5163ffffffff1690565b63ffffffff1663ffffffff19600f541617600f55565b611dfc64010000000064ff00000000196011541617601155565b611e0f64ffffffff0019601e5416601e55565b515163ffffffff1690565b905163ffffffff918216815293909216909116602083015281906040820190565b0390a1610365611e4961061a565b60c8815263ffffffff90511663ffffffff196015541617601555565b80611e7286928a51612959565b515151611e7d6129e4565b5050611e888261131f565b505f5b828110611e9b5750505001611d5e565b611ecd611ec7611eba611eb38a98959697989a6129d6565b9989612959565b516001600160a01b031690565b84612a1a565b019087939291611e8b565b80611ee586928a51612959565b515183905f845b611efa575b50505001611d51565b90919293825182101561210757506002611f2b611f25611f25611f1d8587612959565b515160ff1690565b60ff1690565b036120c2576020601481611f3f8486612959565b510151510361207d57611f61611f6791611f598486612959565b51015161299f565b60601c90565b6001600160a01b03811680156120375790849392918a955b888110611fb957505090611fb08592611fa1611f9a8a6129d6565b998b612959565b6001600160a01b039091169052565b01839493611eec565b9080929394959650611fdd611fd1611eba848d612959565b6001600160a01b031690565b14611ff0578a01908a9594939291611f7f565b50508f5163098d772360e21b815260206004820152601a60248201527f6475706c6963617465206f70657261746f7220616464726573730000000000006044820152606490fd5b508f5163098d772360e21b815260206004820152601860248201527f6f70657261746f722061646472657373206973207a65726f00000000000000006044820152606490fd5b8f5163098d772360e21b815260206004820181905260248201527f6f70657261746f722061646472657373206973206e6f742032302062797465736044820152606490fd5b8e5163098d772360e21b815260206004820152601e60248201527f6f70657261746f72206973206e6f7420616e2045564d206164647265737300006044820152606490fd5b9392611ef1565b9561211a878751612959565b51515190811561213657839161212f91612282565b9601611d3d565b8a5163098d772360e21b815260206004820152600b60248201526a0656d7074792067726f75760ac1b6044820152606490fd5b875163098d772360e21b815260206004820152601d60248201527f61637469766547726f7570496e646578206f7574206f662072616e67650000006044820152606490fd5b875163098d772360e21b815260206004820152601860248201527f65706f63684475726174696f6e536563206973207a65726f00000000000000006044820152606490fd5b855163098d772360e21b81526020600482015260096024820152686e6f2067726f75707360b81b6044820152606490fd5b634e487b7160e01b5f52601160045260245ffd5b5f1981019190821161224657565b612224565b9190820391821161224657565b906001820180921161224657565b906004820180921161224657565b906008820180921161224657565b9190820180921161224657565b60115460ff8160201c161561230a5760105490811561231d57601e546122bd9060081c63ffffffff1661196b565b9063ffffffff80941682811115612312576122e46122df6122eb94879361224b565b612238565b9116612282565b9081101561230a576122fc8161131f565b50541561230a571690600190565b50505f905f90565b50505050505f905f90565b5050505f905f90565b906123309061228f565b1561239e576010548110156113415760105f525f80516020614c3b83398151915201908154915f5b83811061236757505050505f90565b6123718183611346565b905460039190911b1c6001600160a01b039081169084161461239557600101612358565b50505050600190565b50505f90565b6040513d5f823e3d90fd5b90600161ffff8093160191821161224657565b91909161ffff8080941691160191821161224657565b906123f08263ffffffff165f526020805260405f2090565b90612400825460ff9060181c1690565b61259057600354612419906001600160a01b0316611fd1565b90813b1561035657604051905f82636744c79f60e01b9485825281838161244860048201906001602083019252565b03925af19081156125785761246e92869261257d575b50845460081c61ffff1690612f42565b60035490929190612487906001600160a01b0316611fd1565b91823b15610356575f9060405193849182528183816124ad60048201905f602083019252565b03925af1908115612578576124ec926124d49261255f575b50845460081c61ffff166123c2565b835462ffff00191660089190911b62ffff0016178355565b1561251657805463ff0000001916630100000017815561250e6119dc83612c93565b612516575050565b547f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc95009063ffffffff9060081c61ffff1660405161ffff909116815292169180602081015b0390a2565b8061256c612572926105b0565b80610444565b5f6124c5565b6123a4565b8061256c61258a926105b0565b5f61245e565b5061250e6119dc83612c93565b9060206106aa928181520190611051565b90816020910312610356575190565b63ffffffff8091169081146122465760010190565b91906125dd816131ab565b602091828083015101516125f2815160ff1690565b90600260ff831614801590612906575b6128d5575050606082019461261b865163ffffffff1690565b9063ffffffff808216908316036128af57505060a082015183815180155f146127ee57505050600254806127ce57505b612654826133ea565b9281519082012091612684611b05612670885163ffffffff1690565b63ffffffff165f52600d60205260405f2090565b5480156126d757830361269c576103659495506135dd565b610bba6126ad875163ffffffff1690565b60405163082085ab60e21b815263ffffffff90911660048201523360248201529081906044820190565b5061279a61196b61036596978561270e336126f9612670855163ffffffff1690565b9060018060a01b03165f5260205260405f2090565b55612761612737612723835163ffffffff1690565b63ffffffff165f52600e60205260405f2090565b61274d612748825463ffffffff1690565b6125bd565b63ffffffff1663ffffffff19825416179055565b611dc26127378761278d612779855163ffffffff1690565b63ffffffff165f52601760205260405f2090565b905f5260205260405f2090565b6040518481523391907fcafc00e7c35f462868d78e8228850e885bd0870978967c446c6bb67122a3fb2890602090a36135dd565b604051631d734b9160e01b815260048101919091525f6024820152604490fd5b03612873576040516347f5343160e01b815290848280612811846004830161259d565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4918215612578575f92612890575b508115612873575060025480820361285157505061264b565b604051631d734b9160e01b815260048101919091526024810191909152604490fd5b604051630ca3ddf160e01b8152908190610bba906004830161259d565b6128a8919250853d8711610c2057610c1181836105f9565b905f612838565b604051635070175360e01b815263ffffffff918216600482015291166024820152604490fd5b8401516040516385a89b3360e01b815246600482015260ff92909216602483015263ffffffff166044820152606490fd5b508085015163ffffffff1663ffffffff16461415612602565b6129289061228f565b15612954576010548110156113415760105f525f80516020614c3b833981519152015463ffffffff1690565b505f90565b80518210156113415760209160051b010190565b9061297782610627565b61298460405191826105f9565b8281528092612995601f1991610627565b0190602036910137565b90602082519201516bffffffffffffffffffffffff199081811693601481106129c757505050565b60140360031b82901b16169150565b5f1981146122465760010190565b601054600160401b81101561055a5760018101806010558110156113415760105f525f80516020614c3b83398151915201905f90565b8054600160401b81101561055a57612a3791600182018155611346565b60018060a09493941b038084549260031b9316831b921b1916179055565b600411610356575f90600490565b6001600160e01b03199035818116939260048110612a8057505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f80516020614bfb8339815191528054612afe906001600160a01b0316612af5612aef86612a55565b90612a63565b90843091613949565b9015612b0b575b50505050565b63ffffffff1615612bbc575f80516020614bfb833981519152805460ff60a01b1916600160a01b179055612b4c90611fd1908190546001600160a01b031690565b91823b1561035657612b77925f9283604051809681958294634a63ebf760e11b845260048401612a8f565b03925af1801561257857612ba9575b505f80516020614bfb833981519152805460ff60a01b191690555f808080612b05565b8061256c612bb6926105b0565b5f612b86565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f80516020614bfb83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b63ffffffff906001818316111561239e575f19011690565b63ffffffff91908282168382161115612c6457031690565b5050505f90565b6106aa9063ffffffff6015541690612c4c565b90600163ffffffff8093160191821161224657565b612c9c81613a52565b156129545760207fe784b22f2061de501b77364cab02ee109528afe4ddc8d423d076f6b49021b58e9163ffffffff612cf981831692835f5284805260405f2064010000000064ff0000000019825416179055600b54600255612c7e565b1663ffffffff195f5416175f55604051908152a1600190565b6020908181840312610356578051906001600160401b038211610356570182601f8201121561035657805191612d4783610627565b936040612d5760405196876105f9565b848652828601918360c080970286010194818611610356578401925b858410612d84575050505050505090565b8684830312610356578487918451612d9b816105c3565b8651612da6816108e1565b815282870151838201528587015186820152606080880151908201526080808801519082015260a08088015190820152815201930192612d73565b905f916040805191637d3b06bb60e11b83525f8380612e03846004830161259d565b038173__$583c08fff37be670b82ce66a4a046aa62c$__5af4928315612578575f93612f1e575b505f945b8351861015612f125761ffff811615801590612f0b575b80612eff575b612ef457612eec600191612ee78789612e7a612e73612e6a838c612959565b515161ffff1690565b61ffff1690565b612ea28a8a612e98856020612e8f8286612959565b51015193612959565b510151908a613b0a565b612ec28b60a0612eb8866080612e8f8286612959565b510151908b613b0a565b906001600160401b03612edf5a612ed95a60031c90565b9061224b565b941692613bff565b6123af565b950194612e2e565b945050505050905f90565b50620f42405a10612e4b565b505f612e45565b94505050505090600190565b612f3b9193503d805f833e612f3381836105f9565b810190612d12565b915f612e2a565b60408051637d3b06bb60e11b815292945f94939290858480612f67856004830161259d565b038173__$583c08fff37be670b82ce66a4a046aa62c$__5af4938415612578575f9461304c575b5061ffff8097169586905b855188101561303d5788811615801590613034575b80613028575b61301a57613012600191612ee7898b612fd3612e73612e6a838e612959565b612ff28c8c612fe8856020612e8f8286612959565b510151908c613b0a565b612ec28d60a0613008866080612e8f8286612959565b510151908d613b0a565b970196612f99565b965050955050505050905f90565b50620f42405a10612fb4565b50811515612fae565b96505095505050505090600190565b6130619194503d805f833e612f3381836105f9565b925f612f8e565b604051906130758261057a565b5f6020838281520152565b6040519061308d8261057a565b81613096613068565b815260206130a2613068565b910152565b604051906130b4826105de565b606060c0838281526130c4613080565b60208201525f60408201525f838201525f60808201528260a08201520152565b604051906130f1826105de565b606060c0836130fe613080565b81528260208201528260408201525f838201528260808201525f60a08201520152565b6040519061312e8261057a565b60606020835f81520152565b604051906131478261057a565b816131506130e4565b815260206130a2613121565b9061316682610627565b61317360405191826105f9565b8281528092613184601f1991610627565b01905f5b82811061319457505050565b60209061319f61313a565b82828501015201613188565b906131b46130a7565b915f8151905f805b83811061338457506131cd9061315c565b9160c086019283525f915b8181106131e6575050505050565b6131f09085613e14565b90816001600160401b0391828116600a81145f1461322157505050506132169085613fcd565b919087525b906131d8565b60128103613269575050506132396132629287613e14565b91169061325861325361324c8484612282565b838a613e58565b613ffc565b60208a0152612282565b905b61321b565b602881036132965750505050906132836132649286613e14565b6001600160401b03909116604089015291565b603081036132c65750505050906132646132b563ffffffff9387613e14565b9316606089019063ffffffff169052565b603881036132f65750505050906132646132e563ffffffff9387613e14565b9316608089019063ffffffff169052565b60a28103613318575050505061330c9085613fcd565b919060a088015261321b565b6101420361337457505061332f6132629287613e14565b91169061336e61335161334c6133458585612282565b848b613e58565b613f04565b95875161335d826129d6565b976133688383612959565b52612959565b50612282565b6132629350600716915086613d5a565b6133919085949394613e14565b6101426001600160401b0383949316146133bd575b60076133b59293169086613d5a565b9291926131bc565b60076133cb6133b5936129d6565b9250506133a6565b6040906106aa939281528160208201520190611051565b90601f54918215159060c080910191825151915f9073__$b91d2ad766fd07dc99f56c1a0b020615bb$__925b84831061342557505050505050565b613436838799939495969751612959565b5151602092838201519060408093015193858351036135c25784518681151591826135b7575b505061359c5783516347f5343160e01b9384825287828c818061348360049c8d830161259d565b03915af49485156125785788925f96613575575b50906134af92918751938492839283528a830161259d565b03818d5af4968715612578575f97613556575b50508061354c575b61352357506134da90851c612258565b83851c918183036134f957505050509560018093019190949392613416565b5163eb9ba7ad60e01b81529283018681526020810191909152604081019190915281906060010390fd5b9151630b296d8760e31b8152928301868152602081019290925260408201528190036060019150fd5b50808214156134ca565b61356d929750803d10610c2057610c1181836105f9565b945f806134c2565b8392919650613593906134af943d8511610c2057610c1181836105f9565b95909192613497565b8351632d9a492360e21b815280610bba878b600484016133d3565b14159050865f61345c565b8351635812b67560e01b815280610bba858b600484016133d3565b60609093919301916135fb6135f6845163ffffffff1690565b61291f565b63ffffffff80821690811561391257613634637fffffff61362a6112168761278d6127798c5163ffffffff1690565b9460011c16612c7e565b90600f54926136536001600160401b0394858482169160201c16612282565b421015918086169182109383613905575b5050508115916138fd575b50156138f557613729936136c16136c69261368986600b55565b600f80546cffffffffffffffffff000000001916429290921660201b6bffffffffffffffff000000001691909117600160601b179055565b601f55565b7fa77ed82a4a8c2f7b412630e92b1c8f59e6645b05820a8b0ed43ae5fac990491c6137196136fb61196b875163ffffffff1690565b6040805186815263ffffffff90951660208601529093918291820190565b0390a2825163ffffffff166140a8565b61373f61373a825163ffffffff1690565b61410e565b613763613750825163ffffffff1690565b63ffffffff165f526020805260405f2090565b805460ff1916600117815591600354613784906001600160a01b0316611fd1565b90813b1561035657604051905f82636744c79f60e01b948582528183816137b360048201906001602083019252565b03925af1918215612578576137d6926138e2575b50835163ffffffff1690612de1565b60035491949092916137f0906001600160a01b0316611fd1565b91823b15610356575f90604051938491825281838161381660048201905f602083019252565b03925af1908115612578576138559284926138cf575b50805463ffffff00191662ffff00600888901b161791151560181b63ff00000016919091179055565b1580156138b0575b613865575050565b61255a61389a61196b7f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc9500935163ffffffff1690565b60405161ffff9094168452929081906020820190565b506138ca6119dc6138c5835163ffffffff1690565b612c93565b61385d565b8061256c6138dc926105b0565b5f61382c565b8061256c6138ef926105b0565b5f6137c7565b505050505050565b90505f61366f565b16111590505f8080613664565b50505050505050565b60ff5f80516020614c5b8339815191525460401c161561393757565b604051631afcd79f60e31b8152600490fd5b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b0384111761055a576040935f9385528380528360205251915afa6139b857565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613a08575b5050610cb057565b5f80516020614c1b8339815191525416141590505f80613a00565b3d15613a4d573d90613a348261063e565b91613a4260405193846105f9565b82523d5f602084013e565b606090565b623d09005a1061295457600354613a71906001600160a01b0316611fd1565b803b156103565760405163a3ad9cc360e01b815263ffffffff9290921660048301525f908290602490829084905af19081613acf575b50613aca57613ab4613a23565b8051918215613ac35750602001fd5b9150505f90565b600190565b8061256c613adc926105b0565b5f613aa7565b90613aec8261063e565b613af960405191826105f9565b8281528092612995601f199161063e565b919080820391821161224657602090613b2283613ae2565b930101602083015e90565b5190811515820361035657565b90816020910312610356576106aa90613b2d565b91613b709061ffff604093969596168452606060208501526060840190611051565b6001600160a01b03909416910152565b9193926001600160401b039061ffff613ba3604094606087526060870190611051565b9616602085015216910152565b9160a093613be6613bf49261ffff6001600160401b03959a99989a6004885216602087015260c0604087015260c0860190611051565b908482036060860152611051565b951660808201520152565b91929461ffff83169061ee0f8214613d4a575061ee1314613d3c576016546040516359109f8560e01b815273__$a62ffa76e12530392778037a67b422bd63$__939160209082908190613c60906001600160a01b0316878760048501613b4e565b0381875af4908115612578575f91613d0d575b50613cbf57823b1561035657613ca15f956040519788968795869563173d424160e21b875260048701613bb0565b03915af4801561257857613cb25750565b8061256c610365926105b0565b6016547f6615e715ef29da98af4d4b50f6beb57ec1a6a61bb207b117e5fb58632755e93795509093925061255a9150613d00906001600160a01b0316611fd1565b9460405193849384613b80565b613d2f915060203d602011613d35575b613d2781836105f9565b810190613b3a565b5f613c73565b503d613d1d565b925050506103659150614492565b945050915050610365925061430d565b6001600160401b039283169283613d7b5750613d769250613e14565b905090565b919260018103613d91575050506106aa90612274565b60028103613daf57506106aa92613da791613e14565b911690612282565b600514159150613dc49050576106aa90612266565b60405162461bcd60e51b815260206004820152602260248201527f50726f746f62756652756e74696d653a20756e6b6e6f776e2077697265207479604482015261706560f01b6064820152608490fd5b91908201602001905f805b60806007600186515f1a94607f8616851b1796019201921615613e4b57603f8211610356579290613e1f565b9293909203601f19019150565b918181101580613ef9575b15613ea85781613e729161224b565b90613e7c82613ae2565b9282613e89575b50505090565b015f5b82811015613e8357602081830181015185830182015201613e8c565b60405162461bcd60e51b8152602060048201526024808201527f50726f746f62756652756e74696d653a20736c696365206f7574206f6620626f604482015263756e647360e01b6064820152608490fd5b508251811115613e63565b90613f0d61313a565b915f8151905b818110613f1f57505050565b613f299083613e14565b90816001600160401b0391828116600a81145f14613f7f57505050613f51613f779285613e14565b911690613f70613f6b613f648484612282565b8388613e58565b6146ec565b8752612282565b905b90613f13565b601203613fbd575050613f95613fb79285613e14565b911690613fad613fa8613f648484612282565b6145cf565b6020880152612282565b90613f79565b613fb79350600716915084613d5a565b9190613fe16001600160401b039184613e14565b911681019283821161224657613ff8918491613e58565b9190565b90614005613080565b915f8151905b81811061401757505050565b6140219083613e14565b90816001600160401b0391828116600a81145f14614069575050506140496140619285613e14565b911690613f7061405c613f648484612282565b61484f565b905b9061400b565b60120361409857505061407f6140929285613e14565b911690613fad61405c613f648484612282565b90614063565b6140929350600716915084613d5a565b63ffffffff8082165f52601460205260405f2090815416614109576140df600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b6141259063ffffffff918291826015541690612c4c565b1690811515908161414e575b506141395750565b5f5260146020525f6001604082208281550155565b9050815f52601460205260405f20541615155f614131565b90813b156141ea575f80516020614c1b83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28051156141cf576141cc916148e8565b50565b5050346141d857565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b908160a0910312610356576040519060a082018281106001600160401b0382111761055a576040528051906006821015610356576080918352602081015161425281610348565b6020840152604081015161426581610348565b604084015261427660608201613b2d565b6060840152015161428681610348565b608082015290565b916142a660809295949560a0855260a0850190611051565b94601060208501526012604085015263ffffffff809216606085015216910152565b600611156142d257565b634e487b7160e01b5f52602160045260245ffd5b63ffffffff9182168152911660208201526060810192919060068210156142d25760400152565b9060a061431f600f5463ffffffff1690565b601e5460081c63ffffffff169361434a6040519586938493632b6e75a560e11b85526004850161428e565b038173__$a62ffa76e12530392778037a67b422bd63$__5af4918215612578575f92614461575b50815161437d816142c8565b614386816142c8565b61441357506143f66080826143a8611dac602061036596015163ffffffff1690565b6143bc611dcc604083015163ffffffff1690565b6143eb6143cc6060830151151590565b64ff0000000060115491151560201b169064ff00000000191617601155565b015163ffffffff1690565b64ffffffff00601e549160081b169064ffffffff00191617601e55565b907f06bc5809fcff2cb3c29e300b687d3444b42c7836482312d880b48c4bcfbec92291611c3561444a608084015163ffffffff1690565b9251614455816142c8565b604051938493846142e6565b61448491925060a03d60a01161448b575b61447c81836105f9565b81019061420b565b905f614371565b503d614472565b6001600160401b03906001826018541601908282116122465773__$a62ffa76e12530392778037a67b422bd63$__90813b15610356575f916144f291604051809681948293637401af5d60e01b845260e0600485015260e4840190611051565b90601260248401526013604484015260196064840152601a6084840152601b60a4840152871660c483015203915af4918215612578576103659261454e575b506001600160401b03166001600160401b03196018541617601855565b8061256c61455b926105b0565b5f614531565b6040519061456e82610595565b60606040835f81525f60208201520152565b9061458a82610627565b61459760405191826105f9565b82815280926145a8601f1991610627565b01905f5b8281106145b857505050565b6020906145c3614561565b828285010152016145ac565b906145d8613121565b915f8151905f805b83811061469e57506145f190614580565b91602086019283525f915b81811061460a575050505050565b6146149085613e14565b90816001600160401b0391828116600881145f1461465757505050509061465161464363ffffffff9387613e14565b9316889063ffffffff169052565b906145fc565b60120361468e57505061466d6146889287613e14565b91169061336e6133516146836133458585612282565b614905565b90614651565b6146889350600716915086613d5a565b6146ab9085949394613e14565b60126001600160401b0383949316146146d6575b60076146ce9293169086613d5a565b9291926145e0565b60076146e46146ce936129d6565b9250506146bf565b906146f56130e4565b915f8151905b81811061470757505050565b6147119083613e14565b90816001600160401b0391828116600a81145f146147545750505061473961474c9285613e14565b911690613f70613253613f648484612282565b905b906146fb565b91925090601281036147795750505061476d9083613fcd565b9190602086015261474e565b601a810361479a5750505061478e9083613fcd565b9190604086015261474e565b602881036147ce57505050906147c96147b863ffffffff9385613e14565b9316606087019063ffffffff169052565b61474e565b603281036147ef575050506147e39083613fcd565b9190608086015261474e565b6038810361481b57505050906148086147c99284613e14565b6001600160401b0390911660a087015291565b60420361483a57505061482e9083613fcd565b919060c086015261474e565b61484992506007169084613d5a565b9061474e565b90614858613068565b915f8151905b81811061486a57505050565b6148749083613e14565b80916001600160401b038116600881145f146148ae575050509060ff61489d6148a49385613e14565b93906149d1565b1685525b9061485e565b6010036148d3575050906148c763ffffffff9284613e14565b921660208601526148a8565b6148e292506007169084613d5a565b906148a8565b5f806106aa93602081519101845af46148ff613a23565b91614a22565b9061490e614561565b915f8151905b81811061492057505050565b61492a9083613e14565b80916001600160401b038116600881145f14614969575050509061496361495461495b9385613e14565b9390614a85565b61ffff168652565b90614914565b6010810361499d575050509061499861498763ffffffff9385613e14565b9316602087019063ffffffff169052565b614963565b601a036149bc5750506149b09083613fcd565b91906040860152614963565b6149cb92506007169084613d5a565b90614963565b6001600160401b031680156129545760018114614a1c5760028114614a165760038114614a105760249060405190632cd3748760e21b82526004820152fd5b50600390565b50600290565b50600190565b90614a495750805115614a3757602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614a7c575b614a5a575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15614a52565b6001600160401b038116801561239e576107d1808214614bf45750610bb9808214614bf45750610bba808214614bf45750610bbc808214614bf45750610bbe808214614bf4575061aa00808214614bf4575061ee00808214614bf4575061ee02808214614bf4575061ee04808214614bf4575061ee06808214614bf4575061ee10808214614bf4575061ee11808214614bf4575061ee13808214614bf4575061ee0f808214614bf4575061ee15808214614bf4575061ee16808214614bf4575061ee17808214614bf4575061ee18808214614bf4575061ee19808214614bf4575061ee1b808214614bf4575061ee1c808214614bf4575061ee1e808214614bf4575061ee1f808214614bf4575061ee20808214614bf4575061ee21808214614bf4575061ee22808214614bf4575061ee23808214614bf4575061ee24808214614bf4575061ee25809114613d7657604051632cd3748760e21b81526001600160401b0383166004820152602490fd5b9150509056fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a264697066735822122010a68012febf8fe1754f31c152149d058698cd9a133878ab2a1ddcfd7b362bf164736f6c63430008190033", - "deployedBytecode": "0x60806040526004361015610011575f80fd5b5f3560e01c80624e356a146103435780630fa92c531461033e578063151b8f4e146103395780631bdf0a72146103345780631d7a88271461032f57806320857e651461032a57806322fe388b14610325578063239e1d67146103205780632945698b1461031b5780632c92892f146103165780632f4d9fa9146103115780633367cca51461030c57806336051135146103075780633915ba63146103025780634ab47f43146102fd5780634f1ef286146102f85780635126a52d146102f357806352d1902d146102ee578063533ae1cc146102e95780635701a93f146102e45780635c0c8f44146102df5780635d4e25ce146102da5780635ff8195e146102d5578063621fbd59146102d05780637a9e5e4b146102cb5780637b778122146102c65780637e283cea146102c157806380e643b6146102bc5780638b75e929146102b75780638bdebf21146102b25780638d9f616b146102ad5780638fb36037146102a857806393ec06be146102a357806399b1a19c1461029e578063ad3cb1cc14610299578063af243e8914610294578063b41565b81461028f578063bf7e214f1461028a578063c0d0373e14610285578063c268863f14610280578063c45fdcb71461027b578063cd2ad3fb14610276578063d0841f2214610271578063d1ad8d011461026c578063d3f0730614610267578063d5b5272a14610262578063e3dff2af1461025d578063e55ba57614610258578063ea8dbafe14610253578063f552501a1461024e5763fdfd07df14610249575f80fd5b611910565b6117ce565b6115de565b61159f565b611582565b6114d4565b6114a4565b61144a565b611422565b6113be565b61135b565b6112e3565b6111cd565b611199565b6110ed565b6110ba565b611075565b61102c565b611010565b610fc0565b610f81565b610f5b565b610f20565b610efc565b610e80565b610e27565b610dad565b610d90565b610d6d565b610d50565b610d0b565b610ce8565b610cc2565b610c59565b610c27565b610b1e565b610af1565b610abf565b610a75565b610a3f565b610a22565b610a05565b6109d8565b6109b6565b61099b565b6108ed565b6106ad565b610471565b61044e565b610409565b610367565b63ffffffff81160361035657565b5f80fd5b359061036582610348565b565b346103565760403660031901126103565760043561038481610348565b6001600160401b036024358181116103565736602382011215610356578060040135918211610356573660248383010111610356577f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0091825c6103f7575f9360246103f4936001865d0190611956565b5d005b604051633ee5aeb560e01b8152600490fd5b3461035657602036600319011261035657602060043561042881610348565b63ffffffff8091165f52600e825260405f205416604051908152f35b5f91031261035657565b34610356575f36600319011261035657602063ffffffff600f5416604051908152f35b34610356575f366003190112610356576001600160401b0363ffffffff6104fb815f5416805f52600e6020528260405f205416906104ae8161291f565b600f549260405196879685169460201c169286936001600160401b039060809497969260a087019863ffffffff8096818094168a5216602089015216604087015216606085015216910152565b0390f35b600435906001600160a01b038216820361035657565b602435906001600160a01b038216820361035657565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b0382111761055a57604052565b61052b565b602081019081106001600160401b0382111761055a57604052565b604081019081106001600160401b0382111761055a57604052565b606081019081106001600160401b0382111761055a57604052565b6001600160401b03811161055a57604052565b60c081019081106001600160401b0382111761055a57604052565b60e081019081106001600160401b0382111761055a57604052565b90601f801991011681019081106001600160401b0382111761055a57604052565b604051906103658261055f565b6001600160401b03811161055a5760051b60200190565b6001600160401b03811161055a57601f01601f191660200190565b9291926106658261063e565b9161067360405193846105f9565b829481845281830111610356578281602093845f960137010152565b9080601f83011215610356578160206106aa93359101610659565b90565b3461035657600319604036820112610356576106c76104ff565b602435916001600160401b03831161035657608090833603011261035657604051906106f28261053f565b826004013561070081610348565b8252602483013561071081610348565b60208301526044926044810135936001600160401b03851161035657366023868401011215610356576004858301013561074981610627565b9561075760405197886105f9565b81875260208701903660248460051b83880101011161035657602481860101915b60248460051b838801010183106107ac576107aa88886107a060648a8e60408601520161035a565b6060830152611b82565b005b82356001600160401b03811161035657828701016020602319823603011261035657604051906107db8261055f565b60248101356001600160401b0381116103565736604382840101121561035657602481830101359061080c82610627565b9261081a60405194856105f9565b8284526020840190368b8560051b858401010111610356578a83820101915b8b8560051b8584010101831061085f575050509183525050815260209283019201610778565b82356001600160401b038111610356576040838601820136036043190112610356576040519161088e8361057a565b8d8287860101013560ff81168103610356578352606482878601010135926001600160401b038411610356576108d28f9360209594869536928b8a0101010161068f565b83820152815201920191610839565b61ffff81160361035657565b346103565760403660031901126103565760043561090a816108e1565b61ffff610915610515565b916109203633612ac6565b165f818152600460209081526040918290205482516001600160a01b03868116825291821692810192909252929182917f46d568db0216a0215fe7f56360f9fa1a002c4bf0773add9cd62cd06eff1601899190a25f52600460205260405f2091166bffffffffffffffffffffffff60a01b8254161790555f80f35b34610356575f36600319011261035657602060405160078152f35b34610356575f36600319011261035657602063ffffffff5f5416604051908152f35b34610356576020366003190112610356576004355f52601b602052602060ff60405f205416604051908152f35b34610356575f366003190112610356576020600c54604051908152f35b34610356575f366003190112610356576020601f54604051908152f35b34610356576020366003190112610356576020610a6b610a5d6104ff565b63ffffffff5f541690612326565b6040519015158152f35b34610356576020366003190112610356576001600160401b0363ffffffff6104fb815f5416805f52601760205260405f206004355f526020528260405f205416906104ae8161291f565b34610356576020366003190112610356576004355f526012602052602060018060a01b0360405f205416604051908152f35b34610356576020366003190112610356576004355f52601a602052602060ff60405f205416604051908152f35b604036600319011261035657610b326104ff565b6024356001600160401b03811161035657610b5190369060040161068f565b90610b5a6139ca565b610b643633612ac6565b610b6c6139ca565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610bf6575b50610bbe57604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b0390fd5b905f80516020614c1b8339815191528303610bdd576107aa9250614166565b604051632a87526960e21b815260048101849052602490fd5b610c1991945060203d602011610c20575b610c1181836105f9565b8101906125ae565b925f610b94565b503d610c07565b34610356576020366003190112610356576004355f526013602052602060018060a01b0360405f205416604051908152f35b34610356575f366003190112610356577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610cb05760206040515f80516020614c1b8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610356575f36600319011261035657602063ffffffff601e5460081c16604051908152f35b34610356575f36600319011261035657602063ffffffff60155416604051908152f35b3461035657602036600319011261035657610d246104ff565b610d2e3633612ac6565b600380546001600160a01b0319166001600160a01b0392909216919091179055005b34610356575f366003190112610356576020600254604051908152f35b34610356575f36600319011261035657602063ffffffff60115416604051908152f35b34610356575f366003190112610356576020600154604051908152f35b3461035657602036600319011261035657610dc66104ff565b5f80516020614bfb833981519152546001600160a01b03919082163303610e1057803b15610df7576107aa90612bdc565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b34610356576040366003190112610356576020610e77600435610e4981610348565b63ffffffff610e56610515565b91165f52600d835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b34610356576020366003190112610356577f2b797207c3f71ed55e7ee9f751f9d4e23795bff49d40a0d3d05e160e1257798d610eba6104ff565b610ec43633612ac6565b601680546001600160a01b039283166001600160a01b031982168117909255604080519283529216602082015290819081015b0390a1005b34610356575f36600319011261035657602060115460ff60405191831c1615158152f35b34610356576020366003190112610356576040610f47600435610f4281610348565b61228f565b63ffffffff83519216825215156020820152f35b34610356575f3660031901126103565760206001600160401b0360185416604051908152f35b346103565760203660031901126103565761ffff600435610fa1816108e1565b165f526004602052602060018060a01b0360405f205416604051908152f35b34610356575f366003190112610356575f80516020614bfb8339815191525460a01c60ff1615611008576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f610ff5565b34610356575f3660031901126103565760206040516180008152f35b34610356575f36600319011261035657602060ff600f5460601c166040519015158152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b34610356575f366003190112610356576104fb6040516110948161057a565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611051565b34610356576020366003190112610356576004355f52601960205260206001600160401b0360405f205416604051908152f35b34610356576020366003190112610356576104fb60043561110d81610348565b60405f81805161111c81610595565b828152826020820152015263ffffffff8092165f5260146020526001815f206001600160401b0383519461114f86610595565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610356575f366003190112610356575f80516020614bfb833981519152546040516001600160a01b039091168152602090f35b34610356576020366003190112610356576004356111ea81610348565b6111f43633612ac6565b63ffffffff90816112206112168363ffffffff165f52601460205260405f2090565b5463ffffffff1690565b16156112c35761124461123f61123a5f5463ffffffff1690565b612c34565b612c6b565b828216928116831161129d575061126c6112779163ffffffff165f52601460205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b604051631cc9a6c760e21b815263ffffffff928316600482015291166024820152604490fd5b604051630529520560e31b815263ffffffff919091166004820152602490fd5b34610356575f366003190112610356576003546040516001600160a01b039091168152602090f35b634e487b7160e01b5f52603260045260245ffd5b6010548110156113415760105f525f80516020614c3b83398151915201905f90565b61130b565b8054821015611341575f5260205f2001905f90565b3461035657604036600319011261035657600435602435906010548110156103565760105f525f80516020614c3b833981519152018054821015610356576020916113a591611346565b905460405160039290921b1c6001600160a01b03168152f35b346103565760203660031901126103565763ffffffff6004356113e081610348565b165f5260208052608060405f205460ff604051918181161515835261ffff8160081c166020840152818160181c161515604084015260201c1615156060820152f35b34610356575f366003190112610356576020600f546001600160401b0360405191831c168152f35b3461035657602036600319011261035657606060043561146981610348565b63ffffffff8091165f52601460205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b34610356576040366003190112610356576020610a6b6114c26104ff565b602435906114cf82610348565b612326565b34610356576020366003190112610356576004356114f181610348565b6114fb3633612ac6565b6040516115078161055f565b63ffffffff908183168082521561157057601580549151831663ffffffff1983161790556040805163ffffffff93909216831682529290911660208201527f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69181908101610ef7565b604051639ca50c6160e01b8152600490fd5b34610356575f366003190112610356576020600b54604051908152f35b346103565760203660031901126103565763ffffffff6004356115c181610348565b6115cb3633612ac6565b1663ffffffff19600f541617600f555f80f35b34610356575f366003190112610356576016546040516001600160a01b039091168152602090f35b604051905f6009546001918160011c92600183169081156116eb575b6020906020861083146116d757858852879460208601939081156116b85750600114611658575b505050610365925003836105f9565b92509361168660095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af90565b945f935b8285106116a2575050506103659350015f8080611649565b865485850152958601958895509381019361168a565b925050506103659491925060ff19168252151560051b015f8080611649565b634e487b7160e01b5f52602260045260245ffd5b93607f1693611622565b604051906117028261057a565b60065460ff8116835260081c63ffffffff166020830152565b604051906117288261057a565b60075460ff8116835260081c63ffffffff166020830152565b926001600160401b03906117a760206117696106aa9a98969995610120808a52890190611051565b9861178a828901825163ffffffff6020809260ff8151168552015116910152565b0151805160ff1660608801526020015163ffffffff166080870152565b1660a084015263ffffffff80921660c08401521660e0820152610100818403910152611051565b34610356575f366003190112610356576040515f6005546001918160011c9260018316908115611906575b6020906020861083146116d757858752869460208601939081156118e75750600114611887575b8461182d818603826105f9565b6040516118398161057a565b6118416116f5565b815261184b61171b565b60208201526104fb60085463ffffffff611863611606565b916040519586956001600160401b03848460601c16948460401c1693169187611741565b9250936118b560055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db090565b945f935b8285106118d15750505061182d9350015f8080611820565b86548585015295860195879550938101936118b9565b9250505061182d9491925060ff19168252151560051b015f8080611820565b93607f16936117f9565b3461035657604036600319011261035657602060043561192f81610348565b63ffffffff8091165f526017825260405f206024355f52825260405f205416604051908152f35b9092919261197461196b5f5463ffffffff1690565b63ffffffff1690565b63ffffffff90818416908110611b7a5761199b8463ffffffff165f526020805260405f2090565b5460ff81169081611b6b575b50611ae7575f5463ffffffff169082821603611ac0575060115460ff8160201c16611a4b575b50506119e06119dc8333612326565b1590565b611a335783158015611a28575b611a0757611a019061036593943691610659565b906125d2565b604051630c6f042160e31b8152600481018590526180006024820152604490fd5b5061800084116119ed565b604051632af0051560e21b8152336004820152602490fd5b611a548461228f565b90506119cd57610bba84611a71601e5463ffffffff9060081c1690565b90611a8160105463ffffffff1690565b604051631bf926d360e11b815263ffffffff92831660048201529282166024840152939094168416604482015292909116606483015281906084820190565b6040516307d3e83d60e51b815263ffffffff91821660048201529084166024820152604490fd5b505092611b17611b058363ffffffff165f52600d60205260405f2090565b335f9081526020919091526040902090565b5493600b54809503611a3357611b2e913691610659565b908151602083012093808503611b4a57506103659293506123d8565b6040516340d6b4cd60e01b8152600481019190915260248101859052604490fd5b60ff915060201c16155f6119a7565b505050509050565b905f80516020614c5b8339815191529081549260ff8460401c1615936001600160401b03811680159081611c83575b6001149081611c79575b159081611c70575b50611c5e5767ffffffffffffffff1916600117909255611be79183611c3a57611c8b565b611bed57565b5f80516020614c5b833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1565b5f80516020614c5b833981519152805460ff60401b1916600160401b179055611c8b565b60405163f92ee8a960e01b8152600490fd5b9050155f611bc3565b303b159150611bbb565b869150611bb1565b611caf90929192611c9a61391b565b611ca261391b565b611caa61391b565b612bdc565b611cb761391b565b5f805463ffffffff19166001908117909155600f80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161790556040928381018051519182156121f357606081019063ffffffff9485611d1b845163ffffffff1690565b16156121ae5784611d3361196b845163ffffffff1690565b1015612169575f945f5b81811061210e5750611d4e8661296d565b5f805b838110611ed85750505f905f5b838110611e65575050505050611e3b92611de2611dcc7ff485fcf1271cbcd2319ebf860c827016cef79959673d605215dcd74ed0629ea4989994611dc2611dac611e1a965163ffffffff1690565b63ffffffff1663ffffffff196011541617601155565b5163ffffffff1690565b63ffffffff1663ffffffff19600f541617600f55565b611dfc64010000000064ff00000000196011541617601155565b611e0f64ffffffff0019601e5416601e55565b515163ffffffff1690565b905163ffffffff918216815293909216909116602083015281906040820190565b0390a1610365611e4961061a565b60c8815263ffffffff90511663ffffffff196015541617601555565b80611e7286928a51612959565b515151611e7d6129e4565b5050611e888261131f565b505f5b828110611e9b5750505001611d5e565b611ecd611ec7611eba611eb38a98959697989a6129d6565b9989612959565b516001600160a01b031690565b84612a1a565b019087939291611e8b565b80611ee586928a51612959565b515183905f845b611efa575b50505001611d51565b90919293825182101561210757506002611f2b611f25611f25611f1d8587612959565b515160ff1690565b60ff1690565b036120c2576020601481611f3f8486612959565b510151510361207d57611f61611f6791611f598486612959565b51015161299f565b60601c90565b6001600160a01b03811680156120375790849392918a955b888110611fb957505090611fb08592611fa1611f9a8a6129d6565b998b612959565b6001600160a01b039091169052565b01839493611eec565b9080929394959650611fdd611fd1611eba848d612959565b6001600160a01b031690565b14611ff0578a01908a9594939291611f7f565b50508f5163098d772360e21b815260206004820152601a60248201527f6475706c6963617465206f70657261746f7220616464726573730000000000006044820152606490fd5b508f5163098d772360e21b815260206004820152601860248201527f6f70657261746f722061646472657373206973207a65726f00000000000000006044820152606490fd5b8f5163098d772360e21b815260206004820181905260248201527f6f70657261746f722061646472657373206973206e6f742032302062797465736044820152606490fd5b8e5163098d772360e21b815260206004820152601e60248201527f6f70657261746f72206973206e6f7420616e2045564d206164647265737300006044820152606490fd5b9392611ef1565b9561211a878751612959565b51515190811561213657839161212f91612282565b9601611d3d565b8a5163098d772360e21b815260206004820152600b60248201526a0656d7074792067726f75760ac1b6044820152606490fd5b875163098d772360e21b815260206004820152601d60248201527f61637469766547726f7570496e646578206f7574206f662072616e67650000006044820152606490fd5b875163098d772360e21b815260206004820152601860248201527f65706f63684475726174696f6e536563206973207a65726f00000000000000006044820152606490fd5b855163098d772360e21b81526020600482015260096024820152686e6f2067726f75707360b81b6044820152606490fd5b634e487b7160e01b5f52601160045260245ffd5b5f1981019190821161224657565b612224565b9190820391821161224657565b906001820180921161224657565b906004820180921161224657565b906008820180921161224657565b9190820180921161224657565b60115460ff8160201c161561230a5760105490811561231d57601e546122bd9060081c63ffffffff1661196b565b9063ffffffff80941682811115612312576122e46122df6122eb94879361224b565b612238565b9116612282565b9081101561230a576122fc8161131f565b50541561230a571690600190565b50505f905f90565b50505050505f905f90565b5050505f905f90565b906123309061228f565b1561239e576010548110156113415760105f525f80516020614c3b83398151915201908154915f5b83811061236757505050505f90565b6123718183611346565b905460039190911b1c6001600160a01b039081169084161461239557600101612358565b50505050600190565b50505f90565b6040513d5f823e3d90fd5b90600161ffff8093160191821161224657565b91909161ffff8080941691160191821161224657565b906123f08263ffffffff165f526020805260405f2090565b90612400825460ff9060181c1690565b61259057600354612419906001600160a01b0316611fd1565b90813b1561035657604051905f82636744c79f60e01b9485825281838161244860048201906001602083019252565b03925af19081156125785761246e92869261257d575b50845460081c61ffff1690612f42565b60035490929190612487906001600160a01b0316611fd1565b91823b15610356575f9060405193849182528183816124ad60048201905f602083019252565b03925af1908115612578576124ec926124d49261255f575b50845460081c61ffff166123c2565b835462ffff00191660089190911b62ffff0016178355565b1561251657805463ff0000001916630100000017815561250e6119dc83612c93565b612516575050565b547f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc95009063ffffffff9060081c61ffff1660405161ffff909116815292169180602081015b0390a2565b8061256c612572926105b0565b80610444565b5f6124c5565b6123a4565b8061256c61258a926105b0565b5f61245e565b5061250e6119dc83612c93565b9060206106aa928181520190611051565b90816020910312610356575190565b63ffffffff8091169081146122465760010190565b91906125dd816131ab565b602091828083015101516125f2815160ff1690565b90600260ff831614801590612906575b6128d5575050606082019461261b865163ffffffff1690565b9063ffffffff808216908316036128af57505060a082015183815180155f146127ee57505050600254806127ce57505b612654826133ea565b9281519082012091612684611b05612670885163ffffffff1690565b63ffffffff165f52600d60205260405f2090565b5480156126d757830361269c576103659495506135dd565b610bba6126ad875163ffffffff1690565b60405163082085ab60e21b815263ffffffff90911660048201523360248201529081906044820190565b5061279a61196b61036596978561270e336126f9612670855163ffffffff1690565b9060018060a01b03165f5260205260405f2090565b55612761612737612723835163ffffffff1690565b63ffffffff165f52600e60205260405f2090565b61274d612748825463ffffffff1690565b6125bd565b63ffffffff1663ffffffff19825416179055565b611dc26127378761278d612779855163ffffffff1690565b63ffffffff165f52601760205260405f2090565b905f5260205260405f2090565b6040518481523391907fcafc00e7c35f462868d78e8228850e885bd0870978967c446c6bb67122a3fb2890602090a36135dd565b604051631d734b9160e01b815260048101919091525f6024820152604490fd5b03612873576040516347f5343160e01b815290848280612811846004830161259d565b038173__$b91d2ad766fd07dc99f56c1a0b020615bb$__5af4918215612578575f92612890575b508115612873575060025480820361285157505061264b565b604051631d734b9160e01b815260048101919091526024810191909152604490fd5b604051630ca3ddf160e01b8152908190610bba906004830161259d565b6128a8919250853d8711610c2057610c1181836105f9565b905f612838565b604051635070175360e01b815263ffffffff918216600482015291166024820152604490fd5b8401516040516385a89b3360e01b815246600482015260ff92909216602483015263ffffffff166044820152606490fd5b508085015163ffffffff1663ffffffff16461415612602565b6129289061228f565b15612954576010548110156113415760105f525f80516020614c3b833981519152015463ffffffff1690565b505f90565b80518210156113415760209160051b010190565b9061297782610627565b61298460405191826105f9565b8281528092612995601f1991610627565b0190602036910137565b90602082519201516bffffffffffffffffffffffff199081811693601481106129c757505050565b60140360031b82901b16169150565b5f1981146122465760010190565b601054600160401b81101561055a5760018101806010558110156113415760105f525f80516020614c3b83398151915201905f90565b8054600160401b81101561055a57612a3791600182018155611346565b60018060a09493941b038084549260031b9316831b921b1916179055565b600411610356575f90600490565b6001600160e01b03199035818116939260048110612a8057505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f80516020614bfb8339815191528054612afe906001600160a01b0316612af5612aef86612a55565b90612a63565b90843091613949565b9015612b0b575b50505050565b63ffffffff1615612bbc575f80516020614bfb833981519152805460ff60a01b1916600160a01b179055612b4c90611fd1908190546001600160a01b031690565b91823b1561035657612b77925f9283604051809681958294634a63ebf760e11b845260048401612a8f565b03925af1801561257857612ba9575b505f80516020614bfb833981519152805460ff60a01b191690555f808080612b05565b8061256c612bb6926105b0565b5f612b86565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b5f80516020614bfb83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b63ffffffff906001818316111561239e575f19011690565b63ffffffff91908282168382161115612c6457031690565b5050505f90565b6106aa9063ffffffff6015541690612c4c565b90600163ffffffff8093160191821161224657565b612c9c81613a52565b156129545760207fe784b22f2061de501b77364cab02ee109528afe4ddc8d423d076f6b49021b58e9163ffffffff612cf981831692835f5284805260405f2064010000000064ff0000000019825416179055600b54600255612c7e565b1663ffffffff195f5416175f55604051908152a1600190565b6020908181840312610356578051906001600160401b038211610356570182601f8201121561035657805191612d4783610627565b936040612d5760405196876105f9565b848652828601918360c080970286010194818611610356578401925b858410612d84575050505050505090565b8684830312610356578487918451612d9b816105c3565b8651612da6816108e1565b815282870151838201528587015186820152606080880151908201526080808801519082015260a08088015190820152815201930192612d73565b905f916040805191637d3b06bb60e11b83525f8380612e03846004830161259d565b038173__$583c08fff37be670b82ce66a4a046aa62c$__5af4928315612578575f93612f1e575b505f945b8351861015612f125761ffff811615801590612f0b575b80612eff575b612ef457612eec600191612ee78789612e7a612e73612e6a838c612959565b515161ffff1690565b61ffff1690565b612ea28a8a612e98856020612e8f8286612959565b51015193612959565b510151908a613b0a565b612ec28b60a0612eb8866080612e8f8286612959565b510151908b613b0a565b906001600160401b03612edf5a612ed95a60031c90565b9061224b565b941692613bff565b6123af565b950194612e2e565b945050505050905f90565b50620f42405a10612e4b565b505f612e45565b94505050505090600190565b612f3b9193503d805f833e612f3381836105f9565b810190612d12565b915f612e2a565b60408051637d3b06bb60e11b815292945f94939290858480612f67856004830161259d565b038173__$583c08fff37be670b82ce66a4a046aa62c$__5af4938415612578575f9461304c575b5061ffff8097169586905b855188101561303d5788811615801590613034575b80613028575b61301a57613012600191612ee7898b612fd3612e73612e6a838e612959565b612ff28c8c612fe8856020612e8f8286612959565b510151908c613b0a565b612ec28d60a0613008866080612e8f8286612959565b510151908d613b0a565b970196612f99565b965050955050505050905f90565b50620f42405a10612fb4565b50811515612fae565b96505095505050505090600190565b6130619194503d805f833e612f3381836105f9565b925f612f8e565b604051906130758261057a565b5f6020838281520152565b6040519061308d8261057a565b81613096613068565b815260206130a2613068565b910152565b604051906130b4826105de565b606060c0838281526130c4613080565b60208201525f60408201525f838201525f60808201528260a08201520152565b604051906130f1826105de565b606060c0836130fe613080565b81528260208201528260408201525f838201528260808201525f60a08201520152565b6040519061312e8261057a565b60606020835f81520152565b604051906131478261057a565b816131506130e4565b815260206130a2613121565b9061316682610627565b61317360405191826105f9565b8281528092613184601f1991610627565b01905f5b82811061319457505050565b60209061319f61313a565b82828501015201613188565b906131b46130a7565b915f8151905f805b83811061338457506131cd9061315c565b9160c086019283525f915b8181106131e6575050505050565b6131f09085613e14565b90816001600160401b0391828116600a81145f1461322157505050506132169085613fcd565b919087525b906131d8565b60128103613269575050506132396132629287613e14565b91169061325861325361324c8484612282565b838a613e58565b613ffc565b60208a0152612282565b905b61321b565b602881036132965750505050906132836132649286613e14565b6001600160401b03909116604089015291565b603081036132c65750505050906132646132b563ffffffff9387613e14565b9316606089019063ffffffff169052565b603881036132f65750505050906132646132e563ffffffff9387613e14565b9316608089019063ffffffff169052565b60a28103613318575050505061330c9085613fcd565b919060a088015261321b565b6101420361337457505061332f6132629287613e14565b91169061336e61335161334c6133458585612282565b848b613e58565b613f04565b95875161335d826129d6565b976133688383612959565b52612959565b50612282565b6132629350600716915086613d5a565b6133919085949394613e14565b6101426001600160401b0383949316146133bd575b60076133b59293169086613d5a565b9291926131bc565b60076133cb6133b5936129d6565b9250506133a6565b6040906106aa939281528160208201520190611051565b90601f54918215159060c080910191825151915f9073__$b91d2ad766fd07dc99f56c1a0b020615bb$__925b84831061342557505050505050565b613436838799939495969751612959565b5151602092838201519060408093015193858351036135c25784518681151591826135b7575b505061359c5783516347f5343160e01b9384825287828c818061348360049c8d830161259d565b03915af49485156125785788925f96613575575b50906134af92918751938492839283528a830161259d565b03818d5af4968715612578575f97613556575b50508061354c575b61352357506134da90851c612258565b83851c918183036134f957505050509560018093019190949392613416565b5163eb9ba7ad60e01b81529283018681526020810191909152604081019190915281906060010390fd5b9151630b296d8760e31b8152928301868152602081019290925260408201528190036060019150fd5b50808214156134ca565b61356d929750803d10610c2057610c1181836105f9565b945f806134c2565b8392919650613593906134af943d8511610c2057610c1181836105f9565b95909192613497565b8351632d9a492360e21b815280610bba878b600484016133d3565b14159050865f61345c565b8351635812b67560e01b815280610bba858b600484016133d3565b60609093919301916135fb6135f6845163ffffffff1690565b61291f565b63ffffffff80821690811561391257613634637fffffff61362a6112168761278d6127798c5163ffffffff1690565b9460011c16612c7e565b90600f54926136536001600160401b0394858482169160201c16612282565b421015918086169182109383613905575b5050508115916138fd575b50156138f557613729936136c16136c69261368986600b55565b600f80546cffffffffffffffffff000000001916429290921660201b6bffffffffffffffff000000001691909117600160601b179055565b601f55565b7fa77ed82a4a8c2f7b412630e92b1c8f59e6645b05820a8b0ed43ae5fac990491c6137196136fb61196b875163ffffffff1690565b6040805186815263ffffffff90951660208601529093918291820190565b0390a2825163ffffffff166140a8565b61373f61373a825163ffffffff1690565b61410e565b613763613750825163ffffffff1690565b63ffffffff165f526020805260405f2090565b805460ff1916600117815591600354613784906001600160a01b0316611fd1565b90813b1561035657604051905f82636744c79f60e01b948582528183816137b360048201906001602083019252565b03925af1918215612578576137d6926138e2575b50835163ffffffff1690612de1565b60035491949092916137f0906001600160a01b0316611fd1565b91823b15610356575f90604051938491825281838161381660048201905f602083019252565b03925af1908115612578576138559284926138cf575b50805463ffffff00191662ffff00600888901b161791151560181b63ff00000016919091179055565b1580156138b0575b613865575050565b61255a61389a61196b7f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc9500935163ffffffff1690565b60405161ffff9094168452929081906020820190565b506138ca6119dc6138c5835163ffffffff1690565b612c93565b61385d565b8061256c6138dc926105b0565b5f61382c565b8061256c6138ef926105b0565b5f6137c7565b505050505050565b90505f61366f565b16111590505f8080613664565b50505050505050565b60ff5f80516020614c5b8339815191525460401c161561393757565b604051631afcd79f60e31b8152600490fd5b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b0384111761055a576040935f9385528380528360205251915afa6139b857565b9150505f51906020518060201c150290565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613a08575b5050610cb057565b5f80516020614c1b8339815191525416141590505f80613a00565b3d15613a4d573d90613a348261063e565b91613a4260405193846105f9565b82523d5f602084013e565b606090565b623d09005a1061295457600354613a71906001600160a01b0316611fd1565b803b156103565760405163a3ad9cc360e01b815263ffffffff9290921660048301525f908290602490829084905af19081613acf575b50613aca57613ab4613a23565b8051918215613ac35750602001fd5b9150505f90565b600190565b8061256c613adc926105b0565b5f613aa7565b90613aec8261063e565b613af960405191826105f9565b8281528092612995601f199161063e565b919080820391821161224657602090613b2283613ae2565b930101602083015e90565b5190811515820361035657565b90816020910312610356576106aa90613b2d565b91613b709061ffff604093969596168452606060208501526060840190611051565b6001600160a01b03909416910152565b9193926001600160401b039061ffff613ba3604094606087526060870190611051565b9616602085015216910152565b9160a093613be6613bf49261ffff6001600160401b03959a99989a6004885216602087015260c0604087015260c0860190611051565b908482036060860152611051565b951660808201520152565b91929461ffff83169061ee0f8214613d4a575061ee1314613d3c576016546040516359109f8560e01b815273__$a62ffa76e12530392778037a67b422bd63$__939160209082908190613c60906001600160a01b0316878760048501613b4e565b0381875af4908115612578575f91613d0d575b50613cbf57823b1561035657613ca15f956040519788968795869563173d424160e21b875260048701613bb0565b03915af4801561257857613cb25750565b8061256c610365926105b0565b6016547f6615e715ef29da98af4d4b50f6beb57ec1a6a61bb207b117e5fb58632755e93795509093925061255a9150613d00906001600160a01b0316611fd1565b9460405193849384613b80565b613d2f915060203d602011613d35575b613d2781836105f9565b810190613b3a565b5f613c73565b503d613d1d565b925050506103659150614492565b945050915050610365925061430d565b6001600160401b039283169283613d7b5750613d769250613e14565b905090565b919260018103613d91575050506106aa90612274565b60028103613daf57506106aa92613da791613e14565b911690612282565b600514159150613dc49050576106aa90612266565b60405162461bcd60e51b815260206004820152602260248201527f50726f746f62756652756e74696d653a20756e6b6e6f776e2077697265207479604482015261706560f01b6064820152608490fd5b91908201602001905f805b60806007600186515f1a94607f8616851b1796019201921615613e4b57603f8211610356579290613e1f565b9293909203601f19019150565b918181101580613ef9575b15613ea85781613e729161224b565b90613e7c82613ae2565b9282613e89575b50505090565b015f5b82811015613e8357602081830181015185830182015201613e8c565b60405162461bcd60e51b8152602060048201526024808201527f50726f746f62756652756e74696d653a20736c696365206f7574206f6620626f604482015263756e647360e01b6064820152608490fd5b508251811115613e63565b90613f0d61313a565b915f8151905b818110613f1f57505050565b613f299083613e14565b90816001600160401b0391828116600a81145f14613f7f57505050613f51613f779285613e14565b911690613f70613f6b613f648484612282565b8388613e58565b6146ec565b8752612282565b905b90613f13565b601203613fbd575050613f95613fb79285613e14565b911690613fad613fa8613f648484612282565b6145cf565b6020880152612282565b90613f79565b613fb79350600716915084613d5a565b9190613fe16001600160401b039184613e14565b911681019283821161224657613ff8918491613e58565b9190565b90614005613080565b915f8151905b81811061401757505050565b6140219083613e14565b90816001600160401b0391828116600a81145f14614069575050506140496140619285613e14565b911690613f7061405c613f648484612282565b61484f565b905b9061400b565b60120361409857505061407f6140929285613e14565b911690613fad61405c613f648484612282565b90614063565b6140929350600716915084613d5a565b63ffffffff8082165f52601460205260405f2090815416614109576140df600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b6141259063ffffffff918291826015541690612c4c565b1690811515908161414e575b506141395750565b5f5260146020525f6001604082208281550155565b9050815f52601460205260405f20541615155f614131565b90813b156141ea575f80516020614c1b83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a28051156141cf576141cc916148e8565b50565b5050346141d857565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b908160a0910312610356576040519060a082018281106001600160401b0382111761055a576040528051906006821015610356576080918352602081015161425281610348565b6020840152604081015161426581610348565b604084015261427660608201613b2d565b6060840152015161428681610348565b608082015290565b916142a660809295949560a0855260a0850190611051565b94601060208501526012604085015263ffffffff809216606085015216910152565b600611156142d257565b634e487b7160e01b5f52602160045260245ffd5b63ffffffff9182168152911660208201526060810192919060068210156142d25760400152565b9060a061431f600f5463ffffffff1690565b601e5460081c63ffffffff169361434a6040519586938493632b6e75a560e11b85526004850161428e565b038173__$a62ffa76e12530392778037a67b422bd63$__5af4918215612578575f92614461575b50815161437d816142c8565b614386816142c8565b61441357506143f66080826143a8611dac602061036596015163ffffffff1690565b6143bc611dcc604083015163ffffffff1690565b6143eb6143cc6060830151151590565b64ff0000000060115491151560201b169064ff00000000191617601155565b015163ffffffff1690565b64ffffffff00601e549160081b169064ffffffff00191617601e55565b907f06bc5809fcff2cb3c29e300b687d3444b42c7836482312d880b48c4bcfbec92291611c3561444a608084015163ffffffff1690565b9251614455816142c8565b604051938493846142e6565b61448491925060a03d60a01161448b575b61447c81836105f9565b81019061420b565b905f614371565b503d614472565b6001600160401b03906001826018541601908282116122465773__$a62ffa76e12530392778037a67b422bd63$__90813b15610356575f916144f291604051809681948293637401af5d60e01b845260e0600485015260e4840190611051565b90601260248401526013604484015260196064840152601a6084840152601b60a4840152871660c483015203915af4918215612578576103659261454e575b506001600160401b03166001600160401b03196018541617601855565b8061256c61455b926105b0565b5f614531565b6040519061456e82610595565b60606040835f81525f60208201520152565b9061458a82610627565b61459760405191826105f9565b82815280926145a8601f1991610627565b01905f5b8281106145b857505050565b6020906145c3614561565b828285010152016145ac565b906145d8613121565b915f8151905f805b83811061469e57506145f190614580565b91602086019283525f915b81811061460a575050505050565b6146149085613e14565b90816001600160401b0391828116600881145f1461465757505050509061465161464363ffffffff9387613e14565b9316889063ffffffff169052565b906145fc565b60120361468e57505061466d6146889287613e14565b91169061336e6133516146836133458585612282565b614905565b90614651565b6146889350600716915086613d5a565b6146ab9085949394613e14565b60126001600160401b0383949316146146d6575b60076146ce9293169086613d5a565b9291926145e0565b60076146e46146ce936129d6565b9250506146bf565b906146f56130e4565b915f8151905b81811061470757505050565b6147119083613e14565b90816001600160401b0391828116600a81145f146147545750505061473961474c9285613e14565b911690613f70613253613f648484612282565b905b906146fb565b91925090601281036147795750505061476d9083613fcd565b9190602086015261474e565b601a810361479a5750505061478e9083613fcd565b9190604086015261474e565b602881036147ce57505050906147c96147b863ffffffff9385613e14565b9316606087019063ffffffff169052565b61474e565b603281036147ef575050506147e39083613fcd565b9190608086015261474e565b6038810361481b57505050906148086147c99284613e14565b6001600160401b0390911660a087015291565b60420361483a57505061482e9083613fcd565b919060c086015261474e565b61484992506007169084613d5a565b9061474e565b90614858613068565b915f8151905b81811061486a57505050565b6148749083613e14565b80916001600160401b038116600881145f146148ae575050509060ff61489d6148a49385613e14565b93906149d1565b1685525b9061485e565b6010036148d3575050906148c763ffffffff9284613e14565b921660208601526148a8565b6148e292506007169084613d5a565b906148a8565b5f806106aa93602081519101845af46148ff613a23565b91614a22565b9061490e614561565b915f8151905b81811061492057505050565b61492a9083613e14565b80916001600160401b038116600881145f14614969575050509061496361495461495b9385613e14565b9390614a85565b61ffff168652565b90614914565b6010810361499d575050509061499861498763ffffffff9385613e14565b9316602087019063ffffffff169052565b614963565b601a036149bc5750506149b09083613fcd565b91906040860152614963565b6149cb92506007169084613d5a565b90614963565b6001600160401b031680156129545760018114614a1c5760028114614a165760038114614a105760249060405190632cd3748760e21b82526004820152fd5b50600390565b50600290565b50600190565b90614a495750805115614a3757602081519101fd5b60405163d6bda27560e01b8152600490fd5b81511580614a7c575b614a5a575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15614a52565b6001600160401b038116801561239e576107d1808214614bf45750610bb9808214614bf45750610bba808214614bf45750610bbc808214614bf45750610bbe808214614bf4575061aa00808214614bf4575061ee00808214614bf4575061ee02808214614bf4575061ee04808214614bf4575061ee06808214614bf4575061ee10808214614bf4575061ee11808214614bf4575061ee13808214614bf4575061ee0f808214614bf4575061ee15808214614bf4575061ee16808214614bf4575061ee17808214614bf4575061ee18808214614bf4575061ee19808214614bf4575061ee1b808214614bf4575061ee1c808214614bf4575061ee1e808214614bf4575061ee1f808214614bf4575061ee20808214614bf4575061ee21808214614bf4575061ee22808214614bf4575061ee23808214614bf4575061ee24808214614bf4575061ee25809114613d7657604051632cd3748760e21b81526001600160401b0383166004820152602490fd5b9150509056fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc1b6847dc741a1b0cd08d278845f9d819d87b734759afb55fe2de5cb82a9ae672f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a264697066735822122010a68012febf8fe1754f31c152149d058698cd9a133878ab2a1ddcfd7b362bf164736f6c63430008190033", + "bytecode": "0x60a0806040523460d657306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1660c757506001600160401b036002600160401b0319828216016083575b600163ffffffff195f5416175f55604051614ad490816100db8239608051818181610df30152613b950152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f80806056565b63f92ee8a960e01b8152600490fd5b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80624e356a1461032357806308a9a6de1461031e5780630fa92c5314610319578063151b8f4e146103145780631bdf0a721461030f5780631d7a88271461030a57806320857e651461030557806322fe388b14610300578063239e1d67146102fb5780632945698b146102f65780632f4d9fa9146102f15780633367cca5146102ec57806336051135146102e75780633915ba63146102e25780634ab47f43146102dd5780634cc04c94146102d85780634f1ef286146102d35780635126a52d146102ce57806352d1902d146102c9578063533ae1cc146102c45780635701a93f146102bf5780635c0c8f44146102ba5780635d4e25ce146102b55780635ff8195e146102b05780637a9e5e4b146102ab5780637b778122146102a657806380e643b6146102a15780638b75e9291461029c5780638bdebf21146102975780638d9f616b146102925780638fb360371461028d57806393ec06be1461028857806399b1a19c14610283578063ad3cb1cc1461027e578063af243e8914610279578063b1635f9f14610274578063b41565b81461026f578063bf7e214f1461026a578063c0d0373e14610265578063c268863f14610260578063c45fdcb71461025b578063cd2ad3fb14610256578063d0841f2214610251578063d1ad8d011461024c578063d3f0730614610247578063d5b5272a14610242578063e3dff2af1461023d578063e55ba576146102385763fdfd07df14610233575f80fd5b611789565b61174a565b61172d565b61167f565b61164b565b6115f1565b6115c9565b611564565b611501565b611489565b611373565b61133f565b611293565b6111e5565b6111b2565b61116d565b611124565b611108565b6110b8565b611079565b611053565b611018565b610ff4565b610f97565b610f19565b610ef6565b610ed9565b610e90565b610e6d565b610e4a565b610de1565b610daf565b610ca6565b610c8b565b610c5e565b610c2c565b610be2565b610ba8565b610b8b565b610b5e565b610b3c565b610b21565b610a80565b61092a565b61088b565b610868565b610823565b610737565b610374565b63ffffffff81160361033657565b5f80fd5b359061034582610328565b565b9181601f84011215610336578235916001600160401b038311610336576020838186019501011161033657565b346103365760403660031901126103365760043561039181610328565b6024356001600160401b038111610336576103b0903690600401610347565b90917f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0091825c6103ec575f936103e9926001855d6117cf565b5d005b604051633ee5aeb560e01b8152600490fd5b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b0382111761042d57604052565b6103fe565b602081019081106001600160401b0382111761042d57604052565b604081019081106001600160401b0382111761042d57604052565b606081019081106001600160401b0382111761042d57604052565b6001600160401b03811161042d57604052565b60c081019081106001600160401b0382111761042d57604052565b60e081019081106001600160401b0382111761042d57604052565b90601f801991011681019081106001600160401b0382111761042d57604052565b6001600160401b03811161042d5760051b60200190565b6001600160401b03811161042d57601f01601f191660200190565b92919261052b82610504565b9161053960405193846104cc565b829481845281830111610336578281602093845f960137010152565b9080601f83011215610336578160206105709335910161051f565b90565b91906080838203126103365760409283519161058e83610412565b8294823561059b81610328565b84526020808401356105ac81610328565b858201526001600160401b0392848301358481116103365785019080601f83011215610336578135906105de826104ed565b956105eb865197886104cc565b828752848701918560059460051b8601019481861161033657868101935b86851061062f57505050505050505060609261062a9284928601520161033a565b910152565b843584811161033657820188601f1982860301126103365789519061065382610432565b8981013590868211610336570184603f820112156103365789810135610678816104ed565b916106858d5193846104cc565b8183528c8c8401928b1b82010190878211610336578c94929593918e8201915b8183106106bf575050508394508152815201940193610609565b91809496509690929496358a8111610336578f90830190603f19828c030112610336578f90918f928251906106f38261044d565b838101359060ff82168203610336578e9183526060810135918211610336578594610721928f920101610555565b83820152815201930190918d95939694926106a5565b34610336576020366003190112610336576001600160401b0360043581811161033657610768903690600401610573565b906107733633612265565b63ffffffff805f5416805f52600660205261079660405f2063ffffffff90541690565b916001821490811591610817575b506107ed576107eb836107be866107b9611a0c565b612477565b42166bffffffffffffffff000000006007549160201b16906bffffffffffffffff00000000191617600755565b005b6040516325ea24eb60e11b815263ffffffff918216600482015291166024820152604490fd5b0390fd5b9050821615155f6107a4565b3461033657602036600319011261033657602060043561084281610328565b63ffffffff8091165f526006825260405f205416604051908152f35b5f91031261033657565b34610336575f36600319011261033657602063ffffffff60075416604051908152f35b34610336575f366003190112610336576001600160401b0363ffffffff610915815f5416805f5260066020528260405f205416906108c881612950565b6007549260405196879685169460201c169286936001600160401b039060809497969260a087019863ffffffff8096818094168a5216602089015216604087015216606085015216910152565b0390f35b6001600160a01b0381160361033657565b346103365760403660031901126103365760043561094781610919565b6001600160401b039060243582811161033657610968903690600401610573565b5f80516020614a7f833981519152549260ff8460401c1615931680159081610a6c575b6001149081610a62575b159081610a59575b50610a47575f80516020614a7f833981519152805467ffffffffffffffff191660011790556109d09183610a2357611a75565b6109d657005b5f80516020614a7f833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b5f80516020614a7f833981519152805460ff60401b1916600160401b179055611a75565b60405163f92ee8a960e01b8152600490fd5b9050155f61099d565b303b159150610995565b84915061098b565b61ffff81160361033657565b3461033657604036600319011261033657600435610a9d81610a74565b61ffff60243591610aad83610919565b610ab73633612265565b16805f526003602052807f46d568db0216a0215fe7f56360f9fa1a002c4bf0773add9cd62cd06eff160189604060018060a01b039485825f2054169582519116958682526020820152a25f90815260036020526040902080546001600160a01b0319169091179055005b34610336575f36600319011261033657602060405160078152f35b34610336575f36600319011261033657602063ffffffff5f5416604051908152f35b34610336576020366003190112610336576004355f526012602052602060ff60405f205416604051908152f35b34610336575f366003190112610336576020601454604051908152f35b34610336576020366003190112610336576020610bd8600435610bca81610919565b63ffffffff5f541690611be8565b6040519015158152f35b34610336576020366003190112610336576001600160401b0363ffffffff610915815f5416805f52600e60205260405f206004355f526020528260405f205416906108c881612950565b34610336576020366003190112610336576004355f52600a602052602060018060a01b0360405f205416604051908152f35b34610336576020366003190112610336576004355f526011602052602060ff60405f205416604051908152f35b34610336575f36600319011261033657602060405160018152f35b604036600319011261033657600435610cbe81610919565b6024356001600160401b03811161033657610cdd903690600401610555565b90610ce6613b8b565b610cf03633612265565b610cf8613b8b565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610d7e575b50610d4657604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b905f80516020614a3f8339815191528303610d65576107eb92506142e0565b604051632a87526960e21b815260048101849052602490fd5b610da191945060203d602011610da8575b610d9981836104cc565b810190611e79565b925f610d20565b503d610d8f565b34610336576020366003190112610336576004355f52600b602052602060018060a01b0360405f205416604051908152f35b34610336575f366003190112610336577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610e385760206040515f80516020614a3f8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610336575f36600319011261033657602063ffffffff60135416604051908152f35b34610336575f36600319011261033657602063ffffffff600d5416604051908152f35b3461033657602036600319011261033657600435610ead81610919565b610eb73633612265565b600280546001600160a01b0319166001600160a01b0392909216919091179055005b34610336575f366003190112610336576020600154604051908152f35b34610336575f36600319011261033657602063ffffffff60095416604051908152f35b3461033657602036600319011261033657600435610f3681610919565b5f80516020614a1f833981519152546001600160a01b03919082163303610f8057803b15610f67576107eb9061298a565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b34610336576040366003190112610336576020610feb600435610fb981610328565b63ffffffff60243591610fcb83610919565b165f526005835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b34610336575f36600319011261033657602060095460ff60405191831c1615158152f35b3461033657602036600319011261033657604061103f60043561103a81610328565b611b54565b63ffffffff83519216825215156020820152f35b34610336575f3660031901126103365760206001600160401b03600f5416604051908152f35b346103365760203660031901126103365761ffff60043561109981610a74565b165f526003602052602060018060a01b0360405f205416604051908152f35b34610336575f366003190112610336575f80516020614a1f8339815191525460a01c60ff1615611100576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f6110ed565b34610336575f3660031901126103365760206040516180008152f35b34610336575f36600319011261033657602060ff60075460601c166040519015158152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b34610336575f3660031901126103365761091560405161118c8161044d565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611149565b34610336576020366003190112610336576004355f52601060205260206001600160401b0360405f205416604051908152f35b346103365760603660031901126103365760043561120281610a74565b6024356001600160401b03811161033657611221903690600401610347565b6044929192359161123183610328565b30330361127b5761ffff1661ee0f810361125a57506107eb9261125591369161051f565b612bc0565b61ee139192501461126757005b6107eb9161127691369161051f565b6129e2565b604051630c86f24360e41b8152336004820152602490fd5b34610336576020366003190112610336576109156004356112b381610328565b60405f8180516112c281610468565b828152826020820152015263ffffffff8092165f52600c6020526001815f206001600160401b038351946112f586610468565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610336575f366003190112610336575f80516020614a1f833981519152546040516001600160a01b039091168152602090f35b346103365760203660031901126103365760043561139081610328565b61139a3633612265565b63ffffffff90816113c66113bc8363ffffffff165f52600c60205260405f2090565b5463ffffffff1690565b1615611469576113ea6113e56113e05f5463ffffffff1690565b612d3b565b612d72565b8282169281168311611443575061141261141d9163ffffffff165f52600c60205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b604051631cc9a6c760e21b815263ffffffff928316600482015291166024820152604490fd5b604051630529520560e31b815263ffffffff919091166004820152602490fd5b34610336575f366003190112610336576002546040516001600160a01b039091168152602090f35b634e487b7160e01b5f52603260045260245ffd5b6008548110156114e75760085f525f80516020614a5f83398151915201905f90565b6114b1565b80548210156114e7575f5260205f2001905f90565b3461033657604036600319011261033657600435602435906008548110156103365760085f525f80516020614a5f8339815191520180548210156103365760209161154b916114ec565b905460405160039290921b1c6001600160a01b03168152f35b346103365760203660031901126103365763ffffffff60043561158681610328565b165f526015602052608060405f205460ff604051918181161515835261ffff8160081c166020840152818160181c161515604084015260201c1615156060820152f35b34610336575f3660031901126103365760206007546001600160401b0360405191831c168152f35b3461033657602036600319011261033657606060043561161081610328565b63ffffffff8091165f52600c60205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b34610336576040366003190112610336576020610bd860043561166d81610919565b6024359061167a82610328565b611be8565b346103365760203660031901126103365760043561169c81610328565b6116a63633612265565b6040516116b281610432565b63ffffffff908183168082521561171b57600d80549151831663ffffffff1983161790556040805163ffffffff93909216831682529290911660208201527f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69181908101610a1e565b604051639ca50c6160e01b8152600490fd5b34610336575f366003190112610336576020600454604051908152f35b346103365760203660031901126103365763ffffffff60043561176c81610328565b6117763633612265565b1663ffffffff1960075416176007555f80f35b346103365760403660031901126103365760206004356117a881610328565b63ffffffff8091165f52600e825260405f206024355f52825260405f205416604051908152f35b909291926117ed6117e45f5463ffffffff1690565b63ffffffff1690565b63ffffffff908184169081106119f0576118158463ffffffff165f52601560205260405f2090565b5460ff811690816119e1575b5061195d575f5463ffffffff169082821603611936575060095460ff8160201c166118c5575b505061185a6118568333611be8565b1590565b6118ad57831580156118a2575b6118815761187b906103459394369161051f565b90611e9d565b604051630c6f042160e31b8152600481018590526180006024820152604490fd5b506180008411611867565b604051632af0051560e21b8152336004820152602490fd5b6118ce84611b54565b905061184757610813846118e760135463ffffffff1690565b906118f760085463ffffffff1690565b604051631bf926d360e11b815263ffffffff92831660048201529282166024840152939094168416604482015292909116606483015281906084820190565b6040516307d3e83d60e51b815263ffffffff91821660048201529084166024820152604490fd5b50509261198d61197b8363ffffffff165f52600560205260405f2090565b335f9081526020919091526040902090565b54936004548095036118ad576119a491369161051f565b9081516020830120938085036119c05750610345929350611c9a565b6040516340d6b4cd60e01b8152600481019190915260248101859052604490fd5b60ff915060201c16155f611821565b505050509050565b634e487b7160e01b5f52601160045260245ffd5b6008545f60085580611a1b5750565b60085f525f80516020614a5f833981519152908101905b818110611a3d575050565b80545f825580611a51575b50600101611a32565b815f5260205f20908101905b818110611a6a5750611a48565b5f8155600101611a5d565b90611a9960c892611a84613b5d565b611a8c613b5d565b611a94613b5d565b61298a565b611aa1613b5d565b5f805463ffffffff19908116600117909155600780546bffffffffffffffff0000000019164260201b6bffffffffffffffff000000001617905590611ae590612477565b81604051611af281610432565b52600d541617600d55565b5f19810191908211611b0b57565b6119f8565b91908203918211611b0b57565b9060018201809211611b0b57565b9060048201809211611b0b57565b9060088201809211611b0b57565b91908201809211611b0b57565b60095460ff8160201c1615611bcc57600854908115611bdf57611b7f6117e460135463ffffffff1690565b9063ffffffff80941682811115611bd457611ba6611ba1611bad948793611b10565b611afd565b9116611b47565b90811015611bcc57611bbe816114c5565b505415611bcc571690600190565b50505f905f90565b50505050505f905f90565b5050505f905f90565b90611bf290611b54565b15611c60576008548110156114e75760085f525f80516020614a5f83398151915201908154915f5b838110611c2957505050505f90565b611c3381836114ec565b905460039190911b1c6001600160a01b0390811690841614611c5757600101611c1a565b50505050600190565b50505f90565b6040513d5f823e3d90fd5b90600161ffff80931601918211611b0b57565b91909161ffff80809416911601918211611b0b57565b90611cb38263ffffffff165f52601560205260405f2090565b90611cc3825460ff9060181c1690565b611e5b57600254611ce4906001600160a01b03165b6001600160a01b031690565b90813b1561033657604051905f82636744c79f60e01b94858252818381611d1360048201906001602083019252565b03925af1908115611e4357611d39928692611e48575b50845460081c61ffff16906130c4565b60025490929190611d52906001600160a01b0316611cd8565b91823b15610336575f906040519384918252818381611d7860048201905f602083019252565b03925af1908115611e4357611db792611d9f92611e2a575b50845460081c61ffff16611c84565b835462ffff00191660089190911b62ffff0016178355565b15611de157805463ff00000019166301000000178155611dd961185683612d9a565b611de1575050565b547f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc95009063ffffffff9060081c61ffff1660405161ffff909116815292169180602081015b0390a2565b80611e37611e3d92610483565b8061085e565b5f611d90565b611c66565b80611e37611e5592610483565b5f611d29565b50611dd961185683612d9a565b906020610570928181520190611149565b90816020910312610336575190565b63ffffffff809116908114611b0b5760010190565b9190611ea88161338b565b60209182808301510151611ebd815160ff1690565b90600260ff8316148015906121db575b6121aa5750506060820194611ee6865163ffffffff1690565b9063ffffffff8082169083160361218457505060a082015183815180155f146120c357505050600154806120a357505b611f1f826135ca565b9281519082012091611f4f61197b611f3b885163ffffffff1690565b63ffffffff165f52600560205260405f2090565b548015611fa2578303611f67576103459495506137bd565b610813611f78875163ffffffff1690565b60405163082085ab60e21b815263ffffffff90911660048201523360248201529081906044820190565b5061206f6117e4610345969785611fd933611fc4611f3b855163ffffffff1690565b9060018060a01b03165f5260205260405f2090565b5561202c612002611fee835163ffffffff1690565b63ffffffff165f52600660205260405f2090565b612018612013825463ffffffff1690565b611e88565b63ffffffff1663ffffffff19825416179055565b61206561200287612058612044855163ffffffff1690565b63ffffffff165f52600e60205260405f2090565b905f5260205260405f2090565b5163ffffffff1690565b6040518481523391907fcafc00e7c35f462868d78e8228850e885bd0870978967c446c6bb67122a3fb2890602090a36137bd565b604051631d734b9160e01b815260048101919091525f6024820152604490fd5b03612148576040516347f5343160e01b8152908482806120e68460048301611e68565b038173__$6c94be45b3cad4e4a0bccaa807f5821f2b$__5af4918215611e43575f92612165575b5081156121485750600154808203612126575050611f16565b604051631d734b9160e01b815260048101919091526024810191909152604490fd5b604051630ca3ddf160e01b81529081906108139060048301611e68565b61217d919250853d8711610da857610d9981836104cc565b905f61210d565b604051635070175360e01b815263ffffffff918216600482015291166024820152604490fd5b8401516040516385a89b3360e01b815246600482015260ff92909216602483015263ffffffff166044820152606490fd5b508085015163ffffffff1663ffffffff16461415611ecd565b600411610336575f90600490565b6001600160e01b0319903581811693926004811061221f57505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f80516020614a1f833981519152805461229d906001600160a01b031661229461228e866121f4565b90612202565b90843091613adc565b90156122aa575b50505050565b63ffffffff161561235b575f80516020614a1f833981519152805460ff60a01b1916600160a01b1790556122eb90611cd8908190546001600160a01b031690565b91823b1561033657612316925f9283604051809681958294634a63ebf760e11b84526004840161222e565b03925af18015611e4357612348575b505f80516020614a1f833981519152805460ff60a01b191690555f8080806122a4565b80611e3761235592610483565b5f612325565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b80518210156114e75760209160051b010190565b90612399826104ed565b6123a660405191826104cc565b82815280926123b7601f19916104ed565b0190602036910137565b90602082519201516bffffffffffffffffffffffff199081811693601481106123e957505050565b60140360031b82901b16169150565b5f198114611b0b5760010190565b600854600160401b81101561042d5760018101806008558110156114e75760085f525f80516020614a5f83398151915201905f90565b8054600160401b81101561042d57612459916001820181556114ec565b60018060a09493941b038084549260031b9316831b921b1916179055565b9060409182810192835151908115612920576060830163ffffffff93846124a2835163ffffffff1690565b16156128db57836124ba6117e4835163ffffffff1690565b1015612896575f935f5b81811061283a57506124d58561238f565b5f805b8382106126265750505f905f5b8381106125bb57505050507ff485fcf1271cbcd2319ebf860c827016cef79959673d605215dcd74ed0629ea4959661255e6125486125b69594612065612532612595965163ffffffff1690565b63ffffffff1663ffffffff196009541617600955565b63ffffffff1663ffffffff196007541617600755565b61257864010000000064ff00000000196009541617600955565b61258a63ffffffff1960135416601355565b515163ffffffff1690565b905163ffffffff918216815293909216909116602083015281906040820190565b0390a1565b6125c6818c5161237b565b5151516125d1612406565b50506125dc826114c5565b505f5b8281106125f1575050506001016124e5565b60019061262061261a61260d6126068a6123f8565b998961237b565b516001600160a01b031690565b8461243c565b016125df565b97909891966126398a8c95969c5161237b565b515195899b5f5b885181101561282357600261266c61266661266661265e858e61237b565b515160ff1690565b60ff1690565b036127de576020601481612680848d61237b565b5101515103612799576126a26126a89161269a848d61237b565b5101516123c1565b60601c90565b6001600160a01b0381168015612754578f8d8f5b82106126ef575050508c6126cf906123f8565b9c6126da908d61237b565b6001600160a01b039091169052600101612640565b612700611cd861260d84869461237b565b1461270f576001018d8f6126bc565b8c5163098d772360e21b815260206004820152601a60248201527f6475706c6963617465206f70657261746f7220616464726573730000000000006044820152606490fd5b8b5163098d772360e21b815260206004820152601860248201527f6f70657261746f722061646472657373206973207a65726f00000000000000006044820152606490fd5b8a5163098d772360e21b815260206004820181905260248201527f6f70657261746f722061646472657373206973206e6f742032302062797465736044820152606490fd5b895163098d772360e21b815260206004820152601e60248201527f6f70657261746f72206973206e6f7420616e2045564d206164647265737300006044820152606490fd5b5095949b50955096919860019098919801906124d8565b94612846868a5161237b565b5151519081156128635760019161285c91611b47565b95016124c4565b855163098d772360e21b815260206004820152600b60248201526a0656d7074792067726f75760ac1b6044820152606490fd5b825163098d772360e21b815260206004820152601d60248201527f61637469766547726f7570496e646578206f7574206f662072616e67650000006044820152606490fd5b825163098d772360e21b815260206004820152601860248201527f65706f63684475726174696f6e536563206973207a65726f00000000000000006044820152606490fd5b5163098d772360e21b81526020600482015260096024820152686e6f2067726f75707360b81b6044820152606490fd5b61295990611b54565b15612985576008548110156114e75760085f525f80516020614a5f833981519152015463ffffffff1690565b505f90565b5f80516020614a1f83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b6001600160401b0390600182600f54160190828211611b0b5773__$a62ffa76e12530392778037a67b422bd63$__90813b15610336575f91612a4291604051809681948293637401af5d60e01b845260e0600485015260e4840190611149565b90600a6024840152600b60448401526010606484015260116084840152601260a4840152871660c483015203915af4918215611e435761034592612a9e575b506001600160401b03166001600160401b0319600f541617600f55565b80611e37612aab92610483565b5f612a81565b5190811515820361033657565b908160a0910312610336576040519060a082018281106001600160401b0382111761042d5760405280519060068210156103365760809183526020810151612b0581610328565b60208401526040810151612b1881610328565b6040840152612b2960608201612ab1565b60608401520151612b3981610328565b608082015290565b91612b5960809295949560a0855260a0850190611149565b9460086020850152600a604085015263ffffffff809216606085015216910152565b60061115612b8557565b634e487b7160e01b5f52602160045260245ffd5b63ffffffff918216815291166020820152606081019291906006821015612b855760400152565b9060a0612bd260075463ffffffff1690565b60135463ffffffff1693612bfa6040519586938493632b6e75a560e11b855260048501612b41565b038173__$a62ffa76e12530392778037a67b422bd63$__5af4918215611e43575f92612d0a575b508151612c2d81612b7b565b612c3681612b7b565b612cbc5750612ca6608082612c58612532602061034596015163ffffffff1690565b612c6c612548604083015163ffffffff1690565b612c9b612c7c6060830151151590565b64ff0000000060095491151560201b169064ff00000000191617600955565b015163ffffffff1690565b63ffffffff1663ffffffff196013541617601355565b907f06bc5809fcff2cb3c29e300b687d3444b42c7836482312d880b48c4bcfbec922916125b6612cf3608084015163ffffffff1690565b9251612cfe81612b7b565b60405193849384612b99565b612d2d91925060a03d60a011612d34575b612d2581836104cc565b810190612abe565b905f612c21565b503d612d1b565b63ffffffff9060018183161115611c60575f19011690565b63ffffffff91908282168382161115612d6b57031690565b5050505f90565b6105709063ffffffff600d541690612d53565b90600163ffffffff80931601918211611b0b57565b612da381613c13565b156129855760207fe784b22f2061de501b77364cab02ee109528afe4ddc8d423d076f6b49021b58e9163ffffffff612e0181831692835f526015855260405f2064010000000064ff0000000019825416179055600454600155612d85565b1663ffffffff195f5416175f55604051908152a1600190565b6020908181840312610336578051906001600160401b038211610336570182601f8201121561033657805191612e4f836104ed565b936040612e5f60405196876104cc565b848652828601918360c080970286010194818611610336578401925b858410612e8c575050505050505090565b8684830312610336578487918451612ea381610496565b8651612eae81610a74565b815282870151838201528587015186820152606080880151908201526080808801519082015260a08088015190820152815201930192612e7b565b60408051637d3b06bb60e11b81525f94929360049190868280612f0e89878301611e68565b038173__$9d71d8de471125782e5046de17c7aa75b6$__5af4918215611e43575f926130a0575b505f965b825188101561309457620f42405a106130505784612f615a612f5b5a60031c90565b90611b10565b612fe184828b8d6001600160401b03612fd8612fbb8c612f97612f90612f87878461237b565b515161ffff1690565b61ffff1690565b9a612fb1866020612fa8828661237b565b5101519361237b565b5101519086613ccb565b938c60a0612fcf866080612fa8828661237b565b51015191613ccb565b91169187613dbe565b9290612ffd57505050612ff5600191611c71565b970196612f39565b959799509593509650905061ffff8116613045575051632845f55560e21b81526001600160a01b0390941692840192835261ffff166020830152604082015281906060010390fd5b945050505050905f90565b9396505050925061ffff811661308d5750506108135a9251630e76ff3560e41b8152918201928352620f4240602084015290918291604090910190565b92505f9150565b96506001955050505050565b6130bd9192503d805f833e6130b581836104cc565b810190612e1a565b905f612f35565b92905f926040805190637d3b06bb60e11b82526004925f83806130ea8b60048301611e68565b038173__$9d71d8de471125782e5046de17c7aa75b6$__5af4928315611e43575f93613231575b5061ffff809516965b835188101561322457620f42405a106131e95761313b5a612f5b5a60031c90565b61314b612f90612f878b8861237b565b9061318784828d8d6001600160401b03612fd86131738d8d612fb1866020612fa8828661237b565b938d60a0612fcf866080612fa8828661237b565b92906131a35750505061319b600191611c71565b97019661311a565b96989a5096925097508092935016613045575051632845f55560e21b81526001600160a01b0390941692840192835261ffff166020830152604082015281906060010390fd5b9493965050955050811661308d5750506108135a9251630e76ff3560e41b8152918201928352620f4240602084015290918291604090910190565b9750600196505050505050565b6132469193503d805f833e6130b581836104cc565b915f613111565b6040519061325a8261044d565b5f6020838281520152565b604051906132728261044d565b8161327b61324d565b8152602061062a61324d565b60405190613294826104b1565b606060c0838281526132a4613265565b60208201525f60408201525f838201525f60808201528260a08201520152565b604051906132d1826104b1565b606060c0836132de613265565b81528260208201528260408201525f838201528260808201525f60a08201520152565b6040519061330e8261044d565b60606020835f81520152565b604051906133278261044d565b816133306132c4565b8152602061062a613301565b90613346826104ed565b61335360405191826104cc565b8281528092613364601f19916104ed565b01905f5b82811061337457505050565b60209061337f61331a565b82828501015201613368565b90613394613287565b915f8151905f805b83811061356457506133ad9061333c565b9160c086019283525f915b8181106133c6575050505050565b6133d09085613f8e565b90816001600160401b0391828116600a81145f1461340157505050506133f69085614147565b919087525b906133b8565b60128103613449575050506134196134429287613f8e565b91169061343861343361342c8484611b47565b838a613fd2565b614176565b60208a0152611b47565b905b6133fb565b602881036134765750505050906134636134449286613f8e565b6001600160401b03909116604089015291565b603081036134a657505050509061344461349563ffffffff9387613f8e565b9316606089019063ffffffff169052565b603881036134d65750505050906134446134c563ffffffff9387613f8e565b9316608089019063ffffffff169052565b60a281036134f857505050506134ec9085614147565b919060a08801526133fb565b6101420361355457505061350f6134429287613f8e565b91169061354e61353161352c6135258585611b47565b848b613fd2565b61407e565b95875161353d826123f8565b97613548838361237b565b5261237b565b50611b47565b6134429350600716915086613ed4565b6135719085949394613f8e565b6101426001600160401b03839493161461359d575b60076135959293169086613ed4565b92919261339c565b60076135ab613595936123f8565b925050613586565b604090610570939281528160208201520190611149565b90601454918215159060c080910191825151915f9073__$6c94be45b3cad4e4a0bccaa807f5821f2b$__925b84831061360557505050505050565b61361683879993949596975161237b565b5151602092838201519060408093015193858351036137a2578451868115159182613797575b505061377c5783516347f5343160e01b9384825287828c818061366360049c8d8301611e68565b03915af4948515611e435788925f96613755575b509061368f92918751938492839283528a8301611e68565b03818d5af4968715611e43575f97613736575b50508061372c575b61370357506136ba90851c611b1d565b83851c918183036136d9575050505095600180930191909493926135f6565b5163eb9ba7ad60e01b81529283018681526020810191909152604081019190915281906060010390fd5b9151630b296d8760e31b8152928301868152602081019290925260408201528190036060019150fd5b50808214156136aa565b61374d929750803d10610da857610d9981836104cc565b945f806136a2565b83929196506137739061368f943d8511610da857610d9981836104cc565b95909192613677565b8351632d9a492360e21b815280610813878b600484016135b3565b14159050865f61363c565b8351635812b67560e01b815280610813858b600484016135b3565b60609093919301916137db6137d6845163ffffffff1690565b612950565b63ffffffff808216908115613ad357613814637fffffff61380a6113bc876120586120448c5163ffffffff1690565b9460011c16612d85565b90600754926138336001600160401b0394858482169160201c16611b47565b421015918086169182109383613ac6575b505050811591613abe575b5015613ab6576138e9936138816138869261386986600455565b6007805460ff60601b1916600160601b1790556107be565b601455565b7fa77ed82a4a8c2f7b412630e92b1c8f59e6645b05820a8b0ed43ae5fac990491c6138d96138bb6117e4875163ffffffff1690565b6040805186815263ffffffff90951660208601529093918291820190565b0390a2825163ffffffff16614222565b6138ff6138fa825163ffffffff1690565b614288565b613924613910825163ffffffff1690565b63ffffffff165f52601560205260405f2090565b805460ff1916600117815591600254613945906001600160a01b0316611cd8565b90813b1561033657604051905f82636744c79f60e01b9485825281838161397460048201906001602083019252565b03925af1918215611e435761399792613aa3575b50835163ffffffff1690612ee9565b60025491949092916139b1906001600160a01b0316611cd8565b91823b15610336575f9060405193849182528183816139d760048201905f602083019252565b03925af1908115611e4357613a16928492613a90575b50805463ffffff00191662ffff00600888901b161791151560181b63ff00000016919091179055565b158015613a71575b613a26575050565b611e25613a5b6117e47f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc9500935163ffffffff1690565b60405161ffff9094168452929081906020820190565b50613a8b611856613a86835163ffffffff1690565b612d9a565b613a1e565b80611e37613a9d92610483565b5f6139ed565b80611e37613ab092610483565b5f613988565b505050505050565b90505f61384f565b16111590505f8080613844565b50505050505050565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b0384111761042d576040935f9385528380528360205251915afa613b4b57565b9150505f51906020518060201c150290565b60ff5f80516020614a7f8339815191525460401c1615613b7957565b604051631afcd79f60e31b8152600490fd5b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613bc9575b5050610e3857565b5f80516020614a3f8339815191525416141590505f80613bc1565b3d15613c0e573d90613bf582610504565b91613c0360405193846104cc565b82523d5f602084013e565b606090565b623d09005a1061298557600254613c32906001600160a01b0316611cd8565b803b156103365760405163a3ad9cc360e01b815263ffffffff9290921660048301525f908290602490829084905af19081613c90575b50613c8b57613c75613be4565b8051918215613c845750602001fd5b9150505f90565b600190565b80611e37613c9d92610483565b5f613c68565b90613cad82610504565b613cba60405191826104cc565b82815280926123b7601f1991610504565b91908181118015613d14575b613cfc57808203918211611b0b57602090613cf183613ca3565b930101602083015e90565b6024906040519063d98776dd60e01b82526004820152fd5b5082518211613cd7565b91613d4563ffffffff9161ffff604094979697168552606060208601526060850190611149565b9416910152565b91908260409103126103365761057060208351613d6881610919565b9301612ab1565b9160a093613da5613db39261ffff6001600160401b03959a99989a6003885216602087015260c0604087015260c0860190611149565b908482036060860152611149565b951660808201520152565b9194959390929561ffff831661ee0f8114908115613ec8575b50613e5b5750613e009060409495968551978895869563173d424160e21b875260048701613d6f565b038173__$a62ffa76e12530392778037a67b422bd63$__5af48015611e43575f925f91613e2c57509190565b9050613e5191925060403d604011613e54575b613e4981836104cc565b810190613d4c565b91565b503d613e3f565b5f955085949650613e97859294613e89604051938492602084019763b1635f9f60e01b895260248501613d1e565b03601f1981018352826104cc565b51923090f1613ea4613be4565b90613ec1578051918215613eb85750602001fd5b60019250309150565b505f903090565b61ee139150145f613dd7565b6001600160401b039283169283613ef55750613ef09250613f8e565b905090565b919260018103613f0b5750505061057090611b39565b60028103613f29575061057092613f2191613f8e565b911690611b47565b600514159150613f3e90505761057090611b2b565b60405162461bcd60e51b815260206004820152602260248201527f50726f746f62756652756e74696d653a20756e6b6e6f776e2077697265207479604482015261706560f01b6064820152608490fd5b91908201602001905f805b60806007600186515f1a94607f8616851b1796019201921615613fc557603f8211610336579290613f99565b9293909203601f19019150565b918181101580614073575b156140225781613fec91611b10565b90613ff682613ca3565b9282614003575b50505090565b015f5b82811015613ffd57602081830181015185830182015201614006565b60405162461bcd60e51b8152602060048201526024808201527f50726f746f62756652756e74696d653a20736c696365206f7574206f6620626f604482015263756e647360e01b6064820152608490fd5b508251811115613fdd565b9061408761331a565b915f8151905b81811061409957505050565b6140a39083613f8e565b90816001600160401b0391828116600a81145f146140f9575050506140cb6140f19285613f8e565b9116906140ea6140e56140de8484611b47565b8388613fd2565b614510565b8752611b47565b905b9061408d565b60120361413757505061410f6141319285613f8e565b9116906141276141226140de8484611b47565b6143f3565b6020880152611b47565b906140f3565b6141319350600716915084613ed4565b919061415b6001600160401b039184613f8e565b9116810192838211611b0b57614172918491613fd2565b9190565b9061417f613265565b915f8151905b81811061419157505050565b61419b9083613f8e565b90816001600160401b0391828116600a81145f146141e3575050506141c36141db9285613f8e565b9116906140ea6141d66140de8484611b47565b614673565b905b90614185565b6012036142125750506141f961420c9285613f8e565b9116906141276141d66140de8484611b47565b906141dd565b61420c9350600716915084613ed4565b63ffffffff8082165f52600c60205260405f209081541661428357614259600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b61429f9063ffffffff91829182600d541690612d53565b169081151590816142c8575b506142b35750565b5f52600c6020525f6001604082208281550155565b9050815f52600c60205260405f20541615155f6142ab565b90813b15614364575f80516020614a3f83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2805115614349576143469161470c565b50565b50503461435257565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b6040519061439282610468565b60606040835f81525f60208201520152565b906143ae826104ed565b6143bb60405191826104cc565b82815280926143cc601f19916104ed565b01905f5b8281106143dc57505050565b6020906143e7614385565b828285010152016143d0565b906143fc613301565b915f8151905f805b8381106144c25750614415906143a4565b91602086019283525f915b81811061442e575050505050565b6144389085613f8e565b90816001600160401b0391828116600881145f1461447b57505050509061447561446763ffffffff9387613f8e565b9316889063ffffffff169052565b90614420565b6012036144b25750506144916144ac9287613f8e565b91169061354e6135316144a76135258585611b47565b614729565b90614475565b6144ac9350600716915086613ed4565b6144cf9085949394613f8e565b60126001600160401b0383949316146144fa575b60076144f29293169086613ed4565b929192614404565b60076145086144f2936123f8565b9250506144e3565b906145196132c4565b915f8151905b81811061452b57505050565b6145359083613f8e565b90816001600160401b0391828116600a81145f146145785750505061455d6145709285613f8e565b9116906140ea6134336140de8484611b47565b905b9061451f565b919250906012810361459d575050506145919083614147565b91906020860152614572565b601a81036145be575050506145b29083614147565b91906040860152614572565b602881036145f257505050906145ed6145dc63ffffffff9385613f8e565b9316606087019063ffffffff169052565b614572565b60328103614613575050506146079083614147565b91906080860152614572565b6038810361463f575050509061462c6145ed9284613f8e565b6001600160401b0390911660a087015291565b60420361465e5750506146529083614147565b919060c0860152614572565b61466d92506007169084613ed4565b90614572565b9061467c61324d565b915f8151905b81811061468e57505050565b6146989083613f8e565b80916001600160401b038116600881145f146146d2575050509060ff6146c16146c89385613f8e565b93906147f5565b1685525b90614682565b6010036146f7575050906146eb63ffffffff9284613f8e565b921660208601526146cc565b61470692506007169084613ed4565b906146cc565b5f8061057093602081519101845af4614723613be4565b91614846565b90614732614385565b915f8151905b81811061474457505050565b61474e9083613f8e565b80916001600160401b038116600881145f1461478d575050509061478761477861477f9385613f8e565b93906148a9565b61ffff168652565b90614738565b601081036147c157505050906147bc6147ab63ffffffff9385613f8e565b9316602087019063ffffffff169052565b614787565b601a036147e05750506147d49083614147565b91906040860152614787565b6147ef92506007169084613ed4565b90614787565b6001600160401b031680156129855760018114614840576002811461483a57600381146148345760249060405190632cd3748760e21b82526004820152fd5b50600390565b50600290565b50600190565b9061486d575080511561485b57602081519101fd5b60405163d6bda27560e01b8152600490fd5b815115806148a0575b61487e575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15614876565b6001600160401b0381168015611c60576107d1808214614a185750610bb9808214614a185750610bba808214614a185750610bbc808214614a185750610bbe808214614a18575061aa00808214614a18575061ee00808214614a18575061ee02808214614a18575061ee04808214614a18575061ee06808214614a18575061ee10808214614a18575061ee11808214614a18575061ee13808214614a18575061ee0f808214614a18575061ee15808214614a18575061ee16808214614a18575061ee17808214614a18575061ee18808214614a18575061ee19808214614a18575061ee1b808214614a18575061ee1c808214614a18575061ee1e808214614a18575061ee1f808214614a18575061ee20808214614a18575061ee21808214614a18575061ee22808214614a18575061ee23808214614a18575061ee24808214614a18575061ee25809114613ef057604051632cd3748760e21b81526001600160401b0383166004820152602490fd5b9150509056fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a264697066735822122062e7b41158989b69b850a966fb57e71e463e33b0d516806bd4e56f9e996f4f9964736f6c63430008190033", + "deployedBytecode": "0x60806040526004361015610011575f80fd5b5f3560e01c80624e356a1461032357806308a9a6de1461031e5780630fa92c5314610319578063151b8f4e146103145780631bdf0a721461030f5780631d7a88271461030a57806320857e651461030557806322fe388b14610300578063239e1d67146102fb5780632945698b146102f65780632f4d9fa9146102f15780633367cca5146102ec57806336051135146102e75780633915ba63146102e25780634ab47f43146102dd5780634cc04c94146102d85780634f1ef286146102d35780635126a52d146102ce57806352d1902d146102c9578063533ae1cc146102c45780635701a93f146102bf5780635c0c8f44146102ba5780635d4e25ce146102b55780635ff8195e146102b05780637a9e5e4b146102ab5780637b778122146102a657806380e643b6146102a15780638b75e9291461029c5780638bdebf21146102975780638d9f616b146102925780638fb360371461028d57806393ec06be1461028857806399b1a19c14610283578063ad3cb1cc1461027e578063af243e8914610279578063b1635f9f14610274578063b41565b81461026f578063bf7e214f1461026a578063c0d0373e14610265578063c268863f14610260578063c45fdcb71461025b578063cd2ad3fb14610256578063d0841f2214610251578063d1ad8d011461024c578063d3f0730614610247578063d5b5272a14610242578063e3dff2af1461023d578063e55ba576146102385763fdfd07df14610233575f80fd5b611789565b61174a565b61172d565b61167f565b61164b565b6115f1565b6115c9565b611564565b611501565b611489565b611373565b61133f565b611293565b6111e5565b6111b2565b61116d565b611124565b611108565b6110b8565b611079565b611053565b611018565b610ff4565b610f97565b610f19565b610ef6565b610ed9565b610e90565b610e6d565b610e4a565b610de1565b610daf565b610ca6565b610c8b565b610c5e565b610c2c565b610be2565b610ba8565b610b8b565b610b5e565b610b3c565b610b21565b610a80565b61092a565b61088b565b610868565b610823565b610737565b610374565b63ffffffff81160361033657565b5f80fd5b359061034582610328565b565b9181601f84011215610336578235916001600160401b038311610336576020838186019501011161033657565b346103365760403660031901126103365760043561039181610328565b6024356001600160401b038111610336576103b0903690600401610347565b90917f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0091825c6103ec575f936103e9926001855d6117cf565b5d005b604051633ee5aeb560e01b8152600490fd5b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b0382111761042d57604052565b6103fe565b602081019081106001600160401b0382111761042d57604052565b604081019081106001600160401b0382111761042d57604052565b606081019081106001600160401b0382111761042d57604052565b6001600160401b03811161042d57604052565b60c081019081106001600160401b0382111761042d57604052565b60e081019081106001600160401b0382111761042d57604052565b90601f801991011681019081106001600160401b0382111761042d57604052565b6001600160401b03811161042d5760051b60200190565b6001600160401b03811161042d57601f01601f191660200190565b92919261052b82610504565b9161053960405193846104cc565b829481845281830111610336578281602093845f960137010152565b9080601f83011215610336578160206105709335910161051f565b90565b91906080838203126103365760409283519161058e83610412565b8294823561059b81610328565b84526020808401356105ac81610328565b858201526001600160401b0392848301358481116103365785019080601f83011215610336578135906105de826104ed565b956105eb865197886104cc565b828752848701918560059460051b8601019481861161033657868101935b86851061062f57505050505050505060609261062a9284928601520161033a565b910152565b843584811161033657820188601f1982860301126103365789519061065382610432565b8981013590868211610336570184603f820112156103365789810135610678816104ed565b916106858d5193846104cc565b8183528c8c8401928b1b82010190878211610336578c94929593918e8201915b8183106106bf575050508394508152815201940193610609565b91809496509690929496358a8111610336578f90830190603f19828c030112610336578f90918f928251906106f38261044d565b838101359060ff82168203610336578e9183526060810135918211610336578594610721928f920101610555565b83820152815201930190918d95939694926106a5565b34610336576020366003190112610336576001600160401b0360043581811161033657610768903690600401610573565b906107733633612265565b63ffffffff805f5416805f52600660205261079660405f2063ffffffff90541690565b916001821490811591610817575b506107ed576107eb836107be866107b9611a0c565b612477565b42166bffffffffffffffff000000006007549160201b16906bffffffffffffffff00000000191617600755565b005b6040516325ea24eb60e11b815263ffffffff918216600482015291166024820152604490fd5b0390fd5b9050821615155f6107a4565b3461033657602036600319011261033657602060043561084281610328565b63ffffffff8091165f526006825260405f205416604051908152f35b5f91031261033657565b34610336575f36600319011261033657602063ffffffff60075416604051908152f35b34610336575f366003190112610336576001600160401b0363ffffffff610915815f5416805f5260066020528260405f205416906108c881612950565b6007549260405196879685169460201c169286936001600160401b039060809497969260a087019863ffffffff8096818094168a5216602089015216604087015216606085015216910152565b0390f35b6001600160a01b0381160361033657565b346103365760403660031901126103365760043561094781610919565b6001600160401b039060243582811161033657610968903690600401610573565b5f80516020614a7f833981519152549260ff8460401c1615931680159081610a6c575b6001149081610a62575b159081610a59575b50610a47575f80516020614a7f833981519152805467ffffffffffffffff191660011790556109d09183610a2357611a75565b6109d657005b5f80516020614a7f833981519152805460ff60401b19169055604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b5f80516020614a7f833981519152805460ff60401b1916600160401b179055611a75565b60405163f92ee8a960e01b8152600490fd5b9050155f61099d565b303b159150610995565b84915061098b565b61ffff81160361033657565b3461033657604036600319011261033657600435610a9d81610a74565b61ffff60243591610aad83610919565b610ab73633612265565b16805f526003602052807f46d568db0216a0215fe7f56360f9fa1a002c4bf0773add9cd62cd06eff160189604060018060a01b039485825f2054169582519116958682526020820152a25f90815260036020526040902080546001600160a01b0319169091179055005b34610336575f36600319011261033657602060405160078152f35b34610336575f36600319011261033657602063ffffffff5f5416604051908152f35b34610336576020366003190112610336576004355f526012602052602060ff60405f205416604051908152f35b34610336575f366003190112610336576020601454604051908152f35b34610336576020366003190112610336576020610bd8600435610bca81610919565b63ffffffff5f541690611be8565b6040519015158152f35b34610336576020366003190112610336576001600160401b0363ffffffff610915815f5416805f52600e60205260405f206004355f526020528260405f205416906108c881612950565b34610336576020366003190112610336576004355f52600a602052602060018060a01b0360405f205416604051908152f35b34610336576020366003190112610336576004355f526011602052602060ff60405f205416604051908152f35b34610336575f36600319011261033657602060405160018152f35b604036600319011261033657600435610cbe81610919565b6024356001600160401b03811161033657610cdd903690600401610555565b90610ce6613b8b565b610cf03633612265565b610cf8613b8b565b6040516352d1902d60e01b8152916020836004816001600160a01b0386165afa5f9381610d7e575b50610d4657604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b905f80516020614a3f8339815191528303610d65576107eb92506142e0565b604051632a87526960e21b815260048101849052602490fd5b610da191945060203d602011610da8575b610d9981836104cc565b810190611e79565b925f610d20565b503d610d8f565b34610336576020366003190112610336576004355f52600b602052602060018060a01b0360405f205416604051908152f35b34610336575f366003190112610336577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610e385760206040515f80516020614a3f8339815191528152f35b60405163703e46dd60e11b8152600490fd5b34610336575f36600319011261033657602063ffffffff60135416604051908152f35b34610336575f36600319011261033657602063ffffffff600d5416604051908152f35b3461033657602036600319011261033657600435610ead81610919565b610eb73633612265565b600280546001600160a01b0319166001600160a01b0392909216919091179055005b34610336575f366003190112610336576020600154604051908152f35b34610336575f36600319011261033657602063ffffffff60095416604051908152f35b3461033657602036600319011261033657600435610f3681610919565b5f80516020614a1f833981519152546001600160a01b03919082163303610f8057803b15610f67576107eb9061298a565b6040516361798f2f60e11b815291166004820152602490fd5b60405162d1953b60e31b8152336004820152602490fd5b34610336576040366003190112610336576020610feb600435610fb981610328565b63ffffffff60243591610fcb83610919565b165f526005835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b34610336575f36600319011261033657602060095460ff60405191831c1615158152f35b3461033657602036600319011261033657604061103f60043561103a81610328565b611b54565b63ffffffff83519216825215156020820152f35b34610336575f3660031901126103365760206001600160401b03600f5416604051908152f35b346103365760203660031901126103365761ffff60043561109981610a74565b165f526003602052602060018060a01b0360405f205416604051908152f35b34610336575f366003190112610336575f80516020614a1f8339815191525460a01c60ff1615611100576020638fb3603760e01b5b6040516001600160e01b03199091168152f35b60205f6110ed565b34610336575f3660031901126103365760206040516180008152f35b34610336575f36600319011261033657602060ff60075460601c166040519015158152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b34610336575f3660031901126103365761091560405161118c8161044d565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611149565b34610336576020366003190112610336576004355f52601060205260206001600160401b0360405f205416604051908152f35b346103365760603660031901126103365760043561120281610a74565b6024356001600160401b03811161033657611221903690600401610347565b6044929192359161123183610328565b30330361127b5761ffff1661ee0f810361125a57506107eb9261125591369161051f565b612bc0565b61ee139192501461126757005b6107eb9161127691369161051f565b6129e2565b604051630c86f24360e41b8152336004820152602490fd5b34610336576020366003190112610336576109156004356112b381610328565b60405f8180516112c281610468565b828152826020820152015263ffffffff8092165f52600c6020526001815f206001600160401b038351946112f586610468565b8254908116865260201c1660208501520154818301525191829182919091604080606083019463ffffffff81511684526001600160401b0360208201511660208501520151910152565b34610336575f366003190112610336575f80516020614a1f833981519152546040516001600160a01b039091168152602090f35b346103365760203660031901126103365760043561139081610328565b61139a3633612265565b63ffffffff90816113c66113bc8363ffffffff165f52600c60205260405f2090565b5463ffffffff1690565b1615611469576113ea6113e56113e05f5463ffffffff1690565b612d3b565b612d72565b8282169281168311611443575061141261141d9163ffffffff165f52600c60205260405f2090565b60015f918281550155565b7f7763580cbd840265225211dbf07de735c95fbca2c310158df03a6ffcae688b4c5f80a2005b604051631cc9a6c760e21b815263ffffffff928316600482015291166024820152604490fd5b604051630529520560e31b815263ffffffff919091166004820152602490fd5b34610336575f366003190112610336576002546040516001600160a01b039091168152602090f35b634e487b7160e01b5f52603260045260245ffd5b6008548110156114e75760085f525f80516020614a5f83398151915201905f90565b6114b1565b80548210156114e7575f5260205f2001905f90565b3461033657604036600319011261033657600435602435906008548110156103365760085f525f80516020614a5f8339815191520180548210156103365760209161154b916114ec565b905460405160039290921b1c6001600160a01b03168152f35b346103365760203660031901126103365763ffffffff60043561158681610328565b165f526015602052608060405f205460ff604051918181161515835261ffff8160081c166020840152818160181c161515604084015260201c1615156060820152f35b34610336575f3660031901126103365760206007546001600160401b0360405191831c168152f35b3461033657602036600319011261033657606060043561161081610328565b63ffffffff8091165f52600c60205260405f206001600160401b036001825492015491604051938116845260201c1660208301526040820152f35b34610336576040366003190112610336576020610bd860043561166d81610919565b6024359061167a82610328565b611be8565b346103365760203660031901126103365760043561169c81610328565b6116a63633612265565b6040516116b281610432565b63ffffffff908183168082521561171b57600d80549151831663ffffffff1983161790556040805163ffffffff93909216831682529290911660208201527f0eddf948a07b1e3a53a3d441bc72367af384f1c2d2730bf6b679331c4e8f75e69181908101610a1e565b604051639ca50c6160e01b8152600490fd5b34610336575f366003190112610336576020600454604051908152f35b346103365760203660031901126103365763ffffffff60043561176c81610328565b6117763633612265565b1663ffffffff1960075416176007555f80f35b346103365760403660031901126103365760206004356117a881610328565b63ffffffff8091165f52600e825260405f206024355f52825260405f205416604051908152f35b909291926117ed6117e45f5463ffffffff1690565b63ffffffff1690565b63ffffffff908184169081106119f0576118158463ffffffff165f52601560205260405f2090565b5460ff811690816119e1575b5061195d575f5463ffffffff169082821603611936575060095460ff8160201c166118c5575b505061185a6118568333611be8565b1590565b6118ad57831580156118a2575b6118815761187b906103459394369161051f565b90611e9d565b604051630c6f042160e31b8152600481018590526180006024820152604490fd5b506180008411611867565b604051632af0051560e21b8152336004820152602490fd5b6118ce84611b54565b905061184757610813846118e760135463ffffffff1690565b906118f760085463ffffffff1690565b604051631bf926d360e11b815263ffffffff92831660048201529282166024840152939094168416604482015292909116606483015281906084820190565b6040516307d3e83d60e51b815263ffffffff91821660048201529084166024820152604490fd5b50509261198d61197b8363ffffffff165f52600560205260405f2090565b335f9081526020919091526040902090565b54936004548095036118ad576119a491369161051f565b9081516020830120938085036119c05750610345929350611c9a565b6040516340d6b4cd60e01b8152600481019190915260248101859052604490fd5b60ff915060201c16155f611821565b505050509050565b634e487b7160e01b5f52601160045260245ffd5b6008545f60085580611a1b5750565b60085f525f80516020614a5f833981519152908101905b818110611a3d575050565b80545f825580611a51575b50600101611a32565b815f5260205f20908101905b818110611a6a5750611a48565b5f8155600101611a5d565b90611a9960c892611a84613b5d565b611a8c613b5d565b611a94613b5d565b61298a565b611aa1613b5d565b5f805463ffffffff19908116600117909155600780546bffffffffffffffff0000000019164260201b6bffffffffffffffff000000001617905590611ae590612477565b81604051611af281610432565b52600d541617600d55565b5f19810191908211611b0b57565b6119f8565b91908203918211611b0b57565b9060018201809211611b0b57565b9060048201809211611b0b57565b9060088201809211611b0b57565b91908201809211611b0b57565b60095460ff8160201c1615611bcc57600854908115611bdf57611b7f6117e460135463ffffffff1690565b9063ffffffff80941682811115611bd457611ba6611ba1611bad948793611b10565b611afd565b9116611b47565b90811015611bcc57611bbe816114c5565b505415611bcc571690600190565b50505f905f90565b50505050505f905f90565b5050505f905f90565b90611bf290611b54565b15611c60576008548110156114e75760085f525f80516020614a5f83398151915201908154915f5b838110611c2957505050505f90565b611c3381836114ec565b905460039190911b1c6001600160a01b0390811690841614611c5757600101611c1a565b50505050600190565b50505f90565b6040513d5f823e3d90fd5b90600161ffff80931601918211611b0b57565b91909161ffff80809416911601918211611b0b57565b90611cb38263ffffffff165f52601560205260405f2090565b90611cc3825460ff9060181c1690565b611e5b57600254611ce4906001600160a01b03165b6001600160a01b031690565b90813b1561033657604051905f82636744c79f60e01b94858252818381611d1360048201906001602083019252565b03925af1908115611e4357611d39928692611e48575b50845460081c61ffff16906130c4565b60025490929190611d52906001600160a01b0316611cd8565b91823b15610336575f906040519384918252818381611d7860048201905f602083019252565b03925af1908115611e4357611db792611d9f92611e2a575b50845460081c61ffff16611c84565b835462ffff00191660089190911b62ffff0016178355565b15611de157805463ff00000019166301000000178155611dd961185683612d9a565b611de1575050565b547f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc95009063ffffffff9060081c61ffff1660405161ffff909116815292169180602081015b0390a2565b80611e37611e3d92610483565b8061085e565b5f611d90565b611c66565b80611e37611e5592610483565b5f611d29565b50611dd961185683612d9a565b906020610570928181520190611149565b90816020910312610336575190565b63ffffffff809116908114611b0b5760010190565b9190611ea88161338b565b60209182808301510151611ebd815160ff1690565b90600260ff8316148015906121db575b6121aa5750506060820194611ee6865163ffffffff1690565b9063ffffffff8082169083160361218457505060a082015183815180155f146120c357505050600154806120a357505b611f1f826135ca565b9281519082012091611f4f61197b611f3b885163ffffffff1690565b63ffffffff165f52600560205260405f2090565b548015611fa2578303611f67576103459495506137bd565b610813611f78875163ffffffff1690565b60405163082085ab60e21b815263ffffffff90911660048201523360248201529081906044820190565b5061206f6117e4610345969785611fd933611fc4611f3b855163ffffffff1690565b9060018060a01b03165f5260205260405f2090565b5561202c612002611fee835163ffffffff1690565b63ffffffff165f52600660205260405f2090565b612018612013825463ffffffff1690565b611e88565b63ffffffff1663ffffffff19825416179055565b61206561200287612058612044855163ffffffff1690565b63ffffffff165f52600e60205260405f2090565b905f5260205260405f2090565b5163ffffffff1690565b6040518481523391907fcafc00e7c35f462868d78e8228850e885bd0870978967c446c6bb67122a3fb2890602090a36137bd565b604051631d734b9160e01b815260048101919091525f6024820152604490fd5b03612148576040516347f5343160e01b8152908482806120e68460048301611e68565b038173__$6c94be45b3cad4e4a0bccaa807f5821f2b$__5af4918215611e43575f92612165575b5081156121485750600154808203612126575050611f16565b604051631d734b9160e01b815260048101919091526024810191909152604490fd5b604051630ca3ddf160e01b81529081906108139060048301611e68565b61217d919250853d8711610da857610d9981836104cc565b905f61210d565b604051635070175360e01b815263ffffffff918216600482015291166024820152604490fd5b8401516040516385a89b3360e01b815246600482015260ff92909216602483015263ffffffff166044820152606490fd5b508085015163ffffffff1663ffffffff16461415611ecd565b600411610336575f90600490565b6001600160e01b0319903581811693926004811061221f57505050565b60040360031b82901b16169150565b6001600160a01b0390911681526040602082018190528101829052606091805f848401375f828201840152601f01601f1916010190565b5f80516020614a1f833981519152805461229d906001600160a01b031661229461228e866121f4565b90612202565b90843091613adc565b90156122aa575b50505050565b63ffffffff161561235b575f80516020614a1f833981519152805460ff60a01b1916600160a01b1790556122eb90611cd8908190546001600160a01b031690565b91823b1561033657612316925f9283604051809681958294634a63ebf760e11b84526004840161222e565b03925af18015611e4357612348575b505f80516020614a1f833981519152805460ff60a01b191690555f8080806122a4565b80611e3761235592610483565b5f612325565b60405162d1953b60e31b81526001600160a01b0383166004820152602490fd5b80518210156114e75760209160051b010190565b90612399826104ed565b6123a660405191826104cc565b82815280926123b7601f19916104ed565b0190602036910137565b90602082519201516bffffffffffffffffffffffff199081811693601481106123e957505050565b60140360031b82901b16169150565b5f198114611b0b5760010190565b600854600160401b81101561042d5760018101806008558110156114e75760085f525f80516020614a5f83398151915201905f90565b8054600160401b81101561042d57612459916001820181556114ec565b60018060a09493941b038084549260031b9316831b921b1916179055565b9060409182810192835151908115612920576060830163ffffffff93846124a2835163ffffffff1690565b16156128db57836124ba6117e4835163ffffffff1690565b1015612896575f935f5b81811061283a57506124d58561238f565b5f805b8382106126265750505f905f5b8381106125bb57505050507ff485fcf1271cbcd2319ebf860c827016cef79959673d605215dcd74ed0629ea4959661255e6125486125b69594612065612532612595965163ffffffff1690565b63ffffffff1663ffffffff196009541617600955565b63ffffffff1663ffffffff196007541617600755565b61257864010000000064ff00000000196009541617600955565b61258a63ffffffff1960135416601355565b515163ffffffff1690565b905163ffffffff918216815293909216909116602083015281906040820190565b0390a1565b6125c6818c5161237b565b5151516125d1612406565b50506125dc826114c5565b505f5b8281106125f1575050506001016124e5565b60019061262061261a61260d6126068a6123f8565b998961237b565b516001600160a01b031690565b8461243c565b016125df565b97909891966126398a8c95969c5161237b565b515195899b5f5b885181101561282357600261266c61266661266661265e858e61237b565b515160ff1690565b60ff1690565b036127de576020601481612680848d61237b565b5101515103612799576126a26126a89161269a848d61237b565b5101516123c1565b60601c90565b6001600160a01b0381168015612754578f8d8f5b82106126ef575050508c6126cf906123f8565b9c6126da908d61237b565b6001600160a01b039091169052600101612640565b612700611cd861260d84869461237b565b1461270f576001018d8f6126bc565b8c5163098d772360e21b815260206004820152601a60248201527f6475706c6963617465206f70657261746f7220616464726573730000000000006044820152606490fd5b8b5163098d772360e21b815260206004820152601860248201527f6f70657261746f722061646472657373206973207a65726f00000000000000006044820152606490fd5b8a5163098d772360e21b815260206004820181905260248201527f6f70657261746f722061646472657373206973206e6f742032302062797465736044820152606490fd5b895163098d772360e21b815260206004820152601e60248201527f6f70657261746f72206973206e6f7420616e2045564d206164647265737300006044820152606490fd5b5095949b50955096919860019098919801906124d8565b94612846868a5161237b565b5151519081156128635760019161285c91611b47565b95016124c4565b855163098d772360e21b815260206004820152600b60248201526a0656d7074792067726f75760ac1b6044820152606490fd5b825163098d772360e21b815260206004820152601d60248201527f61637469766547726f7570496e646578206f7574206f662072616e67650000006044820152606490fd5b825163098d772360e21b815260206004820152601860248201527f65706f63684475726174696f6e536563206973207a65726f00000000000000006044820152606490fd5b5163098d772360e21b81526020600482015260096024820152686e6f2067726f75707360b81b6044820152606490fd5b61295990611b54565b15612985576008548110156114e75760085f525f80516020614a5f833981519152015463ffffffff1690565b505f90565b5f80516020614a1f83398151915280546001600160a01b0319166001600160a01b0390921691821790556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad90602090a1565b6001600160401b0390600182600f54160190828211611b0b5773__$a62ffa76e12530392778037a67b422bd63$__90813b15610336575f91612a4291604051809681948293637401af5d60e01b845260e0600485015260e4840190611149565b90600a6024840152600b60448401526010606484015260116084840152601260a4840152871660c483015203915af4918215611e435761034592612a9e575b506001600160401b03166001600160401b0319600f541617600f55565b80611e37612aab92610483565b5f612a81565b5190811515820361033657565b908160a0910312610336576040519060a082018281106001600160401b0382111761042d5760405280519060068210156103365760809183526020810151612b0581610328565b60208401526040810151612b1881610328565b6040840152612b2960608201612ab1565b60608401520151612b3981610328565b608082015290565b91612b5960809295949560a0855260a0850190611149565b9460086020850152600a604085015263ffffffff809216606085015216910152565b60061115612b8557565b634e487b7160e01b5f52602160045260245ffd5b63ffffffff918216815291166020820152606081019291906006821015612b855760400152565b9060a0612bd260075463ffffffff1690565b60135463ffffffff1693612bfa6040519586938493632b6e75a560e11b855260048501612b41565b038173__$a62ffa76e12530392778037a67b422bd63$__5af4918215611e43575f92612d0a575b508151612c2d81612b7b565b612c3681612b7b565b612cbc5750612ca6608082612c58612532602061034596015163ffffffff1690565b612c6c612548604083015163ffffffff1690565b612c9b612c7c6060830151151590565b64ff0000000060095491151560201b169064ff00000000191617600955565b015163ffffffff1690565b63ffffffff1663ffffffff196013541617601355565b907f06bc5809fcff2cb3c29e300b687d3444b42c7836482312d880b48c4bcfbec922916125b6612cf3608084015163ffffffff1690565b9251612cfe81612b7b565b60405193849384612b99565b612d2d91925060a03d60a011612d34575b612d2581836104cc565b810190612abe565b905f612c21565b503d612d1b565b63ffffffff9060018183161115611c60575f19011690565b63ffffffff91908282168382161115612d6b57031690565b5050505f90565b6105709063ffffffff600d541690612d53565b90600163ffffffff80931601918211611b0b57565b612da381613c13565b156129855760207fe784b22f2061de501b77364cab02ee109528afe4ddc8d423d076f6b49021b58e9163ffffffff612e0181831692835f526015855260405f2064010000000064ff0000000019825416179055600454600155612d85565b1663ffffffff195f5416175f55604051908152a1600190565b6020908181840312610336578051906001600160401b038211610336570182601f8201121561033657805191612e4f836104ed565b936040612e5f60405196876104cc565b848652828601918360c080970286010194818611610336578401925b858410612e8c575050505050505090565b8684830312610336578487918451612ea381610496565b8651612eae81610a74565b815282870151838201528587015186820152606080880151908201526080808801519082015260a08088015190820152815201930192612e7b565b60408051637d3b06bb60e11b81525f94929360049190868280612f0e89878301611e68565b038173__$9d71d8de471125782e5046de17c7aa75b6$__5af4918215611e43575f926130a0575b505f965b825188101561309457620f42405a106130505784612f615a612f5b5a60031c90565b90611b10565b612fe184828b8d6001600160401b03612fd8612fbb8c612f97612f90612f87878461237b565b515161ffff1690565b61ffff1690565b9a612fb1866020612fa8828661237b565b5101519361237b565b5101519086613ccb565b938c60a0612fcf866080612fa8828661237b565b51015191613ccb565b91169187613dbe565b9290612ffd57505050612ff5600191611c71565b970196612f39565b959799509593509650905061ffff8116613045575051632845f55560e21b81526001600160a01b0390941692840192835261ffff166020830152604082015281906060010390fd5b945050505050905f90565b9396505050925061ffff811661308d5750506108135a9251630e76ff3560e41b8152918201928352620f4240602084015290918291604090910190565b92505f9150565b96506001955050505050565b6130bd9192503d805f833e6130b581836104cc565b810190612e1a565b905f612f35565b92905f926040805190637d3b06bb60e11b82526004925f83806130ea8b60048301611e68565b038173__$9d71d8de471125782e5046de17c7aa75b6$__5af4928315611e43575f93613231575b5061ffff809516965b835188101561322457620f42405a106131e95761313b5a612f5b5a60031c90565b61314b612f90612f878b8861237b565b9061318784828d8d6001600160401b03612fd86131738d8d612fb1866020612fa8828661237b565b938d60a0612fcf866080612fa8828661237b565b92906131a35750505061319b600191611c71565b97019661311a565b96989a5096925097508092935016613045575051632845f55560e21b81526001600160a01b0390941692840192835261ffff166020830152604082015281906060010390fd5b9493965050955050811661308d5750506108135a9251630e76ff3560e41b8152918201928352620f4240602084015290918291604090910190565b9750600196505050505050565b6132469193503d805f833e6130b581836104cc565b915f613111565b6040519061325a8261044d565b5f6020838281520152565b604051906132728261044d565b8161327b61324d565b8152602061062a61324d565b60405190613294826104b1565b606060c0838281526132a4613265565b60208201525f60408201525f838201525f60808201528260a08201520152565b604051906132d1826104b1565b606060c0836132de613265565b81528260208201528260408201525f838201528260808201525f60a08201520152565b6040519061330e8261044d565b60606020835f81520152565b604051906133278261044d565b816133306132c4565b8152602061062a613301565b90613346826104ed565b61335360405191826104cc565b8281528092613364601f19916104ed565b01905f5b82811061337457505050565b60209061337f61331a565b82828501015201613368565b90613394613287565b915f8151905f805b83811061356457506133ad9061333c565b9160c086019283525f915b8181106133c6575050505050565b6133d09085613f8e565b90816001600160401b0391828116600a81145f1461340157505050506133f69085614147565b919087525b906133b8565b60128103613449575050506134196134429287613f8e565b91169061343861343361342c8484611b47565b838a613fd2565b614176565b60208a0152611b47565b905b6133fb565b602881036134765750505050906134636134449286613f8e565b6001600160401b03909116604089015291565b603081036134a657505050509061344461349563ffffffff9387613f8e565b9316606089019063ffffffff169052565b603881036134d65750505050906134446134c563ffffffff9387613f8e565b9316608089019063ffffffff169052565b60a281036134f857505050506134ec9085614147565b919060a08801526133fb565b6101420361355457505061350f6134429287613f8e565b91169061354e61353161352c6135258585611b47565b848b613fd2565b61407e565b95875161353d826123f8565b97613548838361237b565b5261237b565b50611b47565b6134429350600716915086613ed4565b6135719085949394613f8e565b6101426001600160401b03839493161461359d575b60076135959293169086613ed4565b92919261339c565b60076135ab613595936123f8565b925050613586565b604090610570939281528160208201520190611149565b90601454918215159060c080910191825151915f9073__$6c94be45b3cad4e4a0bccaa807f5821f2b$__925b84831061360557505050505050565b61361683879993949596975161237b565b5151602092838201519060408093015193858351036137a2578451868115159182613797575b505061377c5783516347f5343160e01b9384825287828c818061366360049c8d8301611e68565b03915af4948515611e435788925f96613755575b509061368f92918751938492839283528a8301611e68565b03818d5af4968715611e43575f97613736575b50508061372c575b61370357506136ba90851c611b1d565b83851c918183036136d9575050505095600180930191909493926135f6565b5163eb9ba7ad60e01b81529283018681526020810191909152604081019190915281906060010390fd5b9151630b296d8760e31b8152928301868152602081019290925260408201528190036060019150fd5b50808214156136aa565b61374d929750803d10610da857610d9981836104cc565b945f806136a2565b83929196506137739061368f943d8511610da857610d9981836104cc565b95909192613677565b8351632d9a492360e21b815280610813878b600484016135b3565b14159050865f61363c565b8351635812b67560e01b815280610813858b600484016135b3565b60609093919301916137db6137d6845163ffffffff1690565b612950565b63ffffffff808216908115613ad357613814637fffffff61380a6113bc876120586120448c5163ffffffff1690565b9460011c16612d85565b90600754926138336001600160401b0394858482169160201c16611b47565b421015918086169182109383613ac6575b505050811591613abe575b5015613ab6576138e9936138816138869261386986600455565b6007805460ff60601b1916600160601b1790556107be565b601455565b7fa77ed82a4a8c2f7b412630e92b1c8f59e6645b05820a8b0ed43ae5fac990491c6138d96138bb6117e4875163ffffffff1690565b6040805186815263ffffffff90951660208601529093918291820190565b0390a2825163ffffffff16614222565b6138ff6138fa825163ffffffff1690565b614288565b613924613910825163ffffffff1690565b63ffffffff165f52601560205260405f2090565b805460ff1916600117815591600254613945906001600160a01b0316611cd8565b90813b1561033657604051905f82636744c79f60e01b9485825281838161397460048201906001602083019252565b03925af1918215611e435761399792613aa3575b50835163ffffffff1690612ee9565b60025491949092916139b1906001600160a01b0316611cd8565b91823b15610336575f9060405193849182528183816139d760048201905f602083019252565b03925af1908115611e4357613a16928492613a90575b50805463ffffff00191662ffff00600888901b161791151560181b63ff00000016919091179055565b158015613a71575b613a26575050565b611e25613a5b6117e47f1aaaa8b219dd84dddbfc5836221ae6ca620f0ec0711a5fb907846becfacc9500935163ffffffff1690565b60405161ffff9094168452929081906020820190565b50613a8b611856613a86835163ffffffff1690565b612d9a565b613a1e565b80611e37613a9d92610483565b5f6139ed565b80611e37613ab092610483565b5f613988565b505050505050565b90505f61384f565b16111590505f8080613844565b50505050505050565b93915f945f9460405193602085019363b700961360e01b855260018060a01b03809216602487015216604485015263ffffffff60e01b1660648401526064835260a08301918383106001600160401b0384111761042d576040935f9385528380528360205251915afa613b4b57565b9150505f51906020518060201c150290565b60ff5f80516020614a7f8339815191525460401c1615613b7957565b604051631afcd79f60e31b8152600490fd5b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114918215613bc9575b5050610e3857565b5f80516020614a3f8339815191525416141590505f80613bc1565b3d15613c0e573d90613bf582610504565b91613c0360405193846104cc565b82523d5f602084013e565b606090565b623d09005a1061298557600254613c32906001600160a01b0316611cd8565b803b156103365760405163a3ad9cc360e01b815263ffffffff9290921660048301525f908290602490829084905af19081613c90575b50613c8b57613c75613be4565b8051918215613c845750602001fd5b9150505f90565b600190565b80611e37613c9d92610483565b5f613c68565b90613cad82610504565b613cba60405191826104cc565b82815280926123b7601f1991610504565b91908181118015613d14575b613cfc57808203918211611b0b57602090613cf183613ca3565b930101602083015e90565b6024906040519063d98776dd60e01b82526004820152fd5b5082518211613cd7565b91613d4563ffffffff9161ffff604094979697168552606060208601526060850190611149565b9416910152565b91908260409103126103365761057060208351613d6881610919565b9301612ab1565b9160a093613da5613db39261ffff6001600160401b03959a99989a6003885216602087015260c0604087015260c0860190611149565b908482036060860152611149565b951660808201520152565b9194959390929561ffff831661ee0f8114908115613ec8575b50613e5b5750613e009060409495968551978895869563173d424160e21b875260048701613d6f565b038173__$a62ffa76e12530392778037a67b422bd63$__5af48015611e43575f925f91613e2c57509190565b9050613e5191925060403d604011613e54575b613e4981836104cc565b810190613d4c565b91565b503d613e3f565b5f955085949650613e97859294613e89604051938492602084019763b1635f9f60e01b895260248501613d1e565b03601f1981018352826104cc565b51923090f1613ea4613be4565b90613ec1578051918215613eb85750602001fd5b60019250309150565b505f903090565b61ee139150145f613dd7565b6001600160401b039283169283613ef55750613ef09250613f8e565b905090565b919260018103613f0b5750505061057090611b39565b60028103613f29575061057092613f2191613f8e565b911690611b47565b600514159150613f3e90505761057090611b2b565b60405162461bcd60e51b815260206004820152602260248201527f50726f746f62756652756e74696d653a20756e6b6e6f776e2077697265207479604482015261706560f01b6064820152608490fd5b91908201602001905f805b60806007600186515f1a94607f8616851b1796019201921615613fc557603f8211610336579290613f99565b9293909203601f19019150565b918181101580614073575b156140225781613fec91611b10565b90613ff682613ca3565b9282614003575b50505090565b015f5b82811015613ffd57602081830181015185830182015201614006565b60405162461bcd60e51b8152602060048201526024808201527f50726f746f62756652756e74696d653a20736c696365206f7574206f6620626f604482015263756e647360e01b6064820152608490fd5b508251811115613fdd565b9061408761331a565b915f8151905b81811061409957505050565b6140a39083613f8e565b90816001600160401b0391828116600a81145f146140f9575050506140cb6140f19285613f8e565b9116906140ea6140e56140de8484611b47565b8388613fd2565b614510565b8752611b47565b905b9061408d565b60120361413757505061410f6141319285613f8e565b9116906141276141226140de8484611b47565b6143f3565b6020880152611b47565b906140f3565b6141319350600716915084613ed4565b919061415b6001600160401b039184613f8e565b9116810192838211611b0b57614172918491613fd2565b9190565b9061417f613265565b915f8151905b81811061419157505050565b61419b9083613f8e565b90816001600160401b0391828116600a81145f146141e3575050506141c36141db9285613f8e565b9116906140ea6141d66140de8484611b47565b614673565b905b90614185565b6012036142125750506141f961420c9285613f8e565b9116906141276141d66140de8484611b47565b906141dd565b61420c9350600716915084613ed4565b63ffffffff8082165f52600c60205260405f209081541661428357614259600192829063ffffffff1663ffffffff19825416179055565b80546bffffffffffffffff0000000019164260201b6bffffffffffffffff00000000161781550155565b505050565b61429f9063ffffffff91829182600d541690612d53565b169081151590816142c8575b506142b35750565b5f52600c6020525f6001604082208281550155565b9050815f52600c60205260405f20541615155f6142ab565b90813b15614364575f80516020614a3f83398151915280546001600160a01b0319166001600160a01b0384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2805115614349576143469161470c565b50565b50503461435257565b60405163b398979f60e01b8152600490fd5b604051634c9c8ce360e01b81526001600160a01b0383166004820152602490fd5b6040519061439282610468565b60606040835f81525f60208201520152565b906143ae826104ed565b6143bb60405191826104cc565b82815280926143cc601f19916104ed565b01905f5b8281106143dc57505050565b6020906143e7614385565b828285010152016143d0565b906143fc613301565b915f8151905f805b8381106144c25750614415906143a4565b91602086019283525f915b81811061442e575050505050565b6144389085613f8e565b90816001600160401b0391828116600881145f1461447b57505050509061447561446763ffffffff9387613f8e565b9316889063ffffffff169052565b90614420565b6012036144b25750506144916144ac9287613f8e565b91169061354e6135316144a76135258585611b47565b614729565b90614475565b6144ac9350600716915086613ed4565b6144cf9085949394613f8e565b60126001600160401b0383949316146144fa575b60076144f29293169086613ed4565b929192614404565b60076145086144f2936123f8565b9250506144e3565b906145196132c4565b915f8151905b81811061452b57505050565b6145359083613f8e565b90816001600160401b0391828116600a81145f146145785750505061455d6145709285613f8e565b9116906140ea6134336140de8484611b47565b905b9061451f565b919250906012810361459d575050506145919083614147565b91906020860152614572565b601a81036145be575050506145b29083614147565b91906040860152614572565b602881036145f257505050906145ed6145dc63ffffffff9385613f8e565b9316606087019063ffffffff169052565b614572565b60328103614613575050506146079083614147565b91906080860152614572565b6038810361463f575050509061462c6145ed9284613f8e565b6001600160401b0390911660a087015291565b60420361465e5750506146529083614147565b919060c0860152614572565b61466d92506007169084613ed4565b90614572565b9061467c61324d565b915f8151905b81811061468e57505050565b6146989083613f8e565b80916001600160401b038116600881145f146146d2575050509060ff6146c16146c89385613f8e565b93906147f5565b1685525b90614682565b6010036146f7575050906146eb63ffffffff9284613f8e565b921660208601526146cc565b61470692506007169084613ed4565b906146cc565b5f8061057093602081519101845af4614723613be4565b91614846565b90614732614385565b915f8151905b81811061474457505050565b61474e9083613f8e565b80916001600160401b038116600881145f1461478d575050509061478761477861477f9385613f8e565b93906148a9565b61ffff168652565b90614738565b601081036147c157505050906147bc6147ab63ffffffff9385613f8e565b9316602087019063ffffffff169052565b614787565b601a036147e05750506147d49083614147565b91906040860152614787565b6147ef92506007169084613ed4565b90614787565b6001600160401b031680156129855760018114614840576002811461483a57600381146148345760249060405190632cd3748760e21b82526004820152fd5b50600390565b50600290565b50600190565b9061486d575080511561485b57602081519101fd5b60405163d6bda27560e01b8152600490fd5b815115806148a0575b61487e575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15614876565b6001600160401b0381168015611c60576107d1808214614a185750610bb9808214614a185750610bba808214614a185750610bbc808214614a185750610bbe808214614a18575061aa00808214614a18575061ee00808214614a18575061ee02808214614a18575061ee04808214614a18575061ee06808214614a18575061ee10808214614a18575061ee11808214614a18575061ee13808214614a18575061ee0f808214614a18575061ee15808214614a18575061ee16808214614a18575061ee17808214614a18575061ee18808214614a18575061ee19808214614a18575061ee1b808214614a18575061ee1c808214614a18575061ee1e808214614a18575061ee1f808214614a18575061ee20808214614a18575061ee21808214614a18575061ee22808214614a18575061ee23808214614a18575061ee24808214614a18575061ee25809114613ef057604051632cd3748760e21b81526001600160401b0383166004820152602490fd5b9150509056fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a264697066735822122062e7b41158989b69b850a966fb57e71e463e33b0d516806bd4e56f9e996f4f9964736f6c63430008190033", "linkReferences": { - "contracts/outpost/OPPChunkedLib.sol": { - "OPPChunkedLib": [ + "contracts/outpost/OPPCommon.sol": { + "OPPCommonV2": [ { "length": 20, - "start": 12006 + "start": 8645 }, { "length": 20, - "start": 12362 + "start": 14012 } ] }, - "contracts/outpost/OPPCommon.sol": { - "OPPCommon": [ + "contracts/outpost/OPPEnvelopeWalkLib.sol": { + "OPPEnvelopeWalkLib": [ { "length": 20, - "start": 10484 + "start": 12269 }, { "length": 20, - "start": 13536 + "start": 12745 } ] }, @@ -1982,41 +1992,41 @@ "OPPInboundLib": [ { "length": 20, - "start": 15627 + "start": 10968 }, { "length": 20, - "start": 17453 + "start": 11481 }, { "length": 20, - "start": 17804 + "start": 16095 } ] } }, "deployedLinkReferences": { - "contracts/outpost/OPPChunkedLib.sol": { - "OPPChunkedLib": [ + "contracts/outpost/OPPCommon.sol": { + "OPPCommonV2": [ { "length": 20, - "start": 11783 + "start": 8426 }, { "length": 20, - "start": 12139 + "start": 13793 } ] }, - "contracts/outpost/OPPCommon.sol": { - "OPPCommon": [ + "contracts/outpost/OPPEnvelopeWalkLib.sol": { + "OPPEnvelopeWalkLib": [ { "length": 20, - "start": 10261 + "start": 12050 }, { "length": 20, - "start": 13313 + "start": 12526 } ] }, @@ -2024,15 +2034,15 @@ "OPPInboundLib": [ { "length": 20, - "start": 15404 + "start": 10749 }, { "length": 20, - "start": 17230 + "start": 11262 }, { "length": 20, - "start": 17581 + "start": 15876 } ] } From 12ef66962f691ffb9198c81ba4c208bcd96cd87b Mon Sep 17 00:00:00 2001 From: Brian Johnson Date: Tue, 13 Oct 2026 16:56:47 -0500 Subject: [PATCH 6/9] outpost_ethereum_client tests: a progress read the backend cannot serve yet is retried at its pinned block Covers a "header not found" refusal on the dispatchSpill progress read, both at the tick-start head and at the block the relay's own delivery confirmed in: the read is retried at the same pinned block, never mistaken for "not tipped" (no duplicate delivery), and never falls back to a fresh head read. A backend that never catches up ends the tick in fc::timeout_exception with exactly one delivery. Co-Authored-By: Claude Opus 5.5 Change-Id: I10c59fd8bb489d2ee469000610b01e45cbfb944e --- .../test_outpost_ethereum_client_plugin.cpp | 105 +++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp index 3d18d67731..f2c700e4df 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp @@ -231,6 +231,11 @@ constexpr uint64_t below_cap_policy_gas_limit = 9'000'000; constexpr int contract_revert_rpc_code = 3; /// JSON-RPC parse error: the node never executed the call, so nothing on chain changed. constexpr int json_rpc_parse_error_code = -32700; +/// The server error geth answers a read pinned to a block it does not have yet +/// with ("header not found") — what a load-balanced backend behind the one that +/// confirmed the relay's transaction returns. +constexpr int unknown_block_rpc_code = -32000; +constexpr std::string_view unknown_block_rpc_message = "header not found"; /// A one-address custom error the client has no reading for — the same shape as /// `AccessManagedUnauthorized(address)`, a different selector. @@ -539,6 +544,12 @@ fc::network::json_rpc::json_rpc_error epoch_in_refusal(std::string_view selector return fc::network::json_rpc::json_rpc_error(contract_revert_rpc_code, "execution reverted", fc::variant(data)); } +/// A node's refusal of a read pinned to a block it has not seen yet — a +/// load-balanced backend behind the one that confirmed the relay's call. +fc::network::json_rpc::json_rpc_error unknown_block_refusal() { + return fc::network::json_rpc::json_rpc_error(unknown_block_rpc_code, std::string(unknown_block_rpc_message)); +} + /// Harness binding a real `outpost_ethereum_client` to a stubbed OPPInbound /// wrapper over a scripted chain. /// @@ -569,8 +580,14 @@ struct whole_envelope_delivery_fixture { /// Per attempt, in order, a refusal the stub throws instead of confirming; /// attempts past the end confirm. std::vector> refusals; - size_t next_epoch_reads = 0; - size_t spill_reads = 0; + /// Per `dispatchSpill` read attempt, in order, an error the stub throws + /// instead of answering — a backend that cannot serve the pinned block yet. + /// Attempts past the end answer. A refused attempt consumes no response. + std::vector> spill_read_refusals; + size_t next_epoch_reads = 0; + size_t spill_reads = 0; + /// Every `dispatchSpill` read attempt, refused ones included. + size_t spill_read_attempts = 0; size_t settlement_reads = 0; size_t consensus_reads = 0; /// The block parameter of every stubbed view read, in order. @@ -766,6 +783,10 @@ std::unique_ptr create_whole_envelope_delivery_ [raw, note_pinned](const block_number_or_tag_t& block, uint32_t& epoch_index) -> fc::variant { note_pinned(block); BOOST_CHECK_EQUAL(epoch_index, test_wire_epoch); + const size_t attempt = raw->spill_read_attempts++; + if (attempt < raw->spill_read_refusals.size() && raw->spill_read_refusals[attempt]) { + throw *raw->spill_read_refusals[attempt]; + } return fc::variant(whole_envelope_delivery_fixture::response_at(raw->spill_responses, raw->spill_reads++)); }; raw->inbound->epoch_deliveries = @@ -2162,6 +2183,86 @@ BOOST_AUTO_TEST_CASE(delivery_rereads_at_the_head_after_a_stale_refusal) try { } } FC_LOG_AND_RETHROW(); +/// A progress read refused by a backend that has not seen the pinned block yet +/// is retried AT THAT BLOCK, and the tick decides from the read that succeeds. +/// The refusal is never mistaken for state: it is not "not tipped" (which would +/// send a duplicate delivery), and the retry never falls back to a fresh head +/// read. Covered both at the tick-start head and — the case the receipt pinning +/// exists for — at the block the relay's own call just confirmed in. +BOOST_AUTO_TEST_CASE(delivery_retries_a_progress_read_the_backend_cannot_serve_yet) try { + const std::vector expected_blocks{pinned_block_hex(test_head_block), + pinned_block_hex(test_head_block + 1)}; + // Refused once at the head, before anything is sent. + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->spill_read_refusals = {unknown_block_refusal()}; + + const auto tx = fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); + + BOOST_CHECK(!tx.empty()); + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); + // Three attempts at the cursor: refused at the head, answered at the head, + // answered at the receipt's block. Only the answers consume a response. + BOOST_CHECK_EQUAL(fixture->spill_read_attempts, 3u); + BOOST_CHECK_EQUAL(fixture->spill_reads, 2u); + // The refused attempt's earlier reads are discarded and re-read whole. + BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 3u); + const auto blocks = fixture->blocks_read(); + BOOST_CHECK_EQUAL_COLLECTIONS(blocks.begin(), blocks.end(), expected_blocks.begin(), expected_blocks.end()); + BOOST_CHECK_EQUAL(fixture->rpc->block_number_reads, 1u); + } + // Refused once at the receipt's block, right after the relay's own delivery. + { + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->spill_read_refusals = {std::nullopt, unknown_block_refusal()}; + + const auto tx = fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)); + + BOOST_CHECK(!tx.empty()); + // ONE delivery: the refused read did not read as "nothing recorded". + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); + BOOST_CHECK_EQUAL(fixture->spill_read_attempts, 3u); + BOOST_CHECK_EQUAL(fixture->spill_reads, 2u); + BOOST_CHECK_EQUAL(fixture->next_epoch_reads, 3u); + // The retry stayed on the receipt's block; the head was never re-read. + const auto blocks = fixture->blocks_read(); + BOOST_CHECK_EQUAL_COLLECTIONS(blocks.begin(), blocks.end(), expected_blocks.begin(), expected_blocks.end()); + BOOST_CHECK_EQUAL(fixture->rpc->block_number_reads, 1u); + } +} FC_LOG_AND_RETHROW(); + +/// A backend that never catches up to the pinned block within the tick's +/// deadline ends the tick in a timeout — which the job retries — never in a +/// decision taken without the read, and never in a second, paid delivery. +BOOST_AUTO_TEST_CASE(delivery_times_out_rather_than_guess_when_the_pinned_block_never_serves) try { + /// More refusals than the retry's backoff can attempt inside the deadline. + constexpr size_t persistent_refusal_attempts = 64; + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = make_envelope(mid_envelope_bytes); + fixture->spill_read_refusals.push_back(std::nullopt); // the head read before the delivery answers + fixture->spill_read_refusals.insert(fixture->spill_read_refusals.end(), persistent_refusal_attempts, + unknown_block_refusal()); + + BOOST_CHECK_THROW(fixture->outpost->deliver_outbound_envelope( + test_wire_epoch, envelope, fc::seconds(test_rpc_deadline_seconds)), + fc::timeout_exception); + + check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); + // The read was retried, not abandoned after the first refusal … + BOOST_CHECK_GT(fixture->spill_read_attempts, 2u); + BOOST_CHECK_EQUAL(fixture->spill_reads, 1u); + // … and every retry stayed pinned to the receipt's block. + const std::vector expected_blocks{pinned_block_hex(test_head_block), + pinned_block_hex(test_head_block + 1)}; + const auto blocks = fixture->blocks_read(); + BOOST_CHECK_EQUAL_COLLECTIONS(blocks.begin(), blocks.end(), expected_blocks.begin(), expected_blocks.end()); + BOOST_CHECK_EQUAL(fixture->rpc->block_number_reads, 1u); +} FC_LOG_AND_RETHROW(); + /// A tick sends at most `MAX_CONTINUATIONS_PER_TICK` calls; an epoch still /// open after that is handed to the next tick through the retry exception, /// never marked handled. From 576d54d89e85c10952e27c7916cbeb65bbe1e4cc Mon Sep 17 00:00:00 2001 From: Josh Burroughs Date: Mon, 19 Oct 2026 12:44:14 -0400 Subject: [PATCH 7/9] outpost_ethereum_client: wait for each epochIn receipt until the delivery's deadline Every receipt wait used the library's default retry budget, 15 s, so the wait capped out at that whatever `batch-delivery-timeout-ms` allowed the delivery as a whole. On a chain whose blocks come slower than that, raising the option could not let a single wait outlast one block. `epoch_in` now takes the delivery's absolute deadline, and the wait gets whatever is left of it once the call is sent. `delivery_confirm_options` carries that wait alongside the gas budget. Tests: - The delivery hands its own deadline to every `epochIn` attempt, the retry after a refusal included. - The options carry a wait longer than the default. - The real wrapper stops waiting at the deadline it is given, well inside 15 s. The option's help text, README row and sizing comment now say that it bounds the whole delivery, and that one Ethereum receipt wait may use what is left. Co-Authored-By: Claude Opus 5.5 Change-Id: Ib09b53e61eb5bb8865e33a4a639eb6cae5518b3c --- plugins/batch_operator_plugin/README.md | 2 +- .../src/batch_operator_plugin.cpp | 6 +- .../sysio/outpost_ethereum_client_plugin.hpp | 44 ++++++++------ .../src/outpost_ethereum_client.cpp | 4 +- .../test/test_ethereum_transaction_policy.cpp | 57 +++++++++++++++++-- .../test_outpost_ethereum_client_plugin.cpp | 36 +++++++++--- 6 files changed, 116 insertions(+), 33 deletions(-) diff --git a/plugins/batch_operator_plugin/README.md b/plugins/batch_operator_plugin/README.md index 36fbccfcac..bd17edb4f9 100644 --- a/plugins/batch_operator_plugin/README.md +++ b/plugins/batch_operator_plugin/README.md @@ -51,7 +51,7 @@ by the underwriter every poll (see [Underwriter](#underwriter)). |--------|---------|-------------| | `--batch-operator-account` | — | WIRE account name for this operator. Configuring it enables the relay | | `--batch-epoch-poll-ms` | 15000 | How often to check epoch state (ms) | -| `--batch-delivery-timeout-ms` | 15000 | Max time to wait for chain delivery confirmation (ms) | +| `--batch-delivery-timeout-ms` | 15000 | Max time for one outbound delivery, every transaction and confirmation wait included (ms). On Ethereum a single receipt wait may use whatever of it is left, so size it past the outpost chain's block time. | | `--batch-yield-tick-interval-ms` | 60000 | Minimum spacing between this operator's `sysio.swap::tickyield` pushes per yield pool (ms) | | `--batch-underwriter-account` | none | `account` or `account@permission` (default `active`) the underwriter bonds from. Configuring it enables the underwriter | | `--batch-underwriter-max-exposure` | none | Most the underwriter may have bonded at once in one shadow token, as an asset (`100.000000000 LIQETH`). Repeat per token; a token without one is not underwritten | diff --git a/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp b/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp index 672448bbca..190abd6e1e 100644 --- a/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp +++ b/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp @@ -1945,8 +1945,8 @@ void batch_operator_plugin::set_program_options(options_description& cli, // delivery, which is a sequence of confirmed transactions. On Ethereum the // whole envelope goes in ONE `epochIn`, continued by a further call for each // time the outpost's dispatch spills (a full-cap envelope spills a handful - // of times at most); on Solana the envelope is chunked at 672 bytes. Size it - // as + // of times at most), and each receipt wait may use whatever of this budget + // is left; on Solana the envelope is chunked at 672 bytes. Size it as // expected transactions x (target block time + confirmation margin) // for the largest envelope the outpost is expected to carry; the 15000 ms // default covers a 1 s block time with wide margin. Undersizing is not @@ -1960,7 +1960,7 @@ void batch_operator_plugin::set_program_options(options_description& cli, // Help text below must stay ASCII with no " --" sequence (PerformanceHarness // splits nodeop --help output on that token). opts("batch-delivery-timeout-ms", bpo::value()->default_value(whole_milliseconds(DELIVERY_TIMEOUT)), - "Max time to wait for chain delivery confirmation (ms)"); + "Max time for one outbound delivery, every transaction and confirmation wait included (ms)"); opts("batch-yield-tick-interval-ms", bpo::value()->default_value(whole_milliseconds(YIELD_TICK_INTERVAL)), "Minimum spacing between this operator's sysio.swap::tickyield pushes per yield pool (ms)"); diff --git a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp index 1e26343870..4edb79e580 100644 --- a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp +++ b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp @@ -171,21 +171,28 @@ inline uint64_t delivery_gas_budget(const delivery_gas_request& request) { } /** - * @brief Confirmation options for one `epochIn`: the defaults, funded to - * exactly `gas_budget`. + * @brief Confirmation options for one `epochIn`: funded to exactly + * `gas_budget`, its receipt awaited for `receipt_wait`. * * The cap is the limit the transaction is sent with and the gas its * pre-flight estimate runs under: a call the budget cannot carry is refused * by the node before it is signed, and a call that can is sent with the * budget, never with a buffered estimate. * - * @param gas_budget The budget from `delivery_gas_budget`. + * The receipt wait replaces the library's default retry budget, which would + * otherwise cap every wait at its own 15 s however long the delivery was + * given: on a chain whose blocks come slower than that, raising + * `batch-delivery-timeout-ms` would not let a single wait outlast one block. + * + * @param gas_budget The budget from `delivery_gas_budget`. + * @param receipt_wait How long to wait for the receipt. * @return The options the call is sent with. */ -inline ethereum_confirm_options delivery_confirm_options(uint64_t gas_budget) { +inline ethereum_confirm_options delivery_confirm_options(uint64_t gas_budget, fc::microseconds receipt_wait) { ethereum_confirm_options options = ethereum_confirm_option_defaults; - options.gas_limit_floor = gas_budget; - options.gas_limit_cap = gas_budget; + options.gas_limit_floor = gas_budget; + options.gas_limit_cap = gas_budget; + options.retry.total_timeout = receipt_wait; return options; } @@ -247,9 +254,10 @@ struct opp_inbound_contract_client : ethereum_contract_client { /// dispatches as many attestations as its gas allows and records where it /// stopped; a continuation is the SAME call with the same arguments, which /// the contract resumes from its cursor. Funded to exactly `gas_budget` - /// (see `delivery_gas_budget`), which is why this is not a plain - /// `ethereum_contract_tx_fn`: that binds its options once at construction, - /// and the budget differs per call. Returns the confirmed receipt rather + /// (see `delivery_gas_budget`) and its receipt awaited until `deadline`, + /// which is why this is not a plain `ethereum_contract_tx_fn`: that binds + /// its options once at construction, and both differ per call. Returns the + /// confirmed receipt rather /// than the hash alone, because the relay's next move is decided from the /// block the call landed in. `envelopeData` rides as a hex-encoded string /// because the libfc ABI encoder takes `dt::bytes` that way (see @@ -257,7 +265,9 @@ struct opp_inbound_contract_client : ethereum_contract_client { /// /// The ABI arguments are bound as non-const lvalue references, so callers /// must materialize named locals for both. - std::function epoch_in; + std::function + epoch_in; /// `nextEpochIndex()` view — the epoch the outpost is currently accepting. ethereum_contract_call_fn next_epoch_index; /// `dispatchSpill(uint32 epochIndex)` view — where a tipped epoch's dispatch @@ -291,14 +301,16 @@ struct opp_inbound_contract_client : ethereum_contract_client { const address_compat_type& contract_address, const std::vector& contracts) : ethereum_contract_client(client, contract_address, contracts) - , epoch_in([this](uint32_t& epoch_index, std::string& envelope_hex, uint64_t gas_budget) -> epoch_in_receipt { - const auto& abi = get_abi(opp_inbound_abi_name::epoch_in); - const auto options = delivery_confirm_options(gas_budget); + , epoch_in([this](uint32_t& epoch_index, std::string& envelope_hex, uint64_t gas_budget, + fc::time_point deadline) -> epoch_in_receipt { + const auto& abi = get_abi(opp_inbound_abi_name::epoch_in); contract_invoke_data_items params = {epoch_index, envelope_hex}; - auto tx = this->client->create_default_tx(this->contract_address, abi, params, options.gas_limit_floor, - options.gas_limit_cap); + auto tx = this->client->create_default_tx(this->contract_address, abi, params, gas_budget, gas_budget); const auto tx_hash = this->client->execute_contract_tx_fn(tx, abi, params).as_string(); - return parse_epoch_in_receipt(tx_hash, this->client->wait_for_receipt(tx_hash, options)); + // Measured after the send, so the wait gets exactly what is left. + const auto receipt_wait = std::max(fc::microseconds(), deadline - fc::time_point::now()); + return parse_epoch_in_receipt( + tx_hash, this->client->wait_for_receipt(tx_hash, delivery_confirm_options(gas_budget, receipt_wait))); }) , next_epoch_index(create_call(get_abi(opp_inbound_abi_name::next_epoch_index))) , dispatch_spill(create_call(get_abi(opp_inbound_abi_name::dispatch_spill))) diff --git a/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp b/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp index 5c2f7c625b..dd597cabca 100644 --- a/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp +++ b/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp @@ -918,7 +918,7 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( std::string payload_hex = envelope_hex; epoch_in_receipt receipt; try { - receipt = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget); + receipt = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget, deadline_abs); } catch (const fc::network::json_rpc::json_rpc_error& refusal) { // The node refused the call before anything was signed: the // pre-flight estimate runs under the budget. Which refusal decides @@ -937,7 +937,7 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( "ceiling ({} gas)", to_string(), epoch_index, gas_budget, magic_enum::enum_name(*revert), gas_ceiling); gas_budget = gas_ceiling; - receipt = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget); + receipt = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget, deadline_abs); break; case detail::epoch_in_revert::non_sequential_epoch: diff --git a/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp b/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp index e81a3556f6..4d165af494 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp @@ -44,6 +44,13 @@ constexpr std::string_view rpc_url_placeholder_a = "RPC_URL_A"; constexpr std::string_view rpc_url_placeholder_b = "RPC_URL_B"; constexpr std::string_view test_chain_id_quantity = "0x7a69"; constexpr std::string_view ethereum_mainnet_chain_id_quantity = "0x1"; +/// Receipt deadline for a wrapper call whose receipt is already there. +constexpr int64_t receipt_deadline_seconds = 30; +/// Receipt deadline for a wrapper call whose receipt never appears: far below +/// the library's default retry budget, so a wait that ignored it would show. +constexpr int64_t short_receipt_deadline_ms = 300; +/// A receipt wait longer than the library's default retry budget. +constexpr int64_t long_receipt_wait_seconds = 90; using tcp = boost::asio::ip::tcp; /** One-shot JSON-RPC endpoint used to preserve coverage of the historical three-field CLI client form. */ @@ -608,6 +615,48 @@ BOOST_AUTO_TEST_CASE(wait_for_receipt_returns_the_confirmed_receipt) { BOOST_CHECK_EQUAL(sign_count.load(), 0u); } +/// One `epochIn`'s options fund it to exactly its budget and wait for its +/// receipt as long as asked, past the library's default retry budget too. +BOOST_AUTO_TEST_CASE(delivery_confirm_options_carry_the_budget_and_the_receipt_wait) { + constexpr uint64_t budget = 1'234'567; + const auto options = sysio::delivery_confirm_options(budget, fc::seconds(long_receipt_wait_seconds)); + BOOST_CHECK_EQUAL(options.gas_limit_floor, budget); + BOOST_CHECK_EQUAL(options.gas_limit_cap, budget); + BOOST_CHECK(options.retry.total_timeout == fc::seconds(long_receipt_wait_seconds)); + BOOST_CHECK(options.retry.total_timeout > ethereum_confirm_option_defaults.retry.total_timeout); +} + +/// The wrapper stops waiting for a receipt at the deadline it is given, not +/// at a budget of its own: a delivery's deadline is the only one. +BOOST_AUTO_TEST_CASE(epoch_in_waits_for_its_receipt_until_the_given_deadline) { + std::atomic sign_count = 0; + const auto provider = make_recording_signer(sign_count); + auto client = std::make_shared(provider, bounded_policy()); + sysio::opp_inbound_contract_client inbound{ + client, + std::string(contract_address), + {whole_envelope_epoch_in_function("epochIn"), no_argument_function("nextEpochIndex"), + uint32_argument_function("dispatchSpill"), epoch_deliveries_function("epochDeliveries"), + no_argument_function("pendingEpochHash"), bytes32_argument_function("pendingConsensusForDigest"), + uint16_argument_function("attestationHandlers")}, + }; + uint32_t epoch_index = 1; + std::string envelope = "01"; + + // No receipt ever appears. + const auto start = fc::time_point::now(); + BOOST_CHECK_THROW( + inbound.epoch_in(epoch_index, envelope, 999, start + fc::milliseconds(short_receipt_deadline_ms)), + fc::timeout_exception); + const auto waited = fc::time_point::now() - start; + + BOOST_CHECK(waited >= fc::milliseconds(short_receipt_deadline_ms)); + BOOST_CHECK(waited < ethereum_confirm_option_defaults.retry.total_timeout); + BOOST_CHECK_GE(client->receipt_reads, 1u); + // The call was sent; only the wait gave up. + BOOST_CHECK_EQUAL(client->broadcast_count, 1u); +} + BOOST_AUTO_TEST_CASE(all_typed_write_wrappers_share_the_policy_enforced_path) { std::atomic sign_count = 0; const auto provider = make_recording_signer(sign_count); @@ -630,13 +679,13 @@ BOOST_AUTO_TEST_CASE(all_typed_write_wrappers_share_the_policy_enforced_path) { // the limit) and comes back as the confirmed receipt. uint32_t epoch_index = 1; std::string envelope = "01"; - expect_policy_rejection([&] { inbound.epoch_in(epoch_index, envelope, 1000); }); + const auto deadline = fc::time_point::now() + fc::seconds(receipt_deadline_seconds); + expect_policy_rejection([&] { inbound.epoch_in(epoch_index, envelope, 1000, deadline); }); client->receipt_response = fc::mutable_variant_object("status", "0x1")("blockNumber", "0x10")("logs", fc::variants{}); - const auto receipt = inbound.epoch_in(epoch_index, envelope, 999); + const auto receipt = inbound.epoch_in(epoch_index, envelope, 999, deadline); BOOST_CHECK_EQUAL(receipt.block_number, fc::uint256{16}); BOOST_CHECK(receipt.logs.empty()); - // The budget reached the pre-flight as the gas the call must fit in — the - // wrapper hands `delivery_confirm_options(budget)` to `create_default_tx`. + // The budget reached the pre-flight as the gas the call must fit in. BOOST_CHECK_EQUAL(client->estimate_gas_params.get_array().front().get_object()["gas"].as_string(), "0x3e7"); BOOST_CHECK_EQUAL(sign_count.load(), 1u); BOOST_CHECK_EQUAL(client->broadcast_count, 1u); diff --git a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp index f2c700e4df..38795150bb 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp @@ -577,6 +577,8 @@ struct whole_envelope_delivery_fixture { std::vector delivery_calls; /// The budget of every `epochIn` attempt, refused ones included. std::vector attempted_budgets; + /// The receipt deadline of every `epochIn` attempt, refused ones included. + std::vector attempted_deadlines; /// Per attempt, in order, a refusal the stub throws instead of confirming; /// attempts past the end confirm. std::vector> refusals; @@ -746,9 +748,11 @@ std::unique_ptr create_whole_envelope_delivery_ auto* raw = fixture.get(); raw->inbound->epoch_in = [raw, inbound_address](uint32_t& epoch_index, std::string& envelope_hex, - uint64_t gas_budget) -> sysio::epoch_in_receipt { + uint64_t gas_budget, + fc::time_point deadline) -> sysio::epoch_in_receipt { const size_t attempt = raw->attempted_budgets.size(); raw->attempted_budgets.push_back(gas_budget); + raw->attempted_deadlines.push_back(deadline); if (attempt < raw->refusals.size() && raw->refusals[attempt]) throw *raw->refusals[attempt]; raw->delivery_calls.push_back(observed_delivery_call{epoch_index, envelope_hex, gas_budget}); @@ -1486,8 +1490,7 @@ BOOST_AUTO_TEST_CASE(read_emitted_envelope_digest_reads_the_outpost_record) try // --------------------------------------------------------------------------- /// The most an `epochIn` may be funded with follows the client's policy -/// ceiling and never exceeds EIP-7825's cap; the options a call is sent with -/// pin floor and cap to the one budget, so the pre-flight runs under it. +/// ceiling and never exceeds EIP-7825's cap. BOOST_AUTO_TEST_CASE(delivery_gas_ceiling_is_the_policy_ceiling_bounded_by_the_cap) try { auto policy = relay_test_policy(); BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy), sysio::EIP_7825_TX_GAS_CAP); @@ -1497,10 +1500,6 @@ BOOST_AUTO_TEST_CASE(delivery_gas_ceiling_is_the_policy_ceiling_bounded_by_the_c policy.max_gas_limit = fc::uint256{below_cap_policy_gas_limit}; BOOST_CHECK_EQUAL(sysio::delivery_gas_ceiling(policy), below_cap_policy_gas_limit); - - const auto options = sysio::delivery_confirm_options(below_cap_policy_gas_limit); - BOOST_CHECK_EQUAL(options.gas_limit_floor, below_cap_policy_gas_limit); - BOOST_CHECK_EQUAL(options.gas_limit_cap, below_cap_policy_gas_limit); } FC_LOG_AND_RETHROW(); /// At a fee, the ceiling is also what the policy's total-cost term pays for: @@ -2104,6 +2103,29 @@ BOOST_AUTO_TEST_CASE(delivery_escalates_after_a_call_that_advanced_nothing_and_s BOOST_CHECK_EQUAL(fixture->delivery_calls.size(), 3u); } FC_LOG_AND_RETHROW(); +/// Every `epochIn` attempt, the retry after a refusal included, waits for its +/// receipt until the delivery's own deadline rather than for a fixed budget of +/// its own, so a deadline sized past one slow block lets a single wait outlast it. +BOOST_AUTO_TEST_CASE(delivery_awaits_each_receipt_until_the_delivery_deadline) try { + /// Longer than the 15 s the library's default retry budget gives one wait. + constexpr int64_t long_delivery_deadline_seconds = 60; + auto fixture = create_whole_envelope_delivery_fixture(); + auto envelope = serialize_envelope_with_attestations(test_wire_epoch, 2, 64); + fixture->refusals = {epoch_in_refusal(dispatch_underfunded_selector, 2)}; + + const auto before = fc::time_point::now(); + const auto tx = fixture->outpost->deliver_outbound_envelope(test_wire_epoch, envelope, + fc::seconds(long_delivery_deadline_seconds)); + const auto after = fc::time_point::now(); + + BOOST_CHECK(!tx.empty()); + BOOST_REQUIRE_EQUAL(fixture->attempted_deadlines.size(), 2u); + for (const auto& deadline : fixture->attempted_deadlines) { + BOOST_CHECK(deadline >= before + fc::seconds(long_delivery_deadline_seconds)); + BOOST_CHECK(deadline <= after + fc::seconds(long_delivery_deadline_seconds)); + } +} FC_LOG_AND_RETHROW(); + /// A node refusal for gas below the ceiling is retried once at the ceiling; /// one at the ceiling is the stalled-epoch report, with nothing sent. BOOST_AUTO_TEST_CASE(delivery_retries_a_gas_refusal_at_the_ceiling_and_reports_one_there) try { From 5288d0b77d2e3a97664d3c332062fd4da672a967 Mon Sep 17 00:00:00 2001 From: Josh Burroughs Date: Mon, 19 Oct 2026 12:44:19 -0400 Subject: [PATCH 8/9] outpost_client docs: the emit runs in the call that finishes dispatch, and what delivery returns Since dispatch can spill, the outpost's outbound emit runs in whichever `epochIn` finishes dispatch with gas left for it, which after a spill is a continuation, not the consensus-reaching call. The `deliver_outbound_envelope` and `read_inbound_envelope` docs and three READMEs still said the consensus-reaching delivery emits. Solana's terminal `epoch_in` does still emit, and the batch operator README says so per chain. The outpost_client README row now gives the return value as the last transaction's id, or empty when nothing needed sending and the epoch counts as handled, plus the incomplete-delivery exception. Co-Authored-By: Claude Opus 5.5 Change-Id: Id915a440336d61862eff47c4bc095aac82c2db9e --- plugins/batch_operator_plugin/README.md | 3 ++- plugins/outpost_client_plugin/README.md | 6 +++--- .../include/sysio/outpost_client/outpost_client.hpp | 7 ++++--- plugins/outpost_ethereum_client_plugin/README.md | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/plugins/batch_operator_plugin/README.md b/plugins/batch_operator_plugin/README.md index bd17edb4f9..dd1d95884f 100644 --- a/plugins/batch_operator_plugin/README.md +++ b/plugins/batch_operator_plugin/README.md @@ -22,7 +22,8 @@ Run by every member of the elected group, for every active outpost chain: 3. The outpost reaches consensus once enough group members have delivered identical bytes **Inbound (Outposts → WIRE):** -1. The consensus-reaching delivery emits the outpost's outbound envelope +1. The outpost emits its outbound envelope once it has dispatched the inbound one (ETH: in the `epochIn` + that finishes dispatch, a continuation if it spilled; SOL: in the consensus-reaching `epoch_in`) 2. Read the latest outbound envelope from Outpost storage 3. Deliver its raw protobuf bytes to Depot (`sysio.msgch::deliver`) 4. Depot evaluates consensus across the group's deliveries diff --git a/plugins/outpost_client_plugin/README.md b/plugins/outpost_client_plugin/README.md index c3b385af5c..b59f217121 100644 --- a/plugins/outpost_client_plugin/README.md +++ b/plugins/outpost_client_plugin/README.md @@ -31,7 +31,7 @@ an EVM address, a Solana public key, a PDA, or a signature-provider format. | `chain_id()` | Numeric chain id on the target chain; Solana has none and reports `0` | | `authenticated_caller_address()` | Raw chain-native address that authenticates this client's writes — 20 bytes on Ethereum, 32 on Solana | | `to_string()` | `{chain_code}:{ChainKind_Name}:{chain_id}`. The default prints `chain_code()` as the raw packed integer, so the row registered as `ETH` against a local chain renders `23373212024832:CHAIN_KIND_EVM:31337`. Virtual, with a default derived from the three getters | -| `deliver_outbound_envelope(epoch_index, envelope_bytes, deadline)` | OPP outbound — submit one envelope to the remote chain; returns the chain-native transaction id | +| `deliver_outbound_envelope(epoch_index, envelope_bytes, deadline)` | OPP outbound — submit one envelope to the remote chain. Returns the chain-native id of the last transaction sent, or an empty string when nothing needed sending and the epoch counts as handled; throws `outpost_delivery_incomplete_exception` when the epoch is left open for this relay to finish on a later tick | | `read_inbound_envelope(epoch_index, deadline)` | OPP inbound — return the envelope the remote chain produced for this epoch, or an empty vector when the latest slot does not match | | `crank_outpost(epoch_index, deadline)` | Drive the outpost's permissionless per-epoch instructions right after this operator's delivery lands; best-effort, retried next epoch. The default cranks nothing; Solana overrides it with `report_liq_yield`, Ethereum with `SyndicationPool.realizeYield` | @@ -39,8 +39,8 @@ Two rules bind every implementation. Each RPC-bound call must enforce the `deadl remote chain cannot starve a cron worker; the protected helper `throw_if_past_deadline(deadline_abs, op)` throws `fc::timeout_exception` labelled with `to_string()` and is meant to be called before each blocking RPC. And `read_inbound_envelope` must filter by `epoch_index` itself — both outposts expose a single -latest-outbound storage slot, so a poll can still observe the preceding epoch until the consensus-reaching -delivery overwrites it, and forwarding that stale envelope to `sysio.msgch::deliver` trips an +latest-outbound storage slot, so a poll can still observe the preceding epoch until the delivery that emits +this one overwrites it, and forwarding that stale envelope to `sysio.msgch::deliver` trips an `envelope epoch_index mismatch` assertion. The header also declares the cross-chain envelope cap: diff --git a/plugins/outpost_client_plugin/include/sysio/outpost_client/outpost_client.hpp b/plugins/outpost_client_plugin/include/sysio/outpost_client/outpost_client.hpp index 9f50e12041..0ad0fa3530 100644 --- a/plugins/outpost_client_plugin/include/sysio/outpost_client/outpost_client.hpp +++ b/plugins/outpost_client_plugin/include/sysio/outpost_client/outpost_client.hpp @@ -95,8 +95,9 @@ class outpost_client { * caller beyond this duration. Solana may chunk one envelope across * multiple `epoch_in` transactions; Ethereum sends the whole envelope in * one `epochIn` and re-sends it while the outpost reports the epoch tipped - * on this relay's digest and unfinished. The consensus-reaching transaction - * performs the outpost's outbound emit internally. + * on this relay's digest and unfinished. The outpost's outbound emit runs in + * whichever of those calls finishes dispatch with gas left for it, so after + * a spill it is a continuation, not the consensus-reaching call. * * @param epoch_index The current WIRE epoch this envelope belongs to. * @param envelope_bytes Raw protobuf `opp::Envelope` bytes. @@ -121,7 +122,7 @@ class outpost_client { * * Filters by `epoch_index` internally. Ethereum and Solana each expose a * single latest-outbound storage slot, so a poll may still observe the - * preceding epoch until the consensus-reaching delivery overwrites it. + * preceding epoch until the delivery that emits this one overwrites it. * Delivering that stale envelope to `sysio.msgch::deliver` trips an * `envelope epoch_index mismatch` assertion. * diff --git a/plugins/outpost_ethereum_client_plugin/README.md b/plugins/outpost_ethereum_client_plugin/README.md index 49c4b8b4cf..1af25acc58 100644 --- a/plugins/outpost_ethereum_client_plugin/README.md +++ b/plugins/outpost_ethereum_client_plugin/README.md @@ -179,7 +179,7 @@ mid-sequence failure or deadline abandons the tick, and the next one resumes fro `latest`. WIRE consensus on inbound is committed forward against this read, so an operator that read at `latest` could reach WIRE-side consensus on a slot that a reorg then removes. The decoded result's epoch is compared with the requested one and a mismatch returns an empty vector at debug level, since observing the -preceding epoch is normal until the consensus-reaching delivery overwrites the slot. +preceding epoch is normal until the `epochIn` that emits this one overwrites the slot. ### Outpost cranks From d7a87bc39abbaf91d21f082bd62323e11449ddf9 Mon Sep 17 00:00:00 2001 From: Josh Burroughs Date: Mon, 19 Oct 2026 12:50:55 -0400 Subject: [PATCH 9/9] outpost_ethereum_client: trim the branch's comments to the why and the non-obvious A pass over the comments and README prose this branch added. Kept: design reasons, invariants, and RPC, gas and reorg behaviour a reader would not guess. Cut or shortened: comments that restate the code or a log line, repeats (the floor/cap rationale was spelled out in four places; it now lives on `ethereum_confirm_options`), long prose, and history. Two corrections came out of it: - The Ethereum client README still said a reorg under the progress reads costs at most an idempotent no-op. It costs a paid re-delivery before the tip or a refused continuation after it, as the header already said. - The `epoch_in` member doc had a broken line wrap. Comment and documentation text only; no code changes. Co-Authored-By: Claude Opus 5.5 Change-Id: Ib8012eabb16c5505b52e312de25ae166df7d56b6 --- .../fc/network/ethereum/ethereum_client.hpp | 39 +++++++-------- .../src/network/ethereum/ethereum_client.cpp | 29 ++++------- .../src/batch_operator_plugin.cpp | 25 +++++----- .../src/outpost_opp_job.cpp | 21 ++++---- .../test/test_outpost_opp_job.cpp | 6 +-- .../outpost_ethereum_client_plugin/README.md | 29 ++++++----- .../sysio/outpost_ethereum_client_plugin.hpp | 36 +++++--------- .../outpost_ethereum_client.hpp | 24 +++++----- .../src/outpost_ethereum_client.cpp | 48 +++++++------------ .../test/test_ethereum_transaction_policy.cpp | 31 ++++++------ .../test_outpost_ethereum_client_plugin.cpp | 35 +++++--------- 11 files changed, 132 insertions(+), 191 deletions(-) diff --git a/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp b/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp index 999ff99b47..6a7c089133 100644 --- a/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp +++ b/libraries/libfc/include/fc/network/ethereum/ethereum_client.hpp @@ -43,27 +43,22 @@ struct ethereum_confirm_options { /// Least gas limit the transaction is funded with, applied AFTER the /// buffered estimate; `0` leaves the estimate alone. /// - /// For a call whose execution stops on a `gasleft()` watchdog and records - /// where it stopped rather than reverting, `eth_estimateGas` converges on - /// the least gas at which the call SUCCEEDS — the smallest amount of work it - /// can do and still return cleanly — so the estimate systematically - /// under-funds it. A floor lets the caller fund such a call to what the - /// chain allows instead. The floor is still subject to the client's - /// `max_gas_limit`: a floor above the policy ceiling is a policy rejection, - /// never a silently clamped limit. Ignored when `gas_limit_cap` is set: the - /// cap is then the limit outright. + /// For a call that stops on a `gasleft()` watchdog and records where it + /// stopped rather than reverting, `eth_estimateGas` converges on the least + /// gas at which the call returns cleanly, so the estimate systematically + /// under-funds it. A floor above the client's `max_gas_limit` is a policy + /// rejection, never a silently clamped limit. Ignored when `gas_limit_cap` + /// is set. uint64_t gas_limit_floor = 0; /// The gas limit the transaction is sent with, and the gas the pre-flight /// estimate runs under; `0` leaves the limit to the buffered estimate (and - /// the floor). A capped call is sent with EXACTLY this limit: the estimate - /// is a pre-flight at that gas and nothing more, so a call that cannot - /// succeed inside it is refused by the node before anything is signed, - /// instead of being sent and reverting on chain for the whole budget. The - /// estimate's value is not an input to the limit -- neither the headroom - /// buffer nor the buffered-estimate ceiling check applies, since what is - /// simulated is exactly what is sent. Must be at least the floor; a cap - /// above the policy ceiling is a policy rejection. + /// the floor). A call that cannot succeed inside the cap is refused by the + /// node before anything is signed, instead of reverting on chain for the + /// whole budget. The estimate's value is discarded: what is simulated is + /// what is sent, so no headroom buffer or buffered-estimate ceiling check + /// applies. Must be at least the floor; a cap above the policy ceiling is a + /// policy rejection. uint64_t gas_limit_cap = 0; }; @@ -513,12 +508,10 @@ class ethereum_client : public std::enable_shared_from_this { * @brief Wait for a previously-submitted tx hash to be included and * confirmed at `opts.confirmations` depth, and return its receipt. * - * The receipt is the authoritative record of what the transaction did: - * its `blockNumber` pins every follow-up read to the block that holds the - * effect, and its `logs` carry the events the contract emitted. A caller - * that decides its next move from those reads the receipt rather than - * re-reading state at `latest`, which a lagging backend may serve from a - * block before the transaction landed. + * Its `blockNumber` lets follow-up reads pin to the block that holds the + * effect, and its `logs` carry the events the contract emitted; re-reading + * state at `latest` instead risks a lagging backend serving a block from + * before the transaction landed. * * Same two-phase wait as `wait_for_confirmation`. * diff --git a/libraries/libfc/src/network/ethereum/ethereum_client.cpp b/libraries/libfc/src/network/ethereum/ethereum_client.cpp index ac96d07d3c..05fea84d39 100644 --- a/libraries/libfc/src/network/ethereum/ethereum_client.cpp +++ b/libraries/libfc/src/network/ethereum/ethereum_client.cpp @@ -289,9 +289,8 @@ fc::variant ethereum_client::get_syncing_status() { * Constructs a complete EIP-1559 transaction by: * - Fetching current gas configuration (base fee, priority fee) * - Encoding the contract call data according to the ABI - * - Sizing the gas limit: with a cap, the cap IS the limit and the estimate - * is a pre-flight at that gas; without one, the estimate plus a 20% buffer, - * raised to `gas_limit_floor` when the caller funds the call to a floor + * - Sizing the gas limit: the cap when one is given, else the estimate plus a + * 20% buffer, raised to `gas_limit_floor` * - Setting the nonce from the pending transaction count * * @param to The recipient address (contract address for contract calls) @@ -300,9 +299,8 @@ fc::variant ethereum_client::get_syncing_status() { * @param gas_limit_floor Least gas limit to fund with, or 0 for the buffered estimate * @param gas_limit_cap The limit to send with, or 0 for the buffered estimate * @return A configured eip1559_tx ready for signing and submission - * @throws fc::network::json_rpc::json_rpc_exception if any RPC call fails -- - * including the node refusing a capped pre-flight that cannot succeed - * inside the cap + * @throws fc::network::json_rpc::json_rpc_exception if any RPC call fails, + * including a capped pre-flight that cannot succeed inside the cap * @throws ethereum_transaction_policy_exception if the estimate, the floor or * the cap breaches the client's `max_gas_limit` */ @@ -315,9 +313,7 @@ eip1559_tx ethereum_client::create_default_tx(const address_compat_type& to, con auto gc = get_gas_config_unlogged(); auto data = contract_encode_data(contract, params); - // The floor and the cap are bounded by the same ceiling as the estimate: - // a caller asking for more than the policy allows is refused, not - // clamped, so an under-funded call is never silently sent. + // Refused, not clamped: clamping would silently send an under-funded call. const auto require_within_policy = [&](uint64_t limit, std::string_view field) { const fc::uint256 wide{limit}; if (wide > _transaction_policy.max_gas_limit) { @@ -328,13 +324,9 @@ eip1559_tx ethereum_client::create_default_tx(const address_compat_type& to, con fc::uint256 gas_limit; if (gas_limit_cap != 0) { - // A capped call is sent with exactly the cap. The estimate runs under - // it as a pre-flight -- the node refuses a call that cannot succeed - // inside the cap -- and its VALUE is discarded: what is simulated is - // what is sent, so neither the headroom buffer nor the buffered - // ceiling check has anything to add. (Buffering a call that uses most - // of its cap would only manufacture a policy rejection for a - // transaction the cap already carries.) + // The estimate is only a pre-flight; its value is discarded. Buffering + // a call that uses most of its cap would manufacture a policy + // rejection for a transaction the cap already carries. require_within_policy(gas_limit_cap, transaction_policy_field::gas_limit_cap); (void) estimate_gas(to, contract, data, gc, gas_limit_cap); gas_limit = fc::uint256{gas_limit_cap}; @@ -568,9 +560,8 @@ fc::uint256 ethereum_client::estimate_gas(const address_compat_type& to, const a (std::string(ethereum_rpc_field::max_fee_per_gas), format_rpc_quantity(gc.max_fee_per_gas)) ("data", data) ("input", data); - // A capped estimate is a pre-flight at the budget the transaction will - // carry: the node searches only up to `gas` and reports a revert when the - // call cannot succeed inside it. + // With `gas` set, the node searches only up to it and reports a revert when + // the call cannot succeed inside it. if (gas_limit_cap != 0) tx(std::string(ethereum_rpc_field::gas), format_rpc_quantity(fc::uint256{gas_limit_cap})); auto resp = execute_idempotent("eth_estimateGas", fc::variants{tx}); diff --git a/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp b/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp index 190abd6e1e..a53c918f6d 100644 --- a/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp +++ b/plugins/batch_operator_plugin/src/batch_operator_plugin.cpp @@ -1942,21 +1942,18 @@ void batch_operator_plugin::set_program_options(options_description& cli, opts("batch-epoch-poll-ms", bpo::value()->default_value(whole_milliseconds(EPOCH_POLL)), "How often to check epoch state (ms)"); // SIZING RULE for batch-delivery-timeout-ms: it bounds the WHOLE outbound - // delivery, which is a sequence of confirmed transactions. On Ethereum the - // whole envelope goes in ONE `epochIn`, continued by a further call for each - // time the outpost's dispatch spills (a full-cap envelope spills a handful - // of times at most), and each receipt wait may use whatever of this budget - // is left; on Solana the envelope is chunked at 672 bytes. Size it as + // delivery, a sequence of confirmed transactions. Ethereum sends the envelope + // in ONE `epochIn` plus one continuation per dispatch spill (a handful at + // most for a full-cap envelope), and each receipt wait may use whatever of + // this budget is left; Solana chunks the envelope at 672 bytes. Size it as // expected transactions x (target block time + confirmation margin) - // for the largest envelope the outpost is expected to carry; the 15000 ms - // default covers a 1 s block time with wide margin. Undersizing is not - // fatal: the relay resumes from the outpost's on-chain cursor on the next - // cron tick, so a truncated tick converges rather than restarting. SHARED - // KNOB: this same value also bounds the depot push_action completion wait - // (see push_action above); post-WIRE-331 a timeout there is safe (the - // async_action_completion callback retains its own state), but raising this - // option lengthens BOTH waits, so raise it for delivery deliberately rather - // than as a blanket tuning step. + // for the largest expected envelope; the 15000 ms default covers a 1 s block + // time with wide margin. Undersizing is not fatal: the relay resumes from the + // outpost's on-chain cursor on the next cron tick. SHARED KNOB: this value + // also bounds the depot push_action completion wait (see push_action above). + // A timeout there is safe (the async_action_completion callback retains its + // own state), but raising this lengthens BOTH waits, so raise it deliberately + // rather than as a blanket tuning step. // Help text below must stay ASCII with no " --" sequence (PerformanceHarness // splits nodeop --help output on that token). opts("batch-delivery-timeout-ms", bpo::value()->default_value(whole_milliseconds(DELIVERY_TIMEOUT)), diff --git a/plugins/batch_operator_plugin/src/outpost_opp_job.cpp b/plugins/batch_operator_plugin/src/outpost_opp_job.cpp index 3b19f1a1a0..51113304b4 100644 --- a/plugins/batch_operator_plugin/src/outpost_opp_job.cpp +++ b/plugins/batch_operator_plugin/src/outpost_opp_job.cpp @@ -119,15 +119,14 @@ void outpost_opp_job::run_outbound() { } FC_LOG_AND_DROP("outpost_opp_job[{}]: emit_debug_envelope threw", _client->to_string()); try { - // An EMPTY tx id is a defined outcome, not a failure: the relay found - // nothing to send — the outpost had already finalized the epoch, or - // (Ethereum) it tipped on another relay's digest, or this relay's - // delivery is recorded and waits on the rest of the group. The epoch is - // correctly marked handled below; the relay's own ilog is the truthful - // record of what happened. An epoch the relay could NOT finish with — - // the outpost behind, a continuation bound reached, a call at the gas - // ceiling that advanced nothing — arrives as - // `outpost_delivery_incomplete_exception` and is retried next tick. + // An EMPTY tx id is not a failure: the relay had nothing to send. The + // outpost already finalized the epoch, or on Ethereum it tipped on + // another relay's digest or this relay's delivery is recorded and waits + // on the rest of the group. The epoch is marked handled below; the relay's own + // ilog records which case. An epoch the relay could NOT finish (outpost + // behind, continuation bound reached, a call at the gas ceiling that + // advanced nothing) arrives as `outpost_delivery_incomplete_exception` + // and is retried next tick. auto tx_id = _client->deliver_outbound_envelope(epoch, pending->raw_envelope, _outpost_deadline); ilog("outpost_opp_job[{}]: {} outbound envelope ({} bytes) tx={}", _client->to_string(), @@ -142,8 +141,8 @@ void outpost_opp_job::run_outbound() { _last_outbound_epoch = epoch; } } catch (const chain::outpost_delivery_incomplete_exception& e) { - // Expected while an epoch spans ticks: not a failure, and not marked - // handled, so the next tick resumes it from the outpost's cursor. + // Expected while an epoch spans ticks; not marked handled, so the next + // tick resumes it from the outpost's cursor. ilog("outpost_opp_job[{}]: outbound delivery for epoch {} continues next tick: {}", _client->to_string(), epoch, e.top_message()); } catch (const fc::network::json_rpc::json_rpc_error& e) { diff --git a/plugins/batch_operator_plugin/test/test_outpost_opp_job.cpp b/plugins/batch_operator_plugin/test/test_outpost_opp_job.cpp index 4f5a617d19..a9bc332134 100644 --- a/plugins/batch_operator_plugin/test/test_outpost_opp_job.cpp +++ b/plugins/batch_operator_plugin/test/test_outpost_opp_job.cpp @@ -287,10 +287,8 @@ BOOST_AUTO_TEST_CASE(run_outbound_swallows_exceptions_and_does_not_mark_epoch) { BOOST_REQUIRE_EQUAL(depot.emitted_events.size(), 2u); } -/// A delivery the relay could not finish with — the outpost behind, a -/// continuation bound reached, a stalled call at the gas ceiling — is not a -/// failure and not a handled epoch: the job neither cranks nor marks it, and -/// the next tick delivers again. +/// An incomplete delivery is neither a failure nor a handled epoch: the job +/// neither cranks nor marks it, and the next tick delivers again. BOOST_AUTO_TEST_CASE(run_outbound_retries_an_incomplete_delivery_next_tick_without_cranking) { auto client = make_client(CHAIN_KIND_EVM, 0, 31337); mock_depot_ops depot; diff --git a/plugins/outpost_ethereum_client_plugin/README.md b/plugins/outpost_ethereum_client_plugin/README.md index 1af25acc58..f0c39b0b47 100644 --- a/plugins/outpost_ethereum_client_plugin/README.md +++ b/plugins/outpost_ethereum_client_plugin/README.md @@ -63,10 +63,10 @@ A client that delivers OPP envelopes (one handed an `OPPInbound` address by `cre also fund a delivery: its `max_gas_limit`, bounded by EIP-7825's 16 777 216 per-transaction cap, has to reach `DELIVERY_MINIMUM_GAS_CEILING` (9 932 160 gas — what an envelope at the 32 768-byte platform cap costs to deliver and tip, dispatch one attestation at its allowance, and emit). The relay refuses to be -built on a smaller ceiling, since the largest envelope the platform allows could never complete on it. The -total-cost term binds at the fee a call is sent at: when `max_total_native_cost_wei / max_fee_per_gas_wei` -is below that minimum the relay starts with a warning, and deliveries at fees near the policy maximum spill -into more continuations than their budgets size for. +built on a smaller ceiling, since a full-cap envelope could never complete on it. The total-cost term binds +at the fee a call is sent at: when `max_total_native_cost_wei / max_fee_per_gas_wei` is below that minimum +the relay starts with a warning, and deliveries at fees near the policy maximum spill into more +continuations than their budgets size for. What the signer needs follows from the same arithmetic. Every `epochIn` is sent with its budget as the gas limit — a full-cap first delivery at the EIP-7825 cap, less for a continuation — and a node reserves @@ -130,11 +130,10 @@ bytes; every continuation re-supplies them. **Reads are pinned to one block.** Every decision is taken from `nextEpochIndex()`, `dispatchSpill(epoch)`, `epochDeliveries(epoch, self)` and `pendingEpochHash()` read at ONE block number — the head when the tick starts, then the block each confirmed call landed in (from its receipt) — never at `latest`, which a -backend behind the block that just confirmed could serve from before the call. A backend that does not -know the block yet is retried for a bounded time. The reads are not at `finalized` either: the cursor is -the outpost's own bookkeeping about work this relay is doing now, a reorg costs at most a re-sent call the -contract treats as an idempotent no-op, and a continuation gated on finality would wait ~64 blocks between -every stretch of dispatch. +backend lagging the block that just confirmed could serve from before the call. A backend that does not +know the block yet is retried for a bounded time. The reads are not at `finalized` either. The cursor is +the outpost's bookkeeping for work in progress, a reorg under it costs a paid re-delivery before the tip or a +refused continuation after it, and gating on finality would wait ~64 blocks between continuations. From those reads the relay decides, per `outpost_ethereum_client_detail::decide_delivery`: @@ -154,9 +153,9 @@ tick that fell short, never above the ceiling. The ceiling (`delivery_gas_ceilin `max_gas_limit` bounded by EIP-7825's cap AND by what `max_total_native_cost` pays for at the current fee, so a budget it admits is never refused by the policy at signing. The node's estimate is deliberately not the budget: `epochIn` stops on a `gasleft()` watchdog and records where it stopped, so `eth_estimateGas` -converges on the least gas at which the call succeeds — the tip plus one attestation. Instead the budget is -both the pre-flight estimate's gas and the limit the transaction is sent with, so a call that cannot run -inside it is refused before anything is signed, and one that can is sent with exactly it. +converges on the least gas at which the call succeeds — the tip plus one attestation. The budget is instead +both the pre-flight estimate's gas and the transaction's limit, so a call that cannot run inside it is +refused before anything is signed. **Every call paid for must move the cursor.** After each confirmed call the relay reads the outpost again at the receipt's block; a call that advanced nothing (no delivery recorded, no tip, no dispatch progress, no @@ -331,9 +330,9 @@ delivery path — one call, tip-and-spill continued in the same tick, resume fro and never-delivered digests, consensus retry gated on the outpost's own majority view, pinned-block reads, escalation after a call that advanced nothing and the stall report at the ceiling, revert classification, outpost behind, the per-tick bound, epoch advanced, deadline abandonment, and the empty and over-cap -rejections — and the outpost crank: idle without -the pool ABI or a registered handler, binding the registered pool and re-binding on a change, the pool's own -three refusals, unrecognised reverts and protocol errors, and deadline abandonment. +rejections — and the outpost crank: idle without the pool ABI or a registered handler, binding the +registered pool and re-binding on a change, the pool's own three refusals, unrecognised reverts and protocol +errors, and deadline abandonment. `outpost_ethereum_transaction_policy_tests` covers the expenditure-policy boundary: that a rejection happens before signing or broadcasting, that exact caps pass through once, that two clients enforce their own policies, that file and CLI configuration produce the expected policies, and that a partial client map is diff --git a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp index 4edb79e580..5006a11d28 100644 --- a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp +++ b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin.hpp @@ -152,11 +152,8 @@ struct delivery_gas_request { * whatever the call uses, so a small envelope funded to the cap is refused for * balance at exactly the fee levels where delivery matters most. * - * The budget is the fixed and per-byte cost, an allowance per attestation - * still to dispatch, and the emit; doubled per spill already met this tick; - * never above the ceiling. The contract spills what does not fit and the - * next call carries on, so an under-estimate costs a continuation, never the - * epoch. An envelope that cannot be read is funded to the ceiling. + * The contract spills what does not fit and the next call carries on, so an + * under-estimate costs a continuation, never the epoch. * * @param request What the call has to carry. * @return The budget, in gas. @@ -172,12 +169,8 @@ inline uint64_t delivery_gas_budget(const delivery_gas_request& request) { /** * @brief Confirmation options for one `epochIn`: funded to exactly - * `gas_budget`, its receipt awaited for `receipt_wait`. - * - * The cap is the limit the transaction is sent with and the gas its - * pre-flight estimate runs under: a call the budget cannot carry is refused - * by the node before it is signed, and a call that can is sent with the - * budget, never with a buffered estimate. + * `gas_budget` (floor and cap both), its receipt awaited for + * `receipt_wait`. * * The receipt wait replaces the library's default retry budget, which would * otherwise cap every wait at its own 15 s however long the delivery was @@ -250,18 +243,15 @@ inline constexpr auto attestation_handlers = "attestationHandlers"; /// policy as `opp_contract_client` for the write path. struct opp_inbound_contract_client : ethereum_contract_client { /// `epochIn(uint32 epochIndex, bytes envelopeData)` — the WHOLE envelope in - /// ONE call, addressed to its epoch. The call that reaches consensus - /// dispatches as many attestations as its gas allows and records where it - /// stopped; a continuation is the SAME call with the same arguments, which - /// the contract resumes from its cursor. Funded to exactly `gas_budget` - /// (see `delivery_gas_budget`) and its receipt awaited until `deadline`, - /// which is why this is not a plain `ethereum_contract_tx_fn`: that binds - /// its options once at construction, and both differ per call. Returns the - /// confirmed receipt rather - /// than the hash alone, because the relay's next move is decided from the - /// block the call landed in. `envelopeData` rides as a hex-encoded string - /// because the libfc ABI encoder takes `dt::bytes` that way (see - /// `ethereum_abi::encode_dynamic_data`). + /// ONE call. The call that reaches consensus dispatches as many attestations + /// as its gas allows and records where it stopped; a continuation is the + /// SAME call with the same arguments, resumed from the contract's cursor. + /// Not a plain `ethereum_contract_tx_fn`, which binds its options once at + /// construction: the gas budget (see `delivery_gas_budget`) and `deadline` + /// differ per call. Returns the confirmed receipt because the relay's next + /// move is decided from the block the call landed in. `envelopeData` rides + /// as a hex-encoded string because the libfc ABI encoder takes `dt::bytes` + /// that way (see `ethereum_abi::encode_dynamic_data`). /// /// The ABI arguments are bound as non-const lvalue references, so callers /// must materialize named locals for both. diff --git a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp index 392a782361..932c8e2f77 100644 --- a/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp +++ b/plugins/outpost_ethereum_client_plugin/include/sysio/outpost_ethereum_client_plugin/outpost_ethereum_client.hpp @@ -323,8 +323,7 @@ class outpost_ethereum_client : public outpost_client { const std::string& opp_address() const { return _opp_addr; } const std::string& opp_inbound_address() const { return _opp_inbound_addr; } /// This relay's own signer address in `0x`-hex — the identity the outpost - /// records deliveries under. Derived once at construction; the continuation - /// check compares the digest recorded under it against the settled one. + /// records deliveries under. Derived once at construction. const std::string& signer_address_hex() const { return _signer_address_hex; } /// The liq syndication pool the crank drives, `0x`-hex, or empty until the /// outpost's `DESYNDICATE_LIQ` handler has been discovered. @@ -349,17 +348,16 @@ class outpost_ethereum_client : public outpost_client { /// /// Pinned to a block number, never `latest`: the block is the head read at /// the start of the tick, or the block a call this tick confirmed in, so a - /// backend serving `latest` from behind that block cannot hand back the - /// state from before the call and have the relay pay for it twice. A - /// backend that does not know the block yet is retried for a bounded time. - /// Not `finalized`, where `read_inbound_envelope` reads at `finalized`: the - /// cursor is this outpost's own bookkeeping about work this relay is doing - /// right now. A reorg under it costs a paid re-delivery before the tip or a - /// refused continuation after it, and the dearer direction — a send not made - /// because the read was behind — is what the per-call progress invariant and - /// the next tick cover; finality lags head by roughly 64 blocks, and a - /// continuation gated on it would wait that out between every stretch of - /// dispatch. + /// backend lagging behind it cannot hand back the pre-call state and have + /// the relay pay for the call twice. A backend that does not know the block + /// yet is retried for a bounded time. + /// + /// Not `finalized` either (unlike `read_inbound_envelope`): finality lags + /// head by roughly 64 blocks, and a continuation gated on it would wait that + /// out between every stretch of dispatch. A reorg under the read costs a + /// paid re-delivery before the tip or a refused continuation after it; a + /// send missed because the read was behind is covered by the per-call + /// progress invariant and the next tick. /// /// @throws fc::exception on transport failure, deadline expiry, or an /// unparsable response. diff --git a/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp b/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp index dd597cabca..81bb4652db 100644 --- a/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp +++ b/plugins/outpost_ethereum_client_plugin/src/outpost_ethereum_client.cpp @@ -229,8 +229,8 @@ std::string normalized_hex(std::string_view value) { return hex; } -/// A 32-byte word as the decoder renders it, normalised for comparison: -/// lower-case hex with no `0x` prefix. Empty when the value is not a string. +/// A 32-byte word as the decoder renders it, normalised as by `normalized_hex`. +/// Empty when the value is not a string. std::string normalized_word(const fc::variant& value) { if (!value.is_string()) return {}; return normalized_hex(value.as_string()); @@ -502,8 +502,7 @@ std::optional classify_realize_yield_revert(std::string_v if (data.size() < EVM_SELECTOR_HEX_CHARS) return std::nullopt; const auto selector = data.substr(0, EVM_SELECTOR_HEX_CHARS); const auto argument_chars = data.size() - EVM_SELECTOR_HEX_CHARS; - // Exact shape, as for the `epochIn` refusals: an error with other arguments hashes - // differently, and a payload that does not fit the error is not that error. + // Exact shape, as for the `epochIn` refusals. const auto is = [&](const char* signature, size_t words) { return argument_chars == words * EVM_ABI_WORD_HEX_CHARS && same_hex(selector, selector_hex(signature)); }; @@ -573,13 +572,10 @@ outpost_ethereum_client::outpost_ethereum_client( _opp_inbound_client = _entry->client->get_contract(_opp_inbound_addr, _abis); - // A delivery is funded to what it has to carry, up to the policy's - // ceiling. A ceiling under what the largest envelope the platform - // allows needs is a configuration that cannot deliver, and it is - // refused here rather than discovered one stuck epoch at a time. The - // total-cost term is a warning, not a refusal: it binds at the fee a - // call is sent at, and only the policy's own maximum is known here; an - // unbounded total cost (the maximum policy) has nothing to warn about. + // A ceiling below what a full-cap envelope needs cannot deliver, so it + // is refused here rather than discovered one stuck epoch at a time. + // The total-cost term only warns: it binds at the fee a call is sent + // at, and only the policy's maximum fee is known here. const auto& policy = _entry->client->transaction_policy(); const uint64_t ceiling = delivery_gas_ceiling(policy); SYS_ASSERT(ceiling >= DELIVERY_MINIMUM_GAS_CEILING, chain::plugin_config_exception, @@ -600,10 +596,9 @@ outpost_ethereum_client::outpost_ethereum_client( } } - // OPPInbound records every delivery under the delivering signer, and a - // continuation is admitted only from a signer whose recorded digest is the - // settled one, so the continuation check needs this relay's own address. - // Derive it once here rather than per tick. + // OPPInbound keys recorded deliveries by signer, and a continuation is + // admitted only from a signer whose recorded digest is the settled one. + // Derive the address once here rather than per tick. _signer_address_hex = fc::to_hex(_entry->client->get_signer_address(), /*add_prefix=*/true); } @@ -700,17 +695,14 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( // spills across continuations of this same call. The envelope is // re-supplied on every continuation — the outpost never stores its bytes. const std::string envelope_hex = fc::to_hex(envelope_bytes.data(), static_cast(total)); - // The digest the outpost records for this delivery and settles consensus - // on: a continuation is only valid with the bytes that hash to the settled - // digest, which the relay checks before paying to find out. + // The digest the outpost records for this delivery and settles consensus on. const std::string local_digest = detail::envelope_digest_word(envelope_bytes); // Each call is funded to what it has left to carry (`delivery_gas_budget`), // never to a fixed figure: the attestations the outpost's cursor says are - // still to dispatch, doubled for every call this tick that fell short. An - // envelope the relay cannot read is funded to the ceiling. The ceiling is - // what the policy allows AT THE CURRENT FEE, so a budget it admits is a - // budget the policy's total-cost term will not refuse at signing. + // still to dispatch, doubled for every call this tick that fell short. The + // ceiling is what the policy allows AT THE CURRENT FEE, so a budget it + // admits is a budget the policy's total-cost term will not refuse at signing. const auto attestation_count = detail::count_envelope_attestations(envelope_bytes); const auto gas_config = _entry->client->get_gas_config(); const uint64_t gas_ceiling = @@ -728,9 +720,8 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( fc::uint256 pinned_block = _entry->client->get_block_number(); // A call that cannot be sent at its budget, or that was sent and moved - // nothing, is reported as a stalled epoch: once at error level for each - // place the cursor stalls, then at debug until the cursor moves. The tick - // ends in `outpost_delivery_incomplete_exception` so the job retries it. + // nothing, is a stalled epoch: logged at error once per stall position, + // then at debug until the cursor moves. The throw makes the job retry. const auto report_stall = [&](const detail::dispatch_spill& spill, const std::string& reason) { const std::pair where{epoch_index, spill.dispatched}; if (_reported_stall != where) { @@ -806,9 +797,7 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( case detail::delivery_action::wait_for_deliverer: if (settlement == detail::delivery_settlement::divergent) { - // This relay's envelope disagreed with the majority's: worth a - // warning with both digests, since it means the depot handed this - // relay different bytes than its peers. + // The depot handed this relay different bytes than its peers. wlog("outpost_ethereum_client[{}]: epoch={} tipped on digest {} but this relay delivered {}; its " "deliverers carry the continuation (dispatched={} complete={})", to_string(), epoch_index, progress.settled_digest, progress.own_digest, progress.spill.dispatched, @@ -921,8 +910,7 @@ std::string outpost_ethereum_client::deliver_outbound_envelope( receipt = _opp_inbound_client->epoch_in(epoch_arg, payload_hex, gas_budget, deadline_abs); } catch (const fc::network::json_rpc::json_rpc_error& refusal) { // The node refused the call before anything was signed: the - // pre-flight estimate runs under the budget. Which refusal decides - // what the relay does about it. + // pre-flight estimate runs under the budget. const auto revert = detail::classify_epoch_in_revert(refusal.code, revert_hex(refusal)); if (!revert) throw; switch (*revert) { diff --git a/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp b/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp index 4d165af494..a9594648f9 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_ethereum_transaction_policy.cpp @@ -528,10 +528,10 @@ BOOST_AUTO_TEST_CASE(gas_limit_floor_raises_an_under_estimate_and_is_policy_boun BOOST_CHECK_EQUAL(client->broadcast_count, 0u); } -/// A cap IS the limit: the transaction is sent with exactly it, the estimate -/// runs under it as a pre-flight only (its value neither raises nor lowers -/// the limit, and the buffered-estimate ceiling check does not apply), it -/// cannot sit below the floor, and it is policy-bounded exactly like the floor. +/// A cap IS the limit: the transaction is sent with exactly it and the estimate +/// is only a pre-flight under it (its value is not an input, and the +/// buffered-estimate ceiling check does not apply). A cap below the floor or +/// above the policy ceiling is refused. BOOST_AUTO_TEST_CASE(gas_limit_cap_is_the_limit_and_runs_the_estimate_under_it) { std::atomic sign_count = 0; const auto provider = make_recording_signer(sign_count); @@ -559,9 +559,8 @@ BOOST_AUTO_TEST_CASE(gas_limit_cap_is_the_limit_and_runs_the_estimate_under_it) BOOST_CHECK_EQUAL( client->create_default_tx(std::string(contract_address), no_argument_function("submit"), {}, 1500, 1500).gas_limit, 1500); - // An estimate whose buffer would breach the policy is a rejection without a - // cap, and irrelevant with one: 1000 buffers to 1200 > 1000, but a capped - // call that fits in 1000 is sent with 1000. + // A buffered estimate over the policy is a rejection without a cap and + // irrelevant with one: 1000 buffers to 1200 > 1000, yet a 1000 cap is sent. client->estimate_response = "0x3e8"; policy.max_gas_limit = 1000; auto tight_client = std::make_shared(provider, policy); @@ -585,9 +584,9 @@ BOOST_AUTO_TEST_CASE(gas_limit_cap_is_the_limit_and_runs_the_estimate_under_it) BOOST_CHECK_EQUAL(tight_client->broadcast_count, 0u); } -/// The confirmation wait hands back the receipt itself — block number and -/// logs — so a caller can pin its next reads to the block that holds the -/// effect; a reverted receipt is an error, and the depth wait still applies. +/// The confirmation wait returns the receipt (block number and logs) so a +/// caller can pin its next reads to the block holding the effect; a reverted +/// receipt is an error, and the depth wait still applies. BOOST_AUTO_TEST_CASE(wait_for_receipt_returns_the_confirmed_receipt) { std::atomic sign_count = 0; const auto provider = make_recording_signer(sign_count); @@ -626,8 +625,8 @@ BOOST_AUTO_TEST_CASE(delivery_confirm_options_carry_the_budget_and_the_receipt_w BOOST_CHECK(options.retry.total_timeout > ethereum_confirm_option_defaults.retry.total_timeout); } -/// The wrapper stops waiting for a receipt at the deadline it is given, not -/// at a budget of its own: a delivery's deadline is the only one. +/// The wrapper stops waiting for a receipt at the given deadline, not at a +/// budget of its own. BOOST_AUTO_TEST_CASE(epoch_in_waits_for_its_receipt_until_the_given_deadline) { std::atomic sign_count = 0; const auto provider = make_recording_signer(sign_count); @@ -672,11 +671,9 @@ BOOST_AUTO_TEST_CASE(all_typed_write_wrappers_share_the_policy_enforced_path) { no_argument_function("pendingEpochHash"), bytes32_argument_function("pendingConsensusForDigest"), uint16_argument_function("attestationHandlers")}, }; - // OPPInbound's one write wrapper — the whole-envelope delivery — must be - // rejected by the policy before signing. A budget above the policy ceiling - // (999 here) is refused outright; one at the ceiling is sent with exactly - // it (the estimate of 834 is a pre-flight under the cap, not an input to - // the limit) and comes back as the confirmed receipt. + // OPPInbound's one write wrapper: a budget above the policy ceiling (999 + // here) is refused before signing; one at the ceiling is sent with exactly + // it and returns the confirmed receipt. uint32_t epoch_index = 1; std::string envelope = "01"; const auto deadline = fc::time_point::now() + fc::seconds(receipt_deadline_seconds); diff --git a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp index 38795150bb..58a0f81613 100644 --- a/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp +++ b/plugins/outpost_ethereum_client_plugin/test/test_outpost_ethereum_client_plugin.cpp @@ -556,10 +556,9 @@ fc::network::json_rpc::json_rpc_error unknown_block_refusal() { /// `ethereum_client::get_contract` caches one typed wrapper per address, so a /// wrapper materialized here is the SAME object the client resolves in its /// constructor — replacing its `std::function` members intercepts every RPC at -/// the typed callable boundary, exactly as the `getLatestOutboundEnvelope` -/// coverage above does. The stubbed `epochIn` confirms in a fresh block each -/// time and answers with a receipt the case scripts; every view records the -/// block it was pinned to. +/// the typed callable boundary. The stubbed `epochIn` confirms in a fresh block +/// each time and answers with a receipt the case scripts; every view records +/// the block it was pinned to. struct whole_envelope_delivery_fixture { ~whole_envelope_delivery_fixture() { outpost.reset(); @@ -729,9 +728,7 @@ relay_test_stack create_relay_test_stack(ethereum_transaction_policy transaction return stack; } -/// Stand up a `whole_envelope_delivery_fixture`: a real `outpost_ethereum_client` -/// whose OPPInbound wrapper has every typed callable replaced by a recording -/// stub. The caller owns the returned fixture; the stubs capture it by +/// Stand up a `whole_envelope_delivery_fixture`. The stubs capture it by /// reference, so it must not be moved after this returns. std::unique_ptr create_whole_envelope_delivery_fixture() { auto fixture = std::make_unique(); @@ -1185,7 +1182,7 @@ BOOST_AUTO_TEST_CASE(opp_inbound_contract_client_construction) try { // Every ABI entry the typed `opp_inbound_contract_client` binds at // construction: the whole-envelope write and the five views the - // deliver-or-continue decision reads. The staged-chunk surface is gone. + // deliver-or-continue decision reads — and none of the retired staged-chunk surface. bool has_epoch_in = false, has_next_epoch = false, has_spill = false; bool has_deliveries = false, has_pending_hash = false, has_pending_consensus = false; bool has_discard = false, has_chunk_state = false; @@ -1231,8 +1228,7 @@ BOOST_AUTO_TEST_CASE(epoch_in_abi_encoding_whole_envelope) try { std::vector{fc::variant(uint64_t{test_wire_epoch}), fc::variant(test_envelope_hex)}); BOOST_CHECK(!encoded.empty()); - // keccak256("epochIn(uint32,bytes)")[0..4) — neither retired form's - // selector survives. + // Neither retired form's selector survives. BOOST_CHECK_EQUAL(encoded.substr(0, evm_function_selector_hex_chars), std::string(epoch_in_selector)); BOOST_CHECK(encoded.substr(0, evm_function_selector_hex_chars) != @@ -2206,11 +2202,9 @@ BOOST_AUTO_TEST_CASE(delivery_rereads_at_the_head_after_a_stale_refusal) try { } FC_LOG_AND_RETHROW(); /// A progress read refused by a backend that has not seen the pinned block yet -/// is retried AT THAT BLOCK, and the tick decides from the read that succeeds. -/// The refusal is never mistaken for state: it is not "not tipped" (which would -/// send a duplicate delivery), and the retry never falls back to a fresh head -/// read. Covered both at the tick-start head and — the case the receipt pinning -/// exists for — at the block the relay's own call just confirmed in. +/// is retried AT THAT BLOCK, never taken as "not tipped" (which would send a +/// duplicate delivery) and never re-read at a fresh head. Covered at the +/// tick-start head and at the block the relay's own call just confirmed in. BOOST_AUTO_TEST_CASE(delivery_retries_a_progress_read_the_backend_cannot_serve_yet) try { const std::vector expected_blocks{pinned_block_hex(test_head_block), pinned_block_hex(test_head_block + 1)}; @@ -2362,12 +2356,10 @@ BOOST_AUTO_TEST_CASE(delivery_propagates_a_transport_failure_on_a_continuation) BOOST_CHECK_EQUAL(fixture->attempted_budgets.size(), 2u); } FC_LOG_AND_RETHROW(); -/// Mid-sequence deadline expiry — the scenario the cursor exists for. -/// -/// The delivery burns more wall clock than the whole budget, so the -/// continuation's pre-flight `throw_if_past_deadline` fires: the tick abandons -/// after the delivery landed rather than rolling anything back. The next tick -/// reads the cursor and continues from it instead of re-delivering. +/// Mid-sequence deadline expiry, the scenario the cursor exists for: the +/// delivery outlasts the whole budget, so the continuation's pre-flight +/// `throw_if_past_deadline` abandons the tick with nothing rolled back, and the +/// next tick continues from the cursor instead of re-delivering. BOOST_AUTO_TEST_CASE(delivery_abandons_on_deadline_then_continues_next_tick) try { auto fixture = create_whole_envelope_delivery_fixture(); auto envelope = make_envelope(sysio::OPP_MAX_ENVELOPE_BYTES); @@ -2384,7 +2376,6 @@ BOOST_AUTO_TEST_CASE(delivery_abandons_on_deadline_then_continues_next_tick) try test_wire_epoch, envelope, fc::milliseconds(delivery_budget_ms)), fc::timeout_exception); - // Exactly the delivery landed, carrying the whole envelope. check_delivery_calls(fixture->delivery_calls, envelope, test_wire_epoch, 1); // Next tick: the outpost reports the epoch tipped on our digest with six