@@ -355,6 +355,11 @@ impl<T: TrieTable> MerkleRadixTrie<T> {
355355 value : Vec < u8 > ,
356356 missing_range : & Option < ops:: RangeFrom < KeyNibbles > > ,
357357 ) {
358+ debug_assert ! (
359+ !key. is_empty( ) ,
360+ "empty (ROOT) key must be rejected at the put_chunk boundary" ,
361+ ) ;
362+
358363 // Start by getting the root node.
359364 let mut cur_node = self
360365 . get_root ( txn)
@@ -413,7 +418,8 @@ impl<T: TrieTable> MerkleRadixTrie<T> {
413418 // with the given key. Update the value.
414419 if cur_node. key == * key {
415420 // Update the node and store it.
416- // TODO This unwrap() will fail when attempting to store a value at the root node
421+ // `put_raw` rejects empty keys at its entry, so `cur_node` here never has
422+ // `root_data.is_some()`; `put_value` cannot return `RootCantHaveValue`.
417423 let prev_kind = cur_node. kind ( ) ;
418424 let old_value = cur_node. put_value ( value) . unwrap ( ) ;
419425 self . put_node ( txn, & cur_node, old_value. into ( ) ) ;
@@ -840,6 +846,15 @@ impl<T: TrieTable> MerkleRadixTrie<T> {
840846 "first key is inconsistent with key range" ,
841847 ) ) ;
842848 }
849+
850+ // The root node holds no value, so a ROOT-keyed item would panic in `put_raw`.
851+ // ROOT is the minimum key, so checking `items[0]` suffices given the sort check below.
852+ if chunk. items [ 0 ] . key . is_empty ( ) {
853+ return Err ( MerkleRadixTrieError :: InvalidChunk (
854+ "item key must not be the root key" ,
855+ ) ) ;
856+ }
857+
843858 let last_item_key = chunk. items . last ( ) . unwrap ( ) . key . clone ( ) ;
844859 if let Some ( end_key) = & chunk. end_key
845860 && last_item_key >= * end_key
@@ -2214,6 +2229,35 @@ mod tests {
22142229 ) ;
22152230 }
22162231
2232+ #[ test]
2233+ fn put_chunk_rejects_root_keyed_item ( ) {
2234+ // A ROOT-keyed item used to reach `put_raw` and panic on
2235+ // `put_value(...).unwrap()`; `put_chunk` must reject it as `InvalidChunk`.
2236+ let env = MdbxDatabase :: new_volatile ( Default :: default ( ) ) . unwrap ( ) ;
2237+ let trie = MerkleRadixTrie :: new_incomplete ( & env, TestTrie ) ;
2238+ let mut raw_txn = env. write_transaction ( ) ;
2239+ let mut txn: WriteTransactionProxy = ( & mut raw_txn) . into ( ) ;
2240+ assert ! ( !trie. is_complete( & txn) ) ;
2241+
2242+ let proof_root = TrieNode :: new_root ( ) ;
2243+ let expected_hash = proof_root. hash_assert ( ) ;
2244+ let proof = TrieProof :: new ( vec ! [ proof_root. into( ) ] , Default :: default ( ) ) ;
2245+
2246+ let malicious_chunk = TrieChunk :: new (
2247+ None ,
2248+ vec ! [ TrieItem :: new(
2249+ KeyNibbles :: ROOT ,
2250+ vec![ 0xab , 0xbc , 0xcd , 0xde ] ,
2251+ ) ] ,
2252+ proof,
2253+ ) ;
2254+
2255+ match trie. put_chunk ( & mut txn, KeyNibbles :: ROOT , malicious_chunk, expected_hash) {
2256+ Err ( MerkleRadixTrieError :: InvalidChunk ( _) ) => { }
2257+ other => panic ! ( "expected InvalidChunk(_), got {:?}" , other) ,
2258+ }
2259+ }
2260+
22172261 #[ test]
22182262 fn partial_tree_put_chunks_manual ( ) {
22192263 let key_1 = "413f22" . parse ( ) . unwrap ( ) ;
0 commit comments