mirror of
https://github.com/vk496/MeshCore.git
synced 2026-09-02 08:33:44 +00:00
111 lines
3.7 KiB
C++
111 lines
3.7 KiB
C++
#include "MerkleTree.h"
|
|
#include "Multihash.h"
|
|
#include <string.h>
|
|
|
|
namespace mesh {
|
|
namespace ota {
|
|
|
|
void merkle_leaf(uint8_t out[4], const uint8_t* block, uint32_t block_len) {
|
|
mh4(out, block, block_len);
|
|
}
|
|
|
|
void merkle_combine(uint8_t out[4], const uint8_t* left, const uint8_t* right) {
|
|
sha256_trunc2(out, 4, left, 4, right, 4);
|
|
}
|
|
|
|
// Root via binary-counter / Merkle-Mountain-Range with right-to-left bagging.
|
|
// Equivalent to the level-by-level "pair adjacent, promote lone last (left||right)" reduction
|
|
// (verified against the reference implementation across many counts in the native tests).
|
|
void merkle_root(uint8_t out[4], const uint8_t* leaves, uint32_t count) {
|
|
if (count == 0) { memset(out, 0, 4); return; }
|
|
if (count == 1) { memcpy(out, leaves, 4); return; }
|
|
|
|
uint8_t peaks[32][4];
|
|
bool valid[32] = { false };
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
uint8_t cur[4];
|
|
memcpy(cur, leaves + (size_t)i * 4, 4);
|
|
uint32_t level = 0;
|
|
while (valid[level]) { // carry: combine with the pending peak at this level
|
|
merkle_combine(cur, peaks[level], cur); // peak is earlier (left), cur is right
|
|
valid[level] = false;
|
|
level++;
|
|
}
|
|
memcpy(peaks[level], cur, 4);
|
|
valid[level] = true;
|
|
}
|
|
|
|
// bag peaks right-to-left: acc starts at the lowest set level (rightmost peak)
|
|
int level = 0;
|
|
while (level < 32 && !valid[level]) level++;
|
|
uint8_t acc[4];
|
|
memcpy(acc, peaks[level], 4);
|
|
for (int l = level + 1; l < 32; l++) {
|
|
if (valid[l]) merkle_combine(acc, peaks[l], acc); // higher peak is left, acc is right
|
|
}
|
|
memcpy(out, acc, 4);
|
|
}
|
|
|
|
bool merkle_verify(const uint8_t* block, uint32_t block_len, uint32_t index,
|
|
const uint8_t* siblings, uint8_t n_siblings,
|
|
const uint8_t root[4], uint32_t count) {
|
|
uint8_t leaf[4];
|
|
merkle_leaf(leaf, block, block_len);
|
|
return merkle_verify_from_leaf(leaf, index, siblings, n_siblings, root, count);
|
|
}
|
|
|
|
bool merkle_verify_from_leaf(const uint8_t leaf[4], uint32_t index,
|
|
const uint8_t* siblings, uint8_t n_siblings,
|
|
const uint8_t root[4], uint32_t count) {
|
|
if (count == 0 || index >= count) return false;
|
|
uint8_t h[4];
|
|
memcpy(h, leaf, 4);
|
|
|
|
uint32_t idx = index;
|
|
uint32_t n = count;
|
|
uint8_t p = 0;
|
|
while (n > 1) {
|
|
bool is_last_odd = (n & 1u) && (idx == n - 1);
|
|
if (!is_last_odd) {
|
|
if (p >= n_siblings) return false;
|
|
const uint8_t* sib = siblings + (size_t)p * 4;
|
|
p++;
|
|
if (idx & 1u) merkle_combine(h, sib, h); // odd index -> sibling on the left
|
|
else merkle_combine(h, h, sib); // even index -> sibling on the right
|
|
}
|
|
idx >>= 1;
|
|
n = (n + 1) >> 1;
|
|
}
|
|
return (p == n_siblings) && (memcmp(h, root, 4) == 0);
|
|
}
|
|
|
|
uint8_t merkle_gen_proof(const uint8_t* leaves, uint32_t count, uint32_t index,
|
|
uint8_t* scratch, uint8_t* out_siblings) {
|
|
if (count == 0 || index >= count) return 0;
|
|
memcpy(scratch, leaves, (size_t)count * 4);
|
|
uint32_t n = count, idx = index;
|
|
uint8_t p = 0;
|
|
while (n > 1) {
|
|
bool is_last_odd = (n & 1u) && (idx == n - 1);
|
|
if (!is_last_odd) {
|
|
uint32_t s = (idx & 1u) ? idx - 1 : idx + 1;
|
|
memcpy(out_siblings + (size_t)p * 4, scratch + (size_t)s * 4, 4);
|
|
p++;
|
|
}
|
|
// reduce one level in place (parent m written from children 2m,2m+1; m <= i so it's safe)
|
|
uint32_t m = 0;
|
|
for (uint32_t i = 0; i < n; i += 2) {
|
|
if (i + 1 < n) merkle_combine(scratch + (size_t)m * 4, scratch + (size_t)i * 4, scratch + (size_t)(i + 1) * 4);
|
|
else memmove(scratch + (size_t)m * 4, scratch + (size_t)i * 4, 4);
|
|
m++;
|
|
}
|
|
idx >>= 1;
|
|
n = (n + 1) >> 1;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
} // namespace ota
|
|
} // namespace mesh
|