Implement node settings (#1884)

* Implement node settings class

* Fix price policy for zero price

* Remove old comment

* Remove old comments

* Remove old comments
This commit is contained in:
Jonathan Zernik 2021-12-23 08:33:40 -08:00 committed by GitHub
parent d4a1843e30
commit c2aeb344bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 160 additions and 60 deletions

View file

@ -0,0 +1,64 @@
# MIT License
#
# Copyright (c) 2020 Jonathan Zernik
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import logging
from typing import Optional
from squeaknode.core.user_config import UserConfig
logger = logging.getLogger(__name__)
NODE_SETTINGS_USERNAME = "default"
class NodeSettings:
def __init__(self, squeak_db):
self.squeak_db = squeak_db
self.username = NODE_SETTINGS_USERNAME
def insert_user_config(self) -> Optional[str]:
user_config = UserConfig(username=self.username)
return self.squeak_db.insert_config(user_config)
def set_sell_price_msat(self, sell_price_msat: int) -> None:
self.insert_user_config()
if sell_price_msat < 0:
raise Exception("Sell price cannot be negative.")
self.squeak_db.set_config_sell_price_msat(
username=self.username,
sell_price_msat=sell_price_msat,
)
def clear_sell_price_msat(self) -> None:
self.insert_user_config()
self.squeak_db.clear_config_sell_price_msat(
username=self.username,
)
def get_sell_price_msat(self) -> Optional[int]:
user_config = self.squeak_db.get_config(
username=self.username,
)
if user_config is None:
return None
return user_config.sell_price_msat

View file

@ -27,8 +27,8 @@ from squeak.core import CSqueak
from squeaknode.config.config import SqueaknodeConfig
from squeaknode.core.peer_address import PeerAddress
from squeaknode.core.squeak_peer import SqueakPeer
from squeaknode.core.user_config import UserConfig
from squeaknode.db.squeak_db import SqueakDb
from squeaknode.node.node_settings import NodeSettings
logger = logging.getLogger(__name__)
@ -36,9 +36,10 @@ logger = logging.getLogger(__name__)
class PricePolicy:
def __init__(self, squeak_db: SqueakDb, config: SqueaknodeConfig):
def __init__(self, squeak_db: SqueakDb, config: SqueaknodeConfig, node_settings: NodeSettings):
self.squeak_db = squeak_db
self.config = config
self.node_settings = node_settings
def get_price(self, squeak: CSqueak, peer_address: PeerAddress) -> int:
"""Get the price to sell this squeak to this peer.
@ -48,11 +49,10 @@ class PricePolicy:
peer = self.get_peer(peer_address)
if peer is not None and peer.share_for_free:
return 0
# Return sell price from settings if configured
sell_price = self.get_sell_price_msat()
if sell_price is not None:
return sell_price
return self.get_default_price()
sell_price_msat = self.get_sell_price_msat()
if sell_price_msat is None:
return self.get_default_price()
return sell_price_msat
def get_peer(self, peer_address: PeerAddress) -> Optional[SqueakPeer]:
return self.squeak_db.get_peer_by_address(peer_address)
@ -60,13 +60,5 @@ class PricePolicy:
def get_default_price(self) -> int:
return self.config.node.price_msat
def get_user_config(self) -> Optional[UserConfig]:
return self.squeak_db.get_config(
username=self.config.webadmin.username,
)
def get_sell_price_msat(self) -> Optional[int]:
user_config = self.get_user_config()
if user_config is None:
return None
return user_config.sell_price_msat
return self.node_settings.get_sell_price_msat()

View file

@ -87,6 +87,7 @@ class SqueakController:
network_manager,
download_manager,
tweet_forwarder,
node_settings,
config,
):
self.squeak_db = squeak_db
@ -100,6 +101,7 @@ class SqueakController:
self.twitter_stream_change_listener = EventListener()
self.active_download_manager = download_manager
self.tweet_forwarder = tweet_forwarder
self.node_settings = node_settings
self.config = config
def save_squeak(self, squeak: CSqueak) -> Optional[bytes]:
@ -253,7 +255,8 @@ class SqueakController:
return sent_offer
def get_price_for_squeak(self, squeak: CSqueak, peer_address: PeerAddress) -> int:
price_policy = PricePolicy(self.squeak_db, self.config)
price_policy = PricePolicy(
self.squeak_db, self.config, self.node_settings)
return price_policy.get_price(squeak, peer_address)
def create_signing_profile(self, profile_name: str) -> int:
@ -868,27 +871,13 @@ class SqueakController:
return self.squeak_db.insert_config(user_config)
def set_sell_price_msat(self, sell_price_msat: int) -> None:
self.insert_user_config()
if sell_price_msat < 0:
raise Exception("Sell price cannot be negative.")
self.squeak_db.set_config_sell_price_msat(
username=self.config.webadmin.username,
sell_price_msat=sell_price_msat,
)
self.node_settings.set_sell_price_msat(sell_price_msat)
def clear_sell_price_msat(self) -> None:
self.insert_user_config()
self.squeak_db.clear_config_sell_price_msat(
username=self.config.webadmin.username,
)
self.node_settings.clear_sell_price_msat()
def get_sell_price_msat(self) -> Optional[int]:
user_config = self.squeak_db.get_config(
username=self.config.webadmin.username,
)
if user_config is None:
return None
return user_config.sell_price_msat
return self.node_settings.get_sell_price_msat()
def get_default_sell_price_msat(self) -> int:
return self.config.node.price_msat

View file

@ -36,6 +36,7 @@ from squeaknode.db.squeak_db import SqueakDb
from squeaknode.lightning.lnd_lightning_client import LNDLightningClient
from squeaknode.network.network_manager import NetworkManager
from squeaknode.node.active_download_manager import ActiveDownloadManager
from squeaknode.node.node_settings import NodeSettings
from squeaknode.node.payment_processor import PaymentProcessor
from squeaknode.node.peer_connection_worker import PeerConnectionWorker
from squeaknode.node.peer_subscription_update_worker import PeerSubscriptionUpdateWorker
@ -61,6 +62,7 @@ class SqueakNode:
def _initialize(self):
self.initialize_network()
self.initialize_db()
self.initialize_node_settings()
self.initialize_lightning_client()
self.initialize_bitcoin_client()
self.initialize_bitcoin_block_subscription_client()
@ -132,6 +134,9 @@ class SqueakNode:
self.squeak_db = SqueakDb(engine)
self.squeak_db.init_with_retries()
def initialize_node_settings(self):
self.node_settings = NodeSettings(self.squeak_db)
def initialize_lightning_client(self):
# load the lightning client
self.lightning_client = LNDLightningClient(
@ -188,6 +193,7 @@ class SqueakNode:
self.network_manager,
self.download_manager,
self.twitter_forwarder,
self.node_settings,
self.config,
)

View file

@ -0,0 +1,58 @@
# MIT License
#
# Copyright (c) 2020 Jonathan Zernik
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import pytest
from sqlalchemy import create_engine
from squeaknode.db.squeak_db import SqueakDb
from squeaknode.node.node_settings import NodeSettings
@pytest.fixture
def db_engine():
yield create_engine('sqlite://')
@pytest.fixture
def squeak_db(db_engine):
db = SqueakDb(db_engine)
db.init()
yield db
@pytest.fixture
def node_settings(
squeak_db,
):
return NodeSettings(squeak_db)
def test_default_set_sell_price(node_settings):
retrieved_sell_price_msat = node_settings.get_sell_price_msat()
assert retrieved_sell_price_msat is None
def test_set_sell_price(node_settings):
node_settings.set_sell_price_msat(666)
retrieved_sell_price_msat = node_settings.get_sell_price_msat()
assert retrieved_sell_price_msat == 666

View file

@ -27,54 +27,35 @@ from squeaknode.node.price_policy import PricePolicy
@pytest.fixture()
def price_policy():
yield PricePolicy(None, None)
yield PricePolicy(None, None, None)
def test_get_price(price_policy, squeak, peer_address, user_config):
with mock.patch.object(price_policy, 'get_peer', autospec=True) as mock_get_peer, \
mock.patch.object(price_policy, 'get_user_config', autospec=True) as mock_get_user_config, \
mock.patch.object(price_policy, 'get_default_price', autospec=True) as mock_get_default_price:
mock.patch.object(price_policy, 'get_sell_price_msat', autospec=True) as mock_get_sell_price_msat:
mock_get_peer.return_value = None
mock_get_user_config.return_value = user_config
mock_get_default_price.return_value = 555
mock_get_sell_price_msat.return_value = 555
assert price_policy.get_price(squeak, peer_address) == 555
def test_get_price_profile_share_free_peer(price_policy, squeak, peer_address, peer, user_config):
with mock.patch.object(price_policy, 'get_peer', autospec=True) as mock_get_peer, \
mock.patch.object(price_policy, 'get_user_config', autospec=True) as mock_get_user_config, \
mock.patch.object(price_policy, 'get_default_price', autospec=True) as mock_get_default_price:
mock.patch.object(price_policy, 'get_sell_price_msat', autospec=True) as mock_get_sell_price_msat:
mock_get_peer.return_value = peer._replace(
share_for_free=True,
)
mock_get_user_config.return_value = user_config
mock_get_default_price.return_value = 555
mock_get_sell_price_msat.return_value = 555
assert price_policy.get_price(squeak, peer_address) == 0
def test_get_price_profile_no_share_free_peer(price_policy, squeak, peer_address, peer, user_config):
with mock.patch.object(price_policy, 'get_peer', autospec=True) as mock_get_peer, \
mock.patch.object(price_policy, 'get_user_config', autospec=True) as mock_get_user_config, \
mock.patch.object(price_policy, 'get_default_price', autospec=True) as mock_get_default_price:
mock.patch.object(price_policy, 'get_sell_price_msat', autospec=True) as mock_get_sell_price_msat:
mock_get_peer.return_value = peer._replace(
share_for_free=False,
)
mock_get_user_config.return_value = user_config
mock_get_default_price.return_value = 555
mock_get_sell_price_msat.return_value = 555
assert price_policy.get_price(squeak, peer_address) == 555
def test_get_price_sell_price_set(price_policy, squeak, peer_address, user_config):
with mock.patch.object(price_policy, 'get_peer', autospec=True) as mock_get_peer, \
mock.patch.object(price_policy, 'get_user_config', autospec=True) as mock_get_user_config, \
mock.patch.object(price_policy, 'get_default_price', autospec=True) as mock_get_default_price:
mock_get_peer.return_value = None
mock_get_user_config.return_value = user_config._replace(
sell_price_msat=7777,
)
mock_get_default_price.return_value = 555
assert price_policy.get_price(squeak, peer_address) == 7777

View file

@ -31,6 +31,7 @@ from squeaknode.core.squeak_peer import SqueakPeer
from squeaknode.db.squeak_db import SqueakDb
from squeaknode.network.network_manager import NetworkManager
from squeaknode.node.active_download_manager import ActiveDownloadManager
from squeaknode.node.node_settings import NodeSettings
from squeaknode.node.payment_processor import PaymentProcessor
from squeaknode.node.squeak_controller import SqueakController
from squeaknode.twitter.twitter_forwarder import TwitterForwarder
@ -58,6 +59,11 @@ def squeak_db():
return mock.Mock(spec=SqueakDb)
@pytest.fixture
def node_settings():
return mock.Mock(spec=NodeSettings)
@pytest.fixture
def network_manager():
return mock.Mock(spec=NetworkManager)
@ -119,6 +125,7 @@ def squeak_controller(
network_manager,
download_manager,
twitter_forwarder,
node_settings,
config,
):
return SqueakController(
@ -128,6 +135,7 @@ def squeak_controller(
network_manager,
download_manager,
twitter_forwarder,
node_settings,
config,
)
@ -140,6 +148,7 @@ def regtest_squeak_controller(
network_manager,
download_manager,
twitter_forwarder,
node_settings,
regtest_config,
):
return SqueakController(
@ -149,6 +158,7 @@ def regtest_squeak_controller(
network_manager,
download_manager,
twitter_forwarder,
node_settings,
regtest_config,
)