feat: live block streaming

This commit is contained in:
Daniil Polyakov 2026-04-02 22:36:51 +03:00
parent 578f0d2b15
commit 5b1d74f237
6 changed files with 96 additions and 4 deletions

View File

@ -47,6 +47,10 @@ ExplorerWidget::ExplorerWidget(QWidget* parent)
connect(m_navBar, &NavigationBar::homeClicked, this, &ExplorerWidget::navigateHome);
connect(m_searchBar, &SearchBar::searchRequested, this, &ExplorerWidget::onSearch);
connect(m_indexer.get(), &IndexerService::newBlockAdded, this, [this](const Block& block) {
m_mainPage->onNewBlock(block);
});
updateNavButtons();
}

View File

@ -315,6 +315,19 @@ void MainPage::showSearchResults(const SearchResults& results)
m_contentLayout->insertWidget(0, m_searchResultsWidget);
}
void MainPage::onNewBlock(const Block& block)
{
if (!m_blocksLayout) return;
m_healthLabel->setText(QString("Chain height: %1").arg(block.blockId));
// Append to layout then move to top (index 0)
addBlockRow(m_blocksLayout, block);
auto* newRow = m_blocksLayout->itemAt(m_blocksLayout->count() - 1)->widget();
m_blocksLayout->removeWidget(newRow);
m_blocksLayout->insertWidget(0, newRow);
}
void MainPage::clearSearchResults()
{
if (m_searchResultsWidget) {

View File

@ -18,6 +18,7 @@ public:
void refresh();
void showSearchResults(const SearchResults& results);
void clearSearchResults();
void onNewBlock(const Block& block);
signals:
void blockClicked(quint64 blockId);

View File

@ -4,6 +4,7 @@
#include "models/Transaction.h"
#include "models/Account.h"
#include <QObject>
#include <QVector>
#include <optional>
@ -13,9 +14,12 @@ struct SearchResults {
QVector<Account> accounts;
};
class IndexerService {
class IndexerService : public QObject {
Q_OBJECT
public:
virtual ~IndexerService() = default;
explicit IndexerService(QObject* parent = nullptr) : QObject(parent) {}
~IndexerService() override = default;
virtual std::optional<Account> getAccount(const QString& accountId) = 0;
virtual std::optional<Block> getBlockById(quint64 blockId) = 0;
@ -25,4 +29,7 @@ public:
virtual quint64 getLatestBlockId() = 0;
virtual QVector<Transaction> getTransactionsByAccount(const QString& accountId, int offset, int limit) = 0;
virtual SearchResults search(const QString& query) = 0;
signals:
void newBlockAdded(Block block);
};

View File

@ -31,9 +31,68 @@ QString randomBase58String(int length)
} // namespace
MockIndexerService::MockIndexerService()
MockIndexerService::MockIndexerService(QObject* parent)
: IndexerService(parent)
{
generateData();
connect(&m_blockTimer, &QTimer::timeout, this, &MockIndexerService::onGenerateBlock);
m_blockTimer.start(30000); // 30 seconds
}
Block MockIndexerService::generateBlock(quint64 blockId, const QString& prevHash)
{
auto* rng = QRandomGenerator::global();
Block block;
block.blockId = blockId;
block.prevBlockHash = prevHash;
block.hash = randomHash();
block.timestamp = QDateTime::currentDateTimeUtc();
block.signature = randomHexString(64);
block.bedrockParentId = randomHexString(32);
block.bedrockStatus = BedrockStatus::Pending;
int txCount = rng->bounded(1, 6);
for (int t = 0; t < txCount; ++t) {
Transaction tx;
int typeRoll = rng->bounded(100);
if (typeRoll < 60) {
tx = generatePublicTransaction();
} else if (typeRoll < 85) {
tx = generatePrivacyPreservingTransaction();
} else {
tx = generateProgramDeploymentTransaction();
}
block.transactions.append(tx);
m_transactionsByHash[tx.hash] = tx;
for (const auto& accRef : tx.accounts) {
if (!m_accounts.contains(accRef.accountId)) {
Account acc;
acc.accountId = accRef.accountId;
acc.programOwner = randomBase58String(44);
acc.balance = QString::number(rng->bounded(0, 1000000));
acc.nonce = accRef.nonce;
acc.dataSizeBytes = rng->bounded(0, 4096);
m_accounts[acc.accountId] = acc;
}
}
}
return block;
}
void MockIndexerService::onGenerateBlock()
{
quint64 newId = m_blocks.isEmpty() ? 1 : m_blocks.last().blockId + 1;
QString prevHash = m_blocks.isEmpty() ? QString(64, '0') : m_blocks.last().hash;
Block block = generateBlock(newId, prevHash);
m_blocks.append(block);
m_blocksByHash[block.hash] = block;
emit newBlockAdded(block);
}
QString MockIndexerService::randomHash()

View File

@ -3,10 +3,13 @@
#include "IndexerService.h"
#include <QMap>
#include <QTimer>
class MockIndexerService : public IndexerService {
Q_OBJECT
public:
MockIndexerService();
explicit MockIndexerService(QObject* parent = nullptr);
std::optional<Account> getAccount(const QString& accountId) override;
std::optional<Block> getBlockById(quint64 blockId) override;
@ -17,6 +20,9 @@ public:
QVector<Transaction> getTransactionsByAccount(const QString& accountId, int offset, int limit) override;
SearchResults search(const QString& query) override;
private slots:
void onGenerateBlock();
private:
void generateData();
QString randomHash();
@ -24,9 +30,11 @@ private:
Transaction generatePublicTransaction();
Transaction generatePrivacyPreservingTransaction();
Transaction generateProgramDeploymentTransaction();
Block generateBlock(quint64 blockId, const QString& prevHash);
QVector<Block> m_blocks;
QMap<QString, Block> m_blocksByHash;
QMap<QString, Transaction> m_transactionsByHash;
QMap<QString, Account> m_accounts;
QTimer m_blockTimer;
};