From e9b9740f955d932ceb50f054e53248a11bd639ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Bj=C3=B6rkqvist?= Date: Mon, 18 Nov 2024 15:17:47 +0100 Subject: [PATCH] fix: FI-1574: last_block_index shall be leb128 encoded (#147) --- CHANGELOG.md | 1 + Cargo.lock | 1 + cycles-ledger/Cargo.toml | 1 + cycles-ledger/src/storage.rs | 17 ++++++++++++++++- cycles-ledger/tests/tests.rs | 3 ++- 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18eed36..9ce4ccd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ # CHANGELOG ## [Unreleased] - ReleaseDate +* Adapted `icrc3_get_tip_certificate` to be compliant with the ICRC-3 specification by changing the encoding of `last_block_index` to `leb128`. ## [1.0.2] - 2024-10-28 * Added `get_icp_xdr_conversion_rate` to mock CMC diff --git a/Cargo.lock b/Cargo.lock index e412eba..c44f3b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -386,6 +386,7 @@ dependencies = [ "icrc1-test-env-state-machine", "icrc1-test-suite", "lazy_static", + "leb128", "minicbor", "num-bigint", "num-traits", diff --git a/cycles-ledger/Cargo.toml b/cycles-ledger/Cargo.toml index cec1172..a0d8025 100644 --- a/cycles-ledger/Cargo.toml +++ b/cycles-ledger/Cargo.toml @@ -28,6 +28,7 @@ ic-canister-log = "0.2.0" ic-canisters-http-types = { git = "https://github.com/dfinity/ic", rev = "b2f18ac0794d2225b53d9c3190b60dbadb4ac9b9" } ic-metrics-encoder = "1.1" serde_json = "1.0.107" +leb128 = "0.2.5" [dev-dependencies] assert_matches = "1.5.0" diff --git a/cycles-ledger/src/storage.rs b/cycles-ledger/src/storage.rs index f2a2855..45b33c9 100644 --- a/cycles-ledger/src/storage.rs +++ b/cycles-ledger/src/storage.rs @@ -775,12 +775,17 @@ fn check_invariants(s: &State) { } } +/// The maximum number of bytes a 64-bit number can occupy when encoded in LEB128. +const MAX_U64_ENCODING_BYTES: usize = 10; + pub fn populate_last_block_hash_and_hash_tree( hash_tree: &mut RbTree<&'static str, Vec>, last_block_index: u64, last_block_hash: Hash, ) { - hash_tree.insert("last_block_index", last_block_index.to_be_bytes().to_vec()); + let mut last_block_index_buf = Vec::with_capacity(MAX_U64_ENCODING_BYTES); + leb128::write::unsigned(&mut last_block_index_buf, last_block_index).unwrap(); + hash_tree.insert("last_block_index", last_block_index_buf.to_vec()); hash_tree.insert("last_block_hash", last_block_hash.to_vec()); } @@ -2713,6 +2718,16 @@ mod tests { }, } } + + #[test] + fn test_u64_to_leb128() { + for i in [0, 1, u64::MAX - 1, u64::MAX] { + let mut buf = Vec::with_capacity(crate::storage::MAX_U64_ENCODING_BYTES); + leb128::write::unsigned(&mut buf, i).unwrap(); + let decoded = leb128::read::unsigned(&mut buf.as_slice()).unwrap(); + assert_eq!(i, decoded); + } + } } mod phash { diff --git a/cycles-ledger/tests/tests.rs b/cycles-ledger/tests/tests.rs index 124b8ce..c0248e5 100644 --- a/cycles-ledger/tests/tests.rs +++ b/cycles-ledger/tests/tests.rs @@ -563,7 +563,8 @@ impl TestEnv { let expected_last_block_index = match hash_tree.lookup_subtree([b"last_block_index"]) { SubtreeLookupResult::Found(tree) => match tree.as_ref() { HashTreeNode::Leaf(last_block_index_bytes) => { - u64::from_be_bytes(last_block_index_bytes.clone().try_into().unwrap()) + leb128::read::unsigned(&mut last_block_index_bytes.as_slice()) + .expect("Unable to read last_block_index from the hash_tree") } _ => panic!("last_block_index value in the hash_tree should be a Leaf"), },