bitcoincore development – What is the motivation behind Russell Yanofsky’s work to separate Bitcoin Core into independent node, wallet and GUI processes?

There are benefits to both users and developers to having Bitcoin Core split into separate node, wallet and GUI processes.

As Alyssa Hertig outlines here the benefit to users will be being able to run the Bitcoin Core node on a different machine to the Bitcoin Core wallet rather than being forced to run them on the same machine. A user could leave a node running continuously in the background but start and stop the wallet and the GUI as needed. It also opens up the prospect of using a different (i.e. not the Bitcoin Core) GUI or wallet with the Bitcoin Core node.

For Bitcoin Core developers, Yanofsky highlights maintainability and security as the key advantages.

Process separation will make Bitcoin Core more easily maintainable as it defines interfaces at process boundaries. Different parts of the code can interact by calling each other instead of sharing state. This helps code review by making it easier to identify dependencies between parts of the code. Defining boundaries in the codebase will also make code review more scalable as reviewers will just need to understand part of the codebase well rather than needing to understand interdependencies across the whole codebase.

From a security perspective, the wallet and node code could run with different privileges and vulnerabilities should be harder to exploit given they will be limited to a single process. Inter-process communication (IPC) makes new debugging tools available such as the IPC_DEBUG environment variable to log all IPC calls.

There are some potential disadvantages that Yanofsky highlights. Inter-process communication is generally slower. IPC code can be tricky to write and may have bugs. Bad interfaces and unnecessary layers of abstraction can make it harder to implement new features. Features such as SPV (Simplified Payment Verification) that cross process boundaries will likely be more difficult to build.

Overall it seems clear the advantages outweigh the disadvantages. At the time of writing (August 2020) there are four remaining PRs to be reviewed and merged into Bitcoin Core and then Bitcoin Core should be multiprocess!

For more details on the process separation project see here.

node js – Limit session per user – ExpressJS

I am using mongoDB as a Database and passportjs for authentication.

I want to have a maximum of 3 sessions per user.

For that, I created a sessionCount field on my mongo document.

everytime the user logs in, I increment the count and when they log out, I decrement.

But the problem arises when the session auto-expires. The session count stays the same.

Q. is there any way to “detect” session expiration so that I could decrement that sessionCount field ?

bitcoin core – How does a full node decide which outbound peers to have?

I understand that a full node can have up to 10 outbound connections (8 full relay peers and 2 block-relay-only peers). I wonder how my full node decides which nodes to connect to for outbound. What are the selection criteria does a node use to evaluate if a peer is good?

If I want to connect to a new specific peer (let’s say I know the node’s owner IRL or something) but I have already reached my outbound capacity, what specific criteria does my node use to decide which outbound peer to drop in order to make room for the new peer?

8 – Map array of node IDs to bundle types without loading all the nodes

For complicated reasons I need to map an array of node IDs to a list of node bundles. That is, for a given list of node IDs, I need to get all the different bundles (node types) of the nodes the IDs belong to. The simple approach would be to load all the nodes, map that to their bundles and using array_unique to get the final list. Unfortunately, this is not a good solution because there may be hundreds of IDs in the array and loading all those nodes will result in a bad performance hit and/or go over the memory limit.

What is the best way to map an array of node IDs to (unique) node bundle types? Is there something built-in or does this require a custom database query? If so, I would be grateful for a dynamic query I could use for this (we’re using MySQL). Thanks!

8 – How to disable redirection to the node after node form submission

I have a node form exposed in the front-end to allow visitors to add their organisation to the list of partners. Below the form, I have exposed view block with the list of existing partners.

When a visitor enters a new organisation to the list I want the form just to add a new node type “partner” and reload the page/block without redirecting to the node content (node/(node ID)).

At the beginning I ed taxonomy terms instead and it worked as it should, now I want to achieve the same with node entity.

I have tried various solutions but none of it works correctly and I didn’t find much by googling it.

For example, this is not functioning:

function THEME_form_alter(&$form, DrupalCoreFormFormStateInterface &$form_state, $form_id) {
  if ($form_id == 'node_partners_form') {
    $form_state->disableRedirect(); // This is not functioning
  }
}

This does not work either:

function THEME_form_alter(&$form, DrupalCoreFormFormStateInterface &$form_state, $form_id) {
  if ($form_id == 'node_partners_form') {
    foreach (array_keys($form('actions')) as $action) {
      if ($action != 'preview' && isset($form('actions')($action)('#type')) && $form('actions')($action)('#type') === 'submit') {
        $form('actions')($action)('#submit')() = 'THEME_node_form_submit';
      }
    }
  }
}

function THEME_node_form_submit($form, &$form_state) {
  $form_state->setRedirect(Url::fromUri('internal:/node/1')->toString());
}

=> Error: Route /partners does not exist.

