تطوير العقود الذكية بلغة Rust: التغلب على مشكلة دقة الحسابات العددية

مشاكل دقة الحسابات العددية في تطوير العقود الذكية باستخدام Rust

1. مشكلة دقة العمليات العشرية

تدعم لغة Rust العمليات الحسابية للأرقام العشرية بشكل أصلي، ولكن توجد مشاكل لا مفر منها تتعلق بدقة الحساب عند إجراء العمليات الحسابية على الأرقام العشرية. عند كتابة العقود الذكية، لا يُوصى باستخدام العمليات الحسابية للأرقام العشرية، خاصة عند التعامل مع النسب أو أسعار الفائدة المتعلقة بالقرارات الاقتصادية/المالية المهمة.

تتبع نوع النقطة العائمة بدقة مزدوجة f64 في لغة Rust معيار IEEE 754، حيث يتم التعبير عنه بطريقة العد العلمي ذات الأساس 2. لا يمكن تمثيل بعض الأعداد العشرية بدقة باستخدام أرقام النقطة العائمة ذات الطول المحدود، مما يؤدي إلى "ظاهرة التقريب".

على سبيل المثال، عند توزيع 0.7 من رموز NEAR على 10 مستخدمين على سلسلة NEAR العامة:

صدأ #[test] الجبهة precision_test_float() { دع المبلغ: F64 = 0.7 ؛
دع المقسوم عليه: F64 = 10.0 ؛
دع result_0 = الكمية / القاسم ؛
println!("قيمة المبلغ: {:.20}", amount); assert_eq!(result_0 ، 0.07 ، "") ؛ }

تظهر نتائج الإخراج أن قيمة amount هي 0.69999999999999995559، وليست 0.7 الدقيقة. كما أن نتيجة عملية القسمة أصبحت غير دقيقة 0.06999999999999999، وليس 0.07 المتوقعة.

لحل هذه المشكلة، يمكن النظر في استخدام الأعداد العشرية الثابتة. في بروتوكول NEAR، يُستخدم عادة 10^24 yoctoNEAR لتمثيل 1 رمز NEAR. الكود الاختباري المعدل:

صدأ #[test] الجبهة الوطنية precision_test_integer() { دع N: u128 = 1_000_000_000_000_000_000_000_000_000 ؛
المبلغ اليدخ: 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_000, ""); }

يمكن الحصول على نتائج حسابية دقيقة بهذه الطريقة: 0.7 NEAR / 10 = 0.07 NEAR.

!

2. مشكلة دقة حسابات الأعداد الصحيحة في Rust

يمكن استخدام العمليات الحسابية الصحيحة لحل مشكلة فقدان دقة العمليات الحسابية العائمة في بعض السيناريوهات، ولكن نتائج الحسابات الصحيحة ليست دقيقة وموثوقة تماماً.

2.1 ترتيب العمليات

ترتيب العمليات بين الضرب والقسمة ذات الأولوية الحسابية نفسها يمكن أن يؤثر بشكل مباشر على نتيجة الحساب، مما يؤدي إلى مشاكل في دقة الحسابات الصحيحة. على سبيل المثال:

صدأ #[test] الجبهة precision_test_div_before_mul() { دع أ: U128 = 1_0000 ؛ دع ب: U128 = 10_0000 ؛ دع C: U128 = 20 ؛

دع result_0 = a.checked_mul(c).expect( "ERR_MUL").checked_ div(b).expect( "ERR_DIV" );
دع result_1 = a.checked_div(b).expect("ERR_DIV").checked_ mul(c).expect( "ERR_MUL" );

assert_eq!(result_0,result_1,"");

}

تظهر نتائج التنفيذ أن result_0 و result_1 غير متساويين. السبب هو أن قسمة الأعداد الصحيحة تتجاهل الدقة الأقل من المقام. عند حساب result_1، يتم فقدان الدقة أولاً لتصبح 0 عند (a / b)؛ بينما عند حساب result_0، يتم حساب a * c أولاً لتجنب فقدان الدقة.

2.2 كمية صغيرة جدًا

عند التعامل مع حسابات الأعداد العشرية، قد تؤدي العمليات على الأعداد الصحيحة إلى فقدان الدقة:

صدأ #[test] الجبهة precision_test_decimals() { دع أ: u128 = 10 ؛ دع ب: u128 = 3 ؛ دع C: U128 = 4 ؛ دع الرقم العشري: U128 = 100_0000 ؛

