Giới thiệu về Bitcoin Script
Trang chủ
Bài viết
Giới thiệu về Bitcoin Script

Giới thiệu về Bitcoin Script

Nâng cao
Đã đăng Jul 10, 2020Đã cập nhật Jan 18, 2022
13m

Giới thiệu

Bitcoin đôi khi còn được gọi là đồng tiền có thể lập trình. Với bản chất là đồng tiền kỹ thuật số, Bitcoin cho phép người dùng linh hoạt đặt ra các điều kiện về cách chi tiêu tiền. 
Khi thảo luận về Bitcoin, chúng ta thường nhắc đến coin. Nhưng chúng ta cũng có thể coi ví là chìa khóa, coin là séc, còn blockchain là các két sắt nối tiếp nhau theo hàng. Mỗi két sắt đều có một khe nhỏ để bất kỳ ai cũng có thể nhét séc vào hoặc nhìn vào để xem giá trị trong két sắt là bao nhiêu. Tuy nhiên, chỉ người giữ chìa khóa mới có thể truy cập vào bên trong.

Khi người giữ chìa khóa muốn đưa tiền cho người khác, họ sẽ mở két sắt của mình. Họ viết một tấm séc mới tham chiếu tới tấm séc cũ (sau đó sẽ bị hủy), rồi cất séc mới vào một két sắt mà người nhận có thể mở. Để tiêu số tiền đó, người nhận mới sẽ lặp lại quy trình.

Trong bài viết này, chúng ta sẽ xem xét kỹ hơn về Script, ngôn ngữ lập trình được diễn giải bởi các node trên mạng Bitcoin. Script chính là thứ chi phối cơ chế khóa/mở khóa két sắt mà chúng tôi đề cập ở trên.


Bitcoin hoạt động như thế nào?

Theo cách tương tự, bạn có thể nói rằng có hai phần trong mỗi giao dịch là – chìa khóa (để mở két sắt) và ổ khóa. Bạn sử dụng chìa khóa để mở két có chứa séc bạn muốn gửi, sau đó bạn cất séc mới vào két mới có ổ khóa khác. Để tiêu tiền cất trong két mới, bạn cần có chìa khóa khác.
Rất đơn giản. Các loại ổ khóa trong hệ thống cũng có thể hơi khác một chút. Một số két sắt có thể yêu cầu bạn cung cấp nhiều chìa khóa, trong khi số khác yêu cầu bạn chứng minh bạn biết một bí mật. Mọi người có thể đặt ra một loạt điều kiện. 
Chìa khóa mà chúng ta sử dụng chính là scriptSig. Còn ổ khóa là scriptPubKey. Nếu xem xét hai thành phần này kỹ hơn một chút, chúng ta sẽ thấy thực tế, chúng được tạo từ các bit dữ liệu và các khối mã. Khi kết hợp lại với nhau, chúng sẽ tạo ra một chương trình nhỏ.

Khi bạn thực hiện một giao dịch, điều đó đồng nghĩa với việc bạn đang truyền tổ hợp đó tới mạng lưới. Mỗi node nhận được tổ hợp này sẽ kiểm tra chương trình để biết liệu giao dịch có hợp lệ hay không. Nếu không hợp lệ, giao dịch sẽ bị loại bỏ và bạn sẽ không thể sử dụng số tiền được khóa.

Séc (coin) mà bạn nắm giữ được gọi là đầu ra giao dịch chưa sử dụng (UTXO). Những ai cung cấp chìa khóa khớp với ổ khóa sẽ sử dụng được tiền. Cụ thể, chìa khóa là scriptSig, còn ổ khóa là scriptPubKey.
Nếu UTXO nằm trong ví của bạn, có thể điều kiện được đưa ra sẽ là chỉ người nào chứng minh được quyền sở hữu khóa công khai này mới có thể mở khóa các khoản tiền này. Để mở khóa, bạn cung cấp scriptSig bao gồm chữ ký số, sử dụng khóa riêng tư liên kết với khóa công khai được quy định trong scriptPubKey. Chúng tôi sẽ giải thích ơ chế này rõ ràng hơn trong phần sau.


Hiểu về ngăn xếp Bitcoin

Script được biết đến như là ngôn ngữ dựa trên ngăn xếp. Điều này nghĩa là khi đọc một tập hợp các chỉ thị, chúng ta đặt các chỉ thị này vào thứ gọi là cột dọc. Ví dụ, danh sách A, B, C sẽ tạo thành một ngăn xếp, trong đó A nằm dưới đáy ngăn xếp, còn C nằm trên đỉnh ngăn xếp. Khi chỉ thị yêu cầu chúng ta làm điều gì đó, chúng ta sẽ thực hiện trên một hoặc nhiều phần tử bắt đầu ở đỉnh ngăn xếp.


Các phần tử A, B và C được thêm vào và “lấy ra” khỏi ngăn xếp.


Chúng ta có thể phân biệt giữa dữ liệu (những thứ như chữ ký, hash và khóa công khai) và chỉ thị (hay mã opcode). Các chỉ thị xóa dữ liệu và làm gì đó với dữ liệu. Sau đây là một ví dụ rất đơn giản giúp bạn hình dung về script (tập lệnh):
<xyz> <md5 hasher> <d16fb36f0911f878998c136191af705e> <check if equal>
Màu đỏ là dữ liệu, còn màu xanh lam là mã opcode. Chúng ta đọc từ trái sang phải, vì vậy trước tiên chúng tôi sẽ đặt chuỗi <xyz> vào ngăn xếp. Tiếp theo là mã opcode <md5 hasher>. Script này không tồn tại trong Bitcoin, nhưng giả sử nó loại bỏ phần tử trên đỉnh ngăn xếp (<xyz>), rồi hash (băm) phần tử này bằng thuật toán MD5. Sau đó, đầu ra được thêm trở lại vào ngăn xếp. Đầu ra ở đây là d16fb36f0911f878998c136191af705e.
Thật trùng hợp! Phần tử tiếp theo chúng tôi thêm vào là <d16fb36f0911f878998c136191af705e>, nên giờ ngăn xếp có hai phần tử giống hệt nhau. Cuối cùng, <check if equal> lấy hai phần tử trên đỉnh ngăn xếp ra và kiểm tra xem hai phần tử này có bằng nhau không. Nếu bằng nhau, nó sẽ thêm <1> vào ngăn xếp. Nếu không bằng nhau, nó sẽ thêm <0>
Chúng ta đã đi đến cuối danh sách các chỉ thị. Script của chúng ta có thể bị lỗi theo hai cách – nếu phần tử còn lại là 0 hoặc nếu một trong các toán tử khiến script bị lỗi khi không đáp ứng một số điều kiện. Không có bất kỳ toán tử nào như vậy trong ví dụ này, nên chúng ta có phần tử không phải là 0 (<1>), vì vậy script của chúng ta hợp lệ. Các quy tắc này cũng áp dụng cho giao dịch Bitcoin trong thực tế.

Đây chỉ là một ví dụ về chương trình. Giờ hãy xem một số chương trình trong thực tế.


Pay-to-Pubkey (P2PK)

Pay-to-Pubkey (P2PK) cực kỳ đơn giản. Chương trình này liên quan đến việc khóa tiền vào một khóa công khai. Nếu bạn muốn nhận tiền theo cách này, bạn sẽ cung cấp khóa công khai của bạn cho người gửi, trái ngược với địa chỉ Bitcoin. 

Giao dịch đầu tiên giữa Satoshi Nakamoto và Hal Finney vào năm 2009 là giao dịch P2PK. Cấu trúc này được sử dụng nhiều trong những ngày đầu Bitcoin ra đời, nhưng ngày nay, Pay-to-Pubkey-Hash (P2PKH) phần lớn đã thay thế cấu trúc này. 
Script khóa của giao dịch P2PK tuân theo định dạng <public key> OP_CHECKSIG. Rất đơn giản. Có thể bạn sẽ đoán rằng OP_CHECKSIG kiểm tra chữ ký và khóa công khai được cung cấp xem có khớp nhau hay không. Do đó, scriptSig của chúng ta sẽ là một <signature> đơn giản. Hãy nhớ rằng scriptSig là chìa khóa để mở ổ khóa.



Về cơ bản là như vậy. Một chữ ký được thêm vào ngăn xếp, sau đó là khóa công khai. OP_CHECKSIG lấy cả chữ ký và khóa công khai ra, rồi xác minh xem chữ ký có khớp với khóa công khai không. Nếu chữ ký khớp với khóa công khai, nó sẽ thêm <1> vào ngăn xếp. Nếu ngược lại, nó sẽ thêm <0>.

