From 8ef68ac91acd2455758aa934fa76b151d3aec5ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Bj=C3=B6rkqvist?= Date: Mon, 11 Nov 2024 21:07:34 +0100 Subject: [PATCH 1/5] last_block_index shall be leb128 encoded --- Cargo.lock | 1 + cycles-ledger/Cargo.toml | 1 + cycles-ledger/src/storage.rs | 7 ++++++- cycles-ledger/tests/tests.rs | 3 ++- 4 files changed, 10 insertions(+), 2 deletions(-) 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..24c453d 100644 --- a/cycles-ledger/src/storage.rs +++ b/cycles-ledger/src/storage.rs @@ -775,12 +775,17 @@ fn check_invariants(s: &State) { } } +/// The maximum amount 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()); } 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"), }, From 4c672ebeb653d879abd2cabaf9422d7ce643e3fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Bj=C3=B6rkqvist?= Date: Mon, 11 Nov 2024 21:20:45 +0100 Subject: [PATCH 2/5] Add unit test for u64 to leb128 conversion --- cycles-ledger/src/storage.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cycles-ledger/src/storage.rs b/cycles-ledger/src/storage.rs index 24c453d..f697160 100644 --- a/cycles-ledger/src/storage.rs +++ b/cycles-ledger/src/storage.rs @@ -2718,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 { From 5eb703a608fd1427ade9e34a8f18d4c44db50fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Bj=C3=B6rkqvist?= Date: Mon, 11 Nov 2024 21:23:51 +0100 Subject: [PATCH 3/5] Clippy --- cycles-ledger/src/storage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cycles-ledger/src/storage.rs b/cycles-ledger/src/storage.rs index f697160..af28d11 100644 --- a/cycles-ledger/src/storage.rs +++ b/cycles-ledger/src/storage.rs @@ -2721,7 +2721,7 @@ mod tests { #[test] fn test_u64_to_leb128() { - for i in [0, 1, u64::MAX-1, u64::MAX] { + 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(); From c6575bfe45566e75d91d250e1c8620331b27be8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Bj=C3=B6rkqvist?= Date: Mon, 11 Nov 2024 21:42:57 +0100 Subject: [PATCH 4/5] Added bullet point to change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) 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 From d50e9c809ec9069b090dd78d2af1e3cbe4721a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20Bj=C3=B6rkqvist?= Date: Tue, 12 Nov 2024 11:07:17 +0100 Subject: [PATCH 5/5] Update comment Co-authored-by: Thomas Locher --- cycles-ledger/src/storage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cycles-ledger/src/storage.rs b/cycles-ledger/src/storage.rs index af28d11..45b33c9 100644 --- a/cycles-ledger/src/storage.rs +++ b/cycles-ledger/src/storage.rs @@ -775,7 +775,7 @@ fn check_invariants(s: &State) { } } -/// The maximum amount of bytes a 64-bit number can occupy when encoded in LEB128. +/// 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(