Complex lua literals in proposal generator (#1694)

This commit is contained in:
Eddy Ashton 2020-10-02 15:30:53 +01:00 коммит произвёл GitHub
Родитель a13b33d442
Коммит aa07efb755
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 66 добавлений и 45 удалений

Просмотреть файл

@ -26,17 +26,41 @@ def dump_to_file(output_path: str, obj: dict, dump_args: dict):
json.dump(obj, f, **dump_args) json.dump(obj, f, **dump_args)
def list_as_lua_literal(l): def as_lua_literal(arg):
return str(l).translate(str.maketrans("[]", "{}")) if isinstance(arg, str):
# This long string swallows any initial newline. This means if we
# had an actual newline, it will be lost. To work around this, we
# insert a newline to every string. If there was originally a
# newline at the start, its now the second character, and is kept.
return f"[====[\n{arg}]====]"
elif isinstance(arg, bool):
return str(arg).lower()
elif isinstance(arg, collections.abc.Sequence):
return f"{{ {', '.join(as_lua_literal(e) for e in arg)} }}"
elif isinstance(arg, collections.abc.Mapping):
inner = ", ".join(
f"[ {as_lua_literal(k)} ] = {as_lua_literal(v)}" for k, v in arg.items()
)
return f"{{ {inner} }}"
else:
return str(arg)
LUA_FUNCTION_EQUAL_ARRAYS = """function equal_arrays(a, b) LUA_FUNCTION_EQUAL_TABLES = """function equal_tables(a, b)
if #a ~= #b then if #a ~= #b then
return false return false
else else
for k, v in ipairs(a) do for k, v in pairs(a) do
if b[k] ~= v then if type(v) ~= type(b[k]) then
return false return false
elseif type(v) == "table" then
if not equal_tables(v, b[k]) then
return false
end
else
if v ~= b[k] then
return false
end
end end
end end
return true return true
@ -84,48 +108,38 @@ def add_arg_construction(
arg: Union[str, collections.abc.Sequence, collections.abc.Mapping], arg: Union[str, collections.abc.Sequence, collections.abc.Mapping],
arg_name: str = "args", arg_name: str = "args",
): ):
if isinstance(arg, str): lines.append(f"{arg_name} = {as_lua_literal(arg)}")
lines.append(f"{arg_name} = [====[{arg}]====]")
elif isinstance(arg, collections.abc.Sequence):
lines.append(f"{arg_name} = {list_as_lua_literal(arg)}")
elif isinstance(arg, collections.abc.Mapping):
lines.append(f"{arg_name} = {{}}")
for k, v in args.items():
add_arg_construction(lines, v, arg_name=f"{arg_name}.{k}")
else:
lines.append(f"{arg_name} = {arg}")
def add_arg_checks( def add_arg_checks(
lines: list, lines: list,
arg: Union[str, collections.abc.Sequence, collections.abc.Mapping], arg: Union[str, collections.abc.Sequence, collections.abc.Mapping],
arg_name: str = "args", arg_name: str = "args",
added_equal_arrays_fn: bool = False, added_equal_tables_fn: bool = False,
): ):
lines.append(f"if {arg_name} == nil then return false end") lines.append(f"if {arg_name} == nil then return false end")
if isinstance(arg, str): if isinstance(arg, str):
lines.append(f"if not {arg_name} == [====[{arg}]====] then return false end")
elif isinstance(arg, collections.abc.Sequence):
if not added_equal_arrays_fn:
lines.extend(
line.strip() for line in LUA_FUNCTION_EQUAL_ARRAYS.splitlines()
)
added_equal_arrays_fn = True
expected_name = arg_name.replace(".", "_")
lines.append(f"{expected_name} = {list_as_lua_literal(arg)}")
lines.append( lines.append(
f"if not equal_arrays({arg_name}, {expected_name}) then return false end" f"if not {arg_name} == {as_lua_literal(arg)} then return false end"
) )
elif isinstance(arg, collections.abc.Mapping): elif isinstance(arg, collections.abc.Sequence) or isinstance(
for k, v in arg.items(): arg, collections.abc.Mapping
add_arg_checks( ):
lines, if not added_equal_tables_fn:
v, lines.extend(
arg_name=f"{arg_name}.{k}", line.strip() for line in LUA_FUNCTION_EQUAL_TABLES.splitlines()
added_equal_arrays_fn=added_equal_arrays_fn, )
added_equal_tables_fn = True
expected_name = "expected"
lines.append(f"{expected_name} = {as_lua_literal(arg)}")
lines.append(
f"if not equal_tables({arg_name}, {expected_name}) then return false end"
) )
else: else:
lines.append(f"if not {arg_name} == {arg} then return false end") lines.append(
f"if not {arg_name} == {as_lua_literal(arg)} then return false end"
)
return added_equal_tables_fn
def build_proposal( def build_proposal(
@ -146,7 +160,7 @@ def build_proposal(
proposal_script_lines.append("tables, args = ...") proposal_script_lines.append("tables, args = ...")
proposal_script_lines.append(f'return Calls:call("{proposed_call}", args)') proposal_script_lines.append(f'return Calls:call("{proposed_call}", args)')
proposal_script_text = "; ".join(proposal_script_lines) proposal_script_text = ";\n".join(proposal_script_lines)
proposal = { proposal = {
"script": {"text": proposal_script_text}, "script": {"text": proposal_script_text},
} }
@ -165,7 +179,7 @@ def build_proposal(
vote_lines.append("args = call.args") vote_lines.append("args = call.args")
add_arg_checks(vote_lines, args) add_arg_checks(vote_lines, args)
vote_lines.append("return true") vote_lines.append("return true")
vote_text = "; ".join(vote_lines) vote_text = ";\n".join(vote_lines)
vote = {"ballot": {"text": vote_text}} vote = {"ballot": {"text": vote_text}}
LOG.trace(f"Made {proposed_call} proposal:\n{json.dumps(proposal, indent=2)}") LOG.trace(f"Made {proposed_call} proposal:\n{json.dumps(proposal, indent=2)}")

Просмотреть файл

@ -174,25 +174,25 @@ namespace ccf
static int lua_log_trace(lua_State* l) static int lua_log_trace(lua_State* l)
{ {
LOG_TRACE_FMT(get_var_string_from_args(l)); LOG_TRACE_FMT("{}", get_var_string_from_args(l));
return 0; return 0;
} }
static int lua_log_debug(lua_State* l) static int lua_log_debug(lua_State* l)
{ {
LOG_DEBUG_FMT(get_var_string_from_args(l)); LOG_DEBUG_FMT("{}", get_var_string_from_args(l));
return 0; return 0;
} }
static int lua_log_info(lua_State* l) static int lua_log_info(lua_State* l)
{ {
LOG_INFO_FMT(get_var_string_from_args(l)); LOG_INFO_FMT("{}", get_var_string_from_args(l));
return 0; return 0;
} }
static int lua_log_fail(lua_State* l) static int lua_log_fail(lua_State* l)
{ {
LOG_FAIL_FMT(get_var_string_from_args(l)); LOG_FAIL_FMT("{}", get_var_string_from_args(l));
return 0; return 0;
} }

Просмотреть файл

@ -316,8 +316,11 @@ class Consortium:
return self.vote_using_majority(remote_node, proposal) return self.vote_using_majority(remote_node, proposal)
def deploy_js_app(self, remote_node, app_bundle_path): def deploy_js_app(self, remote_node, app_bundle_path):
proposal_body, _ = self.make_proposal("deploy_js_app", app_bundle_path) proposal_body, careful_vote = self.make_proposal(
"deploy_js_app", app_bundle_path
)
proposal = self.get_any_active_member().propose(remote_node, proposal_body) proposal = self.get_any_active_member().propose(remote_node, proposal_body)
proposal.vote_for = careful_vote
return self.vote_using_majority(remote_node, proposal) return self.vote_using_majority(remote_node, proposal)
def accept_recovery(self, remote_node): def accept_recovery(self, remote_node):

Просмотреть файл

@ -55,10 +55,11 @@ def make_module_set_proposal(path, content, network):
with tempfile.NamedTemporaryFile("w") as f: with tempfile.NamedTemporaryFile("w") as f:
f.write(content) f.write(content)
f.flush() f.flush()
proposal_body, _ = ccf.proposal_generator.set_module(path, f.name) proposal_body, careful_vote = ccf.proposal_generator.set_module(path, f.name)
proposal = network.consortium.get_any_active_member().propose( proposal = network.consortium.get_any_active_member().propose(
primary, proposal_body primary, proposal_body
) )
proposal.vote_for = careful_vote
network.consortium.vote_using_majority(primary, proposal) network.consortium.vote_using_majority(primary, proposal)
@ -77,10 +78,11 @@ def test_module_set_and_remove(network, args):
assert r.body.json()["js"] == MODULE_CONTENT_1, r.body assert r.body.json()["js"] == MODULE_CONTENT_1, r.body
LOG.info("Member makes a module remove proposal") LOG.info("Member makes a module remove proposal")
proposal_body, _ = ccf.proposal_generator.remove_module(MODULE_PATH_1) proposal_body, careful_vote = ccf.proposal_generator.remove_module(MODULE_PATH_1)
proposal = network.consortium.get_any_active_member().propose( proposal = network.consortium.get_any_active_member().propose(
primary, proposal_body primary, proposal_body
) )
proposal.vote_for = careful_vote
network.consortium.vote_using_majority(primary, proposal) network.consortium.vote_using_majority(primary, proposal)
with primary.client( with primary.client(
@ -106,10 +108,11 @@ def test_modules_remove(network, args):
assert r.body.json()["js"] == MODULE_CONTENT_1, r.body assert r.body.json()["js"] == MODULE_CONTENT_1, r.body
LOG.info("Member makes a prefix-based modules remove proposal") LOG.info("Member makes a prefix-based modules remove proposal")
proposal_body, _ = ccf.proposal_generator.remove_modules(MODULE_PREFIX_1) proposal_body, careful_vote = ccf.proposal_generator.remove_modules(MODULE_PREFIX_1)
proposal = network.consortium.get_any_active_member().propose( proposal = network.consortium.get_any_active_member().propose(
primary, proposal_body primary, proposal_body
) )
proposal.vote_for = careful_vote
network.consortium.vote_using_majority(primary, proposal) network.consortium.vote_using_majority(primary, proposal)
with primary.client( with primary.client(
@ -177,10 +180,11 @@ def test_app_bundle(network, args):
assert r.body.json() == {"error": "invalid operand type"}, r.body assert r.body.json() == {"error": "invalid operand type"}, r.body
LOG.info("Removing js app") LOG.info("Removing js app")
proposal_body, _ = ccf.proposal_generator.remove_js_app() proposal_body, careful_vote = ccf.proposal_generator.remove_js_app()
proposal = network.consortium.get_any_active_member().propose( proposal = network.consortium.get_any_active_member().propose(
primary, proposal_body primary, proposal_body
) )
proposal.vote_for = careful_vote
network.consortium.vote_using_majority(primary, proposal) network.consortium.vote_using_majority(primary, proposal)
LOG.info("Verifying that modules and endpoints were removed") LOG.info("Verifying that modules and endpoints were removed")