From a762240611457bf262699713bf3db71004631139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 17 Oct 2024 20:26:58 +0200 Subject: [PATCH] Extend fixed-point numbers module (#19336) ## Description - Deprecated `fixed_point32` - Added `uq32_32` to replace it ## Test plan - New tests --- ## Release notes Check each box that your changes affect. If none of the boxes relate to your changes, release notes aren't required. For each box you select, include information after the relevant heading that describes the impact of your changes that a user might notice and any actions they must take to implement updates. - [ ] Protocol: - [ ] Nodes (Validators and Full nodes): - [ ] Indexer: - [ ] JSON-RPC: - [ ] GraphQL: - [X] CLI: Move `fixed_point32` has been deprecated for a new `uq32_32` module - [ ] Rust SDK: - [ ] REST API: --------- Co-authored-by: Todd Nowacki --- .../move-stdlib/sources/fixed_point32.move | 2 +- .../packages/move-stdlib/sources/uq32_32.move | 160 +++++++++++ .../move-stdlib/tests/fixedpoint32_tests.move | 2 +- .../move-stdlib/tests/uq32_32_tests.move | 257 ++++++++++++++++++ .../packages_compiled/move-stdlib | Bin 14348 -> 15569 bytes crates/sui-framework/published_api.txt | 48 ++++ ..._populated_genesis_snapshot_matches-2.snap | 28 +- 7 files changed, 481 insertions(+), 16 deletions(-) create mode 100644 crates/sui-framework/packages/move-stdlib/sources/uq32_32.move create mode 100644 crates/sui-framework/packages/move-stdlib/tests/uq32_32_tests.move diff --git a/crates/sui-framework/packages/move-stdlib/sources/fixed_point32.move b/crates/sui-framework/packages/move-stdlib/sources/fixed_point32.move index 557400e813d40..9b1a2fe577010 100644 --- a/crates/sui-framework/packages/move-stdlib/sources/fixed_point32.move +++ b/crates/sui-framework/packages/move-stdlib/sources/fixed_point32.move @@ -3,7 +3,7 @@ /// Defines a fixed-point numeric type with a 32-bit integer part and /// a 32-bit fractional part. - +#[deprecated(note = b"Use `std::uq32_32` instead. If you need to convert from a `FixedPoint32` to a `UQ32_32`, you can use the `std::fixed_point32::get_raw_value` with `std::uq32_32::from_raw_value`.")] module std::fixed_point32; /// Define a fixed-point numeric type with 32 fractional bits. diff --git a/crates/sui-framework/packages/move-stdlib/sources/uq32_32.move b/crates/sui-framework/packages/move-stdlib/sources/uq32_32.move new file mode 100644 index 0000000000000..cad6381324090 --- /dev/null +++ b/crates/sui-framework/packages/move-stdlib/sources/uq32_32.move @@ -0,0 +1,160 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +/// Defines an unsigned, fixed-point numeric type with a 32-bit integer part and a 32-bit fractional +/// part. The notation `uq32_32` and `UQ32_32` is based on +/// [Q notation](https://en.wikipedia.org/wiki/Q_(number_format)). `q` indicates it a fixed-point +/// number. The `u` prefix indicates it is unsigned. The `32_32` suffix indicates the number of +/// bits, where the first number indicates the number of bits in the integer part, and the second +/// the number of bits in the fractional part--in this case 32 bits for each. +module std::uq32_32; + +#[error] +const EDenominator: vector = b"Quotient specified with a zero denominator"; + +#[error] +const EQuotientTooSmall: vector = + b"Quotient specified is too small, and is outside of the supported range"; + +#[error] +const EQuotientTooLarge: vector = + b"Quotient specified is too large, and is outside of the supported range"; + +#[error] +const EOverflow: vector = b"Overflow from an arithmetic operation"; + +#[error] +const EDivisionByZero: vector = b"Division by zero"; + +/// A fixed-point numeric type with 32 integer bits and 32 fractional bits, represented by an +/// underlying 64 bit value. This is a binary representation, so decimal values may not be exactly +/// representable, but it provides more than 9 decimal digits of precision both before and after the +/// decimal point (18 digits total). +public struct UQ32_32(u64) has copy, drop, store; + +/// Create a fixed-point value from a quotient specified by its numerator and denominator. +/// `from_quotient` and `from_int` should be preferred over using `from_raw`. +/// Unless the denominator is a power of two, fractions can not be represented accurately, +/// so be careful about rounding errors. +/// Aborts if the denominator is zero. +/// Aborts if the input is non-zero but so small that it will be represented as zero, e.g. smaller +/// than 2^{-32}. +/// Aborts if the input is too large, e.g. larger than or equal to 2^32. +public fun from_quotient(numerator: u64, denominator: u64): UQ32_32 { + assert!(denominator != 0, EDenominator); + + // Scale the numerator to have 64 fractional bits and the denominator to have 32 fractional + // bits, so that the quotient will have 32 fractional bits. + let scaled_numerator = numerator as u128 << 64; + let scaled_denominator = denominator as u128 << 32; + let quotient = scaled_numerator / scaled_denominator; + + // The quotient can only be zero if the numerator is also zero. + assert!(quotient != 0 || numerator == 0, EQuotientTooSmall); + + // Return the quotient as a fixed-point number. We first need to check whether the cast + // can succeed. + assert!(quotient <= std::u64::max_value!() as u128, EQuotientTooLarge); + UQ32_32(quotient as u64) +} + +/// Create a fixed-point value from an integer. +/// `from_int` and `from_quotient` should be preferred over using `from_raw`. +public fun from_int(integer: u32): UQ32_32 { + UQ32_32((integer as u64) << 32) +} + +/// Add two fixed-point numbers, `a + b`. +/// Aborts if the sum overflows. +public fun add(a: UQ32_32, b: UQ32_32): UQ32_32 { + let sum = a.0 as u128 + (b.0 as u128); + assert!(sum <= std::u64::max_value!() as u128, EOverflow); + UQ32_32(sum as u64) +} + +/// Subtract two fixed-point numbers, `a - b`. +/// Aborts if `a < b`. +public fun sub(a: UQ32_32, b: UQ32_32): UQ32_32 { + assert!(a.0 >= b.0, EOverflow); + UQ32_32(a.0 - b.0) +} + +/// Multiply two fixed-point numbers, truncating any fractional part of the product. +/// Aborts if the product overflows. +public fun mul(a: UQ32_32, b: UQ32_32): UQ32_32 { + UQ32_32(int_mul(a.0, b)) +} + +/// Divide two fixed-point numbers, truncating any fractional part of the quotient. +/// Aborts if the divisor is zero. +/// Aborts if the quotient overflows. +public fun div(a: UQ32_32, b: UQ32_32): UQ32_32 { + UQ32_32(int_div(a.0, b)) +} + +/// Convert a fixed-point number to an integer, truncating any fractional part. +public fun to_int(a: UQ32_32): u32 { + (a.0 >> 32) as u32 +} + +/// Multiply a `u64` integer by a fixed-point number, truncating any fractional part of the product. +/// Aborts if the product overflows. +public fun int_mul(val: u64, multiplier: UQ32_32): u64 { + // The product of two 64 bit values has 128 bits, so perform the + // multiplication with u128 types and keep the full 128 bit product + // to avoid losing accuracy. + let unscaled_product = val as u128 * (multiplier.0 as u128); + // The unscaled product has 32 fractional bits (from the multiplier) + // so rescale it by shifting away the low bits. + let product = unscaled_product >> 32; + // Check whether the value is too large. + assert!(product <= std::u64::max_value!() as u128, EOverflow); + product as u64 +} + +/// Divide a `u64` integer by a fixed-point number, truncating any fractional part of the quotient. +/// Aborts if the divisor is zero. +/// Aborts if the quotient overflows. +public fun int_div(val: u64, divisor: UQ32_32): u64 { + // Check for division by zero. + assert!(divisor.0 != 0, EDivisionByZero); + // First convert to 128 bits and then shift left to + // add 32 fractional zero bits to the dividend. + let scaled_value = val as u128 << 32; + let quotient = scaled_value / (divisor.0 as u128); + // Check whether the value is too large. + assert!(quotient <= std::u64::max_value!() as u128, EOverflow); + quotient as u64 +} + +/// Less than or equal to. Returns `true` if and only if `a <= a`. +public fun le(a: UQ32_32, b: UQ32_32): bool { + a.0 <= b.0 +} + +/// Less than. Returns `true` if and only if `a < b`. +public fun lt(a: UQ32_32, b: UQ32_32): bool { + a.0 < b.0 +} + +/// Greater than or equal to. Returns `true` if and only if `a >= b`. +public fun ge(a: UQ32_32, b: UQ32_32): bool { + a.0 >= b.0 +} + +/// Greater than. Returns `true` if and only if `a > b`. +public fun gt(a: UQ32_32, b: UQ32_32): bool { + a.0 > b.0 +} + +/// Accessor for the raw u64 value. Can be paired with `from_raw` to perform less common operations +/// on the raw values directly. +public fun to_raw(a: UQ32_32): u64 { + a.0 +} + +/// Accessor for the raw u64 value. Can be paired with `to_raw` to perform less common operations +/// on the raw values directly. +public fun from_raw(raw_value: u64): UQ32_32 { + UQ32_32(raw_value) +} diff --git a/crates/sui-framework/packages/move-stdlib/tests/fixedpoint32_tests.move b/crates/sui-framework/packages/move-stdlib/tests/fixedpoint32_tests.move index 28071e935742c..f3a42e898ed11 100644 --- a/crates/sui-framework/packages/move-stdlib/tests/fixedpoint32_tests.move +++ b/crates/sui-framework/packages/move-stdlib/tests/fixedpoint32_tests.move @@ -3,7 +3,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -#[test_only] +#[test_only, allow(deprecated_usage)] module std::fixed_point32_tests; use std::fixed_point32; diff --git a/crates/sui-framework/packages/move-stdlib/tests/uq32_32_tests.move b/crates/sui-framework/packages/move-stdlib/tests/uq32_32_tests.move new file mode 100644 index 0000000000000..78c1d9f543c33 --- /dev/null +++ b/crates/sui-framework/packages/move-stdlib/tests/uq32_32_tests.move @@ -0,0 +1,257 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module std::uq32_32_tests; + +use std::unit_test::assert_eq; +use std::uq32_32::{ + Self, + add, + sub, + mul, + div, + int_div, + int_mul, + from_int, + from_quotient, + from_raw, + to_raw, +}; + +#[test] +fun from_quotient_zero() { + let x = from_quotient(0, 1); + assert_eq!(x.to_raw(), 0); +} + +#[test] +fun from_quotient_max_numerator_denominator() { + // Test creating a 1.0 fraction from the maximum u64 value. + let f = from_quotient(std::u64::max_value!(), std::u64::max_value!()); + let one = f.to_raw(); + assert_eq!(one, 1 << 32); // 0x1.00000000 +} + +#[test] +#[expected_failure(abort_code = uq32_32::EDenominator)] +fun from_quotient_div_zero() { + // A denominator of zero should cause an arithmetic error. + from_quotient(2, 0); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EQuotientTooLarge)] +fun from_quotient_ratio_too_large() { + // The maximum value is 2^32 - 1. Check that anything larger aborts + // with an overflow. + from_quotient(1 << 32, 1); // 2^32 +} + +#[test] +#[expected_failure(abort_code = uq32_32::EQuotientTooSmall)] +fun from_quotient_ratio_too_small() { + // The minimum non-zero value is 2^-32. Check that anything smaller + // aborts. + from_quotient(1, (1 << 32) + 1); // 1/(2^32 + 1) +} + +#[test] +fun test_from_int() { + assert_eq!(from_int(0).to_raw(), 0); + assert_eq!(from_int(1).to_raw(), 0x1_0000_0000); + assert_eq!(from_int(std::u32::max_value!()).to_raw(), std::u32::max_value!() as u64 << 32); +} + +#[test] +fun test_add() { + let a = from_quotient(3, 4); + assert!(a.add(from_int(0)) == a); + + let c = a.add(from_int(1)); + assert!(from_quotient(7, 4) == c); + + let b = from_quotient(1, 4); + let c = a.add(b); + assert!(from_int(1) == c); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EOverflow)] +fun test_add_overflow() { + let a = from_int(1 << 31); + let b = from_int(1 << 31); + let _ = a.add(b); +} + +#[test] +fun test_sub() { + let a = from_int(5); + assert_eq!(a.sub(from_int(0)), a); + + let b = from_int(4); + let c = a.sub(b); + assert_eq!(from_int(1), c); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EOverflow)] +fun test_sub_underflow() { + let a = from_int(3); + let b = from_int(5); + a.sub(b); +} + +#[test] +fun test_mul() { + let a = from_quotient(3, 4); + assert!(a.mul(from_int(0)) == from_int(0)); + assert!(a.mul(from_int(1)) == a); + + let b = from_quotient(3, 2); + let c = a.mul(b); + let expected = from_quotient(9, 8); + assert_eq!(c, expected); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EOverflow)] +fun test_mul_overflow() { + let a = from_int(1 << 16); + let b = from_int(1 << 16); + let _ = a.mul(b); +} + +#[test] +fun test_div() { + let a = from_quotient(3, 4); + assert!(a.div(from_int(1)) == a); + + let b = from_int(8); + let c = a.div(b); + let expected = from_quotient(3, 32); + assert_eq!(c, expected); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EDivisionByZero)] +fun test_div_by_zero() { + let a = from_int(7); + let b = from_int(0); + let _ = a.div(b); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EOverflow)] +fun test_div_overflow() { + let a = from_int(1 << 31); + let b = from_quotient(1, 2); + let _ = a.div(b); +} + +#[test] +fun exact_int_div() { + let f = from_quotient(3, 4); // 0.75 + let twelve = int_div(9, f); // 9 / 0.75 + assert_eq!(twelve, 12); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EDivisionByZero)] +fun int_div_by_zero() { + let f = from_raw(0); // 0 + // Dividing by zero should cause an arithmetic error. + int_div(1, f); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EOverflow)] +fun int_div_overflow_small_divisor() { + let f = from_raw(1); // 0x0.00000001 + // Divide 2^32 by the minimum fractional value. This should overflow. + int_div(1 << 32, f); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EOverflow)] +fun int_div_overflow_large_numerator() { + let f = from_quotient(1, 2); // 0.5 + // Divide the maximum u64 value by 0.5. This should overflow. + int_div(std::u64::max_value!(), f); +} + +#[test] +fun exact_int_mul() { + let f = from_quotient(3, 4); // 0.75 + let nine = int_mul(12, f); // 12 * 0.75 + assert_eq!(nine, 9); +} + +#[test] +fun int_mul_truncates() { + let f = from_quotient(1, 3); // 0.333... + let not_three = int_mul(9, copy f); // 9 * 0.333... + // multiply_u64 does NOT round -- it truncates -- so values that + // are not perfectly representable in binary may be off by one. + assert_eq!(not_three, 2); + + // Try again with a fraction slightly larger than 1/3. + let f = from_raw(f.to_raw() + 1); + let three = int_mul(9, f); + assert_eq!(three, 3); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EOverflow)] +fun int_mul_overflow_small_multiplier() { + let f = from_quotient(3, 2); // 1.5 + // Multiply the maximum u64 value by 1.5. This should overflow. + int_mul(std::u64::max_value!(), f); +} + +#[test] +#[expected_failure(abort_code = uq32_32::EOverflow)] +fun int_mul_overflow_large_multiplier() { + let f = from_raw(std::u64::max_value!()); + // Multiply 2^32 + 1 by the maximum fixed-point value. This should overflow. + int_mul((1 << 32) + 1, f); +} + +#[test] +fun test_comparison() { + let a = from_quotient(5, 2); + let b = from_quotient(5, 3); + let c = from_quotient(5, 2); + + assert!(b.le(a)); + assert!(b.lt(a)); + assert!(c.le(a)); + assert_eq!(c, a); + assert!(a.ge(b)); + assert!(a.gt(b)); + assert!(from_int(0).le(a)); +} + +#[random_test] +fun test_raw(raw: u64) { + assert_eq!(from_raw(raw).to_raw(), raw); +} + +#[random_test] +fun test_int_roundtrip(c: u32) { + assert_eq!(from_int(c).to_int(), c); +} + +#[random_test] +fun test_mul_rand(n: u16, d: u16, c: u16) { + if (d == 0) return; + let q = from_quotient(n as u64, d as u64); + assert_eq!(int_mul(c as u64, q), q.mul(from_int(c as u32)).to_int() as u64); +} + +#[random_test] +fun test_div_rand(n: u16, d: u16, c: u16) { + if (d == 0) return; + let q = from_quotient(n as u64, d as u64); + assert_eq!(int_div(c as u64, q), from_int(c as u32).div(q).to_int() as u64); +} diff --git a/crates/sui-framework/packages_compiled/move-stdlib b/crates/sui-framework/packages_compiled/move-stdlib index 8526133aab6a42f2b55fcc6b66147182fd964640..191c7bd74e552887d4ce54cbdffbabd73852fb75 100644 GIT binary patch delta 1127 zcmaJ>%}x_h6h8OdJOBMfP(mcRL5c~I6bdzQN0iD!q9N+W4H@VZCTZuEnJI`1>jSuw zC-4n4E_?u=02e04@B}V=0;A_n+fZ0Ai*L^T?sva)?&+Dn@1C8o8=u*C-v8!&y#3oV z0l;N|h=Q9>jO|$~QuaBM=3n*`-5TZ)wzFE}ezVD&;gQ{n zRc9xR(tH*i_EZ`MQ97Vw{}}fJ+)phM?2tNpcY1AX2iQ(IMWU;cWuEkQ%~WZdqJGmk zt9O{?IX$b*_4B#LMi8k^82PD+UA(urJ z{kQyBlh^sTwPeiydVAODH=Z2@ajUJ4U0kS)VyPQWMM^)Wn|FdV+>@#s#C{s8$i-5z zu@N4H2`wI;yrQPKST2nixqFf|3XtWwo*Zl0Wi$kXXHFP~WfBWZ0PpU)MX0Z=S96BP z>MOFE6Q`f>{tJ@>LOt8(wSU8Bp5sREQ> zvR+^sZ=SBtPxULJXp~J0OxVoMqCTsK*q+S9nRY=qhL?$+YSG2UEfNbYwldCRWs4Z& z5@QpiuQ$}J$mK1xXzfgTP^k?$r IoLATW1DAN*ZU6uP delta 15 Wcmcau*;BwMw2|==^X3^GmL>o)wFRL7 diff --git a/crates/sui-framework/published_api.txt b/crates/sui-framework/published_api.txt index e6d0acfbb736b..4d9f1f6f33262 100644 --- a/crates/sui-framework/published_api.txt +++ b/crates/sui-framework/published_api.txt @@ -4282,6 +4282,54 @@ sha2_256 sha3_256 public fun 0x1::hash +UQ32_32 + public struct + 0x1::uq32_32 +from_quotient + public fun + 0x1::uq32_32 +from_int + public fun + 0x1::uq32_32 +add + public fun + 0x1::uq32_32 +sub + public fun + 0x1::uq32_32 +mul + public fun + 0x1::uq32_32 +div + public fun + 0x1::uq32_32 +to_int + public fun + 0x1::uq32_32 +int_mul + public fun + 0x1::uq32_32 +int_div + public fun + 0x1::uq32_32 +le + public fun + 0x1::uq32_32 +lt + public fun + 0x1::uq32_32 +ge + public fun + 0x1::uq32_32 +gt + public fun + 0x1::uq32_32 +to_raw + public fun + 0x1::uq32_32 +from_raw + public fun + 0x1::uq32_32 empty public fun 0x1::vector diff --git a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap index 05eaa6318c556..0293be0f3f153 100644 --- a/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap +++ b/crates/sui-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap @@ -240,13 +240,13 @@ validators: next_epoch_worker_address: ~ extra_fields: id: - id: "0xcc91d06982135046b4cfa9bbd4f7f206cdaf3f9fb27c782a7137b3bef08892ed" + id: "0x5e216be58ccd6edc40d76fd21795437e3c135e1f4bcd3cf3ed07995d25456060" size: 0 voting_power: 10000 - operation_cap_id: "0xfc07728a8857acbf12a2d5684de4f98920fad0952f784426eaafdda79b94ee20" + operation_cap_id: "0x1b99479eef6dbadd1755f24adcdb074855f39e29941b53908a379c7e7091af81" gas_price: 1000 staking_pool: - id: "0xdb3034b1953243443f3cbc912d09de43d8e836803c018ba14245d7fa0a4c383d" + id: "0xb38eb98b4e99b71bcab647fbabf992c3cc19129c9528b767c6c303a986fa2c42" activation_epoch: 0 deactivation_epoch: ~ sui_balance: 20000000000000000 @@ -254,14 +254,14 @@ validators: value: 0 pool_token_balance: 20000000000000000 exchange_rates: - id: "0x8d37fa87257f45904f0ef1424ead1cc8e1ea23cf00ff16ce9a63f2b77df6231f" + id: "0x4dc309edc1409b3f194ac1758aef9372ce2eb471ced67d80e5b0f4bba96d28d5" size: 1 pending_stake: 0 pending_total_sui_withdraw: 0 pending_pool_token_withdraw: 0 extra_fields: id: - id: "0x9f7ae3a49a73bbfdf1721536325cb5699b3cdb3174e42e461bb4eed6cb45aeb0" + id: "0xe4f5ca1ff5bd26453325097073da8f2ee7bacf92155c291c62ae119c1cb5f832" size: 0 commission_rate: 200 next_epoch_stake: 20000000000000000 @@ -269,27 +269,27 @@ validators: next_epoch_commission_rate: 200 extra_fields: id: - id: "0xcf38a9cd46d8931bb0e06800c7dd818023842c6eed78c74f4d3663e3b256e9fe" + id: "0x2324e3ec47e27aa137997a532567f5afa2a9645a921ac016aea5a3eaaab9ec68" size: 0 pending_active_validators: contents: - id: "0x5757ed8fb231e16f163db4f3a31aa660fccf4189e5c8fe535588abfbabde730f" + id: "0xa07bbd43dc12c089a98ce90dec1e59dd1634de032a7506e063e2ebc6b8790567" size: 0 pending_removals: [] staking_pool_mappings: - id: "0x34c34655c2c9a2f2b708915189cf536c6f0e129655ef50d7157f461d74487741" + id: "0x617c029b1c4c382d85289275adffe619f9bd6d0d54eae1a6bd20854ae86dba51" size: 1 inactive_validators: - id: "0x15c6d3a2bd25a1232b8114376e0421dbdd5acf92f50bbd091588e7a43d5276da" + id: "0x8738f13db0421d917e79ee20e0b50f810922b60d44873fa9f5f4b07287d94a0e" size: 0 validator_candidates: - id: "0xb2c748bd9e0c7cc676667f7ea2909b6edc224d8dc1d1797626b926409a5f04ef" + id: "0x419362971d83e7403641c492fae230e83eed8e09662b83e6758808839c0fe0d9" size: 0 at_risk_validators: contents: [] extra_fields: id: - id: "0xfcd0b94a9048c129e2e0e690bc3afe2a9e35377d64821c33076e7c14969b9143" + id: "0x624540ee67bc94279d5a4eab2b2eca6bbf10d9b6dfe05deb1ba9d4f7530a494c" size: 0 storage_fund: total_object_storage_rebates: @@ -306,7 +306,7 @@ parameters: validator_low_stake_grace_period: 7 extra_fields: id: - id: "0x34e56a96336b73d4983cfdb95c4392f7214fc632973e1529f63ee7caf6ed0458" + id: "0x9ae4daddf54c4d97b85626f3e4441c7093a7c632ac1bbc3b521d71160ebbca65" size: 0 reference_gas_price: 1000 validator_report_records: @@ -320,7 +320,7 @@ stake_subsidy: stake_subsidy_decrease_rate: 1000 extra_fields: id: - id: "0xbfd2b299dab597fcf4e004a252274bd8262e18521b63168c49eca2b1839dc207" + id: "0x065bccd62b599e64be7189489a86c0c5758ab648a090d7e3ef765650d5018dda" size: 0 safe_mode: false safe_mode_storage_rewards: @@ -332,5 +332,5 @@ safe_mode_non_refundable_storage_fee: 0 epoch_start_timestamp_ms: 10 extra_fields: id: - id: "0x7f447c7cb0761fe5d2350b00849a94f887b78b2b3a5afd75f97092be04e1bf27" + id: "0x602bc63f6783de4a51993457c225e56158f59fbd07474b385a3a3ce45bfde7c6" size: 0