حول كيفية كتابة عقد ذكي ونشره في شبكة Telegram المفتوحة (TON)

حول كيفية كتابة ونشر عقد ذكي في TON

عن ماذا تتحدث هذه المقالة؟

سأتحدث في المقال عن كيفية مشاركتي في مسابقة Telegram blockchain الأولى (من اثنتين)، ولم أحصل على جائزة، وقررت تسجيل التجربة في المقالة حتى لا تغرق في غياهب النسيان، وربما تساعد شخص ما.

نظرًا لأنني لم أرغب في كتابة تعليمات برمجية مجردة، ولكن للقيام بشيء ناجح، فقد كتبت في المقالة عقدًا ذكيًا لليانصيب الفوري وموقعًا على الويب يعرض بيانات العقد الذكي مباشرة من TON دون استخدام التخزين الوسيط.

ستكون هذه المقالة مفيدة لأولئك الذين يرغبون في إبرام أول عقد ذكي لهم في TON، ولكنهم لا يعرفون من أين يبدأون.

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

حول المشاركة في المسابقة

في أكتوبر الماضي، أعلنت Telegram عن مسابقة blockchain بلغات جديدة Fift и FunC. كان من الضروري اختيار كتابة أي من العقود الذكية الخمسة المقترحة. اعتقدت أنه سيكون من الجيد أن أفعل شيئًا خارجًا عن المألوف، أتعلم لغة وأقوم بشيء ما، حتى لو لم أضطر إلى كتابة أي شيء آخر في المستقبل. بالإضافة إلى أن الموضوع يتم الاستماع إليه باستمرار.

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

لقد خططت للمشاركة حتى النهاية، طالما اتضح ثم أكتب مقالة مراجعة، لكنني فشلت على الفور في المقالة الأولى. أنا كتب المحفظة مع التوقيع المتعدد على FunC وكان يعمل بشكل عام. اتخذت كأساس العقد الذكي على Solidity.

في ذلك الوقت، اعتقدت أن هذا كان بالتأكيد كافيًا لأخذ مكان ما على الأقل للفوز بالجائزة. ونتيجة لذلك، أصبح حوالي 40 من أصل 60 مشاركاً فائزين ولم أكن من بينهم. بشكل عام، هذا ليس شيئا فظيعا، ولكن هناك شيء واحد توترني. في وقت إعلان النتائج لم يتم إجراء مراجعة مع اختبار لعقدي، سألت المشاركين في الدردشة إذا كان هناك أي شخص آخر ليس لديه ذلك، لم يكن هناك أي شخص.

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

تم إنفاق الكثير من الوقت على الفهم، لذلك تقرر كتابة مقال. نظرًا لعدم وجود الكثير من المعلومات حتى الآن، ستساعد المقالة في توفير الوقت لجميع المهتمين.

مفهوم العقود الذكية في TON

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

سنركز على كتابة عقد ذكي والعمل معه TON Virtual Machine (TVM), Fift и FunCلذا فإن المقالة أشبه بوصف لتطوير برنامج تقليدي. لن نتناول هنا كيفية عمل المنصة نفسها.

عموما حول كيفية عمله TVM واللغة Fift هناك وثائق رسمية جيدة. أثناء مشاركتي في المسابقة والآن أثناء كتابة العقد الحالي، كنت أتوجه إليها كثيرًا.

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

لنفترض أننا كتبنا بالفعل عقدًا ذكيًا لـ FunC، بعد ذلك نقوم بتجميع الكود في مجمع Fift.

لا يزال يتعين نشر العقد الذكي المجمع. للقيام بذلك، تحتاج إلى كتابة وظيفة على Fift، والذي سيأخذ رمز العقد الذكي وبعض المعلمات الأخرى كمدخلات، وسيكون الإخراج ملفًا بالامتداد .boc (والتي تعني "كيس الخلايا")، واعتمادًا على كيفية كتابتها، يتم إنشاء مفتاح خاص وعنوان بناءً على رمز العقد الذكي. يمكن بالفعل إرسال Grams إلى عنوان عقد ذكي لم يتم نشره بعد.

لنشر العقد الذكي في TON المستلمة .boc سيلزم إرسال الملف إلى blockchain باستخدام عميل خفيف (المزيد حول ذلك أدناه). ولكن قبل النشر، تحتاج إلى نقل الجرام إلى العنوان الذي تم إنشاؤه، وإلا فلن يتم نشر العقد الذكي. بعد النشر، سيكون من الممكن التفاعل مع العقد الذكي عن طريق إرسال رسائل إليه من الخارج (على سبيل المثال، باستخدام عميل خفيف) أو من الداخل (على سبيل المثال، يرسل عقد ذكي رسالة إلى آخر داخل TON).

بعد أن نفهم كيفية نشر الكود، يصبح الأمر أسهل. نحن نعرف تقريبًا ما نريد كتابته وكيف سيعمل برنامجنا. وأثناء الكتابة، نبحث عن كيفية تنفيذ ذلك بالفعل في العقود الذكية الحالية، أو ننظر إلى كود التنفيذ Fift и FunC في المستودع الرسمي، أو ابحث في الوثائق الرسمية.

في كثير من الأحيان كنت أبحث عن كلمات رئيسية في دردشة Telegram حيث تجمع جميع المشاركين في المسابقة وموظفي Telegram، بما في ذلك، وحدث أنه خلال المسابقة اجتمع الجميع هناك وبدأوا في مناقشة Fift وFunC. الرابط في نهاية المقال.

حان الوقت للانتقال من النظرية إلى التطبيق.

إعداد البيئة للعمل مع TON

كل ما سيتم وصفه في المقالة قمت به على نظام MacOS وقمت بالتحقق منه مرة أخرى في Ubuntu 18.04 LTS النظيف على Docker.

أول شيء فعله هو التنزيل والتثبيت lite-client يمكنك من خلاله إرسال الطلبات إلى TON.

