github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/types/address_oec.go (about) 1 package types 2 3 import ( 4 "fmt" 5 6 "github.com/tendermint/go-amino" 7 ) 8 9 const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 10 11 const ( 12 gen0 = 0x3b6a57b2 13 gen1 = 0x26508e6d 14 gen2 = 0x1ea119fa 15 gen3 = 0x3d4233dd 16 gen4 = 0x2a1462b3 17 ) 18 19 var gen = []int{gen0, gen1, gen2, gen3, gen4} 20 21 func (aa AccAddress) Bech32StringOptimized(bech32PrefixAccAddr string) string { 22 convertedLen := len(aa.Bytes())*8/5 + 1 23 resultLen := len(bech32PrefixAccAddr) + 1 + convertedLen + 6 24 var result = make([]byte, resultLen) 25 26 copy(result, bech32PrefixAccAddr) 27 result[len(bech32PrefixAccAddr)] = '1' 28 29 var err error 30 prefixLen := len(bech32PrefixAccAddr) + 1 31 converted := result[prefixLen:prefixLen] 32 converted, err = convertBitsTo(aa.Bytes(), 8, 5, true, converted) 33 if err != nil { 34 panic(fmt.Errorf("encoding bech32 failed %w", err)) 35 } 36 if len(converted) > convertedLen { 37 panic("returned unexpected length") 38 } 39 if len(converted) < convertedLen { 40 result = result[0 : len(result)-(convertedLen-len(converted))] 41 } 42 43 bech32ChecksumTo(bech32PrefixAccAddr, converted, result[prefixLen+len(converted):]) 44 45 err = toChars(result[prefixLen:]) 46 if err != nil { 47 panic(fmt.Errorf("unable to convert data bytes to chars: "+ 48 "%v", err)) 49 } 50 return amino.BytesToStr(result) 51 } 52 53 // Encode encodes a byte slice into a bech32 string with the 54 // human-readable part hrb. Note that the bytes must each encode 5 bits 55 // (base32). 56 func encode(hrp string, data []byte) (string, error) { 57 // Calculate the checksum of the data and append it at the end. 58 resultLen := len(hrp) + 1 + len(data) + 6 59 var result = make([]byte, resultLen) 60 copy(result, hrp) 61 result[len(hrp)] = '1' 62 copy(result[len(hrp)+1:], data) 63 64 bech32ChecksumTo(hrp, data, result[len(hrp)+1+len(data):]) 65 66 err := toChars(result[len(hrp)+1:]) 67 if err != nil { 68 return "", fmt.Errorf("unable to convert data bytes to chars: "+ 69 "%v", err) 70 } 71 return amino.BytesToStr(result), nil 72 } 73 74 func convertBits(data []byte, fromBits, toBits uint8, pad bool) ([]byte, error) { 75 var regrouped = make([]byte, 0, len(data)*int(fromBits)/int(toBits)+1) 76 return convertBitsTo(data, fromBits, toBits, pad, regrouped) 77 } 78 79 func convertBitsTo(data []byte, fromBits, toBits uint8, pad bool, target []byte) ([]byte, error) { 80 if fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8 { 81 return nil, fmt.Errorf("only bit groups between 1 and 8 allowed") 82 } 83 84 // The final bytes, each byte encoding toBits bits. 85 var regrouped []byte 86 if target != nil { 87 regrouped = target[0:] 88 } else { 89 regrouped = make([]byte, 0, len(data)*int(fromBits)/int(toBits)+1) 90 } 91 92 // Keep track of the next byte we create and how many bits we have 93 // added to it out of the toBits goal. 94 nextByte := byte(0) 95 filledBits := uint8(0) 96 97 for _, b := range data { 98 99 // Discard unused bits. 100 b = b << (8 - fromBits) 101 102 // How many bits remaining to extract from the input data. 103 remFromBits := fromBits 104 for remFromBits > 0 { 105 // How many bits remaining to be added to the next byte. 106 remToBits := toBits - filledBits 107 108 // The number of bytes to next extract is the minimum of 109 // remFromBits and remToBits. 110 toExtract := remFromBits 111 if remToBits < toExtract { 112 toExtract = remToBits 113 } 114 115 // Add the next bits to nextByte, shifting the already 116 // added bits to the left. 117 nextByte = (nextByte << toExtract) | (b >> (8 - toExtract)) 118 119 // Discard the bits we just extracted and get ready for 120 // next iteration. 121 b = b << toExtract 122 remFromBits -= toExtract 123 filledBits += toExtract 124 125 // If the nextByte is completely filled, we add it to 126 // our regrouped bytes and start on the next byte. 127 if filledBits == toBits { 128 regrouped = append(regrouped, nextByte) 129 filledBits = 0 130 nextByte = 0 131 } 132 } 133 } 134 135 // We pad any unfinished group if specified. 136 if pad && filledBits > 0 { 137 nextByte = nextByte << (toBits - filledBits) 138 regrouped = append(regrouped, nextByte) 139 filledBits = 0 140 nextByte = 0 141 } 142 143 // Any incomplete group must be <= 4 bits, and all zeroes. 144 if filledBits > 0 && (filledBits > 4 || nextByte != 0) { 145 return nil, fmt.Errorf("invalid incomplete group") 146 } 147 148 return regrouped, nil 149 } 150 151 func bech32Polymod(values []int) int { 152 chk := 1 153 for _, v := range values { 154 b := chk >> 25 155 chk = (chk&0x1ffffff)<<5 ^ v 156 for i := 0; i < 5; i++ { 157 if (b>>uint(i))&1 == 1 { 158 chk ^= gen[i] 159 } 160 } 161 } 162 return chk 163 } 164 165 func bech32PolymodInternal(chk int, v int) int { 166 b := chk >> 25 167 chk = (chk&0x1ffffff)<<5 ^ v 168 169 if (b>>uint(0))&1 == 1 { 170 chk ^= gen0 171 } 172 if (b>>uint(1))&1 == 1 { 173 chk ^= gen1 174 } 175 if (b>>uint(2))&1 == 1 { 176 chk ^= gen2 177 } 178 if (b>>uint(3))&1 == 1 { 179 chk ^= gen3 180 } 181 if (b>>uint(4))&1 == 1 { 182 chk ^= gen4 183 } 184 185 return chk 186 } 187 188 // For more details on HRP expansion, please refer to BIP 173. 189 func bech32HrpExpand(hrp string) []int { 190 v := make([]int, 0, len(hrp)*2+1) 191 for i := 0; i < len(hrp); i++ { 192 v = append(v, int(hrp[i]>>5)) 193 } 194 v = append(v, 0) 195 for i := 0; i < len(hrp); i++ { 196 v = append(v, int(hrp[i]&31)) 197 } 198 return v 199 } 200 201 func bech32ChecksumTo(hrp string, data []byte, result []byte) { 202 chk := 1 203 204 // hrpExpand 205 for i := 0; i < len(hrp); i++ { 206 chk = bech32PolymodInternal(chk, int(hrp[i]>>5)) 207 } 208 chk = bech32PolymodInternal(chk, 0) 209 for i := 0; i < len(hrp); i++ { 210 chk = bech32PolymodInternal(chk, int(hrp[i]&31)) 211 } 212 213 // Convert the bytes to list of integers, as this is needed for the 214 // checksum calculation. 215 for i := 0; i < len(data); i++ { 216 chk = bech32PolymodInternal(chk, int(data[i])) 217 } 218 219 for i := 0; i < 6; i++ { 220 chk = bech32PolymodInternal(chk, 0) 221 } 222 223 polymod := chk ^ 1 224 if len(result) != 6 { 225 panic("need more space") 226 } 227 for i := 0; i < 6; i++ { 228 result[i] = byte((polymod >> uint(5*(5-i))) & 31) 229 } 230 } 231 232 // toChars converts the byte slice 'data' to a string where each byte in 'data' 233 // encodes the index of a character in 'charset'. 234 func toChars(data []byte) error { 235 for i := 0; i < len(data); i++ { 236 if int(data[i]) >= len(charset) { 237 return fmt.Errorf("invalid data byte: %v", data[i]) 238 } 239 data[i] = charset[data[i]] 240 } 241 return nil 242 }