You are on page 1of 16

PPC CLONE GUIDE

by
YarkoL

v.0.1

Extremely quick n dirty. But should work :)


Prerequisites some programming knowledge and some skill with ubuntu linux.
Disclaimer, this is for learning purposes only - so if I hear that you have been using my guide for
some funny business, I swear Ill come and slap your sorry little ears til you cry for mummy.
In case youre lost, check out my blog, where I intend to go through all this jazz step by step,
holding hands and spoon feeding whenever I have the time.
https://yarkol.github.io
Or ask me on the practicecoin forum. Im very economical with words here, these are just the rough
notes, and you need to search for relevant parts in the code. Im not intentionally trying to mystify
you, but this is complex stuff, and you need to do your homework.
Dedicated to Cinnamon carter & Shakezula, Bitrick, Cryddit, Nite69, the spirit lives on.
OK lets roll.

Build dependencies

lets install

sudo apt-get install git nano build-essential autoconf libssl-dev


libboost-all-dev libdb++-dev libdb-dev --yes

Get the source files and test your build system


go to your home dir or whenever you want the coin source to be, and

$ git clone https://github.com/peercoin/peercoin

Newer coins use autotools, introduced in Bitcoin 0.9. However Peercoin is based on older builds
and they still have makefiles. read build instructions in doc/build-unix.txt

Now you can check that you can build the headless client
cd stanislaw/src

then
$ make -f makefile.unix USE_UPNP=-

Got the ppcoind client? Then

$ ./ppcoind -printtoconsole -debug

-printtoconsole will print out what the node is doing, and -debug will show some special messages,
that can be useful

After getting up and opening databases, it will exit with this message about missing conf file

Open nano or some other text editor


$ nano /home/jarkko/.ppcoin/ppcoin.conf

and think of a password


Since we are testing, we can do with

rpcuser=rpc
rpcpassword=pwd
testnet=1

Just remember to replace the password with something reasonable (like a random string ) when you
run a node out there in the wild

Again run the node


Now it ought to be running nicely

Install QT Creator

We need Qt Creator for the graphical wallet.


Get installer here
http://download.qt.io/official_releases/online_installers/

In the download dir,

$ chmod a+x qt-unified-linux-x64-2.0.5-2-online.run


$ ./qt-unified-linux-x64-2.0.5-2-online.run

It will ask you to make a developer account, then it will start to install

Now let's open it up in Qt Creator

go to peercoin directory
open the pro file in qt creator
configure the project, select qt5 for the build, then projects > build steps and add
"USE_UPNP=-"
to qmake additional arguments

Build the GUI wallet.


Hack the coin
Let's start modifying the source.
Your New Identity

Use find&replace and search Peercoin


Leave Peercoin Developers as they are, but change others to your coin's name

Change the currency units

/src/qt/bitcoinunits
QString BitcoinUnits::name(int unit)
{
switch(unit)
{
case BTC: return QString("PPC");
case mBTC: return QString("mPPC");
case uBTC: return QString::fromUtf8("PPC");
default: return QString("???");
}
}

QString BitcoinUnits::description(int unit)


{
switch(unit)
{
case BTC: return QString("PPCoins");
case mBTC: return QString("Milli-PPCoins (1 / 1,000)");
case uBTC: return QString("Micro-PPCoins (1 / 1,000,000)");
default: return QString("???");
}
}

Now the coin prefix

See list of prefixes, and this will make sense


https://en.bitcoin.it/wiki/List_of_address_prefixes

If base58.h is not showing up in the file window of qt creator, Search for CBase58Data and it
should open that

class CBitcoinAddress : public CBase58Data