Value of attribute action in the exposed node form is correct and therefore I think the proper solution should be elsewhere then altering form properties – probably it has more with the Drupal behaviour after adding a new node.

<form action="/en/partners">...</form>

node.js – How to launch a detached child process in Node, and reuse it on subsequent executions if already running?

I want to write a Node CLI that leaves a server/daemon running in the background, so subsequent calls to the CLI are much faster, as it can just print the latest info from the daemon (which is already primed with info as it’s been watching the filesystem).

My purpose is not type-checking, but the CLI architecture I want is similar to how flow seems to work: to be able to launch the background service if it’s not running (unique for the current working directory), or find the existing Node process if it’s already running, and communicate with it either way. Looking at how flow-bin is set up in my local node_modules, it seems to ship with binaries (>20MB each) for the three main platforms (which I guess are all compiled from OCaml?). The flow CLI entry point is actually a tiny Node bin script which just spawns the relevant binary for the current platform. So I guess somehow the binary itself checks for an already-running instance of itself, and shuts itself down if found…? But I’m not sure how that could work, as it would need to still print something to stdout.

Can anyone shed any light on the architecture I want – basically, launching a detached child process from a parent Node process, and then reusing the same child process on subsequent executions of the parent? Are there any other open source examples I could look at, preferably just using plain Node JavaScript instead of OCaml?

node.js – Angular Universal in production with Node Express

I’m trying to deploy an Angular app using Angular universal with Node and Express in the backend.
It worked fine without angular universal, but now I encounter several issues.
Should I simply have a server.ts file, and get rid of the server.js file in the backend since they do exactly the same thing?
The app loads fine initially, but when I try to login, CORS issues arise.
Should I be setting up headers in the server.ts file?/n
Also, the environment variables used are the dev ones, despite adding the file replacement accordingly in the angular.json file.
I’ve run a build:ssr, moved the app in the backend folder, as well as the main.js instantiated by the build in a dist folder.
Any help would be much appreciated.

angular.json:

{
    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
    "version": 1,
    "newProjectRoot": "projects",
    "projects": {
        "mean-app": {
            "root": "",
            "sourceRoot": "src",
            "projectType": "application",
            "prefix": "app",
            "schematics": {
                "@schematics/angular:component": {
                    "style": "sass"
                }
            },
            "architect": {
                "build": {
                    "builder": "@angular-devkit/build-angular:browser",
                    "options": {
                        "outputPath": "backend/mean-app/browser",
                        "index": "src/index.html",
                        "main": "src/main.ts",
                        "polyfills": "src/polyfills.ts",
                        "tsConfig": "src/tsconfig.app.json",
                        "assets": (
                            "src/favicon.ico",
                            "src/assets"
                        ),
                        "styles": (
                            "src/sass/styles.sass",
                            "node_modules/bootstrap/dist/css/bootstrap.css",
                            "node_modules/@fortawesome/fontawesome-pro/js/all.js",
                            "src/sass/my-themes.sass"
                        ),
                        "scripts": (
                            "node_modules/jquery/dist/jquery.js",
                            "node_modules/popper.js/dist/umd/index.js",
                            "node_modules/bootstrap/dist/js/bootstrap.js",
                            "node_modules/@fortawesome/fontawesome-pro/js/all.js"
                        ),
                        "stylePreprocessorOptions": {
                            "includePaths": (
                                "node_modules/",
                                "./src/sass"
                            )
                        }
                    },
                    "configurations": {
                        "production": {
                            "fileReplacements": ({
                                "replace": "src/environments/environment.ts",
                                "with": "src/environments/environment.prod.ts"
                            }),
                            "optimization": true,
                            "outputHashing": "all",
                            "extractCss": true,
                            "namedChunks": false,
                            "aot": true,
                            "extractLicenses": true,
                            "vendorChunk": false,
                            "buildOptimizer": true,
                            "budgets": ({
                                "type": "initial",
                                "maximumWarning": "2mb",
                                "maximumError": "15mb"
                            })
                        }
                    }
                },
                "serve": {
                    "builder": "@angular-devkit/build-angular:dev-server",
                    "options": {
                        "browserTarget": "mean-app:build"
                    },
                    "configurations": {
                        "production": {
                            "browserTarget": "mean-app:build:production"
                        }
                    }
                },
                "extract-i18n": {
                    "builder": "@angular-devkit/build-angular:extract-i18n",
                    "options": {
                        "browserTarget": "mean-app:build"
                    }
                },
                "test": {
                    "builder": "@angular-devkit/build-angular:karma",
                    "options": {
                        "main": "src/test.ts",
                        "polyfills": "src/polyfills.ts",
                        "tsConfig": "src/tsconfig.spec.json",
                        "karmaConfig": "src/karma.conf.js",
                        "styles": (
                            "./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
                            "src/styles.sass"
                        ),
                        "scripts": (),
                        "assets": (
                            "src/favicon.ico",
                            "src/assets"
                        )
                    }
                },
                "lint": {
                    "builder": "@angular-devkit/build-angular:tslint",
                    "options": {
                        "tsConfig": (
                            "src/tsconfig.app.json",
                            "src/tsconfig.spec.json",
                            "src/tsconfig.server.json"
                        ),
                        "exclude": (
                            "**/node_modules/**"
                        )
                    }
                },
                "server": {
                    "builder": "@angular-devkit/build-angular:server",
                    "options": {
                        "outputPath": "dist/mean-app/server",
                        "main": "server.ts",
                        "tsConfig": "src/tsconfig.server.json",
                        "stylePreprocessorOptions": {
                            "includePaths": (
                                "src/",
                                "src/sass",
                                "src/sass/styles.sass",
                                "node_modules"
                            )
                        }
                    },
                    "configurations": {
                        "production": {
                            "outputHashing": "media",
                            "fileReplacements": ({
                                "replace": "src/environments/environment.ts",
                                "with": "src/environments/environment.prod.ts"
                            }),
                            "sourceMap": false,
                            "optimization": true
                        }
                    }
                },
                "serve-ssr": {
                    "builder": "@nguniversal/builders:ssr-dev-server",
                    "options": {
                        "browserTarget": "mean-app:build",
                        "serverTarget": "mean-app:server",
                        "stylePreprocessorOptions": {
                            "includePaths": (
                                "src/sass/styles.sass",
                                "node_modules"
                            )
                        }
                    },
                    "configurations": {
                        "production": {
                            "browserTarget": "mean-app:build:production",
                            "serverTarget": "mean-app:server:production",
                            "fileReplacements": ({
                                "replace": "src/environments/environment.ts",
                                "with": "src/environments/environment.prod.ts"
                            })
                        }
                    }
                },
                "prerender": {
                    "builder": "@nguniversal/builders:prerender",
                    "options": {
                        "browserTarget": "mean-app:build:production",
                        "serverTarget": "mean-app:server:production",
                        "routes": (
                            "/"
                        )
                    },
                    "configurations": {
                        "production": {}
                    }
                }
            }
        },
        "mean-app-e2e": {
            "root": "e2e/",
            "projectType": "application",
            "prefix": "",
            "architect": {
                "e2e": {
                    "builder": "@angular-devkit/build-angular:protractor",
                    "options": {
                        "protractorConfig": "e2e/protractor.conf.js",
                        "devServerTarget": "mean-app:serve"
                    },
                    "configurations": {
                        "production": {
                            "devServerTarget": "mean-app:serve:production"
                        }
                    }
                },
                "lint": {
                    "builder": "@angular-devkit/build-angular:tslint",
                    "options": {
                        "tsConfig": "e2e/tsconfig.e2e.json",
                        "exclude": (
                            "**/node_modules/**"
                        )
                    }
                }
            }
        }
    },
    "defaultProject": "mean-app"
}

server.ts:

import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';

import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
    const server = express();
    const distFolder = join(process.cwd(), 'backend/mean-app/browser');
    const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

    // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
    server.engine('html', ngExpressEngine({
        bootstrap: AppServerModule,
    }));

    server.set('view engine', 'html');
    server.set('views', distFolder);

    // Example Express Rest API endpoints
    // server.get('/api/**', (req, res) => { });
    // Serve static files from /browser
    server.get('*.*', express.static(distFolder, {
        maxAge: '1y'
    }));

    // All regular routes use the Universal engine
    server.get('*', (req, res) => {
        res.render(indexHtml, { req, providers: ({ provide: APP_BASE_HREF, useValue: req.baseUrl }) });
    });

    return server;
}

function run(): void {
    const port = process.env.PORT || 4000;

    // Start up the Node server
    const server = app();
    server.listen(port, () => {
        console.log(`Node Express server listening on http://localhost:${port}`);
    });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
    run();
}

export * from './src/main.server';

app.js:

const express = require('express');
require('./db/mongoose');
const app = express();
const postsRoutes = require('./routers/posts');
const userRoutes = require('./routers/user');
const groupsRoutes = require('./routers/group');
const topicsRoutes = require('./routers/topics');
const path = require('path');

app.use(express.json());
app.use("/images", express.static(path.join(__dirname, "images")));
app.use("/", express.static(path.join(__dirname, "mean-app/browser")));

app.use((req, res, next) => {
    res.setHeader('Access-Control-Allow-Origin', "*");
    res.setHeader('Access-Control-Allow-Headers', "Origin, X-Request-With, Content-Type, Accept, Authorization");
    res.setHeader("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS");
    next();
});

