Skip to content

Commit

Permalink
Merge pull request #268 from dappnode/v0.2.12
Browse files Browse the repository at this point in the history
v0.2.12 Release
  • Loading branch information
eduadiez authored Oct 1, 2019
2 parents 7b6dad7 + 4a38dab commit 4c04b7b
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 56 deletions.
74 changes: 41 additions & 33 deletions build/src/src/calls/fetchDirectory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,47 +54,55 @@ export default async function fetchDirectory(): Promise<
const dnpsFromDirectory = await getDirectory();

// Extend package object contents
dnpsCache = await Promise.all(
const dnpsCacheTemp: (DirectoryDnp | undefined)[] = await Promise.all(
dnpsFromDirectory.map(async pkg => {
const name = pkg.name;
// Now resolve the last version of the package
const release = await getRelease(name);
const legacyManifest = getLegacyManifestFromRelease(release);
emitPkg({ ...pkg, name, manifest: legacyManifest });
try {
const name = pkg.name;
// Now resolve the last version of the package
const release = await getRelease(name);
const legacyManifest = getLegacyManifestFromRelease(release);
emitPkg({ ...pkg, name, manifest: legacyManifest });

// Fetch the package avatar
const avatarFile = release.avatarFile;
let avatar;
if (avatarFile && isIpfsHash(avatarFile.hash)) {
const avatarHash = avatarFile.hash;
try {
// Retrieve cached avatar or fetch it
if (avatarCache[avatarHash]) {
avatar = avatarCache[avatarHash];
} else {
avatar = await getAvatar(avatarHash);
avatarCache[avatarHash] = avatar;
// Fetch the package avatar
const avatarFile = release.avatarFile;
let avatar;
if (avatarFile && isIpfsHash(avatarFile.hash)) {
const avatarHash = avatarFile.hash;
try {
// Retrieve cached avatar or fetch it
if (avatarCache[avatarHash]) {
avatar = avatarCache[avatarHash];
} else {
avatar = await getAvatar(avatarHash);
avatarCache[avatarHash] = avatar;
}
emitPkg({ ...pkg, name, avatar });
} catch (e) {
// If the avatar can not be fetched don't stop the function
logs.error(
`Error fetching avatar of ${name} at ${avatarHash}: ${e.message}`
);
}
emitPkg({ ...pkg, name, avatar });
} catch (e) {
// If the avatar can not be fetched don't stop the function
logs.error(
`Error fetching avatar of ${name} at ${avatarHash}: ${e.message}`
);
}
}

// Merge results and return
return {
...pkg,
name,
// Appended
manifest: legacyManifest,
avatar
};
// Merge results and return
return {
...pkg,
name,
// Appended
manifest: legacyManifest,
avatar
};
} catch (e) {
logs.error(`Error fetching ${name} release: ${e.message}`);
}
})
);

// Make sure the order is correct
dnpsCache = [];
for (const dnp of dnpsCacheTemp) if (dnp) dnpsCache.push(dnp);

const payloadSize = Math.floor(
Buffer.byteLength(JSON.stringify(dnpsCache), "utf8") / 1000
);
Expand Down
77 changes: 57 additions & 20 deletions build/src/src/watchers/chains/bitcoin.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
const Client = require("bitcoin-core");
import shell from "../../utils/shell";
import params from "../../params";
import { ChainData } from "../../types";

const MIN_BLOCK_DIFF_SYNC = 3;
const getMinBlockDiffSync = (api: string) =>
// minTimeDiff = 30 min
api.includes("bitcoin")
? 30 / 10
: // ZCash, Litecoin, etc
30 / 2.5;

const apiPrefix = "my.";

// After revising 'bitcoin-core' source code,
// there is no problem in creating a new instance of Client on each request
Expand Down Expand Up @@ -36,27 +44,17 @@ export default async function bitcoin(
): Promise<ChainData> {
// To initialize the bitcoin client, the RPC user and password are necessary
// They are stored in the package envs
const cmd = `docker inspect --format='{{.Config.Env}}' DAppNodePackage-bitcoin.dnp.dappnode.eth`;
let envsString = await shell(cmd);
if (typeof envsString !== "string") throw Error("Can't read bitcoin ENVs");
// envsString = '[BTC_RPCUSER=dappnode BTC_RPCPASSWORD=dappnode BTC_TXINDEX=1 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin]';
if (envsString.startsWith("[")) envsString = envsString.substring(1);
if (envsString.endsWith("]"))
envsString = envsString.substring(0, envsString.length - 1);
let rpcUser;
let rpcPassword;
envsString.split(" ").forEach(envPair => {
if (envPair.startsWith("BTC_RPCUSER")) rpcUser = envPair.split("=")[1];
if (envPair.startsWith("BTC_RPCPASSWORD"))
rpcPassword = envPair.split("=")[1];
});
if (typeof rpcUser !== "string") throw Error("Couldn't get rpcUser");
if (typeof rpcPassword !== "string") throw Error("Couldn't get rpcPassword");
const containerName = getContainerNameFromApi(api);
const cmd = `docker inspect --format='{{.Config.Env}}' ${containerName}`;
const envsString = await shell(cmd);
if (!envsString) throw Error(`Error reading ${containerName} ENVs`);
const { username, password, port } = parseCredentialsFromEnvs(envsString);

const client = new Client({
host: api,
password: rpcUser,
username: rpcPassword
username,
password,
port // If port is falsy, it will take the default value. From source code: `this.port = port || networks[network];`
});
const blockIndex = await client.getBlockCount();
// If the cached blockIndex is the same, return cached block
Expand All @@ -69,7 +67,7 @@ export default async function bitcoin(
const secondsDiff = Math.floor(Date.now() / 1000) - block.time;
const blockDiffAprox = Math.floor(secondsDiff / (60 * 10));

if (blockDiffAprox > MIN_BLOCK_DIFF_SYNC)
if (blockDiffAprox > getMinBlockDiffSync(api))
return {
name,
syncing: true,
Expand All @@ -85,3 +83,42 @@ export default async function bitcoin(
message: `Synced #${blockIndex}`
};
}

// Util

/**
* Parses the username and password from the ENVs in a one line format
* @param envLine
* "[BTC_RPCUSER=dappnode BTC_RPCPASSWORD=dappnode BTC_TXINDEX=1 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin]"
* "[ZCASH_RPCUSER=dappnode ZCASH_RPCPASSWORD=dappnode ZCASH_RPCPORT=8342 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin]"
*/
export function parseCredentialsFromEnvs(
envLine: string
): {
username: string;
password: string;
port: number | null;
} {
const envLineClean = envLine.replace("[", "").replace("]", "");
const envPairs = envLineClean.split(" ").map(envPair => {
const [key, value] = (envPair || "").trim().split(/=(.*)/);
return { key, value };
});
const userEnv = envPairs.find(({ key }) => key.includes("_RPCUSER"));
const passEnv = envPairs.find(({ key }) => key.includes("_RPCPASSWORD"));
const portEnv = envPairs.find(({ key }) => key.includes("_RPCPORT"));
if (!userEnv || !userEnv.value) throw Error("Couldn't get RPCUSER");
if (!passEnv || !passEnv.value) throw Error("Couldn't get RPCPASSWORD");
const port = portEnv ? portEnv.value : null;
return {
username: userEnv.value,
password: passEnv.value,
port: port && !isNaN(parseInt(port)) ? parseInt(port) : null
};
}

export function getContainerNameFromApi(api: string): string {
const dnpName = api.split(apiPrefix)[1];
if (!dnpName) throw Error(`Expected API format my.<dnpName>`);
return `${params.CONTAINER_NAME_PREFIX}${dnpName}`;
}
50 changes: 50 additions & 0 deletions build/src/test/watchers/chains/bitcoin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import "mocha";
import { expect } from "chai";

import {
parseCredentialsFromEnvs,
getContainerNameFromApi
} from "../../../src/watchers/chains/bitcoin";

describe("Watchers > chains > bitcoin", () => {
describe("parseCredentialsFromEnvs", () => {
it("Should parse bitcoin envs", () => {
const envLine =
"[BTC_RPCUSER=dappnode BTC_RPCPASSWORD=dappnode BTC_TXINDEX=1 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin]";
expect(parseCredentialsFromEnvs(envLine)).to.deep.equal({
username: "dappnode",
password: "dappnode",
port: null
});
});

it("Should parse zcash envs", () => {
const envLine =
"[ZCASH_RPCUSER=dappnode ZCASH_RPCPASSWORD=dappnode ZCASH_RPCPORT=8342 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin]";
expect(parseCredentialsFromEnvs(envLine)).to.deep.equal({
username: "dappnode",
password: "dappnode",
port: 8342
});
});
});
describe("getContainerNameFromApi", () => {
it("Should return correct container name for bitcoin", () => {
expect(getContainerNameFromApi("my.bitcoin.dnp.dappnode.eth")).to.equal(
"DAppNodePackage-bitcoin.dnp.dappnode.eth"
);
});

it("Should return correct container name for zcash", () => {
expect(getContainerNameFromApi("my.zcash.dnp.dappnode.eth")).to.equal(
"DAppNodePackage-zcash.dnp.dappnode.eth"
);
});

it("Should return correct container name", () => {
expect(function() {
getContainerNameFromApi("http://bitcoin.dappnode");
}).to.throw("Expected API format my.<dnpName>");
});
});
});
4 changes: 2 additions & 2 deletions dappnode_package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "dappmanager.dnp.dappnode.eth",
"version": "0.2.11",
"version": "0.2.12",
"description": "Dappnode package responsible for providing the DappNode Package Manager",
"avatar": "/ipfs/QmdT2GX9ybaoE25HBk7is8CDnfn2KaFTpZVkLdfkAQs1PN",
"avatar": "/ipfs/QmfTpBLzoSdrG88ETRnDus27DTDRUrTXyyVmhXDuMNYVaN",
"type": "dncore",
"image": {
"path": "",
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ services:
build:
context: .
dockerfile: ./build/Dockerfile
image: 'dappmanager.dnp.dappnode.eth:0.2.11'
image: 'dappmanager.dnp.dappnode.eth:0.2.12'
container_name: DAppNodeCore-dappmanager.dnp.dappnode.eth
restart: always
volumes:
Expand Down

0 comments on commit 4c04b7b

Please sign in to comment.