国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - PHP教程 - 詳解php與ethereum客戶端交互

詳解php與ethereum客戶端交互

2019-10-10 11:23church PHP教程

本篇文章給大家講述了php與ethereum客戶端交互的相關知識點,對此有需要的朋友可以跟著學習下。

php與ethereum rpc server通信

一、Json RPC

Json RPC就是基于json的遠程過程調用,這么解釋比較抽象。簡單來說,就是post一個json格式的數據調用rpc server中的方法. 而這個json格式是固定的, 總的來說有這么幾項:

{
  "method": "",
  "params": [],
  "id": idNumber
}
  • method: 方法名
  • params: 參數列表
  • id: 對過程調用的唯一標識號

二、構建一個Json RPC客戶端

<?php

class jsonRPCClient {
  
  /**
   * Debug state
   *
   * @var boolean
   */
  private $debug;
  
  /**
   * The server URL
   *
   * @var string
   */
  private $url;
  /**
   * The request id
   *
   * @var integer
   */
  private $id;
  /**
   * If true, notifications are performed instead of requests
   *
   * @var boolean
   */
  private $notification = false;
  
  /**
   * Takes the connection parameters
   *
   * @param string $url
   * @param boolean $debug
   */
  public function __construct($url,$debug = false) {
    // server URL
    $this->url = $url;
    // proxy
    empty($proxy) ? $this->proxy = '' : $this->proxy = $proxy;
    // debug state
    empty($debug) ? $this->debug = false : $this->debug = true;
    // message id
    $this->id = 1;
  }
  
  /**
   * Sets the notification state of the object. In this state, notifications are performed, instead of requests.
   *
   * @param boolean $notification
   */
  public function setRPCNotification($notification) {
    empty($notification) ?
              $this->notification = false
              :
              $this->notification = true;
  }
  
  /**
   * Performs a jsonRCP request and gets the results as an array
   *
   * @param string $method
   * @param array $params
   * @return array
   */
  public function __call($method,$params) {
    
    // check
    if (!is_scalar($method)) {
      throw new Exception('Method name has no scalar value');
    }
    
    // check
    if (is_array($params)) {
      // no keys
      $params = $params[0];
    } else {
      throw new Exception('Params must be given as array');
    }
    
    // sets notification or request task
    if ($this->notification) {
      $currentId = NULL;
    } else {
      $currentId = $this->id;
    }
    
    // prepares the request
    $request = array(
            'method' => $method,
            'params' => $params,
            'id' => $currentId
            );
    $request = json_encode($request);
    $this->debug && $this->debug.='***** Request *****'."\n".$request."\n".'***** End Of request *****'."\n\n";

    // performs the HTTP POST
    $opts = array ('http' => array (
              'method' => 'POST',
              'header' => 'Content-type: application/json',
              'content' => $request
              ));
    $context = stream_context_create($opts);
    if ($fp = fopen($this->url, 'r', false, $context)) {
      $response = '';
      while($row = fgets($fp)) {
        $response.= trim($row)."\n";
      }
      $this->debug && $this->debug.='***** Server response *****'."\n".$response.'***** End of server response *****'."\n";
      $response = json_decode($response,true);
    } else {
      throw new Exception('Unable to connect to '.$this->url);
    }
    
    // debug output
    if ($this->debug) {
      echo nl2br($debug);
    }
    
    // final checks and return
    if (!$this->notification) {
      // check
      if ($response['id'] != $currentId) {
        throw new Exception('Incorrect response id (request id: '.$currentId.', response id: '.$response['id'].')');
      }
      if (!is_null($response['error'])) {
        throw new Exception('Request error: '. var_export($response['error'], true));
      }
      
      return $response['result'];
      
    } else {
      return true;
    }
  }
}
?>

比較簡單的代碼,如果比較懶,拿過去用就行了。也可以上packagist.org自己找一個rpc client.

三、調用RPC的兩類方法

有兩類方法需要調用. 一類是RPC server自帶方法,另一類就是合約方法.

RPC server方法調用json格式

