Add support for get and set storage inline functions.

This commit is contained in:
Jacques Wagener 2019-07-03 15:54:51 +02:00
parent 5fbf236e00
commit cdf1ad57a9
No known key found for this signature in database
GPG Key ID: C294D1025DA0E923
7 changed files with 269 additions and 47 deletions

View File

@ -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)

2
nimplay.nim Normal file
View File

@ -0,0 +1,2 @@
import nimplay/nimplay_macros

30
nimplay.nimble Normal file
View File

@ -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")

View File

@ -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 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:
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)
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

View File

@ -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

70
nimplay/storage.nim Normal file
View File

@ -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..<N:
# output[i] = tmp[i]
# return output
# """)
return (new_proc, new_proc_name)
proc generate_storage_set_func*(storage_keyword: string, global_ctx: GlobalContext): (NimNode, string) =
var
global_var_name = storage_keyword.split(".")[1]
new_proc_name = fmt"set_{global_var_name}_in_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}(value:uint256) =
var
tmp: array[32, byte] = value.toByteArrayBE
pos = {$slot_number}.stuint(32).toByteArrayBE
storageStore(pos, addr tmp)
""")
return (new_proc, new_proc_name)

View File

@ -16,7 +16,7 @@ type
VariableType* = object
name*: string
var_type*: string
slot*: uint
slot*: int64
type
FunctionSignature* = object
@ -52,6 +52,6 @@ type ParserError* = object of Exception
# Nimplay constants.
let
KEYWORDS* {.compileTime.} = @["contract"]
TYPE_NAMES* {.compileTime.} = @["address", "uint256"]
KEYWORDS* {.compileTime.} = @["contract", "self"]
TYPE_NAMES* {.compileTime.} = @["address", "uint256", "bytes32"]
ALL_KEYWORDS* {.compileTime.} = concat(TYPE_NAMES, KEYWORDS)