天天看點

比特币錢包位址生成比特币錢包位址生成代碼-go實作

比特币錢包位址生成代碼-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)
    }

    //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)
    }

    //4 - 在RIPEMD-160散列前添加版本位元組(主網絡的0x00)
    fmt.Println("4 - 在RIPEMD-160散列前添加版本位元組(主網絡的0x00)")
    var by ]byte
    by] =x00
    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 :=
    firstSHA := sha256.Sum256(payload)
    secondSHA := sha256.Sum256(firstSHA[:])

    return secondSHA[:addressChecksumLen]
}

func Tool_DecimalByteSlice2HexString(DecimalSlice []byte) string {
    var sa = make([]string,)
    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)
    zero := big.NewInt)

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

    for i, j := start, len(dst; i < j; i, j = i, j {
        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]...)

    //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,, 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 != {
            break
        }
        buffer = append(buffer, '1')
    }
    buffer = append(buffer, base58EncodedChecksum...)
    return string(buffer)
}
           

注意上方引入的ripemd160包,如果使用golang自帶的包,則會出現方法無法到達異常。

具體堆棧如下:

panic: crypto: requested hash function #9 is unavailable

goroutine  [running]:
crypto.Hash.New(, , )
        C:/Go/src/crypto/crypto.go: +
main.HashPubKey(, , , , , )
        D:/go/src/Blockchain5/wallet.go: +
main.Wallet.GetAddress(, , , , , , , , , , ...)
        D:/go/src/Blockchain5/wallet.go: +
main.(*Wallets).CreateWallet(, , )
        D:/go/src/Blockchain5/wallets.go: +
main.(*CLI).createWallet()
        D:/go/src/Blockchain5/cli_createwallet.go: +
main.(*CLI).Run()
        D:/go/src/Blockchain5/cli.go: +
main.main()
        D:/go/src/Blockchain5/main.go: +
           

執行結果參考:

- 有一個私有的ECDSA鍵
這是私鑰:B7133F493C6288D903D54FC69051890411C11090CA39E6D0F8D67A490CF28
 - 生成相應的公鑰
這是公鑰:A7BF91E4DE89F28FAAF7392C36D9698358A4879A07B707CA2E882BAB7DE64A965D8A043E351CF8F2475600785A1F9ADBC266801AF77ED2E0A9B4D5374E6FC7ED
 - 将公鑰進行hash
這是公鑰hash:D39DB4AB6772DA1F1A682A62530F3EC75C0105DEC79C555834D247DAB377993
 - 在SHA-的結果上執行RIPEMD-哈希。
 - 在RIPEMD-散列前添加版本位元組(主網絡的x00)
RIPEMD-散列:BBEB938C24AE7EE8A2DCCA286EC7EC99FDD0378B
、在擴充的RIPEMD-結果上執行SHA-散列。
第一次hash:B84E12A59098511B31D8430AEED1BE2106DD0D56121A6E75476BAE604FA3F02
、對之前的SHA-散列的結果執行SHA-散列。
再一次hash:D7B2F0A128F959E1E00B28C3E3B411C3F5BF35A4BFC90D0D6B4F4D7F683A8DF7
、以第二個SHA-散列的前個位元組為例。這是位址校驗和。
校驗位址:FC5
、将第階段的個校驗和位元組添加到第階段擴充的RIPEMD-散列的末尾。這是位元組的二進制比特币位址。
拼接校驗位址:BBEB938C24AE7EE8A2DCCA286EC7EC99FDD0378B74493FC5
、使用Base58Check編碼将一個位元組字元串的結果轉換為base58字元串。這是最常用的比特币位址格式。
最終位址:w6y8LqLJdfUm1DhsniqSEn1wCmGjLC7PVVwrem

Process finished with exit code 
           

繼續閱讀