Merge branch 'main' into the-federation-layer-v0.6.0

This commit is contained in:
Reckless_Satoshi
2024-01-10 14:50:07 +00:00
committed by GitHub
12 changed files with 130 additions and 58 deletions

View File

@ -108,7 +108,7 @@ MAX_PUBLIC_ORDERS = 100
# Minimum order size (must be bigger than DB constrain in /robosats/settings.py MIN_TRADE, currently 20_000 Sats) # Minimum order size (must be bigger than DB constrain in /robosats/settings.py MIN_TRADE, currently 20_000 Sats)
MIN_ORDER_SIZE = 20000 MIN_ORDER_SIZE = 20000
# Minimum order size (must be smaller than DB constrain in /robosats/settings.py MAX_TRADE, currently 5_000_000 Sats) # Minimum order size (must be smaller than DB constrain in /robosats/settings.py MAX_TRADE, currently 5_000_000 Sats)
MAX_ORDER_SIZE = 5000000 MAX_ORDER_SIZE = 500000
# For CLTV_expiry calculation # For CLTV_expiry calculation
# Assume 8 min/block assumed # Assume 8 min/block assumed

View File

@ -20,9 +20,9 @@ jobs:
strategy: strategy:
max-parallel: 2 max-parallel: 2
matrix: matrix:
python-tag: ['3.11.6-slim-bookworm', '3.12-slim-bookworm'] python-tag: ['3.11.6-slim-bookworm', '3.12.1-slim-bookworm']
lnd-version: ['v0.17.0-beta', 'v0.17.2-beta.rc1'] lnd-version: ['v0.17.3-beta']
cln-version: ['v23.08.1'] cln-version: ['v23.11.2']
ln-vendor: ['LND'] #, 'CLN'] ln-vendor: ['LND'] #, 'CLN']
steps: steps:
@ -37,7 +37,7 @@ jobs:
- uses: satackey/action-docker-layer-caching@v0.0.11 - uses: satackey/action-docker-layer-caching@v0.0.11
continue-on-error: true continue-on-error: true
with: with:
key: coordinator-docker-cache-${{ hashFiles('Dockerfile', 'requirements.txt', 'requirements_dev.txt') }} key: coordinator-docker-cache-${{ hashFiles('Dockerfile', 'requirements.txt', 'requirements_dev.txt') }}-${{ matrix.ln-vendor }}
restore-keys: | restore-keys: |
coordinator-docker-cache- coordinator-docker-cache-
@ -79,5 +79,5 @@ jobs:
- name: 'Upload coverage report' - name: 'Upload coverage report'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: coverage-report-${{ matrix.python-tag }}-${{ matrix.ln-vendor }} name: coverage-report-${{ matrix.python-tag }}-${{ matrix.ln-vendor }}-${{ github.run_id }}
path: htmlcov/ path: htmlcov/

View File

@ -1,6 +1,3 @@
# TODO
# add a hook which automatically generates the OpenApi schema on API changes
# and places them in an appropriate location
exclude: '(api|chat|control)/migrations/.*' exclude: '(api|chat|control)/migrations/.*'
repos: repos:
- repo: 'https://github.com/pre-commit/pre-commit-hooks' - repo: 'https://github.com/pre-commit/pre-commit-hooks'
@ -50,27 +47,20 @@ repos:
files: ^mobile/ files: ^mobile/
types_or: [javascript, jsx, ts, tsx, css, markdown, json] # uses https://github.com/pre-commit/identify types_or: [javascript, jsx, ts, tsx, css, markdown, json] # uses https://github.com/pre-commit/identify
entry: bash -c 'cd mobile && npm run format' entry: bash -c 'cd mobile && npm run format'
- id: isort - repo: https://github.com/astral-sh/ruff-pre-commit
name: isort rev: v0.1.11
stages: hooks:
- commit - id: ruff
- merge-commit stages:
language: system - commit
types: [python] - merge-commit
entry: isort language: system
- id: black args: [ --fix ]
name: black types: [python]
stages: - id: ruff-format
- commit stages:
- merge-commit - commit
language: system - merge-commit
types: [python] language: system
entry: black types: [python]
- id: flake8
name: flake8
stages:
- commit
- merge-commit
language: system
types: [python]
entry: flake8

View File

