Skip to content
Permalink
Browse files

Merge branch 'master' of https://github.com/input-output-hk/marlowe

  • Loading branch information...
simonjohnthompson committed Apr 11, 2019
2 parents 8e15ab1 + c2f83a5 commit 8acdca0e9336e631d3da4525eff8f2e4eef4177b
Showing with 162 additions and 2 deletions.
  1. +147 −2 docs/tutorial-v2.0/marlowe-semantics.md
  2. +15 −0 semantics-2.0/Semantics.hs
@@ -39,7 +39,19 @@ In addition to these three functions, there are three additional functions that

Let us start by looking at the parts of the processing that the semantics does exclusively for actions (that is, inputs of the kind `Commit` and `Pay`).

An action is represented by its `IdAction`. The function `fetchPrimitive` will traverse the parts of the contract that are currently in force (activated) and look for a primitive that matches the `IdAction` provided by the input. `fetchPrimitive` will only succeed if there is exactly one primitive that corresponds to the given `IdAction`. If it succeeds, `fetchPrimitive` will return a `DetachedPrimitive`:
An action is represented by its `IdAction`. The function `fetchPrimitive` will traverse the parts of the contract that are currently in force (activated) and look for a primitive that matches the `IdAction` provided by the input. `fetchPrimitive` will only succeed if there is exactly one primitive that corresponds to the given `IdAction`.

```haskell
fetchPrimitive idAction blockNum state (Both leftContract rightContract) =
case (go leftContract, go rightContract) of
(Picked (result, cont), NoMatch) -> Picked (result, (Both cont rightContract))
(NoMatch, Picked (result, cont)) -> Picked (result, (Both leftContract cont))
(NoMatch, NoMatch) -> NoMatch
_ -> MultipleMatches
where go = fetchPrimitive idAction blockNum state
```

If it succeeds, `fetchPrimitive` will return a `DetachedPrimitive`:

