Merged
Conversation
- Introduce readonly JSON-RPC plugin for HF14 prediction markets state access - Document market-related API methods including markets, outcomes, bets, liquidity, and metadata - Describe position, leverage, oracle, dispute, lazy pool, and governance methods - Provide details on kline/time series for market weight history and pagination approach - Explain computed DTOs representing bets, oracles, votes, and payout structures - Include example usage and code snippets for API calls and data processing - Link to relevant protocol operations and chain property documentation docs(governance): update chain properties with HF13 and PM parameters - Add chain_properties_hf13 with distribution_epoch_length parameter - Introduce chain_properties_pm (v5) for ~30 prediction market parameters and kill-switch flags - Detail all median-voted parameters for oracle, market, batch, dispute, time penalty, lazy pool, leverage, and fairness - Clarify live kill-switch flags to disable commit-reveal, lazy pool, or leverage without hardfork docs(advanced): extend hardfork management with HF13 and prediction markets - Add entries for HF13 epoch length and HF14 prediction markets including CPMM/LMSR, oracles, disputes, commit-reveal, lazy-pool, and chain properties v5 docs(prediction-markets): add comprehensive analysis of conceptual mapping of Onix PM protocol - Provide detailed table comparing 90 theoretical prediction market concepts against VIZ Onix on-chain implementation - Categorize concepts as solved, inherent, not needed, partial/roadmap, client layer, or open risks - Discuss information theory, mechanism design, liquidity and trading aspects in depth - Highlight Onix innovations: risk-free LP, CPMM binary, LMSR multi, commit-reveal batch bets, optional leverage subsystem, lazy pool governance voting weight - Explain architectural decisions omitting orderbooks, combinatorial markets, and peer prediction
- Updated chainbase submodule commit from 39ab2c2 to d429230 - Ensures third-party library is aligned with latest upstream changes
…cycle, coverage floors and thin-client APIs Consensus (HF14 follow-up ops, appended so operation indices stay stable): - pm_dispute_oracle_respond (op 22): the market oracle posts a public rebuttal onto an open dispute; stored on the dispute object (public-hearing model), allowed only while open and within oracle_response_deadline, re-post overwrites. - pm_unban (op 23): the resolver that imposed an account-mode ban (banned_by) may lift it early; sets banned_until to epoch and clears banned_by. - pm_ban_expired (virtual): the per-block cron sweeps temporary oracle/creator bans at banned_until, clears them and emits the lift for history/indexers. On-chain state: - pm_market gains decision_url/decision_reason — the oracle's resolution statement stored on-chain (set by pm_resolve_market / pm_no_contest reason), readable via get_market with no history scan. - pm_resolve_market_operation gains decision_reason (reflected on the wire). - pm_dispute gains oracle_response/oracle_response_time. - pm_oracle and pm_creator_ban gain banned_by; pm_creator_ban gains a by_ban_expiry index so the cron sweeps expired bans oldest-first (cleared bans sort into the 0-bucket, permanent bans past now, both skipped). Chain properties (witness-median tunables): - pm_listing_min_coverage_percent (2.5x): hide under-insured markets from the default catalog (enforced by the API plugin, revealed via show_risky). - pm_betting_min_coverage_percent (1.5x, advisory): client risk-confirm threshold; validated betting <= listing. Thin-client read APIs (non-consensus, for the viz-js client): - get_leverage_quote / get_leverage_close_preview / get_leverage_convert_preview reuse the frozen pm::leverage math to mirror the open/close/convert evaluators. - get_market_categories (taxonomy + live counts), get_market_full (one-call enriched, account-scoped), get_lazy_allocations / get_market_lazy_allocation. - Wallet remote_node_api bindings for all of the above. Docs & tests: - EN + ru + zh-CN docs updated (chain-properties, prediction-market-api, specification, operations overview/prediction-markets/validators, virtual-operations); library-integration spec + thin-client plan added. - test_pm_lifecycle: cases #58-#63 cover oracle rebuttal + decision_reason, no-contest rationale, manual unban and its guards, and ban auto-expiry vop.
…throughs The cli_wallet build failed because remote_prediction_market_api and the wallet_api pm_get_*/pm_list_* methods returned the node's typed objects. Those chainbase state objects (pm_market_object, pm_bet_object, ...) and the API DTOs embedding them are not default-constructible (deleted default ctor / shared_string members require a segment manager), so fc::api's client deserializer (T tmp; var.as<T>()) could not instantiate them. Return fc::variant instead: the node already emits fully-formed JSON and cli_wallet prints the variant unchanged, so the read surface is identical.
…te_node_api cli_wallet failed to compile because remote_node_api.hpp pulled in <graphene/plugins/prediction_market_api/prediction_market_api.hpp> transitively, but programs/cli_wallet has no include path to that plugin. After the read pass-throughs switched to fc::variant, the header (and the pmapi alias) are no longer referenced anywhere in the wallet, so remove them. graphene_wallet still builds; the public wallet header no longer leaks a plugin-only dependency.
…LP fee Add two HF14 median-voted consensus parameters and their enforcement: - pm_oracle_accept_window_sec (default 1h): a pending market the named oracle never accepts nor rejects is voided by the per-block cron once now >= created_time + window. The creators seed liquidity is refunded (return_liquidity); the non-refundable creation fee stays with the DAO fund. Tracked via a new pm_market_object.accept_deadline field and a by_accept_deadline index; emits the new pm_market_expired virtual op (op-id 101, appended last in the operation variant to keep tags stable). - pm_lazy_min_liquidity_fee_percent (default 2%): the lazy pool skips markets whose liquidity_fee_percent is below this reward floor, so it never subsidizes depth it is not paid enough to provide. Wired into calc_median and chain_properties_pm::validate().
… fee Cover the new pm_oracle_accept_window_sec / pm_market_expired lifecycle and the pm_lazy_min_liquidity_fee_percent reward-floor gate across: - EN docs (chain-properties, specification, operations, virtual-operations) - RU and zh-CN localizations (@l10n) at full parity with the EN source - library integration spec (delta section + property/vop tables, op-id 101) and thin-client plan - Onix paper EN + RU (state machine, acceptance flow, lazy-pool gate); PDFs rebuilt via pandoc + xelatex (EN 30pp, RU 32pp, 0 missing glyphs).
- Added warning that the live protocol uses basis points (bp), not permille (‰) - Explained the conversion from original PHP prototype’s permille to bp in on-chain code - Specified that all fee fields (oracle_fee_percent, creator_fee_percent, liquidity_fee_percent, etc.) use bp (10000 = 100%) - Highlighted the use of `fromBP` parser for fee fields and rejection of markets exceeding fee sum 10000 - Warned that using deprecated `fromPermille` leads to incorrect fee values, off by a factor of 10
…line The ?: between time_point_sec() and (now + fc::seconds(...)) has no common type — the latter yields fc::time_point, and each type converts to the other, which GCC rejects as ambiguous. Wrap the second branch in an explicit time_point_sec(), matching the copy-init conversion already used for the reveal/dispute deadlines in this file.
…erations The generic impacted-account visitor only collected signing authorities, so prediction-market events were missing from the histories of accounts that did not sign them: - signed ops lost their counterparties (pm_create_market -> oracle, pm_transfer_position -> recipient, pm_unban -> target, oracle auto-accept whitelist); - virtual ops carry no authority at all, so payouts, forfeits, liquidations, oracle penalties, market accept/expire and ban expiry were invisible to the affected users. Add explicit get_impacted_account_visitor overloads for the PM user and virtual operations, inserting every account field they carry. Market-only virtual ops that reference a market by id but carry no account name (pm_batch_settle / pm_dispute_finalize / pm_dispute_auto_close / pm_lazy_recall) are intentionally left to the generic handler.
…, per-node) The free-form `metadata` JSON was stored in the consensus `pm_market_object` (shared_string) permanently — never pruned — even though consensus never reads it (it is written once and only parsed off-chain by the prediction_market_api plugin). That let a market permanently bloat every node's chainbase/shared memory with unbounded, unvalidated data. Move it out of consensus entirely: - pm_market_object: drop the `metadata` field (member, ctor, FC_REFLECT). The operation `pm_create_market_operation.metadata` is unchanged — clients still send it and it lives in the block log, exactly like custom_operation.json. - pm_create_market_evaluator: stop persisting metadata into state. - prediction_market_api: ingest metadata off-chain from the create operation (post_apply_operation) into the existing prunable pm_market_meta_object, instead of reading it back from the consensus object in on_block. - snapshot: drop the metadata import/export for pm_market (auto-excluded from the reflected dump; import of legacy snapshots ignores the field). Because it is now non-consensus, each node prunes it on its own schedule via --pmm-ttl-days (default lowered 7 -> 5; 0 keeps it forever for archival nodes). No consensus length/UTF-8 cap is needed — the blob no longer touches state.
…xed retention A resolved+settled market (status 3, payout_status 3) is immutable — no betting, dispute, resolve or payout can touch it again; it only lingered in chainbase "for history", growing shared-memory state without bound. process_pm_markets() now GCs such markets and their whole object cluster (outcomes, bets, liquidity, commits, dispute votes, leverage positions, the dispute and lazy-allocation rows) once they have been closed for a FIXED protocol constant PM_CLOSED_MARKET_RETENTION_SEC = 5 days (measured from result_expiration + dispute grace). The retention is hardcoded and identical on every node, so pruning is fully deterministic: every node deletes exactly the same markets at the same block, keeping shared-memory state and snapshots in lock-step network-wide (a node syncing from a snapshot ends up with the same market set as everyone else). Work is bounded by the existing per-block cap. Only status-3/payout-3 markets are collected; disputed (payout_status 2) and never-settled markets are left untouched. Nothing holds an id-reference to a settled market, so there are no dangling references after removal.
…ones Extend the market garbage collector to reclaim ANY dead market a fixed 5 days after it becomes terminal — not only resolved+paid ones. A market is dead once nothing can act on it: resolved and paid out, void/no-contest, oracle-rejected, or the oracle never accepted and the accept window expired. To anchor the retention on the actual moment of death (rather than the declared result_expiration), add a `finalized_time` field to pm_market_object, set to the head-block time at every terminal transition: - oracle rejects the market (status -1) - accept window expires, market voided (pm_market_expired) - oracle misses resolution, refund (pm_oracle_missed_penalty) - dispute auto-close refund - settlement / auto-payout (covers resolved, no-contest, post-dispute) A new by_finalized index (finalized_time, id) lets process_pm_markets() sweep terminal markets in time order, skipping the finalized_time==0 live bucket, and delete each cluster PM_CLOSED_MARKET_RETENTION_SEC (5 days) later. Retention is a fixed protocol constant identical on every node, so pruning stays deterministic and snapshots identical network-wide. Snapshot import reads finalized_time when present. Work stays bounded by the per-block cap.
Add consensus_sim scenarios asserting that every way a market can die gets its whole object cluster reclaimed from state after the retention window: - gc_resolved_market_after_retention (resolved + auto-paid) - gc_oracle_reject_after_retention (oracle rejects the terms) - gc_accept_window_expired_after_retention (oracle never accepts, window expires) - gc_oracle_missed_after_retention (oracle misses the resolution deadline) - gc_dispute_auto_close_after_retention (dispute filed, nobody votes, auto-close) Each drives the market to its terminal state, asserts finalized_time is stamped and the cluster is still present, then advances past the retention and asserts the market plus its outcomes/bets/liquidity/commits/dispute-votes/leverage/ dispute/lazy-allocation rows are all gone (market_cluster_absent helper). To make the 5-day retention testable, expose it as a median-voted chain property pm_closed_market_retention_sec (default 432000 = 5 days) instead of a hardcoded constant. It stays identical on every node at any block (median), so GC remains deterministic and snapshot-safe, while tests can shorten it to 30s. The GC cron now reads mp.pm_closed_market_retention_sec.
…tor forks On a testnet fork where one operator controls all validators, the minority-fork detector loops forever: healthy participation (>=33%) auto-clears the enable-stale-production override every tick, then "last 21 blocks all ours" triggers a reset-to-LIB resync. Add an explicit disable-minority-fork-detection flag that bypasses both the standard and DLT detection blocks and is never auto-cleared.
…e-operator forks - Introduce `disable-minority-fork-detection` config to fully skip minority fork detection - Document usage warnings: only for testnets or single-operator forks, never enable in public networks - Clarify difference from `enable-stale-production` which auto-clears at high participation - Update validator plugin docs and configuration references to include new flag - Explain behavior in validator-node docs and warnings for detection loop avoidance - Note watchdog and fork detection interactions with new flag in documentation tables
The prediction_market_api plugin extracted only category/subcategory/tags/ banned_jurisdictions from a market's metadata JSON and discarded title, image and the source condition_id, so thin clients had no human-readable question or icon to render (markets showed as 'Market #N'). Add title, image and condition_id to pm_market_meta_object + parse_market_metadata + FC_REFLECT and copy them in ingest_market_meta. Non-consensus prunable index — no hardfork; a replay re-extracts these for existing markets from the block log. condition_id also enables reliable client back-linking of a mirrored market to its source. Tests extended (meta_parse_test).
The consensus pm_market_object deliberately does NOT carry the free-form metadata; the plugin parses it off-chain into pm_market_meta_index. So clients that read market.metadata (title/image/category) got nothing — cards and the market detail showed 'Market #N' with no thumbnail. Add market_card(db, market): serialize the market and inject a reconstructed 'metadata' object (title/image/category/subcategory/tags[]/banned_jurisdictions[]/ condition_id) plus flat title/image/category fields. Route the market-returning read APIs through it: get_market, list_markets, list_markets_by_oracle, list_markets_by_creator (return vector<fc::variant>), and get_market_full (overlay the enriched card onto the .market field). Existing clients that parse market.metadata now work unchanged; no consensus/state change, no replay. remote_node_api already types these as fc::variant, so the wallet/CLI is unaffected.
Read-only query for the markets that need an oracle's result now: active (status 1) markets whose betting window has closed (betting_expiration <= head_block_time) and are therefore not yet resolved. "Awaiting" is not a distinct status — a market stays active from open through betting-close until it is resolved — so it can't be filtered by status alone. Walk the by_betting_expiration index (keyed status, betting_expiration, id) over the bounded prefix of active markets past their betting deadline and keep the given oracle's rows. This lets an Oracle Console list pending-resolution markets without scanning the oracle's full (mostly resolved) history client-side. Plugin-only read method — no consensus change.
…et meta The parser already ships each market's short rules text under metadata.description (Polymarket description / Kalshi rules_primary), but meta_parse dropped it. Add description to the meta whitelist: parse it, store it in pm_market_meta_object, and return it in market_card's metadata. The on-chain url still points to the full legal terms at the source; description is the short "how the oracle resolves" text for clients. Display/indexing only — no consensus change, no hardfork.
list_markets gains an optional order arg ("oldest" default = legacy, "newest"
= id desc via reverse traversal of the by_status equal-range). Discovery feeds
need newest-first without a full scan.
Meta backfill: after --replay-from-snapshot, off-chain market metadata is rebuilt
only for markets whose create op fell in the reindex window. The DLT rolling block
log can reach further back than the snapshot, so those older create ops are still
on disk. A one-shot pass (run from on_block, budgeted 500 blocks/apply) scans the
DLT log and re-ingests meta for any market still missing it. Op->market mapping is
positional per block (meta ingest is all-or-nothing per block, so the k-th create
op created the k-th market of that block); create_meta_for() is idempotent.
Runs post-replay (reindex_from_dlt does not flush applied_block), no-op when nothing
is missing (normal restart) or when there is no DLT log.
…ional) The first backfill mapped create ops to markets positionally by block timestamp, assuming market.created_time == the DLT block's own timestamp. created_time is set from head_block_time() which lags by a block, so the timestamp bucket was off and titles landed on the wrong markets (observed shift on testnet). Replace with a strong identity key (creator + url + betting/result expiration + outcome count) — all consensus fields the evaluator copies verbatim from the op. A key mismatch now simply leaves a market's meta empty; it can never mis-assign a title to the wrong market. Duplicate keys resolve in id order via a small list. Recovery: replay-from-snapshot starts with an empty meta index, so rebuilding the node on this image re-ingests all meta correctly (the previous wrong meta is gone).
…ves) A market created with betting_expiration == 0 keeps betting open until the oracle resolves it; result_expiration (<= now + pm_max_market_duration, i.e. <= 1 year) becomes a pure emergency backstop. If the oracle never resolves by then, the existing missed-resolution path in process_pm_markets refunds every bet and slashes the oracle insurance — so the "oracle abandoned the market" case has a trustless recourse without a dispute. - create: betting_expiration==0 branch requires allow_early_resolution and a result_expiration within (now, now + pm_max_market_duration]. Non-open-ended markets keep the existing checks. - betting / liquidity gates (place, commit, reveal, add, withdraw): treat 0 as "open while the market is active". - leverage: available on open-ended markets too (the extra volatility is the bettor's own risk). The expiration-buffer check only applies when there is a real betting deadline; guarding it also avoids the epoch-0 underflow of (betting_expiration - buffer). - prediction_market_api: mirror the leverage buffer guard; report auto_close_time = 0 (no force-close point) for open-ended markets. Resolve evaluator and the missed-resolution backstop are reused unchanged. Gated by HF14, no separate feature flag.
Adds a time-based funding cost to leverage positions so a long-held loan (now possible on open-ended markets) pays the lazy pool for the capital it ties up. - New median-voted chain property pm_leverage_funding_rate_ppm_per_day (uint32, ppm of the loan per 24h; default 50 = 0.005%/day; 0 disables). Validated <= 100%/day. - pm_leverage_position_object gains funding_paid (cumulative) and funding_due_time (next 24h boundary), plus a by_lev_funding_due index (status, funding_due_time, id). - accrue_leverage_funding() charges whole 24h periods (one-shot catch-up) from the bettor's equity into funding_paid, which raises the effective obligation (liquidation_threshold + funding_paid) and thus pulls up the liquidation point. Funding flows to LP yield through the existing pool_profit path. - process_pm_markets() gets a funding sweep: for each due active position, accrue, reprice at current reserves, and liquidate (reason 3) if now underwater. - open sets funding_due_time = created_time + 24h; close/convert/liquidate accrue first and settle funding to the pool. cascade trigger includes funding_paid. - prediction_market_api leverage quote exposes funding_rate_ppm_per_day; get_pm_chain_properties returns the new property automatically. Snapshot uses generic FC_REFLECT import/export so the new fields are covered.
Sibling markets of one real-world event (a match/game) carry the same opaque metadata.event key. Index it off-chain in the prediction_market_api plugin (non-consensus, prunable — same mechanism as category/description), mirroring by_meta_category with a by_meta_event index, and expose: list_markets_by_event(event, from, limit) -> full market cards, oldest-first market_card and get_market_meta now surface the event field; wallet CLI gets pm_list_markets_by_event. Parsers already emit meta.event (Polymarket event_id, Kalshi event_ticker). Enables client event pages / parent-child nesting. Plugin TU compiles clean; meta_parse_test covers the new field.
…0->64, MAX_PM_OUTCOMES_PER_MARKET 16->128)
…pletes multi-outcome limit bump)
…event pages/cards)
Fresh chains built from this branch enable leverage out of the box. On an already-running testnet the median value persists in state, so validators must still vote pm_leverage_enabled on to activate it there.
The activation checklist told the operator to watch the fork become pending on the testnet before its timestamp. Measured on the testnet after deploying 4.1.0 over a chain at 4.0.0, that cannot happen: emergency_consensus_active is true and the schedule is filled with CHAIN_EMERGENCY_VALIDATOR_ACCOUNT (committee), which is excluded from the fork vote in both directions - database.cpp:2811 skips the vote injection for the producing validator and database.cpp:3413 skips its slots in the tally. Every produced block carries an empty extensions array and get_next_scheduled_hardfork keeps returning 4.0.0 with the previous fork's time, so next_hardfork stays pinned to current_hardfork_version and the compiled activation time is a dead knob. Deploying first still verifies the image, the snapshot import and the invariants, but not activation; observing activation before mainnet needs a chain whose producer is not the emergency committee (a fresh BUILD_TESTNET build). Also corrects §3 to the RPC methods this build actually exposes (get_hardfork_property is not registered on VIZ; get_hardfork_version and get_next_scheduled_hardfork are), and both locales carry the same correction.
A chain in emergency consensus mode can never activate a hardfork: the committee holds every schedule slot and is excluded from the vote tally, so neither next_hardfork nor the 17-of-21 quorum can ever move. That blocks verifying an activation on a testnet before the production date. testnet_plugin adds one startup command, --testnet-hardfork <version|number>. When set, it applies every hardfork up to the requested one as soon as the chain state is loaded - after the snapshot import, before block production starts - bypassing the vote tally entirely. The plugin is inert unless it is loaded and given a target, so shipping it in the production image is harmless; config_testnet.ini enables it. Also add database::get_hardfork_number() so a caller can name a hardfork by version (4.1.0) instead of the number set_hardfork() takes.
…on on the testnet An emergency-consensus chain can never schedule a hardfork, so the previous note ended at "activation is unobservable on the production-config testnet". testnet_plugin removes that limitation: describe the command, the operator sequence (image first, then the target - a config line for an unregistered plugin aborts startup) and the log lines to expect, in both locales.
main.cpp registers the plugin, so the binary must link it: plugins/CMakeLists.txt globs its subdirectories, which builds the library but leaves main.cpp's reference to testnet_plugin::testnet_plugin() unresolved.
appbase constructs a plugin object when it is registered — before any plugin is initialized — and get_plugin() deliberately refuses plugins that are still in the 'registered' state. Building the impl in the constructor therefore threw "unable to find plugin: chain" while merely registering the plugin, which aborted every startup of the binary, `--help` included. Chain::plugin is resolved in plugin_initialize instead: appbase's plugin<>::initialize() initializes the dependencies declared via APPBASE_PLUGIN_REQUIRES before calling it, so the chain plugin is reachable there.
Declaring the option in both descriptions (the "also show it in --help" move) puts two options with the same long name into the set appbase parses argv with (all_options = _cli_options + _cfg_options), and boost then refuses every start with option '--testnet-hardfork' is ambiguous and matches different versions of '--testnet-hardfork' Registering it in the config description alone keeps both documented forms working — appbase parses the command line against cli + cfg — at the cost of it not being listed by --help, which prints _cli_options alone. That trade is documented in the upgrade notes, together with how to verify the flag (the FORCING HARDFORK banner), because grepping --help is the obvious-but-wrong check.
The owner confirmed 2026-10-05 00:00:00 UTC (question steemit#1642), so the comment claiming the date is provisional is stale: it reads as if the date still had to be re-set before the release is scheduled. A validator or packager reading that line could take the announced date for a draft.
The operations table stopped at id 100 while the chain variant already contains 101 pm_market_expired, 102 pm_dispute_opened, 103 pm_early_exit_claim_paid and 104 pm_lp_payout (all virtual, declared in pm_virtual_operations.hpp). A client author reading the doc would build a wrong op-id map. Verified by diffing the chain operation variant against the table in all three locales: 105 rows, zero mismatches. Gap note updated to 100-104.
…id 105 Account-level delegation of an explicit operation list: `agent` may broadcast the listed operations on behalf of `account`, signing with its own active key, until `expiration` (epoch = perpetual, empty list = revoke). No roles, no masks: a mask would silently widen the grant when a new operation is appended to the chain. Wiring: - op struct appended at the END of the operation static_variant (index 105), so no existing op-id moves; - operation_wire_name() / is_broadcastable_operation_wire_name() in operations.cpp: name lookup derived from the same type names the wire format uses, with virtual operations excluded (they are never broadcast, so a delegation to one is dead); - validate() refuses what looks fine but is not: unknown names, virtual names, legacy aliases (would duplicate a canonical name), malformed names, self-agent, set_agent_permission itself (an agent must not re-delegate) and the proposal wrappers (a proposal carries arbitrary operations whose authorities are collected at execution time, so delegating proposal_create would bypass the explicit list). Tests: tests/pm/agent_access_test.cpp (6 cases: op-id anchors on both sides of the append, wire-name classification, and the validate() accept/refuse sets). Proven to be able to fail: with the never-delegable assert disabled, exactly the 4 escalation checks go red.
… delegation names Step 2 of HF15 agent access: the consensus state behind set_agent_permission. Object `agent_permission_object` holds one row per (principal, agent) pair: the canonical `,`-joined list of operation names and an expiration (epoch = perpetual). Unique index on (principal, agent) so a re-grant replaces rather than accumulates, and a non-unique index on the agent — the wipe rules need both directions. Two adversarial findings drove the deny-list change (protocol side, same branch): - `account_update` is an account takeover: an op WITHOUT the master field is satisfied by the ACTIVE authority and may carry a new `active` authority, so an agent granted account_update for "metadata edits" can rotate the principal's active key to its own and own the account. - master-only operations (recover_account, change_recovery_account, set_account_price, set_subaccount_price, target_account_sale) are unreachable through a delegation — the hook never substitutes master — so granting one would be a permission that can never succeed. The deny-list is now a single source of truth in protocol (`never_delegable_operation_names`), checked both at grant time and, in step 3, in the authority hook. The evaluator re-checks the list and the operation names on every grant: the hook trusts these rows, so a bad row would be live escalation rather than cosmetics. Tests: 7 cases / 33 checks green (op-id anchors, wire names, accept/refuse sets). Verified: graphene_protocol + graphene_chain build clean (rc=0) with the new object type in the enum.
A principal's active authority is answered with its agent's active authority only when a live delegation row covers every authority-requiring operation of the transaction, the principal's own signatures do not already satisfy it, and the transaction asks for no master or regular authority. Signature recovery is deferred until a row exists, so ordinary transactions pay nothing extra. Chain-level tests (consensus_sim, BUILD_TESTNET): granted op, principal still signs, full-coverage rule (mutation-checked), master-mixed tx, missing/expired/deny-listed/unknown rows, no leak through nested account_auths.
Export the index and import it with an explicit setter for the shared_string operations list.
Rows go on both sides (principal and agent) in the same step as a master change (account_update, recover_account), an active change, a direct sale (buy_account) and an auction close. Regular-only updates keep the row: agents sign with active. Tests: principal active rotation and agent master rotation wipe the row (mutation-checked), regular-only change keeps it.
buy_account extended an auction by fc::time_point::now() + 5 min, so each node (and every replay) computed its own close time. Validators could close the same auction in different blocks and a block-log replay could not reproduce historical state. From HF15 the base is head_block_time(); older blocks keep the legacy read.
The auction-close path is left untested for now: bid extensions are measured by wall clock, which the virtual-clock harness cannot reach (fixed separately in fix-auction-wallclock).
… on grant The authority hook walks all of a principal's rows for each transaction the principal did not sign itself, and a rejected transaction pays no bandwidth, so the row count must be bounded. Every grant now removes the principal's expired rows before counting, so a dead row lives at most until the next grant and the total stays <= 16. Test: cap refuses the 17th agent, a re-grant is an overwrite, an expired row is swept and its slot reused (cap mutation-checked).
HF15: measure auction bid extension from head block time, not wall clock
Possible now that #166 (auction extension from head block time) is in pm.
An agent is no longer a separate account but a record on the principal: agent_name (unique per principal) + agent_key + operation list + expiration. A transaction signed by the agent key passes on behalf of the principal when every operation is in the list. - one key per agent; reissue by name replaces the key; key reuse across names rejected - all agents wiped on master/active change, recovery, direct sale and auction close; regular-only change keeps them - snapshot imports agent_name/agent_key - 15 chain tests rewritten; mutations (coverage, wipe, key uniqueness) are caught
Returns a principal's agents (name, key, operation list, expiration) ordered by name, with expired rows flagged. At most CHAIN_AGENT_MAX_PER_ACCOUNT rows per principal, so no paging.
Agents carry an optional set of addons, e.g. ["vizhub"]: scopes external services read to accept the agent key for their own actions. The node stores them and never interprets them; they grant nothing on chain. At most 10, each shorter than 64 bytes, no ','. An addon-only agent is valid; revoke = both lists empty. Stored in the object, snapshot and get_agent_permissions.
…object The addons import landed in import_transactions (same expiration line) and used variant::contains, which does not exist; the Docker build failed on it.
HF15: agent access — delegate listed operations to an agent account
… invalidated the workflow)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
HF15 batch for Prediction Markets, accumulated on
pmafter #124 landed inmaster.What is inside
fix(pm): gated LP/batch corrections and inclusive quote cap(fix(pm): gated LP/batch corrections and inclusive quote cap #163, reviewed by @web3blind): LP contributions at/above the floor are no longer rejected by an exclusive comparison; the legacy directpm_place_bet(mode=1)path is gated so batch bets only enter through commit→reveal;max_leverage_loanquotes are inclusive of the pool cap.fix(pm-api): never quote a loan thatapply()will refuse— closes the "quote says available, open fails" divergence.test(pm): fork-aware audit regressions+ a CI job that runsconsensus_sim/pm unit tests.feat(pm): register the audit fixes as HF15in both configs.Why this PR is open now (CI vehicle)
The docker workflow (
.github/workflows/docker-pr-build.yml) triggers onpull_requestonly, so pushing topmno longer rebuildsvizd:pm. This PR is what makes CI buildvizblockchain/vizd:pm— the image the VIZ testnet (testnet.viz.world, shelter) runs.Do not merge until
mode=1is refused once HF15 activates);Merging this into
masteractivates HF15 for anyone runningvizblockchain/vizd:latest, which is why it stays open until the above is done.