7.以太坊php web3 使用裸交易

正文开始

[md]

交易类型

在以太坊中,约定了两种交易:普通交易(Transaction)和裸交易(RawTransaction)。

这两种交易的区别在于:普通交易由节点负责签名,然后发送到网络中进行确认;

而裸交易则由外部应用进行签名,节点不再额外处理,而只是负责发送到网络中进行确认 —— 这也是裸交易名称的由来 —— 未经节点加工的原始交易:

以太坊约定了两种交易不同的提交接口:普通交易使用 eth_sendTransaction 调用提交,而裸交易则应当使用 eth_sendRawTransaction 调用提交。事实上, 在公共节点中,通常会拒绝普通交易的提交,而要求外部应用必须进行离线签名。


提交普通交易

普通交易由节点负责进行签名。在 web3.php 中,提交一个普通交易,需要使用 Web3Eth类的 sendTransaction() 方法发送一个请求包,该方法对应 eth_sendTransaction 这个RPC接口。

应用首先应当准备一个请求包,其中可以包含以下内容:

from: 发送交易的源地址 to: 交易的目标地址 gas: 交易执行gas用量上限 gasPrice: gas价格 value: 交易发送的金额,单位:wei data: 额外携带的数据 nonce: 一次性序号,用来对抗重放攻击

不过只有发送方和接收方的信息是必须的,其他不需要的信息都可以不填。例如, 下面的代码(demo repo\chapter4\raw-transaction.php )从节点第1个账户向第2个账户转100wei的资金,我们只需要设置from、 to和value字段:

$txreq = [
  "from" => $accounts[0],
  "to" => $accounts[1],
  "value" => 100
];
$web3->eth->sendTransaction($txreq,$cb);
echo 'tx hash: ' . $cb->result . PHP_EOL;

向节点成功发送交易请求后,节点将返回该交易的哈希值。我们将在 下一节课程中,使用这个交易哈希值,来检查交易的执行状态,也就是 获取该交易的收据。


获取交易收据

由于以太坊的交易需要提交到网络中进行共识处理,因此我们提交的交易不会 马上生效。要查询交易是否生效,需要使用之前 eth_sendTransaction 调用返回 的交易哈希读取交易收据。

根据以太坊的约定,应用需要调用eth_getTransactionReceipt 接口来检索具有指定哈希交易的收据。

例如,下面的代码读取具有指定哈希的交易的收据:(demo: repo\chapter4\raw-transaction.php )

$web3->eth->getTransactionReceipt($txhash,$cb);
var_dump($cb->result);

交易收据是一个stdClass对象,包含以下属性:

Receipt:
stdClass Object
(
    [transactionHash] => 0x9fbe9258f1efd4bde0932e119ba71689d10a230c12966aef24b3b
f8fe7896507
    [transactionIndex] => 0x0
    [blockHash] => 0x92813154b524c65910c9ae9ad4bfa9ab35df5fe2cd2e38330009e04bd7d
57ebd
    [blockNumber] => 0x8
    [from] => 0x9c53300e5e1b9ff41d5043bcd40cb0bab33d5d74
    [to] => 0x22d491bde2303f2f43325b2108d26f1eaba1e32b
    [gasUsed] => 0x5258
    [cumulativeGasUsed] => 0x5258
    [contractAddress] =>
    [logs] => Array
        (
        )

    [status] => 0x1
    [logsBloom] => 0x00000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000
)

transactionHash: 交易哈希 transactionIndex: 交易在块内的索引序号 blockHash: 交易所在块的哈希 blockNumber: 交易所在块的编号 cumulativeGasUsed: 交易所在块消耗的gas总量 gasUsed: 本次交易消耗的gas用量 contractAddress: 对于合约创建交易,该值为新创建的合约地址,否则为null logs: 本次交易生成的日志对象数组

按照以太坊的出块速度,大约最快需要15秒交易才可能得到确认,因此我们需要 周期性地检查交易收据。例如,下面的代码每隔10秒钟检查一次交易收据, 直到超时或取得有效收据:

$timeout = 60;
$interval = 10;
$t0 = time();
while(true){
  $web3->eth->getTransactionReceipt($txhash,$cb);  
  if($cb->result) break;
  $t1 = time();
  if(($t1 - $t0) > $timeout) break;
  sleep($interval);  
}
var_dump($cb->result);

