作者:AngryFox 分类: Uncategorized April 18th, 2018 暂无评论

#centos升级gcc到4.8.1(支持c++11)步骤

##下载gcc最新版

wget http://ftp.gnu.org/gnu/gcc/gcc-4.8.1/gcc-4.8.1.tar.gz
然后解压到文件夹

tar -xvzf gcc-4.8.1.tar.gz
进入解压缩之后的目录

cd gcc-4.8.1
然后执行下面的运行

./contrib/download_prerequisites
再返回上一层,建立build_gcc_4.8.1目录,这个目录和gcc-4.8.1平行

cd ..
mkdir build_gcc_4.8.1
进入刚建立的目录,并执行编译过程

cd build_gcc_4.8.1
../gcc-4.8.1/configure –enable-checking=release –enable-languages=c,c++ –disable-multilib
make -j23
make install
OK,在build_gcc_4.8.1中将gcc已经安装完成

确定新安装的GCC的路径,之前安装时记下最后mv时的路径即可,我是默认安在了/usr/local/bin

ls /usr/local/bin | grep gcc
看图

执行

/usr/sbin/update-alternatives –install /usr/bin/gcc gcc /usr/local/bin/x86_64-unknown-linux-gnu-gcc-4.8.1 40

gcc –version #查看版本

/usr/sbin/update-alternatives –install /usr/bin/g++ g++ /usr/local/bin/g++ 40

g++ –version #查看版本
来源:http://lonelyprogram.blog.51cto.com/6246243/1355261

作者:AngryFox 分类: Uncategorized April 8th, 2018 暂无评论

头文件就是库
使用者最常问的问题就是“我该怎么安装Boost”,这个也是我一开始最关心的问题,Boost这点做的很好,将大部分实现都封装在头文件里,所以对于一些基本的Boost库,其实是不需要安装的,只需要将头文件include到自己的程序里,当然前提是你把Boost的所有用到的头文件都拷贝了一份。
Boost是如何做到这点的?
这是因为Boost的头文件(*.hpp)包含了模板和内联函数,这点随便找一个hpp文件来看你就明白了,所以不需要去静态链接活动态链接二进制lib库了。
不过Boost的某些库还是需要生成二进制的库的,如果你要使用他们,必须编译安装哦,他们是:
Boost.Filesystem
Boost.IOStreams
Boost.ProgramOptions
Boost.Python
Boost.Regex
Boost.Serialization
Boost.Signals
Boost.Thread
Boost.Wave
命名规则
libboost_filesystem-vc80-mt-sgdp-1_42.lib
前缀:统一为lib,但在Windows下只有静态库有lib前缀;
库名称:以”boost一”开头的库名称,在这里是boost_filesystem;
编译器标识:编译该库文件的编译器名称和版本,在这里是-vc80;
多线程标识:支持多线程使用-mt,没有表示不支持多线程;
ABI标识:这个标识比较复杂,标识了Boost库的几个编译链接选项;
s: 静态库标识;
gd:debug版标识;
p: 使用STlport而不是编译器自带STL实现;
版本号:Boost库的版本号,小数点用下画线代替,在这里是1_42;
扩展名:在Windows上是lib,在Linux等类Unix操作系统上是a或者.so。
几个常用宏
#define BOOST_ALL_DYN_LINK
同样,此时boost也会默认帮我们包含对应的lib。如果不想使用boost提供的auto-link机制,或者对它的自动链接不太放心的话(其实大可不必担心),可以预先定义宏:
#define BOOST_ALL_NO_LIB
然后使用以下方法链接:
#pragma comment(lib, “boost_thread-vc100-mt-1_46.lib”)或
#pragma comment(lib, “boost_thread-vc100-mt.lib”)
这两个lib其实是一样的,实在不明白boost编译时为什么每个库都要复制一份,难道是因为后者在升级boost版本后不用改代码?另外还有一个比较有用的宏:
#define BOOST_LIB_DIAGNOSTIC
它可以让VC在编译时的output窗口中输出程序具体链接了哪些boost库以及链接顺序。
加快Boost编译的方法
可以采用预编译头来解决这个问题。

update 三个命令的区别

apt-get update 更新软件源中的所有软件列表。
apt-get upgrade 更新软件。
apt-get dist-upgrade 更新系统版本。如果你对新版本软件的需求不是那么迫切,可以不执行

作者:AngryFox 分类: Uncategorized April 8th, 2018 暂无评论

不小心删除了lib库 终极恢复命令
apt-get坏掉了,dpkg也说错误太多
如果这个也不行 那只能重装了

