Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
55ac5b5
fix(nginx): import correct resource, also improve Makefile
jansegre Jun 25, 2021
a4c651e
Merge pull request #246 from HathorNetwork/fix/nginx-docker-build
jansegre Jun 28, 2021
7a39a5f
fix(tx): Fix duplicate entries in metadata.conflict_with
msbrogli Jun 30, 2021
2464456
Merge branch 'fix/tx-meta-duplicate-conflict-with' into dev
msbrogli Jul 1, 2021
e805cd1
feat(api): add API for mempool (#241)
jansegre Jul 1, 2021
3a0d3fe
feat(profile): Add reset option to profiler
msbrogli Jul 1, 2021
0120337
Merge branch 'feat/profile-reset' into dev
msbrogli Jul 2, 2021
7f8d8b6
fix(init): Remove voided tx tips from indexes
msbrogli Jul 1, 2021
f7f81f7
Merge branch 'fix/init-voided-tx-tips' into dev
msbrogli Jul 2, 2021
c5451a3
fix(tests): pytest warnings when collecting tests
jansegre Jul 1, 2021
3092c1a
Merge pull request #255 from HathorNetwork/fix/test-collection-warnings
jansegre Jul 2, 2021
4d25b8c
chore(ci): run tests on Windows, do not continue on error
jansegre Jul 1, 2021
81acba9
Merge pull request #256 from HathorNetwork/chore/tests-on-windows
jansegre Jul 3, 2021
ccdb072
chore(ci): use docker cache from master instead of dev
jansegre Jul 2, 2021
bab424e
Merge pull request #259 from HathorNetwork/chore/fix-docker-cache
jansegre Jul 3, 2021
0b9c203
chore(ci): dropping pypy-3.6 because image is no longe provided
jansegre Jul 3, 2021
4f0b20b
Merge pull request #261 from HathorNetwork/chore/fix-docker-pypy
jansegre Jul 3, 2021
072a273
chore: bump version
jansegre Jul 2, 2021
084021f
Merge pull request #257 from HathorNetwork/chore/v0.40.1
jansegre Jul 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions extras/nginx_docker/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ tag = 769498303037.dkr.ecr.us-east-1.amazonaws.com/webtank:latest
docker: nginx.conf set_real_ip_from_cloudfront
docker buildx build --pull --push --platform linux/arm64/v8,linux/amd64 --tag $(tag) .


nginx.conf: export PYTHONPATH := ../..
nginx.conf:
python -m hathor generate_nginx_config nginx.conf
@python -c "import os; import hathor; print('Using hathor-core from:', os.path.dirname(hathor.__file__))"
python -m hathor generate_nginx_config - > $@

set_real_ip_from_cloudfront:
curl https://ip-ranges.amazonaws.com/ip-ranges.json -s \
Expand Down
4 changes: 3 additions & 1 deletion hathor/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ def execute_from_command_line(self):
if json_logs:
sys.argv.remove('--json-logs')

setup_logging(debug, capture_stdout, json_logs)
sentry = '--sentry-dsn' in sys.argv

setup_logging(debug, capture_stdout, json_logs, sentry=sentry)
module.main()


Expand Down
16 changes: 8 additions & 8 deletions hathor/cli/openapi_files/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ def register_resource(resource_class: Resource) -> Resource:
def get_registered_resources() -> List[Resource]:
""" Returns a list with all the resources registered for the docs
"""
from hathor.p2p.resources import __all__ # noqa: 401
from hathor.resources import ProfilerResource # noqa: 401
from hathor.stratum.resources import MiningStatsResource # noqa: 401
from hathor.transaction.resources import __all__ # noqa: 401
from hathor.version_resource import VersionResource # noqa: 401
from hathor.wallet.resources.nano_contracts import __all__ # noqa: 401
from hathor.wallet.resources.thin_wallet import __all__ # noqa: 401
from hathor.websocket import WebsocketStatsResource # noqa: 401
import hathor.p2p.resources # noqa: 401
import hathor.profiler.resources # noqa: 401
import hathor.stratum.resources # noqa: 401
import hathor.transaction.resources # noqa: 401
import hathor.version_resource # noqa: 401
import hathor.wallet.resources.nano_contracts # noqa: 401
import hathor.wallet.resources.thin_wallet # noqa: 401
import hathor.websocket # noqa: 401
global _registered_resources
return _registered_resources
2 changes: 2 additions & 0 deletions hathor/cli/run_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ def register_resources(self, args: Namespace) -> None:
GetBlockTemplateResource,
GraphvizFullResource,
GraphvizNeighboursResource,
MempoolResource,
PushTxResource,
SubmitBlockResource,
TransactionAccWeightResource,
Expand Down Expand Up @@ -380,6 +381,7 @@ def register_resources(self, args: Namespace) -> None:
(b'dashboard_tx', DashboardTransactionResource(self.manager), root),
(b'profiler', ProfilerResource(self.manager), root),
(b'top', CPUProfilerResource(self.manager, cpu), root),
(b'mempool', MempoolResource(self.manager), root),
# mining
(b'mining', MiningResource(self.manager), root),
(b'getmininginfo', MiningInfoResource(self.manager), root),
Expand Down
6 changes: 2 additions & 4 deletions hathor/cli/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def setup_logging(
capture_stdout: bool = False,
json_logging: bool = False,
*,
sentry: bool = False,
_test_logging: bool = False,
) -> None:
import logging
Expand Down Expand Up @@ -219,11 +220,8 @@ def kwargs_formatter(_, __, event_dict):
structlog.stdlib.add_log_level,
]

