Merge branch 'master' of github.com:bigchaindb/bigchaindb

This commit is contained in:
z-bowen 2018-08-20 17:09:49 +02:00
commit fa3a8ddb2c
37 changed files with 640 additions and 365 deletions

View File

@ -8,7 +8,6 @@ from pymongo import DESCENDING
from bigchaindb import backend
from bigchaindb.backend.exceptions import DuplicateKeyError
from bigchaindb.common.exceptions import MultipleValidatorOperationError
from bigchaindb.backend.utils import module_dispatch_registrar
from bigchaindb.backend.localmongodb.connection import LocalMongoDBConnection
from bigchaindb.common.transaction import Transaction
@ -115,7 +114,8 @@ def get_spent(conn, transaction_id, output):
def get_latest_block(conn):
return conn.run(
conn.collection('blocks')
.find_one(sort=[('height', DESCENDING)]))
.find_one(projection={'_id': False},
sort=[('height', DESCENDING)]))
@register_query(LocalMongoDBConnection)
@ -282,13 +282,15 @@ def get_pre_commit_state(conn, commit_id):
@register_query(LocalMongoDBConnection)
def store_validator_set(conn, validator_update):
try:
def store_validator_set(conn, validators_update):
height = validators_update['height']
return conn.run(
conn.collection('validators')
.insert_one(validator_update))
except DuplicateKeyError:
raise MultipleValidatorOperationError('Validator update already exists')
conn.collection('validators').replace_one(
{'height': height},
validators_update,
upsert=True
)
)
@register_query(LocalMongoDBConnection)
@ -305,3 +307,16 @@ def get_validator_set(conn, height=None):
)
return list(cursor)[0]
@register_query(LocalMongoDBConnection)
def get_asset_tokens_for_public_key(conn, asset_id, public_key):
query = {'outputs.public_keys': [public_key],
'asset.id': asset_id}
cursor = conn.run(
conn.collection('transactions').aggregate([
{'$match': query},
{'$project': {'_id': False}}
]))
return cursor

View File

@ -372,3 +372,17 @@ def get_validator_set(conn, height):
"""
raise NotImplementedError
@singledispatch
def get_asset_tokens_for_public_key(connection, asset_id,
public_key, operation):
"""Retrieve a list of tokens of type `asset_id` that are owned by the `public_key`.
Args:
asset_id (str): Id of the token.
public_key (str): base58 encoded public key
operation: filter transaction based on `operation`
Returns:
Iterator of transaction that list given owner in conditions.
"""
raise NotImplementedError

View File

@ -9,7 +9,6 @@ import logging
import jsonschema
import yaml
import rapidjson
import rapidjson_schema
from bigchaindb.common.exceptions import SchemaValidationError
@ -22,7 +21,7 @@ def _load_schema(name, path=__file__):
path = os.path.join(os.path.dirname(path), name + '.yaml')
with open(path) as handle:
schema = yaml.safe_load(handle)
fast_schema = rapidjson_schema.loads(rapidjson.dumps(schema))
fast_schema = rapidjson.Validator(rapidjson.dumps(schema))
return path, (schema, fast_schema)
@ -57,7 +56,7 @@ def _validate_schema(schema, body):
# a helpful error message.
try:
schema[1].validate(rapidjson.dumps(body))
schema[1](rapidjson.dumps(body))
except ValueError as exc:
try:
jsonschema.validate(body, schema[0])

View File

@ -10,7 +10,9 @@ required:
- operation
- outputs
properties:
operation: "VALIDATOR_ELECTION_VOTE"
operation:
type: string
value: "VALIDATOR_ELECTION_VOTE"
outputs:
type: array
items:

View File

@ -6,7 +6,6 @@
with Tendermint.
"""
import logging
import codecs
from abci.application import BaseApplication
from abci.types_pb2 import (
@ -17,8 +16,6 @@ from abci.types_pb2 import (
ResponseDeliverTx,
ResponseEndBlock,
ResponseCommit,
Validator,
PubKey
)
from bigchaindb import BigchainDB
@ -26,6 +23,8 @@ from bigchaindb.tendermint_utils import (decode_transaction,
calculate_hash)
from bigchaindb.lib import Block, PreCommitState
from bigchaindb.backend.query import PRE_COMMIT_ID
from bigchaindb.upsert_validator import ValidatorElection
import bigchaindb.upsert_validator.validator_utils as vutils
CodeTypeOk = 0
@ -52,7 +51,7 @@ class App(BaseApplication):
def init_chain(self, genesis):
"""Initialize chain with block of height 0"""
validator_set = [decode_validator(v) for v in genesis.validators]
validator_set = [vutils.decode_validator(v) for v in genesis.validators]
block = Block(app_hash='', height=0, transactions=[])
self.bigchaindb.store_block(block._asdict())
self.bigchaindb.store_validator_set(1, validator_set)
@ -141,11 +140,11 @@ class App(BaseApplication):
else:
self.block_txn_hash = block['app_hash']
# TODO: calculate if an election has concluded
# NOTE: ensure the local validator set is updated
# validator_updates = self.bigchaindb.get_validator_update()
# validator_updates = [encode_validator(v) for v in validator_updates]
validator_updates = []
# Check if the current block concluded any validator elections and
# update the locally tracked validator set
validator_updates = ValidatorElection.get_validator_update(self.bigchaindb,
self.new_height,
self.block_transactions)
# Store pre-commit state to recover in case there is a crash
# during `commit`
@ -176,22 +175,3 @@ class App(BaseApplication):
self.block_txn_ids)
logger.benchmark('COMMIT_BLOCK, height:%s', self.new_height)
return ResponseCommit(data=data)
def encode_validator(v):
ed25519_public_key = v['pub_key']['data']
# NOTE: tendermint expects public to be encoded in go-amino format
pub_key = PubKey(type='ed25519',
data=bytes.fromhex(ed25519_public_key))
return Validator(pub_key=pub_key,
address=b'',
power=v['power'])
def decode_validator(v):
return {'address': codecs.encode(v.address, 'hex').decode().upper().rstrip('\n'),
'pub_key': {'type': v.pub_key.type,
'data': codecs.encode(v.pub_key.data, 'base64').decode().rstrip('\n')},
'voting_power': v.power}

View File

@ -29,6 +29,7 @@ from bigchaindb.tendermint_utils import encode_transaction, merkleroot
from bigchaindb import exceptions as core_exceptions
from bigchaindb.consensus import BaseConsensusRules
logger = logging.getLogger(__name__)
@ -442,16 +443,8 @@ class BigchainDB(object):
def get_validators(self, height=None):
result = backend.query.get_validator_set(self.connection, height)
validators = result['validators']
for v in validators:
v.pop('address')
v['voting_power'] = int(v['voting_power'])
return validators
def get_validator_update(self):
update = backend.query.get_validator_update(self.connection)
return [update['validator']] if update else []
def delete_validator_update(self):
return backend.query.delete_validator_update(self.connection)

View File

@ -3,5 +3,5 @@
# Code is Apache-2.0 and docs are CC-BY-4.0
from bigchaindb.upsert_validator.validator_election import ValidatorElection # noqa
from bigchaindb.upsert_validator.validator_election_vote import ValidatorElectionVote # noqa
from bigchaindb.upsert_validator.validator_election import ValidatorElection # noqa

View File

