2018-08-10 12:49:26 +02:00
|
|
|
// Copyright BigchainDB GmbH and BigchainDB contributors
|
|
|
|
// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
|
|
|
// Code is Apache-2.0 and docs are CC-BY-4.0
|
|
|
|
|
2017-06-19 17:50:34 +02:00
|
|
|
import test from 'ava'
|
|
|
|
import { Ed25519Keypair, Transaction, Connection } from '../../src'
|
|
|
|
|
|
|
|
import {
|
2018-03-26 12:15:36 +02:00
|
|
|
API_PATH,
|
2017-06-19 17:50:34 +02:00
|
|
|
alice,
|
|
|
|
aliceCondition,
|
|
|
|
aliceOutput,
|
|
|
|
bob,
|
|
|
|
bobOutput,
|
2017-06-20 16:17:43 +02:00
|
|
|
asset,
|
|
|
|
metaData
|
2017-06-19 17:50:34 +02:00
|
|
|
} from '../constants'
|
|
|
|
|
|
|
|
|
|
|
|
test('Keypair is created', t => {
|
|
|
|
const keyPair = new Ed25519Keypair()
|
|
|
|
|
|
|
|
t.truthy(keyPair.publicKey)
|
|
|
|
t.truthy(keyPair.privateKey)
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: The following tests are a bit messy currently, please do:
|
|
|
|
//
|
|
|
|
// - tidy up dependency on `pollStatusAndFetchTransaction`
|
2018-08-28 14:09:26 +02:00
|
|
|
test('Valid CREATE transaction with default node', t => {
|
|
|
|
const conn = new Connection()
|
2017-06-19 17:50:34 +02:00
|
|
|
|
|
|
|
const tx = Transaction.makeCreateTransaction(
|
2017-06-20 16:17:43 +02:00
|
|
|
asset(),
|
|
|
|
metaData,
|
2017-06-19 17:50:34 +02:00
|
|
|
[aliceOutput],
|
|
|
|
alice.publicKey
|
|
|
|
)
|
|
|
|
const txSigned = Transaction.signTransaction(tx, alice.privateKey)
|
2017-06-20 16:17:43 +02:00
|
|
|
|
2018-06-19 16:52:23 +02:00
|
|
|
return conn.postTransaction(txSigned)
|
2018-08-23 17:14:59 +02:00
|
|
|
.then(resTx => {
|
|
|
|
t.truthy(resTx)
|
|
|
|
})
|
2018-06-19 16:52:23 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
test('Valid CREATE transaction using async', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
|
|
|
|
|
|
|
const tx = Transaction.makeCreateTransaction(
|
|
|
|
asset(),
|
|
|
|
metaData,
|
|
|
|
[aliceOutput],
|
|
|
|
alice.publicKey
|
|
|
|
)
|
|
|
|
const txSigned = Transaction.signTransaction(tx, alice.privateKey)
|
|
|
|
|
|
|
|
return conn.postTransactionAsync(txSigned)
|
|
|
|
.then(resTx => t.truthy(resTx))
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
test('Valid CREATE transaction using sync', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
|
|
|
|
|
|
|
const tx = Transaction.makeCreateTransaction(
|
|
|
|
asset(),
|
|
|
|
metaData,
|
|
|
|
[aliceOutput],
|
|
|
|
alice.publicKey
|
|
|
|
)
|
|
|
|
const txSigned = Transaction.signTransaction(tx, alice.privateKey)
|
|
|
|
|
|
|
|
return conn.postTransactionSync(txSigned)
|
2017-06-19 17:50:34 +02:00
|
|
|
.then(resTx => t.truthy(resTx))
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
test('Valid TRANSFER transaction with single Ed25519 input', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
|
|
|
const createTx = Transaction.makeCreateTransaction(
|
2017-06-20 16:17:43 +02:00
|
|
|
asset(),
|
|
|
|
metaData,
|
2017-06-19 17:50:34 +02:00
|
|
|
[aliceOutput],
|
|
|
|
alice.publicKey
|
|
|
|
)
|
|
|
|
const createTxSigned = Transaction.signTransaction(
|
|
|
|
createTx,
|
|
|
|
alice.privateKey
|
|
|
|
)
|
|
|
|
|
2018-03-27 16:44:59 +02:00
|
|
|
return conn.postTransactionCommit(createTxSigned)
|
2017-06-19 17:50:34 +02:00
|
|
|
.then(() => {
|
|
|
|
const transferTx = Transaction.makeTransferTransaction(
|
2017-09-19 21:47:33 +02:00
|
|
|
[{ tx: createTxSigned, output_index: 0 }],
|
2017-06-19 17:50:34 +02:00
|
|
|
[aliceOutput],
|
2017-09-19 21:47:33 +02:00
|
|
|
metaData
|
2017-06-19 17:50:34 +02:00
|
|
|
)
|
|
|
|
const transferTxSigned = Transaction.signTransaction(
|
|
|
|
transferTx,
|
|
|
|
alice.privateKey
|
|
|
|
)
|
2018-03-27 16:44:59 +02:00
|
|
|
return conn.postTransactionCommit(transferTxSigned)
|
2017-06-19 17:50:34 +02:00
|
|
|
.then(resTx => t.truthy(resTx))
|
2017-06-20 11:03:19 +02:00
|
|
|
})
|
2017-06-19 17:50:34 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
test('Valid TRANSFER transaction with multiple Ed25519 inputs', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
|
|
|
const createTx = Transaction.makeCreateTransaction(
|
2017-06-20 16:17:43 +02:00
|
|
|
asset(),
|
|
|
|
metaData,
|
2017-06-19 17:50:34 +02:00
|
|
|
[aliceOutput, bobOutput],
|
|
|
|
alice.publicKey
|
|
|
|
)
|
|
|
|
const createTxSigned = Transaction.signTransaction(
|
|
|
|
createTx,
|
|
|
|
alice.privateKey
|
|
|
|
)
|
|
|
|
|
2018-03-27 16:44:59 +02:00
|
|
|
return conn.postTransactionCommit(createTxSigned)
|
2017-06-19 17:50:34 +02:00
|
|
|
.then(() => {
|
|
|
|
const transferTx = Transaction.makeTransferTransaction(
|
2017-09-19 21:47:33 +02:00
|
|
|
[{ tx: createTxSigned, output_index: 0 }, { tx: createTxSigned, output_index: 1 }],
|
2017-06-19 17:50:34 +02:00
|
|
|
[Transaction.makeOutput(aliceCondition, '2')],
|
2017-09-19 21:47:33 +02:00
|
|
|
metaData
|
2017-06-19 17:50:34 +02:00
|
|
|
)
|
|
|
|
const transferTxSigned = Transaction.signTransaction(
|
|
|
|
transferTx,
|
|
|
|
alice.privateKey,
|
|
|
|
bob.privateKey
|
|
|
|
)
|
2018-03-27 16:44:59 +02:00
|
|
|
return conn.postTransactionCommit(transferTxSigned)
|
2017-06-19 17:50:34 +02:00
|
|
|
.then(resTx => t.truthy(resTx))
|
2017-06-20 11:03:19 +02:00
|
|
|
})
|
2017-06-19 17:50:34 +02:00
|
|
|
})
|
2017-06-20 16:17:43 +02:00
|
|
|
|
|
|
|
|
2017-10-31 17:58:47 +01:00
|
|
|
test('Valid TRANSFER transaction with multiple Ed25519 inputs from different transactions', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
|
|
|
const carol = new Ed25519Keypair()
|
|
|
|
const carolCondition = Transaction.makeEd25519Condition(carol.publicKey)
|
|
|
|
const carolOutput = Transaction.makeOutput(carolCondition)
|
|
|
|
const trent = new Ed25519Keypair()
|
|
|
|
const trentCondition = Transaction.makeEd25519Condition(trent.publicKey)
|
|
|
|
const trentOutput = Transaction.makeOutput(trentCondition)
|
|
|
|
const eli = new Ed25519Keypair()
|
|
|
|
const eliCondition = Transaction.makeEd25519Condition(eli.publicKey)
|
|
|
|
|
|
|
|
const createTx = Transaction.makeCreateTransaction(
|
|
|
|
asset(),
|
|
|
|
metaData,
|
|
|
|
[aliceOutput, bobOutput],
|
|
|
|
alice.publicKey
|
|
|
|
)
|
|
|
|
const createTxSigned = Transaction.signTransaction(
|
|
|
|
createTx,
|
|
|
|
alice.privateKey
|
|
|
|
)
|
|
|
|
|
2018-03-27 16:44:59 +02:00
|
|
|
return conn.postTransactionCommit(createTxSigned)
|
2017-10-31 17:58:47 +01:00
|
|
|
.then(() => {
|
|
|
|
const transferTx1 = Transaction.makeTransferTransaction(
|
|
|
|
[{ tx: createTxSigned, output_index: 0 }],
|
|
|
|
[carolOutput],
|
|
|
|
metaData
|
|
|
|
)
|
|
|
|
const transferTxSigned1 = Transaction.signTransaction(
|
|
|
|
transferTx1,
|
|
|
|
alice.privateKey
|
|
|
|
)
|
|
|
|
const transferTx2 = Transaction.makeTransferTransaction(
|
|
|
|
[{ tx: createTxSigned, output_index: 1 }],
|
|
|
|
[trentOutput],
|
|
|
|
metaData
|
|
|
|
)
|
|
|
|
const transferTxSigned2 = Transaction.signTransaction(
|
|
|
|
transferTx2,
|
|
|
|
bob.privateKey
|
|
|
|
)
|
|
|
|
|
2018-03-27 16:44:59 +02:00
|
|
|
return conn.postTransactionCommit(transferTxSigned1)
|
2018-05-23 14:33:45 +02:00
|
|
|
.then(() => conn.postTransactionCommit(transferTxSigned2))
|
2017-10-31 17:58:47 +01:00
|
|
|
.then(() => {
|
|
|
|
const transferTxMultipleInputs = Transaction.makeTransferTransaction(
|
|
|
|
[{ tx: transferTxSigned1, output_index: 0 },
|
|
|
|
{ tx: transferTxSigned2, output_index: 0 }],
|
|
|
|
[Transaction.makeOutput(eliCondition, '2')],
|
|
|
|
metaData
|
|
|
|
)
|
|
|
|
const transferTxSignedMultipleInputs = Transaction.signTransaction(
|
|
|
|
transferTxMultipleInputs,
|
|
|
|
carol.privateKey,
|
|
|
|
trent.privateKey
|
|
|
|
)
|
2018-03-27 16:44:59 +02:00
|
|
|
return conn.postTransactionCommit(transferTxSignedMultipleInputs)
|
2017-10-31 17:58:47 +01:00
|
|
|
.then(resTx => t.truthy(resTx))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2017-06-20 17:46:25 +02:00
|
|
|
test('Search for spent and unspent outputs of a given public key', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
2017-06-21 15:59:28 +02:00
|
|
|
const carol = new Ed25519Keypair()
|
|
|
|
const carolCondition = Transaction.makeEd25519Condition(carol.publicKey)
|
|
|
|
const carolOutput = Transaction.makeOutput(carolCondition)
|
|
|
|
const trent = new Ed25519Keypair()
|
|
|
|
const trentCondition = Transaction.makeEd25519Condition(trent.publicKey)
|
|
|
|
const trentOutput = Transaction.makeOutput(trentCondition)
|
|
|
|
|
2017-06-20 17:46:25 +02:00
|
|
|
|
|
|
|
const createTx = Transaction.makeCreateTransaction(
|
|
|
|
asset(),
|
|
|
|
metaData,
|
2017-06-21 15:59:28 +02:00
|
|
|
[carolOutput, carolOutput],
|
|
|
|
carol.publicKey
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
const createTxSigned = Transaction.signTransaction(
|
|
|
|
createTx,
|
2017-06-21 15:59:28 +02:00
|
|
|
carol.privateKey,
|
|
|
|
carol.privateKey
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// We spent output 1 (of 0, 1)
|
|
|
|
const transferTx = Transaction.makeTransferTransaction(
|
2017-09-19 21:47:33 +02:00
|
|
|
[{ tx: createTxSigned, output_index: 1 }],
|
2017-06-21 15:59:28 +02:00
|
|
|
[trentOutput],
|
2017-09-19 21:47:33 +02:00
|
|
|
metaData
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
const transferTxSigned = Transaction.signTransaction(
|
|
|
|
transferTx,
|
2017-06-21 15:59:28 +02:00
|
|
|
carol.privateKey,
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
|
2018-03-26 12:15:36 +02:00
|
|
|
return conn.postTransactionCommit(createTxSigned)
|
2018-03-27 16:44:59 +02:00
|
|
|
.then(() => conn.postTransactionCommit(transferTxSigned))
|
2017-06-21 15:59:28 +02:00
|
|
|
.then(() => conn.listOutputs(carol.publicKey))
|
2017-06-20 17:46:25 +02:00
|
|
|
// now listOutputs should return us outputs 0 and 1 (unfiltered)
|
|
|
|
.then(outputs => t.truthy(outputs.length === 2))
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
test('Search for unspent outputs for a given public key', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
2017-06-21 15:59:28 +02:00
|
|
|
const carol = new Ed25519Keypair()
|
|
|
|
const carolCondition = Transaction.makeEd25519Condition(carol.publicKey)
|
|
|
|
const carolOutput = Transaction.makeOutput(carolCondition)
|
|
|
|
const trent = new Ed25519Keypair()
|
|
|
|
const trentCondition = Transaction.makeEd25519Condition(trent.publicKey)
|
|
|
|
const trentOutput = Transaction.makeOutput(trentCondition)
|
2017-06-20 17:46:25 +02:00
|
|
|
|
|
|
|
const createTx = Transaction.makeCreateTransaction(
|
|
|
|
asset(),
|
|
|
|
metaData,
|
2017-06-21 15:59:28 +02:00
|
|
|
[carolOutput, carolOutput, carolOutput],
|
|
|
|
carol.publicKey
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
const createTxSigned = Transaction.signTransaction(
|
|
|
|
createTx,
|
2017-06-21 15:59:28 +02:00
|
|
|
carol.privateKey,
|
|
|
|
carol.privateKey
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// We spent output 1 (of 0, 1, 2)
|
|
|
|
const transferTx = Transaction.makeTransferTransaction(
|
2017-09-19 21:47:33 +02:00
|
|
|
[{ tx: createTxSigned, output_index: 1 }],
|
2017-06-21 15:59:28 +02:00
|
|
|
[trentOutput],
|
2017-09-19 21:47:33 +02:00
|
|
|
metaData
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
const transferTxSigned = Transaction.signTransaction(
|
|
|
|
transferTx,
|
2017-06-21 15:59:28 +02:00
|
|
|
carol.privateKey,
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
|
2018-03-26 12:15:36 +02:00
|
|
|
return conn.postTransactionCommit(createTxSigned)
|
2018-03-27 16:44:59 +02:00
|
|
|
.then(() => conn.postTransactionCommit(transferTxSigned))
|
2017-06-20 17:46:25 +02:00
|
|
|
// now listOutputs should return us outputs 0 and 2 (1 is spent)
|
2017-06-21 15:59:28 +02:00
|
|
|
.then(() => conn.listOutputs(carol.publicKey, 'false'))
|
2017-06-20 17:46:25 +02:00
|
|
|
.then(outputs => t.truthy(outputs.length === 2))
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
test('Search for spent outputs for a given public key', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
2017-06-21 15:59:28 +02:00
|
|
|
const carol = new Ed25519Keypair()
|
|
|
|
const carolCondition = Transaction.makeEd25519Condition(carol.publicKey)
|
|
|
|
const carolOutput = Transaction.makeOutput(carolCondition)
|
|
|
|
const trent = new Ed25519Keypair()
|
|
|
|
const trentCondition = Transaction.makeEd25519Condition(trent.publicKey)
|
|
|
|
const trentOutput = Transaction.makeOutput(trentCondition)
|
2017-06-20 17:46:25 +02:00
|
|
|
|
|
|
|
const createTx = Transaction.makeCreateTransaction(
|
|
|
|
asset(),
|
|
|
|
metaData,
|
2017-06-21 15:59:28 +02:00
|
|
|
[carolOutput, carolOutput, carolOutput],
|
|
|
|
carol.publicKey
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
const createTxSigned = Transaction.signTransaction(
|
|
|
|
createTx,
|
2017-06-21 15:59:28 +02:00
|
|
|
carol.privateKey,
|
|
|
|
carol.privateKey
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// We spent output 1 (of 0, 1, 2)
|
|
|
|
const transferTx = Transaction.makeTransferTransaction(
|
2017-09-19 21:47:33 +02:00
|
|
|
[{ tx: createTxSigned, output_index: 1 }],
|
2017-06-21 15:59:28 +02:00
|
|
|
[trentOutput],
|
2017-09-19 21:47:33 +02:00
|
|
|
metaData
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
const transferTxSigned = Transaction.signTransaction(
|
|
|
|
transferTx,
|
2017-06-21 15:59:28 +02:00
|
|
|
carol.privateKey,
|
2017-06-20 17:46:25 +02:00
|
|
|
)
|
|
|
|
|
2018-03-26 12:15:36 +02:00
|
|
|
return conn.postTransactionCommit(createTxSigned)
|
2018-03-27 16:44:59 +02:00
|
|
|
.then(() => conn.postTransactionCommit(transferTxSigned))
|
2017-06-20 17:46:25 +02:00
|
|
|
// now listOutputs should only return us output 1 (0 and 2 are unspent)
|
2017-06-21 15:59:28 +02:00
|
|
|
.then(() => conn.listOutputs(carol.publicKey, true))
|
2017-06-20 17:46:25 +02:00
|
|
|
.then(outputs => t.truthy(outputs.length === 1))
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2017-06-20 16:17:43 +02:00
|
|
|
test('Search for an asset', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
|
|
|
|
|
|
|
const createTx = Transaction.makeCreateTransaction(
|
|
|
|
asset(),
|
|
|
|
metaData,
|
|
|
|
[aliceOutput],
|
|
|
|
alice.publicKey
|
|
|
|
)
|
|
|
|
const createTxSigned = Transaction.signTransaction(
|
|
|
|
createTx,
|
|
|
|
alice.privateKey
|
|
|
|
)
|
|
|
|
|
2018-03-26 12:15:36 +02:00
|
|
|
return conn.postTransactionCommit(createTxSigned)
|
2017-06-20 16:17:43 +02:00
|
|
|
.then(() => conn.searchAssets(createTxSigned.asset.data.message))
|
|
|
|
.then(assets => t.truthy(
|
|
|
|
assets.pop(),
|
|
|
|
createTxSigned.asset.data.message
|
|
|
|
))
|
|
|
|
})
|
2017-06-21 11:00:51 +02:00
|
|
|
|
|
|
|
|
2017-11-21 16:51:31 +01:00
|
|
|
test('Search for metadata', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
|
|
|
|
|
|
|
const createTx = Transaction.makeCreateTransaction(
|
|
|
|
asset(),
|
|
|
|
metaData,
|
|
|
|
[aliceOutput],
|
|
|
|
alice.publicKey
|
|
|
|
)
|
|
|
|
const createTxSigned = Transaction.signTransaction(
|
|
|
|
createTx,
|
|
|
|
alice.privateKey
|
|
|
|
)
|
|
|
|
|
2018-03-26 12:15:36 +02:00
|
|
|
return conn.postTransactionCommit(createTxSigned)
|
2017-11-21 16:51:31 +01:00
|
|
|
.then(() => conn.searchMetadata(createTxSigned.metadata.message))
|
|
|
|
.then(assets => t.truthy(
|
|
|
|
assets.pop(),
|
|
|
|
createTxSigned.metadata.message
|
|
|
|
))
|
|
|
|
})
|
|
|
|
|
2018-03-21 11:30:54 +01:00
|
|
|
|
2017-06-21 11:00:51 +02:00
|
|
|
test('Search blocks containing a transaction', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
|
|
|
|
|
|
|
const createTx = Transaction.makeCreateTransaction(
|
|
|
|
asset(),
|
|
|
|
metaData,
|
|
|
|
[aliceOutput],
|
|
|
|
alice.publicKey
|
|
|
|
)
|
|
|
|
const createTxSigned = Transaction.signTransaction(
|
|
|
|
createTx,
|
|
|
|
alice.privateKey
|
|
|
|
)
|
|
|
|
|
2018-03-26 12:15:36 +02:00
|
|
|
return conn.postTransactionCommit(createTxSigned)
|
2018-03-27 16:44:59 +02:00
|
|
|
.then(({ id }) => conn.listBlocks(id))
|
|
|
|
.then(blockHeight => conn.getBlock(blockHeight.pop()))
|
2018-03-26 12:15:36 +02:00
|
|
|
.then(({ transactions }) => transactions.filter(({ id }) => id === createTxSigned.id))
|
2017-06-21 11:00:51 +02:00
|
|
|
.then(transactions => t.truthy(transactions.length === 1))
|
|
|
|
})
|
2017-06-21 11:01:28 +02:00
|
|
|
|
|
|
|
|
|
|
|
test('Search transaction containing an asset', t => {
|
|
|
|
const conn = new Connection(API_PATH)
|
|
|
|
|
|
|
|
const createTx = Transaction.makeCreateTransaction(
|
|
|
|
asset(),
|
|
|
|
metaData,
|
|
|
|
[aliceOutput],
|
|
|
|
alice.publicKey
|
|
|
|
)
|
|
|
|
const createTxSigned = Transaction.signTransaction(
|
|
|
|
createTx,
|
|
|
|
alice.privateKey
|
|
|
|
)
|
|
|
|
|
2018-03-26 12:15:36 +02:00
|
|
|
return conn.postTransactionCommit(createTxSigned)
|
2017-06-21 11:01:28 +02:00
|
|
|
.then(({ id }) => conn.listTransactions(id))
|
2018-03-21 11:30:54 +01:00
|
|
|
.then(transactions => {
|
|
|
|
t.truthy(transactions.length === 1)
|
|
|
|
})
|
2017-06-21 11:01:28 +02:00
|
|
|
})
|
2017-06-22 17:19:31 +02:00
|
|
|
|
|
|
|
|
|
|
|
test('Content-Type cannot be set', t => {
|
|
|
|
t.throws(() => new Connection(API_PATH, { 'Content-Type': 'application/json' }), Error)
|
|
|
|
})
|