您的位置:首页 > 编程语言 > PHP开发

比特币开发原理浅析

2017-07-05 00:00 253 查看
随着勒索病毒的爆发,比特币更是近一步的被人们接触,机缘巧合之下,从大四开始接触比特币钱包的后台开发,我只做简单的介绍。

比特币终端是一个跨平台的软件,运行在windows ,linux ,移动设备上,本文以Windows平台为例。

服务器:windows 2012 server . Bitcoin Core . 由于同步钱包需要很多时间,并且现在同步容量应该在200G内(我最近一次是160g).

先上图



你的钱包只是在官网上下载的一个客户端,每个钱包可以生成N个钱包地址,从别的钱包转账可以转到这N个地址中的一个,你最先收到钱包转账的通知,也就是确认数为1,但是这个时候区块链还没有发出确认通知,当确认数>=2的时候可以认为转账成功了。

钱包服务器配置:

// 文件在家目录会有一个bitcoin文件夹 ,新建一个bitcoin.conf
// 下面4个注释的是最重要的。
listen=1
server=1
daemon=1
rpcuser=user # rpc 通讯用户的账号
rpcpassword=password # rpc 密码
rpcallowip=127.0.0.1 # rpc 允许接受哪一个ip的消息
rpcport=8333 # rpc 端口  
rpcallowip=122.114.20.103
addnode=122.114.20.103


下面是一个简单的和钱包服务器进行rpc通讯的类

<?php

namespace App\Coin\Client;

class CoinClient
{
// 钱包服务器ip
private $url;
// 连接超时时间
private $timeout;
// 钱包服务器 用户名
private $username;
// 钱包服务器密码
private $password;

public $is_batch = false;
public $batch = array();
public $debug = false;
public $jsonformat = false;
public $res = '';
private $headers = array('User-Agent: github.com Rpc', 'Content-Type: application/json', 'Accept: application/json', 'Connection: close');
public $ssl_verify_peer = true;

public function __construct($username, $password, $ip, $port, $timeout = 3, $headers = array(), $jsonformat = false)
{
$this->url = 'http://' . $ip . ':' . $port;
$this->username = $username;
$this->password = $password;
$this->timeout = $timeout;
$this->headers = array_merge($this->headers, $headers);
$this->jsonformat = $jsonformat;
}

/**
* 这里通过魔术方法调用api
* 比如 $client->getinfo() ; 可以直接获取到getinfo方法,
* @param       $method
* @param array $params
* @return string
*/
public function __call($method, array $params)
{
if ((count($params) === 1) && is_array($params[0])) {
$params = $params[0];
}
$res = $this->execute($method, $params);
$this->debug(array('method' => $method, 'params' => $params, 'res' => $res));
return $res ? $res : $this->res;
}

/**
* 构造参数
* @param       $procedure
* @param array $params
* @return string
*/
public function execute($procedure, array $params = array())
{
return $this->doRequest($this->prepareRequest($procedure, $params));
}

/**
* 构造 发送的内容
* @param       $procedure
* @param array $params
* @return array
*/
public function prepareRequest($procedure, array $params = array())
{
$payload = array('jsonrpc' => '2.0', 'method' => $procedure, 'id' => mt_rand());

if (!empty($params)) {
$payload['params'] = $params;
}

return $payload;
}

/**
* 执行 rpc 请求
* @param array $payload
* @return string
* @throws \Exception
*/
private function doRequest(array $payload)
{
$stream = @fopen(trim($this->url), 'r', false, $this->getContext($payload));
if (!is_resource($stream)) {
throw new \Exception('钱包链接失败') ;
}
$metadata = stream_get_meta_data($stream);
// 服务器响应内容 .
$response = json_decode(stream_get_contents($stream), true);
$header_1 = $metadata['wrapper_data'][0];
preg_match('/[\\d]{3}/i', $header_1, $code);
$code = trim($code[0]);
// 返回的状态值 .
if ($code == '200') {
// 这段就是服务器返回的信息 .
return isset($response['result']) ? $response['result'] : 'nodata';
}
else {
if ($response['error'] && is_array($response['error'])) {
$detail = 'code=' . $response['error']['code'] . ',message=' . $response['error']['message'];
throw new \Exception($detail) ;
}
else {
throw new \Exception($code) ;
}
}
}

/**
* 获取上下文
* @param array $payload
* @return resource
*/
private function getContext(array $payload)
{
$headers = $this->headers;
// 用户名密码 认证过程
if (!empty($this->username) && !empty($this->password)) {
$headers[] = 'Authorization: Basic ' . base64_encode($this->username . ':' . $this->password);
}
// 请求内容 
$context = stream_context_create(array(
'http' => array('method' => 'POST',
'protocol_version' => 1.1000000000000001,
'timeout' => $this->timeout,
'max_redirects' => 2,
'header' => implode("\r\n", $headers),
'content' => json_encode($payload),
'ignore_errors' => true
),
// ssl 证书 
'ssl'  => array('verify_peer' => $this->ssl_verify_peer, 'verify_peer_name' => $this->ssl_verify_peer)
));
return $context ;
}
}


下面简述开发过程中经常用到的方法:

查询钱包状态

生成钱包地址

对外转账

收取转账信息

<?php

