編者按:本文系 imToken 首席架構師 Kai 講師,在由掘金技術社區主辦,以太坊社區基金會、以太坊愛好者與 ConsenSys 協辦的《開發者的以太坊入門指南 | Jeth 第二期 - 杭州場》 活動上的分享整理。Jeth 圍繞以太坊技術開發主題的系列線下活動。每期 Jeth 會邀請以太坊開發領域的優秀技術團隊和工程師在線下分享技術乾貨。旨在爲開發者提供線下技術交流互動機會,幫助開發者成長。git
我是 imToken 的首席架構師 Kai。開篇我先給你們拋出此次分享的核心要點: 在以太坊上,你的私鑰就是你的帳戶。程序員
一開始你什麼都沒有,你須要生成一個隨機數,隨機數我以爲程序員都很熟悉,某個事情隨機發生須要找一個結果,要產生隨機數。github
隨機數會生成私鑰,這時你有兩個選擇:算法
直接隨機生成 32 bytes 的二進制內容,把這個做爲本身的私鑰。假如你打開任何一個以太坊的錢包,你併入到私鑰的界面,你隨便輸出一個東西,只要位數符合要求,它均可以做爲錢包導進去。但這裏面有一個安全性的問題,你們想到我這樣能夠看到別人的錢包,可是本身能夠回去算下,32個字節能夠生成多少個私鑰。安全
這個作法在錢包裏很常見。錢包生成一個隨機數,而後再拿這個隨機數查字典,獲得12個單詞左右的助記詞,再經過路徑拿到你的私鑰。網絡
GitHub 連接:https://github.com/bitcoinjs/bip39數據結構
助記詞的生成過程也頗有趣,你把隨機數拆分11個 bits,就有了11個二進制的字符串,社區裏面有一個叫作 bip39 的二進制字符串的對應規範,裏面有一個字典,你根據拿到的字符串查字典,查到11個字符串對應的11個單詞。最後對前面這11個詞作一個 Checksum ,你的錢包獲得12個助記詞,最終能夠推出你的私鑰,進而推出你的錢包帳戶。架構
這裏有兩個概念,一個叫作分層,另外一個叫作肯定性。併發
什麼是分層,一開始隨機數 seed 推導出了一把 Master key,這把 Master key 在下面會繼續推導 Child keys,再往下推能獲得私鑰。這就是前面所提到的肯定性推導。我知道一份助記詞,這個助記詞不管如何,按照固定的路徑能夠推出一個能夠預期的私鑰,叫作肯定性;分層你有不少把私鑰,不少把私鑰裏面從一個助記詞裏面一層一層倒出來,因此咱們在這裏叫作分層決定性錢包。運維
助記詞有了,咱們要推導私鑰,私鑰的推導路徑就是由這樣一串字符格式:m/purpose/coin/account/change/address_index, 一層一層推導出來的,咱們叫它 BIP44。不一樣的鏈有不一樣的推導路徑,好比說,以太坊是60。你有一個助記詞以後,你能夠根據肯定性的路徑推導出不一樣的私鑰,不一樣的私鑰就是在不一樣的這些區塊鏈上面的一個身份的一個錢包。
隨機數變成助記詞,助記詞變成私鑰,私鑰如 STR 何保存下來,畢竟你的錢包不可能使用一次,好比說你有一個朋友談到衆籌,1.8元買到以太坊,他轉給你2個,這2個以太坊你要存下來,你存下來以後,你的私鑰須要落地存儲。但存儲不是簡單地存儲便可。網絡空間不安全,天天都有人被盜 QQ 號,區塊鏈世界裏面當你的帳戶資產被盜是不可逆的,這時你須要作安全加密的的存儲。
最終你的私鑰會變成 V3 KeyStore 這樣的長字符串。
私鑰有了,也落地存儲了,而你的私鑰要轉換到帳戶地址,這裏有另外的一個推導過程。
這裏用到了前面提的不對稱加密算法,先作一個橢圓曲線給你一把密鑰,還有一把公鑰,就是他把私鑰和公鑰,把公鑰投入到 hash ,再去拿第一位的一部分做爲一個地址,你的錢包就有一個地址。
這是一個比較複雜的數學過程,做爲咱們作工程的人來說,大 K 的最終的結果是一把私鑰,小 k 是一把公鑰;公鑰跟私鑰中間的關係是固定的,只能單向推導——從私鑰推到公鑰;這個橢圓曲線是比特幣最先實現時所選的橢圓曲線,是相對而言效率最高的橢圓曲線。
剛剛這些事情作完以後,你就能夠去轉帳,可是轉帳以前,你的帳戶須要有一些資產。
在以太坊中轉帳的數據結構如上圖所示,包括一個帳戶交易計數 Nonce,你要轉給誰,你要轉多少錢,以及在最下面有一個簽名。你拿你的私鑰去對這個交易作一個簽名。這裏面有不少種參數,咱們一個一個來看一下。
首先是Gas,Gas 是以太坊代幣的機消耗機制,你發起一個以太坊交易的時候,你必須消耗必定的以太幣並算到 Gas 中,Gas 最終會流到礦工手裏。Gas 的機制是先扣再退——發起交易時,你最初付出的 Gas 要寫得大一點,跑完一條交易之後,最終的結果是消耗的 Gas 比填寫的要少,系統會把剩下的退還。
Gas 就是讓礦工打包須要付出的費用,你本身能夠定義須要多少個 Gas,每一個 Gas 值多少以太幣,並告訴礦工說,我願意爲這筆交易付出多少錢、這筆錢轉給誰和轉多少。這裏面 Gas 的計算最終在以太坊的虛擬機中按一條一條的指令扣費,你的這些轉帳裏面,按照指令條數和存儲空間計費,最終把你的手續費扣下來。
Nonce 是帳戶交易計數,比方說你的第一筆交易 Nonce 是0,而後一、二、3以此類推加上去。
ChainID 是分叉鏈區分。以太坊和比特幣都有本身的測試網絡,假如說你在測試網絡上面簽署一筆交易,別人把它放到主網中運行,這時會發生轉帳。ChainID 會在當前跑的區塊鏈裏面,記錄下你的 ID,防止你測試時引起交易。別人拿到這筆交易到主網上面從新運行時,會有一個負責全部交易的計數器,能夠防止交易的二次發生。
獲得簽名以後,就會有橢圓曲線還原出你的公鑰,公鑰能夠算出來你的地址,因此這是剛剛所說的以太坊的交易裏面沒有 from。由於有橢圓曲線這個非對稱加密的存在,它拿到你的公鑰後,能夠反推出這個地址是否爲發出人的地址。這也就意味着,若是你們的 nonce 和交易計數是一致的話,不管這個交易發給誰簽名,我只要拿簽名後的數據就能夠上傳並執行這筆交易。
交易簽名完以後你要廣播並上鏈,上圖是以太坊礦工打包的記錄。
交易上鍊的過程分爲四點,首先發送交易到指定節點,或前面提到的某一個授信節點。對於錢包來講,它們會運行本身的節點,像以太坊會有官方的客戶端 Gas。咱們作錢包須要本身運維提供服務的節點池,用戶把這一條交易發到節點上面,節點之間互相通信,把這筆交易的信息擴散到全網,而後礦工節點(即專門作打包交易的節點)有一個打包池。以太坊交易的活躍程度比你當前打包要快,特別是網絡擁堵的時候,放到這個池子以後,礦工在池子裏面挑選一些對於他們有利的,誰願意花錢打包,被礦工選中打包到區塊裏面。
礦工打包策略很簡單,那就是向錢看。網絡蒐集全網蒐集到各類各樣的交易上傳到 TxPool 即交易池當中。礦工老是在尋找找到開出更高 Gas,也就是說願意付出更多手續費的交易,並打包進來。由於最終的前30名的 Gas 費用最終落入礦工,因此他們就會由高到低排序,並先服務 Gas 開價更高的富人。在打包的過程當中有可能出現空塊,出空塊的意義很大,由於你打包一塊出來的同時,還能夠拿到以太坊網絡自己給你的獎勵。
一條鏈上數目龐大的節點,能保證你的礦工足夠的分散。全部的打包的礦工都是在同一個大樓裏面,我控制這個大樓就是控制這個網絡。目前來講主鏈上只有礦池,沒有孤兒礦工。由於礦池是全數據的節點,礦池找了不少礦工(也就是礦機),把交易派發給礦工,讓它們算出來比較合理的塊,並儘快地把塊打包。如今來講礦機幾乎是不知道區塊鏈,它不會理解這一串長鏈有什麼意義。另外,跟礦機和礦池強大的計算能力相比,你的電腦計算能力很弱,挖礦的效率跟不上。因此目前來看,礦池纔是金主,即網絡的實際控制人。
講了這麼多,你們應該理解到我文章開頭下的結論————你的帳戶就是一把私鑰,並且你的帳戶有了一把私鑰以後,拿你的私鑰作了簽名。這時就會發一筆交易,你能夠轉帳,你有私鑰地址後能夠接收別人轉過來的資產。接下來咱們往下繼續探究帳戶的結構,從底層思考以太坊。
你能夠把以太坊帳戶想象成狀態機模型的結構。如圖, alice 和 Bob 發生一筆交易併發生簽名(Nonce = 1, Balance = 3)而後這筆交易被區塊打包。打包以前,這個 alice 有42個,打包完成以後變成39個且 Nonce +1。
這裏 nonce 和 balbance 沒必要贅述,當 Stateroot 不爲空時,這個帳號是一個合約,即合約的狀態就存在該字段中。 CodeHash 是你的合約帳戶裏面的具體代碼,但不徹底是在這兩個字段裏面,這兩個字段引用到底下的時候,只是一個指引你去找到對應東西的索引。
合約帳戶跟普通帳戶不同,普通用戶的狀態是空的,不可以存儲狀態。以太坊用戶的錢包狀態是每一個人有多少錢。
爲何要講這個呢?你們知道以太坊去年由於不少項目發衆籌而變得很火。對於衆籌來講,每一個 Token 即每一個代幣都是一個合約,這個合約的數據記錄在合約帳戶上,而像以太坊的餘額是記錄在帳戶自己。
在這裏舉標準的以太坊 ERC-20 代幣規範做爲例子。decimal 是餘額中小數點的位數。當用戶用這個方法查代幣餘額時,記錄在這個部署的合約中。剩下的提供給咱們更加經常使用的方法,包括受權,而在受權以後你能夠作一些頗有趣的事情,好比去中心化交易。去中心化交易依賴於標準,把你的代幣受權給合約的接口。須要留意以太坊自己的 Gas 是不可變動的,這與 imToken 不一樣。
剛剛說有合約,你要去調用這個合約,調用合約的時候讀、寫。讀合約的時候能夠經過 JSON-RPC 的接口;寫合約就是發交易,把調用合約的方法寫在前面的交易的 data 字段,兩種調用都須要進行 solidity-ABI 編碼,裏面有調取合約的方法,每次發這個交易的時候,你要根據這個編碼編出來再調用。
編碼的過程大概是這樣的:你這裏有一個 balance ,發起交易時,首先要把 ID 找出來,把你的參數再編碼,加起來拼接在一塊兒,最終你作調用的時候讀取合約,寫一下的 data 參數。
想了解更多,我推薦閱讀 Ethereumjs 源碼。
Ethereumjs 在個人分享中,講到代碼相關的內容都附帶了 GitHub 連接,源碼或者裏面的例子都來自於對應的倉庫。若想快速瞭解一下以太坊如何運做,不妨去看看 Ethereumjs 的源碼。
GitHub: kaichen(github.com/kaichen)
Twitter: _kaichen
以上就是我今天分享的內容,謝謝你們。