Skip to content

Commit 8b8c3f7

Browse files
committed
feat!: add slicing to machine::get_proof
1 parent 1287fe8 commit 8b8c3f7

18 files changed

+184
-79
lines changed

src/clua-i-machine.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ extern "C" {
3636

3737
#include "base64.h"
3838
#include "clua.h"
39+
#include "hash-tree-constants.h"
3940
#include "machine-c-api.h"
4041

4142
namespace cartesi {
@@ -548,12 +549,13 @@ void clua_push_schemed_json_table(lua_State *L, const char *s, const std::string
548549
/// \brief This is the machine:get_proof() method implementation.
549550
/// \param L Lua state.
550551
static int machine_obj_index_get_proof(lua_State *L) {
551-
lua_settop(L, 3);
552+
lua_settop(L, 4);
552553
auto &m = clua_check<clua_managed_cm_ptr<cm_machine>>(L, 1);
553554
const uint64_t address = luaL_checkinteger(L, 2);
554-
const int log2_size = static_cast<int>(luaL_checkinteger(L, 3));
555+
const int log2_target_size = static_cast<int>(luaL_checkinteger(L, 3));
556+
const int log2_root_size = static_cast<int>(luaL_optinteger(L, 4, cartesi::HASH_TREE_LOG2_ROOT_SIZE));
555557
const char *proof = nullptr;
556-
if (cm_get_proof(m.get(), address, log2_size, &proof) != 0) {
558+
if (cm_get_proof(m.get(), address, log2_target_size, log2_root_size, &proof) != 0) {
557559
return luaL_error(L, "%s", cm_get_last_error_message());
558560
}
559561
clua_push_schemed_json_table(L, proof, "Proof");

src/hash-tree.cpp

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,15 @@ const_machine_hash_view hash_tree::get_sparse_node_hash_view(index_type node_ind
7575

7676
void hash_tree::get_pristine_proof(int curr_log2_size, proof_type &proof) const {
7777
const auto log2_target_size = proof.get_log2_target_size();
78-
for (int log2_size = curr_log2_size - 1; log2_size >= log2_target_size; --log2_size) {
78+
const auto log2_root_size = proof.get_log2_root_size();
79+
const auto start_log2_size = std::min(log2_root_size, curr_log2_size) - 1;
80+
for (int log2_size = start_log2_size; log2_size >= log2_target_size; --log2_size) {
7981
proof.set_sibling_hash(m_pristine_hashes[log2_size], log2_size);
8082
}
8183
proof.set_target_hash(m_pristine_hashes[log2_target_size]);
84+
if (log2_root_size <= curr_log2_size) {
85+
proof.set_root_hash(m_pristine_hashes[log2_root_size]);
86+
}
8287
}
8388

8489
static inline uint64_t get_sibling_address(uint64_t address, int log2_size) {
@@ -103,46 +108,65 @@ void hash_tree::get_page_proof(address_range &ar, uint64_t address, proof_type &
103108
update_dirty_page(ar, opt_br->get(), changed);
104109
}
105110
const auto log2_target_size = proof.get_log2_target_size();
111+
const auto log2_root_size = proof.get_log2_root_size();
106112
assert(log2_target_size >= HASH_TREE_LOG2_WORD_SIZE && "log2_size is too small");
107113
const auto &entry = opt_br->get();
108114
const auto node_offset = address & (HASH_TREE_PAGE_SIZE - 1);
109-
for (int log2_size = HASH_TREE_LOG2_PAGE_SIZE - 1; log2_size >= log2_target_size; --log2_size) {
115+
const auto start_log2_size = std::min(log2_root_size, HASH_TREE_LOG2_PAGE_SIZE) - 1;
116+
for (int log2_size = start_log2_size; log2_size >= log2_target_size; --log2_size) {
110117
proof.set_sibling_hash(entry.node_hash_view(get_sibling_address(node_offset, log2_size), log2_size), log2_size);
111118
}
112119
proof.set_target_hash(entry.node_hash_view(node_offset, log2_target_size));
120+
if (log2_root_size <= HASH_TREE_LOG2_PAGE_SIZE) {
121+
proof.set_root_hash(entry.node_hash_view(0, log2_root_size));
122+
}
113123
page_hash_tree_cache::return_entry(*opt_br);
114124
}
115125

116126
void hash_tree::get_dense_proof(address_range &ar, int ar_log2_size, uint64_t address, proof_type &proof) {
117127
const auto &dht = ar.get_dense_hash_tree();
118128
const auto log2_target_size = proof.get_log2_target_size();
129+
const auto log2_root_size = proof.get_log2_root_size();
119130
const auto dht_end = std::max<int>(HASH_TREE_LOG2_PAGE_SIZE, log2_target_size);
120131
const auto node_offset = address - ar.get_start();
121-
for (int log2_size = ar_log2_size - 1; log2_size >= dht_end; --log2_size) {
132+
const auto start_log2_size = std::min(log2_root_size, ar_log2_size) - 1;
133+
for (int log2_size = start_log2_size; log2_size >= dht_end; --log2_size) {
122134
const auto sibling_offset = get_sibling_address(node_offset, log2_size);
123135
proof.set_sibling_hash(dht.node_hash_view(sibling_offset, log2_size), log2_size);
124136
}
137+
if (log2_root_size >= HASH_TREE_LOG2_PAGE_SIZE && log2_root_size <= ar_log2_size) {
138+
proof.set_root_hash(dht.node_hash_view(0, log2_root_size));
139+
}
125140
if (log2_target_size >= HASH_TREE_LOG2_PAGE_SIZE) {
126141
proof.set_target_hash(dht.node_hash_view(node_offset, log2_target_size));
127142
} else {
128143
get_page_proof(ar, address, proof);
129144
}
130145
}
131146

132-
hash_tree::proof_type hash_tree::get_proof(address_ranges ars, uint64_t address, int log2_size) {
133-
if (log2_size < HASH_TREE_LOG2_WORD_SIZE || log2_size > HASH_TREE_LOG2_ROOT_SIZE) {
134-
throw std::domain_error{"invalid log2_size"};
147+
hash_tree::proof_type hash_tree::get_proof(address_ranges ars, uint64_t address, int log2_target_size,
148+
int log2_root_size) {
149+
if (log2_root_size < HASH_TREE_LOG2_WORD_SIZE) {
150+
throw std::domain_error{"log2_root_size is too small"};
135151
}
136-
if (log2_size == HASH_TREE_LOG2_ROOT_SIZE) {
152+
if (log2_root_size > HASH_TREE_LOG2_ROOT_SIZE) {
153+
throw std::domain_error{"log2_root_size is too large"};
154+
}
155+
if (log2_target_size < HASH_TREE_LOG2_WORD_SIZE) {
156+
throw std::domain_error{"log2_target_size is too small"};
157+
}
158+
if (log2_target_size > log2_root_size) {
159+
throw std::domain_error{"log2_target_size is larger than log2_root_size"};
160+
}
161+
if (log2_target_size == HASH_TREE_LOG2_ROOT_SIZE) {
137162
if (address != 0) {
138-
throw std::domain_error{"address not aligned to log2_size"};
163+
throw std::domain_error{"address not aligned to log2_target_size"};
139164
}
140-
} else if (((address >> log2_size) << log2_size) != address) {
141-
throw std::domain_error{"address not aligned to log2_size"};
165+
} else if (((address >> log2_target_size) << log2_target_size) != address) {
166+
throw std::domain_error{"address not aligned to log2_target_size"};
142167
}
143-
proof_type proof{HASH_TREE_LOG2_ROOT_SIZE, log2_size};
168+
proof_type proof{log2_root_size, log2_target_size};
144169
proof.set_target_address(address);
145-
proof.set_root_hash(get_root_hash());
146170
index_type node_index = 1;
147171
int curr_log2_size = HASH_TREE_LOG2_ROOT_SIZE;
148172
for (;;) {
@@ -152,9 +176,13 @@ hash_tree::proof_type hash_tree::get_proof(address_ranges ars, uint64_t address,
152176
break;
153177
}
154178
const auto &node = m_sparse_nodes[node_index];
179+
// found node corresponding to root along the way
180+
if (curr_log2_size == proof.get_log2_root_size()) {
181+
proof.set_root_hash(node.hash);
182+
}
155183
assert(std::cmp_equal(node.log2_size, curr_log2_size) && "incorrect node log2_size");
156-
// hit sparse tree node
157-
if (curr_log2_size == log2_size) {
184+
// hit target at a sparse tree node
185+
if (curr_log2_size == proof.get_log2_target_size()) {
158186
proof.set_target_hash(node.hash);
159187
break;
160188
}
@@ -169,10 +197,14 @@ hash_tree::proof_type hash_tree::get_proof(address_ranges ars, uint64_t address,
169197
// go down left or right on sparse tree depending on address
170198
--curr_log2_size;
171199
if ((address & (UINT64_C(1) << curr_log2_size)) == 0) {
172-
proof.set_sibling_hash(get_sparse_node_hash_view(node.right, curr_log2_size), curr_log2_size);
200+
if (curr_log2_size < log2_root_size && curr_log2_size >= log2_target_size) {
201+
proof.set_sibling_hash(get_sparse_node_hash_view(node.right, curr_log2_size), curr_log2_size);
202+
}
173203
node_index = node.left;
174204
} else {
175-
proof.set_sibling_hash(get_sparse_node_hash_view(node.left, curr_log2_size), curr_log2_size);
205+
if (curr_log2_size < log2_root_size && curr_log2_size >= log2_target_size) {
206+
proof.set_sibling_hash(get_sparse_node_hash_view(node.left, curr_log2_size), curr_log2_size);
207+
}
176208
node_index = node.right;
177209
}
178210
}
@@ -1061,6 +1093,10 @@ hash_tree::nodes_type hash_tree::create_nodes(const_address_ranges ars) {
10611093
}
10621094

10631095
// LCOV_EXCL_START
1096+
#if defined(__GNUC__) && __GNUC__ >= 13
1097+
#pragma GCC diagnostic push
1098+
#pragma GCC diagnostic ignored "-Wdangling-reference"
1099+
#endif
10641100
void hash_tree::dump(const_address_ranges ars, std::ostream &out) {
10651101
out << "digraph HashTree {\n";
10661102
out << " rankdir=TB;\n";
@@ -1130,6 +1166,9 @@ void hash_tree::dump(const_address_ranges ars, std::ostream &out) {
11301166
}
11311167
out << "}\n";
11321168
}
1169+
#if defined(__GNUC__) && __GNUC__ >= 13
1170+
#pragma GCC diagnostic pop
1171+
#endif
11331172
// LCOV_EXCL_STOP
11341173

11351174
} // namespace cartesi

src/hash-tree.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ class hash_tree {
172172

173173
machine_hash get_root_hash() const noexcept;
174174
machine_hash get_node_hash(address_ranges ars, uint64_t address, int log2_size);
175-
proof_type get_proof(address_ranges ars, uint64_t address, int log2_size);
175+
proof_type get_proof(address_ranges ars, uint64_t address, int log2_target_size,
176+
int log2_root_size = HASH_TREE_LOG2_ROOT_SIZE);
176177

177178
void dump(const_address_ranges ars, std::ostream &out);
178179

src/i-machine.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "access-log.h"
2525
#include "address-range-description.h"
2626
#include "back-merkle-tree.h"
27+
#include "hash-tree-constants.h"
2728
#include "hash-tree-proof.h"
2829
#include "hash-tree-stats.h"
2930
#include "interpret.h"
@@ -124,8 +125,9 @@ class i_machine {
124125
}
125126

126127
/// \brief Obtains the proof for a node in the hash tree.
127-
hash_tree_proof get_proof(uint64_t address, int log2_size) const {
128-
return do_get_proof(address, log2_size);
128+
hash_tree_proof get_proof(uint64_t address, int log2_target_size,
129+
int log2_root_size = HASH_TREE_LOG2_ROOT_SIZE) const {
130+
return do_get_proof(address, log2_target_size, log2_root_size);
129131
}
130132

131133
/// \brief Obtains the root hash of the hash tree.
@@ -323,7 +325,7 @@ class i_machine {
323325
virtual void do_remove_stored(const std::string &dir) const = 0;
324326
virtual interpreter_break_reason do_log_step(uint64_t mcycle_count, const std::string &filename) = 0;
325327
virtual access_log do_log_step_uarch(const access_log::type &log_type) = 0;
326-
virtual hash_tree_proof do_get_proof(uint64_t address, int log2_size) const = 0;
328+
virtual hash_tree_proof do_get_proof(uint64_t address, int log2_target_size, int log2_root_size) const = 0;
327329
virtual machine_hash do_get_root_hash() const = 0;
328330
virtual machine_hash do_get_node_hash(uint64_t address, int log2_size) const = 0;
329331
virtual uint64_t do_read_reg(reg r) const = 0;

src/jsonrpc-discover.json

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,11 +511,24 @@
511511
}
512512
},
513513
{
514-
"name": "log2_size",
515-
"description": "Log2 of size of range",
514+
"name": "log2_target_size",
515+
"description": "Log2 of size of target range",
516516
"required": true,
517517
"schema": {
518-
"$ref": "#/components/schemas/UnsignedInteger"
518+
"$ref": "#/components/schemas/UnsignedInteger",
519+
"minimum": 5,
520+
"maximum": 64
521+
}
522+
},
523+
{
524+
"name": "log2_root_size",
525+
"description": "Log2 of size of enclosing range",
526+
"required": false,
527+
"schema": {
528+
"$ref": "#/components/schemas/UnsignedInteger",
529+
"minimum": 5,
530+
"maximum": 64,
531+
"default": 64
519532
}
520533
}
521534
],

src/jsonrpc-machine.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,9 +810,9 @@ machine_hash jsonrpc_machine::do_get_node_hash(uint64_t address, int log2_size)
810810
return hash;
811811
}
812812

813-
hash_tree_proof jsonrpc_machine::do_get_proof(uint64_t address, int log2_size) const {
813+
hash_tree_proof jsonrpc_machine::do_get_proof(uint64_t address, int log2_target_size, int log2_root_size) const {
814814
not_default_constructible<hash_tree_proof> result;
815-
request("machine.get_proof", std::tie(address, log2_size), result);
815+
request("machine.get_proof", std::tie(address, log2_target_size, log2_root_size), result);
816816
if (!result.has_value()) {
817817
throw std::runtime_error("jsonrpc server error: missing result");
818818
}

src/jsonrpc-machine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class jsonrpc_machine final : public i_machine {
135135
access_log do_log_reset_uarch(const access_log::type &log_type) override;
136136
machine_hash do_get_root_hash() const override;
137137
machine_hash do_get_node_hash(uint64_t address, int log2_size) const override;
138-
hash_tree_proof do_get_proof(uint64_t address, int log2_size) const override;
138+
hash_tree_proof do_get_proof(uint64_t address, int log2_target_size, int log2_root_size) const override;
139139
void do_replace_memory_range(const memory_range_config &new_range) override;
140140
access_log do_log_step_uarch(const access_log::type &log_type) override;
141141
machine_runtime_config do_get_runtime_config() const override;

src/jsonrpc-remote-machine.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ static std::ostream &operator<<(std::ostream &out, log_prefix prefix) {
120120
using namespace std::string_literals;
121121
using json = nlohmann::json;
122122

123+
/// \brief Checks if uint64_t value can be converted to integer
124+
static int check_int(uint64_t val, const char *what) {
125+
if (val > INT_MAX) {
126+
throw std::domain_error{std::string("value ").append(what).append(" is out of range")};
127+
}
128+
return static_cast<int>(val);
129+
}
130+
123131
/// \brief Installs a signal handler
124132
template <typename HANDLER>
125133
static void install_signal_handler(int signum, HANDLER handler) {
@@ -1158,13 +1166,19 @@ static json jsonrpc_machine_get_proof_handler(const json &j, const std::shared_p
11581166
if (!session->handler->machine) {
11591167
return jsonrpc_response_invalid_request(j, "no machine");
11601168
}
1161-
static const char *const param_name[] = {"address", "log2_size"};
1162-
auto args = parse_args<uint64_t, uint64_t>(j, param_name);
1163-
if (std::get<1>(args) > INT_MAX) {
1164-
throw std::domain_error("log2_size is out of range");
1169+
static const char *const param_name[] = {"address", "log2_target_size", "log2_root_size"};
1170+
auto args = parse_args<uint64_t, uint64_t, cartesi::optional_param<uint64_t>>(j, param_name);
1171+
switch (count_args(args)) {
1172+
case 2:
1173+
return jsonrpc_response_ok(j,
1174+
session->handler->machine->get_proof(std::get<0>(args), check_int(std::get<1>(args), param_name[1])));
1175+
case 3:
1176+
return jsonrpc_response_ok(j,
1177+
session->handler->machine->get_proof(std::get<0>(args), check_int(std::get<1>(args), param_name[1]),
1178+
check_int(std::get<2>(args).value(), param_name[2]))); // NOLINT(bugprone-unchecked-optional-access)
1179+
default:
1180+
throw std::runtime_error{"error detecting number of arguments"};
11651181
}
1166-
return jsonrpc_response_ok(j,
1167-
session->handler->machine->get_proof(std::get<0>(args), static_cast<int>(std::get<1>(args))));
11681182
}
11691183

11701184
/// \brief JSONRPC handler for the machine.get_hash_tree_stats method

src/local-machine.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ access_log local_machine::do_log_step_uarch(const access_log::type &log_type) {
104104
return get_machine()->log_step_uarch(log_type);
105105
}
106106

107-
hash_tree_proof local_machine::do_get_proof(uint64_t address, int log2_size) const {
108-
return get_machine()->get_proof(address, log2_size);
107+
hash_tree_proof local_machine::do_get_proof(uint64_t address, int log2_target_size, int log2_root_size) const {
108+
return get_machine()->get_proof(address, log2_target_size, log2_root_size);
109109
}
110110

111111
machine_hash local_machine::do_get_root_hash() const {

src/local-machine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class local_machine final : public i_machine {
6363
void do_clone_stored(const std::string &from_dir, const std::string &to_dir) const override;
6464
void do_remove_stored(const std::string &dir) const override;
6565
access_log do_log_step_uarch(const access_log::type &log_type) override;
66-
hash_tree_proof do_get_proof(uint64_t address, int log2_size) const override;
66+
hash_tree_proof do_get_proof(uint64_t address, int log2_target_size, int log2_root_size) const override;
6767
machine_hash do_get_root_hash() const override;
6868
machine_hash do_get_node_hash(uint64_t address, int log2_size) const override;
6969
bool do_verify_hash_tree() const override;

0 commit comments

Comments
 (0)