---
# System prepended metadata

title: FiroVM Proof of authority setup Guide v1

---



# 0. Setup

## 1. Build

### 1.1 Docker image

```sh
git clone --recursive <git url>
cd firovm
git checkout testnet-poa

docker build -t firovm .
```

### 1.2. Update config
Some configuration need to be updated for PoA

***firovm.conf***
```
rpcuser=<e.g. `test`>
rpcpassword=<Securely random>
addrindex=1
chain=test
txindex=1
noconnect=1
logevents=1
readdusedcoin=1 # For auto readding used coins

[test]
bind=0.0.0.0 # allow connection to docker containers
rpcbind=0.0.0.0 # allow connection to docker containers
debug=1
logevents=1
listenonion=0
minimumchainwork=0
connect=<ip1> # ip of other nodes
connect=<ip2> #
```

## 2. Start FiroVM

### Start a docker container
```sh!
docker run -v <firovm.conf path>:/firovm/firovm.conf -p 13889:13889 --name node1 firovm
```

### Run many nodes using docker compose

Example files

***docker-compose.yaml***
```yaml!
version: "3.8"
services:
  node1:
    image: firovm:latest
    volumes:
      - ./firovm1.conf:/firovm/firovm.conf

  node2:
    image: firovm
    volumes:
      - ./firovm2.conf:/firovm/firovm.conf

  node3:
    image: firovm
    volumes:
      - ./firovm3.conf:/firovm/firovm.conf
```

***firovm1.conf***
```
rpcuser=test
rpcpassword=<random this>
chain=test
addrindex=1
txindex=1
noconnect=1
logevents=1
readdusedcoin=1

[test]
bind=0.0.0.0
rpcbind=0.0.0.0
rpcallowip=0.0.0.0/0
debug=1
logevents=1
listenonion=0
readdusedcoin=true
readdusedcoin=1
minimumchainwork=0
connect=node2
connect=node3
```

***firovm2.conf***
```yaml!
rpcuser=test
rpcpassword=<random this>
chain=test
addrindex=1
txindex=1
noconnect=1
logevents=1
readdusedcoin=1

[test]
bind=0.0.0.0
rpcbind=0.0.0.0
rpcallowip=0.0.0.0/0
debug=1
logevents=1
listenonion=0
readdusedcoin=true
readdusedcoin=1
minimumchainwork=0
connect=node1
connect=node3
```

***firovm3.conf***
```yaml!
rpcuser=test
rpcpassword=<random this>
chain=test
addrindex=1
txindex=1
noconnect=1
logevents=1
readdusedcoin=1

[test]
bind=0.0.0.0
rpcbind=0.0.0.0
rpcallowip=0.0.0.0/0
debug=1
logevents=1
listenonion=0
readdusedcoin=true
readdusedcoin=1
minimumchainwork=0
connect=node1
connect=node2
```

Then start docker-compose

```sh!
docker compose up -d

# use firovm-cli
docker compose exec node1 firovm-cli <command> [<args>]

# print log
docker compose logs -f --tail 100 node1
```

## 3. Setup commands

```shell
# set up CLI to call firovm node
# e.g.
CLI="./src/firovm-cli"
```

## 4. Setup firovm tool

### prerequisites

- `Node v18`
- `yarn`

```sh
unzip firovm-tool-master.zip
cd firovm-tool-master
yarn build
```

# 1. Setup admin keys (At least 2)

These keys would be used to manage network following
- Add/Remove miners
- Update network params
- Setup network

You can use CLI to generate following command below

**NOTE: this is for testing, on production we should generate it offline**

```sh!
# run this on admin node
$CLI createwallet ''

admin1=$($CLI getnewaddress 'admin')
hex_admin1=$($CLI gethexaddress $admin1)

admin2=$($CLI getnewaddress 'admin')
hex_admin2=$($CLI gethexaddress $admin2)
```

# 2. Funding miner

```shell
$CLI generatetoaddress 1 $admin1
```

# 3. Generate first 2500 blocks
We have to generate some fund so we can create transactions to manage network. For the section below we show the example to generate using cli.

```sh!
current=$($CLI getblockcount)
target=2500
address=$($CLI fromhexaddress 0000000000000000000000000000000000000000)

while [ "$target" -gt "$current" ]; do
    diff=$(expr $target - $current)
    if [ "$diff" -gt "10" ]; then
        diff=10
    fi

    $CLI generatetoaddress $diff $address > /dev/null
    current=$($CLI getblockcount)
    printf "\rgenerated: %d/%d" $current $target
done
```

# 3. Initilize contracts

In our network contains 3 main contracts
- **QtumGov**: 0000000000000000000000000000000000000083
    - To manage admins and govs list
