Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 58 additions & 16 deletions src/libstore/ipfs-binary-cache-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class IPFSBinaryCacheStore : public BinaryCacheStore
auto state(_state.lock());
return state->ipfsPath;
}
std::string initialIpfsPath;
std::optional<string> optIpnsPath;

struct State
Expand All @@ -41,9 +42,10 @@ class IPFSBinaryCacheStore : public BinaryCacheStore
if (cacheUri.back() == '/')
cacheUri.pop_back();

if (hasPrefix(cacheUri, "ipfs://"))
state->ipfsPath = "/ipfs/" + std::string(cacheUri, 7);
else if (hasPrefix(cacheUri, "ipns://"))
if (hasPrefix(cacheUri, "ipfs://")) {
initialIpfsPath = "/ipfs/" + std::string(cacheUri, 7);
state->ipfsPath = initialIpfsPath;
} else if (hasPrefix(cacheUri, "ipns://"))
optIpnsPath = "/ipns/" + std::string(cacheUri, 7);
else
throw Error("unknown IPNS URI '%s'", cacheUri);
Expand All @@ -64,16 +66,8 @@ class IPFSBinaryCacheStore : public BinaryCacheStore
// Resolve the IPNS name to an IPFS object
if (optIpnsPath) {
auto ipnsPath = *optIpnsPath;
debug("Resolving IPFS object of '%s', this could take a while.", ipnsPath);
auto uri = daemonUri + "/api/v0/name/resolve?offline=true&arg=" + getFileTransfer()->urlEncode(ipnsPath);
FileTransferRequest request(uri);
request.post = true;
request.tries = 1;
auto res = getFileTransfer()->download(request);
auto json = nlohmann::json::parse(*res.data);
if (json.find("Path") == json.end())
throw Error("daemon for IPFS is not running properly");
state->ipfsPath = json["Path"];
initialIpfsPath = resolveIPNSName(ipnsPath, true);
state->ipfsPath = initialIpfsPath;
}
}

Expand All @@ -92,6 +86,18 @@ class IPFSBinaryCacheStore : public BinaryCacheStore

protected:

// Given a ipns path, checks if it corresponds to a DNSLink path, and in
// case returns the domain
std::optional<string> isDNSLinkPath(std::string path) {
if (path.find("/ipns/") != 0)
throw Error("The provided path is not a ipns path");
auto subpath = std::string(path, 6);
if (subpath.find(".") != std::string::npos) {
return subpath;
}
return std::nullopt;
}

bool fileExists(const std::string & path) override
{
auto uri = daemonUri + "/api/v0/object/stat?arg=" + getFileTransfer()->urlEncode(getIpfsPath() + "/" + path);
Expand All @@ -111,14 +117,50 @@ class IPFSBinaryCacheStore : public BinaryCacheStore
}
}

// Resolve the IPNS name to an IPFS object
std::string resolveIPNSName(std::string ipnsPath, bool offline) {
debug("Resolving IPFS object of '%s', this could take a while.", ipnsPath);
auto uri = daemonUri + "/api/v0/name/resolve?offline=" + (offline?"true":"false") + "&arg=" + getFileTransfer()->urlEncode(ipnsPath);
FileTransferRequest request(uri);
request.post = true;
request.tries = 1;
auto res = getFileTransfer()->download(request);
auto json = nlohmann::json::parse(*res.data);
if (json.find("Path") == json.end())
throw Error("daemon for IPFS is not running properly");
return json["Path"];
}

// IPNS publish can be slow, we try to do it rarely.
void sync() override
{
if (!optIpnsPath)
return;
auto state(_state.lock());

if (!optIpnsPath) {
throw Error("We don't have an ipns path and the current ipfs address doesn't match the initial one.\n current: %s\n initial: %s",
state->ipfsPath, initialIpfsPath);
}

auto ipnsPath = *optIpnsPath;

auto state(_state.lock());
auto resolvedIpfsPath = resolveIPNSName(ipnsPath, false);
if (resolvedIpfsPath != initialIpfsPath) {
throw Error("The IPNS hash or DNS link %s resolves now to something different from the value it had when Nix was started;\n wanted: %s\n got %s\nPerhaps something else updated it in the meantime?",
initialIpfsPath, resolvedIpfsPath);
}

if (resolvedIpfsPath == state->ipfsPath) {
printMsg(lvlInfo, "The hash is already up to date, nothing to do");
return;
}

// Now, we know that paths are not up to date but also not changed due to updates in DNS or IPNS hash.
auto optDomain = isDNSLinkPath(ipnsPath);
if (optDomain) {
auto domain = *optDomain;
throw Error("The provided ipns path is a DNSLink, and syncing those is not supported.\n Current DNSLink: %s\nYou should update your DNS settings"
, domain);
}

debug("Publishing '%s' to '%s', this could take a while.", state->ipfsPath, ipnsPath);

Expand Down