def test_end_block_return_validator_updates(b, init_chain_request): app = App(b) app.init_chain(init_chain_request) begin_block = 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(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.data
def test_chain_migration_election_show_shows_concluded(b): validators = generate_validators([1] * 4) b.store_validator_set(1, [v['storage'] for v in validators]) 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, ChainMigrationElection, public_key, private_key, {}, voter_keys) assert not run_election_show(Namespace(election_id=election.id), b) b.store_bulk_transactions([election]) Election.process_block(b, 1, [election]) assert run_election_show(Namespace(election_id=election.id), b) == \ 'status=ongoing' b.store_abci_chain(1, 'chain-X') b.store_block(Block(height=1, transactions=[v.id for v in votes], app_hash='last_app_hash')._asdict()) Election.process_block(b, 2, votes) assert run_election_show(Namespace(election_id=election.id), b) == \ f'''status=concluded
def test_chain_migration_election_show_shows_inconclusive(b): validators = generate_validators([1] * 4) b.store_validator_set(1, [v['storage'] for v in validators]) 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, ChainMigrationElection, public_key, private_key, {}, voter_keys) assert not run_election_show(Namespace(election_id=election.id), b) Election.process_block(b, 1, [election]) b.store_bulk_transactions([election]) assert run_election_show(Namespace(election_id=election.id), b) == \ 'status=ongoing' b.store_block(Block(height=1, transactions=[], app_hash='')._asdict()) b.store_validator_set(2, [v['storage'] for v in validators]) assert run_election_show(Namespace(election_id=election.id), b) == \ 'status=ongoing' b.store_block(Block(height=2, transactions=[], app_hash='')._asdict()) # TODO insert yet another block here when upgrading to Tendermint 0.22.4. assert run_election_show(Namespace(election_id=election.id), b) == \ 'status=inconclusive'
def test_chain_migration_election_show_shows_concluded(b): validators = generate_validators([1] * 4) b.store_validator_set(1, [v['storage'] for v in validators]) 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, ChainMigrationElection, public_key, private_key, {}, voter_keys) assert not run_election_show(Namespace(election_id=election.id), b) b.store_bulk_transactions([election]) Election.process_block(b, 1, [election]) assert run_election_show(Namespace(election_id=election.id), b) == \ 'status=ongoing' b.store_abci_chain(1, 'chain-X') b.store_block( Block(height=1, transactions=[v.id for v in votes], app_hash='last_app_hash')._asdict()) Election.process_block(b, 2, votes) assert run_election_show(Namespace(election_id=election.id), b) == \ f'''status=concluded
def test_end_block_return_validator_updates(b, init_chain_request): app = App(b) app.init_chain(init_chain_request) begin_block = 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(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.data
def test_process_block_approves_after_pending_validator_update(b): 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) txs = [election] total_votes = votes another_validator = generate_validators([1])[0] election, votes = generate_election(b, ValidatorElection, public_key, private_key, another_validator['election'], voter_keys) txs += [election] total_votes += votes election, votes = generate_election(b, ChainMigrationElection, public_key, private_key, {}, voter_keys) txs += [election] total_votes += votes b.store_abci_chain(1, 'chain-X') Election.process_block(b, 1, txs) b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash='')._asdict()) b.store_bulk_transactions(txs) Election.process_block(b, 2, total_votes) validators = b.get_validators() assert len(validators) == 5 assert new_validator['storage'] in validators assert another_validator['storage'] not in validators assert b.get_election(txs[0].id)['is_concluded'] assert not b.get_election(txs[1].id)['is_concluded'] assert b.get_election(txs[2].id)['is_concluded'] assert b.get_latest_abci_chain() == {'height': 2, 'chain_id': 'chain-X-migrated-at-height-1', 'is_synced': False}
def test_get_validator_update(b, node_keys, node_key, ed25519_node_keys): reset_validator_set(b, node_keys, 1) power = 1 public_key = '9B3119650DF82B9A5D8A12E38953EA47475C09F0C48A4E6A0ECE182944B24403' public_key64 = public_key_to_base64(public_key) new_validator = {'public_key': {'value': public_key, 'type': 'ed25519-base16'}, 'node_id': 'some_node_id', 'power': power} voters = ValidatorElection.recipients(b) election = ValidatorElection.generate([node_key.public_key], voters, new_validator).sign([node_key.private_key]) # store election b.store_bulk_transactions([election]) tx_vote0 = gen_vote(election, 0, ed25519_node_keys) tx_vote1 = gen_vote(election, 1, ed25519_node_keys) tx_vote2 = gen_vote(election, 2, ed25519_node_keys) assert not election.has_concluded(b, [tx_vote0]) assert not election.has_concluded(b, [tx_vote0, tx_vote1]) assert election.has_concluded(b, [tx_vote0, tx_vote1, tx_vote2]) assert Election.process_block(b, 4, [tx_vote0]) == [] assert Election.process_block(b, 4, [tx_vote0, tx_vote1]) == [] update = Election.process_block(b, 4, [tx_vote0, tx_vote1, tx_vote2]) assert len(update) == 1 update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n') assert update_public_key == public_key64 # remove validator power = 0 new_validator = {'public_key': {'value': public_key, 'type': 'ed25519-base16'}, 'node_id': 'some_node_id', 'power': power} voters = ValidatorElection.recipients(b) election = ValidatorElection.generate([node_key.public_key], voters, new_validator).sign([node_key.private_key]) # store election b.store_bulk_transactions([election]) tx_vote0 = gen_vote(election, 0, ed25519_node_keys) tx_vote1 = gen_vote(election, 1, ed25519_node_keys) tx_vote2 = gen_vote(election, 2, ed25519_node_keys) b.store_bulk_transactions([tx_vote0, tx_vote1]) update = Election.process_block(b, 9, [tx_vote2]) assert len(update) == 1 update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n') assert update_public_key == public_key64 # assert that the public key is not a part of the current validator set for v in b.get_validators(10): assert not v['public_key']['value'] == public_key64
def test_process_block_approves_after_pending_validator_update(b): 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) txs = [election] total_votes = votes another_validator = generate_validators([1])[0] election, votes = generate_election(b, ValidatorElection, public_key, private_key, another_validator['election'], voter_keys) txs += [election] total_votes += votes election, votes = generate_election(b, ChainMigrationElection, public_key, private_key, {}, voter_keys) txs += [election] total_votes += votes b.store_abci_chain(1, 'chain-X') Election.process_block(b, 1, txs) b.store_block( Block(height=1, transactions=[tx.id for tx in txs], app_hash='')._asdict()) b.store_bulk_transactions(txs) Election.process_block(b, 2, total_votes) validators = b.get_validators() assert len(validators) == 5 assert new_validator['storage'] in validators assert another_validator['storage'] not in validators assert b.get_election(txs[0].id)['is_concluded'] assert not b.get_election(txs[1].id)['is_concluded'] assert b.get_election(txs[2].id)['is_concluded'] assert b.get_latest_abci_chain() == { 'height': 2, 'chain_id': 'chain-X-migrated-at-height-1', 'is_synced': False }
def rollback(b): pre_commit = b.get_pre_commit_state() if pre_commit is None: # the pre_commit record is first stored in the first `end_block` return latest_block = b.get_latest_block() if latest_block is None: logger.error('Found precommit state but no blocks!') sys.exit(1) # NOTE: the pre-commit state is always at most 1 block ahead of the commited state if latest_block['height'] < pre_commit['height']: Election.rollback(b, pre_commit['height'], pre_commit['transactions']) b.delete_transactions(pre_commit['transactions'])
def end_block(self, request_end_block): """Calculate block hash using transaction ids and previous block hash to be stored in the next block. Args: height (int): new height of the chain. """ self.abort_if_abci_chain_is_not_synced() chain_shift = 0 if self.chain is None else self.chain['height'] height = request_end_block.height + chain_shift self.new_height = height block_txn_hash = calculate_hash(self.block_txn_ids) block = self.bigchaindb.get_latest_block() if self.block_txn_ids: self.block_txn_hash = calculate_hash( [block['app_hash'], block_txn_hash]) else: self.block_txn_hash = block['app_hash'] validator_update = Election.process_block(self.bigchaindb, self.new_height, self.block_transactions) # Store pre-commit state to recover in case there is a crash during `commit` pre_commit_state = PreCommitState(commit_id=PRE_COMMIT_ID, height=self.new_height, transactions=self.block_txn_ids) logger.debug('Updating PreCommitState: %s', self.new_height) self.bigchaindb.store_pre_commit_state(pre_commit_state._asdict()) return ResponseEndBlock(validator_updates=validator_update)
def end_block(self, request_end_block): """Calculate block hash using transaction ids and previous block hash to be stored in the next block. Args: height (int): new height of the chain. """ self.abort_if_abci_chain_is_not_synced() chain_shift = 0 if self.chain is None else self.chain['height'] height = request_end_block.height + chain_shift self.new_height = height # store pre-commit state to recover in case there is a crash during # `end_block` or `commit` logger.debug(f'Updating pre-commit state: {self.new_height}') pre_commit_state = dict(height=self.new_height, transactions=self.block_txn_ids) self.bigchaindb.store_pre_commit_state(pre_commit_state) block_txn_hash = calculate_hash(self.block_txn_ids) block = self.bigchaindb.get_latest_block() if self.block_txn_ids: self.block_txn_hash = calculate_hash([block['app_hash'], block_txn_hash]) else: self.block_txn_hash = block['app_hash'] validator_update = Election.process_block(self.bigchaindb, self.new_height, self.block_transactions) return ResponseEndBlock(validator_updates=validator_update)
def gen_vote(election, i, ed25519_node_keys): (input_i, votes_i, key_i) = to_inputs(election, i, ed25519_node_keys) election_pub_key = Election.to_public_key(election.id) return Vote.generate([input_i], [([election_pub_key], votes_i)], election_id=election.id)\ .sign([key_i.private_key])
def test_approved_elections_concludes_all_elections(b): 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'] election, votes = generate_election(b, ValidatorElection, public_key, private_key, new_validator['election']) txs = [election] total_votes = votes election, votes = generate_election(b, ChainMigrationElection, public_key, private_key, {}) txs += [election] total_votes += votes b.store_abci_chain(1, 'chain-X') b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash='')._asdict()) b.store_bulk_transactions(txs) Election.approved_elections(b, 1, total_votes) validators = b.get_validators() assert len(validators) == 5 assert new_validator['storage'] in validators chain = b.get_latest_abci_chain() assert chain assert chain == { 'height': 2, 'is_synced': False, 'chain_id': 'chain-X-migrated-at-height-1', } for tx in txs: election = b.get_election(tx.id) assert election
def test_process_block_applies_only_one_migration(b): validators = generate_validators([1] * 4) b.store_validator_set(1, [v['storage'] for v in validators]) 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, ChainMigrationElection, public_key, private_key, {}, voter_keys) txs = [election] total_votes = votes election, votes = generate_election(b, ChainMigrationElection, public_key, private_key, {}, voter_keys) txs += [election] total_votes += votes b.store_abci_chain(1, 'chain-X') Election.process_block(b, 1, txs) b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash='')._asdict()) b.store_bulk_transactions(txs) Election.process_block(b, 1, total_votes) chain = b.get_latest_abci_chain() assert chain assert chain == { 'height': 2, 'is_synced': False, 'chain_id': 'chain-X-migrated-at-height-1', } assert b.get_election(txs[0].id)['is_concluded'] assert not b.get_election(txs[1].id)['is_concluded']
def test_process_block_approves_only_one_validator_update(b): 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) txs = [election] total_votes = votes another_validator = generate_validators([1])[0] election, votes = generate_election(b, ValidatorElection, public_key, private_key, another_validator['election'], voter_keys) txs += [election] total_votes += votes Election.process_block(b, 1, txs) b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash='')._asdict()) b.store_bulk_transactions(txs) Election.process_block(b, 2, total_votes) validators = b.get_validators() assert len(validators) == 5 assert new_validator['storage'] in validators assert another_validator['storage'] not in validators assert b.get_election(txs[0].id)['is_concluded'] assert not b.get_election(txs[1].id)['is_concluded']
def generate_election(b, cls, public_key, private_key, asset_data): voters = cls.recipients(b) election = cls.generate([public_key], voters, asset_data, None).sign([private_key]) votes = [ Vote.generate([election.to_inputs()[i]], [([Election.to_public_key(election.id)], power)], election.id) for i, (_, power) in enumerate(voters) ] return election, votes
def test_process_block_approves_only_one_validator_update(b): 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) txs = [election] total_votes = votes another_validator = generate_validators([1])[0] election, votes = generate_election(b, ValidatorElection, public_key, private_key, another_validator['election'], voter_keys) txs += [election] total_votes += votes Election.process_block(b, 1, txs) b.store_block( Block(height=1, transactions=[tx.id for tx in txs], app_hash='')._asdict()) b.store_bulk_transactions(txs) Election.process_block(b, 2, total_votes) validators = b.get_validators() assert len(validators) == 5 assert new_validator['storage'] in validators assert another_validator['storage'] not in validators assert b.get_election(txs[0].id)['is_concluded'] assert not b.get_election(txs[1].id)['is_concluded']
def test_process_block_applies_only_one_migration(b): validators = generate_validators([1] * 4) b.store_validator_set(1, [v['storage'] for v in validators]) public_key = validators[0]['public_key'] private_key = validators[0]['private_key'] election, votes = generate_election(b, ChainMigrationElection, public_key, private_key, {}) txs = [election] total_votes = votes election, votes = generate_election(b, ChainMigrationElection, public_key, private_key, {}) txs += [election] total_votes += votes b.store_abci_chain(1, 'chain-X') Election.process_block(b, 1, txs) b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash='')._asdict()) b.store_bulk_transactions(txs) Election.process_block(b, 1, total_votes) chain = b.get_latest_abci_chain() assert chain assert chain == { 'height': 2, 'is_synced': False, 'chain_id': 'chain-X-migrated-at-height-1', } assert b.get_election(txs[0].id)['is_concluded'] assert not b.get_election(txs[1].id)['is_concluded']
def test_process_block_does_not_approve_after_validator_update(b): 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) txs = [election] total_votes = votes b.store_block( Block(height=1, transactions=[tx.id for tx in txs], app_hash='')._asdict()) Election.process_block(b, 1, txs) b.store_bulk_transactions(txs) second_election, second_votes = generate_election(b, ChainMigrationElection, public_key, private_key, {}, voter_keys) Election.process_block(b, 2, total_votes + [second_election]) b.store_block( Block(height=2, transactions=[v.id for v in total_votes + [second_election]], app_hash='')._asdict()) b.store_abci_chain(1, 'chain-X') Election.process_block(b, 3, second_votes) assert not b.get_election(second_election.id)['is_concluded'] assert b.get_latest_abci_chain() == { 'height': 1, 'chain_id': 'chain-X', 'is_synced': True }
def test_process_block_does_not_approve_after_validator_update(b): 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) txs = [election] total_votes = votes b.store_block(Block(height=1, transactions=[tx.id for tx in txs], app_hash='')._asdict()) Election.process_block(b, 1, txs) b.store_bulk_transactions(txs) second_election, second_votes = generate_election(b, ChainMigrationElection, public_key, private_key, {}, voter_keys) Election.process_block(b, 2, total_votes + [second_election]) b.store_block(Block(height=2, transactions=[v.id for v in total_votes + [second_election]], app_hash='')._asdict()) b.store_abci_chain(1, 'chain-X') Election.process_block(b, 3, second_votes) assert not b.get_election(second_election.id)['is_concluded'] assert b.get_latest_abci_chain() == {'height': 1, 'chain_id': 'chain-X', 'is_synced': True}
def test_approved_elections_gracefully_handles_empty_block(b): Election.approved_elections(b, 1, [])
def test_process_block_gracefully_handles_empty_block(b): Election.process_block(b, 1, [])
def test_get_validator_update(b, node_keys, node_key, ed25519_node_keys): reset_validator_set(b, node_keys, 1) power = 1 public_key = '9B3119650DF82B9A5D8A12E38953EA47475C09F0C48A4E6A0ECE182944B24403' public_key64 = public_key_to_base64(public_key) new_validator = { 'public_key': { 'value': public_key, 'type': 'ed25519-base16' }, 'node_id': 'some_node_id', 'power': power } voters = ValidatorElection.recipients(b) election = ValidatorElection.generate([node_key.public_key], voters, new_validator).sign( [node_key.private_key]) # store election b.store_bulk_transactions([election]) tx_vote0 = gen_vote(election, 0, ed25519_node_keys) tx_vote1 = gen_vote(election, 1, ed25519_node_keys) tx_vote2 = gen_vote(election, 2, ed25519_node_keys) assert not election.has_concluded(b, [tx_vote0]) assert not election.has_concluded(b, [tx_vote0, tx_vote1]) assert election.has_concluded(b, [tx_vote0, tx_vote1, tx_vote2]) assert Election.process_block(b, 4, [tx_vote0]) == [] assert Election.process_block(b, 4, [tx_vote0, tx_vote1]) == [] update = Election.process_block(b, 4, [tx_vote0, tx_vote1, tx_vote2]) assert len(update) == 1 update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n') assert update_public_key == public_key64 # remove validator power = 0 new_validator = { 'public_key': { 'value': public_key, 'type': 'ed25519-base16' }, 'node_id': 'some_node_id', 'power': power } voters = ValidatorElection.recipients(b) election = ValidatorElection.generate([node_key.public_key], voters, new_validator).sign( [node_key.private_key]) # store election b.store_bulk_transactions([election]) tx_vote0 = gen_vote(election, 0, ed25519_node_keys) tx_vote1 = gen_vote(election, 1, ed25519_node_keys) tx_vote2 = gen_vote(election, 2, ed25519_node_keys) b.store_bulk_transactions([tx_vote0, tx_vote1]) update = Election.process_block(b, 9, [tx_vote2]) assert len(update) == 1 update_public_key = codecs.encode(update[0].pub_key.data, 'base64').decode().rstrip('\n') assert update_public_key == public_key64 # assert that the public key is not a part of the current validator set for v in b.get_validators(10): assert not v['public_key']['value'] == public_key64