Fixing contract timeouts in Glow

Issue

Scenario

Bob wants to pay Alice to sign a document.

#lang glow

@interaction([Buyer, Seller])
let payForSignature = (digest : Digest, price : Nat) => {
  deposit! Buyer -> price;

  @publicly!(Seller) let signature = sign(digest);
  // The line above is equivalent to the three below:
  //// @verifiably!(Seller) let signature = sign(digest);
  //// publish! Seller -> signature;
  //// verify! signature; // This line is itself the same as the one below:
  ////// require! isValidSignature(Seller, signature, digest);

  withdraw! Seller <- price;
  return signature;
};

After we publish, the seller verifies the signature.

The corresponding glow-asm is:

(set-participant Seller)
(def signature (sign digest0))
(add-to-publish 'signature signature)
(def tmp (@app isValidSignature Seller digest0 signature))
(require! tmp)
(@debug-label dlb2)
(participant:withdraw Seller price)
(return (@tuple))
(@label end0)

The problem is that at the final step, after Seller returns, Buyer does not, until it timeouts.

It should watch for logs and immediately return instead of waiting.

How do we change this?

We first need to understand Glow’s execution model.

A Glow contract is first deployed and then blocks are ran.

Each block has an “active participant”, in the case of the above code block, the active participant is the Seller.

To sequence execution we perform “handshakes”, which toggle the active participant. whenever we hit an interaction boundary, to wait for the right participant to run their actions.

This is done off-chain, by sending an agreement to the other participant.

ASSUMPTION

When running the interaction, we specify which participant we are. With this information, we find the first unexecuted codeblock for which we are the active participant, and run it to completion / until another boundary.

Each code block is ran by the active participant. You can see this from (set-participant Seller).

We also have “passive” participants, who listen for changes.

There are 2 sorts: “handshake” and “contract”.

“handshake” is when a participant is waiting for the other participant to send over the handshake.

“contract” is when a participant is not waiting for a handshake.

How are this changes detected? We use the JSON-RPC to watch for events. (TODO further details)

Currently this is not implemented properly, because when the Seller is done with their execution, the Buyer is still waiting for the Seller to be done, when it should have exited.

Following could be happening:

  • Buyer not parsing the event properly
  • Seller not emitting an event when it is done

As such we need to look into the following parts of the Glow Compiler / Runtime:

Runtime -> passive-code-block -> watch event -> JSON-RPC

Debugging the code

We should have a cheap way of reproducing this.

We can do this by writing some integration tests for Buyer, Seller parts of the runtime.

The integration tests

The integration test should test the following sequence of instructions:

  1. Seller emitting an event
  2. Buyer listening for the said event

If we want to be more specific:

  1. Emit Seller signing event.

What are we emitting exactly? How is it emitted?

  1. Buyer listen for the signing event.

How is watch implemented. What are we watching for exactly?

Relevant code is in participant-runtime.ss.

After review it appears we can and should also test the watch function.

Understanding EVM runtime better

The following is a high level overview of our calling convention:

  1. Setup call frame, pc, etc…
  2. Run codeblock(s)
  3. Save digest to persistent state
  4. log published data about whether to continue and commit transaction.

Why do we LOG stuff?

We want to understand when evaluation should stop. We want to know what information has been published. This is passed off-chain via an agreement, which allows the buyer to interrupt execution, and on-chain as well. The contract

What I am still confused by

What is a transaction, how are we interfacing with one in Glow? See how geth implements it. Why are we digesting frames? How do we use these digests? Why is define-consecutive-addresses doing in evm-runtime, what is with-id? Recap define-rule in gerbil scheme.

Extras

A related but separate issue would be watch, which should be eventually fixed to properly timeout as well.