ethereumjs/ethereumjs-account-1-簡介和API
https://github.com/ethereumjs/ethereumjs-account
Encoding, decoding and validation of Ethereum's Account schema
以太坊賬戶概要的編碼、解碼和驗證
賬戶 = 賬戶address + 賬戶狀態
這個庫處理的是賬戶狀態,然後將賬戶address作為key,賬戶狀態作為value,新增到state字首樹中,即生成了一個賬戶
SYNOPSIS概要
This library eases the handling of Ethereum accounts, where accounts can be either external accounts or contracts (see
這個庫簡化了以太坊賬戶狀態的處理,賬戶可以是外部賬戶(即我們日常使用的賬戶)或合約賬戶(即合約部署處)
Note that the library is not meant to be used to handle your wallet accounts, use e.g. the web3-eth-personal package from the web3.js
library for that. This is just a semantic wrapper to ease the use of account data and provide functionality for reading and writing accounts from and to the Ethereum state trie.
注意這個庫不意味著用來處理你的錢包賬戶,錢包賬戶應該使用來自web3.js
庫的web3-eth-personal包。這個只是用來簡化賬戶資料的使用和提供從和給以太坊狀態字首樹讀寫賬戶的函式的語義封裝
INSTALL安裝
npm install ethereumjs-account
BROWSER瀏覽器
This module work with browserify
. 該模組browserify
一起使用
⚠️賬戶 = 賬戶address + 賬戶狀態
這裡的account = new Account(data)生成的是賬戶狀態,最後還需要將其與某個address對應起來,對應的方法就是將其作為區塊鏈的state merkle-Patricia樹的key = address ,value = account.serialize()-即其的RLP序列化值,然後新增到字首樹中去,這樣才真正生成了一個賬戶,如:
stateTrie.put(address, account.serialize(), cb)
API
new Account([data])
Creates a new account object建立一個賬戶物件
data
- an account can be initialized with either abuffer
containing the RLP serialized account. Or anArray
of buffers relating to each of the account Properties, listed in order below. For example: 一個賬戶能夠帶著要麼是包含著RLP編碼的序列化賬戶的buffer,或者是關聯每個賬戶屬性,就像下面順序列舉一樣的buffers陣列來進行初始化
var raw = [ //這個是陣列格式的初始化 '0x02', //nonce '0x0384', //balance '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', //stateRoot '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' //codeHash ]; var account = new Account(raw);
Or lastly an Object
containing the Properties of the account:
下面這個例子就是包含著賬戶屬性的物件
var raw = {//這個是object物件格式的初始化 nonce: '', balance: '0x03e7', stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' }; var account = new Account(raw);
For Object
and Array
each of the elements can either be a Buffer
, hex String
, Number
, or an object with a toBuffer
method such as Bignum
.
對於物件或者陣列的每一個元素都要麼是Buffer,十六進位制字串,數字,或者帶著toBuffer
方法的物件,如Bignum
。
還有RLP編碼的序列化格式:
var account = new Account('f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470')
Account
Properties 賬戶屬性
nonce
- The account's nonce.balance
- The account's balance in wei.stateRoot
- The stateRoot for the storage of the contract. 用於合約的儲存的stateRootcodeHash
- The hash of the code of the contract. 合約程式碼的hash
注意:如果是外部賬戶,它的codeHash
為空字串的hash值
Account
Methods賬戶方法
account.isEmpty()
Returns a Boolean
determining if the account is empty.
返回一個Boolean
值,確定賬戶是否為空
account.isContract() 看程式碼是怎麼檢測的
Returns a Boolean
deteremining if the account is a contract.
返回一個Boolean
值,確定賬戶是否為合約賬戶
account.serialize()
Returns the RLP serialization of the account as a Buffer
.
返回Buffer型別的賬戶的RLP編碼序列化,得到的就是上面使用RLP編碼的序列化格式方法進行初始化時的RLP序列化的值,如:
'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'
account.toJSON([object])
Returns the account as JSON.
返回JSON型別的賬戶
object
- ABoolean
that defaults to false. Ifobject
is true then this will return anObject
, else it will return anArray
. 預設為false的Boolean
值。如果object
為true,那麼該方法將返回一個物件,否則他將返回一個數組
account.getCode(trie, cb)
Fetches the code from the trie.
從字首樹中獲取程式碼,得到的是賬戶中codeHash的值
trie
- The trie storing the accounts. 儲存賬戶的字首樹cb
- The callback. 回撥函式
account.setCode(trie, code, cb)
Stores the code in the trie.
在字首樹上儲存程式碼。就是更改賬戶中codeHash的值,如果改後不為空字串hash值,則相應存到字首樹的底層資料庫上
trie
- The trie storing the accounts. 儲存賬戶的字首樹code
- ABuffer
. Buffer值的程式碼cb
- The callback. 回撥函式
Example for getCode
and setCode
: getCode
和setCode方法的例子
// Requires manual merkle-patricia-tree install const SecureTrie = require('merkle-patricia-tree/secure') const Account = require('./index.js') let code = Buffer.from('73095e7baea6a6c7c4c2dfeb977efac326af552d873173095e7baea6a6c7c4c2dfeb977efac326af552d873157', 'hex') let raw = { nonce: '', balance: '0x03e7', stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', codeHash: '0xb30fb32201fe0486606ad451e1a61e2ae1748343cd3d411ed992ffcc0774edd4' } let account = new Account(raw) let trie = new SecureTrie() account.setCode(trie, code, function (err, codeHash) { console.log(`Code with hash 0x${codeHash.toString('hex')} set to trie`) account.getCode(trie, function (err, code) {//因為上面的setCode會對應更改account中的codeHash,當getCode時查詢的就是account中的codeHash對應的code console.log(`Code ${code.toString('hex')} read from trie`) }) })
account.getStorage(trie, key, cb)
Fetches key
from the account's storage.
從賬戶的儲存中(即從字首樹中)獲取key對應的值
account.setStorage(trie, key, val, cb)
Stores a val
at the key
in the contract's storage.
在合約的儲存中對應的key值處儲存val值,會導致account的stateRoot值更改
Example for getStorage
and setStorage
: getStorage
和setStorage
方法的例子
// Requires manual merkle-patricia-tree install const SecureTrie = require('merkle-patricia-tree/secure') const Account = require('./index.js') let raw = { nonce: '', balance: '0x03e7', stateRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', codeHash: '0xb30fb32201fe0486606ad451e1a61e2ae1748343cd3d411ed992ffcc0774edd4' } let account = new Account(raw) let trie = new SecureTrie() let key = Buffer.from('0000000000000000000000000000000000000000', 'hex') let value = Buffer.from('01', 'hex') account.setStorage(trie, key, value, function (err, value) { account.getStorage(trie, key, function (err, value) { console.log(`Value ${value.toString('hex')} set and retrieved from trie.`) }) })
更加詳細的理解可看ethereumjs/ethereumjs-account-2-test
實現程式碼:
index.js
const ethUtil = require('ethereumjs-util') const rlp = require('rlp') const Buffer = require('safe-buffer').Buffer var Account = module.exports = function (data) { // Define Properties,定義賬戶狀態的屬性 var fields = [{ name: 'nonce', default: Buffer.alloc(0) }, { name: 'balance', default: Buffer.alloc(0) }, { name: 'stateRoot', length: 32, default: ethUtil.SHA3_RLP }, { name: 'codeHash', length: 32, default: ethUtil.SHA3_NULL }] ethUtil.defineProperties(this, fields, data) } Account.prototype.serialize = function () { return rlp.encode(this.raw) } Account.prototype.isContract = function () { return this.codeHash.toString('hex') !== ethUtil.SHA3_NULL_S //即只要賬戶的codeHash值不是空字串的hash值即可 } Account.prototype.getCode = function (state, cb) { if (!this.isContract()) {//不是合約賬戶則返回0 cb(null, Buffer.alloc(0)) return } state.getRaw(this.codeHash, cb) //否則得到的是賬戶中codeHash的值 } Account.prototype.setCode = function (trie, code, cb) { var self = this this.codeHash = ethUtil.sha3(code) //其實就是將賬戶中的codeHash改了 if (this.codeHash.toString('hex') === ethUtil.SHA3_NULL_S) { //如果改了後發現codeHash值變成了空字串的hash值 cb(null, Buffer.alloc(0)) //則返回0的Buffer return } trie.putRaw(this.codeHash, code, function (err) { //如果不是空字串的hash值,則設定key = codeHash,value = code存到樹的底層資料庫中 cb(err, self.codeHash) }) } Account.prototype.getStorage = function (trie, key, cb) { var t = trie.copy() t.root = this.stateRoot t.get(key, cb) } Account.prototype.setStorage = function (trie, key, val, cb) { var self = this var t = trie.copy() t.root = self.stateRoot t.put(key, val, function (err) { if (err) return cb() self.stateRoot = t.root //會導致stateRoot值更改 cb() }) } Account.prototype.isEmpty = function () { return this.balance.toString('hex') === '' && this.nonce.toString('hex') === '' && this.stateRoot.toString('hex') === ethUtil.SHA3_RLP_S && this.codeHash.toString('hex') === ethUtil.SHA3_NULL_S }