Các vấn đề tiềm ẩn với hợp đồng

Ngôn ngữ Marlowe được thiết kế để có ít lỗi và khó hiểu nhất có thể, để các hợp đồng có thể được viết một cách trực quan, tránh bất kỳ sự bất ngờ nào. Tuy nhiên, theo thiết kế, không thể loại trừ tất cả các hợp đồng không nên được viết, mà không làm cho Marlowe khó sử dụng hơn nhiều. Hơn nữa, ngay cả khi một hợp đồng được viết tốt, người dùng của nó vẫn có thể tương tác với nó theo những cách không hợp lệ, bằng cách đưa ra các giao dịch không hợp lệ.

Trong mọi trường hợp, khi những tác động ngoài ý muốn này xảy ra, Marlowe được thiết kế để hành xử theo cách trực quan và thận trọng nhất có thể. Tuy nhiên, cần lưu ý những vấn đề tiềm ẩn này và xem lại cách Marlowe ứng xử trong những tình huống này. Đó là chủ đề của hướng dẫn này.

Cảnh báo

Cảnh báo của Marlowe là dấu hiệu cho thấy hợp đồng bị viết sai. Một hợp đồng được viết tốt sẽ không bao giờ đưa ra cảnh báo, bất kể người dùng tương tác với nó như thế nào. Lý tưởng nhất là chúng tôi muốn cấm các hợp đồng có thể đưa ra cảnh báo chưa bao giờ được viết, nhưng điều đó sẽ yêu cầu hợp đồng Marlowe phải được gõ phụ thuộc và việc viết các biểu thức được gõ phụ thuộc sẽ rườm rà hơn nhiều.

Thay vào đó, Marlowe cho phép viết các hợp đồng đưa ra cảnh báo và chúng tôi cung cấp các công cụ phân tích tĩnh cho phép các nhà phát triển hợp đồng kiểm tra xem một hợp đồng cụ thể có thể đưa ra cảnh báo hay không. Ngoài ra, chúng tôi cung cấp các hành vi dự phòng khi hợp đồng đưa ra cảnh báo, bất chấp lời khuyên của chúng tôi. Chúng tôi cung cấp các hành vi dự phòng vì chúng tôi thừa nhận rằng việc phân tích các hợp đồng lớn có thể rất tốn kém về mặt tính toán và vì có thể mắc sai lầm. Chúng tôi muốn những hợp đồng viết dở không thành công theo cách vô hại nhất có thể, đó là một cách thận trọng.

Non-positive payments

Khi một hợp đồng được cho là thanh toán một số tiền nhỏ hơn một đơn vị tiền tệ hoặc mã thông báo, nó sẽ đưa ra cảnh báo NonPositivePay và nó sẽ không chuyển bất kỳ khoản tiền nào.

Thanh toán âm nên được thực hiện dưới dạng tiền gửi dương (khi thanh toán cho một người tham gia) hoặc thanh toán dương theo chiều ngược lại (khi thanh toán giữa các tài khoản).

Non-positive deposits

Khi một hợp đồng được cho là mong đợi một số tiền nhỏ hơn một đơn vị tiền tệ mã thông báo, nó sẽ vẫn đợi một IDepositgiao dịch, nhưng giao dịch đó không cần chuyển bất kỳ khoản tiền nào vào hợp đồng và không có tiền nào được chuyển đến người tham gia phát hành giao dịch. Khi việc đặt cọc 'giả' này thành công, hợp đồng sẽ đưa ra cảnh báo NonPositiveDeposit.

Các khoản tiền gửi âm phải luôn được thực hiện như các khoản thanh toán dương.

Thanh toán một phần - Partial payment

Khi một hợp đồng được cho là phải trả một số tiền lớn hơn số tiền có trong tài khoản nguồn, nó sẽ chỉ chuyển bất cứ thứ gì có sẵn trong tài khoản đó, ngay cả khi có đủ tiền trong tất cả các tài khoản của hợp đồng, và nó sẽ đưa ra một cảnh báo PartialPay.

