Skip to main content

Bài 13: Sử dụng Marlowe từ dòng lệnh GHCI

Hướng dẫn này chỉ cho bạn cách sử dụng Marlowe từ bên trong Haskell và đặc biệt cho thấy cách thực hiện hợp đồng bằng cách sử dụng ngữ nghĩa đã cho trước đó.

Marlowe ở Haskell

Hướng dẫn này hoạt động trong phiên bản 3.0 của Marlowe có thể được tìm thấy trong masternhánh của kho lưu trữ:

git clone https://github.com/input-output-hk/marlowe.git
cd marlowe

Xem lại các hợp đồng

Như chúng ta đã thấy trước đó, ngữ nghĩa của một giao dịch đơn lẻ được xác định bởi hàm

computeTransaction :: TransactionInput -> State -> Contract -> TransactionOutput

trong đó các loại được định nghĩa như thế này:

data TransactionInput = TransactionInput
{ txInterval :: SlotInterval
, txInputs :: [Input] }

data TransactionOutput = TransactionOutput
{ txOutWarnings :: [ReduceWarning]
, txOutPayments :: [Payment]
, txOutState :: State
, txOutContract :: Contract }
| Error TransactionError

States được định nghĩa như thế này, với một hàm trợ giúp để xác định trạng thái trống ban đầu:

data State = State { accounts :: Map Party Money
, choices :: Map ChoiceId ChosenNum
, boundValues :: Map ValueId Integer
, minSlot :: Slot }

emptyState :: Slot -> State
emptyState sn = State { accounts = Map.empty
, choices = Map.empty
, boundValues = Map.empty
, minSlot = sn }

chúng ta có thể sử dụng các phương tiện GHCI để thực hiện từng giao dịch trong hợp đồng và tại đây, chúng ta sẽ thực hiện điều đó với hợp đồng ký quỹ được nhúng có trong EscrowSimmpleV2.hs< https://github.com/input-output-hk/marlowe/blob /master/semantics-3.0/src/Language/Marlowe/Examples/EscrowSimpleV2.hs > _.

Đối với một bước duy nhất, bạn có thể làm việc GHCI như thế này, sử dụng cơ sở để tạo các ràng buộc cục bộ:

Prelude> :set -XOverloadedStrings
Prelude> :l Language/Marlowe/Examples/EscrowSimpleV2.hs
...
\*Lang...V2> let (TransactionOutput txWarn1 txPay1 state1 con1) = computeTransaction (TransactionInput (0, 0) [IDeposit "alice" "alice" ada 450]) (emptyState 0) contract

Khi làm điều này, chúng ta có mẫu phù hợp với đầu ra của một ứng dụng computeTransaction, trong đó có ba đầu vào: đầu vào thứ hai là trạng thái ban đầu (tại slot số 0) và thứ ba là hợp đồng ký quỹ ban đầu. Đầu tiên là một TransactionInput trong đó có một SlotInterval - tại đây SlotInterval 0 0- và một khoản tiền gửi 450 Lovelace từ "alice" vào tài khoản của cô "alice" ấy cụ thể là IDeposit "alice" "alice" ada 450

Ghi chú

Nếu bạn muốn thử điều này cho chính mình trong GHCI, bạn có thể sao chép và dán từ các ví dụ mã: chúng nằm trong cửa sổ cuộn theo chiều ngang.

Đầu ra được so khớp với TransactionOutput txWarn1 txPay1 state1 con1 để chúng ta có thể kiểm tra các thành phần khác nhau một cách riêng biệt:

