Что такое подходящий тип для смарт-контрактов?

Мне интересно, какой лучший способ выразить умные контракты на типизированных языках, таких как Haskell или Idris (чтобы вы могли, например, скомпилировать его для работы в сети Ethereum). Моя главная проблема заключается в следующем: что такое тип, который фиксирует все, что может сделать контракт?

Наивное решение: EthIO

Наивное решение заключалось бы в определении контракта как члена типа EthIO. Такой тип был бы похож на Haskell IO, но вместо включения системных вызовов он включал вызовы с цепочкой, т.е. Позволял бы считывать и записывать в состояние блок-цепи, вызывать другие контракты, получать данные блоков и т.д.

-- incrementer.contract

main : EthIO
main = do
   x <- SREAD 0x123456789ABCDEF
   SSTORE (x + 1) 0x123456789ABCDEF

Этого явно достаточно для реализации любого контракта, но:

  • Будет слишком сильным.

  • Будет очень привязана к блочной цепочке Ethereum.

Консервативное решение: шаблон поиска событий

В соответствии с этой идеей контракт будет определен как сгиб над списком действий:

type Contract action state = {
    act  : UserID -> action -> state -> state,
    init : state
}

Итак, программа будет выглядеть так:

incrementer.contract

main : Contract
main = {
    act _ _ state = state + 1,
    init          = 0
}

То есть вы определяете начальное состояние, тип действий и как это состояние изменяется, когда пользователь отправляет действие. Это позволило бы определить любой произвольный контракт, который не связан с отправкой/получением денег. Большинство блокчейнов имеют какую-то валюту, а самые полезные контракты каким-то образом связаны с деньгами, поэтому тип будет слишком ограничительным.

Менее консервативное решение: события + валюта

Мы можем сделать тип выше осведомленным о валютах, жестко кодируя валютную логику в тип выше. Таким образом, мы получили бы что-то вроде:

type Contract action state = {
    act        : UserID -> action -> state -> state,
    init       : state,
    deposit    : UserID -> Amount -> state -> state,
    withdrawal : UserID -> Amount -> state -> Maybe state
}

I.e. разработчику контракта необходимо будет четко определить, как бороться с денежными депозитами и изъятиями. Такого типа было бы достаточно, чтобы определить любой автономный контракт, который может взаимодействовать с валютой цепочки узлов. К сожалению, такой контракт не сможет взаимодействовать с другими контрактами. На практике контракты часто взаимодействуют друг с другом. Например, Exchange должен связываться с обменными контрактами Token для запросов на баланс и т.д.

Обобщение: глобальное состояние?

Итак, давайте сделаем шаг назад и перепишем консервативное решение следующим образом:

type Contract = {
    act  : UserID -> Action -> Map ContractID State -> State,
    init : State
}

В соответствии с этим определением функция act имела бы доступ не только к собственному состоянию контракта, но и к состоянию каждого другого контракта на той же цепочке. Поскольку каждый контракт может считывать состояние друг друга, можно легко реализовать протокол связи поверх этого, и, таким образом, такого типа достаточно для реализации произвольно взаимодействующих контрактов. Кроме того, если валюта blockchain сама была реализована в качестве контракта (возможно, с использованием обертки), тогда этого типа было бы достаточно, чтобы иметь дело с деньгами, несмотря на то, что он не был жестко запрограммирован на тип. Но это решение имеет 2 проблемы:

  • Peeking в другом состоянии контракта выглядит очень "взломанным" способом для общения;

  • Контракт, определенный таким образом, не сможет взаимодействовать с существующими контрактами, которые не знают об этом решении.

Что теперь?

Теперь я в темноте. Я знаю, что я не в правильной абстракции для этой проблемы, но я не уверен, что это будет. Похоже, что корень проблемы заключается в том, что я не способен правильно зафиксировать феномен межконтрактной связи. Какой конкретный тип был бы более подходящим для определения произвольных интеллектуальных контрактов?