Source file base58.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
(** Base58Check-style base58btc (Bitcoin alphabet, no checksum) for did:key. *)
module Base58 = struct
  let alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

  (** Encode raw bytes as base58btc (Bitcoin alphabet, no checksum). *)
  let encode (data : string) : string =
    let len = String.length data in
    if len = 0 then ""
    else
      let rec count_zeros i =
        if i < len && data.[i] = '\x00' then count_zeros (i + 1) else i
      in
      let leading = count_zeros 0 in
      let size = (len * 138 / 100) + 1 in
      let buf = Array.make size 0 in
      let length = ref 0 in
      for i = leading to len - 1 do
        let carry = ref (Char.code data.[i]) in
        let j = ref 0 in
        let k = ref (size - 1) in
        while !k >= 0 && (!carry <> 0 || !j < !length) do
          carry := !carry + (256 * buf.(!k));
          buf.(!k) <- !carry mod 58;
          carry := !carry / 58;
          incr j;
          decr k
        done;
        length := !j
      done;
      let rec skip i = if i < size && buf.(i) = 0 then skip (i + 1) else i in
      let start = skip 0 in
      let prefix = String.make leading '1' in
      let body =
        String.init (size - start) (fun i -> alphabet.[buf.(start + i)])
      in
      prefix ^ body

  let value_of c =
    match String.index_opt alphabet c with
    | Some i -> i
    | None -> failwith ("Base58.decode: invalid character " ^ String.make 1 c)

  (** Decode a base58btc string to raw bytes. *)
  let decode (s : string) : string =
    let len = String.length s in
    if len = 0 then ""
    else
      let rec count_ones i =
        if i < len && s.[i] = '1' then count_ones (i + 1) else i
      in
      let leading = count_ones 0 in
      let size = (len * 733 / 1000) + 1 in
      let buf = Array.make size 0 in
      let length = ref 0 in
      for i = leading to len - 1 do
        let carry = ref (value_of s.[i]) in
        let j = ref 0 in
        let k = ref (size - 1) in
        while !k >= 0 && (!carry <> 0 || !j < !length) do
          carry := !carry + (58 * buf.(!k));
          buf.(!k) <- !carry mod 256;
          carry := !carry / 256;
          incr j;
          decr k
        done;
        length := !j
      done;
      let rec skip i = if i < size && buf.(i) = 0 then skip (i + 1) else i in
      let start = skip 0 in
      let prefix = String.make leading '\x00' in
      let body =
        String.init (size - start) (fun i -> Char.chr buf.(start + i))
      in
      prefix ^ body
end