app.use('/api/posts', postsRoutes);
app.use('/api/users', userRoutes);
app.use('/api/groups', groupsRoutes);
app.use('/api/topics', topicsRoutes);
app.use((req, res, next) => {
    res.sendFile(path.join(__dirname, "mean-app/browser", "index.html"));
});

module.exports = app;

server.js

const http = require('http');
const debug = require('debug')("node-kit");
const app = require('./app');
const { socket } = require('./socket');

const normalizePort = val => {
    var port = parseInt(val, 10);
    if (isNaN(port)) {
        return val;
    }
    if (port >= 0) {
        return port;
    }
    return false;
};

const onError = error => {
    if (error.syscall !== 'listen') {
        throw error
    }
    const bind = typeof addr === "string" ? "pipe" + addr : "port" + port;
    switch (error.code) {
        case "EACCES":
            console.error(bind + " requires elevated privileges");
            process.exit(1);
            break;
        case "EADDRINUSE":
            console.error(bind + " is already in use");
            process.exit(1);
            break;
        default:
            throw error;
    }
};

const onListening = () => {
    const addr = server.address();
    const bind = typeof addr === "string" ? "pipe" + addr : "port" + port;
    debug("Listening on " + bind);
};

const port = normalizePort(process.env.PORT || "3000");
app.set('port', port);

const server = http.createServer(app);
server.on('error', onError);
server.on('listening', onListening);
socket(server);

server.listen(port, () => {
    console.log('Server server.js running on port...' + port);
});

7 – How to update value of single field of same node using node_save?

The problem statement:

I have one content type called ‘Employee’ which has 10 nodes. I have to perform the voting for each node. I am saving the vote count in the field called ‘vote count’.

But when I met the below scenario I have an issue of updating vote count for a single employee.

Scenario:
20 people voting for one employee at a time. This means the node will save the 20 times for updating the vote count. This may lead to race_condition while saving the node. But vote count is not updating correctly. This m=is due to race_condition?

I am using the lock_aquire() and lock_release() in below code.

is it right way to use this two functions while saving same node multiple times?
or
any other way to update the count?

I have tried below attempt so far:

  1. added ajax call back in js
jQuery.ajax({
        type: 'POST',
        url: '/ajax/vote',
        dataType: 'json',
        data: {"nid": nid},
         success: function(response){}
});
  1. menu hook with path :

/ajax/vote

  1. A page call back function general_voting_update_vote to save the node;
function general_voting_update_vote() {
          $lock_name = 'register_vote';
          $nodeId = !empty($_POST('nid')) ? $_POST('nid') : '';
          $node = node_load($nodeId);
          $vote = $node->field_vote_count_submitted($node->language)(0)('value') + 1;
          $node->field_vote_count_submitted($node->language)(0)('value') = $vote;
          $lock_acquired = lock_acquire($lock_name, 5.0);
          if (!empty($lock_acquired)) { 
            node_save($node);
          }
          lock_release($lock_name);
}

blocks – Invalid node context using ContextAwarePluginInterface

I am trying to create a block in Drupal 8 that would help me to show an image using a link given in a certain content type link. I tought it would be useful to use the ContextAwarePluginInterface.

I tried a simple example:


namespace Drupalimage_viewPluginBlock;

use DrupalCoreAccessAccessResult;
use DrupalCoreBlockBlockBase;
use DrupalCorePluginContextAwarePluginInterface;
use DrupalCoreFormFormStateInterface;
use DrupalCoreSessionAccountInterface;

/**
 * Currently this is a block with a simple text
 * 
 * @Block(
 *  id = "image_view_block",
 *  admin_label = @Translation("Link to Image"),
 *  context = {
 *      "node" = @ContextDefinition("entity:node")
 *  }
 * )
 */
class LinkToImageBlock extends BlockBase implements ContextAwarePluginInterface{
    /**
     * {@inheritdoc}
     */
    public function build() {
        $context = $this->getContextValue("node");
        return (
            '#markup' => $this->t('Simple Block text. This will be an image, ha ha!'),
        );
    }
}

If I comment the following line everything works fine.

$context = $this->getContextValue("node");

What would cause this? I tried to look it up but didn’t find any answers.

shortest path algorithm – why backtrack from the end node instead of starting from the starting node?

You want to find the shortest paths $S(s,v)$ to all nodes $v$ (31:30 in the video). In other words, we are interested in computing a function $f(v) = S(s,v)$, and, as explained in lecture, you can write it as $f(v) = min_{(u,v) in E} f(u) + w(u,v)$. In the end, it allows you to compute all such values in one go.

If you instead consider an equation $S(s,v) = min_{(s,u) in E} w(s,u) + S(u,v)$, you do can find $S(s,v)$ this way. However, you won’t be able to find $S(s,u)$ for all other $u$ in the process, and you’ll have to run the same algorithm again for other end vertices.