مقدمة إلى Bitcoin Script
الصفحة الرئيسية
المقالات
مقدمة إلى Bitcoin Script

مقدمة إلى Bitcoin Script

متقدم
تاريخ النشر Jul 10, 2020تاريخ التحديث Jan 18, 2022
13m

المقدمة

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

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

في هذه المقالة، سنلقي نظرة عن كثب على Script، لغة البرمجة التي تُفسرها العُقد على شبكة بيتكوين. تتحكم لغة البرنامج النصي Script في آليات فتح/غلق الخزائن المذكورة مسبقاً.


ما آلية عمل البيتكوين؟

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

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

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


فهم التراكم على شبكة بيتكوين

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


وتُضاف العناصر أ، ب، ج و"تنبثق" من المكدس.


يمكننا التمييز بين البيانات (أشياء مثل التوقيعات، و التجزئة، والمفاتيح العامة) والتعليمات (أو رموز التشغيل). تزيل التعليمات البيانات وتقوم بشيء ما. فيما يلي مثال بسيط للغاية لما يبدو عليه البرنامج النصي:
<xyz> <md5 hasher> <d16fb36f0911f878998c136191af705e> <check if equal>
سنجد البيانات باللون الأحمر، ورموز التشغيل باللون الأزرق. نقرأ من اليسار إلى اليمين، لذلك نضع التسلسل <xyz> في المكدس أولاً. ويليه رمز التشغيل <md5 hasher>. وهذا الرمز غير موجود على شبكة بيتكوين، ولكن لنفترض أنه يزيل العنصر الموجود أعلى المكدس (<xyz>) ويقوم بتجزئته باستخدام خوارزمية MD5. ثم تُضاف المخرجات مرة أخرى إلى المكدس. وتكون المخرجات هنا d16fb36f0911f878998c136191af705e.
يا لها من مصادفة! والعنصر التالي الذي سيتم إضافته هو <d16fb36f0911f878998c136191af705e>، وبذلك يتضمن المكدس عنصرين متطابقين. وأخيراً، يُظهر الرمز <check if equal> عنصرين من الأعلى ويتحقق مما إذا كانا متطابقين. وإذا كانا متطابقين، فيضيف <1> إلى المكدس. في حالة عدم التطابق، فيضيف <0>
لقد وصلنا إلى نهاية قائمة التعليمات. قد يفشل البرنامج النصي في حالتين – إذا كان العنصر المتبقي صفر، أو إذا تسبب أحد عناصر التشغيل في تعرضه إلى الفشل عندما لا تتحقق بعض الشروط. لم يكن لدينا أي عناصر تشغيل في هذا المثال، وأنتهي الأمر بعنصر غير صفري (<1>)، لذلك كان البرنامج النصي صحيحاً. تنطبق هذه القواعد على المعاملات الحقيقية بشبكة بيتكوين أيضاً.

كان هذا مجرد برنامج افتراضي. لنلقِ نظرة على بعض البرامج الفعلية الآن.


Pay-to-Pubkey (P2PK)

يعد Pay-to-Pubkey (P2PK) سهل وبسيط للغاية. فهو يتضمن عملية حجز الأموال لمفتاح عام معين. إذا كنت ترغب في استلام الأموال بهذه الطريقة، فعليك تزويد المرسل بمفتاحك العام، على عكس عنوان شبكة البيتكوين. 

كانت المعاملة الأولى بين ساتوشي ناكاموتو وهال فيني خلال عام 2009 معاملة P2PK. وتم استخدام هذه البنية بشكل كبير في بداية عهد البيتكوين، ولكن في الوقت الحاضر، حلت معاملات Pay-to-Pubkey-Hash (P2PKH) محلها بدرجة كبيرة. 
يتبع البرنامج النصي لحجز المخرجات الخاص بمعاملة P2PK التنسيق الآتي <public key> OP_CHECKSIG. الأمر في غاية البساطة. لعلك خمنت أن رمز OP_CHECKSIG يتحقق من التوقيع مقابل المفتاح العام المقدم. وعلى هذا النحو، scriptSig سيكون <signature> بسيطاً. تذكر أن scriptSig يمثل المفتاح الخاص بالقفل.



