keys.gno
2.03 Kb · 82 lines
1package hyperlane
2
3import (
4 "encoding/hex"
5 "errors"
6 "strings"
7)
8
9// The Bitcoin base58 alphabet, which Solana writes its 32-byte keys in.
10const b58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
11
12// ParseKey32 reads a 32-byte key the way a person types one: 64 hex digits (with or without 0x), or a Solana
13// address in base58.
14func ParseKey32(s string) ([32]byte, error) {
15 var out [32]byte
16 s = strings.TrimSpace(s)
17 if h := strings.TrimPrefix(s, "0x"); len(h) == 64 {
18 b, err := hex.DecodeString(h)
19 if err != nil {
20 return out, errors.New("hyperlane: not hex: " + s)
21 }
22 copy(out[:], b)
23 return out, nil
24 }
25 if len(s) == 0 || len(s) > 44 {
26 return out, errors.New("hyperlane: not a 32-byte key in hex or base58: " + s)
27 }
28 digits := make([]byte, 0, 32) // the number so far, base 256, least significant first
29 for i := 0; i < len(s); i++ {
30 c := strings.IndexByte(b58, s[i])
31 if c < 0 {
32 return out, errors.New("hyperlane: not base58: " + s)
33 }
34 carry := c
35 for j := range digits {
36 carry += int(digits[j]) * 58
37 digits[j] = byte(carry)
38 carry >>= 8
39 }
40 for carry > 0 {
41 digits = append(digits, byte(carry))
42 carry >>= 8
43 }
44 }
45 zeros := len(s) - len(strings.TrimLeft(s, "1")) // each leading '1' is a leading zero byte
46 if zeros+len(digits) != 32 {
47 return out, errors.New("hyperlane: base58 key is not 32 bytes: " + s)
48 }
49 for i, d := range digits {
50 out[31-i] = d
51 }
52 return out, nil
53}
54
55// Base58 writes bytes the way Solana writes a key.
56func Base58(b []byte) string {
57 zeros := 0
58 for zeros < len(b) && b[zeros] == 0 {
59 zeros++
60 }
61 digits := make([]byte, 0, len(b)*138/100+1) // base 58, least significant first
62 for _, x := range b[zeros:] {
63 carry := int(x)
64 for j := range digits {
65 carry += int(digits[j]) << 8
66 digits[j] = byte(carry % 58)
67 carry /= 58
68 }
69 for carry > 0 {
70 digits = append(digits, byte(carry%58))
71 carry /= 58
72 }
73 }
74 out := make([]byte, zeros+len(digits))
75 for i := 0; i < zeros; i++ {
76 out[i] = '1'
77 }
78 for i, d := range digits {
79 out[len(out)-1-i] = b58[d]
80 }
81 return string(out)
82}