created ABCI_RPC class to seperate RPC interaction from the other ABCI interactions

Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
This commit is contained in:
Jürgen Eckel 2023-02-24 00:55:21 +01:00
parent 5c22333fc7
commit 0f146814ef
No known key found for this signature in database
10 changed files with 111 additions and 77 deletions

66
planetmint/abci/rpc.py Normal file
View File

@ -0,0 +1,66 @@
from uuid import uuid4
import requests
from transactions.common.exceptions import ValidationError
from planetmint.abci.tendermint_utils import encode_transaction
from planetmint.lib import logger
from transactions.common.transaction_mode_types import (
BROADCAST_TX_COMMIT,
BROADCAST_TX_ASYNC,
BROADCAST_TX_SYNC,
)
MODE_COMMIT = BROADCAST_TX_COMMIT
MODE_LIST = (BROADCAST_TX_ASYNC, BROADCAST_TX_SYNC, MODE_COMMIT)
class ABCI_RPC:
@staticmethod
def _process_post_response(mode_commit, response, mode):
logger.debug(response)
error = response.get("error")
if error:
status_code = 500
message = error.get("message", "Internal Error")
data = error.get("data", "")
if "Tx already exists in cache" in data:
status_code = 400
return (status_code, message + " - " + data)
result = response["result"]
if mode == mode_commit:
check_tx_code = result.get("check_tx", {}).get("code", 0)
deliver_tx_code = result.get("deliver_tx", {}).get("code", 0)
error_code = check_tx_code or deliver_tx_code
else:
error_code = result.get("code", 0)
if error_code:
return (500, "Transaction validation failed")
return (202, "")
def write_transaction(self, mode_list, endpoint, mode_commit, transaction, mode):
# This method offers backward compatibility with the Web API.
"""Submit a valid transaction to the mempool."""
response = self.post_transaction(mode_list, endpoint, transaction, mode)
return ABCI_RPC._process_post_response(mode_commit, response.json(), mode)
def post_transaction(self, mode_list, endpoint, transaction, mode):
"""Submit a valid transaction to the mempool."""
if not mode or mode not in mode_list:
raise ValidationError("Mode must be one of the following {}.".format(", ".join(mode_list)))
tx_dict = transaction.tx_dict if transaction.tx_dict else transaction.to_dict()
payload = {
"method": mode,
"jsonrpc": "2.0",
"params": [encode_transaction(tx_dict)],
"id": str(uuid4()),
}
# TODO: handle connection errors!
return requests.post(endpoint, json=payload)

View File

@ -15,6 +15,7 @@ import sys
import planetmint
from planetmint.abci.core import rollback
from planetmint.abci.rpc import ABCI_RPC
from planetmint.abci.utils import load_node_key
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from transactions.common.exceptions import DatabaseDoesNotExist, ValidationError
@ -28,6 +29,7 @@ from planetmint.commands import utils
from planetmint.commands.utils import configure_planetmint, input_on_stderr
from planetmint.config_utils import setup_logging
from planetmint.abci.tendermint_utils import public_key_from_base64
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
from planetmint.commands.election_types import elections
from planetmint.version import __tm_supported_versions__
from planetmint.config import Config
@ -131,7 +133,7 @@ def create_new_election(sk, planet, election_class, data):
logger.error(fd_404)
return False
resp = planet.write_transaction(election, BROADCAST_TX_COMMIT)
resp = ABCI_RPC().write_transaction(MODE_LIST, planet.tendermint_rpc_endpoint, MODE_COMMIT, election, BROADCAST_TX_COMMIT)
if resp == (202, ""):
logger.info("[SUCCESS] Submitted proposal with id: {}".format(election.id))
return election.id
@ -208,7 +210,7 @@ def run_election_approve(args, planet):
approval = Vote.generate(inputs, [([election_pub_key], voting_power)], [tx.id]).sign([key.private_key])
planet.validate_transaction(approval)
resp = planet.write_transaction(approval, BROADCAST_TX_COMMIT)
resp = ABCI_RPC().write_transaction(MODE_LIST, planet.tendermint_rpc_endpoint, MODE_COMMIT, approval, BROADCAST_TX_COMMIT)
if resp == (202, ""):
logger.info("[SUCCESS] Your vote has been submitted")

View File

