ECLL:Ethereum C類似言語
※CLLの翻訳です。
ECLLの目的は契約が記述し易く、しかも、Ethereumのスクリプトコードに容易にかつ効率的に翻訳することのできる単純な言語を提供することである。ECLLはポインタ、直接的な記憶装置への読み書き、より従来型の条件文や反復文や配列や変数の構文では使い勝手の良い無条件分岐のような概念を抽象化することによって排除している。第一級関数への対応を含む関数に関する対応はEHLL(Ethereum高水準言語)という名称が付けられている将来的に計画されているより強力ではあるものの効率性では劣る言語に委ねられている。
本仕様は3つの部分から成る。最初の部分はLISP様式のS式の構文を使って形式化された抽象構文木(abstract syntax tree)として言語を定義する。2つ目の部分はそれぞれの操作の動作についての仕様を与える。3つ目の部分は抽象構文木のスクリプト中での表され方を示す。
抽象構文木
式(expression)は次のように定義される。
・数字は式である。
・変数名は式である。
・("arr" a b)は式である。ここで、a及びbは式である。この式はC言語におけるa[b]と同等のものである。
・(OP a b)は式である。ここで、OPは(+ - * / % s/ s% ^ < > <= >= ==)の何れかである。
・(OP a)は式である。ここで、OPは(- !)の何れかである。
・特殊値tx.datan、tx.value、tx.sender、contract.address、block.number、block.difficulty、block.timestamp、block.parenthash及びblock.basefeeは式である。
・("pseudo" a b)は式である。ここで、aは擬似配列(pseudoarray)であり、bは式である。
・("fun" a b...)は式である。ここで、aは戻り値のある関数(returning function)であり、b...は正しい数の引数を含む。
擬似関数(pseudofunction)は次のように定義される。
・block.contract_storageは擬似関数である。
擬似配列は次のように定義される。
・tx.data及びcontract.storageは擬似配列である。
・("pfun" a b...)は擬似配列である。ここで、aは擬似関数であり、b...は正しい数の引数を含む。
contract.storageは変更可能な擬似配列(mutable pseudoarray)である。それ以外の2つは変更不能な擬似配列(immutable pseudoarray)である。
戻り値のある関数は次のように定義される。
・block.account_balance、sha256、sha3、ripemd160及びecvalidは戻り値のある関数である。
・arrayは戻り値のある関数である。
複数の戻り値のある関数(multireturning function)は次のように定義される。
・ecsign及びecrecoverは複数の戻り値のある関数である。
複数の値を有する式(multiexpression)は次のように定義される。
・("mfun" a b...)は複数の値を有する式である。ここで、aは複数の戻り値のある関数であり、b...は正しい数の引数を含む。
戻り値のない関数(acting function)は次のように定義される。
・mktxは戻り値のない関数である。
代入式の左辺に配置できる式(left-hand-side expression)は次のように定義される。
・変数は代入式の左辺に配置できる式である。
・("arr" a b)は代入式の左辺に配置できる式である。ここで、aは代入式の左辺に配置できる式であり、bは式である。
・("pseudo" a b)は代入式の左辺に配置できる式である。ここで、aは変更可能な擬似配列であり、bは式である。
文(statement)は次のように定義される。
・("afun" a b...)は文である。ここで、aは戻り値のない関数であり、b...は正しい数の引数を含む。
・("set" a b)は文である。ここで、aは代入式の左辺に配置できる式であり、bは式である。
・("mset" a... b)は文である。ここで、a...は正しい数の代入式の左辺に配置できる式であり、bは複数の値を有する式である。
・("seq" a...)は文である。ここで、a...は複数の文である。
・("if" a b)は文である。ここで、aは式であり、bは文である。
・("if" a1 b1 "elif" a2 b2 "elif" a3 b3 ...)は文である。ここで、akは式であり、bkは文である。
・("if" a1 b1 "elif" a2 b2 "elif" a3 b3 ... "else" c)は文である。ここで、akは式であり、bk及びcは文である。
・("while" a b)は文である。ここで、aは式であり、bは文である。
・"exit"は文である。
定義
n個の変数がある場合には、翻訳の前に、それぞれの変数には[0,n-1]の範囲の添字が割り当てられる。ind(x)を変数xの添字とし、M[x]を契約におけるx番目の記憶領域とする。
代入式の右辺に配置される式の文脈では、
・算術演算の動作は通常の場合と同様である。たとえば、(* (+ 3 5) (+ 4 8))は96を返す。
・a && bは!(!(a) + !(b))の略記である。
・a || bは!(!(a + b))の略記である。
・変数xはM[ind(x)]となる。
・("arr" a b)はM[M[ind(a)]+b]を返す。
・tx.datanは取引におけるデータ領域の数を返す。
・("pseudo" "tx.data" i)は取引におけるi番目のデータ領域の値を返す。ただし、i >= tx.datanである場合には、0を返す。
・tx.sender、tx.value、block.basefee、block.difficulty、block.timestamp、block.parenthash及びcontract.addressは名前から明らかなそれぞれの値を返す。
・("pseudo "contract.storage" i)は契約におけるi番目の記憶領域を返す。
・("fun" "block.account_balance" a)は口座番号aの残高を返す。
・("pseudo" ("pfun" "block.contract_storage" a) b)は口座番号aのb番目の契約記憶領域を返す。
・("mfun" "ecsign" hash key)は署名からのv,r,s値を返す。
・("mfun" "ecrcover" h v r s)は公開鍵x,yを返す。
・("mfun" "sha256" len arr)はM[ind(arr)]から長さlen分の列のSHA256ハッシュ値を返す。sha3及びripemd160も同様に動作する。
・("fun" "array")は2^160 + M[2^256 - 1]を返し、M[2^256 - 1]に2^160 + M[2^256 - 1]を設定する。
代入式の左辺に配置できる式の文脈では、
・変数xはind(x)を返す。
・("arr" a b)はa + bを返す。ここで、a及びbは既に評価が終わっている。
・("pseudo" a b)は設定されている場合には擬似配列の指示対象のa番目からb番目までの値を変更するオブジェクトを返す。直接記憶領域への値の設定を行う訳ではない。
文の文脈では、
・("afun" "mktx" dest value dn ds)はvalue分のetherをM[ind(ds)]からdn個のデータ領域と共にdestに送金する取引を作成する。
・("set" a b)はM[a]にbを設定する。ここで、a及びbは既に評価が終わっている。
・("mset" a... b)はa...に格納されているそれぞれの値が指し示す記憶領域に複数の値を有する式bからのそれぞれの値を順番に設定する。
・("if" a b)、("while" a b)などは他の言語の場合と同様に動作する。
構文
・算術演算は中置記法を使って実行される。演算子の優先順位はC++と同様である。すなわち、^、* / % s/ s%、+ -、< <= > >=、==、&&、||の順番である。結局のところ、算術演算式は通常の場合と同様に振舞う。たとえば、(+ (* 3 5) (* x y))は(3 * 5 + x * y)となり、(* (+ 3 5) (+ x y))は*1となる。
・("arr" a b)及び("pseudo" a b)は何れもa[b]と表される。
・("fun" a b...)、("mfun" a b...)、("afun" a b...)及び("pfun" a...)は何れもa(b1,b2, ... ,bn)と表される。
・複数の値を有する式はa1, a2, ... anと表される。
・("set" a b)及び("mset a... b)はそれぞれa = b、a1, a2 ... ,an = bと表される。
・("seq" a...)は別々の行に記述される一連の文から成る。
・("if" a b)及び("while" a b)はPythonにおける場合と同様に表される。キーワードの直ぐ後に式aが置かれ、次の行に字下げされた文bが置かれる。
・値は整数又は引用符で囲まれた文字列として表され、16進数として取り扱われる。
・exitはスクリプトの実行を終了する。
例:
階乗:
x = 1
n = 1
while x < 10:
n = n * x
x = x + 1
Fibonacci数列:
a = array()
a[0] = 1
a[1] = 1
i = 2
while i < 70:
a[i] = a[i-1] + a[i-2]
i = i + 1
mktx("0676d13c8d2cf5e9e988cc3b5f6dfb5a6a3938fa",a[69],70,a)
実際的な例――譲渡可能な口座を作成するための単純な転送契約
if tx.value < tx.basefee * 200:
exit
else:
a = contract.storage[2^256 - 1]
if !(tx.sender == a) && (tx.sender > 0):
exit
if tx.data[0] == 2^160:
contract.storage[2^256 - 1] = tx.data[1]
else:
if a == 0:
contract.storage[2^256 - 1] = tx.sender
o = array()
i = 0
while i < tx.datan - 1:
o[i] = tx.data[i+1]
i = i + 1
mktx(tx.data[0],tx.value - tx.basefee * 250,i,o)
別の構文:C++様式
・("seq" a...)はセミコロンで文を分離することで実装される。
・("if" a b)及び("while" a b)はC++における場合と同様に表される。式aは丸括弧で囲まれ、文bは中括弧で囲まれる。
別の構文:Lisp様式
抽象構文木が直接コードとして使用される。ただし、("fun" a b...)、("pfun" a b...)及び("mfun" a b...)は(a b...)と表される。
*1:3 + 5) * (x + y