\*Lang...V2> txWarn1
[]
\*Lang...V2> txPay1
[]
\*Lang...V2> state1
State {accounts = fromList [("alice", ada), 450)], choices = fromList [], boundValues = fromList [], minSlot = 0}
\*Lang...V2> con1
When [Case (Choice (ChoiceId "choice" "alice") [Bound 0 1])
...

Điều này cho thấy rằng giao dịch không tạo ra cảnh báo hoặc thanh toán, nhưng cập nhật trạng thái để hiển thị số dư trong tài khoản "alice" và cập nhật hợp đồng, sẵn sàng nhận được lựa chọn từ Alice hoặc Bob.

Ở trạng thái tiếp theo, hợp đồng đang chờ đầu vào và nếu cả Alice và Bob đồng ý thanh toán cho Bob bằng cách chọn 0, thì một khoản thanh toán cho Bob sẽ được tạo. Điều này được xác minh thông qua tương tác này trong GHCI:

\*Lang...V2> let (TransactionOutput txWarn2 txPay2 state2 con2) = computeTransaction (TransactionInput (SlotInterval 0 0) [IChoice (ChoiceId "choice" "alice") 0, IChoice (ChoiceId "choice" "bob") 0]) state1 con1
\*Lang...V2> txPay2
[Payment "bob" ada 450]
\*Lang...V2> con2
Close
\*Lang...V2> state2
State {accounts = fromList [], choices = fromList [(ChoiceId "choice" "alice",0),(ChoiceId "choice" "bob",0)], boundValues = fromList [], minSlot = 0}

Một cách khác để làm điều này là thêm các định nghĩa này vào một tệp đang làm việc, ví dụ Build.hs, nơi các định nghĩa này sẽ được giữ nguyên. Thật vậy, sẽ rất hợp lý nếu đưa một số định nghĩa được sử dụng ở trên vào một tệp như vậy.

Các cách thay thế khác duyệt qua hợp đồng

Các liên kết cục bộ bị mất mỗi khi một :load hoặc :l lệnh được thực hiện, do đó, làm điều đó cho phép chúng ta sử dụng lại một số lệnh trước đó. Một cách thực hiện hợp đồng thay thế được đưa ra bởi

  • Bước đầu tiên: Alice gửi tiền như trong ví dụ trước đó.
  • Bước thứ hai: Alice và Bob chọn các tùy chọn khác nhau. Điều này có thể được thực hiện như thế này:
\*Lang...V2> let (TransactionOutput txWarn2 txPay2 state2 con2) = computeTransaction (TransactionInput (SlotInterval 0 0) [IChoice (ChoiceId "choice" "alice") 0, IChoice (ChoiceId "choice" "bob") 1]) state1 con1
\*Lang...V2> con2
When [Case (Choice (ChoiceId "choice" "carol") [Bound 1 1]) Close, Case (Choice (ChoiceId "choice" "carol") [Bound 0 0]) (Pay "alice" (Party "bob") ada (Constant 450) Close)] 100 Close
\*Lang...V2> state2
State {accounts = fromList [("alice", ada), 450)], choices = fromList [(ChoiceId "choice" "alice",0),(ChoiceId "choice" "bob",1)], boundValues = fromList [], minSlot = 0}

Điều này cho thấy rằng chúng ta hiện đang ở trong một hợp đồng mà sự lựa chọn là tùy thuộc vào Carol và rằng vẫn còn 450 Lovelace trong "alice" tài khoản.

  • Bước thứ ba: Carol đưa ra lựa chọn. Nếu cô ấy chọn 0, khoản thanh toán cho Bob sẽ được thực hiện. Nếu cô ấy chọn 1, Alice sẽ được hoàn lại tiền. Hãy làm điều đó ngay bây giờ:
\*Lang...V2> let (TransactionOutput txWarn3 txPay3 state3 con3) = computeTransaction  (TransactionInput (SlotInterval 0 0) [IChoice (ChoiceId "choice" "carol") 1]) state2 con2
\*Lang...V2> txPay3
[Payment "alice" ada 450]
\*Lang...V2> con3
Close
\*Lang...V2> state3
State {accounts = fromList [], choices = fromList [(ChoiceId "choice" "alice",0), (ChoiceId "choice" "bob",1),(ChoiceId "choice" "carol",1)], boundValues = fromList [], minSlot = 0}

Vì vậy, bây giờ hợp đồng đã sẵn sàng Close, và do đó, để hoàn trả bất kỳ số tiền còn lại nào, nhưng rõ ràng state3 là không có tài khoản nào chứa số dư khác 0, và do đó, hợp đồng bị chấm dứt.

Tại sao một bước lại hữu ích? Nó tương đương với gỡ lỗi và chúng ta có thể thấy trạng thái bên trong của hợp đồng ở mỗi giai đoạn, sự tiếp tục của hợp đồng, tức là những gì còn lại sẽ được thực hiện và các thực thi được tạo ra ở mỗi bước.

Bài tập

Khám phá một số cách khác để tham gia vào hợp đồng - Điều gì sẽ xảy ra khi Bob và Alice chọn hoàn lại tiền cho Alice? - Điều gì xảy ra nếu Bob và Alice không đồng ý, nhưng Carol lại đứng về phía Bob?


Picture