sudo dpkg -i /var/cache/apt/archives/*.deb
sudo dpkg –configure -a

cenos上安装 apt-get

http://www.rpm-find.net/linux/rpm2html/search.php?query=rpmforge-release

#!/bin/bash
#这个脚本最后都加了-y代表直接输入y的意思,效果相当于一个回车
#安装gnome3、ubuntu-tweak和歌词osdlyrics,添加源并更新
sudo add-apt-repository ppa:tualatrix/ppa -y
sudo add-apt-repository ppa:gnome3-team/gnome3 -y
sudo add-apt-repository ppa:osd-lyrics/ppa
sudo apt-get update
#在终端中打开
sudo apt-get install nautilus-open-terminal -y
#gnome-do
sudo apt-get install gnome-do -y
#必须的第三方软件
sudo apt-get install ubuntu-restricted-extras -y

#编程必须
sudo apt-get install build-essential scons -y
#编程工具codelite和codeblocks
sudo apt-get install codelite -y
sudo apt-get install codeblocks -y

#stardict
sudo apt-get install stardict -y

#浏览器
sudo apt-get install chromium-browser -y
#wireshark
sudo apt-get install wireshark -y
#iptux信使,相当于飞鸽飞秋
sudo apt-get install iptux -y
#显示天气、系统温度和系统负载情况
sudo apt-get install indicator-weather -y
sudo apt-get install indicator-multiload -y
sudo apt-get install psensor -y
#gnome3、tweak和osdlyrics的真正安装部分
sudo apt-get dist-upgrade -y
sudo apt-get install gnome-shell -y
sudo apt-get install ubuntu-tweak -y
#肯定系统需要更新一些软件包
sudo apt-get upgrade -y
#重启
sudo reboot

Ubuntu配置文件系统初始化

1. /etc/timezone 时区
2. /etc/inetd.conf 超级进程

Ubuntu配置文件文件系统

1. /etc/fstab 开机时挂载的文件系统
2. /etc/mtab 当前挂载的文件系统

Ubuntu配置文件用户系统

1. /etc/passwd 用户信息
2. /etc/shadow 用户密码
3. /etc/group 群组信息
4. /etc/gshadow 群组密码
5. /etc/sudoers Sudoer列表(请使用“visudo”命令修改此文件,而不要直接编辑)

Ubuntu配置文件Shell

1. /etc/shell 可用Shell列表
2. /etc/inputrc ReadLine控件设定
3. /etc/profile 用户首选项
4. /etc/bash.bashrc bash配置文件

Ubuntu配置文件系统环境

1. /etc/environment 环境变量
2. /etc/updatedb.conf 文件检索数据库配置信息
3. /etc/issue 发行信息
4. /etc/issue.net
5. /etc/screenrc 屏幕设定

Ubuntu配置文件网络

1. /etc/iftab 网卡MAC地址绑定
2. /etc/hosts 主机列表
3. /etc/hostname 主机名
4. /etc/resolv.conf 域名解析服务器地址
5. /etc/network/interfaces 网卡配置文件

Ubuntu配置文件via 来自Ubuntu部落的教程

用DD命令生成引导文件方法:
dd if=/dev/hdax of=/mnt/fat32/bootsect.lnx bs=512 count=1 并口硬盘
dd if=/dev/sdax of=/mnt/fat32/bootsect.lnx bs=512 count=1 串口硬盘
命令解释:
hdax/sdax上面已经解释过了~~~~生成的引导文件名字就是bootsect.lnx
弄好之后进入Windows把生成的引导文件COPY进C盘,在boot.ini加C:”bootsect.lnx=”Ubuntu”,OK!

//最终解决问题O(∩_∩)O哈哈~
$ sudo apt-get install apt-transport-https
$ sudo apt-key adv –keyserver hkp://keyserver.ubuntu.com:80 –recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
$ sudo bash -c “echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list”
$ sudo apt-get update
$ sudo apt-get install lxc-docker

作者:AngryFox 分类: Uncategorized April 4th, 2018 暂无评论

mongodb replSet

一般启动

/opt/mongodb/bin/mongod –dbpath=/data/mongodata/ –fork –logpath=/data/mongodata/logs/mongodb.log
replSet 启动

/opt/mongodb/bin/mongod –dbpath=/data/mongodata/ –fork –logpath=/data/mongodata/logs/mongodb.log –replSet rs0 27017
/opt/mongodb/bin/mongod –dbpath=/data/mongodata/rs1/ –fork –logpath=/data/mongodata/rs1/mongodb.log –replSet rs0 –port 27018

###在安装文件下有README,描述了常用的mongodb相关命令行工具
# more /usr/local/mongodb/README
MongoDB README
Welcome to MongoDB!
COMPONENTS

bin/mongod – The database process.
bin/mongos – Sharding controller.
bin/mongo – The database shell (uses interactive javascript).

UTILITIES

bin/mongodump – MongoDB dump tool – for backups, snapshots, etc..
bin/mongorestore – MongoDB restore a dump
bin/mongoexport – Export a single collection to test (JSON, CSV)
bin/mongoimport – Import from JSON or CSV
bin/mongofiles – Utility for putting and getting files from MongoDB GridFS
bin/mongostat – Show performance statistics

作者:AngryFox 分类: Uncategorized April 4th, 2018 暂无评论

VirtualBox 配置虚拟网卡(桥接),实现主机-虚拟机网络互通

环境:
  在Oracle VM VirtualBox中安装的Ubuntu,具体版本名是:ubuntu-14.10-server-amd64

Step1:
VirtualBox在虚拟机关机状态下 设置桥接

Step 2:
虚拟机开机,输入命令:sudo ifconfig或者sudo ifconfig -a查看当前网卡配置

查看当前ip地址
Step3:

  输入命令:vim /etc/network/interfaces

  打开网卡配置文件,添加eth1,保存修改。修改后的文件内容,如下:

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet dhcp

auto eth1
iface eth1 inet static
#本机当前ip地址,通过Step2中ifconfig查询出来的即可
address 192.168.56.11
netmask 255.255.255.0

Step4:
  重启虚拟机(必要时重启主机电脑),命令ifconfig查看当前网卡设置。能看到eth1,并且有值,即可。

Step5:
  1) Windows的cmd中,命令:ipconfig查看当前ip
结果:

至此,虚拟机与主机桥接网卡成功,ip互通,且虚拟机成功通过主机网络访问外网。
四、启动ssh服务

安装ssh-server

sudo apt-get install openssh-server

然后重启虚拟机

确认sshserver是否安装好

ps -e | grep ssh

16224 ? 00:00:00 sshd

如果看到sshd那说明ssh-server已经启动了。

可以使用下述命令查看是否有进程在22端口上监听,即是否已启动:

netstat -nat | grep 22

扩展配置(未尝试)

ssh的配置文件位置:/etc/ssh/sshd_config

设置允许root登录:

注释这一行:PermitRootLogin without-password

在修改完配置之后记得保存并重启SSH 服务:

sudo/etc/init.d/ssh restart 或者 service ssh restart

作者:AngryFox 分类: Uncategorized April 3rd, 2018 暂无评论

可以总结出4个实体:区块、交易、输入、输出。而其中的关系是,一个区块对应多个交易,一个交易对应多个输入和多个输出。除了Coinbase的输入外,一笔输入对应另一笔交易中的输出。于是我们可以得出这样的数据模型:

需要特别说明几点的是:

1.TxId是自增的int,我没有用TxHash做Transaction的PK,那是因为TxHash根本就不唯一啊!有好几个不同区块里面的第一笔交易,也就是Coinbase交易是相同的。这其实应该是异常数据,因为相同的TxHash将导致只能花费一次,所以这个矿工杯具了。

2.对于一笔Coinbase 的Transaction,其输入的PreOutTxId是0000000000000000000000000000000000000000000000000000000000000000,而其PreOutIndex是-1,这是一条不存在的TxOutput,所以我并没有建立TXInput和TxOutput的外键关联。

3.对于Block,PreId就是上一个Block的ID,而创世区块的PreId是0000000000000000000000000000000000000000000000000000000000000000,也是一个不存在的BlockId,所以我没有建立Block的自引用外键。

4.有很多字段其实并不是区块链数据结构中的,这些字段是我添加为了接下来方便分析用的。在导入的时候并没有值,需要经过一定的SQL运算才能得到。比如Trans里面的TotalInAmount,TransFee等。

create table Block (
   Height               int                  not null,
   BlkId                char(64)             not null,
   TxCount              int                  not null,
   Size                 int                  not null,
   PreId                char(64)             not null,
   Timestamp            datetime             not null,
   Nonce                bigint               not null,
   Difficulty           double precision     not null,
   Bits                 char(64)             not null,
   Version              int                  not null,
   TxMerkleRoot         char(64)             not null,
   constraint PK_BLOCK primary key nonclustered (BlkId)
)
go

/*==============================================================*/
/* Index: Block_Height                                          */
/*==============================================================*/
create unique clustered index Block_Height on Block (
Height ASC
)
go

