Cơ chế của Bitcoin 2: Bitcoin scripts

3.2. Bitcoin scripts

Mỗi đầu ra giao dịch không chỉ xác định một khóa công khai. Nó thực sự chỉ định một tập lệnh. Script là gì và tại sao chúng ta sử dụng chúng? Trong phần này, chúng ta nghiên cứu ngôn ngữ kịch bản Bitcoin và tìm hiểu lý do tại sao một tập lệnh được sử dụng thay vì chỉ chỉ định một khóa công khai.

Loại giao dịch phổ biến nhất trong Bitcoin là mua lại kết quả giao dịch trước đó bằng cách ký bằng khóa chính xác. Trong trường hợp này, chúng ta muốn kết quả giao dịch xác định, “điều này có thể được đổi bằng chữ ký của chủ sở hữu địa chỉ X.” Nhớ lại rằng địa chỉ là một hàm băm của khóa công khai. Vì vậy, việc chỉ định địa chỉ X không cho chúng ta biết khóa công khai là gì và nó không cung cấp cho chúng ta cách kiểm tra chữ ký! Vì vậy, thay vào đó, đầu ra giao dịch phải nêu rõ: “điều này có thể được đổi bằng khóa công khai băm thành X, cùng với chữ ký từ chủ sở hữu của khóa công khai đó”. Như chúng ta sẽ thấy, đây chính xác là loại tập lệnh phổ biến nhất trong Bitcoin chỉ định (Hình 3.4).

Nhưng điều gì xảy ra với kịch bản này? Ai điều hành nó, và chính xác thì trình tự chỉ thị này thực thi câu lệnh trên như thế nào? Bí mật là các đầu vào cũng chứa script thay vì chữ ký. Để xác nhận rằng một giao dịch sử dụng chính xác đầu ra của giao dịch trước đó, chúng ta kết hợp tập lệnh đầu vào của giao dịch mới và tập lệnh đầu ra của giao dịch trước đó. Chúng ta chỉ cần nối chúng và tập lệnh kết quả phải chạy thành công để giao dịch hợp lệ. Hai tập lệnh này là scriptPubKeyscriptSig, vì trong trường hợp đơn giản nhất, tập lệnh đầu ra chỉ xác định khóa công khai (hoặc địa chỉ mà khóa công khai băm) và tập lệnh đầu vào chỉ định chữ ký với khóa công khai đó. Tập lệnh kết hợp có thể được nhìn thấy trong Hình 3.5.

Ngôn ngữ kịch bản Bitcoin

Ngôn ngữ kịch bản được xây dựng đặc biệt cho Bitcoin và chỉ được gọi là “Script” hoặc “ngôn ngữ kịch bản Bitcoin”. Nó có nhiều điểm tương đồng với ngôn ngữ gọi là “Forth”, là một ngôn ngữ lập trình cũ, đơn giản, dựa trên ngăn xếp. Nhưng bạn không cần phải hiểu Forth để hiểu kịch bản Bitcoin. Các mục tiêu thiết kế chính cho Script là có một thứ gì đó đơn giản và nhỏ gọn, nhưng có hỗ trợ riêng cho các hoạt động mật mã. Vì vậy, chẳng hạn, có các chỉ thị có mục đích đặc biệt để tính toán các hàm băm và tính toán và xác minh chữ ký.

HÌNH 3.4. Ví dụ về tập lệnh Pay-to-PubkeyHash.

HÌNH 3.4. Ví dụ về tập lệnh Pay-to-PubkeyHash, loại tập lệnh đầu ra phổ biến nhất trong Bitcoin.

HÌNH 3.5. Kết hợp scriptPubKey và scriptSig.

HÌNH 3.5. Kết hợp scriptPubKey và scriptSig. Để kiểm tra xem một giao dịch có quy đổi đầu ra một cách chính xác hay không, chúng ta tạo một tập lệnh kết hợp bằng cách nối scriptPubKey của giao dịch đầu ra được tham chiếu (dưới cùng) vào scriptSig của giao dịch quy đổi (trên cùng). Lưu ý rằng <pubKeyHash?> Chứa dấu “?.” Chúng ta sử dụng ký hiệu này để chỉ ra rằng sau này chúng ta sẽ kiểm tra để xác nhận rằng điều này bằng với mã băm của khóa công khai được cung cấp trong tập lệnh quy đổi.

