Add trust inherited from authchain

This commit is contained in:
2025-04-11 18:14:47 +02:00
parent c57fd2ddca
commit 7c455754db
4 changed files with 82 additions and 19 deletions
+20 -2
View File
@@ -53,8 +53,8 @@ The file is very simple name/value pairs in standard unix line-endings.
Fields are read from top to bottom, you can mention the same field various Fields are read from top to bottom, you can mention the same field various
times such that the item is processed in order. times such that the item is processed in order.
* trust=ultimate * trust=good
The trust level is given. none, marginal, good, high, ultimate The trust level is given. absent, marginal, good, high
* domain=bitcoincash.org * domain=bitcoincash.org
Provides the source of the metadata in dns form. Provides the source of the metadata in dns form.
* category=deadbeef0124 * category=deadbeef0124
@@ -64,3 +64,21 @@ times such that the item is processed in order.
A 64 character, hex-encoded authbase (txid). Which will then inherit A 64 character, hex-encoded authbase (txid). Which will then inherit
the most recently set trust. Allowing for all metadata files indicated the most recently set trust. Allowing for all metadata files indicated
by it to likewise inherit this trust. by it to likewise inherit this trust.
Trust levels
+---+---+
|Level|Description|
+---+---+
| absent | Trust has been violated, this is likely a scam or spam. |
| marginal | [Default] No trust could be established |
| good | Minimal checking has been done, likely an honest party |
| high | The party is trusted by the registry owner |
| ultimate | We know that the content comes from the party they say they are.|
Notice that the ultimate trust is really only used to describe confidence
level of the metadata file being published by who they say they are. Or
really if you are publishing your own metadata on your own registry this
way.
+2 -2
View File
@@ -49,7 +49,7 @@ void DownloadJob::start()
else { else {
auto data = reply->readAll(); auto data = reply->readAll();
if (checkHash(data)) { if (checkHash(data)) {
out.write(reply->readAll()); out.write(data);
out.flush(); out.flush();
} else { } else {
logWarning() << "Download failed hash-check" << m_sourceUrl.toString(); logWarning() << "Download failed hash-check" << m_sourceUrl.toString();
@@ -116,12 +116,12 @@ bool DownloadJob::checkHash(const QByteArray &contents) const
{ {
if (m_targetHash.isEmpty()) if (m_targetHash.isEmpty())
return true; return true;
static_assert(CSHA256::OUTPUT_SIZE == 32);
assert(m_targetHash.size() == 32); assert(m_targetHash.size() == 32);
CSHA256 hasher; CSHA256 hasher;
hasher.write(contents.constData(), contents.size()); hasher.write(contents.constData(), contents.size());
char hash[CSHA256::OUTPUT_SIZE]; char hash[CSHA256::OUTPUT_SIZE];
static_assert(CSHA256::OUTPUT_SIZE == 32);
hasher.finalize(hash); hasher.finalize(hash);
return memcmp(hash, m_targetHash.begin(), 32) == 0; return memcmp(hash, m_targetHash.begin(), 32) == 0;
} }
+54 -9
View File
@@ -31,7 +31,7 @@ static bool walk(QJsonObject &item, const QStringList &steps) {
static Processor::Trust trustFromString(const QString &string) { static Processor::Trust trustFromString(const QString &string) {
auto lc = string.toLower(); auto lc = string.toLower();
if (lc == "none") if (lc == "absent")
return Processor::NoTrust; return Processor::NoTrust;
if (lc == "marginal") if (lc == "marginal")
return Processor::MarginalTrust; return Processor::MarginalTrust;
@@ -42,13 +42,13 @@ static Processor::Trust trustFromString(const QString &string) {
if (lc == "ultimate") if (lc == "ultimate")
return Processor::UltimateTrust; return Processor::UltimateTrust;
logWarning() << "Unrecognized trust passed:" << string; logWarning() << "Unrecognized trust passed:" << string;
return Processor::NoTrust; return Processor::MarginalTrust;
} }
static QString trustToString(Processor::Trust trust) { static QString trustToString(Processor::Trust trust) {
switch (trust) { switch (trust) {
case Processor::NoTrust: case Processor::NoTrust:
return "none"; return "absent";
case Processor::MarginalTrust: case Processor::MarginalTrust:
return "marginal"; return "marginal";
case Processor::GoodTrust: case Processor::GoodTrust:
@@ -113,6 +113,24 @@ void Processor::run2()
} }
} }
} }
for (auto iter = m_transactions.begin(); iter != m_transactions.end(); ++iter) {
// create placeholder bcmr data objects in order to inherit trust from
// the authchain group.
Transaction *d = iter->second;
auto gId = d->authbase();
auto gIter = m_groups.find(gId);
if (gIter != m_groups.end()) {
MetaGroup *g = gIter->second;
if (g->trust != MarginalTrust) {
// the transaction, and any bcmrs it might contain would inherit
// the trust from that group. Lets create a placeholder to mark that.
QFileInfo target(m_inDir + "bcmrs/downloaded/"
+ QString::fromStdString(d->txid.ToString()) + ".json");
auto *meta = fetchOrCreate(target.absoluteFilePath());
meta->trust = g->trust;
}
}
}
QDirIterator iter(m_inDir + "bcmrs", QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs, QDirIterator::Subdirectories); QDirIterator iter(m_inDir + "bcmrs", QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs, QDirIterator::Subdirectories);
while (iter.hasNext()) { while (iter.hasNext()) {
@@ -365,7 +383,9 @@ void Processor::parseBCMR(const QString &path)
id->category = cat; id->category = cat;
MetaGroup *mg = fetchOrCreate(cat, Category); MetaGroup *mg = fetchOrCreate(cat, Category);
id->tokens.push_back(mg); id->tokens.push_back(mg);
mg->trust = std::max(mg->trust, me->trust); // identities inherit trust from the bcmr
if (me->trust != MarginalTrust)
mg->trust = me->trust;
mg->owners.insert(me); mg->owners.insert(me);
// walk all nfts and extract image and icon resource urls // walk all nfts and extract image and icon resource urls
auto types = token; auto types = token;
@@ -394,6 +414,10 @@ void Processor::parseBCMR(const QString &path)
// we conclude it was an authbase // we conclude it was an authbase
auto *group = fetchOrCreate(id->identity, AuthBase); auto *group = fetchOrCreate(id->identity, AuthBase);
group->owners.insert(me); group->owners.insert(me);
logFatal() << "xxxx" << in.fileName();
group->trust = me->trust; group->trust = me->trust;
} }
} }
@@ -407,11 +431,11 @@ void Processor::parseTrustFile(const QString &path)
logCritical() << "Failed to read" << path; logCritical() << "Failed to read" << path;
return; return;
} }
Trust trust = NoTrust; Trust trust = MarginalTrust;
for (const QString &line : QString::fromUtf8(in.readAll()).split('\n')) { for (const QString &line : QString::fromUtf8(in.readAll()).split('\n')) {
if (line.startsWith("domain=")) { if (line.startsWith("domain=")) {
QFileInfo cache(m_inDir + "bcmrs/downloaded/" + me.fileName()); QFileInfo cache(m_inDir + "bcmrs/downloaded/" + me.fileName());
if (trust != NoTrust) if (trust != MarginalTrust)
fetchOrCreate(cache.absoluteFilePath())->trust = trust; fetchOrCreate(cache.absoluteFilePath())->trust = trust;
if (cache.exists()) { if (cache.exists()) {
@@ -440,7 +464,7 @@ void Processor::parseTrustFile(const QString &path)
continue; continue;
} }
if (trust != NoTrust) if (trust != MarginalTrust)
fetchOrCreate(cat, Category)->trust = trust; fetchOrCreate(cat, Category)->trust = trust;
} }
else if (line.startsWith("authbase=")) { else if (line.startsWith("authbase=")) {
@@ -450,7 +474,7 @@ void Processor::parseTrustFile(const QString &path)
continue; continue;
} }
if (trust != NoTrust) if (trust != MarginalTrust)
fetchOrCreate(ab, AuthBase)->trust = trust; fetchOrCreate(ab, AuthBase)->trust = trust;
} }
} }
@@ -459,6 +483,12 @@ void Processor::parseTrustFile(const QString &path)
void Processor::parseBCHTx(QFile &file) void Processor::parseBCHTx(QFile &file)
{ {
if (file.size() < 65) { if (file.size() < 65) {
// downloading a bcmr from a transaction may fail to match the hash
// and then we create an empty file to avoid downloading it again
// in the future. So don't complain about those.
// (scenario is not weird, maybe bcmr owners don't host new files
// when they update the on-chain reference to them, so old ones fail)
if (file.size() > 0)
logWarning() << "Skipping file, it's too small"<< file.fileName(); logWarning() << "Skipping file, it's too small"<< file.fileName();
return; return;
} }
@@ -486,8 +516,10 @@ void Processor::parseBCHTx(QFile &file)
&& output.outputScript[6] == 0x20) { // space && output.outputScript[6] == 0x20) { // space
auto hash = output.outputScript.mid(7, 32); auto hash = output.outputScript.mid(7, 32);
// filename is the original transaction id, so we can figure out
// who owns it.
QFileInfo target(m_inDir + "bcmrs/downloaded/" QFileInfo target(m_inDir + "bcmrs/downloaded/"
+ QString::fromStdString(hash.toHex()) + ".json"); + QString::fromStdString(tx.createHash().ToString()) + ".json");
if (target.exists()) if (target.exists())
continue; continue;
@@ -579,3 +611,16 @@ void Processor::addJob(DownloadJob *job)
}); });
m_downloadJobs.append(job); m_downloadJobs.append(job);
} }
QString Processor::Transaction::authbase() const
{
auto *root = this;
while (true) {
if (root->previous)
root = root->previous;
else
break;
}
return QString::fromStdString(root->txid.ToString());
}
+3 -3
View File
@@ -59,7 +59,7 @@ private:
QSet<QString> resources; QSet<QString> resources;
}; };
struct MetaBCMR { struct MetaBCMR {
Trust trust = NoTrust; Trust trust = MarginalTrust;
QString hash; QString hash;
QString origFilename; QString origFilename;
std::vector<MetaIdentity*> identities; std::vector<MetaIdentity*> identities;
@@ -71,7 +71,7 @@ private:
struct MetaGroup { struct MetaGroup {
GroupType type; GroupType type;
QString id; QString id;
Trust trust = NoTrust; Trust trust = MarginalTrust;
std::set<MetaBCMR*> owners; std::set<MetaBCMR*> owners;
}; };
@@ -81,7 +81,7 @@ private:
GroupMap m_groups; GroupMap m_groups;
struct Transaction { struct Transaction {
uint256 authbase() const; QString authbase() const;
Tx transaction; Tx transaction;
uint256 txid; uint256 txid;
Transaction *previous = nullptr; Transaction *previous = nullptr;