/*==============================================================*/
/* Table: Trans                                                 */
/*==============================================================*/
create table Trans (
   TxId                 int                  not null,
   BlkId                char(64)             not null,
   TxHash               char(64)             not null,
   Version              int                  not null,
   InputCount           int                  not null,
   OutputCount          int                  not null,
   TotalOutAmount       bigint               not null,
   TotalInAmount        bigint               not null,
   TransFee             bigint               not null,
   IsCoinbase           bit                  not null,
   IsHeightLock         bit                  not null,
   IsTimeLock           bit                  not null,
   LockTimeValue        int                  not null,
   Size                 int                  not null,
   TransTime            datetime             not null,
   constraint PK_TRANS primary key (TxId)
)
go

/*==============================================================*/
/* Index: Relationship_1_FK                                     */
/*==============================================================*/
create index Relationship_1_FK on Trans (
BlkId ASC
)
go

/*==============================================================*/
/* Index: Trans_Hash                                            */
/*==============================================================*/
create index Trans_Hash on Trans (
TxHash ASC
)
go

/*==============================================================*/
/* Table: TxInput                                               */
/*==============================================================*/
create table TxInput (
   TxId                 int                  not null,
   Idx                  int                  not null,
   Amount               bigint               not null,
   PrevOutTxId          char(64)             not null,
   PrevOutIndex         int                  not null,
   PaymentScriptLen     int                  not null,
   PaymentScript        varchar(8000)        not null,
   Address              char(58)             null,
   constraint PK_TXINPUT primary key (TxId, Idx)
)
go

/*==============================================================*/
/* Index: Relationship_2_FK                                     */
/*==============================================================*/
create index Relationship_2_FK on TxInput (
TxId ASC
)
go

/*==============================================================*/
/* Table: TxOutput                                              */
/*==============================================================*/
create table TxOutput (
   TxId                 int                  not null,
   Idx                  int                  not null,
   Amount               bigint               not null,
   ScriptPubKeyLen      int                  not null,
   ScriptPubKey         varchar(8000)        not null,
   Address              char(58)             null,
   IsUnspendable        bit                  not null,
   IsPayToScriptHash    bit                  not null,
   IsValid              bit                  not null,
   IsSpent              bit                  not null,
   constraint PK_TXOUTPUT primary key (TxId, Idx)
)
go

/*==============================================================*/
/* Index: Relationship_3_FK                                     */
/*==============================================================*/
create index Relationship_3_FK on TxOutput (
TxId ASC
)
go

alter table Trans
   add constraint FK_TRANS_RELATIONS_BLOCK foreign key (BlkId)
      references Block (BlkId)
go

alter table TxInput
   add constraint FK_TXINPUT_RELATIONS_TRANS foreign key (TxId)
      references Trans (TxId)
go

alter table TxOutput
   add constraint FK_TXOUTPUT_RELATIONS_TRANS foreign key (TxId)
      references Trans (TxId)
go
作者:AngryFox 分类: Uncategorized April 1st, 2018 暂无评论

在以前某个项目上竟然用-w把gcc的警告给关闭了,怪不得编译代码完全没警告,多漂亮的代码!

1、未使用变量、未使用函数返回值,
未使用变量:

warning: unused variable ‘ret’ [-Wunused-variable] int ret;

–>修改:删之。

没有达到函数末尾:

