Skip to content

Commit

Permalink
Lite version (#26)
Browse files Browse the repository at this point in the history
* allow to run REST server without postgresql database

* removed default SQL_URI
  • Loading branch information
lAmeR1 authored May 3, 2023
1 parent d59cb9d commit 436c11e
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 22 deletions.
2 changes: 1 addition & 1 deletion dbsession.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

_logger = logging.getLogger(__name__)

engine = create_async_engine(os.getenv("SQL_URI"), pool_pre_ping=True, echo=False)
engine = create_async_engine(os.getenv("SQL_URI", "postgresql+asyncpg://127.0.0.1:5432"), pool_pre_ping=True, echo=False)
Base = declarative_base()

session_maker = sessionmaker(engine)
Expand Down
1 change: 0 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ ARG REPO_DIR
EXPOSE 8000

ENV KASPAD_HOST_1=n.seeder1.kaspad.net:16110
ENV SQL_URI=postgresql+asyncpg://postgres:password@postgresql:5432/postgres
ARG version
ENV VERSION=$version

Expand Down
16 changes: 16 additions & 0 deletions endpoints/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# encoding: utf-8
import os
from functools import wraps

from fastapi import HTTPException


def filter_fields(response_dict, fields):
if fields:
Expand All @@ -7,3 +12,14 @@ def filter_fields(response_dict, fields):
}
else:
return response_dict


def sql_db_only(func):
@wraps(func)
async def wrapper(*args, **kwargs):
if not os.getenv("SQL_URI"):
raise HTTPException(status_code=503, detail="Endpoint not available. "
"This endpoint needs a configured SQL database.")
return await func(*args, **kwargs)

return wrapper
9 changes: 6 additions & 3 deletions endpoints/get_address_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
from pydantic import BaseModel
from sqlalchemy import text, func
from sqlalchemy.future import select
from endpoints.get_transactions import search_for_transactions, TxSearch, TxModel

from dbsession import async_session
from server import app

from endpoints import sql_db_only
from endpoints.get_transactions import search_for_transactions, TxSearch, TxModel
from models.TxAddrMapping import TxAddrMapping
from server import app

