>백엔드 개발 >PHP 튜토리얼 >PHP와 Ethereum 클라이언트 간의 상호 작용에 대한 자세한 설명

PHP와 Ethereum 클라이언트 간의 상호 작용에 대한 자세한 설명

php中世界最好的语言
php中世界最好的语言원래의
2018-05-16 11:52:452884검색

이번에는 PHP 및 Ethereum 클라이언트의 대화형 사용에 대한 자세한 설명을 가져왔습니다. PHP 및 Ethereum 클라이언트의 대화형 사용에 대한 주의 사항은 무엇입니까?

php는 ethereum rpc 서버와 통신합니다

1. Json RPC

Json RPC는 json 기반의 원격 프로시저 호출입니다. 쉽게 말하면 rpc 서버에서 메소드를 호출하기 위한 json 형식의 데이터를 게시하는 것입니다. 일반적으로 다음 항목이 있습니다.

{
  "method": "",
  "params": [],
  "id": idNumber
}
  • method: 메소드 이름

  • params: 매개변수 목록

  • id: 프로시저 호출의 고유 식별 번호

2. 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 = &#39;&#39; : $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(&#39;Method name has no scalar value&#39;);
    }
    
    // check
    if (is_array($params)) {
      // no keys
      $params = $params[0];
    } else {
      throw new Exception(&#39;Params must be given as array&#39;);
    }
    
    // sets notification or request task
    if ($this->notification) {
      $currentId = NULL;
    } else {
      $currentId = $this->id;
    }
    
    // prepares the request
    $request = array(
            &#39;method&#39; => $method,
            &#39;params&#39; => $params,
            &#39;id&#39; => $currentId
            );
    $request = json_encode($request);
    $this->debug && $this->debug.=&#39;***** Request *****&#39;."\n".$request."\n".&#39;***** End Of request *****&#39;."\n\n";
    // performs the HTTP POST
    $opts = array (&#39;http&#39; => array (
              &#39;method&#39; => &#39;POST&#39;,
              &#39;header&#39; => &#39;Content-type: application/json&#39;,
              &#39;content&#39; => $request
              ));
    $context = stream_context_create($opts);
    if ($fp = fopen($this->url, &#39;r&#39;, false, $context)) {
      $response = &#39;&#39;;
      while($row = fgets($fp)) {
        $response.= trim($row)."\n";
      }
      $this->debug && $this->debug.=&#39;***** Server response *****&#39;."\n".$response.&#39;***** End of server response *****&#39;."\n";
      $response = json_decode($response,true);
    } else {
      throw new Exception(&#39;Unable to connect to &#39;.$this->url);
    }
    
    // debug output
    if ($this->debug) {
      echo nl2br($debug);
    }
    
    // final checks and return
    if (!$this->notification) {
      // check
      if ($response[&#39;id&#39;] != $currentId) {
        throw new Exception(&#39;Incorrect response id (request id: &#39;.$currentId.&#39;, response id: &#39;.$response[&#39;id&#39;].&#39;)&#39;);
      }
      if (!is_null($response[&#39;error&#39;])) {
        throw new Exception(&#39;Request error: &#39;. var_export($response[&#39;error&#39;], true));
      }
      
      return $response[&#39;result&#39;];
      
    } else {
      return true;
    }
  }
}
?>

비교적 간단한 코드이므로 게으른 경우 그냥 사용하세요. packagist.org에 가서 직접 rpc 클라이언트를 찾을 수도 있습니다.

3. RPC를 호출하는 두 가지 유형의 메소드

호출해야 하는 두 가지 유형의 메소드가 있는데, 하나는 RPC 서버 고유의 메소드입니다. 그리고 다른 하나는 계약 방식입니다.

RPC 서버 메소드는 json 형식을 호출합니다

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

RPC 서버에 내장된 메소드 목록

내장 메소드를 호출하는 방법은 비교적 간단합니다. 위 링크를 참조하세요. 대부분 예제가 있습니다.

계약 메소드는 json 형식을 호출합니다.

내장 메소드에서 계약 메소드 eth_call을 호출하는 데 사용해야 합니다. 계약 메소드 이름과 계약 메소드 매개변수 목록은 params를 사용하여 반영됩니다. 계약, json 데이터는 어떻게 구성되어야 할까요?

먼저 getBalanace의 함수 구현을 살펴보세요.

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

함수 프로토타입 추출:

balanceOf(address)

geth 콘솔에서 명령을 실행하세요.

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

함수 해시 가져오기 "0x70a08231 "

조회할 주소가 address _owner = "0x38aabef4cd283ccd5091298dedc88d27c5ec5750"라고 가정하고, 앞의 "0x"를 제거하고 왼쪽에 24개의 0을 추가합니다(일반 주소 길이는 42비트, '0x' 제거 후 40비트) , 64비트 16진수 매개변수를 구성합니다.

계약 주소가 "0xaeab4084라고 가정할 때 최종 매개변수는 "0x70a082310000000000000000000000038aabef4cd283ccd5091298dedc88d27c5ec57 50"입니다. 194B2a425096fb583Fbcd67385210ac3".

얻은 최종 json 데이터는 다음과 같습니다.

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

위 json 데이터를 서버는 포스트 모드에서 계약 메소드 "balanceOf"를 호출하여 주어진 주소의 토큰 잔액을 쿼리할 수 있습니다.

계약에서 다른 메소드를 호출하는 경우에도 위의 메소드를 따라야 합니다. :

먼저 코드에서 함수 구현을 살펴보세요.

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

두 번째로 함수 프로토타입을 추출합니다.

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

다시 콘솔에서 sha3 함수를 실행합니다.

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

함수 해시 "0xa9059cbb"를 가져옵니다

The 첫 번째 매개변수는 주소를 _to = "0x38aabef4cd283ccd5091298dedc88d27c5ec5750"으로 가정하고 "0x"로 이동하여 64비트에 0을 추가합니다.

두 번째 uint256 _value = 43776이라고 가정하고 16진수 "0xab00"으로 변환하고 "0x"를 제거하고 0을 추가합니다. 64비트로.

to 계약 주소

data 위 연산으로 얻은 16진수

  • 위 단계를 코드로 변환 .

    Ethereum RPC 클라이언트 구축
  • {
      "method": "eth_call",
      "params": [{"from": "0x38aabef4cd283ccd5091298dedc88d27c5ec5750", "to": "0xaeab4084194B2a425096fb583Fbcd67385210ac3", "data": "0xa9059cbb00000000000000000000000038aabef4cd283ccd5091298dedc88d27c5ec5750000000000000000000000000000000000000000000000000000000000000ab00"}, "latest"],
      "id": 1
    }
  • 코드는 비교적 간단하지만 몇 가지 주의할 점이 있습니다.

  • transfer 함수의 값 단위는 10^-18로 매우 작습니다. 1000번 전송하려면 실제로 10의 18승을 곱해야 합니다. 여기서 18은 소수입니다.

포인트 1로 인해 pow 함수 대신 bcpow를 사용해야 합니다.

  • 不能使用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([&#39;password&#39;]));
    var_dump(EthereumRPCClient::personal_unlockAccount([EthereumRPCClient::COINBASE, "password", 3600]);
    var_dump(EthereumRPCClient::getBalance("0x...."));

    相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

    推荐阅读:

    PHP使用file_get_contents发送http请求步骤详解

    PHP实现随机剔除算法

    위 내용은 PHP와 Ethereum 클라이언트 간의 상호 작용에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    성명:
    본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.