Skip to content

Commit

Permalink
Correct and enforce schema on CBOR proofs (#6520)
Browse files Browse the repository at this point in the history
  • Loading branch information
achamayou authored Oct 8, 2024
1 parent 8428954 commit 0b466fd
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 27 deletions.
15 changes: 15 additions & 0 deletions cddl/ccf-tree-alg.cddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ccf-inclusion-proof = {
&(leaf: 1) => ccf-leaf
&(path: 2) => [+ ccf-proof-element]
}

ccf-leaf = [
internal-transaction-hash: bstr .size 32 ; a string of HASH_SIZE(32) bytes
internal-evidence: tstr .size (1..1024) ; a string of at most 1024 bytes
data-hash: bstr .size 32 ; a string of HASH_SIZE(32) bytes
]

ccf-proof-element = [
left: bool ; position of the element
hash: bstr .size 32 ; hash of the proof element (string of HASH_SIZE(32) bytes)
]
4 changes: 2 additions & 2 deletions src/node/historical_queries_adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ namespace
&ctx, ccf::MerkleProofLabel::MERKLE_PROOF_PATH_LABEL);
for (const auto& node : path)
{
const int64_t dir =
const bool dir =
(node.direction == ccf::HistoryTree::Path::Direction::PATH_LEFT);
std::vector<uint8_t> hash{node.hash};

QCBOREncode_OpenArray(&ctx);
QCBOREncode_AddInt64(&ctx, dir);
QCBOREncode_AddBool(&ctx, dir);
QCBOREncode_AddBytes(&ctx, {hash.data(), hash.size()});
QCBOREncode_CloseArray(&ctx);
}
Expand Down
17 changes: 14 additions & 3 deletions src/node/test/historical_queries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,19 @@ MerkleProofData decode_merkle_proof(const std::vector<uint8_t>& encoded)
std::pair<int64_t, std::vector<uint8_t>> path_item;

REQUIRE(QCBORDecode_GetNext(&ctx, &item) == QCBOR_SUCCESS);
REQUIRE(item.uDataType == QCBOR_TYPE_INT64);
path_item.first = item.val.int64;
if (item.uDataType == CBOR_SIMPLEV_TRUE)
{
path_item.first = true;
}
else if (item.uDataType == CBOR_SIMPLEV_FALSE)
{
path_item.first = false;
}
else
{
// Not a valid CBOR boolean
REQUIRE(false);
}

REQUIRE(QCBORDecode_GetNext(&ctx, &item) == QCBOR_SUCCESS);
REQUIRE(item.uDataType == QCBOR_TYPE_BYTE_STRING);
Expand Down Expand Up @@ -1972,7 +1983,7 @@ TEST_CASE("Valid merkle proof from receipts")
auto it = decoded.path.begin();
for (const auto& node : *historical_state->receipt->path)
{
const int64_t dir =
const bool dir =
(node.direction == ccf::HistoryTree::Path::Direction::PATH_LEFT);
std::vector<uint8_t> hash{node.hash};

Expand Down
32 changes: 10 additions & 22 deletions tests/e2e_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
import threading
import copy
import programmability
import cbor2
import e2e_common_endpoints
import subprocess

from loguru import logger as LOG

Expand Down Expand Up @@ -932,27 +932,15 @@ def test_cbor_merkle_proof(network, args):
)
if r.status_code == http.HTTPStatus.OK:
cbor_proof = r.body.data()
proof = cbor2.loads(cbor_proof)
assert 1 in proof
leaf = proof[1]
assert len(leaf) == 3
assert isinstance(leaf[0], bytes) # bstr write_set_digest
assert len(leaf[0]) == 32
assert isinstance(leaf[1], str) # tstr commit_evidence
assert len(leaf[1]) < 1024
assert isinstance(leaf[2], bytes) # bstr claims_digest
assert len(leaf[2]) == 32
# path
assert 2 in proof
path = proof[2]
assert isinstance(path, list)
for node in path:
assert isinstance(node, list)
assert len(node) == 2
assert isinstance(node[0], int)
assert node[0] in {0, 1} # boolean left
assert isinstance(node[1], bytes) # bstr intermediary digest
assert len(node[1]) == 32
cbor_proof_filename = os.path.join(
network.common_dir, f"proof_{txid}.cbor"
)
with open(cbor_proof_filename, "wb") as f:
f.write(cbor_proof)
subprocess.run(
["cddl", "../cddl/ccf-tree-alg.cddl", "v", cbor_proof_filename],
check=True,
)
found_proof = True
LOG.debug(f"Checked CBOR Merkle proof for txid {txid}")
break
Expand Down

0 comments on commit 0b466fd

Please sign in to comment.