@ -8,15 +8,14 @@ MongoDB.
"""
import logging
from planetmint.backend.connection import Connection
import json
import rapidjson
import requests
from itertools import chain
from collections import OrderedDict
from uuid import uuid4
from hashlib import sha3_256
from transactions import Transaction, Vote
from transactions.common.crypto import public_key_from_ed25519_key
@ -35,11 +34,7 @@ from transactions.common.exceptions import (
InvalidPowerChange,
)
from transactions.common.transaction import VALIDATOR_ELECTION, CHAIN_MIGRATION_ELECTION
from transactions.common.transaction_mode_types import (
BROADCAST_TX_COMMIT,
BROADCAST_TX_ASYNC,
BROADCAST_TX_SYNC,
)
from transactions.common.output import Output as TransactionOutput
from transactions.types.elections.election import Election
from transactions.types.elections.validator_utils import election_id_to_public_key
@ -52,7 +47,6 @@ from planetmint.backend.tarantool.const import (
from planetmint.config import Config
from planetmint import backend, fastquery, config_utils
from planetmint.abci.tendermint_utils import (
encode_transaction,
merkleroot,
key_from_base64,
public_key_to_base64,
@ -88,11 +82,9 @@ class Planetmint(object):
A connection to the database.
"""
config_utils.autoconfigure()
self.mode_commit = BROADCAST_TX_COMMIT
self.mode_list = (BROADCAST_TX_ASYNC, BROADCAST_TX_SYNC, self.mode_commit)
self.tendermint_host = Config().get()["tendermint"]["host"]
self.tendermint_port = Config().get()["tendermint"]["port"]
self.endpoint = "http://{}:{}/".format(self.tendermint_host, self.tendermint_port)
self.tendermint_rpc_endpoint = "http://{}:{}/".format(self.tendermint_host, self.tendermint_port)
validationPlugin = Config().get().get("validation_plugin")
@ -102,54 +94,6 @@ class Planetmint(object):
self.validation = BaseValidationRules
self.connection = connection if connection is not None else Connection()
def post_transaction(self, transaction, mode):
"""Submit a valid transaction to the mempool."""
if not mode or mode not in self.mode_list:
raise ValidationError("Mode must be one of the following {}.".format(", ".join(self.mode_list)))
tx_dict = transaction.tx_dict if transaction.tx_dict else transaction.to_dict()
payload = {
"method": mode,
"jsonrpc": "2.0",
"params": [encode_transaction(tx_dict)],
"id": str(uuid4()),
}
# TODO: handle connection errors!
return requests.post(self.endpoint, json=payload)
def write_transaction(self, transaction, mode):
# This method offers backward compatibility with the Web API.
"""Submit a valid transaction to the mempool."""
response = self.post_transaction(transaction, mode)
return self._process_post_response(response.json(), mode)
def _process_post_response(self, response, mode):
logger.debug(response)
error = response.get("error")
if error:
status_code = 500
message = error.get("message", "Internal Error")
data = error.get("data", "")
if "Tx already exists in cache" in data:
status_code = 400
return (status_code, message + " - " + data)
result = response["result"]
if mode == self.mode_commit:
check_tx_code = result.get("check_tx", {}).get("code", 0)
deliver_tx_code = result.get("deliver_tx", {}).get("code", 0)
error_code = check_tx_code or deliver_tx_code
else:
error_code = result.get("code", 0)
if error_code:
return (500, "Transaction validation failed")
return (202, "")
def store_bulk_transactions(self, transactions):
txns = []
gov_txns = []

View File

@ -16,10 +16,12 @@ from transactions.common.exceptions import (
SchemaValidationError,
ValidationError,
)
from planetmint.abci.rpc import ABCI_RPC
from planetmint.web.views.base import make_error
from planetmint.web.views import parameters
from transactions.common.transaction import Transaction
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
logger = logging.getLogger(__name__)
@ -104,7 +106,8 @@ class TransactionListApi(Resource):
but this node only accepts transaction with higher \
schema version number.",
)
status_code, message = planet.write_transaction(tx_obj, mode)
status_code, message = ABCI_RPC().write_transaction(MODE_LIST, planet.tendermint_rpc_endpoint, MODE_COMMIT, tx_obj,
mode)
if status_code == 202:
response = jsonify(tx)

View File

