···9191 [@to_yojson fun v -> to_yojson (`LexMap v)] )
9292[@@deriving yojson]
93939494+let repo_record_to_string (record : repo_record) =
9595+ record |> repo_record_to_yojson |> Yojson.Safe.to_string
9696+9797+let repo_record_to_cbor_block (record : repo_record) =
9898+ to_cbor_block (`LexMap record)
9999+94100let of_cbor encoded : repo_record =
95101 let decoded = Dag_cbor.decode encoded in
96102 match of_ipld decoded with
+40-97
mist/lib/mst.ml
···88888989 val proof_for_key : t -> Cid.t -> string -> Block_map.t Lwt.t
90909191- val get_covering_proof : t -> string -> Storage.Block_map.t Lwt.t
9292-9391 val leaf_count : t -> int Lwt.t
94929593 val layer : t -> int Lwt.t
···496494 let index = find_gte_leaf_index key seq in
497495 let%lwt blocks =
498496 match Util.at_index index seq with
499499- | Some (Leaf (k, v, _)) when k = key -> (
500500- (* include the found leaf block to prove existence *)
501501- match%lwt
502502- Store.get_bytes t.blockstore v
503503- with
504504- | Some leaf_bytes ->
505505- Lwt.return (Block_map.set v leaf_bytes Block_map.empty)
506506- | None ->
507507- Lwt.return Block_map.empty )
508508- | _ -> (
497497+ | Some (Leaf (k, _, _)) when k = key ->
498498+ Lwt.return Block_map.empty
499499+ | Some (Leaf (k, v_right, _)) -> (
500500+ let prev =
501501+ if index - 1 >= 0 then Util.at_index (index - 1) seq else None
502502+ in
503503+ match prev with
504504+ | Some (Tree c) ->
505505+ proof_for_key t c key
506506+ | _ ->
507507+ (* include bounding neighbor leaf blocks to prove nonexistence *)
508508+ let left_leaf =
509509+ match prev with
510510+ | Some (Leaf (_, v_left, _)) ->
511511+ Some v_left
512512+ | _ ->
513513+ None
514514+ in
515515+ let%lwt bm =
516516+ match left_leaf with
517517+ | Some cid_left -> (
518518+ match%lwt Store.get_bytes t.blockstore cid_left with
519519+ | Some b ->
520520+ Lwt.return (Block_map.set cid_left b Block_map.empty)
521521+ | None ->
522522+ Lwt.return Block_map.empty )
523523+ | None ->
524524+ Lwt.return Block_map.empty
525525+ in
526526+ let%lwt bm =
527527+ match%lwt Store.get_bytes t.blockstore v_right with
528528+ | Some b ->
529529+ Lwt.return (Block_map.set v_right b bm)
530530+ | None ->
531531+ Lwt.return bm
532532+ in
533533+ Lwt.return bm )
534534+ | Some (Tree c) ->
535535+ proof_for_key t c key
536536+ | None -> (
509537 let prev =
510538 if index - 1 >= 0 then Util.at_index (index - 1) seq else None
511539 in
···553581 Lwt.return bm )
554582 in
555583 Lwt.return (Block_map.set cid bytes blocks)
556556-557557- (* returns all mst nodes needed to prove the value of a given key's left sibling *)
558558- let rec proof_for_left_sibling t cid key : Block_map.t Lwt.t =
559559- match%lwt Store.get_bytes t.blockstore cid with
560560- | None ->
561561- Lwt.return Block_map.empty
562562- | Some bytes ->
563563- let raw = decode_block_raw bytes in
564564- let keys = node_entry_keys raw in
565565- let seq = interleave_raw raw keys in
566566- let index = find_gte_leaf_index key seq in
567567- let%lwt blocks =
568568- let prev =
569569- if index - 1 >= 0 then Util.at_index (index - 1) seq else None
570570- in
571571- match prev with
572572- | Some (Tree c) ->
573573- proof_for_left_sibling t c key
574574- | Some (Leaf (_, v_left, _)) -> (
575575- match%lwt Store.get_bytes t.blockstore v_left with
576576- | Some b ->
577577- Lwt.return (Block_map.set v_left b Block_map.empty)
578578- | None ->
579579- Lwt.return Block_map.empty )
580580- | _ ->
581581- Lwt.return Block_map.empty
582582- in
583583- Lwt.return (Block_map.set cid bytes blocks)
584584-585585- (* returns all mst nodes needed to prove the value of a given key's right sibling *)
586586- let rec proof_for_right_sibling t cid key : Block_map.t Lwt.t =
587587- match%lwt Store.get_bytes t.blockstore cid with
588588- | None ->
589589- Lwt.return Block_map.empty
590590- | Some bytes ->
591591- let raw = decode_block_raw bytes in
592592- let keys = node_entry_keys raw in
593593- let seq = interleave_raw raw keys in
594594- let index = find_gte_leaf_index key seq in
595595- let found =
596596- match Util.at_index index seq with
597597- | None ->
598598- if index - 1 >= 0 then Util.at_index (index - 1) seq else None
599599- | some ->
600600- some
601601- in
602602- let%lwt blocks =
603603- match found with
604604- | Some (Tree c) ->
605605- proof_for_right_sibling t c key
606606- | Some (Leaf (k, _, _)) -> (
607607- let neighbor =
608608- if k = key then Util.at_index (index + 1) seq
609609- else if index - 1 >= 0 then Util.at_index (index - 1) seq
610610- else None
611611- in
612612- match neighbor with
613613- | Some (Tree c) ->
614614- proof_for_right_sibling t c key
615615- | Some (Leaf (_, v_right, _)) -> (
616616- match%lwt Store.get_bytes t.blockstore v_right with
617617- | Some b ->
618618- Lwt.return (Block_map.set v_right b Block_map.empty)
619619- | None ->
620620- Lwt.return Block_map.empty )
621621- | _ ->
622622- Lwt.return Block_map.empty )
623623- | None ->
624624- Lwt.return Block_map.empty
625625- in
626626- Lwt.return (Block_map.set cid bytes blocks)
627627-628628- (* a covering proof is all mst nodes needed to prove the value of a given leaf
629629- and its siblings to its immediate right and left (if applicable) *)
630630- let get_covering_proof t key : Block_map.t Lwt.t =
631631- let%lwt proofs =
632632- Lwt.all
633633- [ proof_for_key t t.root key
634634- ; proof_for_left_sibling t t.root key
635635- ; proof_for_right_sibling t t.root key ]
636636- in
637637- Lwt.return
638638- (List.fold_left
639639- (fun acc proof -> Block_map.merge acc proof)
640640- Block_map.empty proofs )
641584642585 (* collects all node blocks (cid, bytes) and all leaf cids reachable from root
643586 only traverses nodes; doesn't fetch leaf blocks
-183
mist/test/test_mst.ml
···88let cid_of_string_exn s =
99 match Cid.of_string s with Ok c -> c | Error msg -> failwith msg
10101111-module Keys = struct
1212- let a0 = "A0/501344"
1313-1414- let a2 = "A2/239654"
1515-1616- let b0 = "B0/436099"
1717-1818- let b1 = "B1/293486"
1919-2020- let b2 = "B2/303249"
2121-2222- let c0 = "C0/535043"
2323-2424- let c2 = "C2/953910"
2525-2626- let d0 = "D0/360671"
2727-2828- let d2 = "D2/915466"
2929-3030- let e0 = "E0/922708"
3131-3232- let e2 = "E2/413113"
3333-3434- let f0 = "F0/606463"
3535-3636- let f1 = "F1/415452"
3737-3838- let g0 = "G0/714257"
3939-4040- let g2 = "G2/536869"
4141-4242- let h0 = "H0/740256"
4343-end
4444-4545-let leaf_cid =
4646- cid_of_string_exn
4747- "bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454"
4848-4949-let mst_of_proof root proof : Mem_mst.t =
5050- let store = Storage.Memory_blockstore.create ~blocks:proof () in
5151- Mem_mst.create store root
5252-5353-let test_two_deep_split () =
5454- let store = Storage.Memory_blockstore.create () in
5555- let* mst = Mem_mst.create_empty store in
5656- let%lwt mst = Mem_mst.add mst Keys.a0 leaf_cid in
5757- let%lwt mst = Mem_mst.add mst Keys.b1 leaf_cid in
5858- let%lwt mst = Mem_mst.add mst Keys.c0 leaf_cid in
5959- let%lwt mst = Mem_mst.add mst Keys.e0 leaf_cid in
6060- let%lwt mst = Mem_mst.add mst Keys.f1 leaf_cid in
6161- let%lwt mst = Mem_mst.add mst Keys.g0 leaf_cid in
6262- let%lwt mst = Mem_mst.add mst Keys.d2 leaf_cid in
6363- let%lwt proof = Mem_mst.get_covering_proof mst Keys.d2 in
6464- let proof_mst = mst_of_proof mst.root proof in
6565- let%lwt got = Mem_mst.get_cid proof_mst Keys.d2 in
6666- Alcotest.(check bool)
6767- "covering proof proves d2" true
6868- (Option.value
6969- (Option.map (fun x -> Cid.equal leaf_cid x) got)
7070- ~default:false ) ;
7171- Lwt.return_ok ()
7272-7373-let test_two_deep_leafless_splits () =
7474- let store = Storage.Memory_blockstore.create () in
7575- let* mst = Mem_mst.create_empty store in
7676- let%lwt mst = Mem_mst.add mst Keys.a0 leaf_cid in
7777- let%lwt mst = Mem_mst.add mst Keys.b0 leaf_cid in
7878- let%lwt mst = Mem_mst.add mst Keys.d0 leaf_cid in
7979- let%lwt mst = Mem_mst.add mst Keys.e0 leaf_cid in
8080- let%lwt mst = Mem_mst.add mst Keys.c2 leaf_cid in
8181- let%lwt proof = Mem_mst.get_covering_proof mst Keys.c2 in
8282- let proof_mst = mst_of_proof mst.root proof in
8383- let%lwt got = Mem_mst.get_cid proof_mst Keys.c2 in
8484- Alcotest.(check bool)
8585- "covering proof proves c2" true
8686- (Option.value
8787- (Option.map (fun x -> Cid.equal leaf_cid x) got)
8888- ~default:false ) ;
8989- Lwt.return_ok ()
9090-9191-let test_add_on_edge_with_neighbor_two_layers_down () =
9292- let store = Storage.Memory_blockstore.create () in
9393- let* mst = Mem_mst.create_empty store in
9494- let%lwt mst = Mem_mst.add mst Keys.a0 leaf_cid in
9595- let%lwt mst = Mem_mst.add mst Keys.b2 leaf_cid in
9696- let%lwt mst = Mem_mst.add mst Keys.c0 leaf_cid in
9797- let%lwt mst = Mem_mst.add mst Keys.d2 leaf_cid in
9898- let%lwt proof = Mem_mst.get_covering_proof mst Keys.d2 in
9999- let proof_mst = mst_of_proof mst.root proof in
100100- let%lwt got = Mem_mst.get_cid proof_mst Keys.d2 in
101101- Alcotest.(check bool)
102102- "covering proof proves d2" true
103103- (Option.value
104104- (Option.map (fun x -> Cid.equal leaf_cid x) got)
105105- ~default:false ) ;
106106- Lwt.return_ok ()
107107-108108-let test_merge_and_split_in_multi_op_commit () =
109109- let store = Storage.Memory_blockstore.create () in
110110- let* mst = Mem_mst.create_empty store in
111111- let%lwt mst = Mem_mst.add mst Keys.b0 leaf_cid in
112112- let%lwt mst = Mem_mst.add mst Keys.c2 leaf_cid in
113113- let%lwt mst = Mem_mst.add mst Keys.d0 leaf_cid in
114114- let%lwt mst = Mem_mst.add mst Keys.e2 leaf_cid in
115115- let%lwt mst = Mem_mst.add mst Keys.f0 leaf_cid in
116116- let%lwt mst = Mem_mst.add mst Keys.h0 leaf_cid in
117117- let%lwt mst = Mem_mst.delete mst Keys.b2 in
118118- let%lwt mst = Mem_mst.delete mst Keys.d2 in
119119- let%lwt mst = Mem_mst.add mst Keys.c2 leaf_cid in
120120- let%lwt proofs =
121121- Lwt.all
122122- [ Mem_mst.get_covering_proof mst Keys.b2
123123- ; Mem_mst.get_covering_proof mst Keys.d2
124124- ; Mem_mst.get_covering_proof mst Keys.c2 ]
125125- in
126126- let proof =
127127- List.fold_left Storage.Block_map.merge Storage.Block_map.empty proofs
128128- in
129129- let proof_mst = mst_of_proof mst.root proof in
130130- let%lwt got_c2 = Mem_mst.get_cid proof_mst Keys.c2 in
131131- Alcotest.(check bool)
132132- "covering proof proves c2" true
133133- (Option.value
134134- (Option.map (fun x -> Cid.equal leaf_cid x) got_c2)
135135- ~default:false ) ;
136136- let%lwt got_b2 = Mem_mst.get_cid proof_mst Keys.b2 in
137137- Alcotest.(check bool)
138138- "covering proof proves non-membership of b2" true (got_b2 = None) ;
139139- let%lwt got_d2 = Mem_mst.get_cid proof_mst Keys.d2 in
140140- Alcotest.(check bool)
141141- "covering proof proves non-membership of d2" true (got_d2 = None) ;
142142- Lwt.return_ok ()
143143-144144-let test_complex_multi_op_commit () =
145145- let store = Storage.Memory_blockstore.create () in
146146- let* mst = Mem_mst.create_empty store in
147147- let%lwt mst = Mem_mst.add mst Keys.b0 leaf_cid in
148148- let%lwt mst = Mem_mst.add mst Keys.c2 leaf_cid in
149149- let%lwt mst = Mem_mst.add mst Keys.d0 leaf_cid in
150150- let%lwt mst = Mem_mst.add mst Keys.e2 leaf_cid in
151151- let%lwt mst = Mem_mst.add mst Keys.f0 leaf_cid in
152152- let%lwt mst = Mem_mst.add mst Keys.h0 leaf_cid in
153153- let%lwt mst = Mem_mst.add mst Keys.a2 leaf_cid in
154154- let%lwt mst = Mem_mst.add mst Keys.g2 leaf_cid in
155155- let%lwt mst = Mem_mst.delete mst Keys.c2 in
156156- let%lwt proofs =
157157- Lwt.all
158158- [ Mem_mst.get_covering_proof mst Keys.a2
159159- ; Mem_mst.get_covering_proof mst Keys.g2
160160- ; Mem_mst.get_covering_proof mst Keys.c2 ]
161161- in
162162- let proof =
163163- List.fold_left Storage.Block_map.merge Storage.Block_map.empty proofs
164164- in
165165- let proof_mst = mst_of_proof mst.root proof in
166166- let%lwt got_a2 = Mem_mst.get_cid proof_mst Keys.a2 in
167167- Alcotest.(check bool)
168168- "covering proof proves a2" true
169169- (Option.value
170170- (Option.map (fun x -> Cid.equal leaf_cid x) got_a2)
171171- ~default:false ) ;
172172- let%lwt got_g2 = Mem_mst.get_cid proof_mst Keys.g2 in
173173- Alcotest.(check bool)
174174- "covering proof proves g2" true
175175- (Option.value
176176- (Option.map (fun x -> Cid.equal leaf_cid x) got_g2)
177177- ~default:false ) ;
178178- let%lwt got_c2 = Mem_mst.get_cid proof_mst Keys.c2 in
179179- Alcotest.(check bool)
180180- "covering proof proves non-membership of c2" true (got_c2 = None) ;
181181- Lwt.return_ok ()
182182-18311let test_trims_top_on_delete () =
18412 let store = Storage.Memory_blockstore.create () in
18513 let cid1 =
···893721 , [ test_case "allowed mst keys" `Quick (fun () ->
894722 run_test test_allowable_keys ) ] )
895723 ; ("diffs", [test_case "diffs" `Quick (fun () -> run_test test_diffs)])
896896- ; ( "covering-proofs"
897897- , [ test_case "two deep split" `Quick (fun () ->
898898- run_test test_two_deep_split )
899899- ; test_case "two deep leafless splits" `Quick (fun () ->
900900- run_test test_two_deep_leafless_splits )
901901- ; test_case "edge with neighbour two layers down" `Quick (fun () ->
902902- run_test test_add_on_edge_with_neighbor_two_layers_down )
903903- ; test_case "merge and split in multi-op commit" `Quick (fun () ->
904904- run_test test_merge_and_split_in_multi_op_commit )
905905- ; test_case "complex multi-op commit" `Quick (fun () ->
906906- run_test test_complex_multi_op_commit ) ] )
907724 ; ( "interop edge cases"
908725 , [ test_case "trims top of tree on delete" `Quick (fun () ->
909726 run_test test_trims_top_on_delete )
+34-23
pegasus/lib/api/sync/getRecord.ml
···99 Xrpc.parse_query ctx.req query_of_yojson
1010 in
1111 let path = collection ^ "/" ^ rkey in
1212- let%lwt {db; commit; _} =
1212+ let%lwt repo =
1313 Repository.load did ~ensure_active:true ~write:false ~ds:ctx.db
1414 in
1515- let commit_cid, commit_signed = Option.get commit in
1616- let commit_block =
1717- commit_signed |> User_store.Types.signed_commit_to_yojson
1818- |> Dag_cbor.encode_yojson
1919- in
2020- let mst_root = commit_signed.data in
2121- let%lwt blocks =
2222- Mst.proof_for_key {blockstore= db; root= mst_root} mst_root path
2323- in
2424- let blocks_stream =
2525- Repository.Block_map.entries blocks |> Lwt_seq.of_list
2626- in
2727- let car_stream =
2828- Lwt_seq.cons (commit_cid, commit_block) blocks_stream
2929- |> Car.blocks_to_stream commit_cid
3030- in
3131- Dream.stream
3232- ~headers:[("Content-Type", "application/vnd.ipld.car")]
3333- (fun res_stream ->
3434- Lwt_seq.iter_s
3535- (fun chunk -> Dream.write res_stream (Bytes.to_string chunk))
3636- car_stream ) )
1515+ match%lwt Repository.get_record repo path with
1616+ | None ->
1717+ Printf.ksprintf
1818+ (Errors.not_found ?name:None)
1919+ "record %s not found"
2020+ ("at://" ^ did ^ "/" ^ path)
2121+ | Some record ->
2222+ let record_block = Mist.Lex.repo_record_to_cbor_block record.value in
2323+ let commit_cid, commit_signed = Option.get repo.commit in
2424+ let commit_block =
2525+ commit_signed |> User_store.Types.signed_commit_to_yojson
2626+ |> Dag_cbor.encode_yojson
2727+ in
2828+ let mst_root = commit_signed.data in
2929+ let%lwt blocks =
3030+ Mst.proof_for_key
3131+ {blockstore= repo.db; root= mst_root}
3232+ mst_root path
3333+ in
3434+ let blocks_stream =
3535+ Repository.Block_map.entries blocks |> Lwt_seq.of_list
3636+ in
3737+ let car_stream =
3838+ Lwt_seq.cons (commit_cid, commit_block)
3939+ @@ Lwt_seq.cons record_block blocks_stream
4040+ |> Car.blocks_to_stream commit_cid
4141+ in
4242+ Dream.stream
4343+ ~headers:[("Content-Type", "application/vnd.ipld.car")]
4444+ (fun res_stream ->
4545+ Lwt_seq.iter_s
4646+ (fun chunk -> Dream.write res_stream (Bytes.to_string chunk))
4747+ car_stream ) )
+1-4
pegasus/lib/api/well_known.ml
···8080 ~status:`OK did
8181 | None ->
8282 failwith "not found"
8383- with _ ->
8484- Dream.respond
8585- ~headers:[("Content-Type", "text/plain; charset=utf-8")]
8686- ~status:`Not_Found "user not found" )
8383+ with _ -> Errors.not_found "user not found" )