diff --git a/bigchaindb/web/server.py b/bigchaindb/web/server.py index 022320d7..91892807 100644 --- a/bigchaindb/web/server.py +++ b/bigchaindb/web/server.py @@ -13,6 +13,7 @@ import gunicorn.app.base from bigchaindb import utils from bigchaindb import Bigchain from bigchaindb.web.routes import add_routes +from bigchaindb.web.strip_content_type_middleware import StripContentTypeMiddleware # TODO: Figure out if we do we need all this boilerplate. @@ -60,6 +61,7 @@ def create_app(*, debug=False, threads=1): """ app = Flask(__name__) + app.wsgi_app = StripContentTypeMiddleware(app.wsgi_app) CORS(app) diff --git a/bigchaindb/web/strip_content_type_middleware.py b/bigchaindb/web/strip_content_type_middleware.py new file mode 100644 index 00000000..953c8b78 --- /dev/null +++ b/bigchaindb/web/strip_content_type_middleware.py @@ -0,0 +1,27 @@ +import logging + +logger = logging.getLogger(__name__) + + +class StripContentTypeMiddleware: + """WSGI middleware to strip Content-Type header for GETs.""" + + def __init__(self, app): + """Create the new middleware. + + Args: + app: a flask application + """ + self.app = app + + def __call__(self, environ, start_response): + """Run the middleware and then call the original WSGI application.""" + + if environ['REQUEST_METHOD'] == 'GET': + try: + del environ['CONTENT_TYPE'] + except KeyError: + pass + else: + logger.debug('Remove header "Content-Type" from GET request') + return self.app(environ, start_response) diff --git a/docs/README.md b/docs/README.md index 5169f871..220569e2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,5 @@ -[Documentation on ReadTheDocs](http://bigchaindb.readthedocs.org/) +- [Documentation on ReadTheDocs](http://bigchaindb.readthedocs.org/) +- [BigchainDB Upgrade Guides](upgrade-guides/) # The BigchainDB Documentation Strategy diff --git a/docs/server/source/data-models/inputs-outputs.rst b/docs/server/source/data-models/inputs-outputs.rst index 76d7062d..696dac83 100644 --- a/docs/server/source/data-models/inputs-outputs.rst +++ b/docs/server/source/data-models/inputs-outputs.rst @@ -29,7 +29,7 @@ A CREATE transaction should have exactly one input. That input can contain one o See the reference on :ref:`inputs ` for more description about the meaning of each field. To calculate a fulfillment URI, you can use one of the -:ref:`BigchainDB drivers or transaction-builders `, +:ref:`BigchainDB drivers or transaction-builders `, or use a low-level crypto-conditions library as illustrated in the page about `Handcrafting Transactions `_. @@ -52,7 +52,7 @@ See the reference on :ref:`outputs ` for more description about the mean Below is a high-level description of what goes into building a ``condition`` object. To construct an actual ``condition`` object, you can use one of the -:ref:`BigchainDB drivers or transaction-builders `, +:ref:`BigchainDB drivers or transaction-builders `, or use a low-level crypto-conditions library as illustrated in the page about `Handcrafting Transactions `_. diff --git a/docs/server/source/drivers-clients/index.rst b/docs/server/source/drivers-clients/index.rst index f6e5c6ae..2c4f3f01 100644 --- a/docs/server/source/drivers-clients/index.rst +++ b/docs/server/source/drivers-clients/index.rst @@ -1,5 +1,5 @@ -Drivers & Clients -================= +Drivers & Tools +=============== Libraries and Tools Maintained by the BigchainDB Team ----------------------------------------------------- diff --git a/docs/upgrade-guides/v0.10-v1.0.md b/docs/upgrade-guides/v0.10-v1.0.md new file mode 100644 index 00000000..63778371 --- /dev/null +++ b/docs/upgrade-guides/v0.10-v1.0.md @@ -0,0 +1,433 @@ +# Updating from BigchainDB v0.10 to v1.0 + +BigchainDB v1.0 stands for backwards compatibility. This means that all +following [minor](http://semver.org/) releases after version 1.0 will always be +backwards-compatible to previous versions. + +For all future releases, we commit to not introduce breaking changes to the +public interfaces of BigchainDB's: + +- [Data + model](https://docs.bigchaindb.com/projects/server/en/latest/data-models/index.html) +- [HTTP + API](https://docs.bigchaindb.com/projects/server/en/latest/http-client-server-api.html) +- [WebSocket Event Stream + API](https://docs.bigchaindb.com/projects/server/en/latest/websocket-event-stream-api.html) + + +As we saw the version bump to v1.0 as our last chance in a while to fix minor +annoyances, we intentionally did clean up on the above interfaces. In this +document, we'd like to give a comprehensive summary of those changes to allow +you to upgrade efficiently. + +The next sections will go over each of the above mentioned interfaces and +detail the exact changes. + +## A note upfront + +We tried to test this upgrade guide as best as we could by using it to adjust +our official drivers. If you still find breaking changes that causes your +software to crash, please let us and others reading this guide know by sending +over a Pull-Request or notifying us on Gitter. + +Thank you very much :) + + +## Syntactical changes + +#### `tx`, `txid` and `tx_id` becomes `transaction_id` + +To establish better consistency between external interfaces, all usages of +`txid`, `tx` and `tx_id` in data models and HTTP API were renamed to +`transaction_id` or `transaction`. + + +## Breaking Changes to the Data Model + +#### Output amount is now a string + +BigchainDB transactions may have multiple inputs and outputs, and each output +has an amount, which is the integral number of the asset being transferred. In +prior versions of BigchainDB, the amount was encoded as a number, which on the +face of it is the obvious way to encode an integer. However, as usual the devil +is in the details; JSON, the encoding of choice for BigchainDB transactions, +encodes all numbers including integers as floating point. This isn't a problem +for the majority of circumstances where numbers are small, however in some +environments and for some use cases\*, the number may lose precision. + +In order to safeguard against this, amounts are now encoded as strings, and it +is recommended to use a decimal math library (such as +[big.js](https://github.com/MikeMcl/big.js)) when dealing with large numbers in +Javascript. Additionally, numbers are capped at 9e18 to stay comfortably within +the boundary of a 64 bit signed integer. + +\* Try this in the Chrome developer console: `2**60 == 2**60+1`. + + +#### Input `fulfills.txid` is now `transaction_id` + +We renamed a TRANSFER transaction's `inputs.fulfills.txid` to +`inputs.fulfills.transaction_id`. + +#### Input `fulfills.output` is now `output_index` + +We renamed a TRANSFER transaction's `inputs.fulfills.output` to +`inputs.fulfills.output_index`. + +#### Signing payload is now the transaction body + +The signature payload of a BigchainDB transaction is now "just" the JSON +serialized body of the transaction. This change is invisible to applications +that do not produce transactions with more than one input. However, prior to +the 1.0 release, transactions with multiple inputs had a special signing +protocol, which included reassembly of the transaction. This was identified as +being unneeded, so now the payload that is signed is always just the serialized +transaction, minus signatures. More details, take a look at the +[Pull-Request](https://github.com/bigchaindb/bigchaindb/pull/1225) introducing +the change or at our updated [Handcrafting +Transactions](https://docs.bigchaindb.com/projects/py-driver/en/latest/handcraft.html) +document. + + +#### Update to Crypto-Conditions version 2 + +Earlier this year the IETF Interledger working group released an [updated +draft](https://tools.ietf.org/html/draft-thomas-crypto-conditions-02) of their +Crypto-Conditions specification. To send transactions to BigchainDB v1.0, all +transaction's inputs and outputs need to comply to this new version. + +Several of the language specific implementations have already been updated, +including: + +- [py-crypto-conditions](https://github.com/bigchaindb/cryptoconditions) +- [js-crypto-conditions](https://github.com/interledgerjs/five-bells-condition) +- [java-crypto-conditions](https://github.com/interledger/java-crypto-conditions) + + +If you don't find your preferred language in this list, do not despair but +reach out to us for help on Github/Gitter or product@bigchaindb.com. + + +#### Revamp of Crypto-Conditions signing details + +In order to create a correct fulfillment for an output condition in BigchainDB, +we include the conditon URI ("ni:///sha-256;..."), to verify the fulfillment +against. However, the condition URI does not tell you who may sign in order to +create a correct fulfillment. + +For this, we have the `condition.details` object. This is a recursive data structure +which mirrors the n-of-m threshold / ed25519 condition types that we support. + +An example of the new structure is: + +```json +{ + "details": { + "type": "threshold-sha-256", + "threshold": 2, + "subconditions": [ + { + "public_key": "", + "type": "ed25519-sha-256", + }, + { + "public_key": "", + "type": "ed25519-sha-256", + } + ], + }, +} +``` + +#### Transaction version is now 1.0 + +The `version` key in the transaction is now set to `'1.0'`. + + +## Breaking Changes to the HTTP API + +In this section, we'll go over each of the endpoints separately and list the +changes done to them: + + +### `GET /` + +Documentation: + +- [Old](https://docs.bigchaindb.com/projects/server/en/v0.10.2/http-client-server-api.html#bigchaindb-root-url) +- [New](https://docs.bigchaindb.com/projects/server/en/v1.0.0/http-client-server-api.html#bigchaindb-root-url) + + +Changes: + +- All notion of `_links` was removed +- `api_v1` is now an object including currently only one further object called + `v1` +- `api.v1` includes links that were originally only available through + `/api/v1/` +- `api.v1` now also includes links to `assets` (a new endpoint) and `outputs` +- `streams_v1`'s link was changed from `streams/valid_tx` to + `streams/valid_transactions` +- `streams_v1` was renamed to `streams` +- Usages of scheme, host and port to API V1's endpoints were removed to allow + for configurations of BigchainDB behind reverse proxies + - e.g. `http://example.com:9984/api/v1/transactions` ==> + `/api/v1/transactions` + + +```json +// Old +{ + "_links": { + "api_v1": "http://example.com:9984/api/v1/", + "docs": "https://docs.bigchaindb.com/projects/server/en/v0.10.2/" + }, + "keyring": [ + "6qHyZew94NMmUTYyHnkZsB8cxJYuRNEiEpXHe1ih9QX3", + "AdDuyrTyjrDt935YnFu4VBCVDhHtY2Y6rcy7x2TFeiRi" + ], + "public_key": "NC8c8rYcAhyKVpx1PCV65CBmyq4YUbLysy3Rqrg8L8mz", + "software": "BigchainDB", + "version": "0.10.2" +} + +// New +{ + "api": { + "v1": { + "docs": "https://docs.bigchaindb.com/projects/server/en/v0.11.0.dev/http-client-server-api.html", + "statuses": "/api/v1/statuses/", + "streams": "ws://example.com:9985/api/v1/streams/valid_transactions", + "transactions": "/api/v1/transactions/", + "assets": "/api/v1/assets/", + "outputs": "/api/v1/outputs/" + } + }, + "docs": "https://docs.bigchaindb.com/projects/server/en/v0.11.0.dev/", + "keyring": [ + "6qHyZew94NMmUTYyHnkZsB8cxJYuRNEiEpXHe1ih9QX3", + "AdDuyrTyjrDt935YnFu4VBCVDhHtY2Y6rcy7x2TFeiRi" + ], + "public_key": "NC8c8rYcAhyKVpx1PCV65CBmyq4YUbLysy3Rqrg8L8mz", + "software": "BigchainDB", + "version": "0.11.0.dev" +} +``` + +### `GET /api/v1` + +Documentation: + +- [Old](https://docs.bigchaindb.com/projects/server/en/v0.10.2/http-client-server-api.html#api-root-endpoint) +- [New](https://docs.bigchaindb.com/projects/server/en/v1.0.0/http-client-server-api.html#api-root-endpoint) + +Changes: + +- All notion of `_links` was removed +- The response object of `/api/v1` now includes links to `assets` (a new + endpoint) and `outputs` +- `streams_v1`'s link was changed from `streams/valid_tx` to + `streams/valid_transactions` +- `streams_v1` was renamed to `streams` +- Usages of scheme, host and port to API V1's endpoints were removed to allow + for configurations of BigchainDB behind reverse proxies + - e.g. `http://example.com:9984/api/v1/transactions` ==> + `/api/v1/transactions` + + +```json +// Old +{ + "_links": { + "docs": "https://docs.bigchaindb.com/projects/server/en/v0.10.2/http-client-server-api.html", + "self": "http://example.com:9984/api/v1/", + "statuses": "http://example.com:9984/api/v1/statuses/", + "streams_v1": "ws://example.com:9985/api/v1/streams/valid_tx", + "transactions": "http://example.com:9984/api/v1/transactions/" + } +} + +// New +{ + "docs": "https://docs.bigchaindb.com/projects/server/en/v0.11.0.dev/http-client-server-api.html", + "statuses": "/api/v1/statuses/", + "streams": "ws://example.com:9985/api/v1/streams/valid_transactions", + "transactions": "/api/v1/transactions/", + "assets": "/api/v1/assets/", + "outputs": "/api/v1/outputs/" +} +``` + + +### `GET /api/v1/transactions/{tx_id}` + +Documentation: + +- [Old](https://docs.bigchaindb.com/projects/server/en/v0.10.2/http-client-server-api.html#get--api-v1-transactions-tx_id) +- [New](https://docs.bigchaindb.com/projects/server/en/v1.0.0/http-client-server-api.html#get--api-v1-transactions-transaction_id) + + +Changes: + +- Previously this endpoint returned transactions from BigchainDB's `BACKLOG` +and from blocks marked as `UNDECIDED`. With version 1.0, this endpoint will +only return transactions included in blocks marked as `VALID`. + + +### `POST /api/v1/transactions` + +Documentation: + +- [Old](https://docs.bigchaindb.com/projects/server/en/v0.10.2/http-client-server-api.html#post--api-v1-transactions) +- [New](https://docs.bigchaindb.com/projects/server/en/v1.0.0/http-client-server-api.html#post--api-v1-transactions) + + +Changes: + +- A `Location` HTTP header was included in the endpoint's response to allow + users to check the transaction's status more easily via the + `/statuses?transaction_id` endpoint + + +### `GET /api/v1/outputs` + +Documentation: + +- [Old](https://docs.bigchaindb.com/projects/server/en/v0.10.2/http-client-server-api.html#get--api-v1-outputs?public_key=public_key) +- [New](https://docs.bigchaindb.com/projects/server/en/v1.0.0/http-client-server-api.html#get--api-v1-outputs?public_key=public_key) + + +Changes: + +- Reversed the behavior of the `unspent` query parameter to `spent`, implying + the following behavior: + - If `?spent=true`, the response is an array of all spent outputs + associated with a given public key + - If `?spent=false`, response is an array of all NOT YET spent (or + "unspent" outputs associated with a given public key + - If no ``spent=` filter is present in the request, the response is an + array of all outputs associated with a given public key (spent and + unspent) + +Previously the response included a list of relative URLs pointed to +transations' outputs: + +```json +// Old +[ + "../transactions/2d431073e1477f3073a4693ac7ff9be5634751de1b8abaa1f4e19548ef0b4b0e/outputs/0" +] + +// New +[ + { + "output_index": 0, + "transaction_id": "2d431073e1477f3073a4693ac7ff9be5634751de1b8abaa1f4e19548ef0b4b0e" + } +] +``` + +In the future, we're planning to [upgrade this endpoint +further](https://github.com/bigchaindb/bigchaindb/blob/99499b1f8783719a082813912ac9a0d363ae278f/bdb-ip.md#6-a-new-outputs-endpoint) +to meet the requirements of [our +users](https://github.com/bigchaindb/bigchaindb/issues/1227#issuecomment-307297473). + + +### `GET /api/v1/statuses?tx_id` + +Documentation: + +- [Old](https://docs.bigchaindb.com/projects/server/en/v0.10.2/http-client-server-api.html#get--api-v1-statuses?tx_id=tx_id) +- [New](https://docs.bigchaindb.com/projects/server/en/v1.0.0/http-client-server-api.html#get--api-v1-statuses?transaction_id=transaction_id) + + +Changes: + +- All notion of `_links` was removed. In case of querying the status of a + transaction already included in a block marked `VALID`, no `_links` object is + provided anymore. The response object now only contains a single key value + pair named `status` +- The query parameter `tx_id` was renamed to `transaction_id`, e.g. `GET + /api/v1/statuses?transaction_id=` + + +```json +// Old +{ + "status": "valid", + "_links": { + "tx": "/transactions/04c00267af82c161b4bf2ad4a47d1ddbfeb47eef1a14b8d51f37d6ee00ea5cdd" + } +} + +// New +{ + "status": "valid", +} +``` + + +### `GET /api/v1/statuses?block_id` + +Documentation: + +- [Old](https://docs.bigchaindb.com/projects/server/en/v0.10.2/http-client-server-api.html#get--api-v1-statuses?block_id=block_id) +- [New](https://docs.bigchaindb.com/projects/server/en/v1.0.0/http-client-server-api.html#get--api-v1-statuses?block_id=block_id) + + +Changes: + +- All notion of `_links` was removed. The response object now only contains a + single key value pair named `status` + + +```json +// Old +{ + "status": "valid", + "_links": { + "tx": "/transactions/04c00267af82c161b4bf2ad4a47d1ddbfeb47eef1a14b8d51f37d6ee00ea5cdd" + } +} + +// New +{ + "status": "valid", +} +``` + + +### `GET /api/v1/blocks?tx_id` + +Documentation: + +- [Old](https://docs.bigchaindb.com/projects/server/en/v0.10.2/http-client-server-api.html#get--api-v1-blocks?tx_id=tx_id&status=UNDECIDED|VALID|INVALID) +- [New](https://docs.bigchaindb.com/projects/server/en/v1.0.0/http-client-server-api.html#get--api-v1-blocks?transaction_id=transaction_id&status=UNDECIDED|VALID|INVALID) + + +Changes: + +- The query parameter `tx_id` was renamed to `transaction_id`, e.g. `GET + /api/v1/blocks?transaction_id` + + +## Breaking Changes to the WebSocket Event Stream API + +In the event object sent to a listener, `tx_id` was renamed to +`transaction_id`. + +```json +// Old +{ + "tx_id": "", + "asset_id": "", + "block_id": "" +} + +// New +{ + "transaction_id": "", + "asset_id": "", + "block_id": "" +} +``` diff --git a/tests/web/test_content_type_middleware.py b/tests/web/test_content_type_middleware.py new file mode 100644 index 00000000..103bd615 --- /dev/null +++ b/tests/web/test_content_type_middleware.py @@ -0,0 +1,40 @@ +from unittest.mock import Mock + +OUTPUTS_ENDPOINT = '/api/v1/outputs/' + + +def test_middleware_does_nothing_when_no_content_type_is_provided(): + from bigchaindb.web.strip_content_type_middleware import StripContentTypeMiddleware + mock = Mock() + middleware = StripContentTypeMiddleware(mock) + middleware({'REQUEST_METHOD': 'GET'}, None) + + assert 'CONTENT_TYPE' not in mock.call_args[0][0] + + +def test_middleware_strips_content_type_from_gets(): + from bigchaindb.web.strip_content_type_middleware import StripContentTypeMiddleware + mock = Mock() + middleware = StripContentTypeMiddleware(mock) + middleware({'REQUEST_METHOD': 'GET', + 'CONTENT_TYPE': 'application/json'}, + None) + + assert 'CONTENT_TYPE' not in mock.call_args[0][0] + + +def test_middleware_does_notstrip_content_type_from_other_methods(): + from bigchaindb.web.strip_content_type_middleware import StripContentTypeMiddleware + mock = Mock() + middleware = StripContentTypeMiddleware(mock) + middleware({'REQUEST_METHOD': 'POST', + 'CONTENT_TYPE': 'application/json'}, + None) + + assert 'CONTENT_TYPE' in mock.call_args[0][0] + + +def test_get_outputs_endpoint_with_content_type(client, user_pk): + res = client.get(OUTPUTS_ENDPOINT + '?public_key={}'.format(user_pk), + headers=[('Content-Type', 'application/json')]) + assert res.status_code == 200