- **MinerList**: 0000000000000000000000000000000000000880
    - To store miners and their UTXOs
    - To allow UTXOs list to be updated after used
- **Mobile**: 0000000000000000000000000000000000000881
    - **NOTE: untested**

## 3.1 Set initial admin

Initializing **QtumGov** contract by calling function below. Caller of this function would become the first admin so in the example we use **\$admin1**

```solidity
function setInitialAdmin() external {
    ...
}
```

Calling contract **QtumGov** with fixed payload `6fb81cb` 

### Using CLI
```sh!
firovm-cli sendtocontract 0000000000000000000000000000000000000083 6fb81cbb 0 200000 0.0000004 $admin1

# to make sure contract state be changed
firovm-cli generatetoaddress 1 $admin1
```

## 3.2 Set QtumGov address to MinerList and Mobile contracts

**MinerList** and **Mobile** contracts must be set **QtumGov** address before using by calling function by **\$admin1**

```solidity
function setDGPContract(address qtumGov) external {
    ...
}
```

Calling contract **MinerList** and **Mobile** using payload `9f080402000000000000000000000000` + **QtumGov**

### Using CLI
```sh!
# MinerList contract
firovm-cli sendtocontract 0000000000000000000000000000000000000880 9f0804020000000000000000000000000000000000000000000000000000000000000083 0 1000000 0.0000004 $admin1

# Mobile contract
firovm-cli sendtocontract 0000000000000000000000000000000000000881 9f0804020000000000000000000000000000000000000000000000000000000000000083 0 1000000 0.0000004 $admin1

# to make sure contract state be changed
firovm-cli generatetoaddress 1 $admin1
```


## 3.3 Set Miner and Mobile contracts on QtumGov

To sync admins and govs list we have to add **MinerList** and **Mobile** contracts to **QtumGov** contract by calling function by **admin1**

```solidity
function setContracts(address __validatorContractAddress, address __poaContractAddress) public onlyAdmin {
    ...
}
```
Calling contract **QtumGov** with payload `d8952a49000000000000000000000000` + **MinerList** + `000000000000000000000000` + **Mobile**

### Using CLI

```sh!
firovm-cli sendtocontract 0000000000000000000000000000000000000083 d8952a4900000000000000000000000000000000000000000000000000000000000008800000000000000000000000000000000000000000000000000000000000000881 0 1000000 0.0000004 $admin1

# to make sure contract state be changed
firovm-cli generatetoaddress 1 $admin1
```

## 3.4 Add one more admin

We can add more admin by calling function by **\$admin1**

```solidity
function addAddressProposal(address _proposalAddress, uint _type) external {
    ...
}
```

Calling contract **QtumGov** with payload `bf5f1e83000000000000000000000000` + **hex address of new admin** + `0000000000000000000000000000000000000000000000000000000000000000`

### Using CLI

```sh!
hex_address2=$(firovm-cli gethexaddress $admin2)
calldata=bf5f1e83000000000000000000000000
calldata+=$hex_address2
calldata+=0000000000000000000000000000000000000000000000000000000000000000
firovm-cli sendtocontract 0000000000000000000000000000000000000083 $calldata 0 1000000 0.0000004 $admin1

# to make sure contract state be changed
firovm-cli generatetoaddress 1 $admin1
```

# 4. Prepare Miners

## 4.1 Generate miner addresses on miner nodes

```sh!
firovm-cli createwallet ''
miners=10 # number of miner addresses

for i in $(seq 1 $firovm)
do
    firovm-cli getnewaddress 'miner' >> miners.txt
done

# then send miners.txt to admins
```


## 4.2 Use script to fund and register miners

Admins can add new miner addresses one by one by using script `addminer.sh` which is provided in the last section

**Script**

```sh!
addminer.sh <address> <number of utxos> $admin1 $admin2
```

**Arguments**
- **address**: miner addresses generate from 4.1
- **number of utxos**: to generate which should not more than 10
- **admin1**, **admin2**: generated from 1.1

**Environments**
- **CLI=<firovm-cli path>**: to tell the script where to find rpc client. The default value would be `./firovm-cli`
    - you may use docker exec by set `CLI=docker exec -it firovm firovm-cli`
- **JQ**: path of jq the JSON processor. The default value would be `/usr/bin/jq`
    
**Example**
```sh!
CLI="docker exec -it firovm firovm-cli" JQ="./bin/jq" addminer.sh TEQa8Qug3qsuWUNutcy1bpachTwoCZCzw2 10 $admin1 $admin2
```
    
Then add admin from addresses file line by line

```sh!
CLI="..."

while read addr; do
    if [ ! -z $addr ]; then
        CLI=$CLI ./addminer.sh $addr 10 $admin1 $admin2
    fi
done < miners.txt
```

