如果你想要的是 Python 语言版,请移步 奇亚(Chia, XCH) 核心之 xch
地址和 puzzle_hash
之间的相互转换
Golang 语言版的奇亚(Chia, XCH) 核心之 xch
地址和 puzzle_hash
之间的相互转换,如果你用的是 bech32
那个库,那么应该是达不到要求的
https://pkg.go.dev/github.com/btcsuite/btcutil/bech32#readme-examples
我小更改了下才完成要求
完整的代码如下
package main import ( "encoding/hex" "fmt" "strings" ) const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" const M = 0x2BC830A3 var gen = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} // Decode decodes a bech32 encoded string, returning the human-readable // part and the data part excluding the checksum. func Decode(bech string) (string, []byte, error) { // The maximum allowed length for a bech32 string is 90. It must also // be at least 8 characters, since it needs a non-empty HRP, a // separator, and a 6 character checksum. if len(bech) < 8 || len(bech) > 90 { return "", nil, fmt.Errorf("invalid bech32 string length %d", len(bech)) } // Only ASCII characters between 33 and 126 are allowed. for i := 0; i < len(bech); i++ { if bech[i] < 33 || bech[i] > 126 { return "", nil, fmt.Errorf("invalid character in "+ "string: '%c'", bech[i]) } } // The characters must be either all lowercase or all uppercase. lower := strings.ToLower(bech) upper := strings.ToUpper(bech) if bech != lower && bech != upper { return "", nil, fmt.Errorf("string not all lowercase or all " + "uppercase") } // We'll work with the lowercase string from now on. bech = lower // The string is invalid if the last '1' is non-existent, it is the // first character of the string (no human-readable part) or one of the // last 6 characters of the string (since checksum cannot contain '1'), // or if the string is more than 90 characters in total. one := strings.LastIndexByte(bech, '1') if one < 1 || one+7 > len(bech) { return "", nil, fmt.Errorf("invalid index of 1") } // The human-readable part is everything before the last '1'. hrp := bech[:one] data := bech[one+1:] // Each character corresponds to the byte with value of the index in // 'charset'. decoded, err := toBytes(data) if err != nil { return "", nil, fmt.Errorf("failed converting data to bytes: "+ "%v", err) } if !bech32VerifyChecksum(hrp, decoded) { moreInfo := "" checksum := bech[len(bech)-6:] expected, err := toChars(bech32Checksum(hrp, decoded[:len(decoded)-6])) if err == nil { moreInfo = fmt.Sprintf("Expected %v, got %v.", expected, checksum) } return "", nil, fmt.Errorf("checksum failed. " + moreInfo) } // We exclude the last 6 bytes, which is the checksum. return hrp, decoded[:len(decoded)-6], nil } // Encode encodes a byte slice into a bech32 string with the // human-readable part hrb. Note that the bytes must each encode 5 bits // (base32). func Encode(hrp string, data []byte) (string, error) { // Calculate the checksum of the data and append it at the end. checksum := bech32Checksum(hrp, data) combined := append(data, checksum...) // The resulting bech32 string is the concatenation of the hrp, the // separator 1, data and checksum. Everything after the separator is // represented using the specified charset. dataChars, err := toChars(combined) if err != nil { return "", fmt.Errorf("unable to convert data bytes to chars: "+ "%v", err) } return hrp + "1" + dataChars, nil } // toBytes converts each character in the string 'chars' to the value of the // index of the correspoding character in 'charset'. func toBytes(chars string) ([]byte, error) { decoded := make([]byte, 0, len(chars)) for i := 0; i < len(chars); i++ { index := strings.IndexByte(charset, chars[i]) if index < 0 { return nil, fmt.Errorf("invalid character not part of "+ "charset: %v", chars[i]) } decoded = append(decoded, byte(index)) } return decoded, nil } // toChars converts the byte slice 'data' to a string where each byte in 'data' // encodes the index of a character in 'charset'. func toChars(data []byte) (string, error) { result := make([]byte, 0, len(data)) for _, b := range data { if int(b) >= len(charset) { return "", fmt.Errorf("invalid data byte: %v", b) } result = append(result, charset[b]) } return string(result), nil } // ConvertBits converts a byte slice where each byte is encoding fromBits bits, // to a byte slice where each byte is encoding toBits bits. func ConvertBits(data []byte, fromBits, toBits uint8, pad bool) ([]byte, error) { if fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8 { return nil, fmt.Errorf("only bit groups between 1 and 8 allowed") } // The final bytes, each byte encoding toBits bits. var regrouped []byte // Keep track of the next byte we create and how many bits we have // added to it out of the toBits goal. nextByte := byte(0) filledBits := uint8(0) for _, b := range data { // Discard unused bits. b = b << (8 - fromBits) // How many bits remaining to extract from the input data. remFromBits := fromBits for remFromBits > 0 { // How many bits remaining to be added to the next byte. remToBits := toBits - filledBits // The number of bytes to next extract is the minimum of // remFromBits and remToBits. toExtract := remFromBits if remToBits < toExtract { toExtract = remToBits } // Add the next bits to nextByte, shifting the already // added bits to the left. nextByte = (nextByte << toExtract) | (b >> (8 - toExtract)) // Discard the bits we just extracted and get ready for // next iteration. b = b << toExtract remFromBits -= toExtract filledBits += toExtract // If the nextByte is completely filled, we add it to // our regrouped bytes and start on the next byte. if filledBits == toBits { regrouped = append(regrouped, nextByte) filledBits = 0 nextByte = 0 } } } // We pad any unfinished group if specified. if pad && filledBits > 0 { nextByte = nextByte << (toBits - filledBits) regrouped = append(regrouped, nextByte) filledBits = 0 nextByte = 0 } // Any incomplete group must be <= 4 bits, and all zeroes. if filledBits > 0 && (filledBits > 4 || nextByte != 0) { return nil, fmt.Errorf("invalid incomplete group") } return regrouped, nil } // For more details on the checksum calculation, please refer to BIP 173. func bech32Checksum(hrp string, data []byte) []byte { // Convert the bytes to list of integers, as this is needed for the // checksum calculation. integers := make([]int, len(data)) for i, b := range data { integers[i] = int(b) } values := append(bech32HrpExpand(hrp), integers...) values = append(values, []int{0, 0, 0, 0, 0, 0}...) polymod := bech32Polymod(values) ^ M println(polymod) var res []byte for i := 0; i < 6; i++ { res = append(res, byte((polymod>>uint(5*(5-i)))&31)) } return res } // For more details on the polymod calculation, please refer to BIP 173. func bech32Polymod(values []int) int { chk := 1 for _, v := range values { b := chk >> 25 chk = (chk&0x1ffffff)<<5 ^ v for i := 0; i < 5; i++ { if (b>>uint(i))&1 == 1 { chk ^= gen[i] } } } return chk } // For more details on HRP expansion, please refer to BIP 173. func bech32HrpExpand(hrp string) []int { v := make([]int, 0, len(hrp)*2+1) for i := 0; i < len(hrp); i++ { v = append(v, int(hrp[i]>>5)) } v = append(v, 0) for i := 0; i < len(hrp); i++ { v = append(v, int(hrp[i]&31)) } return v } // For more details on the checksum verification, please refer to BIP 173. func bech32VerifyChecksum(hrp string, data []byte) bool { integers := make([]int, len(data)) for i, b := range data { integers[i] = int(b) } concat := append(bech32HrpExpand(hrp), integers...) return bech32Polymod(concat) == 1 } func encode_puzzle_hash(puzzle, prefix string) (string, error) { var data []byte var err error if strings.HasPrefix(puzzle, "0x") || strings.HasPrefix(puzzle, "0X") { data, err = hex.DecodeString(string([]byte(puzzle)[2:])) } else { data, err = hex.DecodeString(puzzle) } if err != nil { return "", err } // Convert test data to base32: conv, err := ConvertBits(data, 8, 5, true) if err != nil { return "", err } return Encode(prefix, conv) } func decode_puzzle_hash(address string) (string, []byte, error) { hrp, decoded, err := Decode(address) if err != nil { return "", nil, err } decoded_s, err := ConvertBits(decoded, 5, 8, false) if err != nil { return "", nil, err } return hrp, decoded_s, nil } func main() { // puzzle -> address address, err := encode_puzzle_hash("e1e45b735b87f904c85d10104191af5988449a6ddb12a913e8f3478eb028fa9c", "xch") if err != nil { panic(err) } fmt.Println("Address is:", address) // address -> puzzle hrp, decoded, err := decode_puzzle_hash("xch1u8j9ku6mslusfjzazqgyryd0txyyfxndmvf2jylg7drcavpgl2wqrp26py") if err != nil { panic(err) } fmt.Println("Decoded human-readable part:", hrp) fmt.Println("Decoded Data:", hex.EncodeToString(decoded)) }
调用方式很简单
address -> puzzle
address, err := encode_puzzle_hash("e1e45b735b87f904c85d10104191af5988449a6ddb12a913e8f3478eb028fa9c", "xch") if err != nil { panic(err) } fmt.Println("Address is:", address)
puzzle -> address
hrp, decoded, err := decode_puzzle_hash("xch1u8j9ku6mslusfjzazqgyryd0txyyfxndmvf2jylg7drcavpgl2wqrp26py") if err != nil { panic(err) } fmt.Println("Decoded human-readable part:", hrp) fmt.Println("Decoded Data:", hex.EncodeToString(decoded))
目前尚无回复