This repository has been archived by the owner on Aug 11, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BREAKING CHANGE: The API is now async/await based There are numerous changes, the most significant one is that the API is no longer callback based, but it using async/await. If you want to access the original JavaScript Ethereum object, it is now stored in a property called `_ethObj`. So if you e.g. called `deserialized.hash()`, you now have to call `deserialized._ethObj.hash()`. For the full new API please see the [IPLD Formats spec]. [IPLD Formats spec]: https://github.com/ipld/interface-ipld-format
- Loading branch information
Showing
24 changed files
with
1,222 additions
and
1,210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,70 +1,31 @@ | ||
'use strict' | ||
const EthAccount = require('ethereumjs-account') | ||
const multicodec = require('multicodec') | ||
|
||
const cidFromHash = require('../util/cidFromHash') | ||
const createResolver = require('../util/createResolver') | ||
const emptyCodeHash = require('../util/emptyCodeHash') | ||
|
||
module.exports = createResolver('eth-account-snapshot', EthAccount, mapFromEthObj) | ||
|
||
|
||
function mapFromEthObj (account, options, callback) { | ||
const paths = [] | ||
|
||
// external links | ||
|
||
paths.push({ | ||
path: 'storage', | ||
value: { '/': cidFromHash('eth-storage-trie', account.stateRoot).toBaseEncodedString() } | ||
}) | ||
|
||
// resolve immediately if empty, otherwise link to code | ||
if (emptyCodeHash.equals(account.codeHash)) { | ||
paths.push({ | ||
path: 'code', | ||
value: Buffer.from(''), | ||
}) | ||
} else { | ||
paths.push({ | ||
path: 'code', | ||
value: { '/': cidFromHash('raw', account.codeHash).toBaseEncodedString() } | ||
}) | ||
const deserialize = (serialized) => { | ||
const ethObj = new EthAccount(serialized) | ||
|
||
const deserialized = { | ||
balance: ethObj.balance, | ||
code: emptyCodeHash.equals(ethObj.codeHash) | ||
? Buffer.alloc(0) | ||
: cidFromHash(multicodec.RAW, ethObj.codeHash), | ||
codeHash: ethObj.codeHash, | ||
isEmpty: ethObj.isEmpty(), | ||
isContract: ethObj.isContract(), | ||
nonce: ethObj.nonce, | ||
stateRoot: ethObj.stateRoot, | ||
storage: cidFromHash(multicodec.ETH_STORAGE_TRIE, ethObj.stateRoot), | ||
_ethObj: ethObj | ||
} | ||
|
||
// external links as data | ||
|
||
paths.push({ | ||
path: 'stateRoot', | ||
value: account.stateRoot | ||
}) | ||
|
||
paths.push({ | ||
path: 'codeHash', | ||
value: account.codeHash | ||
}) | ||
Object.defineProperty(deserialized, '_ethObj', { enumerable: false }) | ||
|
||
// internal data | ||
|
||
paths.push({ | ||
path: 'nonce', | ||
value: account.nonce | ||
}) | ||
|
||
paths.push({ | ||
path: 'balance', | ||
value: account.balance | ||
}) | ||
|
||
// helpers | ||
|
||
paths.push({ | ||
path: 'isEmpty', | ||
value: account.isEmpty() | ||
}) | ||
|
||
paths.push({ | ||
path: 'isContract', | ||
value: account.isContract() | ||
}) | ||
|
||
callback(null, paths) | ||
return deserialized | ||
} | ||
|
||
module.exports = createResolver(multicodec.ETH_ACCOUNT_SNAPSHOT, deserialize) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,62 +1,104 @@ | ||
/* eslint-env mocha */ | ||
'use strict' | ||
|
||
const expect = require('chai').expect | ||
const chai = require('chai') | ||
chai.use(require('dirty-chai')) | ||
const expect = chai.expect | ||
const dagEthAccount = require('../index') | ||
const resolver = dagEthAccount.resolver | ||
const Account = require('ethereumjs-account') | ||
const emptyCodeHash = require('../../util/emptyCodeHash') | ||
const CID = require('cids') | ||
const multicodec = require('multicodec') | ||
|
||
describe('IPLD format resolver (local)', () => { | ||
let testBlob | ||
let testData = { | ||
nonce: new Buffer('02', 'hex'), | ||
balance: new Buffer('04a817c800', 'hex'), | ||
const testData = { | ||
nonce: Buffer.from('02', 'hex'), | ||
balance: Buffer.from('04a817c800', 'hex'), | ||
codeHash: emptyCodeHash, | ||
stateRoot: new Buffer('012304a817c80004a817c80004a817c80004a817c80004a817c80004a817c800', 'hex') | ||
stateRoot: Buffer.from('012304a817c80004a817c80004a817c80004a817c80004a817c80004a817c800', 'hex') | ||
} | ||
|
||
before((done) => { | ||
const testAccount = new Account(testData) | ||
dagEthAccount.util.serialize(testAccount, (err, result) => { | ||
if (err) return done(err) | ||
testBlob = result | ||
done() | ||
}) | ||
const testAccount = new Account(testData) | ||
const testBlob = dagEthAccount.util.serialize({ | ||
_ethObj: testAccount | ||
}) | ||
|
||
it('multicodec is eth-account-snapshot', () => { | ||
expect(resolver.multicodec).to.equal('eth-account-snapshot') | ||
expect(dagEthAccount.codec).to.equal(multicodec.ETH_ACCOUNT_SNAPSHOT) | ||
}) | ||
|
||
it('defaultHashAlg is keccak-256', () => { | ||
expect(resolver.defaultHashAlg).to.equal('keccak-256') | ||
expect(dagEthAccount.defaultHashAlg).to.equal(multicodec.KECCAK_256) | ||
}) | ||
|
||
describe('resolver.resolve', () => { | ||
it('path within scope', () => { | ||
resolver.resolve(testBlob, 'nonce', (err, result) => { | ||
expect(err).to.not.exist | ||
expect(result.value.toString('hex')).to.equal(testData.nonce.toString('hex')) | ||
}) | ||
const result = resolver.resolve(testBlob, 'nonce') | ||
expect(result.value).to.eql(testData.nonce) | ||
}) | ||
|
||
it('resolves "storage" to correct type', () => { | ||
const result = resolver.resolve(testBlob, 'storage') | ||
expect(CID.isCID(result.value)).to.be.true() | ||
}) | ||
|
||
it('resolves "code" to correct type', () => { | ||
const result = resolver.resolve(testBlob, 'storage') | ||
expect(CID.isCID(result.value)).to.be.true() | ||
}) | ||
|
||
it('resolves "stateRoot" to correct type', () => { | ||
const result = resolver.resolve(testBlob, 'stateRoot') | ||
expect(Buffer.isBuffer(result.value)).to.be.true() | ||
}) | ||
|
||
it('resolves "codeHash" to correct type', () => { | ||
const result = resolver.resolve(testBlob, 'codeHash') | ||
expect(Buffer.isBuffer(result.value)).to.be.true() | ||
}) | ||
|
||
it('resolves "nonce" to correct type', () => { | ||
const result = resolver.resolve(testBlob, 'nonce') | ||
expect(Buffer.isBuffer(result.value)).to.be.true() | ||
}) | ||
|
||
it('resolves "balance" to correct type', () => { | ||
const result = resolver.resolve(testBlob, 'balance') | ||
expect(Buffer.isBuffer(result.value)).to.be.true() | ||
}) | ||
|
||
it('resolves empty code', () => { | ||
resolver.resolve(testBlob, 'code', (err, result) => { | ||
expect(err).to.not.exist | ||
expect(result.remainderPath).to.equal('') | ||
expect(Buffer.isBuffer(result.value)).to.equal(true) | ||
expect(result.value.length).to.equal(0) | ||
}) | ||
it('resolves "isEmpty" to correct type', () => { | ||
const result = resolver.resolve(testBlob, 'isEmpty') | ||
expect(result.value).to.be.false() | ||
}) | ||
|
||
it('resolves "isContract" to correct type', () => { | ||
const result = resolver.resolve(testBlob, 'isContract') | ||
expect(result.value).to.be.false() | ||
}) | ||
}) | ||
|
||
it('resolves empty code', () => { | ||
const result = resolver.resolve(testBlob, 'code') | ||
expect(result.remainderPath).to.equal('') | ||
expect(Buffer.isBuffer(result.value)).to.be.true() | ||
expect(result.value.length).to.equal(0) | ||
}) | ||
|
||
describe('resolver.tree', () => { | ||
it('basic sanity test', () => { | ||
resolver.tree(testBlob, (err, paths) => { | ||
expect(err).to.not.exist | ||
expect(Array.isArray(paths)).to.eql(true) | ||
}) | ||
it('basic sanity test', async () => { | ||
const tree = resolver.tree(testBlob) | ||
const paths = [...tree] | ||
expect(paths).to.have.members([ | ||
'storage', | ||
'code', | ||
'stateRoot', | ||
'codeHash', | ||
'nonce', | ||
'balance', | ||
'isEmpty', | ||
'isContract' | ||
]) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,78 +1,26 @@ | ||
'use strict' | ||
const waterfall = require('async/waterfall') | ||
const each = require('async/each') | ||
const asyncify = require('async/asyncify') | ||
const RLP = require('rlp') | ||
const EthBlockHead = require('ethereumjs-block/header') | ||
const multihash = require('multihashing-async') | ||
const cidFromHash = require('../util/cidFromHash') | ||
const ethBlockResolver = require('../eth-block').resolver | ||
const multicodec = require('multicodec') | ||
const ipldEthBlock = require('../eth-block') | ||
const createResolver = require('../util/createResolver') | ||
|
||
const ethBlockListResolver = createResolver('eth-block-list', undefined, mapFromEthObj) | ||
const util = ethBlockListResolver.util | ||
util.serialize = asyncify((ethBlockList) => { | ||
const rawOmmers = ethBlockList.map((ethBlock) => ethBlock.raw) | ||
return RLP.encode(rawOmmers) | ||
}) | ||
util.deserialize = asyncify((serialized) => { | ||
const deserialize = (serialized) => { | ||
const rawOmmers = RLP.decode(serialized) | ||
return rawOmmers.map((rawBlock) => new EthBlockHead(rawBlock)) | ||
}) | ||
util.cid = (blockList, options, callback) => { | ||
if (typeof options === 'function') { | ||
callback = options | ||
options = {} | ||
} | ||
options = options || {} | ||
const hashAlg = options.hashAlg || 'keccak-256' | ||
const version = typeof options.version === 'undefined' ? 1 : options.version | ||
|
||
waterfall([ | ||
(cb) => util.serialize(blockList, cb), | ||
(data, cb) => multihash.digest(data, hashAlg, cb), | ||
asyncify((mhash) => cidFromHash('eth-block-list', mhash, options)) | ||
], callback) | ||
} | ||
|
||
module.exports = ethBlockListResolver | ||
|
||
|
||
function mapFromEthObj (ethBlockList, options, callback) { | ||
let paths = [] | ||
|
||
// external links (none) | ||
|
||
// external links as data (none) | ||
const deserialized = rawOmmers.map((rawBlock) => { | ||
return ipldEthBlock.util.deserialize(rawBlock) | ||
}) | ||
|
||
// helpers | ||
deserialized.count = deserialized.length | ||
|
||
paths.push({ | ||
path: 'count', | ||
value: ethBlockList.length | ||
}) | ||
return deserialized | ||
} | ||
|
||
// internal data | ||
const ethBlockListResolver = createResolver( | ||
multicodec.ETH_BLOCK_LIST, deserialize) | ||
|
||
// add paths for each block | ||
each(ethBlockList, (ethBlock, next) => { | ||
const index = ethBlockList.indexOf(ethBlock) | ||
const blockPath = index.toString() | ||
// block root | ||
paths.push({ | ||
path: blockPath, | ||
value: ethBlock | ||
}) | ||
// block children | ||
ethBlockResolver._mapFromEthObject(ethBlock, {}, (err, subpaths) => { | ||
if (err) return next(err) | ||
// append blockPath to each subpath | ||
subpaths.forEach((path) => path.path = blockPath + '/' + path.path) | ||
paths = paths.concat(subpaths) | ||
next() | ||
}) | ||
}, (err) => { | ||
if (err) return callback(err) | ||
callback(null, paths) | ||
}) | ||
ethBlockListResolver.util.serialize = (ethBlockList) => { | ||
const rawOmmers = ethBlockList.map((ethBlock) => ethBlock._ethObj.raw) | ||
return RLP.encode(rawOmmers) | ||
} | ||
|
||
module.exports = ethBlockListResolver |
Oops, something went wrong.