Дневник по разработке смарт-контрактов на Rust (2) Написание модульных тестов для смарт-контрактов на Rust
Дневник по разработке смарт-контрактов на Rust (3) Развертывание смарт-контрактов на Rust, вызов функций и использование Explorer
Дневник развития смарт-контрактов на Rust (4) Целочисленный переполнение смарт-контракта Rust
Rust смарт-контракты养成日记(5)重入攻击
Rust смарт-контракты养成日记(6)拒绝服务攻击
1. Проблема точности операций с плавающей запятой
В отличие от Solidity, Rust нативно поддерживает операции с плавающей точкой. Однако операции с плавающей точкой имеют неизбежные проблемы с точностью, поэтому не рекомендуется использовать их в смарт-контрактах, особенно при обработке коэффициентов или ставок, связанных с важными экономическими/финансовыми решениями.
Rust следует стандарту IEEE 754 для представления чисел с плавающей запятой. Тип двойной точности f64 представляет собой двоичное научное представление в компьютере.
Некоторые дробные числа могут быть точно представлены с помощью конечного количества двоичных знаков, например, 0.8125 можно записать как 0.1101. Но дробные числа, такие как 0.7, будут иметь бесконечное циклическое двоичное представление и не могут быть точно представлены с помощью конечных плавающих чисел, что приводит к проблеме "округления".
В примере распределения 0.7 токена NEAR среди 10 пользователей на блокчейне NEAR:
Результаты выполнения показывают, что фактическое значение amount равно 0.69999999999999995559, result_0 равно 0.06999999999999999, что не соответствует ожидаемому 0.07.
Чтобы решить эту проблему, можно использовать фиксированную точку. В NEAR обычно 1 токен NEAR представляется как 10^24 yoctoNEAR. Изменённый код:
Таким образом, можно получить точный результат: 0.7 NEAR / 10 = 0.07 NEAR.
2. Проблема точности вычислений с целыми числами в Rust
Хотя целочисленные операции могут решить проблемы точности с плавающей запятой в некоторых сценариях, целочисленные вычисления также имеют проблемы с точностью.
2.1 Порядок операций
При равном уровне сложности, изменение порядка операций может повлиять на результат:
let result_0 = a.checked_mul(c).unwrap().checked_div(b). unwrap();
let result_1 = a.checked_div(b).unwrap().checked_mul(c). unwrap();
assert_eq!(result_0,result_1);
}
Результаты показывают result_0 = 2, result_1 = 0.
Причина в том, что целочисленное деление отбрасывает точность, меньшую, чем делитель. При вычислении result_1, (a / b) сначала теряет точность и становится 0; в то время как result_0 сначала вычисляет (a * c), что позволяет избежать потери точности.
#[test]
FN record_offset_test() {
let mut offset: u128 = 0;
для i в 1..7 {
смещение = distribute(10_000_000_000_000_000_000_000_000, offset);
}
}
!
3.4 Использование библиотеки Rust Crate rust-decimal
Библиотека подходит для финансовых расчетов с высокоточной десятичной арифметикой без ошибок округления.
3.5 Учитывайте механизм округления
В дизайне смарт-контрактов округление обычно следует принципу "мне это выгодно": если округление вниз мне выгодно, то округляем вниз, если округление вверх мне выгодно, то округляем вверх, редко используется округление до ближайшего.
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
17 Лайков
Награда
17
8
Поделиться
комментарий
0/400
LostBetweenChains
· 07-25 17:53
Не могу справиться с целыми числами, на каких контрактах играть?
Посмотреть ОригиналОтветить0
WagmiWarrior
· 07-25 11:45
Уже вижу 7-ю статью, так тонко, так тонко.
Посмотреть ОригиналОтветить0
MetaMisfit
· 07-25 01:13
Rust эти смарт-контракты слишком много проблем, не так ли?
Посмотреть ОригиналОтветить0
DuckFluff
· 07-22 22:44
Ну и дела, с плавающей точкой опять проблемы~
Посмотреть ОригиналОтветить0
MaticHoleFiller
· 07-22 22:44
Те, кто сталкивался с проблемами точности, приходите и делитесь своим опытом!
Посмотреть ОригиналОтветить0
MetadataExplorer
· 07-22 22:44
Целые числа часто игнорируются, но они довольно важны.
Посмотреть ОригиналОтветить0
MEVHunter
· 07-22 22:29
точность является приманкой mev... держите ваши флоаты плотно или получите rekt, сер
Проблема точности вычислений целых чисел в смарт-контрактах Rust и решения этой проблемы
Rust смарт-контракты养成日记(7):целочисленная арифметическая точность
Обзор прошлых периодов:
1. Проблема точности операций с плавающей запятой
В отличие от Solidity, Rust нативно поддерживает операции с плавающей точкой. Однако операции с плавающей точкой имеют неизбежные проблемы с точностью, поэтому не рекомендуется использовать их в смарт-контрактах, особенно при обработке коэффициентов или ставок, связанных с важными экономическими/финансовыми решениями.
Rust следует стандарту IEEE 754 для представления чисел с плавающей запятой. Тип двойной точности f64 представляет собой двоичное научное представление в компьютере.
Некоторые дробные числа могут быть точно представлены с помощью конечного количества двоичных знаков, например, 0.8125 можно записать как 0.1101. Но дробные числа, такие как 0.7, будут иметь бесконечное циклическое двоичное представление и не могут быть точно представлены с помощью конечных плавающих чисел, что приводит к проблеме "округления".
В примере распределения 0.7 токена NEAR среди 10 пользователей на блокчейне NEAR:
ржавчина #[test] fn precision_test_float() { Пусть сумма: f64 = 0.7;
пусть делитель: f64 = 10.0;
пусть result_0 = сумма / делитель;
println!("Значение суммы: {:.20}", amount); assert_eq!(result_0, 0,07); }
Результаты выполнения показывают, что фактическое значение amount равно 0.69999999999999995559, result_0 равно 0.06999999999999999, что не соответствует ожидаемому 0.07.
Чтобы решить эту проблему, можно использовать фиксированную точку. В NEAR обычно 1 токен NEAR представляется как 10^24 yoctoNEAR. Изменённый код:
руда #[test] fn precision_test_integer() { пусть N: u128 = 1_000_000_000_000_000_000_000_000;
let amount: u128 = 700_000_000_000_000_000_000_000; пусть делитель: u128 = 10;
пусть result_0 = сумма / делитель; assert_eq!(result_0, 70_000_000_000_000_000_000_000); }
Таким образом, можно получить точный результат: 0.7 NEAR / 10 = 0.07 NEAR.
2. Проблема точности вычислений с целыми числами в Rust
Хотя целочисленные операции могут решить проблемы точности с плавающей запятой в некоторых сценариях, целочисленные вычисления также имеют проблемы с точностью.
2.1 Порядок операций
При равном уровне сложности, изменение порядка операций может повлиять на результат:
ржавчина #[test] fn precision_test_div_before_mul() { пусть a: u128 = 1_0000; пусть b: u128 = 10_0000; пусть c: u128 = 20;
}
Результаты показывают result_0 = 2, result_1 = 0.
Причина в том, что целочисленное деление отбрасывает точность, меньшую, чем делитель. При вычислении result_1, (a / b) сначала теряет точность и становится 0; в то время как result_0 сначала вычисляет (a * c), что позволяет избежать потери точности.
2.2 слишком маленький масштаб
ржавчина #[test] fn precision_test_decimals() { пусть a: u128 = 10; пусть b: u128 = 3; пусть c: u128 = 4; пусть десятичная: u128 = 100_0000;
}
Результаты показывают result_0 = 12, result_1 = 13, последний ближе к действительному значению 13.3333.
!
3. Как написать смарт-контракт на Rust для числового актуарного расчета
Для повышения точности можно принять следующие меры:
3.1 Изменение порядка операций
Дайте приоритет умножению целых чисел перед делением.
3.2 увеличение порядка целых чисел
Используйте больший порядок величины, чтобы создать большие молекулы. Например, определите 1 NEAR = 10^24 yoctoNEAR.
3.3 Потеря точности при накоплении вычислений
Записывать и накапливать потери точности, компенсировать в последующих вычислениях:
ржавчина константа USER_NUM: u128 = 3;
FN distribute(amount: u128, смещение: u128) -> u128 { пусть token_to_distribute = смещение + сумма; пусть per_user_share = token_to_distribute / USER_NUM; пусть recorded_offset = token_to_distribute - per_user_share * USER_NUM; записанный_сдвиг }
#[test] FN record_offset_test() { let mut offset: u128 = 0; для i в 1..7 { смещение = distribute(10_000_000_000_000_000_000_000_000, offset); } }
!
3.4 Использование библиотеки Rust Crate rust-decimal
Библиотека подходит для финансовых расчетов с высокоточной десятичной арифметикой без ошибок округления.
3.5 Учитывайте механизм округления
В дизайне смарт-контрактов округление обычно следует принципу "мне это выгодно": если округление вниз мне выгодно, то округляем вниз, если округление вверх мне выгодно, то округляем вверх, редко используется округление до ближайшего.
!