-
Notifications
You must be signed in to change notification settings - Fork 59
feat(platform): define protocol-versioned smart-contract computation limits and their gas representation #4705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
DCG-Claude
wants to merge
12
commits into
v6.0-dev
Choose a base branch
from
dashvm/r06-01
base: v6.0-dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
053d479
feat(platform): add smart-contract computation limits to the system l…
DCG-Claude 7974f81
feat(platform): price smart-contract computation in the fee schedule
DCG-Claude 948a0d3
feat(platform): introduce protocol versions 15 to 17 with 17 as the 5…
DCG-Claude 6f8a15d
feat(dpp): price smart-contract computation units in credits
DCG-Claude dc91958
feat(drive-abci): add the per-block smart-contract computation ledger
DCG-Claude e2deac7
docs: describe smart-contract computation limits, pricing and gas map…
DCG-Claude c622e17
fix(dpp): read the smart-contract computation price from the active p…
DCG-Claude 8416f5a
fix(drive-abci): bind computation reservations to the ledger that iss…
DCG-Claude 801d2be
refactor(drive-abci): drop equality on the block computation ledger
DCG-Claude b51a013
docs: correct the fee version number rule and the version count in th…
DCG-Claude ef166e7
docs: list the contract validation fee group among those read from th…
DCG-Claude 437efa2
docs: state that v17's overridden tables must be rebased on a forward…
DCG-Claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| //! Pricing of smart-contract computation. | ||
| //! | ||
| //! Contract work is metered by the runtime in [`ComputationUnits`], a deterministic count under | ||
| //! the active metering generation, and bounded per invocation and per block by | ||
| //! [`SmartContractComputationLimits`] in the protocol version's system limits. This module turns | ||
| //! the units an invocation consumed into credits at the protocol-versioned price of the fee | ||
| //! schedule (`FeeVersion::dashvm`). | ||
| //! | ||
| //! The price is read from the fee schedule of the **active protocol version** | ||
| //! (`platform_version.fee_version.dashvm`), never from the persisted epoch fee history. That | ||
| //! history is keyed by `fee_version_number`, records a schedule only when that number changes, | ||
| //! and is restored from saved state through `FeeVersion::get(number)`; it serves the storage, | ||
| //! processing, hashing and signature groups (`KnownCostItem`) and the storage refund rates, and | ||
| //! nothing else. A schedule that adds contract pricing does not change the number, so the history | ||
| //! never carries the `dashvm` group, exactly as it never carried `data_contract_registration`, | ||
| //! `state_transition_min_fees` or `vote_resolution_fund_fees`, all of which are likewise read from | ||
| //! the active protocol version. The function below therefore takes `&PlatformVersion`, so a | ||
| //! history entry cannot be passed to it by mistake. | ||
| //! | ||
| //! The charge enters the processing fee of the invocation's `FeeResult`, exactly like every other | ||
| //! processing charge, and therefore reaches Tenderdash through the existing `gas_used` and | ||
| //! `gas_wanted` fields, which report `FeeResult::total_base_fee()` in credits. Gas stays | ||
| //! denominated in credits; computation units are never reported to Tenderdash and no unit | ||
| //! equivalence between them and Tenderdash gas exists. | ||
|
|
||
| use crate::fee::Credits; | ||
| use crate::ProtocolError; | ||
| pub use platform_version::version::system_limits::smart_contract::{ | ||
| ComputationUnits, SmartContractComputationLimits, | ||
| }; | ||
| use platform_version::version::PlatformVersion; | ||
|
|
||
| /// Prices `units` of smart-contract computation in credits at the active protocol version's | ||
| /// rate (`platform_version.fee_version.dashvm.credits_per_computation_unit`). | ||
| /// | ||
| /// The table is the versioned part: a schedule that prices computation differently is a new | ||
| /// `FEE_VERSION*` with a different `dashvm` group, not a new generation of this function. | ||
| /// Callers on a block path pass the version from platform state | ||
| /// (`platform_state.current_platform_version()`), never `PlatformVersion::latest()` and never a | ||
| /// schedule taken from the epoch fee history (see the module documentation). | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// * `ProtocolError::CorruptedCodeExecution` when the protocol version has no smart-contract | ||
| /// pricing. A caller only reaches this function after the protocol version admitted contract | ||
| /// execution, and the tables guarantee that such a version prices computation, so a missing | ||
| /// price is a broken build rather than a user mistake. | ||
| /// * `ProtocolError::Overflow` when the charge does not fit in `Credits`. With the provisional | ||
| /// rate of one credit per unit and limits far below `u64::MAX` this is unreachable, but the | ||
| /// arithmetic is checked so that no revision of either table can wrap a fee. | ||
| pub fn computation_units_to_credits( | ||
| units: ComputationUnits, | ||
| platform_version: &PlatformVersion, | ||
| ) -> Result<Credits, ProtocolError> { | ||
| let price = platform_version | ||
| .fee_version | ||
| .dashvm | ||
| .as_ref() | ||
| .ok_or_else(|| { | ||
| ProtocolError::CorruptedCodeExecution(format!( | ||
| "computation_units_to_credits requires fee_version.dashvm, which protocol version {} does not carry", | ||
| platform_version.protocol_version | ||
| )) | ||
| })?; | ||
|
|
||
| units | ||
| .checked_mul(price.credits_per_computation_unit) | ||
| .ok_or(ProtocolError::Overflow( | ||
| "smart-contract computation charge overflowed credits", | ||
| )) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use platform_version::version::fee::dashvm::FeeDashVmVersion; | ||
| use platform_version::version::fee::FeeVersion; | ||
| use platform_version::version::PlatformVersion; | ||
|
|
||
| #[test] | ||
| fn should_price_computation_units_at_the_schedule_rate() { | ||
| let platform_version = PlatformVersion::latest(); | ||
| let limits = platform_version | ||
| .system_limits | ||
| .smart_contract_computation | ||
| .as_ref() | ||
| .expect("the latest protocol version bounds smart-contract computation"); | ||
| let price = platform_version | ||
| .fee_version | ||
| .dashvm | ||
| .as_ref() | ||
| .expect("the latest protocol version prices smart-contract computation"); | ||
|
|
||
| let units = limits.max_computation_units_per_invocation; | ||
|
|
||
| let credits = computation_units_to_credits(units, platform_version) | ||
| .expect("a maximal invocation must be priceable"); | ||
|
|
||
| assert_eq!(credits, units * price.credits_per_computation_unit); | ||
| } | ||
|
|
||
| #[test] | ||
| fn should_price_zero_units_as_zero_credits() { | ||
| let platform_version = PlatformVersion::latest(); | ||
|
|
||
| let credits = computation_units_to_credits(0, platform_version) | ||
| .expect("zero units must be priceable"); | ||
|
|
||
| assert_eq!(credits, 0); | ||
| } | ||
|
|
||
| #[test] | ||
| fn should_fail_with_overflow_when_the_charge_does_not_fit_in_credits() { | ||
| let latest = PlatformVersion::latest(); | ||
| let platform_version = PlatformVersion { | ||
| fee_version: FeeVersion { | ||
| dashvm: Some(FeeDashVmVersion { | ||
| credits_per_computation_unit: 2, | ||
| }), | ||
| ..latest.fee_version.clone() | ||
| }, | ||
| ..latest.clone() | ||
| }; | ||
|
|
||
| let result = computation_units_to_credits(u64::MAX, &platform_version); | ||
|
|
||
| assert!( | ||
| matches!(result, Err(ProtocolError::Overflow(_))), | ||
| "expected an overflow error, got {result:?}" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn should_report_corrupted_code_execution_when_the_protocol_version_has_no_smart_contract_pricing( | ||
| ) { | ||
| let platform_version = PlatformVersion::get(14).expect("protocol version 14 exists"); | ||
| assert!( | ||
| platform_version.fee_version.dashvm.is_none(), | ||
| "protocol version 14 predates smart-contract pricing" | ||
| ); | ||
|
|
||
| let result = computation_units_to_credits(1, platform_version); | ||
|
|
||
| assert!( | ||
| matches!(result, Err(ProtocolError::CorruptedCodeExecution(_))), | ||
| "expected a corrupted code execution error, got {result:?}" | ||
| ); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.