mirror of
https://github.com/bigchaindb/js-bigchaindb-driver.git
synced 2024-12-29 08:07:51 +01:00
Merge pull request #159 from bigchaindb/tendermint-tx-fixed
Update driver in order to be compatible with BigchainDB2.0
This commit is contained in:
commit
618c2526f8
23
.babelrc
23
.babelrc
@ -1,9 +1,5 @@
|
||||
{
|
||||
"presets": [
|
||||
"@ava/stage-4",
|
||||
"@ava/transform-test-files",
|
||||
"es2015-no-commonjs"
|
||||
],
|
||||
"presets": ["env"],
|
||||
|
||||
"plugins": [
|
||||
"transform-export-extensions",
|
||||
@ -11,21 +7,4 @@
|
||||
"transform-object-rest-spread"
|
||||
],
|
||||
"sourceMaps": true,
|
||||
|
||||
"env": {
|
||||
"bundle": {
|
||||
"plugins": [
|
||||
["transform-runtime", {
|
||||
"polyfill": true,
|
||||
"regenerator": false
|
||||
}]
|
||||
]
|
||||
},
|
||||
"cjs": {
|
||||
"plugins": [
|
||||
"add-module-exports",
|
||||
"transform-es2015-modules-commonjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
9
.ci/travis-before-install.sh
Executable file
9
.ci/travis-before-install.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
|
||||
|
||||
sudo rm /usr/local/bin/docker-compose
|
||||
curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
|
||||
chmod +x docker-compose
|
||||
sudo mv docker-compose /usr/local/bin
|
5
.ci/travis-before-script.sh
Executable file
5
.ci/travis-before-script.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -x
|
||||
|
||||
docker-compose up -d bigchaindb
|
5
.ci/travis-install.sh
Executable file
5
.ci/travis-install.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -x
|
||||
|
||||
docker-compose build --no-cache bigchaindb
|
5
.ci/travis_script.sh
Executable file
5
.ci/travis_script.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -x
|
||||
|
||||
docker-compose run --rm js-bigchaindb-driver yarn test
|
23
.travis.yml
23
.travis.yml
@ -11,17 +11,20 @@ cache:
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
env:
|
||||
global:
|
||||
- DOCKER_COMPOSE_VERSION=1.19.0
|
||||
|
||||
before_install:
|
||||
- docker run -d -p 27017:27017 mongo:3.4 --replSet=bigchain-rs
|
||||
- docker run -d -p 9984:9984
|
||||
-e BIGCHAINDB_KEYPAIR_PUBLIC=8wHUvvraRo5yEoJAt66UTZaFq9YZ9tFFwcauKPDtjkGw
|
||||
-e BIGCHAINDB_KEYPAIR_PRIVATE=5C5Cknco7YxBRP9AgB1cbUVTL4FAcooxErLygw1DeG2D
|
||||
-e BIGCHAINDB_DATABASE_BACKEND=mongodb
|
||||
-e BIGCHAINDB_DATABASE_HOST=172.17.0.1
|
||||
bigchaindb/bigchaindb:1.3.0
|
||||
start
|
||||
- gem install cowsay
|
||||
- npm install -g codecov
|
||||
- .ci/travis-before-install.sh
|
||||
|
||||
install:
|
||||
- .ci/travis-install.sh
|
||||
|
||||
before_script:
|
||||
- .ci/travis-before-script.sh
|
||||
- gem install cowsay
|
||||
- npm install -g codecov
|
||||
|
||||
script: yarn test
|
||||
|
||||
|
12
compose/Dockerfile
Normal file
12
compose/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM python:3.6
|
||||
|
||||
RUN apt-get update && apt-get install -y vim
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
RUN pip install --upgrade pip ipdb ipython
|
||||
|
||||
COPY . /usr/src/app/
|
||||
|
||||
RUN pip install git+https://github.com/bigchaindb/bigchaindb.git
|
17
compose/tendermint/tmdata/config.toml
Normal file
17
compose/tendermint/tmdata/config.toml
Normal file
@ -0,0 +1,17 @@
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
proxy_app = "tcp://bigchaindb:46658"
|
||||
moniker = "anonymous"
|
||||
fast_sync = true
|
||||
db_backend = "leveldb"
|
||||
log_level = "state:debug,*:error"
|
||||
|
||||
[consensus]
|
||||
create_empty_blocks = false
|
||||
|
||||
[rpc]
|
||||
laddr = "tcp://0.0.0.0:46657"
|
||||
|
||||
[p2p]
|
||||
laddr = "tcp://0.0.0.0:46656"
|
39
docker-compose.yml
Normal file
39
docker-compose.yml
Normal file
@ -0,0 +1,39 @@
|
||||
version: '2.1'
|
||||
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo:3.4.13
|
||||
ports:
|
||||
- "27017"
|
||||
command: mongod
|
||||
bigchaindb:
|
||||
depends_on:
|
||||
- mongodb
|
||||
- tendermint
|
||||
image: bigchaindb/bigchaindb:master
|
||||
environment:
|
||||
BIGCHAINDB_DATABASE_HOST: mongodb
|
||||
BIGCHAINDB_DATABASE_PORT: 27017
|
||||
BIGCHAINDB_SERVER_BIND: 0.0.0.0:9984
|
||||
BIGCHAINDB_WSSERVER_HOST: 0.0.0.0
|
||||
BIGCHAINDB_TENDERMINT_HOST: tendermint
|
||||
BIGCHAINDB_TENDERMINT_PORT: 46657
|
||||
ports:
|
||||
- "9984:9984"
|
||||
- "9985:9985"
|
||||
- "46658"
|
||||
healthcheck:
|
||||
test: ["CMD", "bash", "-c", "curl http://bigchaindb:9984 && curl http://tendermint:46657/abci_query"]
|
||||
interval: 3s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
command: -l DEBUG start
|
||||
tendermint:
|
||||
image: tendermint/tendermint:0.12
|
||||
volumes:
|
||||
- ./compose/tendermint/tmdata/config.toml:/tendermint/config.toml
|
||||
entrypoint: ''
|
||||
ports:
|
||||
- "46656"
|
||||
- "46657"
|
||||
command: bash -c "tendermint init && tendermint node"
|
@ -36,10 +36,33 @@ Compatibility Matrix
|
||||
+-----------------------+----------------------------------+
|
||||
| ``1.3`` | ``3.x.x`` |
|
||||
+-----------------------+----------------------------------+
|
||||
| ``2.0`` | ``4.x.x`` |
|
||||
+-----------------------+----------------------------------+
|
||||
|
||||
|
||||
|
||||
Older versions
|
||||
--------------------
|
||||
|
||||
#### Versions 4.x.x
|
||||
|
||||
|
||||
|
||||
As part of the changes in the BigchainDB 2.0 server, some endpoint were
|
||||
modified. In order to be consistent with them, the JS driver does not have
|
||||
anymore the `pollStatusAndFetchTransaction()` method as there are three
|
||||
different ways of posting a transaction.
|
||||
- `async` using the `postTransaction`: the response will return immediately and not wait to see if the transaction is valid.
|
||||
- `sync` using the `postTransactionSync`: the response will return after the transaction is validated.
|
||||
- `commit` using the `postTransactionCommit`: the response will return after the transaction is committed to a block.
|
||||
|
||||
By default in the docs we will use the `postTransactionCommit` as is way of
|
||||
being sure that the transaction is validated and commited to a block, so
|
||||
there will not be any issue if you try to transfer the asset immediately.
|
||||
|
||||
|
||||
#### Versions 3.2.x
|
||||
|
||||
For versions below 3.2, a transfer transaction looked like:
|
||||
|
||||
.. code-block:: js
|
||||
@ -72,7 +95,7 @@ transaction should be now:
|
||||
|
||||
|
||||
The upgrade allows to create transfer transaction spending outputs that belong
|
||||
to different transactions. So for instance is now possible to create a transfer
|
||||
to different transactions. So for instance is now possible to create a transfer
|
||||
transaction spending two outputs from two different create transactions:
|
||||
|
||||
|
||||
|
@ -128,7 +128,7 @@ And sent over to a BigchainDB node:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
conn.postTransaction(txCreateAliceSimpleSigned)
|
||||
conn.postTransactionCommit(txCreateAliceSimpleSigned)
|
||||
|
||||
Notice the transaction ``id``:
|
||||
|
||||
@ -136,22 +136,6 @@ Notice the transaction ``id``:
|
||||
|
||||
txid = txCreateAliceSimpleSigned.id
|
||||
|
||||
To check the status of the transaction:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
conn.getStatus(txCreateAliceSimpleSigned.id)
|
||||
|
||||
It is also possible to check the status every 0.5 seconds
|
||||
with use of the transaction ``id``:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
conn.pollStatusAndFetchTransaction(txCreateAliceSimpleSigned.id)
|
||||
|
||||
.. note:: It may take a small amount of time before a BigchainDB cluster
|
||||
confirms a transaction as being valid.
|
||||
|
||||
Asset Transfer
|
||||
--------------
|
||||
|
||||
@ -198,13 +182,10 @@ And sent over to a BigchainDB node:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
conn.postTransaction(txTransferBobSigned)
|
||||
conn.postTransactionCommit(txTransferBobSigned)
|
||||
|
||||
Check the status again:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
conn.pollStatusAndFetchTransaction(txTransferBobSigned.id)
|
||||
|
||||
Bob is the new owner:
|
||||
|
||||
@ -362,15 +343,10 @@ Recap: Asset Creation & Transfer
|
||||
// Send the transaction off to BigchainDB
|
||||
const conn = new driver.Connection(API_PATH)
|
||||
|
||||
conn.postTransaction(txCreateAliceSimpleSigned)
|
||||
// Check status of transaction every 0.5 seconds until fulfilled
|
||||
.then(() => conn.pollStatusAndFetchTransaction(txCreateAliceSimpleSigned.id))
|
||||
conn.postTransactionCommit(txCreateAliceSimpleSigned)
|
||||
.then(retrievedTx => console.log('Transaction', retrievedTx.id, 'successfully posted.'))
|
||||
// Check status after transaction has completed (result: { 'status': 'valid' })
|
||||
// If you check the status of a transaction to fast without polling,
|
||||
// It returns that the transaction is waiting in the 'backlog'
|
||||
.then(() => conn.getStatus(txCreateAliceSimpleSigned.id))
|
||||
.then(status => console.log('Retrieved status method 2: ', status))
|
||||
// With the postTransactionCommit if the response is correct, then the transaction
|
||||
// is valid and commited to a block
|
||||
|
||||
// Transfer bicycle to Bob
|
||||
.then(() => {
|
||||
@ -386,12 +362,12 @@ Recap: Asset Creation & Transfer
|
||||
let txTransferBobSigned = driver.Transaction.signTransaction(txTransferBob, alice.privateKey)
|
||||
console.log('Posting signed transaction: ', txTransferBobSigned)
|
||||
|
||||
// Post and poll status
|
||||
return conn.postTransaction(txTransferBobSigned)
|
||||
// Post with commit so transaction is validated and included in a block
|
||||
return conn.postTransactionCommit(txTransferBobSigned)
|
||||
})
|
||||
.then(res => {
|
||||
console.log('Response from BDB server:', res)
|
||||
return conn.pollStatusAndFetchTransaction(res.id)
|
||||
return res.id
|
||||
})
|
||||
.then(tx => {
|
||||
console.log('Is Bob the owner?', tx['outputs'][0]['public_keys'][0] == bob.publicKey)
|
||||
@ -608,9 +584,7 @@ and further we transfer it from Bob to Chris. Expectations:
|
||||
const txCreateAliceSimpleSigned = driver.Transaction.signTransaction(txCreateAliceSimple, alice.privateKey)
|
||||
console.log('\n\nPosting signed create transaction for Alice:\n', txCreateAliceSimpleSigned)
|
||||
|
||||
conn.postTransaction(txCreateAliceSimpleSigned)
|
||||
// Check status of transaction every 0.5 seconds until fulfilled
|
||||
.then(() => conn.pollStatusAndFetchTransaction(txCreateAliceSimpleSigned.id))
|
||||
conn.postTransactionCommit(txCreateAliceSimpleSigned)
|
||||
|
||||
// Transfer bicycle from Alice to Bob
|
||||
.then(() => {
|
||||
@ -624,10 +598,9 @@ and further we transfer it from Bob to Chris. Expectations:
|
||||
txTransferBobSigned = driver.Transaction.signTransaction(txTransferBob, alice.privateKey)
|
||||
console.log('\n\nPosting signed transaction to Bob:\n', txTransferBobSigned)
|
||||
|
||||
// Post and poll status
|
||||
return conn.postTransaction(txTransferBobSigned)
|
||||
// Post with commit so transaction is validated and included in a block
|
||||
return conn.postTransactionCommit(txTransferBobSigned)
|
||||
})
|
||||
.then(res => conn.pollStatusAndFetchTransaction(res.id))
|
||||
|
||||
// Second transfer of bicycle from Bob to Chris
|
||||
.then(tx => {
|
||||
@ -641,10 +614,9 @@ and further we transfer it from Bob to Chris. Expectations:
|
||||
let txTransferChrisSigned = driver.Transaction.signTransaction(txTransferChris, bob.privateKey)
|
||||
console.log('\n\nPosting signed transaction to Chris:\n', txTransferChrisSigned)
|
||||
|
||||
// Post and poll status
|
||||
return conn.postTransaction(txTransferChrisSigned)
|
||||
// Post with commit so transaction is validated and included in a block
|
||||
return conn.postTransactionCommit(txTransferChrisSigned)
|
||||
})
|
||||
.then(res => conn.pollStatusAndFetchTransaction(res.id))
|
||||
.then(() => conn.listOutputs(alice.publicKey, true))
|
||||
.then(listSpentOutputs => {
|
||||
console.log("\nSpent outputs for Alice: ", listSpentOutputs.length) // Spent outputs: 1
|
||||
|
56
package.json
56
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bigchaindb-driver",
|
||||
"version": "3.2.0",
|
||||
"version": "4.0.0",
|
||||
"description": "Node.js driver for BigchainDB",
|
||||
"homepage": "https://www.bigchaindb.com/",
|
||||
"bugs": "https://github.com/bigchaindb/js-bigchaindb-driver/issues",
|
||||
@ -20,6 +20,7 @@
|
||||
"build:dist": "cross-env NODE_ENV=production webpack -p",
|
||||
"clean": "rimraf dist/bundle dist/node",
|
||||
"test": "npm run lint && nyc ava test/ && npm run thanks && npm run report-coverage",
|
||||
"testmine": "npm run lint && nyc ava test/integration/test_min*",
|
||||
"thanks": "cowsay Hi, thanks for your interest in BigchainDB. We appreciate your contribution!",
|
||||
"release": "./node_modules/release-it/bin/release-it.js --src.tagName='v%s' --github.release --npm.publish --non-interactive",
|
||||
"release-minor": "./node_modules/release-it/bin/release-it.js minor --src.tagName='v%s' --github.release --npm.publish --non-interactive",
|
||||
@ -35,46 +36,47 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"ava": "^0.25.0",
|
||||
"babel-cli": "^6.22.2",
|
||||
"babel-eslint": "^8.0.0",
|
||||
"babel-loader": "^7.0.0",
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-eslint": "^8.2.2",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-plugin-add-module-exports": "^0.2.1",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.23.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
|
||||
"babel-plugin-transform-export-extensions": "^6.22.0",
|
||||
"babel-plugin-transform-object-assign": "^6.22.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-plugin-transform-runtime": "^6.22.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"babel-preset-es2015-no-commonjs": "0.0.2",
|
||||
"babel-preset-latest": "^6.22.0",
|
||||
"babel-runtime": "^6.22.0",
|
||||
"cross-env": "^5.0.1",
|
||||
"eslint": "^4.1.1",
|
||||
"eslint-config-ascribe": "^3.0.4",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"husky": "^0.14.0",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"cross-env": "^5.1.4",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-config-ascribe": "^3.0.5",
|
||||
"eslint-plugin-import": "^2.9.0",
|
||||
"husky": "^0.14.3",
|
||||
"lint-staged": "^7.0.0",
|
||||
"nyc": "^11.0.2",
|
||||
"release-it": "^7.0.0",
|
||||
"rimraf": "^2.5.4",
|
||||
"sinon": "^4.0.0",
|
||||
"webpack": "^4.0.0"
|
||||
"nyc": "^11.6.0",
|
||||
"release-it": "^7.2.1",
|
||||
"rimraf": "^2.6.2",
|
||||
"sinon": "^4.4.9",
|
||||
"webpack": "^4.4.1",
|
||||
"webpack-cli": "^2.0.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"browser-resolve": "^1.11.2",
|
||||
"bs58": "^4.0.0",
|
||||
"buffer": "^5.0.2",
|
||||
"clone": "^2.1.0",
|
||||
"core-js": "^2.4.1",
|
||||
"bs58": "^4.0.1",
|
||||
"buffer": "^5.1.0",
|
||||
"clone": "^2.1.1",
|
||||
"core-js": "^2.5.4",
|
||||
"decamelize": "^2.0.0",
|
||||
"es6-promise": "^4.0.5",
|
||||
"fetch-ponyfill": "^6.0.0",
|
||||
"es6-promise": "^4.2.4",
|
||||
"fetch-ponyfill": "^6.0.1",
|
||||
"crypto-conditions": "^2.0.1",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"js-sha3": "^0.7.0",
|
||||
"js-utility-belt": "^1.5.0",
|
||||
"json-stable-stringify": "^1.0.1",
|
||||
"query-string": "^5.0.0",
|
||||
"sprintf-js": "^1.0.3",
|
||||
"query-string": "^6.0.0",
|
||||
"sprintf-js": "^1.1.1",
|
||||
"tweetnacl": "^1.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
|
@ -19,10 +19,11 @@ export default class Connection {
|
||||
getApiUrls(endpoint) {
|
||||
return this.path + {
|
||||
'blocks': 'blocks',
|
||||
'blocksDetail': 'blocks/%(blockId)s',
|
||||
'blocksDetail': 'blocks/%(blockHeight)s',
|
||||
'outputs': 'outputs',
|
||||
'statuses': 'statuses',
|
||||
'transactions': 'transactions',
|
||||
'transactionsSync': 'transactions?mode=sync',
|
||||
'transactionsCommit': 'transactions?mode=commit',
|
||||
'transactionsDetail': 'transactions/%(transactionId)s',
|
||||
'assets': 'assets',
|
||||
'metadata': 'metadata',
|
||||
@ -38,24 +39,12 @@ export default class Connection {
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param blockId
|
||||
* @param blockHeight
|
||||
*/
|
||||
getBlock(blockId) {
|
||||
getBlock(blockHeight) {
|
||||
return this._req(this.getApiUrls('blocksDetail'), {
|
||||
urlTemplateSpec: {
|
||||
blockId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param transactionId
|
||||
*/
|
||||
getStatus(transactionId) {
|
||||
return this._req(this.getApiUrls('statuses'), {
|
||||
query: {
|
||||
transaction_id: transactionId
|
||||
blockHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -77,11 +66,10 @@ export default class Connection {
|
||||
* @param transactionId
|
||||
* @param status
|
||||
*/
|
||||
listBlocks(transactionId, status) {
|
||||
listBlocks(transactionId) {
|
||||
return this._req(this.getApiUrls('blocks'), {
|
||||
query: {
|
||||
transaction_id: transactionId,
|
||||
status
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -133,27 +121,12 @@ export default class Connection {
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param txId
|
||||
* @return {Promise}
|
||||
* @param transaction
|
||||
*/
|
||||
pollStatusAndFetchTransaction(txId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setInterval(() => {
|
||||
this.getStatus(txId)
|
||||
.then((res) => {
|
||||
if (res.status === 'valid') {
|
||||
clearInterval(timer)
|
||||
this.getTransaction(txId)
|
||||
.then((res_) => {
|
||||
resolve(res_)
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
clearInterval(timer)
|
||||
reject(err)
|
||||
})
|
||||
}, 500)
|
||||
postTransaction(transaction) {
|
||||
return this._req(this.getApiUrls('transactions'), {
|
||||
method: 'POST',
|
||||
jsonBody: transaction
|
||||
})
|
||||
}
|
||||
|
||||
@ -161,8 +134,20 @@ export default class Connection {
|
||||
* @public
|
||||
* @param transaction
|
||||
*/
|
||||
postTransaction(transaction) {
|
||||
return this._req(this.getApiUrls('transactions'), {
|
||||
postTransactionSync(transaction) {
|
||||
return this._req(this.getApiUrls('transactionsSync'), {
|
||||
method: 'POST',
|
||||
jsonBody: transaction
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param transaction
|
||||
*/
|
||||
postTransactionCommit(transaction) {
|
||||
return this._req(this.getApiUrls('transactionsCommit'), {
|
||||
|
||||
method: 'POST',
|
||||
jsonBody: transaction
|
||||
})
|
||||
|
@ -30,14 +30,6 @@ export default class Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
static hashTransaction(transaction) {
|
||||
// Safely remove any tx id from the given transaction for hashing
|
||||
const tx = { ...transaction }
|
||||
delete tx.id
|
||||
|
||||
return sha256Hash(Transaction.serializeTransactionIntoCanonicalString(tx))
|
||||
}
|
||||
|
||||
static makeTransactionTemplate() {
|
||||
const txTemplate = {
|
||||
'id': null,
|
||||
@ -46,7 +38,7 @@ export default class Transaction {
|
||||
'inputs': [],
|
||||
'metadata': null,
|
||||
'asset': null,
|
||||
'version': '1.0',
|
||||
'version': '2.0',
|
||||
}
|
||||
return txTemplate
|
||||
}
|
||||
@ -58,8 +50,6 @@ export default class Transaction {
|
||||
tx.metadata = metadata
|
||||
tx.inputs = inputs
|
||||
tx.outputs = outputs
|
||||
|
||||
tx.id = Transaction.hashTransaction(tx)
|
||||
return tx
|
||||
}
|
||||
|
||||
@ -246,15 +236,23 @@ export default class Transaction {
|
||||
signedTx.inputs.forEach((input, index) => {
|
||||
const privateKey = privateKeys[index]
|
||||
const privateKeyBuffer = Buffer.from(base58.decode(privateKey))
|
||||
const serializedTransaction = Transaction
|
||||
.serializeTransactionIntoCanonicalString(transaction)
|
||||
const serializedTransaction =
|
||||
Transaction.serializeTransactionIntoCanonicalString(transaction)
|
||||
|
||||
const transactionUniqueFulfillment = input.fulfills ? serializedTransaction
|
||||
.concat(input.fulfills.transaction_id)
|
||||
.concat(input.fulfills.output_index) : serializedTransaction
|
||||
const transactionHash = sha256Hash(transactionUniqueFulfillment)
|
||||
const ed25519Fulfillment = new cc.Ed25519Sha256()
|
||||
ed25519Fulfillment.sign(Buffer.from(serializedTransaction), privateKeyBuffer)
|
||||
ed25519Fulfillment.sign(Buffer.from(transactionHash, 'hex'), privateKeyBuffer)
|
||||
const fulfillmentUri = ed25519Fulfillment.serializeUri()
|
||||
|
||||
input.fulfillment = fulfillmentUri
|
||||
})
|
||||
|
||||
const serializedTransaction =
|
||||
Transaction.serializeTransactionIntoCanonicalString(signedTx)
|
||||
signedTx.id = sha256Hash(serializedTransaction)
|
||||
return signedTx
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ import sinon from 'sinon'
|
||||
|
||||
import * as request from '../../src/request' // eslint-disable-line
|
||||
import { Connection } from '../../src'
|
||||
import { API_PATH } from '../constants'
|
||||
|
||||
const API_PATH = 'http://localhost:9984/api/v1/'
|
||||
const conn = new Connection(API_PATH)
|
||||
|
||||
test('Payload thrown at incorrect API_PATH', t => {
|
||||
@ -24,9 +24,8 @@ test('Payload thrown at incorrect API_PATH', t => {
|
||||
test('Generate API URLS', t => {
|
||||
const endpoints = {
|
||||
'blocks': 'blocks',
|
||||
'blocksDetail': 'blocks/%(blockId)s',
|
||||
'blocksDetail': 'blocks/%(blockHeight)s',
|
||||
'outputs': 'outputs',
|
||||
'statuses': 'statuses',
|
||||
'transactions': 'transactions',
|
||||
'transactionsDetail': 'transactions/%(transactionId)s',
|
||||
'assets': 'assets',
|
||||
@ -59,30 +58,15 @@ test('Request with custom headers', t => {
|
||||
|
||||
test('Get block for a block id', t => {
|
||||
const expectedPath = 'path'
|
||||
const blockId = 'abc'
|
||||
const blockHeight = 'abc'
|
||||
|
||||
conn._req = sinon.spy()
|
||||
conn.getApiUrls = sinon.stub().returns(expectedPath)
|
||||
|
||||
conn.getBlock(blockId)
|
||||
conn.getBlock(blockHeight)
|
||||
t.truthy(conn._req.calledWith(
|
||||
expectedPath,
|
||||
{ urlTemplateSpec: { blockId } }
|
||||
))
|
||||
})
|
||||
|
||||
|
||||
test('Get status for a transaction id', t => {
|
||||
const expectedPath = 'path'
|
||||
const transactionId = 'abc'
|
||||
|
||||
conn._req = sinon.spy()
|
||||
conn.getApiUrls = sinon.stub().returns(expectedPath)
|
||||
|
||||
conn.getStatus(transactionId)
|
||||
t.truthy(conn._req.calledWith(
|
||||
expectedPath,
|
||||
{ query: { transaction_id: transactionId } }
|
||||
{ urlTemplateSpec: { blockHeight } }
|
||||
))
|
||||
})
|
||||
|
||||
@ -105,18 +89,16 @@ test('Get transaction for a transaction id', t => {
|
||||
test('Get list of blocks for a transaction id', t => {
|
||||
const expectedPath = 'path'
|
||||
const transactionId = 'abc'
|
||||
const status = 'status'
|
||||
|
||||
conn._req = sinon.spy()
|
||||
conn.getApiUrls = sinon.stub().returns(expectedPath)
|
||||
|
||||
conn.listBlocks(transactionId, status)
|
||||
conn.listBlocks(transactionId)
|
||||
t.truthy(conn._req.calledWith(
|
||||
expectedPath,
|
||||
{
|
||||
query: {
|
||||
transaction_id: transactionId,
|
||||
status
|
||||
}
|
||||
}
|
||||
))
|
||||
|
@ -4,6 +4,7 @@ import { Transaction, Ed25519Keypair } from '../src'
|
||||
|
||||
// NOTE: It's safer to cast `Math.random()` to a string, to avoid differences
|
||||
// in "float interpretation" between languages (e.g. JavaScript and Python)
|
||||
export const API_PATH = 'http://localhost:9984/api/v1/'
|
||||
export function asset() { return { message: `${Math.random()}` } }
|
||||
export const metaData = { message: 'metaDataMessage' }
|
||||
|
||||
|
@ -2,6 +2,7 @@ import test from 'ava'
|
||||
import { Ed25519Keypair, Transaction, Connection } from '../../src'
|
||||
|
||||
import {
|
||||
API_PATH,
|
||||
alice,
|
||||
aliceCondition,
|
||||
aliceOutput,
|
||||
@ -11,8 +12,6 @@ import {
|
||||
metaData
|
||||
} from '../constants'
|
||||
|
||||
const API_PATH = 'http://localhost:9984/api/v1/'
|
||||
|
||||
|
||||
test('Keypair is created', t => {
|
||||
const keyPair = new Ed25519Keypair()
|
||||
@ -36,8 +35,7 @@ test('Valid CREATE transaction', t => {
|
||||
)
|
||||
const txSigned = Transaction.signTransaction(tx, alice.privateKey)
|
||||
|
||||
return conn.postTransaction(txSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(txSigned)
|
||||
.then(resTx => t.truthy(resTx))
|
||||
})
|
||||
|
||||
@ -55,8 +53,7 @@ test('Valid TRANSFER transaction with single Ed25519 input', t => {
|
||||
alice.privateKey
|
||||
)
|
||||
|
||||
return conn.postTransaction(createTxSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(createTxSigned)
|
||||
.then(() => {
|
||||
const transferTx = Transaction.makeTransferTransaction(
|
||||
[{ tx: createTxSigned, output_index: 0 }],
|
||||
@ -67,8 +64,7 @@ test('Valid TRANSFER transaction with single Ed25519 input', t => {
|
||||
transferTx,
|
||||
alice.privateKey
|
||||
)
|
||||
return conn.postTransaction(transferTxSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(transferTxSigned)
|
||||
.then(resTx => t.truthy(resTx))
|
||||
})
|
||||
})
|
||||
@ -87,8 +83,7 @@ test('Valid TRANSFER transaction with multiple Ed25519 inputs', t => {
|
||||
alice.privateKey
|
||||
)
|
||||
|
||||
return conn.postTransaction(createTxSigned)
|
||||
.then(({ 'id': txId }) => conn.pollStatusAndFetchTransaction(txId))
|
||||
return conn.postTransactionCommit(createTxSigned)
|
||||
.then(() => {
|
||||
const transferTx = Transaction.makeTransferTransaction(
|
||||
[{ tx: createTxSigned, output_index: 0 }, { tx: createTxSigned, output_index: 1 }],
|
||||
@ -100,8 +95,7 @@ test('Valid TRANSFER transaction with multiple Ed25519 inputs', t => {
|
||||
alice.privateKey,
|
||||
bob.privateKey
|
||||
)
|
||||
return conn.postTransaction(transferTxSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(transferTxSigned)
|
||||
.then(resTx => t.truthy(resTx))
|
||||
})
|
||||
})
|
||||
@ -129,8 +123,7 @@ test('Valid TRANSFER transaction with multiple Ed25519 inputs from different tra
|
||||
alice.privateKey
|
||||
)
|
||||
|
||||
return conn.postTransaction(createTxSigned)
|
||||
.then(({ 'id': txId }) => conn.pollStatusAndFetchTransaction(txId))
|
||||
return conn.postTransactionCommit(createTxSigned)
|
||||
.then(() => {
|
||||
const transferTx1 = Transaction.makeTransferTransaction(
|
||||
[{ tx: createTxSigned, output_index: 0 }],
|
||||
@ -151,10 +144,8 @@ test('Valid TRANSFER transaction with multiple Ed25519 inputs from different tra
|
||||
bob.privateKey
|
||||
)
|
||||
|
||||
return conn.postTransaction(transferTxSigned1)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
.then(conn.postTransaction(transferTxSigned2))
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(transferTxSigned1)
|
||||
.then(conn.postTransactionCommit(transferTxSigned2))
|
||||
.then(() => {
|
||||
const transferTxMultipleInputs = Transaction.makeTransferTransaction(
|
||||
[{ tx: transferTxSigned1, output_index: 0 },
|
||||
@ -167,14 +158,12 @@ test('Valid TRANSFER transaction with multiple Ed25519 inputs from different tra
|
||||
carol.privateKey,
|
||||
trent.privateKey
|
||||
)
|
||||
return conn.postTransaction(transferTxSignedMultipleInputs)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(transferTxSignedMultipleInputs)
|
||||
.then(resTx => t.truthy(resTx))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
test('Search for spent and unspent outputs of a given public key', t => {
|
||||
const conn = new Connection(API_PATH)
|
||||
const carol = new Ed25519Keypair()
|
||||
@ -208,10 +197,8 @@ test('Search for spent and unspent outputs of a given public key', t => {
|
||||
carol.privateKey,
|
||||
)
|
||||
|
||||
return conn.postTransaction(createTxSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
.then(() => conn.postTransaction(transferTxSigned))
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(createTxSigned)
|
||||
.then(() => conn.postTransactionCommit(transferTxSigned))
|
||||
.then(() => conn.listOutputs(carol.publicKey))
|
||||
// now listOutputs should return us outputs 0 and 1 (unfiltered)
|
||||
.then(outputs => t.truthy(outputs.length === 2))
|
||||
@ -250,10 +237,8 @@ test('Search for unspent outputs for a given public key', t => {
|
||||
carol.privateKey,
|
||||
)
|
||||
|
||||
return conn.postTransaction(createTxSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
.then(() => conn.postTransaction(transferTxSigned))
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(createTxSigned)
|
||||
.then(() => conn.postTransactionCommit(transferTxSigned))
|
||||
// now listOutputs should return us outputs 0 and 2 (1 is spent)
|
||||
.then(() => conn.listOutputs(carol.publicKey, 'false'))
|
||||
.then(outputs => t.truthy(outputs.length === 2))
|
||||
@ -292,10 +277,8 @@ test('Search for spent outputs for a given public key', t => {
|
||||
carol.privateKey,
|
||||
)
|
||||
|
||||
return conn.postTransaction(createTxSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
.then(() => conn.postTransaction(transferTxSigned))
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(createTxSigned)
|
||||
.then(() => conn.postTransactionCommit(transferTxSigned))
|
||||
// now listOutputs should only return us output 1 (0 and 2 are unspent)
|
||||
.then(() => conn.listOutputs(carol.publicKey, true))
|
||||
.then(outputs => t.truthy(outputs.length === 1))
|
||||
@ -316,8 +299,7 @@ test('Search for an asset', t => {
|
||||
alice.privateKey
|
||||
)
|
||||
|
||||
return conn.postTransaction(createTxSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(createTxSigned)
|
||||
.then(() => conn.searchAssets(createTxSigned.asset.data.message))
|
||||
.then(assets => t.truthy(
|
||||
assets.pop(),
|
||||
@ -340,8 +322,7 @@ test('Search for metadata', t => {
|
||||
alice.privateKey
|
||||
)
|
||||
|
||||
return conn.postTransaction(createTxSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
return conn.postTransactionCommit(createTxSigned)
|
||||
.then(() => conn.searchMetadata(createTxSigned.metadata.message))
|
||||
.then(assets => t.truthy(
|
||||
assets.pop(),
|
||||
@ -349,6 +330,7 @@ test('Search for metadata', t => {
|
||||
))
|
||||
})
|
||||
|
||||
|
||||
test('Search blocks containing a transaction', t => {
|
||||
const conn = new Connection(API_PATH)
|
||||
|
||||
@ -363,11 +345,10 @@ test('Search blocks containing a transaction', t => {
|
||||
alice.privateKey
|
||||
)
|
||||
|
||||
return conn.postTransaction(createTxSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
|
||||
.then(({ id }) => conn.listBlocks(id, 'VALID'))
|
||||
.then(blocks => conn.getBlock(blocks.pop()))
|
||||
.then(({ block: { transactions } }) => transactions.filter(({ id }) => id === createTxSigned.id))
|
||||
return conn.postTransactionCommit(createTxSigned)
|
||||
.then(({ id }) => conn.listBlocks(id))
|
||||
.then(blockHeight => conn.getBlock(blockHeight.pop()))
|
||||
.then(({ transactions }) => transactions.filter(({ id }) => id === createTxSigned.id))
|
||||
.then(transactions => t.truthy(transactions.length === 1))
|
||||
})
|
||||
|
||||
@ -386,10 +367,11 @@ test('Search transaction containing an asset', t => {
|
||||
alice.privateKey
|
||||
)
|
||||
|
||||
return conn.postTransaction(createTxSigned)
|
||||
.then(({ id }) => conn.pollStatusAndFetchTransaction(id, 'CREATE'))
|
||||
return conn.postTransactionCommit(createTxSigned)
|
||||
.then(({ id }) => conn.listTransactions(id))
|
||||
.then(transactions => t.truthy(transactions.length === 1))
|
||||
.then(transactions => {
|
||||
t.truthy(transactions.length === 1)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import test from 'ava'
|
||||
import cc from 'crypto-conditions'
|
||||
import { Ed25519Keypair, Transaction, ccJsonLoad } from '../../src'
|
||||
import sha256Hash from '../../src/sha256Hash'
|
||||
|
||||
test('Ed25519 condition encoding', t => {
|
||||
const publicKey = '4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS'
|
||||
@ -53,16 +54,27 @@ test('Fulfillment correctly formed', t => {
|
||||
[Transaction.makeOutput(Transaction.makeEd25519Condition(alice.publicKey))],
|
||||
alice.publicKey
|
||||
)
|
||||
// Sign in order to get the tx id, needed for the unique fulfillment in the transfer transaction
|
||||
const signCreateTransaction = Transaction.signTransaction(txCreate, alice.privateKey)
|
||||
|
||||
const txTransfer = Transaction.makeTransferTransaction(
|
||||
[{ tx: txCreate, output_index: 0 }],
|
||||
[{ tx: signCreateTransaction, output_index: 0 }],
|
||||
[Transaction.makeOutput(Transaction.makeEd25519Condition(alice.publicKey))],
|
||||
{}
|
||||
)
|
||||
const msg = Transaction.serializeTransactionIntoCanonicalString(txTransfer)
|
||||
const txSigned = Transaction.signTransaction(txTransfer, alice.privateKey)
|
||||
|
||||
// Here reconstruct the fulfillment of the transfer transaction
|
||||
// The tx is serialized, and extended with tx_id and output index, and then hashed into bytes
|
||||
const msg = Transaction.serializeTransactionIntoCanonicalString(txTransfer)
|
||||
const msgUniqueFulfillment = txTransfer.inputs[0].fulfills ? msg
|
||||
.concat(txTransfer.inputs[0].fulfills.transaction_id)
|
||||
.concat(txTransfer.inputs[0].fulfills.output_index) : msg
|
||||
const msgHash = sha256Hash(msgUniqueFulfillment)
|
||||
|
||||
t.truthy(cc.validateFulfillment(
|
||||
txSigned.inputs[0].fulfillment, txCreate.outputs[0].condition.uri,
|
||||
Buffer.from(msg)
|
||||
Buffer.from(msgHash, 'hex')
|
||||
))
|
||||
})
|
||||
|
||||
|
@ -54,6 +54,7 @@ const PLUGINS = [
|
||||
]
|
||||
|
||||
const PROD_PLUGINS = [
|
||||
/*
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false,
|
||||
@ -67,6 +68,7 @@ const PROD_PLUGINS = [
|
||||
debug: false,
|
||||
minimize: true,
|
||||
}),
|
||||
*/
|
||||
]
|
||||
|
||||
if (PRODUCTION) {
|
||||
|
Loading…
Reference in New Issue
Block a user