Nên tránh thanh toán từng phần vì hợp đồng không bao giờ tạo ra thanh toán từng phần là một hợp đồng rõ ràng. Các hợp đồng rõ ràng trấn an người dùng của họ rằng chúng sẽ có thể thực thi và bất cứ nơi nào trong hợp đồng có ghi rằng sẽ có thanh toán thì điều đó sẽ thực sự xảy ra.

Letshadowing (không bị che phủ bởi phân tích tĩnh)

Khi hợp đồng đạt đến cấu trúc Let xác định lại giá trị bằng số nhận dạng đã được xác định bởi bên ngoài Let, hợp đồng sẽ đưa ra cảnh báo Shadowing và nó sẽ ghi đè định nghĩa trước đó.

Shadowing là một thực hành lập trình không tốt vì nó dẫn đến sự nhầm lẫn. Việc sử dụng cùng một số nhận dạng cho nhiều thứ có thể đánh lừa các nhà phát triển hoặc người dùng nghĩ rằng một lần sử dụng Usesẽ được đánh giá bằng một số tiền trong khi thực sự nó sẽ được đánh giá bằng một số lượng khác.

Bad smells

Có một số 'Bad smells' khác cho thấy rằng một hợp đồng có thể đã được thiết kế kém.

Các hợp đồng này có hiệu lực, theo nghĩa là chúng sẽ không nhất thiết gây ra bất kỳ cảnh báo nào và chúng làm theo những gì họ nói là làm, nhưng chúng có các đặc điểm cho thấy rằng hoặc nhà phát triển hợp đồng đã không nhận thức đầy đủ về hậu quả của hợp đồng, hoặc rằng nhà phát triển đã cố ý viết hợp đồng theo cách gây khó hiểu cho người đọc.

Sử dụng Undefined Let(phải là một cảnh báo)

Khi một cấu trúc Use sử dụng một số nhận dạng chưa được xác định, nó sẽ đánh giá giá trị mặc định là 0. Sẽ không có cảnh báo nào được đưa ra, nhưng một lần nữa, đây là một thực hành không tốt vì nó có thể gây hiểu lầm. (Constant 0) nên được sử dụng thay thế vì nó làm rõ số tiền được đề cập.

Unreachable parts of a contract

Đây là bad smell chính trong các hợp đồng của Marlowe. Nếu một phần của hợp đồng không thể truy cập được, tại sao nó lại được đưa vào ngay từ đầu?

bad smell này có một số hình dạng.

Sub- Contractis not reachable

Ví dụ:

If FalseObs contract1 contract2

Hợp đồng trước tương đương với contract2. Nói chung, bạn không bao giờ nên sử dụng FalseObs, và bạn chỉ nên sử dụng TrueObsnhư là quan sát gốc của một cấu trúc Case.

Observationis always short-cut

Ví dụ:

OrObs TrueObs observation1

Các quan sát trước đó là tương đương với observation1. Một lần nữa, bạn chỉ nên sử dụng TrueObslàm quan sát gốc của một cấu trúc Case .

Whenbranch is unreachable

Ví dụ:

When [ Case (Notify TrueObs) contract1
     , Case (Notify TrueObs) contract2 ]
     10
     contract3

contract2là không thể truy cập, toàn bộ Casecó thể bị xóa khỏi hợp đồng và hành vi sẽ giống nhau.

Nested non-increasing timeouts

Ví dụ:

When []
     10
     When [ Case (Notify TrueObs)
                 contract1 ]
          10
          contract2

contract1không thể truy cập được: sau khi chặn 10, hợp đồng sẽ trực tiếp phát triển thành contract2. Bên trong Whenkhông tạo ra bất kỳ sự khác biệt nào đối với hợp đồng.

Các vấn đề về khả năng sử dụng

Ngay cả khi hợp đồng tránh được các cảnh báo và không có mã không thể truy cập, nó vẫn có thể cho phép người dùng độc hại buộc người dùng khác vào các tình huống không mong muốn mà không phải do nhà phát triển hợp đồng dự định ban đầu.

Bad timing of When constructs

Hãy xem xét hợp đồng sau:

When [Case (Choice (ChoiceId "choice1" (Role "alice")) [Bound 0 10])
           (When [Case (Choice (ChoiceId "choice2" (Role "bob")) [Bound 0 10])
                       Close
                 ]
            10
            (Pay (Role "bob") (Party (Role "alice"))
                 ada
                 (Constant 10)
                 Close
            )
        )
     ]
     10
     Close

Không có gì sai về nguyên tắc với hợp đồng này, nhưng nếu (Role "alice") đưa ra lựa chọn của cô ấy ở block 9, bob sẽ hầu như không thể đưa ra lựa chọn của anh ấy đúng thời hạn và nhận được hoàn lại số tiền trong tài khoản của anh ấy (Role "bob"). Trừ khi, đây là một phần của trò chơi và đó là một hiệu ứng dự kiến, đây có thể là một hợp đồng không công bằng cho (Role "bob").

Nói chung, đó là một thực tiễn tốt để đảm bảo rằng các Whencấu trúc có thời gian chờ ngày càng tăng và việc tăng thời gian chờ là hợp lý để các bên khác nhau phát hành và làm cho giao dịch của họ được chấp nhận bởi blockchain. Có nhiều lý do khiến sự tham gia của một nhóm có thể bị trì hoãn: sự cố cung cấp năng lượng, số lượng giao dịch đang chờ xử lý trong chuỗi khối tăng đột ngột, các cuộc tấn công mạng, v.v. Vì vậy, điều quan trọng là phải có nhiều thời gian và hào phóng với thời gian chờ và tăng thời gian chờ.

Errors

Cuối cùng, ngay cả khi một hợp đồng được viết hoàn hảo. Người dùng có thể sử dụng nó không đúng cách và chúng tôi gọi đó là những lỗi sử dụng không chính xác.

Trong mọi trường hợp, bất cứ khi nào một giao dịch gây ra lỗi, giao dịch đó sẽ không ảnh hưởng đến Contracthoặc đối với nó State. Trên thực tế, ví của người dùng sẽ biết trước liệu một giao dịch có tạo ra lỗi hay không, bởi vì các giao dịch là xác định, vì vậy người dùng không bao giờ cần phải gửi một giao dịch có lỗi đến blockchain.

Ambiguous interval

Khi một giao dịch đạt đến thời gian chờ, khoảng thời gian của nó phải rõ ràng về việc thời gian chờ đã qua hay chưa. Ví dụ: nếu phần lớn nhất Whencủa hợp đồng có thời gian chờ 1700003600và giao dịch có khoảng thời gian [1700000000, 1700007200] được phát hành, giao dịch sẽ gây ra lỗi AmbiguousTimeIntervalError, vì không thể biết liệu thời gian chờ đã qua hay chưa chỉ bằng cách nhìn vào giao dịch. Để tránh điều này, giao dịch phải được chia thành hai giao dịch riêng biệt:

  1. Một với khoảng thời gian .[1700000000, 1700003599]

  2. Một cái khác với khoảng thời gian .[1700003600, 1700007200]

Áp dụng không phù hợp

Nếu một giao dịch không cung cấp các yếu tố đầu vào như mong đợi Contract, thì hợp đồng sẽ xuất hiện NoMatchErrorlỗi và toàn bộ giao dịch sẽ bị hủy.

Useless transaction

Nếu một giao dịch không có bất kỳ ảnh hưởng nào đến Contracthoặc State, nó sẽ dẫn đến UselessTransactionlỗi và toàn bộ giao dịch sẽ bị hủy. Lý do tại sao chúng tôi loại bỏ các giao dịch vô ích là chúng mở ra cánh cửa cho các cuộc tấn công Từ chối Dịch vụ (DoS), bởi vì kẻ tấn công tiềm năng có thể làm ngập hợp đồng với các giao dịch không cần thiết và ngăn chặn các giao dịch cần thiết để đưa nó vào blockchain.

Last updated