{
  "method": "eth_accounts",
  "params": [],
  "id": 1
}

RPC Server自帶方法的列表

調用自帶方法比較簡單,參考上述鏈接,大部分都有示例.

合約方法調用json格式

調用合約方法必須使用自帶方法中的eth_call. 而合約方法名稱和合約方法參數列表則使用params進行體現, 比如: 我們要調用合約中的balanceOf方法, 則json數據應該如何構造呢?

首先看看getBalanace的函數實現:

function balanceOf(address _owner) public view returns (uint256 balance)

提煉出函數原型:

balanceOf(address)

在geth控制臺下運行命令:

web3.sha3("balanceOf(address)").substring(0, 10)

得到函數hash "0x70a08231"

假設待查詢的地址 address _owner = "0x38aabef4cd283ccd5091298dedc88d27c5ec5750", 則去掉前面的"0x", 并在左邊補24個零(一般地址長度為42位, 去掉'0x'后為40位),構成64位十六進制參數.

最終得到的參數為 "0x70a0823100000000000000000000000038aabef4cd283ccd5091298dedc88d27c5ec5750"

假設我們的合約地址為 "0xaeab4084194B2a425096fb583Fbcd67385210ac3".

則得到最終的json數據為:

{
  "method": "eth_call",
  "params": [{"from": "0x38aabef4cd283ccd5091298dedc88d27c5ec5750", "to": "0xaeab4084194B2a425096fb583Fbcd67385210ac3", "data": "0x70a0823100000000000000000000000038aabef4cd283ccd5091298dedc88d27c5ec5750"}, "latest"],
  "id": 1
}

把以上json數據以post方式發送給服務器,就可以調用合約方法"balanceOf", 查詢給定的地址中的代幣余額.

調用合約中的其他方法也要新遵循上面的方式, 我們再分析一下transfer方法, 加深印象:

首先, 看看代碼中的函數實現:

function transfer(address _to, uint256 _value) public returns (bool)

其次, 提煉出函數原型:

transfer(address,uint256) //注意逗號后面不能有空格

再次, 在控制臺運行sha3函數:

web3.sha3("transfer(address,uint256)").substring(0, 10)

得到函數hash "0xa9059cbb"

第一個參數假設 address _to = "0x38aabef4cd283ccd5091298dedc88d27c5ec5750", 則去"0x", 補零到64位.

第二個參數假設 uint256 _value = 43776, 則化為十六進制"0xab00"后, 去"0x", 補零到64位.

連接起來

"0xa9059cbb00000000000000000000000038aabef4cd283ccd5091298dedc88d27c5ec5750000000000000000000000000000000000000000000000000000000000000ab00"

構建json數據:

{
  "method": "eth_call",
  "params": [{"from": "0x38aabef4cd283ccd5091298dedc88d27c5ec5750", "to": "0xaeab4084194B2a425096fb583Fbcd67385210ac3", "data": "0xa9059cbb00000000000000000000000038aabef4cd283ccd5091298dedc88d27c5ec5750000000000000000000000000000000000000000000000000000000000000ab00"}, "latest"],
  "id": 1
}
  • from 轉出者地址
  • to 合約地址
  • data 上述操作得到的十六進制數

把以上的步驟轉化為代碼.

構建一個以太坊RPC client

<?php 

require './jsonRPCClient.php';

//php自帶的dechex無法把大整型轉換為十六進制
function bc_dechex($decimal)
{
  $result = [];

  while ($decimal != 0) {
    $mod = $decimal % 16;
    $decimal = floor($decimal / 16);
    array_push($result, dechex($mod));    
  }

  return join(array_reverse($result));
}

class EthereumRPCClient
{
  public static $client = null;
  
  //布署合約的賬戶地址
  const COINBASE = '0x38aabef4cd283ccd5091298dedc88d27c5ec5750';
  
  //合約地址
  const CONTRACT = '0xaeab4084194B2a425096fb583Fbcd67385210ac3';

