Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Storage list logging #571

Merged
merged 6 commits into from
Dec 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions tests/parser/features/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,29 @@ def foo():
assert get_last_log(t, c)["_value"] == [1, 2, 3, 4]


def test_storage_list_packing(t, get_last_log, bytes_helper, get_contract_with_gas_estimation, chain):
t.s = chain
code = """
Bar: __log__({_value: num[4]})
x: num[4]

@public
def foo():
log.Bar(self.x)

@public
def set_list():
self.x = [1, 2, 3, 4]
"""
c = get_contract_with_gas_estimation(code)

c.foo()
assert get_last_log(t, c)["_value"] == [0, 0, 0, 0]
c.set_list()
c.foo()
assert get_last_log(t, c)["_value"] == [1, 2, 3, 4]


def test_passed_list_packing(t, get_last_log, get_contract_with_gas_estimation, chain):
t.s = chain
code = """
Expand Down Expand Up @@ -570,3 +593,50 @@ def foo():

c.foo()
assert get_last_log(t, c)["_value"] == [1.11, 2.22, 3.33, 4.44]


def test_storage_byte_packing(t, get_last_log, bytes_helper, get_contract_with_gas_estimation, chain):
t.s = chain
code = """
MyLog: __log__({arg1: bytes <= 29})
x:bytes<=5

@public
def foo(a:num):
log.MyLog(self.x)

@public
def setbytez():
self.x = 'hello'
"""

c = get_contract_with_gas_estimation(code)

c.foo()
assert get_last_log(t, c)['arg1'] == bytes_helper('', 29)
c.setbytez()
c.foo()
assert get_last_log(t, c)['arg1'] == bytes_helper('hello', 29)


def test_storage_decimal_list_packing(t, get_last_log, bytes_helper, get_contract_with_gas_estimation, chain):
t.s = chain
code = """
Bar: __log__({_value: decimal[4]})
x: decimal[4]

@public
def foo():
log.Bar(self.x)

@public
def set_list():
self.x = [1.33, 2.33, 3.33, 4.33]
"""
c = get_contract_with_gas_estimation(code)

c.foo()
assert get_last_log(t, c)["_value"] == [0, 0, 0, 0]
c.set_list()
c.foo()
assert get_last_log(t, c)["_value"] == [1.33, 2.33, 3.33, 4.33]
46 changes: 46 additions & 0 deletions tests/parser/syntax/test_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import pytest
from pytest import raises

from viper import compiler
from viper.exceptions import TypeMismatchException


fail_list = [
"""
Bar: __log__({_value: num[4]})
x: decimal[4]

@public
def foo():
log.Bar(self.x)
""",
"""
Bar: __log__({_value: num[4]})

@public
def foo():
x: decimal[4]
log.Bar(x)
""",
"""
# larger than 32 bytes logging.

MyLog: __log__({arg1: bytes <= 29})
x:bytes<=55

@public
def foo(a:num):
log.MyLog(self.x)
"""
]


@pytest.mark.parametrize('bad_code', fail_list)
def test_logging_fail(bad_code):

if isinstance(bad_code, tuple):
with raises(bad_code[1]):
compiler.compile(bad_code[0])
else:
with raises(TypeMismatchException):
compiler.compile(bad_code)
35 changes: 30 additions & 5 deletions viper/parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def __init__(self, vars=None, globals=None, sigs=None, forvars=None, return_type
self.placeholder_count = 1
# Original code (for error pretty-printing purposes)
self.origcode = origcode
# In Loop status. Wether body is currently evaluating within a for-loop or not.
# In Loop status. Whether body is currently evaluating within a for-loop or not.
self.in_for_loop = set()

def set_in_for_loop(self, name_of_list):
Expand Down Expand Up @@ -364,7 +364,7 @@ def parse_other_functions(o, otherfuncs, _globals, sigs, external_contracts, ori
def parse_tree_to_lll(code, origcode, runtime_only=False):
_contracts, _events, _defs, _globals = get_contracts_and_defs_and_globals(code)
_names = [_def.name for _def in _defs] + [_event.target.id for _event in _events]
# Checks for duplicate funciton / event names
# Checks for duplicate function / event names
if len(set(_names)) < len(_names):
raise VariableDeclarationException("Duplicate function or event name: %s" % [name for name in _names if _names.count(name) > 1][0])
# Initialization function
Expand Down Expand Up @@ -681,8 +681,15 @@ def pack_args_by_32(holder, maxlen, arg, typ, context, placeholder):
holder.append(LLLnode.from_list(['mstore', placeholder, value], typ=typ, location='memory'))
elif isinstance(typ, ByteArrayType):
bytez = b''
# Bytes from Storage
if isinstance(arg, ast.Attribute) and arg.value.id == 'self':
stor_bytes = context.globals[arg.attr]
if stor_bytes.typ.maxlen > 32:
raise TypeMismatchException("Can only log a maximum of 32 bytes at a time.")
arg2 = LLLnode.from_list(['sload', ['add', ['sha3_32', Expr(arg, context).lll_node], 1]], typ=BaseType(32))
holder, maxlen = pack_args_by_32(holder, maxlen, arg2, BaseType(32), context, context.new_placeholder(BaseType(32)))
# String literals
if isinstance(arg, ast.Str):
elif isinstance(arg, ast.Str):
if len(arg.s) > typ.maxlen:
raise TypeMismatchException("Data input bytes are to big: %r %r" % (len(arg.s), typ))
for c in arg.s:
Expand All @@ -703,14 +710,32 @@ def pack_args_by_32(holder, maxlen, arg, typ, context, placeholder):
maxlen += (typ.count - 1) * 32
typ = typ.subtype

if isinstance(arg, ast.Name):
def check_list_type_match(provided): # Check list types match.
if provided != typ:
raise TypeMismatchException(
"Log list type '%s' does not match provided, expected '%s'" % (provided, typ)
)

# List from storage
if isinstance(arg, ast.Attribute) and arg.value.id == 'self':
stor_list = context.globals[arg.attr]
check_list_type_match(stor_list.typ.subtype)
size = stor_list.typ.count
for offset in range(0, size):
arg2 = LLLnode.from_list(['sload', ['add', ['sha3_32', Expr(arg, context).lll_node], offset]],
typ=typ)
holder, maxlen = pack_args_by_32(holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32)))
# List from variable.
elif isinstance(arg, ast.Name):
size = context.vars[arg.id].size
pos = context.vars[arg.id].pos
check_list_type_match(context.vars[arg.id].typ.subtype)
for i in range(0, size):
offset = 32 * i
arg2 = LLLnode.from_list(pos + offset, typ=typ, location='memory')
holder, maxlen = pack_args_by_32(holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32)))
else: # is list literal.
# is list literal.
else:
holder, maxlen = pack_args_by_32(holder, maxlen, arg.elts[0], typ, context, placeholder)
for j, arg2 in enumerate(arg.elts[1:]):
holder, maxlen = pack_args_by_32(holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32)))
Expand Down