Vì những lý do mà chúng tôi sẽ giải thích trong phần tiếp theo mà P2PK không còn được sử dụng nữa.


Pay-to-Pubkey-Hash (P2PKH)

Pay-to-Pubkey-Hash (P2PKH) hiện là loại giao dịch phổ biến nhất. Trừ khi bạn đang cố tải phần mềm lỗi thời xuống, còn không ví của bạn sẽ thực hiện loại giao dịch này theo mặc định.

scriptPubKey trong P2PKH sẽ như sau:

OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG

Trước khi tìm hiểu scriptSig, hãy phân tích những gì mà mã opcode mới sẽ làm:


OP_DUP

OP_DUP lấy phần tử đầu tiên ra, rồi sao chép phần tử này. Sau đó, nó thêm cả hai trở lại ngăn xếp. Thông thường, việc này sẽ được thực hiện sao cho chúng ta có thể thực hiện một thao tác trên bản sao mà không ảnh hưởng đến bản gốc.


OP_HASH160

Mã này sẽ lấy phần tử đầu tiên ra và hash nó hai lần. Vòng đầu tiên sẽ hash bằng thuật toán SHA-256. Đầu ra SHA-256 được băm bằng thuật toán RIPEMD-160. Sau đó, đầu ra này được thêm trở lại vào ngăn xếp.


OP_EQUALVERIFY

OP_EQUALVERIFY kết hợp hai toán tử khác là – OP_EQUAL và OP_VERIFY. OP_EQUAL lấy hai phần tử ra và kiểm tra xem chúng có giống nhau không. Nếu giống nhau, nó sẽ thêm 1 vào ngăn xếp. Nếu không giống nhau, nó sẽ thêm 0. OP_VERIFY lấy phần tử trên đỉnh ngăn xếp ra và kiểm tra xem nó có True không (tức là không phải là 0). Nếu không phải True, giao dịch sẽ không thành công. Kết hợp lại, OP_EQUALVERIFY sẽ làm cho giao dịch không thành công nếu hai phần tử trên đỉnh ngăn xếp không khớp nhau.

Lần này, scriptSig sẽ như thế này:

<signature> <public key>

Bạn sẽ cần cung cấp chữ ký và khóa công khai tương ứng để mở khóa đầu ra P2PKH.



Bạn có thể xem những gì diễn ra trong ảnh GIF ở trên. Nó không quá khác so với script P2PK. Chúng ta chỉ bổ sung thêm bước kiểm tra xem khóa công khai có khớp với hash trong script hay không.

Tuy nhiên, có một số điểm cần lưu ý. Trong script khóa P2PKH, khóa công khai không hiển thị – chúng ta chỉ có thể thấy hash của nó. Nếu truy cập trình khám phá blockchain và xem đầu ra P2PKH chưa được chi tiêu, chúng ta sẽ không biết được khóa công khai. Khóa công khai chỉ được tiết lộ khi người nhận quyết định chuyển tiền.
Điểm này có thể đem lại một số lợi ích. Đầu tiên là hash khóa công khai dễ chuyển hơn một khóa công khai đầy đủ. Satoshi đã ra mắt hash khóa công khai vào năm 2009 vì lý do này. Hash khóa công khai chính là địa chỉ Bitcoin ngày nay.
Lợi ích thứ hai là hash khóa công khai có thể tạo ra một lớp bảo mật bổ sung chống lại kỹ thuật tính toán lượng tử. Do chúng ta không thể biết khóa công khai cho đến khi tiêu tiền, nên những người khác thậm chí còn khó tính toán khóa riêng tư hơn. Họ sẽ phải đảo ngược hai vòng hash (RIPEMD-160 và SHA-256) để biết được khóa nàyu.



Pay-to-Script-Hash (P2SH)

Pay-to-Script-Hash (P2SH) là một bước phát triển thú vị đối với Bitcoin. P2SH cho phép người gửi khóa tiền vào hash của một script – họ không cần biết script thực sự làm gì. Chúng ta sẽ lấy hash SHA-256 sau làm ví dụ:

e145fe9ed5c23aa71fdb443de00c7d9b4a69f8a27a2e4fbb1fe1d0dbfb6583f1