@ -2,6 +2,9 @@
# 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 bigchaindb import backend
from bigchaindb.common.exceptions import (InvalidSignature,
MultipleInputsError,
InvalidProposer,
@ -15,6 +18,8 @@ from bigchaindb.common.schema import (_validate_schema,
TX_SCHEMA_VALIDATOR_ELECTION,
TX_SCHEMA_COMMON,
TX_SCHEMA_CREATE)
from . import ValidatorElectionVote
from .validator_utils import (new_validator_set, encode_validator)
class ValidatorElection(Transaction):
@ -32,15 +37,15 @@ class ValidatorElection(Transaction):
super().__init__(operation, asset, inputs, outputs, metadata, version, hash_id)
@classmethod
def current_validators(cls, bigchain):
def get_validators(cls, bigchain, height=None):
"""Return a dictionary of validators with key as `public_key` and
value as the `voting_power`
"""
validators = {}
for validator in bigchain.get_validators():
for validator in bigchain.get_validators(height):
# NOTE: we assume that Tendermint encodes public key in base64
public_key = public_key_from_ed25519_key(key_from_base64(validator['pub_key']['value']))
public_key = public_key_from_ed25519_key(key_from_base64(validator['pub_key']['data']))
validators[public_key] = validator['voting_power']
return validators
@ -50,7 +55,7 @@ class ValidatorElection(Transaction):
"""Convert validator dictionary to a recipient list for `Transaction`"""
recipients = []
for public_key, voting_power in cls.current_validators(bigchain).items():
for public_key, voting_power in cls.get_validators(bigchain).items():
recipients.append(([public_key], voting_power))
return recipients
@ -84,7 +89,7 @@ class ValidatorElection(Transaction):
bigchain (BigchainDB): an instantiated bigchaindb.lib.BigchainDB object.
Returns:
`True` if the election is valid
ValidatorElection object
Raises:
ValidationError: If the election is invalid
@ -99,7 +104,7 @@ class ValidatorElection(Transaction):
if not self.inputs_valid(input_conditions):
raise InvalidSignature('Transaction signature is invalid.')
current_validators = self.current_validators(bigchain)
current_validators = self.get_validators(bigchain)
# NOTE: Proposer should be a single node
if len(self.inputs) != 1 or len(self.inputs[0].owners_before) != 1:
@ -118,7 +123,7 @@ class ValidatorElection(Transaction):
if not self.is_same_topology(current_validators, self.outputs):
raise UnequalValidatorSet('Validator set much be exactly same to the outputs of election')
return True
return self
@classmethod
def generate(cls, initiator, voters, election_data, metadata=None):
@ -145,3 +150,77 @@ class ValidatorElection(Transaction):
@classmethod
def transfer(cls, tx_signers, recipients, metadata=None, asset=None):
raise NotImplementedError
@classmethod
def to_public_key(cls, election_id):
return base58.b58encode(bytes.fromhex(election_id))
@classmethod
def count_votes(cls, election_pk, transactions, getter=getattr):
votes = 0
for txn in transactions:
if getter(txn, 'operation') == 'VALIDATOR_ELECTION_VOTE':
for output in getter(txn, 'outputs'):
# NOTE: We enforce that a valid vote to election id will have only
# election_pk in the output public keys, including any other public key
# along with election_pk will lead to vote being not considered valid.
if len(getter(output, 'public_keys')) == 1 and [election_pk] == getter(output, 'public_keys'):
votes = votes + int(getter(output, 'amount'))
return votes
def get_commited_votes(self, bigchain, election_pk=None):
if election_pk is None:
election_pk = self.to_public_key(self.id)
txns = list(backend.query.get_asset_tokens_for_public_key(bigchain.connection,
self.id,
election_pk))
return self.count_votes(election_pk, txns, dict.get)
@classmethod
def has_concluded(cls, bigchain, election_id, current_votes=[], height=None):
"""Check if the given `election_id` can be concluded or not
NOTE:
* Election is concluded iff the current validator set is exactly equal
to the validator set encoded in election outputs
* Election can concluded only if the current votes achieves a supermajority
"""
election = bigchain.get_transaction(election_id)
if election:
election_pk = election.to_public_key(election.id)
votes_commited = election.get_commited_votes(bigchain, election_pk)
votes_current = election.count_votes(election_pk, current_votes)
current_validators = election.get_validators(bigchain, height)
if election.is_same_topology(current_validators, election.outputs):
total_votes = sum(current_validators.values())
if (votes_commited < (2/3)*total_votes) and \
(votes_commited + votes_current >= (2/3)*total_votes):
return election
return False
@classmethod
def get_validator_update(cls, bigchain, new_height, txns):
votes = {}
for txn in txns:
if not isinstance(txn, ValidatorElectionVote):
continue
election_id = txn.asset['id']
election_votes = votes.get(election_id, [])
election_votes.append(txn)
votes[election_id] = election_votes
election = cls.has_concluded(bigchain, election_id, election_votes, new_height)
# Once an election concludes any other conclusion for the same
# or any other election is invalidated
if election:
# The new validator set comes into effect from height = new_height+1
validator_updates = [election.asset['data']]
curr_validator_set = bigchain.get_validators(new_height)
updated_validator_set = new_validator_set(curr_validator_set,
new_height, validator_updates)
bigchain.store_validator_set(new_height+1, updated_validator_set)
return [encode_validator(election.asset['data'])]
return []

View File

@ -2,8 +2,6 @@
# 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 bigchaindb.common.transaction import Transaction
from bigchaindb.common.schema import (_validate_schema,
TX_SCHEMA_COMMON,
@ -30,7 +28,7 @@ class ValidatorElectionVote(Transaction):
bigchain (BigchainDB): an instantiated bigchaindb.lib.BigchainDB object.
Returns:
`True` if the election vote is valid
ValidatorElectionVote object
Raises:
ValidationError: If the election vote is invalid
@ -38,10 +36,6 @@ class ValidatorElectionVote(Transaction):
self.validate_transfer_inputs(bigchain, current_transactions)
return self
@classmethod
def to_public_key(cls, election_id):
return base58.b58encode(bytes.fromhex(election_id))
@classmethod
def generate(cls, inputs, recipients, election_id, metadata=None):
(inputs, outputs) = cls.validate_transfer(inputs, recipients, election_id, metadata)

View File

@ -0,0 +1,37 @@
import codecs
from abci.types_pb2 import (Validator,
PubKey)
from bigchaindb.tendermint_utils import public_key_to_base64
def encode_validator(v):
ed25519_public_key = v['public_key']
# NOTE: tendermint expects public to be encoded in go-amino format
pub_key = PubKey(type='ed25519',
data=bytes.fromhex(ed25519_public_key))
return Validator(pub_key=pub_key,
address=b'',
power=v['power'])
def decode_validator(v):
return {'pub_key': {'type': v.pub_key.type,
'data': codecs.encode(v.pub_key.data, 'base64').decode().rstrip('\n')},
'voting_power': v.power}
def new_validator_set(validators, height, updates):
validators_dict = {}
for v in validators:
validators_dict[v['pub_key']['data']] = v
updates_dict = {}
for u in updates:
public_key64 = public_key_to_base64(u['public_key'])
updates_dict[public_key64] = {'pub_key': {'type': 'ed25519',
'data': public_key64},
'voting_power': u['power']}
new_validators_dict = {**validators_dict, **updates_dict}
return list(new_validators_dict.values())

View File

@ -8,7 +8,7 @@ How BigchainDB is Good for Asset Registrations & Transfers
BigchainDB can store data of any kind (within reason), but it's designed to be particularly good for storing asset registrations and transfers:
* The fundamental thing that one sends to a BigchainDB cluster, to be checked and stored (if valid), is a *transaction*, and there are two kinds: CREATE transactions and TRANSFER transactions.
* The fundamental thing that one sends to a BigchainDB network, to be checked and stored (if valid), is a *transaction*, and there are two kinds: CREATE transactions and TRANSFER transactions.
* A CREATE transaction can be use to register any kind of asset (divisible or indivisible), along with arbitrary metadata.
* An asset can have zero, one, or several owners.
* The owners of an asset can specify (crypto-)conditions which must be satisfied by anyone wishing transfer the asset to new owners. For example, a condition might be that at least 3 of the 5 current owners must cryptographically sign a TRANSFER transaction.

View File

@ -8,9 +8,9 @@ Code is Apache-2.0 and docs are CC-BY-4.0
Decentralization means that no one owns or controls everything, and there is no single point of failure.
Ideally, each node in a BigchainDB cluster is owned and controlled by a different person or organization. Even if the cluster lives within one organization, it's still preferable to have each node controlled by a different person or subdivision.
Ideally, each node in a BigchainDB network is owned and controlled by a different person or organization. Even if the network lives within one organization, it's still preferable to have each node controlled by a different person or subdivision.
We use the phrase "BigchainDB consortium" (or just "consortium") to refer to the set of people and/or organizations who run the nodes of a BigchainDB cluster. A consortium requires some form of governance to make decisions such as membership and policies. The exact details of the governance process are determined by each consortium, but it can be very decentralized.
We use the phrase "BigchainDB consortium" (or just "consortium") to refer to the set of people and/or organizations who run the nodes of a BigchainDB network. A consortium requires some form of governance to make decisions such as membership and policies. The exact details of the governance process are determined by each consortium, but it can be very decentralized.
If sharding is turned on (i.e. if the number of shards is larger than one), then the actual data is decentralized in that no one node stores all the data.
@ -18,8 +18,8 @@ Every node has its own locally-stored list of the public keys of other consortiu
A consortium can increase its decentralization (and its resilience) by increasing its jurisdictional diversity, geographic diversity, and other kinds of diversity. This idea is expanded upon in [the section on node diversity](diversity.html).
Theres no node that has a long-term special position in the cluster. All nodes run the same software and perform the same duties.
Theres no node that has a long-term special position in the BigchainDB network. All nodes run the same software and perform the same duties.
If someone has (or gets) admin access to a node, they can mess with that node (e.g. change or delete data stored on that node), but those changes should remain isolated to that node. The BigchainDB cluster can only be compromised if more than one third of the nodes get compromised. See the [Tendermint documentation](https://tendermint.readthedocs.io/projects/tools/en/master/introduction.html) for more details.
If someone has (or gets) admin access to a node, they can mess with that node (e.g. change or delete data stored on that node), but those changes should remain isolated to that node. The BigchainDB network can only be compromised if more than one third of the nodes get compromised. See the [Tendermint documentation](https://tendermint.readthedocs.io/projects/tools/en/master/introduction.html) for more details.
Its worth noting that not even the admin or superuser of a node can transfer assets. The only way to create a valid transfer transaction is to fulfill the current crypto-conditions on the asset, and the admin/superuser cant do that because the admin user doesnt have the necessary information (e.g. private keys).

View File

@ -13,7 +13,7 @@ including `decentralization <decentralized.html>`_,
`immutability <immutable.html>`_
and `native support for assets <assets.html>`_.
At a high level, one can communicate with a BigchainDB cluster (set of nodes) using the BigchainDB HTTP API, or a wrapper for that API, such as the BigchainDB Python Driver. Each BigchainDB node runs BigchainDB Server and various other software. The `terminology page <terminology.html>`_ explains some of those terms in more detail.
At a high level, one can communicate with a BigchainDB network (set of nodes) using the BigchainDB HTTP API, or a wrapper for that API, such as the BigchainDB Python Driver. Each BigchainDB node runs BigchainDB Server and various other software. The `terminology page <terminology.html>`_ explains some of those terms in more detail.
.. raw:: html

View File

@ -12,14 +12,14 @@ There is some specialized terminology associated with BigchainDB. To get started
A **BigchainDB node** is a machine (or logical machine) running [BigchainDB Server](https://docs.bigchaindb.com/projects/server/en/latest/introduction.html) and related software. Each node is controlled by one person or organization.
## BigchainDB Cluster
## BigchainDB Network
A set of BigchainDB nodes can connect to each other to form a **BigchainDB cluster**. Each node in the cluster runs the same software. A cluster may have additional machines to do things such as cluster monitoring.
A set of BigchainDB nodes can connect to each other to form a **BigchainDB network**. Each node in the network runs the same software. A BigchainDB network may have additional machines to do things such as monitoring.
## BigchainDB Consortium
The people and organizations that run the nodes in a cluster belong to a **BigchainDB consortium** (i.e. another organization). A consortium must have some sort of governance structure to make decisions. If a cluster is run by a single company, then the "consortium" is just that company.
The people and organizations that run the nodes in a BigchainDB network belong to a **BigchainDB consortium** (i.e. another organization). A consortium must have some sort of governance structure to make decisions. If a BigchainDB network is run by a single company, then the "consortium" is just that company.
**What's the Difference Between a Cluster and a Consortium?**
**What's the Difference Between a BigchainDB Network and a Consortium?**
A cluster is just a bunch of connected nodes. A consortium is an organization which has a cluster, and where each node in the cluster has a different operator.
A BigchaindB network is just a bunch of connected nodes. A consortium is an organization which has a BigchainDB network, and where each node in that network has a different operator.

View File

@ -8,32 +8,32 @@ Code is Apache-2.0 and docs are CC-BY-4.0
Before you can deploy anything on AWS, you must do a few things.
## Get an AWS Account
If you don't already have an AWS account, you can [sign up for one for free at aws.amazon.com](https://aws.amazon.com/).
## Install the AWS Command-Line Interface
To install the AWS Command-Line Interface (CLI), just do:
```text
pip install awscli
```
## Create an AWS Access Key
The next thing you'll need is AWS access keys (access key ID and secret access key). If you don't have those, see [the AWS documentation about access keys](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys).
You should also pick a default AWS region name (e.g. `eu-central-1`). That's where your cluster will run. The AWS documentation has [a list of them](http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region).
You should also pick a default AWS region name (e.g. `eu-central-1`). The AWS documentation has [a list of them](http://docs.aws.amazon.com/general/latest/gr/rande.html#ec2_region).
Once you've got your AWS access key, and you've picked a default AWS region name, go to a terminal session and enter:
```text
aws configure
```
and answer the four questions. For example:
```text
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
@ -43,17 +43,16 @@ Default output format [None]: [Press Enter]
This writes two files: `~/.aws/credentials` and `~/.aws/config`. AWS tools and packages look for those files.
## Generate an RSA Key Pair for SSH
Eventually, you'll have one or more instances (virtual machines) running on AWS and you'll want to SSH to them. To do that, you need a public/private key pair. The public key will be sent to AWS, and you can tell AWS to put it in any instances you provision there. You'll keep the private key on your local workstation.
See the [page about how to generate a key pair for SSH](generate-key-pair-for-ssh.html).
## Send the Public Key to AWS
To send the public key to AWS, use the AWS Command-Line Interface:
```text
aws ec2 import-key-pair \
--key-name "<key-name>" \

View File

@ -12,9 +12,9 @@ BigchainDB Server Documentation
← Back to All BigchainDB Docs <https://docs.bigchaindb.com/en/latest/index.html>
introduction
quickstart
simple-network-setup
network-operations/index
production-nodes/index
clusters
networks
dev-and-test/index
server-reference/index
http-client-server-api

View File

@ -14,17 +14,15 @@ Note that there are a few kinds of nodes:
- A **dev/test node** is a node created by a developer working on BigchainDB Server, e.g. for testing new or changed code. A dev/test node is typically run on the developer's local machine.
- A **bare-bones node** is a node deployed in the cloud, either as part of a testing cluster or as a starting point before upgrading the node to be production-ready.
- A **production node** is a node that is part of a consortium's BigchainDB cluster. A production node has the most components and requirements.
- A **bare-bones node** is a node deployed in the cloud, either as part of a testing network or as a starting point before upgrading the node to be production-ready.
- A **production node** is a node that is part of a consortium's BigchainDB network. A production node has the most components and requirements.
## Setup Instructions for Various Cases
* [Quickstart](quickstart.html)
* [Set up a local BigchainDB node for development, experimenting and testing](https://docs.bigchaindb.com/projects/contributing/en/latest/dev-setup-coding-and-contribution-process/index.html)
* [Set up and run a BigchainDB cluster](clusters.html)
- [Quickstart](quickstart.html)
- [Set up a local BigchainDB node for development, experimenting and testing](https://docs.bigchaindb.com/projects/contributing/en/latest/dev-setup-coding-and-contribution-process/index.html)
- [Set up and run a BigchainDB network](network-operations/index.html)
## Can I Help?

View File

@ -9,8 +9,8 @@ How to Set Up a Self-Signed Certificate Authority
=================================================
This page enumerates the steps *we* use to set up a self-signed certificate authority (CA).
This is something that only needs to be done once per cluster,
by the organization managing the cluster, i.e. the CA is for the whole cluster.
This is something that only needs to be done once per BigchainDB network,
by the organization managing the network, i.e. the CA is for the whole network.
We use Easy-RSA.

View File

@ -9,7 +9,7 @@ How to Generate a Client Certificate for MongoDB
================================================
This page enumerates the steps *we* use to generate a client certificate to be
used by clients who want to connect to a TLS-secured MongoDB cluster.
used by clients who want to connect to a TLS-secured MongoDB database.
We use Easy-RSA.
@ -42,7 +42,7 @@ and using:
You should change the Common Name (e.g. ``bdb-instance-0``)
to a value that reflects what the
client certificate is being used for, e.g. ``mdb-mon-instance-3`` or ``mdb-bak-instance-4``. (The final integer is specific to your BigchainDB node in the BigchainDB cluster.)
client certificate is being used for, e.g. ``mdb-mon-instance-3`` or ``mdb-bak-instance-4``. (The final integer is specific to your BigchainDB node in the BigchainDB network.)
You will be prompted to enter the Distinguished Name (DN) information for this certificate. For each field, you can accept the default value [in brackets] by pressing Enter.
@ -66,7 +66,7 @@ Step 3: Get the Client Certificate Signed
The CSR file created in the previous step
should be located in ``pki/reqs/bdb-instance-0.req``
(or whatever Common Name you used in the ``gen-req`` command above).
You need to send it to the organization managing the cluster
You need to send it to the organization managing the BigchainDB network
so that they can use their CA
to sign the request.
(The managing organization should already have a self-signed CA.)

View File

@ -17,7 +17,7 @@ Kubernetes Deployment Template
and your organization has people who know Kubernetes,
then this Kubernetes deployment template might be helpful.
This section outlines a way to deploy a BigchainDB node (or BigchainDB cluster)
This section outlines a way to deploy a BigchainDB node (or BigchainDB network)
on Microsoft Azure using Kubernetes.
You may choose to use it as a template or reference for your own deployment,
but *we make no claim that it is suitable for your purposes*.

View File

@ -170,7 +170,7 @@ Until we figure out a way to obtain the *workspace key* via the command line,
you can get it via the OMS Portal.
To get to the OMS Portal, go to the Azure Portal and click on:
Resource Groups > (Your k8s cluster's resource group) > Log analytics (OMS) > (Name of the only item listed) > OMS Workspace > OMS Portal
Resource Groups > (Your Kubernetes cluster's resource group) > Log analytics (OMS) > (Name of the only item listed) > OMS Workspace > OMS Portal
(Let us know if you find a faster way.)
Then see `Microsoft's instructions to obtain your workspace ID and key

View File

@ -74,7 +74,7 @@ This user is created on the *admin* database with the authorization to create ot
vars.BDB_PERSISTENT_PEERS, BDB_VALIDATORS, BDB_VALIDATORS_POWERS, BDB_GENESIS_TIME and BDB_CHAIN_ID
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These parameters are shared across the cluster. More information about the generation
These parameters are shared across the BigchainDB network. More information about the generation
of these parameters can be found at :ref:`generate-the-blockchain-id-and-genesis-time`.
@ -82,7 +82,7 @@ vars.NODE_DNS_SERVER
^^^^^^^^^^^^^^^^^^^^
IP of Kubernetes service(kube-dns), can be retrieved using
using CLI(kubectl) or k8s dashboard. This parameter is used by the Nginx gateway instance
to resolve the hostnames of all the services running in the k8s cluster.
to resolve the hostnames of all the services running in the Kubernetes cluster.
.. code::

View File

@ -7,9 +7,9 @@ How to Revoke an SSL/TLS Certificate
====================================
This page enumerates the steps *we* take to revoke a self-signed SSL/TLS
certificate in a cluster.
certificate in a BigchainDB network.
It can only be done by someone with access to the self-signed CA
associated with the cluster's managing organization.
associated with the network's managing organization.
Step 1: Revoke a Certificate
----------------------------
@ -45,4 +45,4 @@ Generate a new CRL for your infrastructure using:
The generated ``crl.pem`` file needs to be uploaded to your infrastructure to
prevent the revoked certificate from being used again.
In particlar, the generated ``crl.pem`` file should be sent to all BigchainDB node operators in your BigchainDB cluster, so that they can update it in their MongoDB instance and their BigchainDB Server instance.
In particlar, the generated ``crl.pem`` file should be sent to all BigchainDB node operators in your BigchainDB network, so that they can update it in their MongoDB instance and their BigchainDB Server instance.

View File

@ -47,7 +47,7 @@ and using something like:
./easyrsa --req-cn=mdb-instance-0 --subject-alt-name=DNS:localhost,DNS:mdb-instance-0 gen-req mdb-instance-0 nopass
You should replace the Common Name (``mdb-instance-0`` above) with the correct name for *your* MongoDB instance in the cluster, e.g. ``mdb-instance-5`` or ``mdb-instance-12``. (This name is decided by the organization managing the cluster.)
You should replace the Common Name (``mdb-instance-0`` above) with the correct name for *your* MongoDB instance in the network, e.g. ``mdb-instance-5`` or ``mdb-instance-12``. (This name is decided by the organization managing the network.)
You will be prompted to enter the Distinguished Name (DN) information for this certificate.
For each field, you can accept the default value [in brackets] by pressing Enter.
@ -68,7 +68,7 @@ Step 3: Get the Server Certificate Signed
The CSR file created in the last step
should be located in ``pki/reqs/mdb-instance-0.req``
(where the integer ``0`` may be different for you).
You need to send it to the organization managing the cluster
You need to send it to the organization managing the BigchainDB network
so that they can use their CA
to sign the request.
(The managing organization should already have a self-signed CA.)

View File

@ -20,7 +20,7 @@ Overview
then this Kubernetes deployment template might be helpful.
This page summarizes some steps to go through
to set up a BigchainDB cluster.
to set up a BigchainDB network.
You can modify them to suit your needs.
.. _generate-the-blockchain-id-and-genesis-time:
@ -30,7 +30,7 @@ Generate All Shared BigchainDB Setup Parameters
There are some shared BigchainDB setup paramters that every node operator
in the consortium shares
because they are properties of the Tendermint cluster.
because they are properties of the Tendermint network.
They look like this:
.. code::
@ -46,7 +46,7 @@ Those paramters only have to be generated once, by one member of the consortium.
That person will then share the results (Tendermint setup parameters)
with all the node operators.
The above example parameters are for a cluster of 4 initial (seed) nodes.
The above example parameters are for a network of 4 initial (seed) nodes.
Note how ``BDB_PERSISTENT_PEERS``, ``BDB_VALIDATORS`` and ``BDB_VALIDATOR_POWERS`` are lists
with 4 items each.
**If your consortium has a different number of initial nodes,
@ -119,7 +119,7 @@ to all POST requests with a secret token in the HTTP headers.
You can make up that ``SECRET_TOKEN`` now.
For example, ``superSECRET_token4-POST*requests``.
You will put it in the ``vars`` file later.
Every BigchainDB node in a cluster can have a different secret token.
Every BigchainDB node in a BigchainDB network can have a different secret token.
To make an HTTP POST request to your BigchainDB node,
you must include an HTTP header named ``X-Secret-Access-Token``
and set it equal to your secret token, e.g.
@ -136,7 +136,7 @@ and set it equal to your secret token, e.g.
between different Kubernetes clusters, especially if they are running different versions of Kubernetes.
We tested this Kubernetes Deployment Template on Azure ACS in February 2018 and at that time
ACS was deploying a **Kubernetes 1.7.7** cluster. If you can force your cluster to have that version of Kubernetes,
then you'll increase the likelihood that everything will work in your cluster.
then you'll increase the likelihood that everything will work.
4. Deploy your BigchainDB node inside your new Kubernetes cluster.
You will fill up the ``vars`` file,

View File

@ -0,0 +1,12 @@
.. 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
How to Set Up and Run a BigchainDB Network
==========================================
.. toctree::
:maxdepth: 1
network-setup

View File

@ -4,9 +4,9 @@ SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
Code is Apache-2.0 and docs are CC-BY-4.0
--->
# Clusters
# BigchainDB Networks
A **BigchainDB Cluster** is a set of connected **BigchainDB Nodes**, managed by a **BigchainDB Consortium** (i.e. an organization). Those terms are defined in the [BigchainDB Terminology page](https://docs.bigchaindb.com/en/latest/terminology.html).
A **BigchainDB network** is a set of connected **BigchainDB nodes**, managed by a **BigchainDB consortium** (i.e. an organization). Those terms are defined in the [BigchainDB Terminology page](https://docs.bigchaindb.com/en/latest/terminology.html).
## Consortium Structure & Governance
@ -15,18 +15,18 @@ It must make many decisions, e.g. How will new members be added? Who can read th
A governance process is required to make those decisions, and therefore one of the first steps for any new consortium is to specify its governance process (if one doesn't already exist).
This documentation doesn't explain how to create a consortium, nor does it outline the possible governance processes.
It's worth noting that the decentralization of a BigchainDB cluster depends,
It's worth noting that the decentralization of a BigchainDB network depends,
to some extent, on the decentralization of the associated consortium. See the pages about [decentralization](https://docs.bigchaindb.com/en/latest/decentralized.html) and [node diversity](https://docs.bigchaindb.com/en/latest/diversity.html).
## Cluster DNS Records and SSL Certificates
## DNS Records and SSL Certificates
We now describe how *we* set up the external (public-facing) DNS records for a BigchainDB cluster. Your consortium may opt to do it differently.
We now describe how *we* set up the external (public-facing) DNS records for a BigchainDB network. Your consortium may opt to do it differently.
There were several goals:
* Allow external users/clients to connect directly to any BigchainDB node in the cluster (over the internet), if they want.
* Allow external users/clients to connect directly to any BigchainDB node in the network (over the internet), if they want.
* Each BigchainDB node operator should get an SSL certificate for their BigchainDB node, so that their BigchainDB node can serve the [BigchainDB HTTP API](http-client-server-api.html) via HTTPS. (The same certificate might also be used to serve the [WebSocket API](websocket-event-stream-api.html).)
* There should be no sharing of SSL certificates among BigchainDB node operators.
* Optional: Allow clients to connect to a "random" BigchainDB node in the cluster at one particular domain (or subdomain).
* Optional: Allow clients to connect to a "random" BigchainDB node in the network at one particular domain (or subdomain).
### Node Operator Responsibilities
@ -36,8 +36,8 @@ There were several goals:
### Consortium Responsibilities
Optional: The consortium managing the BigchainDB cluster could register a domain name and set up CNAME records mapping that domain name (or one of its subdomains) to each of the nodes in the cluster. For example, if the consortium registered `bdbcluster.io`, they could set up CNAME records like the following:
Optional: The consortium managing the BigchainDB network could register a domain name and set up CNAME records mapping that domain name (or one of its subdomains) to each of the nodes in the network. For example, if the consortium registered `bdbnetwork.io`, they could set up CNAME records like the following:
* CNAME record mapping `api.bdbcluster.io` to `abc-org73.net`
* CNAME record mapping `api.bdbcluster.io` to `api.dynabob8.io`
* CNAME record mapping `api.bdbcluster.io` to `figmentdb3.ninja`
* CNAME record mapping `api.bdbnetwork.io` to `abc-org73.net`
* CNAME record mapping `api.bdbnetwork.io` to `api.dynabob8.io`
* CNAME record mapping `api.bdbnetwork.io` to `figmentdb3.ninja`

View File

@ -8,11 +8,10 @@ Code is Apache-2.0 and docs are CC-BY-4.0
Be sure you know the key BigchainDB terminology:
* [BigchainDB node, BigchainDB cluster and BigchainDB consortium](https://docs.bigchaindb.com/en/latest/terminology.html)
* [BigchainDB node, BigchainDB network and BigchainDB consortium](https://docs.bigchaindb.com/en/latest/terminology.html)
* [dev/test node, bare-bones node and production node](../introduction.html)
We make some assumptions about production nodes:
1. Production nodes use MongoDB (not RethinkDB, PostgreSQL, Couchbase or whatever).
1. Each production node is set up and managed by an experienced professional system administrator or a team of them.
1. Each production node in a cluster is managed by a different person or team.
1. Each production node in a network is managed by a different person or team.

View File

@ -17,7 +17,7 @@ It could also include several other components, including:
* NGINX or similar, to provide authentication, rate limiting, etc.
* An NTP daemon running on all machines running BigchainDB Server or mongod, and possibly other machines
* **Not** MongoDB Automation Agent. It's for automating the deployment of an entire MongoDB cluster, not just one MongoDB node within a cluster.
* Probably _not_ MongoDB Automation Agent. It's for automating the deployment of an entire MongoDB cluster.
* MongoDB Monitoring Agent
* MongoDB Backup Agent
* Log aggregation software

View File

@ -6,43 +6,20 @@ Code is Apache-2.0 and docs are CC-BY-4.0
# Configuration Settings
The value of each BigchainDB Server configuration setting is determined according to the following rules:
Every BigchainDB Server configuration setting has two names: a config-file name and an environment variable name. For example, one of the settings has the config-file name `database.host` and the environment variable name `BIGCHAINDB_DATABASE_HOST`. Here are some more examples:
`database.port``BIGCHAINDB_DATABASE_PORT`
`database.keyfile_passphrase``BIGCHAINDB_DATABASE_KEYFILE_PASSPHRASE`
`server.bind``BIGCHAINDB_SERVER_BIND`
The value of each setting is determined according to the following rules:
* If it's set by an environment variable, then use that value
* Otherwise, if it's set in a local config file, then use that value
* Otherwise, use the default value
For convenience, here's a list of all the relevant environment variables (documented below):
`BIGCHAINDB_DATABASE_BACKEND`<br>
`BIGCHAINDB_DATABASE_HOST`<br>
`BIGCHAINDB_DATABASE_PORT`<br>
`BIGCHAINDB_DATABASE_NAME`<br>
`BIGCHAINDB_DATABASE_CONNECTION_TIMEOUT`<br>
`BIGCHAINDB_DATABASE_MAX_TRIES`<br>
`BIGCHAINDB_SERVER_BIND`<br>
`BIGCHAINDB_SERVER_LOGLEVEL`<br>
`BIGCHAINDB_SERVER_WORKERS`<br>
`BIGCHAINDB_WSSERVER_SCHEME`<br>
`BIGCHAINDB_WSSERVER_HOST`<br>
`BIGCHAINDB_WSSERVER_PORT`<br>
`BIGCHAINDB_WSSERVER_ADVERTISED_SCHEME`<br>
`BIGCHAINDB_WSSERVER_ADVERTISED_HOST`<br>
`BIGCHAINDB_WSSERVER_ADVERTISED_PORT`<br>
`BIGCHAINDB_CONFIG_PATH`<br>
`BIGCHAINDB_LOG`<br>
`BIGCHAINDB_LOG_FILE`<br>
`BIGCHAINDB_LOG_ERROR_FILE`<br>
`BIGCHAINDB_LOG_LEVEL_CONSOLE`<br>
`BIGCHAINDB_LOG_LEVEL_LOGFILE`<br>
`BIGCHAINDB_LOG_DATEFMT_CONSOLE`<br>
`BIGCHAINDB_LOG_DATEFMT_LOGFILE`<br>
`BIGCHAINDB_LOG_FMT_CONSOLE`<br>
`BIGCHAINDB_LOG_FMT_LOGFILE`<br>
`BIGCHAINDB_TENDERMINT_HOST`<br>
`BIGCHAINDB_TENDERMINT_PORT`<br>
The local config file is `$HOME/.bigchaindb` by default (a file which might not even exist), but you can tell BigchainDB to use a different file by using the `-c` command-line option, e.g. `bigchaindb -c path/to/config_file.json start`
or using the `BIGCHAINDB_CONFIG_PATH` environment variable, e.g. `BIGHAINDB_CONFIG_PATH=.my_bigchaindb_config bigchaindb start`.
Note that the `-c` command line option will always take precedence if both the `BIGCHAINDB_CONFIG_PATH` and the `-c` command line option are used.
@ -51,25 +28,30 @@ You can read the current default values in the file [bigchaindb/\_\_init\_\_.py]
Running `bigchaindb -y configure localmongodb` will generate a local config file in `$HOME/.bigchaindb` with all the default values.
## database.*
The settings with names of the form `database.*` are for the database backend
The settings with names of the form `database.*` are for the backend database
(currently only MongoDB). They are:
* `database.backend` is only `localmongodb`, currently.
* `database.backend` can only be `localmongodb`, currently.
* `database.host` is the hostname (FQDN) of the backend database.
* `database.port` is self-explanatory.
* `database.name` is a user-chosen name for the database inside MongoDB, e.g. `bigchain`.
* `database.connection_timeout` is the maximum number of milliseconds that BigchainDB will wait before giving up on one attempt to connect to the database backend.
* `database.max_tries` is the maximum number of times that BigchainDB will try to establish a connection with the database backend. If 0, then it will try forever.
* `database.connection_timeout` is the maximum number of milliseconds that BigchainDB will wait before giving up on one attempt to connect to the backend database.
* `database.max_tries` is the maximum number of times that BigchainDB will try to establish a connection with the backend database. If 0, then it will try forever.
* `database.replicaset` is the name of the MongoDB replica set. The default value is `null` because in BighainDB 2.0+, each BigchainDB node has its own independent MongoDB database and no replica set is necessary.
* `database.login` and `database.password` are the login and password used to authenticate to the backend database, specified in plaintext.
* `database.ssl` determines if BigchainDB connects to MongoDB over TLS/SSL or not. It can be set to `true` or `false`.
* `database.ca_cert`, `database.certfile`, `database.keyfile` and `database.crlfile` are the paths to the CA, signed certificate, private key and certificate revocation list files respectively.
* `database.keyfile_passphrase` is the private key decryption passphrase, specified in plaintext.
**Example using environment variables**
```text
export BIGCHAINDB_DATABASE_BACKEND=localmongodb
export BIGCHAINDB_DATABASE_HOST=localhost
export BIGCHAINDB_DATABASE_PORT=27017
export BIGCHAINDB_DATABASE_NAME=database8
export BIGCHAINDB_DATABASE_CONNECTION_TIMEOUT=5000
export BIGCHAINDB_DATABASE_MAX_TRIES=3
```
@ -77,30 +59,31 @@ export BIGCHAINDB_DATABASE_MAX_TRIES=3
**Default values**
If (no environment variables were set and there's no local config file), or you used `bigchaindb -y configure localmongodb` to create a default local config file for a `localmongodb` backend, then the defaults will be:
```js
"database": {
"backend": "localmongodb",
"host": "localhost",
"port": 27017,
"name": "bigchain",
"replicaset": null,
"connection_timeout": 5000,
"max_tries": 3,
"replicaset": null,
"login": null,
"password": null
"ssl": false,
"ca_cert": null,
"crlfile": null,
"certfile": null,
"keyfile": null,
"crlfile": null,
"keyfile_passphrase": null,
}
```
## server.*
## server.bind, server.loglevel & server.workers
These settings are for the [Gunicorn HTTP server](http://gunicorn.org/), which is used to serve the [HTTP client-server API](../http-client-server-api.html).
`server.bind`, `server.loglevel` and `server.workers`
are settings for the [Gunicorn HTTP server](http://gunicorn.org/), which is used to serve the [HTTP client-server API](../http-client-server-api.html).
`server.bind` is where to bind the Gunicorn HTTP server socket. It's a string. It can be any valid value for [Gunicorn's bind setting](http://docs.gunicorn.org/en/stable/settings.html#bind). If you want to allow IPv4 connections from anyone, on port 9984, use `0.0.0.0:9984`. In a production setting, we recommend you use Gunicorn behind a reverse proxy server. If Gunicorn and the reverse proxy are running on the same machine, then use `localhost:PORT` where PORT is _not_ 9984 (because the reverse proxy needs to listen on port 9984). Maybe use PORT=9983 in that case because we know 9983 isn't used. If Gunicorn and the reverse proxy are running on different machines, then use `A.B.C.D:9984` where A.B.C.D is the IP address of the reverse proxy. There's [more information about deploying behind a reverse proxy in the Gunicorn documentation](http://docs.gunicorn.org/en/stable/deploy.html). (They call it a proxy.)
@ -111,6 +94,7 @@ for more information.
`server.workers` is [the number of worker processes](http://docs.gunicorn.org/en/stable/settings.html#workers) for handling requests. If set to `None`, the value will be (2 × cpu_count + 1). Each worker process has a single thread. The HTTP server will be able to handle `server.workers` requests simultaneously.
**Example using environment variables**
```text
export BIGCHAINDB_SERVER_BIND=0.0.0.0:9984
export BIGCHAINDB_SERVER_LOGLEVEL=debug
@ -118,6 +102,7 @@ export BIGCHAINDB_SERVER_WORKERS=5
```
**Example config file snippet**
```js
"server": {
"bind": "0.0.0.0:9984",
@ -127,6 +112,7 @@ export BIGCHAINDB_SERVER_WORKERS=5
```
**Default values (from a config file)**
```js
"server": {
"bind": "localhost:9984",
@ -135,8 +121,10 @@ export BIGCHAINDB_SERVER_WORKERS=5
}
```
## wsserver.*
## wsserver.scheme, wsserver.host and wsserver.port
### wsserver.scheme, wsserver.host and wsserver.port
These settings are for the
[aiohttp server](https://aiohttp.readthedocs.io/en/stable/index.html),
@ -150,6 +138,7 @@ If you want to allow connections from anyone, on port 9985,
set `wsserver.host` to 0.0.0.0 and `wsserver.port` to 9985.
**Example using environment variables**
```text
export BIGCHAINDB_WSSERVER_SCHEME=ws
export BIGCHAINDB_WSSERVER_HOST=0.0.0.0
@ -157,6 +146,7 @@ export BIGCHAINDB_WSSERVER_PORT=9985
```
**Example config file snippet**
```js
"wsserver": {
"scheme": "wss",
@ -166,6 +156,7 @@ export BIGCHAINDB_WSSERVER_PORT=9985
```
**Default values (from a config file)**
```js
"wsserver": {
"scheme": "ws",
@ -174,7 +165,7 @@ export BIGCHAINDB_WSSERVER_PORT=9985
}
```
## wsserver.advertised_scheme, wsserver.advertised_host and wsserver.advertised_port
### wsserver.advertised_scheme, wsserver.advertised_host and wsserver.advertised_port
These settings are for the advertising the Websocket URL to external clients in
the root API endpoint. These configurations might be useful if your deployment
@ -182,6 +173,7 @@ is hosted behind a firewall, NAT, etc. where the exposed public IP or domain is
different from where BigchainDB is running.
**Example using environment variables**
```text
export BIGCHAINDB_WSSERVER_ADVERTISED_SCHEME=wss
export BIGCHAINDB_WSSERVER_ADVERTISED_HOST=mybigchaindb.com
@ -189,6 +181,7 @@ export BIGCHAINDB_WSSERVER_ADVERTISED_PORT=443
```
**Example config file snippet**
```js
"wsserver": {
"advertised_scheme": "wss",
@ -198,6 +191,7 @@ export BIGCHAINDB_WSSERVER_ADVERTISED_PORT=443
```
**Default values (from a config file)**
```js
"wsserver": {
"advertised_scheme": "ws",
@ -206,14 +200,13 @@ export BIGCHAINDB_WSSERVER_ADVERTISED_PORT=443
}
```
## log
## log.*
The `log` key is expected to point to a mapping (set of key/value pairs)
holding the logging configuration.
The `log.*` settings are to configure logging.
**Example**:
**Example**
```
```js
{
"log": {
"file": "/var/log/bigchaindb.log",
@ -223,13 +216,14 @@ holding the logging configuration.
"datefmt_console": "%Y-%m-%d %H:%M:%S",
"datefmt_logfile": "%Y-%m-%d %H:%M:%S",
"fmt_console": "%(asctime)s [%(levelname)s] (%(name)s) %(message)s",
"fmt_logfile": "%(asctime)s [%(levelname)s] (%(name)s) %(message)s"
"fmt_logfile": "%(asctime)s [%(levelname)s] (%(name)s) %(message)s",
"granular_levels": {}
}
```
**Defaults to**:
**Default values**
```
```js
{
"log": {
"file": "~/bigchaindb.log",
@ -239,49 +233,20 @@ holding the logging configuration.
"datefmt_console": "%Y-%m-%d %H:%M:%S",
"datefmt_logfile": "%Y-%m-%d %H:%M:%S",
"fmt_logfile": "[%(asctime)s] [%(levelname)s] (%(name)s) %(message)s (%(processName)-10s - pid: %(process)d)",
"fmt_console": "[%(asctime)s] [%(levelname)s] (%(name)s) %(message)s (%(processName)-10s - pid: %(process)d)"
"fmt_console": "[%(asctime)s] [%(levelname)s] (%(name)s) %(message)s (%(processName)-10s - pid: %(process)d)",
"granular_levels": {}
}
```
The next subsections explain each field of the `log` configuration.
### log.file
The full path to the file where logs should be written.
The user running `bigchaindb` must have write access to the
specified path.
### log.file & log.error_file
The full paths to the files where logs and error logs should be written to.
**Example**:
```
{
"log": {
"file": "/var/log/bigchaindb/bigchaindb.log"
"error_file": "/var/log/bigchaindb/bigchaindb-errors.log"
}
}
```
**Defaults to**:
* `"~/bigchaindb.log"`
* `"~/bigchaindb-errors.log"`
Please note that the user running `bigchaindb` must have write access to the
locations.
#### Log rotation
Log files have a size limit of 200 MB and will be rotated up to five times.
For example if we consider the log file setting:
```
{
"log": {
"file": "~/bigchain.log"
}
}
```
**Log rotation:** Log files have a size limit of 200 MB
and will be rotated up to five times.
For example, if we `log.file` is set to `"~/bigchain.log"`, then
logs would always be written to `bigchain.log`. Each time the file
`bigchain.log` reaches 200 MB it would be closed and renamed
`bigchain.log.1`. If `bigchain.log.1` and `bigchain.log.2` already exist they
@ -290,141 +255,73 @@ applied up to `bigchain.log.5` after which `bigchain.log.5` would be
overwritten by `bigchain.log.4`, thus ending the rotation cycle of whatever
logs were in `bigchain.log.5`.
### log.error_file
Similar to `log.file` (see above), this is the
full path to the file where error logs should be written.
### log.level_console
The log level used to log to the console. Possible allowed values are the ones
defined by [Python](https://docs.python.org/3.6/library/logging.html#levels),
but case insensitive for convenience's sake:
but case-insensitive for the sake of convenience:
```
```text
"critical", "error", "warning", "info", "benchmark", "debug", "notset"
```
**Example**:
```
{
"log": {
"level_console": "info"
}
}
```
**Defaults to**: `"info"`.
### log.level_logfile
The log level used to log to the log file. Possible allowed values are the ones
defined by [Python](https://docs.python.org/3.6/library/logging.html#levels),
but case insensitive for convenience's sake:
but case-insensitive for the sake of convenience:
```
```text
"critical", "error", "warning", "info", "benchmark", "debug", "notset"
```
**Example**:
```
{
"log": {
"level_file": "info"
}
}
```
**Defaults to**: `"info"`.
### log.datefmt_console
The format string for the date/time portion of a message, when logged to the
console.
**Example**:
```
{
"log": {
"datefmt_console": "%x %X %Z"
}
}
```
**Defaults to**: `"%Y-%m-%d %H:%M:%S"`.
For more information on how to construct the format string please consult the
table under Python's documentation of
[`time.strftime(format[, t])`](https://docs.python.org/3.6/library/time.html#time.strftime)
table under [Python's documentation of time.strftime(format[, t])](https://docs.python.org/3.6/library/time.html#time.strftime).
### log.datefmt_logfile
The format string for the date/time portion of a message, when logged to a log
file.
**Example**:
```
{
"log": {
"datefmt_logfile": "%c %z"
}
}
```
**Defaults to**: `"%Y-%m-%d %H:%M:%S"`.
For more information on how to construct the format string please consult the
table under Python's documentation of
[`time.strftime(format[, t])`](https://docs.python.org/3.6/library/time.html#time.strftime)
table under [Python's documentation of time.strftime(format[, t])](https://docs.python.org/3.6/library/time.html#time.strftime).
### log.fmt_console
A string used to format the log messages when logged to the console.
**Example**:
```
{
"log": {
"fmt_console": "%(asctime)s [%(levelname)s] %(message)s %(process)d"
}
}
```
**Defaults to**: `"[%(asctime)s] [%(levelname)s] (%(name)s) %(message)s (%(processName)-10s - pid: %(process)d)"`
For more information on possible formatting options please consult Python's
documentation on
[LogRecord attributes](https://docs.python.org/3.6/library/logging.html#logrecord-attributes)
[LogRecord attributes](https://docs.python.org/3.6/library/logging.html#logrecord-attributes).
### log.fmt_logfile
A string used to format the log messages when logged to a log file.
**Example**:
```
{
"log": {
"fmt_logfile": "%(asctime)s [%(levelname)s] %(message)s %(process)d"
}
}
```
**Defaults to**: `"[%(asctime)s] [%(levelname)s] (%(name)s) %(message)s (%(processName)-10s - pid: %(process)d)"`
For more information on possible formatting options please consult Python's
documentation on
[LogRecord attributes](https://docs.python.org/3.6/library/logging.html#logrecord-attributes)
[LogRecord attributes](https://docs.python.org/3.6/library/logging.html#logrecord-attributes).
### log.granular_levels
Log levels for BigchainDB's modules. This can be useful to control the log
level of specific parts of the application. As an example, if you wanted the
logging of the `core.py` module to be more verbose, you would set the
configuration shown in the example below.
**Example**:
**Example**
```
```js
{
"log": {
"granular_levels": {
@ -433,17 +330,22 @@ logging of the `core.py` module to be more verbose, you would set the
}
```
**Defaults to**: `{}`
**Default value**
## tendermint.host & tendermint.port
```js
{}
```
The settings with names of the form `tendermint.*` are for
consensus(Tendermint) backend that we are using:
## tendermint.*
* `tendermint.host` is the hostname (FQDN)/IP address of the tendermint backend.
The settings with names of the form `tendermint.*` tell BigchainDB Server
where it can connect to the node's Tendermint instance.
* `tendermint.host` is the hostname (FQDN)/IP address of the Tendermint instance.
* `tendermint.port` is self-explanatory.
**Example using environment variables**
```text
export BIGCHAINDB_TENDERMINT_HOST=tendermint
export BIGCHAINDB_TENDERMINT_PORT=26657
@ -454,6 +356,6 @@ export BIGCHAINDB_TENDERMINT_PORT=26657
```js
"tendermint": {
"host": "localhost",
"port": 26657,
"port": 26657
}
```

View File

@ -80,7 +80,7 @@ install_requires = [
'pymongo~=3.6',
'pysha3~=1.0.2',
'cryptoconditions~=0.6.0.dev',
'python-rapidjson==0.0.11',
'python-rapidjson~=0.6.0',
'logstats~=0.2.1',
'flask>=0.10.1',
'flask-cors~=3.0.0',
@ -90,7 +90,6 @@ install_requires = [
'jsonschema~=2.5.1',
'pyyaml~=3.12',
'aiohttp~=3.0',
'python-rapidjson-schema==0.1.1',
'bigchaindb-abci==0.5.1',
'setproctitle~=1.1.0',
]

View File

@ -388,9 +388,9 @@ def test_upsert_validator_new_with_tendermint(b, priv_validator_path, user_sk, m
def test_upsert_validator_new_without_tendermint(b, priv_validator_path, user_sk, monkeypatch):
from bigchaindb.commands.bigchaindb import run_upsert_validator_new
def mock_get():
def mock_get(height):
return [
{'pub_key': {'value': 'zL/DasvKulXZzhSNFwx4cLRXKkSM9GPK7Y0nZ4FEylM=',
{'pub_key': {'data': 'zL/DasvKulXZzhSNFwx4cLRXKkSM9GPK7Y0nZ4FEylM=',
'type': 'tendermint/PubKeyEd25519'},
'voting_power': 10}
]
@ -402,8 +402,6 @@ def test_upsert_validator_new_without_tendermint(b, priv_validator_path, user_sk
b.get_validators = mock_get
b.write_transaction = mock_write
monkeypatch.setattr('requests.get', mock_get)
args = Namespace(action='new',
public_key='CJxdItf4lz2PwEf4SmYNAu/c/VpmX39JEgC5YpH7fxg=',
power=1,

View File

@ -10,7 +10,11 @@ from abci.types_pb2 import (
RequestEndBlock
)
from bigchaindb.core import CodeTypeOk, CodeTypeError
from bigchaindb.core import (CodeTypeOk,
CodeTypeError,
)
from bigchaindb.upsert_validator.validator_utils import new_validator_set
from bigchaindb.tendermint_utils import public_key_to_base64
pytestmark = [pytest.mark.tendermint, pytest.mark.bdb]
@ -220,3 +224,26 @@ def test_store_pre_commit_state_in_end_block(b, alice, init_chain_request):
assert resp['commit_id'] == PRE_COMMIT_ID
assert resp['height'] == 100
assert resp['transactions'] == [tx.id]
def test_new_validator_set(b):
node1 = {'pub_key': {'type': 'ed25519',
'data': 'FxjS2/8AFYoIUqF6AcePTc87qOT7e4WGgH+sGCpTUDQ='},
'voting_power': 10}
node1_new_power = {'public_key': '1718D2DBFF00158A0852A17A01C78F4DCF3BA8E4FB7B8586807FAC182A535034',
'power': 20}
node2 = {'public_key': '1888A353B181715CA2554701D06C1665BC42C5D936C55EA9C5DBCBDB8B3F02A3',
'power': 10}
validators = [node1]
updates = [node1_new_power, node2]
b.store_validator_set(1, validators)
updated_validator_set = new_validator_set(b.get_validators(1), 1, updates)
updated_validators = []
for u in updates:
updated_validators.append({'pub_key': {'type': 'ed25519',
'data': public_key_to_base64(u['public_key'])},
'voting_power': u['power']})
assert updated_validator_set == updated_validators

View File

@ -11,7 +11,6 @@ import pytest
from abci.server import ProtocolHandler
from abci.encoding import read_messages
from copy import deepcopy
from io import BytesIO
@ -109,43 +108,6 @@ def test_app(tb, init_chain_request):
assert block0['app_hash'] == new_block_hash
@pytest.mark.skip
@pytest.mark.abci
def test_upsert_validator(b, alice):
from bigchaindb.backend.query import VALIDATOR_UPDATE_ID
from bigchaindb.backend import query, connect
from bigchaindb.models import Transaction
from bigchaindb.tendermint_utils import public_key_to_base64
import time
conn = connect()
power = 1
public_key = '9B3119650DF82B9A5D8A12E38953EA47475C09F0C48A4E6A0ECE182944B24403'
validator = {'pub_key': {'type': 'AC26791624DE60',
'data': public_key},
'power': power}
validator_update = {'validator': validator,
'update_id': VALIDATOR_UPDATE_ID}
query.store_validator_update(conn, deepcopy(validator_update))
tx = Transaction.create([alice.public_key],
[([alice.public_key], 1)],
asset=None)\
.sign([alice.private_key])
code, message = b.write_transaction(tx, 'broadcast_tx_commit')
assert code == 202
time.sleep(5)
validators = b.get_validators()
validators = [(v['pub_key']['value'], v['voting_power']) for v in validators]
public_key64 = public_key_to_base64(public_key)
assert ((public_key64, str(power)) in validators)
@pytest.mark.abci
def test_post_transaction_responses(tendermint_ws_url, b):
from bigchaindb.common.crypto import generate_key_pair

View File

@ -26,11 +26,11 @@ def new_validator():
def mock_get_validators(network_validators):
def validator_set():
def validator_set(height):
validators = []
for public_key, power in network_validators.items():
validators.append({
'pub_key': {'type': 'AC26791624DE60', 'value': public_key},
'pub_key': {'type': 'AC26791624DE60', 'data': public_key},
'voting_power': power
})
return validators
@ -44,3 +44,11 @@ def valid_election(b_mock, node_key, new_validator):
return ValidatorElection.generate([node_key.public_key],
voters,
new_validator, None).sign([node_key.private_key])
@pytest.fixture
def valid_election_b(b, node_key, new_validator):
voters = ValidatorElection.recipients(b)
return ValidatorElection.generate([node_key.public_key],
voters,
new_validator, None).sign([node_key.private_key])

View File

@ -3,14 +3,20 @@
# Code is Apache-2.0 and docs are CC-BY-4.0
import pytest
import codecs
from bigchaindb.upsert_validator import ValidatorElectionVote
from bigchaindb.tendermint_utils import public_key_to_base64
from bigchaindb.upsert_validator import ValidatorElection, ValidatorElectionVote
from bigchaindb.common.exceptions import AmountError
from bigchaindb.common.crypto import generate_key_pair
from bigchaindb.common.exceptions import ValidationError
pytestmark = [pytest.mark.tendermint, pytest.mark.bdb]
pytestmark = [pytest.mark.execute]
@pytest.mark.tendermint
@pytest.mark.bdb
def test_upsert_validator_valid_election_vote(b_mock, valid_election, ed25519_node_keys):
b_mock.store_bulk_transactions([valid_election])
@ -19,7 +25,7 @@ def test_upsert_validator_valid_election_vote(b_mock, valid_election, ed25519_no
public_key0 = input0.owners_before[0]
key0 = ed25519_node_keys[public_key0]
election_pub_key = ValidatorElectionVote.to_public_key(valid_election.id)
election_pub_key = ValidatorElection.to_public_key(valid_election.id)
vote = ValidatorElectionVote.generate([input0],
[([election_pub_key], votes)],
@ -28,9 +34,29 @@ def test_upsert_validator_valid_election_vote(b_mock, valid_election, ed25519_no
assert vote.validate(b_mock)
def test_upsert_validator_delegate_election_vote(b_mock, valid_election, ed25519_node_keys):
from bigchaindb.common.crypto import generate_key_pair
@pytest.mark.tendermint
@pytest.mark.bdb
def test_upsert_validator_valid_non_election_vote(b_mock, valid_election, ed25519_node_keys):
b_mock.store_bulk_transactions([valid_election])
input0 = valid_election.to_inputs()[0]
votes = valid_election.outputs[0].amount
public_key0 = input0.owners_before[0]
key0 = ed25519_node_keys[public_key0]
election_pub_key = ValidatorElection.to_public_key(valid_election.id)
# Ensure that threshold conditions are now allowed
with pytest.raises(ValidationError):
ValidatorElectionVote.generate([input0],
[([election_pub_key, key0.public_key], votes)],
election_id=valid_election.id)\
.sign([key0.private_key])
@pytest.mark.tendermint
@pytest.mark.bdb
def test_upsert_validator_delegate_election_vote(b_mock, valid_election, ed25519_node_keys):
alice = generate_key_pair()
b_mock.store_bulk_transactions([valid_election])
@ -48,7 +74,7 @@ def test_upsert_validator_delegate_election_vote(b_mock, valid_election, ed25519
assert delegate_vote.validate(b_mock)
b_mock.store_bulk_transactions([delegate_vote])
election_pub_key = ValidatorElectionVote.to_public_key(valid_election.id)
election_pub_key = ValidatorElection.to_public_key(valid_election.id)
alice_votes = delegate_vote.to_inputs()[0]
alice_casted_vote = ValidatorElectionVote.generate([alice_votes],
@ -65,6 +91,8 @@ def test_upsert_validator_delegate_election_vote(b_mock, valid_election, ed25519
assert key0_casted_vote.validate(b_mock)
@pytest.mark.tendermint
@pytest.mark.bdb
def test_upsert_validator_invalid_election_vote(b_mock, valid_election, ed25519_node_keys):
b_mock.store_bulk_transactions([valid_election])
@ -73,7 +101,7 @@ def test_upsert_validator_invalid_election_vote(b_mock, valid_election, ed25519_
public_key0 = input0.owners_before[0]
key0 = ed25519_node_keys[public_key0]
election_pub_key = ValidatorElectionVote.to_public_key(valid_election.id)
election_pub_key = ValidatorElection.to_public_key(valid_election.id)
vote = ValidatorElectionVote.generate([input0],
[([election_pub_key], votes+1)],
@ -82,3 +110,233 @@ def test_upsert_validator_invalid_election_vote(b_mock, valid_election, ed25519_
with pytest.raises(AmountError):
assert vote.validate(b_mock)
@pytest.mark.tendermint
@pytest.mark.bdb
def test_valid_election_votes_received(b_mock, valid_election, ed25519_node_keys):
alice = generate_key_pair()
b_mock.store_bulk_transactions([valid_election])
assert valid_election.get_commited_votes(b_mock) == 0
input0 = valid_election.to_inputs()[0]
votes = valid_election.outputs[0].amount
public_key0 = input0.owners_before[0]
key0 = ed25519_node_keys[public_key0]
# delegate some votes to alice
delegate_vote = ValidatorElectionVote.generate([input0],
[([alice.public_key], 4), ([key0.public_key], votes-4)],
election_id=valid_election.id)\
.sign([key0.private_key])
b_mock.store_bulk_transactions([delegate_vote])
assert valid_election.get_commited_votes(b_mock) == 0
election_public_key = ValidatorElection.to_public_key(valid_election.id)
alice_votes = delegate_vote.to_inputs()[0]
key0_votes = delegate_vote.to_inputs()[1]
alice_casted_vote = ValidatorElectionVote.generate([alice_votes],
[([election_public_key], 2), ([alice.public_key], 2)],
election_id=valid_election.id)\
.sign([alice.private_key])
assert alice_casted_vote.validate(b_mock)
b_mock.store_bulk_transactions([alice_casted_vote])
# Check if the delegated vote is count as valid vote
assert valid_election.get_commited_votes(b_mock) == 2
key0_casted_vote = ValidatorElectionVote.generate([key0_votes],
[([election_public_key], votes-4)],
election_id=valid_election.id)\
.sign([key0.private_key])
assert key0_casted_vote.validate(b_mock)
b_mock.store_bulk_transactions([key0_casted_vote])
assert valid_election.get_commited_votes(b_mock) == votes-2
@pytest.mark.tendermint
@pytest.mark.bdb
def test_valid_election_conclude(b_mock, valid_election, ed25519_node_keys):
# Node 0: cast vote
tx_vote0 = gen_vote(valid_election, 0, ed25519_node_keys)
# check if the vote is valid even before the election doesn't exist
with pytest.raises(ValidationError):
assert tx_vote0.validate(b_mock)
# store election
b_mock.store_bulk_transactions([valid_election])
# cannot conclude election as not votes exist
assert not ValidatorElection.has_concluded(b_mock, valid_election.id)
# validate vote
assert tx_vote0.validate(b_mock)
assert not ValidatorElection.has_concluded(b_mock, valid_election.id, [tx_vote0])
b_mock.store_bulk_transactions([tx_vote0])
assert not ValidatorElection.has_concluded(b_mock, valid_election.id)
# Node 1: cast vote
tx_vote1 = gen_vote(valid_election, 1, ed25519_node_keys)
# Node 2: cast vote
tx_vote2 = gen_vote(valid_election, 2, ed25519_node_keys)
# Node 3: cast vote
tx_vote3 = gen_vote(valid_election, 3, ed25519_node_keys)
assert tx_vote1.validate(b_mock)
assert not ValidatorElection.has_concluded(b_mock, valid_election.id, [tx_vote1])
# 2/3 is achieved in the same block so the election can be.has_concludedd
assert ValidatorElection.has_concluded(b_mock, valid_election.id, [tx_vote1, tx_vote2])
b_mock.store_bulk_transactions([tx_vote1])
assert not ValidatorElection.has_concluded(b_mock, valid_election.id)
assert tx_vote2.validate(b_mock)
assert tx_vote3.validate(b_mock)
# conclusion can be triggered my different votes in the same block
assert ValidatorElection.has_concluded(b_mock, valid_election.id, [tx_vote2])
assert ValidatorElection.has_concluded(b_mock, valid_election.id, [tx_vote2, tx_vote3])
b_mock.store_bulk_transactions([tx_vote2])
# Once the blockchain records >2/3 of the votes the election is assumed to be.has_concludedd
# so any invocation of `.has_concluded` for that election should return False
assert not ValidatorElection.has_concluded(b_mock, valid_election.id)
# Vote is still valid but the election cannot be.has_concludedd as it it assmed that it has
# been.has_concludedd before
assert tx_vote3.validate(b_mock)
assert not ValidatorElection.has_concluded(b_mock, valid_election.id, [tx_vote3])
@pytest.mark.abci
def test_upsert_validator(b, node_key, node_keys, new_validator, ed25519_node_keys):
import time
import requests
(node_pub, _) = list(node_keys.items())[0]
validators = [{'pub_key': {'type': 'ed25519',
'data': node_pub},
'voting_power': 10}]
latest_block = b.get_latest_block()
# reset the validator set
b.store_validator_set(latest_block['height'], validators)
power = 1
public_key = '9B3119650DF82B9A5D8A12E38953EA47475C09F0C48A4E6A0ECE182944B24403'
public_key64 = public_key_to_base64(public_key)
new_validator = {'public_key': public_key,
'node_id': 'some_node_id',
'power': power}
voters = ValidatorElection.recipients(b)
election = ValidatorElection.generate([node_key.public_key],
voters,
new_validator, None).sign([node_key.private_key])
code, message = b.write_transaction(election, 'broadcast_tx_commit')
assert code == 202
time.sleep(3)
assert b.get_transaction(election.id)
tx_vote = gen_vote(election, 0, ed25519_node_keys)
assert tx_vote.validate(b)
code, message = b.write_transaction(tx_vote, 'broadcast_tx_commit')
assert code == 202
time.sleep(3)
resp = requests.get(b.endpoint + 'validators')
validator_pub_keys = []
for v in resp.json()['result']['validators']:
validator_pub_keys.append(v['pub_key']['value'])
assert (public_key64 in validator_pub_keys)
new_validator_set = b.get_validators()
validator_pub_keys = []
for v in new_validator_set:
validator_pub_keys.append(v['pub_key']['data'])
assert (public_key64 in validator_pub_keys)
@pytest.mark.tendermint
@pytest.mark.bdb
def test_get_validator_update(b, node_keys, node_key, ed25519_node_keys):
reset_validator_set(b, node_keys, 1)
power = 1
public_key = '9B3119650DF82B9A5D8A12E38953EA47475C09F0C48A4E6A0ECE182944B24403'
public_key64 = public_key_to_base64(public_key)
new_validator = {'public_key': public_key,
'node_id': 'some_node_id',
'power': power}
voters = ValidatorElection.recipients(b)
election = ValidatorElection.generate([node_key.public_key],
voters,
new_validator).sign([node_key.private_key])
# store election
b.store_bulk_transactions([election])
tx_vote0 = gen_vote(election, 0, ed25519_node_keys)
tx_vote1 = gen_vote(election, 1, ed25519_node_keys)
tx_vote2 = gen_vote(election, 2, ed25519_node_keys)
assert not ValidatorElection.has_concluded(b, election.id, [tx_vote0])
assert not ValidatorElection.has_concluded(b, election.id, [tx_vote0, tx_vote1])
assert ValidatorElection.has_concluded(b, election.id, [tx_vote0, tx_vote1, tx_vote2])
assert ValidatorElection.get_validator_update(b, 4, [tx_vote0]) == []
assert ValidatorElection.get_validator_update(b, 4, [tx_vote0, tx_vote1]) == []
update = ValidatorElection.get_validator_update(b, 4, [tx_vote0, tx_vote1, tx_vote2])
update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n')
assert len(update) == 1
assert update_public_key == public_key64
b.store_bulk_transactions([tx_vote0, tx_vote1])
update = ValidatorElection.get_validator_update(b, 4, [tx_vote2])
print('update', update)
update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n')
assert len(update) == 1
assert update_public_key == public_key64
# ============================================================================
# Helper functions
# ============================================================================
def to_inputs(election, i, ed25519_node_keys):
input0 = election.to_inputs()[i]
votes = election.outputs[i].amount
public_key0 = input0.owners_before[0]
key0 = ed25519_node_keys[public_key0]
return (input0, votes, key0)
def gen_vote(election, i, ed25519_node_keys):
(input_i, votes_i, key_i) = to_inputs(election, i, ed25519_node_keys)
election_pub_key = ValidatorElection.to_public_key(election.id)
return ValidatorElectionVote.generate([input_i],
[([election_pub_key], votes_i)],
election_id=election.id)\
.sign([key_i.private_key])
def reset_validator_set(b, node_keys, height):
validators = []
for (node_pub, _) in node_keys.items():
validators.append({'pub_key': {'type': 'ed25519',
'data': node_pub},
'voting_power': 10})
b.store_validator_set(height, validators)