{
public:
enum
{
PUBKEY_ADDRESS = 55, // ppcoin: addresses begin with 'P'
SCRIPT_ADDRESS = 117, // ppcoin: addresses begin with 'p'
PUBKEY_ADDRESS_TEST = 111,
SCRIPT_ADDRESS_TEST = 196,
};
Then let's look at some network stuff

// DNS seeds
// Each pair gives a source name and a seed name.
// The first name is used as information source for addrman.
// The second name should resolve to a list of seed addresses.
// testnet dns seed begins with 't', all else are ppcoin dns
seeds.
static const char *strDNSSeed[][2] = {
{"seed", "seed.fencoin.net"},

};

/src/net.cpp

(If you want to configure a dns seed,

you add a DNS Record on your hosts settings for example

seed.<yourcoinname>.net
A Record
14400
<IP of your node>
)

Change p2p and Rpc ports in protocol.h

#define PPCOIN_PORT 9901


#define RPC_PORT 9902
#define TESTNET_PORT 9903
#define TESTNET_RPC_PORT 9904

Now search for these in protocol.cpp

// Public testnet message start


// unsigned char pchMessageStartTestBitcoin[4] = { 0xfa, 0xbf,
0xb5, 0xda };
static unsigned char pchMessageStartTestOld[4] = { 0xdb, 0xe1,
0xf2, 0xf6 };
static unsigned char pchMessageStartTestNew[4] = { 0xcb, 0xf2,
0xc0, 0xef };
static unsigned int nMessageStartTestSwitchTime = 1346200000;

// PPCoin message start (switch from Bitcoin's in v0.2)


static unsigned char pchMessageStartBitcoin[4] = { 0xf9, 0xbe,
0xb4, 0xd9 };
static unsigned char pchMessageStartPPCoin[4] = { 0xe6, 0xe8,
0xe9, 0xe5 };
static unsigned int nMessageStartSwitchTime = 1347300000;

there is a lot of Peercoin/Bitcoin history in the source code, that is totally superfluous, but since
were only learning stuff, it doesnt matter.
However, regarding these network identifiers, replace with something like this. (Dont use these
values, though, they are taken from Fencoin, a coin that Im developing now, and I dont want it to
collide with your coin out there. Think up some hex values of your own, OK?)

static unsigned char pchMessageStartMain[4] = { 0x29, 0x29, 0x65,


0x14 };
static unsigned char pchMessageStartTest[4] = { 0x14, 0x29, 0x29,
0x65 };

Then, replace

void GetMessageStart(unsigned char pchMessageStart[], bool


fPersistent)
{
if (fTestNet)
memcpy(pchMessageStart, (fPersistent || GetAdjustedTime()
> nMessageStartTestSwitchTime)? pchMessageStartTestNew :
pchMessageStartTestOld, sizeof(pchMessageStartTestNew));
else
memcpy(pchMessageStart, (fPersistent || GetAdjustedTime()
> nMessageStartSwitchTime)? pchMessageStartPPCoin :
pchMessageStartBitcoin, sizeof(pchMessageStartPPCoin));
}

with

void GetMessageStart(unsigned char pchMessageStart[], bool


fPersistent)
{
if (fTestNet)
memcpy(pchMessageStart, pchMessageStartTest,
sizeof(pchMessageStartTest));
else
memcpy(pchMessageStart, pchMessageStartMain,
sizeof(pchMessageStartMain));
}

Then delete the second argument fPersistent, and do so in declaration in the header file too
Search for usage of GetMessageStart and remove the boolean where it occurs

Let's reset the version to something else


version cpp

#include "version.h"
// Name of client reported in the 'version' message. Report the
same name
// for both bitcoind and bitcoin-qt, to make it harder for
attackers to
// target servers or GUI users specifically.
const std::string CLIENT_NAME("Satoshi");

// Client version number


#define CLIENT_VERSION_SUFFIX "-beta"

after that you switch to header and if you wish you can edit

#define PPCOIN_VERSION_MAJOR 0
#define PPCOIN_VERSION_MINOR 0
#define PPCOIN_VERSION_REVISION 0
#define PPCOIN_VERSION_BUILD 1

Go below and comment out

// only request blocks from nodes outside this range of versions


//static const int NOBLKS_VERSION_START = 32000;
//static const int NOBLKS_VERSION_END = 32400;

Those constants are found in main.cpp: 2804, comment out

// Ask the first connected node for block updates


static int nAskedForBlocks = 0;
if (!pfrom->fClient &&
//(pfrom->nVersion < NOBLKS_VERSION_START ||
//pfrom->nVersion >= NOBLKS_VERSION_END) &&
(nAskedForBlocks < 1 || vNodes.size() <= 1))
{
nAskedForBlocks++;
pfrom->PushGetBlocks(pindexBest, uint256(0));
}

Back in version.h, do this replace

// nTime field added to CAddress, starting with this version;


// if possible, avoid requesting addresses nodes older than this
//static const int CADDR_TIME_VERSION = 31402;
static const int CADDR_TIME_VERSION = 1;

That's all for versioning

