|
5 | 5 |
|
6 | 6 | #include "brave/components/brave_wallet/common/encoding_utils.h"
|
7 | 7 |
|
| 8 | +#include <utility> |
| 9 | + |
| 10 | +#include "base/containers/span.h" |
| 11 | +#include "base/containers/span_writer.h" |
8 | 12 | #include "brave/components/brave_wallet/common/hash_utils.h"
|
9 | 13 | #include "brave/third_party/bitcoin-core/src/src/base58.h"
|
10 | 14 |
|
11 | 15 | namespace brave_wallet {
|
12 | 16 |
|
| 17 | +namespace { |
| 18 | +// Prefix is added a public key to calculate |
| 19 | +// blake2b hash. |
| 20 | +constexpr char kSs58HashPrefix[] = "SS58PRE"; |
| 21 | +constexpr size_t kSs58HashPrefixSize = 7u; |
| 22 | +constexpr size_t kSs58HashChecksumSize = 2u; |
| 23 | +} // namespace |
| 24 | + |
| 25 | +Ss58Address::Ss58Address() = default; |
| 26 | +Ss58Address::~Ss58Address() = default; |
| 27 | +Ss58Address::Ss58Address(Ss58Address&& addr) = default; |
| 28 | +Ss58Address& Ss58Address::operator=(Ss58Address&& addr) = default; |
| 29 | + |
13 | 30 | std::string Base58EncodeWithCheck(const std::vector<uint8_t>& bytes) {
|
14 | 31 | auto with_checksum = bytes;
|
15 | 32 | auto checksum = DoubleSHA256Hash(bytes);
|
@@ -42,4 +59,100 @@ std::string Base58Encode(base::span<const uint8_t> bytes) {
|
42 | 59 | return EncodeBase58(bytes);
|
43 | 60 | }
|
44 | 61 |
|
| 62 | +// Reference implementation |
| 63 | +// https://github.com/gear-tech/gear/blob/7d481fed39e7b0633ca9afeed8ce1b3cbb636f3e/utils/ss58/src/lib.rs#L295 |
| 64 | +std::optional<std::string> Ss58Encode(const Ss58Address& addr) { |
| 65 | + uint16_t prefix = addr.prefix; |
| 66 | + if (prefix > 16383) { |
| 67 | + return std::nullopt; |
| 68 | + } |
| 69 | + |
| 70 | + std::vector<uint8_t> buff; |
| 71 | + size_t offset = prefix < 64 ? 1 : 2; |
| 72 | + buff.resize(offset + kSs58PublicKeySize + kSs58HashChecksumSize); |
| 73 | + auto output_span_writer = base::SpanWriter(base::span(buff)); |
| 74 | + |
| 75 | + if (offset == 1) { |
| 76 | + output_span_writer.WriteU8BigEndian(prefix); |
| 77 | + } else { |
| 78 | + output_span_writer.WriteU8BigEndian(((prefix & 0b11111100) >> 2) | |
| 79 | + 0b01000000); |
| 80 | + output_span_writer.WriteU8BigEndian((prefix >> 8) | ((prefix & 0b11) << 6)); |
| 81 | + } |
| 82 | + |
| 83 | + output_span_writer.Write(base::span(addr.public_key)); |
| 84 | + DCHECK_EQ(output_span_writer.remaining(), kSs58HashChecksumSize); |
| 85 | + DCHECK_EQ(buff.size(), offset + kSs58PublicKeySize + kSs58HashChecksumSize); |
| 86 | + |
| 87 | + std::vector<uint8_t> hash_input; |
| 88 | + hash_input.resize(kSs58HashPrefixSize + offset + kSs58PublicKeySize); |
| 89 | + auto hash_input_writer = base::SpanWriter(base::span(hash_input)); |
| 90 | + hash_input_writer.Write(base::byte_span_from_cstring(kSs58HashPrefix)); |
| 91 | + hash_input_writer.Write(base::span(buff).first(offset + kSs58PublicKeySize)); |
| 92 | + DCHECK_EQ(hash_input_writer.remaining(), 0u); |
| 93 | + auto hash = Blake2bHash<64>(base::span(hash_input)); |
| 94 | + |
| 95 | + output_span_writer.Write(base::span(hash).first(kSs58HashChecksumSize)); |
| 96 | + |
| 97 | + return Base58Encode(buff); |
| 98 | +} |
| 99 | + |
| 100 | +// Reference implementation |
| 101 | +// https://github.com/gear-tech/gear/blob/7d481fed39e7b0633ca9afeed8ce1b3cbb636f3e/utils/ss58/src/lib.rs#L243 |
| 102 | +std::optional<Ss58Address> Ss58Decode(const std::string& str) { |
| 103 | + auto result = |
| 104 | + Base58Decode(str, kSs58HashChecksumSize + kSs58PublicKeySize + 2, false); |
| 105 | + if (!result || |
| 106 | + result->size() < kSs58HashChecksumSize + kSs58PublicKeySize + 1) { |
| 107 | + return std::nullopt; |
| 108 | + } |
| 109 | + uint8_t offset = 0; |
| 110 | + uint8_t address_type = (*result)[0]; |
| 111 | + uint16_t prefix = 0; |
| 112 | + if (address_type < 64) { |
| 113 | + offset = 1; |
| 114 | + prefix = address_type; |
| 115 | + } else if (address_type < 128) { |
| 116 | + offset = 2; |
| 117 | + uint8_t address_type_1 = (*result)[1]; |
| 118 | + uint8_t lower = ((address_type << 2) | (address_type_1 >> 6)) & 0b11111111; |
| 119 | + uint8_t upper = address_type_1 & 0b00111111; |
| 120 | + prefix = lower | (upper << 8); |
| 121 | + } else { |
| 122 | + return std::nullopt; |
| 123 | + } |
| 124 | + |
| 125 | + if (result->size() != offset + kSs58PublicKeySize + kSs58HashChecksumSize) { |
| 126 | + return std::nullopt; |
| 127 | + } |
| 128 | + |
| 129 | + // Prepare input to calculate checksum |
| 130 | + std::vector<uint8_t> hash_input(kSs58HashPrefixSize + offset + |
| 131 | + kSs58PublicKeySize); |
| 132 | + auto hash_input_writer = base::SpanWriter(base::span(hash_input)); |
| 133 | + hash_input_writer.Write(base::byte_span_from_cstring(kSs58HashPrefix)); |
| 134 | + hash_input_writer.Write( |
| 135 | + base::span(*result).first(offset + kSs58PublicKeySize)); |
| 136 | + DCHECK_EQ(hash_input_writer.remaining(), 0u); |
| 137 | + auto hash = Blake2bHash<64>(base::span(hash_input)); |
| 138 | + |
| 139 | + // Verify checksum |
| 140 | + for (size_t i = 0; i < kSs58HashChecksumSize; i++) { |
| 141 | + // Checking last 2 bytes |
| 142 | + if (hash[i] != (*result)[offset + kSs58PublicKeySize + i]) { |
| 143 | + return std::nullopt; |
| 144 | + } |
| 145 | + } |
| 146 | + |
| 147 | + // Prepare output |
| 148 | + Ss58Address result_addr; |
| 149 | + result_addr.prefix = prefix; |
| 150 | + auto public_key_span_writer = |
| 151 | + base::SpanWriter(base::span(result_addr.public_key)); |
| 152 | + public_key_span_writer.Write( |
| 153 | + base::span(*result).subspan(offset, kSs58PublicKeySize)); |
| 154 | + DCHECK_EQ(public_key_span_writer.remaining(), 0u); |
| 155 | + return std::move(result_addr); |
| 156 | +} |
| 157 | + |
45 | 158 | } // namespace brave_wallet
|
0 commit comments