تصف التعليمات الموجودة على الموقع الرسمي عملية التثبيت بشيء من التفصيل وبشكل واضح وتغفل بعض التفاصيل. نتبع هنا التعليمات على طول الطريق، لتثبيت التبعيات المفقودة. لم أقم بتجميع كل مشروع بنفسي وقمت بتثبيته من مستودع Ubuntu الرسمي (على نظام MacOS الذي استخدمته brew).

apt -y install git 
apt -y install wget 
apt -y install cmake 
apt -y install g++ 
apt -y install zlib1g-dev 
apt -y install libssl-dev 

بمجرد تثبيت كافة التبعيات، يمكنك التثبيت lite-client, Fift, FunC.

أولاً، نقوم باستنساخ مستودع TON مع التبعيات. للراحة، سنفعل كل شيء في مجلد ~/TON.

cd ~/TON
git clone https://github.com/ton-blockchain/ton.git
cd ./ton
git submodule update --init --recursive

يقوم المستودع أيضًا بتخزين التطبيقات Fift и FunC.

الآن نحن على استعداد لبناء المشروع. تم استنساخ رمز المستودع في المجلد ~/TON/ton. في ~/TON إنشاء مجلد build وجمع المشروع فيه.

mkdir ~/TON/build 
cd ~/TON/build
cmake ../ton

وبما أننا سنكتب عقدًا ذكيًا، فلا نحتاج فقط lite-clientلكن Fift с FunC، لذلك قمنا بتجميع كل شيء. ليست عملية سريعة لذلك نحن ننتظر.

cmake --build . --target lite-client
cmake --build . --target fift
cmake --build . --target func

بعد ذلك، قم بتنزيل ملف التكوين الذي يحتوي على بيانات حول العقدة التي lite-client سوف تتصل.

wget https://test.ton.org/ton-lite-client-test1.config.json

تقديم الطلبات الأولى في TON

الآن دعونا نركض lite-client.

cd ~/TON/build
./lite-client/lite-client -C ton-lite-client-test1.config.json

إذا كان البناء ناجحًا، فبعد الإطلاق، سترى سجل اتصال العميل الخفيف بالعقدة.

[ 1][t 2][1582054822.963129282][lite-client.h:201][!testnode]   conn ready
[ 2][t 2][1582054823.085654020][lite-client.cpp:277][!testnode] server version is 1.1, capabilities 7
[ 3][t 2][1582054823.085725069][lite-client.cpp:286][!testnode] server time is 1582054823 (delta 0)
...

يمكنك تشغيل الأمر help ومعرفة ما هي الأوامر المتاحة.

help

دعونا ندرج الأوامر التي سنستخدمها في هذه المقالة.

list of available commands:
last    Get last block and state info from server
sendfile <filename> Load a serialized message from <filename> and send it to server
getaccount <addr> [<block-id-ext>]  Loads the most recent state of specified account; <addr> is in [<workchain>:]<hex-or-base64-addr> format
runmethod <addr> [<block-id-ext>] <method-id> <params>...   Runs GET method <method-id> of account <addr> with specified parameters

last получает последний созданный блок с сервера. 

sendfile <filename> отправляет в TON файл с сообщением, именно с помощью этой команды публикуется смарт-контракт и запрсосы к нему. 

getaccount <addr> загружает текущее состояние смарт-контракта с указанным адресом. 

runmethod <addr> [<block-id-ext>] <method-id> <params>  запускает get-методы смартконтракта. 

الآن نحن على استعداد لكتابة العقد نفسه.

تطبيق

فكرة

كما كتبت أعلاه، فإن العقد الذكي الذي نكتبه هو يانصيب.

علاوة على ذلك، فهذا ليس يانصيبًا تحتاج فيه إلى شراء تذكرة والانتظار لمدة ساعة أو يوم أو شهر، ولكنه يانصيب فوري ينقل فيه المستخدم إلى عنوان العقد N جرامًا، ويعود على الفور 2 * N جرام أو يخسر. سنجعل احتمالية الفوز حوالي 40%. إذا لم يكن هناك غرامات كافية للدفع، فسنعتبر المعاملة بمثابة تجديد.

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

كتابة عقد ذكي

للراحة، قمت بإبراز التعليمات البرمجية لـ FunC، ويمكن العثور على المكون الإضافي وتثبيته في بحث Visual Studio Code، إذا كنت تريد فجأة إضافة شيء ما، فقمت بنشر المكون الإضافي في المجال العام. أيضًا، قام شخص ما مسبقًا بإنشاء مكون إضافي للعمل مع Fift، ويمكنك أيضًا تثبيته والعثور عليه في VSC.

قم بإنشاء مستودع على الفور حيث سنلتزم بالنتائج المتوسطة.

لتسهيل حياتنا، سنكتب عقدًا ذكيًا ونختبره محليًا حتى يصبح جاهزًا. فقط بعد ذلك سوف نقوم بنشره في TON.

يحتوي العقد الذكي على طريقتين خارجيتين يمكن الوصول إليهما. أولاً، recv_external() يتم تنفيذ هذه الوظيفة عندما يأتي طلب العقد من العالم الخارجي، أي ليس من TON، على سبيل المثال، عندما نشكل بأنفسنا رسالة ونرسلها عبر عميل لايت. ثانية، recv_internal() يحدث هذا عندما يشير أي عقد داخل TON نفسها إلى عقدنا. في كلتا الحالتين، يمكنك تمرير المعلمات إلى الدالة.

لنبدأ بمثال بسيط سيعمل إذا تم نشره، لكنه لا يحتوي على أي حمل وظيفي.

() recv_internal(slice in_msg) impure {
    ;; TODO: implementation 
}

() recv_external(slice in_msg) impure {
    ;; TODO: implementation  
}

هنا لا بد من شرح ما slice. جميع البيانات المخزنة في TON Blockchain عبارة عن مجموعة TVM cell أو ببساطة cell، يمكن لهذه الخلية تخزين ما يصل إلى 1023 بت من البيانات وما يصل إلى 4 إشارات إلى خلايا أخرى.

TVM cell slice أو slice هو جزء من الموجود cell يتم استخدامه لتحليله، وسوف يكون واضحا أكثر. الشيء الرئيسي بالنسبة لنا هو أن نتمكن من التحول إلى عقد ذكي slice واعتمادًا على نوع الرسالة، قم بمعالجة البيانات فيها recv_external() أو recv_internal().

