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