RSA אקראי בבלוקצ'יין

יש בעיה - קשה לייצר מספר אקראי ברשת מבוזרת. כמעט כל הבלוקצ'יין כבר נתקלו בזה. ואכן, ברשתות שבהן אין אמון בין משתמשים, יצירת מספר אקראי שאין להכחישה פותרת בעיות רבות.

במאמר זה אנו מספרים לכם כיצד הצלחנו לפתור את הבעיה באמצעות משחקים כדוגמה. הראשון שבהם היה גלים עץ חג המולד. לצורך הפיתוח, היינו צריכים מחולל מספרים אקראיים.

RSA אקראי בבלוקצ'יין

בתחילה, תכננו ליצור מספר המבוסס על מידע מהבלוקצ'יין. עם זאת, אז התברר: ניתן לשנות את המספר, מה שאומר שהפתרון אינו מתאים.

המצאנו פתרון לעקיפת הבעיה: השתמש בסכימת commit-expand. השרת ניחש מספר מ-1 עד 5, הוסיף לו מלח ואז גיבש את התוצאה באמצעות פונקציות Keccak. השרת פרס את החוזה החכם עם המספר שכבר שמור מראש. מסתבר שהמשחק מסתכם בכך שהמשתמש מנחש את המספר שמסתיר ה-hash.

השחקן ביצע הימור, והשרת שלח את המספר החבוי וה"מלח" לחוזה החכם. במילים פשוטות, הוא חשף את הקלפים. לאחר מכן, השרת בדק את המספרים והחליט אם המשתמש ניצח או הפסיד.

אם השרת לא שלח מספר או "מלח" לאימות, המשתמש ניצח. במקרה זה, עבור כל משחק היה צורך לפרוס חוזה חכם מראש ולכלול בו זכיות פוטנציאליות. התברר שזה לא נוח, לוקח זמן ויקר. באותה תקופה לא היה פתרון בטוח אחר.

לאחרונה, צוות Tradisys הציע להוסיף פונקציה לפרוטוקול Waves rsaVerify(). הוא בודק את תקפות חתימת ה-RSA בהתבסס על המפתח הציבורי והפרטי. כתוצאה מכך, התכונה נוספה.

פיתחנו שלושה משחקים: אומר רולר, הפוך מטבעות и לרכוב על גלים. כל אחד מהם מיישם טכנולוגיית מספרים אקראיים. בואו נבין איך זה עובד.

RSA אקראי בבלוקצ'יין

בואו נסתכל על יצירת מספר אקראי באמצעות Ride on Waves כדוגמה. ניתן למצוא את החוזה החכם כאן.

עבור לכרטיסייה תסריט ובחר פורק קומפילציה. תראה את קוד החוזה החכם (המכונה גם סקריפט).

RSA אקראי בבלוקצ'יין

קוד החוזה החכם מכיל קבוצה של פונקציות. אלה המסומנים כ-@Callable ניתן להפעיל באמצעות עסקאות הזמנה. אנו מעוניינים בשתי פונקציות: להמר и לסגת:

  • הימור func (בחירת שחקן)
  • func draw(gameId,rsaSign)

1. המשתמש בוחר את אורך הקטע ואת גודל ההימור.

RSA אקראי בבלוקצ'יין

2. הלקוח יוצר פונקציית הימור. עבור התמונה למעלה זה יהיה bet("50").

3. הלקוח שולח עסקת Invocation לכתובת החוזה החכם (broadcast InvocationTx). העסקה מכילה את פונקציית ההימור כפרמטר שיחה. המשמעות היא שעסקת ה-Invocation מפעילה את ביצוע פונקציית ההימור (בחירה: String) בחוזה החכם.

RSA אקראי בבלוקצ'יין

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)
  • מפתח ציבורי
  • זכיות פוטנציאליות (תלוי בהימור של השחקן)

RSA אקראי בבלוקצ'יין

כך נראית רשומת נתונים בבלוקצ'יין (ערך-מפתח):

{
    "type": "string",
    "value": "03WON_0283_448t8Jn9P3717UnXFEVD5VWjfeGE5gBNeWg58H2aJeQEgJ_06574069_09116020000_0229",
    "key": "2GKTX6NLTgUrE4iy9HtpSSHpZ3G8W4cMfdjyvvnc21dx"
  }

"מפתח" (מפתח) - מזהה משחק משחק חדש. שאר הנתונים כלולים בשורת השדה "ערך". ערכים אלה מאוחסנים בכרטיסייה נתונים חוזה חכם:

RSA אקראי בבלוקצ'יין

RSA אקראי בבלוקצ'יין

5. השרת "מסתכל" על החוזה החכם ומוצא את העסקה שנשלחה (משחק חדש) באמצעות הבלוקצ'יין Api. מזהה המשחק של המשחק החדש כבר מתועד בבלוקצ'יין, מה שאומר שכבר לא ניתן לשנות אותו או להשפיע עליו

