Skip to content

Commit

Permalink
chore: Replace proof with sibling_hashes
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcos Pernambuco Motta committed Nov 13, 2023
1 parent c3b9b30 commit 06078b1
Show file tree
Hide file tree
Showing 37 changed files with 561 additions and 166 deletions.
2 changes: 1 addition & 1 deletion lib/grpc-interfaces
78 changes: 52 additions & 26 deletions src/access-log.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,12 @@ static inline uint64_t get_word_access_data(const access_data &ad) {
/// NOLINTNEXTLINE(bugprone-exception-escape)
class access {

using proof_type = machine_merkle_tree::proof_type;
using hasher_type = machine_merkle_tree::hasher_type;

public:
using hash_type = machine_merkle_tree::hash_type;
using sibling_hashes_type = std::vector<hash_type>;
using proof_type = machine_merkle_tree::proof_type;

public:
void set_type(access_type type) {
Expand Down Expand Up @@ -158,38 +162,60 @@ class access {
return m_read_hash;
}

/// \brief Sets proof that data read at address was in
/// Merkle tree before access.
/// \param proof Corresponding Merkle tree proof.
void set_proof(const proof_type &proof) {
m_proof = proof;
}
void set_proof(proof_type &&proof) {
m_proof = std::move(proof);
/// \brief Makes a proof that data read at address was in
/// Merkle tree before access. The access must have sibling hashes.
/// \returns Corresponding Merkle tree proof.
proof_type make_proof() const {
if (!m_sibling_hashes.has_value()) {
throw std::runtime_error("can't make proof if access doesn't have sibling hashes");
}
// NOLINTNEXTLINE(bugprone-unchecked-optional-access)
const auto &sibiling_hashes = m_sibling_hashes.value();
auto proof_log2_root_size = m_log2_size + sibiling_hashes.size();
proof_type proof(proof_log2_root_size, m_log2_size);
proof.set_target_address(m_address);
proof.set_target_hash(m_read_hash);
// Copy sibling hashes from access to proof, rolling up the target hash until obtaining the root hash
hash_type rolling_hash = proof.get_target_hash();
hasher_type hasher{};
for (size_t log2_size = m_log2_size; log2_size < proof_log2_root_size; log2_size++) {
auto sibling_hash = sibiling_hashes[log2_size - m_log2_size];
proof.set_sibling_hash(sibling_hash, log2_size);
auto bit = (m_address >> log2_size) & 1;
hasher.begin();
if (bit) {
hasher.add_data(sibling_hash.data(), sibling_hash.size());
hasher.add_data(rolling_hash.data(), rolling_hash.size());
} else {
hasher.add_data(rolling_hash.data(), rolling_hash.size());
hasher.add_data(sibling_hash.data(), sibling_hash.size());
}
hasher.end(rolling_hash);
}
proof.set_root_hash(rolling_hash);
return proof;
}

/// \brief Gets proof that data read at address was in
/// Merkle tree before access.
/// \returns Proof, if one is available.
const std::optional<proof_type> &get_proof(void) const {
return m_proof;
std::optional<sibling_hashes_type> &get_sibling_hashes() {
return m_sibling_hashes;
}
const std::optional<sibling_hashes_type> &get_sibling_hashes() const {
return m_sibling_hashes;
}

/// \brief Removes proof that data read at address was in
/// Merkle tree before access.
void clear_proof(void) {
m_proof = std::nullopt;
void set_sibling_hashes(const sibling_hashes_type &sibling_hashes) {
m_sibling_hashes = sibling_hashes;
}

private:
access_type m_type{0}; ///< Type of access
uint64_t m_address{0}; ///< Address of access
int m_log2_size{0}; ///< Log2 of size of access
std::optional<access_data> m_read{}; ///< Data before access
hash_type m_read_hash; ///< Hash of data before access
std::optional<access_data> m_written{}; ///< Written data
std::optional<hash_type> m_written_hash{}; ///< Hash of written data
std::optional<proof_type> m_proof{}; ///< Proof of data before access
access_type m_type{0}; ///< Type of access
uint64_t m_address{0}; ///< Address of access
int m_log2_size{0}; ///< Log2 of size of access
std::optional<access_data> m_read{}; ///< Data before access
hash_type m_read_hash; ///< Hash of data before access
std::optional<access_data> m_written{}; ///< Written data
std::optional<hash_type> m_written_hash{}; ///< Hash of written data
std::optional<sibling_hashes_type> m_sibling_hashes{}; ///< Hashes of siblings in path from address to root
};

/// \brief Log of state accesses
Expand Down
6 changes: 5 additions & 1 deletion src/cartesi/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
-- with this program (see COPYING). If not, see <https://www.gnu.org/licenses/>.
--

local cartesi = require("cartesi")

local _M = {}

local function indentout(f, indent, fmt, ...) f:write(string.rep(" ", indent), string.format(fmt, ...)) end
Expand Down Expand Up @@ -232,7 +234,9 @@ function _M.dump_log(log, out)
j = j + 1
-- Otherwise, output access
elseif ai then
if ai.proof then indentout(out, indent, "hash %s\n", hexhash8(ai.proof.root_hash)) end
if ai.sibling_hashes then
indentout(out, indent, "hash %s\n", hexhash8(cartesi.machine.get_access_proof(ai).root_hash))
end
local read = accessdatastring(ai.read, ai.read_hash, ai.log2_size)
if ai.type == "read" then
indentout(out, indent, "%d: read %s@0x%x(%u): %s\n", i, notes[i] or "", ai.address, ai.address, read)
Expand Down
17 changes: 17 additions & 0 deletions src/clua-grpc-machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,22 @@ static int grpc_machine_class_verify_uarch_reset_state_transition(lua_State *L)
return 1;
}

/// \brief This is the machine.get_access_proof()
/// static method implementation.
static int grpc_machine_class_get_access_proof(lua_State *L) {
const int stubidx = lua_upvalueindex(1);
const int ctxidx = lua_upvalueindex(2);
lua_settop(L, 1);
auto &managed_grpc_stub = clua_check<clua_managed_cm_ptr<cm_grpc_machine_stub>>(L, stubidx, ctxidx);
auto &managed_access = clua_push_to(L, clua_managed_cm_ptr<cm_access>(clua_check_cm_access(L, 1, ctxidx)), ctxidx);
auto &managed_proof = clua_push_to(L, clua_managed_cm_ptr<cm_merkle_tree_proof>(nullptr), ctxidx);
TRY_EXECUTE(cm_grpc_get_access_proof(managed_grpc_stub.get(), managed_access.get(), &managed_proof.get(), err_msg));
clua_push_cm_proof(L, managed_proof.get());
managed_access.reset();
managed_proof.reset();
return 1;
}

/// \brief This is the machine.verify_uarch_step_state_transition()
/// static method implementation.
static int grpc_machine_class_verify_uarch_step_state_transition(lua_State *L) {
Expand Down Expand Up @@ -168,6 +184,7 @@ static const auto grpc_machine_static_methods = cartesi::clua_make_luaL_Reg_arra
{"verify_uarch_step_state_transition", grpc_machine_class_verify_uarch_step_state_transition},
{"verify_uarch_reset_log", grpc_machine_class_verify_uarch_reset_log},
{"verify_uarch_reset_state_transition", grpc_machine_class_verify_uarch_reset_state_transition},
{"get_access_proof", grpc_machine_class_get_access_proof},
{"get_x_address", grpc_machine_class_get_x_address},
{"get_uarch_x_address", grpc_machine_class_get_uarch_x_address},
{"get_csr_address", grpc_machine_class_get_csr_address},
Expand Down
17 changes: 17 additions & 0 deletions src/clua-jsonrpc-machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,22 @@ static int jsonrpc_machine_class_verify_uarch_reset_state_transition(lua_State *
return 1;
}

/// \brief This is the machine.get_access_proof()
/// static method implementation.
static int jsonrpc_machine_class_get_access_proof(lua_State *L) {
const int stubidx = lua_upvalueindex(1);
const int ctxidx = lua_upvalueindex(2);
lua_settop(L, 1);
auto &managed_jsonrpc_mg_mgr = clua_check<clua_managed_cm_ptr<cm_jsonrpc_mg_mgr>>(L, stubidx, ctxidx);
auto &managed_access = clua_push_to(L, clua_managed_cm_ptr<cm_access>(clua_check_cm_access(L, 1, ctxidx)), ctxidx);
auto &managed_proof = clua_push_to(L, clua_managed_cm_ptr<cm_merkle_tree_proof>(nullptr), ctxidx);
TRY_EXECUTE(
cm_jsonrpc_get_access_proof(managed_jsonrpc_mg_mgr.get(), managed_access.get(), &managed_proof.get(), err_msg));
clua_push_cm_proof(L, managed_proof.get());
managed_proof.reset();
return 1;
}

/// \brief This is the machine.get_x_address() method implementation.
static int jsonrpc_machine_class_get_x_address(lua_State *L) {
auto &managed_jsonrpc_mg_mgr =
Expand Down Expand Up @@ -181,6 +197,7 @@ static const auto jsonrpc_machine_static_methods = cartesi::clua_make_luaL_Reg_a
{"verify_uarch_step_state_transition", jsonrpc_machine_class_verify_uarch_step_state_transition},
{"verify_uarch_reset_log", jsonrpc_machine_class_verify_uarch_reset_log},
{"verify_uarch_reset_state_transition", jsonrpc_machine_class_verify_uarch_reset_state_transition},
{"get_access_proof", jsonrpc_machine_class_get_access_proof},
{"get_x_address", jsonrpc_machine_class_get_x_address},
{"get_f_address", jsonrpc_machine_class_get_f_address},
{"get_uarch_x_address", jsonrpc_machine_class_get_uarch_x_address},
Expand Down
42 changes: 32 additions & 10 deletions src/clua-machine-util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ void cm_delete(cm_access_log *ptr) {
cm_delete_access_log(ptr);
}

/// \brief Deleter for C api access
template <>
void cm_delete(cm_access *ptr) {
cm_delete_access(ptr);
}

/// \brief Deleter for C api merkle tree proof
template <>
void cm_delete(cm_merkle_tree_proof *ptr) {
Expand Down Expand Up @@ -352,9 +358,9 @@ static void check_sibling_cm_hashes(lua_State *L, int idx, size_t log2_target_si
}
sibling_hashes->count = sibling_hashes_count;
sibling_hashes->entry = new cm_hash[sibling_hashes_count]{};
for (; log2_target_size < log2_root_size; ++log2_target_size) {
lua_rawgeti(L, idx, static_cast<lua_Integer>(log2_root_size - log2_target_size));
auto index = log2_root_size - 1 - log2_target_size;
for (size_t log2_size = log2_target_size; log2_size < log2_root_size; ++log2_size) {
lua_rawgeti(L, idx, static_cast<lua_Integer>(log2_size - log2_target_size) + 1);
auto index = log2_size - log2_target_size;
clua_check_cm_hash(L, -1, &sibling_hashes->entry[index]);
lua_pop(L, 1);
}
Expand Down Expand Up @@ -424,8 +430,7 @@ static unsigned char *opt_cm_access_data_field(lua_State *L, int tabidx, const c
/// \param a Pointer to receive access
/// \param ctxidx Index (or pseudo-index) of clua context
static void check_cm_access(lua_State *L, int tabidx, bool proofs, cm_access *a, int ctxidx) {
ctxidx = lua_absindex(L, ctxidx);
tabidx = lua_absindex(L, tabidx);
(void) ctxidx;
luaL_checktype(L, tabidx, LUA_TTABLE);
a->type = check_cm_access_type_field(L, tabidx, "type");
a->address = check_uint_field(L, tabidx, "address");
Expand All @@ -435,8 +440,9 @@ static void check_cm_access(lua_State *L, int tabidx, bool proofs, cm_access *a,
CM_TREE_LOG2_ROOT_SIZE);
}
if (proofs) {
lua_getfield(L, tabidx, "proof");
a->proof = clua_check_cm_merkle_tree_proof(L, -1, ctxidx);
lua_getfield(L, tabidx, "sibling_hashes");
a->sibling_hashes = new cm_hash_array{};
check_sibling_cm_hashes(L, -1, a->log2_size, CM_TREE_LOG2_ROOT_SIZE, a->sibling_hashes);
lua_pop(L, 1);
}

Expand All @@ -452,6 +458,18 @@ static void check_cm_access(lua_State *L, int tabidx, bool proofs, cm_access *a,
a->written_data = opt_cm_access_data_field(L, tabidx, "written", a->log2_size, &a->written_data_size);
}

cm_access *clua_check_cm_access(lua_State *L, int tabidx, int ctxidx) {
tabidx = lua_absindex(L, tabidx);
ctxidx = lua_absindex(L, ctxidx);
luaL_checktype(L, tabidx, LUA_TTABLE);
auto &managed = clua_push_to(L, clua_managed_cm_ptr<cm_access>(new cm_access{}), ctxidx);
cm_access *access = managed.get();
check_cm_access(L, tabidx, true, access, ctxidx);
managed.release();
lua_pop(L, 1); // cleanup managed log from stack
return access;
}

cm_access_log *clua_check_cm_access_log(lua_State *L, int tabidx, int ctxidx) {
tabidx = lua_absindex(L, tabidx);
ctxidx = lua_absindex(L, ctxidx);
Expand Down Expand Up @@ -651,9 +669,13 @@ void clua_push_cm_access_log(lua_State *L, const cm_access_log *log) {
lua_setfield(L, -2, "written");
}
}
if (log->log_type.proofs && a->proof != nullptr) {
clua_push_cm_proof(L, a->proof);
lua_setfield(L, -2, "proof");
if (log->log_type.proofs && a->sibling_hashes != nullptr) {
lua_newtable(L);
for (size_t log2_size = a->log2_size; log2_size < CM_TREE_LOG2_ROOT_SIZE; log2_size++) {
clua_push_cm_hash(L, &a->sibling_hashes->entry[log2_size - a->log2_size]);
lua_rawseti(L, -2, static_cast<lua_Integer>(log2_size - a->log2_size) + 1);
}
lua_setfield(L, -2, "sibling_hashes");
}
lua_rawseti(L, -2, static_cast<lua_Integer>(i) + 1);
}
Expand Down
8 changes: 8 additions & 0 deletions src/clua-machine-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,14 @@ cm_merkle_tree_proof *clua_check_cm_merkle_tree_proof(lua_State *L, int tabidx);
/// \returns The access log. Must be delete by the user with cm_delete_access_log
cm_access_log *clua_check_cm_access_log(lua_State *L, int tabidx, int ctxidx = lua_upvalueindex(1));

/// \brief Loads an cm_access from Lua
/// \param L Lua state
/// \param tabidx access stack index
/// \param a Pointer to receive access
/// \param ctxidx Index (or pseudo-index) of clua context
/// \returns The access. Must be delete by the user with cm_delete_access
cm_access *clua_check_cm_access(lua_State *L, int tabidx, int ctxidx = lua_upvalueindex(1));

/// \brief Loads a cm_machine_config object from a Lua table
/// \param L Lua state
/// \param tabidx Index of table in Lua stack
Expand Down
14 changes: 14 additions & 0 deletions src/clua-machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ static int machine_class_index_verify_uarch_reset_state_transition(lua_State *L)
return 1;
}

/// \brief This is the machine.get_access_proof() method implementation.
static int machine_class_index_get_access_proof(lua_State *L) {
lua_settop(L, 1);
auto &managed_access = clua_push_to(L, clua_managed_cm_ptr<cm_access>(clua_check_cm_access(L, 1)));
auto &managed_proof = clua_push_to(L, clua_managed_cm_ptr<cm_merkle_tree_proof>(nullptr));
TRY_EXECUTE(cm_get_access_proof(managed_access.get(), &managed_proof.get(), err_msg));
clua_push_cm_proof(L, managed_proof.get());
managed_proof.reset();
managed_access.reset();
return 1;
}

/// \brief This is the machine.get_x_address() method implementation.
static int machine_class_index_get_x_address(lua_State *L) {
const int i = static_cast<int>(luaL_checkinteger(L, 1));
Expand Down Expand Up @@ -139,6 +151,7 @@ static const auto machine_class_index = cartesi::clua_make_luaL_Reg_array({
{"verify_uarch_step_state_transition", machine_class_index_verify_uarch_step_state_transition},
{"verify_uarch_reset_log", machine_class_index_verify_uarch_reset_log},
{"verify_uarch_reset_state_transition", machine_class_index_verify_uarch_reset_state_transition},
{"get_access_proof", machine_class_index_get_access_proof},
{"get_x_address", machine_class_index_get_x_address},
{"get_uarch_x_address", machine_class_index_get_uarch_x_address},
{"get_f_address", machine_class_index_get_f_address},
Expand Down Expand Up @@ -176,6 +189,7 @@ int clua_machine_init(lua_State *L, int ctxidx) {
clua_createnewtype<clua_managed_cm_ptr<const cm_machine_config>>(L, ctxidx);
clua_createnewtype<clua_managed_cm_ptr<cm_machine_config>>(L, ctxidx);
clua_createnewtype<clua_managed_cm_ptr<cm_access_log>>(L, ctxidx);
clua_createnewtype<clua_managed_cm_ptr<cm_access>>(L, ctxidx);
clua_createnewtype<clua_managed_cm_ptr<cm_machine_runtime_config>>(L, ctxidx);
clua_createnewtype<clua_managed_cm_ptr<cm_merkle_tree_proof>>(L, ctxidx);
clua_createnewtype<clua_managed_cm_ptr<char>>(L, ctxidx);
Expand Down
12 changes: 12 additions & 0 deletions src/grpc-machine-c-api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,18 @@ int cm_grpc_verify_uarch_reset_state_transition(const cm_grpc_machine_stub *stub
return cm_result_failure(err_msg);
}

int cm_grpc_get_access_proof(const cm_grpc_machine_stub *stub, const cm_access *access,
cm_merkle_tree_proof **result_proof, char **err_msg) try {
const auto *cpp_stub = convert_from_c(stub);
const cartesi::access cpp_access = convert_from_c(access);
auto proof = cartesi::grpc_virtual_machine::get_access_proof(*cpp_stub, cpp_access);
*result_proof = convert_to_c(proof);
return cm_result_success(err_msg);

} catch (...) {
return cm_result_failure(err_msg);
}

int cm_grpc_get_x_address(const cm_grpc_machine_stub *stub, int i, uint64_t *val, char **err_msg) try {
const auto *cpp_stub = convert_from_c(stub);
*val = cartesi::grpc_virtual_machine::get_x_address(*cpp_stub, i);
Expand Down
9 changes: 9 additions & 0 deletions src/grpc-machine-c-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ CM_API int cm_grpc_verify_uarch_reset_state_transition(const cm_grpc_machine_stu
const cm_hash *root_hash_before, const cm_access_log *log, const cm_hash *root_hash_after,
const cm_machine_runtime_config *runtime_config, bool one_based, char **err_msg);

/// \brief Gets the access proof for a given access
/// \param stub Cartesi grpc machine stub. Must be pointer to valid object
/// \param access Access to construct proof for
/// \param result_proof Receives the corresponding proof. Must be deleted with cm_delete_merkle_tree_proof()
/// \param err_msg Receives the error message if function execution fails
/// \return 0 for success, non zero code for error
CM_API int cm_grpc_get_access_proof(const cm_grpc_machine_stub *stub, const cm_access *access,
cm_merkle_tree_proof **result_proof, char **err_msg);

/// \brief Checks the validity of a state transition
/// \param stub Cartesi grpc machine stub. Must be pointer to valid object
/// \param root_hash_before State hash before step
Expand Down
10 changes: 10 additions & 0 deletions src/grpc-virtual-machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,16 @@ void grpc_virtual_machine::verify_uarch_reset_state_transition(const grpc_machin
check_status(stub->get_stub()->VerifyUarchResetStateTransition(&context, request, &response));
}

machine_merkle_tree::proof_type grpc_virtual_machine::get_access_proof(const grpc_machine_stub_ptr &stub,
const access &access) {
GetAccessProofRequest request;
GetAccessProofResponse response;
ClientContext context;
set_proto_access(access, request.mutable_access());
check_status(stub->get_stub()->GetAccessProof(&context, request, &response));
return get_proto_merkle_tree_proof(response.proof());
}

void grpc_virtual_machine::verify_uarch_step_state_transition(const grpc_machine_stub_ptr &stub,
const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after,
const machine_runtime_config &r, bool one_based) {
Expand Down
2 changes: 2 additions & 0 deletions src/grpc-virtual-machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class grpc_virtual_machine : public i_virtual_machine {
const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after,
const machine_runtime_config &r = {}, bool one_based = false);

static machine_merkle_tree::proof_type get_access_proof(const grpc_machine_stub_ptr &stub, const access &access);

static uint64_t get_x_address(const grpc_machine_stub_ptr &stub, int i);
static uint64_t get_f_address(const grpc_machine_stub_ptr &stub, int i);
static uint64_t get_uarch_x_address(const grpc_machine_stub_ptr &stub, int i);
Expand Down
Loading

0 comments on commit 06078b1

Please sign in to comment.