  public static function __callStatic($method, $params)
  {
    $params = count($params) < 1 ? [] : $params[0];

    try {
      if (is_null(self::$client)) {
        self::$client = new jsonRPCClient('http://127.0.0.1:8545', true);  
      }
    } catch (\Exception $e) {
      echo $e->getMessage();
    }

    return call_user_func([self::$client, $method], $params);

  }

  public static function getBalance($address)
  {
    $method_hash = '0x70a08231';
    $method_param1_hex = str_pad(substr($address, 2), 64, '0', STR_PAD_LEFT);
    $data = $method_hash . $method_param1_hex;

    $params = ['from' => $address, 'to' => self::CONTRACT, 'data' => $data];

    $total_balance = self::eth_call([$params, "latest"]);

    return hexdec($total_balance) / (pow(10, 18));
  }

  public static function transfer($to, $value)
  {
    self::personal_unlockAccount([self::COINBASE, "123456", 3600]);

    $value = bcpow(10, 18) * $value;

    $method_hash = '0xa9059cbb';
    $method_param1_hex =str_pad(substr($to, 2), 64, '0', STR_PAD_LEFT);  
    $method_param2_hex = str_pad(strval(bc_dechex($value)), 64, '0', STR_PAD_LEFT);

    $data = $method_hash . $method_param1_hex . $method_param2_hex;
    $params = ['from' => self::COINBASE, 'to' => self::CONTRACT, 'data' => $data];

    return self::eth_sendTransaction([$params]);

  }

}

代碼比較簡單, 要注意幾點:

  • transfer函數的value單位很小, 是 10 ^ -18, 所以如果你想轉1000個,其實是要乘于 10的18次方, 這里的18是decimals.
  • 由于第1點, 應該使用bcpow代替pow函數.
  • 不能使用php自帶的dechex函數. 因為dechex要求整型不能大于 PHP_INT_MAX, 而這個數在32位機上為4294967295。由于第1 點, 所有的數都要乘于10的18次方, 所以得到的數要遠遠大于PHP_INT_MAX. 建議自己實現10進制轉16進制,如果你不知道如何實現,參考上述代碼。
  • 在運行某些合約方法, 比如transfer時, 要先unlock用戶.
  • 發送交易之后, 一定要在服務器端啟動挖礦, 這樣交易才會真的寫入到區塊, 比如你調用transfer之后,卻發現對方沒有到賬,先別吃驚,啟動挖礦試試。如果想啟用自動挖碼, 在geth --rpc ...最后加上 --mine.

測試:

<?php 
var_dump(EthereumRPCClient::personal_newAccount(['password']));
var_dump(EthereumRPCClient::personal_unlockAccount([EthereumRPCClient::COINBASE, "password", 3600]);
var_dump(EthereumRPCClient::getBalance("0x...."));

 

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 日韩欧美a级v片免费播放 | 1000部精品久久久久久久久 | 欧美日韩精品久久久免费观看 | 91av免费在线观看 | 亚洲国产精品一区二区三区 | 国产精品毛片在线 | 黄色片免费在线看 | 日韩精品毛片免费看 | 91午夜精品 | 日日久| 天天草视频 | 日韩av电影在线观看 | 综合久久精品 | 国产精品久久久久久吹潮 | 日韩小视频 | 亚洲男人天堂 | 亚洲视频成人 | 高清视频一区 | 日韩成人在线电影 | 中文字幕天天操 | 免费看男女www网站入口在线 | 国产一区www | 欧美精品久久一区 | 成人一区二区在线观看 | 日韩综合网 | 狠狠色噜噜 | 中文字幕免费观看 | 国产伦精品一区二区三区四区视频 | 性做久久久久久久免费看 | 国产精品久久久久久久9999 | 永久免费av片在线观看全网站 | 久久久久国产精品 | 电影一级毛片 | 一区二区国产精品 | 亚洲艹 | 亚州国产| 亚洲欧美一区二区三区在线 | 午夜免费福利视频 | 国产精品久久久久久久一区探花 | 国产成人精品一区二区三区网站观看 | 中文字幕日韩欧美 |