前言

上一章我们介绍了 ethereum运行开启console 的过程,那么在这一章我们将会介绍如何在以太坊中创建一个新的账号。以下的理解可能存在错误,如果各位大虾发现错误,还望指正。

指令分析

指令: personal.newAccount(password)

介绍:上面的指令的主要的作用是生成一个account

分析: password 是一个字符串,即你为账户设置的密码(ps:这个密码跟身份验证有关)

例子:

  • 首先我们先需要开启console,开启之后会在控制台出现下图的信息:

  • 然后我们输入 personal.newAccount("123") 指令,之后我们就可以看到下面信息:

"0x58f722f1aebff6ad0334d49786bfe448bfafef33" 就是我们的钱包地址,那么下面就让我们来看看账号的生成过程吧。

相关知识

从文献中我们可以知道,secp256k1这条曲线有几个参数 T =(p,a,b,G,n,h) ,这几个参数在eth的源代码中已经指定。它们分别是:

func init() {
// See SEC 2 section 2.7.1
// curve parameters taken from:
// http://www.secg.org/collateral/sec2_final.pdf
theCurve.P = math.MustParseBig256("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F")
theCurve.N = math.MustParseBig256("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")
theCurve.B = math.MustParseBig256("0x0000000000000000000000000000000000000000000000000000000000000007")
theCurve.Gx = math.MustParseBig256("0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798")
theCurve.Gy = math.MustParseBig256("0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8")
theCurve.BitSize = 256
}

在生成账号时我们需要用到上面的几个参数。

再eth中 scrypt 的初始化参数为:

const (
keyHeaderKDF = "scrypt" // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB
// memory and taking approximately 1s CPU time on a modern processor.
StandardScryptN = 1 << 18 // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB
// memory and taking approximately 1s CPU time on a modern processor.
StandardScryptP = 1 // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB
// memory and taking approximately 100ms CPU time on a modern processor.
LightScryptN = 1 << 12 // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB
// memory and taking approximately 100ms CPU time on a modern processor.
LightScryptP = 6 scryptR = 8
scryptDKLen = 32
)

账号生成过程

账号的生成主要分为以下几个过程:

  • 1. 生成 privateKey
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
params := c.Params()
b := make([]byte, params.BitSize/8+8)
_, err = io.ReadFull(rand, b)//生成一个40byte的随机值
if err != nil {
return
} k = new(big.Int).SetBytes(b)
n := new(big.Int).Sub(params.N, one)//将椭圆曲线中的参数N减1,为了防止秘钥不等于0且秘钥不能超过参数N
k.Mod(k, n)//用生成的40byte的随机值去mod N得到一个32byte的值
k.Add(k, one)//再对得到了32位的值进行加1处理,这里我认为是为了防止秘钥等于0
return
}
  • 2.通过秘钥生成 publicKey
func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
k, err := randFieldElement(c, rand)//生成秘钥
if err != nil {
return nil, err
} priv := new(PrivateKey)
priv.PublicKey.Curve = c
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())//生成公钥
return priv, nil
}

对于公钥的生成你只需要知道它是通过椭圆曲线生成出来的就可以了。

  • 3.通过 publicKey 生成 address

首先我们需要拼接公钥对,生成一个未压缩的公钥:

// Marshal converts a point into the uncompressed form specified in section 4.3.6 of ANSI X9.62.
func Marshal(curve Curve, x, y *big.Int) []byte {
byteLen := (curve.Params().BitSize + 7) >> 3 ret := make([]byte, 1+2*byteLen)//创建一个65size的byte数组
ret[0] = 4 // uncompressed point,将ret[0]赋值4,表明它是未压缩的公钥 //下面的代码是为了将公钥对拼接到一起即x+""+y
xBytes := x.Bytes()
copy(ret[1+byteLen-len(xBytes):], xBytes)
yBytes := y.Bytes()
copy(ret[1+2*byteLen-len(yBytes):], yBytes)
return ret
}

然后我们使用刚刚生成的未压缩的公钥生成地址:

func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
pubBytes := FromECDSAPub(&p)
return common.BytesToAddress(Keccak256(pubBytes[1:])[12:])//使用压缩的公钥生成地址,首先公钥进行hash,生成一个32byte的值,在这里我们使用的hash算法是Keccak256,感兴趣的同学请自行百度。然后我们取后20位做我们的地址
}
  • 4.通过输入的 password 生成 mac ,为了做身份验证
