Tutorial completo em Tutorial Marlowe 3.0 (Parte 14) – Educação Cardano
14. Migrando para o Marlowe 3.0
Este tutorial explica como o Marlowe 3.0 difere das versões anteriores do idioma, versões 1.3 e 2.0.
14.1 Both removido
Não incluímos um construto Both no Marlowe 3.0, o que torna todos os contratos seqüenciais.
Como em nenhuma das versões do Marlowe havia comunicação entre os ramos do Both, a única funcionalidade extra fornecida pelo Both na prática era a capacidade de aguardar vários depósitos em dinheiro ao mesmo tempo.
Nós cuidamos dessa funcionalidade atualizando a construção When. Em vez de ter ramificações diferentes aguardando entradas diferentes, passamos para um modelo completamente sequencial e síncrono, onde podemos esperar por uma das várias entradas possíveis ao mesmo tempo (como em select).
A razão pela qual removemos essa construção é que os programas seqüenciais são mais fáceis de analisar e mais fáceis de raciocinar, já que não há necessidade de sincronização nem oportunidade de condições de corrida.
14.2 Contas incluídas
Nas versões anteriores do Marlowe, cada compromisso tem seu próprio tempo limite. Isso significa que o dinheiro depositado em um contrato não é fungível, porque pode ser diferenciado pelo tempo limite. Para alcançar a fungibilidade, removemos os tempos limites das construções individuais e temos um único tempo limite para todos os compromissos. A vida útil do contrato é direta para deduzir dos tempos limite do contrato.
No entanto, incluímos contas para organizar o dinheiro depositado no contrato. Isso torna mais transparente como o dinheiro flui dentro do contrato e, em particular, identifica a quem o dinheiro é reembolsado quando o contrato é rescindido.
Cada conta é identificada por um par de número (número da conta) e um participante; o participante indica quem receberá o dinheiro na conta por padrão quando o Close for alcançado.
A razão pela qual optamos por incluir contas é que, sem elas, descobrimos que estávamos basicamente monitorando as contas manualmente. Além disso, em todas as folhas da AST, descobrimos que estávamos calculando quanto devemos retornar a cada participante, bagunçando a árvore com repetidos “clichês”. Assim, ter dinheiro organizado em contas pode facilitar a argumentação dos contratos e torna-los menos propenso a erros.
Observe que podemos fornecer fungibilidade total usando uma única conta. Só precisamos escrever os comandos Pay adequados nas folhas do contrato. Se todo o dinheiro for pago aos participantes, o Close não terá efeito. [9]
Discussão: Contas implícitas vs explícitas
Muitos dos casos de uso de contas – e todos os que podemos identificar para contratos do ACTUS – têm uma conta por participante e um participante por conta (o “modelo 1-1”). Isso levanta a questão de saber se devemos dar um tratamento implícito às contas, com cada participante possuindo uma conta.
Por outro lado, há uma variedade de cenários plausíveis para contas que não estão em conformidade com o modelo 1-1.
Exemplos em que vários participantes usam uma conta.
-
Alice possui uma conta na qual compromete dinheiro para Bob gastar (pense em Alice como empregadora de Bob). Bob consegue gastar até o limite da conta, mas após o tempo limite do compromisso expirar, Alice recupera tudo o que resta.
-
Alice possui uma conta na qual compromete dinheiro para que Bob e Carol gastem (pense em Alice como empregadora de Bob e Carol). Eles podem gastar (em conjunto) até o limite da conta, mas após o tempo limite do comprometimento, Alice recupera tudo o que resta.
-
Por outro lado, cada um poderia receber uma conta separada da qual gastar: isso aplicaria limites individuais e também um limite agregado.
-
Se Bob [e Carol] quiserem gastar dinheiro, eles também podem adicionar dinheiro à conta, mas devem perceber que qualquer coisa não utilizada será devolvida a Alice.
Exemplos de várias contas para uma pessoa:
- Exemplos de subscrição se encaixariam aqui. Uma pessoa subscreve riscos de primeiro nível e riscos de segundo nível usando contas diferentes. Somente quando a subscrição do primeiro nível de todos os participantes for gasta, ocorrerão os gastos do segundo nível.
14.3 Close substitui Null / Pay
Como todos os contratos são seqüenciais agora, podemos saber facilmente quando um contrato é rescindido, ou seja: quando resta apenas Null. Usamos essa oportunidade para fechar o contrato e reembolsar qualquer dinheiro restante nas contas; por esse motivo, renomeamos Null para Close (melhorando a compreensibilidade).
Como observado anteriormente, não há mais tempo limite explícito nas contas, pois todos os contratos serão reduzidos para Close. De fato, podemos calcular estaticamente e eficientemente um limite superior para quando isso acontecer, tornando este aspecto do Marlowe analisável.
14.4 Pay
O Pay agora é imediato e tem uma única continuação e menos parâmetros. [10] Permite pagamentos de uma conta para um participante ou para outra conta. Descartamos o PayAll, pois ele pode ser emulado como uma série finita de Pay. De fato, podemos definir payAll como uma função Haskell (veja o exemplo zeroCouponBond).
É uma conseqüência da remoção da construção Both, que agora é inequívoco qual Pay vai primeiro: todos eles são seqüenciais, ajudando assim na análise. Com a construção Both, poderíamos fazer com que Pays acontecesse em qualquer ordem (uma vez que ambos os lados de Both devem ser executados simultaneamente).
14.5 Cláusula múltipla When
Modificamos When para incluir um conjunto de ações possíveis que podem ser inseridas enquanto When aguarda. Chamamos essa abordagem de “Uma das muitas”, porque ela aceita uma ação dentre muitas ações permitidas. When permanece o seguinte:
onde When aguardará o Timeout e continuará como Contract ou continuará conforme especificado em um dos Cases, o que ocorrer primeiro. Case é definido como:
e Action como:
Uma cláusula Case será ativada apenas se a Action correspondente for produzida e continuará como Contract. No caso de duas Actions correspondentes, a primeira da lista será executada.
Três tipos de ações são suportados:
-
Deposit representa um depósito de dinheiro em uma conta; isso foi originalmente chamado de Commit.
-
Choice representa uma escolha feita por um participante de dentro de um conjunto de valores Integers (Inteiros) (especificado pela lista de Bounds).
-
O Notify aguardará uma ação do Notify emitida quando a Observation for verdadeira. Chamamos isso de Notify para deixar claro que não podemos esperar apenas pelas Observations, mas que alguém deve acionar o contrato no momento em que uma Observation é verdadeira.
Descartamos a adição de observações ao Deposit and Choice, pois não seria óbvio se a Observation fosse avaliada antes ou depois da aplicação da ação.
Além dos casos explícitos em When, devemos lembrar que o ramo de timeout também é um caso e também precisa ser acionado (da mesma forma que o Notify). [11] [12]
14.6 Observações e Valores
Descartamos Observations e Values que podem ser expressos pela combinação de outros: como o AvailableMoney geral (para todo o contrato) ou como o DepositedMoneyBy, que lembra a quantidade de dinheiro depositada por um participante, pois o contrato pode ser reestruturado para observar que, e o apoio exigiria informações adicionais no estado (simplicidade).
Mantivemos a observação ChoseSomething, embora, na semântica proposta, toda ocorrência de ChoseSomething possa ser avaliada estaticamente e eficientemente examinando seu contexto.
Por exemplo, no contrato a seguir, podemos ver que a primeira ocorrência de ChoseSomething será avaliada como True e a segunda como False:
No entanto, optamos por manter o construto por dois motivos:
Ele permite a reutilização do código (conveniência). Por exemplo, no contrato anterior, poderíamos definir chosen1:
Mas isso não seria possível se não tivéssemos o construto ChoseSomething, pois o valor ao qual ele reduz depende do contexto.
Pode não ser mais o caso de as ocorrências da construção poderem ser avaliadas estaticamente se estendermos a construção When para suportar “muitas de muitas” entradas.
14.7 Inclusão de SlotIntervals
A especificação EUTxO fornece scripts de validação com intervalos de slots, em vez de números de slots. Isso é para promover o determinismo nos scripts de validação. No entanto, mantivemos o tempo limite de When (o único tempo limite) como um número de slot. A maneira como lidamos com intervalos de slots é exigindo que o intervalo de uma transação não inclua nenhum tempo limite durante o qual a semântica deve fazer uma escolha. Por exemplo: se um tempo limite for 10, uma transação com intervalo de 5 a 15 falhará com AmbiguousSlotInterval. Os participantes teriam que emitir uma transação com intervalo 5-9 ou 10-15 (ou ambos).
No entanto, para Values, fornecemos as duas construções SlotIntervalStart e SlotIntervalEnd. Uma alternativa a considerar seria modificar a semântica para que os Values não sejam determinísticos, para que possamos incluir uma construção CurrentSlot e apenas invalidar transações ambíguas, mas isso complicaria a semântica e as tornaria menos previsíveis.
[9] Podemos fornecer uma maneira de analisar estaticamente o contrato para verificar se existe algum dinheiro restante em qualquer conta quando o fechamento for alcançado.
[10] Isso significa que os pagamentos agora obedecem a um modelo “push” em vez de um modelo “pull”.
[11] No entanto, o acionamento do contrato para processar tempos limite não é urgente, como acontece com o Notify, porque, embora as Observations possam alternar entre True e False, os tempos limite só podem ocorrer uma vez e, independentemente de terem sido observados ou não pelo contrato, eles não podem ser revertidos.
[12] De fato, um Case explícito não pode mais ser emitido após o tempo limite, mesmo que o tempo limite não tenha sido observado pelo contrato, pois o tempo limite é verificado antes das Entradas (Inputs). No entanto, um participante pode querer acionar um tempo limite nos casos em que nenhuma outra Entrada é necessária, a fim de acionar um ou mais pagamentos, por exemplo. Na implementação atual da semântica, isso seria feito emitindo uma transação com uma lista vazia de Entradas.