5

如何仿照比特币创造自己的山寨币(二)

 2 years ago
source link: https://shusunny.github.io/2016/04/how-to-make-altcoin-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
2016-04-14 • bitcoin C++

如何仿照比特币创造自己的山寨币(二)

上次我们把比特币中的所有文件名及内容都改成了我们的新币newcoin。但是这还远远不够。 why? 因为所有的内核参数还是比特币的,所以即使运行了客户端,也会同步之前的比特币数据。 现在是时候进入代码的内部,探寻比特币的奥秘,并做一些真正属于我们的改动。

修改区块链参数

首先我们需要进入chainparams.cpp

cd src
sudo gedit chainparams.cpp

然后我们在代码中搜索mapCheckpoints,在第55行,它大概是这个样子的:

static Checkpoints::MapCheckpoints mapCheckpoints =
      boost::assign::map_list_of
      ( 11111, uint256("0x00223155*****"));

这里会显示一大串数据,分别是不同区块的hash值,是比特币客户端用来验证之前区块值是否正确。 而我们需要把11111那一行的数值改为

( 0, uint256("0x001"));

这里,0代表的是第几个区块。0后面的uint256表示的就是初始块的hash值。 可是我们的新币还没有正式运行呢?怎么得到它的hash值呢?我们下面会讲如果挖创始块。这里我们姑且做一个大胆并且绝对错误的推断,让其等于001。

同样的,我们还需要搜索并更改mapCheckpointsTestnetmapCheckpointsRegtest 同一位置的数据。可能他们只有一行,并非大串数据。他们表示的是测试网络的区块值。 没关系,大胆的把他们全部变成( 0, uint256("0x001"));。

现在我们返回mapCheckpoints,在刚才的数据地图的下面,我们可以看到

1397080064, // * UNIX timestamp of last checkpoint block
36544669,   // * total number of transactions between genesis and last checkpoint
            //   (the tx=... number in the SetBestChain debug.log lines)
60000.0     // * estimated number of transactions per day after checkpoint

这里通过英文注释我们可以看到,第一行代表上个检查点的时间,第二行是创始块直到上次检查时的交易数量。第三行表示平均每天的交易量。我们运行

date +%s

将上面命令行得到的值替换到上述bitcoin代码的第一行,第二行交易数量随便填,估且填为10吧。第三行看情况来,先填成10000吧。

将同样的时间和交易数量数据像上面那样填进test和regtest的信息里。平均交易量填一半就好。

mapCheckpointsRegtest下是CMainParams函数。在函数中有四行指针代码表示pchMessageStart。一般作为用来对比特币的交易进行验证的协议(protocol)。其中每个数组的值都是0-255中的数值,以16进制的形式表示。他们一旦改变,任何人都无法将我们的客户端连进比特币的了。

我们可以在http://numbermonk.com/?all=1 的表中从0-255找到我们喜欢的数字填进去。如果可以的话,再找8个填进CTestNetParamsCRegTestParams

下面的代码是vAlertPubKey。所以我们需要更改alert和genesis coinbase的key值。我们用命令行生成一些

openssl ecparam -genkey -name secp256k1 -out alertkey.pem
openssl ec -in alertkey.pem -text > alertkey.hex
openssl ecparam -genkey -name secp256k1 -out testnetalert.pem
openssl ec -in testnetalert.pem -text > testnetalert.hex
openssl ecparam -genkey -name secp256k1 -out genesiscoinbase.pem
openssl ec -in testnetalert.pem -text > genesiscoinbase.hex

这样我们就得到了一系列alertkey和genesiscoinbase key。我们首先打开alertkey

cat alertkey.hex

我们把‘pub’和‘ASN1 OID: secp256k1′中间的5行数值去掉冒号和空格填到vAlertPubKey = ParseHex(“…”);里去。然后我们再用cat testnetalert.hex把其中的数据填入到CTestNetParams函数中去。

下面我们更改时间标签pszTimestamp。在这一行

const char* pszTimestamp = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";

我们去外国网站上找到一条今天的新闻(但不要太长,最好小于90个字母)填进去。

接下来我们将创始块的pubkey填进去。打开cat genesiscoinbase.hex,在

txNew.vout[0].scriptPubKey = CScript() << ParseHex("...") << OP_CHECKSIG;

中插入我们的key值。同样的去掉冒号和空格。

接下来,我们需要将

assert(hashGenesisBlock == uint256("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"));
assert(genesis.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));

这两行用//注释掉,以免在我们找初始块的时候报错影响程序运行。所有的文件改完别忘了保存一下。