Ngôn ngữ kịch bản dựa trên ngăn xếp. Điều này có nghĩa là mọi lệnh được thực thi chính xác một lần, theo cách tuyến tính. Đặc biệt, không có vòng lặp nào trong ngôn ngữ kịch bản Bitcoin. Vì vậy, số lượng chỉ thị trong tập lệnh cung cấp cho chúng ta giới hạn trên về thời gian có thể chạy và dung lượng bộ nhớ mà nó có thể sử dụng. Ngôn ngữ này không phải là ngôn ngữ Turing hoàn chỉnh, có nghĩa là nó không có khả năng tính toán các chức năng mạnh mẽ tùy ý. Và đây là do thiết kế—những người khai thác phải chạy các tập lệnh này, được gửi bởi những người tham gia tùy ý trong mạng. Họ không được phép gửi một tập lệnh có thể có vòng lặp vô hạn.

Chỉ có hai kết quả có thể xảy ra khi một tập lệnh Bitcoin được thực thi. Nó thực hiện thành công mà không có lỗi, trong trường hợp đó giao dịch hợp lệ. Hoặc, nếu có bất kỳ lỗi nào trong khi tập lệnh đang thực thi, toàn bộ giao dịch sẽ không hợp lệ và không được chấp nhận vào chuỗi khối.

Ngôn ngữ kịch bản Bitcoin rất nhỏ. Chỉ có chỗ cho 256 lệnh, vì mỗi lệnh được biểu thị bằng một byte. Trong số 256 lệnh đó, 15 lệnh hiện đang bị vô hiệu hóa và 75 lệnh được bảo lưu. Các mã chỉ thị dành riêng vẫn chưa được gán bất kỳ ý nghĩa cụ thể nào, nhưng có thể được sử dụng cho các chỉ thị được thêm vào sau này.

Nhiều chỉ thị cơ bản là những chỉ thị bạn mong đợi bằng bất kỳ ngôn ngữ lập trình nào. Có số học cơ bản, logic cơ bản (ví dụ: câu lệnh “if” và “then”), ném lỗi, không ném lỗi và trả về sớm. Cuối cùng, có các chỉ thị về tiền điện tử, bao gồm các hàm băm, chỉ thị xác minh chữ ký, cũng như một chỉ thị đặc biệt và quan trọng được gọi là CHECKMULTISIG cho phép bạn kiểm tra nhiều chữ ký bằng một lệnh. Bảng 3.1 liệt kê một số chỉ thị phổ biến nhất trong ngôn ngữ kịch bản Bitcoin.

Lệnh CHECKMULTISIG yêu cầu chỉ định n khóa công khai và tham số t cho một ngưỡng. Để lệnh này thực thi thành công, ít nhất t chữ ký từ t trong số n khóa công khai đó phải có mặt và hợp lệ. Chúng ta sẽ đưa ra một số ví dụ về việc sử dụng đa chữ ký (multisignatures) trong Phần 3.3, nhưng cần phải rõ ràng rằng đây là một nguyên thủy khá mạnh mẽ. Chúng ta có thể sử dụng nó để diễn đạt một cách ngắn gọn khái niệm rằng t trong số n thực thể được chỉ định phải ký để giao dịch có hiệu lực.

BẢNG 3.1. CÁC CHỈ THỊ KỊCH BẢN PHỔ BIẾN VÀ CHỨC NĂNG CỦA CHÚNG

TênChức năng
OP_DUPNhân đôi mục hàng đầu trên ngăn xếp.
OP_HASH160Hàm băm hai lần: đầu tiên sử dụng SHA-256 và sau đó là một hàm băm khác có tên RIPEMD-160.
OP_EQUALVERIFYTrả về true nếu các đầu vào bằng nhau. Trả về false và đánh dấu giao dịch là không hợp lệ nếu chúng không bằng nhau.
OP_CHECKSIGKiểm tra xem chữ ký đầu vào có hợp lệ hay không bằng cách sử dụng khóa công khai đầu vào cho băm của giao dịch hiện tại.
OP_CHECKMULTISIGKiểm tra xem t chữ ký trên giao dịch có phải là chữ ký hợp lệ từ t trong số các khóa công khai được chỉ định hay không.
TABLE 3.1. COMMON SCRIPT INSTRUCTIONS AND THEIR FUNCTION