6. השרת יוצר פונקציית משיכה (gameId, rsaSign). לדוגמה, כך:

withdraw ("FwsuaaShC6DMWdSWQ5osGWtYkVbTEZrsnxqDbVx5oUpq", "base64:Gy69dKdmXUEsAmUrpoWxDLTQOGj5/qO8COA+QjyPVYTAjxXYvEESJbSiCSBRRCOAliqCWwaS161nWqoTL/TltiIvw3nKyd4RJIBNSIgEWGM1tEtNwwnRwSVHs7ToNfZ2Dvk/GgPUqLFDSjnRQpTHdHUPj9mQ8erWw0r6cJXrzfcagKg3yY/0wJ6AyIrflR35mUCK4cO7KumdvC9Mx0hr/ojlHhN732nuG8ps4CUlRw3CkNjNIajBUlyKQwpBKmmiy3yJa/QM5PLxqdppmfFS9y0sxgSlfLOgZ51xRDYuS8NViOA7c1JssH48ZtDbBT5yqzRJXs3RnmZcMDr/q0x6Bg==")

7. השרת שולח עסקת Invocation לחוזה החכם (broadcast InvocationTx). העסקה מכילה קריאה לפונקציית המשיכה שנוצרה (gameId, rsaSign):

RSA אקראי בבלוקצ'יין

הפונקציה מכילה מזהה משחק משחק חדש והתוצאה של חתימת RSA של מזהה ייחודי עם מפתח פרטי. תוצאת החתימה ללא שינוי.

מה זה אומר?

אנחנו לוקחים את אותו ערך (מזהה משחק) ומחילים עליו את שיטת החתימה RSA. תמיד נקבל את אותה תוצאה. כך עובד אלגוריתם RSA. לא ניתן לתמרן את המספר הסופי, מכיוון שמזהה המשחק והתוצאה של החלת RSA אינם ידועים. גם בחירת מספר היא חסרת טעם.

8. Blockchain מקבל את העסקה. הוא מפעיל את פונקציית הנסיגה (gameId, rsaSign)

9. בתוך פונקציית הנסיגה מתרחשת נסיגה פונקציות GenerateRandInt (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 מזהה משחק מפתח פרטי (rsaSign). לאחר מכן גיבוב עם SHA-256 (sha256(rsaSign)).

איננו יכולים לחזות את התוצאה של החתימה וה-hashing שלאחר מכן. לכן, אי אפשר להשפיע על יצירת מספר אקראי. כדי לקבל מספר בטווח מסוים (לדוגמה, מ-1 עד 100), השתמש בפונקציית ההמרה toInt וב-%100 (בדומה ל mod).

בתחילת המאמר הזכרנו את הפונקציה rsaVerify(), המאפשר לבדוק את תקפות חתימת RSA באמצעות מפתח פרטי מול מפתח ציבורי. הנה החלק GenerateRandInt(gameId,rsaSign):

rsaVerify (SHA256, toBytes(gameId), rsaSign, RSAPUBLIC)

המפתח הציבורי RSAPUBLIC והמחרוזת rsaSign מועברים לקלט. החתימה נבדקת לגבי תוקף. המספר נוצר אם הבדיקה הצליחה. אחרת, המערכת מחשיבה שהחתימה אינה חוקית (חתימת RSA לא חוקית).

על השרת לחתום על מזהה המשחק עם מפתח פרטי ולשלוח חתימת Rsa חוקית בתוך 2880 בלוקים. הפרמטר מוגדר בעת פריסת החוזה החכם. אם שום דבר לא קורה בזמן המוקצב, המשתמש מנצח. במקרה זה, יש לשלוח את הפרס לכתובת שלך בעצמך. מסתבר ש"לא משתלם לשרת לרמות", כי זה מוביל להפסד. להלן דוגמה.

RSA אקראי בבלוקצ'יין

המשתמש משחק אומר רולר. בחרתי 2 מתוך 6 הצדדים של הקוביה, ההימור הוא 14 WAVES. אם השרת לא ישלח חתימת RSA חוקית לחוזה החכם בתוך הזמן שצוין (2880 בלוקים), המשתמש ייקח 34.44 WAVES.

כדי ליצור מספרים במשחקים, אנו משתמשים באורקל - מערכת חיצונית שאינה בלוקצ'יין. השרת מבצע חתימת RSA של מזהה המשחק. החוזה החכם בודק את תוקף החתימה וקובע את הזוכה. אם השרת לא שולח כלום, המשתמש מנצח אוטומטית.

זוהי שיטת דור כנה, כי מניפולציה היא בלתי אפשרית מבחינה טכנית. כל משחקי Tradisys עובדים על בסיס האלגוריתם המתואר. כך פועלים משחקי בלוקצ'יין. הכל שקוף וניתן לאימות. אין אנלוגים למערכת כזו באף בלוקצ'יין אחר. זהו אקראי הוגן.

מקור: www.habr.com

הוספת תגובה