Now let's take care of checkpoints

Let's generate a keypair for our future checkpoint server


Open a new terminal tab and make sure you're in the same directory as ppcoind, then
$ ./ppcoind makekeypair
{
"PrivateKey" : " < your private key >",
"PublicKey" :
"04d1a358d2898638f374726cfdd45220fffae53f147d60d939b6847b90bed5bbadf6c5884ee02d7729f
4954e31e97b2705cd6561467217aa0cd9b5d66360ae014c"
}

Stop the node


$ ./ppcoind stop

Back to qcreator
replace in checkpoints cpp
// sync-checkpoint master key
const std::string CSyncCheckpoint::strMasterPubKey =
"04c0c707c28533fd5c9f79d2d3a2d80dff259ad8f915241cd14608fb9bc07c748
30efe8438f2b272a866b4af5e0c2cc2a9909972aefbd976937e39f46bb38c277c"
;

with the pubkey that your node gave to you

Put the private key to safekeeping, you're going to use it on your server
with checkpointkey=<the private key> in the conf file

Remove hardcoded checkpoints from checkpoints.cpp


Leave the official genesis block

static MapCheckpoints mapCheckpoints =


boost::assign::map_list_of
( 0, hashGenesisBlockOfficial )
;

Changing PoS

Now let's look at some parameters in main.h

static const int64 MAX_MINT_PROOF_OF_WORK = 9999 * COIN;

static const int STAKE_TARGET_SPACING = 10 * 60; // 10-minute


block spacing
static const int STAKE_MIN_AGE = 60 * 60 * 24 * 30; // minimum age
for coin age
static const int STAKE_MAX_AGE = 60 * 60 * 24 * 90; // stake age
of full weight

You might add here a


