16
16
17
17
package fr.acinq.bitcoin
18
18
19
+ import fr.acinq.bitcoin.utils.Either
19
20
import kotlin.jvm.JvmStatic
20
21
21
22
public const val MaxBlockSize : Int = 1000000
@@ -26,63 +27,14 @@ public fun <T> List<T>.updated(i: Int, t: T): List<T> = when (i) {
26
27
else -> this .take(i) + t + this .drop(i + 1 )
27
28
}
28
29
29
- public sealed class AddressToPublicKeyScriptResult {
30
-
31
- public abstract val result: List <ScriptElt >?
32
-
33
- public fun isSuccess (): Boolean = result != null
34
-
35
- public fun isFailure (): Boolean = ! isSuccess()
36
-
37
- public data class Success (val script : List <ScriptElt >) : AddressToPublicKeyScriptResult() {
38
- override val result: List <ScriptElt > = script
39
- }
40
-
41
- public sealed class Failure : AddressToPublicKeyScriptResult () {
42
- override val result: List <ScriptElt >? = null
43
-
44
- public object ChainHashMismatch : Failure() {
45
- override fun toString (): String = " chain hash mismatch"
46
- }
47
-
48
- public object InvalidAddress : Failure() {
49
- override fun toString (): String = " invalid base58 or bech32 address "
50
- }
51
-
52
- public object InvalidBech32Address : Failure() {
53
- override fun toString (): String = " invalid bech32 address"
54
- }
55
-
56
- public data class InvalidWitnessVersion (val version : Int ) : Failure() {
57
- override fun toString (): String = " invalid witness version $version "
58
- }
59
- }
60
- }
61
-
62
- public sealed class AddressFromPublicKeyScriptResult {
63
- public abstract val result: String?
64
- public fun isSuccess (): Boolean = result != null
65
- public fun isFailure (): Boolean = ! isSuccess()
66
-
67
- public data class Success (val address : String ) : AddressFromPublicKeyScriptResult() {
68
- override val result: String = address
69
- }
70
-
71
- public sealed class Failure : AddressFromPublicKeyScriptResult () {
72
- override val result: String? = null
73
-
74
- public object InvalidChainHash : Failure() {
75
- override fun toString (): String = " invalid chain hash"
76
- }
77
-
78
- public object InvalidScript : Failure() {
79
- override fun toString (): String = " invalid script"
80
- }
81
-
82
- public data class GenericError (val t : Throwable ) : Failure() {
83
- override fun toString (): String = " generic failure: ${t.message} "
84
- }
85
- }
30
+ public sealed class BitcoinException (message : String , cause : Throwable ? ) : Exception(message, cause) {
31
+ public data object InvalidChainHash : BitcoinException (" invalid chain hash" , null )
32
+ public data object ChainHashMismatch : BitcoinException (" chain hash mismatch" , null )
33
+ public data object InvalidScript : BitcoinException (" invalid script" , null )
34
+ public data object InvalidAddress : BitcoinException (" invalid address" , null )
35
+ public data object InvalidBech32Address : BitcoinException (" invalid address" , null )
36
+ public data class InvalidWitnessVersion (val version : Int ) : BitcoinException(" invalid witness version $version " , null )
37
+ public data class GenericError (override val message : String , override val cause : Throwable ? ) : BitcoinException(message, cause)
86
38
}
87
39
88
40
public object Bitcoin {
@@ -121,77 +73,77 @@ public object Bitcoin {
121
73
* @param pubkeyScript public key script
122
74
*/
123
75
@JvmStatic
124
- public fun addressFromPublicKeyScript (chainHash : BlockHash , pubkeyScript : List <ScriptElt >): AddressFromPublicKeyScriptResult {
76
+ public fun addressFromPublicKeyScript (chainHash : BlockHash , pubkeyScript : List <ScriptElt >): Either < BitcoinException , String > {
125
77
try {
126
78
return when {
127
79
Script .isPay2pkh(pubkeyScript) -> {
128
80
val prefix = when (chainHash) {
129
81
Block .LivenetGenesisBlock .hash -> Base58 .Prefix .PubkeyAddress
130
82
Block .TestnetGenesisBlock .hash, Block .RegtestGenesisBlock .hash, Block .SignetGenesisBlock .hash -> Base58 .Prefix .PubkeyAddressTestnet
131
- else -> return AddressFromPublicKeyScriptResult . Failure .InvalidChainHash
83
+ else -> return Either . Left ( BitcoinException .InvalidChainHash )
132
84
}
133
- AddressFromPublicKeyScriptResult . Success (Base58Check .encode(prefix, (pubkeyScript[2 ] as OP_PUSHDATA ).data))
85
+ Either . Right (Base58Check .encode(prefix, (pubkeyScript[2 ] as OP_PUSHDATA ).data))
134
86
}
135
87
136
88
Script .isPay2sh(pubkeyScript) -> {
137
89
val prefix = when (chainHash) {
138
90
Block .LivenetGenesisBlock .hash -> Base58 .Prefix .ScriptAddress
139
91
Block .TestnetGenesisBlock .hash, Block .RegtestGenesisBlock .hash, Block .SignetGenesisBlock .hash -> Base58 .Prefix .ScriptAddressTestnet
140
- else -> return AddressFromPublicKeyScriptResult . Failure .InvalidChainHash
92
+ else -> return Either . Left ( BitcoinException .InvalidChainHash )
141
93
}
142
- AddressFromPublicKeyScriptResult . Success (Base58Check .encode(prefix, (pubkeyScript[1 ] as OP_PUSHDATA ).data))
94
+ Either . Right (Base58Check .encode(prefix, (pubkeyScript[1 ] as OP_PUSHDATA ).data))
143
95
}
144
96
145
97
Script .isNativeWitnessScript(pubkeyScript) -> {
146
98
val hrp = Bech32 .hrp(chainHash)
147
99
val witnessScript = (pubkeyScript[1 ] as OP_PUSHDATA ).data.toByteArray()
148
100
when (pubkeyScript[0 ]) {
149
101
is OP_0 -> when {
150
- Script .isPay2wpkh(pubkeyScript) || Script .isPay2wsh(pubkeyScript) -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 0 , witnessScript))
151
- else -> AddressFromPublicKeyScriptResult . Failure .InvalidScript
102
+ Script .isPay2wpkh(pubkeyScript) || Script .isPay2wsh(pubkeyScript) -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 0 , witnessScript))
103
+ else -> return Either . Left ( BitcoinException .InvalidScript )
152
104
}
153
105
154
- is OP_1 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 1 , witnessScript))
155
- is OP_2 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 2 , witnessScript))
156
- is OP_3 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 3 , witnessScript))
157
- is OP_4 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 4 , witnessScript))
158
- is OP_5 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 5 , witnessScript))
159
- is OP_6 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 6 , witnessScript))
160
- is OP_7 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 7 , witnessScript))
161
- is OP_8 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 8 , witnessScript))
162
- is OP_9 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 9 , witnessScript))
163
- is OP_10 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 10 , witnessScript))
164
- is OP_11 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 11 , witnessScript))
165
- is OP_12 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 12 , witnessScript))
166
- is OP_13 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 13 , witnessScript))
167
- is OP_14 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 14 , witnessScript))
168
- is OP_15 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 15 , witnessScript))
169
- is OP_16 -> AddressFromPublicKeyScriptResult . Success (Bech32 .encodeWitnessAddress(hrp, 16 , witnessScript))
170
- else -> AddressFromPublicKeyScriptResult . Failure .InvalidScript
106
+ is OP_1 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 1 , witnessScript))
107
+ is OP_2 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 2 , witnessScript))
108
+ is OP_3 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 3 , witnessScript))
109
+ is OP_4 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 4 , witnessScript))
110
+ is OP_5 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 5 , witnessScript))
111
+ is OP_6 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 6 , witnessScript))
112
+ is OP_7 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 7 , witnessScript))
113
+ is OP_8 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 8 , witnessScript))
114
+ is OP_9 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 9 , witnessScript))
115
+ is OP_10 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 10 , witnessScript))
116
+ is OP_11 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 11 , witnessScript))
117
+ is OP_12 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 12 , witnessScript))
118
+ is OP_13 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 13 , witnessScript))
119
+ is OP_14 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 14 , witnessScript))
120
+ is OP_15 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 15 , witnessScript))
121
+ is OP_16 -> Either . Right (Bech32 .encodeWitnessAddress(hrp, 16 , witnessScript))
122
+ else -> return Either . Left ( BitcoinException .InvalidScript )
171
123
}
172
124
}
173
125
174
- else -> AddressFromPublicKeyScriptResult . Failure .InvalidScript
126
+ else -> return Either . Left ( BitcoinException .InvalidScript )
175
127
}
176
128
} catch (t: Throwable ) {
177
- return AddressFromPublicKeyScriptResult . Failure .GenericError (t )
129
+ return Either . Left ( BitcoinException .GenericError (" " , t) )
178
130
}
179
131
}
180
132
181
133
@JvmStatic
182
- public fun addressFromPublicKeyScript (chainHash : BlockHash , pubkeyScript : ByteArray ): AddressFromPublicKeyScriptResult {
134
+ public fun addressFromPublicKeyScript (chainHash : BlockHash , pubkeyScript : ByteArray ): Either < BitcoinException , String > {
183
135
return runCatching { Script .parse(pubkeyScript) }.fold(
184
136
onSuccess = {
185
137
addressFromPublicKeyScript(chainHash, it)
186
138
},
187
139
onFailure = {
188
- AddressFromPublicKeyScriptResult . Failure .InvalidScript
140
+ Either . Left ( BitcoinException .InvalidScript )
189
141
}
190
142
)
191
143
}
192
144
193
145
@JvmStatic
194
- public fun addressToPublicKeyScript (chainHash : BlockHash , address : String ): AddressToPublicKeyScriptResult {
146
+ public fun addressToPublicKeyScript (chainHash : BlockHash , address : String ): Either < BitcoinException , List < ScriptElt >> {
195
147
val witnessVersions = mapOf (
196
148
0 .toByte() to OP_0 ,
197
149
1 .toByte() to OP_1 ,
@@ -216,36 +168,36 @@ public object Bitcoin {
216
168
onSuccess = {
217
169
when {
218
170
it.first == Base58 .Prefix .PubkeyAddressTestnet && (chainHash == Block .TestnetGenesisBlock .hash || chainHash == Block .RegtestGenesisBlock .hash || chainHash == Block .SignetGenesisBlock .hash) ->
219
- AddressToPublicKeyScriptResult . Success (Script .pay2pkh(it.second))
171
+ Either . Right (Script .pay2pkh(it.second))
220
172
221
173
it.first == Base58 .Prefix .PubkeyAddress && chainHash == Block .LivenetGenesisBlock .hash ->
222
- AddressToPublicKeyScriptResult . Success (Script .pay2pkh(it.second))
174
+ Either . Right (Script .pay2pkh(it.second))
223
175
224
176
it.first == Base58 .Prefix .ScriptAddressTestnet && (chainHash == Block .TestnetGenesisBlock .hash || chainHash == Block .RegtestGenesisBlock .hash || chainHash == Block .SignetGenesisBlock .hash) ->
225
- AddressToPublicKeyScriptResult . Success (listOf (OP_HASH160 , OP_PUSHDATA (it.second), OP_EQUAL ))
177
+ Either . Right (listOf (OP_HASH160 , OP_PUSHDATA (it.second), OP_EQUAL ))
226
178
227
179
it.first == Base58 .Prefix .ScriptAddress && chainHash == Block .LivenetGenesisBlock .hash ->
228
- AddressToPublicKeyScriptResult . Success (listOf (OP_HASH160 , OP_PUSHDATA (it.second), OP_EQUAL ))
180
+ Either . Right (listOf (OP_HASH160 , OP_PUSHDATA (it.second), OP_EQUAL ))
229
181
230
- else -> AddressToPublicKeyScriptResult . Failure .ChainHashMismatch
182
+ else -> Either . Left ( BitcoinException .ChainHashMismatch )
231
183
}
232
184
},
233
185
onFailure = { _ ->
234
186
runCatching { Bech32 .decodeWitnessAddress(address) }.fold(
235
187
onSuccess = {
236
188
val witnessVersion = witnessVersions[it.second]
237
189
when {
238
- witnessVersion == null -> AddressToPublicKeyScriptResult . Failure .InvalidWitnessVersion (it.second.toInt())
239
- it.third.size != 20 && it.third.size != 32 -> AddressToPublicKeyScriptResult . Failure .InvalidBech32Address
240
- it.first == " bc" && chainHash == Block .LivenetGenesisBlock .hash -> AddressToPublicKeyScriptResult . Success (listOf (witnessVersion, OP_PUSHDATA (it.third)))
241
- it.first == " tb" && chainHash == Block .TestnetGenesisBlock .hash -> AddressToPublicKeyScriptResult . Success (listOf (witnessVersion, OP_PUSHDATA (it.third)))
242
- it.first == " tb" && chainHash == Block .SignetGenesisBlock .hash -> AddressToPublicKeyScriptResult . Success (listOf (witnessVersion, OP_PUSHDATA (it.third)))
243
- it.first == " bcrt" && chainHash == Block .RegtestGenesisBlock .hash -> AddressToPublicKeyScriptResult . Success (listOf (witnessVersion, OP_PUSHDATA (it.third)))
244
- else -> AddressToPublicKeyScriptResult . Failure .ChainHashMismatch
190
+ witnessVersion == null -> Either . Left ( BitcoinException .InvalidWitnessVersion (it.second.toInt() ))
191
+ it.third.size != 20 && it.third.size != 32 -> Either . Left ( BitcoinException .InvalidBech32Address )
192
+ it.first == " bc" && chainHash == Block .LivenetGenesisBlock .hash -> Either . Right (listOf (witnessVersion, OP_PUSHDATA (it.third)))
193
+ it.first == " tb" && chainHash == Block .TestnetGenesisBlock .hash -> Either . Right (listOf (witnessVersion, OP_PUSHDATA (it.third)))
194
+ it.first == " tb" && chainHash == Block .SignetGenesisBlock .hash -> Either . Right (listOf (witnessVersion, OP_PUSHDATA (it.third)))
195
+ it.first == " bcrt" && chainHash == Block .RegtestGenesisBlock .hash -> Either . Right (listOf (witnessVersion, OP_PUSHDATA (it.third)))
196
+ else -> Either . Left ( BitcoinException .ChainHashMismatch )
245
197
}
246
198
},
247
199
onFailure = {
248
- AddressToPublicKeyScriptResult . Failure .InvalidAddress
200
+ Either . Left ( BitcoinException .InvalidAddress )
249
201
}
250
202
)
251
203
}
0 commit comments