#include "BlockModel.h" #include #include #include #include #include namespace { QString prettify(const QJsonValue& value) { if (value.isObject()) return QString::fromUtf8(QJsonDocument(value.toObject()).toJson(QJsonDocument::Indented)); if (value.isArray()) return QString::fromUtf8(QJsonDocument(value.toArray()).toJson(QJsonDocument::Indented)); return value.toVariant().toString(); } } // namespace int BlockModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return m_entries.size(); } QVariant BlockModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= m_entries.size()) return QVariant(); const Entry& e = m_entries.at(index.row()); switch (role) { case TimestampRole: return e.timestamp; case SlotRole: return e.slot; case VersionRole: return e.version; case ParentBlockRole: return e.parentBlock; case BlockRootRole: return e.blockRoot; case LeaderKeyRole: return e.leaderKey; case EntropyRole: return e.entropy; case ProofRole: return e.proof; case VoucherCmRole: return e.voucherCm; case SignatureRole: return e.signature; case TxCountRole: return e.txCount; case TransactionsRole: return e.transactions; case RawJsonRole: return e.rawJson; case ParsedRole: return e.parsed; default: return QVariant(); } } QHash BlockModel::roleNames() const { QHash names; names[TimestampRole] = "timestamp"; names[SlotRole] = "slot"; names[VersionRole] = "version"; names[ParentBlockRole] = "parentBlock"; names[BlockRootRole] = "blockRoot"; names[LeaderKeyRole] = "leaderKey"; names[EntropyRole] = "entropy"; names[ProofRole] = "proof"; names[VoucherCmRole] = "voucherCm"; names[SignatureRole] = "signature"; names[TxCountRole] = "txCount"; names[TransactionsRole] = "transactions"; names[RawJsonRole] = "rawJson"; names[ParsedRole] = "parsed"; return names; } void BlockModel::appendRaw(const QString& timestamp, const QString& rawJson) { Entry e; e.timestamp = timestamp; // The payload is double-encoded JSON: an outer {"block":""} // whose value is itself a JSON-encoded block. Tolerate a few shapes: // { "block": "" } (observed) // { "block": { ... } } (already an object) // { "header": ..., "transactions": ... }(block sent directly) QJsonObject block; bool ok = false; QJsonParseError err{}; const QJsonDocument outer = QJsonDocument::fromJson(rawJson.toUtf8(), &err); if (err.error == QJsonParseError::NoError && outer.isObject()) { const QJsonObject o = outer.object(); if (o.contains(QStringLiteral("block"))) { const QJsonValue bv = o.value(QStringLiteral("block")); if (bv.isString()) { const QJsonDocument inner = QJsonDocument::fromJson(bv.toString().toUtf8(), &err); if (err.error == QJsonParseError::NoError && inner.isObject()) { block = inner.object(); ok = true; } } else if (bv.isObject()) { block = bv.toObject(); ok = true; } } else if (o.contains(QStringLiteral("header"))) { block = o; ok = true; } } if (ok) { e.parsed = true; const QJsonObject header = block.value(QStringLiteral("header")).toObject(); e.version = header.value(QStringLiteral("version")).toString(); e.parentBlock = header.value(QStringLiteral("parent_block")).toString(); const QJsonValue slotV = header.value(QStringLiteral("slot")); e.slot = slotV.isDouble() ? QString::number(static_cast(slotV.toDouble())) : slotV.toString(); e.blockRoot = header.value(QStringLiteral("block_root")).toString(); const QJsonObject pol = header.value(QStringLiteral("proof_of_leadership")).toObject(); e.proof = pol.value(QStringLiteral("proof")).toString(); e.entropy = pol.value(QStringLiteral("entropy_contribution")).toString(); e.leaderKey = pol.value(QStringLiteral("leader_key")).toString(); e.voucherCm = pol.value(QStringLiteral("voucher_cm")).toString(); e.signature = block.value(QStringLiteral("signature")).toString(); const QJsonArray txs = block.value(QStringLiteral("transactions")).toArray(); e.txCount = txs.size(); for (const QJsonValue tx : txs) e.transactions << prettify(tx); e.rawJson = QString::fromUtf8(QJsonDocument(block).toJson(QJsonDocument::Indented)); } else { // Keep the raw text so an unexpected format is still inspectable. e.parsed = false; e.rawJson = rawJson; } beginInsertRows(QModelIndex(), 0, 0); m_entries.prepend(e); endInsertRows(); if (m_entries.size() > kMaxBlocks) { const int last = m_entries.size() - 1; beginRemoveRows(QModelIndex(), last, last); m_entries.remove(last); endRemoveRows(); } emit countChanged(); } void BlockModel::clear() { if (m_entries.isEmpty()) return; beginResetModel(); m_entries.clear(); endResetModel(); emit countChanged(); }