-
Notifications
You must be signed in to change notification settings - Fork 1
/
Base58.scala
143 lines (132 loc) · 4.58 KB
/
Base58.scala
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package scoin
import java.nio.ByteOrder
import scodec.bits.ByteVector
/*
* see https://en.bitcoin.it/wiki/Base58Check_encoding
*
* Why base-58 instead of standard base-64 encoding?
* <ul>
* <li>Don't want 0OIl characters that look the same in some fonts and could be used to create visually identical
* looking account numbers.</li>
* <li>A string with non-alphanumeric characters is not as easily accepted as an account number.</li>
* <li>E-mail usually won't line-break if there's no punctuation to break at.</li>
* <li>Doubleclicking selects the whole number as one word if it's all alphanumeric.</li>
*/
object Base58 {
object Prefix {
val PubkeyAddress = 0.toByte
val ScriptAddress = 5.toByte
val SecretKey = 128.toByte
val PubkeyAddressTestnet = 111.toByte
val ScriptAddressTestnet = 196.toByte
val SecretKeyTestnet = 239.toByte
val PubkeyAddressSegnet = 30.toByte
val ScriptAddressSegnet = 50.toByte
val SecretKeySegnet = 158.toByte
}
}
/** https://en.bitcoin.it/wiki/Base58Check_encoding Base58Check is a format
* based on Base58 and used a lot in bitcoin, for encoding addresses and
* private keys for example. It includes a prefix (usually a single byte) and a
* checksum so you know what has been encoded, and that it has been transmitted
* correctly. For example, to create an address for a public key you could
* write:
* {{{
* val pub: BinaryData = "0202a406624211f2abbdc68da3df929f938c3399dd79fac1b51b0e4ad1d26a47aa"
* val address = Base58Check.encode(Base58.Prefix.PubkeyAddress, Crypto.hash160(pub))
* }}}
* And to decode a private key you could write:
* {{{
* // check that is it a mainnet private key
* val (Base58.Prefix.SecretKey, priv) = Base58Check.decode("5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn")
* }}}
*/
object Base58Check {
def checksum(data: ByteVector) = Crypto.hash256(data).take(4)
/** Encode data in Base58Check format. For example, to create an address from
* a public key you could use:
*
* @param prefix
* version prefix (one byte)
* @param data
* date to be encoded
* @return
* a Base58 string
*/
def encode(prefix: Byte, data: ByteVector): String = {
encode(ByteVector(prefix), data)
}
/** @param prefix
* version prefix (integer, as used with BIP32 ExtendedKeys for example)
* @param data
* data to be encoded
* @return
* a Base58 String
*/
def encode(prefix: Int, data: ByteVector): String = {
encode(Protocol.writeUInt32(prefix, ByteOrder.BIG_ENDIAN), data)
}
/** @param prefix
* version prefix (several bytes, as used with BIP32 ExtendedKeys for
* example)
* @param data
* data to be encoded
* @return
* a Base58 String
*/
def encode(prefix: ByteVector, data: ByteVector): String = {
val prefixAndData = prefix ++ data
(prefixAndData ++ checksum(prefixAndData)).toBase58
}
/** Decodes Base58 data that has been encoded with a single byte prefix
*
* NB: requirement check will throw an IllegalArgumentException if the
* checksum that is part of the encoded data cannot be verified
*
* @param encoded
* encoded data
* @return
* a (prefix, data) tuple
*/
def decode(encoded: String): (Byte, ByteVector) = {
val (prefix, data) = decodeWithPrefixLen(encoded, 1)
(prefix(0), data)
}
/** Decodes Base58 data that has been encoded with an integer prefix
*
* NB: requirement check will throw an IllegalArgumentException if the
* checksum that is part of the encoded data cannot be verified
*
* @param encoded
* encoded data
* @return
* a (prefix, data) tuple
*/
def decodeWithIntPrefix(encoded: String): (Int, ByteVector) = {
val (prefix, data) = decodeWithPrefixLen(encoded, 4)
(Protocol.uint32(prefix.toArray, ByteOrder.BIG_ENDIAN).toInt, data)
}
/** Decodes Base58 data that has been encoded with several bytes prefix
*
* NB: requirement check will throw an IllegalArgumentException if the
* checksum that is part of the encoded data cannot be verified
*
* @param encoded
* encoded data
* @return
* a (prefix, data) tuple
*/
def decodeWithPrefixLen(
encoded: String,
prefixLen: Int
): (ByteVector, ByteVector) = {
val raw = ByteVector.fromValidBase58(encoded)
val versionAndHash = raw.dropRight(4)
val checksum = raw.takeRight(4)
require(
checksum == Base58Check.checksum(versionAndHash),
s"invalid Base58Check data $encoded"
)
(versionAndHash.take(prefixLen), versionAndHash.drop(prefixLen))
}
}