go语言实现比特币地址生成及验证

在我的第一篇博客中,讲解了比特币地址生成的原理,接下来我们用代码实现一下,代码看不懂的可以去看一下我第一篇博客的那张图,更容易理解以下代码

一.项目创建

  • 创建项目文件夹GenerateBtcAddress
  • 在该文件夹下创建main.go文件和ADD文件夹
  • 在ADD文件夹下创建两个文件Address.go和Base58.go

二.代码部分

由于我已经在代码中加了非常详细的注释,这里我就直接附上代码,不作过多解释了

  • Base58.go中代码如下:
package ADD

import (
	"math/big"
	"bytes"
)

var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")

// 字节数组转 Base58,加密
func Base58Encode(input []byte) []byte {
	var result []byte

	x := big.NewInt(0).SetBytes(input)

	base := big.NewInt(int64(len(b58Alphabet)))
	zero := big.NewInt(0)
	mod := &big.Int{}

	for x.Cmp(zero) != 0 {
		x.DivMod(x, base, mod)
		result = append(result, b58Alphabet[mod.Int64()])
	}

	ReverseBytes(result)
	for b := range input {
		if b == 0x00 {
			result = append([]byte{b58Alphabet[0]}, result...)
		} else {
			break
		}
	}

	return result
}

// Base58转字节数组,解密
func Base58Decode(input []byte) []byte {
	result := big.NewInt(0)
	zeroBytes := 0

	for b := range input {
		if b == 0x00 {
			zeroBytes++
		}
	}

	payload := input[zeroBytes:]
	for _, b := range payload {
		charIndex := bytes.IndexByte(b58Alphabet, b)
		result.Mul(result, big.NewInt(58))
		result.Add(result, big.NewInt(int64(charIndex)))
	}

	decoded := result.Bytes()
	decoded = append(bytes.Repeat([]byte{byte(0x00)}, zeroBytes), decoded...)

	return decoded
}

// 字节数组反转
func ReverseBytes(data []byte) {
	for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
		data[i], data[j] = data[j], data[i]
	}
}
  • Address.go中代码如下:
package ADD

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"golang.org/x/crypto/ripemd160"
	"log"
)

const version = byte(0x00) //定义版本号,一个字节
const addressChecksumLen = 4 //定义checksum长度为四个字节

type Wallet struct {
	//1. 私钥
	PrivateKey ecdsa.PrivateKey

	//2. 公钥
	PublicKey  []byte
}

//判断地址是否有效
func IsValidForAdress(adress []byte) bool {
	//将地址进行base58反编码,生成的其实是version+Pub Key hash+ checksum这25个字节
	version_public_checksumBytes := Base58Decode(adress)

	//[25-4:],就是21个字节往后的数(22,23,24,25一共4个字节)
	checkSumBytes := version_public_checksumBytes[len(version_public_checksumBytes) - addressChecksumLen:]
  	//[:25-4],就是前21个字节(1~21,一共21个字节)
	version_ripemd160 := version_public_checksumBytes[:len(version_public_checksumBytes) - addressChecksumLen]
	//取version+public+checksum的字节数组的前21个字节进行两次256哈希运算,取结果值的前4个字节
	checkBytes := CheckSum(version_ripemd160)
	//将checksum比较,如果一致则说明地址有效,返回true
	if bytes.Compare(checkSumBytes,checkBytes) == 0 {
		return true
	}

	return false
}


func (w *Wallet) GetAddress() []byte  {

	//调用Ripemd160Hash返回160位的Pub Key hash
	ripemd160Hash := w.Ripemd160Hash(w.PublicKey)

	//将version+Pub Key hash
	version_ripemd160Hash := append([]byte{version},ripemd160Hash...)

	//调用CheckSum方法返回前四个字节的checksum
	checkSumBytes := CheckSum(version_ripemd160Hash)

	//将version+Pub Key hash+ checksum生成25个字节
	bytes := append(version_ripemd160Hash,checkSumBytes...)

	//将这25个字节进行base58编码并返回
	return Base58Encode(bytes)
}
//取前4个字节
func CheckSum(payload []byte) []byte {
	//这里传入的payload其实是version+Pub Key hash,对其进行两次256运算
	hash1 := sha256.Sum256(payload)

	hash2 := sha256.Sum256(hash1[:])

	return hash2[:addressChecksumLen] //返回前四个字节,为CheckSum值
}


func (w *Wallet) Ripemd160Hash(publicKey []byte) []byte {

	//将传入的公钥进行256运算,返回256位hash值
	hash256 := sha256.New()
	hash256.Write(publicKey)
	hash := hash256.Sum(nil)

	//将上面的256位hash值进行160运算,返回160位的hash值
	ripemd160 := ripemd160.New()
	ripemd160.Write(hash)

	return ripemd160.Sum(nil) //返回Pub Key hash
}


// 创建钱包
func NewWallet() *Wallet {

	privateKey,publicKey := newKeyPair()

	return &Wallet{privateKey,publicKey}
}


// 通过私钥产生公钥
func newKeyPair() (ecdsa.PrivateKey,[]byte) {
	//这是一个曲线对象
	curve := elliptic.P256()
	//通过椭圆曲线加密算法生成私钥
	private, err := ecdsa.GenerateKey(curve, rand.Reader)
	if err != nil {
		log.Panic(err)
	}
	//由私钥生成公钥
	pubKey := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...)

	return *private, pubKey
}
  • main.go中代码如下:
package main

import (
	"GenerateBtcAddress/ADD"
	"fmt"
)

func main() {
	wallet := ADD.NewWallet()

	address := wallet.GetAddress()

	fmt.Printf("address:%s\n",address)

	isValid := ADD.IsValidForAdress(address)

	fmt.Printf("%s 这个地址为 %v\n",address,isValid)
}

三.测试结果

go语言实现比特币地址生成及验证_第1张图片

说明:每次都生成固定长度的比特币地址,并且每个地址都不会相同,每个地址都是合法有效的比特币地址,可以到https://www.blockchain.com/btc/address/1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa查询地址,网址中的1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa为比特币创世区块的地址,把创世区块地址换成我们上面生成的地址,可以看到我们生成的地址生成的地址是合法有效的比特币地址,如下所示:

go语言实现比特币地址生成及验证_第2张图片

代码参考:https://github.com/Jeiwan/blockchain_go

 

你可能感兴趣的:(区块链)