源代码解读 [EOS 源码分析] 16 - cleos 如何在 push_transaction 函数中构造交易数据?

Admin · 2019年12月21日 · 108 次阅读
本帖已被设为精华帖!

EOS 源码分析系列文章,由技术作者松果撰写。为帮助更多开发者了解,特获授权转载至此。感谢松果兄支持。

img

上一篇文章介绍了cleos使用send_actions函数发送Action的流程,send_actions内部调用push_actions,后者把actions包装进transaction并调用push_transaction发送交易到EOS区块链或在本地打印数据,这篇文章介绍push_transaction函数内部关于构造交易数据的一些细节。

cleos如何在push_transaction函数中构造交易数据?

回到push_transaction函数,来看这部分代码:

img

通过trx.signatures.size()判断交易是否包含签名,如果交易未签名则设置trx相关数据。

info对象包含了EOS区块的基本信息,通过get_info函数获取,和使用cleos get info命令获取的数据相同:

img

1、下面这句代码用来设置交易的过期时间:

trx.expiration = info.head_block_time + tx_expiration;

上面的示例中,head_block_time的值为2019-08-22T03:51:32.500,它是fc::time_point类型,可以转换成相应的时间戳。

tx_expiration是一个默认30秒的时间:

auto tx_expiration = fc::seconds(30);

可见trx.expiration设置了具体的交易过期时间,即当前头部区块时间加上30秒。这个头部区块指的是最新生产出来的区块,不是最近的不可逆区块。

2、设置引用区块(reference block)

如果查看EOS链上每笔交易的RAW数据,会发现都带有ref_block_numref_block_prefix这两个字段:

img

这种机制在EOS技术白皮书中被称为TAPOS(Transaction as Proof of Stake),ref_block_num和ref_block_prefix的值都是通过ref_block_id计算出来的(分别取ref_block_id哈希值的一部分计算得出,下文会详细介绍)。

TAPOS机制和expiration结合,确保一笔交易在所引用的区块之后,并在交易过期之前执行。

设置引用区块的代码如下:

block_id_type ref_block_id = info.last_irreversible_block_id;

try {
  fc::variant ref_block;
  if (!tx_ref_block_num_or_id.empty()) {
    ref_block = call(get_block_func, fc::mutable_variant_object("block_num_or_id", tx_ref_block_num_or_id));
    ref_block_id = ref_block["id"].as<block_id_type>();
  }
} EOS_RETHROW_EXCEPTIONS(invalid_ref_block_exception, "Invalid reference block num or id: ${block_num_or_id}", ("block_num_or_id", tx_ref_block_num_or_id));
trx.set_reference_block(ref_block_id);

首先设置引用的区块ID,默认值是上一个不可逆区块的ID,block_id_type是fc::sha256类型;

然后根据tx_ref_block_num_or_id的值判断是否有用户设置的引用区块ID,tx_ref_block_num_or_id可以通过--ref-block选项指定;

如果指定了--ref-block,则访问get_block_func(v1/chain/get_block)获取区块信息,并重新设置引用的区块ID,从HTTP RPC获取区块ID主要是为了验证ID的真实性;

最后调用trx.set_reference_block为交易设置引用区块,set_reference_block的定义如下:

img

可以看到set_reference_block通过reference_block_id的哈希值的一部分分别计算出了ref_block_num和ref_block_prefix,即TAPOS的实现原理。

3、如果交易被设置为唯一的(设置了--force-unique选项,值保存在变量tx_force_unique中),就使用generate_nonce_action函数生成一个随机数Action,添加到trx.context_free_actions列表中,context_free_actions保存不需要签名的action。

generate_nonce_action定义如下:

chain::action generate_nonce_action() {
  return chain::action( {}, config::null_account_name, "nonce", fc::raw::pack(fc::time_point::now().time_since_epoch().count()));
}

该函数创建一个名为nonce的action,账户(account)是config::null_account_name,数据(data)是当前的时间戳,每次获取的数据都不相同,用这种方式确保了交易的唯一性。

config::null_account_name定义如下:

const static uint64_t null_account_name      = N(eosio.null);

系统账户eosio.null是一个不需要权限的账户。

4、设置交易的其他选项值:

//交易的最大CPU使用量,单位为毫秒,通过--max-cpu-usage-ms选项设置
trx.max_cpu_usage_ms = tx_max_cpu_usage;
//交易的最大NET使用量,单位为字节,通过--max-net-usage选项设置
//这里计算的是word值,一个word等于8个字节
trx.max_net_usage_words = (tx_max_net_usage + 7)/8;
//延期交易的延期时间,单位为秒,通过--delay-sec选项设置
trx.delay_sec = delaysec;

下一篇文章介绍调用sign_transaction对交易签名的相关细节。

更多内容

币圈信息站开发目录

EOS开发系列目录

共收到 0 条回复
Admin 将本帖设为了精华贴 12月21日 16:19
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册