比特币原理及其代码实践5-地址


review:UTXO和余额模型到底有没有区别?

有,举个例子,比如小红转我80元,小绿转我20元,我再转50元给了小黑。

UTXO模型可以明确的告诉我,我转给小黑的50元到底是来自小红给我的80元加找零,还是小绿的20元加上小红给我的80元加找零。

余额模型却无法做到可追溯,因为我转给小黑50元,是我的余额减50,小黑的余额加50,然后产生一笔交易记录(我->小黑:50元),不能确定我转出去的50来自谁。

引言

在比特币中,没有用户账户,不需要,也不会在任何地方存储个人数据(比如姓名,电话,身份信息等)。

但我们必须要能够实现交易的输出者可以使用这比交易,用官方的话来说,就是你拥有在这些输出上锁定的币

这一节,我们将要实现一个跟比特币一样的真实地址address

需用用到的知识:

  • 公钥加密
  • 数字签名
  • base58算法

公钥加密

公钥加密(public-key cryptography)算法使用的是成对的密钥:公钥和私钥。私钥加密公钥解密,或者公钥加密私钥解密。

在加密货币的世界中,你的私钥代表的就是你,私钥就是一切。

比特币钱包也只不过是这样的密钥对而已。

比特币使用椭圆曲线来产生私钥。

数字签名

签名过程:

hashData = hash(data)

加密的hashData = 私钥加密(hashData)

(data+加密的hashData) 一起发给接受方

验证的过程:

hashData_1 = hash(data)

hashData_2=公钥解密(加密的hashData)

if hashData_1 == hashData_2 : 验证通过

数字签名特点:

  1. 当数据从发送方传送到接收方时,数据不会被修改;
  2. 数据由某一确定的发送方创建;
  3. 发送方无法否认发送过数据这一事实。

比特币中,每一笔交易输入都会由创建交易的人签名。在被放入到一个块之前,必须要对每一笔交易进行验证

Base58

比特币使用 Base58 算法将字节串转换成人类可读的形式。

Base58算法是可逆的,我们对数据进行Base58编码后,可以解码还原成原来的数据。

比特币地址的产生

我们设pubKey=公钥

  1. hash_1 = SHA256(pubKey) //对公钥进行第一次hash
  2. hash_2 = RIPEMD160(hash_1) // 对hash_1进行hash
  3. versionedPayload = version + hash_2 //byte拼接
  4. hash_3 =SHA256(SHA256(versionedPayload )) // 再对versionedPayload 进行两次hash
  5. s = version(1字节) + hash_2(20字节) + hash_3(前4字节) //三个byte拼接
  6. address = Base58Encode(s) //对s进行base58编码,转成人类可读的字符串

s字符串:

Version  Public key hash                           Checksum
00       62E907B15CBF27D5425399EBF6F0FB50EBB88F18  C29B7D93

最终加密结果

1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa  \\ 属于中本聪

图:

代码实现

//钱包
type Wallet struct {
    Privatekey ecdsa.PrivateKey  \\私钥
    PublicKey  []byte  \\公钥
}

//新建一个钱包
func NewWallet() *Wallet {
    private, public = 椭圆曲线生成公私钥
    wallet := Wallet{private, public}
    return &wallet
}

const versionByte = byte(0x00)
const addressChecksumLen = 4

//获得地址
//address = base58(version + publickey hash + checksum)
func (w Wallet) GetAddress() []byte {
    //RIPEMD160(SHA256(pubKey))
    pubkeyhash := HashPubkey(w.PublicKey) 
    //versionByte + RIPEMD160(SHA256(pubKey))
    versionedPayload := append([]byte{versionByte}, pubkeyhash...)
    // sha256(sha256(versionByte + RIPEMD160(SHA256(pubKey))))
    checksum := checksum(versionedPayload)
    //versionedPayload + checksum
    fullPayload := append(versionedPayload, checksum...)
    //base56
    address := Base58Encode(fullPayload)
    return address
}

这就是为什么选择一个合适的公钥加密算法是如此重要:考虑到私钥是随机数,生成同一个数字的概率必须是尽可能地低。理想情况下,必须是低到“永远”不会重复。

这个算法有一个好处,就是可以通过address推出收货人的公钥hash值。

以这个为基础,我们就可以对交易的input和output进行锁定/解锁签名等操作。