impure - كلمة أساسية تشير إلى أن الوظيفة تغير بيانات العقد الذكي.

احفظ رمز العقد في lottery-code.fc وتجميع.

~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc 

يمكن الاطلاع على قيمة الأعلام باستخدام الأمر

~/TON/build/crypto/func -help

لقد قمنا بتجميع كود المجمع Fift في lottery-compiled.fif:

// lottery-compiled.fif

"Asm.fif" include
// automatically generated from `/Users/rajymbekkapisev/TON/ton/crypto/smartcont/stdlib.fc` `./lottery-code.fc` 
PROGRAM{
  DECLPROC recv_internal
  DECLPROC recv_external
  recv_internal PROC:<{
    //  in_msg
    DROP    // 
  }>
  recv_external PROC:<{
    //  in_msg
    DROP    // 
  }>
}END>c

ويمكن تشغيله محليا، ولهذا سنقوم بإعداد البيئة.

لاحظ أن السطر الأول يتصل Asm.fif، هذا هو الكود المكتوب في مجمع Fift for Fift.

وبما أننا نريد تشغيل العقد الذكي واختباره، فسنقوم بإنشاء ملف محليًا lottery-test-suite.fif وانسخ الكود المترجم هناك، مع استبدال السطر الأخير فيه، والذي يكتب كود العقد الذكي إلى ثابت codeلتمريره بعد ذلك إلى الجهاز الظاهري:

"TonUtil.fif" include
"Asm.fif" include

PROGRAM{
  DECLPROC recv_internal
  DECLPROC recv_external
  recv_internal PROC:<{
    //  in_msg
    DROP    // 
  }>
  recv_external PROC:<{
    //  in_msg
    DROP    // 
  }>
}END>s constant code

بينما يبدو الأمر واضحًا، فلنضيف الآن الكود الذي سنستخدمه لتشغيل TVM إلى نفس الملف.

0 tuple 0x076ef1ea , // magic
0 , 0 , // actions msg_sents
1570998536 , // unix_time
1 , 1 , 3 , // block_lt, trans_lt, rand_seed
0 tuple 100000000000000 , dictnew , , // remaining balance
0 , dictnew , // contract_address, global_config
1 tuple // wrap to another tuple
constant c7

0 constant recv_internal // to run recv_internal() 
-1 constant recv_external // to invoke recv_external()

В c7 نكتب السياق، أي البيانات التي سيتم من خلالها إطلاق TVM (أو حالة الشبكة). حتى أثناء المنافسة، أظهر أحد المطورين كيفية الإنشاء c7 وأنا نسخت. في هذه المقالة، قد نحتاج إلى التغيير rand_seed وبما أن توليد رقم عشوائي يعتمد عليه ولا يتغير، فسيتم إرجاع نفس الرقم في كل مرة.

recv_internal и recv_external ستكون الثوابت ذات القيمة 0 و-1 مسؤولة عن استدعاء الوظائف المناسبة في العقد الذكي.

نحن الآن جاهزون لإنشاء الاختبار الأول لعقدنا الذكي الفارغ. للتوضيح، في الوقت الحالي، سنضيف جميع الاختبارات إلى نفس الملف. lottery-test-suite.fif.

لنقم بإنشاء متغير storage واكتب فارغة cellسيكون هذا بمثابة تخزين العقد الذكي.

message هذه هي الرسالة التي سنرسلها إلى جهة الاتصال الذكية من الخارج. دعونا نجعلها فارغة في الوقت الراهن.

variable storage 
<b b> storage ! 

variable message 
<b b> message ! 

بعد أن قمنا بإعداد الثوابت والمتغيرات، نبدأ TVM باستخدام الأمر runvmctx وتمرير المعلمات التي تم إنشاؤها إلى الإدخال.

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx 

في النهاية، سوف نكون قادرين على ذلك مثله رمز وسيط ل Fift.

الآن يمكننا تشغيل الكود الناتج.

export FIFTPATH=~/TON/ton/crypto/fift/lib // выполняем один раз для удобства 
~/TON/build/crypto/fift -s lottery-test-suite.fif 

يجب أن يعمل البرنامج بدون أخطاء وفي الإخراج سنرى سجل التنفيذ:

execute SETCP 0
execute DICTPUSHCONST 19 (xC_,1)
execute DICTIGETJMPZ
execute DROP
execute implicit RET
[ 3][t 0][1582281699.325381279][vm.cpp:479]     steps: 5 gas: used=304, max=9223372036854775807, limit=9223372036854775807, credit=0

عظيم، لقد كتبنا أول نسخة عمل من العقد الذكي.

الآن نحن بحاجة إلى إضافة وظيفة. دعونا نتعامل أولاً مع الرسائل التي تأتي من العالم الخارجي إليه recv_external()

يختار المطور بنفسه تنسيق الرسالة الذي يمكن أن يقبله العقد.

لكن عادة

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

لذلك، في كل عقد تقريبًا، يتم حل هاتين المشكلتين، نظرًا لأن عقدنا يقبل الرسائل الخارجية، فنحن بحاجة أيضًا إلى الاهتمام بهذا الأمر.

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

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

نعود إلى lottery-test-suite.fif وأضف الاختبار الثاني إليه. لنرسل رقمًا غير صالح، يجب أن يطرح الرمز استثناءً. على سبيل المثال، لنفترض أنه تم تخزين 166 في بيانات العقد، وسنرسل 165.

<b 166 32 u, b> storage !
<b 165 32 u, b> message !

message @ 
recv_external 
code 
storage @ 
c7 
runvmctx

drop 
exit_code ! 
."Exit code " exit_code @ . cr 
exit_code @ 33 - abort"Test #2 Not passed"

هيا نركض.

 ~/TON/build/crypto/fift -s lottery-test-suite.fif 

وسنرى أن الاختبار قد تم تنفيذه مع حدوث خطأ.