Ngẫu nhiên, có một lỗi trong việc triển khai đa chữ ký. Lệnh CHECKMULTISIG bật một giá trị dữ liệu bổ sung ra khỏi ngăn xếp và bỏ qua nó. Đây chỉ là một đặc điểm kỳ quặc của ngôn ngữ Bitcoin và người ta phải giải quyết nó bằng cách đặt thêm một biến giả vào ngăn xếp. Lỗi này nằm trong bản triển khai ban đầu và chi phí sửa chữa nó cao hơn nhiều so với thiệt hại mà nó gây ra, như chúng ta đã thảo luận trong Phần 3.6. Tại thời điểm này, lỗi này được coi là một tính năng của Bitcoin, ở chỗ nó sẽ không biến mất.

Thực thi một tập lệnh

Để thực thi một tập lệnh bằng ngôn ngữ lập trình dựa trên ngăn xếp, tất cả những gì chúng ta cần là một ngăn xếp mà chúng ta có thể đẩy dữ liệu đến và bật dữ liệu từ đó. Chúng ta sẽ không cần bất kỳ bộ nhớ hoặc biến nào khác. Đó là điều làm cho ngôn ngữ trở nên đơn giản về mặt tính toán. Có hai loại chỉ thị: chỉ thị dữ liệu và mã tác vụ (opcodes). Khi một lệnh dữ liệu xuất hiện trong một tập lệnh, dữ liệu đó chỉ được đẩy lên trên cùng của ngăn xếp. Ngược lại, các mã opcodes thực hiện một số chức năng, thường lấy làm dữ liệu đầu vào trên đầu ngăn xếp.

Bây giờ chúng ta hãy xem tập lệnh Bitcoin trong Hình 3.5 được thực thi như thế nào. Tham khảo Hình 3.6, cho thấy trạng thái của ngăn xếp sau mỗi lệnh. Hai chỉ thị đầu tiên trong tập lệnh này là chỉ thị dữ liệu—chữ ký và khóa công khai được sử dụng để xác minh chữ ký đó—được chỉ định trong thành phần scriptSig của đầu vào giao dịch trong giao dịch mua lại. Như đã đề cập, một lệnh dữ liệu chỉ được đẩy lên ngăn xếp. Phần còn lại của tập lệnh đã được chỉ định trong thành phần scriptPubKey của đầu ra giao dịch trong giao dịch được tham chiếu.

Đầu tiên, chúng ta có lệnh sao chép, OP_DUP, vì vậy chúng ta chỉ cần đẩy một bản sao của khóa công khai lên trên cùng của ngăn xếp. Chỉ thị tiếp theo là OP_HASH160, lệnh này cho chúng ta bật giá trị của ngăn xếp trên cùng, tính toán hàm băm mật mã của nó và đẩy kết quả lên trên cùng của ngăn xếp. Khi lệnh này kết thúc thực thi, chúng ta sẽ thay thế khóa công khai trên đầu ngăn xếp bằng mã băm của nó.

HÌNH 3.6. Thực thi một tập lệnh Bitcoin.

HÌNH 3.6. Thực thi một tập lệnh Bitcoin. Ở phía dưới, chúng ta hiển thị chỉ thị trong tập lệnh. Chỉ thị dữ liệu được biểu thị bằng dấu ngoặc nhọn xung quanh, trong khi mã tác vụ bắt đầu bằng “OP_.” Ở trên cùng, chúng ta hiển thị ngăn xếp ngay sau khi lệnh được liệt kê dưới nó đã được thực thi.

Tiếp theo, chúng ta thực hiện thêm một lần đẩy dữ liệu vào ngăn xếp. Nhớ lại rằng dữ liệu này được chỉ định bởi người gửi của giao dịch được tham chiếu. Nó là băm của một khóa công khai mà người gửi đã chỉ định; khóa cá nhân tương ứng phải được sử dụng để tạo chữ ký để đổi những đồng tiền này. Tại thời điểm này, hai giá trị ở trên cùng của ngăn xếp: băm của khóa công khai (theo chỉ định của người gửi) và băm của khóa công khai đã được người nhận sử dụng khi cố gắng xác nhận tiền.

Tại thời điểm này, lệnh EQUALVERIFY thực thi, lệnh này sẽ kiểm tra xem hai giá trị ở trên cùng của ngăn xếp có bằng nhau không. Nếu không, một lỗi sẽ xảy ra và tập lệnh ngừng thực thi. Nhưng trong ví dụ của chúng ta, chúng ta sẽ giả định rằng chúng bằng nhau; nghĩa là người nhận tiền đã sử dụng đúng khóa công khai. Lệnh đó sẽ sử dụng hai mục dữ liệu đó ở trên cùng của ngăn xếp và ngăn xếp bây giờ chứa hai mục—một chữ ký và khóa công khai.