DESC_RESOLVE_PARAM = "Use this parameter if you want to fetch the TransactionInput previous outpoint details." \
" Light fetches only the address and amount. Full fetches the whole TransactionOutput and " \
Expand Down Expand Up @@ -43,6 +43,7 @@ class PreviousOutpointLookupMode(str, Enum):
response_model_exclude_unset=True,
tags=["Kaspa addresses"],
deprecated=True)
@sql_db_only
async def get_transactions_for_address(
kaspaAddress: str = Path(
description="Kaspa address as string e.g. "
Expand Down Expand Up @@ -87,6 +88,7 @@ async def get_transactions_for_address(
response_model=List[TxModel],
response_model_exclude_unset=True,
tags=["Kaspa addresses"])
@sql_db_only
async def get_full_transactions_for_address(
kaspaAddress: str = Path(
description="Kaspa address as string e.g. "
Expand Down Expand Up @@ -130,6 +132,7 @@ async def get_full_transactions_for_address(
@app.get("/addresses/{kaspaAddress}/transactions-count",
response_model=TransactionCount,
tags=["Kaspa addresses"])
@sql_db_only
async def get_transaction_count_for_address(
kaspaAddress: str = Path(
description="Kaspa address as string e.g. "
Expand Down
33 changes: 22 additions & 11 deletions endpoints/get_blocks.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# encoding: utf-8
import os
from typing import List

from fastapi import Query, Path, HTTPException
Expand All @@ -11,6 +12,8 @@
from models.Transaction import Transaction, TransactionOutput, TransactionInput
from server import app, kaspad_client

IS_SQL_DB_CONFIGURED = os.getenv("SQL_URI") is not None


class VerboseDataModel(BaseModel):
hash: str = "18c7afdf8f447ca06adb8b4946dc45f5feb1188c7d177da6094dfbc760eca699"
Expand Down Expand Up @@ -76,21 +79,23 @@ async def get_block(response: Response,
# We found the block in kaspad. Just use it
requested_block = resp["getBlockResponse"]["block"]
else:
# Didn't find the block in kaspad. Try getting it from the DB
response.headers["X-Data-Source"] = "Database"
requested_block = await get_block_from_db(blockId)

if IS_SQL_DB_CONFIGURED:
# Didn't find the block in kaspad. Try getting it from the DB
response.headers["X-Data-Source"] = "Database"
requested_block = await get_block_from_db(blockId)

if not requested_block:
# Still did not get the block
raise HTTPException(status_code=404, detail="Block not found")

# We found the block, now we guarantee it contains the transactions
# It's possible that the block from kaspad does not contain transactions
if 'transactions' not in requested_block or not requested_block['transactions']:
requested_block['transactions'] = await get_block_transactions(blockId)

return requested_block


@app.get("/blocks", response_model=BlockResponse, tags=["Kaspa blocks"])
async def get_blocks(lowHash: str = Query(regex="[a-f0-9]{64}"),
includeBlocks: bool = False,
Expand All @@ -108,13 +113,16 @@ async def get_blocks(lowHash: str = Query(regex="[a-f0-9]{64}"),

return resp["getBlocksResponse"]


"""
Get the block from the database
"""


async def get_block_from_db(blockId):
async with async_session() as s:
requested_block = await s.execute(select(Block)
.where(Block.hash == blockId).limit(1))
.where(Block.hash == blockId).limit(1))

try:
requested_block = requested_block.first()[0] # type: Block
Expand All @@ -137,7 +145,7 @@ async def get_block_from_db(blockId):
"blueScore": requested_block.blue_score,
"pruningPoint": requested_block.pruning_point
},
"transactions": None, # This will be filled later
"transactions": None, # This will be filled later
"verboseData": {
"hash": requested_block.hash,
"difficulty": requested_block.difficulty,
Expand All @@ -152,9 +160,12 @@ async def get_block_from_db(blockId):
}
return None


"""
Get the transactions associated with a block
"""


async def get_block_transactions(blockId):
# create tx data
tx_list = []
Expand All @@ -165,14 +176,14 @@ async def get_block_transactions(blockId):
transactions = transactions.scalars().all()

tx_outputs = await s.execute(select(TransactionOutput)
.where(TransactionOutput.transaction_id
.where(TransactionOutput.transaction_id
.in_([tx.transaction_id for tx in transactions])))

tx_outputs = tx_outputs.scalars().all()

tx_inputs = await s.execute(select(TransactionInput)
.where(TransactionInput.transaction_id
.in_([tx.transaction_id for tx in transactions])))
.in_([tx.transaction_id for tx in transactions])))

tx_inputs = tx_inputs.scalars().all()

Expand Down Expand Up @@ -208,5 +219,5 @@ async def get_block_transactions(blockId):
"blockTime": tx.block_time
}
})
return tx_list

return tx_list
3 changes: 2 additions & 1 deletion endpoints/get_circulating_supply.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pydantic import BaseModel

from endpoints import sql_db_only
from server import app, kaspad_client
from fastapi.responses import PlainTextResponse

Expand Down Expand Up @@ -39,7 +40,7 @@ async def get_circulating_coins(in_billion : bool = False):

@app.get("/info/coinsupply/total", tags=["Kaspa network info"],
response_class=PlainTextResponse)
async def get_circulating_coins():
async def get_total_coins():
"""
Get total amount of $KAS token as numerical value
"""
Expand Down
2 changes: 2 additions & 0 deletions endpoints/get_hashrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from sqlalchemy import select

from dbsession import async_session
from endpoints import sql_db_only
from helper import KeyValueStore
from models.Block import Block
from server import app, kaspad_client
Expand Down Expand Up @@ -49,6 +50,7 @@ async def get_hashrate(stringOnly: bool = False):


@app.get("/info/hashrate/max", response_model=MaxHashrateResponse, tags=["Kaspa network info"])
@sql_db_only
async def get_max_hashrate():
"""
Returns the current hashrate for Kaspa network in TH/s.
Expand Down
6 changes: 2 additions & 4 deletions endpoints/get_marketcap.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# encoding: utf-8

import requests
from pydantic import BaseModel

from helper import get_kas_price
Expand All @@ -26,7 +25,6 @@ async def get_marketcap(stringOnly: bool = False):
}
else:
if mcap < 1000000000:
return f"{round(mcap / 1000000,1)}M"
return f"{round(mcap / 1000000, 1)}M"
else:
return f"{round(mcap / 1000000000,1)}B"

return f"{round(mcap / 1000000000, 1)}B"
4 changes: 3 additions & 1 deletion endpoints/get_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from sqlalchemy.future import select

from dbsession import async_session
from endpoints import filter_fields
from endpoints import filter_fields, sql_db_only
from models.Block import Block
from models.Transaction import Transaction, TransactionOutput, TransactionInput
from server import app
Expand Down Expand Up @@ -80,6 +80,7 @@ class PreviousOutpointLookupMode(str, Enum):
response_model=TxModel,
tags=["Kaspa transactions"],
response_model_exclude_unset=True)
@sql_db_only
async def get_transaction(transactionId: str = Path(regex="[a-f0-9]{64}"),
inputs: bool = True,
outputs: bool = True,
Expand Down Expand Up @@ -160,6 +161,7 @@ async def get_transaction(transactionId: str = Path(regex="[a-f0-9]{64}"),
response_model=List[TxModel],
tags=["Kaspa transactions"],
response_model_exclude_unset=True)
@sql_db_only
async def search_for_transactions(txSearch: TxSearch,
fields: str = "",
resolve_previous_outpoints: PreviousOutpointLookupMode =
Expand Down

0 comments on commit 436c11e

Please sign in to comment.