Skip to content

Commit

Permalink
Fix block retrieval logic (#19)
Browse files Browse the repository at this point in the history
Partially fixes #9
  • Loading branch information
coderofstuff authored Feb 21, 2023
1 parent a419aec commit 0472857
Showing 1 changed file with 124 additions and 102 deletions.
226 changes: 124 additions & 102 deletions endpoints/get_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from dbsession import async_session
from models.Block import Block
from models.Transaction import TransactionOutput, TransactionInput
from models.Transaction import Transaction, TransactionOutput, TransactionInput
from server import app, kaspad_client


Expand Down Expand Up @@ -70,107 +70,26 @@ async def get_block(response: Response,
"hash": blockId,
"includeTransactions": True
})
try:
return resp["getBlockResponse"]["block"]
except KeyError:
# not found on kaspad - check database
async with async_session() as s:
requested_block = await s.execute(select(Block)
.where(Block.hash == blockId).limit(1))

try:
requested_block = requested_block.first()[0] # type: Block
except TypeError:
raise HTTPException(status_code=404, detail="Block not found")

if requested_block:
response.headers["X-Data-Source"] = "Database"

# get transactions information
async with async_session() as s:
transactions = await s.execute("SELECT * FROM transactions WHERE block_hash @> '{"
f"{blockId}"
"}'""")
transactions = transactions.all()

tx_outputs = await s.execute(select(TransactionOutput)
.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])))

tx_inputs = tx_inputs.scalars().all()

# create tx data
tx_list = []
for tx in transactions:
tx_list.append({
"inputs": [
{
"previousOutpoint": {
"transactionId": tx_inp.previous_outpoint_hash,
"index": tx_inp.previous_outpoint_index
},
"signatureScript": tx_inp.signature_script,
"sigOpCount": tx_inp.sig_op_count
}
for tx_inp in tx_inputs if tx_inp.transaction_id == tx.transaction_id],
"outputs": [
{
"amount": tx_out.amount,
"scriptPublicKey": {
"scriptPublicKey": tx_out.script_public_key
},
"verboseData": {
"scriptPublicKeyType": tx_out.script_public_key_type,
"scriptPublicKeyAddress": tx_out.script_public_key_address
}
} for tx_out in tx_outputs if tx_out.transaction_id == tx.transaction_id],
"subnetworkId": tx.subnetwork_id,
"verboseData": {
"transactionId": tx.transaction_id,
"hash": tx.hash,
"mass": tx.mass,
"blockHash": tx.block_hash,
"blockTime": tx.block_time
}
})

return {
"header": {
"version": requested_block.version,
"hashMerkleRoot": requested_block.hash_merkle_root,
"acceptedIdMerkleRoot": requested_block.accepted_id_merkle_root,
"utxoCommitment": requested_block.utxo_commitment,
"timestamp": round(requested_block.timestamp.timestamp() * 1000),
"bits": requested_block.bits,
"nonce": requested_block.nonce,
"daaScore": requested_block.daa_score,
"blueWork": requested_block.blue_score,
"parents": [{"parentHashes": requested_block.parents}],
"blueScore": requested_block.blue_score,
"pruningPoint": requested_block.pruning_point
},
"transactions": tx_list,
"verboseData": {
"hash": requested_block.hash,
"difficulty": requested_block.difficulty,
"selectedParentHash": requested_block.selected_parent_hash,
"transactionIds": [],
"blueScore": requested_block.blue_score,
"childrenHashes": [],
"mergeSetBluesHashes": requested_block.merge_set_blues_hashes,
"mergeSetRedsHashes": requested_block.merge_set_reds_hashes,
"isChainBlock": requested_block.is_chain_block
}
}
else:
raise HTTPException(status_code=404, detail="Block not found")

requested_block = None

if "block" in resp["getBlockResponse"]:
# 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 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}"),
Expand All @@ -188,3 +107,106 @@ 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))

try:
requested_block = requested_block.first()[0] # type: Block
except TypeError:
raise HTTPException(status_code=404, detail="Block not found")

if requested_block:
return {
"header": {
"version": requested_block.version,
"hashMerkleRoot": requested_block.hash_merkle_root,
"acceptedIdMerkleRoot": requested_block.accepted_id_merkle_root,
"utxoCommitment": requested_block.utxo_commitment,
"timestamp": round(requested_block.timestamp.timestamp() * 1000),
"bits": requested_block.bits,
"nonce": requested_block.nonce,
"daaScore": requested_block.daa_score,
"blueWork": requested_block.blue_work,
"parents": [{"parentHashes": requested_block.parents}],
"blueScore": requested_block.blue_score,
"pruningPoint": requested_block.pruning_point
},
"transactions": None, # This will be filled later
"verboseData": {
"hash": requested_block.hash,
"difficulty": requested_block.difficulty,
"selectedParentHash": requested_block.selected_parent_hash,
"transactionIds": [],
"blueScore": requested_block.blue_score,
"childrenHashes": [],
"mergeSetBluesHashes": requested_block.merge_set_blues_hashes,
"mergeSetRedsHashes": requested_block.merge_set_reds_hashes,
"isChainBlock": requested_block.is_chain_block
}
}
return None

"""
Get the transactions associated with a block
"""
async def get_block_transactions(blockId):
# create tx data
tx_list = []

async with async_session() as s:
transactions = await s.execute(select(Transaction).filter(Transaction.block_hash.contains([blockId])))

transactions = transactions.scalars().all()

tx_outputs = await s.execute(select(TransactionOutput)
.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])))

tx_inputs = tx_inputs.scalars().all()

for tx in transactions:
tx_list.append({
"inputs": [
{
"previousOutpoint": {
"transactionId": tx_inp.previous_outpoint_hash,
"index": tx_inp.previous_outpoint_index
},
"signatureScript": tx_inp.signature_script,
"sigOpCount": tx_inp.sig_op_count
}
for tx_inp in tx_inputs if tx_inp.transaction_id == tx.transaction_id],
"outputs": [
{
"amount": tx_out.amount,
"scriptPublicKey": {
"scriptPublicKey": tx_out.script_public_key
},
"verboseData": {
"scriptPublicKeyType": tx_out.script_public_key_type,
"scriptPublicKeyAddress": tx_out.script_public_key_address
}
} for tx_out in tx_outputs if tx_out.transaction_id == tx.transaction_id],
"subnetworkId": tx.subnetwork_id,
"verboseData": {
"transactionId": tx.transaction_id,
"hash": tx.hash,
"mass": tx.mass,
"blockHash": tx.block_hash,
"blockTime": tx.block_time
}
})

return tx_list

0 comments on commit 0472857

Please sign in to comment.