2020-04-06 11:52:18 +02:00
|
|
|
# Copyright © 2020 Interplanetary Database Association e.V.,
|
|
|
|
# BigchainDB and IPDB software contributors.
|
2018-08-16 12:31:32 +02:00
|
|
|
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
|
|
|
# Code is Apache-2.0 and docs are CC-BY-4.0
|
|
|
|
|
2017-11-30 15:04:14 +01:00
|
|
|
"""Schema validation related functions and data"""
|
2016-11-22 11:17:06 +01:00
|
|
|
import os.path
|
2017-05-31 11:33:57 +02:00
|
|
|
import logging
|
2016-11-22 11:17:06 +01:00
|
|
|
|
|
|
|
import jsonschema
|
|
|
|
import yaml
|
2017-05-23 13:19:10 +02:00
|
|
|
import rapidjson
|
2016-11-22 11:17:06 +01:00
|
|
|
|
|
|
|
from bigchaindb.common.exceptions import SchemaValidationError
|
|
|
|
|
|
|
|
|
2017-05-31 11:33:57 +02:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2018-07-27 17:38:24 +02:00
|
|
|
def _load_schema(name, path=__file__):
|
2017-11-30 15:04:14 +01:00
|
|
|
"""Load a schema from disk"""
|
2018-07-27 17:38:24 +02:00
|
|
|
path = os.path.join(os.path.dirname(path), name + '.yaml')
|
2016-11-24 13:54:09 +01:00
|
|
|
with open(path) as handle:
|
2016-11-25 15:12:12 +01:00
|
|
|
schema = yaml.safe_load(handle)
|
2018-08-17 14:08:49 +02:00
|
|
|
fast_schema = rapidjson.Validator(rapidjson.dumps(schema))
|
2017-05-23 13:19:10 +02:00
|
|
|
return path, (schema, fast_schema)
|
2016-11-22 11:17:06 +01:00
|
|
|
|
2017-11-21 14:52:15 +01:00
|
|
|
|
2018-02-15 11:07:16 +01:00
|
|
|
TX_SCHEMA_VERSION = 'v2.0'
|
2017-11-21 14:12:13 +01:00
|
|
|
|
|
|
|
TX_SCHEMA_PATH, TX_SCHEMA_COMMON = _load_schema('transaction_' +
|
|
|
|
TX_SCHEMA_VERSION)
|
|
|
|
_, TX_SCHEMA_CREATE = _load_schema('transaction_create_' +
|
|
|
|
TX_SCHEMA_VERSION)
|
|
|
|
_, TX_SCHEMA_TRANSFER = _load_schema('transaction_transfer_' +
|
|
|
|
TX_SCHEMA_VERSION)
|
2016-12-09 11:34:42 +01:00
|
|
|
|
2018-07-30 15:19:51 +02:00
|
|
|
_, TX_SCHEMA_VALIDATOR_ELECTION = _load_schema('transaction_validator_election_' +
|
|
|
|
TX_SCHEMA_VERSION)
|
|
|
|
|
2018-09-17 13:59:57 +02:00
|
|
|
_, TX_SCHEMA_CHAIN_MIGRATION_ELECTION = _load_schema('transaction_chain_migration_election_' +
|
|
|
|
TX_SCHEMA_VERSION)
|
|
|
|
|
2018-09-11 15:39:46 +02:00
|
|
|
_, TX_SCHEMA_VOTE = _load_schema('transaction_vote_' + TX_SCHEMA_VERSION)
|
2018-08-02 11:49:59 +02:00
|
|
|
|
2016-12-09 11:34:42 +01:00
|
|
|
|
2016-11-24 13:54:09 +01:00
|
|
|
def _validate_schema(schema, body):
|
2017-11-30 15:04:14 +01:00
|
|
|
"""Validate data against a schema"""
|
2017-05-23 13:19:10 +02:00
|
|
|
|
|
|
|
# Note
|
|
|
|
#
|
|
|
|
# Schema validation is currently the major CPU bottleneck of
|
|
|
|
# BigchainDB. the `jsonschema` library validates python data structures
|
|
|
|
# directly and produces nice error messages, but validation takes 4+ ms
|
|
|
|
# per transaction which is pretty slow. The rapidjson library validates
|
|
|
|
# much faster at 1.5ms, however it produces _very_ poor error messages.
|
|
|
|
# For this reason we use both, rapidjson as an optimistic pathway and
|
|
|
|
# jsonschema as a fallback in case there is a failure, so we can produce
|
|
|
|
# a helpful error message.
|
|
|
|
|
2016-11-22 11:17:06 +01:00
|
|
|
try:
|
2018-08-17 14:08:49 +02:00
|
|
|
schema[1](rapidjson.dumps(body))
|
2017-05-23 13:19:10 +02:00
|
|
|
except ValueError as exc:
|
|
|
|
try:
|
|
|
|
jsonschema.validate(body, schema[0])
|
|
|
|
except jsonschema.ValidationError as exc2:
|
|
|
|
raise SchemaValidationError(str(exc2)) from exc2
|
2017-05-31 11:33:57 +02:00
|
|
|
logger.warning('code problem: jsonschema did not raise an exception, wheras rapidjson raised %s', exc)
|
|
|
|
raise SchemaValidationError(str(exc)) from exc
|
2016-11-22 11:17:06 +01:00
|
|
|
|
|
|
|
|
2016-11-24 16:37:26 +01:00
|
|
|
def validate_transaction_schema(tx):
|
2017-11-30 15:04:14 +01:00
|
|
|
"""Validate a transaction dict.
|
2017-03-14 17:07:18 +01:00
|
|
|
|
|
|
|
TX_SCHEMA_COMMON contains properties that are common to all types of
|
|
|
|
transaction. TX_SCHEMA_[TRANSFER|CREATE] add additional constraints on top.
|
|
|
|
"""
|
|
|
|
_validate_schema(TX_SCHEMA_COMMON, tx)
|
2017-03-07 12:58:32 +01:00
|
|
|
if tx['operation'] == 'TRANSFER':
|
|
|
|
_validate_schema(TX_SCHEMA_TRANSFER, tx)
|
|
|
|
else:
|
|
|
|
_validate_schema(TX_SCHEMA_CREATE, tx)
|