Skip to content
This repository has been archived by the owner on Dec 6, 2022. It is now read-only.

[WIP] feat: async await #32

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,8 @@ Though it can also be used as a standalone module:
const IpldBitcoin = require('ipld-bitcoin')

// `bitcoinBlock` is some binary Bitcoin block
IpldBitcoin.util.deserialize(bitcoinBlock, (err, dagNode) => {
if (err) {
throw err
}
console.log(dagNode)
})
const dagNode = await IpldBitcoin.util.deserialize(bitcoinBlock)
console.log(dagNode)
```

## Contribute
Expand Down
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,15 @@
},
"homepage": "https://github.com/ipld/js-ipld-bitcoin#readme",
"dependencies": {
"async": "^2.6.1",
"bitcoinjs-lib": "^4.0.2",
"cids": "~0.5.2",
"git-validate": "^2.2.2",
"multihashes": "~0.4.12",
"cids": "~0.5.5",
"git-validate": "^2.2.4",
"multihashes": "~0.4.14",
"multihashing-async": "~0.5.1"
},
"devDependencies": {
"aegir": "^17.0.1",
"chai": "^4.1.2",
"chai": "^4.2.0",
"dirty-chai": "^2.0.1"
},
"contributors": [
Expand Down
121 changes: 46 additions & 75 deletions src/resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
const util = require('./util')

/**
* @callback ResolveCallback
* @param {?Error} error - Error if path can't be resolved
* @param {Object} result - Result of the path it it was resolved successfully
* @param {*} result.value - Value the path resolves to
* @param {string} result.remainderPath - If the path resolves half-way to a
* @typedef ResolveObject
* @property {*} value - Value the path resolves to
* @property {string} remainderPath - If the path resolves half-way to a
* link, then the `remainderPath` is the part after the link that can be used
* for further resolving.
*/
Expand All @@ -19,96 +17,69 @@ const util = require('./util')
*
* @param {Buffer} binaryBlob - Binary representation of a Bitcoin block
* @param {string} [path='/'] - Path that should be resolved
* @param {ResolveCallback} callback - Callback that handles the return value
* @returns {void}
* @returns {Promise<ResolveObject>}
*/
const resolve = (binaryBlob, path, callback) => {
if (typeof path === 'function') {
callback = path
path = undefined
}
const resolve = async (binaryBlob, path) => {
const dagNode = await util.deserialize(binaryBlob)

util.deserialize(binaryBlob, (err, dagNode) => {
if (err) {
return callback(err)
// Return the deserialized block if no path is given
if (!path) {
return {
value: dagNode,
remainderPath: ''
}
}

// Return the deserialized block if no path is given
if (!path) {
return callback(null, {
value: dagNode,
remainderPath: ''
})
}
const pathArray = path.split('/')
const value = resolveField(dagNode, pathArray[0])
if (value === null) {
throw new Error('No such path')
}

const pathArray = path.split('/')
const value = resolveField(dagNode, pathArray[0])
if (value === null) {
return callback(new Error('No such path'), null)
let remainderPath = pathArray.slice(1).join('/')
// It is a link, hence it may have a remainder
if (value['/'] !== undefined) {
return {
value: value,
remainderPath: remainderPath
}
} else {
if (remainderPath.length > 0) {
throw new Error('No such path')
}

let remainderPath = pathArray.slice(1).join('/')
// It is a link, hence it may have a remainder
if (value['/'] !== undefined) {
return callback(null, {
value: value,
remainderPath: remainderPath
})
} else {
if (remainderPath.length > 0) {
return callback(new Error('No such path'), null)
} else {
return callback(null, {
value: value,
remainderPath: ''
})
}
return {
value: value,
remainderPath: ''
}
})
}
}

/**
* @callback TreeCallback
* @param {?Error} error - Error if paths can't be retreived
* @param {string[] | Object.<string, *>[]} result - The result depends on
* `options.values`, whether it returns only the paths, or the paths with
* the corresponding values
*/
/**
* Return all available paths of a block.
*
* @param {Buffer} binaryBlob - Binary representation of a Bitcoin block
* @param {Object} [options] - Possible options
* @param {boolean} [options.values=false] - Retun only the paths by default.
* If it is `true` also return the values
* @param {TreeCallback} callback - Callback that handles the return value
* @returns {void}
* @returns {Promise<string[] | Object.<string, *>[]>} - The result depends on
* `options.values`, whether it returns only the paths, or the paths with
* the corresponding values
*/
const tree = (binaryBlob, options, callback) => {
if (typeof options === 'function') {
callback = options
options = undefined
}
options = options || {}
const tree = async (binaryBlob, options = {}) => {
const dagNode = await util.deserialize(binaryBlob)
const paths = ['version', 'timestamp', 'difficulty', 'nonce',
'parent', 'tx']

util.deserialize(binaryBlob, (err, dagNode) => {
if (err) {
return callback(err)
if (options.values === true) {
const pathValues = {}
for (let path of paths) {
pathValues[path] = resolveField(dagNode, path)
}

const paths = ['version', 'timestamp', 'difficulty', 'nonce',
'parent', 'tx']

if (options.values === true) {
const pathValues = {}
for (let path of paths) {
pathValues[path] = resolveField(dagNode, path)
}
return callback(null, pathValues)
} else {
return callback(null, paths)
}
})
return pathValues
} else {
return paths
}
}

// Return top-level fields. Returns `null` if field doesn't exist
Expand Down
76 changes: 18 additions & 58 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,95 +4,55 @@ const BitcoinjsBlock = require('bitcoinjs-lib').Block
const CID = require('cids')
const multihashes = require('multihashes')
const multihashing = require('multihashing-async')
const waterfall = require('async/waterfall')

const BITCOIN_BLOCK_HEADER_SIZE = 80

/**
* @callback SerializeCallback
* @param {?Error} error - Error if serialization failed
* @param {?Buffer} binaryBlob - Binary Bitcoin block if serialization was
* successful
*/
/**
* Serialize internal representation into a binary Bitcoin block.
*
* @param {BitcoinBlock} dagNode - Internal representation of a Bitcoin block
* @param {SerializeCallback} callback - Callback that handles the
* return value
* @returns {void}
* @returns {Promise<Buffer>} - Binary Bitcoin block if serialization was
* successful
*/
const serialize = (dagNode, callback) => {
let err = null
let binaryBlob
try {
binaryBlob = dagNode.toBuffer(true)
} catch (serializeError) {
err = serializeError
} finally {
callback(err, binaryBlob)
}
const serialize = async (dagNode) => {
return dagNode.toBuffer(true)
}

/**
* @callback DeserializeCallback
* @param {?Error} error - Error if deserialization failed
* @param {?BitcoinBlock} dagNode - Internal representation of a Bitcoin block
* if deserialization was successful
*/
/**
* Deserialize Bitcoin block into the internal representation,
*
* @param {Buffer} binaryBlob - Binary representation of a Bitcoin block
* @param {DeserializeCallback} callback - Callback that handles the
* return value
* @returns {void}
* @returns {Promise<BitcoinBlock>} - Internal representation of a Bitcoin block
* if deserialization was successful
*/
const deserialize = (binaryBlob, callback) => {
const deserialize = async (binaryBlob) => {
if (binaryBlob.length !== BITCOIN_BLOCK_HEADER_SIZE) {
const err = new Error(
`Bitcoin block header needs to be ${BITCOIN_BLOCK_HEADER_SIZE} bytes`)
return callback(err)
throw new Error(`Bitcoin block header needs to be ${BITCOIN_BLOCK_HEADER_SIZE} bytes`)
}

const dagNode = BitcoinjsBlock.fromBuffer(binaryBlob)
callback(null, dagNode)
return BitcoinjsBlock.fromBuffer(binaryBlob)
}

/**
* @callback CidCallback
* @param {?Error} error - Error if getting the CID failed
* @param {?CID} cid - CID if call was successful
*/
/**
* Get the CID of the DAG-Node.
*
* @param {BitcoinBlock} dagNode - Internal representation of a Bitcoin block
* @param {Object} [options] - Options to create the CID
* @param {number} [options.version=1] - CID version number
* @param {string} [options.hashAlg='dbl-sha2-256'] - Hashing algorithm
* @param {CidCallback} callback - Callback that handles the return value
* @returns {void}
* @returns {Promise<CID>}
*/
const cid = (dagNode, options, callback) => {
if (typeof options === 'function') {
callback = options
options = {}
}
options = options || {}
const cid = async (dagNode, options = {}) => {
// avoid deadly embrace between resolver and util
const hashAlg = options.hashAlg || require('./resolver').defaultHashAlg
const version = typeof options.version === 'undefined' ? 1 : options.version
waterfall([
(cb) => {
try {
multihashing(dagNode.toBuffer(true), hashAlg, cb)
} catch (err) {
cb(err)
}
},
(mh, cb) => cb(null, new CID(version, 'bitcoin-block', mh))
], callback)

return new Promise((resolve, reject) => {
multihashing(dagNode.toBuffer(true), hashAlg, (err, mh) => {
if (err) return reject(err)
resolve(new CID(version, 'bitcoin-block', mh))
})
})
}

// Convert a Bitcoin hash (as Buffer) to a CID
Expand Down
Loading