```haskell
data DetachedPrimitive = DCommit IdCommit Person Integer Timeout
@@ -48,6 +60,11 @@ data DetachedPrimitive = DCommit IdCommit Person Integer Timeout

`fetchPrimitive` will also return the contract after removing the chosen primitive; this new contract will be taken as the remaining contract if the evaluation succeeds. The `DetachedPrimitive` is passed to `eval`, together with the current `State` of the contract.

```haskell
fetchPrimitive :: IdAction -> BlockNumber -> State -> Contract
-> FetchResult (DetachedPrimitive, Contract)
```

### `Commit`

A `Commit` allows a participant `person` to transfer to the contract an amount of money `value`. This money will be recorded in the contract under the identifier `idCommit` and future payments can use this identifier as a source of money. Once the block specified by `timeout` (the second `Timeout` in `Commit`) is reached, any money from the `Commit` that has not been spent (through payments) will be frozen, and the participant that performed the commitment will be able to withdraw with the next transaction that he or she signs.
@@ -107,5 +124,133 @@ reduceRec blockNum state env c@(Pay _ _ _ _ timeout _ continuation) =

Again, the `fetchPrimitive` function will use the first continuation (the first `Contract` in `Pay`) instead.

## Non-action input processing

`Choice`s and `Oracle`s inputs are processed very differently to actions. They are relatively independent of the state of the contract, and they may be issued anytime, as long as the values provided can potentially be used by the contract. In other words, there must be somewhere in the code of the contract that inspects the `Choice` or `Oracle` value in order for a participant to be able to provide that value. Otherwise, the contract does not need to know the value, and providing it anyway would just be adding clutter and load to the contract and blockchain which could end up translating into problems like DoS. For these reasons, the Marlowe 2.0 semantics disallow providing unnecessary information.

Other than that, the only thing that Marlowe does when provided with `Choice`s and `Oracle`s is to record them in the state so that the `reduce` function can access them.

## Combinators and `Null`

In this section, we describe the remaining of Marlowe contracts, which in general can be used to combine other contracts together and to decide between them depending on the information known to the contract at any given moment.

### `Null`

The `Null` contract does nothing and it stays quiescent forever.

```haskell
reduceRec _ _ _ Null = Null
```

Nevertheless, it is used by the `simplify` function and it can be used to replace the contract with a smaller but equivalent one. For example, `Both Null Null` gets reduced to `Null`

### `Both`

The `Both` construct allows two contracts to be active simultaneously. It would be like having two separate contracts deployed simultaneously, except in that when using `Both` they will share `State`, and thus `Commit`s made in one of the contracts can be used for `Pay`s in the other contract. We have also taken a lot of care in ensuring that `Both` is symmetric, that is, writing `Both A B` should be equivalent to writing `Both B A`, no matter what `A` and `B` are.

```haskell
reduceRec blockNum state env (Both cont1 cont2) = Both (go cont1) (go cont2)
where go = reduceRec blockNum state env
```

### `Choice`

The `Choice` construct immediately chooses between two contracts depending on the value of an `Observation`. The moment that `Choice` is activated, the value of `obs` will decide whether `Choice` gets reduced to `cont1` (if it is true) or to `cont2` (if it is false).

```haskell
reduceRec blockNum state env (Choice obs cont1 cont2) =
reduceRec blockNum state env (if (evalObservation blockNum state obs)
then cont1
else cont2)
```

### `When`

The `When` contruct delays a contract in time until an `Observation` becomes true. `When` will not activate any of its subcontracts until either the `Observation` becomes true, or until the `timeout` block is reached. If `obs` is true, then `When` is reduced to `cont1`, if `timeout` has been reached, then `When` is reduced to `cont2`.

```haskell
reduceRec blockNum state env c@(When obs timeout cont1 cont2) =
if isExpired blockNum timeout
then go cont2
else if evalObservation blockNum state obs
then go cont1
else c
where go = reduceRec blockNum state env
```

It is worth noting that, because Marlowe follows the pull model, it is not just enough for the `Observation` to become true for `When` to evolve; the contract needs to be triggered while the `Observation` is true. The contract can be triggered at any time by issuing a transaction that does not need to have any inputs and does not need to be signed (anyone can trigger the contract).

### `While`

The `While` construct works opposite to `When` in the sense that while `When` gets reduced when it `Observation` becomes true, `While` gets reduced when its `Observation` becomes false.

```haskell
reduceRec blockNum state env (While obs timeout contractWhile contractAfter) =
if isExpired timeout blockNum
then go contractAfter
else if evalObservation blockNum state obs
then (While obs timeout (go contractWhile) contractAfter)
else go contractAfter
where go = reduceRec blockNum state env
```

However, there is one fundamental difference: `When` does not activate its subcontract until it gets reduced, and `While` activates its subcontract immediately (similarly to what `Both` does). And there is something unique about `While`, it may delete a contract that has already been activated once the `Observation` becomes true.

For example, in the following contract:

```
While
(NotObs
(ChoseSomething (1, 1))) 20
(Commit 1 1 1
(ValueFromChoice (1, 1)
(Constant 20)) 10 20 Null Null) Null
```

once the choice `(1, 1)` is made, it will no longer be possible to use the `Commit`.

### `Scale`

The `Scale` construct scales the amounts paid by `Commit` and `Pay`. It takes three `Value`s, the first one is the numerator, the second one is the denominator, and the third one is the default.

As soon as the `Scale` construct is activated, it activates its subcontract `contract`, and it evaluates all the three `Value`s and replaces them with a `Constant` (so that they may not change anymore).

```haskell
reduceRec blockNum state env (Scale divid divis def contract) =
Scale (Constant vsDivid) (Constant vsDivis) (Constant vsDef) (go contract)
where go = reduceRec blockNum state env
vsDivid = evalValue blockNum state divid
vsDivis = evalValue blockNum state divis
vsDef = evalValue blockNum state def
```

Once evaluated, any inner `Commit` or `Pay` (in `contract`) will get their amount scaled as follows:
* If the divisor is `0`, then the amount is replace with the default.
* If the divisor is not `0`, then the amount is multiplied by the numerator, and divided (using integer division) by the denominator.

```haskell
scaleValue :: Integer -> Integer -> Integer -> Integer -> Integer
scaleValue divid divis def val = if (divis == 0) then def else ((val * divid) `div` divis)
```

The process of scaling `Commit`s and `Pay`s is carried out by the `fetchPrimitive` function.

```haskell
fetchPrimitive idAction blockNum state (Scale divid divis def subContract) =
case fetchPrimitive idAction blockNum state subContract of
Picked (result, cont) -> Picked (scaleResult sDivid sDivis sDef result,
Scale divid divis def cont)
NoMatch -> NoMatch
MultipleMatches -> MultipleMatches
where sDivid = evalValue blockNum state divid
sDivis = evalValue blockNum state divis
sDef = evalValue blockNum state def
```

Once there are no `Commit`s or `Pay`s inside a `Scale`, it gets removed by the `simplify` funtion.

### `Let` and `Use`

**TODO**

### [Prev](./marlowe-data.md) [Up](./README.md) [Next](./embedded-marlowe.md)
### [Prev](./marlowe-data.md) [Up](./README.md) [Next](./embedded-marlowe.md)
@@ -88,6 +88,21 @@ data State = State { commits :: CommitInfo
, usedIds :: S.Set IdAction}
deriving (Eq,Ord,Show,Read)

emptyCommitInfo :: CommitInfo
emptyCommitInfo = CommitInfo { redeemedPerPerson = M.empty
, currentCommitsById = M.empty
, expiredCommitIds = S.empty
, timeoutData = M.empty
}

emptyState :: State
emptyState = State { commits = emptyCommitInfo
, choices = M.empty
, oracles = M.empty
, usedIds = S.empty
}


-- Adds a commit identifier to the timeout data map
addToCommByTim :: Timeout -> IdCommit -> TimeoutData -> TimeoutData
addToCommByTim timeout idCommit timData =

0 comments on commit 8acdca0

Please sign in to comment.
You can’t perform that action at this time.