Marlowe được nhúng trong JavaScript

Marlowe được viết dưới dạng kiểu dữ liệu Haskell, và do đó, việc mô tả các hợp đồng thông minh của Marlowe bằng Haskell rất dễ hiểu. Nhưng vì các hợp đồng của Marlowe “chỉ là” một dạng dữ liệu, chúng tôi có thể trình bày chúng bằng các ngôn ngữ khác một cách tốt như nhau.

Ở đây chúng tôi mô tả một thư viện được viết bằng TypeScript có thể được sử dụng để tạo các hợp đồng thông minh Marlowe từ TypeScript hoặc JavaScript theo cách tương tự như cách sử dụng Haskell. Nếu bạn không quen thuộc với TypeScript, bạn cũng có thể sử dụng API như thể nó được viết bằng JavaScript vì TypeScript là một superset JavaScript.

Bạn có thể thử thư viện trực tuyến trong Sân chơi Marlowe bằng cách chọn Start in JavaScript trên trang chủ hoặc bằng cách mở một trong các ví dụ JavaScript.

Chúng tôi bắt đầu phần này bằng cách giải thích cách nhúng, sau đó giải thích một vài điểm cụ thể về cách nhúng trong JavaScript và cuối cùng trình bày một ví dụ về hợp đồng đầy đủ được mô tả bằng cách sử dụng phương pháp nhúng JS.

Sử dụng Trình chỉnh sửa JS trong Sân chơi Marlowe

Bản thân việc triển khai thư viện rất đơn giản và bạn có thể tìm thấy tất cả mã nguồn của nó tại đây: https://github.com/input-output-hk/plutus/blob/master/marlowe-playground-client/src/Language/Javascript/ MarloweJS.ts

Nó dựa trên nguyên tắc là đối với mỗi kiểu (type) Haskell thì có một kiểu TypeScript tương ứng và tương ứng với mỗi hàm constructor sẽ có một định nghĩa không đổi.

import {
   PK, Role, Account, Party, ada, AvailableMoney, Constant, ConstantParam,
   NegValue, AddValue, SubValue, MulValue, DivValue, ChoiceValue, TimeIntervalStart,
   TimeIntervalEnd, UseValue, Cond, AndObs, OrObs, NotObs, ChoseSomething,
   ValueGE, ValueGT, ValueLT, ValueLE, ValueEQ, TrueObs, FalseObs, Deposit,
   Choice, Notify, Close, Pay, If, When, Let, Assert, SomeNumber, AccountId,
   ChoiceId, Token, ValueId, Value, EValue, Observation, Bound, Action, Payee,
   Case, Timeout, ETimeout, TimeParam, Contract
} from 'marlowe-js';

Thư viện JavaScript / TypeScript cung cấp các định nghĩa không đổi cho các cấu trúc Marlowe không có đối số, như trường hợp của TimeIntervalStart:

const TimeIntervalStart: Value

hoặc Closehợp đồng:

const Close: Contract

Các cấu trúc có đối số được biểu diễn dưới dạng hàm, như trong trường hợp AvailableMoney:

const AvailableMoney: (token: Token, accountId: Party) => Value

Bạn có thể xem khai báo kiểu cho từng cấu trúc và kiểu bằng cách di chuột qua số nhận dạng trong khai báo nhập ở đầu tệp xuất hiện trong trình chỉnh sửa của tab Trình chỉnh sửa JS. Cả khai báo nhập và khai báo hàm đều được tô xám để báo hiệu rằng chúng không được sửa đổi, mã tạo hợp đồng phải được viết bên trong nội dung của hàm được cung cấp và hợp đồng kết quả phải được trả về do hàm (sử dụng returnhướng dẫn).

Bên trong, các hàm và hằng số của thư viện JavaScript / TypeScript trả về một biểu diễn JSON của các cấu trúc Marlowe. Ví dụ, hàm AvailableMoneyđược định nghĩa như sau:

const AvailableMoney =
    function (token: Token, accountId: Party) => Value {
        return { "amount_of_token": token,
                 "in_account": accountId };
    };

Khi bạn nhấp vào nút Biên dịch (Compile) trong trình chỉnh sửa JS của Sân chơi Marlowe, mã trong phần thân của tab sẽ được thực thi và đối tượng JSON do hàm trả về trong quá trình thực thi được phân tích cú pháp thành một hợp đồng Marlowe thực tế. Sau khi thành công, có thể Gửi đến Trình mô phỏng (Send to Simulator); cách thức hoạt động sẽ được mô tả trong phần tiếp theo.

