mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge remote-tracking branch 'remotes/origin/master' into feat/inverted-threshold-condition
This commit is contained in:
commit
aa53f20661
@ -13,15 +13,15 @@ A scalable blockchain database. [The whitepaper](https://www.bigchaindb.com/whit
|
||||
|
||||
## Quick Start
|
||||
|
||||
### [Install and Run BigchainDB Server](http://bigchaindb.readthedocs.io/en/latest/installing-server.html)
|
||||
### [Install and Run BigchainDB Server](http://bigchaindb.readthedocs.io/en/latest/servers/installing-server.html)
|
||||
### [Run BigchainDB with Docker](http://bigchaindb.readthedocs.io/en/latest/installing-server.html#run-bigchaindb-with-docker)
|
||||
### [The Python Server API by Example](http://bigchaindb.readthedocs.io/en/latest/python-server-api-examples.html)
|
||||
### [The Python Driver API by Example](http://bigchaindb.readthedocs.io/en/latest/python-driver-api-examples.html)
|
||||
### [The Python Server API by Example](http://bigchaindb.readthedocs.io/en/latest/servers/python-server-api-examples.html)
|
||||
### [The Python Driver API by Example](http://bigchaindb.readthedocs.io/en/latest/drivers-clients/python-driver-api-examples.html)
|
||||
|
||||
## Links for Everyone
|
||||
* [BigchainDB.com](https://www.bigchaindb.com/) - the main BigchainDB website, including newsletter signup
|
||||
* [Whitepaper](https://www.bigchaindb.com/whitepaper/) - outlines the motivations, goals and core algorithms of BigchainDB
|
||||
* [Roadmap](ROADMAP.md)
|
||||
* [Roadmap](https://github.com/bigchaindb/org/blob/master/ROADMAP.md)
|
||||
* [Blog](https://medium.com/the-bigchaindb-blog)
|
||||
* [Twitter](https://twitter.com/BigchainDB)
|
||||
* [Google Group](https://groups.google.com/forum/#!forum/bigchaindb)
|
||||
@ -38,4 +38,3 @@ A scalable blockchain database. [The whitepaper](https://www.bigchaindb.com/whit
|
||||
* [Licenses](LICENSES.md) - open source & open content
|
||||
* [Imprint](https://www.bigchaindb.com/imprint/)
|
||||
* [Contact Us](https://www.bigchaindb.com/contact/)
|
||||
|
||||
|
@ -229,3 +229,41 @@ class Block(object):
|
||||
p_write.start()
|
||||
p_delete.start()
|
||||
|
||||
|
||||
class BlockDeleteRevert(object):
|
||||
|
||||
def __init__(self, q_delete_to_revert):
|
||||
self.q_delete_to_revert = q_delete_to_revert
|
||||
|
||||
def write_blocks(self):
|
||||
"""
|
||||
Write blocks to the bigchain
|
||||
"""
|
||||
|
||||
# create bigchain instance
|
||||
b = Bigchain()
|
||||
|
||||
# Write blocks
|
||||
while True:
|
||||
block = self.q_delete_to_revert.get()
|
||||
|
||||
# poison pill
|
||||
if block == 'stop':
|
||||
return
|
||||
|
||||
b.write_block(block)
|
||||
|
||||
def kill(self):
|
||||
for i in range(mp.cpu_count()):
|
||||
self.q_delete_to_revert.put('stop')
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Initialize, spawn, and start the processes
|
||||
"""
|
||||
|
||||
# initialize the processes
|
||||
p_write = ProcessGroup(name='write_blocks', target=self.write_blocks)
|
||||
|
||||
# start the processes
|
||||
p_write.start()
|
||||
|
@ -16,6 +16,10 @@ class GenesisBlockAlreadyExistsError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ImproperVoteError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Bigchain(object):
|
||||
"""Bigchain API
|
||||
|
||||
@ -415,8 +419,11 @@ class Bigchain(object):
|
||||
The block if the block is valid else it raises and exception
|
||||
describing the reason why the block is invalid.
|
||||
"""
|
||||
# First, make sure this node hasn't already voted on this block
|
||||
if self.has_previous_vote(block):
|
||||
return block
|
||||
|
||||
# First: Run the plugin block validation logic
|
||||
# Run the plugin block validation logic
|
||||
self.consensus.validate_block(self, block)
|
||||
|
||||
# Finally: Tentative assumption that every blockchain will want to
|
||||
@ -428,6 +435,26 @@ class Bigchain(object):
|
||||
|
||||
return block
|
||||
|
||||
def has_previous_vote(self, block):
|
||||
"""Check for previous votes from this node
|
||||
|
||||
Args:
|
||||
block (dict): block to check.
|
||||
|
||||
Returns:
|
||||
True if this block already has a valid vote from this node, False otherwise. If
|
||||
there is already a vote, but the vote is invalid, raises an ImproperVoteError
|
||||
"""
|
||||
if block['votes']:
|
||||
for vote in block['votes']:
|
||||
if vote['node_pubkey'] == self.me:
|
||||
if util.verify_vote_signature(block, vote):
|
||||
return True
|
||||
else:
|
||||
raise ImproperVoteError('Block {block_id} already has an incorrectly signed vote '
|
||||
'from public key {me}').format(block_id=block['id'], me=self.me)
|
||||
return False
|
||||
|
||||
def is_valid_block(self, block):
|
||||
"""Check whether a block is valid or invalid.
|
||||
|
||||
@ -525,6 +552,10 @@ class Bigchain(object):
|
||||
def write_vote(self, block, vote, block_number):
|
||||
"""Write the vote to the database."""
|
||||
|
||||
# First, make sure this block doesn't contain a vote from this node
|
||||
if self.has_previous_vote(block):
|
||||
return None
|
||||
|
||||
update = {'votes': r.row['votes'].append(vote)}
|
||||
|
||||
# We need to *not* override the existing block_number, if any
|
||||
|
@ -6,7 +6,7 @@ import rethinkdb as r
|
||||
import bigchaindb
|
||||
from bigchaindb import Bigchain
|
||||
from bigchaindb.voter import Voter, Election
|
||||
from bigchaindb.block import Block
|
||||
from bigchaindb.block import Block, BlockDeleteRevert
|
||||
from bigchaindb.web import server
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@ class Processes(object):
|
||||
self.q_new_block = mp.Queue()
|
||||
self.q_new_transaction = mp.Queue()
|
||||
self.q_block_new_vote = mp.Queue()
|
||||
self.q_revert_delete = mp.Queue()
|
||||
|
||||
def map_backlog(self):
|
||||
# listen to changes on the backlog and redirect the changes
|
||||
@ -69,7 +70,8 @@ class Processes(object):
|
||||
|
||||
# delete
|
||||
elif change['new_val'] is None:
|
||||
pass
|
||||
# this should never happen in regular operation
|
||||
self.q_revert_delete.put(change['old_val'])
|
||||
|
||||
# update (new vote)
|
||||
elif change['new_val'] is not None and change['old_val'] is not None:
|
||||
@ -80,6 +82,7 @@ class Processes(object):
|
||||
|
||||
# instantiate block and voter
|
||||
block = Block(self.q_new_transaction)
|
||||
delete_reverter = BlockDeleteRevert(self.q_revert_delete)
|
||||
|
||||
# start the web api
|
||||
app_server = server.create_server(bigchaindb.config['server'])
|
||||
@ -90,6 +93,7 @@ class Processes(object):
|
||||
p_map_bigchain = mp.Process(name='bigchain_mapper', target=self.map_bigchain)
|
||||
p_map_backlog = mp.Process(name='backlog_mapper', target=self.map_backlog)
|
||||
p_block = mp.Process(name='block', target=block.start)
|
||||
p_block_delete_revert = mp.Process(name='block_delete_revert', target=delete_reverter.start)
|
||||
p_voter = Voter(self.q_new_block)
|
||||
p_election = Election(self.q_block_new_vote)
|
||||
|
||||
@ -100,6 +104,7 @@ class Processes(object):
|
||||
p_map_backlog.start()
|
||||
logger.info('starting block')
|
||||
p_block.start()
|
||||
p_block_delete_revert.start()
|
||||
|
||||
logger.info('starting voter')
|
||||
p_voter.start()
|
||||
|
BIN
docs/source/_static/models_diagrams.odg
Normal file
BIN
docs/source/_static/models_diagrams.odg
Normal file
Binary file not shown.
BIN
docs/source/_static/stories_3_assets.png
Normal file
BIN
docs/source/_static/stories_3_assets.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
@ -23,7 +23,7 @@ To activate your plugin, you can either set the `consensus_plugin` field in your
|
||||
|
||||
## Plugin API
|
||||
|
||||
BigchainDB's [current plugin API](../../bigchaindb/consensus.py) exposes five functions in an `AbstractConsensusRules` class:
|
||||
BigchainDB's [current plugin API](https://github.com/bigchaindb/bigchaindb/blob/master/bigchaindb/consensus.py) exposes five functions in an `AbstractConsensusRules` class:
|
||||
|
||||
```python
|
||||
validate_transaction(bigchain, transaction)
|
@ -2,11 +2,11 @@
|
||||
|
||||
The section documents the cryptographic algorithms and Python implementations that we use.
|
||||
|
||||
The implementations that we have chosen for now are just for fast prototyping. Some of them are pure Python implementations which may be slow. As future work, we should look at other alternatives.
|
||||
Before hashing or computing the signature of a JSON document, we serialize it as described in [the section on JSON serialization](json-serialization.html).
|
||||
|
||||
## Hashes
|
||||
|
||||
For hashing we are using the sha3-256 algorithm and [pysha3](https://bitbucket.org/tiran/pykeccak) as the Python implementation. We store the hex encoded hash in the database. For example:
|
||||
We compute hashes using the SHA3-256 algorithm and [pysha3](https://bitbucket.org/tiran/pykeccak) as the Python implementation. We store the hex-encoded hash in the database. For example:
|
||||
|
||||
```python
|
||||
import hashlib
|
@ -4,7 +4,7 @@
|
||||
|
||||
**BigchainDB cluster.** A collection of servers running BigchainDB Server software and communicating with each other via RethinkDB protocols. a BigchainDB cluster contains one logical RethinkDB datastore.
|
||||
|
||||
**Block.** A collection of up to 1000 transactions, plus other things. They get chained together by votes. See [the section on the block model](models.html#the-block-model).
|
||||
**Block.** A collection of up to 1000 transactions, plus other things. They get chained together by votes. See [the section on the block model](../topic-guides/models.html#the-block-model).
|
||||
|
||||
**Federation.** 1. An organization with members and some kind of governance structure. 2. The BigchainDB cluster owned and operated by a Federation's members.
|
||||
|
||||
@ -12,6 +12,6 @@
|
||||
|
||||
**Node.** See _Federation node_.
|
||||
|
||||
**Transaction.** The basic informational unit. A transaction can represent the creation or transfer of a digital asset. See [the section on the transaction model](models.html#the-transaction-model).
|
||||
**Transaction.** The basic informational unit. A transaction can represent the creation or transfer of a digital asset. See [the section on the transaction model](../topic-guides/models.html#the-transaction-model).
|
||||
|
||||
**Vote.** Each federation node in a federation is required to vote on the validity of every block (i.e. whether the block valid or not). A node's vote on a block also includes the id of the block it considers to be the previous block. See [the section on the vote model](models.html#the-vote-model).
|
||||
**Vote.** Each federation node in a federation is required to vote on the validity of every block (i.e. whether the block valid or not). A node's vote on a block also includes the id of the block it considers to be the previous block. See [the section on the vote model](../topic-guides/models.html#the-vote-model).
|
16
docs/source/appendices/index.rst
Normal file
16
docs/source/appendices/index.rst
Normal file
@ -0,0 +1,16 @@
|
||||
.. You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Appendices
|
||||
==========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
glossary
|
||||
json-serialization
|
||||
cryptography
|
||||
the-Bigchain-class
|
||||
consensus
|
||||
local-rethinkdb-cluster
|
||||
licenses
|
@ -4,7 +4,7 @@ This section explains one way to deploy a multi-node RethinkDB cluster on one ma
|
||||
|
||||
## Launching More RethinkDB Nodes
|
||||
|
||||
Assuming you've already [installed RethinkDB](installing-server.html#install-and-run-rethinkdb-server) and have one RethinkDB node running, here's how you can launch two more nodes on the same machine. First, prepare two additional nodes. Note that the user who starts RethinkDB must have write access to the created directories:
|
||||
Assuming you've already [installed RethinkDB](../servers/installing-server.html#install-and-run-rethinkdb-server) and have one RethinkDB node running, here's how you can launch two more nodes on the same machine. First, prepare two additional nodes. Note that the user who starts RethinkDB must have write access to the created directories:
|
||||
|
||||
mkdir -p /path/to/node2
|
||||
mkdir -p /path/to/node3
|
10
docs/source/appendices/the-Bigchain-class.rst
Normal file
10
docs/source/appendices/the-Bigchain-class.rst
Normal file
@ -0,0 +1,10 @@
|
||||
##################
|
||||
The Bigchain class
|
||||
##################
|
||||
|
||||
The Bigchain class is the top-level Python API for BigchainDB. If you want to create and initialize a BigchainDB database, you create a Bigchain instance (object). Then you can use its various methods to create transactions, write transactions (to the object/database), read transactions, etc.
|
||||
|
||||
.. autoclass:: bigchaindb.Bigchain
|
||||
:members:
|
||||
|
||||
.. automethod:: bigchaindb.core.Bigchain.__init__
|
@ -1,3 +0,0 @@
|
||||
# Contributing to the BigchainDB Project
|
||||
|
||||
If you're interested in contributing to the BigchainDB project, you can find out how in the [CONTRIBUTING.md file](https://github.com/BigchainDB/bigchaindb/blob/master/CONTRIBUTING.md) (in the root directory of the BigchainDB repository).
|
@ -1,10 +0,0 @@
|
||||
###################
|
||||
Developer Interface
|
||||
###################
|
||||
|
||||
The Bigchain class is the top-level API for BigchainDB. If you want to create and initialize a BigchainDB database, you create a Bigchain instance (object). Then you can use its various methods to create transactions, write transactions (to the object/database), read transactions, etc.
|
||||
|
||||
.. autoclass:: bigchaindb.Bigchain
|
||||
:members:
|
||||
|
||||
.. automethod:: bigchaindb.core.Bigchain.__init__
|
12
docs/source/drivers-clients/index.rst
Normal file
12
docs/source/drivers-clients/index.rst
Normal file
@ -0,0 +1,12 @@
|
||||
.. You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
BigchainDB Drivers & Clients
|
||||
============================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
http-client-server-api
|
||||
python-driver-api-examples
|
||||
|
@ -7,37 +7,12 @@ BigchainDB Documentation
|
||||
Table of Contents
|
||||
-----------------
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 5
|
||||
:numbered:
|
||||
:maxdepth: 1
|
||||
|
||||
introduction
|
||||
installing-server
|
||||
running-unit-tests
|
||||
configuration
|
||||
python-server-api-examples
|
||||
bigchaindb-cli
|
||||
http-client-server-api
|
||||
python-driver-api-examples
|
||||
local-rethinkdb-cluster
|
||||
deploy-on-aws
|
||||
json-serialization
|
||||
cryptography
|
||||
models
|
||||
developer-interface
|
||||
consensus
|
||||
monitoring
|
||||
licenses
|
||||
contributing
|
||||
topic-guides/index
|
||||
servers/index
|
||||
drivers-clients/index
|
||||
release-notes
|
||||
glossary
|
||||
|
||||
|
||||
Indices and Tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
appendices/index
|
||||
|
@ -1,10 +1,29 @@
|
||||
# Introduction
|
||||
|
||||
BigchainDB is a scalable blockchain database. You can read about its motivations, goals and high-level architecture in the [BigchainDB whitepaper](https://www.bigchaindb.com/whitepaper/). This document, the _BigchainDB Documentation_, is intended primarily for:
|
||||
BigchainDB is a scalable blockchain database. That is, it's a "big data" database with some blockchain characteristics, including decentralization, immutability and native support for assets.
|
||||
|
||||
You can read about the motivations, goals and high-level architecture in the [BigchainDB whitepaper](https://www.bigchaindb.com/whitepaper/).
|
||||
|
||||
|
||||
## Who this Documentation for?
|
||||
|
||||
The BigchainDB Documentation is intended primarily for:
|
||||
|
||||
1. Developers of BigchainDB server software.
|
||||
2. People deploying and managing BigchainDB clusters.
|
||||
3. Developers of BigchainDB driver software (SDKs used to develop client software).
|
||||
4. App developers who are developing client apps to talk to one or more live, operational BigchainDB clusters. They would use one of the BigchainDB drivers.
|
||||
|
||||
If you're curious about what's in our roadmap, see [the ROADMAP.md file](https://github.com/bigchaindb/org/blob/master/ROADMAP.md) and [the list of open issues](https://github.com/bigchaindb/bigchaindb/issues). If you want to request a feature, file a bug report, or make a pull request, see [the CONTRIBUTING.md file](https://github.com/bigchaindb/bigchaindb/blob/master/CONTRIBUTING.md).
|
||||
|
||||
## Is BigchainDB Production-Ready?
|
||||
|
||||
No, BigchainDB is not production-ready. You can use it to build a prototype or proof-of-concept (POC); many people are already doing that. Please don't use it for something mission-critical.
|
||||
|
||||
BigchainDB is currently in version 0.X. ([The Releases page on GitHub](https://github.com/bigchaindb/bigchaindb/releases) has the exact version number.) Once we believe that BigchainDB is production-ready, we'll release version 1.0.
|
||||
|
||||
[The BigchainDB Roadmap](https://github.com/bigchaindb/org/blob/master/ROADMAP.md) will give you a sense of the things we intend to do with BigchainDB in the near term and the long term.
|
||||
|
||||
|
||||
## Can I Help?
|
||||
|
||||
Yes! BigchainDB is an open-source project; we welcome contributions of all kinds. If you want to request a feature, file a bug report, make a pull request, or help in some other way, please see [the CONTRIBUTING.md file](https://github.com/bigchaindb/bigchaindb/blob/master/CONTRIBUTING.md).
|
||||
|
@ -1,203 +0,0 @@
|
||||
# The Transaction, Block and Vote Models
|
||||
|
||||
Transactions, blocks and votes are represented using JSON documents with the following models (schemas). See [the section on cryptography](cryptography.html) for more information about how we calculate hashes and signatures.
|
||||
|
||||
## The Transaction Model
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "<SHA3-256 hash hexdigest of transaction (below)>",
|
||||
"version": "<version number of the transaction model>",
|
||||
"transaction": {
|
||||
"fulfillments": ["<list of fulfillments>"],
|
||||
"conditions": ["<list of conditions>"],
|
||||
"operation": "<string>",
|
||||
"timestamp": "<timestamp from client>",
|
||||
"data": {
|
||||
"hash": "<SHA3-256 hash hexdigest of payload>",
|
||||
"payload": "<any JSON document>"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Transactions are the basic records stored by BigchainDB. There are two kinds:
|
||||
|
||||
1. A "CREATE" transaction creates a new asset. It has `"operation": "CREATE"`. The `payload` or a "CREATE" transaction describes, encodes, or links to the asset in some way.
|
||||
2. A "TRANSFER" transaction transfers one or more assets. It has `"operation": "TRANSFER"`. The `payload` of a "TRANSFER" transaction can be empty, but it can also be used for use-case-specific information (e.g. different kinds of transfers).
|
||||
|
||||
Here's some explanation of the contents of a transaction:
|
||||
|
||||
- `id`: The SHA3-256 hash hexdigest of everything inside the serialized `transaction` body (i.e. `fulfillments`, `conditions`, `operation`, `timestamp` and `data`; see below). The `id` is also the database primary key.
|
||||
- `version`: Version number of the transaction model, so that software can support different transaction models.
|
||||
- `transaction`:
|
||||
- `fulfillments`: List of fulfillments. Each _fulfillment_ contains a pointer to an unspent asset
|
||||
and a _crypto fulfillment_ that satisfies a spending condition set on the unspent asset. A _fulfillment_
|
||||
is usually a signature proving the ownership of the asset.
|
||||
See [Conditions and Fulfillments](#conditions-and-fulfillments) below.
|
||||
- `conditions`: List of conditions. Each _condition_ is a _crypto condition_ that needs to be fulfilled by the
|
||||
new owner in order to spend the asset.
|
||||
See [Conditions and Fulfillments](#conditions-and-fulfillments) below.
|
||||
- `operation`: String representation of the operation being performed (currently either "CREATE" or "TRANSFER"). It determines how
|
||||
the transaction should be validated.
|
||||
- `timestamp`: Time of creation of the transaction in UTC. It's provided by the client.
|
||||
- `data`:
|
||||
- `hash`: The SHA3-256 hash hexdigest of the serialized `payload`.
|
||||
- `payload`: Can be any JSON document. Its meaning depends on the whether the transaction
|
||||
is a "CREATE" or "TRANSFER" transaction; see above.
|
||||
|
||||
## Conditions and Fulfillments
|
||||
|
||||
### Conditions
|
||||
|
||||
#### One New Owner
|
||||
|
||||
If there is only one _new owner_, the condition will be a single-signature condition.
|
||||
|
||||
```json
|
||||
{
|
||||
"cid": "<condition index>",
|
||||
"condition": {
|
||||
"details": {
|
||||
"bitmask": "<base16 int>",
|
||||
"public_key": "<new owner public key>",
|
||||
"signature": null,
|
||||
"type": "fulfillment",
|
||||
"type_id": "<base16 int>"
|
||||
},
|
||||
"uri": "<string>"
|
||||
},
|
||||
"new_owners": ["<new owner public key>"]
|
||||
}
|
||||
```
|
||||
|
||||
- **Condition header**:
|
||||
- `cid`: Condition index so that we can reference this output as an input to another transaction. It also matches
|
||||
the input `fid`, making this the condition to fulfill in order to spend the asset used as input with `fid`.
|
||||
- `new_owners`: A list containing one item: the public key of the new owner.
|
||||
- **Condition body**:
|
||||
- `bitmask`: A set of bits representing the features required by the condition type.
|
||||
- `public_key`: The _new_owner's_ public key.
|
||||
- `type_id`: The fulfillment type ID; see the [ILP spec](https://interledger.org/five-bells-condition/spec.html).
|
||||
- `uri`: Binary representation of the condition using only URL-safe characters.
|
||||
|
||||
#### Multiple New Owners
|
||||
|
||||
If there are multiple _new owners_, we can create a ThresholdCondition requiring a signature from each new owner in order
|
||||
to spend the asset. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"cid": "<condition index>",
|
||||
"condition": {
|
||||
"details": {
|
||||
"bitmask": 41,
|
||||
"subfulfillments": [
|
||||
{
|
||||
"bitmask": 32,
|
||||
"public_key": "<new owner 1 public key>",
|
||||
"signature": null,
|
||||
"type": "fulfillment",
|
||||
"type_id": 4,
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"bitmask": 32,
|
||||
"public_key": "<new owner 2 public key>",
|
||||
"signature": null,
|
||||
"type": "fulfillment",
|
||||
"type_id": 4,
|
||||
"weight": 1
|
||||
}
|
||||
],
|
||||
"threshold": 2,
|
||||
"type": "fulfillment",
|
||||
"type_id": 2
|
||||
},
|
||||
"uri": "cc:2:29:ytNK3X6-bZsbF-nCGDTuopUIMi1HCyCkyPewm6oLI3o:206"},
|
||||
"new_owners": [
|
||||
"<new owner 1 public key>",
|
||||
"<new owner 2 public key>"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `subfulfillments`: a list of fulfillments
|
||||
- `weight`: integer weight for each subfulfillment's contribution to the threshold
|
||||
- `threshold`: threshold to reach for the subfulfillments to reach a valid fulfillment
|
||||
|
||||
The `weight`s and `threshold` could be adjusted. For example, if the `threshold` was changed to 1 above, then only one of the new owners would have to provide a signature to spend the asset.
|
||||
|
||||
### Fulfillments
|
||||
|
||||
#### One Current Owner
|
||||
|
||||
If there is only one _current owner_, the fulfillment will be a single-signature fulfillment.
|
||||
|
||||
```json
|
||||
{
|
||||
"current_owners": ["<public key of current owner>"],
|
||||
"fid": 0,
|
||||
"fulfillment": "cf:4:RxFzIE679tFBk8zwEgizhmTuciAylvTUwy6EL6ehddHFJOhK5F4IjwQ1xLu2oQK9iyRCZJdfWAefZVjTt3DeG5j2exqxpGliOPYseNkRAWEakqJ_UrCwgnj92dnFRAEE",
|
||||
"input": {
|
||||
"cid": 0,
|
||||
"txid": "11b3e7d893cc5fdfcf1a1706809c7def290a3b10b0bef6525d10b024649c42d3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `fid`: Fulfillment index. It matches a `cid` in the conditions with a new _crypto condition_ that the new owner
|
||||
needs to fulfill to spend this asset.
|
||||
- `current_owners`: A list of public keys of the current owners; in this case it has just one public key.
|
||||
- `fulfillment`: A cryptoconditions URI that encodes the cryptographic fulfillments like signatures and others, see crypto-conditions.
|
||||
- `input`: Pointer to the asset and condition of a previous transaction
|
||||
- `cid`: Condition index
|
||||
- `txid`: Transaction id
|
||||
|
||||
## The Block Model
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "<SHA3-256 hash hexdigest of the serialized block contents>",
|
||||
"block": {
|
||||
"timestamp": "<block-creation timestamp>",
|
||||
"transactions": ["<list of transactions>"],
|
||||
"node_pubkey": "<public key of the node creating the block>",
|
||||
"voters": ["<list of federation nodes public keys>"]
|
||||
},
|
||||
"signature": "<signature of the block>",
|
||||
"votes": ["<list of votes>"]
|
||||
}
|
||||
```
|
||||
|
||||
- `id`: SHA3-256 hash hexdigest of the contents of `block` (i.e. the timestamp, list of transactions, node_pubkey, and voters). This is also a database primary key; that's how we ensure that all blocks are unique.
|
||||
- `block`:
|
||||
- `timestamp`: Timestamp when the block was created. It's provided by the node that created the block.
|
||||
- `transactions`: A list of the transactions included in the block.
|
||||
- `node_pubkey`: The public key of the node that create the block.
|
||||
- `voters`: A list of public keys of federation nodes. Since the size of the
|
||||
federation may change over time, this will tell us how many nodes existed
|
||||
in the federation when the block was created, so that at a later point in
|
||||
time we can check that the block received the correct number of votes.
|
||||
- `signature`: Signature of the block by the node that created the block. (To create the signature, the node serializes the block contents and signs that with its private key.)
|
||||
- `votes`: Initially an empty list. New votes are appended as they come in from the nodes.
|
||||
|
||||
## The Vote Model
|
||||
|
||||
Each node must generate a vote for each block, to be appended to that block's `votes` list. A vote has the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"node_pubkey": "<the public key of the voting node>",
|
||||
"vote": {
|
||||
"voting_for_block": "<id of the block the node is voting for>",
|
||||
"previous_block": "<id of the block previous to this one>",
|
||||
"is_block_valid": "<true|false>",
|
||||
"invalid_reason": "<None|DOUBLE_SPEND|TRANSACTIONS_HASH_MISMATCH|NODES_PUBKEYS_MISMATCH",
|
||||
"timestamp": "<timestamp of the voting action>"
|
||||
},
|
||||
"signature": "<signature of vote block>"
|
||||
}
|
||||
```
|
||||
|
||||
Note: The `invalid_reason` was not being used as of v0.1.3 and may be dropped in a future version of BigchainDB.
|
@ -64,7 +64,7 @@ environment variables available are:
|
||||
- `BIGCHAINDB_STATSD_PORT` defines the port of the statsd server for monitoring.
|
||||
- `BIGCHAINDB_STATSD_RATE` is a float between `0` and `1` that defines the fraction of transaction operations sampled.
|
||||
- `BIGCHAINDB_API_ENDPOINT` defines the API endpoint to use (e.g. `http://localhost:9984/api/v1`).
|
||||
- `BIGCHAINDB_CONSENSUS_PLUGIN` defines the name of the [consensus plugin](consensus.html) to use.
|
||||
- `BIGCHAINDB_CONSENSUS_PLUGIN` defines the name of the [consensus plugin](../appendices/consensus.html) to use.
|
||||
- `BIGCHAINDB_SERVER_BIND` defines where to bind the server socket, the format is `addr:port` (e.g. `localhost:9984`).
|
||||
- `BIGCHAINDB_SERVER_WORKERS` defines the [number of workers](http://docs.gunicorn.org/en/stable/settings.html#workers)
|
||||
to start for the server API.
|
@ -8,7 +8,7 @@ This section explains a way to deploy a cluster of BigchainDB nodes on Amazon We
|
||||
|
||||
You might ask why one would want to deploy a centrally-controlled BigchainDB cluster. Isn't BigchainDB supposed to be decentralized, where each node is controlled by a different person or organization?
|
||||
|
||||
That's true, but there are some reasons why one might want a centrally-controlled cluster: 1) for testing, and 2) for initial deployment. Afterwards, the control of each node can be handed over to a different entity.
|
||||
That's true, but there are some reasons why one might want a centrally-controlled cluster: 1) for testing, and 2) for initial deployment, after which the control of each node can be handed over to a different entity.
|
||||
|
||||
## Python Setup
|
||||
|
16
docs/source/servers/index.rst
Normal file
16
docs/source/servers/index.rst
Normal file
@ -0,0 +1,16 @@
|
||||
.. You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
BigchainDB Servers (Nodes) & Clusters
|
||||
=====================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
installing-server
|
||||
running-unit-tests
|
||||
configuration
|
||||
bigchaindb-cli
|
||||
python-server-api-examples
|
||||
deploy-on-aws
|
||||
monitoring
|
@ -8,7 +8,7 @@ BigchainDB uses [StatsD](https://github.com/etsy/statsd) for monitoring. We requ
|
||||
|
||||
We put each of those inside its own Docker container. The whole system is illustrated below.
|
||||
|
||||

|
||||

|
||||
|
||||
For ease of use, we've created a Docker [_Compose file_](https://docs.docker.com/compose/compose-file/) (named `docker-compose-monitor.yml`) to define the monitoring system setup. To use it, just go to to the top `bigchaindb` directory and run:
|
||||
```text
|
@ -30,9 +30,7 @@ This instantiates an object `b` of class `Bigchain`. When instantiating a `Bigch
|
||||
|
||||
In a federation of BigchainDB nodes, each node has its own `Bigchain` instance.
|
||||
|
||||
The `Bigchain` class is the main API for all BigchainDB interactions, right now. It does things that BigchainDB nodes do, but it also does things that BigchainDB clients do. In the future, it will be broken apart into a node/server class and a client class.
|
||||
|
||||
The `Bigchain` class is documented in the [Developer Interface](developer-interface.html) section.
|
||||
The `Bigchain` class is the main API for all BigchainDB interactions, right now. It does things that BigchainDB nodes do, but it also does things that BigchainDB clients do. In the future, it will be refactored into different parts. The `Bigchain` class is documented [elsewhere (link)](../appendices/the-Bigchain-class.html).
|
||||
|
||||
## Create a Digital Asset
|
||||
|
||||
@ -130,7 +128,7 @@ The locking script is refered to as a `condition` and a corresponding `fulfillme
|
||||
Since a transaction can have multiple outputs with each its own (crypto)condition, each transaction input should also refer to the condition index `cid`.
|
||||
|
||||
<p align="center">
|
||||
<img width="70%" height="70%" src ="./_static/tx_single_condition_single_fulfillment_v1.png" />
|
||||
<img width="70%" height="70%" src ="../_static/tx_single_condition_single_fulfillment_v1.png" />
|
||||
</p>
|
||||
|
||||
|
||||
@ -382,7 +380,7 @@ With BigchainDB it is possible to send multiple assets to someone in a single tr
|
||||
The transaction will create a `fulfillment` - `condition` pair for each input, which can be refered to by `fid` and `cid` respectively.
|
||||
|
||||
<p align="center">
|
||||
<img width="70%" height="70%" src ="./_static/tx_multi_condition_multi_fulfillment_v1.png" />
|
||||
<img width="70%" height="70%" src ="../_static/tx_multi_condition_multi_fulfillment_v1.png" />
|
||||
</p>
|
||||
|
||||
```python
|
||||
@ -1017,7 +1015,7 @@ BigchainDB and cryptoconditions provides escrow out-of-the-box, without the need
|
||||
A threshold condition is used to represent the escrow, since BigchainDB transactions cannot have a _pending_ state.
|
||||
|
||||
<p align="center">
|
||||
<img width="70%" height="70%" src ="./_static/tx_escrow_execute_abort.png" />
|
||||
<img width="70%" height="70%" src ="../_static/tx_escrow_execute_abort.png" />
|
||||
</p>
|
||||
|
||||
The logic for switching between `execute` and `abort` conditions is conceptually simple:
|
||||
@ -1034,7 +1032,7 @@ else:
|
||||
The above switch can be implemented as follows using threshold cryptoconditions:
|
||||
|
||||
<p align="center">
|
||||
<img width="100%" height="100%" src ="./_static/cc_escrow_execute_abort.png" />
|
||||
<img width="100%" height="100%" src ="../_static/cc_escrow_execute_abort.png" />
|
||||
</p>
|
||||
|
||||
The inverted timeout is denoted by a -1 threshold, which negates the output of the fulfillment.
|
19
docs/source/topic-guides/decentralized.md
Normal file
19
docs/source/topic-guides/decentralized.md
Normal file
@ -0,0 +1,19 @@
|
||||
# How BigchainDB is Decentralized
|
||||
|
||||
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.
|
||||
|
||||
We use the phrase "BigchainDB federation" (or just "federation") to refer to the set of people and/or organizations who run the nodes of a BigchainDB cluster. A federation requires some form of governance to make decisions such as membership and policies. The exact details of the governance process are determined by each federation, but it can be very decentralized (e.g. purely vote-based, where each node gets a vote, and there are no special leadership roles).
|
||||
|
||||
The actual data is decentralized in that it doesn’t all get stored in one place. Each federation node stores the primary of one shard and replicas of some other shards. (A shard is a subset of the total set of documents.) Sharding and replication are handled by RethinkDB.
|
||||
|
||||
A federation 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 federation. All nodes run the same software and perform the same duties.
|
||||
|
||||
RethinkDB has an “admin” user which can’t be deleted and which can make big changes to the database, such as dropping a table. Right now, that’s a big security vulnerability, but we have plans to mitigate it by:
|
||||
1. Locking down the admin user as much as possible.
|
||||
2. Having all nodes inspect RethinkDB admin-type requests before acting on them. Requests can be checked against an evolving whitelist of allowed actions (voted on by federation nodes).
|
||||
|
||||
It’s worth noting that the RethinkDB admin user can’t transfer assets, even today. The only way to create a valid transfer transaction is to fulfill the current (crypto) conditions on the asset, and the admin user can’t do that because the admin user doesn’t have the necessary private keys (or preimages, in the case of hashlock conditions). They’re not stored in the database.
|
11
docs/source/topic-guides/diversity.md
Normal file
11
docs/source/topic-guides/diversity.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Kinds of Node Diversity
|
||||
|
||||
Steps should be taken to make it difficult for any one actor or event to control or damage “enough” of the nodes. (“Enough” is usually a quorum.) There are many kinds of diversity to consider, listed below. It may be quite difficult to have high diversity of all kinds.
|
||||
|
||||
1. **Jurisdictional diversity.** The nodes should be controlled by entities within multiple legal jurisdictions, so that it becomes difficult to use legal means to compel enough of them to do something.
|
||||
2. **Geographic diversity.** The servers should be physically located at multiple geographic locations, so that it becomes difficult for a natural disaster (such as a flood or earthquake) to damage enough of them to cause problems.
|
||||
3. **Hosting diversity.** The servers should be hosted by multiple hosting providers (e.g. Amazon Web Services, Microsoft Azure, Digital Ocean, Rackspace), so that it becomes difficult for one hosting provider to influence enough of the nodes.
|
||||
4. **Operating system diversity.** The servers should use a variety of operating systems, so that a security bug in one OS can’t be used to exploit enough of the nodes.
|
||||
5. **Diversity in general.** In general, membership diversity (of all kinds) confers many advantages on a federation. For example, it provides the federation with a source of various ideas for addressing challenges.
|
||||
|
||||
Note: If all the nodes are running the same code, i.e. the same implementation of BigchainDB, then a bug in that code could be used to compromise all of the nodes. Ideally, there would be several different, well-maintained implementations of BigchainDB Server (e.g. one in Python, one in Go, etc.), so that a federation could also have a diversity of server implementations.
|
17
docs/source/topic-guides/immutable.md
Normal file
17
docs/source/topic-guides/immutable.md
Normal file
@ -0,0 +1,17 @@
|
||||
# How BigchainDB is Immutable / Tamper-Resistant
|
||||
|
||||
The blockchain community often describes blockchains as “immutable.” If we interpret that word literally, it means that blockchain data is unchangeable or permanent, which is absurd. The data _can_ be changed. For example, a plague might drive humanity extinct; the data would then get corrupted over time due to water damage, thermal noise, and the general increase of entropy. In the case of Bitcoin, nothing so drastic is required: a 51% attack will suffice.
|
||||
|
||||
It’s true that blockchain data is more difficult to change than usual: it’s more tamper-resistant than a typical file system or database. Therefore, in the context of blockchains, we interpret the word “immutable” to mean tamper-resistant. (Linguists would say that the word “immutable” is a _term of art_ in the blockchain community.)
|
||||
|
||||
BigchainDB achieves strong tamper-resistance in the following ways:
|
||||
|
||||
1. **Replication.** All data is sharded and shards are replicated in several (different) places. The replication factor can be set by the federation. The higher the replication factor, the more difficult it becomes to change or delete all replicas.
|
||||
2. **Internal watchdogs.** All nodes monitor all changes and if some unallowed change happens, then appropriate action is taken. For example, if a valid block is deleted, then it is put back.
|
||||
3. **External watchdogs.** Federations may opt to have trusted third-parties to monitor and audit their data, looking for irregularities. For federations with publicly-readable data, the public can act as an auditor.
|
||||
4. **Cryptographic signatures** are used throughout BigchainDB as a way to check if messages (transactions, blocks and votes) have been tampered with enroute, and as a way to verify who signed the messages. Each block is signed by the node that created it. Each vote is signed by the node that cast it. A creation transaction is signed by the node that created it, although there are plans to improve that by adding signatures from the sending client and multiple nodes; see [Issue #347](https://github.com/bigchaindb/bigchaindb/issues/347). Transfer transactions can contain multiple fulfillments (one per asset transferred). Each fulfillment will typically contain one or more signatures from the owners (i.e. the owners before the transfer). Hashlock fulfillments are an exception; there’s an open issue ([#339](https://github.com/bigchaindb/bigchaindb/issues/339)) to address that.
|
||||
5. **Full or partial backups** of the database may be recorded from time to time, possibly on magnetic tape storage, other blockchains, printouts, etc.
|
||||
6. **Strong security.** Node owners can adopt and enforce strong security policies.
|
||||
7. **Node diversity.** Diversity makes it so that no one thing (e.g. natural disaster or operating system bug) can compromise enough of the nodes. See [the section on the kinds of node diversity](diversity.html).
|
||||
|
||||
Some of these things come "for free" as part of the BigchainDB software, and others require some extra effort from the federation and node owners.
|
15
docs/source/topic-guides/index.rst
Normal file
15
docs/source/topic-guides/index.rst
Normal file
@ -0,0 +1,15 @@
|
||||
.. You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Topic Guides
|
||||
============
|
||||
|
||||
Topic guides give background and explain concepts at a high level.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
decentralized
|
||||
diversity
|
||||
immutable
|
||||
models
|
247
docs/source/topic-guides/models.md
Normal file
247
docs/source/topic-guides/models.md
Normal file
@ -0,0 +1,247 @@
|
||||
# The Transaction, Block and Vote Models
|
||||
|
||||
BigchainDB stores all its records in JSON documents.
|
||||
|
||||
The three main kinds of records are transactions, blocks and votes.
|
||||
_Transactions_ are used to register, issue, create or transfer things (e.g. assets). Multiple transactions are combined with some other metadata to form _blocks_. Nodes append _votes_ to blocks. This section is a reference on the details of transactions, blocks and votes.
|
||||
|
||||
Below we often refer to cryptographic hashes, keys and signatures. The details of those are covered in [the section on cryptography](../appendices/cryptography.html).
|
||||
|
||||
|
||||
## Transaction Concepts
|
||||
|
||||
Transactions are the most basic kind of record stored by BigchainDB. There are two kinds: creation transactions and transfer transactions.
|
||||
|
||||
A _creation transaction_ can be used to register, issue, create or otherwise initiate the history of a single thing (or asset) in BigchainDB. For example, one might register an identity or a creative work. The things are often called "assets" but they might not be literal assets. A creation transaction also establishes the initial owner or owners of the asset. Only a federation node can create a valid creation transaction (but it's usually made based on a message from a client).
|
||||
|
||||
Currently, BigchainDB only supports indivisible assets. You can't split an asset apart into multiple assets, nor can you combine several assets together into one. [Issue #129](https://github.com/bigchaindb/bigchaindb/issues/129) is an enhancement proposal to support divisible assets.
|
||||
|
||||
A _transfer transaction_ can transfer one or more assets to new owners.
|
||||
|
||||
BigchainDB works with the [Interledger Protocol (ILP)](https://interledger.org/), a protocol for transferring assets between different ledgers, blockchains or payment systems.
|
||||
|
||||
The owner(s) of an asset can specifiy conditions (ILP crypto-conditions) which others must fulfill (satisfy) in order to become the new owner(s) of the asset. For example, a crypto-condition might require a signature from the owner, or from m-of-n owners (a threshold condition, e.g. 3-of-4).
|
||||
|
||||
When someone creates a transfer transaction with the goal of changing an asset's owners, they must fulfill the asset's current crypto-conditions (i.e. in a fulfillment), and they must provide new conditions (including the list of new owners).
|
||||
|
||||
Every create transaction contains exactly one fulfillment-condition pair. A transfer transaction can contain multiple fulfillment-condition pairs: one per asset transferred. Every fulfillment in a transfer transaction (input) must correspond to a condition (output) in a previous transaction. The diagram below illustrates some of these concepts: transactions are represented by light grey boxes, fulfillments have a label like `f:0`, and conditions have a label like `c:0`.
|
||||
|
||||

|
||||
|
||||
When a node is asked to check the validity of a transaction, it must do several things; the main things are:
|
||||
|
||||
* schema validation,
|
||||
* double-spending checks (for transfer transactions),
|
||||
* hash validation (i.e. is the calculated transaction hash equal to its id?), and
|
||||
* validation of all fulfillments, including validation of cryptographic signatures if they’re among the conditions.
|
||||
|
||||
The full details of transaction validation can be found in the code for `validate_transaction()` in the `BaseConsensusRules` class of [`consensus.py`](https://github.com/bigchaindb/bigchaindb/blob/master/bigchaindb/consensus.py) (unless other validation rules are being used by a federation, in which case those should be consulted instead).
|
||||
|
||||
|
||||
## Some Words of Caution
|
||||
|
||||
BigchainDB is still in the early stages of development. The data models described below may change substantially before BigchainDB reaches a production-ready state (i.e. version 1.0 and higher).
|
||||
|
||||
Also, note that timestamps come from clients and nodes. Unless you have some reason to believe that some timestamps are correct or meaningful, we advise you to ignore them (i.e. don't make any decisions based on them). (You might trust a timestamp, for example, if it came from a trusted timestamping service and it is embedded in the transaction data `payload` along with the signature from the timestamping service. You might trust node timestamps if you know all the nodes are running NTP servers.)
|
||||
|
||||
|
||||
## The Transaction Model
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "<hash of transaction, excluding signatures (see explanation)>",
|
||||
"version": "<version number of the transaction model>",
|
||||
"transaction": {
|
||||
"fulfillments": ["<list of fulfillments>"],
|
||||
"conditions": ["<list of conditions>"],
|
||||
"operation": "<string>",
|
||||
"timestamp": "<timestamp from client>",
|
||||
"data": {
|
||||
"hash": "<hash of payload>",
|
||||
"payload": "<any JSON document>"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's some explanation of the contents of a transaction:
|
||||
|
||||
- `id`: The hash of everything inside the serialized `transaction` body (i.e. `fulfillments`, `conditions`, `operation`, `timestamp` and `data`; see below), with one wrinkle: for each fulfillment in `fulfillments`, `fulfillment` is set to `null`. The `id` is also the database primary key.
|
||||
- `version`: Version number of the transaction model, so that software can support different transaction models.
|
||||
- `transaction`:
|
||||
- `fulfillments`: List of fulfillments. Each _fulfillment_ contains a pointer to an unspent asset
|
||||
and a _crypto fulfillment_ that satisfies a spending condition set on the unspent asset. A _fulfillment_
|
||||
is usually a signature proving the ownership of the asset.
|
||||
See [Conditions and Fulfillments](#conditions-and-fulfillments) below.
|
||||
- `conditions`: List of conditions. Each _condition_ is a _crypto-condition_ that needs to be fulfilled by a transfer transaction in order to transfer ownership to new owners.
|
||||
See [Conditions and Fulfillments](#conditions-and-fulfillments) below.
|
||||
- `operation`: String representation of the operation being performed (currently either "CREATE" or "TRANSFER"). It determines how the transaction should be validated.
|
||||
- `timestamp`: Time of creation of the transaction in UTC. It's provided by the client.
|
||||
- `data`:
|
||||
- `hash`: The hash of the serialized `payload`.
|
||||
- `payload`: Can be any JSON document. It may be empty in the case of a transfer transaction.
|
||||
|
||||
Later, when we get to the models for the block and the vote, we'll see that both include a signature (from the node which created it). You may wonder why transactions don't have signatures... The answer is that they do! They're just hidden inside the `fulfillment` string of each fulfillment. A creation transaction is signed by the node that created it. A transfer transaction is signed by whoever currently controls or owns it.
|
||||
|
||||
What gets signed? For each fulfillment in the transaction, the "fullfillment message" that gets signed includes the `operation`, `timestamp`, `data`, `version`, `id`, corresponding `condition`, and the fulfillment itself, except with its fulfillment string set to `null`. The computed signature goes into creating the `fulfillment` string of the fulfillment.
|
||||
|
||||
One other note: Currently, transactions contain only the public keys of asset-owners (i.e. who own an asset or who owned an asset in the past), inside the conditions and fulfillments. A transaction does _not_ contain the public key of the client (computer) which generated and sent it to a BigchainDB node. In fact, there's no need for a client to _have_ a public/private keypair. In the future, each client may also have a keypair, and it may have to sign each sent transaction (using its private key); see [Issue #347 on GitHub](https://github.com/bigchaindb/bigchaindb/issues/347). In practice, a person might think of their keypair as being both their "ownership-keypair" and their "client-keypair," but there is a difference, just like there's a difference between Joe and Joe's computer.
|
||||
|
||||
|
||||
## Conditions and Fulfillments
|
||||
|
||||
An aside: In what follows, the list of `new_owners` (in a condition) is always who owned the asset at the time the transaction completed, but before the next transaction started. The list of `current_owners` (in a fulfillment) is always equal to the list of `new_owners` in that asset's previous transaction.
|
||||
|
||||
### Conditions
|
||||
|
||||
#### One New Owner
|
||||
|
||||
If there is only one _new owner_, the condition will be a single-signature condition.
|
||||
|
||||
```json
|
||||
{
|
||||
"cid": "<condition index>",
|
||||
"condition": {
|
||||
"details": {
|
||||
"bitmask": "<base16 int>",
|
||||
"public_key": "<new owner public key>",
|
||||
"signature": null,
|
||||
"type": "fulfillment",
|
||||
"type_id": "<base16 int>"
|
||||
},
|
||||
"uri": "<string>"
|
||||
},
|
||||
"new_owners": ["<new owner public key>"]
|
||||
}
|
||||
```
|
||||
|
||||
- **Condition header**:
|
||||
- `cid`: Condition index so that we can reference this output as an input to another transaction. It also matches
|
||||
the input `fid`, making this the condition to fulfill in order to spend the asset used as input with `fid`.
|
||||
- `new_owners`: A list containing one item: the public key of the new owner.
|
||||
- **Condition body**:
|
||||
- `bitmask`: A set of bits representing the features required by the condition type.
|
||||
- `public_key`: The _new_owner's_ public key.
|
||||
- `type_id`: The fulfillment type ID; see the [ILP spec](https://interledger.org/five-bells-condition/spec.html).
|
||||
- `uri`: Binary representation of the condition using only URL-safe characters.
|
||||
|
||||
#### Multiple New Owners
|
||||
|
||||
If there are multiple _new owners_, they can create a ThresholdCondition requiring a signature from each of them in order
|
||||
to spend the asset. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"cid": "<condition index>",
|
||||
"condition": {
|
||||
"details": {
|
||||
"bitmask": 41,
|
||||
"subfulfillments": [
|
||||
{
|
||||
"bitmask": 32,
|
||||
"public_key": "<new owner 1 public key>",
|
||||
"signature": null,
|
||||
"type": "fulfillment",
|
||||
"type_id": 4,
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"bitmask": 32,
|
||||
"public_key": "<new owner 2 public key>",
|
||||
"signature": null,
|
||||
"type": "fulfillment",
|
||||
"type_id": 4,
|
||||
"weight": 1
|
||||
}
|
||||
],
|
||||
"threshold": 2,
|
||||
"type": "fulfillment",
|
||||
"type_id": 2
|
||||
},
|
||||
"uri": "cc:2:29:ytNK3X6-bZsbF-nCGDTuopUIMi1HCyCkyPewm6oLI3o:206"},
|
||||
"new_owners": [
|
||||
"<new owner 1 public key>",
|
||||
"<new owner 2 public key>"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `subfulfillments`: a list of fulfillments
|
||||
- `weight`: integer weight for each subfulfillment's contribution to the threshold
|
||||
- `threshold`: threshold to reach for the subfulfillments to reach a valid fulfillment
|
||||
|
||||
The `weight`s and `threshold` could be adjusted. For example, if the `threshold` was changed to 1 above, then only one of the new owners would have to provide a signature to spend the asset.
|
||||
|
||||
### Fulfillments
|
||||
|
||||
#### One Current Owner
|
||||
|
||||
If there is only one _current owner_, the fulfillment will be a single-signature fulfillment.
|
||||
|
||||
```json
|
||||
{
|
||||
"current_owners": ["<public key of current owner>"],
|
||||
"fid": 0,
|
||||
"fulfillment": "cf:4:RxFzIE679tFBk8zwEgizhmTuciAylvTUwy6EL6ehddHFJOhK5F4IjwQ1xLu2oQK9iyRCZJdfWAefZVjTt3DeG5j2exqxpGliOPYseNkRAWEakqJ_UrCwgnj92dnFRAEE",
|
||||
"input": {
|
||||
"cid": 0,
|
||||
"txid": "11b3e7d893cc5fdfcf1a1706809c7def290a3b10b0bef6525d10b024649c42d3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `fid`: Fulfillment index. It matches a `cid` in the conditions with a new _crypto-condition_ that the new owner
|
||||
needs to fulfill to spend this asset.
|
||||
- `current_owners`: A list of public keys of the current owners; in this case it has just one public key.
|
||||
- `fulfillment`: A crypto-conditions URI that encodes the cryptographic fulfillments like signatures and others, see [crypto-conditions](https://interledger.org/five-bells-condition/spec.html).
|
||||
- `input`: Pointer to the asset and condition of a previous transaction
|
||||
- `cid`: Condition index
|
||||
- `txid`: Transaction id
|
||||
|
||||
## The Block Model
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "<hash of block>",
|
||||
"block": {
|
||||
"timestamp": "<block-creation timestamp>",
|
||||
"transactions": ["<list of transactions>"],
|
||||
"node_pubkey": "<public key of the node creating the block>",
|
||||
"voters": ["<list of federation nodes public keys>"]
|
||||
},
|
||||
"signature": "<signature of block>",
|
||||
"votes": ["<list of votes>"]
|
||||
}
|
||||
```
|
||||
|
||||
- `id`: The hash of the serialized `block` (i.e. the `timestamp`, `transactions`, `node_pubkey`, and `voters`). This is also a database primary key; that's how we ensure that all blocks are unique.
|
||||
- `block`:
|
||||
- `timestamp`: Timestamp when the block was created. It's provided by the node that created the block.
|
||||
- `transactions`: A list of the transactions included in the block.
|
||||
- `node_pubkey`: The public key of the node that create the block.
|
||||
- `voters`: A list of public keys of federation nodes. Since the size of the
|
||||
federation may change over time, this will tell us how many nodes existed
|
||||
in the federation when the block was created, so that at a later point in
|
||||
time we can check that the block received the correct number of votes.
|
||||
- `signature`: Signature of the block by the node that created the block. (To create the signature, the node serializes the block contents and signs that with its private key.)
|
||||
- `votes`: Initially an empty list. New votes are appended as they come in from the nodes.
|
||||
|
||||
## The Vote Model
|
||||
|
||||
Each node must generate a vote for each block, to be appended to that block's `votes` list. A vote has the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"node_pubkey": "<the public key of the voting node>",
|
||||
"vote": {
|
||||
"voting_for_block": "<id of the block the node is voting for>",
|
||||
"previous_block": "<id of the block previous to this one>",
|
||||
"is_block_valid": "<true|false>",
|
||||
"invalid_reason": "<None|DOUBLE_SPEND|TRANSACTIONS_HASH_MISMATCH|NODES_PUBKEYS_MISMATCH",
|
||||
"timestamp": "<timestamp of the voting action>"
|
||||
},
|
||||
"signature": "<signature of vote>"
|
||||
}
|
||||
```
|
||||
|
||||
Note: The `invalid_reason` was not being used as of v0.1.3 and may be dropped in a future version of BigchainDB.
|
1
setup.py
1
setup.py
@ -29,7 +29,6 @@ check_setuptools_features()
|
||||
|
||||
|
||||
tests_require = [
|
||||
'pytest',
|
||||
'coverage',
|
||||
'pep8',
|
||||
'pyflakes',
|
||||
|
@ -13,7 +13,7 @@ from bigchaindb import util
|
||||
from bigchaindb import exceptions
|
||||
from bigchaindb import crypto
|
||||
from bigchaindb.voter import Voter
|
||||
from bigchaindb.block import Block
|
||||
from bigchaindb.block import Block, BlockDeleteRevert
|
||||
|
||||
|
||||
@pytest.mark.skipif(reason='Some tests throw a ResourceWarning that might result in some weird '
|
||||
@ -257,6 +257,21 @@ class TestBigchainApi(object):
|
||||
b.write_vote(block_3, b.vote(block_3, b.get_last_voted_block(), True), 3)
|
||||
assert b.get_last_voted_block()['id'] == block_3['id']
|
||||
|
||||
def test_no_vote_written_if_block_already_has_vote(self, b):
|
||||
b.create_genesis_block()
|
||||
|
||||
block_1 = b.create_block([])
|
||||
|
||||
b.write_block(block_1, durability='hard')
|
||||
|
||||
b.write_vote(block_1, b.vote(block_1, b.get_last_voted_block(), True), 1)
|
||||
retrieved_block_1 = r.table('bigchain').get(block_1['id']).run(b.conn)
|
||||
|
||||
# try to vote again on the retrieved block, should do nothing
|
||||
b.write_vote(retrieved_block_1, b.vote(retrieved_block_1, b.get_last_voted_block(), True), 1)
|
||||
retrieved_block_2 = r.table('bigchain').get(block_1['id']).run(b.conn)
|
||||
|
||||
assert retrieved_block_1 == retrieved_block_2
|
||||
|
||||
class TestTransactionValidation(object):
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
@ -787,6 +802,39 @@ class TestBigchainBlock(object):
|
||||
# join the process
|
||||
block.kill()
|
||||
|
||||
def test_revert_delete_block(self, b):
|
||||
b.create_genesis_block()
|
||||
|
||||
block_1 = b.create_block([])
|
||||
block_2 = b.create_block([])
|
||||
block_3 = b.create_block([])
|
||||
|
||||
b.write_block(block_1, durability='hard')
|
||||
b.write_block(block_2, durability='hard')
|
||||
b.write_block(block_3, durability='hard')
|
||||
|
||||
b.write_vote(block_1, b.vote(block_1, b.get_last_voted_block(), True), 1)
|
||||
b.write_vote(block_2, b.vote(block_2, b.get_last_voted_block(), True), 2)
|
||||
b.write_vote(block_3, b.vote(block_3, b.get_last_voted_block(), True), 3)
|
||||
|
||||
q_revert_delete = mp.Queue()
|
||||
|
||||
reverter = BlockDeleteRevert(q_revert_delete)
|
||||
|
||||
# simulate changefeed
|
||||
r.table('bigchain').get(block_2['id']).delete().run(b.conn)
|
||||
q_revert_delete.put(block_2)
|
||||
|
||||
assert r.table('bigchain').get(block_2['id']).run(b.conn) is None
|
||||
|
||||
reverter.start()
|
||||
time.sleep(1)
|
||||
reverter.kill()
|
||||
|
||||
reverted_block_2 = r.table('bigchain').get(block_2['id']).run(b.conn)
|
||||
|
||||
assert reverted_block_2 == block_2
|
||||
|
||||
def test_duplicated_transactions(self):
|
||||
pytest.skip('We may have duplicates in the initial_results and changefeed')
|
||||
|
||||
|
@ -292,6 +292,31 @@ class TestBigchainVoter(object):
|
||||
assert blocks[1]['votes'][0]['vote']['voting_for_block'] == block_1['id']
|
||||
assert blocks[2]['votes'][0]['vote']['voting_for_block'] == block_2['id']
|
||||
|
||||
def test_voter_checks_for_previous_vote(self, b):
|
||||
b.create_genesis_block()
|
||||
block_1 = b.create_block([])
|
||||
b.write_block(block_1, durability='hard')
|
||||
|
||||
q_new_block = mp.Queue()
|
||||
|
||||
voter = Voter(q_new_block)
|
||||
voter.start()
|
||||
|
||||
# queue block for voting
|
||||
q_new_block.put(block_1)
|
||||
time.sleep(1)
|
||||
retrieved_block = r.table('bigchain').get(block_1['id']).run(b.conn)
|
||||
|
||||
# queue block for voting AGAIN
|
||||
q_new_block.put(retrieved_block)
|
||||
time.sleep(1)
|
||||
voter.kill()
|
||||
|
||||
re_retrieved_block = r.table('bigchain').get(block_1['id']).run(b.conn)
|
||||
|
||||
# block should be unchanged
|
||||
assert retrieved_block == re_retrieved_block
|
||||
|
||||
@pytest.mark.skipif(reason='Updating the block_number must be atomic')
|
||||
def test_updating_block_number_must_be_atomic(self):
|
||||
pass
|
||||
|
Loading…
x
Reference in New Issue
Block a user