Bạn không cần biết đầu vào của hash để khóa tiền vào hàm băm này. Tuy nhiên, người tiêu tiền cần cung cấp script đã được sử dụng để hash và cần đáp ứng các điều kiện của script đó.

Hash ở trên được tạo từ script sau:

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

Nếu bạn muốn tiêu số tiền liên kết với scriptPubKey đó, bạn không chỉ cung cấp các lệnh đó. Bạn cũng cần một scriptSig làm cho script đã hoàn thành được đánh giá là True. Trong ví dụ này, đó là một phần tử mà bạn <multiply by 2> để cho kết quả là <4>. Tất nhiên, điều đó nghĩa là scriptSig của chúng ta chỉ là <2>.

Trong thực tế, scriptPubKey cho đầu ra P2SH là:

OP_HASH160 <redeemScript hash> OP_EQUAL

Không có toán tử mới ở đây. Tuy nhiên, chúng ta có <redeemScript hash> là phần tử mới. Đúng như tên gọi, đó là hash của script mà chúng ta cần cung cấp để rút tiền (được gọi là redeemScript). scriptSig sẽ thay đổi tùy thuộc vào những gì có trong redeemScript. Tuy nhiên, nhìn chung, bạn sẽ thấy đó là sự kết hợp của chữ ký và khóa công khai đính kèm, theo sau là redeemScript (bắt buộc):

<signature> <public key> <redeemScript>

Đánh giá của chúng ta khác một chút so với việc thực thi ngăn xếp mà chúng ta đã thấy cho đến nay. Nó xảy ra theo hai phần. Phần đầu tiên là kiểm tra xem bạn đã cung cấp đúng hash hay chưa. 



Bạn sẽ nhận thấy chúng tôi không thực hiện bất kỳ điều gì với các phần tử đứng trước redeemScript. Chúng không được sử dụng tại thời điểm này. Chúng ta đã đi đến phần cuối của chương trình mini này và phần tử trên đỉnh ngăn xếp không phải là 0. Điều đó có nghĩa là script hợp lệ.

Tuy nhiên, mọi việc vẫn chưa xong. Các node mạng nhận ra cấu trúc này là P2SH, vì vậy thực tế chúng có các phần tử của scriptSig đang chờ trong một ngăn xếp khác. Đó là lúc chữ ký và khóa công khai sẽ được sử dụng.
Cho đến nay, chúng ta vẫn coi redeemScript là một phần tử. Nhưng bây giờ, redeemScript sẽ được hiểu là chỉ thị mà chỉ thị có thể là bất cứ thứ gì. Hãy cùng xem một ví dụ về  script khóa P2PKH, mà chúng ta phải cung cấp <signature><public key> khớp với <public key hash> bên trong <redeemScript>.



Khi redeemScript được mở rộng, bạn có thể thấy tình huống giống hệt như một giao dịch P2PKH thông thường. Từ đó, bạn chỉ cần chạy nó như bình thường.

Chúng tôi đã cho bạn thấy ví dụ về script P2SH (P2PKH), nhưng không chắc bạn sẽ tìm thấy script đó trong tự nhiên. Bạn có thể tạo script này, nhưng nó không mang lại cho bạn thêm lợi ích nào và kết quả là chiếm nhiều không gian hơn trong block (nên chi phí cao hơn).

P2SH thường có ích cho những thứ như giao dịch tương thích với đa chữ ký hoặc SegWit. Giao dịch đa chữ ký có thể có kích thước rất lớn do yêu cầu nhiều khóa. Trước khi triển khai Pay-to-Script-Hash, người gửi sẽ phải liệt kê tất cả các khóa công khai có thể có trong script khóa của mình. 

Nhưng với P2SH, điều kiện chi tiêu phức tạp đến đâu không quan trọng. Hash của redeemScript’s luôn có kích thước cố định. Do đó, (những) người dùng muốn mở khóa script khóa sẽ phải chịu chi phí phát sinh.

Khả năng tương thích với SegWit là một trường hợp khác mà P2SH có ích (chúng ta sẽ đi sâu vào điểm khác biệt về cấu trúc giao dịch trong phần tiếp theo). SegWit là một soft fork dẫn đến sự thay đổi về định dạng block/giao dịch. Do đây là bản nâng cấp chọn tham gia, nên không phải tất cả phần mềm ví đều công nhận sự thay đổi này.