[ 1][t 0][1582283084.210902214][words.cpp:3046] lottery-test-suite.fif:67: abort": Test #2 Not passed
[ 1][t 0][1582283084.210941076][fift-main.cpp:196]      Error interpreting file `lottery-test-suite.fif`: error interpreting included file `lottery-test-suite.fif` : lottery-test-suite.fif:67: abort": Test #2 Not passed

في هذه المرحلة lottery-test-suite.fif يجب أن تبدو رابط.

الآن دعونا نضيف المنطق المضاد إلى العقد الذكي lottery-code.fc.

() recv_internal(slice in_msg) impure {
    ;; TODO: implementation 
}

() recv_external(slice in_msg) impure {
    if (slice_empty?(in_msg)) {
        return (); 
    }
    int msg_seqno = in_msg~load_uint(32);
    var ds = begin_parse(get_data());
    int stored_seqno = ds~load_uint(32);
    throw_unless(33, msg_seqno == stored_seqno);
}

В slice in_msg تكمن الرسالة التي نرسلها.

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

بعد ذلك، نقوم بتحليل الرسالة. in_msg~load_uint(32) الأحمال رقم 165، 32 بت unsigned int من الرسالة المرسلة.

بعد ذلك، نقوم بتحميل 32 بت من مخزن العقد الذكي. نتحقق من أن الرقم الذي تم تحميله يطابق الرقم الذي تم تمريره، وإذا لم يكن كذلك، فإننا نطرح استثناءً. في حالتنا، نظرًا لأننا نمرر في حالة عدم تطابق، فيجب طرح استثناء.

الآن دعونا تجميع.

~/TON/build/crypto/func -APSR -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc 

انسخ الكود الناتج إلى lottery-test-suite.fif، ولا ننسى استبدال السطر الأخير.

التأكد من اجتياز الاختبار:

~/TON/build/crypto/fift -s lottery-test-suite.fif

هنا يمكنك رؤية الالتزام المقابل مع النتائج الحالية.

لاحظ أنه من غير المناسب نسخ كود العقد الذكي المترجم باستمرار إلى ملف الاختبار، لذلك سنكتب نصًا يكتب الكود إلى ثابت لنا، وسنقوم ببساطة بتوصيل الكود المترجم باختباراتنا باستخدام "include".

قم بإنشاء ملف في مجلد المشروع build.sh مع المحتوى التالي.

#!/bin/bash

~/TON/build/crypto/func -SPA -R -o lottery-compiled.fif ~/TON/ton/crypto/smartcont/stdlib.fc ./lottery-code.fc

دعونا نجعلها قابلة للتنفيذ.

chmod +x ./build.sh

الآن، يكفي تشغيل البرنامج النصي الخاص بنا لتجميع العقد. لكن بالإضافة إلى ذلك، علينا كتابته في صورة ثابت code. لذلك سوف نقوم بإنشاء ملف جديد lotter-compiled-for-test.fif، والتي سوف نقوم بإدراجها في الملف lottery-test-suite.fif.

دعونا نضيف التعليمات البرمجية إلى البرنامج النصي sh الذي سيؤدي ببساطة إلى تكرار الملف المترجم فيه lotter-compiled-for-test.fif وتغيير السطر الأخير فيه.

# copy and change for test 
cp lottery-compiled.fif lottery-compiled-for-test.fif
sed '$d' lottery-compiled-for-test.fif > test.fif
rm lottery-compiled-for-test.fif
mv test.fif lottery-compiled-for-test.fif
echo -n "}END>s constant code" >> lottery-compiled-for-test.fif

الآن، للتحقق، قم بتشغيل البرنامج النصي الناتج وسنقوم بإنشاء ملف lottery-compiled-for-test.fifوالتي سوف ندرجها في موقعنا lottery-test-suite.fif

В lottery-test-suite.fif قم بإزالة رمز العقد وأضف السطر "lottery-compiled-for-test.fif" include.

قم بإجراء الاختبارات لمعرفة ما إذا كانت ستنجح.

~/TON/build/crypto/fift -s lottery-test-suite.fif

عظيم، الآن لأتمتة إطلاق الاختبارات، فلنقم بإنشاء ملف test.sh، والذي سيتم تنفيذه أولاً build.shثم قم بإجراء الاختبارات.

touch test.sh
chmod +x test.sh

نكتب في الداخل

./build.sh 

echo "nCompilation completedn"

export FIFTPATH=~/TON/ton/crypto/fift/lib
~/TON/build/crypto/fift -s lottery-test-suite.fif

دعنا نفعل test.sh وتشغيل للتأكد من نجاح الاختبارات.

chmod +x ./test.sh
./test.sh

نتحقق من تجميع العقد وتنفيذ الاختبارات.

عظيم، الآن عند بدء التشغيل test.sh سيتم تجميع الاختبارات وتشغيلها على الفور. هنا رابط ل ارتكب.

حسنًا، قبل أن نواصل، دعونا نفعل شيئًا آخر من أجل الراحة.

لنقم بإنشاء مجلد build حيث سنقوم بتخزين العقد المنسوخ واستنساخه مكتوبًا في ملف ثابت lottery-compiled.fif, lottery-compiled-for-test.fif. سنقوم أيضًا بإنشاء مجلد test أين سيتم تخزين ملف الاختبار lottery-test-suite.fif وربما ملفات الدعم الأخرى. رابط إلى التغييرات ذات الصلة.

دعونا نواصل تطوير العقد الذكي.

يجب أن يكون التالي اختبارًا للتحقق من استلام الرسالة وتحديث العداد في المتجر عندما نرسل الرقم الصحيح. لكننا سنفعل ذلك لاحقًا.

الآن دعونا نفكر في بنية البيانات والبيانات التي يجب تخزينها في العقد الذكي.

سأصف كل ما لدينا.

`seqno` 32-х битное целое положительное число счетчик. 

`pubkey` 256-ти битное целое положительное число публичный ключ, с помощью которого, мы будем проверять подпись отправленного извне сообщения, о чем ниже. 

`order_seqno` 32-х битное целое положительное число хранит счетчик количества ставок. 

