diff --git a/lib/primitives.ml b/lib/primitives.ml index 228e6d2..1c75c07 100644 --- a/lib/primitives.ml +++ b/lib/primitives.ml @@ -2,6 +2,7 @@ type address = string type uint = UInt of int type sint = SInt of int + let caller () = assert false (* binary operators for unsigned int *) @@ -15,3 +16,10 @@ let ( + ) (SInt x) (SInt y) = SInt (x + y) let ( - ) (SInt x) (SInt y) = SInt (x - y) let ( * ) (SInt x) (SInt y) = SInt (x * y) let ( / ) (SInt x) (SInt y) = SInt (x / y) + +(* operations for boolean *) +(* let ( && ) b1 b2 = b1 && b2 +let ( || ) b1 b2 = b1 || b2 +let not b = not b +let (==) b1 b2 = b1 == b2 +let (!=) b1 b2 = not (b1 == b2) *) \ No newline at end of file diff --git a/lib/primitives.mli b/lib/primitives.mli index 115d3fa..450dd44 100644 --- a/lib/primitives.mli +++ b/lib/primitives.mli @@ -1,6 +1,7 @@ type address type uint = UInt of int type sint = SInt of int +(* type bool_ = bool *) val caller : unit -> address @@ -15,3 +16,9 @@ val ( + ) : sint -> sint -> sint val ( - ) : sint -> sint -> sint val ( * ) : sint -> sint -> sint val ( / ) : sint -> sint -> sint + +(* val ( && ) : bool_ -> bool_ -> bool_ +val ( || ) : bool_ -> bool_ -> bool_ +val not : bool_ -> bool_ +val ( == ) : bool_ -> bool_ -> bool_ +val ( != ) : bool_ -> bool_ -> bool_ *) \ No newline at end of file diff --git a/sample/contract_test/SimpleStorageIf.test.js b/sample/contract_test/SimpleStorageIf.test.js new file mode 100644 index 0000000..06466ec --- /dev/null +++ b/sample/contract_test/SimpleStorageIf.test.js @@ -0,0 +1,75 @@ +const assert = require('assert'); +const ganache = require('ganache'); +const { Web3 } = require('web3'); + +const web3 = new Web3(ganache.provider()) + +const { abi, bytecode } = require('../contracts/SimpleStorageIf.json'); + +describe('SimpleStorageIf', async () => { + let accounts; + let contract; + let from; + + beforeEach(async () => { + accounts = await web3.eth.getAccounts(); + from = accounts[0]; + + contract = await new web3.eth.Contract(JSON.parse(JSON.stringify(abi))) + .deploy({ data: bytecode }) + .send({ from: accounts[0], gas: 1000000 }); + + await contract.methods.set(100).send({ from: from }); + }); + + it('should deploy', () => { + assert.ok(contract.options.address); + }); + + it('getting value', async () => { + const v = await contract.methods.get(from).call(); + assert.equal(100, v); + }); + + it('setting value', async () => { + await contract.methods.set(200).send({ from: from }); + const v = await contract.methods.get().call(); + assert.equal(200, v); + }); + + it('is less than value', async () => { + const v = await contract.methods.lt(1, 2).call(); + assert.equal(true, v); + }); + + it('is greater than value', async () => { + const v = await contract.methods.gt(1, 2).call(); + assert.equal(false, v); + }); + + it('is less than equal value', async () => { + const v = await contract.methods.lte(1, 1).call(); + assert.equal(true, v); + }); + + it('is greater than equal value', async () => { + const v = await contract.methods.gte(1, 1).call(); + assert.equal(true, v); + }); + + it('calculate xor', async () => { + const v = await contract.methods.xor(true, true).call(); + assert.equal(false, v); + }); + + it('equalize xor2 to xor', async () => { + const v = await contract.methods.xor(false, true).call(); + const v2 = await contract.methods.xor2(false, true).call(); + assert.equal(v, v2); + }); + + it('calculate max', async () => { + const v = await contract.methods.max(5, 3).call(); + assert.equal(5, v); + }); +}); \ No newline at end of file diff --git a/sample/contracts/SimpleStorageIf.json b/sample/contracts/SimpleStorageIf.json new file mode 100644 index 0000000..cd1aeda --- /dev/null +++ b/sample/contracts/SimpleStorageIf.json @@ -0,0 +1,84 @@ +{ + "contractName": "SimpleStorageIf", + "abi": [ + { + "name": "max", + "type": "function", + "inputs": [ + { "name": "", "type": "int256" }, { "name": "", "type": "int256" } + ], + "outputs": [ { "name": "", "type": "int256" } ], + "stateMutability": "view" + }, + { + "name": "xor2", + "type": "function", + "inputs": [ + { "name": "", "type": "bool" }, { "name": "", "type": "bool" } + ], + "outputs": [ { "name": "", "type": "bool" } ], + "stateMutability": "view" + }, + { + "name": "xor", + "type": "function", + "inputs": [ + { "name": "", "type": "bool" }, { "name": "", "type": "bool" } + ], + "outputs": [ { "name": "", "type": "bool" } ], + "stateMutability": "view" + }, + { + "name": "gte", + "type": "function", + "inputs": [ + { "name": "", "type": "int256" }, { "name": "", "type": "int256" } + ], + "outputs": [ { "name": "", "type": "bool" } ], + "stateMutability": "view" + }, + { + "name": "lte", + "type": "function", + "inputs": [ + { "name": "", "type": "int256" }, { "name": "", "type": "int256" } + ], + "outputs": [ { "name": "", "type": "bool" } ], + "stateMutability": "view" + }, + { + "name": "gt", + "type": "function", + "inputs": [ + { "name": "", "type": "int256" }, { "name": "", "type": "int256" } + ], + "outputs": [ { "name": "", "type": "bool" } ], + "stateMutability": "view" + }, + { + "name": "lt", + "type": "function", + "inputs": [ + { "name": "", "type": "int256" }, { "name": "", "type": "int256" } + ], + "outputs": [ { "name": "", "type": "bool" } ], + "stateMutability": "view" + }, + { + "name": "get", + "type": "function", + "inputs": [], + "outputs": [ { "name": "", "type": "int256" } ], + "stateMutability": "view" + }, + { + "name": "set", + "type": "function", + "inputs": [ { "name": "", "type": "int256" } ], + "outputs": [], + "stateMutability": "nonpayable" + } + ], + "bytecode": "335f556102f36100115f396102f35ff3fe6100076102d5565b8063e5c19b2d146101a25780636d4ce63c1461018c5780633088003814610163578063ac08973d1461013a57806350b978401461011157806344f45307146100e8578063e95e21bc146100bf578063fd3ce04014610096576381fe57861461006d575f80fd5b6100906100786102c4565b61008260016102dd565b61008b5f6102dd565b61028f565b506102ca565b6100b96100a16102c4565b6100ab60016102e7565b6100b45f6102e7565b61022c565b506102ca565b6100e26100ca6102c4565b6100d460016102e7565b6100dd5f6102e7565b6101f4565b506102ca565b61010b6100f36102c4565b6100fd60016102dd565b6101065f6102dd565b6101eb565b506102ca565b61013461011c6102c4565b61012660016102dd565b61012f5f6102dd565b6101e2565b506102ca565b61015d6101456102c4565b61014f60016102dd565b6101585f6102dd565b6101da565b506102ca565b61018661016e6102c4565b61017860016102dd565b6101815f6102dd565b6101d2565b506102ca565b61019c6101976102c4565b6101cd565b506102ca565b6101c36101be6101b06102c4565b6101b95f6102dd565b6101c8565b6102bf565b6102d1565b905090565b908190565b929192109190565b929192119190565b92919211159190565b92919210159190565b92919280918115610223575b8161021b575b501581610213575b509190565b90505f61020e565b90505f610206565b80925091610200565b909291925f918060011461026a575f1461024657505b9190565b80600114610261575f1461025a575b610242565b505f610255565b50506001610255565b5080600114610287575f1461027f575b610242565b50600161027a565b50505f61027a565b90929192808211915f9290816001146102b657505f146102af57505b9190565b90506102ab565b925050506102ab565b600155565b60015490565b5f5260205ff35b5f80f35b5f3560e01c90565b6020026004013590565b6102f0906102dd565b9056", + "deployedBytecode": "6100076102d5565b8063e5c19b2d146101a25780636d4ce63c1461018c5780633088003814610163578063ac08973d1461013a57806350b978401461011157806344f45307146100e8578063e95e21bc146100bf578063fd3ce04014610096576381fe57861461006d575f80fd5b6100906100786102c4565b61008260016102dd565b61008b5f6102dd565b61028f565b506102ca565b6100b96100a16102c4565b6100ab60016102e7565b6100b45f6102e7565b61022c565b506102ca565b6100e26100ca6102c4565b6100d460016102e7565b6100dd5f6102e7565b6101f4565b506102ca565b61010b6100f36102c4565b6100fd60016102dd565b6101065f6102dd565b6101eb565b506102ca565b61013461011c6102c4565b61012660016102dd565b61012f5f6102dd565b6101e2565b506102ca565b61015d6101456102c4565b61014f60016102dd565b6101585f6102dd565b6101da565b506102ca565b61018661016e6102c4565b61017860016102dd565b6101815f6102dd565b6101d2565b506102ca565b61019c6101976102c4565b6101cd565b506102ca565b6101c36101be6101b06102c4565b6101b95f6102dd565b6101c8565b6102bf565b6102d1565b905090565b908190565b929192109190565b929192119190565b92919211159190565b92919210159190565b92919280918115610223575b8161021b575b501581610213575b509190565b90505f61020e565b90505f610206565b80925091610200565b909291925f918060011461026a575f1461024657505b9190565b80600114610261575f1461025a575b610242565b505f610255565b50506001610255565b5080600114610287575f1461027f575b610242565b50600161027a565b50505f61027a565b90929192808211915f9290816001146102b657505f146102af57505b9190565b90506102ab565b925050506102ab565b600155565b60015490565b5f5260205ff35b5f80f35b5f3560e01c90565b6020026004013590565b6102f0906102dd565b9056" +} \ No newline at end of file diff --git a/sample/src/simple_hash_if.ml b/sample/src/simple_hash_if.ml deleted file mode 100644 index 70acd17..0000000 --- a/sample/src/simple_hash_if.ml +++ /dev/null @@ -1,31 +0,0 @@ -open OCamYul.Primitives - -module SimpleHashIf : sig - type storage - type mut_storage - - val set : address * uint -> storage -> mut_storage -> unit * storage - val get : address -> storage -> mut_storage -> uint * storage - val set_caller : uint -> storage -> mut_storage -> unit * storage -end = struct - type storage = unit - type mut_storage = (address, uint) Hashtbl.t - - let set (x, y) () h = - (if true then let a = UInt 2 in let b = UInt 3 in let c = a +^ b in Hashtbl.replace h x y else ()); let a = if false then SInt 1 else SInt 2 in let b = SInt 2 in let c = a + b in - ((), ()) - - let get x () h = - let a = - if let c = true in c - then SInt 1 - else let b = if false - then SInt 3 - else SInt 4 in b + b - in (Hashtbl.find h x, ()) - - - let set_caller x () h = - Hashtbl.replace h (caller ()) x; - ((), ()) -end diff --git a/sample/src/simple_storage_if.ml b/sample/src/simple_storage_if.ml new file mode 100644 index 0000000..b2abc3d --- /dev/null +++ b/sample/src/simple_storage_if.ml @@ -0,0 +1,36 @@ +open OCamYul.Primitives + +module SimpleStorageIf : sig + type storage + + val set : sint -> storage -> unit * storage + val get : unit -> storage -> sint * storage + val lt : sint * sint -> storage -> bool * storage + val gt : sint * sint -> storage -> bool * storage + val lte : sint * sint -> storage -> bool * storage + val gte : sint * sint -> storage -> bool * storage + val xor : bool * bool -> storage -> bool * storage + val xor2 : bool * bool -> storage -> bool * storage + val max : sint * sint -> storage -> sint * storage +end = struct + type storage = sint + + let set n _ = ((), n) + let get () s = (s, s) + let lt (x, y) s = (x < y, s) + let gt (x, y) s = (x > y, s) + let lte (x, y) s = (x <= y, s) + let gte (x, y) s = (x >= y, s) + + let xor (x, y) s = + let b = (x || y) && not (x && y) in + (b, s) + + let xor2 (x, y) s = + let b = if x then (if y then false else true) else (if y then true else false) in + (b, s) + + let max (n, m) s = + let b = n > m in + ((if b then n else m), s) +end diff --git a/src/abi.ml b/src/abi.ml index ebcfa60..0512eb0 100644 --- a/src/abi.ml +++ b/src/abi.ml @@ -9,7 +9,7 @@ type abi = { and input = { input_name : string; input_type : param_type } and output = { output_name : string; output_type : param_type } and abi_type = Func -and param_type = Uint256 | Int256 | Address +and param_type = Uint256 | Int256 | Address | Bool and state_mutability = Pure | View | Nonpayable | Payable let stronger_mutability mut1 mut2 = @@ -23,6 +23,7 @@ let string_of_param_type = function | Uint256 -> "uint256" | Int256 -> "int256" | Address -> "address" + | Bool -> "bool" let string_of_mutability = function | Pure -> "pure" diff --git a/src/abi.mli b/src/abi.mli index 2bbaab5..33fe050 100644 --- a/src/abi.mli +++ b/src/abi.mli @@ -10,7 +10,7 @@ type abi = { and input = { input_name : string; input_type : param_type } and output = { output_name : string; output_type : param_type } and abi_type = Func -and param_type = Uint256 | Int256 | Address +and param_type = Uint256 | Int256 | Address | Bool and state_mutability = Pure | View | Nonpayable | Payable val string_of_mutability : state_mutability -> string diff --git a/src/anormal_ir.ml b/src/anormal_ir.ml index 1fcb5fc..8ed332a 100644 --- a/src/anormal_ir.ml +++ b/src/anormal_ir.ml @@ -9,7 +9,26 @@ let const_to_aval = function | _ -> assert false let get_bop s = - if s = "+^" then UAdd + match s with + | "+^" -> UAdd + | "-^" -> USub + | "*^" -> UMul + | "/^" -> UDiv + | "+" -> SAdd + | "-" -> SSub + | "*" -> SMul + | "/" -> SDiv + | "&&" -> And + | "||" -> Or + | "not" -> Not + | "=" -> Eq + | "!=" -> Neq + | "<" -> Lt + | ">" -> Gt + | "<=" -> Lte + | ">=" -> Gte + | _ -> assert false + (*if s = "+^" then UAdd else if s = "-^" then USub else if s = "*^" then UMul else if s = "/^" then UDiv @@ -17,7 +36,7 @@ let get_bop s = else if s = "-" then SSub else if s = "*" then SMul else if s = "/" then SDiv - else assert false + else assert false*) (* operation functions made usable especially, such as replace caller*) let pdot_to_aval p s = @@ -32,6 +51,7 @@ let pdot_to_aval p s = if Ident.name id = "OCamYul" then if s = "caller" then Caller else Bop (get_bop s) else assert false + | Path.Pident id -> if Ident.name id = "Stdlib" then Bop (get_bop s) else assert false | _ -> assert false (* The first argument p is a storage. To check whether the storage changes, it is needed. diff --git a/src/normalized_common_ast.ml b/src/normalized_common_ast.ml index 0f631bf..6dd371f 100644 --- a/src/normalized_common_ast.ml +++ b/src/normalized_common_ast.ml @@ -1,4 +1,4 @@ -type bop = UAdd | USub | UMul | UDiv | SAdd | SSub | SMul | SDiv +type bop = UAdd | USub | UMul | UDiv | SAdd | SSub | SMul | SDiv | And | Or | Not | Eq | Neq | Lt | Gt | Lte | Gte type value = | Var of string @@ -20,6 +20,15 @@ let string_of_bop = function | SSub -> "-" | SMul -> "*" | SDiv -> "/" + | And -> "&&" + | Or -> "||" + | Not -> "not" + | Eq -> "=" + | Neq -> "!=" + | Lt -> "<" + | Gt -> ">" + | Lte -> "<=" + | Gte -> ">=" let string_of_value = function | Var s -> s diff --git a/src/normalized_common_ast.mli b/src/normalized_common_ast.mli index cef95ef..4b75e6c 100644 --- a/src/normalized_common_ast.mli +++ b/src/normalized_common_ast.mli @@ -1,5 +1,5 @@ (** binary operators for signed/unsigned integers *) -type bop = UAdd | USub | UMul | UDiv | SAdd | SSub | SMul | SDiv +type bop = UAdd | USub | UMul | UDiv | SAdd | SSub | SMul | SDiv | And | Or | Not | Eq | Neq | Lt | Gt | Lte | Gte (** a common part of value expressions *) type value = diff --git a/src/ocamyul.ml b/src/ocamyul.ml index 29237ef..caf47fe 100644 --- a/src/ocamyul.ml +++ b/src/ocamyul.ml @@ -50,7 +50,9 @@ let abi_input_of_typ typ = let abi_of_constr = function | Ttyp_constr (Path.Pident s, _, _) -> let s = Ident.name s in - if s = "unit" then [] else raise Not_implemented + if s = "unit" then [] + else if s = "bool" then [ { input_name = ""; input_type = Bool } ] + else raise Not_implemented | Ttyp_constr (Path.Pdot (_, s), _, _) -> if s = "uint" then [ { input_name = ""; input_type = Uint256 } ] else if s = "sint" then [ { input_name = ""; input_type = Int256 } ] @@ -71,7 +73,9 @@ let abi_output_of_typ typ = let abi_of_constr = function | Ttyp_constr (Path.Pident s, _, _) -> let s = Ident.name s in - if s = "unit" then [] else raise Not_implemented + if s = "unit" then [] + else if s = "bool" then [ { output_name = ""; output_type = Bool } ] + else raise Not_implemented | Ttyp_constr (Path.Pdot (_, s), _, _) -> if s = "uint" then [ { output_name = ""; output_type = Uint256 } ] else if s = "sint" then [ { output_name = ""; output_type = Int256 } ] @@ -170,6 +174,7 @@ let params_of_external_function { abi_inputs = inputs; _ } = | Uint256 -> decode_as_uint | Int256 -> decode_as_uint | Address -> decode_as_address + | Bool -> decode_as_bool in let rec aux n = function | [] -> [] diff --git a/src/yul_ast.ml b/src/yul_ast.ml index a793ffd..f32309d 100644 --- a/src/yul_ast.ml +++ b/src/yul_ast.ml @@ -230,6 +230,7 @@ let get_hash_slot = "getHashSlot" let selector = "selector" let decode_as_uint = "decodeAsUint" let decode_as_address = "decodeAsAddress" +let decode_as_bool = "decodeAsBool" let uint_add = "uintAdd" let uint_sub = "uintSub" let uint_mul = "uintMul" @@ -366,6 +367,17 @@ let decode_as_address_def = [ Assign ((return_arg, []), FunctionCall (decode_as_uint, [ ID arg ])) ] ) + let decode_as_bool_def = + let arg = "offset" in + let return_arg = "v" in + FunctionDef + ( decode_as_bool, + [ arg ], + [ return_arg ], + [ Assign ((return_arg, []), FunctionCall (decode_as_uint, [ ID arg ])) ] + ) + + let default_revert_def = Default [ Exp (EVM (Revert (Literal (Dec 0), Literal (Dec 0)))) ] @@ -544,11 +556,11 @@ let sint_div_def = adding some functions to default happens in different phases *) let default_function_defs = - ref [ selector_def; decode_as_uint_def; decode_as_address_def ] + ref [ selector_def; decode_as_uint_def; decode_as_address_def; decode_as_bool_def ] let reset_default_function_defs () = default_function_defs := - [ selector_def; decode_as_uint_def; decode_as_address_def ] + [ selector_def; decode_as_uint_def; decode_as_address_def; decode_as_bool_def ] let get_default_function_defs () = !default_function_defs diff --git a/src/yul_ast.mli b/src/yul_ast.mli index c253480..e1cbff1 100644 --- a/src/yul_ast.mli +++ b/src/yul_ast.mli @@ -73,6 +73,7 @@ val get_hash_slot : id val selector : id val decode_as_uint : id val decode_as_address : id +val decode_as_bool : id val uint_add : id val uint_sub : id val uint_mul : id @@ -93,6 +94,7 @@ val get_hash_slot_def : statement val selector_def : statement val decode_as_uint_def : statement val decode_as_address_def : statement +val decode_as_bool_def : statement val default_revert_def : default val uint_add_def : statement val uint_sub_def : statement diff --git a/src/yul_compile.ml b/src/yul_compile.ml index a4790d6..fd49b39 100644 --- a/src/yul_compile.ml +++ b/src/yul_compile.ml @@ -55,7 +55,17 @@ let letexp_to_yul = function | SAdd -> gen_bop_call sint_add_def sint_add | SSub -> gen_bop_call sint_sub_def sint_sub | SMul -> gen_bop_call sint_mul_def sint_mul - | SDiv -> gen_bop_call sint_div_def sint_div) + | SDiv -> gen_bop_call sint_div_def sint_div + | And -> assert false + | Or -> assert false + | Eq -> EVM (Eq (v1, v2)) + | Neq -> EVM (Iszero (EVM (Eq (v1, v2)))) + | Lt -> EVM (Lt (v1, v2)) + | Gt -> EVM (Gt (v1, v2)) + | Lte -> EVM (Iszero (EVM (Gt (v1, v2)))) + | Gte -> EVM (Iszero (EVM (Lt (v1, v2)))) + | _ -> assert false) + | LApp (Bop Not,[v]) -> let v = aval_to_yul v in EVM (Iszero v) | LApp (HashReplace, [ h; x; y ]) -> EVM (Sstore @@ -95,6 +105,14 @@ let rec translate_body_aux e acc ret_vars= let v = aval_to_yul v in let acc = Switch (v, [Case(Dec 1, e1_block); Case(Dec 0, e2_block)], Default []) :: (Let((List.hd vars, List.tl vars), Literal (Dec 0))) :: acc in (*initialization with 0*) translate_body_aux e2 acc ret_vars + | LApp (Bop ((And | Or) as b), [v1; v2]) -> + assert (List.length vars = 1); + let var = List.hd vars in + let v1 = aval_to_yul v1 in + let v2 = aval_to_yul v2 in + let bexp = (match b with And -> v1 | Or -> EVM (Iszero v1)| _ -> assert false) in + let acc = (Yul_ast.If (bexp, [Assign ((var, []), v2)])) :: Let ((var, []), v1) :: acc in + translate_body_aux e2 acc ret_vars |_ -> let acc = Let ((List.hd vars, List.tl vars), letexp_to_yul e1) :: acc in translate_body_aux e2 acc ret_vars) in a | If (v, e1, e2) ->