Introduce the concept of trust.

This commit is contained in:
2025-04-10 23:12:53 +02:00
parent 02690cf994
commit 3d7fdadc0b
4 changed files with 142 additions and 45 deletions
+15 -3
View File
@@ -35,10 +35,15 @@ The opposite is true too, a known company can have identified itself as the
owner and that we can advertise the trust to be higher.
In hour generated metadata this is reflected by the (token) category itself
In our generated metadata this is reflected by the (token) category itself
having a trust, and each metadata file linked having it's own specific
trust.
As a suggestion of policy, if a publisher of metadata has inconsistent data,
for instance they failed to have 18+ tags in their metadata file while this
would be appropriate, then what we suggest is to simply not include this
teams data in the registry until such a time that this omission has been fixed.
# Config format
In the trust subdir of the input directory you can place many text files
@@ -49,6 +54,13 @@ Fields are read from top to bottom, you can mention the same field various
times such that the item is processed in order.
* trust=ultimate
The trust level is given. none, marginal, good, ultimate
The trust level is given. none, marginal, good, high, ultimate
* domain=bitcoincash.org
Provides the source of the
Provides the source of the metadata in dns form.
* category=deadbeef0124
A 64 character, hex-encoded token category. Which will then inherit
the most recently set trust.
* authbase=deadbeef0124
A 64 character, hex-encoded authbase (txid). Which will then inherit
the most recently set trust. Allowing for all metadata files indicated
by it to likewise inherit this trust.
+5 -5
View File
@@ -24,8 +24,8 @@ void DownloadJob::start()
connect (reply, &QNetworkReply::finished, this, [=]() {
if (reply->error() != QNetworkReply::NoError) {
// TODO mark file as not available somehow.
logCritical() << "Download failed" << m_sourceUrl.toString();
logCritical() << reply->errorString();
logWarning() << "Download failed" << m_sourceUrl.toString();
logInfo() << reply->errorString();
}
else {
QFileInfo info(m_targetFilePath);
@@ -44,12 +44,12 @@ void DownloadJob::start()
});
connect (reply, &QNetworkReply::errorOccurred, this, [=](QNetworkReply::NetworkError error) {
logFatal() << "error occurred" << error;
logInfo() << "Download error occurred" << error << "on" << m_sourceUrl.toString();
});
connect (reply, &QNetworkReply::sslErrors, this, [=](const QList<QSslError> &error) {
logFatal() << "ssl errors";
logWarning() << "ssl errors on" << m_sourceUrl.toString();
for (const auto &e : error) {
logFatal() << e.error() << e.errorString();
logWarning() << e.error() << e.errorString();
}
});
}
+93 -19
View File
@@ -29,6 +29,40 @@ static bool walk(QJsonObject &item, const QStringList &steps) {
return true;
}
static Processor::Trust trustFromString(const QString &string) {
auto lc = string.toLower();
if (lc == "none")
return Processor::NoTrust;
if (lc == "marginal")
return Processor::MarginalTrust;
if (lc == "good")
return Processor::GoodTrust;
if (lc == "high")
return Processor::HighTrust;
if (lc == "ultimate")
return Processor::UltimateTrust;
logWarning() << "Unrecognized trust passed:" << string;
return Processor::NoTrust;
}
static QString trustToString(Processor::Trust trust) {
switch (trust) {
case Processor::NoTrust:
return "none";
case Processor::MarginalTrust:
return "marginal";
case Processor::GoodTrust:
return "good";
case Processor::HighTrust:
return "high";
case Processor::UltimateTrust:
return "ultimate";
default:
assert(false);
break;
}
}
Processor::Processor(const QString &inDir, const QString &outDir)
: m_inDir(inDir),
@@ -68,7 +102,8 @@ void Processor::run2()
for (auto *source : m_sources) {
assert(source);
assert(source->identities.size() > 0);
if (source->identities.empty()) // created by a trust file, but seems no data.
continue;
QString outPath = m_outDir + source->hash + ".json";
if (!QFile::exists(outPath)) {
logCritical() << "Placing BCMR " << source->identities.at(0)->name << "as" << source->hash;
@@ -124,10 +159,12 @@ void Processor::run2()
MetaIdentity *id = owner->idForGroupId(mg->id);
o.insert("name", id->name);
o.insert("timestamp", id->timestampStr);
o.insert("trust", trustToString(owner->trust));
sources.append(o);
}
QJsonObject root;
root.insert("sources", sources);
root.insert("trust", trustToString(mg->trust));
QJsonDocument doc;
doc.setObject(root);
auto memData = doc.toJson(QJsonDocument::Compact);
@@ -183,7 +220,9 @@ void Processor::runDownloadQueue()
logInfo() << "Starting download" << one->sourceUrl().toString();
logInfo() << "for" << one->targetFilePath();
// we should iterate all BCMRs that may have the same path already on the filesystem.
if (id) {
for (auto &group : m_groups) {
assert(id);
if ((group.second->type == AuthBase && id->identity == group.second->id)
|| (group.second->type != AuthBase && id->category == group.second->id)) {
for (auto *owner : group.second->owners) {
@@ -205,6 +244,7 @@ void Processor::runDownloadQueue()
}
}
}
}
one->start();
}
@@ -268,16 +308,13 @@ void Processor::parseBCMR(const QString &path)
if (revision.isEmpty())
continue;
if (me == nullptr) {
me = new MetaBCMR();
me->origFilename = path;
me = fetchOrCreate(path);
// calc hash
CSHA256 hasher;
hasher.write(data.constData(), data.size());
char buf[CSHA256::OUTPUT_SIZE];
hasher.finalize(buf);
me->hash = QByteArray(buf, sizeof(buf)).toHex();
m_sources.push_back(me);
}
MetaIdentity *id = new MetaIdentity();
id->name = revision["name"].toString();
@@ -305,6 +342,7 @@ void Processor::parseBCMR(const QString &path)
id->category = cat;
MetaGroup *mg = fetchOrCreate(cat, Category);
id->tokens.push_back(mg);
mg->trust = std::max(mg->trust, me->trust);
mg->owners.insert(me);
// walk all nfts and extract image and icon resource urls
auto types = token;
@@ -331,31 +369,35 @@ void Processor::parseBCMR(const QString &path)
if (iter == m_groups.end()) {
// the identity was not duplicated as a category, then.
// we conclude it was an authbase
fetchOrCreate(id->identity, AuthBase)->owners.insert(me);
auto *group = fetchOrCreate(id->identity, AuthBase);
group->owners.insert(me);
group->trust = me->trust;
}
}
}
void Processor::parseTrustFile(const QString &path)
{
// TODO move the cache to a in/bcmrs/downloaded/trust_[filename].domain file
QFileInfo me(path);
QFileInfo cache(me.absolutePath() + "/../bcmrs/downloaded/" + me.fileName());
if (cache.exists()) {
QDateTime marker = QDateTime::currentDateTimeUtc().addDays(-7);
if (cache.lastModified() > marker) {
logInfo() << "Skipping check of" << path;
return;
}
}
QFile in(path);
if (!in.open(QIODevice::ReadOnly)) {
logCritical() << "Failed to read" << path;
return;
}
Trust trust = NoTrust;
for (const QString &line : QString::fromUtf8(in.readAll()).split('\n')) {
if (line.startsWith("domain=")) {
// .well-known/bitcoin-cash-metadata-registry.json
QFileInfo cache(me.absolutePath() + "/../bcmrs/downloaded/" + me.fileName());
if (trust != NoTrust)
fetchOrCreate(cache.absoluteFilePath())->trust = trust;
if (cache.exists()) {
QDateTime marker = QDateTime::currentDateTimeUtc().addDays(-7);
if (cache.lastModified() > marker) {
logInfo() << "Skipping check of" << path;
continue;
}
}
QUrl location;
location.setHost(line.mid(7).trimmed());
location.setPath("/.well-known/bitcoin-cash-metadata-registry.json");
@@ -369,11 +411,30 @@ void Processor::parseTrustFile(const QString &path)
});
m_downloadJobs.append(job);
}
// if (line.startsWith("trust=")) {
// //TODO
// }
else if (line.startsWith("trust=")) {
trust = trustFromString(line.mid(6).trimmed());
}
else if (line.startsWith("category=")) {
auto cat = line.mid(9).trimmed();
if (cat.length() != 64) {
logWarning() << "category line in" << path << "has wrong format.";
continue;
}
if (trust != NoTrust)
fetchOrCreate(cat, Category)->trust = trust;
}
else if (line.startsWith("authbase=")) {
auto ab = line.mid(9).trimmed();
if (ab.length() != 64) {
logWarning() << "authbase line in" << path << "has wrong format.";
continue;
}
if (trust != NoTrust)
fetchOrCreate(ab, AuthBase)->trust = trust;
}
}
}
void Processor::parseBCHTx(QFile &file)
@@ -382,6 +443,19 @@ void Processor::parseBCHTx(QFile &file)
logFatal() << "parse tx is a TODO";
}
Processor::MetaBCMR *Processor::fetchOrCreate(const QString &bcmrFileName)
{
for (auto *s : m_sources) {
if (s->origFilename == bcmrFileName) {
return s;
}
}
MetaBCMR *meta = new MetaBCMR();
meta->origFilename = bcmrFileName;
m_sources.push_back(meta);
return meta;
}
bool Processor::offline() const
{
return m_offline;
+11
View File
@@ -21,6 +21,14 @@ public:
bool offline() const;
void setOffline(bool newOffline);
enum Trust {
NoTrust,
MarginalTrust,
GoodTrust,
HighTrust,
UltimateTrust
};
private slots:
void runDownloadQueue();
void run2();
@@ -47,16 +55,19 @@ private:
QSet<QString> resources;
};
struct MetaBCMR {
Trust trust = NoTrust;
QString hash;
QString origFilename;
std::vector<MetaIdentity*> identities;
MetaIdentity* idForGroupId(const QString &groupId) const;
};
MetaBCMR *fetchOrCreate(const QString &bcmrFileName);
struct MetaGroup {
GroupType type;
QString id;
Trust trust = NoTrust;
std::set<MetaBCMR*> owners;
};