try:
if sentry:
from structlog_sentry import SentryProcessor
except ModuleNotFoundError:
pass
else:
processors.append(SentryProcessor(level=logging.ERROR))

processors.extend([
Expand Down
3 changes: 3 additions & 0 deletions hathor/conf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@ def MAXIMUM_NUMBER_OF_HALVINGS(self) -> int:
# As a normal tx has ~2-4 inputs and 2 outputs, I would say the maximum should be 150*6 = 900 elements
MAX_INPUTS_OUTPUTS_ADDRESS_HISTORY: int = 6*MAX_TX_ADDRESSES_HISTORY

# Maximum number of TXs that will be sent by the Mempool API.
MEMPOOL_API_TX_LIMIT: int = 100

# Multiplier coefficient to adjust the minimum weight of a normal tx to 18
MIN_TX_WEIGHT_COEFFICIENT: float = 1.6

Expand Down
8 changes: 5 additions & 3 deletions hathor/consensus.py
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ def mark_input_as_used(self, tx: Transaction, txin: TxInput) -> None:

spent_tx = tx.storage.get_transaction(txin.tx_id)
spent_meta = spent_tx.get_metadata()
spent_by = spent_meta.spent_outputs[txin.index] # Set[bytes(hash)]
spent_by = spent_meta.spent_outputs[txin.index]
assert tx.hash not in spent_by

# Update our meta.conflict_with.
Expand All @@ -616,7 +616,7 @@ def mark_input_as_used(self, tx: Transaction, txin: TxInput) -> None:
else:
meta.voided_by.add(tx.hash)
if meta.conflict_with:
meta.conflict_with.extend(spent_by)
meta.conflict_with.extend(set(spent_by) - set(meta.conflict_with))
else:
meta.conflict_with = spent_by.copy()
tx.storage.save_transaction(tx, only_metadata=True)
Expand All @@ -626,7 +626,9 @@ def mark_input_as_used(self, tx: Transaction, txin: TxInput) -> None:
conflict_tx = tx.storage.get_transaction(h)
tx_meta = conflict_tx.get_metadata()
if tx_meta.conflict_with:
tx_meta.conflict_with.append(tx.hash)
if tx.hash not in tx_meta.conflict_with:
# We could use a set instead of a list but it consumes ~2.15 times more of memory.
tx_meta.conflict_with.append(tx.hash)
else:
tx_meta.conflict_with = [tx.hash]
tx.storage.save_transaction(conflict_tx, only_metadata=True)
Expand Down
2 changes: 2 additions & 0 deletions hathor/daa.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@


class TestMode(IntFlag):
__test__ = False

DISABLED = 0
TEST_TX_WEIGHT = 1
TEST_BLOCK_WEIGHT = 2
Expand Down
6 changes: 4 additions & 2 deletions hathor/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,11 @@ def do_discovery(self) -> None:
for peer_discovery in self.peer_discoveries:
peer_discovery.discover_and_connect(self.connections.connect_to)

def start_profiler(self) -> None:
def start_profiler(self, *, reset: bool = False) -> None:
"""
Start profiler. It can be activated from a web resource, as well.
"""
if not self.profiler:
if reset or not self.profiler:
import cProfile
self.profiler = cProfile.Profile()
self.profiler.enable()
Expand Down Expand Up @@ -408,6 +408,8 @@ def _initialize_components(self) -> None:
elif tx.is_block:
self.tx_storage.add_to_parent_blocks_index(tx.hash)
self.tx_storage.add_to_indexes(tx)
if tx.is_transaction and tx_meta.voided_by:
self.tx_storage.del_from_indexes(tx)
except (InvalidNewTransaction, TxValidationError):
self.log.error('unexpected error when initializing', tx=tx, exc_info=True)
raise
Expand Down
12 changes: 11 additions & 1 deletion hathor/profiler/resources/profiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ def render_POST(self, request):
ret = {'success': True}

if 'start' in post_data:
self.manager.start_profiler()
reset = False
if 'reset' in post_data:
reset = True
self.manager.start_profiler(reset=reset)

elif 'stop' in post_data:
if 'filepath' in post_data:
Expand Down Expand Up @@ -101,6 +104,13 @@ def render_OPTIONS(self, request: Request) -> int:
'start': True
}
},
'start-reset': {
'summary': 'Start profiler',
'value': {
'start': True,
'reset': True
}
},
'stop': {
'summary': 'Stop profiler',
'value': {
Expand Down
2 changes: 2 additions & 0 deletions hathor/transaction/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from hathor.transaction.resources.dashboard import DashboardTransactionResource
from hathor.transaction.resources.decode_tx import DecodeTxResource
from hathor.transaction.resources.graphviz import GraphvizFullResource, GraphvizNeighboursResource
from hathor.transaction.resources.mempool import MempoolResource
from hathor.transaction.resources.mining import GetBlockTemplateResource, SubmitBlockResource
from hathor.transaction.resources.push_tx import PushTxResource
from hathor.transaction.resources.transaction import TransactionResource
Expand All @@ -38,4 +39,5 @@
'DashboardTransactionResource',
'TxParentsResource',
'ValidateAddressResource',
'MempoolResource',
]
113 changes: 113 additions & 0 deletions hathor/transaction/resources/mempool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Copyright 2021 Hathor Labs
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json
from itertools import islice
from typing import TYPE_CHECKING

from twisted.web import resource

from hathor.api_util import set_cors
from hathor.cli.openapi_files.register import register_resource
from hathor.conf import HathorSettings

settings = HathorSettings()

if TYPE_CHECKING:
from twisted.web.http import Request

from hathor.manager import HathorManager


@register_resource
class MempoolResource(resource.Resource):
""" Implements a web server API to return transactions on the mempool.

You must run with option `--status <PORT>`.
"""
isLeaf = True

def __init__(self, manager: 'HathorManager'):
# Important to have the manager so we can know the tx_storage
self.manager = manager

def render_GET(self, request: 'Request') -> bytes:
""" Get request /mempool/ that returns transactions on the mempool

:rtype: string (json list)
"""
request.setHeader(b'content-type', b'application/json; charset=utf-8')
set_cors(request, 'GET')

# Get a list of all txs on the mempool
tx_ids = map(
# get only tx_ids
lambda tx: tx.hash_hex,
# order by timestamp
sorted(list(self.manager.tx_storage.iter_mempool()), key=lambda tx: tx.timestamp),
)
# Only return up to settings.MEMPOOL_API_TX_LIMIT txs per call (default: 100)
data = {'success': True, 'transactions': list(islice(tx_ids, settings.MEMPOOL_API_TX_LIMIT))}
return json.dumps(data, indent=4).encode('utf-8')


MempoolResource.openapi = {
'/mempool': {
'x-visibility': 'public',
'x-rate-limit': {
'global': [
{
'rate': '50r/s',
'burst': 100,
'delay': 50
}
],
'per-ip': [
{
'rate': '1r/s',
'burst': 5,
'delay': 3
}
]
},
'get': {
'tags': ['mempool'],
'operationId': 'mempool',
'summary': 'List of mempool transactions',
'description': 'Returns a list of all transactions currently on the mempool',
'responses': {
'200': {
'description': 'Success',
'content': {
'application/json': {
'examples': {
'success': {
'summary': 'Success',
'value': {
'success': True,
'transactions': [
'339f47da87435842b0b1b528ecd9eac2495ce983b3e9c923a37e1befbe12c792',
'16ba3dbe424c443e571b00840ca54b9ff4cff467e10b6a15536e718e2008f952',
'33e14cb555a96967841dcbe0f95e9eab5810481d01de8f4f73afb8cce365e869',
],
},
},
},
},
}
}
}
}
}
}
3 changes: 2 additions & 1 deletion hathor/transaction/storage/transaction_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,10 @@ def iter_mempool(self) -> Iterator[Transaction]:
bfs = BFSWalk(self, is_dag_verifications=True, is_left_to_right=False)
for tx in bfs.run(map(self.get_transaction, self._tx_tips_index), skip_root=False):
assert isinstance(tx, Transaction)
yield tx
if tx.get_metadata().first_block is not None:
bfs.skip_neighbors(tx)
else:
yield tx

def update_tx_tips(self, tx: BaseTransaction) -> None:
"""
Expand Down
6 changes: 3 additions & 3 deletions hathor/transaction/transaction_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ def to_json(self) -> Dict[str, Any]:
data['spent_outputs'].append([idx, [h_bytes.hex() for h_bytes in hashes]])
data['received_by'] = list(self.received_by)
data['children'] = [x.hex() for x in self.children]
data['conflict_with'] = [x.hex() for x in self.conflict_with] if self.conflict_with else []
data['conflict_with'] = [x.hex() for x in set(self.conflict_with)] if self.conflict_with else []
data['voided_by'] = [x.hex() for x in self.voided_by] if self.voided_by else []
data['twins'] = [x.hex() for x in self.twins]
data['accumulated_weight'] = self.accumulated_weight
Expand All @@ -285,8 +285,8 @@ def create_from_json(cls, data: Dict[str, Any]) -> 'TransactionMetadata':
meta.received_by = list(data['received_by'])
meta.children = [bytes.fromhex(h) for h in data['children']]

if 'conflict_with' in data:
meta.conflict_with = [bytes.fromhex(h) for h in data['conflict_with']] if data['conflict_with'] else None
if 'conflict_with' in data and data['conflict_with']:
meta.conflict_with = [bytes.fromhex(h) for h in set(data['conflict_with'])]
else:
meta.conflict_with = None

Expand Down
2 changes: 2 additions & 0 deletions hathor/websocket/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ def serialize_message_data(self, event, args):
tx = data['tx']
data = tx.to_json_extended()
data['is_block'] = tx.is_block
# needed to check if the transaction is from the mempool
data['first_block'] = tx.get_metadata().first_block
return data
elif event == HathorEvents.WALLET_BALANCE_UPDATED:
data['balance'] = data['balance'][settings.HATHOR_TOKEN_UID]._asdict()
Expand Down
Loading