Skip to content

Commit

Permalink
Tendermint v0.22.8 backward compatibility (#2677)
Browse files Browse the repository at this point in the history
Problem statement:

BigchainDB v2.0.0b9 has been around for quite a while. Recently we have updated
Tendermint supported version to v0.31.5 which has incompatible blockchain.
Despite the fact that we have defined instructions on chain migration, no one
expected to migrate to incompatible chain within patch version range. So there
is a demand for Tendermint v0.22.8 compatibility among BigchainDB users.

Work has been done:

bigchaindb-abci package was upgraded to support multiple API versions.
New configuration field stating tendermint version was added.

Signed-off-by: David Dashyan <mail@davie.li>
  • Loading branch information
davie0 authored and eckelj committed Jan 23, 2020
1 parent 8647215 commit 7df5999
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 121 deletions.
1 change: 1 addition & 0 deletions bigchaindb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
'tendermint': {
'host': 'localhost',
'port': 26657,
'version': 'v0.31.5', # look for __tm_supported_versions__
},
# FIXME: hardcoding to localmongodb for now
'database': _database_map['localmongodb'],
Expand Down
32 changes: 12 additions & 20 deletions bigchaindb/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,7 @@
import sys

from abci.application import BaseApplication
from abci import (
ResponseInitChain,
ResponseInfo,
ResponseCheckTx,
ResponseBeginBlock,
ResponseDeliverTx,
ResponseEndBlock,
ResponseCommit,
)
from abci import CodeTypeOk

from bigchaindb import BigchainDB
from bigchaindb.elections.election import Election
Expand All @@ -30,7 +22,6 @@
from bigchaindb.events import EventTypes, Event


CodeTypeOk = 0
CodeTypeError = 1
logger = logging.getLogger(__name__)

Expand All @@ -42,7 +33,8 @@ class App(BaseApplication):
transaction logic to Tendermint Core.
"""

def __init__(self, bigchaindb=None, events_queue=None):
def __init__(self, abci, bigchaindb=None, events_queue=None,):
super().__init__(abci)
self.events_queue = events_queue
self.bigchaindb = bigchaindb or BigchainDB()
self.block_txn_ids = []
Expand Down Expand Up @@ -108,7 +100,7 @@ def init_chain(self, genesis):
genesis.chain_id, True)
self.chain = {'height': abci_chain_height, 'is_synced': True,
'chain_id': genesis.chain_id}
return ResponseInitChain()
return self.abci.ResponseInitChain()

def info(self, request):
"""Return height of the latest committed block."""
Expand All @@ -123,7 +115,7 @@ def info(self, request):

logger.info(f"Tendermint version: {request.version}")

r = ResponseInfo()
r = self.abci.ResponseInfo()
block = self.bigchaindb.get_latest_block()
if block:
chain_shift = 0 if self.chain is None else self.chain['height']
Expand All @@ -148,10 +140,10 @@ def check_tx(self, raw_transaction):
transaction = decode_transaction(raw_transaction)
if self.bigchaindb.is_valid_transaction(transaction):
logger.debug('check_tx: VALID')
return ResponseCheckTx(code=CodeTypeOk)
return self.abci.ResponseCheckTx(code=CodeTypeOk)
else:
logger.debug('check_tx: INVALID')
return ResponseCheckTx(code=CodeTypeError)
return self.abci.ResponseCheckTx(code=CodeTypeError)

def begin_block(self, req_begin_block):
"""Initialize list of transaction.
Expand All @@ -168,7 +160,7 @@ def begin_block(self, req_begin_block):

self.block_txn_ids = []
self.block_transactions = []
return ResponseBeginBlock()
return self.abci.ResponseBeginBlock()

def deliver_tx(self, raw_transaction):
"""Validate the transaction before mutating the state.
Expand All @@ -185,12 +177,12 @@ def deliver_tx(self, raw_transaction):

if not transaction:
logger.debug('deliver_tx: INVALID')
return ResponseDeliverTx(code=CodeTypeError)
return self.abci.ResponseDeliverTx(code=CodeTypeError)
else:
logger.debug('storing tx')
self.block_txn_ids.append(transaction.id)
self.block_transactions.append(transaction)
return ResponseDeliverTx(code=CodeTypeOk)
return self.abci.ResponseDeliverTx(code=CodeTypeOk)

def end_block(self, request_end_block):
"""Calculate block hash using transaction ids and previous block
Expand Down Expand Up @@ -226,7 +218,7 @@ def end_block(self, request_end_block):
self.new_height,
self.block_transactions)

return ResponseEndBlock(validator_updates=validator_update)
return self.abci.ResponseEndBlock(validator_updates=validator_update)

def commit(self):
"""Store the new height and along with block hash."""
Expand Down Expand Up @@ -257,7 +249,7 @@ def commit(self):
})
self.events_queue.put(event)

return ResponseCommit(data=data)
return self.abci.ResponseCommit(data=data)


def rollback(b):
Expand Down
16 changes: 6 additions & 10 deletions bigchaindb/parallel_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,23 @@
import multiprocessing as mp
from collections import defaultdict

from abci import ResponseCheckTx, ResponseDeliverTx

