diff --git a/examples/wrc202.nim b/examples/wrc202.nim index add2a39..e455de4 100644 --- a/examples/wrc202.nim +++ b/examples/wrc202.nim @@ -3,34 +3,96 @@ import ../nimplay/nimplay_macros import ../nimplay/types # import endians -import macros +# import macros import stint -expandMacros: - contract("MyContract"): - - # proc becomeKing(): uint256 = - # var a: int32 - # a = (2^31-1).int32 - # a += 1.int32 - # return a.stuint(256) +# expandMacros: +contract("MyContract"): var a: uint256 - b: address + owner: address - proc get_sender222(): address = - if true: - return msg.sender + # proc default*() {.payable.} + # ... - proc get_sender(): address = - if true: - return msg.sender + # proc publicGetSender(slot_number: uint256) = + # discard + # dumpAstGen: + # tmp_func_get_storage_owner() + # proc tmp_func_get_storage_owner(): address = + # var + # tmp: array[32, byte] + # position: array[32, byte] + # slot_number: uint32 = 1 + # position[0..3] = cast[array[4, byte]](slot_number) + # storageLoad(position, addr tmp) + # var output: address + # output[0..20] = tmp[0..20] + # return output - proc publicGetSender*(): address = - return msg.sender + proc get_storage*(): uint256 = + return self.a - proc addition(in_a: uint256, in_b: uint256): uint256 = - return in_a + in_b + proc set_storage*(in_a: uint256) = + self.a = in_a + + # proc set_storage*(a: uint256) = + # discard + + # proc set_storage*() = + # var + # pos = 0.stuint(32).toByteArrayBE + # value = 999999999999999.stuint(256).toByteArrayBE + # storageStore(pos, addr value) + + # return msg.sender + + # proc tmp_func_get_storage_owner(slot_number: uint256): address = + # var tmp: array[32, byte] + # var position = slot_number.toByteArrayBE + # storageLoad(position, addr tmp) + # var output: address + # output[0..19] = tmp[12..31] + # return output + + # proc addition(in_a: uint256, in_b: uint256): uint256 = + # return in_a + in_b + + # proc get*(): uint256 = + # var tmp: array[32, byte] + # var pos = 0.stuint(32).toByteArrayBE + # storageLoad(pos, addr tmp) + # return Uint256.fromBytesBE(tmp) + + # proc set*() = + # var tmp = 556677.stuint(256).toByteArrayBE + # var pos = 0.stuint(32).toByteArrayBE + # storageStore(pos, addr tmp) + + # proc get_storage*(slot_number: uint256): uint256 = + # var tmp: uint256 + # var pos = cast[bytes32](slot_number) + # storageLoad(pos, addr tmp) + # return tmp + + # proc set_storage*(slot_number: uint256, value: uint256) = + # var tmp: array[32, byte] = value.toByteArrayBE + # var pos = cast[bytes32](slot_number) + # storageStore(pos, addr tmp) + + # proc setOwner(in_owner: address) = + # self.owner = in_owner + + # proc getOwner(): address = + # return self.owner + + # proc get_sender222(): address = + # if true: + # return msg.sender + + # proc get_sender(): address = + # if true: + # return msg.sender # getCaller(addr tmp_addr) diff --git a/nimplay.nim b/nimplay.nim new file mode 100644 index 0000000..65e8c07 --- /dev/null +++ b/nimplay.nim @@ -0,0 +1,2 @@ +import nimplay/nimplay_macros + diff --git a/nimplay.nimble b/nimplay.nimble new file mode 100644 index 0000000..c19e056 --- /dev/null +++ b/nimplay.nimble @@ -0,0 +1,30 @@ +version = "0.1.0" +author = "Status Research & Development GmbH" +description = "Nimplay - Ethereum smart contracts language" +license = "Apache License 2.0" +skipDirs = @["examples", "docs"] + +# Dependencies + +requires "nim >= 0.18.1", "stint", "nimcrypto" + +proc buildExample(name: string) = + exec "nim c -d:release --out:examples/" & name & ".wasm examples/" & name + exec "./postprocess.sh examples/" & name & ".wasm" + + +proc buildTool(name: string) = + exec "nim c -d:release --out:tools/" & name & " tools/" & name + + +task examples, "Build examples": + buildExample("wrc20") + buildExample("wrc202") + # buildExample("hello") + # buildExample("hello2") + # buildExample("hello3") + + +task tools, "Build tools": + buildTool("abi_gen") + buildTool("k256_sig") diff --git a/nimplay/builtin_keywords.nim b/nimplay/builtin_keywords.nim index 153edcf..d8304c6 100644 --- a/nimplay/builtin_keywords.nim +++ b/nimplay/builtin_keywords.nim @@ -1,34 +1,66 @@ import macros import strformat import tables +import strutils +import sequtils + +import ./types, ./utils, ./storage -proc is_message_sender(node: NimNode): bool = +proc is_dot_variable(node: NimNode): bool = if node.kind == nnkDotExpr: var correct_types = (node[0].kind, node[1].kind) == (nnkIdent, nnkIdent) var correct_length = node.len == 2 if correct_types and correct_length: - if node[0].strVal == "msg" and node[1].strVal == "sender": - return true + return true return false -proc is_keyword(node: NimNode): (bool, string) = +proc is_message_sender(node: NimNode): bool = + if is_dot_variable(node) and node[0].strVal == "msg" and node[1].strVal == "sender": + return true + return false + + +proc has_self(node: NimNode, global_ctx: GlobalContext): bool = + if is_dot_variable(node) and node[0].strVal == "self": + if node[1].strVal in global_ctx.global_variables: + return true + else: + raiseParserError( + fmt"Invalid global variable {node[1].strVal}, has not been defined.", + node + ) + return false + + +proc has_self_assignment(node: NimNode, global_ctx: GlobalContext): bool = + if node.kind == nnkAsgn: + if is_dot_variable(node[0]) and node[0][0].strVal == "self": + return true + return false + + +proc is_keyword(node: NimNode, global_ctx: GlobalContext): (bool, string) = if is_message_sender(node): return (true, "msg.sender") + elif has_self_assignment(node, global_ctx): + return (true, "set_self." & node[0][1].strVal) + elif has_self(node, global_ctx): + return (true, "self." & node[1].strVal) else: return (false, "") -proc find_builtin_keywords(func_body: NimNode, used_keywords: var seq[string]) = +proc find_builtin_keywords(func_body: NimNode, used_keywords: var seq[string], global_ctx: GlobalContext) = for child in func_body: - let (is_kw, kw_key_name) = is_keyword(child) - if is_kw: - used_keywords.add(kw_key_name) - find_builtin_keywords(child, used_keywords) + let (is_kw, kw_key_name) = is_keyword(child, global_ctx) + if is_kw and not ("set_" & kw_key_name in used_keywords): + used_keywords.add(kw_key_name) + find_builtin_keywords(child, used_keywords, global_ctx) -proc generate_defines(keywords: seq[string]): (NimNode, Table[string, string]) = +proc generate_defines(keywords: seq[string], global_ctx: GlobalContext): (NimNode, Table[string, string]) = # Allocate keywords that do not alter their value # during execution of a function e.g. msg.sender, msg.value etc. var stmts = newStmtList() @@ -54,24 +86,49 @@ proc generate_defines(keywords: seq[string]): (NimNode, Table[string, string]) = ) ) tmp_vars["msg.sender"] = tmp_var_name + + for kw in keywords: + if kw.startsWith("set_self"): + var (new_proc, new_proc_name) = generate_storage_set_func(kw, global_ctx) + tmp_vars[kw] = new_proc_name + stmts.add( + new_proc + ) + elif kw.startsWith("self"): + var (new_proc, new_proc_name) = generate_storage_get_func(kw, global_ctx) + tmp_vars[kw] = new_proc_name + stmts.add( + new_proc + ) + return (stmts, tmp_vars) -proc get_keyword_defines*(proc_def: NimNode): (NimNode, Table[string, string]) = +proc get_keyword_defines*(proc_def: NimNode, global_ctx: GlobalContext): (NimNode, Table[string, string]) = var keywords_used: seq[string] - find_builtin_keywords(proc_def, keywords_used) - let (global_define_stmts, global_keyword_map) = generate_defines(keywords_used) + find_builtin_keywords(proc_def, keywords_used, global_ctx) + keywords_used = deduplicate(keywords_used) + let (global_define_stmts, global_keyword_map) = generate_defines(keywords_used, global_ctx) return (global_define_stmts, global_keyword_map) -proc replace_keywords*(ast_node: NimNode, global_keyword_map: Table[string, string]): NimNode = +proc replace_keywords*(ast_node: NimNode, global_keyword_map: Table[string, string], global_ctx: GlobalContext): NimNode = var res_node = copyNimNode(ast_node) for child in ast_node: var next: NimNode - let (is_kw, kw_key_name) = is_keyword(child) - if is_kw: + let (is_kw, kw_key_name) = is_keyword(child, global_ctx) + if is_kw and kw_key_name.startsWith("self."): + next = nnkCall.newTree( + newIdentNode(global_keyword_map[kw_key_name]) + ) + elif is_kw and kw_key_name.startsWith("set_self."): + next = nnkCall.newTree( + newIdentNode(global_keyword_map[kw_key_name]), + child[1] + ) + elif is_kw: next = newIdentNode(global_keyword_map[kw_key_name]) else: next = child - res_node.add(replace_keywords(next, global_keyword_map)) + res_node.add(replace_keywords(next, global_keyword_map, global_ctx)) return res_node diff --git a/nimplay/nimplay_macros.nim b/nimplay/nimplay_macros.nim index 6a14d45..fb03c69 100644 --- a/nimplay/nimplay_macros.nim +++ b/nimplay/nimplay_macros.nim @@ -113,12 +113,12 @@ proc generate_context(proc_def: NimNode, global_ctx: GlobalContext): LocalContex var ctx = LocalContext() ctx.name = get_func_name(proc_def) ctx.sig = generate_function_signature(proc_def, global_ctx) - (ctx.keyword_define_stmts, ctx.global_keyword_map) = get_keyword_defines(proc_def) + (ctx.keyword_define_stmts, ctx.global_keyword_map) = get_keyword_defines(proc_def, global_ctx) return ctx proc handle_global_variables(var_section: NimNode, global_ctx :var GlobalContext) = - var slot_number = 0.uint + var slot_number = 0 for child in var_section: case child.kind of nnkIdentDefs: @@ -163,7 +163,8 @@ proc handle_contract_interface(stmts: NimNode): NimNode = function_signatures.add(ctx.sig) var new_proc_def = replace_keywords( ast_node=child, - global_keyword_map=ctx.global_keyword_map + global_keyword_map=ctx.global_keyword_map, + global_ctx=global_ctx ) # Insert global defines. new_proc_def[6].insert(0, ctx.keyword_define_stmts) @@ -172,10 +173,10 @@ proc handle_contract_interface(stmts: NimNode): NimNode = discard # raise newException(ParserError, ">> Invalid stmt \"" & getTypeInst(child) & "\" not supported in contract block") - if filter(function_signatures, proc(x: FunctionSignature): bool = x.is_private).len == 0: + if filter(function_signatures, proc(x: FunctionSignature): bool = not x.is_private).len == 0: raise newException( ParserError, - "No public functions have defined, use * postfix to annotate public functions." + "No public functions have been defined, use * postfix to annotate public functions. e.g. proc myfunc*(a: uint256)" ) # Build Main Entrypoint. @@ -404,6 +405,6 @@ macro contract*(contract_name: string, proc_def: untyped): untyped = # echo treeRepr(proc_def) expectKind(proc_def, nnkStmtList) var stmtlist = handle_contract_interface(proc_def) - # echo "After:" - # echo treeRepr(stmtlist) + echo "Final Contract Code:" + echo repr(stmtlist) return stmtlist diff --git a/nimplay/storage.nim b/nimplay/storage.nim new file mode 100644 index 0000000..a10aadd --- /dev/null +++ b/nimplay/storage.nim @@ -0,0 +1,70 @@ +import macros +import strformat +import strutils +import tables + +import ./types + + +proc generate_storage_get_func*(storage_keword: string, global_ctx: GlobalContext): (NimNode, string) = + var + global_var_name = storage_keword.split(".")[1] + new_proc_name = fmt"get_{global_var_name}_from_storage" + var_info = global_ctx.global_variables[global_var_name] + slot_number = var_info.slot + + if var_info.var_type != "uint256": + raise newException(ParserError, "Only uint256 storage supported at the moment.") + + var new_proc = parseStmt(fmt""" + proc {new_proc_name}(): uint256 = + var + tmp: array[32, byte] + pos = {$slot_number}.stuint(32).toByteArrayBE + storageLoad(pos, addr tmp) + return Uint256.fromBytesBE(tmp) + """) + + #[ + proc get*(): uint256 = + var tmp: array[32, byte] + var pos = 0.stuint(32).toByteArrayBE + storageLoad(pos, addr tmp) + return Uint256.fromBytesBE(tmp) + ]# + + # var new_proc = parseStmt(fmt""" + # proc {new_proc_name}(): address = + # var + # tmp: array[32, byte] + # pos = {$slot_number}.stuint(32).toByteArrayBE + + # storageLoad(position, addr tmp) + # var output: address + # const N = 20 + # for i in 0..