亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

NEO 之從私鑰到地址

標簽:
區塊鏈

学习 NEO 钱包的 O3 项目 ,其中有用到 NeoSwift 库,记录一下。

私钥是怎么来的?

私钥是一个32字节的随机数,这个数的范围是介于 1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141 之间。

Account.swift 类:

public init?() {        var pkeyData = Data(count: 32)        let result = pkeyData.withUnsafeMutableBytes {            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
        }        
        if result != errSecSuccess {            fatalError()
        }        
        var error: NSError?        guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }        self.wif = wallet.wif()        self.publicKey = wallet.publicKey()        self.privateKey = pkeyData        self.address = wallet.address()        self.hashedSignature = wallet.hashedSignature()        //default to mainnet
        self.neoClient = NeoClient.sharedMain
    }

它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:

/*!
     @function SecRandomCopyBytes
     @abstract Return count random bytes in *bytes, allocated by the caller.
        It is critical to check the return value for error
     @result Return 0 on success, any other value on failure.
*/@available(iOS 2.0, *)public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32

随机生成一个32字节的 Data 数据,即 privatekeyData

var pkeyData = Data(count: 32)        let result = pkeyData.withUnsafeMutableBytes {            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
        }

然后根据私钥(用 privatekeyDataHexString 作为参数)生成一个钱包,见 neo-utils

var error: NSError?guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
// Generate a wallet from a private keyfunc GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
    pb := hex2bytes(privateKey)    var priv btckey.PrivateKey
    err := priv.FromBytes(pb)    if err != nil {        return &Wallet{}, err
    }
    wallet := &Wallet{        PublicKey:       priv.PublicKey.ToBytes(),        PrivateKey:      priv.ToBytes(),        Address:         priv.ToNeoAddress(),        WIF:             priv.ToWIFC(),        HashedSignature: priv.ToNeoSignature(),
    }    return wallet, nil}

公钥是怎么来的?

公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。

neowallet.gobtckey.go

// Generate a wallet from a private keyfunc GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
    pb := hex2bytes(privateKey)    var priv btckey.PrivateKey
    err := priv.FromBytes(pb)    if err != nil {        return &Wallet{}, err
    }
    wallet := &Wallet{        PublicKey:       priv.PublicKey.ToBytes(),        PrivateKey:      priv.ToBytes(),        Address:         priv.ToNeoAddress(),        WIF:             priv.ToWIFC(),        HashedSignature: priv.ToNeoSignature(),
    }    return wallet, nil}
// derive derives a Bitcoin public key from a Bitcoin private key.func (priv *PrivateKey) derive() (pub *PublicKey) {    /* See Certicom's SEC1 3.2.1, pg.23 */

    /* Derive public key from Q = d*G */
    Q := secp256r1.ScalarBaseMult(priv.D)    /* Check that Q is on the curve */
    if !secp256r1.IsOnCurve(Q) {
        panic("Catastrophic math logic failure in public key derivation.")
    }

    priv.X = Q.X
    priv.Y = Q.Y

    return &priv.PublicKey}

地址脚本是怎么来的?

地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:

  • 前面是:0x21

  • 后面是:0xAC

btckey.go

/* Convert the public key to bytes */
    pub_bytes := pub.ToBytes()

    pub_bytes = append([]byte{0x21}, pub_bytes...)
    pub_bytes = append(pub_bytes, 0xAC)

地址ScriptHash是怎么来的?

地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160

btckey.go

/* SHA256 Hash */
    sha256_h := sha256.New()
    sha256_h.Reset()
    sha256_h.Write(pub_bytes)
    pub_hash_1 := sha256_h.Sum(nil)    /* RIPEMD-160 Hash */
    ripemd160_h := ripemd160.New()
    ripemd160_h.Reset()
    ripemd160_h.Write(pub_hash_1)
    pub_hash_2 := ripemd160_h.Sum(nil)

    program_hash := pub_hash_2

地址是怎么来的?

地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:

  • 加盐:前面加了一个字节 0x17

  • 加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节

  • 编码:Base58 编码

btckey.go 完整的由公钥生成地址的代码:

// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.func (pub *PublicKey) ToNeoAddress() (address string) {    /* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */

    /* Convert the public key to bytes */
    pub_bytes := pub.ToBytes()

    pub_bytes = append([]byte{0x21}, pub_bytes...)
    pub_bytes = append(pub_bytes, 0xAC)    /* SHA256 Hash */
    sha256_h := sha256.New()
    sha256_h.Reset()
    sha256_h.Write(pub_bytes)
    pub_hash_1 := sha256_h.Sum(nil)    /* RIPEMD-160 Hash */
    ripemd160_h := ripemd160.New()
    ripemd160_h.Reset()
    ripemd160_h.Write(pub_hash_1)
    pub_hash_2 := ripemd160_h.Sum(nil)

    program_hash := pub_hash_2    //wallet version
    //program_hash = append([]byte{0x17}, program_hash...)

    // doublesha := sha256Bytes(sha256Bytes(program_hash))

    // checksum := doublesha[0:4]

    // result := append(program_hash, checksum...)
    /* Convert hash bytes to base58 check encoded sequence */
    address = b58checkencodeNEO(0x17, program_hash)    return address
}
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.func b58checkencodeNEO(ver uint8, b []byte) (s string) {    /* Prepend version */
    bcpy := append([]byte{ver}, b...)    /* Create a new SHA256 context */
    sha256_h := sha256.New()    /* SHA256 Hash #1 */
    sha256_h.Reset()
    sha256_h.Write(bcpy)
    hash1 := sha256_h.Sum(nil)    /* SHA256 Hash #2 */
    sha256_h.Reset()
    sha256_h.Write(hash1)
    hash2 := sha256_h.Sum(nil)    /* Append first four bytes of hash */
    bcpy = append(bcpy, hash2[0:4]...)    /* Encode base58 string */
    s = b58encode(bcpy)    // /* For number of leading 0's in bytes, prepend 1 */
    // for _, v := range bcpy {
    //  if v != 0 {
    //      break
    //  }
    //  s = "1" + s
    // }

    return s
}

WIF 是怎么来的?

WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:

  • 前面加版本字节:0x80

  • 后面加压缩标志字节:0x01

  • 对这34个字节进行哈希:取哈希值的前4个字节加在最后面

  • 编码:Base58 编码

【注】其中“对这34个字节进行哈希”,我找的在线工具做的Hash计算,结果跟 NEO学习笔记,从WIF到地址 文章中的结果不一致,不知道怎么计算的,有了解的请留言,谢谢!

图解:

NEO 之从私钥到地址

总结

欢迎留言讨论,有错误请指出,谢谢!

参考链接


作者:宅小馒
原文链接
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消