@ -9,7 +9,7 @@ from django.utils import timezone
from api.lightning.node import LNNode from api.lightning.node import LNNode
from api.models import Currency, LNPayment, MarketTick, OnchainPayment, Order from api.models import Currency, LNPayment, MarketTick, OnchainPayment, Order
from api.tasks import send_devfund_donation, send_notification from api.tasks import send_devfund_donation, send_notification
from api.utils import validate_onchain_address from api.utils import get_minning_fee, validate_onchain_address
from chat.models import Message from chat.models import Message
FEE = float(config("FEE")) FEE = float(config("FEE"))
@ -19,7 +19,7 @@ ESCROW_USERNAME = config("ESCROW_USERNAME")
PENALTY_TIMEOUT = int(config("PENALTY_TIMEOUT")) PENALTY_TIMEOUT = int(config("PENALTY_TIMEOUT"))
MIN_ORDER_SIZE = config("MIN_ORDER_SIZE", cast=int, default=20_000) MIN_ORDER_SIZE = config("MIN_ORDER_SIZE", cast=int, default=20_000)
MAX_ORDER_SIZE = config("MAX_ORDER_SIZE", cast=int, default=5_000_000) MAX_ORDER_SIZE = config("MAX_ORDER_SIZE", cast=int, default=500_000)
EXP_MAKER_BOND_INVOICE = int(config("EXP_MAKER_BOND_INVOICE")) EXP_MAKER_BOND_INVOICE = int(config("EXP_MAKER_BOND_INVOICE"))
EXP_TAKER_BOND_INVOICE = int(config("EXP_TAKER_BOND_INVOICE")) EXP_TAKER_BOND_INVOICE = int(config("EXP_TAKER_BOND_INVOICE"))
@ -640,10 +640,7 @@ class Logics:
): # Not enough onchain balance to commit for this swap. ): # Not enough onchain balance to commit for this swap.
return False return False
suggested_mining_fee_rate = LNNode.estimate_fee( suggested_mining_fee_rate = get_minning_fee("suggested", preliminary_amount)
amount_sats=preliminary_amount,
target_conf=config("SUGGESTED_TARGET_CONF", cast=int, default=2),
)["mining_fee_rate"]
# Hardcap mining fee suggested at 1000 sats/vbyte # Hardcap mining fee suggested at 1000 sats/vbyte
if suggested_mining_fee_rate > 1000: if suggested_mining_fee_rate > 1000:
@ -795,10 +792,7 @@ class Logics:
num_satoshis = cls.payout_amount(order, user)[1]["invoice_amount"] num_satoshis = cls.payout_amount(order, user)[1]["invoice_amount"]
if mining_fee_rate: if mining_fee_rate:
# not a valid mining fee # not a valid mining fee
min_mining_fee_rate = LNNode.estimate_fee( min_mining_fee_rate = get_minning_fee("minimum", num_satoshis)
amount_sats=num_satoshis,
target_conf=config("MINIMUM_TARGET_CONF", cast=int, default=24),
)["mining_fee_rate"]
min_mining_fee_rate = max(2, min_mining_fee_rate) min_mining_fee_rate = max(2, min_mining_fee_rate)

View File

@ -11,7 +11,6 @@ from api.utils import get_session
class Command(BaseCommand): class Command(BaseCommand):
help = "Polls telegram /getUpdates method" help = "Polls telegram /getUpdates method"
rest = 3 # seconds between consecutive polls rest = 3 # seconds between consecutive polls

View File

@ -6,7 +6,6 @@ from django.utils import timezone
class Currency(models.Model): class Currency(models.Model):
with open("frontend/static/assets/currencies.json") as f: with open("frontend/static/assets/currencies.json") as f:
currency_dict = json.load(f) currency_dict = json.load(f)
currency_choices = [(int(val), label) for val, label in list(currency_dict.items())] currency_choices = [(int(val), label) for val, label in list(currency_dict.items())]

View File

@ -98,15 +98,9 @@ def send_devfund_donation(order_id, proceeds, reason):
from api.lightning.node import LNNode from api.lightning.node import LNNode
from api.models import LNPayment, Order from api.models import LNPayment, Order
from api.utils import get_devfund_pubkey
if config("NETWORK", cast=str) == "testnet": target_pubkey = get_devfund_pubkey(config("NETWORK", cast=str))
target_pubkey = (
"03ecb271b3e2e36f2b91c92c65bab665e5165f8cdfdada1b5f46cfdd3248c87fd6"
)
else:
target_pubkey = (
"02187352cc4b1856b9604e0a79e1bc9b301be7e0c14acbbb8c29f7051d507127d7"
)
order = Order.objects.get(id=order_id) order = Order.objects.get(id=order_id)
coordinator_alias = config("COORDINATOR_ALIAS", cast=str, default="NoAlias") coordinator_alias = config("COORDINATOR_ALIAS", cast=str, default="NoAlias")

View File