static const int LAST_POW_BLOCK = <block number>;
-
then in main cpp
if (IsProofOfWork() && nHeight > LAST_POW_BLOCK)
return DoS(100, error("AcceptBlock() : reject proof-
of-work at height %d", nHeight));

// Check proof-of-work or proof-of-stake


if (nBits != GetNextTargetRequired(pindexPrev,
IsProofOfStake()))
return DoS(100, error("AcceptBlock() : incorrect proof-of-
work/proof-of-stake"));

New genesis block (testnet)

In main.cpp, heres the code

//
// Init with genesis block
//
if (mapBlockIndex.empty())
{
if (!fAllowNew)
return false;

// Genesis Block:
// CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000,
hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893,
vtx=1)
// CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1,
nLockTime=0)
// CTxIn(COutPoint(000000, -1), coinbase
04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72
206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73)
// CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
// vMerkleTree: 4a5e1e

// Genesis block
const char* pszTimestamp = "Matonis 07-AUG-2012 Parallel Currencies And
The Roadmap To Monetary Freedom";
CTransaction txNew;
txNew.nTime = 1345083810;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) <<
vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned
char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout[0].SetEmpty();
CBlock block;
block.vtx.push_back(txNew);
block.hashPrevBlock = 0;
block.hashMerkleRoot = block.BuildMerkleTree();
block.nVersion = 1;
block.nTime = 1345084287;
block.nBits = bnProofOfWorkLimit.GetCompact();
block.nNonce = 2179302059u;

if (fTestNet)
{
block.nTime = 1345090000;
block.nNonce = 122894938;
}

//// debug print


printf("%s\n", block.GetHash().ToString().c_str());
printf("%s\n", hashGenesisBlock.ToString().c_str());
printf("%s\n", block.hashMerkleRoot.ToString().c_str());
assert(block.hashMerkleRoot ==
uint256("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2"));
block.print();
assert(block.GetHash() == hashGenesisBlock);
assert(block.CheckBlock());

Lets modify this.

Lets make the debug print nicer


//// debug print
printf("My beautiful block hash %s\n",
block.GetHash().ToString().c_str());
printf("hashGenesisBlock (hardcoded) %s\n",
hashGenesisBlock.ToString().c_str());
printf("block merkle root %s\n",
block.hashMerkleRoot.ToString().c_str());

Then the magic ingredient the genesis-mining-code , paste this above debug print

if (true && (block.GetHash() != hashGenesisBlock))


{
printf("Mining genesis\n");
uint256 hashTarget =
CBigNum().SetCompact(block.nBits).getuint256();
while (block.GetHash() > hashTarget)
{
++block.nNonce;
if (block.nNonce == 0)
{
printf("NONCE WRAPPED, incrementing time");
++block.nTime;
}
if (block.nNonce % 10000 == 0) {
printf("nonce %08u: hash = %s \n", block.nNonce,
block.GetHash().ToString().c_str());
}
}
}

See the definition for block.nTime ?

Get current timestamp

$ date +%s
1493226199

Now plug that in and reset the nonce

if (fTestNet)
{
block.nTime = 1493226199;
block.nNonce = 0;
}

And lets redo the human-readable timestamp


// Genesis blockk
const char* pszTimestamp = "Chancellor on brink of whatever the daily
paper says";

Build the headless client in the src directory


Were still on testnet, as it is specified on the conf file
Go to the .ppcoin folder and delete everything inside testnet dir

Run ./ppcoind -printtoconsole

PPCoin version v0.5.4ppc-3-g668ab24-dirty-stanislaw (2017-04-26 14:31:41 +0300)


Default data directory /home/jarkko/.ppcoin
ppcoin server starting
Loading addresses...

And then
My beautiful block hash
14c563d82d3dc1f75185b60dd081130690a116c36ce590555b65ca9d1a38cb3d
hashGenesisBlock (hardcoded)
00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06
block merkle root
172bffb461d76bffadc16d8f78bb2650288b434c3441603302903e29eb2fab42
ppcoind: main.cpp:2355: bool LoadBlockIndex(bool): Assertion
`block.hashMerkleRoot ==
uint256("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2")'
failed.

we need only the merkle root. Put it into the assertion code.

assert(block.hashMerkleRoot ==
uint256("0x172bffb461d76bffadc16d8f78bb2650288b434c3441603302903e2
9eb2fab42"));

Lets change the PoW limit to something easy that we dont have to wait all day for it to mine

bool LoadBlockIndex(bool fAllowNew)


{
if (fTestNet)
{
hashGenesisBlock = hashGenesisBlockTestNet;
bnProofOfWorkLimit = CBigNum(~uint256(0) >> 16);
nStakeMinAge = 60 * 60 * 24; // test net min age is 1 day
nCoinbaseMaturity = 60;
bnInitialHashTarget = CBigNum(~uint256(0) >> 20);
nModifierInterval = 60 * 20; // test net modifier interval is 20 minutes
}

again build the client and run. Now the mining for genesis starts. This is what I got
Mining genesis
nonce 00010000: hash =
a795e25979854e9a3b282905682a7bd2a5d39b9191c4c247848de70d7dbf3310
nonce 00020000: hash =
a48b7db1d24e06838e7889e7bfa5238e040ba76ca86d1b48f21de5ba37027a84
nonce 00030000: hash =
35d0c768e02f46f47d161f53544fe0f9ca2272d3deee3358d6660ac4aad03c2d
nonce 00040000: hash =
34fea0087334542d614adc002e6d9aed4956367bb0e1532025f65a77c9838f72
nonce 00050000: hash =
90a85eb12331b2971f76d7dfcbf422994c1059b37df3018b0df6dcb67f551498
nonce 00060000: hash =
2790491ca7e08b9dc169e161398422178809a18b9f826c16188b4f592890b358

My beautiful block hash


0000e9369064f518c37c40707c9d580eaa4b9d100fb848ca33ea5264ca9cc510
hashGenesisBlock (hardcoded)
00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06
block merkle root 172bffb461d76bffadc16d8f78bb2650288b434c3441603302903e29eb2fab42

CBlock(hash=0000e9369064f518c37c, ver=1, hashPrevBlock=00000000000000000000,


hashMerkleRoot=172bffb461, nTime=1493226199, nBits=1f00ffff, nNonce=68425, vtx=1,
vchBlockSig=)
Coinbase(hash=172bffb461, nTime=1345083810, ver=1, vin.size=1, vout.size=1, nLockTime=0)
CTxIn(COutPoint(0000000000, -1), coinbase
04ffff001d020f272431372d30342d323654697272697573206d696e6f7269732061766f73756f69656
e736973)
CTxOut(empty)
vMerkleTree: 172bffb461
ppcoind: main.cpp:2377: bool LoadBlockIndex(bool): Assertion `block.GetHash() ==
hashGenesisBlock' failed.

We have a winner here. Put the My beautiful block hash into genesis block hash, in this case
in main.h

static const uint256


hashGenesisBlockTestNet("0x0000e9369064f518c37c40707c9d580eaa4b9d1
00fb848ca33ea5264ca9cc510");

Then update the nonce

if (fTestNet)
{
block.nTime = 1493226199;
block.nNonce = 68425;
}

Set the flag to false in your genesis-mining-code


if (false && (block.GetHash() != hashGenesisBlock))

In main.cpp, disable the timestamp check if on testnet

// Check coinbase timestamp


//disabling this for testnet
//see Hoshimaru's remarks at
https://bitcointalk.org/index.php?topic=1025299.10
if (!fTestNet && GetBlockTime() > (int64)vtx[0].nTime +
nMaxClockDrift)
return DoS(50, error("CheckBlock() : coinbase timestamp is
too early"));

Rebuild and run.

You have now an unique coin, with an identity, network and genesis of its own.

Private test network


Set up Virtualbox

sudo apt-get install virtualbox

Then get ISO from


https://help.ubuntu.com/community/Lubuntu/Documentation/MinimalInstall

Let's get a small 32-bit OS


Ubuntu 14.04 LTS "Trusty Tahr" 31MB (MD5: a2502844750ecb6477d8fb4ff6b9aaf8, SHA1:
d17c34ce716f13396040ccdc02d32482ed6b01a1)

$ md5sum mini32.iso
a2502844750ecb6477d8fb4ff6b9aaf8 mini32.iso

Start virtualbox

(what is below follows http://www.cs.jhu.edu/~joanne/virtualBox.html)


Make new VM, give it a name and select ubuntu as the OS
Leave the 512MB memory default, you can change it later
Let it create a hard drive, leave the default type VDI, dynamically allocated etc

Now that it stands there, go to settings->storage

Set up the virtual cd drive


Choose your virtual disk, it should show up under Controller IDE
To copy-paste Go to general->advanced-> set shared clipboard to birectional
OK, boot it from Start

Installation follows, ought to be straightforward.

After it has installed, close the VM. Go to settings and remove the iso file under controller IDE
and restart. Log in with the details you entered during the installation.

Get these utils


sudo apt-get install virtualbox-guest-utils virtualbox-guest-x11 virtualbox-guest-dkms

Reboot.
Network with the guest virtual machine
So we can mine some blocks!
Follow this
https://2buntu.com/articles/1513/accessing-your-virtualbox-guest-from-your-host-os/

Open your linux VM in virtualbox


Go to FilePreferences in tht Virtualbox Manager window
select NetworkHost-only Networks
and add new. See that the DHCP server details match
Then in the settings of the VM
add a new adapter and attach it to your host-only adapter
Start your VM
Issue ifconfig If you are not seeing a new interface, then issue ifconfig -a
Take note of the new interfaces name and add it to
/etc/network/interfaces file :
auto eth1
iface eth1 inet dhcp

Save file, reboot VM


Issue ifconfig, take note of the IP
ping IP of your VM from your host machine

Now edit your conf file and put this there

rpccuser="enter anything as a username"


rpcpassword="enter anything for a password"
rpcport=
server=1
listen=1
daemon=1
testnet=1
addnode=<VM IP>

On the VM edit the conf accordingly, adding your host IP


Open your two nodes, one on your host, one on your VM
Check that they connect.
Start mining with either client, or both

./ppcoind setgenerate true

GRAPHICS etc

Now let's do a paint job over the QT wallet


In /src/qt/bitcoingui.cpp:

BitcoinGUI::BitcoinGUI(QWidget *parent):
QMainWindow(parent),
clientModel(0),
walletModel(0),
encryptWalletAction(0),
changePassphraseAction(0),
unlockWalletAction(0),
lockWalletAction(0),
aboutQtAction(0),
trayIcon(0),
notificator(0),
rpcConsole(0)
{
resize(850, 550);
setWindowTitle(tr("Fencoin"));

The bitmaps go into here

/src/qt/res/icons - This is the folder that contains all the icons for your wallet
/src/qt/res/images - This is the splash (loading) screen for your new coin

Set up server

Get one from online.net or somewhere, for example here (no affiliate)
https://www.online.net/en/dedicated-cloud#starter-cloud

if you did that, just follow


https://community.online.net/t/how-to-create-your-own-peercoin-full-active-node-running-on-
debian-with-online-labs-cloud/680

You might also like