Chúng ta đã kiểm tra xem khóa công khai này trên thực tế có phải là khóa công khai mà giao dịch được tham chiếu chỉ định hay không và bây giờ chúng ta phải kiểm tra xem chữ ký có hợp lệ hay không. Đây là một ví dụ tuyệt vời về cách ngôn ngữ kịch bản Bitcoin được xây dựng dựa trên mật mã. Mặc dù nó là một ngôn ngữ khá đơn giản về mặt logic, nó có một số chỉ thị khá mạnh mẽ, chẳng hạn như OP_CHECKSIG. Lệnh đơn này vừa bật hai giá trị đó ra khỏi ngăn xếp vừa thực hiện xác minh toàn bộ chữ ký.

Nhưng đây là chữ ký của cái gì? Đầu vào cho hàm chữ ký là gì? Hóa ra bạn chỉ có thể ký một thứ bằng Bitcoin—toàn bộ một giao dịch. Vì vậy, lệnh CHECKSIG bật hai giá trị (khóa công khai và chữ ký) ra khỏi ngăn xếp và xác minh rằng chữ ký đó hợp lệ cho toàn bộ giao dịch bằng cách sử dụng khóa công khai đó. Bây giờ chúng ta đã thực hiện mọi lệnh trong script và không còn gì trên ngăn xếp. Miễn là không có lỗi nào xảy ra, đầu ra của tập lệnh này sẽ đơn giản là true, cho biết rằng giao dịch hợp lệ.

Những gì được sử dụng trong thực tế

Về lý thuyết, Script cho phép chúng ta chỉ định, theo một nghĩa nào đó, các điều kiện tùy ý phải được đáp ứng để tiêu tiền xu. Nhưng tính đến năm 2015, tính linh hoạt này không được sử dụng nhiều. Nếu chúng ta nhìn vào các tập lệnh đã thực sự được sử dụng trong lịch sử của Bitcoin, thì gần như tất cả đều giống với tập lệnh được sử dụng trong ví dụ của chúng ta. Tập lệnh này chỉ chỉ định một khóa công khai và yêu cầu chữ ký cho khóa công khai đó để sử dụng tiền.

Một vài chỉ thị khác cũng được sử dụng. MULTISIG được sử dụng một chút, cũng như một loại tập lệnh đặc biệt, Pay-to-Script-Hash, chúng ta sẽ thảo luận ngay sau đây. Nhưng ngoài ra, không có nhiều sự đa dạng trong các tập lệnh được sử dụng. Điều này là do các nút Bitcoin, theo mặc định, có một danh sách trắng gồm các tập lệnh tiêu chuẩn và chúng từ chối chấp nhận các tập lệnh không có trong danh sách. Điều này không có nghĩa là những tập lệnh đó hoàn toàn không thể được sử dụng; nó chỉ làm cho chúng khó sử dụng hơn. Trên thực tế, sự khác biệt này là một điểm tinh tế, mà chúng ta quay lại khi thảo luận về mạng ngang hàng Bitcoin.

Bằng chứng đốt (Proof of Burn)

Bằng chứng đốt là một kịch bản không bao giờ có thể đổi được. Việc gửi các đồng xu tới một tập lệnh bằng chứng đốt xác nhận rằng chúng đã bị phá hủy, vì không có cách nào để tiêu chúng. Một cách sử dụng bằng chứng đốt là khởi động một giải pháp thay thế cho Bitcoin bằng cách buộc mọi người phá hủy bitcoin để kiếm được tiền trong hệ thống mới. Chúng ta thảo luận chi tiết hơn về việc sử dụng này trong Chương 10. Bằng chứng đốt khá đơn giản để hiện thực: opcode OP_RETURN thông báo lỗi nếu nó đã từng đạt đến. Bất kể bạn đặt giá trị nào trước OP_RETURN, chỉ thị đó cuối cùng sẽ được thực thi, trong trường hợp đó, tập lệnh này sẽ trả về false.