from bigchaindb import BigchainDB, App
from bigchaindb import App, BigchainDB
from bigchaindb.tendermint_utils import decode_transaction


CodeTypeOk = 0
from abci import CodeTypeOk


class ParallelValidationApp(App):
def __init__(self, bigchaindb=None, events_queue=None):
super().__init__(bigchaindb, events_queue)
def __init__(self, bigchaindb=None, events_queue=None, abci=None):
super().__init__(bigchaindb, events_queue, abci=abci)
self.parallel_validator = ParallelValidator()
self.parallel_validator.start()

def check_tx(self, raw_transaction):
return ResponseCheckTx(code=CodeTypeOk)
return self.abci.ResponseCheckTx(code=CodeTypeOk)

def deliver_tx(self, raw_transaction):
self.parallel_validator.validate(raw_transaction)
return ResponseDeliverTx(code=CodeTypeOk)
return self.abci.ResponseDeliverTx(code=CodeTypeOk)

def end_block(self, request_end_block):
result = self.parallel_validator.result(timeout=30)
Expand Down
19 changes: 16 additions & 3 deletions bigchaindb/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import logging
import setproctitle

from abci import TmVersion, ABCI

import bigchaindb
from bigchaindb.lib import BigchainDB
from bigchaindb.core import App
Expand Down Expand Up @@ -62,15 +64,26 @@ def start(args):
# We need to import this after spawning the web server
# because import ABCIServer will monkeypatch all sockets
# for gevent.
from abci import ABCIServer
from abci.server import ABCIServer

setproctitle.setproctitle('bigchaindb')

# Start the ABCIServer
abci = ABCI(TmVersion(bigchaindb.config['tendermint']['version']))
if args.experimental_parallel_validation:
app = ABCIServer(app=ParallelValidationApp(events_queue=exchange.get_publisher_queue()))
app = ABCIServer(
app=ParallelValidationApp(
abci=abci.types,
events_queue=exchange.get_publisher_queue(),
)
)
else:
app = ABCIServer(app=App(events_queue=exchange.get_publisher_queue()))
app = ABCIServer(
app=App(
abci=abci.types,
events_queue=exchange.get_publisher_queue(),
)
)
app.run()


Expand Down
24 changes: 17 additions & 7 deletions bigchaindb/upsert_validator/validator_utils.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import codecs
import base64
import binascii
import codecs

from abci import (ValidatorUpdate, PubKey)
from bigchaindb.common.exceptions import InvalidPublicKey
import bigchaindb
from abci import types_v0_22_8, types_v0_31_5, TmVersion
from bigchaindb.common.exceptions import InvalidPublicKey, BigchainDBError


def encode_validator(v):
ed25519_public_key = v['public_key']['value']
# NOTE: tendermint expects public to be encoded in go-amino format
pub_key = PubKey(type='ed25519',
data=bytes.fromhex(ed25519_public_key))
return ValidatorUpdate(pub_key=pub_key,
power=v['power'])
try:
version = TmVersion(bigchaindb.config["tendermint"]["version"])
except ValueError:
raise BigchainDBError('Invalid tendermint version, '
'check BigchainDB configuration file')

validator_update_t, pubkey_t = {
TmVersion.v0_22_8: (types_v0_22_8.Validator, types_v0_22_8.PubKey),
TmVersion.v0_31_5: (types_v0_31_5.ValidatorUpdate, types_v0_31_5.PubKey)
}[version]
pub_key = pubkey_t(type='ed25519', data=bytes.fromhex(ed25519_public_key))

return validator_update_t(pub_key=pub_key, power=v['power'])


def decode_validator(v):
Expand Down
2 changes: 1 addition & 1 deletion bigchaindb/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
__short_version__ = '2.1'

# Supported Tendermint versions
__tm_supported_versions__ = ["0.31.5"]
__tm_supported_versions__ = ["0.31.5", "0.22.8"]
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def check_setuptools_features():
'jsonschema~=2.5.1',
'pyyaml>=4.2b1',
'aiohttp~=3.0',
'bigchaindb-abci==0.7.1',
'bigchaindb-abci==1.0.1',
'setproctitle~=1.1.0',
'packaging~=18.0',
]
Expand Down
11 changes: 9 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ def merlin():
return generate_key_pair()


@pytest.fixture
def a():
from abci import types_v0_31_5
return types_v0_31_5


@pytest.fixture
def b():
from bigchaindb import BigchainDB
Expand Down Expand Up @@ -434,11 +440,12 @@ def event_loop():

@pytest.fixture(scope='session')
def abci_server():
from abci import ABCIServer
from abci.server import ABCIServer
from abci import types_v0_31_5
from bigchaindb.core import App
from bigchaindb.utils import Process

app = ABCIServer(app=App())
app = ABCIServer(app=App(types_v0_31_5))
abci_proxy = Process(name='ABCI', target=app.run)
yield abci_proxy.start()
abci_proxy.terminate()
Expand Down
2 changes: 1 addition & 1 deletion tests/tendermint/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import pytest
import codecs

import abci as types
from abci import types_v0_31_5 as types


@pytest.fixture
Expand Down

0 comments on commit 7df5999

Please sign in to comment.