# 5. Generate more blocks
```sh!
CLI=firovm-cli
current=$($CLI getblockcount)
target=8000
address=<address>

while [ "$target" -gt "$current" ]; do
    diff=$(expr $target - $current)
    if [ "$diff" -gt "10" ]; then
        diff=10
    fi

    $CLI generatetoaddress $diff $address > /dev/null
    current=$($CLI getblockcount)
    printf "\rgenerated: %d/%d" $current $target
done
```

# 6. Check PoA status
### a) PoA block

Check the latest block

```
firovm-cli getblock $(firovm-cli getblockhash $(firovm-cli getblockcount))
```

If the latest block is **proof of authority** the result should contains `"flags": "proof-of-stake"`

### b) Miner status

Message like below should be logged

```
SloveBlock(): coins=1000, solved=1
```

which **coins** represents the number of coins which could be used to generate blocks and **solved** represents number of coins that qualify to generate the next block.
    
# 7. Propose supply mint

To mint supply we will work on contract `0000000000000000000000000000000000000882` and we will use list of `admins` and `govs` to vote by using 2 of 3
    
To encode calldata we recommend to compile and deploy [SupplyControlABIEncoder.sol](#SupplyControlABIEncodersol) contract on [Remix IDE](https://remix.ethereum.org). And then you can encode payload using GUI
    
## 7.1 Create proposal

To mint native token we have to create proposal and allow `admins` and `govs` to vote

```solidity
function propose(
    uint256 _block, // block to mint
    uint256[] calldata _amounts, // array of amount to mint to scripts
    string[] calldata _scriptTypes,
        // array of script type for the most cast should be "pkh"
    bytes[] calldata _scripts // array of script to payout e.g. 
        // 0x76a91413c03891b1a982bb15620def6c70225a5829b8e288ac
) {
    ...
}
```
    
**NOTE**: encode using Remix IDE

### Using CLI
```sh!
$CLI  sendtocontract 0000000000000000000000000000000000000882 <calldata> 0 200000 0.0000004 $admin1

# to make sure contract state be changed
$CLI generatetoaddress 1 $admin1
    
# then you can get proposalId using
$CLI gettransactionreceipt <txid from `sendtocontract`>
# Then you can getproposal ID from [0].log[0].topics[1]
    
# [
#   {
#     "blockHash": "5ef54892242772d6c4a1fd15fc450811f4bc7d9515f4d6914ee039c05f79bb0d",
#     "blockNumber": 2019,
#     "transactionHash": "952bc5265669f8f29b0a8f19d2d74dc56369f16be1e9270dd7faee06b645ad93",
#     "transactionIndex": 1,
#     "outputIndex": 0,
#     "from": "66132fa10e1660ebe7cd9a4af6f31b1622df6ff7",
#     "to": "0000000000000000000000000000000000000882",
#     "cumulativeGasUsed": 276559,
#     "gasUsed": 276559,
#     "contractAddress": "0000000000000000000000000000000000000882",
#     "excepted": "None",
#     "exceptedMessage": "",
#     "bloom": "00000000000000000000000020000000000000000000100000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000100008000000000000000000000000000100000000000000000000000",
#     "stateRoot": "e5a0087aeffe04fd465100f2d93416b1f51b811cae260af23a17099b004d525f",
#     "utxoRoot": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
#     "log": [
#       {
#         "address": "0000000000000000000000000000000000000882",
#         "topics": [
#           "c2c021f5d73c63c481d336fbbafec58f694fc45095f00b02d2deb8cca59afe07",
#           "1d460b64f7b8ba0be629afe9b4ae65333b379985d7ea823ff4c0b8c3b5102153" << proposal ID
#         ],
#         "data": ""
#       }
#     ]
#   }
# ]
```
    
## 7.2 Cast vote on the proposal

We can cast vote on proposal by calling function

```solidity
function castVote(
    uint256 proposalId, // from 7.1
    uint8 support // 0 = vote agaist, 1 = vote for, 2 = abstain
        // in this test we will use 1
) public {
    ...
}
```
**NOTE**: encode using Remix IDE

### Using CLI
```sh!
$CLI sendtocontract 0000000000000000000000000000000000000882 <calldata> 0 200000 0.0000004 $admin1
$CLI sendtocontract 0000000000000000000000000000000000000882 <calldata> 0 200000 0.0000004 $admin2

# to make sure contract state be changed
$CLI generatetoaddress 1 $admin1
```
    
## 7.3 Execute proposal

We can cast vote on proposal by calling function

```solidity
function execute(
    uint256 proposalId // proposal id from 7.1
) public {
    ...    
}
```
**NOTE**: encode using Remix IDE

### Using CLI
```sh!
# To make block number over voting period
$CLI generatetoaddress 5760 $admin1
    
$CLI sendtocontract 0000000000000000000000000000000000000882 <calldata> 0 200000 0.0000004 $admin1

# to make sure contract state be changed
$CLI generatetoaddress 1 $admin1
```

## 7.4 Verify

We can cast vote on proposal by calling function

```solidity
function getMint(
    uint256 _block // block number specify on 7.1
) public {
    ...
}
```

### Using CLI
```sh!
$CLI callcontract 0000000000000000000000000000000000000882 <calldata>
```

## CODE

### `addminer.sh`

```sh!
#!/bin/bash

CLI=$CLI
if [ -z "$CLI" ]; then
    CLI="firovm-cli"
fi

JQ=$JQ
if [ -z "$JQ" ]; then
    JQ="/usr/bin/jq"
fi

ADDRESS=$1
HEX_ADDRESS=$($CLI gethexaddress $ADDRESS)
UTXOS=$2
ADMIN1=$3
ADMIN2=$4

MINER_LIST="0000000000000000000000000000000000000880"

for i in $(seq 1 $UTXOS)
do
    $CLI sendtoaddress $ADDRESS 100 >/dev/null
    
    MOD=$(($i%10))
    if [ "$MOD" -eq "0" ]; then
        $CLI generatetoaddress 1 $ADMIN1 >/dev/null
    fi
done

echo "generated UTXOS"

$CLI generatetoaddress 1 $ADMIN1 >/dev/null

## contract payload
utxos=$($CLI getaddressutxos "{\"addresses\":[\"${ADDRESS}\"]}")
_len=$(printf "%064x" $($JQ '. | length' <<< "$utxos"))

calldata="c5bc45af000000000000000000000000"
calldata+="${HEX_ADDRESS}"
calldata+="0000000000000000000000000000000000000000000000000000000000000060"
calldata+="0000000000000000000000000000000000000000000000000000000000000001"
calldata+="${_len}"

calldata+=$(
    $JQ -c '.[] | objects' <<< "$utxos" | while read obj; do
        txid=$($JQ '.txid' <<< $obj | tr -d '"')
        outputIndex=$(printf "%064x" $($JQ '.outputIndex' <<< $obj))
        printf "%s%s" $outputIndex $txid
    done
)

proposal=$($CLI sendtocontract $MINER_LIST $calldata 0 1000000 0.0000004 $ADMIN1 | $JQ -r '.txid')

$CLI generatetoaddress 1 $ADMIN1 >/dev/null

receipts=$($CLI gettransactionreceipt $proposal)
if [ "$($JQ '.|length' <<< $receipts)" -eq "0" ]; then
    echo "fail to create proposal";
    exit 0;
fi

receipt=$($JQ -r '.[0]' <<< $receipts)
if [ "$($JQ -r '.excepted' <<< $receipt)" != "None" ]; then
    echo "error on create proposal $receipt";
    exit 0;
fi

logs=$($JQ -r '.log' <<< $receipt)
if [ $($JQ '.|length' <<< $logs) -eq "0" ]; then
    echo "no log in receipt";
    exit 0;
fi

proposalid=$($JQ -r '.[0].data' <<< $logs)

echo "proposalid: ${proposalid}"

# vote
calldata="943e8216${proposalid}0000000000000000000000000000000000000000000000000000000000000001"

voteid=$($CLI sendtocontract $MINER_LIST $calldata 0 1000000 0.0000004 $ADMIN2 | $JQ -r '.txid')

$CLI generatetoaddress 1 $ADMIN1 >/dev/null

receipts=$($CLI gettransactionreceipt $voteid)
if [ "$($JQ '.|length' <<< $receipts)" -eq "0" ]; then
    echo "fail to vote";
    exit 0;
fi

receipt=$($JQ -r '.[0]' <<< $receipts)
if [ "$($JQ -r '.excepted' <<< $receipt)" != "None" ]; then
    echo "error on vote";
    exit 0;
fi
echo "vote: $voteid"

# add one more for readding
$CLI sendtoaddress $ADDRESS 100 >/dev/null

$CLI generatetoaddress 1 $ADMIN1 >/dev/null

echo "Success to add miner $ADDRESS"
```
    
### SupplyControlABIEncoder.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

contract SupplyControlABIEncoder {
    function propose(
        uint256 _block,
        uint256[] calldata _amounts,
        string[] calldata _scriptTypes,
        bytes[] calldata _scripts
    ) public {}

    function castVote(uint256 proposalId, uint8 support) public {}
    function execute(uint256 proposalId) public {}
    function getMint(uint256 _block) public {}
}
```