Skip to content

Commit

Permalink
Merge pull request #125 from bigchaindb/transfer-multiple-inputs-rebase
Browse files Browse the repository at this point in the history
Transfer multiple inputs rebase
  • Loading branch information
Manolo committed Dec 20, 2017
2 parents 8f5864e + 2348a49 commit 879ae68
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 90 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ coverage
coverage.lcov
.nyc_output
yarn.lock

docs/build/
docs/_build/
docs/build/
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
| BigchainDB Server | BigchainDB JavaScript Driver |
| ----------------- |------------------------------|
| `0.10` | `0.1.x` |
| `>= 1.0.0` | `0.3.x` |
| `1.0.0` | `0.3.x` |
| `>= 1.3.0` | `3.x.x` |

## Breaking changes
Version 3.2 of BigchainDB JavaScript Driver introduces a new way of creating transfer transactions. Check [older versions](https://docs.bigchaindb.com/projects/js-driver/en/latest/readme.html#features)

## Contents

Expand Down
Binary file added docs/source/.conf.py.swp
Binary file not shown.
15 changes: 7 additions & 8 deletions docs/source/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ First, we create an asset registering the bicycle:
const txCreateAliceSimple = driver.Transaction.makeCreateTransaction(
{'asset': 'bicycle'},
{'purchase_price': '€240'},
[
[
driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(alice.publicKey))
],
alice.publicKey
Expand All @@ -29,10 +29,10 @@ So, Alice needs a crypto conditions that defines that she or her daughter can si
We need to define a threshold as well. This defines how many persons have to sign the transaction to ``TRANSFER`` it.
In this case, we define two subconditions with the public keys from Alice and Carly. Next, we set the threshold to **one**.
This means that just one of the subconditions has to sign the transaction to transfer it.
This can be the mother Alice, or Carly herself.
This can be the mother Alice, or Carly herself.

.. code-block:: js
// Create condition for Alice and Carly
let subConditionFrom = driver.Transaction.makeEd25519Condition(alice.publicKey, false)
let subConditionTo = driver.Transaction.makeEd25519Condition(carly.publicKey, false)
Expand All @@ -47,11 +47,10 @@ This can be the mother Alice, or Carly herself.
output.public_keys = [carly.publicKey]
let transaction = driver.Transaction.makeTransferTransaction(
txCreateAliceSimpleSigned,
{'meta': 'Transfer to new user with conditions'},
[output],
0
);
[{ tx: txCreateAliceSimpleSigned, output_index: 0 }],
[output],
{'meta': 'Transfer to new user with conditions'}
);
// Add alice as previous owner
transaction.inputs[0].owners_before = [alice.publicKey]
Expand Down
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ BigchainDB Javascript Driver Documentation

.. toctree::
:maxdepth: 2

← Back to All BigchainDB Docs <https://bigchaindb.readthedocs.io/en/latest/index.html>
readme
quickstart
Expand Down
46 changes: 46 additions & 0 deletions docs/source/readme.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,49 @@ Compatibility Matrix
+-----------------------+----------------------------------+
| ``1.0`` | ``0.3.x`` |
+-----------------------+----------------------------------+
| ``1.3`` | ``3.x.x`` |
+-----------------------+----------------------------------+


Older versions
--------------------
For versions below 3.2, a transfer transaction looked like:

.. code-block:: js
const createTranfer = BigchainDB.Transaction.makeTransferTransaction(
txCreated,
metadata, [BigchainDB.Transaction.makeOutput(
BigchainDB.Transaction.makeEd25519Condition(alice.publicKey))],
0
)
const signedTransfer = BigchainDB.Transaction.signTransaction(createTranfer, keypair.privateKey)
In order to upgrade and do it compatible with the new driver version, this transaction should be now:

.. code-block:: js
const createTranfer = BigchainDB.Transaction.makeTransferTransaction(
[{ tx: txCreated, output_index: 0 }],
[aliceOutput],
metaData
)
const signedTransfer = BigchainDB.Transaction.signTransaction(createTranfer, keypair.privateKey)
The upgrade allows to create transfer transaction spending outputs that belong to different transactions.
So for instance is now possible to create a transfer transaction spending two outputs from two different create transactions:


.. code-block:: js
const createTranfer = BigchainDB.Transaction.makeTransferTransaction(
[{ tx: txCreated1, output_index: 0 }, { tx: txCreated2, output_index: 0 }],
[aliceOutput],
metaData
)
const signedTransfer = BigchainDB.Transaction.signTransaction(createTranfer, keypair.privateKey)
61 changes: 30 additions & 31 deletions docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -173,22 +173,20 @@ First, let's prepare the transaction to be transferred.
.. code-block:: js
const txTransferBob = driver.Transaction.makeTransferTransaction(
// signedTx to transfer
txCreateAliceSimpleSigned,
// metadata
{price: '100 euro'},
// signedTx to transfer and output index
[{ tx: txCreateAliceSimpleSigned, output_index: 0 }],
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(bob.publicKey))],
0
// metadata
{price: '100 euro'}
);
The function ``makeTransferTransaction()`` needs following parameters:

