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

في البداية ، خططنا لإنشاء رقم باستخدام معلومات من blockchain. ومع ذلك ، عند إجراء مزيد من التحقيقات ، أصبح من الواضح أنه يمكن التلاعب بالعملية المستخدمة لإنشاء رقم بهذه الطريقة. كان علينا أن نتجاهل هذا الحل.
لقد توصلنا إلى حل بديل ، باستخدام نظام "كشف الالتزام". اقترح الخادم رقمًا من 1 إلى 5 ، وأضاف "ملح" إليه وقام
بتجزئة النتيجة باستخدام
وظيفة Keccak . قام الخادم بتصحيح عقد ذكي مسبقًا مع رقم محفوظ بالفعل. وكانت النتيجة أن اللعبة تم تخفيضها بشكل فعال إلى المستخدم الذي يخمن الرقم المخفي بواسطة التجزئة.
وضع اللاعب رهانه وأرسل الخادم رقمًا مخفيًا و "ملح" إلى عقد ذكي. بعبارة أخرى ، تم الكشف عن البطاقات. بعد ذلك ، تحقق الخادم من الأرقام وقرر ما إذا كان المستخدم قد فاز أم خسر.
إذا لم يرسل الخادم الرقم و "الملح" للتحقق ، ففاز المستخدم بذلك. في هذه الحالة ، كان من الضروري نشر عقد ذكي مقدمًا وترتيب المكاسب المحتملة لكل لعبة. وكان هذا غير مريح ومكلفة وتستغرق وقتا طويلا. في ذلك الوقت ، رغم ذلك ، لم يكن هناك حل آمن آخر.
بعد ذلك بوقت قصير ، اقترح فريق Tradisys إضافة وظيفة
rsaVerify () إلى بروتوكول Waves. يتحقق هذا من صلاحية توقيع RSA استنادًا إلى المفاتيح العامة والخاصة. نتيجة لاقتراحنا ، تمت إضافة الوظيفة.
بنينا ثلاث ألعاب جديدة:
Dice Roller و
Coin Flip و
Ride On Waves . في كل منها ، تم تطبيق تقنية الأرقام العشوائية الجديدة. دعونا نلقي نظرة فاحصة على كيفية عمله.

لنلقِ نظرة على الجيل العشوائي أولاً. يمكنك العثور على العقد الذكي
هنا .
انتقل إلى
علامة التبويب Script واختر
Decompiled . سترى رمز العقد الذكي (أو البرنامج النصي).

يتكون رمز العقد الذكي من قائمة الوظائف. يمكن تشغيل تلك التي هيCallable عبر
معاملات الاحتجاج. نحن مهتمون بإثنان منهم:
الرهان والسحب :
- رهان func (playerChoice)
- سحب func (gameId ، rsaSign)
1. يختار المستخدم النطاق وحجم الرهان.

2. يرتب العميل وظيفة الرهان. للصورة أعلاه سيكون
رهان ("50")3. يرسل العميل معاملة Invocation إلى عنوان العقد الذكي (بث InvocationTx). تحتوي المعاملة كمعلمة Call على وظيفة الرهان. هذا يعني أن معاملة الاحتجاج تبدأ في تنفيذ وظيفة الرهان على العقد الذكي (اختيار: سلسلة).

4. دعنا ننظر إلى وظيفة الرهان:
@Callable(i) func bet (playerChoice) = { let newGameNum = IncrementGameNum() let gameId = toBase58String(i.transactionId) let pmt = extract(i.payment) let betNotInWaves = isDefined(pmt.assetId) let feeNotInWaves = isDefined(pmt.assetId) let winAmt = ValidateBetAndDefineWinAmt(pmt.amount, playerChoice) let txIdUsed = isDefined(getString(this, gameId)) if (betNotInWaves) then throw ("Bet amount must be in Waves") else if (feeNotInWaves) then throw ("Transaction's fee must be in Waves") else if (txIdUsed) then throw ("Passed txId had been used before. Game aborted.") else { let playerPubKey58 = toBase58String(i.callerPublicKey) let gameDataStr = FormatGameDataStr(STATESUBMITTED, playerChoice, playerPubKey58, height, winAmt, "") ScriptResult(WriteSet(cons(DataEntry(RESERVATIONKEY, ValidateAndIncreaseReservedAmt(winAmt)), cons(DataEntry(GAMESCOUNTERKEY, newGameNum), cons(DataEntry(gameId, gameDataStr), nil)))), TransferSet(cons(ScriptTransfer(SERVER, COMMISSION, unit), nil))) } }
تقوم الوظيفة بتسجيل لعبة جديدة في حالة العقد الذكي:
- معرف لعبة جديد فريد (معرف اللعبة)
- حالة اللعبة = المقدمة
- اختيار اللاعب (النطاق هو 50)
- المفتاح العام
- المكافأة المحتملة (يعتمد على رهان اللاعب)

هذه هي الطريقة التي تبدو بها قاعدة بيانات المفتاح على blockchain:
{ "type": "string", "value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229", "key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx" }
"المفتاح" هو
معرف اللعبة للعبة جديدة. توجد البيانات المتبقية في الحقل "قيمة". يتم تخزين هذه الإدخالات في علامة تبويب
البيانات في العقد الذكي:


