Skip to content

Commit 624f193

Browse files
committedAug 10, 2023
vm: move JNumbers parsing precision under HFBasilisk
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
1 parent d90608d commit 624f193

File tree

5 files changed

+64
-25
lines changed

5 files changed

+64
-25
lines changed
 

‎docs/node-configuration.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ protocol-related settings described in the table below.
343343
| --- | --- | --- | --- | --- |
344344
| CommitteeHistory | map[uint32]uint32 | none | Number of committee members after the given height, for example `{0: 1, 20: 4}` sets up a chain with one committee member since the genesis and then changes the setting to 4 committee members at the height of 20. `StandbyCommittee` committee setting must have the number of keys equal or exceeding the highest value in this option. Blocks numbers where the change happens must be divisible by the old and by the new values simultaneously. If not set, committee size is derived from the `StandbyCommittee` setting and never changes. |
345345
| GarbageCollectionPeriod | `uint32` | 10000 | Controls MPT garbage collection interval (in blocks) for configurations with `RemoveUntraceableBlocks` enabled and `KeepOnlyLatestState` disabled. In this mode the node stores a number of MPT trees (corresponding to `MaxTraceableBlocks` and `StateSyncInterval`), but the DB needs to be clean from old entries from time to time. Doing it too often will cause too much processing overhead, doing it too rarely will leave more useless data in the DB. This setting is deprecated in favor of the same setting in the ApplicationConfiguration and will be removed in future node versions. If both settings are used, ApplicationConfiguration is prioritized over this one. |
346-
| Hardforks | `map[string]uint32` | [] | The set of incompatible changes that affect node behaviour starting from the specified height. The default value is an empty set which should be interpreted as "each known hard-fork is applied from the zero blockchain height". The list of valid hard-fork names:<br>• `Aspidochelone` represents hard-fork introduced in [#2469](https://github.com/nspcc-dev/neo-go/pull/2469) (ported from the [reference](https://github.com/neo-project/neo/pull/2712)). It adjusts the prices of `System.Contract.CreateStandardAccount` and `System.Contract.CreateMultisigAccount` interops so that the resulting prices are in accordance with `sha256` method of native `CryptoLib` contract. It also includes [#2519](https://github.com/nspcc-dev/neo-go/pull/2519) (ported from the [reference](https://github.com/neo-project/neo/pull/2749)) that adjusts the price of `System.Runtime.GetRandom` interop and fixes its vulnerability. A special NeoGo-specific change is included as well for ContractManagement's update/deploy call flags behaviour to be compatible with pre-0.99.0 behaviour that was changed because of the [3.2.0 protocol change](https://github.com/neo-project/neo/pull/2653).<br>• `Basilisk` represents hard-fork introduced in [#3056](https://github.com/nspcc-dev/neo-go/pull/3056) (ported from the [reference](https://github.com/neo-project/neo/pull/2881)). It enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. |
346+
| Hardforks | `map[string]uint32` | [] | The set of incompatible changes that affect node behaviour starting from the specified height. The default value is an empty set which should be interpreted as "each known hard-fork is applied from the zero blockchain height". The list of valid hard-fork names:<br>• `Aspidochelone` represents hard-fork introduced in [#2469](https://github.com/nspcc-dev/neo-go/pull/2469) (ported from the [reference](https://github.com/neo-project/neo/pull/2712)). It adjusts the prices of `System.Contract.CreateStandardAccount` and `System.Contract.CreateMultisigAccount` interops so that the resulting prices are in accordance with `sha256` method of native `CryptoLib` contract. It also includes [#2519](https://github.com/nspcc-dev/neo-go/pull/2519) (ported from the [reference](https://github.com/neo-project/neo/pull/2749)) that adjusts the price of `System.Runtime.GetRandom` interop and fixes its vulnerability. A special NeoGo-specific change is included as well for ContractManagement's update/deploy call flags behaviour to be compatible with pre-0.99.0 behaviour that was changed because of the [3.2.0 protocol change](https://github.com/neo-project/neo/pull/2653).<br>• `Basilisk` represents hard-fork introduced in [#3056](https://github.com/nspcc-dev/neo-go/pull/3056) (ported from the [reference](https://github.com/neo-project/neo/pull/2881)). It enables strict smart contract script check against a set of JMP instructions and against method boundaries enabled on contract deploy or update. It also includes [#3080](https://github.com/nspcc-dev/neo-go/pull/3080) (ported from the [reference](https://github.com/neo-project/neo/pull/2883)) that increases `stackitem.Integer` JSON parsing precision up to the maximum value supported by the NeoVM.|
347347
| KeepOnlyLatestState | `bool` | `false` | Specifies if MPT should only store the latest state (or a set of latest states, see `P2PStateExcangeExtensions` section for details). If true, DB size will be smaller, but older roots won't be accessible. This value should remain the same for the same database. | This setting is deprecated in favor of the same setting in the ApplicationConfiguration and will be removed in future node versions. If both settings are used, setting any of them to true enables the function. |
348348
| Magic | `uint32` | `0` | Magic number which uniquely identifies Neo network. |
349349
| MaxBlockSize | `uint32` | `262144` | Maximum block size in bytes. |

‎pkg/config/hardfork.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ const (
1111
// https://github.com/neo-project/neo/pull/2749).
1212
HFAspidochelone Hardfork = 1 << iota // Aspidochelone
1313
// HFBasilisk represents hard-fork introduced in #3056 (ported from
14-
// https://github.com/neo-project/neo/pull/2881).
14+
// https://github.com/neo-project/neo/pull/2881) and #3080 (ported from
15+
// https://github.com/neo-project/neo/pull/2883).
1516
HFBasilisk // Basilisk
1617
)
1718

‎pkg/core/native/std.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
"github.com/mr-tron/base58"
12+
"github.com/nspcc-dev/neo-go/pkg/config"
1213
"github.com/nspcc-dev/neo-go/pkg/core/dao"
1314
"github.com/nspcc-dev/neo-go/pkg/core/interop"
1415
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
@@ -199,13 +200,13 @@ func (s *Std) jsonSerialize(_ *interop.Context, args []stackitem.Item) stackitem
199200
return stackitem.NewByteArray(data)
200201
}
201202

202-
func (s *Std) jsonDeserialize(_ *interop.Context, args []stackitem.Item) stackitem.Item {
203+
func (s *Std) jsonDeserialize(ic *interop.Context, args []stackitem.Item) stackitem.Item {
203204
data, err := args[0].TryBytes()
204205
if err != nil {
205206
panic(err)
206207
}
207208

208-
item, err := stackitem.FromJSON(data, stackitem.MaxDeserialized)
209+
item, err := stackitem.FromJSON(data, stackitem.MaxDeserialized, ic.IsHardforkEnabled(config.HFBasilisk))
209210
if err != nil {
210211
panic(err)
211212
}

‎pkg/vm/stackitem/json.go

+24-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ type decoder struct {
1818

1919
count int
2020
depth int
21+
// bestIntPrecision denotes whether maximum allowed integer precision should
22+
// be used to parse big.Int items. If false, then default NeoC# value will be
23+
// used which doesn't allow to precisely parse big values. This behaviour is
24+
// managed by the config.HFBasilisk.
25+
bestIntPrecision bool
2126
}
2227

2328
// MaxAllowedInteger is the maximum integer allowed to be encoded.
@@ -26,10 +31,16 @@ const MaxAllowedInteger = 2<<53 - 1
2631
// MaxJSONDepth is the maximum allowed nesting level of an encoded/decoded JSON.
2732
const MaxJSONDepth = 10
2833

29-
// MaxIntegerPrec is the maximum precision allowed for big.Integer parsing.
30-
// It allows to properly parse integer numbers that our 256-bit VM is able to
31-
// handle.
32-
const MaxIntegerPrec = 1<<8 + 1
34+
const (
35+
// MaxIntegerPrec is the maximum precision allowed for big.Integer parsing.
36+
// It allows to properly parse integer numbers that our 256-bit VM is able to
37+
// handle.
38+
MaxIntegerPrec = 1<<8 + 1
39+
// CompatIntegerPrec is the maximum precision allowed for big.Integer parsing
40+
// by the C# node before the Basilisk hardfork. It doesn't allow to precisely
41+
// parse big numbers, see the https://github.com/neo-project/neo/issues/2879.
42+
CompatIntegerPrec = 53
43+
)
3344

3445
// ErrInvalidValue is returned when an item value doesn't fit some constraints
3546
// during serialization or deserialization.
@@ -166,10 +177,11 @@ func itemToJSONString(it Item) ([]byte, error) {
166177
// null -> Null
167178
// array -> Array
168179
// map -> Map, keys are UTF-8
169-
func FromJSON(data []byte, maxCount int) (Item, error) {
180+
func FromJSON(data []byte, maxCount int, bestIntPrecision bool) (Item, error) {
170181
d := decoder{
171-
Decoder: *json.NewDecoder(bytes.NewReader(data)),
172-
count: maxCount,
182+
Decoder: *json.NewDecoder(bytes.NewReader(data)),
183+
count: maxCount,
184+
bestIntPrecision: bestIntPrecision,
173185
}
174186
d.UseNumber()
175187
if item, err := d.decode(); err != nil {
@@ -226,7 +238,11 @@ func (d *decoder) decode() (Item, error) {
226238
if isScientific {
227239
// As a special case numbers like 2.8e+22 are allowed (SetString rejects them).
228240
// That's the way how C# code works.
229-
f, _, err := big.ParseFloat(ts, 10, MaxIntegerPrec, big.ToNearestEven)
241+
var prec uint = CompatIntegerPrec
242+
if d.bestIntPrecision {
243+
prec = MaxIntegerPrec
244+
}
245+
f, _, err := big.ParseFloat(ts, 10, prec, big.ToNearestEven)
230246
if err != nil {
231247
return nil, fmt.Errorf("%w (malformed exp value for int)", ErrInvalidValue)
232248
}

‎pkg/vm/stackitem/json_test.go

+34-13
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func getTestDecodeFunc(js string, expected ...interface{}) func(t *testing.T) {
1414

1515
func getTestDecodeEncodeFunc(js string, needEncode bool, expected ...interface{}) func(t *testing.T) {
1616
return func(t *testing.T) {
17-
actual, err := FromJSON([]byte(js), 20)
17+
actual, err := FromJSON([]byte(js), 20, true)
1818
if expected[0] == nil {
1919
require.Error(t, err)
2020
return
@@ -59,10 +59,10 @@ func TestFromToJSON(t *testing.T) {
5959
NewArray([]Item{NewArray([]Item{}), NewArray([]Item{NewMap(), Null{}})})))
6060
t.Run("ManyElements", func(t *testing.T) {
6161
js := `[1, 2, 3]` // 3 elements + array itself
62-
_, err := FromJSON([]byte(js), 4)
62+
_, err := FromJSON([]byte(js), 4, true)
6363
require.NoError(t, err)
6464

65-
_, err = FromJSON([]byte(js), 3)
65+
_, err = FromJSON([]byte(js), 3, true)
6666
require.ErrorIs(t, err, errTooBigElements)
6767
})
6868
})
@@ -82,10 +82,10 @@ func TestFromToJSON(t *testing.T) {
8282

8383
t.Run("ManyElements", func(t *testing.T) {
8484
js := `{"a":1,"b":3}` // 4 elements + map itself
85-
_, err := FromJSON([]byte(js), 5)
85+
_, err := FromJSON([]byte(js), 5, true)
8686
require.NoError(t, err)
8787

88-
_, err = FromJSON([]byte(js), 4)
88+
_, err = FromJSON([]byte(js), 4, true)
8989
require.ErrorIs(t, err, errTooBigElements)
9090
})
9191
})
@@ -133,17 +133,38 @@ func TestFromToJSON(t *testing.T) {
133133
// TestFromJSON_CompatBigInt ensures that maximum BigInt parsing precision matches
134134
// the C# one, ref. https://github.com/neo-project/neo/issues/2879.
135135
func TestFromJSON_CompatBigInt(t *testing.T) {
136-
tcs := map[string]string{
137-
`9.05e+28`: "90500000000000000000000000000",
138-
`1.871e+21`: "1871000000000000000000",
139-
`3.0366e+32`: "303660000000000000000000000000000",
140-
`1e+30`: "1000000000000000000000000000000",
136+
tcs := map[string]struct {
137+
bestPrec string
138+
compatPrec string
139+
}{
140+
`9.05e+28`: {
141+
bestPrec: "90500000000000000000000000000",
142+
compatPrec: "90499999999999993918259200000",
143+
},
144+
`1.871e+21`: {
145+
bestPrec: "1871000000000000000000",
146+
compatPrec: "1871000000000000000000",
147+
},
148+
`3.0366e+32`: {
149+
bestPrec: "303660000000000000000000000000000",
150+
compatPrec: "303660000000000004445016810323968",
151+
},
152+
`1e+30`: {
153+
bestPrec: "1000000000000000000000000000000",
154+
compatPrec: "1000000000000000019884624838656",
155+
},
141156
}
142157
for in, expected := range tcs {
143158
t.Run(in, func(t *testing.T) {
144-
actual, err := FromJSON([]byte(in), 5)
159+
// Best precision.
160+
actual, err := FromJSON([]byte(in), 5, true)
161+
require.NoError(t, err)
162+
require.Equal(t, expected.bestPrec, actual.Value().(*big.Int).String())
163+
164+
// Compatible precision.
165+
actual, err = FromJSON([]byte(in), 5, false)
145166
require.NoError(t, err)
146-
require.Equal(t, expected, actual.Value().(*big.Int).String())
167+
require.Equal(t, expected.compatPrec, actual.Value().(*big.Int).String())
147168
})
148169
}
149170
}
@@ -156,7 +177,7 @@ func testToJSON(t *testing.T, expectedErr error, item Item) {
156177
}
157178
require.NoError(t, err)
158179

159-
actual, err := FromJSON(data, 1024)
180+
actual, err := FromJSON(data, 1024, true)
160181
require.NoError(t, err)
161182
require.Equal(t, item, actual)
162183
}

0 commit comments

Comments
 (0)