-
Notifications
You must be signed in to change notification settings - Fork 287
/
address.go
457 lines (400 loc) · 18.3 KB
/
address.go
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
// Copyright (c) 2021-2023 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
// Package stdaddr provides facilities for working with human-readable Decred
// payment addresses.
package stdaddr
import (
"fmt"
"github.com/decred/dcrd/crypto/ripemd160"
)
// AddressParams defines an interface that is used to provide the parameters
// required when encoding and decoding addresses. These values are typically
// well-defined and unique per network.
type AddressParams interface {
AddressParamsV0
}
// Address represents any type of destination a transaction output may spend to.
// Some examples include pay-to-pubkey (P2PK), pay-to-pubkey-hash (P2PKH), and
// pay-to-script-hash (P2SH). Address is designed to be generic enough that
// other kinds of addresses may be added in the future without changing the
// decoding and encoding API.
type Address interface {
// String returns the string encoding of the payment address for the
// associated script version and payment script.
String() string
// PaymentScript returns the script version associated with the address
// along with a script to pay a transaction output to the address.
PaymentScript() (uint16, []byte)
}
// StakeAddress is an interface for generating the specialized scripts that are
// used in the staking system. Only specific address types are supported by the
// staking system and therefore only those address types will implement this
// interface.
//
// Version 1 and 3 staking transactions only support version 0 scripts and
// address types of AddressPubKeyHashEcdsaSecp256k1V0 and AddressScriptHashV0.
//
// Callers can programmatically assert a specific address implements this
// interface to access the methods.
type StakeAddress interface {
Address
// VotingRightsScript returns the script version associated with the address
// along with a script to give voting rights to the address. It is only
// valid when used in stake ticket purchase transactions.
VotingRightsScript() (uint16, []byte)
// RewardCommitmentScript returns the script version associated with the
// address along with a script that commits the original funds locked to
// purchase a ticket plus the reward to the address along with limits to
// impose on any fees (in atoms).
//
// Note that fee limits are encoded in the commitment script in terms of the
// closest base 2 exponent that results in a limit that is >= the provided
// limit. In other words, the limits are rounded up to the next power of 2
// when they are not already an exact power of 2. For example, a revocation
// limit of 2^23 + 1 will result in allowing a revocation fee of up to 2^24
// atoms.
RewardCommitmentScript(amount, voteFeeLimit, revocationFeeLimit int64) (uint16, []byte)
// StakeChangeScript returns the script version associated with the address
// along with a script to pay change to the address. It is only valid when
// used in stake ticket purchase and treasury add transactions.
StakeChangeScript() (uint16, []byte)
// PayVoteCommitmentScript returns the script version associated with the
// address along with a script to pay the original funds locked to purchase
// a ticket plus the reward to the address. The address must have
// previously been committed to by the ticket purchase. The script is only
// valid when used in stake vote transactions whose associated tickets are
// eligible to vote.
PayVoteCommitmentScript() (uint16, []byte)
// PayRevokeCommitmentScript returns the script version associated with the
// address along with a script to revoke an expired or missed ticket which
// pays the original funds locked to purchase a ticket to the address. The
// address must have previously been committed to by the ticket purchase.
// The script is only valid when used in stake revocation transactions whose
// associated tickets have been missed or have expired.
PayRevokeCommitmentScript() (uint16, []byte)
// PayFromTreasuryScript returns the script version associated with the
// address along with a script that pays funds from the treasury to the
// address. The script is only valid when used in treasury spend
// transactions.
PayFromTreasuryScript() (uint16, []byte)
}
// SerializedPubKeyer is an interface for public key addresses that allows the
// serialized public key to be obtained from addresses that involve them.
type SerializedPubKeyer interface {
SerializedPubKey() []byte
}
// AddressPubKeyHasher is an interface for public key addresses that can be
// converted to an address that imposes an encumbrance that requires the public
// key that hashes to a given public key hash along with a valid signature for
// that public key.
//
// The specific public key and signature types are dependent on the original
// address.
type AddressPubKeyHasher interface {
AddressPubKeyHash() Address
}
// Hash160er is an interface that allows the RIPEMD-160 hash to be obtained from
// addresses that involve them.
type Hash160er interface {
Hash160() *[ripemd160.Size]byte
}
// Secp256k1PublicKey is an interface that represents a secp256k1 public key for
// use in creating pay-to-pubkey addresses that involve them.
type Secp256k1PublicKey interface {
// SerializeCompressed serializes a public key in the 33-byte compressed
// format.
SerializeCompressed() []byte
}
// NewAddressPubKeyEcdsaSecp256k1Raw returns an address that represents a
// payment destination which imposes an encumbrance that requires a valid ECDSA
// signature for a specific secp256k1 public key.
//
// The provided public key MUST be a valid secp256k1 public key serialized in
// the _compressed_ format or an error will be returned.
//
// See NewAddressPubKeyEcdsaSecp256k1 for a variant that accepts the public
// key as a concrete type instance instead.
//
// This function can be useful to callers who already need the serialized public
// key for other purposes to avoid the need to serialize it multiple times.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressPubKeyEcdsaSecp256k1Raw(scriptVersion uint16,
serializedPubKey []byte,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressPubKeyEcdsaSecp256k1V0Raw(serializedPubKey, params)
}
str := fmt.Sprintf("pubkey addresses for version %d are not supported",
scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// NewAddressPubKeyEcdsaSecp256k1 returns an address that represents a
// payment destination which imposes an encumbrance that requires a valid ECDSA
// signature for a specific secp256k1 public key.
//
// See NewAddressPubKeyEcdsaSecp256k1Raw for a variant that accepts the public
// key already serialized in the _compressed_ format instead of a concrete type.
// It can be useful to callers who already need the serialized public key for
// other purposes to avoid the need to serialize it multiple times.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressPubKeyEcdsaSecp256k1(scriptVersion uint16,
pubKey Secp256k1PublicKey,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressPubKeyEcdsaSecp256k1V0(pubKey, params)
}
str := fmt.Sprintf("pubkey addresses for version %d are not supported",
scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// NewAddressPubKeyEd25519Raw returns an address that represents a payment
// destination which imposes an encumbrance that requires a valid Ed25519
// signature for a specific Ed25519 public key.
//
// See NewAddressPubKeyEd25519 for a variant that accepts the public key as a
// concrete type instance instead.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressPubKeyEd25519Raw(scriptVersion uint16, serializedPubKey []byte,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressPubKeyEd25519V0Raw(serializedPubKey, params)
}
str := fmt.Sprintf("pubkey addresses for version %d are not supported",
scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// Ed25519PublicKey is an interface type that represents an Ed25519 public key
// for use in creating pay-to-pubkey addresses that involve them.
type Ed25519PublicKey interface {
// Serialize serializes the public key in a 32-byte compressed little endian
// format.
Serialize() []byte
}
// NewAddressPubKeyEd25519 returns an address that represents a payment
// destination which imposes an encumbrance that requires a valid Ed25519
// signature for a specific Ed25519 public key.
//
// See NewAddressPubKeyEd25519Raw for a variant that accepts the public key
// already serialized instead of a concrete type. It can be useful to callers
// who already need the serialized public key for other purposes to avoid the
// need to serialize it multiple times.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressPubKeyEd25519(scriptVersion uint16, pubKey Ed25519PublicKey,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressPubKeyEd25519V0(pubKey, params)
}
str := fmt.Sprintf("pubkey addresses for version %d are not supported",
scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// NewAddressPubKeySchnorrSecp256k1Raw returns an address that represents a
// payment destination which imposes an encumbrance that requires a valid
// EC-Schnorr-DCR signature for a specific secp256k1 public key.
//
// The provided public key MUST be a valid secp256k1 public key serialized in
// the _compressed_ format or an error will be returned.
//
// See NewAddressPubKeySchnorrSecp256k1 for a variant that accepts the public
// key as a concrete type instance instead.
//
// This function can be useful to callers who already need the serialized public
// key for other purposes to avoid the need to serialize it multiple times.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressPubKeySchnorrSecp256k1Raw(scriptVersion uint16,
serializedPubKey []byte,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressPubKeySchnorrSecp256k1V0Raw(serializedPubKey, params)
}
str := fmt.Sprintf("pubkey addresses for version %d are not supported",
scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// NewAddressPubKeySchnorrSecp256k1 returns an address that represents a payment
// destination which imposes an encumbrance that requires a valid EC-Schnorr-DCR
// signature for a specific secp256k1 public key.
//
// See NewAddressPubKeySchnorrSecp256k1Raw for a variant that accepts the public
// key already serialized in the _compressed_ format instead of a concrete type.
// It can be useful to callers who already need the serialized public key for
// other purposes to avoid the need to serialize it multiple times.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressPubKeySchnorrSecp256k1(scriptVersion uint16,
pubKey Secp256k1PublicKey,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressPubKeySchnorrSecp256k1V0(pubKey, params)
}
str := fmt.Sprintf("pubkey addresses for version %d are not supported",
scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// NewAddressPubKeyHashEcdsaSecp256k1 returns an address that represents a
// payment destination which imposes an encumbrance that requires a secp256k1
// public key that hashes to the provided public key hash along with a valid
// ECDSA signature for that public key.
//
// For version 0 scripts, the provided public key hash must be 20 bytes and is
// expected to be the Hash160 of the associated secp256k1 public key serialized
// in the _compressed_ format.
//
// It is important to note that while it is technically possible for legacy
// reasons to create this specific type of address based on the hash of a public
// key in the uncompressed format, so long as it is also redeemed with that same
// public key in uncompressed format, it is *HIGHLY* recommended to use the
// compressed format since it occupies less space on the chain and is more
// consistent with other address formats where uncompressed public keys are NOT
// supported.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressPubKeyHashEcdsaSecp256k1(scriptVersion uint16, pkHash []byte,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressPubKeyHashEcdsaSecp256k1V0(pkHash, params)
}
str := fmt.Sprintf("pubkey hash addresses for version %d are not "+
"supported", scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// NewAddressPubKeyHashEd25519 returns an address that represents a payment
// destination which imposes an encumbrance that requires an Ed25519 public key
// that hashes to the provided public key hash along with a valid Ed25519
// signature for that public key.
//
// For version 0 scripts, the provided public key hash must be 20 bytes and be
// the Hash160 of the correct public key or it will not be redeemable with the
// expected public key because it would hash to a different value than the
// payment script generated for the provided incorrect public key hash expects.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressPubKeyHashEd25519(scriptVersion uint16, pkHash []byte,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressPubKeyHashEd25519V0(pkHash, params)
}
str := fmt.Sprintf("pubkey hash addresses for version %d are not "+
"supported", scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// NewAddressPubKeyHashSchnorrSecp256k1 returns an address that represents a
// payment destination which imposes an encumbrance that requires a secp256k1
// public key in the _compressed_ format that hashes to the provided public key
// hash along with a valid EC-Schnorr-DCR signature for that public key.
//
// For version 0 scripts, the provided public key hash must be 20 bytes and is
// expected to be the Hash160 of the associated secp256k1 public key serialized
// in the _compressed_ format.
//
// WARNING: It is important to note that, unlike in the case of the ECDSA
// variant of this type of address, redemption via a public key in the
// uncompressed format is NOT supported by the consensus rules for this type, so
// it is *EXTREMELY* important to ensure the provided hash is of the serialized
// public key in the compressed format or the associated coins will NOT be
// redeemable.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressPubKeyHashSchnorrSecp256k1(scriptVersion uint16, pkHash []byte,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressPubKeyHashSchnorrSecp256k1V0(pkHash, params)
}
str := fmt.Sprintf("pubkey hash addresses for version %d are not "+
"supported", scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// NewAddressScriptHashFromHash returns an address that represents a payment
// destination which imposes an encumbrance that requires a script that hashes
// to the provided script hash along with all of the encumbrances that script
// itself imposes. The script is commonly referred to as a redeem script.
//
// For version 0 scripts, the provided script hash must be 20 bytes and is
// expected to be the Hash160 of the associated redeem script.
//
// See NewAddressScriptHash for a variant that accepts the redeem script instead
// of its hash. It can be used as a convenience for callers that have the
// redeem script available.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressScriptHashFromHash(scriptVersion uint16, scriptHash []byte,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressScriptHashV0FromHash(scriptHash, params)
}
str := fmt.Sprintf("script hash addresses for version %d are not "+
"supported", scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// NewAddressScriptHash returns an address that represents a payment destination
// which imposes an encumbrance that requires a script that hashes to the same
// value as the provided script along with all of the encumbrances that script
// itself imposes. The script is commonly referred to as a redeem script.
//
// See NewAddressScriptHashFromHash for a variant that accepts the hash of the
// script directly instead of the script. It can be useful to callers that
// either already have the script hash available or do not know the associated
// script.
//
// NOTE: Version 0 scripts are the only currently supported version.
func NewAddressScriptHash(scriptVersion uint16, redeemScript []byte,
params AddressParams) (Address, error) {
switch scriptVersion {
case 0:
return NewAddressScriptHashV0(redeemScript, params)
}
str := fmt.Sprintf("script hash addresses for version %d are not "+
"supported", scriptVersion)
return nil, makeError(ErrUnsupportedScriptVersion, str)
}
// probablyV0Base58Addr returns true when the provided string looks like a
// version 0 base58 address as determined by their length and only containing
// runes in the base58 alphabet used by Decred for version 0 addresses.
func probablyV0Base58Addr(s string) bool {
// Ensure the length is one of the possible values for supported version 0
// addresses.
if len(s) != 35 && len(s) != 53 {
return false
}
// The modified base58 alphabet used by Decred for version 0 addresses is:
// 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
for _, r := range s {
if r < '1' || r > 'z' ||
r == 'I' || r == 'O' || r == 'l' ||
(r > '9' && r < 'A') || (r > 'Z' && r < 'a') {
return false
}
}
return true
}
// DecodeAddress decodes the string encoding of an address and returns the
// relevant Address if it is a valid encoding for a known address type and is
// for the provided network.
func DecodeAddress(addr string, params AddressParams) (Address, error) {
// Parsing code for future address/script versions should be added as the
// most recent case in the switch statement. The expectation is that newer
// version addresses will become more common, so they should be checked
// first.
switch {
case probablyV0Base58Addr(addr):
return DecodeAddressV0(addr, params)
}
str := fmt.Sprintf("address %q is not a supported type", addr)
return nil, makeError(ErrUnsupportedAddress, str)
}