Merge pull request #159 from bigchaindb/tendermint-tx-fixed

Update driver in order to be compatible with BigchainDB2.0
This commit is contained in:
Manolo 2018-03-29 19:29:56 +02:00 committed by GitHub
commit 618c2526f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 261 additions and 228 deletions

View File

@ -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
View 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
View File

@ -0,0 +1,5 @@
#!/bin/bash
set -e -x
docker-compose up -d bigchaindb

5
.ci/travis-install.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
set -e -x
docker-compose build --no-cache bigchaindb

5
.ci/travis_script.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
set -e -x
docker-compose run --rm js-bigchaindb-driver yarn test

View File

@ -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
View 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

View 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
View 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"

View File

@ -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:

View File

@ -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

View File

@ -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": [

View File

@ -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
})

View File

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

View File

@ -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
}
}
))

View File

@ -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' }

View File

@ -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)
})
})

View File

@ -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')
))
})

View File

@ -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) {