mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Adjust ABCI handlers to shift migration height.
And abort if the current chain is not synced.
This commit is contained in:
parent
b5fe2b15ce
commit
cef559cdb0
@ -49,6 +49,19 @@ class App(BaseApplication):
|
|||||||
self.validators = None
|
self.validators = None
|
||||||
self.new_height = None
|
self.new_height = None
|
||||||
|
|
||||||
|
def log_abci_migration_error(self, chain_id, validators):
|
||||||
|
logger.error(f'An ABCI chain migration is in process. ' +
|
||||||
|
'Download the new ABCI client and configure it with ' +
|
||||||
|
'chain_id={chain_id} and validators={validators}.')
|
||||||
|
|
||||||
|
def abort_if_abci_chain_is_not_synced(self, chain):
|
||||||
|
if chain is None or chain['is_synced']:
|
||||||
|
return
|
||||||
|
|
||||||
|
validators = self.bigchaindb.get_validators()
|
||||||
|
self.log_abci_migration_error(chain['chain_id'], validators)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
def init_chain(self, genesis):
|
def init_chain(self, genesis):
|
||||||
"""Initialize chain upon genesis or a migration"""
|
"""Initialize chain upon genesis or a migration"""
|
||||||
|
|
||||||
@ -60,17 +73,14 @@ class App(BaseApplication):
|
|||||||
chain_id = known_chain['chain_id']
|
chain_id = known_chain['chain_id']
|
||||||
|
|
||||||
if known_chain['is_synced']:
|
if known_chain['is_synced']:
|
||||||
msg = f'Ignoring the InitChain ABCI request ({genesis}) - ' + \
|
msg = f'Got invalid InitChain ABCI request ({genesis}) - ' + \
|
||||||
'the chain {chain_id} is already synced.'
|
'the chain {chain_id} is already synced.'
|
||||||
|
|
||||||
logger.error(msg)
|
logger.error(msg)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if chain_id != genesis.chain_id:
|
if chain_id != genesis.chain_id:
|
||||||
msg = f'Got mismatching chain ID in the InitChain ' + \
|
validators = self.bigchaindb.get_validators()
|
||||||
'ABCI request - you need to migrate the ABCI client ' + \
|
self.log_abci_migration_error(chain_id, validators)
|
||||||
'and set new chain ID: {chain_id}.'
|
|
||||||
logger.error(msg)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# set migration values for app hash and height
|
# set migration values for app hash and height
|
||||||
@ -83,10 +93,8 @@ class App(BaseApplication):
|
|||||||
for v in genesis.validators]
|
for v in genesis.validators]
|
||||||
|
|
||||||
if known_validators and known_validators != validator_set:
|
if known_validators and known_validators != validator_set:
|
||||||
msg = f'Got mismatching validator set in the InitChain ' + \
|
self.log_abci_migration_error(known_chain['chain_id'],
|
||||||
'ABCI request - you need to migrate the ABCI client ' + \
|
known_validators)
|
||||||
'and set new validator set: {known_validators}.'
|
|
||||||
logger.error(msg)
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
block = Block(app_hash=app_hash, height=height, transactions=[])
|
block = Block(app_hash=app_hash, height=height, transactions=[])
|
||||||
@ -99,10 +107,15 @@ class App(BaseApplication):
|
|||||||
|
|
||||||
def info(self, request):
|
def info(self, request):
|
||||||
"""Return height of the latest committed block."""
|
"""Return height of the latest committed block."""
|
||||||
|
|
||||||
|
chain = self.bigchaindb.get_latest_abci_chain()
|
||||||
|
self.abort_if_abci_chain_is_not_synced(chain)
|
||||||
|
|
||||||
r = ResponseInfo()
|
r = ResponseInfo()
|
||||||
block = self.bigchaindb.get_latest_block()
|
block = self.bigchaindb.get_latest_block()
|
||||||
if block:
|
if block:
|
||||||
r.last_block_height = block['height']
|
chain_shift = 0 if chain is None else chain['height']
|
||||||
|
r.last_block_height = block['height'] - chain_shift
|
||||||
r.last_block_app_hash = block['app_hash'].encode('utf-8')
|
r.last_block_app_hash = block['app_hash'].encode('utf-8')
|
||||||
else:
|
else:
|
||||||
r.last_block_height = 0
|
r.last_block_height = 0
|
||||||
@ -117,6 +130,9 @@ class App(BaseApplication):
|
|||||||
raw_tx: a raw string (in bytes) transaction.
|
raw_tx: a raw string (in bytes) transaction.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
chain = self.bigchaindb.get_latest_abci_chain()
|
||||||
|
self.abort_if_abci_chain_is_not_synced(chain)
|
||||||
|
|
||||||
logger.benchmark('CHECK_TX_INIT')
|
logger.benchmark('CHECK_TX_INIT')
|
||||||
logger.debug('check_tx: %s', raw_transaction)
|
logger.debug('check_tx: %s', raw_transaction)
|
||||||
transaction = decode_transaction(raw_transaction)
|
transaction = decode_transaction(raw_transaction)
|
||||||
@ -135,8 +151,12 @@ class App(BaseApplication):
|
|||||||
req_begin_block: block object which contains block header
|
req_begin_block: block object which contains block header
|
||||||
and block hash.
|
and block hash.
|
||||||
"""
|
"""
|
||||||
|
chain = self.bigchaindb.get_latest_abci_chain()
|
||||||
|
self.abort_if_abci_chain_is_not_synced(chain)
|
||||||
|
|
||||||
|
chain_shift = 0 if chain is None else chain['height']
|
||||||
logger.benchmark('BEGIN BLOCK, height:%s, num_txs:%s',
|
logger.benchmark('BEGIN BLOCK, height:%s, num_txs:%s',
|
||||||
req_begin_block.header.height,
|
req_begin_block.header.height + chain_shift,
|
||||||
req_begin_block.header.num_txs)
|
req_begin_block.header.num_txs)
|
||||||
|
|
||||||
self.block_txn_ids = []
|
self.block_txn_ids = []
|
||||||
@ -149,6 +169,10 @@ class App(BaseApplication):
|
|||||||
Args:
|
Args:
|
||||||
raw_tx: a raw string (in bytes) transaction.
|
raw_tx: a raw string (in bytes) transaction.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
chain = self.bigchaindb.get_latest_abci_chain()
|
||||||
|
self.abort_if_abci_chain_is_not_synced(chain)
|
||||||
|
|
||||||
logger.debug('deliver_tx: %s', raw_transaction)
|
logger.debug('deliver_tx: %s', raw_transaction)
|
||||||
transaction = self.bigchaindb.is_valid_transaction(
|
transaction = self.bigchaindb.is_valid_transaction(
|
||||||
decode_transaction(raw_transaction), self.block_transactions)
|
decode_transaction(raw_transaction), self.block_transactions)
|
||||||
@ -170,7 +194,12 @@ class App(BaseApplication):
|
|||||||
height (int): new height of the chain.
|
height (int): new height of the chain.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
height = request_end_block.height
|
chain = self.bigchaindb.get_latest_abci_chain()
|
||||||
|
self.abort_if_abci_chain_is_not_synced(chain)
|
||||||
|
|
||||||
|
chain_shift = 0 if chain is None else chain['height']
|
||||||
|
|
||||||
|
height = request_end_block.height + chain_shift
|
||||||
self.new_height = height
|
self.new_height = height
|
||||||
block_txn_hash = calculate_hash(self.block_txn_ids)
|
block_txn_hash = calculate_hash(self.block_txn_ids)
|
||||||
block = self.bigchaindb.get_latest_block()
|
block = self.bigchaindb.get_latest_block()
|
||||||
@ -198,6 +227,9 @@ class App(BaseApplication):
|
|||||||
def commit(self):
|
def commit(self):
|
||||||
"""Store the new height and along with block hash."""
|
"""Store the new height and along with block hash."""
|
||||||
|
|
||||||
|
chain = self.bigchaindb.get_latest_abci_chain()
|
||||||
|
self.abort_if_abci_chain_is_not_synced(chain)
|
||||||
|
|
||||||
data = self.block_txn_hash.encode('utf-8')
|
data = self.block_txn_hash.encode('utf-8')
|
||||||
|
|
||||||
# register a new block only when new transactions are received
|
# register a new block only when new transactions are received
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from abci.types_pb2 import (
|
|||||||
PubKey,
|
PubKey,
|
||||||
ResponseInitChain,
|
ResponseInitChain,
|
||||||
RequestInitChain,
|
RequestInitChain,
|
||||||
|
RequestInfo,
|
||||||
RequestBeginBlock,
|
RequestBeginBlock,
|
||||||
RequestEndBlock,
|
RequestEndBlock,
|
||||||
Validator,
|
Validator,
|
||||||
@ -166,6 +167,39 @@ def test_init_chain_recognizes_new_chain_after_migration(b):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_info(b):
|
||||||
|
r = RequestInfo()
|
||||||
|
app = App(b)
|
||||||
|
|
||||||
|
res = app.info(r)
|
||||||
|
assert res.last_block_height == 0
|
||||||
|
assert res.last_block_app_hash == b''
|
||||||
|
|
||||||
|
b.store_block(Block(app_hash='1', height=1, transactions=[])._asdict())
|
||||||
|
res = app.info(r)
|
||||||
|
assert res.last_block_height == 1
|
||||||
|
assert res.last_block_app_hash == b'1'
|
||||||
|
|
||||||
|
# simulate a migration and assert the height is shifted
|
||||||
|
b.store_abci_chain(2, 'chain-XYZ')
|
||||||
|
b.store_block(Block(app_hash='2', height=2, transactions=[])._asdict())
|
||||||
|
res = app.info(r)
|
||||||
|
assert res.last_block_height == 0
|
||||||
|
assert res.last_block_app_hash == b'2'
|
||||||
|
|
||||||
|
b.store_block(Block(app_hash='3', height=3, transactions=[])._asdict())
|
||||||
|
res = app.info(r)
|
||||||
|
assert res.last_block_height == 1
|
||||||
|
assert res.last_block_app_hash == b'3'
|
||||||
|
|
||||||
|
# it's always the latest migration that is taken into account
|
||||||
|
b.store_abci_chain(4, 'chain-XYZ-new')
|
||||||
|
b.store_block(Block(app_hash='4', height=4, transactions=[])._asdict())
|
||||||
|
res = app.info(r)
|
||||||
|
assert res.last_block_height == 0
|
||||||
|
assert res.last_block_app_hash == b'4'
|
||||||
|
|
||||||
|
|
||||||
def test_check_tx__signed_create_is_ok(b):
|
def test_check_tx__signed_create_is_ok(b):
|
||||||
from bigchaindb import App
|
from bigchaindb import App
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
@ -199,7 +233,6 @@ def test_check_tx__unsigned_create_is_error(b):
|
|||||||
assert result.code == CodeTypeError
|
assert result.code == CodeTypeError
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.bdb
|
|
||||||
def test_deliver_tx__valid_create_updates_db(b, init_chain_request):
|
def test_deliver_tx__valid_create_updates_db(b, init_chain_request):
|
||||||
from bigchaindb import App
|
from bigchaindb import App
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
@ -367,6 +400,16 @@ def test_store_pre_commit_state_in_end_block(b, alice, init_chain_request):
|
|||||||
assert resp['height'] == 100
|
assert resp['height'] == 100
|
||||||
assert resp['transactions'] == [tx.id]
|
assert resp['transactions'] == [tx.id]
|
||||||
|
|
||||||
|
# simulate a chain migration and assert the height is shifted
|
||||||
|
b.store_abci_chain(100, 'new-chain')
|
||||||
|
app.begin_block(begin_block)
|
||||||
|
app.deliver_tx(encode_tx_to_bytes(tx))
|
||||||
|
app.end_block(RequestEndBlock(height=1))
|
||||||
|
resp = query.get_pre_commit_state(b.connection, PRE_COMMIT_ID)
|
||||||
|
assert resp['commit_id'] == PRE_COMMIT_ID
|
||||||
|
assert resp['height'] == 101
|
||||||
|
assert resp['transactions'] == [tx.id]
|
||||||
|
|
||||||
|
|
||||||
def test_new_validator_set(b):
|
def test_new_validator_set(b):
|
||||||
node1 = {'pub_key': {'type': 'ed25519',
|
node1 = {'pub_key': {'type': 'ed25519',
|
||||||
@ -389,3 +432,45 @@ def test_new_validator_set(b):
|
|||||||
'voting_power': u['power']})
|
'voting_power': u['power']})
|
||||||
|
|
||||||
assert updated_validator_set == updated_validators
|
assert updated_validator_set == updated_validators
|
||||||
|
|
||||||
|
|
||||||
|
def test_info_aborts_if_chain_is_not_synced(b):
|
||||||
|
b.store_abci_chain(0, 'chain-XYZ', False)
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
App(b).info(RequestInfo())
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_tx_aborts_if_chain_is_not_synced(b):
|
||||||
|
b.store_abci_chain(0, 'chain-XYZ', False)
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
App(b).check_tx('some bytes')
|
||||||
|
|
||||||
|
|
||||||
|
def test_begin_aborts_if_chain_is_not_synced(b):
|
||||||
|
b.store_abci_chain(0, 'chain-XYZ', False)
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
App(b).info(RequestBeginBlock())
|
||||||
|
|
||||||
|
|
||||||
|
def test_deliver_tx_aborts_if_chain_is_not_synced(b):
|
||||||
|
b.store_abci_chain(0, 'chain-XYZ', False)
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
App(b).deliver_tx('some bytes')
|
||||||
|
|
||||||
|
|
||||||
|
def test_end_block_aborts_if_chain_is_not_synced(b):
|
||||||
|
b.store_abci_chain(0, 'chain-XYZ', False)
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
App(b).info(RequestEndBlock())
|
||||||
|
|
||||||
|
|
||||||
|
def test_commit_aborts_if_chain_is_not_synced(b):
|
||||||
|
b.store_abci_chain(0, 'chain-XYZ', False)
|
||||||
|
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
App(b).commit()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user