比特币钱包地址生成

比特币钱包地址生成代码-go实现

参照贴:https://blog.csdn.net/jason_cuijiahui/article/details/79305689

package main

import (
    "bytes"
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/sha256"
    "fmt"
    "golang.org/x/crypto/ripemd160"
    "math/big"
    "os"
    "strings"
)

func main() {
    //0 - 有一个私有的ECDSA键
    fmt.Println("0 - 有一个私有的ECDSA键")
    curve := elliptic.P256()

    private, err := ecdsa.GenerateKey(curve, rand.Reader)
    fmt.Println("这是私钥:" + Tool_DecimalByteSlice2HexString(private.D.Bytes()))

    //1 - 生成相应的公钥
    fmt.Println("1 - 生成相应的公钥")
    pubKey := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...)
    fmt.Println("这是公钥:" + Tool_DecimalByteSlice2HexString(pubKey))
    if err != nil {
        fmt.Errorf(err.Error())
        os.Exit(1)
    }

    //2 - 将公钥进行256hash
    fmt.Println("2 - 将公钥进行256hash")
    publicSHA256 := sha256.Sum256(pubKey)
    fmt.Println("这是公钥hash:" + Tool_DecimalByteSlice2HexString(publicSHA256[:]))

    //crypto.RIPEMD160.HashFunc().New()
    RIPMD160Hasher := ripemd160.New()

    //3 - 在SHA-256的结果上执行RIPEMD-160哈希。
    fmt.Println("3 - 在SHA-256的结果上执行RIPEMD-160哈希。")
    _, err = RIPMD160Hasher.Write(publicSHA256[:])
    if err != nil {
        fmt.Errorf(err.Error())
        os.Exit(1)
    }

    //4 - 在RIPEMD-160散列前添加版本字节(主网络的0x00)
    fmt.Println("4 - 在RIPEMD-160散列前添加版本字节(主网络的0x00)")
    var by [1]byte
    by[0] = 0x00
    publicRIPEMD160 := RIPMD160Hasher.Sum(by[:])
    fmt.Println("RIPEMD-160散列:" + Tool_DecimalByteSlice2HexString(publicRIPEMD160))

    //注意下面的步骤是Base58Check编码,它有多个库选项可用来实现它。

    //5、在扩展的RIPEMD-160结果上执行SHA-256散列。
    fmt.Println("5、在扩展的RIPEMD-160结果上执行SHA-256散列。")
    rehash := sha256.Sum256(publicRIPEMD160)
    fmt.Println("第一次hash:" + Tool_DecimalByteSlice2HexString(rehash[:]))

    //6、对之前的SHA-256散列的结果执行SHA-256散列。
    fmt.Println("6、对之前的SHA-256散列的结果执行SHA-256散列。")
    rerehash := sha256.Sum256(rehash[:])
    fmt.Println("再一次hash:" + Tool_DecimalByteSlice2HexString(rerehash[:]))

    //7、以第二个SHA-256散列的前4个字节为例。这是地址校验和。
    fmt.Println("7、以第二个SHA-256散列的前4个字节为例。这是地址校验和。")
    sum := checksum(rerehash[:])
    fmt.Println("校验地址:" + Tool_DecimalByteSlice2HexString(sum))

    //8、将第7阶段的4个校验和字节添加到第4阶段扩展的RIPEMD-160散列的末尾。这是25字节的二进制比特币地址。
    fmt.Println("8、将第7阶段的4个校验和字节添加到第4阶段扩展的RIPEMD-160散列的末尾。这是25字节的二进制比特币地址。")
    var b bytes.Buffer
    b.Write(publicRIPEMD160[:])
    b.Write(sum)
    result := b.Bytes()
    fmt.Println("拼接校验地址:" + Tool_DecimalByteSlice2HexString(result))

    //9、使用Base58Check编码将一个字节字符串的结果转换为base58字符串。这是最常用的比特币地址格式。
    fmt.Println("9、使用Base58Check编码将一个字节字符串的结果转换为base58字符串。这是最常用的比特币地址格式。")
    address := Encode(result)

    fmt.Println("最终地址:" + address)
}

func checksum(payload []byte) []byte {
    addressChecksumLen := 4
    firstSHA := sha256.Sum256(payload)
    secondSHA := sha256.Sum256(firstSHA[:])

    return secondSHA[:addressChecksumLen]
}

func Tool_DecimalByteSlice2HexString(DecimalSlice []byte) string {
    var sa = make([]string, 0)
    for _, v := range DecimalSlice {
        sa = append(sa, fmt.Sprintf("%02X", v))
    }
    ss := strings.Join(sa, "")
    return ss
}

const alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