Về nguyên tắc, bạn có thể viết mã JavaScript tạo ra biểu diễn JSON của Marlowe trực tiếp, nhưng bạn không cần phải lo lắng về JSON khi sử dụng thư viện JS.

Khi bạn sử dụng thư viện JS Marlowe và việc bạn sử dụng các hàm và hằng số của kiểm tra kiểu thư viện, thì kết quả mã của bạn sẽ tạo ra một biểu diễn JSON hợp lệ của hợp đồng Marlowe, vì vậy chúng tôi đảm bảo an toàn cho việc tạo hợp đồng thông qua kiểu hệ thống của TypeScript.

SomeNumber Type

Có một kiểu quan trọng không có trong định nghĩa Haskell của Marlowe, chúng tôi đã gọi kiểu đó là SomeNumber và nó được định nghĩa như sau:

type SomeNumber = string | number | bigint

Lý do chúng ta có kiểu này là kiểu gốc cho các số trong JavaScript và TypeScript mất độ chính xác khi được sử dụng với các số nguyên lớn. Điều này là do việc triển khai nó dựa trên các số dấu phẩy động.

Biểu thức sau đúng trong JavaScript:

9007199254740992 == 9007199254740993

Điều này có thể gây rắc rối cho các hợp đồng tài chính, vì cuối cùng nó có thể dẫn đến mất tiền.

Do đó, chúng tôi khuyên bạn nên sử dụng loại bigint. Nhưng chúng tôi hỗ trợ ba cách biểu diễn số để thuận tiện và khả năng tương thích ngược với các phiên bản JS cũ:

  • Số tự nhiên:

    • Chúng rất dễ sử dụng

    • Ký hiệu rất đơn giản và có thể được sử dụng với các toán tử tiêu chuẩn, ví dụ:32 + 57

    • Chúng mất độ chính xác đối với số lượng lớn

  • Biểu diễn chuỗi:

    • Ký hiệu chỉ yêu cầu thêm dấu ngoặc kép xung quanh các số

    • Bạn không thể sử dụng trực tiếp các toán tử tiêu chuẩn, ví dụ: "32" + "57" = "3257"

    • Chúng không mất độ chính xác

  • bigint:

    • Chúng rất dễ sử dụng (chỉ cần thêm nvào sau các ký tự số)

    • Ký hiệu rất đơn giản và có thể được sử dụng với các toán tử tiêu chuẩn, ví dụ:32n + 57n

    • Chúng không mất độ chính xác

Tất cả các biểu diễn này được chuyển đổi thành BigNumbernội bộ, nhưng có thể xảy ra mất độ chính xác nếu sử dụng các số tự nhiên, như BigNumberđược xây dựng, trước khi chuyển đổi xảy ra và API không thể làm gì với nó.

Quá tải kiểu EValue và kiểu boolean

Trong Haskell, các quan sát boolean không đổi được biểu thị bằng TrueObsFalseObs, và các giá trị nguyên không đổi được biểu thị bằng dấu Constanttheo sau là dấu Integer. Trong JavaScript và TypeScript, bạn cũng có thể sử dụng các hàm tạo này, nhưng bạn không cần phải làm như vậy, vì loại Quan sát được quá tải để chấp nhận các boolean JavaScript gốc và các hàm mà trong Haskell lấy một Value, trong JavaScript chúng lấy một EValuethay thế, và EValuelà được định nghĩa như sau:

type EValue = SomeNumber | Value

Ví dụ: Viết hợp đồng Hoán đổi trong TypeScript

Cho dù chúng tôi bắt đầu bằng cách sửa đổi một ví dụ hiện có hay bằng cách tạo một hợp đồng JavaScript mới, chúng tôi sẽ tự động được cung cấp danh sách nhập và khai báo hàm. Chúng ta có thể bắt đầu bằng cách xóa mọi thứ không bị chuyển sang màu xám và bắt đầu viết bên trong dấu ngoặc nhọn của định nghĩa hàm đã cung cấp.

Giả sử chúng tôi muốn viết một hợp đồng để Alice có thể đổi 1000 Ada với Bob lấy 100 đô la.

Đầu tiên, hãy tính toán số tiền chúng ta muốn làm việc với mỗi đơn vị, chúng ta có thể xác định một số hằng số bằng cách sử dụng const:

const lovelacePerAda : SomeNumber = 1000000n;
const amountOfAda : SomeNumber = 1000n;
const amountOfLovelace : SomeNumber = lovelacePerAda * amountOfAda;
const amountOfDollars : SomeNumber = 100n;

Số tiền trong hợp đồng phải được viết bằng Lovelace, là 0,000001 Ada. Vì vậy, chúng tôi tính số lượng Lovelace bằng cách nhân 1.000 Ada với 1.000.000. Số lượng đô la là 100 trong ví dụ của chúng tôi.

API đã cung cấp một phương thức khởi tạo cho tiền tệ ADA và hiện không có biểu tượng tiền tệ nào trong Cardano cho đô la, nhưng chúng ta hãy tưởng tượng có và hãy định nghĩa nó như sau:

const dollars : Token = Token("85bb65", "dollar")

Trong thực tế , chuỗi "85bb65"sẽ tương ứng với ký hiệu tiền tệ, là một hàm băm và phải được viết bằng cơ số16 (biểu diễn hệ thập lục phân của một chuỗi byte). Và chuỗi "dollar"sẽ tương ứng với tên mã thông báo.

Bây giờ chúng ta hãy xác định một loại đối tượng để chứa thông tin về các bên và những gì họ muốn trao đổi để thuận tiện:

type SwapParty = {
 party: Party;
 currency: Token;
 amount: SomeNumber;
};

Chúng tôi sẽ lưu tên của bên trong trường bên, tên của đơn vị tiền tệ trong trường tiền tệ và số tiền mà bên đó muốn trao đổi trong trường số tiền:

const alice : SwapParty = {
   party: Role("alice"),
   currency: ada,
   amount: amountOfLovelace
}

const bob : SwapParty = {
   party: Role("bob"),
   currency: dollars,
   amount: amountOfDollars
}

Bây giờ chúng tôi đã sẵn sàng để bắt đầu viết hợp đồng của mình. Đầu tiên, hãy xác định các khoản tiền gửi. Chúng tôi lấy thông tin từ bên phải thực hiện việc đặt cọc, thời gian chờ cho đến khi chúng tôi đợi khoản tiền gửi được thực hiện và hợp đồng tiếp tục sẽ được thực thi nếu khoản tiền gửi thành công.

function makeDeposit(src: SwapParty, timeout: ETimeout,
                     timeoutContinuation: Contract, continuation: Contract): Contract {
    return When([Case(Deposit(src.party, src.party, src.currency, src.amount),
                      continuation)],
                timeout,
                timeoutContinuation);
}

Chúng tôi chỉ cần một Whencấu trúc với một đơn Caseđại diện cho một Depositbên srcvào tài khoản của chính họ, theo cách này nếu chúng tôi hủy bỏ hợp đồng trước khi hoán đổi, mỗi bên sẽ lấy lại những gì họ đã đặt cọc.

Tiếp theo, chúng tôi xác định một trong hai khoản thanh toán của hoán đổi. Chúng tôi lấy nguồn và bên đích làm tham số, cũng như hợp đồng tiếp tục sẽ được thực thi sau khi thanh toán.

const makePayment = function (src: SwapParty, dest: SwapParty,
                              continuation: Contract): Contract {
    return Pay(src.party, Party(dest.party), src.currency, src.amount,
               continuation);
}

Đối với điều này, chúng tôi chỉ cần sử dụng Paycấu trúc để thanh toán từ tài khoản mà bên nguồn đã thực hiện ký quỹ cho bên đích.

Cuối cùng, chúng tôi có thể kết hợp tất cả các phần:

const contract: Contract = makeDeposit(alice, 1700000000n, Close,
                             makeDeposit(bob, 1700003600n, Close,
                                 makePayment(alice, bob,
                                     makePayment(bob, alice,
                                         Close))))

return contract;

Hợp đồng có bốn bước:

  1. Alice có thể gửi tiền cho đến thời điểm POSIX 1700000000 (2023-11-14 22:13:20 GMT).

  2. Bob có thể gửi tiền cho đến thời điểm POSIX 1700003600 (2023-11-14 23:13:20 GMT), một giờ sau, nếu không Alice sẽ được hoàn lại tiền và hợp đồng bị hủy bỏ.

  3. Sau đó, chúng tôi trả tiền đặt cọc của Alice cho Bob.

  4. Chúng tôi trả tiền đặt cọc của Bob cho Alice.

Và đó là nó. Bạn có thể tìm thấy mã nguồn đầy đủ cho phiên bản mẫu của hợp đồng thông minh hoán đổi trong các ví dụ trong Sân chơi Marlowe mà chúng ta sẽ xem xét tiếp theo.

Last updated