1
0
mirror of https://github.com/bigchaindb/js-bigchaindb-driver.git synced 2024-06-28 16:47:45 +02:00

Compare commits

..

18 Commits

Author SHA1 Message Date
vrde
a9fb073072
Release 0.3.0 2017-07-05 18:16:17 +02:00
vrde
b79c8f5cd8
Update README compatibility matrix 2017-07-05 18:14:15 +02:00
vrde
75e8dac075
Merge branch 'update-travis-to-bigchaindb-1.0' 2017-07-05 18:11:16 +02:00
vrde
f8a924d214
Update BigchainDB docker container for travis to 1.0.0 2017-07-05 18:06:13 +02:00
vrde
197ece2264
Merge branch 'greenkeeper/eslint-4.1.1' 2017-07-05 17:58:21 +02:00
vrde
a49e935b8f
Fix eslint errors 2017-07-05 17:52:07 +02:00
greenkeeper[bot]
692cbe56d0
chore(package): update eslint to version 4.1.1
Closes #64
2017-07-05 17:52:07 +02:00
vrde
0462aca426 Merge pull request #72 from bigchaindb/test-combine
Test combine PRs
2017-07-05 16:38:03 +02:00
Scott Sadler
19db948726 add has subcondition to ccJsonLoad test 2017-07-05 15:39:47 +02:00
Scott Sadler
1a61cde58d add additional test for ccJsonLoad 2017-07-05 15:09:33 +02:00
vrde
769cb59c34 Merge pull request #73 from bigchaindb/greenkeeper/yarn-0.27.5
Update yarn to the latest version 🚀
2017-07-05 11:39:47 +02:00
greenkeeper[bot]
3ef181b6b9 fix(package): update yarn to version 0.27.5 2017-07-04 06:26:54 +00:00
diminator
91caf13f1e
ignore yarn.lock 2017-06-30 21:52:47 +02:00
vrde
98410ca69c Merge pull request #70 from bigchaindb/greenkeeper/ava-0.20.0
Update ava to the latest version 🚀
2017-06-30 15:19:34 +02:00
Scott Sadler
0a400550a3 Merge branch 'output-index' into combine 2017-06-30 10:02:14 +02:00
Scott Sadler
490c1e1017 fulfills.output -> fulfills.output_index 2017-06-29 11:40:29 +02:00
Scott Sadler
368c04279e point travis at bigchaindb master 2017-06-28 10:10:46 +02:00
Scott Sadler
fe08f26a43 remove signature in details and rename subfulfillments to subconditions 2017-06-28 01:25:07 +02:00
95 changed files with 6566 additions and 5179 deletions

View File

@ -1,29 +1,31 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": [
"> 0.25%, not dead",
"not IE 11",
"maintained node versions"
]
}
]
],
"plugins": [
"@babel/plugin-proposal-export-default-from",
"@babel/plugin-transform-object-assign",
"@babel/plugin-proposal-object-rest-spread",
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 3,
"helpers": true,
"regenerator": true
}
]
],
"sourceMaps": true
"presets": [
"@ava/stage-4",
"@ava/transform-test-files",
"es2015-no-commonjs"
],
"plugins": [
"transform-export-extensions",
"transform-object-assign",
"transform-object-rest-spread"
],
"sourceMaps": true,
"env": {
"bundle": {
"plugins": [
["transform-runtime", {
"polyfill": true,
"regenerator": false
}]
]
},
"cjs": {
"plugins": [
"add-module-exports",
"transform-es2015-modules-commonjs"
]
}
}
}

View File

@ -1,13 +0,0 @@
#!/bin/bash
# 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
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

View File

@ -1,13 +0,0 @@
#!/bin/bash
# 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
set -e -x
docker-compose up -d bigchaindb
npm install
gem install cowsay
npm install -g codecov

View File

@ -1,9 +0,0 @@
#!/bin/bash
# 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
set -e -x
docker-compose build --no-cache bigchaindb

View File

@ -1,6 +1,3 @@
dist
node_modules
coverage
media
docs
compose

View File

@ -1,231 +0,0 @@
module.exports = {
extends: ['eslint:recommended', 'airbnb-base', 'plugin:import/recommended'],
parser: '@babel/eslint-parser',
parserOptions: { requireConfigFile: false },
env: {
browser: true,
node: true,
},
settings: {
'import/ignore': ['node_modules', '.(scss|css)$', '.(jpe?g|png|gif|svg)'],
},
rules: {
/**
* Possible Errors
* http://eslint.org/docs/rus/#possible-errors
*/
// Allow dangling commas for multiline arrays and objects
// http://eslint.org/docs/rules/comma-dangle
'comma-dangle': [1, 'only-multiline'],
// Warn against use of console for non-error logging
// http://eslint.org/docs/rules/no-console
'no-console': [1, { allow: ['error'] }],
// Allow use of Object.prototypes builtins directly
// http://eslint.org/docs/rules/no-prototype-builtins
'no-prototype-builtins': [0],
/**
* Best Practices
* http://eslint.org/docs/rules/#best-practices
*/
// Allow else clauses after an if with a return
// http://eslint.org/docs/rules/no-else-return
'no-else-return': [0],
// Disallow reassignment of function parameters (but allow assigning to parameter's properties)
// http://eslint.org/docs/rules/no-param-reassign.html
'no-param-reassign': [2, { props: false }],
/**
* Variables
* http://eslint.org/docs/rules/#variables
*/
// Disallow use of variables and classes before they are defined
// http://eslint.org/docs/rules/no-use-before-define
'no-use-before-define': [2, { functions: false, classes: true }],
// Disallow declaration of variables that are not used in the code, unless they are prefixed by
// `ignored` (useful for creating subset objects through destructuring and rest objects)
// http://eslint.org/docs/rules/no-unused-vars
'no-unused-vars': [
2,
{
vars: 'local',
args: 'after-used',
varsIgnorePattern: 'ignored.+',
},
],
/**
* Stylelistic Issues
* (http://eslint.org/docs/rules/#stylistic-issues)
*/
// Enforce 4-space indents, except for switch cases
// http://eslint.org/docs/rules/indent
'indent': [2, 4, { SwitchCase: 1, VariableDeclarator: 1 }],
// Specify the maximum length of a code line to be 100
// http://eslint.org/docs/rules/max-len
'max-len': [
2,
{
code: 105, // Use 105 to give some leeway for *just* slightly longer lines when convienient
ignorePattern: '^(import|export) .* from .*$',
ignoreComments: false,
ignoreTrailingComments: true,
ignoreUrls: true,
},
],
// Require capitalization when using `new`, but don't require capitalized functions to be called
// with new
// http://eslint.org/docs/rules/new-cap
'new-cap': [2, { newIsCap: true, capIsNew: false }],
// Allow the continue statement
// http://eslint.org/docs/rules/no-continue
'no-continue': [0],
// Disallow un-paren'd mixes of different operators if they're not of the same precendence
// http://eslint.org/docs/rules/no-mixed-operators
'no-mixed-operators': [
2,
{
groups: [
['+', '-', '*', '/', '%', '**'],
['&', '|', '^', '~', '<<', '>>', '>>>'],
['==', '!=', '===', '!==', '>', '>=', '<', '<='],
['&&', '||'],
['in', 'instanceof'],
],
allowSamePrecedence: true,
},
],
// Allow use of unary increment/decrement operators
// http://eslint.org/docs/rules/no-plusplus
'no-plusplus': [0],
// Always allow dangling underscores
// http://eslint.org/docs/rules/no-underscore-dangle
'no-underscore-dangle': [0],
// Require unix-style line breaks
// http://eslint.org/docs/rules/linebreak-style
'linebreak-style': [2, 'unix'],
// Require operators to always be at the end of a line, except for the ternary operator
// http://eslint.org/docs/rules/operator-linebreak
'operator-linebreak': [
2,
'after',
{ overrides: { '?': 'ignore', ':': 'ignore' } },
],
// Require properties to be consistently quoted. Force numbers to be quoted, as they can have
// weird behaviour during the coercion into a string)
// http://eslint.org/docs/rules/quote-props
'quote-props': [
2,
'consistent',
{ keywords: false, unnecessary: true, numbers: true },
],
// Require spaces before parens for anonymous function declarations
// http://eslint.org/docs/rules/space-before-function-paren
'space-before-function-paren': [2, { anonymous: 'always', named: 'never' }],
// Require a space immediately following the // or /* in a comment for most comments
// http://eslint.org/docs/rules/spaced-comment
'spaced-comment': [
2,
'always',
{
line: {
exceptions: ['-', '+'],
},
block: {
exceptions: ['*'],
},
},
],
// We don't like semicolons so kill them
// http://eslint.org/docs/rules/semi
'semi': [2, 'never'],
/**
* Import rules
* https://github.com/benmosher/eslint-plugin-import#rules
*/
// Ensure named imports coupled with named exports
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/named.md#when-not-to-use-it
'import/named': 2,
// Ensure default import coupled with default export
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/default.md#when-not-to-use-it
'import/default': 2,
// Disallow namespace (wildcard) imports
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-namespace.md
'import/no-namespace': 2,
// Enforce imports to not specify a trailing .js extension
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md
'import/extensions': [2, { js: 'never' }],
// Enforce module import order: builtin -> external -> internal
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/order.md
'import/order': [
2,
{
groups: [
'builtin',
'external',
['internal', 'parent', 'sibling', 'index'],
],
},
],
'import/no-extraneous-dependencies': ['error', { 'devDependencies': true }],
/**
* ES6-specific Issues
* (http://eslint.org/docs/rules/#ecmascript-6)
*/
'arrow-body-style': [0],
'arrow-parens': [0],
'arrow-spacing': [0],
'constructor-super': [0],
'generator-star-spacing': [0],
'no-class-assign': [0],
'no-confusing-arrow': [0],
'no-const-assign': [0],
'no-dupe-class-members': [0],
'no-duplicate-imports': [0],
'no-new-symbol': [0],
'no-restricted-imports': [0],
'no-this-before-super': [0],
'no-useless-computed-key': [0],
'no-useless-constructor': [0],
'no-useless-rename': [0],
'no-var': [0],
'object-shorthand': [0],
'prefer-arrow-callback': [0],
'prefer-const': [0],
'prefer-reflect': [0],
'prefer-rest-params': [0],
'prefer-spread': [0],
'prefer-template': [0],
'require-yield': [0],
'rest-spread-spacing': [0],
'sort-imports': [0],
'template-curly-spacing': [0],
'yield-star-spacing': [0],
},
}

3
.eslintrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "ascribe"
}

View File

