1. 程式人生 > >ethereumjs/ethereumjs-account-1-簡介和API

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

Account 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 a buffer containing the RLP serialized account. Or an Array 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. 用於合約的儲存的stateRoot
  • codeHash - 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 - A Boolean that defaults to false. If object is true then this will return an Object, else it will return an Array. 預設為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 - A Buffer. Buffer值的程式碼
  • cb - The callback. 回撥函式

 

Example for getCode and setCodegetCodesetCode方法的例子

 

// 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 setStoragegetStoragesetStorage方法的例子

// 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
}