class HandleClient{

// 客户端
private $client ;

public function __construct()
{
$host = "127.0.0.1" ; // 钱包地址,本地开发通常是本机 本地开发可以用轻量级的山寨币钱包,
$user = 'user';
$password = 'password';
$port = '6300';

$this->client = new Client($host ,$user ,$password ,$port ,30);
}

/**
* 给站内用户生成一个 地址 ,表结构大致如下 
* --------------------------------
* id   |     address |     username
* ---------------------------------
* @param $label 用户->address 一对多的关系
* @return bool|mixed
*/
public function createAddress($label)
{
// 这步骤是获取这个标签的钱包地址,如果已经存在则返回,
$address = $this->client->getaddressesbyaccount($label);
if(!is_array($address)){
$address = $this->client->getnewaddress($label);
if(!$address){
return false ;
}
}else{
$address = $address [0];
}
return $address ;
}

/**
* 将币转出,平台会有转出币钱包场景,在这之前需要判断是否是站内互转
* 在这之前需要判断是否是一个有效的钱包地址
* 该步骤通常以后台任务队列运行
*/
public function turnout($address,$number)
{
// 判断是否是一个有效的钱包地址
$isAddr = $this->client->validateaddress($address);
if(!$isAddr){
return false;
}
// 获取钱包的余额
$info = $this->client->getinfo();
if($info['balance'] < $number ){
return false ;
}
// 发送接口
return $this->client->sendtoaddress($address, $number );
}

/**
* 交易查询接口.
*/
public function trade()
{
// 该方法 有3个参数,第一个是指查询标签的意思* 查找所有用户 123456 ,查找123456的交易情况
$list =  $this->client->listtransactions("*",100,0);
foreach($list as $key => $val){
if($val ['category'] == 'receive'){
// 接受的交易
$this->handleReceive($val);
}elseif($val ['category'] == 'send'){
// 发送的交易
$this->handleSend($val);
}
}
}

// 这里是系统用户向平台充币的处理,
// 由于充币事先是不知道的,这里需要动态生成订单
// txid 是交易的唯一编号 注意启动事务
private function handleReceive($item)
{
$address = DB::table('address')->where(['address' => $item['address']])->get();
if(!$address){
return ;
}
$row = DB::table('trade')->where(['txid' => $item['txid'] , 'status' => 0 , 'type' => 'receive'])->get();
if($row){
// 如果有订单,判断确认次数
if($item['confirmations'] >= 2){
$row->save(['status' => 1 , 'confirmations' => $item['confirmations']]);
// 给用户加钱
$user = $address->user()->increment('coin', $item['amount']);
}
}else{
$arr = [
'confirmations' => $item['confirmations'],
'user_id' => $address->user->id ,
'amount' => $item['amount'],
'txid' => $item['txid'],
];
if($item['confirmations'] >= 2){
$arr ['status'] = 1 ;
$user = $address->user()->increment('coin', $item['amount']);
}else{
$arr ['status'] = 0;
}
DB::table('trade')->create($arr);
}
}

// 处理转出情况,转出是用户发起的,不需要动态插表
private function handleSend($item)
{
$row = DB::where(['address' => $item['address']])->get();
if(!$row){
return ;
}
if($item['confirmations'] >= 2){
$row->save(['status' => 1,'txid' => $item['txid'] , 'confirmations' => $item['confirmations']]);
// 可能需要处理用户币的冻结情况,
}
}

}


由于接触虚拟币开发比较多,因此对这个略有了解,虚拟币开发我几乎没找到什么文档,希望给广大的爱好者来一发!

rpc接口列表#

addmultisigaddress <nrequired> <'["key","key"]'> [account]
addnode <node> <add|remove|onetry>
backupwallet <destination>
createmultisig <nrequired> <'["key","key"]'>
createrawtransaction [{"txid":txid,"vout":n},...] {address:amount,...}
decoderawtransaction <hex string>
dumpprivkey <chinacoinaddress>
encryptwallet <passphrase>
getaccount <chinacoinaddress>
getaccountaddress <account>
getaddednodeinfo <dns> [node]
getaddressesbyaccount <account>
getbalance [account] [minconf=1]
getbestblockhash
getblock <hash> [verbose=true]
getblockcount
getblockhash <index>
getblocktemplate [params]
getconnectioncount
getdifficulty
getgenerate
gethashespersec
getinfo
getmininginfo
getnetworkhashps [blocks] [height]
getnewaddress [account]
getpeerinfo
getrawmempool
getrawtransaction <txid> [verbose=0]
getreceivedbyaccount <account> [minconf=1]
getreceivedbyaddress <chinacoinaddress> [minconf=1]
gettransaction <txid>
gettxout <txid> <n> [includemempool=true]
gettxoutsetinfo
getwork [data]
getworkex [data, coinbase]
help [command]
importprivkey <chinacoinprivkey> [label] [rescan=true]
keypoolrefill
listaccounts [minconf=1]
listaddressgroupings
listlockunspent
listreceivedbyaccount [minconf=1] [includeempty=false]
listreceivedbyaddress [minconf=1] [includeempty=false]
listsinceblock [blockhash] [target-confirmations]
listtransactions [account] [count=10] [from=0]
listunspent [minconf=1] [maxconf=9999999] ["address",...]
lockunspent unlock? [array-of-Objects]
move <fromaccount> <toaccount> <amount> [minconf=1] [comment]
sendfrom <fromaccount> <tochinacoinaddress> <amount> [minconf=1] [comment] [comment-to]
sendmany <fromaccount> {address:amount,...} [minconf=1] [comment]
sendrawtransaction <hex string>
sendtoaddress <chinacoinaddress> <amount> [comment] [comment-to]
setaccount <chinacoinaddress> <account>
setgenerate <generate> [genproclimit]
setmininput <amount>
settxfee <amount>
signmessage <chinacoinaddress> <message>
signrawtransaction <hex string> [{"txid":txid,"vout":n,"scriptPubKey":hex,"redeemScript":hex},...] [<privatekey1>,...] [sighashtype="ALL"]
stop
submitblock <hex data> [optional-params-obj]
validateaddress <chinacoinaddress>
verifychain [check level] [num blocks]
verifymessage <chinacoinaddress> <signature> <message>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  比特币 php json-rpc