mirror of
https://github.com/planetmint/planetmint.git
synced 2025-11-25 15:05:49 +00:00
* added initial interfaces for backend, refactored Asset and MetaData logic
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* adjusted input dataclass, added queries, removed convert
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* created backend models folder, replaced token_hex with uuid
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* Add cleanup and add constants
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* added to and from static methods to asset, input model and removed logic from tools
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* simplified store_bulk_transaction and corresponding query, adjusted test cases
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* changed script queries
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* Add Output model
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* Adapt Output class
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* Further fixes
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* Further fixes
* Get rid of decompose
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* refactored init.lua
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* refactored drop.lua
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* Add transaction data class
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* refactored init.lua
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* fix tests
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* Fix more tests
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* Format file
* Fix recursion error
* More fixes
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* Further fixes
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* using init.lua for db setup
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* fixed flush_db for new tarantool implementation
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* changed unique constraints
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* used new indexes on block related db operations
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* Adapt models
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* Check if blocks is empty
* adjusted get_txids_filtered for new indexes
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* Adaptions due to schema change
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* fixed get block test case
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* Fix subcondition serialization
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* Remove unnecessary method
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
* More fixes
* renamed group_txs and used data models in fastquery
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* adjusted query test cases, removed unused code
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* replaced asset search with get_asset_by_cid
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* added limit to asset queries
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* replaced metadata search with cid lookup
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* fixed most of the test_lib test cases
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* fixed election test cases
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* fixed some more test cases
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* fixed 'is' vs '==' issue
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* - blackified & fixed recovery / delete transactions issues becaues of data model transitions
- reintegrated get_transaction() call in query -> delegating this to get_complete_transactions_by_ids
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* show election status uses the governance table from now on
show election status maps the asset["data"] object properly
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed input object differences between old / new version and lookup of transaction in the governance pool
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed TX lookup issues due to different pools
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed wrong index name issue: transaction_by_asset vs transaction_by_asset_id
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed asset class key mixup
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* moved field removal methods to DbTransaction
redefined strcuture of DbTransction.to_dict() to be equal to the one of Transactions.to_dict()
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* added proper input conversion of the test cases and a proper input validation and object converion
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* simplified imports
fixed transfer input issues of the tests
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed comparision issue : dict vs. object
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed schema validation errors
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* added verification of ConditionDetails to the owner verification to avoid mixup between ConditionDetails and SubCondition
fixed Object comparision issues due to object changes
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed object handling issue and complicated stuff
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* added missing import
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* added proper corner case handling in case a requested block is not found
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed object comparision issue
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed output handling for validate_transfer_inputs
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* fixed wrong search pool usage
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed zenroom testcase
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed last abci issues and blackified the code
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* added tarantool exception catching and raising as well as logging
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed obj comparision issue in test_get_spent_issue_1271
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* added raiing CriticialDoubleSpend Exception for governance and transactions
fixed search space issue with election / voting commit lookup
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* * made returned outputs unique (get_owned_ids)
* added delete_output method to init.lua
* fixd output deletion issue by relaying the deletion to lua instead of the python code
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed rollback after crash
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* adjusted assets=None to assets=[{"data":None}] to avoid exeptions in the background service
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* removed unused code
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* removed unused code, reverted transaction fetching, added return types to queries
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* removed duplicate code
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* removed depricated code
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
* store transactions of various versions (backwardcompatibility)
added _bdb variable to init/drop DBs for the single use cases (started failing as TXs are looked up in DB - compared to before)
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* added support for v2.0 transaction to DB writing/reading
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* fixed merge errors (arguments ... )
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* blackified
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* Simplified unit tests (#294)
* adjusted make test
* 1st improvments to ease testing
* simplified gh actions
* adjusted gh action file
* removed deps
* added sudo to apt calls
* removed predefined pytest module definitions
* added installing planetmint into the unit test container
* give time to the db container
* added environment variables to unit-test.yml
* removed acceptances tests from test executions
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
* removed unused code, updated version number
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
Co-authored-by: cybnon <stefan.weber93@googlemail.com>
Co-authored-by: Jürgen Eckel <juergen@riddleandcode.com>
Co-authored-by: Jürgen Eckel <eckelj@users.noreply.github.com>
271 lines
10 KiB
Python
271 lines
10 KiB
Python
# Copyright © 2020 Interplanetary Database Association e.V.,
|
|
# Planetmint and IPDB software contributors.
|
|
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
|
# Code is Apache-2.0 and docs are CC-BY-4.0
|
|
|
|
"""This module contains all the goodness to integrate Planetmint
|
|
with Tendermint.
|
|
"""
|
|
import logging
|
|
import sys
|
|
|
|
from tendermint.abci import types_pb2
|
|
from abci.application import BaseApplication
|
|
from abci.application import OkCode
|
|
from tendermint.abci.types_pb2 import (
|
|
ResponseInfo,
|
|
ResponseInitChain,
|
|
ResponseCheckTx,
|
|
ResponseDeliverTx,
|
|
ResponseBeginBlock,
|
|
ResponseEndBlock,
|
|
ResponseCommit,
|
|
)
|
|
from planetmint import Planetmint
|
|
from planetmint.tendermint_utils import decode_transaction, calculate_hash, decode_validator
|
|
from planetmint.lib import Block
|
|
from planetmint.events import EventTypes, Event
|
|
|
|
|
|
CodeTypeError = 1
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class App(BaseApplication):
|
|
"""Bridge between Planetmint and Tendermint.
|
|
|
|
The role of this class is to expose the Planetmint
|
|
transaction logic to Tendermint Core.
|
|
"""
|
|
|
|
def __init__(self, planetmint_node=None, events_queue=None):
|
|
# super().__init__(abci)
|
|
logger.debug("Checking values of types")
|
|
logger.debug(dir(types_pb2))
|
|
self.events_queue = events_queue
|
|
self.planetmint_node = planetmint_node or Planetmint()
|
|
self.block_txn_ids = []
|
|
self.block_txn_hash = ""
|
|
self.block_transactions = []
|
|
self.validators = None
|
|
self.new_height = None
|
|
self.chain = self.planetmint_node.get_latest_abci_chain()
|
|
|
|
def log_abci_migration_error(self, chain_id, validators):
|
|
logger.error(
|
|
"An ABCI chain migration is in process. "
|
|
"Download theself.planetmint_node.get_latest_abci_chain new ABCI client and configure it with "
|
|
f"chain_id={chain_id} and validators={validators}."
|
|
)
|
|
|
|
def abort_if_abci_chain_is_not_synced(self):
|
|
if self.chain is None or self.chain["is_synced"]:
|
|
return
|
|
validators = self.planetmint_node.get_validators()
|
|
self.log_abci_migration_error(self.chain["chain_id"], validators)
|
|
sys.exit(1)
|
|
|
|
def init_chain(self, genesis):
|
|
"""Initialize chain upon genesis or a migration"""
|
|
app_hash = ""
|
|
height = 0
|
|
known_chain = self.planetmint_node.get_latest_abci_chain()
|
|
if known_chain is not None:
|
|
chain_id = known_chain["chain_id"]
|
|
|
|
if known_chain["is_synced"]:
|
|
msg = f"Got invalid InitChain ABCI request ({genesis}) - " f"the chain {chain_id} is already synced."
|
|
logger.error(msg)
|
|
sys.exit(1)
|
|
if chain_id != genesis.chain_id:
|
|
validators = self.planetmint_node.get_validators()
|
|
self.log_abci_migration_error(chain_id, validators)
|
|
sys.exit(1)
|
|
# set migration values for app hash and height
|
|
block = self.planetmint_node.get_latest_block()
|
|
app_hash = "" if block is None else block["app_hash"]
|
|
height = 0 if block is None else block["height"] + 1
|
|
known_validators = self.planetmint_node.get_validators()
|
|
validator_set = [decode_validator(v) for v in genesis.validators]
|
|
if known_validators and known_validators != validator_set:
|
|
self.log_abci_migration_error(known_chain["chain_id"], known_validators)
|
|
sys.exit(1)
|
|
block = Block(app_hash=app_hash, height=height, transactions=[])
|
|
self.planetmint_node.store_block(block._asdict())
|
|
self.planetmint_node.store_validator_set(height + 1, validator_set)
|
|
abci_chain_height = 0 if known_chain is None else known_chain["height"]
|
|
self.planetmint_node.store_abci_chain(abci_chain_height, genesis.chain_id, True)
|
|
self.chain = {"height": abci_chain_height, "is_synced": True, "chain_id": genesis.chain_id}
|
|
return ResponseInitChain()
|
|
|
|
def info(self, request):
|
|
"""Return height of the latest committed block."""
|
|
|
|
self.abort_if_abci_chain_is_not_synced()
|
|
|
|
# Check if Planetmint supports the Tendermint version
|
|
# if not (hasattr(request, 'version') and tendermint_version_is_compatible(request.version)):
|
|
# logger.error(f'Unsupported Tendermint version: {getattr(request, "version", "no version")}.'
|
|
# f' Currently, Planetmint only supports {__tm_supported_versions__}. Exiting!')
|
|
# sys.exit(1)
|
|
|
|
# logger.info(f"Tendermint version: {request.version}")
|
|
|
|
r = ResponseInfo()
|
|
block = self.planetmint_node.get_latest_block()
|
|
if block:
|
|
chain_shift = 0 if self.chain is None else self.chain["height"]
|
|
r.last_block_height = block["height"] - chain_shift
|
|
r.last_block_app_hash = block["app_hash"].encode("utf-8")
|
|
else:
|
|
r.last_block_height = 0
|
|
r.last_block_app_hash = b""
|
|
return r
|
|
|
|
def check_tx(self, raw_transaction):
|
|
"""Validate the transaction before entry into
|
|
the mempool.
|
|
|
|
Args:
|
|
raw_tx: a raw string (in bytes) transaction.
|
|
"""
|
|
|
|
self.abort_if_abci_chain_is_not_synced()
|
|
|
|
logger.debug("check_tx: %s", raw_transaction)
|
|
transaction = decode_transaction(raw_transaction)
|
|
if self.planetmint_node.is_valid_transaction(transaction):
|
|
logger.debug("check_tx: VALID")
|
|
return ResponseCheckTx(code=OkCode)
|
|
else:
|
|
logger.debug("check_tx: INVALID")
|
|
return ResponseCheckTx(code=CodeTypeError)
|
|
|
|
def begin_block(self, req_begin_block):
|
|
"""Initialize list of transaction.
|
|
Args:
|
|
req_begin_block: block object which contains block header
|
|
and block hash.
|
|
"""
|
|
self.abort_if_abci_chain_is_not_synced()
|
|
|
|
chain_shift = 0 if self.chain is None else self.chain["height"]
|
|
# req_begin_block.header.num_txs not found, so removing it.
|
|
logger.debug("BEGIN BLOCK, height:%s", req_begin_block.header.height + chain_shift)
|
|
|
|
self.block_txn_ids = []
|
|
self.block_transactions = []
|
|
return ResponseBeginBlock()
|
|
|
|
def deliver_tx(self, raw_transaction):
|
|
"""Validate the transaction before mutating the state.
|
|
|
|
Args:
|
|
raw_tx: a raw string (in bytes) transaction.
|
|
"""
|
|
|
|
self.abort_if_abci_chain_is_not_synced()
|
|
|
|
logger.debug("deliver_tx: %s", raw_transaction)
|
|
transaction = self.planetmint_node.is_valid_transaction(
|
|
decode_transaction(raw_transaction), self.block_transactions
|
|
)
|
|
|
|
if not transaction:
|
|
logger.debug("deliver_tx: INVALID")
|
|
return ResponseDeliverTx(code=CodeTypeError)
|
|
else:
|
|
logger.debug("storing tx")
|
|
self.block_txn_ids.append(transaction.id)
|
|
self.block_transactions.append(transaction)
|
|
return ResponseDeliverTx(code=OkCode)
|
|
|
|
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.planetmint_node.store_pre_commit_state(pre_commit_state)
|
|
|
|
block_txn_hash = calculate_hash(self.block_txn_ids)
|
|
block = self.planetmint_node.get_latest_block()
|
|
|
|
logger.debug("BLOCK: ", 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 = self.planetmint_node.process_block(self.new_height, self.block_transactions)
|
|
|
|
return ResponseEndBlock(validator_updates=validator_update)
|
|
|
|
def commit(self):
|
|
"""Store the new height and along with block hash."""
|
|
|
|
self.abort_if_abci_chain_is_not_synced()
|
|
|
|
data = self.block_txn_hash.encode("utf-8")
|
|
|
|
# register a new block only when new transactions are received
|
|
if self.block_txn_ids:
|
|
self.planetmint_node.store_bulk_transactions(self.block_transactions)
|
|
|
|
block = Block(app_hash=self.block_txn_hash, height=self.new_height, transactions=self.block_txn_ids)
|
|
# NOTE: storing the block should be the last operation during commit
|
|
# this effects crash recovery. Refer BEP#8 for details
|
|
self.planetmint_node.store_block(block._asdict())
|
|
|
|
logger.debug(
|
|
"Commit-ing new block with hash: apphash=%s ," "height=%s, txn ids=%s",
|
|
data,
|
|
self.new_height,
|
|
self.block_txn_ids,
|
|
)
|
|
|
|
if self.events_queue:
|
|
event = Event(
|
|
EventTypes.BLOCK_VALID,
|
|
{"height": self.new_height, "hash": self.block_txn_hash, "transactions": self.block_transactions},
|
|
)
|
|
self.events_queue.put(event)
|
|
|
|
return ResponseCommit(data=data)
|
|
|
|
|
|
def rollback(planetmint):
|
|
pre_commit = None
|
|
|
|
try:
|
|
pre_commit = planetmint.get_pre_commit_state()
|
|
except Exception as e:
|
|
logger.exception("Unexpected error occurred while executing get_pre_commit_state()", e)
|
|
|
|
if pre_commit is None or len(pre_commit) == 0:
|
|
# the pre_commit record is first stored in the first `end_block`
|
|
return
|
|
|
|
latest_block = planetmint.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"]:
|
|
planetmint.rollback_election(pre_commit["height"], pre_commit["transactions"])
|
|
planetmint.delete_transactions(pre_commit["transactions"])
|