لن يكون الأمر أبسط من ذلك. يُضاف التوقيع إلى المكدس، ويليه المفتاح العام. ثم يقوم الرمز OP_CHECKSIGبإظهارهما والتحقق من التوقيع في ضوء المفتاح العام. وإذا كانا متطابقين، فيضيف<1> إلى المكدس. بخلاف ذلك، يضيف <0>.

لبعض الأسباب التي سنقوم بتوضيحها في القسم التالي، لم يعد P2PK مُستخدماً الآن.


Pay-to-Pubkey-Hash (P2PKH)

يعد Pay-to-Pubkey-Hash (P2PKH) هو النوع الأكثر شيوعاً من المعاملات. ما لم تبذل قصارى جهدك لتنزيل برنامج قديم، فمن المحتمل أن تقوم محفظتك بذلك بشكل افتراضي.

فيما يلي scriptPubKey في معاملة P2PKH:

OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG

قبل أن نستعرض scriptSig، دعونا نتناول ما ستفعله رموز التشغيل الجديدة:


OP_DUP

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


OP_HASH160

يُظهر ذلك العنصر الأول وتجزئته مرتين. سيتم تجزئة الجولة الأولى باستخدام خوارزمية فهرس تجزئة 256. ثم يتم تجزئة مخرجات فهرس تجزئة 256 باستخدام خوارزمية RIPEMD-160. ثم تُضاف المخرجات الناتجة مرة أخرى إلى المكدس.


OP_EQUALVERIFY

يجمع OP_EQUALVERIFY عنصرين تشغيل آخرين –OP_EQUAL وOP_VERIFY. يُظهر OP_EQUAL عنصرين ويتحقق من مدى تطابقهما. إذا كانا متطابقين، فيضيف 1 إلى المكدس. في حالة عدم التطابق، سيضيف 0. يُظهر OP_VERIFY العنصر العلوي ويتحقق من صحته. (أي أنه، غير صفري). إذا كان غير صحيحاً، ستفشل المعاملة. عن دمجهما معاً، يؤدي OP_EQUALVERIFY إلى فشل المعاملة في حالة عدم تطابق العنصرين بالأعلى.

هذه المرة، يبدو scriptSig كما يلي:

<signature> <public key>

ستحتاج إلى توفير التوقيع والمفتاح العام المقابل لإلغاء حجز مخرجات P2PKH.



يمكنك الاطلاع على ما يحدث في الصور المتحركة (GIF). لا يختلف الأمر كثيراً عن البرنامج النصي P2PK. ونضيف فقط خطوة أخرى للتحقق من أن المفتاح العام مطابق للتجزئة في البرنامج النصي.

مع ذلك، هناك ثمة أمر يجب ملاحظته. في البرنامج النصي لحجز مخرجات P2PKH، يكون المفتاح العام غير مرئياً – حيث يمكننا فقط رؤية تجزئته. إذا انتقلنا إلى مستكشف سلسلة البلوكشين وألقينا نظرة على مخرجات P2PKH غير المُنفقة، لن نستطيع تحديد المفتاح العام. يمكن معرفته فقط عندما يقرر المستلم تحويل الأموال.
يقدم ذلك بعض المزايا. الأولى هي أن تمرير تجزئة المفتاح العام يكون أيسر بكثير من المفتاح العام الكامل. ولهذا السبب تحديداً أطلقها ساتوشي عام 2009. تجزئة المفتاح العام هي ما نُشير إليها بعنوان بيتكوين الآن.
والميزة الثانية هي أن تجزئة المفتاح العام قد توفر طبقة أمان إضافية لمواجهة الحوسبة الكمية. نظراً لعدم معرفة المفتاح العام حتى يتم إنفاق الأموال، يصعب على الآخرين حوسبة المفتاح الخاص. لذلك يتعين عليهم إلغاء جولتي التجزئة (خوارزمية RIPEMD-160 وخوارزمية فهرس تجزئة 256) للحصول عليه.



