จากที่ได้กล่าวมาแล้วใน บทความส่วนแรก ถึงวิธีการ Random แบบต่างๆ ในบทความนี้เราจะมาพูดถึงการ Random ที่ ZMINE ใช้แจกรางวัล ST Rewards กันครับ
การออกแบบ
โจทย์ของการ Random ก็คือต้องสามารถ Random ค่าระหว่างเลข 1 ถึง n ได้ โดย n คือจำนวนสิทธิที่ของ ST Rewards ในรอบนั้นๆ สำหรับคุณสมบัติต่างๆที่ต้องการมีดังนี้
- โปร่งใส ตรวจสอบย้อนหลังได้ ซึ่งคุณสมบัติข้อนี้เป็นพื้นฐานของเทคโนโลยี Blockchain ดังนั้นการเลือกเอา Smart Contract ใน Ethereum Network มาใช้จึงสามารถตอบโจทย์ได้เป็นอย่างดี
- คาดเดาผลลัพธ์ได้ยาก หรือหา Seed ที่ไม่สามารถคาดเดาได้โดยง่ายนั่นเอง ซึ่งในส่วนนี้เราได้ออกแบบให้ใช้ทั้งการสร้าง Seed แบบ True Random Number Generator และส่วนที่เป็นการสร้าง Seed แบบ Pseudo Random Number Generator ผสมกัน
- ง่ายและรวดเร็ว วิธีการที่ใช้ควรไม่ซับซ้อน สามารถให้ผลลัพธ์ได้ทันที ดังนั้นเราจึงตัดการใช้วิธี Random แบบใช้ Third party API ออก (เช่น random.org) หรือการ Random แบบที่มีความซับซ้อนสูงและใช้เวลานาน เช่น RANDAO ออกไป
- จำนวนและช่วงเวลาที่ใช้ข้อมูล การ Random ทำเดือนละ 2 ครั้ง ทุกวันที่ 1 กับ 16 จำนวนเลขประมาณ 10-20 จำนวนต่อครั้ง
จากที่กล่าวมาข้างต้นทางทีมงานได้ออกแบบที่มาของ Seed ได้ดังนี้
- Counter (Nonce) คือตัวแปรจำนวนครั้งที่ Random เลขนี้จะเพิ่มขึ้นทีละหนึ่งทุกคร้ังที่การ Random สำเร็จ
- Variables in Ethereum Network ได้แก่ ตัวแปร Block No (เลข Block ก่อนหน้า) และ Block Difficulty ซึ่งมีการเปลี่ยนแปลงในทุกๆ Block ที่เกิดขึ้น ทั้งสองตัวแปรนี้ถือว่าเป็นตัวแปรที่นำมาใช้ได้ง่าย เวลาเขียน Smart Contract เราสามารถเรียกค่าตัวแปรเหล่านี้ออกมาใช้ได้เลย อย่างไรก็ตามตัวแปรเหล่านี้เป็นตัวแปรที่สามารถคาดเดาได้ง่าย
- Third-party Random ทางทีมงานเลือกใช้ ผลฉลากกินแบ่งรัฐบาลมาใช้ เนื่องจากเป็นการสุ่มที่มีความน่าเชื่อถือ และช่วงเวลาสอดคล้องกับที่ ST Rewards ต้องใช้คือวันที่ 1 และ 16 ของเดือน
เมื่อได้แหล่งสร้าง Seed ทั้ง 3 แหล่งแล้ว ก็จะนำมาใช้ในการสร้างเลขสุ่มผ่านการ Hash (Algorithm KECCAK-256) ที่จะทำให้ได้ผลลัพธ์เป็นเลข uint256 ซึ่งการ Hash นี้จะให้ผลลัพธ์ออกมาค่อนข้างมีการกระจาย กล่าวคือถึงแม้ตัวเลข Seed จะเปลี่ยนไปแค่หลักเดียว แต่ผลลัพธ์การ Hash ก็จะออกมาแตกต่างจากเดิมมาก
หลังได้ผลลัพธ์การ Hash แล้ว เราก็จะนำมาทำให้อยู่ในช่วงตัวเลขที่เราต้องการ (1 ถึง n) ด้วยการ Modulo (%)
Smart Contract
ทางทีมงานได้เขียน Smart Contract ชื่อ ZmineRandom เพื่อใช้ในการ Random ST Rewards สามารถตรวจสอบได้จาก URL https://etherscan.io/address/0xF912eFb4E59Cd2910D90a78b1c1491a870c54b12 ซึ่งมีการ Verified Source Code ไว้เรียบร้อยแล้ว
ZmineRandom จะทำการ Random ค่า โดยเฉพาะผู้ที่มีสิทธิเท่านั้นถึงจะทำการ Random ได้ (Authorizors) เมื่อทำการ Random แล้วจะทำการเก็บค่า Seed ที่ใช้ไว้ด้วย เพื่อให้สามารถตรวจสอบย้อนหลังได้
ข้อมูล Smart Contract: ZmineRandom บน Etherscan
จาก Code จะเห็นว่าในการ Random จะกำหนดตัวแปร 3 ตัวได้แก่
- min : ค่าที่ Random ได้ (จำนวนเต็ม) ตํ่าสุดที่เป็นไปได้
- max : ค่าที่ Random ได้ (จำนวนเต็ม) สูงสุดที่เป็นไปได้
- lotto: ตัวเลข Random จากภายนอก (ผลฉลากกินแบ่งรัฐบาล ไล่ไปตั้งแต่รางวัลที่ 1,2,3 ตามลำดับ)
เมื่อทำการ Random ได้แล้วก็จะเก็บผลลัพธ์ไว้ที่ randomResultMap และเก็บ Seed ไว้ใน randomInputMap ซึ่งสามารถตรวจสอบได้จากแทป Read Contract ใน Etherscan
Note: จาก Source Code ข้างต้น จะสังเกตได้ว่า Seed แต่ละตัวเมื่อผ่านการ Hash แล้วจะนำมา “+” กัน ซึ่งตรงนี้ต้องระวังเพราะอาจเกิดการ Overflow ของตัวเลขได้ เนื่องจาก uint256 จะมีค่าสูงสุดคือ 2**256-1 หรือประมาณ 1.157920892373162e+77 อาจดูเป็นตัวเลขที่เยอะมาก แต่ผลลัพธ์การ Hash ก็มักให้ตัวเลขที่มากเหมือนกัน ยกตัวอย่างเช่น
uint256(keccak256(abi.encodePacked(1))) = 8.008442285988055e+76
uint256(keccak256(abi.encodePacked(2))) = 2.910267648167304e+76
uint256(keccak256(abi.encodePacked(3))) = 8.790302987107592e+76
จะเห็นได้ว่าแค่ 3 พจน์นี้รวมกันก็ Overflow แล้ว ดังนั้นการ + – x % ใน Smart Contract นั้นควรที่จะใช้ Library SafeMath เสมอ เพื่อป้องกันการ Overflow หรือ Arithmetic error เช่นการหารด้วย 0 เป็นต้น
อย่างไรก็ตาม เนื่องจาก Smart Contract นี้มีจุดประสงค์คือการ “Random” ดังนั้นจึงไม่สนใจว่าจะเกิดการ Overflow หรือไม่ กล่าวคือถ้า Overflow ก็เหมือนการเอาฉลากจากโถเก่ามาใส่โถใหม่เท่านั้นเอง ยิ่งมั่วยิ่งดี 🙂
ตัวอย่างการอ่านค่าจาก Smart Contract ผ่านทาง Etherscan
การ Random ST Rewards ครั้งที่ 1 จำนวน 9 รางวัลได้ผลลัพธ์ดังนี้
Counter | TxID | Min | Max | ThirdParty Random No. | Random Result |
---|---|---|---|---|---|
#1 | 0x73722ce3ada144c1845e97b7ff9a6106c65b44aba5e5687b5d6bcc689ffd197b | 1 | 320,261 | 452643 | 037425 |
#2 | 0x58ba0a9731e1fae4e5c1fe7fc93131db4fce617163ce58ebcb9aa8216a31df90 | 1 | 320,261 | 261071 | 293284 |
#3 | 0x680e2ce365568085f3716834f61d4e7f51b0495ee4709d5caee9f47d9466adf3 | 1 | 320,261 | 350893 | 055588 |
#4 | 0x656862b0cccb8dd3ba405672868f11c706463dac9bacd488e69f8f76a209e639 | 1 | 320,261 | 616843 | 267931 |
#5 | 0x5be1ee99be7ac4f67950eab68f6c34f9badf950b61fe982068a5411b7feadd8c | 1 | 320,261 | 875042 | 056760 |
#6 | 0x76affdae124a196cdfc3b784a2916591d3097adf7ed954abcca68b6a7e3e1606 | 1 | 320,261 | 964263 | 076413 |
#7 | 0x7ec93b92772d745ad2db965ee3e5cfb52a6099d40f78c14e5faee5f7b5183bf3 | 1 | 320,261 | 021460 | 078133 |
#8 | 0x69fa2cc01ae5f08b66c608be70e854d27e084a687eb5a75e6604f3ec7a69cc8f | 1 | 320,261 | 315781 | 289283 |
#9 | 0x1cc67b412c42097b5bbb1c351280d076dfa67861107c7f4f1516a9c4905feba9 | 1 | 320,261 | 239381 | 275876 |
จากแทป Read Contract ใน Etherscan
ข้อ 2. counter เป็นการบอกว่ามีการ Random ไปแล้ว 9 ครั้ง
ข้อ 4. randomResultMap เป็นการบอกผลลัพธ์การ Random จากรูป การ Random ครั้งแรกให้ผลลัพธ์ 37425
ข้อ 6. randomInputMap เป็นการเก็บ Seed ทั้งหมดที่ใช้ในการ Random ประกอบด้วย
- Index 0: Min
- Index 1: Max
- Index 2: Lotto
- Index 3: Block Difficulty
- Index 4: Block No
สำหรับเลขที่ใช้ Lotto สามารถตรวจสอบได้จากผลสลากกินแบ่งรัฐบาลไทยล่าสุด เช่น ST Rewards ครั้งที่ 1 ตรงกับผลสลากงวด 1 October 2018 ตามลำดับ รางวัลที่ 1, 2, 3 …
Note: Random #8 ใช้ 315781 และ Random #9 ใช้ 239381 สลับลำดับกัน เนื่องจากความผิดพลาดเล็กน้อย 😛
สรุป
ST Rewards เป็นการแจกรางวัลแบบสุ่ม ซึ่งทางทีมงาน ZMINE ได้ให้ความสำคัญกับเรื่องของความโปร่งใส และต้องการให้ทุกคนสามารถเข้ามาร่วมกันตรวจสอบได้ จึงได้นำหลักการของ Smart Contract มาทำการ Random และเก็บข้อมูลย้อนหลังไว้ หวังว่าบทความนี้จะเป็นประโยชน์ต่อทุกท่านนะครับ 🙂