`number_of_wins` 32-х битное целое положительное число хранит  количество побед. 

`incoming_amount` тип данных Gram (первые 4 бита отвечает за длину), хранит общее количество грамов, которые были отправлены на контртакт. 

`outgoing_amount` общее количество грамов, которое было отправлено победителям. 

`owner_wc` номер воркчейна, 32-х битное (в некоторых местах написано, что 8-ми битное) целое число. В данный момент всего два -1 и 0. 

`owner_account_id` 256-ти битное целое положительное число, адрес контракта в текущем воркчейне. 

`orders` переменная типа словарь, хранит последние двадцать ставок. 

والخطوة التالية هي كتابة وظيفتين. دعنا نتصل بالأول pack_state()، والتي ستحزم البيانات للتخزين اللاحق في مخزن العقد الذكي. والثاني سوف نتصل به unpack_state() سوف يقرأ ويعيد البيانات من التخزين.

_ pack_state(int seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) inline_ref {
    return begin_cell()
            .store_uint(seqno, 32)
            .store_uint(pubkey, 256)
            .store_uint(order_seqno, 32)
            .store_uint(number_of_wins, 32)
            .store_grams(incoming_amount)
            .store_grams(outgoing_amount)
            .store_int(owner_wc, 32)
            .store_uint(owner_account_id, 256)
            .store_dict(orders)
            .end_cell();
}

_ unpack_state() inline_ref {
    var ds = begin_parse(get_data());
    var unpacked = (ds~load_uint(32), ds~load_uint(256), ds~load_uint(32), ds~load_uint(32), ds~load_grams(), ds~load_grams(), ds~load_int(32), ds~load_uint(256), ds~load_dict());
    ds.end_parse();
    return unpacked;
}

نضيف هاتين الوظيفتين في بداية العقد الذكي. اتضح مثله نتيجة وسيطة.

لحفظ البيانات، سوف تحتاج إلى استدعاء الوظيفة المضمنة set_data() وسوف يكتب البيانات من pack_state() في تخزين العقد الذكي.

cell packed_state = pack_state(arg_1, .., arg_n); 
set_data(packed_state);

والآن بعد أن أصبح لدينا وظائف ملائمة لكتابة البيانات وقراءتها، يمكننا المضي قدمًا.

نحتاج إلى التحقق من أن الرسالة الواردة موقعة من مالك العقد (أو مستخدم آخر لديه حق الوصول إلى المفتاح الخاص).

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

قبل المتابعة، دعونا ننشئ مفتاحًا خاصًا ونكتبه إليه test/keys/owner.pk. للقيام بذلك، قم بتشغيل Fift في الوضع التفاعلي وقم بتشغيل أربعة أوامر.

`newkeypair` генерация публичного и приватного ключа и запись их в стек. 

`drop` удаления из стека верхнего элемента (в данном случае публичный ключ)  

`.s` просто посмотреть что лежит в стеке в данный момент 

`"owner.pk" B>file` запись приватного ключа в файл с именем `owner.pk`. 

`bye` завершает работу с Fift. 

لنقم بإنشاء مجلد keys داخل مجلد test واكتب المفتاح الخاص هناك.

mkdir test/keys
cd test/keys
~/TON/build/crypto/fift -i 
newkeypair
 ok
.s 
BYTES:128DB222CEB6CF5722021C3F21D4DF391CE6D5F70C874097E28D06FCE9FD6917 BYTES:DD0A81AAF5C07AAAA0C7772BB274E494E93BB0123AA1B29ECE7D42AE45184128 
drop 
 ok
"owner.pk" B>file
 ok
bye

نرى الملف في المجلد الحالي owner.pk.

نقوم بإزالة المفتاح العام من المكدس، وعندما نحتاج إليه يمكننا الحصول عليه من المفتاح الخاص.

الآن نحن بحاجة إلى كتابة التحقق من التوقيع. لنبدأ بالاختبار. أولاً، نقرأ المفتاح الخاص من الملف باستخدام الوظيفة file>B واكتبها في متغير owner_private_key، ثم استخدم الدالة priv>pub قم بتحويل المفتاح الخاص إلى عام واكتب النتيجة إليه owner_public_key.

variable owner_private_key
variable owner_public_key 

"./keys/owner.pk" file>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !

نحن بحاجة إلى كلا المفتاحين.

نقوم بتهيئة تخزين العقد الذكي ببيانات عشوائية بنفس التسلسل كما في الوظيفة pack_state()والكتابة إلى متغير storage.

variable owner_private_key
variable owner_public_key 
variable orders
variable owner_wc
variable owner_account_id

"./keys/owner.pk" file>B owner_private_key !
owner_private_key @ priv>pub owner_public_key !
dictnew orders !
0 owner_wc !
0 owner_account_id !

<b 0 32 u, owner_public_key @ B, 0 32 u, 0 32 u, 0 Gram, 0 Gram, owner_wc @ 32 i, owner_account_id @ 256 u,  orders @ dict, b> storage !

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

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

variable message_to_sign
variable message_to_send
variable signature
<b 0 32 u, b> message_to_sign !
message_to_sign @ hashu owner_private_key @ ed25519_sign_uint signature !
<b signature @ B, 0 32 u, b> <s  message_to_send !  

ونتيجة لذلك، تتم كتابة الرسالة التي سنرسلها إلى العقد الذكي إلى المتغير message_to_send، حول الوظائف hashu, ed25519_sign_uint يمكنك القراءة في الوثائق الخمس.

وندعو مرة أخرى لإجراء الاختبار.

message_to_send @ 
recv_external 
code 
storage @
c7
runvmctx

مثل يجب أن يبدو ملف الاختبار في هذه المرحلة.

لنجري الاختبار وسيفشل، لذلك سنقوم بتغيير العقد الذكي حتى يتمكن من تلقي رسائل بهذا التنسيق والتحقق من التوقيع.

أولا، نقرأ 512 بت من التوقيع من الرسالة ونكتبها إلى متغير، ثم نقرأ 32 بت من متغير العداد.

نظرًا لأن لدينا وظيفة لقراءة البيانات من مخزن العقد الذكي، فسوف نستخدمها.

