-
Notifications
You must be signed in to change notification settings - Fork 212
/
address.go
139 lines (116 loc) · 4.24 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
package types
import (
"errors"
"fmt"
"github.com/cosmos/btcutil/bech32"
"github.com/spacemeshos/go-scale"
"github.com/spacemeshos/go-spacemesh/log"
)
const (
// AddressLength is the expected length of the address.
AddressLength = 24
// AddressReservedSpace define how much bytes from top is reserved in address for future.
AddressReservedSpace = 4
)
var (
// ErrWrongAddressLength is returned when the length of the address is not correct.
ErrWrongAddressLength = errors.New("wrong address length")
// ErrUnsupportedNetwork is returned when a network is not supported.
ErrUnsupportedNetwork = errors.New("unsupported network")
// ErrDecodeBech32 is returned when an error occurs during decoding bech32.
ErrDecodeBech32 = errors.New("error decoding bech32")
// ErrMissingReservedSpace is returned if top bytes of address is not 0.
ErrMissingReservedSpace = errors.New("missing reserved space")
)
// Config is the configuration of the address package.
var networkHrp = "sm"
func SetNetworkHRP(update string) {
networkHrp = update
log.With().Debug("network hrp updated", log.String("hrp", update))
}
func NetworkHRP() string {
return networkHrp
}
// Address represents the address of a spacemesh account with AddressLength length.
type Address [AddressLength]byte
// StringToAddress returns a new Address from a given string like `sm1abc...`.
func StringToAddress(src string) (Address, error) {
var addr Address
hrp, data, err := bech32.DecodeNoLimit(src)
if err != nil {
return addr, fmt.Errorf("%s: %w", ErrDecodeBech32, err)
}
// for encoding bech32 uses slice of 5-bit unsigned integers. convert it back it 8-bit uints.
dataConverted, err := bech32.ConvertBits(data, 5, 8, true)
if err != nil {
return addr, fmt.Errorf("error converting bech32 bits: %w", err)
}
// AddressLength+1 cause ConvertBits append empty byte to the end of the slice.
if len(dataConverted) != AddressLength+1 {
return addr, fmt.Errorf("expected %d bytes, got %d: %w", AddressLength, len(data), ErrWrongAddressLength)
}
if networkHrp != hrp {
return addr, fmt.Errorf("wrong network id: expected `%s`, got `%s`: %w", NetworkHRP(), hrp, ErrUnsupportedNetwork)
}
// check that first 4 bytes are 0.
for i := 0; i < AddressReservedSpace; i++ {
if dataConverted[i] != 0 {
return addr, fmt.Errorf("expected first %d bytes to be 0, got %d: %w", AddressReservedSpace, dataConverted[i], ErrMissingReservedSpace)
}
}
copy(addr[:], dataConverted[:])
return addr, nil
}
// Bytes gets the string representation of the underlying address.
func (a Address) Bytes() []byte { return a[:] }
// IsEmpty checks if address is empty.
func (a Address) IsEmpty() bool {
for i := AddressReservedSpace; i < AddressLength; i++ {
if a[i] != 0 {
return false
}
}
return true
}
// String implements fmt.Stringer.
func (a Address) String() string {
dataConverted, err := bech32.ConvertBits(a[:], 8, 5, true)
if err != nil {
log.Panic("error converting bech32 bits: ", err.Error())
}
result, err := bech32.Encode(NetworkHRP(), dataConverted)
if err != nil {
log.Panic("error encoding to bech32: ", err.Error())
}
return result
}
// Field returns a log field. Implements the LoggableField interface.
func (a Address) Field() log.Field {
return log.String("address", a.String())
}
// Format implements fmt.Formatter, forcing the byte slice to be formatted as is,
// without going through the stringer interface used for logging.
func (a Address) Format(s fmt.State, c rune) {
_, _ = fmt.Fprintf(s, "%"+string(c), a[:])
}
// EncodeScale implements scale codec interface.
func (a *Address) EncodeScale(e *scale.Encoder) (int, error) {
return scale.EncodeByteArray(e, a[:])
}
// DecodeScale implements scale codec interface.
func (a *Address) DecodeScale(d *scale.Decoder) (int, error) {
return scale.DecodeByteArray(d, a[:])
}
// GenerateAddress generates an address from a public key.
func GenerateAddress(publicKey []byte) Address {
var addr Address
if len(publicKey) > len(addr)-AddressReservedSpace {
publicKey = publicKey[len(publicKey)-AddressLength+AddressReservedSpace:]
}
copy(addr[AddressReservedSpace:], publicKey[:])
return addr
}
// GetHRPNetwork returns the Human-Readable-Part of bech32 addresses for a networkID.
func (a Address) GetHRPNetwork() string {
return NetworkHRP()
}