mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge branch 'master' into upsert-val-approve
This commit is contained in:
commit
08bb60ecb8
@ -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:
|
||||
return conn.run(
|
||||
conn.collection('validators')
|
||||
.insert_one(validator_update))
|
||||
except DuplicateKeyError:
|
||||
raise MultipleValidatorOperationError('Validator update already exists')
|
||||
def store_validator_set(conn, validators_update):
|
||||
height = validators_update['height']
|
||||
return conn.run(
|
||||
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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -10,7 +10,9 @@ required:
|
||||
- operation
|
||||
- outputs
|
||||
properties:
|
||||
operation: "VALIDATOR_ELECTION_VOTE"
|
||||
operation:
|
||||
type: string
|
||||
value: "VALIDATOR_ELECTION_VOTE"
|
||||
outputs:
|
||||
type: array
|
||||
items:
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,12 +37,12 @@ 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']['data']))
|
||||
validators[public_key] = validator['voting_power']
|
||||
@ -49,7 +54,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
|
||||
@ -83,7 +88,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
|
||||
@ -98,7 +103,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:
|
||||
@ -144,3 +149,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 []
|
||||
|
||||
@ -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)
|
||||
|
||||
37
bigchaindb/upsert_validator/validator_utils.py
Normal file
37
bigchaindb/upsert_validator/validator_utils.py
Normal 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())
|
||||
@ -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.
|
||||
|
||||
@ -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).
|
||||
|
||||
There’s no node that has a long-term special position in the cluster. All nodes run the same software and perform the same duties.
|
||||
There’s 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.
|
||||
|
||||
It’s 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 can’t do that because the admin user doesn’t have the necessary information (e.g. private keys).
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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.
|
||||
@ -4,36 +4,36 @@ SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
Code is Apache-2.0 and docs are CC-BY-4.0
|
||||
--->
|
||||
|
||||
# Basic AWS Setup
|
||||
# Basic AWS Setup
|
||||
|
||||
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>" \
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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?
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
|
||||
@ -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.)
|
||||
|
||||
@ -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*.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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::
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.)
|
||||
|
||||
@ -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,
|
||||
|
||||
12
docs/server/source/network-operations/index.rst
Normal file
12
docs/server/source/network-operations/index.rst
Normal 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
|
||||
@ -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`
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
```
|
||||
|
||||
3
setup.py
3
setup.py
@ -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',
|
||||
]
|
||||
|
||||
@ -390,7 +390,7 @@ 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': {'data': 'zL/DasvKulXZzhSNFwx4cLRXKkSM9GPK7Y0nZ4FEylM=',
|
||||
'type': 'tendermint/PubKeyEd25519'},
|
||||
@ -404,8 +404,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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -26,7 +26,7 @@ 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({
|
||||
@ -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])
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user