Merge pull request #28 from ArpitShukla007/main

Migrated to Tendermint v0.34.11 with Py-ABCI
This commit is contained in:
Arpit Shukla 2022-02-04 15:39:25 +01:00 committed by GitHub
commit 263dfd49ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 685 additions and 264 deletions

View File

@ -1,7 +1,7 @@
FROM alpine:3.9 FROM alpine:3.9
LABEL maintainer "contact@ipdb.global" LABEL maintainer "contact@ipdb.global"
ARG TM_VERSION=v0.31.5 ARG TM_VERSION=v0.34.11
RUN mkdir -p /usr/src/app RUN mkdir -p /usr/src/app
ENV HOME /root ENV HOME /root
COPY . /usr/src/app/ COPY . /usr/src/app/

View File

@ -51,14 +51,14 @@ services:
command: '.ci/entrypoint.sh' command: '.ci/entrypoint.sh'
restart: always restart: always
tendermint: tendermint:
image: tendermint/tendermint:v0.31.5 image: tendermint/tendermint:v0.34.11
# volumes: # volumes:
# - ./tmdata:/tendermint # - ./tmdata:/tendermint
entrypoint: '' entrypoint: ''
ports: ports:
- "26656:26656" - "26656:26656"
- "26657:26657" - "26657:26657"
command: sh -c "tendermint init && tendermint node --consensus.create_empty_blocks=false --proxy_app=tcp://planetmint:26658" command: sh -c "tendermint init && tendermint node --consensus.create_empty_blocks=false --rpc.laddr=tcp://0.0.0.0:26657 --proxy_app=tcp://planetmint:26658"
restart: always restart: always
bdb: bdb:
image: busybox image: busybox

View File

@ -30,9 +30,9 @@ The version of Planetmint Server described in these docs only works well with Te
```bash ```bash
$ sudo apt install -y unzip $ sudo apt install -y unzip
$ wget https://github.com/tendermint/tendermint/releases/download/v0.31.5/tendermint_v0.31.5_linux_amd64.zip $ wget https://github.com/tendermint/tendermint/releases/download/v0.34.11/tendermint_v0.34.11_linux_amd64.zip
$ unzip tendermint_v0.31.5_linux_amd64.zip $ unzip tendermint_v0.34.11_linux_amd64.zip
$ rm tendermint_v0.31.5_linux_amd64.zip $ rm tendermint_v0.34.11_linux_amd64.zip
$ sudo mv tendermint /usr/local/bin $ sudo mv tendermint /usr/local/bin
``` ```

View File

@ -60,7 +60,7 @@ you can do this:
.. code:: .. code::
$ mkdir $(pwd)/tmdata $ mkdir $(pwd)/tmdata
$ docker run --rm -v $(pwd)/tmdata:/tendermint/config tendermint/tendermint:v0.31.5 init $ docker run --rm -v $(pwd)/tmdata:/tendermint/config tendermint/tendermint:v0.34.11 init
$ cat $(pwd)/tmdata/genesis.json $ cat $(pwd)/tmdata/genesis.json
You should see something that looks like: You should see something that looks like:

View File

@ -1,4 +1,4 @@
FROM tendermint/tendermint:v0.31.5 FROM tendermint/tendermint:v0.34.11
LABEL maintainer "contact@ipdb.global" LABEL maintainer "contact@ipdb.global"
WORKDIR / WORKDIR /
USER root USER root

View File

@ -17,7 +17,7 @@ stack_size=${STACK_SIZE:=4}
stack_type=${STACK_TYPE:="docker"} stack_type=${STACK_TYPE:="docker"}
stack_type_provider=${STACK_TYPE_PROVIDER:=""} stack_type_provider=${STACK_TYPE_PROVIDER:=""}
# NOTE versions prior v0.28.0 have different priv_validator format! # NOTE versions prior v0.28.0 have different priv_validator format!
tm_version=${TM_VERSION:="v0.31.5"} tm_version=${TM_VERSION:="v0.34.11"}
mongo_version=${MONGO_VERSION:="3.6"} mongo_version=${MONGO_VERSION:="3.6"}
stack_vm_memory=${STACK_VM_MEMORY:=2048} stack_vm_memory=${STACK_VM_MEMORY:=2048}
stack_vm_cpus=${STACK_VM_CPUS:=2} stack_vm_cpus=${STACK_VM_CPUS:=2}

View File