مزيد من التحقق من العداد المنقول مع التخزين والتحقق من التوقيع. إذا كان هناك شيء غير متطابق، فإننا نطرح استثناءً بالرمز المقابل.

var signature = in_msg~load_bits(512);
var message = in_msg;
int msg_seqno = message~load_uint(32);
(int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, check_signature(slice_hash(in_msg), signature, pubkey));

الالتزام ذو الصلة هنا.

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

في الاختبار الثاني، سنضيف توقيع الرسالة ونغير مساحة تخزين العقد الذكي. مثل يبدو وكأنه ملف مع الاختبارات في الوقت الراهن.

لنكتب الاختبار الرابع الذي سنرسل فيه رسالة موقعة بالمفتاح الخاص لشخص آخر. لنقم بإنشاء مفتاح خاص آخر وحفظه في ملف not-owner.pk. لنوقع الرسالة بهذا المفتاح الخاص. لنجري الاختبارات ونتأكد من اجتياز جميع الاختبارات. يقترف بهذه اللحظة.

الآن يمكننا أخيرًا الانتقال إلى تنفيذ منطق العقد الذكي.
В recv_external() سوف نتلقى نوعين من الرسائل.

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

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

لنبدأ بالأول. دعونا أولاً نكتب اختبارًا للتحقق من أنه بعد إرسال الرسالة، قام العقد الذكي بحفظ العنوان الجديد في وحدة التخزين. لاحظ أنه في الرسالة، بالإضافة إلى العداد والعنوان الجديد، نرسل أيضًا action عدد صحيح غير سالب 7 بت، اعتمادًا على ذلك، سنختار كيفية معالجة الرسالة في العقد الذكي.

<b 0 32 u, 1 @ 7 u, new_owner_wc @  32 i, new_owner_account_id @ 256 u, b> message_to_sign !

في الاختبار، يمكنك أن ترى كيف يتم إلغاء تحقيق تخزين العقد الذكي storage في الخمس. تم وصف إلغاء التسلسل المتغير في وثائق Fift.

رابط الالتزام مع إضافة اختبار.

دعونا نجري الاختبار ونرى ما إذا كان سيفشل. الآن دعونا نضيف منطقًا لتغيير عنوان مالك اليانصيب.

في العقد الذكي، نواصل التحليل message، اقرأ في action. تذكر أن لدينا اثنان action: تغيير العنوان وإرسال غرام.

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

لنكتب الآن منطق إرسال العدد المحدد من الجرامات إلى العنوان المحفوظ مسبقًا.

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

الآن دعونا نضيف بعض التعليمات البرمجية. أولاً، دعونا نكتب طريقتين مساعدتين. طريقة الحصول الأولى هي معرفة الرصيد الحالي للعقد الذكي.

int balance() inline_ref method_id {
    return get_balance().pair_first();
}

والثاني هو إرسال الجرام إلى عقد ذكي آخر. لقد قمت بنسخ هذه الطريقة بالكامل من عقد ذكي آخر.

() send_grams(int wc, int addr, int grams) impure {
    ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000
    cell msg = begin_cell()
    ;;  .store_uint(0, 1) ;; 0 <= format indicator int_msg_info$0 
    ;;  .store_uint(1, 1) ;; 1 <= ihr disabled
    ;;  .store_uint(1, 1) ;; 1 <= bounce = true
    ;;  .store_uint(0, 1) ;; 0 <= bounced = false
    ;;  .store_uint(4, 5)  ;; 00100 <= address flags, anycast = false, 8-bit workchain
        .store_uint (196, 9)
        .store_int(wc, 8)
        .store_uint(addr, 256)
        .store_grams(grams)
        .store_uint(0, 107) ;; 106 zeroes +  0 as an indicator that there is no cell with the data.
        .end_cell(); 
    send_raw_message(msg, 3); ;; mode, 2 for ignoring errors, 1 for sender pays fees, 64 for returning inbound message value
}

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

int amount_to_send = message~load_grams();
throw_if(36, amount_to_send + 500000000 > balance());
accept_message();
send_grams(owner_wc, owner_account_id, amount_to_send);
set_data(pack_state(stored_seqno + 1, pubkey, order_seqno, number_of_wins, incoming_amount, outgoing_amount, owner_wc, owner_account_id, orders));

مثل يبدو وكأنه عقد ذكي في الوقت الراهن. دعونا نجري الاختبارات ونتأكد من اجتيازها.

بالمناسبة، يتم خصم عمولة مقابل الرسالة المعالجة من عقد ذكي في كل مرة. لكي تتمكن رسائل العقد الذكي من تنفيذ الطلب، بعد إجراء الفحوصات الأساسية، يجب عليك الاتصال accept_message().

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

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

يتكون عنوان العقد الذكي من رقمين، عدد صحيح 32 بت مسؤول عن سلسلة العمل ورقم حساب فريد عدد صحيح غير سالب 256 بت في سلسلة العمل هذه. على سبيل المثال، -1 و12345، سيتم حفظ هذا العنوان في ملف.

لقد قمت بنسخ وظيفة حفظ العنوان من TonUtil.fif.

// ( wc addr fname -- )  Save address to file in 36-byte format
{ -rot 256 u>B swap 32 i>B B+ swap B>file } : save-address

دعونا نلقي نظرة على كيفية عمل الدالة، وهذا سيعطي فهمًا لكيفية عمل Fift. قم بتشغيل Five في الوضع التفاعلي.

~/TON/build/crypto/fift -i 

أولاً ندفع -1، 12345 واسم الملف المستقبلي "sender.addr" إلى المكدس:

-1 12345 "sender.addr" 

والخطوة التالية هي تنفيذ الوظيفة -rot، الذي يقوم بإزاحة المكدس بحيث يكون الرقم الفريد للعقد الذكي في الجزء العلوي من المكدس:

"sender.addr" -1 12345

256 u>B تحويل عدد صحيح غير سالب 256 بت إلى بايت.

"sender.addr" -1 BYTES:0000000000000000000000000000000000000000000000000000000000003039

