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 (seeAccount Types docs).
这个库简化了以太坊账户状态的处理,账户可以是外部账户(即我们日常使用的账户)或合约账户(即合约部署处)
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 }