Proof of Useful Work (PoUW) is a novel blockchain platform with a new type of user, that is additional to the typical blockchain transactor: the requestor. This paradigm shift has a dual purpose: we provide a typical cryptocurrency with the added benefit of a computational platform at an affordable price.
Our requestor model is using machine learning as useful computation. The work is distributed among worker nodes and verifiers. A client (the requestor) can submit a machine learning task along with preferences, the worker nodes are matched to the task, miners train and mine with nonces derived from the ML processing, and at end they are paid from the client's fee. All interactions are sent as special transactions into the blockchain, so they can be later verified.
PoUW can be run on Mac OS X and Linux, but also on derived systems such as Docker or Kubernetes.
When blocks are successfully mined we can see an output like this in the PAI blockchain server window:
Meanwhile, the ML training process takes place at a faster pace inside the miner nodes.
Here is how to run PoUW locally in just 10 simple steps.
- paicoind is the PAICoin server binary
- verification/server.py is the PAICoin server extension that re-runs and verifies ML iterations when a lucky nonce is found by a miner
- worker.py contains the mining and training code executed by a miner; start_cluster.py is calling it to simulate several miners on the same machine
- client.py is the client code that triggers the training and mining processes.
This setup assumes you will be running a local PAICoin node and three ML miners (on the same Apple machine). This should be used for testing and debugging purposes.
-
Let's clone the PAICoin PoUW branch and the ML trainer extension by running the following commands:
git clone -b "pouw-q4-clean" --single-branch https://github.com/projectpai/paicoin.git git clone https://github.com/projectpai/pouw-main-iteration
-
Install the prerequisites to build the PAICoin code and to run the ML trainer extension:
brew install automake berkeley-db4 libtool boost miniupnpc pkg-config python qt libevent qrencode zmq brew install python3 grpc
We also need Redis:
brew install redis brew services start redis
-
Create a directory
~/Application\ Support/PAIcoin/
where we will add two configuration files:mkdir ~/Library/Application\ Support/PAIcoin/ cd ~/Library/Application\ Support/PAIcoin/
-
We'll place here a file called
paicoin.conf
that has the following content:server=1 bantime=1 daemon=0 rpcuser=paicoin rpcpassword=10050021 rpcport=4002 testnet=1 rpcallowip=0.0.0.0/0 txindex=1 onlynet=ipv4 listenonion=0 maxtipage=31104000 listen=1 rpcbind=0.0.0.0 verificationserver=0.0.0.0:50011 printtoconsole=1 connect=0 ignore-not-connected=1 dnsseed=0
-
We'll add another file called
chainparams.conf
with this content:GENESIS_BLOCK_TIME = 5 GENESIS_BLOCK_REWARD = 1470000000 INITIAL_BLOCK_REWARD = 150 BLOCK_TIME = 5 TESTNET_CONSENSUS_POW_LIMIT = 01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff TESTNET_GENESIS_BLOCK_POW_BITS = 20 TESTNET_GENESIS_BLOCK_NBITS = 0x2001ffff TESTNET_GENESIS_BLOCK_SIGNATURE = 9a8abac6c3d97d37d627e6ebcaf68be72275168b TESTNET_GENESIS_BLOCK_UNIX_TIMESTAMP = 1626260220 TESTNET_PUBKEY_ADDRESS = 51 TESTNET_SCRIPT_ADDRESS = 180 TESTNET_SECRET_KEY = 226 TESTNET_MAGIC_BYTE_0 = 11 TESTNET_MAGIC_BYTE_1 = 9 TESTNET_MAGIC_BYTE_2 = 17 TESTNET_MAGIC_BYTE_3 = 7 TESTNET_PORT = 18567 TESTNET_RPC_PORT = 18566 TESTNET_SEED_0 = TESTNET_SEED_1 = TESTNET_SEED_2 =
-
Now we'll switch back to where we cloned the two repositories and we'll build the the PAIcoin blockchain part by running:
cd paicoin/ ./autogen.sh ./configure --with-gui=no --disable-tests --disable-bench --enable-chainparams-conf make -j $(sysctl -n hw.physicalcpu)
-
We'll create the genesis block.
cd src/ ./paicoind -mine-genesis-block
-
Now, we switch to the Python code and install the ML trainer:
cd ../../pouw-main-iteration/ pip3 install -r requirements.txt pip3 install -U setuptools python3 setup.py develop
-
Now, it's already time to run the system.
-
Start the verification server, so we type:
python3 pai/pouw/verification/server.py
-
In another terminal, we start the paicoind process, therefore from the original directory where we initially cloned the repos, we run this:
cd paicoin/src/ ./paicoind -ignore-not-connected
-
We start another terminal in the initial clone directory and we'll start the ML training cluster with 3 nodes:
cd pouw-main-iteration/ python3 pai/pouw/start_cluster.py --nodes-number 3
-
From another terminal we run the client that starts the training process. The output should be similar to this window during the whole process:
cd pouw-main-iteration/ python3 pai/pouw/nodes/decentralized/client.py --client-task-definition-path=pai/pouw/client-task-definition.yaml
- Let's check the results:
- You can open a new Terminal and check with
paicoin-cli
the blockchain status:cd paicoin/src/ ./paicoin-cli --rpcuser=paicoin --rpcpassword=10050021 --rpcport=4002 getmininginfo
- While the mining and training is taking place, you can see the verifications in the output window of
server.py
:.
-
We install the prerequisites for the blockchain part of PoUW.
sudo apt-get update && sudo apt-get install -y software-properties-common && \ sudo add-apt-repository ppa:rock-core/qt4 -y && \ sudo apt-get update && \ sudo apt-get install -y \ python3 \ cpp \ build-essential \ gcc \ g++ \ make \ pkg-config \ autoconf \ libboost-all-dev \ libssl-dev \ libprotobuf-dev \ protobuf-compiler \ libqt4-dev \ libqrencode-dev \ libtool \ bsdmainutils \ libevent-dev \ curl \ ssh \ git
-
Install Redis:
sudo apt-get install -y redis-server sudo systemctl enable redis-server.service
-
Install gRPC:
sudo -i cd /tmp && \ cd /tmp && \ git clone -b v1.30.2 --single-branch https://github.com/grpc/grpc && \ cd grpc && \ git submodule update --init && \ CXXFLAGS='-Wno-error' make -j $(lscpu | grep -E '^CPU\(s)' | awk '{print $2}') HAS_SYSTEM_PROTOBUF=false && \ sudo make -j $(lscpu | grep -E '^CPU\(s)' | awk '{print $2}') install && \ cd third_party/protobuf && \ sudo make -j $(lscpu | grep -E '^CPU\(s)' | awk '{print $2}') install && \ rm -rf /tmp/grpc && \ export LD_LIBRARY_PATH=/usr/local/lib
-
Build and setup PAICoin:
First, we build paicoin:
cd /tmp sudo mkdir /opt/paicoin sudo git clone -b pouw-q4-clean --single-branch https://github.com/projectpai/paicoin.git /opt/paicoin cd /opt/paicoin sudo /bin/bash berkley.sh && \ mkdir ~/app && \ ln -s /opt/paicoin/src/paicoind ~/app/paicoind && \ ln -s /opt/paicoin/src/paicoin-cli ~/app/paicoin-cli
Then, we configure it:
mkdir ~/.paicoin && cd ~/.paicoin
We'll place here a file called
paicoin.conf
that has the following content:server=1 bantime=1 daemon=0 rpcuser=paicoin rpcpassword=10050021 rpcport=4002 testnet=1 rpcallowip=0.0.0.0/0 txindex=1 onlynet=ipv4 listenonion=0 maxtipage=31104000 listen=1 rpcbind=0.0.0.0 verificationserver=0.0.0.0:50011 printtoconsole=1 connect=0 ignore-not-connected=1 dnsseed=0
We'll also add another file called
chainparams.conf
with this content:GENESIS_BLOCK_TIME = 5 GENESIS_BLOCK_REWARD = 1470000000 INITIAL_BLOCK_REWARD = 150 BLOCK_TIME = 5 TESTNET_CONSENSUS_POW_LIMIT = 01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff TESTNET_GENESIS_BLOCK_POW_BITS = 20 TESTNET_GENESIS_BLOCK_NBITS = 0x2001ffff TESTNET_GENESIS_BLOCK_SIGNATURE = 9a8abac6c3d97d37d627e6ebcaf68be72275168b TESTNET_GENESIS_BLOCK_UNIX_TIMESTAMP = 1626260220 TESTNET_PUBKEY_ADDRESS = 51 TESTNET_SCRIPT_ADDRESS = 180 TESTNET_SECRET_KEY = 226 TESTNET_MAGIC_BYTE_0 = 11 TESTNET_MAGIC_BYTE_1 = 9 TESTNET_MAGIC_BYTE_2 = 17 TESTNET_MAGIC_BYTE_3 = 7 TESTNET_PORT = 18567 TESTNET_RPC_PORT = 18566 TESTNET_SEED_0 = TESTNET_SEED_1 = TESTNET_SEED_2 =
-
Let's mine the genesis block:
cd ~/app/ ./paicoind -mine-genesis-block
-
Install the Python dependencies:
sudo apt-get update && sudo apt-get install -y cmake python3-pip python3-dev python3 python3-setuptools cd /opt
-
Setup the Python PoUW extension:
git clone https://github.com/projectpai/pouw-main-iteration cd /opt/pouw-main-iteration && \ sed -i '1s/boto3//;' requirements.txt && \ pip install -r requirements.txt && \ python3 setup.py develop
-
Start the verification server and the PAICoin daemon:
python3 pai/pouw/verification/server.py
Then, in another terminal:
cd ~/app ./paicoind -ignore-not-connected
-
Start a PoUW cluster (using a new terminal):
cd /opt/pouw-main-iteration/ python3 pai/pouw/start_cluster.py --nodes-number 3
-
In another terminal, we start the client:
cd /opt/pouw-main-iteration/ python3 pai/pouw/nodes/decentralized/client.py --client-task-definition-path=pai/pouw/client-task-definition.yaml
The environment is the PAI (Personalised Artificial Intelligence) blockchain, a hybrid Proof of Work/Proof of Stake (PoW/PoS) blockchain. It is a P2P decentralised network composed of various actor types to ensure security.
The system has 6 types of actors:
- clients
- miners
- supervisors
- evaluators
- verifiers
- peers
Clients are interested to have their models trained a good price, miners perform the actual training, supervisors log the ML task activity and audit for malicious behaviour, evaluators decide how to split the client's fee, verifiers do delegated verification when a miner finds a lucky nonce and peers are regular transactors.
Here is a typical task workflow:
In the first phase, called Registration, a client submits a ML task and miners and supervisors are assigned automatically based on a matching algorithm. Then in the Initialisation phase, the worker nodes exchange their cryptographic keys with each other, the client shares the training data and this data is prepared for training and stored on the worker nodes. Data is split into very small batches, each batch will be processed in an iteration. During the next phase, Training, miners will perform the distributed machine learning training. When a miner finishes to process an iteration (there can be thousands, millions of iterations), it is allowed to try to mine with a nonce obtained from specifics of that iteration. If it finds a lucky nonce, it will hand over the inputs and the data to several verifiers, who can certify that the nonce was obtained correctly. During this time, supervisors audit the whole training process. In the Finalisation phase, our protocol will pick up a set of evaluators, who decide how to split the client's fee.
We use an asynchronous ML training algorithm. The whole training dataset is split into small pieces, called batches. These pieces are given to miners to process. Every miner has a set of its own batches to process in a predetermined order. At the same time, every miner also has a copy of the ML model. At every iteration, it processes a batch, updates its model and tells the other miners what changes it applied to its model. The other miners listen to these kinds of messages and update their own models with these changes, while also performing their own work.
During a ML iteration the miner receives gradient updates and applies them to its local model, then loads its assigned mini-batch for this iteration. Then it’s the usual backpropagation, after which we obtain gradients. To minimize the sent data, we send only significant values for gradients. We use an accumulation scheme: we keep a gradient residual and everything that exceeds a threshold is encoded in a map, applied to the local model and announced over the network. Eventually, all changes to gradients are applied to the local model with a delay. After this is done, the miner is allowed to mine.
At the end of the training, a number of evaluators are selected, they are provided with the models from each miner and the test dataset from the client. Very important: nobody sees the client dataset until this stage. Evaluators just test the models on the test dataset and decide how to split the client’s money across the ML training participants, that include the miners, supervisors, and evaluators themselves. Then, the best model is handed to the final beneficiary, which is the client, the requestor.
You might ask yourself: Is this feasible in practice? Yes. I would like to mention that we tested ML task groups of up to 80 participants that still converged to a good solution with this distributed setup.
What happens when there are times when no requestor asks for computation? We have fill-in tasks designed by Project PAI, such as protein folding, that can be solved using ML. Once defined, we can add here computational tasks that could help with finding a cure or vaccine to Covid-19 and so on. The model architecture and parameters are randomly generated, so there are no repeat-jobs.
How about bad behaviour, cheating, doing cheap work? There are two protections in place: the architecture of the system and staking. We ensure good behaviour using staking. All nodes stake coins to participate. If they do proper work, stakes are returned, otherwise they are confiscated and added to the task fee and redistributed to honest actors at the end of training. Staking is our first differentiator. This makes our platform different from others.
Our second differentiator is the ML-based mining. We enforce useful work by linking mining to AI-work. Every nonce is restricted to a value that can only be obtained after executing a ML iteration. Without getting into very technical details, a nonce is deterministically derived from the model state at the end of the iteration and the gradients applied during iteration. A miner is allowed to try an interval of nonces based on model complexity and batch size.
All participants benefit from PoUW.
-
Miners have 2 income sources:
- paid by client in a descending order based on the final model metrics
- from actual mining.
-
Supervisors and evaluators get an equal share from a quota of the client share.
-
Verifiers are paid by miners, clients have lower computation costs
-
Peers win by holding PAI-Coin as a long-term investment.
We have done significant work in regards to bringing this idea to reality. We also have a comprehensive Kubernetes tutorial at https://github.com/projectpai/pouw-k8s, that you can follow and deploy a network of PoUW nodes on your local computer. You can clone the repositories, explore the code, build and run the implementations, modify the code, submit your proposals and become a PoUW contributor!
PoUW is distributed under the MIT License.