swap مبادلة العنصرين العلويين من المكدس.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 -1

32 i>B تحويل عدد صحيح 32 بت إلى بايت.

"sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039 BYTES:FFFFFFFF

B+ يربط تسلسلين من البايتات.

 "sender.addr" BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF

ثانية swap.

BYTES:0000000000000000000000000000000000000000000000000000000000003039FFFFFFFF "sender.addr" 

وأخيرًا، تتم كتابة البايتات إلى الملف B>file. بعد ذلك، مكدسنا فارغ. وقف Fift. تم إنشاء الملف في المجلد الحالي sender.addr. انقل الملف إلى المجلد الذي تم إنشاؤه test/addresses/.

لنكتب اختبارًا بسيطًا يرسل الجرام إلى عقد ذكي. هنا الالتزام.

الآن دعونا نتعامل مع منطق اليانصيب.

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

نتحقق مما إذا كان الرصيد أقل من نصف جرام، ثم نقبل الرسالة ببساطة ونتجاهلها.

بعد ذلك، نقوم بتحليل عنوان العقد الذكي الذي جاءت منه الرسالة.

نقرأ البيانات من المخزن ثم نحذف الرهانات القديمة من السجل إذا كان هناك أكثر من عشرين منها. للراحة، كتبت ثلاث وظائف إضافية pack_order(), unpack_order(), remove_old_orders().

ثم ننظر إذا كان الرصيد لا يكفي للدفع، فإننا نعتبر أن هذا ليس رهانًا، ولكنه تجديد وحفظ التجديد في orders.

ثم أخيرًا جوهر العقد الذكي.

أولاً، إذا خسر اللاعب، فإننا نحفظه في سجل الرهانات وإذا كان المبلغ أكثر من 3 جرام، نرسل 1/3 إلى صاحب العقد الذكي.

إذا فاز اللاعب، فإننا نرسل مبلغًا مضاعفًا إلى عنوان اللاعب ثم نحفظ المعلومات المتعلقة بالرهان في السجل.

() recv_internal(int order_amount, cell in_msg_cell, slice in_msg) impure {
    var cs = in_msg_cell.begin_parse();
    int flags = cs~load_uint(4);  ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
    if (flags & 1) { ;; ignore bounced
        return ();
    }
    if (order_amount < 500000000) { ;; just receive grams without changing state 
        return ();
    }
    slice src_addr_slice = cs~load_msg_addr();
    (int src_wc, int src_addr) = parse_std_addr(src_addr_slice);
    (int stored_seqno, int pubkey, int order_seqno, int number_of_wins, int incoming_amount, int outgoing_amount, int owner_wc, int owner_account_id, cell orders) = unpack_state();
    orders = remove_old_orders(orders, order_seqno);
    if (balance() < 2 * order_amount + 500000000) { ;; not enough grams to pay the bet back, so this is re-fill
        builder order = pack_order(order_seqno, 1, now(), order_amount, src_wc, src_addr);
        orders~udict_set_builder(32, order_seqno, order);
        set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins, incoming_amount + order_amount, outgoing_amount, owner_wc, owner_account_id, orders));
        return ();
    }
    if (rand(10) >= 4) {
        builder order = pack_order(order_seqno, 3, now(), order_amount, src_wc, src_addr);
        orders~udict_set_builder(32, order_seqno, order);
        set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins, incoming_amount + order_amount, outgoing_amount, owner_wc, owner_account_id, orders));
        if (order_amount > 3000000000) {
            send_grams(owner_wc, owner_account_id, order_amount / 3);
        }
        return ();
    }
    send_grams(src_wc, src_addr, 2 * order_amount);
    builder order = pack_order(order_seqno, 2, now(), order_amount, src_wc, src_addr);
    orders~udict_set_builder(32, order_seqno, order);
    set_data(pack_state(stored_seqno, pubkey, order_seqno + 1, number_of_wins + 1, incoming_amount, outgoing_amount + 2 * order_amount, owner_wc, owner_account_id, orders));
}

هذا هو. الالتزام ذو الصلة.

الآن يبقى الأمر بسيطًا، سنجعل طرق الحصول حتى يمكن الحصول على معلومات حول حالة العقد من العالم الخارجي (في الواقع، قراءة البيانات من مخزن العقد الذكي).

إضافة الحصول على الأساليب. سنكتب أدناه عن كيفية الحصول على معلومات حول العقد الذكي.

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

والخطوة التالية هي نشر العقد الذكي. لنقم بإنشاء مجلد requests.

أخذت رمز النشر كأساس simple-wallet-code.fc التي يمكن أن تجد في المستودع الرسمي .

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

بعد ذلك، نقوم بتنفيذ رمز النشر والحصول على lottery-query.boc الملف وعنوان العقد الذكي.

~/TON/build/crypto/fift -s requests/new-lottery.fif 0

لا تنس حفظ الملفات التي تم إنشاؤها: lottery-query.boc, lottery.addr, lottery.pk.

من بين أمور أخرى، سنرى في سجلات التنفيذ عنوان العقد الذكي.

new wallet address = 0:044910149dbeaf8eadbb2b28722e7d6a2dc6e264ec2f1d9bebd6fb209079bc2a 
(Saving address to file lottery.addr)
Non-bounceable address (for init): 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd
Bounceable address (for later access): kQAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8KpFY

من أجل الفائدة، سوف نقوم بتقديم طلب إلى TON

$ ./lite-client/lite-client -C ton-lite-client-test1.config.json 
getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

وسنرى أن الحساب بهذا العنوان فارغ.

account state is empty

نرسل إلى العنوان 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd 2 جرام وبعد ثواني قليلة نقوم بتنفيذ نفس الأمر. لإرسال غرام أستخدمه المحفظة الرسمية، ويمكنك طلب اختبار الجرام من أحد الأشخاص في الدردشة، وهو ما سأتحدث عنه في نهاية المقال.

> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

يبحث عن غير مهيأ (state:account_uninit) عقد ذكي بهذا العنوان ورصيد قدره 1 نانوجرام.

