zoukankan      html  css  js  c++  java
  • 创建钱包地址

    参考:https://github.com/liuchengxu/blockchain-tutorial

    引言

    在比特币中,没有用户账户,不需要也不会在任何地方存储个人数据(比如姓名,护照号码或者 SSN),但总要有某种途径识别出你是交易输出的所有者(也就是说,你拥有在这些输出上锁定的币),即比特币地址(address)需要完成的使命。

    比特币地址

    比特币地址是完全公开的,如果想要给某个人发送币,只需要知道他的地址就可以了。但是,地址(尽管地址也是独一无二的)并不是用来证明你是一个“钱包”所有者的信物。实际上,所谓的地址,只不过是将公钥表示成人类可读的形式而已,因为原生的公钥人类很难阅读。在比特币中,你的身份(identity)就是一对(或者多对)保存在你的电脑(或者你能够获取到的地方)上的公钥(public key)和私钥(private key)。比特币基于一些加密算法的组合来创建这些密钥,并且保证了在这个世界上没有其他人能够取走你的币,除非拿到你的密钥。
    涉及到以下算法:

    公钥加密

    公钥加密(public-key cryptography)算法使用的是成对的密钥:公钥和私钥,其中公钥并不是敏感信息,可以告诉其他人,但是私钥绝对不能告诉他人,只有所有者(owner)才能拥有,其作用为识别、鉴定和证明所有者的身份。在加密货币的世界中,你的私钥代表的就是你,私钥就是一切。
    私钥和公钥是随机的字节序列,因此它们无法在屏幕上打印,人类也无法通过肉眼去读取。这就是为什么比特币使用了一个转换算法,将公钥转化为一个人类可读的字符串(也就是我们看到的地址)。
    注:公钥是通过私钥产生

    数字签名

    在数学和密码学中,有一个数字签名(digital signature)的概念,算法可以保证:

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

    通过在数据上应用签名算法(即对数据进行签名),就会得到一个签名,之后这个签名会被验证。
    生成数字签名时需要一个私钥,验证签名时需要一个公钥

    签名有点类似于印章,比方说我做了一幅画,完了用印章一盖,就说明了这幅画是我的作品。给数据生成签名,就是给数据盖了章。

    为数据进行签名时,需要以下两样东西:

    1、要签名的数据
    2、私钥

    应用签名算法可以生成一个签名,并且这个签名会被存储在交易输入中。
    为对一个签名进行验证,需要以下三种东西:

    1、 被签名的数据
    2、签名
    3、公钥

    其验证过程可以简单地描述为:检查签名是由被签名数据加上私钥得来,并且公钥恰好是由该私钥生成。

    数据签名并不是加密,你无法从一个签名重新构造出数据。这有点像哈希:你在数据上运行一个哈希算法,然后得到一个该数据的唯一表示。签名与哈希的区别在于密钥对:有了密钥对,才有签名验证。但是密钥对也可以被用于加密数据:私钥用于加密,公钥用于解密数据。不过比特币并不使用加密算法。

    在比特币中,每一笔交易输入都会由创建交易的人签名。在被放入到一个块之前,必须要对每一笔交易进行验证。除了一些其他步骤,验证意味着:

    1. 检查交易输入有权使用来自之前交易的输出
    2. 检查交易签名是正确的

    对数据进行签名和对签名进行验证的过程大致如下:

    一个交易完整的生命周期:
    1. 起初,创世块里面包含了一个 coinbase 交易。在 coinbase 交易中,没有输入,所以也就不需要签名。coinbase 交易的输出包含了一个哈希过的公钥(使用的是
      RIPEMD16(SHA256(PubKey)) 算法)
    2. 当一个人发送币时,就会创建一笔交易。这笔交易的输入会引用之前交易的输出。每个输入会存储一个公钥(没有被哈希)和整个交易的一个签名。
    3. 比特币网络中接收到交易的其他节点会对该交易进行验证。除了一些其他事情,他们还会检查:在一个输入中,公钥哈希与所引用的输出哈希相匹配(这保证了发送方只能花费属于自己的币);签名是正确的(这保证了交易是由币的实际拥有者所创建)。
    4. 当一个矿工准备挖一个新块时,他会将交易放到块中,然后开始挖矿。
    5. 当新块被挖出来以后,网络中的所有其他节点会接收到一条消息,告诉其他人这个块已经被挖出并被加入到区块链。
    6. 当一个块被加入到区块链以后,交易就算完成,它的输出就可以在新的交易中被引用。

    椭圆曲线加密

    公钥和私钥是随机的字节序列。私钥能够用于证明持币人的身份,需要有一个条件:随机算法必须生成真正随机的字节。因为没有人会想要生成一个私钥,而这个私钥意外地也被别人所有。
    比特币使用椭圆曲线来产生私钥,该曲线可以生成非常大的随机数,
    比特币使用的是 ECDSA(Elliptic Curve Digital Signature Algorithm)算法来对交易进行签名,我们也会使用该算法。

    Base58

    公钥为16进制的数据,为转换成人类可读的形式比特币采用了Base58算法,该算法与著名的Base64很类似,区别在于它使用了更短的字母表:为了避免一些利用字母相似性的攻击,从字母表中移除了一些字母。也就是,没有这些符号:0(零),O(大写的 o),I(大写的i),l(小写的 L),因为这几个字母看着很像。另外,也没有 + 和 / 符号。
    从一个公钥获得一个地址的过程:

    对应上图代码实现如下:

    type Wallet struct {
    	// 1、私钥
    	privateKey ecdsa.PrivateKey
    	// 2、公钥
    	publicKey []byte
    }
    
    const version = byte(0x00)
    const addressChecksumLen = 4
    type Wallet struct {
    	// 1、私钥
    	privateKey ecdsa.PrivateKey
    	// 2、公钥
    	publicKey []byte
    }
    
    func IsValidForAddress(address []byte) bool {
    	version_public_checksumBytes := Base58Decode(address)
    	fmt.Println(version_public_checksumBytes)
    
    	checkSumBytes := version_public_checksumBytes[len(version_public_checksumBytes)-addressChecksumLen:]
    
    	version_ripemd160 := version_public_checksumBytes[:len(version_public_checksumBytes)-addressChecksumLen]
    	fmt.Println(len(checkSumBytes))
    	fmt.Println(len(version_ripemd160))
    
    	checkBytes := CheckSum(version_ripemd160)
    	if bytes.Compare(checkSumBytes, checkBytes) == 0 {
    		return true
    	}
    	return false
    }
    
    // 获取地址
    func (w *Wallet) GetAddress() []byte {
    	// 1、hash160
    	ripemd160Hash := w.Ripemd160Hash(w.publicKey)
    	version_ripemd160Hash := append([]byte{version}, ripemd160Hash...)
    	checkSumBytes := CheckSum(version_ripemd160Hash)
    	bytes := append(version_ripemd160Hash, checkSumBytes...)
    	return Base58Encode(bytes)
    }
    
    func CheckSum(payload []byte) []byte {
    	hash1 := sha256.Sum256(payload)
    	hash2 := sha256.Sum256(hash1[:])
    	return hash2[:addressChecksumLen]
    }
    func (w *Wallet) Ripemd160Hash(publicKey []byte) []byte {
    	// 256
    	hash256 := sha256.New()
    	hash256.Write(publicKey)
    	hash := hash256.Sum(nil)
    
    	//160
    	ripemd160 := ripemd160.New()
    	ripemd160.Write(hash)
    	return ripemd160.Sum(nil)
    
    }
    
    // 创建钱包
    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)
    	}
    	publicKey := append(private.X.Bytes(), private.PublicKey.Y.Bytes()...)
    	return *private, publicKey
    }
    
    

    因此,从上图可看出,公钥解码后包含三个部分:

    Version Public key hash Checksum
    00 62E907B15CBF27D5425399EBF6F0FB50EBB88F18 C29B7D93
    • base58加密
    • base58 解密

    最后的效果图:

    如有需要可详看:https://github.com/NGLHarry/Blockchainer/tree/main/wallet_address

    注:
    在进行加密过程中需要使用到ripemd160,其go的安装命令为:go get "golang.org/x/crypto/ripemd160"
    但是,大概率会被墙,有位好心大哥已经上传到github上,只需将其下载到本地,并放在Go项目中的src目录下即可
    ripemd160地址

  • 相关阅读:
    ansible笔记(11):初识ansible playbook(二)
    Linux下查看占用CPU与内存最高的进程
    ansible笔记(10):初识ansible playbook
    AbpZero Http 模式下 Chrome浏览器因Cookie 不能登录
    Tomcat 8443&8080 并存
    接入腾讯cos文件存储
    安卓包打渠道标签
    java Android与PHP encode的区别
    thinkphp常用
    phalcon task任务
  • 原文地址:https://www.cnblogs.com/whiteBear/p/15677385.html
Copyright © 2011-2022 走看看