Pay-to-Script-Hash (P2SH)

Pay-to-Script-Hash (P2SH) هي تطوير مثير للاهتمام بالنسبة لبيتكوين. فهي تسمح للمرسل بحجز الأموال للتجزئة بالبرنامج النصي – حيث لا تحتاج لمعرفة ما يفعله البرنامج النصي بالفعل. فيما يلي التجزئة باستخدام خوارزمية فهرس تجزئة 256:

e145fe9ed5c23aa71fdb443de00c7d9b4a69f8a27a2e4fbb1fe1d0dbfb6583f1

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

تم إنشاء التجزئة السابقة من البرنامج النصي التالي:

<multiply by 2> <4> <check if equal>

إذا كنت ترغب في إنفاق العملات المرتبطة بـ scriptPubKey، فعليك توفير هذه الأوامر. بالإضافة إلى scriptSig يجعل البرنامج النصي المكتمل صحيحاً عند التقييم. في هذا المثال، أنت تستخدم العنصر <multiply by 2> للحصول على النتيجة <4>. وبالتأكيد، هذا يعني أن scriptSig الخاص بنا هو مجرد <2>.

في الواقع، يكون scriptPubKey لمخرجات P2SH كما يلي:

OP_HASH160 <redeemScript hash> OP_EQUAL

لا يوجد عناصر تشغيل جديدة هنا. ولكن لدينا <redeemScript hash> كعنصر جديد. كما يتضح من الاسم، فهو تجزئة البرنامج النصي الذي نحتاج إلى توفيره من أجل استرداد الأموال (وهو يُعرف بـ redeemScript). سيتغير scriptSig بناءً على ما يتضمنه redeemScript. ولكن بشكل عام، ستجد أنه مزيج من التوقيعات والمفاتيح العامة المرفقة، يليه redeemScript (الإجباري):

<signature> <public key> <redeemScript>

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



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

ومع ذلك، لم ننتهي بعد. تتعرف عُقد الشبكة على هذا الهيكل باعتباره P2SH، وتجعل عناصر scriptSig تنتظر في مكدس آخر. وهنا يتم استخدام التوقيع والمفتاح العام.
نتعامل حتى الآن مع redeemScript على أنه عنصر. ولكن الآن، سيتم اعتباره تعليمات، والتي قد تكون أي شيء. لنأخذ مثال  البرنامج النصي لحجز مخرجات P2PKH، الذي يجب أن نقدم له <signature> و<public key> الذي يطابق <public key hash> داخل <redeemScript>.



بمجرد توسيع redeemScript، يمكنك ملاحظة أن هذه الحالة تشبه تماماً معاملة P2PKH العادية. ومن ثم، تستطيع تشغيله مثلما تفعل مع البرنامج العادي.

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

قد يكون P2SH مفيداً بشكل عام لأشياء مثل محفظة متعددة التوقيع أو معاملات متوافقة مع SegWit. قد يكون حجم معاملات التوقيعات المتعددة كبير جداً وذلك لأنها قد تتطلب مفاتيح متعددة. قبل تطبيق برنامج Pay-to-Script-Hash، يتعين على المرسل تسجيل المفاتيح العامة الممكنة في البرنامج النصي لحجز المخرجات الخاص به. 

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

ويعد توافق SegWit حالة استخدام أخرى يكون فيها P2SH مفيداً (سوف نتناول بمزيد من التفاصيل كيفية اختلاف هيكل المعاملة في القسم التالي). كان SegWit ترقية بروتوكول أدت إلى تغيير تنسيقات المعاملة/الكتلة. نظراً لأنها ترقية اختيارية، لا تتعرف جميع برامج المحفظة على التغييرات.

لا يهم ذلك إذا ربط العملاء تجزئة البرنامج النصي SegWit في P2SH. على غرار جميع المعاملات من هذا النوع، لا تحتاج إلى معرفة ما سيكون عليه فتح redeemScript. 


معاملات SegWit (P2WPKH و P2WSH)

للاطلاع على مقدمة أكثر شمولاً عن SegWit، راجع قسم دليل المبتدئين إلى Segregated Witness.
لفهم تنسيق المعاملة في SegWit، ستحتاج فقط إلى معرفة أنه لم نعد نستخدم scriptSig وscriptPubKey فقط بعد الآن. لدينا الآن حقل جديد يُسمى الإقرار. تم نقل البيانات التي نحتفظ بها في scriptSig إلى الإقرار، وبذلك يصبح scriptSig فارغاً.

إذا رأيت عنوان يبدأ بـ ’bc1‘، هذا ما نسميه SegWit الأصلي (على عكس العنوان المتوافق مع SegWit الذي يبدأ بـ ’3‘ لأنها عناوين P2SH).


Pay-to-Witness-Pubkey-Hash (P2WPKH)

Pay-to-Witness-Pubkey-Hash (P2WPKH) هو إصدار SegWit من P2PKH. ويكون الإقرار الخاص بنا كما يلي:

<signature> <public key>

ستلاحظ أنه يماثل scriptSig من P2PKH. ويصبح هنا scriptSig فارغاً. على الجانب الآخر، يشبه scriptPubKey ما يلي:

<OP_0> <public key hash>

هذا يبدو غريباً، أليس كذلك؟ أين توجد رموز التشغيل التي تسمح لنا بمقارنة التوقيع والمفتاح العام وتجزئته؟

لن نستعرض هنا عناصر تشغيل إضافية، لأن العُقد التي تستقبل المعاملات تَعرف ما يجب القيام به بناءً على فترة <public key hash>. كما أنها ستقوم بحساب الفترة وفهم أنه يجب تشغيلها بنفس أسلوب معاملة P2PKH قديمة جيدة.
لا تعرف العُقد التي لم يتم ترقيتها كيفية تفسير المعاملة بهذه الطريقة، ولكن هذا لا يهم. وفقاً للقواعد القديمة، لا يوجد إقرار، لذلك يتم قراءة scriptSig فارغ وبعض البيانات. كما تعمل على تقييمه واعتباره صحيح – وبذلك، يستطيع أي شخص إنفاق المخرجات. لهذا السبب يعد SegWit ترقية بروتوكول متوافقة مع الإصدارات السابقة.


Pay-to-Witness-Script-Hash (P2WSH)

Pay-to-Witness-Script Hash (P2WSH) هو P2SH جديد. إذا وصلت لهذه المرحلة، لعلك تعرف كيف سيبدو ذلك، ولكن سنتناوله على أية حال. وعادةً ما نقوم بإضافة الإقرار الخاص بنا في scriptSig. في P2WSH التي تربط معاملة P2PKH، على سبيل المثال، ستبدو كما يلي:

<signature 1> <public key>

فيما يلي scriptPubKey:

<OP_0> <script hash>

تنطبق القواعد نفسها. تقرأ عُقد SegWit فترة تجزئة البرنامج النصي وتحدد أنها مخرجات P2WSH، التي سيتم تقييمها بطريقة مشابهة لمخرجات P2SH. بينما تتعرف عليها العُقد القديمة على أنها مخرجات يستطيع أي شخص إنفاقها.


أفكار ختامية

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


نوع البرنامج النصيالوصف

Pay-to-Pubkey (P2PK)

يحجز الأموال لمفتاح عام معين

Pay-to-Pubkey-Hash (P2PKH)

يحجز الأموال لتجزئة مفتاح عام معين (عنوان)

Pay-to-Script-Hash (P2SH)

يحجز الأموال لتجزئة البرنامج النصي التي يجب أن يوفرها المستلم

Pay-to-Witness-Pubkey-Hash (P2WPKH)

إصدار SegWit من P2PK

Pay-to-Witness-Script-Hash (P2WSH)

إصدار SegWit من P2SH


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