> For the complete documentation index, see [llms.txt](https://bitcoin-scaling-labs-docs.gitbook.io/ipc-btc-scaling-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bitcoin-scaling-labs-docs.gitbook.io/ipc-btc-scaling-docs/user-guide-for-using-subnets/optional-manual-subnet-creation-and-deployment.md).

# Optional: Manual subnet creation and deployment

## Alternative: Install on the host machine

If in page [Installation](/ipc-btc-scaling-docs/user-guide-for-using-subnets/installation.md) you chose the alternative deployment — hence `bitcoin-core` , the monitors and providers, and `ipc-cli` are running on your host machine — then you **must** also use this alternative approach here to create a child subnet.

Even if you used the default Docker-based deployment, you still have the option to use this alternative approach here to create a child subnet. In that case, you need to run all following commands from inside the `bitcoin-ipc` container — the `ipc` and `bitcoin-ipc` repos can be found in the `/root/workspace` directory.

### Step 1: Create a child subnet

We can now create a subnet under `/b4` — Bitcoin Regtest. If you have not created the env variable, replace `$WHITELIST` with the one you created earlier.

```bash
ipc-cli --config-path ~/.ipc/validator1/config.toml subnet create \
	--parent /b4 --min-validators 4 --bottomup-check-period 60 \
	btc --min-validator-stake 100000000 --min-cross-msg-fee 10 \
	--validator-whitelist $WHITELIST
```

You should see the subnet ID printed to the console. Let's save the subnet ID for later use — we will refer to it as `$SUBNET_ID`.

```sh
export SUBNET_ID="the_new_subnet_id_from_terminal"
```

You can now start the *miner* helper script (as described in [Optional: Helper scripts](/ipc-btc-scaling-docs/user-guide-for-using-subnets/optional-helper-scripts.md)) by running the following **from the bitcoin-ipc repo**:

```sh
./scripts/miner.sh
```

All monitors should have printed the subnet create message to the console.

{% hint style="success" %}

* With the provided command, validator 1 will pay the Bitcoin fees (as the specified config file points to the provider of validator 1), but it could be any of the validators or someone else entirely.
* The parameter `--bottomup-check-period` defines how often (in number of blocks) the subnet will create a checkpoint message. As we will discuss in the next step, withdrawals and cross-subnet transfers are included in the checkpoint message. A higher checkpoint period implies higher latency for transfers and withdrawals, a lower valuer implies more fees paid for submitting the checkpoint messages to the parent. For testing the code we recommend a value between 50 and 200.
  {% endhint %}

### Step 2: Update the config.toml files

We now need to add the new subnet configuration to **all** of the following configuration files:

* `~/.ipc/validator1/config.toml`
* `~/.ipc/validator2/config.toml`
* `~/.ipc/validator3/config.toml`
* `~/.ipc/validator4/config.toml`&#x20;
* `~/.ipc/validator5/config.toml`&#x20;
* `~/.ipc/user1/config.toml`&#x20;
* `~/.ipc/user2/config.toml`&#x20;
* `~/.ipc/config.toml`&#x20;

It might be easier to leave those files open in an editor, so we can modify them later when we create a second subnet.&#x20;

A subnet configuration looks as follows. For ease of use, each `config.toml` file already contains the entry — you only have to **replace** `/b4/t410fyi77izvsakigfjnocb7f3hm52bjdeb76jayo4ti` with your SubnetID.&#x20;

```toml
# Subnet A
[[subnets]]
id = "/b4/t410fyi77izvsakigfjnocb7f3hm52bjdeb76jayo4ti"

[subnets.config]
network_type = "fevm"
provider_http = "http://localhost:8845/"
gateway_addr = "0x77aa40b105843728088c0132e43fc44348881da8"
registry_addr = "0x74539671a1d2f1c8f200826baba665179f53a1b7"
```

{% hint style="success" %}

* The ETH addresses for `gateway_addr` and `registry_addr` used when they are deployed in genesis in a child subnet by Fendermint are `0x77aa40b105843728088c0132e43fc44348881da8` and `0x74539671a1d2f1c8f200826baba665179f53a1b7`, respectively, so no need to change them.
* The configuration files may contain multiple `[[subnets]]` entries.
* Notice that each validator's `config.toml` files uses a different port in the `provider_http` field. This is because each validator interacts with its own docker container. Users interact with the container of validator 1.
* We update the config file of validator 5 already at this step. We will only use that validator at [Validator Operations within subnet](/ipc-btc-scaling-docs/user-guide-for-using-subnets/validator-operations.md).
  {% endhint %}

### Step 3: Join the subnet

Let's have four validators join the subnet with some initial collateral.

{% code overflow="wrap" %}

```sh
ipc-cli --config-path ~/.ipc/validator1/config.toml subnet join --from $IPC_ADDRESS_OF_VALIDATOR_1 --subnet $SUBNET_ID btc --collateral=200000000 --ip 66.222.44.55:8080 --backup-address "$(bitcoin-cli --rpcwallet=validator1 getnewaddress)"

ipc-cli --config-path ~/.ipc/validator2/config.toml subnet join --from $IPC_ADDRESS_OF_VALIDATOR_2 --subnet $SUBNET_ID btc --collateral=110000000 --ip 66.222.44.55:8081 --backup-address "$(bitcoin-cli --rpcwallet=validator2 getnewaddress)"

ipc-cli --config-path ~/.ipc/validator3/config.toml subnet join --from $IPC_ADDRESS_OF_VALIDATOR_3 --subnet $SUBNET_ID btc --collateral=150000000 --ip 66.222.44.55:8082 --backup-address "$(bitcoin-cli --rpcwallet=validator3 getnewaddress)"

ipc-cli --config-path ~/.ipc/validator4/config.toml subnet join --from $IPC_ADDRESS_OF_VALIDATOR_4 --subnet $SUBNET_ID btc --collateral=180000000 --ip 66.222.44.55:8083 --backup-address "$(bitcoin-cli --rpcwallet=validator4 getnewaddress)"
```

{% endcode %}

Let's include the join transactions in the blockchain by mining a block (not needed if you are running the `miner.sh`  script).&#x20;

```sh
bitcoin-cli generatetoaddress 1 "$(bitcoin-cli --rpcwallet=default getnewaddress)"
```

We should see the monitors print the join messages to the console, and also reporting that the subnet has been bootstrapped, like so:

{% code overflow="wrap" %}

```log
[2025-05-12T10:41:29Z INFO  monitor] Processed JoinSubnet for Subnet ID: /b4/t410fyi77izvsakigfjnocb7f3hm52bjdeb76jayo4ti Validator XPK: 851c1bda327584479e98a7c28ea7adc097d290efd105310bcf714231bb99faf4 Collateral: 1.10000000 BTC
[2025-05-12T10:41:29Z INFO  monitor] Subnet ID: /b4/t410fyi77izvsakigfjnocb7f3hm52bjdeb76jayo4ti has been bootstrapped
```

{% endcode %}

{% hint style="success" %}
The `--backup-address` will be used to the return the collateral when the validator leaves the subnet.
{% endhint %}

### Step 4: Fund the validators

{% hint style="warning" %}
It is crucial that a validator funds itself in the subnet **before** deploying the infrastructure. This because some actors (smart contracts, in the language of IPC) are initialized when the validator is funded. Without these actors the subnet will still make progress, but certain functionalities (such as the top-down messaging) will not work. Hence, please make sure you fund the validators before proceeding to the next step.
{% endhint %}

All validators need to have some balance in the subnet, as they use it to pay transaction fees. Let's fund their accounts with wBTC.

{% code overflow="wrap" %}

```sh
ipc-cli --config-path ~/.ipc/validator1/config.toml cross-msg fund --subnet=$SUBNET_ID btc --to $IPC_ADDRESS_OF_VALIDATOR_1 210000000

ipc-cli --config-path ~/.ipc/validator2/config.toml cross-msg fund --subnet=$SUBNET_ID btc --to $IPC_ADDRESS_OF_VALIDATOR_2 220000000

ipc-cli --config-path ~/.ipc/validator3/config.toml cross-msg fund --subnet=$SUBNET_ID btc --to $IPC_ADDRESS_OF_VALIDATOR_3 230000000

ipc-cli --config-path ~/.ipc/validator4/config.toml cross-msg fund --subnet=$SUBNET_ID btc --to $IPC_ADDRESS_OF_VALIDATOR_4 240000000
```

{% endcode %}

Make sure you mine a block (not needed if you are running the `miner.sh`  script).

```bash
bitcoin-cli generatetoaddress 1 "$(bitcoin-cli --rpcwallet=default getnewaddress)"
```

You should see the logs being printed, like so:

{% code overflow="wrap" %}

```log
[2025-05-12T10:41:29Z INFO  monitor] Processed FundSubnet for Subnet ID: /b4/t410fyi77izvsakigfjnocb7f3hm52bjdeb76jayo4ti Address: 0x27B60D9f71D6806cCa7D5A92b391093FE100f8e8 Amount: 2.40000000 BTC
```

{% endcode %}

### **Step 5A: Spin-up docker containers using script**

We have provided a script that automates spinning up the subnets. The script assumes that you have used the ports, wallet names, and authentication tokens as described in these docs, and that you have already created and joined a subnet with four validators, and that the monitor and provider binaries for those validators are running.&#x20;

Make sure **docker is running** on your machine.

The only input to the script is the subnet id. **From the bitcoin-ipc repo**, run

```bash
./scripts/spin_up_subnet_a.sh $SUBNET_ID
```

{% hint style="warning" %}
**Attention:**\
The scripts outputs a `CometBFT ID` and a `Resolver Address` . This looks like:

```bash
CometBFT ID: 1938b373fb77b24215320bc31dbf7f33469d980e
Resolver Address: 16Uiu2HAmURvM8XziBhzB57YBMQ2fiT8RvUkGpACeCzn1rV8zQQVv
```

We will need this information later when we start a fifth validator for the subnet. Please **note them down and/or create the following environment variables**:

```bash
export CometBftID=<The CometBFT ID>
export ResolverAddress=<The Resolver Address>
```

{% endhint %}

Verify on docker that the containers are up and running — for each validator there should be three containers: `validatorX-fendermint` , `validatorX-cometbft` , and `validatorX-ethapi` .

You can now use the the scripts provided in [Optional: Helper scripts](/ipc-btc-scaling-docs/user-guide-for-using-subnets/optional-helper-scripts.md) to check the status of the subnet.

In particular, you can verify that the subnet is producing blocks:

```bash
./scripts/l2_block_checker.sh $SUBNET_ID
```

and check the balances in the subnet

```bash
./scripts/balancer.sh $SUBNET_ID
```

```bash
ipc-cli wallet balances --wallet-type btc --subnet $SUBNET_ID
```

### Step 5B (Optional): Deploy the infrastructure using cargo make

Instead of using the `spin_up_subnet_a.sh` script, you can manually start the validator.

Let's start with the first validator, which the rest of the validators will bootstrap from. Make sure you have docker running before running this command.

```sh
cargo make --makefile infra/fendermint/Makefile.toml \
    -e NODE_NAME=validator-1 \
    -e SUBNET_ID=<SUBNET_ID> \
    -e PRIVATE_KEY_PATH=$HOME/.ipc/validator1/validator.sk \
    -e CMT_P2P_HOST_PORT=26656 \
    -e CMT_RPC_HOST_PORT=26657 \
    -e ETHAPI_HOST_PORT=8545 \
    -e RESOLVER_HOST_PORT=26655 \
    -e PARENT_ENDPOINT="http://host.docker.internal:3030/api" \
    -e PARENT_AUTH_TOKEN="validator1_auth_token" \
    -e TOPDOWN_CHAIN_HEAD_DELAY=0 \
    -e TOPDOWN_PROPOSAL_DELAY=0 \
    -e FM_PULL_SKIP=1 \
    child-validator
```

{% hint style="success" %}
`TOPDOWN_CHAIN_HEAD_DELAY` specifies  "the number of blocks to delay before reporting a height as final on the parent chain". `TOPDOWN_PROPOSAL_DELAY` specifies "the number of blocks on top of `TOPDOWN_CHAIN_HEAD_DELAY` to wait before proposing a height as final on the parent chain". These parameters are used by standard IPC, but when the parent chain is bitcoin the finalization period is handled by the bitcoin monitor (by default, 0 blocks for bitcoin regtest and 6 for bitcoin mainnet, but these values can be configured). Hence, we do not need to specify additional delay on the fendermint level.
{% endhint %}

Once the first validator is up and running, it will print out the relative information for this validator.

This is shown in [this video guide](https://drive.google.com/file/d/1AKVxciZBd111KK2auZTlMEn2CkRW_SZ4/view?usp=drive_link).

```
#################################
#                               #
# Subnet node ready! 🚀         #
#                               #
#################################

Subnet ID:
	/b4/t410f6b2qto756ox3qfoonq4ii6pdrylxwyretgpixuy

Eth API:
	http://0.0.0.0:8545

Chain ID:
	3684170297508395

Fendermint API:
	http://localhost:26658

CometBFT API:
	http://0.0.0.0:26657

CometBFT node ID:
	ca644ac3194d39a2834f5d98e141d682772c149b

CometBFT P2P:
	http://0.0.0.0:26656

IPLD Resolver Multiaddress:
	/ip4/0.0.0.0/tcp/26655/p2p/16Uiu2HAkwhrWn9hYFQMR2QmW5Ky7HJKSGVkT8xKnQr1oUGCkqWms

```

You'll need the final component of the `IPLD Resolver Multiaddress` (the `peer ID`) and the `CometBFT node ID` for the next nodes to start.

* ***BOOTSTRAPS***: \<CometBFT node ID for validator1>@validator-1-cometbft:26656

  ```
  // An example
  ca644ac3194d39a2834f5d98e141d682772c149b@validator-1-cometbft:26656
  ```
* ***RESOLVER\_BOOTSTRAPS***: /dns/validator-1-fendermint/tcp/26655/p2p/\<Peer ID in IPLD Resolver Multiaddress>, where \<PeerID> is the last part of the IPLD Resolver Multiaddress

  <pre><code>// An example
  <strong>/dns/validator-1-fendermint/tcp/26655/p2p/16Uiu2HAkwhrWn9hYFQMR2QmW5Ky7HJKSGVkT8xKnQr1oUGCkqWms
  </strong></code></pre>

{% hint style="warning" %}
**Attention:**

We will need this information later when we start a fifth validator for the subnet. Please **note them down and/or create the following environment variables**:

```bash
export CometBftID=<The CometBFT ID>
export ResolverAddress=<The Resolver Address>
```

{% endhint %}

Now, let's start the rest of the validators:

```sh
# Run second validator
cargo make --makefile infra/fendermint/Makefile.toml \
	-e NODE_NAME=validator-2 \
	-e SUBNET_ID=<SUBNET_ID> \
	-e PRIVATE_KEY_PATH=$HOME/.ipc/validator2/validator.sk \
	-e CMT_P2P_HOST_PORT=26756 \
	-e CMT_RPC_HOST_PORT=26757 \
	-e ETHAPI_HOST_PORT=8645 \
	-e RESOLVER_HOST_PORT=26755 \
	-e BOOTSTRAPS=<BOOTSTRAPS> \
	-e RESOLVER_BOOTSTRAPS=<RESOLVER_BOOTSTRAPS> \
	-e PARENT_ENDPOINT="http://host.docker.internal:3031/api" \
	-e PARENT_AUTH_TOKEN="validator2_auth_token" \
	-e TOPDOWN_CHAIN_HEAD_DELAY=0 \
    	-e TOPDOWN_PROPOSAL_DELAY=0 \
	-e FM_PULL_SKIP=1 \
	child-validator

# Run third validator
cargo make --makefile infra/fendermint/Makefile.toml \
	-e NODE_NAME=validator-3 \
	-e SUBNET_ID=<SUBNET_ID> \
	-e PRIVATE_KEY_PATH=$HOME/.ipc/validator3/validator.sk \
	-e CMT_P2P_HOST_PORT=26856 \
	-e CMT_RPC_HOST_PORT=26857 \
	-e ETHAPI_HOST_PORT=8745 \
	-e RESOLVER_HOST_PORT=26855 \
	-e BOOTSTRAPS=<BOOTSTRAPS> \
	-e RESOLVER_BOOTSTRAPS=<RESOLVER_BOOTSTRAPS> \
	-e PARENT_ENDPOINT="http://host.docker.internal:3032/api" \
	-e PARENT_AUTH_TOKEN="validator3_auth_token" \
	-e TOPDOWN_CHAIN_HEAD_DELAY=0 \
	-e TOPDOWN_PROPOSAL_DELAY=0 \
	-e FM_PULL_SKIP=1 \
	child-validator

# Run fourth validator
cargo make --makefile infra/fendermint/Makefile.toml \
	-e NODE_NAME=validator-4 \
	-e SUBNET_ID=<SUBNET_ID> \
	-e PRIVATE_KEY_PATH=$HOME/.ipc/validator4/validator.sk \
	-e CMT_P2P_HOST_PORT=26956 \
	-e CMT_RPC_HOST_PORT=26957 \
	-e ETHAPI_HOST_PORT=8845 \
	-e RESOLVER_HOST_PORT=26955 \
	-e BOOTSTRAPS=<BOOTSTRAPS> \
	-e RESOLVER_BOOTSTRAPS=<RESOLVER_BOOTSTRAPS> \
	-e PARENT_ENDPOINT="http://host.docker.internal:3033/api" \
	-e PARENT_AUTH_TOKEN="validator4_auth_token" \
	-e TOPDOWN_CHAIN_HEAD_DELAY=0 \
	-e TOPDOWN_PROPOSAL_DELAY=0 \
	-e FM_PULL_SKIP=1 \
	child-validator
```
