Repository: nspcc-dev/neo-go
Pull request #3849 (2025-03-26 16:25:43)

native: move MaxValidUntilBlockIncrement to Policy
A part of #3841. Port https://github.com/neo-project/neo/pull/3861.

AnnaShaleva: @roman-khimov, MaxVUBIncrement change relatively simple, but MaxTraceableBlocks is a little bit more unobvious, there's a couple of places where "hacks" need to be used to properly handle some situations. Take a look at the TODO comments, please.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev) Report
Attention: Patch coverage is `90.37657%` with `23 lines` in your changes missing coverage. Please review.
> Project coverage is 82.57%. Comparing base [(`42beabc`)](https://app.codecov.io/gh/nspcc-dev/neo-go/commit/42beabcde0405369f6f68c66f5ac15c94785772c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev) to head [(`3d1c143`)](https://app.codecov.io/gh/nspcc-dev/neo-go/commit/3d1c143c2a62fa3449b701e52aa1843e160b0a60?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev).
> Report is 3 commits behind head on master.

| [Files with missing lines](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev) | Patch % | Lines |
|---|---|---|
| [pkg/core/native/policy.go](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?src=pr&el=tree&filepath=pkg%2Fcore%2Fnative%2Fpolicy.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev#diff-cGtnL2NvcmUvbmF0aXZlL3BvbGljeS5nbw==) | 88.72% | [14 Missing and 1 partial :warning: ](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev) |
| [pkg/core/blockchain.go](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?src=pr&el=tree&filepath=pkg%2Fcore%2Fblockchain.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev#diff-cGtnL2NvcmUvYmxvY2tjaGFpbi5nbw==) | 90.32% | [5 Missing and 1 partial :warning: ](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev) |
| [cli/server/server.go](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?src=pr&el=tree&filepath=cli%2Fserver%2Fserver.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev#diff-Y2xpL3NlcnZlci9zZXJ2ZXIuZ28=) | 66.66% | [1 Missing :warning: ](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev) |
| [pkg/network/tcp\_peer.go](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?src=pr&el=tree&filepath=pkg%2Fnetwork%2Ftcp_peer.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev#diff-cGtnL25ldHdvcmsvdGNwX3BlZXIuZ28=) | 0.00% | [1 Missing :warning: ](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #3849      +/-   ##
==========================================
+ Coverage   82.53%   82.57%   +0.03%     
==========================================
  Files         342      342              
  Lines       47891    48071     +180     
==========================================
+ Hits        39527    39694     +167     
- Misses       6730     6743      +13     
  Partials     1634     1634              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/nspcc-dev/neo-go/pull/3849?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nspcc-dev).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
AnnaShaleva: Let's use this PR for `MaxValidUntilBlockIncrement` change only, I'll move `MaxTraceableBlocks` related changes to another PR.

Patch:
diff --git a/config/protocol.mainnet.neofs.yml b/config/protocol.mainnet.neofs.yml
index 1cf650f1d..e9bafea07 100644
--- a/config/protocol.mainnet.neofs.yml
+++ b/config/protocol.mainnet.neofs.yml
@@ -4,6 +4,8 @@ ProtocolConfiguration:
   MaxValidUntilBlockIncrement: 8640
   InitialGASSupply: 52000000
   TimePerBlock: 1s
+  Genesis:
+    MaxValidUntilBlockIncrement: 8640
   MemPoolSize: 50000
   StandbyCommittee:
   - 026fa34ec057d74c2fdf1a18e336d0bd597ea401a0b2ad57340d5c220d09f44086
diff --git a/docs/node-configuration.md b/docs/node-configuration.md
index c9892a29d..f3a62a7b6 100644
--- a/docs/node-configuration.md
+++ b/docs/node-configuration.md
@@ -414,7 +414,7 @@ protocol-related settings described in the table below.
 | MaxBlockSystemFee | `int64` | `900000000000` | Maximum overall transactions system fee per block. |
 | MaxTraceableBlocks | `uint32` | `2102400` | Length of the chain accessible to smart contracts. | `RemoveUntraceableBlocks` should be enabled to use this setting. |
 | MaxTransactionsPerBlock | `uint16` | `512` | Maximum number of transactions per block. |
-| MaxValidUntilBlockIncrement | `uint32` | `5760` | Upper height increment limit for transaction's ValidUntilBlock field value relative to the current blockchain height, exceeding which a transaction will fail validation. It is set to estimated daily number of blocks with 15s interval by default. |
+| MaxValidUntilBlockIncrement | `uint32` | `5760` | Upper height increment limit for transaction's ValidUntilBlock field value relative to the current blockchain height, exceeding which a transaction will fail validation. It is set to estimated daily number of blocks with 15s interval by default. This setting is replaced by [`Genesis`-level](#Genesis-Configuration) `MaxValidUntilBlockIncrement` protocol configuration setting and corresponding Policy value starting from `Echidna` hardfork. |
 | MemPoolSize | `int` | `50000` | Size of the node's memory pool where transactions are stored before they are added to block. |
 | P2PNotaryRequestPayloadPoolSize | `int` | `1000` | Size of the node's P2P Notary request payloads memory pool where P2P Notary requests are stored before main or fallback transaction is completed and added to the chain.<br>This option is valid only if `P2PSigExtensions` are enabled. | Not supported by the C# node, thus may affect heterogeneous networks functionality. |
 | P2PSigExtensions | `bool` | `false` | Enables following additional Notary service related logic:<br>• Transaction attribute `NotaryAssisted`<br>• Network payload of the `P2PNotaryRequest` type<br>• Native `Notary` contract<br>• Notary node module | Not supported by the C# node, thus may affect heterogeneous networks functionality. |
@@ -437,6 +437,7 @@ during genesis block persist or at the moment of native contracts initialisation
 `Genesis` has the following structure:
 ```
 Genesis:
+  MaxValidUntilBlockIncrement: 5760
   Roles:
     NeoFSAlphabet:
       - 033238fa63bd08115ebf442d4af897eea2f6866e4c2001cd1f6e7656acdd91a5d3
@@ -451,6 +452,17 @@ Genesis:
     SystemFee: 100000000
 ```
 where:
+- `MaxValidUntilBlockIncrement` is an upper height increment limit for transaction's
+  ValidUntilBlock field value relative to the current blockchain height, exceeding
+  which a transaction will fail validation. This setting is used to initialize
+  `MaxValidUntilBlockIncrement` value of native Policy contract at `Echidna`
+  hardfork. If not set, then `ProtocolConfiguration`-level
+  `MaxValidUntilBlockIncrement` setting is used as the default value.
+
+  Note that this value is stored directly in the Policy contract storage, i.e. it
+  affects the chain's state, so it's important to keep it the same on all nodes
+  in frames of a single network.
+
 - `Roles` is a map from node roles that should be set at the moment of native
   RoleManagement contract initialisation to the list of hex-encoded public keys
   corresponding to this role. The set of valid roles includes:
diff --git a/examples/engine/go.mod b/examples/engine/go.mod
index caae27ff2..27fb424e9 100644
--- a/examples/engine/go.mod
+++ b/examples/engine/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/engine
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/engine/go.sum b/examples/engine/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/engine/go.sum
+++ b/examples/engine/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/examples/events/go.mod b/examples/events/go.mod
index 8d10c9db0..5ebfe63c4 100644
--- a/examples/events/go.mod
+++ b/examples/events/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/events
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/events/go.sum b/examples/events/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/events/go.sum
+++ b/examples/events/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/examples/iterator/go.mod b/examples/iterator/go.mod
index 7ce1ffd2e..0459480f4 100644
--- a/examples/iterator/go.mod
+++ b/examples/iterator/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/iterator
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/iterator/go.sum b/examples/iterator/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/iterator/go.sum
+++ b/examples/iterator/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/examples/nft-d/go.mod b/examples/nft-d/go.mod
index d4cc2346f..89efb1208 100644
--- a/examples/nft-d/go.mod
+++ b/examples/nft-d/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/nft
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/nft-d/go.sum b/examples/nft-d/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/nft-d/go.sum
+++ b/examples/nft-d/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/examples/nft-nd-nns/go.mod b/examples/nft-nd-nns/go.mod
index 9c52f6477..12a56faaf 100644
--- a/examples/nft-nd-nns/go.mod
+++ b/examples/nft-nd-nns/go.mod
@@ -4,7 +4,7 @@ go 1.23
 
 require (
 	github.com/nspcc-dev/neo-go v0.108.2-0.20250325170620-379142252b3c
-	github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+	github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
 	github.com/stretchr/testify v1.10.0
 )
 
diff --git a/examples/nft-nd-nns/go.sum b/examples/nft-nd-nns/go.sum
index cdf623c35..f1fa48af3 100644
--- a/examples/nft-nd-nns/go.sum
+++ b/examples/nft-nd-nns/go.sum
@@ -124,8 +124,8 @@ github.com/nspcc-dev/hrw/v2 v2.0.3 h1:GUIitIiDpAaQat9SZccp7XVAuwtqaM40+uZ9D8Q4A8
 github.com/nspcc-dev/hrw/v2 v2.0.3/go.mod h1:VWlFSGGPcHG1abuIDJb5u83tIF2EqOatC8Z7svZmgWQ=
 github.com/nspcc-dev/neo-go v0.108.2-0.20250325170620-379142252b3c h1:jbzNz1QwF+aq2KBXVykrcCgZqcOfUAht4yn/Y5XlyKs=
 github.com/nspcc-dev/neo-go v0.108.2-0.20250325170620-379142252b3c/go.mod h1:RNZWXV1fDAWKte46TusXi6ha81HJDl6cQcna6diXNZg=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
 github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.13 h1:q1ne/4d10NuHSqs+fA5XVVBRJ9jSap4cjrsxYzNvYZA=
 github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.13/go.mod h1:ycdDlPq8Wy+w5ePCmCfbHuEWQXbP7ZjBL5rj3QDiiM0=
 github.com/nspcc-dev/rfc6979 v0.2.3 h1:QNVykGZ3XjFwM/88rGfV3oj4rKNBy+nYI6jM7q19hDI=
diff --git a/examples/nft-nd/go.mod b/examples/nft-nd/go.mod
index 8a9dda7c6..59b20e1cf 100644
--- a/examples/nft-nd/go.mod
+++ b/examples/nft-nd/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/nft-nd
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/nft-nd/go.sum b/examples/nft-nd/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/nft-nd/go.sum
+++ b/examples/nft-nd/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/examples/oracle/go.mod b/examples/oracle/go.mod
index fddc4d2df..479f5a802 100644
--- a/examples/oracle/go.mod
+++ b/examples/oracle/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/oracle
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/oracle/go.sum b/examples/oracle/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/oracle/go.sum
+++ b/examples/oracle/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/examples/runtime/go.mod b/examples/runtime/go.mod
index 5a64a9a9c..122c00760 100644
--- a/examples/runtime/go.mod
+++ b/examples/runtime/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/runtime
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/runtime/go.sum b/examples/runtime/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/runtime/go.sum
+++ b/examples/runtime/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/examples/storage/go.mod b/examples/storage/go.mod
index c6549889b..8bc09c2dd 100644
--- a/examples/storage/go.mod
+++ b/examples/storage/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/storage
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/storage/go.sum b/examples/storage/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/storage/go.sum
+++ b/examples/storage/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/examples/timer/go.mod b/examples/timer/go.mod
index 80795b4e8..01d34097a 100644
--- a/examples/timer/go.mod
+++ b/examples/timer/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/timer
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/timer/go.sum b/examples/timer/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/timer/go.sum
+++ b/examples/timer/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/examples/token/go.mod b/examples/token/go.mod
index cbc4a0d1f..3380eda37 100644
--- a/examples/token/go.mod
+++ b/examples/token/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/token
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/token/go.sum b/examples/token/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/token/go.sum
+++ b/examples/token/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/examples/zkp/cubic_circuit/go.mod b/examples/zkp/cubic_circuit/go.mod
index 8e315e01e..3aa6401ce 100644
--- a/examples/zkp/cubic_circuit/go.mod
+++ b/examples/zkp/cubic_circuit/go.mod
@@ -31,7 +31,7 @@ require (
 	github.com/mmcloughlin/addchain v0.4.0 // indirect
 	github.com/mr-tron/base58 v1.2.0 // indirect
 	github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 // indirect
-	github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 // indirect
+	github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 // indirect
 	github.com/nspcc-dev/rfc6979 v0.2.1 // indirect
 	github.com/pierrec/lz4 v2.6.1+incompatible // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
diff --git a/examples/zkp/cubic_circuit/go.sum b/examples/zkp/cubic_circuit/go.sum
index 4dcef0084..f582cc181 100644
--- a/examples/zkp/cubic_circuit/go.sum
+++ b/examples/zkp/cubic_circuit/go.sum
@@ -88,8 +88,8 @@ github.com/nspcc-dev/hrw/v2 v2.0.1 h1:CxYUkBeJvNfMEn2lHhrV6FjY8pZPceSxXUtMVq0BUO
 github.com/nspcc-dev/hrw/v2 v2.0.1/go.mod h1:iZAs5hT2q47EGq6AZ0FjaUI6ggntOi7vrY4utfzk5VA=
 github.com/nspcc-dev/neo-go v0.106.3 h1:HEyhgkjQY+HfBzotMJ12xx2VuOUphkngZ4kEkjvXDtE=
 github.com/nspcc-dev/neo-go v0.106.3/go.mod h1:3vEwJ2ld12N7HRGCaH/l/7EwopplC/+8XdIdPDNmD/M=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
 github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4 h1:arN0Ypn+jawZpu1BND7TGRn44InAVIqKygndsx0y2no=
 github.com/nspcc-dev/neofs-api-go/v2 v2.14.1-0.20240305074711-35bc78d84dc4/go.mod h1:7Tm1NKEoUVVIUlkVwFrPh7GG5+Lmta2m7EGr4oVpBd8=
 github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.12 h1:mdxtlSU2I4oVZ/7AXTLKyz8uUPbDWikZw4DM8gvrddA=
diff --git a/examples/zkp/xor_compat/go.mod b/examples/zkp/xor_compat/go.mod
index 28f573733..88ef5f2a7 100644
--- a/examples/zkp/xor_compat/go.mod
+++ b/examples/zkp/xor_compat/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/zkp/xor
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/examples/zkp/xor_compat/go.sum b/examples/zkp/xor_compat/go.sum
index 5c185f70c..26d62a01c 100644
--- a/examples/zkp/xor_compat/go.sum
+++ b/examples/zkp/xor_compat/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/go.mod b/go.mod
index d3c19dbfd..b122cb99f 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/mr-tron/base58 v1.2.0
 	github.com/nspcc-dev/dbft v0.3.3-0.20250321140139-7462b47e4d2d
 	github.com/nspcc-dev/go-ordered-json v0.0.0-20250226190835-fb3f82b1f468
-	github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+	github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
 	github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.13
 	github.com/nspcc-dev/rfc6979 v0.2.3
 	github.com/pierrec/lz4 v2.6.1+incompatible
diff --git a/go.sum b/go.sum
index 01035d7de..c9e14df6a 100644
--- a/go.sum
+++ b/go.sum
@@ -150,8 +150,8 @@ github.com/nspcc-dev/go-ordered-json v0.0.0-20250226190835-fb3f82b1f468 h1:qOd9/
 github.com/nspcc-dev/go-ordered-json v0.0.0-20250226190835-fb3f82b1f468/go.mod h1:d3cUseu4Asxfo9/QA/w4TtGjM0AbC9ynyab+PfH+Bso=
 github.com/nspcc-dev/hrw/v2 v2.0.3 h1:GUIitIiDpAaQat9SZccp7XVAuwtqaM40+uZ9D8Q4A84=
 github.com/nspcc-dev/hrw/v2 v2.0.3/go.mod h1:VWlFSGGPcHG1abuIDJb5u83tIF2EqOatC8Z7svZmgWQ=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
 github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.13 h1:q1ne/4d10NuHSqs+fA5XVVBRJ9jSap4cjrsxYzNvYZA=
 github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.13/go.mod h1:ycdDlPq8Wy+w5ePCmCfbHuEWQXbP7ZjBL5rj3QDiiM0=
 github.com/nspcc-dev/rfc6979 v0.2.3 h1:QNVykGZ3XjFwM/88rGfV3oj4rKNBy+nYI6jM7q19hDI=
diff --git a/internal/contracts/oracle_contract/go.mod b/internal/contracts/oracle_contract/go.mod
index 7d08c7c6e..c786184ce 100644
--- a/internal/contracts/oracle_contract/go.mod
+++ b/internal/contracts/oracle_contract/go.mod
@@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/internal/examples/oracle
 
 go 1.23
 
-require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167
+require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83
diff --git a/internal/contracts/oracle_contract/go.sum b/internal/contracts/oracle_contract/go.sum
index 5c185f70c..26d62a01c 100644
--- a/internal/contracts/oracle_contract/go.sum
+++ b/internal/contracts/oracle_contract/go.sum
@@ -1,2 +1,2 @@
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167 h1:qakU0s/xyLhEgZI5Uinw1xNIAhn9z/ugznYCXJv6cWA=
-github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250409155823-eb2238a18167/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83 h1:/KgsA/svNkniRm6sZr3jdCBd45WILNzaOrjC1b+wn8g=
+github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20250410112417-d414d8a86b83/go.mod h1:3byneDNT60tiD8MSyGSyjpI1uVp9v+coySegJoQPF8c=
diff --git a/pkg/config/genesis_extensions.go b/pkg/config/genesis_extensions.go
index ca4eeb588..cc83e3311 100644
--- a/pkg/config/genesis_extensions.go
+++ b/pkg/config/genesis_extensions.go
@@ -11,6 +11,12 @@ import (
 // Genesis represents a set of genesis block settings including the extensions
 // enabled in the genesis block or during native contracts initialization.
 type Genesis struct {
+	// MaxValidUntilBlockIncrement is the upper increment size of blockchain
+	// height (in blocks) exceeding that a transaction should fail validation.
+	// It differs from Protocol level configuration in that this value is used
+	// starting from HFEchidna to initialize MaxValidUntilBlockIncrement value
+	// of native Policy contract.
+	MaxValidUntilBlockIncrement uint32
 	// Roles contains the set of roles that should be designated during native
 	// Designation contract initialization. It is NeoGo extension and must be
 	// disabled on the public Neo N3 networks.
@@ -33,8 +39,9 @@ type GenesisTransaction struct {
 type (
 	// genesisAux is an auxiliary structure for Genesis YAML marshalling.
 	genesisAux struct {
-		Roles       map[string]keys.PublicKeys `yaml:"Roles"`
-		Transaction *genesisTransactionAux     `yaml:"Transaction"`
+		MaxValidUntilBlockIncrement uint32                     `yaml:"MaxValidUntilBlockIncrement"`
+		Roles                       map[string]keys.PublicKeys `yaml:"Roles"`
+		Transaction                 *genesisTransactionAux     `yaml:"Transaction"`
 	}
 	// genesisTransactionAux is an auxiliary structure for GenesisTransaction YAML
 	// marshalling.
@@ -57,6 +64,7 @@ func (e Genesis) MarshalYAML() (any, error) {
 			SystemFee: e.Transaction.SystemFee,
 		}
 	}
+	aux.MaxValidUntilBlockIncrement = e.MaxValidUntilBlockIncrement
 	return aux, nil
 }
 
@@ -87,5 +95,7 @@ func (e *Genesis) UnmarshalYAML(unmarshal func(any) error) error {
 		}
 	}
 
+	e.MaxValidUntilBlockIncrement = aux.MaxValidUntilBlockIncrement
+
 	return nil
 }
diff --git a/pkg/config/protocol_config.go b/pkg/config/protocol_config.go
index 481252f89..d9bf0dd5a 100644
--- a/pkg/config/protocol_config.go
+++ b/pkg/config/protocol_config.go
@@ -231,6 +231,7 @@ func (p *ProtocolConfiguration) Equals(o *ProtocolConfiguration) bool {
 		p.StateRootInHeader != o.StateRootInHeader ||
 		p.StateSyncInterval != o.StateSyncInterval ||
 		p.TimePerBlock != o.TimePerBlock ||
+		p.Genesis.MaxValidUntilBlockIncrement != o.Genesis.MaxValidUntilBlockIncrement ||
 		p.ValidatorsCount != o.ValidatorsCount ||
 		p.VerifyTransactions != o.VerifyTransactions ||
 		!maps.Equal(p.CommitteeHistory, o.CommitteeHistory) ||
diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go
index 3f9528b72..68cf26c59 100644
--- a/pkg/core/blockchain.go
+++ b/pkg/core/blockchain.go
@@ -300,6 +300,11 @@ func NewBlockchain(s storage.Store, cfg config.Blockchain, log *zap.Logger) (*Bl
 		log.Info("MaxValidUntilBlockIncrement is not set or wrong, using default value",
 			zap.Uint32("MaxValidUntilBlockIncrement", cfg.MaxValidUntilBlockIncrement))
 	}
+	if cfg.Genesis.MaxValidUntilBlockIncrement == 0 {
+		cfg.Genesis.MaxValidUntilBlockIncrement = cfg.MaxValidUntilBlockIncrement
+		log.Info("Genesis MaxValidUntilBlockIncrement is not set or wrong, using default value",
+			zap.Uint32("Genesis MaxValidUntilBlockIncrement", cfg.Genesis.MaxValidUntilBlockIncrement))
+	}
 	if cfg.P2PStateExchangeExtensions && cfg.NeoFSStateSyncExtensions {
 		return nil, errors.New("P2PStateExchangeExtensions and NeoFSStateSyncExtensions cannot be enabled simultaneously")
 	}
@@ -1109,7 +1114,7 @@ func (bc *Blockchain) initializeNativeCache(blockHeight uint32, d *dao.Simple) e
 		if !bc.isHardforkEnabled(c.ActiveIn(), blockHeight) {
 			continue
 		}
-		err := c.InitializeCache(blockHeight, d)
+		err := c.InitializeCache(bc.isHardforkEnabled, blockHeight, d)
 		if err != nil {
 			return fmt.Errorf("failed to initialize cache for %s: %w", c.Metadata().Name, err)
 		}
@@ -2619,7 +2624,7 @@ func (bc *Blockchain) verifyAndPoolTx(t *transaction.Transaction, pool *mempool.
 
 	height := bc.BlockHeight()
 	isPartialTx := data != nil
-	if t.ValidUntilBlock <= height || !isPartialTx && t.ValidUntilBlock > height+bc.config.MaxValidUntilBlockIncrement {
+	if t.ValidUntilBlock <= height || !isPartialTx && t.ValidUntilBlock > height+bc.GetMaxValidUntilBlockIncrement() {
 		return fmt.Errorf("%w: ValidUntilBlock = %d, current height = %d", ErrTxExpired, t.ValidUntilBlock, height)
 	}
 	// Policying.
@@ -2953,6 +2958,18 @@ func (bc *Blockchain) GetFakeNextBlock(nextBlockHeight uint32) (*block.Block, er
 	return b, nil
 }
 
+// GetMaxValidUntilBlockIncrement returns the maximum value for upper increment size
+// of blockchain height (in blocks) exceeding that a transaction should fail
+// validation. This method performs access to native Policy storage hence Policy is
+// expected to be initialized by this moment.
+func (bc *Blockchain) GetMaxValidUntilBlockIncrement() uint32 {
+	var hf = config.HFEchidna
+	if bc.isHardforkEnabled(&hf, bc.BlockHeight()) {
+		return bc.contracts.Policy.GetMaxValidUntilBlockIncrementFromCache(bc.dao)
+	}
+	return bc.GetConfig().MaxValidUntilBlockIncrement
+}
+
 // Various witness verification errors.
 var (
 	ErrWitnessHashMismatch         = errors.New("witness hash mismatch")
diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go
index 50e727c43..dfe266aa4 100644
--- a/pkg/core/interop/context.go
+++ b/pkg/core/interop/context.go
@@ -189,13 +189,17 @@ type Contract interface {
 	// been deployed, but in-memory cached data were lost due to the node reset.
 	// It should be called each time after node restart iff the contract was
 	// deployed and no Initialize method was called.
-	InitializeCache(blockHeight uint32, d *dao.Simple) error
+	InitializeCache(isHardforkEnabled IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error
 	// Metadata returns generic native contract metadata.
 	Metadata() *ContractMD
 	OnPersist(*Context) error
 	PostPersist(*Context) error
 }
 
+// IsHardforkEnabled is a delegate denoting whether specific hardfork is enabled
+// at the given height.
+type IsHardforkEnabled func(hf *config.Hardfork, blockHeight uint32) bool
+
 // ContractMD represents a generic hardfork-independent native contract instance.
 type ContractMD struct {
 	ID   int32
diff --git a/pkg/core/native/crypto.go b/pkg/core/native/crypto.go
index 837ba23d5..8673cbacf 100644
--- a/pkg/core/native/crypto.go
+++ b/pkg/core/native/crypto.go
@@ -442,7 +442,7 @@ func (c *Crypto) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *int
 }
 
 // InitializeCache implements the Contract interface.
-func (c *Crypto) InitializeCache(blockHeight uint32, d *dao.Simple) error {
+func (c *Crypto) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
 	return nil
 }
 
diff --git a/pkg/core/native/designate.go b/pkg/core/native/designate.go
index 56ca19feb..00865c05f 100644
--- a/pkg/core/native/designate.go
+++ b/pkg/core/native/designate.go
@@ -163,7 +163,7 @@ func (s *Designate) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *
 
 // InitializeCache fills native Designate cache from DAO. It is called at non-zero height, thus
 // we can fetch the roles data right from the storage.
-func (s *Designate) InitializeCache(blockHeight uint32, d *dao.Simple) error {
+func (s *Designate) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
 	cache := &DesignationCache{}
 	roles := []noderoles.Role{noderoles.Oracle, noderoles.NeoFSAlphabet, noderoles.StateValidator, noderoles.P2PNotary}
 	for _, r := range roles {
diff --git a/pkg/core/native/ledger.go b/pkg/core/native/ledger.go
index a67222666..c44f58a63 100644
--- a/pkg/core/native/ledger.go
+++ b/pkg/core/native/ledger.go
@@ -86,7 +86,7 @@ func (l *Ledger) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *int
 }
 
 // InitializeCache implements the Contract interface.
-func (l *Ledger) InitializeCache(blockHeight uint32, d *dao.Simple) error {
+func (l *Ledger) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
 	return nil
 }
 
diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go
index 0bd36c138..994b4ef27 100644
--- a/pkg/core/native/management.go
+++ b/pkg/core/native/management.go
@@ -704,7 +704,7 @@ func (m *Management) OnPersist(ic *interop.Context) error {
 // InitializeCache initializes contract cache with the proper values from storage.
 // Cache initialization should be done apart from Initialize because Initialize is
 // called only when deploying native contracts.
-func (m *Management) InitializeCache(blockHeight uint32, d *dao.Simple) error {
+func (m *Management) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
 	cache := &ManagementCache{
 		contracts: make(map[util.Uint160]*state.Contract),
 		nep11:     make(map[util.Uint160]struct{}),
diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go
index d0a290498..7caa23494 100644
--- a/pkg/core/native/native_gas.go
+++ b/pkg/core/native/native_gas.go
@@ -104,7 +104,7 @@ func (g *GAS) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *intero
 }
 
 // InitializeCache implements the Contract interface.
-func (g *GAS) InitializeCache(blockHeight uint32, d *dao.Simple) error {
+func (g *GAS) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
 	return nil
 }
 
diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go
index 42e72cd05..793ef5a92 100644
--- a/pkg/core/native/native_neo.go
+++ b/pkg/core/native/native_neo.go
@@ -362,7 +362,7 @@ func (n *NEO) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *intero
 // Cache initialization should be done apart from Initialize because Initialize is
 // called only when deploying native contracts. InitializeCache implements the Contract
 // interface.
-func (n *NEO) InitializeCache(blockHeight uint32, d *dao.Simple) error {
+func (n *NEO) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
 	cache := &NeoCache{
 		gasPerVoteCache: make(map[string]big.Int),
 		votesChanged:    true,
diff --git a/pkg/core/native/notary.go b/pkg/core/native/notary.go
index e17dedfcc..803d1cbc7 100644
--- a/pkg/core/native/notary.go
+++ b/pkg/core/native/notary.go
@@ -143,7 +143,7 @@ func (n *Notary) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *int
 	return nil
 }
 
-func (n *Notary) InitializeCache(blockHeight uint32, d *dao.Simple) error {
+func (n *Notary) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
 	cache := &NotaryCache{
 		maxNotValidBeforeDelta: uint32(getIntWithKey(n.ID, d, maxNotValidBeforeDeltaKey)),
 	}
@@ -421,7 +421,7 @@ func (n *Notary) GetMaxNotValidBeforeDelta(dao *dao.Simple) uint32 {
 func (n *Notary) setMaxNotValidBeforeDelta(ic *interop.Context, args []stackitem.Item) stackitem.Item {
 	value := toUint32(args[0])
 	cfg := ic.Chain.GetConfig()
-	maxInc := cfg.MaxValidUntilBlockIncrement
+	maxInc := n.Policy.GetMaxValidUntilBlockIncrementInternal(ic)
 	if value > maxInc/2 || value < uint32(cfg.GetNumOfCNs(ic.BlockHeight())) {
 		panic(fmt.Errorf("MaxNotValidBeforeDelta cannot be more than %d or less than %d", maxInc/2, cfg.GetNumOfCNs(ic.BlockHeight())))
 	}
diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go
index 5c5a9dbe2..591f18d80 100644
--- a/pkg/core/native/oracle.go
+++ b/pkg/core/native/oracle.go
@@ -271,7 +271,7 @@ func (o *Oracle) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *int
 	return nil
 }
 
-func (o *Oracle) InitializeCache(blockHeight uint32, d *dao.Simple) error {
+func (o *Oracle) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
 	cache := &OracleCache{}
 	cache.requestPrice = getIntWithKey(o.ID, d, prefixRequestPrice)
 	d.SetCache(o.ID, cache)
diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go
index be2f9de50..f201f72ea 100644
--- a/pkg/core/native/policy.go
+++ b/pkg/core/native/policy.go
@@ -43,6 +43,9 @@ const (
 	maxStoragePrice = 10000000
 	// maxAttributeFee is the maximum allowed value for a transaction attribute fee.
 	maxAttributeFee = 10_00000000
+	// maxMaxVUBIncrement the maximum value for upper increment size of blockchain
+	// height (in blocks) exceeding that a transaction should fail validation.
+	maxMaxVUBIncrement = 86400
 
 	// blockedAccountPrefix is a prefix used to store blocked account.
 	blockedAccountPrefix = 15
@@ -58,6 +61,8 @@ var (
 	feePerByteKey = []byte{10}
 	// storagePriceKey is a key used to store storage price.
 	storagePriceKey = []byte{19}
+	// maxVUBIncrementKey is a key used to store maximum ValidUntilBlock increment.
+	maxVUBIncrementKey = []byte{22}
 )
 
 // Policy represents Policy native contract.
@@ -74,6 +79,7 @@ type PolicyCache struct {
 	feePerByte         int64
 	maxVerificationGas int64
 	storagePrice       uint32
+	maxVUBIncrement    uint32
 	attributeFee       map[transaction.AttrType]uint32
 	blockedAccounts    []util.Uint160
 }
@@ -157,6 +163,15 @@ func newPolicy(p2pSigExtensionsEnabled bool) *Policy {
 	md = newMethodAndPrice(p.unblockAccount, 1<<15, callflag.States)
 	p.AddMethod(md, desc)
 
+	desc = newDescriptor("getMaxValidUntilBlockIncrement", smartcontract.IntegerType)
+	md = newMethodAndPrice(p.getMaxValidUntilBlockIncrement, 1<<15, callflag.ReadStates, config.HFEchidna)
+	p.AddMethod(md, desc)
+
+	desc = newDescriptor("setMaxValidUntilBlockIncrement", smartcontract.VoidType,
+		manifest.NewParameter("value", smartcontract.IntegerType))
+	md = newMethodAndPrice(p.setMaxValidUntilBlockIncrement, 1<<15, callflag.States, config.HFEchidna)
+	p.AddMethod(md, desc)
+
 	return p
 }
 
@@ -167,34 +182,40 @@ func (p *Policy) Metadata() *interop.ContractMD {
 
 // Initialize initializes Policy native contract and implements the Contract interface.
 func (p *Policy) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error {
-	if hf != p.ActiveIn() {
-		return nil
+	if hf == p.ActiveIn() {
+		setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte)
+		setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor)
+		setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)
+
+		cache := &PolicyCache{
+			execFeeFactor:      defaultExecFeeFactor,
+			feePerByte:         defaultFeePerByte,
+			maxVerificationGas: defaultMaxVerificationGas,
+			storagePrice:       DefaultStoragePrice,
+			attributeFee:       map[transaction.AttrType]uint32{},
+			blockedAccounts:    make([]util.Uint160, 0),
+		}
+		if p.p2pSigExtensionsEnabled {
+			setIntWithKey(p.ID, ic.DAO, []byte{attributeFeePrefix, byte(transaction.NotaryAssistedT)}, defaultNotaryAssistedFee)
+			cache.attributeFee[transaction.NotaryAssistedT] = defaultNotaryAssistedFee
+		}
+		ic.DAO.SetCache(p.ID, cache)
 	}
 
-	setIntWithKey(p.ID, ic.DAO, feePerByteKey, defaultFeePerByte)
-	setIntWithKey(p.ID, ic.DAO, execFeeFactorKey, defaultExecFeeFactor)
-	setIntWithKey(p.ID, ic.DAO, storagePriceKey, DefaultStoragePrice)
-
-	cache := &PolicyCache{
-		execFeeFactor:      defaultExecFeeFactor,
-		feePerByte:         defaultFeePerByte,
-		maxVerificationGas: defaultMaxVerificationGas,
-		storagePrice:       DefaultStoragePrice,
-		attributeFee:       map[transaction.AttrType]uint32{},
-		blockedAccounts:    make([]util.Uint160, 0),
-	}
-	if p.p2pSigExtensionsEnabled {
-		setIntWithKey(p.ID, ic.DAO, []byte{attributeFeePrefix, byte(transaction.NotaryAssistedT)}, defaultNotaryAssistedFee)
-		cache.attributeFee[transaction.NotaryAssistedT] = defaultNotaryAssistedFee
+	if hf != nil && *hf == config.HFEchidna {
+		cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
+
+		maxVUBIncrement := ic.Chain.GetConfig().Genesis.MaxValidUntilBlockIncrement
+		setIntWithKey(p.ID, ic.DAO, maxVUBIncrementKey, int64(maxVUBIncrement))
+		cache.maxVUBIncrement = maxVUBIncrement
 	}
-	ic.DAO.SetCache(p.ID, cache)
 
 	return nil
 }
 
-func (p *Policy) InitializeCache(blockHeight uint32, d *dao.Simple) error {
+func (p *Policy) InitializeCache(isHardforkEnabled interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
 	cache := &PolicyCache{}
-	err := p.fillCacheFromDAO(cache, d)
+	err := p.fillCacheFromDAO(cache, d, isHardforkEnabled, blockHeight)
 	if err != nil {
 		return err
 	}
@@ -202,7 +223,7 @@ func (p *Policy) InitializeCache(blockHeight uint32, d *dao.Simple) error {
 	return nil
 }
 
-func (p *Policy) fillCacheFromDAO(cache *PolicyCache, d *dao.Simple) error {
+func (p *Policy) fillCacheFromDAO(cache *PolicyCache, d *dao.Simple, isHardforkEnabled interop.IsHardforkEnabled, blockHeight uint32) error {
 	cache.execFeeFactor = uint32(getIntWithKey(p.ID, d, execFeeFactorKey))
 	cache.feePerByte = getIntWithKey(p.ID, d, feePerByteKey)
 	cache.maxVerificationGas = defaultMaxVerificationGas
@@ -241,6 +262,12 @@ func (p *Policy) fillCacheFromDAO(cache *PolicyCache, d *dao.Simple) error {
 	if fErr != nil {
 		return fmt.Errorf("failed to initialize attribute fees: %w", fErr)
 	}
+
+	var echidna = config.HFEchidna
+	if isHardforkEnabled(&echidna, blockHeight) {
+		cache.maxVUBIncrement = uint32(getIntWithKey(p.ID, d, maxVUBIncrementKey))
+	}
+
 	return nil
 }
 
@@ -454,6 +481,42 @@ func (p *Policy) unblockAccount(ic *interop.Context, args []stackitem.Item) stac
 	return stackitem.NewBool(true)
 }
 
+func (p *Policy) getMaxValidUntilBlockIncrement(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
+	return stackitem.NewBigInteger(big.NewInt(int64(p.GetMaxValidUntilBlockIncrementFromCache(ic.DAO))))
+}
+
+// GetMaxValidUntilBlockIncrementInternal returns current MaxValidUntilBlockIncrement.
+// It respects Echidna enabling height.
+func (p *Policy) GetMaxValidUntilBlockIncrementInternal(ic *interop.Context) uint32 {
+	if ic.IsHardforkEnabled(config.HFEchidna) {
+		return p.GetMaxValidUntilBlockIncrementFromCache(ic.DAO)
+	}
+	return ic.Chain.GetConfig().MaxValidUntilBlockIncrement
+}
+
+// GetMaxValidUntilBlockIncrementFromCache returns current MaxValidUntilBlockIncrement.
+// It doesn't check neither Echidna enabling height nor cache initialization, so it's
+// the caller's duty to ensure that Echidna is enabled before a call to this method.
+func (p *Policy) GetMaxValidUntilBlockIncrementFromCache(d *dao.Simple) uint32 {
+	cache := d.GetROCache(p.ID).(*PolicyCache)
+	return cache.maxVUBIncrement
+}
+
+func (p *Policy) setMaxValidUntilBlockIncrement(ic *interop.Context, args []stackitem.Item) stackitem.Item {
+	value := toUint32(args[0])
+	if value <= 0 || maxMaxVUBIncrement < value {
+		panic(fmt.Errorf("MaxValidUntilBlockIncrement should be positive and not greater than %d, got %d", maxMaxVUBIncrement, value))
+	}
+	if !p.NEO.checkCommittee(ic) {
+		panic("invalid committee signature")
+	}
+	setIntWithKey(p.ID, ic.DAO, maxVUBIncrementKey, int64(value))
+	cache := ic.DAO.GetRWCache(p.ID).(*PolicyCache)
+	cache.maxVUBIncrement = value
+
+	return stackitem.Null{}
+}
+
 // CheckPolicy checks whether a transaction conforms to the current policy restrictions,
 // like not being signed by a blocked account or not exceeding the block-level system
 // fee limit.
diff --git a/pkg/core/native/std.go b/pkg/core/native/std.go
index 2d528a449..01b6883b3 100644
--- a/pkg/core/native/std.go
+++ b/pkg/core/native/std.go
@@ -471,7 +471,7 @@ func (s *Std) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *intero
 }
 
 // InitializeCache implements the Contract interface.
-func (s *Std) InitializeCache(blockHeight uint32, d *dao.Simple) error {
+func (s *Std) InitializeCache(_ interop.IsHardforkEnabled, blockHeight uint32, d *dao.Simple) error {
 	return nil
 }
 
diff --git a/pkg/interop/native/policy/policy.go b/pkg/interop/native/policy/policy.go
index 6e0fdc78e..33b84ce12 100644
--- a/pkg/interop/native/policy/policy.go
+++ b/pkg/interop/native/policy/policy.go
@@ -67,3 +67,15 @@ func BlockAccount(addr interop.Hash160) bool {
 func UnblockAccount(addr interop.Hash160) bool {
 	return neogointernal.CallWithToken(Hash, "unblockAccount", int(contract.States), addr).(bool)
 }
+
+// GetMaxValidUntilBlockIncrement represents `getMaxValidUntilBlockIncrement` method of Policy native contract.
+// Note that this method is available starting from [config.HFEchidna] hardfork.
+func GetMaxValidUntilBlockIncrement() int {
+	return neogointernal.CallWithToken(Hash, "getMaxValidUntilBlockIncrement", int(contract.ReadStates)).(int)
+}
+
+// SetMaxValidUntilBlockIncrement represents `setMaxValidUntilBlockIncrement` method of Policy native contract.
+// Note that this method is available starting from [config.HFEchidna] hardfork.
+func SetMaxValidUntilBlockIncrement(value int) {
+	neogointernal.CallWithTokenNoRet(Hash, "setMaxValidUntilBlockIncrement", int(contract.States), value)
+}
diff --git a/pkg/services/oracle/oracle.go b/pkg/services/oracle/oracle.go
index 5b1e18e43..19911ee9b 100644
--- a/pkg/services/oracle/oracle.go
+++ b/pkg/services/oracle/oracle.go
@@ -32,6 +32,7 @@ type (
 		GetMaxVerificationGAS() int64
 		GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*interop.Context, error)
 		GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
+		GetMaxValidUntilBlockIncrement() uint32
 	}
 
 	// Oracle represents an oracle module capable of talking
diff --git a/pkg/services/oracle/request.go b/pkg/services/oracle/request.go
index 9a9f16fbb..2abb3938e 100644
--- a/pkg/services/oracle/request.go
+++ b/pkg/services/oracle/request.go
@@ -192,7 +192,7 @@ func (o *Oracle) processRequest(priv *keys.PrivateKey, req request) error {
 	o.Log.Debug("oracle request processed", zap.String("url", req.Req.URL), zap.Int("code", int(resp.Code)), zap.String("result", string(resp.Result)))
 
 	currentHeight := o.Chain.BlockHeight()
-	vubInc := o.Chain.GetConfig().MaxValidUntilBlockIncrement
+	vubInc := o.Chain.GetMaxValidUntilBlockIncrement()
 	_, h, err := o.Chain.GetTransaction(req.Req.OriginalTxID)
 	if err != nil {
 		if !errors.Is(err, storage.ErrKeyNotFound) {
diff --git a/pkg/services/rpcsrv/server.go b/pkg/services/rpcsrv/server.go
index aa553a5c3..1decf6087 100644
--- a/pkg/services/rpcsrv/server.go
+++ b/pkg/services/rpcsrv/server.go
@@ -98,6 +98,7 @@ type (
 		GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
 		HeaderHeight() uint32
 		InitVerificationContext(ic *interop.Context, hash util.Uint160, witness *transaction.Witness) error
+		GetMaxValidUntilBlockIncrement() uint32
 		P2PSigExtensionsEnabled() bool
 		SubscribeForBlocks(ch chan *block.Block)
 		SubscribeForHeadersOfAddedBlocks(ch chan *block.Header)
@@ -891,7 +892,7 @@ func (s *Server) getVersion(_ params.Params) (any, *neorpc.Error) {
 			Network:                     cfg.Magic,
 			MillisecondsPerBlock:        int(cfg.TimePerBlock / time.Millisecond),
 			MaxTraceableBlocks:          cfg.MaxTraceableBlocks,
-			MaxValidUntilBlockIncrement: cfg.MaxValidUntilBlockIncrement,
+			MaxValidUntilBlockIncrement: s.chain.GetMaxValidUntilBlockIncrement(),
 			MaxTransactionsPerBlock:     cfg.MaxTransactionsPerBlock,
 			MemoryPoolMaxTransactions:   cfg.MemPoolSize,
 			ValidatorsCount:             byte(cfg.GetNumOfCNs(s.chain.BlockHeight())),


Test patch:
diff --git a/config/protocol.testnet.neofs.yml b/config/protocol.testnet.neofs.yml
index 417ed32c3..093c06360 100644
--- a/config/protocol.testnet.neofs.yml
+++ b/config/protocol.testnet.neofs.yml
@@ -4,6 +4,8 @@ ProtocolConfiguration:
   MaxValidUntilBlockIncrement: 8640
   InitialGASSupply: 52000000
   TimePerBlock: 1s
+  Genesis:
+    MaxValidUntilBlockIncrement: 8640
   MemPoolSize: 50000
   StandbyCommittee:
   - 0337f5f45e5be5aeae4a919d0787fcb743656560949061d5b8b05509b85ffbfd53
diff --git a/pkg/compiler/native_test.go b/pkg/compiler/native_test.go
index f95d66261..c9097af2d 100644
--- a/pkg/compiler/native_test.go
+++ b/pkg/compiler/native_test.go
@@ -199,6 +199,8 @@ func TestNativeHelpersCompile(t *testing.T) {
 		{"unblockAccount", []string{u160}},
 		{"getAttributeFee", []string{"1"}},
 		{"setAttributeFee", []string{"1", "123"}},
+		{"getMaxValidUntilBlockIncrement", nil},
+		{"setMaxValidUntilBlockIncrement", []string{"10"}},
 	})
 	runNativeTestCases(t, cs.Ledger.ContractMD, "ledger", []nativeTestCase{
 		{"currentHash", nil},
diff --git a/pkg/core/native/management_test.go b/pkg/core/native/management_test.go
index 73859507b..6450bd7d7 100644
--- a/pkg/core/native/management_test.go
+++ b/pkg/core/native/management_test.go
@@ -3,6 +3,7 @@ package native
 import (
 	"testing"
 
+	"github.com/nspcc-dev/neo-go/pkg/config"
 	"github.com/nspcc-dev/neo-go/pkg/core/dao"
 	"github.com/nspcc-dev/neo-go/pkg/core/interop"
 	"github.com/nspcc-dev/neo-go/pkg/core/state"
@@ -83,13 +84,13 @@ func TestManagement_Initialize(t *testing.T) {
 	t.Run("good", func(t *testing.T) {
 		d := dao.NewSimple(storage.NewMemoryStore(), false)
 		mgmt := newManagement()
-		require.NoError(t, mgmt.InitializeCache(0, d))
+		require.NoError(t, mgmt.InitializeCache(func(hf *config.Hardfork, blockHeight uint32) bool { return false }, 0, d))
 	})
 	t.Run("invalid contract state", func(t *testing.T) {
 		d := dao.NewSimple(storage.NewMemoryStore(), false)
 		mgmt := newManagement()
 		d.PutStorageItem(mgmt.ID, []byte{PrefixContract}, state.StorageItem{0xFF})
-		require.Error(t, mgmt.InitializeCache(0, d))
+		require.Error(t, mgmt.InitializeCache(func(hf *config.Hardfork, blockHeight uint32) bool { return false }, 0, d))
 	})
 }
 
@@ -100,7 +101,7 @@ func TestManagement_GetNEP17Contracts(t *testing.T) {
 	err := mgmt.Initialize(&interop.Context{DAO: d}, nil, nil)
 	require.NoError(t, err)
 	require.NoError(t, mgmt.Policy.Initialize(&interop.Context{DAO: d}, nil, nil))
-	err = mgmt.InitializeCache(0, d)
+	err = mgmt.InitializeCache(func(hf *config.Hardfork, blockHeight uint32) bool { return false }, 0, d)
 	require.NoError(t, err)
 
 	require.Empty(t, mgmt.GetNEP17Contracts(d))
diff --git a/pkg/core/native/native_test/common_test.go b/pkg/core/native/native_test/common_test.go
index bc452b4e2..28044c13d 100644
--- a/pkg/core/native/native_test/common_test.go
+++ b/pkg/core/native/native_test/common_test.go
@@ -57,8 +57,12 @@ func testGetSet(t *testing.T, c *neotest.ContractInvoker, name string, defaultVa
 	}
 
 	t.Run("set, success", func(t *testing.T) {
+		v := defaultValue + 1
+		if v > maxValue { // sanitize default value that is equal to max value.
+			v = defaultValue - 1
+		}
 		// Set and get in the same block.
-		txSet := committeeInvoker.PrepareInvoke(t, setName, defaultValue+1)
+		txSet := committeeInvoker.PrepareInvoke(t, setName, v)
 		txGet := randomInvoker.PrepareInvoke(t, getName)
 		c.AddNewBlock(t, txSet, txGet)
 		c.CheckHalt(t, txSet.Hash(), stackitem.Null{})
@@ -68,19 +72,19 @@ func testGetSet(t *testing.T, c *neotest.ContractInvoker, name string, defaultVa
 			// GasPerBlock is set on the next block
 			c.CheckHalt(t, txGet.Hash(), stackitem.Make(defaultValue))
 			c.AddNewBlock(t)
-			randomInvoker.Invoke(t, defaultValue+1, getName)
+			randomInvoker.Invoke(t, v, getName)
 		case "ExecFeeFactor":
 			// ExecFeeFactor was risen, so the second transaction will fail because
 			// of gas limit exceeding (its fees are out-of-date).
 			c.CheckFault(t, txGet.Hash(), "gas limit exceeded")
 			// Set in a separate block.
-			committeeInvoker.Invoke(t, stackitem.Null{}, setName, defaultValue+1)
+			committeeInvoker.Invoke(t, stackitem.Null{}, setName, v)
 			// Get in the next block.
-			randomInvoker.Invoke(t, defaultValue+1, getName)
+			randomInvoker.Invoke(t, v, getName)
 		default:
-			c.CheckHalt(t, txGet.Hash(), stackitem.Make(defaultValue+1))
+			c.CheckHalt(t, txGet.Hash(), stackitem.Make(v))
 			// Get in the next block.
-			randomInvoker.Invoke(t, defaultValue+1, getName)
+			randomInvoker.Invoke(t, v, getName)
 		}
 	})
 }
diff --git a/pkg/core/native/native_test/management_test.go b/pkg/core/native/native_test/management_test.go
index fda8fb3e4..f63d8f575 100644
--- a/pkg/core/native/native_test/management_test.go
+++ b/pkg/core/native/native_test/management_test.go
@@ -60,6 +60,7 @@ var (
 		nativenames.StdLib:      `{"id":-2,"hash":"0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0A=","checksum":2681632925},"manifest":{"name":"StdLib","abi":{"methods":[{"name":"atoi","offset":0,"parameters":[{"name":"value","type":"String"}],"returntype":"Integer","safe":true},{"name":"atoi","offset":7,"parameters":[{"name":"value","type":"String"},{"name":"base","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"base58CheckDecode","offset":14,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base58CheckEncode","offset":21,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"base58Decode","offset":28,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base58Encode","offset":35,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"base64Decode","offset":42,"parameters":[{"name":"s","type":"String"}],"returntype":"ByteArray","safe":true},{"name":"base64Encode","offset":49,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"String","safe":true},{"name":"base64UrlDecode","offset":56,"parameters":[{"name":"s","type":"String"}],"returntype":"String","safe":true},{"name":"base64UrlEncode","offset":63,"parameters":[{"name":"data","type":"String"}],"returntype":"String","safe":true},{"name":"deserialize","offset":70,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"Any","safe":true},{"name":"itoa","offset":77,"parameters":[{"name":"value","type":"Integer"}],"returntype":"String","safe":true},{"name":"itoa","offset":84,"parameters":[{"name":"value","type":"Integer"},{"name":"base","type":"Integer"}],"returntype":"String","safe":true},{"name":"jsonDeserialize","offset":91,"parameters":[{"name":"json","type":"ByteArray"}],"returntype":"Any","safe":true},{"name":"jsonSerialize","offset":98,"parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","safe":true},{"name":"memoryCompare","offset":105,"parameters":[{"name":"str1","type":"ByteArray"},{"name":"str2","type":"ByteArray"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":112,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":119,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"memorySearch","offset":126,"parameters":[{"name":"mem","type":"ByteArray"},{"name":"value","type":"ByteArray"},{"name":"start","type":"Integer"},{"name":"backward","type":"Boolean"}],"returntype":"Integer","safe":true},{"name":"serialize","offset":133,"parameters":[{"name":"item","type":"Any"}],"returntype":"ByteArray","safe":true},{"name":"strLen","offset":140,"parameters":[{"name":"str","type":"String"}],"returntype":"Integer","safe":true},{"name":"stringSplit","offset":147,"parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"}],"returntype":"Array","safe":true},{"name":"stringSplit","offset":154,"parameters":[{"name":"str","type":"String"},{"name":"separator","type":"String"},{"name":"removeEmptyEntries","type":"Boolean"}],"returntype":"Array","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
 		nativenames.CryptoLib:   `{"id":-3,"hash":"0x726cb6e0cd8628a1350a611384688911ab75f51b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":174904780},"manifest":{"name":"CryptoLib","abi":{"methods":[{"name":"bls12381Add","offset":0,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Deserialize","offset":7,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Equal","offset":14,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"y","type":"InteropInterface"}],"returntype":"Boolean","safe":true},{"name":"bls12381Mul","offset":21,"parameters":[{"name":"x","type":"InteropInterface"},{"name":"mul","type":"ByteArray"},{"name":"neg","type":"Boolean"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Pairing","offset":28,"parameters":[{"name":"g1","type":"InteropInterface"},{"name":"g2","type":"InteropInterface"}],"returntype":"InteropInterface","safe":true},{"name":"bls12381Serialize","offset":35,"parameters":[{"name":"g","type":"InteropInterface"}],"returntype":"ByteArray","safe":true},{"name":"keccak256","offset":42,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"murmur32","offset":49,"parameters":[{"name":"data","type":"ByteArray"},{"name":"seed","type":"Integer"}],"returntype":"ByteArray","safe":true},{"name":"recoverSecp256K1","offset":56,"parameters":[{"name":"messageHash","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"ripemd160","offset":63,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"sha256","offset":70,"parameters":[{"name":"data","type":"ByteArray"}],"returntype":"ByteArray","safe":true},{"name":"verifyWithECDsa","offset":77,"parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"},{"name":"curveHash","type":"Integer"}],"returntype":"Boolean","safe":true},{"name":"verifyWithEd25519","offset":84,"parameters":[{"name":"message","type":"ByteArray"},{"name":"pubkey","type":"ByteArray"},{"name":"signature","type":"ByteArray"}],"returntype":"Boolean","safe":true}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
 		nativenames.Neo:         `{"id":-5,"hash":"0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dA","checksum":1991619121},"manifest":{"name":"NeoToken","abi":{"methods":[{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":7,"parameters":[],"returntype":"Integer","safe":true},{"name":"getAccountState","offset":14,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Array","safe":true},{"name":"getAllCandidates","offset":21,"parameters":[],"returntype":"InteropInterface","safe":true},{"name":"getCandidateVote","offset":28,"parameters":[{"name":"pubKey","type":"PublicKey"}],"returntype":"Integer","safe":true},{"name":"getCandidates","offset":35,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommittee","offset":42,"parameters":[],"returntype":"Array","safe":true},{"name":"getCommitteeAddress","offset":49,"parameters":[],"returntype":"Hash160","safe":true},{"name":"getGasPerBlock","offset":56,"parameters":[],"returntype":"Integer","safe":true},{"name":"getNextBlockValidators","offset":63,"parameters":[],"returntype":"Array","safe":true},{"name":"getRegisterPrice","offset":70,"parameters":[],"returntype":"Integer","safe":true},{"name":"onNEP17Payment","offset":77,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"registerCandidate","offset":84,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"setGasPerBlock","offset":91,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setRegisterPrice","offset":98,"parameters":[{"name":"registerPrice","type":"Integer"}],"returntype":"Void","safe":false},{"name":"symbol","offset":105,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":112,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":119,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false},{"name":"unclaimedGas","offset":126,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"unregisterCandidate","offset":133,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean","safe":false},{"name":"vote","offset":140,"parameters":[{"name":"account","type":"Hash160"},{"name":"voteTo","type":"PublicKey"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]},{"name":"CandidateStateChanged","parameters":[{"name":"pubkey","type":"PublicKey"},{"name":"registered","type":"Boolean"},{"name":"votes","type":"Integer"}]},{"name":"Vote","parameters":[{"name":"account","type":"Hash160"},{"name":"from","type":"PublicKey"},{"name":"to","type":"PublicKey"},{"name":"amount","type":"Integer"}]},{"name":"CommitteeChanged","parameters":[{"name":"old","type":"Array"},{"name":"new","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-17","NEP-27"],"trusts":[],"extra":null},"updatecounter":0}`,
+		nativenames.Policy:      `{"id":-7,"hash":"0xcc5e4edd9f5f8dba8bb65734541df7a1c081c67b","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQBBBGvd7Z0AQQRr3e2dAEEEa93tnQA==","checksum":174904780},"manifest":{"name":"PolicyContract","abi":{"methods":[{"name":"blockAccount","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false},{"name":"getAttributeFee","offset":7,"parameters":[{"name":"attributeType","type":"Integer"}],"returntype":"Integer","safe":true},{"name":"getExecFeeFactor","offset":14,"parameters":[],"returntype":"Integer","safe":true},{"name":"getFeePerByte","offset":21,"parameters":[],"returntype":"Integer","safe":true},{"name":"getMaxValidUntilBlockIncrement","offset":28,"parameters":[],"returntype":"Integer","safe":true},{"name":"getStoragePrice","offset":35,"parameters":[],"returntype":"Integer","safe":true},{"name":"isBlocked","offset":42,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":true},{"name":"setAttributeFee","offset":49,"parameters":[{"name":"attributeType","type":"Integer"},{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setExecFeeFactor","offset":56,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setFeePerByte","offset":63,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setMaxValidUntilBlockIncrement","offset":70,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"setStoragePrice","offset":77,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Void","safe":false},{"name":"unblockAccount","offset":84,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean","safe":false}],"events":[]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
 		nativenames.Designation: `{"id":-8,"hash":"0x49cf4e5378ffcd4dec034fd98a174c5491e395e2","nef":{"magic":860243278,"compiler":"neo-core-v3.0","source":"","tokens":[],"script":"EEEa93tnQBBBGvd7Z0A=","checksum":983638438},"manifest":{"name":"RoleManagement","abi":{"methods":[{"name":"designateAsRole","offset":0,"parameters":[{"name":"role","type":"Integer"},{"name":"nodes","type":"Array"}],"returntype":"Void","safe":false},{"name":"getDesignatedByRole","offset":7,"parameters":[{"name":"role","type":"Integer"},{"name":"index","type":"Integer"}],"returntype":"Array","safe":true}],"events":[{"name":"Designation","parameters":[{"name":"Role","type":"Integer"},{"name":"BlockIndex","type":"Integer"},{"name":"Old","type":"Array"},{"name":"New","type":"Array"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null},"updatecounter":0}`,
 	}
 )
diff --git a/pkg/core/native/native_test/policy_test.go b/pkg/core/native/native_test/policy_test.go
index bb724ed67..ba2fca95f 100644
--- a/pkg/core/native/native_test/policy_test.go
+++ b/pkg/core/native/native_test/policy_test.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/nspcc-dev/neo-go/pkg/config"
 	"github.com/nspcc-dev/neo-go/pkg/core/interop"
 	"github.com/nspcc-dev/neo-go/pkg/core/native"
 	"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
@@ -45,6 +46,36 @@ func TestPolicy_StoragePriceCache(t *testing.T) {
 	testGetSetCache(t, newPolicyClient(t), "StoragePrice", native.DefaultStoragePrice)
 }
 
+func TestPolicy_MaxVUBIncrement(t *testing.T) {
+	c := newCustomNativeClient(t, nativenames.Policy, func(cfg *config.Blockchain) {
+		cfg.Hardforks = map[string]uint32{
+			config.HFEchidna.String(): 3,
+		}
+	})
+	committeeInvoker := c.WithSigners(c.Committee)
+	name := "MaxValidUntilBlockIncrement"
+
+	t.Run("set, before Echidna", func(t *testing.T) {
+		committeeInvoker.InvokeFail(t, "method not found: setMaxValidUntilBlockIncrement/1", "set"+name, 123)
+	})
+	t.Run("get, before Echidna", func(t *testing.T) {
+		committeeInvoker.InvokeFail(t, "method not found: getMaxValidUntilBlockIncrement/0", "get"+name)
+	})
+
+	c.AddNewBlock(t) // enable Echidna.
+	testGetSet(t, c, name, int64(c.Chain.GetConfig().Genesis.MaxValidUntilBlockIncrement), 1, 86400)
+}
+
+func TestPolicy_MaxVUBIncrementCache(t *testing.T) {
+	c := newCustomNativeClient(t, nativenames.Policy, func(cfg *config.Blockchain) {
+		cfg.Hardforks = map[string]uint32{
+			config.HFEchidna.String(): 1,
+		}
+	})
+	c.AddNewBlock(t) // enable Echidna.
+	testGetSetCache(t, c, "MaxValidUntilBlockIncrement", int64(c.Chain.GetConfig().Genesis.MaxValidUntilBlockIncrement))
+}
+
 func TestPolicy_AttributeFee(t *testing.T) {
 	c := newPolicyClient(t)
 	getName := "getAttributeFee"
diff --git a/pkg/services/rpcsrv/server_test.go b/pkg/services/rpcsrv/server_test.go
index af796c54b..0e6e456ff 100644
--- a/pkg/services/rpcsrv/server_test.go
+++ b/pkg/services/rpcsrv/server_test.go
@@ -122,7 +122,7 @@ const (
 	// not yet deployed to the testing basic chain.
 	invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
 	// block20StateRootLE is an LE stateroot of block #20 of basic testing chain.
-	block20StateRootLE = "2feff04551f085c7ba64b43faca8155e7cf172d69c8fcf5af4195faeb2748e2f"
+	block20StateRootLE = "672da91ae5489fa065bfdd7d0b8f918ac735389446ea5f5cad4392713e19791f"
 )
 
 var (