The supplied console application builds

When you open up the Stratis.Bitcoin.FullNode solution in Visual Studio, you will notice that nine projects at the top level. Six of these projects build the Full Node as a console application, and we will be taking a closer look at these. The following table describes the purpose of the six projects:

Project Description
Stratis.BitcoinD Runs the Full Node as a daemon on the Bitcoin network.
Stratis.BreezeD Runs a lightweight version of the Full Node, which supports a Breeze wallet running on either a Stratis or Bitcoin network.
Stratis.PoAChainD Runs the Full Node as a daemon on a test Proof-of-Authority network.
Stratis.StratisD Runs the Full Node as a daemon on the Stratis network.
Stratis.StratisDnsD Runs the Full Node with a DNS service for initial peer discovery.
Stratis.StratisSmartContractsD Runs the Full Node as a daemon on a test network with the Proof-of-Authority consensus algorithm and smart contracts enabled.

If you look inside any of these projects, you will notice a single C# file, program.cs, which contains the entry point, Main(), for the application.

What happens in Main()?

It is in the Main() function that the settings for a Full Node are registered and its features are added in. Let’s take a look at what happens in Main() at the code level using the Stratis.StratisD project as an example.

public class Program
    public static async Task Main(string[] args)
            var nodeSettings = new NodeSettings(networksSelector: Networks.Stratis, protocolVersion: ProtocolVersion.PROVEN_HEADER_VERSION, args: args)
                MinProtocolVersion = ProtocolVersion.ALT_PROTOCOL_VERSION

            IFullNode node = new FullNodeBuilder()

            if (node != null)
                await node.RunAsync();
        catch (Exception ex)
            Console.WriteLine("There was a problem initializing the node. Details: '{0}'", ex.ToString());

Two classes are instantiated in the Main() function: NodeSettings and FullNodeBuilder.

NodeSettings contains the configuration for the node, and FullNodeBuilder is responsible for adding features to the node. Take a look at the fluid interface that creates the instance of the node. To make the settings available to the Full Node Builder, the instance of NodeSettings needs to first be supplied using UseNodeSettings(). Next, all the features are added. Finally, Build() is called, which returns an IFullNode interface. A call is made to IFullNode.RunAsync(), and the node is up and running. All six of the console applications follow this pattern.

If you look at the FullNodeBuilder class, you will notice that Build() is the only function, out of those called in Main(), that is declared in the class. The other functions, which pull in the features, are extension methods. UseNodeSettings() is, for example, declared in the static FullNodeBuilderNodeSettingsExtension class. The other functions are also declared in static extension classes. As you will see, if you create your own Full Node feature, you will create an extension function to register it.

The UseNodeSettings() function is a useful entry point to the next topic. You will notice that the last line in the function makes a call to UseBaseFeature(). The next chapter will explore how features (components) are defined in the Full Node using the Base Feature as an example. Adding the Base Feature in is, in fact, an obligatory step, when building the Full Node, and for this reason, calling UseBaseFeature() before the Build() call is an obligatory step too.


If your goal is to create a build of the Full Node which uses a different set of features to any of the supplied builds, you should be able to create your own modified build now. Look at the existing builds to see the functions that pull in the feature sets. Then create your own project with a modified Program.cs.