2023-10-23 04:12:54 +03:00
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
|
2021-07-26 14:31:09 +03:00
|
|
|
#include "path-info.hh"
|
2022-03-09 00:03:03 +02:00
|
|
|
#include "store-api.hh"
|
2023-10-23 04:12:54 +03:00
|
|
|
#include "json-utils.hh"
|
2021-07-26 14:31:09 +03:00
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
2023-04-18 00:13:44 +03:00
|
|
|
GENERATE_CMP_EXT(
|
|
|
|
,
|
|
|
|
UnkeyedValidPathInfo,
|
|
|
|
me->deriver,
|
|
|
|
me->narHash,
|
|
|
|
me->references,
|
|
|
|
me->registrationTime,
|
|
|
|
me->narSize,
|
|
|
|
//me->id,
|
|
|
|
me->ultimate,
|
|
|
|
me->sigs,
|
|
|
|
me->ca);
|
|
|
|
|
|
|
|
GENERATE_CMP_EXT(
|
|
|
|
,
|
|
|
|
ValidPathInfo,
|
|
|
|
me->path,
|
|
|
|
static_cast<const UnkeyedValidPathInfo &>(*me));
|
|
|
|
|
2023-01-13 22:23:29 +02:00
|
|
|
std::string ValidPathInfo::fingerprint(const Store & store) const
|
|
|
|
{
|
|
|
|
if (narSize == 0)
|
|
|
|
throw Error("cannot calculate fingerprint of path '%s' because its size is not known",
|
|
|
|
store.printStorePath(path));
|
|
|
|
return
|
2023-11-28 16:38:15 +02:00
|
|
|
"1;" + store.printStorePath(path) + ";"
|
|
|
|
+ narHash.to_string(HashFormat::Nix32, true) + ";"
|
|
|
|
+ std::to_string(narSize) + ";"
|
2023-01-14 23:38:43 +02:00
|
|
|
+ concatStringsSep(",", store.printStorePathSet(references));
|
2023-01-13 22:23:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-01-03 22:02:20 +02:00
|
|
|
void ValidPathInfo::sign(const Store & store, const Signer & signer)
|
2023-01-13 22:23:29 +02:00
|
|
|
{
|
2024-01-03 22:02:20 +02:00
|
|
|
sigs.insert(signer.signDetached(fingerprint(store)));
|
2023-01-13 22:23:29 +02:00
|
|
|
}
|
|
|
|
|
2023-02-28 18:34:18 +02:00
|
|
|
std::optional<ContentAddressWithReferences> ValidPathInfo::contentAddressWithReferences() const
|
2023-01-14 21:27:28 +02:00
|
|
|
{
|
|
|
|
if (! ca)
|
|
|
|
return std::nullopt;
|
|
|
|
|
2023-01-23 19:58:11 +02:00
|
|
|
return std::visit(overloaded {
|
2023-07-06 01:53:44 +03:00
|
|
|
[&](const TextIngestionMethod &) -> ContentAddressWithReferences {
|
2023-01-23 19:58:11 +02:00
|
|
|
assert(references.count(path) == 0);
|
|
|
|
return TextInfo {
|
2023-07-06 01:53:44 +03:00
|
|
|
.hash = ca->hash,
|
2023-02-28 18:57:20 +02:00
|
|
|
.references = references,
|
2023-01-23 19:58:11 +02:00
|
|
|
};
|
|
|
|
},
|
2023-07-06 01:53:44 +03:00
|
|
|
[&](const FileIngestionMethod & m2) -> ContentAddressWithReferences {
|
2023-01-23 19:58:11 +02:00
|
|
|
auto refs = references;
|
|
|
|
bool hasSelfReference = false;
|
|
|
|
if (refs.count(path)) {
|
|
|
|
hasSelfReference = true;
|
|
|
|
refs.erase(path);
|
|
|
|
}
|
|
|
|
return FixedOutputInfo {
|
2023-07-06 01:53:44 +03:00
|
|
|
.method = m2,
|
|
|
|
.hash = ca->hash,
|
2023-02-28 18:57:20 +02:00
|
|
|
.references = {
|
2023-01-23 19:58:11 +02:00
|
|
|
.others = std::move(refs),
|
|
|
|
.self = hasSelfReference,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
},
|
2023-07-06 01:53:44 +03:00
|
|
|
}, ca->method.raw);
|
2023-01-14 21:27:28 +02:00
|
|
|
}
|
2023-01-13 22:23:29 +02:00
|
|
|
|
|
|
|
bool ValidPathInfo::isContentAddressed(const Store & store) const
|
|
|
|
{
|
2023-02-28 18:34:18 +02:00
|
|
|
auto fullCaOpt = contentAddressWithReferences();
|
2023-01-13 22:23:29 +02:00
|
|
|
|
2023-01-14 21:27:28 +02:00
|
|
|
if (! fullCaOpt)
|
|
|
|
return false;
|
|
|
|
|
2023-01-23 19:58:11 +02:00
|
|
|
auto caPath = store.makeFixedOutputPathFromCA(path.name(), *fullCaOpt);
|
2023-01-13 22:23:29 +02:00
|
|
|
|
|
|
|
bool res = caPath == path;
|
|
|
|
|
|
|
|
if (!res)
|
|
|
|
printError("warning: path '%s' claims to be content-addressed but isn't", store.printStorePath(path));
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t ValidPathInfo::checkSignatures(const Store & store, const PublicKeys & publicKeys) const
|
|
|
|
{
|
|
|
|
if (isContentAddressed(store)) return maxSigs;
|
|
|
|
|
|
|
|
size_t good = 0;
|
|
|
|
for (auto & sig : sigs)
|
|
|
|
if (checkSignature(store, publicKeys, sig))
|
|
|
|
good++;
|
|
|
|
return good;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ValidPathInfo::checkSignature(const Store & store, const PublicKeys & publicKeys, const std::string & sig) const
|
|
|
|
{
|
|
|
|
return verifyDetached(fingerprint(store), sig, publicKeys);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Strings ValidPathInfo::shortRefs() const
|
|
|
|
{
|
|
|
|
Strings refs;
|
2023-01-14 23:38:43 +02:00
|
|
|
for (auto & r : references)
|
2023-01-13 22:23:29 +02:00
|
|
|
refs.push_back(std::string(r.to_string()));
|
|
|
|
return refs;
|
|
|
|
}
|
|
|
|
|
2023-01-14 21:27:28 +02:00
|
|
|
ValidPathInfo::ValidPathInfo(
|
|
|
|
const Store & store,
|
2023-01-23 19:58:11 +02:00
|
|
|
std::string_view name,
|
|
|
|
ContentAddressWithReferences && ca,
|
2023-01-14 21:27:28 +02:00
|
|
|
Hash narHash)
|
2023-04-18 00:13:44 +03:00
|
|
|
: UnkeyedValidPathInfo(narHash)
|
|
|
|
, path(store.makeFixedOutputPathFromCA(name, ca))
|
2023-01-14 21:27:28 +02:00
|
|
|
{
|
|
|
|
std::visit(overloaded {
|
|
|
|
[this](TextInfo && ti) {
|
2023-01-14 23:38:43 +02:00
|
|
|
this->references = std::move(ti.references);
|
2023-07-06 01:53:44 +03:00
|
|
|
this->ca = ContentAddress {
|
|
|
|
.method = TextIngestionMethod {},
|
|
|
|
.hash = std::move(ti.hash),
|
|
|
|
};
|
2023-01-14 21:27:28 +02:00
|
|
|
},
|
|
|
|
[this](FixedOutputInfo && foi) {
|
2023-01-14 23:38:43 +02:00
|
|
|
this->references = std::move(foi.references.others);
|
|
|
|
if (foi.references.self)
|
|
|
|
this->references.insert(path);
|
2023-07-06 01:53:44 +03:00
|
|
|
this->ca = ContentAddress {
|
|
|
|
.method = std::move(foi.method),
|
|
|
|
.hash = std::move(foi.hash),
|
|
|
|
};
|
2023-01-14 21:27:28 +02:00
|
|
|
},
|
2023-03-31 00:12:49 +03:00
|
|
|
}, std::move(ca).raw);
|
2023-01-14 21:27:28 +02:00
|
|
|
}
|
|
|
|
|
2023-10-23 04:12:54 +03:00
|
|
|
|
2023-10-23 04:12:54 +03:00
|
|
|
nlohmann::json UnkeyedValidPathInfo::toJSON(
|
2023-10-23 04:12:54 +03:00
|
|
|
const Store & store,
|
|
|
|
bool includeImpureInfo,
|
|
|
|
HashFormat hashFormat) const
|
|
|
|
{
|
|
|
|
using nlohmann::json;
|
|
|
|
|
|
|
|
auto jsonObject = json::object();
|
|
|
|
|
|
|
|
jsonObject["narHash"] = narHash.to_string(hashFormat, true);
|
|
|
|
jsonObject["narSize"] = narSize;
|
|
|
|
|
|
|
|
{
|
2024-02-12 17:51:20 +02:00
|
|
|
auto & jsonRefs = jsonObject["references"] = json::array();
|
2023-10-23 04:12:54 +03:00
|
|
|
for (auto & ref : references)
|
|
|
|
jsonRefs.emplace_back(store.printStorePath(ref));
|
|
|
|
}
|
|
|
|
|
2024-02-12 17:51:20 +02:00
|
|
|
jsonObject["ca"] = ca ? (std::optional { renderContentAddress(*ca) }) : std::nullopt;
|
2023-10-23 04:12:54 +03:00
|
|
|
|
|
|
|
if (includeImpureInfo) {
|
2024-02-12 17:51:20 +02:00
|
|
|
jsonObject["deriver"] = deriver ? (std::optional { store.printStorePath(*deriver) }) : std::nullopt;
|
2023-10-23 04:12:54 +03:00
|
|
|
|
2024-02-12 17:51:20 +02:00
|
|
|
jsonObject["registrationTime"] = registrationTime ? (std::optional { registrationTime }) : std::nullopt;
|
2023-10-23 04:12:54 +03:00
|
|
|
|
2024-02-12 17:51:20 +02:00
|
|
|
jsonObject["ultimate"] = ultimate;
|
2023-10-23 04:12:54 +03:00
|
|
|
|
2024-02-12 17:51:20 +02:00
|
|
|
auto & sigsObj = jsonObject["signatures"] = json::array();
|
|
|
|
for (auto & sig : sigs)
|
|
|
|
sigsObj.push_back(sig);
|
2023-10-23 04:12:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return jsonObject;
|
|
|
|
}
|
|
|
|
|
2023-10-23 04:12:54 +03:00
|
|
|
UnkeyedValidPathInfo UnkeyedValidPathInfo::fromJSON(
|
2023-10-23 04:12:54 +03:00
|
|
|
const Store & store,
|
2024-04-03 21:04:00 +03:00
|
|
|
const nlohmann::json & _json)
|
2023-10-23 04:12:54 +03:00
|
|
|
{
|
2023-10-23 04:12:54 +03:00
|
|
|
UnkeyedValidPathInfo res {
|
2023-10-23 04:12:54 +03:00
|
|
|
Hash(Hash::dummy),
|
|
|
|
};
|
|
|
|
|
2024-04-03 21:04:00 +03:00
|
|
|
auto & json = getObject(_json);
|
|
|
|
res.narHash = Hash::parseAny(getString(valueAt(json, "narHash")), std::nullopt);
|
|
|
|
res.narSize = getInteger(valueAt(json, "narSize"));
|
2023-10-23 04:12:54 +03:00
|
|
|
|
|
|
|
try {
|
2024-04-03 21:04:00 +03:00
|
|
|
auto references = getStringList(valueAt(json, "references"));
|
2023-10-23 04:12:54 +03:00
|
|
|
for (auto & input : references)
|
|
|
|
res.references.insert(store.parseStorePath(static_cast<const std::string &>
|
|
|
|
(input)));
|
|
|
|
} catch (Error & e) {
|
|
|
|
e.addTrace({}, "while reading key 'references'");
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
2024-02-12 17:51:20 +02:00
|
|
|
// New format as this as nullable but mandatory field; handling
|
|
|
|
// missing is for back-compat.
|
2023-10-23 04:12:54 +03:00
|
|
|
if (json.contains("ca"))
|
2024-02-12 17:51:20 +02:00
|
|
|
if (auto * rawCa = getNullable(valueAt(json, "ca")))
|
|
|
|
res.ca = ContentAddress::parse(getString(*rawCa));
|
2023-10-23 04:12:54 +03:00
|
|
|
|
|
|
|
if (json.contains("deriver"))
|
2024-02-12 17:51:20 +02:00
|
|
|
if (auto * rawDeriver = getNullable(valueAt(json, "deriver")))
|
|
|
|
res.deriver = store.parseStorePath(getString(*rawDeriver));
|
2023-10-23 04:12:54 +03:00
|
|
|
|
|
|
|
if (json.contains("registrationTime"))
|
2024-02-12 17:51:20 +02:00
|
|
|
if (auto * rawRegistrationTime = getNullable(valueAt(json, "registrationTime")))
|
|
|
|
res.registrationTime = getInteger(*rawRegistrationTime);
|
2023-10-23 04:12:54 +03:00
|
|
|
|
|
|
|
if (json.contains("ultimate"))
|
2024-04-03 21:04:00 +03:00
|
|
|
res.ultimate = getBoolean(valueAt(json, "ultimate"));
|
2023-10-23 04:12:54 +03:00
|
|
|
|
|
|
|
if (json.contains("signatures"))
|
2024-02-12 17:51:20 +02:00
|
|
|
res.sigs = getStringSet(valueAt(json, "signatures"));
|
2023-10-23 04:12:54 +03:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2021-07-26 14:31:09 +03:00
|
|
|
}
|