warning: control reaches end of non-void function [-Wreturn-type] int open(const char* file) { if(foo==11) { return ok; } // --> 修改:此处要加上返回值 }

没有返回值:

warning: no return statement in function returning non-void [-Wreturn-type] int Open(char* file){}

–> 修改:加上返回值

2、参数一致性,参数类型检查
空字符串:

warning: zero-length gnu_printf format string [-Wformat-zero-length] sprintf(buffer, "");

类型不对:

warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘char*’ [-Wformat=] warning: format ‘%d’ expects argument of type ‘int*’, but argument 4 has type ‘short int*’ [-Wformat=] --> 修改:按printf格式来修改,unsigned long使用%lu。不要用%d来打印字符串指针。

有符号和无符号:

warning: pointer targets in passing argument 2 of ‘ll_foobar’ differ in signedness [-Wpointer-sign] expected ‘unsigned char *’ but argument is of type ‘char *’

有符号和无符号比较:

warning: comparison between signed and unsigned integer expressions [-Wsign-compare]

–> 修改:强制转换类型

参数类型:

warning: passing NULL to non-pointer argument 2 of ‘void* memset(void*, int, size_t)’ [-Wconversion-null] memset(foobar, NULL, sizeof(foobar)); (C++中,NULL是指针,不是数值,而memset要求的是数值0)

–> 修改:用0来代替,或将NULL强转为INT。

比较诡异:

warning: argument to ‘sizeof’ in ‘char* strncpy(char*, const char*, size_t)’ call is the same expression as the destination; did you mean to provide an explicit length? [-Wsizeof-pointer-memaccess] strncpy(ptr, buffer, sizeof(ptr));

用意本身是好的,用法却是错的,ptr是指针,sizeof指针只能得到4。
类似的有:

warning: argument to ‘sizeof’ in ‘void* memset(void*, int, size_t)’ call is the same expression as the destination; did you mean to dereference it? [-Wsizeof-pointer-memaccess] memset(this, 0, sizeof(this));

–> 修改:strncpy、memset第三个参数按实际给数值。

C++类构造函数初始化顺序问题:

warning: CFoobar::m_nInit will be initialized after [-Wreorder] int m_nInit; ^ warning: ‘int CFoobar::m_nTime’ [-Wreorder] int m_nTime’;

–> 修改:按声明的顺序排列。

常量字符串:

warning: deprecated conversion from string constant to ‘CHAR* {aka char*}’ [-Wwrite-strings]

–> 修改:字符串要加const。

3、打印格式化

warning: too few arguments for format warning: too many arguments for format

如本身要打印2个参数,但只有一个%。
myprintf(“hello world %d!\n”, count, getname());
–> 修改:认真检查参数。

4、类型一致性,类型转换

整数溢出:

warning: integer overflow in expression [-Woverflow] #define BIG_SIZE 800*1024*1024*1024

转换转换:

warning: narrowing conversion of ‘height’ from ‘DWORD {aka unsigned int}’ to ‘int’ inside { } is ill-formed in C++11 [-Wnarrowing] my_dst_size foo = {width, height};

my_dst_size类型为int,但width和height为DWORD
–> 修改:强制转换类型,保持一致。

warning: extended initializer lists only available with -std=c++11 or -std=gnu++11 [enabled by default] my_pos.pos = {0,0};

my_pos.pos为坐标。
–> 修改:一一赋值。

my_pos.pos.x = 0; my_pos.pos.y = 0;

5、括号、优先级

warning: suggest parentheses around arithmetic in operand of '^' [-Wparentheses] #define HELLO ((((z>>51)^(y<<32))+((y>>23)^(z<<5)))^((sum^y)+(k[p&7^e]^z)))

–> 添加括号

#define HELLO ((((z>>51)^(y<<32))+((y>>23)^(z<<5)))^((sum^y)+(k[(p&7)^e]^z)))

另一个例子:

warning: suggest parentheses around ‘&&’ within ‘||’ [-Wparentheses] return id == foo::Get() || foo::Get() != bar::Get() && bar::Go();

6、switch

warning: enumeration value ‘LL_FOO’ not handled in switch [-Wswitch]

–>枚举类型没有完全使用,如没有default语句。添加之

7、

warning: the address of ‘filename’ will always evaluate as ‘true’ [-Waddress] #define LL_STR(foo) foo ? foo : ""

其它

error: function declaration isn’t a prototype -->函数声明如无参数,要加上void

 

作者:AngryFox 分类: Uncategorized April 1st, 2018 暂无评论

主要修改内容在chainparams.cpp源码文件中。

关于比特币家族的新的币种修改方法主要有以下几点

创世块
创世块时间戳
私钥地址的开头
块分割的魔法四字节
难度、出块时间
币的总量
欲挖币数量
  • 创世块 创世块时间戳 私钥地址的开头 块分割的魔法四字节 难度、出块时间 币的总量 欲挖币数量

算法难度

算法难度是指,在哈希的过程中,哈希后的值匹配的格式命中难度。

例:0 必须匹配 0f 匹配任意字符。

因此,算法难度取决于哈希匹配格式中 0 的数量,0 越多,匹配命中率越低,即算法难度高。

在 Qtum 中使用了两种共识机制的算法,分别是 PoW 工作量证明与 PoS 权益证明

此处将两者的算法难度分离,以更好的让官方 Qtum 实施预挖。

consensus.powLimit = uint256S("0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
consensus.posLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");

创世块

创世块是指整个区块中的第一个块。


// 创建创世块函数原型
// @params: nTime 发起时间 (Unix 时间戳)
// @params: nNonce 创世块的合理随机数 该随机数得到的结果必须符合协议的难度
// @params: nBits
// @params: nVersion 版本号
// @params: genesisReward 创世块奖励
static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce,
                uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) {
    // 当时的头条事件
    const char* pszTimestamp = "Sep 02, 2017 Bitcoin breaks $5,000 in latest price frenzy";
    // 公钥的十六进制
    // 拥有创世块真正私钥的人,才是这个链的主人。
    const CScript genesisOutputScript =
                    CScript() << ParseHex("040d61d8653448c98731ee5fffd303c15e71ec"\
                    "2057b77f11ab3601979728cdaff2d68afbba14e4fa0bc44f2072b0b23ef63"\
                    "717f8cdfbe58dcd33f32b6afe98741a") << OP_CHECKSIG;
    return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
}

创世块的生成是整个主链的基础,接下来主链的所有块都是创世块的延伸。

genesis = CreateGenesisBlock(1504695029, 8026361, 0x1f00ffff, 1, 50 * COIN);
genesis.GetHash();

魔法四字节

这里的四字节用于比特币网络中二进制数据传输过程中,作为数据分隔作用。

以该四字节作为分隔标准,这里可以修改成任意约定字节内容。

pchMessageStart[0] = 0xfd;
pchMessageStart[1] = 0xdd;
pchMessageStart[2] = 0xc6;
pchMessageStart[3] = 0xe1;

默认同步主机

服务器起来时,程序会向主机拉去其它节点的信息。

由该中心节点,完成 P2P 网络基础传输地址建设。

此处的中心节点,可以向上添加。

// CDNSSeedData @param 1: 列表名称
// CDNSSeedData @param 2: 服务器地址
vSeeds.push_back(CDNSSeedData("qtum3.dynu.net", "qtum3.dynu.net", false)); // Qtum mainnet

确认数

确认数是指该笔交易第一次记录到区块中后,后面的延伸块的长度。

例如当前块高为1000,则创世块的确认数为:1000个。

节点检验

节点检验是指在同步块的过程中,将已发生的块哈希写死在代码中。

在同步过程中检验对应高度的块,若块哈希不同,直接停止块同步。

此处的节点检验,可以向上添加。

checkpointData = (CCheckpointData) {
    boost::assign::map_list_of
    ( 0, uint256S("000075aef83cf2853580f8ae8ce6f8c3096cfa21d98334d6e3f95e5582ed986c"))
    ( 5000, uint256S("00006a5338e5647872bd91de1d291365e941e14dff1939b5f16d1804d1ce61cd")) //last PoW block
};

问题分析

在修改源码参数的过程中,可能会遇到一些问题。

第5000个块过度 PoW 过度 PoS

该问题可以查看源码

StakeQtums(true, pwalletMain);
// 摘自 init.cpp (1740)
// @brief : 矿机的主要操作函数
// @param : 当前钱包
void ThreadStakeMiner(CWallet *pwallet)

PoS的运行前提

  1. 正在同步块的时候,不会执行 PoS 挖矿操作
  2. 当前无其它在线节点时,不会执行PoS挖矿操作
  3. 拥有钱包功能( ENABLE_WALLET 宏被声明)
//don't disable PoS mining for no connections if in regtest mode
if(!regtestMode && !GetBoolArg("-emergencystaking", false)) {
    while (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0 || IsInitialBlockDownload()) {
        nLastCoinStakeSearchInterval = 0;
        fTryToSync = true;
        MilliSleep(1000);
    }
    if (fTryToSync) {
        fTryToSync = false;
        if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) < 3 ||
            pindexBestHeader->GetBlockTime() < GetTime() - 10 * 60) {
            MilliSleep(60000);
            continue;
        }
    }
} // 摘自 miner.cpp - ThreadStakeMiner(1093)

