Ethereum #53: LLL

in #ethereum8 years ago


Let’s start by diving right into an abstruse example of the low-level lisp-like language (LLL).

{ 
[[69]] (caller ) 
(return 0 (lll 
    (when (= (caller) @@69) 
        (for {} (< @i (calldatasize)) [i](+ @i 64) 
            [[ (calldataload @i) ]] (calldataload (+ @i 32)) 
        ) 
    ) 
0)) 
} 

As the name suggests, LLL is a low level language. It operates more closely to the EVM bytecode than higher level languages like Solidity or Vyper.

LLL has direct access to storage and memory of the EVM so you can determine exactly where your data sits. When you use an EVM opcode in LLL, it translates directly to the bytecode representation of that opcode. In fact, all EVM opcodes are available to LLL.

It produces smaller binaries that Solidity. As an example, reimplementing uPort's registry contract in LLL. The Solidity implementation comes in at 591 bytes, compiled with the optimize flag. Admittedly, this is pretty small. But the LLL implementation compiles to 171 bytes. So in this case the LLL version is about 70% smaller than the Solidity version. That matters a lot when a contract will be accessed many times. It also makes deployment less costly. This is an extreme case, of course. Generally LLL binaries are about 30 to 40% smaller than the equivalent in Solidity.

LLL is considered lisp like because of its syntax. There are many parenthesis compared to other smart contract languages. LLL uses symbolic expression. Symbolic expressions are a notation for nested list data.

Lisp developers generally use indentation to indicate code blocks.

LLL also uses prefix notation, where operators are placed to the left of their operands. Infix notation is the more familiar notation (2 + 3), which looks like (+ 2 3) in prefix notation. The expression (+ @i 64) adds i and 64.

“Symbolic expressions are a means of representing semistructured data in human readable text form mostly composed of symbols and lists and extensively used in the Lisp programming language.” -yourdictionary.com

An example of LLL

;; check that the caller is the node owner

(def ‘only-node-owner (node)
(when (!= caller) (get-owner-node))
(panic))

LLL uses semicolons for comments.

def starts the definition of an LLL macro. Macros can be used to clarify your code. They aren't even close to the power of Lisp macros. They're more like the C macro system. They do a simple substitution.

Here we're defining a macro called only-node-owner. This will be used as a modifier to ensure that only the node owner can execute a block of code. So when only-node-owner is used in the source, the macro will be looked up and its definition will be compiled into the bytecode.

Macros can take parameters. In this case the node's ID.

The rest of the code is what will be substituted at compile time when the macro is invoked. Here you can see the use of symbolic expressions and prefix notation. Everything is composed of lists defined within matching parentheses. Prefix notation can be seen with the use of !=. That operator comes first, and its operands follow. One of the != operands uses another macro I defined that's not shown here, called get-owner. So you can see that it's easy to build up complex code but keep things clear with the use of macros.

There is actually an LLL contract deployed on the mainnet. The registry contract of the Ethereum Name service (ENS) was written in LLL mainly because of the gas savings due to its size and the brevity of its code. It's the contract in the ENS system that's called more than any other.

Check out the github (https://github.com/ethereum/ens) for details about the LLL contracts as well as the Solidity counterparts.


▶️ DTube
▶️ IPFS

Coin Marketplace

STEEM 0.04
TRX 0.32
JST 0.078
BTC 65309.22
ETH 1721.00
USDT 1.00
SBD 0.41