- Unspent transaction: Previous transaction you have control over (i.e. can fulfill its Output Condition)
- Metadata for transaction (e.g. price of sold bike)
- Unspent outputs: Array of `unspent transactions outputs`. Each item contains `Transaction` itself and index of `unspent output` for that `Transaction`.
- Array of output objects to add to the transaction: Think of these as the recipients of the asset after the transaction. For `TRANSFER` transactions, this should usually just be a list of outputs wrapping Ed25519 conditions generated from the public keys of the recipients.
- Indices of the outputs in `unspent transaction` that this transaction fulfills.
- Metadata for transaction (e.g. price of sold bike)

Fulfill transaction by signing it with Alice's private key.

Expand Down Expand Up @@ -377,12 +375,12 @@ Recap: Asset Creation & Transfer
// Transfer bicycle to Bob
.then(() => {
const txTransferBob = driver.Transaction.makeTransferTransaction(
// signedTx to transfer
txCreateAliceSimpleSigned,
// metadata
{price: '100 euro'},
// signedTx to transfer and output index
[{ tx: txCreateAliceSimpleSigned, output_index: 0 }],
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(bob.publicKey))],
0)
// metadata
{price: '100 euro'}
)
// Sign with alice's private key
let txTransferBobSigned = driver.Transaction.signTransaction(txTransferBob, alice.privateKey)
Expand Down Expand Up @@ -617,10 +615,10 @@ and further we transfer it from Bob to Chris. Expectations:
// Transfer bicycle from Alice to Bob
.then(() => {
const txTransferBob = driver.Transaction.makeTransferTransaction(
txCreateAliceSimpleSigned,
{'newOwner': 'Bob'},
[{ tx: txCreateAliceSimpleSigned, output_index: 0 }],
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(bob.publicKey))],
0)
{'newOwner': 'Bob'}
)
// Sign with alice's private key
txTransferBobSigned = driver.Transaction.signTransaction(txTransferBob, alice.privateKey)
Expand All @@ -634,10 +632,10 @@ and further we transfer it from Bob to Chris. Expectations:
// Second transfer of bicycle from Bob to Chris
.then(tx => {
const txTransferChris = driver.Transaction.makeTransferTransaction(
txTransferBobSigned,
{'newOwner': 'Chris'},
[{ tx: txTransferBobSigned, output_index: 0 }],
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(chris.publicKey))],
0)
{'newOwner': 'Chris'}
)
// Sign with bob's private key
let txTransferChrisSigned = driver.Transaction.signTransaction(txTransferChris, bob.privateKey)
Expand Down Expand Up @@ -722,15 +720,16 @@ This gives us 4 tokens to transfer.
.. code-block:: js
const txTransferDivisible = driver.Transaction.makeTransferTransaction(
txCreateAliceDivisibleSigned,
{
metaDataMessage: 'I am specific to this transfer transaction'
},
[{ tx: txCreateAliceDivisibleSigned, output_index: 0 }],
[
driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(carly.publicKey), '2'),
driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(bob.publicKey), '1'),
driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(alice.publicKey), '1')
], 0);
],
{
metaDataMessage: 'I am specific to this transfer transaction'
}
);
To make the use of the last parameter of ``makeTransferTransaction()`` function more clear, we will do another transfer.
We will fulfill the first and second output of the create transaction (0, 1) because Carly and Bob decide to redistribute some money.
Expand All @@ -741,16 +740,16 @@ We will fulfill the first and second output of the create transaction (0, 1) bec
This gives us 3 tokens to redistribute. I want to give 1 token to Carly and 2 tokens Alice.

