Skip to content

franck44/yul-dafny

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

License

made-for-VSCode

Overview

This repository contributes a shallow embedding of Yul in Dafny. It defines the semantics of the source language, Yul, using a host language, Dafny, by translating Yul structures into Dafny structures.

The instructions of the Yul EVM-Dialect are implemented in a Dafny module Common Semantics.

What is Yul?

Yul can be seen as structured EVM bytecode: it has control flow structures (if, for, block, etc) and functions. It does not provide a stack as in the EVM, but rather variables and scopes. As a result, it is easier to read than EVM bytecode.

The following example defines a function max and uses it to store (in memory) the largest of two values.

object "Runtime" {
  code {
    function max(x, y) -> result 
    {
        result := x
        if lt(x, y) {
            result := y 
        } 
    }
    //  Main code
    let x := 8
    let y := 3
    let z := max(x, y)
    mstore(0x40, z)
  }
}

The built-in functions lt, mstore are part of the EVM dialect of Yul. This dialect has a single variable/literal type which is u256 (unsigned integers over 256 bits), so the type of all variables (x, y, result) is u256. Yul has been designed to be easy to translate into EVM bytecode. This still requires to translate the local variables into stack cells, and implement function calls with jumps instead of standard control structures.

It is also claimed that Yul is a good target for formal verification and this project endeavours to show that indeed it is.

Semantics of Yul

An informal semantics of Yul is defined in the Yul documentation. There are several formal semantics of Yul (see resources below), all them being deep embeddings in the sense that the formalisation provides:

  • the syntax of Yul, and
  • an operational or denotational semantics of the language.

In this project we propose a shallow embedding a Yul into the (host) verification-friendly language Dafny. A shallow embedding re-uses the host language features (control structures, variables declaration, scopes) to equip the source language (Yul) with a formal semantics that is inherited from the host (Dafny).

For instance, the Yul max function above can be translated into Dafny as:

method Max(x: u256, y: u256) returns (result: u256)
  //  Specification of max
  ensures result == x || result == y
  ensures result >= x && result >= y
{
  result := x;        
  if Lt(x, y) {   //  The Dafny lt instruction returns a Boolean
      result := y;
  }
}

The semantics of assignment, function declarations, if and variables' scopes is inherited from the Dafny semantics. The advantage of a shallow embedding is that it is usually easier to implement. The max function above is a good example where the body of the function in Yul and Dafny is almost the same.

The advantage is that we can leverage use the powerful verification features of Dafny to provide some guarantees about the code (e.g. ensures).
The example above embeds a specification (ensures) that defines properties of the function. The Dafny verification engine verifies at compile time that for all input x, y the properties hold.

In our project we provide the semantics of the Yul instructions as a Dafny library in the Yul folder. This library leverages the data structures (states, etc) defined in the EVM in Dafny (Dafny-EVM) project.

From Solidity to Yul

Yul is an intermediate representation (IR) that can be compiled to EVM bytecode. The solidity compiler can generate Yul as an intermediate representation of Solidity code:

> solc --ir file.sol >file.yul

The Yul code in 'file.yul' can be compiled into EVM bytecode:

> solc --yul file.yul >file.txt

Formal verification of Yul and EVM bytecode

With a shallow embedding of Yul, we get the ability to prove some properties of Yul programs. We can leverage this feature to prove properties of EVM bytecode generated from Yul.

An example is provided in this folder. The verification works as follow:

  • some properties are verified on the Yul code
  • we then compile the Yul code into EVM bytecode
  • we prove that the bytecode for each function (e.g. max) simulates the Yul code.

Examples

The examples folder contains examples of Yul to Dafny translation and verification.

Resources

About

Yul semantics and verification in Dafny

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published