ThreadStakeMiner

函数原型:void ThreadStakeMiner(CWallet *pwallet);

函数功能:该函数负责主要的 PoS 挖矿操作,涉及所有挖矿的任务流操作

由 bitcoind.cpp 的 main 函数发起运行在线程池中

  1. 检查钱包状态是否可用
  2. 检测是否满足 PoS 协议的运行前提
  3. 检验是否存在可兑换币龄

附言

若出现同步到第五千个块后,无法继续同步,在测试情况下,一般是由于在线节点数量不够的原因

 

作者:AngryFox 分类: Uncategorized March 30th, 2018 暂无评论

/*最近在看Ethereum,其中一个重要的概念是Merkle Tree,以前从来没有听说过,所以查了些资料,学习了Merkle Tree的知识,因为接触时间不长,对Merkle Tree的理解也不是很深入,如果有不对的地方,希望各位大神指正*/

Merkle Tree概念

Merkle Tree,通常也被称作Hash Tree,顾名思义,就是存储hash值的一棵树。Merkle树的叶子是数据块(例如,文件或者文件的集合)的hash值。非叶节点是其对应子节点串联字符串的hash。[1]

1. Hash

Hash是一个把任意长度的数据映射成固定长度数据的函数[2]。例如,对于数据完整性校验,最简单的方法是对整个数据做Hash运算得到固定长度的Hash值,然后把得到的Hash值公布在网上,这样用户下载到数据之后,对数据再次进行Hash运算,比较运算结果和网上公布的Hash值进行比较,如果两个Hash值相等,说明下载的数据没有损坏。可以这样做是因为输入数据的稍微改变就会引起Hash运算结果的面目全非,而且根据Hash值反推原始输入数据的特征是困难的。[3]

如果从一个稳定的服务器进行下载,采用单一Hash是可取的。但如果数据源不稳定,一旦数据损坏,就需要重新下载,这种下载的效率是很低的。

2. Hash List

在点对点网络中作数据传输的时候,会同时从多个机器上下载数据,而且很多机器可以认为是不稳定或者不可信的。为了校验数据的完整性,更好的办法是把大的文件分割成小的数据块(例如,把分割成2K为单位的数据块)。这样的好处是,如果小块数据在传输过程中损坏了,那么只要重新下载这一快数据就行了,不用重新下载整个文件。

怎么确定小的数据块没有损坏哪?只需要为每个数据块做Hash。BT下载的时候,在下载到真正数据之前,我们会先下载一个Hash列表。那么问题又来了,怎么确定这个Hash列表本事是正确的哪?答案是把每个小块数据的Hash值拼到一起,然后对这个长字符串在作一次Hash运算,这样就得到Hash列表的根Hash(Top Hash or Root Hash)。下载数据的时候,首先从可信的数据源得到正确的根Hash,就可以用它来校验Hash列表了,然后通过校验后的Hash列表校验数据块。

3. Merkle Tree

Merkle Tree可以看做Hash List的泛化(Hash List可以看作一种特殊的Merkle Tree,即树高为2的多叉Merkle Tree)。

在最底层,和哈希列表一样,我们把数据分成小的数据块,有相应地哈希和它对应。但是往上走,并不是直接去运算根哈希,而是把相邻的两个哈希合并成一个字符串,然后运算这个字符串的哈希,这样每两个哈希就结婚生子,得到了一个”子哈希“。如果最底层的哈希总数是单数,那到最后必然出现一个单身哈希,这种情况就直接对它进行哈希运算,所以也能得到它的子哈希。于是往上推,依然是一样的方式,可以得到数目更少的新一级哈希,最终必然形成一棵倒挂的树,到了树根的这个位置,这一代就剩下一个根哈希了,我们把它叫做 Merkle Root[3]

在p2p网络下载网络之前,先从可信的源获得文件的Merkle Tree树根。一旦获得了树根,就可以从其他从不可信的源获取Merkle tree。通过可信的树根来检查接受到的Merkle Tree。如果Merkle Tree是损坏的或者虚假的,就从其他源获得另一个Merkle Tree,直到获得一个与可信树根匹配的Merkle Tree。

Merkle Tree和Hash List的主要区别是,可以直接下载并立即验证Merkle Tree的一个分支。因为可以将文件切分成小的数据块,这样如果有一块数据损坏,仅仅重新下载这个数据块就行了。如果文件非常大,那么Merkle tree和Hash list都很到,但是Merkle tree可以一次下载一个分支,然后立即验证这个分支,如果分支验证通过,就可以下载数据了。而Hash list只有下载整个hash list才能验证。

Merkle Tree的特点

  1. MT是一种树,大多数是二叉树,也可以多叉树,无论是几叉树,它都具有树结构的所有特点;
  2. Merkle Tree的叶子节点的value是数据集合的单元数据或者单元数据HASH。
  3. 非叶子节点的value是根据它下面所有的叶子节点值,然后按照Hash算法计算而得出的。[4][5]

通常,加密的hash方法像SHA-2和MD5用来做hash。但如果仅仅防止数据不是蓄意的损坏或篡改,可以改用一些安全性低但效率高的校验和算法,如CRC。

Second Preimage Attack: Merkle tree的树根并不表示树的深度,这可能会导致second-preimage attack,即攻击者创建一个具有相同Merkle树根的虚假文档。一个简单的解决方法在Certificate Transparency中定义:当计算叶节点的hash时,在hash数据前加0×00。当计算内部节点是,在前面加0×01。另外一些实现限制hash tree的根,通过在hash值前面加深度前缀。因此,前缀每一步会减少,只有当到达叶子时前缀依然为正,提取的hash链才被定义为有效。

