EosTool - PHP版的EOS區塊鏈應用開發包
EosTool的目的是消除使用PHP開發EOS區塊鏈應用的痛苦,例如:
- 通過Nodeos和Keosd的RPC介面呼叫其功能
- 離線生成EOS格式的私鑰和公鑰
- 使用本地私鑰生成符合EOS要求的交易簽名
- 將交易物件序列化為Nodeos要求的packed_trx格式
<!--more-->
可以認為EosTool是PHP版本的eosjs,利用它可以完整地實現EOS官方客戶端Cleos的功能, 也可以很方便地在PHP應用中增加對EOS區塊鏈的支援能力,極大地提高開發效率。
原文連結:http://sc.hubwiz.com/codebag/eos-php-sdk/
EosTool執行在Php 7.1+環境下,當前版本1.0.0,主要程式碼檔案清單如下:
<table class="table table-striped"> <thead> <tr> <th>程式碼檔案</th><th>說明</th> </tr> </thead> <tbody> <tr><td>eostool/src/client/NodeClient.php</td><td>節點軟體nodeos的rpc介面封裝類</td></tr> <tr><td>eostool/src/client/WalletClient.php</td><td>錢包軟體keosd的rpc介面封裝類</td></tr> <tr><td>eostool/src/client/RpcOutput.php</td><td>RPC返回結果封裝類</td></tr> <tr><td>eostool/src/Crypto/PrivateKey.php</td><td>EOS私鑰類</td></tr> <tr><td>eostool/src/Crypto/PublicKey.php</td><td>EOS公鑰類</td></tr> <tr><td>eostool/src/Crypto/Signature.php</td><td>EOS簽名類</td></tr> <tr><td>eostool/src/Serializer/AbiType.php</td><td>EOS的ABI型別封裝類</td></tr> <tr><td>eostool/src/Serializer/AbiTypeFactory.php</td><td>ABI型別工廠類</td></tr> <tr><td>eostool/src/Serializer/SerialBuffer.php</td><td>序列化緩衝區實現類</td></tr> <tr><td>eostool/src/Serializer/Serializer.php</td><td>序列化器實現類</td></tr> <tr><td>eostool/src/Signer/Signer.php</td><td>簽名器介面</td></tr> <tr><td>eostool/src/Signer/KeosdSigner.php</td><td>Keosd簽名器實現類</td></tr> <tr><td>eostool/src/Signer/LocalSigner.php</td><td>本地離線簽名器實現介面</td></tr> <tr><td>eostool/src/Contract.php</td><td>合約類</td></tr> <tr><td>eostool/src/EosTool.php</td><td>開發包入口類</td></tr> <tr><td>eostool/tests</td><td>單元測試用例目錄</td></tr> <tr><td>eostool/phpunit.xml</td><td>單元測試配置檔案</td></tr> <tr><td>eostool/vendor</td><td>第三方依賴包</td></tr> <tr><td>eostool/composer.json</td><td>composer配置檔案</td></tr> </tbody> </table>
2. 訪問節點伺服器
使用NodeClient類訪問nodeos的rpc介面。例如,下面的程式碼訪問本機執行的 Nodeos節點的chain
外掛的get_info
介面:
use EosTool\Client\NodeClient;
$nc = new NodeClient();
$ret = $nc->chain->getInfo();
if($ret->hasError()) throw new Exception($ret->getError());
$info = $ret->getResult();
2.1 RPC呼叫分組
Nodeos採用了外掛化架構,不同的外掛的API也歸入不同的分組,EosTool採用了保持一致的 命名方法,根據api即可推斷出NodeClient的呼叫方法:API分組對應於NodeClient的一個同名 屬性,API則對應與No的Client的分組同名屬性下的一個經過camelCase
<table class="table table-striped"> <thead> <tr><th>外掛</th><th>API分組</th><th>RPC API</th><th>NodeClient方法</th></tr> </thead> <tbody> <tr><td>chain_api_plugin</td><td>chain</td><td>get_info</td><td>$nc->chain->getInfo()</td></tr> <tr><td>history_api_plugin</td><td>history</td><td>get_transaction</td><td>$nc->history->getTransaction()</td></tr> <tr><td>net_api_plugin</td><td>net</td><td>status</td><td>$nc->net->status()</td></tr> <tr><td>producer_api_plugin</td><td>producer</td><td>get_runtime_options</td><td>$nc->producer->getRunTimeOptions()</td></tr> <tr><td>dbsize_api_plugin</td><td>dbsize</td><td>get</td><td>$nc->dbsize->get()</td></tr> </tbody> </table>
RPC API的官方文件:https://developers.eos.io/eosio-nodeos/reference
2.2 RPC呼叫引數
對於Nodeos而言,有些呼叫需要傳入額外的引數,例如chain外掛的get_block介面, 使用EosTool進行呼叫時,將引數組織為一個關聯陣列即可,示例程式碼如下:
$payload = [ 'block_num_or_id' => 1 ];
$ret = $nc->chain->getBlock($payload);
2.3 RPC呼叫返回值
所有RPC呼叫的返回結果都是一個RpcOutput例項,呼叫其hasError()
方法可以 判斷是否調用出錯,進一步可以利用getError()
方法獲取錯誤資訊。
RPC呼叫的響應則可以通過getResult()
方法獲取,它是一個由原始的JSON結果 轉化出的StdClass物件,因此可以方便的提取屬性資訊,例如:
echo 'chain id' . $info->chain_id . PHP_EOL;
2.4 訪問主網/測試網節點
在建立NodeClient例項時,可以傳入額外的引數執行來制定要訪問的EOS主網或測試網節點。 例如,使用下面的程式碼訪問某個主網節點:
$nc = new NodeClient(['base_uri' => 'https://api.eosnewyork.io:443/v1/']);
或者訪問jungle測試網的某個節點:
$nc = new NodeClient(['base_uri' => 'https://jungle.eosio.cr:443/v1/']);
3、訪問錢包伺服器
新版的Keosd已經不提供RPC API文件,這可能意味著它在EOS軟體棧中已經開始滑向邊緣地位。 不過可以在這個地址訪問老版的文件:https://developers.eos.io/eosio-nodeos/v1.1.0/reference
使用WalletClient類訪問Keosd的rpc介面。例如,下面的程式碼訪問本機執行的 Keosd的list_wallets
介面:
use EosTool\Client\WalletClient;
$wc = new WalletClient();
$ret = $wc->listWallets();
if($ret->hasError()) throw new Exception($ret->getError());
$wallets = $ret->getResult();
由於Keosd的API不再分組,因此RPC對應的方法直接掛在WalletClient物件上,這是一個不同之處。 與NodeClient一樣的是,WalletClient的呼叫返回結果也是一個RpcOutput物件。
1.4版的Keosd預設使用UNIX套接字而不是HTTP提供RPC介面,這可能是考慮到絕大多數情況下 Keosd都執行在本機,使用IPC會更安全一些。因此這也是WalletClient的預設例項化選項, 在絕大多數情況下,不需要傳入額外的引數來例項化WalletClient。
4. 私鑰與公鑰
EOS的金鑰演算法類似於比特幣,但做了一些調整,定義了自己的格式。
使用PrivateKey類的靜態方法new()
生成隨機私鑰。例如:
use EosTool\Crypto\PrivateKey;
$prv = PrivateKey::new();
echo $prv->toEos() . PHP_EOL; //類似:5Hu6nxM6s6UQ3nYkr1s1GKA17zPqpceUuWxH3JBwK8ZorMSRqGi
toEos()
方法用來將私鑰物件轉換為EOS的自定義格式。
4.1 公鑰推導
從私鑰可以推匯出公鑰,例如:
$pub = $prv->getPublicKey();
echo $pub->toEos() . PHP_EOL; //類似:EOS6wQ6t3n148GfzLzgxq7cC8ARDKxeaB3hQXdXn7oZYdwEyAXiSv
同樣,使用toEos()
方法將公鑰轉換為EOS的自定義格式。
4.2 匯入EOS私鑰
可以將一個EOS格式的私鑰轉化為EosTool的PrivateKey物件,例如,下面的 程式碼將指定的EOS私鑰匯入,並顯示其對應的EOS公鑰:
$prv = PrivateKey::fromEos('5Hu6nxM6s6UQ3nYkr1s1GKA17zPqpceUuWxH3JBwK8ZorMSRqGi');
echo $prv->getPublicKey()->toEos() . PHP_EOL;
4.3 權威簽名
PrivateKey的sign()
方法支援普通簽名和EOS節點要求的權威簽名。例如下面的程式碼返回一個 普通簽名:
$hex = '1234567890abcdef...';
$signature = $prv->sign($hex);
傳入額外的引數來獲得指定資料的權威簽名:
$hex = '1234567890abcdef...';
$signature = $prv->sign($hex,true);
5. 序列化
EOS要求交易在提交節點push_transaction
之前先進行序列化,這也是在PHP中操作EOS交易 繞不過去的一個環節。
在EosTool中,使用Serializer類進行序列化操作。例如,下面的程式碼將一個EOS轉賬交易 序列化為可以提交給EOS節點旳16進位制碼流格式:
use EosTool\Serializer\Serializer;
$abi = json_decode(file_get_contents('transaction.abi'),true);
$serializer = Serializer::fromAbi($abi);
$tx = [
'expiration'=>'2018-12-04T17:00:00',
'ref_block_num' => 2878,
'ref_block_prefix' => 29012031,
'max_net_usage_words' => 0,
'max_cpu_usage_ms' => 0,
'delay_sec' => 0,
'context_free_actions' => [],
'actions' => [[
'account' => 'eosio.token',
'name' => 'transfer',
'authorization' => [[
'actor' => 'eosio',
'permission' => 'active'
]],
'data' => '1122334455667788990011223344556677.....889900'
]],
'transaction_extensions' => []
];
$hex = $serializer->serialize('transaction',$tx);
echo 'serialized tx => ' . $hex . PHP_EOL;
Serializer的靜態方法fromAbi()
用來根據一個指定的abi構造序列化器例項,然後 利用例項的serialize()
方法對指定型別的資料進行序列化操作,得到16進位制碼流。
6. 簽名
EosTool提供了兩種進行交易簽名的方法:利用Keosd進行簽名,或者使用本地私鑰進行簽名。
使用KeosdSigner類來利用錢包伺服器完成簽名。例如:
use EosTool\Signer\KeosdSigner;
$signer = new KeosdSigner();
$signatures = $signer->sign($tx,$pubKeys,$chainId);
利用__LocalSigner__類,則可以避免使用keosd,直接利用離線私鑰簽名。例如:
use EosTool\Signer\LocalSigner;
$prvKeys = ['5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3'];
$signer = new LocalSigner($prvKeys);
$signatures = $signer->sign($tx,$pubKeys,$chainId);
7. 交易提交
一個交易資料,需要經過規範化、序列化、簽名、打包一系列操作,才可以提交給 Nodeos節點廣播出去。EosTool類提供了transact()
方法來隔離這些繁瑣的操作。
例如,下面的程式碼使用NodeClient和LocalSigner建立一個EosTool例項,然後提交 一個交易:
use EosTool\Client\NodeClient;
use EosTool\Signer\LocalSigner;
use EosTool\EosTool;
$nc = new NodeClient();
$signer = new LocalSigner(['5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3']);
$tool = new EosTool($nc,$signer);
$tx = [
'actions' => [[
'account' => 'eosio.token',
'name' => 'transfer',
'authorization' => [[
'actor' => 'eosio',
'permission' => 'active'
]],
'data' => [
'from' => 'eosio',
'to' => 'tommy',
'quantity' => '200.0000 EOS',
'memo' => 'take care'
]
]]
];
$ret = $tool->transact($tx);
echo $ret->getResult()->transaction_id . PHP_EOL;
可以很方便地將簽名器改為KeosdSigner,例如:
$nc = new NodeClient();
$signer = new KeosdSigner();
$tool = new EosTool($nc,$signer);
8. 呼叫單個合約動作
使用EosTool的pushAction()
方法呼叫單個合約動作。例如,下面的程式碼呼叫tommy 賬戶託管合約的hi()
方法:
$tool = new EosTool(new NodeClient(),new KeosdSigner());
$ret = $tool->pushAction('tommy','hi',['user'=>'tommy']);
9. 部署合約
使用EosTool的setContract()
方法部署合約,例如:
$tool = new EosTool(new NodeClient(),new KeosdSigner());
$account = 'tommy';
$abi = file_get_contents('hello.abi');
$wasm = file_get_contents('hello.wasm');
$ret = $tool->setContract($account,$abi,$wasm);