From bd90534852945a25e2555a91b1e174f38342b053 Mon Sep 17 00:00:00 2001 From: Michael Maurer Date: Fri, 12 Jul 2024 10:17:57 -0400 Subject: [PATCH] Add read locks to the Lua runner Support requesting read locks from Lua contracts instead of only write-locks. Signed-off-by: Michael Maurer --- scripts/gen_bytecode.lua | 14 +++++++-- src/parsec/agent/runners/interface.hpp | 2 +- src/parsec/agent/runners/lua/impl.cpp | 39 +++++++++++++++++++++++--- src/parsec/agent/runners/lua/impl.hpp | 6 ++++ 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/scripts/gen_bytecode.lua b/scripts/gen_bytecode.lua index 69709750f..802280f5c 100644 --- a/scripts/gen_bytecode.lua +++ b/scripts/gen_bytecode.lua @@ -1,5 +1,10 @@ function gen_bytecode() pay_contract = function(param) + -- 0 is a read lock, 1 is a write lock in C++ scope + Locks = { + READ = 0, + WRITE = 1, + } from, to, value, sequence, sig = string.unpack("c32 c32 I8 I8 c64", param) function get_account_key(name) @@ -8,9 +13,12 @@ function gen_bytecode() function get_account(name) account_key = get_account_key(name) - account_data = coroutine.yield(account_key) - if #account_data > 0 then - return string.unpack("I8 I8", account_data) + + account_data = coroutine.yield(account_key, Locks.WRITE) + if string.len(account_data) > 0 then + account_balance, account_sequence + = string.unpack("I8 I8", account_data) + return account_balance, account_sequence end return 0, 0 end diff --git a/src/parsec/agent/runners/interface.hpp b/src/parsec/agent/runners/interface.hpp index 9557f7257..1511e9ce0 100644 --- a/src/parsec/agent/runners/interface.hpp +++ b/src/parsec/agent/runners/interface.hpp @@ -38,7 +38,7 @@ namespace cbdc::parsec::agent::runner { internal_error, /// Function yielded more than one key to lock. yield_count, - /// Function yielded a non-string key. + /// Function yielded a invalid datatype. yield_type, /// Error acquiring lock on key. lock_error, diff --git a/src/parsec/agent/runners/lua/impl.cpp b/src/parsec/agent/runners/lua/impl.cpp index 4c79dc147..61f162eae 100644 --- a/src/parsec/agent/runners/lua/impl.cpp +++ b/src/parsec/agent/runners/lua/impl.cpp @@ -138,25 +138,56 @@ namespace cbdc::parsec::agent::runner { return buf; } + auto lua_runner::get_stack_integer(int index) -> std::optional { + if(lua_isinteger(m_state.get(), index) != 1) { + return std::nullopt; + } + return lua_tointeger(m_state.get(), index); + } + void lua_runner::schedule_contract() { int n_results{}; auto resume_ret = lua_resume(m_state.get(), nullptr, 1, &n_results); if(resume_ret == LUA_YIELD) { - if(n_results != 1) { - m_log->error("Contract yielded more than one key"); + if(n_results > 2) { + m_log->error("Contract yielded more than two keys"); + m_result_callback(error_code::yield_count); + return; + } + if(n_results < 1) { + m_log->error("Contract yielded no keys"); m_result_callback(error_code::yield_count); return; } + + auto lock_level = broker::lock_type::write; + if(n_results == 2) { + auto lock_type = get_stack_integer(-1); + if(!lock_type.has_value()) { + m_log->error("Contract yielded two keys, but the second " + "is not an integer"); + m_result_callback(error_code::yield_type); + return; + } + lua_pop(m_state.get(), 1); + + lock_level = (lock_type.value() == 0) + ? broker::lock_type::read + : broker::lock_type::write; + } + auto key_buf = get_stack_string(-1); if(!key_buf.has_value()) { m_log->error("Contract did not yield a string"); m_result_callback(error_code::yield_type); return; } - lua_pop(m_state.get(), n_results); + + lua_pop(m_state.get(), 1); + auto success = m_try_lock_callback(std::move(key_buf.value()), - broker::lock_type::write, + lock_level, [&](auto res) { handle_try_lock(std::move(res)); }); diff --git a/src/parsec/agent/runners/lua/impl.hpp b/src/parsec/agent/runners/lua/impl.hpp index 7b2c111d8..4f808fde3 100644 --- a/src/parsec/agent/runners/lua/impl.hpp +++ b/src/parsec/agent/runners/lua/impl.hpp @@ -18,6 +18,10 @@ namespace cbdc::parsec::agent::runner { /// function execution, signature checking and commiting execution results. /// Class cannot be re-used for different functions/transactions, manages /// the lifecycle of a single transaction. + /// NOTE: When writing contracts, to pass data between the Lua environment + /// and the C++ environment, use `coroutine.yield()`. To request a + /// read-lock use coroutine.yield(, 0). To request a write-lock use + /// coroutine.yield(, 1) or coroutine.yield(). class lua_runner : public interface { public: /// \copydoc interface::interface() @@ -47,6 +51,8 @@ namespace cbdc::parsec::agent::runner { auto get_stack_string(int index) -> std::optional; + auto get_stack_integer(int index) -> std::optional; + void schedule_contract(); void