<rss
      xmlns:atom="http://www.w3.org/2005/Atom"
      xmlns:media="http://search.yahoo.com/mrss/"
      xmlns:content="http://purl.org/rss/1.0/modules/content/"
      xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
      xmlns:dc="http://purl.org/dc/elements/1.1/"
      version="2.0"
    >
      <channel>
        <title><![CDATA[Learning BTC&NOSTR]]></title>
        <description><![CDATA[สร้างมาเพื่อแชร์เรื่องราวที่ได้ศึกษาจากการเรียนรู้เรื่องของบิตคอยน์และนอสเตอร์ จะพยายามเขียนทุกวันวันละโพสต์เพื่อเป็นการบังคับให้ตัวเองได้ศึกษามันเรื่อย ๆ ทุกวัน ถ้าผิดพลาดตรงไหนรบกวนช่วยแก้ไขกันด้วยนะครับ]]></description>
        <link>https://learnbn.npub.pro/tag/siamstr/</link>
        <atom:link href="https://learnbn.npub.pro/tag/siamstr/rss/" rel="self" type="application/rss+xml"/>
        <itunes:new-feed-url>https://learnbn.npub.pro/tag/siamstr/rss/</itunes:new-feed-url>
        <itunes:author><![CDATA[Learning_BTC&NOSTR]]></itunes:author>
        <itunes:subtitle><![CDATA[สร้างมาเพื่อแชร์เรื่องราวที่ได้ศึกษาจากการเรียนรู้เรื่องของบิตคอยน์และนอสเตอร์ จะพยายามเขียนทุกวันวันละโพสต์เพื่อเป็นการบังคับให้ตัวเองได้ศึกษามันเรื่อย ๆ ทุกวัน ถ้าผิดพลาดตรงไหนรบกวนช่วยแก้ไขกันด้วยนะครับ]]></itunes:subtitle>
        <itunes:type>episodic</itunes:type>
        <itunes:owner>
          <itunes:name><![CDATA[Learning_BTC&NOSTR]]></itunes:name>
          <itunes:email><![CDATA[Learning_BTC&NOSTR]]></itunes:email>
        </itunes:owner>
            
      <pubDate>Fri, 02 Jan 2026 14:28:47 GMT</pubDate>
      <lastBuildDate>Fri, 02 Jan 2026 14:28:47 GMT</lastBuildDate>
      
      <itunes:image href="https://i.imgur.com/e6cvtcn.png" />
      <image>
        <title><![CDATA[Learning BTC&NOSTR]]></title>
        <link>https://learnbn.npub.pro/tag/siamstr/</link>
        <url>https://i.imgur.com/e6cvtcn.png</url>
      </image>
      <item>
      <title><![CDATA[สรุป Mastering Bitcoin บทที่ 7 Authorization and Authentication]]></title>
      <description><![CDATA[ฮี่ ๆๆๆๆๆ สวัสดีปีใหม่คร้าบบบบบ]]></description>
             <itunes:subtitle><![CDATA[ฮี่ ๆๆๆๆๆ สวัสดีปีใหม่คร้าบบบบบ]]></itunes:subtitle>
      <pubDate>Fri, 02 Jan 2026 14:28:47 GMT</pubDate>
      <link>https://learnbn.npub.pro/post/i-0kfbut8da_crrpiaeae/</link>
      <comments>https://learnbn.npub.pro/post/i-0kfbut8da_crrpiaeae/</comments>
      <guid isPermaLink="false">naddr1qq25jtfsddnxya258pzyzhmr2ff8qj2pv4s52q3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65wf0l0hd</guid>
      <category>ลองฟอร์มของไดโน</category>
      
        <media:content url="https://image.nostr.build/2eb9a47ba4fe6ecbc3d12dae8434d45ad15840655e6ee833420bb2ee307cf614.jpg" medium="image"/>
        <enclosure 
          url="https://image.nostr.build/2eb9a47ba4fe6ecbc3d12dae8434d45ad15840655e6ee833420bb2ee307cf614.jpg" length="0" 
          type="image/jpeg" 
        />
      <noteId>naddr1qq25jtfsddnxya258pzyzhmr2ff8qj2pv4s52q3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65wf0l0hd</noteId>
      <npub>npub10yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2syrhcl3</npub>
      <dc:creator><![CDATA[Learning_BTC&NOSTR]]></dc:creator>
      <content:encoded><![CDATA[<h1><strong>บทที่ 7: Authorization and Authentication</strong></h1>
<hr>
<p>เมื่อคุณได้รับบิตคอยน์ คุณต้องตัดสินใจว่าใครจะได้รับสิทธิ์ในการใช้จ่ายเหรียญนั้น ซึ่งเรียกว่า authorization (การอนุญาต) นอกจากนี้ คุณต้องตัดสินใจด้วยว่า full node ควรแยกแยะผู้ที่ได้รับอนุญาตให้ใช้จ่ายออกจากคนอื่น ๆ อย่างไร ซึ่งเรียกว่า authentication (การยืนยันตัวตน) คำสั่งเกี่ยวกับการอนุญาตของคุณ และหลักฐานการยืนยันตัวตนจากผู้ใช้จ่าย จะถูกตรวจสอบโดย full node นับพัน ซึ่งต้องได้ข้อสรุปตรงกันว่าการใช้จ่ายนั้นได้รับอนุญาตและได้รับการยืนยันตัวตนอย่างถูกต้อง เพื่อให้ธุรกรรมดังกล่าวเป็นธุรกรรมที่ถูกต้อง</p>
<p>คำอธิบายดั้งเดิมของบิตคอยน์ใช้ public key ในการทำ authorization โดย Alice ส่งเหรียญให้ Bob ด้วยการใส่ public key ของเขาลงในเอาต์พุตของธุรกรรม ส่วนการทำ authentication มาจากการลงนาม (signature) โดย Bob ซึ่งผูกพันกับธุรกรรมการใช้จ่าย เช่น การที่ Bob ส่งต่อให้ Carol</p>
<p>ในเวอร์ชันแรกของบิตคอยน์ที่ถูกปล่อยออกมา ได้จัดเตรียมกลไกที่ยืดหยุ่นกว่าสำหรับทั้ง authorization และ authentication และการพัฒนาต่อมาทำให้กลไกเหล่านี้ยืดหยุ่นยิ่งขึ้นกว่าเดิม ในบทนี้ เราจะสำรวจฟีเจอร์เหล่านั้นและดูว่ามันถูกใช้งานอย่างแพร่หลายได้อย่างไร</p>
<h2>Transaction Scripts and Script Language</h2>
<p>เวอร์ชันแรก ๆ ของบิตคอยน์ได้แนะนำภาษาการเขียนโปรแกรมใหม่ชื่อว่า Script ซึ่งเป็นภาษาลักษณะคล้าย Forth ที่ทำงานบนสแตก ทั้งสคริปต์ที่ถูกวางไว้ในเอาต์พุต และสคริปต์อินพุตแบบ legacy ที่ใช้ในธุรกรรมการใช้จ่าย ล้วนเขียนด้วยภาษาสคริปต์นี้ทั้งสิ้น</p>
<p>Script เป็นภาษาที่เรียบง่ายมาก ต้องการการประมวลผลเพียงเล็กน้อย และไม่สามารถทำสิ่งซับซ้อนแบบที่ภาษาสมัยใหม่สามารถทำได้</p>
<p>เมื่อธุรกรรมแบบ legacy ยังเป็นรูปแบบที่ใช้กันมากที่สุด ธุรกรรมส่วนใหญ่ที่ประมวลผลบนเครือข่ายบิตคอยน์อยู่ในรูปแบบเดียวกันกับที่ alice จ่ายให้ bob ในบทก่อนหน้าและใช้สคริปต์ที่เรียกว่า pay to public key hash (P2PKH) แต่อย่างไรก็ตาม ธุรกรรมบิตคอยน์ไม่ได้จำกัดอยู่แค่สคริปต์ในรูปแบบนี้เท่านั้น จริง ๆ แล้วสคริปต์สามารถเขียนเพื่อกำหนดเงื่อนไขที่ซับซ้อนหลากหลายรูปแบบได้ แต่เพื่อที่จะเข้าใจสคริปต์ที่ซับซ้อนเหล่านั้น เราจำเป็นต้องเข้าใจพื้นฐานของสคริปต์ธุรกรรมและภาษาสคริปต์เสียก่อน</p>
<p>ในส่วนนี้ เราจะสาธิตองค์ประกอบพื้นฐานของภาษาสคริปต์สำหรับธุรกรรมบิตคอยน์ และแสดงให้เห็นว่าภาษานี้ใช้เพื่อกำหนดเงื่อนไขสำหรับการใช้จ่ายอย่างไร และจะสามารถทำให้เงื่อนไขนั้นสำเร็จได้อย่างไร</p>
<blockquote>
<p>TIP: การตรวจสอบความถูกต้องของธุรกรรมบิตคอยน์ไม่ได้อาศัยรูปแบบตายตัว แต่เกิดจากการ รันภาษาสคริปต์ ซึ่งเปิดโอกาสให้สามารถกำหนดเงื่อนไขได้หลากหลายแทบไม่จำกัด</p>
</blockquote>
<h2>Turing Incompleteness</h2>
<p>ภาษาสคริปต์ของธุรกรรมบิตคอยน์มีโอเปอเรเตอร์จำนวนมาก แต่ถูกจำกัดไว้อย่างจงใจในด้านสำคัญอย่างหนึ่ง คือ ไม่มีลูปหรือโครงสร้างควบคุมการไหลที่ซับซ้อน นอกเหนือจากการควบคุมแบบมีเงื่อนไขเท่านั้น สิ่งนี้ทำให้ภาษา ไม่เป็น Turing Complete หมายความว่าสคริปต์มีความซับซ้อนได้จำกัดและมีเวลาการประมวลผลที่คาดเดาได้ Script ไม่ใช่ภาษาสำหรับงานทั่วไป ข้อจำกัดเหล่านี้ช่วยป้องกันไม่ให้ภาษาใช้สร้างลูปไม่รู้จบหรือ “logic bomb” รูปแบบอื่นที่อาจแฝงอยู่ในธุรกรรมและถูกใช้โจมตีแบบปฏิเสธการให้บริการ (DoS) ต่อเครือข่ายบิตคอยน์ได้ โปรดจำไว้ว่า ทุกธุรกรรมจะถูกยืนยันโดย full node ทุกตัวในเครือข่ายบิตคอยน์ ภาษาแบบจำกัดช่วยป้องกันไม่ให้กลไกตรวจสอบธุรกรรมกลายเป็นช่องโหว่</p>
<h2>Stateless Verification</h2>
<p>ภาษาสคริปต์ของธุรกรรมบิตคอยน์เป็นแบบ stateless กล่าวคือไม่มีสถานะก่อนเริ่มการประมวลผลสคริปต์ และไม่มีการเก็บสถานะหลังจากสคริปต์รันเสร็จ ข้อมูลทั้งหมดที่จำเป็นสำหรับการประมวลผลสคริปต์อยู่ในสคริปต์และธุรกรรมที่กำลังรันสคริปต์เท่านั้น สคริปต์จะทำงานเหมือนกันทุกครั้งบนทุกระบบ หากระบบของคุณตรวจสอบสคริปต์ผ่าน คุณมั่นใจได้ว่าระบบอื่นในเครือข่ายบิตคอยน์ทั้งหมดก็จะตรวจสอบผ่านเช่นกัน หมายความว่า ธุรกรรมที่ถูกต้องจะถูกต้องสำหรับทุกคน และทุกคนรู้เช่นนั้น ความสามารถในการคาดเดาผลลัพธ์ได้แบบนี้เป็นประโยชน์สำคัญของระบบบิตคอยน์</p>
<h2>Script Construction</h2>
<p>กลไกการตรวจสอบธุรกรรมแบบดั้งเดิมของ Bitcoin อาศัยสองส่วนของสคริปต์ในการตรวจสอบธุรกรรม: output script และ input script</p>
<p>output script ระบุเงื่อนไขที่ต้องถูกทำให้สำเร็จเพื่อใช้จ่ายเอาต์พุตในอนาคต เช่น ใครมีสิทธิ์ใช้จ่ายเอาต์พุต และจะมีการตรวจสอบสิทธิ์อย่างไร</p>
<p>input script คือสคริปต์ที่ทำให้เงื่อนไขใน output script สำเร็จ และอนุญาตให้เอาต์พุตถูกใช้จ่ายได้ Input script เป็นส่วนหนึ่งของทุก ๆ อินพุตภายในธุรกรรม ส่วนใหญ่ในธุรกรรมแบบเดิมจะมีลายเซ็นดิจิทัลที่สร้างจาก private key ของผู้ใช้ แต่ input script ไม่จำเป็นต้องมีลายเซ็นเสมอไป</p>
<p>ทุกโหนดที่ตรวจสอบ Bitcoin จะตรวจสอบธุรกรรมโดยรัน output script และ input script ตามที่ได้กล่าวไว้ในบทที่ 4 อินพุตแต่ละตัวมี outpoint ที่อ้างถึงเอาต์พุตของธุรกรรมก่อนหน้า อินพุตยังมี input script อยู่ด้วย ซอฟต์แวร์ตรวจสอบจะคัดลอก input script ดึง UTXO ที่อินพุตอ้างถึง และคัดลอก output script จาก UTXO นั้น จากนั้น output script และ input script จะถูกประมวลผลรวมกัน อินพุตจะถูกพิจารณาว่าถูกต้องหาก input script ทำให้เงื่อนไขใน output script สำเร็จ (จะอธิบายในหัวข้อ Separate execution of output and input scripts) อินพุตทุกตัวจะถูกตรวจสอบแยกกันเป็นส่วนหนึ่งของการตรวจสอบธุรกรรมโดยรวม</p>
<p>โปรดสังเกตว่า ขั้นตอนข้างต้นเกี่ยวข้องกับการคัดลอกข้อมูลทั้งหมด ข้อมูลต้นฉบับในเอาต์พุตก่อนหน้าและในอินพุตปัจจุบันจะไม่ถูกเปลี่ยนแปลงเลย เอาต์พุตก่อนหน้าไม่ถูกเปลี่ยนแปลงและไม่ได้รับผลกระทบจากความพยายามใช้จ่ายที่ล้มเหลว มีเพียงธุรกรรมที่ถูกต้องซึ่งทำให้เงื่อนไขใน output script สำเร็จเท่านั้นที่จะทำให้เอาต์พุตถูกพิจารณาว่า “ถูกใช้จ่ายแล้ว”</p>
<p>การผสาน input script และ output script เพื่อประเมินสคริปต์ของธุรกรรม คือ ตัวอย่างของ output script และ input script ของธุรกรรม Bitcoin แบบดั้งเดิมที่พบมากที่สุด (การชำระเงินไปยัง public key hash) ซึ่งแสดงสคริปต์ที่รวมกันจากการนำทั้งสองสคริปต์มาต่อกันก่อนการตรวจสอบ</p>
<p> <img src="https://image.nostr.build/5be02a704fca95d54b831b0ae9a66e09ea4804b95dde7c25d75decb167b1d55c.jpg" alt="image"></p>
<h3>The script execution stack</h3>
<p>ภาษาสคริปต์ของ Bitcoin ถูกเรียกว่า stack-based language เพราะใช้โครงสร้างข้อมูลที่เรียกว่า stack สแตกเป็นโครงสร้างข้อมูลที่เรียบง่ายมาก มองภาพได้เหมือนกองไพ่ สแตกมีสองการทำงานพื้นฐาน: push และ pop push คือการเพิ่มข้อมูลหนึ่งรายการไว้ด้านบนของสแตก ส่วน pop คือการนำรายการด้านบนสุดออกจากสแตก</p>
<p>ภาษาสคริปต์จะรันสคริปต์โดยประมวลผลรายการแต่ละรายการจากซ้ายไปขวา ตัวเลข (ค่าคงที่ของข้อมูล) จะถูก push ลงบนสแตก ตัวโอเปอเรเตอร์จะ push หรือ pop พารามิเตอร์หนึ่งตัวหรือมากกว่าจากสแตก นำไปประมวลผล และอาจ push ผลลัพธ์กลับลงสแตก ตัวอย่างเช่น OP_ADD จะ pop ข้อมูลสองรายการออกจากสแตก นำมาบวกกัน แล้ว push ผลรวมกลับลงบนสแตก</p>
<p>โอเปอเรเตอร์แบบมีเงื่อนไขจะประเมินเงื่อนไขและให้ผลลัพธ์เป็น boolean TRUE หรือ FALSE ตัวอย่างเช่น OP_EQUAL จะ pop ข้อมูลสองรายการจากสแตก และ push TRUE (TRUE แทนด้วยเลข 1) หากสองค่านั้นเท่ากัน หรือ push FALSE (แทนด้วยเลข 0) หากไม่เท่ากัน สคริปต์ของธุรกรรม Bitcoin มักมีโอเปอเรเตอร์แบบมีเงื่อนไขเพื่อสร้างผลลัพธ์ TRUE ที่แสดงว่าธุรกรรมนั้นถูกต้อง</p>
<h3>A simple script (สคริปต์อย่างง่าย)</h3>
<p>ตอนนี้เรามาลองประยุกต์สิ่งที่เราได้เรียนรู้เกี่ยวกับ scripts และ stacks กับตัวอย่างง่าย ๆ กัน </p>
<p>ในบทก่อนหน้าเราได้ยกสคริปต์อย่างง่ายมาตัวหนึ่งคือ</p>
<pre><code>2 3 OP_ADD 5 OP_EQUAL
</code></pre>
<p>แสดงการทำงานของโอเปอเรเตอร์คณิตศาสตร์ OP_ADD ซึ่งทำการบวกตัวเลขสองตัวแล้ววางผลลัพธ์ไว้บนสแต็ก จากนั้นตามด้วยโอเปอเรเตอร์แบบเงื่อนไข OP_EQUAL ที่ตรวจสอบว่าผลรวมดังกล่าวเท่ากับ 5 หรือไม่ ในหนังสือเล่มนี้ เพื่อความกระชับ อาจมีการละคำนำหน้า OP_ ในบางตัวอย่าง หากต้องการรายละเอียดเพิ่มเติมเกี่ยวกับโอเปอเรเตอร์และฟังก์ชันทั้งหมดของสคริปต์ สามารถดูได้ที่หน้า Script ของ Bitcoin Wiki<br>แม้ว่าผลลัพธ์สคริปต์แบบ Legacy ส่วนใหญ่จะอ้างอิง public key hash (ซึ่งโดยพื้นฐานคือ Bitcoin address แบบดั้งเดิม) เพื่อบังคับให้ต้องพิสูจน์ความเป็นเจ้าของก่อนจึงจะใช้เงินได้ แต่จริง ๆ แล้วสคริปต์ไม่จำเป็นต้องซับซ้อนขนาดนั้นก็ได้ สคริปต์ใด ๆ ที่ผสมกันระหว่าง output script และ input script แล้วให้ผลเป็น TRUE ถือว่า “ถูกต้อง”ดังนั้นสคริปต์คณิตศาสตร์ง่าย ๆ อย่างที่ใช้เป็นตัวอย่างในที่นี้ ก็ถือว่าเป็นสคริปต์ที่ถูกต้องเช่นกัน</p>
<p>ใช้ส่วนหนึ่งของสคริปต์คณิตศาสตร์เป็น output script</p>
<p>ให้ output script เป็น:</p>
<pre><code>3 OP_ADD 5 OP_EQUAL
</code></pre>
<p>ซึ่งสามารถถูกทำให้สำเร็จ (satisfied) ด้วยธุรกรรมที่มี input script แบบนี้:</p>
<pre><code>2
</code></pre>
<p>ซอฟต์แวร์ตรวจสอบความถูกต้อง (validation software) จะรวมสองสคริปต์เข้าด้วยกันกลายเป็น:</p>
<pre><code>2 3 OP_ADD 5 OP_EQUAL
</code></pre>
<p>เมื่อสคริปต์นี้ถูกประมวลผล ตามที่เราเห็นในบทก่อนหน้า ผลลัพธ์สุดท้ายคือ OP_TRUE ทำให้ธุรกรรมนี้ “ถูกต้อง” แม้ว่านี่จะเป็น output script ที่ถูกต้อง แต่ให้สังเกตว่า UTXO ที่สร้างขึ้น สามารถถูกใช้จ่ายได้โดยใครก็ตาม ที่มีทักษะคณิตศาสตร์พอจะรู้ว่าเลข 2 จะทำให้สคริปต์นี้สำเร็จได้<br> <img src="https://image.nostr.build/277a418d6958becf1c914c6eecd0732585265acceed0e238e9258e464c280728.jpg" alt="image"></p>
<blockquote>
<p>TIP: ธุรกรรมจะถือว่า ถูกต้อง (valid) หากผลลัพธ์บนยอดสแต็กเป็นค่า TRUE ซึ่งหมายถึงค่าที่ไม่ใช่ศูนย์ใด ๆ ทั้งหมด ธุรกรรมจะถือว่า ไม่ถูกต้อง (invalid) หากผลบนยอดสแต็กเป็นค่า FALSE (ศูนย์ หรือสแต็กว่าง), หรือการประมวลผลสคริปต์ถูกหยุดโดยตรงด้วยโอเปอเรเตอร์บางตัว (เช่น VERIFY, OP_RETURN), หรือสคริปต์นั้นมีความผิดพลาดทางไวยากรณ์/ความหมาย (semantic) เช่น มีคำสั่ง OP_IF แต่ไม่มี OP_ENDIF ปิดท้าย ดูรายละเอียดเพิ่มเติมได้ที่หน้า Script ของ Bitcoin Wiki</p>
</blockquote>
<p>สคริปต์ตัวอย่างถัดไปนี้จะมีความซับซ้อนขึ้นเล็กน้อย โดยมันคำนวณค่า 2 + 7 – 3 + 1 สังเกตว่าเมื่อสคริปต์มีโอเปอเรเตอร์หลายตัวต่อกัน สแต็กจะทำให้ผลลัพธ์ของโอเปอเรเตอร์ก่อนหน้า ถูกนำไปใช้ต่อโดยโอเปอเรเตอร์ตัวถัดไปได้</p>
<pre><code>2 7 OP_ADD 3 OP_SUB 1 OP_ADD 7 OP_EQUAL
</code></pre>
<p>ลองตรวจสอบสคริปต์ก่อนหน้านี้ด้วยตัวเอง โดยใช้ดินสอและกระดาษ เมื่อการประมวลผลสคริปต์สิ้นสุดลง คุณควรจะเหลือค่าบนสแต็กเป็นค่า TRUE</p>
<h3>Separate execution of output and input scripts</h3>
<p>ในไคลเอนต์ Bitcoin เวอร์ชันแรก ๆ  นั้น สคริปต์ของ output และ input ถูกต่อกันแล้วประมวลผลเป็นลำดับเดียว ด้วยเหตุผลด้านความปลอดภัย ซึ่งเกิดจากช่องโหว่ที่รู้จักกันในชื่อ 1 OP_RETURN bug ซึ่งวิธีนี้ถูกเปลี่ยนแปลงในปี 2010 เพื่อความปลอดภัย ในการใช้งานปัจจุบัน สคริปต์ทั้งสองจะถูกประมวลผลแยกกันโดยมีการคัดลอกสแต็กไปยังการประมวลผลครั้งถัดไป</p>
<p>อันดับแรก สคริปต์ input จะถูกประมวลผลโดยการดำเนินการของสแต็ก (stack execution engine) หากสคริปต์ input ถูกประมวลผลโดยไม่มีข้อผิดพลาดและไม่มีโอเปอเรชันเหลืออยู่ สแต็กจะถูกคัดลอกแล้วสคริปต์ output จะถูกประมวลผลต่อ หากผลลัพธ์ของการประมวลผลสคริปต์ output โดยใช้ข้อมูลสแต็กที่คัดลอกมาจาก input เป็น TRUE แสดงว่าสคริปต์ input สามารถแก้เงื่อนไขที่สคริปต์ output กำหนดได้ และด้วยเหตุนี้ input จึงเป็นการอนุญาตที่ถูกต้องสำหรับการใช้จ่าย UTXO นั้น แต่หากผลลัพธ์ใด ๆ นอกเหนือจาก TRUE ยังคงอยู่หลังการประมวลผลของสคริปต์รวมกัน แสดงว่า input นั้นไม่ถูกต้องเพราะล้มเหลวในการตอบสนองเงื่อนไขการใช้จ่ายที่วางไว้บน output</p>
<h2>Pay to Public Key Hash</h2>
<p>สคริปต์แบบ pay to public key hash (P2PKH) ใช้ output script ที่ภายในมีค่าแฮชซึ่งผูกพัน (commit) กับ public key หนึ่งค่า P2PKH เป็นที่รู้จักกันดีที่สุดในฐานะ Legacy Bitcoin address เอาต์พุตแบบ P2PKH สามารถถูกใช้จ่ายได้โดยการนำเสนอ public key ที่ตรงกับค่าแฮชที่ระบุไว้ และลายเซ็นดิจิทัลที่สร้างขึ้นด้วย private key ที่สอดคล้องกัน (ดูเรื่องนี้เพิ่มได้ในบทถัดไป) ส่วนตอนนี้มาดูตัวอย่างของ P2PKH output script กัน:</p>
<pre><code>OP_DUP OP_HASH160 &lt;Key Hash&gt; OP_EQUALVERIFY OP_CHECKSIG
</code></pre>
<p>Key Hash คือข้อมูลที่เมื่อนำไปเข้ารหัส จะกลายเป็น Bitcoin address แบบ legacy ในรูปแบบ base58check แอปพลิเคชันส่วนใหญ่จะแสดงค่า public key hash ภายในสคริปต์ในรูปแบบเลขฐานสิบหก (hexadecimal) แทนที่จะเป็นรูปแบบ Bitcoin address แบบ base58check ที่ผู้ใช้คุ้นเคย ซึ่งจะขึ้นต้นด้วยตัวอักษร “1”<br>output script ก่อนหน้านี้สามารถถูกทำให้สำเร็จ (satisfied) ได้ด้วย input script ในรูปแบบ:</p>
<pre><code>&lt;Signature&gt; &lt;Public Key&gt;
</code></pre>
<p>เมื่อรวมสคริปต์ทั้งสองเข้าด้วยกัน จะได้สคริปต์ตรวจสอบความถูกต้อง (combined validation script) ดังนี้:</p>
<pre><code>&lt;Sig&gt; &lt;Pubkey&gt; OP_DUP OP_HASH160 &lt;Hash&gt; OP_EQUALVERIFY OP_CHECKSIG
</code></pre>
<p>ผลลัพธ์จะเป็นค่า TRUE หาก input script มีลายเซ็นที่ถูกต้อง ซึ่งถูกสร้างขึ้นจาก private key ของของผู้ส่ง และสอดคล้องกับ public key hash ที่ถูกกำหนดไว้เป็นเงื่อนไขการใช้จ่ายนั้น</p>
<p> <img src="https://image.nostr.build/53dda9b219893a6b51c0dbf4b96b4c5eda32f2c2cd62a8c77b13fbec2e6280dc.jpg" alt="image"><br> <img src="https://image.nostr.build/318690aac1933790ae7bd5600e90c2fa311383a30515d80d9b2f9e0ff99bbcd7.jpg" alt="image"></p>
<h2>Scripted Multisignatures</h2>
<p>สคริปต์แบบมัลติซิกเนเจอร์ (multisignature) กำหนดเงื่อนไขโดยบันทึกค่าของ public key จำนวน k ค่าไว้ในสคริปต์ และต้องมีลายเซ็นอย่างน้อย t ค่าจาก public key เหล่านั้นจึงจะสามารถใช้จ่ายเงินได้ เรียกรูปแบบนี้ว่า t-of-k ตัวอย่างเช่น 2-of-3 multisignature คือกรณีที่มีการระบุ public key ไว้ทั้งหมดสามค่าในฐานะผู้มีสิทธิ์ลงนาม และต้องใช้ลายเซ็นอย่างน้อยสองจากสามค่านั้น เพื่อสร้างธุรกรรมที่ถูกต้องสำหรับการใช้จ่ายเงิน</p>
<blockquote>
<p>TIP: เอกสารของบิตคอยน์บางแหล่ง รวมถึงหนังสือเล่มนี้ในฉบับก่อนหน้า ใช้คำว่า “m-of-n” เพื่อเรียกมัลติซิกเนเจอร์แบบดั้งเดิม อย่างไรก็ตาม เมื่อพูดออกเสียงแล้ว ตัวอักษร m และ n แยกออกจากกันได้ยาก จึงมีการเลือกใช้คำว่า t-of-k แทน ทั้งสองคำนี้หมายถึงรูปแบบของระบบลายเซ็นแบบเดียวกัน</p>
</blockquote>
<p>รูปแบบทั่วไปของ output script ที่กำหนดเงื่อนไขมัลติซิกเนเจอร์แบบ t-of-k มีดังนี้:</p>
<pre><code>t &lt;Public Key 1&gt; &lt;Public Key 2&gt; ... &lt;Public Key k&gt; k OP_CHECKMULTISIG
</code></pre>
<p>โดยที่ k คือจำนวน public key ทั้งหมดที่ระบุไว้ และ t คือจำนวนลายเซ็นขั้นต่ำที่ต้องใช้เพื่อสามารถใช้จ่าย output นั้นได้<br>output script ที่กำหนดเงื่อนไขมัลติซิกเนเจอร์แบบ 2-of-3 จะมีลักษณะดังนี้:</p>
<pre><code>2 &lt;Public Key A&gt; &lt;Public Key B&gt; &lt;Public Key C&gt; 3 OP_CHECKMULTISIG
</code></pre>
<p>output script ข้างต้นสามารถถูกทำให้สำเร็จได้ด้วย input script ที่มีลายเซ็น เช่น:</p>
<pre><code>&lt;Signature B&gt; &lt;Signature C&gt;
</code></pre>
<p>หรือจะเป็นชุดลายเซ็นสองชุดใดก็ได้ ที่มาจาก private key ที่สอดคล้องกับ public key ทั้งสามค่าที่ถูกระบุไว้</p>
<p>เมื่อรวมสคริปต์ทั้งสองเข้าด้วยกัน จะได้สคริปต์ตรวจสอบความถูกต้องดังนี้:</p>
<pre><code>&lt;Sig B&gt; &lt;Sig C&gt; 2 &lt;Pubkey A&gt; &lt;Pubkey B&gt; &lt;Pubkey C&gt; 3 OP_CHECKMULTISIG
</code></pre>
<p>เมื่อถูกประมวลผล สคริปต์ที่ถูกรวมกันนี้จะให้ผลลัพธ์เป็นค่า TRUE หาก input script มีลายเซ็นที่ถูกต้องจำนวนสองชุด ซึ่งมาจาก private key ที่สอดคล้องกับ public key สองค่าใดก็ได้ จากสามค่าที่ถูกกำหนดไว้เป็นเงื่อนไขการใช้จ่าย</p>
<p>ในปัจจุบัน นโยบายการส่งต่อธุรกรรม (transaction relay policy) ของ Bitcoin Core จำกัดจำนวน public key ใน multisignature output script ไว้สูงสุดที่สามค่า หมายความว่าสามารถสร้างมัลติซิกเนเจอร์ได้ตั้งแต่แบบ 1-of-1 ไปจนถึง 3-of-3 หรือรูปแบบใด ๆ ภายในช่วงนี้ คุณอาจต้องการตรวจสอบฟังก์ชัน IsStandard() เพื่อดูว่าขณะนี้เครือข่ายยอมรับรูปแบบใดบ้าง โปรดสังเกตว่า ข้อจำกัดที่สามคีย์นี้ใช้กับ multisignature script แบบมาตรฐานเท่านั้น (ซึ่งเรียกอีกชื่อหนึ่งว่า “bare” multisignature) ไม่ได้ใช้กับสคริปต์ที่ถูกห่อหุ้มด้วยโครงสร้างอื่น เช่น P2SH, P2WSH, หรือ P2TR สำหรับ multisignature script แบบ P2SH จะถูกจำกัดทั้งในระดับนโยบาย (policy) และฉันทามติ (consensus) ไว้ที่สูงสุด 15 คีย์ ทำให้สามารถสร้างมัลติซิกเนเจอร์ได้สูงสุดแบบ 15-of-15 เราจะเรียนรู้เกี่ยวกับ P2SH ในหัวข้อ Pay to Script Hash ส่วนสคริปต์รูปแบบอื่นทั้งหมด จะถูกจำกัดโดยฉันทามติไว้ที่ public key ได้ไม่เกิน 20 คีย์ ต่อคำสั่ง OP_CHECKMULTISIG หรือ OP_CHECKMULTISIGVERIFY หนึ่งคำสั่ง ทั้งนี้ สคริปต์หนึ่งรายการอาจมีคำสั่งเหล่านี้ได้มากกว่าหนึ่งครั้ง</p>
<h3>An Oddity in CHECKMULTISIG Execution</h3>
<p>มีความผิดปกติ (oddity) อย่างหนึ่งในการทำงานของ OP_CHECKMULTISIG ซึ่งทำให้ต้องมีวิธีแก้ไขเฉพาะหน้าเล็กน้อย เมื่อ OP_CHECKMULTISIG ถูกประมวลผล ตามหลักแล้วมันควรจะนำข้อมูลออกจากสแต็กมาใช้เป็นพารามิเตอร์จำนวน t + k + 2 ค่า อย่างไรก็ตาม เนื่องจากความผิดปกตินี้ OP_CHECKMULTISIG จะดึงค่าจากสแต็กออกมา มากกว่าที่คาดไว้หนึ่งค่า</p>
<p>เรามาดูรายละเอียดของเรื่องนี้ให้ชัดเจนขึ้น โดยใช้ตัวอย่างสคริปต์ตรวจสอบความถูกต้องจากก่อนหน้านี้:</p>
<pre><code>&lt;Sig B&gt; &lt;Sig C&gt; 2 &lt;Pubkey A&gt; &lt;Pubkey B&gt; &lt;Pubkey C&gt; 3 OP_CHECKMULTISIG
</code></pre>
<p>ขั้นแรก OP_CHECKMULTISIG จะนำค่าแรกบนสุดของสแต็กออกมา ซึ่งก็คือ k (ในตัวอย่างนี้คือ “3”) จากนั้นจะนำข้อมูลออกมาอีก k ค่า ซึ่งเป็น public key ที่สามารถใช้ลงนามได้ ในตัวอย่างนี้คือ public key A, B และ C จากนั้นมันจะนำค่าออกมาอีกหนึ่งค่า ซึ่งก็คือ t หรือจำนวนลายเซ็นขั้นต่ำที่ต้องใช้ (quorum) โดยในกรณีนี้ t = 2 ตามปกติแล้ว ณ จุดนี้ OP_CHECKMULTISIG ควรจะนำค่าออกมาอีก t ค่า ซึ่งเป็นลายเซ็น เพื่อตรวจสอบว่าถูกต้องหรือไม่ อย่างไรก็ตาม เนื่องจากความผิดปกติในการนำไปใช้งานจริง OP_CHECKMULTISIG จะนำค่าจากสแต็กออกมา มากกว่าที่ควรหนึ่งค่า (รวมเป็น t + 1 ค่า) ค่าส่วนเกินนี้ถูกเรียกว่า dummy stack element และจะถูกละเว้นในระหว่างการตรวจสอบลายเซ็น ดังนั้นมันจึงไม่มีผลโดยตรงต่อการทำงานของ OP_CHECKMULTISIG เอง อย่างไรก็ตาม dummy element นี้ จำเป็นต้องมีอยู่ เพราะหากไม่มีค่าเหลืออยู่บนสแต็กในขณะที่ OP_CHECKMULTISIG พยายามดึงค่าออกมาเพิ่ม จะทำให้เกิด stack error และสคริปต์ล้มเหลว ส่งผลให้ธุรกรรมนั้นไม่ถูกต้อง เนื่องจาก dummy element ถูกละเว้น ค่าของมันจึงสามารถเป็นค่าอะไรก็ได้ แต่ในช่วงแรกได้มีธรรมเนียมใช้ค่า OP_0 สำหรับตำแหน่งนี้ ต่อมาสิ่งนี้ได้กลายเป็นกฎของ relay policy และในที่สุดก็กลายเป็นกฎระดับฉันทามติ (consensus rule) เมื่อมีการบังคับใช้ BIP147</p>
<p>เนื่องจากการนำ dummy element ออกจากสแต็กเป็นส่วนหนึ่งของกฎฉันทามติ (consensus rules) พฤติกรรมนี้จึงต้องถูกคงไว้และทำซ้ำตลอดไป ดังนั้นสคริปต์จึงควรมีหน้าตาเป็นดังนี้:</p>
<pre><code>OP_0 &lt;Sig B&gt; &lt;Sig C&gt; 2 &lt;Pubkey A&gt; &lt;Pubkey B&gt; &lt;Pubkey C&gt; 3 OP_CHECKMULTISIG
</code></pre>
<p>ด้วยเหตุนี้ input script ที่ถูกใช้งานจริงใน multisignature จะ ไม่ใช่:</p>
<pre><code>&lt;Signature B&gt; &lt;Signature C&gt;
</code></pre>
<p>แต่จะต้องเป็น:</p>
<pre><code>OP_0 &lt;Sig B&gt; &lt;Sig C&gt;
</code></pre>
<p>บางคนเชื่อว่าความผิดปกตินี้เกิดจากบั๊กในโค้ดต้นฉบับของบิตคอยน์ แต่อย่างไรก็ตาม ยังมีคำอธิบายทางเลือกที่สมเหตุสมผลอยู่ การตรวจสอบลายเซ็นแบบ t-of-k อาจต้องใช้การตรวจสอบลายเซ็นมากกว่าทั้งค่า t หรือ k เพียงอย่างเดียว</p>
<p>ลองพิจารณาตัวอย่างง่าย ๆ แบบ 1-in-5 โดยมีสคริปต์ตรวจสอบความถูกต้องที่ถูกรวมกันดังนี้:</p>
<pre><code>&lt;dummy&gt; &lt;Sig4&gt; 1 &lt;key0&gt; &lt;key1&gt; &lt;key2&gt; &lt;key3&gt; &lt;key4&gt; 5 OP_CHECKMULTISIG
</code></pre>
<p>ลายเซ็นจะถูกตรวจสอบกับ key0 ก่อน จากนั้นกับ key1 และต่อเนื่องไปยัง key อื่น ๆ จนกระทั่งในที่สุดจึงถูกนำไปเปรียบเทียบกับ public key ที่สอดคล้องกันจริง ๆ คือ key4 นั่นหมายความว่า จำเป็นต้องมีการตรวจสอบลายเซ็นถึง ห้าครั้ง ทั้งที่มีลายเซ็นเพียงหนึ่งรายการเท่านั้น แนวทางหนึ่งในการลดความซ้ำซ้อนนี้ คือให้ OP_CHECKMULTISIG รับข้อมูลในลักษณะเป็น map ที่ระบุว่าแต่ละลายเซ็นที่ส่งเข้ามาสัมพันธ์กับ public key ใด ซึ่งจะทำให้ OP_CHECKMULTISIG ต้องทำการตรวจสอบลายเซ็นเพียง t ครั้ง เท่านั้น จึงเป็นไปได้ว่า นักพัฒนาของบิตคอยน์ได้เพิ่มองค์ประกอบพิเศษนี้เข้าไปตั้งแต่เวอร์ชันแรก (ซึ่งปัจจุบันเราเรียกว่า dummy stack element) เพื่อเปิดทางให้สามารถเพิ่มความสามารถดังกล่าวผ่าน soft fork ในอนาคตได้ อย่างไรก็ตาม ฟีเจอร์ดังกล่าวไม่เคยถูกนำมาใช้งานจริง และการอัปเดตกฎฉันทามติด้วย BIP147 ในปี 2017 ก็ทำให้ไม่สามารถเพิ่มฟีเจอร์นี้ได้อีกต่อไปในอนาคต </p>
<p>มีเพียงนักพัฒนาของบิตคอยน์เท่านั้น ที่จะสามารถบอกได้ว่าการมีอยู่ของ dummy stack element นั้นเกิดจากบั๊ก หรือเป็นการวางแผนสำหรับการอัปเกรดในอนาคต สำหรับหนังสือเล่มนี้ เราเรียกสิ่งนี้เพียงว่าเป็น “ความผิดปกติ” (oddity) </p>
<p>นับจากนี้เป็นต้นไป หากคุณเห็นสคริปต์แบบ multisignature ให้คาดไว้เลยว่าจะต้องมี OP_0 เพิ่มเข้ามาในตอนต้น โดยจุดประสงค์เดียวของมันคือการเป็นวิธีแก้ปัญหาเฉพาะหน้า สำหรับความผิดปกติที่มีอยู่ในกฎฉันทามติ</p>
<h2>Pay to Script Hash</h2>
<p>Pay to Script Hash (P2SH) ถูกนำมาใช้ในปี 2012 ในฐานะรูปแบบการทำงานแบบใหม่ที่ทรงพลัง ซึ่งช่วยทำให้การใช้งานสคริปต์ที่ซับซ้อนเป็นเรื่องง่ายขึ้นอย่างมาก เพื่ออธิบายความจำเป็นของ P2SH เรามาดูตัวอย่างเชิงปฏิบัติกัน</p>
<p>โมฮัมเหม็ดเป็นผู้นำเข้าอุปกรณ์อิเล็กทรอนิกส์ซึ่งตั้งอยู่ในดูไบ บริษัทของโมฮัมเหม็ดใช้ฟีเจอร์มัลติซิกเนเจอร์ของบิตคอยน์อย่างแพร่หลายสำหรับบัญชีขององค์กร สคริปต์มัลติซิกเนเจอร์เป็นหนึ่งในการใช้งานความสามารถด้านสคริปต์ขั้นสูงของบิตคอยน์ที่พบได้บ่อยที่สุด และเป็นฟีเจอร์ที่ทรงพลังมาก บริษัทของโมฮัมเหม็ดใช้สคริปต์มัลติซิกเนเจอร์กับการชำระเงินจากลูกค้าทุกราย การชำระเงินของลูกค้าจะถูกล็อกไว้ในลักษณะที่ต้องใช้ลายเซ็นอย่างน้อยสองชุดจึงจะสามารถปลดล็อกได้ โมฮัมเหม็ด หุ้นส่วนอีกสามคนของเขา และทนายความของบริษัท ต่างสามารถให้ลายเซ็นได้คนละหนึ่งชุด รูปแบบมัลติซิกเนเจอร์เช่นนี้ช่วยสร้างกลไกกำกับดูแลกิจการ และป้องกันการโจรกรรม การยักยอก หรือการสูญหายของเงินได้</p>
<p>สคริปต์ที่ได้จากเงื่อนไขดังกล่าวจะค่อนข้างยาว และมีลักษณะดังนี้:</p>
<pre><code>2 &lt;Mohammed's Public Key&gt; &lt;Partner1 Public Key&gt; &lt;Partner2 Public Key&gt;
&lt;Partner3 Public Key&gt; &lt;Attorney Public Key&gt; 5 OP_CHECKMULTISIG
</code></pre>
<p>แม้ว่าสคริปต์แบบมัลติซิกเนเจอร์จะเป็นฟีเจอร์ที่ทรงพลังมาก แต่ก็ใช้งานได้ค่อนข้างยุ่งยาก จากสคริปต์ตัวอย่างก่อนหน้า โมฮัมเหม็ดจะต้องสื่อสารสคริปต์นี้ให้ลูกค้าทุกรายทราบก่อนทำการชำระเงิน ลูกค้าแต่ละรายยังจำเป็นต้องใช้ซอฟต์แวร์กระเป๋าเงินบิตคอยน์แบบพิเศษ ที่สามารถสร้างสคริปต์ธุรกรรมแบบกำหนดเองได้ นอกจากนี้ ธุรกรรมที่ได้จะมีขนาดใหญ่กว่าธุรกรรมการชำระเงินแบบธรรมดาประมาณห้าเท่า เนื่องจากสคริปต์นี้มี public key ที่มีความยาวมาก ภาระของข้อมูลส่วนเกินนี้จะตกอยู่กับลูกค้าในรูปของค่าธรรมเนียมธุรกรรมที่สูงขึ้น สุดท้ายแล้ว สคริปต์ธุรกรรมขนาดใหญ่เช่นนี้จะถูกเก็บไว้ในชุดข้อมูล UTXO set ของทุก full node จนกว่าจะถูกใช้จ่าย ซึ่งปัญหาเหล่านี้ทั้งหมดทำให้การใช้งาน output script ที่ซับซ้อนเป็นเรื่องยากในทางปฏิบัติ</p>
<p>P2SH ถูกพัฒนาขึ้นมาเพื่อแก้ไขปัญหาเชิงปฏิบัติเหล่านี้ และเพื่อทำให้การใช้งานสคริปต์ที่ซับซ้อนง่ายพอ ๆ กับการชำระเงินไปยัง Bitcoin address แบบกุญแจเดียว ด้วยการชำระเงินแบบ P2SH สคริปต์ที่ซับซ้อนจะถูกแทนที่ด้วย “คำมั่น” (commitment) ซึ่งก็คือค่าแฮชเชิงคริปโตกราฟีของสคริปต์นั้น เมื่อมีการนำเสนอธุรกรรมเพื่อใช้จ่าย UTXO ในภายหลัง ธุรกรรมนั้นจะต้องมีทั้งสคริปต์ที่ตรงกับค่าแฮชที่ถูกผูกไว้ และข้อมูลที่ใช้ทำให้สคริปต์ดังกล่าวสำเร็จ กล่าวอย่างง่ายที่สุด P2SH หมายถึง “จ่ายเงินให้กับสคริปต์ที่ตรงกับค่าแฮชนี้ โดยสคริปต์นั้นจะถูกนำมาแสดงในภายหลังเมื่อ output นี้ถูกใช้จ่าย”</p>
<p>ในธุรกรรมแบบ P2SH สคริปต์ที่ถูกแทนที่ด้วยค่าแฮชจะถูกเรียกว่า redeem script เนื่องจากสคริปต์นี้จะถูกนำเสนอให้ระบบในช่วงเวลาที่มีการนำมาใช้จ่าย (redeem) แทนที่จะถูกใส่ไว้เป็น output script ตั้งแต่ต้นตารางด้านล่างจะ แสดงสคริปต์ในกรณีที่ไม่ใช้ P2SH และแสดงสคริปต์เดียวกันที่ถูกเข้ารหัสในรูปแบบ P2SH</p>
<p>Complex script without P2SH</p>
<table>
<thead>
<tr>
<th align="left">Output script</th>
<th align="left">2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 OP_CHECKMULTISIG</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Input script</td>
<td align="left">Sig1 Sig2</td>
</tr>
</tbody></table>
<p>Complex script as P2SH</p>
<table>
<thead>
<tr>
<th align="left">Redeem script</th>
<th align="left">2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 OP_CHECKMULTISIG</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Output script</td>
<td align="left">OP_HASH160 &lt;20-byte hash of redeem script&gt; OP_EQUAL</td>
</tr>
<tr>
<td align="left">Input script</td>
<td align="left">Sig1 Sig2 &lt;redeem script&gt;</td>
</tr>
</tbody></table>
<p>ดังที่เห็นได้จากตาราง เมื่อใช้ P2SH สคริปต์ที่ซับซ้อนซึ่งระบุเงื่อนไขในการใช้จ่าย output (redeem script) จะไม่ถูกนำมาใส่ไว้ใน output script อีกต่อไป แต่จะมีเพียงค่าแฮชของสคริปต์นั้นอยู่ใน output script เท่านั้น ส่วนตัว redeem script เองจะถูกนำมาแสดงภายหลังในฐานะส่วนหนึ่งของ input script เมื่อมีการใช้จ่าย output นั้นแนวทางนี้ทำให้ภาระในด้านค่าธรรมเนียมและความซับซ้อน ถูกย้ายจากฝั่งผู้รับเงิน ไปยังฝั่งผู้ใช้จ่ายเงินในภายหลังเรามาดูกรณีของบริษัทโมฮัมเหม็ด สคริปต์มัลติซิกเนเจอร์ที่ซับซ้อน และสคริปต์ P2SH ที่ได้จากมันกัน อันดับแรก คือสคริปต์มัลติซิกเนเจอร์ที่บริษัทของโมฮัมเหม็ดใช้สำหรับการรับชำระเงินจากลูกค้าทุกราย:</p>
<pre><code>2 &lt;Mohammed's Public Key&gt; &lt;Partner1 Public Key&gt; &lt;Partner2 Public Key&gt;
&lt;Partner3 Public Key&gt; &lt;Attorney Public Key&gt; 5 OP_CHECKMULTISIG
</code></pre>
<p>สคริปต์ทั้งหมดนี้สามารถถูกแทนที่ด้วยค่าแฮชเชิงคริปโตกราฟีขนาด 20 ไบต์ ได้ โดยเริ่มจากการนำสคริปต์ไปผ่านอัลกอริทึมแฮช SHA256 ก่อน จากนั้นจึงนำผลลัพธ์ที่ได้ไปแฮชต่อด้วยอัลกอริทึม RIPEMD-160 ตัวอย่างเช่น เริ่มต้นด้วยค่าแฮชของ redeem script ของบริษัทโมฮัมเหม็ด:</p>
<pre><code>54c557e07dde5bb6cb791c7a540e0a4796f5e97e
</code></pre>
<p>ธุรกรรมแบบ P2SH จะล็อก output ไว้กับค่าแฮชนี้ แทนที่จะใช้ redeem script ที่ยาวกว่า โดยใช้แม่แบบ output script แบบพิเศษดังนี้:</p>
<pre><code>OP_HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e OP_EQUAL
</code></pre>
<p>ซึ่งอย่างที่เห็น จะสั้นกว่ามาก แทนที่จะเป็น “จ่ายไปยังสคริปต์มัลติซิกเนเจอร์ที่มี 5 คีย์นี้” ธุรกรรมแบบ P2SH จะกลายเป็น “จ่ายไปยังสคริปต์ที่มีค่าแฮชนี้” ลูกค้าที่ชำระเงินให้บริษัทของโมฮัมเหม็ด จำเป็นต้องใส่เพียง output script ที่สั้นกว่านี้ลงในธุรกรรมเท่านั้น</p>
<p>เมื่อโมฮัมเหม็ดและหุ้นส่วนต้องการใช้จ่าย UTXO นี้ พวกเขาจะต้องแสดง redeem script ต้นฉบับ (ซึ่งเป็นสคริปต์ที่ค่าแฮชของมันถูกใช้ล็อก UTXO ไว้) พร้อมกับลายเซ็นที่จำเป็นเพื่อปลดล็อกสคริปต์นั้น ดังนี้:</p>
<pre><code>&lt;Sig1&gt; &lt;Sig2&gt; &lt;2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG&gt;
</code></pre>
<p>สคริปต์ทั้งสองจะถูกรวมกันเป็นสองขั้นตอน ขั้นแรก redeem script จะถูกตรวจสอบกับ output script เพื่อยืนยันว่าค่าแฮชตรงกัน:</p>
<pre><code>&lt;2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG&gt; OP_HASH160 &lt;script hash&gt; OP_EQUAL
</code></pre>
<p>หากค่าแฮชของ redeem script ตรงกัน จากนั้น redeem script จะถูกนำมาประมวลผล:</p>
<pre><code>&lt;Sig1&gt; &lt;Sig2&gt; 2 &lt;PK1&gt; &lt;PK2&gt; &lt;PK3&gt; &lt;PK4&gt; &lt;PK5&gt; 5 OP_CHECKMULTISIG
</code></pre>
<h3>P2SH Addresses</h3>
<p>อีกส่วนสำคัญของฟีเจอร์ P2SH คือความสามารถในการเข้ารหัสค่าแฮชของสคริปต์ให้อยู่ในรูปของ “address” ตามที่กำหนดไว้ใน BIP13 ที่อยู่แบบ P2SH คือการเข้ารหัสแบบ base58check ของค่าแฮชขนาด 20 ไบต์ของสคริปต์ เช่นเดียวกับที่ Bitcoin address ปกติคือการเข้ารหัสแบบ base58check ของค่าแฮชขนาด 20 ไบต์ของ public key ที่อยู่แบบ P2SH ใช้ version prefix เป็น “5” ซึ่งทำให้ที่อยู่ที่ถูกเข้ารหัสออกมาในรูป base58check เริ่มต้นด้วยตัวเลข “3”</p>
<p>ตัวอย่างเช่น สคริปต์ที่ซับซ้อนของบริษัทโมฮัมเหม็ด เมื่อนำไปแฮชและเข้ารหัสแบบ base58check เป็นที่อยู่ P2SH จะได้เป็น:</p>
<pre><code>39RF6JqABiHdYHkfChV6USGMe6Nsr66Gzw
</code></pre>
<p>จากนี้ไป โมฮัมเหม็ดสามารถให้ “address” นี้กับลูกค้าได้ และลูกค้าก็สามารถใช้กระเป๋าเงินบิตคอยน์ทั่วไปแทบทุกแบบ เพื่อทำการชำระเงินอย่างง่าย ๆ ได้ เหมือนกับการจ่ายไปยัง Bitcoin address ปกติทั่วไป ตัวเลขนำหน้า 3 จะช่วยบอกใบ้ให้ทราบว่านี่คือที่อยู่แบบพิเศษ ซึ่งเชื่อมโยงกับสคริปต์ ไม่ใช่กับ public key โดยตรง แต่ในแง่ของการใช้งานแล้ว มันทำงานเหมือนกับการชำระเงินไปยัง Bitcoin address อื่น ๆ ทุกประการ ที่อยู่แบบ </p>
<p>P2SH ช่วยซ่อนความซับซ้อนทั้งหมดเอาไว้ ทำให้ผู้ที่ทำการชำระเงินไม่จำเป็นต้องเห็นหรือเข้าใจสคริปต์เลย</p>
<h3>Benefits of P2SH</h3>
<p>ฟีเจอร์ P2SH มีข้อดีเมื่อเทียบกับการใช้งานสคริปต์ที่ซับซ้อนโดยตรงใน output ดังนี้:</p>
<ul>
<li><p>ความคล้ายคลึงกับ Bitcoin address แบบ legacy เดิม ทำให้ผู้ส่งและซอฟต์แวร์กระเป๋าเงินของผู้ส่ง ไม่จำเป็นต้องมีการออกแบบหรือพัฒนาเพิ่มเติมที่ซับซ้อนเพื่อรองรับ P2SH</p>
</li>
<li><p>P2SH ย้ายภาระในการจัดเก็บข้อมูลของสคริปต์ที่ยาว จากฝั่ง output (ซึ่งนอกจากจะถูกเก็บไว้บนบล็อกเชนแล้ว ยังอยู่ในชุดข้อมูล UTXO set ด้วย) ไปอยู่ที่ฝั่ง input (ซึ่งจะถูกเก็บไว้บนบล็อกเชนเท่านั้น)</p>
</li>
<li><p>P2SH ย้ายภาระในการจัดเก็บข้อมูลของสคริปต์ที่ยาว จากช่วงเวลาปัจจุบันขณะชำระเงิน ไปยังช่วงเวลาในอนาคตเมื่อมีการนำ output นั้นมาใช้จ่าย</p>
</li>
<li><p>P2SH ย้ายภาระค่าธรรมเนียมธุรกรรมที่เกิดจากสคริปต์ยาว จากผู้ส่ง ไปยังผู้รับ ซึ่งเป็นฝ่ายที่ต้องใส่ redeem script ที่ยาวลงไปเมื่อทำการใช้จ่าย</p>
</li>
</ul>
<h3>Redeem Script and Validation</h3>
<p>คุณไม่สามารถใส่ P2SH ซ้อนอยู่ภายใน redeem script ของ P2SH ได้ เนื่องจากข้อกำหนดของ P2SH ไม่ได้รองรับการทำงานแบบเรียกซ้ำ (recursive) นอกจากนี้ แม้ในเชิงเทคนิคจะสามารถใส่ OP_RETURN (ดูหัวข้อ Data Recording Output (OP_RETURN)) ลงไปใน redeem script ได้ เพราะกฎไม่ได้ห้ามไว้ แต่ในทางปฏิบัติกลับไม่มีประโยชน์ใด ๆ เนื่องจากเมื่อมีการประมวลผล OP_RETURN ระหว่างขั้นตอนการตรวจสอบความถูกต้อง ธุรกรรมจะถูกตัดสินว่าไม่ถูกต้องทันที</p>
<p>โปรดสังเกตว่า เนื่องจาก redeem script จะไม่ถูกเผยแพร่ให้เครือข่ายทราบจนกว่าจะมีการพยายามใช้จ่าย P2SH output หากคุณสร้าง output ด้วยค่าแฮชของ redeem script ที่ไม่ถูกต้อง คุณจะไม่สามารถใช้จ่ายมันได้เลย ธุรกรรมที่พยายามใช้จ่าย ซึ่งต้องแนบ redeem script เข้ามาด้วย จะไม่ถูกยอมรับ เนื่องจากสคริปต์นั้นไม่ถูกต้อง สิ่งนี้ก่อให้เกิดความเสี่ยง เพราะเป็นไปได้ที่จะส่งบิตคอยน์ไปยัง P2SH address ที่ไม่สามารถถูกใช้จ่ายได้ในภายหลัง</p>
<blockquote>
<p>คำเตือน: output script แบบ P2SH จะบรรจุเพียงค่าแฮชของ redeem script เท่านั้น ซึ่งไม่ได้ให้ข้อมูลใด ๆ เกี่ยวกับเนื้อหาของ redeem script เลย ดังนั้น P2SH output จะยังคงถูกถือว่าถูกต้องและถูกรับรอง แม้ว่า redeem script ที่ถูกอ้างถึงนั้นจะไม่ถูกต้องก็ตาม ด้วยเหตุนี้เองที่จะทำให้คุณอาจเผลอได้รับบิตคอยน์ในรูปแบบที่ไม่สามารถนำไปใช้จ่ายได้ในภายหลัง</p>
</blockquote>
<h3>Data Recording Output (OP_RETURN)</h3>
<p>บิตคอยน์มีบล็อกเชนกระจายศูนย์ที่มีการประทับเวลา (timestamped blockchain) ซึ่งมีศักยภาพในการนำไปใช้มากกว่าการชำระเงิน หลายคนพยายามใช้ภาษาสคริปต์ของธุรกรรมเพื่อใช้ประโยชน์จากความปลอดภัยและความทนทานของระบบสำหรับแอปพลิเคชันอื่น เช่น บริการทนายความดิจิทัล (digital notary services) ความพยายามในช่วงแรก ๆ เพื่อใช้สคริปต์ของบิตคอยน์ในลักษณะนี้เกี่ยวข้องกับการสร้าง transaction output ที่บันทึกข้อมูลลงบนบล็อกเชน เช่น การบันทึก commitment ต่อไฟล์หนึ่งไฟล์ เพื่อให้ใครก็ตามสามารถตรวจสอบการมีอยู่จริงของไฟล์นั้น ณ วันที่หนึ่งได้ โดยอ้างอิงจากธุรกรรมนั้น</p>
<p>การใช้บล็อกเชนของบิตคอยน์เพื่อเก็บข้อมูลที่ไม่เกี่ยวข้องกับการชำระเงินเป็นประเด็นที่ถกเถียงกัน หลายคนมองว่าเป็นการใช้งานที่ไม่เหมาะสมและควรถูกขัดขวาง ขณะที่อีกกลุ่มมองว่าเป็นตัวอย่างของความสามารถทรงพลังของเทคโนโลยีบล็อกเชนและควรสนับสนุนการทดลองเช่นนี้ ผู้คัดค้านให้เหตุผลว่าการบรรจุข้อมูลเหล่านี้ทำให้ผู้ที่รันฟูลโหนดต้องแบกรับต้นทุนพื้นที่จัดเก็บข้อมูลที่ไม่ใช่สิ่งที่บล็อกเชนถูกออกแบบมาเพื่อรองรับ นอกจากนี้ ธุรกรรมลักษณะนี้ยังอาจสร้าง UTXO ที่ไม่สามารถใช้จ่ายได้ โดยใช้ legacy Bitcoin address เป็นพื้นที่ข้อมูลขนาด 20 ไบต์ เพราะแอดเดรสนั้นถูกใช้เป็นข้อมูลและไม่สอดคล้องกับกุญแจส่วนตัว UTXO ที่เกิดขึ้นจึงไม่มีวันถูกใช้จ่ายได้ และเป็น "ธุรกรรมปลอม" ซึ่งจะไม่ถูกลบออกจาก UTXO set ทำให้ฐานข้อมูล UTXO โตขึ้นเรื่อย ๆ หรือที่เรียกว่า "bloat"</p>
<p>โดยในที่สุดก็ได้มีการประนีประนอมโดยอนุญาตให้ output script ที่เริ่มต้นด้วย OP_RETURN สามารถเพิ่มข้อมูลที่ไม่เกี่ยวกับการชำระเงินลงใน transaction output ได้ อย่างไรก็ตาม แตกต่างจาก UTXO ปลอม OP_RETURN จะสร้างเอาต์พุตที่พิสูจน์ได้อย่างชัดเจนว่าไม่สามารถใช้จ่ายได้ ทำให้ไม่จำเป็นต้องถูกเก็บไว้ใน UTXO set เอาต์พุตที่มี OP_RETURN จะถูกบันทึกลงในบล็อกเชน ดังนั้นจึงใช้พื้นที่ดิสก์และเพิ่มขนาดบล็อกเชน แต่จะไม่ถูกเก็บใน UTXO set และจึงไม่ทำให้ Full node ต้องแบกรับต้นทุนฐานข้อมูลที่แพงขึ้น</p>
<p>OP_RETURN scripts จะมีรูปแบบประมาณนี้:</p>
<pre><code>OP_RETURN &lt;data&gt;
</code></pre>
<p>ส่วนข้อมูล (data portion) มักใช้แทนค่าแฮช เช่น เอาต์พุตจากอัลกอริทึม SHA256 ซึ่งมีขนาด 32 ไบต์ แอปพลิเคชันบางประเภทจะใส่คำนำหน้า (prefix) ไว้หน้าข้อมูล เพื่อช่วยระบุว่าเป็นข้อมูลของแอปพลิเคชันใด ตัวอย่างเช่น บริการรับรองเอกสารดิจิทัล Proof of Existence ใช้คำนำหน้าขนาด 8 ไบต์คือคำว่า DOCPROOF ซึ่งเข้ารหัสแบบ ASCII และแสดงในรูปเลขฐานสิบหกเป็น 44 4f 43 50 52 4f 4f 46</p>
<p>ควรทำความเข้าใจว่าไม่มี input script ใดที่สามารถสอดคล้องกับ OP_RETURN เพื่อนำมา “ใช้จ่าย” (spend) เอาต์พุตแบบ OP_RETURN ได้ จุดประสงค์ทั้งหมดของ OP_RETURN คือการสร้างเอาต์พุตที่ไม่สามารถใช้จ่ายได้ตั้งแต่ต้น ดังนั้นจึงไม่จำเป็นต้องถูกเก็บไว้ใน UTXO set ในฐานะเอาต์พุตที่อาจถูกใช้จ่ายในอนาคต กล่าวได้ว่า OP_RETURN outputs เป็นเอาต์พุตที่พิสูจน์ได้ชัดเจนว่าไม่สามารถใช้จ่ายได้ (provably unspendable)</p>
<p>โดยทั่วไป OP_RETURN outputs จะกำหนดจำนวนบิตคอยน์เป็นศูนย์ เพราะบิตคอยน์ใด ๆ ที่ถูกกำหนดไว้ในเอาต์พุตลักษณะนี้ จะถือว่าสูญหายไปตลอดกาล หากมีการอ้างอิง OP_RETURN output มาเป็น input ของธุรกรรมใด ธุรกรรมนั้นจะถูกเครื่องยนต์ตรวจสอบสคริปต์ (script validation engine) ยุติการทำงานทันทีและถูกตัดสินว่าไม่ถูกต้อง การทำงานของ OP_RETURN จะทำให้สคริปต์ “คืนค่า” FALSE และหยุดการประมวลผล ดังนั้น หากอ้างอิง OP_RETURN output มาเป็น input โดยไม่ตั้งใจ ธุรกรรมนั้นจะเป็นธุรกรรมที่ไม่ถูกต้องทันที</p>
<h2>ข้อจำกัดของ Transaction Lock Time</h2>
<p>การใช้ค่า lock time ทำให้ผู้ใช้จ่ายสามารถกำหนดเงื่อนไขได้ว่าธุรกรรมจะยังไม่ถูกนำไปรวมอยู่ในบล็อกจนกว่าจะถึงความสูงของบล็อก (block height) หรือเวลาที่กำหนดไว้ แต่ ไม่ได้ ป้องกันไม่ให้เงินก้อนนั้นถูกนำไปใช้จ่ายผ่านธุรกรรมอื่นก่อนหน้านั้น อธิบายด้วยตัวอย่างต่อไปนี้</p>
<p>Alice ลงนามในธุรกรรมที่ใช้จ่ายเอาต์พุตหนึ่งของเธอไปยังที่อยู่ของ Bob และตั้งค่า transaction lock time ไว้ล่วงหน้า 3 เดือน จากนั้น Alice ส่งธุรกรรมนั้นให้ Bob เก็บไว้ เมื่อเป็นเช่นนี้ Alice และ Bob จะทราบว่า:</p>
<ul>
<li>Bob ไม่สามารถกระจาย (broadcast) ธุรกรรมนี้เพื่อรับเงินได้ จนกว่าจะครบกำหนด 3 เดือน  </li>
<li>Bob สามารถกระจายธุรกรรมนี้ได้หลังจากครบ 3 เดือนแล้ว</li>
</ul>
<p>อย่างไรก็ตาม:</p>
<ul>
<li>Alice สามารถสร้างธุรกรรมอีกชุดที่ขัดแย้งกัน โดยใช้จ่ายอินพุตเดียวกันแต่ไม่ใส่ค่า lock time ซึ่งหมายความว่า Alice สามารถใช้จ่าย UTXO เดียวกันได้ก่อนครบ 3 เดือน  </li>
<li>Bob จึงไม่มีหลักประกันใด ๆ ว่า Alice จะไม่ทำเช่นนั้น</li>
</ul>
<p>สิ่งสำคัญคือ ต้องเข้าใจข้อจำกัดของ transaction lock time ให้ชัดเจน เงื่อนไขเพียงอย่างเดียวที่รับประกันได้คือ Bob จะไม่สามารถนำธุรกรรมที่ลงนามล่วงหน้าไปใช้ก่อนครบ 3 เดือนได้ แต่ ไม่มีการรับประกันว่า Bob จะได้รับเงินก้อนนั้นจริง หากต้องการให้ Bob ได้รับเงินอย่างแน่นอน แต่ไม่สามารถใช้จ่ายได้จนกว่าจะครบ 3 เดือน ต้องกำหนดเงื่อนไข timelock ไว้บนตัว UTXO เองในสคริปต์ แทนที่จะกำหนดไว้ในธุรกรรม ซึ่งทำได้ด้วยรูปแบบของ timelock ถัดไปที่เรียกว่า Check Lock Time Verify (CLTV)</p>
<h2>Check Lock Time Verify (OP_CLTV)</h2>
<p>ในเดือนธันวาคม ค.ศ. 2015 ได้มีการเพิ่มรูปแบบของ timelock แบบใหม่เข้ามาในบิตคอยน์ผ่านการอัปเกรดแบบ soft fork โดยอ้างอิงตามข้อกำหนดใน BIP65 มีการเพิ่มคำสั่งสคริปต์ใหม่ที่ชื่อว่า OP_CHECKLOCKTIMEVERIFY (OP_CLTV) เข้าไปในภาษา scripting ของบิตคอยน์ OP_CLTV เป็น timelock ระดับเอาต์พุต (per-output timelock) แตกต่างจาก lock time แบบเดิมซึ่งเป็น timelock ระดับธุรกรรม (per-transaction timelock) ทำให้สามารถกำหนดเงื่อนไขด้านเวลาได้ยืดหยุ่นมากยิ่งขึ้น</p>
<p>กล่าวโดยสรุป เมื่อมีการใส่ opcode OP_CLTV ลงไปในเอาต์พุต เอาต์พุตนั้นจะถูกจำกัดไม่ให้สามารถใช้จ่ายได้จนกว่าจะถึงเวลาที่กำหนดไว้</p>
<p>OP_CLTV ไม่ได้มาแทนที่ lock time แต่ทำหน้าที่จำกัด UTXO เฉพาะรายการ ให้สามารถถูกใช้จ่ายได้ก็ต่อเมื่อธุรกรรมที่จะนำมาใช้จ่ายนั้นมีค่า lock time ที่ตั้งไว้มากกว่าหรือเท่ากับค่าที่กำหนดใน OP_CLTV</p>
<p>คำสั่ง OP_CLTV รับพารามิเตอร์หนึ่งค่าเป็นอินพุต ซึ่งแสดงเป็นตัวเลขในรูปแบบเดียวกับ lock time (อาจเป็นความสูงของบล็อก หรือเวลา Unix epoch) และตามที่คำว่า VERIFY ระบุไว้ OP_CLTV เป็น opcode ประเภทที่หากผลลัพธ์เป็น FALSE จะหยุดการทำงานของสคริปต์ทันที แต่ถ้าผลลัพธ์เป็น TRUE สคริปต์จะทำงานต่อไป</p>
<p>ในการใช้งาน OP_CLTV จะต้องใส่คำสั่งนี้ไว้ใน redeem script ของเอาต์พุตในธุรกรรมที่สร้างเอาต์พุตนั้นขึ้นมา ตัวอย่างเช่น หากอลิซต้องการจ่ายเงินให้บ็อบ ปกติบ็อบอาจยอมรับการจ่ายเงินผ่านสคริปต์ P2SH ลักษณะดังนี้</p>
<pre><code>&lt;Bob's public key&gt; OP_CHECKSIG
</code></pre>
<p>เพื่อทำการล็อกเอาต์พุตไว้จนถึงเวลาที่กำหนด เช่น อีก 3 เดือนนับจากนี้ สคริปต์ P2SH ของบ็อบจะถูกเปลี่ยนเป็นแบบนี้แทน:</p>
<pre><code>&lt;Bob's pubkey&gt; OP_CHECKSIGVERIFY &lt;now + 3 months&gt; OP_CHECKLOCKTIMEVERIFY
</code></pre>
<p>โดยที่ค่า &lt;now {plus} 3 months&gt; คือความสูงบล็อกหรือค่าเวลาที่ประเมินว่าจะตรงกับอีก 3 เดือนหลังจากที่ธุรกรรมถูกขุดลงบล็อก เช่น ความสูงบล็อกปัจจุบัน + 12,960 บล็อก หรือ Unix epoch time ปัจจุบัน + 7,760,000 วินาที</p>
<p>เมื่อบ็อบต้องการใช้จ่าย UTXO นี้ เขาจะสร้างธุรกรรมใหม่ที่อ้างอิง UTXO นั้นเป็นอินพุต จากนั้นใส่ลายเซ็นและกุญแจสาธารณะของเขาในสคริปต์อินพุต พร้อมตั้งค่า lock time ของธุรกรรมให้มากกว่าหรือเท่ากับค่าทิมล็อกที่ระบุไว้ใน OP_CHECKLOCKTIMEVERIFY ที่อลิซกำหนด แล้วจึงกระจายธุรกรรมนี้ไปยังเครือข่ายบิตคอยน์</p>
<p>ธุรกรรมของบ็อบจะถูกประเมินดังนี้: หากค่าพารามิเตอร์ของ OP_CHECKLOCKTIMEVERIFY ที่อลิซตั้งไว้นั้นน้อยกว่าหรือเท่ากับ lock time ของธุรกรรมที่ใช้จ่าย สคริปต์จะทำงานต่อไป (มีผลเหมือนกับไม่มีการทำงาน หรือเหมือน opcode ประเภท OP_NOP) แต่ถ้าค่าดังกล่าวมากกว่า lock time การทำงานของสคริปต์จะหยุดลงทันที และธุรกรรมจะถูกมองว่าไม่ถูกต้อง</p>
<p>กล่าวให้ชัดเจนยิ่งขึ้น BIP65 อธิบายว่า OP_CHECKLOCKTIMEVERIFY จะล้มเหลวและหยุดการทำงานของสคริปต์ทันที หากเกิดเงื่อนไขอย่างใดอย่างหนึ่งต่อไปนี้:</p>
<ul>
<li>สแตกว่างเปล่า  </li>
<li>ค่าบนสุดของสแตกมีค่าน้อยกว่า 0  </li>
<li>ประเภทของ lock time (ความสูงบล็อกเทียบกับ timestamp) ของค่าบนสุดในสแตกไม่ตรงกับประเภทของ lock time ในฟิลด์ lock time ของธุรกรรม  </li>
<li>ค่าบนสุดของสแตกมีค่ามากกว่าค่า lock time ของธุรกรรม  </li>
<li>ฟิลด์ sequence ของอินพุตมีค่าเป็น 0xffffffff</li>
</ul>
<h3>Timelock Conflicts</h3>
<p>OP_CLTV และ locktime นั้นได้ใช้รูปแบบเดียวกันในการอธิบายระยะเวลาในการล๊อค ไม่ว่าจะเป็น block height หรือเมื่อเวลาผ่านไปในหน่วยของวินาทีตาม unix epoch ส่วนที่สำคัญคือ รูปแบบของ timelock จะต้องตรงกับรูปแบบของ OP_CLTV ใน output ทั้งคู่ต้องอ้างอิงแบบเดียวกัน คือจะเป็น block height หรือหน่วยวินาทีก็ตาม</p>
<p>สิ่งนี้หมายความว่าสคริปต์จะไม่มีทางเป็นสคริปต์ได้ ถ้ามีการเรียก OP_CLTV สองครั้งโดยใช้รูแปบบที่แตกต่างกัน คือครั้งนึงเป็น block height และ อีกครั้งหนึ่งเป็น timestamp ซึ่งส่วนนี้เองเป็นข้อผิดพลาดที่เกิดได้ง่ายและมักจะเกิดขึ้น เพราะฉะนั้นควรทดสอบสคริปต์อย่างละเอียดบน testnet หรือใช้เครื่องมือที่ออกแบบมาเพื่อป้องกันปัญหานี้ เช่น Miniscript compiler</p>
<p>ผลอีกอย่างที่สืบเนื่องกันมาจากข้อกำหนดที่ว่าหนึ่งธุรกรรมสามารถใช้ OP_CLTV ได้แบบเดียวเท่านั้น จึงทำให้ในธุรกรรมที่มีหลาย ๆ input โดยแต่ละ input ใช้รูปแบบของ OP_CLTV ที่แตกต่างกัน จะไม่สามารถนำมาสร้างธุรกรรมที่ใช้ input ทั้งสองพร้อมกันได้</p>
<p>โดยหลังจากมีการประมวลผล หากเงื่อนไขของ OP_CLTV เป็นจริง พารามิเตอร์ที่อยู่ก่อนหน้า OP_CLTV จะยังคงอยู่บนสุดของแสตก และอาจจำเป็นต้องเอาออกด้วย OP_DROP เพื่อให้การทำงานของ opcode อื่น ๆ ในสคริปต์ถัดไปได้เป็นไปอย่างถูกต้อง ด้วยเหตุนี้เราจึงมักจะเห็น OP_CHECKLOCKTIMEVERIFY จะถูกใช้ตามด้วย OP_DROP ในสคริปต์ต่าง ๆ เช่นเดียวกับ OP_CSV (จะอธิบายต่อในหัวข้อ Relative timelock) OP_CLTV มีพฤติกรรมที่แตกต่างจาก opcode ในกลุ่ม CHECKVERIFY ตัวอื่น ๆ ตรงที่ไม่ได้นำข้อมูลออกจากสแตก แต่ทั้งนี้ทั้งนั้นมันเป็นเพราะ ซอฟต์ฟอร์กที่เพิ่ม opcode เหล่านี้เข้าไป ได้อาศัยการนิยามใหม่ของ opcode ที่เคยเป็น no-operation (NOP) ซึ่งเดิมไม่ได้ลบข้อมูลออกจากสแตก และพฤติกรรมของ opcode NOP เดิมเหล่านั้นจำเป็นต้องถูกคงไว้เพื่อความเข้ากันได้</p>
<p>และด้วยการใช้ lock time ร่วมกันกับ OP_CLTV นั้นจะทำให้ในส่วนที่เราได้อธิบายไว้ในหัวข้อ ข้อจำกัดของ Transaction Lock Time เปลี่ยนแปลงไปเล็กน้อย เนื่องจากอลิสสามารถส่งบิตคอยน์ของเธอได้ทันที โดยกำหนดให้เงินถูกล๊อคไว้ที่กุญแจของบ๊อบตั้งแต่วินาทีนั้น อลิสจะไม่สามารถใช้เงินก้อนนี้ซ้ำได้อีก และในขณะเดียวกันบ๊อบก็ยังคงต้องรอ 3 เดือนก่อนตามที่กำหนดไว้</p>
<p>อยากตัวอย่างดังกล่าว ผู้อ่านทุกท่านคงจะได้เห็นแล้วว่าการนำ timelock เข้ามาไว้ในภาษา scripting ทำให้ OP_CLTV เปิดโอกาสให้เราสามารถพัฒนาสคริปต์ที่ซับซ้อนและน่าสนใจได้หลากหลายรูปแบบ</p>
<p>ซึ่งมาตรฐานดังกล่าวถูกกำหนดไว้ใน BIP65 (OP_CHECKLOCKTIMEVERIFY)</p>
<h3>Relative Timelocks</h3>
<p>Lock time และ OP_CLTV ทั้งคู่เป็น absolute timelocks กล่าวคือ เป็นการกำหนดเวลาที่ตายตัว ส่วนกลไก timelock อีกสองรูปแบบที่เราจะพิจรณาต่อไปนี้เป็น relative timelocks ซึ่งเป็นการกำหนดเงื่อนไขการใช้จ่าย output โดยอ้างอิงกับกับระยะเวลาตั้งแต่ธุกรรมก่อนหน้าได้รับการยืนยันและถูกบันทึกลงบนบล๊อคเชน</p>
<p>relative timelocks นั้นสามารถช่วยให้เราสามารถกำหนดข้อจำกัดในด้านของเวลาให้กำธุรกรรมนั้น ๆ โดยอ้างอิงจากเวลาที่ธุรกรรมก่อนหน้าถูกยืนยัน หรือกล่าวได้ว่าเวลาของธุรกรรมนั้นจะไม่ถูกนับจนกว่า UTXO ก่อนหน้าจะปรากฎบนบล๊อคเชน ฟังก์ชันนี้มีความสำคัญอย่างยิ่งกับการทำ bidirectional state channels และ Lightning Network (LN) ซึ่งเราจะได้อ่านเรื่องนี้เพิ่มเติมในหัวข้อ state channels </p>
<p>Relative timelocks นั้นได้มีการถูกนำมาใช้งานทั้งในระดับธุรกรรมและในระดับของสคริปต์ เช่นเดียวกันกับ absolute timelocks โดยในระดับธุรกรรมนั้นจะถูกใช้ในฟิลด์ของ sequence ซึ่งเป็นฟิลด์ที่ต้องอยู่ในส่วนของ input ในทุกธุรกรรมของบิตคอยน์ ส่วนในระดับของสคริปต์นั้นจะถูกเรียกใช้ผ่าน opcode ที่ชื่อ OP_CHECKSEQUENCEVERIFY (OP_CSV)</p>
<p>Relative timelocks ถูกนำมาใช้งานตามข้อกำหนดที่ระบุไว้ใน BIP68: Relative Lock-Time Using Consensus-Enforced Sequence Numbers และ BIP112: OP_CHECKSEQUENCEVERIFY</p>
<p>ทั้ง BIP68 และ BIP112 ถูกเปิดใช้งานในเดือนพฤษภาคม ปี 2016 ในรูปแบบของการอัปเกรดกฎ consensus แบบ soft fork</p>
<h3>Relative Timelocks with OP_CSV</h3>
<p>เช่นเดียวกันกับ OP_CLTV และ lock time ในระบบของบิตคอยน์มี opcode ในระดัสคริปต์สำหรับ relative timelock ที่อาศัยค่า sequence ภายในสคริปต์ด้วย opcode นั้นคือ OP_CHECKSEQUENCEVERIFY ซึ่งมักเรียกย่อว่า OP_CSV</p>
<p>เมื่อ opcode อย่าง OP_CSV ถูกประเมิณค่าในสคริปต์ของ UTXO จะอนุญาตให้ใช้จ่ายได้เฉพาะในธุรกรรมที่ค่า sequence ของ input มีค่ามากกว่าหรือเท่ากับพารามิเตอร์ของ OP_CSV เท่านั้น โดยสรุปแล้ว กลไกนี้จะจำกัดการใช้จ่าย UTXO จนกว่าจะมีจำนวนบล็อกหรือเวลาหน่วยวินาทีผ่านไปตามที่กำหนดไว้ นับจากเวลาที่ UTXO นั้นถูกขุดและบันทึกลงในบล็อกเชน</p>
<p>และเช่นเดียวกันกับ CLTV ค่าใน OP_CSV จะต้องมีรูปแบบที่สอดคล้องกับค่า sequence ที่ใช้คู่กัน โดยหาก OP_CSV ถูกกำหนดไว้เป็นจำนวนบล๊อก ค่า sequence ก็จำเป็นต้องอยู่ในรูปแบบของจำนวนบล๊อก และหาก OP_CSV ถูกกำหนดค่าไว้เป็นวินาที ค่า sequence ก็จำเป็นต้องเป็นค่าวินาทีเช่นกัน</p>
<blockquote>
<p>คำเตือน: สคริปต์ที่มีการเรียกใช้ OP_CSV หลายครั้ง จะต้องใช้รูปแบบเดียวกันทั้งหมด คือจะเป็นรูปแบบของวินาที ก็ต้องเป็นวินาทีทั้งหมด ถ้าเป็นจำนวนบล๊อกก็ต้องเป็นจำนวนบล๊อกทั้งหมดเท่านั้น การผสมทั้งสองรูปแบบเข้าด้วยกันจะทำให้สคริปต์นั้นถูกมองว่าไม่ถูกต้อง และไม่สามารถถูกใช้จ่ายได้ตลอดไป ซึ่งเป็นปัญหาเดียวกันกับ OP_CLTV ในหัวข้อ Timelock Conflicts แต่อย่างไรก็ตาม OP_CSV อณุญาติให้มี input ที่ถูกต้องได้หลายแบบภายในธุรกรรมเดียวกัน ดังนั้นปัญหาของการปฎิสัมพันธ์ข้าม input ที่เกิดขึ้นกับ OP_CLTV จึงจะไม่เกิดขึ้นกับ OP_CSV</p>
</blockquote>
<p>relative timelock ด้วย OP_CSV นั้นมีประโยชน์อย่างยิ่งในกรณีที่มีการสร้างและลงนามธุรกรรมหลายรายการที่เชื่อมต่อกันเป็นลูกโซ่ แต่ยังไม่ถูกเผยแพร่ไปยังเครือข่าย นั่นคือถูกเก็บไว้นอกบล็อกเชน ธุรกรรมลูกเหล่านั้นจะไม่สามารถถูกใช้งานได้จนกว่าธุรกรรมแม่จะถูกเผยแพร่ ถูกขุดรวมเข้าไปในบล็อกเชน และมีอายุผ่านไปตามระยะเวลาที่ระบุไว้ใน relative timelock การใช้งานลักษณะนี้แสดงตัวอย่างไว้ใน state channels และ lightning network</p>
<p>OP_CSV ถูกกำหนดรายละเอียดไว้ใน BIP112 และ CHECKSEQUENCEVERIFY</p>
<h3>สคริปต์ที่มีการควบคุมลำดับการทำงาน (เงื่อนไขหลายเงื่อนไข)</h3>
<p>หนึ่งในสิ่งที่น่าสนใจ และคุณสมบัติที่ทรงพลังมาก ๆ ของ Bitcoin script คือการควบคุมลำดับการทำงาน (Flow control) หรือที่เลือกว่าเงื่อนไขหลายเงื่อนไข คุณน่าจะคุ้นเคยกับแนวคิดของ flow control จากภาษาโปรแกรมต่าง ๆ ที่ใช้โครงสร้าง IF…THEN…ELSE เงื่อนไขใน Bitcoin Script แม้จะมีรูปแบบแตกต่างออกไปเล็กน้อย แต่โดยรวมแล้วเป็นโครงสร้างเดียวกัน</p>
<p>ในระดับพื้นฐาน opcode นั้นมีเงื่อนไขของบิตคอยน์ที่ช่วยให้เราสามารถสร้างสคริปต์ที่มีวิธีปลดล็อกได้สองแบบ ขึ้นอยู่กับผลลัพธ์ TRUE/FALSE จากการประเมินเงื่อนไขในทางตรรกะ ตัวอย่างเช่น หากค่า x เป็น TRUE เส้นทางของโค้ดที่ถูกประมวลผลคือ A และหากไม่เป็นเช่นนั้น (ELSE) เส้นทางของโค้ดคือเส้นทาง B</p>
<p>นอกจากนี้ นิพจน์ต่าง  ๆ ภายในเงื่อนไขของบิตคอยน์ยังสามารถทับซ้อนกันได้อย่างไม่จำกัด หรือกล่าวได้ว่าภายในเงื่อนไขใด ๆ สามารถอีกอีกเงื่อนไขอยู่ด้านในได้ สคริปต์ของบิตคอยน์ที่มีการควบคุมลำดับการทำงาน จึงสามารถใช้สร้างสคริปต์ที่ซับซ้อนมาก โดยมีเส้นทางการประมวลผลที่เป็นไปได้หลายร้อยแบบ แม้จะไม่มีข้อจำกัดด้านระดับความลึกของการซ้อนเงื่อนไขแต่กฎฉันทามติ ก็ได้กำหนดข้อจำกัดไว้ที่ขนาดสูงสุดของสคริปต์ในหน่วยไบต์</p>
<p>บิตคอยน์ได้ใช้การควบคุมระดับการทำงานผ่าน opcode  อย่าง OP_IF OP_ELSE OP_ENDIF และ OP_NOTIF นอกจากนี้นิพจน์เงื่อนไขยังสามารถมีตัวดำเนินการเชิงตระกะอย่าง OP_BOOLAND, OP_BOOLOR และ OP_NOT ได้ด้วย</p>
<p>ในภาษาโปรแกรมแบบดั้งเดิม (เชิงกระบวนการ) ส่วนใหญ่ การควบคุมลำดับการทำงานจะมีลักษณะดังนี้:<br>pseudocode ของการควบคุมลำดับการทำงานในภาษาโปรแกรมส่วนใหญ่</p>
<pre><code>if (condition):
  code to run when condition is true
else:
  code to run when condition is false
endif
code to run in either case
</code></pre>
<p>แต่ในภาษาที่อิงกับสแตก (stack-based) อย่าง Bitcoin Script เงื่อนไขเชิงตรรกะจะมาก่อนคำสั่ง IF ซึ่งทำให้โครงสร้างดูเหมือน “กลับด้าน”:<br>การควบคุมลำดับการทำงานใน Bitcoin Script</p>
<pre><code>condition
IF
  code to run when condition is true
OP_ELSE
  code to run when condition is false
OP_ENDIF
code to run in either case
</code></pre>
<p>เมื่ออ่าน Bitcoin Script ให้จำไว้ว่า เงื่อนไขที่ถูกนำมาประเมินค่าจะมาก่อน opcode IF เสมอ</p>
<h2>เงื่อนไขแบบมี verify opcode</h2>
<p>เงื่อนไขในอีกรูปแบบหนึ่งของบิตคอยน์สคริปต์ คือ opcode ใด ๆ ที่ลงท้ายด้วย VERIFY โดยคำต่อท้าย VERIFY หมายความว่า หากเงื่อนไขที่ถูกประเมินค่าไม่เป็น TRUE การทำงานของสคริปต์จะถูกยุติทันที และธุรกรรมจะถูกพิจารณาว่าไม่ถูกต้อง</p>
<p>ซึ่งแตกต่างจากการทำงานของ IF ซึ่งได้เปิดโอกาสให้มีเส้นทางการทำงานหลายรูปแบบ opcode ที่ลงท้ายด้วย VERIFY จะทำหน้าที่เป็น guard clause หรือก็คือตรรกะที่ต้องประเมินผลให้เป็นจริง เพื่อให้การดำเนินการของโปรแกรมสามารถดำเนินต่อไปได้</p>
<p>ตัวอย่างเช่น ในสคริปต์ต่อไปนี้ได้มีการกำหนดให้ต้องมีทั้งลายเซ็นของบ๊อบและค่า preimage ที่เมื่อแฮชแล้วต้องได้ค่าตามที่กำหนด ทั้งสองเงื่อนไขนี้เองต้องถูกทำให้เป็นจริงจึงจะสามารถปลดล๊อคได้:</p>
<p>สคริปต์ที่มี OP_EQUALVERIFY เป็น guard clause</p>
<pre><code>OP_HASH160 &lt;expected hash&gt; OP_EQUALVERIFY &lt;Bob's Pubkey&gt; OP_CHECKSIG
</code></pre>
<p>เพื่อที่จะใช้จ่ายเงินก้อนนี้บ๊อบจำเป็นต้องแสดง preimage และลายเซ็นที่ถูกต้องเท่านั้น</p>
<pre><code>&lt;Bob's Sig&gt; &lt;hash pre-image&gt;
</code></pre>
<p>หากไม่ได้นำเสนอ preimage มาก่อน Bob จะไม่สามารถไปถึงส่วนของสคริปต์ที่ตรวจสอบลายเซ็นของเขาได้</p>
<p>สคริปต์นี้สามารถเขียนใหม่โดยใช้ OP_IF แทนได้ดังนี้:</p>
<p>สคริปต์ที่มี IF ทำหน้าที่เป็น guard clause</p>
<pre><code>OP_HASH160 &lt;expected hash&gt; OP_EQUAL
OP_IF
   &lt;Bob's Pubkey&gt; OP_CHECKSIG
OP_ENDIF
</code></pre>
<p>ข้อมูลยืนยันตัวตนของ Bob ยังคงเหมือนเดิม:</p>
<p>การทำให้สคริปต์ข้างต้นเป็นจริง</p>
<pre><code>&lt;Bob's Sig&gt; &lt;hash pre-image&gt;
</code></pre>
<p>สคริปต์ที่มีการใช้ OP_IF ให้ผลลัพธ์เหมือนกับการใช้ opcode ที่มีคำต่อท้าย VERIFY โดยทั้งสองแบบทำหน้าที่เป็น guard clause เหมือนกัน แต่อย่างไรก็ตาม โครงสร้างที่ใช้ VERIFY ใช้จะมีประสิทธิภาพมากกว่า เนื่องจากมีการใช้ opcode น้อยกว่าสองตัว</p>
<p>แล้วเมื่อไหร่ที่เราควรใช้ VERIFY แล้วเมื่อไหร่ที่เราควรใช้ OP_IF ? หากสิ่งที่เราต้องการทำมีเพียงการกำหนดเงื่อนไขเบื้องต้น (guard clause) VERIFY จะเป็นทางเลือกที่ดีกว่า แต่หากเราต้องการให้มีเส้นทางการทำงานมากกว่าหนึ่งแบบ (flow control) เราจำเป็นต้องใช้โครงสร้างควบคุมลำดับการทำงานแบบ OP_IF...OP_ELSE แทน</p>
<h3>การใช้ Flow control ในสคริปต์</h3>
<p>วิธีใช้การควบคุมลำดับการทำงานโดยทั่วไปภายในสคริปต์ของบิตคอยน์นั้นมักเป็นการสร้างสคริปต์ที่เปิดโอกาสให้สามารถสร้างเส้นทางการทำงานของสคริปต์ได้หลายแบบ โดยแต่ละเส้นทางก็มีวิธีการในการใช้จ่าย UTXO ที่แตกต่างกัน </p>
<p>ลองมาพิจรณาจากตัวยอ่างง่าย ๆ นี้กัน โดยเรามีผู้ที่สามารถลงนามในธุรกรรมนี้ได้สองคน นั้นคืออลิซและบ๊อบโดยขอแค่เป็นใครคนใดคนหนึ่งในสองคนนี้ก็สามารถใช้จ่ายได้ โดยจะเขียนออกมาเป็น 1-of-2 multisig แต่เราจะทำสิ่งเดียวกันนี้โดยใช้คำสั่ง OP_IF:</p>
<pre><code>OP_IF
 &lt;Alice's Pubkey&gt;
OP_ELSE
 &lt;Bob's Pubkey&gt;
OP_ENDIF
OP_CHECKSIG
</code></pre>
<p>ถ้ามองสคริปต์ในตัวอย่างแล้วคุณอาจสงสัยว่า แล้วเงื่อนไขอยู่ตรงไหน? ทำไมไม่มีอะไรอยู่ก่อนคำสั่ง IF เลย</p>
<p>ส่วนเหตุผลนั้นก็เป็นเพราะว่าเงื่อนไขนั้นไม่ได้เป็นส่วนหนึ่งของสคริปต์ แต่จะถูกส่งมาในตอนที่มีการใช้จ่ายแทน ซึ่งทำให้ Alice และ Bob สามารถ “เลือก” เส้นทางการทำงานที่ต้องการได้:</p>
<pre><code>&lt;Alice's Sig&gt; OP_TRUE
</code></pre>
<p>OP_TRUE ที่อยู่ท้ายสุดทำหน้าที่เป็นเงื่อนไข (TRUE) ซึ่งจะทำให้คำสั่ง OP_IF เลือกเส้นทางการใช้จ่ายแรก โดยในเงื่อนไขนี้จะทำให้ public key ที่อลิซมีลายเซ็นตรงกันถูกนำขึ้นสแตก Opcode OP_TRUE หรือที่เรียกว่า OP_1 จะใส่ค่าเลข 1 ลงบนสแตก</p>
<p>ส่วนสำหรับบ๊อบหากต้องการใช้จ่าย UTXO นี้ เขาจะต้องเลือกเส้นทางการทำงานที่สองของ OP_IF โดยให้ค่า FALSE แทน Opcode OP_FALSE หรือที่เรียกว่า OP_0 จะใส่ค่าเป็นอาร์เรย์ไบต์ว่างลงบนสแตก:</p>
<pre><code>&lt;Bob's Sig&gt; OP_FALSE
</code></pre>
<p>input สคริปต์ของ Bob จะทำให้คำสั่ง OP_IF ไปสคริปต์ในส่วนที่สอง (OP_ELSE) ซึ่งต้องการลายเซ็นของบ๊อบแทน</p>
<p>เนื่องจากคำสั่ง OP_IF สามารถซ้อนกันได้ เราจึงสามารถสร้าง “เขาวงกต” ของเส้นทางการทำงานได้ โดย input script จะทำหน้าที่เสมือน “แผนที่” ที่ใช้เลือกว่าจริง ๆ แล้วเส้นทางการทำงานใดจะถูกนำมาใช้</p>
<pre><code>OP_IF
  subscript A
OP_ELSE
  OP_IF
    subscript B
  OP_ELSE
    subscript C
  OP_ENDIF
OP_ENDIF
</code></pre>
<p>ในตัวอย่างนี้จะมีเส้นทางการทำงานทั้งหมดสามเส้นทางด้วยกัน (subscript A, subscript B, subscript C) โดย input script จะเป็นผู้กำหนดเส้นทางตามรูปแบบของลำดับค่า TRUE หรือ FALSE อย่างเช่น หากต้องการเลือกเส้นทาง subscript B input script จะต้องลงท้ายด้วย OP_1 OP_0 (TRUE, FALSE) ค่าเหล่านี้จะถูกนำขึ้นแสตกทำให้ค่า FALSE อยู่จุดบนสุดของแสตก ซึ่ง OP_IF ชั้นนอกจะทำการดึงค่า FALSE ออกมาและนำส่วน OP_ELSE แรก จากนั้นค่า TRUE จะเลื่อนขึ้นมาอยู่บนสุดของแสตก และจะถูกประเมิณโดย OP_IF ชั้นใน ซึ่งจะเลือกเส้นทางการทำงานแบบ B</p>
<p>ด้วยโครงสร้างของสคริปต์แบบนี้ เราจะสามารถสร้าง redeem script ที่มีเส้นทางการทำงานได้หลายสิบ หรือหลายร้อยเส้นทาง โดยแต่ละเส้นจะเสนอวิธีการใช้จ่าย UTXO ที่แตกต่างกันออกไป เมื่อเราต้องการใช้จ่าย เราจะสร้าง input script ที่นำทางไปตามเส้นทางการทำงานนั้น ๆ ด้วยการใส่ค่า TRUE และ FALSE ที่เหมาะสมลงบนสแตกในแต่ละจุดของการควบคุมลำดับการทำงาน</p>
<h3>ตัวอย่างสคริปต์แบบซับซ้อน</h3>
<p>ในส่วนนี้เราจะนำแนวคิดทั้งหมดที่ได้เรียนรู้จากบทนี้มารวมกันไว้ในตัวอย่างเดียว</p>
<p>สมมุติว่าโมฮัมเหม็ด ซึ่งเป็นเจ้าของบริษัทนำเข้าและส่งออกในดูไบ เขาต้องการสร้างบัญชีเงินทุนของบริษัทที่มีกฏเกณฑ์ที่ยืดหยุ่น และแผนของเขาคือการออกแบบมาเพื่อให้ใช้ระดับในการอณุญาติที่แตกต่างกันไปตามเงื่อนไขของ timelock และผู้เข้าร่วมโครงสร้าง multisig นี้จะประกอบไปด้วย โมฮัมเหม็ด หุ้นส่วนอย่าง ซาอีดและไซรา และสุดท้ายเป็นทนายความของบริษัท โดยหุ้นส่วนทั้งสามตัดสินใจโดยใช้หลักเสียงข้างมาก ดังนั้นจะต้องมีอย่างน้อยสองในสามคนเห็นชอบสำหรับกรณีต่าง ๆ แต่อย่างไรก็ตาม ในกรณีที่เกิดปัญหากับ key ของพวกเขา พวกเขาเพียงต้องการให้ทนายความสามารถกู้คืนเงินได้ โดยใช้ลายเซ็นของหุ้นส่วนคนใดคนหนึ่งจากสามคนร่วมด้วย สุดท้ายแล้วหากหุ้นส่วนทั้งหมดไม่สามารถติดต่อได้หรือไม่สามารถปฏิบัติน่าที่ได้เป็นระยะเวลาหนึ่ง พวกเขาต้องการให้ทนายความสามารถจัดการบัญชีได้โดยตรงหลังจากที่เขาได้รับสิทธ์เข้าถึงบันทึกธุรกรรมของบัญชีเงินทุนนี้ได้</p>
<p>สคริปต์ไถ่ถอนแบบ multisignature ที่เปลี่ยนแปลงได้ร่วมกันกับ timelock คือสิ่งที่โมฮัมเหม็ดต้องออกแบบขึ้นมาเพื่อให้บรรลุเป้าหมายนี้</p>
<p>ตัวอย่างที่ 1: การใช้ Multisignature ร่วมกับ timelock</p>
<pre><code>01  OP_IF
02    OP_IF
03      2
04    OP_ELSE
05      &lt;30 days&gt; OP_CHECKSEQUENCEVERIFY OP_DROP
06      &lt;Lawyer's Pubkey&gt; OP_CHECKSIGVERIFY
07      1
08    OP_ENDIF
09    &lt;Mohammed's Pubkey&gt; &lt;Saeed's Pubkey&gt; &lt;Zaira's Pubkey&gt; 3 OP_CHECKMULTISIG
10  OP_ELSE
11    &lt;90 days&gt; OP_CHECKSEQUENCEVERIFY OP_DROP
12    &lt;Lawyer's Pubkey&gt; OP_CHECKSIG
13  OP_ENDIF
</code></pre>
<p>สคริปต์ของโมฮัมเหม็ดใช้โครงสร้างควบคุมลำดับการทำงานแบบ OP_IF…OP_ELSE ที่ซ้อนกันเพื่อสร้างเส้นทางการทำงานทั้งหมดสามเส้นทาง</p>
<p>ในเส้นทางการทำงานเส้นทางแรก สคริปต์นี้จะทำงานเหมือน multisig 2-of-3 ธรรมดา ๆ ระหว่างหุ้นส่วนทั้งสาม โดยเส้นทางการทำงานนี้ประกอบด้วยบรรทัดที่ 3 และบรรทัดที่ 9 โดยบรรทัดที่ 3 กำหนดโดย quorum ของ multisig เป็น 2 (2-of-3) เส้นทางการทำงานนี้สามารถถูกเลือกได้โดยการใส่ OP_TRUE OP_TRUE ต่อท้ายใน input script: </p>
<p>ข้อมูลการใช้จ่ายสำหรับเส้นทางการทำงานแรก (multisig แบบ 2-of-3)</p>
<pre><code>OP_0 &lt;Mohammed's Sig&gt; &lt;Zaira's Sig&gt; OP_TRUE OP_TRUE
</code></pre>
<blockquote>
<p>TIP: OP_0 ที่อยู่ตอนต้นของ input script นี้มีสาเหตุมาจากความผิดปกติของ OP_CHECKMULTISIG ซึ่งจะดึงค่าออกจากแสตกมากกว่าที่ควรหนึ่งค่า ค่าที่ถูกดึงออกมาเกินนี้จะถูกละเลยโดย OP_CHECKMULTISIG แต่มันจำเป็นต้องมีอยู่ มิฉะนั้นสคริปค์จะล้มเหลว การใส่อาร์เรย์ไบต์ว่างด้วย OP_0 จึงเป็นวิธีแก้ปัญหาชั่วคราวสำหรับความผิดปกตินี้ ตามที่อธิบายไว้ในหัวข้อ An Oddity in CHECKMULTISIG Execution</p>
</blockquote>
<p>ในเส้นทางการทำงานเส้นทางที่สองสามารถถูกใช้งานได้ก็ต่อเมื่อผ่านไปแล้ว 30 วันหลังจากที่ UTXO ถูกสร้างขึ้น และเมื่อถึงเวลานั้น สคริปต์จะต้องการลายเซ็นของทนายความ และลายเซ็นจากหนึ่งในหุ้นส่วน (multisig 1 of 3) โดยเงื่อนไขนี้ถูกกำหนดโดยบรรทัดที่ 7 ซึ่งตั้งค่า quorum ของ multisigไว้ที่ 1 และเพื่อเลือกใช้เส้นทางการทำงานนี้ input script จะต้องลงท้ายด้วย OP_FALSE OP_TRUE:</p>
<p>ข้อมูลการใช้จ่ายสำหรับเส้นทางการทำงานที่สอง (ทนายความ + multisig แบบ 1-of-3)</p>
<pre><code>OP_0 &lt;Saeed's Sig&gt; &lt;Lawer's Sig&gt; OP_FALSE OP_TRUE
</code></pre>
<blockquote>
<p>TIP: OP_FALSE จะถูกใส่ลงบนสแตกก่อน จากนั้น OP_TRUE จะถูกใส่ลงไปอยู่ด้านบนของมัน ดังนั้นค่า TRUE จึงถูกดึง (pop) ออกมาก่อนโดย opcode OP_IF ตัวแรก</p>
</blockquote>
<p>สุดท้าย เส้นทางการทำงานที่สามอนุญาตให้ทนายความสามารถใช้จ่ายเงินได้เพียงคนเดียว แต่จะทำได้ก็ต่อเมื่อผ่านไปแล้ว 90 วันเท่านั้น เพื่อเลือกเส้นทางการทำงานนี้  Input script จะต้องลงท้ายด้วย OP_FALSE:</p>
<p>ข้อมูลการใช้จ่ายสำหรับเส้นทางการทำงานที่สาม (ทนายความ)</p>
<pre><code>&lt;Lawyer's Sig&gt; OP_FALSE
</code></pre>
<p>ลองจำลองการรันสคริปต์บนกระดาษเพื่อดูว่ามันทำงานอย่างไรบนสแตก</p>
<h2>Pay to witness public key hash (P2WPKH)</h2>
<p>เริ่มจากดูตัวอย่างของ output script แบบ P2PKH กันก่อน:</p>
<p>ตัวอย่าง output script P2PKH</p>
<pre><code>OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7
OP_EQUALVERIFY OP_CHECKSIG
</code></pre>
<p>เมื่อใช้ segregated witness อลิซจะสร้างสคริปต์แบบ P2WPKH หากสคริปต์นั้นผูกกับ public key เดียวกัน ก็จะมีหน้าตาประมาณนี้: </p>
<pre><code>0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7
</code></pre>
<p>อย่างที่เห็นว่า output script แบบ P2WPKH นั้นเรียบง่ายกว่าสคริปต์แบบ P2PKH เป็นอย่างมากถ้าเราเอามาเปรียบเทียบกัน มันประกอบด้วยค่าเพียงแค่สองค่าเท่านั้นที่จะถูกใส่ลงในแสตกสำหรับการประมวลผลสคริปต์ โดยสำหรับไคล์เอนต์บิตคอยน์รุ่นเก่า ๆ (ที่ยังไม่รองรับ segwit) การใส่สองค่านี้จะดูเหมือน output ที่ใครก็สามารถใช้จ่ายได้ แต่สำหรับไคลเอนต์รุ่นใหม่ที่รองรับ segwit ตัวเลขตัวแรก (0) จะถูกตีความเป็นหมายเลขเวอร์ชัน (witness version) และส่วนที่สอง (ขนาด 20 ไบต์) คือ witness program โดย witness program ขนาด 20 ไบต์นี้ก็คือแฮชของ public key เช่นเดียวกับในสคริปต์แบบ P2PKH</p>
<p>ตอนนี้เรามาดูธุรกรรมที่เกี่ยวข้องกันซึ่งบ๊อบใช้เพื่อจ่าย output นี้กัน โดยสำหรับสคริปต์แบบ lagacy นั้น ธุรกรรมที่ใช้จ่ายนั้นจะต้องใส่ลายเซ็นไว้ในส่วน input ของธุรกรรม:</p>
<pre><code>[...]
"vin" : [
  "txid": "abcdef12345...",
  "vout": 0,
  "scriptSig": “&lt;Bob’s scriptSig&gt;”,
]
[...]
</code></pre>
<p>แต่อย่างไรก็ตาม เพื่อใช้จ่าย output แบบ P2WPKH ธุรกรรมจะไม่มีลายเซ็นอยู่ในส่วนของ input เลย แต่แทนที่จะเป็นเช่นนั้น ธุรกรรมของบ๊อบจะมี input script ที่ว่างเปล่า และจะใส่ข้อมูลไว้ในส่วนของ witness แทน:</p>
<pre><code>[...]
"vin" : [
  "txid": "abcdef12345...",
  "vout": 0,
  "scriptSig": “”,
]
[...]
“witness”: “&lt;Bob’s witness structure&gt;”
[...]
</code></pre>
<h2>การสร้างกระเป๋าเงินแบบ P2WPKH</h2>
<p>สิ่งที่สำคัญที่สุดและควรจะทราบไว้คือ witness program แบบ P2WPKH ควรจะถูกสร้างขึ้นโดยผู้รับเงินเท่านั้น และไม่ควรแปลงมาจาก public key ของผู้ที่รู้จักอยู่แล้ว เพราะสคริปต์ของ P2PKH หรือแม้แต่จาก address ใด ๆ นั้นผู้ใช้จ่ายไม่มีทางรู้ได้เลยว่ากระเป๋าเงินของผู้รับนั้นสามารถสร้างธุรกรรมแบบ segwit และสามารถใช้จ่าย output แบบ P2WPKH ได้หรือไม่</p>
<p>นอกจากนี้ output แบบ P2WPKH นั้นจะต้องถูกสร้างจาก hash ของ compressed public key เท่านั้น เพราะว่า public key แบบ uncompressed นั้นถือว่าไม่อยู่ในมาตรฐานของ segwit และอาจจะถูกปิดการใช้งานอย่างชัดเจนใน soft fork ที่เกิดขึ้นภายหลัง หาก hash ที่ใช้ได้ใน P2WPKH มาจาก public key แบบ uncompressed จะทำให้ output ที่เกิดขึ้นนั้นไม่สามารถจ่ายออกได้อีก และจะทำให้คุณอาจที่จะสูญเสียเงินนั้นไปตลอดกาล นั่นจึงเป็นเหตุผลที่ว่าทำไม output แบบ P2WPKH นั้นถึงควรถูกสร้างโดยแอปกระเป๋าเงินของผู้รับผ่านการสร้าง compress public key จาก private key ของเขาเอง</p>
<blockquote>
<p>Warning: P2WPKH ควรถูกสร้างโดยผู้รับ โดยการนำ public key แบบ compressed มาแปลงเป็น P2WPKH hash เท่านั้น ไม่ควรให้ผู้ใช้หรือบุคคลอื่นใด แปลง P2PKH script, Address, หรือ public key แบบ uncompressed ให้กลายเป็น P2WPKH witness script ซึ่งเป็นเรื่องปกติอยู่แล้วที่ผู้ใช้ควรส่งเงินให้ผู้รับ ในรูปแบบที่ผู้รับได้ระบุไว้เท่านั้น</p>
</blockquote>
<h3>Pay to witness script hash (P2WSH)</h3>
<p>witness program ประเภทนี้จะสอดคล้องกันกับ P2SH ที่เราได้ผ่านกันมาก่อนหน้านี้ ในหัวข้อของ ในหัวข้อ Pay to Script Hash และในตัวอย่างที่บริษัทของนายโมฮัมเหม็ดได้ใช้สคริปต์แบบ P2SH เพื่อทำ multisig โดยสคริปต์ที่ถูกเข้ารหัสของเขาจะมีหน้าตาแบบนี้</p>
<pre><code>OP_HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e OP_EQUAL
</code></pre>
<p>สคริปต์ P2SH นี้อ้างอิงไปยังแฮชของ redeem script ซึ่งได้กำหนดเงื่อนไขการใช้จ่ายแบบ multisig 2-of-3 เอาไว้เพื่อใช้ในการปลดล๊อคเงินทุน การจะใช้จ่าย output นี้ บริษัทของโมฮัมเหม็ดจะต้องแสดง redeem script ซึ่งมีค่าแฮชตรงกันกับ script hash ที่อยู่ภายใน P2SH output พร้อมกับลายเซ็นที่จำเป็นตามเงื่อนไขของ redeem script นั้น โดยใส่ทั้งหมดไว้ภายใน transaction input:</p>
<pre><code>[...]
"vin" : [
  "txid": "abcdef12345...",
  "vout": 0,
  "scriptSig": “&lt;SigA&gt; &lt;SigB&gt; &lt;2 PubA PubB PubC PubD PubE 5 OP_CHECKMULTISIG&gt;”,
]
</code></pre>
<p>ทีนี้เรามาดูกันดีกว่าว่าตัวอย่างที่เกริ่นมาข้างต้นนี้จะถูกอัปเกรดเป็น segwit v0 ได้อย่างไร หากลูกค้าของนายโมฮัมเหม็ดใช้กระเป๋าเงินที่รับรอง segwit พวกเขาจะทำการชำระเงินได้โดยการสร้าง P2WSH output ซึ่งมีลักษณะดังนี้:</p>
<pre><code>0 a9b7b38d972cabc7961dbfbcb841ad4508d133c47ba87457b4a0e8aae86dbb89
</code></pre>
<p>เช่นเดียวกันกับตัวอย่างของ P2WPKH คุณจะเห็นได้ว่าสคริปต์แบบ segregated witness ที่ทำงานในแบบเดียวกันนั้นเรียบง่ายกว่า และนอกจากนี้ยังช่วยลด overhead ของเทมเพลตที่พบในสคริปต์ P2SH ลงไปได้มากอีกด้วย เนื่องจากสคริปต์ของ segreted witness นั้นประกอบด้วยค่าเพียง 2 ค่าที่ถูกใส่ลงในแสตก นั่นคือ witness version(0) และค่าแฮชของ SHA256 ขนาด 32 ไบต์ของ witness script (ในบางครั้งอาจถูกเรียกว่า witness program)</p>
<blockquote>
<p>TIP: ในขณะที่ P2SH ใช้แฮชแบบ RIPEMD160(SHA256(script)) ขนาด 20 ไบต์ แต่ witness program ของ P2WSH จะใช้แฮชแบบ SHA256(script) ขนาด 32 ไบต์ ความแตกต่างในการเลือกอัลกอริทึมในการแฮชนี้ถูกออกแบบมาเพื่อเพิ่มความแข็งแกร่งในด้านความปลอดภัยให้กับ P2WSH แต่ในบางกรณีก็มีบ้างที่ความปลอดภัยระดับ 128 บิตใน P2WSH นั้นมีความปลอดภัยเท่ากับ 80 บิตใน P2SH ดูรายละเอียดเพิ่มเติมได้ที่ P2SH collision attack</p>
</blockquote>
<p>หลังจากนั้นบริษัทของนายโมฮัมเหม็ดก็สามารถใช้จ่าย output แบบ P2WSH ได้โดยการนำเสนอ witness script ที่ถูกต้องพร้อมกับลายเซ็นในจำนวนที่เพียงพอเพื่อทำให้สคริปต์นั้นผ่านเงื่อนไขของ witness script และลายเซ็นเหล่านี้เองจะถูกรวมอยู่ในโครงสร้างของ witness โดยจะไม่มีข้อมูลใดถูกใส่ไว้ใน input script เนื่องจากนี่เป็น native witness program ซึ่งไม่ใช้ฟิลด์ input script เหมือนกับประเภท legacy</p>
<pre><code>[...]
"vin" : [
  "txid": "abcdef12345...",
  "vout": 0,
  "scriptSig": “”,
]
[...]
“witness”: “&lt;SigA&gt; &lt;SigB&gt; &lt;2 PubA PubB PubC PubD PubE 5 OP_CHECKMULTISIG&gt;”
[...]
</code></pre>
<h3>ความแตกต่างระหว่าง P2WPKH และ P2WSH</h3>
<p>ในสองหัวข้อก่อนหน้านี้เราได้อธิบายถึง witness program อยู่ 2 ประเภท ได้แก่ Pay to Witness Public Key Hash (P2WPKH) และ Pay to Witness Script Hash (P2WSH) โดย witness program ของทั้งสองประเภทนั้นมีหมายเลขเวอร์ชันเดียวกัน ตามด้วยการใส่ข้อมูลหนึ่งค่าลงในแสตกเหมือนกัน ทำให้รูปแบบของทั้งสองดูคล้ายคลึงกัน แต่การตีความของทั้งสองนั้นแตกต่างกันอย่างสิ้นเชิง โดยแบบหนึ่งจะถูกตีความเป็นแฮชของ public key ซึ่งปลดล๊อกได้ด้วยลายเซ็นในขณะที่อีกแบบหนึ่งนั้นจะถูกตีความเป็นค่าแฮชของสคริปต์ซึ่งปลดล๊อกได้ด้วย witness script</p>
<p>และความแตกต่างที่สำคัญที่สุดระหว่างทั้งสองคือความยาวของ witness program:</p>
<ul>
<li>witness program ของ P2WPKH มีขนาด 20 ไบต์  </li>
<li>witness program ของ P2WSH มีขนาด 32 ไบต์</li>
</ul>
<p>ความแตกต่างเพียงจุดเดียวนี้เองทำให้โหนดของบิตคอยน์สามารถแยกได้ว่า witness program นั้นเป็นของสคริปต์รูปแบบใด โดยดูจากเพียงแค่ความยาวของแฮช</p>
<h3>การอัปเกรดไปใช้ Segregated Witness</h3>
<p>จากตัวอย่างก่อนหน้านี้ เราจะเห็นได้ว่าการอัปเกรดไปใช้ segregated witness นั้นเป็นกระบวนการที่มี 2 ขั้นตอนด้วยกัน ขั้นตอนแรก คือกระเป๋าเงินต้องสามารถสร้าง output แบบ segwit ได้ จากนั้น output เหล่านี้จึงสามารถถูกใช้ได้ด้วยกระเป๋าเงินที่รู้วิผะีสร้างธุรกรรมแบบ segregated witness โดยในตัวอย่างที่กล่าวมากระเป๋าเงินของอลิซสามารถสร้าง output จ่ายไปยังสคริปต์แบบ segregated witness ได้และกระเป๋าของบ๊อบก็รองรับ segwit และสามารถใช้จ่ายได้เช่นกัน</p>
<p>Segregated witness ถูกนำมาใช้ในลักษณะของการอัปเกรดที่ยังคงเข้ากันได้ย้อนหลัง (backward-compatible) ซึ่งทำให้ไคลเอนต์ทั้งแบบเก่าและแบบใหม่สามารถอยู่ร่วมกันได้ นักพัฒนากระเป๋าเงินได้อัปเกรดซอฟต์แวร์ของตนอย่างอิสระเพื่อเพิ่มความสามารถรองรับ segwit ส่วนรูปแบบเดิมอย่าง P2PKH และ P2SH ก็ยังคงใช้งานได้ตามปกติสำหรับกระเป๋าเงินที่ยังไม่ได้อัปเกรด</p>
<p>สิ่งนี้ทำให้เกิดสองสถานการณ์สำคัญ ซึ่งจะถูกอธิบายในหัวข้อถัดไป:</p>
<ul>
<li>ความสามารถของกระเป๋าเงินฝั่งผู้จ่ายที่ไม่รองรับ segwit ในการชำระเงินให้กับกระเป๋าเงินฝั่งผู้รับที่สามารถประมวลผลธุรกรรมแบบ segwit ได้  </li>
<li>ความสามารถของกระเป๋าเงินฝั่งผู้จ่ายที่รองรับ segwit ในการรับรู้และแยกแยะได้ว่าผู้รับรายใดรองรับ segwit และรายใดไม่รองรับ โดยพิจารณาจากรูปแบบของ address</li>
</ul>
<h3>การฝัง segregated witness ไว้ภายใน P2SH</h3>
<p>สมมุติว่ากระเป๋าเงินของอลิซยังไม่ได้อัปเกรดเป็น segwit แต่กระเป๋าเงินของบ๊อบได้อัปเกรดแล้ว และสามารถจัดการธุรกรรมแบบ segwit ได้ ทั้งอลิซและบ๊อบยังสามารถใช้ output แบบดั้งเดิมที่ไม่ใช้ segwit ได้ตามปกติ อย่างไรก็ตามบ๊อบมักจะต้องการใช้ segwit เพื่อช่วยลดค่าธรรมเนียมธุรกรรมโดยอาศัยต้นทุนที่ต่ำกว่าสำหรับส่วน witness</p>
<p>ในกรณีนี้กระเป๋าเงินของบ๊อบสามารถสร้างที่อยู่แบบ P2SH ซึ่งภายในบรรจุสคริปต์ segwit อยู่ได้ กระเป๋าเงินของอลิซสามารถส่งเงินไปยัง address นั้นได้โดยไม่จำเป็นต้องรู้จักหรือเข้าใจ segwit แต่อย่างใด นอกจากนี้กระเป๋าเงินของบ๊อบก็สามารถใช้จ่าย output นั้นด้วยธุรกรรมแบบ segwit ได้ และทำให้ได้ประโยชน์จาก segwit และช่วยลดค่าธรรมเนียมธุรกรรมได้</p>
<p>สคริปต์ witness ทั้งสองรูปแบบ คือ P2WPKH และ P2WSH สามารถถูกฝังอยู่ภายใน address แบบ P2SH ได้ โดยจะเรียกว่า nested P2WPKH และ nested P2WSH ตามลำดับ</p>
<h3>Nested pay to witness public key hash</h3>
<p>รูปแบบแรกของ output script ที่เราจะพิจรณาคือ nested P2WPKH ซึ่งเป็น witness program แบบ pay to witness public key hash ที่ถูกฝังอยู่ภายในสคริปต์แบบ pay to script hash เพื่อให้กระเป๋าเงินที่ยังไม่รู้จัก segwit สามารถส่งเงินไปยัง output นี้ได้</p>
<p>กระเป๋าเงินของบ๊อบจะสร้าง P2WPKH witness program จาก public key ของ Bob จากนั้นนำ witness program นี้ไปแฮช และนำค่าแฮชที่ได้ไปเข้ารหัสเป็นสคริปต์แบบ P2SH โดยตัวสคริปต์นี้เองจะถูกแปลงเป็น address ของบิตคอยน์ ซึ่งจะเป็น address ที่ขึ้นต้นด้วย 3 เช่นเดียวกันกับ Pay to script hash</p>
<p>กระเป๋าเงินของบ๊อบเริ่มต้นด้วย witness version และ witness program แบบ P2WPKH ที่เราได้เห็นก่อนหน้านี้:</p>
<pre><code>0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7
</code></pre>
<p>ข้อมูลนี้จะประกอบด้วย witness version และค่าแฮชของ public key ของบ๊อบขนาด 20 ไบต์</p>
<p>จากนั้นกระเป๋าของเขาจะนำข้อมูลนี้ไปแฮช โดยเริ่มจาก SHA256 ก่อน แล้วตามด้วย RIPEMD 160 ทำให้ได้ค่าแฮชขนาด 20 ไบต์อีกค่าหนึ่ง ต่อมาแฮชของ redeem script นี้จะถูกแปลงเป็น address ของบิตคอยน์ในท้ายที่สุด จากนั้นกระเป๋าเงินของอลิซก็สามารถส่งเงินไปยังที่อยู่ 37Lx99uaGn5avKBxiW26HjedQE3LrDCZru ได้เช่นเดียวกันกับการส่งไปยัง address ทั่ว ๆ ไป</p>
<p>เพื่อจ่ายเงินให้บ๊อบ กระเป๋าเงินของอลิซจะล็อกเอาต์พุตด้วยสคริปต์แบบ P2SH ดังนี้:</p>
<pre><code>OP_HASH160 3e0547268b3b19288b3adef9719ec8659f4b2b0b OP_EQUAL
</code></pre>
<p>แม้ว่ากระเป๋าเงินของอลิซจะไม่รองรับ segwit เลยก็ตาม แต่การชำระเงินที่เธอสร้างขึ้นนี้บ๊อบก็ยังสามารถนำไปใช้จ่ายต่อได้ด้วยธุรกรรมแบบ segwit</p>
<h3>Nested pay to witness script hash</h3>
<p>ในทำนองเดียวกันกับ witness program แบบ P2WSH สำหรับสคริปต์ multisig หรือสคริปต์ที่ซับซ้อนอื่น ๆ สามารถถูกฝังอยู่ภายใน address แบบ P2SH ได้ ทำให้กระเป๋าเงินใด ๆ ก็ตามสามารถชำระเงินได้ในลักษณะที่เข้ากันได้กับ segwit</p>
<p>ดังที่เราเห็นในหัวข้อของ Pay to witness script hash (P2WSH) บริษัทของโมฮัมเหม็ดใช้การชำระเงินแบบ segregated witness ไปยังสคริปต์ multisignature เพื่อให้ลูกค้าทุกรายสามารถชำระเงินให้บริษัทของเขาได้ ไม่ว่ากระเป๋าเงินของลูกค้าจะอัปเกรดรองรับ segwit แล้วหรือไม่ กระเป๋าเงินของโมฮัมเหม็ดก็สามารถผัง P2WSH witness program ไว้ภายในสคริปต์แบบ P2SH ได้</p>
<p>ขั้นแรกกระเป๋าเงินของโมฮัมเหม็ดจะนำ witness script ไปแฮชด้วย SHA256(ครั้งเดียว) และได้ค่าแฮชดังนี้:</p>
<pre><code>9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73
</code></pre>
<p>ถัดมา witness script ที่ถูกแฮชแล้วจะถูกแปลงให้เป็น P2WSH witness program ที่มีการใส่เวอร์ชันนำหน้า:</p>
<pre><code>0 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73
</code></pre>
<p>จากนั้น witness program นี้จะถูกแฮชอีกครั้งด้วย SHA256 และ RIPEMD160 ตามลำดับ ทำให้ได้แฮชขนาด 20 ไบต์ใหม่ดังงนี้:</p>
<pre><code>86762607e8fe87c0c37740cddee880988b9455b2
</code></pre>
<p>ต่อมากระเป๋าเงินจะสร้าง address บิตคอยน์ แบบ P2SH จากแฮชนี้:</p>
<pre><code>3Dwz1MXhM6EfFoJChHCxh1jWHb8GQqRenG
</code></pre>
<p>โดยจากจุดนี้เอง ลูกค้าของโมฮัมเหม็ดก็สามารถชำระเงินมายัง address นี้ได้ แม้ว่ากระเป๋าเงินของพวกเขาจะไม่รับรอง segwit ก็ตามเพื่อส่งเงินให้โมฮัมเหม็ด กระเป๋าเงินของผู้ส่งจะล็อก output ด้วยสคริปต์ P2SH ดังต่อไปนี้:</p>
<pre><code>OP_HASH160 86762607e8fe87c0c37740cddee880988b9455b2 OP_EQUAL
</code></pre>
<p>จากนั้นบริษัทของโมฮัมเหม็ดก็สามารถสร้างธุรกรรมแบบ segwit เพื่อใช้จ่ายเงินเหล่านี้ได้ โดยได้รับประโยชน์จากคุณสมบัติของ segwit รวมถึงค่าธรรมเนียมธุรกรรมที่ต่ำลง</p>
<h2>Merklized Alternative Script Trees (MAST)</h2>
<p>การใช้ OP_IF นั้นสามารถกำหนดเงื่อนไขสำหรับการใช้จ่ายได้หลายรูปแบบ แต่แนวทางนี้เองก็มีข้อเสียที่ไม่พึงประสงค์อยู่หลายประการ อาทิเช่น</p>
<h5>น้ำหนัก(ต้นทุนในการทำธุรกรรม)</h5>
<p>ทุกเงื่อนไขที่เพิ่มเข้าไปในสคริปต์นั้นจะทำให้สคริปต์มีขนาดใหญ่ขึ้น ซึ่งจะส่งผลให้ต้นทุนในการทำธุรกรรมเพิ่มขึ้นเมื่อต้องการใช้จ่ายบิตคอยน์ที่ถูกป้องกันไว้ด้วยสคริปต์นี้</p>
<h5>ข้อจำกัดด้านขนาด</h5>
<p>แม้ว่าคุณจะยอมจ่ายค่าธรรมเนียมเพิ่มเพื่อให้ใส่เงื่อนไขได้มากขึ้นก็ตาม แต่ก็ยังมีข้อจำกัดในด้านของเงื่อนไขสูงสุดที่สามารถใส่ได้ในสคริปต์อยู่ดี อย่างเช่น legacy script ถูกจำกัดขนาดไว้ที่ 10,000 ไบต์ ซึ่งทำให้ในทางปฏิบัตินั้นสามารถใส่ได้เพียงไม่กี่ร้อยเงื่อนไขเท่านั้น และแม้ว่าจะสามารถสร้างสคริปต์ที่มีขนาดใหญ่เท่ากับบล๊อคทั้งบล๊อคได้ ก็ยังจะมีได้เพียงประมาณ 20,000 เงื่อนไขเท่านั้นที่ใช้งานได้จริง ซึ่งถือว่ามากแล้วสำหรับการชำระเงินแบบง่าย ๆ แต่ก็เล็กมากเมื่อเทียบกับการใช้งานบางอย่างที่จินตนาการไว้สำหรับบิตคอยน์</p>
<h5>การขาดแคลนความเป็นส่วนตัว</h5>
<p>ทุกเงื่อนไขที่มีการเพิ่มเข้าไปในสคริปต์นั้นจะกลายเป๋นข้อมูลสาธารณะเมื่อคุณใช้จ่ายบิตคอยน์ที่ถูกปกป้องไว้ด้วยสคริปต์นั้น ๆ ตัวอย่างเช่นทนายความและหุ้นส่วนทางธุรกิจของนายโมฮัมเหม็ดก็จะสามารถเห็นสคริปต์ทั้งหมดไม่ว่าจะในตัว multi-signature หรือ timelock ก็ตาม และทุกครั้งที่มีการใช้จ่ายกับบัญชีนี้ จะทำให้ทนายความของพวกเขาติดตามธุรกรรมทั้งหมดได้แม้ว่าจะไม่ได้ร่วมลงนามก็ตาม</p>
<p>แต่อย่างไรก็ตาม บิตคอยน์มีการใช้โครงสร้างข้อมูลที่เรียกว่า merkle tree อยู่แล้ว ซึ่งมันช่วยให้สามารถตรวจสอบได้ว่าองค์ประกอบใด ๆ เป็นสมาชิกของข้อมูลหรือไม่โดยไม่จำเป็นต้องเปิดเผยหรือระบุสมาชิกอื่น ๆ ทั้งหมดของชุดนั้น</p>
<p>ซึ่งเราจะได้เรียนรู้เกี่ยวกับ merkle tree มากขึ้นในหัวข้อของ merkle trees แต่สาระสำคัญคือ สมาชิกแต่ละตัวของชุดข้อมูลที่เราต้องการ (เช่น เงื่อนไขการอณุญาตที่มีความยาวเท่าใดก็ได้) สามารถนำไปผ่านฟังก์ชันแฮชแล้วนำแฮชมารวมกันอีกครั้งเพื่อสร้างค่าผูกมัดของกิ่งทั้งสองกิ่ง ซึ่งเรียกว่า branch commitment</p>
<p>โดยค่าผูกมัดของกิ่งทั้งสองเองก็สามารถสร้างได้ด้วยวิธีเดียวกัน ขั้นตอนนี้จะถูกทำซ้ำเรื่อย ๆ จนเหลือตัวเดียวและเราจะเรียกตัวนั้นว่า merkle root</p>
<p>จากตัวอย่างสคริปต์ในหัวข้อ Variable multi-signature with timelock เราสามารถสร้าง Merkle tree สำหรับเงื่อนไขการอณุญาตทั้งสามแบบใน A MAST ที่มีสามสคริปต์ย่อยได้<br> <img src="https://image.nostr.build/3d6230c0a0ee745196a957384b79606c729d3829efbaf1b4125e357bc6a68f24.jpg" alt="image"></p>
<p>ในขณะนี้เราสามารถสร้างหลักฐานการเป็นสมาชิกแบบกระทัดรัด (Compact membership proof) เพื่อพิสูจน์ได้ว่าเงื่อนไขการอนุญาตเงื่อนไขใดเงื่อนไขหนึ่งเป็นสมาชิกของ merkle tree โดยไม่ต้องเปิดเผยรายละเอียดใด ๆ ของสมาชิกอื่น ๆ ใน merkle tree สามาดูได้ในตัวอย่างของ A MAST membership proof for one of the subscripts และสังเกตว่าโหนดที่แรเงาไว้สามารถคำนวณได้จากข้อมูลอื่นที่ผู้ใช้ให้มา ดังนั้นจึงไม่จำเป็นต้องระบุโหนดเหล่านั้นในขณะทำการใช้จ่าย<br> <img src="https://image.nostr.build/3a5c3e43c266357582fb4bc17eedefaed398306d84750072af061d07cc99e513.jpg" alt="image"></p>
<p>ค่าแฮชที่ใช้ในการสร้างคอมมิจเมนต์แต่ละตัวมีขนาด 32 ไบต์ ดังนั้นการพิสูจน์ว่าการใช้จ่ายใน A MAST membership proof for one of the subscripts ได้รับอนุญาต (โดยใช้ merkle tree และเงื่อนไขที่เกี่ยวข้อง) และได้รับการพิสูจน์ตัวตน(ด้วยการใช้ลายเซ็น) จะใช้ข้อมูลทั้งหมด 383 ไบต์เมื่อเปรียบเทียบกันแล้ว การใช้จ่ายแบบเดียวกันโดยไม่ใช้ merkle tree (กล่าวคือ ต้องแสดงเงื่อนไขทั้งหมดที่เป็นไปได้) ที่จะใช้ข้อมูลถึง 412 ไบต์</p>
<p>การประหยัดได้ 29 ไบต์ (7%) ในตัวอย่างนี้ยังสะท้อนให้เห็นถึงศักยภาพในการประหยัดได้ไม่ครบถ้วน เนื่องจากลักษณะของ merkle tree ที่เป็นโครงสร้างแบบไบนารีทรีนั้น ทุกครั้งที่มีการเพิ่มจำนวนสมาชิกในเซตเพิ่มขึ้นเป็นสองเท่า เราจำเป็นต้องเพิ่มคอมมิตเมนต์ขนาด 32 ไบต์เพียงตัวเดียวเท่านั้น เช่นในกรณีที่มีทั้งหมด 3 เงื่อนไขเราจำเป็นต้องใช้คอมมิตเมนต์ 3 ตัว (ซึ่งหนึ่งในนั้นคือ merkle root ที่ต้องถูกรวมในการอณุญาตอยู่แล้ว) และเรายังสามารถมีคอมมิตเมนต์ได้ถึง 4 ตัวด้วยต้นทุนที่เท่าเดิม และหากมีคอมมิตเมนต์เพิ่มขึ้นอีกตัวหนึ่งจะทำให้เราสามารถรองรับเงื่อนไขได้สูงสุดถึงแปดเงื่อนไขด้วยกัน และในทำนองเดียวกันหากเรามี 16 คอมมิตเมนต์(ขนาดรวมเป็น 512 ไบต์) เราจะสามารถมีเงื่อนไขการอณุญาตได้มากกว่า 32,000 เงื่อนไข ซึ่งนั้นก็มากเกินกว่าที่จะนำไปใช้ได้จริงในบล็อก ๆ หนึ่งที่เต็มไปด้วยธุรกรรมที่มีคำสั่ง OP_IF ทั้งหมดเสียอีก และหากเราใช้ถึง 128 คอมมิตเมนต์ (4096 ไบต์) จำนวนเงื่อนไขที่เราจะสามารถสร้างได้ในเชิงทฤษฏีจะมีมากเกินกว่าเงื่อนไขทั้งหมดที่คอมพิวเตอร์ทุกเครื่องบนโลกจะสามารถสร้างขึ้นมาได้</p>
<p>โดยทั่วไปแล้ว เงื่อนไขการอณุญาตไม่ได้มีโอกาสถูกใช้งานเท่า ๆ กันทั้งหมด ในกรณีตัวอย่างของเรา เราคาดว่าโมฮัมเหม็ดและหุ้นส่วนของเขาจะใช้จ่ายเป็นประจำ ส่วนเงื่อนไขการหน่วงเวลามีไว้ใช้ในกรณีที่เกิดปัญหาเท่านั้น เราสามารถนำความรู้นี้มาใช้ในการปรับโครงสร้างของ tree ใหม่ทั้งหมดได้ ดังที่แสดงในรูปภาพข้างล่างนี้ โดยมีการจัดสคริปต์ที่คาดว่าจะมีการใช้งานมากที่สุดอยู่ในตำแหน่งที่เหมาะสมที่สุด</p>
<p> <img src="https://image.nostr.build/a60366f9810f9eb3e364bc0d83e4ef733991fdbd3ae1625c9acfe45256b08578.jpg" alt="image"></p>
<p>ในกรณีนี้ เราจำเป็นที่จะต้องให้คอมมิตเมนต์เพียงสองค่าเท่านั้นในกรณีที่ใช้จ่ายแบบที่คาดว่าจะบ่อยที่สุด (ประหยัดได้ 32 ไบต์) แม้ว่าการใช้จ่ายด้วยกรณีอื่น ๆ ยังคงจำเป็นต้องใช้ 3 คอมมิตเมนต์ก็ตาม ซึ่งแบบว่าหากคุณทราบหรือสามารถคาดเดาถึงความน่าจะเป็นของการใช้เงื่อนไขแต่ละแบบได้ คุณจะสามารถใช้อัลกอริทึมอย่าง Huffman เพื่อจัดวางเงื่อนไขเหล่านี้ลงไปในตำแหน่งที่ทำให้มีประสิทธิภาพสูงสุดได้ โดยสามารถดูรายละเอียดเพิ่มเติมได้ใน BIP341</p>
<p>นอกจากจะเพิ่มความซับซ้อนให้กับบิตคอยน์มาอีกเล็กน้อยแล้ว MAST ก็ยังแทบไม่มีข้อเสียที่สำคัญอะไรต่อบิตคอยน์เลย และก่อนที่จะพบแนวทางที่ดีกว่าในภายหลัง (เดี๋ยวเราจะได้เห็นในหัวข้อ taproot) ก็มีข้อเสนอที่แข็งแรงอยู่แล้วถึงสองอันสำหรับ MAST นั้นคือ BIP114 และ BIP116</p>
<h3>MAST เทียบกับ MAST</h3>
<p>แนวคิดแรกเริ่มของสิ่งที่ปัจจุบันเราเรียกว่า MAST ในบิตคอยน์ คือแนวคิดของ merklized abstract syntax trees (ต้นไม้โครงสร้างไวยากรณ์เชิงนามธรรมที่ถูกเมอร์เคิลไลซ์) ในต้นไม้โครงสร้างไวยากรณ์เชิงนามธรรม (AST) แต่ละเงื่อนไขในสคริปต์จะสร้างสาขาใหม่ ดังที่แสดงไว้ในรูปภาพข้างล่าง<br> <img src="https://image.nostr.build/2278f96ec80d7fe7e93c87a73ef70b42934a05bc110abcf9c2d1aa1c41433d91.jpg" alt="image"></p>
<p>AST นั้นได้ถูกใช้อย่างแพร่หลายในโปรแกรมที่ทำหน้าที่แยกวิเคราห์และปรับแต่งโค้ดของโปรแกรมอื่น ๆ เช่น คอมไพเลอร์ หากนำ AST มาใช้ในรูปแบบที่ถูกเมอร์เคิลไลซ์ (merklized AST) ก็จะสามารถผูกมัดได้กับทุกส่วนของโปรแกรม และเปิดให้ใช้คุณสมบัติต่าง ๆ ที่อธิบายไว้ใน merklized Alternative Script Trees (MAST) แต่อย่างไรก็ตามวิธีนี้จะต้องเปิดเผยค่าแฮชขนาด 32 ไบต์อย่างน้อยหนึ่งค่า สำหรับทุกส่วนย่อยของโปรแกรม ซึ่งทำให้ไม่ค่อยมีประสิธิภาพในด้านพื้นที่ของบล็อกเชนสำหรับโปรแกรมส่วนใหญ่</p>
<p>สิ่งที่ผู้คนส่วนใหญ่มักเรียกว่า MAST ในบริบทของบิตคอยน์ในปัจจุบันคือ merklized alternative script trees ซึ่งเป็น backronym ที่ถูกตั้งขึ้นโดยนักพัฒนา Anthony Towns ซึ่ง alternative script tree คือชุดของสคริปต์หลายสคริปต์ ซึ่งแต่ละสคริปต์นั้นมีความสมบูรณืในตัวเองและสามารถเลือกใช้งานได้เพียงหนึ่งสคริปต์เท่านั้น ทำให้สครืปต์เหบ่านี้เป็นทางเลือกแทนกัน ซึ่งแสดงไว้ในภาพด้านล่าง</p>
<p> <img src="https://image.nostr.build/d87c36148baa4429f15264771a0341e787e343df3de17136c16db0d3d61397d6.jpg" alt="image"></p>
<p>ต้นไม้สคริปต์แบบทางเลือก (alternative script trees) ต้องการเพียงเปิดเผยไดเจสเพียง 32 ไบต์เท่านั้นสำหรับความลึกในแต่ละลำดับที่ผู้ใช้เลือกใช้และ merkle root สำหรับสคริปต์ส่วนใหญ่ นี่ถือเป็นการใช้พื้นที่ในบล๊อกเชนที่มีประสิธิภาพมากกว่าเป็นอย่างมาก</p>
<h2>Pay to Contract (P2C)</h2>
<p>ดังที่เราได้เห็นในบทก่อนหน้าในหัวข้อ public child key derivation คณิตศาสตร์ของการเข้ารหัสด้วยเส้นโค้งวงรี (Eliptic Curve Cryptography:ECC) อณุญาตให้อลิซใช้ private key เพื่อสร้าง public key ที่เธอมอบให้บ๊อบ สามารถเพิ่มค่าที่เป็นไปได้ค่าใดก็ได้เข้ามาใน public key นั้น  เพื่อสร้าง public key ใหม่ขึ้นมา หากบ๊อบส่งค่าที่เขาเพิ่มนั้นกลับไปให้อลิซ เธอก็สามารถนำค่าเดียวกันไปบวกกับ private key ของเธอ เพื่อสร้าง private key ที่สอดคล้องกับ public key ที่ถูกอนุพันธ์ขึ้นมาได้</p>
<p>สรุปสั้น ๆ ก็คือบ๊อบสามารถสร้าง child public key ซึ่งมีเพียงอลิซเท่านั้นที่สามารถสร้าง private key ที่สอดคล้องกันขึ้นมาได้ สิ่งนี้เป็นประโยชน์สำหรับการกู้คืนกระเป๋าเงินแบบ Hierarchical Determinstic (HD) ตามมาตรฐานของ BIP32 แต่ก็ยังสามารถนำไปใช้ในวัตถุประสงค์อื่นได้อีกด้วย</p>
<p>ลองจินตนาการดูสิว่าบ๊อบต้องการซื้อของบางอย่างจากอลิซ แต่เขาก็ต้องการความสามารถในการพิสูจน์ภายหลังได้ว่าเขาจ่ายเงินไปเพื่ออะไร ในกรณีที่เกิดข้อพิพาทขึ้นอลิซและบ๊อบจึงตกลงกันเกี่ยวกับชื่อของสินค้าหรือบริการที่ขาย (เช่น “พอดแคสต์ของอลิซตอนที่#123”) แล้วแปลงคำอธิบายนั้นให้เป็นตัวเลข โดยการนำไปแฮชและตีความค่าแฮชที่ได้เป็นตัวเลข จากนั้นบ๊อบนำตัวเลขนั้นไปบวกกับ public key ของอลิซและจ่ายเงินให้ไป กระบวนการนี้เรียกว่า key tweaking และตัวเลขนั้นเรียกว่า tweak</p>
<p>อลิซสามารถใช้จ่ายเงินดังกล่าวได้โดยการปรับแต่ง(tweak) private key ของตัวเธอเองด้วยเลขเดียวกัน</p>
<p>และในภายหลังบ๊อบเองก็สามารถพิสูจน์กับใครก็ตามได้ว่าเขาได้จ่ายเงินให้อลิซเพื่ออะไร โดยการเปิดเผยถึงกุญแจตั้งต้น (underlying key) ของอลิซและคำอธิบายของสินค้าหรือบริการที่ทั้งสองใช้ร่วมกัน ทุกคนสามารถตรวจสอบได้ว่า public key ที่ถูกนำไปใช้ชำระเงินนั้นเท่ากับกุญแจตั้งต้นบวกกับค่าแฮช ของคำอธิบายนั้น และหากอลิซยอมรับว่ากุญแจดังกล่าวเป็นของเธอ ก็ย่อมแสดงว่าเธอยอมรับว่าเธอเป็นผู้รับเงินก้อนนั้น และหากอลิซเป็นผู้ใช้จ่ายเงินออกไป ก็ยิ่งเป็นหลักฐานยืนยันเพิ่มเติมว่าเธอรู้คำอธิบายนั้นตั้งแต่ตอนที่เธอลงนามในธุรกรรมการจ่ายเงิน เพราะเธอสามารถสร้างลายเซ็นที่ถูกต้องได้สำหรับ public key ที่ถูกปรับแต่ง ได้ก็ต่อเมื่อเธอรู้ว่าค่า tweak คืออะไรเท่านั้น</p>
<p>หากอลิซและบ๊อบไม่ได้ตัดสินใจเปิดเผยคำอธิบายที่พวกเขาใช้ต่อสาธารณะการชำระเงินระหว่างทั้งสองก็จะดูไม่ต่างจากการชำระเงินทั่วไปอื่น ๆ และจะไม่ก่อให้เกิดการสูญเสียความเป็นส่วนตัวแต่อย่างใด</p>
<p>เนื่องจาก P2C มีความเป็นส่วนตัวโดยปริยาย เราจึงไม่สามารถทราบได้ว่ามันถูกใช้งานตามวัตถุประสงค์ดั้งเดิมบ่อยเพียงใด ในทางทฤษฏีแล้ว การชำระเงินทุกครั้งอาจใช้วิธีนี้ก็ได้ แม้ว่าเราจะมองว่าสถานการณ์เช่นนั้นไม่น่าเกิดขึ้นก็ตาม แต่ในปัจจุบันนั้น P2C ถูกใช้อย่างแพร่หลายในรูปแบบที่แตกต่างกันออกไปเล็กน้อย ซึ่งเราจะได้เห็นกันใน taproot ที่จะอธิบายต่อภายหลัง</p>
<h2>Scriptless Multisignatures and Threshold Signatures</h2>
<p>ในการทำ Scripted Multisignatures คือการพิจรณาสคริปต์ที่กำหนดให้ต้องมีลายเซ็นจากหลายฝ่าย อย่างไรก็ตามยังมีอีกหนึ่งวิธีในการบังคับให้ต้องใช้กุญแจหลายดอกร่วมกัน ซึ่งก็น่าจะทำให้สับสนได้ไม่น้อย เพราะมันก็ถูกเรียกว่า multisignature เช่นกัน แต่เพื่อแยกความแตกต่างระหว่างสองแนวทางนี้ ในส่วนนี้เราจะเรียกเวอร์ชันที่ใช้ opcode แบบ OP_CHECKSIG ว่า script multisignatures และอีกเวอร์ชันที่จะพูดถึงต่อไปว่า scriptless multisignatures</p>
<p>Scriptless multisignature ทำงานโดยให้ผู้เข้าร่วมแต่ละคนสร้างความลับของตนเองขึ้นมาคล้ายกับวิธีสร้าง private key โดยเราจะเรียกความลับนี้ว่า partial private key โดยแม้ว่ามันจะมีความยาวเท่ากับ private key แบบปกติทุกประการจาก partial private key อันนี้ ผู้เข้าร่วมแต่ละคนจะอนุมาน partial public key โดยใช้อัลกอริทึมเดียวกันกับที่ใช้สร้าง public key ทั่วไป ตามที่อธิบายไว้ในหัวข้อ public key derivation จากนั้นผู้เข้าร่วมทุกคนจะแชร์ partial public key ของตนให้กับผู้เข้าร่วมอื่น ๆ และนำกุญแจทั้งหมดมารวมกันเพื่อสร้าง scriptless multisignature public key เพียงดอกเดียว</p>
<p>กุญแจสาธรณะที่ถูกรวมกันนี้จะมีลักษณะไม่ต่างจาก public key ทั่วไป บุคคลที่สามไม่สามารถแยกแยะได้ว่า public key นี้เกิดจากผู้ใช้หลายคนร่วมกัน หรือเป็น public key ธรรมดาที่สร้างขึ้นโดยผู้ใช้เพียงคนเดียว</p>
<p>สำหรับการใช้จ่ายบิตคอยน์ที่ถูกป้องกันด้วย scriptless multisignature public key ผู้เข้าร่วมแต่ละคนจะสร้างลายเซ็นย่อย (partial signature) ของตนเอง จากนั้นลายเซ็นย่อยเหล่านี้จะถูกรวมเข้าด้วยกันเพื่อสร้างเป็นลายเซ็นที่สมบูรณ์ (บางที่เรียกว่า Full signature) ซึ่งก็มีวิธีที่เป็นที่รู้จักกันหลายรูปแบบสำหรับการสร้างและรวมลายเซ็น ซึ่งเราจะกลับมาลงลึกในประเด็นนี้กันอีกครั้งในหัวข้อ Signature (บทที่ 8) และเช่นเดียวกันกับกรณีของ public key สำหรับ scriptless multisignatures ลายเซ็นที่ได้จากกระบวนการนี้จะมีหน้าตาเหมือนกับลายเซ็นทั่วไปทุกประการ บุคคลที่สามไม่สามารถตรวจสอบได้ว่าลายเซ็นนั้นถูกสร้างขึ้นโดยคนเพียงคนเดียวหรือเกิดจากความร่วมมือของผู้คนจำนวนมาก แม้ว่าจะเป็นการร่วมมือกันระดับล้านคนก็มองไม่ออก</p>
<p>Scriptless multisignatures มีทั้งขนาดเล็กกว่าและมีความเป็นส่วนตัวสูงกว่า scripted multisignatures อย่างชัดเจน สำหรับ scripted multisignatures จำนวนไบต์ที่ต้องบันทึกลงในธุรกรรมจะเพิ่มขึ้นตามจำนวนกุญแจและลายเซ็นที่เกี่ยวข้อง ยิ่งมีผู้ลงนามมาก ข้อมูลในธุรกรรมก็ยิ่งใหญ่ตามไปด้วย แต่ในทางตรงกันข้ามกัน สำหรับ scriptless signatures ขนาดของข้อมูลจะคงที่ ไม่ว่าจะมีผู้เข้าร่วมกี่คนก็ตาม แม้จะมีส่วนร่วมโดยคนเป็นล้านคนก็ตาม แต่ละคนสร้าง partial key และ partial signature ของตนเอง ธุรกรรมที่ได้จะมีขนาดข้อมูลเท่ากันทุกประการกับกรณีที่มีผู้ใช้เพียงคนเดียวใช้กุญแจดอกเดียวและลายเซ็นเพียงอันเดียว</p>
<p>ในแง่ของความเป็นส่วนตัวก็เป็นไปในทิศทางเดียวกัน เนื่องจาก scripted multisignatures ต้องเพิ่มข้อมูลทุกครั้งที่มีการเพิ่มกุญแจหรือลายเซ็น ธุรกรรมจึงเปิดเผยโดยนัยว่ามีการใช้กุญแจและลายเซ็นจำนวนเท่าใด ซึ่งอาจทำให้สามารถคาดเดาได้ว่าธุรกรรมนั้นจะสร้างขึ้นโดยกลุ่มคนใด แต่สำหรับ scriptless multisignatures ธุรกรรมทุกอันจะมีลักษณะเหมือนกันหมด และยังเหมือนกับธุรกรรมแบบลายเซ็นเดียวด้วย ทำให้ไม่มีข้อมูลรั่วไหลลออกมาทำให้ลดทอนความเป็นส่วนตัวของผู้ใช้งาน</p>
<p>ส่วนสำหรับข้อเสียของ scriptless multisignatures มีอยู่สองประการ อย่างแรกคืออัลกอริทึมที่ปลอดภัยทั้งหมดซึ่งเป็นที่รู้จักกันในปัจจุบันสำหรับการสร้าง scriptless multisignatures บนบิตคอยน์ จำเป็นต้องมีรอบของการโต้ตอบกันหรือไม่ก็ต้องมีการจัดการสถานะที่รอบคอบกว่าเมื่อเทียบกับ scripted signatures สิ่งนี้กลายเป็นความท้าทายในกรณีที่ลายเซ็นนั้นถูกสร้างโดยอุปกรณ์ลงนามแบบฮาร์ดแวร์ซึ่งแทบไม่มีการเช็คสถานะ และกุญแจกระจายอยู่ในสถานที่ทางกายภาพที่แตกต่างกัน ตัวอย่างเช่น หากคุณเก็บอุปกรณ์ลงนามแบบฮาร์ดแวร์ไว้ในตู้เซฟของธนาคาร สำหรับ scripted multisignature คุณอาจจะต้องไปที่ตู้เซฟนั้นเพียงครั้งเดียว แต่สำหรับ scriptless แล้วคุณอาจต้องไปสองถึงสามครั้งเพื่อทำให้กระบวนการลงนามเสร็จสมบูรณ์</p>
<p>ส่วนข้อเสียอีกอย่างหนึ่งคือการลงนามแบบ threshold signing ไม่เปิดเผยว่าใครเป็นผู้ลงนามจริงใน scripted threshold signing ตัวอย่างเช่น อลิซ บ๊อบ และคอลไลร์ ตกลงกันว่าเพียงมีลายเซ็นจากใครก็ได้สองในสามคน ก็เพียงพอที่จะใช้จ่ายเงินก้อนนี้ได้ หากอลิซและบ๊อบเป็นผู้ลงนามจะต้องใส่ลายเซ็นของทั้งสองคนลงในบล๊อกเชนซึ่งทำให้ใครก็ตามที่รู้จักกุญแจของพวกเขาก็สามารถพิสูจน์ได้ว่าอลิซและบ๊อบเป็นผู้ลงนามและคอไลร์ไม่ใช่ผู้ลงนาม แต่ใน scriptless threshold signing ลายเซ็นที่เกิดจากอลิซและบ๊อบจะแยกไม่ออกจากลายเซ็นที่เกิดขึ้นโดยอลิซกับคอไลร์หรือจากบ๊อบกับคอไลร์ ซึ่งมันเป็นข้อดีด้านความเป็นส่วนตัว เนื่องจากมันไม่เปิดเผยว่าใครร่วมลงนามบ้าง แต่อย่างไรก็ตามมันก็มีข้อเสียตามมา นั้นก็คือแม้ว่าคอไลร์จะอ้างว่าเธอไม่ได้ลงนาม แต่ก็ไม่มีอะไรเลยที่สามารถพิสูจน์ได้ว่าเธอไม่ได้ลงนามจริง ๆ ซึ่งอาจเป็นปัญหาในแง่ความรับผิดชอบและการตรวจสอบย้อนหลัง</p>
<p>สำหรับผู้ใช้ที่ต้องมีการย้ายเงินเข้าออกบ่อย ๆ ข้อดีด้านขนาดธุรกรรมที่เล็กลง และความเป็นส่วนตัวที่เพิ่มขึ้นของ multisignatures มีประโยชน์มากกว่าความยุ่งยากที่อาจเกิดขึ้นเป็นครั้งคราวในการสร้างลายเซ็นและการตรวจสอบย้อนหลังของลายเซ็นเหล่านั้น</p>
<h2>Taproot</h2>
<p>เหตุผลอย่างหนึ่งที่ผู้คนเลือกใช้บิตคอยน์คือสามารถสร้างสัญญาที่ให้ผลลัพธ์ได้อย่างคาดการณ์ได้สูงมาก สัญญาทางกฎหมายที่บังคับใช้โดยศาลขึ้นอยู่กับการตัดสินใจของผู้พิพากษาและคณะลูกขุน ในทางกลับกัน สัญญาของบิตคอยน์มักจะต้องการการกระทำจากผู้เข้าร่วมแต่จะถูกบังคับใช้โดยโหนดแบบเต็มนับพันที่รันโค้ดเหมือนกัน เมื่อได้รับสัญญาเดียวกันและข้อมูลชุดเดียวกัน โหนดแบบเต็มทุกตัวจะให้ผลลัพธ์เหมือนกันเสมอ การเบี่ยงเบนใด ๆ หมายความว่าบิตคอยน์ถูกทำลายไปแล้ว ผู้พิพากษาและคณะลูกขุนสามารถยืดหยุ่นได้มากกว่าโปรแกรม แต่เมื่อไม่ต้องการหรือไม่จำเป็นต้องมีความยืดหยุ่นนั้น ความสามารถในการคาดการณ์ของสัญญาบิตคอยน์ถือเป็นคุณสมบัติสำคัญ</p>
<p>หากผู้เข้าร่วมทั้งหมดเห็นว่าผลลัพธ์ของสัญญากลายเป็นเรื่องที่คาดการณ์ได้ทั้งหมด จริง ๆ แล้วก็ไม่มีความจำเป็นที่พวกเขาจะต้องใช้สัญญาต่อไป พวกเขาอาจทำในสิ่งที่สัญญาบังคับให้ทำแล้วยุติสัญญา ในสังคม นี่คือวิธีที่สัญญาส่วนใหญ่ยุติ: หากผู้ที่เกี่ยวข้องพอใจ พวกเขาจะไม่ยื่นสัญญาต่อศาล ในบิตคอยน์ หมายความว่าสัญญาที่ย่อมต้องใช้พื้นที่บล็อกจำนวนมากในการยุติ ควรมีข้อคลอสหนึ่งที่อนุญาตให้ยุติโดยความพึงพอใจร่วมกันแทน</p>
<p>ใน MAST และ scriptless multisignatures การออกแบบสัญญาแบบยินยอมร่วมกันเป็นเรื่องง่าย เพราะเราเพียงทำให้หนึ่งในใบไม้ชั้นบนสุดของต้นสคริปต์เป็น scriptless multisignature ระหว่างผู้เข้าร่วมทั้งหมด ซึ่งเราจะแสดงให้เห็นในรูปภาพที่ได้เห็นข้างบนเราสามารถทำให้มันมีประสิทธิภาพยิ่งขึ้นโดยเปลี่ยนจาก scripted multisignature เป็น scriptless multisignature</p>
<p>แนวทางนี้เองถือว่ามีประสิทธิภาพและความเป็นส่วนตัวพอสมควร หากมีการใช้สัญญาการยินยอมร่วมกัน เราจำเป็นต้องเปิดเผยเพียง merkle branch เดียว และสิ่งที่ถูกเปิดเผยก็มีแค่ว่าลายเซ็นใดที่ถูกใช้เท่านั้น ซึ่งอาจเป็นลายเซ็นจากคนเพียงคนเดียว หรือจากผู้เข้าร่วมหลายพันคนก็ไม่ได้มีใครแยกแยะได้</p>
<p>อย่างไรก็ตามในปี 2018 นักพัฒนาได้ตระหนักได้ว่าเราสามารถทำให้ดีกว่านี้ได้อีก หากนำแนวคิด pay to contract มาใช้ร่วมด้วย</p>
<p>ในคำอธิบายก่อนหน้านี้ของ Pay to contract ในหัวข้อ Pay to Contract (P2C) เราได้ tweak public key เพื่อผูกมัดกับข้อความของข้อตกลงระหว่างอลิซและบ๊อบ แทนที่จะเป็นเช่นนั้น เราสามารถผูกมัดกับโค้ดโปรแกรมของสัญญาได้โดยการผูกมัดกับ root ของ MAST tweak public key นั้นเป็น public key ปกติ ซึ่งหมายความว่ามันอาจต้องการลายเซ็นจากบุคคลเดียว หรืออาจเป็นจากหลายคน (หรืออาจถูกสร้างในลักษณะพิเศษให้เป็นไปไม่ได้ที่จะสร้างลายเซ็นสำหรับมัน) นั่นหมายความว่าเราสามารถทำให้สัญญาสำเร็จได้ด้วยลายเซ็นเดียวจากทุกฝ่ายที่เกี่ยวข้อง หรือโดยการเปิด branch ของ MAST ที่เราต้องการใช้ commitment tree ที่ประกอบด้วย public key และ MAST ดังกล่าวดังที่แสดงไว้ในภาพข้างล่าง<br> <img src="https://image.nostr.build/6155acd9c2852a12cea4667b89c2adc912d68aeab458213a729b13bbdb3d811b.jpg" alt="image"></p>
<p>สิ่งนี้เองที่ทำให้สัญญาแบบยินยอมร่วมกันโดยใช้ multisignature มีประสิธิภาพสูงและมีความเป็นส่วนตัวมากยิ่งขึ้น เพราะธุรกรรมที่สร้างโดยใช้โดยคนเพียงคนเดียวซึ่งต้องการให้ยุติโดยลายเซ็นเดียว (หรือ multisignature ที่สร้างโดยหลายกระเป๋าที่เขาควบคุม) จะดูเหมือนกันกับการใช้จ่ายของสัญญาแบบยินยอมร่วมกัน ไม่มีความแตกต่างกันบนบล๊อกเชน ในกรณีนี้ระหว่างการใช้จ่ายโดยกลุ่มผู้ใช้เป็นล้านคนในสัญญาที่ซับซ้อน หรือจ่ายโดยคนเพียงคนเดียว</p>
<p>เมื่อการใช้จ่ายสามารถทำได้โดยใช้เพียงกุญแจ เช่นกรณีลายเซ็นเดี่ยวหรือ scriptless multisig จะเรียกการใช้จ่ายแบบนี้ว่า keypath spending เมื่อใช้ต้นไม้ของสคริปต์ จะเรียกการใช้จ่ายแบบนั้นว่า scriptpath spending สำหรับการใช้จ่ายแบบ keypath ข้อมูลที่จะถูกใส่บนบล๊อกเชนคือ public key (ที่อยู่ใน witness program) และลายเซ็น (ที่อยู่บน witness stack)</p>
<p>สำหรับการใช้จ่ายแบบ scriptpath spending ข้อมูลบนบล๊อกเชนจะรวมถึง public key ด้วยโดย public key นี้จะถูกใส่ไว้ใน witness program และในบริบทนี้เรียกว่า taproot output key โครงสร้างของ witness จะประกอบไปด้วยข้อมูลต่อไปนี้:</p>
<ul>
<li>หมายเลขเวอร์ชัน (Version number)  </li>
<li>กุญแจพื้นฐาน (Underlying key) คือกุญแจที่มีอยู่ก่อนที่จะถูกปรับแต่ง (tweak) ด้วย merkle root เพื่อสร้าง taproot output key กุญแจพื้นฐานนี้เรียกว่า taproot internal key  </li>
<li>สคริปต์ที่ถูกนำมาประมวลผลนี้เรียกว่า leaf script  </li>
<li>ค่าแฮชขนาด 32 ไบต์จำนวนหนึ่งค่าต่อหนึ่งจุดที่เชื่อมใน merkle tree ตามเส้นทางที่ leaf เชื่อมเข้ากับ Merkle root  </li>
<li>ข้อมูลใด ๆ ที่จำเป็นต่อการทำให้สคริปต์เป็นจริง (เช่น ลายเซ็น หรือ hash preimage)</li>
</ul>
<p>ข้อเสียสำคัญเพียงอย่างเดียวของ taproot ที่มีการอธิบายไว้คือ กรณีของสัญญาที่ผู้เข้าร่วมต้องการใช้ MAST แต่ไม่ต้องการมีเงื่อนไขการยุติความพึงพอใจร่วมกัน สัญญาเหล่านั้นจำเป็นต้องบันทึก taproot internal key ได้บนบล๊อกเชน ซึ่งเพิ่ม overhead ประมาณ 33 ไบต์ แต่อย่างไรก็ตาม เนื่องจากคาดว่าสัญญาส่วนใหญ่จะได้รับประโยชน์จากเงื่อนไขหารยุติโดยใช้ความพึงพอใจร่วมกัน หรืออย่างน้อยก็มีเงื่อนไข multisignature อื่นที่ใช้ public key ที่อยู่ในระดับบนสุด และผู้ใช้ทุกคนยังได้รับประโยชน์จากการเพิ่ม anonymity set ที่ทำให้ output มีลักษณะคล้ายกันมากขึ้น overhead ที่เกิดขึ้นในกรณีที่พบไม่บ่อยนี้จึงไม่ถูกมองว่าสำคัญโดยผู้ใช้ส่วนใหญ่ที่มีส่วนร่วมในการเปิดใช้งาน taproot</p>
<p>การรองรับ taproot ถูกเพิ่มเข้ามาในบิตคอยน์ผ่านการทำ soft fork ซึ่งถูกเปิดใช้งานในเดือนพฤศจิกายนปี 2021</p>
<h3>Tapscript</h3>
<p>Taproot ทำให้สามารถใช้ MAST ได้ แต่ในภาษาสคริปต์ของบิตคอยน์ในเวอร์ชันที่แตกต่างจากเดิมเล็กน้อย โดยเวอร์ชันใหม่นี้เรียกว่า Tapscript โดยความแตกต่างสำคัญประกอบด้วย:</p>
<h5>การเปลี่ยนแปลงของ scripted multisignature:</h5>
<p>opcode เดิมอย่าง OP_CHECKMULTISIG และ OP_CHECKMULTISIGVERIFY ถูกนำออกไป เนื่องจาก opcode เหล่านี้ไม่สามารถทำงานร่วมกันได้ดีนักกับการเปลี่ยนแปลงอีกอย่างใน soft fork ของ taproot นั่นคือความสามารถในการใช้ลายเซ็นแบบ schnorr ร่วมกับการตรวจสอบแบบ batch แทนที่ด้วย opcode ใหม่คือ OP_CHECKSIGADD</p>
<p>เมื่อ OP_CHECKSIGADD ตรวจสอบลายเซ็นสำเร็จแล้ว จะเพิ่มค่าตัวนับขึ้นหนึ่ง ทำให้สามารถนับจำนวนลายเซ็นที่ผ่านการตรวจสอบได้อย่างสะดวก และนำไปเปรียบเทียบกับจำนวนลายเซ็นที่ต้องการ เพื่อสร้างพฤติกรรมเดียวกันกับที่ OP_CHECKMULTISIG เคยทำได้</p>
<h5>การเปลี่ยนแปลงของลายเซ็นทั้งหมด:</h5>
<p>การดำเนินการที่เกี่ยวข้องกับลายเซ็นทั้งหมดใน tapscript ใช้อัลกอริทึมลายเซ็นแบบ Schorr จามที่นิยามไว้ใน BIP340 เราจะอธิบายรายละเอียดของลายเซ็นแบบ schorr เพิ่มเติมในบทถัดไป นอกจากนี้ การดำเนินการตรวจสอบใด ๆ ที่คาดว่าจะไม่ผ่านการตรวจสอบ จะต้องได้รับค่า OP_FALSE (หรือที่เรียกว่า OP_0) แทนการส่งลายเซ็นจริงเข้าไป หากส่งค่าอื่นใดไปยังการตรวจสอบลายเซ็นที่ล้มเหลว สคริปต์ทั้งหมดจะล้มเหลวทันที กลไกนี้ยังช่วยสนับสนุนการตรวจสอบลายเซ็น Schnorr แบบ batch validation ได้อีกด้วย</p>
<h5>opcode กลุ่ม OP_SUCCESSx:</h5>
<p>opcode ที่ไม่สามารถใช้งานได้ใน script เวอร์ชันก่อนหน้าได้ถูกนิยามใหม่ให้เมื่อถูกเรียกใช้แล้วทำให้สคริปต์ทั้งหมดสำเร็จทันที (script succeeds) โดยกลไกนี้เปิดทางให้ soft fork ในอนาคตสามารถนิยามเงื่อนไขเพิ่มเติมได้ว่า opcode เหล่านี้จะไม่ทำให้สคริปต์สำเร็จในบางกรณี ซึ่งถือเป็นการ “จำกัดความสามารถลง” และจึงสามารถทำได้ผ่าน soft fork ในทางกลับกัน การเปลี่ยน opcode ที่เดิม ไม่ทำให้สคริปต์สำเร็จ ให้กลายเป็น opcode ที่ทำให้สคริปต์สำเร็จนั้น จะทำได้ผ่าน hard fork เท่านั้น ซึ่งเป็นแนวทางการอัปเกรดที่ยากและมีความเสี่ยงมากกว่า</p>
<p>แม้ว่าในบทนี้เราจะได้พิจรณาเรื่องการให้สิทธิ์และการยืนยันตัวตนอย่างละเอียดแล้ว แต่เรายังข้ามที่ส่วนสำคัญมากส่วนหนึ่งของวิธีที่บิตคอยน์ใช้ยืนยันผู้ใช้จ่ายไป นั่นคือลายเซ็น ซึ่งเราจะไปศึกษาในลำดับถัดไปในบทที่ 8 <img src="https://image.nostr.build/2eb9a47ba4fe6ecbc3d12dae8434d45ad15840655e6ee833420bb2ee307cf614.jpg" alt="image"></p>
]]></content:encoded>
      <itunes:author><![CDATA[Learning_BTC&NOSTR]]></itunes:author>
      <itunes:summary><![CDATA[<h1><strong>บทที่ 7: Authorization and Authentication</strong></h1>
<hr>
<p>เมื่อคุณได้รับบิตคอยน์ คุณต้องตัดสินใจว่าใครจะได้รับสิทธิ์ในการใช้จ่ายเหรียญนั้น ซึ่งเรียกว่า authorization (การอนุญาต) นอกจากนี้ คุณต้องตัดสินใจด้วยว่า full node ควรแยกแยะผู้ที่ได้รับอนุญาตให้ใช้จ่ายออกจากคนอื่น ๆ อย่างไร ซึ่งเรียกว่า authentication (การยืนยันตัวตน) คำสั่งเกี่ยวกับการอนุญาตของคุณ และหลักฐานการยืนยันตัวตนจากผู้ใช้จ่าย จะถูกตรวจสอบโดย full node นับพัน ซึ่งต้องได้ข้อสรุปตรงกันว่าการใช้จ่ายนั้นได้รับอนุญาตและได้รับการยืนยันตัวตนอย่างถูกต้อง เพื่อให้ธุรกรรมดังกล่าวเป็นธุรกรรมที่ถูกต้อง</p>
<p>คำอธิบายดั้งเดิมของบิตคอยน์ใช้ public key ในการทำ authorization โดย Alice ส่งเหรียญให้ Bob ด้วยการใส่ public key ของเขาลงในเอาต์พุตของธุรกรรม ส่วนการทำ authentication มาจากการลงนาม (signature) โดย Bob ซึ่งผูกพันกับธุรกรรมการใช้จ่าย เช่น การที่ Bob ส่งต่อให้ Carol</p>
<p>ในเวอร์ชันแรกของบิตคอยน์ที่ถูกปล่อยออกมา ได้จัดเตรียมกลไกที่ยืดหยุ่นกว่าสำหรับทั้ง authorization และ authentication และการพัฒนาต่อมาทำให้กลไกเหล่านี้ยืดหยุ่นยิ่งขึ้นกว่าเดิม ในบทนี้ เราจะสำรวจฟีเจอร์เหล่านั้นและดูว่ามันถูกใช้งานอย่างแพร่หลายได้อย่างไร</p>
<h2>Transaction Scripts and Script Language</h2>
<p>เวอร์ชันแรก ๆ ของบิตคอยน์ได้แนะนำภาษาการเขียนโปรแกรมใหม่ชื่อว่า Script ซึ่งเป็นภาษาลักษณะคล้าย Forth ที่ทำงานบนสแตก ทั้งสคริปต์ที่ถูกวางไว้ในเอาต์พุต และสคริปต์อินพุตแบบ legacy ที่ใช้ในธุรกรรมการใช้จ่าย ล้วนเขียนด้วยภาษาสคริปต์นี้ทั้งสิ้น</p>
<p>Script เป็นภาษาที่เรียบง่ายมาก ต้องการการประมวลผลเพียงเล็กน้อย และไม่สามารถทำสิ่งซับซ้อนแบบที่ภาษาสมัยใหม่สามารถทำได้</p>
<p>เมื่อธุรกรรมแบบ legacy ยังเป็นรูปแบบที่ใช้กันมากที่สุด ธุรกรรมส่วนใหญ่ที่ประมวลผลบนเครือข่ายบิตคอยน์อยู่ในรูปแบบเดียวกันกับที่ alice จ่ายให้ bob ในบทก่อนหน้าและใช้สคริปต์ที่เรียกว่า pay to public key hash (P2PKH) แต่อย่างไรก็ตาม ธุรกรรมบิตคอยน์ไม่ได้จำกัดอยู่แค่สคริปต์ในรูปแบบนี้เท่านั้น จริง ๆ แล้วสคริปต์สามารถเขียนเพื่อกำหนดเงื่อนไขที่ซับซ้อนหลากหลายรูปแบบได้ แต่เพื่อที่จะเข้าใจสคริปต์ที่ซับซ้อนเหล่านั้น เราจำเป็นต้องเข้าใจพื้นฐานของสคริปต์ธุรกรรมและภาษาสคริปต์เสียก่อน</p>
<p>ในส่วนนี้ เราจะสาธิตองค์ประกอบพื้นฐานของภาษาสคริปต์สำหรับธุรกรรมบิตคอยน์ และแสดงให้เห็นว่าภาษานี้ใช้เพื่อกำหนดเงื่อนไขสำหรับการใช้จ่ายอย่างไร และจะสามารถทำให้เงื่อนไขนั้นสำเร็จได้อย่างไร</p>
<blockquote>
<p>TIP: การตรวจสอบความถูกต้องของธุรกรรมบิตคอยน์ไม่ได้อาศัยรูปแบบตายตัว แต่เกิดจากการ รันภาษาสคริปต์ ซึ่งเปิดโอกาสให้สามารถกำหนดเงื่อนไขได้หลากหลายแทบไม่จำกัด</p>
</blockquote>
<h2>Turing Incompleteness</h2>
<p>ภาษาสคริปต์ของธุรกรรมบิตคอยน์มีโอเปอเรเตอร์จำนวนมาก แต่ถูกจำกัดไว้อย่างจงใจในด้านสำคัญอย่างหนึ่ง คือ ไม่มีลูปหรือโครงสร้างควบคุมการไหลที่ซับซ้อน นอกเหนือจากการควบคุมแบบมีเงื่อนไขเท่านั้น สิ่งนี้ทำให้ภาษา ไม่เป็น Turing Complete หมายความว่าสคริปต์มีความซับซ้อนได้จำกัดและมีเวลาการประมวลผลที่คาดเดาได้ Script ไม่ใช่ภาษาสำหรับงานทั่วไป ข้อจำกัดเหล่านี้ช่วยป้องกันไม่ให้ภาษาใช้สร้างลูปไม่รู้จบหรือ “logic bomb” รูปแบบอื่นที่อาจแฝงอยู่ในธุรกรรมและถูกใช้โจมตีแบบปฏิเสธการให้บริการ (DoS) ต่อเครือข่ายบิตคอยน์ได้ โปรดจำไว้ว่า ทุกธุรกรรมจะถูกยืนยันโดย full node ทุกตัวในเครือข่ายบิตคอยน์ ภาษาแบบจำกัดช่วยป้องกันไม่ให้กลไกตรวจสอบธุรกรรมกลายเป็นช่องโหว่</p>
<h2>Stateless Verification</h2>
<p>ภาษาสคริปต์ของธุรกรรมบิตคอยน์เป็นแบบ stateless กล่าวคือไม่มีสถานะก่อนเริ่มการประมวลผลสคริปต์ และไม่มีการเก็บสถานะหลังจากสคริปต์รันเสร็จ ข้อมูลทั้งหมดที่จำเป็นสำหรับการประมวลผลสคริปต์อยู่ในสคริปต์และธุรกรรมที่กำลังรันสคริปต์เท่านั้น สคริปต์จะทำงานเหมือนกันทุกครั้งบนทุกระบบ หากระบบของคุณตรวจสอบสคริปต์ผ่าน คุณมั่นใจได้ว่าระบบอื่นในเครือข่ายบิตคอยน์ทั้งหมดก็จะตรวจสอบผ่านเช่นกัน หมายความว่า ธุรกรรมที่ถูกต้องจะถูกต้องสำหรับทุกคน และทุกคนรู้เช่นนั้น ความสามารถในการคาดเดาผลลัพธ์ได้แบบนี้เป็นประโยชน์สำคัญของระบบบิตคอยน์</p>
<h2>Script Construction</h2>
<p>กลไกการตรวจสอบธุรกรรมแบบดั้งเดิมของ Bitcoin อาศัยสองส่วนของสคริปต์ในการตรวจสอบธุรกรรม: output script และ input script</p>
<p>output script ระบุเงื่อนไขที่ต้องถูกทำให้สำเร็จเพื่อใช้จ่ายเอาต์พุตในอนาคต เช่น ใครมีสิทธิ์ใช้จ่ายเอาต์พุต และจะมีการตรวจสอบสิทธิ์อย่างไร</p>
<p>input script คือสคริปต์ที่ทำให้เงื่อนไขใน output script สำเร็จ และอนุญาตให้เอาต์พุตถูกใช้จ่ายได้ Input script เป็นส่วนหนึ่งของทุก ๆ อินพุตภายในธุรกรรม ส่วนใหญ่ในธุรกรรมแบบเดิมจะมีลายเซ็นดิจิทัลที่สร้างจาก private key ของผู้ใช้ แต่ input script ไม่จำเป็นต้องมีลายเซ็นเสมอไป</p>
<p>ทุกโหนดที่ตรวจสอบ Bitcoin จะตรวจสอบธุรกรรมโดยรัน output script และ input script ตามที่ได้กล่าวไว้ในบทที่ 4 อินพุตแต่ละตัวมี outpoint ที่อ้างถึงเอาต์พุตของธุรกรรมก่อนหน้า อินพุตยังมี input script อยู่ด้วย ซอฟต์แวร์ตรวจสอบจะคัดลอก input script ดึง UTXO ที่อินพุตอ้างถึง และคัดลอก output script จาก UTXO นั้น จากนั้น output script และ input script จะถูกประมวลผลรวมกัน อินพุตจะถูกพิจารณาว่าถูกต้องหาก input script ทำให้เงื่อนไขใน output script สำเร็จ (จะอธิบายในหัวข้อ Separate execution of output and input scripts) อินพุตทุกตัวจะถูกตรวจสอบแยกกันเป็นส่วนหนึ่งของการตรวจสอบธุรกรรมโดยรวม</p>
<p>โปรดสังเกตว่า ขั้นตอนข้างต้นเกี่ยวข้องกับการคัดลอกข้อมูลทั้งหมด ข้อมูลต้นฉบับในเอาต์พุตก่อนหน้าและในอินพุตปัจจุบันจะไม่ถูกเปลี่ยนแปลงเลย เอาต์พุตก่อนหน้าไม่ถูกเปลี่ยนแปลงและไม่ได้รับผลกระทบจากความพยายามใช้จ่ายที่ล้มเหลว มีเพียงธุรกรรมที่ถูกต้องซึ่งทำให้เงื่อนไขใน output script สำเร็จเท่านั้นที่จะทำให้เอาต์พุตถูกพิจารณาว่า “ถูกใช้จ่ายแล้ว”</p>
<p>การผสาน input script และ output script เพื่อประเมินสคริปต์ของธุรกรรม คือ ตัวอย่างของ output script และ input script ของธุรกรรม Bitcoin แบบดั้งเดิมที่พบมากที่สุด (การชำระเงินไปยัง public key hash) ซึ่งแสดงสคริปต์ที่รวมกันจากการนำทั้งสองสคริปต์มาต่อกันก่อนการตรวจสอบ</p>
<p> <img src="https://image.nostr.build/5be02a704fca95d54b831b0ae9a66e09ea4804b95dde7c25d75decb167b1d55c.jpg" alt="image"></p>
<h3>The script execution stack</h3>
<p>ภาษาสคริปต์ของ Bitcoin ถูกเรียกว่า stack-based language เพราะใช้โครงสร้างข้อมูลที่เรียกว่า stack สแตกเป็นโครงสร้างข้อมูลที่เรียบง่ายมาก มองภาพได้เหมือนกองไพ่ สแตกมีสองการทำงานพื้นฐาน: push และ pop push คือการเพิ่มข้อมูลหนึ่งรายการไว้ด้านบนของสแตก ส่วน pop คือการนำรายการด้านบนสุดออกจากสแตก</p>
<p>ภาษาสคริปต์จะรันสคริปต์โดยประมวลผลรายการแต่ละรายการจากซ้ายไปขวา ตัวเลข (ค่าคงที่ของข้อมูล) จะถูก push ลงบนสแตก ตัวโอเปอเรเตอร์จะ push หรือ pop พารามิเตอร์หนึ่งตัวหรือมากกว่าจากสแตก นำไปประมวลผล และอาจ push ผลลัพธ์กลับลงสแตก ตัวอย่างเช่น OP_ADD จะ pop ข้อมูลสองรายการออกจากสแตก นำมาบวกกัน แล้ว push ผลรวมกลับลงบนสแตก</p>
<p>โอเปอเรเตอร์แบบมีเงื่อนไขจะประเมินเงื่อนไขและให้ผลลัพธ์เป็น boolean TRUE หรือ FALSE ตัวอย่างเช่น OP_EQUAL จะ pop ข้อมูลสองรายการจากสแตก และ push TRUE (TRUE แทนด้วยเลข 1) หากสองค่านั้นเท่ากัน หรือ push FALSE (แทนด้วยเลข 0) หากไม่เท่ากัน สคริปต์ของธุรกรรม Bitcoin มักมีโอเปอเรเตอร์แบบมีเงื่อนไขเพื่อสร้างผลลัพธ์ TRUE ที่แสดงว่าธุรกรรมนั้นถูกต้อง</p>
<h3>A simple script (สคริปต์อย่างง่าย)</h3>
<p>ตอนนี้เรามาลองประยุกต์สิ่งที่เราได้เรียนรู้เกี่ยวกับ scripts และ stacks กับตัวอย่างง่าย ๆ กัน </p>
<p>ในบทก่อนหน้าเราได้ยกสคริปต์อย่างง่ายมาตัวหนึ่งคือ</p>
<pre><code>2 3 OP_ADD 5 OP_EQUAL
</code></pre>
<p>แสดงการทำงานของโอเปอเรเตอร์คณิตศาสตร์ OP_ADD ซึ่งทำการบวกตัวเลขสองตัวแล้ววางผลลัพธ์ไว้บนสแต็ก จากนั้นตามด้วยโอเปอเรเตอร์แบบเงื่อนไข OP_EQUAL ที่ตรวจสอบว่าผลรวมดังกล่าวเท่ากับ 5 หรือไม่ ในหนังสือเล่มนี้ เพื่อความกระชับ อาจมีการละคำนำหน้า OP_ ในบางตัวอย่าง หากต้องการรายละเอียดเพิ่มเติมเกี่ยวกับโอเปอเรเตอร์และฟังก์ชันทั้งหมดของสคริปต์ สามารถดูได้ที่หน้า Script ของ Bitcoin Wiki<br>แม้ว่าผลลัพธ์สคริปต์แบบ Legacy ส่วนใหญ่จะอ้างอิง public key hash (ซึ่งโดยพื้นฐานคือ Bitcoin address แบบดั้งเดิม) เพื่อบังคับให้ต้องพิสูจน์ความเป็นเจ้าของก่อนจึงจะใช้เงินได้ แต่จริง ๆ แล้วสคริปต์ไม่จำเป็นต้องซับซ้อนขนาดนั้นก็ได้ สคริปต์ใด ๆ ที่ผสมกันระหว่าง output script และ input script แล้วให้ผลเป็น TRUE ถือว่า “ถูกต้อง”ดังนั้นสคริปต์คณิตศาสตร์ง่าย ๆ อย่างที่ใช้เป็นตัวอย่างในที่นี้ ก็ถือว่าเป็นสคริปต์ที่ถูกต้องเช่นกัน</p>
<p>ใช้ส่วนหนึ่งของสคริปต์คณิตศาสตร์เป็น output script</p>
<p>ให้ output script เป็น:</p>
<pre><code>3 OP_ADD 5 OP_EQUAL
</code></pre>
<p>ซึ่งสามารถถูกทำให้สำเร็จ (satisfied) ด้วยธุรกรรมที่มี input script แบบนี้:</p>
<pre><code>2
</code></pre>
<p>ซอฟต์แวร์ตรวจสอบความถูกต้อง (validation software) จะรวมสองสคริปต์เข้าด้วยกันกลายเป็น:</p>
<pre><code>2 3 OP_ADD 5 OP_EQUAL
</code></pre>
<p>เมื่อสคริปต์นี้ถูกประมวลผล ตามที่เราเห็นในบทก่อนหน้า ผลลัพธ์สุดท้ายคือ OP_TRUE ทำให้ธุรกรรมนี้ “ถูกต้อง” แม้ว่านี่จะเป็น output script ที่ถูกต้อง แต่ให้สังเกตว่า UTXO ที่สร้างขึ้น สามารถถูกใช้จ่ายได้โดยใครก็ตาม ที่มีทักษะคณิตศาสตร์พอจะรู้ว่าเลข 2 จะทำให้สคริปต์นี้สำเร็จได้<br> <img src="https://image.nostr.build/277a418d6958becf1c914c6eecd0732585265acceed0e238e9258e464c280728.jpg" alt="image"></p>
<blockquote>
<p>TIP: ธุรกรรมจะถือว่า ถูกต้อง (valid) หากผลลัพธ์บนยอดสแต็กเป็นค่า TRUE ซึ่งหมายถึงค่าที่ไม่ใช่ศูนย์ใด ๆ ทั้งหมด ธุรกรรมจะถือว่า ไม่ถูกต้อง (invalid) หากผลบนยอดสแต็กเป็นค่า FALSE (ศูนย์ หรือสแต็กว่าง), หรือการประมวลผลสคริปต์ถูกหยุดโดยตรงด้วยโอเปอเรเตอร์บางตัว (เช่น VERIFY, OP_RETURN), หรือสคริปต์นั้นมีความผิดพลาดทางไวยากรณ์/ความหมาย (semantic) เช่น มีคำสั่ง OP_IF แต่ไม่มี OP_ENDIF ปิดท้าย ดูรายละเอียดเพิ่มเติมได้ที่หน้า Script ของ Bitcoin Wiki</p>
</blockquote>
<p>สคริปต์ตัวอย่างถัดไปนี้จะมีความซับซ้อนขึ้นเล็กน้อย โดยมันคำนวณค่า 2 + 7 – 3 + 1 สังเกตว่าเมื่อสคริปต์มีโอเปอเรเตอร์หลายตัวต่อกัน สแต็กจะทำให้ผลลัพธ์ของโอเปอเรเตอร์ก่อนหน้า ถูกนำไปใช้ต่อโดยโอเปอเรเตอร์ตัวถัดไปได้</p>
<pre><code>2 7 OP_ADD 3 OP_SUB 1 OP_ADD 7 OP_EQUAL
</code></pre>
<p>ลองตรวจสอบสคริปต์ก่อนหน้านี้ด้วยตัวเอง โดยใช้ดินสอและกระดาษ เมื่อการประมวลผลสคริปต์สิ้นสุดลง คุณควรจะเหลือค่าบนสแต็กเป็นค่า TRUE</p>
<h3>Separate execution of output and input scripts</h3>
<p>ในไคลเอนต์ Bitcoin เวอร์ชันแรก ๆ  นั้น สคริปต์ของ output และ input ถูกต่อกันแล้วประมวลผลเป็นลำดับเดียว ด้วยเหตุผลด้านความปลอดภัย ซึ่งเกิดจากช่องโหว่ที่รู้จักกันในชื่อ 1 OP_RETURN bug ซึ่งวิธีนี้ถูกเปลี่ยนแปลงในปี 2010 เพื่อความปลอดภัย ในการใช้งานปัจจุบัน สคริปต์ทั้งสองจะถูกประมวลผลแยกกันโดยมีการคัดลอกสแต็กไปยังการประมวลผลครั้งถัดไป</p>
<p>อันดับแรก สคริปต์ input จะถูกประมวลผลโดยการดำเนินการของสแต็ก (stack execution engine) หากสคริปต์ input ถูกประมวลผลโดยไม่มีข้อผิดพลาดและไม่มีโอเปอเรชันเหลืออยู่ สแต็กจะถูกคัดลอกแล้วสคริปต์ output จะถูกประมวลผลต่อ หากผลลัพธ์ของการประมวลผลสคริปต์ output โดยใช้ข้อมูลสแต็กที่คัดลอกมาจาก input เป็น TRUE แสดงว่าสคริปต์ input สามารถแก้เงื่อนไขที่สคริปต์ output กำหนดได้ และด้วยเหตุนี้ input จึงเป็นการอนุญาตที่ถูกต้องสำหรับการใช้จ่าย UTXO นั้น แต่หากผลลัพธ์ใด ๆ นอกเหนือจาก TRUE ยังคงอยู่หลังการประมวลผลของสคริปต์รวมกัน แสดงว่า input นั้นไม่ถูกต้องเพราะล้มเหลวในการตอบสนองเงื่อนไขการใช้จ่ายที่วางไว้บน output</p>
<h2>Pay to Public Key Hash</h2>
<p>สคริปต์แบบ pay to public key hash (P2PKH) ใช้ output script ที่ภายในมีค่าแฮชซึ่งผูกพัน (commit) กับ public key หนึ่งค่า P2PKH เป็นที่รู้จักกันดีที่สุดในฐานะ Legacy Bitcoin address เอาต์พุตแบบ P2PKH สามารถถูกใช้จ่ายได้โดยการนำเสนอ public key ที่ตรงกับค่าแฮชที่ระบุไว้ และลายเซ็นดิจิทัลที่สร้างขึ้นด้วย private key ที่สอดคล้องกัน (ดูเรื่องนี้เพิ่มได้ในบทถัดไป) ส่วนตอนนี้มาดูตัวอย่างของ P2PKH output script กัน:</p>
<pre><code>OP_DUP OP_HASH160 &lt;Key Hash&gt; OP_EQUALVERIFY OP_CHECKSIG
</code></pre>
<p>Key Hash คือข้อมูลที่เมื่อนำไปเข้ารหัส จะกลายเป็น Bitcoin address แบบ legacy ในรูปแบบ base58check แอปพลิเคชันส่วนใหญ่จะแสดงค่า public key hash ภายในสคริปต์ในรูปแบบเลขฐานสิบหก (hexadecimal) แทนที่จะเป็นรูปแบบ Bitcoin address แบบ base58check ที่ผู้ใช้คุ้นเคย ซึ่งจะขึ้นต้นด้วยตัวอักษร “1”<br>output script ก่อนหน้านี้สามารถถูกทำให้สำเร็จ (satisfied) ได้ด้วย input script ในรูปแบบ:</p>
<pre><code>&lt;Signature&gt; &lt;Public Key&gt;
</code></pre>
<p>เมื่อรวมสคริปต์ทั้งสองเข้าด้วยกัน จะได้สคริปต์ตรวจสอบความถูกต้อง (combined validation script) ดังนี้:</p>
<pre><code>&lt;Sig&gt; &lt;Pubkey&gt; OP_DUP OP_HASH160 &lt;Hash&gt; OP_EQUALVERIFY OP_CHECKSIG
</code></pre>
<p>ผลลัพธ์จะเป็นค่า TRUE หาก input script มีลายเซ็นที่ถูกต้อง ซึ่งถูกสร้างขึ้นจาก private key ของของผู้ส่ง และสอดคล้องกับ public key hash ที่ถูกกำหนดไว้เป็นเงื่อนไขการใช้จ่ายนั้น</p>
<p> <img src="https://image.nostr.build/53dda9b219893a6b51c0dbf4b96b4c5eda32f2c2cd62a8c77b13fbec2e6280dc.jpg" alt="image"><br> <img src="https://image.nostr.build/318690aac1933790ae7bd5600e90c2fa311383a30515d80d9b2f9e0ff99bbcd7.jpg" alt="image"></p>
<h2>Scripted Multisignatures</h2>
<p>สคริปต์แบบมัลติซิกเนเจอร์ (multisignature) กำหนดเงื่อนไขโดยบันทึกค่าของ public key จำนวน k ค่าไว้ในสคริปต์ และต้องมีลายเซ็นอย่างน้อย t ค่าจาก public key เหล่านั้นจึงจะสามารถใช้จ่ายเงินได้ เรียกรูปแบบนี้ว่า t-of-k ตัวอย่างเช่น 2-of-3 multisignature คือกรณีที่มีการระบุ public key ไว้ทั้งหมดสามค่าในฐานะผู้มีสิทธิ์ลงนาม และต้องใช้ลายเซ็นอย่างน้อยสองจากสามค่านั้น เพื่อสร้างธุรกรรมที่ถูกต้องสำหรับการใช้จ่ายเงิน</p>
<blockquote>
<p>TIP: เอกสารของบิตคอยน์บางแหล่ง รวมถึงหนังสือเล่มนี้ในฉบับก่อนหน้า ใช้คำว่า “m-of-n” เพื่อเรียกมัลติซิกเนเจอร์แบบดั้งเดิม อย่างไรก็ตาม เมื่อพูดออกเสียงแล้ว ตัวอักษร m และ n แยกออกจากกันได้ยาก จึงมีการเลือกใช้คำว่า t-of-k แทน ทั้งสองคำนี้หมายถึงรูปแบบของระบบลายเซ็นแบบเดียวกัน</p>
</blockquote>
<p>รูปแบบทั่วไปของ output script ที่กำหนดเงื่อนไขมัลติซิกเนเจอร์แบบ t-of-k มีดังนี้:</p>
<pre><code>t &lt;Public Key 1&gt; &lt;Public Key 2&gt; ... &lt;Public Key k&gt; k OP_CHECKMULTISIG
</code></pre>
<p>โดยที่ k คือจำนวน public key ทั้งหมดที่ระบุไว้ และ t คือจำนวนลายเซ็นขั้นต่ำที่ต้องใช้เพื่อสามารถใช้จ่าย output นั้นได้<br>output script ที่กำหนดเงื่อนไขมัลติซิกเนเจอร์แบบ 2-of-3 จะมีลักษณะดังนี้:</p>
<pre><code>2 &lt;Public Key A&gt; &lt;Public Key B&gt; &lt;Public Key C&gt; 3 OP_CHECKMULTISIG
</code></pre>
<p>output script ข้างต้นสามารถถูกทำให้สำเร็จได้ด้วย input script ที่มีลายเซ็น เช่น:</p>
<pre><code>&lt;Signature B&gt; &lt;Signature C&gt;
</code></pre>
<p>หรือจะเป็นชุดลายเซ็นสองชุดใดก็ได้ ที่มาจาก private key ที่สอดคล้องกับ public key ทั้งสามค่าที่ถูกระบุไว้</p>
<p>เมื่อรวมสคริปต์ทั้งสองเข้าด้วยกัน จะได้สคริปต์ตรวจสอบความถูกต้องดังนี้:</p>
<pre><code>&lt;Sig B&gt; &lt;Sig C&gt; 2 &lt;Pubkey A&gt; &lt;Pubkey B&gt; &lt;Pubkey C&gt; 3 OP_CHECKMULTISIG
</code></pre>
<p>เมื่อถูกประมวลผล สคริปต์ที่ถูกรวมกันนี้จะให้ผลลัพธ์เป็นค่า TRUE หาก input script มีลายเซ็นที่ถูกต้องจำนวนสองชุด ซึ่งมาจาก private key ที่สอดคล้องกับ public key สองค่าใดก็ได้ จากสามค่าที่ถูกกำหนดไว้เป็นเงื่อนไขการใช้จ่าย</p>
<p>ในปัจจุบัน นโยบายการส่งต่อธุรกรรม (transaction relay policy) ของ Bitcoin Core จำกัดจำนวน public key ใน multisignature output script ไว้สูงสุดที่สามค่า หมายความว่าสามารถสร้างมัลติซิกเนเจอร์ได้ตั้งแต่แบบ 1-of-1 ไปจนถึง 3-of-3 หรือรูปแบบใด ๆ ภายในช่วงนี้ คุณอาจต้องการตรวจสอบฟังก์ชัน IsStandard() เพื่อดูว่าขณะนี้เครือข่ายยอมรับรูปแบบใดบ้าง โปรดสังเกตว่า ข้อจำกัดที่สามคีย์นี้ใช้กับ multisignature script แบบมาตรฐานเท่านั้น (ซึ่งเรียกอีกชื่อหนึ่งว่า “bare” multisignature) ไม่ได้ใช้กับสคริปต์ที่ถูกห่อหุ้มด้วยโครงสร้างอื่น เช่น P2SH, P2WSH, หรือ P2TR สำหรับ multisignature script แบบ P2SH จะถูกจำกัดทั้งในระดับนโยบาย (policy) และฉันทามติ (consensus) ไว้ที่สูงสุด 15 คีย์ ทำให้สามารถสร้างมัลติซิกเนเจอร์ได้สูงสุดแบบ 15-of-15 เราจะเรียนรู้เกี่ยวกับ P2SH ในหัวข้อ Pay to Script Hash ส่วนสคริปต์รูปแบบอื่นทั้งหมด จะถูกจำกัดโดยฉันทามติไว้ที่ public key ได้ไม่เกิน 20 คีย์ ต่อคำสั่ง OP_CHECKMULTISIG หรือ OP_CHECKMULTISIGVERIFY หนึ่งคำสั่ง ทั้งนี้ สคริปต์หนึ่งรายการอาจมีคำสั่งเหล่านี้ได้มากกว่าหนึ่งครั้ง</p>
<h3>An Oddity in CHECKMULTISIG Execution</h3>
<p>มีความผิดปกติ (oddity) อย่างหนึ่งในการทำงานของ OP_CHECKMULTISIG ซึ่งทำให้ต้องมีวิธีแก้ไขเฉพาะหน้าเล็กน้อย เมื่อ OP_CHECKMULTISIG ถูกประมวลผล ตามหลักแล้วมันควรจะนำข้อมูลออกจากสแต็กมาใช้เป็นพารามิเตอร์จำนวน t + k + 2 ค่า อย่างไรก็ตาม เนื่องจากความผิดปกตินี้ OP_CHECKMULTISIG จะดึงค่าจากสแต็กออกมา มากกว่าที่คาดไว้หนึ่งค่า</p>
<p>เรามาดูรายละเอียดของเรื่องนี้ให้ชัดเจนขึ้น โดยใช้ตัวอย่างสคริปต์ตรวจสอบความถูกต้องจากก่อนหน้านี้:</p>
<pre><code>&lt;Sig B&gt; &lt;Sig C&gt; 2 &lt;Pubkey A&gt; &lt;Pubkey B&gt; &lt;Pubkey C&gt; 3 OP_CHECKMULTISIG
</code></pre>
<p>ขั้นแรก OP_CHECKMULTISIG จะนำค่าแรกบนสุดของสแต็กออกมา ซึ่งก็คือ k (ในตัวอย่างนี้คือ “3”) จากนั้นจะนำข้อมูลออกมาอีก k ค่า ซึ่งเป็น public key ที่สามารถใช้ลงนามได้ ในตัวอย่างนี้คือ public key A, B และ C จากนั้นมันจะนำค่าออกมาอีกหนึ่งค่า ซึ่งก็คือ t หรือจำนวนลายเซ็นขั้นต่ำที่ต้องใช้ (quorum) โดยในกรณีนี้ t = 2 ตามปกติแล้ว ณ จุดนี้ OP_CHECKMULTISIG ควรจะนำค่าออกมาอีก t ค่า ซึ่งเป็นลายเซ็น เพื่อตรวจสอบว่าถูกต้องหรือไม่ อย่างไรก็ตาม เนื่องจากความผิดปกติในการนำไปใช้งานจริง OP_CHECKMULTISIG จะนำค่าจากสแต็กออกมา มากกว่าที่ควรหนึ่งค่า (รวมเป็น t + 1 ค่า) ค่าส่วนเกินนี้ถูกเรียกว่า dummy stack element และจะถูกละเว้นในระหว่างการตรวจสอบลายเซ็น ดังนั้นมันจึงไม่มีผลโดยตรงต่อการทำงานของ OP_CHECKMULTISIG เอง อย่างไรก็ตาม dummy element นี้ จำเป็นต้องมีอยู่ เพราะหากไม่มีค่าเหลืออยู่บนสแต็กในขณะที่ OP_CHECKMULTISIG พยายามดึงค่าออกมาเพิ่ม จะทำให้เกิด stack error และสคริปต์ล้มเหลว ส่งผลให้ธุรกรรมนั้นไม่ถูกต้อง เนื่องจาก dummy element ถูกละเว้น ค่าของมันจึงสามารถเป็นค่าอะไรก็ได้ แต่ในช่วงแรกได้มีธรรมเนียมใช้ค่า OP_0 สำหรับตำแหน่งนี้ ต่อมาสิ่งนี้ได้กลายเป็นกฎของ relay policy และในที่สุดก็กลายเป็นกฎระดับฉันทามติ (consensus rule) เมื่อมีการบังคับใช้ BIP147</p>
<p>เนื่องจากการนำ dummy element ออกจากสแต็กเป็นส่วนหนึ่งของกฎฉันทามติ (consensus rules) พฤติกรรมนี้จึงต้องถูกคงไว้และทำซ้ำตลอดไป ดังนั้นสคริปต์จึงควรมีหน้าตาเป็นดังนี้:</p>
<pre><code>OP_0 &lt;Sig B&gt; &lt;Sig C&gt; 2 &lt;Pubkey A&gt; &lt;Pubkey B&gt; &lt;Pubkey C&gt; 3 OP_CHECKMULTISIG
</code></pre>
<p>ด้วยเหตุนี้ input script ที่ถูกใช้งานจริงใน multisignature จะ ไม่ใช่:</p>
<pre><code>&lt;Signature B&gt; &lt;Signature C&gt;
</code></pre>
<p>แต่จะต้องเป็น:</p>
<pre><code>OP_0 &lt;Sig B&gt; &lt;Sig C&gt;
</code></pre>
<p>บางคนเชื่อว่าความผิดปกตินี้เกิดจากบั๊กในโค้ดต้นฉบับของบิตคอยน์ แต่อย่างไรก็ตาม ยังมีคำอธิบายทางเลือกที่สมเหตุสมผลอยู่ การตรวจสอบลายเซ็นแบบ t-of-k อาจต้องใช้การตรวจสอบลายเซ็นมากกว่าทั้งค่า t หรือ k เพียงอย่างเดียว</p>
<p>ลองพิจารณาตัวอย่างง่าย ๆ แบบ 1-in-5 โดยมีสคริปต์ตรวจสอบความถูกต้องที่ถูกรวมกันดังนี้:</p>
<pre><code>&lt;dummy&gt; &lt;Sig4&gt; 1 &lt;key0&gt; &lt;key1&gt; &lt;key2&gt; &lt;key3&gt; &lt;key4&gt; 5 OP_CHECKMULTISIG
</code></pre>
<p>ลายเซ็นจะถูกตรวจสอบกับ key0 ก่อน จากนั้นกับ key1 และต่อเนื่องไปยัง key อื่น ๆ จนกระทั่งในที่สุดจึงถูกนำไปเปรียบเทียบกับ public key ที่สอดคล้องกันจริง ๆ คือ key4 นั่นหมายความว่า จำเป็นต้องมีการตรวจสอบลายเซ็นถึง ห้าครั้ง ทั้งที่มีลายเซ็นเพียงหนึ่งรายการเท่านั้น แนวทางหนึ่งในการลดความซ้ำซ้อนนี้ คือให้ OP_CHECKMULTISIG รับข้อมูลในลักษณะเป็น map ที่ระบุว่าแต่ละลายเซ็นที่ส่งเข้ามาสัมพันธ์กับ public key ใด ซึ่งจะทำให้ OP_CHECKMULTISIG ต้องทำการตรวจสอบลายเซ็นเพียง t ครั้ง เท่านั้น จึงเป็นไปได้ว่า นักพัฒนาของบิตคอยน์ได้เพิ่มองค์ประกอบพิเศษนี้เข้าไปตั้งแต่เวอร์ชันแรก (ซึ่งปัจจุบันเราเรียกว่า dummy stack element) เพื่อเปิดทางให้สามารถเพิ่มความสามารถดังกล่าวผ่าน soft fork ในอนาคตได้ อย่างไรก็ตาม ฟีเจอร์ดังกล่าวไม่เคยถูกนำมาใช้งานจริง และการอัปเดตกฎฉันทามติด้วย BIP147 ในปี 2017 ก็ทำให้ไม่สามารถเพิ่มฟีเจอร์นี้ได้อีกต่อไปในอนาคต </p>
<p>มีเพียงนักพัฒนาของบิตคอยน์เท่านั้น ที่จะสามารถบอกได้ว่าการมีอยู่ของ dummy stack element นั้นเกิดจากบั๊ก หรือเป็นการวางแผนสำหรับการอัปเกรดในอนาคต สำหรับหนังสือเล่มนี้ เราเรียกสิ่งนี้เพียงว่าเป็น “ความผิดปกติ” (oddity) </p>
<p>นับจากนี้เป็นต้นไป หากคุณเห็นสคริปต์แบบ multisignature ให้คาดไว้เลยว่าจะต้องมี OP_0 เพิ่มเข้ามาในตอนต้น โดยจุดประสงค์เดียวของมันคือการเป็นวิธีแก้ปัญหาเฉพาะหน้า สำหรับความผิดปกติที่มีอยู่ในกฎฉันทามติ</p>
<h2>Pay to Script Hash</h2>
<p>Pay to Script Hash (P2SH) ถูกนำมาใช้ในปี 2012 ในฐานะรูปแบบการทำงานแบบใหม่ที่ทรงพลัง ซึ่งช่วยทำให้การใช้งานสคริปต์ที่ซับซ้อนเป็นเรื่องง่ายขึ้นอย่างมาก เพื่ออธิบายความจำเป็นของ P2SH เรามาดูตัวอย่างเชิงปฏิบัติกัน</p>
<p>โมฮัมเหม็ดเป็นผู้นำเข้าอุปกรณ์อิเล็กทรอนิกส์ซึ่งตั้งอยู่ในดูไบ บริษัทของโมฮัมเหม็ดใช้ฟีเจอร์มัลติซิกเนเจอร์ของบิตคอยน์อย่างแพร่หลายสำหรับบัญชีขององค์กร สคริปต์มัลติซิกเนเจอร์เป็นหนึ่งในการใช้งานความสามารถด้านสคริปต์ขั้นสูงของบิตคอยน์ที่พบได้บ่อยที่สุด และเป็นฟีเจอร์ที่ทรงพลังมาก บริษัทของโมฮัมเหม็ดใช้สคริปต์มัลติซิกเนเจอร์กับการชำระเงินจากลูกค้าทุกราย การชำระเงินของลูกค้าจะถูกล็อกไว้ในลักษณะที่ต้องใช้ลายเซ็นอย่างน้อยสองชุดจึงจะสามารถปลดล็อกได้ โมฮัมเหม็ด หุ้นส่วนอีกสามคนของเขา และทนายความของบริษัท ต่างสามารถให้ลายเซ็นได้คนละหนึ่งชุด รูปแบบมัลติซิกเนเจอร์เช่นนี้ช่วยสร้างกลไกกำกับดูแลกิจการ และป้องกันการโจรกรรม การยักยอก หรือการสูญหายของเงินได้</p>
<p>สคริปต์ที่ได้จากเงื่อนไขดังกล่าวจะค่อนข้างยาว และมีลักษณะดังนี้:</p>
<pre><code>2 &lt;Mohammed's Public Key&gt; &lt;Partner1 Public Key&gt; &lt;Partner2 Public Key&gt;
&lt;Partner3 Public Key&gt; &lt;Attorney Public Key&gt; 5 OP_CHECKMULTISIG
</code></pre>
<p>แม้ว่าสคริปต์แบบมัลติซิกเนเจอร์จะเป็นฟีเจอร์ที่ทรงพลังมาก แต่ก็ใช้งานได้ค่อนข้างยุ่งยาก จากสคริปต์ตัวอย่างก่อนหน้า โมฮัมเหม็ดจะต้องสื่อสารสคริปต์นี้ให้ลูกค้าทุกรายทราบก่อนทำการชำระเงิน ลูกค้าแต่ละรายยังจำเป็นต้องใช้ซอฟต์แวร์กระเป๋าเงินบิตคอยน์แบบพิเศษ ที่สามารถสร้างสคริปต์ธุรกรรมแบบกำหนดเองได้ นอกจากนี้ ธุรกรรมที่ได้จะมีขนาดใหญ่กว่าธุรกรรมการชำระเงินแบบธรรมดาประมาณห้าเท่า เนื่องจากสคริปต์นี้มี public key ที่มีความยาวมาก ภาระของข้อมูลส่วนเกินนี้จะตกอยู่กับลูกค้าในรูปของค่าธรรมเนียมธุรกรรมที่สูงขึ้น สุดท้ายแล้ว สคริปต์ธุรกรรมขนาดใหญ่เช่นนี้จะถูกเก็บไว้ในชุดข้อมูล UTXO set ของทุก full node จนกว่าจะถูกใช้จ่าย ซึ่งปัญหาเหล่านี้ทั้งหมดทำให้การใช้งาน output script ที่ซับซ้อนเป็นเรื่องยากในทางปฏิบัติ</p>
<p>P2SH ถูกพัฒนาขึ้นมาเพื่อแก้ไขปัญหาเชิงปฏิบัติเหล่านี้ และเพื่อทำให้การใช้งานสคริปต์ที่ซับซ้อนง่ายพอ ๆ กับการชำระเงินไปยัง Bitcoin address แบบกุญแจเดียว ด้วยการชำระเงินแบบ P2SH สคริปต์ที่ซับซ้อนจะถูกแทนที่ด้วย “คำมั่น” (commitment) ซึ่งก็คือค่าแฮชเชิงคริปโตกราฟีของสคริปต์นั้น เมื่อมีการนำเสนอธุรกรรมเพื่อใช้จ่าย UTXO ในภายหลัง ธุรกรรมนั้นจะต้องมีทั้งสคริปต์ที่ตรงกับค่าแฮชที่ถูกผูกไว้ และข้อมูลที่ใช้ทำให้สคริปต์ดังกล่าวสำเร็จ กล่าวอย่างง่ายที่สุด P2SH หมายถึง “จ่ายเงินให้กับสคริปต์ที่ตรงกับค่าแฮชนี้ โดยสคริปต์นั้นจะถูกนำมาแสดงในภายหลังเมื่อ output นี้ถูกใช้จ่าย”</p>
<p>ในธุรกรรมแบบ P2SH สคริปต์ที่ถูกแทนที่ด้วยค่าแฮชจะถูกเรียกว่า redeem script เนื่องจากสคริปต์นี้จะถูกนำเสนอให้ระบบในช่วงเวลาที่มีการนำมาใช้จ่าย (redeem) แทนที่จะถูกใส่ไว้เป็น output script ตั้งแต่ต้นตารางด้านล่างจะ แสดงสคริปต์ในกรณีที่ไม่ใช้ P2SH และแสดงสคริปต์เดียวกันที่ถูกเข้ารหัสในรูปแบบ P2SH</p>
<p>Complex script without P2SH</p>
<table>
<thead>
<tr>
<th align="left">Output script</th>
<th align="left">2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 OP_CHECKMULTISIG</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Input script</td>
<td align="left">Sig1 Sig2</td>
</tr>
</tbody></table>
<p>Complex script as P2SH</p>
<table>
<thead>
<tr>
<th align="left">Redeem script</th>
<th align="left">2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 OP_CHECKMULTISIG</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Output script</td>
<td align="left">OP_HASH160 &lt;20-byte hash of redeem script&gt; OP_EQUAL</td>
</tr>
<tr>
<td align="left">Input script</td>
<td align="left">Sig1 Sig2 &lt;redeem script&gt;</td>
</tr>
</tbody></table>
<p>ดังที่เห็นได้จากตาราง เมื่อใช้ P2SH สคริปต์ที่ซับซ้อนซึ่งระบุเงื่อนไขในการใช้จ่าย output (redeem script) จะไม่ถูกนำมาใส่ไว้ใน output script อีกต่อไป แต่จะมีเพียงค่าแฮชของสคริปต์นั้นอยู่ใน output script เท่านั้น ส่วนตัว redeem script เองจะถูกนำมาแสดงภายหลังในฐานะส่วนหนึ่งของ input script เมื่อมีการใช้จ่าย output นั้นแนวทางนี้ทำให้ภาระในด้านค่าธรรมเนียมและความซับซ้อน ถูกย้ายจากฝั่งผู้รับเงิน ไปยังฝั่งผู้ใช้จ่ายเงินในภายหลังเรามาดูกรณีของบริษัทโมฮัมเหม็ด สคริปต์มัลติซิกเนเจอร์ที่ซับซ้อน และสคริปต์ P2SH ที่ได้จากมันกัน อันดับแรก คือสคริปต์มัลติซิกเนเจอร์ที่บริษัทของโมฮัมเหม็ดใช้สำหรับการรับชำระเงินจากลูกค้าทุกราย:</p>
<pre><code>2 &lt;Mohammed's Public Key&gt; &lt;Partner1 Public Key&gt; &lt;Partner2 Public Key&gt;
&lt;Partner3 Public Key&gt; &lt;Attorney Public Key&gt; 5 OP_CHECKMULTISIG
</code></pre>
<p>สคริปต์ทั้งหมดนี้สามารถถูกแทนที่ด้วยค่าแฮชเชิงคริปโตกราฟีขนาด 20 ไบต์ ได้ โดยเริ่มจากการนำสคริปต์ไปผ่านอัลกอริทึมแฮช SHA256 ก่อน จากนั้นจึงนำผลลัพธ์ที่ได้ไปแฮชต่อด้วยอัลกอริทึม RIPEMD-160 ตัวอย่างเช่น เริ่มต้นด้วยค่าแฮชของ redeem script ของบริษัทโมฮัมเหม็ด:</p>
<pre><code>54c557e07dde5bb6cb791c7a540e0a4796f5e97e
</code></pre>
<p>ธุรกรรมแบบ P2SH จะล็อก output ไว้กับค่าแฮชนี้ แทนที่จะใช้ redeem script ที่ยาวกว่า โดยใช้แม่แบบ output script แบบพิเศษดังนี้:</p>
<pre><code>OP_HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e OP_EQUAL
</code></pre>
<p>ซึ่งอย่างที่เห็น จะสั้นกว่ามาก แทนที่จะเป็น “จ่ายไปยังสคริปต์มัลติซิกเนเจอร์ที่มี 5 คีย์นี้” ธุรกรรมแบบ P2SH จะกลายเป็น “จ่ายไปยังสคริปต์ที่มีค่าแฮชนี้” ลูกค้าที่ชำระเงินให้บริษัทของโมฮัมเหม็ด จำเป็นต้องใส่เพียง output script ที่สั้นกว่านี้ลงในธุรกรรมเท่านั้น</p>
<p>เมื่อโมฮัมเหม็ดและหุ้นส่วนต้องการใช้จ่าย UTXO นี้ พวกเขาจะต้องแสดง redeem script ต้นฉบับ (ซึ่งเป็นสคริปต์ที่ค่าแฮชของมันถูกใช้ล็อก UTXO ไว้) พร้อมกับลายเซ็นที่จำเป็นเพื่อปลดล็อกสคริปต์นั้น ดังนี้:</p>
<pre><code>&lt;Sig1&gt; &lt;Sig2&gt; &lt;2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG&gt;
</code></pre>
<p>สคริปต์ทั้งสองจะถูกรวมกันเป็นสองขั้นตอน ขั้นแรก redeem script จะถูกตรวจสอบกับ output script เพื่อยืนยันว่าค่าแฮชตรงกัน:</p>
<pre><code>&lt;2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG&gt; OP_HASH160 &lt;script hash&gt; OP_EQUAL
</code></pre>
<p>หากค่าแฮชของ redeem script ตรงกัน จากนั้น redeem script จะถูกนำมาประมวลผล:</p>
<pre><code>&lt;Sig1&gt; &lt;Sig2&gt; 2 &lt;PK1&gt; &lt;PK2&gt; &lt;PK3&gt; &lt;PK4&gt; &lt;PK5&gt; 5 OP_CHECKMULTISIG
</code></pre>
<h3>P2SH Addresses</h3>
<p>อีกส่วนสำคัญของฟีเจอร์ P2SH คือความสามารถในการเข้ารหัสค่าแฮชของสคริปต์ให้อยู่ในรูปของ “address” ตามที่กำหนดไว้ใน BIP13 ที่อยู่แบบ P2SH คือการเข้ารหัสแบบ base58check ของค่าแฮชขนาด 20 ไบต์ของสคริปต์ เช่นเดียวกับที่ Bitcoin address ปกติคือการเข้ารหัสแบบ base58check ของค่าแฮชขนาด 20 ไบต์ของ public key ที่อยู่แบบ P2SH ใช้ version prefix เป็น “5” ซึ่งทำให้ที่อยู่ที่ถูกเข้ารหัสออกมาในรูป base58check เริ่มต้นด้วยตัวเลข “3”</p>
<p>ตัวอย่างเช่น สคริปต์ที่ซับซ้อนของบริษัทโมฮัมเหม็ด เมื่อนำไปแฮชและเข้ารหัสแบบ base58check เป็นที่อยู่ P2SH จะได้เป็น:</p>
<pre><code>39RF6JqABiHdYHkfChV6USGMe6Nsr66Gzw
</code></pre>
<p>จากนี้ไป โมฮัมเหม็ดสามารถให้ “address” นี้กับลูกค้าได้ และลูกค้าก็สามารถใช้กระเป๋าเงินบิตคอยน์ทั่วไปแทบทุกแบบ เพื่อทำการชำระเงินอย่างง่าย ๆ ได้ เหมือนกับการจ่ายไปยัง Bitcoin address ปกติทั่วไป ตัวเลขนำหน้า 3 จะช่วยบอกใบ้ให้ทราบว่านี่คือที่อยู่แบบพิเศษ ซึ่งเชื่อมโยงกับสคริปต์ ไม่ใช่กับ public key โดยตรง แต่ในแง่ของการใช้งานแล้ว มันทำงานเหมือนกับการชำระเงินไปยัง Bitcoin address อื่น ๆ ทุกประการ ที่อยู่แบบ </p>
<p>P2SH ช่วยซ่อนความซับซ้อนทั้งหมดเอาไว้ ทำให้ผู้ที่ทำการชำระเงินไม่จำเป็นต้องเห็นหรือเข้าใจสคริปต์เลย</p>
<h3>Benefits of P2SH</h3>
<p>ฟีเจอร์ P2SH มีข้อดีเมื่อเทียบกับการใช้งานสคริปต์ที่ซับซ้อนโดยตรงใน output ดังนี้:</p>
<ul>
<li><p>ความคล้ายคลึงกับ Bitcoin address แบบ legacy เดิม ทำให้ผู้ส่งและซอฟต์แวร์กระเป๋าเงินของผู้ส่ง ไม่จำเป็นต้องมีการออกแบบหรือพัฒนาเพิ่มเติมที่ซับซ้อนเพื่อรองรับ P2SH</p>
</li>
<li><p>P2SH ย้ายภาระในการจัดเก็บข้อมูลของสคริปต์ที่ยาว จากฝั่ง output (ซึ่งนอกจากจะถูกเก็บไว้บนบล็อกเชนแล้ว ยังอยู่ในชุดข้อมูล UTXO set ด้วย) ไปอยู่ที่ฝั่ง input (ซึ่งจะถูกเก็บไว้บนบล็อกเชนเท่านั้น)</p>
</li>
<li><p>P2SH ย้ายภาระในการจัดเก็บข้อมูลของสคริปต์ที่ยาว จากช่วงเวลาปัจจุบันขณะชำระเงิน ไปยังช่วงเวลาในอนาคตเมื่อมีการนำ output นั้นมาใช้จ่าย</p>
</li>
<li><p>P2SH ย้ายภาระค่าธรรมเนียมธุรกรรมที่เกิดจากสคริปต์ยาว จากผู้ส่ง ไปยังผู้รับ ซึ่งเป็นฝ่ายที่ต้องใส่ redeem script ที่ยาวลงไปเมื่อทำการใช้จ่าย</p>
</li>
</ul>
<h3>Redeem Script and Validation</h3>
<p>คุณไม่สามารถใส่ P2SH ซ้อนอยู่ภายใน redeem script ของ P2SH ได้ เนื่องจากข้อกำหนดของ P2SH ไม่ได้รองรับการทำงานแบบเรียกซ้ำ (recursive) นอกจากนี้ แม้ในเชิงเทคนิคจะสามารถใส่ OP_RETURN (ดูหัวข้อ Data Recording Output (OP_RETURN)) ลงไปใน redeem script ได้ เพราะกฎไม่ได้ห้ามไว้ แต่ในทางปฏิบัติกลับไม่มีประโยชน์ใด ๆ เนื่องจากเมื่อมีการประมวลผล OP_RETURN ระหว่างขั้นตอนการตรวจสอบความถูกต้อง ธุรกรรมจะถูกตัดสินว่าไม่ถูกต้องทันที</p>
<p>โปรดสังเกตว่า เนื่องจาก redeem script จะไม่ถูกเผยแพร่ให้เครือข่ายทราบจนกว่าจะมีการพยายามใช้จ่าย P2SH output หากคุณสร้าง output ด้วยค่าแฮชของ redeem script ที่ไม่ถูกต้อง คุณจะไม่สามารถใช้จ่ายมันได้เลย ธุรกรรมที่พยายามใช้จ่าย ซึ่งต้องแนบ redeem script เข้ามาด้วย จะไม่ถูกยอมรับ เนื่องจากสคริปต์นั้นไม่ถูกต้อง สิ่งนี้ก่อให้เกิดความเสี่ยง เพราะเป็นไปได้ที่จะส่งบิตคอยน์ไปยัง P2SH address ที่ไม่สามารถถูกใช้จ่ายได้ในภายหลัง</p>
<blockquote>
<p>คำเตือน: output script แบบ P2SH จะบรรจุเพียงค่าแฮชของ redeem script เท่านั้น ซึ่งไม่ได้ให้ข้อมูลใด ๆ เกี่ยวกับเนื้อหาของ redeem script เลย ดังนั้น P2SH output จะยังคงถูกถือว่าถูกต้องและถูกรับรอง แม้ว่า redeem script ที่ถูกอ้างถึงนั้นจะไม่ถูกต้องก็ตาม ด้วยเหตุนี้เองที่จะทำให้คุณอาจเผลอได้รับบิตคอยน์ในรูปแบบที่ไม่สามารถนำไปใช้จ่ายได้ในภายหลัง</p>
</blockquote>
<h3>Data Recording Output (OP_RETURN)</h3>
<p>บิตคอยน์มีบล็อกเชนกระจายศูนย์ที่มีการประทับเวลา (timestamped blockchain) ซึ่งมีศักยภาพในการนำไปใช้มากกว่าการชำระเงิน หลายคนพยายามใช้ภาษาสคริปต์ของธุรกรรมเพื่อใช้ประโยชน์จากความปลอดภัยและความทนทานของระบบสำหรับแอปพลิเคชันอื่น เช่น บริการทนายความดิจิทัล (digital notary services) ความพยายามในช่วงแรก ๆ เพื่อใช้สคริปต์ของบิตคอยน์ในลักษณะนี้เกี่ยวข้องกับการสร้าง transaction output ที่บันทึกข้อมูลลงบนบล็อกเชน เช่น การบันทึก commitment ต่อไฟล์หนึ่งไฟล์ เพื่อให้ใครก็ตามสามารถตรวจสอบการมีอยู่จริงของไฟล์นั้น ณ วันที่หนึ่งได้ โดยอ้างอิงจากธุรกรรมนั้น</p>
<p>การใช้บล็อกเชนของบิตคอยน์เพื่อเก็บข้อมูลที่ไม่เกี่ยวข้องกับการชำระเงินเป็นประเด็นที่ถกเถียงกัน หลายคนมองว่าเป็นการใช้งานที่ไม่เหมาะสมและควรถูกขัดขวาง ขณะที่อีกกลุ่มมองว่าเป็นตัวอย่างของความสามารถทรงพลังของเทคโนโลยีบล็อกเชนและควรสนับสนุนการทดลองเช่นนี้ ผู้คัดค้านให้เหตุผลว่าการบรรจุข้อมูลเหล่านี้ทำให้ผู้ที่รันฟูลโหนดต้องแบกรับต้นทุนพื้นที่จัดเก็บข้อมูลที่ไม่ใช่สิ่งที่บล็อกเชนถูกออกแบบมาเพื่อรองรับ นอกจากนี้ ธุรกรรมลักษณะนี้ยังอาจสร้าง UTXO ที่ไม่สามารถใช้จ่ายได้ โดยใช้ legacy Bitcoin address เป็นพื้นที่ข้อมูลขนาด 20 ไบต์ เพราะแอดเดรสนั้นถูกใช้เป็นข้อมูลและไม่สอดคล้องกับกุญแจส่วนตัว UTXO ที่เกิดขึ้นจึงไม่มีวันถูกใช้จ่ายได้ และเป็น "ธุรกรรมปลอม" ซึ่งจะไม่ถูกลบออกจาก UTXO set ทำให้ฐานข้อมูล UTXO โตขึ้นเรื่อย ๆ หรือที่เรียกว่า "bloat"</p>
<p>โดยในที่สุดก็ได้มีการประนีประนอมโดยอนุญาตให้ output script ที่เริ่มต้นด้วย OP_RETURN สามารถเพิ่มข้อมูลที่ไม่เกี่ยวกับการชำระเงินลงใน transaction output ได้ อย่างไรก็ตาม แตกต่างจาก UTXO ปลอม OP_RETURN จะสร้างเอาต์พุตที่พิสูจน์ได้อย่างชัดเจนว่าไม่สามารถใช้จ่ายได้ ทำให้ไม่จำเป็นต้องถูกเก็บไว้ใน UTXO set เอาต์พุตที่มี OP_RETURN จะถูกบันทึกลงในบล็อกเชน ดังนั้นจึงใช้พื้นที่ดิสก์และเพิ่มขนาดบล็อกเชน แต่จะไม่ถูกเก็บใน UTXO set และจึงไม่ทำให้ Full node ต้องแบกรับต้นทุนฐานข้อมูลที่แพงขึ้น</p>
<p>OP_RETURN scripts จะมีรูปแบบประมาณนี้:</p>
<pre><code>OP_RETURN &lt;data&gt;
</code></pre>
<p>ส่วนข้อมูล (data portion) มักใช้แทนค่าแฮช เช่น เอาต์พุตจากอัลกอริทึม SHA256 ซึ่งมีขนาด 32 ไบต์ แอปพลิเคชันบางประเภทจะใส่คำนำหน้า (prefix) ไว้หน้าข้อมูล เพื่อช่วยระบุว่าเป็นข้อมูลของแอปพลิเคชันใด ตัวอย่างเช่น บริการรับรองเอกสารดิจิทัล Proof of Existence ใช้คำนำหน้าขนาด 8 ไบต์คือคำว่า DOCPROOF ซึ่งเข้ารหัสแบบ ASCII และแสดงในรูปเลขฐานสิบหกเป็น 44 4f 43 50 52 4f 4f 46</p>
<p>ควรทำความเข้าใจว่าไม่มี input script ใดที่สามารถสอดคล้องกับ OP_RETURN เพื่อนำมา “ใช้จ่าย” (spend) เอาต์พุตแบบ OP_RETURN ได้ จุดประสงค์ทั้งหมดของ OP_RETURN คือการสร้างเอาต์พุตที่ไม่สามารถใช้จ่ายได้ตั้งแต่ต้น ดังนั้นจึงไม่จำเป็นต้องถูกเก็บไว้ใน UTXO set ในฐานะเอาต์พุตที่อาจถูกใช้จ่ายในอนาคต กล่าวได้ว่า OP_RETURN outputs เป็นเอาต์พุตที่พิสูจน์ได้ชัดเจนว่าไม่สามารถใช้จ่ายได้ (provably unspendable)</p>
<p>โดยทั่วไป OP_RETURN outputs จะกำหนดจำนวนบิตคอยน์เป็นศูนย์ เพราะบิตคอยน์ใด ๆ ที่ถูกกำหนดไว้ในเอาต์พุตลักษณะนี้ จะถือว่าสูญหายไปตลอดกาล หากมีการอ้างอิง OP_RETURN output มาเป็น input ของธุรกรรมใด ธุรกรรมนั้นจะถูกเครื่องยนต์ตรวจสอบสคริปต์ (script validation engine) ยุติการทำงานทันทีและถูกตัดสินว่าไม่ถูกต้อง การทำงานของ OP_RETURN จะทำให้สคริปต์ “คืนค่า” FALSE และหยุดการประมวลผล ดังนั้น หากอ้างอิง OP_RETURN output มาเป็น input โดยไม่ตั้งใจ ธุรกรรมนั้นจะเป็นธุรกรรมที่ไม่ถูกต้องทันที</p>
<h2>ข้อจำกัดของ Transaction Lock Time</h2>
<p>การใช้ค่า lock time ทำให้ผู้ใช้จ่ายสามารถกำหนดเงื่อนไขได้ว่าธุรกรรมจะยังไม่ถูกนำไปรวมอยู่ในบล็อกจนกว่าจะถึงความสูงของบล็อก (block height) หรือเวลาที่กำหนดไว้ แต่ ไม่ได้ ป้องกันไม่ให้เงินก้อนนั้นถูกนำไปใช้จ่ายผ่านธุรกรรมอื่นก่อนหน้านั้น อธิบายด้วยตัวอย่างต่อไปนี้</p>
<p>Alice ลงนามในธุรกรรมที่ใช้จ่ายเอาต์พุตหนึ่งของเธอไปยังที่อยู่ของ Bob และตั้งค่า transaction lock time ไว้ล่วงหน้า 3 เดือน จากนั้น Alice ส่งธุรกรรมนั้นให้ Bob เก็บไว้ เมื่อเป็นเช่นนี้ Alice และ Bob จะทราบว่า:</p>
<ul>
<li>Bob ไม่สามารถกระจาย (broadcast) ธุรกรรมนี้เพื่อรับเงินได้ จนกว่าจะครบกำหนด 3 เดือน  </li>
<li>Bob สามารถกระจายธุรกรรมนี้ได้หลังจากครบ 3 เดือนแล้ว</li>
</ul>
<p>อย่างไรก็ตาม:</p>
<ul>
<li>Alice สามารถสร้างธุรกรรมอีกชุดที่ขัดแย้งกัน โดยใช้จ่ายอินพุตเดียวกันแต่ไม่ใส่ค่า lock time ซึ่งหมายความว่า Alice สามารถใช้จ่าย UTXO เดียวกันได้ก่อนครบ 3 เดือน  </li>
<li>Bob จึงไม่มีหลักประกันใด ๆ ว่า Alice จะไม่ทำเช่นนั้น</li>
</ul>
<p>สิ่งสำคัญคือ ต้องเข้าใจข้อจำกัดของ transaction lock time ให้ชัดเจน เงื่อนไขเพียงอย่างเดียวที่รับประกันได้คือ Bob จะไม่สามารถนำธุรกรรมที่ลงนามล่วงหน้าไปใช้ก่อนครบ 3 เดือนได้ แต่ ไม่มีการรับประกันว่า Bob จะได้รับเงินก้อนนั้นจริง หากต้องการให้ Bob ได้รับเงินอย่างแน่นอน แต่ไม่สามารถใช้จ่ายได้จนกว่าจะครบ 3 เดือน ต้องกำหนดเงื่อนไข timelock ไว้บนตัว UTXO เองในสคริปต์ แทนที่จะกำหนดไว้ในธุรกรรม ซึ่งทำได้ด้วยรูปแบบของ timelock ถัดไปที่เรียกว่า Check Lock Time Verify (CLTV)</p>
<h2>Check Lock Time Verify (OP_CLTV)</h2>
<p>ในเดือนธันวาคม ค.ศ. 2015 ได้มีการเพิ่มรูปแบบของ timelock แบบใหม่เข้ามาในบิตคอยน์ผ่านการอัปเกรดแบบ soft fork โดยอ้างอิงตามข้อกำหนดใน BIP65 มีการเพิ่มคำสั่งสคริปต์ใหม่ที่ชื่อว่า OP_CHECKLOCKTIMEVERIFY (OP_CLTV) เข้าไปในภาษา scripting ของบิตคอยน์ OP_CLTV เป็น timelock ระดับเอาต์พุต (per-output timelock) แตกต่างจาก lock time แบบเดิมซึ่งเป็น timelock ระดับธุรกรรม (per-transaction timelock) ทำให้สามารถกำหนดเงื่อนไขด้านเวลาได้ยืดหยุ่นมากยิ่งขึ้น</p>
<p>กล่าวโดยสรุป เมื่อมีการใส่ opcode OP_CLTV ลงไปในเอาต์พุต เอาต์พุตนั้นจะถูกจำกัดไม่ให้สามารถใช้จ่ายได้จนกว่าจะถึงเวลาที่กำหนดไว้</p>
<p>OP_CLTV ไม่ได้มาแทนที่ lock time แต่ทำหน้าที่จำกัด UTXO เฉพาะรายการ ให้สามารถถูกใช้จ่ายได้ก็ต่อเมื่อธุรกรรมที่จะนำมาใช้จ่ายนั้นมีค่า lock time ที่ตั้งไว้มากกว่าหรือเท่ากับค่าที่กำหนดใน OP_CLTV</p>
<p>คำสั่ง OP_CLTV รับพารามิเตอร์หนึ่งค่าเป็นอินพุต ซึ่งแสดงเป็นตัวเลขในรูปแบบเดียวกับ lock time (อาจเป็นความสูงของบล็อก หรือเวลา Unix epoch) และตามที่คำว่า VERIFY ระบุไว้ OP_CLTV เป็น opcode ประเภทที่หากผลลัพธ์เป็น FALSE จะหยุดการทำงานของสคริปต์ทันที แต่ถ้าผลลัพธ์เป็น TRUE สคริปต์จะทำงานต่อไป</p>
<p>ในการใช้งาน OP_CLTV จะต้องใส่คำสั่งนี้ไว้ใน redeem script ของเอาต์พุตในธุรกรรมที่สร้างเอาต์พุตนั้นขึ้นมา ตัวอย่างเช่น หากอลิซต้องการจ่ายเงินให้บ็อบ ปกติบ็อบอาจยอมรับการจ่ายเงินผ่านสคริปต์ P2SH ลักษณะดังนี้</p>
<pre><code>&lt;Bob's public key&gt; OP_CHECKSIG
</code></pre>
<p>เพื่อทำการล็อกเอาต์พุตไว้จนถึงเวลาที่กำหนด เช่น อีก 3 เดือนนับจากนี้ สคริปต์ P2SH ของบ็อบจะถูกเปลี่ยนเป็นแบบนี้แทน:</p>
<pre><code>&lt;Bob's pubkey&gt; OP_CHECKSIGVERIFY &lt;now + 3 months&gt; OP_CHECKLOCKTIMEVERIFY
</code></pre>
<p>โดยที่ค่า &lt;now {plus} 3 months&gt; คือความสูงบล็อกหรือค่าเวลาที่ประเมินว่าจะตรงกับอีก 3 เดือนหลังจากที่ธุรกรรมถูกขุดลงบล็อก เช่น ความสูงบล็อกปัจจุบัน + 12,960 บล็อก หรือ Unix epoch time ปัจจุบัน + 7,760,000 วินาที</p>
<p>เมื่อบ็อบต้องการใช้จ่าย UTXO นี้ เขาจะสร้างธุรกรรมใหม่ที่อ้างอิง UTXO นั้นเป็นอินพุต จากนั้นใส่ลายเซ็นและกุญแจสาธารณะของเขาในสคริปต์อินพุต พร้อมตั้งค่า lock time ของธุรกรรมให้มากกว่าหรือเท่ากับค่าทิมล็อกที่ระบุไว้ใน OP_CHECKLOCKTIMEVERIFY ที่อลิซกำหนด แล้วจึงกระจายธุรกรรมนี้ไปยังเครือข่ายบิตคอยน์</p>
<p>ธุรกรรมของบ็อบจะถูกประเมินดังนี้: หากค่าพารามิเตอร์ของ OP_CHECKLOCKTIMEVERIFY ที่อลิซตั้งไว้นั้นน้อยกว่าหรือเท่ากับ lock time ของธุรกรรมที่ใช้จ่าย สคริปต์จะทำงานต่อไป (มีผลเหมือนกับไม่มีการทำงาน หรือเหมือน opcode ประเภท OP_NOP) แต่ถ้าค่าดังกล่าวมากกว่า lock time การทำงานของสคริปต์จะหยุดลงทันที และธุรกรรมจะถูกมองว่าไม่ถูกต้อง</p>
<p>กล่าวให้ชัดเจนยิ่งขึ้น BIP65 อธิบายว่า OP_CHECKLOCKTIMEVERIFY จะล้มเหลวและหยุดการทำงานของสคริปต์ทันที หากเกิดเงื่อนไขอย่างใดอย่างหนึ่งต่อไปนี้:</p>
<ul>
<li>สแตกว่างเปล่า  </li>
<li>ค่าบนสุดของสแตกมีค่าน้อยกว่า 0  </li>
<li>ประเภทของ lock time (ความสูงบล็อกเทียบกับ timestamp) ของค่าบนสุดในสแตกไม่ตรงกับประเภทของ lock time ในฟิลด์ lock time ของธุรกรรม  </li>
<li>ค่าบนสุดของสแตกมีค่ามากกว่าค่า lock time ของธุรกรรม  </li>
<li>ฟิลด์ sequence ของอินพุตมีค่าเป็น 0xffffffff</li>
</ul>
<h3>Timelock Conflicts</h3>
<p>OP_CLTV และ locktime นั้นได้ใช้รูปแบบเดียวกันในการอธิบายระยะเวลาในการล๊อค ไม่ว่าจะเป็น block height หรือเมื่อเวลาผ่านไปในหน่วยของวินาทีตาม unix epoch ส่วนที่สำคัญคือ รูปแบบของ timelock จะต้องตรงกับรูปแบบของ OP_CLTV ใน output ทั้งคู่ต้องอ้างอิงแบบเดียวกัน คือจะเป็น block height หรือหน่วยวินาทีก็ตาม</p>
<p>สิ่งนี้หมายความว่าสคริปต์จะไม่มีทางเป็นสคริปต์ได้ ถ้ามีการเรียก OP_CLTV สองครั้งโดยใช้รูแปบบที่แตกต่างกัน คือครั้งนึงเป็น block height และ อีกครั้งหนึ่งเป็น timestamp ซึ่งส่วนนี้เองเป็นข้อผิดพลาดที่เกิดได้ง่ายและมักจะเกิดขึ้น เพราะฉะนั้นควรทดสอบสคริปต์อย่างละเอียดบน testnet หรือใช้เครื่องมือที่ออกแบบมาเพื่อป้องกันปัญหานี้ เช่น Miniscript compiler</p>
<p>ผลอีกอย่างที่สืบเนื่องกันมาจากข้อกำหนดที่ว่าหนึ่งธุรกรรมสามารถใช้ OP_CLTV ได้แบบเดียวเท่านั้น จึงทำให้ในธุรกรรมที่มีหลาย ๆ input โดยแต่ละ input ใช้รูปแบบของ OP_CLTV ที่แตกต่างกัน จะไม่สามารถนำมาสร้างธุรกรรมที่ใช้ input ทั้งสองพร้อมกันได้</p>
<p>โดยหลังจากมีการประมวลผล หากเงื่อนไขของ OP_CLTV เป็นจริง พารามิเตอร์ที่อยู่ก่อนหน้า OP_CLTV จะยังคงอยู่บนสุดของแสตก และอาจจำเป็นต้องเอาออกด้วย OP_DROP เพื่อให้การทำงานของ opcode อื่น ๆ ในสคริปต์ถัดไปได้เป็นไปอย่างถูกต้อง ด้วยเหตุนี้เราจึงมักจะเห็น OP_CHECKLOCKTIMEVERIFY จะถูกใช้ตามด้วย OP_DROP ในสคริปต์ต่าง ๆ เช่นเดียวกับ OP_CSV (จะอธิบายต่อในหัวข้อ Relative timelock) OP_CLTV มีพฤติกรรมที่แตกต่างจาก opcode ในกลุ่ม CHECKVERIFY ตัวอื่น ๆ ตรงที่ไม่ได้นำข้อมูลออกจากสแตก แต่ทั้งนี้ทั้งนั้นมันเป็นเพราะ ซอฟต์ฟอร์กที่เพิ่ม opcode เหล่านี้เข้าไป ได้อาศัยการนิยามใหม่ของ opcode ที่เคยเป็น no-operation (NOP) ซึ่งเดิมไม่ได้ลบข้อมูลออกจากสแตก และพฤติกรรมของ opcode NOP เดิมเหล่านั้นจำเป็นต้องถูกคงไว้เพื่อความเข้ากันได้</p>
<p>และด้วยการใช้ lock time ร่วมกันกับ OP_CLTV นั้นจะทำให้ในส่วนที่เราได้อธิบายไว้ในหัวข้อ ข้อจำกัดของ Transaction Lock Time เปลี่ยนแปลงไปเล็กน้อย เนื่องจากอลิสสามารถส่งบิตคอยน์ของเธอได้ทันที โดยกำหนดให้เงินถูกล๊อคไว้ที่กุญแจของบ๊อบตั้งแต่วินาทีนั้น อลิสจะไม่สามารถใช้เงินก้อนนี้ซ้ำได้อีก และในขณะเดียวกันบ๊อบก็ยังคงต้องรอ 3 เดือนก่อนตามที่กำหนดไว้</p>
<p>อยากตัวอย่างดังกล่าว ผู้อ่านทุกท่านคงจะได้เห็นแล้วว่าการนำ timelock เข้ามาไว้ในภาษา scripting ทำให้ OP_CLTV เปิดโอกาสให้เราสามารถพัฒนาสคริปต์ที่ซับซ้อนและน่าสนใจได้หลากหลายรูปแบบ</p>
<p>ซึ่งมาตรฐานดังกล่าวถูกกำหนดไว้ใน BIP65 (OP_CHECKLOCKTIMEVERIFY)</p>
<h3>Relative Timelocks</h3>
<p>Lock time และ OP_CLTV ทั้งคู่เป็น absolute timelocks กล่าวคือ เป็นการกำหนดเวลาที่ตายตัว ส่วนกลไก timelock อีกสองรูปแบบที่เราจะพิจรณาต่อไปนี้เป็น relative timelocks ซึ่งเป็นการกำหนดเงื่อนไขการใช้จ่าย output โดยอ้างอิงกับกับระยะเวลาตั้งแต่ธุกรรมก่อนหน้าได้รับการยืนยันและถูกบันทึกลงบนบล๊อคเชน</p>
<p>relative timelocks นั้นสามารถช่วยให้เราสามารถกำหนดข้อจำกัดในด้านของเวลาให้กำธุรกรรมนั้น ๆ โดยอ้างอิงจากเวลาที่ธุรกรรมก่อนหน้าถูกยืนยัน หรือกล่าวได้ว่าเวลาของธุรกรรมนั้นจะไม่ถูกนับจนกว่า UTXO ก่อนหน้าจะปรากฎบนบล๊อคเชน ฟังก์ชันนี้มีความสำคัญอย่างยิ่งกับการทำ bidirectional state channels และ Lightning Network (LN) ซึ่งเราจะได้อ่านเรื่องนี้เพิ่มเติมในหัวข้อ state channels </p>
<p>Relative timelocks นั้นได้มีการถูกนำมาใช้งานทั้งในระดับธุรกรรมและในระดับของสคริปต์ เช่นเดียวกันกับ absolute timelocks โดยในระดับธุรกรรมนั้นจะถูกใช้ในฟิลด์ของ sequence ซึ่งเป็นฟิลด์ที่ต้องอยู่ในส่วนของ input ในทุกธุรกรรมของบิตคอยน์ ส่วนในระดับของสคริปต์นั้นจะถูกเรียกใช้ผ่าน opcode ที่ชื่อ OP_CHECKSEQUENCEVERIFY (OP_CSV)</p>
<p>Relative timelocks ถูกนำมาใช้งานตามข้อกำหนดที่ระบุไว้ใน BIP68: Relative Lock-Time Using Consensus-Enforced Sequence Numbers และ BIP112: OP_CHECKSEQUENCEVERIFY</p>
<p>ทั้ง BIP68 และ BIP112 ถูกเปิดใช้งานในเดือนพฤษภาคม ปี 2016 ในรูปแบบของการอัปเกรดกฎ consensus แบบ soft fork</p>
<h3>Relative Timelocks with OP_CSV</h3>
<p>เช่นเดียวกันกับ OP_CLTV และ lock time ในระบบของบิตคอยน์มี opcode ในระดัสคริปต์สำหรับ relative timelock ที่อาศัยค่า sequence ภายในสคริปต์ด้วย opcode นั้นคือ OP_CHECKSEQUENCEVERIFY ซึ่งมักเรียกย่อว่า OP_CSV</p>
<p>เมื่อ opcode อย่าง OP_CSV ถูกประเมิณค่าในสคริปต์ของ UTXO จะอนุญาตให้ใช้จ่ายได้เฉพาะในธุรกรรมที่ค่า sequence ของ input มีค่ามากกว่าหรือเท่ากับพารามิเตอร์ของ OP_CSV เท่านั้น โดยสรุปแล้ว กลไกนี้จะจำกัดการใช้จ่าย UTXO จนกว่าจะมีจำนวนบล็อกหรือเวลาหน่วยวินาทีผ่านไปตามที่กำหนดไว้ นับจากเวลาที่ UTXO นั้นถูกขุดและบันทึกลงในบล็อกเชน</p>
<p>และเช่นเดียวกันกับ CLTV ค่าใน OP_CSV จะต้องมีรูปแบบที่สอดคล้องกับค่า sequence ที่ใช้คู่กัน โดยหาก OP_CSV ถูกกำหนดไว้เป็นจำนวนบล๊อก ค่า sequence ก็จำเป็นต้องอยู่ในรูปแบบของจำนวนบล๊อก และหาก OP_CSV ถูกกำหนดค่าไว้เป็นวินาที ค่า sequence ก็จำเป็นต้องเป็นค่าวินาทีเช่นกัน</p>
<blockquote>
<p>คำเตือน: สคริปต์ที่มีการเรียกใช้ OP_CSV หลายครั้ง จะต้องใช้รูปแบบเดียวกันทั้งหมด คือจะเป็นรูปแบบของวินาที ก็ต้องเป็นวินาทีทั้งหมด ถ้าเป็นจำนวนบล๊อกก็ต้องเป็นจำนวนบล๊อกทั้งหมดเท่านั้น การผสมทั้งสองรูปแบบเข้าด้วยกันจะทำให้สคริปต์นั้นถูกมองว่าไม่ถูกต้อง และไม่สามารถถูกใช้จ่ายได้ตลอดไป ซึ่งเป็นปัญหาเดียวกันกับ OP_CLTV ในหัวข้อ Timelock Conflicts แต่อย่างไรก็ตาม OP_CSV อณุญาติให้มี input ที่ถูกต้องได้หลายแบบภายในธุรกรรมเดียวกัน ดังนั้นปัญหาของการปฎิสัมพันธ์ข้าม input ที่เกิดขึ้นกับ OP_CLTV จึงจะไม่เกิดขึ้นกับ OP_CSV</p>
</blockquote>
<p>relative timelock ด้วย OP_CSV นั้นมีประโยชน์อย่างยิ่งในกรณีที่มีการสร้างและลงนามธุรกรรมหลายรายการที่เชื่อมต่อกันเป็นลูกโซ่ แต่ยังไม่ถูกเผยแพร่ไปยังเครือข่าย นั่นคือถูกเก็บไว้นอกบล็อกเชน ธุรกรรมลูกเหล่านั้นจะไม่สามารถถูกใช้งานได้จนกว่าธุรกรรมแม่จะถูกเผยแพร่ ถูกขุดรวมเข้าไปในบล็อกเชน และมีอายุผ่านไปตามระยะเวลาที่ระบุไว้ใน relative timelock การใช้งานลักษณะนี้แสดงตัวอย่างไว้ใน state channels และ lightning network</p>
<p>OP_CSV ถูกกำหนดรายละเอียดไว้ใน BIP112 และ CHECKSEQUENCEVERIFY</p>
<h3>สคริปต์ที่มีการควบคุมลำดับการทำงาน (เงื่อนไขหลายเงื่อนไข)</h3>
<p>หนึ่งในสิ่งที่น่าสนใจ และคุณสมบัติที่ทรงพลังมาก ๆ ของ Bitcoin script คือการควบคุมลำดับการทำงาน (Flow control) หรือที่เลือกว่าเงื่อนไขหลายเงื่อนไข คุณน่าจะคุ้นเคยกับแนวคิดของ flow control จากภาษาโปรแกรมต่าง ๆ ที่ใช้โครงสร้าง IF…THEN…ELSE เงื่อนไขใน Bitcoin Script แม้จะมีรูปแบบแตกต่างออกไปเล็กน้อย แต่โดยรวมแล้วเป็นโครงสร้างเดียวกัน</p>
<p>ในระดับพื้นฐาน opcode นั้นมีเงื่อนไขของบิตคอยน์ที่ช่วยให้เราสามารถสร้างสคริปต์ที่มีวิธีปลดล็อกได้สองแบบ ขึ้นอยู่กับผลลัพธ์ TRUE/FALSE จากการประเมินเงื่อนไขในทางตรรกะ ตัวอย่างเช่น หากค่า x เป็น TRUE เส้นทางของโค้ดที่ถูกประมวลผลคือ A และหากไม่เป็นเช่นนั้น (ELSE) เส้นทางของโค้ดคือเส้นทาง B</p>
<p>นอกจากนี้ นิพจน์ต่าง  ๆ ภายในเงื่อนไขของบิตคอยน์ยังสามารถทับซ้อนกันได้อย่างไม่จำกัด หรือกล่าวได้ว่าภายในเงื่อนไขใด ๆ สามารถอีกอีกเงื่อนไขอยู่ด้านในได้ สคริปต์ของบิตคอยน์ที่มีการควบคุมลำดับการทำงาน จึงสามารถใช้สร้างสคริปต์ที่ซับซ้อนมาก โดยมีเส้นทางการประมวลผลที่เป็นไปได้หลายร้อยแบบ แม้จะไม่มีข้อจำกัดด้านระดับความลึกของการซ้อนเงื่อนไขแต่กฎฉันทามติ ก็ได้กำหนดข้อจำกัดไว้ที่ขนาดสูงสุดของสคริปต์ในหน่วยไบต์</p>
<p>บิตคอยน์ได้ใช้การควบคุมระดับการทำงานผ่าน opcode  อย่าง OP_IF OP_ELSE OP_ENDIF และ OP_NOTIF นอกจากนี้นิพจน์เงื่อนไขยังสามารถมีตัวดำเนินการเชิงตระกะอย่าง OP_BOOLAND, OP_BOOLOR และ OP_NOT ได้ด้วย</p>
<p>ในภาษาโปรแกรมแบบดั้งเดิม (เชิงกระบวนการ) ส่วนใหญ่ การควบคุมลำดับการทำงานจะมีลักษณะดังนี้:<br>pseudocode ของการควบคุมลำดับการทำงานในภาษาโปรแกรมส่วนใหญ่</p>
<pre><code>if (condition):
  code to run when condition is true
else:
  code to run when condition is false
endif
code to run in either case
</code></pre>
<p>แต่ในภาษาที่อิงกับสแตก (stack-based) อย่าง Bitcoin Script เงื่อนไขเชิงตรรกะจะมาก่อนคำสั่ง IF ซึ่งทำให้โครงสร้างดูเหมือน “กลับด้าน”:<br>การควบคุมลำดับการทำงานใน Bitcoin Script</p>
<pre><code>condition
IF
  code to run when condition is true
OP_ELSE
  code to run when condition is false
OP_ENDIF
code to run in either case
</code></pre>
<p>เมื่ออ่าน Bitcoin Script ให้จำไว้ว่า เงื่อนไขที่ถูกนำมาประเมินค่าจะมาก่อน opcode IF เสมอ</p>
<h2>เงื่อนไขแบบมี verify opcode</h2>
<p>เงื่อนไขในอีกรูปแบบหนึ่งของบิตคอยน์สคริปต์ คือ opcode ใด ๆ ที่ลงท้ายด้วย VERIFY โดยคำต่อท้าย VERIFY หมายความว่า หากเงื่อนไขที่ถูกประเมินค่าไม่เป็น TRUE การทำงานของสคริปต์จะถูกยุติทันที และธุรกรรมจะถูกพิจารณาว่าไม่ถูกต้อง</p>
<p>ซึ่งแตกต่างจากการทำงานของ IF ซึ่งได้เปิดโอกาสให้มีเส้นทางการทำงานหลายรูปแบบ opcode ที่ลงท้ายด้วย VERIFY จะทำหน้าที่เป็น guard clause หรือก็คือตรรกะที่ต้องประเมินผลให้เป็นจริง เพื่อให้การดำเนินการของโปรแกรมสามารถดำเนินต่อไปได้</p>
<p>ตัวอย่างเช่น ในสคริปต์ต่อไปนี้ได้มีการกำหนดให้ต้องมีทั้งลายเซ็นของบ๊อบและค่า preimage ที่เมื่อแฮชแล้วต้องได้ค่าตามที่กำหนด ทั้งสองเงื่อนไขนี้เองต้องถูกทำให้เป็นจริงจึงจะสามารถปลดล๊อคได้:</p>
<p>สคริปต์ที่มี OP_EQUALVERIFY เป็น guard clause</p>
<pre><code>OP_HASH160 &lt;expected hash&gt; OP_EQUALVERIFY &lt;Bob's Pubkey&gt; OP_CHECKSIG
</code></pre>
<p>เพื่อที่จะใช้จ่ายเงินก้อนนี้บ๊อบจำเป็นต้องแสดง preimage และลายเซ็นที่ถูกต้องเท่านั้น</p>
<pre><code>&lt;Bob's Sig&gt; &lt;hash pre-image&gt;
</code></pre>
<p>หากไม่ได้นำเสนอ preimage มาก่อน Bob จะไม่สามารถไปถึงส่วนของสคริปต์ที่ตรวจสอบลายเซ็นของเขาได้</p>
<p>สคริปต์นี้สามารถเขียนใหม่โดยใช้ OP_IF แทนได้ดังนี้:</p>
<p>สคริปต์ที่มี IF ทำหน้าที่เป็น guard clause</p>
<pre><code>OP_HASH160 &lt;expected hash&gt; OP_EQUAL
OP_IF
   &lt;Bob's Pubkey&gt; OP_CHECKSIG
OP_ENDIF
</code></pre>
<p>ข้อมูลยืนยันตัวตนของ Bob ยังคงเหมือนเดิม:</p>
<p>การทำให้สคริปต์ข้างต้นเป็นจริง</p>
<pre><code>&lt;Bob's Sig&gt; &lt;hash pre-image&gt;
</code></pre>
<p>สคริปต์ที่มีการใช้ OP_IF ให้ผลลัพธ์เหมือนกับการใช้ opcode ที่มีคำต่อท้าย VERIFY โดยทั้งสองแบบทำหน้าที่เป็น guard clause เหมือนกัน แต่อย่างไรก็ตาม โครงสร้างที่ใช้ VERIFY ใช้จะมีประสิทธิภาพมากกว่า เนื่องจากมีการใช้ opcode น้อยกว่าสองตัว</p>
<p>แล้วเมื่อไหร่ที่เราควรใช้ VERIFY แล้วเมื่อไหร่ที่เราควรใช้ OP_IF ? หากสิ่งที่เราต้องการทำมีเพียงการกำหนดเงื่อนไขเบื้องต้น (guard clause) VERIFY จะเป็นทางเลือกที่ดีกว่า แต่หากเราต้องการให้มีเส้นทางการทำงานมากกว่าหนึ่งแบบ (flow control) เราจำเป็นต้องใช้โครงสร้างควบคุมลำดับการทำงานแบบ OP_IF...OP_ELSE แทน</p>
<h3>การใช้ Flow control ในสคริปต์</h3>
<p>วิธีใช้การควบคุมลำดับการทำงานโดยทั่วไปภายในสคริปต์ของบิตคอยน์นั้นมักเป็นการสร้างสคริปต์ที่เปิดโอกาสให้สามารถสร้างเส้นทางการทำงานของสคริปต์ได้หลายแบบ โดยแต่ละเส้นทางก็มีวิธีการในการใช้จ่าย UTXO ที่แตกต่างกัน </p>
<p>ลองมาพิจรณาจากตัวยอ่างง่าย ๆ นี้กัน โดยเรามีผู้ที่สามารถลงนามในธุรกรรมนี้ได้สองคน นั้นคืออลิซและบ๊อบโดยขอแค่เป็นใครคนใดคนหนึ่งในสองคนนี้ก็สามารถใช้จ่ายได้ โดยจะเขียนออกมาเป็น 1-of-2 multisig แต่เราจะทำสิ่งเดียวกันนี้โดยใช้คำสั่ง OP_IF:</p>
<pre><code>OP_IF
 &lt;Alice's Pubkey&gt;
OP_ELSE
 &lt;Bob's Pubkey&gt;
OP_ENDIF
OP_CHECKSIG
</code></pre>
<p>ถ้ามองสคริปต์ในตัวอย่างแล้วคุณอาจสงสัยว่า แล้วเงื่อนไขอยู่ตรงไหน? ทำไมไม่มีอะไรอยู่ก่อนคำสั่ง IF เลย</p>
<p>ส่วนเหตุผลนั้นก็เป็นเพราะว่าเงื่อนไขนั้นไม่ได้เป็นส่วนหนึ่งของสคริปต์ แต่จะถูกส่งมาในตอนที่มีการใช้จ่ายแทน ซึ่งทำให้ Alice และ Bob สามารถ “เลือก” เส้นทางการทำงานที่ต้องการได้:</p>
<pre><code>&lt;Alice's Sig&gt; OP_TRUE
</code></pre>
<p>OP_TRUE ที่อยู่ท้ายสุดทำหน้าที่เป็นเงื่อนไข (TRUE) ซึ่งจะทำให้คำสั่ง OP_IF เลือกเส้นทางการใช้จ่ายแรก โดยในเงื่อนไขนี้จะทำให้ public key ที่อลิซมีลายเซ็นตรงกันถูกนำขึ้นสแตก Opcode OP_TRUE หรือที่เรียกว่า OP_1 จะใส่ค่าเลข 1 ลงบนสแตก</p>
<p>ส่วนสำหรับบ๊อบหากต้องการใช้จ่าย UTXO นี้ เขาจะต้องเลือกเส้นทางการทำงานที่สองของ OP_IF โดยให้ค่า FALSE แทน Opcode OP_FALSE หรือที่เรียกว่า OP_0 จะใส่ค่าเป็นอาร์เรย์ไบต์ว่างลงบนสแตก:</p>
<pre><code>&lt;Bob's Sig&gt; OP_FALSE
</code></pre>
<p>input สคริปต์ของ Bob จะทำให้คำสั่ง OP_IF ไปสคริปต์ในส่วนที่สอง (OP_ELSE) ซึ่งต้องการลายเซ็นของบ๊อบแทน</p>
<p>เนื่องจากคำสั่ง OP_IF สามารถซ้อนกันได้ เราจึงสามารถสร้าง “เขาวงกต” ของเส้นทางการทำงานได้ โดย input script จะทำหน้าที่เสมือน “แผนที่” ที่ใช้เลือกว่าจริง ๆ แล้วเส้นทางการทำงานใดจะถูกนำมาใช้</p>
<pre><code>OP_IF
  subscript A
OP_ELSE
  OP_IF
    subscript B
  OP_ELSE
    subscript C
  OP_ENDIF
OP_ENDIF
</code></pre>
<p>ในตัวอย่างนี้จะมีเส้นทางการทำงานทั้งหมดสามเส้นทางด้วยกัน (subscript A, subscript B, subscript C) โดย input script จะเป็นผู้กำหนดเส้นทางตามรูปแบบของลำดับค่า TRUE หรือ FALSE อย่างเช่น หากต้องการเลือกเส้นทาง subscript B input script จะต้องลงท้ายด้วย OP_1 OP_0 (TRUE, FALSE) ค่าเหล่านี้จะถูกนำขึ้นแสตกทำให้ค่า FALSE อยู่จุดบนสุดของแสตก ซึ่ง OP_IF ชั้นนอกจะทำการดึงค่า FALSE ออกมาและนำส่วน OP_ELSE แรก จากนั้นค่า TRUE จะเลื่อนขึ้นมาอยู่บนสุดของแสตก และจะถูกประเมิณโดย OP_IF ชั้นใน ซึ่งจะเลือกเส้นทางการทำงานแบบ B</p>
<p>ด้วยโครงสร้างของสคริปต์แบบนี้ เราจะสามารถสร้าง redeem script ที่มีเส้นทางการทำงานได้หลายสิบ หรือหลายร้อยเส้นทาง โดยแต่ละเส้นจะเสนอวิธีการใช้จ่าย UTXO ที่แตกต่างกันออกไป เมื่อเราต้องการใช้จ่าย เราจะสร้าง input script ที่นำทางไปตามเส้นทางการทำงานนั้น ๆ ด้วยการใส่ค่า TRUE และ FALSE ที่เหมาะสมลงบนสแตกในแต่ละจุดของการควบคุมลำดับการทำงาน</p>
<h3>ตัวอย่างสคริปต์แบบซับซ้อน</h3>
<p>ในส่วนนี้เราจะนำแนวคิดทั้งหมดที่ได้เรียนรู้จากบทนี้มารวมกันไว้ในตัวอย่างเดียว</p>
<p>สมมุติว่าโมฮัมเหม็ด ซึ่งเป็นเจ้าของบริษัทนำเข้าและส่งออกในดูไบ เขาต้องการสร้างบัญชีเงินทุนของบริษัทที่มีกฏเกณฑ์ที่ยืดหยุ่น และแผนของเขาคือการออกแบบมาเพื่อให้ใช้ระดับในการอณุญาติที่แตกต่างกันไปตามเงื่อนไขของ timelock และผู้เข้าร่วมโครงสร้าง multisig นี้จะประกอบไปด้วย โมฮัมเหม็ด หุ้นส่วนอย่าง ซาอีดและไซรา และสุดท้ายเป็นทนายความของบริษัท โดยหุ้นส่วนทั้งสามตัดสินใจโดยใช้หลักเสียงข้างมาก ดังนั้นจะต้องมีอย่างน้อยสองในสามคนเห็นชอบสำหรับกรณีต่าง ๆ แต่อย่างไรก็ตาม ในกรณีที่เกิดปัญหากับ key ของพวกเขา พวกเขาเพียงต้องการให้ทนายความสามารถกู้คืนเงินได้ โดยใช้ลายเซ็นของหุ้นส่วนคนใดคนหนึ่งจากสามคนร่วมด้วย สุดท้ายแล้วหากหุ้นส่วนทั้งหมดไม่สามารถติดต่อได้หรือไม่สามารถปฏิบัติน่าที่ได้เป็นระยะเวลาหนึ่ง พวกเขาต้องการให้ทนายความสามารถจัดการบัญชีได้โดยตรงหลังจากที่เขาได้รับสิทธ์เข้าถึงบันทึกธุรกรรมของบัญชีเงินทุนนี้ได้</p>
<p>สคริปต์ไถ่ถอนแบบ multisignature ที่เปลี่ยนแปลงได้ร่วมกันกับ timelock คือสิ่งที่โมฮัมเหม็ดต้องออกแบบขึ้นมาเพื่อให้บรรลุเป้าหมายนี้</p>
<p>ตัวอย่างที่ 1: การใช้ Multisignature ร่วมกับ timelock</p>
<pre><code>01  OP_IF
02    OP_IF
03      2
04    OP_ELSE
05      &lt;30 days&gt; OP_CHECKSEQUENCEVERIFY OP_DROP
06      &lt;Lawyer's Pubkey&gt; OP_CHECKSIGVERIFY
07      1
08    OP_ENDIF
09    &lt;Mohammed's Pubkey&gt; &lt;Saeed's Pubkey&gt; &lt;Zaira's Pubkey&gt; 3 OP_CHECKMULTISIG
10  OP_ELSE
11    &lt;90 days&gt; OP_CHECKSEQUENCEVERIFY OP_DROP
12    &lt;Lawyer's Pubkey&gt; OP_CHECKSIG
13  OP_ENDIF
</code></pre>
<p>สคริปต์ของโมฮัมเหม็ดใช้โครงสร้างควบคุมลำดับการทำงานแบบ OP_IF…OP_ELSE ที่ซ้อนกันเพื่อสร้างเส้นทางการทำงานทั้งหมดสามเส้นทาง</p>
<p>ในเส้นทางการทำงานเส้นทางแรก สคริปต์นี้จะทำงานเหมือน multisig 2-of-3 ธรรมดา ๆ ระหว่างหุ้นส่วนทั้งสาม โดยเส้นทางการทำงานนี้ประกอบด้วยบรรทัดที่ 3 และบรรทัดที่ 9 โดยบรรทัดที่ 3 กำหนดโดย quorum ของ multisig เป็น 2 (2-of-3) เส้นทางการทำงานนี้สามารถถูกเลือกได้โดยการใส่ OP_TRUE OP_TRUE ต่อท้ายใน input script: </p>
<p>ข้อมูลการใช้จ่ายสำหรับเส้นทางการทำงานแรก (multisig แบบ 2-of-3)</p>
<pre><code>OP_0 &lt;Mohammed's Sig&gt; &lt;Zaira's Sig&gt; OP_TRUE OP_TRUE
</code></pre>
<blockquote>
<p>TIP: OP_0 ที่อยู่ตอนต้นของ input script นี้มีสาเหตุมาจากความผิดปกติของ OP_CHECKMULTISIG ซึ่งจะดึงค่าออกจากแสตกมากกว่าที่ควรหนึ่งค่า ค่าที่ถูกดึงออกมาเกินนี้จะถูกละเลยโดย OP_CHECKMULTISIG แต่มันจำเป็นต้องมีอยู่ มิฉะนั้นสคริปค์จะล้มเหลว การใส่อาร์เรย์ไบต์ว่างด้วย OP_0 จึงเป็นวิธีแก้ปัญหาชั่วคราวสำหรับความผิดปกตินี้ ตามที่อธิบายไว้ในหัวข้อ An Oddity in CHECKMULTISIG Execution</p>
</blockquote>
<p>ในเส้นทางการทำงานเส้นทางที่สองสามารถถูกใช้งานได้ก็ต่อเมื่อผ่านไปแล้ว 30 วันหลังจากที่ UTXO ถูกสร้างขึ้น และเมื่อถึงเวลานั้น สคริปต์จะต้องการลายเซ็นของทนายความ และลายเซ็นจากหนึ่งในหุ้นส่วน (multisig 1 of 3) โดยเงื่อนไขนี้ถูกกำหนดโดยบรรทัดที่ 7 ซึ่งตั้งค่า quorum ของ multisigไว้ที่ 1 และเพื่อเลือกใช้เส้นทางการทำงานนี้ input script จะต้องลงท้ายด้วย OP_FALSE OP_TRUE:</p>
<p>ข้อมูลการใช้จ่ายสำหรับเส้นทางการทำงานที่สอง (ทนายความ + multisig แบบ 1-of-3)</p>
<pre><code>OP_0 &lt;Saeed's Sig&gt; &lt;Lawer's Sig&gt; OP_FALSE OP_TRUE
</code></pre>
<blockquote>
<p>TIP: OP_FALSE จะถูกใส่ลงบนสแตกก่อน จากนั้น OP_TRUE จะถูกใส่ลงไปอยู่ด้านบนของมัน ดังนั้นค่า TRUE จึงถูกดึง (pop) ออกมาก่อนโดย opcode OP_IF ตัวแรก</p>
</blockquote>
<p>สุดท้าย เส้นทางการทำงานที่สามอนุญาตให้ทนายความสามารถใช้จ่ายเงินได้เพียงคนเดียว แต่จะทำได้ก็ต่อเมื่อผ่านไปแล้ว 90 วันเท่านั้น เพื่อเลือกเส้นทางการทำงานนี้  Input script จะต้องลงท้ายด้วย OP_FALSE:</p>
<p>ข้อมูลการใช้จ่ายสำหรับเส้นทางการทำงานที่สาม (ทนายความ)</p>
<pre><code>&lt;Lawyer's Sig&gt; OP_FALSE
</code></pre>
<p>ลองจำลองการรันสคริปต์บนกระดาษเพื่อดูว่ามันทำงานอย่างไรบนสแตก</p>
<h2>Pay to witness public key hash (P2WPKH)</h2>
<p>เริ่มจากดูตัวอย่างของ output script แบบ P2PKH กันก่อน:</p>
<p>ตัวอย่าง output script P2PKH</p>
<pre><code>OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7
OP_EQUALVERIFY OP_CHECKSIG
</code></pre>
<p>เมื่อใช้ segregated witness อลิซจะสร้างสคริปต์แบบ P2WPKH หากสคริปต์นั้นผูกกับ public key เดียวกัน ก็จะมีหน้าตาประมาณนี้: </p>
<pre><code>0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7
</code></pre>
<p>อย่างที่เห็นว่า output script แบบ P2WPKH นั้นเรียบง่ายกว่าสคริปต์แบบ P2PKH เป็นอย่างมากถ้าเราเอามาเปรียบเทียบกัน มันประกอบด้วยค่าเพียงแค่สองค่าเท่านั้นที่จะถูกใส่ลงในแสตกสำหรับการประมวลผลสคริปต์ โดยสำหรับไคล์เอนต์บิตคอยน์รุ่นเก่า ๆ (ที่ยังไม่รองรับ segwit) การใส่สองค่านี้จะดูเหมือน output ที่ใครก็สามารถใช้จ่ายได้ แต่สำหรับไคลเอนต์รุ่นใหม่ที่รองรับ segwit ตัวเลขตัวแรก (0) จะถูกตีความเป็นหมายเลขเวอร์ชัน (witness version) และส่วนที่สอง (ขนาด 20 ไบต์) คือ witness program โดย witness program ขนาด 20 ไบต์นี้ก็คือแฮชของ public key เช่นเดียวกับในสคริปต์แบบ P2PKH</p>
<p>ตอนนี้เรามาดูธุรกรรมที่เกี่ยวข้องกันซึ่งบ๊อบใช้เพื่อจ่าย output นี้กัน โดยสำหรับสคริปต์แบบ lagacy นั้น ธุรกรรมที่ใช้จ่ายนั้นจะต้องใส่ลายเซ็นไว้ในส่วน input ของธุรกรรม:</p>
<pre><code>[...]
"vin" : [
  "txid": "abcdef12345...",
  "vout": 0,
  "scriptSig": “&lt;Bob’s scriptSig&gt;”,
]
[...]
</code></pre>
<p>แต่อย่างไรก็ตาม เพื่อใช้จ่าย output แบบ P2WPKH ธุรกรรมจะไม่มีลายเซ็นอยู่ในส่วนของ input เลย แต่แทนที่จะเป็นเช่นนั้น ธุรกรรมของบ๊อบจะมี input script ที่ว่างเปล่า และจะใส่ข้อมูลไว้ในส่วนของ witness แทน:</p>
<pre><code>[...]
"vin" : [
  "txid": "abcdef12345...",
  "vout": 0,
  "scriptSig": “”,
]
[...]
“witness”: “&lt;Bob’s witness structure&gt;”
[...]
</code></pre>
<h2>การสร้างกระเป๋าเงินแบบ P2WPKH</h2>
<p>สิ่งที่สำคัญที่สุดและควรจะทราบไว้คือ witness program แบบ P2WPKH ควรจะถูกสร้างขึ้นโดยผู้รับเงินเท่านั้น และไม่ควรแปลงมาจาก public key ของผู้ที่รู้จักอยู่แล้ว เพราะสคริปต์ของ P2PKH หรือแม้แต่จาก address ใด ๆ นั้นผู้ใช้จ่ายไม่มีทางรู้ได้เลยว่ากระเป๋าเงินของผู้รับนั้นสามารถสร้างธุรกรรมแบบ segwit และสามารถใช้จ่าย output แบบ P2WPKH ได้หรือไม่</p>
<p>นอกจากนี้ output แบบ P2WPKH นั้นจะต้องถูกสร้างจาก hash ของ compressed public key เท่านั้น เพราะว่า public key แบบ uncompressed นั้นถือว่าไม่อยู่ในมาตรฐานของ segwit และอาจจะถูกปิดการใช้งานอย่างชัดเจนใน soft fork ที่เกิดขึ้นภายหลัง หาก hash ที่ใช้ได้ใน P2WPKH มาจาก public key แบบ uncompressed จะทำให้ output ที่เกิดขึ้นนั้นไม่สามารถจ่ายออกได้อีก และจะทำให้คุณอาจที่จะสูญเสียเงินนั้นไปตลอดกาล นั่นจึงเป็นเหตุผลที่ว่าทำไม output แบบ P2WPKH นั้นถึงควรถูกสร้างโดยแอปกระเป๋าเงินของผู้รับผ่านการสร้าง compress public key จาก private key ของเขาเอง</p>
<blockquote>
<p>Warning: P2WPKH ควรถูกสร้างโดยผู้รับ โดยการนำ public key แบบ compressed มาแปลงเป็น P2WPKH hash เท่านั้น ไม่ควรให้ผู้ใช้หรือบุคคลอื่นใด แปลง P2PKH script, Address, หรือ public key แบบ uncompressed ให้กลายเป็น P2WPKH witness script ซึ่งเป็นเรื่องปกติอยู่แล้วที่ผู้ใช้ควรส่งเงินให้ผู้รับ ในรูปแบบที่ผู้รับได้ระบุไว้เท่านั้น</p>
</blockquote>
<h3>Pay to witness script hash (P2WSH)</h3>
<p>witness program ประเภทนี้จะสอดคล้องกันกับ P2SH ที่เราได้ผ่านกันมาก่อนหน้านี้ ในหัวข้อของ ในหัวข้อ Pay to Script Hash และในตัวอย่างที่บริษัทของนายโมฮัมเหม็ดได้ใช้สคริปต์แบบ P2SH เพื่อทำ multisig โดยสคริปต์ที่ถูกเข้ารหัสของเขาจะมีหน้าตาแบบนี้</p>
<pre><code>OP_HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e OP_EQUAL
</code></pre>
<p>สคริปต์ P2SH นี้อ้างอิงไปยังแฮชของ redeem script ซึ่งได้กำหนดเงื่อนไขการใช้จ่ายแบบ multisig 2-of-3 เอาไว้เพื่อใช้ในการปลดล๊อคเงินทุน การจะใช้จ่าย output นี้ บริษัทของโมฮัมเหม็ดจะต้องแสดง redeem script ซึ่งมีค่าแฮชตรงกันกับ script hash ที่อยู่ภายใน P2SH output พร้อมกับลายเซ็นที่จำเป็นตามเงื่อนไขของ redeem script นั้น โดยใส่ทั้งหมดไว้ภายใน transaction input:</p>
<pre><code>[...]
"vin" : [
  "txid": "abcdef12345...",
  "vout": 0,
  "scriptSig": “&lt;SigA&gt; &lt;SigB&gt; &lt;2 PubA PubB PubC PubD PubE 5 OP_CHECKMULTISIG&gt;”,
]
</code></pre>
<p>ทีนี้เรามาดูกันดีกว่าว่าตัวอย่างที่เกริ่นมาข้างต้นนี้จะถูกอัปเกรดเป็น segwit v0 ได้อย่างไร หากลูกค้าของนายโมฮัมเหม็ดใช้กระเป๋าเงินที่รับรอง segwit พวกเขาจะทำการชำระเงินได้โดยการสร้าง P2WSH output ซึ่งมีลักษณะดังนี้:</p>
<pre><code>0 a9b7b38d972cabc7961dbfbcb841ad4508d133c47ba87457b4a0e8aae86dbb89
</code></pre>
<p>เช่นเดียวกันกับตัวอย่างของ P2WPKH คุณจะเห็นได้ว่าสคริปต์แบบ segregated witness ที่ทำงานในแบบเดียวกันนั้นเรียบง่ายกว่า และนอกจากนี้ยังช่วยลด overhead ของเทมเพลตที่พบในสคริปต์ P2SH ลงไปได้มากอีกด้วย เนื่องจากสคริปต์ของ segreted witness นั้นประกอบด้วยค่าเพียง 2 ค่าที่ถูกใส่ลงในแสตก นั่นคือ witness version(0) และค่าแฮชของ SHA256 ขนาด 32 ไบต์ของ witness script (ในบางครั้งอาจถูกเรียกว่า witness program)</p>
<blockquote>
<p>TIP: ในขณะที่ P2SH ใช้แฮชแบบ RIPEMD160(SHA256(script)) ขนาด 20 ไบต์ แต่ witness program ของ P2WSH จะใช้แฮชแบบ SHA256(script) ขนาด 32 ไบต์ ความแตกต่างในการเลือกอัลกอริทึมในการแฮชนี้ถูกออกแบบมาเพื่อเพิ่มความแข็งแกร่งในด้านความปลอดภัยให้กับ P2WSH แต่ในบางกรณีก็มีบ้างที่ความปลอดภัยระดับ 128 บิตใน P2WSH นั้นมีความปลอดภัยเท่ากับ 80 บิตใน P2SH ดูรายละเอียดเพิ่มเติมได้ที่ P2SH collision attack</p>
</blockquote>
<p>หลังจากนั้นบริษัทของนายโมฮัมเหม็ดก็สามารถใช้จ่าย output แบบ P2WSH ได้โดยการนำเสนอ witness script ที่ถูกต้องพร้อมกับลายเซ็นในจำนวนที่เพียงพอเพื่อทำให้สคริปต์นั้นผ่านเงื่อนไขของ witness script และลายเซ็นเหล่านี้เองจะถูกรวมอยู่ในโครงสร้างของ witness โดยจะไม่มีข้อมูลใดถูกใส่ไว้ใน input script เนื่องจากนี่เป็น native witness program ซึ่งไม่ใช้ฟิลด์ input script เหมือนกับประเภท legacy</p>
<pre><code>[...]
"vin" : [
  "txid": "abcdef12345...",
  "vout": 0,
  "scriptSig": “”,
]
[...]
“witness”: “&lt;SigA&gt; &lt;SigB&gt; &lt;2 PubA PubB PubC PubD PubE 5 OP_CHECKMULTISIG&gt;”
[...]
</code></pre>
<h3>ความแตกต่างระหว่าง P2WPKH และ P2WSH</h3>
<p>ในสองหัวข้อก่อนหน้านี้เราได้อธิบายถึง witness program อยู่ 2 ประเภท ได้แก่ Pay to Witness Public Key Hash (P2WPKH) และ Pay to Witness Script Hash (P2WSH) โดย witness program ของทั้งสองประเภทนั้นมีหมายเลขเวอร์ชันเดียวกัน ตามด้วยการใส่ข้อมูลหนึ่งค่าลงในแสตกเหมือนกัน ทำให้รูปแบบของทั้งสองดูคล้ายคลึงกัน แต่การตีความของทั้งสองนั้นแตกต่างกันอย่างสิ้นเชิง โดยแบบหนึ่งจะถูกตีความเป็นแฮชของ public key ซึ่งปลดล๊อกได้ด้วยลายเซ็นในขณะที่อีกแบบหนึ่งนั้นจะถูกตีความเป็นค่าแฮชของสคริปต์ซึ่งปลดล๊อกได้ด้วย witness script</p>
<p>และความแตกต่างที่สำคัญที่สุดระหว่างทั้งสองคือความยาวของ witness program:</p>
<ul>
<li>witness program ของ P2WPKH มีขนาด 20 ไบต์  </li>
<li>witness program ของ P2WSH มีขนาด 32 ไบต์</li>
</ul>
<p>ความแตกต่างเพียงจุดเดียวนี้เองทำให้โหนดของบิตคอยน์สามารถแยกได้ว่า witness program นั้นเป็นของสคริปต์รูปแบบใด โดยดูจากเพียงแค่ความยาวของแฮช</p>
<h3>การอัปเกรดไปใช้ Segregated Witness</h3>
<p>จากตัวอย่างก่อนหน้านี้ เราจะเห็นได้ว่าการอัปเกรดไปใช้ segregated witness นั้นเป็นกระบวนการที่มี 2 ขั้นตอนด้วยกัน ขั้นตอนแรก คือกระเป๋าเงินต้องสามารถสร้าง output แบบ segwit ได้ จากนั้น output เหล่านี้จึงสามารถถูกใช้ได้ด้วยกระเป๋าเงินที่รู้วิผะีสร้างธุรกรรมแบบ segregated witness โดยในตัวอย่างที่กล่าวมากระเป๋าเงินของอลิซสามารถสร้าง output จ่ายไปยังสคริปต์แบบ segregated witness ได้และกระเป๋าของบ๊อบก็รองรับ segwit และสามารถใช้จ่ายได้เช่นกัน</p>
<p>Segregated witness ถูกนำมาใช้ในลักษณะของการอัปเกรดที่ยังคงเข้ากันได้ย้อนหลัง (backward-compatible) ซึ่งทำให้ไคลเอนต์ทั้งแบบเก่าและแบบใหม่สามารถอยู่ร่วมกันได้ นักพัฒนากระเป๋าเงินได้อัปเกรดซอฟต์แวร์ของตนอย่างอิสระเพื่อเพิ่มความสามารถรองรับ segwit ส่วนรูปแบบเดิมอย่าง P2PKH และ P2SH ก็ยังคงใช้งานได้ตามปกติสำหรับกระเป๋าเงินที่ยังไม่ได้อัปเกรด</p>
<p>สิ่งนี้ทำให้เกิดสองสถานการณ์สำคัญ ซึ่งจะถูกอธิบายในหัวข้อถัดไป:</p>
<ul>
<li>ความสามารถของกระเป๋าเงินฝั่งผู้จ่ายที่ไม่รองรับ segwit ในการชำระเงินให้กับกระเป๋าเงินฝั่งผู้รับที่สามารถประมวลผลธุรกรรมแบบ segwit ได้  </li>
<li>ความสามารถของกระเป๋าเงินฝั่งผู้จ่ายที่รองรับ segwit ในการรับรู้และแยกแยะได้ว่าผู้รับรายใดรองรับ segwit และรายใดไม่รองรับ โดยพิจารณาจากรูปแบบของ address</li>
</ul>
<h3>การฝัง segregated witness ไว้ภายใน P2SH</h3>
<p>สมมุติว่ากระเป๋าเงินของอลิซยังไม่ได้อัปเกรดเป็น segwit แต่กระเป๋าเงินของบ๊อบได้อัปเกรดแล้ว และสามารถจัดการธุรกรรมแบบ segwit ได้ ทั้งอลิซและบ๊อบยังสามารถใช้ output แบบดั้งเดิมที่ไม่ใช้ segwit ได้ตามปกติ อย่างไรก็ตามบ๊อบมักจะต้องการใช้ segwit เพื่อช่วยลดค่าธรรมเนียมธุรกรรมโดยอาศัยต้นทุนที่ต่ำกว่าสำหรับส่วน witness</p>
<p>ในกรณีนี้กระเป๋าเงินของบ๊อบสามารถสร้างที่อยู่แบบ P2SH ซึ่งภายในบรรจุสคริปต์ segwit อยู่ได้ กระเป๋าเงินของอลิซสามารถส่งเงินไปยัง address นั้นได้โดยไม่จำเป็นต้องรู้จักหรือเข้าใจ segwit แต่อย่างใด นอกจากนี้กระเป๋าเงินของบ๊อบก็สามารถใช้จ่าย output นั้นด้วยธุรกรรมแบบ segwit ได้ และทำให้ได้ประโยชน์จาก segwit และช่วยลดค่าธรรมเนียมธุรกรรมได้</p>
<p>สคริปต์ witness ทั้งสองรูปแบบ คือ P2WPKH และ P2WSH สามารถถูกฝังอยู่ภายใน address แบบ P2SH ได้ โดยจะเรียกว่า nested P2WPKH และ nested P2WSH ตามลำดับ</p>
<h3>Nested pay to witness public key hash</h3>
<p>รูปแบบแรกของ output script ที่เราจะพิจรณาคือ nested P2WPKH ซึ่งเป็น witness program แบบ pay to witness public key hash ที่ถูกฝังอยู่ภายในสคริปต์แบบ pay to script hash เพื่อให้กระเป๋าเงินที่ยังไม่รู้จัก segwit สามารถส่งเงินไปยัง output นี้ได้</p>
<p>กระเป๋าเงินของบ๊อบจะสร้าง P2WPKH witness program จาก public key ของ Bob จากนั้นนำ witness program นี้ไปแฮช และนำค่าแฮชที่ได้ไปเข้ารหัสเป็นสคริปต์แบบ P2SH โดยตัวสคริปต์นี้เองจะถูกแปลงเป็น address ของบิตคอยน์ ซึ่งจะเป็น address ที่ขึ้นต้นด้วย 3 เช่นเดียวกันกับ Pay to script hash</p>
<p>กระเป๋าเงินของบ๊อบเริ่มต้นด้วย witness version และ witness program แบบ P2WPKH ที่เราได้เห็นก่อนหน้านี้:</p>
<pre><code>0 ab68025513c3dbd2f7b92a94e0581f5d50f654e7
</code></pre>
<p>ข้อมูลนี้จะประกอบด้วย witness version และค่าแฮชของ public key ของบ๊อบขนาด 20 ไบต์</p>
<p>จากนั้นกระเป๋าของเขาจะนำข้อมูลนี้ไปแฮช โดยเริ่มจาก SHA256 ก่อน แล้วตามด้วย RIPEMD 160 ทำให้ได้ค่าแฮชขนาด 20 ไบต์อีกค่าหนึ่ง ต่อมาแฮชของ redeem script นี้จะถูกแปลงเป็น address ของบิตคอยน์ในท้ายที่สุด จากนั้นกระเป๋าเงินของอลิซก็สามารถส่งเงินไปยังที่อยู่ 37Lx99uaGn5avKBxiW26HjedQE3LrDCZru ได้เช่นเดียวกันกับการส่งไปยัง address ทั่ว ๆ ไป</p>
<p>เพื่อจ่ายเงินให้บ๊อบ กระเป๋าเงินของอลิซจะล็อกเอาต์พุตด้วยสคริปต์แบบ P2SH ดังนี้:</p>
<pre><code>OP_HASH160 3e0547268b3b19288b3adef9719ec8659f4b2b0b OP_EQUAL
</code></pre>
<p>แม้ว่ากระเป๋าเงินของอลิซจะไม่รองรับ segwit เลยก็ตาม แต่การชำระเงินที่เธอสร้างขึ้นนี้บ๊อบก็ยังสามารถนำไปใช้จ่ายต่อได้ด้วยธุรกรรมแบบ segwit</p>
<h3>Nested pay to witness script hash</h3>
<p>ในทำนองเดียวกันกับ witness program แบบ P2WSH สำหรับสคริปต์ multisig หรือสคริปต์ที่ซับซ้อนอื่น ๆ สามารถถูกฝังอยู่ภายใน address แบบ P2SH ได้ ทำให้กระเป๋าเงินใด ๆ ก็ตามสามารถชำระเงินได้ในลักษณะที่เข้ากันได้กับ segwit</p>
<p>ดังที่เราเห็นในหัวข้อของ Pay to witness script hash (P2WSH) บริษัทของโมฮัมเหม็ดใช้การชำระเงินแบบ segregated witness ไปยังสคริปต์ multisignature เพื่อให้ลูกค้าทุกรายสามารถชำระเงินให้บริษัทของเขาได้ ไม่ว่ากระเป๋าเงินของลูกค้าจะอัปเกรดรองรับ segwit แล้วหรือไม่ กระเป๋าเงินของโมฮัมเหม็ดก็สามารถผัง P2WSH witness program ไว้ภายในสคริปต์แบบ P2SH ได้</p>
<p>ขั้นแรกกระเป๋าเงินของโมฮัมเหม็ดจะนำ witness script ไปแฮชด้วย SHA256(ครั้งเดียว) และได้ค่าแฮชดังนี้:</p>
<pre><code>9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73
</code></pre>
<p>ถัดมา witness script ที่ถูกแฮชแล้วจะถูกแปลงให้เป็น P2WSH witness program ที่มีการใส่เวอร์ชันนำหน้า:</p>
<pre><code>0 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73
</code></pre>
<p>จากนั้น witness program นี้จะถูกแฮชอีกครั้งด้วย SHA256 และ RIPEMD160 ตามลำดับ ทำให้ได้แฮชขนาด 20 ไบต์ใหม่ดังงนี้:</p>
<pre><code>86762607e8fe87c0c37740cddee880988b9455b2
</code></pre>
<p>ต่อมากระเป๋าเงินจะสร้าง address บิตคอยน์ แบบ P2SH จากแฮชนี้:</p>
<pre><code>3Dwz1MXhM6EfFoJChHCxh1jWHb8GQqRenG
</code></pre>
<p>โดยจากจุดนี้เอง ลูกค้าของโมฮัมเหม็ดก็สามารถชำระเงินมายัง address นี้ได้ แม้ว่ากระเป๋าเงินของพวกเขาจะไม่รับรอง segwit ก็ตามเพื่อส่งเงินให้โมฮัมเหม็ด กระเป๋าเงินของผู้ส่งจะล็อก output ด้วยสคริปต์ P2SH ดังต่อไปนี้:</p>
<pre><code>OP_HASH160 86762607e8fe87c0c37740cddee880988b9455b2 OP_EQUAL
</code></pre>
<p>จากนั้นบริษัทของโมฮัมเหม็ดก็สามารถสร้างธุรกรรมแบบ segwit เพื่อใช้จ่ายเงินเหล่านี้ได้ โดยได้รับประโยชน์จากคุณสมบัติของ segwit รวมถึงค่าธรรมเนียมธุรกรรมที่ต่ำลง</p>
<h2>Merklized Alternative Script Trees (MAST)</h2>
<p>การใช้ OP_IF นั้นสามารถกำหนดเงื่อนไขสำหรับการใช้จ่ายได้หลายรูปแบบ แต่แนวทางนี้เองก็มีข้อเสียที่ไม่พึงประสงค์อยู่หลายประการ อาทิเช่น</p>
<h5>น้ำหนัก(ต้นทุนในการทำธุรกรรม)</h5>
<p>ทุกเงื่อนไขที่เพิ่มเข้าไปในสคริปต์นั้นจะทำให้สคริปต์มีขนาดใหญ่ขึ้น ซึ่งจะส่งผลให้ต้นทุนในการทำธุรกรรมเพิ่มขึ้นเมื่อต้องการใช้จ่ายบิตคอยน์ที่ถูกป้องกันไว้ด้วยสคริปต์นี้</p>
<h5>ข้อจำกัดด้านขนาด</h5>
<p>แม้ว่าคุณจะยอมจ่ายค่าธรรมเนียมเพิ่มเพื่อให้ใส่เงื่อนไขได้มากขึ้นก็ตาม แต่ก็ยังมีข้อจำกัดในด้านของเงื่อนไขสูงสุดที่สามารถใส่ได้ในสคริปต์อยู่ดี อย่างเช่น legacy script ถูกจำกัดขนาดไว้ที่ 10,000 ไบต์ ซึ่งทำให้ในทางปฏิบัตินั้นสามารถใส่ได้เพียงไม่กี่ร้อยเงื่อนไขเท่านั้น และแม้ว่าจะสามารถสร้างสคริปต์ที่มีขนาดใหญ่เท่ากับบล๊อคทั้งบล๊อคได้ ก็ยังจะมีได้เพียงประมาณ 20,000 เงื่อนไขเท่านั้นที่ใช้งานได้จริง ซึ่งถือว่ามากแล้วสำหรับการชำระเงินแบบง่าย ๆ แต่ก็เล็กมากเมื่อเทียบกับการใช้งานบางอย่างที่จินตนาการไว้สำหรับบิตคอยน์</p>
<h5>การขาดแคลนความเป็นส่วนตัว</h5>
<p>ทุกเงื่อนไขที่มีการเพิ่มเข้าไปในสคริปต์นั้นจะกลายเป๋นข้อมูลสาธารณะเมื่อคุณใช้จ่ายบิตคอยน์ที่ถูกปกป้องไว้ด้วยสคริปต์นั้น ๆ ตัวอย่างเช่นทนายความและหุ้นส่วนทางธุรกิจของนายโมฮัมเหม็ดก็จะสามารถเห็นสคริปต์ทั้งหมดไม่ว่าจะในตัว multi-signature หรือ timelock ก็ตาม และทุกครั้งที่มีการใช้จ่ายกับบัญชีนี้ จะทำให้ทนายความของพวกเขาติดตามธุรกรรมทั้งหมดได้แม้ว่าจะไม่ได้ร่วมลงนามก็ตาม</p>
<p>แต่อย่างไรก็ตาม บิตคอยน์มีการใช้โครงสร้างข้อมูลที่เรียกว่า merkle tree อยู่แล้ว ซึ่งมันช่วยให้สามารถตรวจสอบได้ว่าองค์ประกอบใด ๆ เป็นสมาชิกของข้อมูลหรือไม่โดยไม่จำเป็นต้องเปิดเผยหรือระบุสมาชิกอื่น ๆ ทั้งหมดของชุดนั้น</p>
<p>ซึ่งเราจะได้เรียนรู้เกี่ยวกับ merkle tree มากขึ้นในหัวข้อของ merkle trees แต่สาระสำคัญคือ สมาชิกแต่ละตัวของชุดข้อมูลที่เราต้องการ (เช่น เงื่อนไขการอณุญาตที่มีความยาวเท่าใดก็ได้) สามารถนำไปผ่านฟังก์ชันแฮชแล้วนำแฮชมารวมกันอีกครั้งเพื่อสร้างค่าผูกมัดของกิ่งทั้งสองกิ่ง ซึ่งเรียกว่า branch commitment</p>
<p>โดยค่าผูกมัดของกิ่งทั้งสองเองก็สามารถสร้างได้ด้วยวิธีเดียวกัน ขั้นตอนนี้จะถูกทำซ้ำเรื่อย ๆ จนเหลือตัวเดียวและเราจะเรียกตัวนั้นว่า merkle root</p>
<p>จากตัวอย่างสคริปต์ในหัวข้อ Variable multi-signature with timelock เราสามารถสร้าง Merkle tree สำหรับเงื่อนไขการอณุญาตทั้งสามแบบใน A MAST ที่มีสามสคริปต์ย่อยได้<br> <img src="https://image.nostr.build/3d6230c0a0ee745196a957384b79606c729d3829efbaf1b4125e357bc6a68f24.jpg" alt="image"></p>
<p>ในขณะนี้เราสามารถสร้างหลักฐานการเป็นสมาชิกแบบกระทัดรัด (Compact membership proof) เพื่อพิสูจน์ได้ว่าเงื่อนไขการอนุญาตเงื่อนไขใดเงื่อนไขหนึ่งเป็นสมาชิกของ merkle tree โดยไม่ต้องเปิดเผยรายละเอียดใด ๆ ของสมาชิกอื่น ๆ ใน merkle tree สามาดูได้ในตัวอย่างของ A MAST membership proof for one of the subscripts และสังเกตว่าโหนดที่แรเงาไว้สามารถคำนวณได้จากข้อมูลอื่นที่ผู้ใช้ให้มา ดังนั้นจึงไม่จำเป็นต้องระบุโหนดเหล่านั้นในขณะทำการใช้จ่าย<br> <img src="https://image.nostr.build/3a5c3e43c266357582fb4bc17eedefaed398306d84750072af061d07cc99e513.jpg" alt="image"></p>
<p>ค่าแฮชที่ใช้ในการสร้างคอมมิจเมนต์แต่ละตัวมีขนาด 32 ไบต์ ดังนั้นการพิสูจน์ว่าการใช้จ่ายใน A MAST membership proof for one of the subscripts ได้รับอนุญาต (โดยใช้ merkle tree และเงื่อนไขที่เกี่ยวข้อง) และได้รับการพิสูจน์ตัวตน(ด้วยการใช้ลายเซ็น) จะใช้ข้อมูลทั้งหมด 383 ไบต์เมื่อเปรียบเทียบกันแล้ว การใช้จ่ายแบบเดียวกันโดยไม่ใช้ merkle tree (กล่าวคือ ต้องแสดงเงื่อนไขทั้งหมดที่เป็นไปได้) ที่จะใช้ข้อมูลถึง 412 ไบต์</p>
<p>การประหยัดได้ 29 ไบต์ (7%) ในตัวอย่างนี้ยังสะท้อนให้เห็นถึงศักยภาพในการประหยัดได้ไม่ครบถ้วน เนื่องจากลักษณะของ merkle tree ที่เป็นโครงสร้างแบบไบนารีทรีนั้น ทุกครั้งที่มีการเพิ่มจำนวนสมาชิกในเซตเพิ่มขึ้นเป็นสองเท่า เราจำเป็นต้องเพิ่มคอมมิตเมนต์ขนาด 32 ไบต์เพียงตัวเดียวเท่านั้น เช่นในกรณีที่มีทั้งหมด 3 เงื่อนไขเราจำเป็นต้องใช้คอมมิตเมนต์ 3 ตัว (ซึ่งหนึ่งในนั้นคือ merkle root ที่ต้องถูกรวมในการอณุญาตอยู่แล้ว) และเรายังสามารถมีคอมมิตเมนต์ได้ถึง 4 ตัวด้วยต้นทุนที่เท่าเดิม และหากมีคอมมิตเมนต์เพิ่มขึ้นอีกตัวหนึ่งจะทำให้เราสามารถรองรับเงื่อนไขได้สูงสุดถึงแปดเงื่อนไขด้วยกัน และในทำนองเดียวกันหากเรามี 16 คอมมิตเมนต์(ขนาดรวมเป็น 512 ไบต์) เราจะสามารถมีเงื่อนไขการอณุญาตได้มากกว่า 32,000 เงื่อนไข ซึ่งนั้นก็มากเกินกว่าที่จะนำไปใช้ได้จริงในบล็อก ๆ หนึ่งที่เต็มไปด้วยธุรกรรมที่มีคำสั่ง OP_IF ทั้งหมดเสียอีก และหากเราใช้ถึง 128 คอมมิตเมนต์ (4096 ไบต์) จำนวนเงื่อนไขที่เราจะสามารถสร้างได้ในเชิงทฤษฏีจะมีมากเกินกว่าเงื่อนไขทั้งหมดที่คอมพิวเตอร์ทุกเครื่องบนโลกจะสามารถสร้างขึ้นมาได้</p>
<p>โดยทั่วไปแล้ว เงื่อนไขการอณุญาตไม่ได้มีโอกาสถูกใช้งานเท่า ๆ กันทั้งหมด ในกรณีตัวอย่างของเรา เราคาดว่าโมฮัมเหม็ดและหุ้นส่วนของเขาจะใช้จ่ายเป็นประจำ ส่วนเงื่อนไขการหน่วงเวลามีไว้ใช้ในกรณีที่เกิดปัญหาเท่านั้น เราสามารถนำความรู้นี้มาใช้ในการปรับโครงสร้างของ tree ใหม่ทั้งหมดได้ ดังที่แสดงในรูปภาพข้างล่างนี้ โดยมีการจัดสคริปต์ที่คาดว่าจะมีการใช้งานมากที่สุดอยู่ในตำแหน่งที่เหมาะสมที่สุด</p>
<p> <img src="https://image.nostr.build/a60366f9810f9eb3e364bc0d83e4ef733991fdbd3ae1625c9acfe45256b08578.jpg" alt="image"></p>
<p>ในกรณีนี้ เราจำเป็นที่จะต้องให้คอมมิตเมนต์เพียงสองค่าเท่านั้นในกรณีที่ใช้จ่ายแบบที่คาดว่าจะบ่อยที่สุด (ประหยัดได้ 32 ไบต์) แม้ว่าการใช้จ่ายด้วยกรณีอื่น ๆ ยังคงจำเป็นต้องใช้ 3 คอมมิตเมนต์ก็ตาม ซึ่งแบบว่าหากคุณทราบหรือสามารถคาดเดาถึงความน่าจะเป็นของการใช้เงื่อนไขแต่ละแบบได้ คุณจะสามารถใช้อัลกอริทึมอย่าง Huffman เพื่อจัดวางเงื่อนไขเหล่านี้ลงไปในตำแหน่งที่ทำให้มีประสิทธิภาพสูงสุดได้ โดยสามารถดูรายละเอียดเพิ่มเติมได้ใน BIP341</p>
<p>นอกจากจะเพิ่มความซับซ้อนให้กับบิตคอยน์มาอีกเล็กน้อยแล้ว MAST ก็ยังแทบไม่มีข้อเสียที่สำคัญอะไรต่อบิตคอยน์เลย และก่อนที่จะพบแนวทางที่ดีกว่าในภายหลัง (เดี๋ยวเราจะได้เห็นในหัวข้อ taproot) ก็มีข้อเสนอที่แข็งแรงอยู่แล้วถึงสองอันสำหรับ MAST นั้นคือ BIP114 และ BIP116</p>
<h3>MAST เทียบกับ MAST</h3>
<p>แนวคิดแรกเริ่มของสิ่งที่ปัจจุบันเราเรียกว่า MAST ในบิตคอยน์ คือแนวคิดของ merklized abstract syntax trees (ต้นไม้โครงสร้างไวยากรณ์เชิงนามธรรมที่ถูกเมอร์เคิลไลซ์) ในต้นไม้โครงสร้างไวยากรณ์เชิงนามธรรม (AST) แต่ละเงื่อนไขในสคริปต์จะสร้างสาขาใหม่ ดังที่แสดงไว้ในรูปภาพข้างล่าง<br> <img src="https://image.nostr.build/2278f96ec80d7fe7e93c87a73ef70b42934a05bc110abcf9c2d1aa1c41433d91.jpg" alt="image"></p>
<p>AST นั้นได้ถูกใช้อย่างแพร่หลายในโปรแกรมที่ทำหน้าที่แยกวิเคราห์และปรับแต่งโค้ดของโปรแกรมอื่น ๆ เช่น คอมไพเลอร์ หากนำ AST มาใช้ในรูปแบบที่ถูกเมอร์เคิลไลซ์ (merklized AST) ก็จะสามารถผูกมัดได้กับทุกส่วนของโปรแกรม และเปิดให้ใช้คุณสมบัติต่าง ๆ ที่อธิบายไว้ใน merklized Alternative Script Trees (MAST) แต่อย่างไรก็ตามวิธีนี้จะต้องเปิดเผยค่าแฮชขนาด 32 ไบต์อย่างน้อยหนึ่งค่า สำหรับทุกส่วนย่อยของโปรแกรม ซึ่งทำให้ไม่ค่อยมีประสิธิภาพในด้านพื้นที่ของบล็อกเชนสำหรับโปรแกรมส่วนใหญ่</p>
<p>สิ่งที่ผู้คนส่วนใหญ่มักเรียกว่า MAST ในบริบทของบิตคอยน์ในปัจจุบันคือ merklized alternative script trees ซึ่งเป็น backronym ที่ถูกตั้งขึ้นโดยนักพัฒนา Anthony Towns ซึ่ง alternative script tree คือชุดของสคริปต์หลายสคริปต์ ซึ่งแต่ละสคริปต์นั้นมีความสมบูรณืในตัวเองและสามารถเลือกใช้งานได้เพียงหนึ่งสคริปต์เท่านั้น ทำให้สครืปต์เหบ่านี้เป็นทางเลือกแทนกัน ซึ่งแสดงไว้ในภาพด้านล่าง</p>
<p> <img src="https://image.nostr.build/d87c36148baa4429f15264771a0341e787e343df3de17136c16db0d3d61397d6.jpg" alt="image"></p>
<p>ต้นไม้สคริปต์แบบทางเลือก (alternative script trees) ต้องการเพียงเปิดเผยไดเจสเพียง 32 ไบต์เท่านั้นสำหรับความลึกในแต่ละลำดับที่ผู้ใช้เลือกใช้และ merkle root สำหรับสคริปต์ส่วนใหญ่ นี่ถือเป็นการใช้พื้นที่ในบล๊อกเชนที่มีประสิธิภาพมากกว่าเป็นอย่างมาก</p>
<h2>Pay to Contract (P2C)</h2>
<p>ดังที่เราได้เห็นในบทก่อนหน้าในหัวข้อ public child key derivation คณิตศาสตร์ของการเข้ารหัสด้วยเส้นโค้งวงรี (Eliptic Curve Cryptography:ECC) อณุญาตให้อลิซใช้ private key เพื่อสร้าง public key ที่เธอมอบให้บ๊อบ สามารถเพิ่มค่าที่เป็นไปได้ค่าใดก็ได้เข้ามาใน public key นั้น  เพื่อสร้าง public key ใหม่ขึ้นมา หากบ๊อบส่งค่าที่เขาเพิ่มนั้นกลับไปให้อลิซ เธอก็สามารถนำค่าเดียวกันไปบวกกับ private key ของเธอ เพื่อสร้าง private key ที่สอดคล้องกับ public key ที่ถูกอนุพันธ์ขึ้นมาได้</p>
<p>สรุปสั้น ๆ ก็คือบ๊อบสามารถสร้าง child public key ซึ่งมีเพียงอลิซเท่านั้นที่สามารถสร้าง private key ที่สอดคล้องกันขึ้นมาได้ สิ่งนี้เป็นประโยชน์สำหรับการกู้คืนกระเป๋าเงินแบบ Hierarchical Determinstic (HD) ตามมาตรฐานของ BIP32 แต่ก็ยังสามารถนำไปใช้ในวัตถุประสงค์อื่นได้อีกด้วย</p>
<p>ลองจินตนาการดูสิว่าบ๊อบต้องการซื้อของบางอย่างจากอลิซ แต่เขาก็ต้องการความสามารถในการพิสูจน์ภายหลังได้ว่าเขาจ่ายเงินไปเพื่ออะไร ในกรณีที่เกิดข้อพิพาทขึ้นอลิซและบ๊อบจึงตกลงกันเกี่ยวกับชื่อของสินค้าหรือบริการที่ขาย (เช่น “พอดแคสต์ของอลิซตอนที่#123”) แล้วแปลงคำอธิบายนั้นให้เป็นตัวเลข โดยการนำไปแฮชและตีความค่าแฮชที่ได้เป็นตัวเลข จากนั้นบ๊อบนำตัวเลขนั้นไปบวกกับ public key ของอลิซและจ่ายเงินให้ไป กระบวนการนี้เรียกว่า key tweaking และตัวเลขนั้นเรียกว่า tweak</p>
<p>อลิซสามารถใช้จ่ายเงินดังกล่าวได้โดยการปรับแต่ง(tweak) private key ของตัวเธอเองด้วยเลขเดียวกัน</p>
<p>และในภายหลังบ๊อบเองก็สามารถพิสูจน์กับใครก็ตามได้ว่าเขาได้จ่ายเงินให้อลิซเพื่ออะไร โดยการเปิดเผยถึงกุญแจตั้งต้น (underlying key) ของอลิซและคำอธิบายของสินค้าหรือบริการที่ทั้งสองใช้ร่วมกัน ทุกคนสามารถตรวจสอบได้ว่า public key ที่ถูกนำไปใช้ชำระเงินนั้นเท่ากับกุญแจตั้งต้นบวกกับค่าแฮช ของคำอธิบายนั้น และหากอลิซยอมรับว่ากุญแจดังกล่าวเป็นของเธอ ก็ย่อมแสดงว่าเธอยอมรับว่าเธอเป็นผู้รับเงินก้อนนั้น และหากอลิซเป็นผู้ใช้จ่ายเงินออกไป ก็ยิ่งเป็นหลักฐานยืนยันเพิ่มเติมว่าเธอรู้คำอธิบายนั้นตั้งแต่ตอนที่เธอลงนามในธุรกรรมการจ่ายเงิน เพราะเธอสามารถสร้างลายเซ็นที่ถูกต้องได้สำหรับ public key ที่ถูกปรับแต่ง ได้ก็ต่อเมื่อเธอรู้ว่าค่า tweak คืออะไรเท่านั้น</p>
<p>หากอลิซและบ๊อบไม่ได้ตัดสินใจเปิดเผยคำอธิบายที่พวกเขาใช้ต่อสาธารณะการชำระเงินระหว่างทั้งสองก็จะดูไม่ต่างจากการชำระเงินทั่วไปอื่น ๆ และจะไม่ก่อให้เกิดการสูญเสียความเป็นส่วนตัวแต่อย่างใด</p>
<p>เนื่องจาก P2C มีความเป็นส่วนตัวโดยปริยาย เราจึงไม่สามารถทราบได้ว่ามันถูกใช้งานตามวัตถุประสงค์ดั้งเดิมบ่อยเพียงใด ในทางทฤษฏีแล้ว การชำระเงินทุกครั้งอาจใช้วิธีนี้ก็ได้ แม้ว่าเราจะมองว่าสถานการณ์เช่นนั้นไม่น่าเกิดขึ้นก็ตาม แต่ในปัจจุบันนั้น P2C ถูกใช้อย่างแพร่หลายในรูปแบบที่แตกต่างกันออกไปเล็กน้อย ซึ่งเราจะได้เห็นกันใน taproot ที่จะอธิบายต่อภายหลัง</p>
<h2>Scriptless Multisignatures and Threshold Signatures</h2>
<p>ในการทำ Scripted Multisignatures คือการพิจรณาสคริปต์ที่กำหนดให้ต้องมีลายเซ็นจากหลายฝ่าย อย่างไรก็ตามยังมีอีกหนึ่งวิธีในการบังคับให้ต้องใช้กุญแจหลายดอกร่วมกัน ซึ่งก็น่าจะทำให้สับสนได้ไม่น้อย เพราะมันก็ถูกเรียกว่า multisignature เช่นกัน แต่เพื่อแยกความแตกต่างระหว่างสองแนวทางนี้ ในส่วนนี้เราจะเรียกเวอร์ชันที่ใช้ opcode แบบ OP_CHECKSIG ว่า script multisignatures และอีกเวอร์ชันที่จะพูดถึงต่อไปว่า scriptless multisignatures</p>
<p>Scriptless multisignature ทำงานโดยให้ผู้เข้าร่วมแต่ละคนสร้างความลับของตนเองขึ้นมาคล้ายกับวิธีสร้าง private key โดยเราจะเรียกความลับนี้ว่า partial private key โดยแม้ว่ามันจะมีความยาวเท่ากับ private key แบบปกติทุกประการจาก partial private key อันนี้ ผู้เข้าร่วมแต่ละคนจะอนุมาน partial public key โดยใช้อัลกอริทึมเดียวกันกับที่ใช้สร้าง public key ทั่วไป ตามที่อธิบายไว้ในหัวข้อ public key derivation จากนั้นผู้เข้าร่วมทุกคนจะแชร์ partial public key ของตนให้กับผู้เข้าร่วมอื่น ๆ และนำกุญแจทั้งหมดมารวมกันเพื่อสร้าง scriptless multisignature public key เพียงดอกเดียว</p>
<p>กุญแจสาธรณะที่ถูกรวมกันนี้จะมีลักษณะไม่ต่างจาก public key ทั่วไป บุคคลที่สามไม่สามารถแยกแยะได้ว่า public key นี้เกิดจากผู้ใช้หลายคนร่วมกัน หรือเป็น public key ธรรมดาที่สร้างขึ้นโดยผู้ใช้เพียงคนเดียว</p>
<p>สำหรับการใช้จ่ายบิตคอยน์ที่ถูกป้องกันด้วย scriptless multisignature public key ผู้เข้าร่วมแต่ละคนจะสร้างลายเซ็นย่อย (partial signature) ของตนเอง จากนั้นลายเซ็นย่อยเหล่านี้จะถูกรวมเข้าด้วยกันเพื่อสร้างเป็นลายเซ็นที่สมบูรณ์ (บางที่เรียกว่า Full signature) ซึ่งก็มีวิธีที่เป็นที่รู้จักกันหลายรูปแบบสำหรับการสร้างและรวมลายเซ็น ซึ่งเราจะกลับมาลงลึกในประเด็นนี้กันอีกครั้งในหัวข้อ Signature (บทที่ 8) และเช่นเดียวกันกับกรณีของ public key สำหรับ scriptless multisignatures ลายเซ็นที่ได้จากกระบวนการนี้จะมีหน้าตาเหมือนกับลายเซ็นทั่วไปทุกประการ บุคคลที่สามไม่สามารถตรวจสอบได้ว่าลายเซ็นนั้นถูกสร้างขึ้นโดยคนเพียงคนเดียวหรือเกิดจากความร่วมมือของผู้คนจำนวนมาก แม้ว่าจะเป็นการร่วมมือกันระดับล้านคนก็มองไม่ออก</p>
<p>Scriptless multisignatures มีทั้งขนาดเล็กกว่าและมีความเป็นส่วนตัวสูงกว่า scripted multisignatures อย่างชัดเจน สำหรับ scripted multisignatures จำนวนไบต์ที่ต้องบันทึกลงในธุรกรรมจะเพิ่มขึ้นตามจำนวนกุญแจและลายเซ็นที่เกี่ยวข้อง ยิ่งมีผู้ลงนามมาก ข้อมูลในธุรกรรมก็ยิ่งใหญ่ตามไปด้วย แต่ในทางตรงกันข้ามกัน สำหรับ scriptless signatures ขนาดของข้อมูลจะคงที่ ไม่ว่าจะมีผู้เข้าร่วมกี่คนก็ตาม แม้จะมีส่วนร่วมโดยคนเป็นล้านคนก็ตาม แต่ละคนสร้าง partial key และ partial signature ของตนเอง ธุรกรรมที่ได้จะมีขนาดข้อมูลเท่ากันทุกประการกับกรณีที่มีผู้ใช้เพียงคนเดียวใช้กุญแจดอกเดียวและลายเซ็นเพียงอันเดียว</p>
<p>ในแง่ของความเป็นส่วนตัวก็เป็นไปในทิศทางเดียวกัน เนื่องจาก scripted multisignatures ต้องเพิ่มข้อมูลทุกครั้งที่มีการเพิ่มกุญแจหรือลายเซ็น ธุรกรรมจึงเปิดเผยโดยนัยว่ามีการใช้กุญแจและลายเซ็นจำนวนเท่าใด ซึ่งอาจทำให้สามารถคาดเดาได้ว่าธุรกรรมนั้นจะสร้างขึ้นโดยกลุ่มคนใด แต่สำหรับ scriptless multisignatures ธุรกรรมทุกอันจะมีลักษณะเหมือนกันหมด และยังเหมือนกับธุรกรรมแบบลายเซ็นเดียวด้วย ทำให้ไม่มีข้อมูลรั่วไหลลออกมาทำให้ลดทอนความเป็นส่วนตัวของผู้ใช้งาน</p>
<p>ส่วนสำหรับข้อเสียของ scriptless multisignatures มีอยู่สองประการ อย่างแรกคืออัลกอริทึมที่ปลอดภัยทั้งหมดซึ่งเป็นที่รู้จักกันในปัจจุบันสำหรับการสร้าง scriptless multisignatures บนบิตคอยน์ จำเป็นต้องมีรอบของการโต้ตอบกันหรือไม่ก็ต้องมีการจัดการสถานะที่รอบคอบกว่าเมื่อเทียบกับ scripted signatures สิ่งนี้กลายเป็นความท้าทายในกรณีที่ลายเซ็นนั้นถูกสร้างโดยอุปกรณ์ลงนามแบบฮาร์ดแวร์ซึ่งแทบไม่มีการเช็คสถานะ และกุญแจกระจายอยู่ในสถานที่ทางกายภาพที่แตกต่างกัน ตัวอย่างเช่น หากคุณเก็บอุปกรณ์ลงนามแบบฮาร์ดแวร์ไว้ในตู้เซฟของธนาคาร สำหรับ scripted multisignature คุณอาจจะต้องไปที่ตู้เซฟนั้นเพียงครั้งเดียว แต่สำหรับ scriptless แล้วคุณอาจต้องไปสองถึงสามครั้งเพื่อทำให้กระบวนการลงนามเสร็จสมบูรณ์</p>
<p>ส่วนข้อเสียอีกอย่างหนึ่งคือการลงนามแบบ threshold signing ไม่เปิดเผยว่าใครเป็นผู้ลงนามจริงใน scripted threshold signing ตัวอย่างเช่น อลิซ บ๊อบ และคอลไลร์ ตกลงกันว่าเพียงมีลายเซ็นจากใครก็ได้สองในสามคน ก็เพียงพอที่จะใช้จ่ายเงินก้อนนี้ได้ หากอลิซและบ๊อบเป็นผู้ลงนามจะต้องใส่ลายเซ็นของทั้งสองคนลงในบล๊อกเชนซึ่งทำให้ใครก็ตามที่รู้จักกุญแจของพวกเขาก็สามารถพิสูจน์ได้ว่าอลิซและบ๊อบเป็นผู้ลงนามและคอไลร์ไม่ใช่ผู้ลงนาม แต่ใน scriptless threshold signing ลายเซ็นที่เกิดจากอลิซและบ๊อบจะแยกไม่ออกจากลายเซ็นที่เกิดขึ้นโดยอลิซกับคอไลร์หรือจากบ๊อบกับคอไลร์ ซึ่งมันเป็นข้อดีด้านความเป็นส่วนตัว เนื่องจากมันไม่เปิดเผยว่าใครร่วมลงนามบ้าง แต่อย่างไรก็ตามมันก็มีข้อเสียตามมา นั้นก็คือแม้ว่าคอไลร์จะอ้างว่าเธอไม่ได้ลงนาม แต่ก็ไม่มีอะไรเลยที่สามารถพิสูจน์ได้ว่าเธอไม่ได้ลงนามจริง ๆ ซึ่งอาจเป็นปัญหาในแง่ความรับผิดชอบและการตรวจสอบย้อนหลัง</p>
<p>สำหรับผู้ใช้ที่ต้องมีการย้ายเงินเข้าออกบ่อย ๆ ข้อดีด้านขนาดธุรกรรมที่เล็กลง และความเป็นส่วนตัวที่เพิ่มขึ้นของ multisignatures มีประโยชน์มากกว่าความยุ่งยากที่อาจเกิดขึ้นเป็นครั้งคราวในการสร้างลายเซ็นและการตรวจสอบย้อนหลังของลายเซ็นเหล่านั้น</p>
<h2>Taproot</h2>
<p>เหตุผลอย่างหนึ่งที่ผู้คนเลือกใช้บิตคอยน์คือสามารถสร้างสัญญาที่ให้ผลลัพธ์ได้อย่างคาดการณ์ได้สูงมาก สัญญาทางกฎหมายที่บังคับใช้โดยศาลขึ้นอยู่กับการตัดสินใจของผู้พิพากษาและคณะลูกขุน ในทางกลับกัน สัญญาของบิตคอยน์มักจะต้องการการกระทำจากผู้เข้าร่วมแต่จะถูกบังคับใช้โดยโหนดแบบเต็มนับพันที่รันโค้ดเหมือนกัน เมื่อได้รับสัญญาเดียวกันและข้อมูลชุดเดียวกัน โหนดแบบเต็มทุกตัวจะให้ผลลัพธ์เหมือนกันเสมอ การเบี่ยงเบนใด ๆ หมายความว่าบิตคอยน์ถูกทำลายไปแล้ว ผู้พิพากษาและคณะลูกขุนสามารถยืดหยุ่นได้มากกว่าโปรแกรม แต่เมื่อไม่ต้องการหรือไม่จำเป็นต้องมีความยืดหยุ่นนั้น ความสามารถในการคาดการณ์ของสัญญาบิตคอยน์ถือเป็นคุณสมบัติสำคัญ</p>
<p>หากผู้เข้าร่วมทั้งหมดเห็นว่าผลลัพธ์ของสัญญากลายเป็นเรื่องที่คาดการณ์ได้ทั้งหมด จริง ๆ แล้วก็ไม่มีความจำเป็นที่พวกเขาจะต้องใช้สัญญาต่อไป พวกเขาอาจทำในสิ่งที่สัญญาบังคับให้ทำแล้วยุติสัญญา ในสังคม นี่คือวิธีที่สัญญาส่วนใหญ่ยุติ: หากผู้ที่เกี่ยวข้องพอใจ พวกเขาจะไม่ยื่นสัญญาต่อศาล ในบิตคอยน์ หมายความว่าสัญญาที่ย่อมต้องใช้พื้นที่บล็อกจำนวนมากในการยุติ ควรมีข้อคลอสหนึ่งที่อนุญาตให้ยุติโดยความพึงพอใจร่วมกันแทน</p>
<p>ใน MAST และ scriptless multisignatures การออกแบบสัญญาแบบยินยอมร่วมกันเป็นเรื่องง่าย เพราะเราเพียงทำให้หนึ่งในใบไม้ชั้นบนสุดของต้นสคริปต์เป็น scriptless multisignature ระหว่างผู้เข้าร่วมทั้งหมด ซึ่งเราจะแสดงให้เห็นในรูปภาพที่ได้เห็นข้างบนเราสามารถทำให้มันมีประสิทธิภาพยิ่งขึ้นโดยเปลี่ยนจาก scripted multisignature เป็น scriptless multisignature</p>
<p>แนวทางนี้เองถือว่ามีประสิทธิภาพและความเป็นส่วนตัวพอสมควร หากมีการใช้สัญญาการยินยอมร่วมกัน เราจำเป็นต้องเปิดเผยเพียง merkle branch เดียว และสิ่งที่ถูกเปิดเผยก็มีแค่ว่าลายเซ็นใดที่ถูกใช้เท่านั้น ซึ่งอาจเป็นลายเซ็นจากคนเพียงคนเดียว หรือจากผู้เข้าร่วมหลายพันคนก็ไม่ได้มีใครแยกแยะได้</p>
<p>อย่างไรก็ตามในปี 2018 นักพัฒนาได้ตระหนักได้ว่าเราสามารถทำให้ดีกว่านี้ได้อีก หากนำแนวคิด pay to contract มาใช้ร่วมด้วย</p>
<p>ในคำอธิบายก่อนหน้านี้ของ Pay to contract ในหัวข้อ Pay to Contract (P2C) เราได้ tweak public key เพื่อผูกมัดกับข้อความของข้อตกลงระหว่างอลิซและบ๊อบ แทนที่จะเป็นเช่นนั้น เราสามารถผูกมัดกับโค้ดโปรแกรมของสัญญาได้โดยการผูกมัดกับ root ของ MAST tweak public key นั้นเป็น public key ปกติ ซึ่งหมายความว่ามันอาจต้องการลายเซ็นจากบุคคลเดียว หรืออาจเป็นจากหลายคน (หรืออาจถูกสร้างในลักษณะพิเศษให้เป็นไปไม่ได้ที่จะสร้างลายเซ็นสำหรับมัน) นั่นหมายความว่าเราสามารถทำให้สัญญาสำเร็จได้ด้วยลายเซ็นเดียวจากทุกฝ่ายที่เกี่ยวข้อง หรือโดยการเปิด branch ของ MAST ที่เราต้องการใช้ commitment tree ที่ประกอบด้วย public key และ MAST ดังกล่าวดังที่แสดงไว้ในภาพข้างล่าง<br> <img src="https://image.nostr.build/6155acd9c2852a12cea4667b89c2adc912d68aeab458213a729b13bbdb3d811b.jpg" alt="image"></p>
<p>สิ่งนี้เองที่ทำให้สัญญาแบบยินยอมร่วมกันโดยใช้ multisignature มีประสิธิภาพสูงและมีความเป็นส่วนตัวมากยิ่งขึ้น เพราะธุรกรรมที่สร้างโดยใช้โดยคนเพียงคนเดียวซึ่งต้องการให้ยุติโดยลายเซ็นเดียว (หรือ multisignature ที่สร้างโดยหลายกระเป๋าที่เขาควบคุม) จะดูเหมือนกันกับการใช้จ่ายของสัญญาแบบยินยอมร่วมกัน ไม่มีความแตกต่างกันบนบล๊อกเชน ในกรณีนี้ระหว่างการใช้จ่ายโดยกลุ่มผู้ใช้เป็นล้านคนในสัญญาที่ซับซ้อน หรือจ่ายโดยคนเพียงคนเดียว</p>
<p>เมื่อการใช้จ่ายสามารถทำได้โดยใช้เพียงกุญแจ เช่นกรณีลายเซ็นเดี่ยวหรือ scriptless multisig จะเรียกการใช้จ่ายแบบนี้ว่า keypath spending เมื่อใช้ต้นไม้ของสคริปต์ จะเรียกการใช้จ่ายแบบนั้นว่า scriptpath spending สำหรับการใช้จ่ายแบบ keypath ข้อมูลที่จะถูกใส่บนบล๊อกเชนคือ public key (ที่อยู่ใน witness program) และลายเซ็น (ที่อยู่บน witness stack)</p>
<p>สำหรับการใช้จ่ายแบบ scriptpath spending ข้อมูลบนบล๊อกเชนจะรวมถึง public key ด้วยโดย public key นี้จะถูกใส่ไว้ใน witness program และในบริบทนี้เรียกว่า taproot output key โครงสร้างของ witness จะประกอบไปด้วยข้อมูลต่อไปนี้:</p>
<ul>
<li>หมายเลขเวอร์ชัน (Version number)  </li>
<li>กุญแจพื้นฐาน (Underlying key) คือกุญแจที่มีอยู่ก่อนที่จะถูกปรับแต่ง (tweak) ด้วย merkle root เพื่อสร้าง taproot output key กุญแจพื้นฐานนี้เรียกว่า taproot internal key  </li>
<li>สคริปต์ที่ถูกนำมาประมวลผลนี้เรียกว่า leaf script  </li>
<li>ค่าแฮชขนาด 32 ไบต์จำนวนหนึ่งค่าต่อหนึ่งจุดที่เชื่อมใน merkle tree ตามเส้นทางที่ leaf เชื่อมเข้ากับ Merkle root  </li>
<li>ข้อมูลใด ๆ ที่จำเป็นต่อการทำให้สคริปต์เป็นจริง (เช่น ลายเซ็น หรือ hash preimage)</li>
</ul>
<p>ข้อเสียสำคัญเพียงอย่างเดียวของ taproot ที่มีการอธิบายไว้คือ กรณีของสัญญาที่ผู้เข้าร่วมต้องการใช้ MAST แต่ไม่ต้องการมีเงื่อนไขการยุติความพึงพอใจร่วมกัน สัญญาเหล่านั้นจำเป็นต้องบันทึก taproot internal key ได้บนบล๊อกเชน ซึ่งเพิ่ม overhead ประมาณ 33 ไบต์ แต่อย่างไรก็ตาม เนื่องจากคาดว่าสัญญาส่วนใหญ่จะได้รับประโยชน์จากเงื่อนไขหารยุติโดยใช้ความพึงพอใจร่วมกัน หรืออย่างน้อยก็มีเงื่อนไข multisignature อื่นที่ใช้ public key ที่อยู่ในระดับบนสุด และผู้ใช้ทุกคนยังได้รับประโยชน์จากการเพิ่ม anonymity set ที่ทำให้ output มีลักษณะคล้ายกันมากขึ้น overhead ที่เกิดขึ้นในกรณีที่พบไม่บ่อยนี้จึงไม่ถูกมองว่าสำคัญโดยผู้ใช้ส่วนใหญ่ที่มีส่วนร่วมในการเปิดใช้งาน taproot</p>
<p>การรองรับ taproot ถูกเพิ่มเข้ามาในบิตคอยน์ผ่านการทำ soft fork ซึ่งถูกเปิดใช้งานในเดือนพฤศจิกายนปี 2021</p>
<h3>Tapscript</h3>
<p>Taproot ทำให้สามารถใช้ MAST ได้ แต่ในภาษาสคริปต์ของบิตคอยน์ในเวอร์ชันที่แตกต่างจากเดิมเล็กน้อย โดยเวอร์ชันใหม่นี้เรียกว่า Tapscript โดยความแตกต่างสำคัญประกอบด้วย:</p>
<h5>การเปลี่ยนแปลงของ scripted multisignature:</h5>
<p>opcode เดิมอย่าง OP_CHECKMULTISIG และ OP_CHECKMULTISIGVERIFY ถูกนำออกไป เนื่องจาก opcode เหล่านี้ไม่สามารถทำงานร่วมกันได้ดีนักกับการเปลี่ยนแปลงอีกอย่างใน soft fork ของ taproot นั่นคือความสามารถในการใช้ลายเซ็นแบบ schnorr ร่วมกับการตรวจสอบแบบ batch แทนที่ด้วย opcode ใหม่คือ OP_CHECKSIGADD</p>
<p>เมื่อ OP_CHECKSIGADD ตรวจสอบลายเซ็นสำเร็จแล้ว จะเพิ่มค่าตัวนับขึ้นหนึ่ง ทำให้สามารถนับจำนวนลายเซ็นที่ผ่านการตรวจสอบได้อย่างสะดวก และนำไปเปรียบเทียบกับจำนวนลายเซ็นที่ต้องการ เพื่อสร้างพฤติกรรมเดียวกันกับที่ OP_CHECKMULTISIG เคยทำได้</p>
<h5>การเปลี่ยนแปลงของลายเซ็นทั้งหมด:</h5>
<p>การดำเนินการที่เกี่ยวข้องกับลายเซ็นทั้งหมดใน tapscript ใช้อัลกอริทึมลายเซ็นแบบ Schorr จามที่นิยามไว้ใน BIP340 เราจะอธิบายรายละเอียดของลายเซ็นแบบ schorr เพิ่มเติมในบทถัดไป นอกจากนี้ การดำเนินการตรวจสอบใด ๆ ที่คาดว่าจะไม่ผ่านการตรวจสอบ จะต้องได้รับค่า OP_FALSE (หรือที่เรียกว่า OP_0) แทนการส่งลายเซ็นจริงเข้าไป หากส่งค่าอื่นใดไปยังการตรวจสอบลายเซ็นที่ล้มเหลว สคริปต์ทั้งหมดจะล้มเหลวทันที กลไกนี้ยังช่วยสนับสนุนการตรวจสอบลายเซ็น Schnorr แบบ batch validation ได้อีกด้วย</p>
<h5>opcode กลุ่ม OP_SUCCESSx:</h5>
<p>opcode ที่ไม่สามารถใช้งานได้ใน script เวอร์ชันก่อนหน้าได้ถูกนิยามใหม่ให้เมื่อถูกเรียกใช้แล้วทำให้สคริปต์ทั้งหมดสำเร็จทันที (script succeeds) โดยกลไกนี้เปิดทางให้ soft fork ในอนาคตสามารถนิยามเงื่อนไขเพิ่มเติมได้ว่า opcode เหล่านี้จะไม่ทำให้สคริปต์สำเร็จในบางกรณี ซึ่งถือเป็นการ “จำกัดความสามารถลง” และจึงสามารถทำได้ผ่าน soft fork ในทางกลับกัน การเปลี่ยน opcode ที่เดิม ไม่ทำให้สคริปต์สำเร็จ ให้กลายเป็น opcode ที่ทำให้สคริปต์สำเร็จนั้น จะทำได้ผ่าน hard fork เท่านั้น ซึ่งเป็นแนวทางการอัปเกรดที่ยากและมีความเสี่ยงมากกว่า</p>
<p>แม้ว่าในบทนี้เราจะได้พิจรณาเรื่องการให้สิทธิ์และการยืนยันตัวตนอย่างละเอียดแล้ว แต่เรายังข้ามที่ส่วนสำคัญมากส่วนหนึ่งของวิธีที่บิตคอยน์ใช้ยืนยันผู้ใช้จ่ายไป นั่นคือลายเซ็น ซึ่งเราจะไปศึกษาในลำดับถัดไปในบทที่ 8 <img src="https://image.nostr.build/2eb9a47ba4fe6ecbc3d12dae8434d45ad15840655e6ee833420bb2ee307cf614.jpg" alt="image"></p>
]]></itunes:summary>
      <itunes:image href="https://image.nostr.build/2eb9a47ba4fe6ecbc3d12dae8434d45ad15840655e6ee833420bb2ee307cf614.jpg"/>
      </item>
      
      <item>
      <title><![CDATA[สรุป Mastering Bitcoin: Programming the Open Blockchain บทที่ 6]]></title>
      <description><![CDATA[ต้นฉบับแบบว่ายาวไปไม่อ่าน อ่านละปวดหัว มาดูสรุปดีกว่า แต่ดูทรงปวดหัวกว่าเดิม งั้นไปอ่านต้นฉบับล้ากานนนนนนนน]]></description>
             <itunes:subtitle><![CDATA[ต้นฉบับแบบว่ายาวไปไม่อ่าน อ่านละปวดหัว มาดูสรุปดีกว่า แต่ดูทรงปวดหัวกว่าเดิม งั้นไปอ่านต้นฉบับล้ากานนนนนนนน]]></itunes:subtitle>
      <pubDate>Wed, 26 Nov 2025 07:21:18 GMT</pubDate>
      <link>https://learnbn.npub.pro/post/lggw7qwvndvilmhvl5tz3/</link>
      <comments>https://learnbn.npub.pro/post/lggw7qwvndvilmhvl5tz3/</comments>
      <guid isPermaLink="false">naddr1qq25c368wumhz46kfej9v62vf458vnp423drxq3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65wg7tnyr</guid>
      <category>ลองฟอร์มของไดโน</category>
      
        <media:content url="https://image.nostr.build/4b81c193b6eb1d9d47353f31f88c3268884a8a2a3d74e771052448fec8a6f793.jpg" medium="image"/>
        <enclosure 
          url="https://image.nostr.build/4b81c193b6eb1d9d47353f31f88c3268884a8a2a3d74e771052448fec8a6f793.jpg" length="0" 
          type="image/jpeg" 
        />
      <noteId>naddr1qq25c368wumhz46kfej9v62vf458vnp423drxq3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65wg7tnyr</noteId>
      <npub>npub10yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2syrhcl3</npub>
      <dc:creator><![CDATA[Learning_BTC&NOSTR]]></dc:creator>
      <content:encoded><![CDATA[<h1>บทที่ 6:Transactions: ธุรกรรมในบิตคอยน์</h1>
<hr>
<p>วิธีที่เรามักใช้ในการโอนเงินสดนั้นแทบไม่เหมือนกับวิธีที่เราโอนบิตคอยน์เลย เงินสดจริงเป็นโทเค็นที่ผู้ใช้ถือครองได้โดยตรง เช่นเมื่ออลิซจ่ายให้บ็อบโดยการยื่นโทเค็นจำนวนหนึ่ง เช่น การส่งธนบัตรดอลลาร์ให้เขา แต่มันไม่ใช่แบบนั้นเลยสำหรับบิตคอยน์  บิตคอยน์ไม่ได้มีอยู่จริงทั้งในรูปแบบทางกายภาพหรือในรูปแบบข้อมูลดิจิทัล อลิซไม่สามารถยื่นบิตคอยน์ให้บ็อบหรือส่งให้ทางอีเมลได้</p>
<p>ลองจินตนการดูว่าหากอลิซต้องการโอนสิทธ์ในการครอบครองที่ดินผืนหนึ่งให้บ๊อบได้อย่างไร เธอไม่สามารถยกที่ดินขึ้นมาแล้วยื่นให้บ็อบได้ แต่จะมีบันทึกบางอย่าง (ที่โดยทั่วไปดูแลโดยหน่วยงานของรัฐท้องถิ่น) ที่ระบุว่าที่ดินผืนนั้นเป็นของอลิซ แปลว่าอลิซสามารถโอนที่ดินให้บ็อบโดยทำให้รัฐบาลอัปเดตบันทึกนั้นให้ระบุว่าบ็อบเป็นเจ้าของที่ดินแทนเธอ</p>
<p>บิตคอยน์ทำงานในลักษณะคล้ายกัน มีฐานข้อมูลของมันอยู่บนโหนดทุก ๆ โหนดของบิตคอยน์ ที่ระบุว่าอลิซควบคุมบิตคอยน์จำนวนหนึ่ง อลิซจ่ายให้บ็อบโดยทำให้โหนด เหล่านั้นอัปเดตฐานข้อมูลของพวกเขาให้ระบุว่าบิตคอยน์บางส่วนของอลิซตอนนี้อยู่ภายใต้การควบคุมของบ็อบ ข้อมูลที่อลิซใช้เพื่อทำให้โหนดอัปเดตฐานข้อมูลของพวกเขาเรียกว่า “ธุรกรรม” ซึ่งกระทำได้โดยไม่ต้องใช้ตัวตนของอลิซหรือบ็อบโดยตรง</p>
<p>ในบทนี้ เราจะมาวิเคราะห์โครงสร้างของธุรกรรมบิตคอยน์และพิจารณาแต่ละส่วนประกอบ เพื่อดูว่ามันเอื้อต่อการโอนมูลค่าได้อย่างไรในรูปแบบที่มีความยืดหยุ่นและน่าเชื่อถือได้อย่างไร</p>
<h2>Serialized Bitcoin Transaction: ธุรกรรมบิตคอยน์ในรูปแบบลำดับข้อมูล</h2>
<p>ในบทที่ 3 เราได้ใช้ Bitcoin Core โดยเปิดใช้งานตัวเลือก txindex เพื่อดึงสำเนาของธุรกรรมการชำระเงินที่อลิซส่งให้บ็อบ ทีนี้เรามาลองดึงธุรกรรมที่มีการชำระเงินนั้นอีกครั้ง โดยจะแสดงอยู่ในธุรกรรมแบบลำดับข้อมูลของอลิซแทน</p>
<pre><code>$ bitcoin-cli getrawtransaction 466200308696215bbc949d5141a49a41\
38ecdfdfaa2a8029c1f9bcecd1f96177

01000000000101eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da13569
8679268041c54a0100000000ffffffff02204e0000000000002251203b41daba
4c9ace578369740f15e5ec880c28279ee7f51b07dca69c7061e07068f8240100
000000001600147752c165ea7be772b2c0acb7f4d6047ae6f4768e0141cf5efe
2d8ef13ed0af21d4f4cb82422d6252d70324f6f4576b727b7d918e521c00b51b
e739df2f899c49dc267c0ad280aca6dab0d2fa2b42a45182fc83e81713010000
0000
</code></pre>
<p>รูปแบบการลำดับข้อมูลของ Bitcoin Core นั้นมีความพิเศษ เพราะเป็นรูปแบบที่ใช้ในการสร้างคอมมิตเมนต์ของธุรกรรมและส่งต่อธุรกรรมนั้นผ่านเครือข่าย P2P ของบิตคอยน์ อย่างไรก็ตาม โปรแกรมอื่น ๆ สามารถใช้รูปแบบที่ต่างออกไปได้ ตราบใดที่มีการส่งข้อมูลชุดเดียวกันทั้งหมด แต่เนื่องจากรูปแบบของ Bitcoin Core มีความกระทัดรัดเมื่อเทียบกับปริมาณข้อมูลที่ส่ง และยังสามารถแยกวิเคราะห์ได้ง่าย โปรแกรมบิตคอยน์อื่น ๆ จำนวนมากจึงเลือกใช้รูปแบบนี้เช่นกัน</p>
<blockquote>
<p>Tip: รูปแบบการลำดับธุรกรรมอีกแบบหนึ่งที่ถูกใช้อย่างแพร่หลายที่เราทราบคือ partially signed bitcoin transaction (PSBT) ซึ่งมีการอธิบายไว้ใน BIP 174 และ BIP 370 (รวมถึงส่วนขยายที่ระบุไว้ใน BIP อื่น ๆ ) PSBT อนุญาตให้โปรแกรมที่ไม่น่าเชื่อถือสร้างเทมเพลตของธุรกรรมขึ้นมา เพื่อให้โปรแกรมที่เชื่อถือได้ (เช่น อุปกรณ์ฮาร์ดแวร์ที่ใช้ลงนามธุรกรรม) ซึ่งถือกุญแจส่วนตัวหรือข้อมูลสำคัญอื่น ๆ ที่จำเป็น สามารถตรวจสอบและเติมข้อมูลในเทมเพลตนั้นได้ เพื่อให้ทำเช่นนี้ได้ PSBT จึงรองรับการจัดเก็บข้อมูลเมตา (metadata) จำนวนมากเกี่ยวกับธุรกรรม ซึ่งทำให้มันมีขนาดใหญ่และไม่กระทัดรัดเท่ารูปแบบการลำดับข้อมูลมาตรฐาน หนังสือเล่มนี้จะไม่ลงรายละเอียดเกี่ยวกับ PSBT แต่เราแนะนำอย่างยิ่งสำหรับนักพัฒนา wallet ที่ต้องการรองรับการเซ็นชื่อด้วยหลายกุญแจ</p>
</blockquote>
<p>ธุรกรรมที่แสดงในรูปเลขฐาน 16 ข้างต้น ถูกจำลองเป็นแผนผังไบต์ในรูปข้างล่างนี้ โปรดสังเกตว่าการแสดงข้อมูลขนาด 32 ไบต์ต้องใช้เลขฐานสิบหกจำนวน 64 ตัว แผนผังนี้แสดงเฉพาะฟิลด์ในระดับบนสุดเท่านั้น เราจะมาพิจารณาแต่ละฟิลด์ตามลำดับที่มันปรากฏในธุรกรรม พร้อมอธิบายฟิลด์เพิ่มเติมที่อยู่ภายในแต่ละส่วน</p>
<p> <img src="https://image.nostr.build/14264e82a143c3c72a68453c951fb6ec4e0a542186c365eee5483108c9de1be7.jpg" alt="image"></p>
<h2>Version</h2>
<p>ไบต์สี่ตัวแรกของธุรกรรมบิตคอยน์ในรูปแบบลำดับข้อมูล คือ “เวอร์ชัน” ของมัน เวอร์ชันดั้งเดิมของธุรกรรมบิตคอยน์คือเวอร์ชัน 1 (0x01000000) ธุรกรรมทั้งหมดในระบบบิตคอยน์ต้องปฏิบัติตามกฎของธุรกรรมเวอร์ชัน 1 ซึ่งหลายส่วนของหนังสือเล่มนี้ได้อธิบายกฎเหล่านั้นไว้แล้ว</p>
<p>ธุรกรรมบิตคอยน์เวอร์ชัน 2 ถูกนำมาใช้ครั้งแรกในการอัปเกรดแบบซอฟต์ฟอร์กตามข้อเสนอ BIP68 ซึ่งเพิ่มข้อจำกัดเพิ่มเติมกับฟิลด์ sequence แต่ข้อจำกัดนี้จะมีผลเฉพาะกับธุรกรรมเวอร์ชัน 2 ขึ้นไปเท่านั้น ส่วนธุรกรรมเวอร์ชัน 1 จะไม่ได้รับผลกระทบ ข้อเสนอ BIP112 ซึ่งเป็นส่วนหนึ่งของซอฟต์ฟอร์กเดียวกันกับ BIP68 ได้อัปเกรดคำสั่ง OP_CHECKSEQUENCEVERIFY โดยทำให้คำสั่งนี้ล้มเหลวหากถูกประเมินในธุรกรรมที่มีเวอร์ชันต่ำกว่า 2 นอกเหนือจากการเปลี่ยนแปลงสองจุดนี้ ธุรกรรมเวอร์ชัน 2 จะเหมือนกับเวอร์ชัน 1 ทุกประการ</p>
<h3>Protecting Presigned Transactions</h3>
<p>ขั้นตอนสุดท้ายก่อนการกระจายธุรกรรมเข้าสู่เครือข่ายเพื่อบันทึกลงในบล็อกเชนคือ “การลงนาม” ธุรกรรม อย่างไรก็ตาม สามารถลงนามในธุรกรรมได้โดยยังไม่ต้องกระจายออกไปทันที คุณสามารถเก็บธุรกรรมที่ลงนามล่วงหน้าไว้นานหลายเดือนหรือหลายปี โดยเชื่อว่าจะสามารถนำมาบันทึกลงบล็อกเชนได้ในภายหลังเมื่อมีการกระจายธุรกรรมนั้น ในระหว่างนั้นคุณอาจสูญเสียการเข้าถึง private key ที่จำเป็นสำหรับการเซ็นธุรกรรมอื่นเพื่อใช้จ่ายเงินเดียวกันได้ ปรากฏการณ์นี้ไม่ใช่เรื่องสมมติ โปรโตคอลหลายอย่างที่สร้างบนบิตคอยน์ เช่น Lightning Network พึ่งพาธุรกรรมที่ลงนามล่วงหน้าเหล่านี้</p>
<p>สิ่งนี้สร้างความท้าทายให้กับนักพัฒนาโปรโตคอล เมื่อพวกเขาต้องช่วยผู้ใช้อัปเกรดโปรโตคอลฉันทามติของบิตคอยน์ การเพิ่มข้อจำกัดใหม่ เช่นที่ BIP68 เพิ่มไว้ในฟิลด์ sequence  อาจทำให้ธุรกรรมที่ลงนามล่วงหน้าบางรายการกลายเป็นโมฆะ และหากไม่สามารถสร้างลายเซ็นใหม่สำหรับธุรกรรมที่เทียบเท่ากันได้ เงินในธุรกรรมเหล่านั้นก็จะสูญหายไปอย่างถาวร</p>
<p>ปัญหานี้ได้รับการแก้ไขโดยการ “สงวน” ฟีเจอร์บางอย่างของธุรกรรมไว้สำหรับการอัปเกรดในอนาคต เช่น หมายเลขเวอร์ชัน ผู้ที่สร้างธุรกรรมแบบลงนามล่วงหน้าก่อน BIP68 ควรใช้ธุรกรรมเวอร์ชัน 1 ดังนั้นการที่ BIP68 กำหนดข้อจำกัดเพิ่มเติมให้มีผลเฉพาะกับธุรกรรมเวอร์ชัน 2 ขึ้นไป จึงไม่ทำให้ธุรกรรมที่ลงนามไว้ก่อนหน้านั้นเป็นโมฆะ</p>
<p>หากคุณกำลังพัฒนาโปรโตคอลที่ใช้ธุรกรรมแบบลงนามล่วงหน้า ให้แน่ใจว่าโปรโตคอลของคุณไม่ได้ใช้ฟีเจอร์ที่ถูกสงวนไว้สำหรับการอัปเกรดในอนาคต นโยบายการส่งต่อธุรกรรม (relay policy) เริ่มต้นของ Bitcoin Core ไม่อนุญาตให้ใช้ฟีเจอร์ที่ถูกสงวนเหล่านี้ คุณสามารถทดสอบว่าธุรกรรมของคุณสอดคล้องกับนโยบายดังกล่าวหรือไม่ โดยใช้คำสั่ง RPC testmempoolaccept บน Bitcoin mainnet</p>
<p>ณ เวลาที่เขียนนี้ มีข้อเสนอให้เริ่มใช้ธุรกรรมเวอร์ชัน 3 อย่างแพร่หลาย ข้อเสนอนี้ไม่ได้เปลี่ยนแปลงกฎฉันทามติของบิตคอยน์ แต่เปลี่ยนเฉพาะนโยบายที่โหนดใช้ในการส่งต่อธุรกรรม โดยภายใต้ข้อเสนอนั้น ธุรกรรมเวอร์ชัน 3 จะถูกกำหนดข้อจำกัดเพิ่มเติม เพื่อป้องกันการโจมตีแบบ Denial of Service (DoS) บางรูปแบบ ซึ่งเราจะกล่าวถึงเพิ่มเติมในส่วนถัดไป</p>
<h2>Extended Marker and Flag: ตัวระบุ Marker และ Flag แบบขยาย</h2>
<p>ฟิลด์สองช่องถัดไปของตัวอย่างธุรกรรมในรูปแบบลำดับข้อมูล ถูกเพิ่มเข้ามาในกระบวนการอัปเกรดแบบ soft fork ของ Segregated Witness (SegWit) ซึ่งเป็นการเปลี่ยนแปลงในกฎฉันทามติของบิตคอยน์ การเปลี่ยนแปลงนี้อ้างอิงตามข้อเสนอ BIP141 และ BIP143 โดยที่รูปแบบการลำดับข้อมูลแบบขยายนี้ถูกกำหนดไว้ใน BIP144</p>
<p>หากธุรกรรมมีโครงสร้างของ witness (ซึ่งเราจะอธิบายในหัวข้อ “Witness Structure”) ค่า marker ต้องเป็นศูนย์ (0x00) และค่า flag ต้องไม่เป็นศูนย์ ในโปรโตคอล P2P ปัจจุบัน ค่าของ flag ควรเป็นหนึ่ง (0x01) เสมอ โดยมีการสงวนค่าอื่นไว้สำหรับการอัปเกรดโปรโตคอลในอนาคต</p>
<p>หากธุรกรรมไม่มีข้อมูล witness stack ค่า marker และ flag จะต้องไม่ปรากฏอยู่ในธุรกรรม ซึ่งทำให้สามารถทำงานร่วมกับรูปแบบการลำดับข้อมูลดั้งเดิมของบิตคอยน์ได้ หรือที่ปัจจุบันเรียกว่า legacy serialization (รายละเอียดเพิ่มเติมดูได้ในหัวข้อ “Legacy Serialization”)</p>
<p>ในรูปแบบ legacy การตีความค่า marker จะถือว่าเป็นจำนวนของอินพุต (ซึ่งในกรณีนี้คือศูนย์) แต่ธุรกรรมไม่สามารถมีอินพุตเป็นศูนย์ได้ ดังนั้น marker จึงทำหน้าที่เป็นสัญญาณให้โปรแกรมรุ่นใหม่รู้ว่า ธุรกรรมนี้ใช้รูปแบบการลำดับข้อมูลแบบขยาย ส่วนฟิลด์ flag ก็ทำหน้าที่ในลักษณะเดียวกัน และยังช่วยให้การอัปเดตรูปแบบการลำดับข้อมูลในอนาคตเป็นไปได้ง่ายขึ้นอีกด้วย</p>
<h2>Inputs</h2>
<p>ฟิลด์ inputs ประกอบด้วยฟิลด์ย่อยหลายส่วน ดังนั้นเราจะมาเริ่มกันจากการดูแผนผังไบต์ของฟิลด์นี้ในตัวอย่างธุรกรรมของอลิซ ที่แสดงไว้ในภาพด้านล่าง<br> <img src="https://image.nostr.build/604cd0c7982b98dde29a0a676db0f8d28e080317d20a1a81a886730139ff3b11.jpg" alt="image"></p>
<h3>Length of Transaction Input List: ความยาวของรายการอินพุตของธุรกรรม</h3>
<p>รายการอินพุตของธุรกรรมจะเริ่มต้นด้วยจำนวนเต็มที่ระบุจำนวนอินพุตทั้งหมดในธุรกรรมนั้น ค่าต่ำสุดคือหนึ่ง และแม้จะไม่มีการกำหนดค่าสูงสุดอย่างชัดเจน แต่ข้อจำกัดของขนาดธุรกรรมสูงสุดในบิตคอยน์จะจำกัดให้ธุรกรรมหนึ่งมีได้เพียงไม่กี่พันอินพุตเท่านั้น ตัวเลขนี้จะถูกเข้ารหัสในรูปแบบ compactSize unsigned integer</p>
<h3>CompactSize Unsigned Integers: จำนวนเต็มแบบ CompactSize</h3>
<p>จำนวนเต็มที่ไม่มีเครื่องหมาย (unsigned integers) ในบิตคอยน์ ซึ่งโดยทั่วไปมักมีค่าต่ำ แต่บางครั้งอาจมีค่าสูง จะถูกเข้ารหัสโดยใช้ชนิดข้อมูลที่เรียกว่า compactSize ซึ่งเป็นรูปแบบของจำนวนเต็มแบบความยาวแปรผัน (variable-length integer) จึงมักถูกเรียกว่า var_int หรือ varint (ดูรายละเอียดเพิ่มเติมได้ในเอกสารของ BIP37 และ BIP144)</p>
<blockquote>
<p>warning: มีรูปแบบของ “จำนวนเต็มแบบความยาวแปรผัน” (variable-length integers) หลายแบบที่ถูกใช้ในโปรแกรมต่าง ๆ รวมถึงโปรแกรมในระบบบิตคอยน์เองด้วย ตัวอย่างเช่น Bitcoin Core ใช้ชนิดข้อมูลที่เรียกว่า VarInts ในการจัดเก็บฐานข้อมูล UTXO ซึ่ง แตกต่างจาก compactSize นอกจากนี้ ฟิลด์ nBits ในส่วนหัวของบล็อก (block header) ก็ถูกเข้ารหัสด้วยชนิดข้อมูลเฉพาะที่เรียกว่า Compact ซึ่งก็ไม่เกี่ยวข้องกับ compactSize เช่นกัน ดังนั้น เมื่อเราอ้างถึงจำนวนเต็มแบบความยาวแปรผันที่ใช้ในการ serialize ธุรกรรมบิตคอยน์ (Bitcoin transaction) และในส่วนอื่นของ โปรโตคอล P2P ของบิตคอยน์ เราจะใช้คำเต็มว่า compactSize เสมอ เพื่อป้องกันความสับสนกับรูปแบบอื่น ๆ ที่ชื่อคล้ายกันแต่มีโครงสร้างต่างกัน</p>
</blockquote>
<p>สำหรับตัวเลขในช่วง 0 ถึง 252 ค่าของ compactSize unsigned integers จะเหมือนกับชนิดข้อมูล uint8_t ในภาษา C ทุกประการ ซึ่งเป็นรูปแบบการเข้ารหัสตัวเลขที่โปรแกรมเมอร์ส่วนใหญ่คุ้นเคยอยู่แล้ว แต่สำหรับตัวเลขที่มีค่ามากกว่า 252 (จนถึง 0xffffffffffffffff) จะมีการเพิ่ม ไบต์นำหน้า (prefix byte) เพื่อระบุความยาวของจำนวนตัวเลขนั้น อย่างไรก็ตาม นอกจากไบต์นำหน้าแล้ว ตัวเลขส่วนที่เหลือจะยังคงมีลักษณะเหมือนกับการเข้ารหัสของจำนวนเต็มแบบ unsigned ปกติในภาษา C อยู่ดี</p>
<table>
<thead>
<tr>
<th align="left">ค่าตัวเลข (Value)</th>
<th>จำนวนไบต์ที่ใช้ (Bytes used)</th>
<th>รูปแบบ (Format)</th>
</tr>
</thead>
<tbody><tr>
<td align="left">≥ 0 และ ≤ 252 (0xfc)</td>
<td>1</td>
<td>เข้ารหัสเป็น uint8_t</td>
</tr>
<tr>
<td align="left">≥ 253 และ ≤ 0xffff</td>
<td>3</td>
<td>เริ่มด้วย 0xfd ตามด้วยตัวเลขที่เข้ารหัสเป็น uint16_t</td>
</tr>
<tr>
<td align="left">≥ 0x10000 และ ≤ 0xffffffff</td>
<td>5</td>
<td>เริ่มด้วย 0xfe ตามด้วยตัวเลขที่เข้ารหัสเป็น uint32_t</td>
</tr>
<tr>
<td align="left">≥ 0x100000000 และ ≤ 0xffffffffffffffff</td>
<td>9</td>
<td>เริ่มด้วย 0xff ตามด้วยตัวเลขที่เข้ารหัสเป็น uint64_t</td>
</tr>
</tbody></table>
<p>อินพุตแต่ละรายการในธุรกรรมจะต้องมีสามฟิลด์หลัก ได้แก่ Outpoint field, Length-prefixed input script field, และ Sequence</p>
<p>เราจะพิจารณาแต่ละฟิลด์เหล่านี้ในส่วนถัดไป อินพุตบางรายการยังมี witness stack ด้วย แต่จะถูกจัดลำดับข้อมูลไว้ที่ท้ายธุรกรรม ดังนั้นเราจะมาศึกษามันในภายหลัง</p>
<h3>Outpoint</h3>
<p>ธุรกรรมบิตคอยน์คือคำขอที่ส่งให้ full node ทำการอัปเดตฐานข้อมูลเกี่ยวกับข้อมูลความเป็นเจ้าของเหรียญ สำหรับอลิซที่จะโอนบิตคอยน์บางส่วนของเธอให้บ๊อบ เธอต้องบอกให้โหนดรู้ก่อนว่าจะหาธุรกรรมก่อนหน้าที่เธอได้รับบิตคอยน์เหล่านั้นได้จากที่ไหน เนื่องจากการควบคุมบิตคอยน์ถูกกำหนดไว้ใน output ของธุรกรรม อลิซจึงชี้ไปยัง output ก่อนหน้าโดยใช้ฟิลด์ outpoint อินพุตแต่ละรายการต้องมี outpoint หนึ่งรายการเสมอ</p>
<p>outpoint ประกอบด้วย txid ขนาด 32 ไบต์ของธุรกรรมที่อลิซได้รับบิตคอยน์ที่เธอต้องการใช้จ่าย txid นี้อยู่ในลำดับไบต์ภายในของบิตคอยน์สำหรับแฮช (ดูหัวข้อ Internal and Display Byte Orders)</p>
<p>เนื่องจากธุรกรรมหนึ่งรายการอาจมีหลาย output อลิซจึงต้องระบุด้วยว่าเธอจะใช้ output ใดจากธุรกรรมนั้น ซึ่งเรียกว่า output index โดย output index เป็นจำนวนเต็มแบบไม่ติดลบ (unsigned integer) ขนาด 4 ไบต์ เริ่มจากศูนย์</p>
<p>เมื่อ full node พบ outpoint มันจะใช้ข้อมูลนี้เพื่อค้นหา output ที่ถูกอ้างอิง โหนดเต็มจะต้องค้นหาเฉพาะธุรกรรมก่อนหน้าในบล็อกเชนเท่านั้น ตัวอย่างเช่น หากธุรกรรมของอลิซถูกบันทึกไว้ในบล็อก 774,958  full node ที่ตรวจสอบธุรกรรมของเธอจะค้นหา output ที่ถูกอ้างอิงในบล็อกนั้นและบล็อกก่อนหน้าเท่านั้น ไม่ใช่บล็อกที่อยู่หลังจากนั้น ภายในบล็อก 774,958 full node จะค้นหาเฉพาะธุรกรรมที่อยู่ก่อนธุรกรรมของอลิซ โดยพิจารณาตามลำดับของ leaf ใน merkle tree ของบล็อกนั้น </p>
<p>เมื่อพบ output ก่อนหน้าแล้ว full node จะได้รับข้อมูลสำคัญหลายอย่างจากมัน ได้แก่</p>
<ul>
<li><p>จำนวนบิตคอยน์ที่ถูกกำหนดไว้ใน output ก่อนหน้า: บิตคอยน์ทั้งหมดใน output นั้นจะถูกโอนไปในธุรกรรมนี้ ตัวอย่างเช่น ในธุรกรรมตัวอย่าง ค่าใน output ก่อนหน้าคือ 100,000 satoshis</p>
</li>
<li><p>เงื่อนไขการอนุญาตของ output ก่อนหน้า: เงื่อนไขที่ต้องถูกปฏิบัติตามเพื่อที่จะใช้จ่ายบิตคอยน์ที่ถูกกำหนดไว้ใน output นั้น</p>
</li>
<li><p>สำหรับธุรกรรมที่ได้รับการยืนยันแล้ว (confirmed transactions) full node จะทราบ block height ที่ธุรกรรมได้รับการยืนยัน และ ค่า median time past (MTP) ของบล็อกนั้น ข้อมูลนี้จำเป็นสำหรับการทำงานของ relative timelock (อธิบายไว้ในหัวข้อ Sequence as a consensus-enforced relative timelock) และสำหรับ output ของ coinbase transaction (อธิบายไว้ในหัวข้อ Coinbase Transactions)</p>
</li>
<li><p>หลักฐานว่า output ก่อนหน้านั้นมีอยู่จริงในบล็อกเชน (หรือเป็นธุรกรรมที่ยังไม่ได้ยืนยันแต่เป็นที่รู้จัก) และยังไม่มีธุรกรรมอื่นใดที่ใช้มันไปแล้ว หนึ่งในกฎฉันทามติของบิตคอยน์ห้ามไม่ให้ output เดียวกันถูกใช้มากกว่าหนึ่งครั้งภายในบล็อกเชน นี่คือกฎที่ป้องกัน double spending อลิซไม่สามารถใช้ output เดิมเดียวกันเพื่อจ่ายทั้งให้บ๊อบและแครอลในธุรกรรมที่แยกกันได้ สองธุรกรรมที่พยายามใช้ output เดียวกันเรียกว่า conflicting transactions เพราะมีเพียงหนึ่งในนั้นเท่านั้นที่สามารถถูกบันทึกในบล็อกเชนได้</p>
</li>
</ul>
<p>แนวทางที่แตกต่างกันในการติดตาม output ก่อนหน้าได้ถูกทดลองใช้โดยการทำงานของ full node ที่แตกต่างกันในช่วงเวลาต่าง ๆ ส่วนในปัจจุบัน Bitcoin Core ใช้วิธีที่เชื่อว่าได้ผลดีที่สุดในการเก็บรักษาข้อมูลที่จำเป็นทั้งหมดในขณะที่ลดการใช้พื้นที่ดิสก์ให้น้อยที่สุด คือมันเก็บฐานข้อมูลที่บันทึกทุก UTXO และเมตาดาต้าที่จำเป็นเกี่ยวกับมัน (เช่น block height) ทุกครั้งที่บล็อกใหม่ของธุรกรรมเข้ามา output ทั้งหมดที่ถูกใช้จะถูกลบออกจากฐานข้อมูล UTXO และ output ทั้งหมดที่ถูกสร้างขึ้นใหม่จะถูกเพิ่มเข้าไปในฐานข้อมูล</p>
<h3>Internal and Display Byte Orders(การจัดเรียงไบต์ภายในและสำหรับการแสดงผล)</h3>
<p>บิตคอยน์ได้ใช้ hash function ที่เรียกว่า digest ในหลายรูปแบบ Digest ถูกใช้เป็นตัวระบุเฉพาะสำหรับบล็อกและธุรกรรม ใช้ในกระบวนการยืนยันความถูกต้องของ address บล็อก ธุรกรรม ลายเซ็น และอื่น ๆ อีกมากมาย นอกจากนี้ digest ยังถูกนำมาใช้ซ้ำในฟังก์ชัน proof-of-work ของ Bitcoin ในบางกรณี digest ของ hash จะถูกแสดงให้ผู้ใช้เห็นในรูปแบบการจัดเรียงไบต์หนึ่ง แต่ระบบภายในกลับใช้รูปแบบการจัดเรียงไบต์อีกแบบหนึ่ง ซึ่งอาจก่อให้เกิดความสับสนได้ ตัวอย่างเช่น ลองพิจารณา txid ของ output ก่อนหน้าใน outpoint ของธุรกรรมตัวอย่างของเรา</p>
<pre><code>eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da135698679268041c54a
</code></pre>
<p>ถ้าเราพยายามใช้ txid นั้นเพื่อดึงข้อมูลธุรกรรมจาก Bitcoin Core เราจะพบข้อผิดพลาด และจำเป็นต้องสลับลำดับไบต์ของมันก่อน</p>
<pre><code>$ bitcoin-cli getrawtransaction \
  eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da135698679268041c54a
error code: -5
error message:
No such mempool or blockchain transaction.
Use gettransaction for wallet transactions.
$ echo eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da135698679268041c54a \
  | fold -w2 | tac | tr -d "\n"
4ac541802679866935a19d4f40728bb89204d0cac90d85f3a51a19278fe33aeb
$ bitcoin-cli getrawtransaction \
  4ac541802679866935a19d4f40728bb89204d0cac90d85f3a51a19278fe33aeb
02000000000101c25ae90c9f3d40cc1fc509ecfd54b06e35450702…
</code></pre>
<p>พฤติกรรมแปลก ๆ นี้น่าจะเป็นผลที่เกิดขึ้นโดยไม่ได้ตั้งใจจากการตัดสินใจออกแบบในซอฟต์แวร์บิตคอยน์ยุคแรก ๆ ในทางปฏิบัติ นั่นหมายความว่านักพัฒนาซอฟต์แวร์บิตคอยน์จำเป็นต้องจำไว้ว่าต้องสลับลำดับไบต์ในตัวระบุธุรกรรมและบล็อกก่อนที่จะแสดงให้ผู้ใช้เห็น</p>
<p>ในหนังสือเล่มนี้ เราใช้คำว่า ลำดับไบต์ภายใน (internal byte order) สำหรับข้อมูลที่ปรากฏอยู่ภายในธุรกรรมและบล็อก และใช้คำว่า ลำดับไบต์สำหรับแสดงผล (display byte order) สำหรับรูปแบบที่แสดงต่อผู้ใช้ อีกชุดของคำที่มักใช้กันคือ ลำดับไบต์แบบ little-endian สำหรับรูปแบบภายใน และ ลำดับไบต์แบบ big-endian สำหรับรูปแบบที่แสดงผล</p>
<blockquote>
<p>อันนี้หลามเสริมให้ มันคือการแบ่งทีละสองตำแหน่งและนับว่านั่นเป็นหนึ่ง เช่น abcdef ก็จะเป็น ab คือ 1 cd คือ 2 ef คือ 3 พอแปลงเป็น display byte order ก็ทำการสลับเป็น 3,2,1 หรือ efcdab</p>
</blockquote>
<h2>Input Script</h2>
<p>ฟิลด์ input script เป็นส่วนที่หลงเหลือมาจากรูปแบบธุรกรรมแบบดั้งเดิม (legacy transaction format) ตัวอย่างธุรกรรมของเราเป็น input ที่ใช้จ่าย output แบบ native segwit ซึ่งไม่จำเป็นต้องมีข้อมูลใน input script ดังนั้นค่านำหน้าความยาว (length prefix) ของ input script จึงถูกตั้งค่าเป็นศูนย์ (0x00)</p>
<p>สำหรับตัวอย่างของ input script ที่มีค่านำหน้าความยาวและใช้จ่าย output แบบดั้งเดิม เราจะใช้ตัวอย่างจากธุรกรรมหนึ่งในบล็อกล่าสุดในขณะที่เขียนอยู่นี้</p>
<pre><code>6b483045022100a6cc4e8cd0847951a71fad3bc9b14f24d44ba59d19094e0a8c
fa2580bb664b020220366060ea8203d766722ed0a02d1599b99d3c95b97dab8e
41d3e4d3fe33a5706201210369e03e2c91f0badec46c9c903d9e9edae67c167b
9ef9b550356ee791c9a40896
</code></pre>
<p>ค่านำหน้าความยาว (length prefix) เป็นจำนวนเต็มแบบ compactSize unsigned integer ที่ระบุความยาวของฟิลด์ input script ที่ถูกซีเรียลไลซ์ไว้ ในกรณีนี้เป็นไบต์เดียว (0x6b) ซึ่งระบุว่า input script มีความยาว 107 ไบต์</p>
<h2>Sequence(ลำดับ)</h2>
<p>ไบต์สี่ตัวสุดท้ายของ input คือหมายเลขลำดับ (sequence number) ซึ่งการใช้งานและความหมายของฟิลด์นี้ได้มีการเปลี่ยนแปลงไปตามกาลเวลา</p>
<h3>การแทนที่ธุรกรรมโดยอิงตามลำดับ (Original sequence-based transaction replacement)</h3>
<p>เดิมทีฟิลด์ sequence ถูกออกแบบมาเพื่อให้สามารถสร้างหลายเวอร์ชันของธุรกรรมเดียวกันได้ โดยที่เวอร์ชันหลังสามารถแทนที่เวอร์ชันก่อนหน้าในฐานะตัวเลือกสำหรับการยืนยัน หมายเลขลำดับทำหน้าที่ติดตามเวอร์ชันของธุรกรรม</p>
<p>ตัวอย่างเช่น ลองจินตนาการว่าอลิซและบ็อบต้องการเดิมพันผลเกมไพ่ พวกเขาเริ่มจากการลงนามธุรกรรมที่แต่ละคนฝากเงินไว้ในเอาต์พุตซึ่งต้องการลายเซ็นจากทั้งคู่เพื่อใช้จ่าย ซึ่งเรียกว่า สคริปต์หลายลายเซ็น (multisignature script หรือ multisig) ธุรกรรมนี้เรียกว่า setup transaction จากนั้นพวกเขาสร้างธุรกรรมที่ใช้จ่ายเอาต์พุตนั้นดังนี้</p>
<ul>
<li>เวอร์ชันแรกของธุรกรรม โดยมีค่า <code>nSequence</code> เท่ากับ 0 (0x00000000) เป็นการคืนเงินให้อลิซและบ็อบตามจำนวนที่แต่ละคนฝากไว้ในตอนแรก ธุรกรรมนี้เรียกว่า <em>refund transaction</em> ซึ่งทั้งคู่ยังไม่เผยแพร่ในตอนนี้ จะใช้ก็ต่อเมื่อเกิดปัญหาเท่านั้น  </li>
<li>อลิซชนะรอบแรกของเกมไพ่ ธุรกรรมเวอร์ชันที่สองมีค่า <em>sequence</em> เป็น 1 และปรับจำนวนเงินที่อลิซได้รับให้มากขึ้น ในขณะที่ส่วนของบ็อบลดลง ทั้งคู่ลงนามในธุรกรรมที่อัปเดตแล้วนี้อีกครั้ง แต่ก็ยังไม่เผยแพร่ เว้นแต่จะมีเหตุจำเป็น  </li>
<li>บ็อบชนะรอบที่สอง ลำดับจึงถูกเพิ่มเป็น 2 ส่วนแบ่งของอลิซลดลงและของบ็อบเพิ่มขึ้น ทั้งคู่ลงนามอีกครั้งแต่ยังไม่เผยแพร่</li>
</ul>
<p>หลังจากเล่นอีกหลายรอบ โดยที่ค่า sequence ถูกเพิ่มขึ้นในแต่ละรอบ เงินถูกปรับสมดุลใหม่ และธุรกรรมถูกลงนามแต่ยังไม่เผยแพร่ พวกเขาตัดสินใจจะสิ้นสุดเกม ด้วยการสร้างธุรกรรมสุดท้ายที่สะท้อนยอดเงินสุดท้ายของทั้งคู่ จากนั้นตั้งค่า sequence เป็นค่าสูงสุด (0xffffffff) เพื่อเป็นการ “สิ้นสุด” ธุรกรรมนี้ พวกเขาจึงเผยแพร่เวอร์ชันนี้ออกไป ธุรกรรมถูกส่งต่อไปทั่วเครือข่าย และในที่สุดก็ได้รับการยืนยันโดยนักขุด</p>
<p>เราสามารถเห็นกฎการแทนที่ของ sequence ทำงานอย่างไรได้ หากพิจารณาสถานการณ์ทางเลือกดังต่อไปนี้</p>
<ul>
<li><p>ลองจินตนาการว่าอลิซเผยแพร่ธุรกรรมสุดท้ายที่มีค่า sequence เท่ากับ 0xffffffff จากนั้นบ็อบเผยแพร่ธุรกรรมเวอร์ชันก่อนหน้าที่เขาได้รับส่วนแบ่งมากกว่า เนื่องจากเวอร์ชันของบ็อบมีหมายเลขลำดับต่ำกว่า โหนดเต็ม (full nodes) ที่ใช้โค้ดบิตคอยน์เวอร์ชันดั้งเดิมจะไม่ส่งต่อธุรกรรมนั้นไปยังนักขุด และนักขุดที่ใช้โค้ดเวอร์ชันเดิมก็จะไม่ขุดมันเช่นกัน</p>
</li>
<li><p>ในอีกสถานการณ์หนึ่ง สมมติว่าบ็อบเผยแพร่ธุรกรรมเวอร์ชันก่อนหน้าเพียงไม่กี่วินาทีก่อนที่อลิซจะเผยแพร่เวอร์ชันสุดท้าย โหนดต่าง ๆ จะส่งต่อเวอร์ชันของบ็อบและนักขุดจะพยายามขุดมัน แต่เมื่อเวอร์ชันของอลิซที่มีหมายเลขลำดับสูงกว่ามาถึง โหนดก็จะส่งต่อเวอร์ชันของอลิซด้วย และนักขุดที่ใช้โค้ดบิตคอยน์เวอร์ชันดั้งเดิมจะพยายามขุดเวอร์ชันของอลิซแทนเวอร์ชันของบ็อบ เว้นแต่ว่าบ็อบจะโชคดีและมีการค้นพบบล็อกก่อนที่เวอร์ชันของอลิซจะมาถึง มิฉะนั้นธุรกรรมเวอร์ชันของอลิซจะเป็นเวอร์ชันที่ได้รับการยืนยันในที่สุด</p>
</li>
</ul>
<p>โปรโตคอลประเภทนี้คือสิ่งที่เราเรียกว่า payment channel ในปัจจุบัน ผู้สร้างบิตคอยน์ได้กล่าวถึงในอีเมลที่มีการอ้างถึงเขา เรียกธุรกรรมประเภทนี้ว่า high-frequency transactions และได้อธิบายคุณสมบัติบางอย่างที่เพิ่มเข้าไปในโปรโตคอลเพื่อรองรับการใช้งานเหล่านี้ เราจะเรียนรู้เกี่ยวกับคุณสมบัติอื่น ๆ เหล่านี้ในภายหลัง และยังจะได้เห็นด้วยว่าเวอร์ชันสมัยใหม่ของ payment channel ถูกนำมาใช้ในบิตคอยน์อย่างแพร่หลายมากขึ้นในปัจจุบัน</p>
<p>มีปัญหาบางประการกับ payment channel ที่อิงตาม sequence เพียงอย่างเดียว ปัญหาแรกคือกฎสำหรับการแทนที่ธุรกรรมที่มี sequence ต่ำด้วยธุรกรรมที่มี sequence สูงเป็นเพียงนโยบายของซอฟต์แวร์เท่านั้น ไม่มีแรงจูงใจโดยตรงให้นักขุดชอบเวอร์ชันใดเวอร์ชันหนึ่งมากกว่าเวอร์ชันอื่น ปัญหาที่สองคือคนแรกที่ส่งธุรกรรมอาจโชคดีให้ธุรกรรมนั้นได้รับการยืนยัน แม้ว่าจะไม่ใช่ธุรกรรมที่มี sequence สูงสุดก็ตาม โปรโตคอลด้านความปลอดภัยที่ล้มเหลวบ้างเพียงไม่กี่เปอร์เซ็นต์เพราะโชคไม่ดีจึงไม่ใช่โปรโตคอลที่มีประสิทธิภาพนัก</p>
<p>ปัญหาที่สามคือสามารถแทนที่เวอร์ชันหนึ่งของธุรกรรมด้วยเวอร์ชันที่ต่างออกไปได้โดยไม่จำกัดจำนวนครั้ง แต่ละครั้งที่แทนที่จะใช้แบนด์วิดท์ของโหนดเต็มที่ทำหน้าที่ส่งต่อบนเครือข่ายทั้งหมด ยกตัวอย่างเช่น ณ เวลาที่เขียนนี้ มี full node ที่ทำหน้าที่ส่งต่อประมาณ 50,000 โหนด; ผู้โจมตีที่สร้างธุรกรรมแทนที่ 1,000 รายการต่อหนึ่งนาที โดยแต่ละรายการมีขนาด 200 ไบต์ จะใช้แบนด์วิดท์ส่วนตัวของเขาประมาณ 20 KB ต่อหนึ่งนาที แต่จะใช้แบนด์วิดท์เครือข่ายของ full node ประมาณ 10 GB ทุก ๆ นาที นอกจากค่าใช้จ่ายแบนด์วิดท์ส่วนตัว 20 KB/นาที และค่าธรรมเนียมเพียงเป็นครั้งคราวเมื่อต้องมีธุรกรรมที่ถูกยืนยัน ผู้โจมตีจะไม่ต้องจ่ายค่าใช้จ่ายใด ๆ สำหรับภาระอันมหาศาลที่พวกเขาก่อขึ้นกับผู้ดำเนิน full node เลย</p>
<p>เพื่อกำจัดความเสี่ยงจากการโจมตีนี้ รูปแบบการแทนที่ธุรกรรมที่อิงตาม sequence แบบเดิมถูกปิดใช้งานในเวอร์ชันแรก ๆ ของซอฟต์แวร์บิตคอยน์ ในช่วงหลายปี  full node ของบิตคอยน์จะไม่อนุญาตให้ธุรกรรมที่ยังไม่ได้ยืนยันซึ่งมีอินพุตหนึ่ง (ตามที่ระบุโดย outpoint) ถูกแทนที่ด้วยธุรกรรมอื่นที่มีอินพุตเดียวกัน อย่างไรก็ตาม สถานการณ์นั้นไม่ได้ดำเนินต่อไปตลอดเวลา</p>
<h3>การสัญญาณเพื่อแทนที่ธุรกรรมแบบ Opt-in</h3>
<p>หลังจากรูปแบบการแทนที่ธุรกรรมที่อิงตาม sequence แบบเดิมถูกปิดใช้งานเนื่องจากมีความเสี่ยงที่จะถูกละเมิด ได้มีการเสนอวิธีแก้ไข: การโปรแกรม Bitcoin Core และซอฟต์แวร์โหนดเต็มที่ส่งต่อธุรกรรมอื่น ๆ ให้อนุญาตให้ธุรกรรมที่จ่ายค่าธรรมเนียมสูงแทนที่ธุรกรรมที่ขัดแย้งและจ่ายค่าธรรมเนียมต่ำกว่า เรียกวิธีนี้ว่า replace by fee (RBF)</p>
<p>ผู้ใช้และธุรกิจบางรายคัดค้านการเพิ่มการสนับสนุนการแทนที่ธุรกรรมกลับเข้าไปใน Bitcoin Core ดังนั้นจึงได้ข้อยุติด้วยการใช้ฟิลด์ sequence อีกครั้งเพื่อสนับสนุนการแทนที่</p>
<p>ตามที่ระบุใน BIP125 ธุรกรรมที่ยังไม่ได้ยืนยันซึ่งมีอินพุตใด ๆ ที่ตั้งค่า sequence ต่ำกว่า 0xfffffffe (เช่น อย่างน้อย 2 ต่ำกว่าค่าสูงสุด) จะส่งสัญญาณไปยังเครือข่ายว่าผู้ลงนามต้องการให้ธุรกรรมนี้สามารถถูกแทนที่ด้วยธุรกรรมที่จ่ายค่าธรรมเนียมสูงกว่าได้ Bitcoin Core อนุญาตให้ธุรกรรมที่ยังไม่ได้ยืนยันเหล่านี้ถูกแทนที่ได้ และยังคงไม่อนุญาตให้ธุรกรรมอื่น ๆ ถูกแทนที่ ทำให้ผู้ใช้และธุรกิจที่คัดค้านการแทนที่สามารถเพิกเฉยต่อธุรกรรมที่ยังไม่ได้ยืนยันซึ่งมีสัญญาณ BIP125 จนกว่าจะได้รับการยืนยัน</p>
<p>นโยบายการแทนที่ธุรกรรมสมัยใหม่มีมากกว่าการพิจารณาค่าธรรมเนียมและสัญญาณ sequence ซึ่งเราจะเห็นรายละเอียดเพิ่มเติมในหัวข้อ rbf</p>
<h3>Sequence ในฐานะกลไกกำหนดเวลาล็อกแบบสัมพัทธ์ (relative timelock) ที่ถูกบังคับโดยฉันทามติ</h3>
<p>ในหัวข้อ Version เราได้เรียนรู้ว่า soft fork BIP68 ได้เพิ่มข้อจำกัดใหม่ให้กับธุรกรรมที่มีหมายเลขเวอร์ชัน 2 ขึ้นไป ซึ่งข้อจำกัดนั้นถูกใช้กับฟิลด์ sequence</p>
<p>อินพุตของธุรกรรมที่มีค่า sequence น้อยกว่า 2³¹ จะถูกตีความว่ามีการตั้ง relative timelock และธุรกรรมดังกล่าวจะสามารถถูกบรรจุเข้าไปในบล็อกเชนได้ก็ต่อเมื่อเอาต์พุตก่อนหน้า (ที่ถูกอ้างอิงโดย outpoint) ผ่านระยะเวลาเท่ากับค่าของ relative timelock มาแล้ว ตัวอย่างเช่น ธุรกรรมที่มีอินพุตหนึ่งตัวตั้ง relative timelock ไว้ 30 บล็อก จะสามารถถูกยืนยันในบล็อกที่มีอย่างน้อย 29 บล็อกคั่นอยู่ระหว่างบล็อกนั้นกับบล็อกที่มีเอาต์พุตซึ่งกำลังถูกใช้จ่ายอยู่บนบล็อกเชนเดียวกัน เนื่องจาก sequence เป็นฟิลด์ที่ผูกกับแต่ละอินพุต ธุรกรรมหนึ่งรายการอาจมีอินพุตหลายตัวที่มีการตั้ง timelock และอินพุตทั้งหมดต้องผ่านช่วงเวลาตามที่กำหนดแล้วจึงทำให้ธุรกรรมถือว่าถูกต้อง มี disable flag ที่อนุญาตให้ธุรกรรมสามารถรวมอินพุตแบบมี relative timelock (sequence &lt; 2³¹) และอินพุตที่ไม่มี relative timelock (sequence ≥ 2³¹) ไว้ในธุรกรรมเดียวกันได้</p>
<p>ค่า sequence สามารถกำหนดเป็นหน่วยบล็อกหรือหน่วยวินาทีได้ โดยมี type-flag ใช้แยกความแตกต่างระหว่างค่าที่นับเป็นบล็อกกับค่าที่นับเป็นวินาที type-flag จะถูกตั้งค่าไว้ที่บิตลำดับที่ 23 (กล่าวคือค่า 1&lt;&lt;22) หาก type-flag ถูกตั้งค่า ค่า sequence จะถูกตีความเป็นจำนวนเท่าของ 512 วินาที แต่ถ้า type-flag ไม่ได้ถูกตั้งค่า ค่า sequence จะถูกตีความเป็นจำนวนบล็อก เมื่อทำการตีความ sequence เป็น relative timelock จะพิจารณาเฉพาะ 16 บิตที่มีค่าน้อยที่สุดเท่านั้น เมื่อทำการประเมินค่าสถานะของ flags แล้ว (บิต 32 และบิต 23) ค่า sequence จะถูก “mask” ด้วย mask ขนาด 16 บิต (เช่น sequence &amp; 0x0000FFFF) ค่าหนึ่งเท่าของ 512 วินาทีมีค่าประมาณเท่ากับเวลาเฉลี่ยระหว่างบล็อก ดังนั้นค่า relative timelock สูงสุดจาก 16 บิต (2¹⁶) ทั้งในหน่วยบล็อกและหน่วยวินาที จะมีค่าเกินหนึ่งปีเล็กน้อย</p>
<p>คำจำกัดความของ sequence encoding ตาม BIP68 (ที่มา: BIP68) แสดงผังไบนารีของค่า sequence ตามที่กำหนดใน BIP68<br> <img src="https://image.nostr.build/a19cad6116cdee116a4b577c21c4b4d5861df50e7874d1da00ca9286e25970cf.jpg" alt="image"></p>
<h2>Outputs</h2>
<p>ฟิลด์ outputs ของธุรกรรมประกอบด้วยฟิลด์หลายตัวที่เกี่ยวข้องกับเอาต์พุตแต่ละรายการ เช่นเดียวกับที่เราทำกับฟิลด์ inputs เราจะเริ่มจากการดูไบต์เฉพาะของฟิลด์ outputs จากตัวอย่างธุรกรรมที่ Alice จ่ายให้ Bob ซึ่งถูกแสดงในรูปแบบแผนที่ของไบต์เหล่านั้นในตัวอย่างตอนต้นของบทนี้<br> <img src="https://image.nostr.build/d9e1d6dbe12c88189f42ee36cc478a37039a81fb4cfa13e3efad7e97eb40a5e0.jpg" alt="image"></p>
<h3><strong>Output count (จำนวน Outputs)</strong></h3>
<p>เช่นเดียวกับจุดเริ่มต้นของส่วน inputs ของธุรกรรม ฟิลด์ outputs จะเริ่มต้นด้วยตัวนับที่ระบุจำนวนของ outputs ในธุรกรรมนี้ ซึ่งเป็น compactSize integer และต้องมีค่ามากกว่าศูนย์ โดยธุรกรรมตัวอย่างมีสอง outputs</p>
<h3><strong>Amount(จำนวนเงิน)</strong></h3>
<p>ฟิลด์แรกของ output หนึ่งรายการคือจำนวนเงินของมัน ซึ่งใน Bitcoin Core เรียกว่า “value” เป็น signed integer ขนาด 8 ไบต์ที่ระบุจำนวน satoshis ที่ต้องการโอน (satoshi คือหน่วยที่เล็กที่สุดของ bitcoin ที่สามารถแสดงได้ในธุรกรรมบนบล็อกเชน หนึ่ง bitcoin มี 100 ล้าน satoshis) และในกฎฉันทามติของ Bitcoin อนุญาตให้ output มีค่าตั้งแต่ศูนย์จนถึงสูงสุด 21 ล้าน bitcoins (2.1 quadrillion satoshis)</p>
<h4>Uneconomical outputs and disallowed dust (เอาต์พุตที่ไม่คุ้มค่าเชิงเศรษฐกิจและฝุ่น (dust) ที่ถูกห้าม)</h4>
<p>แม้ว่าจะไม่มีมูลค่า แต่เอาต์พุตที่มีค่าเป็นศูนย์สามารถถูกใช้จ่ายได้ภายใต้กฎเดียวกับเอาต์พุตอื่น ๆ อย่างไรก็ตาม การใช้จ่ายเอาต์พุต (นำมันไปใช้เป็นอินพุตในธุรกรรม) ทำให้ขนาดของธุรกรรมเพิ่มขึ้น ซึ่งทำให้ค่าธรรมเนียมที่ต้องจ่ายเพิ่มขึ้นด้วย หากมูลค่าของเอาต์พุตมีค่าน้อยกว่าค่าธรรมเนียมที่ต้องจ่ายเพิ่ม การใช้จ่ายเอาต์พุตนั้นก็ไม่มีความคุ้มค่าทางเศรษฐกิจ เอาต์พุตเช่นนี้เรียกว่า uneconomical outputs</p>
<p>เอาต์พุตที่มีค่าเป็นศูนย์เป็น uneconomical output เสมอ เพราะมันจะไม่เพิ่มมูลค่าใด ๆ ให้กับธุรกรรมที่ใช้มัน แม้ว่าอัตราค่าธรรมเนียมจะเป็นศูนย์ก็ตาม แต่อย่างไรก็ตาม เอาต์พุตอื่น ๆ ที่มีมูลค่าต่ำก็อาจเป็น uneconomical เช่นกัน แม้จะไม่ได้ตั้งใจ ตัวอย่างเช่น ที่อัตราค่าธรรมเนียมปกติบนเครือข่ายในวันนี้ เอาต์พุตหนึ่งอาจเพิ่มมูลค่ามากกว่าค่าใช้จ่ายในการนำไปใช้ แต่วันพรุ่งนี้อัตราค่าธรรมเนียมอาจสูงขึ้นและทำให้เอาต์พุตนั้นกลายเป็น uneconomical</p>
<p>ความจำเป็นที่ full node ต้องติดตาม UTXO ทั้งหมด ตามที่อธิบายไว้ในหัวข้อ Outpoint หมายความว่า UTXO ทุกตัวทำให้การรัน full node ยากขึ้นเล็กน้อย สำหรับ UTXO ที่มีมูลค่ามาก ผู้ใช้มีแรงจูงใจที่จะใช้จ่ายมันในที่สุด ดังนั้นมันไม่ก่อปัญหา แต่สำหรับผู้ที่ถือ UTXO ที่ไม่คุ้มค่าทางเศรษฐกิจ ไม่มีแรงจูงใจที่จะใช้จ่ายมันเลย ซึ่งอาจทำให้มันกลายเป็นภาระถาวรสำหรับผู้ดูแล full node และเนื่องจากการกระจายศูนย์ของ Bitcoin ขึ้นอยู่กับจำนวนผู้ที่ยินดีรัน full node ซอฟต์แวร์ full node หลาย ๆ ตัว เช่น Bitcoin Core จึงใช้ข้อกำหนดที่ส่งผลต่อการส่งต่อธุรกรรมและการขุดธุรกรรมที่ยังไม่ได้คอนเฟิร์ม เพื่อป้องกันการสร้างเอาต์พุตที่ไม่คุ้มค่าทางเศรษฐกิจ</p>
<p>ข้อกำหนดที่ห้ามการส่งต่อหรือการขุดธุรกรรมที่สร้างเอาต์พุตที่ไม่คุ้มค่าเรียกว่า dust policies ตั้งตามการเปรียบเทียบเชิงสัญลักษณ์ระหว่างเอาต์พุตที่มีมูลค่าน้อยมากกับอนุภาคที่มีขนาดเล็กมาก (เล็กจนเป็นฝุ่น) dust policy ของ Bitcoin Core นั้นซับซ้อนและมีตัวเลขกำหนดหลายค่า ดังนั้นโปรแกรมจำนวนมากมักถือเอาว่าเอาต์พุตที่มีค่าน้อยกว่า 546 satoshis คือ dust และจะไม่ถูกส่งต่อหรือขุดตามค่าตั้งต้น แต่ก็มีบ้างเป็นครั้งคราวที่มีข้อเสนอให้ลดขีดจำกัดของ dust และก็มีข้อเสนอคัดค้านให้เพิ่มขีดจำกัดเช่นกัน ดังนั้นเราขอแนะนำให้นักพัฒนาที่ใช้ presigned transactions หรือโปรโตคอลแบบหลายฝ่าย ตรวจสอบว่าข้อกำหนดนี้มีการเปลี่ยนแปลงตั้งแต่วันที่หนังสือเล่มนี้เผยแพร่หรือไม่</p>
<blockquote>
<p>TIP: ตั้งแต่การกำเนิดของ Bitcoin full node ทุกตัวจำเป็นต้องเก็บสำเนาของ UTXO ทุกตัว แต่สิ่งนี้อาจไม่จำเป็นต้องเป็นเช่นนั้นตลอดไป นักพัฒนาหลายคนกำลังทำงานกับ Utreexo ซึ่งเป็นโปรเจกต์ที่ทำให้โหนดเต็มสามารถเก็บข้อมูลแบบ commitment ต่อชุดของ UTXO แทนการเก็บข้อมูลจริง commitment ที่เล็กที่สุดอาจมีขนาดเพียงหนึ่งหรือสองกิโลไบต์—เทียบกับข้อมูลมากกว่า 5 กิกะไบต์ที่ Bitcoin Core เก็บ ณ เวลาที่เขียนหนังสือนี้ (และตอนที่ผมแปลอยู่ที่ประมาณ 11 GB) </p>
</blockquote>
<blockquote>
<p>อย่างไรก็ตาม Utreexo จะยังคงต้องให้บางโหนดเก็บข้อมูล UTXO ทั้งหมด โดยเฉพาะโหนดที่ให้บริการนักขุดหรือการดำเนินงานอื่น ๆ ที่ต้องตรวจสอบบล็อกใหม่อย่างรวดเร็ว นั่นหมายความว่า uneconomical outputs ก็ยังคงเป็นปัญหาสำหรับ full node แม้ในอนาคตที่เป็นไปได้ซึ่งโหนดส่วนใหญ่ใช้ Utreexo ก็ตาม</p>
</blockquote>
<p>กฎนโยบายของ Bitcoin Core เกี่ยวกับ dust มีข้อยกเว้นหนึ่งข้อ: output script ที่เริ่มต้นด้วย OP_RETURN ซึ่งเรียกว่า data carrier outputs สามารถมีมูลค่าเป็นศูนย์ได้ OP_RETURN opcode ทำให้สคริปต์ล้มเหลวทันทีไม่ว่าจะมีอะไรตามมาหลังจากนั้น ดังนั้นเอาต์พุตเหล่านี้ไม่สามารถถูกใช้จ่ายได้เลย นั่นหมายความว่า full node ไม่จำเป็นต้องติดตามเอาต์พุตเหล่านี้ ซึ่งเป็นคุณสมบัติที่ Bitcoin Core ใช้เพื่อให้ผู้ใช้สามารถจัดเก็บข้อมูลเล็กน้อยตามต้องการลงในบล็อกเชนได้โดยไม่ทำให้ฐานข้อมูล UTXO มีขนาดใหญ่ขึ้น เนื่องจากเอาต์พุตเหล่านี้ไม่สามารถใช้จ่ายได้ จึงไม่ถือว่าเป็น uneconomical แต่ว่า satoshis ใด ๆ ก็ตามที่กำหนดให้กับมันจะกลายเป็นใช้ไม่ได้อย่างถาวร ดังนั้นการอนุญาตให้จำนวนเงินเป็นศูนย์จึงช่วยให้มั่นใจว่า satoshis จะไม่ถูกทำลาย</p>
<h3><strong>Output Scripts</strong></h3>
<p>หลังจากจำนวนเงินของเอาต์พุต จะมี compactSize integer ที่บอกความยาวของ output script ซึ่งเป็นสคริปต์ที่มีเงื่อนไขที่ต้องปฏิบัติตามเพื่อใช้จ่ายบิตคอยน์ ตามกฎฉันทามติของ Bitcoin ขนาดขั้นต่ำของ output script คือศูนย์</p>
<p>ขนาดสูงสุดตามฉันทามติที่อนุญาตของ output script จะแตกต่างกันไปขึ้นอยู่กับช่วงเวลาที่มีการตรวจสอบ ไม่มีขีดจำกัดที่ระบุไว้อย่างชัดเจนสำหรับขนาดของ output script ในเอาต์พุตของธุรกรรม แต่ธุรกรรมถัดไปสามารถใช้จ่ายเอาต์พุตก่อนหน้าได้เฉพาะเมื่อสคริปต์มีขนาด 10,000 ไบต์หรือน้อยกว่าเท่านั้น โดยนัยคือสคริปต์ของเอาต์พุตสามารถมีขนาดใหญ่เกือบเท่ากับตัวธุรกรรมที่บรรจุมันไว้ และธุรกรรมก็สามารถมีขนาดใหญ่เกือบเท่ากับบล็อกที่บรรจุธุรกรรมนั้นไว้</p>
<blockquote>
<p>TIP: output script ที่มีความยาวเป็นศูนย์สามารถถูกใช้จ่ายได้โดย input script ที่มี OP_TRUE อยู่ภายใน โดยใคร ๆ ก็สามารถสร้าง input script แบบนั้นได้ ซึ่งหมายความว่าใครก็สามารถใช้จ่าย output script ที่ว่างเปล่าได้ มีจำนวนสคริปต์แทบไม่จำกัดที่ใครก็สามารถใช้จ่ายได้ และนักพัฒนาโปรโตคอล Bitcoin เรียกสิ่งเหล่านี้ว่า anyone can spends การอัปเกรดภาษาสคริปต์ของ Bitcoin มักจะนำสคริปต์ที่เป็น anyone-can-spend มาเพิ่มเงื่อนไขใหม่เข้าไป ทำให้สามารถใช้จ่ายได้เฉพาะภายใต้เงื่อนไขใหม่เท่านั้น นักพัฒนาแอปพลิเคชันไม่ควรจำเป็นต้องใช้ anyone-can-spend script เลย แต่หากคุณจำเป็นต้องใช้ เราขอแนะนำอย่างยิ่งให้ประกาศแผนของคุณอย่างชัดเจนต่อผู้ใช้และนักพัฒนา Bitcoin เพื่อไม่ให้การอัปเกรดในอนาคตไปรบกวนระบบของคุณโดยไม่ตั้งใจ</p>
</blockquote>
<p>นโยบายของ Bitcoin Core สำหรับการรีเลย์และการขุดธุรกรรม จะจำกัด output script ให้เหลือเพียงไม่กี่รูปแบบ ซึ่งเรียกว่า standard transaction outputs การจำกัดนี้ถูกนำมาใช้ครั้งแรกหลังจากค้นพบบั๊กในยุคแรกของ Bitcoin ที่เกี่ยวข้องกับภาษา Script และยังถูกคงไว้ใน Bitcoin Core ยุคปัจจุบันเพื่อรองรับการอัปเกรดแบบ anyone-can-spend และเพื่อส่งเสริมแนวทางที่ดีที่สุดในการวางเงื่อนไขสคริปต์ไว้ใน P2SH redeem scripts, segwit v0 witness scripts และ segwit v1 (taproot) leaf scripts เราจะดูแต่ละรูปแบบของ standard transaction templates ในปัจจุบัน และเรียนรู้วิธีการ parse สคริปต์ในบทถัดไป</p>
<h3>Witness Structure</h3>
<p>ในศาล “พยาน” คือบุคคลที่ให้การว่าตนเห็นเหตุการณ์สำคัญบางอย่างเกิดขึ้น พยานมนุษย์ไม่ใช่สิ่งที่เชื่อถือได้เสมอไป ดังนั้นศาลจึงมีขั้นตอนต่าง ๆ สำหรับการสอบสวนพยานเพื่อ (อย่างน้อยก็ในอุดมคติ) รับหลักฐานเฉพาะจากผู้ที่เชื่อถือได้เท่านั้น </p>
<p>ลองจินตนาการว่าถ้ามี “พยาน” สำหรับโจทย์คณิตศาสตร์จะมีหน้าตาอย่างไร ตัวอย่างเช่น หากปัญหาสำคัญคือ x + 2 == 4 และมีใครสักคนอ้างว่าตนเป็นพยานที่เห็นวิธีแก้ เราจะถามอะไรพวกเขา? เราต้องการ “หลักฐานทางคณิตศาสตร์” ที่แสดงค่าหนึ่งที่สามารถนำมาบวกกับ 2 แล้วได้ 4 เราอาจตัดความจำเป็นของการมีบุคคลออกไปเลย และใช้ “ค่าที่เสนอให้เป็น x” นั้นเป็นพยานแทนก็ได้ หากเราถูกบอกว่าพยานคือ 2 เราก็สามารถแทนค่าในสมการ ตรวจสอบว่าถูกต้อง และสรุปว่าปัญหาสำคัญนั้นได้รับการแก้ไขแล้ว </p>
<p>เมื่อมีการใช้จ่ายบิตคอยน์ ปัญหาสำคัญที่เราต้องการแก้คือ “การยืนยันว่าการใช้จ่ายนั้นได้รับอนุญาตจากบุคคลหรือกลุ่มบุคคลที่ควบคุมบิตคอยน์เหล่านั้นจริงหรือไม่” โหนดเต็มนับพันที่บังคับใช้กฎฉันทามติของบิตคอยน์ไม่สามารถสอบสวนพยานมนุษย์ได้ แต่พวกมันสามารถรับ “พยาน” ที่ประกอบด้วยข้อมูลล้วน ๆ สำหรับแก้ปัญหาทางคณิตศาสตร์ได้ ตัวอย่างเช่น พยานที่เป็นค่า 2 จะทำให้สามารถใช้จ่ายบิตคอยน์ที่ถูกป้องกันด้วยสคริปต์ต่อไปนี้ได้:</p>
<pre><code>2 OP_ADD 4 OP_EQUAL
</code></pre>
<p>แน่นอนว่า การอนุญาตให้บิตคอยน์ของคุณถูกใช้จ่ายโดยใครก็ตามที่สามารถแก้สมการง่าย ๆ ได้ย่อมไม่ปลอดภัย อย่างที่เราจะเห็นในบทที่ 8 กลไกการลงนามดิจิทัลที่ปลอมแปลงไม่ได้ (unforgeable digital signature scheme) ใช้สมการที่สามารถถูกแก้ได้โดยเฉพาะผู้ที่ครอบครองข้อมูลบางอย่างซึ่งพวกเขาสามารถเก็บไว้เป็นความลับได้ พวกเขาสามารถอ้างอิงข้อมูลลับนั้นโดยใช้ตัวระบุสาธารณะ ซึ่งตัวระบุดังกล่าวเรียกว่า public key และคำตอบของสมการนั้นเรียกว่า signature</p>
<p>สคริปต์ต่อไปนี้มี public key และ opcode ที่ต้องการ signature ที่สอดคล้องกันเพื่อ commit กับข้อมูลในธุรกรรมที่กำลังใช้จ่าย เช่นเดียวกับตัวเลข 2 ในตัวอย่างง่าย ๆ ของเรา signature คือพยานของเรา:</p>
<pre><code>&lt;public key&gt; OP_CHECKSIG
</code></pre>
<p>พยาน (witnesses) ซึ่งเป็นค่าที่ใช้ในการแก้ปัญหาทางคณิตศาสตร์ที่ปกป้องบิตคอยน์ จำเป็นต้องถูกใส่รวมอยู่ในธุรกรรมที่ใช้มัน เพื่อให้โหนดเต็มสามารถตรวจสอบได้ ในรูปแบบธุรกรรมแบบดั้งเดิม (legacy transaction format) ที่ใช้ในธุรกรรมบิตคอยน์ยุคแรก ๆ ทั้งหมดนั้น ลายเซ็นและข้อมูลอื่น ๆ ถูกวางไว้ในฟิลด์ input script อย่างไรก็ตาม เมื่อผู้พัฒนาเริ่มนำโปรโตคอลสัญญาต่าง ๆ มาใช้บนบิตคอยน์ เช่นที่เราเห็นใน Original sequence-based transaction replacement พวกเขาค้นพบปัญหาที่สำคัญหลายประการเกี่ยวกับการใส่พยานไว้ในฟิลด์ input script</p>
<h4>Circular Dependencies</h4>
<p>โปรโตคอลสัญญาหลายอย่างสำหรับบิตคอยน์เกี่ยวข้องกับชุดของธุรกรรมที่ถูกเซ็นนอกลำดับ ตัวอย่างเช่น Alice และ Bob ต้องการฝากเงินเข้าไปในสคริปต์ที่สามารถใช้จ่ายได้ด้วยลายเซ็นของทั้งคู่ แต่พวกเขาก็ต้องการให้สามารถได้เงินคืนได้หากอีกฝ่ายหนึ่งไม่ตอบสนอง วิธีแก้ปัญหาอย่างง่ายคือการเซ็นธุรกรรมนอกลำดับ:</p>
<ul>
<li>Tx0 จ่ายเงินจาก Alice และเงินจาก Bob ไปยังเอาต์พุตที่มีสคริปต์ที่ต้องการลายเซ็นจากทั้ง Alice และ Bob เพื่อที่จะใช้จ่ายได้  </li>
<li>Tx1 ใช้จ่ายเอาต์พุตก่อนหน้าไปยังสองเอาต์พุต—หนึ่งคืนเงินให้ Alice และอีกหนึ่งคืนเงินให้ Bob (หักจำนวนเล็กน้อยเป็นค่าธรรมเนียมธุรกรรม)  </li>
<li>หาก Alice และ Bob เซ็น Tx1 ก่อนที่พวกเขาจะเซ็น Tx0 พวกเขาทั้งคู่ก็จะมั่นใจได้ว่าจะรับเงินคืนได้ทุกเมื่อ โปรโตคอลนี้ไม่ต้องการให้ฝ่ายหนึ่งไว้ใจอีกฝ่ายหนึ่ง ทำให้มันเป็นโปรโตคอลแบบ trustless</li>
</ul>
<p>ปัญหาของโครงสร้างนี้ในรูปแบบธุรกรรมแบบดั้งเดิมคือทุกฟิลด์ รวมถึงฟิลด์ input script ที่มีลายเซ็น จะถูกใช้ในการสร้างตัวระบุของธุรกรรม (txid) ค่า txid ของ Tx0 เป็นส่วนหนึ่งของ outpoint ของอินพุตใน Tx1 นั่นหมายความว่าไม่มีทางที่ Alice และ Bob จะสร้าง Tx1 ได้จนกว่าจะทราบลายเซ็นทั้งสองของ Tx0 — แต่ถ้าพวกเขารู้ลายเซ็นของ Tx0 หนึ่งในนั้นสามารถออกอากาศธุรกรรมนั้นก่อนที่จะเซ็นธุรกรรมคืนเงิน ทำให้การรับประกันการคืนเงินถูกลบล้างไป นี่คือปัญหา circular dependency</p>
<h4>Third-Party Transaction Malleability (การเปลี่ยนแปลงธุรกรรมโดยบุคคลที่สาม)</h4>
<p>ลำดับธุรกรรมที่ซับซ้อนกว่าสามารถแก้ปัญหา circular dependency ได้ในบางครั้ง แต่โปรโตคอลจำนวนมากจะพบกับความกังวลใหม่: มักจะสามารถแก้สคริปต์เดียวกันได้หลายวิธี ตัวอย่างเช่น พิจารณาสคริปต์แบบง่ายของเราจากหัวข้อ Witness Structure:</p>
<pre><code>2 OP_ADD 4 OP_EQUAL
</code></pre>
<p>เราสามารถทำให้สคริปต์นี้ผ่านได้โดยการใส่ค่า 2 ลงใน input script แต่มีหลายวิธีที่จะวางค่านั้นลงบนสแตกในบิตคอยน์ ต่อไปนี้คือเพียงบางตัวอย่าง:</p>
<pre><code>OP_2
OP_PUSH1 0x02
OP_PUSH2 0x0002
OP_PUSH3 0x000002
...
OP_PUSHDATA1 0x0102
OP_PUSHDATA1 0x020002
...
OP_PUSHDATA2 0x000102
OP_PUSHDATA2 0x00020002
...
OP_PUSHDATA4 0x0000000102
OP_PUSHDATA4 0x000000020002
...
</code></pre>
<p>แต่ละรูปแบบการเข้ารหัสของตัวเลข 2 ใน input script จะสร้างธุรกรรมที่แตกต่างกันเล็กน้อย พร้อมกับ txid ที่แตกต่างกันอย่างสิ้นเชิง แต่ละเวอร์ชันของธุรกรรมจะใช้จ่ายอินพุต (outpoints) เดียวกันกับทุกเวอร์ชันอื่น ๆ ทำให้พวกมันขัดแย้งกันเอง ทั้งหมดนี้ทำให้มีเพียงหนึ่งเวอร์ชันจากชุดของธุรกรรมที่ขัดแย้งกันเท่านั้นที่สามารถอยู่ในบล็อกเชนที่ถูกต้องได้</p>
<p>ลองนึกภาพว่ามีครั้งหนึ่ง Alice สร้างเวอร์ชันของธุรกรรมที่มี OP_2 ใน input script และมี output ที่จ่ายให้ Bob จากนั้น Bob ก็ใช้จ่าย output นั้นต่อให้ Carol ทันที บุคคลใดก็ตามบนเครือข่ายสามารถแทนที่ OP_2 ด้วย OP_PUSH1 0x02 ทำให้เกิดธุรกรรมที่ขัดแย้งกับเวอร์ชันต้นฉบับของ Alice หากธุรกรรมที่ขัดแย้งนั้นถูกยืนยัน ก็จะไม่มีทางรวมเวอร์ชันต้นฉบับของ Alice เข้าไปในบล็อกเชนเดียวกันได้ ซึ่งหมายความว่าไม่มีทางที่ธุรกรรมของ Bob จะใช้จ่าย output ของมันได้ การจ่ายเงินของ Bob ให้ Carol จึงกลายเป็นโมฆะ ทั้งที่ Alice, Bob หรือ Carol ไม่ได้ทำอะไรผิดเลย คนที่ไม่ได้เกี่ยวข้องกับธุรกรรม (บุคคลที่สาม) สามารถเปลี่ยนแปลง (mutate) ธุรกรรมของ Alice ได้ ซึ่งเป็นปัญหาที่เรียกว่า unwanted third-party transaction malleability</p>
<blockquote>
<p>TIP: มีหลายกรณีที่ผู้คนต้องการให้ธุรกรรมของพวกเขาถูกดัดแปลงได้ (malleable) และบิตคอยน์ก็มีฟีเจอร์หลายอย่างรองรับสิ่งนี้ โดยเฉพาะ signature hashes (sighash) ที่เราจะได้เรียนรู้ใน [sighash_types] ตัวอย่างเช่น Alice สามารถใช้ sighash เพื่อให้ Bob ช่วยจ่ายค่าธรรมเนียมบางส่วนได้ ซึ่งทำให้ธุรกรรมของ Alice ถูกเปลี่ยนแปลง แต่เป็นการเปลี่ยนแปลงในแบบที่ Alice ต้องการ ด้วยเหตุนี้เราจะใส่คำว่า unwanted นำหน้าคำว่า transaction malleability ในบางครั้ง แม้ว่าเราหรือผู้เขียนเทคนิคบิตคอยน์คนอื่น ๆ จะใช้คำเวอร์ชันสั้น เราก็กำลังพูดถึงรูปแบบของ malleability ที่ไม่พึงประสงค์แทบทุกครั้ง</p>
</blockquote>
<h4>Second-Party Transaction Malleability(ผู้ร่วมธุรกรรมอีกฝ่าย)</h4>
<p>เมื่อรูปแบบธุรกรรมแบบดั้งเดิม (legacy transaction format) เป็นรูปแบบเดียวที่มีให้ใช้ นักพัฒนาก็ทำงานบนข้อเสนอเพื่อลดปัญหา third-party malleability เอาไว้อย่างเช่น BIP62 แต่อย่างไรก็ตาม แม้ว่าจะสามารถกำจัด third-party malleability ได้ทั้งหมด ผู้ใช้งานโปรโตคอลแบบสัญญา (contract protocols) ก็ยังพบปัญหาอีกแบบหนึ่ง คือถ้าต้องใช้ลายเซ็นของอีกฝ่ายหนึ่งที่อยู่ในโปรโตคอล ฝ่ายนั้นสามารถสร้างลายเซ็นทางเลือกขึ้นมาและเปลี่ยน txid ได้</p>
<p>ตัวอย่างเช่น Alice และ Bob ฝากเงินของพวกเขาเข้าไปในสคริปต์ที่ต้องการลายเซ็นของทั้งคู่เพื่อใช้จ่าย พวกเขายังสร้าง "ธุรกรรมคืนเงิน" (refund transaction) ที่จะให้ทั้งสองฝ่ายสามารถดึงเงินกลับได้ทุกเมื่อ และต่อมา Alice ต้องการใช้เงินบางส่วน เธอจึงร่วมมือกับ Bob สร้างลำดับธุรกรรมดังนี้:</p>
<ul>
<li>Tx0 รวมลายเซ็นจาก Alice และ Bob และใช้จ่ายบิตคอยน์ไปยังสอง output โดย output แรกจ่ายเงินบางส่วนให้ Alice และอีกอัน คืนเงินส่วนที่เหลือกลับเข้าไปยังสคริปต์เดิม ซึ่งยังคงต้องใช้ลายเซ็นของทั้ง Alice และ Bob แน่นอนว่าก่อนที่พวกเขาจะเซ็น Tx0 พวกเขาสร้างธุรกรรมคืนเงินชุดใหม่ Tx1  </li>
<li>Tx1 ใช้จ่าย output ที่สองของ Tx0 ไปยังสอง output ใหม่—หนึ่งให้ Alice ตามส่วนแบ่งของเธอ และอีกหนึ่งให้ Bob ตามส่วนแบ่งของเขา Alice และ Bob ต่างเซ็น Tx1 เสร็จก่อนที่จะเซ็น Tx0</li>
</ul>
<p>ตรงนี้ไม่มี circular dependency และถ้าเรามองข้าม third-party malleability ก็จะดูเหมือนเป็นโปรโตคอลที่ไม่ต้องเชื่อใจใคร (trustless) ได้ แต่ปัญหาคือ คุณสมบัติของลายเซ็นบิตคอยน์มีความสุ่มโดยธรรมชาติ —ผู้เซ็นต้องเลือกตัวเลขสุ่มขนาดใหญ่ทุกครั้งที่สร้างลายเซ็น หากเลือกเลขสุ่มต่างกัน ลายเซ็นที่ได้ก็จะต่างกัน แม้ข้อมูลที่ถูกเซ็นจะเหมือนเดิมทั้งหมดก็ตาม คล้ายกับการที่คุณเซ็นชื่อบนสัญญาเหมือนกันสองชุด ลายเซ็นจริง ๆ บนกระดาษทั้งสองแผ่นก็ไม่เหมือนกัน 100%</p>
<p>ความสามารถในการถูกดัดแปลงของลายเซ็นนี้ เป็นช่องให้ Bob สร้างธุรกรรมที่ขัดแย้งกับ Tx0 ได้ หาก Alice พยายามประกาศ Tx0 (ซึ่งมีลายเซ็นของ Bob อยู่) Bob สามารถสร้างลายเซ็นใหม่ที่แตกต่างออกมา ผลิต Tx0 เวอร์ชันใหม่ที่มี txid แตกต่างกันขึ้นมาได้ ถ้า Tx0 เวอร์ชันที่ Bob เปลี่ยนแปลงถูกยืนยันขึ้นมา Alice จะไม่สามารถใช้ Tx1 ที่ลงนามไว้ล่วงหน้าเพื่อรับเงินคืนของเธอได้ เพราะ Tx1 อ้างอิง txid เดิมของ Tx0 ซึ่งไม่ตรงกับเวอร์ชันที่ถูกยืนยัน พฤติกรรมแบบนี้เรียกว่า unwanted second-party transaction malleability คือการที่อีกฝ่ายในโปรโตคอลสามารถดัดแปลงธุรกรรมให้เปลี่ยน txid ได้โดยไม่ต้องร่วมมือกับคุณ</p>
<h4>Segregated Witness(การแยกพยานออกจากธุรกรรม)</h4>
<p>ตั้งแต่ช่วงต้นปี 2011 นักพัฒนาระบบโปรโตคอลของบิตคอยน์ก็ทราบวิธีแก้ปัญหา circular dependence, third-party malleability, และ second-party malleability แล้ว แนวคิดคือการหลีกเลี่ยงไม่ให้นำ input script มารวมอยู่ในการคำนวณที่ใช้สร้าง txid ของธุรกรรม จำได้ว่า ชื่อเชิงนามธรรมสำหรับข้อมูลที่อยู่ใน input script คือคำว่า witness แนวคิดในการแยกข้อมูลส่วนอื่น ๆ ของธุรกรรมออกจาก witness เพื่อใช้ในการสร้าง txid นี้ เรียกว่า segregated witness (segwit)</p>
<p>วิธีที่ตรงไปตรงมาที่สุดในการทำให้ segwit ใช้งานได้จริงนั้น ต้องมีการเปลี่ยนแปลงกฎ consensus ของบิตคอยน์ในลักษณะที่ ไม่สามารถเข้ากันได้กับ full node รุ่นเก่า หรือที่เรียกว่า hard fork Hard fork นั้นมาพร้อมกับความท้าทายจำนวนมาก ซึ่งเราจะอธิบายเพิ่มเติมในส่วน hardfork</p>
<p>แนวทางทางเลือกสำหรับ segwit ถูกอธิบายไว้ในช่วงปลายปี 2015 แนวทางนี้ใช้การเปลี่ยนแปลงกฎ consensus แบบ เข้ากันได้ย้อนหลัง (backward-compatible) ซึ่งเรียกว่า soft fork คำว่า “เข้ากันได้ย้อนหลัง” หมายความว่า full node ที่ใช้กฎใหม่จะต้อง ไม่ยอมรับบล็อกใด ๆ ที่ full node รุ่นเก่ามองว่าไม่ถูกต้อง ตราบเท่าที่พวกเขาปฏิบัติตามกฎนั้น full node รุ่นใหม่จึงสามารถ ปฏิเสธบล็อกที่ full node รุ่นเก่ายอมรับได้ ซึ่งทำให้พวกเขาสามารถบังคับใช้กฎ consensus ใหม่ได้ (แต่มีเงื่อนไขว่า full node รุ่นใหม่เหล่านั้นจะต้อง เป็นตัวแทนของฉันทามติทางเศรษฐกิจของผู้ใช้บิตคอยน์ด้วย—เราจะสำรวจรายละเอียดเกี่ยวกับการอัปเกรดกฎ consensus ของบิตคอยน์ในส่วนของ mining</p>
<p>การใช้ซอฟต์ฟอร์กแบบเซกวิทอาศัยแนวคิดเอาต์พุตสคริปต์แบบ “anyone-can-spend” เป็นพื้นฐาน สคริปต์ที่ขึ้นต้นด้วยตัวเลขตั้งแต่ 0 ถึง 16 และตามด้วยข้อมูลขนาด 2 ถึง 40 ไบต์ ถูกกำหนดให้เป็นแม่แบบเอาต์พุตสคริปต์ของเซกวิท ตัวเลขนั้นคือหมายเลขเวอร์ชันของเซกวิท (เช่น 0 คือเซกวิทเวอร์ชัน 0 หรือ segwit v0) ส่วนข้อมูลที่ตามมานั้นเรียกว่า witness program นอกจากนี้ยังสามารถนำแม่แบบเซกวิทไปห่อด้วยคอมมิตเมนต์แบบ P2SH ได้ แต่ในบทนี้เราจะยังไม่กล่าวถึง</p>
<p>จากมุมมองของโหนดเก่า เอาต์พุตสคริปต์แม่แบบเหล่านี้สามารถถูกใช้จ่ายด้วย input script ที่ว่างเปล่าได้ จากมุมมองของโหนดใหม่ที่รู้กฎของเซกวิท การใช้จ่ายเอาต์พุตที่เป็นแม่แบบเซกวิทต้องใช้ input script ที่ว่างเปล่าเท่านั้น สังเกตความแตกต่าง: โหนดเก่า “อนุญาต” ให้ใช้สคริปต์ว่างเปล่า ส่วนโหนดใหม่ “กำหนด” ให้ต้องเป็นสคริปต์ว่างเปล่า</p>
<p>การใช้ input script ว่างเปล่าทำให้ข้อมูลพยานไม่ไปกระทบกับ txid ช่วยกำจัดปัญหาการพึ่งพาวนซ้ำ การเปลี่ยนแปลงธุรกรรมโดยบุคคลที่สาม และการเปลี่ยนแปลงธุรกรรมโดยคู่สัญญา แต่เมื่อไม่สามารถใส่ข้อมูลใด ๆ ใน input script ได้ ผู้ใช้แม่แบบเอาต์พุตเซกวิทจึงต้องมีฟิลด์ใหม่ ฟิลด์นี้คือโครงสร้างพยาน (witness structure)</p>
<p>การเกิดขึ้นของ witness programs และ witness structure ทำให้บิตคอยน์ซับซ้อนขึ้น แต่สิ่งนี้ก็สอดคล้องกับแนวโน้มที่มีอยู่แล้วของการเพิ่มระดับนามธรรมในระบบบิตคอยน์ จากที่กล่าวไว้ในบทที่ 4 ว่าไวท์เปเปอร์บิตคอยน์ดั้งเดิมอธิบายระบบที่บิตคอยน์ถูก “รับ” ไปยัง public keys (pubkeys) และถูก “ใช้จ่าย” ด้วย signatures (sigs) pubkey ระบุว่าใครได้รับอนุญาตให้ใช้จ่ายบิตคอยน์ (คือผู้ที่ควบคุม private key ที่สอดคล้องกัน) และลายเซ็นเป็นการตรวจสอบว่าธุรกรรมการใช้จ่ายนั้นมาจากผู้ที่ควบคุม private key จริง ๆ เพื่อทำให้ระบบนั้นยืดหยุ่นขึ้น รุ่นแรกสุดของบิตคอยน์ได้แนะนำ scripts ซึ่งอนุญาตให้รับบิตคอยน์ไปยัง output scripts และใช้จ่ายด้วย input scripts ประสบการณ์ภายหลังจากการใช้งานโปรโตคอลสัญญาต่าง ๆ ได้นำไปสู่การอนุญาตให้รับบิตคอยน์ไปยัง witness programs และใช้จ่ายด้วย witness structure คำต่าง ๆ และฟิลด์ที่ถูกใช้ในเวอร์ชันต่าง ๆ ของบิตคอยน์ถูกแสดงไว้ในตารางข้างล่างนี้</p>
<table>
<thead>
<tr>
<th></th>
<th>Authorization</th>
<th>Authentication</th>
</tr>
</thead>
<tbody><tr>
<td>Whitepaper</td>
<td>Public key</td>
<td>Signature</td>
</tr>
<tr>
<td>Original (Legacy)</td>
<td>Output script</td>
<td>Input script</td>
</tr>
<tr>
<td>Segwit</td>
<td>Witness program</td>
<td>Witness structure</td>
</tr>
</tbody></table>
<h4>Witness Structure Serialization</h4>
<p>เช่นเดียวกับฟิลด์ inputs และ outputs, witness structure ก็มีฟิลด์ย่อยอื่น ๆ อยู่ภายใน ดังนั้นเราจะเริ่มต้นด้วยแผนที่ไบต์ (byte map) ของข้อมูลเหล่านั้นจากธุรกรรมของ Alice ซึ่งแสดงอยู่ในรูปด้านล่าง</p>
<p> <img src="https://image.nostr.build/d5c8cf4712e321ee4a37fa93b0a5c12364a7f321c6655a035ce56a20e3d9367e.jpg" alt="image"></p>
<p>ต่างจากฟิลด์ inputs และ outputs ตรงที่ witness structure โดยรวมจะไม่เริ่มต้นด้วยตัวบ่งชี้จำนวนของ witness stack ทั้งหมดที่มีอยู่ แต่จำนวนดังกล่าวถูกกำหนดโดยฟิลด์ inputs—กล่าวคือ ธุรกรรมหนึ่งรายการจะมีหนึ่ง witness stack ต่อหนึ่ง input เสมอ</p>
<p>สำหรับ witness structure ของแต่ละ input จะเริ่มต้นด้วยตัวนับจำนวนขององค์ประกอบที่อยู่ภายใน ซึ่งองค์ประกอบเหล่านี้เรียกว่า witness items เราจะศึกษารายละเอียดของมันในภายหลังในบทต่อไป แต่ตอนนี้สิ่งที่ต้องรู้คือ witness item แต่ละรายการจะถูกนำหน้าด้วย compactSize integer ที่ระบุขนาดของมัน</p>
<p>สำหรับ legacy inputs จะไม่มี witness items อยู่เลย ดังนั้น witness stack ของมันประกอบด้วยค่า 0 เพียงค่าเดียว (0x00)</p>
<p>ธุรกรรมของ Alice มี input หนึ่งรายการ และมี witness item หนึ่งรายการ</p>
<h3>Lock Time</h3>
<p>ฟิลด์สุดท้ายในธุรกรรมที่ถูกซีเรียลไลซ์คือ lock time ฟิลด์นี้เป็นส่วนหนึ่งของรูปแบบการซีเรียลไลซ์ดั้งเดิมของบิตคอยน์ แต่ในช่วงแรกมีผลเฉพาะกับนโยบายการเลือกธุรกรรมเข้าสู่บล็อกของบิตคอยน์เท่านั้น</p>
<p>ซอฟต์ฟอร์กที่บันทึกได้เป็นครั้งแรกของบิตคอยน์ได้เพิ่มกฎใหม่ว่า เริ่มตั้งแต่บล็อกหมายเลข 31,000 เป็นต้นไป ห้ามรวมธุรกรรมในบล็อก เว้นแต่ธุรกรรมนั้นจะเป็นไปตามหนึ่งในกฎต่อไปนี้:</p>
<ul>
<li>ธุรกรรมระบุว่า สามารถถูกรวมในบล็อกใดก็ได้ โดยตั้งค่า lock time เป็น 0  </li>
<li>ธุรกรรมระบุว่าต้องการจำกัดว่าบล็อกใดที่สามารถรวมมันได้ โดยตั้งค่า lock time ให้มีค่าต่ำกว่า 500,000,000 ในกรณีนี้ ธุรกรรมสามารถถูกบรรจุได้เฉพาะในบล็อกที่มีความสูงของบล็อก (block height) เท่ากับ lock time หรือมากกว่า ตัวอย่างเช่น ธุรกรรมที่มี lock time เป็น 123,456 สามารถอยู่ในบล็อก 123,456 หรือบล็อกใดที่สูงกว่า  </li>
<li>ธุรกรรมระบุว่าต้องการจำกัดเวลาที่สามารถถูกบรรจุเข้าสู่บล็อกเชนได้ โดยตั้งค่า lock time ให้มีค่า 500,000,000 หรือมากกว่า ในกรณีนี้ lock time จะถูกตีความเป็นเวลาตาม epoch time (จำนวนวินาทีตั้งแต่ 1970-01-01T00:00 UTC) ธุรกรรมจะถูกบรรจุได้เฉพาะในบล็อกที่มี median time past (MTP) มากกว่า lock time โดยทั่วไป MTP จะช้ากว่าเวลาปัจจุบันประมาณหนึ่งถึงสองชั่วโมง กฎของ MTP อธิบายไว้ในส่วนของ mtp</li>
</ul>
<h2><strong>Coinbase Transactions</strong></h2>
<p>ธุรกรรมแรกในทุกบล็อกเป็นกรณีพิเศษ เอกสารรุ่นเก่าหลายฉบับเรียกธุรกรรมนี้ว่า generation transaction แต่เอกสารรุ่นใหม่ส่วนใหญ่เรียกว่า coinbase transaction (ไม่เกี่ยวข้องกับธุรกรรมที่สร้างโดยบริษัทชื่อ “Coinbase”)</p>
<p>Coinbase transactions ถูกสร้างขึ้นโดยนักขุดที่ขุดบล็อกนั้น และเปิดโอกาสให้นักขุดสามารถรับ ค่าธรรมเนียม (fees) ทั้งหมดจากธุรกรรมที่อยู่ในบล็อกนั้นได้ นอกจากนี้ จนถึงบล็อกหมายเลข 6,720,000 นักขุดยังสามารถรับ subsidy ซึ่งเป็นบิตคอยน์ที่ถูกสร้างขึ้นใหม่และไม่เคยหมุนเวียนมาก่อน เรียกว่า block subsidy จำนวนรวมที่นักขุดสามารถรับได้จากบล็อกหนึ่ง ๆ — ประกอบด้วยค่าธรรมเนียมและ block subsidy — เรียกว่า block reward</p>
<p>พฤติกรรมพิเศษบางประการของ coinbase transactions มีดังนี้:</p>
<ul>
<li>ธุรกรรม coinbase สามารถมีอินพุตได้เพียงหนึ่งรายการเท่านั้น  </li>
<li>อินพุตเพียงรายการนั้นต้องมี outpoint ที่มี txid เป็นค่าว่าง (ศูนย์ทั้งหมด) และมี output index สูงสุด (0xffffffff) สิ่งนี้ทำให้ coinbase transaction ไม่สามารถอ้างอิงเอาต์พุตของธุรกรรมก่อนหน้า ซึ่งจะสร้างความสับสนเพราะ coinbase transaction มีหน้าที่จ่ายค่าธรรมเนียมและ subsidy  </li>
<li>ฟิลด์ที่โดยปกติจะเป็น input script จะถูกเรียกว่า coinbase ในธุรกรรมประเภทนี้ และนี่คือที่มาของชื่อ coinbase transaction ฟิลด์ coinbase ต้องมีความยาวอย่างน้อย 2 ไบต์ และต้องไม่เกิน 100 ไบต์ สคริปต์นี้ ไม่ถูกประมวลผล แต่ยังคงอยู่ภายใต้กฎจำกัดจำนวน sigops แบบ legacy ดังนั้นข้อมูลใด ๆ ที่ใส่เข้าไปควรถูกนำหน้าด้วย opcode สำหรับการ push data นอกจากนี้ ตั้งแต่ soft fork ปี 2013 (BIP34) ไบต์แรก ๆ ของฟิลด์นี้ต้องปฏิบัติตามกฎเพิ่มเติม ซึ่งจะอธิบายทีหลัง  </li>
<li>ผลรวมของค่าเอาต์พุตทั้งหมดต้องไม่เกินค่าธรรมเนียมจากธุรกรรมทั้งหมดในบล็อกนั้นรวมกับ subsidy โดย subsidy เริ่มต้นที่ 50 BTC ต่อบล็อก และลดลงครึ่งหนึ่งทุก 210,000 บล็อก (ประมาณทุก 4 ปี) โดยปัดเศษลงถึง satoshi ที่ใกล้ที่สุด  </li>
<li>ตั้งแต่ soft fork segwit ปี 2017 (BIP141) บล็อกใดก็ตามที่มีธุรกรรมใช้จ่าย segwit output ต้องมีเอาต์พุตใน coinbase transaction ที่เป็นการ commit ถึงธุรกรรมทั้งหมดในบล็อก (รวมถึง witness ด้วย) รายละเอียดจะอธิบายในส่วน mining</li>
</ul>
<p>coinbase transaction สามารถมีเอาต์พุตอื่น ๆ ที่ถูกต้องตามกฎเหมือนธุรกรรมทั่วไปได้ อย่างไรก็ตาม ธุรกรรมที่ใช้จ่ายเอาต์พุตของ coinbase จะไม่สามารถใส่ในบล็อกได้จนกว่าธุรกรรมนั้นจะมีอย่างน้อย 100 confirmations สิ่งนี้เรียกว่า maturity rule และเอาต์พุตของ coinbase ที่ยังไม่ถึง 100 confirmations จะถูกเรียกว่า immature แต่เนื่องจากลักษณะพิเศษของมัน ทำให้บางครั้งอาจเป็นสาเหตุของปัญหาที่ไม่คาดคิดในซอฟต์แวร์ที่ไม่ได้ออกแบบมาเพื่อรองรับกรณีเหล่านี้</p>
<h2><strong>Weight and Vbytes</strong></h2>
<p>แต่ละบล็อกของบิตคอยน์มีข้อจำกัดปริมาณข้อมูลธุรกรรมที่บรรจุได้ ดังนั้นซอฟต์แวร์บิตคอยน์ส่วนใหญ่จึงต้องสามารถวัดขนาดของธุรกรรมที่สร้างหรือประมวลผลได้ หน่วยวัดสมัยใหม่ที่ใช้คือ weight อีกหน่วยหนึ่งคือ vbytes ซึ่งคำนวณจากการนำ weight มาหารด้วยสี่ ทำให้สามารถเทียบกับหน่วยไบต์ในบล็อกแบบดั้งเดิมได้ง่ายขึ้น</p>
<p>ขนาดสูงสุดของบล็อกคือ <strong>4 ล้าน weight</strong> ส่วน <strong>block header</strong> ใช้พื้นที่ไป 240 weight และช่องข้อมูล <strong>transaction count</strong> ใช้อีก 4 หรือ 12 weight พื้นที่ที่เหลือทั้งหมดสามารถใช้สำหรับข้อมูลธุรกรรมได้</p>
<p>การคำนวณ weight ของฟิลด์หนึ่ง ๆ ในธุรกรรมทำได้โดยการนำจำนวนไบต์ของฟิลด์ที่ serialize แล้วไปคูณกับตัวคูณเฉพาะฟิลด์นั้น ๆ จากนั้นเมื่อต้องการหาน้ำหนักรวมของธุรกรรม ก็เพียงนำ weight ของทุกฟิลด์มาบวกกัน ตัวคูณสำหรับแต่ละฟิลด์ถูกระบุไว้ในตารางข้างล่างนี้ นอกจากนี้ยังมีตัวอย่างการคำนวณ weight ของแต่ละฟิลด์ในธุรกรรมตัวอย่างของบทนี้ (ธุรกรรมจาก Alice ไปยัง Bob)</p>
<p>ตัวคูณเหล่านี้ รวมถึงฟิลด์ที่เกี่ยวข้อง ถูกออกแบบมาเพื่อลดน้ำหนักเมื่อมีการใช้ UTXO ช่วยลดแรงจูงใจในการสร้างเอาต์พุตที่ไม่คุ้มค่า (uneconomical outputs) และหลีกเลี่ยงปัญหา dust ที่ไม่เหมาะสมตามที่อธิบายไว้ในหัวข้อ Uneconomical outputs and disallowed dust</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Factor</th>
<th>Weight in Alice’s Tx</th>
</tr>
</thead>
<tbody><tr>
<td>Version</td>
<td>4</td>
<td>16</td>
</tr>
<tr>
<td>Marker &amp; Flag</td>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>Inputs Count</td>
<td>4</td>
<td>4</td>
</tr>
<tr>
<td>Outpoint</td>
<td>4</td>
<td>144</td>
</tr>
<tr>
<td>Input script</td>
<td>4</td>
<td>4</td>
</tr>
<tr>
<td>Sequence</td>
<td>4</td>
<td>16</td>
</tr>
<tr>
<td>Outputs Count</td>
<td>4</td>
<td>4</td>
</tr>
<tr>
<td>Amount</td>
<td>4</td>
<td>64 (2 outputs)</td>
</tr>
<tr>
<td>Output script</td>
<td>4</td>
<td>232 (2 outputs with different scripts)</td>
</tr>
<tr>
<td>Witness Count</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>Witness items</td>
<td>1</td>
<td>66</td>
</tr>
<tr>
<td>Lock time</td>
<td>4</td>
<td>16</td>
</tr>
<tr>
<td>Total</td>
<td><em>N/A</em></td>
<td>569</td>
</tr>
</tbody></table>
<p>เราสามารถตรวจสอบความถูกต้องของการคำนวณ weight ได้โดยดูค่าน้ำหนักรวมของธุรกรรมของ Alice จาก Bitcoin Core ได้โดยตรง:</p>
<pre><code>$ bitcoin-cli getrawtransaction 466200308696215bbc949d5141a49a41\\

38ecdfdfaa2a8029c1f9bcecd1f96177 2 | jq .weight

569
</code></pre>
<p> <img src="https://image.nostr.build/2e3675764c1b67d8b9e19db81101b336607f7aa8ec79b094fc2244a70a6ad7b2.jpg" alt="image"></p>
<h3><strong>Legacy Serialization</strong></h3>
<p>รูปแบบการซีเรียลไลซ์ที่อธิบายไว้ในบทนี้คือรูปแบบที่ใช้กับธุรกรรมบิตคอยน์ส่วนใหญ่ในปัจจุบัน แต่ก็ยังมีรูปแบบเก่าที่ถูกใช้งานอยู่ในธุรกรรมจำนวนมากเช่นกัน รูปแบบเก่านั้นเรียกว่า <strong>legacy serialization</strong> และต้องใช้บนเครือข่าย Bitcoin P2P สำหรับธุรกรรมที่มีโครงสร้าง witness ว่างเปล่า (ซึ่งจะเกิดขึ้นได้ก็ต่อเมื่อธุรกรรมนั้นไม่ได้ใช้จ่าย witness programs ใด ๆ)</p>
<p>Legacy serialization จะ <strong>ไม่มี</strong> ฟิลด์ต่อไปนี้: marker, flag, witness structure</p>
<p>ในบทนี้ เราได้ดูฟิลด์ต่าง ๆ ในหนึ่งธุรกรรมและเห็นว่าฟิลด์เหล่านั้นสื่อสารข้อมูลให้โหนดเต็มทราบว่าเหรียญควรถูกโอนจากใครไปหาใครอย่างไร เราได้กล่าวถึง output script, input script และ witness structure เพียงคร่าว ๆ ซึ่งมีหน้าที่กำหนดเงื่อนไขและเปิดโอกาสให้ผู้ใช้พิสูจน์สิทธิ์ในการใช้จ่ายเหรียญได้</p>
<p>การเข้าใจวิธีสร้างและใช้งานเงื่อนไขเหล่านี้เป็นสิ่งสำคัญเพื่อให้แน่ใจว่าเฉพาะ Alice เท่านั้นที่จะสามารถใช้จ่ายบิตคอยน์ของเธอได้ ดังนั้นหัวข้อนี้จะเป็นเนื้อหาของบทถัดไป</p>
]]></content:encoded>
      <itunes:author><![CDATA[Learning_BTC&NOSTR]]></itunes:author>
      <itunes:summary><![CDATA[<h1>บทที่ 6:Transactions: ธุรกรรมในบิตคอยน์</h1>
<hr>
<p>วิธีที่เรามักใช้ในการโอนเงินสดนั้นแทบไม่เหมือนกับวิธีที่เราโอนบิตคอยน์เลย เงินสดจริงเป็นโทเค็นที่ผู้ใช้ถือครองได้โดยตรง เช่นเมื่ออลิซจ่ายให้บ็อบโดยการยื่นโทเค็นจำนวนหนึ่ง เช่น การส่งธนบัตรดอลลาร์ให้เขา แต่มันไม่ใช่แบบนั้นเลยสำหรับบิตคอยน์  บิตคอยน์ไม่ได้มีอยู่จริงทั้งในรูปแบบทางกายภาพหรือในรูปแบบข้อมูลดิจิทัล อลิซไม่สามารถยื่นบิตคอยน์ให้บ็อบหรือส่งให้ทางอีเมลได้</p>
<p>ลองจินตนการดูว่าหากอลิซต้องการโอนสิทธ์ในการครอบครองที่ดินผืนหนึ่งให้บ๊อบได้อย่างไร เธอไม่สามารถยกที่ดินขึ้นมาแล้วยื่นให้บ็อบได้ แต่จะมีบันทึกบางอย่าง (ที่โดยทั่วไปดูแลโดยหน่วยงานของรัฐท้องถิ่น) ที่ระบุว่าที่ดินผืนนั้นเป็นของอลิซ แปลว่าอลิซสามารถโอนที่ดินให้บ็อบโดยทำให้รัฐบาลอัปเดตบันทึกนั้นให้ระบุว่าบ็อบเป็นเจ้าของที่ดินแทนเธอ</p>
<p>บิตคอยน์ทำงานในลักษณะคล้ายกัน มีฐานข้อมูลของมันอยู่บนโหนดทุก ๆ โหนดของบิตคอยน์ ที่ระบุว่าอลิซควบคุมบิตคอยน์จำนวนหนึ่ง อลิซจ่ายให้บ็อบโดยทำให้โหนด เหล่านั้นอัปเดตฐานข้อมูลของพวกเขาให้ระบุว่าบิตคอยน์บางส่วนของอลิซตอนนี้อยู่ภายใต้การควบคุมของบ็อบ ข้อมูลที่อลิซใช้เพื่อทำให้โหนดอัปเดตฐานข้อมูลของพวกเขาเรียกว่า “ธุรกรรม” ซึ่งกระทำได้โดยไม่ต้องใช้ตัวตนของอลิซหรือบ็อบโดยตรง</p>
<p>ในบทนี้ เราจะมาวิเคราะห์โครงสร้างของธุรกรรมบิตคอยน์และพิจารณาแต่ละส่วนประกอบ เพื่อดูว่ามันเอื้อต่อการโอนมูลค่าได้อย่างไรในรูปแบบที่มีความยืดหยุ่นและน่าเชื่อถือได้อย่างไร</p>
<h2>Serialized Bitcoin Transaction: ธุรกรรมบิตคอยน์ในรูปแบบลำดับข้อมูล</h2>
<p>ในบทที่ 3 เราได้ใช้ Bitcoin Core โดยเปิดใช้งานตัวเลือก txindex เพื่อดึงสำเนาของธุรกรรมการชำระเงินที่อลิซส่งให้บ็อบ ทีนี้เรามาลองดึงธุรกรรมที่มีการชำระเงินนั้นอีกครั้ง โดยจะแสดงอยู่ในธุรกรรมแบบลำดับข้อมูลของอลิซแทน</p>
<pre><code>$ bitcoin-cli getrawtransaction 466200308696215bbc949d5141a49a41\
38ecdfdfaa2a8029c1f9bcecd1f96177

01000000000101eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da13569
8679268041c54a0100000000ffffffff02204e0000000000002251203b41daba
4c9ace578369740f15e5ec880c28279ee7f51b07dca69c7061e07068f8240100
000000001600147752c165ea7be772b2c0acb7f4d6047ae6f4768e0141cf5efe
2d8ef13ed0af21d4f4cb82422d6252d70324f6f4576b727b7d918e521c00b51b
e739df2f899c49dc267c0ad280aca6dab0d2fa2b42a45182fc83e81713010000
0000
</code></pre>
<p>รูปแบบการลำดับข้อมูลของ Bitcoin Core นั้นมีความพิเศษ เพราะเป็นรูปแบบที่ใช้ในการสร้างคอมมิตเมนต์ของธุรกรรมและส่งต่อธุรกรรมนั้นผ่านเครือข่าย P2P ของบิตคอยน์ อย่างไรก็ตาม โปรแกรมอื่น ๆ สามารถใช้รูปแบบที่ต่างออกไปได้ ตราบใดที่มีการส่งข้อมูลชุดเดียวกันทั้งหมด แต่เนื่องจากรูปแบบของ Bitcoin Core มีความกระทัดรัดเมื่อเทียบกับปริมาณข้อมูลที่ส่ง และยังสามารถแยกวิเคราะห์ได้ง่าย โปรแกรมบิตคอยน์อื่น ๆ จำนวนมากจึงเลือกใช้รูปแบบนี้เช่นกัน</p>
<blockquote>
<p>Tip: รูปแบบการลำดับธุรกรรมอีกแบบหนึ่งที่ถูกใช้อย่างแพร่หลายที่เราทราบคือ partially signed bitcoin transaction (PSBT) ซึ่งมีการอธิบายไว้ใน BIP 174 และ BIP 370 (รวมถึงส่วนขยายที่ระบุไว้ใน BIP อื่น ๆ ) PSBT อนุญาตให้โปรแกรมที่ไม่น่าเชื่อถือสร้างเทมเพลตของธุรกรรมขึ้นมา เพื่อให้โปรแกรมที่เชื่อถือได้ (เช่น อุปกรณ์ฮาร์ดแวร์ที่ใช้ลงนามธุรกรรม) ซึ่งถือกุญแจส่วนตัวหรือข้อมูลสำคัญอื่น ๆ ที่จำเป็น สามารถตรวจสอบและเติมข้อมูลในเทมเพลตนั้นได้ เพื่อให้ทำเช่นนี้ได้ PSBT จึงรองรับการจัดเก็บข้อมูลเมตา (metadata) จำนวนมากเกี่ยวกับธุรกรรม ซึ่งทำให้มันมีขนาดใหญ่และไม่กระทัดรัดเท่ารูปแบบการลำดับข้อมูลมาตรฐาน หนังสือเล่มนี้จะไม่ลงรายละเอียดเกี่ยวกับ PSBT แต่เราแนะนำอย่างยิ่งสำหรับนักพัฒนา wallet ที่ต้องการรองรับการเซ็นชื่อด้วยหลายกุญแจ</p>
</blockquote>
<p>ธุรกรรมที่แสดงในรูปเลขฐาน 16 ข้างต้น ถูกจำลองเป็นแผนผังไบต์ในรูปข้างล่างนี้ โปรดสังเกตว่าการแสดงข้อมูลขนาด 32 ไบต์ต้องใช้เลขฐานสิบหกจำนวน 64 ตัว แผนผังนี้แสดงเฉพาะฟิลด์ในระดับบนสุดเท่านั้น เราจะมาพิจารณาแต่ละฟิลด์ตามลำดับที่มันปรากฏในธุรกรรม พร้อมอธิบายฟิลด์เพิ่มเติมที่อยู่ภายในแต่ละส่วน</p>
<p> <img src="https://image.nostr.build/14264e82a143c3c72a68453c951fb6ec4e0a542186c365eee5483108c9de1be7.jpg" alt="image"></p>
<h2>Version</h2>
<p>ไบต์สี่ตัวแรกของธุรกรรมบิตคอยน์ในรูปแบบลำดับข้อมูล คือ “เวอร์ชัน” ของมัน เวอร์ชันดั้งเดิมของธุรกรรมบิตคอยน์คือเวอร์ชัน 1 (0x01000000) ธุรกรรมทั้งหมดในระบบบิตคอยน์ต้องปฏิบัติตามกฎของธุรกรรมเวอร์ชัน 1 ซึ่งหลายส่วนของหนังสือเล่มนี้ได้อธิบายกฎเหล่านั้นไว้แล้ว</p>
<p>ธุรกรรมบิตคอยน์เวอร์ชัน 2 ถูกนำมาใช้ครั้งแรกในการอัปเกรดแบบซอฟต์ฟอร์กตามข้อเสนอ BIP68 ซึ่งเพิ่มข้อจำกัดเพิ่มเติมกับฟิลด์ sequence แต่ข้อจำกัดนี้จะมีผลเฉพาะกับธุรกรรมเวอร์ชัน 2 ขึ้นไปเท่านั้น ส่วนธุรกรรมเวอร์ชัน 1 จะไม่ได้รับผลกระทบ ข้อเสนอ BIP112 ซึ่งเป็นส่วนหนึ่งของซอฟต์ฟอร์กเดียวกันกับ BIP68 ได้อัปเกรดคำสั่ง OP_CHECKSEQUENCEVERIFY โดยทำให้คำสั่งนี้ล้มเหลวหากถูกประเมินในธุรกรรมที่มีเวอร์ชันต่ำกว่า 2 นอกเหนือจากการเปลี่ยนแปลงสองจุดนี้ ธุรกรรมเวอร์ชัน 2 จะเหมือนกับเวอร์ชัน 1 ทุกประการ</p>
<h3>Protecting Presigned Transactions</h3>
<p>ขั้นตอนสุดท้ายก่อนการกระจายธุรกรรมเข้าสู่เครือข่ายเพื่อบันทึกลงในบล็อกเชนคือ “การลงนาม” ธุรกรรม อย่างไรก็ตาม สามารถลงนามในธุรกรรมได้โดยยังไม่ต้องกระจายออกไปทันที คุณสามารถเก็บธุรกรรมที่ลงนามล่วงหน้าไว้นานหลายเดือนหรือหลายปี โดยเชื่อว่าจะสามารถนำมาบันทึกลงบล็อกเชนได้ในภายหลังเมื่อมีการกระจายธุรกรรมนั้น ในระหว่างนั้นคุณอาจสูญเสียการเข้าถึง private key ที่จำเป็นสำหรับการเซ็นธุรกรรมอื่นเพื่อใช้จ่ายเงินเดียวกันได้ ปรากฏการณ์นี้ไม่ใช่เรื่องสมมติ โปรโตคอลหลายอย่างที่สร้างบนบิตคอยน์ เช่น Lightning Network พึ่งพาธุรกรรมที่ลงนามล่วงหน้าเหล่านี้</p>
<p>สิ่งนี้สร้างความท้าทายให้กับนักพัฒนาโปรโตคอล เมื่อพวกเขาต้องช่วยผู้ใช้อัปเกรดโปรโตคอลฉันทามติของบิตคอยน์ การเพิ่มข้อจำกัดใหม่ เช่นที่ BIP68 เพิ่มไว้ในฟิลด์ sequence  อาจทำให้ธุรกรรมที่ลงนามล่วงหน้าบางรายการกลายเป็นโมฆะ และหากไม่สามารถสร้างลายเซ็นใหม่สำหรับธุรกรรมที่เทียบเท่ากันได้ เงินในธุรกรรมเหล่านั้นก็จะสูญหายไปอย่างถาวร</p>
<p>ปัญหานี้ได้รับการแก้ไขโดยการ “สงวน” ฟีเจอร์บางอย่างของธุรกรรมไว้สำหรับการอัปเกรดในอนาคต เช่น หมายเลขเวอร์ชัน ผู้ที่สร้างธุรกรรมแบบลงนามล่วงหน้าก่อน BIP68 ควรใช้ธุรกรรมเวอร์ชัน 1 ดังนั้นการที่ BIP68 กำหนดข้อจำกัดเพิ่มเติมให้มีผลเฉพาะกับธุรกรรมเวอร์ชัน 2 ขึ้นไป จึงไม่ทำให้ธุรกรรมที่ลงนามไว้ก่อนหน้านั้นเป็นโมฆะ</p>
<p>หากคุณกำลังพัฒนาโปรโตคอลที่ใช้ธุรกรรมแบบลงนามล่วงหน้า ให้แน่ใจว่าโปรโตคอลของคุณไม่ได้ใช้ฟีเจอร์ที่ถูกสงวนไว้สำหรับการอัปเกรดในอนาคต นโยบายการส่งต่อธุรกรรม (relay policy) เริ่มต้นของ Bitcoin Core ไม่อนุญาตให้ใช้ฟีเจอร์ที่ถูกสงวนเหล่านี้ คุณสามารถทดสอบว่าธุรกรรมของคุณสอดคล้องกับนโยบายดังกล่าวหรือไม่ โดยใช้คำสั่ง RPC testmempoolaccept บน Bitcoin mainnet</p>
<p>ณ เวลาที่เขียนนี้ มีข้อเสนอให้เริ่มใช้ธุรกรรมเวอร์ชัน 3 อย่างแพร่หลาย ข้อเสนอนี้ไม่ได้เปลี่ยนแปลงกฎฉันทามติของบิตคอยน์ แต่เปลี่ยนเฉพาะนโยบายที่โหนดใช้ในการส่งต่อธุรกรรม โดยภายใต้ข้อเสนอนั้น ธุรกรรมเวอร์ชัน 3 จะถูกกำหนดข้อจำกัดเพิ่มเติม เพื่อป้องกันการโจมตีแบบ Denial of Service (DoS) บางรูปแบบ ซึ่งเราจะกล่าวถึงเพิ่มเติมในส่วนถัดไป</p>
<h2>Extended Marker and Flag: ตัวระบุ Marker และ Flag แบบขยาย</h2>
<p>ฟิลด์สองช่องถัดไปของตัวอย่างธุรกรรมในรูปแบบลำดับข้อมูล ถูกเพิ่มเข้ามาในกระบวนการอัปเกรดแบบ soft fork ของ Segregated Witness (SegWit) ซึ่งเป็นการเปลี่ยนแปลงในกฎฉันทามติของบิตคอยน์ การเปลี่ยนแปลงนี้อ้างอิงตามข้อเสนอ BIP141 และ BIP143 โดยที่รูปแบบการลำดับข้อมูลแบบขยายนี้ถูกกำหนดไว้ใน BIP144</p>
<p>หากธุรกรรมมีโครงสร้างของ witness (ซึ่งเราจะอธิบายในหัวข้อ “Witness Structure”) ค่า marker ต้องเป็นศูนย์ (0x00) และค่า flag ต้องไม่เป็นศูนย์ ในโปรโตคอล P2P ปัจจุบัน ค่าของ flag ควรเป็นหนึ่ง (0x01) เสมอ โดยมีการสงวนค่าอื่นไว้สำหรับการอัปเกรดโปรโตคอลในอนาคต</p>
<p>หากธุรกรรมไม่มีข้อมูล witness stack ค่า marker และ flag จะต้องไม่ปรากฏอยู่ในธุรกรรม ซึ่งทำให้สามารถทำงานร่วมกับรูปแบบการลำดับข้อมูลดั้งเดิมของบิตคอยน์ได้ หรือที่ปัจจุบันเรียกว่า legacy serialization (รายละเอียดเพิ่มเติมดูได้ในหัวข้อ “Legacy Serialization”)</p>
<p>ในรูปแบบ legacy การตีความค่า marker จะถือว่าเป็นจำนวนของอินพุต (ซึ่งในกรณีนี้คือศูนย์) แต่ธุรกรรมไม่สามารถมีอินพุตเป็นศูนย์ได้ ดังนั้น marker จึงทำหน้าที่เป็นสัญญาณให้โปรแกรมรุ่นใหม่รู้ว่า ธุรกรรมนี้ใช้รูปแบบการลำดับข้อมูลแบบขยาย ส่วนฟิลด์ flag ก็ทำหน้าที่ในลักษณะเดียวกัน และยังช่วยให้การอัปเดตรูปแบบการลำดับข้อมูลในอนาคตเป็นไปได้ง่ายขึ้นอีกด้วย</p>
<h2>Inputs</h2>
<p>ฟิลด์ inputs ประกอบด้วยฟิลด์ย่อยหลายส่วน ดังนั้นเราจะมาเริ่มกันจากการดูแผนผังไบต์ของฟิลด์นี้ในตัวอย่างธุรกรรมของอลิซ ที่แสดงไว้ในภาพด้านล่าง<br> <img src="https://image.nostr.build/604cd0c7982b98dde29a0a676db0f8d28e080317d20a1a81a886730139ff3b11.jpg" alt="image"></p>
<h3>Length of Transaction Input List: ความยาวของรายการอินพุตของธุรกรรม</h3>
<p>รายการอินพุตของธุรกรรมจะเริ่มต้นด้วยจำนวนเต็มที่ระบุจำนวนอินพุตทั้งหมดในธุรกรรมนั้น ค่าต่ำสุดคือหนึ่ง และแม้จะไม่มีการกำหนดค่าสูงสุดอย่างชัดเจน แต่ข้อจำกัดของขนาดธุรกรรมสูงสุดในบิตคอยน์จะจำกัดให้ธุรกรรมหนึ่งมีได้เพียงไม่กี่พันอินพุตเท่านั้น ตัวเลขนี้จะถูกเข้ารหัสในรูปแบบ compactSize unsigned integer</p>
<h3>CompactSize Unsigned Integers: จำนวนเต็มแบบ CompactSize</h3>
<p>จำนวนเต็มที่ไม่มีเครื่องหมาย (unsigned integers) ในบิตคอยน์ ซึ่งโดยทั่วไปมักมีค่าต่ำ แต่บางครั้งอาจมีค่าสูง จะถูกเข้ารหัสโดยใช้ชนิดข้อมูลที่เรียกว่า compactSize ซึ่งเป็นรูปแบบของจำนวนเต็มแบบความยาวแปรผัน (variable-length integer) จึงมักถูกเรียกว่า var_int หรือ varint (ดูรายละเอียดเพิ่มเติมได้ในเอกสารของ BIP37 และ BIP144)</p>
<blockquote>
<p>warning: มีรูปแบบของ “จำนวนเต็มแบบความยาวแปรผัน” (variable-length integers) หลายแบบที่ถูกใช้ในโปรแกรมต่าง ๆ รวมถึงโปรแกรมในระบบบิตคอยน์เองด้วย ตัวอย่างเช่น Bitcoin Core ใช้ชนิดข้อมูลที่เรียกว่า VarInts ในการจัดเก็บฐานข้อมูล UTXO ซึ่ง แตกต่างจาก compactSize นอกจากนี้ ฟิลด์ nBits ในส่วนหัวของบล็อก (block header) ก็ถูกเข้ารหัสด้วยชนิดข้อมูลเฉพาะที่เรียกว่า Compact ซึ่งก็ไม่เกี่ยวข้องกับ compactSize เช่นกัน ดังนั้น เมื่อเราอ้างถึงจำนวนเต็มแบบความยาวแปรผันที่ใช้ในการ serialize ธุรกรรมบิตคอยน์ (Bitcoin transaction) และในส่วนอื่นของ โปรโตคอล P2P ของบิตคอยน์ เราจะใช้คำเต็มว่า compactSize เสมอ เพื่อป้องกันความสับสนกับรูปแบบอื่น ๆ ที่ชื่อคล้ายกันแต่มีโครงสร้างต่างกัน</p>
</blockquote>
<p>สำหรับตัวเลขในช่วง 0 ถึง 252 ค่าของ compactSize unsigned integers จะเหมือนกับชนิดข้อมูล uint8_t ในภาษา C ทุกประการ ซึ่งเป็นรูปแบบการเข้ารหัสตัวเลขที่โปรแกรมเมอร์ส่วนใหญ่คุ้นเคยอยู่แล้ว แต่สำหรับตัวเลขที่มีค่ามากกว่า 252 (จนถึง 0xffffffffffffffff) จะมีการเพิ่ม ไบต์นำหน้า (prefix byte) เพื่อระบุความยาวของจำนวนตัวเลขนั้น อย่างไรก็ตาม นอกจากไบต์นำหน้าแล้ว ตัวเลขส่วนที่เหลือจะยังคงมีลักษณะเหมือนกับการเข้ารหัสของจำนวนเต็มแบบ unsigned ปกติในภาษา C อยู่ดี</p>
<table>
<thead>
<tr>
<th align="left">ค่าตัวเลข (Value)</th>
<th>จำนวนไบต์ที่ใช้ (Bytes used)</th>
<th>รูปแบบ (Format)</th>
</tr>
</thead>
<tbody><tr>
<td align="left">≥ 0 และ ≤ 252 (0xfc)</td>
<td>1</td>
<td>เข้ารหัสเป็น uint8_t</td>
</tr>
<tr>
<td align="left">≥ 253 และ ≤ 0xffff</td>
<td>3</td>
<td>เริ่มด้วย 0xfd ตามด้วยตัวเลขที่เข้ารหัสเป็น uint16_t</td>
</tr>
<tr>
<td align="left">≥ 0x10000 และ ≤ 0xffffffff</td>
<td>5</td>
<td>เริ่มด้วย 0xfe ตามด้วยตัวเลขที่เข้ารหัสเป็น uint32_t</td>
</tr>
<tr>
<td align="left">≥ 0x100000000 และ ≤ 0xffffffffffffffff</td>
<td>9</td>
<td>เริ่มด้วย 0xff ตามด้วยตัวเลขที่เข้ารหัสเป็น uint64_t</td>
</tr>
</tbody></table>
<p>อินพุตแต่ละรายการในธุรกรรมจะต้องมีสามฟิลด์หลัก ได้แก่ Outpoint field, Length-prefixed input script field, และ Sequence</p>
<p>เราจะพิจารณาแต่ละฟิลด์เหล่านี้ในส่วนถัดไป อินพุตบางรายการยังมี witness stack ด้วย แต่จะถูกจัดลำดับข้อมูลไว้ที่ท้ายธุรกรรม ดังนั้นเราจะมาศึกษามันในภายหลัง</p>
<h3>Outpoint</h3>
<p>ธุรกรรมบิตคอยน์คือคำขอที่ส่งให้ full node ทำการอัปเดตฐานข้อมูลเกี่ยวกับข้อมูลความเป็นเจ้าของเหรียญ สำหรับอลิซที่จะโอนบิตคอยน์บางส่วนของเธอให้บ๊อบ เธอต้องบอกให้โหนดรู้ก่อนว่าจะหาธุรกรรมก่อนหน้าที่เธอได้รับบิตคอยน์เหล่านั้นได้จากที่ไหน เนื่องจากการควบคุมบิตคอยน์ถูกกำหนดไว้ใน output ของธุรกรรม อลิซจึงชี้ไปยัง output ก่อนหน้าโดยใช้ฟิลด์ outpoint อินพุตแต่ละรายการต้องมี outpoint หนึ่งรายการเสมอ</p>
<p>outpoint ประกอบด้วย txid ขนาด 32 ไบต์ของธุรกรรมที่อลิซได้รับบิตคอยน์ที่เธอต้องการใช้จ่าย txid นี้อยู่ในลำดับไบต์ภายในของบิตคอยน์สำหรับแฮช (ดูหัวข้อ Internal and Display Byte Orders)</p>
<p>เนื่องจากธุรกรรมหนึ่งรายการอาจมีหลาย output อลิซจึงต้องระบุด้วยว่าเธอจะใช้ output ใดจากธุรกรรมนั้น ซึ่งเรียกว่า output index โดย output index เป็นจำนวนเต็มแบบไม่ติดลบ (unsigned integer) ขนาด 4 ไบต์ เริ่มจากศูนย์</p>
<p>เมื่อ full node พบ outpoint มันจะใช้ข้อมูลนี้เพื่อค้นหา output ที่ถูกอ้างอิง โหนดเต็มจะต้องค้นหาเฉพาะธุรกรรมก่อนหน้าในบล็อกเชนเท่านั้น ตัวอย่างเช่น หากธุรกรรมของอลิซถูกบันทึกไว้ในบล็อก 774,958  full node ที่ตรวจสอบธุรกรรมของเธอจะค้นหา output ที่ถูกอ้างอิงในบล็อกนั้นและบล็อกก่อนหน้าเท่านั้น ไม่ใช่บล็อกที่อยู่หลังจากนั้น ภายในบล็อก 774,958 full node จะค้นหาเฉพาะธุรกรรมที่อยู่ก่อนธุรกรรมของอลิซ โดยพิจารณาตามลำดับของ leaf ใน merkle tree ของบล็อกนั้น </p>
<p>เมื่อพบ output ก่อนหน้าแล้ว full node จะได้รับข้อมูลสำคัญหลายอย่างจากมัน ได้แก่</p>
<ul>
<li><p>จำนวนบิตคอยน์ที่ถูกกำหนดไว้ใน output ก่อนหน้า: บิตคอยน์ทั้งหมดใน output นั้นจะถูกโอนไปในธุรกรรมนี้ ตัวอย่างเช่น ในธุรกรรมตัวอย่าง ค่าใน output ก่อนหน้าคือ 100,000 satoshis</p>
</li>
<li><p>เงื่อนไขการอนุญาตของ output ก่อนหน้า: เงื่อนไขที่ต้องถูกปฏิบัติตามเพื่อที่จะใช้จ่ายบิตคอยน์ที่ถูกกำหนดไว้ใน output นั้น</p>
</li>
<li><p>สำหรับธุรกรรมที่ได้รับการยืนยันแล้ว (confirmed transactions) full node จะทราบ block height ที่ธุรกรรมได้รับการยืนยัน และ ค่า median time past (MTP) ของบล็อกนั้น ข้อมูลนี้จำเป็นสำหรับการทำงานของ relative timelock (อธิบายไว้ในหัวข้อ Sequence as a consensus-enforced relative timelock) และสำหรับ output ของ coinbase transaction (อธิบายไว้ในหัวข้อ Coinbase Transactions)</p>
</li>
<li><p>หลักฐานว่า output ก่อนหน้านั้นมีอยู่จริงในบล็อกเชน (หรือเป็นธุรกรรมที่ยังไม่ได้ยืนยันแต่เป็นที่รู้จัก) และยังไม่มีธุรกรรมอื่นใดที่ใช้มันไปแล้ว หนึ่งในกฎฉันทามติของบิตคอยน์ห้ามไม่ให้ output เดียวกันถูกใช้มากกว่าหนึ่งครั้งภายในบล็อกเชน นี่คือกฎที่ป้องกัน double spending อลิซไม่สามารถใช้ output เดิมเดียวกันเพื่อจ่ายทั้งให้บ๊อบและแครอลในธุรกรรมที่แยกกันได้ สองธุรกรรมที่พยายามใช้ output เดียวกันเรียกว่า conflicting transactions เพราะมีเพียงหนึ่งในนั้นเท่านั้นที่สามารถถูกบันทึกในบล็อกเชนได้</p>
</li>
</ul>
<p>แนวทางที่แตกต่างกันในการติดตาม output ก่อนหน้าได้ถูกทดลองใช้โดยการทำงานของ full node ที่แตกต่างกันในช่วงเวลาต่าง ๆ ส่วนในปัจจุบัน Bitcoin Core ใช้วิธีที่เชื่อว่าได้ผลดีที่สุดในการเก็บรักษาข้อมูลที่จำเป็นทั้งหมดในขณะที่ลดการใช้พื้นที่ดิสก์ให้น้อยที่สุด คือมันเก็บฐานข้อมูลที่บันทึกทุก UTXO และเมตาดาต้าที่จำเป็นเกี่ยวกับมัน (เช่น block height) ทุกครั้งที่บล็อกใหม่ของธุรกรรมเข้ามา output ทั้งหมดที่ถูกใช้จะถูกลบออกจากฐานข้อมูล UTXO และ output ทั้งหมดที่ถูกสร้างขึ้นใหม่จะถูกเพิ่มเข้าไปในฐานข้อมูล</p>
<h3>Internal and Display Byte Orders(การจัดเรียงไบต์ภายในและสำหรับการแสดงผล)</h3>
<p>บิตคอยน์ได้ใช้ hash function ที่เรียกว่า digest ในหลายรูปแบบ Digest ถูกใช้เป็นตัวระบุเฉพาะสำหรับบล็อกและธุรกรรม ใช้ในกระบวนการยืนยันความถูกต้องของ address บล็อก ธุรกรรม ลายเซ็น และอื่น ๆ อีกมากมาย นอกจากนี้ digest ยังถูกนำมาใช้ซ้ำในฟังก์ชัน proof-of-work ของ Bitcoin ในบางกรณี digest ของ hash จะถูกแสดงให้ผู้ใช้เห็นในรูปแบบการจัดเรียงไบต์หนึ่ง แต่ระบบภายในกลับใช้รูปแบบการจัดเรียงไบต์อีกแบบหนึ่ง ซึ่งอาจก่อให้เกิดความสับสนได้ ตัวอย่างเช่น ลองพิจารณา txid ของ output ก่อนหน้าใน outpoint ของธุรกรรมตัวอย่างของเรา</p>
<pre><code>eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da135698679268041c54a
</code></pre>
<p>ถ้าเราพยายามใช้ txid นั้นเพื่อดึงข้อมูลธุรกรรมจาก Bitcoin Core เราจะพบข้อผิดพลาด และจำเป็นต้องสลับลำดับไบต์ของมันก่อน</p>
<pre><code>$ bitcoin-cli getrawtransaction \
  eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da135698679268041c54a
error code: -5
error message:
No such mempool or blockchain transaction.
Use gettransaction for wallet transactions.
$ echo eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da135698679268041c54a \
  | fold -w2 | tac | tr -d "\n"
4ac541802679866935a19d4f40728bb89204d0cac90d85f3a51a19278fe33aeb
$ bitcoin-cli getrawtransaction \
  4ac541802679866935a19d4f40728bb89204d0cac90d85f3a51a19278fe33aeb
02000000000101c25ae90c9f3d40cc1fc509ecfd54b06e35450702…
</code></pre>
<p>พฤติกรรมแปลก ๆ นี้น่าจะเป็นผลที่เกิดขึ้นโดยไม่ได้ตั้งใจจากการตัดสินใจออกแบบในซอฟต์แวร์บิตคอยน์ยุคแรก ๆ ในทางปฏิบัติ นั่นหมายความว่านักพัฒนาซอฟต์แวร์บิตคอยน์จำเป็นต้องจำไว้ว่าต้องสลับลำดับไบต์ในตัวระบุธุรกรรมและบล็อกก่อนที่จะแสดงให้ผู้ใช้เห็น</p>
<p>ในหนังสือเล่มนี้ เราใช้คำว่า ลำดับไบต์ภายใน (internal byte order) สำหรับข้อมูลที่ปรากฏอยู่ภายในธุรกรรมและบล็อก และใช้คำว่า ลำดับไบต์สำหรับแสดงผล (display byte order) สำหรับรูปแบบที่แสดงต่อผู้ใช้ อีกชุดของคำที่มักใช้กันคือ ลำดับไบต์แบบ little-endian สำหรับรูปแบบภายใน และ ลำดับไบต์แบบ big-endian สำหรับรูปแบบที่แสดงผล</p>
<blockquote>
<p>อันนี้หลามเสริมให้ มันคือการแบ่งทีละสองตำแหน่งและนับว่านั่นเป็นหนึ่ง เช่น abcdef ก็จะเป็น ab คือ 1 cd คือ 2 ef คือ 3 พอแปลงเป็น display byte order ก็ทำการสลับเป็น 3,2,1 หรือ efcdab</p>
</blockquote>
<h2>Input Script</h2>
<p>ฟิลด์ input script เป็นส่วนที่หลงเหลือมาจากรูปแบบธุรกรรมแบบดั้งเดิม (legacy transaction format) ตัวอย่างธุรกรรมของเราเป็น input ที่ใช้จ่าย output แบบ native segwit ซึ่งไม่จำเป็นต้องมีข้อมูลใน input script ดังนั้นค่านำหน้าความยาว (length prefix) ของ input script จึงถูกตั้งค่าเป็นศูนย์ (0x00)</p>
<p>สำหรับตัวอย่างของ input script ที่มีค่านำหน้าความยาวและใช้จ่าย output แบบดั้งเดิม เราจะใช้ตัวอย่างจากธุรกรรมหนึ่งในบล็อกล่าสุดในขณะที่เขียนอยู่นี้</p>
<pre><code>6b483045022100a6cc4e8cd0847951a71fad3bc9b14f24d44ba59d19094e0a8c
fa2580bb664b020220366060ea8203d766722ed0a02d1599b99d3c95b97dab8e
41d3e4d3fe33a5706201210369e03e2c91f0badec46c9c903d9e9edae67c167b
9ef9b550356ee791c9a40896
</code></pre>
<p>ค่านำหน้าความยาว (length prefix) เป็นจำนวนเต็มแบบ compactSize unsigned integer ที่ระบุความยาวของฟิลด์ input script ที่ถูกซีเรียลไลซ์ไว้ ในกรณีนี้เป็นไบต์เดียว (0x6b) ซึ่งระบุว่า input script มีความยาว 107 ไบต์</p>
<h2>Sequence(ลำดับ)</h2>
<p>ไบต์สี่ตัวสุดท้ายของ input คือหมายเลขลำดับ (sequence number) ซึ่งการใช้งานและความหมายของฟิลด์นี้ได้มีการเปลี่ยนแปลงไปตามกาลเวลา</p>
<h3>การแทนที่ธุรกรรมโดยอิงตามลำดับ (Original sequence-based transaction replacement)</h3>
<p>เดิมทีฟิลด์ sequence ถูกออกแบบมาเพื่อให้สามารถสร้างหลายเวอร์ชันของธุรกรรมเดียวกันได้ โดยที่เวอร์ชันหลังสามารถแทนที่เวอร์ชันก่อนหน้าในฐานะตัวเลือกสำหรับการยืนยัน หมายเลขลำดับทำหน้าที่ติดตามเวอร์ชันของธุรกรรม</p>
<p>ตัวอย่างเช่น ลองจินตนาการว่าอลิซและบ็อบต้องการเดิมพันผลเกมไพ่ พวกเขาเริ่มจากการลงนามธุรกรรมที่แต่ละคนฝากเงินไว้ในเอาต์พุตซึ่งต้องการลายเซ็นจากทั้งคู่เพื่อใช้จ่าย ซึ่งเรียกว่า สคริปต์หลายลายเซ็น (multisignature script หรือ multisig) ธุรกรรมนี้เรียกว่า setup transaction จากนั้นพวกเขาสร้างธุรกรรมที่ใช้จ่ายเอาต์พุตนั้นดังนี้</p>
<ul>
<li>เวอร์ชันแรกของธุรกรรม โดยมีค่า <code>nSequence</code> เท่ากับ 0 (0x00000000) เป็นการคืนเงินให้อลิซและบ็อบตามจำนวนที่แต่ละคนฝากไว้ในตอนแรก ธุรกรรมนี้เรียกว่า <em>refund transaction</em> ซึ่งทั้งคู่ยังไม่เผยแพร่ในตอนนี้ จะใช้ก็ต่อเมื่อเกิดปัญหาเท่านั้น  </li>
<li>อลิซชนะรอบแรกของเกมไพ่ ธุรกรรมเวอร์ชันที่สองมีค่า <em>sequence</em> เป็น 1 และปรับจำนวนเงินที่อลิซได้รับให้มากขึ้น ในขณะที่ส่วนของบ็อบลดลง ทั้งคู่ลงนามในธุรกรรมที่อัปเดตแล้วนี้อีกครั้ง แต่ก็ยังไม่เผยแพร่ เว้นแต่จะมีเหตุจำเป็น  </li>
<li>บ็อบชนะรอบที่สอง ลำดับจึงถูกเพิ่มเป็น 2 ส่วนแบ่งของอลิซลดลงและของบ็อบเพิ่มขึ้น ทั้งคู่ลงนามอีกครั้งแต่ยังไม่เผยแพร่</li>
</ul>
<p>หลังจากเล่นอีกหลายรอบ โดยที่ค่า sequence ถูกเพิ่มขึ้นในแต่ละรอบ เงินถูกปรับสมดุลใหม่ และธุรกรรมถูกลงนามแต่ยังไม่เผยแพร่ พวกเขาตัดสินใจจะสิ้นสุดเกม ด้วยการสร้างธุรกรรมสุดท้ายที่สะท้อนยอดเงินสุดท้ายของทั้งคู่ จากนั้นตั้งค่า sequence เป็นค่าสูงสุด (0xffffffff) เพื่อเป็นการ “สิ้นสุด” ธุรกรรมนี้ พวกเขาจึงเผยแพร่เวอร์ชันนี้ออกไป ธุรกรรมถูกส่งต่อไปทั่วเครือข่าย และในที่สุดก็ได้รับการยืนยันโดยนักขุด</p>
<p>เราสามารถเห็นกฎการแทนที่ของ sequence ทำงานอย่างไรได้ หากพิจารณาสถานการณ์ทางเลือกดังต่อไปนี้</p>
<ul>
<li><p>ลองจินตนาการว่าอลิซเผยแพร่ธุรกรรมสุดท้ายที่มีค่า sequence เท่ากับ 0xffffffff จากนั้นบ็อบเผยแพร่ธุรกรรมเวอร์ชันก่อนหน้าที่เขาได้รับส่วนแบ่งมากกว่า เนื่องจากเวอร์ชันของบ็อบมีหมายเลขลำดับต่ำกว่า โหนดเต็ม (full nodes) ที่ใช้โค้ดบิตคอยน์เวอร์ชันดั้งเดิมจะไม่ส่งต่อธุรกรรมนั้นไปยังนักขุด และนักขุดที่ใช้โค้ดเวอร์ชันเดิมก็จะไม่ขุดมันเช่นกัน</p>
</li>
<li><p>ในอีกสถานการณ์หนึ่ง สมมติว่าบ็อบเผยแพร่ธุรกรรมเวอร์ชันก่อนหน้าเพียงไม่กี่วินาทีก่อนที่อลิซจะเผยแพร่เวอร์ชันสุดท้าย โหนดต่าง ๆ จะส่งต่อเวอร์ชันของบ็อบและนักขุดจะพยายามขุดมัน แต่เมื่อเวอร์ชันของอลิซที่มีหมายเลขลำดับสูงกว่ามาถึง โหนดก็จะส่งต่อเวอร์ชันของอลิซด้วย และนักขุดที่ใช้โค้ดบิตคอยน์เวอร์ชันดั้งเดิมจะพยายามขุดเวอร์ชันของอลิซแทนเวอร์ชันของบ็อบ เว้นแต่ว่าบ็อบจะโชคดีและมีการค้นพบบล็อกก่อนที่เวอร์ชันของอลิซจะมาถึง มิฉะนั้นธุรกรรมเวอร์ชันของอลิซจะเป็นเวอร์ชันที่ได้รับการยืนยันในที่สุด</p>
</li>
</ul>
<p>โปรโตคอลประเภทนี้คือสิ่งที่เราเรียกว่า payment channel ในปัจจุบัน ผู้สร้างบิตคอยน์ได้กล่าวถึงในอีเมลที่มีการอ้างถึงเขา เรียกธุรกรรมประเภทนี้ว่า high-frequency transactions และได้อธิบายคุณสมบัติบางอย่างที่เพิ่มเข้าไปในโปรโตคอลเพื่อรองรับการใช้งานเหล่านี้ เราจะเรียนรู้เกี่ยวกับคุณสมบัติอื่น ๆ เหล่านี้ในภายหลัง และยังจะได้เห็นด้วยว่าเวอร์ชันสมัยใหม่ของ payment channel ถูกนำมาใช้ในบิตคอยน์อย่างแพร่หลายมากขึ้นในปัจจุบัน</p>
<p>มีปัญหาบางประการกับ payment channel ที่อิงตาม sequence เพียงอย่างเดียว ปัญหาแรกคือกฎสำหรับการแทนที่ธุรกรรมที่มี sequence ต่ำด้วยธุรกรรมที่มี sequence สูงเป็นเพียงนโยบายของซอฟต์แวร์เท่านั้น ไม่มีแรงจูงใจโดยตรงให้นักขุดชอบเวอร์ชันใดเวอร์ชันหนึ่งมากกว่าเวอร์ชันอื่น ปัญหาที่สองคือคนแรกที่ส่งธุรกรรมอาจโชคดีให้ธุรกรรมนั้นได้รับการยืนยัน แม้ว่าจะไม่ใช่ธุรกรรมที่มี sequence สูงสุดก็ตาม โปรโตคอลด้านความปลอดภัยที่ล้มเหลวบ้างเพียงไม่กี่เปอร์เซ็นต์เพราะโชคไม่ดีจึงไม่ใช่โปรโตคอลที่มีประสิทธิภาพนัก</p>
<p>ปัญหาที่สามคือสามารถแทนที่เวอร์ชันหนึ่งของธุรกรรมด้วยเวอร์ชันที่ต่างออกไปได้โดยไม่จำกัดจำนวนครั้ง แต่ละครั้งที่แทนที่จะใช้แบนด์วิดท์ของโหนดเต็มที่ทำหน้าที่ส่งต่อบนเครือข่ายทั้งหมด ยกตัวอย่างเช่น ณ เวลาที่เขียนนี้ มี full node ที่ทำหน้าที่ส่งต่อประมาณ 50,000 โหนด; ผู้โจมตีที่สร้างธุรกรรมแทนที่ 1,000 รายการต่อหนึ่งนาที โดยแต่ละรายการมีขนาด 200 ไบต์ จะใช้แบนด์วิดท์ส่วนตัวของเขาประมาณ 20 KB ต่อหนึ่งนาที แต่จะใช้แบนด์วิดท์เครือข่ายของ full node ประมาณ 10 GB ทุก ๆ นาที นอกจากค่าใช้จ่ายแบนด์วิดท์ส่วนตัว 20 KB/นาที และค่าธรรมเนียมเพียงเป็นครั้งคราวเมื่อต้องมีธุรกรรมที่ถูกยืนยัน ผู้โจมตีจะไม่ต้องจ่ายค่าใช้จ่ายใด ๆ สำหรับภาระอันมหาศาลที่พวกเขาก่อขึ้นกับผู้ดำเนิน full node เลย</p>
<p>เพื่อกำจัดความเสี่ยงจากการโจมตีนี้ รูปแบบการแทนที่ธุรกรรมที่อิงตาม sequence แบบเดิมถูกปิดใช้งานในเวอร์ชันแรก ๆ ของซอฟต์แวร์บิตคอยน์ ในช่วงหลายปี  full node ของบิตคอยน์จะไม่อนุญาตให้ธุรกรรมที่ยังไม่ได้ยืนยันซึ่งมีอินพุตหนึ่ง (ตามที่ระบุโดย outpoint) ถูกแทนที่ด้วยธุรกรรมอื่นที่มีอินพุตเดียวกัน อย่างไรก็ตาม สถานการณ์นั้นไม่ได้ดำเนินต่อไปตลอดเวลา</p>
<h3>การสัญญาณเพื่อแทนที่ธุรกรรมแบบ Opt-in</h3>
<p>หลังจากรูปแบบการแทนที่ธุรกรรมที่อิงตาม sequence แบบเดิมถูกปิดใช้งานเนื่องจากมีความเสี่ยงที่จะถูกละเมิด ได้มีการเสนอวิธีแก้ไข: การโปรแกรม Bitcoin Core และซอฟต์แวร์โหนดเต็มที่ส่งต่อธุรกรรมอื่น ๆ ให้อนุญาตให้ธุรกรรมที่จ่ายค่าธรรมเนียมสูงแทนที่ธุรกรรมที่ขัดแย้งและจ่ายค่าธรรมเนียมต่ำกว่า เรียกวิธีนี้ว่า replace by fee (RBF)</p>
<p>ผู้ใช้และธุรกิจบางรายคัดค้านการเพิ่มการสนับสนุนการแทนที่ธุรกรรมกลับเข้าไปใน Bitcoin Core ดังนั้นจึงได้ข้อยุติด้วยการใช้ฟิลด์ sequence อีกครั้งเพื่อสนับสนุนการแทนที่</p>
<p>ตามที่ระบุใน BIP125 ธุรกรรมที่ยังไม่ได้ยืนยันซึ่งมีอินพุตใด ๆ ที่ตั้งค่า sequence ต่ำกว่า 0xfffffffe (เช่น อย่างน้อย 2 ต่ำกว่าค่าสูงสุด) จะส่งสัญญาณไปยังเครือข่ายว่าผู้ลงนามต้องการให้ธุรกรรมนี้สามารถถูกแทนที่ด้วยธุรกรรมที่จ่ายค่าธรรมเนียมสูงกว่าได้ Bitcoin Core อนุญาตให้ธุรกรรมที่ยังไม่ได้ยืนยันเหล่านี้ถูกแทนที่ได้ และยังคงไม่อนุญาตให้ธุรกรรมอื่น ๆ ถูกแทนที่ ทำให้ผู้ใช้และธุรกิจที่คัดค้านการแทนที่สามารถเพิกเฉยต่อธุรกรรมที่ยังไม่ได้ยืนยันซึ่งมีสัญญาณ BIP125 จนกว่าจะได้รับการยืนยัน</p>
<p>นโยบายการแทนที่ธุรกรรมสมัยใหม่มีมากกว่าการพิจารณาค่าธรรมเนียมและสัญญาณ sequence ซึ่งเราจะเห็นรายละเอียดเพิ่มเติมในหัวข้อ rbf</p>
<h3>Sequence ในฐานะกลไกกำหนดเวลาล็อกแบบสัมพัทธ์ (relative timelock) ที่ถูกบังคับโดยฉันทามติ</h3>
<p>ในหัวข้อ Version เราได้เรียนรู้ว่า soft fork BIP68 ได้เพิ่มข้อจำกัดใหม่ให้กับธุรกรรมที่มีหมายเลขเวอร์ชัน 2 ขึ้นไป ซึ่งข้อจำกัดนั้นถูกใช้กับฟิลด์ sequence</p>
<p>อินพุตของธุรกรรมที่มีค่า sequence น้อยกว่า 2³¹ จะถูกตีความว่ามีการตั้ง relative timelock และธุรกรรมดังกล่าวจะสามารถถูกบรรจุเข้าไปในบล็อกเชนได้ก็ต่อเมื่อเอาต์พุตก่อนหน้า (ที่ถูกอ้างอิงโดย outpoint) ผ่านระยะเวลาเท่ากับค่าของ relative timelock มาแล้ว ตัวอย่างเช่น ธุรกรรมที่มีอินพุตหนึ่งตัวตั้ง relative timelock ไว้ 30 บล็อก จะสามารถถูกยืนยันในบล็อกที่มีอย่างน้อย 29 บล็อกคั่นอยู่ระหว่างบล็อกนั้นกับบล็อกที่มีเอาต์พุตซึ่งกำลังถูกใช้จ่ายอยู่บนบล็อกเชนเดียวกัน เนื่องจาก sequence เป็นฟิลด์ที่ผูกกับแต่ละอินพุต ธุรกรรมหนึ่งรายการอาจมีอินพุตหลายตัวที่มีการตั้ง timelock และอินพุตทั้งหมดต้องผ่านช่วงเวลาตามที่กำหนดแล้วจึงทำให้ธุรกรรมถือว่าถูกต้อง มี disable flag ที่อนุญาตให้ธุรกรรมสามารถรวมอินพุตแบบมี relative timelock (sequence &lt; 2³¹) และอินพุตที่ไม่มี relative timelock (sequence ≥ 2³¹) ไว้ในธุรกรรมเดียวกันได้</p>
<p>ค่า sequence สามารถกำหนดเป็นหน่วยบล็อกหรือหน่วยวินาทีได้ โดยมี type-flag ใช้แยกความแตกต่างระหว่างค่าที่นับเป็นบล็อกกับค่าที่นับเป็นวินาที type-flag จะถูกตั้งค่าไว้ที่บิตลำดับที่ 23 (กล่าวคือค่า 1&lt;&lt;22) หาก type-flag ถูกตั้งค่า ค่า sequence จะถูกตีความเป็นจำนวนเท่าของ 512 วินาที แต่ถ้า type-flag ไม่ได้ถูกตั้งค่า ค่า sequence จะถูกตีความเป็นจำนวนบล็อก เมื่อทำการตีความ sequence เป็น relative timelock จะพิจารณาเฉพาะ 16 บิตที่มีค่าน้อยที่สุดเท่านั้น เมื่อทำการประเมินค่าสถานะของ flags แล้ว (บิต 32 และบิต 23) ค่า sequence จะถูก “mask” ด้วย mask ขนาด 16 บิต (เช่น sequence &amp; 0x0000FFFF) ค่าหนึ่งเท่าของ 512 วินาทีมีค่าประมาณเท่ากับเวลาเฉลี่ยระหว่างบล็อก ดังนั้นค่า relative timelock สูงสุดจาก 16 บิต (2¹⁶) ทั้งในหน่วยบล็อกและหน่วยวินาที จะมีค่าเกินหนึ่งปีเล็กน้อย</p>
<p>คำจำกัดความของ sequence encoding ตาม BIP68 (ที่มา: BIP68) แสดงผังไบนารีของค่า sequence ตามที่กำหนดใน BIP68<br> <img src="https://image.nostr.build/a19cad6116cdee116a4b577c21c4b4d5861df50e7874d1da00ca9286e25970cf.jpg" alt="image"></p>
<h2>Outputs</h2>
<p>ฟิลด์ outputs ของธุรกรรมประกอบด้วยฟิลด์หลายตัวที่เกี่ยวข้องกับเอาต์พุตแต่ละรายการ เช่นเดียวกับที่เราทำกับฟิลด์ inputs เราจะเริ่มจากการดูไบต์เฉพาะของฟิลด์ outputs จากตัวอย่างธุรกรรมที่ Alice จ่ายให้ Bob ซึ่งถูกแสดงในรูปแบบแผนที่ของไบต์เหล่านั้นในตัวอย่างตอนต้นของบทนี้<br> <img src="https://image.nostr.build/d9e1d6dbe12c88189f42ee36cc478a37039a81fb4cfa13e3efad7e97eb40a5e0.jpg" alt="image"></p>
<h3><strong>Output count (จำนวน Outputs)</strong></h3>
<p>เช่นเดียวกับจุดเริ่มต้นของส่วน inputs ของธุรกรรม ฟิลด์ outputs จะเริ่มต้นด้วยตัวนับที่ระบุจำนวนของ outputs ในธุรกรรมนี้ ซึ่งเป็น compactSize integer และต้องมีค่ามากกว่าศูนย์ โดยธุรกรรมตัวอย่างมีสอง outputs</p>
<h3><strong>Amount(จำนวนเงิน)</strong></h3>
<p>ฟิลด์แรกของ output หนึ่งรายการคือจำนวนเงินของมัน ซึ่งใน Bitcoin Core เรียกว่า “value” เป็น signed integer ขนาด 8 ไบต์ที่ระบุจำนวน satoshis ที่ต้องการโอน (satoshi คือหน่วยที่เล็กที่สุดของ bitcoin ที่สามารถแสดงได้ในธุรกรรมบนบล็อกเชน หนึ่ง bitcoin มี 100 ล้าน satoshis) และในกฎฉันทามติของ Bitcoin อนุญาตให้ output มีค่าตั้งแต่ศูนย์จนถึงสูงสุด 21 ล้าน bitcoins (2.1 quadrillion satoshis)</p>
<h4>Uneconomical outputs and disallowed dust (เอาต์พุตที่ไม่คุ้มค่าเชิงเศรษฐกิจและฝุ่น (dust) ที่ถูกห้าม)</h4>
<p>แม้ว่าจะไม่มีมูลค่า แต่เอาต์พุตที่มีค่าเป็นศูนย์สามารถถูกใช้จ่ายได้ภายใต้กฎเดียวกับเอาต์พุตอื่น ๆ อย่างไรก็ตาม การใช้จ่ายเอาต์พุต (นำมันไปใช้เป็นอินพุตในธุรกรรม) ทำให้ขนาดของธุรกรรมเพิ่มขึ้น ซึ่งทำให้ค่าธรรมเนียมที่ต้องจ่ายเพิ่มขึ้นด้วย หากมูลค่าของเอาต์พุตมีค่าน้อยกว่าค่าธรรมเนียมที่ต้องจ่ายเพิ่ม การใช้จ่ายเอาต์พุตนั้นก็ไม่มีความคุ้มค่าทางเศรษฐกิจ เอาต์พุตเช่นนี้เรียกว่า uneconomical outputs</p>
<p>เอาต์พุตที่มีค่าเป็นศูนย์เป็น uneconomical output เสมอ เพราะมันจะไม่เพิ่มมูลค่าใด ๆ ให้กับธุรกรรมที่ใช้มัน แม้ว่าอัตราค่าธรรมเนียมจะเป็นศูนย์ก็ตาม แต่อย่างไรก็ตาม เอาต์พุตอื่น ๆ ที่มีมูลค่าต่ำก็อาจเป็น uneconomical เช่นกัน แม้จะไม่ได้ตั้งใจ ตัวอย่างเช่น ที่อัตราค่าธรรมเนียมปกติบนเครือข่ายในวันนี้ เอาต์พุตหนึ่งอาจเพิ่มมูลค่ามากกว่าค่าใช้จ่ายในการนำไปใช้ แต่วันพรุ่งนี้อัตราค่าธรรมเนียมอาจสูงขึ้นและทำให้เอาต์พุตนั้นกลายเป็น uneconomical</p>
<p>ความจำเป็นที่ full node ต้องติดตาม UTXO ทั้งหมด ตามที่อธิบายไว้ในหัวข้อ Outpoint หมายความว่า UTXO ทุกตัวทำให้การรัน full node ยากขึ้นเล็กน้อย สำหรับ UTXO ที่มีมูลค่ามาก ผู้ใช้มีแรงจูงใจที่จะใช้จ่ายมันในที่สุด ดังนั้นมันไม่ก่อปัญหา แต่สำหรับผู้ที่ถือ UTXO ที่ไม่คุ้มค่าทางเศรษฐกิจ ไม่มีแรงจูงใจที่จะใช้จ่ายมันเลย ซึ่งอาจทำให้มันกลายเป็นภาระถาวรสำหรับผู้ดูแล full node และเนื่องจากการกระจายศูนย์ของ Bitcoin ขึ้นอยู่กับจำนวนผู้ที่ยินดีรัน full node ซอฟต์แวร์ full node หลาย ๆ ตัว เช่น Bitcoin Core จึงใช้ข้อกำหนดที่ส่งผลต่อการส่งต่อธุรกรรมและการขุดธุรกรรมที่ยังไม่ได้คอนเฟิร์ม เพื่อป้องกันการสร้างเอาต์พุตที่ไม่คุ้มค่าทางเศรษฐกิจ</p>
<p>ข้อกำหนดที่ห้ามการส่งต่อหรือการขุดธุรกรรมที่สร้างเอาต์พุตที่ไม่คุ้มค่าเรียกว่า dust policies ตั้งตามการเปรียบเทียบเชิงสัญลักษณ์ระหว่างเอาต์พุตที่มีมูลค่าน้อยมากกับอนุภาคที่มีขนาดเล็กมาก (เล็กจนเป็นฝุ่น) dust policy ของ Bitcoin Core นั้นซับซ้อนและมีตัวเลขกำหนดหลายค่า ดังนั้นโปรแกรมจำนวนมากมักถือเอาว่าเอาต์พุตที่มีค่าน้อยกว่า 546 satoshis คือ dust และจะไม่ถูกส่งต่อหรือขุดตามค่าตั้งต้น แต่ก็มีบ้างเป็นครั้งคราวที่มีข้อเสนอให้ลดขีดจำกัดของ dust และก็มีข้อเสนอคัดค้านให้เพิ่มขีดจำกัดเช่นกัน ดังนั้นเราขอแนะนำให้นักพัฒนาที่ใช้ presigned transactions หรือโปรโตคอลแบบหลายฝ่าย ตรวจสอบว่าข้อกำหนดนี้มีการเปลี่ยนแปลงตั้งแต่วันที่หนังสือเล่มนี้เผยแพร่หรือไม่</p>
<blockquote>
<p>TIP: ตั้งแต่การกำเนิดของ Bitcoin full node ทุกตัวจำเป็นต้องเก็บสำเนาของ UTXO ทุกตัว แต่สิ่งนี้อาจไม่จำเป็นต้องเป็นเช่นนั้นตลอดไป นักพัฒนาหลายคนกำลังทำงานกับ Utreexo ซึ่งเป็นโปรเจกต์ที่ทำให้โหนดเต็มสามารถเก็บข้อมูลแบบ commitment ต่อชุดของ UTXO แทนการเก็บข้อมูลจริง commitment ที่เล็กที่สุดอาจมีขนาดเพียงหนึ่งหรือสองกิโลไบต์—เทียบกับข้อมูลมากกว่า 5 กิกะไบต์ที่ Bitcoin Core เก็บ ณ เวลาที่เขียนหนังสือนี้ (และตอนที่ผมแปลอยู่ที่ประมาณ 11 GB) </p>
</blockquote>
<blockquote>
<p>อย่างไรก็ตาม Utreexo จะยังคงต้องให้บางโหนดเก็บข้อมูล UTXO ทั้งหมด โดยเฉพาะโหนดที่ให้บริการนักขุดหรือการดำเนินงานอื่น ๆ ที่ต้องตรวจสอบบล็อกใหม่อย่างรวดเร็ว นั่นหมายความว่า uneconomical outputs ก็ยังคงเป็นปัญหาสำหรับ full node แม้ในอนาคตที่เป็นไปได้ซึ่งโหนดส่วนใหญ่ใช้ Utreexo ก็ตาม</p>
</blockquote>
<p>กฎนโยบายของ Bitcoin Core เกี่ยวกับ dust มีข้อยกเว้นหนึ่งข้อ: output script ที่เริ่มต้นด้วย OP_RETURN ซึ่งเรียกว่า data carrier outputs สามารถมีมูลค่าเป็นศูนย์ได้ OP_RETURN opcode ทำให้สคริปต์ล้มเหลวทันทีไม่ว่าจะมีอะไรตามมาหลังจากนั้น ดังนั้นเอาต์พุตเหล่านี้ไม่สามารถถูกใช้จ่ายได้เลย นั่นหมายความว่า full node ไม่จำเป็นต้องติดตามเอาต์พุตเหล่านี้ ซึ่งเป็นคุณสมบัติที่ Bitcoin Core ใช้เพื่อให้ผู้ใช้สามารถจัดเก็บข้อมูลเล็กน้อยตามต้องการลงในบล็อกเชนได้โดยไม่ทำให้ฐานข้อมูล UTXO มีขนาดใหญ่ขึ้น เนื่องจากเอาต์พุตเหล่านี้ไม่สามารถใช้จ่ายได้ จึงไม่ถือว่าเป็น uneconomical แต่ว่า satoshis ใด ๆ ก็ตามที่กำหนดให้กับมันจะกลายเป็นใช้ไม่ได้อย่างถาวร ดังนั้นการอนุญาตให้จำนวนเงินเป็นศูนย์จึงช่วยให้มั่นใจว่า satoshis จะไม่ถูกทำลาย</p>
<h3><strong>Output Scripts</strong></h3>
<p>หลังจากจำนวนเงินของเอาต์พุต จะมี compactSize integer ที่บอกความยาวของ output script ซึ่งเป็นสคริปต์ที่มีเงื่อนไขที่ต้องปฏิบัติตามเพื่อใช้จ่ายบิตคอยน์ ตามกฎฉันทามติของ Bitcoin ขนาดขั้นต่ำของ output script คือศูนย์</p>
<p>ขนาดสูงสุดตามฉันทามติที่อนุญาตของ output script จะแตกต่างกันไปขึ้นอยู่กับช่วงเวลาที่มีการตรวจสอบ ไม่มีขีดจำกัดที่ระบุไว้อย่างชัดเจนสำหรับขนาดของ output script ในเอาต์พุตของธุรกรรม แต่ธุรกรรมถัดไปสามารถใช้จ่ายเอาต์พุตก่อนหน้าได้เฉพาะเมื่อสคริปต์มีขนาด 10,000 ไบต์หรือน้อยกว่าเท่านั้น โดยนัยคือสคริปต์ของเอาต์พุตสามารถมีขนาดใหญ่เกือบเท่ากับตัวธุรกรรมที่บรรจุมันไว้ และธุรกรรมก็สามารถมีขนาดใหญ่เกือบเท่ากับบล็อกที่บรรจุธุรกรรมนั้นไว้</p>
<blockquote>
<p>TIP: output script ที่มีความยาวเป็นศูนย์สามารถถูกใช้จ่ายได้โดย input script ที่มี OP_TRUE อยู่ภายใน โดยใคร ๆ ก็สามารถสร้าง input script แบบนั้นได้ ซึ่งหมายความว่าใครก็สามารถใช้จ่าย output script ที่ว่างเปล่าได้ มีจำนวนสคริปต์แทบไม่จำกัดที่ใครก็สามารถใช้จ่ายได้ และนักพัฒนาโปรโตคอล Bitcoin เรียกสิ่งเหล่านี้ว่า anyone can spends การอัปเกรดภาษาสคริปต์ของ Bitcoin มักจะนำสคริปต์ที่เป็น anyone-can-spend มาเพิ่มเงื่อนไขใหม่เข้าไป ทำให้สามารถใช้จ่ายได้เฉพาะภายใต้เงื่อนไขใหม่เท่านั้น นักพัฒนาแอปพลิเคชันไม่ควรจำเป็นต้องใช้ anyone-can-spend script เลย แต่หากคุณจำเป็นต้องใช้ เราขอแนะนำอย่างยิ่งให้ประกาศแผนของคุณอย่างชัดเจนต่อผู้ใช้และนักพัฒนา Bitcoin เพื่อไม่ให้การอัปเกรดในอนาคตไปรบกวนระบบของคุณโดยไม่ตั้งใจ</p>
</blockquote>
<p>นโยบายของ Bitcoin Core สำหรับการรีเลย์และการขุดธุรกรรม จะจำกัด output script ให้เหลือเพียงไม่กี่รูปแบบ ซึ่งเรียกว่า standard transaction outputs การจำกัดนี้ถูกนำมาใช้ครั้งแรกหลังจากค้นพบบั๊กในยุคแรกของ Bitcoin ที่เกี่ยวข้องกับภาษา Script และยังถูกคงไว้ใน Bitcoin Core ยุคปัจจุบันเพื่อรองรับการอัปเกรดแบบ anyone-can-spend และเพื่อส่งเสริมแนวทางที่ดีที่สุดในการวางเงื่อนไขสคริปต์ไว้ใน P2SH redeem scripts, segwit v0 witness scripts และ segwit v1 (taproot) leaf scripts เราจะดูแต่ละรูปแบบของ standard transaction templates ในปัจจุบัน และเรียนรู้วิธีการ parse สคริปต์ในบทถัดไป</p>
<h3>Witness Structure</h3>
<p>ในศาล “พยาน” คือบุคคลที่ให้การว่าตนเห็นเหตุการณ์สำคัญบางอย่างเกิดขึ้น พยานมนุษย์ไม่ใช่สิ่งที่เชื่อถือได้เสมอไป ดังนั้นศาลจึงมีขั้นตอนต่าง ๆ สำหรับการสอบสวนพยานเพื่อ (อย่างน้อยก็ในอุดมคติ) รับหลักฐานเฉพาะจากผู้ที่เชื่อถือได้เท่านั้น </p>
<p>ลองจินตนาการว่าถ้ามี “พยาน” สำหรับโจทย์คณิตศาสตร์จะมีหน้าตาอย่างไร ตัวอย่างเช่น หากปัญหาสำคัญคือ x + 2 == 4 และมีใครสักคนอ้างว่าตนเป็นพยานที่เห็นวิธีแก้ เราจะถามอะไรพวกเขา? เราต้องการ “หลักฐานทางคณิตศาสตร์” ที่แสดงค่าหนึ่งที่สามารถนำมาบวกกับ 2 แล้วได้ 4 เราอาจตัดความจำเป็นของการมีบุคคลออกไปเลย และใช้ “ค่าที่เสนอให้เป็น x” นั้นเป็นพยานแทนก็ได้ หากเราถูกบอกว่าพยานคือ 2 เราก็สามารถแทนค่าในสมการ ตรวจสอบว่าถูกต้อง และสรุปว่าปัญหาสำคัญนั้นได้รับการแก้ไขแล้ว </p>
<p>เมื่อมีการใช้จ่ายบิตคอยน์ ปัญหาสำคัญที่เราต้องการแก้คือ “การยืนยันว่าการใช้จ่ายนั้นได้รับอนุญาตจากบุคคลหรือกลุ่มบุคคลที่ควบคุมบิตคอยน์เหล่านั้นจริงหรือไม่” โหนดเต็มนับพันที่บังคับใช้กฎฉันทามติของบิตคอยน์ไม่สามารถสอบสวนพยานมนุษย์ได้ แต่พวกมันสามารถรับ “พยาน” ที่ประกอบด้วยข้อมูลล้วน ๆ สำหรับแก้ปัญหาทางคณิตศาสตร์ได้ ตัวอย่างเช่น พยานที่เป็นค่า 2 จะทำให้สามารถใช้จ่ายบิตคอยน์ที่ถูกป้องกันด้วยสคริปต์ต่อไปนี้ได้:</p>
<pre><code>2 OP_ADD 4 OP_EQUAL
</code></pre>
<p>แน่นอนว่า การอนุญาตให้บิตคอยน์ของคุณถูกใช้จ่ายโดยใครก็ตามที่สามารถแก้สมการง่าย ๆ ได้ย่อมไม่ปลอดภัย อย่างที่เราจะเห็นในบทที่ 8 กลไกการลงนามดิจิทัลที่ปลอมแปลงไม่ได้ (unforgeable digital signature scheme) ใช้สมการที่สามารถถูกแก้ได้โดยเฉพาะผู้ที่ครอบครองข้อมูลบางอย่างซึ่งพวกเขาสามารถเก็บไว้เป็นความลับได้ พวกเขาสามารถอ้างอิงข้อมูลลับนั้นโดยใช้ตัวระบุสาธารณะ ซึ่งตัวระบุดังกล่าวเรียกว่า public key และคำตอบของสมการนั้นเรียกว่า signature</p>
<p>สคริปต์ต่อไปนี้มี public key และ opcode ที่ต้องการ signature ที่สอดคล้องกันเพื่อ commit กับข้อมูลในธุรกรรมที่กำลังใช้จ่าย เช่นเดียวกับตัวเลข 2 ในตัวอย่างง่าย ๆ ของเรา signature คือพยานของเรา:</p>
<pre><code>&lt;public key&gt; OP_CHECKSIG
</code></pre>
<p>พยาน (witnesses) ซึ่งเป็นค่าที่ใช้ในการแก้ปัญหาทางคณิตศาสตร์ที่ปกป้องบิตคอยน์ จำเป็นต้องถูกใส่รวมอยู่ในธุรกรรมที่ใช้มัน เพื่อให้โหนดเต็มสามารถตรวจสอบได้ ในรูปแบบธุรกรรมแบบดั้งเดิม (legacy transaction format) ที่ใช้ในธุรกรรมบิตคอยน์ยุคแรก ๆ ทั้งหมดนั้น ลายเซ็นและข้อมูลอื่น ๆ ถูกวางไว้ในฟิลด์ input script อย่างไรก็ตาม เมื่อผู้พัฒนาเริ่มนำโปรโตคอลสัญญาต่าง ๆ มาใช้บนบิตคอยน์ เช่นที่เราเห็นใน Original sequence-based transaction replacement พวกเขาค้นพบปัญหาที่สำคัญหลายประการเกี่ยวกับการใส่พยานไว้ในฟิลด์ input script</p>
<h4>Circular Dependencies</h4>
<p>โปรโตคอลสัญญาหลายอย่างสำหรับบิตคอยน์เกี่ยวข้องกับชุดของธุรกรรมที่ถูกเซ็นนอกลำดับ ตัวอย่างเช่น Alice และ Bob ต้องการฝากเงินเข้าไปในสคริปต์ที่สามารถใช้จ่ายได้ด้วยลายเซ็นของทั้งคู่ แต่พวกเขาก็ต้องการให้สามารถได้เงินคืนได้หากอีกฝ่ายหนึ่งไม่ตอบสนอง วิธีแก้ปัญหาอย่างง่ายคือการเซ็นธุรกรรมนอกลำดับ:</p>
<ul>
<li>Tx0 จ่ายเงินจาก Alice และเงินจาก Bob ไปยังเอาต์พุตที่มีสคริปต์ที่ต้องการลายเซ็นจากทั้ง Alice และ Bob เพื่อที่จะใช้จ่ายได้  </li>
<li>Tx1 ใช้จ่ายเอาต์พุตก่อนหน้าไปยังสองเอาต์พุต—หนึ่งคืนเงินให้ Alice และอีกหนึ่งคืนเงินให้ Bob (หักจำนวนเล็กน้อยเป็นค่าธรรมเนียมธุรกรรม)  </li>
<li>หาก Alice และ Bob เซ็น Tx1 ก่อนที่พวกเขาจะเซ็น Tx0 พวกเขาทั้งคู่ก็จะมั่นใจได้ว่าจะรับเงินคืนได้ทุกเมื่อ โปรโตคอลนี้ไม่ต้องการให้ฝ่ายหนึ่งไว้ใจอีกฝ่ายหนึ่ง ทำให้มันเป็นโปรโตคอลแบบ trustless</li>
</ul>
<p>ปัญหาของโครงสร้างนี้ในรูปแบบธุรกรรมแบบดั้งเดิมคือทุกฟิลด์ รวมถึงฟิลด์ input script ที่มีลายเซ็น จะถูกใช้ในการสร้างตัวระบุของธุรกรรม (txid) ค่า txid ของ Tx0 เป็นส่วนหนึ่งของ outpoint ของอินพุตใน Tx1 นั่นหมายความว่าไม่มีทางที่ Alice และ Bob จะสร้าง Tx1 ได้จนกว่าจะทราบลายเซ็นทั้งสองของ Tx0 — แต่ถ้าพวกเขารู้ลายเซ็นของ Tx0 หนึ่งในนั้นสามารถออกอากาศธุรกรรมนั้นก่อนที่จะเซ็นธุรกรรมคืนเงิน ทำให้การรับประกันการคืนเงินถูกลบล้างไป นี่คือปัญหา circular dependency</p>
<h4>Third-Party Transaction Malleability (การเปลี่ยนแปลงธุรกรรมโดยบุคคลที่สาม)</h4>
<p>ลำดับธุรกรรมที่ซับซ้อนกว่าสามารถแก้ปัญหา circular dependency ได้ในบางครั้ง แต่โปรโตคอลจำนวนมากจะพบกับความกังวลใหม่: มักจะสามารถแก้สคริปต์เดียวกันได้หลายวิธี ตัวอย่างเช่น พิจารณาสคริปต์แบบง่ายของเราจากหัวข้อ Witness Structure:</p>
<pre><code>2 OP_ADD 4 OP_EQUAL
</code></pre>
<p>เราสามารถทำให้สคริปต์นี้ผ่านได้โดยการใส่ค่า 2 ลงใน input script แต่มีหลายวิธีที่จะวางค่านั้นลงบนสแตกในบิตคอยน์ ต่อไปนี้คือเพียงบางตัวอย่าง:</p>
<pre><code>OP_2
OP_PUSH1 0x02
OP_PUSH2 0x0002
OP_PUSH3 0x000002
...
OP_PUSHDATA1 0x0102
OP_PUSHDATA1 0x020002
...
OP_PUSHDATA2 0x000102
OP_PUSHDATA2 0x00020002
...
OP_PUSHDATA4 0x0000000102
OP_PUSHDATA4 0x000000020002
...
</code></pre>
<p>แต่ละรูปแบบการเข้ารหัสของตัวเลข 2 ใน input script จะสร้างธุรกรรมที่แตกต่างกันเล็กน้อย พร้อมกับ txid ที่แตกต่างกันอย่างสิ้นเชิง แต่ละเวอร์ชันของธุรกรรมจะใช้จ่ายอินพุต (outpoints) เดียวกันกับทุกเวอร์ชันอื่น ๆ ทำให้พวกมันขัดแย้งกันเอง ทั้งหมดนี้ทำให้มีเพียงหนึ่งเวอร์ชันจากชุดของธุรกรรมที่ขัดแย้งกันเท่านั้นที่สามารถอยู่ในบล็อกเชนที่ถูกต้องได้</p>
<p>ลองนึกภาพว่ามีครั้งหนึ่ง Alice สร้างเวอร์ชันของธุรกรรมที่มี OP_2 ใน input script และมี output ที่จ่ายให้ Bob จากนั้น Bob ก็ใช้จ่าย output นั้นต่อให้ Carol ทันที บุคคลใดก็ตามบนเครือข่ายสามารถแทนที่ OP_2 ด้วย OP_PUSH1 0x02 ทำให้เกิดธุรกรรมที่ขัดแย้งกับเวอร์ชันต้นฉบับของ Alice หากธุรกรรมที่ขัดแย้งนั้นถูกยืนยัน ก็จะไม่มีทางรวมเวอร์ชันต้นฉบับของ Alice เข้าไปในบล็อกเชนเดียวกันได้ ซึ่งหมายความว่าไม่มีทางที่ธุรกรรมของ Bob จะใช้จ่าย output ของมันได้ การจ่ายเงินของ Bob ให้ Carol จึงกลายเป็นโมฆะ ทั้งที่ Alice, Bob หรือ Carol ไม่ได้ทำอะไรผิดเลย คนที่ไม่ได้เกี่ยวข้องกับธุรกรรม (บุคคลที่สาม) สามารถเปลี่ยนแปลง (mutate) ธุรกรรมของ Alice ได้ ซึ่งเป็นปัญหาที่เรียกว่า unwanted third-party transaction malleability</p>
<blockquote>
<p>TIP: มีหลายกรณีที่ผู้คนต้องการให้ธุรกรรมของพวกเขาถูกดัดแปลงได้ (malleable) และบิตคอยน์ก็มีฟีเจอร์หลายอย่างรองรับสิ่งนี้ โดยเฉพาะ signature hashes (sighash) ที่เราจะได้เรียนรู้ใน [sighash_types] ตัวอย่างเช่น Alice สามารถใช้ sighash เพื่อให้ Bob ช่วยจ่ายค่าธรรมเนียมบางส่วนได้ ซึ่งทำให้ธุรกรรมของ Alice ถูกเปลี่ยนแปลง แต่เป็นการเปลี่ยนแปลงในแบบที่ Alice ต้องการ ด้วยเหตุนี้เราจะใส่คำว่า unwanted นำหน้าคำว่า transaction malleability ในบางครั้ง แม้ว่าเราหรือผู้เขียนเทคนิคบิตคอยน์คนอื่น ๆ จะใช้คำเวอร์ชันสั้น เราก็กำลังพูดถึงรูปแบบของ malleability ที่ไม่พึงประสงค์แทบทุกครั้ง</p>
</blockquote>
<h4>Second-Party Transaction Malleability(ผู้ร่วมธุรกรรมอีกฝ่าย)</h4>
<p>เมื่อรูปแบบธุรกรรมแบบดั้งเดิม (legacy transaction format) เป็นรูปแบบเดียวที่มีให้ใช้ นักพัฒนาก็ทำงานบนข้อเสนอเพื่อลดปัญหา third-party malleability เอาไว้อย่างเช่น BIP62 แต่อย่างไรก็ตาม แม้ว่าจะสามารถกำจัด third-party malleability ได้ทั้งหมด ผู้ใช้งานโปรโตคอลแบบสัญญา (contract protocols) ก็ยังพบปัญหาอีกแบบหนึ่ง คือถ้าต้องใช้ลายเซ็นของอีกฝ่ายหนึ่งที่อยู่ในโปรโตคอล ฝ่ายนั้นสามารถสร้างลายเซ็นทางเลือกขึ้นมาและเปลี่ยน txid ได้</p>
<p>ตัวอย่างเช่น Alice และ Bob ฝากเงินของพวกเขาเข้าไปในสคริปต์ที่ต้องการลายเซ็นของทั้งคู่เพื่อใช้จ่าย พวกเขายังสร้าง "ธุรกรรมคืนเงิน" (refund transaction) ที่จะให้ทั้งสองฝ่ายสามารถดึงเงินกลับได้ทุกเมื่อ และต่อมา Alice ต้องการใช้เงินบางส่วน เธอจึงร่วมมือกับ Bob สร้างลำดับธุรกรรมดังนี้:</p>
<ul>
<li>Tx0 รวมลายเซ็นจาก Alice และ Bob และใช้จ่ายบิตคอยน์ไปยังสอง output โดย output แรกจ่ายเงินบางส่วนให้ Alice และอีกอัน คืนเงินส่วนที่เหลือกลับเข้าไปยังสคริปต์เดิม ซึ่งยังคงต้องใช้ลายเซ็นของทั้ง Alice และ Bob แน่นอนว่าก่อนที่พวกเขาจะเซ็น Tx0 พวกเขาสร้างธุรกรรมคืนเงินชุดใหม่ Tx1  </li>
<li>Tx1 ใช้จ่าย output ที่สองของ Tx0 ไปยังสอง output ใหม่—หนึ่งให้ Alice ตามส่วนแบ่งของเธอ และอีกหนึ่งให้ Bob ตามส่วนแบ่งของเขา Alice และ Bob ต่างเซ็น Tx1 เสร็จก่อนที่จะเซ็น Tx0</li>
</ul>
<p>ตรงนี้ไม่มี circular dependency และถ้าเรามองข้าม third-party malleability ก็จะดูเหมือนเป็นโปรโตคอลที่ไม่ต้องเชื่อใจใคร (trustless) ได้ แต่ปัญหาคือ คุณสมบัติของลายเซ็นบิตคอยน์มีความสุ่มโดยธรรมชาติ —ผู้เซ็นต้องเลือกตัวเลขสุ่มขนาดใหญ่ทุกครั้งที่สร้างลายเซ็น หากเลือกเลขสุ่มต่างกัน ลายเซ็นที่ได้ก็จะต่างกัน แม้ข้อมูลที่ถูกเซ็นจะเหมือนเดิมทั้งหมดก็ตาม คล้ายกับการที่คุณเซ็นชื่อบนสัญญาเหมือนกันสองชุด ลายเซ็นจริง ๆ บนกระดาษทั้งสองแผ่นก็ไม่เหมือนกัน 100%</p>
<p>ความสามารถในการถูกดัดแปลงของลายเซ็นนี้ เป็นช่องให้ Bob สร้างธุรกรรมที่ขัดแย้งกับ Tx0 ได้ หาก Alice พยายามประกาศ Tx0 (ซึ่งมีลายเซ็นของ Bob อยู่) Bob สามารถสร้างลายเซ็นใหม่ที่แตกต่างออกมา ผลิต Tx0 เวอร์ชันใหม่ที่มี txid แตกต่างกันขึ้นมาได้ ถ้า Tx0 เวอร์ชันที่ Bob เปลี่ยนแปลงถูกยืนยันขึ้นมา Alice จะไม่สามารถใช้ Tx1 ที่ลงนามไว้ล่วงหน้าเพื่อรับเงินคืนของเธอได้ เพราะ Tx1 อ้างอิง txid เดิมของ Tx0 ซึ่งไม่ตรงกับเวอร์ชันที่ถูกยืนยัน พฤติกรรมแบบนี้เรียกว่า unwanted second-party transaction malleability คือการที่อีกฝ่ายในโปรโตคอลสามารถดัดแปลงธุรกรรมให้เปลี่ยน txid ได้โดยไม่ต้องร่วมมือกับคุณ</p>
<h4>Segregated Witness(การแยกพยานออกจากธุรกรรม)</h4>
<p>ตั้งแต่ช่วงต้นปี 2011 นักพัฒนาระบบโปรโตคอลของบิตคอยน์ก็ทราบวิธีแก้ปัญหา circular dependence, third-party malleability, และ second-party malleability แล้ว แนวคิดคือการหลีกเลี่ยงไม่ให้นำ input script มารวมอยู่ในการคำนวณที่ใช้สร้าง txid ของธุรกรรม จำได้ว่า ชื่อเชิงนามธรรมสำหรับข้อมูลที่อยู่ใน input script คือคำว่า witness แนวคิดในการแยกข้อมูลส่วนอื่น ๆ ของธุรกรรมออกจาก witness เพื่อใช้ในการสร้าง txid นี้ เรียกว่า segregated witness (segwit)</p>
<p>วิธีที่ตรงไปตรงมาที่สุดในการทำให้ segwit ใช้งานได้จริงนั้น ต้องมีการเปลี่ยนแปลงกฎ consensus ของบิตคอยน์ในลักษณะที่ ไม่สามารถเข้ากันได้กับ full node รุ่นเก่า หรือที่เรียกว่า hard fork Hard fork นั้นมาพร้อมกับความท้าทายจำนวนมาก ซึ่งเราจะอธิบายเพิ่มเติมในส่วน hardfork</p>
<p>แนวทางทางเลือกสำหรับ segwit ถูกอธิบายไว้ในช่วงปลายปี 2015 แนวทางนี้ใช้การเปลี่ยนแปลงกฎ consensus แบบ เข้ากันได้ย้อนหลัง (backward-compatible) ซึ่งเรียกว่า soft fork คำว่า “เข้ากันได้ย้อนหลัง” หมายความว่า full node ที่ใช้กฎใหม่จะต้อง ไม่ยอมรับบล็อกใด ๆ ที่ full node รุ่นเก่ามองว่าไม่ถูกต้อง ตราบเท่าที่พวกเขาปฏิบัติตามกฎนั้น full node รุ่นใหม่จึงสามารถ ปฏิเสธบล็อกที่ full node รุ่นเก่ายอมรับได้ ซึ่งทำให้พวกเขาสามารถบังคับใช้กฎ consensus ใหม่ได้ (แต่มีเงื่อนไขว่า full node รุ่นใหม่เหล่านั้นจะต้อง เป็นตัวแทนของฉันทามติทางเศรษฐกิจของผู้ใช้บิตคอยน์ด้วย—เราจะสำรวจรายละเอียดเกี่ยวกับการอัปเกรดกฎ consensus ของบิตคอยน์ในส่วนของ mining</p>
<p>การใช้ซอฟต์ฟอร์กแบบเซกวิทอาศัยแนวคิดเอาต์พุตสคริปต์แบบ “anyone-can-spend” เป็นพื้นฐาน สคริปต์ที่ขึ้นต้นด้วยตัวเลขตั้งแต่ 0 ถึง 16 และตามด้วยข้อมูลขนาด 2 ถึง 40 ไบต์ ถูกกำหนดให้เป็นแม่แบบเอาต์พุตสคริปต์ของเซกวิท ตัวเลขนั้นคือหมายเลขเวอร์ชันของเซกวิท (เช่น 0 คือเซกวิทเวอร์ชัน 0 หรือ segwit v0) ส่วนข้อมูลที่ตามมานั้นเรียกว่า witness program นอกจากนี้ยังสามารถนำแม่แบบเซกวิทไปห่อด้วยคอมมิตเมนต์แบบ P2SH ได้ แต่ในบทนี้เราจะยังไม่กล่าวถึง</p>
<p>จากมุมมองของโหนดเก่า เอาต์พุตสคริปต์แม่แบบเหล่านี้สามารถถูกใช้จ่ายด้วย input script ที่ว่างเปล่าได้ จากมุมมองของโหนดใหม่ที่รู้กฎของเซกวิท การใช้จ่ายเอาต์พุตที่เป็นแม่แบบเซกวิทต้องใช้ input script ที่ว่างเปล่าเท่านั้น สังเกตความแตกต่าง: โหนดเก่า “อนุญาต” ให้ใช้สคริปต์ว่างเปล่า ส่วนโหนดใหม่ “กำหนด” ให้ต้องเป็นสคริปต์ว่างเปล่า</p>
<p>การใช้ input script ว่างเปล่าทำให้ข้อมูลพยานไม่ไปกระทบกับ txid ช่วยกำจัดปัญหาการพึ่งพาวนซ้ำ การเปลี่ยนแปลงธุรกรรมโดยบุคคลที่สาม และการเปลี่ยนแปลงธุรกรรมโดยคู่สัญญา แต่เมื่อไม่สามารถใส่ข้อมูลใด ๆ ใน input script ได้ ผู้ใช้แม่แบบเอาต์พุตเซกวิทจึงต้องมีฟิลด์ใหม่ ฟิลด์นี้คือโครงสร้างพยาน (witness structure)</p>
<p>การเกิดขึ้นของ witness programs และ witness structure ทำให้บิตคอยน์ซับซ้อนขึ้น แต่สิ่งนี้ก็สอดคล้องกับแนวโน้มที่มีอยู่แล้วของการเพิ่มระดับนามธรรมในระบบบิตคอยน์ จากที่กล่าวไว้ในบทที่ 4 ว่าไวท์เปเปอร์บิตคอยน์ดั้งเดิมอธิบายระบบที่บิตคอยน์ถูก “รับ” ไปยัง public keys (pubkeys) และถูก “ใช้จ่าย” ด้วย signatures (sigs) pubkey ระบุว่าใครได้รับอนุญาตให้ใช้จ่ายบิตคอยน์ (คือผู้ที่ควบคุม private key ที่สอดคล้องกัน) และลายเซ็นเป็นการตรวจสอบว่าธุรกรรมการใช้จ่ายนั้นมาจากผู้ที่ควบคุม private key จริง ๆ เพื่อทำให้ระบบนั้นยืดหยุ่นขึ้น รุ่นแรกสุดของบิตคอยน์ได้แนะนำ scripts ซึ่งอนุญาตให้รับบิตคอยน์ไปยัง output scripts และใช้จ่ายด้วย input scripts ประสบการณ์ภายหลังจากการใช้งานโปรโตคอลสัญญาต่าง ๆ ได้นำไปสู่การอนุญาตให้รับบิตคอยน์ไปยัง witness programs และใช้จ่ายด้วย witness structure คำต่าง ๆ และฟิลด์ที่ถูกใช้ในเวอร์ชันต่าง ๆ ของบิตคอยน์ถูกแสดงไว้ในตารางข้างล่างนี้</p>
<table>
<thead>
<tr>
<th></th>
<th>Authorization</th>
<th>Authentication</th>
</tr>
</thead>
<tbody><tr>
<td>Whitepaper</td>
<td>Public key</td>
<td>Signature</td>
</tr>
<tr>
<td>Original (Legacy)</td>
<td>Output script</td>
<td>Input script</td>
</tr>
<tr>
<td>Segwit</td>
<td>Witness program</td>
<td>Witness structure</td>
</tr>
</tbody></table>
<h4>Witness Structure Serialization</h4>
<p>เช่นเดียวกับฟิลด์ inputs และ outputs, witness structure ก็มีฟิลด์ย่อยอื่น ๆ อยู่ภายใน ดังนั้นเราจะเริ่มต้นด้วยแผนที่ไบต์ (byte map) ของข้อมูลเหล่านั้นจากธุรกรรมของ Alice ซึ่งแสดงอยู่ในรูปด้านล่าง</p>
<p> <img src="https://image.nostr.build/d5c8cf4712e321ee4a37fa93b0a5c12364a7f321c6655a035ce56a20e3d9367e.jpg" alt="image"></p>
<p>ต่างจากฟิลด์ inputs และ outputs ตรงที่ witness structure โดยรวมจะไม่เริ่มต้นด้วยตัวบ่งชี้จำนวนของ witness stack ทั้งหมดที่มีอยู่ แต่จำนวนดังกล่าวถูกกำหนดโดยฟิลด์ inputs—กล่าวคือ ธุรกรรมหนึ่งรายการจะมีหนึ่ง witness stack ต่อหนึ่ง input เสมอ</p>
<p>สำหรับ witness structure ของแต่ละ input จะเริ่มต้นด้วยตัวนับจำนวนขององค์ประกอบที่อยู่ภายใน ซึ่งองค์ประกอบเหล่านี้เรียกว่า witness items เราจะศึกษารายละเอียดของมันในภายหลังในบทต่อไป แต่ตอนนี้สิ่งที่ต้องรู้คือ witness item แต่ละรายการจะถูกนำหน้าด้วย compactSize integer ที่ระบุขนาดของมัน</p>
<p>สำหรับ legacy inputs จะไม่มี witness items อยู่เลย ดังนั้น witness stack ของมันประกอบด้วยค่า 0 เพียงค่าเดียว (0x00)</p>
<p>ธุรกรรมของ Alice มี input หนึ่งรายการ และมี witness item หนึ่งรายการ</p>
<h3>Lock Time</h3>
<p>ฟิลด์สุดท้ายในธุรกรรมที่ถูกซีเรียลไลซ์คือ lock time ฟิลด์นี้เป็นส่วนหนึ่งของรูปแบบการซีเรียลไลซ์ดั้งเดิมของบิตคอยน์ แต่ในช่วงแรกมีผลเฉพาะกับนโยบายการเลือกธุรกรรมเข้าสู่บล็อกของบิตคอยน์เท่านั้น</p>
<p>ซอฟต์ฟอร์กที่บันทึกได้เป็นครั้งแรกของบิตคอยน์ได้เพิ่มกฎใหม่ว่า เริ่มตั้งแต่บล็อกหมายเลข 31,000 เป็นต้นไป ห้ามรวมธุรกรรมในบล็อก เว้นแต่ธุรกรรมนั้นจะเป็นไปตามหนึ่งในกฎต่อไปนี้:</p>
<ul>
<li>ธุรกรรมระบุว่า สามารถถูกรวมในบล็อกใดก็ได้ โดยตั้งค่า lock time เป็น 0  </li>
<li>ธุรกรรมระบุว่าต้องการจำกัดว่าบล็อกใดที่สามารถรวมมันได้ โดยตั้งค่า lock time ให้มีค่าต่ำกว่า 500,000,000 ในกรณีนี้ ธุรกรรมสามารถถูกบรรจุได้เฉพาะในบล็อกที่มีความสูงของบล็อก (block height) เท่ากับ lock time หรือมากกว่า ตัวอย่างเช่น ธุรกรรมที่มี lock time เป็น 123,456 สามารถอยู่ในบล็อก 123,456 หรือบล็อกใดที่สูงกว่า  </li>
<li>ธุรกรรมระบุว่าต้องการจำกัดเวลาที่สามารถถูกบรรจุเข้าสู่บล็อกเชนได้ โดยตั้งค่า lock time ให้มีค่า 500,000,000 หรือมากกว่า ในกรณีนี้ lock time จะถูกตีความเป็นเวลาตาม epoch time (จำนวนวินาทีตั้งแต่ 1970-01-01T00:00 UTC) ธุรกรรมจะถูกบรรจุได้เฉพาะในบล็อกที่มี median time past (MTP) มากกว่า lock time โดยทั่วไป MTP จะช้ากว่าเวลาปัจจุบันประมาณหนึ่งถึงสองชั่วโมง กฎของ MTP อธิบายไว้ในส่วนของ mtp</li>
</ul>
<h2><strong>Coinbase Transactions</strong></h2>
<p>ธุรกรรมแรกในทุกบล็อกเป็นกรณีพิเศษ เอกสารรุ่นเก่าหลายฉบับเรียกธุรกรรมนี้ว่า generation transaction แต่เอกสารรุ่นใหม่ส่วนใหญ่เรียกว่า coinbase transaction (ไม่เกี่ยวข้องกับธุรกรรมที่สร้างโดยบริษัทชื่อ “Coinbase”)</p>
<p>Coinbase transactions ถูกสร้างขึ้นโดยนักขุดที่ขุดบล็อกนั้น และเปิดโอกาสให้นักขุดสามารถรับ ค่าธรรมเนียม (fees) ทั้งหมดจากธุรกรรมที่อยู่ในบล็อกนั้นได้ นอกจากนี้ จนถึงบล็อกหมายเลข 6,720,000 นักขุดยังสามารถรับ subsidy ซึ่งเป็นบิตคอยน์ที่ถูกสร้างขึ้นใหม่และไม่เคยหมุนเวียนมาก่อน เรียกว่า block subsidy จำนวนรวมที่นักขุดสามารถรับได้จากบล็อกหนึ่ง ๆ — ประกอบด้วยค่าธรรมเนียมและ block subsidy — เรียกว่า block reward</p>
<p>พฤติกรรมพิเศษบางประการของ coinbase transactions มีดังนี้:</p>
<ul>
<li>ธุรกรรม coinbase สามารถมีอินพุตได้เพียงหนึ่งรายการเท่านั้น  </li>
<li>อินพุตเพียงรายการนั้นต้องมี outpoint ที่มี txid เป็นค่าว่าง (ศูนย์ทั้งหมด) และมี output index สูงสุด (0xffffffff) สิ่งนี้ทำให้ coinbase transaction ไม่สามารถอ้างอิงเอาต์พุตของธุรกรรมก่อนหน้า ซึ่งจะสร้างความสับสนเพราะ coinbase transaction มีหน้าที่จ่ายค่าธรรมเนียมและ subsidy  </li>
<li>ฟิลด์ที่โดยปกติจะเป็น input script จะถูกเรียกว่า coinbase ในธุรกรรมประเภทนี้ และนี่คือที่มาของชื่อ coinbase transaction ฟิลด์ coinbase ต้องมีความยาวอย่างน้อย 2 ไบต์ และต้องไม่เกิน 100 ไบต์ สคริปต์นี้ ไม่ถูกประมวลผล แต่ยังคงอยู่ภายใต้กฎจำกัดจำนวน sigops แบบ legacy ดังนั้นข้อมูลใด ๆ ที่ใส่เข้าไปควรถูกนำหน้าด้วย opcode สำหรับการ push data นอกจากนี้ ตั้งแต่ soft fork ปี 2013 (BIP34) ไบต์แรก ๆ ของฟิลด์นี้ต้องปฏิบัติตามกฎเพิ่มเติม ซึ่งจะอธิบายทีหลัง  </li>
<li>ผลรวมของค่าเอาต์พุตทั้งหมดต้องไม่เกินค่าธรรมเนียมจากธุรกรรมทั้งหมดในบล็อกนั้นรวมกับ subsidy โดย subsidy เริ่มต้นที่ 50 BTC ต่อบล็อก และลดลงครึ่งหนึ่งทุก 210,000 บล็อก (ประมาณทุก 4 ปี) โดยปัดเศษลงถึง satoshi ที่ใกล้ที่สุด  </li>
<li>ตั้งแต่ soft fork segwit ปี 2017 (BIP141) บล็อกใดก็ตามที่มีธุรกรรมใช้จ่าย segwit output ต้องมีเอาต์พุตใน coinbase transaction ที่เป็นการ commit ถึงธุรกรรมทั้งหมดในบล็อก (รวมถึง witness ด้วย) รายละเอียดจะอธิบายในส่วน mining</li>
</ul>
<p>coinbase transaction สามารถมีเอาต์พุตอื่น ๆ ที่ถูกต้องตามกฎเหมือนธุรกรรมทั่วไปได้ อย่างไรก็ตาม ธุรกรรมที่ใช้จ่ายเอาต์พุตของ coinbase จะไม่สามารถใส่ในบล็อกได้จนกว่าธุรกรรมนั้นจะมีอย่างน้อย 100 confirmations สิ่งนี้เรียกว่า maturity rule และเอาต์พุตของ coinbase ที่ยังไม่ถึง 100 confirmations จะถูกเรียกว่า immature แต่เนื่องจากลักษณะพิเศษของมัน ทำให้บางครั้งอาจเป็นสาเหตุของปัญหาที่ไม่คาดคิดในซอฟต์แวร์ที่ไม่ได้ออกแบบมาเพื่อรองรับกรณีเหล่านี้</p>
<h2><strong>Weight and Vbytes</strong></h2>
<p>แต่ละบล็อกของบิตคอยน์มีข้อจำกัดปริมาณข้อมูลธุรกรรมที่บรรจุได้ ดังนั้นซอฟต์แวร์บิตคอยน์ส่วนใหญ่จึงต้องสามารถวัดขนาดของธุรกรรมที่สร้างหรือประมวลผลได้ หน่วยวัดสมัยใหม่ที่ใช้คือ weight อีกหน่วยหนึ่งคือ vbytes ซึ่งคำนวณจากการนำ weight มาหารด้วยสี่ ทำให้สามารถเทียบกับหน่วยไบต์ในบล็อกแบบดั้งเดิมได้ง่ายขึ้น</p>
<p>ขนาดสูงสุดของบล็อกคือ <strong>4 ล้าน weight</strong> ส่วน <strong>block header</strong> ใช้พื้นที่ไป 240 weight และช่องข้อมูล <strong>transaction count</strong> ใช้อีก 4 หรือ 12 weight พื้นที่ที่เหลือทั้งหมดสามารถใช้สำหรับข้อมูลธุรกรรมได้</p>
<p>การคำนวณ weight ของฟิลด์หนึ่ง ๆ ในธุรกรรมทำได้โดยการนำจำนวนไบต์ของฟิลด์ที่ serialize แล้วไปคูณกับตัวคูณเฉพาะฟิลด์นั้น ๆ จากนั้นเมื่อต้องการหาน้ำหนักรวมของธุรกรรม ก็เพียงนำ weight ของทุกฟิลด์มาบวกกัน ตัวคูณสำหรับแต่ละฟิลด์ถูกระบุไว้ในตารางข้างล่างนี้ นอกจากนี้ยังมีตัวอย่างการคำนวณ weight ของแต่ละฟิลด์ในธุรกรรมตัวอย่างของบทนี้ (ธุรกรรมจาก Alice ไปยัง Bob)</p>
<p>ตัวคูณเหล่านี้ รวมถึงฟิลด์ที่เกี่ยวข้อง ถูกออกแบบมาเพื่อลดน้ำหนักเมื่อมีการใช้ UTXO ช่วยลดแรงจูงใจในการสร้างเอาต์พุตที่ไม่คุ้มค่า (uneconomical outputs) และหลีกเลี่ยงปัญหา dust ที่ไม่เหมาะสมตามที่อธิบายไว้ในหัวข้อ Uneconomical outputs and disallowed dust</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Factor</th>
<th>Weight in Alice’s Tx</th>
</tr>
</thead>
<tbody><tr>
<td>Version</td>
<td>4</td>
<td>16</td>
</tr>
<tr>
<td>Marker &amp; Flag</td>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>Inputs Count</td>
<td>4</td>
<td>4</td>
</tr>
<tr>
<td>Outpoint</td>
<td>4</td>
<td>144</td>
</tr>
<tr>
<td>Input script</td>
<td>4</td>
<td>4</td>
</tr>
<tr>
<td>Sequence</td>
<td>4</td>
<td>16</td>
</tr>
<tr>
<td>Outputs Count</td>
<td>4</td>
<td>4</td>
</tr>
<tr>
<td>Amount</td>
<td>4</td>
<td>64 (2 outputs)</td>
</tr>
<tr>
<td>Output script</td>
<td>4</td>
<td>232 (2 outputs with different scripts)</td>
</tr>
<tr>
<td>Witness Count</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>Witness items</td>
<td>1</td>
<td>66</td>
</tr>
<tr>
<td>Lock time</td>
<td>4</td>
<td>16</td>
</tr>
<tr>
<td>Total</td>
<td><em>N/A</em></td>
<td>569</td>
</tr>
</tbody></table>
<p>เราสามารถตรวจสอบความถูกต้องของการคำนวณ weight ได้โดยดูค่าน้ำหนักรวมของธุรกรรมของ Alice จาก Bitcoin Core ได้โดยตรง:</p>
<pre><code>$ bitcoin-cli getrawtransaction 466200308696215bbc949d5141a49a41\\

38ecdfdfaa2a8029c1f9bcecd1f96177 2 | jq .weight

569
</code></pre>
<p> <img src="https://image.nostr.build/2e3675764c1b67d8b9e19db81101b336607f7aa8ec79b094fc2244a70a6ad7b2.jpg" alt="image"></p>
<h3><strong>Legacy Serialization</strong></h3>
<p>รูปแบบการซีเรียลไลซ์ที่อธิบายไว้ในบทนี้คือรูปแบบที่ใช้กับธุรกรรมบิตคอยน์ส่วนใหญ่ในปัจจุบัน แต่ก็ยังมีรูปแบบเก่าที่ถูกใช้งานอยู่ในธุรกรรมจำนวนมากเช่นกัน รูปแบบเก่านั้นเรียกว่า <strong>legacy serialization</strong> และต้องใช้บนเครือข่าย Bitcoin P2P สำหรับธุรกรรมที่มีโครงสร้าง witness ว่างเปล่า (ซึ่งจะเกิดขึ้นได้ก็ต่อเมื่อธุรกรรมนั้นไม่ได้ใช้จ่าย witness programs ใด ๆ)</p>
<p>Legacy serialization จะ <strong>ไม่มี</strong> ฟิลด์ต่อไปนี้: marker, flag, witness structure</p>
<p>ในบทนี้ เราได้ดูฟิลด์ต่าง ๆ ในหนึ่งธุรกรรมและเห็นว่าฟิลด์เหล่านั้นสื่อสารข้อมูลให้โหนดเต็มทราบว่าเหรียญควรถูกโอนจากใครไปหาใครอย่างไร เราได้กล่าวถึง output script, input script และ witness structure เพียงคร่าว ๆ ซึ่งมีหน้าที่กำหนดเงื่อนไขและเปิดโอกาสให้ผู้ใช้พิสูจน์สิทธิ์ในการใช้จ่ายเหรียญได้</p>
<p>การเข้าใจวิธีสร้างและใช้งานเงื่อนไขเหล่านี้เป็นสิ่งสำคัญเพื่อให้แน่ใจว่าเฉพาะ Alice เท่านั้นที่จะสามารถใช้จ่ายบิตคอยน์ของเธอได้ ดังนั้นหัวข้อนี้จะเป็นเนื้อหาของบทถัดไป</p>
]]></itunes:summary>
      <itunes:image href="https://image.nostr.build/4b81c193b6eb1d9d47353f31f88c3268884a8a2a3d74e771052448fec8a6f793.jpg"/>
      </item>
      
      <item>
      <title><![CDATA[สรุป Mastering Bitcoin: Programming the Open Blockchain บทที่ 4]]></title>
      <description><![CDATA[อ่านต้นฉบับแล้วมัน อู้อี่อี่อี่อู้อี่อี่อ้า อู่อู่อี้อี้อ้า อีอ้าอีอู่ เลยหาสรุป แต่พออ่านสรุปแล้วมัน อู่อีอีอ้าอีอู่ อูอีอีอ้าอีอู่ กลับไปอ่านต้นฉบับดีกว่าา !!!]]></description>
             <itunes:subtitle><![CDATA[อ่านต้นฉบับแล้วมัน อู้อี่อี่อี่อู้อี่อี่อ้า อู่อู่อี้อี้อ้า อีอ้าอีอู่ เลยหาสรุป แต่พออ่านสรุปแล้วมัน อู่อีอีอ้าอีอู่ อูอีอีอ้าอีอู่ กลับไปอ่านต้นฉบับดีกว่าา !!!]]></itunes:subtitle>
      <pubDate>Sat, 22 Mar 2025 11:11:35 GMT</pubDate>
      <link>https://learnbn.npub.pro/post/bbid3bt12898zobcwj9ex/</link>
      <comments>https://learnbn.npub.pro/post/bbid3bt12898zobcwj9ex/</comments>
      <guid isPermaLink="false">naddr1qq25ysnfvsekyap3xgurjwz6fapyxam289jhsq3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65wsceefr</guid>
      <category>ลองฟอร์มของไดโน</category>
      
        <media:content url="https://image.nostr.build/0e43fa1dec4fb36a9b30035d0e83cf3759ece78d64813fc0b5182c5a62dd6e34.jpg" medium="image"/>
        <enclosure 
          url="https://image.nostr.build/0e43fa1dec4fb36a9b30035d0e83cf3759ece78d64813fc0b5182c5a62dd6e34.jpg" length="0" 
          type="image/jpeg" 
        />
      <noteId>naddr1qq25ysnfvsekyap3xgurjwz6fapyxam289jhsq3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65wsceefr</noteId>
      <npub>npub10yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2syrhcl3</npub>
      <dc:creator><![CDATA[Learning_BTC&NOSTR]]></dc:creator>
      <content:encoded><![CDATA[<h2>Keys and Addresses</h2>
<p>อลิซต้องการจ่ายเงินให้กับบ๊อบแต่โหนดของบิตคอยน์ในระบบหลายพันโหนดจะตรวจสอบธุรกรรมของเธอ โดยไม่รู้ว่าอลิซหรือบ๊อบเป็นใคร ละเราต้องการรักษาความเป็นส่วนตัวของพวกเขาไว้เช่นนี้ อลิซจำเป็นต้องสื่อสารว่าบ๊อบควรได้รับบิตคอยน์บางส่วนของเธอโดยไม่เชื่อมโยงแง่มุมใด ๆ ของธุรกรรมนั้นกับตัวตนในโลกจริงของบ๊อบ หรือกับการชำระเงินด้วยบิตคอยน์ครั้งอื่น ๆ ที่บ๊อบได้รับ อลิซใช้ต้องทำให้มั่นใจว่ามีเพียแค่บ๊อบเท่านั้นที่สามารถใช้จ่ายบิตคอยน์ที่เขาได้รับต่อไปได้</p>
<p>ในบิตคอยน์ไวท์เปเปอร์ได้อธิบายถึงแผนการที่เรียบง่ายมากสำหรับการบรรลุเป้าหมายเหล่านั้น ดังที่แสดงในรูปด้านล่างนี้ </p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_aain01.png" alt="image"></p>
<p>ตัวของผู้รับอย่างบ๊อบเองจะได้รับบิตคอยน์ไปยัง public key ของเขาที่ถูกลงนามโดยผู้จ่ายอย่างอลิซ โดยบิตคอยน์ที่อลิซนำมาจ่ายนั้นก็ได้รับมาจากที่ใครสักคนส่งมาที่ public key ของเธอ และเธอก็ใช้ private key ของเธอในการลงนามเพื่อสร้างลายเซ็นของเธอและโหนดต่าง ๆ ของบิตคอยน์จะทำการตรวจสอบว่าลายเซ็นของอลิซผูกมัดกับเอาต์พุตของฟังก์ชันแฮชซึ่งตัวมันเองผูกมัดกับ public key ของบ๊อบและรายละเอียดธุรกรรมอื่นๆ</p>
<p>ในบทนี้เราจะพิจารณาpublic key  private key  Digital signatrue และ hash function จากนั้นใช้ทั้งหมดนี้ร่วมกันเพื่ออธิบาย address ที่ใช้โดยซอฟต์แวร์บิตคอยน์สมัยใหม่</p>
<h3>Public Key Cryptography (การเข้ารหัสของ public key)</h3>
<p>ระบบเข้ารหัสของ public key ถูกคิดค้นขึ้นในทศวรรษ 1970 มาจากรากฐานทางคณิตศาสตร์สำหรับความปลอดภัยของคอมพิวเตอร์และข้อมูลสมัยใหม่</p>
<p>นับตั้งแต่การคิดค้นระบบเข้ารหัส public key ได้มีการค้นพบฟังก์ชันทางคณิตศาสตร์ที่เหมาะสมหลายอย่าง เช่น การยกกำลังของจำนวนเฉพาะและการคูณของเส้นโค้งวงรี โดยฟังก์ชันทางคณิตศาสตร์เหล่านี้สามารถคำนวณได้ง่ายในทิศทางหนึ่ง แต่เป็นไปไม่ได้ที่จะคำนวณในทิศทางตรงกันข้ามโดยใช้คอมพิวเตอร์และอัลกอริทึมที่มีอยู่ในปัจจุบัน จากฟังก์ชันทางคณิตศาสตร์เหล่านี้ การเข้ารหัสลับช่วยให้สามารถสร้างลายเซ็นดิจิทัลที่ไม่สามารถปลอมแปลงได้และบิตคอยน์ได้ใช้การบวกและการคูณของเส้นโค้งวงรีเป็นพื้นฐานสำหรับการเข้ารหัสลับของมัน</p>
<p>ในบิตคอยน์ เราสามารถใช้ระบบเข้ารหัส public key เพื่อสร้างคู่กุญแจที่ควบคุมการเข้าถึงบิตคอยน์ คู่กุญแจประกอบด้วย private key และ public key ที่ได้มาจาก private key public keyใช้สำหรับรับเงิน และ private key ใช้สำหรับลงนามในธุรกรรมเพื่อใช้จ่ายเงิน</p>
<p>ความสัมพันธ์ทางคณิตศาสตร์ระหว่าง public key และ private key ที่ช่วยให้ private key สามารถใช้สร้างลายเซ็นบนข้อความได้ ลายเซ็นเหล่านี้สามารถตรวจสอบความถูกต้องกับ public key ได้โดยไม่เปิดเผย private key</p>
<blockquote>
<p>TIP: ในการใช้งานซอฟแวร์กระเป๋าเงินบิตคอยน์บสงอัน จะทำการเก็บ private key และ public key ถูกเก็บไว้ด้วยกันในรูปแบบคู่กุญแจเพื่อความสะดวก แต่อย่างไรก็ตาม public key สามารถคำนวณได้จาก private key ดังนั้นการเก็บเพียง private key เท่านั้นก็เป็นไปได้เช่นกัน</p>
</blockquote>
<p>bitcoin wallet มักจะทำการรวบรวมคู่กุญแต่ละคู่ ซึ่งจะประกอบไปด้วย private key และ public key โดย private key จะเป็นตัวเลขที่ถูกสุ่มเลือกขึ้นมา และเราขะใช้เส้นโค้งวงรี ซึ่งเป็นฟังก์ชันการเข้ารหัสทางเดียว เพื่อสร้าง public key ขึ้นมา</p>
<h3>ทำไมจึงใช้การเข้ารหัสแบบอสมมาตร</h3>
<p>ทำไมการเข้ารหัสแบบอสมมาตรจึงถูกใช้บิตคอยน์? มันไม่ได้ถูกใช้เพื่อ "เข้ารหัส" (ทำให้เป็นความลับ) ธุรกรรม แต่คุณสมบัติที่มีประโยชน์ของการเข้ารหัสแบบอสมมาตรคือความสามารถในการสร้าง ลายเซ็นดิจิทัล private key สามารถนำไปใช้กับธุรกรรมเพื่อสร้างลายเซ็นเชิงตัวเลข ลายเซ็นนี้สามารถสร้างได้เฉพาะโดยผู้ที่มีความเกี่ยวข้องกับ private key เท่านั้น แต่อย่างไรก็ตาม ทุกคนที่สามารถเข้าถึง public key และธุรกรรมสามารถใช้สิ่งเหล่านี้เพื่อ ตรวจสอบ ลายเซ็นได้ คุณสมบัติที่มีประโยชน์นี้ของการเข้ารหัสแบบอสมมาตรทำให้ทุกคนสามารถตรวจสอบลายเซ็นทุกรายการในทุกธุรกรรมได้ ในขณะที่มั่นใจว่าเฉพาะเจ้าของ private key เท่านั้นที่สามารถสร้างลายเซ็นที่ถูกต้องได้</p>
<h3>Private keys</h3>
<p>private key เป็นเพียงตัวเลขที่ถูกสุ่มขึ้น และการควบคุม private key ก็เป็นรากฐานสำคัญที่ทำให้เจ้าชองกุญแจดอกนี้สามารถควบคุมบิตคอยน์ทั้งหมดที่มีความเกี่ยวข้องกับ public key ที่คู่กัน private key นั้นใช้ในการสร้างลายเซ็นดิจิทัลที่ใช้ในการเคลื่อนย้ายบิตคอยน์ เราจำเป็นต้องเก็บ private key ให้เป็นความลับตลอดเวลา เพราะการเปิดเผยมันให้กับบุคคลอื่นนั้นก็เปรียบเสมือนกับการนำอำนาจในการควบคุมบิตคอยน์ไปให้แก่เขา นอกจากนี้ private key ยังจำเป็นต้องได้รับการสำรองข้อมูลและป้องกันจากการสูญหายโดยไม่ตั้งใจ เพราะหากเราได้ทำมันสูญหายไป จะไม่สามารถกู้คืนได้ และบิตคอยน์เหล่านั้นจะถูกปกป้องโดยกุญแจที่หายไปนั้นตลอดกาลเช่นกัน</p>
<blockquote>
<p>TIP: private key ของบิตคอยน์นั้นเป็นเพียงแค่ตัวเลข คุณสามารถสร้างมันได้โดยใช้เพียงเหรียญ ดินสอ และกระดาษ โดยการโยนเหรียญเพียง 256 ครั้งจะทำให้คุณได้เลขฐานสองที่สามารถใช้เป็น private key ของบิตคอยน์ จากนั้นคุณสามารถใช้มันในการคำนวณหา public key แต่อย่างไรก็ตาม โปรดระมัดระวังเกี่ยวกับการเลือใช้วิธีการสุ่มที่ไม่สมบูรณ์ เพราะนั่นอาจลดความปลอดภัยของ private key และบิตคอยน์ที่มัมปกป้องอยู่อย่างมีนัยสำคัญ</p>
</blockquote>
<p>ขั้นตอนแรกและสำคัญที่สุดในการสร้างกุญแจคือการหาแหล่งที่มาของความสุ่มที่ปลอดภัย (ซึ่งเรียกว่า เอนโทรปี) การสร้างกุญแจของบิตคอยน์นั้นเกือบเหมือนกับ "เลือกตัวเลขระหว่าง 1 และ 2^256" ซึ่งวิธีที่แน่นอนที่คุณใช้ในการเลือกตัวเลขนั้นไม่สำคัญตราบใดที่มันไม่สามารถคาดเดาหรือทำซ้ำได้ โดยปกติแล้วซอฟต์แวร์ของบิตคอยน์มักจะใช้ตัวสร้างตัวเลขสุ่มที่มีความปลอดภัยทางการเข้ารหัสเพื่อสร้างเอนโทรปี 256 บิต</p>
<p>สิ่งที่สำคัญในเรื่องนี้คือ private key สามารถเป็นตัวเลขใดๆ ระหว่าง 0 และ n - 1 (รวมทั้งสองค่า) โดยที่ n เป็นค่าคงที่ (n = 1.1578 × 10^77 ซึ่งน้อยกว่า 2^256 เล็กน้อย) ซึ่งกำหนดอยู่ใน elliptic curve ที่ใช้ใน Bitcoin ในการสร้างกุญแจดังกล่าว เราสุ่มเลือกเลขขนาด 256 บิตและตรวจสอบว่ามันน้อยกว่า n ในแง่ของการเขียนโปรแกรม โดยปกติแล้วสิ่งนี้ทำได้โดยการป้อนสตริงของบิตสุ่มที่ใหญ่กว่า ซึ่งรวบรวมจากแหล่งที่มาของความสุ่มที่มีความปลอดภัยทางการเข้ารหัส เข้าไปในอัลกอริทึมแฮช SHA256 ซึ่งจะสร้างค่าขนาด 256 บิตที่สามารถตีความเป็นตัวเลขได้อย่างสะดวก หากผลลัพธ์น้อยกว่า n เราจะได้กุญแจส่วนตัวที่เหมาะสม มิฉะนั้น เราก็เพียงแค่ลองอีกครั้งด้วยตัวเลขสุ่มอื่น</p>
<blockquote>
<p>คำเตือน: อย่าเขียนโค้ดของคุณเองเพื่อสร้างตัวเลขสุ่ม หรือใช้ตัวสร้างตัวเลขสุ่ม "แบบง่าย" ที่มีให้ในภาษาโปรแกรมของคุณ ใช้ตัวสร้างตัวเลขสุ่มเทียมที่มีความปลอดภัยทางการเข้ารหัส (CSPRNG) จากแหล่งที่มีเอนโทรปีเพียงพอ ศึกษาเอกสารของไลบรารีตัวสร้างตัวเลขสุ่มที่คุณเลือกเพื่อให้มั่นใจว่ามีความปลอดภัยทางการเข้ารหัส การใช้งาน CSPRNG ที่ถูกต้องมีความสำคัญอย่างยิ่งต่อความปลอดภัยของกุญแจ</p>
</blockquote>
<p>ต่อไปนี้คือกุญแจส่วนตัว (k) ที่สร้างขึ้นแบบสุ่มซึ่งแสดงในรูปแบบเลขฐานสิบหก (256 บิตแสดงเป็น 64 หลักเลขฐานสิบหก โดยแต่ละหลักคือ 4 บิต):</p>
<pre><code>1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD
</code></pre>
<blockquote>
<p>TIP: จำนวนที่เป็นไปได้ของ private key ทั้งหมดนั้นมีอยู่ 2^256 เป็นตัวเลขที่ใหญ่มากจนยากจะจินตนาการได้ มันมีค่าประมาณ 10^77 (เลข 1 ตามด้วยเลข 0 อีก 77 ตัว) ในระบบเลขฐานสิบ เพื่อให้เข้าใจง่ายขึ้น ลองเปรียบเทียบกับจักรวาลที่เรามองเห็นได้ซึ่งนักวิทยาศาสตร์ประมาณการว่ามีอะตอมทั้งหมดประมาณ 10^80 อะตอม นั่นหมายความว่าช่วงค่าของกุญแจส่วนตัว Bitcoin มีขนาดใกล้เคียงกับจำนวนอะตอมทั้งหมดในจักรวาลที่เรามองเห็นได้</p>
</blockquote>
<h3>การอธิบายเกี่ยวกับวิทยาการเข้ารหัสแบบเส้นโค้งวงรี (Elliptic Curve Cryptography)</h3>
<p>วิทยาการเข้ารหัสแบบเส้นโค้งวงรี (ECC) เป็นประเภทหนึ่งของการเข้ารหัสแบบอสมมาตรหรือ public key ซึ่งอาศัยหลักการของปัญหาลอการิทึมแบบไม่ต่อเนื่อง โดยแสดงออกผ่านการบวกและการคูณบนจุดต่างๆ ของเส้นโค้งวงรี</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0402.png" alt="image"></p>
<p>บิตคอยน์ใช้เส้นโค้งวงรีเฉพาะและชุดค่าคงที่ทางคณิตศาสตร์ ตามที่กำหนดไว้ในมาตรฐานที่เรียกว่า secp256k1 ซึ่งกำหนดโดยสถาบันมาตรฐานและเทคโนโลยีแห่งชาติ (NIST) เส้นโค้ง secp256k1 ถูกกำหนดโดยฟังก์ชันต่อไปนี้ ซึ่งสร้างเส้นโค้งวงรี: y² = (x³ + 7) บนฟิลด์จำกัด (F_p) หรือ y² mod p = (x³ + 7) mod p</p>
<p>โดยที่ mod p (มอดูโลจำนวนเฉพาะ p) แสดงว่าเส้นโค้งนี้อยู่บนฟิลด์จำกัดของอันดับจำนวนเฉพาะ p ซึ่งเขียนได้เป็น F_p โดย p = 2^256 – 2^32 – 2^9 – 2^8 – 2^7 – 2^6 – 2^4 – 1 ซึ่งเป็นจำนวนเฉพาะที่มีค่ามหาศาล</p>
<p>บิตคอยน์ใช้เส้นโค้งวงรีที่ถูกนิยามบนฟิลด์จำกัดของอันดับจำนวนเฉพาะแทนที่จะอยู่บนจำนวนจริง ทำให้มันมีลักษณะเหมือนรูปแบบของจุดที่กระจัดกระจายในสองมิติ ซึ่งทำให้ยากต่อการจินตนาการภาพ อย่างไรก็ตาม คณิตศาสตร์ที่ใช้นั้นเหมือนกับเส้นโค้งวงรีบนจำนวนจริง</p>
<p>ตัวอย่างเช่น การเข้ารหัสลับด้วยเส้นโค้งวงรี: การแสดงภาพเส้นโค้งวงรีบน F(p) โดยที่ p=17 แสดงเส้นโค้งวงรีเดียวกันบนฟิลด์จำกัดของอันดับจำนวนเฉพาะ 17 ที่มีขนาดเล็กกว่ามาก ซึ่งแสดงรูปแบบของจุดบนตาราง</p>
<p>เส้นโค้งวงรี secp256k1 ที่ใช้ในบิตคอยน์สามารถนึกถึงได้ว่าเป็นรูปแบบของจุดที่ซับซ้อนมากกว่าบนตารางที่มีขนาดใหญ่มหาศาลจนยากจะเข้าใจได้</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0403.png" alt="image"></p>
<p>ตัวอย่างเช่น จุด P ที่มีพิกัด (x, y) ต่อไปนี้เป็นจุดที่อยู่บนเส้นโค้ง secp256k1:</p>
<pre><code>P =
(55066263022277343669578718895168534326250603453777594175500187360389116729240,
32670510020758816978083085130507043184471273380659243275938904335757337482424)
</code></pre>
<p>เราสามารถใช้ Python เพื่อยืนยันว่าจุดนี้อยู่บนเส้นโค้งวงรีได้ตามตัวอย่างนี้:<br>ตัวอย่างที่ 1: การใช้ Python เพื่อยืนยันว่าจุดนี้อยู่บนเส้นโค้งวงรี</p>
<pre><code>Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
&gt; p = 115792089237316195423570985008687907853269984665640564039457584007908834671663
&gt; x = 55066263022277343669578718895168534326250603453777594175500187360389116729240
&gt; y = 32670510020758816978083085130507043184471273380659243275938904335757337482424
&gt; (x ** 3 + 7 - y**2) % p
0
</code></pre>
<p>ผลลัพธ์เป็น 0 ซึ่งแสดงว่าจุดนี้อยู่บนเส้นโค้งวงรีจริง เพราะเมื่อแทนค่า x และ y ลงในสมการ y² = (x³ + 7) mod p แล้ว ทั้งสองด้านของสมการมีค่าเท่ากัน</p>
<p>ในคณิตศาสตร์ของเส้นโค้งวงรี มีจุดที่เรียกว่า "จุดที่อนันต์" (point at infinity) ซึ่งมีบทบาทคล้ายกับศูนย์ในการบวก บนคอมพิวเตอร์ บางครั้งจุดนี้แทนด้วย x = y = 0 (ซึ่งไม่เป็นไปตามสมการเส้นโค้งวงรี แต่เป็นกรณีพิเศษที่สามารถตรวจสอบได้ง่าย)</p>
<p>มีตัวดำเนินการ + ที่เรียกว่า "การบวก" ซึ่งมีคุณสมบัติคล้ายกับการบวกแบบดั้งเดิมของจำนวนจริงที่เด็กๆ เรียนในโรงเรียน เมื่อมีจุดสองจุด P1 และ P2 บนเส้นโค้งวงรี จะมีจุดที่สาม P3 = P1 + P2 ซึ่งอยู่บนเส้นโค้งวงรีเช่นกัน</p>
<p>ในเชิงเรขาคณิต จุดที่สาม P3 นี้คำนวณได้โดยการลากเส้นระหว่าง P1 และ P2 เส้นนี้จะตัดกับเส้นโค้งวงรีที่จุดเพิ่มเติมอีกหนึ่งจุดพอดี เรียกจุดนี้ว่า P3' = (x, y) จากนั้นให้สะท้อนกับแกน x เพื่อได้ P3 = (x, -y)</p>
<p>มีกรณีพิเศษบางกรณีที่อธิบายความจำเป็นของ "จุดที่อนันต์":</p>
<ol>
<li>ถ้า P1 และ P2 เป็นจุดเดียวกัน เส้น "ระหว่าง" P1 และ P2 ควรขยายเป็นเส้นสัมผัสกับเส้นโค้ง ณ จุด P1 นี้ เส้นสัมผัสนี้จะตัดกับเส้นโค้งที่จุดใหม่อีกหนึ่งจุดพอดี คุณสามารถใช้เทคนิคจากแคลคูลัสเพื่อหาความชันของเส้นสัมผัส เทคนิคเหล่านี้ใช้ได้อย่างน่าแปลกใจ แม้ว่าเราจะจำกัดความสนใจไว้ที่จุดบนเส้นโค้งที่มีพิกัดเป็นจำนวนเต็มเท่านั้น!</li>
<li>ในบางกรณี (เช่น ถ้า P1 และ P2 มีค่า x เดียวกันแต่ค่า y ต่างกัน) เส้นสัมผัสจะตั้งฉากพอดี ซึ่งในกรณีนี้ P3 = "จุดที่อนันต์"</li>
<li>ถ้า P1 เป็น "จุดที่อนันต์" แล้ว P1 + P2 = P2 ในทำนองเดียวกัน ถ้า P2 เป็นจุดที่อนันต์ แล้ว P1 + P2 = P1 นี่แสดงให้เห็นว่าจุดที่อนันต์มีบทบาทเป็นศูนย์</li>
</ol>
<p>การบวกนี้มีคุณสมบัติเชิงสมาคม (associative) ซึ่งหมายความว่า (A + B) + C = A + (B + C) นั่นหมายความว่าเราสามารถเขียน A + B + C โดยไม่ต้องมีวงเล็บและไม่มีความกำกวม</p>
<p>เมื่อเรานิยามการบวกแล้ว เราสามารถนิยามการคูณในแบบมาตรฐานที่ต่อยอดจากการบวก สำหรับจุด P บนเส้นโค้งวงรี ถ้า k เป็นจำนวนเต็มบวก แล้ว kP = P + P + P + … + P (k ครั้ง) โปรดทราบว่า k บางครั้งถูกเรียกว่า "เลขชี้กำลัง"</p>
<h3>Public Keys</h3>
<p>ในระบบคริปโตกราฟีแบบเส้นโค้งวงรี (Elliptic Curve Cryptography)  public key ถูกคำนวณจาก private key โดยใช้การคูณเส้นโค้งวงรี ซึ่งเป็นกระบวนการที่ไม่สามารถย้อนกลับได้:</p>
<p>K = k × G</p>
<p>โดยที่:</p>
<ul>
<li>k คือ private key</li>
<li>G คือจุดคงที่ที่เรียกว่า จุดกำเนิด (generator point)</li>
<li>K คือ public key</li>
</ul>
<p>การดำเนินการย้อนกลับ ที่เรียกว่า "การหาลอการิทึมแบบไม่ต่อเนื่อง" (finding the discrete logarithm) - คือการคำนวณหา k เมื่อรู้ค่า K - เป็นสิ่งที่ยากมากเทียบเท่ากับการลองค่า k ทุกค่าที่เป็นไปได้ (วิธีการแบบ brute-force)</p>
<p>ความยากของการย้อนกลับนี้คือหลักการความปลอดภัยหลักของระบบ ECC ที่ใช้ในบิตคอยน์ ซึ่งทำให้สามารถเผยแพร่ public key ได้อย่างปลอดภัย โดยที่ไม่ต้องกังวลว่าจะมีใครสามารถคำนวณย้อนกลับเพื่อหา private key ได้</p>
<blockquote>
<p>TIP:การคูณเส้นโค้งวงรีเป็นฟังก์ชันประเภทที่นักเข้ารหัสลับเรียกว่า “ trap door function ”:</p>
</blockquote>
<blockquote>
</blockquote>
<ul>
<li>เป็นสิ่งที่ทำได้ง่ายในทิศทางหนึ่ง</li>
<li>แต่เป็นไปไม่ได้ที่จะทำในทิศทางตรงกันข้าม</li>
</ul>
<blockquote>
<p>คนที่มี private key สามารถสร้าง public key ได้อย่างง่ายดาย และสามารถแบ่งปันกับโลกได้โดยรู้ว่าไม่มีใครสามารถย้อนกลับฟังก์ชันและคำนวณ private key จาก public key ได้ กลวิธีทางคณิตศาสตร์นี้กลายเป็นพื้นฐานสำหรับลายเซ็นดิจิทัลที่ปลอมแปลงไม่ได้และมีความปลอดภัย ซึ่งใช้พิสูจน์การควบคุมเงินบิตคอยน์</p>
</blockquote>
<p>เริ่มต้นด้วยการใช้ private key ในรูปแบบของตัวเลขสุ่ม เราคูณมันด้วยจุดที่กำหนดไว้ล่วงหน้าบนเส้นโค้งที่เรียกว่า จุดกำเนิด (generator point)  เพื่อสร้างจุดอื่นที่อยู่บนเส้นโค้งเดียวกัน ซึ่งคำตอบจะเป็น public key ที่สอดคล้องกัน จุดกำเนิดถูกกำหนดไว้เป็นส่วนหนึ่งของมาตรฐาน secp256k1 และเป็นค่าเดียวกันสำหรับกุญแจทั้งหมดในระบบบิตคอยน์</p>
<p>เนื่องจากจุดกำเนิด G เป็นค่าเดียวกันสำหรับผู้ใช้บิตคอยน์ทุกคน private key (k) ที่คูณกับ G จะได้ public key (K) เดียวกันเสมอ ความสัมพันธ์ระหว่าง k และ K เป็นแบบตายตัวแต่สามารถคำนวณได้ในทิศทางเดียวเท่านั้น คือจาก k ไปยัง K นี่คือเหตุผลที่ public key ของบิตคอยน์ (K) สามารถแบ่งปันกับทุกคนได้โดยไม่เปิดเผย private key (k) ของผู้ใช้</p>
<blockquote>
<p>TIP: private key สามารถแปลงเป็น public key ได้ แต่ public key ไม่สามารถแปลงกลับเป็น private key ได้ เพราะคณิตศาสตร์ที่ใช้ทำงานได้เพียงทิศทางเดียวเท่านั้น</p>
</blockquote>
<p>เมื่อนำการคูณเส้นโค้งวงรีมาใช้งาน เราจะนำ private key (k) ที่สร้างขึ้นก่อนหน้านี้มาคูณกับจุดกำเนิด G เพื่อหา public key (K):</p>
<pre><code>K = 1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD × G
</code></pre>
<p>public key (K) จะถูกกำหนดเป็นจุด K = (x, y) โดยที่:</p>
<pre><code>x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
</code></pre>
<p>เพื่อจะให้เห็นภาพของการคูณจุดด้วยจำนวนเต็มมากขึ้น เราจะใช้เส้นโค้งวงรีที่ง่ายกว่าบนจำนวนจริง (โดยหลักการทางคณิตศาสตร์ยังคงเหมือนกัน) เป้าหมายของเราคือการหาผลคูณ kG ของจุดกำเนิด G ซึ่งเทียบเท่ากับการบวก G เข้ากับตัวเอง k ครั้งติดต่อกัน</p>
<p>ในเส้นโค้งวงรี การบวกจุดเข้ากับตัวเองเทียบเท่ากับการลากเส้นสัมผัสที่จุดนั้นและหาว่าเส้นนั้นตัดกับเส้นโค้งอีกครั้งที่จุดใด จากนั้นจึงสะท้อนจุดนั้นบนแกน x</p>
<p>การเข้ารหัสลับด้วยเส้นโค้งวงรี: การแสดงภาพการคูณจุด G ด้วยจำนวนเต็ม k บนเส้นโค้งวงรี แสดงกระบวนการในการหา G, 2G, 4G เป็นการดำเนินการทางเรขาคณิตบนเส้นโค้งได้ดังนี้</p>
<blockquote>
<p>TIP: ในซอฟแวร์ของบิตคอยน์ส่วนใหญ่ใช้ไลบรารีเข้ารหัสลับ libsecp256k1 เพื่อทำการคำนวณทางคณิตศาสตร์เส้นโค้งวงรี<br><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0404.png" alt="image"></p>
</blockquote>
<h3>Output and Input Scripts</h3>
<p>แม้ว่าภาพประกอบจาก Bitcoin whitepaper ที่แสดงเรื่อง "Transaction chain" จะแสดงให้เห็นว่ามีการใช้ public key และ digital signature โดยตรง แต่ในความเป็นจริงบิตคอยน์เวอร์ชันแรกนั้นมีการส่งการชำระเงินไปยังฟิลด์ที่เรียกว่า output script และมีการใช้จ่ายบิตคอยน์เหล่านั้นโดยได้รับอนุญาตจากฟิลด์ที่เรียกว่า input script ฟิลด์เหล่านี้อนุญาตให้มีการดำเนินการเพิ่มเติมนอกเหนือจาก (หรือแทนที่) การตรวจสอบว่าลายเซ็นสอดคล้องกับ public key หรือไม่ ตัวอย่างเช่น output script สามารถมี public key สองดอกและต้องการลายเซ็นสองลายเซ็นที่สอดคล้องกันในฟิลด์ input script ที่ใช้จ่าย</p>
<p>ในภายหลัง ในหัวข้อ [tx_script] เราจะได้เรียนรู้เกี่ยวกับสคริปต์อย่างละเอียด สำหรับตอนนี้ สิ่งที่เราต้องเข้าใจคือ บิตคอยน์จะถูกรับเข้า output script ที่ทำหน้าที่เหมือน public key และการใช้จ่ายบิตคอยน์จะได้รับอนุญาตโดย input script ที่ทำหน้าที่เหมือนลายเซ็น</p>
<h4>IP Addresses: The Original Address for Bitcoin (P2PK)</h4>
<p>เราได้เห็นแล้วว่าอลิซสามารถจ่ายเงินให้บ็อบโดยการมอบบิตคอยน์บางส่วนของเธอให้กับกุญแจสาธารณะของบ็อบ แต่อลิซจะได้กุญแจสาธารณะของบ็อบมาได้อย่างไร? บ็อบอาจจะให้สำเนากุญแจแก่เธอ แต่ลองดูกุญแจสาธารณะที่เราใช้งานในตัวอย่างที่ผ่านมาอีกครั้ง:</p>
<pre><code>x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
</code></pre>
<blockquote>
<p>TIP จากหลาม: :สังเกตได้ว่า public key มีความยาวมาก ลองจินตนาการว่าบ็อบพยายามอ่านกุญแจนี้ให้อลิซฟังทางโทรศัพท์ คงจะยากมากที่จะอ่านและบันทึกโดยไม่มีข้อผิดพลาด</p>
</blockquote>
<p>แทนที่จะป้อนกุญแจสาธารณะโดยตรง เวอร์ชันแรกของซอฟต์แวร์บิตคอยน์อนุญาตให้ผู้จ่ายเงินป้อนที่อยู่ IP ของผู้รับได้ ตามที่แสดงในหน้าจอการส่งเงินรุ่นแรกของบิตคอยน์ผ่าน The Internet Archive</p>
<p>คุณสมบัตินี้ถูกลบออกในภายหลัง เนื่องจากมีปัญหามากมายในการใช้ที่อยู่ IP แต่คำอธิบายสั้นๆ จะช่วยให้เราเข้าใจได้ดีขึ้นว่าทำไมคุณสมบัติบางอย่างอาจถูกเพิ่มเข้าไปในโปรโตคอลบิตคอยน์</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0405.png" alt="image"></p>
<p>เมื่ออลิซป้อนที่อยู่ IP ของบ็อบในบิตคอยน์เวอร์ชัน 0.1 Full node ของเธอจะทำการเชื่อมต่อกับ full node ของเขาและได้รับ public key ใหม่จากกระเป๋าสตางค์ของบ็อบที่โหนดของเขาไม่เคยให้กับใครมาก่อน การที่เป็น public key ใหม่นี้มีความสำคัญเพื่อให้แน่ใจว่าธุรกรรมต่าง ๆ ที่จ่ายให้บ็อบจะไม่สามารถถูกเชื่อมโยงเข้าด้วยกันโดยคนที่กำลังดูบล็อกเชนและสังเกตเห็นว่าธุรกรรมทั้งหมดจ่ายไปยัง public key เดียวกัน</p>
<p>เมื่อใช้ public key จากโหนดของอลิซซึ่งได้รับมาจากโหนดของบ็อบ กระเป๋าสตางค์ของอลิซจะสร้างเอาต์พุตธุรกรรมที่จ่ายให้กับสคริปต์เอาต์พุตดังนี้</p>
<pre><code>&lt;Bob's public key&gt; OP_CHECKSIG
</code></pre>
<p>ต่อมาบ็อบจะสามารถใช้จ่ายเอาต์พุตนั้นด้วยสคริปต์อินพุตที่ประกอบด้วยลายเซ็นของเขาเท่านั้น:</p>
<pre><code>&lt;Bob's signature&gt;
</code></pre>
<p>เพื่อให้เข้าใจว่าสคริปต์อินพุตและเอาต์พุตกำลังทำอะไร คุณสามารถรวมพวกมันเข้าด้วยกัน (สคริปต์อินพุตก่อน) แล้วสังเกตว่าข้อมูลแต่ละชิ้น (แสดงในเครื่องหมาย &lt; &gt;) จะถูกวางไว้ที่ด้านบนสุดของรายการที่เรียกว่าสแตก (stack) เมื่อพบรหัสคำสั่ง (opcode) มันจะใช้รายการจากสแตก โดยเริ่มจากรายการบนสุด มาดูว่ามันทำงานอย่างไรโดยเริ่มจากสคริปต์ที่รวมกัน:</p>
<pre><code>&lt;Bob's signature&gt; &lt;Bob's public key&gt; OP_CHECKSIG
</code></pre>
<p>สำหรับสคริปต์นี้ ลายเซ็นของบ็อบจะถูกนำไปไว้บนสแตก จากนั้น public key ของบ็อบจะถูกวางไว้ด้านบนของลายเซ็น และบนสุดจะเป็นคำสั่ง OP_CHECKSIG ที่จะใช้องค์ประกอบสองอย่าง เริ่มจาก public key ตามด้วยลายเซ็น โดยลบพวกมันออกจากสแตก มันจะตรวจสอบว่าลายเซ็นตรงกับ public key และยืนยันฟิลด์ต่าง ๆ ในธุรกรรม ถ้าลายเซ็นถูกต้อง OP_CHECKSIG จะแทนที่ตัวเองบนสแตกด้วยค่า 1 ถ้าลายเซ็นไม่ถูกต้อง มันจะแทนที่ตัวเองด้วย 0 ถ้ามีรายการที่ไม่ใช่ศูนย์อยู่บนสุดของสแตกเมื่อสิ้นสุดการประเมิน สคริปต์ก็จะผ่าน ถ้าสคริปต์ทั้งหมดในธุรกรรมผ่าน และรายละเอียดอื่น ๆ ทั้งหมดเกี่ยวกับธุรกรรมนั้นต้องถูกต้องจึงจะถือว่าธุรกรรมนั้นถูกต้อง</p>
<p>โดยสรุป สคริปต์ข้างต้นใช้ public key และลายเซ็นเดียวกันกับที่อธิบายใน whitepaper แต่เพิ่มความซับซ้อนของฟิลด์สคริปต์สองฟิลด์และรหัสคำสั่งหนึ่งตัว ซึ่งเราจะเริ่มเห็นประโยชน์เมื่อเรามองที่ส่วนต่อไป</p>
<blockquote>
<p>TIP:จากหลาม agian: เอาต์พุตประเภทนี้เป็นที่รู้จักในปัจจุบันว่า P2PK ซึ่งมันไม่เคยถูกใช้อย่างแพร่หลายสำหรับการชำระเงิน และไม่มีโปรแกรมที่ใช้กันอย่างแพร่หลายที่รองรับการชำระเงินผ่านที่อยู่ IP เป็นเวลาเกือบทศวรรษแล้ว</p>
</blockquote>
<h4>Legacy addresses for P2PKH</h4>
<p>แน่นอนว่าการป้อนที่อยู่ IP ของคนที่คุณต้องการจ่ายเงินให้นั้นมีข้อดีหลายประการ แต่ก็มีข้อเสียหลายประการเช่นกัน หนึ่งในข้อเสียที่สำคัญคือผู้รับจำเป็นต้องให้กระเป๋าสตางค์ของพวกเขาออนไลน์ที่ที่อยู่ IP ของพวกเขา และต้องสามารถเข้าถึงได้จากโลกภายนอก </p>
<p>ซึ่งสำหรับคนจำนวนมากนั่นไม่ใช่ตัวเลือกที่เป็นไปได้เพราะหากพวกเขา:</p>
<ul>
<li>ปิดคอมพิวเตอร์ในเวลากลางคืน</li>
<li>แล็ปท็อปของพวกเขาเข้าสู่โหมดสลีป</li>
<li>อยู่หลังไฟร์วอลล์</li>
<li>หรือกำลังใช้การแปลงที่อยู่เครือข่าย (NAT)</li>
</ul>
<p>ปัญหานี้นำเรากลับมาสู่ความท้าทายเดิมที่ผู้รับเงินอย่างบ็อบต้องให้ public key ที่มีความยาวมากแก่ผู้จ่ายเงินอย่างอลิซ  public key ของบิตคอยน์ที่สั้นที่สุดที่นักพัฒนาบิตคอยน์รุ่นแรกรู้จักมีขนาด 65 ไบต์ เทียบเท่ากับ 130 ตัวอักษรเมื่อเขียนในรูปแบบเลขฐานสิบหก (เฮกซาเดซิมอล) แต่อย่างไรก็ตาม บิตคอยน์มีโครงสร้างข้อมูลหลายอย่างที่มีขนาดใหญ่กว่า 65 ไบต์มาก ซึ่งจำเป็นต้องถูกอ้างอิงอย่างปลอดภัยในส่วนอื่น ๆ ของบิตคอยน์โดยใช้ข้อมูลขนาดเล็กที่สุดเท่าที่จะปลอดภัยได้</p>
<p>โดยบิตคอยน์แก้ปัญหานี้ด้วย ฟังก์ชันแฮช (hash function) ซึ่งเป็นฟังก์ชันที่รับข้อมูลที่อาจมีขนาดใหญ่ นำมาแฮช และให้ผลลัพธ์เป็นข้อมูลขนาดคงที่ ฟังก์ชันแฮชจะผลิตผลลัพธ์เดียวกันเสมอเมื่อได้รับข้อมูลนำเข้าแบบเดียวกัน และฟังก์ชันที่ปลอดภัยจะทำให้เป็นไปไม่ได้ในทางปฏิบัติสำหรับผู้ที่ต้องการเลือกข้อมูลนำเข้าอื่นที่ให้ผลลัพธ์เหมือนกันได้ นั่นทำให้ผลลัพธ์เป็น คำมั่นสัญญา (commitment) ต่อข้อมูลนำเข้า เป็นสัญญาว่าในทางปฏิบัติ มีเพียงข้อมูลนำเข้า x เท่านั้นที่จะให้ผลลัพธ์ X</p>
<p>สมมติว่าผมต้องการถามคำถามคุณและให้คำตอบของผมในรูปแบบที่คุณไม่สามารถอ่านได้ทันที สมมติว่าคำถามคือ "ในปีไหนที่ซาโตชิ นาคาโมโตะเริ่มทำงานบนบิทคอยน์?" ผมจะให้การยืนยันคำตอบของผมในรูปแบบของผลลัพธ์จากฟังก์ชันแฮช SHA256 ซึ่งเป็นฟังก์ชันที่ใช้บ่อยที่สุดในบิทคอยน์:</p>
<pre><code>94d7a772612c8f2f2ec609d41f5bd3d04a5aa1dfe3582f04af517d396a302e4e
</code></pre>
<p>ต่อมา หลังจากคุณบอกคำตอบที่คุณเดาสำหรับคำถามนั้น ผมสามารถเปิดเผยคำตอบของผมและพิสูจน์ให้คุณเห็นว่าคำตอบของผม เมื่อใช้เป็นข้อมูลสำหรับฟังก์ชันแฮช จะให้ผลลัพธ์เดียวกันกับที่ผมให้คุณก่อนหน้านี้</p>
<pre><code>$ echo "2007.  He said about a year and a half before Oct 2008" | sha256sum
94d7a772612c8f2f2ec609d41f5bd3d04a5aa1dfe3582f04af517d396a302e4e
</code></pre>
<p>ทีนี้ให้สมมติว่าเราถามบ็อบว่า " public key ของคุณคืออะไร?" บ็อบสามารถใช้ฟังก์ชันแฮชเพื่อให้การยืนยันที่ปลอดภัยทางการเข้ารหัสต่อ public key ของเขา หากเขาเปิดเผยกุญแจในภายหลัง และเราตรวจสอบว่ามันให้ผลการยืนยันเดียวกันกับที่เขาให้เราก่อนหน้านี้ เราสามารถมั่นใจได้ว่ามันเป็นกุญแจเดียวกันที่ใช้สร้างการยืนยันก่อนหน้านี้</p>
<p>ฟังก์ชันแฮช SHA256 ถือว่าปลอดภัยมากและให้ผลลัพธ์ 256 บิต (32 ไบต์) น้อยกว่าครึ่งหนึ่งของขนาด public key ของบิทคอยน์ดั้งเดิม แต่อย่างไรก็ตาม มีฟังก์ชันแฮชอื่นๆ ที่ปลอดภัยน้อยกว่าเล็กน้อยที่ให้ผลลัพธ์ขนาดเล็กกว่า เช่น ฟังก์ชันแฮช RIPEMD-160 ซึ่งให้ผลลัพธ์ 160 บิต (20 ไบต์) ด้วยเหตุผลที่ซาโตชิ นาคาโมโตะไม่เคยระบุ เวอร์ชันดั้งเดิมของบิทคอยน์สร้างการยืนยันต่อ public key โดยการแฮชกุญแจด้วย SHA256 ก่อน แล้วแฮชผลลัพธ์นั้นด้วย RIPEMD-160 ซึ่งให้การยืนยันขนาด 20 ไบต์ต่อ public key</p>
<p>เราสามารถดูสิ่งนี้ตามอัลกอริทึม เริ่มจากกุญแจสาธารณะ K เราคำนวณแฮช SHA256 และคำนวณแฮช RIPEMD-160 ของผลลัพธ์ ซึ่งให้ตัวเลข 160 บิต (20 ไบต์): A = RIPEMD160(SHA256(K))</p>
<p>ทีนี้เราคงเข้าใจวิธีสร้างการยืนยันต่อ public key แล้ว ต่อไปเราจะมาดูวิธีการใช้งานโดยพิจารณาสคริปต์เอาต์พุตต่อไปนี้:</p>
<pre><code>OP_DUP OP_HASH160 &lt;Bob's commitment&gt; OP_EQUAL OP_CHECKSIG
</code></pre>
<p>และสคริปต์อินพุตต่อไปนี้:</p>
<pre><code>&lt;Bob's signature&gt; &lt;Bob's public key&gt;
</code></pre>
<p>และเมื่อเรารวมมันเข้าด้วยกันเราจะได้ผลลัพธ์ดังนี้:</p>
<pre><code>&lt;Bob's signature&gt; &lt;Bob's public key&gt; OP_DUP OP_HASH160 &lt;Bob's commitment&gt; OP_EQUAL OP_CHECKSIG
</code></pre>
<p>เหมือนที่เราทำใน IP Addresses: The Original Address for Bitcoin (P2PK) เราเริ่มวางรายการลงในสแต็ก ลายเซ็นของบ็อบถูกวางก่อน จากนั้น public key ของเขาถูกวางไว้ด้านบน จากนั้นดำเนินการ OP_DUP เพื่อทำสำเนารายการบนสุด ดังนั้นรายการบนสุดและรายการที่สองจากบนในสแต็กตอนนี้เป็น public key ของบ็อบทั้งคู่ การดำเนินการ OP_HASH160 ใช้ (ลบ) public key บนสุดและแทนที่ด้วยผลลัพธ์ของการแฮชด้วย RIPEMD160(SHA256(K)) ดังนั้นตอนนี้บนสุดของสแต็กคือแฮชของ public key ของบ็อบ ต่อไป  commitment ถูกเพิ่มไว้บนสุดของสแต็ก การดำเนินการ OP_EQUALVERIFY ใช้รายการสองรายการบนสุดและตรวจสอบว่าพวกมันเท่ากัน ซึ่งควรเป็นเช่นนั้นหาก public key ที่บ็อบให้ในสคริปต์อินพุตเป็น public key เดียวกันกับที่ใช้สร้างการยืนยันในสคริปต์เอาต์พุตที่อลิซจ่าย หาก OP_EQUALVERIFY ล้มเหลว ทั้งสคริปต์จะล้มเหลว สุดท้าย เราเหลือสแต็กที่มีเพียงลายเซ็นของบ็อบและ public key ของเขา รหัสปฏิบัติการ OP_CHECKSIG ตรวจสอบว่าพวกมันสอดคล้องกัน</p>
<blockquote>
<p>TIP: จากหลาม ถ้าอ่านตรงนี้และงง ๆ ผมไปทำรูปมาให้ดูง่ายขึ้นครับ</p>
</blockquote>
<p><img src="https://image.nostr.build/3ea1036b7de3d83b960d1ed862e738120159e4f173d55a4dfdaddc75c3b25166.png" alt="image"></p>
<p>แม้กระบวนการของการ pay-to-publickey-hash(P2PKH) อาจดูซับซ้อน แต่มันทำให้การที่อลิซจ่ายเงินให้บ็อบมีเพียงการยืนยันเพียง 20 ไบต์ต่อ public key ของเขาแทนที่จะเป็นตัวกุญแจเอง ซึ่งจะมีขนาด 65 ไบต์ในเวอร์ชันดั้งเดิมของบิทคอยน์ นั่นเป็นข้อมูลที่น้อยกว่ามากที่บ็อบต้องสื่อสารกับอลิซ</p>
<p>แต่อย่างไรก็ตาม เรายังไม่ได้พูดถึงวิธีที่บ็อบรับ 20 ไบต์เหล่านั้นจากกระเป๋าเงินบิทคอยน์ของเขาไปยังกระเป๋าเงินของอลิซ มีการเข้ารหัสค่าไบต์ที่ใช้กันอย่างแพร่หลาย เช่น เลขฐานสิบหก แต่ข้อผิดพลาดใด ๆ ในการคัดลอกการยืนยันจะทำให้บิทคอยน์ถูกส่งไปยังเอาต์พุตที่ไม่สามารถใช้จ่ายได้ ทำให้พวกมันสูญหายไปตลอดกาล โดยในส่วนถัดไป เราจะดูที่การเข้ารหัสแบบกะทัดรัดและการตรวจสอบความถูกต้อง</p>
<h4>Base58check Encoding</h4>
<p>ระบบคอมพิวเตอร์มีวิธีเขียนตัวเลขยาวๆ ให้สั้นลงโดยใช้ทั้งตัวเลขและตัวอักษรผสมกัน เพื่อใช้พื้นที่น้อยลงอย่างเช่น</p>
<ul>
<li>ระบบเลขฐานสิบ (ปกติที่เราใช้) - ใช้เลข 0-9 เท่านั้น</li>
<li>ระบบเลขฐานสิบหก - ใช้เลข 0-9 และตัวอักษร A-F ตัวอย่าง: เลข 255 ในระบบปกติ เขียนเป็น FF ในระบบเลขฐานสิบหก (สั้นกว่า)</li>
<li>ระบบเลขฐานหกสิบสี่ (Base64) - ใช้สัญลักษณ์ถึง 64 ตัว: ตัวอักษรเล็ก (a-z) 26 ตัว, ตัวอักษรใหญ่ (A-Z) 26 ตัว, ตัวเลข (0-9) 10 ตัว, สัญลักษณ์พิเศษอีก 2 ตัว ("+" และ "/")</li>
</ul>
<p>โดยระบบ Base64 นี้ช่วยให้เราส่งไฟล์คอมพิวเตอร์ผ่านข้อความธรรมดาได้ เช่น การส่งรูปภาพผ่านอีเมล โดยใช้พื้นที่น้อยกว่าการเขียนเป็นเลขฐานสิบแบบปกติมาก</p>
<p>การเข้ารหัสแบบ Base58 คล้ายกับ Base64 โดยใช้ตัวอักษรพิมพ์ใหญ่ พิมพ์เล็ก และตัวเลข แต่ได้ตัดตัวอักษรบางตัวที่มักถูกเข้าใจผิดว่าเป็นตัวอื่นและอาจดูเหมือนกันเมื่อแสดงในฟอนต์บางประเภทออกไป</p>
<p>Base58 คือ Base64 ที่ตัดตัวอักษรต่อไปนี้ออก:</p>
<ul>
<li>เลข 0 (ศูนย์)</li>
<li>ตัวอักษร O (ตัว O พิมพ์ใหญ่)</li>
<li>ตัวอักษร l (ตัว L พิมพ์เล็ก)</li>
<li>ตัวอักษร I (ตัว I พิมพ์ใหญ่)</li>
<li>และสัญลักษณ์ "+" และ "/"</li>
</ul>
<p>หรือพูดให้ง่ายขึ้น Base58 คือกลุ่มตัวอักษรพิมพ์เล็ก พิมพ์ใหญ่ และตัวเลข แต่ไม่มีตัวอักษรทั้งสี่ตัว (0, O, l, I) ที่กล่าวถึงข้างต้น ตัวอักษรทั้งหมดที่ใช้ใน Base58 จะแสดงให้เห็นในตัวอักษร Base58 ของบิทคอยน์</p>
<p>Example 2. Bitcoin’s base58 alphabet</p>
<pre><code>123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
</code></pre>
<p>การเพิ่มความปลอดภัยพิเศษเพื่อป้องกันการพิมพ์ผิดหรือข้อผิดพลาดในการคัดลอก base58check ได้รวม รหัสตรวจสอบ (checksum) ที่เข้ารหัสในตัวอักษร base58 เข้าไปด้วย รหัสตรวจสอบนี้คือข้อมูลเพิ่มเติมอีก 4 ไบต์ที่เพิ่มเข้าไปที่ท้ายของข้อมูลที่กำลังถูกเข้ารหัส</p>
<p>รหัสตรวจสอบนี้ได้มาจากการแฮชข้อมูลที่ถูกเข้ารหัส และจึงสามารถใช้เพื่อตรวจจับข้อผิดพลาดจากการคัดลอกและการพิมพ์ได้ เมื่อโปรแกรมได้รับรหัส base58check ซอฟต์แวร์ถอดรหัสจะคำนวณรหัสตรวจสอบของข้อมูลและเปรียบเทียบกับรหัสตรวจสอบที่รวมอยู่ในรหัสนั้น</p>
<p>หากทั้งสองไม่ตรงกัน แสดงว่ามีข้อผิดพลาดเกิดขึ้น และข้อมูล base58check นั้นไม่ถูกต้อง กระบวนการนี้ช่วยป้องกันไม่ให้ address บิทคอยน์ที่พิมพ์ผิดถูกยอมรับโดยซอฟต์แวร์กระเป๋าเงินว่าเป็น address ที่ถูกต้อง ซึ่งเป็นข้อผิดพลาดที่อาจส่งผลให้สูญเสียเงินได้</p>
<p>การแปลงข้อมูล (ตัวเลข) เป็นรูปแบบ base58check มีขั้นตอนดังนี้:</p>
<ol>
<li>เราเริ่มโดยการเพิ่ม prefix เข้าไปในข้อมูล เรียกว่า "version byte" ซึ่งช่วยให้ระบุประเภทของข้อมูลที่ถูกเข้ารหัสได้ง่าย ตัวอย่างเช่น: prefix ศูนย์ (0x00 ในระบบเลขฐานสิบหก) แสดงว่าข้อมูลควรถูกใช้เป็นการยืนยัน (hash) ในสคริปต์เอาต์พุต legacy P2PKH</li>
<li>จากนั้น เราคำนวณ "double-SHA" checksum ซึ่งหมายถึงการใช้อัลกอริทึมแฮช SHA256 สองครั้งกับผลลัพธ์ก่อนหน้า (prefix ต่อกับข้อมูล):<pre><code>checksum = SHA256(SHA256(prefix||data))
</code></pre>
</li>
<li>จากแฮช 32 ไบต์ที่ได้ (การแฮชซ้อนแฮช) เราเลือกเฉพาะ 4 ไบต์แรก ไบต์ทั้งสี่นี้ทำหน้าที่เป็นรหัสตรวจสอบข้อผิดพลาดหรือ checksum</li>
<li>นำ checksum นี้ไปต่อที่ท้ายข้อมูล</li>
</ol>
<p>การเข้ารหัสแบบ base58check คือรูปแบบการเข้ารหัสที่ใช้ base58 พร้อมกับการระบุเวอร์ชันและการตรวจสอบความถูกต้อง เพื่อการเข้ารหัสข้อมูลบิทคอยน์ โดยคุณสามารถดูภาพประกอบด้านล่างเพื่อความเข้าใจเพิ่มเติม</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0406.png" alt="image"></p>
<p>ในบิตคอยน์นั้น นอกจากจะใช้ base58check ในการยืนยัน public key แล้ว ก็ยังมีการใช้ในข้อมูลอื่น ๆ ด้วย เพื่อทำให้ข้อมูลนั้นกะทัดรัด อ่านง่าย และตรวจจับข้อผิดพลาดได้ง่ายด้วยรหัสนำหน้า (version prefix) ในการเข้ารหัสแบบ base58check ถูกใช้เพื่อสร้างรูปแบบที่แยกแยะได้ง่าย ซึ่งเมื่อเข้ารหัสด้วย base58 โดยจะมีตัวอักษรเฉพาะที่จุดเริ่มต้นของข้อมูลที่เข้ารหัส base58check ตัวอักษรเหล่านี้ช่วยให้เราระบุประเภทของข้อมูลที่ถูกเข้ารหัสและวิธีการใช้งานได้ง่าย นี่คือสิ่งที่แยกความแตกต่าง ตัวอย่างเช่น ระหว่าง address บิทคอยน์ที่เข้ารหัส base58check ซึ่งขึ้นต้นด้วยเลข 1 กับรูปแบบการนำเข้า private key  (WIF - Wallet Import Format) ที่เข้ารหัส base58check ซึ่งขึ้นต้นด้วยเลข 5 ตัวอย่างของ version prefix สามารถดูได้ตามตารางด้านล่างนี้</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742640432919-YAKIHONNES3.png" alt="image"></p>
<p>ภาพต่อไปนี้จะทำให้คุณเห็นภาพของกระบวนการแปลง public key ให้เป็น bitcoin address</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0407.png" alt="image"></p>
<h3>Compressed Public Keys</h3>
<p>ในยุคแรก ๆ ของบิตคอยน์นั้น มีเพียงการสร้าง public key แบบ 65 Bytes เท่านั้น แต่ในเวลาต่อมา เหล่านักพัฒนาในยุคหลังได้พบวิธีการสร้าง public key แบบใหม่ที่มีเพียง 33 Bytes และสามารถทำงานร่วมกันกับโหนดทั้งหมดในขณะนั้นได้ จีงไม่จะเป็นต้องเปลี่ยนแปลงกฎหรือโครงสร้างภายในโปรโตคอลของบิตคอยน์ โดย poublic key แบบใหม่ที่มีขนาด 33 Bytes นี้เรียกว่า compressed public key (public key ที่ถูกบีบอัด) และมีการเรียก public key ที่มีขนาด 65 Bytes ว่า uncompressed public key (public key ที่ไม่ถูกบีบอัด) ซึ่งประโยชน์ของ public key ที่เล็กลงนั้น นอกจากจะช่วยให้การส่ง public key ให้ผู้อื่นทำได้ง่ายขึ้นแล้ว ยังช่วยให้ธุรกรรมมีขนาดเล็กลง และช่วยให้สามารถทำการชำระเงินได้มากขึ้นในบล็อกเดียวกัน</p>
<p>อย่างที่เราได้เรียนรู้จากเนื้อหาในส่วนของ public key เราได้ทราบว่า public key คือจุด (x, y) บนเส้นโค้งวงรี เนื่องจากเส้นโค้งแสดงฟังก์ชันทางคณิตศาสตร์ จุดบนเส้นโค้งจึงเป็นคำตอบของสมการ ดังนั้นหากเรารู้พิกัด x เราก็สามารถคำนวณพิกัด y ได้โดยแก้สมการ y² mod p = (x³ + 7) mod p นั่นหมายความว่าเราสามารถเก็บเพียงพิกัด x ของ public key โดยละพิกัด y ไว้ ซึ่งช่วยลดขนาดของกุญแจและพื้นที่ที่ต้องใช้เก็บข้อมูลลง 256 บิต การลดขนาดลงเกือบ 50% ในทุกธุรกรรมรวมกันแล้วช่วยประหยัดข้อมูลได้มากมายในระยะยาว!</p>
<p>นี่คือ public key ที่ได้ยกเป็นตัวอย่างไว้ก่อนหน้า</p>
<pre><code>x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
</code></pre>
<p>และนี่คือ public key ที่มีตัวนำหน้า 04 ตามด้วยพิกัด x และ y ในรูปแบบ 04 x y:</p>
<pre><code>K = 04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
</code></pre>
<p>uncompressed public key นั้นจะมีตัวนำหน้าเป็น 04 แต่ compressed public key จะมีตัวนำหน้าเป็น 02 หรือ 03 โดยเหตุผลนั้นมาจากสมการ y² mod p = (x³ + 7) mod p เนื่องจากด้านซ้ายของสมการคือ y² คำตอบสำหรับ y จึงเป็นรากที่สอง ซึ่งอาจมีค่าเป็นบวกหรือลบก็ได้ หากมองเชิงภาพ นี่หมายความว่าพิกัด y ที่ได้อาจอยู่เหนือหรือใต้แกน x เราต้องไม่ลืมว่าเส้นโค้งมีความสมมาตร ซึ่งหมายความว่ามันจะสะท้อนเหมือนกระจกโดยแกน x ดังนั้น แม้เราจะละพิกัด y ได้ แต่เราต้องเก็บ เครื่องหมาย ของ y (บวกหรือลบ) หรืออีกนัยหนึ่งคือเราต้องจำว่ามันอยู่เหนือหรือใต้แกน x เพราะแต่ละตำแหน่งแทนจุดที่แตกต่างกันและเป็น public key ที่แตกต่างกัน</p>
<p>เมื่อคำนวณเส้นโค้งวงรีในระบบเลขฐานสองบนสนามจำกัดของเลขจำนวนเฉพาะ p พิกัด y จะเป็นเลขคู่หรือเลขคี่ ซึ่งสอดคล้องกับเครื่องหมายบวก/ลบตามที่อธิบายก่อนหน้านี้ ดังนั้น เพื่อแยกความแตกต่างระหว่างค่าที่เป็นไปได้สองค่าของ y เราจึงเก็บ compressed public key ด้วยตัวนำหน้า 02 ถ้า y เป็นเลขคู่ และ 03 ถ้า y เป็นเลขคี่ ซึ่งช่วยให้ซอฟต์แวร์สามารถอนุมานพิกัด y จากพิกัด x และคลายการบีบอัดของ public key ไปยังพิกัดเต็มของจุดได้อย่างถูกต้อง ดังภาพประกอบต่อไปนี้</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0408.png" alt="image"></p>
<p>นี่คือ public key เดียวกันกับที่ยกตัวอย่างไว้ข้างต้นซึ่งแสดงให้เห็นในรูป compressed public key ที่เก็บใน 264 บิต (66 ตัวอักษรเลขฐานสิบหก) โดยมีตัวนำหน้า 03 ซึ่งบ่งชี้ว่าพิกัด y เป็นเลขคี่:</p>
<pre><code>K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
</code></pre>
<p>compressed public key สอดคล้องกับ private key เดียวกันกับ uncompressed public key หมายความว่ามันถูกสร้างจาก private key เดียวกัน แต่อย่างไรก็ตาม มันก็มีส่วนที่แตกต่างจาก uncompressed public key นั้นคือ หากเราแปลง compressed public key เป็น commitment โดยใช้ฟังก์ชัน HASH160 (RIPEMD160(SHA256(K))) มันจะสร้าง commitment ที่แตกต่างจาก uncompressed public key และจะนำไปสู่ bitcoin address ที่แตกต่างกันในที่สุด สิ่งนี้อาจทำให้สับสนเพราะหมายความว่า private key เดียวสามารถสร้าง public key ในสองรูปแบบที่แตกต่างกัน (แบบบีบอัดและแบบไม่บีบอัด) ซึ่งสร้าง bitcoin address ที่แตกต่างกัน</p>
<p>compressed public key เป็นค่าเริ่มต้นในซอฟต์แวร์บิตคอยน์เกือบทั้งหมดในปัจจุบัน และถูกกำหนดให้ใช้กับคุณสมบัติใหม่บางอย่างที่เพิ่มในการอัปเกรดโปรโตคอลในภายหลัง</p>
<p>อย่างไรก็ตาม ซอฟต์แวร์บางตัวยังคงต้องรองรับ uncompressed public key เช่น แอปพลิเคชันกระเป๋าเงินที่นำเข้า private key จากกระเป๋าเงินเก่า เมื่อกระเป๋าเงินใหม่สแกนบล็อกเชนสำหรับผลลัพธ์และอินพุต P2PKH เก่า มันจำเป็นต้องรู้ว่าควรสแกนกุญแจขนาด 65 ไบต์ (และ commitment ของกุญแจเหล่านั้น) หรือกุญแจขนาด 33 ไบต์ (และ commitment ของกุญแจเหล่านั้น) หากไม่สแกนหาประเภทที่ถูกต้อง อาจทำให้ผู้ใช้ไม่สามารถใช้ยอดคงเหลือทั้งหมดได้ เพื่อแก้ไขปัญหานี้ เมื่อส่งออก private key จากกระเป๋าเงิน WIF ที่ใช้แสดง private key ในกระเป๋าเงินบิตคอยน์รุ่นใหม่จะถูกนำไปใช้แตกต่างกันเล็กน้อยเพื่อบ่งชี้ว่า private key เหล่านี้ถูกใช้ในการสร้าง compressed public key</p>
<h4>Legacy: Pay to Script Hash (P2SH)</h4>
<p>ตามที่เราได้เห็นในส่วนก่อนหน้านี้ ผู้รับบิตคอยน์ สามารถกำหนดให้การชำระเงินที่ส่งมาให้เขานั้นมีเงื่อนไขบางอย่างในสคริปต์เอาต์พุตได้โดยจะต้องปฏิบัติตามเงื่อนไขเหล่านั้นโดยใช้สคริปต์อินพุตเมื่อเขาใช้จ่ายบิตคอยน์เหล่านั้น ในส่วน IP Addresses: The Original Address for Bitcoin (P2PK) เงื่อนไขก็คือสคริปต์อินพุตต้องให้ลายเซ็นที่เหมาะสม ในส่วน Legacy Addresses for P2PKH นั้นจำเป็นต้องมี public key ที่เหมาะสมด้วย</p>
<p>ส่วนสำหรับผู้ส่งก็จะวางเงื่อนไขที่ผู้รับต้องการในสคริปต์เอาต์พุตที่ใช้จ่ายให้กับผู้รับ โดยผู้รับจะต้องสื่อสารเงื่อนไขเหล่านั้นให้ผู้ส่งทราบ ซึ่งคล้ายกับปัญหาที่บ๊อบต้องสื่อสาร public key ของเขาให้อลิซทราบ และเช่นเดียวกับปัญหานั้นที่ public key อาจมีขนาดค่อนข้างใหญ่ เงื่อนไขที่บ๊อบใช้ก็อาจมีขนาดใหญ่มากเช่นกัน—อาจมีขนาดหลายพันไบต์ นั่นไม่เพียงแต่เป็นข้อมูลหลายพันไบต์ที่ต้องสื่อสารให้อลิซทราบ แต่ยังเป็นข้อมูลหลายพันไบต์ที่เธอต้องจ่ายค่าธรรมเนียมธุรกรรมทุกครั้งที่ต้องการใช้จ่ายเงินให้บ๊อบ อย่างไรก็ตาม การใช้ฟังก์ชันแฮชเพื่อสร้าง commitment ขนาดเล็กสำหรับข้อมูลขนาดใหญ่ก็สามารถนำมาใช้ได้ในกรณีนี้เช่นกัน</p>
<p>ในเวลาต่อมานั้น การอัปเกรด BIP16 สำหรับโปรโตคอลบิตคอยน์ในปี 2012 ได้อนุญาตให้สคริปต์เอาต์พุตสร้าง commitment กับ redemption script (redeem script) ได้ แปลว่าเมื่อบ๊อบใช้จ่ายบิตคอยน์ของเขา ภายในสคริปต์อินพุตของเขานั้นจะต้องให้ redeem script ที่ตรงกับ commitment และข้อมูลที่จำเป็นเพื่อให้เป็นไปตาม redeem script (เช่น ลายเซ็น) เริ่มต้นด้วยการจินตนาการว่าบ๊อบต้องการให้มีลายเซ็นสองอันเพื่อใช้จ่ายบิตคอยน์ของเขา หนึ่งลายเซ็นจากกระเป๋าเงินบนเดสก์ท็อปและอีกหนึ่งจากอุปกรณ์เซ็นแบบฮาร์ดแวร์ เขาใส่เงื่อนไขเหล่านั้นลงใน redeem script:</p>
<pre><code>&lt;public key 1&gt; OP_CHECKSIGVERIFY &lt;public key 2&gt; OP_CHECKSIG
</code></pre>
<p>จากนั้นเขาสร้าง commitment กับ redeem script โดยใช้กลไก HASH160 เดียวกับที่ใช้สำหรับ commitment แบบ P2PKH, RIPEMD160(SHA256(script)) commitment นั้นถูกวางไว้ในสคริปต์เอาต์พุตโดยใช้เทมเพลตพิเศษ:</p>
<pre><code>OP_HASH160 &lt;commitment&gt; OP_EQUAL
</code></pre>
<blockquote>
<p>คำเตือน: เมื่อใช้ pay to script hash (P2SH) คุณต้องใช้เทมเพลต P2SH โดยเฉพาะ ซึ่งจะไม่มีข้อมูลหรือเงื่อนไขเพิ่มเติมในสคริปต์เอาต์พุต หากสคริปต์เอาต์พุตไม่ได้เป็น OP_HASH160 &lt;20 ไบต์&gt; OP_EQUAL แน่นอนว่า redeem script จะไม่ถูกใช้และบิตคอยน์ใด ๆ อาจไม่สามารถใช้จ่ายได้หรืออาจถูกใช้จ่ายได้โดยทุกคน (หมายความว่าใครก็สามารถนำไปใช้ได้)</p>
</blockquote>
<p>เมื่อบ๊อบต้องการจ่ายเงินที่เขาได้รับผ่าน commitment สำหรับสคริปต์ของเขา เขาจะใช้สคริปต์อินพุตที่รวมถึง redeem script ซึ่งถูกแปลงให้เป็นข้อมูลอีลิเมนต์เดียว นอกจากนี้เขายังให้ลายเซ็นที่จำเป็นเพื่อให้เป็นไปตาม redeem script โดยเรียงลำดับตามที่จะถูกใช้โดย opcodes:</p>
<pre><code>&lt;signature2&gt; &lt;signature1&gt; &lt;redeem script&gt;
</code></pre>
<p>เมื่อโหนดของบิตคอยน์ได้รับการใช้จ่ายของบ๊อบพวกมันจะตรวจสอบว่า redeem script ที่ถูกแปลงเป็นค่าแฮชแล้วมีค่าเดียวกันกับ commitment มั้ย หลังจากนั้นพวกมันจะแทนที่มันบนสแต็คด้วยค่าที่ถอดรหัสแล้ว:</p>
<pre><code>&lt;signature2&gt; &lt;signature1&gt; &lt;pubkey1&gt; OP_CHECKSIGVERIFY &lt;pubkey2&gt; OP_CHECKSIG
</code></pre>
<p>สคริปต์จะถูกประมวลผล และหากผ่านการตรวจสอบและรายละเอียดธุรกรรมอื่น ๆ ทั้งหมดถูกต้อง ธุรกรรมก็จะถือว่าใช้ได้</p>
<p>address สำหรับ P2SH ก็ถูกสร้างด้วย base58check เช่นกัน คำนำหน้าเวอร์ชันถูกตั้งเป็น 5 ซึ่งทำให้ที่อยู่ที่เข้ารหัสแล้วขึ้นต้นด้วยเลข 3 ตัวอย่างของที่อยู่ P2SH คือ 3F6i6kwkevjR7AsAd4te2YB2zZyASEm1HM</p>
<blockquote>
<p>TIP: P2SH ไม่จำเป็นต้องเหมือนกับธุรกรรมแบบหลายลายเซ็น (multisignature) เสมอไป ถึง address P2SH ส่วนใหญ่ แทนสคริปต์แบบหลายลายเซ็นก็ตาม แต่อาจแทนสคริปต์ที่เข้ารหัสธุรกรรมประเภทอื่น ๆ ได้ด้วย</p>
</blockquote>
<p>P2PKH และ P2SH เป็นสองเทมเพลตสคริปต์เท่านั้นที่ใช้กับการเข้ารหัสแบบ base58check พวกมันเป็นที่รู้จักในปัจจุบันว่าเป็น address แบบ legacy และกลายเป็นรูปแบบที่พบน้อยลงเรื่อยๆ address แบบ legacy ถูกแทนที่ด้วยaddress ตระกูล bech32</p>
<h4>การโจมตี P2SH แบบ Collision</h4>
<p>address ทั้งหมดที่อิงกับฟังก์ชันแฮชมีความเสี่ยงในทางทฤษฎีต่อผู้โจมตีที่อาจค้นพบอินพุตเดียวกันที่สร้างเอาต์พุตฟังก์ชันแฮช (commitment) โดยอิสระ ในกรณีของบิตคอยน์ หากพวกเขาค้นพบอินพุตในวิธีเดียวกับที่ผู้ใช้ดั้งเดิมทำ พวกเขาจะรู้ private key ของผู้ใช้และสามารถใช้จ่ายบิตคอยน์ของผู้ใช้นั้นได้ โอกาสที่ผู้โจมตีจะสร้างอินพุตสำหรับ commitment ที่มีอยู่แล้วโดยอิสระนั้นขึ้นอยู่กับความแข็งแกร่งของอัลกอริทึมแฮช สำหรับอัลกอริทึมที่ปลอดภัย 160 บิตอย่าง HASH160 ความน่าจะเป็นอยู่ที่ 1 ใน 2^160 นี่เรียกว่าการโจมตีแบบ preimage attack</p>
<p>ผู้โจมตีสามารถพยายามสร้างข้อมูลนำเข้าสองชุดที่แตกต่างกัน (เช่น redeem scripts) ที่สร้างการเข้ารหัสแบบเดียวกันได้ สำหรับ address ที่สร้างโดยฝ่ายเดียวทั้งหมด โอกาสที่ผู้โจมตีจะสร้างข้อมูลนำเข้าที่แตกต่างสำหรับการเข้ารหัสที่มีอยู่แล้วมีประมาณ 1 ใน 2^160 สำหรับอัลกอริทึม HASH160 นี่คือการโจมตีแบบ second preimage attack </p>
<p>อย่างไรก็ตาม สถานการณ์จะเปลี่ยนไปเมื่อผู้โจมตีสามารถมีอิทธิพลต่อค่าข้อมูลนำเข้าดั้งเดิมได้ ตัวอย่างเช่น ผู้โจมตีมีส่วนร่วมในการสร้างสคริปต์แบบหลายลายเซ็น (multisignature script) ซึ่งพวกเขาไม่จำเป็นต้องส่ง public key ของตนจนกว่าจะทราบ public key ของฝ่ายอื่นทั้งหมด ในกรณีนั้น ความแข็งแกร่งของอัลกอริทึมการแฮชจะลดลงเหลือรากที่สองของมัน สำหรับ HASH160 ความน่าจะเป็นจะกลายเป็น 1 ใน 2^80 นี่คือการโจมตีแบบ collision attack</p>
<p>เพื่อให้เข้าใจตัวเลขเหล่านี้ในบริบทที่ชัดเจน ข้อมูล ณ ต้นปี 2023 นักขุดบิตคอยน์ทั้งหมดรวมกันสามารถประมวลผลฟังก์ชันแฮชประมาณ 2^80 ทุกชั่วโมง พวกเขาใช้ฟังก์ชันแฮชที่แตกต่างจาก HASH160 ดังนั้นฮาร์ดแวร์ที่มีอยู่จึงไม่สามารถสร้างการโจมตีแบบ collision attack สำหรับมันได้ แต่การมีอยู่ของเครือข่ายบิตคอยน์พิสูจน์ว่าการโจมตีแบบชนกันต่อฟังก์ชัน 160 บิตอย่าง HASH160 สามารถทำได้จริงในทางปฏิบัติ นักขุดบิตคอยน์ได้ลงทุนเทียบเท่ากับหลายพันล้านดอลลาร์สหรัฐในฮาร์ดแวร์พิเศษ ดังนั้นการสร้างการโจมตีแบบ collision attack จึงไม่ใช่เรื่องถูก แต่มีองค์กรที่คาดหวังว่าจะได้รับบิตคอยน์มูลค่าหลายพันล้านดอลลาร์ไปยัง address ที่สร้างโดยกระบวนการที่เกี่ยวข้องกับหลายฝ่าย ซึ่งอาจทำให้การโจมตีนี้มีกำไร</p>
<p>มีโปรโตคอลการเข้ารหัสที่เป็นที่ยอมรับอย่างดีในการป้องกันการโจมตีแบบ collision attack แต่วิธีแก้ปัญหาที่ง่ายโดยไม่ต้องใช้ความรู้พิเศษจากผู้พัฒนากระเป๋าเงินคือการใช้ฟังก์ชันแฮชที่แข็งแกร่งกว่า การอัปเกรดบิตคอยน์ในภายหลังทำให้เป็นไปได้ และ address บิตคอยน์ใหม่ให้ความต้านทานการชนกันอย่างน้อย 128 บิต การดำเนินการแฮช 2^128 ครั้งจะใช้เวลานักขุดบิตคอยน์ปัจจุบันทั้งหมดประมาณ 32 พันล้านปี</p>
<p>แม้ว่าเราไม่เชื่อว่ามีภัยคุกคามเร่งด่วนต่อผู้ที่สร้าง address P2SH ใหม่ แต่เราแนะนำให้กระเป๋าเงินใหม่ทั้งหมดใช้ที่อยู่ประเภทใหม่เพื่อขจัดความกังวลเกี่ยวกับการโจมตีแบบ collision attack ของ P2SH address</p>
<h4>Bech32 Addresses</h4>
<p>ในปี 2017 โปรโตคอลบิตคอยน์ได้รับการอัปเกรด เพื่อป้องกันไม่ให้ตัวระบุธุรกรรม (txids) ไม่สามารถเปลี่ยนแปลงได้ โดยไม่ได้รับความยินยอมจากผู้ใช้ที่ทำการใช้จ่าย (หรือองค์ประชุมของผู้ลงนามเมื่อต้องมีลายเซ็นหลายรายการ) การอัปเกรดนี้เรียกว่า segregated witness (หรือเรียกสั้นๆ ว่า segwit) ซึ่งยังให้ความสามารถเพิ่มเติมสำหรับข้อมูลธุรกรรมในบล็อกและประโยชน์อื่น ๆ อีกหลายประการ แต่อย่างไรก็ตาม หากมีผู้ใช้เก่าที่ต้องการเข้าถึงประโยชน์ของ segwit โดยตรงต้องยอมรับการชำระเงินไปยังสคริปต์เอาต์พุตใหม่</p>
<p>ตามที่ได้กล่าวไว้ใน p2sh หนึ่งในข้อดีของเอาต์พุตประเภท P2SH คือผู้จ่ายไม่จำเป็นต้องรู้รายละเอียดของสคริปต์ที่ผู้รับใช้ การอัปเกรด segwit ถูกออกแบบมาให้ใช้กลไกนี้ได้ดังเดิม จึง ทำให้ผู้จ่ายสามารถเริ่มเข้าถึงประโยชน์ใหม่ ๆ หลายอย่างได้ทันทีโดยใช้ที่อยู่ P2SH แต่เพื่อให้ผู้รับสามารถเข้าถึงประโยชน์เหล่านั้นได้ พวกเขาจำเป็นจะต้องให้กระเป๋าเงินของผู้จ่ายจ่ายเงินให้เขาโดยใช้สคริปต์ประเภทอื่นแทน ซึ่งจะต้องอาศัยการอัปเกรดกระเป๋าเงินของผู้จ่ายเพื่อรองรับสคริปต์ใหม่เหล่านี้</p>
<p>ในช่วงแรก เหล่านักพัฒนาบิตคอยน์ได้นำเสนอ BIP142 ซึ่งจะยังคงใช้ base58check ร่วมกับไบต์เวอร์ชันใหม่ คล้ายกับการอัปเกรด P2SH แต่การให้กระเป๋าเงินทั้งหมดอัปเกรดไปใช้สคริปต์ใหม่ที่มีเวอร์ชัน base58check ใหม่นั้น คาดว่าจะต้องใช้ความพยายามเกือบเท่ากับการให้พวกเขาอัปเกรดไปใช้รูปแบบ address ที่เป็นแบบใหม่ทั้งหมด ด้วยเหตุนี้้เอง ผู้สนับสนุนบิตคอยน์หลายคนจึงเริ่มออกแบบรูปแบบ address ที่ดีที่สุดเท่าที่เป็นไปได้ พวกเขาระบุปัญหาหลายอย่างกับ base58check ไว้ดังนี้:</p>
<ul>
<li>การที่ base58check ใช้อักษรที่มีทั้งตัวพิมพ์ใหญ่และตัวพิมพ์เล็กทำให้ไม่สะดวกในการอ่านออกเสียงหรือคัดลอก ลองอ่าน address แบบเก่าในบทนี้ให้เพื่อนฟังและให้พวกเขาคัดลอก คุณจะสังเกตว่าคุณต้องระบุคำนำหน้าทุกตัวอักษรด้วยคำว่า "ตัวพิมพ์ใหญ่" และ "ตัวพิมพ์เล็ก" และเมื่อคุณตรวจสอบสิ่งที่พวกเขาเขียน คุณจะพบว่าตัวพิมพ์ใหญ่และตัวพิมพ์เล็กของตัวอักษรบางตัวอาจดูคล้ายกันในลายมือของคนส่วนใหญ่</li>
<li>รูปแบบนี้สามารถตรวจจับข้อผิดพลาดได้ แต่ไม่สามารถช่วยผู้ใช้แก้ไขข้อผิดพลาดเหล่านั้น ตัวอย่างเช่น หากคุณสลับตำแหน่งตัวอักษรสองตัวโดยไม่ตั้งใจเมื่อป้อน address ด้วยตนเอง กระเป๋าเงินของคุณจะเตือนว่ามีข้อผิดพลาดเกิดขึ้นแน่นอน แต่จะไม่ช่วยให้คุณค้นพบว่าข้อผิดพลาดอยู่ที่ไหน คุณอาจต้องใช้เวลาหลายนาทีที่น่าหงุดหงิดเพื่อค้นหาข้อผิดพลาดในที่สุด</li>
<li>การใช้ตัวอักษรที่มีทั้งตัวพิมพ์ใหญ่และตัวพิมพ์เล็กยังต้องใช้พื้นที่เพิ่มเติมในการเข้ารหัสใน QR code ซึ่งนิยมใช้ในการแชร์ address และ invoice ระหว่างกระเป๋าเงิน พื้นที่เพิ่มเติมนี้หมายความว่า QR code จำเป็นต้องมีขนาดใหญ่ขึ้นที่ความละเอียดเดียวกัน หรือไม่เช่นนั้นก็จะยากต่อการสแกนอย่างรวดเร็ว</li>
<li>การที่ต้องการให้กระเป๋าเงินผู้จ่ายทุกใบอัปเกรดเพื่อรองรับคุณสมบัติโปรโตคอลใหม่ เช่น P2SH และ segwit แม้ว่าการอัปเกรดเองอาจไม่ต้องใช้โค้ดมากนัก แต่ประสบการณ์แสดงให้เห็นว่าผู้พัฒนากระเป๋าเงินหลายรายมักยุ่งกับงานอื่น ๆ และบางครั้งอาจล่าช้าในการอัปเกรดเป็นเวลาหลายปี สิ่งนี้ส่งผลเสียต่อทุกคนที่ต้องการใช้คุณสมบัติใหม่ ๆ เหล่านี้</li>
</ul>
<p>นักพัฒนาที่ทำงานเกี่ยวกับรูปแบบ address สำหรับ segwit ได้พบวิธีแก้ปัญหาเหล่านี้ทั้งหมดในรูปแบบ address แบบใหม่ที่เรียกว่า bech32 (ออกเสียงด้วย "ch" อ่อน เช่นใน "เบช สามสิบสอง") คำว่า "bech" มาจาก BCH ซึ่งเป็นอักษรย่อของบุคคลสามคนที่ค้นพบรหัสวนนี้ในปี 1959 และ 1960 ซึ่งเป็นพื้นฐานของ bech32 ส่วน "32" หมายถึงจำนวนตัวอักษรในชุดตัวอักษร bech32 (คล้ายกับ 58 ใน base58check):</p>
<ul>
<li><p>Bech32 ใช้เฉพาะตัวเลขและตัวอักษรรูปแบบเดียว (โดยปกติจะแสดงเป็นตัวพิมพ์เล็ก) แม้ว่าชุดตัวอักษรของมันจะมีขนาดเกือบครึ่งหนึ่งของชุดตัวอักษรใน base58check ก็ตามแต่ address bech32 สำหรับสคริปต์ pay to witness public key hash (P2WPKH) ก็ยังยาวกว่า legacy address และมีขนาดเท่ากันกับสคริปต์ P2PKH </p>
</li>
<li><p>Bech32 สามารถทั้งตรวจจับและช่วยแก้ไขข้อผิดพลาดได้ ใน address ที่มีความยาวตามที่คาดหวังได้ และสามารถรับประกันทางคณิตศาสตร์ได้ว่าจะตรวจพบข้อผิดพลาดใด ๆ ที่ส่งผลกระทบต่อตัวอักษร 4 ตัวหรือน้อยกว่า ซึ่งเชื่อถือได้มากกว่า base58check ส่วนสำหรับข้อผิดพลาดที่ยาวกว่านั้น จะไม่สามารถตรวจพบได้ (โอกาสเกิดน้อยกว่าหนึ่งครั้งในหนึ่งพันล้าน) ซึ่งมีความเชื่อถือได้ประมาณเท่ากับ base58check ยิ่งไปกว่านั้น สำหรับ adddress ที่พิมพ์โดยมีข้อผิดพลาดเพียงเล็กน้อย มันสามารถบอกผู้ใช้ได้ว่าข้อผิดพลาดเหล่านั้นเกิดขึ้นที่ไหน ช่วยให้พวกเขาสามารถแก้ไขข้อผิดพลาดจากการคัดลอกเล็ก ๆ น้อย ๆ ได้อย่างรวดเร็ว </p>
</li>
<li><p>ตัวอย่างที่ 3 Bech32 address ที่มีข้อผิดพลาด<br>Address: bc1p9nh05ha8wrljf7ru236awn4t2x0d5ctkkywmv9sclnm4t0av2vgs4k3au7<br>ข้อผิดพลาดที่ตรวจพบแสดงเป็นตัวหนาและขีดเส้นใต้ สร้างโดยใช้โปรแกรมสาธิตการถอดรหัส  bech32 address</p>
</li>
<li><p>bech32 address นิยมเขียนด้วยตัวอักษรพิมพ์เล็กเท่านั้น แต่ตัวอักษรพิมพ์เล็กเหล่านี้สามารถแทนที่ด้วยตัวอักษรพิมพ์ใหญ่ก่อนการเข้ารหัส address ในรหัส QR ได้ วิธีนี้ช่วยให้สามารถใช้โหมดการเข้ารหัส QR แบบพิเศษที่ใช้พื้นที่น้อยกว่า คุณจะสังเกตเห็นความแตกต่างในขนาดและความซับซ้อนของรหัส QR ทั้งสองสำหรับที่อยู่เดียวกันในรูปภาพข้างล่างนี้</p>
</li>
</ul>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0409.png" alt="image"></p>
<ul>
<li>Bech32 ใช้ประโยชน์จากกลไกการอัปเกรดที่ออกแบบมาเป็นส่วนหนึ่งของ segwit เพื่อทำให้กระเป๋าเงินผู้จ่ายสามารถจ่ายเงินไปยังประเภทเอาต์พุตที่ยังไม่ได้ใช้งานได้ โดยมีเป้าหมายคือการอนุญาตให้นักพัฒนาสร้างกระเป๋าเงินในวันนี้ที่สามารถใช้จ่ายไปยัง bech32 address และทำให้กระเป๋าเงินนั้นยังคงสามารถใช้จ่ายไปยัง bech32address ได้สำหรับผู้ใช้คุณสมบัติใหม่ที่เพิ่มในการอัปเกรดโปรโตคอลในอนาคต โดยที่มีความหวังว่าเราอาจไม่จำเป็นต้องผ่านรอบการอัปเกรดทั้งระบบอีกต่อไป ซึ่งจำเป็นสำหรับการให้ผู้คนใช้งาน P2SH และ segwit ได้อย่างเต็มรูปแบบ</li>
<li></li>
<li><h4>Problems with Bech32 Addresses</h4>
</li>
</ul>
<p>address แบบ bech32 ประสบความสำเร็จในทุกด้านยกเว้นปัญหาหนึ่ง คือการรับประกันทางคณิตศาสตร์เกี่ยวกับความสามารถในการตรวจจับข้อผิดพลาดจะใช้ได้เฉพาะเมื่อความยาวของ address ที่คุณป้อนเข้าไปในกระเป๋าเงินมีความยาวเท่ากับ address ดั้งเดิมเท่านั้น หากคุณเพิ่มหรือลบตัวอักษรใด ๆ ระหว่างการคัดลอกจะทำให้ไม่สามารถตรวจจับได้ การรับประกันนี้จะไม่มีผล และกระเป๋าเงินของคุณอาจใช้จ่ายเงินไปยัง address ที่ไม่ถูกต้อง แต่อย่างไรก็ตาม แม้จะไม่มีคุณสมบัตินี้ มีความเชื่อว่าเป็นไปได้ยากมากที่ผู้ใช้ที่เพิ่มหรือลบตัวอักษรจะสร้างสตริงที่มีผลรวมตรวจสอบที่ถูกต้อง ซึ่งช่วยให้มั่นใจได้ว่าเงินของผู้ใช้จะปลอดภัย</p>
<p>น่าเสียดายที่การเลือกใช้ค่าคงที่ตัวหนึ่งในอัลกอริทึม bech32 บังเอิญทำให้การเพิ่มหรือลบตัวอักษร "q" ในตำแหน่งที่สองจากท้ายของ address ที่ลงท้ายด้วยตัวอักษร "p" เป็นเรื่องง่ายมาก ในกรณีเหล่านั้น คุณยังสามารถเพิ่มหรือลบตัวอักษร "q" หลายครั้งได้ด้วย ข้อผิดพลาดนี้จะถูกตรวจจับโดยผลรวมตรวจสอบ (checksum) ในบางครั้ง แต่จะถูกมองข้ามบ่อยกว่าความคาดหวังหนึ่งในพันล้านสำหรับข้อผิดพลาดจากการแทนที่ของ bech32 อย่างมาก สำหรับตัวอย่างสามารถดูได้ในรูปภาพข้างล่างนี้</p>
<p>ตัวอย่างที่ 4. การขยายความยาวของ bech32 address โดยไม่ทำให้ผลรวมตรวจสอบเป็นโมฆะ</p>
<pre><code>bech32 address ที่ถูกต้อง:
bc1pqqqsq9txsqp

address ที่ไม่ถูกต้องแต่มีผลรวมตรวจสอบที่ถูกต้อง:
bc1pqqqsq9txsqqqqp
bc1pqqqsq9txsqqqqqqp
bc1pqqqsq9txsqqqqqqqqp
bc1pqqqsq9txsqqqqqqqqqp
bc1pqqqsq9txsqqqqqqqqqqqp
</code></pre>
<p>จากตัวอย่างนี้ คุณจะเห็นว่าแม้มีการเพิ่มตัวอักษร "q" เข้าไปหลายตัวก่อนตัวอักษร "p" ตัวสุดท้าย ระบบตรวจสอบก็ยังคงยอมรับว่า address เหล่านี้ถูกต้อง นี่เป็นข้อบกพร่องสำคัญของ bech32 เพราะอาจทำให้เงินถูกส่งไปยัง address ที่ไม่มีใครเป็นเจ้าของจริง ๆ หรือ address ที่ไม่ได้ตั้งใจจะส่งไป</p>
<p>สำหรับเวอร์ชันเริ่มต้นของ segwit (เวอร์ชัน 0) ปัญหานี้ไม่ใช่ความกังวลในทางปฏิบัติ เพราะมีความยาวที่ถูกต้องมีเพียงสองแบบที่กำหนดไว้สำหรับเอาต์พุต นั้นคือ 22 Byte และ 34 Byte ซึ่งสอดคล้องกับ bech32 address ที่มีความยาวยาวที่ 42 หรือ 62 ตัวอักษร ดังนั้นคนจะต้องเพิ่มหรือลบตัวอักษร "q" จากตำแหน่งที่สองจากท้ายของ bech32 address ถึง 20 ครั้งเพื่อส่งเงินไปยัง address ที่ไม่ถูกต้องโดยที่กระเป๋าเงินไม่สามารถตรวจจับได้ อย่างไรก็ตาม มันอาจกลายเป็นปัญหาสำหรับผู้ใช้ในอนาคตหากมีการนำการอัปเกรดบนพื้นฐานของ segwit มาใช้</p>
<h4>Bech32m</h4>
<p>แม้ว่า bech32 จะทำงานได้ดีสำหรับ segwit v0 แต่นักพัฒนาไม่ต้องการจำกัดขนาดเอาต์พุตโดยไม่จำเป็นในเวอร์ชันหลังๆ ของ segwit หากไม่มีข้อจำกัด การเพิ่มหรือลบตัวอักษร "q" เพียงตัวเดียวใน bech32 address อาจทำให้ผู้ใช้ส่งเงินโดยไม่ตั้งใจไปยังเอาต์พุตที่ไม่สามารถใช้จ่ายได้หรือสามารถใช้จ่ายได้โดยทุกคน (ทำให้บิตคอยน์เหล่านั้นถูกนำไปโดยทุกคนได้) นักพัฒนาได้วิเคราะห์ปัญหา bech32 อย่างละเอียดและพบว่าการเปลี่ยนค่าคงที่เพียงตัวเดียวในอัลกอริทึมของพวกเขาจะขจัดปัญหานี้ได้ ทำให้มั่นใจว่าการแทรกหรือลบตัวอักษรสูงสุดห้าตัวจะไม่ถูกตรวจจับน้อยกว่าหนึ่งครั้งในหนึ่งพันล้านเท่านั้น</p>
<p>เวอร์ชันของ bech32 ที่มีค่าคงที่เพียงหนึ่งตัวที่แตกต่างกันเรียกว่า bech32 แบบปรับแต่ง (bech32m) ตัวอักษรทั้งหมดใน address แบบ bech32 และ bech32m สำหรับข้อมูลพื้นฐานเดียวกันจะเหมือนกันทั้งหมด ยกเว้นหกตัวสุดท้าย (ซึ่งเป็นส่วนของ checksum) นั่นหมายความว่ากระเป๋าเงินจำเป็นต้องรู้ว่ากำลังใช้เวอร์ชันใดเพื่อตรวจสอบความถูกต้องของ checksum แต่ address ทั้งสองประเภทมีไบต์เวอร์ชันภายในที่ทำให้การระบุเวอร์ชันที่ใช้อยู่เป็นเรื่องที่ง่าย<br>ในการทำงานกับทั้ง bech32 และ bech32m เราจะพิจารณากฎการเข้ารหัสและการแยกวิเคราะห์สำหรับ address บิตคอยน์แบบ bech32m เนื่องจากพวกมันครอบคลุมความสามารถในการแยกวิเคราะห์บน address แบบ bech32 และเป็นรูปแบบ address ที่แนะนำในปัจจุบันสำหรับกระเป๋าเงินบิตคอยน์</p>
<blockquote>
<p>ข้อความจากหลาม: คือผมว่าตรงนี้เขาเขียนไม่รู้เรื่อง แต่เดาว่าเขาน่าจะสื่อว่า เราควรเรียนรู้วิธีการทำงานกับ bech32m เพราะมันเป็นรูปแบบที่แนะนำให้ใช้ในปัจจุบัน และมันมีข้อดีเพราะbech32m สามารถรองรับการอ่าน address แบบ bech32 แบบเก่าได้ด้วย ง่ายๆ คือ ถ้าคุณเรียนรู้วิธีทำงานกับ bech32m คุณจะสามารถทำงานกับทั้ง bech32m และ bech32 ได้ทั้งสองแบบ</p>
</blockquote>
<p>bech32m address ริ่มต้นด้วยส่วนที่มนุษย์อ่านได้ (Human Readable Part: HRP) BIP173 มีกฎสำหรับการสร้าง HRP ของคุณเอง แต่สำหรับบิตคอยน์ คุณเพียงแค่จำเป็นต้องรู้จัก HRP ที่ถูกเลือกไว้แล้วตามที่แสดงในตารางข้างล่างนี้</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641071606-YAKIHONNES3.png" alt="image"></p>
<p>ส่วน HRP ตามด้วยตัวคั่น ซึ่งก็คือเลข "1" ในข้อเสนอก่อนหน้านี้สำหรับตัวคั่นโปรโตคอลได้ใช้เครื่องหมายทวิภาค (colon) แต่ระบบปฏิบัติการและแอปพลิเคชันบางตัวที่อนุญาตให้ผู้ใช้ดับเบิลคลิกคำเพื่อไฮไลต์สำหรับการคัดลอกและวางนั้นจะไม่ขยายการไฮไลต์ไปถึงและผ่านเครื่องหมายทวิภาค</p>
<p>การใช้ตัวเลขช่วยให้มั่นใจได้ว่าการไฮไลต์ด้วยดับเบิลคลิกจะทำงานได้กับโปรแกรมใดๆ ที่รองรับสตริง bech32m โดยทั่วไป (ซึ่งรวมถึงตัวเลขอื่นๆ ด้วย) เลข "1" ถูกเลือกเพราะสตริง bech32 ไม่ได้ใช้เลข 1 ในกรณีอื่น เพื่อป้องกันการแปลงโดยไม่ตั้งใจระหว่างเลข "1" กับตัวอักษรพิมพ์เล็ก "l"</p>
<p>และส่วนอื่นของ bech32m address เรียกว่า "ส่วนข้อมูล" (data part) ซึ่งประกอบด้วยสามองค์ประกอบ:</p>
<ul>
<li><strong>Witness version:</strong> ไบต์ถัดไปหลังจากตัวคั่นตัวอักษรนี้แทนเวอร์ชันของ segwit ตัวอักษร "q" คือการเข้ารหัสของ "0" สำหรับ segwit v0 ซึ่งเป็นเวอร์ชันแรกของ segwit ที่มีการแนะนำที่อยู่ bech32 ตัวอักษร "p" คือการเข้ารหัสของ "1" สำหรับ segwit v1 (หรือเรียกว่า taproot) ซึ่งเริ่มมีการใช้งาน bech32m มีเวอร์ชันที่เป็นไปได้ทั้งหมด 17 เวอร์ชันของ segwit และสำหรับ Bitcoin จำเป็นต้องให้ไบต์แรกของส่วนข้อมูล bech32m ถอดรหัสเป็นตัวเลข 0 ถึง 16 (รวมทั้งสองค่า)</li>
<li><strong>Witness program:</strong> คือตำแหน่งหลังจาก witnessversion ตั้งแต่ตำแหน่ง 2 ถึง 40 Byte สำหรับ segwit v0 นี้ต้องมีความยาว 20 หรือ 32 Byte ไม่สามารถ<br>ffมีขนาดอื่นได้ สำหรับ segwit v1 ความยาวเดียวที่ถูกกำหนดไว้ ณ เวลาที่เขียนนี้คือ 32 ไบต์ แต่อาจมีการกำหนดความยาวอื่น ๆ ได้ในภายหลัง</li>
<li><strong>Checksum:</strong> มีความยาว 6 ตัวอักษร โดยส่วนนี้ถูกสร้างขึ้นโดยใช้รหัส BCH ซึ่งเป็นประเภทของรหัสแก้ไขข้อผิดพลาด (error corection code) (แต่อย่างไรก็ตาม สำหรับ address บิตคอยน์ เราจะเห็นในภายหลังว่าเป็นสิ่งสำคัญที่จะใช้ checksum เพื่อการตรวจจับข้อผิดพลาดเท่านั้น—ไม่ใช่การแก้ไข</li>
</ul>
<p>ในส่วนต่อไปหลังจากนี้เราจะลองสร้าง address แบบ bech32 และ bech32m สำหรับตัวอย่างทั้งหมดต่อไปนี้ เราจะใช้โค้ดอ้างอิง bech32m สำหรับ Python</p>
<p>เราจะเริ่มด้วยการสร้างสคริปต์เอาต์พุตสี่ตัว หนึ่งตัวสำหรับแต่ละเอาต์พุต segwit ที่แตกต่างกันที่ใช้ในช่วงเวลาของการเผยแพร่ บวกกับอีกหนึ่งตัวสำหรับเวอร์ชัน segwit ในอนาคตที่ยังไม่มีความหมายที่กำหนดไว้ สคริปต์เหล่านี้แสดงอยู่ในตารางข้างล่างนี้<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641245046-YAKIHONNES3.png" alt="image"></p>
<p>สำหรับเอาต์พุต P2WPKH witness program มีการผูก commitment ที่สร้างขึ้นในลักษณะเดียวกันกับ P2PKH ที่เห็นใน Legacy Addresses for P2PKH โดย public key ถูกส่งเข้าไปในฟังก์ชันแฮช SHA256 ไดเจสต์ขนาด 32 ไบต์ที่ได้จะถูกส่งเข้าไปในฟังก์ชันแฮช RIPEMD-160 ไดเจสต์ของฟังก์ชันนั้น จะถูกวางไว้ใน witness program</p>
<p>สำหรับเอาต์พุตแบบ pay to witness script hash (P2WSH) เราไม่ได้ใช้อัลกอริทึม P2SH แต่เราจะนำสคริปต์ ส่งเข้าไปในฟังก์ชันแฮช SHA256 และใช้ไดเจสต์ขนาด 32 ไบต์ของฟังก์ชันนั้นใน witness program สำหรับ P2SH ไดเจสต์ SHA256 จะถูกแฮชอีกครั้งด้วย RIPEMD-160 ซึ่งแน่นอนว่าอาจจะไม่ปลอดภัย ในบางกรณี สำหรับรายละเอียด ดูที่ P2SH Collision Attacks ผลลัพธ์ของการใช้ SHA256 โดยไม่มี RIPEMD-160 คือ การผูกพันแบบ P2WSH มีขนาด 32 ไบต์ (256 บิต) แทนที่จะเป็น 20 ไบต์ (160 บิต)</p>
<p>สำหรับเอาต์พุตแบบ pay-to-taproot (P2TR) witness program คือจุดบนเส้นโค้ง secp256k1 มันอาจเป็น public key แบบธรรมดา แต่ในกรณีส่วนใหญ่มันควรเป็น public key ที่ผูกพันกับข้อมูลเพิ่มเติมบางอย่าง เราจะเรียนรู้เพิ่มเติมเกี่ยวกับการผูกพันนั้นในหัวข้อของ taproot</p>
<p>สำหรับตัวอย่างของเวอร์ชัน segwit ในอนาคต เราเพียงแค่ใช้หมายเลขเวอร์ชัน segwit ที่สูงที่สุดที่เป็นไปได้ (16) และ witness program ที่มีขนาดเล็กที่สุดที่อนุญาต (2 ไบต์) โดยมีค่าเป็นศูนย์ (null value)</p>
<p>เมื่อเรารู้หมายเลขเวอร์ชันและ witness program แล้ว เราสามารถแปลงแต่ละอย่างให้เป็น bech32 address ได้ โดยการใช้ไลบรารีอ้างอิง bech32m สำหรับ Python เพื่อสร้าง address เหล่านั้นอย่างรวดเร็ว และจากนั้นมาดูอย่างละเอียดว่าเกิดอะไรขึ้น:</p>
<pre><code>$ github=" https://raw.githubusercontent.com"
$ wget $github/sipa/bech32/master/ref/python/segwit_addr.py
$ python
&gt;&gt;&gt; from segwit_addr import *
&gt;&gt;&gt; from binascii import unhexlify
&gt;&gt;&gt; help(encode)
encode(hrp, witver, witprog)
    Encode a segwit address.
&gt;&gt;&gt; encode('bc', 0, unhexlify('2b626ed108ad00a944bb2922a309844611d25468'))
'bc1q9d3xa5gg45q2j39m9y32xzvygcgay4rgc6aaee'
&gt;&gt;&gt; encode('bc', 0,
unhexlify('648a32e50b6fb7c5233b228f60a6a2ca4158400268844c4bc295ed5e8c3d626f'))
'bc1qvj9r9egtd7mu2gemy28kpf4zefq4ssqzdzzycj7zjhk4arpavfhsct5a3p'
&gt;&gt;&gt; encode('bc', 1,
unhexlify('2ceefa5fa770ff24f87c5475d76eab519eda6176b11dbe1618fcf755bfac5311'))
'bc1p9nh05ha8wrljf7ru236awm4t2x0d5ctkkywmu9sclnm4t0av2vgs4k3au7'
&gt;&gt;&gt; encode('bc', 16, unhexlify('0000'))
'bc1sqqqqkfw08p'
</code></pre>
<p>หากเราเปิดไฟล์ segwit_addr.py และดูว่าโค้ดกำลังทำอะไร สิ่งแรกที่เราจะสังเกตเห็นคือความแตกต่างเพียงอย่างเดียวระหว่าง bech32 (ที่ใช้สำหรับ segwit v0) และ bech32m (ที่ใช้สำหรับเวอร์ชัน segwit รุ่นหลัง) คือค่าคงที่:</p>
<pre><code>BECH32_CONSTANT = 1
BECH32M_CONSTANT = 0x2bc830a3
</code></pre>
<p>และในส่วนต่อไป เราจะเห็นโค้ดที่สร้าง checksum ในขั้นตอนสุดท้ายของการสร้าง checksum ค่าคงที่ที่เหมาะสมถูกรวมเข้ากับข้อมูลอื่น ๆ โดยใช้การดำเนินการ xor ค่าเดียวนั้นคือความแตกต่างเพียงอย่างเดียวระหว่าง bech32 และ bech32m</p>
<p>เมื่อสร้าง checksum แล้ว อักขระ 5 บิตแต่ละตัวในส่วนข้อมูล (รวมถึง witness version, witness program และ checksum) จะถูกแปลงเป็นตัวอักษรและตัวเลข</p>
<p>สำหรับการถอดรหัสกลับเป็นสคริปต์เอาต์พุต เราทำงานย้อนกลับ ลองใช้ไลบรารีอ้างอิงเพื่อถอดรหัส address สอง address ของเรา:</p>
<pre><code>&gt;&gt;&gt; help(decode)
decode(hrp, addr)
    Decode a segwit address.
&gt;&gt;&gt; _ = decode("bc", "bc1q9d3xa5gg45q2j39m9y32xzvygcgay4rgc6aaee")
&gt;&gt;&gt;  _[0], bytes(_[1]).hex()
(0, '2b626ed108ad00a944bb2922a309844611d25468')
&gt;&gt;&gt; _ = decode("bc",
        "bc1p9nh05ha8wrljf7ru236awm4t2x0d5ctkkywmu9sclnm4t0av2vgs4k3au7")
&gt;&gt;&gt; _[0], bytes(_[1]).hex()
(1, '2ceefa5fa770ff24f87c5475d76eab519eda6176b11dbe1618fcf755bfac5311')
</code></pre>
<p>เราได้รับทั้ง witness version และ witness program กลับมา สิ่งเหล่านี้สามารถแทรกลงในเทมเพลตสำหรับสคริปต์เอาต์พุตของเรา:</p>
<pre><code>&lt;version&gt; &lt;program&gt;
</code></pre>
<p>ตัวอย่างเช่น:</p>
<pre><code>OP_0 2b626ed108ad00a944bb2922a309844611d25468
OP_1 2ceefa5fa770ff24f87c5475d76eab519eda6176b11dbe1618fcf755bfac5311
</code></pre>
<blockquote>
<p>คำเตือน: ข้อผิดพลาดที่อาจเกิดขึ้นที่ควรระวังคือ witness version ที่มีค่า 0 ใช้สำหรับ OP_0 ซึ่งใช้ไบต์ 0x00—แต่เวอร์ชัน witness ที่มีค่า 1 ใช้ OP_1 ซึ่งเป็นไบต์ 0x51 เวอร์ชัน witness 2 ถึง 16 ใช้ไบต์ 0x52 ถึง 0x60 ตามลำดับ</p>
</blockquote>
<p>เมื่อทำการเขียนโค้ดเพื่อเข้ารหัสหรือถอดรหัส bech32m เราขอแนะนำอย่างยิ่งให้คุณใช้เวกเตอร์ทดสอบ (test vectors) ที่มีให้ใน BIP350 เราขอให้คุณตรวจสอบให้แน่ใจว่าโค้ดของคุณผ่านเวกเตอร์ทดสอบที่เกี่ยวข้องกับการจ่ายเงินให้กับเวอร์ชัน segwit ในอนาคตที่ยังไม่ได้รับการกำหนด สิ่งนี้จะช่วยให้ซอฟต์แวร์ของคุณสามารถใช้งานได้อีกหลายปีข้างหน้า แม้ว่าคุณอาจจะไม่สามารถเพิ่มการรองรับคุณสมบัติใหม่ ๆ ของบิตคอยน์ได้ทันทีที่คุณสมบัตินั้น ๆ เริ่มใช้งานได้</p>
<h3>Private Key Formats</h3>
<p>private key สามารถถูกแสดงได้ในหลาย ๆ รูปแบบที่ต่างกันซึ่งสามารถแปลงเป็นตัวเลขขนาด 256 bit ชุดเดียวกันได้ ดังที่เราจะแสดงให้ดูในตารางข้างล่างนี้ รูปแบบที่แตกต่างกันถูกใช้ในสถานการณ์ที่ต่างกัน รูปแบบเลขฐานสิบหก (Hexadecimal) และรูปแบบไบนารี (raw binary) ถูกใช้ภายในซอฟต์แวร์และแทบจะไม่แสดงให้ผู้ใช้เห็น WIF ถูกใช้สำหรับการนำเข้า/ส่งออกกุญแจระหว่างกระเป๋าเงินและมักใช้ในการแสดงกุญแจส่วนตัวแบบ QR code</p>
<h4>รูปแบบของ private key ในปัจจุบัน</h4>
<p>ซอฟต์แวร์กระเป๋าเงินบิตคอยน์ในยุคแรกได้สร้าง private key อิสระอย่างน้อยหนึ่งดอกเมื่อกระเป๋าเงินของผู้ใช้ใหม่ถูกเริ่มต้น เมื่อชุดกุญแจเริ่มต้นถูกใช้ทั้งหมดแล้ว กระเป๋าเงินอาจสร้าง private key เพิ่มเติม  private key แต่ละดอกสามารถส่งออกหรือนำเข้าได้ ทุกครั้งที่มีการสร้างหรือนำเข้า private key ใหม่ จะต้องมีการสร้างการสำรองข้อมูลกระเป๋าเงินใหม่ด้วย</p>
<p>กระเป๋าเงินบิตคอยน์ในยุคหลังเริ่มใช้กระเป๋าเงินแบบกำหนดได้ (deterministic wallets) ซึ่ง private key ทั้งหมดถูกสร้างจาก seed เพียงค่าเดียว กระเป๋าเงินเหล่านี้จำเป็นต้องสำรองข้อมูลเพียงครั้งเดียวเท่านั้นสำหรับการใช้งานบนเชนทั่วไป แต่อย่างไรก็ตาม หากผู้ใช้ส่งออก private key เพียงดอกเดียวจากกระเป๋าเงินเหล่านี้ และผู้โจมตีได้รับกุญแจนั้นรวมถึงข้อมูลที่ไม่ใช่ข้อมูลส่วนตัวบางอย่างเกี่ยวกับกระเป๋าเงิน พวกเขาอาจสามารถสร้างกุญแจส่วนตัวใด ๆ ในกระเป๋าเงินได้—ทำให้ผู้โจมตีสามารถขโมยเงินทั้งหมดในกระเป๋าเงินได้ นอกจากนี้ ยังไม่สามารถนำเข้ากุญแจสู่กระเป๋าเงินแบบกำหนดได้ นี่หมายความว่าแทบไม่มีกระเป๋าเงินสมัยใหม่ที่รองรับความสามารถในการส่งออกหรือนำเข้ากุญแจเฉพาะดอก ข้อมูลในส่วนนี้มีความสำคัญหลัก ๆ สำหรับผู้ที่ต้องการความเข้ากันได้กับกระเป๋าเงินบิตคอยน์ในยุคแรก ๆ</p>
<p>รูปแบบของ private key (รูปแบบการเข้ารหัส)<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641438678-YAKIHONNES3.png" alt="image"><br>private key เดียวกันในแต่ละ format<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641472205-YAKIHONNES3.png" alt="image"><br>รูปแบบการแสดงผลทั้งหมดเหล่านี้เป็นวิธีต่างๆ ในการแสดงเลขจำนวนเดียวกัน private key เดียวกัน พวกมันดูแตกต่างกัน แต่รูปแบบใดรูปแบบหนึ่งสามารถแปลงไปเป็นรูปแบบอื่นได้อย่างง่ายดาย</p>
<h3>Compressed Private Keys</h3>
<p>คำว่า compressed private key ที่ใช้กันทั่วไปนั้นเป็นคำที่เรียกผิด เพราะเมื่อ private key ถูกส่งออกไปในรูปแบบ WIF-compressed มันจะมีความยาวมากกว่า private key แบบ uncompressed 1 Byte (เลข 01 ในช่อง Hex-compressed ในตารางด้านล่างนี้) ซึ่งบ่งบอกว่า private key ตัวนี้ มาจากกระเป๋าเงินรุ่นใหม่และควรใช้เพื่อสร้าง compressed public key เท่านั้น</p>
<p>private key เองไม่ได้ถูกบีบอัดและไม่สามารถบีบอัดได้ คำว่า compressed private key จริงๆ แล้วหมายถึง " private key ซึ่งควรใช้สร้าง compressed public key เท่านั้น" ในขณะที่ uncompressed private key จริงๆ แล้วหมายถึง “private key ซึ่งควรใช้สร้าง uncompressed public key เท่านั้น” คุณควรใช้เพื่ออ้างถึงรูปแบบการส่งออกเป็น "WIF-compressed" หรือ "WIF" เท่านั้น และไม่ควรอ้างถึง private key ว่า "บีบอัด" เพื่อหลีกเลี่ยงความสับสนต่อไป</p>
<p>ตารางนี้แสดงกุญแจเดียวกันที่ถูกเข้ารหัสในรูปแบบ WIF และ WIF-compressed</p>
<p>ตัวอย่าง: กุญแจเดียวกัน แต่รูปแบบต่างกัน<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641542538-YAKIHONNES3.png" alt="image"></p>
<p>สังเกตว่ารูปแบบ Hex-compressed มีไบต์เพิ่มเติมหนึ่งไบต์ที่ท้าย (01 ในเลขฐานสิบหก) ในขณะที่คำนำหน้าเวอร์ชันการเข้ารหัสแบบ base58 เป็นค่าเดียวกัน (0x80) สำหรับทั้งรูปแบบ WIF และ WIF-compressed การเพิ่มหนึ่งไบต์ที่ท้ายของตัวเลขทำให้อักขระตัวแรกของการเข้ารหัสแบบ base58 เปลี่ยนจาก 5 เป็น K หรือ L</p>
<p>คุณสามารถคิดถึงสิ่งนี้เหมือนกับความแตกต่างของการเข้ารหัสเลขฐานสิบระหว่างตัวเลข 100 และตัวเลข 99 ในขณะที่ 100 มีความยาวมากกว่า 99 หนึ่งหลัก มันยังมีคำนำหน้าเป็น 1 แทนที่จะเป็นคำนำหน้า 9 เมื่อความยาวเปลี่ยนไป มันส่งผลต่อคำนำหน้า ในระบบ base58 คำนำหน้า 5 เปลี่ยนเป็น K หรือ L เมื่อความยาวของตัวเลขเพิ่มขึ้นหนึ่งไบต์</p>
<blockquote>
<p>TIPจากหลาม: ผมว่าเขาเขียนย่อหน้านี้ไม่ค่อยรู้เรื่อง แต่ความหมายมันจะประมาณว่า เหมือนถ้าเราต้องการเขียนเลข 100 ในฐาน 10 เราต้องใช้สามตำแหน่ง 100 แต่ถ้าใช้ฐาน 16 เราจะใช้แค่ 2 ตำแหน่งคือ 64 ซึ่งมีค่าเท่ากัน</p>
</blockquote>
<p>ถ้ากระเป๋าเงินบิตคอยน์สามารถใช้ compressed public key ได้ มันจะใช้ในทุกธุรกรรม private key ในกระเป๋าเงินจะถูกใช้เพื่อสร้างจุด public key บนเส้นโค้ง ซึ่งจะถูกบีบอัด compressed public key จะถูกใช้เพื่อสร้าง address และ address เหล่านี้จะถูกใช้ในธุรกรรม เมื่อส่งออก private key จากกระเป๋าเงินใหม่ที่ใช้ compressed public key WIF จะถูกปรับเปลี่ยน โดยเพิ่มต่อท้ายขนาด 1 ไบต์ 01 ให้กับ private key ที่ถูกเข้ารหัสแบบ base58check ที่ได้จะเรียกว่า "WIF-compressed" และจะขึ้นต้นด้วยอักษร K หรือ L แทนที่จะขึ้นต้นด้วย "5" เหมือนกับกรณีของคีย์ที่เข้ารหัสแบบ WIF (ไม่บีบอัด) จากกระเป๋าเงินรุ่นเก่า</p>
<h3>Advanced Keys and Addresses</h3>
<p>ในส่วนต่อไปนี้ เราจะดูรูปแบบของคีย์และ address เช่น vanity addresses และ paper wallets </p>
<h4>vanity addresses</h4>
<p>vanity addresses หรือ addresses แบบกำหนดเอง คือ address ที่มีข้อความที่มนุษย์อ่านได้และสามารถใช้งานได้จริง ตัวอย่างเช่น 1LoveBPzzD72PUXLzCkYAtGFYmK5vYNR33 อย่างที่เห็นว่ามันเป็น address ที่ถูกต้องซึ่งมีตัวอักษรเป็นคำว่า Love เป็นตัวอักษร base58 สี่ตัวแรก addresses แบบกำหนดเองต้องอาศัยการสร้างและทดสอบ private key หลายพันล้านตัวจนกว่าจะพบ address ที่มีรูปแบบตามที่ต้องการ แม้ว่าจะมีการปรับปรุงบางอย่างในอัลกอริทึมการสร้าง addresses แบบกำหนดเอง แต่กระบวนการนี้ต้องใช้การสุ่มเลือก private key มาสร้าง public key และนำไปสร้าง address และตรวจสอบว่าตรงกับรูปแบบที่ต้องการหรือไม่ โดยทำซ้ำหลายพันล้านครั้งจนกว่าจะพบที่ตรงกัน</p>
<p>เมื่อพบ  address ที่ตรงกับรูปแบบที่ต้องการแล้ว  private key ที่ใช้สร้าง address นั้นสามารถใช้โดยเจ้าของเพื่อใช้จ่ายบิตคอยน์ได้เหมือนกับ address อื่น ๆ ทุกประการ address ที่กำหนดเองไม่ได้มีความปลอดภัยน้อยกว่าหรือมากกว่าที่ address ๆ พวกมันขึ้นอยู่กับการเข้ารหัสเส้นโค้งรูปวงรี (ECC) และอัลกอริทึมแฮชที่ปลอดภัย (SHA) เหมือนกับ address อื่น ๆ คุณไม่สามารถค้นหา private key ของ address ที่ขึ้นต้นด้วยรูปแบบที่กำหนดเองได้ง่ายกว่า address อื่น ๆ</p>
<p>ตัวอย่างเช่น ยูจีเนียเป็นผู้อำนวยการการกุศลเพื่อเด็กที่ทำงานในฟิลิปปินส์ สมมติว่ายูจีเนียกำลังจัดการระดมทุนและต้องการใช้ address ที่กำหนดเองเพื่อประชาสัมพันธ์การระดมทุน ยูจีเนียจะสร้าง address ที่กำหนดเองที่ขึ้นต้นด้วย "1Kids" เพื่อส่งเสริมการระดมทุนเพื่อการกุศลสำหรับเด็ก มาดูกันว่า address ที่กำหนดเองนี้จะถูกสร้างขึ้นอย่างไรและมีความหมายอย่างไรต่อความปลอดภัยของการกุศลของยูจีเนีย</p>
<h4>การสร้าง address ที่กำหนดเอง</h4>
<p>ควรเข้าใจว่า address ของบิตคอยน์เป็นเพียงตัวเลขที่แสดงด้วยสัญลักษณ์ในรูปแบบตัวอักษร base58 เท่านั้น เพราะฉะนั้นแล้ว การค้นหารูปแบบเช่น "1Kids" สามารถมองได้ว่าเป็นการค้นหาที่อยู่ในช่วงตั้งแต่ 1Kids11111111111111111111111111111 ถึง 1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzz มีประมาณ 5829 (ประมาณ 1.4 × 1051) address ในช่วงนั้น ทั้งหมดขึ้นต้นด้วย "1Kids" ตารางด้านล่างนี้แสดงช่วงของ address ที่มีคำนำหน้า 1Kids<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641701955-YAKIHONNES3.png" alt="image">ลองดูรูปแบบ "1Kids" ในรูปของตัวเลขและดูว่าเราอาจพบรูปแบบนี้ใน bitcoin address บ่อยแค่ไหน โดยตารางข้างล่างนี้แสดงให้เห็นถีงคอมพิวเตอร์เดสก์ท็อปทั่วไปที่ไม่มีฮาร์ดแวร์พิเศษสามารถค้นหาคีย์ได้ประมาณ 100,000 คีย์ต่อวินาที</p>
<p>ความถี่ของ address ที่กำหนดเอง (1KidsCharity) และเวลาค้นหาเฉลี่ยบนคอมพิวเตอร์เดสก์ท็อป</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641750817-YAKIHONNES3.png" alt="image"></p>
<p>ดังที่เห็นได้ ยูจีเนียคงไม่สามารถสร้าง address แบบกำหนดเอง "1KidsCharity" ได้ในเร็ว ๆ นี้ แม้ว่าเธอจะมีคอมพิวเตอร์หลายพันเครื่องก็ตาม ทุกตัวอักษรที่เพิ่มขึ้นจะเพิ่มความยากขึ้น 58 เท่า รูปแบบที่มีมากกว่า 7 ตัวอักษรมักจะถูกค้นพบโดยฮาร์ดแวร์พิเศษ เช่น คอมพิวเตอร์เดสก์ท็อปที่สร้างขึ้นเป็นพิเศษที่มีหน่วยประมวลผลกราฟิก (GPUs) หลายตัว การค้นหา address แบบกำหนดเองบนระบบ GPU เร็วกว่าบน CPU ทั่วไปหลายเท่า</p>
<p>อีกวิธีหนึ่งในการหา address แบบกำหนดเองคือการจ้างงานไปยังกลุ่มคนขุด vanity addresses กลุ่มคนขุดvanity addresses เป็นบริการที่ให้ผู้ที่มีฮาร์ดแวร์ที่เร็วได้รับบิตคอยน์จากการค้นหา vanity addresses ให้กับผู้อื่น ยูจีเนียสามารถจ่ายค่าธรรมเนียมเพื่อจ้างงานการค้นหา vanity addresses ที่มีรูปแบบ 7 ตัวอักษรและได้ผลลัพธ์ในเวลาเพียงไม่กี่ชั่วโมงแทนที่จะต้องใช้ CPU ค้นหาเป็นเดือน ๆ</p>
<p>การสร้างที่ address แบบกำหนดเองเป็นการใช้วิธีการแบบ brute-force (ลองทุกความเป็นไปได้): ลองใช้คีย์สุ่ม ตรวจสอบ address ที่ได้ว่าตรงกับรูปแบบที่ต้องการหรือไม่ และทำซ้ำจนกว่าจะสำเร็จ</p>
<h4>ความปลอดภัยและความเป็นส่วนตัวของ address แบบกำหนดเอง</h4>
<p>address แบบกำหนดเองเคยเป็นที่นิยมในช่วงแรก ๆ ของบิตคอยน์ แต่แทบจะหายไปจากการใช้งานทั้งหมดในปี 2023 มีสาเหตุที่น่าจะเป็นไปได้สองประการสำหรับแนวโน้มนี้:</p>
<ul>
<li><p><strong>Deterministic wallets:</strong> ดังที่เราเห็นในพาร์ทของการกู้คืน การที่จะสำรองคีย์ทุกตัวในกระเป๋าเงินสมัยใหม่ส่วนใหญ่นั้น ทำเพียงแค่จดคำหรือตัวอักษรไม่กี่ตัว ซึ่งนี่เป็นผลจากการสร้างคีย์ทุกตัวในกระเป๋าเงินจากคำหรือตัวอักษรเหล่านั้นโดยใช้อัลกอริทึมแบบกำหนดได้ จึงไม่สามารถใช้ address แบบกำหนดเองกับ Deterministic wallets ได้ เว้นแต่ผู้ใช้จะสำรองข้อมูลเพิ่มเติมสำหรับ address แบบกำหนดเองทุก address ที่พวกเขาสร้าง ในทางปฏิบัติแล้วกระเป๋าเงินส่วนใหญ่ที่ใช้การสร้างคีย์แบบกำหนดได้ โดยไม่อนุญาตให้นำเข้าคีย์ส่วนตัวหรือการปรับแต่งคีย์จากโปรแกรมสร้าง address ที่กำหนดเอง</p>
</li>
<li><p><strong>การหลีกเลี่ยงการใช้ address ซ้ำซ้อน:</strong> การใช้ address แบบกำหนดเองเพื่อรับการชำระเงินหลายครั้งไปยัง address เดียวกันจะสร้างความเชื่อมโยงระหว่างการชำระเงินทั้งหมดเหล่านั้น นี่อาจเป็นที่ยอมรับได้สำหรับยูจีเนียหากองค์กรไม่แสวงหาผลกำไรของเธอจำเป็นต้องรายงานรายได้และค่าใช้จ่ายต่อหน่วยงานภาษีอยู่แล้ว แต่อย่างไรก็ตาม มันยังลดความเป็นส่วนตัวของคนที่จ่ายเงินให้ยูจีเนียหรือรับเงินจากเธอด้วย ตัวอย่างเช่น อลิซอาจต้องการบริจาคโดยไม่เปิดเผยตัวตน และบ็อบอาจไม่ต้องการให้ลูกค้ารายอื่นของเขารู้ว่าเขาให้ราคาส่วนลดแก่ยูจีเนีย</p>
</li>
</ul>
<p>เราไม่คาดว่าจะเห็น address แบบกำหนดเองมากนักในอนาคต เว้นแต่ปัญหาที่กล่าวมาก่อนหน้านี้จะได้รับการแก้ไข</p>
<h3>Paper Wallets</h3>
<p>paper wallet หรือก็คือ private key ที่พิมพ์ลงในกระดาษ และโดยทั่วไปแล้วมักจะมีข้อมูลของ public key หรือ address บนกระดาษนั้นด้วยแม้ว่าจริง ๆ แล้วมันจะสามารถคำนวณได้ด้วย private key ก็ตาม</p>
<blockquote>
<p>คำเตือน: paper wallet เป็นเทคโนโลยีที่ล้าสมัยแล้วและอันตรายสำหรับผู้ใช้ส่วนใหญ่ เพราะเป็นเรื่องยากที่จะสร้างมันอย่างปลอดภัย โดยเฉพาะอย่างยิ่งความเป็นไปได้ที่โค้ดที่ใช้สร้างอาจถูกแทรกแซงด้วยผู้ไม่ประสงค์ดี และอาจจะทำให้ผู้ใช้โดนขโมยบิตคอยน์ทั้งหมดไปได้ paper wallet ถูกแสดงที่นี่เพื่อวัตถุประสงค์ในการให้ข้อมูลเท่านั้นและไม่ควรใช้สำหรับเก็บบิตคอยน์</p>
</blockquote>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0410.png" alt="image"></p>
<p>paper wallet ได้ถูกออกแบบมาเพื่อเป็นของขวัญและมีธีมตามฤดูกาล เช่น คริสต์มาสและปีใหม่ ส่วนเหตุผลอื่น ๆ ถูกออกแบบเพื่อการเก็บรักษาในตู้นิรภัยของธนาคารหรือตู้เซฟโดยมี private key ถูกซ่อนไว้ในบางวิธี ไม่ว่าจะด้วยสติกเกอร์แบบขูดที่ทึบแสงหรือพับและปิดผนึกด้วยแผ่นฟอยล์กันการงัดแงะ ส่วนการออกแบบอื่น ๆ มีสำเนาเพิ่มเติมของคีย์และ address ในรูปแบบของตอนฉีกที่แยกออกได้คล้ายกับตั๋ว ช่วยให้คุณสามารถเก็บสำเนาหลายชุดเพื่อป้องกันจากไฟไหม้ น้ำท่วม หรือภัยพิบัติทางธรรมชาติอื่น ๆ</p>
<p>จากการออกแบบเดิมของบิตคอยน์ที่เน้น public key ไปจนถึง address และสคริปต์สมัยใหม่อย่าง bech32m และ pay to taproot—และแม้แต่การอัพเกรดบิตคอยน์ในอนาคต—คุณได้เรียนรู้วิธีที่โปรโตคอลบิตคอยน์อนุญาตให้ผู้จ่ายเงินระบุกระเป๋าเงินที่ควรได้รับการชำระเงินของพวกเขา แต่เมื่อเป็นกระเป๋าเงินของคุณเองที่รับการชำระเงิน คุณจะต้องการความมั่นใจว่าคุณจะยังคงเข้าถึงเงินนั้นได้แม้ว่าจะเกิดอะไรขึ้นกับข้อมูลกระเป๋าเงินของคุณ ในบทต่อไป เราจะดูว่ากระเป๋าเงินบิตคอยน์ถูกออกแบบอย่างไรเพื่อปกป้องเงินทุนจากภัยคุกคามหลากหลายรูปแบบ</p>
]]></content:encoded>
      <itunes:author><![CDATA[Learning_BTC&NOSTR]]></itunes:author>
      <itunes:summary><![CDATA[<h2>Keys and Addresses</h2>
<p>อลิซต้องการจ่ายเงินให้กับบ๊อบแต่โหนดของบิตคอยน์ในระบบหลายพันโหนดจะตรวจสอบธุรกรรมของเธอ โดยไม่รู้ว่าอลิซหรือบ๊อบเป็นใคร ละเราต้องการรักษาความเป็นส่วนตัวของพวกเขาไว้เช่นนี้ อลิซจำเป็นต้องสื่อสารว่าบ๊อบควรได้รับบิตคอยน์บางส่วนของเธอโดยไม่เชื่อมโยงแง่มุมใด ๆ ของธุรกรรมนั้นกับตัวตนในโลกจริงของบ๊อบ หรือกับการชำระเงินด้วยบิตคอยน์ครั้งอื่น ๆ ที่บ๊อบได้รับ อลิซใช้ต้องทำให้มั่นใจว่ามีเพียแค่บ๊อบเท่านั้นที่สามารถใช้จ่ายบิตคอยน์ที่เขาได้รับต่อไปได้</p>
<p>ในบิตคอยน์ไวท์เปเปอร์ได้อธิบายถึงแผนการที่เรียบง่ายมากสำหรับการบรรลุเป้าหมายเหล่านั้น ดังที่แสดงในรูปด้านล่างนี้ </p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_aain01.png" alt="image"></p>
<p>ตัวของผู้รับอย่างบ๊อบเองจะได้รับบิตคอยน์ไปยัง public key ของเขาที่ถูกลงนามโดยผู้จ่ายอย่างอลิซ โดยบิตคอยน์ที่อลิซนำมาจ่ายนั้นก็ได้รับมาจากที่ใครสักคนส่งมาที่ public key ของเธอ และเธอก็ใช้ private key ของเธอในการลงนามเพื่อสร้างลายเซ็นของเธอและโหนดต่าง ๆ ของบิตคอยน์จะทำการตรวจสอบว่าลายเซ็นของอลิซผูกมัดกับเอาต์พุตของฟังก์ชันแฮชซึ่งตัวมันเองผูกมัดกับ public key ของบ๊อบและรายละเอียดธุรกรรมอื่นๆ</p>
<p>ในบทนี้เราจะพิจารณาpublic key  private key  Digital signatrue และ hash function จากนั้นใช้ทั้งหมดนี้ร่วมกันเพื่ออธิบาย address ที่ใช้โดยซอฟต์แวร์บิตคอยน์สมัยใหม่</p>
<h3>Public Key Cryptography (การเข้ารหัสของ public key)</h3>
<p>ระบบเข้ารหัสของ public key ถูกคิดค้นขึ้นในทศวรรษ 1970 มาจากรากฐานทางคณิตศาสตร์สำหรับความปลอดภัยของคอมพิวเตอร์และข้อมูลสมัยใหม่</p>
<p>นับตั้งแต่การคิดค้นระบบเข้ารหัส public key ได้มีการค้นพบฟังก์ชันทางคณิตศาสตร์ที่เหมาะสมหลายอย่าง เช่น การยกกำลังของจำนวนเฉพาะและการคูณของเส้นโค้งวงรี โดยฟังก์ชันทางคณิตศาสตร์เหล่านี้สามารถคำนวณได้ง่ายในทิศทางหนึ่ง แต่เป็นไปไม่ได้ที่จะคำนวณในทิศทางตรงกันข้ามโดยใช้คอมพิวเตอร์และอัลกอริทึมที่มีอยู่ในปัจจุบัน จากฟังก์ชันทางคณิตศาสตร์เหล่านี้ การเข้ารหัสลับช่วยให้สามารถสร้างลายเซ็นดิจิทัลที่ไม่สามารถปลอมแปลงได้และบิตคอยน์ได้ใช้การบวกและการคูณของเส้นโค้งวงรีเป็นพื้นฐานสำหรับการเข้ารหัสลับของมัน</p>
<p>ในบิตคอยน์ เราสามารถใช้ระบบเข้ารหัส public key เพื่อสร้างคู่กุญแจที่ควบคุมการเข้าถึงบิตคอยน์ คู่กุญแจประกอบด้วย private key และ public key ที่ได้มาจาก private key public keyใช้สำหรับรับเงิน และ private key ใช้สำหรับลงนามในธุรกรรมเพื่อใช้จ่ายเงิน</p>
<p>ความสัมพันธ์ทางคณิตศาสตร์ระหว่าง public key และ private key ที่ช่วยให้ private key สามารถใช้สร้างลายเซ็นบนข้อความได้ ลายเซ็นเหล่านี้สามารถตรวจสอบความถูกต้องกับ public key ได้โดยไม่เปิดเผย private key</p>
<blockquote>
<p>TIP: ในการใช้งานซอฟแวร์กระเป๋าเงินบิตคอยน์บสงอัน จะทำการเก็บ private key และ public key ถูกเก็บไว้ด้วยกันในรูปแบบคู่กุญแจเพื่อความสะดวก แต่อย่างไรก็ตาม public key สามารถคำนวณได้จาก private key ดังนั้นการเก็บเพียง private key เท่านั้นก็เป็นไปได้เช่นกัน</p>
</blockquote>
<p>bitcoin wallet มักจะทำการรวบรวมคู่กุญแต่ละคู่ ซึ่งจะประกอบไปด้วย private key และ public key โดย private key จะเป็นตัวเลขที่ถูกสุ่มเลือกขึ้นมา และเราขะใช้เส้นโค้งวงรี ซึ่งเป็นฟังก์ชันการเข้ารหัสทางเดียว เพื่อสร้าง public key ขึ้นมา</p>
<h3>ทำไมจึงใช้การเข้ารหัสแบบอสมมาตร</h3>
<p>ทำไมการเข้ารหัสแบบอสมมาตรจึงถูกใช้บิตคอยน์? มันไม่ได้ถูกใช้เพื่อ "เข้ารหัส" (ทำให้เป็นความลับ) ธุรกรรม แต่คุณสมบัติที่มีประโยชน์ของการเข้ารหัสแบบอสมมาตรคือความสามารถในการสร้าง ลายเซ็นดิจิทัล private key สามารถนำไปใช้กับธุรกรรมเพื่อสร้างลายเซ็นเชิงตัวเลข ลายเซ็นนี้สามารถสร้างได้เฉพาะโดยผู้ที่มีความเกี่ยวข้องกับ private key เท่านั้น แต่อย่างไรก็ตาม ทุกคนที่สามารถเข้าถึง public key และธุรกรรมสามารถใช้สิ่งเหล่านี้เพื่อ ตรวจสอบ ลายเซ็นได้ คุณสมบัติที่มีประโยชน์นี้ของการเข้ารหัสแบบอสมมาตรทำให้ทุกคนสามารถตรวจสอบลายเซ็นทุกรายการในทุกธุรกรรมได้ ในขณะที่มั่นใจว่าเฉพาะเจ้าของ private key เท่านั้นที่สามารถสร้างลายเซ็นที่ถูกต้องได้</p>
<h3>Private keys</h3>
<p>private key เป็นเพียงตัวเลขที่ถูกสุ่มขึ้น และการควบคุม private key ก็เป็นรากฐานสำคัญที่ทำให้เจ้าชองกุญแจดอกนี้สามารถควบคุมบิตคอยน์ทั้งหมดที่มีความเกี่ยวข้องกับ public key ที่คู่กัน private key นั้นใช้ในการสร้างลายเซ็นดิจิทัลที่ใช้ในการเคลื่อนย้ายบิตคอยน์ เราจำเป็นต้องเก็บ private key ให้เป็นความลับตลอดเวลา เพราะการเปิดเผยมันให้กับบุคคลอื่นนั้นก็เปรียบเสมือนกับการนำอำนาจในการควบคุมบิตคอยน์ไปให้แก่เขา นอกจากนี้ private key ยังจำเป็นต้องได้รับการสำรองข้อมูลและป้องกันจากการสูญหายโดยไม่ตั้งใจ เพราะหากเราได้ทำมันสูญหายไป จะไม่สามารถกู้คืนได้ และบิตคอยน์เหล่านั้นจะถูกปกป้องโดยกุญแจที่หายไปนั้นตลอดกาลเช่นกัน</p>
<blockquote>
<p>TIP: private key ของบิตคอยน์นั้นเป็นเพียงแค่ตัวเลข คุณสามารถสร้างมันได้โดยใช้เพียงเหรียญ ดินสอ และกระดาษ โดยการโยนเหรียญเพียง 256 ครั้งจะทำให้คุณได้เลขฐานสองที่สามารถใช้เป็น private key ของบิตคอยน์ จากนั้นคุณสามารถใช้มันในการคำนวณหา public key แต่อย่างไรก็ตาม โปรดระมัดระวังเกี่ยวกับการเลือใช้วิธีการสุ่มที่ไม่สมบูรณ์ เพราะนั่นอาจลดความปลอดภัยของ private key และบิตคอยน์ที่มัมปกป้องอยู่อย่างมีนัยสำคัญ</p>
</blockquote>
<p>ขั้นตอนแรกและสำคัญที่สุดในการสร้างกุญแจคือการหาแหล่งที่มาของความสุ่มที่ปลอดภัย (ซึ่งเรียกว่า เอนโทรปี) การสร้างกุญแจของบิตคอยน์นั้นเกือบเหมือนกับ "เลือกตัวเลขระหว่าง 1 และ 2^256" ซึ่งวิธีที่แน่นอนที่คุณใช้ในการเลือกตัวเลขนั้นไม่สำคัญตราบใดที่มันไม่สามารถคาดเดาหรือทำซ้ำได้ โดยปกติแล้วซอฟต์แวร์ของบิตคอยน์มักจะใช้ตัวสร้างตัวเลขสุ่มที่มีความปลอดภัยทางการเข้ารหัสเพื่อสร้างเอนโทรปี 256 บิต</p>
<p>สิ่งที่สำคัญในเรื่องนี้คือ private key สามารถเป็นตัวเลขใดๆ ระหว่าง 0 และ n - 1 (รวมทั้งสองค่า) โดยที่ n เป็นค่าคงที่ (n = 1.1578 × 10^77 ซึ่งน้อยกว่า 2^256 เล็กน้อย) ซึ่งกำหนดอยู่ใน elliptic curve ที่ใช้ใน Bitcoin ในการสร้างกุญแจดังกล่าว เราสุ่มเลือกเลขขนาด 256 บิตและตรวจสอบว่ามันน้อยกว่า n ในแง่ของการเขียนโปรแกรม โดยปกติแล้วสิ่งนี้ทำได้โดยการป้อนสตริงของบิตสุ่มที่ใหญ่กว่า ซึ่งรวบรวมจากแหล่งที่มาของความสุ่มที่มีความปลอดภัยทางการเข้ารหัส เข้าไปในอัลกอริทึมแฮช SHA256 ซึ่งจะสร้างค่าขนาด 256 บิตที่สามารถตีความเป็นตัวเลขได้อย่างสะดวก หากผลลัพธ์น้อยกว่า n เราจะได้กุญแจส่วนตัวที่เหมาะสม มิฉะนั้น เราก็เพียงแค่ลองอีกครั้งด้วยตัวเลขสุ่มอื่น</p>
<blockquote>
<p>คำเตือน: อย่าเขียนโค้ดของคุณเองเพื่อสร้างตัวเลขสุ่ม หรือใช้ตัวสร้างตัวเลขสุ่ม "แบบง่าย" ที่มีให้ในภาษาโปรแกรมของคุณ ใช้ตัวสร้างตัวเลขสุ่มเทียมที่มีความปลอดภัยทางการเข้ารหัส (CSPRNG) จากแหล่งที่มีเอนโทรปีเพียงพอ ศึกษาเอกสารของไลบรารีตัวสร้างตัวเลขสุ่มที่คุณเลือกเพื่อให้มั่นใจว่ามีความปลอดภัยทางการเข้ารหัส การใช้งาน CSPRNG ที่ถูกต้องมีความสำคัญอย่างยิ่งต่อความปลอดภัยของกุญแจ</p>
</blockquote>
<p>ต่อไปนี้คือกุญแจส่วนตัว (k) ที่สร้างขึ้นแบบสุ่มซึ่งแสดงในรูปแบบเลขฐานสิบหก (256 บิตแสดงเป็น 64 หลักเลขฐานสิบหก โดยแต่ละหลักคือ 4 บิต):</p>
<pre><code>1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD
</code></pre>
<blockquote>
<p>TIP: จำนวนที่เป็นไปได้ของ private key ทั้งหมดนั้นมีอยู่ 2^256 เป็นตัวเลขที่ใหญ่มากจนยากจะจินตนาการได้ มันมีค่าประมาณ 10^77 (เลข 1 ตามด้วยเลข 0 อีก 77 ตัว) ในระบบเลขฐานสิบ เพื่อให้เข้าใจง่ายขึ้น ลองเปรียบเทียบกับจักรวาลที่เรามองเห็นได้ซึ่งนักวิทยาศาสตร์ประมาณการว่ามีอะตอมทั้งหมดประมาณ 10^80 อะตอม นั่นหมายความว่าช่วงค่าของกุญแจส่วนตัว Bitcoin มีขนาดใกล้เคียงกับจำนวนอะตอมทั้งหมดในจักรวาลที่เรามองเห็นได้</p>
</blockquote>
<h3>การอธิบายเกี่ยวกับวิทยาการเข้ารหัสแบบเส้นโค้งวงรี (Elliptic Curve Cryptography)</h3>
<p>วิทยาการเข้ารหัสแบบเส้นโค้งวงรี (ECC) เป็นประเภทหนึ่งของการเข้ารหัสแบบอสมมาตรหรือ public key ซึ่งอาศัยหลักการของปัญหาลอการิทึมแบบไม่ต่อเนื่อง โดยแสดงออกผ่านการบวกและการคูณบนจุดต่างๆ ของเส้นโค้งวงรี</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0402.png" alt="image"></p>
<p>บิตคอยน์ใช้เส้นโค้งวงรีเฉพาะและชุดค่าคงที่ทางคณิตศาสตร์ ตามที่กำหนดไว้ในมาตรฐานที่เรียกว่า secp256k1 ซึ่งกำหนดโดยสถาบันมาตรฐานและเทคโนโลยีแห่งชาติ (NIST) เส้นโค้ง secp256k1 ถูกกำหนดโดยฟังก์ชันต่อไปนี้ ซึ่งสร้างเส้นโค้งวงรี: y² = (x³ + 7) บนฟิลด์จำกัด (F_p) หรือ y² mod p = (x³ + 7) mod p</p>
<p>โดยที่ mod p (มอดูโลจำนวนเฉพาะ p) แสดงว่าเส้นโค้งนี้อยู่บนฟิลด์จำกัดของอันดับจำนวนเฉพาะ p ซึ่งเขียนได้เป็น F_p โดย p = 2^256 – 2^32 – 2^9 – 2^8 – 2^7 – 2^6 – 2^4 – 1 ซึ่งเป็นจำนวนเฉพาะที่มีค่ามหาศาล</p>
<p>บิตคอยน์ใช้เส้นโค้งวงรีที่ถูกนิยามบนฟิลด์จำกัดของอันดับจำนวนเฉพาะแทนที่จะอยู่บนจำนวนจริง ทำให้มันมีลักษณะเหมือนรูปแบบของจุดที่กระจัดกระจายในสองมิติ ซึ่งทำให้ยากต่อการจินตนาการภาพ อย่างไรก็ตาม คณิตศาสตร์ที่ใช้นั้นเหมือนกับเส้นโค้งวงรีบนจำนวนจริง</p>
<p>ตัวอย่างเช่น การเข้ารหัสลับด้วยเส้นโค้งวงรี: การแสดงภาพเส้นโค้งวงรีบน F(p) โดยที่ p=17 แสดงเส้นโค้งวงรีเดียวกันบนฟิลด์จำกัดของอันดับจำนวนเฉพาะ 17 ที่มีขนาดเล็กกว่ามาก ซึ่งแสดงรูปแบบของจุดบนตาราง</p>
<p>เส้นโค้งวงรี secp256k1 ที่ใช้ในบิตคอยน์สามารถนึกถึงได้ว่าเป็นรูปแบบของจุดที่ซับซ้อนมากกว่าบนตารางที่มีขนาดใหญ่มหาศาลจนยากจะเข้าใจได้</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0403.png" alt="image"></p>
<p>ตัวอย่างเช่น จุด P ที่มีพิกัด (x, y) ต่อไปนี้เป็นจุดที่อยู่บนเส้นโค้ง secp256k1:</p>
<pre><code>P =
(55066263022277343669578718895168534326250603453777594175500187360389116729240,
32670510020758816978083085130507043184471273380659243275938904335757337482424)
</code></pre>
<p>เราสามารถใช้ Python เพื่อยืนยันว่าจุดนี้อยู่บนเส้นโค้งวงรีได้ตามตัวอย่างนี้:<br>ตัวอย่างที่ 1: การใช้ Python เพื่อยืนยันว่าจุดนี้อยู่บนเส้นโค้งวงรี</p>
<pre><code>Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
&gt; p = 115792089237316195423570985008687907853269984665640564039457584007908834671663
&gt; x = 55066263022277343669578718895168534326250603453777594175500187360389116729240
&gt; y = 32670510020758816978083085130507043184471273380659243275938904335757337482424
&gt; (x ** 3 + 7 - y**2) % p
0
</code></pre>
<p>ผลลัพธ์เป็น 0 ซึ่งแสดงว่าจุดนี้อยู่บนเส้นโค้งวงรีจริง เพราะเมื่อแทนค่า x และ y ลงในสมการ y² = (x³ + 7) mod p แล้ว ทั้งสองด้านของสมการมีค่าเท่ากัน</p>
<p>ในคณิตศาสตร์ของเส้นโค้งวงรี มีจุดที่เรียกว่า "จุดที่อนันต์" (point at infinity) ซึ่งมีบทบาทคล้ายกับศูนย์ในการบวก บนคอมพิวเตอร์ บางครั้งจุดนี้แทนด้วย x = y = 0 (ซึ่งไม่เป็นไปตามสมการเส้นโค้งวงรี แต่เป็นกรณีพิเศษที่สามารถตรวจสอบได้ง่าย)</p>
<p>มีตัวดำเนินการ + ที่เรียกว่า "การบวก" ซึ่งมีคุณสมบัติคล้ายกับการบวกแบบดั้งเดิมของจำนวนจริงที่เด็กๆ เรียนในโรงเรียน เมื่อมีจุดสองจุด P1 และ P2 บนเส้นโค้งวงรี จะมีจุดที่สาม P3 = P1 + P2 ซึ่งอยู่บนเส้นโค้งวงรีเช่นกัน</p>
<p>ในเชิงเรขาคณิต จุดที่สาม P3 นี้คำนวณได้โดยการลากเส้นระหว่าง P1 และ P2 เส้นนี้จะตัดกับเส้นโค้งวงรีที่จุดเพิ่มเติมอีกหนึ่งจุดพอดี เรียกจุดนี้ว่า P3' = (x, y) จากนั้นให้สะท้อนกับแกน x เพื่อได้ P3 = (x, -y)</p>
<p>มีกรณีพิเศษบางกรณีที่อธิบายความจำเป็นของ "จุดที่อนันต์":</p>
<ol>
<li>ถ้า P1 และ P2 เป็นจุดเดียวกัน เส้น "ระหว่าง" P1 และ P2 ควรขยายเป็นเส้นสัมผัสกับเส้นโค้ง ณ จุด P1 นี้ เส้นสัมผัสนี้จะตัดกับเส้นโค้งที่จุดใหม่อีกหนึ่งจุดพอดี คุณสามารถใช้เทคนิคจากแคลคูลัสเพื่อหาความชันของเส้นสัมผัส เทคนิคเหล่านี้ใช้ได้อย่างน่าแปลกใจ แม้ว่าเราจะจำกัดความสนใจไว้ที่จุดบนเส้นโค้งที่มีพิกัดเป็นจำนวนเต็มเท่านั้น!</li>
<li>ในบางกรณี (เช่น ถ้า P1 และ P2 มีค่า x เดียวกันแต่ค่า y ต่างกัน) เส้นสัมผัสจะตั้งฉากพอดี ซึ่งในกรณีนี้ P3 = "จุดที่อนันต์"</li>
<li>ถ้า P1 เป็น "จุดที่อนันต์" แล้ว P1 + P2 = P2 ในทำนองเดียวกัน ถ้า P2 เป็นจุดที่อนันต์ แล้ว P1 + P2 = P1 นี่แสดงให้เห็นว่าจุดที่อนันต์มีบทบาทเป็นศูนย์</li>
</ol>
<p>การบวกนี้มีคุณสมบัติเชิงสมาคม (associative) ซึ่งหมายความว่า (A + B) + C = A + (B + C) นั่นหมายความว่าเราสามารถเขียน A + B + C โดยไม่ต้องมีวงเล็บและไม่มีความกำกวม</p>
<p>เมื่อเรานิยามการบวกแล้ว เราสามารถนิยามการคูณในแบบมาตรฐานที่ต่อยอดจากการบวก สำหรับจุด P บนเส้นโค้งวงรี ถ้า k เป็นจำนวนเต็มบวก แล้ว kP = P + P + P + … + P (k ครั้ง) โปรดทราบว่า k บางครั้งถูกเรียกว่า "เลขชี้กำลัง"</p>
<h3>Public Keys</h3>
<p>ในระบบคริปโตกราฟีแบบเส้นโค้งวงรี (Elliptic Curve Cryptography)  public key ถูกคำนวณจาก private key โดยใช้การคูณเส้นโค้งวงรี ซึ่งเป็นกระบวนการที่ไม่สามารถย้อนกลับได้:</p>
<p>K = k × G</p>
<p>โดยที่:</p>
<ul>
<li>k คือ private key</li>
<li>G คือจุดคงที่ที่เรียกว่า จุดกำเนิด (generator point)</li>
<li>K คือ public key</li>
</ul>
<p>การดำเนินการย้อนกลับ ที่เรียกว่า "การหาลอการิทึมแบบไม่ต่อเนื่อง" (finding the discrete logarithm) - คือการคำนวณหา k เมื่อรู้ค่า K - เป็นสิ่งที่ยากมากเทียบเท่ากับการลองค่า k ทุกค่าที่เป็นไปได้ (วิธีการแบบ brute-force)</p>
<p>ความยากของการย้อนกลับนี้คือหลักการความปลอดภัยหลักของระบบ ECC ที่ใช้ในบิตคอยน์ ซึ่งทำให้สามารถเผยแพร่ public key ได้อย่างปลอดภัย โดยที่ไม่ต้องกังวลว่าจะมีใครสามารถคำนวณย้อนกลับเพื่อหา private key ได้</p>
<blockquote>
<p>TIP:การคูณเส้นโค้งวงรีเป็นฟังก์ชันประเภทที่นักเข้ารหัสลับเรียกว่า “ trap door function ”:</p>
</blockquote>
<blockquote>
</blockquote>
<ul>
<li>เป็นสิ่งที่ทำได้ง่ายในทิศทางหนึ่ง</li>
<li>แต่เป็นไปไม่ได้ที่จะทำในทิศทางตรงกันข้าม</li>
</ul>
<blockquote>
<p>คนที่มี private key สามารถสร้าง public key ได้อย่างง่ายดาย และสามารถแบ่งปันกับโลกได้โดยรู้ว่าไม่มีใครสามารถย้อนกลับฟังก์ชันและคำนวณ private key จาก public key ได้ กลวิธีทางคณิตศาสตร์นี้กลายเป็นพื้นฐานสำหรับลายเซ็นดิจิทัลที่ปลอมแปลงไม่ได้และมีความปลอดภัย ซึ่งใช้พิสูจน์การควบคุมเงินบิตคอยน์</p>
</blockquote>
<p>เริ่มต้นด้วยการใช้ private key ในรูปแบบของตัวเลขสุ่ม เราคูณมันด้วยจุดที่กำหนดไว้ล่วงหน้าบนเส้นโค้งที่เรียกว่า จุดกำเนิด (generator point)  เพื่อสร้างจุดอื่นที่อยู่บนเส้นโค้งเดียวกัน ซึ่งคำตอบจะเป็น public key ที่สอดคล้องกัน จุดกำเนิดถูกกำหนดไว้เป็นส่วนหนึ่งของมาตรฐาน secp256k1 และเป็นค่าเดียวกันสำหรับกุญแจทั้งหมดในระบบบิตคอยน์</p>
<p>เนื่องจากจุดกำเนิด G เป็นค่าเดียวกันสำหรับผู้ใช้บิตคอยน์ทุกคน private key (k) ที่คูณกับ G จะได้ public key (K) เดียวกันเสมอ ความสัมพันธ์ระหว่าง k และ K เป็นแบบตายตัวแต่สามารถคำนวณได้ในทิศทางเดียวเท่านั้น คือจาก k ไปยัง K นี่คือเหตุผลที่ public key ของบิตคอยน์ (K) สามารถแบ่งปันกับทุกคนได้โดยไม่เปิดเผย private key (k) ของผู้ใช้</p>
<blockquote>
<p>TIP: private key สามารถแปลงเป็น public key ได้ แต่ public key ไม่สามารถแปลงกลับเป็น private key ได้ เพราะคณิตศาสตร์ที่ใช้ทำงานได้เพียงทิศทางเดียวเท่านั้น</p>
</blockquote>
<p>เมื่อนำการคูณเส้นโค้งวงรีมาใช้งาน เราจะนำ private key (k) ที่สร้างขึ้นก่อนหน้านี้มาคูณกับจุดกำเนิด G เพื่อหา public key (K):</p>
<pre><code>K = 1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD × G
</code></pre>
<p>public key (K) จะถูกกำหนดเป็นจุด K = (x, y) โดยที่:</p>
<pre><code>x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
</code></pre>
<p>เพื่อจะให้เห็นภาพของการคูณจุดด้วยจำนวนเต็มมากขึ้น เราจะใช้เส้นโค้งวงรีที่ง่ายกว่าบนจำนวนจริง (โดยหลักการทางคณิตศาสตร์ยังคงเหมือนกัน) เป้าหมายของเราคือการหาผลคูณ kG ของจุดกำเนิด G ซึ่งเทียบเท่ากับการบวก G เข้ากับตัวเอง k ครั้งติดต่อกัน</p>
<p>ในเส้นโค้งวงรี การบวกจุดเข้ากับตัวเองเทียบเท่ากับการลากเส้นสัมผัสที่จุดนั้นและหาว่าเส้นนั้นตัดกับเส้นโค้งอีกครั้งที่จุดใด จากนั้นจึงสะท้อนจุดนั้นบนแกน x</p>
<p>การเข้ารหัสลับด้วยเส้นโค้งวงรี: การแสดงภาพการคูณจุด G ด้วยจำนวนเต็ม k บนเส้นโค้งวงรี แสดงกระบวนการในการหา G, 2G, 4G เป็นการดำเนินการทางเรขาคณิตบนเส้นโค้งได้ดังนี้</p>
<blockquote>
<p>TIP: ในซอฟแวร์ของบิตคอยน์ส่วนใหญ่ใช้ไลบรารีเข้ารหัสลับ libsecp256k1 เพื่อทำการคำนวณทางคณิตศาสตร์เส้นโค้งวงรี<br><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0404.png" alt="image"></p>
</blockquote>
<h3>Output and Input Scripts</h3>
<p>แม้ว่าภาพประกอบจาก Bitcoin whitepaper ที่แสดงเรื่อง "Transaction chain" จะแสดงให้เห็นว่ามีการใช้ public key และ digital signature โดยตรง แต่ในความเป็นจริงบิตคอยน์เวอร์ชันแรกนั้นมีการส่งการชำระเงินไปยังฟิลด์ที่เรียกว่า output script และมีการใช้จ่ายบิตคอยน์เหล่านั้นโดยได้รับอนุญาตจากฟิลด์ที่เรียกว่า input script ฟิลด์เหล่านี้อนุญาตให้มีการดำเนินการเพิ่มเติมนอกเหนือจาก (หรือแทนที่) การตรวจสอบว่าลายเซ็นสอดคล้องกับ public key หรือไม่ ตัวอย่างเช่น output script สามารถมี public key สองดอกและต้องการลายเซ็นสองลายเซ็นที่สอดคล้องกันในฟิลด์ input script ที่ใช้จ่าย</p>
<p>ในภายหลัง ในหัวข้อ [tx_script] เราจะได้เรียนรู้เกี่ยวกับสคริปต์อย่างละเอียด สำหรับตอนนี้ สิ่งที่เราต้องเข้าใจคือ บิตคอยน์จะถูกรับเข้า output script ที่ทำหน้าที่เหมือน public key และการใช้จ่ายบิตคอยน์จะได้รับอนุญาตโดย input script ที่ทำหน้าที่เหมือนลายเซ็น</p>
<h4>IP Addresses: The Original Address for Bitcoin (P2PK)</h4>
<p>เราได้เห็นแล้วว่าอลิซสามารถจ่ายเงินให้บ็อบโดยการมอบบิตคอยน์บางส่วนของเธอให้กับกุญแจสาธารณะของบ็อบ แต่อลิซจะได้กุญแจสาธารณะของบ็อบมาได้อย่างไร? บ็อบอาจจะให้สำเนากุญแจแก่เธอ แต่ลองดูกุญแจสาธารณะที่เราใช้งานในตัวอย่างที่ผ่านมาอีกครั้ง:</p>
<pre><code>x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
</code></pre>
<blockquote>
<p>TIP จากหลาม: :สังเกตได้ว่า public key มีความยาวมาก ลองจินตนาการว่าบ็อบพยายามอ่านกุญแจนี้ให้อลิซฟังทางโทรศัพท์ คงจะยากมากที่จะอ่านและบันทึกโดยไม่มีข้อผิดพลาด</p>
</blockquote>
<p>แทนที่จะป้อนกุญแจสาธารณะโดยตรง เวอร์ชันแรกของซอฟต์แวร์บิตคอยน์อนุญาตให้ผู้จ่ายเงินป้อนที่อยู่ IP ของผู้รับได้ ตามที่แสดงในหน้าจอการส่งเงินรุ่นแรกของบิตคอยน์ผ่าน The Internet Archive</p>
<p>คุณสมบัตินี้ถูกลบออกในภายหลัง เนื่องจากมีปัญหามากมายในการใช้ที่อยู่ IP แต่คำอธิบายสั้นๆ จะช่วยให้เราเข้าใจได้ดีขึ้นว่าทำไมคุณสมบัติบางอย่างอาจถูกเพิ่มเข้าไปในโปรโตคอลบิตคอยน์</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0405.png" alt="image"></p>
<p>เมื่ออลิซป้อนที่อยู่ IP ของบ็อบในบิตคอยน์เวอร์ชัน 0.1 Full node ของเธอจะทำการเชื่อมต่อกับ full node ของเขาและได้รับ public key ใหม่จากกระเป๋าสตางค์ของบ็อบที่โหนดของเขาไม่เคยให้กับใครมาก่อน การที่เป็น public key ใหม่นี้มีความสำคัญเพื่อให้แน่ใจว่าธุรกรรมต่าง ๆ ที่จ่ายให้บ็อบจะไม่สามารถถูกเชื่อมโยงเข้าด้วยกันโดยคนที่กำลังดูบล็อกเชนและสังเกตเห็นว่าธุรกรรมทั้งหมดจ่ายไปยัง public key เดียวกัน</p>
<p>เมื่อใช้ public key จากโหนดของอลิซซึ่งได้รับมาจากโหนดของบ็อบ กระเป๋าสตางค์ของอลิซจะสร้างเอาต์พุตธุรกรรมที่จ่ายให้กับสคริปต์เอาต์พุตดังนี้</p>
<pre><code>&lt;Bob's public key&gt; OP_CHECKSIG
</code></pre>
<p>ต่อมาบ็อบจะสามารถใช้จ่ายเอาต์พุตนั้นด้วยสคริปต์อินพุตที่ประกอบด้วยลายเซ็นของเขาเท่านั้น:</p>
<pre><code>&lt;Bob's signature&gt;
</code></pre>
<p>เพื่อให้เข้าใจว่าสคริปต์อินพุตและเอาต์พุตกำลังทำอะไร คุณสามารถรวมพวกมันเข้าด้วยกัน (สคริปต์อินพุตก่อน) แล้วสังเกตว่าข้อมูลแต่ละชิ้น (แสดงในเครื่องหมาย &lt; &gt;) จะถูกวางไว้ที่ด้านบนสุดของรายการที่เรียกว่าสแตก (stack) เมื่อพบรหัสคำสั่ง (opcode) มันจะใช้รายการจากสแตก โดยเริ่มจากรายการบนสุด มาดูว่ามันทำงานอย่างไรโดยเริ่มจากสคริปต์ที่รวมกัน:</p>
<pre><code>&lt;Bob's signature&gt; &lt;Bob's public key&gt; OP_CHECKSIG
</code></pre>
<p>สำหรับสคริปต์นี้ ลายเซ็นของบ็อบจะถูกนำไปไว้บนสแตก จากนั้น public key ของบ็อบจะถูกวางไว้ด้านบนของลายเซ็น และบนสุดจะเป็นคำสั่ง OP_CHECKSIG ที่จะใช้องค์ประกอบสองอย่าง เริ่มจาก public key ตามด้วยลายเซ็น โดยลบพวกมันออกจากสแตก มันจะตรวจสอบว่าลายเซ็นตรงกับ public key และยืนยันฟิลด์ต่าง ๆ ในธุรกรรม ถ้าลายเซ็นถูกต้อง OP_CHECKSIG จะแทนที่ตัวเองบนสแตกด้วยค่า 1 ถ้าลายเซ็นไม่ถูกต้อง มันจะแทนที่ตัวเองด้วย 0 ถ้ามีรายการที่ไม่ใช่ศูนย์อยู่บนสุดของสแตกเมื่อสิ้นสุดการประเมิน สคริปต์ก็จะผ่าน ถ้าสคริปต์ทั้งหมดในธุรกรรมผ่าน และรายละเอียดอื่น ๆ ทั้งหมดเกี่ยวกับธุรกรรมนั้นต้องถูกต้องจึงจะถือว่าธุรกรรมนั้นถูกต้อง</p>
<p>โดยสรุป สคริปต์ข้างต้นใช้ public key และลายเซ็นเดียวกันกับที่อธิบายใน whitepaper แต่เพิ่มความซับซ้อนของฟิลด์สคริปต์สองฟิลด์และรหัสคำสั่งหนึ่งตัว ซึ่งเราจะเริ่มเห็นประโยชน์เมื่อเรามองที่ส่วนต่อไป</p>
<blockquote>
<p>TIP:จากหลาม agian: เอาต์พุตประเภทนี้เป็นที่รู้จักในปัจจุบันว่า P2PK ซึ่งมันไม่เคยถูกใช้อย่างแพร่หลายสำหรับการชำระเงิน และไม่มีโปรแกรมที่ใช้กันอย่างแพร่หลายที่รองรับการชำระเงินผ่านที่อยู่ IP เป็นเวลาเกือบทศวรรษแล้ว</p>
</blockquote>
<h4>Legacy addresses for P2PKH</h4>
<p>แน่นอนว่าการป้อนที่อยู่ IP ของคนที่คุณต้องการจ่ายเงินให้นั้นมีข้อดีหลายประการ แต่ก็มีข้อเสียหลายประการเช่นกัน หนึ่งในข้อเสียที่สำคัญคือผู้รับจำเป็นต้องให้กระเป๋าสตางค์ของพวกเขาออนไลน์ที่ที่อยู่ IP ของพวกเขา และต้องสามารถเข้าถึงได้จากโลกภายนอก </p>
<p>ซึ่งสำหรับคนจำนวนมากนั่นไม่ใช่ตัวเลือกที่เป็นไปได้เพราะหากพวกเขา:</p>
<ul>
<li>ปิดคอมพิวเตอร์ในเวลากลางคืน</li>
<li>แล็ปท็อปของพวกเขาเข้าสู่โหมดสลีป</li>
<li>อยู่หลังไฟร์วอลล์</li>
<li>หรือกำลังใช้การแปลงที่อยู่เครือข่าย (NAT)</li>
</ul>
<p>ปัญหานี้นำเรากลับมาสู่ความท้าทายเดิมที่ผู้รับเงินอย่างบ็อบต้องให้ public key ที่มีความยาวมากแก่ผู้จ่ายเงินอย่างอลิซ  public key ของบิตคอยน์ที่สั้นที่สุดที่นักพัฒนาบิตคอยน์รุ่นแรกรู้จักมีขนาด 65 ไบต์ เทียบเท่ากับ 130 ตัวอักษรเมื่อเขียนในรูปแบบเลขฐานสิบหก (เฮกซาเดซิมอล) แต่อย่างไรก็ตาม บิตคอยน์มีโครงสร้างข้อมูลหลายอย่างที่มีขนาดใหญ่กว่า 65 ไบต์มาก ซึ่งจำเป็นต้องถูกอ้างอิงอย่างปลอดภัยในส่วนอื่น ๆ ของบิตคอยน์โดยใช้ข้อมูลขนาดเล็กที่สุดเท่าที่จะปลอดภัยได้</p>
<p>โดยบิตคอยน์แก้ปัญหานี้ด้วย ฟังก์ชันแฮช (hash function) ซึ่งเป็นฟังก์ชันที่รับข้อมูลที่อาจมีขนาดใหญ่ นำมาแฮช และให้ผลลัพธ์เป็นข้อมูลขนาดคงที่ ฟังก์ชันแฮชจะผลิตผลลัพธ์เดียวกันเสมอเมื่อได้รับข้อมูลนำเข้าแบบเดียวกัน และฟังก์ชันที่ปลอดภัยจะทำให้เป็นไปไม่ได้ในทางปฏิบัติสำหรับผู้ที่ต้องการเลือกข้อมูลนำเข้าอื่นที่ให้ผลลัพธ์เหมือนกันได้ นั่นทำให้ผลลัพธ์เป็น คำมั่นสัญญา (commitment) ต่อข้อมูลนำเข้า เป็นสัญญาว่าในทางปฏิบัติ มีเพียงข้อมูลนำเข้า x เท่านั้นที่จะให้ผลลัพธ์ X</p>
<p>สมมติว่าผมต้องการถามคำถามคุณและให้คำตอบของผมในรูปแบบที่คุณไม่สามารถอ่านได้ทันที สมมติว่าคำถามคือ "ในปีไหนที่ซาโตชิ นาคาโมโตะเริ่มทำงานบนบิทคอยน์?" ผมจะให้การยืนยันคำตอบของผมในรูปแบบของผลลัพธ์จากฟังก์ชันแฮช SHA256 ซึ่งเป็นฟังก์ชันที่ใช้บ่อยที่สุดในบิทคอยน์:</p>
<pre><code>94d7a772612c8f2f2ec609d41f5bd3d04a5aa1dfe3582f04af517d396a302e4e
</code></pre>
<p>ต่อมา หลังจากคุณบอกคำตอบที่คุณเดาสำหรับคำถามนั้น ผมสามารถเปิดเผยคำตอบของผมและพิสูจน์ให้คุณเห็นว่าคำตอบของผม เมื่อใช้เป็นข้อมูลสำหรับฟังก์ชันแฮช จะให้ผลลัพธ์เดียวกันกับที่ผมให้คุณก่อนหน้านี้</p>
<pre><code>$ echo "2007.  He said about a year and a half before Oct 2008" | sha256sum
94d7a772612c8f2f2ec609d41f5bd3d04a5aa1dfe3582f04af517d396a302e4e
</code></pre>
<p>ทีนี้ให้สมมติว่าเราถามบ็อบว่า " public key ของคุณคืออะไร?" บ็อบสามารถใช้ฟังก์ชันแฮชเพื่อให้การยืนยันที่ปลอดภัยทางการเข้ารหัสต่อ public key ของเขา หากเขาเปิดเผยกุญแจในภายหลัง และเราตรวจสอบว่ามันให้ผลการยืนยันเดียวกันกับที่เขาให้เราก่อนหน้านี้ เราสามารถมั่นใจได้ว่ามันเป็นกุญแจเดียวกันที่ใช้สร้างการยืนยันก่อนหน้านี้</p>
<p>ฟังก์ชันแฮช SHA256 ถือว่าปลอดภัยมากและให้ผลลัพธ์ 256 บิต (32 ไบต์) น้อยกว่าครึ่งหนึ่งของขนาด public key ของบิทคอยน์ดั้งเดิม แต่อย่างไรก็ตาม มีฟังก์ชันแฮชอื่นๆ ที่ปลอดภัยน้อยกว่าเล็กน้อยที่ให้ผลลัพธ์ขนาดเล็กกว่า เช่น ฟังก์ชันแฮช RIPEMD-160 ซึ่งให้ผลลัพธ์ 160 บิต (20 ไบต์) ด้วยเหตุผลที่ซาโตชิ นาคาโมโตะไม่เคยระบุ เวอร์ชันดั้งเดิมของบิทคอยน์สร้างการยืนยันต่อ public key โดยการแฮชกุญแจด้วย SHA256 ก่อน แล้วแฮชผลลัพธ์นั้นด้วย RIPEMD-160 ซึ่งให้การยืนยันขนาด 20 ไบต์ต่อ public key</p>
<p>เราสามารถดูสิ่งนี้ตามอัลกอริทึม เริ่มจากกุญแจสาธารณะ K เราคำนวณแฮช SHA256 และคำนวณแฮช RIPEMD-160 ของผลลัพธ์ ซึ่งให้ตัวเลข 160 บิต (20 ไบต์): A = RIPEMD160(SHA256(K))</p>
<p>ทีนี้เราคงเข้าใจวิธีสร้างการยืนยันต่อ public key แล้ว ต่อไปเราจะมาดูวิธีการใช้งานโดยพิจารณาสคริปต์เอาต์พุตต่อไปนี้:</p>
<pre><code>OP_DUP OP_HASH160 &lt;Bob's commitment&gt; OP_EQUAL OP_CHECKSIG
</code></pre>
<p>และสคริปต์อินพุตต่อไปนี้:</p>
<pre><code>&lt;Bob's signature&gt; &lt;Bob's public key&gt;
</code></pre>
<p>และเมื่อเรารวมมันเข้าด้วยกันเราจะได้ผลลัพธ์ดังนี้:</p>
<pre><code>&lt;Bob's signature&gt; &lt;Bob's public key&gt; OP_DUP OP_HASH160 &lt;Bob's commitment&gt; OP_EQUAL OP_CHECKSIG
</code></pre>
<p>เหมือนที่เราทำใน IP Addresses: The Original Address for Bitcoin (P2PK) เราเริ่มวางรายการลงในสแต็ก ลายเซ็นของบ็อบถูกวางก่อน จากนั้น public key ของเขาถูกวางไว้ด้านบน จากนั้นดำเนินการ OP_DUP เพื่อทำสำเนารายการบนสุด ดังนั้นรายการบนสุดและรายการที่สองจากบนในสแต็กตอนนี้เป็น public key ของบ็อบทั้งคู่ การดำเนินการ OP_HASH160 ใช้ (ลบ) public key บนสุดและแทนที่ด้วยผลลัพธ์ของการแฮชด้วย RIPEMD160(SHA256(K)) ดังนั้นตอนนี้บนสุดของสแต็กคือแฮชของ public key ของบ็อบ ต่อไป  commitment ถูกเพิ่มไว้บนสุดของสแต็ก การดำเนินการ OP_EQUALVERIFY ใช้รายการสองรายการบนสุดและตรวจสอบว่าพวกมันเท่ากัน ซึ่งควรเป็นเช่นนั้นหาก public key ที่บ็อบให้ในสคริปต์อินพุตเป็น public key เดียวกันกับที่ใช้สร้างการยืนยันในสคริปต์เอาต์พุตที่อลิซจ่าย หาก OP_EQUALVERIFY ล้มเหลว ทั้งสคริปต์จะล้มเหลว สุดท้าย เราเหลือสแต็กที่มีเพียงลายเซ็นของบ็อบและ public key ของเขา รหัสปฏิบัติการ OP_CHECKSIG ตรวจสอบว่าพวกมันสอดคล้องกัน</p>
<blockquote>
<p>TIP: จากหลาม ถ้าอ่านตรงนี้และงง ๆ ผมไปทำรูปมาให้ดูง่ายขึ้นครับ</p>
</blockquote>
<p><img src="https://image.nostr.build/3ea1036b7de3d83b960d1ed862e738120159e4f173d55a4dfdaddc75c3b25166.png" alt="image"></p>
<p>แม้กระบวนการของการ pay-to-publickey-hash(P2PKH) อาจดูซับซ้อน แต่มันทำให้การที่อลิซจ่ายเงินให้บ็อบมีเพียงการยืนยันเพียง 20 ไบต์ต่อ public key ของเขาแทนที่จะเป็นตัวกุญแจเอง ซึ่งจะมีขนาด 65 ไบต์ในเวอร์ชันดั้งเดิมของบิทคอยน์ นั่นเป็นข้อมูลที่น้อยกว่ามากที่บ็อบต้องสื่อสารกับอลิซ</p>
<p>แต่อย่างไรก็ตาม เรายังไม่ได้พูดถึงวิธีที่บ็อบรับ 20 ไบต์เหล่านั้นจากกระเป๋าเงินบิทคอยน์ของเขาไปยังกระเป๋าเงินของอลิซ มีการเข้ารหัสค่าไบต์ที่ใช้กันอย่างแพร่หลาย เช่น เลขฐานสิบหก แต่ข้อผิดพลาดใด ๆ ในการคัดลอกการยืนยันจะทำให้บิทคอยน์ถูกส่งไปยังเอาต์พุตที่ไม่สามารถใช้จ่ายได้ ทำให้พวกมันสูญหายไปตลอดกาล โดยในส่วนถัดไป เราจะดูที่การเข้ารหัสแบบกะทัดรัดและการตรวจสอบความถูกต้อง</p>
<h4>Base58check Encoding</h4>
<p>ระบบคอมพิวเตอร์มีวิธีเขียนตัวเลขยาวๆ ให้สั้นลงโดยใช้ทั้งตัวเลขและตัวอักษรผสมกัน เพื่อใช้พื้นที่น้อยลงอย่างเช่น</p>
<ul>
<li>ระบบเลขฐานสิบ (ปกติที่เราใช้) - ใช้เลข 0-9 เท่านั้น</li>
<li>ระบบเลขฐานสิบหก - ใช้เลข 0-9 และตัวอักษร A-F ตัวอย่าง: เลข 255 ในระบบปกติ เขียนเป็น FF ในระบบเลขฐานสิบหก (สั้นกว่า)</li>
<li>ระบบเลขฐานหกสิบสี่ (Base64) - ใช้สัญลักษณ์ถึง 64 ตัว: ตัวอักษรเล็ก (a-z) 26 ตัว, ตัวอักษรใหญ่ (A-Z) 26 ตัว, ตัวเลข (0-9) 10 ตัว, สัญลักษณ์พิเศษอีก 2 ตัว ("+" และ "/")</li>
</ul>
<p>โดยระบบ Base64 นี้ช่วยให้เราส่งไฟล์คอมพิวเตอร์ผ่านข้อความธรรมดาได้ เช่น การส่งรูปภาพผ่านอีเมล โดยใช้พื้นที่น้อยกว่าการเขียนเป็นเลขฐานสิบแบบปกติมาก</p>
<p>การเข้ารหัสแบบ Base58 คล้ายกับ Base64 โดยใช้ตัวอักษรพิมพ์ใหญ่ พิมพ์เล็ก และตัวเลข แต่ได้ตัดตัวอักษรบางตัวที่มักถูกเข้าใจผิดว่าเป็นตัวอื่นและอาจดูเหมือนกันเมื่อแสดงในฟอนต์บางประเภทออกไป</p>
<p>Base58 คือ Base64 ที่ตัดตัวอักษรต่อไปนี้ออก:</p>
<ul>
<li>เลข 0 (ศูนย์)</li>
<li>ตัวอักษร O (ตัว O พิมพ์ใหญ่)</li>
<li>ตัวอักษร l (ตัว L พิมพ์เล็ก)</li>
<li>ตัวอักษร I (ตัว I พิมพ์ใหญ่)</li>
<li>และสัญลักษณ์ "+" และ "/"</li>
</ul>
<p>หรือพูดให้ง่ายขึ้น Base58 คือกลุ่มตัวอักษรพิมพ์เล็ก พิมพ์ใหญ่ และตัวเลข แต่ไม่มีตัวอักษรทั้งสี่ตัว (0, O, l, I) ที่กล่าวถึงข้างต้น ตัวอักษรทั้งหมดที่ใช้ใน Base58 จะแสดงให้เห็นในตัวอักษร Base58 ของบิทคอยน์</p>
<p>Example 2. Bitcoin’s base58 alphabet</p>
<pre><code>123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
</code></pre>
<p>การเพิ่มความปลอดภัยพิเศษเพื่อป้องกันการพิมพ์ผิดหรือข้อผิดพลาดในการคัดลอก base58check ได้รวม รหัสตรวจสอบ (checksum) ที่เข้ารหัสในตัวอักษร base58 เข้าไปด้วย รหัสตรวจสอบนี้คือข้อมูลเพิ่มเติมอีก 4 ไบต์ที่เพิ่มเข้าไปที่ท้ายของข้อมูลที่กำลังถูกเข้ารหัส</p>
<p>รหัสตรวจสอบนี้ได้มาจากการแฮชข้อมูลที่ถูกเข้ารหัส และจึงสามารถใช้เพื่อตรวจจับข้อผิดพลาดจากการคัดลอกและการพิมพ์ได้ เมื่อโปรแกรมได้รับรหัส base58check ซอฟต์แวร์ถอดรหัสจะคำนวณรหัสตรวจสอบของข้อมูลและเปรียบเทียบกับรหัสตรวจสอบที่รวมอยู่ในรหัสนั้น</p>
<p>หากทั้งสองไม่ตรงกัน แสดงว่ามีข้อผิดพลาดเกิดขึ้น และข้อมูล base58check นั้นไม่ถูกต้อง กระบวนการนี้ช่วยป้องกันไม่ให้ address บิทคอยน์ที่พิมพ์ผิดถูกยอมรับโดยซอฟต์แวร์กระเป๋าเงินว่าเป็น address ที่ถูกต้อง ซึ่งเป็นข้อผิดพลาดที่อาจส่งผลให้สูญเสียเงินได้</p>
<p>การแปลงข้อมูล (ตัวเลข) เป็นรูปแบบ base58check มีขั้นตอนดังนี้:</p>
<ol>
<li>เราเริ่มโดยการเพิ่ม prefix เข้าไปในข้อมูล เรียกว่า "version byte" ซึ่งช่วยให้ระบุประเภทของข้อมูลที่ถูกเข้ารหัสได้ง่าย ตัวอย่างเช่น: prefix ศูนย์ (0x00 ในระบบเลขฐานสิบหก) แสดงว่าข้อมูลควรถูกใช้เป็นการยืนยัน (hash) ในสคริปต์เอาต์พุต legacy P2PKH</li>
<li>จากนั้น เราคำนวณ "double-SHA" checksum ซึ่งหมายถึงการใช้อัลกอริทึมแฮช SHA256 สองครั้งกับผลลัพธ์ก่อนหน้า (prefix ต่อกับข้อมูล):<pre><code>checksum = SHA256(SHA256(prefix||data))
</code></pre>
</li>
<li>จากแฮช 32 ไบต์ที่ได้ (การแฮชซ้อนแฮช) เราเลือกเฉพาะ 4 ไบต์แรก ไบต์ทั้งสี่นี้ทำหน้าที่เป็นรหัสตรวจสอบข้อผิดพลาดหรือ checksum</li>
<li>นำ checksum นี้ไปต่อที่ท้ายข้อมูล</li>
</ol>
<p>การเข้ารหัสแบบ base58check คือรูปแบบการเข้ารหัสที่ใช้ base58 พร้อมกับการระบุเวอร์ชันและการตรวจสอบความถูกต้อง เพื่อการเข้ารหัสข้อมูลบิทคอยน์ โดยคุณสามารถดูภาพประกอบด้านล่างเพื่อความเข้าใจเพิ่มเติม</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0406.png" alt="image"></p>
<p>ในบิตคอยน์นั้น นอกจากจะใช้ base58check ในการยืนยัน public key แล้ว ก็ยังมีการใช้ในข้อมูลอื่น ๆ ด้วย เพื่อทำให้ข้อมูลนั้นกะทัดรัด อ่านง่าย และตรวจจับข้อผิดพลาดได้ง่ายด้วยรหัสนำหน้า (version prefix) ในการเข้ารหัสแบบ base58check ถูกใช้เพื่อสร้างรูปแบบที่แยกแยะได้ง่าย ซึ่งเมื่อเข้ารหัสด้วย base58 โดยจะมีตัวอักษรเฉพาะที่จุดเริ่มต้นของข้อมูลที่เข้ารหัส base58check ตัวอักษรเหล่านี้ช่วยให้เราระบุประเภทของข้อมูลที่ถูกเข้ารหัสและวิธีการใช้งานได้ง่าย นี่คือสิ่งที่แยกความแตกต่าง ตัวอย่างเช่น ระหว่าง address บิทคอยน์ที่เข้ารหัส base58check ซึ่งขึ้นต้นด้วยเลข 1 กับรูปแบบการนำเข้า private key  (WIF - Wallet Import Format) ที่เข้ารหัส base58check ซึ่งขึ้นต้นด้วยเลข 5 ตัวอย่างของ version prefix สามารถดูได้ตามตารางด้านล่างนี้</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742640432919-YAKIHONNES3.png" alt="image"></p>
<p>ภาพต่อไปนี้จะทำให้คุณเห็นภาพของกระบวนการแปลง public key ให้เป็น bitcoin address</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0407.png" alt="image"></p>
<h3>Compressed Public Keys</h3>
<p>ในยุคแรก ๆ ของบิตคอยน์นั้น มีเพียงการสร้าง public key แบบ 65 Bytes เท่านั้น แต่ในเวลาต่อมา เหล่านักพัฒนาในยุคหลังได้พบวิธีการสร้าง public key แบบใหม่ที่มีเพียง 33 Bytes และสามารถทำงานร่วมกันกับโหนดทั้งหมดในขณะนั้นได้ จีงไม่จะเป็นต้องเปลี่ยนแปลงกฎหรือโครงสร้างภายในโปรโตคอลของบิตคอยน์ โดย poublic key แบบใหม่ที่มีขนาด 33 Bytes นี้เรียกว่า compressed public key (public key ที่ถูกบีบอัด) และมีการเรียก public key ที่มีขนาด 65 Bytes ว่า uncompressed public key (public key ที่ไม่ถูกบีบอัด) ซึ่งประโยชน์ของ public key ที่เล็กลงนั้น นอกจากจะช่วยให้การส่ง public key ให้ผู้อื่นทำได้ง่ายขึ้นแล้ว ยังช่วยให้ธุรกรรมมีขนาดเล็กลง และช่วยให้สามารถทำการชำระเงินได้มากขึ้นในบล็อกเดียวกัน</p>
<p>อย่างที่เราได้เรียนรู้จากเนื้อหาในส่วนของ public key เราได้ทราบว่า public key คือจุด (x, y) บนเส้นโค้งวงรี เนื่องจากเส้นโค้งแสดงฟังก์ชันทางคณิตศาสตร์ จุดบนเส้นโค้งจึงเป็นคำตอบของสมการ ดังนั้นหากเรารู้พิกัด x เราก็สามารถคำนวณพิกัด y ได้โดยแก้สมการ y² mod p = (x³ + 7) mod p นั่นหมายความว่าเราสามารถเก็บเพียงพิกัด x ของ public key โดยละพิกัด y ไว้ ซึ่งช่วยลดขนาดของกุญแจและพื้นที่ที่ต้องใช้เก็บข้อมูลลง 256 บิต การลดขนาดลงเกือบ 50% ในทุกธุรกรรมรวมกันแล้วช่วยประหยัดข้อมูลได้มากมายในระยะยาว!</p>
<p>นี่คือ public key ที่ได้ยกเป็นตัวอย่างไว้ก่อนหน้า</p>
<pre><code>x = F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
y = 07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
</code></pre>
<p>และนี่คือ public key ที่มีตัวนำหน้า 04 ตามด้วยพิกัด x และ y ในรูปแบบ 04 x y:</p>
<pre><code>K = 04F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB
</code></pre>
<p>uncompressed public key นั้นจะมีตัวนำหน้าเป็น 04 แต่ compressed public key จะมีตัวนำหน้าเป็น 02 หรือ 03 โดยเหตุผลนั้นมาจากสมการ y² mod p = (x³ + 7) mod p เนื่องจากด้านซ้ายของสมการคือ y² คำตอบสำหรับ y จึงเป็นรากที่สอง ซึ่งอาจมีค่าเป็นบวกหรือลบก็ได้ หากมองเชิงภาพ นี่หมายความว่าพิกัด y ที่ได้อาจอยู่เหนือหรือใต้แกน x เราต้องไม่ลืมว่าเส้นโค้งมีความสมมาตร ซึ่งหมายความว่ามันจะสะท้อนเหมือนกระจกโดยแกน x ดังนั้น แม้เราจะละพิกัด y ได้ แต่เราต้องเก็บ เครื่องหมาย ของ y (บวกหรือลบ) หรืออีกนัยหนึ่งคือเราต้องจำว่ามันอยู่เหนือหรือใต้แกน x เพราะแต่ละตำแหน่งแทนจุดที่แตกต่างกันและเป็น public key ที่แตกต่างกัน</p>
<p>เมื่อคำนวณเส้นโค้งวงรีในระบบเลขฐานสองบนสนามจำกัดของเลขจำนวนเฉพาะ p พิกัด y จะเป็นเลขคู่หรือเลขคี่ ซึ่งสอดคล้องกับเครื่องหมายบวก/ลบตามที่อธิบายก่อนหน้านี้ ดังนั้น เพื่อแยกความแตกต่างระหว่างค่าที่เป็นไปได้สองค่าของ y เราจึงเก็บ compressed public key ด้วยตัวนำหน้า 02 ถ้า y เป็นเลขคู่ และ 03 ถ้า y เป็นเลขคี่ ซึ่งช่วยให้ซอฟต์แวร์สามารถอนุมานพิกัด y จากพิกัด x และคลายการบีบอัดของ public key ไปยังพิกัดเต็มของจุดได้อย่างถูกต้อง ดังภาพประกอบต่อไปนี้</p>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0408.png" alt="image"></p>
<p>นี่คือ public key เดียวกันกับที่ยกตัวอย่างไว้ข้างต้นซึ่งแสดงให้เห็นในรูป compressed public key ที่เก็บใน 264 บิต (66 ตัวอักษรเลขฐานสิบหก) โดยมีตัวนำหน้า 03 ซึ่งบ่งชี้ว่าพิกัด y เป็นเลขคี่:</p>
<pre><code>K = 03F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A
</code></pre>
<p>compressed public key สอดคล้องกับ private key เดียวกันกับ uncompressed public key หมายความว่ามันถูกสร้างจาก private key เดียวกัน แต่อย่างไรก็ตาม มันก็มีส่วนที่แตกต่างจาก uncompressed public key นั้นคือ หากเราแปลง compressed public key เป็น commitment โดยใช้ฟังก์ชัน HASH160 (RIPEMD160(SHA256(K))) มันจะสร้าง commitment ที่แตกต่างจาก uncompressed public key และจะนำไปสู่ bitcoin address ที่แตกต่างกันในที่สุด สิ่งนี้อาจทำให้สับสนเพราะหมายความว่า private key เดียวสามารถสร้าง public key ในสองรูปแบบที่แตกต่างกัน (แบบบีบอัดและแบบไม่บีบอัด) ซึ่งสร้าง bitcoin address ที่แตกต่างกัน</p>
<p>compressed public key เป็นค่าเริ่มต้นในซอฟต์แวร์บิตคอยน์เกือบทั้งหมดในปัจจุบัน และถูกกำหนดให้ใช้กับคุณสมบัติใหม่บางอย่างที่เพิ่มในการอัปเกรดโปรโตคอลในภายหลัง</p>
<p>อย่างไรก็ตาม ซอฟต์แวร์บางตัวยังคงต้องรองรับ uncompressed public key เช่น แอปพลิเคชันกระเป๋าเงินที่นำเข้า private key จากกระเป๋าเงินเก่า เมื่อกระเป๋าเงินใหม่สแกนบล็อกเชนสำหรับผลลัพธ์และอินพุต P2PKH เก่า มันจำเป็นต้องรู้ว่าควรสแกนกุญแจขนาด 65 ไบต์ (และ commitment ของกุญแจเหล่านั้น) หรือกุญแจขนาด 33 ไบต์ (และ commitment ของกุญแจเหล่านั้น) หากไม่สแกนหาประเภทที่ถูกต้อง อาจทำให้ผู้ใช้ไม่สามารถใช้ยอดคงเหลือทั้งหมดได้ เพื่อแก้ไขปัญหานี้ เมื่อส่งออก private key จากกระเป๋าเงิน WIF ที่ใช้แสดง private key ในกระเป๋าเงินบิตคอยน์รุ่นใหม่จะถูกนำไปใช้แตกต่างกันเล็กน้อยเพื่อบ่งชี้ว่า private key เหล่านี้ถูกใช้ในการสร้าง compressed public key</p>
<h4>Legacy: Pay to Script Hash (P2SH)</h4>
<p>ตามที่เราได้เห็นในส่วนก่อนหน้านี้ ผู้รับบิตคอยน์ สามารถกำหนดให้การชำระเงินที่ส่งมาให้เขานั้นมีเงื่อนไขบางอย่างในสคริปต์เอาต์พุตได้โดยจะต้องปฏิบัติตามเงื่อนไขเหล่านั้นโดยใช้สคริปต์อินพุตเมื่อเขาใช้จ่ายบิตคอยน์เหล่านั้น ในส่วน IP Addresses: The Original Address for Bitcoin (P2PK) เงื่อนไขก็คือสคริปต์อินพุตต้องให้ลายเซ็นที่เหมาะสม ในส่วน Legacy Addresses for P2PKH นั้นจำเป็นต้องมี public key ที่เหมาะสมด้วย</p>
<p>ส่วนสำหรับผู้ส่งก็จะวางเงื่อนไขที่ผู้รับต้องการในสคริปต์เอาต์พุตที่ใช้จ่ายให้กับผู้รับ โดยผู้รับจะต้องสื่อสารเงื่อนไขเหล่านั้นให้ผู้ส่งทราบ ซึ่งคล้ายกับปัญหาที่บ๊อบต้องสื่อสาร public key ของเขาให้อลิซทราบ และเช่นเดียวกับปัญหานั้นที่ public key อาจมีขนาดค่อนข้างใหญ่ เงื่อนไขที่บ๊อบใช้ก็อาจมีขนาดใหญ่มากเช่นกัน—อาจมีขนาดหลายพันไบต์ นั่นไม่เพียงแต่เป็นข้อมูลหลายพันไบต์ที่ต้องสื่อสารให้อลิซทราบ แต่ยังเป็นข้อมูลหลายพันไบต์ที่เธอต้องจ่ายค่าธรรมเนียมธุรกรรมทุกครั้งที่ต้องการใช้จ่ายเงินให้บ๊อบ อย่างไรก็ตาม การใช้ฟังก์ชันแฮชเพื่อสร้าง commitment ขนาดเล็กสำหรับข้อมูลขนาดใหญ่ก็สามารถนำมาใช้ได้ในกรณีนี้เช่นกัน</p>
<p>ในเวลาต่อมานั้น การอัปเกรด BIP16 สำหรับโปรโตคอลบิตคอยน์ในปี 2012 ได้อนุญาตให้สคริปต์เอาต์พุตสร้าง commitment กับ redemption script (redeem script) ได้ แปลว่าเมื่อบ๊อบใช้จ่ายบิตคอยน์ของเขา ภายในสคริปต์อินพุตของเขานั้นจะต้องให้ redeem script ที่ตรงกับ commitment และข้อมูลที่จำเป็นเพื่อให้เป็นไปตาม redeem script (เช่น ลายเซ็น) เริ่มต้นด้วยการจินตนาการว่าบ๊อบต้องการให้มีลายเซ็นสองอันเพื่อใช้จ่ายบิตคอยน์ของเขา หนึ่งลายเซ็นจากกระเป๋าเงินบนเดสก์ท็อปและอีกหนึ่งจากอุปกรณ์เซ็นแบบฮาร์ดแวร์ เขาใส่เงื่อนไขเหล่านั้นลงใน redeem script:</p>
<pre><code>&lt;public key 1&gt; OP_CHECKSIGVERIFY &lt;public key 2&gt; OP_CHECKSIG
</code></pre>
<p>จากนั้นเขาสร้าง commitment กับ redeem script โดยใช้กลไก HASH160 เดียวกับที่ใช้สำหรับ commitment แบบ P2PKH, RIPEMD160(SHA256(script)) commitment นั้นถูกวางไว้ในสคริปต์เอาต์พุตโดยใช้เทมเพลตพิเศษ:</p>
<pre><code>OP_HASH160 &lt;commitment&gt; OP_EQUAL
</code></pre>
<blockquote>
<p>คำเตือน: เมื่อใช้ pay to script hash (P2SH) คุณต้องใช้เทมเพลต P2SH โดยเฉพาะ ซึ่งจะไม่มีข้อมูลหรือเงื่อนไขเพิ่มเติมในสคริปต์เอาต์พุต หากสคริปต์เอาต์พุตไม่ได้เป็น OP_HASH160 &lt;20 ไบต์&gt; OP_EQUAL แน่นอนว่า redeem script จะไม่ถูกใช้และบิตคอยน์ใด ๆ อาจไม่สามารถใช้จ่ายได้หรืออาจถูกใช้จ่ายได้โดยทุกคน (หมายความว่าใครก็สามารถนำไปใช้ได้)</p>
</blockquote>
<p>เมื่อบ๊อบต้องการจ่ายเงินที่เขาได้รับผ่าน commitment สำหรับสคริปต์ของเขา เขาจะใช้สคริปต์อินพุตที่รวมถึง redeem script ซึ่งถูกแปลงให้เป็นข้อมูลอีลิเมนต์เดียว นอกจากนี้เขายังให้ลายเซ็นที่จำเป็นเพื่อให้เป็นไปตาม redeem script โดยเรียงลำดับตามที่จะถูกใช้โดย opcodes:</p>
<pre><code>&lt;signature2&gt; &lt;signature1&gt; &lt;redeem script&gt;
</code></pre>
<p>เมื่อโหนดของบิตคอยน์ได้รับการใช้จ่ายของบ๊อบพวกมันจะตรวจสอบว่า redeem script ที่ถูกแปลงเป็นค่าแฮชแล้วมีค่าเดียวกันกับ commitment มั้ย หลังจากนั้นพวกมันจะแทนที่มันบนสแต็คด้วยค่าที่ถอดรหัสแล้ว:</p>
<pre><code>&lt;signature2&gt; &lt;signature1&gt; &lt;pubkey1&gt; OP_CHECKSIGVERIFY &lt;pubkey2&gt; OP_CHECKSIG
</code></pre>
<p>สคริปต์จะถูกประมวลผล และหากผ่านการตรวจสอบและรายละเอียดธุรกรรมอื่น ๆ ทั้งหมดถูกต้อง ธุรกรรมก็จะถือว่าใช้ได้</p>
<p>address สำหรับ P2SH ก็ถูกสร้างด้วย base58check เช่นกัน คำนำหน้าเวอร์ชันถูกตั้งเป็น 5 ซึ่งทำให้ที่อยู่ที่เข้ารหัสแล้วขึ้นต้นด้วยเลข 3 ตัวอย่างของที่อยู่ P2SH คือ 3F6i6kwkevjR7AsAd4te2YB2zZyASEm1HM</p>
<blockquote>
<p>TIP: P2SH ไม่จำเป็นต้องเหมือนกับธุรกรรมแบบหลายลายเซ็น (multisignature) เสมอไป ถึง address P2SH ส่วนใหญ่ แทนสคริปต์แบบหลายลายเซ็นก็ตาม แต่อาจแทนสคริปต์ที่เข้ารหัสธุรกรรมประเภทอื่น ๆ ได้ด้วย</p>
</blockquote>
<p>P2PKH และ P2SH เป็นสองเทมเพลตสคริปต์เท่านั้นที่ใช้กับการเข้ารหัสแบบ base58check พวกมันเป็นที่รู้จักในปัจจุบันว่าเป็น address แบบ legacy และกลายเป็นรูปแบบที่พบน้อยลงเรื่อยๆ address แบบ legacy ถูกแทนที่ด้วยaddress ตระกูล bech32</p>
<h4>การโจมตี P2SH แบบ Collision</h4>
<p>address ทั้งหมดที่อิงกับฟังก์ชันแฮชมีความเสี่ยงในทางทฤษฎีต่อผู้โจมตีที่อาจค้นพบอินพุตเดียวกันที่สร้างเอาต์พุตฟังก์ชันแฮช (commitment) โดยอิสระ ในกรณีของบิตคอยน์ หากพวกเขาค้นพบอินพุตในวิธีเดียวกับที่ผู้ใช้ดั้งเดิมทำ พวกเขาจะรู้ private key ของผู้ใช้และสามารถใช้จ่ายบิตคอยน์ของผู้ใช้นั้นได้ โอกาสที่ผู้โจมตีจะสร้างอินพุตสำหรับ commitment ที่มีอยู่แล้วโดยอิสระนั้นขึ้นอยู่กับความแข็งแกร่งของอัลกอริทึมแฮช สำหรับอัลกอริทึมที่ปลอดภัย 160 บิตอย่าง HASH160 ความน่าจะเป็นอยู่ที่ 1 ใน 2^160 นี่เรียกว่าการโจมตีแบบ preimage attack</p>
<p>ผู้โจมตีสามารถพยายามสร้างข้อมูลนำเข้าสองชุดที่แตกต่างกัน (เช่น redeem scripts) ที่สร้างการเข้ารหัสแบบเดียวกันได้ สำหรับ address ที่สร้างโดยฝ่ายเดียวทั้งหมด โอกาสที่ผู้โจมตีจะสร้างข้อมูลนำเข้าที่แตกต่างสำหรับการเข้ารหัสที่มีอยู่แล้วมีประมาณ 1 ใน 2^160 สำหรับอัลกอริทึม HASH160 นี่คือการโจมตีแบบ second preimage attack </p>
<p>อย่างไรก็ตาม สถานการณ์จะเปลี่ยนไปเมื่อผู้โจมตีสามารถมีอิทธิพลต่อค่าข้อมูลนำเข้าดั้งเดิมได้ ตัวอย่างเช่น ผู้โจมตีมีส่วนร่วมในการสร้างสคริปต์แบบหลายลายเซ็น (multisignature script) ซึ่งพวกเขาไม่จำเป็นต้องส่ง public key ของตนจนกว่าจะทราบ public key ของฝ่ายอื่นทั้งหมด ในกรณีนั้น ความแข็งแกร่งของอัลกอริทึมการแฮชจะลดลงเหลือรากที่สองของมัน สำหรับ HASH160 ความน่าจะเป็นจะกลายเป็น 1 ใน 2^80 นี่คือการโจมตีแบบ collision attack</p>
<p>เพื่อให้เข้าใจตัวเลขเหล่านี้ในบริบทที่ชัดเจน ข้อมูล ณ ต้นปี 2023 นักขุดบิตคอยน์ทั้งหมดรวมกันสามารถประมวลผลฟังก์ชันแฮชประมาณ 2^80 ทุกชั่วโมง พวกเขาใช้ฟังก์ชันแฮชที่แตกต่างจาก HASH160 ดังนั้นฮาร์ดแวร์ที่มีอยู่จึงไม่สามารถสร้างการโจมตีแบบ collision attack สำหรับมันได้ แต่การมีอยู่ของเครือข่ายบิตคอยน์พิสูจน์ว่าการโจมตีแบบชนกันต่อฟังก์ชัน 160 บิตอย่าง HASH160 สามารถทำได้จริงในทางปฏิบัติ นักขุดบิตคอยน์ได้ลงทุนเทียบเท่ากับหลายพันล้านดอลลาร์สหรัฐในฮาร์ดแวร์พิเศษ ดังนั้นการสร้างการโจมตีแบบ collision attack จึงไม่ใช่เรื่องถูก แต่มีองค์กรที่คาดหวังว่าจะได้รับบิตคอยน์มูลค่าหลายพันล้านดอลลาร์ไปยัง address ที่สร้างโดยกระบวนการที่เกี่ยวข้องกับหลายฝ่าย ซึ่งอาจทำให้การโจมตีนี้มีกำไร</p>
<p>มีโปรโตคอลการเข้ารหัสที่เป็นที่ยอมรับอย่างดีในการป้องกันการโจมตีแบบ collision attack แต่วิธีแก้ปัญหาที่ง่ายโดยไม่ต้องใช้ความรู้พิเศษจากผู้พัฒนากระเป๋าเงินคือการใช้ฟังก์ชันแฮชที่แข็งแกร่งกว่า การอัปเกรดบิตคอยน์ในภายหลังทำให้เป็นไปได้ และ address บิตคอยน์ใหม่ให้ความต้านทานการชนกันอย่างน้อย 128 บิต การดำเนินการแฮช 2^128 ครั้งจะใช้เวลานักขุดบิตคอยน์ปัจจุบันทั้งหมดประมาณ 32 พันล้านปี</p>
<p>แม้ว่าเราไม่เชื่อว่ามีภัยคุกคามเร่งด่วนต่อผู้ที่สร้าง address P2SH ใหม่ แต่เราแนะนำให้กระเป๋าเงินใหม่ทั้งหมดใช้ที่อยู่ประเภทใหม่เพื่อขจัดความกังวลเกี่ยวกับการโจมตีแบบ collision attack ของ P2SH address</p>
<h4>Bech32 Addresses</h4>
<p>ในปี 2017 โปรโตคอลบิตคอยน์ได้รับการอัปเกรด เพื่อป้องกันไม่ให้ตัวระบุธุรกรรม (txids) ไม่สามารถเปลี่ยนแปลงได้ โดยไม่ได้รับความยินยอมจากผู้ใช้ที่ทำการใช้จ่าย (หรือองค์ประชุมของผู้ลงนามเมื่อต้องมีลายเซ็นหลายรายการ) การอัปเกรดนี้เรียกว่า segregated witness (หรือเรียกสั้นๆ ว่า segwit) ซึ่งยังให้ความสามารถเพิ่มเติมสำหรับข้อมูลธุรกรรมในบล็อกและประโยชน์อื่น ๆ อีกหลายประการ แต่อย่างไรก็ตาม หากมีผู้ใช้เก่าที่ต้องการเข้าถึงประโยชน์ของ segwit โดยตรงต้องยอมรับการชำระเงินไปยังสคริปต์เอาต์พุตใหม่</p>
<p>ตามที่ได้กล่าวไว้ใน p2sh หนึ่งในข้อดีของเอาต์พุตประเภท P2SH คือผู้จ่ายไม่จำเป็นต้องรู้รายละเอียดของสคริปต์ที่ผู้รับใช้ การอัปเกรด segwit ถูกออกแบบมาให้ใช้กลไกนี้ได้ดังเดิม จึง ทำให้ผู้จ่ายสามารถเริ่มเข้าถึงประโยชน์ใหม่ ๆ หลายอย่างได้ทันทีโดยใช้ที่อยู่ P2SH แต่เพื่อให้ผู้รับสามารถเข้าถึงประโยชน์เหล่านั้นได้ พวกเขาจำเป็นจะต้องให้กระเป๋าเงินของผู้จ่ายจ่ายเงินให้เขาโดยใช้สคริปต์ประเภทอื่นแทน ซึ่งจะต้องอาศัยการอัปเกรดกระเป๋าเงินของผู้จ่ายเพื่อรองรับสคริปต์ใหม่เหล่านี้</p>
<p>ในช่วงแรก เหล่านักพัฒนาบิตคอยน์ได้นำเสนอ BIP142 ซึ่งจะยังคงใช้ base58check ร่วมกับไบต์เวอร์ชันใหม่ คล้ายกับการอัปเกรด P2SH แต่การให้กระเป๋าเงินทั้งหมดอัปเกรดไปใช้สคริปต์ใหม่ที่มีเวอร์ชัน base58check ใหม่นั้น คาดว่าจะต้องใช้ความพยายามเกือบเท่ากับการให้พวกเขาอัปเกรดไปใช้รูปแบบ address ที่เป็นแบบใหม่ทั้งหมด ด้วยเหตุนี้้เอง ผู้สนับสนุนบิตคอยน์หลายคนจึงเริ่มออกแบบรูปแบบ address ที่ดีที่สุดเท่าที่เป็นไปได้ พวกเขาระบุปัญหาหลายอย่างกับ base58check ไว้ดังนี้:</p>
<ul>
<li>การที่ base58check ใช้อักษรที่มีทั้งตัวพิมพ์ใหญ่และตัวพิมพ์เล็กทำให้ไม่สะดวกในการอ่านออกเสียงหรือคัดลอก ลองอ่าน address แบบเก่าในบทนี้ให้เพื่อนฟังและให้พวกเขาคัดลอก คุณจะสังเกตว่าคุณต้องระบุคำนำหน้าทุกตัวอักษรด้วยคำว่า "ตัวพิมพ์ใหญ่" และ "ตัวพิมพ์เล็ก" และเมื่อคุณตรวจสอบสิ่งที่พวกเขาเขียน คุณจะพบว่าตัวพิมพ์ใหญ่และตัวพิมพ์เล็กของตัวอักษรบางตัวอาจดูคล้ายกันในลายมือของคนส่วนใหญ่</li>
<li>รูปแบบนี้สามารถตรวจจับข้อผิดพลาดได้ แต่ไม่สามารถช่วยผู้ใช้แก้ไขข้อผิดพลาดเหล่านั้น ตัวอย่างเช่น หากคุณสลับตำแหน่งตัวอักษรสองตัวโดยไม่ตั้งใจเมื่อป้อน address ด้วยตนเอง กระเป๋าเงินของคุณจะเตือนว่ามีข้อผิดพลาดเกิดขึ้นแน่นอน แต่จะไม่ช่วยให้คุณค้นพบว่าข้อผิดพลาดอยู่ที่ไหน คุณอาจต้องใช้เวลาหลายนาทีที่น่าหงุดหงิดเพื่อค้นหาข้อผิดพลาดในที่สุด</li>
<li>การใช้ตัวอักษรที่มีทั้งตัวพิมพ์ใหญ่และตัวพิมพ์เล็กยังต้องใช้พื้นที่เพิ่มเติมในการเข้ารหัสใน QR code ซึ่งนิยมใช้ในการแชร์ address และ invoice ระหว่างกระเป๋าเงิน พื้นที่เพิ่มเติมนี้หมายความว่า QR code จำเป็นต้องมีขนาดใหญ่ขึ้นที่ความละเอียดเดียวกัน หรือไม่เช่นนั้นก็จะยากต่อการสแกนอย่างรวดเร็ว</li>
<li>การที่ต้องการให้กระเป๋าเงินผู้จ่ายทุกใบอัปเกรดเพื่อรองรับคุณสมบัติโปรโตคอลใหม่ เช่น P2SH และ segwit แม้ว่าการอัปเกรดเองอาจไม่ต้องใช้โค้ดมากนัก แต่ประสบการณ์แสดงให้เห็นว่าผู้พัฒนากระเป๋าเงินหลายรายมักยุ่งกับงานอื่น ๆ และบางครั้งอาจล่าช้าในการอัปเกรดเป็นเวลาหลายปี สิ่งนี้ส่งผลเสียต่อทุกคนที่ต้องการใช้คุณสมบัติใหม่ ๆ เหล่านี้</li>
</ul>
<p>นักพัฒนาที่ทำงานเกี่ยวกับรูปแบบ address สำหรับ segwit ได้พบวิธีแก้ปัญหาเหล่านี้ทั้งหมดในรูปแบบ address แบบใหม่ที่เรียกว่า bech32 (ออกเสียงด้วย "ch" อ่อน เช่นใน "เบช สามสิบสอง") คำว่า "bech" มาจาก BCH ซึ่งเป็นอักษรย่อของบุคคลสามคนที่ค้นพบรหัสวนนี้ในปี 1959 และ 1960 ซึ่งเป็นพื้นฐานของ bech32 ส่วน "32" หมายถึงจำนวนตัวอักษรในชุดตัวอักษร bech32 (คล้ายกับ 58 ใน base58check):</p>
<ul>
<li><p>Bech32 ใช้เฉพาะตัวเลขและตัวอักษรรูปแบบเดียว (โดยปกติจะแสดงเป็นตัวพิมพ์เล็ก) แม้ว่าชุดตัวอักษรของมันจะมีขนาดเกือบครึ่งหนึ่งของชุดตัวอักษรใน base58check ก็ตามแต่ address bech32 สำหรับสคริปต์ pay to witness public key hash (P2WPKH) ก็ยังยาวกว่า legacy address และมีขนาดเท่ากันกับสคริปต์ P2PKH </p>
</li>
<li><p>Bech32 สามารถทั้งตรวจจับและช่วยแก้ไขข้อผิดพลาดได้ ใน address ที่มีความยาวตามที่คาดหวังได้ และสามารถรับประกันทางคณิตศาสตร์ได้ว่าจะตรวจพบข้อผิดพลาดใด ๆ ที่ส่งผลกระทบต่อตัวอักษร 4 ตัวหรือน้อยกว่า ซึ่งเชื่อถือได้มากกว่า base58check ส่วนสำหรับข้อผิดพลาดที่ยาวกว่านั้น จะไม่สามารถตรวจพบได้ (โอกาสเกิดน้อยกว่าหนึ่งครั้งในหนึ่งพันล้าน) ซึ่งมีความเชื่อถือได้ประมาณเท่ากับ base58check ยิ่งไปกว่านั้น สำหรับ adddress ที่พิมพ์โดยมีข้อผิดพลาดเพียงเล็กน้อย มันสามารถบอกผู้ใช้ได้ว่าข้อผิดพลาดเหล่านั้นเกิดขึ้นที่ไหน ช่วยให้พวกเขาสามารถแก้ไขข้อผิดพลาดจากการคัดลอกเล็ก ๆ น้อย ๆ ได้อย่างรวดเร็ว </p>
</li>
<li><p>ตัวอย่างที่ 3 Bech32 address ที่มีข้อผิดพลาด<br>Address: bc1p9nh05ha8wrljf7ru236awn4t2x0d5ctkkywmv9sclnm4t0av2vgs4k3au7<br>ข้อผิดพลาดที่ตรวจพบแสดงเป็นตัวหนาและขีดเส้นใต้ สร้างโดยใช้โปรแกรมสาธิตการถอดรหัส  bech32 address</p>
</li>
<li><p>bech32 address นิยมเขียนด้วยตัวอักษรพิมพ์เล็กเท่านั้น แต่ตัวอักษรพิมพ์เล็กเหล่านี้สามารถแทนที่ด้วยตัวอักษรพิมพ์ใหญ่ก่อนการเข้ารหัส address ในรหัส QR ได้ วิธีนี้ช่วยให้สามารถใช้โหมดการเข้ารหัส QR แบบพิเศษที่ใช้พื้นที่น้อยกว่า คุณจะสังเกตเห็นความแตกต่างในขนาดและความซับซ้อนของรหัส QR ทั้งสองสำหรับที่อยู่เดียวกันในรูปภาพข้างล่างนี้</p>
</li>
</ul>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0409.png" alt="image"></p>
<ul>
<li>Bech32 ใช้ประโยชน์จากกลไกการอัปเกรดที่ออกแบบมาเป็นส่วนหนึ่งของ segwit เพื่อทำให้กระเป๋าเงินผู้จ่ายสามารถจ่ายเงินไปยังประเภทเอาต์พุตที่ยังไม่ได้ใช้งานได้ โดยมีเป้าหมายคือการอนุญาตให้นักพัฒนาสร้างกระเป๋าเงินในวันนี้ที่สามารถใช้จ่ายไปยัง bech32 address และทำให้กระเป๋าเงินนั้นยังคงสามารถใช้จ่ายไปยัง bech32address ได้สำหรับผู้ใช้คุณสมบัติใหม่ที่เพิ่มในการอัปเกรดโปรโตคอลในอนาคต โดยที่มีความหวังว่าเราอาจไม่จำเป็นต้องผ่านรอบการอัปเกรดทั้งระบบอีกต่อไป ซึ่งจำเป็นสำหรับการให้ผู้คนใช้งาน P2SH และ segwit ได้อย่างเต็มรูปแบบ</li>
<li></li>
<li><h4>Problems with Bech32 Addresses</h4>
</li>
</ul>
<p>address แบบ bech32 ประสบความสำเร็จในทุกด้านยกเว้นปัญหาหนึ่ง คือการรับประกันทางคณิตศาสตร์เกี่ยวกับความสามารถในการตรวจจับข้อผิดพลาดจะใช้ได้เฉพาะเมื่อความยาวของ address ที่คุณป้อนเข้าไปในกระเป๋าเงินมีความยาวเท่ากับ address ดั้งเดิมเท่านั้น หากคุณเพิ่มหรือลบตัวอักษรใด ๆ ระหว่างการคัดลอกจะทำให้ไม่สามารถตรวจจับได้ การรับประกันนี้จะไม่มีผล และกระเป๋าเงินของคุณอาจใช้จ่ายเงินไปยัง address ที่ไม่ถูกต้อง แต่อย่างไรก็ตาม แม้จะไม่มีคุณสมบัตินี้ มีความเชื่อว่าเป็นไปได้ยากมากที่ผู้ใช้ที่เพิ่มหรือลบตัวอักษรจะสร้างสตริงที่มีผลรวมตรวจสอบที่ถูกต้อง ซึ่งช่วยให้มั่นใจได้ว่าเงินของผู้ใช้จะปลอดภัย</p>
<p>น่าเสียดายที่การเลือกใช้ค่าคงที่ตัวหนึ่งในอัลกอริทึม bech32 บังเอิญทำให้การเพิ่มหรือลบตัวอักษร "q" ในตำแหน่งที่สองจากท้ายของ address ที่ลงท้ายด้วยตัวอักษร "p" เป็นเรื่องง่ายมาก ในกรณีเหล่านั้น คุณยังสามารถเพิ่มหรือลบตัวอักษร "q" หลายครั้งได้ด้วย ข้อผิดพลาดนี้จะถูกตรวจจับโดยผลรวมตรวจสอบ (checksum) ในบางครั้ง แต่จะถูกมองข้ามบ่อยกว่าความคาดหวังหนึ่งในพันล้านสำหรับข้อผิดพลาดจากการแทนที่ของ bech32 อย่างมาก สำหรับตัวอย่างสามารถดูได้ในรูปภาพข้างล่างนี้</p>
<p>ตัวอย่างที่ 4. การขยายความยาวของ bech32 address โดยไม่ทำให้ผลรวมตรวจสอบเป็นโมฆะ</p>
<pre><code>bech32 address ที่ถูกต้อง:
bc1pqqqsq9txsqp

address ที่ไม่ถูกต้องแต่มีผลรวมตรวจสอบที่ถูกต้อง:
bc1pqqqsq9txsqqqqp
bc1pqqqsq9txsqqqqqqp
bc1pqqqsq9txsqqqqqqqqp
bc1pqqqsq9txsqqqqqqqqqp
bc1pqqqsq9txsqqqqqqqqqqqp
</code></pre>
<p>จากตัวอย่างนี้ คุณจะเห็นว่าแม้มีการเพิ่มตัวอักษร "q" เข้าไปหลายตัวก่อนตัวอักษร "p" ตัวสุดท้าย ระบบตรวจสอบก็ยังคงยอมรับว่า address เหล่านี้ถูกต้อง นี่เป็นข้อบกพร่องสำคัญของ bech32 เพราะอาจทำให้เงินถูกส่งไปยัง address ที่ไม่มีใครเป็นเจ้าของจริง ๆ หรือ address ที่ไม่ได้ตั้งใจจะส่งไป</p>
<p>สำหรับเวอร์ชันเริ่มต้นของ segwit (เวอร์ชัน 0) ปัญหานี้ไม่ใช่ความกังวลในทางปฏิบัติ เพราะมีความยาวที่ถูกต้องมีเพียงสองแบบที่กำหนดไว้สำหรับเอาต์พุต นั้นคือ 22 Byte และ 34 Byte ซึ่งสอดคล้องกับ bech32 address ที่มีความยาวยาวที่ 42 หรือ 62 ตัวอักษร ดังนั้นคนจะต้องเพิ่มหรือลบตัวอักษร "q" จากตำแหน่งที่สองจากท้ายของ bech32 address ถึง 20 ครั้งเพื่อส่งเงินไปยัง address ที่ไม่ถูกต้องโดยที่กระเป๋าเงินไม่สามารถตรวจจับได้ อย่างไรก็ตาม มันอาจกลายเป็นปัญหาสำหรับผู้ใช้ในอนาคตหากมีการนำการอัปเกรดบนพื้นฐานของ segwit มาใช้</p>
<h4>Bech32m</h4>
<p>แม้ว่า bech32 จะทำงานได้ดีสำหรับ segwit v0 แต่นักพัฒนาไม่ต้องการจำกัดขนาดเอาต์พุตโดยไม่จำเป็นในเวอร์ชันหลังๆ ของ segwit หากไม่มีข้อจำกัด การเพิ่มหรือลบตัวอักษร "q" เพียงตัวเดียวใน bech32 address อาจทำให้ผู้ใช้ส่งเงินโดยไม่ตั้งใจไปยังเอาต์พุตที่ไม่สามารถใช้จ่ายได้หรือสามารถใช้จ่ายได้โดยทุกคน (ทำให้บิตคอยน์เหล่านั้นถูกนำไปโดยทุกคนได้) นักพัฒนาได้วิเคราะห์ปัญหา bech32 อย่างละเอียดและพบว่าการเปลี่ยนค่าคงที่เพียงตัวเดียวในอัลกอริทึมของพวกเขาจะขจัดปัญหานี้ได้ ทำให้มั่นใจว่าการแทรกหรือลบตัวอักษรสูงสุดห้าตัวจะไม่ถูกตรวจจับน้อยกว่าหนึ่งครั้งในหนึ่งพันล้านเท่านั้น</p>
<p>เวอร์ชันของ bech32 ที่มีค่าคงที่เพียงหนึ่งตัวที่แตกต่างกันเรียกว่า bech32 แบบปรับแต่ง (bech32m) ตัวอักษรทั้งหมดใน address แบบ bech32 และ bech32m สำหรับข้อมูลพื้นฐานเดียวกันจะเหมือนกันทั้งหมด ยกเว้นหกตัวสุดท้าย (ซึ่งเป็นส่วนของ checksum) นั่นหมายความว่ากระเป๋าเงินจำเป็นต้องรู้ว่ากำลังใช้เวอร์ชันใดเพื่อตรวจสอบความถูกต้องของ checksum แต่ address ทั้งสองประเภทมีไบต์เวอร์ชันภายในที่ทำให้การระบุเวอร์ชันที่ใช้อยู่เป็นเรื่องที่ง่าย<br>ในการทำงานกับทั้ง bech32 และ bech32m เราจะพิจารณากฎการเข้ารหัสและการแยกวิเคราะห์สำหรับ address บิตคอยน์แบบ bech32m เนื่องจากพวกมันครอบคลุมความสามารถในการแยกวิเคราะห์บน address แบบ bech32 และเป็นรูปแบบ address ที่แนะนำในปัจจุบันสำหรับกระเป๋าเงินบิตคอยน์</p>
<blockquote>
<p>ข้อความจากหลาม: คือผมว่าตรงนี้เขาเขียนไม่รู้เรื่อง แต่เดาว่าเขาน่าจะสื่อว่า เราควรเรียนรู้วิธีการทำงานกับ bech32m เพราะมันเป็นรูปแบบที่แนะนำให้ใช้ในปัจจุบัน และมันมีข้อดีเพราะbech32m สามารถรองรับการอ่าน address แบบ bech32 แบบเก่าได้ด้วย ง่ายๆ คือ ถ้าคุณเรียนรู้วิธีทำงานกับ bech32m คุณจะสามารถทำงานกับทั้ง bech32m และ bech32 ได้ทั้งสองแบบ</p>
</blockquote>
<p>bech32m address ริ่มต้นด้วยส่วนที่มนุษย์อ่านได้ (Human Readable Part: HRP) BIP173 มีกฎสำหรับการสร้าง HRP ของคุณเอง แต่สำหรับบิตคอยน์ คุณเพียงแค่จำเป็นต้องรู้จัก HRP ที่ถูกเลือกไว้แล้วตามที่แสดงในตารางข้างล่างนี้</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641071606-YAKIHONNES3.png" alt="image"></p>
<p>ส่วน HRP ตามด้วยตัวคั่น ซึ่งก็คือเลข "1" ในข้อเสนอก่อนหน้านี้สำหรับตัวคั่นโปรโตคอลได้ใช้เครื่องหมายทวิภาค (colon) แต่ระบบปฏิบัติการและแอปพลิเคชันบางตัวที่อนุญาตให้ผู้ใช้ดับเบิลคลิกคำเพื่อไฮไลต์สำหรับการคัดลอกและวางนั้นจะไม่ขยายการไฮไลต์ไปถึงและผ่านเครื่องหมายทวิภาค</p>
<p>การใช้ตัวเลขช่วยให้มั่นใจได้ว่าการไฮไลต์ด้วยดับเบิลคลิกจะทำงานได้กับโปรแกรมใดๆ ที่รองรับสตริง bech32m โดยทั่วไป (ซึ่งรวมถึงตัวเลขอื่นๆ ด้วย) เลข "1" ถูกเลือกเพราะสตริง bech32 ไม่ได้ใช้เลข 1 ในกรณีอื่น เพื่อป้องกันการแปลงโดยไม่ตั้งใจระหว่างเลข "1" กับตัวอักษรพิมพ์เล็ก "l"</p>
<p>และส่วนอื่นของ bech32m address เรียกว่า "ส่วนข้อมูล" (data part) ซึ่งประกอบด้วยสามองค์ประกอบ:</p>
<ul>
<li><strong>Witness version:</strong> ไบต์ถัดไปหลังจากตัวคั่นตัวอักษรนี้แทนเวอร์ชันของ segwit ตัวอักษร "q" คือการเข้ารหัสของ "0" สำหรับ segwit v0 ซึ่งเป็นเวอร์ชันแรกของ segwit ที่มีการแนะนำที่อยู่ bech32 ตัวอักษร "p" คือการเข้ารหัสของ "1" สำหรับ segwit v1 (หรือเรียกว่า taproot) ซึ่งเริ่มมีการใช้งาน bech32m มีเวอร์ชันที่เป็นไปได้ทั้งหมด 17 เวอร์ชันของ segwit และสำหรับ Bitcoin จำเป็นต้องให้ไบต์แรกของส่วนข้อมูล bech32m ถอดรหัสเป็นตัวเลข 0 ถึง 16 (รวมทั้งสองค่า)</li>
<li><strong>Witness program:</strong> คือตำแหน่งหลังจาก witnessversion ตั้งแต่ตำแหน่ง 2 ถึง 40 Byte สำหรับ segwit v0 นี้ต้องมีความยาว 20 หรือ 32 Byte ไม่สามารถ<br>ffมีขนาดอื่นได้ สำหรับ segwit v1 ความยาวเดียวที่ถูกกำหนดไว้ ณ เวลาที่เขียนนี้คือ 32 ไบต์ แต่อาจมีการกำหนดความยาวอื่น ๆ ได้ในภายหลัง</li>
<li><strong>Checksum:</strong> มีความยาว 6 ตัวอักษร โดยส่วนนี้ถูกสร้างขึ้นโดยใช้รหัส BCH ซึ่งเป็นประเภทของรหัสแก้ไขข้อผิดพลาด (error corection code) (แต่อย่างไรก็ตาม สำหรับ address บิตคอยน์ เราจะเห็นในภายหลังว่าเป็นสิ่งสำคัญที่จะใช้ checksum เพื่อการตรวจจับข้อผิดพลาดเท่านั้น—ไม่ใช่การแก้ไข</li>
</ul>
<p>ในส่วนต่อไปหลังจากนี้เราจะลองสร้าง address แบบ bech32 และ bech32m สำหรับตัวอย่างทั้งหมดต่อไปนี้ เราจะใช้โค้ดอ้างอิง bech32m สำหรับ Python</p>
<p>เราจะเริ่มด้วยการสร้างสคริปต์เอาต์พุตสี่ตัว หนึ่งตัวสำหรับแต่ละเอาต์พุต segwit ที่แตกต่างกันที่ใช้ในช่วงเวลาของการเผยแพร่ บวกกับอีกหนึ่งตัวสำหรับเวอร์ชัน segwit ในอนาคตที่ยังไม่มีความหมายที่กำหนดไว้ สคริปต์เหล่านี้แสดงอยู่ในตารางข้างล่างนี้<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641245046-YAKIHONNES3.png" alt="image"></p>
<p>สำหรับเอาต์พุต P2WPKH witness program มีการผูก commitment ที่สร้างขึ้นในลักษณะเดียวกันกับ P2PKH ที่เห็นใน Legacy Addresses for P2PKH โดย public key ถูกส่งเข้าไปในฟังก์ชันแฮช SHA256 ไดเจสต์ขนาด 32 ไบต์ที่ได้จะถูกส่งเข้าไปในฟังก์ชันแฮช RIPEMD-160 ไดเจสต์ของฟังก์ชันนั้น จะถูกวางไว้ใน witness program</p>
<p>สำหรับเอาต์พุตแบบ pay to witness script hash (P2WSH) เราไม่ได้ใช้อัลกอริทึม P2SH แต่เราจะนำสคริปต์ ส่งเข้าไปในฟังก์ชันแฮช SHA256 และใช้ไดเจสต์ขนาด 32 ไบต์ของฟังก์ชันนั้นใน witness program สำหรับ P2SH ไดเจสต์ SHA256 จะถูกแฮชอีกครั้งด้วย RIPEMD-160 ซึ่งแน่นอนว่าอาจจะไม่ปลอดภัย ในบางกรณี สำหรับรายละเอียด ดูที่ P2SH Collision Attacks ผลลัพธ์ของการใช้ SHA256 โดยไม่มี RIPEMD-160 คือ การผูกพันแบบ P2WSH มีขนาด 32 ไบต์ (256 บิต) แทนที่จะเป็น 20 ไบต์ (160 บิต)</p>
<p>สำหรับเอาต์พุตแบบ pay-to-taproot (P2TR) witness program คือจุดบนเส้นโค้ง secp256k1 มันอาจเป็น public key แบบธรรมดา แต่ในกรณีส่วนใหญ่มันควรเป็น public key ที่ผูกพันกับข้อมูลเพิ่มเติมบางอย่าง เราจะเรียนรู้เพิ่มเติมเกี่ยวกับการผูกพันนั้นในหัวข้อของ taproot</p>
<p>สำหรับตัวอย่างของเวอร์ชัน segwit ในอนาคต เราเพียงแค่ใช้หมายเลขเวอร์ชัน segwit ที่สูงที่สุดที่เป็นไปได้ (16) และ witness program ที่มีขนาดเล็กที่สุดที่อนุญาต (2 ไบต์) โดยมีค่าเป็นศูนย์ (null value)</p>
<p>เมื่อเรารู้หมายเลขเวอร์ชันและ witness program แล้ว เราสามารถแปลงแต่ละอย่างให้เป็น bech32 address ได้ โดยการใช้ไลบรารีอ้างอิง bech32m สำหรับ Python เพื่อสร้าง address เหล่านั้นอย่างรวดเร็ว และจากนั้นมาดูอย่างละเอียดว่าเกิดอะไรขึ้น:</p>
<pre><code>$ github=" https://raw.githubusercontent.com"
$ wget $github/sipa/bech32/master/ref/python/segwit_addr.py
$ python
&gt;&gt;&gt; from segwit_addr import *
&gt;&gt;&gt; from binascii import unhexlify
&gt;&gt;&gt; help(encode)
encode(hrp, witver, witprog)
    Encode a segwit address.
&gt;&gt;&gt; encode('bc', 0, unhexlify('2b626ed108ad00a944bb2922a309844611d25468'))
'bc1q9d3xa5gg45q2j39m9y32xzvygcgay4rgc6aaee'
&gt;&gt;&gt; encode('bc', 0,
unhexlify('648a32e50b6fb7c5233b228f60a6a2ca4158400268844c4bc295ed5e8c3d626f'))
'bc1qvj9r9egtd7mu2gemy28kpf4zefq4ssqzdzzycj7zjhk4arpavfhsct5a3p'
&gt;&gt;&gt; encode('bc', 1,
unhexlify('2ceefa5fa770ff24f87c5475d76eab519eda6176b11dbe1618fcf755bfac5311'))
'bc1p9nh05ha8wrljf7ru236awm4t2x0d5ctkkywmu9sclnm4t0av2vgs4k3au7'
&gt;&gt;&gt; encode('bc', 16, unhexlify('0000'))
'bc1sqqqqkfw08p'
</code></pre>
<p>หากเราเปิดไฟล์ segwit_addr.py และดูว่าโค้ดกำลังทำอะไร สิ่งแรกที่เราจะสังเกตเห็นคือความแตกต่างเพียงอย่างเดียวระหว่าง bech32 (ที่ใช้สำหรับ segwit v0) และ bech32m (ที่ใช้สำหรับเวอร์ชัน segwit รุ่นหลัง) คือค่าคงที่:</p>
<pre><code>BECH32_CONSTANT = 1
BECH32M_CONSTANT = 0x2bc830a3
</code></pre>
<p>และในส่วนต่อไป เราจะเห็นโค้ดที่สร้าง checksum ในขั้นตอนสุดท้ายของการสร้าง checksum ค่าคงที่ที่เหมาะสมถูกรวมเข้ากับข้อมูลอื่น ๆ โดยใช้การดำเนินการ xor ค่าเดียวนั้นคือความแตกต่างเพียงอย่างเดียวระหว่าง bech32 และ bech32m</p>
<p>เมื่อสร้าง checksum แล้ว อักขระ 5 บิตแต่ละตัวในส่วนข้อมูล (รวมถึง witness version, witness program และ checksum) จะถูกแปลงเป็นตัวอักษรและตัวเลข</p>
<p>สำหรับการถอดรหัสกลับเป็นสคริปต์เอาต์พุต เราทำงานย้อนกลับ ลองใช้ไลบรารีอ้างอิงเพื่อถอดรหัส address สอง address ของเรา:</p>
<pre><code>&gt;&gt;&gt; help(decode)
decode(hrp, addr)
    Decode a segwit address.
&gt;&gt;&gt; _ = decode("bc", "bc1q9d3xa5gg45q2j39m9y32xzvygcgay4rgc6aaee")
&gt;&gt;&gt;  _[0], bytes(_[1]).hex()
(0, '2b626ed108ad00a944bb2922a309844611d25468')
&gt;&gt;&gt; _ = decode("bc",
        "bc1p9nh05ha8wrljf7ru236awm4t2x0d5ctkkywmu9sclnm4t0av2vgs4k3au7")
&gt;&gt;&gt; _[0], bytes(_[1]).hex()
(1, '2ceefa5fa770ff24f87c5475d76eab519eda6176b11dbe1618fcf755bfac5311')
</code></pre>
<p>เราได้รับทั้ง witness version และ witness program กลับมา สิ่งเหล่านี้สามารถแทรกลงในเทมเพลตสำหรับสคริปต์เอาต์พุตของเรา:</p>
<pre><code>&lt;version&gt; &lt;program&gt;
</code></pre>
<p>ตัวอย่างเช่น:</p>
<pre><code>OP_0 2b626ed108ad00a944bb2922a309844611d25468
OP_1 2ceefa5fa770ff24f87c5475d76eab519eda6176b11dbe1618fcf755bfac5311
</code></pre>
<blockquote>
<p>คำเตือน: ข้อผิดพลาดที่อาจเกิดขึ้นที่ควรระวังคือ witness version ที่มีค่า 0 ใช้สำหรับ OP_0 ซึ่งใช้ไบต์ 0x00—แต่เวอร์ชัน witness ที่มีค่า 1 ใช้ OP_1 ซึ่งเป็นไบต์ 0x51 เวอร์ชัน witness 2 ถึง 16 ใช้ไบต์ 0x52 ถึง 0x60 ตามลำดับ</p>
</blockquote>
<p>เมื่อทำการเขียนโค้ดเพื่อเข้ารหัสหรือถอดรหัส bech32m เราขอแนะนำอย่างยิ่งให้คุณใช้เวกเตอร์ทดสอบ (test vectors) ที่มีให้ใน BIP350 เราขอให้คุณตรวจสอบให้แน่ใจว่าโค้ดของคุณผ่านเวกเตอร์ทดสอบที่เกี่ยวข้องกับการจ่ายเงินให้กับเวอร์ชัน segwit ในอนาคตที่ยังไม่ได้รับการกำหนด สิ่งนี้จะช่วยให้ซอฟต์แวร์ของคุณสามารถใช้งานได้อีกหลายปีข้างหน้า แม้ว่าคุณอาจจะไม่สามารถเพิ่มการรองรับคุณสมบัติใหม่ ๆ ของบิตคอยน์ได้ทันทีที่คุณสมบัตินั้น ๆ เริ่มใช้งานได้</p>
<h3>Private Key Formats</h3>
<p>private key สามารถถูกแสดงได้ในหลาย ๆ รูปแบบที่ต่างกันซึ่งสามารถแปลงเป็นตัวเลขขนาด 256 bit ชุดเดียวกันได้ ดังที่เราจะแสดงให้ดูในตารางข้างล่างนี้ รูปแบบที่แตกต่างกันถูกใช้ในสถานการณ์ที่ต่างกัน รูปแบบเลขฐานสิบหก (Hexadecimal) และรูปแบบไบนารี (raw binary) ถูกใช้ภายในซอฟต์แวร์และแทบจะไม่แสดงให้ผู้ใช้เห็น WIF ถูกใช้สำหรับการนำเข้า/ส่งออกกุญแจระหว่างกระเป๋าเงินและมักใช้ในการแสดงกุญแจส่วนตัวแบบ QR code</p>
<h4>รูปแบบของ private key ในปัจจุบัน</h4>
<p>ซอฟต์แวร์กระเป๋าเงินบิตคอยน์ในยุคแรกได้สร้าง private key อิสระอย่างน้อยหนึ่งดอกเมื่อกระเป๋าเงินของผู้ใช้ใหม่ถูกเริ่มต้น เมื่อชุดกุญแจเริ่มต้นถูกใช้ทั้งหมดแล้ว กระเป๋าเงินอาจสร้าง private key เพิ่มเติม  private key แต่ละดอกสามารถส่งออกหรือนำเข้าได้ ทุกครั้งที่มีการสร้างหรือนำเข้า private key ใหม่ จะต้องมีการสร้างการสำรองข้อมูลกระเป๋าเงินใหม่ด้วย</p>
<p>กระเป๋าเงินบิตคอยน์ในยุคหลังเริ่มใช้กระเป๋าเงินแบบกำหนดได้ (deterministic wallets) ซึ่ง private key ทั้งหมดถูกสร้างจาก seed เพียงค่าเดียว กระเป๋าเงินเหล่านี้จำเป็นต้องสำรองข้อมูลเพียงครั้งเดียวเท่านั้นสำหรับการใช้งานบนเชนทั่วไป แต่อย่างไรก็ตาม หากผู้ใช้ส่งออก private key เพียงดอกเดียวจากกระเป๋าเงินเหล่านี้ และผู้โจมตีได้รับกุญแจนั้นรวมถึงข้อมูลที่ไม่ใช่ข้อมูลส่วนตัวบางอย่างเกี่ยวกับกระเป๋าเงิน พวกเขาอาจสามารถสร้างกุญแจส่วนตัวใด ๆ ในกระเป๋าเงินได้—ทำให้ผู้โจมตีสามารถขโมยเงินทั้งหมดในกระเป๋าเงินได้ นอกจากนี้ ยังไม่สามารถนำเข้ากุญแจสู่กระเป๋าเงินแบบกำหนดได้ นี่หมายความว่าแทบไม่มีกระเป๋าเงินสมัยใหม่ที่รองรับความสามารถในการส่งออกหรือนำเข้ากุญแจเฉพาะดอก ข้อมูลในส่วนนี้มีความสำคัญหลัก ๆ สำหรับผู้ที่ต้องการความเข้ากันได้กับกระเป๋าเงินบิตคอยน์ในยุคแรก ๆ</p>
<p>รูปแบบของ private key (รูปแบบการเข้ารหัส)<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641438678-YAKIHONNES3.png" alt="image"><br>private key เดียวกันในแต่ละ format<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641472205-YAKIHONNES3.png" alt="image"><br>รูปแบบการแสดงผลทั้งหมดเหล่านี้เป็นวิธีต่างๆ ในการแสดงเลขจำนวนเดียวกัน private key เดียวกัน พวกมันดูแตกต่างกัน แต่รูปแบบใดรูปแบบหนึ่งสามารถแปลงไปเป็นรูปแบบอื่นได้อย่างง่ายดาย</p>
<h3>Compressed Private Keys</h3>
<p>คำว่า compressed private key ที่ใช้กันทั่วไปนั้นเป็นคำที่เรียกผิด เพราะเมื่อ private key ถูกส่งออกไปในรูปแบบ WIF-compressed มันจะมีความยาวมากกว่า private key แบบ uncompressed 1 Byte (เลข 01 ในช่อง Hex-compressed ในตารางด้านล่างนี้) ซึ่งบ่งบอกว่า private key ตัวนี้ มาจากกระเป๋าเงินรุ่นใหม่และควรใช้เพื่อสร้าง compressed public key เท่านั้น</p>
<p>private key เองไม่ได้ถูกบีบอัดและไม่สามารถบีบอัดได้ คำว่า compressed private key จริงๆ แล้วหมายถึง " private key ซึ่งควรใช้สร้าง compressed public key เท่านั้น" ในขณะที่ uncompressed private key จริงๆ แล้วหมายถึง “private key ซึ่งควรใช้สร้าง uncompressed public key เท่านั้น” คุณควรใช้เพื่ออ้างถึงรูปแบบการส่งออกเป็น "WIF-compressed" หรือ "WIF" เท่านั้น และไม่ควรอ้างถึง private key ว่า "บีบอัด" เพื่อหลีกเลี่ยงความสับสนต่อไป</p>
<p>ตารางนี้แสดงกุญแจเดียวกันที่ถูกเข้ารหัสในรูปแบบ WIF และ WIF-compressed</p>
<p>ตัวอย่าง: กุญแจเดียวกัน แต่รูปแบบต่างกัน<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641542538-YAKIHONNES3.png" alt="image"></p>
<p>สังเกตว่ารูปแบบ Hex-compressed มีไบต์เพิ่มเติมหนึ่งไบต์ที่ท้าย (01 ในเลขฐานสิบหก) ในขณะที่คำนำหน้าเวอร์ชันการเข้ารหัสแบบ base58 เป็นค่าเดียวกัน (0x80) สำหรับทั้งรูปแบบ WIF และ WIF-compressed การเพิ่มหนึ่งไบต์ที่ท้ายของตัวเลขทำให้อักขระตัวแรกของการเข้ารหัสแบบ base58 เปลี่ยนจาก 5 เป็น K หรือ L</p>
<p>คุณสามารถคิดถึงสิ่งนี้เหมือนกับความแตกต่างของการเข้ารหัสเลขฐานสิบระหว่างตัวเลข 100 และตัวเลข 99 ในขณะที่ 100 มีความยาวมากกว่า 99 หนึ่งหลัก มันยังมีคำนำหน้าเป็น 1 แทนที่จะเป็นคำนำหน้า 9 เมื่อความยาวเปลี่ยนไป มันส่งผลต่อคำนำหน้า ในระบบ base58 คำนำหน้า 5 เปลี่ยนเป็น K หรือ L เมื่อความยาวของตัวเลขเพิ่มขึ้นหนึ่งไบต์</p>
<blockquote>
<p>TIPจากหลาม: ผมว่าเขาเขียนย่อหน้านี้ไม่ค่อยรู้เรื่อง แต่ความหมายมันจะประมาณว่า เหมือนถ้าเราต้องการเขียนเลข 100 ในฐาน 10 เราต้องใช้สามตำแหน่ง 100 แต่ถ้าใช้ฐาน 16 เราจะใช้แค่ 2 ตำแหน่งคือ 64 ซึ่งมีค่าเท่ากัน</p>
</blockquote>
<p>ถ้ากระเป๋าเงินบิตคอยน์สามารถใช้ compressed public key ได้ มันจะใช้ในทุกธุรกรรม private key ในกระเป๋าเงินจะถูกใช้เพื่อสร้างจุด public key บนเส้นโค้ง ซึ่งจะถูกบีบอัด compressed public key จะถูกใช้เพื่อสร้าง address และ address เหล่านี้จะถูกใช้ในธุรกรรม เมื่อส่งออก private key จากกระเป๋าเงินใหม่ที่ใช้ compressed public key WIF จะถูกปรับเปลี่ยน โดยเพิ่มต่อท้ายขนาด 1 ไบต์ 01 ให้กับ private key ที่ถูกเข้ารหัสแบบ base58check ที่ได้จะเรียกว่า "WIF-compressed" และจะขึ้นต้นด้วยอักษร K หรือ L แทนที่จะขึ้นต้นด้วย "5" เหมือนกับกรณีของคีย์ที่เข้ารหัสแบบ WIF (ไม่บีบอัด) จากกระเป๋าเงินรุ่นเก่า</p>
<h3>Advanced Keys and Addresses</h3>
<p>ในส่วนต่อไปนี้ เราจะดูรูปแบบของคีย์และ address เช่น vanity addresses และ paper wallets </p>
<h4>vanity addresses</h4>
<p>vanity addresses หรือ addresses แบบกำหนดเอง คือ address ที่มีข้อความที่มนุษย์อ่านได้และสามารถใช้งานได้จริง ตัวอย่างเช่น 1LoveBPzzD72PUXLzCkYAtGFYmK5vYNR33 อย่างที่เห็นว่ามันเป็น address ที่ถูกต้องซึ่งมีตัวอักษรเป็นคำว่า Love เป็นตัวอักษร base58 สี่ตัวแรก addresses แบบกำหนดเองต้องอาศัยการสร้างและทดสอบ private key หลายพันล้านตัวจนกว่าจะพบ address ที่มีรูปแบบตามที่ต้องการ แม้ว่าจะมีการปรับปรุงบางอย่างในอัลกอริทึมการสร้าง addresses แบบกำหนดเอง แต่กระบวนการนี้ต้องใช้การสุ่มเลือก private key มาสร้าง public key และนำไปสร้าง address และตรวจสอบว่าตรงกับรูปแบบที่ต้องการหรือไม่ โดยทำซ้ำหลายพันล้านครั้งจนกว่าจะพบที่ตรงกัน</p>
<p>เมื่อพบ  address ที่ตรงกับรูปแบบที่ต้องการแล้ว  private key ที่ใช้สร้าง address นั้นสามารถใช้โดยเจ้าของเพื่อใช้จ่ายบิตคอยน์ได้เหมือนกับ address อื่น ๆ ทุกประการ address ที่กำหนดเองไม่ได้มีความปลอดภัยน้อยกว่าหรือมากกว่าที่ address ๆ พวกมันขึ้นอยู่กับการเข้ารหัสเส้นโค้งรูปวงรี (ECC) และอัลกอริทึมแฮชที่ปลอดภัย (SHA) เหมือนกับ address อื่น ๆ คุณไม่สามารถค้นหา private key ของ address ที่ขึ้นต้นด้วยรูปแบบที่กำหนดเองได้ง่ายกว่า address อื่น ๆ</p>
<p>ตัวอย่างเช่น ยูจีเนียเป็นผู้อำนวยการการกุศลเพื่อเด็กที่ทำงานในฟิลิปปินส์ สมมติว่ายูจีเนียกำลังจัดการระดมทุนและต้องการใช้ address ที่กำหนดเองเพื่อประชาสัมพันธ์การระดมทุน ยูจีเนียจะสร้าง address ที่กำหนดเองที่ขึ้นต้นด้วย "1Kids" เพื่อส่งเสริมการระดมทุนเพื่อการกุศลสำหรับเด็ก มาดูกันว่า address ที่กำหนดเองนี้จะถูกสร้างขึ้นอย่างไรและมีความหมายอย่างไรต่อความปลอดภัยของการกุศลของยูจีเนีย</p>
<h4>การสร้าง address ที่กำหนดเอง</h4>
<p>ควรเข้าใจว่า address ของบิตคอยน์เป็นเพียงตัวเลขที่แสดงด้วยสัญลักษณ์ในรูปแบบตัวอักษร base58 เท่านั้น เพราะฉะนั้นแล้ว การค้นหารูปแบบเช่น "1Kids" สามารถมองได้ว่าเป็นการค้นหาที่อยู่ในช่วงตั้งแต่ 1Kids11111111111111111111111111111 ถึง 1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzz มีประมาณ 5829 (ประมาณ 1.4 × 1051) address ในช่วงนั้น ทั้งหมดขึ้นต้นด้วย "1Kids" ตารางด้านล่างนี้แสดงช่วงของ address ที่มีคำนำหน้า 1Kids<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641701955-YAKIHONNES3.png" alt="image">ลองดูรูปแบบ "1Kids" ในรูปของตัวเลขและดูว่าเราอาจพบรูปแบบนี้ใน bitcoin address บ่อยแค่ไหน โดยตารางข้างล่างนี้แสดงให้เห็นถีงคอมพิวเตอร์เดสก์ท็อปทั่วไปที่ไม่มีฮาร์ดแวร์พิเศษสามารถค้นหาคีย์ได้ประมาณ 100,000 คีย์ต่อวินาที</p>
<p>ความถี่ของ address ที่กำหนดเอง (1KidsCharity) และเวลาค้นหาเฉลี่ยบนคอมพิวเตอร์เดสก์ท็อป</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1742641750817-YAKIHONNES3.png" alt="image"></p>
<p>ดังที่เห็นได้ ยูจีเนียคงไม่สามารถสร้าง address แบบกำหนดเอง "1KidsCharity" ได้ในเร็ว ๆ นี้ แม้ว่าเธอจะมีคอมพิวเตอร์หลายพันเครื่องก็ตาม ทุกตัวอักษรที่เพิ่มขึ้นจะเพิ่มความยากขึ้น 58 เท่า รูปแบบที่มีมากกว่า 7 ตัวอักษรมักจะถูกค้นพบโดยฮาร์ดแวร์พิเศษ เช่น คอมพิวเตอร์เดสก์ท็อปที่สร้างขึ้นเป็นพิเศษที่มีหน่วยประมวลผลกราฟิก (GPUs) หลายตัว การค้นหา address แบบกำหนดเองบนระบบ GPU เร็วกว่าบน CPU ทั่วไปหลายเท่า</p>
<p>อีกวิธีหนึ่งในการหา address แบบกำหนดเองคือการจ้างงานไปยังกลุ่มคนขุด vanity addresses กลุ่มคนขุดvanity addresses เป็นบริการที่ให้ผู้ที่มีฮาร์ดแวร์ที่เร็วได้รับบิตคอยน์จากการค้นหา vanity addresses ให้กับผู้อื่น ยูจีเนียสามารถจ่ายค่าธรรมเนียมเพื่อจ้างงานการค้นหา vanity addresses ที่มีรูปแบบ 7 ตัวอักษรและได้ผลลัพธ์ในเวลาเพียงไม่กี่ชั่วโมงแทนที่จะต้องใช้ CPU ค้นหาเป็นเดือน ๆ</p>
<p>การสร้างที่ address แบบกำหนดเองเป็นการใช้วิธีการแบบ brute-force (ลองทุกความเป็นไปได้): ลองใช้คีย์สุ่ม ตรวจสอบ address ที่ได้ว่าตรงกับรูปแบบที่ต้องการหรือไม่ และทำซ้ำจนกว่าจะสำเร็จ</p>
<h4>ความปลอดภัยและความเป็นส่วนตัวของ address แบบกำหนดเอง</h4>
<p>address แบบกำหนดเองเคยเป็นที่นิยมในช่วงแรก ๆ ของบิตคอยน์ แต่แทบจะหายไปจากการใช้งานทั้งหมดในปี 2023 มีสาเหตุที่น่าจะเป็นไปได้สองประการสำหรับแนวโน้มนี้:</p>
<ul>
<li><p><strong>Deterministic wallets:</strong> ดังที่เราเห็นในพาร์ทของการกู้คืน การที่จะสำรองคีย์ทุกตัวในกระเป๋าเงินสมัยใหม่ส่วนใหญ่นั้น ทำเพียงแค่จดคำหรือตัวอักษรไม่กี่ตัว ซึ่งนี่เป็นผลจากการสร้างคีย์ทุกตัวในกระเป๋าเงินจากคำหรือตัวอักษรเหล่านั้นโดยใช้อัลกอริทึมแบบกำหนดได้ จึงไม่สามารถใช้ address แบบกำหนดเองกับ Deterministic wallets ได้ เว้นแต่ผู้ใช้จะสำรองข้อมูลเพิ่มเติมสำหรับ address แบบกำหนดเองทุก address ที่พวกเขาสร้าง ในทางปฏิบัติแล้วกระเป๋าเงินส่วนใหญ่ที่ใช้การสร้างคีย์แบบกำหนดได้ โดยไม่อนุญาตให้นำเข้าคีย์ส่วนตัวหรือการปรับแต่งคีย์จากโปรแกรมสร้าง address ที่กำหนดเอง</p>
</li>
<li><p><strong>การหลีกเลี่ยงการใช้ address ซ้ำซ้อน:</strong> การใช้ address แบบกำหนดเองเพื่อรับการชำระเงินหลายครั้งไปยัง address เดียวกันจะสร้างความเชื่อมโยงระหว่างการชำระเงินทั้งหมดเหล่านั้น นี่อาจเป็นที่ยอมรับได้สำหรับยูจีเนียหากองค์กรไม่แสวงหาผลกำไรของเธอจำเป็นต้องรายงานรายได้และค่าใช้จ่ายต่อหน่วยงานภาษีอยู่แล้ว แต่อย่างไรก็ตาม มันยังลดความเป็นส่วนตัวของคนที่จ่ายเงินให้ยูจีเนียหรือรับเงินจากเธอด้วย ตัวอย่างเช่น อลิซอาจต้องการบริจาคโดยไม่เปิดเผยตัวตน และบ็อบอาจไม่ต้องการให้ลูกค้ารายอื่นของเขารู้ว่าเขาให้ราคาส่วนลดแก่ยูจีเนีย</p>
</li>
</ul>
<p>เราไม่คาดว่าจะเห็น address แบบกำหนดเองมากนักในอนาคต เว้นแต่ปัญหาที่กล่าวมาก่อนหน้านี้จะได้รับการแก้ไข</p>
<h3>Paper Wallets</h3>
<p>paper wallet หรือก็คือ private key ที่พิมพ์ลงในกระดาษ และโดยทั่วไปแล้วมักจะมีข้อมูลของ public key หรือ address บนกระดาษนั้นด้วยแม้ว่าจริง ๆ แล้วมันจะสามารถคำนวณได้ด้วย private key ก็ตาม</p>
<blockquote>
<p>คำเตือน: paper wallet เป็นเทคโนโลยีที่ล้าสมัยแล้วและอันตรายสำหรับผู้ใช้ส่วนใหญ่ เพราะเป็นเรื่องยากที่จะสร้างมันอย่างปลอดภัย โดยเฉพาะอย่างยิ่งความเป็นไปได้ที่โค้ดที่ใช้สร้างอาจถูกแทรกแซงด้วยผู้ไม่ประสงค์ดี และอาจจะทำให้ผู้ใช้โดนขโมยบิตคอยน์ทั้งหมดไปได้ paper wallet ถูกแสดงที่นี่เพื่อวัตถุประสงค์ในการให้ข้อมูลเท่านั้นและไม่ควรใช้สำหรับเก็บบิตคอยน์</p>
</blockquote>
<p><img src="https://github.com/bitcoinbook/bitcoinbook/raw/develop/images/mbc3_0410.png" alt="image"></p>
<p>paper wallet ได้ถูกออกแบบมาเพื่อเป็นของขวัญและมีธีมตามฤดูกาล เช่น คริสต์มาสและปีใหม่ ส่วนเหตุผลอื่น ๆ ถูกออกแบบเพื่อการเก็บรักษาในตู้นิรภัยของธนาคารหรือตู้เซฟโดยมี private key ถูกซ่อนไว้ในบางวิธี ไม่ว่าจะด้วยสติกเกอร์แบบขูดที่ทึบแสงหรือพับและปิดผนึกด้วยแผ่นฟอยล์กันการงัดแงะ ส่วนการออกแบบอื่น ๆ มีสำเนาเพิ่มเติมของคีย์และ address ในรูปแบบของตอนฉีกที่แยกออกได้คล้ายกับตั๋ว ช่วยให้คุณสามารถเก็บสำเนาหลายชุดเพื่อป้องกันจากไฟไหม้ น้ำท่วม หรือภัยพิบัติทางธรรมชาติอื่น ๆ</p>
<p>จากการออกแบบเดิมของบิตคอยน์ที่เน้น public key ไปจนถึง address และสคริปต์สมัยใหม่อย่าง bech32m และ pay to taproot—และแม้แต่การอัพเกรดบิตคอยน์ในอนาคต—คุณได้เรียนรู้วิธีที่โปรโตคอลบิตคอยน์อนุญาตให้ผู้จ่ายเงินระบุกระเป๋าเงินที่ควรได้รับการชำระเงินของพวกเขา แต่เมื่อเป็นกระเป๋าเงินของคุณเองที่รับการชำระเงิน คุณจะต้องการความมั่นใจว่าคุณจะยังคงเข้าถึงเงินนั้นได้แม้ว่าจะเกิดอะไรขึ้นกับข้อมูลกระเป๋าเงินของคุณ ในบทต่อไป เราจะดูว่ากระเป๋าเงินบิตคอยน์ถูกออกแบบอย่างไรเพื่อปกป้องเงินทุนจากภัยคุกคามหลากหลายรูปแบบ</p>
]]></itunes:summary>
      <itunes:image href="https://image.nostr.build/0e43fa1dec4fb36a9b30035d0e83cf3759ece78d64813fc0b5182c5a62dd6e34.jpg"/>
      </item>
      
      <item>
      <title><![CDATA[สรุป Mastering Bitcoin: Programming the Open Blockchain บทที่ 3]]></title>
      <description><![CDATA[ต้นฉบับแบบว่ายาวไปไม่อ่าน อ่านละปวดหัว มาดูสรุปดีกว่า แต่ดูทรงปวดหัวกว่าเดิม งั้นไปอ่านต้นฉบับล้ากานนนนนนนน]]></description>
             <itunes:subtitle><![CDATA[ต้นฉบับแบบว่ายาวไปไม่อ่าน อ่านละปวดหัว มาดูสรุปดีกว่า แต่ดูทรงปวดหัวกว่าเดิม งั้นไปอ่านต้นฉบับล้ากานนนนนนนน]]></itunes:subtitle>
      <pubDate>Sat, 08 Mar 2025 07:04:36 GMT</pubDate>
      <link>https://learnbn.npub.pro/post/zhbi56eoe8gyr4ti9n5le/</link>
      <comments>https://learnbn.npub.pro/post/zhbi56eoe8gyr4ti9n5le/</comments>
      <guid isPermaLink="false">naddr1qq2456rzfy6nve20g5uywk2jx36yjw2wx4xx2q3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65w9y4smc</guid>
      <category>ลองฟอร์มของไดโน</category>
      
        <media:content url="https://image.nostr.build/29da6e18e9b5a07f5cd1b52ef33ccaad9e0d438a203316facaed75bc6b83de6c.jpg" medium="image"/>
        <enclosure 
          url="https://image.nostr.build/29da6e18e9b5a07f5cd1b52ef33ccaad9e0d438a203316facaed75bc6b83de6c.jpg" length="0" 
          type="image/jpeg" 
        />
      <noteId>naddr1qq2456rzfy6nve20g5uywk2jx36yjw2wx4xx2q3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65w9y4smc</noteId>
      <npub>npub10yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2syrhcl3</npub>
      <dc:creator><![CDATA[Learning_BTC&NOSTR]]></dc:creator>
      <content:encoded><![CDATA[<h2>Bitcoin Core: The Reference Implementation</h2>
<hr>
<p>ผู้คนจะยอมรับเงินใด ๆ เพื่อแลกเปลี่ยนกับสินค้าและบริการก็ต่อเมื่อคนนั้น ๆ เชื่อว่าเงินนี้จะมีมูลค่าในอนาคต เงินปลอมหรือเงินที่เสื่อมค่าโดยไม่คาดคิดนั้นอาจไม่สามารถใช้ได้ในอนาคต ดังนั้นทุกคนที่รับบิตคอยน์จึงมีแรงจูงใจที่แข็งแกร่งในการตรวจสอบความถูกต้องของบิตคอยน์ที่พวกเขาได้รับ ระบบของบิตคอยน์นั้นถูกออกแบบมาให้เข้าถึง, ป้องกันการปลอมแปลง, การเสื่อมค่า และปัญหาสำคัญอื่น ๆ ได้อย่างสมบูรณ์ได้ด้วยคอมพิวเตอร์ทั่วไป โดยซอฟต์แวร์ที่ให้ฟังก์ชันนี้เรียกว่า Full node ซึ่งทำหน้าที่ตรวจสอบธุรกรรมบิตคอยน์ทุกครั้งที่ได้รับการยืนยันตามกฎของระบบ นอกจากนี้ Full node ยังสามารถให้เครื่องมือและข้อมูลเพื่อทำความเข้าใจการทำงานของบิตคอยน์และสภาพปัจจุบันของเครือข่าย</p>
<p>ในบทนี้เอง เราจะทำการติดตั้ง Bitcoin Core ซึ่งเป็นซอฟต์แวร์ที่ผู้ใช้งาน Full node ส่วนใหญ่เลือกใช้เพื่อเป็นประตูบานแรกในการเข้าถึงระบบนิเวศของบิตคอยน์ เราจะตรวจสอบบล็อก ธุรกรรม และข้อมูลอื่น ๆ จากโหนดของคุณ ซึ่งเป็นข้อมูลที่เชื่อถือได้ (ไม่ใช่เพราะหน่วยงานทรงอำนาจกำหนดให้เป็นเช่นนั้น) แต่เป็นเพราะโหนดของคุณได้ตรวจสอบข้อมูลนั้นอย่างอิสระ ตลอดเนื้อหาที่เหลือในหนังสือเล่มนี้ เราจะใช้ Bitcoin Core เพื่อสร้างและตรวจสอบข้อมูลที่เกี่ยวข้องกับบล็อกเชนและเครือข่าย</p>
<h3>จาก Bitcoin สู่ Bitcoin Core</h3>
<p>บิตคอยน์เป็นโครงการโอเพ่นซอร์ส โดยซอร์สโค้ดทั้งหมดก็สามารถดาวน์โหลดและใช้งานได้ฟรีภายใต้ใบอณุญาตแบบเปิด (MIT License) นอกจากจะเป็นโอเพ่นซอร์สแล้วบิตคอยน์ยังได้รับการพัฒนาโดยชุมชนอาสาสมัครแบบเปิดกว้าง แน่นอนว่าในช่วงแรกนั้นชุมชนนี้ประกอบด้วย Satoshi Nakamoto เพียงคนเดียว แต่ภายในปี 2023 ซอร์สโค้ดของบิตคอยน์มีผู้ร่วมพัฒนามากกว่า 1,000 คน</p>
<p>เมื่อ Satoshi Nakamoto ได้สร้างซอฟแวร์บิตคอยน์ตัวนี้และพัฒนามันจนเกือบสมบูรณ์ก่อนแล้วจึงเผยแพร่เอกสารไวท์เปเปอร์ เขาน่าจะต้องการให้มั่นใจว่าการใช้งานจริงสามารถทำงานได้ก่อนเผยแพร่เอกสาร โดยซอฟต์แวร์เวอร์ชันแรกที่รู้จักในชื่อ "Bitcoin" นั้นได้รับการปรับปรุงและพัฒนามาอย่างมาก จนได้กลายเป็นสิ่งที่เรารู้จักกันในชื่อ Bitcoin Core และเพื่อแยกความแตกต่างจากการใช้งานอื่น ๆ Bitcoin Core เป็นซอฟต์แวร์ต้นแบบอ้างอิง (reference implementation) ของระบบบิตคอยน์ซึ่งแสดงวิธีการทำงานของแต่ละส่วนในเชิงเทคโนโลยี นอกจากนี้ Bitcoin Core รวมถึงการใช้งานฟังก์ชันทั้งหมดของบิตคอยน์ เช่น กระเป๋าเงิน เครื่องมือตรวจสอบธุรกรรมและบล็อก เครื่องมือสำหรับการสร้างบล็อก และส่วนต่าง ๆ ของการสื่อสารแบบ peer-to-peer ของบิตคอยน์<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1741413625372-YAKIHONNES3.png" alt="image"></p>
<h3>Bitcoin Development Environment</h3>
<p>สำหรับนักพัฒนาที่ต้องการเขียนแอปพลิเคชันเกี่ยวกับบิตคอยน์ ทั้งการตั้งค่าสภาพแวดล้อมสำหรับการพัฒนาพร้อมเครื่องมือ ไลบรารี และซอฟต์แวร์สนับสนุนเป็นสิ่งสำคัญ ซึ่งเนื้อหาในบทนี้นั้นจะเป็นเรื่องทางเทคนิคอลค่อนข้างเยอะ ในบทนี้เราจะอธิบายขั้นตอนการตั้งค่าอย่างละเอียด หากคุณพบว่าเนื้อหานี้ซับซ้อนเกินไป (และไม่ได้ต้องการตั้งค่าสภาพแวดล้อมสำหรับการพัฒนาจริง ๆ) คุณสามารถข้ามไปยังบทถัดไปที่มีเนื้อหาน้อยทางเทคนิคกว่าได้</p>
<h3>มาคอมไพล์ Bitcoin core จากซอร์สโค้ดกันเถอะ !!</h3>
<p>ซอร์สโค้ดทั้งหมดของ BItcoin Core นั้นสามารถดาวน์โหลดได้ในรูปแบบไฟล์อาร์ไคฟ์หรือโดยการโคลนที่เก็บซอร์สโค้ดจาก GitHub โดยตรง บนหน้าดาวน์โหลดของ Bitcoin Core ให้เลือกเวอร์ชันล่าสุดและดาวน์โหลดไฟล์อัดบีบของซอร์สโค้ด หรือใช้คำสั่ง Git เพื่อสร้างสำเนาซอร์สโค้ดบนเครื่องของคุณจากหน้า GitHub ของ Bitcoin</p>
<blockquote>
<p>TIP: ในตัวอย่างหลาย ๆ ส่วนของบทนี้ เราจะใช้ อินเทอร์เฟซบรรทัดคำสั่ง (Command-Line Interface - CLI) ของระบบปฏิบัติการ หรือที่เรียกว่า "shell" ซึ่งสามารถเข้าถึงได้ผ่านแอปพลิเคชัน terminal โดย shell จะแสดง พรอมต์ (prompt) เพื่อรอรับคำสั่งที่คุณพิมพ์ จากนั้นจะแสดงผลลัพธ์ออกมาแล้วรอรับคำสั่งถัดไป</p>
</blockquote>
<blockquote>
<p>TIP จากหลาม: แบบง่าย ๆ ก็คือไม่ต้องพิมพ์ $ และถ้าพิมพ์จบหนึ่งคำสั่งก็กด enter ซ่ะด้วย</p>
</blockquote>
<p>ขั้นตอนในการลง bitcoin core มีดังนี้:</p>
<ol>
<li>สั่ง git clone เพื่อทำการสร้างสำเนาของซอร์สโค้ดลงในเครื่องของเรา</li>
</ol>
<pre><code>$ git clone https://github.com/bitcoin/bitcoin.git
Cloning into 'bitcoin'...
remote: Enumerating objects: 245912, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 245912 (delta 1), reused 2 (delta 1), pack-reused 245909
Receiving objects: 100% (245912/245912), 217.74 MiB | 13.05 MiB/s, done.
Resolving deltas: 100% (175649/175649), done.
</code></pre>
<blockquote>
<p>TIP: Git เป็นระบบควบคุมเวอร์ชันแบบกระจายที่ใช้กันอย่างแพร่หลายและเป็นส่วนสำคัญในเครื่องมือของนักพัฒนาซอฟต์แวร์ คุณอาจจำเป็นต้องติดตั้งคำสั่ง git หรือส่วนต่อประสานกราฟิก (GUI) สำหรับ Git บนระบบปฏิบัติการของคุณ หากยังไม่มี </p>
</blockquote>
<ol start="2">
<li>เมื่อการโคลน Git เสร็จสมบูรณ์แล้ว คุณจะมีสำเนาท้องถิ่นครบถ้วนของที่เก็บซอร์สโค้ดในไดเรกทอรี bitcoin ให้เปลี่ยนไปยังไดเรกทอรีนี้โดยใช้คำสั่ง cd:</li>
</ol>
<pre><code>$ cd bitcoin
</code></pre>
<ol start="3">
<li>เลือก version ของ bitcoin core: โดยค่าเริ่มต้น สำเนาจองเราจะซิงโครไนซ์กับโค้ดล่าสุด ซึ่งอาจเป็นเวอร์ชันที่ไม่เสถียรหรือเบต้าของ Bitcoin ก่อนที่จะคอมไพล์โค้ด ให้เลือกเวอร์ชันเฉพาะโดยการตรวจสอบ (checkout) แท็กของการปล่อย (release tag) ซึ่งจะซิงโครไนซ์สำเนาท้องถิ่นกับสแนปช็อตของที่เก็บซอร์สโค้ดที่ระบุด้วยแท็ก แท็กเหล่านี้ถูกใช้งานโดยนักพัฒนาเพื่อระบุเวอร์ชันของโค้ดตามหมายเลขเวอร์ชัน ซึ่งทำได้โดยใช้คำสั่ง git tag</li>
</ol>
<pre><code>$ git tag
v0.1.5
v0.1.6test1
v0.10.0
...
v0.11.2
v0.11.2rc1
v0.12.0rc1
v0.12.0rc2
...
</code></pre>
<p>รายการแท็กจะแสดงทุกเวอร์ชันที่ปล่อยออกมา โดยทั่วไป release candidates (เวอร์ชันทดสอบ) จะมีต่อท้ายว่า "rc" ส่วนเวอร์ชันเสถียรที่ใช้งานในระบบ production จะไม่มีต่อท้ายอะไรเลย จากรายการด้านบน ให้เลือกเวอร์ชันที่สูงสุด ซึ่งในขณะที่เขียนบทความนี้คือ v24.0.1 เพื่อซิงโครไนซ์โค้ดท้องถิ่นกับเวอร์ชันนี้ ให้ใช้คำสั่ง:</p>
<pre><code>$ git checkout v24.0.1
Note: switching to 'v24.0.1'.
</code></pre>
<p>จากนั้นสั่ง git status เพื่อเช็คเวอร์ชัน</p>
<h3>Configuring the Bitcoin Core Build</h3>
<p>ในโค้ดของบิตคอยน์ที่เราได้ดาวน์โหลดมาในหัวข้อก่อนหน้านั้น มีเอกสารประกอบอยู่หลายไฟล์ โดยคุณสามารถดูเอกสารหลักได้จากไฟล์ README.md ในไดเรกทอรี bitcoin ในบทนี้ เราจะสร้าง daemon (เซิร์ฟเวอร์) ของ Bitcoin Core ซึ่งรู้จักกันในชื่อ bitcoind บน Linux (หรือระบบที่คล้ายกับ Unix) โดยให้ตรวจสอบคำแนะนำสำหรับการคอมไพล์ bitcoind แบบบรรทัดคำสั่งบนแพลตฟอร์มของคุณโดยอ่านไฟล์ doc/build-unix.md นอกจากนี้ ยังมีคำแนะนำสำหรับระบบอื่น ๆ ในไดเรกทอรี doc เช่น build-windows.md สำหรับ Windows จนถึงขณะนี้ คำแนะนำมีให้สำหรับ Android, FreeBSD, NetBSD, OpenBSD, macOS (OSX), Unix</p>
<p>หลังจากนั้นคุณควรตรวจสอบความต้องการเบื้องต้นในการสร้าง (build pre-requisites) ซึ่งระบุไว้ในส่วนแรกของเอกสารการสร้าง สิ่งเหล่านี้คือไลบรารีที่ต้องมีอยู่ในระบบของคุณก่อนที่คุณจะเริ่มคอมไพล์ Bitcoin หากมีไลบรารีที่จำเป็นหายไป กระบวนการสร้างจะล้มเหลวและแสดงข้อผิดพลาด หากเกิดปัญหานี้เพราะคุณพลาด pre-requisite คุณสามารถติดตั้งไลบรารีที่ขาดหายไปแล้วดำเนินการสร้างต่อจากจุดที่ค้างไว้</p>
<p>สมมุติว่า pre-requisite ถูกติดตั้งแล้ว ให้เริ่มกระบวนการสร้างโดยการสร้างชุดสคริปต์สำหรับการสร้างด้วยการรันสคริปต์ autogen.sh:</p>
<hr>
<pre><code>$ ./autogen.sh
libtoolize: putting auxiliary files in AC_CONFIG_AUX_DIR, 'build-aux'.
libtoolize: copying file 'build-aux/ltmain.sh'
libtoolize: putting macros in AC_CONFIG_MACRO_DIRS, 'build-aux/m4'.
 ...
configure.ac:58: installing 'build-aux/missing'
src/Makefile.am: installing 'build-aux/depcomp'
parallel-tests: installing 'build-aux/test-driver'
</code></pre>
<p>สคริปต์ autogen.sh นี้จะสร้างชุดสคริปต์ที่กำหนดค่าอัตโนมัติที่จะตรวจสอบระบบของคุณเพื่อค้นหาการตั้งค่าที่ถูกต้องและตรวจสอบให้แน่ใจว่ามีไลบรารีที่จำเป็นสำหรับการคอมไพล์โค้ด โดยสคริปต์ที่สำคัญที่สุดในสคริปต์เหล่านี้คือสคริปต์ configure ซึ่งมีตัวเลือกต่าง ๆ สำหรับการปรับแต่งกระบวนการสร้าง</p>
<p>ใช้ flag --help เพื่อดูตัวเลือกทั้งหมด:</p>
<pre><code>$ ./configure --help
`configure' configures Bitcoin Core 24.0.1 to adapt to many kinds of systems.
Usage: ./configure [OPTION]... [VAR=VALUE]...
...
Optional Features:
  --disable-option-checking  ignore unrecognized --enable/--with options
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --enable-silent-rules   less verbose build output (undo: "make V=1")
  --disable-silent-rules  verbose build output (undo: "make V=0")
...
</code></pre>
<p>สคริปต์ configure ช่วยให้คุณสามารถเปิดหรือปิดคุณสมบัติบางอย่างของ bitcoind ผ่านการใช้ flag --enable-FEATURE และ --disable-FEATURE โดยที่ FEATURE แทนชื่อคุณสมบัติที่ระบุในข้อความช่วยเหลือ ในบทนี้ เราจะสร้าง bitcoind ด้วยคุณสมบัติตั้งต้นทั้งหมด โดยไม่ใช้ flag การกำหนดค่าเพิ่มเติม แต่คุณควรตรวจสอบตัวเลือกเหล่านี้เพื่อเข้าใจว่ามีคุณสมบัติเพิ่มเติมอะไรบ้าง หากคุณอยู่ในสภาพแวดล้อมทางการศึกษา ห้องปฏิบัติการคอมพิวเตอร์ หรือมีข้อจำกัดในการติดตั้งโปรแกรม คุณอาจต้องติดตั้งแอปพลิเคชันไว้ในไดเรกทอรี home (เช่นโดยใช้ flag --prefix=$HOME)</p>
<p>ตัวเลือกที่มีประโยชน์สำหรับการกำหนดค่า</p>
<ul>
<li>--prefix=$HOME: เปลี่ยนตำแหน่งการติดตั้งเริ่มต้น (ซึ่งโดยปกติคือ /usr/local/) ให้เป็นไดเรกทอรี home ของคุณ หรือเส้นทางที่คุณต้องการ</li>
<li>--disable-wallet: ใช้เพื่อปิดการใช้งานฟังก์ชัน wallet แบบอ้างอิง</li>
<li>--with-incompatible-bdb: หากคุณกำลังสร้าง wallet ให้ยอมรับการใช้ไลบรารี Berkeley DB เวอร์ชันที่ไม่เข้ากันได้</li>
<li>--with-gui=no: ไม่สร้างส่วนติดต่อผู้ใช้แบบกราฟิก (GUI) ซึ่งต้องใช้ไลบรารี Qt โดยตัวเลือกนี้จะสร้างเฉพาะเซิร์ฟเวอร์และ Bitcoin Core แบบ commandline เท่านั้น</li>
</ul>
<pre><code>
</code></pre>
<p>ต่อไป ให้รันสคริปต์ configure เพื่อให้ระบบตรวจสอบไลบรารีที่จำเป็นทั้งหมดและสร้างสคริปต์สำหรับการสร้างที่ปรับแต่งให้ตรงกับระบบของคุณ:</p>
<pre><code>$ ./configure
checking for pkg-config... /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for a BSD-compatible install... /usr/bin/install -c
...
[many pages of configuration tests follow]
...
</code></pre>
<p>หากทุกอย่างดำเนินไปด้วยดี คำสั่ง configure จะสิ้นสุดด้วยการสร้างสคริปต์การสร้างที่ปรับแต่งให้กับระบบของคุณ แต่หากมีไลบรารีที่หายไปหรือเกิดข้อผิดพลาด คำสั่ง configure จะหยุดและแสดงข้อผิดพลาดแทนที่จะสร้างสคริปต์ในกรณีที่เกิดข้อผิดพลาดขึ้น สาเหตุที่พบบ่อยคือการขาดหายหรือความไม่เข้ากันของไลบรารี ให้ตรวจสอบเอกสารการสร้างอีกครั้งและติดตั้ง pre-requisite ที่ขาดไป จากนั้นรัน configure อีกครั้งเพื่อดูว่าปัญหานั้นได้รับการแก้ไขแล้วหรือไม่</p>
<h3>การสร้าง Executable ของ Bitcoin Core</h3>
<p>ต่อไป คุณจะทำการคอมไพล์ซอร์สโค้ด กระบวนการนี้อาจใช้เวลาถึงหนึ่งชั่วโมง ขึ้นอยู่กับความเร็วของ CPU และหน่วยความจำที่มีอยู่ หากเกิดข้อผิดพลาด หรือการคอมไพล์ถูกขัดจังหวะ คุณสามารถดำเนินการต่อได้โดยการพิมพ์คำสั่ง make อีกครั้ง</p>
<p>พิมพ์ make เพื่อเริ่มคอมไพล์แอปพลิเคชันที่สามารถรันได้:</p>
<pre><code>$ make
Making all in src
  CXX      bitcoind-bitcoind.o
  CXX      libbitcoin_node_a-addrdb.o
  CXX      libbitcoin_node_a-addrman.o
  CXX      libbitcoin_node_a-banman.o
  CXX      libbitcoin_node_a-blockencodings.o
  CXX      libbitcoin_node_a-blockfilter.o
[... many more compilation messages follow ...]
</code></pre>
<p>บนระบบที่มีความเร็วและมี CPU หลายคอร์ คุณอาจต้องการตั้งค่าจำนวนงานคอมไพล์แบบขนาน (parallel compile jobs) เช่น การใช้คำสั่ง make -j 2 จะใช้สองคอร์หากมีอยู่ หากทุกอย่างดำเนินไปด้วยดี Bitcoin Core จะถูกคอมไพล์เรียบร้อยแล้ว คุณควรรันชุดการทดสอบหน่วย (unit test suite) ด้วยคำสั่ง make check เพื่อให้แน่ใจว่าไลบรารีที่ลิงค์เข้าด้วยกันไม่มีข้อผิดพลาดอย่าง ขั้นตอนสุดท้ายคือการติดตั้ง executable ต่าง ๆ ลงในระบบของคุณโดยใช้คำสั่ง make install ซึ่งอาจมีการร้องขอรหัสผ่านของผู้ใช้เนื่องจากขั้นตอนนี้ต้องการสิทธิ์ผู้ดูแลระบบ:</p>
<pre><code>$ make check &amp;&amp; sudo make install
Password:
Making install in src
 ../build-aux/install-sh -c -d '/usr/local/lib'
libtool: install: /usr/bin/install -c bitcoind /usr/local/bin/bitcoind
libtool: install: /usr/bin/install -c bitcoin-cli /usr/local/bin/bitcoin-cli
libtool: install: /usr/bin/install -c bitcoin-tx /usr/local/bin/bitcoin-tx
...
</code></pre>
<p>การติดตั้งเริ่มต้นของ bitcoind จะอยู่ในไดเรกทอรี /usr/local/bin โดยคุณสามารถตรวจสอบว่า Bitcoin Core ถูกติดตั้งเรียบร้อยแล้วโดยใช้คำสั่งเพื่อตรวจสอบตำแหน่งของ executable ดังนี้:</p>
<pre><code>$ which bitcoind
/usr/local/bin/bitcoind
$ which bitcoin-cli
/usr/local/bin/bitcoin-cli
</code></pre>
<h3>มาลองรัน Bitcoin node กันเถอะ</h3>
<p>อย่างที่ได้กล่าวในบทก่อนหน้า เครือข่ายแบบเพียร์ทูเพียร์ของบิตคอยน์ประกอบด้วยเครือข่าย "โหนด" ซึ่งส่วนใหญ่รันโดยบุคคลและธุรกิจบางแห่งที่ให้บริการ ผู้ที่รันโหนดบิตคอยน์จะมีมุมมองที่ตรงและน่าเชื่อถือเกี่ยวกับบล๊อกเชนของบิตคอยน์พร้อมสำเนาข้อมูลบิตคอยน์ที่ใช้จ่ายได้ทั้งหมดซึ่งได้รับการตรวจสอบอย่างอิสระโดยระบบของตนเอง การรันโหนดทำให้คุณไม่ต้องพึ่งบุคคลที่สามในการตรวจสอบธุรกรรม นอกจากนี้การใช้โหนดบิตคอยน์เพื่อตรวจสอบธุรกรรมที่ได้รับในกระเป๋าเงินของคุณ ยังช่วยให้คุณมีส่วนร่วมในเครือข่ายบิตคอยน์และช่วยทำให้เครือข่ายมีความแข็งแกร่งมากขึ้นอีกด้วย</p>
<p>การรันโหนดต้องดาวน์โหลดและประมวลผลข้อมูลมากกว่า 500 GB ในช่วงเริ่มแรก และประมาณ 400 MB ของธุรกรรม Bitcoin ต่อวัน ตัวเลขเหล่านี้เป็นของปี 2023 และอาจเพิ่มขึ้นในอนาคต หากคุณปิดโหนดหรือหลุดจากอินเทอร์เน็ตเป็นเวลาหลายวัน โหนดของคุณจะต้องดาวน์โหลดข้อมูลที่พลาดไป ตัวอย่างเช่น หากคุณปิด Bitcoin Core เป็นเวลา 10 วัน คุณจะต้องดาวน์โหลดประมาณ 4 GB ในครั้งถัดไปที่คุณเริ่มใช้งาน</p>
<p>ขึ้นอยู่กับการเลือกของคุณว่าจะทำดัชนีธุรกรรมทั้งหมดและเก็บสำเนาบล๊อกเชนแบบเต็ม คุณอาจต้องใช้พื้นที่ดิสก์มาก - อย่างน้อย 1 TB หากคุณวางแผนจะรัน Bitcoin Core เป็นเวลาหลายปี โดยค่าเริ่มต้นโหนดบิตคอยน์ยังส่งธุรกรรมและบล็อกไปยังโหนดอื่น ๆ (เรียกว่า "เพียร์") ซึ่งจะใช้แบนด์วิดท์อัปโหลดอินเทอร์เน็ต หากการเชื่อมต่ออินเทอร์เน็ตของคุณมีขีดจำกัด มีขีดจำกัดการใช้ข้อมูลต่ำ หรือคิดค่าบริการตามข้อมูล (เมตเตอร์) คุณไม่ควรรันโหนดบิตคอยน์บนระบบนั้น หรือรันโดยจำกัดแบนด์วิดท์ (ดู การกำหนดค่าโหนด Bitcoin Core) คุณอาจเชื่อมต่อโหนดของคุณแทนไปยังเครือข่ายทางเลือก เช่น ผู้ให้บริการข้อมูลดาวเทียมฟรีอย่าง Blockstream Satellite</p>
<blockquote>
<p>Tip: Bitcoin Core เก็บสำเนาบล๊อกเชนแบบเต็ม (ตามค่าเริ่มต้น ) พร้อมธุรกรรมเกือบทั้งหมดที่เคยได้รับการยืนยันบนเครือข่าย Bitcoin ตั้งแต่เริ่มต้นในปี 2009 ชุดข้อมูลนี้มีขนาดหลายร้อย GB และจะถูกดาวน์โหลดเพิ่มขึ้นทีละน้อยในช่วงหลายชั่วโมงหรือหลายวัน ขึ้นอยู่กับความเร็ว CPU และการเชื่อมต่ออินเทอร์เน็ตของคุณ Bitcoin Core จะไม่สามารถประมวลผลธุรกรรมหรืออัปเดตยอดคงเหลือของบัญชีจนกว่าชุดข้อมูล blockchain จะดาวน์โหลดเสร็จสมบูรณ์ ตรวจสอบให้แน่ใจว่าคุณมีพื้นที่ดิสก์ แบนด์วิดท์ และเวลาเพียงพอในการซิงโครไนซ์เริ่มแรก คุณสามารถกำหนดค่า Bitcoin Core เพื่อลดขนาด blockchain โดยการทิ้งบล็อกเก่า แต่โปรแกรมยังคงดาวน์โหลดชุดข้อมูลทั้งหมด</p>
</blockquote>
<blockquote>
<p>TIPจากหลาม agian: ซื้อ NVMe SSD 2TB เป็นอย่างต่ำซ่ะ m.2 ได้ยิ่งดีเลยจ้า </p>
</blockquote>
<p>แม้ว่าจะมีข้อกำหนดด้านทรัพยากรเหล่านี้ แต่มีผู้คนหลายพันรายที่รันโหนด Bitcoin บางคนรันบนระบบง่าย ๆ อย่าง Raspberry Pi (คอมพิวเตอร์ราคา 35 เหรียญสหรัฐที่มีขนาดเท่ากับกล่องบุหรี่)</p>
<p>ทำไมคุณถึงอยากรันโหนด? นี่คือเหตุผลที่พบบ่อยที่สุด:</p>
<ul>
<li>คุณไม่ต้องการพึ่งบุคคลที่สามในการตรวจสอบธุรกรรมที่คุณได้รับ</li>
<li>คุณไม่ต้องการเปิดเผยให้บุคคลที่สามรู้ว่าธุรกรรมใดเป็นของกระเป๋าเงินคุณ</li>
<li>คุณกำลังพัฒนาซอฟต์แวร์ Bitcoin และต้องการพึ่งโหนด Bitcoin เพื่อเข้าถึงเครือข่ายและ blockchain ผ่าน API</li>
<li>คุณกำลังสร้างแอปพลิเคชันที่ต้องตรวจสอบธุรกรรมตามกฎฉันทามติของ Bitcoin โดยทั่วไป บริษัทซอฟต์แวร์ Bitcoin มักจะรันโหนดหลายโหนด</li>
<li>คุณต้องการสนับสนุน Bitcoin การรันโหนดที่คุณใช้ตรวจสอบธุรกรรมที่ได้รับในกระเป๋าเงินจะช่วยทำให้เครือข่ายมีความแข็งแกร่งมากขึ้น</li>
</ul>
<p>หากคุณกำลังอ่านหนังสือเล่มนี้และสนใจความปลอดภัยที่เข้มงวด ความเป็นส่วนตัวที่เหนือกว่า หรือการพัฒนาซอฟต์แวร์ Bitcoin คุณควรรันโหนดของตัวเอง</p>
<h3>การกำหนดค่าโหนด Bitcoin Core</h3>
<p>Bitcoin Core จะค้นหาไฟล์การกำหนดค่าในไดเรกทอรีข้อมูลทุกครั้งที่เริ่มทำงาน ในส่วนนี้เราจะตรวจสอบตัวเลือกการกำหนดค่าต่าง ๆ และตั้งค่าไฟล์การกำหนดค่า</p>
<p>เพื่อค้นหาไฟล์การกำหนดค่า ให้รัน bitcoind -printtoconsole ในเทอร์มินัลของคุณ และดูบรรทัดแรก ๆ:</p>
<pre><code>$ bitcoind -printtoconsole
2023-01-28T03:21:42Z Bitcoin Core version v24.0.1
2023-01-28T03:21:42Z Using the 'x86_shani(1way,2way)' SHA256 implementation
2023-01-28T03:21:42Z Using RdSeed as an additional entropy source
2023-01-28T03:21:42Z Using RdRand as an additional entropy source
2023-01-28T03:21:42Z Default data directory /home/harding/.bitcoin
2023-01-28T03:21:42Z Using data directory /home/harding/.bitcoin
2023-01-28T03:21:42Z Config file: /home/harding/.bitcoin/bitcoin.conf
...
[a lot more debug output]
...
</code></pre>
<blockquote>
<p>tatatipจากหลามอีกครั้ง: สังเกตเห็นหรือไม่ว่าในตัวอย่างนี้ Bitcoin Core กำลังชี้ไปที่ไฟล์การกำหนดค่าที่ไดเรกทอรี /home/harding/.bitcoin/bitcoin.conf ซึ่งจะแตกต่างกันไปขึ้นอยู่กับผู้ใช้และระบบปฏิบัติการ</p>
</blockquote>
<p>คุณสามารถกด Ctrl-C เพื่อปิดโหนดหลังจากที่ระบุตำแหน่งไฟล์การกำหนดค่า โดยปกติไฟล์การกำหนดค่าจะอยู่ในไดเรกทอรี .bitcoin ภายใต้โฮมไดเรกทอรีของผู้ใช้ เปิดไฟล์ configuration ด้วยโปรแกรมแก้ไขได้ตามที่คุณชอบ</p>
<p>Bitcoin Core มีตัวเลือกการกำหนดค่ามากกว่า 100 ตัวเลือกที่สามารถปรับเปลี่ยนพฤติกรรมของโหนดเครือข่าย การจัดเก็บบล๊อกเชน และแง่มุมอื่น ๆ ของการทำงาน เพื่อดูรายการตัวเลือก ให้รัน bitcoind --help:</p>
<pre><code>$ bitcoind --help
Bitcoin Core version v24.0.1
Usage:  bitcoind [options]                     Start Bitcoin Core
Options:
  -?
       Print this help message and exit
  -alertnotify=&lt;cmd&gt;
       Execute command when an alert is raised (%s in cmd is replaced by
       message)
...
[many more options]
</code></pre>
<p>นี่คือตัวเลือกที่บางประการที่คุณสามารถตั้งในไฟล์ configuration หรือเป็นพารามิเตอร์บรรทัดคำสั่งสำหรับ bitcoind:</p>
<ul>
<li>alertnotify: เรียกใช้คำสั่งหรือสคริปต์เพื่อส่งการแจ้งเตือนฉุกเฉินไปยังเจ้าของโหนดนี้</li>
<li>conf: ตำแหน่งทางเลือกสำหรับไฟล์ configuration เมื่อใช้เป็นพารามิเตอร์ cli สำหรับ bitcoind เท่านั้น เนื่องจากไม่สามารถอยู่ในไฟล์ configuration ที่มันอ้างถึงได้</li>
<li>datadir: เลือกไดเรกทอรีและระบบไฟล์สำหรับจัดเก็บข้อมูลบล๊อกเชนตามค่าเริ่มต้นนี้คือไดเรกทอรีย่อย .bitcoin ในไดเรกทอรีโฮมของคุณ ขึ้นอยู่กับการกำหนดค่า สามารถใช้พื้นที่ตั้งแต่ประมาณ 10 GB ถึงเกือบ 1 TB ณ ขณะนี้ คาดว่าขนาดสูงสุดจะเพิ่มขึ้นหลายร้อย GB ต่อปี</li>
<li>prune: ลดความต้องการพื้นที่ดิสก์บล๊อกเชนลงเหลือกี่เมกะไบต์โดยการลบบล็อกเก่า ใช้สำหรับโหนดที่มีทรัพยากรจำกัดซึ่งไม่สามารถบรรจุบล๊อกเชนแบบเต็มได้ ส่วนอื่น ๆ ของระบบจะใช้พื้นที่ดิสก์อื่นที่ไม่สามารถตัดทอนได้ ดังนั้นคุณยังคงต้องมีพื้นที่อย่างน้อยตามที่ระบุในตัวเลือก datadir</li>
<li>txindex: รักษาดัชนีของธุรกรรมทั้งหมด ช่วยให้คุณสามารถดึงธุรกรรมใด ๆ โดยใช้ ID ของมันได้โดยโปรแกรม โดยที่บล็อกที่มีธุรกรรมนั้นยังไม่ถูกตัดทอน</li>
<li>dbcache: ขนาดของแคช UTXO ค่าเริ่มต้นคือ 450 เมบิไบต์ (MiB) เพิ่มขนาดนี้บนฮาร์ดแวร์ระดับสูงเพื่ออ่านและเขียนจากดิสก์น้อยลง หรือลดขนาดลงบนฮาร์ดแวร์ระดับต่ำเพื่อประหยัดหน่วยความจำโดยยอมให้ใช้ดิสก์บ่อยขึ้น</li>
<li>blocksonly: ลดการใช้แบนด์วิดท์โดยการรับเฉพาะบล็อกของธุรกรรมที่ได้รับการยืนยันจากเพียร์ แทนที่จะส่งต่อธุรกรรมที่ยังไม่ได้รับการยืนยัน</li>
<li>maxmempool: จำกัดพูลหน่วยความจำของธุรกรรมเป็นกี่เมกะไบต์ ใช้เพื่อลดการใช้หน่วยความจำบนโหนดที่มีหน่วยความจำจำกัด</li>
</ul>
<h3>ดัชนีฐานข้อมูลธุรกรรมและตัวเลือก txindex</h3>
<p>ตามค่าเริ่มต้น Bitcoin Core จะสร้างฐานข้อมูลที่มีเพียงธุรกรรมที่เกี่ยวข้องกับกระเป๋าเงินของผู้ใช้เท่านั้น หากคุณต้องการสามารถเข้าถึงธุรกรรมใด ๆ ด้วยคำสั่งเช่น getrawtransaction คุณจะต้องกำหนดค่า Bitcoin Core ให้สร้างดัชนีธุรกรรมแบบสมบูรณ์ ซึ่งสามารถทำได้ด้วยตัวเลือก txindex โดยตั้ง txindex=1 ในไฟล์การกำหนดค่า Bitcoin Core หากคุณไม่ได้ตั้งตัวเลือกนี้ตั้งแต่แรกและต่อมาตั้งเป็นการทำดัชนีแบบเต็ม คุณจะต้องรอให้มันสร้างดัชนีใหม่</p>
<p>ตัวอย่างการกำหนดค่าโหนดดัชนีแบบเต็มแสดงวิธีที่คุณอาจรวมตัวเลือกก่อนหน้านี้กับโหนดที่มีดัชนีแบบเต็ม โดยทำงานเป็นแบ็กเอนด์ API สำหรับแอปพลิเคชัน bitcoin</p>
<p>ตัวอย่างที่ 1. การกำหนดค่าโหนดดัชนีแบบเต็ม</p>
<pre><code>alertnotify=myemailscript.sh "Alert: %s"
datadir=/lotsofspace/bitcoin
txindex=1
</code></pre>
<p>ตัวอย่างที่ 2. การกำหนดค่าระบบที่มีทรัพยากรจำกัด</p>
<pre><code>alertnotify=myemailscript.sh "Alert: %s"
blocksonly=1
prune=5000
dbcache=150
maxmempool=150
</code></pre>
<p>หลังจากที่คุณแก้ไขไฟล์การกำหนดค่าและตั้งตัวเลือกที่ดีที่สุดตามความต้องการของคุณ คุณสามารถทดสอบ bitcoind ด้วยการกำหนดค่านี้ รัน Bitcoin Core ด้วยตัวเลือก printtoconsole เพื่อรันที่ foreground พร้อมแสดงผลลัพธ์ที่คอนโซล:</p>
<pre><code>$ bitcoind -printtoconsole
2023-01-28T03:43:39Z Bitcoin Core version v24.0.1
2023-01-28T03:43:39Z Using the 'x86_shani(1way,2way)' SHA256 implementation
2023-01-28T03:43:39Z Using RdSeed as an additional entropy source
2023-01-28T03:43:39Z Using RdRand as an additional entropy source
2023-01-28T03:43:39Z Default data directory /home/harding/.bitcoin
2023-01-28T03:43:39Z Using data directory /lotsofspace/bitcoin
2023-01-28T03:43:39Z Config file: /home/harding/.bitcoin/bitcoin.conf
2023-01-28T03:43:39Z Config file arg: [main] blockfilterindex="1"
2023-01-28T03:43:39Z Config file arg: [main] maxuploadtarget="1000"
2023-01-28T03:43:39Z Config file arg: [main] txindex="1"
2023-01-28T03:43:39Z Setting file arg: wallet = ["msig0"]
2023-01-28T03:43:39Z Command-line arg: printtoconsole=""
2023-01-28T03:43:39Z Using at most 125 automatic connections
2023-01-28T03:43:39Z Using 16 MiB out of 16 MiB requested for signature cache
2023-01-28T03:43:39Z Using 16 MiB out of 16 MiB requested for script execution
2023-01-28T03:43:39Z Script verification uses 3 additional threads
2023-01-28T03:43:39Z scheduler thread start
2023-01-28T03:43:39Z [http] creating work queue of depth 16
2023-01-28T03:43:39Z Using random cookie authentication.
2023-01-28T03:43:39Z Generated RPC cookie /lotsofspace/bitcoin/.cookie
2023-01-28T03:43:39Z [http] starting 4 worker threads
2023-01-28T03:43:39Z Using wallet directory /lotsofspace/bitcoin/wallets
2023-01-28T03:43:39Z init message: Verifying wallet(s)…
2023-01-28T03:43:39Z Using BerkeleyDB version Berkeley DB 4.8.30
2023-01-28T03:43:39Z Using /16 prefix for IP bucketing
2023-01-28T03:43:39Z init message: Loading P2P addresses…
2023-01-28T03:43:39Z Loaded 63866 addresses from peers.dat  114ms
[... more startup messages ...]
</code></pre>
<p>คุณสามารถกด Ctrl-C เพื่อหยุดกระบวนการเมื่อคุณพอใจว่ากำลังโหลดการตั้งค่าที่ถูกต้องและทำงานตามที่คาดหวัง</p>
<p>เพื่อรัน Bitcoin Core ที่พื้นหลังเป็นโพรเซส ให้เริ่มด้วยตัวเลือก daemon เช่น bitcoind -daemon</p>
<p>เพื่อตรวจสอบความคืบหน้าและสถานะการทำงานของโหนด Bitcoin ให้เริ่มในโหมด daemon แล้วใช้คำสั่ง bitcoin-cli getblockchaininfo:</p>
<pre><code>$ bitcoin-cli getblockchaininfo

{
"chain": "main",
"blocks": 0,
"headers": 83999,
"bestblockhash": "[...]19d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
"difficulty": 1,
"time": 1673379796,
"mediantime": 1231006505,
"verificationprogress": 3.783041623201835e-09,
"initialblockdownload": true,
"chainwork": "[...]000000000000000000000000000000000000000000000100010001",
"size_on_disk": 89087,
"pruned": false,
"warnings": ""
}
</code></pre>
<p>นี่แสดงโหนดที่มีความสูงของ blockchain เป็น 0 บล็อก และ 83,999 เฮดเดอร์ โหนดจะดึงเฮดเดอร์ของบล็อกจากเพียร์ของตนก่อนเพื่อค้นหา blockchain ที่มีหลักฐานการทำงานมากที่สุด จากนั้นจึงดำเนินการดาวน์โหลดบล็อกเต็มโดยตรวจสอบความถูกต้องไปพร้อมกัน</p>
<p>เมื่อคุณพอใจกับตัวเลือกการกำหนดค่าที่เลือก คุณควรเพิ่ม Bitcoin Core ลงในสคริปต์เริ่มต้นของระบบปฏิบัติการ เพื่อให้มันทำงานอย่างต่อเนื่องและรีสตาร์ทเมื่อระบบปฏิบัติการรีสตาร์ท คุณจะพบสคริปต์เริ่มต้นตัวอย่างสำหรับระบบปฏิบัติการต่าง ๆ ในไดเรกทอรีซอร์สของ Bitcoin Core ภายใต้ contrib/init และไฟล์ README.md ที่แสดงว่าระบบใดใช้สคริปต์ใด</p>
<h3>Bitcoin Core API</h3>
<p>Bitcoin Core ใช้อินเทอร์เฟซ JSON-RPC ซึ่งสามารถเข้าถึงได้โดยใช้เครื่องมืออย่าง bitcoin-cli ซึ่งช่วยให้เราสามารถทดลองใช้งานความสามารถต่างๆ แบบโต้ตอบได้ ซึ่งความสามารถเหล่านี้ยังสามารถใช้งานได้ผ่านทาง API ในรูปแบบโปรแกรม เพื่อเริ่มต้น ให้เรียกใช้คำสั่ง help เพื่อดูรายการคำสั่ง Bitcoin Core RPC ที่มีอยู่:</p>
<pre><code>$ bitcoin-cli help
+== Blockchain ==
getbestblockhash
getblock "blockhash" ( verbosity )
getblockchaininfo
...
walletpassphrase "passphrase" timeout
walletpassphrasechange "oldpassphrase" "newpassphrase"
walletprocesspsbt "psbt" ( sign "sighashtype" bip32derivs finalize )
</code></pre>
<p>คำสั่งแต่ละรายการอาจต้องการพารามิเตอร์หลายตัว เพื่อรับความช่วยเหลือเพิ่มเติม คำอธิบายโดยละเอียด และข้อมูลเกี่ยวกับพารามิเตอร์ต่างๆ ให้เพิ่มชื่อคำสั่งหลังคำว่า help ตัวอย่างเช่น เพื่อดูความช่วยเหลือเกี่ยวกับคำสั่ง RPC getblockhash:</p>
<pre><code>$ bitcoin-cli help getblockhash
getblockhash height
Returns hash of block in best-block-chain at height provided.
Arguments:
1. height    (numeric, required) The height index
Result:
"hex"    (string) The block hash
Examples:
&gt; bitcoin-cli getblockhash 1000
&gt; curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest",
  "method": "getblockhash",
  "params": [1000]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
</code></pre>
<p>ในส่วนท้ายของข้อมูลคำสั่ง help คุณจะเห็นตัวอย่างสองตัวอย่างของคำสั่ง RPC ซึ่งใช้ตัวช่วย bitcoin-cli หรือ HTTP client curl ตัวอย่างเหล่านี้แสดงให้เห็นว่าคุณอาจเรียกใช้คำสั่งได้อย่างไร ลองคัดลอกตัวอย่างแรกและดูผลลัพธ์:</p>
<pre><code>$ bitcoin-cli getblockhash 1000
00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09
</code></pre>
<p>ผลลัพธ์คือแฮชของบล็อก ซึ่งจะอธิบายในรายละเอียดเพิ่มเติมในบทต่อไป แต่ในตอนนี้ คำสั่งนี้ควรให้ผลลัพธ์เหมือนกันบนระบบของคุณ ซึ่งแสดงให้เห็นว่าโหนด Bitcoin Core ของคุณกำลังทำงาน กำลังรับคำสั่ง และมีข้อมูลเกี่ยวกับบล็อก 1,000 ที่จะส่งกลับมาให้คุณ</p>
<h3>การรับข้อมูลสถานะของ Bitcoin Core</h3>
<p>Bitcoin Core ให้รายงานสถานะเกี่ยวกับโมดูลต่างๆ ผ่านอินเตอร์เฟส JSON-RPC คำสั่งที่สำคัญที่สุดรวมถึง getblockchaininfo, getmempoolinfo, getnetworkinfo และ getwalletinfo</p>
<p>คำสั่ง RPC getblockchaininfo ของ Bitcoin ได้ถูกแนะนำไปก่อนหน้านี้แล้ว คำสั่ง getnetworkinfo แสดงข้อมูลพื้นฐานเกี่ยวกับสถานะของโหนดเครือข่าย Bitcoin ใช้ bitcoin-cli เพื่อรันคำสั่งนี้:</p>
<pre><code>$ bitcoin-cli getnetworkinfo
{
  "version": 240001,
  "subversion": "/Satoshi:24.0.1/",
  "protocolversion": 70016,
  "localservices": "0000000000000409",
  "localservicesnames": [
    "NETWORK",
    "WITNESS",
    "NETWORK_LIMITED"
  ],
  "localrelay": true,
  "timeoffset": -1,
  "networkactive": true,
  "connections": 10,
  "connections_in": 0,
  "connections_out": 10,
  "networks": [
    "...detailed information about all networks..."
  ],
  "relayfee": 0.00001000,
  "incrementalfee": 0.00001000,
  "localaddresses": [
  ],
  "warnings": ""
}
</code></pre>
<p>ซึ่งข้อมูลต่าง ๆ จะถูกส่งคืนในรูปแบบ JavaScript Object Notation (JSON) ซึ่งเป็นรูปแบบที่สามารถ "อ่าน" ได้อย่างง่ายดายโดยทุกภาษาโปรแกรมมิ่ง และยังเป็นรูปแบบที่มนุษย์อ่านได้ง่ายอีกด้วย ในข้อมูลนี้เราเห็นหมายเลขเวอร์ชันสำหรับซอฟต์แวร์ Bitcoin Core และโปรโตคอลบิตคอยน์เราเห็นจำนวนการเชื่อมต่อในปัจจุบันและข้อมูลต่างๆ เกี่ยวกับเครือข่ายบิตคอยน์และการตั้งค่าที่เกี่ยวข้องกับโหนดนี้</p>
<blockquote>
<p>TIP: จะใช้เวลาสักระยะ อาจมากกว่าหนึ่งวัน สำหรับ bitcoind ในการอัพเดทให้ทันกับบล็อกล่าสุดของบล็อกเชนปัจจุบัน ในขณะที่มันดาวน์โหลดบล็อกจากโหนดอื่นๆ และตรวจสอบความถูกต้องของทุกธุรกรรมในบล็อกเหล่านั้น—ซึ่งมีเกือบหนึ่งพันล้านธุรกรรม ณ เวลาที่เขียนนี้ คุณสามารถตรวจสอบความคืบหน้าโดยใช้ getblockchaininfo เพื่อดูจำนวนบล็อกที่ทราบ ตัวอย่างในส่วนที่เหลือของบทนี้สมมติว่าคุณอยู่อย่างน้อยที่บล็อก 775,072 เนื่องจากความปลอดภัยของธุรกรรมขึ้นอยู่กับจำ</p>
</blockquote>
<h3>มาสำรวจและถอดรหัสธุรกรรมของบิตคอยน์กันเถอะ!!</h3>
<p>อย่างในบทที่สอง อลิซได้ซื้อสินค้าจากร้านของบ็อบและธุรกรรมของเธอถูกบันทึกลงในบล็อกเชนของบิตคอยน์ โดยเราสามารถใช้ API เพื่อดึงและตรวจสอบธุรกรรมนั้นได้โดยการใช้ txid เป็นพารามิเตอร์:</p>
<pre><code>$ bitcoin-cli getrawtransaction 466200308696215bbc949d5141a49a4138ecdfdfaa2a8029c1f9bcecd1f96177
--ผลลัพธ์ของคำสั่ง
01000000000101eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da13569
8679268041c54a0100000000ffffffff02204e0000000000002251203b41daba
4c9ace578369740f15e5ec880c28279ee7f51b07dca69c7061e07068f8240100
000000001600147752c165ea7be772b2c0acb7f4d6047ae6f4768e0141cf5efe
2d8ef13ed0af21d4f4cb82422d6252d70324f6f4576b727b7d918e521c00b51b
e739df2f899c49dc267c0ad280aca6dab0d2fa2b42a45182fc83e81713010000
0000
</code></pre>
<blockquote>
<p>TIP: txid ไม่ใช่สิ่งที่สามารถเชื่อถือได้ขนาดนั้น เพราะการไม่มี txid ในบล๊อกเชนของบิตคอยน์นั้นไม่ได้หมายความว่าธุรกรรมไม่ได้ถูกประมวลผล โดยสิ่งนี้เรียกว่า "transaction malleability" (ความสามารถในการเปลี่ยนแปลงธุรกรรม) เพราะธุรกรรมสามารถถูกแก้ไขก่อนการยืนยันในบล็อก ซึ่งเปลี่ยน txid ของพวกมัน หลังจากธุรกรรมถูกรวมอยู่ในบล็อกแล้ว txid ของมันไม่สามารถเปลี่ยนแปลงได้อีก เว้นแต่จะมีการจัดระเบียบบล็อกเชนใหม่และบล็อกนั้นถูกลบออกจากบล็อกเชนที่ดีที่สุด โดยการจัดระเบียบใหม่เกิดขึ้นได้ยากหลังจากธุรกรรมได้รับการยืนยันหลายครั้งแล้ว</p>
</blockquote>
<p>คำสั่ง getrawtransaction จะส่งคืนธุรกรรมที่เข้ารหัสในรูปแบบเลขฐานสิบหกและเพื่อถอดรหัสนั้น เราใช้คำสั่ง decoderawtransaction โดยส่งข้อมูลเลขฐานสิบหกเป็นพารามิเตอร์ คุณสามารถคัดลอกเลขฐานสิบหกที่ส่งคืนโดย getrawtransaction และวางเป็นพารามิเตอร์ให้กับ decoderawtransaction ได้:</p>
<pre><code>$ bitcoin-cli decoderawtransaction 01000000000101eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da135698679268041c54a0100000000ffffffff02204e0000000000002251203b41daba4c9ace578369740f15e5ec880c28279ee7f51b07dca69c7061e07068f8240100000000001600147752c165ea7be772b2c0acb7f4d6047ae6f4768e0141cf5efe2d8ef13ed0af21d4f4cb82422d6252d70324f6f4576b727b7d918e521c00b51be739df2f899c49dc267c0ad280aca6dab0d2fa2b42a45182fc83e817130100000000
--ผลลัพธ์ของคำสั่ง
{
  "txid": "466200308696215bbc949d5141a49a4138ecdfdfaa2a8029c1f9bcecd1f96177",
  "hash": "f7cdbc7cf8b910d35cc69962e791138624e4eae7901010a6da4c02e7d238cdac",
  "version": 1,
  "size": 194,
  "vsize": 143,
  "weight": 569,
  "locktime": 0,
  "vin": [
    {
      "txid": "4ac541802679866935a19d4f40728bb89204d0cac90d85f3a51a19...aeb",
      "vout": 1,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "txinwitness": [
        "cf5efe2d8ef13ed0af21d4f4cb82422d6252d70324f6f4576b727b7d918e5...301"
      ],
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.00020000,
      "n": 0,
      "scriptPubKey": {
        "asm": "1 3b41daba4c9ace578369740f15e5ec880c28279ee7f51b07dca...068",
        "desc": "rawtr(3b41daba4c9ace578369740f15e5ec880c28279ee7f51b...6ev",
        "hex": "51203b41daba4c9ace578369740f15e5ec880c28279ee7f51b07d...068",
        "address": "bc1p8dqa4wjvnt890qmfws83te0v3qxzsfu7ul63kp7u56w8q...5qn",
        "type": "witness_v1_taproot"
      }
    },
    {
      "value": 0.00075000,
      "n": 1,
      "scriptPubKey": {
        "asm": "0 7752c165ea7be772b2c0acb7f4d6047ae6f4768e",
        "desc": "addr(bc1qwafvze0200nh9vkq4jmlf4sy0tn0ga5w0zpkpg)#qq404gts",
        "hex": "00147752c165ea7be772b2c0acb7f4d6047ae6f4768e",
        "address": "bc1qwafvze0200nh9vkq4jmlf4sy0tn0ga5w0zpkpg",
        "type": "witness_v0_keyhash"
      }
    }
  ]
}
</code></pre>
<p>การถอดรหัสธุรกรรมแสดงส่วนประกอบทั้งหมดของธุรกรรมนี้ รวมถึงอินพุตและเอาต์พุตของธุรกรรม ในกรณีนี้เราเห็นว่าธุรกรรมใช้อินพุตหนึ่งรายการและสร้างเอาต์พุตสองรายการ อินพุตของธุรกรรมนี้คือเอาต์พุตจากธุรกรรมที่ได้รับการยืนยันก่อนหน้านี้ (แสดงเป็น txid ของอินพุต) เอาต์พุตทั้งสองรายการสอดคล้องกับการชำระเงินให้บ็อบและเงินทอนกลับให้อลิซ</p>
<h3>มาสำรวจบล็อกของบิตคอยน์กัน!!</h3>
<p>การสำรวจบล็อกนั้นคล้ายกับการสำรวจธุรกรรม แต่อย่างไรก็ตามบล็อกสามารถอ้างอิงได้ทั้งโดยลำดับของบล็อกหรือโดยแฮชของบล็อก เราใช้คำสั่ง getblockhash ซึ่งรับลำดับของบล็อกเป็นพารามิเตอร์และส่งคืน แฮชของบล็อกนั้น:</p>
<pre><code>$ bitcoin-cli getblockhash 123456
--ผลลัพธ์ของคำสั่ง
0000000000002917ed80650c6174aac8dfc46f5fe36480aaef682ff6cd83c3ca
</code></pre>
<p>ตอนนี้เรารู้แฮชสำหรับบล็อกที่เราเลือกแล้ว เราสามารถดูบล็อกนั้นได้ เราใช้คำสั่ง getblock โดยมีแฮชของบล็อกเป็นพารามิเตอร์:</p>
<pre><code>$ bitcoin-cli getblockhash 0000000000002917ed80650c6174aac8dfc46f5fe36480aaef682ff6cd83c3ca
--ผลลัพธ์ของคำสั่ง
{
  "hash": "0000000000002917ed80650c6174aac8dfc46f5fe36480aaef682ff6cd83c3ca",
  "confirmations": 651742,
  "height": 123456,
  "version": 1,
  "versionHex": "00000001",
  "merkleroot": "0e60651a9934e8f0decd1c[...]48fca0cd1c84a21ddfde95033762d86c",
  "time": 1305200806,
  "mediantime": 1305197900,
  "nonce": 2436437219,
  "bits": "1a6a93b3",
  "difficulty": 157416.4018436489,
  "chainwork": "[...]00000000000000000000000000000000000000541788211ac227bc",
  "nTx": 13,
  "previousblockhash": "[...]60bc96a44724fd72daf9b92cf8ad00510b5224c6253ac40095",
  "nextblockhash": "[...]00129f5f02be247070bf7334d3753e4ddee502780c2acaecec6d66",
  "strippedsize": 4179,
  "size": 4179,
  "weight": 16716,
  "tx": [
    "5b75086dafeede555fc8f9a810d8b10df57c46f9f176ccc3dd8d2fa20edd685b",
    "e3d0425ab346dd5b76f44c222a4bb5d16640a4247050ef82462ab17e229c83b4",
    "137d247eca8b99dee58e1e9232014183a5c5a9e338001a0109df32794cdcc92e",
    "5fd167f7b8c417e59106ef5acfe181b09d71b8353a61a55a2f01aa266af5412d",
    "60925f1948b71f429d514ead7ae7391e0edf965bf5a60331398dae24c6964774",
    "d4d5fc1529487527e9873256934dfb1e4cdcb39f4c0509577ca19bfad6c5d28f",
    "7b29d65e5018c56a33652085dbb13f2df39a1a9942bfe1f7e78e97919a6bdea2",
    "0b89e120efd0a4674c127a76ff5f7590ca304e6a064fbc51adffbd7ce3a3deef",
    "603f2044da9656084174cfb5812feaf510f862d3addcf70cacce3dc55dab446e",
    "9a4ed892b43a4df916a7a1213b78e83cd83f5695f635d535c94b2b65ffb144d3",
    "dda726e3dad9504dce5098dfab5064ecd4a7650bfe854bb2606da3152b60e427",
    "e46ea8b4d68719b65ead930f07f1f3804cb3701014f8e6d76c4bdbc390893b94",
    "864a102aeedf53dd9b2baab4eeb898c5083fde6141113e0606b664c41fe15e1f"
  ]
}
</code></pre>
<p>รายการ confirmations บอกเราถึง ความลึก ของบล็อกนี้—มีกี่บล็อกที่ถูกสร้างทับบนบล็อกนี้ ซึ่งบ่งบอกถึงความยากในการเปลี่ยนแปลงธุรกรรมใดๆ ในบล็อกนี้ ลำดับบอกเราว่ามีกี่บล็อกที่มาก่อนหน้าบล็อกนี้ เราเห็นเวอร์ชันของบล็อก เวลาที่มันถูกสร้าง (ตามข้อมูลของนักขุด) เวลาเฉลี่ยของ 11 บล็อกที่มาก่อนหน้าบล็อกนี้ (การวัดเวลาที่นักขุดปลอมแปลงได้ยากกว่า) และขนาดของบล็อกในการวัดสามแบบต่างกัน (ขนาดดั้งเดิมที่ถูกลบข้อมูลบางส่วนออก, ขนาดเต็ม, และขนาดในหน่วยน้ำหนัก) เรายังเห็นฟิลด์บางอย่างที่ใช้สำหรับความปลอดภัยและหลักฐานการทำงาน (merkle root, nonce, bits, difficulty, และ chainwork) เราจะตรวจสอบสิ่งเหล่านี้โดยละเอียดในส่วนของการขุดในบทที่ 12</p>
<h3>การใช้อินเตอร์เฟสโปรแกรมของ Bitcoin Core</h3>
<p>bitcoin-cli มีประโยชน์มากสำหรับการใช้งาน API ของ Bitcoin Core และการทดสอบฟังก์ชันต่าง ๆ แต่จุดประสงค์หลักของ API คือการเข้าถึงฟังก์ชันด้วยโปรแกรม ในส่วนนี้เราจะสาธิตการเข้าถึง Bitcoin Core จากโปรแกรมอื่น</p>
<p>API ของ Bitcoin Core เป็นอินเตอร์เฟส JSON-RPC โดย JSON เป็นวิธีที่สะดวกมากในการนำเสนอข้อมูลที่ทั้งมนุษย์และโปรแกรมสามารถอ่านได้ง่าย RPC ย่อมาจาก remote procedure call ซึ่งหมายความว่าเรากำลังเรียกใช้กระบวนการ (ฟังก์ชัน) ที่อยู่ห่างไกล (บนโหนด Bitcoin Core) ผ่านโปรโตคอลเครือข่าย ในกรณีนี้ โปรโตคอลเครือข่ายคือ HTTP</p>
<p>เมื่อเราใช้คำสั่ง bitcoin-cli เพื่อขอความช่วยเหลือเกี่ยวกับคำสั่ง มันแสดงตัวอย่างการใช้ curl ซึ่งเป็นไคลเอนต์ HTTP ทางคอมมานด์ไลน์ที่ยืดหยุ่น เพื่อสร้างคำเรียก JSON-RPC เหล่านี้:</p>
<pre><code>$ curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest",
  "method": "getblockchaininfo",
  "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
</code></pre>
<p>คำสั่งนี้แสดงว่า curl ส่งคำขอ HTTP ไปยัง localhost (127.0.0.1) เชื่อมต่อกับพอร์ต RPC เริ่มต้นของ Bitcoin (8332) และส่งคำขอ jsonrpc โดยใช้เมธอดเป็น getblockchaininfo โดยใช้การเข้ารหัสแบบ text/plain</p>
<p>คุณอาจสังเกตว่า curl จะขอให้ส่งข้อมูลประจำตัวไปพร้อมกับคำขอ Bitcoin Core จะสร้างรหัสผ่านแบบสุ่มในแต่ละครั้งที่เริ่มต้นและวางไว้ในไดเรกทอรีข้อมูลภายใต้ชื่อ .cookie โดย bitcoin-cli สามารถอ่านไฟล์รหัสผ่านนี้โดยให้ไดเรกทอรีข้อมูล ในทำนองเดียวกัน คุณสามารถคัดลอกรหัสผ่านและส่งไปยัง curl (หรือตัวครอบ Bitcoin Core RPC ระดับสูงอื่น ๆ ) ตามที่เห็นในการใช้การตรวจสอบสิทธิ์แบบใช้คุกกี้กับ Bitcoin Core</p>
<p>ตัวอย่างที่ 3. การใช้การตรวจสอบสิทธิ์แบบใช้คุกกี้กับ Bitcoin Core</p>
<pre><code>$ cat .bitcoin/.cookie
  __cookie__:17c9b71cef21b893e1a019f4bc071950c7942f49796ed061b274031b17b19cd0
$ curl
  --user __cookie__:17c9b71cef21b893e1a019f4bc071950c7942f49796ed061b274031b17b19cd0
  --data-binary '{"jsonrpc": "1.0", "id":"curltest",
  "method": "getblockchaininfo",
  "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
{"result":{"chain":"main","blocks":799278,"headers":799278,
"bestblockhash":"000000000000000000018387c50988ec705a95d6f765b206b6629971e6978879",
"difficulty":53911173001054.59,"time":1689703111,"mediantime":1689701260,
"verificationprogress":0.9999979206082515,"initialblockdownload":false,
"chainwork":"00000000000000000000000000000000000000004f3e111bf32bcb47f9dfad5b",
"size_on_disk":563894577967,"pruned":false,"warnings":""},"error":null,
"id":"curltest"}
</code></pre>
<p>นอกจากนี้คุณยังสามารถตั้งรหัสผ่านด้วยตัวเองใน ./share/rpcauth/rpcauth.py ภายในไดเรกทอรีของ Bitcoin Core</p>
<p>หากคุณกำลังใช้การเรียก JSON-RPC ในโปรแกรมของคุณเอง คุณสามารถใช้ไลบรารี HTTP ทั่วไปเพื่อสร้างการเรียกได้ คล้ายกับที่แสดงในตัวอย่าง curl ก่อนหน้านี้</p>
<p>อย่างไรก็ตาม มีไลบรารีในภาษาโปรแกรมยอดนิยมส่วนใหญ่ที่ "wrap" API ของ Bitcoin Core ในลักษณะที่ทำให้การใช้งานง่ายขึ้นมาก เราจะใช้ไลบรารี python-bitcoinlib เพื่อทำให้การเข้าถึง API นั้นง่ายขึ้น โดยไลบรารีนี้ไม่ได้เป็นส่วนหนึ่งของโครงการ Bitcoin Core และจำเป็นต้องติดตั้งด้วยวิธีปกติที่คุณติดตั้งไลบรารี Python โปรดจำไว้ว่า การใช้งานนี้ต้องมีอินสแตนซ์ Bitcoin Core ที่กำลังทำงานอยู่ ซึ่งจะถูกใช้เพื่อทำการเรียก JSON-RPC</p>
<p>ตัวอย่างสคริปต์ Python ใน " การทำงาน getblockchaininfo ผ่าน API JSON-RPC ของ Bitcoin Core" ซึ่งทำการเรียก getblockchaininfo อย่างง่ายและพิมพ์พารามิเตอร์ block จากข้อมูลที่ส่งคืนโดย Bitcoin Core</p>
<p>ตัวอย่างที่ 4. การทำงาน getblockchaininfo ผ่าน API JSON-RPC ของ Bitcoin Core</p>
<pre><code>from bitcoin.rpc import RawProxy
# Create a connection to local Bitcoin Core node
p = RawProxy()
# Run the getblockchaininfo command, store the resulting data in info
info = p.getblockchaininfo()
# Retrieve the 'blocks' element from the info
print(info['blocks'])
--ผลลัพธ์ของคำสั่ง
$ python rpc_example.py
773973
</code></pre>
<p>มันบอกเราว่าโหนด Bitcoin Core ในเครื่องของเรามีกี่บล็อกในบล็อกเชนของมัน ซึ่งไม่ใช่ผลลัพธ์ที่น่าทึ่ง แต่มันแสดงการใช้งานพื้นฐานของไลบรารีในฐานะอินเตอร์เฟสที่ถูกทำให้ง่ายขึ้นสำหรับ API JSON-RPC ของ Bitcoin Core</p>
<p>ต่อไป เราจะใช้คำสั่ง getrawtransaction และ decodetransaction เพื่อดึงข้อมูลรายละเอียดของการชำระเงินจาก Alice ไปยัง Bob ในส่วนของการดึงข้อมูลธุรกรรมและการวนลูปเอาต์พุตของธุรกรรม เราจะดึงธุรกรรมของ Alice และแสดงรายการเอาต์พุตของธุรกรรม สำหรับแต่ละเอาต์พุต เราจะแสดงที่อยู่ผู้รับและมูลค่า โดยธุรกรรมของ Alice มีเอาต์พุตหนึ่งรายการที่จ่ายให้ Bob และอีกหนึ่งรายการเป็นเงินทอนกลับไปยัง Alice</p>
<p>ตัวอย่างที่ 5 การดึงข้อมูลธุรกรรมและการวนลูปเอาต์พุตของธุรกรรม</p>
<pre><code>from bitcoin.rpc import RawProxy
p = RawProxy()
# Alice's transaction ID
txid = "466200308696215bbc949d5141a49a4138ecdfdfaa2a8029c1f9bcecd1f96177"
# First, retrieve the raw transaction in hex
raw_tx = p.getrawtransaction(txid)
# Decode the transaction hex into a JSON object
decoded_tx = p.decoderawtransaction(raw_tx)
# Retrieve each of the outputs from the transaction
for output in decoded_tx['vout']:
    print(output['scriptPubKey']['address'], output['value'])
--ผลลัพธ์ของคำสั่ง
$ python rpc_transaction.py
bc1p8dqa4wjvnt890qmfws83te0v3qxzsfu7ul63kp7u56w8qc0qwp5qv995qn 0.00020000
bc1qwafvze0200nh9vkq4jmlf4sy0tn0ga5w0zpkpg 0.00075000
</code></pre>
<p>ตัวอย่างทั้งสองข้างต้นค่อนข้างง่าย คุณไม่จำเป็นต้องใช้โปรแกรมในการรันพวกมัน คุณสามารถใช้ตัวช่วย bitcoin-cli ได้ง่าย ๆ แต่อย่างไรก็ตาม ตัวอย่างถัดไปต้องใช้การเรียก RPC หลายร้อยครั้งและแสดงให้เห็นถึงการใช้อินเทอร์เฟซเชิงโปรแกรมได้ชัดเจนยิ่งขึ้น</p>
<p>ในส่วนของการดึงข้อมูลบล็อกและการรวมเอาต์พุตของทุกธุรกรรม เราจะเริ่มต้นด้วยการดึงข้อมูลบล็อก จากนั้นดึงข้อมูลธุรกรรมแต่ละรายการภายในบล็อกโดยอ้างอิงถึง ID ของแต่ละธุรกรรม ต่อมา เราจะวนลูปผ่านเอาต์พุตของแต่ละธุรกรรมและรวมมูลค่าทั้งหมด</p>
<p>ตัวอย่างที่ 6 การดึงข้อมูลบล็อกและการรวมเอาต์พุตของทุกธุรกรรม</p>
<pre><code>from bitcoin.rpc import RawProxy
p = RawProxy()
# The block height where Alice's transaction was recorded
blockheight = 775072
# Get the block hash of the block at the given height
blockhash = p.getblockhash(blockheight)
# Retrieve the block by its hash
block = p.getblock(blockhash)
# Element tx contains the list of all transaction IDs in the block
transactions = block['tx']
block_value = 0
# Iterate through each transaction ID in the block
for txid in transactions:
    tx_value = 0
    # Retrieve the raw transaction by ID
    raw_tx = p.getrawtransaction(txid)
    # Decode the transaction
    decoded_tx = p.decoderawtransaction(raw_tx)
    # Iterate through each output in the transaction
    for output in decoded_tx['vout']:
        # Add up the value of each output
        tx_value = tx_value + output['value']
    # Add the value of this transaction to the total
    block_value = block_value + tx_value
print("Total value in block: ", block_value)
--ผลลัพธ์ของคำสั่ง
$ python rpc_block.py
Total value in block:  10322.07722534
</code></pre>
<p>โค้ดตัวอย่างของเราคำนวณว่ามูลค่ารวมที่ถูกทำธุรกรรมในบล็อกนี้คือ 10,322.07722534 BTC (รวมถึงรางวัล 25 BTC และค่าธรรมเนียม 0.0909 BTC) ลองเปรียบเทียบกับจำนวนที่รายงานโดยเว็บไซต์สำรวจบล็อก (block explorer) โดยการค้นหาแฮชของบล็อกหรือเลขลำดับของบล็อก เครื่องมือสำรวจบล็อกบางตัวรายงานมูลค่ารวมโดยไม่รวมรางวัลและไม่รวมค่าธรรมเนียม ลองดูว่าคุณสามารถสังเกตเห็นความแตกต่างได้หรือไม่</p>
<h3>ไคลเอนต์ทางเลือก, ไลบรารี, และชุดเครื่องมือ</h3>
<p>C/C++<br>Bitcoin Core:การใช้งานอ้างอิงของ Bitcoin<br>JavaScript<br>bcoin: การใช้งานโหนดแบบเต็มรูปแบบที่มีความยืดหยุ่นและขยายได้พร้อม API<br>Bitcore: โหนดเต็มรูปแบบ, API, และไลบรารีโดย Bitpay<br>BitcoinJS: ไลบรารี Bitcoin ที่เขียนด้วย JavaScript ล้วนๆ สำหรับ node.js และเบราว์เซอร์<br>Java<br>bitcoinj: ไลบรารีไคลเอนต์โหนดเต็มรูปแบบที่เขียนด้วย Java<br>Python<br>python-bitcoinlib: ไลบรารี Bitcoin, ไลบรารีฉันทามติ, และโหนดที่เขียนด้วย Python โดย Peter Todd<br>pycoin: ไลบรารี Bitcoin ที่เขียนด้วย Python โดย Richard Kiss<br>Go<br>btcd: ไคลเอนต์ Bitcoin โหนดเต็มรูปแบบที่เขียนด้วยภาษา Go<br>Rust<br>rust-bitcoin: ไลบรารี Bitcoin ที่เขียนด้วย Rust สำหรับการจัดรูปแบบข้อมูล, การแยกวิเคราะห์, และการเรียกใช้ API<br>Scala<br>bitcoin-s: การใช้งาน Bitcoin ที่เขียนด้วย Scala<br>C#<br>NBitcoin: ไลบรารี Bitcoin ที่ครอบคลุมสำหรับเฟรมเวิร์ก .NET</p>
<p>ยังมีไลบรารีอีกมากมายในภาษาโปรแกรมมิ่งอื่น ๆ อีกหลากหลาย และมีการสร้างขึ้นใหม่อยู่ตลอดเวลา</p>
<p>หากคุณทำตามคำแนะนำในบทนี้ ตอนนี้คุณมี Bitcoin Core ที่ทำงานอยู่และได้เริ่มสำรวจเครือข่ายและบล็อกเชนโดยใช้โหนดของคุณเอง จากนี้ไปคุณสามารถใช้ซอฟต์แวร์ที่คุณควบคุมได้โดยอิสระ—บนคอมพิวเตอร์ที่คุณควบคุม—เพื่อตรวจสอบว่า bitcoin ใด ๆ ที่คุณได้รับปฏิบัติตามกฎทุกข้อในระบบ Bitcoin โดยไม่ต้องไว้วางใจองค์กรภายนอกใด ๆ ในบทต่อไป เราจะเรียนรู้เพิ่มเติมเกี่ยวกับกฎของระบบและวิธีที่โหนดและกระเป๋าเงินของคุณใช้กฎเหล่านั้นเพื่อรักษาความปลอดภัยของเงินของคุณ ปกป้องความเป็นส่วนตัวของคุณ และทำให้การใช้จ่ายและการรับเงินสะดวกสบาย</p>
<p>ฮึ่ ๆ หลาม ๆ มาอีกแล้ว จริง ๆ เนื้อหาของบทที่สามมันจบลงตรงนี้แหละ แต่ว่าถ้าสมมุตืว่าใครลองไปทำตามจริง ๆ แล้วอยากรู้ว่าเราสามารถทำอะไรจาก node ของเราได้อีกบ้าง เลยมีกิจกรรมขำ ๆ มาให้ทำครับ โดยความยากจะมีทั้งหมด 3 ระดับ ดังนี้</p>
<ul>
<li>ง่าย (สามารถหาคำตอบได้ด้วย bitcoin-cli command เดียว)</li>
</ul>
<ol>
<li>แฮชของบล๊อก 774698 คืออะไร?</li>
<li>signature ของข้อความจาก address นี้ถูกต้องหรือไม่</li>
</ol>
<pre><code>address: 1E9YwDtYf9R29ekNAfbV7MvB4LNv7v3fGa
message: 1E9YwDtYf9R29ekNAfbV7MvB4LNv7v3fGa
signature:HCsBcgB+Wcm8kOGMH8IpNeg0H4gjCrlqwDf/GlSXphZGBYxm0QkKEPhh9DTJRp2IDNUhVr0FhP9qCqo2W0recNM=
</code></pre>
<ul>
<li>ทำได้แหละ (สามารถหาคำตอบได้ด้วย bitcoin-cli command เดียว หรืออาจจะมากกว่าหนึ่ง)</li>
</ul>
<ol start="3">
<li>มี output ใหม่กี่อันที่เกิดในบล๊อก 774698 ?</li>
<li>ใช้ wallet descriptors หา taproot address ที่ 100 จาก extended public key ที่กำหนดให้ xpub6DLd5RvY42Q5HAmBhHPUbDGdeS9VvsYNauiuN8r6NzbiXSSNWpNVrDGTUScJ9fS2orMtuB3VdxMdUH83fPtwbrizfJg9LwWnGqtL7RTs5h1</li>
<li>สร้าง multisig address แบบ P2SH แบบ 1-of-4 จาก publickey ในอินพุตทั้งสี่ของธุรกรรมนี้:37d966a263350fe747f1c606b159987545844a493dd38d84b070027a895c4517</li>
</ol>
<ul>
<li>ต้องคิดเชิงตรรกะได้เล็กน้อย (สามารถหาคำตอบได้ด้วย bitcoin-cli command และพวก if-else/loop)</li>
</ul>
<ol start="6">
<li>tx ใดในบล็อก 257,343 ที่ใช้เอาท์พุตของ coinbase ของบล็อก 256,128?</li>
<li>มีเอาต์พุตเดียวที่ยังไม่ได้ใช้งานจากบล็อก 123,321 เอาต์พุตดังกล่าวถูกส่งไปที่ address ไหน</li>
<li>public key ใดที่ลงนามอินพุต 0 ใน tx นี้:e5969add849689854ac7f28e45628b89f7454b83e9699e551ce14b6f90c86163</li>
</ol>
<p>ใครทำได้พร้อมแชร์วิธีการใต้โพสต์นี้เดี๋ยวจะมีไดโนเสาร์ส่งแซตเป็นกำลังใจให้เล็กน้อยครับ</p>
]]></content:encoded>
      <itunes:author><![CDATA[Learning_BTC&NOSTR]]></itunes:author>
      <itunes:summary><![CDATA[<h2>Bitcoin Core: The Reference Implementation</h2>
<hr>
<p>ผู้คนจะยอมรับเงินใด ๆ เพื่อแลกเปลี่ยนกับสินค้าและบริการก็ต่อเมื่อคนนั้น ๆ เชื่อว่าเงินนี้จะมีมูลค่าในอนาคต เงินปลอมหรือเงินที่เสื่อมค่าโดยไม่คาดคิดนั้นอาจไม่สามารถใช้ได้ในอนาคต ดังนั้นทุกคนที่รับบิตคอยน์จึงมีแรงจูงใจที่แข็งแกร่งในการตรวจสอบความถูกต้องของบิตคอยน์ที่พวกเขาได้รับ ระบบของบิตคอยน์นั้นถูกออกแบบมาให้เข้าถึง, ป้องกันการปลอมแปลง, การเสื่อมค่า และปัญหาสำคัญอื่น ๆ ได้อย่างสมบูรณ์ได้ด้วยคอมพิวเตอร์ทั่วไป โดยซอฟต์แวร์ที่ให้ฟังก์ชันนี้เรียกว่า Full node ซึ่งทำหน้าที่ตรวจสอบธุรกรรมบิตคอยน์ทุกครั้งที่ได้รับการยืนยันตามกฎของระบบ นอกจากนี้ Full node ยังสามารถให้เครื่องมือและข้อมูลเพื่อทำความเข้าใจการทำงานของบิตคอยน์และสภาพปัจจุบันของเครือข่าย</p>
<p>ในบทนี้เอง เราจะทำการติดตั้ง Bitcoin Core ซึ่งเป็นซอฟต์แวร์ที่ผู้ใช้งาน Full node ส่วนใหญ่เลือกใช้เพื่อเป็นประตูบานแรกในการเข้าถึงระบบนิเวศของบิตคอยน์ เราจะตรวจสอบบล็อก ธุรกรรม และข้อมูลอื่น ๆ จากโหนดของคุณ ซึ่งเป็นข้อมูลที่เชื่อถือได้ (ไม่ใช่เพราะหน่วยงานทรงอำนาจกำหนดให้เป็นเช่นนั้น) แต่เป็นเพราะโหนดของคุณได้ตรวจสอบข้อมูลนั้นอย่างอิสระ ตลอดเนื้อหาที่เหลือในหนังสือเล่มนี้ เราจะใช้ Bitcoin Core เพื่อสร้างและตรวจสอบข้อมูลที่เกี่ยวข้องกับบล็อกเชนและเครือข่าย</p>
<h3>จาก Bitcoin สู่ Bitcoin Core</h3>
<p>บิตคอยน์เป็นโครงการโอเพ่นซอร์ส โดยซอร์สโค้ดทั้งหมดก็สามารถดาวน์โหลดและใช้งานได้ฟรีภายใต้ใบอณุญาตแบบเปิด (MIT License) นอกจากจะเป็นโอเพ่นซอร์สแล้วบิตคอยน์ยังได้รับการพัฒนาโดยชุมชนอาสาสมัครแบบเปิดกว้าง แน่นอนว่าในช่วงแรกนั้นชุมชนนี้ประกอบด้วย Satoshi Nakamoto เพียงคนเดียว แต่ภายในปี 2023 ซอร์สโค้ดของบิตคอยน์มีผู้ร่วมพัฒนามากกว่า 1,000 คน</p>
<p>เมื่อ Satoshi Nakamoto ได้สร้างซอฟแวร์บิตคอยน์ตัวนี้และพัฒนามันจนเกือบสมบูรณ์ก่อนแล้วจึงเผยแพร่เอกสารไวท์เปเปอร์ เขาน่าจะต้องการให้มั่นใจว่าการใช้งานจริงสามารถทำงานได้ก่อนเผยแพร่เอกสาร โดยซอฟต์แวร์เวอร์ชันแรกที่รู้จักในชื่อ "Bitcoin" นั้นได้รับการปรับปรุงและพัฒนามาอย่างมาก จนได้กลายเป็นสิ่งที่เรารู้จักกันในชื่อ Bitcoin Core และเพื่อแยกความแตกต่างจากการใช้งานอื่น ๆ Bitcoin Core เป็นซอฟต์แวร์ต้นแบบอ้างอิง (reference implementation) ของระบบบิตคอยน์ซึ่งแสดงวิธีการทำงานของแต่ละส่วนในเชิงเทคโนโลยี นอกจากนี้ Bitcoin Core รวมถึงการใช้งานฟังก์ชันทั้งหมดของบิตคอยน์ เช่น กระเป๋าเงิน เครื่องมือตรวจสอบธุรกรรมและบล็อก เครื่องมือสำหรับการสร้างบล็อก และส่วนต่าง ๆ ของการสื่อสารแบบ peer-to-peer ของบิตคอยน์<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1741413625372-YAKIHONNES3.png" alt="image"></p>
<h3>Bitcoin Development Environment</h3>
<p>สำหรับนักพัฒนาที่ต้องการเขียนแอปพลิเคชันเกี่ยวกับบิตคอยน์ ทั้งการตั้งค่าสภาพแวดล้อมสำหรับการพัฒนาพร้อมเครื่องมือ ไลบรารี และซอฟต์แวร์สนับสนุนเป็นสิ่งสำคัญ ซึ่งเนื้อหาในบทนี้นั้นจะเป็นเรื่องทางเทคนิคอลค่อนข้างเยอะ ในบทนี้เราจะอธิบายขั้นตอนการตั้งค่าอย่างละเอียด หากคุณพบว่าเนื้อหานี้ซับซ้อนเกินไป (และไม่ได้ต้องการตั้งค่าสภาพแวดล้อมสำหรับการพัฒนาจริง ๆ) คุณสามารถข้ามไปยังบทถัดไปที่มีเนื้อหาน้อยทางเทคนิคกว่าได้</p>
<h3>มาคอมไพล์ Bitcoin core จากซอร์สโค้ดกันเถอะ !!</h3>
<p>ซอร์สโค้ดทั้งหมดของ BItcoin Core นั้นสามารถดาวน์โหลดได้ในรูปแบบไฟล์อาร์ไคฟ์หรือโดยการโคลนที่เก็บซอร์สโค้ดจาก GitHub โดยตรง บนหน้าดาวน์โหลดของ Bitcoin Core ให้เลือกเวอร์ชันล่าสุดและดาวน์โหลดไฟล์อัดบีบของซอร์สโค้ด หรือใช้คำสั่ง Git เพื่อสร้างสำเนาซอร์สโค้ดบนเครื่องของคุณจากหน้า GitHub ของ Bitcoin</p>
<blockquote>
<p>TIP: ในตัวอย่างหลาย ๆ ส่วนของบทนี้ เราจะใช้ อินเทอร์เฟซบรรทัดคำสั่ง (Command-Line Interface - CLI) ของระบบปฏิบัติการ หรือที่เรียกว่า "shell" ซึ่งสามารถเข้าถึงได้ผ่านแอปพลิเคชัน terminal โดย shell จะแสดง พรอมต์ (prompt) เพื่อรอรับคำสั่งที่คุณพิมพ์ จากนั้นจะแสดงผลลัพธ์ออกมาแล้วรอรับคำสั่งถัดไป</p>
</blockquote>
<blockquote>
<p>TIP จากหลาม: แบบง่าย ๆ ก็คือไม่ต้องพิมพ์ $ และถ้าพิมพ์จบหนึ่งคำสั่งก็กด enter ซ่ะด้วย</p>
</blockquote>
<p>ขั้นตอนในการลง bitcoin core มีดังนี้:</p>
<ol>
<li>สั่ง git clone เพื่อทำการสร้างสำเนาของซอร์สโค้ดลงในเครื่องของเรา</li>
</ol>
<pre><code>$ git clone https://github.com/bitcoin/bitcoin.git
Cloning into 'bitcoin'...
remote: Enumerating objects: 245912, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 245912 (delta 1), reused 2 (delta 1), pack-reused 245909
Receiving objects: 100% (245912/245912), 217.74 MiB | 13.05 MiB/s, done.
Resolving deltas: 100% (175649/175649), done.
</code></pre>
<blockquote>
<p>TIP: Git เป็นระบบควบคุมเวอร์ชันแบบกระจายที่ใช้กันอย่างแพร่หลายและเป็นส่วนสำคัญในเครื่องมือของนักพัฒนาซอฟต์แวร์ คุณอาจจำเป็นต้องติดตั้งคำสั่ง git หรือส่วนต่อประสานกราฟิก (GUI) สำหรับ Git บนระบบปฏิบัติการของคุณ หากยังไม่มี </p>
</blockquote>
<ol start="2">
<li>เมื่อการโคลน Git เสร็จสมบูรณ์แล้ว คุณจะมีสำเนาท้องถิ่นครบถ้วนของที่เก็บซอร์สโค้ดในไดเรกทอรี bitcoin ให้เปลี่ยนไปยังไดเรกทอรีนี้โดยใช้คำสั่ง cd:</li>
</ol>
<pre><code>$ cd bitcoin
</code></pre>
<ol start="3">
<li>เลือก version ของ bitcoin core: โดยค่าเริ่มต้น สำเนาจองเราจะซิงโครไนซ์กับโค้ดล่าสุด ซึ่งอาจเป็นเวอร์ชันที่ไม่เสถียรหรือเบต้าของ Bitcoin ก่อนที่จะคอมไพล์โค้ด ให้เลือกเวอร์ชันเฉพาะโดยการตรวจสอบ (checkout) แท็กของการปล่อย (release tag) ซึ่งจะซิงโครไนซ์สำเนาท้องถิ่นกับสแนปช็อตของที่เก็บซอร์สโค้ดที่ระบุด้วยแท็ก แท็กเหล่านี้ถูกใช้งานโดยนักพัฒนาเพื่อระบุเวอร์ชันของโค้ดตามหมายเลขเวอร์ชัน ซึ่งทำได้โดยใช้คำสั่ง git tag</li>
</ol>
<pre><code>$ git tag
v0.1.5
v0.1.6test1
v0.10.0
...
v0.11.2
v0.11.2rc1
v0.12.0rc1
v0.12.0rc2
...
</code></pre>
<p>รายการแท็กจะแสดงทุกเวอร์ชันที่ปล่อยออกมา โดยทั่วไป release candidates (เวอร์ชันทดสอบ) จะมีต่อท้ายว่า "rc" ส่วนเวอร์ชันเสถียรที่ใช้งานในระบบ production จะไม่มีต่อท้ายอะไรเลย จากรายการด้านบน ให้เลือกเวอร์ชันที่สูงสุด ซึ่งในขณะที่เขียนบทความนี้คือ v24.0.1 เพื่อซิงโครไนซ์โค้ดท้องถิ่นกับเวอร์ชันนี้ ให้ใช้คำสั่ง:</p>
<pre><code>$ git checkout v24.0.1
Note: switching to 'v24.0.1'.
</code></pre>
<p>จากนั้นสั่ง git status เพื่อเช็คเวอร์ชัน</p>
<h3>Configuring the Bitcoin Core Build</h3>
<p>ในโค้ดของบิตคอยน์ที่เราได้ดาวน์โหลดมาในหัวข้อก่อนหน้านั้น มีเอกสารประกอบอยู่หลายไฟล์ โดยคุณสามารถดูเอกสารหลักได้จากไฟล์ README.md ในไดเรกทอรี bitcoin ในบทนี้ เราจะสร้าง daemon (เซิร์ฟเวอร์) ของ Bitcoin Core ซึ่งรู้จักกันในชื่อ bitcoind บน Linux (หรือระบบที่คล้ายกับ Unix) โดยให้ตรวจสอบคำแนะนำสำหรับการคอมไพล์ bitcoind แบบบรรทัดคำสั่งบนแพลตฟอร์มของคุณโดยอ่านไฟล์ doc/build-unix.md นอกจากนี้ ยังมีคำแนะนำสำหรับระบบอื่น ๆ ในไดเรกทอรี doc เช่น build-windows.md สำหรับ Windows จนถึงขณะนี้ คำแนะนำมีให้สำหรับ Android, FreeBSD, NetBSD, OpenBSD, macOS (OSX), Unix</p>
<p>หลังจากนั้นคุณควรตรวจสอบความต้องการเบื้องต้นในการสร้าง (build pre-requisites) ซึ่งระบุไว้ในส่วนแรกของเอกสารการสร้าง สิ่งเหล่านี้คือไลบรารีที่ต้องมีอยู่ในระบบของคุณก่อนที่คุณจะเริ่มคอมไพล์ Bitcoin หากมีไลบรารีที่จำเป็นหายไป กระบวนการสร้างจะล้มเหลวและแสดงข้อผิดพลาด หากเกิดปัญหานี้เพราะคุณพลาด pre-requisite คุณสามารถติดตั้งไลบรารีที่ขาดหายไปแล้วดำเนินการสร้างต่อจากจุดที่ค้างไว้</p>
<p>สมมุติว่า pre-requisite ถูกติดตั้งแล้ว ให้เริ่มกระบวนการสร้างโดยการสร้างชุดสคริปต์สำหรับการสร้างด้วยการรันสคริปต์ autogen.sh:</p>
<hr>
<pre><code>$ ./autogen.sh
libtoolize: putting auxiliary files in AC_CONFIG_AUX_DIR, 'build-aux'.
libtoolize: copying file 'build-aux/ltmain.sh'
libtoolize: putting macros in AC_CONFIG_MACRO_DIRS, 'build-aux/m4'.
 ...
configure.ac:58: installing 'build-aux/missing'
src/Makefile.am: installing 'build-aux/depcomp'
parallel-tests: installing 'build-aux/test-driver'
</code></pre>
<p>สคริปต์ autogen.sh นี้จะสร้างชุดสคริปต์ที่กำหนดค่าอัตโนมัติที่จะตรวจสอบระบบของคุณเพื่อค้นหาการตั้งค่าที่ถูกต้องและตรวจสอบให้แน่ใจว่ามีไลบรารีที่จำเป็นสำหรับการคอมไพล์โค้ด โดยสคริปต์ที่สำคัญที่สุดในสคริปต์เหล่านี้คือสคริปต์ configure ซึ่งมีตัวเลือกต่าง ๆ สำหรับการปรับแต่งกระบวนการสร้าง</p>
<p>ใช้ flag --help เพื่อดูตัวเลือกทั้งหมด:</p>
<pre><code>$ ./configure --help
`configure' configures Bitcoin Core 24.0.1 to adapt to many kinds of systems.
Usage: ./configure [OPTION]... [VAR=VALUE]...
...
Optional Features:
  --disable-option-checking  ignore unrecognized --enable/--with options
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
  --enable-silent-rules   less verbose build output (undo: "make V=1")
  --disable-silent-rules  verbose build output (undo: "make V=0")
...
</code></pre>
<p>สคริปต์ configure ช่วยให้คุณสามารถเปิดหรือปิดคุณสมบัติบางอย่างของ bitcoind ผ่านการใช้ flag --enable-FEATURE และ --disable-FEATURE โดยที่ FEATURE แทนชื่อคุณสมบัติที่ระบุในข้อความช่วยเหลือ ในบทนี้ เราจะสร้าง bitcoind ด้วยคุณสมบัติตั้งต้นทั้งหมด โดยไม่ใช้ flag การกำหนดค่าเพิ่มเติม แต่คุณควรตรวจสอบตัวเลือกเหล่านี้เพื่อเข้าใจว่ามีคุณสมบัติเพิ่มเติมอะไรบ้าง หากคุณอยู่ในสภาพแวดล้อมทางการศึกษา ห้องปฏิบัติการคอมพิวเตอร์ หรือมีข้อจำกัดในการติดตั้งโปรแกรม คุณอาจต้องติดตั้งแอปพลิเคชันไว้ในไดเรกทอรี home (เช่นโดยใช้ flag --prefix=$HOME)</p>
<p>ตัวเลือกที่มีประโยชน์สำหรับการกำหนดค่า</p>
<ul>
<li>--prefix=$HOME: เปลี่ยนตำแหน่งการติดตั้งเริ่มต้น (ซึ่งโดยปกติคือ /usr/local/) ให้เป็นไดเรกทอรี home ของคุณ หรือเส้นทางที่คุณต้องการ</li>
<li>--disable-wallet: ใช้เพื่อปิดการใช้งานฟังก์ชัน wallet แบบอ้างอิง</li>
<li>--with-incompatible-bdb: หากคุณกำลังสร้าง wallet ให้ยอมรับการใช้ไลบรารี Berkeley DB เวอร์ชันที่ไม่เข้ากันได้</li>
<li>--with-gui=no: ไม่สร้างส่วนติดต่อผู้ใช้แบบกราฟิก (GUI) ซึ่งต้องใช้ไลบรารี Qt โดยตัวเลือกนี้จะสร้างเฉพาะเซิร์ฟเวอร์และ Bitcoin Core แบบ commandline เท่านั้น</li>
</ul>
<pre><code>
</code></pre>
<p>ต่อไป ให้รันสคริปต์ configure เพื่อให้ระบบตรวจสอบไลบรารีที่จำเป็นทั้งหมดและสร้างสคริปต์สำหรับการสร้างที่ปรับแต่งให้ตรงกับระบบของคุณ:</p>
<pre><code>$ ./configure
checking for pkg-config... /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for a BSD-compatible install... /usr/bin/install -c
...
[many pages of configuration tests follow]
...
</code></pre>
<p>หากทุกอย่างดำเนินไปด้วยดี คำสั่ง configure จะสิ้นสุดด้วยการสร้างสคริปต์การสร้างที่ปรับแต่งให้กับระบบของคุณ แต่หากมีไลบรารีที่หายไปหรือเกิดข้อผิดพลาด คำสั่ง configure จะหยุดและแสดงข้อผิดพลาดแทนที่จะสร้างสคริปต์ในกรณีที่เกิดข้อผิดพลาดขึ้น สาเหตุที่พบบ่อยคือการขาดหายหรือความไม่เข้ากันของไลบรารี ให้ตรวจสอบเอกสารการสร้างอีกครั้งและติดตั้ง pre-requisite ที่ขาดไป จากนั้นรัน configure อีกครั้งเพื่อดูว่าปัญหานั้นได้รับการแก้ไขแล้วหรือไม่</p>
<h3>การสร้าง Executable ของ Bitcoin Core</h3>
<p>ต่อไป คุณจะทำการคอมไพล์ซอร์สโค้ด กระบวนการนี้อาจใช้เวลาถึงหนึ่งชั่วโมง ขึ้นอยู่กับความเร็วของ CPU และหน่วยความจำที่มีอยู่ หากเกิดข้อผิดพลาด หรือการคอมไพล์ถูกขัดจังหวะ คุณสามารถดำเนินการต่อได้โดยการพิมพ์คำสั่ง make อีกครั้ง</p>
<p>พิมพ์ make เพื่อเริ่มคอมไพล์แอปพลิเคชันที่สามารถรันได้:</p>
<pre><code>$ make
Making all in src
  CXX      bitcoind-bitcoind.o
  CXX      libbitcoin_node_a-addrdb.o
  CXX      libbitcoin_node_a-addrman.o
  CXX      libbitcoin_node_a-banman.o
  CXX      libbitcoin_node_a-blockencodings.o
  CXX      libbitcoin_node_a-blockfilter.o
[... many more compilation messages follow ...]
</code></pre>
<p>บนระบบที่มีความเร็วและมี CPU หลายคอร์ คุณอาจต้องการตั้งค่าจำนวนงานคอมไพล์แบบขนาน (parallel compile jobs) เช่น การใช้คำสั่ง make -j 2 จะใช้สองคอร์หากมีอยู่ หากทุกอย่างดำเนินไปด้วยดี Bitcoin Core จะถูกคอมไพล์เรียบร้อยแล้ว คุณควรรันชุดการทดสอบหน่วย (unit test suite) ด้วยคำสั่ง make check เพื่อให้แน่ใจว่าไลบรารีที่ลิงค์เข้าด้วยกันไม่มีข้อผิดพลาดอย่าง ขั้นตอนสุดท้ายคือการติดตั้ง executable ต่าง ๆ ลงในระบบของคุณโดยใช้คำสั่ง make install ซึ่งอาจมีการร้องขอรหัสผ่านของผู้ใช้เนื่องจากขั้นตอนนี้ต้องการสิทธิ์ผู้ดูแลระบบ:</p>
<pre><code>$ make check &amp;&amp; sudo make install
Password:
Making install in src
 ../build-aux/install-sh -c -d '/usr/local/lib'
libtool: install: /usr/bin/install -c bitcoind /usr/local/bin/bitcoind
libtool: install: /usr/bin/install -c bitcoin-cli /usr/local/bin/bitcoin-cli
libtool: install: /usr/bin/install -c bitcoin-tx /usr/local/bin/bitcoin-tx
...
</code></pre>
<p>การติดตั้งเริ่มต้นของ bitcoind จะอยู่ในไดเรกทอรี /usr/local/bin โดยคุณสามารถตรวจสอบว่า Bitcoin Core ถูกติดตั้งเรียบร้อยแล้วโดยใช้คำสั่งเพื่อตรวจสอบตำแหน่งของ executable ดังนี้:</p>
<pre><code>$ which bitcoind
/usr/local/bin/bitcoind
$ which bitcoin-cli
/usr/local/bin/bitcoin-cli
</code></pre>
<h3>มาลองรัน Bitcoin node กันเถอะ</h3>
<p>อย่างที่ได้กล่าวในบทก่อนหน้า เครือข่ายแบบเพียร์ทูเพียร์ของบิตคอยน์ประกอบด้วยเครือข่าย "โหนด" ซึ่งส่วนใหญ่รันโดยบุคคลและธุรกิจบางแห่งที่ให้บริการ ผู้ที่รันโหนดบิตคอยน์จะมีมุมมองที่ตรงและน่าเชื่อถือเกี่ยวกับบล๊อกเชนของบิตคอยน์พร้อมสำเนาข้อมูลบิตคอยน์ที่ใช้จ่ายได้ทั้งหมดซึ่งได้รับการตรวจสอบอย่างอิสระโดยระบบของตนเอง การรันโหนดทำให้คุณไม่ต้องพึ่งบุคคลที่สามในการตรวจสอบธุรกรรม นอกจากนี้การใช้โหนดบิตคอยน์เพื่อตรวจสอบธุรกรรมที่ได้รับในกระเป๋าเงินของคุณ ยังช่วยให้คุณมีส่วนร่วมในเครือข่ายบิตคอยน์และช่วยทำให้เครือข่ายมีความแข็งแกร่งมากขึ้นอีกด้วย</p>
<p>การรันโหนดต้องดาวน์โหลดและประมวลผลข้อมูลมากกว่า 500 GB ในช่วงเริ่มแรก และประมาณ 400 MB ของธุรกรรม Bitcoin ต่อวัน ตัวเลขเหล่านี้เป็นของปี 2023 และอาจเพิ่มขึ้นในอนาคต หากคุณปิดโหนดหรือหลุดจากอินเทอร์เน็ตเป็นเวลาหลายวัน โหนดของคุณจะต้องดาวน์โหลดข้อมูลที่พลาดไป ตัวอย่างเช่น หากคุณปิด Bitcoin Core เป็นเวลา 10 วัน คุณจะต้องดาวน์โหลดประมาณ 4 GB ในครั้งถัดไปที่คุณเริ่มใช้งาน</p>
<p>ขึ้นอยู่กับการเลือกของคุณว่าจะทำดัชนีธุรกรรมทั้งหมดและเก็บสำเนาบล๊อกเชนแบบเต็ม คุณอาจต้องใช้พื้นที่ดิสก์มาก - อย่างน้อย 1 TB หากคุณวางแผนจะรัน Bitcoin Core เป็นเวลาหลายปี โดยค่าเริ่มต้นโหนดบิตคอยน์ยังส่งธุรกรรมและบล็อกไปยังโหนดอื่น ๆ (เรียกว่า "เพียร์") ซึ่งจะใช้แบนด์วิดท์อัปโหลดอินเทอร์เน็ต หากการเชื่อมต่ออินเทอร์เน็ตของคุณมีขีดจำกัด มีขีดจำกัดการใช้ข้อมูลต่ำ หรือคิดค่าบริการตามข้อมูล (เมตเตอร์) คุณไม่ควรรันโหนดบิตคอยน์บนระบบนั้น หรือรันโดยจำกัดแบนด์วิดท์ (ดู การกำหนดค่าโหนด Bitcoin Core) คุณอาจเชื่อมต่อโหนดของคุณแทนไปยังเครือข่ายทางเลือก เช่น ผู้ให้บริการข้อมูลดาวเทียมฟรีอย่าง Blockstream Satellite</p>
<blockquote>
<p>Tip: Bitcoin Core เก็บสำเนาบล๊อกเชนแบบเต็ม (ตามค่าเริ่มต้น ) พร้อมธุรกรรมเกือบทั้งหมดที่เคยได้รับการยืนยันบนเครือข่าย Bitcoin ตั้งแต่เริ่มต้นในปี 2009 ชุดข้อมูลนี้มีขนาดหลายร้อย GB และจะถูกดาวน์โหลดเพิ่มขึ้นทีละน้อยในช่วงหลายชั่วโมงหรือหลายวัน ขึ้นอยู่กับความเร็ว CPU และการเชื่อมต่ออินเทอร์เน็ตของคุณ Bitcoin Core จะไม่สามารถประมวลผลธุรกรรมหรืออัปเดตยอดคงเหลือของบัญชีจนกว่าชุดข้อมูล blockchain จะดาวน์โหลดเสร็จสมบูรณ์ ตรวจสอบให้แน่ใจว่าคุณมีพื้นที่ดิสก์ แบนด์วิดท์ และเวลาเพียงพอในการซิงโครไนซ์เริ่มแรก คุณสามารถกำหนดค่า Bitcoin Core เพื่อลดขนาด blockchain โดยการทิ้งบล็อกเก่า แต่โปรแกรมยังคงดาวน์โหลดชุดข้อมูลทั้งหมด</p>
</blockquote>
<blockquote>
<p>TIPจากหลาม agian: ซื้อ NVMe SSD 2TB เป็นอย่างต่ำซ่ะ m.2 ได้ยิ่งดีเลยจ้า </p>
</blockquote>
<p>แม้ว่าจะมีข้อกำหนดด้านทรัพยากรเหล่านี้ แต่มีผู้คนหลายพันรายที่รันโหนด Bitcoin บางคนรันบนระบบง่าย ๆ อย่าง Raspberry Pi (คอมพิวเตอร์ราคา 35 เหรียญสหรัฐที่มีขนาดเท่ากับกล่องบุหรี่)</p>
<p>ทำไมคุณถึงอยากรันโหนด? นี่คือเหตุผลที่พบบ่อยที่สุด:</p>
<ul>
<li>คุณไม่ต้องการพึ่งบุคคลที่สามในการตรวจสอบธุรกรรมที่คุณได้รับ</li>
<li>คุณไม่ต้องการเปิดเผยให้บุคคลที่สามรู้ว่าธุรกรรมใดเป็นของกระเป๋าเงินคุณ</li>
<li>คุณกำลังพัฒนาซอฟต์แวร์ Bitcoin และต้องการพึ่งโหนด Bitcoin เพื่อเข้าถึงเครือข่ายและ blockchain ผ่าน API</li>
<li>คุณกำลังสร้างแอปพลิเคชันที่ต้องตรวจสอบธุรกรรมตามกฎฉันทามติของ Bitcoin โดยทั่วไป บริษัทซอฟต์แวร์ Bitcoin มักจะรันโหนดหลายโหนด</li>
<li>คุณต้องการสนับสนุน Bitcoin การรันโหนดที่คุณใช้ตรวจสอบธุรกรรมที่ได้รับในกระเป๋าเงินจะช่วยทำให้เครือข่ายมีความแข็งแกร่งมากขึ้น</li>
</ul>
<p>หากคุณกำลังอ่านหนังสือเล่มนี้และสนใจความปลอดภัยที่เข้มงวด ความเป็นส่วนตัวที่เหนือกว่า หรือการพัฒนาซอฟต์แวร์ Bitcoin คุณควรรันโหนดของตัวเอง</p>
<h3>การกำหนดค่าโหนด Bitcoin Core</h3>
<p>Bitcoin Core จะค้นหาไฟล์การกำหนดค่าในไดเรกทอรีข้อมูลทุกครั้งที่เริ่มทำงาน ในส่วนนี้เราจะตรวจสอบตัวเลือกการกำหนดค่าต่าง ๆ และตั้งค่าไฟล์การกำหนดค่า</p>
<p>เพื่อค้นหาไฟล์การกำหนดค่า ให้รัน bitcoind -printtoconsole ในเทอร์มินัลของคุณ และดูบรรทัดแรก ๆ:</p>
<pre><code>$ bitcoind -printtoconsole
2023-01-28T03:21:42Z Bitcoin Core version v24.0.1
2023-01-28T03:21:42Z Using the 'x86_shani(1way,2way)' SHA256 implementation
2023-01-28T03:21:42Z Using RdSeed as an additional entropy source
2023-01-28T03:21:42Z Using RdRand as an additional entropy source
2023-01-28T03:21:42Z Default data directory /home/harding/.bitcoin
2023-01-28T03:21:42Z Using data directory /home/harding/.bitcoin
2023-01-28T03:21:42Z Config file: /home/harding/.bitcoin/bitcoin.conf
...
[a lot more debug output]
...
</code></pre>
<blockquote>
<p>tatatipจากหลามอีกครั้ง: สังเกตเห็นหรือไม่ว่าในตัวอย่างนี้ Bitcoin Core กำลังชี้ไปที่ไฟล์การกำหนดค่าที่ไดเรกทอรี /home/harding/.bitcoin/bitcoin.conf ซึ่งจะแตกต่างกันไปขึ้นอยู่กับผู้ใช้และระบบปฏิบัติการ</p>
</blockquote>
<p>คุณสามารถกด Ctrl-C เพื่อปิดโหนดหลังจากที่ระบุตำแหน่งไฟล์การกำหนดค่า โดยปกติไฟล์การกำหนดค่าจะอยู่ในไดเรกทอรี .bitcoin ภายใต้โฮมไดเรกทอรีของผู้ใช้ เปิดไฟล์ configuration ด้วยโปรแกรมแก้ไขได้ตามที่คุณชอบ</p>
<p>Bitcoin Core มีตัวเลือกการกำหนดค่ามากกว่า 100 ตัวเลือกที่สามารถปรับเปลี่ยนพฤติกรรมของโหนดเครือข่าย การจัดเก็บบล๊อกเชน และแง่มุมอื่น ๆ ของการทำงาน เพื่อดูรายการตัวเลือก ให้รัน bitcoind --help:</p>
<pre><code>$ bitcoind --help
Bitcoin Core version v24.0.1
Usage:  bitcoind [options]                     Start Bitcoin Core
Options:
  -?
       Print this help message and exit
  -alertnotify=&lt;cmd&gt;
       Execute command when an alert is raised (%s in cmd is replaced by
       message)
...
[many more options]
</code></pre>
<p>นี่คือตัวเลือกที่บางประการที่คุณสามารถตั้งในไฟล์ configuration หรือเป็นพารามิเตอร์บรรทัดคำสั่งสำหรับ bitcoind:</p>
<ul>
<li>alertnotify: เรียกใช้คำสั่งหรือสคริปต์เพื่อส่งการแจ้งเตือนฉุกเฉินไปยังเจ้าของโหนดนี้</li>
<li>conf: ตำแหน่งทางเลือกสำหรับไฟล์ configuration เมื่อใช้เป็นพารามิเตอร์ cli สำหรับ bitcoind เท่านั้น เนื่องจากไม่สามารถอยู่ในไฟล์ configuration ที่มันอ้างถึงได้</li>
<li>datadir: เลือกไดเรกทอรีและระบบไฟล์สำหรับจัดเก็บข้อมูลบล๊อกเชนตามค่าเริ่มต้นนี้คือไดเรกทอรีย่อย .bitcoin ในไดเรกทอรีโฮมของคุณ ขึ้นอยู่กับการกำหนดค่า สามารถใช้พื้นที่ตั้งแต่ประมาณ 10 GB ถึงเกือบ 1 TB ณ ขณะนี้ คาดว่าขนาดสูงสุดจะเพิ่มขึ้นหลายร้อย GB ต่อปี</li>
<li>prune: ลดความต้องการพื้นที่ดิสก์บล๊อกเชนลงเหลือกี่เมกะไบต์โดยการลบบล็อกเก่า ใช้สำหรับโหนดที่มีทรัพยากรจำกัดซึ่งไม่สามารถบรรจุบล๊อกเชนแบบเต็มได้ ส่วนอื่น ๆ ของระบบจะใช้พื้นที่ดิสก์อื่นที่ไม่สามารถตัดทอนได้ ดังนั้นคุณยังคงต้องมีพื้นที่อย่างน้อยตามที่ระบุในตัวเลือก datadir</li>
<li>txindex: รักษาดัชนีของธุรกรรมทั้งหมด ช่วยให้คุณสามารถดึงธุรกรรมใด ๆ โดยใช้ ID ของมันได้โดยโปรแกรม โดยที่บล็อกที่มีธุรกรรมนั้นยังไม่ถูกตัดทอน</li>
<li>dbcache: ขนาดของแคช UTXO ค่าเริ่มต้นคือ 450 เมบิไบต์ (MiB) เพิ่มขนาดนี้บนฮาร์ดแวร์ระดับสูงเพื่ออ่านและเขียนจากดิสก์น้อยลง หรือลดขนาดลงบนฮาร์ดแวร์ระดับต่ำเพื่อประหยัดหน่วยความจำโดยยอมให้ใช้ดิสก์บ่อยขึ้น</li>
<li>blocksonly: ลดการใช้แบนด์วิดท์โดยการรับเฉพาะบล็อกของธุรกรรมที่ได้รับการยืนยันจากเพียร์ แทนที่จะส่งต่อธุรกรรมที่ยังไม่ได้รับการยืนยัน</li>
<li>maxmempool: จำกัดพูลหน่วยความจำของธุรกรรมเป็นกี่เมกะไบต์ ใช้เพื่อลดการใช้หน่วยความจำบนโหนดที่มีหน่วยความจำจำกัด</li>
</ul>
<h3>ดัชนีฐานข้อมูลธุรกรรมและตัวเลือก txindex</h3>
<p>ตามค่าเริ่มต้น Bitcoin Core จะสร้างฐานข้อมูลที่มีเพียงธุรกรรมที่เกี่ยวข้องกับกระเป๋าเงินของผู้ใช้เท่านั้น หากคุณต้องการสามารถเข้าถึงธุรกรรมใด ๆ ด้วยคำสั่งเช่น getrawtransaction คุณจะต้องกำหนดค่า Bitcoin Core ให้สร้างดัชนีธุรกรรมแบบสมบูรณ์ ซึ่งสามารถทำได้ด้วยตัวเลือก txindex โดยตั้ง txindex=1 ในไฟล์การกำหนดค่า Bitcoin Core หากคุณไม่ได้ตั้งตัวเลือกนี้ตั้งแต่แรกและต่อมาตั้งเป็นการทำดัชนีแบบเต็ม คุณจะต้องรอให้มันสร้างดัชนีใหม่</p>
<p>ตัวอย่างการกำหนดค่าโหนดดัชนีแบบเต็มแสดงวิธีที่คุณอาจรวมตัวเลือกก่อนหน้านี้กับโหนดที่มีดัชนีแบบเต็ม โดยทำงานเป็นแบ็กเอนด์ API สำหรับแอปพลิเคชัน bitcoin</p>
<p>ตัวอย่างที่ 1. การกำหนดค่าโหนดดัชนีแบบเต็ม</p>
<pre><code>alertnotify=myemailscript.sh "Alert: %s"
datadir=/lotsofspace/bitcoin
txindex=1
</code></pre>
<p>ตัวอย่างที่ 2. การกำหนดค่าระบบที่มีทรัพยากรจำกัด</p>
<pre><code>alertnotify=myemailscript.sh "Alert: %s"
blocksonly=1
prune=5000
dbcache=150
maxmempool=150
</code></pre>
<p>หลังจากที่คุณแก้ไขไฟล์การกำหนดค่าและตั้งตัวเลือกที่ดีที่สุดตามความต้องการของคุณ คุณสามารถทดสอบ bitcoind ด้วยการกำหนดค่านี้ รัน Bitcoin Core ด้วยตัวเลือก printtoconsole เพื่อรันที่ foreground พร้อมแสดงผลลัพธ์ที่คอนโซล:</p>
<pre><code>$ bitcoind -printtoconsole
2023-01-28T03:43:39Z Bitcoin Core version v24.0.1
2023-01-28T03:43:39Z Using the 'x86_shani(1way,2way)' SHA256 implementation
2023-01-28T03:43:39Z Using RdSeed as an additional entropy source
2023-01-28T03:43:39Z Using RdRand as an additional entropy source
2023-01-28T03:43:39Z Default data directory /home/harding/.bitcoin
2023-01-28T03:43:39Z Using data directory /lotsofspace/bitcoin
2023-01-28T03:43:39Z Config file: /home/harding/.bitcoin/bitcoin.conf
2023-01-28T03:43:39Z Config file arg: [main] blockfilterindex="1"
2023-01-28T03:43:39Z Config file arg: [main] maxuploadtarget="1000"
2023-01-28T03:43:39Z Config file arg: [main] txindex="1"
2023-01-28T03:43:39Z Setting file arg: wallet = ["msig0"]
2023-01-28T03:43:39Z Command-line arg: printtoconsole=""
2023-01-28T03:43:39Z Using at most 125 automatic connections
2023-01-28T03:43:39Z Using 16 MiB out of 16 MiB requested for signature cache
2023-01-28T03:43:39Z Using 16 MiB out of 16 MiB requested for script execution
2023-01-28T03:43:39Z Script verification uses 3 additional threads
2023-01-28T03:43:39Z scheduler thread start
2023-01-28T03:43:39Z [http] creating work queue of depth 16
2023-01-28T03:43:39Z Using random cookie authentication.
2023-01-28T03:43:39Z Generated RPC cookie /lotsofspace/bitcoin/.cookie
2023-01-28T03:43:39Z [http] starting 4 worker threads
2023-01-28T03:43:39Z Using wallet directory /lotsofspace/bitcoin/wallets
2023-01-28T03:43:39Z init message: Verifying wallet(s)…
2023-01-28T03:43:39Z Using BerkeleyDB version Berkeley DB 4.8.30
2023-01-28T03:43:39Z Using /16 prefix for IP bucketing
2023-01-28T03:43:39Z init message: Loading P2P addresses…
2023-01-28T03:43:39Z Loaded 63866 addresses from peers.dat  114ms
[... more startup messages ...]
</code></pre>
<p>คุณสามารถกด Ctrl-C เพื่อหยุดกระบวนการเมื่อคุณพอใจว่ากำลังโหลดการตั้งค่าที่ถูกต้องและทำงานตามที่คาดหวัง</p>
<p>เพื่อรัน Bitcoin Core ที่พื้นหลังเป็นโพรเซส ให้เริ่มด้วยตัวเลือก daemon เช่น bitcoind -daemon</p>
<p>เพื่อตรวจสอบความคืบหน้าและสถานะการทำงานของโหนด Bitcoin ให้เริ่มในโหมด daemon แล้วใช้คำสั่ง bitcoin-cli getblockchaininfo:</p>
<pre><code>$ bitcoin-cli getblockchaininfo

{
"chain": "main",
"blocks": 0,
"headers": 83999,
"bestblockhash": "[...]19d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
"difficulty": 1,
"time": 1673379796,
"mediantime": 1231006505,
"verificationprogress": 3.783041623201835e-09,
"initialblockdownload": true,
"chainwork": "[...]000000000000000000000000000000000000000000000100010001",
"size_on_disk": 89087,
"pruned": false,
"warnings": ""
}
</code></pre>
<p>นี่แสดงโหนดที่มีความสูงของ blockchain เป็น 0 บล็อก และ 83,999 เฮดเดอร์ โหนดจะดึงเฮดเดอร์ของบล็อกจากเพียร์ของตนก่อนเพื่อค้นหา blockchain ที่มีหลักฐานการทำงานมากที่สุด จากนั้นจึงดำเนินการดาวน์โหลดบล็อกเต็มโดยตรวจสอบความถูกต้องไปพร้อมกัน</p>
<p>เมื่อคุณพอใจกับตัวเลือกการกำหนดค่าที่เลือก คุณควรเพิ่ม Bitcoin Core ลงในสคริปต์เริ่มต้นของระบบปฏิบัติการ เพื่อให้มันทำงานอย่างต่อเนื่องและรีสตาร์ทเมื่อระบบปฏิบัติการรีสตาร์ท คุณจะพบสคริปต์เริ่มต้นตัวอย่างสำหรับระบบปฏิบัติการต่าง ๆ ในไดเรกทอรีซอร์สของ Bitcoin Core ภายใต้ contrib/init และไฟล์ README.md ที่แสดงว่าระบบใดใช้สคริปต์ใด</p>
<h3>Bitcoin Core API</h3>
<p>Bitcoin Core ใช้อินเทอร์เฟซ JSON-RPC ซึ่งสามารถเข้าถึงได้โดยใช้เครื่องมืออย่าง bitcoin-cli ซึ่งช่วยให้เราสามารถทดลองใช้งานความสามารถต่างๆ แบบโต้ตอบได้ ซึ่งความสามารถเหล่านี้ยังสามารถใช้งานได้ผ่านทาง API ในรูปแบบโปรแกรม เพื่อเริ่มต้น ให้เรียกใช้คำสั่ง help เพื่อดูรายการคำสั่ง Bitcoin Core RPC ที่มีอยู่:</p>
<pre><code>$ bitcoin-cli help
+== Blockchain ==
getbestblockhash
getblock "blockhash" ( verbosity )
getblockchaininfo
...
walletpassphrase "passphrase" timeout
walletpassphrasechange "oldpassphrase" "newpassphrase"
walletprocesspsbt "psbt" ( sign "sighashtype" bip32derivs finalize )
</code></pre>
<p>คำสั่งแต่ละรายการอาจต้องการพารามิเตอร์หลายตัว เพื่อรับความช่วยเหลือเพิ่มเติม คำอธิบายโดยละเอียด และข้อมูลเกี่ยวกับพารามิเตอร์ต่างๆ ให้เพิ่มชื่อคำสั่งหลังคำว่า help ตัวอย่างเช่น เพื่อดูความช่วยเหลือเกี่ยวกับคำสั่ง RPC getblockhash:</p>
<pre><code>$ bitcoin-cli help getblockhash
getblockhash height
Returns hash of block in best-block-chain at height provided.
Arguments:
1. height    (numeric, required) The height index
Result:
"hex"    (string) The block hash
Examples:
&gt; bitcoin-cli getblockhash 1000
&gt; curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest",
  "method": "getblockhash",
  "params": [1000]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
</code></pre>
<p>ในส่วนท้ายของข้อมูลคำสั่ง help คุณจะเห็นตัวอย่างสองตัวอย่างของคำสั่ง RPC ซึ่งใช้ตัวช่วย bitcoin-cli หรือ HTTP client curl ตัวอย่างเหล่านี้แสดงให้เห็นว่าคุณอาจเรียกใช้คำสั่งได้อย่างไร ลองคัดลอกตัวอย่างแรกและดูผลลัพธ์:</p>
<pre><code>$ bitcoin-cli getblockhash 1000
00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09
</code></pre>
<p>ผลลัพธ์คือแฮชของบล็อก ซึ่งจะอธิบายในรายละเอียดเพิ่มเติมในบทต่อไป แต่ในตอนนี้ คำสั่งนี้ควรให้ผลลัพธ์เหมือนกันบนระบบของคุณ ซึ่งแสดงให้เห็นว่าโหนด Bitcoin Core ของคุณกำลังทำงาน กำลังรับคำสั่ง และมีข้อมูลเกี่ยวกับบล็อก 1,000 ที่จะส่งกลับมาให้คุณ</p>
<h3>การรับข้อมูลสถานะของ Bitcoin Core</h3>
<p>Bitcoin Core ให้รายงานสถานะเกี่ยวกับโมดูลต่างๆ ผ่านอินเตอร์เฟส JSON-RPC คำสั่งที่สำคัญที่สุดรวมถึง getblockchaininfo, getmempoolinfo, getnetworkinfo และ getwalletinfo</p>
<p>คำสั่ง RPC getblockchaininfo ของ Bitcoin ได้ถูกแนะนำไปก่อนหน้านี้แล้ว คำสั่ง getnetworkinfo แสดงข้อมูลพื้นฐานเกี่ยวกับสถานะของโหนดเครือข่าย Bitcoin ใช้ bitcoin-cli เพื่อรันคำสั่งนี้:</p>
<pre><code>$ bitcoin-cli getnetworkinfo
{
  "version": 240001,
  "subversion": "/Satoshi:24.0.1/",
  "protocolversion": 70016,
  "localservices": "0000000000000409",
  "localservicesnames": [
    "NETWORK",
    "WITNESS",
    "NETWORK_LIMITED"
  ],
  "localrelay": true,
  "timeoffset": -1,
  "networkactive": true,
  "connections": 10,
  "connections_in": 0,
  "connections_out": 10,
  "networks": [
    "...detailed information about all networks..."
  ],
  "relayfee": 0.00001000,
  "incrementalfee": 0.00001000,
  "localaddresses": [
  ],
  "warnings": ""
}
</code></pre>
<p>ซึ่งข้อมูลต่าง ๆ จะถูกส่งคืนในรูปแบบ JavaScript Object Notation (JSON) ซึ่งเป็นรูปแบบที่สามารถ "อ่าน" ได้อย่างง่ายดายโดยทุกภาษาโปรแกรมมิ่ง และยังเป็นรูปแบบที่มนุษย์อ่านได้ง่ายอีกด้วย ในข้อมูลนี้เราเห็นหมายเลขเวอร์ชันสำหรับซอฟต์แวร์ Bitcoin Core และโปรโตคอลบิตคอยน์เราเห็นจำนวนการเชื่อมต่อในปัจจุบันและข้อมูลต่างๆ เกี่ยวกับเครือข่ายบิตคอยน์และการตั้งค่าที่เกี่ยวข้องกับโหนดนี้</p>
<blockquote>
<p>TIP: จะใช้เวลาสักระยะ อาจมากกว่าหนึ่งวัน สำหรับ bitcoind ในการอัพเดทให้ทันกับบล็อกล่าสุดของบล็อกเชนปัจจุบัน ในขณะที่มันดาวน์โหลดบล็อกจากโหนดอื่นๆ และตรวจสอบความถูกต้องของทุกธุรกรรมในบล็อกเหล่านั้น—ซึ่งมีเกือบหนึ่งพันล้านธุรกรรม ณ เวลาที่เขียนนี้ คุณสามารถตรวจสอบความคืบหน้าโดยใช้ getblockchaininfo เพื่อดูจำนวนบล็อกที่ทราบ ตัวอย่างในส่วนที่เหลือของบทนี้สมมติว่าคุณอยู่อย่างน้อยที่บล็อก 775,072 เนื่องจากความปลอดภัยของธุรกรรมขึ้นอยู่กับจำ</p>
</blockquote>
<h3>มาสำรวจและถอดรหัสธุรกรรมของบิตคอยน์กันเถอะ!!</h3>
<p>อย่างในบทที่สอง อลิซได้ซื้อสินค้าจากร้านของบ็อบและธุรกรรมของเธอถูกบันทึกลงในบล็อกเชนของบิตคอยน์ โดยเราสามารถใช้ API เพื่อดึงและตรวจสอบธุรกรรมนั้นได้โดยการใช้ txid เป็นพารามิเตอร์:</p>
<pre><code>$ bitcoin-cli getrawtransaction 466200308696215bbc949d5141a49a4138ecdfdfaa2a8029c1f9bcecd1f96177
--ผลลัพธ์ของคำสั่ง
01000000000101eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da13569
8679268041c54a0100000000ffffffff02204e0000000000002251203b41daba
4c9ace578369740f15e5ec880c28279ee7f51b07dca69c7061e07068f8240100
000000001600147752c165ea7be772b2c0acb7f4d6047ae6f4768e0141cf5efe
2d8ef13ed0af21d4f4cb82422d6252d70324f6f4576b727b7d918e521c00b51b
e739df2f899c49dc267c0ad280aca6dab0d2fa2b42a45182fc83e81713010000
0000
</code></pre>
<blockquote>
<p>TIP: txid ไม่ใช่สิ่งที่สามารถเชื่อถือได้ขนาดนั้น เพราะการไม่มี txid ในบล๊อกเชนของบิตคอยน์นั้นไม่ได้หมายความว่าธุรกรรมไม่ได้ถูกประมวลผล โดยสิ่งนี้เรียกว่า "transaction malleability" (ความสามารถในการเปลี่ยนแปลงธุรกรรม) เพราะธุรกรรมสามารถถูกแก้ไขก่อนการยืนยันในบล็อก ซึ่งเปลี่ยน txid ของพวกมัน หลังจากธุรกรรมถูกรวมอยู่ในบล็อกแล้ว txid ของมันไม่สามารถเปลี่ยนแปลงได้อีก เว้นแต่จะมีการจัดระเบียบบล็อกเชนใหม่และบล็อกนั้นถูกลบออกจากบล็อกเชนที่ดีที่สุด โดยการจัดระเบียบใหม่เกิดขึ้นได้ยากหลังจากธุรกรรมได้รับการยืนยันหลายครั้งแล้ว</p>
</blockquote>
<p>คำสั่ง getrawtransaction จะส่งคืนธุรกรรมที่เข้ารหัสในรูปแบบเลขฐานสิบหกและเพื่อถอดรหัสนั้น เราใช้คำสั่ง decoderawtransaction โดยส่งข้อมูลเลขฐานสิบหกเป็นพารามิเตอร์ คุณสามารถคัดลอกเลขฐานสิบหกที่ส่งคืนโดย getrawtransaction และวางเป็นพารามิเตอร์ให้กับ decoderawtransaction ได้:</p>
<pre><code>$ bitcoin-cli decoderawtransaction 01000000000101eb3ae38f27191aa5f3850dc9cad00492b88b72404f9da135698679268041c54a0100000000ffffffff02204e0000000000002251203b41daba4c9ace578369740f15e5ec880c28279ee7f51b07dca69c7061e07068f8240100000000001600147752c165ea7be772b2c0acb7f4d6047ae6f4768e0141cf5efe2d8ef13ed0af21d4f4cb82422d6252d70324f6f4576b727b7d918e521c00b51be739df2f899c49dc267c0ad280aca6dab0d2fa2b42a45182fc83e817130100000000
--ผลลัพธ์ของคำสั่ง
{
  "txid": "466200308696215bbc949d5141a49a4138ecdfdfaa2a8029c1f9bcecd1f96177",
  "hash": "f7cdbc7cf8b910d35cc69962e791138624e4eae7901010a6da4c02e7d238cdac",
  "version": 1,
  "size": 194,
  "vsize": 143,
  "weight": 569,
  "locktime": 0,
  "vin": [
    {
      "txid": "4ac541802679866935a19d4f40728bb89204d0cac90d85f3a51a19...aeb",
      "vout": 1,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "txinwitness": [
        "cf5efe2d8ef13ed0af21d4f4cb82422d6252d70324f6f4576b727b7d918e5...301"
      ],
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.00020000,
      "n": 0,
      "scriptPubKey": {
        "asm": "1 3b41daba4c9ace578369740f15e5ec880c28279ee7f51b07dca...068",
        "desc": "rawtr(3b41daba4c9ace578369740f15e5ec880c28279ee7f51b...6ev",
        "hex": "51203b41daba4c9ace578369740f15e5ec880c28279ee7f51b07d...068",
        "address": "bc1p8dqa4wjvnt890qmfws83te0v3qxzsfu7ul63kp7u56w8q...5qn",
        "type": "witness_v1_taproot"
      }
    },
    {
      "value": 0.00075000,
      "n": 1,
      "scriptPubKey": {
        "asm": "0 7752c165ea7be772b2c0acb7f4d6047ae6f4768e",
        "desc": "addr(bc1qwafvze0200nh9vkq4jmlf4sy0tn0ga5w0zpkpg)#qq404gts",
        "hex": "00147752c165ea7be772b2c0acb7f4d6047ae6f4768e",
        "address": "bc1qwafvze0200nh9vkq4jmlf4sy0tn0ga5w0zpkpg",
        "type": "witness_v0_keyhash"
      }
    }
  ]
}
</code></pre>
<p>การถอดรหัสธุรกรรมแสดงส่วนประกอบทั้งหมดของธุรกรรมนี้ รวมถึงอินพุตและเอาต์พุตของธุรกรรม ในกรณีนี้เราเห็นว่าธุรกรรมใช้อินพุตหนึ่งรายการและสร้างเอาต์พุตสองรายการ อินพุตของธุรกรรมนี้คือเอาต์พุตจากธุรกรรมที่ได้รับการยืนยันก่อนหน้านี้ (แสดงเป็น txid ของอินพุต) เอาต์พุตทั้งสองรายการสอดคล้องกับการชำระเงินให้บ็อบและเงินทอนกลับให้อลิซ</p>
<h3>มาสำรวจบล็อกของบิตคอยน์กัน!!</h3>
<p>การสำรวจบล็อกนั้นคล้ายกับการสำรวจธุรกรรม แต่อย่างไรก็ตามบล็อกสามารถอ้างอิงได้ทั้งโดยลำดับของบล็อกหรือโดยแฮชของบล็อก เราใช้คำสั่ง getblockhash ซึ่งรับลำดับของบล็อกเป็นพารามิเตอร์และส่งคืน แฮชของบล็อกนั้น:</p>
<pre><code>$ bitcoin-cli getblockhash 123456
--ผลลัพธ์ของคำสั่ง
0000000000002917ed80650c6174aac8dfc46f5fe36480aaef682ff6cd83c3ca
</code></pre>
<p>ตอนนี้เรารู้แฮชสำหรับบล็อกที่เราเลือกแล้ว เราสามารถดูบล็อกนั้นได้ เราใช้คำสั่ง getblock โดยมีแฮชของบล็อกเป็นพารามิเตอร์:</p>
<pre><code>$ bitcoin-cli getblockhash 0000000000002917ed80650c6174aac8dfc46f5fe36480aaef682ff6cd83c3ca
--ผลลัพธ์ของคำสั่ง
{
  "hash": "0000000000002917ed80650c6174aac8dfc46f5fe36480aaef682ff6cd83c3ca",
  "confirmations": 651742,
  "height": 123456,
  "version": 1,
  "versionHex": "00000001",
  "merkleroot": "0e60651a9934e8f0decd1c[...]48fca0cd1c84a21ddfde95033762d86c",
  "time": 1305200806,
  "mediantime": 1305197900,
  "nonce": 2436437219,
  "bits": "1a6a93b3",
  "difficulty": 157416.4018436489,
  "chainwork": "[...]00000000000000000000000000000000000000541788211ac227bc",
  "nTx": 13,
  "previousblockhash": "[...]60bc96a44724fd72daf9b92cf8ad00510b5224c6253ac40095",
  "nextblockhash": "[...]00129f5f02be247070bf7334d3753e4ddee502780c2acaecec6d66",
  "strippedsize": 4179,
  "size": 4179,
  "weight": 16716,
  "tx": [
    "5b75086dafeede555fc8f9a810d8b10df57c46f9f176ccc3dd8d2fa20edd685b",
    "e3d0425ab346dd5b76f44c222a4bb5d16640a4247050ef82462ab17e229c83b4",
    "137d247eca8b99dee58e1e9232014183a5c5a9e338001a0109df32794cdcc92e",
    "5fd167f7b8c417e59106ef5acfe181b09d71b8353a61a55a2f01aa266af5412d",
    "60925f1948b71f429d514ead7ae7391e0edf965bf5a60331398dae24c6964774",
    "d4d5fc1529487527e9873256934dfb1e4cdcb39f4c0509577ca19bfad6c5d28f",
    "7b29d65e5018c56a33652085dbb13f2df39a1a9942bfe1f7e78e97919a6bdea2",
    "0b89e120efd0a4674c127a76ff5f7590ca304e6a064fbc51adffbd7ce3a3deef",
    "603f2044da9656084174cfb5812feaf510f862d3addcf70cacce3dc55dab446e",
    "9a4ed892b43a4df916a7a1213b78e83cd83f5695f635d535c94b2b65ffb144d3",
    "dda726e3dad9504dce5098dfab5064ecd4a7650bfe854bb2606da3152b60e427",
    "e46ea8b4d68719b65ead930f07f1f3804cb3701014f8e6d76c4bdbc390893b94",
    "864a102aeedf53dd9b2baab4eeb898c5083fde6141113e0606b664c41fe15e1f"
  ]
}
</code></pre>
<p>รายการ confirmations บอกเราถึง ความลึก ของบล็อกนี้—มีกี่บล็อกที่ถูกสร้างทับบนบล็อกนี้ ซึ่งบ่งบอกถึงความยากในการเปลี่ยนแปลงธุรกรรมใดๆ ในบล็อกนี้ ลำดับบอกเราว่ามีกี่บล็อกที่มาก่อนหน้าบล็อกนี้ เราเห็นเวอร์ชันของบล็อก เวลาที่มันถูกสร้าง (ตามข้อมูลของนักขุด) เวลาเฉลี่ยของ 11 บล็อกที่มาก่อนหน้าบล็อกนี้ (การวัดเวลาที่นักขุดปลอมแปลงได้ยากกว่า) และขนาดของบล็อกในการวัดสามแบบต่างกัน (ขนาดดั้งเดิมที่ถูกลบข้อมูลบางส่วนออก, ขนาดเต็ม, และขนาดในหน่วยน้ำหนัก) เรายังเห็นฟิลด์บางอย่างที่ใช้สำหรับความปลอดภัยและหลักฐานการทำงาน (merkle root, nonce, bits, difficulty, และ chainwork) เราจะตรวจสอบสิ่งเหล่านี้โดยละเอียดในส่วนของการขุดในบทที่ 12</p>
<h3>การใช้อินเตอร์เฟสโปรแกรมของ Bitcoin Core</h3>
<p>bitcoin-cli มีประโยชน์มากสำหรับการใช้งาน API ของ Bitcoin Core และการทดสอบฟังก์ชันต่าง ๆ แต่จุดประสงค์หลักของ API คือการเข้าถึงฟังก์ชันด้วยโปรแกรม ในส่วนนี้เราจะสาธิตการเข้าถึง Bitcoin Core จากโปรแกรมอื่น</p>
<p>API ของ Bitcoin Core เป็นอินเตอร์เฟส JSON-RPC โดย JSON เป็นวิธีที่สะดวกมากในการนำเสนอข้อมูลที่ทั้งมนุษย์และโปรแกรมสามารถอ่านได้ง่าย RPC ย่อมาจาก remote procedure call ซึ่งหมายความว่าเรากำลังเรียกใช้กระบวนการ (ฟังก์ชัน) ที่อยู่ห่างไกล (บนโหนด Bitcoin Core) ผ่านโปรโตคอลเครือข่าย ในกรณีนี้ โปรโตคอลเครือข่ายคือ HTTP</p>
<p>เมื่อเราใช้คำสั่ง bitcoin-cli เพื่อขอความช่วยเหลือเกี่ยวกับคำสั่ง มันแสดงตัวอย่างการใช้ curl ซึ่งเป็นไคลเอนต์ HTTP ทางคอมมานด์ไลน์ที่ยืดหยุ่น เพื่อสร้างคำเรียก JSON-RPC เหล่านี้:</p>
<pre><code>$ curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest",
  "method": "getblockchaininfo",
  "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
</code></pre>
<p>คำสั่งนี้แสดงว่า curl ส่งคำขอ HTTP ไปยัง localhost (127.0.0.1) เชื่อมต่อกับพอร์ต RPC เริ่มต้นของ Bitcoin (8332) และส่งคำขอ jsonrpc โดยใช้เมธอดเป็น getblockchaininfo โดยใช้การเข้ารหัสแบบ text/plain</p>
<p>คุณอาจสังเกตว่า curl จะขอให้ส่งข้อมูลประจำตัวไปพร้อมกับคำขอ Bitcoin Core จะสร้างรหัสผ่านแบบสุ่มในแต่ละครั้งที่เริ่มต้นและวางไว้ในไดเรกทอรีข้อมูลภายใต้ชื่อ .cookie โดย bitcoin-cli สามารถอ่านไฟล์รหัสผ่านนี้โดยให้ไดเรกทอรีข้อมูล ในทำนองเดียวกัน คุณสามารถคัดลอกรหัสผ่านและส่งไปยัง curl (หรือตัวครอบ Bitcoin Core RPC ระดับสูงอื่น ๆ ) ตามที่เห็นในการใช้การตรวจสอบสิทธิ์แบบใช้คุกกี้กับ Bitcoin Core</p>
<p>ตัวอย่างที่ 3. การใช้การตรวจสอบสิทธิ์แบบใช้คุกกี้กับ Bitcoin Core</p>
<pre><code>$ cat .bitcoin/.cookie
  __cookie__:17c9b71cef21b893e1a019f4bc071950c7942f49796ed061b274031b17b19cd0
$ curl
  --user __cookie__:17c9b71cef21b893e1a019f4bc071950c7942f49796ed061b274031b17b19cd0
  --data-binary '{"jsonrpc": "1.0", "id":"curltest",
  "method": "getblockchaininfo",
  "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
{"result":{"chain":"main","blocks":799278,"headers":799278,
"bestblockhash":"000000000000000000018387c50988ec705a95d6f765b206b6629971e6978879",
"difficulty":53911173001054.59,"time":1689703111,"mediantime":1689701260,
"verificationprogress":0.9999979206082515,"initialblockdownload":false,
"chainwork":"00000000000000000000000000000000000000004f3e111bf32bcb47f9dfad5b",
"size_on_disk":563894577967,"pruned":false,"warnings":""},"error":null,
"id":"curltest"}
</code></pre>
<p>นอกจากนี้คุณยังสามารถตั้งรหัสผ่านด้วยตัวเองใน ./share/rpcauth/rpcauth.py ภายในไดเรกทอรีของ Bitcoin Core</p>
<p>หากคุณกำลังใช้การเรียก JSON-RPC ในโปรแกรมของคุณเอง คุณสามารถใช้ไลบรารี HTTP ทั่วไปเพื่อสร้างการเรียกได้ คล้ายกับที่แสดงในตัวอย่าง curl ก่อนหน้านี้</p>
<p>อย่างไรก็ตาม มีไลบรารีในภาษาโปรแกรมยอดนิยมส่วนใหญ่ที่ "wrap" API ของ Bitcoin Core ในลักษณะที่ทำให้การใช้งานง่ายขึ้นมาก เราจะใช้ไลบรารี python-bitcoinlib เพื่อทำให้การเข้าถึง API นั้นง่ายขึ้น โดยไลบรารีนี้ไม่ได้เป็นส่วนหนึ่งของโครงการ Bitcoin Core และจำเป็นต้องติดตั้งด้วยวิธีปกติที่คุณติดตั้งไลบรารี Python โปรดจำไว้ว่า การใช้งานนี้ต้องมีอินสแตนซ์ Bitcoin Core ที่กำลังทำงานอยู่ ซึ่งจะถูกใช้เพื่อทำการเรียก JSON-RPC</p>
<p>ตัวอย่างสคริปต์ Python ใน " การทำงาน getblockchaininfo ผ่าน API JSON-RPC ของ Bitcoin Core" ซึ่งทำการเรียก getblockchaininfo อย่างง่ายและพิมพ์พารามิเตอร์ block จากข้อมูลที่ส่งคืนโดย Bitcoin Core</p>
<p>ตัวอย่างที่ 4. การทำงาน getblockchaininfo ผ่าน API JSON-RPC ของ Bitcoin Core</p>
<pre><code>from bitcoin.rpc import RawProxy
# Create a connection to local Bitcoin Core node
p = RawProxy()
# Run the getblockchaininfo command, store the resulting data in info
info = p.getblockchaininfo()
# Retrieve the 'blocks' element from the info
print(info['blocks'])
--ผลลัพธ์ของคำสั่ง
$ python rpc_example.py
773973
</code></pre>
<p>มันบอกเราว่าโหนด Bitcoin Core ในเครื่องของเรามีกี่บล็อกในบล็อกเชนของมัน ซึ่งไม่ใช่ผลลัพธ์ที่น่าทึ่ง แต่มันแสดงการใช้งานพื้นฐานของไลบรารีในฐานะอินเตอร์เฟสที่ถูกทำให้ง่ายขึ้นสำหรับ API JSON-RPC ของ Bitcoin Core</p>
<p>ต่อไป เราจะใช้คำสั่ง getrawtransaction และ decodetransaction เพื่อดึงข้อมูลรายละเอียดของการชำระเงินจาก Alice ไปยัง Bob ในส่วนของการดึงข้อมูลธุรกรรมและการวนลูปเอาต์พุตของธุรกรรม เราจะดึงธุรกรรมของ Alice และแสดงรายการเอาต์พุตของธุรกรรม สำหรับแต่ละเอาต์พุต เราจะแสดงที่อยู่ผู้รับและมูลค่า โดยธุรกรรมของ Alice มีเอาต์พุตหนึ่งรายการที่จ่ายให้ Bob และอีกหนึ่งรายการเป็นเงินทอนกลับไปยัง Alice</p>
<p>ตัวอย่างที่ 5 การดึงข้อมูลธุรกรรมและการวนลูปเอาต์พุตของธุรกรรม</p>
<pre><code>from bitcoin.rpc import RawProxy
p = RawProxy()
# Alice's transaction ID
txid = "466200308696215bbc949d5141a49a4138ecdfdfaa2a8029c1f9bcecd1f96177"
# First, retrieve the raw transaction in hex
raw_tx = p.getrawtransaction(txid)
# Decode the transaction hex into a JSON object
decoded_tx = p.decoderawtransaction(raw_tx)
# Retrieve each of the outputs from the transaction
for output in decoded_tx['vout']:
    print(output['scriptPubKey']['address'], output['value'])
--ผลลัพธ์ของคำสั่ง
$ python rpc_transaction.py
bc1p8dqa4wjvnt890qmfws83te0v3qxzsfu7ul63kp7u56w8qc0qwp5qv995qn 0.00020000
bc1qwafvze0200nh9vkq4jmlf4sy0tn0ga5w0zpkpg 0.00075000
</code></pre>
<p>ตัวอย่างทั้งสองข้างต้นค่อนข้างง่าย คุณไม่จำเป็นต้องใช้โปรแกรมในการรันพวกมัน คุณสามารถใช้ตัวช่วย bitcoin-cli ได้ง่าย ๆ แต่อย่างไรก็ตาม ตัวอย่างถัดไปต้องใช้การเรียก RPC หลายร้อยครั้งและแสดงให้เห็นถึงการใช้อินเทอร์เฟซเชิงโปรแกรมได้ชัดเจนยิ่งขึ้น</p>
<p>ในส่วนของการดึงข้อมูลบล็อกและการรวมเอาต์พุตของทุกธุรกรรม เราจะเริ่มต้นด้วยการดึงข้อมูลบล็อก จากนั้นดึงข้อมูลธุรกรรมแต่ละรายการภายในบล็อกโดยอ้างอิงถึง ID ของแต่ละธุรกรรม ต่อมา เราจะวนลูปผ่านเอาต์พุตของแต่ละธุรกรรมและรวมมูลค่าทั้งหมด</p>
<p>ตัวอย่างที่ 6 การดึงข้อมูลบล็อกและการรวมเอาต์พุตของทุกธุรกรรม</p>
<pre><code>from bitcoin.rpc import RawProxy
p = RawProxy()
# The block height where Alice's transaction was recorded
blockheight = 775072
# Get the block hash of the block at the given height
blockhash = p.getblockhash(blockheight)
# Retrieve the block by its hash
block = p.getblock(blockhash)
# Element tx contains the list of all transaction IDs in the block
transactions = block['tx']
block_value = 0
# Iterate through each transaction ID in the block
for txid in transactions:
    tx_value = 0
    # Retrieve the raw transaction by ID
    raw_tx = p.getrawtransaction(txid)
    # Decode the transaction
    decoded_tx = p.decoderawtransaction(raw_tx)
    # Iterate through each output in the transaction
    for output in decoded_tx['vout']:
        # Add up the value of each output
        tx_value = tx_value + output['value']
    # Add the value of this transaction to the total
    block_value = block_value + tx_value
print("Total value in block: ", block_value)
--ผลลัพธ์ของคำสั่ง
$ python rpc_block.py
Total value in block:  10322.07722534
</code></pre>
<p>โค้ดตัวอย่างของเราคำนวณว่ามูลค่ารวมที่ถูกทำธุรกรรมในบล็อกนี้คือ 10,322.07722534 BTC (รวมถึงรางวัล 25 BTC และค่าธรรมเนียม 0.0909 BTC) ลองเปรียบเทียบกับจำนวนที่รายงานโดยเว็บไซต์สำรวจบล็อก (block explorer) โดยการค้นหาแฮชของบล็อกหรือเลขลำดับของบล็อก เครื่องมือสำรวจบล็อกบางตัวรายงานมูลค่ารวมโดยไม่รวมรางวัลและไม่รวมค่าธรรมเนียม ลองดูว่าคุณสามารถสังเกตเห็นความแตกต่างได้หรือไม่</p>
<h3>ไคลเอนต์ทางเลือก, ไลบรารี, และชุดเครื่องมือ</h3>
<p>C/C++<br>Bitcoin Core:การใช้งานอ้างอิงของ Bitcoin<br>JavaScript<br>bcoin: การใช้งานโหนดแบบเต็มรูปแบบที่มีความยืดหยุ่นและขยายได้พร้อม API<br>Bitcore: โหนดเต็มรูปแบบ, API, และไลบรารีโดย Bitpay<br>BitcoinJS: ไลบรารี Bitcoin ที่เขียนด้วย JavaScript ล้วนๆ สำหรับ node.js และเบราว์เซอร์<br>Java<br>bitcoinj: ไลบรารีไคลเอนต์โหนดเต็มรูปแบบที่เขียนด้วย Java<br>Python<br>python-bitcoinlib: ไลบรารี Bitcoin, ไลบรารีฉันทามติ, และโหนดที่เขียนด้วย Python โดย Peter Todd<br>pycoin: ไลบรารี Bitcoin ที่เขียนด้วย Python โดย Richard Kiss<br>Go<br>btcd: ไคลเอนต์ Bitcoin โหนดเต็มรูปแบบที่เขียนด้วยภาษา Go<br>Rust<br>rust-bitcoin: ไลบรารี Bitcoin ที่เขียนด้วย Rust สำหรับการจัดรูปแบบข้อมูล, การแยกวิเคราะห์, และการเรียกใช้ API<br>Scala<br>bitcoin-s: การใช้งาน Bitcoin ที่เขียนด้วย Scala<br>C#<br>NBitcoin: ไลบรารี Bitcoin ที่ครอบคลุมสำหรับเฟรมเวิร์ก .NET</p>
<p>ยังมีไลบรารีอีกมากมายในภาษาโปรแกรมมิ่งอื่น ๆ อีกหลากหลาย และมีการสร้างขึ้นใหม่อยู่ตลอดเวลา</p>
<p>หากคุณทำตามคำแนะนำในบทนี้ ตอนนี้คุณมี Bitcoin Core ที่ทำงานอยู่และได้เริ่มสำรวจเครือข่ายและบล็อกเชนโดยใช้โหนดของคุณเอง จากนี้ไปคุณสามารถใช้ซอฟต์แวร์ที่คุณควบคุมได้โดยอิสระ—บนคอมพิวเตอร์ที่คุณควบคุม—เพื่อตรวจสอบว่า bitcoin ใด ๆ ที่คุณได้รับปฏิบัติตามกฎทุกข้อในระบบ Bitcoin โดยไม่ต้องไว้วางใจองค์กรภายนอกใด ๆ ในบทต่อไป เราจะเรียนรู้เพิ่มเติมเกี่ยวกับกฎของระบบและวิธีที่โหนดและกระเป๋าเงินของคุณใช้กฎเหล่านั้นเพื่อรักษาความปลอดภัยของเงินของคุณ ปกป้องความเป็นส่วนตัวของคุณ และทำให้การใช้จ่ายและการรับเงินสะดวกสบาย</p>
<p>ฮึ่ ๆ หลาม ๆ มาอีกแล้ว จริง ๆ เนื้อหาของบทที่สามมันจบลงตรงนี้แหละ แต่ว่าถ้าสมมุตืว่าใครลองไปทำตามจริง ๆ แล้วอยากรู้ว่าเราสามารถทำอะไรจาก node ของเราได้อีกบ้าง เลยมีกิจกรรมขำ ๆ มาให้ทำครับ โดยความยากจะมีทั้งหมด 3 ระดับ ดังนี้</p>
<ul>
<li>ง่าย (สามารถหาคำตอบได้ด้วย bitcoin-cli command เดียว)</li>
</ul>
<ol>
<li>แฮชของบล๊อก 774698 คืออะไร?</li>
<li>signature ของข้อความจาก address นี้ถูกต้องหรือไม่</li>
</ol>
<pre><code>address: 1E9YwDtYf9R29ekNAfbV7MvB4LNv7v3fGa
message: 1E9YwDtYf9R29ekNAfbV7MvB4LNv7v3fGa
signature:HCsBcgB+Wcm8kOGMH8IpNeg0H4gjCrlqwDf/GlSXphZGBYxm0QkKEPhh9DTJRp2IDNUhVr0FhP9qCqo2W0recNM=
</code></pre>
<ul>
<li>ทำได้แหละ (สามารถหาคำตอบได้ด้วย bitcoin-cli command เดียว หรืออาจจะมากกว่าหนึ่ง)</li>
</ul>
<ol start="3">
<li>มี output ใหม่กี่อันที่เกิดในบล๊อก 774698 ?</li>
<li>ใช้ wallet descriptors หา taproot address ที่ 100 จาก extended public key ที่กำหนดให้ xpub6DLd5RvY42Q5HAmBhHPUbDGdeS9VvsYNauiuN8r6NzbiXSSNWpNVrDGTUScJ9fS2orMtuB3VdxMdUH83fPtwbrizfJg9LwWnGqtL7RTs5h1</li>
<li>สร้าง multisig address แบบ P2SH แบบ 1-of-4 จาก publickey ในอินพุตทั้งสี่ของธุรกรรมนี้:37d966a263350fe747f1c606b159987545844a493dd38d84b070027a895c4517</li>
</ol>
<ul>
<li>ต้องคิดเชิงตรรกะได้เล็กน้อย (สามารถหาคำตอบได้ด้วย bitcoin-cli command และพวก if-else/loop)</li>
</ul>
<ol start="6">
<li>tx ใดในบล็อก 257,343 ที่ใช้เอาท์พุตของ coinbase ของบล็อก 256,128?</li>
<li>มีเอาต์พุตเดียวที่ยังไม่ได้ใช้งานจากบล็อก 123,321 เอาต์พุตดังกล่าวถูกส่งไปที่ address ไหน</li>
<li>public key ใดที่ลงนามอินพุต 0 ใน tx นี้:e5969add849689854ac7f28e45628b89f7454b83e9699e551ce14b6f90c86163</li>
</ol>
<p>ใครทำได้พร้อมแชร์วิธีการใต้โพสต์นี้เดี๋ยวจะมีไดโนเสาร์ส่งแซตเป็นกำลังใจให้เล็กน้อยครับ</p>
]]></itunes:summary>
      <itunes:image href="https://image.nostr.build/29da6e18e9b5a07f5cd1b52ef33ccaad9e0d438a203316facaed75bc6b83de6c.jpg"/>
      </item>
      
      <item>
      <title><![CDATA[สรุป Mastering Bitcoin: Programming the Open Blockchain บทที่ 2
]]></title>
      <description><![CDATA[อ่านอังกฤษไม่ไหวเลยมาทางนี้แต่อ่านอันนี้ก็ปวดหัวโอ้ยยย กลับไปอ่านต้นฉบับดีกว่าาา!!!!]]></description>
             <itunes:subtitle><![CDATA[อ่านอังกฤษไม่ไหวเลยมาทางนี้แต่อ่านอันนี้ก็ปวดหัวโอ้ยยย กลับไปอ่านต้นฉบับดีกว่าาา!!!!]]></itunes:subtitle>
      <pubDate>Sat, 21 Dec 2024 13:38:09 GMT</pubDate>
      <link>https://learnbn.npub.pro/post/u8oa1nsgwwiruk-qmreu1/</link>
      <comments>https://learnbn.npub.pro/post/u8oa1nsgwwiruk-qmreu1/</comments>
      <guid isPermaLink="false">naddr1qq242wr0gyckuu682at5jun4dvk4zmtjv46nzq3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65w3mgjxl</guid>
      <category>ลองฟอร์มของไดโน</category>
      
        <media:content url="https://image.nostr.build/5648ee8b923cd74f0d7f797a52aa66cff315a0e887e7683a2f9fbd72e6a81187.jpg" medium="image"/>
        <enclosure 
          url="https://image.nostr.build/5648ee8b923cd74f0d7f797a52aa66cff315a0e887e7683a2f9fbd72e6a81187.jpg" length="0" 
          type="image/jpeg" 
        />
      <noteId>naddr1qq242wr0gyckuu682at5jun4dvk4zmtjv46nzq3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65w3mgjxl</noteId>
      <npub>npub10yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2syrhcl3</npub>
      <dc:creator><![CDATA[Learning_BTC&NOSTR]]></dc:creator>
      <content:encoded><![CDATA[<h2>บทที่ 2: ภาพรวมการทำงานของบิตคอยน์</h2>
<h1>บิตคอยน์ทำงานอย่างไร</h1>
<p>ระบบอย่างบิตคอยน์นั้นแตกต่างกับระบบธนาคารและระบบการชำระเงินแบบดั้งเดิมอย่างสิ้นเชิง เพราะมันสามารถทำงานได้โดยไม่จำเป็นต้องไว้วางใจบุคคลที่สาม แทนที่จะมีหน่วยงานกลางที่เชื่อถือได้ บิตคอยน์ได้อณุญาตให้ผู้ใช้แต่ละคนใช้ซอฟต์แวร์บนคอมพิวเตอร์ของตนเองเพื่อตรวจสอบการทำงานที่ถูกต้องของทุกส่วนในระบบ ซึ่งในบทนี้เอง เราจะทำการสำรวจบิตคอยน์ภาพรวมโดยติดตามธุรกรรมหนึ่งรายการผ่านระบบของบิตคอยน์ ดูว่าธุรกรรมนั้นถูกบันทึกลงในบล็อกเชนอย่างไร และการบันทึกธุรกรรมแบบกระจายศูนย์นั้นทำได้อย่างไร ส่วนในบทถัดไปจะลงลึกถึงเทคโนโลยีที่อยู่เบื้องหลังธุรกรรม เครือข่าย และการขุด</p>
<h2>ภาพรวมของบิตคอยน์</h2>
<p>ระบบของบิตคอยน์นั้นประกอบไปด้วย เหล่าผู้ใช้งาน wallet ต่าง ๆ , ธุรกรรมที่กระจายไปทั่วเครือข่าย และเหล่านักขุดที่จะคอยแข่งขันกันเพื่อสร้างบล๊อกใหม่ โดยที่มีบล๊อกเชนเป็นเหมือนสมุดบันทึกธุรกรรมที่รวมธุรกรรมทั้งหมดไว้ ตัวอย่างที่จะได้เห็นต่อไปนี้เป็นธุรกรรมที่เกิดขึ้นจริงบนเครือข่ายของบิตคอยน์ โดยจำลองการโต้ตอบระหว่างผู้ใช้หลายคนผ่านการส่งเงินจาก wallet หนึ่งไปยังอีก wallet และในขณะนั้นเราจะติดตามธุรกรรมผ่านเครือข่ายบิตคอยน์ ไปจนถึงบล็อกเชน เราจะใช้เว็บไซต์สำรวจบล็อกเชน (blockchain explorer) เพื่อดูภาพรวมในแต่ละขั้นตอน โดยมีเว็บไซต์สำรวจบล็อกเชนที่นิยม ดังนี้</p>
<ul>
<li>Blockstream Explorer</li>
<li>Mempool.Space</li>
<li>BlockCypher Explorer</li>
</ul>
<p>เว็บไซต์เหล่านี้มีฟังก์ชันการค้นหาที่สามารถใช้ค้นหา Bitcoin address, Transaction Hash, หมายเลขบล็อก หรือ Block hash และเรียกดูข้อมูลที่เกี่ยวข้องจากเครือข่ายบิตคอยน์ได้ สำหรับแต่ละตัวอย่างธุรกรรมหรือบล็อก เราจะให้ URL เพื่อให้คุณสามารถค้นหาและศึกษาข้อมูลเพิ่มเติมได้ด้วยตัวเอง</p>
<h3>คำเตือนเกี่ยวกับความเป็นส่วนตัวของการใช้ Block Explorer</h3>
<p>การค้นหาข้อมูลใน block explorer อาจเปิดเผยให้ผู้ให้บริการทราบว่าคุณสนใจข้อมูลนั้น ซึ่งอาจเชื่อมโยงกับที่อยู่ IP ของคุณ รายละเอียดของเบราว์เซอร์ การค้นหาที่ผ่านมา หรือข้อมูลที่สามารถระบุตัวตนได้อื่น ๆ หากคุณค้นหาธุรกรรมจากหนังสือเล่มนี้ ผู้ให้บริการอาจคาดเดาได้ว่าคุณกำลังศึกษาเกี่ยวกับบิตคอยน์ ซึ่งไม่น่าจะมีปัญหาอะไร แต่หากคุณค้นหาธุรกรรมของตนเอง ผู้ให้บริการอาจสามารถคาดเดาได้ว่าคุณได้รับ ใช้จ่าย และมีบิตคอยน์อยู่เท่าใดในปัจจุบัน</p>
<h2>การซื้อของจากร้านค้าออนไลน์</h2>
<p>อลิซเป็นผู้ใช้งานใหม่ ที่พึ่งได้รับบิตคอยน์เป็นครั้งแรกจากคำแนะนำของโจเมื่อบทที่แล้ว โดยเธอได้ซื้อบิตคอยน์จากโจเก็บไว้ และตั้งแต่นั้นมาอลิซก็ซื้อบิตคอยน์เพิ่มเรื่อย ๆ และตอนนี้อลิซต้องการทำธุรกรรมด้วยบิตคอยน์ครั้งแรกของเธอ โดยการใช้จ่ายมันเพื่อแลกกับสิทธิ์เข้าถึงพอดแคสต์ตอนพิเศษจากร้านค้าออนไลน์ของบ๊อบ  ร้านค้าออนไลน์ของบ๊อบเองก็เพิ่งมีการเพิ่มบิตคอยน์เป็นตัวเลือกในการทำธุรกรรม ราคาสินค้าในร้านของบ๊อบแสดงเป็นสกุลเงินท้องถิ่น (ดอลลาร์สหรัฐ) แต่ในหน้าชำระเงิน ลูกค้าสามารถเลือกชำระเงินเป็นดอลลาร์หรือบิตคอยน์ก็ได้</p>
<p>อลิซเลือกตอนพอดแคสต์ที่เธอต้องการซื้อและดำเนินการไปยังหน้าชำระเงิน ในหน้าชำระเงิน อลิซพบตัวเลือกในการชำระเงินด้วยบิตคอยน์ นอกเหนือจากตัวเลือกปกติ แต่ในตะกร้าชำระเงินจะแสดงราคาทั้งในรูปแบบดอลลาร์สหรัฐและบิตคอยน์ตามอัตราแลกเปลี่ยนในขณะนั้น หลังจากกดจ่ายด้วยบิตคอยน์ระบบของบ๊อบได้ทำการสร้างใบเรียกเก็บเงิน (invoice) ในรูปแบบของ QR-code ตามภาพด้านล่าง</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734787367004-YAKIHONNES3.png" alt="image"></p>
<p>ต่างจาก QR code ทั่วไปที่มีเพียง Bitcoin address ปลายทางเท่านั้น ใบแจ้งหนี้นี้เป็น QR code แบบ URI ที่ประกอบด้วย Bitcoin address จำนวนเงินชำระ และคำอธิบาย (memo) ซึ่งช่วยให้ Bitcoin wallet แอปพลิเคชันเติมข้อมูลที่ใช้ในการชำระเงินล่วงหน้าได้ และยังแสดงคำอธิบายที่อ่านเข้าใจง่ายให้กับผู้ใช้อีกด้วย คุณสามารถสแกน QR code นี้ด้วย Bitcoin wallet เพื่อดูสิ่งที่อลิซจะเห็น</p>
<h3>QR Code ของใบแจ้งหนี้นี้เข้ารหัส URI ดังต่อไปนี้ ซึ่งถูกกำหนดไว้ใน BIP21:</h3>
<p><code>bitcoin:bc1qk2g6u8p4qm2s2lh3gts5cpt2mrv5skcuu7u3e4?amount=0.01577764&amp;</code><br><code>label=Bob%27s%20Store&amp;</code><br><code>message=Purchase%20at%20Bob%27s%20Store</code></p>
<p>โดยส่วนประกอบของ URI สามารถจำแนกได้ ดังนี้</p>
<ul>
<li>A Bitcoin address: "bc1qk2g6u8p4qm2s2lh3gts5cpt2mrv5skcuu7u3e4"</li>
<li>The payment amount (จำนวนบิตคอยน์) : "0.01577764"</li>
<li>A label for the recipient address (label): "Bob's Store"</li>
<li>A description for the payment (memo): "Purchase at Bob's Store"</li>
</ul>
<p>อลิซใช้สมาร์ทโฟนของเธอสแกนบาร์โค้ดที่แสดงอยู่ หน้าจอสมาร์ทโฟนของเธอจะแสดงการชำระเงินในจำนวนที่ถูกต้องสำหรับร้านของบ๊อบ และเธอกด “Send” เพื่อยืนยันการชำระเงิน ภายในไม่กี่วินาที (เวลาประมาณเดียวกับการอนุมัติบัตรเครดิต) บ๊อบก็จะเห็นธุรกรรมดังกล่าวปรากฏบนเครื่องรับชำระเงินของเขา</p>
<blockquote>
<p>เครือข่ายบิตคอยน์สามารถทำธุรกรรมในมูลค่าเศษส่วนได้ เช่น มิลลิบิทคอยน์ (1/1,000 ของบิทคอยน์) ไปจนถึง 1/100,000,000 ของบิทคอยน์ ซึ่งเรียกว่า "ซาโตชิ" ในหนังสือเล่มนี้ใช้กฎการพหูพจน์เดียวกันกับดอลลาร์หรือสกุลเงินแบบดั้งเดิมเมื่อพูดถึงจำนวนที่มากกว่าหนึ่งบิทคอยน์ เช่น "10 บิตคอยน์" หรือ "0.001 บิตคอยน์" กฎเดียวกันนี้ยังนำไปใช้กับหน่วยบัญชีบิทคอยน์อื่น ๆ เช่น มิลลิบิทคอยน์และซาโตชิอีกด้วย</p>
</blockquote>
<h2>ธุรกรรมในระบบบิตคอยน์</h2>
<p>ธุรกรรมในระบบบิตคอยน์คือการแจ้งเครือข่ายว่าเจ้าของบิทคอยน์ได้อนุมัติการโอนมูลค่าไปยังเจ้าของใหม่แล้ว และ เจ้าของใหม่สามารถใช้บิทคอยน์เหล่านั้นได้ โดยสร้างธุรกรรมใหม่เพื่อรออนุมัติการโอนไปยังเจ้าของคนอื่นต่อ ๆ ไป ทำให้เกิดการส่งต่อความเป็นเจ้าของอย่างต่อเนื่อง</p>
<h3>ธุรกรรมขาเข้าและขาออกของบิตคอยน์</h3>
<p>ธุรกรรมเปรียบเสมือนบันทึกในสมุดบัญชีแบบสองทาง โดยธุรกรรมแต่ละรายการจะมีอินพุต (inputs) หนึ่งรายการหรือมากกว่านั้นที่ใช้จ่ายเงิน และมีเอาต์พุต (outputs) หนึ่งรายการหรือมากกว่าที่รับเงิน มูลค่าของอินพุตและเอาต์พุตไม่จำเป็นต้องเท่ากันเสมอไป เอาต์พุตมักจะมีมูลค่าน้อยกว่าอินพุตเล็กน้อย ซึ่งส่วนต่างนี้คือ "ค่าธรรมเนียมธุรกรรม" ที่นักขุดจะได้รับเมื่อรวมธุรกรรมในบล็อกเชน</p>
<p>ธุรกรรมยังมีหลักฐานการเป็นเจ้าของสำหรับจำนวนบิตคอยน์ (อินพุต) ที่ถูกใช้ในรูปของลายเซ็นดิจิทัลจากเจ้าของเดิม ซึ่งสามารถตรวจสอบความถูกต้องได้ ในระบบบิตคอยน์การใช้จ่ายบิตคอยน์คือการลงนามในธุรกรรมเพื่อโอนมูลค่าจากธุรกรรมก่อนหน้าไปยังเจ้าของใหม่ที่ระบุผ่าน Bitcoin adress</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734787627692-YAKIHONNES3.png" alt="image"></p>
<h3>ห่วงโซ่ของธุรกรรม</h3>
<p>การชำระเงินของอลิซไปยังร้านของบ็อบนั้นเป็นใช้เอาต์พุตจากธุรกรรมก่อนหน้าเป็นอินพุตในธุรกรรมครั้งนี้ (ในบทก่อนหน้า อลิซได้รับบิทคอยน์จากโจเพื่อนของเธอ ) เราเรียกธุรกรรมนี้ว่า "ธุรกรรมที่ 1 (Tx1)" ซึ่งแสดงถึงห่วงโซ่ของธุรกรรมที่เอาต์พุตของธุรกรรมหนึ่งถูกใช้เป็นอินพุตในธุรกรรมถัดไป</p>
<h4>การอ้างอิงอินพุตจากเอาต์พุตก่อนหน้า</h4>
<p>Tx1 โอน 0.001 บิทคอยน์ (100,000 ซาโตชิ) ไปยังเอาต์พุตที่ล็อกด้วยกุญแจของอลิซ และในธุรกรรมใหม่ของอลิซ (Tx2) ที่ส่งให้ร้านของบ็อบ เธออ้างถึงเอาต์พุตก่อนหน้าเป็นอินพุต อย่างที่เห็นในภาพประกอบ การอ้างอิงด้วยลูกศรและระบุอินพุตว่า "Tx1:0" ในธุรกรรมจริง การอ้างอิงจะใช้รหัสประจำธุรกรรม (txid) เป็นตัวระบุขนาด 32 ไบต์ที่แสดงถึงธุรกรรมที่อลิซได้รับเงินจากโจ ส่วน ":0" หมายถึงตำแหน่งของเอาต์พุตที่อลิซได้รับเงิน ซึ่งในกรณีนี้คือตำแหน่งแรก (ตำแหน่ง 0)</p>
<h4>การคำนวณมูลค่าอินพุต</h4>
<p>เนื่องจากธุรกรรมของบิตคอยน์นั้นไม่ได้ระบุค่าของอินพุตอย่างชัดเจน ตัวซอฟต์แวร์เลยจะต้องใช้การอ้างอิงของอินพุตเพื่อค้นหาเอาต์พุตของธุรกรรมก่อนหน้าที่ถูกใช้ไป</p>
<h4>เอาต์พุตใน Tx2 ของอลิซ</h4>
<p>Tx2 ของอลิซมีเอาต์พุตใหม่สองรายการ รายการหนึ่งจ่าย 75,000 ซาโตชิสำหรับพอดแคสต์ และอีกรายการจ่าย 20,000 ซาโตชิคืนให้อลิซเป็นเงินทอน<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734787700413-YAKIHONNES3.png" alt="image"></p>
<blockquote>
<h4>เกร็ดสาระเล็ก ๆ น้อย ๆ</h4>
<ul>
<li>ธุรกรรมของบิตคอยน์นั้นอยู่ในรูปแบบซีเรียลไลซ์ (serialized) เป็นรูปแบบข้อมูลที่ซอฟต์แวร์ใช้สำหรับการส่งธุรกรรม โดยจะเข้ารหัสมูลค่าที่ต้องการโอนด้วยตัวเลขจำนวนเต็มซึ่งเป็นหน่วยมูลค่าที่เล็กที่สุดในระบบ on-chain</li>
<li>ที่มาของชื่อ "ซาโตชิ" :เมื่อบิตคอยน์ถูกสร้างขึ้นในครั้งแรก หน่วยมูลค่านี้ยังไม่มีชื่อเรียก นักพัฒนาบางคนจึงเรียกมันว่า "หน่วยฐาน" (base unit) แต่ต่อมาผู้ใช้งานหลาย ๆ คนเริ่มเรียกหน่วยนี้ว่า "ซาโตชิ" (satoshi หรือ sat) เพื่อเป็นเกียรติแก่ผู้สร้างบิตคอยน์</li>
</ul>
</blockquote>
<h4>การทอนเงิน</h4>
<p>นอกเหนือจากการสร้างเอาต์พุตเพื่อจ่ายให้กับผู้รับบิตคอยน์แล้ว ธุรกรรมจำนวนมากยังมีเอาต์พุตที่จ่ายเงินคืนให้กับผู้จ่าย ซึ่งเรียกว่า เอาต์พุตทอนเงิน (change output) เนื่องจากอินพุตของธุรกรรม (คล้ายกับธนบัตร) ไม่สามารถแบ่งใช้บางส่วนได้ เช่น ถ้าคุณซื้อของราคา 5 ดอลลาร์และจ่ายด้วยธนบัตร 20 ดอลลาร์ คุณคาดหวังว่าจะได้เงินทอน 15 ดอลลาร์ ในทำนองเดียวกัน ในธุรกรรม Bitcoin หากคุณซื้อสินค้าราคา 5 บิตคอยน์แต่มีอินพุตมูลค่า 20 บิตคอยน์ คุณจะสร้างเอาต์พุต 5 บิตคอยน์ไปยังเจ้าของร้าน และอีกเอาต์พุต 15 บิตคอยน์คืนให้ตัวคุณเองเป็นเงินทอน (ไม่นับรวมค่าธรรมเนียมธุรกรรม) </p>
<blockquote>
<ul>
<li>หากมองผ่านมุมของโปรโตคอลบิตคอยน์นั้นไม่ได้มีความแตกต่างใด ๆ ระหว่างเอาต์พุตเงินทอนกับเอาต์พุตการจ่ายเงินปกติ</li>
<li>โดยทั่วไปแล้วเอาต์พุตเงินทอนจะเป็นการโอนไปจ่ายให้กับ Bitcoin Address อันใหม่ในกระเป๋าตัวเองดังรูปที่แสดงข้างล่าง</li>
</ul>
</blockquote>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734787818103-YAKIHONNES3.png" alt="image"></p>
<h3>การสร้างธุรกรรม</h3>
<p>แอปพิเคชั่นกระเป๋าเงินของอลิซจะทำการสร้างอินพุตและสร้างเอาต์พุตตามที่ Alice ต้องการ โดยที่เธอเพียงแค่กรอกปลายทาง จำนวนเงิน และค่าธรรมเนียมธุรกรรมกระเป๋าเงินจะทำงานที่เหลือให้โดยอัตโนมัติ นอกจากนี้ กระเป๋าเงินยังสามารถสร้างธุรกรรมแบบออฟไลน์ได้ คล้ายกับการเขียนเช็คที่บ้านแล้วค่อยนำไปฝากธนาคารในภายหลังอีกด้วย</p>
<h4>การเลือกอินพุตที่เหมาะสม</h4>
<p>กระเป๋าเงินจะเลือกอินพุตที่มีมูลค่าเพียงพอสำหรับการชำระเงินไปยังบ๊อบโดยตรวจสอบเอาต์พุตที่ยังไม่ได้ใช้ (UTXOs) ซึ่งหากมูลค่าไม่เพียงพอ กระเป๋าเงินก็จะทำการรวม UTXOs หลาย ๆ รายการเข้าด้วยกัน เพื่อให้ได้ยอดที่ต้องการ และหากอินพุตมีมูลค่าสูงกว่าค่าที่ต้องจ่าย กระเป๋าเงินจะสร้างเอาต์พุตสำหรับทอนเงินกลับมาให้อลิซ</p>
<h4>การสร้างเอาต์พุต</h4>
<p>เอาต์พุตประกอบด้วยสคริปต์ที่กำหนดให้ผู้ที่มีคีย์ของบ๊อบเท่านั้นสามารถลงนามเพื่อที่จะใช้เงินได้ นอกจากนี้ยังมีเอาต์พุตสำหรับทอนเงินกลับมาให้อลิซ ซึ่งทำให้เธอสามารถใช้เงินทอนนั้นในการทำธุรกรรมถัดไป และค่าธรรมเนียมธุรกรรมจะถูกคำนวณจากส่วนต่างระหว่างอินพุตและเอาต์พุต และนั่นเองที่จะเป็นรางวัลสำหรับนักขุดที่บันทึกธุรกรรมลงบล็อกเชนของบิตคอยน์</p>
<h2>การใส่ธุรกรรมลงในบล๊อกเชน</h2>
<p>ธุรกรรมที่สร้างขึ้นโดย Bitcoin wallet ของอลิซมีข้อมูลทั้งหมดที่จำเป็นสำหรับการสร้างธุรกรรม (การยืนยันว่าอลิซเป็นเจ้าของเงิน และ Bitcoin address ปลายทาง) จากนั้นธุรกรรมนี้จะต้องถูกส่งไปยังเครือข่ายของบิตคอยน์ เพื่อที่จะให้ธุรกรรมนั้นเป็นส่วนนึงในเครือข่ายของบิตคอยน์ และในส่วนถัดไปของหนังสือเล่มนี้ เราจะอธิบายถึงว่าธุรกรรมกลายเป็นส่วนหนึ่งของบล็อกใหม่อย่างไร และกระบวนการขุดบล็อกเป็นอย่างไร รวมถึงการที่บล็อกใหม่ได้รับความไว้วางใจมากขึ้นเมื่อมีการเพิ่มบล็อกใหม่ ๆ เข้ามาเรื่อย ๆ หมายถึงอะไร ?</p>
<h3>การส่งธุรกรรมเข้าไปยังเครือข่าย</h3>
<p>เนื่องจากธุรกรรมมีข้อมูลที่จำเป็นสำหรับการประมวลผลทั้งหมด จึงทำให้การส่งผ่านสามารถทำได้จากที่ไหนหรืออย่างไรก็ได้ อย่างที่ได้กล่าวไปว่าเครือข่ายของบิตคอยน์เป็นเครือข่ายแบบ peer-to-peer โดยที่แต่ละโหนดเชื่อมต่อกับโหนดอื่น ๆ อีกหลายโหนด เพื่อทำหน้าที่กระจายธุรกรรมและบล็อกให้กับผู้เข้าร่วมทั้งหมดในระบบ</p>
<h3>การกระจายธุรกรรม</h3>
<p>โหนดในเครือข่าย peer-to-peer ของบิตคอยน์นั้นเป็นซอฟต์แวร์ที่สามารถตรวจสอบความถูกต้องของธุรกรรมได้  และการเชื่อมต่อระหว่างโหนดสามารถแสดงเป็นเส้นในกราฟ ทำให้โหนดเหล่านี้เรียกว่า “โหนดตรวจสอบเต็มรูปแบบ” (full nodes) กระเป๋าเงินของอลิซสามารถส่งธุรกรรมไปยังโหนดบิตคอยน์ใด ๆ ผ่านการเชื่อมต่อใด ๆ ก็ได้ เช่น สายแลน WiFi หรือ เครือข่ายมือถือ โดยถ้าหากโหนดได้รับธุรกรรมที่ถูกต้องซึ่งยังไม่เคยเห็นมาก่อน มันจะกระจายธุรกรรมนี้ไปยังโหนดอื่น ๆ ที่เชื่อมต่อด้วย ซึ่งเป็นเทคนิคที่เรียกว่า gossiping ซึ่งทำให้ธุรกรรมแพร่กระจายไปทั่วเครือข่ายอย่างรวดเร็วภายในไม่กี่วินาที</p>
<h3>การขุดบิตคอยน์</h3>
<p>ตอนนี้ธุรกรรมของอลิซได้เข้าไปสู่ในเครือข่ายของบิตคอยน์แล้ว แต่มันยังไม่ได้ถูกบรรจุลงในบล๊อกเชนเนื่องจากจะต้องรอให้นักขุดทำการนำธุรกรรมนั้น ๆ เข้าไปในบล๊อกและบล๊อกนั้นจำเป็นต้องผ่านการตรวจสอบโดยโหนดในเครือข่ายของบิตคอยน์เสียก่อน จึงจะถูกบันทึกลงในบล๊อกเชน<br>ในระบบของบิตคอยน์นั้น มีการป้องกันการปลอมแปลงด้วยการคำนวณทางคณิตศาสตร์ ซึ่งเป็นการคำนวณที่จำเป็นต้องใช้พลังงานมหาศาลในการคำนวณ แต่ใช้พลังงานเพียงเล็กน้อยในการตรวจสอบ โดยธุรกรรมทั้งหมดจะถูกจัดเรียงเป็นบล๊อกและแต่ละบล๊อกจะมีบล๊อกเฮดเดอร์ที่จำเป็นต้องสร้างตามเงื่อนไขเฉพาะ โดยกระบวนการขุดบิตคอยน์นั้นมีวัตถุประสงค์อยู่สองอย่าง ดังนี้:</p>
<ul>
<li>สร้างแรงจูงใจให้ขุดเฉพาะธุรกรรมที่ถูกต้องตามกฎ: เนื่องจากวิธีที่เหล่านักขุดจะได้รับผลกำไรที่สูงที่สุดจากการสร้างบล๊อกที่ตรงกับฉันทมติของระบบเท่านั้น (หากไม่ทำตามบล๊อกจะไม่ถูกยอมรับโดยโหนด และนั่นจะเป็นการสิ้นเปลืองพลังงานที่ได้คำนวณมาโดยเปล่าประโยชน์) นั้นจึงเป็นแรงจูงใจหลัก ๆ ให้เหล่านักขุดทำการใส่ธุรกรรมที่ถูกต้องตามกฏเท่านั้นลงในบล๊อกที่ตนสร้าง และสิ่งนี้เองก็ทำให้ผู้ใช้สามารถเลือกที่จะสันนิษฐานโดยอิงตามความไว้วางใจว่าธุรกรรมใด ๆ ในบล็อกนั้น ๆ เป็นธุรกรรมที่ถูกต้อง </li>
<li>สร้างเหรียญใหม่ตามตารางการออกเหรียญที่กำหนดไว้ล่วงหน้า: ในปัจจุบันนั้นจะมีการสร้างบิตคอยน์ใหม่ในแต่ละบล็อก คล้ายคลึงกับธนาคารกลางที่พิมพ์เงินใหม่ โดยจำนวนบิตคอยน์ในแต่ละบล๊อกที่จะถูกผลิตขึ้นมาใหม่นั้นถูกกำหนดมาตั้งแต่วันที่ระบบของบิตคอยน์ได้เริ่มขึ้นและไม่สามารถเปลี่ยนแปลงได้</li>
</ul>
<p>การขุดได้ช่วยให้เกิดความสมดุลระหว่างต้นทุนและผลตอบแทน เนื่องจากการขุดมีการใช้ไฟฟ้าเพื่อแก้ปัญหาการคำนวณ และนักขุดที่ประสบความสำเร็จจะได้รับรางวัลในรูปแบบของบิตคอยน์ใหม่และค่าธรรมเนียมจากการทำธุรกรรม แต่อย่างไรก็ตาม รางวัลจะถูกเก็บรวบรวมก็ต่อเมื่อนักขุดรวมเฉพาะธุรกรรมที่ถูกต้องเท่านั้น โดยกฎของโปรโตคอลบิตคอยน์สำหรับการสร้างฉันทามติ จะกำหนดว่าอะไรถูกต้อง โดยความสมดุลที่ละเอียดอ่อนนี้เองที่คอยสร้างให้ความปลอดภัยแก่บิตคอยน์โดยไม่ต้องมีหน่วยงานกลางมาคอยดูแล</p>
<p>การขุดถูกออกแบบให้เหมือนกับการจับสลากแบบกระจายศูนย์ นักขุดแต่ละคนสามารถสร้าง "สลาก" ของตัวเองได้โดยการสร้างบล็อกตัวอย่างที่ประกอบไปด้วยธุรกรรมใหม่ที่ต้องการขุด พร้อมกับข้อมูลอื่น ๆ และนักขุดจะป้อนบล็อกตัวอย่างนี้เข้าไปในอัลกอริทึมที่ออกแบบมาเป็นพิเศษเพื่อแฮชข้อมูล ทำให้ได้ค่าผลลัพธ์ที่แตกต่างจากข้อมูลเดิมอย่างสิ้นเชิง โดยแฮชฟังก์ชันนี้จะให้ผลลัพธ์เดียวกันเสมอสำหรับข้อมูลชุดเดิม แต่ไม่สามารถคาดเดาผลลัพธ์ได้หากป้อนข้อมูลใหม่ แม้จะแตกต่างเพียงเล็กน้อยจากข้อมูลก่อนหน้า</p>
<p>หากค่าผลลัพธ์ของแฮชตรงกับเงื่อนไขที่กำหนดของโปรโตคอล นักขุดจะชนะการจับสลาก และผู้ใช้งานบิตคอยน์ จะยอมรับบล็อกนี้พร้อมกับธุรกรรมในนั้นว่าเป็นบล็อกที่ถูกต้อง หากไม่ตรงกับเงื่อนไข นักขุดจะปรับข้อมูลในบล็อกเล็กน้อยและลองทำการแฮชใหม่ กระบวนการนี้ต้องทำซ้ำหลายครั้ง โดย ณ ขณะที่เขียนนี้ นักขุดต้องลองสร้างบล็อกตัวอย่างประมาณ 168 พันล้านล้านครั้ง เพื่อหาคำตอบที่ถูกต้อง ซึ่งหมายถึงการรันแฮชฟังก์ชันในจำนวนครั้งมหาศาลมาก ๆ </p>
<p>แต่เมื่อพบคำตอบที่ถูกต้องแล้ว ใครก็ตามสามารถตรวจสอบว่าบล็อกนั้นถูกต้องได้โดยการรันแฮชฟังก์ชันเพียงครั้งเดียว ซึ่งทำให้การสร้างบล็อกที่ถูกต้องต้องใช้พลังงานคำนวณมหาศาล แต่การตรวจสอบทำได้ง่ายมาก กระบวนการตรวจสอบนี้สามารถพิสูจน์ได้อย่างมีหลักการว่ามีการทำงานเกิดขึ้นจริง ดังนั้น ข้อมูลที่ใช้สร้างหลักฐานนี้—ในที่นี้คือบล็อก—เรียกว่า "หลักฐานการทำงาน" หรือ Proof of Work (PoW)</p>
<p>ธุรกรรมจะถูกเพิ่มลงในบล็อกใหม่ โดยให้ความสำคัญกับธุรกรรมที่มีค่าธรรมเนียมสูงสุดก่อนและพิจารณาจากปัจจัยอื่น ๆ อีกเล็กน้อย นักขุดแต่ละคนจะเริ่มกระบวนการสร้างบล็อกตัวอย่างใหม่ทันทีหลังจากได้รับบล็อกก่อนหน้าจากเครือข่าย โดยรู้ว่ามีคนอื่นชนะรางวัลไปแล้วในรอบนั้น พวกเขาจะสร้างบล็อกตัวอย่างใหม่ที่เชื่อมโยงกับบล็อกก่อนหน้า ใส่ธุรกรรมเข้าไป และเริ่มคำนวณ Proof of Work (PoW) สำหรับบล็อกตัวอย่างนี้ นักขุดจะเพิ่มธุรกรรมพิเศษที่จ่ายรางวัลบล็อกและค่าธรรมเนียมธุรกรรมรวมเข้ากับที่อยู่บิตคอยน์ของตนเอง หากพวกเขาพบบล็อกที่ถูกต้องและถูกเพิ่มในบล็อกเชน นักขุดจะได้รับรางวัลนั้น และธุรกรรมรางวัลนี้ก็จะใช้งานได้ นักขุดที่ทำงานร่วมกับพูลจะตั้งค่าให้รางวัลถูกส่งไปยังที่อยู่ของพูล จากนั้นจะแบ่งรางวัลให้สมาชิกตามสัดส่วนการทำงานที่แต่ละคนมีส่วนร่วม</p>
<p>กลับมาที่ธุรกรรมของอลิซ ตอนนี้ธุรกรรมของอลิซได้ถูกเครือข่ายรับไปแล้วและเพิ่มลงในพูลของธุรกรรมที่ยังไม่ได้รับการยืนยันเรียบร้อย จากนั้นเมื่อธุรกรรมนั้นผ่านการตรวจสอบจาก full node แล้ว มันจะถูกรวมไว้ในบล็อกตัวอย่าง และประมาณห้านาทีหลังจากที่อลิซส่งธุรกรรมจากกระเป๋าเงินของเธอ นักขุดคนหนึ่งพบคำตอบสำหรับบล็อกนั้นและประกาศไปยังเครือข่าย หลังจากที่นักขุดคนอื่น ๆ ตรวจสอบความถูกต้องของบล็อกที่ชนะ พวกเขาก็เริ่มกระบวนการสุ่มอีกครั้งเพื่อสร้างบล็อกถัดไป</p>
<p>บล็อกที่ชนะซึ่งมีธุรกรรมของอลิซอยู่ในนั้น ได้กลายเป็นส่วนหนึ่งของบล็อกเชน และบล็อกนี้ถูกนับเป็นการยืนยันหนึ่งครั้งสำหรับธุรกรรมนั้น หลังจากที่บล็อกที่มีธุรกรรมของอลิซได้ถูกเผยแพร่ไปทั่วเครือข่าย การสร้างบล็อกทางเลือกที่มีเวอร์ชันอื่นของธุรกรรมของอลิซ (เช่น ธุรกรรมที่ไม่ได้จ่ายให้ บ๊อบ) จะต้องใช้ปริมาณงานเท่ากับที่นักขุดทั้งหมดต้องใช้ในการสร้างบล็อกใหม่ทั้งบล็อก เมื่อมีบล็อกทางเลือกหลายบล็อกให้เลือก full node ในเครือข่ายของบิตคอยน์ก็จะทำการเลือกบล็อกเชนที่ถูกต้อง โดยจะเป็นเชนซึ่งมี Proof of Work (PoW) รวมมากที่สุด ซึ่งเรียกว่าบล็อกเชนที่ดีที่สุด หากเครือข่ายทั้งหมดจะยอมรับบล็อกทางเลือก จะต้องมีการขุดบล็อกใหม่เพิ่มเติมอีกหนึ่งบล็อกต่อจากบล็อกทางเลือกนั้น</p>
<p>นั่นหมายความว่านักขุดมีตัวเลือกอื่น อย่างเช่นการที่พวกเขาสามารถร่วมมือกับอลิซเพื่อสร้างธุรกรรมทางเลือกที่เธอไม่ได้จ่ายเงินให้บ๊อบ โดยอลิซอาจเสนอส่วนแบ่งจากเงินที่เธอเคยจ่ายให้บ๊อบแก่นักขุด แต่การกระทำที่ไม่ซื่อสัตย์นี้จะต้องใช้ความพยายามเท่ากับการสร้างบล็อกใหม่ถึงสองบล็อก ซึ่งในทางกลับกันแล้ว นักขุดที่ทำงานอย่างซื่อสัตย์สามารถสร้างบล็อกใหม่เพียงบล็อกเดียวและได้รับค่าธรรมเนียมจากธุรกรรมทั้งหมดที่รวมอยู่ในบล็อก พร้อมกับรางวัลบล็อก (block subsidy) นอกจากนี้ต้นทุนที่สูงในการสร้างบล็อกสองบล็อกเพื่อพยายามเปลี่ยนแปลงธุรกรรมที่ยืนยันแล้วสำหรับผลตอบแทนเพียงเล็กน้อยนั้นไม่คุ้มค่าและการกระทำดังกล่าวมีโอกาสน้อยที่จะเกิดขึ้น สำหรับ บ๊อบ นั่นหมายความว่าเขาสามารถเริ่มเชื่อถือได้ว่าการชำระเงินจากอลิซนั้นเป็นสิ่งที่เชื่อถือได้</p>
<p>ประมาณ 19 นาทีหลังจากบล็อกที่มีธุรกรรมของอลิซ ได้ถูกเผยแพร่บล็อกใหม่ถูกขุดขึ้นโดยนักขุดอีกคน และเนื่องจากบล็อกใหม่นี้ถูกสร้างต่อจากบล็อกที่มีธุรกรรมของอลิซ (ทำให้ธุรกรรมของอลิซได้รับการยืนยันสองครั้ง) ธุรกรรมของ อลิซจะสามารถเปลี่ยนแปลงได้ก็ต่อเมื่อมีการขุดบล็อกทางเลือกสองบล็อกขึ้นมา และมีบล็อกใหม่ที่สร้างต่อจากบล็อกเหล่านั้น รวมเป็นสามบล็อกที่ต้องถูกขุดเพื่อให้อลิซสามารถดึงเงินที่เธอส่งให้บ๊อบกลับมาได้ และทุกบล็อกที่ขุดต่อจากบล็อกที่มีธุรกรรมของอลิซนั้นจะนับเป็นการยืนยันเพิ่มเติม เมื่อจำนวนบล็อกที่ต่อกันเพิ่มมากขึ้น การย้อนกลับธุรกรรมก็จะยากขึ้นเรื่อย ๆ ทำให้บ๊อบมั่นใจมากขึ้นเรื่อย ๆ ว่าการชำระเงินของอลิซนั้นจะปลอดภัย</p>
<p>จากภาพที่แนบไว้ข้างล่างนี้ เราสามารถเห็นบล็อกที่มีธุรกรรมของอลิซและด้านล่างของบล็อกนี้มีบล็อกอีกหลายแสนบล็อกที่เชื่อมต่อกันเป็นโซ่ (blockchain) ต่อเนื่องไปจนถึงบล็อกหมายเลข <a href='/tag/0/'>#0</a> หรือที่เรียกว่า genesis block  และเมื่อเวลาผ่านไป "ความสูง" ของบล็อกใหม่ที่เพิ่มขึ้นจะทำให้ความยากในการคำนวณของทั้งเครือข่ายเพิ่มขึ้นตามไปด้วย ตามธรรมเนียมแล้ว บล็อกใด ๆ ที่มีการยืนยันมากกว่าหกครั้งจะถือว่ายากมากที่จะเปลี่ยนแปลง เพราะต้องใช้การคำนวณอย่างมหาศาลในการคำนวณบล็อกหกบล็อกใหม่ (รวมถึงบล็อกใหม่อีกหนึ่งบล็อก)</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734788200516-YAKIHONNES3.png" alt="image"></p>
<h4>การใช้จ่ายในธุรกรรม</h4>
<p>เมื่อธุรกรรมของอลิซได้กลายเป็นส่วนหนึ่งของบล็อกเชน แปลว่ามันสามารถถูกเรียกดูได้จากทุกบิตคอยน์แอปพลิเคชัน และทุกโหนดสามารถที่จะตรวจสอบธุรกรรมนี้ได้อย่างอิสระ ว่าธุรกรรมนี้ถูกต้องหรือไม่ โดยจะตรวจสอบย้อนไปตั้งแต่ตอนที่เหรียญนั้น ๆ ถูกสร้างและตรวจสอบต่อมาเรื่อย ๆ จนถึงธุรกรรมปัจจุบัน ไคลเอนต์จะสามารถตรวจสอบการชำระเงินได้บางส่วน โดยการยืนยันว่าธุรกรรมนั้นอยู่ในบล็อกเชนแล้ว และมีบล็อกจำนวนมากที่ถูกขุดหลังจากนั้น ซึ่งนี่ช่วยให้มั่นใจได้ว่ามีการใช้ความพยายามอย่างมากในการยืนยันธุรกรรมนี้โดยเหล่านักขุดไปแล้ว </p>
<p>ในตอนนี้บ็อบสามารถใช้บิตคอยน์ที่อลิซส่งให้ต่อได้แล้ว! สมมุติว่าบ๊อบต้องการจ่ายค่าจ้างให้ผู้รับเหมาหรือผู้จัดหาสินค้า โดยการโอนมูลค่าจากการชำระเงินของอลิซสำหรับพอดแคสต์ไปยังเจ้าของรายใหม่ เมื่อบ๊อบใช้เงินที่ได้รับจากอลิซและลูกค้าคนอื่น ๆ เขาก็ขยายสายโซ่ของธุรกรรมออกไป สมมติว่าบ๊อบจ่ายค่าจ้างให้กรูฟซึ่งเป็นนักออกแบบเว็บไซต์ของเขาสำหรับสร้างหน้าเว็บใหม่ สายโซ่ของธุรกรรมจะมีลักษณะดังนี้:</p>
<ul>
<li>ธุรกรรมจากโจถึงอลิซ: โจโอนบิตคอยน์ให้อลิซเพื่อการซื้อขายหรือจ่ายค่าบริการบางอย่าง</li>
<li>ธุรกรรมจากอลิซถึงบ๊อบ: อลิซโอนบิตคอยน์ให้บ๊อบเพื่อชำระค่าพอดแคสต์</li>
<li>ธุรกรรมจากบ๊อบถึงกรูฟ: บ๊อบโอนบิตคอยน์จากที่ได้รับจากอลิซให้กรูฟเพื่อจ่ายค่าจ้างออกแบบเว็บไซต์</li>
</ul>
<p>สายโซ่ของธุรกรรมนี้แสดงให้เห็นการเชื่อมต่อของธุรกรรมจากโจถึงอลิซและจากอลิซถึงบ๊อบต่อด้วยจากบ๊อบถึง กรูฟ โดยแต่ละธุรกรรมได้รับการบันทึกไว้ในบล็อกเชน ซึ่งช่วยให้ทุกคนสามารถตรวจสอบความถูกต้องของธุรกรรมในสายโซ่นี้ได้</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734788274465-YAKIHONNES3.png" alt="image"></p>
<p>ในบทนี้ เราได้เห็นวิธีที่ธุรกรรมสร้างสายโซ่ที่ถ่ายโอนมูลค่าจากเจ้าของหนึ่งไปยังอีกเจ้าของหนึ่ง นอกจากนี้เรายังได้ติดตามธุรกรรมของอลิซตั้งแต่เริ่มสร้างในกระเป๋าเงินของเธอ ผ่านเครือข่ายบิตคอยน์ไปจนถึงนักขุดที่บันทึกมันลงในบล็อกเชน และสำหรับในส่วนที่เหลือของหนังสือเล่มนี้ เราจะศึกษารายละเอียดเกี่ยวกับเทคโนโลยีที่เกี่ยวข้อง ไม่ว่าจะเป็นกระเป๋าเงิน, address, digital signature, network และกระบวนการขุดอย่างละเอียด</p>
]]></content:encoded>
      <itunes:author><![CDATA[Learning_BTC&NOSTR]]></itunes:author>
      <itunes:summary><![CDATA[<h2>บทที่ 2: ภาพรวมการทำงานของบิตคอยน์</h2>
<h1>บิตคอยน์ทำงานอย่างไร</h1>
<p>ระบบอย่างบิตคอยน์นั้นแตกต่างกับระบบธนาคารและระบบการชำระเงินแบบดั้งเดิมอย่างสิ้นเชิง เพราะมันสามารถทำงานได้โดยไม่จำเป็นต้องไว้วางใจบุคคลที่สาม แทนที่จะมีหน่วยงานกลางที่เชื่อถือได้ บิตคอยน์ได้อณุญาตให้ผู้ใช้แต่ละคนใช้ซอฟต์แวร์บนคอมพิวเตอร์ของตนเองเพื่อตรวจสอบการทำงานที่ถูกต้องของทุกส่วนในระบบ ซึ่งในบทนี้เอง เราจะทำการสำรวจบิตคอยน์ภาพรวมโดยติดตามธุรกรรมหนึ่งรายการผ่านระบบของบิตคอยน์ ดูว่าธุรกรรมนั้นถูกบันทึกลงในบล็อกเชนอย่างไร และการบันทึกธุรกรรมแบบกระจายศูนย์นั้นทำได้อย่างไร ส่วนในบทถัดไปจะลงลึกถึงเทคโนโลยีที่อยู่เบื้องหลังธุรกรรม เครือข่าย และการขุด</p>
<h2>ภาพรวมของบิตคอยน์</h2>
<p>ระบบของบิตคอยน์นั้นประกอบไปด้วย เหล่าผู้ใช้งาน wallet ต่าง ๆ , ธุรกรรมที่กระจายไปทั่วเครือข่าย และเหล่านักขุดที่จะคอยแข่งขันกันเพื่อสร้างบล๊อกใหม่ โดยที่มีบล๊อกเชนเป็นเหมือนสมุดบันทึกธุรกรรมที่รวมธุรกรรมทั้งหมดไว้ ตัวอย่างที่จะได้เห็นต่อไปนี้เป็นธุรกรรมที่เกิดขึ้นจริงบนเครือข่ายของบิตคอยน์ โดยจำลองการโต้ตอบระหว่างผู้ใช้หลายคนผ่านการส่งเงินจาก wallet หนึ่งไปยังอีก wallet และในขณะนั้นเราจะติดตามธุรกรรมผ่านเครือข่ายบิตคอยน์ ไปจนถึงบล็อกเชน เราจะใช้เว็บไซต์สำรวจบล็อกเชน (blockchain explorer) เพื่อดูภาพรวมในแต่ละขั้นตอน โดยมีเว็บไซต์สำรวจบล็อกเชนที่นิยม ดังนี้</p>
<ul>
<li>Blockstream Explorer</li>
<li>Mempool.Space</li>
<li>BlockCypher Explorer</li>
</ul>
<p>เว็บไซต์เหล่านี้มีฟังก์ชันการค้นหาที่สามารถใช้ค้นหา Bitcoin address, Transaction Hash, หมายเลขบล็อก หรือ Block hash และเรียกดูข้อมูลที่เกี่ยวข้องจากเครือข่ายบิตคอยน์ได้ สำหรับแต่ละตัวอย่างธุรกรรมหรือบล็อก เราจะให้ URL เพื่อให้คุณสามารถค้นหาและศึกษาข้อมูลเพิ่มเติมได้ด้วยตัวเอง</p>
<h3>คำเตือนเกี่ยวกับความเป็นส่วนตัวของการใช้ Block Explorer</h3>
<p>การค้นหาข้อมูลใน block explorer อาจเปิดเผยให้ผู้ให้บริการทราบว่าคุณสนใจข้อมูลนั้น ซึ่งอาจเชื่อมโยงกับที่อยู่ IP ของคุณ รายละเอียดของเบราว์เซอร์ การค้นหาที่ผ่านมา หรือข้อมูลที่สามารถระบุตัวตนได้อื่น ๆ หากคุณค้นหาธุรกรรมจากหนังสือเล่มนี้ ผู้ให้บริการอาจคาดเดาได้ว่าคุณกำลังศึกษาเกี่ยวกับบิตคอยน์ ซึ่งไม่น่าจะมีปัญหาอะไร แต่หากคุณค้นหาธุรกรรมของตนเอง ผู้ให้บริการอาจสามารถคาดเดาได้ว่าคุณได้รับ ใช้จ่าย และมีบิตคอยน์อยู่เท่าใดในปัจจุบัน</p>
<h2>การซื้อของจากร้านค้าออนไลน์</h2>
<p>อลิซเป็นผู้ใช้งานใหม่ ที่พึ่งได้รับบิตคอยน์เป็นครั้งแรกจากคำแนะนำของโจเมื่อบทที่แล้ว โดยเธอได้ซื้อบิตคอยน์จากโจเก็บไว้ และตั้งแต่นั้นมาอลิซก็ซื้อบิตคอยน์เพิ่มเรื่อย ๆ และตอนนี้อลิซต้องการทำธุรกรรมด้วยบิตคอยน์ครั้งแรกของเธอ โดยการใช้จ่ายมันเพื่อแลกกับสิทธิ์เข้าถึงพอดแคสต์ตอนพิเศษจากร้านค้าออนไลน์ของบ๊อบ  ร้านค้าออนไลน์ของบ๊อบเองก็เพิ่งมีการเพิ่มบิตคอยน์เป็นตัวเลือกในการทำธุรกรรม ราคาสินค้าในร้านของบ๊อบแสดงเป็นสกุลเงินท้องถิ่น (ดอลลาร์สหรัฐ) แต่ในหน้าชำระเงิน ลูกค้าสามารถเลือกชำระเงินเป็นดอลลาร์หรือบิตคอยน์ก็ได้</p>
<p>อลิซเลือกตอนพอดแคสต์ที่เธอต้องการซื้อและดำเนินการไปยังหน้าชำระเงิน ในหน้าชำระเงิน อลิซพบตัวเลือกในการชำระเงินด้วยบิตคอยน์ นอกเหนือจากตัวเลือกปกติ แต่ในตะกร้าชำระเงินจะแสดงราคาทั้งในรูปแบบดอลลาร์สหรัฐและบิตคอยน์ตามอัตราแลกเปลี่ยนในขณะนั้น หลังจากกดจ่ายด้วยบิตคอยน์ระบบของบ๊อบได้ทำการสร้างใบเรียกเก็บเงิน (invoice) ในรูปแบบของ QR-code ตามภาพด้านล่าง</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734787367004-YAKIHONNES3.png" alt="image"></p>
<p>ต่างจาก QR code ทั่วไปที่มีเพียง Bitcoin address ปลายทางเท่านั้น ใบแจ้งหนี้นี้เป็น QR code แบบ URI ที่ประกอบด้วย Bitcoin address จำนวนเงินชำระ และคำอธิบาย (memo) ซึ่งช่วยให้ Bitcoin wallet แอปพลิเคชันเติมข้อมูลที่ใช้ในการชำระเงินล่วงหน้าได้ และยังแสดงคำอธิบายที่อ่านเข้าใจง่ายให้กับผู้ใช้อีกด้วย คุณสามารถสแกน QR code นี้ด้วย Bitcoin wallet เพื่อดูสิ่งที่อลิซจะเห็น</p>
<h3>QR Code ของใบแจ้งหนี้นี้เข้ารหัส URI ดังต่อไปนี้ ซึ่งถูกกำหนดไว้ใน BIP21:</h3>
<p><code>bitcoin:bc1qk2g6u8p4qm2s2lh3gts5cpt2mrv5skcuu7u3e4?amount=0.01577764&amp;</code><br><code>label=Bob%27s%20Store&amp;</code><br><code>message=Purchase%20at%20Bob%27s%20Store</code></p>
<p>โดยส่วนประกอบของ URI สามารถจำแนกได้ ดังนี้</p>
<ul>
<li>A Bitcoin address: "bc1qk2g6u8p4qm2s2lh3gts5cpt2mrv5skcuu7u3e4"</li>
<li>The payment amount (จำนวนบิตคอยน์) : "0.01577764"</li>
<li>A label for the recipient address (label): "Bob's Store"</li>
<li>A description for the payment (memo): "Purchase at Bob's Store"</li>
</ul>
<p>อลิซใช้สมาร์ทโฟนของเธอสแกนบาร์โค้ดที่แสดงอยู่ หน้าจอสมาร์ทโฟนของเธอจะแสดงการชำระเงินในจำนวนที่ถูกต้องสำหรับร้านของบ๊อบ และเธอกด “Send” เพื่อยืนยันการชำระเงิน ภายในไม่กี่วินาที (เวลาประมาณเดียวกับการอนุมัติบัตรเครดิต) บ๊อบก็จะเห็นธุรกรรมดังกล่าวปรากฏบนเครื่องรับชำระเงินของเขา</p>
<blockquote>
<p>เครือข่ายบิตคอยน์สามารถทำธุรกรรมในมูลค่าเศษส่วนได้ เช่น มิลลิบิทคอยน์ (1/1,000 ของบิทคอยน์) ไปจนถึง 1/100,000,000 ของบิทคอยน์ ซึ่งเรียกว่า "ซาโตชิ" ในหนังสือเล่มนี้ใช้กฎการพหูพจน์เดียวกันกับดอลลาร์หรือสกุลเงินแบบดั้งเดิมเมื่อพูดถึงจำนวนที่มากกว่าหนึ่งบิทคอยน์ เช่น "10 บิตคอยน์" หรือ "0.001 บิตคอยน์" กฎเดียวกันนี้ยังนำไปใช้กับหน่วยบัญชีบิทคอยน์อื่น ๆ เช่น มิลลิบิทคอยน์และซาโตชิอีกด้วย</p>
</blockquote>
<h2>ธุรกรรมในระบบบิตคอยน์</h2>
<p>ธุรกรรมในระบบบิตคอยน์คือการแจ้งเครือข่ายว่าเจ้าของบิทคอยน์ได้อนุมัติการโอนมูลค่าไปยังเจ้าของใหม่แล้ว และ เจ้าของใหม่สามารถใช้บิทคอยน์เหล่านั้นได้ โดยสร้างธุรกรรมใหม่เพื่อรออนุมัติการโอนไปยังเจ้าของคนอื่นต่อ ๆ ไป ทำให้เกิดการส่งต่อความเป็นเจ้าของอย่างต่อเนื่อง</p>
<h3>ธุรกรรมขาเข้าและขาออกของบิตคอยน์</h3>
<p>ธุรกรรมเปรียบเสมือนบันทึกในสมุดบัญชีแบบสองทาง โดยธุรกรรมแต่ละรายการจะมีอินพุต (inputs) หนึ่งรายการหรือมากกว่านั้นที่ใช้จ่ายเงิน และมีเอาต์พุต (outputs) หนึ่งรายการหรือมากกว่าที่รับเงิน มูลค่าของอินพุตและเอาต์พุตไม่จำเป็นต้องเท่ากันเสมอไป เอาต์พุตมักจะมีมูลค่าน้อยกว่าอินพุตเล็กน้อย ซึ่งส่วนต่างนี้คือ "ค่าธรรมเนียมธุรกรรม" ที่นักขุดจะได้รับเมื่อรวมธุรกรรมในบล็อกเชน</p>
<p>ธุรกรรมยังมีหลักฐานการเป็นเจ้าของสำหรับจำนวนบิตคอยน์ (อินพุต) ที่ถูกใช้ในรูปของลายเซ็นดิจิทัลจากเจ้าของเดิม ซึ่งสามารถตรวจสอบความถูกต้องได้ ในระบบบิตคอยน์การใช้จ่ายบิตคอยน์คือการลงนามในธุรกรรมเพื่อโอนมูลค่าจากธุรกรรมก่อนหน้าไปยังเจ้าของใหม่ที่ระบุผ่าน Bitcoin adress</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734787627692-YAKIHONNES3.png" alt="image"></p>
<h3>ห่วงโซ่ของธุรกรรม</h3>
<p>การชำระเงินของอลิซไปยังร้านของบ็อบนั้นเป็นใช้เอาต์พุตจากธุรกรรมก่อนหน้าเป็นอินพุตในธุรกรรมครั้งนี้ (ในบทก่อนหน้า อลิซได้รับบิทคอยน์จากโจเพื่อนของเธอ ) เราเรียกธุรกรรมนี้ว่า "ธุรกรรมที่ 1 (Tx1)" ซึ่งแสดงถึงห่วงโซ่ของธุรกรรมที่เอาต์พุตของธุรกรรมหนึ่งถูกใช้เป็นอินพุตในธุรกรรมถัดไป</p>
<h4>การอ้างอิงอินพุตจากเอาต์พุตก่อนหน้า</h4>
<p>Tx1 โอน 0.001 บิทคอยน์ (100,000 ซาโตชิ) ไปยังเอาต์พุตที่ล็อกด้วยกุญแจของอลิซ และในธุรกรรมใหม่ของอลิซ (Tx2) ที่ส่งให้ร้านของบ็อบ เธออ้างถึงเอาต์พุตก่อนหน้าเป็นอินพุต อย่างที่เห็นในภาพประกอบ การอ้างอิงด้วยลูกศรและระบุอินพุตว่า "Tx1:0" ในธุรกรรมจริง การอ้างอิงจะใช้รหัสประจำธุรกรรม (txid) เป็นตัวระบุขนาด 32 ไบต์ที่แสดงถึงธุรกรรมที่อลิซได้รับเงินจากโจ ส่วน ":0" หมายถึงตำแหน่งของเอาต์พุตที่อลิซได้รับเงิน ซึ่งในกรณีนี้คือตำแหน่งแรก (ตำแหน่ง 0)</p>
<h4>การคำนวณมูลค่าอินพุต</h4>
<p>เนื่องจากธุรกรรมของบิตคอยน์นั้นไม่ได้ระบุค่าของอินพุตอย่างชัดเจน ตัวซอฟต์แวร์เลยจะต้องใช้การอ้างอิงของอินพุตเพื่อค้นหาเอาต์พุตของธุรกรรมก่อนหน้าที่ถูกใช้ไป</p>
<h4>เอาต์พุตใน Tx2 ของอลิซ</h4>
<p>Tx2 ของอลิซมีเอาต์พุตใหม่สองรายการ รายการหนึ่งจ่าย 75,000 ซาโตชิสำหรับพอดแคสต์ และอีกรายการจ่าย 20,000 ซาโตชิคืนให้อลิซเป็นเงินทอน<br><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734787700413-YAKIHONNES3.png" alt="image"></p>
<blockquote>
<h4>เกร็ดสาระเล็ก ๆ น้อย ๆ</h4>
<ul>
<li>ธุรกรรมของบิตคอยน์นั้นอยู่ในรูปแบบซีเรียลไลซ์ (serialized) เป็นรูปแบบข้อมูลที่ซอฟต์แวร์ใช้สำหรับการส่งธุรกรรม โดยจะเข้ารหัสมูลค่าที่ต้องการโอนด้วยตัวเลขจำนวนเต็มซึ่งเป็นหน่วยมูลค่าที่เล็กที่สุดในระบบ on-chain</li>
<li>ที่มาของชื่อ "ซาโตชิ" :เมื่อบิตคอยน์ถูกสร้างขึ้นในครั้งแรก หน่วยมูลค่านี้ยังไม่มีชื่อเรียก นักพัฒนาบางคนจึงเรียกมันว่า "หน่วยฐาน" (base unit) แต่ต่อมาผู้ใช้งานหลาย ๆ คนเริ่มเรียกหน่วยนี้ว่า "ซาโตชิ" (satoshi หรือ sat) เพื่อเป็นเกียรติแก่ผู้สร้างบิตคอยน์</li>
</ul>
</blockquote>
<h4>การทอนเงิน</h4>
<p>นอกเหนือจากการสร้างเอาต์พุตเพื่อจ่ายให้กับผู้รับบิตคอยน์แล้ว ธุรกรรมจำนวนมากยังมีเอาต์พุตที่จ่ายเงินคืนให้กับผู้จ่าย ซึ่งเรียกว่า เอาต์พุตทอนเงิน (change output) เนื่องจากอินพุตของธุรกรรม (คล้ายกับธนบัตร) ไม่สามารถแบ่งใช้บางส่วนได้ เช่น ถ้าคุณซื้อของราคา 5 ดอลลาร์และจ่ายด้วยธนบัตร 20 ดอลลาร์ คุณคาดหวังว่าจะได้เงินทอน 15 ดอลลาร์ ในทำนองเดียวกัน ในธุรกรรม Bitcoin หากคุณซื้อสินค้าราคา 5 บิตคอยน์แต่มีอินพุตมูลค่า 20 บิตคอยน์ คุณจะสร้างเอาต์พุต 5 บิตคอยน์ไปยังเจ้าของร้าน และอีกเอาต์พุต 15 บิตคอยน์คืนให้ตัวคุณเองเป็นเงินทอน (ไม่นับรวมค่าธรรมเนียมธุรกรรม) </p>
<blockquote>
<ul>
<li>หากมองผ่านมุมของโปรโตคอลบิตคอยน์นั้นไม่ได้มีความแตกต่างใด ๆ ระหว่างเอาต์พุตเงินทอนกับเอาต์พุตการจ่ายเงินปกติ</li>
<li>โดยทั่วไปแล้วเอาต์พุตเงินทอนจะเป็นการโอนไปจ่ายให้กับ Bitcoin Address อันใหม่ในกระเป๋าตัวเองดังรูปที่แสดงข้างล่าง</li>
</ul>
</blockquote>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734787818103-YAKIHONNES3.png" alt="image"></p>
<h3>การสร้างธุรกรรม</h3>
<p>แอปพิเคชั่นกระเป๋าเงินของอลิซจะทำการสร้างอินพุตและสร้างเอาต์พุตตามที่ Alice ต้องการ โดยที่เธอเพียงแค่กรอกปลายทาง จำนวนเงิน และค่าธรรมเนียมธุรกรรมกระเป๋าเงินจะทำงานที่เหลือให้โดยอัตโนมัติ นอกจากนี้ กระเป๋าเงินยังสามารถสร้างธุรกรรมแบบออฟไลน์ได้ คล้ายกับการเขียนเช็คที่บ้านแล้วค่อยนำไปฝากธนาคารในภายหลังอีกด้วย</p>
<h4>การเลือกอินพุตที่เหมาะสม</h4>
<p>กระเป๋าเงินจะเลือกอินพุตที่มีมูลค่าเพียงพอสำหรับการชำระเงินไปยังบ๊อบโดยตรวจสอบเอาต์พุตที่ยังไม่ได้ใช้ (UTXOs) ซึ่งหากมูลค่าไม่เพียงพอ กระเป๋าเงินก็จะทำการรวม UTXOs หลาย ๆ รายการเข้าด้วยกัน เพื่อให้ได้ยอดที่ต้องการ และหากอินพุตมีมูลค่าสูงกว่าค่าที่ต้องจ่าย กระเป๋าเงินจะสร้างเอาต์พุตสำหรับทอนเงินกลับมาให้อลิซ</p>
<h4>การสร้างเอาต์พุต</h4>
<p>เอาต์พุตประกอบด้วยสคริปต์ที่กำหนดให้ผู้ที่มีคีย์ของบ๊อบเท่านั้นสามารถลงนามเพื่อที่จะใช้เงินได้ นอกจากนี้ยังมีเอาต์พุตสำหรับทอนเงินกลับมาให้อลิซ ซึ่งทำให้เธอสามารถใช้เงินทอนนั้นในการทำธุรกรรมถัดไป และค่าธรรมเนียมธุรกรรมจะถูกคำนวณจากส่วนต่างระหว่างอินพุตและเอาต์พุต และนั่นเองที่จะเป็นรางวัลสำหรับนักขุดที่บันทึกธุรกรรมลงบล็อกเชนของบิตคอยน์</p>
<h2>การใส่ธุรกรรมลงในบล๊อกเชน</h2>
<p>ธุรกรรมที่สร้างขึ้นโดย Bitcoin wallet ของอลิซมีข้อมูลทั้งหมดที่จำเป็นสำหรับการสร้างธุรกรรม (การยืนยันว่าอลิซเป็นเจ้าของเงิน และ Bitcoin address ปลายทาง) จากนั้นธุรกรรมนี้จะต้องถูกส่งไปยังเครือข่ายของบิตคอยน์ เพื่อที่จะให้ธุรกรรมนั้นเป็นส่วนนึงในเครือข่ายของบิตคอยน์ และในส่วนถัดไปของหนังสือเล่มนี้ เราจะอธิบายถึงว่าธุรกรรมกลายเป็นส่วนหนึ่งของบล็อกใหม่อย่างไร และกระบวนการขุดบล็อกเป็นอย่างไร รวมถึงการที่บล็อกใหม่ได้รับความไว้วางใจมากขึ้นเมื่อมีการเพิ่มบล็อกใหม่ ๆ เข้ามาเรื่อย ๆ หมายถึงอะไร ?</p>
<h3>การส่งธุรกรรมเข้าไปยังเครือข่าย</h3>
<p>เนื่องจากธุรกรรมมีข้อมูลที่จำเป็นสำหรับการประมวลผลทั้งหมด จึงทำให้การส่งผ่านสามารถทำได้จากที่ไหนหรืออย่างไรก็ได้ อย่างที่ได้กล่าวไปว่าเครือข่ายของบิตคอยน์เป็นเครือข่ายแบบ peer-to-peer โดยที่แต่ละโหนดเชื่อมต่อกับโหนดอื่น ๆ อีกหลายโหนด เพื่อทำหน้าที่กระจายธุรกรรมและบล็อกให้กับผู้เข้าร่วมทั้งหมดในระบบ</p>
<h3>การกระจายธุรกรรม</h3>
<p>โหนดในเครือข่าย peer-to-peer ของบิตคอยน์นั้นเป็นซอฟต์แวร์ที่สามารถตรวจสอบความถูกต้องของธุรกรรมได้  และการเชื่อมต่อระหว่างโหนดสามารถแสดงเป็นเส้นในกราฟ ทำให้โหนดเหล่านี้เรียกว่า “โหนดตรวจสอบเต็มรูปแบบ” (full nodes) กระเป๋าเงินของอลิซสามารถส่งธุรกรรมไปยังโหนดบิตคอยน์ใด ๆ ผ่านการเชื่อมต่อใด ๆ ก็ได้ เช่น สายแลน WiFi หรือ เครือข่ายมือถือ โดยถ้าหากโหนดได้รับธุรกรรมที่ถูกต้องซึ่งยังไม่เคยเห็นมาก่อน มันจะกระจายธุรกรรมนี้ไปยังโหนดอื่น ๆ ที่เชื่อมต่อด้วย ซึ่งเป็นเทคนิคที่เรียกว่า gossiping ซึ่งทำให้ธุรกรรมแพร่กระจายไปทั่วเครือข่ายอย่างรวดเร็วภายในไม่กี่วินาที</p>
<h3>การขุดบิตคอยน์</h3>
<p>ตอนนี้ธุรกรรมของอลิซได้เข้าไปสู่ในเครือข่ายของบิตคอยน์แล้ว แต่มันยังไม่ได้ถูกบรรจุลงในบล๊อกเชนเนื่องจากจะต้องรอให้นักขุดทำการนำธุรกรรมนั้น ๆ เข้าไปในบล๊อกและบล๊อกนั้นจำเป็นต้องผ่านการตรวจสอบโดยโหนดในเครือข่ายของบิตคอยน์เสียก่อน จึงจะถูกบันทึกลงในบล๊อกเชน<br>ในระบบของบิตคอยน์นั้น มีการป้องกันการปลอมแปลงด้วยการคำนวณทางคณิตศาสตร์ ซึ่งเป็นการคำนวณที่จำเป็นต้องใช้พลังงานมหาศาลในการคำนวณ แต่ใช้พลังงานเพียงเล็กน้อยในการตรวจสอบ โดยธุรกรรมทั้งหมดจะถูกจัดเรียงเป็นบล๊อกและแต่ละบล๊อกจะมีบล๊อกเฮดเดอร์ที่จำเป็นต้องสร้างตามเงื่อนไขเฉพาะ โดยกระบวนการขุดบิตคอยน์นั้นมีวัตถุประสงค์อยู่สองอย่าง ดังนี้:</p>
<ul>
<li>สร้างแรงจูงใจให้ขุดเฉพาะธุรกรรมที่ถูกต้องตามกฎ: เนื่องจากวิธีที่เหล่านักขุดจะได้รับผลกำไรที่สูงที่สุดจากการสร้างบล๊อกที่ตรงกับฉันทมติของระบบเท่านั้น (หากไม่ทำตามบล๊อกจะไม่ถูกยอมรับโดยโหนด และนั่นจะเป็นการสิ้นเปลืองพลังงานที่ได้คำนวณมาโดยเปล่าประโยชน์) นั้นจึงเป็นแรงจูงใจหลัก ๆ ให้เหล่านักขุดทำการใส่ธุรกรรมที่ถูกต้องตามกฏเท่านั้นลงในบล๊อกที่ตนสร้าง และสิ่งนี้เองก็ทำให้ผู้ใช้สามารถเลือกที่จะสันนิษฐานโดยอิงตามความไว้วางใจว่าธุรกรรมใด ๆ ในบล็อกนั้น ๆ เป็นธุรกรรมที่ถูกต้อง </li>
<li>สร้างเหรียญใหม่ตามตารางการออกเหรียญที่กำหนดไว้ล่วงหน้า: ในปัจจุบันนั้นจะมีการสร้างบิตคอยน์ใหม่ในแต่ละบล็อก คล้ายคลึงกับธนาคารกลางที่พิมพ์เงินใหม่ โดยจำนวนบิตคอยน์ในแต่ละบล๊อกที่จะถูกผลิตขึ้นมาใหม่นั้นถูกกำหนดมาตั้งแต่วันที่ระบบของบิตคอยน์ได้เริ่มขึ้นและไม่สามารถเปลี่ยนแปลงได้</li>
</ul>
<p>การขุดได้ช่วยให้เกิดความสมดุลระหว่างต้นทุนและผลตอบแทน เนื่องจากการขุดมีการใช้ไฟฟ้าเพื่อแก้ปัญหาการคำนวณ และนักขุดที่ประสบความสำเร็จจะได้รับรางวัลในรูปแบบของบิตคอยน์ใหม่และค่าธรรมเนียมจากการทำธุรกรรม แต่อย่างไรก็ตาม รางวัลจะถูกเก็บรวบรวมก็ต่อเมื่อนักขุดรวมเฉพาะธุรกรรมที่ถูกต้องเท่านั้น โดยกฎของโปรโตคอลบิตคอยน์สำหรับการสร้างฉันทามติ จะกำหนดว่าอะไรถูกต้อง โดยความสมดุลที่ละเอียดอ่อนนี้เองที่คอยสร้างให้ความปลอดภัยแก่บิตคอยน์โดยไม่ต้องมีหน่วยงานกลางมาคอยดูแล</p>
<p>การขุดถูกออกแบบให้เหมือนกับการจับสลากแบบกระจายศูนย์ นักขุดแต่ละคนสามารถสร้าง "สลาก" ของตัวเองได้โดยการสร้างบล็อกตัวอย่างที่ประกอบไปด้วยธุรกรรมใหม่ที่ต้องการขุด พร้อมกับข้อมูลอื่น ๆ และนักขุดจะป้อนบล็อกตัวอย่างนี้เข้าไปในอัลกอริทึมที่ออกแบบมาเป็นพิเศษเพื่อแฮชข้อมูล ทำให้ได้ค่าผลลัพธ์ที่แตกต่างจากข้อมูลเดิมอย่างสิ้นเชิง โดยแฮชฟังก์ชันนี้จะให้ผลลัพธ์เดียวกันเสมอสำหรับข้อมูลชุดเดิม แต่ไม่สามารถคาดเดาผลลัพธ์ได้หากป้อนข้อมูลใหม่ แม้จะแตกต่างเพียงเล็กน้อยจากข้อมูลก่อนหน้า</p>
<p>หากค่าผลลัพธ์ของแฮชตรงกับเงื่อนไขที่กำหนดของโปรโตคอล นักขุดจะชนะการจับสลาก และผู้ใช้งานบิตคอยน์ จะยอมรับบล็อกนี้พร้อมกับธุรกรรมในนั้นว่าเป็นบล็อกที่ถูกต้อง หากไม่ตรงกับเงื่อนไข นักขุดจะปรับข้อมูลในบล็อกเล็กน้อยและลองทำการแฮชใหม่ กระบวนการนี้ต้องทำซ้ำหลายครั้ง โดย ณ ขณะที่เขียนนี้ นักขุดต้องลองสร้างบล็อกตัวอย่างประมาณ 168 พันล้านล้านครั้ง เพื่อหาคำตอบที่ถูกต้อง ซึ่งหมายถึงการรันแฮชฟังก์ชันในจำนวนครั้งมหาศาลมาก ๆ </p>
<p>แต่เมื่อพบคำตอบที่ถูกต้องแล้ว ใครก็ตามสามารถตรวจสอบว่าบล็อกนั้นถูกต้องได้โดยการรันแฮชฟังก์ชันเพียงครั้งเดียว ซึ่งทำให้การสร้างบล็อกที่ถูกต้องต้องใช้พลังงานคำนวณมหาศาล แต่การตรวจสอบทำได้ง่ายมาก กระบวนการตรวจสอบนี้สามารถพิสูจน์ได้อย่างมีหลักการว่ามีการทำงานเกิดขึ้นจริง ดังนั้น ข้อมูลที่ใช้สร้างหลักฐานนี้—ในที่นี้คือบล็อก—เรียกว่า "หลักฐานการทำงาน" หรือ Proof of Work (PoW)</p>
<p>ธุรกรรมจะถูกเพิ่มลงในบล็อกใหม่ โดยให้ความสำคัญกับธุรกรรมที่มีค่าธรรมเนียมสูงสุดก่อนและพิจารณาจากปัจจัยอื่น ๆ อีกเล็กน้อย นักขุดแต่ละคนจะเริ่มกระบวนการสร้างบล็อกตัวอย่างใหม่ทันทีหลังจากได้รับบล็อกก่อนหน้าจากเครือข่าย โดยรู้ว่ามีคนอื่นชนะรางวัลไปแล้วในรอบนั้น พวกเขาจะสร้างบล็อกตัวอย่างใหม่ที่เชื่อมโยงกับบล็อกก่อนหน้า ใส่ธุรกรรมเข้าไป และเริ่มคำนวณ Proof of Work (PoW) สำหรับบล็อกตัวอย่างนี้ นักขุดจะเพิ่มธุรกรรมพิเศษที่จ่ายรางวัลบล็อกและค่าธรรมเนียมธุรกรรมรวมเข้ากับที่อยู่บิตคอยน์ของตนเอง หากพวกเขาพบบล็อกที่ถูกต้องและถูกเพิ่มในบล็อกเชน นักขุดจะได้รับรางวัลนั้น และธุรกรรมรางวัลนี้ก็จะใช้งานได้ นักขุดที่ทำงานร่วมกับพูลจะตั้งค่าให้รางวัลถูกส่งไปยังที่อยู่ของพูล จากนั้นจะแบ่งรางวัลให้สมาชิกตามสัดส่วนการทำงานที่แต่ละคนมีส่วนร่วม</p>
<p>กลับมาที่ธุรกรรมของอลิซ ตอนนี้ธุรกรรมของอลิซได้ถูกเครือข่ายรับไปแล้วและเพิ่มลงในพูลของธุรกรรมที่ยังไม่ได้รับการยืนยันเรียบร้อย จากนั้นเมื่อธุรกรรมนั้นผ่านการตรวจสอบจาก full node แล้ว มันจะถูกรวมไว้ในบล็อกตัวอย่าง และประมาณห้านาทีหลังจากที่อลิซส่งธุรกรรมจากกระเป๋าเงินของเธอ นักขุดคนหนึ่งพบคำตอบสำหรับบล็อกนั้นและประกาศไปยังเครือข่าย หลังจากที่นักขุดคนอื่น ๆ ตรวจสอบความถูกต้องของบล็อกที่ชนะ พวกเขาก็เริ่มกระบวนการสุ่มอีกครั้งเพื่อสร้างบล็อกถัดไป</p>
<p>บล็อกที่ชนะซึ่งมีธุรกรรมของอลิซอยู่ในนั้น ได้กลายเป็นส่วนหนึ่งของบล็อกเชน และบล็อกนี้ถูกนับเป็นการยืนยันหนึ่งครั้งสำหรับธุรกรรมนั้น หลังจากที่บล็อกที่มีธุรกรรมของอลิซได้ถูกเผยแพร่ไปทั่วเครือข่าย การสร้างบล็อกทางเลือกที่มีเวอร์ชันอื่นของธุรกรรมของอลิซ (เช่น ธุรกรรมที่ไม่ได้จ่ายให้ บ๊อบ) จะต้องใช้ปริมาณงานเท่ากับที่นักขุดทั้งหมดต้องใช้ในการสร้างบล็อกใหม่ทั้งบล็อก เมื่อมีบล็อกทางเลือกหลายบล็อกให้เลือก full node ในเครือข่ายของบิตคอยน์ก็จะทำการเลือกบล็อกเชนที่ถูกต้อง โดยจะเป็นเชนซึ่งมี Proof of Work (PoW) รวมมากที่สุด ซึ่งเรียกว่าบล็อกเชนที่ดีที่สุด หากเครือข่ายทั้งหมดจะยอมรับบล็อกทางเลือก จะต้องมีการขุดบล็อกใหม่เพิ่มเติมอีกหนึ่งบล็อกต่อจากบล็อกทางเลือกนั้น</p>
<p>นั่นหมายความว่านักขุดมีตัวเลือกอื่น อย่างเช่นการที่พวกเขาสามารถร่วมมือกับอลิซเพื่อสร้างธุรกรรมทางเลือกที่เธอไม่ได้จ่ายเงินให้บ๊อบ โดยอลิซอาจเสนอส่วนแบ่งจากเงินที่เธอเคยจ่ายให้บ๊อบแก่นักขุด แต่การกระทำที่ไม่ซื่อสัตย์นี้จะต้องใช้ความพยายามเท่ากับการสร้างบล็อกใหม่ถึงสองบล็อก ซึ่งในทางกลับกันแล้ว นักขุดที่ทำงานอย่างซื่อสัตย์สามารถสร้างบล็อกใหม่เพียงบล็อกเดียวและได้รับค่าธรรมเนียมจากธุรกรรมทั้งหมดที่รวมอยู่ในบล็อก พร้อมกับรางวัลบล็อก (block subsidy) นอกจากนี้ต้นทุนที่สูงในการสร้างบล็อกสองบล็อกเพื่อพยายามเปลี่ยนแปลงธุรกรรมที่ยืนยันแล้วสำหรับผลตอบแทนเพียงเล็กน้อยนั้นไม่คุ้มค่าและการกระทำดังกล่าวมีโอกาสน้อยที่จะเกิดขึ้น สำหรับ บ๊อบ นั่นหมายความว่าเขาสามารถเริ่มเชื่อถือได้ว่าการชำระเงินจากอลิซนั้นเป็นสิ่งที่เชื่อถือได้</p>
<p>ประมาณ 19 นาทีหลังจากบล็อกที่มีธุรกรรมของอลิซ ได้ถูกเผยแพร่บล็อกใหม่ถูกขุดขึ้นโดยนักขุดอีกคน และเนื่องจากบล็อกใหม่นี้ถูกสร้างต่อจากบล็อกที่มีธุรกรรมของอลิซ (ทำให้ธุรกรรมของอลิซได้รับการยืนยันสองครั้ง) ธุรกรรมของ อลิซจะสามารถเปลี่ยนแปลงได้ก็ต่อเมื่อมีการขุดบล็อกทางเลือกสองบล็อกขึ้นมา และมีบล็อกใหม่ที่สร้างต่อจากบล็อกเหล่านั้น รวมเป็นสามบล็อกที่ต้องถูกขุดเพื่อให้อลิซสามารถดึงเงินที่เธอส่งให้บ๊อบกลับมาได้ และทุกบล็อกที่ขุดต่อจากบล็อกที่มีธุรกรรมของอลิซนั้นจะนับเป็นการยืนยันเพิ่มเติม เมื่อจำนวนบล็อกที่ต่อกันเพิ่มมากขึ้น การย้อนกลับธุรกรรมก็จะยากขึ้นเรื่อย ๆ ทำให้บ๊อบมั่นใจมากขึ้นเรื่อย ๆ ว่าการชำระเงินของอลิซนั้นจะปลอดภัย</p>
<p>จากภาพที่แนบไว้ข้างล่างนี้ เราสามารถเห็นบล็อกที่มีธุรกรรมของอลิซและด้านล่างของบล็อกนี้มีบล็อกอีกหลายแสนบล็อกที่เชื่อมต่อกันเป็นโซ่ (blockchain) ต่อเนื่องไปจนถึงบล็อกหมายเลข <a href='/tag/0/'>#0</a> หรือที่เรียกว่า genesis block  และเมื่อเวลาผ่านไป "ความสูง" ของบล็อกใหม่ที่เพิ่มขึ้นจะทำให้ความยากในการคำนวณของทั้งเครือข่ายเพิ่มขึ้นตามไปด้วย ตามธรรมเนียมแล้ว บล็อกใด ๆ ที่มีการยืนยันมากกว่าหกครั้งจะถือว่ายากมากที่จะเปลี่ยนแปลง เพราะต้องใช้การคำนวณอย่างมหาศาลในการคำนวณบล็อกหกบล็อกใหม่ (รวมถึงบล็อกใหม่อีกหนึ่งบล็อก)</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734788200516-YAKIHONNES3.png" alt="image"></p>
<h4>การใช้จ่ายในธุรกรรม</h4>
<p>เมื่อธุรกรรมของอลิซได้กลายเป็นส่วนหนึ่งของบล็อกเชน แปลว่ามันสามารถถูกเรียกดูได้จากทุกบิตคอยน์แอปพลิเคชัน และทุกโหนดสามารถที่จะตรวจสอบธุรกรรมนี้ได้อย่างอิสระ ว่าธุรกรรมนี้ถูกต้องหรือไม่ โดยจะตรวจสอบย้อนไปตั้งแต่ตอนที่เหรียญนั้น ๆ ถูกสร้างและตรวจสอบต่อมาเรื่อย ๆ จนถึงธุรกรรมปัจจุบัน ไคลเอนต์จะสามารถตรวจสอบการชำระเงินได้บางส่วน โดยการยืนยันว่าธุรกรรมนั้นอยู่ในบล็อกเชนแล้ว และมีบล็อกจำนวนมากที่ถูกขุดหลังจากนั้น ซึ่งนี่ช่วยให้มั่นใจได้ว่ามีการใช้ความพยายามอย่างมากในการยืนยันธุรกรรมนี้โดยเหล่านักขุดไปแล้ว </p>
<p>ในตอนนี้บ็อบสามารถใช้บิตคอยน์ที่อลิซส่งให้ต่อได้แล้ว! สมมุติว่าบ๊อบต้องการจ่ายค่าจ้างให้ผู้รับเหมาหรือผู้จัดหาสินค้า โดยการโอนมูลค่าจากการชำระเงินของอลิซสำหรับพอดแคสต์ไปยังเจ้าของรายใหม่ เมื่อบ๊อบใช้เงินที่ได้รับจากอลิซและลูกค้าคนอื่น ๆ เขาก็ขยายสายโซ่ของธุรกรรมออกไป สมมติว่าบ๊อบจ่ายค่าจ้างให้กรูฟซึ่งเป็นนักออกแบบเว็บไซต์ของเขาสำหรับสร้างหน้าเว็บใหม่ สายโซ่ของธุรกรรมจะมีลักษณะดังนี้:</p>
<ul>
<li>ธุรกรรมจากโจถึงอลิซ: โจโอนบิตคอยน์ให้อลิซเพื่อการซื้อขายหรือจ่ายค่าบริการบางอย่าง</li>
<li>ธุรกรรมจากอลิซถึงบ๊อบ: อลิซโอนบิตคอยน์ให้บ๊อบเพื่อชำระค่าพอดแคสต์</li>
<li>ธุรกรรมจากบ๊อบถึงกรูฟ: บ๊อบโอนบิตคอยน์จากที่ได้รับจากอลิซให้กรูฟเพื่อจ่ายค่าจ้างออกแบบเว็บไซต์</li>
</ul>
<p>สายโซ่ของธุรกรรมนี้แสดงให้เห็นการเชื่อมต่อของธุรกรรมจากโจถึงอลิซและจากอลิซถึงบ๊อบต่อด้วยจากบ๊อบถึง กรูฟ โดยแต่ละธุรกรรมได้รับการบันทึกไว้ในบล็อกเชน ซึ่งช่วยให้ทุกคนสามารถตรวจสอบความถูกต้องของธุรกรรมในสายโซ่นี้ได้</p>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1734788274465-YAKIHONNES3.png" alt="image"></p>
<p>ในบทนี้ เราได้เห็นวิธีที่ธุรกรรมสร้างสายโซ่ที่ถ่ายโอนมูลค่าจากเจ้าของหนึ่งไปยังอีกเจ้าของหนึ่ง นอกจากนี้เรายังได้ติดตามธุรกรรมของอลิซตั้งแต่เริ่มสร้างในกระเป๋าเงินของเธอ ผ่านเครือข่ายบิตคอยน์ไปจนถึงนักขุดที่บันทึกมันลงในบล็อกเชน และสำหรับในส่วนที่เหลือของหนังสือเล่มนี้ เราจะศึกษารายละเอียดเกี่ยวกับเทคโนโลยีที่เกี่ยวข้อง ไม่ว่าจะเป็นกระเป๋าเงิน, address, digital signature, network และกระบวนการขุดอย่างละเอียด</p>
]]></itunes:summary>
      <itunes:image href="https://image.nostr.build/5648ee8b923cd74f0d7f797a52aa66cff315a0e887e7683a2f9fbd72e6a81187.jpg"/>
      </item>
      
      <item>
      <title><![CDATA[nOStr the series :Nostr Web Services ]]></title>
      <description><![CDATA[ปลดล็อกการเชื่อมต่อสู่โลกโดยไม่ต้องใช้ที่อยู่ IP สาธารณะบน Nostr
]]></description>
             <itunes:subtitle><![CDATA[ปลดล็อกการเชื่อมต่อสู่โลกโดยไม่ต้องใช้ที่อยู่ IP สาธารณะบน Nostr
]]></itunes:subtitle>
      <pubDate>Thu, 12 Sep 2024 11:16:45 GMT</pubDate>
      <link>https://learnbn.npub.pro/post/byjh9iwzfudbgzosbsp/</link>
      <comments>https://learnbn.npub.pro/post/byjh9iwzfudbgzosbsp/</comments>
      <guid isPermaLink="false">naddr1qq2ky7t2fqukja66ge6kgcn80fhhxcjn2qkj6q3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65wt0u5m8</guid>
      <category>ลองฟอร์มของไดโน</category>
      
        <media:content url="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1726139805814-YAKIHONNES3.jpg" medium="image"/>
        <enclosure 
          url="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1726139805814-YAKIHONNES3.jpg" length="0" 
          type="image/jpeg" 
        />
      <noteId>naddr1qq2ky7t2fqukja66ge6kgcn80fhhxcjn2qkj6q3q0yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2sxpqqqp65wt0u5m8</noteId>
      <npub>npub10yqgu7q6mmrk0nywywd4x0kukx029cnqly5p4yf9ay6zthavjw2syrhcl3</npub>
      <dc:creator><![CDATA[Learning_BTC&NOSTR]]></dc:creator>
      <content:encoded><![CDATA[<p><strong>" ผมตื่นเต้นที่จะประกาศเปิดตัว NWS ครั้งแรก นี่เป็นคำตอบของการเชื่อมต่อออกไปยังโลกของอินเตอร์เน็ต โดยใช้ NOSTR เข้าไปแทรกระหว่าง transport layer และ network layer ที่อยู่บน TCP/IP โมเดล NWS นั้นจะเข้ามาช่วยให้การสื่อสารระหว่างอุปกรณ์ต่างๆ ให้เป็นไปอย่างราบรื่น มีความเป็นส่วนตัวเพิ่มขึ้น และทำให้เราสามารถท่องโลกของอินเตอร์เน็ตโดยไม่จำเป็นต้องใช้ Public IP "</strong> -asmogo </p>
<h2>NostrWebServices (NWS) คืออะไร?</h2>
<p>NWS คือการนำ Nostr เข้ามาช่วยในการเข้าถึงบริการของเว็บไซต์ต่าง ๆ โดยยังสามารถส่งข้อมูลขึ้นไปยัง transport layer บน TCP/IP ซึ่งด้วยวิธีนี้เองเลยทำให้เราสามารถสร้างการเชื่อมต่อได้โดยไม่จำเป็นต้องมี Public IP โดยส่วนที่จำเป็นหลัก ๆ ในการทำงานนี้เราจำเป็นต้องเตรียม</p>
<ul>
<li><p><strong>Entry node</strong>: SOCKS5 proxy ที่คอยส่งต่อ TCP packets ที่เข้ารหัสผ่านทาง event ของ Nostr ก่อนส่งส่งผ่านรีเลย์ต่าง ๆ ไปยัง Exit node</p>
</li>
<li><p><strong>Exit node</strong>:  TCP reverse proxy ที่คอยรอรับข้อมูลมาจาก Entry node และส่งต่อข้อมูลต่าง ๆ ไปยังบริการอื่น ๆ ต่อไป</p>
</li>
</ul>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1726136393181-YAKIHONNES3.png" alt="image"></p>
<p>ในส่วนของการทำงานนั้นสามารถดูได้จากในรูปที่ได้แนบไว้ข้างต้นได้เลย<br>อย่างที่เห็นเลยว่ามันเริ่มจากแอพต่าง ๆ เหมือนที่เรามีการใช้งานกันอยู่ในปัจจุบัน เพียงแต่แทนที่เราจะส่งข้อมูลต่าง ๆ ผ่าน IP layer ตามปกติ เราสามารถส่งเข้าไปยัง Entry node และหลังจากนั้นข้อมูลจะถูกส่งผ่าน relay ต่าง ๆ ก่อนที่จะวิ่งไปยัง Exit node และ Exit node จะคอยส่งข้อมูลต่าง ๆ ไปยังบริการหลังบ้านที่เราต้องการจะเชื่อมต่อ </p>
<p>เราจะเห็นได้ว่าการทำงานของ NWS นั้น สำหรับผู้ใช้งานโดยทั่วไปแล้วแทบจะไม่แตกต่างอะไรกับในปัจจุบันเลย นั่นก็เพราะว่าเรายังทำการใช้งานแอพต่าง ๆ ตามปกติ แต่ประโยชน์ที่เราจะได้รับเพิ่มเติมนั้นคือความเป็นส่วนตัวในการใช้บริการเบื้องหลังอื่น ๆ </p>
<p>นอกจากนี้ยังมีในส่วนของ NWS domain names ใน NWS นั้นมีการรับรอง domain สองตัวนั้นคือ </p>
<ol>
<li>".nostr" ซึ่งใช้ชื่อโฮสต์เป็นกุญแจสาธารณะที่เข้ารหัสด้วย base32 และ รีเลย์ที่เข้ารหัสด้วย base32 เป็นโดเมนย่อย</li>
<li>"nprofiles" ซึ่งเป็นการผสมผสานกันระหว่างกุญแจสาธารณะของNostrและรีเลย์หลายตัว</li>
</ol>
<p>ซึ่งโดเมนทั้งสองประเภทจะถูกสร้างและปรากฏอยู่บนคอนโซลเมื่อเริ่มต้นใช้งาน</p>
<h2>หัวใจหลักในการทำงานของ NWS</h2>
<p>อย่างที่ได้กล่าวไว้ข้างต้นว่า NWS เข้ามาช่วยเหลือในการเพิ่มความเป็นส่วนตัวได้ แล้วถ้าเราไม่ได้ต้องการเข้ารหัส หรือถ้าเราอยากจะเปิด Entry node แล้วเราไม่ต้องการให้คนที่จะเชื่อมต่อเข้ามาเข้ารหัสข้อมูลเราสามารถทำได้เช่นกัน โดยทั้งสองระบบที่ NWS ทำได้มีดังนี้</p>
<ul>
<li><p>แบบไม่เข้ารหัส: เราจำเป็นต้องรันโหนดไว้ในเครื่องที่เราใช้รัน client แล้วแปลง client ให้ส่งข้อความใส่โหนดปลายทางตรง ๆ</p>
</li>
<li><p>แบบเข้ารหัส เริ่มจากการเชื่อมไปที่โหนดที่ต้องการ (อารมณ์ ssh เข้าไป) แต่วิธีนี้ยังติดที่บางเว็บที่เป็น https จะเชื่อมไม่ได้หาก cert ของโหนดที่เราเชื่อมมีปัญหา ส่วนข้อมูลที่ส่งจะถูก encrypt ไว้ด้วย Sphinx (เข้ารหัสแบบหนึ่งในมาตรฐาน RSA) ฉนั้นมั่นใจได้ว่าโหนดที่เราไปยืมจมูกเขาหายใจก็ไม่รู้ว่าเราส่งอะไรไป</p>
</li>
</ul>
<h2>การตรวจสอบตัวตนของเซิร์ฟเวอร์ปลายทาง</h2>
<p>การตรวจสอบตัวตนของเซิร์ฟเวอร์ปลายทางของ NWS ทำโดยการใช้ประโยชน์จาก nostr โดยให้โหนดปลายทางปล่อย cert (TLS) บน nostr และเซ็นรับรองด้วยตนเองและอณุญาติให้ไคลเอนต์สามารถดึงใบรับรองนี้มาก่อนเชื่อมต่อกับโหนดที่เป็นจุดเริ่มที่เราใช้</p>
<p>และนอกจากนี้ในอนาคตของการพัฒนา NWS กำลังพัฒนาให้ NWS สามารถกำหนดให้โหนดปลายทางสามารถกำหนดค่าให้เข้าถึงอินเทอร์เน็ตได้ (ไม่ใช่แค่บริการภายใน) ทำให้ NWS ทำงานคล้าย VPN โดยสามารถเข้าเว็บไซต์ปกติได้ เช่น พิมพ์ "https google dot com" ก็เข้า google ได้</p>
<p>และทั้งหมดที่กล่าวไว้ข้างต้นเป็นเพียงการเริ่มต้น ยังมีคุณสมบัติอย่างอื่นอีกมากมายที่จะเข้ามาปรับปรุงปัญหาต่าง ๆ ในการใช้งานอินเตอร์เน็ต โดยส่วนตัวผมรู้สึกตื่นเต้นกับอนาคตของ NWS มาก ๆ และคาดหวังที่จะได้เห็นคนหันมาให้ความสนใจและใช้งานมัน ทั้งนี้หากใครอยากทดลองใช้งานก็สามารถติดตามได้ที่ </p>
<p>   GitHub: <np-embed url="https://github.com/asmogo/nws"><a href="https://github.com/asmogo/nws">https://github.com/asmogo/nws</a></np-embed></p>
<h2>ส่งท้าย</h2>
<p>สำหรับ series nOStr ผมตั้งใจจะหาเรื่องราวเกี่ยวกับ other stuff ที่น่าสนใจ ให้ทุกคน ๆ ได้ศึกษาไปพร้อม ๆ กัน เพราะว่า nostr ไม่ได้มีแค่โน๊ต </p>
]]></content:encoded>
      <itunes:author><![CDATA[Learning_BTC&NOSTR]]></itunes:author>
      <itunes:summary><![CDATA[<p><strong>" ผมตื่นเต้นที่จะประกาศเปิดตัว NWS ครั้งแรก นี่เป็นคำตอบของการเชื่อมต่อออกไปยังโลกของอินเตอร์เน็ต โดยใช้ NOSTR เข้าไปแทรกระหว่าง transport layer และ network layer ที่อยู่บน TCP/IP โมเดล NWS นั้นจะเข้ามาช่วยให้การสื่อสารระหว่างอุปกรณ์ต่างๆ ให้เป็นไปอย่างราบรื่น มีความเป็นส่วนตัวเพิ่มขึ้น และทำให้เราสามารถท่องโลกของอินเตอร์เน็ตโดยไม่จำเป็นต้องใช้ Public IP "</strong> -asmogo </p>
<h2>NostrWebServices (NWS) คืออะไร?</h2>
<p>NWS คือการนำ Nostr เข้ามาช่วยในการเข้าถึงบริการของเว็บไซต์ต่าง ๆ โดยยังสามารถส่งข้อมูลขึ้นไปยัง transport layer บน TCP/IP ซึ่งด้วยวิธีนี้เองเลยทำให้เราสามารถสร้างการเชื่อมต่อได้โดยไม่จำเป็นต้องมี Public IP โดยส่วนที่จำเป็นหลัก ๆ ในการทำงานนี้เราจำเป็นต้องเตรียม</p>
<ul>
<li><p><strong>Entry node</strong>: SOCKS5 proxy ที่คอยส่งต่อ TCP packets ที่เข้ารหัสผ่านทาง event ของ Nostr ก่อนส่งส่งผ่านรีเลย์ต่าง ๆ ไปยัง Exit node</p>
</li>
<li><p><strong>Exit node</strong>:  TCP reverse proxy ที่คอยรอรับข้อมูลมาจาก Entry node และส่งต่อข้อมูลต่าง ๆ ไปยังบริการอื่น ๆ ต่อไป</p>
</li>
</ul>
<p><img src="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1726136393181-YAKIHONNES3.png" alt="image"></p>
<p>ในส่วนของการทำงานนั้นสามารถดูได้จากในรูปที่ได้แนบไว้ข้างต้นได้เลย<br>อย่างที่เห็นเลยว่ามันเริ่มจากแอพต่าง ๆ เหมือนที่เรามีการใช้งานกันอยู่ในปัจจุบัน เพียงแต่แทนที่เราจะส่งข้อมูลต่าง ๆ ผ่าน IP layer ตามปกติ เราสามารถส่งเข้าไปยัง Entry node และหลังจากนั้นข้อมูลจะถูกส่งผ่าน relay ต่าง ๆ ก่อนที่จะวิ่งไปยัง Exit node และ Exit node จะคอยส่งข้อมูลต่าง ๆ ไปยังบริการหลังบ้านที่เราต้องการจะเชื่อมต่อ </p>
<p>เราจะเห็นได้ว่าการทำงานของ NWS นั้น สำหรับผู้ใช้งานโดยทั่วไปแล้วแทบจะไม่แตกต่างอะไรกับในปัจจุบันเลย นั่นก็เพราะว่าเรายังทำการใช้งานแอพต่าง ๆ ตามปกติ แต่ประโยชน์ที่เราจะได้รับเพิ่มเติมนั้นคือความเป็นส่วนตัวในการใช้บริการเบื้องหลังอื่น ๆ </p>
<p>นอกจากนี้ยังมีในส่วนของ NWS domain names ใน NWS นั้นมีการรับรอง domain สองตัวนั้นคือ </p>
<ol>
<li>".nostr" ซึ่งใช้ชื่อโฮสต์เป็นกุญแจสาธารณะที่เข้ารหัสด้วย base32 และ รีเลย์ที่เข้ารหัสด้วย base32 เป็นโดเมนย่อย</li>
<li>"nprofiles" ซึ่งเป็นการผสมผสานกันระหว่างกุญแจสาธารณะของNostrและรีเลย์หลายตัว</li>
</ol>
<p>ซึ่งโดเมนทั้งสองประเภทจะถูกสร้างและปรากฏอยู่บนคอนโซลเมื่อเริ่มต้นใช้งาน</p>
<h2>หัวใจหลักในการทำงานของ NWS</h2>
<p>อย่างที่ได้กล่าวไว้ข้างต้นว่า NWS เข้ามาช่วยเหลือในการเพิ่มความเป็นส่วนตัวได้ แล้วถ้าเราไม่ได้ต้องการเข้ารหัส หรือถ้าเราอยากจะเปิด Entry node แล้วเราไม่ต้องการให้คนที่จะเชื่อมต่อเข้ามาเข้ารหัสข้อมูลเราสามารถทำได้เช่นกัน โดยทั้งสองระบบที่ NWS ทำได้มีดังนี้</p>
<ul>
<li><p>แบบไม่เข้ารหัส: เราจำเป็นต้องรันโหนดไว้ในเครื่องที่เราใช้รัน client แล้วแปลง client ให้ส่งข้อความใส่โหนดปลายทางตรง ๆ</p>
</li>
<li><p>แบบเข้ารหัส เริ่มจากการเชื่อมไปที่โหนดที่ต้องการ (อารมณ์ ssh เข้าไป) แต่วิธีนี้ยังติดที่บางเว็บที่เป็น https จะเชื่อมไม่ได้หาก cert ของโหนดที่เราเชื่อมมีปัญหา ส่วนข้อมูลที่ส่งจะถูก encrypt ไว้ด้วย Sphinx (เข้ารหัสแบบหนึ่งในมาตรฐาน RSA) ฉนั้นมั่นใจได้ว่าโหนดที่เราไปยืมจมูกเขาหายใจก็ไม่รู้ว่าเราส่งอะไรไป</p>
</li>
</ul>
<h2>การตรวจสอบตัวตนของเซิร์ฟเวอร์ปลายทาง</h2>
<p>การตรวจสอบตัวตนของเซิร์ฟเวอร์ปลายทางของ NWS ทำโดยการใช้ประโยชน์จาก nostr โดยให้โหนดปลายทางปล่อย cert (TLS) บน nostr และเซ็นรับรองด้วยตนเองและอณุญาติให้ไคลเอนต์สามารถดึงใบรับรองนี้มาก่อนเชื่อมต่อกับโหนดที่เป็นจุดเริ่มที่เราใช้</p>
<p>และนอกจากนี้ในอนาคตของการพัฒนา NWS กำลังพัฒนาให้ NWS สามารถกำหนดให้โหนดปลายทางสามารถกำหนดค่าให้เข้าถึงอินเทอร์เน็ตได้ (ไม่ใช่แค่บริการภายใน) ทำให้ NWS ทำงานคล้าย VPN โดยสามารถเข้าเว็บไซต์ปกติได้ เช่น พิมพ์ "https google dot com" ก็เข้า google ได้</p>
<p>และทั้งหมดที่กล่าวไว้ข้างต้นเป็นเพียงการเริ่มต้น ยังมีคุณสมบัติอย่างอื่นอีกมากมายที่จะเข้ามาปรับปรุงปัญหาต่าง ๆ ในการใช้งานอินเตอร์เน็ต โดยส่วนตัวผมรู้สึกตื่นเต้นกับอนาคตของ NWS มาก ๆ และคาดหวังที่จะได้เห็นคนหันมาให้ความสนใจและใช้งานมัน ทั้งนี้หากใครอยากทดลองใช้งานก็สามารถติดตามได้ที่ </p>
<p>   GitHub: <np-embed url="https://github.com/asmogo/nws"><a href="https://github.com/asmogo/nws">https://github.com/asmogo/nws</a></np-embed></p>
<h2>ส่งท้าย</h2>
<p>สำหรับ series nOStr ผมตั้งใจจะหาเรื่องราวเกี่ยวกับ other stuff ที่น่าสนใจ ให้ทุกคน ๆ ได้ศึกษาไปพร้อม ๆ กัน เพราะว่า nostr ไม่ได้มีแค่โน๊ต </p>
]]></itunes:summary>
      <itunes:image href="https://yakihonne.s3.ap-east-1.amazonaws.com/79008e781adec767cc8e239b533edcb19ea2e260f9281a9125e93425dfac9395/files/1726139805814-YAKIHONNES3.jpg"/>
      </item>
      
      </channel>
      </rss>
    