Merkle Tree的操作

1. 创建Merckle Tree

加入最底层有9个数据块。

step1:(红色线)对数据块做hash运算,Node0i = hash(Data0i), i=1,2,…,9

step2: (橙色线)相邻两个hash块串联,然后做hash运算,Node1((i+1)/2) = hash(Node0i+Node0(i+1)), i=1,3,5,7;对于i=9, Node1((i+1)/2) = hash(Node0i)

step3: (黄色线)重复step2

step4:(绿色线)重复step2

step5:(蓝色线)重复step2,生成Merkle Tree Root

易得,创建Merkle Tree是O(n)复杂度(这里指O(n)次hash运算),n是数据块的大小。得到Merkle Tree的树高是log(n)+1。

2. 检索数据块

为了更好理解,我们假设有A和B两台机器,A需要与B相同目录下有8个文件,文件分别是f1 f2 f3 ….f8。这个时候我们就可以通过Merkle Tree来进行快速比较。假设我们在文件创建的时候每个机器都构建了一个Merkle Tree。具体如下图:

从上图可得知,叶子节点node7的value = hash(f1),是f1文件的HASH;而其父亲节点node3的value = hash(v7, v8),也就是其子节点node7 node8的值得HASH。就是这样表示一个层级运算关系。root节点的value其实是所有叶子节点的value的唯一特征。

假如A上的文件5与B上的不一样。我们怎么通过两个机器的merkle treee信息找到不相同的文件? 这个比较检索过程如下:

Step1. 首先比较v0是否相同,如果不同,检索其孩子node1和node2.

Step2. v1 相同,v2不同。检索node2的孩子node5 node6;

Step3. v5不同,v6相同,检索比较node5的孩子node 11 和node 12

Step4. v11不同,v12相同。node 11为叶子节点,获取其目录信息。

Step5. 检索比较完毕。

以上过程的理论复杂度是Log(N)。过程描述图如下:

从上图可以得知真个过程可以很快的找到对应的不相同的文件。

3. 更新,插入和删除

虽然网上有很多关于Merkle Tree的资料,但大部分没有涉及Merkle Tree的更新、插入和删除操作,讨论Merkle Tree的检索和遍历的比较多。我也是非常困惑,一种树结构的操作肯定不仅包括查找,也包括更新、插入和删除的啊。后来查到stackexchange上的一个问题,才稍微有点明白,原文见[6]。

对于Merkle Tree数据块的更新操作其实是很简单的,更新完数据块,然后接着更新其到树根路径上的Hash值就可以了,这样不会改变Merkle Tree的结构。但是,插入和删除操作肯定会改变Merkle Tree的结构,如下图,一种插入操作是这样的:

插入数据块0后(考虑数据块的位置),Merkle Tree的结构是这样的:

而[6]中的同学在考虑一种插入的算法,满足下面条件:

  • re-hashing操作的次数控制在log(n)以内
  • 数据块的校验在log(n)+1以内
  • 除非原始树的n是偶数,插入数据后的树没有孤儿,并且如果有孤儿,那么孤儿是最后一个数据块
  • 数据块的顺序保持一致
  • 插入后的Merkle Tree保持平衡

然后上面的插入结果就会变成这样:

根据[6]中回答者所说,Merkle Tree的插入和删除操作其实是一个工程上的问题,不同问题会有不同的插入方法。如果要确保树是平衡的或者是树高是log(n)的,可以用任何的标准的平衡二叉树的模式,如AVL树,红黑树,伸展树,2-3树等。这些平衡二叉树的更新模式可以在O(lgn)时间内完成插入操作,并且能保证树高是O(lgn)的。那么很容易可以看出更新所有的Merkle Hash可以在O((lgn)2)时间内完成(对于每个节点如要更新从它到树根O(lgn)个节点,而为了满足树高的要求需要更新O(lgn)个节点)。如果仔细分析的话,更新所有的hash实际上可以在O(lgn)时间内完成,因为要改变的所有节点都是相关联的,即他们要不是都在从某个叶节点到树根的一条路径上,或者这种情况相近。

[6]的回答者说实际上Merkle Tree的结构(是否平衡,树高限制多少)在大多数应用中并不重要,而且保持数据块的顺序也在大多数应用中也不需要。因此,可以根据具体应用的情况,设计自己的插入和删除操作。一个通用的Merkle Tree插入删除操作是没有意义的。

Merkle Tree的应用

1. 数字签名

最初Merkle Tree目的是高效的处理Lamport one-time signatures。 每一个Lamport key只能被用来签名一个消息,但是与Merkle tree结合可以来签名多条Merkle。这种方法成为了一种高效的数字签名框架,即Merkle Signature Scheme。

2. P2P网络

在P2P网络中,Merkle Tree用来确保从其他节点接受的数据块没有损坏且没有被替换,甚至检查其他节点不会欺骗或者发布虚假的块。大家所熟悉的BT下载就是采用了P2P技术来让客户端之间进行数据传输,一来可以加快数据下载速度,二来减轻下载服务器的负担。BT即BitTorrent,是一种中心索引式的P2P文件分分析通信协议[7]

要进下载必须从中心索引服务器获取一个扩展名为torrent的索引文件(即大家所说的种子),torrent文件包含了要共享文件的信息,包括文件名,大小,文件的Hash信息和一个指向Tracker的URL[8]。Torrent文件中的Hash信息是每一块要下载的文件内容的加密摘要,这些摘要也可运行在下载的时候进行验证。大的torrent文件是Web服务器的瓶颈,而且也不能直接被包含在RSS或gossiped around(用流言传播协议进行传播)。一个相关的问题是大数据块的使用,因为为了保持torrent文件的非常小,那么数据块Hash的数量也得很小,这就意味着每个数据块相对较大。大数据块影响节点之间进行交易的效率,因为只有当大数据块全部下载下来并校验通过后,才能与其他节点进行交易。

就解决上面两个问题是用一个简单的Merkle Tree代替Hash List。设计一个层数足够多的满二叉树,叶节点是数据块的Hash,不足的叶节点用0来代替。上层的节点是其对应孩子节点串联的hash。Hash算法和普通torrent一样采用SHA1。其数据传输过程和第一节中描述的类似。