دع result_0 = a.checked_div(b).expect("ERR_DIV").checked_ mul(c).expect( "ERR_MUL" );
let result_1 = a.checked_mul(decimal).expect("ERR_MUL").checked_ div(b).expect( "ERR_DIV")
    .checked_mul(c).expect("ERR_MUL").checked_ div(decimal).expect("ERR_DIV");

println!("{}:{}", result_0, result_1);
assert_eq!(result_0 ، result_1 ، "") ؛

}

تظهر النتائج result_0=12, result_1=13، بينما يجب أن تكون القيمة المتوقعة الفعلية 13.3333....

!

3. كيفية كتابة العقود الذكية Rust للتقييم العددي

لزيادة الدقة، يمكن اتخاذ التدابير الوقائية التالية:

3.1 تعديل ترتيب العمليات الحسابية

اجعل ضرب الأعداد الصحيحة له الأولوية على قسمة الأعداد الصحيحة.

3.2 زيادة مرتبة العدد الصحيح

استخدم مقاييس أكبر لإنشاء جزيئات أكبر. على سبيل المثال، يمكن التعبير عن 5.123 NEAR باستخدام 5.123 * 10^10 = 51_230_000_000 للمشاركة في العمليات الحسابية اللاحقة.

3.3 خسارة دقة العمليات التراكمية

بالنسبة لمشكلة الدقة التي لا مفر منها، يمكن تسجيل خسارة دقة العمليات التراكمية. على سبيل المثال:

صدأ كونست USER_NUM: U128 = 3 ؛

الجبهة distribute(amount: U128 ، الإزاحة: u128) -> U128 { دع token_to_distribute = الإزاحة + المبلغ ؛ دع per_user_share = token_to_distribute / USER_NUM ؛ println!("per_user_share {}", per_user_share); دع recorded_offset = token_to_distribute - per_user_share * USER_NUM ؛ recorded_offset }

#[test] الجبهة الوطنية record_offset_test() { دع إزاحة MUT: U128 = 0 ؛ من أجل i في 1..7 { println!("Round {}", i); الإزاحة = distribute(to_yocto( "10" ) ، offset) ؛ println!("إزاحة {}\n", offset); } }

يمكن أن تجمع هذه الطريقة الرمز المميز المتبقي من كل توزيع، لتوزيعه في المرة التالية، وبالتالي تحقيق الهدف من التوزيع الكامل.

3.4 استخدام مكتبة Rust Crate rust-decimal

تتناسب هذه المكتبة مع الحسابات المالية العشرية التي تحتاج إلى دقة حساب فعالة ولا تحتوي على أخطاء في التقريب.

3.5 مراعاة آلية التقريب

في تصميم العقود الذكية، تُستخدم عادةً مشكلة التقريب مبدأ "أريد أن أستفيد، ولا يجب على الآخرين أن يسرقوا مني". يتم اختيار التقريب لأسفل أو لأعلى وفقًا للحالة، ونادرًا ما يتم استخدام التقريب إلى أقرب.

!

شاهد النسخة الأصلية
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
  • أعجبني
  • 8
  • مشاركة
تعليق
0/400
rugpull_survivorvip
· منذ 10 س
لقد وقع الكثير من الناس في فخ التقريب
شاهد النسخة الأصليةرد0
WhaleWatchervip
· منذ 18 س
التحكم في الدقة أمر حاسم
شاهد النسخة الأصليةرد0
consensus_failurevip
· 07-13 12:52
استخدام دقة ثابتة أكثر أمانًا
شاهد النسخة الأصليةرد0
Ser_Liquidatedvip
· 07-12 19:41
再出精度问题必الحصول على التصفية
شاهد النسخة الأصليةرد0
DecentralizeMevip
· 07-11 16:13
استخدم الأعداد العشرية الثابتة بدلاً من ذلك
شاهد النسخة الأصليةرد0
BoredWatchervip
· 07-11 15:58
الكود معقد قليلاً
شاهد النسخة الأصليةرد0
BrokenDAOvip
· 07-11 15:53
قوة الحوسبة总溢出就麻烦
شاهد النسخة الأصليةرد0
MEVSandwichMakervip
· 07-11 15:51
إنه فخ قديم ، يوصى بتجنبه.
شاهد النسخة الأصليةرد0
  • تثبيت