当然,在使用ganache时,由于出块速度非常快,可以提高检查频率,例如每隔1秒 检查一次即可。

gas价格与用量

在我们之前创建交易对象时,有意忽略了gas相关的参数,让节点自己决定。 因为,gas是以太坊中最令人迷惑的概念之一。

Gas:Gas对应于一个交易(Transaction)中以太坊虚拟机(EVM)的实际运算步数。越简单的交易,例如单纯的以太币转帐交易,需要的运算步数越少,Gas亦会需要 的少一点。反之,如果要计算一些复杂运算,Gas的消耗量就会大。

Gas Price:Gas Price就是你愿意为一个单位的Gas出多少Eth,一般用Gwei作单位。所以Gas Price 越高,就表示交易中每运算一步,会支付更多的Eth。

因此,以太坊的交易手续费计算公式很简单:

交易手续费(Tx Fee) = 实际运行步数(Actual Gas Used) * 单步价格(Gas Price)

例如你的交易需要以太坊执行50步完成运算,假设你设定的Gas Price是2 Gwei ,那么整个 交易的手续费 就是50 * 2 = 100 Gwei 了。

Gas Limit:Gas Limit就是一次交易中Gas的可用上限,也就是你的交易中最多会执行多少步运算。 由于交易复杂程度各有不同, 确切的Gas消耗量是在完成交易后才会知道,因此在你提交交易 之前,需要为交易设定一个Gas用量的上限。

如果说你提交的交易尚未完成,消耗的Gas就已经超过你设定的Gas Limit,那么这次交易 就会被取消,而已经消耗的手续费同样被扣取 —— 因为要奖励已经付出劳动的矿工。 而如果交易已经完成,消耗的Gas未达到Gas Limit, 那么只会按实际消耗的Gas 收取交易服务费。 换句话说,一个交易可能被收取的最高服务费就是Gas Limit * Gas Price 了。

最后值得一提的是Gas Price 越高,你提交的交易会越快被矿工接纳。 但通常人们都不愿多支付手续费, 那么究竟应该将Gas Price设置为多少,才可以在正常时间(eg 10 mins)内,确保交易被确认到区域链上 呢? 这个网站可以帮到你。


使用裸交易

与普通交易由节点负责签名不同,裸交易需要外部应用进行离线签名。 因此在使用裸交易之前,需要首先载入账户凭证 ———— 要用到凭证里保存的私钥进行签名:

可以使用钱包来实例化一个账户凭证对象,例如,下面的代码使用 密码123解密指定的钱包文件:

$credential = Credential::fromWallet("123","./keystore/...");

接下来创建裸交易对象。由于发送方将对裸交易签名,因此在裸交易对象中不需要 重复指定发送方账户。例如,下面的代码将构造一个从钱包账户向节点第2个账户转账 的裸交易对象:

//获取nonce
$nonce = getAccountNonce($web3j,$credential->getAddress());
$raw = [
  'from': $credential->getAddress(),
  'to' : accounts[1],
  'value': 100000,
  'nonce': $nonce,
  'gasPrice':  '0x9184e72a00',
  'gasLimit': '0x76c0'
];

nonce的作用是对抗重放攻击,在不同的交易中它应当是不重复的,除非你需要覆盖之前 的交易。在以太坊中应当将这个值设置为发送方账户已经发送的交易数量 —— 使用 eth_getTransactionCount 调用来获取这个值:[demo: \repo\chapter4\raw-transaction.php ]

function getAccountNonce($web3j,$account){
  $cb = new Callback();
  $web3->eth->getTransactionCount($credential->getAddress(),$cb);
  $nonce = '0x' . Web3Utils::toHex($cb->result);
  return $nonce;  
}

一旦创建了裸交易对象,就可以使用发送方账户对其进行签名,签名的结果是一个 字符串:

$req = $credential->signTransaction($raw);

一切就绪,调用eth_sendRawTransaction接口来发送裸交易请求:

$web3->eth->sendRawTransaction($req,$cb);
echo 'tx hash: ' . $cb->result . PHP_EOL;

同样,我们也需要检查裸交易的收据来判断交易是否生效。

下一篇:8.php以太坊智能合约概述

正文结束

1.以太坊php web3 在windows10下调试——ganache工具的安装 【hi 以太坊】 5.以太坊php web3:为网站增加以太币支付功能