@ -1,68 +0,0 @@
name: CI
on:
push:
branches:
- master
paths-ignore:
- README.md
- API.md
- docs/*.rst
pull_request:
branches:
- master
types:
- ready_for_review
- opened
- reopened
- synchronize
paths-ignore:
- README.md
- API.md
- docs/*.rst
jobs:
test:
runs-on: ubuntu-latest
if: github.event_name == 'release' || github.event_name == 'push' || !github.event.pull_request.draft
timeout-minutes: 10
steps:
- name: Checkout the commit
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: 14
- name: Cache dependencies
id: cache
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Run BigChainDB node
run: |
echo Building and starting up docker containers
docker-compose -f ./docker-compose.yml up -d
- name: Install dependencies
env:
HUSKY_SKIP_INSTALL: 'true'
run: npm install
- name: Lint
run: npm run lint
- name: Build
run: npm run build
# ensure BCDB node is up and running
- run: sleep 20
- name: Test
run: npm run test

6
.gitignore vendored
View File

@ -8,11 +8,8 @@
*.sublime-project
*.sublime-workspace
*.sublime-workspace
.DS_Store
.vscode
.env
.genv
node_modules
dist
@ -21,6 +18,3 @@ coverage
coverage.lcov
.nyc_output
yarn.lock
docs/build/
docs/_build/
docs/build/

1
.husky/.gitignore vendored
View File

@ -1 +0,0 @@
_

View File

@ -1,24 +0,0 @@
*.seed
*.log
*.dat
*.out
*.pid
*.gz
.idea
*.sublime-project
*.sublime-workspace
*.sublime-workspace
.DS_Store
.vscode
.env
.genv
node_modules
package-lock.json
coverage
coverage.lcov
.nyc_output
yarn.lock
docs/build/

View File

@ -1,7 +1,3 @@
# 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
sudo: required
services:
@ -9,29 +5,25 @@ services:
language: node_js
node_js:
- 10
- 12
- 14
node_js: 7
cache:
directories:
- node_modules
env:
global:
- DOCKER_COMPOSE_VERSION=1.28.5
before_install:
- .ci/travis-before-install.sh
- 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.0.0
start
- gem install cowsay
- npm install -g codecov
install:
- .ci/travis-install.sh
before_script:
- .ci/travis-before-script.sh
script: npm test
script: yarn test
notifications:
email: false

565
API.md
View File

@ -2,540 +2,217 @@
### Table of Contents
- [Ed25519Keypair][1]
- [Parameters][2]
- [Properties][3]
- [Connection][4]
- [Parameters][5]
- [getBlock][6]
- [Parameters][7]
- [getTransaction][8]
- [Parameters][9]
- [listBlocks][10]
- [Parameters][11]
- [listOutputs][12]
- [Parameters][13]
- [listTransactions][14]
- [Parameters][15]
- [postTransaction][16]
- [Parameters][17]
- [postTransactionSync][18]
- [Parameters][19]
- [postTransactionAsync][20]
- [Parameters][21]
- [postTransactionCommit][22]
- [Parameters][23]
- [searchAssets][24]
- [Parameters][25]
- [searchMetadata][26]
- [Parameters][27]
- [Transaction][28]
- [serializeTransactionIntoCanonicalString][29]
- [Parameters][30]
- [makeCreateTransaction][31]
- [Parameters][32]
- [makeEd25519Condition][33]
- [Parameters][34]
- [makeOutput][35]
- [Parameters][36]
- [makeSha256Condition][37]
- [Parameters][38]
- [makeThresholdCondition][39]
- [Parameters][40]
- [makeTransferTransaction][41]
- [Parameters][42]
- [signTransaction][43]
- [Parameters][44]
- [delegateSignTransaction][45]
- [Parameters][46]
- [delegateSignTransactionAsync][47]
- [Parameters][48]
- [ccJsonLoad][49]
- [Parameters][50]
- [ccJsonify][51]
- [Parameters][52]
- [Ed25519Keypair](#ed25519keypair)
- [makeEd25519Condition](#makeed25519condition)
- [makeSha256Condition](#makesha256condition)
- [makeThresholdCondition](#makethresholdcondition)
- [makeCreateTransaction](#makecreatetransaction)
- [makeOutput](#makeoutput)
- [makeTransferTransaction](#maketransfertransaction)
- [serializeTransactionIntoCanonicalString](#serializetransactionintocanonicalstring)
- [signTransaction](#signtransaction)
- [ccJsonLoad](#ccjsonload)
- [ccJsonify](#ccjsonify)
- [getBlock](#getblock)
- [getStatus](#getstatus)
- [getTransaction](#gettransaction)
- [listBlocks](#listblocks)
- [listOutputs](#listoutputs)
- [listTransactions](#listtransactions)
- [listVotes](#listvotes)
- [pollStatusAndFetchTransaction](#pollstatusandfetchtransaction)
- [postTransaction](#posttransaction)
## Ed25519Keypair
[src/Ed25519Keypair.js:16-21][53]
**Parameters**
Type: [Object][54]
- `secret` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** A seed that will be used as a key derivation function
### Parameters
**Properties**
- `seed` **[Buffer][55]?** A seed that will be used as a key derivation function
- `publicKey` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)**
- `privateKey` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)**
### Properties
## makeEd25519Condition
- `publicKey` **[string][56]**
- `privateKey` **[string][56]**
**Parameters**
## Connection
- `publicKey` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** base58 encoded Ed25519 public key for the recipient of the Transaction
- `json` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** If true returns a json object otherwise a crypto-condition type (optional, default `true`)
[src/connection.js:21-199][57]
Returns **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Ed25519 Condition (that will need to wrapped in an Output)
### Parameters
## makeSha256Condition
- `nodes`
- `headers` **[Object][54]** Common headers for every request (optional, default `{}`)
- `timeout` **float** Optional timeout in secs (optional, default `DEFAULT_TIMEOUT`)
**Parameters**
### getBlock
- `preimage` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Preimage to be hashed and wrapped in a crypto-condition
- `json` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** If true returns a json object otherwise a crypto-condition type (optional, default `true`)
[src/connection.js:79-85][58]
Returns **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Preimage-Sha256 Condition (that will need to wrapped in an Output)
#### Parameters
## makeThresholdCondition
- `blockHeight`
**Parameters**
### getTransaction
- `threshold` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)**
- `subconditions` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** (optional, default `[]`)
- `json` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** If true returns a json object otherwise a crypto-condition type (optional, default `true`)
[src/connection.js:90-96][59]
Returns **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Sha256 Threshold Condition (that will need to wrapped in an Output)
#### Parameters
## makeCreateTransaction
- `transactionId`
**Parameters**
### listBlocks
[src/connection.js:102-108][60]
#### Parameters
- `transactionId`
- `status`
### listOutputs
[src/connection.js:114-126][61]
#### Parameters
- `publicKey`
- `spent`
### listTransactions
[src/connection.js:132-139][62]
#### Parameters
- `assetId`
- `operation`
### postTransaction
[src/connection.js:144-146][63]
#### Parameters
- `transaction`
### postTransactionSync
[src/connection.js:151-156][64]
#### Parameters
- `transaction`
### postTransactionAsync
[src/connection.js:161-166][65]
#### Parameters
- `transaction`
### postTransactionCommit
[src/connection.js:171-176][66]
#### Parameters
- `transaction`
### searchAssets
[src/connection.js:181-187][67]
#### Parameters
- `search`
### searchMetadata
[src/connection.js:192-198][68]
#### Parameters
- `search`
## Transaction
[src/transaction.js:16-288][69]
Construct Transactions
### serializeTransactionIntoCanonicalString
[src/transaction.js:22-29][70]
Canonically serializes a transaction into a string by sorting the keys
#### Parameters
- `transaction`
- `null` **[Object][54]** (transaction)
Returns **[string][56]** a canonically serialized Transaction
### makeCreateTransaction
[src/transaction.js:80-87][71]
Generate a `CREATE` transaction holding the `asset`, `metadata`, and `outputs`, to be signed by
the `issuers`.
#### Parameters
- `asset` **[Object][54]** Created asset's data
- `metadata` **[Object][54]** Metadata for the Transaction
- `outputs` **[Array][72]&lt;[Object][54]>** Array of Output objects to add to the Transaction.
- `asset` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Created asset's data
- `metadata` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Metadata for the Transaction
- `outputs` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)>** Array of Output objects to add to the Transaction.
Think of these as the recipients of the asset after the transaction.
For `CREATE` Transactions, this should usually just be a list of
Outputs wrapping Ed25519 Conditions generated from the issuers' public
keys (so that the issuers are the recipients of the created asset).
- `issuers` **...[Array][72]&lt;[string][56]>** Public key of one or more issuers to the asset being created by this
- `issuers` **...[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)>** Public key of one or more issuers to the asset being created by this
Transaction.
Note: Each of the private keys corresponding to the given public
keys MUST be used later (and in the same order) when signing the
Transaction (`signTransaction()`).
Returns **[Object][54]** Unsigned transaction -- make sure to call signTransaction() on it before
Returns **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Unsigned transaction -- make sure to call signTransaction() on it before
sending it off!
### makeEd25519Condition
## makeOutput
[src/transaction.js:96-101][73]
**Parameters**
Create an Ed25519 Cryptocondition from an Ed25519 public key
to put into an Output of a Transaction
- `condition` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Condition (e.g. a Ed25519 Condition from `makeEd25519Condition()`)
- `amount` **[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** Amount of the output (optional, default `1`)
#### Parameters
Returns **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** An Output usable in a Transaction
- `publicKey` **[string][56]** base58 encoded Ed25519 public key for the recipient of the Transaction
- `json` **[boolean][74]** If true returns a json object otherwise a crypto-condition type (optional, default `true`)
## makeTransferTransaction
Returns **[Object][54]** Ed25519 Condition (that will need to wrapped in an Output)
**Parameters**
### makeOutput
[src/transaction.js:111-131][75]
Create an Output from a Condition.
Note: Assumes the given Condition was generated from a
single public key (e.g. a Ed25519 Condition)
#### Parameters
- `condition` **[Object][54]** Condition (e.g. a Ed25519 Condition from `makeEd25519Condition()`)
- `amount` **[string][56]** Amount of the output (optional, default `'1'`)
Returns **[Object][54]** An Output usable in a Transaction
### makeSha256Condition
[src/transaction.js:139-143][76]
Create a Preimage-Sha256 Cryptocondition from a secret to put into an Output of a Transaction
#### Parameters
- `preimage` **[string][56]** Preimage to be hashed and wrapped in a crypto-condition
- `json` **[boolean][74]** If true returns a json object otherwise a crypto-condition type (optional, default `true`)
Returns **[Object][54]** Preimage-Sha256 Condition (that will need to wrapped in an Output)
### makeThresholdCondition
[src/transaction.js:152-162][77]
Create an Sha256 Threshold Cryptocondition from threshold to put into an Output of a Transaction
#### Parameters
- `threshold` **[number][78]**
- `subconditions` **[Array][72]** (optional, default `[]`)
- `json` **[boolean][74]** If true returns a json object otherwise a crypto-condition type (optional, default `true`)
Returns **[Object][54]** Sha256 Threshold Condition (that will need to wrapped in an Output)
### makeTransferTransaction
[src/transaction.js:185-206][79]
Generate a `TRANSFER` transaction holding the `asset`, `metadata`, and `outputs`, that fulfills
the `fulfilledOutputs` of `unspentTransaction`.
#### Parameters
- `unspentOutputs`
- `outputs` **[Array][72]&lt;[Object][54]>** Array of Output objects to add to the Transaction.
- `unspentTransaction` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Previous Transaction you have control over (i.e. can fulfill
its Output Condition)
- `metadata` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Metadata for the Transaction
- `outputs` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)&lt;[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)>** 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.
- `metadata` **[Object][54]** Metadata for the Transaction
- `unspentTransaction` **[Object][54]** Previous Transaction you have control over (i.e. can fulfill
its Output Condition)
- `OutputIndices` **...[number][78]** Indices of the Outputs in `unspentTransaction` that this
- `fulfilledOutputs` **...[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** 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
Note that the public keys listed in the fulfilled Outputs
must be used (and in the same order) to sign the Transaction
(`signTransaction()`).
Returns **[Object][54]** Unsigned transaction -- make sure to call signTransaction() on it before
Returns **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Unsigned transaction -- make sure to call signTransaction() on it before
sending it off!
### signTransaction
## serializeTransactionIntoCanonicalString
[src/transaction.js:219-243][80]
**Parameters**
Sign the given `transaction` with the given `privateKey`s, returning a new copy of `transaction`
that's been signed.
Note: Only generates Ed25519 Fulfillments. Thresholds and other types of Fulfillments are left as
an exercise for the user.
- `transaction`
- `null` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** (transaction)
#### Parameters
Returns **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** a canonically serialized Transaction
- `transaction` **[Object][54]** Transaction to sign. `transaction` is not modified.
- `privateKeys` **...[string][56]** Private keys associated with the issuers of the `transaction`.
## signTransaction
**Parameters**
- `transaction` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Transaction to sign. `transaction` is not modified.
- `privateKeys` **...[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Private keys associated with the issuers of the `transaction`.
Looped through to iteratively sign any Input Fulfillments found in
the `transaction`.
Returns **[Object][54]** The signed version of `transaction`.
### delegateSignTransaction
[src/transaction.js:252-265][81]
Delegate signing of the given `transaction` returning a new copy of `transaction`
that's been signed.
#### Parameters
- `transaction` **[Object][54]** Transaction to sign. `transaction` is not modified.
- `signFn` **[Function][82]** Function signing the transaction, expected to return the fulfillment.
Returns **[Object][54]** The signed version of `transaction`.
### delegateSignTransactionAsync
[src/transaction.js:274-287][83]
Delegate signing of the given `transaction` returning a new copy of `transaction`
that's been signed.
#### Parameters
- `transaction` **[Object][54]** Transaction to sign. `transaction` is not modified.
- `signFn` **[Function][82]** Function signing the transaction, expected to resolve the fulfillment.
Returns **[Promise][84]&lt;[Object][54]>** The signed version of `transaction`.
Returns **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** The signed version of `transaction`.
## ccJsonLoad
[src/utils/ccJsonLoad.js:13-44][85]
**Parameters**
Loads a crypto-condition class (Fulfillment or Condition) from a BigchainDB JSON object
### Parameters
- `conditionJson` **[Object][54]**
- `conditionJson` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
Returns **cc.Condition** Ed25519 Condition (that will need to wrapped in an Output)
## ccJsonify
[src/utils/ccJsonify.js:12-65][86]
**Parameters**
Serializes a crypto-condition class (Condition or Fulfillment) into a BigchainDB-compatible JSON
- `fulfillment` **cc.Fulfillment** base58 encoded Ed25519 public key for the recipient of the Transaction
### Parameters
Returns **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Ed25519 Condition (that will need to wrapped in an Output)
- `fulfillment` **cc.Fulfillment** base58 encoded Ed25519 public key for recipient of the Transaction
## getBlock
Returns **[Object][54]** Ed25519 Condition (that will need to wrapped in an Output)
**Parameters**
[1]: #ed25519keypair
- `blockId`
[2]: #parameters
## getStatus
[3]: #properties
**Parameters**
[4]: #connection
- `tx_id`
[5]: #parameters-1
## getTransaction
[6]: #getblock
**Parameters**
[7]: #parameters-2
- `txId`
[8]: #gettransaction
## listBlocks
[9]: #parameters-3
**Parameters**
[10]: #listblocks
- `$0` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
- `$0.tx_id`
- `$0.status`
- `tx_id`
- `status`
[11]: #parameters-4
## listOutputs
[12]: #listoutputs
**Parameters**
[13]: #parameters-5
- `$0` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
- `$0.public_key`
- `$0.unspent`
- `onlyJsonResponse`
- `public_key`
- `unspent`
[14]: #listtransactions
## listTransactions
[15]: #parameters-6
**Parameters**
[16]: #posttransaction
- `$0` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
- `$0.asset_id`
- `$0.operation`
- `asset_id`
- `operation`
[17]: #parameters-7
## listVotes
[18]: #posttransactionsync
**Parameters**
[19]: #parameters-8
- `block_id`
[20]: #posttransactionasync
## pollStatusAndFetchTransaction
[21]: #parameters-9
**Parameters**
[22]: #posttransactioncommit
- `tx_id`
[23]: #parameters-10
Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)**
[24]: #searchassets
## postTransaction
[25]: #parameters-11
**Parameters**
[26]: #searchmetadata
[27]: #parameters-12
[28]: #transaction
[29]: #serializetransactionintocanonicalstring
[30]: #parameters-13
[31]: #makecreatetransaction
[32]: #parameters-14
[33]: #makeed25519condition
[34]: #parameters-15
[35]: #makeoutput
[36]: #parameters-16
[37]: #makesha256condition
[38]: #parameters-17
[39]: #makethresholdcondition
[40]: #parameters-18
[41]: #maketransfertransaction
[42]: #parameters-19
[43]: #signtransaction
[44]: #parameters-20
[45]: #delegatesigntransaction
[46]: #parameters-21
[47]: #delegatesigntransactionasync
[48]: #parameters-22
[49]: #ccjsonload
[50]: #parameters-23
[51]: #ccjsonify
[52]: #parameters-24
[53]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/Ed25519Keypair.js#L16-L21 "Source code on GitHub"
[54]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[55]: https://nodejs.org/api/buffer.html
[56]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[57]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L21-L199 "Source code on GitHub"
[58]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L79-L85 "Source code on GitHub"
[59]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L90-L96 "Source code on GitHub"
[60]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L102-L108 "Source code on GitHub"
[61]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L114-L126 "Source code on GitHub"
[62]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L132-L139 "Source code on GitHub"
[63]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L144-L146 "Source code on GitHub"
[64]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L151-L156 "Source code on GitHub"
[65]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L161-L166 "Source code on GitHub"
[66]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L171-L176 "Source code on GitHub"
[67]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L181-L187 "Source code on GitHub"
[68]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/connection.js#L192-L198 "Source code on GitHub"
[69]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L16-L288 "Source code on GitHub"
[70]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L22-L29 "Source code on GitHub"
[71]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L80-L87 "Source code on GitHub"
[72]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[73]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L96-L101 "Source code on GitHub"
[74]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[75]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L111-L131 "Source code on GitHub"
[76]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L139-L143 "Source code on GitHub"
[77]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L152-L162 "Source code on GitHub"
[78]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[79]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L185-L206 "Source code on GitHub"
[80]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L219-L243 "Source code on GitHub"
[81]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L252-L265 "Source code on GitHub"
[82]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[83]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/transaction.js#L274-L287 "Source code on GitHub"
[84]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
[85]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/utils/ccJsonLoad.js#L13-L44 "Source code on GitHub"
[86]: https://github.com/bigchaindb/js-bigchaindb-driver/blob/76c877c649801f0a26351d03237e0b59c18bd3ef/src/utils/ccJsonify.js#L12-L65 "Source code on GitHub"
- `transaction`

View File

@ -1,7 +1,46 @@
<!---
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
--->
# Contributor Code of Conduct
See [https://github.com/bigchaindb/bigchaindb/blob/master/CODE_OF_CONDUCT.md](https://github.com/bigchaindb/bigchaindb/blob/master/CODE_OF_CONDUCT.md)
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, sexual identity and orientation, or species—no picking on Wrigley for being a buffalo!
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at conduct@bigchaindb.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -1,8 +1,3 @@
.. 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
.. highlight:: shell
============

View File

@ -1,4 +0,0 @@
The official BigchainDB documentation, _except for the short code snippets
embedded within it_, is licensed under a Creative Commons Attribution-
ShareAlike 4.0 International license, the full text of which can be found
at http://creativecommons.org/licenses/by-sa/4.0/legalcode

123
README.md
View File

@ -1,72 +1,42 @@
<!---
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
--->
# [![js-bigchaindb-driver](media/repo-banner@2x.png)](https://www.bigchaindb.com)
> Official JavaScript driver for [BigchainDB](https://github.com/bigchaindb/bigchaindb) to create transactions in Node.js and the browser.
> Official JavaScript driver for [BigchainDB](https://github.com/bigchaindb/bigchaindb) with some naive helpers to get you on your way making transactions in Node.js and the browser.
[![Join the chat at https://gitter.im/bigchaindb/js-bigchaindb-driver](https://badges.gitter.im/bigchaindb/js-bigchaindb-driver.svg)](https://gitter.im/bigchaindb/js-bigchaindb-driver?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![npm](https://img.shields.io/npm/v/bigchaindb-driver.svg)](https://www.npmjs.com/package/bigchaindb-driver)
[![codecov](https://codecov.io/gh/bigchaindb/js-bigchaindb-driver/branch/master/graph/badge.svg)](https://codecov.io/gh/bigchaindb/js-bigchaindb-driver)
[![js ascribe](https://img.shields.io/badge/js-ascribe-39BA91.svg)](https://github.com/ascribe/javascript)
[![Build Status](https://travis-ci.com/bigchaindb/js-bigchaindb-driver.svg?branch=master)](https://travis-ci.com/bigchaindb/js-bigchaindb-driver)
[![Build Status](https://travis-ci.org/bigchaindb/js-bigchaindb-driver.svg?branch=master)](https://travis-ci.org/bigchaindb/js-bigchaindb-driver)
[![Greenkeeper badge](https://badges.greenkeeper.io/bigchaindb/js-bigchaindb-driver.svg)](https://greenkeeper.io/)
- [Main Documentation](https://docs.bigchaindb.com/projects/js-driver/en/latest/usage.html)
- [Driver API reference](API.md)
## Compatibility
| BigchainDB Server | BigchainDB JavaScript Driver |
| ----------------- |------------------------------|
| `0.10` | `0.1.x` |
| `1.0.0` | `0.3.x` |
| `1.3.x` | `3.x.x` |
| `>= 2.0.0` | `4.x.x` |
| `1.0` | `0.3.x` |
## Breaking changes
- **Version 4.0** of BigchainDB JavaScript Driver makes the driver compatible with BigchainDB 2.0. There are new functions for sending off transactions along with other changes. Check [older versions](https://docs.bigchaindb.com/projects/js-driver/en/latest/readme.html#features)
- **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
## Table of Contents
* [Installation and Usage with package managers (npm/yarn)](#installation-and-usage-with-package-managers-npmyarn)
* [Example: Create a transaction](#example-create-a-transaction)
* [Use a pre-built image (browser only)](#use-a-pre-built-image-browser-only)
* [Documentation](#bigchaindb-documentation)
* [Authors](#authors)
* [License](#license)
- [Installation and Usage](#installation-and-usage)
- [Example: Create a transaction](#example-create-a-transaction)
- [Browser usage](#browser-usage)
- [BigchainDB Documentation](#bigchaindb-documentation)
- [Speed Optimizations](#speed-optimizations)
- [Development](#development)
- [Release Process](#release-process)
- [Authors](#authors)
- [Licenses](#licenses)
---
## Installation and Usage
## Installation and Usage with package managers (npm/yarn)
```bash
npm install bigchaindb-driver
```
```js
const driver = require('bigchaindb-driver')
// or ES6+
import driver from 'bigchaindb-driver'
```
### Example: Create a transaction
```js
const driver = require('bigchaindb-driver')
const base58 = require('bs58');
const crypto = require('crypto');
const { Ed25519Sha256 } = require('crypto-conditions');
// BigchainDB server instance (e.g. https://example.com/api/v1/)
// BigchainDB server instance or IPDB (e.g. https://test.ipdb.io/api/v1/)
const API_PATH = 'http://localhost:9984/api/v1/'
// Create a new keypair.
@ -92,30 +62,15 @@ const tx = driver.Transaction.makeCreateTransaction(
// Sign the transaction with private keys
const txSigned = driver.Transaction.signTransaction(tx, alice.privateKey)
// Or use delegateSignTransaction to provide your own signature function
function signTransaction() {
// get privateKey from somewhere
const privateKeyBuffer = Buffer.from(base58.decode(alice.privateKey))
return function sign(serializedTransaction, input, index) {
const transactionUniqueFulfillment = input.fulfills ? serializedTransaction
.concat(input.fulfills.transaction_id)
.concat(input.fulfills.output_index) : serializedTransaction
const transactionHash = crypto.createHash('sha3-256').update(transactionUniqueFulfillment).digest()
const ed25519Fulfillment = new Ed25519Sha256();
ed25519Fulfillment.sign(transactionHash, privateKeyBuffer);
return ed25519Fulfillment.serializeUri();
};
}
const txSigned = driver.Transaction.delegateSignTransaction(tx, signTransaction())
// Send the transaction off to BigchainDB
const conn = new driver.Connection(API_PATH)
conn.postTransactionCommit(txSigned)
conn.postTransaction(txSigned)
.then(() => conn.pollStatusAndFetchTransaction(txSigned.id))
.then(retrievedTx => console.log('Transaction', retrievedTx.id, 'successfully posted.'))
```
### Browser usage
## Use a pre-built image (browser only)
```html
<!DOCTYPE html>
@ -124,10 +79,10 @@ conn.postTransactionCommit(txSigned)
<meta charset="utf-8">
<title>BigchainDB boilerplate</title>
<!-- Adjust version to your needs -->
<script src="https://unpkg.com/bigchaindb-driver@4.2.0/dist/browser/bigchaindb-driver.window.min.js"></script>
<script src="https://unpkg.com/bigchaindb-driver@0.2.0/dist/browser/bigchaindb-driver.window.min.js"></script>
<script>
// BigchainDB server instance (e.g. https://example.com/api/v1/)
// BigchainDB server instance or IPDB (e.g. https://test.ipdb.io/api/v1/)
const API_PATH = 'http://localhost:9984/api/v1/'
// Create a new keypair.
@ -156,7 +111,8 @@ conn.postTransactionCommit(txSigned)
// Send the transaction off to BigchainDB
let conn = new BigchainDB.Connection(API_PATH)
conn.postTransactionCommit(txSigned)
conn.postTransaction(txSigned)
.then(() => conn.pollStatusAndFetchTransaction(txSigned.id))
.then(res => {
const elem = document.getElementById('lastTransaction')
elem.href = API_PATH + 'transactions/' + txSigned.id
@ -175,7 +131,6 @@ conn.postTransactionCommit(txSigned)
## BigchainDB Documentation
- [The Hitchhiker's Guide to BigchainDB](https://www.bigchaindb.com/developers/guide/)
- [HTTP API Reference](https://docs.bigchaindb.com/projects/server/en/latest/http-client-server-api.html)
- [The Transaction Model](https://docs.bigchaindb.com/projects/server/en/latest/data-models/transaction-model.html?highlight=crypto%20conditions)
- [Inputs and Outputs](https://docs.bigchaindb.com/projects/server/en/latest/data-models/inputs-outputs.html)
@ -189,32 +144,26 @@ This implementation plays "safe" by using JS-native (or downgradable) libraries
* [chloride](https://github.com/dominictarr/chloride), or its underlying [sodium](https://github.com/paixaop/node-sodium) library
* [node-sha3](https://github.com/phusion/node-sha3) -- **MAKE SURE** to use [steakknife's fork](https://github.com/steakknife/node-sha3) if [the FIPS 202 upgrade](https://github.com/phusion/node-sha3/pull/25) hasn't been merged (otherwise, you'll run into all kinds of hashing problems)
## Development
```js
git clone git@github.com:bigchaindb/js-bigchaindb-driver.git
cd js-bigchaindb-driver/
npm i
npm run dev
```
After updating source files in `src/`, make sure to update the API documentation. The following command will scan all source files and create the Markdown output into `./API.md`:
```bash
npm run doc
```
## Release Process
See the file named [RELEASE_PROCESS.md](RELEASE_PROCESS.md).
## Authors
* inspired by [`js-bigchaindb-quickstart`](https://github.com/sohkai/js-bigchaindb-quickstart) of @sohkhai [thanks]
* BigchainDB <contact@ipdb.global>
* BigchainDB contributors
* BigchainDB <dev@bigchaindb.com>
## Licenses
## License
See [LICENSE](LICENSE) and [LICENSE-docs](LICENSE-docs).
```
Copyright 2017 BigchainDB GmbH
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```

View File

@ -1,91 +0,0 @@
<!---
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
--->
# Our Release Process
## Notes
BigchainDB follows
[the Python form of Semantic Versioning](https://packaging.python.org/tutorials/distributing-packages/#choosing-a-versioning-scheme)
(i.e. MAJOR.MINOR.PATCH),
which is almost identical
to [regular semantic versioning](http://semver.org/), but there's no hyphen, e.g.
- `0.9.0` for a typical final release
- `4.5.2a1` not `4.5.2-a1` for the first Alpha release
- `3.4.5rc2` not `3.4.5-rc2` for Release Candidate 2
**Note:** For Git tags (which are used to identify releases on GitHub), we append a `v` in front.
We follow [BEP-1](https://github.com/bigchaindb/BEPs/tree/master/1), which is our variant of C4, the Collective Code Construction Contract, so a release is just a [tagged commit](https://git-scm.com/book/en/v2/Git-Basics-Tagging) on the `master` branch, i.e. a label for a particular Git commit.
## Steps
1. Make sure you have a recent version of node and npm.
1. `npm install`
1. Update all npm package dependencies, where possible. You might have to freeze some versions. Run all tests locally (`npm run test`) and make sure they pass. Make a pull request (to be merged into the `master` branch) and make sure all tests are passing there (in Travis). Merge the pull request.
1. Make sure your local `master` branch is in sync with GitHub: `git checkout master` and `git pull`
1. Do a test build:
`npm run build`
If that fails, then get it working.
1. We use the [release-it](https://www.npmjs.com/package/release-it) package (from npm) to automate most of the release. Make sure you have a recent version.
1. Login to npm using your npm credentials, so you can publish a new [bigchaindb-driver](https://www.npmjs.com/package/bigchaindb-driver) package there. (The npm account must have permission to do so).
`npm login`
1. release-it needs a Github personal access token so it can interact with GitHub on your behalf. To get one, go to:
[https://github.com/settings/tokens](https://github.com/settings/tokens)
and then make that token available as an environment variable, e.g.
`export GITHUB_TOKEN="f941e0..."`
1. Do the release:
- For a patch release, do `npm run release`
- For a minor release, do `npm run release-minor`
- For a major release, do `npm run release-major`
If your npm account is using two-factor authentication,
you will have to append a one-time password (OTP) like `--npm.otp=123456`.
The above command will automatically do a bunch of things:
- bump the project version in `package.json`, then git commit and git push it.
- create a new Git tag of the form `v{verson}`, e.g. `v1.2.3`
- create a new [GitHub release](https://github.com/bigchaindb/js-bigchaindb-driver/releases).
- publish a new npm release
To see all the arguments passed to `release-it`, search for "release" in [package.json](package.json). The arguments are documented in the [release-it GitHub repo](https://github.com/release-it/release-it).
1. Make sure everything worked as expected.
- Was the version number bumped properly in [package.json](package.json)?
- Was a new Git tag created? See the [list of tags](https://github.com/bigchaindb/js-bigchaindb-driver/tags).
- Was a new GitHub release created? See the [list of releases](https://github.com/bigchaindb/js-bigchaindb-driver/releases).
- Was a new npm package published on npm? [Check on npmjs.com](https://www.npmjs.com/package/bigchaindb-driver).
1. You can edit the description of the GitHub release to add or remove details.
If the docs were updated since the last release, [login to readthedocs.org](https://readthedocs.org/accounts/login/) and go to the **BigchainDB JavaScript Driver** project, then:
1. Click on "Builds", select "latest" from the drop-down menu, then click the "Build Version:" button.
1. Wait for the build of "latest" to finish. This can take a few minutes.
1. Go to Admin --> Advanced Settings
and make sure that "Default branch:" (i.e. what "latest" points to)
is set to the new release's tag, e.g. `v0.9.1`.
(It won't be an option if you didn't wait for the build of "latest" to finish.)
Then scroll to the bottom and click "Save".
1. Go to Admin --> Versions
and under **Choose Active Versions**, do these things:
1. Make sure that the new version's tag is "Active" and "Public"
1. Make sure the **stable** branch is _not_ active.
1. Scroll to the bottom of the page and click "Save".
Congratulations, you have released a new version of the BigchainDB JavaScript Driver!

View File

@ -1,12 +0,0 @@
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

@ -1,17 +0,0 @@
# 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"

View File

@ -1,43 +0,0 @@
# 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
version: '2.1'
services:
mongodb:
image: mongo:3.6
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: 26657
ports:
- "9984:9984"
- "9985:9985"
- "26658"
healthcheck:
test: ["CMD", "bash", "-c", "curl http://bigchaindb:9984 && curl http://tendermint:26657/abci_query"]
interval: 3s
timeout: 5s
retries: 3
command: -l DEBUG start
tendermint:
image: tendermint/tendermint:v0.31.5
# volumes:
# - ./tmdata:/tendermint
entrypoint: ''
ports:
- "26656"
- "26657"
command: sh -c "tendermint init && tendermint node --consensus.create_empty_blocks=false --proxy_app=tcp://bigchaindb:26658"

View File

@ -1,32 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = BigchainDBJavascriptDriver
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
.PHONY: clean
clean:
rm -rf $(BUILDDIR)/*
@echo
@echo "Removed $(BUILDDIR)/html."
.PHONY: html
html:
$(SPHINXBUILD) -b html $(SOURCEDIR) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -1,8 +0,0 @@
<!---
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
--->
# BigchainDBJavaScriptDriverDocs
BigchainDB JavaScript Driver Documentation with Sphinx

View File

@ -1,36 +0,0 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=python -msphinx
)
set SOURCEDIR=source
set BUILDDIR=build
set SPHINXPROJ=BigchainDBJavascriptDriver
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The Sphinx module was not found. Make sure you have Sphinx installed,
echo.then set the SPHINXBUILD environment variable to point to the full
echo.path of the 'sphinx-build' executable. Alternatively you may add the
echo.Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd

View File

@ -1,3 +0,0 @@
Sphinx~=1.0
recommonmark>=0.4.0
sphinx-rtd-theme>=0.2.4

Binary file not shown.

View File

@ -1,87 +0,0 @@
.. 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
=================
Advanced Examples
=================
Crypto Conditions
-----------------
Let's start with a basic use case example. Alice bought a bicycle for €240.
She will use the bike for a year and will give it to her daughter afterwards.
First, we create an asset registering the bicycle:
.. code-block:: js
const txCreateAliceSimple = driver.Transaction.makeCreateTransaction(
{'asset': 'bicycle'},
{'purchase_price': '€240'},
[
driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(alice.publicKey))
],
alice.publicKey
)
const txCreateAliceSimpleSigned = driver.Transaction.signTransaction(txCreateAliceSimple, alice.privateKey)
After a year, she decides it's time to transfer the bicycle to her daughter Carly.
However, Alice wants to maintain the right over the bike so she can possibly sell it. If she would transfer the bicycle to Carly, she won't be able to do this.
So, Alice needs a crypto conditions that defines that she or her daughter can sign the ``TRANSFER`` transaction to a possible buyer.
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.
.. 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)
// Create condition object with threshold and subconditions
let condition = driver.Transaction.makeThresholdCondition(1, [subConditionFrom, subConditionTo])
// Generate output with condition added
let output = driver.Transaction.makeOutput(condition)
// Add Carly to the output.public_keys field so she is the owner
output.public_keys = [carly.publicKey]
let transaction = driver.Transaction.makeTransferTransaction(
[{ 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]
// Because the addition of crypto conditions, the id for the transaction has to be regenerated
delete transaction.id
transaction.id = sha3.sha3_256
.create()
.update(driver.Transaction.serializeTransactionIntoCanonicalString(transaction))
.hex()
// Alice has to sign this transfer because she is still the owner of the created asset
let signedCryptoConditionTx = driver.Transaction.signTransaction(transaction, alice.privateKey)
As you can see, we need to generate a new transactionId because we have added crypto conditions.
We do this with the js-sha3 package, you need to install this package through ``npm``:
``npm install --save js-sha3``
Don't forget to import the package in your code:
.. code-block:: js
import * as sha3 from 'js-sha3'
If you would like to see a more complex example, please have a look [here](https://github.com/bigchaindb/project-jannowitz/blob/code-examples/js-examples/crypto-conditions.js)
.. TODO: Document Utils when finished

View File

@ -1,189 +0,0 @@
#!/usr/bin/env python3
# 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
#
# BigchainDB Javascript Driver documentation build configuration file, created by
# sphinx-quickstart on Wed Aug 2 15:39:03 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
import datetime
import sphinx_rtd_theme
from recommonmark.parser import CommonMarkParser
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['ntemplates']
source_parsers = {
'.md': CommonMarkParser,
}
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = ['.rst', '.md']
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'BigchainDB Javascript Driver'
now = datetime.datetime.now()
copyright = str(now.year) + ', BigchainDB Contributors'
author = 'BigchainDB Contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.0.1'
# The full version, including alpha/beta/rc tags.
release = '0.0.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['nstatic']
# Commented out this option because Sphinx can not find the path
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = {
'**': [
'about.html',
'navigation.html',
'relations.html', # needs 'show_related': True theme option to display
'searchbox.html',
'donate.html',
]
}
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'BigchainDBJavascriptDriverdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'BigchainDBJavascriptDriver.tex', 'BigchainDB Javascript Driver Documentation',
'BigchainDB', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'bigchaindbjavascriptdriver', 'BigchainDB Javascript Driver Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'BigchainDBJavascriptDriver', 'BigchainDB Javascript Driver Documentation',
author, 'BigchainDBJavascriptDriver', 'One line description of project.',
'Miscellaneous'),
]

View File

@ -1,23 +0,0 @@
.. 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
BigchainDB Javascript Driver Documentation
==========================================
.. toctree::
:maxdepth: 2
← Back to All BigchainDB Docs <https://bigchaindb.readthedocs.io/en/latest/index.html>
readme
quickstart
usage
advanced
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,14 +0,0 @@
.. 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
=========================
Quickstart / Installation
=========================
Installation with package manager npm:
.. code-block:: bash
$ npm install bigchaindb-driver

View File

@ -1,120 +0,0 @@
.. 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
BigchainDB JavaScript Driver
============================
.. image:: https://img.shields.io/npm/v/bigchaindb-driver.svg
:target: https://www.npmjs.com/package/bigchaindb-driver
.. image:: https://codecov.io/gh/bigchaindb/js-bigchaindb-driver/branch/master/graph/badge.svg
:target: https://codecov.io/gh/bigchaindb/js-bigchaindb-driver
.. image:: https://img.shields.io/badge/js-ascribe-39BA91.svg
:target: https://github.com/ascribe/javascript
.. image:: https://travis-ci.com/bigchaindb/js-bigchaindb-driver.svg?branch=master
:target: https://travis-ci.com/bigchaindb/js-bigchaindb-driver
.. image:: https://badges.greenkeeper.io/bigchaindb/js-bigchaindb-driver.svg
:target: https://greenkeeper.io/
Features
--------
* Support for preparing, fulfilling, and sending transactions to a BigchainDB
node.
* Retrieval of transactions by id.
* Getting status of a transaction by id.
Compatibility Matrix
--------------------
+-----------------------+----------------------------------+
| **BigchainDB Server** | **BigchainDB Javascript Driver** |
+=======================+==================================+
| ``0.10`` | ``0.1.x`` |
+-----------------------+----------------------------------+
| ``1.0`` | ``0.3.x`` |
+-----------------------+----------------------------------+
| ``1.3`` | ``3.x.x`` |
+-----------------------+----------------------------------+
| ``2.0`` | ``4.x.x`` |
+-----------------------+----------------------------------+
Older versions
--------------------
**Version 4.x.x**
As part of the changes in the BigchainDB 2.0 server, some endpoints 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:
- `commit` using the `postTransaction` or the `postTransactionCommit`: the response will return after the transaction is committed to a block.
- `sync` using the `postTransactionSync`: the response will return after the transaction is validated.
- `async` using the `postTransactionAsync`: the response will return immediately and not wait to see if the transaction is valid.
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 do any other action with the asset immediately.
Note: In order to not create breaking changes, both methods `postTransaction` and `postTransactionCommit` are kept although
they do exactly the same
**Version 3.2.x**
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 }],
[BigchainDB.Transaction.makeOutput(
BigchainDB.Transaction.makeEd25519Condition(alice.publicKey))],
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}],
[BigchainDB.Transaction.makeOutput(
BigchainDB.Transaction.makeEd25519Condition(alice.publicKey))],
metaData
)
const signedTransfer = BigchainDB.Transaction.signTransaction(createTranfer,
keypair.privateKey)

View File

@ -1,795 +0,0 @@
.. 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
====================
Basic Usage Examples
====================
For the examples on this page, we assume you've
:doc:`installed the bigchaindb_driver JavaScript package <quickstart>`,
and you have determined the BigchainDB Root URL (issue: move this to general docs)
of the node or cluster you want to connect to.
This example guides you through creating and transferring an asset.
We walk through the code explaining its use, some pieces are left out
because they have no real use (e.g. definition of global variable)
*Full working code* can be found at the bottom of this document.
The following code are just snippets.
Getting Started
---------------
We begin by importing the BigchainDB driver:
.. code-block:: js
const driver = require('bigchaindb-driver')
Next, we define a constant containing the API path.
.. code-block:: js
const API_PATH = 'http://localhost:9984/api/v1/'
Create a Connection with BigchainDB
-----------------------------------
A simple connection with a BigchainDB node can be established like this:
.. code-block:: js
const conn = new driver.Connection(API_PATH)
If the BigchainDB node requires special HTTP request headers
(such as ``app_id`` and ``app_key``),
they can be included like this:
.. code-block:: js
const conn = new driver.Connection(API_PATH, {
app_id: 'app_id_value',
app_key: 'app_key_value'
})
A more complex connection can be created if the BigchainDB network
has several nodes, each with a different API path and different required headers.
The connection strategy will be the one specified in BEP-14_.
.. _BEP-14: https://github.com/bigchaindb/BEPs/tree/master/14#connection-strategy
.. code-block:: js
const conn = new driver.Connection([
API_PATH_1, // the first node does not use custom headers, only common headers
{endpoint: API_PATH_2,
headers: {app_id: 'your_app_id',
app_key: 'your_app_key'}},
{endpoint: API_PATH_3,
headers: {app_id: 'your_app_id',
app_key: 'your_app_key',
extra_header: 'extra value'}},
{endpoint: API_PATH_4,
headers: {app_id: 'your_app_id',
app_key: 'your_app_key',
other_header: 'other value'}},
{endpoint: API_PATH_5,
headers: {custom_auth: 'custom token'}],
{'sender_id': 'ab-12769'} // common header sent to all nodes
)
Cryptographic Identities Generation
-----------------------------------
Alice and Bob are represented by public/private key pairs. The private key is
used to sign transactions, meanwhile the public key is used to verify that a
signed transaction was indeed signed by the one who claims to be the signee.
.. code-block:: js
const alice = new driver.Ed25519Keypair()
const bob = new driver.Ed25519Keypair()
Digital Asset Definition
------------------------
As an example, lets consider the creation and transfer of a digital asset
that represents a bicycle:
.. code-block:: js
const assetdata = {
'bicycle': {
'serial_number': 'abcd1234',
'manufacturer': 'Bicycle Inc.',
}
}
We'll suppose that the bike belongs to Alice, and that it eventually will be
transferred to Bob.
In general, you are free to define any JSON object you which to store for the
``'data'`` property (assetdata).
Metadata Definition (*optional*)
--------------------------------
You can `optionally` add metadata to a transaction. Any JSON object is accepted.
For example, the bicycle will be transferred on earth which is metadata:
.. code-block:: js
const metadata = {'planet': 'earth'}
Asset Creation
--------------
We're now ready to create the digital asset. First, let's make a 'CREATE'
transaction:
.. code-block:: js
const txCreateAliceSimple = driver.Transaction.makeCreateTransaction(
assetdata,
metadata,
// A transaction needs an output
[ driver.Transaction.makeOutput(
driver.Transaction.makeEd25519Condition(alice.publicKey))
],
alice.publicKey
)
Transaction needs an array of Output objects.
Think of these as the recipients of the asset after the transaction.
For `CREATE` Transactions, this should usually just be a list of
Outputs wrapping Ed25519 Conditions generated from the issuers' public
keys (so that the issuers are the recipients of the created asset).
``alice.publicKey`` can be considered as the Input for the transaction.
Each input spends/transfers a previous output by satisfying/fulfilling
the crypto-conditions on that output. A CREATE transaction should have
exactly one input. A TRANSFER transaction should have at least one input (i.e. ≥1).
Sign the transaction with private key of Alice to fulfill it:
.. code-block:: js
driver.Transaction.signTransaction(txCreateAliceSimple, alice.privateKey)
And sent over to a BigchainDB node:
.. code-block:: js
conn.postTransactionCommit(txCreateAliceSimpleSigned)
Notice the transaction ``id``:
.. code-block:: js
txid = txCreateAliceSimpleSigned.id
Asset Transfer
--------------
Imagine some time goes by, during which Alice is happy with her bicycle, and
one day, she meets Bob, who is interested in acquiring her bicycle. The timing
is good for Alice as she had been wanting to get a new bicycle.
To transfer the bicycle (asset) to Bob, Alice must consume the transaction's output in
which the Bicycle asset was created.
Alice could retrieve the transaction:
.. code-block:: js
conn.getTransaction(txCreateAliceSimpleSigned.id)
First, let's prepare the transaction to be transferred.
.. code-block:: js
const txTransferBob = driver.Transaction.makeTransferTransaction(
// signedTx to transfer and output index
[{ tx: txCreateAliceSimpleSigned, output_index: 0 }],
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(bob.publicKey))],
// metadata
{price: '100 euro'}
);
The function ``makeTransferTransaction()`` needs following parameters:
- 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.
- Metadata for transaction (e.g. price of sold bike)
Fulfill transaction by signing it with Alice's private key.
.. code-block:: js
driver.Transaction.signTransaction(txTransferBob, alice.privateKey);
And sent over to a BigchainDB node:
.. code-block:: js
conn.postTransactionCommit(txTransferBobSigned)
Check the status again:
Bob is the new owner:
.. code-block:: js
console.log('Is Bob the owner?', txTransferBobSigned['outputs'][0]['public_keys'][0] == bob.publicKey)
// Output: true
Alice is the former owner:
.. code-block:: js
console.log('Was Alice the previous owner?', txTransferBobSigned['inputs'][0]['owners_before'][0] == alice.publicKey )
// Output: true
Querying for Assets
-------------------
BigchainDB allows you to query for assets using simple text search. This search is applied to all the strings inside the asset payload and returns all the assets that match a given text search string.
BigchainDB also allows you to query for metadata, but there are some differences. The response of the text search call, beside retrieving the asset or metadata in each case, it consist of:
- In the assets search the call returns the asset id which is the same id of the transaction that created the asset.
- In the metadata search the call returns the transaction id that contains this metadata.
Lets assume that we created 3 assets that look like this:
.. code-block:: js
assets = [
{'data': {'bicycle': {'serial_number': 'abc', manufacturer: 'Bicycle Inc.'}}},
{'data': {'bicycle': {'serial_number': 'cde', manufacturer: 'Bicycle Inc.'}}},
{'data': {'bicycle': {'serial_number': 'fgh', manufacturer: 'Bicycle Inc.'}}}
]
Lets perform a text search for all assets that contain the word 'Bicycle Inc.':
.. code-block:: js
conn.searchAssets('Bicycle Inc.')
.then(assets => console.log('Found assets with serial number Bicycle Inc.:', assets))
Which leads to following result:
.. code-block:: js
[
{
'data': {'bicycle': {'serial_number': 'abc', manufacturer: 'Bicycle Inc.'}},
'id': '7582d7a81652d0230fefb47dafc360ff09b2c2566b68f05c3a004d57e7fe7610'
},
{
'data': {'bicycle': {'serial_number': 'cde', manufacturer: 'Bicycle Inc.'}},
'id': 'e40f4b6ac70b9c1b3b237ec13f4174384fd4d54d36dfde25520171577c49caa4'
},
{
'data': {'bicycle': {'serial_number': 'fgh', manufacturer: 'Bicycle Inc.'}},
'id': '748f6c30daaf771c9020d84db9ad8ac4d1f7c8de7013db55e16f10ba090f7013'
}
]
This call returns all the assets that match the string 'Bicycle Inc.', sorted by text score, as well as the asset id.
Querying for Metadata
---------------------
Similar as querying for assets, in BigchainDB you can query for metadata using simple text search.
This search is applied to all the strings inside the metadata payload and returns all the metadata payloads that match a given text search string.
Having 3 metadata objets that look like this:
.. code-block:: js
metadata = [
{'state': {'price': 145, 'eur/us': '1.32'}},
{'state': {'price': 236, 'eur/us': '1.15'}},
{'state': {'price': 102, 'eur/us': '1.32'}},
]
Lets perform a text search for all metadata that contains the word '1.32':
.. code-block:: js
conn.searchMetadata('1.32')
.then(assets => console.log('Found assets with serial number Bicycle Inc.:', assets))
Which leads to following result:
.. code-block:: js
[
{
'metadata': {'state': {'price': 145, 'eur/us': '1.32'}},
'id': '14045a0e27ea971f8ac88762d2d74518d3a21f3f0fcd9d8a9a3b644b689cf3eb'
},
{
'metadata': {'state': {'price': 102, 'eur/us': '1.32'}},
'id': '6dd91f4700b3f66c55c50be009018e96f026d37f565d042d1aedfb322623d17d'
}
]
This call returns all the metadata objects that match the string '1.32', sorted by text score, as well as the transaction id corresponding to each metadata object.
Recap: Asset Creation & Transfer
--------------------------------
.. code-block:: js
const driver = require('bigchaindb-driver')
// BigchainDB server instance or testnetwork (e.g. https://example.com/api/v1/)
const API_PATH = 'http://localhost:9984/api/v1/'
// Create a new keypair for Alice and Bob
const alice = new driver.Ed25519Keypair()
const bob = new driver.Ed25519Keypair()
console.log('Alice: ', alice.publicKey)
console.log('Bob: ', bob.publicKey)
// Define the asset to store, in this example
// we store a bicycle with its serial number and manufacturer
const assetdata = {
'bicycle': {
'serial_number': 'cde',
'manufacturer': 'Bicycle Inc.',
}
}
// Metadata contains information about the transaction itself
// (can be `null` if not needed)
// E.g. the bicycle is fabricated on earth
const metadata = {'planet': 'earth'}
// Construct a transaction payload
const txCreateAliceSimple = driver.Transaction.makeCreateTransaction(
assetdata,
metadata,
// A transaction needs an output
[ driver.Transaction.makeOutput(
driver.Transaction.makeEd25519Condition(alice.publicKey))
],
alice.publicKey
)
// Sign the transaction with private keys of Alice to fulfill it
const txCreateAliceSimpleSigned = driver.Transaction.signTransaction(txCreateAliceSimple, alice.privateKey)
// Send the transaction off to BigchainDB
const conn = new driver.Connection(API_PATH)
conn.postTransactionCommit(txCreateAliceSimpleSigned)
.then(retrievedTx => console.log('Transaction', retrievedTx.id, 'successfully posted.'))
// With the postTransactionCommit if the response is correct, then the transaction
// is valid and commited to a block
// Transfer bicycle to Bob
.then(() => {
const txTransferBob = driver.Transaction.makeTransferTransaction(
// signedTx to transfer and output index
[{ tx: txCreateAliceSimpleSigned, output_index: 0 }],
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(bob.publicKey))],
// metadata
{price: '100 euro'}
)
// Sign with alice's private key
let txTransferBobSigned = driver.Transaction.signTransaction(txTransferBob, alice.privateKey)
console.log('Posting signed transaction: ', txTransferBobSigned)
// Post with commit so transaction is validated and included in a block
return conn.postTransactionCommit(txTransferBobSigned)
})
.then(tx => {
console.log('Response from BDB server:', tx)
console.log('Is Bob the owner?', tx['outputs'][0]['public_keys'][0] == bob.publicKey)
console.log('Was Alice the previous owner?', tx['inputs'][0]['owners_before'][0] == alice.publicKey )
})
// Search for asset based on the serial number of the bicycle
.then(() => conn.searchAssets('Bicycle Inc.'))
.then(assets => console.log('Found assets with serial number Bicycle Inc.:', assets))
Ed25519Keypair Seed Functionality
---------------------------------
BigchainDB JavaScript driver allows you to create a keypair based on a seed.
The constructor accepts a 32 byte seed. One of the ways to create a seed from
a string (e.g. a passphrase) is the one used by ``bip39``, specifically the function ``mnemonicToSeed``.
Install bip39 with npm: ``npm install bip39``
Next, require ``bip39`` in your file like this: ``var bip39 = require('bip39')``
At last, we can create the keypair based on a string. The function will transform the string to a byte array.
As our constructor ``Ed25519Keypair()`` only accepts a seed of 32 bytes, we slice the first 32 bytes: ``slice(0,32)``.
.. code-block:: js
var keypair = new driver.Ed25519Keypair(bip39.mnemonicToSeed("yourString").slice(0, 32))
You can use the ``Ed25519Keypair()`` constructor as well without seed.
.. code-block:: js
var keypair = new driver.Ed25519Keypair()
Websocket Event Stream API Usage
--------------------------------
The Event Stream API enables new ways to interact with BigchainDB,
making it possible for your application to subscribe
to all newlyconfirmed transactions that are happening in the system.
Below piece of code can be opened in your web browser.
It will connect to your websocket (if you are using the testnet, redefine
``var wsUri ='wss://insert-testnet-subdomain-here.com:443/api/v1/streams/valid_transactions'``).
This web page will display all validated transactions.
.. code-block:: html
<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket BigchainDB</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- Websocket Script -->
<script language="javascript" type="text/javascript">
var wsUri = "ws://localhost:9985/api/v1/streams/valid_transactions";
var output;
var alertbox;
function init()
{
output = document.getElementById("output");
alertbox = document.getElementById("alert-box");
setWebSocket();
}
function setWebSocket()
{
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
}
function onOpen(evt)
{
writeAlertMessage("CONNECTED");
}
function onClose(evt)
{
writeAlertMessage("DISCONNECTED");
}
function onMessage(evt)
{
writeToScreen('<a href="#" class="list-group-item"><h4 class="list-group-item-heading">Valid Transaction</h4><p class="list-group-item-text">' + evt.data + '</p></a>');
}
function onError(evt)
{
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}
function closeConnection(evt)
{
websocket.close()
}
function writeToScreen(message)
{
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
function writeAlertMessage(message)
{
var alert = document.createElement("div");
alert.className = "alert alert-success";
alert.setAttribute("role", "alert");
alert.innerHTML = message;
alertbox.appendChild(alert);
}
/* Initialize websocket and attach all events */
window.addEventListener("load", init, false);
/* Event called on closing browser or refreshing page to close connection */
window.addEventListener("beforeunload", closeConnection, false);
</script>
<!-- HTML Template -->
<div class="container">
<h2>WebSocket API Stream Valid Transactions BigchainDB</h2>
<!-- Box for displaying all alerts -->
<div id="alert-box"></div>
<!-- Div for attachting all outputs -->
<div id="output" class="list-group"></div>
</div>
Besides that, a NodeJs version has been created to display the validated transactions.
All transactions are printed to the console. To use this piece of code, you will need the ``ws`` (WebSocket package) through npm: ``npm install --save ws``.
.. code-block:: js
const WebSocket = require('ws')
const ws = new WebSocket('ws://localhost:9985/api/v1/streams/valid_transactions')
ws.on('open', () => {
console.log("CONNECTED")
});
ws.on('message', (data) => {
let json = JSON.parse(data)
console.log("\nTransactionId: ", json.transaction_id)
console.log("AssetId: ", json.asset_id)
console.log("BlockId: ", json.block_id)
});
Difference unspent and spent output
-----------------------------------
An unspent output is simply an output of a transaction which isn't yet an input of another transaction.
So, if we transfer an asset, the output becomes spent, because it becomes the input of the transfer transaction.
The transfer transactions its output becomes unspent now until he transfers the asset again to somebody else.
We will demonstrate this with a piece of code where we transfer a bicycle from Alice to Bob,
and further we transfer it from Bob to Chris. Expectations:
* Output for Alice is spent
* Output for Bob is spent
* Output for Chris is unspent (he is the last person in transaction chain)
.. code-block:: js
const driver = require('bigchaindb-driver')
const API_PATH = 'http://localhost:9984/api/v1/'
const conn = new driver.Connection(API_PATH)
const alice = new driver.Ed25519Keypair()
const bob = new driver.Ed25519Keypair()
const chris = new driver.Ed25519Keypair()
console.log('Alice: ', alice.publicKey)
console.log('Bob: ', bob.publicKey)
console.log('Chris: ', chris.publicKey)
// Define the asset to store, in this example
// we store a bicycle with its serial number and manufacturer
assetdata = {
'bicycle': {
'serial_number': 'cde',
'manufacturer': 'Bicycle Inc.',
}
}
var txTransferBobSigned;
// Construct a transaction payload
const txCreateAliceSimple = driver.Transaction.makeCreateTransaction(
assetdata,
{'meta': 'meta'},
// A transaction needs an output
[ driver.Transaction.makeOutput(
driver.Transaction.makeEd25519Condition(alice.publicKey))
],
alice.publicKey
)
// Sign the transaction with private keys of Alice to fulfill it
const txCreateAliceSimpleSigned = driver.Transaction.signTransaction(txCreateAliceSimple, alice.privateKey)
console.log('\n\nPosting signed create transaction for Alice:\n', txCreateAliceSimpleSigned)
conn.postTransactionCommit(txCreateAliceSimpleSigned)
// Transfer bicycle from Alice to Bob
.then(() => {
const txTransferBob = driver.Transaction.makeTransferTransaction(
[{ tx: txCreateAliceSimpleSigned, output_index: 0 }],
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(bob.publicKey))],
{'newOwner': 'Bob'}
)
// Sign with alice's private key
txTransferBobSigned = driver.Transaction.signTransaction(txTransferBob, alice.privateKey)
console.log('\n\nPosting signed transaction to Bob:\n', txTransferBobSigned)
// Post with commit so transaction is validated and included in a block
return conn.postTransactionCommit(txTransferBobSigned)
})
// Second transfer of bicycle from Bob to Chris
.then(tx => {
const txTransferChris = driver.Transaction.makeTransferTransaction(
[{ tx: txTransferBobSigned, output_index: 0 }],
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(chris.publicKey))],
{'newOwner': 'Chris'}
)
// Sign with bob's private key
let txTransferChrisSigned = driver.Transaction.signTransaction(txTransferChris, bob.privateKey)
console.log('\n\nPosting signed transaction to Chris:\n', txTransferChrisSigned)
// Post with commit so transaction is validated and included in a block
return conn.postTransactionCommit(txTransferChrisSigned)
})
.then(() => conn.listOutputs(alice.publicKey, true))
.then(listSpentOutputs => {
console.log("\nSpent outputs for Alice: ", listSpentOutputs.length) // Spent outputs: 1
return conn.listOutputs(alice.publicKey, false)
})
.then(listUnspentOutputs => {
console.log("Unspent outputs for Alice: ", listUnspentOutputs.length) // Unspent outputs: 0
return conn.listOutputs(bob.publicKey, true)
})
.then(listSpentOutputs => {
console.log("\nSpent outputs for Bob: ", listSpentOutputs.length) // Spent outputs: 1
return conn.listOutputs(bob.publicKey, false)
})
.then(listUnspentOutputs => {
console.log("Unspent outputs for Bob: ", listUnspentOutputs.length) // Unspent outputs: 0
return conn.listOutputs(chris.publicKey, true)
})
.then(listSpentOutputs => {
console.log("\nSpent outputs for Chris: ", listSpentOutputs.length) // Spent outputs: 0
return conn.listOutputs(chris.publicKey, false)
})
.then(listUnspentOutputs => {
console.log("Unspent outputs for Chris: ", listUnspentOutputs.length) // Unspent outputs: 1
})
.catch(res => {console.log(res)})
Output of above code looks like this. As you can see, Chris has no spent output, but one unspent output.
.. code-block:: js
Spent outputs for Alice: 1
Unspent outputs for Alice: 0
Spent outputs for Bob: 1
Unspent outputs for Bob: 0
Spent outputs for Chris: 0
Unspent outputs for Chris: 1
Divisible Assets
----------------
All assets in BigchainDB become implicitly divisible if a transaction contains more than one of that asset (well see how this happens shortly).
Let's assume we have created a token to pay each other for small transactions like a beer or some food between friends.
.. code-block:: js
const token = {
'value': '1 euro'
}
Let's create the asset. Note that we give an extra parameter to the ``makeOutput()`` function.
We give it the parameter ``'4'`` to indicate that we want to create 4 tokens.
**Pay attention to give the function a String instead of a plain Number.**
.. code-block:: js
const txCreateAliceDivisible = driver.Transaction.makeCreateTransaction(
token,
{metaDataMessage: 'I am specific to this create transaction'},
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(alice.publicKey), '4')],
alice.publicKey
)
Alice goes dining at Bob and Carly. She decides to give a small fee to Bob and Carly.
Alice decides to issue 4 tokens as a payment for her food: one to Bob, two to Carly and one to herself.
Why one to herself? If you decide to fulfill an output, you have to spend all tokens.
So if you want to keep one token for yourself, you have to transfer it to yourself.
As you can see, we fulfill the first output of the create transaction (it's 0 because we start counting from 0).
This gives us 4 tokens to transfer.
.. code-block:: js
const txTransferDivisible = driver.Transaction.makeTransferTransaction(
[{ 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')
],
{
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.
* Output 0 represents 2 tokens for Carly
* Output 1 represents 1 token for Bob
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(
[{ 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')
],
{
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:
.. code-block:: js
const txTransferDivisibleInputsSigned = driver.Transaction.signTransaction(
txTransferDivisibleInputs,
carly.privateKey, bob.privateKey)
Here is a better overview of the flow of the tokens.
+-----------+------------+-----------------+
| **Owner** | **Amount** | **Transaction** |
+===========+============+=================+
| ``Alice`` | 4 | ``CREATE`` |
+-----------+------------+-----------------+
| ``Alice`` | 1 | ``TRANSFER 1`` |
+-----------+------------+-----------------+
| ``Bob`` | 1 | ``TRANSFER 1`` |
+-----------+------------+-----------------+
| ``Carly`` | 2 | ``TRANSFER 1`` |
+-----------+------------+-----------------+
| ``Alice`` | 3 | ``TRANSFER 2`` |
+-----------+------------+-----------------+
| ``Bob`` | 0 | ``TRANSFER 2`` |
+-----------+------------+-----------------+
| ``Carly`` | 1 | ``TRANSFER 2`` |
+-----------+------------+-----------------+
.. TODO:
.. - Add lexer: https://stackoverflow.com/questions/4259105/which-sphinx-code-block-language-to-use-for-json
.. - Add divisible assets example
.. - Add more readable code with promises possibly.

View File

@ -1,16 +1,10 @@
<!---
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
--->
# Updating js-bigchaindb-driver from v0.1.x to v0.3.0
# Updating js-bigchaindb-driver from v0.1.x to v0.2.0
The latest version of js-bigchaindb-driver contains breaking changes to its
external API. In this document, we enumerate all changes to allow you to make
upgrades efficiently.
Note that upgrading the js-bigchaindb-driver to v0.3.0 was done to enable
Note that upgrading the js-bigchaindb-driver to v0.2.0 was done to enable
functionality included in the latest (v1.0) BigchainDB release. A full list of
BigchainDB v1.0's breaking changes can be found
[here](https://github.com/bigchaindb/bigchaindb/blob/17913dca682ff105540c0ea73365f1763efc2083/docs/upgrade-guides/v0.10--%3Ev1.0.md).
@ -103,7 +97,7 @@ The driver is now bundled automatically each time we publish it to npm.com. We
now ship packages for `commonjs`, `commonjs2`, `amd`, `umd`, `window` and
node.js. Thanks to unpkg.com, we're also able to provide all these packages on
a CDN. A link to all the bundles can be found
[here](https://unpkg.com/bigchaindb-driver@0.3.0/dist/browser/).
[here](https://unpkg.com/bigchaindb-driver@0.2.0/dist/browser/).
A few notes:

View File

@ -1,16 +0,0 @@
{
"presets": [["@babel/preset-env"]],
"plugins": [
"@babel/plugin-syntax-async-generators",
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"helpers": true,
"regenerator": true
}
],
"@babel/plugin-transform-regenerator",
"@babel/plugin-transform-async-to-generator"
]
}

1
examples/.gitignore vendored
View File

@ -1 +0,0 @@
node_modules

View File

@ -1,32 +0,0 @@
<!---
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
--->
# Quick Notes
`dotenv` is listed as a dependencies in `package.json`.
If you want to use this, add a `.env` file to the root of this project (same level as this `README.md` file)
and replace the variables to fit your specific config.
```
BIGCHAINDB_API_PATH=https://test.bigchaindb.com/api/v1/
BIGCHAINDB_APP_ID=<your-app-id>
BIGCHAINDB_APP_KEY=<your-app-key>
```
# Usage
`npm install` -> Installs all required dependencies to run these examples.
## Different Examples
**Basic Usage**: Create asset and transfer it to new owner.
-> `npm start`
**Async/Await Basic Usage**: Basic usage example rewritten with async/await.
-> `npm run basic-async`
**Querying for Assets**: Query for assetdata or metadata.
-> `npm run query-assets`
**Seed/Keypair Functionality**: Create keypair with bip39 library.
-> `npm run seed-func`

View File

@ -1,38 +0,0 @@
{
"name": "js-driver-bigchaindb-examples",
"version": "1.0.0",
"main": "src/basic-usage.js",
"scripts": {
"build": "npm run clean && babel src -d dist",
"serve": "node dist/basic-usage.js",
"clean": "rimraf ./dist",
"start": "nodemon src/basic-usage.js --exec babel-node",
"query-assets": "nodemon src/query-assets.js --exec babel-node",
"seed-func": "nodemon src/seed-func.js --exec babel-node",
"basic-async": "nodemon src/basic-usage-async-await.js --exec babel-node"
},
"author": "BigchainDB",
"license": "MIT",
"devDependencies": {
"@babel/cli": "^7.13.0",
"@babel/core": "^7.13.8",
"@babel/eslint-parser": "^7.13.8",
"@babel/node": "7.13.0",
"@babel/plugin-syntax-async-generators": "^7.8.4",
"@babel/plugin-transform-async-to-generator": "^7.13.0",
"@babel/plugin-transform-regenerator": "^7.12.13",
"@babel/plugin-transform-runtime": "^7.13.9",
"@babel/preset-env": "^7.13.9",
"@babel/register": "^7.13.8",
"babel-loader": "^8.2.2",
"nodemon": "^2.0.7",
"rimraf": "^3.0.2"
},
"repository": "/",
"private": true,
"dependencies": {
"bigchaindb-driver": "^4.1.2",
"bip39": "^3.0.3",
"dotenv": "^8.2.0"
}
}

View File

@ -1,62 +0,0 @@
// 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
/* eslint-disable import/no-unresolved */
const driver = require('bigchaindb-driver')
require('dotenv').config()
// ======== Preparation ======== //
const conn = new driver.Connection('https://test.ipdb.io/api/v1/', {
header1: 'header1_value',
header2: 'header2_value'
})
const alice = new driver.Ed25519Keypair()
const bob = new driver.Ed25519Keypair()
const assetdata = {
'bicycle': {
'serial_number': 'abcd1234',
'manufacturer': 'Bicycle Inc.',
}
}
const metadata = { 'planet': 'earth' }
// Call async basic usage function
basicUsage()
async function basicUsage() {
// ======== Create Transaction Bicycle ======== //
const txCreateAliceSimple = driver.Transaction.makeCreateTransaction(
assetdata,
metadata,
[
driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(alice.publicKey))
],
alice.publicKey
)
const txCreateAliceSimpleSigned =
driver.Transaction.signTransaction(txCreateAliceSimple, alice.privateKey)
// ======== POST CREATE Transaction ======== //
const createdTx = await conn.postTransactionCommit(txCreateAliceSimpleSigned)
// ======== POST TRANSFER Transaction ======== //
const txTransferBob = driver.Transaction.makeTransferTransaction(
[{ tx: createdTx, output_index: 0 }],
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(bob.publicKey))],
{ price: '100 euro' }
)
const txTransferBobSigned = driver.Transaction.signTransaction(txTransferBob, alice.privateKey)
await conn.postTransactionCommit(txTransferBobSigned)
// ======== Querying Assets ======== //
const assets = await conn.searchAssets('Bicycle Inc.')
console.log(assets) // eslint-disable-line no-console
}

View File

@ -1,63 +0,0 @@
// 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
/* eslint-disable import/no-unresolved */
const driver = require('bigchaindb-driver')
require('dotenv').config()
// ======== Preparation ======== //
const conn = new driver.Connection('https://test.ipdb.io/api/v1/', {
header1: 'header1_value',
header2: 'header2_value'
})
const alice = new driver.Ed25519Keypair()
const bob = new driver.Ed25519Keypair()
const assetdata = {
'bicycle': {
'serial_number': 'abcd1234',
'manufacturer': 'Bicycle Inc.',
}
}
const metadata = { 'planet': 'earth' }
// ======== Create Transaction Bicycle ======== //
const txCreateAliceSimple = driver.Transaction.makeCreateTransaction(
assetdata,
metadata,
[
driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(alice.publicKey))
],
alice.publicKey
)
const txCreateAliceSimpleSigned =
driver.Transaction.signTransaction(txCreateAliceSimple, alice.privateKey)
// ======== Post Transaction and Fetch Result ======== //
conn.postTransactionCommit(txCreateAliceSimpleSigned)
// ======== Transfer Bicycle to Bob ======== //
.then((fetchedTx) => {
const txTransferBob = driver.Transaction.makeTransferTransaction(
[{ tx: fetchedTx, output_index: 0 }],
[driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(bob.publicKey))],
{ price: '100 euro' }
)
// Sign transfer transaction with Alice's private key
const txTransferBobSigned = driver.Transaction.signTransaction(txTransferBob, alice.privateKey)
return conn.postTransactionCommit(txTransferBobSigned)
})
.then(tx => {
console.log('Is Bob the owner?', tx.outputs[0].public_keys[0] === bob.publicKey) // eslint-disable-line no-console
console.log('Was Alice the previous owner?', tx.inputs[0].owners_before[0] === alice.publicKey) // eslint-disable-line no-console
})
// ======== Search Asset by Serial Number ======== //
.then(() => conn.searchAssets('Bicycle Inc.'))
.then(assets => console.log('Found assets with serial number Bicycle Inc.:', assets)) // eslint-disable-line no-console

View File

@ -1,50 +0,0 @@
// 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
/* eslint-disable import/no-unresolved */
const driver = require('bigchaindb-driver')
require('dotenv').config()
// ======== Preparation ======== //
const conn = new driver.Connection('https://example.com/api/v1/', {
header1: 'header1_value',
header2: 'header2_value'
})
const alice = new driver.Ed25519Keypair()
// ======== Asset Array ======== //
const assetArray = []
assetArray.push({ 'bicycle': { 'serial_number': 'abc', 'manufacturer': 'BicyclesInc' } })
assetArray.push({ 'bicycle': { 'serial_number': 'cde', 'manufacturer': 'BicyclesInc' } })
assetArray.push({ 'bicycle': { 'serial_number': 'fgh', 'manufacturer': 'BicyclesInc' } })
const metadata = { 'planet': 'Pluto' }
// ======== Create Transactions for bicycles ======== //
function createTx(assetdata) {
const txCreate = driver.Transaction.makeCreateTransaction(
assetdata,
metadata,
[
driver.Transaction.makeOutput(driver.Transaction.makeEd25519Condition(alice.publicKey))
],
alice.publicKey
)
const txCreateSigned = driver.Transaction.signTransaction(txCreate, alice.privateKey)
return conn.postTransactionCommit(txCreateSigned)
}
// ======== Execute all promises in order to post transactions and fetch them ======== //
Promise.all(assetArray.map(createTx))
// ======== Querying Assets for Assetdata ======== //
.then(() => conn.searchAssets('BicyclesInc'))
.then(assets => console.log('Found assets with serial number "BicyclesInc":', assets)) // eslint-disable-line no-console
// ======== Querying Assets for Metadata ======== //
.then(() => conn.searchMetadata('Pluto'))
.then(assets => console.log('Found assets with metadata "Pluto":', assets)) // eslint-disable-line no-console

View File

@ -1,40 +0,0 @@
// 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
/* eslint-disable import/no-unresolved */
import bip39 from 'bip39'
const driver = require('bigchaindb-driver')
// ======== Create Keypair ======== //
/**
* Use a passphrase to derive a keypair
* If you use the same seed -> you will derive the same keypair
*
* mnemnoicToSeed() transforms the passphrase you gave as an input
* to a byteArray
*
* BigchainDB however only accepts an input length of 32 characters
* so we have to slice this to give it as input for driver.Ed25519Keypair()
*
* Is it safe to slice? Yes, a seed of length 32 is very safe according
* to related papers discussing this.
*/
const passphrase = 'This is a random passphrase'
const seed = bip39.mnemonicToSeed(passphrase).slice(0, 32)
const keypair = new driver.Ed25519Keypair(seed)
console.log(`Public Key: ${keypair.publicKey} - Private Key: ${keypair.privateKey}`) // eslint-disable-line no-console
// ======== Other Bip39 Functionality not related to BigchainDB ======== //
/* Create Random passphrase */
const mnemonic = bip39.generateMnemonic()
console.log('Random passphrase: ', mnemonic) // eslint-disable-line no-console
/* Validate mnemnoic */
console.log(bip39.validateMnemonic(mnemonic)) // eslint-disable-line no-console
console.log(bip39.validateMnemonic('some random strings together but to short')) // eslint-disable-line no-console

View File

@ -1,6 +1,6 @@
{
"name": "bigchaindb-driver",
"version": "4.3.0",
"version": "0.3.0",
"description": "Node.js driver for BigchainDB",
"homepage": "https://www.bigchaindb.com/",
"bugs": "https://github.com/bigchaindb/js-bigchaindb-driver/issues",
@ -10,83 +10,73 @@
},
"license": "Apache-2.0",
"author": "BigchainDB",
"files": [
"dist",
"types"
],
"main": "./dist/node/index.js",
"browser": "./dist/browser/bigchaindb-driver.cjs2.min.js",
"types": "./types/index.d.ts",
"sideEffects": false,
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"lint": "eslint ./",
"build": "npm run clean && npm run build:cjs && npm run build:dist",
"build:bundle": "webpack",
"build:cjs": "cross-env BABEL_ENV=cjs babel ./src -d dist/node",
"build:dist": "cross-env NODE_ENV=production webpack",
"dev": "webpack -w",
"clean": "rimraf dist/bundle dist/browser dist/node",
"test": "npm run lint && nyc ava && npm run report-coverage",
"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",
"thanks": "cowsay Hi, thanks for your interest in BigchainDB. We appreciate your contribution!",
"release": "read -p 'GITHUB_TOKEN: ' GITHUB_TOKEN && export GITHUB_TOKEN=$GITHUB_TOKEN && release-it --src.tagName='v%s'",
"release-minor": "release-it minor --non-interactive",
"release-major": "release-it major --non-interactive",
"prepublishOnly": "npm run build",
"report-coverage": "nyc report --reporter=lcov > coverage.lcov && codecov",
"doc": "documentation build src/index.js -f md -o API.md -g --markdown-toc"
"release": "./node_modules/release-it/bin/release.js --src.tagName='v%s' --github.release --npm.publish --non-interactive",
"release-minor": "./node_modules/release-it/bin/release.js minor --src.tagName='v%s' --github.release --npm.publish --non-interactive",
"release-major": "./node_modules/release-it/bin/release.js major --src.tagName='v%s' --github.release --npm.publish --non-interactive",
"prepublishOnly": "npm update && npm run build",
"precommit": "lint-staged",
"report-coverage": "nyc report --reporter=lcov > coverage.lcov && codecov"
},
"lint-staged": {
"*.js": [
"eslint"
]
},
"devDependencies": {
"@ava/babel": "^2.0.0",
"@babel/cli": "^7.17.0",
"@babel/core": "^7.17.2",
"@babel/eslint-parser": "^7.17.0",
"@babel/plugin-proposal-export-default-from": "^7.16.7",
"@babel/plugin-proposal-object-rest-spread": "^7.16.7",
"@babel/plugin-syntax-async-generators": "^7.8.4",
"@babel/plugin-transform-async-to-generator": "^7.16.8",
"@babel/plugin-transform-object-assign": "^7.16.7",
"@babel/plugin-transform-regenerator": "^7.16.7",
"@babel/plugin-transform-runtime": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"@babel/register": "^7.17.0",
"ava": "^3.15.0",
"babel-loader": "^8.2.2",
"buffer": "^6.0.3",
"codecov": "^3.8.1",
"cross-env": "^7.0.3",
"documentation": "^13.2.5",
"eslint": "^8.9.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.25.4",
"husky": "^7.0.4",
"lint-staged": "^12.3.4",
"nyc": "^15.1.0",
"release-it": "^14.12.4",
"rewire": "^6.0.0",
"rimraf": "^3.0.2",
"sinon": "^13.0.1",
"terser-webpack-plugin": "^5.3.1",
"webpack": "^5.68.0",
"webpack-cli": "^4.9.2",
"webpack-merge": "^5.8.0",
"webpack-sources": "^3.2.3"
"ava": "^0.20.0",
"babel-cli": "^6.22.2",
"babel-eslint": "^7.1.1",
"babel-loader": "^7.0.0",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-transform-es2015-modules-commonjs": "^6.23.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-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",
"lint-staged": "^4.0.0",
"nyc": "^11.0.2",
"release-it": "^2.7.3",
"rimraf": "^2.5.4",
"sinon": "^2.3.4",
"webpack": "^3.0.0"
},
"dependencies": {
"@babel/runtime-corejs3": "^7.17.2",
"abort-controller": "^3.0.0",
"bs58": "^4.0.1",
"clone": "^2.1.2",
"core-js": "^3.21.0",
"crypto-conditions": "2.2.1",
"decamelize": "^5.0.0",
"es6-promise": "^4.2.8",
"fetch-ponyfill": "^7.1.0",
"js-sha3": "^0.8.0",
"browser-resolve": "^1.11.2",
"bs58": "^4.0.0",
"buffer": "^5.0.2",
"clone": "^2.1.0",
"core-js": "^2.4.1",
"decamelize": "^1.2.0",
"es6-promise": "^4.0.5",
"fetch-ponyfill": "^4.0.0",
"five-bells-condition": "^5.0.1",
"isomorphic-fetch": "^2.2.1",
"js-sha3": "^0.6.0",
"js-utility-belt": "^1.5.0",
"json-stable-stringify": "^1.0.1",
"query-string": "^7.1.1",
"sprintf-js": "^1.1.2",
"tweetnacl": "^1.0.3"
"query-string": "^4.3.4",
"sprintf-js": "^1.0.3",
"tweetnacl": "^1.0.0",
"yarn": "^0.27.5"
},
"keywords": [
"bigchaindb",
@ -95,15 +85,9 @@
"decentralized",
"dapp"
],
"lint-staged": {
"*.js": [
"eslint"
]
},
"ava": {
"files": [
"test/**/*.js",
"!test/constants.js"
"test/*.js"
],
"source": [
"**/*.{js,jsx}",
@ -115,29 +99,8 @@
"tap": true,
"powerAssert": false,
"require": [
"@babel/register"
"babel-register"
],
"babel": true
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"release-it": {
"github": {
"release": true
},
"git": {
"tagName": "v${version}"
},
"hooks": {
"before:init": [
"npm run test"
]
},
"npm": {
"publish": true
}
"babel": "inherit"
}
}

View File

@ -1,38 +0,0 @@
// 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
/* eslint-disable strict, no-console, object-shorthand, import/no-extraneous-dependencies */
const { ConcatSource } = require('webpack-sources')
module.exports = class AddVendorsPlugin {
constructor(base) {
this.base = base
}
apply(compiler) {
compiler.hooks.emit.tapAsync(
`AddVendorsPlugin ${this.base}`,
(compilation, callback) => {
const main = compilation.assets[`main.${this.base}`]
const mainMap = compilation.assets[`main.${this.base}.map`]
const vendor = compilation.assets[`vendors.${this.base}`]
if (main && vendor) {
const compiledAsset = new ConcatSource(main._value[0])
compiledAsset.add(vendor)
compiledAsset.add(main._value[1])
compilation.assets = {}
compilation.assets[this.base] = compiledAsset
} else if (main && mainMap) {
compilation.assets = {}
compilation.assets[this.base] = main
compilation.assets[`${this.base}.map`] = mainMap
}
callback()
}
)
}
}

View File

@ -1,21 +1,17 @@
// 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
import base58 from 'bs58'
import { sign } from 'tweetnacl'
import nacl from 'tweetnacl'
/**
* @public
* Ed25519 keypair in base58 (as BigchainDB expects base58 keys)
* @class Keypair Ed25519 keypair in base58 (as BigchainDB expects base58 keys)
* @type {Object}
* @param {Buffer} [seed] A seed that will be used as a key derivation function
* @property {string} publicKey
* @property {string} privateKey
*/
export default function Ed25519Keypair(seed) {
const keyPair = seed ? sign.keyPair.fromSeed(seed) : sign.keyPair()
this.publicKey = base58.encode(Buffer.from(keyPair.publicKey))
const keyPair = seed ? nacl.sign.keyPair.fromSeed(seed) : nacl.sign.keyPair()
this.publicKey = base58.encode(keyPair.publicKey)
// tweetnacl's generated secret key is the secret key + public key (resulting in a 64-byte buffer)
this.privateKey = base58.encode(Buffer.from(keyPair.secretKey.slice(0, 32)))
this.privateKey = base58.encode(keyPair.secretKey.slice(0, 32))
}

View File

@ -1,78 +1,16 @@
// 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
// TODO: remove abort-controller when using Node >=15
import AbortController from 'abort-controller'
import { Promise } from 'es6-promise'
import fetchPonyfill from 'fetch-ponyfill'
import { vsprintf } from 'sprintf-js'
import formatText from './format_text'
import stringifyAsQueryParam from './stringify_as_query_param'
const fetch = fetchPonyfill({ Promise })
export function ResponseError(message, status, requestURI) {
this.name = 'ResponseError'
this.message = message
this.status = status
this.requestURI = requestURI
this.stack = new Error().stack
}
const fetch = fetchPonyfill(Promise)
ResponseError.prototype = new Error()
/**
* @private
* Timeout function following https://github.com/github/fetch/issues/175#issuecomment-284787564
* @param {integer} obj Source object
* @param {Promise} filter Array of key names to select or function to invoke per iteration
* @param {AbortController} controller AbortController instance bound to fetch
* @return {Object} TimeoutError if the time was consumed, otherwise the Promise will be resolved
*/
function timeout(ms, promise, controller) {
return new Promise((resolve, reject) => {
const nodeTimeout = setTimeout(() => {
controller.abort()
const errorObject = {
message: 'TimeoutError',
}
reject(new Error(errorObject))
}, ms)
promise
.then((res) => {
clearTimeout(nodeTimeout)
resolve(res)
})
.catch((err) => {
clearTimeout(nodeTimeout)
reject(err)
})
})
}
/**
* @private
* @param {Promise} res Source object
* @return {Promise} Promise that will resolve with the response if its status was 2xx;
* otherwise rejects with the response
*/
function handleResponse(res) {
// If status is not a 2xx (based on Response.ok), assume it's an error
// See https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch
if (!(res && res.ok)) {
throw new ResponseError(
'HTTP Error: Requested page not reachable',
`${res.status} ${res.statusText}`,
res.url
)
}
return res
}
/**
* @private
* imported from https://github.com/bigchaindb/js-utility-belt/
*
* Global fetch wrapper that adds some basic error handling and ease of use enhancements.
@ -86,44 +24,33 @@ function handleResponse(res) {
* @param {string} url Url to request. Can be specified as a sprintf format string (see
* https://github.com/alexei/sprintf.js) that will be resolved using
* `config.urlTemplateSpec`.
* @param {Object} config Additional configuration, mostly passed to fetch as its 'init' config
* @param {object} config Additional configuration, mostly passed to fetch as its 'init' config
* (see https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch#Parameters).
* @param {*} config.jsonBody Json payload to the request. Will automatically be
* JSON.stringify()-ed and override `config.body`.
* @param {string|Object} config.query Query parameter to append to the end of the url.
* @param {string|object} config.query Query parameter to append to the end of the url.
* If specified as an object, keys will be
* decamelized into snake case first.
* @param {*[]|Object} config.urlTemplateSpec Format spec to use to expand the url (see sprintf).
* @param {*[]|object} config.urlTemplateSpec Format spec to use to expand the url (see sprintf).
* @param {*} config.* All other options are passed through to fetch.
* @param {integer} requestTimeout Timeout for a single request
*
* @return {Promise} If requestTimeout the timeout function will be called. Otherwise resolve the
* Promise with the handleResponse function
* @return {Promise} Promise that will resolve with the response if its status was 2xx;
* otherwise rejects with the response
*/
export default function baseRequest(
url,
{
jsonBody, query, urlTemplateSpec, ...fetchConfig
} = {},
requestTimeout = 0
) {
export default function baseRequest(url, { jsonBody, query, urlTemplateSpec, ...fetchConfig } = {}) {
let expandedUrl = url
if (urlTemplateSpec != null) {
if (Array.isArray(urlTemplateSpec) && urlTemplateSpec.length) {
// Use vsprintf for the array call signature
expandedUrl = vsprintf(url, urlTemplateSpec)
} else if (
urlTemplateSpec &&
typeof urlTemplateSpec === 'object' &&
Object.keys(urlTemplateSpec).length
) {
} else if (urlTemplateSpec &&
typeof urlTemplateSpec === 'object' &&
Object.keys(urlTemplateSpec).length) {
expandedUrl = formatText(url, urlTemplateSpec)
} else if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.warn(
'Supplied urlTemplateSpec was not an array or object. Ignoring...'
)
console.warn('Supplied urlTemplateSpec was not an array or object. Ignoring...')
}
}
@ -142,16 +69,13 @@ export default function baseRequest(
fetchConfig.body = JSON.stringify(jsonBody)
}
if (requestTimeout) {
const controller = new AbortController()
const { signal } = controller
return timeout(
requestTimeout,
fetch.fetch(expandedUrl, { ...fetchConfig, signal }),
controller
)
.then(handleResponse)
} else {
return fetch.fetch(expandedUrl, fetchConfig).then(handleResponse)
}
return fetch.fetch(expandedUrl, fetchConfig)
.then((res) => {
// If status is not a 2xx (based on Response.ok), assume it's an error
// See https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch
if (!(res && res.ok)) {
throw res
}
return res
})
}

View File

@ -1,201 +0,0 @@
// 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
import Transport from './transport'
const HEADER_BLACKLIST = ['content-type']
const DEFAULT_NODE = 'http://localhost:9984/api/v1/'
const DEFAULT_TIMEOUT = 20000 // The default value is 20 seconds
/**
*
* @param {String, Array} nodes Nodes for the connection. String possible to be backwards compatible
* with version before 4.1.0 version
* @param {Object} headers Common headers for every request
* @param {float} timeout Optional timeout in secs
*
*
*/
export default class Connection {
// This driver implements the BEP-14 https://github.com/bigchaindb/BEPs/tree/master/14
constructor(nodes, headers = {}, timeout = DEFAULT_TIMEOUT) {
// Copy object
this.headers = { ...headers }
// Validate headers
Object.keys(headers).forEach(header => {
if (HEADER_BLACKLIST.includes(header.toLowerCase())) {
throw new Error(`Header ${header} is reserved and cannot be set.`)
}
})
this.normalizedNodes = []
if (!nodes) {
this.normalizedNodes.push(Connection.normalizeNode(DEFAULT_NODE, this.headers))
} else if (Array.isArray(nodes)) {
nodes.forEach(node => {
this.normalizedNodes.push(Connection.normalizeNode(node, this.headers))
})
} else {
this.normalizedNodes.push(Connection.normalizeNode(nodes, this.headers))
}
this.transport = new Transport(this.normalizedNodes, timeout)
}
static normalizeNode(node, headers) {
if (typeof node === 'string') {
return { 'endpoint': node, 'headers': headers }
} else {
const allHeaders = { ...headers, ...node.headers }
return { 'endpoint': node.endpoint, 'headers': allHeaders }
}
}
static getApiUrls(endpoint) {
return {
'blocks': 'blocks',
'blocksDetail': 'blocks/%(blockHeight)s',
'outputs': 'outputs',
'transactions': 'transactions',
'transactionsSync': 'transactions?mode=sync',
'transactionsAsync': 'transactions?mode=async',
'transactionsCommit': 'transactions?mode=commit',
'transactionsDetail': 'transactions/%(transactionId)s',
'assets': 'assets',
'metadata': 'metadata'
}[endpoint]
}
_req(path, options = {}) {
return this.transport.forwardRequest(path, options)
}
/**
* @param blockHeight
*/
getBlock(blockHeight) {
return this._req(Connection.getApiUrls('blocksDetail'), {
urlTemplateSpec: {
blockHeight
}
})
}
/**
* @param transactionId
*/
getTransaction(transactionId) {
return this._req(Connection.getApiUrls('transactionsDetail'), {
urlTemplateSpec: {
transactionId
}
})
}
/**
* @param transactionId
* @param status
*/
listBlocks(transactionId) {
return this._req(Connection.getApiUrls('blocks'), {
query: {
transaction_id: transactionId,
}
})
}
/**
* @param publicKey
* @param spent
*/
listOutputs(publicKey, spent) {
const query = {
public_key: publicKey
}
// NOTE: If `spent` is not defined, it must not be included in the
// query parameters.
if (spent !== undefined) {
query.spent = spent.toString()
}
return this._req(Connection.getApiUrls('outputs'), {
query
})
}
/**
* @param assetId
* @param operation
*/
listTransactions(assetId, operation) {
return this._req(Connection.getApiUrls('transactions'), {
query: {
asset_id: assetId,
operation
}
})
}
/**
* @param transaction
*/
postTransaction(transaction) {
return this.postTransactionCommit(transaction)
}
/**
* @param transaction
*/
postTransactionSync(transaction) {
return this._req(Connection.getApiUrls('transactionsSync'), {
method: 'POST',
jsonBody: transaction
})
}
/**
* @param transaction
*/
postTransactionAsync(transaction) {
return this._req(Connection.getApiUrls('transactionsAsync'), {
method: 'POST',
jsonBody: transaction
})
}
/**
* @param transaction
*/
postTransactionCommit(transaction) {
return this._req(Connection.getApiUrls('transactionsCommit'), {
method: 'POST',
jsonBody: transaction
})
}
/**
* @param search
*/
searchAssets(search, limit = 10) {
return this._req(Connection.getApiUrls('assets'), {
query: {
search,
limit
}
})
}
/**
* @param search
*/
searchMetadata(search, limit = 10) {
return this._req(Connection.getApiUrls('metadata'), {
query: {
search,
limit
}
})
}
}

185
src/connection/index.js Normal file
View File

@ -0,0 +1,185 @@
import request from '../request'
const HEADER_BLACKLIST = ['content-type']
export default class Connection {
constructor(path, headers = {}) {
this.path = path
this.headers = Object.assign({}, headers)
Object.keys(headers).forEach(header => {
if (HEADER_BLACKLIST.includes(header.toLowerCase())) {
throw new Error(`Header ${header} is reserved and cannot be set.`)
}
})
}
getApiUrls(endpoint) {
return this.path + {
'blocks': 'blocks',
'blocksDetail': 'blocks/%(blockId)s',
'outputs': 'outputs',
'statuses': 'statuses',
'transactions': 'transactions',
'transactionsDetail': 'transactions/%(transactionId)s',
'assets': 'assets',
'votes': 'votes'
}[endpoint]
}
_req(path, options = {}) {
// NOTE: `options.headers` could be undefined, but that's OK.
options.headers = Object.assign({}, options.headers, this.headers)
return request(path, options)
}
/**
* @public
* @param blockId
*/
getBlock(blockId) {
return this._req(this.getApiUrls('blocksDetail'), {
urlTemplateSpec: {
blockId
}
})
}
/**
* @public
* @param transactionId
*/
getStatus(transactionId) {
return this._req(this.getApiUrls('statuses'), {
query: {
transaction_id: transactionId
}
})
}
/**
* @public
* @param transactionId
*/
getTransaction(transactionId) {
return this._req(this.getApiUrls('transactionsDetail'), {
urlTemplateSpec: {
transactionId
}
})
}
/**
* @public
* @param transactionId
* @param status
*/
listBlocks(transactionId, status) {
return this._req(this.getApiUrls('blocks'), {
query: {
transaction_id: transactionId,
status
}
})
}
/**
* @public
* @param publicKey
* @param spent
* @param onlyJsonResponse
*/
listOutputs(publicKey, spent, onlyJsonResponse = true) {
const query = {
public_key: publicKey
}
// NOTE: If `spent` is not defined, it must not be included in the
// query parameters.
if (spent !== undefined) {
query.spent = spent.toString()
}
return this._req(this.getApiUrls('outputs'), {
query
}, onlyJsonResponse)
}
/**
* @public
* @param assetId
* @param operation
*/
listTransactions(assetId, operation) {
return this._req(this.getApiUrls('transactions'), {
query: {
asset_id: assetId,
operation
}
})
}
/**
* @public
* @param blockId
*/
listVotes(blockId) {
return this._req(this.getApiUrls('votes'), {
query: {
block_id: blockId
}
})
}
/**
* @public
* @param txId
* @return {Promise}
*/
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)
})
}
/**
* @public
*
* @param transaction
*/
postTransaction(transaction) {
return this._req(this.getApiUrls('transactions'), {
method: 'POST',
jsonBody: transaction
})
}
/**
* @public
*
* @param search
*/
searchAssets(search) {
return this._req(this.getApiUrls('assets'), {
query: {
search
}
})
}
}