@ -71,7 +71,7 @@ config = {
'tendermint': { 'tendermint': {
'host': 'localhost', 'host': 'localhost',
'port': 26657, 'port': 26657,
'version': 'v0.31.5', # look for __tm_supported_versions__ 'version': 'v0.34.11', # look for __tm_supported_versions__
}, },
# FIXME: hardcoding to localmongodb for now # FIXME: hardcoding to localmongodb for now
'database': _database_map['localmongodb'], 'database': _database_map['localmongodb'],

View File

@ -8,14 +8,20 @@ with Tendermint.
""" """
import logging import logging
import sys import sys
from tendermint.abci import types_pb2
from abci.application import BaseApplication from abci.application import BaseApplication
from abci import CodeTypeOk from abci.application import OkCode
from tendermint.abci.types_pb2 import (
ResponseInfo,
ResponseInitChain,
ResponseCheckTx,
ResponseDeliverTx,
ResponseBeginBlock,
ResponseEndBlock,
ResponseCommit
)
from planetmint import Planetmint from planetmint import Planetmint
from planetmint.elections.election import Election from planetmint.elections.election import Election
from planetmint.version import __tm_supported_versions__
from planetmint.utils import tendermint_version_is_compatible
from planetmint.tendermint_utils import (decode_transaction, from planetmint.tendermint_utils import (decode_transaction,
calculate_hash) calculate_hash)
from planetmint.lib import Block from planetmint.lib import Block
@ -34,37 +40,36 @@ class App(BaseApplication):
transaction logic to Tendermint Core. transaction logic to Tendermint Core.
""" """
def __init__(self, abci, planetmint=None, events_queue=None,): def __init__(self, planetmint_node=None, events_queue=None):
super().__init__(abci) # super().__init__(abci)
logger.debug('Checking values of types')
logger.debug(dir(types_pb2))
self.events_queue = events_queue self.events_queue = events_queue
self.planetmint = planetmint or Planetmint() self.planetmint_node = planetmint_node or Planetmint()
self.block_txn_ids = [] self.block_txn_ids = []
self.block_txn_hash = '' self.block_txn_hash = ''
self.block_transactions = [] self.block_transactions = []
self.validators = None self.validators = None
self.new_height = None self.new_height = None
self.chain = self.planetmint.get_latest_abci_chain() self.chain = self.planetmint_node.get_latest_abci_chain()
def log_abci_migration_error(self, chain_id, validators): def log_abci_migration_error(self, chain_id, validators):
logger.error('An ABCI chain migration is in process. ' logger.error('An ABCI chain migration is in process. '
'Download the new ABCI client and configure it with ' 'Download theself.planetmint_node.get_latest_abci_chain new ABCI client and configure it with '
f'chain_id={chain_id} and validators={validators}.') f'chain_id={chain_id} and validators={validators}.')
def abort_if_abci_chain_is_not_synced(self): def abort_if_abci_chain_is_not_synced(self):
if self.chain is None or self.chain['is_synced']: if self.chain is None or self.chain['is_synced']:
return return
validators = self.planetmint_node.get_validators()
validators = self.planetmint.get_validators()
self.log_abci_migration_error(self.chain['chain_id'], validators) self.log_abci_migration_error(self.chain['chain_id'], validators)
sys.exit(1) 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"""
app_hash = '' app_hash = ''
height = 0 height = 0
known_chain = self.planetmint_node.get_latest_abci_chain()
known_chain = self.planetmint.get_latest_abci_chain()
if known_chain is not None: if known_chain is not None:
chain_id = known_chain['chain_id'] chain_id = known_chain['chain_id']
@ -73,35 +78,29 @@ class App(BaseApplication):
f'the chain {chain_id} is already synced.') f'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:
validators = self.planetmint.get_validators() validators = self.planetmint_node.get_validators()
self.log_abci_migration_error(chain_id, validators) self.log_abci_migration_error(chain_id, validators)
sys.exit(1) sys.exit(1)
# set migration values for app hash and height # set migration values for app hash and height
block = self.planetmint.get_latest_block() block = self.planetmint_node.get_latest_block()
app_hash = '' if block is None else block['app_hash'] app_hash = '' if block is None else block['app_hash']
height = 0 if block is None else block['height'] + 1 height = 0 if block is None else block['height'] + 1
known_validators = self.planetmint_node.get_validators()
known_validators = self.planetmint.get_validators()
validator_set = [vutils.decode_validator(v) validator_set = [vutils.decode_validator(v)
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:
self.log_abci_migration_error(known_chain['chain_id'], self.log_abci_migration_error(known_chain['chain_id'],
known_validators) known_validators)
sys.exit(1) sys.exit(1)
block = Block(app_hash=app_hash, height=height, transactions=[]) block = Block(app_hash=app_hash, height=height, transactions=[])
self.planetmint.store_block(block._asdict()) self.planetmint_node.store_block(block._asdict())
self.planetmint.store_validator_set(height + 1, validator_set) self.planetmint_node.store_validator_set(height + 1, validator_set)
abci_chain_height = 0 if known_chain is None else known_chain['height'] abci_chain_height = 0 if known_chain is None else known_chain['height']
self.planetmint.store_abci_chain(abci_chain_height, self.planetmint_node.store_abci_chain(abci_chain_height, genesis.chain_id, True)
genesis.chain_id, True)
self.chain = {'height': abci_chain_height, 'is_synced': True, self.chain = {'height': abci_chain_height, 'is_synced': True,
'chain_id': genesis.chain_id} 'chain_id': genesis.chain_id}
return self.abci.ResponseInitChain() return ResponseInitChain()
def info(self, request): def info(self, request):
"""Return height of the latest committed block.""" """Return height of the latest committed block."""
@ -109,15 +108,15 @@ class App(BaseApplication):
self.abort_if_abci_chain_is_not_synced() self.abort_if_abci_chain_is_not_synced()
# Check if Planetmint supports the Tendermint version # Check if Planetmint supports the Tendermint version
if not (hasattr(request, 'version') and tendermint_version_is_compatible(request.version)): # if not (hasattr(request, 'version') and tendermint_version_is_compatible(request.version)):
logger.error(f'Unsupported Tendermint version: {getattr(request, "version", "no version")}.' # logger.error(f'Unsupported Tendermint version: {getattr(request, "version", "no version")}.'
f' Currently, Planetmint only supports {__tm_supported_versions__}. Exiting!') # f' Currently, Planetmint only supports {__tm_supported_versions__}. Exiting!')
sys.exit(1) # sys.exit(1)
logger.info(f"Tendermint version: {request.version}") # logger.info(f"Tendermint version: {request.version}")
r = self.abci.ResponseInfo() r = ResponseInfo()
block = self.planetmint.get_latest_block() block = self.planetmint_node.get_latest_block()
if block: if block:
chain_shift = 0 if self.chain is None else self.chain['height'] chain_shift = 0 if self.chain is None else self.chain['height']
r.last_block_height = block['height'] - chain_shift r.last_block_height = block['height'] - chain_shift
@ -139,12 +138,12 @@ class App(BaseApplication):
logger.debug('check_tx: %s', raw_transaction) logger.debug('check_tx: %s', raw_transaction)
transaction = decode_transaction(raw_transaction) transaction = decode_transaction(raw_transaction)
if self.planetmint.is_valid_transaction(transaction): if self.planetmint_node.is_valid_transaction(transaction):
logger.debug('check_tx: VALID') logger.debug('check_tx: VALID')
return self.abci.ResponseCheckTx(code=CodeTypeOk) return ResponseCheckTx(code=OkCode)
else: else:
logger.debug('check_tx: INVALID') logger.debug('check_tx: INVALID')
return self.abci.ResponseCheckTx(code=CodeTypeError) return ResponseCheckTx(code=CodeTypeError)
def begin_block(self, req_begin_block): def begin_block(self, req_begin_block):
"""Initialize list of transaction. """Initialize list of transaction.
@ -155,13 +154,13 @@ class App(BaseApplication):
self.abort_if_abci_chain_is_not_synced() self.abort_if_abci_chain_is_not_synced()
chain_shift = 0 if self.chain is None else self.chain['height'] chain_shift = 0 if self.chain is None else self.chain['height']
logger.debug('BEGIN BLOCK, height:%s, num_txs:%s', # req_begin_block.header.num_txs not found, so removing it.
req_begin_block.header.height + chain_shift, logger.debug('BEGIN BLOCK, height:%s',
req_begin_block.header.num_txs) req_begin_block.header.height + chain_shift)
self.block_txn_ids = [] self.block_txn_ids = []
self.block_transactions = [] self.block_transactions = []
return self.abci.ResponseBeginBlock() return ResponseBeginBlock()
def deliver_tx(self, raw_transaction): def deliver_tx(self, raw_transaction):
"""Validate the transaction before mutating the state. """Validate the transaction before mutating the state.
@ -173,17 +172,17 @@ class App(BaseApplication):
self.abort_if_abci_chain_is_not_synced() self.abort_if_abci_chain_is_not_synced()
logger.debug('deliver_tx: %s', raw_transaction) logger.debug('deliver_tx: %s', raw_transaction)
transaction = self.planetmint.is_valid_transaction( transaction = self.planetmint_node.is_valid_transaction(
decode_transaction(raw_transaction), self.block_transactions) decode_transaction(raw_transaction), self.block_transactions)
if not transaction: if not transaction:
logger.debug('deliver_tx: INVALID') logger.debug('deliver_tx: INVALID')
return self.abci.ResponseDeliverTx(code=CodeTypeError) return ResponseDeliverTx(code=CodeTypeError)
else: else:
logger.debug('storing tx') logger.debug('storing tx')
self.block_txn_ids.append(transaction.id) self.block_txn_ids.append(transaction.id)
self.block_transactions.append(transaction) self.block_transactions.append(transaction)
return self.abci.ResponseDeliverTx(code=CodeTypeOk) return ResponseDeliverTx(code=OkCode)
def end_block(self, request_end_block): def end_block(self, request_end_block):
"""Calculate block hash using transaction ids and previous block """Calculate block hash using transaction ids and previous block
@ -205,21 +204,21 @@ class App(BaseApplication):
logger.debug(f'Updating pre-commit state: {self.new_height}') logger.debug(f'Updating pre-commit state: {self.new_height}')
pre_commit_state = dict(height=self.new_height, pre_commit_state = dict(height=self.new_height,
transactions=self.block_txn_ids) transactions=self.block_txn_ids)
self.planetmint.store_pre_commit_state(pre_commit_state) self.planetmint_node.store_pre_commit_state(pre_commit_state)
block_txn_hash = calculate_hash(self.block_txn_ids) block_txn_hash = calculate_hash(self.block_txn_ids)
block = self.planetmint.get_latest_block() block = self.planetmint_node.get_latest_block()
if self.block_txn_ids: if self.block_txn_ids:
self.block_txn_hash = calculate_hash([block['app_hash'], block_txn_hash]) self.block_txn_hash = calculate_hash([block['app_hash'], block_txn_hash])
else: else:
self.block_txn_hash = block['app_hash'] self.block_txn_hash = block['app_hash']
validator_update = Election.process_block(self.planetmint, validator_update = Election.process_block(self.planetmint_node,
self.new_height, self.new_height,
self.block_transactions) self.block_transactions)
return self.abci.ResponseEndBlock(validator_updates=validator_update) return ResponseEndBlock(validator_updates=validator_update)
def commit(self): def commit(self):
"""Store the new height and along with block hash.""" """Store the new height and along with block hash."""
@ -230,14 +229,14 @@ class App(BaseApplication):
# register a new block only when new transactions are received # register a new block only when new transactions are received
if self.block_txn_ids: if self.block_txn_ids:
self.planetmint.store_bulk_transactions(self.block_transactions) self.planetmint_node.store_bulk_transactions(self.block_transactions)
block = Block(app_hash=self.block_txn_hash, block = Block(app_hash=self.block_txn_hash,
height=self.new_height, height=self.new_height,
transactions=self.block_txn_ids) transactions=self.block_txn_ids)
# NOTE: storing the block should be the last operation during commit # NOTE: storing the block should be the last operation during commit
# this effects crash recovery. Refer BEP#8 for details # this effects crash recovery. Refer BEP#8 for details
self.planetmint.store_block(block._asdict()) self.planetmint_node.store_block(block._asdict())
logger.debug('Commit-ing new block with hash: apphash=%s ,' logger.debug('Commit-ing new block with hash: apphash=%s ,'
'height=%s, txn ids=%s', data, self.new_height, 'height=%s, txn ids=%s', data, self.new_height,
@ -250,7 +249,7 @@ class App(BaseApplication):
}) })
self.events_queue.put(event) self.events_queue.put(event)
return self.abci.ResponseCommit(data=data) return ResponseCommit(data=data)
def rollback(b): def rollback(b):

View File

@ -6,23 +6,28 @@
import multiprocessing as mp import multiprocessing as mp
from collections import defaultdict from collections import defaultdict
from planetmint import App, Planetmint from planetmint import App
from planetmint.lib import Planetmint
from planetmint.tendermint_utils import decode_transaction from planetmint.tendermint_utils import decode_transaction
from abci import CodeTypeOk from abci.application import OkCode
from tendermint.abci.types_pb2 import (
ResponseCheckTx,
ResponseDeliverTx,
)
class ParallelValidationApp(App): class ParallelValidationApp(App):
def __init__(self, planetmint=None, events_queue=None, abci=None): def __init__(self, planetmint=None, events_queue=None):
super().__init__(planetmint, events_queue, abci=abci) super().__init__(planetmint, events_queue)
self.parallel_validator = ParallelValidator() self.parallel_validator = ParallelValidator()
self.parallel_validator.start() self.parallel_validator.start()
def check_tx(self, raw_transaction): def check_tx(self, raw_transaction):
return self.abci.ResponseCheckTx(code=CodeTypeOk) return ResponseCheckTx(code=OkCode)
def deliver_tx(self, raw_transaction): def deliver_tx(self, raw_transaction):
self.parallel_validator.validate(raw_transaction) self.parallel_validator.validate(raw_transaction)
return self.abci.ResponseDeliverTx(code=CodeTypeOk) return ResponseDeliverTx(code=OkCode)
def end_block(self, request_end_block): def end_block(self, request_end_block):
result = self.parallel_validator.result(timeout=30) result = self.parallel_validator.result(timeout=30)

View File

@ -6,8 +6,6 @@
import logging import logging
import setproctitle import setproctitle
from abci import TmVersion, ABCI
import planetmint import planetmint
from planetmint.lib import Planetmint from planetmint.lib import Planetmint
from planetmint.core import App from planetmint.core import App
@ -68,18 +66,16 @@ def start(args):
setproctitle.setproctitle('planetmint') setproctitle.setproctitle('planetmint')
# Start the ABCIServer # Start the ABCIServer
abci = ABCI(TmVersion(planetmint.config['tendermint']['version'])) # abci = ABCI(TmVersion(planetmint.config['tendermint']['version']))
if args.experimental_parallel_validation: if args.experimental_parallel_validation:
app = ABCIServer( app = ABCIServer(
app=ParallelValidationApp( app=ParallelValidationApp(
abci=abci.types,
events_queue=exchange.get_publisher_queue(), events_queue=exchange.get_publisher_queue(),
) )
) )
else: else:
app = ABCIServer( app = ABCIServer(
app=App( app=App(
abci=abci.types,
events_queue=exchange.get_publisher_queue(), events_queue=exchange.get_publisher_queue(),
) )
) )

View File

@ -2,32 +2,20 @@ import base64
import binascii import binascii
import codecs import codecs
import planetmint from tendermint.abci import types_pb2
from abci import types_v0_22_8, types_v0_31_5, TmVersion from tendermint.crypto import keys_pb2
from planetmint.common.exceptions import InvalidPublicKey, BigchainDBError from planetmint.common.exceptions import InvalidPublicKey
def encode_validator(v): def encode_validator(v):
ed25519_public_key = v['public_key']['value'] ed25519_public_key = v['public_key']['value']
# NOTE: tendermint expects public to be encoded in go-amino format pub_key = keys_pb2.PublicKey(ed25519=bytes.fromhex(ed25519_public_key))
try:
version = TmVersion(planetmint.config["tendermint"]["version"])
except ValueError:
raise BigchainDBError('Invalid tendermint version, '
'check Planetmint configuration file')
validator_update_t, pubkey_t = { return types_pb2.ValidatorUpdate(pub_key=pub_key, power=v['power'])
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): def decode_validator(v):
return {'public_key': {'type': 'ed25519-base64', return {'public_key': {'type': 'ed25519-base64',
'value': codecs.encode(v.pub_key.data, 'base64').decode().rstrip('\n')}, 'value': codecs.encode(v.pub_key.ed25519, 'base64').decode().rstrip('\n')},
'voting_power': v.power} 'voting_power': v.power}

View File

@ -7,4 +7,4 @@ __version__ = '0.9.0'
__short_version__ = '0.9' __short_version__ = '0.9'
# Supported Tendermint versions # Supported Tendermint versions
__tm_supported_versions__ = ["0.31.5", "0.22.8"] __tm_supported_versions__ = ["0.34.11"]

View File

@ -75,7 +75,7 @@ tests_require = [
install_requires = [ install_requires = [
'chardet==3.0.4', 'chardet==3.0.4',
'aiohttp==3.7.4', 'aiohttp==3.7.4',
'bigchaindb-abci==1.0.7', 'abci==0.8.3',
'planetmint-cryptoconditions>=0.9.0', 'planetmint-cryptoconditions>=0.9.0',
'flask-cors==3.0.10', 'flask-cors==3.0.10',
'flask-restful==0.3.9', 'flask-restful==0.3.9',

View File

@ -14,6 +14,7 @@ import os
import copy import copy
import random import random
import tempfile import tempfile
import codecs
from collections import namedtuple from collections import namedtuple
from logging import getLogger from logging import getLogger
from logging.config import dictConfig from logging.config import dictConfig
@ -32,6 +33,9 @@ from planetmint.common.exceptions import DatabaseDoesNotExist
from planetmint.lib import Block from planetmint.lib import Block
from tests.utils import gen_vote from tests.utils import gen_vote
from tendermint.abci import types_pb2 as types
from tendermint.crypto import keys_pb2
TEST_DB_NAME = 'planetmint_test' TEST_DB_NAME = 'planetmint_test'
USER2_SK, USER2_PK = crypto.generate_key_pair() USER2_SK, USER2_PK = crypto.generate_key_pair()
@ -41,6 +45,15 @@ USER_PRIVATE_KEY = '8eJ8q9ZQpReWyQT5aFCiwtZ5wDZC4eDnCen88p3tQ6ie'
USER_PUBLIC_KEY = 'JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE' USER_PUBLIC_KEY = 'JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE'
@pytest.fixture
def init_chain_request():
pk = codecs.decode(b'VAgFZtYw8bNR5TMZHFOBDWk9cAmEu3/c6JgRBmddbbI=',
'base64')
val_a = types.ValidatorUpdate(power=10,
pub_key=keys_pb2.PublicKey(ed25519=pk))
return types.RequestInitChain(validators=[val_a])
def pytest_addoption(parser): def pytest_addoption(parser):
from planetmint.backend.connection import BACKENDS from planetmint.backend.connection import BACKENDS
@ -235,9 +248,10 @@ def merlin():
@pytest.fixture @pytest.fixture
def a(): # def a():
from abci import types_v0_31_5 def abci_fixture():
return types_v0_31_5 from tendermint.abci import types_pb2
return types_pb2
@pytest.fixture @pytest.fixture
@ -245,11 +259,14 @@ def b():
from planetmint import Planetmint from planetmint import Planetmint
return Planetmint() return Planetmint()
@pytest.fixture
def eventqueue_fixture():
from multiprocessing import Queue
return Queue()
@pytest.fixture @pytest.fixture
def b_mock(b, network_validators): def b_mock(b, network_validators):
b.get_validators = mock_get_validators(network_validators) b.get_validators = mock_get_validators(network_validators)
return b return b
@ -442,11 +459,11 @@ def event_loop():
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def abci_server(): def abci_server():
from abci.server import ABCIServer from abci.server import ABCIServer
from abci import types_v0_31_5 # from tendermint.abci import types_pb2 as types_v0_34_11
from planetmint.core import App from planetmint.core import App
from planetmint.utils import Process from planetmint.utils import Process
app = ABCIServer(app=App(types_v0_31_5)) app = ABCIServer(app=App())
abci_proxy = Process(name='ABCI', target=app.run) abci_proxy = Process(name='ABCI', target=app.run)
yield abci_proxy.start() yield abci_proxy.start()
abci_proxy.terminate() abci_proxy.terminate()

View File

@ -7,7 +7,6 @@ from planetmint.elections.election import Election
from planetmint.migrations.chain_migration_election import ChainMigrationElection from planetmint.migrations.chain_migration_election import ChainMigrationElection
from planetmint.upsert_validator.validator_election import ValidatorElection from planetmint.upsert_validator.validator_election import ValidatorElection
@pytest.mark.bdb @pytest.mark.bdb
def test_process_block_concludes_all_elections(b): def test_process_block_concludes_all_elections(b):
validators = generate_validators([1] * 4) validators = generate_validators([1] * 4)

View File

@ -6,8 +6,8 @@
import pytest import pytest
import codecs import codecs
from abci import types_v0_31_5 as types from tendermint.abci import types_pb2 as types
from tendermint.crypto import keys_pb2
@pytest.fixture @pytest.fixture
def validator_pub_key(): def validator_pub_key():
@ -19,5 +19,5 @@ def init_chain_request():
pk = codecs.decode(b'VAgFZtYw8bNR5TMZHFOBDWk9cAmEu3/c6JgRBmddbbI=', pk = codecs.decode(b'VAgFZtYw8bNR5TMZHFOBDWk9cAmEu3/c6JgRBmddbbI=',
'base64') 'base64')
val_a = types.ValidatorUpdate(power=10, val_a = types.ValidatorUpdate(power=10,
pub_key=types.PubKey(type='ed25519', data=pk)) pub_key=keys_pb2.PublicKey(ed25519=pk))
return types.RequestInitChain(validators=[val_a]) return types.RequestInitChain(validators=[val_a])

View File

@ -7,12 +7,13 @@ import json
import pytest import pytest
import random import random
from abci import types_v0_31_5 as types from tendermint.abci import types_pb2 as types
from tendermint.crypto import keys_pb2
from planetmint import App from planetmint import App
from planetmint.backend.localmongodb import query from planetmint.backend.localmongodb import query
from planetmint.common.crypto import generate_key_pair from planetmint.common.crypto import generate_key_pair
from planetmint.core import (CodeTypeOk, from planetmint.core import (OkCode,
CodeTypeError, CodeTypeError,
rollback) rollback)
from planetmint.elections.election import Election from planetmint.elections.election import Election
@ -40,7 +41,7 @@ def generate_address():
def generate_validator(): def generate_validator():
pk, _ = generate_key_pair() pk, _ = generate_key_pair()
pub_key = types.PubKey(type='ed25519', data=pk.encode()) pub_key = keys_pb2.PublicKey(ed25519=pk.encode())
val = types.ValidatorUpdate(power=10, pub_key=pub_key) val = types.ValidatorUpdate(power=10, pub_key=pub_key)
return val return val
@ -50,9 +51,9 @@ def generate_init_chain_request(chain_id, vals=None):
return types.RequestInitChain(validators=vals, chain_id=chain_id) return types.RequestInitChain(validators=vals, chain_id=chain_id)
def test_init_chain_successfully_registers_chain(a, b): def test_init_chain_successfully_registers_chain(b):
request = generate_init_chain_request('chain-XYZ') request = generate_init_chain_request('chain-XYZ')
res = App(a, b).init_chain(request) res = App(b).init_chain(request)
assert res == types.ResponseInitChain() assert res == types.ResponseInitChain()
chain = query.get_latest_abci_chain(b.connection) chain = query.get_latest_abci_chain(b.connection)
assert chain == {'height': 0, 'chain_id': 'chain-XYZ', 'is_synced': True} assert chain == {'height': 0, 'chain_id': 'chain-XYZ', 'is_synced': True}
@ -63,10 +64,10 @@ def test_init_chain_successfully_registers_chain(a, b):
} }
def test_init_chain_ignores_invalid_init_chain_requests(a, b): def test_init_chain_ignores_invalid_init_chain_requests(b):
validators = [generate_validator()] validators = [generate_validator()]
request = generate_init_chain_request('chain-XYZ', validators) request = generate_init_chain_request('chain-XYZ', validators)
res = App(a, b).init_chain(request) res = App(b).init_chain(request)
assert res == types.ResponseInitChain() assert res == types.ResponseInitChain()
validator_set = query.get_validator_set(b.connection) validator_set = query.get_validator_set(b.connection)
@ -80,7 +81,7 @@ def test_init_chain_ignores_invalid_init_chain_requests(a, b):
] ]
for r in invalid_requests: for r in invalid_requests:
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(a, b).init_chain(r) App(b).init_chain(r)
# assert nothing changed - neither validator set, nor chain ID # assert nothing changed - neither validator set, nor chain ID
new_validator_set = query.get_validator_set(b.connection) new_validator_set = query.get_validator_set(b.connection)
assert new_validator_set == validator_set assert new_validator_set == validator_set
@ -93,10 +94,10 @@ def test_init_chain_ignores_invalid_init_chain_requests(a, b):
} }
def test_init_chain_recognizes_new_chain_after_migration(a, b): def test_init_chain_recognizes_new_chain_after_migration(b):
validators = [generate_validator()] validators = [generate_validator()]
request = generate_init_chain_request('chain-XYZ', validators) request = generate_init_chain_request('chain-XYZ', validators)
res = App(a, b).init_chain(request) res = App(b).init_chain(request)
assert res == types.ResponseInitChain() assert res == types.ResponseInitChain()
validator_set = query.get_validator_set(b.connection)['validators'] validator_set = query.get_validator_set(b.connection)['validators']
@ -115,7 +116,7 @@ def test_init_chain_recognizes_new_chain_after_migration(a, b):
] ]
for r in invalid_requests: for r in invalid_requests:
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(a, b).init_chain(r) App(b).init_chain(r)
assert query.get_latest_abci_chain(b.connection) == { assert query.get_latest_abci_chain(b.connection) == {
'chain_id': 'chain-XYZ-migrated-at-height-1', 'chain_id': 'chain-XYZ-migrated-at-height-1',
'is_synced': False, 'is_synced': False,
@ -128,7 +129,7 @@ def test_init_chain_recognizes_new_chain_after_migration(a, b):
# completes the migration # completes the migration
request = generate_init_chain_request('chain-XYZ-migrated-at-height-1', request = generate_init_chain_request('chain-XYZ-migrated-at-height-1',
validators) validators)
res = App(a, b).init_chain(request) res = App(b).init_chain(request)
assert res == types.ResponseInitChain() assert res == types.ResponseInitChain()
assert query.get_latest_abci_chain(b.connection) == { assert query.get_latest_abci_chain(b.connection) == {
'chain_id': 'chain-XYZ-migrated-at-height-1', 'chain_id': 'chain-XYZ-migrated-at-height-1',
@ -149,7 +150,7 @@ def test_init_chain_recognizes_new_chain_after_migration(a, b):
] ]
for r in invalid_requests: for r in invalid_requests:
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(a, b).init_chain(r) App(b).init_chain(r)
assert query.get_latest_abci_chain(b.connection) == { assert query.get_latest_abci_chain(b.connection) == {
'chain_id': 'chain-XYZ-migrated-at-height-1', 'chain_id': 'chain-XYZ-migrated-at-height-1',
'is_synced': True, 'is_synced': True,
@ -164,9 +165,9 @@ def test_init_chain_recognizes_new_chain_after_migration(a, b):
} }
def test_info(a, b): def test_info(b):
r = types.RequestInfo(version=__tm_supported_versions__[0]) r = types.RequestInfo(version=__tm_supported_versions__[0])
app = App(a, b) app = App(b)
res = app.info(r) res = app.info(r)
assert res.last_block_height == 0 assert res.last_block_height == 0
@ -179,7 +180,7 @@ def test_info(a, b):
# simulate a migration and assert the height is shifted # simulate a migration and assert the height is shifted
b.store_abci_chain(2, 'chain-XYZ') b.store_abci_chain(2, 'chain-XYZ')
app = App(a, b) app = App(b)
b.store_block(Block(app_hash='2', height=2, transactions=[])._asdict()) b.store_block(Block(app_hash='2', height=2, transactions=[])._asdict())
res = app.info(r) res = app.info(r)
assert res.last_block_height == 0 assert res.last_block_height == 0
@ -192,14 +193,14 @@ def test_info(a, b):
# it's always the latest migration that is taken into account # it's always the latest migration that is taken into account
b.store_abci_chain(4, 'chain-XYZ-new') b.store_abci_chain(4, 'chain-XYZ-new')
app = App(a, b) app = App(b)
b.store_block(Block(app_hash='4', height=4, transactions=[])._asdict()) b.store_block(Block(app_hash='4', height=4, transactions=[])._asdict())
res = app.info(r) res = app.info(r)
assert res.last_block_height == 0 assert res.last_block_height == 0
assert res.last_block_app_hash == b'4' assert res.last_block_app_hash == b'4'
def test_check_tx__signed_create_is_ok(a, b): def test_check_tx__signed_create_is_ok(b):
from planetmint import App from planetmint import App
from planetmint.models import Transaction from planetmint.models import Transaction
from planetmint.common.crypto import generate_key_pair from planetmint.common.crypto import generate_key_pair
@ -211,12 +212,12 @@ def test_check_tx__signed_create_is_ok(a, b):
[([bob.public_key], 1)])\ [([bob.public_key], 1)])\
.sign([alice.private_key]) .sign([alice.private_key])
app = App(a, b) app = App(b)
result = app.check_tx(encode_tx_to_bytes(tx)) result = app.check_tx(encode_tx_to_bytes(tx))
assert result.code == CodeTypeOk assert result.code == OkCode
def test_check_tx__unsigned_create_is_error(a, b): def test_check_tx__unsigned_create_is_error(b):
from planetmint import App from planetmint import App
from planetmint.models import Transaction from planetmint.models import Transaction
from planetmint.common.crypto import generate_key_pair from planetmint.common.crypto import generate_key_pair
@ -227,12 +228,12 @@ def test_check_tx__unsigned_create_is_error(a, b):
tx = Transaction.create([alice.public_key], tx = Transaction.create([alice.public_key],
[([bob.public_key], 1)]) [([bob.public_key], 1)])
app = App(a, b) app = App(b)
result = app.check_tx(encode_tx_to_bytes(tx)) result = app.check_tx(encode_tx_to_bytes(tx))
assert result.code == CodeTypeError assert result.code == CodeTypeError
def test_deliver_tx__valid_create_updates_db_and_emits_event(a, b, init_chain_request): def test_deliver_tx__valid_create_updates_db_and_emits_event(b, init_chain_request):
import multiprocessing as mp import multiprocessing as mp
from planetmint import App from planetmint import App
from planetmint.models import Transaction from planetmint.models import Transaction
@ -246,7 +247,7 @@ def test_deliver_tx__valid_create_updates_db_and_emits_event(a, b, init_chain_re
[([bob.public_key], 1)])\ [([bob.public_key], 1)])\
.sign([alice.private_key]) .sign([alice.private_key])
app = App(a, b, events) app = App(b, events)
app.init_chain(init_chain_request) app.init_chain(init_chain_request)
@ -254,7 +255,7 @@ def test_deliver_tx__valid_create_updates_db_and_emits_event(a, b, init_chain_re
app.begin_block(begin_block) app.begin_block(begin_block)
result = app.deliver_tx(encode_tx_to_bytes(tx)) result = app.deliver_tx(encode_tx_to_bytes(tx))
assert result.code == CodeTypeOk assert result.code == OkCode
app.end_block(types.RequestEndBlock(height=99)) app.end_block(types.RequestEndBlock(height=99))
app.commit() app.commit()
@ -270,7 +271,7 @@ def test_deliver_tx__valid_create_updates_db_and_emits_event(a, b, init_chain_re
# next(unspent_outputs) # next(unspent_outputs)
def test_deliver_tx__double_spend_fails(a, b, init_chain_request): def test_deliver_tx__double_spend_fails(b, init_chain_request):
from planetmint import App from planetmint import App
from planetmint.models import Transaction from planetmint.models import Transaction
from planetmint.common.crypto import generate_key_pair from planetmint.common.crypto import generate_key_pair
@ -282,14 +283,14 @@ def test_deliver_tx__double_spend_fails(a, b, init_chain_request):
[([bob.public_key], 1)])\ [([bob.public_key], 1)])\
.sign([alice.private_key]) .sign([alice.private_key])
app = App(a, b) app = App(b)
app.init_chain(init_chain_request) app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock() begin_block = types.RequestBeginBlock()
app.begin_block(begin_block) app.begin_block(begin_block)
result = app.deliver_tx(encode_tx_to_bytes(tx)) result = app.deliver_tx(encode_tx_to_bytes(tx))
assert result.code == CodeTypeOk assert result.code == OkCode
app.end_block(types.RequestEndBlock(height=99)) app.end_block(types.RequestEndBlock(height=99))
app.commit() app.commit()
@ -299,12 +300,12 @@ def test_deliver_tx__double_spend_fails(a, b, init_chain_request):
assert result.code == CodeTypeError assert result.code == CodeTypeError
def test_deliver_transfer_tx__double_spend_fails(a, b, init_chain_request): def test_deliver_transfer_tx__double_spend_fails(b, init_chain_request):
from planetmint import App from planetmint import App
from planetmint.models import Transaction from planetmint.models import Transaction
from planetmint.common.crypto import generate_key_pair from planetmint.common.crypto import generate_key_pair
app = App(a, b) app = App(b)
app.init_chain(init_chain_request) app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock() begin_block = types.RequestBeginBlock()
@ -324,7 +325,7 @@ def test_deliver_transfer_tx__double_spend_fails(a, b, init_chain_request):
.sign([alice.private_key]) .sign([alice.private_key])
result = app.deliver_tx(encode_tx_to_bytes(tx)) result = app.deliver_tx(encode_tx_to_bytes(tx))
assert result.code == CodeTypeOk assert result.code == OkCode
tx_transfer = Transaction.transfer(tx.to_inputs(), tx_transfer = Transaction.transfer(tx.to_inputs(),
[([bob.public_key], 1)], [([bob.public_key], 1)],
@ -332,7 +333,7 @@ def test_deliver_transfer_tx__double_spend_fails(a, b, init_chain_request):
.sign([alice.private_key]) .sign([alice.private_key])
result = app.deliver_tx(encode_tx_to_bytes(tx_transfer)) result = app.deliver_tx(encode_tx_to_bytes(tx_transfer))
assert result.code == CodeTypeOk assert result.code == OkCode
double_spend = Transaction.transfer(tx.to_inputs(), double_spend = Transaction.transfer(tx.to_inputs(),
[([carly.public_key], 1)], [([carly.public_key], 1)],
@ -343,8 +344,8 @@ def test_deliver_transfer_tx__double_spend_fails(a, b, init_chain_request):
assert result.code == CodeTypeError assert result.code == CodeTypeError
def test_end_block_return_validator_updates(a, b, init_chain_request): def test_end_block_return_validator_updates(b, init_chain_request):
app = App(a, b) app = App(b)
app.init_chain(init_chain_request) app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock() begin_block = types.RequestBeginBlock()
@ -375,10 +376,10 @@ def test_end_block_return_validator_updates(a, b, init_chain_request):
resp = app.end_block(types.RequestEndBlock(height=2)) resp = app.end_block(types.RequestEndBlock(height=2))
assert resp.validator_updates[0].power == new_validator['election']['power'] assert resp.validator_updates[0].power == new_validator['election']['power']
expected = bytes.fromhex(new_validator['election']['public_key']['value']) expected = bytes.fromhex(new_validator['election']['public_key']['value'])
assert expected == resp.validator_updates[0].pub_key.data assert expected == resp.validator_updates[0].pub_key.ed25519
def test_store_pre_commit_state_in_end_block(a, b, alice, init_chain_request): def test_store_pre_commit_state_in_end_block(b, alice, init_chain_request):
from planetmint import App from planetmint import App
from planetmint.backend import query from planetmint.backend import query
from planetmint.models import Transaction from planetmint.models import Transaction
@ -388,7 +389,7 @@ def test_store_pre_commit_state_in_end_block(a, b, alice, init_chain_request):
asset={'msg': 'live long and prosper'})\ asset={'msg': 'live long and prosper'})\
.sign([alice.private_key]) .sign([alice.private_key])
app = App(a, b) app = App(b)
app.init_chain(init_chain_request) app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock() begin_block = types.RequestBeginBlock()
@ -409,7 +410,7 @@ def test_store_pre_commit_state_in_end_block(a, b, alice, init_chain_request):
# simulate a chain migration and assert the height is shifted # simulate a chain migration and assert the height is shifted
b.store_abci_chain(100, 'new-chain') b.store_abci_chain(100, 'new-chain')
app = App(a, b) app = App(b)
app.begin_block(begin_block) app.begin_block(begin_block)
app.deliver_tx(encode_tx_to_bytes(tx)) app.deliver_tx(encode_tx_to_bytes(tx))
app.end_block(types.RequestEndBlock(height=1)) app.end_block(types.RequestEndBlock(height=1))
@ -502,43 +503,43 @@ def test_new_validator_set(b):
assert updated_validator_set == updated_validators assert updated_validator_set == updated_validators
def test_info_aborts_if_chain_is_not_synced(a, b): def test_info_aborts_if_chain_is_not_synced(b):
b.store_abci_chain(0, 'chain-XYZ', False) b.store_abci_chain(0, 'chain-XYZ', False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(a, b).info(types.RequestInfo()) App(b).info(types.RequestInfo())
def test_check_tx_aborts_if_chain_is_not_synced(a, b): def test_check_tx_aborts_if_chain_is_not_synced(b):
b.store_abci_chain(0, 'chain-XYZ', False) b.store_abci_chain(0, 'chain-XYZ', False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(a, b).check_tx('some bytes') App(b).check_tx('some bytes')
def test_begin_aborts_if_chain_is_not_synced(a, b): def test_begin_aborts_if_chain_is_not_synced(b):
b.store_abci_chain(0, 'chain-XYZ', False) b.store_abci_chain(0, 'chain-XYZ', False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(a, b).info(types.RequestBeginBlock()) App(b).info(types.RequestBeginBlock())
def test_deliver_tx_aborts_if_chain_is_not_synced(a, b): def test_deliver_tx_aborts_if_chain_is_not_synced(b):
b.store_abci_chain(0, 'chain-XYZ', False) b.store_abci_chain(0, 'chain-XYZ', False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(a, b).deliver_tx('some bytes') App(b).deliver_tx('some bytes')
def test_end_block_aborts_if_chain_is_not_synced(a, b): def test_end_block_aborts_if_chain_is_not_synced(b):
b.store_abci_chain(0, 'chain-XYZ', False) b.store_abci_chain(0, 'chain-XYZ', False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(a, b).info(types.RequestEndBlock()) App(b).info(types.RequestEndBlock())
def test_commit_aborts_if_chain_is_not_synced(a, b): def test_commit_aborts_if_chain_is_not_synced(b):
b.store_abci_chain(0, 'chain-XYZ', False) b.store_abci_chain(0, 'chain-XYZ', False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(a, b).commit() App(b).commit()

View File

@ -5,13 +5,13 @@
import codecs import codecs
from abci import types_v0_31_5 as types from tendermint.abci import types_pb2 as types
import json import json
import pytest import pytest
from abci.server import ProtocolHandler from abci.server import ProtocolHandler
from abci.encoding import read_messages from abci.utils import read_messages
from planetmint.common.transaction_mode_types import BROADCAST_TX_COMMIT, BROADCAST_TX_SYNC from planetmint.common.transaction_mode_types import BROADCAST_TX_COMMIT, BROADCAST_TX_SYNC
from planetmint.version import __tm_supported_versions__ from planetmint.version import __tm_supported_versions__
@ -19,13 +19,13 @@ from io import BytesIO
@pytest.mark.bdb @pytest.mark.bdb
def test_app(a, b, init_chain_request): def test_app(b, eventqueue_fixture, init_chain_request):
from planetmint import App from planetmint import App
from planetmint.tendermint_utils import calculate_hash from planetmint.tendermint_utils import calculate_hash
from planetmint.common.crypto import generate_key_pair from planetmint.common.crypto import generate_key_pair
from planetmint.models import Transaction from planetmint.models import Transaction
app = App(a, b) app = App(b, eventqueue_fixture)
p = ProtocolHandler(app) p = ProtocolHandler(app)
data = p.process('info', data = p.process('info',
@ -42,7 +42,7 @@ def test_app(a, b, init_chain_request):
assert block0['height'] == 0 assert block0['height'] == 0
assert block0['app_hash'] == '' assert block0['app_hash'] == ''
pk = codecs.encode(init_chain_request.validators[0].pub_key.data, 'base64').decode().strip('\n') pk = codecs.encode(init_chain_request.validators[0].pub_key.ed25519, 'base64').decode().strip('\n')
[validator] = b.get_validators(height=1) [validator] = b.get_validators(height=1)
assert validator['public_key']['value'] == pk assert validator['public_key']['value'] == pk
assert validator['voting_power'] == 10 assert validator['voting_power'] == 10
@ -144,14 +144,3 @@ def test_post_transaction_responses(tendermint_ws_url, b):
code, message = b.write_transaction(double_spend, mode) code, message = b.write_transaction(double_spend, mode)
assert code == 500 assert code == 500
assert message == 'Transaction validation failed' assert message == 'Transaction validation failed'
@pytest.mark.bdb
def test_exit_when_tm_ver_not_supported(a, b):
from planetmint import App
app = App(a, b)
p = ProtocolHandler(app)
with pytest.raises(SystemExit):
p.process('info', types.Request(info=types.RequestInfo(version='2')))

View File

@ -221,7 +221,7 @@ def test_autoconfigure_read_both_from_file_and_env(monkeypatch, request):
'tendermint': { 'tendermint': {
'host': 'localhost', 'host': 'localhost',
'port': 26657, 'port': 26657,
'version': 'v0.31.5' 'version': 'v0.34.11'
}, },
'log': { 'log': {
'file': LOG_FILE, 'file': LOG_FILE,

View File

@ -1,118 +1,545 @@
# Copyright © 2020 Interplanetary Database Association e.V., # Copyright <EFBFBD> 2020 Interplanetary Database Association e.V.,
# Planetmint and IPDB software contributors. # Planetmint and IPDB software contributors.
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) # SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0 # Code is Apache-2.0 and docs are CC-BY-4.0
import json
import pytest import pytest
import random
from tendermint.abci import types_pb2 as types
from tendermint.crypto import keys_pb2
from planetmint import App
from planetmint.backend.localmongodb import query
from planetmint.common.crypto import generate_key_pair
from planetmint.core import (OkCode,
CodeTypeError,
rollback)
from planetmint.elections.election import Election
from planetmint.lib import Block
from planetmint.migrations.chain_migration_election import ChainMigrationElection
from planetmint.upsert_validator.validator_election import ValidatorElection
from planetmint.upsert_validator.validator_utils import new_validator_set
from planetmint.tendermint_utils import public_key_to_base64
from planetmint.version import __tm_supported_versions__
from tests.utils import generate_election, generate_validators
@pytest.fixture pytestmark = pytest.mark.bdb
def config(request, monkeypatch):
backend = request.config.getoption('--database-backend')
if backend == 'mongodb-ssl':
backend = 'mongodb'
config = {
'database': { def encode_tx_to_bytes(transaction):
'backend': backend, return json.dumps(transaction.to_dict()).encode('utf8')
'host': 'host',
'port': 28015,
'name': 'bigchain', def generate_address():
'replicaset': 'bigchain-rs', return ''.join(random.choices('1,2,3,4,5,6,7,8,9,A,B,C,D,E,F'.split(','),
'connection_timeout': 5000, k=40)).encode()
'max_tries': 3
},
'tendermint': { def generate_validator():
'host': 'localhost', pk, _ = generate_key_pair()
'port': 26657, pub_key = keys_pb2.PublicKey(ed25519=pk.encode())
}, val = types.ValidatorUpdate(power=10, pub_key=pub_key)
'CONFIGURED': True, return val
def generate_init_chain_request(chain_id, vals=None):
vals = vals if vals is not None else [generate_validator()]
return types.RequestInitChain(validators=vals, chain_id=chain_id)
def test_init_chain_successfully_registers_chain(b):
request = generate_init_chain_request('chain-XYZ')
res = App(b).init_chain(request)
assert res == types.ResponseInitChain()
chain = query.get_latest_abci_chain(b.connection)
assert chain == {'height': 0, 'chain_id': 'chain-XYZ', 'is_synced': True}
assert query.get_latest_block(b.connection) == {
'height': 0,
'app_hash': '',
'transactions': [],
} }
monkeypatch.setattr('planetmint.config', config)
return config def test_init_chain_ignores_invalid_init_chain_requests(b):
validators = [generate_validator()]
request = generate_init_chain_request('chain-XYZ', validators)
res = App(b).init_chain(request)
assert res == types.ResponseInitChain()
validator_set = query.get_validator_set(b.connection)
def test_bigchain_class_default_initialization(config): invalid_requests = [
from planetmint import Planetmint request, # the same request again
from planetmint.validation import BaseValidationRules # different validator set
from planetmint.backend.connection import Connection generate_init_chain_request('chain-XYZ'),
planet = Planetmint() # different chain ID
assert isinstance(planet.connection, Connection) generate_init_chain_request('chain-ABC', validators),
assert planet.connection.host == config['database']['host'] ]
assert planet.connection.port == config['database']['port'] for r in invalid_requests:
assert planet.connection.dbname == config['database']['name'] with pytest.raises(SystemExit):
assert planet.validation == BaseValidationRules App(b).init_chain(r)
# assert nothing changed - neither validator set, nor chain ID
new_validator_set = query.get_validator_set(b.connection)
def test_bigchain_class_initialization_with_parameters(): assert new_validator_set == validator_set
from planetmint import Planetmint new_chain_id = query.get_latest_abci_chain(b.connection)['chain_id']
from planetmint.backend import connect assert new_chain_id == 'chain-XYZ'
from planetmint.validation import BaseValidationRules assert query.get_latest_block(b.connection) == {
init_db_kwargs = { 'height': 0,
'backend': 'localmongodb', 'app_hash': '',
'host': 'this_is_the_db_host', 'transactions': [],
'port': 12345,
'name': 'this_is_the_db_name',
} }
connection = connect(**init_db_kwargs)
planet = Planetmint(connection=connection)
assert planet.connection == connection
assert planet.connection.host == init_db_kwargs['host']
assert planet.connection.port == init_db_kwargs['port']
assert planet.connection.dbname == init_db_kwargs['name']
assert planet.validation == BaseValidationRules
@pytest.mark.bdb def test_init_chain_recognizes_new_chain_after_migration(b):
def test_get_spent_issue_1271(b, alice, bob, carol): validators = [generate_validator()]
request = generate_init_chain_request('chain-XYZ', validators)
res = App(b).init_chain(request)
assert res == types.ResponseInitChain()
validator_set = query.get_validator_set(b.connection)['validators']
# simulate a migration
query.store_block(b.connection, Block(app_hash='', height=1,
transactions=[])._asdict())
b.migrate_abci_chain()
# the same or other mismatching requests are ignored
invalid_requests = [
request,
generate_init_chain_request('unknown', validators),
generate_init_chain_request('chain-XYZ'),
generate_init_chain_request('chain-XYZ-migrated-at-height-1'),
]
for r in invalid_requests:
with pytest.raises(SystemExit):
App(b).init_chain(r)
assert query.get_latest_abci_chain(b.connection) == {
'chain_id': 'chain-XYZ-migrated-at-height-1',
'is_synced': False,
'height': 2,
}
new_validator_set = query.get_validator_set(b.connection)['validators']
assert new_validator_set == validator_set
# a request with the matching chain ID and matching validator set
# completes the migration
request = generate_init_chain_request('chain-XYZ-migrated-at-height-1',
validators)
res = App(b).init_chain(request)
assert res == types.ResponseInitChain()
assert query.get_latest_abci_chain(b.connection) == {
'chain_id': 'chain-XYZ-migrated-at-height-1',
'is_synced': True,
'height': 2,
}
assert query.get_latest_block(b.connection) == {
'height': 2,
'app_hash': '',
'transactions': [],
}
# requests with old chain ID and other requests are ignored
invalid_requests = [
request,
generate_init_chain_request('chain-XYZ', validators),
generate_init_chain_request('chain-XYZ-migrated-at-height-1'),
]
for r in invalid_requests:
with pytest.raises(SystemExit):
App(b).init_chain(r)
assert query.get_latest_abci_chain(b.connection) == {
'chain_id': 'chain-XYZ-migrated-at-height-1',
'is_synced': True,
'height': 2,
}
new_validator_set = query.get_validator_set(b.connection)['validators']
assert new_validator_set == validator_set
assert query.get_latest_block(b.connection) == {
'height': 2,
'app_hash': '',
'transactions': [],
}
def test_info(b):
r = types.RequestInfo(version=__tm_supported_versions__[0])
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')
app = App(b)
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')
app = App(b)
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):
from planetmint import App
from planetmint.models import Transaction
from planetmint.common.crypto import generate_key_pair
alice = generate_key_pair()
bob = generate_key_pair()
tx = Transaction.create([alice.public_key],
[([bob.public_key], 1)])\
.sign([alice.private_key])
app = App(b)
result = app.check_tx(encode_tx_to_bytes(tx))
assert result.code == OkCode
def test_check_tx__unsigned_create_is_error(b):
from planetmint import App
from planetmint.models import Transaction
from planetmint.common.crypto import generate_key_pair
alice = generate_key_pair()
bob = generate_key_pair()
tx = Transaction.create([alice.public_key],
[([bob.public_key], 1)])
app = App(b)
result = app.check_tx(encode_tx_to_bytes(tx))
assert result.code == CodeTypeError
def test_deliver_tx__valid_create_updates_db_and_emits_event(b, init_chain_request):
import multiprocessing as mp
from planetmint import App
from planetmint.models import Transaction
from planetmint.common.crypto import generate_key_pair
alice = generate_key_pair()
bob = generate_key_pair()
events = mp.Queue()
tx = Transaction.create([alice.public_key],
[([bob.public_key], 1)])\
.sign([alice.private_key])
app = App(b, events)
app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock()
app.begin_block(begin_block)
result = app.deliver_tx(encode_tx_to_bytes(tx))
assert result.code == OkCode
app.end_block(types.RequestEndBlock(height=99))
app.commit()
assert b.get_transaction(tx.id).id == tx.id
block_event = events.get()
assert block_event.data['transactions'] == [tx]
# unspent_outputs = b.get_unspent_outputs()
# unspent_output = next(unspent_outputs)
# expected_unspent_output = next(tx.unspent_outputs)._asdict()
# assert unspent_output == expected_unspent_output
# with pytest.raises(StopIteration):
# next(unspent_outputs)
def test_deliver_tx__double_spend_fails(b, eventqueue_fixture, init_chain_request):
from planetmint import App
from planetmint.models import Transaction
from planetmint.common.crypto import generate_key_pair
alice = generate_key_pair()
bob = generate_key_pair()
tx = Transaction.create([alice.public_key],
[([bob.public_key], 1)])\
.sign([alice.private_key])
app = App(b, eventqueue_fixture)
app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock()
app.begin_block(begin_block)
result = app.deliver_tx(encode_tx_to_bytes(tx))
assert result.code == OkCode
app.end_block(types.RequestEndBlock(height=99))
app.commit()
assert b.get_transaction(tx.id).id == tx.id
result = app.deliver_tx(encode_tx_to_bytes(tx))
assert result.code == CodeTypeError
def test_deliver_transfer_tx__double_spend_fails(b, init_chain_request):
from planetmint import App
from planetmint.models import Transaction
from planetmint.common.crypto import generate_key_pair
app = App(b)
app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock()
app.begin_block(begin_block)
alice = generate_key_pair()
bob = generate_key_pair()
carly = generate_key_pair()
asset = {
'msg': 'live long and prosper'
}
tx = Transaction.create([alice.public_key],
[([alice.public_key], 1)],
asset=asset)\
.sign([alice.private_key])
result = app.deliver_tx(encode_tx_to_bytes(tx))
assert result.code == OkCode
tx_transfer = Transaction.transfer(tx.to_inputs(),
[([bob.public_key], 1)],
asset_id=tx.id)\
.sign([alice.private_key])
result = app.deliver_tx(encode_tx_to_bytes(tx_transfer))
assert result.code == OkCode
double_spend = Transaction.transfer(tx.to_inputs(),
[([carly.public_key], 1)],
asset_id=tx.id)\
.sign([alice.private_key])
result = app.deliver_tx(encode_tx_to_bytes(double_spend))
assert result.code == CodeTypeError
def test_end_block_return_validator_updates(b, init_chain_request):
app = App(b)
app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock()
app.begin_block(begin_block)
# generate a block containing a concluded validator election
validators = generate_validators([1] * 4)
b.store_validator_set(1, [v['storage'] for v in validators])
new_validator = generate_validators([1])[0]
public_key = validators[0]['public_key']
private_key = validators[0]['private_key']
voter_keys = [v['private_key'] for v in validators]
election, votes = generate_election(b,
ValidatorElection,
public_key, private_key,
new_validator['election'],
voter_keys)
b.store_block(Block(height=1, transactions=[election.id],
app_hash='')._asdict())
b.store_bulk_transactions([election])
Election.process_block(b, 1, [election])
app.block_transactions = votes
resp = app.end_block(types.RequestEndBlock(height=2))
assert resp.validator_updates[0].power == new_validator['election']['power']
expected = bytes.fromhex(new_validator['election']['public_key']['value'])
assert expected == resp.validator_updates[0].pub_key.ed25519
def test_store_pre_commit_state_in_end_block(b, alice, init_chain_request):
from planetmint import App
from planetmint.backend import query
from planetmint.models import Transaction from planetmint.models import Transaction
tx_1 = Transaction.create( tx = Transaction.create([alice.public_key],
[carol.public_key], [([alice.public_key], 1)],
[([carol.public_key], 8)], asset={'msg': 'live long and prosper'})\
).sign([carol.private_key]) .sign([alice.private_key])
assert tx_1.validate(b)
b.store_bulk_transactions([tx_1])
tx_2 = Transaction.transfer( app = App(b)
tx_1.to_inputs(), app.init_chain(init_chain_request)
[([bob.public_key], 2),
([alice.public_key], 2),
([carol.public_key], 4)],
asset_id=tx_1.id,
).sign([carol.private_key])
assert tx_2.validate(b)
b.store_bulk_transactions([tx_2])
tx_3 = Transaction.transfer( begin_block = types.RequestBeginBlock()
tx_2.to_inputs()[2:3], app.begin_block(begin_block)
[([alice.public_key], 1), app.deliver_tx(encode_tx_to_bytes(tx))
([carol.public_key], 3)], app.end_block(types.RequestEndBlock(height=99))
asset_id=tx_1.id,
).sign([carol.private_key])
assert tx_3.validate(b)
b.store_bulk_transactions([tx_3])
tx_4 = Transaction.transfer( resp = query.get_pre_commit_state(b.connection)
tx_2.to_inputs()[1:2] + tx_3.to_inputs()[0:1], assert resp['height'] == 99
[([bob.public_key], 3)], assert resp['transactions'] == [tx.id]
asset_id=tx_1.id,
).sign([alice.private_key])
assert tx_4.validate(b)
b.store_bulk_transactions([tx_4])
tx_5 = Transaction.transfer( app.begin_block(begin_block)
tx_2.to_inputs()[0:1], app.deliver_tx(encode_tx_to_bytes(tx))
[([alice.public_key], 2)], app.end_block(types.RequestEndBlock(height=100))
asset_id=tx_1.id, resp = query.get_pre_commit_state(b.connection)
).sign([bob.private_key]) assert resp['height'] == 100
assert tx_5.validate(b) assert resp['transactions'] == [tx.id]
b.store_bulk_transactions([tx_5]) # simulate a chain migration and assert the height is shifted
b.store_abci_chain(100, 'new-chain')
app = App(b)
app.begin_block(begin_block)
app.deliver_tx(encode_tx_to_bytes(tx))
app.end_block(types.RequestEndBlock(height=1))
resp = query.get_pre_commit_state(b.connection)
assert resp['height'] == 101
assert resp['transactions'] == [tx.id]
assert b.get_spent(tx_2.id, 0) == tx_5
assert not b.get_spent(tx_5.id, 0) def test_rollback_pre_commit_state_after_crash(b):
assert b.get_outputs_filtered(alice.public_key) validators = generate_validators([1] * 4)
assert b.get_outputs_filtered(alice.public_key, spent=False) b.store_validator_set(1, [v['storage'] for v in validators])
b.store_block(Block(height=1, transactions=[], app_hash='')._asdict())
public_key = validators[0]['public_key']
private_key = validators[0]['private_key']
voter_keys = [v['private_key'] for v in validators]
migration_election, votes = generate_election(b,
ChainMigrationElection,
public_key, private_key,
{},
voter_keys)
total_votes = votes
txs = [migration_election, *votes]
new_validator = generate_validators([1])[0]
validator_election, votes = generate_election(b,
ValidatorElection,
public_key, private_key,
new_validator['election'],
voter_keys)
total_votes += votes
txs += [validator_election, *votes]
b.store_bulk_transactions(txs)
b.store_abci_chain(2, 'new_chain')
b.store_validator_set(2, [v['storage'] for v in validators])
# TODO change to `4` when upgrading to Tendermint 0.22.4.
b.store_validator_set(3, [new_validator['storage']])
b.store_election(migration_election.id, 2, is_concluded=False)
b.store_election(validator_election.id, 2, is_concluded=True)
# no pre-commit state
rollback(b)
for tx in txs:
assert b.get_transaction(tx.id)
assert b.get_latest_abci_chain()
assert len(b.get_validator_change()['validators']) == 1
assert b.get_election(migration_election.id)
assert b.get_election(validator_election.id)
b.store_pre_commit_state({'height': 2, 'transactions': [tx.id for tx in txs]})
rollback(b)
for tx in txs:
assert not b.get_transaction(tx.id)
assert not b.get_latest_abci_chain()
assert len(b.get_validator_change()['validators']) == 4
assert len(b.get_validator_change(2)['validators']) == 4
assert not b.get_election(migration_election.id)
assert not b.get_election(validator_election.id)
def test_new_validator_set(b):
node1 = {'public_key': {'type': 'ed25519-base64',
'value': 'FxjS2/8AFYoIUqF6AcePTc87qOT7e4WGgH+sGCpTUDQ='},
'voting_power': 10}
node1_new_power = {'public_key': {'value': '1718D2DBFF00158A0852A17A01C78F4DCF3BA8E4FB7B8586807FAC182A535034',
'type': 'ed25519-base16'},
'power': 20}
node2 = {'public_key': {'value': '1888A353B181715CA2554701D06C1665BC42C5D936C55EA9C5DBCBDB8B3F02A3',
'type': 'ed25519-base16'},
'power': 10}
validators = [node1]
updates = [node1_new_power, node2]
b.store_validator_set(1, validators)
updated_validator_set = new_validator_set(b.get_validators(1), updates)
updated_validators = []
for u in updates:
updated_validators.append({'public_key': {'type': 'ed25519-base64',
'value': public_key_to_base64(u['public_key']['value'])},
'voting_power': u['power']})
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(types.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(types.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(types.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()

View File

@ -296,7 +296,7 @@ def test_get_validator_update(b, node_keys, node_key, ed25519_node_keys):
update = Election.process_block(b, 4, [tx_vote0, tx_vote1, tx_vote2]) update = Election.process_block(b, 4, [tx_vote0, tx_vote1, tx_vote2])
assert len(update) == 1 assert len(update) == 1
update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n') update_public_key = codecs.encode(update[0].pub_key.ed25519, 'base64').decode().rstrip('\n')
assert update_public_key == public_key64 assert update_public_key == public_key64
# remove validator # remove validator
@ -319,7 +319,7 @@ def test_get_validator_update(b, node_keys, node_key, ed25519_node_keys):
update = Election.process_block(b, 9, [tx_vote2]) update = Election.process_block(b, 9, [tx_vote2])
assert len(update) == 1 assert len(update) == 1
update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n') update_public_key = codecs.encode(update[0].pub_key.ed25519, 'base64').decode().rstrip('\n')
assert update_public_key == public_key64 assert update_public_key == public_key64
# assert that the public key is not a part of the current validator set # assert that the public key is not a part of the current validator set