|
6 | 6 | #include "brave/components/brave_wallet/common/encoding_utils.h"
|
7 | 7 |
|
8 | 8 | #include "base/containers/span.h"
|
| 9 | +#include "base/containers/span_writer.h" |
9 | 10 | #include "base/containers/to_vector.h"
|
10 | 11 | #include "brave/components/brave_wallet/common/hash_utils.h"
|
11 | 12 | #include "brave/third_party/bitcoin-core/src/src/base58.h"
|
12 | 13 |
|
13 | 14 | namespace brave_wallet {
|
14 | 15 |
|
15 | 16 | namespace {
|
16 |
| -constexpr char kSs58HashPersonalizer[] = "SS58PRE"; |
17 |
| -} |
| 17 | +constexpr char kSs58HashPrefix[] = "SS58PRE"; |
| 18 | +constexpr size_t kSs58HashPrefixSize = 7u; |
| 19 | +constexpr size_t kSs58HashChecksumSize = 2u; |
| 20 | +} // namespace |
18 | 21 |
|
19 | 22 | Ss58Address::Ss58Address() = default;
|
20 | 23 | Ss58Address::~Ss58Address() = default;
|
| 24 | +Ss58Address::Ss58Address(Ss58Address&& addr) = default; |
| 25 | +Ss58Address& Ss58Address::operator=(Ss58Address&& addr) = default; |
21 | 26 |
|
22 | 27 | std::string Base58EncodeWithCheck(const std::vector<uint8_t>& bytes) {
|
23 | 28 | auto with_checksum = bytes;
|
@@ -51,185 +56,95 @@ std::string Base58Encode(base::span<const uint8_t> bytes) {
|
51 | 56 | return EncodeBase58(bytes);
|
52 | 57 | }
|
53 | 58 |
|
54 |
| -// let prefix = address.prefix |
55 |
| -// assert(Number.isInteger(prefix) && prefix >= 0 && prefix < 16384, 'invalid prefix') |
56 |
| -// let len = address.bytes.length |
57 |
| -// let hashLen: number |
58 |
| -// switch(len) { |
59 |
| -// case 1: |
60 |
| -// case 2: |
61 |
| -// case 4: |
62 |
| -// case 8: |
63 |
| -// hashLen = 1 |
64 |
| -// break |
65 |
| -// case 32: |
66 |
| -// case 33: |
67 |
| -// hashLen = 2 |
68 |
| -// break |
69 |
| -// default: |
70 |
| -// assert(false, 'invalid address length') |
71 |
| -// } |
72 |
| -// let buf |
73 |
| -// let offset |
74 |
| -// if (prefix < 64) { |
75 |
| -// buf = Buffer.allocUnsafe(1 + hashLen + len) |
76 |
| -// buf[0] = prefix |
77 |
| -// offset = 1 |
78 |
| -// } else { |
79 |
| -// buf = Buffer.allocUnsafe(2 + hashLen + len) |
80 |
| -// buf[0] = ((prefix & 0b1111_1100) >> 2) | 0b01000000 |
81 |
| -// buf[1] = (prefix >> 8) | ((prefix & 0b11) << 6) |
82 |
| -// offset = 2 |
83 |
| -// } |
84 |
| -// buf.set(address.bytes, offset) |
85 |
| -// computeHash(buf, hashLen) |
86 |
| -// for (let i = 0; i < hashLen; i++) { |
87 |
| -// buf[offset + len + i] = HASH_BUF[i] |
88 |
| -// } |
89 |
| -// return base58.encode(buf) |
90 |
| - |
| 59 | +// Reference implementation |
| 60 | +// https://github.com/gear-tech/gear/blob/7d481fed39e7b0633ca9afeed8ce1b3cbb636f3e/utils/ss58/src/lib.rs#L295 |
91 | 61 | std::optional<std::string> Ss58Encode(const Ss58Address& addr) {
|
92 | 62 | uint16_t prefix = addr.prefix;
|
93 |
| - if (prefix > 16384) { |
94 |
| - return std::nullopt; |
95 |
| - } |
96 |
| - size_t hash_len = 0; |
97 |
| - size_t len = addr.public_key.size(); |
98 |
| - if (len <= 8) { |
99 |
| - hash_len = 1; |
100 |
| - } else if (len <= 33) { |
101 |
| - hash_len = 2; |
102 |
| - } else { |
| 63 | + if (prefix > 16383) { |
103 | 64 | return std::nullopt;
|
104 | 65 | }
|
105 | 66 |
|
106 |
| - size_t offset = 0; |
107 | 67 | std::vector<uint8_t> buff;
|
108 |
| - if (prefix < 64) { |
109 |
| - buff.reserve(1 + hash_len + len); |
110 |
| - buff.push_back(prefix); |
111 |
| - offset = 1; |
| 68 | + size_t offset = prefix < 64 ? 1 : 2; |
| 69 | + buff.resize(offset + kSs58PublicKeySize + kSs58HashChecksumSize); |
| 70 | + auto output_span_writer = base::SpanWriter(base::span(buff)); |
| 71 | + |
| 72 | + if (offset == 1) { |
| 73 | + output_span_writer.WriteU8BigEndian(prefix); |
112 | 74 | } else {
|
113 |
| - buff.reserve(2 + hash_len + len); |
114 |
| - buff.push_back(((prefix & 0b11111100) >> 2) | 0b01000000); |
115 |
| - buff.push_back((prefix >> 8) | ((prefix & 0b11) << 6)); |
116 |
| - offset = 2; |
| 75 | + output_span_writer.WriteU8BigEndian(((prefix & 0b11111100) >> 2) | 0b01000000); |
| 76 | + output_span_writer.WriteU8BigEndian((prefix >> 8) | ((prefix & 0b11) << 6)); |
117 | 77 | }
|
118 | 78 |
|
119 |
| - std::ranges::insert(buff, addr.public_key); |
120 |
| - std::array<uint8_t, 64> hash; |
121 |
| - Blake2bHash( |
122 |
| - buff, |
123 |
| - hash, |
124 |
| - base::byte_span_from_cstring(kSs58HashPersonalizer)); |
| 79 | + output_span_writer.Write(base::span(addr.public_key)); |
| 80 | + DCHECK_EQ(output_span_writer.remaining(), kSs58HashChecksumSize); |
| 81 | + DCHECK_EQ(buff.size(), offset + kSs58PublicKeySize + kSs58HashChecksumSize); |
125 | 82 |
|
126 |
| - // if (!hash) { |
127 |
| - // return std::nullopt; |
128 |
| - // } |
| 83 | + std::vector<uint8_t> hash_input(kSs58HashPrefixSize + buff.size() - kSs58HashChecksumSize); |
| 84 | + auto hash_input_writer = base::SpanWriter(base::span(hash_input)); |
| 85 | + hash_input_writer.Write(base::byte_span_from_cstring(kSs58HashPrefix)); |
| 86 | + hash_input_writer.Write(base::span(buff).subspan(0u, buff.size() - kSs58HashChecksumSize)); |
| 87 | + DCHECK_EQ(hash_input_writer.remaining(), 0u); |
| 88 | + auto hash = Blake2bHash<64>(base::span(hash_input)); |
129 | 89 |
|
130 |
| - for (size_t i = 0; i < hash_len; i++) { |
131 |
| - buff.push_back(hash[i]); |
132 |
| - } |
| 90 | + output_span_writer.Write(base::span(hash).subspan(0u, kSs58HashChecksumSize)); |
133 | 91 |
|
134 | 92 | return Base58Encode(buff);
|
135 | 93 | }
|
136 | 94 |
|
137 | 95 |
|
138 |
| -// export function decode(s: string): Address { |
139 |
| -// let buf = base58.decodeUnsafe(s) |
140 |
| -// if (buf == null || buf.length < 3) throw invalidAddress(s) |
141 |
| -// let b0 = buf[0] |
142 |
| -// let offset |
143 |
| -// let prefix |
144 |
| -// if (b0 < 64) { |
145 |
| -// prefix = b0 |
146 |
| -// offset = 1 |
147 |
| -// } else if (b0 < 128) { |
148 |
| -// let b1 = buf[1] |
149 |
| -// let lower = ((b0 << 2) | (b1 >> 6)) & 0b11111111 |
150 |
| -// let upper = b1 & 0b00111111 |
151 |
| -// prefix = lower | (upper << 8) |
152 |
| -// offset = 2 |
153 |
| -// } else { |
154 |
| -// throw invalidAddress(s) |
155 |
| -// } |
156 |
| -// let hashLen: number |
157 |
| -// switch(buf.length - offset) { |
158 |
| -// case 34: |
159 |
| -// case 35: |
160 |
| -// hashLen = 2 |
161 |
| -// break |
162 |
| -// case 9: |
163 |
| -// case 5: |
164 |
| -// case 3: |
165 |
| -// case 2: |
166 |
| -// hashLen = 1 |
167 |
| -// break |
168 |
| -// default: |
169 |
| -// throw invalidAddress(s) |
170 |
| -// } |
171 |
| -// computeHash(buf, hashLen) |
172 |
| -// for (let i = 0; i < hashLen; i++) { |
173 |
| -// if (HASH_BUF[i] != buf[buf.length - hashLen + i]) { |
174 |
| -// throw invalidAddress(s) |
175 |
| -// } |
176 |
| -// } |
177 |
| -// return { |
178 |
| -// prefix, |
179 |
| -// bytes: buf.subarray(offset, buf.length - hashLen) |
180 |
| -// } |
181 |
| -// } |
| 96 | +// Reference implementation |
| 97 | +// https://github.com/gear-tech/gear/blob/7d481fed39e7b0633ca9afeed8ce1b3cbb636f3e/utils/ss58/src/lib.rs#L243 |
182 | 98 | std::optional<Ss58Address> Ss58Decode(const std::string& str) {
|
183 |
| - auto result = Base58Decode(str, &result, 0, false); |
184 |
| - if (!result || result->length < 3) { |
| 99 | + auto result = Base58Decode(str, 36, false); |
| 100 | + if (!result || result->size() < kSs58HashChecksumSize + kSs58PublicKeySize + 1) { |
185 | 101 | return std::nullopt;
|
186 | 102 | }
|
187 | 103 | uint8_t offset = 0;
|
188 |
| - uint8_t address_type = result[0]; |
| 104 | + uint8_t address_type = (*result)[0]; |
189 | 105 | uint16_t prefix = 0;
|
190 | 106 | if (address_type < 64) {
|
191 | 107 | offset = 1;
|
192 | 108 | prefix = address_type;
|
193 | 109 | } else if (address_type < 128) {
|
194 | 110 | offset = 2;
|
195 |
| - uint8_t address_type_1 = result[1]; |
| 111 | + uint8_t address_type_1 = (*result)[1]; |
196 | 112 | uint8_t lower = ((address_type << 2) | (address_type_1 >> 6)) & 0b11111111;
|
197 | 113 | uint8_t upper = address_type_1 & 0b00111111;
|
198 | 114 | prefix = lower | (upper << 8);
|
199 | 115 | } else {
|
200 | 116 | return std::nullopt;
|
201 | 117 | }
|
202 | 118 |
|
203 |
| - size_t hash_len = 0; |
204 |
| - switch (result->size() - offset) { |
205 |
| - case 34: |
206 |
| - case 35: |
207 |
| - hash_len = 2; |
208 |
| - break; |
209 |
| - case 9: |
210 |
| - case 5: |
211 |
| - case 3: |
212 |
| - case 2: |
213 |
| - hash_len = 1; |
214 |
| - break; |
215 |
| - default: |
216 |
| - return std::nullopt; |
| 119 | + if (result->size() != offset + kSs58HashChecksumSize + kSs58PublicKeySize) { |
| 120 | + return std::nullopt; |
217 | 121 | }
|
218 | 122 |
|
219 |
| - auto hash = Blake2bHash<64>( |
220 |
| - base::span(*result).subspan(result->length() - hash_len), |
221 |
| - base::byte_span_from_cstring(kSs58HashPersonalizer)); |
222 |
| - |
223 |
| - for (size_t i = 0; i < hash_len; i++) { |
224 |
| - if (hash[i] != *result[result->length() - hash_len + i]) { |
| 123 | + // Prepare input to calculate checksum |
| 124 | + std::vector<uint8_t> hash_input(kSs58HashPrefixSize + result->size() - |
| 125 | + kSs58HashChecksumSize); |
| 126 | + auto hash_input_writer = base::SpanWriter(base::span(hash_input)); |
| 127 | + hash_input_writer.Write(base::byte_span_from_cstring(kSs58HashPrefix)); |
| 128 | + hash_input_writer.Write( |
| 129 | + base::span(*result).subspan(0u, result->size() - kSs58HashChecksumSize)); |
| 130 | + DCHECK_EQ(hash_input_writer.remaining(), 0u); |
| 131 | + auto hash = Blake2bHash<64>(base::span(hash_input)); |
| 132 | + |
| 133 | + // Verify checksum |
| 134 | + for (size_t i = 0; i < kSs58HashChecksumSize; i++) { |
| 135 | + if (hash[i] != (*result)[result->size() - kSs58HashChecksumSize + i]) { |
225 | 136 | return std::nullopt;
|
226 | 137 | }
|
227 | 138 | }
|
228 | 139 |
|
229 |
| - return Ss58Address{ |
230 |
| - prefix, |
231 |
| - base::ToVector(base::span(*result)).subspan(offset, result->size() - hash_len) |
232 |
| - }; |
| 140 | + // Prepare output |
| 141 | + Ss58Address result_addr; |
| 142 | + result_addr.prefix = prefix; |
| 143 | + auto public_key_span_writer = base::SpanWriter(base::span(result_addr.public_key)); |
| 144 | + public_key_span_writer.Write(base::span(*result).subspan( |
| 145 | + offset, result->size() - kSs58HashChecksumSize - offset)); |
| 146 | + DCHECK_EQ(public_key_span_writer.remaining(), 0u); |
| 147 | + return std::move(result_addr); |
233 | 148 | }
|
234 | 149 |
|
235 | 150 | } // namespace brave_wallet
|
0 commit comments