View File

@ -1,9 +1,6 @@
// 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
import { sprintf } from 'sprintf-js'
// Regexes taken from or inspired by sprintf-js
const Regex = {
TEMPLATE_LITERAL: /\${([^)]+?)}/g,
@ -14,7 +11,7 @@ const Regex = {
/**
* imported from https://github.com/bigchaindb/js-utility-belt/
* @private
*
* Formats strings similarly to C's sprintf, with the addition of '${...}' formats.
*
* Makes a first pass replacing '${...}' formats before passing the expanded string and other
@ -50,7 +47,6 @@ export default function formatText(s, ...argv) {
let interpolationLeft = replacement
/**
* @private
* Interpolation algorithm inspired by sprintf-js.
*
* Goes through the replacement string getting the left-most key or index to interpolate
@ -73,7 +69,7 @@ export default function formatText(s, ...argv) {
// Assigning in the conditionals here makes the code less bloated
/* eslint-disable no-cond-assign */
while ((interpolationLeft = interpolationLeft.substring(curMatch[0].length)) &&
value != null) {
value != null) {
if ((curMatch = Regex.KEY_ACCESS.exec(interpolationLeft))) {
value = value[curMatch[1]]
} else if ((curMatch = Regex.INDEX_ACCESS.exec(interpolationLeft))) {
@ -88,7 +84,9 @@ export default function formatText(s, ...argv) {
// If there's anything left to interpolate by the end then we've failed to interpolate
// the entire replacement string.
if (interpolationLeft.length) {
throw new SyntaxError(`[formatText] failed to parse named argument key: ${replacement}`)
throw new SyntaxError(
`[formatText] failed to parse named argument key: ${replacement}`
)
}
return value

View File

@ -1,13 +1,5 @@
// 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
import Ed25519Keypair from './Ed25519Keypair'
import Connection from './connection'
import Transaction from './transaction'
import ccJsonLoad from './utils/ccJsonLoad'
import ccJsonify from './utils/ccJsonify'
export Ed25519Keypair from './Ed25519Keypair'
export {
ccJsonLoad, ccJsonify, Connection, Ed25519Keypair, Transaction
}
export * as Transaction from './transaction'
export Connection from './connection'

View File

@ -1,118 +1,38 @@
// 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
import baseRequest from './baseRequest'
import sanitize from './sanitize'
const DEFAULT_REQUEST_CONFIG = {
headers: {
'Accept': 'application/json'
}
}
const BACKOFF_DELAY = 500 // 0.5 seconds
const ERROR_FROM_SERVER = 'HTTP Error: Requested page not reachable'
/**
* @private
* Small wrapper around js-utility-belt's request that provides url resolving,
* default settings, and response handling.
*/
export default function request(url, config = {}, onlyJsonResponse = true) {
// Load default fetch configuration and remove any falsy query parameters
const requestConfig = Object.assign({}, DEFAULT_REQUEST_CONFIG, config, {
query: config.query && sanitize(config.query)
})
const apiUrl = url
export default class Request {
constructor(node) {
this.node = node
this.backoffTime = null
this.retries = 0
this.connectionError = null
}
async request(urlPath, config, timeout, maxBackoffTime) {
if (!urlPath) {
return Promise.reject(new Error('Request was not given a url.'))
}
// Load default fetch configuration and remove any falsy query parameters
const requestConfig = {
...this.node.headers,
...DEFAULT_REQUEST_CONFIG,
...config,
query: config.query && sanitize(config.query)
}
const apiUrl = this.node.endpoint + urlPath
if (requestConfig.jsonBody) {
requestConfig.headers = { ...requestConfig.headers, 'Content-Type': 'application/json' }
}
// If connectionError occurs, a timestamp equal to now +
// `backoffTimedelta` is assigned to the object.
// Next time the function is called, it either
// waits till the timestamp is passed or raises `TimeoutError`.
// If `ConnectionError` occurs two or more times in a row,
// the retry count is incremented and the new timestamp is calculated
// as now + the `backoffTimedelta`
// The `backoffTimedelta` is the minimum between the default delay
// multiplied by two to the power of the
// number of retries or timeout/2 or 10. See Transport class for that
// If a request is successful, the backoff timestamp is removed,
// the retry count is back to zero.
const backoffTimedelta = this.getBackoffTimedelta()
if (timeout != null && timeout < backoffTimedelta) {
const errorObject = {
message: 'TimeoutError'
}
throw errorObject
}
if (backoffTimedelta > 0) {
await Request.sleep(backoffTimedelta)
}
const requestTimeout = timeout ? timeout - backoffTimedelta : timeout
return baseRequest(apiUrl, requestConfig, requestTimeout)
.then((res) => {
this.connectionError = null
return res.json()
})
.catch(err => {
// ConnectionError
this.connectionError = err
})
.finally(() => {
this.updateBackoffTime(maxBackoffTime)
})
}
updateBackoffTime(maxBackoffTime) {
if (!this.connectionError) {
this.retries = 0
this.backoffTime = null
} else if (this.connectionError.message === ERROR_FROM_SERVER) {
// If status is not a 2xx (based on Response.ok), throw error
this.retries = 0
this.backoffTime = null
throw this.connectionError
} else {
// Timeout or no connection could be stablished
const backoffTimedelta = Math.min(BACKOFF_DELAY * (2 ** this.retries), maxBackoffTime)
this.backoffTime = Date.now() + backoffTimedelta
this.retries += 1
if (this.connectionError.message === 'TimeoutError') {
throw this.connectionError
}
}
}
getBackoffTimedelta() {
if (!this.backoffTime) {
return 0
}
return (this.backoffTime - Date.now())
}
static sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
if (requestConfig.jsonBody) {
requestConfig.headers = Object.assign({}, requestConfig.headers, {
'Content-Type': 'application/json'
})
}
if (!url) {
return Promise.reject(new Error('Request was not given a url.'))
}
return baseRequest(apiUrl, requestConfig)
.then(res => (onlyJsonResponse ? res.json() : { json: res.json(), url: res.url }))
.catch(err => {
console.error(err)
throw err
})
}

View File

@ -1,41 +1,36 @@
// 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
import coreIncludes from 'core-js/library/fn/array/includes'
import coreObjectEntries from 'core-js/library/fn/object/entries'
import 'core-js/features/array/includes'
import 'core-js/features/object/entries'
/**
* @private
* Abstraction for selectFromObject and omitFromObject for DRYness.
* Set isInclusion to true if the filter should be for including the filtered items (ie. selecting
* only them vs omitting only them).
*/
function filterFromObject(obj, filter, { isInclusion = true } = {}) {
if (filter && Array.isArray(filter)) {
return applyFilterOnObject(obj, isInclusion ? (val => filter.includes(val))
: (val => !filter.includes(val)))
return applyFilterOnObject(obj, isInclusion ? ((_, key) => coreIncludes(filter, key))
: ((_, key) => !coreIncludes(filter, key)))
} else if (filter && typeof filter === 'function') {
// Flip the filter fn's return if it's for inclusion
return applyFilterOnObject(obj, isInclusion ? filter
: (...args) => !filter(...args))
} else {
throw new Error('The given filter is not an array or function. Filter aborted')
throw new Error('The given filter is not an array or function. Exclude aborted')
}
}
/**
* @private
* Returns a filtered copy of the given object's own enumerable properties (no inherited
* properties), keeping any keys that pass the given filter function.
*/
function applyFilterOnObject(obj, filterFn) {
if (filterFn == null) {
return { ...obj }
return Object.assign({}, obj)
}
const filteredObj = {}
Object.entries(obj).forEach(([key, val]) => {
coreObjectEntries(obj).forEach(([key, val]) => {
if (filterFn(val, key)) {
filteredObj[key] = val
}
@ -45,26 +40,24 @@ function applyFilterOnObject(obj, filterFn) {
}
/**
* @private
* Similar to lodash's _.pick(), this returns a copy of the given object's
* own and inherited enumerable properties, selecting only the keys in
* the given array or whose value pass the given filter function.
* @param {Object} obj Source object
* @param {Array|function} filter Array of key names to select or function to invoke per iteration
* @return {Object} The new object
* @param {object} obj Source object
* @param {array|function} filter Array of key names to select or function to invoke per iteration
* @return {object} The new object
*/
function selectFromObject(obj, filter) {
return filterFromObject(obj, filter)
}
/**
* @private
* Glorified selectFromObject. Takes an object and returns a filtered shallow copy that strips out
* any properties that are falsy (including coercions, ie. undefined, null, '', 0, ...).
* Does not modify the passed in object.
*
* @param {Object} obj Javascript object
* @return {Object} Sanitized Javascript object
* @param {object} obj Javascript object
* @return {object} Sanitized Javascript object
*/
export default function sanitize(obj) {
return selectFromObject(obj, (val) => !!val)

View File

@ -1,14 +1,8 @@
// 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
/* eslint-disable camelcase */
import { sha3_256 } from 'js-sha3'
import sha3 from 'js-sha3'
export default function sha256Hash(data) {
return sha3_256
return sha3.sha3_256
.create()
.update(data)
.hex()
}
/* eslint-enable camelcase */

View File

@ -1,13 +1,9 @@
// 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
import 'core-js/features/object/entries'
import coreObjectEntries from 'core-js/library/fn/object/entries'
import decamelize from 'decamelize'
import queryString from 'query-string'
/**
* @private
* imported from https://github.com/bigchaindb/js-utility-belt/
*
* Takes a key-value dictionary (ie. object) and converts it to a query-parameter string that you
@ -28,7 +24,7 @@ import queryString from 'query-string'
*
* ?page=1&page_size=10
*
* @param {Object} obj Query params dictionary
* @param {object} obj Query params dictionary
* @param {function} [transform=decamelize] Transform function for each of the param keys
* @return {string} Query param string
*/
@ -37,7 +33,7 @@ export default function stringifyAsQueryParam(obj, transform = decamelize) {
return ''
}
const transformedKeysObj = Object.entries(obj).reduce((paramsObj, [key, value]) => {
const transformedKeysObj = coreObjectEntries(obj).reduce((paramsObj, [key, value]) => {
paramsObj[transform(key)] = value
return paramsObj
}, {})

View File

@ -1,287 +0,0 @@
// 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
import stableStringify from 'json-stable-stringify'
import clone from 'clone'
import base58 from 'bs58'
import { Ed25519Sha256, PreimageSha256, ThresholdSha256 } from 'crypto-conditions'
import ccJsonify from './utils/ccJsonify'
import sha256Hash from './sha256Hash'
/**
* Construct Transactions
*/
export default class Transaction {
/**
* Canonically serializes a transaction into a string by sorting the keys
* @param {Object} (transaction)
* @return {string} a canonically serialized Transaction
*/
static serializeTransactionIntoCanonicalString(transaction) {
// BigchainDB signs fulfillments by serializing transactions into a
// "canonical" format where
const tx = clone(transaction)
// TODO: set fulfillments to null
// Sort the keys
return stableStringify(tx, (a, b) => (a.key > b.key ? 1 : -1))
}
static makeInputTemplate(publicKeys = [], fulfills = null, fulfillment = null) {
return {
fulfillment,
fulfills,
'owners_before': publicKeys,
}
}
static makeTransactionTemplate() {
const txTemplate = {
id: null,
operation: null,
outputs: [],
inputs: [],
metadata: null,
asset: null,
version: '2.0',
}
return txTemplate
}
static makeTransaction(operation, asset, metadata = null, outputs = [], inputs = []) {
const tx = Transaction.makeTransactionTemplate()
tx.operation = operation
tx.asset = asset
tx.metadata = metadata
tx.inputs = inputs
tx.outputs = outputs
return tx
}
/**
* Generate a `CREATE` transaction holding the `asset`, `metadata`, and `outputs`, to be signed by
* the `issuers`.
* @param {Object} asset Created asset's data
* @param {Object} metadata Metadata for the 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 `CREATE` Transactions, this should usually just be a list of
* Outputs wrapping Ed25519 Conditions generated from the issuers' public
* keys (so that the issuers are the recipients of the created asset).
* @param {...string[]} issuers Public key of one or more issuers to the asset being created by this
* Transaction.
* Note: Each of the private keys corresponding to the given public
* keys MUST be used later (and in the same order) when signing the
* Transaction (`signTransaction()`).
* @returns {Object} Unsigned transaction -- make sure to call signTransaction() on it before
* sending it off!
*/
static makeCreateTransaction(asset, metadata, outputs, ...issuers) {
const assetDefinition = {
data: asset || null,
}
const inputs = issuers.map((issuer) => Transaction.makeInputTemplate([issuer]))
return Transaction.makeTransaction('CREATE', assetDefinition, metadata, outputs, inputs)
}
/**
* Create an Ed25519 Cryptocondition from an Ed25519 public key
* to put into an Output of a Transaction
* @param {string} publicKey base58 encoded Ed25519 public key for the recipient of the Transaction
* @param {boolean} [json=true] If true returns a json object otherwise a crypto-condition type
* @returns {Object} Ed25519 Condition (that will need to wrapped in an Output)
*/
static makeEd25519Condition(publicKey, json = true) {
const publicKeyBuffer = base58.decode(publicKey)
const ed25519Fulfillment = new Ed25519Sha256()
ed25519Fulfillment.setPublicKey(publicKeyBuffer)
return json ? ccJsonify(ed25519Fulfillment) : ed25519Fulfillment
}
/**
* Create an Output from a Condition.
* Note: Assumes the given Condition was generated from a
* single public key (e.g. a Ed25519 Condition)
* @param {Object} condition Condition (e.g. a Ed25519 Condition from `makeEd25519Condition()`)
* @param {string} amount Amount of the output
* @returns {Object} An Output usable in a Transaction
*/
static makeOutput(condition, amount = '1') {
if (typeof amount !== 'string') {
throw new TypeError('`amount` must be of type string')
}
const publicKeys = []
const getPublicKeys = details => {
if (details.type === 'ed25519-sha-256') {
if (!publicKeys.includes(details.public_key)) {
publicKeys.push(details.public_key)
}
} else if (details.type === 'threshold-sha-256') {
details.subconditions.map(getPublicKeys)
}
}
getPublicKeys(condition.details)
return {
condition,
amount,
public_keys: publicKeys,
}
}
/**
* Create a Preimage-Sha256 Cryptocondition from a secret to put into an Output of a Transaction
* @param {string} preimage Preimage to be hashed and wrapped in a crypto-condition
* @param {boolean} [json=true] If true returns a json object otherwise a crypto-condition type
* @returns {Object} Preimage-Sha256 Condition (that will need to wrapped in an Output)
*/
static makeSha256Condition(preimage, json = true) {
const sha256Fulfillment = new PreimageSha256()
sha256Fulfillment.setPreimage(Buffer.from(preimage))
return json ? ccJsonify(sha256Fulfillment) : sha256Fulfillment
}
/**
* Create an Sha256 Threshold Cryptocondition from threshold to put into an Output of a Transaction
* @param {number} threshold
* @param {Array} [subconditions=[]]
* @param {boolean} [json=true] If true returns a json object otherwise a crypto-condition type
* @returns {Object} Sha256 Threshold Condition (that will need to wrapped in an Output)
*/
static makeThresholdCondition(threshold, subconditions = [], json = true) {
const thresholdCondition = new ThresholdSha256()
thresholdCondition.setThreshold(threshold)
subconditions.forEach((subcondition) => {
// TODO: add support for Condition
thresholdCondition.addSubfulfillment(subcondition)
// ? Should be thresholdCondition.addSubcondition(subcondition)
})
return json ? ccJsonify(thresholdCondition) : thresholdCondition
}
/**
* 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
* @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()`).
* @returns {Object} Unsigned transaction -- make sure to call signTransaction() on it before
* sending it off!
*/
// TODO:
// - Make `metadata` optional argument
static makeTransferTransaction(
unspentOutputs,
outputs,
metadata
) {
const inputs = unspentOutputs.map((unspentOutput) => {
const { tx, outputIndex } = { tx: unspentOutput.tx, outputIndex: unspentOutput.output_index }
const fulfilledOutput = tx.outputs[outputIndex]
const transactionLink = {
output_index: outputIndex,
transaction_id: tx.id,
}
return Transaction.makeInputTemplate(fulfilledOutput.public_keys, transactionLink)
})
const assetLink = {
id: unspentOutputs[0].tx.operation === 'CREATE' ? unspentOutputs[0].tx.id
: unspentOutputs[0].tx.asset.id
}
return Transaction.makeTransaction('TRANSFER', assetLink, metadata, outputs, inputs)
}
/**
* Sign the given `transaction` with the given `privateKey`s, returning a new copy of `transaction`
* that's been signed.
* Note: Only generates Ed25519 Fulfillments. Thresholds and other types of Fulfillments are left as
* an exercise for the user.
* @param {Object} transaction Transaction to sign. `transaction` is not modified.
* @param {...string} privateKeys Private keys associated with the issuers of the `transaction`.
* Looped through to iteratively sign any Input Fulfillments found in
* the `transaction`.
* @returns {Object} The signed version of `transaction`.
*/
static signTransaction(transaction, ...privateKeys) {
const signedTx = clone(transaction)
const serializedTransaction =
Transaction.serializeTransactionIntoCanonicalString(transaction)
signedTx.inputs.forEach((input, index) => {
const privateKey = privateKeys[index]
const privateKeyBuffer = base58.decode(privateKey)
const transactionUniqueFulfillment = input.fulfills ? serializedTransaction
.concat(input.fulfills.transaction_id)
.concat(input.fulfills.output_index) : serializedTransaction
const transactionHash = sha256Hash(transactionUniqueFulfillment)
const ed25519Fulfillment = new Ed25519Sha256()
ed25519Fulfillment.sign(Buffer.from(transactionHash, 'hex'), privateKeyBuffer)
const fulfillmentUri = ed25519Fulfillment.serializeUri()
input.fulfillment = fulfillmentUri
})
const serializedSignedTransaction =
Transaction.serializeTransactionIntoCanonicalString(signedTx)
signedTx.id = sha256Hash(serializedSignedTransaction)
return signedTx
}
/**
* Delegate signing of the given `transaction` returning a new copy of `transaction`
* that's been signed.
* @param {Object} transaction Transaction to sign. `transaction` is not modified.
* @param {Function} signFn Function signing the transaction, expected to return the fulfillment.
* @returns {Object} The signed version of `transaction`.
*/
static delegateSignTransaction(transaction, signFn) {
const signedTx = clone(transaction)
const serializedTransaction =
Transaction.serializeTransactionIntoCanonicalString(transaction)
signedTx.inputs.forEach((input, index) => {
const fulfillmentUri = signFn(serializedTransaction, input, index)
input.fulfillment = fulfillmentUri
})
const serializedSignedTransaction = Transaction.serializeTransactionIntoCanonicalString(signedTx)
signedTx.id = sha256Hash(serializedSignedTransaction)
return signedTx
}
/**
* Delegate signing of the given `transaction` returning a new copy of `transaction`
* that's been signed.
* @param {Object} transaction Transaction to sign. `transaction` is not modified.
* @param {Function} signFn Function signing the transaction, expected to resolve the fulfillment.
* @returns {Promise<Object>} The signed version of `transaction`.
*/
static async delegateSignTransactionAsync(transaction, signFn) {
const signedTx = clone(transaction)
const serializedTransaction =
Transaction.serializeTransactionIntoCanonicalString(transaction)
await Promise.all(signedTx.inputs.map(async (input, index) => {
const fulfillmentUri = await signFn(serializedTransaction, input, index)
input.fulfillment = fulfillmentUri
}))
const serializedSignedTransaction = Transaction.serializeTransactionIntoCanonicalString(signedTx)
signedTx.id = sha256Hash(serializedSignedTransaction)
return signedTx
}
}

View File

@ -0,0 +1,10 @@
import serializeTransactionIntoCanonicalString from './serializeTransactionIntoCanonicalString'
import sha256Hash from '../sha256Hash'
export default function hashTransaction(transaction) {
// Safely remove any tx id from the given transaction for hashing
const tx = { ...transaction }
delete tx.id
return sha256Hash(serializeTransactionIntoCanonicalString(tx))
}

11
src/transaction/index.js Normal file
View File

@ -0,0 +1,11 @@
export makeEd25519Condition from './makeEd25519Condition'
export makeSha256Condition from './makeSha256Condition'
export makeThresholdCondition from './makeThresholdCondition'
export makeCreateTransaction from './makeCreateTransaction'
export makeOutput from './makeOutput'
export makeTransaction from './makeTransaction'
export makeTransferTransaction from './makeTransferTransaction'
export serializeTransactionIntoCanonicalString from './serializeTransactionIntoCanonicalString'
export signTransaction from './signTransaction'
export ccJsonLoad from './utils/ccJsonLoad'
export ccJsonify from './utils/ccJsonify'

View File

@ -0,0 +1,31 @@
import makeInputTemplate from './makeInputTemplate'
import makeTransaction from './makeTransaction'
/**
* @public
* Generate a `CREATE` transaction holding the `asset`, `metadata`, and `outputs`, to be signed by
* the `issuers`.
* @param {object} asset Created asset's data
* @param {object} metadata Metadata for the 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 `CREATE` Transactions, this should usually just be a list of
* Outputs wrapping Ed25519 Conditions generated from the issuers' public
* keys (so that the issuers are the recipients of the created asset).
* @param {...string[]} issuers Public key of one or more issuers to the asset being created by this
* Transaction.
* Note: Each of the private keys corresponding to the given public
* keys MUST be used later (and in the same order) when signing the
* Transaction (`signTransaction()`).
* @returns {object} Unsigned transaction -- make sure to call signTransaction() on it before
* sending it off!
*/
export default function makeCreateTransaction(asset, metadata, outputs, ...issuers) {
const assetDefinition = {
'data': asset || null,
}
const inputs = issuers.map((issuer) => makeInputTemplate([issuer]))
return makeTransaction('CREATE', assetDefinition, metadata, outputs, inputs)
}

View File

@ -0,0 +1,27 @@
import { Buffer } from 'buffer'
import base58 from 'bs58'
import cc from 'five-bells-condition'
import ccJsonify from './utils/ccJsonify'
/**
* @public
* Create an Ed25519 Cryptocondition from an Ed25519 public key to put into an Output of a Transaction
* @param {string} publicKey base58 encoded Ed25519 public key for the recipient of the Transaction
* @param {boolean} [json=true] If true returns a json object otherwise a crypto-condition type
* @returns {object} Ed25519 Condition (that will need to wrapped in an Output)
*/
export default function makeEd25519Condition(publicKey, json = true) {
const publicKeyBuffer = new Buffer(base58.decode(publicKey))
const ed25519Fulfillment = new cc.Ed25519Sha256()
ed25519Fulfillment.setPublicKey(publicKeyBuffer)
if (json) {
return ccJsonify(ed25519Fulfillment)
}
return ed25519Fulfillment
}

View File

@ -0,0 +1,7 @@
export default function makeInputTemplate(publicKeys = [], fulfills = null, fulfillment = null) {
return {
fulfillment,
fulfills,
'owners_before': publicKeys,
}
}

View File

@ -0,0 +1,29 @@
/**
* @public
* Create an Output from a Condition.
* Note: Assumes the given Condition was generated from a single public key (e.g. a Ed25519 Condition)
* @param {object} condition Condition (e.g. a Ed25519 Condition from `makeEd25519Condition()`)
* @param {string} amount Amount of the output
* @returns {object} An Output usable in a Transaction
*/
export default function makeOutput(condition, amount = '1') {
if (typeof amount !== 'string') {
throw new TypeError('`amount` must be of type string')
}
const publicKeys = []
const getPublicKeys = details => {
if (details.type === 'ed25519-sha-256') {
if (!publicKeys.includes(details.public_key)) {
publicKeys.push(details.public_key)
}
} else if (details.type === 'threshold-sha-256') {
details.subconditions.map(getPublicKeys)
}
}
getPublicKeys(condition.details)
return {
condition,
'amount': amount,
'public_keys': publicKeys,
}
}

View File

@ -0,0 +1,23 @@
import { Buffer } from 'buffer'
import cc from 'five-bells-condition'
import ccJsonify from './utils/ccJsonify'
/**
* @public
* Create a Preimage-Sha256 Cryptocondition from a secret to put into an Output of a Transaction
* @param {string} preimage Preimage to be hashed and wrapped in a crypto-condition
* @param {boolean} [json=true] If true returns a json object otherwise a crypto-condition type
* @returns {object} Preimage-Sha256 Condition (that will need to wrapped in an Output)
*/
export default function makeSha256Condition(preimage, json = true) {
const sha256Fulfillment = new cc.PreimageSha256()
sha256Fulfillment.preimage = new Buffer(preimage)
if (json) {
return ccJsonify(sha256Fulfillment)
}
return sha256Fulfillment
}

View File

@ -0,0 +1,28 @@
import cc from 'five-bells-condition'
import ccJsonify from './utils/ccJsonify'
/**
* @public
* Create an Sha256 Threshold Cryptocondition from threshold to put into an Output of a Transaction
* @param {number} threshold
* @param {Array} [subconditions=[]]
* @param {boolean} [json=true] If true returns a json object otherwise a crypto-condition type
* @returns {object} Sha256 Threshold Condition (that will need to wrapped in an Output)
*/
export default function makeThresholdCondition(threshold, subconditions = [], json = true) {
const thresholdCondition = new cc.ThresholdSha256()
thresholdCondition.threshold = threshold
subconditions.forEach((subcondition) => {
// TODO: add support for Condition and URIs
thresholdCondition.addSubfulfillment(subcondition)
})
if (json) {
return ccJsonify(thresholdCondition)
}
return thresholdCondition
}

View File

@ -0,0 +1,28 @@
import hashTransaction from './hashTransaction'
function makeTransactionTemplate() {
return {
'id': null,
'operation': null,
'outputs': [],
'inputs': [],
'metadata': null,
'asset': null,
'version': '1.0',
}
}
export default function makeTransaction(operation, asset, metadata = null, outputs = [], inputs = []) {
const tx = makeTransactionTemplate()
tx.operation = operation
tx.asset = asset
tx.metadata = metadata
tx.inputs = inputs
tx.outputs = outputs
// Hashing must be done after, as the hash is of the Transaction (up to now)
tx.id = hashTransaction(tx)
return tx
}

View File

@ -0,0 +1,49 @@
import makeInputTemplate from './makeInputTemplate'
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
* @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()`).
* @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,
outputs,
...outputIndices
) {
const inputs = outputIndices.map((outputIndex) => {
const fulfilledOutput = unspentTransaction.outputs[outputIndex]
const transactionLink = {
'output_index': outputIndex,
'transaction_id': unspentTransaction.id,
}
return makeInputTemplate(fulfilledOutput.public_keys, transactionLink)
})
const assetLink = {
'id': unspentTransaction.operation === 'CREATE' ? unspentTransaction.id
: unspentTransaction.asset.id
}
return makeTransaction('TRANSFER', assetLink, metadata, outputs, inputs)
}

View File

@ -0,0 +1,18 @@
import stableStringify from 'json-stable-stringify'
import clone from 'clone'
/**
* @public
* Canonically serializes a transaction into a string by sorting the keys
* @param {object} (transaction)
* @return {string} a canonically serialized Transaction
*/
export default function serializeTransactionIntoCanonicalString(transaction) {
// BigchainDB signs fulfillments by serializing transactions into a
// "canonical" format where
const tx = clone(transaction)
// TODO: set fulfillments to null
// Sort the keys
return stableStringify(tx, (a, b) => (a.key > b.key ? 1 : -1))
}

View File

@ -0,0 +1,35 @@
import { Buffer } from 'buffer'
import base58 from 'bs58'
import cc from 'five-bells-condition'
import clone from 'clone'
import serializeTransactionIntoCanonicalString from './serializeTransactionIntoCanonicalString'
/**
* @public
* Sign the given `transaction` with the given `privateKey`s, returning a new copy of `transaction`
* that's been signed.
* Note: Only generates Ed25519 Fulfillments. Thresholds and other types of Fulfillments are left as
* an exercise for the user.
* @param {object} transaction Transaction to sign. `transaction` is not modified.
* @param {...string} privateKeys Private keys associated with the issuers of the `transaction`.
* Looped through to iteratively sign any Input Fulfillments found in
* the `transaction`.
* @returns {object} The signed version of `transaction`.
*/
export default function signTransaction(transaction, ...privateKeys) {
const signedTx = clone(transaction)
signedTx.inputs.forEach((input, index) => {
const privateKey = privateKeys[index]
const privateKeyBuffer = new Buffer(base58.decode(privateKey))
const serializedTransaction = serializeTransactionIntoCanonicalString(transaction)
const ed25519Fulfillment = new cc.Ed25519Sha256()
ed25519Fulfillment.sign(new Buffer(serializedTransaction), privateKeyBuffer)
const fulfillmentUri = ed25519Fulfillment.serializeUri()
input.fulfillment = fulfillmentUri
})
return signedTx
}

View File

@ -1,29 +1,26 @@
// 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
import { Buffer } from 'buffer'
import base58 from 'bs58'
import { Condition, Ed25519Sha256, ThresholdSha256 } from 'crypto-conditions'
import cc from 'five-bells-condition'
/**
* @public
* Loads a crypto-condition class (Fulfillment or Condition) from a BigchainDB JSON object
* @param {Object} conditionJson
* @param {object} conditionJson
* @returns {cc.Condition} Ed25519 Condition (that will need to wrapped in an Output)
*/
export default function ccJsonLoad(conditionJson) {
if ('hash' in conditionJson) {
const condition = new Condition()
condition.setTypeId(conditionJson.type_id)
condition.setSubtypes(conditionJson.bitmask)
condition.setHash(base58.decode(conditionJson.hash))
// TODO: fix this, maxFulfillmentLength cannot be set in CryptoCondition lib
const condition = new cc.Condition()
condition.type = conditionJson.type_id
condition.bitmask = conditionJson.bitmask
condition.hash = new Buffer(base58.decode(conditionJson.hash))
condition.maxFulfillmentLength = parseInt(conditionJson.max_fulfillment_length, 10)
return condition
} else {
let fulfillment
if (conditionJson.type === 'threshold-sha-256') {
fulfillment = new ThresholdSha256()
fulfillment = new cc.ThresholdSha256()
fulfillment.threshold = conditionJson.threshold
conditionJson.subconditions.forEach((subconditionJson) => {
const subcondition = ccJsonLoad(subconditionJson)
@ -36,8 +33,8 @@ export default function ccJsonLoad(conditionJson) {
}
if (conditionJson.type === 'ed25519-sha-256') {
fulfillment = new Ed25519Sha256()
fulfillment.setPublicKey(base58.decode(conditionJson.public_key))
fulfillment = new cc.Ed25519Sha256()
fulfillment.publicKey = new Buffer(base58.decode(conditionJson.public_key))
}
return fulfillment
}

View File

@ -1,13 +1,10 @@
// 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
import base58 from 'bs58'
/**
* @public
* Serializes a crypto-condition class (Condition or Fulfillment) into a BigchainDB-compatible JSON
* @param {cc.Fulfillment} fulfillment base58 encoded Ed25519 public key for recipient of the Transaction
* @returns {Object} Ed25519 Condition (that will need to wrapped in an Output)
* @returns {object} Ed25519 Condition (that will need to wrapped in an Output)
*/
export default function ccJsonify(fulfillment) {
let conditionUri
@ -19,8 +16,8 @@ export default function ccJsonify(fulfillment) {
}
const jsonBody = {
details: {},
uri: conditionUri,
'details': {},
'uri': conditionUri,
}
if (fulfillment.getTypeId() === 0) {
@ -35,15 +32,15 @@ export default function ccJsonify(fulfillment) {
if (fulfillment.getTypeId() === 2) {
return {
details: {
type: 'threshold-sha-256',
threshold: fulfillment.threshold,
subconditions: fulfillment.subconditions.map((subcondition) => {
'details': {
'type': 'threshold-sha-256',
'threshold': fulfillment.threshold,
'subconditions': fulfillment.subconditions.map((subcondition) => {
const subconditionJson = ccJsonify(subcondition.body)
return subconditionJson.details
})
},
uri: conditionUri,
'uri': conditionUri,
}
}

View File

@ -1,64 +0,0 @@
// 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
import Request from './request'
/**
*
* @private
* If initialized with ``>1`` nodes, the driver will send successive
* requests to different nodes in a round-robin fashion (this will be
* customizable in the future).
*/
export default class Transport {
constructor(nodes, timeout) {
this.connectionPool = []
this.timeout = timeout
// the maximum backoff time is 10 seconds
this.maxBackoffTime = timeout ? timeout / 2 : 10000
nodes.forEach(node => {
this.connectionPool.push(new Request(node))
})
}
// Select the connection with the earliest backoff time, in case of a tie,
// prefer the one with the smaller list index
pickConnection() {
let connection = this.connectionPool[0]
this.connectionPool.forEach(conn => {
// 0 the lowest value is the time for Thu Jan 01 1970 01:00:00 GMT+0100 (CET)
conn.backoffTime = conn.backoffTime ? conn.backoffTime : 0
connection = (conn.backoffTime < connection.backoffTime) ? conn : connection
})
return connection
}
async forwardRequest(path, headers) {
let response
let connection
// A new request will be executed until there is a valid response or timeout < 0
while (this.timeout >= 0) {
connection = this.pickConnection()
// Date in milliseconds
const startTime = Date.now()
// eslint-disable-next-line no-await-in-loop
response = await connection.request(
path,
headers,
this.timeout,
this.maxBackoffTime
)
const elapsed = Date.now() - startTime
if (connection.backoffTime > 0 && this.timeout > 0) {
this.timeout -= elapsed
} else {
// No connection error, the response is valid
return response
}
}
throw new Error('TimeoutError')
}
}

View File

@ -1,30 +0,0 @@
// 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
import test from 'ava'
import rewire from 'rewire'
const baseRequestFile = rewire('../../src/baseRequest.js')
const baseRequest = baseRequestFile.__get__('baseRequest')
const handleResponse = baseRequestFile.__get__('handleResponse')
test('HandleResponse does not throw error for response ok', t => {
const testObj = {
ok: true
}
const expected = testObj
const actual = handleResponse(testObj)
t.deepEqual(actual, expected)
})
test('baseRequest test query and vsprint', async t => {
const error = await t.throwsAsync(baseRequest('https://%s.com/', {
urlTemplateSpec: ['google'],
query: 'teapot'
}), { instanceOf: Error, message: 'HTTP Error: Requested page not reachable' })
t.is(error.requestURI, 'https://www.google.com/teapot')
t.is(error.status, '418 I\'m a Teapot')
})

View File

@ -1,130 +1,85 @@
// 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
import test from 'ava'
import sinon from 'sinon'
import {
Connection
} from '../../src'
import {
API_PATH
} from '../constants'
import * as request from '../../src/request' // eslint-disable-line
import { Connection } from '../../src'
const API_PATH = 'http://localhost:9984/api/v1/'
const conn = new Connection(API_PATH)
test('Payload thrown at incorrect API_PATH', async t => {
const path = 'http://localhost:9984/api/wrong/'
const connection = new Connection(path)
const target = {
message: 'HTTP Error: Requested page not reachable',
status: '404 NOT FOUND',
requestURI: 'http://localhost:9984/api/wrong/transactions/transactionId'
}
const error = await t.throwsAsync(connection.getTransaction('transactionId'), {
instanceOf: Error, message: target.message
})
t.is('ResponseError', error.name)
t.is(target.status, error.status)
t.is(target.requestURI, error.requestURI)
})
test('Generate API URLS', t => {
test('generate API URLS', t => {
const endpoints = {
'blocks': 'blocks',
'blocksDetail': 'blocks/%(blockHeight)s',
'blocksDetail': 'blocks/%(blockId)s',
'outputs': 'outputs',
'statuses': 'statuses',
'transactions': 'transactions',
'transactionsSync': 'transactions?mode=sync',
'transactionsAsync': 'transactions?mode=async',
'transactionsCommit': 'transactions?mode=commit',
'transactionsDetail': 'transactions/%(transactionId)s',
'assets': 'assets',
}
Object.keys(endpoints).forEach(endpointName => {
const url = Connection.getApiUrls(endpointName)
const expected = endpoints[endpointName]
const url = conn.getApiUrls(endpointName)
const expected = API_PATH + endpoints[endpointName]
t.is(url, expected)
})
})
test('Normalize node from an object', t => {
const headers = {
custom: 'headers'
}
const node = {
endpoint: API_PATH,
test('Request with custom headers', t => {
const testConn = new Connection(API_PATH, { hello: 'world' })
const expectedOptions = {
headers: {
hello: 'world'
}
}
const expectedNode = {
'endpoint': API_PATH,
'headers': {
hello: 'world',
custom: 'headers'
}
}
t.deepEqual(Connection.normalizeNode(node, headers), expectedNode)
// request is read only, cannot be mocked?
sinon.spy(request, 'default')
testConn._req(API_PATH, { headers: { custom: 'headers' } })
t.truthy(request.default.calledWith(API_PATH, expectedOptions))
request.default.restore()
})
test('Normalize node from a string', t => {
const headers = {
custom: 'headers'
}
const expectedNode = {
'endpoint': API_PATH,
'headers': {
custom: 'headers'
}
}
t.deepEqual(Connection.normalizeNode(API_PATH, headers), expectedNode)
})
test('Request with custom headers', t => {
const testConn = new Connection(API_PATH, {
hello: 'world'
})
const expectedOptions = {
headers: {
custom: 'headers'
}
}
const PATH = 'blocks'
testConn.transport.forwardRequest = sinon.spy()
testConn._req(PATH, {
headers: {
custom: 'headers'
}
})
t.truthy(testConn.transport.forwardRequest.calledWith(PATH, expectedOptions))
})
test('Get block for a block id', t => {
const expectedPath = 'path'
const blockHeight = 'abc'
const blockId = 'abc'
conn._req = sinon.spy()
Connection.getApiUrls = sinon.stub().returns(expectedPath)
conn.getApiUrls = sinon.stub().returns(expectedPath)
conn.getBlock(blockHeight)
conn.getBlock(blockId)
t.truthy(conn._req.calledWith(
expectedPath,
{ urlTemplateSpec: { blockHeight } }
{ 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 } }
))
})
test('Get transaction for a transaction id', t => {
const expectedPath = 'path'
const transactionId = 'abc'
conn._req = sinon.spy()
Connection.getApiUrls = sinon.stub().returns(expectedPath)
conn.getApiUrls = sinon.stub().returns(expectedPath)
conn.getTransaction(transactionId)
t.truthy(conn._req.calledWith(
@ -133,31 +88,35 @@ 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()
Connection.getApiUrls = sinon.stub().returns(expectedPath)
conn.getApiUrls = sinon.stub().returns(expectedPath)
conn.listBlocks(transactionId)
conn.listBlocks(transactionId, status)
t.truthy(conn._req.calledWith(
expectedPath,
{
query: {
transaction_id: transactionId,
status
}
}
))
})
test('Get list of transactions for an asset id', t => {
const expectedPath = 'path'
const assetId = 'abc'
const operation = 'operation'
conn._req = sinon.spy()
Connection.getApiUrls = sinon.stub().returns(expectedPath)
conn.getApiUrls = sinon.stub().returns(expectedPath)
conn.listTransactions(assetId, operation)
t.truthy(conn._req.calledWith(
@ -171,12 +130,13 @@ test('Get list of transactions for an asset id', t => {
))
})
test('Get outputs for a public key and no spent flag', t => {
const expectedPath = 'path'
const publicKey = 'publicKey'
conn._req = sinon.spy()
Connection.getApiUrls = sinon.stub().returns(expectedPath)
conn.getApiUrls = sinon.stub().returns(expectedPath)
conn.listOutputs(publicKey)
t.truthy(conn._req.calledWith(
@ -185,13 +145,14 @@ test('Get outputs for a public key and no spent flag', t => {
))
})
test('Get outputs for a public key and spent=false', t => {
const expectedPath = 'path'
const publicKey = 'publicKey'
const spent = false
conn._req = sinon.spy()
Connection.getApiUrls = sinon.stub().returns(expectedPath)
conn.getApiUrls = sinon.stub().returns(expectedPath)
conn.listOutputs(publicKey, spent)
t.truthy(conn._req.calledWith(
@ -200,13 +161,14 @@ test('Get outputs for a public key and spent=false', t => {
))
})
test('Get outputs for a public key and spent=true', t => {
const expectedPath = 'path'
const publicKey = 'publicKey'
const spent = true
conn._req = sinon.spy()
Connection.getApiUrls = sinon.stub().returns(expectedPath)
conn.getApiUrls = sinon.stub().returns(expectedPath)
conn.listOutputs(publicKey, spent)
t.truthy(conn._req.calledWith(
@ -215,30 +177,32 @@ test('Get outputs for a public key and spent=true', t => {
))
})
test('Get votes for a block id', t => {
const expectedPath = 'path'
const blockId = 'abc'
conn._req = sinon.spy()
conn.getApiUrls = sinon.stub().returns(expectedPath)
conn.listVotes(blockId)
t.truthy(conn._req.calledWith(
expectedPath,
{ query: { block_id: blockId } }
))
})
test('Get asset for text', t => {
const expectedPath = 'path'
const search = 'abc'
conn._req = sinon.spy()
Connection.getApiUrls = sinon.stub().returns(expectedPath)
conn.getApiUrls = sinon.stub().returns(expectedPath)
conn.searchAssets(search)
t.truthy(conn._req.calledWith(
expectedPath,
{ query: { search, limit: 10 } }
))
})
test('Get metadata for text', t => {
const expectedPath = 'path'
const search = 'abc'
conn._req = sinon.spy()
Connection.getApiUrls = sinon.stub().returns(expectedPath)
conn.searchMetadata(search)
t.truthy(conn._req.calledWith(
expectedPath,
{ query: { search, limit: 10 } }
{ query: { search } }
))
})

View File

@ -1,16 +1,9 @@
// 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
import { createHash } from 'crypto'
import base58 from 'bs58'
import { Ed25519Sha256 } from 'crypto-conditions'
import test from 'ava'
import { Transaction, Ed25519Keypair } from '../src'
// TODO: Find out if ava has something like conftest, if so put this there.
// 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' }
@ -24,30 +17,16 @@ export const createTx = Transaction.makeCreateTransaction(
alice.publicKey
)
export const transferTx = Transaction.makeTransferTransaction(
[{ tx: createTx, output_index: 0 }],
createTx,
metaData,
[aliceOutput],
metaData
0
)
export const bob = new Ed25519Keypair()
export const bobCondition = Transaction.makeEd25519Condition(bob.publicKey)
export const bobOutput = Transaction.makeOutput(bobCondition)
export function delegatedSignTransaction(...keyPairs) {
return function sign(serializedTransaction, input) {
const transactionUniqueFulfillment = input.fulfills ? serializedTransaction
.concat(input.fulfills.transaction_id)
.concat(input.fulfills.output_index) : serializedTransaction
const transactionHash = createHash('sha3-256').update(transactionUniqueFulfillment).digest()
const filteredKeyPairs = keyPairs.filter(
({ publicKey }) => input.owners_before.includes(publicKey)
)
const ed25519Fulfillment = new Ed25519Sha256()
filteredKeyPairs.forEach(keyPair => {
const privateKey = Buffer.from(base58.decode(keyPair.privateKey))
ed25519Fulfillment.sign(transactionHash, privateKey)
})
return ed25519Fulfillment.serializeUri()
}
}
// TODO: https://github.com/avajs/ava/issues/1190
test('', () => 'dirty hack. TODO: Exclude this file from being run by ava')

View File

@ -1,22 +1,19 @@
// 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
import test from 'ava'
import { Ed25519Keypair, Transaction, Connection } from '../../src'
import {
API_PATH,
alice,
aliceCondition,
aliceOutput,
bob,
bobOutput,
asset,
metaData,
delegatedSignTransaction
metaData
} from '../constants'
const API_PATH = 'http://localhost:9984/api/v1/'
test('Keypair is created', t => {
const keyPair = new Ed25519Keypair()
@ -24,8 +21,12 @@ test('Keypair is created', t => {
t.truthy(keyPair.privateKey)
})
test('Valid CREATE transaction with default node', t => {
const conn = new Connection()
// TODO: The following tests are a bit messy currently, please do:
//
// - tidy up dependency on `pollStatusAndFetchTransaction`
test('Valid CREATE transaction', t => {
const conn = new Connection(API_PATH)
const tx = Transaction.makeCreateTransaction(
asset(),
@ -36,40 +37,10 @@ test('Valid CREATE transaction with default node', t => {
const txSigned = Transaction.signTransaction(tx, alice.privateKey)
return conn.postTransaction(txSigned)
.then(resTx => {
t.truthy(resTx)
})
})
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(({ id }) => conn.pollStatusAndFetchTransaction(id))
.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)
.then(resTx => t.truthy(resTx))
})
test('Valid TRANSFER transaction with single Ed25519 input', t => {
const conn = new Connection(API_PATH)
@ -84,22 +55,26 @@ test('Valid TRANSFER transaction with single Ed25519 input', t => {
alice.privateKey
)
return conn.postTransactionCommit(createTxSigned)
return conn.postTransaction(createTxSigned)
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
.then(() => {
const transferTx = Transaction.makeTransferTransaction(
[{ tx: createTxSigned, output_index: 0 }],
createTxSigned,
metaData,
[aliceOutput],
metaData
0
)
const transferTxSigned = Transaction.signTransaction(
transferTx,
alice.privateKey
)
return conn.postTransactionCommit(transferTxSigned)
return conn.postTransaction(transferTxSigned)
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
.then(resTx => t.truthy(resTx))
})
})
test('Valid TRANSFER transaction with multiple Ed25519 inputs', t => {
const conn = new Connection(API_PATH)
const createTx = Transaction.makeCreateTransaction(
@ -113,137 +88,27 @@ test('Valid TRANSFER transaction with multiple Ed25519 inputs', t => {
alice.privateKey
)
return conn.postTransactionCommit(createTxSigned)
return conn.postTransaction(createTxSigned)
.then(({ 'id': txId }) => conn.pollStatusAndFetchTransaction(txId))
.then(() => {
const transferTx = Transaction.makeTransferTransaction(
[{ tx: createTxSigned, output_index: 0 }, { tx: createTxSigned, output_index: 1 }],
createTxSigned,
metaData,
[Transaction.makeOutput(aliceCondition, '2')],
metaData
0,
1
)
const transferTxSigned = Transaction.signTransaction(
transferTx,
alice.privateKey,
bob.privateKey
)
return conn.postTransactionCommit(transferTxSigned)
return conn.postTransaction(transferTxSigned)
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
.then(resTx => t.truthy(resTx))
})
})
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
)
return conn.postTransactionCommit(createTxSigned)
.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
)
return conn.postTransactionCommit(transferTxSigned1)
.then(() => conn.postTransactionCommit(transferTxSigned2))
.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
)
return conn.postTransactionCommit(transferTxSignedMultipleInputs)
.then(resTx => t.truthy(resTx))
})
})
})
test('Valid CREATE transaction using delegateSign with default node', t => {
const conn = new Connection()
const tx = Transaction.makeCreateTransaction(
asset(),
metaData,
[aliceOutput],
alice.publicKey
)
const txSigned = Transaction.delegateSignTransaction(
tx,
delegatedSignTransaction(alice)
)
return conn.postTransaction(txSigned)
.then(resTx => {
t.truthy(resTx)
})
})
test('Valid TRANSFER transaction with multiple Ed25519 inputs using delegateSign', t => {
const conn = new Connection(API_PATH)
const createTx = Transaction.makeCreateTransaction(
asset(),
metaData,
[aliceOutput, bobOutput],
alice.publicKey
)
const createTxSigned = Transaction.signTransaction(
createTx,
alice.privateKey
)
return conn.postTransactionCommit(createTxSigned)
.then(() => {
const transferTx = Transaction.makeTransferTransaction(
[{ tx: createTxSigned, output_index: 0 }, { tx: createTxSigned, output_index: 1 }],
[Transaction.makeOutput(aliceCondition, '2')],
metaData
)
const transferTxSigned = Transaction.delegateSignTransaction(
transferTx,
delegatedSignTransaction(alice, bob)
)
return conn.postTransactionCommit(transferTxSigned)
.then(resTx => t.truthy(resTx))
})
})
test('Search for spent and unspent outputs of a given public key', t => {
const conn = new Connection(API_PATH)
@ -254,6 +119,7 @@ test('Search for spent and unspent outputs of a given public key', t => {
const trentCondition = Transaction.makeEd25519Condition(trent.publicKey)
const trentOutput = Transaction.makeOutput(trentCondition)
const createTx = Transaction.makeCreateTransaction(
asset(),
metaData,
@ -268,22 +134,26 @@ test('Search for spent and unspent outputs of a given public key', t => {
// We spent output 1 (of 0, 1)
const transferTx = Transaction.makeTransferTransaction(
[{ tx: createTxSigned, output_index: 1 }],
createTxSigned,
metaData,
[trentOutput],
metaData
1
)
const transferTxSigned = Transaction.signTransaction(
transferTx,
carol.privateKey,
)
return conn.postTransactionCommit(createTxSigned)
.then(() => conn.postTransactionCommit(transferTxSigned))
return conn.postTransaction(createTxSigned)
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
.then(() => conn.postTransaction(transferTxSigned))
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
.then(() => conn.listOutputs(carol.publicKey))
// 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)
const carol = new Ed25519Keypair()
@ -307,22 +177,26 @@ test('Search for unspent outputs for a given public key', t => {
// We spent output 1 (of 0, 1, 2)
const transferTx = Transaction.makeTransferTransaction(
[{ tx: createTxSigned, output_index: 1 }],
createTxSigned,
metaData,
[trentOutput],
metaData
1
)
const transferTxSigned = Transaction.signTransaction(
transferTx,
carol.privateKey,
)
return conn.postTransactionCommit(createTxSigned)
.then(() => conn.postTransactionCommit(transferTxSigned))
return conn.postTransaction(createTxSigned)
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
.then(() => conn.postTransaction(transferTxSigned))
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
// 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))
})
test('Search for spent outputs for a given public key', t => {
const conn = new Connection(API_PATH)
const carol = new Ed25519Keypair()
@ -346,22 +220,26 @@ test('Search for spent outputs for a given public key', t => {
// We spent output 1 (of 0, 1, 2)
const transferTx = Transaction.makeTransferTransaction(
[{ tx: createTxSigned, output_index: 1 }],
createTxSigned,
metaData,
[trentOutput],
metaData
1
)
const transferTxSigned = Transaction.signTransaction(
transferTx,
carol.privateKey,
)
return conn.postTransactionCommit(createTxSigned)
.then(() => conn.postTransactionCommit(transferTxSigned))
return conn.postTransaction(createTxSigned)
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
.then(() => conn.postTransaction(transferTxSigned))
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
// 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))
})
test('Search for an asset', t => {
const conn = new Connection(API_PATH)
@ -376,7 +254,8 @@ test('Search for an asset', t => {
alice.privateKey
)
return conn.postTransactionCommit(createTxSigned)
return conn.postTransaction(createTxSigned)
.then(({ id }) => conn.pollStatusAndFetchTransaction(id))
.then(() => conn.searchAssets(createTxSigned.asset.data.message))
.then(assets => t.truthy(
assets.pop(),
@ -384,27 +263,6 @@ test('Search for an asset', t => {
))
})
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
)
return conn.postTransactionCommit(createTxSigned)
.then(() => conn.searchMetadata(createTxSigned.metadata.message))
.then(assets => t.truthy(
assets.pop(),
createTxSigned.metadata.message
))
})
test('Search blocks containing a transaction', t => {
const conn = new Connection(API_PATH)
@ -420,13 +278,17 @@ test('Search blocks containing a transaction', t => {
alice.privateKey
)
return conn.postTransactionCommit(createTxSigned)
.then(({ id }) => conn.listBlocks(id))
.then(blockHeight => conn.getBlock(blockHeight.pop()))
.then(({ transactions }) => transactions.filter(({ id }) => id === createTxSigned.id))
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
))
.then(transactions => t.truthy(transactions.length === 1))
})
test('Search transaction containing an asset', t => {
const conn = new Connection(API_PATH)
@ -441,15 +303,13 @@ test('Search transaction containing an asset', t => {
alice.privateKey
)
return conn.postTransactionCommit(createTxSigned)
return conn.postTransaction(createTxSigned)
.then(({ id }) => conn.pollStatusAndFetchTransaction(id, 'CREATE'))
.then(({ id }) => conn.listTransactions(id))
.then(transactions => {
t.truthy(transactions.length === 1)
})
.then(transactions => t.truthy(transactions.length === 1))
})
test('Content-Type cannot be set', t => {
t.throws(() => new Connection(API_PATH, { 'Content-Type': 'application/json' }), {
instanceOf: Error
})
t.throws(() => new Connection(API_PATH, { 'Content-Type': 'application/json' }), Error)
})

View File

@ -1,26 +0,0 @@
// 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
import test from 'ava'
import Connection from '../../src/connection'
const conn = new Connection()
test('Ensure that BackoffTimedelta works properly', t => {
const req = conn.transport.pickConnection()
req.backoffTime = Date.now() + 50
const target = req.getBackoffTimedelta()
// The value should be close to 50
t.is(target > 45, true)
})
test('Ensure that updateBackoffTime throws and error on TimeoutError', async t => {
const req = conn.transport.pickConnection()
const errorMessage = 'TimeoutError'
req.connectionError = new Error(errorMessage)
t.throws(() => {
req.updateBackoffTime()
}, { instanceOf: Error, message: errorMessage })
})

View File

@ -1,55 +0,0 @@
// 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
import test from 'ava'
import rewire from 'rewire'
const sanitize = rewire('../../src/sanitize.js')
const applyFilterOnObject = sanitize.__get__('applyFilterOnObject')
const filterFromObject = sanitize.__get__('filterFromObject')
test('Ensure that null filter returns same object', t => {
const expected = { 'testObj': 'test' }
const actual = applyFilterOnObject({ 'testObj': 'test' }, null)
t.deepEqual(actual, expected)
})
test('Ensure function filter with isInclusion true works properly', t => {
const testObj = [true, false, undefined, '', 0, null]
const expected = { 0: true }
const actual = filterFromObject(testObj, (val) => !!val, { isInclusion: true })
t.deepEqual(actual, expected)
})
test('Ensure function filter with isInclusion false works properly', t => {
const testObj = [false, true, 1, 10, 'this will be removed as it is truthy']
const expected = { 0: false }
const actual = filterFromObject(testObj, (val) => !!val, { isInclusion: false })
t.deepEqual(actual, expected)
})
test('Ensure array filter with isInclusion true works properly', t => {
const testObj = [true, false, undefined, '', 0, null]
const expected = { 0: true }
const actual = filterFromObject(testObj, [true], { isInclusion: true })
t.deepEqual(actual, expected)
})
test('Ensure array filter with isInclusion false works properly', t => {
const testObj = [false, true, 1, 10]
const expected = { 0: false }
const actual = filterFromObject(testObj, [true, 1, 10], { isInclusion: false })
t.deepEqual(actual, expected)
})
test('Ensure throws error when given invalid filter', t => {
t.throws(() => {
filterFromObject({}, 'lol')
}, { instanceOf: Error, message: 'The given filter is not an array or function. Filter aborted' })
})

View File

@ -1,50 +0,0 @@
// 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
import test from 'ava'
import formatText from '../../src/format_text'
test('formatText test type 1', t => {
const expected = 'Hi there Dimi!'
const actual = formatText('Hi there ${dimi}!', { dimi: 'Dimi' }) // eslint-disable-line no-template-curly-in-string
t.is(actual, expected)
})
test('formatText test type 2', t => {
const expected = 'BigchainDB is big'
const actual = formatText('${database} is %(status)s', { // eslint-disable-line no-template-curly-in-string
database: 'BigchainDB',
status: 'big'
})
t.is(actual, expected)
})
test('formatText test type 3', t => {
const expected = 'Berlin is best known for its Currywurst'
const actual = formatText(
'Berlin is best known for its ${berlin.topKnownFor[0].name}', // eslint-disable-line no-template-curly-in-string
{
berlin: {
topKnownFor: [
{
name: 'Currywurst'
}
]
}
}
)
t.is(actual, expected)
})
test('formatText test throws', t => {
t.throws(() => {
formatText(
'This will give ${error.}', // eslint-disable-line no-template-curly-in-string
{ error: [{}] }
)
}, { instanceOf: SyntaxError, message: '[formatText] failed to parse named argument key: error.' })
})

View File

@ -1,14 +1,7 @@
// 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
import { createHash } from 'crypto'
import { validateFulfillment } from 'crypto-conditions'
import test from 'ava'
import base58 from 'bs58'
import { Ed25519Keypair, Transaction, ccJsonLoad } from '../../src'
import { delegatedSignTransaction } from '../constants'
import sha256Hash from '../../src/sha256Hash'
import cc from 'five-bells-condition'
import { Ed25519Keypair, Transaction } from '../../src'
test('Ed25519 condition encoding', t => {
const publicKey = '4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS'
@ -22,24 +15,12 @@ test('Ed25519 condition encoding', t => {
t.deepEqual(target, Transaction.makeEd25519Condition(publicKey))
})
test('Sha256Condition fulfillment', t => {
const preimage = 'secret'
const target = {
details: {
type_id: 0,
bitmask: 3,
preimage,
type: 'fulfillment'
},
uri: 'ni:///sha-256;K7gNU3sdo-OL0wNhqoVWhr3g6s1xYv72ol_pe_Unols?fpt=preimage-sha-256&cost=6'
}
t.deepEqual(target, Transaction.makeSha256Condition(preimage))
})
test('Threshold condition encoding', t => {
const publicKey = '4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS'
const ed25519 = Transaction.makeEd25519Condition(publicKey, false)
const condition = Transaction.makeThresholdCondition(1, [ed25519, ed25519])
const condition = Transaction.makeThresholdCondition(
1, [ed25519, ed25519])
const output = Transaction.makeOutput(condition)
const target = {
condition: {
@ -65,6 +46,7 @@ test('Threshold condition encoding', t => {
t.deepEqual(target, output)
})
test('Fulfillment correctly formed', t => {
const alice = new Ed25519Keypair()
const txCreate = Transaction.makeCreateTransaction(
@ -73,78 +55,30 @@ 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: signCreateTransaction, output_index: 0 }],
txCreate,
{},
[Transaction.makeOutput(Transaction.makeEd25519Condition(alice.publicKey))],
{}
[0]
)
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(validateFulfillment(
txSigned.inputs[0].fulfillment,
const txSigned = Transaction.signTransaction(txTransfer, alice.privateKey)
t.truthy(cc.validateFulfillment(txSigned.inputs[0].fulfillment,
txCreate.outputs[0].condition.uri,
Buffer.from(msgHash, 'hex')
))
new Buffer(msg)))
})
test('Delegated signature is correct', t => {
const alice = new Ed25519Keypair()
const txCreate = Transaction.makeCreateTransaction(
{},
{},
[Transaction.makeOutput(Transaction.makeEd25519Condition(alice.publicKey))],
alice.publicKey
)
const signCreateTransaction = Transaction.signTransaction(txCreate, alice.privateKey)
const delegatedSignCreateTransaction = Transaction.delegateSignTransaction(
txCreate,
delegatedSignTransaction(alice)
)
t.deepEqual(signCreateTransaction, delegatedSignCreateTransaction)
})
test('Delegated async signature is correct', async t => {
const alice = new Ed25519Keypair()
const txCreate = Transaction.makeCreateTransaction(
{},
{},
[Transaction.makeOutput(Transaction.makeEd25519Condition(alice.publicKey))],
alice.publicKey
)
const signCreateTransaction = Transaction.signTransaction(txCreate, alice.privateKey)
const delegatedSignCreateTransaction = await Transaction.delegateSignTransactionAsync(
txCreate,
delegatedSignTransaction(alice)
)
t.deepEqual(signCreateTransaction, delegatedSignCreateTransaction)
})
test('CryptoConditions JSON load', t => {
const publicKey = '4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS'
const cond = ccJsonLoad({
const cond = Transaction.ccJsonLoad({
type: 'threshold-sha-256',
threshold: 1,
subconditions: [{
type: 'ed25519-sha-256',
public_key: publicKey
public_key: 'a'
},
{
hash: base58.encode(createHash('sha256').update('a').digest())
hash: 'a'
}],
})
t.truthy(cond.subconditions.length === 2)

View File

@ -1,11 +1,9 @@
// 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
import test from 'ava'
import sinon from 'sinon'
import { Transaction } from '../../src'
import * as makeTransaction from '../../src/transaction/makeTransaction' // eslint-disable-line
import makeInputTemplate from '../../src/transaction/makeInputTemplate'
import {
alice,
@ -15,6 +13,7 @@ import {
transferTx
} from '../constants'
test('Create valid output with default amount', t => {
const condition = {
details: {
@ -31,6 +30,7 @@ test('Create valid output with default amount', t => {
t.deepEqual(res, expected)
})
test('Create valid output with custom amount', t => {
const condition = {
details: {
@ -63,53 +63,60 @@ test('Pass condition not based on public_keys to makeOutput', t => {
t.deepEqual(res, expected)
})
test('makeOutput throws TypeError with incorrect amount type', t => {
t.throws(() => Transaction.makeOutput({}, 1337), { instanceOf: TypeError })
t.throws(() => Transaction.makeOutput({}, 1337), TypeError)
})
test('Create TRANSFER transaction based on CREATE transaction', t => {
sinon.spy(Transaction, 'makeTransaction')
sinon.spy(makeTransaction, 'default')
Transaction.makeTransferTransaction(
[{ tx: createTx, output_index: 0 }],
createTx,
metaData,
[aliceOutput],
metaData
0
)
const expected = [
'TRANSFER',
{ id: createTx.id },
metaData,
[aliceOutput],
[Transaction.makeInputTemplate(
[makeInputTemplate(
[alice.publicKey],
{ output_index: 0, transaction_id: createTx.id }
)]
]
// NOTE: `src/transaction/makeTransaction` is `export default`, hence we
// can only mock `makeTransaction.default` with a hack:
// See: https://stackoverflow.com/a/33676328/1263876
t.truthy(Transaction.makeTransaction.calledWith(...expected))
Transaction.makeTransaction.restore()
t.truthy(makeTransaction.default.calledWith(...expected))
makeTransaction.default.restore()
})
test('Create TRANSFER transaction based on TRANSFER transaction', t => {
sinon.spy(Transaction, 'makeTransaction')
sinon.spy(makeTransaction, 'default')
Transaction.makeTransferTransaction(
[{ tx: transferTx, output_index: 0 }],
transferTx,
metaData,
[aliceOutput],
metaData
0
)
const expected = [
'TRANSFER',
{ id: transferTx.asset.id },
metaData,
[aliceOutput],
[Transaction.makeInputTemplate(
[makeInputTemplate(
[alice.publicKey],
{ output_index: 0, transaction_id: transferTx.id }
)]
]
t.truthy(Transaction.makeTransaction.calledWith(...expected))
Transaction.makeTransaction.restore()
t.truthy(makeTransaction.default.calledWith(...expected))
makeTransaction.default.restore()
})

View File

@ -1,23 +0,0 @@
// 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
import test from 'ava'
import {
Connection
} from '../../src'
test('Pick connection with earliest backoff time', async t => {
const path1 = 'http://localhost:9984/api/v1/'
const path2 = 'http://localhostwrong:9984/api/v1/'
// Reverse order
const conn = new Connection([path2, path1])
// This will trigger the 'forwardRequest' so the correct connection will be taken
await conn.searchAssets('example')
const connection1 = conn.transport.connectionPool[1]
t.deepEqual(conn.transport.pickConnection(), connection1)
})

View File

@ -1,10 +0,0 @@
// 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
export default class Ed25519Keypair {
publicKey: string;
privateKey: string;
constructor(seed?: Buffer);
}

View File

@ -1,31 +0,0 @@
// 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
export interface RequestConfig {
headers?: Record<string, string | string[]>;
jsonBody?: Record<string, any>;
query?: Record<string, any>;
method?: 'GET' | ' POST' | 'PUT';
urlTemplateSpec?: any[] | Record<string, any>;
[key: string]: any;
}
export function ResponseError(
message: string,
status?: number,
requestURI?: string
): void;
declare function timeout<T = Response>(
ms: number,
promise: Promise<T>
): Promise<T>;
declare function handleResponse(res: Response): Response;
export default function baseRequest(
url: string,
config: RequestConfig,
requestTimeout?: number
): Promise<Response>;

179
types/connection.d.ts vendored
View File

@ -1,179 +0,0 @@
// 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
import type { RequestConfig } from './baseRequest';
import type { Node } from './request';
import type Transport from './transport';
import type {
CreateTransaction,
TransactionOperations,
TransferTransaction,
TransactionCommon,
} from './transaction';
declare const DEFAULT_NODE = 'http://localhost:9984/api/v1/';
declare const DEFAULT_TIMEOUT = 20000; // The default value is 20 seconds
export interface InputNode {
endpoint: string;
}
export type AssetResult = {
id: string;
data: Record<string, any>;
};
export type MetadataResult = {
id: string;
metadata: Record<string, any>;
};
export enum Endpoints {
blocks = 'blocks',
blocksDetail = 'blocksDetail',
outputs = 'outputs',
transactions = 'transactions',
transactionsSync = 'transactionsSync',
transactionsAsync = 'transactionsAsync',
transactionsCommit = 'transactionsCommit',
transactionsDetail = 'transactionsDetail',
assets = 'assets',
metadata = 'metadata',
}
export interface EndpointsUrl {
[Endpoints.blocks]: 'blocks';
[Endpoints.blocksDetail]: 'blocks/%(blockHeight)s';
[Endpoints.outputs]: 'outputs';
[Endpoints.transactions]: 'transactions';
[Endpoints.transactionsSync]: 'transactions?mode=sync';
[Endpoints.transactionsAsync]: 'transactions?mode=async';
[Endpoints.transactionsCommit]: 'transactions?mode=commit';
[Endpoints.transactionsDetail]: 'transactions/%(transactionId)s';
[Endpoints.assets]: 'assets';
[Endpoints.metadata]: 'metadata';
}
export interface EndpointsResponse<
O = TransactionOperations.CREATE,
A = Record<string, any>,
M = Record<string, any>
> {
[Endpoints.blocks]: number[];
[Endpoints.blocksDetail]: {
height: number;
transactions: (CreateTransaction | TransferTransaction)[];
};
[Endpoints.outputs]: {
transaction_id: string;
output_index: number;
}[];
[Endpoints.transactions]: O extends TransactionOperations.CREATE
? CreateTransaction[]
: O extends TransactionOperations.TRANSFER
? TransferTransaction[]
: (CreateTransaction | TransferTransaction)[];
[Endpoints.transactionsSync]: O extends TransactionOperations.CREATE
? CreateTransaction<A, M>
: TransferTransaction<M>;
[Endpoints.transactionsAsync]: O extends TransactionOperations.CREATE
? CreateTransaction<A, M>
: TransferTransaction<M>;
[Endpoints.transactionsCommit]: O extends TransactionOperations.CREATE
? CreateTransaction<A, M>
: TransferTransaction<M>;
[Endpoints.transactionsDetail]: O extends TransactionOperations.CREATE
? CreateTransaction<A, M>
: TransferTransaction<M>;
[Endpoints.assets]: AssetResult[];
[Endpoints.metadata]: MetadataResult[];
}
export default class Connection {
private transport: Transport;
private normalizedNodes: Node[];
private headers: Record<string, string | string[]>;
constructor(
nodes: string | InputNode | (string | InputNode)[],
headers?: Record<string, string | string[]>,
timeout?: number
);
static normalizeNode(
node: string | InputNode,
headers: Record<string, string | string[]>
): Node;
static getApiUrls<E extends keyof EndpointsUrl>(endpoint: E): EndpointsUrl[E];
private _req<E extends keyof EndpointsUrl, O = Record<string, any>>(
path: EndpointsUrl[E],
options: RequestConfig
): Promise<O>;
getBlock(
blockHeight: number | string
): Promise<EndpointsResponse[Endpoints.blocksDetail]>;
getTransaction<O = TransactionOperations.CREATE>(
transactionId: string
): Promise<EndpointsResponse<O>[Endpoints.transactionsDetail]>;
listBlocks(
transactionId: string
): Promise<EndpointsResponse[Endpoints.blocks]>;
listOutputs(
publicKey: string,
spent?: boolean
): Promise<EndpointsResponse[Endpoints.outputs]>;
listTransactions(
assetId: string,
operation?: TransactionOperations
): Promise<EndpointsResponse<typeof operation>[Endpoints.transactions]>;
postTransaction<
O extends TransactionOperations = TransactionOperations.CREATE,
A = Record<string, any>,
M = Record<string, any>
>(
transaction: TransactionCommon<O>
): Promise<EndpointsResponse<O, A, M>[Endpoints.transactionsCommit]>;
postTransactionSync<
O extends TransactionOperations = TransactionOperations.CREATE,
A = Record<string, any>,
M = Record<string, any>
>(
transaction: TransactionCommon<O>
): Promise<EndpointsResponse<O, A, M>[Endpoints.transactionsSync]>;
postTransactionAsync<
O extends TransactionOperations = TransactionOperations.CREATE,
A = Record<string, any>,
M = Record<string, any>
>(
transaction: TransactionCommon<O>
): Promise<EndpointsResponse<O, A, M>[Endpoints.transactionsAsync]>;
postTransactionCommit<
O extends TransactionOperations = TransactionOperations.CREATE,
A = Record<string, any>,
M = Record<string, any>
>(
transaction: TransactionCommon<O>
): Promise<EndpointsResponse<O, A, M>[Endpoints.transactionsCommit]>;
searchAssets(
search: string,
limit?: number
): Promise<EndpointsResponse[Endpoints.assets]>;
searchMetadata(
search: string,
limit?: number
): Promise<EndpointsResponse[Endpoints.metadata]>;
}

39
types/index.d.ts vendored
View File

@ -1,39 +0,0 @@
// 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
import Ed25519Keypair from './Ed25519Keypair';
import Connection, {
Endpoints,
EndpointsResponse,
EndpointsUrl,
} from './connection';
import Transaction, {
CreateTransaction,
TransactionCommon,
TransactionCommonSigned,
TransactionInput,
TransactionOutput,
TransferTransaction,
TransactionUnspentOutput,
TransactionOperations,
} from './transaction';
import ccJsonLoad from './utils/ccJsonLoad';
import ccJsonify from './utils/ccJsonify';
export { ccJsonLoad, ccJsonify, Connection, Ed25519Keypair, Transaction };
// Extras
export {
Endpoints,
EndpointsResponse,
EndpointsUrl,
CreateTransaction,
TransactionCommon,
TransactionCommonSigned,
TransactionInput,
TransactionOutput,
TransferTransaction,
TransactionUnspentOutput,
TransactionOperations,
};

32
types/request.d.ts vendored
View File

@ -1,32 +0,0 @@
// 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
import type { RequestConfig } from './baseRequest';
export interface Node {
endpoint: string;
headers: Record<string, string | string[]>;
}
export default class Request {
private node: Node;
private backoffTime: number;
private retries: number;
private connectionError?: Error;
constructor(node: Node);
request<O = Record<string, any>>(
urlPath: string,
config?: RequestConfig,
timeout?: number,
maxBackoffTime?: number
): Promise<O>;
updateBackoffTime(maxBackoffTime: number): void;
getBackoffTimedelta(): number;
static sleep(ms: number): void;
}

25
types/sanitize.d.ts vendored
View File

@ -1,25 +0,0 @@
// 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
declare type FilterFn = (val: any, key?: string) => void;
declare function filterFromObject<I = Record<string, any>>(
obj: I,
filter: Array<any> | FilterFn,
conf: { isInclusion?: boolean }
): Partial<I>;
declare function applyFilterOnObject<I = Record<string, any>>(
obj: I,
filterFn?: FilterFn
): Partial<I>;
declare function selectFromObject<I = Record<string, any>>(
obj: I,
filter: Array<any> | FilterFn
): Partial<I>;
export default function sanitize<I = Record<string, any>>(
obj: I
): Partial<I> | I;

236
types/transaction.d.ts vendored
View File

@ -1,236 +0,0 @@
import type {
Ed25519Sha256,
Fulfillment,
PreimageSha256,
ThresholdSha256,
} from 'crypto-conditions';
import {
Ed25519Sha256JSONCondition,
PreimageSha256JSONCondition,
ThresholdSha256JSONCondition,
} from './utils/ccJsonify';
export interface TransactionInput {
fulfillment: string;
fulfills: {
output_index: number;
transaction_id: string;
} | null;
owners_before: string[];
}
export interface TransactionOutput {
amount: string;
condition:
| PreimageSha256JSONCondition
| ThresholdSha256JSONCondition
| Ed25519Sha256JSONCondition;
public_keys: string[];
}
export enum TransactionOperations {
CREATE = 'CREATE',
TRANSFER = 'TRANSFER',
}
export interface TransactionCommon<
O extends TransactionOperations = TransactionOperations.CREATE,
A extends Record<string, any> = Record<string, unknown>,
M extends Record<string, any> = Record<string, unknown>
> {
id?: string;
inputs: TransactionInput[];
outputs: TransactionOutput[];
version: string;
metadata: M;
operation: O;
asset: TransactionAssetMap<O, A>;
}
export interface TransactionCommonSigned<
O extends TransactionOperations = TransactionOperations.CREATE,
A extends Record<string, any> = Record<string, unknown>,
M extends Record<string, any> = Record<string, unknown>
> extends Omit<TransactionCommon<O, A, M>, 'id'> {
id: string;
}
export type TransactionAssetMap<
Operation,
A extends Record<string, any>
> = Operation extends TransactionOperations.CREATE
? {
data: A;
}
: {
id: string;
};
export interface CreateTransaction<
A extends Record<string, any> = Record<string, unknown>,
M extends Record<string, any> = Record<string, unknown>
> extends TransactionCommon<TransactionOperations.CREATE, A, M> {
id: string;
asset: TransactionAssetMap<TransactionOperations.CREATE, A>;
operation: TransactionOperations.CREATE;
}
export interface TransferTransaction<
M extends Record<string, any> = Record<string, unknown>
> extends TransactionCommon<TransactionOperations.TRANSFER, any, M> {
id: string;
asset: TransactionAssetMap<TransactionOperations.TRANSFER, { id: string }>;
operation: TransactionOperations.TRANSFER;
}
export interface TransactionUnspentOutput {
tx: TransactionCommon;
output_index: number;
}
interface TxTemplate {
id: null;
operation: null;
outputs: [];
inputs: [];
metadata: null;
asset: null;
version: '2.0';
}
export type DelegateSignFunction = (
serializedTransaction: string,
input: TransactionInput,
index?: number
) => string;
export type DelegateSignFunctionAsync = (
serializedTransaction: string,
input: TransactionInput,
index?: number
) => Promise<string>;
export default class Transaction {
static serializeTransactionIntoCanonicalString<
O extends TransactionOperations = TransactionOperations
>(transaction: TransactionCommon<O>): string;
static serializeTransactionIntoCanonicalString(
transaction: CreateTransaction | TransferTransaction
): string;
static makeEd25519Condition(publicKey: string): Ed25519Sha256JSONCondition;
static makeEd25519Condition(
publicKey: string,
json: true
): Ed25519Sha256JSONCondition;
static makeEd25519Condition(publicKey: string, json: false): Ed25519Sha256;
static makeEd25519Condition(
publicKey: string,
json?: boolean
): Ed25519Sha256 | Ed25519Sha256JSONCondition;
static makeSha256Condition(preimage: string): PreimageSha256JSONCondition;
static makeSha256Condition(
preimage: string,
json: true
): PreimageSha256JSONCondition;
static makeSha256Condition(preimage: string, json: false): PreimageSha256;
static makeSha256Condition(
preimage: string,
json?: boolean
): PreimageSha256 | PreimageSha256JSONCondition;
static makeThresholdCondition(
threshold: number,
subconditions: (string | Fulfillment)[]
): ThresholdSha256JSONCondition;
static makeThresholdCondition(
threshold: number,
subconditions: (string | Fulfillment)[],
json: true
): ThresholdSha256JSONCondition;
static makeThresholdCondition(
threshold: number,
subconditions: (string | Fulfillment)[],
json: false
): ThresholdSha256;
static makeThresholdCondition(
threshold: number,
subconditions: (string | Fulfillment)[],
json?: boolean
): ThresholdSha256 | ThresholdSha256JSONCondition;
static makeInputTemplate(
publicKeys: string[],
fulfills?: TransactionInput['fulfills'],
fulfillment?: TransactionInput['fulfillment']
): TransactionInput;
static makeOutput(
condition:
| PreimageSha256JSONCondition
| ThresholdSha256JSONCondition
| Ed25519Sha256JSONCondition,
amount?: string
): TransactionOutput;
static makeTransactionTemplate(): TxTemplate;
static makeTransaction<
O extends TransactionOperations,
A = Record<string, any>,
M = Record<string, any>
>(
operation: O,
asset: A,
metadata: M,
outputs: TransactionOutput[],
inputs: TransactionInput[]
): TransactionCommon<O, A, M>;
static makeCreateTransaction<
A = Record<string, any>,
M = Record<string, any>
>(
asset: A,
metadata: M,
outputs: TransactionOutput[],
...issuers: string[]
): CreateTransaction<A, M>;
static makeTransferTransaction<M = Record<string, any>>(
unspentOutputs: TransactionUnspentOutput[],
outputs: TransactionOutput[],
metadata: M
): TransferTransaction<M>;
static signTransaction<
O extends TransactionOperations = TransactionOperations.CREATE
>(
transaction: TransactionCommon<O>,
...privateKeys: string[]
): TransactionCommonSigned<O>;
static delegateSignTransaction<
O extends TransactionOperations = TransactionOperations.CREATE
>(
transaction: TransactionCommon<O>,
signFn: DelegateSignFunction
): TransactionCommonSigned<O>;
static delegateSignTransactionAsync<
O extends TransactionOperations = TransactionOperations.CREATE
>(
transaction: TransactionCommon<O>,
signFn: DelegateSignFunctionAsync
): Promise<TransactionCommonSigned<O>>;
}

21
types/transport.d.ts vendored
View File

@ -1,21 +0,0 @@
// 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
import Request, { Node } from './request';
import type { RequestConfig } from './baseRequest';
export default class Transport {
private connectionPool: Request[];
private timeout: number;
private maxBackoffTime: number;
constructor(nodes: Node[], timeout: number);
pickConnection(): Request;
forwardRequest<O = Record<string, any>>(
path: string,
config: RequestConfig
): Promise<O>;
}

View File

@ -1,32 +0,0 @@
// 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
import type {
Condition,
Ed25519Sha256,
PreimageSha256,
ThresholdSha256,
} from 'crypto-conditions';
import type {
Ed25519Sha256JSONCondition,
JSONCondition,
PreimageSha256JSONCondition,
ThresholdSha256JSONCondition,
} from './ccJsonify';
declare function ccJsonLoad(
conditionJson: PreimageSha256JSONCondition
): PreimageSha256;
declare function ccJsonLoad(
conditionJson: ThresholdSha256JSONCondition
): ThresholdSha256;
declare function ccJsonLoad(
conditionJson: Ed25519Sha256JSONCondition
): Ed25519Sha256;
declare function ccJsonLoad(conditionJson: JSONCondition): Condition;
export default ccJsonLoad;

View File

@ -1,70 +0,0 @@
// 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
import type {
Condition,
Ed25519Sha256,
PreimageSha256,
ThresholdSha256,
} from 'crypto-conditions';
import type { TypeId, TypeName } from 'crypto-conditions/types/types';
interface BaseJSONCondition {
details: {
[key: string]: any;
};
uri: string;
}
export interface JSONCondition extends BaseJSONCondition {
details: {
type_id: TypeId;
bitmask: number;
type: 'condition';
hash: string;
max_fulfillment_length: number;
};
}
export interface PreimageSha256JSONCondition extends BaseJSONCondition {
details: {
type_id: TypeId.PreimageSha256;
bitmask: 3;
preimage?: string;
type?: 'fulfillement';
};
}
export interface ThresholdSha256JSONCondition extends BaseJSONCondition {
details: {
type: TypeName.ThresholdSha256;
subConditions: (Ed25519Sha256JSONCondition | PreimageSha256JSONCondition)[];
};
}
export interface Ed25519Sha256JSONCondition extends BaseJSONCondition {
details: { type: TypeName.Ed25519Sha256; publicKey?: string };
}
export type JSONConditionUnion =
| JSONCondition
| PreimageSha256JSONCondition
| ThresholdSha256JSONCondition
| Ed25519Sha256JSONCondition;
declare function ccJsonify(
fulfillment: PreimageSha256
): PreimageSha256JSONCondition;
declare function ccJsonify(
fulfillment: ThresholdSha256
): ThresholdSha256JSONCondition;
declare function ccJsonify(
fulfillment: Ed25519Sha256
): Ed25519Sha256JSONCondition;
declare function ccJsonify(fulfillment: Condition): JSONCondition;
export default ccJsonify;

View File

@ -1,42 +0,0 @@
// 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
/* eslint-disable strict, no-console, object-shorthand */
'use strict'
const { ProvidePlugin } = require('webpack')
const { paths } = require('./webpack.parts')
module.exports = {
entry: paths.entry,
mode: 'none',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
}
]
},
optimization: {
minimize: true,
emitOnErrors: false
},
resolve: {
extensions: ['.js'],
modules: ['node_modules'],
fallback: {
buffer: require.resolve('buffer/'),
}
},
plugins: [
new ProvidePlugin({
Buffer: ['buffer', 'Buffer']
})
]
}

View File

@ -1,34 +1,111 @@
// 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
/* eslint-disable strict, no-console, object-shorthand */
'use strict'
const path = require('path')
const webpack = require('webpack')
const PRODUCTION = process.env.NODE_ENV === 'production'
const common = require('./webpack.common')
const { outputs } = require('./webpack.parts')
// '[libraryTarget]': [file extension]
const OUTPUT_MAPPING = {
'amd': 'amd',
'commonjs': 'cjs',
'commonjs2': 'cjs2',
'umd': 'umd',
'window': 'window',
const PATHS = {
ENTRY: path.resolve(__dirname, './src/index.js'),
BUNDLE: path.resolve(__dirname, 'dist/browser'),
NODE_MODULES: path.resolve(__dirname, 'node_modules'),
}
const OVERRIDES = {
// optimization: {
// minimize: false
// }
}
const OUTPUTS = [
{
filename: PRODUCTION ? 'bigchaindb-driver.window.min.js' : 'bigchaindb-driver.window.js',
library: 'BigchainDB',
libraryTarget: 'window',
path: PATHS.BUNDLE,
},
{
filename: PRODUCTION ? 'bigchaindb-driver.umd.min.js' : 'bigchaindb-driver.umd.js',
library: 'bigchaindb-driver',
libraryTarget: 'umd',
path: PATHS.BUNDLE,
},
{
filename: PRODUCTION ? 'bigchaindb-driver.cjs.min.js' : 'bigchaindb-driver.cjs.js',
library: 'bigchaindb-driver',
libraryTarget: 'commonjs',
path: PATHS.BUNDLE,
},
{
filename: PRODUCTION ? 'bigchaindb-driver.cjs2.min.js' : 'bigchaindb-driver.cjs2.js',
library: 'bigchaindb-driver',
libraryTarget: 'commonjs2',
path: PATHS.BUNDLE,
},
{
filename: PRODUCTION ? 'bigchaindb-driver.amd.min.js' : 'bigchaindb-driver.amd.js',
library: 'bigchaindb-driver',
libraryTarget: 'amd',
path: PATHS.BUNDLE,
}
]
/** PLUGINS **/
const PLUGINS = [
new webpack.NoEmitOnErrorsPlugin(),
]
const PROD_PLUGINS = [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
},
output: {
comments: false,
},
sourceMap: true,
}),
new webpack.LoaderOptionsPlugin({
debug: false,
minimize: true,
}),
]
if (PRODUCTION) {
module.exports = outputs(common, 'production', OUTPUT_MAPPING, OVERRIDES)
} else {
module.exports = outputs(common, 'development', OUTPUT_MAPPING, OVERRIDES)
PLUGINS.push(...PROD_PLUGINS)
}
const configBoilerplate = {
entry: [PATHS.ENTRY],
devtool: PRODUCTION ? '#source-map' : '#inline-source-map',
resolve: {
extensions: ['.js'],
modules: ['node_modules'], // Don't use absolute path here to allow recursive matching
},
plugins: PLUGINS,
module: {
rules: [
{
test: /\.js$/,
exclude: [PATHS.NODE_MODULES],
use: [{
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
}],
},
],
},
}
/** EXPORTED WEBPACK CONFIG **/
const config = OUTPUTS.map(output => {
const configCopy = Object.assign({}, configBoilerplate)
configCopy.output = output
return configCopy
})
module.exports = config

View File

@ -1,32 +0,0 @@
// 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
/* eslint-disable strict, no-console, object-shorthand, import/no-extraneous-dependencies */
'use strict'
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
devtool: 'inline-source-map',
optimization: {
minimizer: [
new TerserPlugin({
test: /vendor/,
}),
new TerserPlugin({
test: /^((?!(vendor)).)*.js$/,
})
],
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
}

View File

@ -1,59 +0,0 @@
// 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
/* eslint-disable strict, no-console, object-shorthand, import/no-extraneous-dependencies */
'use strict'
const path = require('path')
const { merge } = require('webpack-merge')
const development = require('./webpack.development')
const production = require('./webpack.production')
const AddVendorsPlugin = require('./plugins/add-vendors-plugin')
const paths = {
entry: path.resolve(__dirname, './src/index.js'),
bundle: path.resolve(__dirname, 'dist/browser'),
}
const outputs = (base, env, mapping, overrides) => {
const collection = []
const library = 'bigchaindb-driver'
const windowLibrary = 'BigchainDB'
let environment = development
let ext = 'js'
if (env === 'production') {
environment = production
ext = `min.${ext}`
}
Object.entries(mapping).forEach(([target, extension]) => {
const filename = `[name].${library}.${extension}.${ext}`
const compiled = {
output: {
filename: filename,
library: target === 'window' ? windowLibrary : library,
libraryTarget: target,
path: paths.bundle
},
plugins: [
new AddVendorsPlugin(`${library}.${extension}.${ext}`)
]
}
collection.push(merge(base, environment, compiled, overrides))
})
return collection
}
module.exports = {
outputs,
paths
}

View File

@ -1,11 +0,0 @@
// 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
/* eslint-disable strict, no-console, object-shorthand */
'use strict'
module.exports = {
devtool: 'source-map',
}

5401
yarn.lock Normal file

File diff suppressed because it is too large Load Diff