account state is (account
  addr:(addr_std
    anycast:nothing workchain_id:0 address:x044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A)
  storage_stat:(storage_info
    used:(storage_used
      cells:(var_uint len:1 value:1)
      bits:(var_uint len:1 value:103)
      public_cells:(var_uint len:0 value:0)) last_paid:1583257959
    due_payment:nothing)
  storage:(account_storage last_trans_lt:3825478000002
    balance:(currencies
      grams:(nanograms
        amount:(var_uint len:4 value:2000000000))
      other:(extra_currencies
        dict:hme_empty))
    state:account_uninit))
x{C00044910149DBEAF8EADBB2B28722E7D6A2DC6E264EC2F1D9BEBD6FB209079BC2A20259C2F2F4CB3800000DEAC10776091DCD650004_}
last transaction lt = 3825478000001 hash = B043616AE016682699477FFF01E6E903878CDFD6846042BA1BFC64775E7AC6C4
account balance is 2000000000ng

الآن دعونا ننشر العقد الذكي. لنبدأ العميل البسيط وننفذه.

> sendfile lottery-query.boc
[ 1][t 2][1583008371.631410122][lite-client.cpp:966][!testnode] sending query from file lottery-query.boc
[ 3][t 1][1583008371.828550100][lite-client.cpp:976][!query]    external message status is 1 

دعونا نتحقق من نشر العقد.

> last
> getaccount 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd

من بين أمور أخرى، نحصل عليها

  storage:(account_storage last_trans_lt:3825499000002
    balance:(currencies
      grams:(nanograms
        amount:(var_uint len:4 value:1987150999))
      other:(extra_currencies
        dict:hme_empty))
    state:(account_active

نحن نرى ذلك account_active.

الالتزام ذو الصلة بالتغييرات هنا.

لنقم الآن بإنشاء طلبات للتفاعل مع العقد الذكي.

وبشكل أدق، سنترك الأول لتغيير العنوان كعمل مستقل، والثاني لإرسال الجرام إلى عنوان المالك. في الواقع، سنحتاج إلى القيام بنفس الشيء كما في اختبار إرسال الجرامات.

هذه هي الرسالة التي سنرسلها إلى العقد الذكي، حيث msg_seqno 165 action 2 و 9.5 جرام للإرسال.

<b 165 32 u, 2 7 u, 9500000000 Gram, b>

لا تنسَ توقيع الرسالة باستخدام مفتاحك الخاص lottery.pk، والتي تم إنشاؤها مسبقًا عند إنشاء عقد ذكي. هنا هو الالتزام ذات الصلة.

الحصول على المعلومات من عقد ذكي باستخدام أساليب الحصول

الآن دعونا نلقي نظرة على كيفية تشغيل طرق الحصول على العقد الذكي.

نطلق lite-client وقم بتشغيل طرق الحصول التي كتبناها.

$ ./lite-client/lite-client -C ton-lite-client-test1.config.json
> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd balance
arguments:  [ 104128 ] 
result:  [ 64633878952 ] 
...

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

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_seqno
...
arguments:  [ 77871 ] 
result:  [ 1 ] 

سوف نطلب تاريخ الأسعار.

> runmethod 0QAESRAUnb6vjq27KyhyLn1qLcbiZOwvHZvr1vsgkHm8Ksyd get_orders
...
arguments:  [ 67442 ] 
result:  [ ([0 1 1583258284 10000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [1 3 1583258347 4000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308] [2 1 1583259901 50000000000 0 74649920601963823558742197308127565167945016780694342660493511643532213172308]) ] 

سوف نستخدم lite-client ونحصل على طرق لعرض معلومات حول العقد الذكي على الموقع.

عرض بيانات العقد الذكي على الموقع

لقد قمت بكتابة موقع ويب بسيط بلغة Python لعرض بيانات العقود الذكية بطريقة مناسبة. هنا لن أتطرق إليه بالتفصيل وأنشر الموقع في التزام واحد.

يتم تقديم الطلبات إلى TON من Python من خلال lite-client. للراحة، تم تجميع الموقع في Docker ونشره على Google Cloud. نهاية لهذه الغاية.

محاولة

الآن دعونا نحاول إرسال الجرام إلى هناك للتجديد منه محفظة. سوف نرسل 40 جراما. ودعنا نراهن على الوضوح. نرى أن الموقع يعرض تاريخ الرهانات ونسبة الفوز الحالية وغيرها من المعلومات المفيدة.

نحن نرىأننا فزنا بالأولى وخسرنا الثانية.

خاتمة

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

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

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

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

هناك أيضًا Libra من Facebook، والتي لديها جمهور محتمل أكبر من المستخدمين من TON. لا أعرف شيئًا تقريبًا عن Libra، نظرًا لأن منتدى الأنشطة به نشاط أكثر بكثير من مجتمع TON. على الرغم من أن المطورين ومجتمع TON يشبهون مترو الأنفاق، وهو أمر رائع أيضًا.

مراجع

  1. الوثائق الرسمية على TON: https://test.ton.org
  2. مستودع TON الرسمي: https://github.com/ton-blockchain/ton
  3. المحفظة الرسمية لمنصات مختلفة: https://wallet.ton.org
  4. مستودع العقد الذكي من هذه المقالة: https://github.com/raiym/astonished
  5. رابط موقع العقد الذكي: https://ton-lottery.appspot.com
  6. مستودع الامتداد لـ Visual Studio Code لـ FunC: https://github.com/raiym/func-visual-studio-plugin
  7. الدردشة حول TON في Telegram، مما ساعد كثيرًا في التعرف عليها في المرحلة الأولية. أعتقد أنه لن يكون من الخطأ أن أقول إن هناك كل من كتب شيئًا لـ TON. يمكنك أيضًا طلب اختبار الجرام هناك. https://t.me/tondev_ru
  8. محادثة أخرى حول TON وجدت فيها معلومات مفيدة: https://t.me/TONgramDev
  9. المرحلة الأولى من المسابقة: https://contest.com/blockchain
  10. الجولة الثانية من المسابقة: https://contest.com/blockchain-2

المصدر: www.habr.com

إضافة تعليق