一、外部賬戶
外部賬戶創(chuàng)建流程:
- 創(chuàng)建隨機私鑰(64位16進制字符/32字節(jié)):
ecdsa.GenerateKey(crypto.S256(), rand)
- 從私鑰推導(dǎo)出公鑰(128位16進制字符/64字節(jié)):
privateKeyECDSA.PublicKey
- 從公鑰推導(dǎo)出地址(40位16進制字符/20字節(jié)):
Keccak256(pubBytes[1:])[12:]
當(dāng)使用geth account new
命令新建賬戶晓锻,最終調(diào)用accountCreate(accountcmd.go)=>keystore.StoreKey=>storeNewKey(key.go)
func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
id, err := uuid.NewRandom()
if err != nil {
panic(fmt.Sprintf("Could not create random uuid: %v", err))
}
key := &Key{
Id: id,
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), // 生成地址 Keccak256(pubBytes[1:])[12:]
PrivateKey: privateKeyECDSA,
}
return key
}
func newKey(rand io.Reader) (*Key, error) {
privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand) // 隨機生成私鑰
if err != nil {
return nil, err
}
return newKeyFromECDSA(privateKeyECDSA), nil
}
func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Account, error) {
key, err := newKey(rand)
if err != nil {
return nil, accounts.Account{}, err
}
a := accounts.Account{
Address: key.Address,
URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
}
if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
zeroKey(key.PrivateKey)
return nil, a, err
}
return key, a, err
}
storeNewKey完成私鑰雌芽、公鑰、地址的生產(chǎn)继榆,最后保存成keystore文件到指定路徑。
最后保存的keystore文件為json格式,如下:
{
"address": "26ce833a705af6846da8c1a43d1e418b93458137", //賬戶地址
"crypto": {
//使用的加密算法,這里使用AES-CTR模式加密私鑰,分組模式是128比特
"cipher": "aes-128-ctr",
//這是對原始私鑰加密后的私鑰的密文
"ciphertext": "e2edc5df564536dcf7fb8bcfde99404215d8dd8327684e9d27327a267181a791",
"cipherparams": {
//這是向量
"iv": "9847020ef0bb269b0c463d2ed4bb2ac4"
},
"kdf": "scrypt",
"kdfparams": {
"dklen": 32, //解密秘鑰的長度
//進行加密解密運算的次數(shù),在越大的情況下,可以增加暴力破解的成本,也會使簽名速度變慢
"n": 262144,
//設(shè)置為1為只能串行運算.0為并行運算.串行運算可以增加安全性.也會影響簽名速度
"p": 1,
//加密的分組長度
"r": 8,
//可以理解為隨機生成的向量
"salt": "56fc7ac270cd1a357a2bc1959119f10df4b69fabb4d0c198d6527f3c0fe2df6b"
},
//這是解密私鑰密文的秘鑰的hash值,防止用戶輸入錯誤的密碼而計算出錯誤的私鑰.用與比對私鑰是否正確.
"mac": "7fde1727799710cf122d441c57c50cbc8182f666cca5a7717a8cb3bb8d21639d"
},
"id": "1d6b8676-de36-441d-a736-2a8ee94019ea",
"version": 3
}
以下為用密碼可以推出私鑰的流程
二猾警、內(nèi)部賬戶
對交易發(fā)起人的地址和nonce進行RLP編碼孔祸,再算出Keccak哈希值,取后20個字節(jié)作為該合約的地址,即:Keccak-256(RLP(sender, nonce))[12:]
函數(shù)位于:crypto/crypto.go
// CreateAddress creates an ethereum address given the bytes and the nonce
func CreateAddress(b common.Address, nonce uint64) common.Address {
data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
return common.BytesToAddress(Keccak256(data)[12:])
}
三发皿、賬戶結(jié)構(gòu)
賬戶在區(qū)塊鏈上的存儲結(jié)構(gòu)崔慧,內(nèi)外賬戶的結(jié)構(gòu)都是一樣
type Account struct {
Nonce uint64 // 賬戶發(fā)起交易的次數(shù)
Balance *big.Int // 賬戶的余額
Root common.Hash // 合約賬戶存儲空間的一棵MPT樹的根節(jié)點的Hash
CodeHash []byte // 合約代碼的Hash值
}