mirror of https://github.com/status-im/nimplay.git
Add support for get and set storage inline functions.
This commit is contained in:
parent
5fbf236e00
commit
cdf1ad57a9
|
@ -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)
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import nimplay/nimplay_macros
|
||||
|
|
@ -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")
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue