Skip to content

Commit

Permalink
Merge pull request #1234 from openkfw/blockchain-wallet-import
Browse files Browse the repository at this point in the history
Add blockchain wallet import endpoint
  • Loading branch information
Stezido committed Aug 11, 2022
2 parents 4d02f7a + c3737fe commit 1c0922a
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 1,683 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add Budgets in Table-View [#1226](https://github.com/openkfw/TruBudget/issues/1226)
- Add Button to view additional data object in Table-View [#1203](https://github.com/openkfw/TruBudget/issues/1203)
- Docker-compose files including a better structure for more transparent configuration [#1046](https://github.com/openkfw/TruBudget/issues/1046)
- Add wallet import endpoint to api [#1061](https://github.com/openkfw/TruBudget/pull/1234)

<!-- ### Changed -->

Expand Down
6 changes: 4 additions & 2 deletions blockchain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ The Blockchain node is fully configured through environment variables.
Depending on the Trubudget setup environment variables

| Env Variable | Required | Default Value | Description |
| --------------------------- | -------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| --------------------------- | -------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| API_HOST | no | | Used to build the URL to the alpha-node's API when requesting network access. (The IP addresses are usually the same as for the P2P host address). |
| API_PORT | no | 8080 | The port used to connect to the alpha-node's api. |
| API_PROTO | no | http | The Protocol which should be used to connect to the alpha-node's api. (http, https) |
Expand All @@ -42,7 +42,7 @@ Depending on the Trubudget setup environment variables
| MULTICHAIN_RPC_USER | no | multichainrpc | The user used to connect to the multichain daemon. |
| MULTICHAIN_RPC_PASSWORD | no | [hardcoded] | Password used by the API to connect to the blockchain. The password is set by the origin node upon start. Every beta node needs to use the same RPC password in order to be able to connect to the blockchain. <br/>**Hint:** Although the MULTICHAIN_RPC_PASSWORD is not required it is highly recommended to set an own secure one |
| MULTICHAIN_RPC_PORT | no | 8000 | The port used to expose the multichain daemon of your Trubudget blockchain installation(bc). The port used to connect to the multichain daemon(api). This will be used internally for the communication between the API and the multichain daemon. |
| PORT | no | 8085 | This is the port where the multichain can be downloaded (backup) | |
| PORT | no | 8085 | This is the port where the multichain can be downloaded (backup) | |
| ACCESS_CONTROL_ALLOW_ORIGIN | no | "\*" | This environment variable is needed for the feature "Export to Excel". Since the export service uses CORS, the domain by which it can be called needs to be set. Setting this value to `"*"` means that it can be called from any domain. Read more about this topic [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). |
| CI_COMMIT_SHA | no | | The /version endpoint returns this variable as `commit` property |
| BUILDTIMESTAMP | no | | The /version endpoint returns this variable as `buildTimeStamp` property |
Expand All @@ -51,6 +51,8 @@ Depending on the Trubudget setup environment variables
| CERT_PATH | no | | The path to the certificate used by the blockchain to authenticate with the connection peer. Note that self-signed certificates are not allowed in production environments. [More information can be found here](https://www.cloudflare.com/en-gb/learning/access-management/what-is-mutual-authentication/) |
| CERT_CA_PATH | no | | The path to the certificate authority root certificate by the blockchain to authenticate with the connection peer. Note that self-signed certificates are not allowed in production environments.[More information can be found here](https://www.cloudflare.com/en-gb/learning/access-management/what-is-mutual-authentication/) |
| CERT_KEY_PATH | no | | The path to the certificate key used by the blockchain to authenticate with the connection peer. [More information can be found here](https://www.cloudflare.com/en-gb/learning/access-management/what-is-mutual-authentication/) |
| MULTICHAIN_FEED_ENABLED | no | false | bc | If set to true the multichain-feed go script in src/multichain-feed/multichain-feed is passed to the multichain daemon and executed in a separate process. |
| AUTOSTART | no | true | bc | If set to false multichain daemon will not start automatically. |

#### Email-Service

Expand Down
113 changes: 91 additions & 22 deletions blockchain/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const KubernetesClient = require("./kubernetesClient");
const log = require("./log/logger");
const logService = require("trubudget-logging-service");
const { version } = require("../package.json");
const shell = require("shelljs");

const {
startEmailNotificationWatcher,
Expand All @@ -19,6 +20,8 @@ const { startBeta, registerNodeAtAlpha } = require("./connectToChain");
const { startMultichainDaemon, configureChain } = require("./createChain");
const { isMultichainReady } = require("./readiness");

const { importWallet, listAvailableWallets } = require("./wallet-backup");

const {
moveBackup,
verifyHashSha256,
Expand All @@ -40,9 +43,9 @@ const RPC_ALLOW_IP = process.env.RPC_ALLOW_IP || "0.0.0.0/0";
const CERT_PATH = process.env.CERT_PATH || undefined;
const CERT_CA_PATH = process.env.CERT_CA_PATH || undefined;
const CERT_KEY_PATH = process.env.CERT_KEY_PATH || undefined;
let AUTOSTART = process.env.AUTOSTART === "false" ? false : true;

let autostart = true;
let isRunning = true;
let isRunning = AUTOSTART ? true : false;

const EXTERNAL_IP = process.env.EXTERNAL_IP;
const P2P_HOST = process.env.P2P_HOST;
Expand All @@ -69,6 +72,8 @@ const MULTICHAIN_FEED_ENABLED =
const isMultichainFeedEnabled =
EMAIL_SERVICE_ENABLED || MULTICHAIN_FEED_ENABLED;

const ENV = process.env.NODE_ENV || "production";

const connectArg = `${CHAINNAME}@${P2P_HOST}:${P2P_PORT}`;

const multichainDir = `${MULTICHAIN_DIR}/.multichain`;
Expand Down Expand Up @@ -123,22 +128,16 @@ const spawnProcess = (startProcess) => {
isRunning = true;
mcproc.on("close", async (code, signal) => {
isRunning = false;
if (!autostart) {
log.info(
`multichaind stopped with exit code ${code} and signal ${signal}. Autorestart is disabled`,
);
} else {
const retryIntervalMs = 10000;
log.info(
`Multichain stopped with exit code ${code} and signal ${signal}. Retry in ${
retryIntervalMs / 1000
} Seconds...`,
);
await new Promise((resolve) => {
setTimeout(resolve, retryIntervalMs);
});
spawnProcess(startProcess);
}
const retryIntervalMs = 10000;
log.info(
`Multichain stopped with exit code ${code} and signal ${signal}. Retry in ${
retryIntervalMs / 1000
} Seconds...`,
);
await new Promise((resolve) => {
setTimeout(resolve, retryIntervalMs);
});
spawnProcess(startProcess);
});
};

Expand All @@ -154,6 +153,13 @@ configureChain(
);

function initMultichain() {
if (!AUTOSTART) {
isRunning = false;
log.info(
"Multichain not started since autostart is disabled. Make sure to set the env variable AUTOSTART to true.",
);
return;
}
if (isAlpha) {
spawnProcess(() =>
startMultichainDaemon(
Expand Down Expand Up @@ -257,7 +263,7 @@ const stopMultichain = async (mcproc) => {
app.get("/chain-sha256", async (req, res) => {
try {
log.info("Start packaging");
autostart = false;
AUTOSTART = false;
await stopMultichain(mcproc);
await createMetadataFileSha256(CHAINNAME, multichainDir, ORGANIZATION);
res.setHeader("Content-Type", "application/gzip");
Expand All @@ -278,7 +284,7 @@ app.get("/chain-sha256", async (req, res) => {
multichainDir,
),
);
autostart = true;
AUTOSTART = true;
},
})
.pipe(res);
Expand Down Expand Up @@ -334,6 +340,69 @@ const loadConfig = (path) => {
return config;
};

app.post("/restoreWallet", async (req, res) => {
if (!ENV === "development") {
return res.status(401).send();
}

const extractPath = `/tmp/backup${Date.now()}`;
try {
const unTARer = rawTar.extract();
unTARer.on("error", (err) => {
log.error({ err }, "Error while extracting rawTar: ");
unTARer.destroy();
res.status(400).send(err.message);
});
const extract = tar.extract(extractPath, { extract: unTARer });
const file = streamifier.createReadStream(req.body);
const stream = file.pipe(extract);
stream.on("finish", async () => {
try {
AUTOSTART = false;
if (isRunning) await stopMultichain(mcproc);
await importWallet(`${extractPath}`, CHAINNAME);
if (isMultichainFeedEnabled) {
log.info("Multichain feed is enabled");
shell.exec(`cat <<EOF >"${multichainDir}/multichain.conf"
rpcport=${MULTICHAIN_RPC_PORT}
rpcuser=${MULTICHAIN_RPC_USER}
rpcpassword=${MULTICHAIN_RPC_PASSWORD}
rpcallowip=${RPC_ALLOW_IP}
walletnotifynew=${__dirname}/multichain-feed/multichain-feed %j`);
} else {
shell.exec(`cat <<EOF >"${multichainDir}/multichain.conf"
rpcport=${MULTICHAIN_RPC_PORT}
rpcuser=${MULTICHAIN_RPC_USER}
rpcpassword=${MULTICHAIN_RPC_PASSWORD}
rpcallowip=${RPC_ALLOW_IP}`);
}
await spawnProcess(() =>
startMultichainDaemon(
CHAINNAME,
externalIpArg,
blockNotifyArg,
P2P_PORT,
multichainDir,
),
);
AUTOSTART = true;
/*eslint no-promise-executor-return: "off"*/
await new Promise((resolve) => setTimeout(resolve, 10000));
const availableWallets = await listAvailableWallets(CHAINNAME);
return res.json(
`Ok. Available wallets are: ${JSON.stringify(availableWallets)}`,
);
} catch (err) {
log.error({ err }, "Error while trying to restore wallet: ");
return res.status(500).send(err.message);
}
});
} catch (err) {
log.error({ err }, "Error while trying to restore wallet: ");
return res.status(500).send(err.message);
}
});

app.post("/chain", async (req, res) => {
const extractPath = `/tmp/backup${Date.now()}`;
const metadataPath = `${extractPath}/metadata.yml`;
Expand Down Expand Up @@ -369,7 +438,7 @@ app.post("/chain", async (req, res) => {

if (correctConfig && compatibleVersions) {
if (validSha256) {
autostart = false;
AUTOSTART = false;
await stopMultichain(mcproc);
await moveBackup(multichainDir, extractPath, CHAINNAME);
spawnProcess(() =>
Expand All @@ -381,7 +450,7 @@ app.post("/chain", async (req, res) => {
multichainDir,
),
);
autostart = true;
AUTOSTART = true;
res.send("OK");
} else {
log.warn("Request did not contain a valid trubudget backup");
Expand Down
35 changes: 35 additions & 0 deletions blockchain/src/wallet-backup/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const util = require("util");
const exec = util.promisify(require("child_process").exec);
const log = require("./../log/logger");
const shell = require("shelljs");

const importWallet = async (walletPath, chainName) => {
console.log("importing params ...");
shell.mv(
`${walletPath}/params.dat`,
`/root/.multichain/${chainName}/params.dat`,
);
shell.mv(
`${walletPath}/params.dat.bak`,
`/root/.multichain/${chainName}/params.dat.bak`,
);

console.log("importing wallet...");
shell.mv(`${walletPath}/wallet`, `/root/.multichain/${chainName}/wallet`);
shell.mv(
`${walletPath}/wallet.dat`,
`/root/.multichain/${chainName}/wallet.dat`,
);
};

const listAvailableWallets = async (chainName) => {
const { stdout } = await exec(`multichain-cli ${chainName} getaddresses`);
log.debug({ message: "Available wallets:", stdout });

return stdout;
};

module.exports = {
importWallet,
listAvailableWallets,
};
4 changes: 2 additions & 2 deletions bump-tag-versions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ for project in "${trubudget_projects[@]}"; do
eval "cd $project"
echo "Bumping $project ..."
eval "perl -pi -e 's/\"version\": .*/\"version\": \"$trubudget_version\",/' ./package.json"
eval "npm install --no-audit"
eval "npm install --legacy-peer-deps --no-audit"
eval "npm audit fix"
if [ $project == frontend ]; then
echo "Auditing only production dependencies ..."
Expand All @@ -28,4 +28,4 @@ done
eval "npm install --no-audit"
eval "npm audit fix"
echo "Auditing dependencies ..."
eval "npm run audit"
eval "npm run audit"
2 changes: 2 additions & 0 deletions docker-compose/.env_example
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ EXTERNAL_ALPHA_API_HOST=127.0.0.1
EXTERNAL_ALPHA_API_PORT=8080
EXTERNAL_ALPHA_API_PROTO=http
BLOCKNOTIFY_SCRIPT=""
MULTICHAIN_FEED_ENABLED=""
AUTOSTART=true

# api
ROOT_SECRET=root-secret
Expand Down
27 changes: 6 additions & 21 deletions docker-compose/blockchain/docker-compose.alphanode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,12 @@ services:
blockchain:
image: trubudget/blockchain:${TAG}
environment:
PORT: ${BLOCKCHAIN_PORT}
MULTICHAIN_RPC_PORT: ${MULTICHAIN_RPC_PORT}
MULTICHAIN_RPC_PASSWORD: ${MULTICHAIN_RPC_PASSWORD}
MULTICHAIN_RPC_USER: ${MULTICHAIN_RPC_USER}
RPC_ALLOW_IP: ${RPC_ALLOW_IP}
ORGANIZATION: ${ORGANIZATION}
JWT_SECRET: ${JWT_SECRET}
LOG_LEVEL: ${BLOCKCHAIN_LOG_LEVEL}
PRETTY_PRINT: ${PRETTY_PRINT}
NODE_ENV: ${ENVIRONMENT}
CERT_PATH: ${CERT_PATH}
CERT_CA_PATH: ${CERT_CA_PATH}
CERT_KEY_PATH: ${CERT_KEY_PATH}
BLOCKNOTIFY_SCRIPT: ${BLOCKNOTIFY_SCRIPT}
KUBE_SERVICE_NAME: ${KUBE_SERVICE_NAME}
KUBE_NAMESPACE: ${KUBE_NAMESPACE}
EXPOSE_MC: ${EXPOSE_MC}
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
BUILDTIMESTAMP: ${BUILDTIMESTAMP}
EXTERNAL_IP: ${EXTERNAL_IP}
MULTICHAIN_DIR: ${MULTICHAIN_DIR}
BLOCKCHAIN_PORT: 8085
CHAINNAME: TrubudgetChain
MULTICHAIN_RPC_PASSWORD: k1oR5s12kY93NaUz1w5nQHJynqTtLw8Lk68E7KwWHQfW
MULTICHAIN_RPC_PORT: 8000
ORGANIZATION: KfW
P2P_PORT: 7447
# networks:
# mynetwork:
# ipv4_address: 172.20.0.11
Expand Down
2 changes: 2 additions & 0 deletions docker-compose/blockchain/docker-compose.betanode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ services:
NOTIFICATION_PATH: ${NOTIFICATION_PATH}
NOTIFICATION_MAX_LIFETIME: ${NOTIFICATION_MAX_LIFETIME}
NOTIFICATION_SEND_INTERVAL: ${NOTIFICATION_SEND_INTERVAL}
MULTICHAIN_FEED_ENABLED: ${MULTICHAIN_FEED_ENABLED}
AUTOSTART: ${AUTOSTART}
# networks:
# mynetwork:
# ipv4_address: 172.20.0.11
Expand Down
Loading

0 comments on commit 1c0922a

Please sign in to comment.