Đó không phải là vấn đề nếu khách hàng bọc script hash SegWit trong P2SH. Giống tất cả các giao dịch kiểu này, khách hàng không cần biết redeemScript mở khóa sẽ như thế nào. 


Giao dịch SegWit (P2WPKH và P2WSH)

Để xem phần giới thiệu toàn diện hơn về SegWit, hãy đọc bài viết Giới thiệu về Segregated Witness dành cho người mới bắt đầu.
Để hiểu được định dạng giao dịch trong SegWit, bạn chỉ cần biết rằng chúng ta không chỉ có scriptSig và scriptPubKey nữa. Mà chúng ta sẽ có một yếu tố mới tên là bằng chứng. Dữ liệu mà chúng ta sử dụng để giữ trong scriptSig được chuyển đến nhân chứng, vì vậy scriptSig sẽ trống.

Nếu bạn gặp các địa chỉ bắt đầu bằng "bc1", đó chính là SegWit gốc (trái ngược với tương thích với SegWit, bắt đầu bằng "3" do chúng là địa chỉ P2SH).


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

Pay-to-Witness-Pubkey-Hash (P2WPKH) là P2PKH phiên bản SegWit. Bằng chứng sẽ trông như thế này:

<signature> <public key>

Bạn sẽ thấy nó giống với scriptSig của P2PKH. Ở đây, scriptSig trống. Trong khi đó, scriptPubKey sẽ như sau:

<OP_0> <public key hash>

Trông có vẻ hơi lạ phải không? Mã opcode ở đâu để cho chúng ta so sánh chữ ký, khóa công khai và hash?

Chúng tôi sẽ không hiển thị các toán tử bổ sung ở đây, do các node nhận được giao dịch sẽ biết phải làm gì với giao dịch dựa trên độ dài của <public key hash>. Các node sẽ tính toán độ dài và hiểu rằng giao dịch phải được chạy theo cùng một kiểu như giao dịch P2PKH cũ.
Những node chưa nâng cấp sẽ không biết cách diễn giải giao dịch theo cách đó, nhưng điều đó không thành vấn đề. Theo quy tắc cũ, không có bằng chứng, vì vậy các node sẽ đọc một scriptSig trống và một số dữ liệu. Các node đánh giá script này và đánh dấu là hợp lệ – theo như những gì mình biết, bất kỳ ai cũng có thể chi tiêu đầu ra. Đây là lý do tại sao SegWit được coi là soft fork tương thích ngược.


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

Pay-to-Witness-Script Hash (P2WSH) là P2SH mới. Nếu đã đi đến bước này, có thể bạn sẽ biết script này trông như thế nào, nhưng chúng tôi cũng sẽ giải thích thêm. Bằng chứng là những gì chúng tôi thường đưa vào scriptSig. Ví dụ: trong một P2WSH bọc một giao dịch P2PKH, nó có thể trông giống như bên dưới:

<signature 1> <public key>

Còn đây là scriptPubKey của chúng tôi:

<OP_0> <script hash>

Các quy tắc là tương tự nhau. Các node SegWit đọc độ dài của script hash và xác định đó là đầu ra P2WSH, được đánh giá tương tự như P2SH. Trong khi đó, node cũ chỉ xem nó như một đầu ra mà ai cũng có thể chi tiêu.


Tổng kết

Trong bài viết này, chúng ta đã tìm hiểu một chút về các yếu tố nền tảng của Bitcoin. Hãy cùng xem thông tin tóm tắt bên dưới:


Loại scriptMô tả

Pay-to-Pubkey (P2PK)

Khóa tiền vào một khóa công khai cụ thể

Pay-to-Pubkey-Hash (P2PKH)

Khóa tiền vào một hash khóa công khai cụ thể (tức là một địa chỉ)

Pay-to-Script-Hash (P2SH)

Khóa tiền vào hash của một script mà người nhận có thể cung cấp

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

P2PK phiên bản SegWit

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

P2SH phiên bản SegWit


Khi tìm hiểu sâu hơn về Bitcoin, bạn bắt đầu hiểu tại sao đồng tiền này lại có nhiều tiềm năng như vậy. Giao dịch có thể được tạo thành từ nhiều thành phần khác nhau. Bằng cách sửa đổi các thành phần này, người dùng có thể linh hoạt đặt ra các điều kiện về cách thức và thời điểm chi tiêu tiền.