@ -70,6 +70,96 @@ def validate_onchain_address(address):
return True, None return True, None
mining_fee = {}
@ring.dict(mining_fee, expire=60) # keeps in cache for 60 seconds
def get_minning_fee(priority: str, preliminary_amount: int) -> int:
"""
priority: (str) 'suggested' | 'minimum'
Fetches suggested and minimum mining fee rates from mempool.space
uses LND/CLN fee estimator as fallback.
mempool.space response object:
{
fastestFee: 1,
halfHourFee: 1,
hourFee: 1,
economyFee: 1,
minimumFee: 1
}
Where 'suggested' is 'fastestFee' and 'minimum' is 'economyFee'
"""
from api.lightning.node import LNNode
session = get_session()
mempool_url = (
"http://mempoolhqx4isw62xs7abwphsq7ldayuidyx2v2oethdhhj6mlo2r6ad.onion"
if USE_TOR
else "https://mempool.space"
)
api_path = "/api/v1/fees/recommended"
try:
response = session.get(mempool_url + api_path)
response.raise_for_status() # Raises stored HTTPError, if one occurred
data = response.json()
if priority == "suggested":
value = data.get("fastestFee")
elif priority == "minimum":
value = data.get("economyFee")
else:
raise Exception(
"an error occurred",
"unexpected value for mining fee priority",
priority,
)
except Exception:
# Fetch mining fee from LND/CLN instance
if priority == "suggested":
target_conf = config("SUGGESTED_TARGET_CONF", cast=int, default=2)
if priority == "minimum":
target_conf = config("MINIMUM_TARGET_CONF", cast=int, default=24)
value = LNNode.estimate_fee(
amount_sats=preliminary_amount,
target_conf=target_conf,
)["mining_fee_rate"]
return value
devfund_pubkey = {}
@ring.dict(devfund_pubkey, expire=3600) # keeps in cache for 3600 seconds
def get_devfund_pubkey(network: str) -> str:
"""
network: (str) "mainnet" | "testnet";
Fetches devfund pubkey from `main` branch in the repository
fallback to hardcoded pubkey
"""
session = get_session()
url = "https://raw.githubusercontent.com/RoboSats/robosats/main/devfund_pubey.json"
try:
response = session.get(url)
response.raise_for_status() # Raises stored HTTPError, if one occurred
value = response.json().get(network)
if len(value) != 66:
raise Exception()
except Exception as e:
print(e)
with open("devfund_pubkey.json", "r") as f:
data = json.load(f)
value = data.get(network)
return value
market_cache = {} market_cache = {}

View File

@ -913,7 +913,7 @@ class LimitView(ListAPIView):
def get(self, request): def get(self, request):
# Trade limits as BTC # Trade limits as BTC
min_trade = config("MIN_ORDER_SIZE", cast=int, default=20_000) / 100_000_000 min_trade = config("MIN_ORDER_SIZE", cast=int, default=20_000) / 100_000_000
max_trade = config("MAX_ORDER_SIZE", cast=int, default=5_000_000) / 100_000_000 max_trade = config("MAX_ORDER_SIZE", cast=int, default=500_000) / 100_000_000
payload = {} payload = {}
queryset = Currency.objects.all().order_by("currency") queryset = Currency.objects.all().order_by("currency")

View File

@ -8,7 +8,6 @@ from control.models import AccountingDay, BalanceLog
@admin.register(AccountingDay) @admin.register(AccountingDay)
class AccountingDayAdmin(ImportExportModelAdmin): class AccountingDayAdmin(ImportExportModelAdmin):
list_display = ( list_display = (
"day", "day",
"contracted", "contracted",
@ -34,7 +33,6 @@ class AccountingDayAdmin(ImportExportModelAdmin):
@admin.register(BalanceLog) @admin.register(BalanceLog)
class BalanceLogAdmin(ImportExportModelAdmin): class BalanceLogAdmin(ImportExportModelAdmin):
list_display = ( list_display = (
"time", "time",
"total", "total",

4
devfund_pubkey.json Normal file
View File

@ -0,0 +1,4 @@
{
"mainnet": "02187352cc4b1856b9604e0a79e1bc9b301be7e0c14acbbb8c29f7051d507127d7",
"testnet": "03ecb271b3e2e36f2b91c92c65bab665e5165f8cdfdada1b5f46cfdd3248c87fd6"
}

View File

@ -1,5 +1,9 @@
[tool.isort] [tool.ruff]
profile = "black" # Exclude a variety of commonly ignored directories.
exclude = [
"*migrations/*",
"api/nick_generator/nick_generator.py",
]
[tool.coverage.run] [tool.coverage.run]
omit = [ omit = [