3. Trusted Computing

可信计算是可信计算组为分布式计算环境中参与节点的计算平台提供端点可信性而提出的。可信计算技术在计算平台的硬件层引入可信平台模块(Trusted Platform,TPM),实际上为计算平台提供了基于硬件的可信根(Root of trust,RoT)。从可信根出发,使用信任链传递机制,可信计算技术可对本地平台的硬件及软件实施逐层的完整性度量,并将度量结果可靠地保存再TPM的平台配置寄存器(Platform configuration register,PCR)中,此后远程计算平台可通过远程验证机制(Remote Attestation)比对本地PCR中度量结果,从而验证本地计算平台的可信性。可信计算技术让分布式应用的参与节点摆脱了对中心服务器的依赖,而直接通过用户机器上的TPM芯片来建立信任,使得创建扩展性更好、可靠性更高、可用性更强的安全分布式应用成为可能[10]。可信计算技术的核心机制是远程验证(remote attestation),分布式应用的参与结点正是通过远程验证机制来建立互信,从而保障应用的安全。

文献[10]提出了一种基于Merkle Tree的远程验证机制,其核心是完整性度量值哈希树。

首先,RAMT 在内核中维护的不再是一张完整性度量值列表(ML),而是一棵完整性度量值哈希树(integrity measurement hash tree,简称IMHT).其中,IMHT的叶子结点存储的数据对象是待验证计算平台上被度量的各种程序的完整性哈希值,而其内部结点则依据Merkle 哈希树的构建规则由子结点的连接的哈希值动态生成。

其次,为了维护IMHT 叶子结点的完整性,RAMT 需要使用TPM 中的一段存储器来保存IMHT 可信根哈希的值。

再次,RAMT 的完整性验证过程基于认证路径(authentication path)实施.认证路径是指IMHT 上从待验证叶子结点到根哈希的路径。

4. IPFS

IPFS(InterPlanetary File System)是很多NB的互联网技术的综合体,如DHT( Distributed HashTable,分布式哈希表),Git版本控制系统,Bittorrent等。它创建了一个P2P的集群,这个集群允许IPFS对象的交换。全部的IPFS对象形成了一个被称作Merkle DAG的加密认证数据结构。

IPFS对象是一个含有两个域的数据结构:

  • Data – 非结构的二进制数据,大小小于256kB
  • Links – 一个Link数据结构的数组。IPFS对象通过他们链接到其他对象

Link数据结构包含三个域:

  • Name – Link的名字
  • Hash – Link链接到对象的Hash
  • Size – Link链接到对象的累积大小,包括它的Links

通过Name和Links,IPFS的集合组成了一个Merkle DAG(有向无环图)。

对于小文件(<256kB),是一个没有Links的IPFS对象。

对于大文件,被表示为一个文件块(<256kB)的集合。只有拥有最小的Data的对象来代表这个大文件。这个对象的Links的名字都为空字符串。

目录结构:目录是没有数据的IPFS对象,它的链接指向其包含的文件和目录。

IPFS可以表示Git使用的数据结构,Git commit object。Commit Object主要的特点是他有一个或多个名为’parent0’和‘parent1’等的链接(这些链接指向前一个版本),以及一个名为object的对象(在Git中成为tree)指向引用这个commit的文件系统结构。

5. BitCoin和Ethereum[12][13]

Merkle Proof最早的应用是Bitcoin,它是由中本聪在2009年描述并创建的。Bitcoin的Blockchain利用Merkle proofs来存储每个区块的交易。

而这样做的好处,也就是中本聪描述到的“简化支付验证”(Simplified Payment Verification,SPV)的概念:一个“轻客户端”(light client)可以仅下载链的区块头即每个区块中的80byte的数据块,仅包含五个元素,而不是下载每一笔交易以及每一个区块:

  • 上一区块头的哈希值
  • 时间戳
  • 挖矿难度值
  • 工作量证明随机数(nonce)
  • 包含该区块交易的Merkle Tree的根哈希

如果客户端想要确认一个交易的状态,它只需简单的发起一个Merkle proof请求,这个请求显示出这个特定的交易在Merkle trees的一个之中,而且这个Merkle Tree的树根在主链的一个区块头中。

但是Bitcoin的轻客户端有它的局限。一个局限是,尽管它可以证明包含的交易,但是它不能进行涉及当前状态的证明(如数字资产的持有,名称注册,金融合约的状态等)。

Bitcoin如何查询你当前有多少币?一个比特币轻客户端,可以使用一种协议,它涉及查询多个节点,并相信其中至少会有一个节点会通知你,关于你的地址中任何特定的交易支出,而这可以让你实现更多的应用。但对于其他更为复杂的应用而言,这些远远是不够的。一笔交易影响的确切性质(precise nature),可以取决于此前的几笔交易,而这些交易本身则依赖于更为前面的交易,所以最终你可以验证整个链上的每一笔交易。为了解决这个问题,Ethereum的Merkle Tree的概念,会更进一步。

Ethereum的Merkle Proof

每个以太坊区块头不是包括一个Merkle树,而是为三种对象设计的三棵树:

  • 交易Transaction
  • 收据Receipts(本质上是显示每个交易影响的多块数据)
  • 状态State

这使得一个非常先进的轻客户端协议成为了可能,它允许轻客户端轻松地进行并核实以下类型的查询答案:

  • 这笔交易被包含在特定的区块中了么?
  • 告诉我这个地址在过去30天中,发出X类型事件的所有实例(例如,一个众筹合约完成了它的目标)
  • 目前我的账户余额是多少?
  • 这个账户是否存在?
  • 假如在这个合约中运行这笔交易,它的输出会是什么?

第一种是由交易树(transaction tree)来处理的;第三和第四种则是由状态树(state tree)负责处理,第二种则由收据树(receipt tree)处理。计算前四个查询任务是相当简单的。服务器简单地找到对象,获取Merkle分支,并通过分支来回复轻客户端。

第五种查询任务同样也是由状态树处理,但它的计算方式会比较复杂。这里,我们需要构建一个Merkle状态转变证明(Merkle state transition proof)。从本质上来讲,这样的证明也就是在说“如果你在根S的状态树上运行交易T,其结果状态树将是根为S’,log为L,输出为O” (“输出”作为存在于以太坊的一种概念,因为每一笔交易都是一个函数调用;它在理论上并不是必要的)。