5. يعثر الخادم على المعاملة المرسلة (اللعبة الجديدة) عبر blockchain API. معرف اللعبة مسجل بالفعل في blockchain ، لذلك من المستحيل تغييره أو حذفه.
6. يشكل الخادم وظيفة سحب (gameId ، rsaSign) مثل:
سحب ( «FwsuaaShC6DMWdSWQ5osGWtYkVbTEZrsnxqDbVx5oUpq»، «base64 في: Gy69dKdmXUEsAmUrpoWxDLTQOGj5 / qO8COA + QjyPVYTAjxXYvEESJbSiCSBRRCOAliqCWwaS161nWqoTL / TltiIvw3nKyd4RJIBNSIgEWGM1tEtNwwnRwSVHs7ToNfZ2Dvk / GgPUqLFDSjnRQpTHdHUPj9mQ8erWw0r6cJXrzfcagKg3yY / 0wJ6AyIrflR35mUCK4cO7KumdvC9Mx0hr / ojlHhN732nuG8ps4CUlRw3CkNjNIajBUlyKQwpBKmmiy3yJa / QM5PLxqdppmfFS9y0sxgSlfLOgZ51xRDYuS8NViOA7c1JssH48ZtDbBT5yqzRJXs3RnmZcMDr / q0x6Bg ==»)
7. يرسل الخادم معاملة Invocation إلى العقد الذكي (بث InvocationTx). تحتوي المعاملة على استدعاء لوظيفة السحب التي تم إنشاؤها (gameId ، rsaSign):

تحتوي الوظيفة على
معرف اللعبة وتوقيع RSA لمعرف فريد. نتيجة التوقيع غير قابلة للتغيير.
ماذا يعني هذا؟
نأخذ نفس القيمة (معرف اللعبة) ونطبق طريقة توقيع RSA عليها. هذه هي الطريقة التي تعمل خوارزمية RSA. من المستحيل التعامل مع الرقم النهائي لأن
معرف اللعبة ونتائج خوارزمية RSA غير معروفين. من غير المجدي أيضًا محاولة تخمين عدد.
8. يتلقى blockchain معاملة تدير وظيفة السحب (gameId ، rsaSign).
9. هناك دعوة لوظيفة GenerateRandIn داخل وظيفة السحب (gameId ، rsaSign). هذا هو مولد رقم عشوائي.
# @return 1 ... 100 func GenerateRandInt (gameId,rsaSign) = { # verify RSA signature to proof random let rsaSigValid = rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC) if (rsaSigValid) then { let rand = (toInt(sha256(rsaSign)) % 100) if ((0 > rand)) then ((-1 * rand) + 1) else (rand + 1) } else throw ("Invalid RSA signature") }
راند هو رقم عشوائي
أولاً ، يتم أخذ السلسلة الناتجة عن توقيع RSA. بعد ذلك ، يتم تجزئته عبر SHA-256 (
sha256 (rsaSign) ).
لا يمكننا التنبؤ بنتيجة التوقيع والتجزئة اللاحقة. وبالتالي ، من المستحيل التأثير على جيله. للحصول على رقم في نطاق معين (على سبيل المثال من 1 إلى 100) ، يتم تطبيق وظائف التحويل إلى INT و٪ 100 (التناظرية
mod ).
في بداية المقال ، ذكرنا وظيفة
rsaVerify () التي تسمح بالتحقق من صلاحية توقيع RSA بواسطة مفتاح خاص مقابل مفتاح عمومي. هنا جزء من GenerateRandInt (gameId ، rsaSign):
rsaVerify (SHA256 ، toBytes (gameId) ، rsaSign ، RSAPUBLIC)
بادئ ذي بدء ، تؤخذ RSAPUBLIC المفتاح العمومي وسلسلة rsaSign. يتم التحقق من صحة التوقيع. إذا تم التحقق بنجاح ، يتم إنشاء الرقم. خلاف ذلك ، يعتبر النظام أن التوقيع غير صالح (توقيع RSA غير صالح).
يجب على الخادم التوقيع على معرف اللعبة باستخدام مفتاح خاص وإرسال توقيع RSA صالح ضمن 2880 قطعة. تتم إدارة الخيار أثناء نشر العقد الذكي. إذا لم يحدث شيء في الوقت المحدد ، يفوز المستخدم. في هذه الحالة ، يجب إرسال المكافأة بواسطة المستخدم بشكل مستقل. اتضح أن الغش غير مربح للخادم لأن هذا يؤدي إلى خسارة. هناك مثال أدناه.

المستخدم يلعب
النرد الرول . اختار 2 من 6 وجوه مكعب ، مع رهان 14 WAVES. إذا لم يرسل الخادم توقيع RSA صالحًا إلى العقد الذكي خلال فترة زمنية محددة (2880 قطعة) ، فسيحصل المستخدم على 34.44 WAVES.
لتوليد الأرقام ، نستخدم أوراكل ، نظام خارجي بدلاً من blockchain. يقوم الخادم بتنفيذ توقيع RSA لمعرف اللعبة. يتحقق العقد الذكي من صحة التوقيع ويحدد الفائز. إذا لم يرسل الخادم أي شيء ، فسيفوز المستخدم تلقائيًا.
تضمن هذه الطريقة أن التلاعب مستحيل تقنياً. تعتمد جميع ألعاب Tradisys على الخوارزمية الموضحة أعلاه - ضمان أن تكون ألعابنا نزيهة وشفافة. كل شيء يمكن تدقيقه علنا لضمان الصدق.