Vì lỗi được tạo ra, dữ liệu trong tập lệnh đến sau OP_RETURN sẽ không được xử lý. Vì vậy, đây là cơ hội để người dùng đưa dữ liệu tùy ý vào một tập lệnh và do đó vào chuỗi khối. Nếu vì lý do nào đó, bạn muốn viết tên của mình hoặc nếu bạn muốn đánh dấu thời gian và chứng minh rằng bạn biết một số dữ liệu tại một thời điểm cụ thể, bạn có thể tạo một giao dịch Bitcoin giá trị thấp. Bạn có thể phá hủy một lượng rất nhỏ tiền tệ, nhưng sau đó bạn có thể viết bất cứ thứ gì bạn muốn vào chuỗi khối, chuỗi khối này sẽ được giữ lại cho vòng đời của hệ thống Bitcoin.

Pay-to-Script-Hash

Một hệ quả của cách thức hoạt động của tập lệnh Bitcoin là người gửi tiền xu phải chỉ định chính xác tập lệnh. Nhưng đây đôi khi có thể là một cách làm khá kỳ lạ. Ví dụ, giả sử rằng bạn đang mua sắm trực tuyến, và bạn chuẩn bị đặt một thứ gì đó và sẵn sàng thanh toán. Bạn yêu cầu địa chỉ mà tiền của bạn sẽ được gửi đến. Bây giờ, giả sử rằng công ty bạn đang đặt hàng đang sử dụng địa chỉ MULTISIG. Sau đó, vì người tiêu tiền phải chỉ định điều này, nhà bán lẻ nói với bạn, “Ồ, chúng ta đang làm một cái gì đó thú vị. Chúng ta đang sử dụng MULTISIG. Gửi các đồng tiền đến một số tập lệnh phức tạp. ” Bạn có thể nói, “Tôi không biết làm thế nào để làm điều đó. Nó quá phức tạp. Là một người tiêu dùng, tôi chỉ muốn gửi đến một địa chỉ đơn giản ”.

Bitcoin có một giải pháp thông minh cho vấn đề này và nó không chỉ áp dụng cho các địa chỉ có nhiều chữ ký mà còn cho bất kỳ điều kiện phức tạp nào quy định thời điểm có thể sử dụng tiền xu. Thay vì nói với người gửi “gửi tiền của bạn đến băm của khóa công khai này”, thay vào đó, người nhận có thể nói với người gửi “gửi tiền của bạn đến băm của tập lệnh này. Đặt ra điều kiện là để đổi những đồng xu đó, cần phải tiết lộ đoạn mã có băm đã cho và hơn nữa, cung cấp dữ liệu sẽ khiến tập lệnh được đánh giá là đúng. ” Người gửi đạt được điều này bằng cách sử dụng kiểu giao dịch Pay-to-Script-Hash (P2SH), có ngữ nghĩa như trên.

Cụ thể, tập lệnh P2SH chỉ cần băm giá trị trên cùng trên ngăn xếp, kiểm tra xem nó có khớp với giá trị băm được cung cấp hay không, sau đó thực hiện bước xác thực thứ hai đặc biệt: giá trị dữ liệu hàng đầu từ ngăn xếp được diễn giải lại thành một chuỗi chỉ thị và được thực thi. lần thứ hai dưới dạng tập lệnh, với phần còn lại của ngăn xếp làm đầu vào.

Nhận hỗ trợ cho P2SH khá phức tạp, vì nó không phải là một phần của đặc điểm thiết kế ban đầu của Bitcoin. Nó đã được thêm vào sau khi thực tế. Đây có lẽ là tính năng đáng chú ý nhất đã được thêm vào Bitcoin sau đặc tả ban đầu của nó. Và nó giải quyết một số vấn đề quan trọng. Nó loại bỏ nhu cầu phản hồi phức tạp từ người gửi, vì người nhận chỉ có thể chỉ định một hàm băm mà người gửi gửi tiền đến. Trong ví dụ của chúng ta ở trên, Alice không cần lo lắng rằng Bob đang sử dụng MULTISIG; cô ấy chỉ gửi đến địa chỉ P2SH của Bob và Bob có trách nhiệm chỉ định tập lệnh ưa thích khi anh ấy muốn đổi các đồng xu.

P2SH cũng có mức tăng hiệu quả tốt. Những người khai thác phải theo dõi tập hợp các tập lệnh đầu ra chưa được mua lại và với đầu ra P2SH, các tập lệnh đầu ra giờ đây nhỏ hơn nhiều, vì chúng chỉ chỉ định một hàm băm. Tất cả sự phức tạp được đẩy lên các tập lệnh đầu vào.

Leave a comment

Your email address will not be published.