【区块链笔记】BTC_地址计算(13)

Page content

今天看了有关算比特币地址的算法,简单的整理了一下。

1.计算方法

比特币地址生成步骤
从私钥生成开始经过非对称加密算法、哈希算法(SHA-256和RIPEMD-160)、以及Base58编码方式,最终产生公开的比特币地址。

第一步,随机选取一个32字节的数、大小介于1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141之间,作为私钥。 18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725

第二步,使用椭圆曲线加密算法(ECDSA-secp256k1)计算私钥所对应的非压缩公钥。
(共65字节, 1字节 0x04, 32字节为x坐标,32字节为y坐标)关于公钥压缩、非压缩的问题另文说明。

0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B 23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6

第三步,计算公钥的 SHA-256 哈希值 600FFE422B4E00731A59557A5CCA46CC183944191006324A447BDB2D98D4B408

第四步,取上一步结果,计算 RIPEMD-160 哈希值 010966776006953D5567439E5E39F86A0D273BEE

第五步,取上一步结果,前面加入地址版本号(比特币主网版本号“0x00”) 00010966776006953D5567439E5E39F86A0D273BEE

第六步,取上一步结果,计算 SHA-256 哈希值 445C7A8007A93D8733188288BB320A8FE2DEBD2AE1B47F0F50BC10BAE845C094

第七步,取上一步结果,再计算一下 SHA-256 哈希值 D61967F63C7DD183914A4AE452C9F6AD5D462CE3D277798075B107615C1A8A30

第八步,取上一步结果的前4个字节(8位十六进制) D61967F6

第九步,把这4个字节加在第五步的结果后面,作为校验(这就是比特币地址的16进制形态)。 00010966776006953D5567439E5E39F86A0D273BEED61967F6

第十步,用base58表示法变换一下地址(这就是最常见的比特币地址形态)。 16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM

说明: 有些数字以"0x"开头,意思是此数字使用十六进制表示法。“0x"本身没有任何含义,它是C语言流传下来的,约定俗成的写法,比如0xA就是十进制的10。另外,1个字节 = 8位二进制 = 2位十六进制)。

1BLvJjhACw8keNeqrkXTbMHYTP1cH4zoDk

详细的内容可以参考这里:https://gobittest.appspot.com/Address

2.模拟代码

在早起的交易中找到了公钥和地址,用代码一起算一下。

图片备用地址
blockchain_header_cal

参考代码

func HASH256(text string, isHex bool) (string, error) {
 sha256Instance := sha256.New()
 if isHex {
  arr, err := hex.DecodeString(text)
  if err != nil {
   return "", err
  }
  sha256Instance.Write(arr)
 } else {
  sha256Instance.Write([]byte(text))
 }
 ciphertext := sha256Instance.Sum(nil)
 return fmt.Sprintf("%x", ciphertext), nil
}

func RipMD160(text string, isHex bool) (string, error) {
 ripemd160Instance := ripemd160.New()
 if isHex {
  arr, err := hex.DecodeString(text)
  if err != nil {
   return "", err
  }
  ripemd160Instance.Write(arr)
 } else {
  ripemd160Instance.Write([]byte(text))
 }
 hashBytes := ripemd160Instance.Sum(nil)
 hashString := fmt.Sprintf("%x", hashBytes)
 return hashString, nil
}

func Hash256Double(text string, isHex bool) ([]byte, error) {
 hashInstance := sha256.New()
 if isHex {
  arr, err := hex.DecodeString(text)
  if err != nil {
   return nil, err
  }
  hashInstance.Write(arr)
 } else {
  hashInstance.Write([]byte(text))
 }

 bytes := hashInstance.Sum(nil)
 hashInstance.Reset()
 hashInstance.Write(bytes)
 bytes = hashInstance.Sum(nil)
 return bytes, nil
}

func Hash256DoubleString(text string, isHex bool) (string, error) {
 bytes, err := Hash256Double(text, isHex)
 if err != nil {
  return "", err
 }
 return fmt.Sprintf("%x", bytes), nil
}