// EncodeBig encodes src, appending to dst. Be sure to use the returned
// new value of dst.
func EncodeBig(dst []byte, src *big.Int) []byte {
    start := len(dst)
    n := new(big.Int)
    n.Set(src)
    radix := big.NewInt(58)
    zero := big.NewInt(0)

    for n.Cmp(zero) > 0 {
        mod := new(big.Int)
        n.DivMod(n, radix, mod)
        dst = append(dst, alphabet[mod.Int64()])
    }

    for i, j := start, len(dst)-1; i < j; i, j = i+1, j-1 {
        dst[i], dst[j] = dst[j], dst[i]
    }
    return dst
}

func Encode(encoded []byte) string {
    //Perform SHA-256 twice
    hash := sha256.Sum256(encoded)
    hash = sha256.Sum256(hash[:])

    //First 4 bytes if this double-sha'd byte array is the checksum
    //Append this checksum to the input bytes
    encoded = append(encoded, hash[0:4]...)

    //Convert this checksum'd version to a big Int
    bigIntEncodedChecksum := new(big.Int).SetBytes(encoded)

    //Encode the big int checksum'd version into a Base58Checked string
    base58EncodedChecksum := EncodeBig(nil, bigIntEncodedChecksum)

    //Now for each zero byte we counted above we need to prepend a 1 to our
    //base58 encoded string. The rational behind this is that base58 removes 0's (0x00).
    //So bitcoin demands we add leading 0s back on as 1s.
    buffer := make([]byte, 0, len(base58EncodedChecksum))

    //base58 alone is not enough. We need to first count each of the zero bytes
    //which are at the beginning of the encodedCheckSum

    for _, v := range encoded {
        if v != 0 {
            break
        }
        buffer = append(buffer, '1')
    }
    buffer = append(buffer, base58EncodedChecksum...)
    return string(buffer)
}

注意上方引入的ripemd160包,如果使用golang自带的包,则会出现方法无法到达异常。
具体堆栈如下:

panic: crypto: requested hash function #9 is unavailable

goroutine 1 [running]:
crypto.Hash.New(0x9, 0x40, 0x40)
        C:/Go/src/crypto/crypto.go:89 +0x117
main.HashPubKey(0xc042096100, 0x40, 0x40, 0xc0420491c0, 0xc0420491e0, 0xc042049180)
        D:/go/src/Blockchain5/wallet.go:49 +0x91
main.Wallet.GetAddress(0x637da0, 0xc04204f540, 0xc0420491c0, 0xc0420491e0, 0xc042049180, 0xc042096100, 0x40, 0x40, 0x0, 0x0, ...)
        D:/go/src/Blockchain5/wallet.go:34 +0x61
main.(*Wallets).CreateWallet(0xc0420724e8, 0x6365e0, 0xc042066a80)
        D:/go/src/Blockchain5/wallets.go:31 +0x5f
main.(*CLI).createWallet(0xc04206ff70)
        D:/go/src/Blockchain5/cli_createwallet.go:7 +0x3e
main.(*CLI).Run(0xc04206ff70)
        D:/go/src/Blockchain5/cli.go:100 +0x5a8
main.main()
        D:/go/src/Blockchain5/main.go:5 +0x32

执行结果参考:

0 - 有一个私有的ECDSA键
这是私钥:255B7133F493C6288D903D54FC69051890411C11090CA39E6D0F8D67A490CF28
1 - 生成相应的公钥
这是公钥:A7BF91E4DE89F28FAAF7392C36D9698358A4879A07B707CA2E882BAB7DE64A965D8A043E351CF8F2475600785A1F9ADBC266801AF77ED2E0A9B4D5374E6FC7ED
2 - 将公钥进行256hash
这是公钥hash2D39DB4AB6772DA1F1A682A62530F3EC75C0105DEC79C555834D247DAB377993
3 - 在SHA-256的结果上执行RIPEMD-160哈希。
4 - 在RIPEMD-160散列前添加版本字节(主网络的0x00)
RIPEMD-160散列:00BBEB938C24AE7EE8A2DCCA286EC7EC99FDD0378B
5、在扩展的RIPEMD-160结果上执行SHA-256散列。
第一次hash4B84E12A59098511B31D8430AEED1BE2106DD0D56121A6E75476BAE604FA3F02
6、对之前的SHA-256散列的结果执行SHA-256散列。
再一次hash:D7B2F0A128F959E1E00B28C3E3B411C3F5BF35A4BFC90D0D6B4F4D7F683A8DF7
7、以第二个SHA-256散列的前4个字节为例。这是地址校验和。
校验地址:74493FC5
8、将第7阶段的4个校验和字节添加到第4阶段扩展的RIPEMD-160散列的末尾。这是25字节的二进制比特币地址。
拼接校验地址:00BBEB938C24AE7EE8A2DCCA286EC7EC99FDD0378B74493FC5
9、使用Base58Check编码将一个字节字符串的结果转换为base58字符串。这是最常用的比特币地址格式。
最终地址:12w6y8LqLJdfUm1DhsniqSEn1wCmGjLC7PVVwrem

Process finished with exit code 0

猜你喜欢

转载自blog.csdn.net/fangdengfu123/article/details/80291150