Skip to content

Commit d24e487

Browse files
committed
Fix Base58 decoding, fixes #424
Minor optimization to the decoder (allocate a single buffer and trim it instead of allocating 2 buffers and performing a trim and then a copy and append) basic lint cleanup of the file
1 parent 2f090dd commit d24e487

File tree

1 file changed

+47
-57
lines changed

1 file changed

+47
-57
lines changed

Sources/web3swift/Convenience/Base58.swift

Lines changed: 47 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,39 @@ import Foundation
88

99
struct Base58 {
1010
static let base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
11-
11+
1212
// Encode
1313
static func base58FromBytes(_ bytes: [UInt8]) -> String {
1414
var bytes = bytes
1515
var zerosCount = 0
1616
var length = 0
17-
17+
1818
for b in bytes {
1919
if b != 0 { break }
2020
zerosCount += 1
2121
}
22-
22+
2323
bytes.removeFirst(zerosCount)
24-
24+
2525
let size = bytes.count * 138 / 100 + 1
26-
26+
2727
var base58: [UInt8] = Array(repeating: 0, count: size)
2828
for b in bytes {
2929
var carry = Int(b)
3030
var i = 0
31-
31+
3232
for j in 0...base58.count-1 where carry != 0 || i < length {
3333
carry += 256 * Int(base58[base58.count - j - 1])
3434
base58[base58.count - j - 1] = UInt8(carry % 58)
3535
carry /= 58
3636
i += 1
3737
}
38-
38+
3939
assert(carry == 0)
40-
40+
4141
length = i
4242
}
43-
43+
4444
// skip leading zeros
4545
var zerosToRemove = 0
4646
var str = ""
@@ -49,41 +49,44 @@ struct Base58 {
4949
zerosToRemove += 1
5050
}
5151
base58.removeFirst(zerosToRemove)
52-
52+
5353
while 0 < zerosCount {
5454
str = "\(str)1"
5555
zerosCount -= 1
5656
}
57-
57+
5858
for b in base58 {
59-
//str = "\(str)\(base58Alphabet[String.Index(encodedOffset: Int(b))])"
6059
str = "\(str)\(base58Alphabet[String.Index(utf16Offset: Int(b), in: base58Alphabet)])"
6160
}
62-
61+
6362
return str
6463
}
65-
64+
6665
// Decode
6766
static func bytesFromBase58(_ base58: String) -> [UInt8] {
6867
// remove leading and trailing whitespaces
6968
let string = base58.trimmingCharacters(in: CharacterSet.whitespaces)
70-
7169
guard !string.isEmpty else { return [] }
72-
73-
var zerosCount = 0
74-
var length = 0
70+
71+
// count leading "1"'s [decodes directly to zero bytes]
72+
var leadingZeros = 0
7573
for c in string {
7674
if c != "1" { break }
77-
zerosCount += 1
75+
leadingZeros += 1
7876
}
79-
80-
let size = string.lengthOfBytes(using: String.Encoding.utf8) * 733 / 1000 + 1 - zerosCount
81-
var base58: [UInt8] = Array(repeating: 0, count: size)
77+
78+
// calculate the size of the decoded output, rounded up
79+
let size = (string.lengthOfBytes(using: String.Encoding.utf8) - leadingZeros) * 733 / 1000 + 1
80+
81+
// allocate a buffer large enough for the decoded output
82+
var base58: [UInt8] = Array(repeating: 0, count: size + leadingZeros)
83+
84+
// decode what remains of the data
85+
var length = 0
8286
for c in string where c != " " {
8387
// search for base58 character
84-
guard let base58Index = base58Alphabet.index(of: c) else { return [] }
85-
86-
// var carry = base58Index.encodedOffset
88+
guard let base58Index = base58Alphabet.firstIndex(of: c) else { return [] }
89+
8790
var carry = base58Index.utf16Offset(in: base58Alphabet)
8891
var i = 0
8992
for j in 0...base58.count where carry != 0 || i < length {
@@ -92,42 +95,36 @@ struct Base58 {
9295
carry /= 256
9396
i += 1
9497
}
95-
98+
9699
assert(carry == 0)
97100
length = i
98101
}
99-
100-
// skip leading zeros
101-
var zerosToRemove = 0
102-
102+
103+
// calculate how many leading zero bytes we have
104+
var totalZeros = 0
103105
for b in base58 {
104106
if b != 0 { break }
105-
zerosToRemove += 1
107+
totalZeros += 1
106108
}
107-
base58.removeFirst(zerosToRemove)
108-
109-
var result: [UInt8] = Array(repeating: 0, count: zerosCount)
110-
for b in base58 {
111-
result.append(b)
112-
}
113-
return result
109+
// remove the excess zero bytes
110+
base58.removeFirst(totalZeros - leadingZeros)
111+
112+
return base58
114113
}
115114
}
116115

117-
118-
119116
extension Array where Element == UInt8 {
120117
public var base58EncodedString: String {
121118
guard !self.isEmpty else { return "" }
122119
return Base58.base58FromBytes(self)
123120
}
124-
121+
125122
public var base58CheckEncodedString: String {
126123
var bytes = self
127124
let checksum = [UInt8](bytes.sha256().sha256()[0..<4])
128-
125+
129126
bytes.append(contentsOf: checksum)
130-
127+
131128
return Base58.base58FromBytes(bytes)
132129
}
133130
}
@@ -136,35 +133,28 @@ extension String {
136133
public var base58EncodedString: String {
137134
return [UInt8](utf8).base58EncodedString
138135
}
139-
136+
140137
public var base58DecodedData: Data? {
141138
let bytes = Base58.bytesFromBase58(self)
142139
return Data(bytes)
143140
}
144-
141+
145142
public var base58CheckDecodedData: Data? {
146143
guard let bytes = self.base58CheckDecodedBytes else { return nil }
147144
return Data(bytes)
148145
}
149-
146+
150147
public var base58CheckDecodedBytes: [UInt8]? {
151148
var bytes = Base58.bytesFromBase58(self)
152149
guard 4 <= bytes.count else { return nil }
153-
150+
154151
let checksum = [UInt8](bytes[bytes.count-4..<bytes.count])
155152
bytes = [UInt8](bytes[0..<bytes.count-4])
156-
153+
157154
let calculatedChecksum = [UInt8](bytes.sha256().sha256()[0...3])
158155
if checksum != calculatedChecksum { return nil }
159-
156+
160157
return bytes
161158
}
162-
163-
// public var littleEndianHexToUInt: UInt {
164-
// let data = Data.fromHex(self)!
165-
// let revensed =
166-
// return UInt(sel)
167-
// return UInt(self.dataWithHexString().bytes.reversed().fullHexString,radix: 16)!
168-
// }
169-
159+
170160
}

0 commit comments

Comments
 (0)