func GenerateAddress(pubKey string, nettype int) (string, error) {
 //1.判断公钥的有效性
 if len(pubKey) != 130 && len(pubKey) != 66 {
  return "", errors.New("公钥输入的长度不合法!")
 }
 //2.计算公钥sha256
 hashString, err := HASH256(pubKey, true)
 if err != nil {
  return "", err
 }

 //3.ripemd160
 ripemd160String, err := RipMD160(hashString, true)
 if err != nil {
  return "", err
 }

 //4.添加网络id号,比特币主网0x00 testnet 0x6f
 prefix := ""
 switch nettype {
 case 0:
  prefix = "00"
 case 1:
  prefix = "6f"
 case 2:
  prefix = "34"
 }

 versionString := prefix + ripemd160String

 //5.计算hash 2次
 sha256DoubleString, err := Hash256DoubleString(versionString, true)
 if err != nil {
  return "", err
 }

 //6.获取校验码
 rs := []rune(sha256DoubleString)
 checknum := string(rs[:8])

 //7.形成16进制比特币地址
 addrHex := versionString + checknum

 //8.对16进制地址进行base58编码
 arr, err := hex.DecodeString(addrHex)
 if err != nil {
  return "", err
 }
 //9.base58
 addrBase58 := Base58Encode(arr)
 result := fmt.Sprintf("%s \n", addrBase58)

 return result, nil
}

func main() {
 pubKey := "0417a4a4037defe1c30da0c3ed0ca096ae99e846a10c594b0224cb4c54cc0d087028e27b218c422d095a11c8b9a34e2bfdb2f112311adea31245e190e3775cc5b7"
 address, _ := GenerateAddress(pubKey, 0)
 fmt.Println(address) //1Lr12b7kWNXbRiwPEGq65Y19NuKYXyPJiK
}

base58

import (
 "math/big"
)

const (
 // alphabet is the modified base58 alphabet used by Bitcoin.
 alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

 alphabetIdx0 = '1'
)

var b58 = [256]byte{
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 0, 1, 2, 3, 4, 5, 6,
 7, 8, 255, 255, 255, 255, 255, 255,
 255, 9, 10, 11, 12, 13, 14, 15,
 16, 255, 17, 18, 19, 20, 21, 255,
 22, 23, 24, 25, 26, 27, 28, 29,
 30, 31, 32, 255, 255, 255, 255, 255,
 255, 33, 34, 35, 36, 37, 38, 39,
 40, 41, 42, 43, 255, 44, 45, 46,
 47, 48, 49, 50, 51, 52, 53, 54,
 55, 56, 57, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
 255, 255, 255, 255, 255, 255, 255, 255,
}

//go:generate go run genalphabet.go

var bigRadix = big.NewInt(58)
var bigZero = big.NewInt(0)

// Decode decodes a modified base58 string to a byte slice.
func Base58Decode(b string) []byte {
 answer := big.NewInt(0)
 j := big.NewInt(1)

 scratch := new(big.Int)
 for i := len(b) - 1; i >= 0; i-- {
  tmp := b58[b[i]]
  if tmp == 255 {
   return []byte("")
  }
  scratch.SetInt64(int64(tmp))
  scratch.Mul(j, scratch)
  answer.Add(answer, scratch)
  j.Mul(j, bigRadix)
 }

 tmpval := answer.Bytes()

 var numZeros int
 for numZeros = 0; numZeros < len(b); numZeros++ {
  if b[numZeros] != alphabetIdx0 {
   break
  }
 }
 flen := numZeros + len(tmpval)
 val := make([]byte, flen)
 copy(val[numZeros:], tmpval)

 return val
}

// Encode encodes a byte slice to a modified base58 string.
func Base58Encode(b []byte) string {
 x := new(big.Int)
 x.SetBytes(b)

 answer := make([]byte, 0, len(b)*136/100)
 for x.Cmp(bigZero) > 0 {
  mod := new(big.Int)
  x.DivMod(x, bigRadix, mod)
  answer = append(answer, alphabet[mod.Int64()])
 }

 // leading zero bytes
 for _, i := range b {
  if i != 0 {
   break
  }
  answer = append(answer, alphabetIdx0)
 }

 // reverse
 alen := len(answer)
 for i := 0; i < alen/2; i++ {
  answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
 }

 return string(answer)
}

欢迎大家的意见和交流

email: li_mingxie@163.com