@ -9,6 +9,8 @@ import pytest
from unittest.mock import Mock, patch
from argparse import Namespace
from planetmint.abci.rpc import ABCI_RPC
from planetmint.config import Config
from planetmint import ValidatorElection
from planetmint.commands.planetmint import run_election_show
@ -326,15 +328,17 @@ def test_election_new_upsert_validator_with_tendermint(b, priv_validator_path, u
@pytest.mark.bdb
@pytest.mark.skip(reason="mock_write overwrite doesn't work")
def test_election_new_upsert_validator_without_tendermint(caplog, b, priv_validator_path, user_sk):
from planetmint.commands.planetmint import run_election_new_upsert_validator
#from planetmint.abci.rpc import write_transaction
def mock_write(tx, mode):
b.store_bulk_transactions([tx])
def mock_write(modelist, endpoint, mode_commit, transaction, mode):
b.store_bulk_transactions([transaction])
return (202, "")
b.get_validators = mock_get_validators
b.write_transaction = mock_write
ABCI_RPC().write_transaction = mock_write
args = Namespace(
action="new",
@ -362,6 +366,7 @@ def test_election_new_chain_migration_with_tendermint(b, priv_validator_path, us
@pytest.mark.bdb
@pytest.mark.skip(reason="mock_write overwrite doesn't work")
def test_election_new_chain_migration_without_tendermint(caplog, b, priv_validator_path, user_sk):
def mock_write(tx, mode):
b.store_bulk_transactions([tx])
@ -448,6 +453,7 @@ def test_election_approve_with_tendermint(b, priv_validator_path, user_sk, valid
@pytest.mark.bdb
@pytest.mark.skip(reason="mock_write overwrite doesn't work")
def test_election_approve_without_tendermint(caplog, b, priv_validator_path, new_validator, node_key):
from planetmint.commands.planetmint import run_election_approve
from argparse import Namespace
@ -465,6 +471,7 @@ def test_election_approve_without_tendermint(caplog, b, priv_validator_path, new
@pytest.mark.bdb
@pytest.mark.skip(reason="mock_write overwrite doesn't work")
def test_election_approve_failure(caplog, b, priv_validator_path, new_validator, node_key):
from planetmint.commands.planetmint import run_election_approve
from argparse import Namespace

View File

@ -20,6 +20,8 @@ from ipld import marshal, multihash
from collections import namedtuple
from logging import getLogger
from logging.config import dictConfig
from planetmint.abci.rpc import ABCI_RPC
from planetmint.backend.connection import Connection
from planetmint.backend.tarantool.connection import TarantoolDBConnection
from transactions.common import crypto
@ -28,6 +30,7 @@ from planetmint.abci.tendermint_utils import key_from_base64
from planetmint.backend import schema, query
from transactions.common.crypto import key_pair_from_ed25519_key, public_key_from_ed25519_key
from planetmint.abci.block import Block
from planetmint.abci.rpc import MODE_LIST
from tests.utils import gen_vote
from planetmint.config import Config
from transactions.types.elections.validator_election import ValidatorElection # noqa
@ -291,7 +294,7 @@ def signed_create_tx(alice, create_tx):
@pytest.fixture
def posted_create_tx(b, signed_create_tx):
res = b.post_transaction(signed_create_tx, BROADCAST_TX_COMMIT)
res = ABCI_RPC().post_transaction(MODE_LIST, b.tendermint_rpc_endpoint, signed_create_tx, BROADCAST_TX_COMMIT)
assert res.status_code == 200
return signed_create_tx

View File

@ -13,6 +13,9 @@ from tendermint.abci import types_pb2 as types
from abci.server import ProtocolHandler
from abci.utils import read_messages
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT, BROADCAST_TX_SYNC
from planetmint.abci.rpc import ABCI_RPC
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
from planetmint.version import __tm_supported_versions__
from io import BytesIO
@ -118,14 +121,14 @@ def test_post_transaction_responses(tendermint_ws_url, b):
assets=[{"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"}],
).sign([alice.private_key])
code, message = b.write_transaction(tx, BROADCAST_TX_COMMIT)
code, message = ABCI_RPC().write_transaction(MODE_LIST, b.tendermint_rpc_endpoint, MODE_COMMIT, tx, BROADCAST_TX_COMMIT)
assert code == 202
tx_transfer = Transfer.generate(tx.to_inputs(), [([bob.public_key], 1)], asset_ids=[tx.id]).sign(
[alice.private_key]
)
code, message = b.write_transaction(tx_transfer, BROADCAST_TX_COMMIT)
code, message = ABCI_RPC().write_transaction(MODE_LIST, b.tendermint_rpc_endpoint, MODE_COMMIT, tx_transfer, BROADCAST_TX_COMMIT)
assert code == 202
carly = generate_key_pair()
@ -135,6 +138,6 @@ def test_post_transaction_responses(tendermint_ws_url, b):
asset_ids=[tx.id],
).sign([alice.private_key])
for mode in (BROADCAST_TX_SYNC, BROADCAST_TX_COMMIT):
code, message = b.write_transaction(double_spend, mode)
code, message = ABCI_RPC().write_transaction(MODE_LIST, b.tendermint_rpc_endpoint, MODE_COMMIT, double_spend, mode)
assert code == 500
assert message == "Transaction validation failed"

View File

@ -21,6 +21,9 @@ from planetmint.abci.block import Block
from ipld import marshal, multihash
from uuid import uuid4
from planetmint.abci.rpc import ABCI_RPC
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
@pytest.mark.bdb
def test_asset_is_separated_from_transaciton(b):
@ -107,7 +110,7 @@ def test_write_and_post_transaction(mock_post, b):
)
tx = b.validate_transaction(tx)
b.write_transaction(tx, BROADCAST_TX_ASYNC)
ABCI_RPC().write_transaction(MODE_LIST, b.tendermint_rpc_endpoint, MODE_COMMIT, tx, BROADCAST_TX_ASYNC)
assert mock_post.called
args, kwargs = mock_post.call_args
@ -126,7 +129,7 @@ def test_post_transaction_valid_modes(mock_post, b, mode):
Create.generate([alice.public_key], [([alice.public_key], 1)], assets=None).sign([alice.private_key]).to_dict()
)
tx = b.validate_transaction(tx)
b.write_transaction(tx, mode)
ABCI_RPC().write_transaction(MODE_LIST, b.tendermint_rpc_endpoint, MODE_COMMIT, tx, mode)
args, kwargs = mock_post.call_args
assert mode == kwargs["json"]["method"]
@ -142,7 +145,7 @@ def test_post_transaction_invalid_mode(b):
)
tx = b.validate_transaction(tx)
with pytest.raises(ValidationError):
b.write_transaction(tx, "nope")
ABCI_RPC().write_transaction(MODE_LIST, b.tendermint_rpc_endpoint, MODE_COMMIT, tx, "nope")
@pytest.mark.bdb

