Repository navigation
ApplicationEngine: fix IsHardforkEnabled behaviour - #4764
Conversation
IsHardforkEnabled behaviour
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master-n3 #4764 +/- ##
=============================================
- Coverage 83.93% 83.90% -0.04%
=============================================
Files 240 240
Lines 17129 17128 -1
Branches 2453 2453
=============================================
- Hits 14377 14371 -6
- Misses 1967 1968 +1
- Partials 785 789 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
f1bc773 to
49461b3
Compare
ApplicationEngine.IsHardforkEnabled should always respect persisting block index. Returning `true` based on the fact that hardfork is just _present_ in the list of configured hardforks irrespective from the current block index is misleading and will affect methods that don't set persisting block (like VerifyWitness): https://github.com/neo-project/neo/blob/99618f483a7e8bf783ad50af8a049752abd5ce4e/src/Neo/SmartContract/Helper.cs#L333 This problem will also lead to improper dynamic price calculation introduced by #4760 as described in #4760 (comment). Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
49461b3 to
0b61521
Compare
For those callers who don't set persisting block explicitly on ApplicationEngine construction, we use `NativeContract.Ledger.CurrentIndex` instead of persisitng block index. However, Ledger's `CurrentIndex` returns the index of last _persisted_ block most of the time (see the note in nspcc-dev/neo-go#3889 (comment)). Whereas we need the _persisting_ block index. Hence, the suggestion is to use an increment of `NativeContract.Ledger.CurrentIndex` as persisting block index. Related to #4764. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
For those callers who don't set persisting block explicitly on ApplicationEngine construction, we use `NativeContract.Ledger.CurrentIndex` instead of persisitng block index. However, Ledger's `CurrentIndex` returns the index of last _persisted_ block most of the time (see the note in nspcc-dev/neo-go#3889 (comment)). Whereas we need the _persisting_ block index. Hence, the suggestion is to use an increment of `NativeContract.Ledger.CurrentIndex` as persisting block index. Related to #4764. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
|
Mainnet states match up to 13909277 (checked in combination with #4765):
|
|
@neo-project/core ready for review & merge. Testnet states check is in progress, but I don't expect this change to affect the states in any way because we always set |
|
It's compatibility with previous behaviour? |
States are compatible, no fork is required. |
|
|
||
| return ProtocolSettings.IsHardforkEnabled(hardfork, PersistingBlock.Index); | ||
| var persistingIndex = PersistingBlock?.Index ?? (SnapshotCache is null ? 0 : NativeContract.Ledger.CurrentIndex(SnapshotCache) + 1 /* Ledger.CurrentIndex returns already persisted block, hence use +1 to define _persisting_ block index */); | ||
| return ProtocolSettings.IsHardforkEnabled(hardfork, persistingIndex); |
There was a problem hiding this comment.
[suggestion] The checklist claims tests were added, but the PR only touches ApplicationEngine.cs (2 insertions, 5 deletions). Codecov reports 50% patch coverage with the SnapshotCache is null branch untested. Existing UT_ApplicationEngine_Residual.IsHardforkEnabled_MatchesProtocolSettings builds an engine via TestEngineRunner.Create (no persistingBlock) and compares against ProtocolSettings.IsHardforkEnabled(hf, Ledger.CurrentIndex(_snapshot)) under TestProtocolSettings.Default, whose omitted hardforks are filled at height 0 — so ContainsKey, CurrentIndex, and CurrentIndex + 1 all return true and the test cannot prove this fix. The motivating case from neo#4760 (verification engine, Huyao listed at a future height, ledger still below it) is not covered here.
Suggestion: Add unit tests that construct ApplicationEngine.Create(TriggerType.Verification, …, persistingBlock: null) with a snapshot whose Ledger.CurrentIndex is known, and protocol settings that place a hardfork at (a) a future height (CurrentIndex + 2 → false), (b) CurrentIndex + 1 → true, (c) CurrentIndex → true, plus a control with an explicit PersistingBlock.Index that must win over the snapshot height. That is the matrix #4760’s review asked for and the only way to lock the +1 rule.
| return ProtocolSettings.Hardforks.ContainsKey(hardfork); | ||
|
|
||
| return ProtocolSettings.IsHardforkEnabled(hardfork, PersistingBlock.Index); | ||
| var persistingIndex = PersistingBlock?.Index ?? (SnapshotCache is null ? 0 : NativeContract.Ledger.CurrentIndex(SnapshotCache) + 1 /* Ledger.CurrentIndex returns already persisted block, hence use +1 to define _persisting_ block index */); |
There was a problem hiding this comment.
[suggestion] This helper now uses CurrentIndex + 1 when PersistingBlock is null, but three sibling height calculations still use CurrentIndex with no +1: the constructor’s persistingIndex (opcode / Faun / Huyao gating, line 262), ApplicationEngine.Create jump-table selection (line 781), and NativeContract.GetAllowedMethods (line 41). After this lands, a verification engine at CurrentIndex == hfHeight - 1 will report the hardfork enabled here (and therefore in OnSysCall, crypto, NativeContract.Invoke’s ActiveIn/DeprecatedIn checks, etc.) while still selecting the pre-fork jump table, opcode-price path, and native-method cache. neo-go uses persistingBlockHeight() for both IsHardforkEnabled and opcode pricing (initVM), so #4760 syscall prices gated on this helper will disagree with C# opcode prices for that one block. Persist is unaffected (PersistingBlock.Index is used in all four places).
Suggestion: Cache one persisting index on the engine — PersistingBlock?.Index ?? (snapshot is null ? 0 : Ledger.CurrentIndex(snapshot) + 1) — and use it from the constructor, Create, GetAllowedMethods, and this method. At minimum, document that verification still has a 1-block split until those call sites are updated; do not let #4760 assume this helper and constructor height are the same.
| return ProtocolSettings.Hardforks.ContainsKey(hardfork); | ||
|
|
||
| return ProtocolSettings.IsHardforkEnabled(hardfork, PersistingBlock.Index); | ||
| var persistingIndex = PersistingBlock?.Index ?? (SnapshotCache is null ? 0 : NativeContract.Ledger.CurrentIndex(SnapshotCache) + 1 /* Ledger.CurrentIndex returns already persisted block, hence use +1 to define _persisting_ block index */); |
There was a problem hiding this comment.
[nit] The +1 rationale is worth keeping, but it is buried in an inline comment on a packed ternary, which makes the new rule easy to miss and the line hard to read.
Suggestion: Split the fallback out and put the why-comment on its own line, e.g. compute Ledger.CurrentIndex(SnapshotCache) + 1 in a following statement (or a small local) with the comment above it.
|
Maybe add some unit tests. Coverage is down. The code is OK and compatible. |
) For those callers who don't set persisting block explicitly on ApplicationEngine construction, we use `NativeContract.Ledger.CurrentIndex` instead of persisitng block index. However, Ledger's `CurrentIndex` returns the index of last _persisted_ block most of the time (see the note in nspcc-dev/neo-go#3889 (comment)). Whereas we need the _persisting_ block index. Hence, the suggestion is to use an increment of `NativeContract.Ledger.CurrentIndex` as persisting block index. Related to #4764. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru> Co-authored-by: Alvaro <amjarag@gmail.com>
|
Just for the record, testnet states match up to (checked in combination with #4765):
Will do in a separate PR. |
…o-project#4765) For those callers who don't set persisting block explicitly on ApplicationEngine construction, we use `NativeContract.Ledger.CurrentIndex` instead of persisitng block index. However, Ledger's `CurrentIndex` returns the index of last _persisted_ block most of the time (see the note in nspcc-dev/neo-go#3889 (comment)). Whereas we need the _persisting_ block index. Hence, the suggestion is to use an increment of `NativeContract.Ledger.CurrentIndex` as persisting block index. Related to neo-project#4764. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru> Co-authored-by: Alvaro <amjarag@gmail.com>
ApplicationEngine.IsHardforkEnabled should always respect persisting block index. Returning `true` based on the fact that hardfork is just _present_ in the list of configured hardforks irrespective from the current block index is misleading and will affect methods that don't set persisting block (like VerifyWitness): https://github.com/neo-project/neo/blob/99618f483a7e8bf783ad50af8a049752abd5ce4e/src/Neo/SmartContract/Helper.cs#L333 This problem will also lead to improper dynamic price calculation introduced by neo-project#4760 as described in neo-project#4760 (comment). Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru> Co-authored-by: Alvaro <amjarag@gmail.com> Co-authored-by: Shargon <shargon@gmail.com>


Description
ApplicationEngine.IsHardforkEnabledshould always respect persisting block index. Returningtruebased on the fact that hardfork is just present in the list of configured hardforks irrespective from the current block index is misleading and will affect methods that don't set persisting block (like VerifyWitness):neo/src/Neo/SmartContract/Helper.cs
Line 333 in 99618f4
This problem will also lead to improper dynamic price calculation introduced by #4760 as described in #4760 (comment).
Draft because states check is in progress. This PR is required for #4760 and related to #4726.
Type of change
How Has This Been Tested?
Checklist: