Golang 语言版奇亚(Chia, XCH) 核心之 `xch` 地址和 `puzzle_hash` 之间的相互转换

yufei       3 年, 6 月 前       1192

如果你想要的是 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))
目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.