引言
在以太坊私有链搭建及基本操作中,使用了下面的命令来新建账号。接下来我就来看下以太坊账号的原理
1
|
./geth --datadir data account new
|
另外,我们还关心一个命令 list
1
|
./geth --datadir data account list
|
- 账号文件目录
- data/keystore
- 打开其中一个账号文件,可以看到(部分字符被…代替)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
{
"address": "...c2504e15b05572df94f0df385cf3c4b130879",
"crypto": {
"cipher": "aes-128-ctr",
"ciphertext": "d4...37",
"cipherparams": {
"iv": "94...a2"
},
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"n": 262144,
"p": 1,
"r": 8,
"salt": "cf...0f"
},
"mac": "26...7c"
},
"id": "75...11",
"version": 3
}
|
- 地址就是其中的address字段,所以address是直接存储的
- 密钥逻辑
- 关于 非对称加密
- 几个概念:公钥pubKey,私钥privKey,密码pass,签名sign,任意内容content
- 每笔交易发起会去校验由私钥+密码是否能够得到账号地址:privKey+pass -> address,由此需要密码和私钥才能发起交易
- 交易被确认前校验由公钥+内容能否解析出私钥+内容加密得到的签名:pubKey + content -> sign=[privKey+content]
- 密码+keyStore对象获得私钥:pass+keyObject->privKey,所以务必确保自己保存好keystore和密码,丢失其中一个都不能发起交易。这点和比特币可能不太一样
- 以太坊使用椭圆曲线算法,具体逻辑暂时不做理解
从main函数开始
当运行如下命令时
1
|
./geth --datadir data account list
|
首先会进入cmd/geth/main.go的主函数
1
2
3
4
5
6
|
func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
|
随后,解析参数放入上下文context,如果发现命令,则进入命令。命令行框架由cli提供
1
2
3
4
5
6
7
8
|
args := context.Args()
if args.Present() {
name := args.First()
c := a.Command(name)
if c != nil {
return c.Run(context)
}
}
|
具体地,account list 命令进入cmd/geth/accountcmd.go的accountList
1
2
3
4
5
6
7
8
9
10
11
|
func accountList(ctx *cli.Context) error {
stack, _ := makeConfigNode(ctx)
var index int
for _, wallet := range stack.AccountManager().Wallets() {
for _, account := range wallet.Accounts() {
fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL)
index++
}
}
return nil
}
|
accountList会去keystore目录找出所有账号文件,解析json,拿到所有address字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//accounts/keystore/account_cache.go
// scanAccounts checks if any changes have occurred on the filesystem, and
// updates the account cache accordingly
func (ac *accountCache) scanAccounts() error {
// Scan the entire folder metadata for file changes
...
// Create a helper method to scan the contents of the key files
var (
buf = new(bufio.Reader)
key struct {
Address string `json:"address"`
}
)
readAccount := func(path string) *accounts.Account {
fd, err := os.Open(path)
if err != nil {
log.Trace("Failed to open keystore file", "path", path, "err", err)
return nil
}
defer fd.Close()
buf.Reset(fd)
// Parse the address.
key.Address = ""
err = json.NewDecoder(buf).Decode(&key)
addr := common.HexToAddress(key.Address)
...
|
对于account new原理类似,使用加密算法创建账户文件
对于unlock,正如引言所说,验证privKey+pass->address
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// cmd/geth/accountcmd.go
// tries unlocking the specified account a few times.
func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) {
account, err := utils.MakeAddress(ks, address)
if err != nil {
utils.Fatalf("Could not list accounts: %v", err)
}
for trials := 0; trials < 3; trials++ {
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
password := getPassPhrase(prompt, false, i, passwords)
err = ks.Unlock(account, password)
...
|
对于交易,需要将pubKey和签名和交易数据放在一起,让其他peer验证,如V R S字段所示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//core/types/transaction.go
type txdata struct {
AccountNonce uint64 `json:"nonce" gencodec:"required"`
Price *big.Int `json:"gasPrice" gencodec:"required"`
GasLimit uint64 `json:"gas" gencodec:"required"`
Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation
Amount *big.Int `json:"value" gencodec:"required"`
Payload []byte `json:"input" gencodec:"required"`
// Signature values
V *big.Int `json:"v" gencodec:"required"`
R *big.Int `json:"r" gencodec:"required"`
S *big.Int `json:"s" gencodec:"required"`
// This is only used when marshaling to JSON.
Hash *common.Hash `json:"hash" rlp:"-"`
}
|
account list 命令执行路径
1
2
3
4
5
6
7
8
9
10
|
graph TD
A(cmd/geth/main.go main) -->B(urfave/cli.v1 Run)
B --> C(cmd/geth/accountcmd.go accountList)
C -->|Node配置初始化| D(cmd/geth/config.go makeConfigNode)
C -->|查询账户|E(accounts/manage.go Wallets)
D --> F(node/node.go New)
F --> G(node/config.go makeAccountManager)
G --> |配置keystore目录|H(accounts/keystore/keystore.go NewKeyStore)
H --> |初始化账户cache|J(accounts/keystore/account_cache.go newAccountCache)
H --> |扫描keystore目录加载账户|K(accounts/keystore/account_cache.go accounts )
|

持续学习中,理解可能有误,敬请谅解
版权所有,转载请注明来源