// EncryptKey encrypts a key using the specified scrypt parameters into a json
// blob that can be decrypted later on.
func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
authArray := []byte(auth)
salt := randentropy.GetEntropyCSPRNG(32)//生成一个随机的32byte的salt
derivedKey, err := scrypt.Key(authArray, salt, scryptN, scryptR, scryptP, scryptDKLen)//使用scrypt算法将输入的password加密,生成一个32位的derivedKey
if err != nil {
return nil, err
}
encryptKey := derivedKey[:16]//取derivedKey的前16byte
keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 生成一个随机的16byte的iv
cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)//对进行provateKey进行aes加密,生成一个32byte的cipherText
//fmt.Printf("cipherText:%s\n", hex.EncodeToString(cipherText))
if err != nil {
return nil, err
}
mac := crypto.Keccak256(derivedKey[16:32], cipherText)//将derivedKey的后16byte与cipherText进行Keccak256,生成32byte的mac,我猜这里是对这两个byte进行了拼接处理 //后面的代码是构造要写入硬盘的json
scryptParamsJSON := make(map[string]interface{}, 5)
scryptParamsJSON["n"] = scryptN
scryptParamsJSON["r"] = scryptR
scryptParamsJSON["p"] = scryptP
scryptParamsJSON["dklen"] = scryptDKLen
scryptParamsJSON["salt"] = hex.EncodeToString(salt) cipherParamsJSON := cipherparamsJSON{
IV: hex.EncodeToString(iv),
} cryptoStruct := cryptoJSON{
Cipher: "aes-128-ctr",
CipherText: hex.EncodeToString(cipherText),
CipherParams: cipherParamsJSON,
KDF: keyHeaderKDF,
KDFParams: scryptParamsJSON,
MAC: hex.EncodeToString(mac),
}
encryptedKeyJSONV3 := encryptedKeyJSONV3{
hex.EncodeToString(key.Address[:]),
cryptoStruct,
key.Id.String(),
version,
}
return json.Marshal(encryptedKeyJSONV3)
}
  • 5.存储生成的信息
func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
if err != nil {
return err
}
return writeKeyFile(filename, keyjson)//写入文件,文件的名包含了address,并且存储位置是我们启动eth时,--datadir所指定的位置,--datadir下面有个文件夹keystore,里面存储了你的账号信息
}

那么让我们来看一看我们生成的文件:

{
"address": "30384e0690af2e71710cc0cce26227a4d9418177",
"crypto": {
"cipher": "aes-128-ctr",//使用的加密算法
"ciphertext": "77f85a3664573f1bb719acda4990af9814ddc3e2e6acaa2a9b88daa8b8979028",//加密后的私钥
"cipherparams": {
"iv": "1c72678046f622b9bf63cfa4a6615304"//之前代码中生成的iv,用于加密私钥
},
"kdf": "scrypt",//使用的hash算法
"kdfparams": {//hash算法的参数信息
"dklen": 32,
"n": 262144,
"p": 1,
"r": 8,
"salt": "749a66fea9065b07c71519e60da65cad58a1a0b9c38f7fbcb36d5fe6f32ee493"//之前代码中生成的salt,用于生成dk
},
"mac": "53fc76f41231ed418a91ddf3758f2ee78b1261f08896aa18a1a0cef6bfbccfd6"//之前代码中生成的mac,用于身份验证
},
"id": "9c760694-7026-4d2e-9172-56a44e35d10e",//随机生成的uuid
"version": 3
}

至此我们的账号就生成完成了,生成账号的入口函数在 go-ethereum/accounts/keystore/keystore.go ,大家可以运行 go-ethereum/accounts/keystore/keystore_test.go 中的代码来debug。

小结

总结来说eth生成账号的过程为:

  1. 生成一个随机的32 byte的 privateKey
  2. 通过 privateKey 生成一个公钥对,将公钥对进行拼接就得到了一个64 byte未压缩形式的 publicKey
  3. 对这个 publicKey 进行 Keccak256 ,然后取后20 byte,就生成了我们的20 byte的 address
  4. 将我们输入的 password 和随机生成的32 byte的 salt 进行 scrypt 加密,会得到一个32 byte的 derivedKey
  5. 用 derivedKey 的前16 byte与随机生成的16 byte的iv对 privateKey 进行aes加密,得到32 byte的 cipherText
  6. 将 derivedKey 的后16 byte与 cipherText 进行 Keccak256 ,得到一个32 byte的mac
  7. 将数据写入文件

[ethereum源码分析](5) 创建新账号的更多相关文章

  1. 干货分享之spring框架源码分析02-(对象创建or生命周期)

    记录并分享一下本人学习spring源码的过程,有什么问题或者补充会持续更新.欢迎大家指正! 环境: spring5.X + idea 之前分析了Spring读取xml文件的所有信息封装成beanDef ...

  2. [ethereum源码分析](3) ethereum初始化指令

    前言 在上一章介绍了关于区块链的一些基础知识,这一章会分析指令 geth --datadir dev/data/02 init private-geth/genesis.json 的源码,若你的eth ...

  3. live555源码分析----RSTPServer创建过程分析

    最近五一回家,终于有机会能安静的看一下流媒体这方面相关的知识,准备分析live555的源码,接下来会把我读源码的过程记录成博客,以供其他的同路人参考. 因为再读源码的过程中,并不是一路顺着读下来,往往 ...

  4. Akka源码分析-Actor创建(续)

    在上一遍博客中,我们已经分析了actor创建的大致过程,但只是涉及到了Dipatcher/Mailbox/ActorCell/InternalActorRef等对象的创建,并没有介绍我们自定义的继承A ...

  5. 1.live555源码分析----RSTPServer创建过程分析

    最近五一回家,终于有机会能安静的看一下流媒体这方面相关的知识,准备分析live555的源码,接下来会把我读源码的过程记录成博客,以供其他的同路人参考. 因为再读源码的过程中,并不是一路顺着读下来,往往 ...

  6. Akka源码分析-Actor创建

    上一篇博客我们介绍了ActorSystem的创建过程,下面我们就研究一下actor的创建过程. val system = ActorSystem("firstActorSystem" ...

  7. 从源码分析 MGR 的新主选举算法

    MGR 的新主选举算法,在节点版本一致的情况下,其实也挺简单的. 首先比较权重,权重越高,选为新主的优先级越高. 如果权重一致,则会进一步比较节点的 server_uuid.server_uuid 越 ...

  8. Ethereum 源码分析之 accounts

    一.Account // Account represents an Ethereum account located at a specific location defined // by the ...

  9. [ethereum源码分析](1) dubug环境搭建

    前言 因为最近云小哥哥换了一份工作,新公司比较忙,所以一直没有更新新的博客.云小哥哥新的公司是做区块链的,最近在学习区块链相关的东西(也算是乘坐上了区块链这艘大船).本博客是记录我搭建ethereum ...

随机推荐

  1. 20191128 Spring Boot官方文档学习(9.4-9.8)

    9.4.Spring MVC Spring Boot有许多启动器包含Spring MVC.请注意,一些启动器包括对Spring MVC的依赖,而不是直接包含它. 9.4.1.编写JSON REST服务 ...

  2. 红帽学习笔记[RHCSA] 第九课[文件归档、硬盘、分区以及自动挂载、Swap、链接]

    文件归档 tar是什么 通过tar命令可以将大型文件汇集成一个文件(归档),注意没有压缩功能. 压缩方式 gzip 通过gzip过滤文档,使用最广泛 bzip2 通常比gzip压缩小,但是不如gzip ...

  3. [转帖]Ubuntu 清理 历史命令

    Ubuntu彻底清除history命令历史记录 https://blog.csdn.net/u013554213/article/details/84954062 centos 可以使用这个命令 清理 ...

  4. uva-796.critical links(连通图的桥)

    本题大意:求出一个无向图的桥的个数并且按照顺序输出所有桥. 本题思路:注意判重就行了,就是一个桥的裸题. 判重思路目前知道的有两种,第一种是哈希判重,第二种和邻接矩阵的优化一样,就是只存图的上半角或者 ...

  5. SpringBoot整合mybatis碰到的问题

    整合mybatis 1.  导包:在原有的web项目的基础上加上 <!--JDBC连接-->     <dependency>         <groupId>m ...

  6. [FJOI2007]轮状病毒 题解(dp(找规律)+高精度)

    [FJOI2007]轮状病毒 题解(dp(找规律)+高精度) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1335733 没什么好说的,直接把规律找出来,有 ...

  7. PHP 如何实现页面静态化

    页面静态化分为两种 一种伪静态,即url重写,一种纯静态化. 一.静态化的优点: 1有利于搜索引擎收录网站页面的信息:搜索引擎更喜欢静态的,更变于抓取,搜索引擎SEO排名会更容易提高. 2静态网页化网 ...

  8. 关于position的操作

    1.position:relative 相较于正常位置的定位 <!DOCTYPE html> <html lang="en"> <head> & ...

  9. 史上最全Java学习视频下载地址分享

    http://blog.csdn.net/xlgen157387/article/details/39735141

  10. vlan的三种模式access、trunk、hybrid

    untag就是普通的ethernet报文,普通PC机的网卡是可以识别这样的报文进行通讯:tag报文结构的变化是在源mac地址和目的mac地址之后,加上了4bytes的vlan信息,也就是vlan ta ...