本篇是介绍PHP如何生成管理以太坊私钥并签名交易。
之前的ETH以太坊钱包交互方式
- 一台Web服务器,一台钱包服务器运行Geth或Parity
- 一台钱包服务器存储keystore
- 需要交易时先解锁钱包再发送交易
优点
- Web服务器本身不用存储钱包,只用记住钱包密码即可
缺点
- 钱包服务器存储所有地址,并不能单独服务于某一个应用
- 钱包服务器如果挂了或者要迁移,比较麻烦。
目前要用的方式
- 一台钱包服务器供多个Web服务器使用,本身不存储keystore
- Web服务器自己生成并管理私钥,需要交易时自己签名并发送RAW交易
imToken及其它一众钱包就是类似这种方式,不同的是它们是客户端应用,但原理是一样的。
优点
- 不用管理多个钱包服务器,节省运维成本
- 方便迁移
缺点
- 私钥在Web服务器上,一旦被盗,资产全丢。所以私钥一定要妥善保管,可采取加密存储。
下面是关键的实现环节,主要是生成私钥、交易签名及广播。
首先安装依赖
# 这两个用来生成BTC/ETH/LTC等的助记词、种子、私钥和公钥
composer require bitwasp/bitcoin
composer require web3p/ethereum-util
# 这两个用来生成交易、签名并与钱包服务器交互
composer require web3p/ethereum-tx
composer require sc0vu/web3.php
生成私钥
use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Bitcoin\Crypto\Random\Random;
use BitWasp\Bitcoin\Key\Factory\HierarchicalKeyFactory;
use BitWasp\Bitcoin\Mnemonic\Bip39\Bip39Mnemonic;
use BitWasp\Bitcoin\Mnemonic\Bip39\Bip39SeedGenerator;
use BitWasp\Bitcoin\Mnemonic\MnemonicFactory;
use Web3p\EthereumUtil\Util;
// Bip39
$random = new Random();
// 生成随机数(initial entropy)
$entropy = $random->bytes(Bip39Mnemonic::MIN_ENTROPY_BYTE_LEN);
$bip39 = MnemonicFactory::bip39();
// 通过随机数生成助记词
$mnemonic = $bip39->entropyToMnemonic($entropy);
echo "mnemonic: " . $mnemonic.PHP_EOL.PHP_EOL;// 助记词
$seedGenerator = new Bip39SeedGenerator();
// 通过助记词生成种子,传入可选加密串'hello'
$seed = $seedGenerator->getSeed($mnemonic);
echo "seed: " . $seed->getHex() . PHP_EOL;
$hdFactory = new HierarchicalKeyFactory();
$master = $hdFactory->fromEntropy($seed);
$util = new Util();
// 设置路径account
$hardened = $master->derivePath("44'/60'/0'/0/0");
echo " - m/44'/60'/0'/0/0 " .PHP_EOL;
echo " public key: " . $hardened->getPublicKey()->getHex().PHP_EOL;
echo " private key: " . $hardened->getPrivateKey()->getHex().PHP_EOL;// 可以导入到imtoken使用的私钥
echo " address: " . $util->publicKeyToAddress($util->privateKeyToPublicKey($hardened->getPrivateKey()->getHex())) . PHP_EOL;// 私钥导入imtoken后一样的地址
生成好了之后把地址、公钥、私钥保存到数据库中,留待备用。
交易签名并广播
use Web3\Providers\HttpProvider;
use Web3\RequestManagers\HttpRequestManager;
use Web3\Web3;
use Web3p\EthereumTx\Transaction;
//构造交易
$transaction = new Transaction([
'nonce' => '0x1',
'from' => 'address_from',
'to' => 'address_to',
'gas' => '0x76c0',
'gasPrice' => '0x10',
'value' => '0x1000',
'chainId' => 1,
]);
//签名
$signedTransaction = $transaction->sign("private_key");
var_dump($signedTransaction);
//使用web3发送交易
$web3 = new Web3(new HttpProvider(new HttpRequestManager('http://localhost:8545', 5)));
$eth = $web3->eth;
$eth->sendRawTransaction('0x' .$signedTransaction,function ($err, $tx) {
if ($err !== null) {
var_dump($err->getMessage());
}else{
echo 'TX: '. $tx . PHP_EOL;
}
});
注意点:
- 交易参数中有两个参数需要注意下,一是 nonce,nonce必须严格递增,不能跳跃,不然会暂存到交易池中,直到nonce到达它所在的高度时才会被提交打包。可通过
eth_getTransactionCount
获取某个地址的交易量,第二个参数传pending
,即包含未确认的交易,下一笔交易nonce就用此交易量即可(因为nonce从0开始);二是 chainId,主网是1,也是默认的,如果用测试网络,须指定chainId。