为了推断这个证明,服务器在本地创建了一个假的区块,将状态设为 S,并在请求这笔交易时假装是一个轻客户端。也就是说,如果请求这笔交易的过程,需要客户端确定一个账户的余额,这个轻客户端(由服务器模拟的)会发出一个余额查询请求。如果需要轻客户端在特点某个合约的存储中查询特定的条目,这个轻客户端就会发出这样的请求。也就是说服务器(通过模拟一个轻客户端)正确回应所有自己的请求,但服务器也会跟踪它所有发回的数据。

然后,服务器从上述的这些请求中把数据合并并把数据以一个证明的方式发送给客户端。

然后,客户端会进行相同的步骤,但会将服务器提供的证明作为一个数据库来使用。如果客户端进行步骤的结果和服务器提供的是一样的话,客户端就接受这个证明。

MPT(Merkle Patricia Trees)

前面我们提到,最为简单的一种Merkle Tree大多数情况下都是一棵二叉树。然而,Ethereum所使用的Merkle Tree则更为复杂,我们称之为“梅克尔.帕特里夏树”(Merkle Patricia tree)。

对于验证属于list格式(本质上来讲,它就是一系列前后相连的数据块)的信息而言,二叉Merkle Tree是非常好的数据结构。对于交易树来说,它们也同样是不错的,因为一旦树已经建立,花多少时间来编辑这棵树并不重要,树一旦建立了,它就会永远存在并且不会改变。

但是,对于状态树,情况会更复杂些。以太坊中的状态树基本上包含了一个键值映射,其中的键是地址,而值包括账户的声明、余额、随机数nounce、代码以及每一个账户的存储(其中存储本身就是一颗树)。例如,摩登测试网络(the Morden testnet )的创始状态如下所示:

然而,不同于交易历史记录,状态树需要经常地进行更新:账户余额和账户的随机数nonce经常会更变,更重要的是,新的账户会频繁地插入,存储的键( key)也会经常被插入以及删除。我们需要这样的数据结构,它能在一次插入、更新、删除操作后快速计算到树根,而不需要重新计算整个树的Hash。这种数据结构同样得包括两个非常好的第二特征:

  • 树的深度是有限制的,即使考虑攻击者会故意地制造一些交易,使得这颗树尽可能地深。不然,攻击者可以通过操纵树的深度,执行拒绝服务攻击(DOS attack),使得更新变得极其缓慢。
  • 树的根只取决于数据,和其中的更新顺序无关。换个顺序进行更新,甚至重新从头计算树,并不会改变根。

MPT是最接近同时满足上面的性质的的数据结构。MPT的工作原理的最简单的解释是,值通过键来存储,键被编码到搜索树必须要经过的路径中。每个节点有16个孩子,因此路径又16进制的编码决定:例如,键‘dog’的16进制编码是6 4 6 15 6 7,所以从root开始到第六个分支,然后到第四个,再到第六个,再到第十五个,这样依次进行到达树的叶子。

在实践中,当树稀少时也会有一些额外的优化,我们会使过程更为有效,但这是基本的原则。

6. 其他应用

用到Merkle Tree的应用还有很多,比如Git,Amazon Dynamo,Apache Wave Protocol,Tahoe-LAFS backup system,Certificate Transparency framework,NoSQL systems like Apache Cassadra and Riak等

参考

[1]  https://en.wikipedia.org/wiki/Merkle_tree

[2]  https://en.wikipedia.org/wiki/Hash_function#Hash_function_algorithms

[3]  http://www.jianshu.com/p/458e5890662f

[4]  http://blog.csdn.net/xtu_xiaoxin/article/details/8148237

[5]  http://blog.csdn.net/yuanrxdu/article/details/22474697?utm_source=tuicool&utm_medium=referral

[6]  http://crypto.stackexchange.com/questions/22669/merkle-hash-tree-updates

[7]  https://en.wikipedia.org/wiki/BitTorrent

[8]  梁成仁, 李健勇, 黄道颖, 等. 基于 Merkle 树的 BT 系统 torrent 文件优化策略[J]. 计算机工程, 2008, 34(3): 85-87.

[9]  http://bittorrent.org/beps/bep_0030.html

[10] 徐梓耀, 贺也平, 邓灵莉. 一种保护隐私的高效远程验证机制[J]. Journal of Software, 2011, 22(2).

[11] http://whatdoesthequantsay.com/2015/09/13/ipfs-introduction-by-example/

[12] https://www.weusecoins.com/what-is-a-merkle-tree/

[13] http://www.8btc.com/merkling-in-ethereum

作者:AngryFox 分类: Uncategorized March 30th, 2018 暂无评论

1.若nginx用所用的 php 请求解析服务是 fpm, 则检查 /etc/php5/fpm/php.ini 文件中的参数

[/plain] view plain copy
upload_max_filesize = 20M
post_max_size = 20M
重启fpm服务

1 view plain copy
server {
    ...
    client_max_body_size 20m;
    ...
}
重启nginx服务
1

view plain copy
service nginx restart
问题解决!

Function eregi is deprecated (解决方法)

在php升级到php5.3之后后,在使用的过程经常发现有的程序会出现Function eregi() is deprecated 的报错信息。是什么原因呢?
这是因为php5.3中不再支持eregi()函数,而使用preg_match()函数替代。
解决的方法是:将eregi()函数替换成preg_match() 函数。
if(eregi(‘^test’,$file))
可以替换为
if(preg_match(‘/^test/i’,$file))

————-
PHP 5.3.0 之後的 regex, 希望使用 PCRE 的規格, POSIX Regex 都不建議使用了(統一 Regex, 避免規格太多?).
所以下述是不建議使用的 Function (POSIX), 與建議替換成的 Function (PCRE) 列表, 詳可見: PHP:
Differences from POSIX regex
* POSIX → PCRE
* ereg_replace() → preg_replace()
* ereg() → preg_match()
* eregi_replace() → preg_replace()
* eregi() → preg_match()
* split() → preg_split()
* spliti() → preg_split()
* sql_regcase() → No equivalent
* 需要 regex 的 split, 可用 preg_split() 代替
* 不需要 regex, 只要要快速分割固定的字串, 可用 explode() 代替. (速度會比需要 regex 的快很多)