Dusk - Tiny Net with Staking
Published: 2024-02-15 18:31:18

I will make a network with one genesis staked provisioner, then add a second staked provisioner later.

We will use TWO machines, both Ubuntu 22.04 installed.

M1 - Digital Ocean Basic/Regular 4GB/2CPU - Frankfurt - 142.XX.XX.240
M2 - Digital Ocean Basic/Regular 4GB/2CPU - London - 167.XX.XX.102

// NB: I could not build Dusk with less than 4GB

I will setup M1 first...

**Build Prerequisites**

```
# update packages
apt update 
apt upgrade  -y 

# packages we need
apt install make pkg-config libssl-dev npm unzip libclang-dev build-essential -y

# rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 	# select opt 1
source "$HOME/.cargo/env"
rustup toolchain install nightly
rustup default nightly

# rust extras
cargo install wasm-pack

# nvm and node
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
exit
# and login again :-)
nvm list-remote
nvm install v20.11.0
```

**Build Dusk - Everything**

```
git clone https://github.com/dusk-network/rusk.git
cd rusk
make

# check it built
./target/release/rusk -V
```

**Setup**

```
# Consensus keys (wallet generated) for node:
mkdir -p ~/.dusk/rusk
cp examples/consensus.keys ~/.dusk/rusk/consensus.keys

# the example genesis.toml starts a staked provisioner
# note the two addresses...
cat examples/genesis.toml

# edit the file as follows -- give wallet 300k:
[[balance]]
address = '4ZH3oyfTuMHyWD1Rp4e7QKp5yK6wLrWvxHneufAiYBAjvereFvfjtDvTbBcZN5ZCsaoMo49s1LKPTwGpowik6QJG'
seed = 0xdead_beef
notes = [300_000_000_000_000]

[[stake]]
address = 'oCqYsUMRqpRn2kSabH52Gt6FQCwH5JXj5MtRdYVtjMSJ73AFvdbPf98p3gz98fQwNy9ZBiDem6m9BivzURKFSKLYWP3N9JahSPZs9PnZ996P18rTGAjQTNFsxtbrKx79yWu'
amount = 1_000_000_000_000
```

**Launch M1 node**

```
# Generate genesis state
cargo r --release -p rusk -- recovery-state --init examples/genesis.toml -o /tmp/example.state

# Launch node
cargo r --release -p rusk -- -s /tmp/example.state --http-listen-addr 0.0.0.0:4321 --kadcast-public-address 167.99.241.102:9000
```

**Make Wallet**

```
git clone https://github.com/dusk-network/wallet-cli.git
cd wallet-cli/
make build
./target/release/rusk-wallet -V

# the wallet makes / looks for a wallet.dat file in this location
# /root/.dusk/rusk-wallet/wallet.dat
# the genesis addresses are from the .dat file in the examples directory of the repo
# copy it in-place
cp ~/rusk/examples/wallet.dat /root/.dusk/rusk-wallet/

# if there is a cache in /root/.dusk/rusk-wallet/ (from some previous wallet) delete it
# run the wallet
./target/release/rusk-wallet --state http://127.0.0.1:4321 --prover http://127.0.0.1:4321

# nb: password of the example.dat is "password"
```

You will see the 4ZH3oy... address from the genesis toml, it should have 300k Dusk on it.

If you "Check existing stake" there should be 1000 Dusk on staking address oCqYsUM...

With this node still running, we will join a second node to the network....

on M2:

1) *repeat all the build steps for rusk and wallet from M1*
2) *edit the genesis.toml file to be the same*
3) *do NOT copy example.dat file, this is new wallet*

**Run Wallet**

```
cd wallet-cli
./target/release/rusk-wallet --state http://127.0.0.1:4321 --prover http://127.0.0.1:4321
```

create a new wallet, or recover to get same addresses;
my 12 words were: tag tide chef like history submit field minor hub ridge prepare pause
password: "password"

Export provisioner keys > will write consensus.keys to /root/.dusk/rusk-wallet
Exit Wallet

**Setup M2 node**

```
cd rusk
mkdir -p ~/.dusk/rusk

# previously we used the consensus.keys from example directory, which corresponded to the example wallet, but here we use the consensus keys for this wallet

cp /root/.dusk/rusk-wallet/*.keys ~/.dusk/rusk/

# I renamed the file to con.keys to make it simpler
```

**Run M2 node**

```
# run the node, specifying the first node as bootstrap node
# note that it is the Kadcast port (9000) that must be referenced
# nb: port needs to be open, check using telnet

./target/release/rusk -s /tmp/example.state --http-listen-addr 0.0.0.0:4321 --kadcast-bootstrap "<M2-IP>:9000" --consensus-keys-path="$HOME/.dusk/rusk/<filename.keys>" --kadcast-public-address <THIS-IP>:9000

eg:

export DUSK_CONSENSUS_KEYS_PASS=password
./target/release/rusk -s /tmp/example.state --http-listen-addr 0.0.0.0:4321 --kadcast-bootstrap "167.XX.XX.102:9000" --consensus-keys-path="/$HOME/.dusk/rusk/con.keys" --kadcast-public-address 142.XX.XX.240:9000

# note: Kadcast uses UDP, you can check the port is listening with netstat -ulpn
```

In a new session start the M2 wallet and fund from M1:
```
cd wallet-cli
./target/release/rusk-wallet --state http://127.0.0.1:4321 --prover http://127.0.0.1:4321

# address1 is 4oPWfmo... with a balance of zero.

# on the M1 wallet, transfer 200k to this address1 

after a short while (if node and wallet is fully synced) M2 - 4oPWfmo... should have 200k balance.
```

At this point both nodes are on the same network, with the respective wallets connected to their nodes. We will now stake M2 node.

If you observe the logs of both running nodes, you will see that M1 node has "Proposal" in the log output, whereas the M2 node does not, immediately after "Proposal" you will see pk="oCqYsUMR..." which is the public staking key from the wallet. M2 node does not propose blocks, as it is not yet staked.

From the M2 wallet stake 100k.

After doing that, in the wallet do "Check existing stake", this will tell you that the stake becomes eligible from some future block number and epoch. Stake becomes active at the next epoch boundary. You will see the current block height of the node in the log output, so you will know how long you have to wait. 

Once the block height is reached, you will see "Proposal" messages in the M2 node log, as the node is now making blocks. Indeed, as the staked amount on the M2 node is 100k and the staked amount on the M1 node is 1k, almost ALL blocks are made on the M2 node. 

If you "Check existing stake" in the wallet, you will see the stake is now active and rewards are accumulating.

Until next time.