View File

@ -6,6 +6,7 @@
import pytest
import codecs
from planetmint.abci.rpc import ABCI_RPC, MODE_LIST, MODE_COMMIT
from planetmint.abci.tendermint_utils import public_key_to_base64
from transactions.types.elections.validator_election import ValidatorElection
@ -243,13 +244,13 @@ def test_upsert_validator(b, node_key, node_keys, ed25519_node_keys):
election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign(
[node_key.private_key]
)
code, message = b.write_transaction(election, BROADCAST_TX_COMMIT)
code, message = ABCI_RPC().write_transaction(MODE_LIST, b.tendermint_rpc_endpoint, MODE_COMMIT, election, BROADCAST_TX_COMMIT)
assert code == 202
assert b.get_transaction(election.id)
tx_vote = gen_vote(election, 0, ed25519_node_keys)
assert b.validate_transaction(tx_vote)
code, message = b.write_transaction(tx_vote, BROADCAST_TX_COMMIT)
code, message = ABCI_RPC().write_transaction(MODE_LIST, b.tendermint_rpc_endpoint, MODE_COMMIT, tx_vote, BROADCAST_TX_COMMIT)
assert code == 202
resp = b.get_validators()

View File

@ -8,6 +8,8 @@ import base64
import random
from functools import singledispatch
from planetmint.abci.rpc import ABCI_RPC
from planetmint.backend.localmongodb.connection import LocalMongoDBConnection
from planetmint.backend.tarantool.connection import TarantoolDBConnection
from planetmint.backend.schema import TABLES
@ -17,7 +19,7 @@ from transactions.types.assets.create import Create
from transactions.types.elections.vote import Vote
from transactions.types.elections.validator_utils import election_id_to_public_key
from planetmint.abci.tendermint_utils import key_to_base64
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
@singledispatch
def flush_db(connection, dbname):
@ -44,7 +46,7 @@ def generate_block(planet):
[alice.private_key]
)
code, message = planet.write_transaction(tx, BROADCAST_TX_COMMIT)
code, message = ABCI_RPC().write_transaction(MODE_LIST, planet.tendermint_rpc_endpoint, MODE_COMMIT, tx, BROADCAST_TX_COMMIT)
assert code == 202