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 ../nimplay/types
|
||||||
|
|
||||||
# import endians
|
# import endians
|
||||||
import macros
|
# import macros
|
||||||
import stint
|
import stint
|
||||||
|
|
||||||
expandMacros:
|
# expandMacros:
|
||||||
contract("MyContract"):
|
contract("MyContract"):
|
||||||
|
|
||||||
# proc becomeKing(): uint256 =
|
|
||||||
# var a: int32
|
|
||||||
# a = (2^31-1).int32
|
|
||||||
# a += 1.int32
|
|
||||||
# return a.stuint(256)
|
|
||||||
var
|
var
|
||||||
a: uint256
|
a: uint256
|
||||||
b: address
|
owner: address
|
||||||
|
|
||||||
proc get_sender222(): address =
|
# proc default*() {.payable.}
|
||||||
if true:
|
# ...
|
||||||
return msg.sender
|
|
||||||
|
|
||||||
proc get_sender(): address =
|
# proc publicGetSender(slot_number: uint256) =
|
||||||
if true:
|
# discard
|
||||||
return msg.sender
|
# 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 =
|
proc get_storage*(): uint256 =
|
||||||
return msg.sender
|
return self.a
|
||||||
|
|
||||||
proc addition(in_a: uint256, in_b: uint256): uint256 =
|
proc set_storage*(in_a: uint256) =
|
||||||
return in_a + in_b
|
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)
|
# 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 macros
|
||||||
import strformat
|
import strformat
|
||||||
import tables
|
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:
|
if node.kind == nnkDotExpr:
|
||||||
var correct_types = (node[0].kind, node[1].kind) == (nnkIdent, nnkIdent)
|
var correct_types = (node[0].kind, node[1].kind) == (nnkIdent, nnkIdent)
|
||||||
var correct_length = node.len == 2
|
var correct_length = node.len == 2
|
||||||
if correct_types and correct_length:
|
if correct_types and correct_length:
|
||||||
if node[0].strVal == "msg" and node[1].strVal == "sender":
|
return true
|
||||||
return true
|
|
||||||
return false
|
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):
|
if is_message_sender(node):
|
||||||
return (true, "msg.sender")
|
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:
|
else:
|
||||||
return (false, "")
|
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:
|
for child in func_body:
|
||||||
let (is_kw, kw_key_name) = is_keyword(child)
|
let (is_kw, kw_key_name) = is_keyword(child, global_ctx)
|
||||||
if is_kw:
|
if is_kw and not ("set_" & kw_key_name in used_keywords):
|
||||||
used_keywords.add(kw_key_name)
|
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
|
# Allocate keywords that do not alter their value
|
||||||
# during execution of a function e.g. msg.sender, msg.value etc.
|
# during execution of a function e.g. msg.sender, msg.value etc.
|
||||||
var stmts = newStmtList()
|
var stmts = newStmtList()
|
||||||
|
@ -54,24 +86,49 @@ proc generate_defines(keywords: seq[string]): (NimNode, Table[string, string]) =
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
tmp_vars["msg.sender"] = tmp_var_name
|
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)
|
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]
|
var keywords_used: seq[string]
|
||||||
find_builtin_keywords(proc_def, keywords_used)
|
find_builtin_keywords(proc_def, keywords_used, global_ctx)
|
||||||
let (global_define_stmts, global_keyword_map) = generate_defines(keywords_used)
|
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)
|
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)
|
var res_node = copyNimNode(ast_node)
|
||||||
for child in ast_node:
|
for child in ast_node:
|
||||||
var next: NimNode
|
var next: NimNode
|
||||||
let (is_kw, kw_key_name) = is_keyword(child)
|
let (is_kw, kw_key_name) = is_keyword(child, global_ctx)
|
||||||
if is_kw:
|
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])
|
next = newIdentNode(global_keyword_map[kw_key_name])
|
||||||
else:
|
else:
|
||||||
next = child
|
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
|
return res_node
|
||||||
|
|
|
@ -113,12 +113,12 @@ proc generate_context(proc_def: NimNode, global_ctx: GlobalContext): LocalContex
|
||||||
var ctx = LocalContext()
|
var ctx = LocalContext()
|
||||||
ctx.name = get_func_name(proc_def)
|
ctx.name = get_func_name(proc_def)
|
||||||
ctx.sig = generate_function_signature(proc_def, global_ctx)
|
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
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
proc handle_global_variables(var_section: NimNode, global_ctx :var GlobalContext) =
|
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:
|
for child in var_section:
|
||||||
case child.kind
|
case child.kind
|
||||||
of nnkIdentDefs:
|
of nnkIdentDefs:
|
||||||
|
@ -163,7 +163,8 @@ proc handle_contract_interface(stmts: NimNode): NimNode =
|
||||||
function_signatures.add(ctx.sig)
|
function_signatures.add(ctx.sig)
|
||||||
var new_proc_def = replace_keywords(
|
var new_proc_def = replace_keywords(
|
||||||
ast_node=child,
|
ast_node=child,
|
||||||
global_keyword_map=ctx.global_keyword_map
|
global_keyword_map=ctx.global_keyword_map,
|
||||||
|
global_ctx=global_ctx
|
||||||
)
|
)
|
||||||
# Insert global defines.
|
# Insert global defines.
|
||||||
new_proc_def[6].insert(0, ctx.keyword_define_stmts)
|
new_proc_def[6].insert(0, ctx.keyword_define_stmts)
|
||||||
|
@ -172,10 +173,10 @@ proc handle_contract_interface(stmts: NimNode): NimNode =
|
||||||
discard
|
discard
|
||||||
# raise newException(ParserError, ">> Invalid stmt \"" & getTypeInst(child) & "\" not supported in contract block")
|
# 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(
|
raise newException(
|
||||||
ParserError,
|
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.
|
# Build Main Entrypoint.
|
||||||
|
@ -404,6 +405,6 @@ macro contract*(contract_name: string, proc_def: untyped): untyped =
|
||||||
# echo treeRepr(proc_def)
|
# echo treeRepr(proc_def)
|
||||||
expectKind(proc_def, nnkStmtList)
|
expectKind(proc_def, nnkStmtList)
|
||||||
var stmtlist = handle_contract_interface(proc_def)
|
var stmtlist = handle_contract_interface(proc_def)
|
||||||
# echo "After:"
|
echo "Final Contract Code:"
|
||||||
# echo treeRepr(stmtlist)
|
echo repr(stmtlist)
|
||||||
return 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
|
VariableType* = object
|
||||||
name*: string
|
name*: string
|
||||||
var_type*: string
|
var_type*: string
|
||||||
slot*: uint
|
slot*: int64
|
||||||
|
|
||||||
type
|
type
|
||||||
FunctionSignature* = object
|
FunctionSignature* = object
|
||||||
|
@ -52,6 +52,6 @@ type ParserError* = object of Exception
|
||||||
# Nimplay constants.
|
# Nimplay constants.
|
||||||
|
|
||||||
let
|
let
|
||||||
KEYWORDS* {.compileTime.} = @["contract"]
|
KEYWORDS* {.compileTime.} = @["contract", "self"]
|
||||||
TYPE_NAMES* {.compileTime.} = @["address", "uint256"]
|
TYPE_NAMES* {.compileTime.} = @["address", "uint256", "bytes32"]
|
||||||
ALL_KEYWORDS* {.compileTime.} = concat(TYPE_NAMES, KEYWORDS)
|
ALL_KEYWORDS* {.compileTime.} = concat(TYPE_NAMES, KEYWORDS)
|
||||||
|
|
Loading…
Reference in New Issue