Skip to content
This repository was archived by the owner on Aug 11, 2021. It is now read-only.

Commit dc19aa7

Browse files
committed
feat: new IPLD Format API
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
1 parent 4eaa791 commit dc19aa7

File tree

24 files changed

+1222
-1210
lines changed

24 files changed

+1222
-1210
lines changed

eth-account-snapshot/index.js

Lines changed: 21 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,31 @@
11
'use strict'
22
const EthAccount = require('ethereumjs-account')
3+
const multicodec = require('multicodec')
4+
35
const cidFromHash = require('../util/cidFromHash')
46
const createResolver = require('../util/createResolver')
57
const emptyCodeHash = require('../util/emptyCodeHash')
68

7-
module.exports = createResolver('eth-account-snapshot', EthAccount, mapFromEthObj)
8-
9-
10-
function mapFromEthObj (account, options, callback) {
11-
const paths = []
12-
13-
// external links
14-
15-
paths.push({
16-
path: 'storage',
17-
value: { '/': cidFromHash('eth-storage-trie', account.stateRoot).toBaseEncodedString() }
18-
})
19-
20-
// resolve immediately if empty, otherwise link to code
21-
if (emptyCodeHash.equals(account.codeHash)) {
22-
paths.push({
23-
path: 'code',
24-
value: Buffer.from(''),
25-
})
26-
} else {
27-
paths.push({
28-
path: 'code',
29-
value: { '/': cidFromHash('raw', account.codeHash).toBaseEncodedString() }
30-
})
9+
const deserialize = (serialized) => {
10+
const ethObj = new EthAccount(serialized)
11+
12+
const deserialized = {
13+
balance: ethObj.balance,
14+
code: emptyCodeHash.equals(ethObj.codeHash)
15+
? Buffer.alloc(0)
16+
: cidFromHash(multicodec.RAW, ethObj.codeHash),
17+
codeHash: ethObj.codeHash,
18+
isEmpty: ethObj.isEmpty(),
19+
isContract: ethObj.isContract(),
20+
nonce: ethObj.nonce,
21+
stateRoot: ethObj.stateRoot,
22+
storage: cidFromHash(multicodec.ETH_STORAGE_TRIE, ethObj.stateRoot),
23+
_ethObj: ethObj
3124
}
3225

33-
// external links as data
34-
35-
paths.push({
36-
path: 'stateRoot',
37-
value: account.stateRoot
38-
})
39-
40-
paths.push({
41-
path: 'codeHash',
42-
value: account.codeHash
43-
})
26+
Object.defineProperty(deserialized, '_ethObj', { enumerable: false })
4427

45-
// internal data
46-
47-
paths.push({
48-
path: 'nonce',
49-
value: account.nonce
50-
})
51-
52-
paths.push({
53-
path: 'balance',
54-
value: account.balance
55-
})
56-
57-
// helpers
58-
59-
paths.push({
60-
path: 'isEmpty',
61-
value: account.isEmpty()
62-
})
63-
64-
paths.push({
65-
path: 'isContract',
66-
value: account.isContract()
67-
})
68-
69-
callback(null, paths)
28+
return deserialized
7029
}
30+
31+
module.exports = createResolver(multicodec.ETH_ACCOUNT_SNAPSHOT, deserialize)
Lines changed: 74 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,104 @@
11
/* eslint-env mocha */
22
'use strict'
33

4-
const expect = require('chai').expect
4+
const chai = require('chai')
5+
chai.use(require('dirty-chai'))
6+
const expect = chai.expect
57
const dagEthAccount = require('../index')
68
const resolver = dagEthAccount.resolver
79
const Account = require('ethereumjs-account')
810
const emptyCodeHash = require('../../util/emptyCodeHash')
11+
const CID = require('cids')
12+
const multicodec = require('multicodec')
913

1014
describe('IPLD format resolver (local)', () => {
11-
let testBlob
12-
let testData = {
13-
nonce: new Buffer('02', 'hex'),
14-
balance: new Buffer('04a817c800', 'hex'),
15+
const testData = {
16+
nonce: Buffer.from('02', 'hex'),
17+
balance: Buffer.from('04a817c800', 'hex'),
1518
codeHash: emptyCodeHash,
16-
stateRoot: new Buffer('012304a817c80004a817c80004a817c80004a817c80004a817c80004a817c800', 'hex')
19+
stateRoot: Buffer.from('012304a817c80004a817c80004a817c80004a817c80004a817c80004a817c800', 'hex')
1720
}
18-
19-
before((done) => {
20-
const testAccount = new Account(testData)
21-
dagEthAccount.util.serialize(testAccount, (err, result) => {
22-
if (err) return done(err)
23-
testBlob = result
24-
done()
25-
})
21+
const testAccount = new Account(testData)
22+
const testBlob = dagEthAccount.util.serialize({
23+
_ethObj: testAccount
2624
})
2725

2826
it('multicodec is eth-account-snapshot', () => {
29-
expect(resolver.multicodec).to.equal('eth-account-snapshot')
27+
expect(dagEthAccount.codec).to.equal(multicodec.ETH_ACCOUNT_SNAPSHOT)
3028
})
3129

3230
it('defaultHashAlg is keccak-256', () => {
33-
expect(resolver.defaultHashAlg).to.equal('keccak-256')
31+
expect(dagEthAccount.defaultHashAlg).to.equal(multicodec.KECCAK_256)
3432
})
3533

3634
describe('resolver.resolve', () => {
3735
it('path within scope', () => {
38-
resolver.resolve(testBlob, 'nonce', (err, result) => {
39-
expect(err).to.not.exist
40-
expect(result.value.toString('hex')).to.equal(testData.nonce.toString('hex'))
41-
})
36+
const result = resolver.resolve(testBlob, 'nonce')
37+
expect(result.value).to.eql(testData.nonce)
38+
})
39+
40+
it('resolves "storage" to correct type', () => {
41+
const result = resolver.resolve(testBlob, 'storage')
42+
expect(CID.isCID(result.value)).to.be.true()
43+
})
44+
45+
it('resolves "code" to correct type', () => {
46+
const result = resolver.resolve(testBlob, 'storage')
47+
expect(CID.isCID(result.value)).to.be.true()
48+
})
49+
50+
it('resolves "stateRoot" to correct type', () => {
51+
const result = resolver.resolve(testBlob, 'stateRoot')
52+
expect(Buffer.isBuffer(result.value)).to.be.true()
53+
})
54+
55+
it('resolves "codeHash" to correct type', () => {
56+
const result = resolver.resolve(testBlob, 'codeHash')
57+
expect(Buffer.isBuffer(result.value)).to.be.true()
58+
})
59+
60+
it('resolves "nonce" to correct type', () => {
61+
const result = resolver.resolve(testBlob, 'nonce')
62+
expect(Buffer.isBuffer(result.value)).to.be.true()
63+
})
64+
65+
it('resolves "balance" to correct type', () => {
66+
const result = resolver.resolve(testBlob, 'balance')
67+
expect(Buffer.isBuffer(result.value)).to.be.true()
4268
})
4369

44-
it('resolves empty code', () => {
45-
resolver.resolve(testBlob, 'code', (err, result) => {
46-
expect(err).to.not.exist
47-
expect(result.remainderPath).to.equal('')
48-
expect(Buffer.isBuffer(result.value)).to.equal(true)
49-
expect(result.value.length).to.equal(0)
50-
})
70+
it('resolves "isEmpty" to correct type', () => {
71+
const result = resolver.resolve(testBlob, 'isEmpty')
72+
expect(result.value).to.be.false()
5173
})
74+
75+
it('resolves "isContract" to correct type', () => {
76+
const result = resolver.resolve(testBlob, 'isContract')
77+
expect(result.value).to.be.false()
78+
})
79+
})
80+
81+
it('resolves empty code', () => {
82+
const result = resolver.resolve(testBlob, 'code')
83+
expect(result.remainderPath).to.equal('')
84+
expect(Buffer.isBuffer(result.value)).to.be.true()
85+
expect(result.value.length).to.equal(0)
5286
})
5387

5488
describe('resolver.tree', () => {
55-
it('basic sanity test', () => {
56-
resolver.tree(testBlob, (err, paths) => {
57-
expect(err).to.not.exist
58-
expect(Array.isArray(paths)).to.eql(true)
59-
})
89+
it('basic sanity test', async () => {
90+
const tree = resolver.tree(testBlob)
91+
const paths = [...tree]
92+
expect(paths).to.have.members([
93+
'storage',
94+
'code',
95+
'stateRoot',
96+
'codeHash',
97+
'nonce',
98+
'balance',
99+
'isEmpty',
100+
'isContract'
101+
])
60102
})
61103
})
62104
})

eth-block-list/index.js

Lines changed: 16 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,26 @@
11
'use strict'
2-
const waterfall = require('async/waterfall')
3-
const each = require('async/each')
4-
const asyncify = require('async/asyncify')
52
const RLP = require('rlp')
6-
const EthBlockHead = require('ethereumjs-block/header')
7-
const multihash = require('multihashing-async')
8-
const cidFromHash = require('../util/cidFromHash')
9-
const ethBlockResolver = require('../eth-block').resolver
3+
const multicodec = require('multicodec')
4+
const ipldEthBlock = require('../eth-block')
105
const createResolver = require('../util/createResolver')
116

12-
const ethBlockListResolver = createResolver('eth-block-list', undefined, mapFromEthObj)
13-
const util = ethBlockListResolver.util
14-
util.serialize = asyncify((ethBlockList) => {
15-
const rawOmmers = ethBlockList.map((ethBlock) => ethBlock.raw)
16-
return RLP.encode(rawOmmers)
17-
})
18-
util.deserialize = asyncify((serialized) => {
7+
const deserialize = (serialized) => {
198
const rawOmmers = RLP.decode(serialized)
20-
return rawOmmers.map((rawBlock) => new EthBlockHead(rawBlock))
21-
})
22-
util.cid = (blockList, options, callback) => {
23-
if (typeof options === 'function') {
24-
callback = options
25-
options = {}
26-
}
27-
options = options || {}
28-
const hashAlg = options.hashAlg || 'keccak-256'
29-
const version = typeof options.version === 'undefined' ? 1 : options.version
30-
31-
waterfall([
32-
(cb) => util.serialize(blockList, cb),
33-
(data, cb) => multihash.digest(data, hashAlg, cb),
34-
asyncify((mhash) => cidFromHash('eth-block-list', mhash, options))
35-
], callback)
36-
}
37-
38-
module.exports = ethBlockListResolver
39-
40-
41-
function mapFromEthObj (ethBlockList, options, callback) {
42-
let paths = []
43-
44-
// external links (none)
45-
46-
// external links as data (none)
9+
const deserialized = rawOmmers.map((rawBlock) => {
10+
return ipldEthBlock.util.deserialize(rawBlock)
11+
})
4712

48-
// helpers
13+
deserialized.count = deserialized.length
4914

50-
paths.push({
51-
path: 'count',
52-
value: ethBlockList.length
53-
})
15+
return deserialized
16+
}
5417

55-
// internal data
18+
const ethBlockListResolver = createResolver(
19+
multicodec.ETH_BLOCK_LIST, deserialize)
5620

57-
// add paths for each block
58-
each(ethBlockList, (ethBlock, next) => {
59-
const index = ethBlockList.indexOf(ethBlock)
60-
const blockPath = index.toString()
61-
// block root
62-
paths.push({
63-
path: blockPath,
64-
value: ethBlock
65-
})
66-
// block children
67-
ethBlockResolver._mapFromEthObject(ethBlock, {}, (err, subpaths) => {
68-
if (err) return next(err)
69-
// append blockPath to each subpath
70-
subpaths.forEach((path) => path.path = blockPath + '/' + path.path)
71-
paths = paths.concat(subpaths)
72-
next()
73-
})
74-
}, (err) => {
75-
if (err) return callback(err)
76-
callback(null, paths)
77-
})
21+
ethBlockListResolver.util.serialize = (ethBlockList) => {
22+
const rawOmmers = ethBlockList.map((ethBlock) => ethBlock._ethObj.raw)
23+
return RLP.encode(rawOmmers)
7824
}
25+
26+
module.exports = ethBlockListResolver

0 commit comments

Comments
 (0)