下面的vSeeds表示的种子文件的位置,我们还没有种子文件的域名,先不要进行改动。

挖取创始块

现在我们开挖,打开main.cpp

sudo gedit main.cpp

我们在这一行后面

CBlock &block = const_cast<CBlock&>(Params().GenesisBlock());
// Start new block file
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);

这一行前面,加入我们挖创始块的代码

uint256 bnTarget;
bool fNegative;
bool fOverflow;
uint256 hashGenesisBlock;
block.nBits    = 0x1f00ffff;
bnTarget.SetCompact(block.nBits, &fNegative, &fOverflow);
LogPrintf("ProofOfWorkLimit %s\n", Params().ProofOfWorkLimit().ToString());
if (fNegative || bnTarget == 0 || fOverflow || bnTarget > Params().ProofOfWorkLimit()) {
	error("InitBlockIndex CheckProofOfWork() : nBits below minimum work");
}else {
	block.nTime    = GetTime();//1231006505;
	LogPrintf("block.nTime %d\n", block.nTime);
	LogPrintf("bnTarget %s\n", bnTarget.ToString());
	block.nNonce = 0;
	while (true) {
		if (block.nNonce%1000000000 == 0)
			LogPrintf("block.nNonce--- %d\n", block.nNonce);
		hashGenesisBlock = block.GetHash();
		if (hashGenesisBlock <= bnTarget)
			break;
		block.nNonce++;
	}
	LogPrintf("block.nNonce******** %d\n", block.nNonce);
	LogPrintf("hashGenesisBlock******** %s\n", hashGenesisBlock.ToString());
	LogPrintf("hashMerkleRoot******** %s\n", block.hashMerkleRoot.ToString());
}

这段代码是会在我们程序运行的时候不断计算,并在debug.log中导出我们所需要的GenesisBlock的Hash值。保存并返回newcoin文件夹。重新编译

cd ..
make

如果我们上一篇中环境安装完全的话,应该会秒通过。然后我们运行我们的程序

cd src/qt
sudo ./newcoin-qt

然后程序会通知我们将文件目录放在哪里,用默认的设置就好。这时程序可能会跑一段时间崩溃掉。没关系,我们用

tail ~/.newcoin/debug.log

进入到debug文件中去。看看我们是否导出了以下数值

2016-04-11 21:16:20 ProofOfWorkLimit 00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
2016-04-11 21:16:20 block.nTime 1460410772
2016-04-11 21:16:20 bnTarget 0000ffff00000000000000000000000000000000000000000000000000000000
2016-04-11 21:16:20 block.nNonce--- 0
2016-04-11 21:16:20 block.nNonce******** 43973
2016-04-11 21:16:20 hashGenesisBlock******** 0000f51a735d0f7da96f10797ad76e00e53614422987f2b6056aaf61857e165e
2016-04-11 21:16:20 hashMerkleRoot******** d5c218da6f5c257709c0d59177ae4527ea5049f15baf26260a071c2e212154ac
2016-04-11 21:16:20 Pre-allocating up to position 0x1000000 in blk00000.dat
2016-04-11 21:16:20 Pre-allocating up to position 0x100000 in rev00000.dat

如果有,那么恭喜你,你已经挖到了创始块,那么让我们返回到chainparams.cpp中,将hashGenesisBlock,hashMerkleRoot填入到刚才的区块地图中,即uint256(“0x001”),将001改为我们前面得到的hashGenesisBlock值,并将其填入已经被我们注释掉的assert中,并把注释去掉。 同样的,将上面log中block.nTime和block.nNonce的值填回到CMainParams的genesis.nTime和genesis.nNonce中去。他们在下面这行代码的上边。

hashGenesisBlock = genesis.GetHash();

保存。现在我们已经得到了我们的初始块,别忘了将main.cpp中我们刚才填加的代码删掉,这样我们就完全构建出了一套属于我们的电子币系统。并且不会出现像比特币那样挖不着的情况。(毕竟是我们自己的,想挖多少就挖多少。哈哈哈)

你也可以将上面的程序额外运行两次,并把得到的hash值和各参数填入到Testnet和Regtest中去。

下面我们返回newcoin文件夹中,重新编译,并删除原来的文件目录(非常重要)

cd ..
sudo make
rm -rf ~/.newcoin

然后运行起来。如果一切顺利的话,程序会运行起来,然后我们就可以得到下面的程序

不用羡慕上面的数值,既然所有的参数都是我们自己改的,当然可以在main.cpp里面修改区块数据,从而在短时间内挖到最多的币。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK