Skip to content

Commit

Permalink
Add read locks to the Lua runner
Browse files Browse the repository at this point in the history
Support requesting read locks from Lua contracts instead
of only write-locks.

Signed-off-by: Michael Maurer <maurer.mi@northeastern.edu>
  • Loading branch information
maurermi committed Jul 12, 2024
1 parent d792c5e commit 9d79b5c
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 6 deletions.
8 changes: 7 additions & 1 deletion scripts/gen_bytecode.lua
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -10,7 +15,8 @@ function gen_bytecode()

function get_account(name)
account_key = get_account_key(name)
account_data = coroutine.yield(account_key)

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)
Expand Down
2 changes: 1 addition & 1 deletion src/parsec/agent/runners/interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
38 changes: 34 additions & 4 deletions src/parsec/agent/runners/lua/impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,25 +138,55 @@ namespace cbdc::parsec::agent::runner {
return buf;
}

auto lua_runner::get_stack_integer(int index) -> std::optional<int64_t> {
if(!lua_isinteger(m_state.get(), index)) {
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;
} else 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));
});
Expand Down
6 changes: 6 additions & 0 deletions src/parsec/agent/runners/lua/impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(<data>, 0). To request a write-lock use
/// coroutine.yield(<data>, 1) or coroutine.yield(<data>).
class lua_runner : public interface {
public:
/// \copydoc interface::interface()
Expand Down Expand Up @@ -47,6 +51,8 @@ namespace cbdc::parsec::agent::runner {

auto get_stack_string(int index) -> std::optional<buffer>;

auto get_stack_integer(int index) -> std::optional<int64_t>;

void schedule_contract();

void
Expand Down

0 comments on commit 9d79b5c

Please sign in to comment.