.. code-block:: js
const txTransferDivisibleInputs = driver.Transaction.makeTransferTransaction(
txTransferDivisibleSigned,
{
metaDataMessage: 'I am specific to this transfer transaction'
},
[{ tx: txTransferDivisibleSigned, output_index: 0 }, { tx: txTransferDivisibleSigned, output_index: 1 }],
[
driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(carly.publicKey), '1'),
driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(alice.publicKey), '2')
], 0, 1)
],
{
metaDataMessage: 'I am specific to this transfer transaction'
}
);
Because we want to fulfill two outputs (Carly and Bob), we have to sign the transfer transaction in the same order:

Expand Down
37 changes: 17 additions & 20 deletions src/transaction/makeTransferTransaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,42 @@ import makeTransaction from './makeTransaction'
/**
* @public
* Generate a `TRANSFER` transaction holding the `asset`, `metadata`, and `outputs`, that fulfills
* the `fulfilledOutputs` of `unspentTransaction`.
* @param {object} unspentTransaction Previous Transaction you have control over (i.e. can fulfill
* its Output Condition)
* @param {object} metadata Metadata for the Transaction
* the `fulfilledOutputs` of each `unspentTransaction`.
* @param {object[]} unspentOutputs Array of unspent Transactions' Outputs.
* Each item contains Transaction itself
* and index of unspent Output for that Transaction.
* @param {object[]} outputs Array of Output objects to add to the Transaction.
* Think of these as the recipients of the asset after the transaction.
* For `TRANSFER` Transactions, this should usually just be a list of
* Outputs wrapping Ed25519 Conditions generated from the public keys of
* the recipients.
* @param {...number} OutputIndices Indices of the Outputs in `unspentTransaction` that this
* Transaction fulfills.
* Note that listed public keys listed must be used (and in
* the same order) to sign the Transaction
* (`signTransaction()`).
* @param {object} metadata Metadata for the Transaction - optional
* @returns {object} Unsigned transaction -- make sure to call signTransaction() on it before
* sending it off!
*/
// TODO:
// - Make `metadata` optional argument
export default function makeTransferTransaction(
unspentTransaction,
metadata,
unspentOutputs,
outputs,
...outputIndices
metadata
) {
const inputs = outputIndices.map((outputIndex) => {
const fulfilledOutput = unspentTransaction.outputs[outputIndex]
const inputs = unspentOutputs.map((unspentOutput) => {
const tx = unspentOutput.tx
const outputIndex = unspentOutput.output_index
const fulfilledOutput = tx.outputs[outputIndex]
const transactionLink = {
'output_index': outputIndex,
'transaction_id': unspentTransaction.id,
'transaction_id': tx.id,
}

return makeInputTemplate(fulfilledOutput.public_keys, transactionLink)
})

const assetLink = {
'id': unspentTransaction.operation === 'CREATE' ? unspentTransaction.id
: unspentTransaction.asset.id
'id': unspentOutputs[0].tx.operation === 'CREATE' ? unspentOutputs[0].tx.id
: unspentOutputs[0].tx.asset.id
}

return makeTransaction('TRANSFER', assetLink, metadata, outputs, inputs)
const meta = metadata || null

return makeTransaction('TRANSFER', assetLink, meta, outputs, inputs)
}
5 changes: 2 additions & 3 deletions test/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ export const createTx = Transaction.makeCreateTransaction(
alice.publicKey
)
export const transferTx = Transaction.makeTransferTransaction(
createTx,
metaData,
[{ tx: createTx, output_index: 0 }],
[aliceOutput],
0
metaData
)

export const bob = new Ed25519Keypair()
Expand Down
Loading

0 comments on commit 879ae68

Please sign in to comment.