mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-06-28 08:37:45 +02:00
Merge pull request #1209 from bigchaindb/tymlez-consensus
Tymlez consensus plugin
This commit is contained in:
commit
289080358d
|
@ -16,11 +16,15 @@ import copy
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import collections
|
import collections
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
from pkg_resources import iter_entry_points, ResolutionError
|
||||||
|
|
||||||
from bigchaindb.common import exceptions
|
from bigchaindb.common import exceptions
|
||||||
|
|
||||||
import bigchaindb
|
import bigchaindb
|
||||||
|
|
||||||
|
from bigchaindb.consensus import BaseConsensusRules
|
||||||
|
|
||||||
# TODO: move this to a proper configuration file for logging
|
# TODO: move this to a proper configuration file for logging
|
||||||
logging.getLogger('requests').setLevel(logging.WARNING)
|
logging.getLogger('requests').setLevel(logging.WARNING)
|
||||||
|
@ -240,3 +244,40 @@ def autoconfigure(filename=None, config=None, force=False):
|
||||||
newconfig = update(newconfig, config)
|
newconfig = update(newconfig, config)
|
||||||
|
|
||||||
set_config(newconfig) # sets bigchaindb.config
|
set_config(newconfig) # sets bigchaindb.config
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def load_consensus_plugin(name=None):
|
||||||
|
"""Find and load the chosen consensus plugin.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (string): the name of the entry_point, as advertised in the
|
||||||
|
setup.py of the providing package.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
an uninstantiated subclass of ``bigchaindb.consensus.AbstractConsensusRules``
|
||||||
|
"""
|
||||||
|
if not name:
|
||||||
|
return BaseConsensusRules
|
||||||
|
|
||||||
|
# TODO: This will return the first plugin with group `bigchaindb.consensus`
|
||||||
|
# and name `name` in the active WorkingSet.
|
||||||
|
# We should probably support Requirements specs in the config, e.g.
|
||||||
|
# consensus_plugin: 'my-plugin-package==0.0.1;default'
|
||||||
|
plugin = None
|
||||||
|
for entry_point in iter_entry_points('bigchaindb.consensus', name):
|
||||||
|
plugin = entry_point.load()
|
||||||
|
|
||||||
|
# No matching entry_point found
|
||||||
|
if not plugin:
|
||||||
|
raise ResolutionError(
|
||||||
|
'No plugin found in group `bigchaindb.consensus` with name `{}`'.
|
||||||
|
format(name))
|
||||||
|
|
||||||
|
# Is this strictness desireable?
|
||||||
|
# It will probably reduce developer headaches in the wild.
|
||||||
|
if not issubclass(plugin, (BaseConsensusRules,)):
|
||||||
|
raise TypeError('object of type "{}" does not implement `bigchaindb.'
|
||||||
|
'consensus.BaseConsensusRules`'.format(type(plugin)))
|
||||||
|
|
||||||
|
return plugin
|
||||||
|
|
|
@ -10,6 +10,11 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class BaseConsensusRules():
|
class BaseConsensusRules():
|
||||||
"""Base consensus rules for Bigchain.
|
"""Base consensus rules for Bigchain.
|
||||||
|
|
||||||
|
A consensus plugin must expose a class inheriting from this one via an entry_point.
|
||||||
|
|
||||||
|
All methods listed below must be implemented.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -56,10 +56,18 @@ class Bigchain(object):
|
||||||
self.me = public_key or bigchaindb.config['keypair']['public']
|
self.me = public_key or bigchaindb.config['keypair']['public']
|
||||||
self.me_private = private_key or bigchaindb.config['keypair']['private']
|
self.me_private = private_key or bigchaindb.config['keypair']['private']
|
||||||
self.nodes_except_me = keyring or bigchaindb.config['keyring']
|
self.nodes_except_me = keyring or bigchaindb.config['keyring']
|
||||||
|
|
||||||
if backlog_reassign_delay is None:
|
if backlog_reassign_delay is None:
|
||||||
backlog_reassign_delay = bigchaindb.config['backlog_reassign_delay']
|
backlog_reassign_delay = bigchaindb.config['backlog_reassign_delay']
|
||||||
self.backlog_reassign_delay = backlog_reassign_delay
|
self.backlog_reassign_delay = backlog_reassign_delay
|
||||||
self.consensus = BaseConsensusRules
|
|
||||||
|
consensusPlugin = bigchaindb.config.get('consensus_plugin')
|
||||||
|
|
||||||
|
if consensusPlugin:
|
||||||
|
self.consensus = config_utils.load_consensus_plugin(consensusPlugin)
|
||||||
|
else:
|
||||||
|
self.consensus = BaseConsensusRules
|
||||||
|
|
||||||
self.connection = connection if connection else backend.connect(**bigchaindb.config['database'])
|
self.connection = connection if connection else backend.connect(**bigchaindb.config['database'])
|
||||||
if not self.me or not self.me_private:
|
if not self.me or not self.me_private:
|
||||||
raise exceptions.KeypairNotFoundException()
|
raise exceptions.KeypairNotFoundException()
|
||||||
|
|
|
@ -13,6 +13,7 @@ from multipipes import Pipeline, Node
|
||||||
import bigchaindb
|
import bigchaindb
|
||||||
from bigchaindb import Bigchain
|
from bigchaindb import Bigchain
|
||||||
from bigchaindb import backend
|
from bigchaindb import backend
|
||||||
|
from bigchaindb import config_utils
|
||||||
from bigchaindb.backend.changefeed import ChangeFeed
|
from bigchaindb.backend.changefeed import ChangeFeed
|
||||||
from bigchaindb.consensus import BaseConsensusRules
|
from bigchaindb.consensus import BaseConsensusRules
|
||||||
from bigchaindb.models import Transaction, Block
|
from bigchaindb.models import Transaction, Block
|
||||||
|
@ -35,10 +36,17 @@ class Vote:
|
||||||
# Since cannot share a connection to RethinkDB using multiprocessing,
|
# Since cannot share a connection to RethinkDB using multiprocessing,
|
||||||
# we need to create a temporary instance of BigchainDB that we use
|
# we need to create a temporary instance of BigchainDB that we use
|
||||||
# only to query RethinkDB
|
# only to query RethinkDB
|
||||||
self.consensus = BaseConsensusRules
|
|
||||||
|
consensusPlugin = bigchaindb.config.get('consensus_plugin')
|
||||||
|
|
||||||
|
if consensusPlugin:
|
||||||
|
self.consensus = config_utils.load_consensus_plugin(consensusPlugin)
|
||||||
|
else:
|
||||||
|
self.consensus = BaseConsensusRules
|
||||||
|
|
||||||
# This is the Bigchain instance that will be "shared" (aka: copied)
|
# This is the Bigchain instance that will be "shared" (aka: copied)
|
||||||
# by all the subprocesses
|
# by all the subprocesses
|
||||||
|
|
||||||
self.bigchain = Bigchain()
|
self.bigchain = Bigchain()
|
||||||
self.last_voted_id = Bigchain().get_last_voted_block().id
|
self.last_voted_id = Bigchain().get_last_voted_block().id
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ For convenience, here's a list of all the relevant environment variables (docume
|
||||||
`BIGCHAINDB_SERVER_THREADS`<br>
|
`BIGCHAINDB_SERVER_THREADS`<br>
|
||||||
`BIGCHAINDB_CONFIG_PATH`<br>
|
`BIGCHAINDB_CONFIG_PATH`<br>
|
||||||
`BIGCHAINDB_BACKLOG_REASSIGN_DELAY`<br>
|
`BIGCHAINDB_BACKLOG_REASSIGN_DELAY`<br>
|
||||||
|
`BIGCHAINDB_CONSENSUS_PLUGIN`<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`
|
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`.
|
or using the `BIGCHAINDB_CONFIG_PATH` environment variable, e.g. `BIGHAINDB_CONFIG_PATH=.my_bigchaindb_config bigchaindb start`.
|
||||||
|
@ -54,7 +55,7 @@ Internally (i.e. in the Python code), both keys have a default value of `None`,
|
||||||
|
|
||||||
## keyring
|
## keyring
|
||||||
|
|
||||||
A list of the public keys of all the nodes in the cluster, excluding the public key of this node.
|
A list of the public keys of all the nodes in the cluster, excluding the public key of this node.
|
||||||
|
|
||||||
**Example using an environment variable**
|
**Example using an environment variable**
|
||||||
```text
|
```text
|
||||||
|
@ -65,7 +66,7 @@ Note how the keys in the list are separated by colons.
|
||||||
|
|
||||||
**Example config file snippet**
|
**Example config file snippet**
|
||||||
```js
|
```js
|
||||||
"keyring": ["BnCsre9MPBeQK8QZBFznU2dJJ2GwtvnSMdemCmod2XPB",
|
"keyring": ["BnCsre9MPBeQK8QZBFznU2dJJ2GwtvnSMdemCmod2XPB",
|
||||||
"4cYQHoQrvPiut3Sjs8fVR1BMZZpJjMTC4bsMTt9V71aQ"]
|
"4cYQHoQrvPiut3Sjs8fVR1BMZZpJjMTC4bsMTt9V71aQ"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -152,9 +153,23 @@ Specifies how long, in seconds, transactions can remain in the backlog before be
|
||||||
**Example using environment variables**
|
**Example using environment variables**
|
||||||
```text
|
```text
|
||||||
export BIGCHAINDB_BACKLOG_REASSIGN_DELAY=30
|
export BIGCHAINDB_BACKLOG_REASSIGN_DELAY=30
|
||||||
```
|
```
|
||||||
|
|
||||||
**Default value (from a config file)**
|
**Default value (from a config file)**
|
||||||
```js
|
```js
|
||||||
"backlog_reassign_delay": 120
|
"backlog_reassign_delay": 120
|
||||||
|
```
|
||||||
|
|
||||||
|
## consensus_plugin
|
||||||
|
|
||||||
|
The [consensus plugin](../appendices/consensus.html) to use.
|
||||||
|
|
||||||
|
**Example using an environment variable**
|
||||||
|
```text
|
||||||
|
export BIGCHAINDB_CONSENSUS_PLUGIN=default
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example config file snippet: the default**
|
||||||
|
```js
|
||||||
|
"consensus_plugin": "default"
|
||||||
```
|
```
|
||||||
|
|
|
@ -48,6 +48,36 @@ def test_bigchain_instance_raises_when_not_configured(request, monkeypatch):
|
||||||
bigchaindb.Bigchain()
|
bigchaindb.Bigchain()
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_consensus_plugin_loads_default_rules_without_name():
|
||||||
|
from bigchaindb import config_utils
|
||||||
|
from bigchaindb.consensus import BaseConsensusRules
|
||||||
|
|
||||||
|
assert config_utils.load_consensus_plugin() == BaseConsensusRules
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_consensus_plugin_raises_with_unknown_name():
|
||||||
|
from pkg_resources import ResolutionError
|
||||||
|
from bigchaindb import config_utils
|
||||||
|
|
||||||
|
with pytest.raises(ResolutionError):
|
||||||
|
config_utils.load_consensus_plugin('bogus')
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_consensus_plugin_raises_with_invalid_subclass(monkeypatch):
|
||||||
|
# Monkeypatch entry_point.load to return something other than a
|
||||||
|
# ConsensusRules instance
|
||||||
|
from bigchaindb import config_utils
|
||||||
|
import time
|
||||||
|
monkeypatch.setattr(config_utils,
|
||||||
|
'iter_entry_points',
|
||||||
|
lambda *args: [type('entry_point', (object), {'load': lambda: object})])
|
||||||
|
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
# Since the function is decorated with `lru_cache`, we need to
|
||||||
|
# "miss" the cache using a name that has not been used previously
|
||||||
|
config_utils.load_consensus_plugin(str(time.time()))
|
||||||
|
|
||||||
|
|
||||||
def test_map_leafs_iterator():
|
def test_map_leafs_iterator():
|
||||||
from bigchaindb import config_utils
|
from bigchaindb import config_utils
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user