寂寞如花落,窗前更无声,故园旧影迹难寻,倚看数点残红,已是梦中人。
今天,来讲讲eos中一个有关于约定的故事。
有两个问题:
1,transaction中的delay字段的含义是什么?
2,抵押交易的撤回,3天之后怎么就自动到账了?
其实,以上两个问题,本质上是一个问题,下面是系统合约中创建延时交易的部分:
eosio::transaction out;
out.actions.emplace_back( permission_level{ from, N(active) }, _self, N(refund), from );
out.delay_sec = refund_delay;
cancel_deferred( from ); // TODO: Remove this line when replacing deferred trxs is fixed
out.send( from, from, true );
可以看到:out.delay_sec = refund_delay; refund_delay的值为三天。三天之后,交易自动进行。
那么这个交易是在合约中创建的,要不要签名什么的呢?
答案是,在合约中创建的交易不需要签名,但会验证权限:
.........
bool check_auth = false;
for( const auto& act : trx.actions ) {
if( act.account != receiver ) {
check_auth = true;
break;
}
}
if( check_auth ) {
control.get_authorization_manager()
.check_authorization( trx.actions,
{},
{{receiver, config::eosio_code_name}},
delay,
std::bind(&transaction_context::checktime, &this->trx_context),
false
);
}
.........
上面的代码的意思是:
1,如果是延时交易的执行对象是自己,那么就不用验证权限了。
2,如果延时交易的的执行对象不是自己,那么就会提供自己的code权限。
3,上文中的"自己"是指的当前合约。
这里做一个延伸,“合约”与“账户”的区别。合约是一段代码,账户是eos上的身份。合约不可以单独存在与eos中,合约是一个账户对外提供的功能。多个账户可以部署同一份合约代码。
然后,将交易放入数据库里面:
auto& d = control.db();
if ( auto ptr = d.find(boost::make_tuple(receiver, sender_id)) ) {
EOS_ASSERT( replace_existing, deferred_tx_duplicate, "deferred transaction with the same sender_id and payer already exists" );
// TODO: Remove the following subjective check when the deferred trx replacement RAM bug has been fixed with a hard fork.
EOS_ASSERT( !control.is_producing_block(), subjective_block_production_exception,
"Replacing a deferred transaction is temporarily disabled." );
// TODO: The logic of the next line needs to be incorporated into the next hard fork.
// trx_context.add_ram_usage( ptr->payer, -(config::billable_size_v + ptr->packed_trx.size()) );
db.modify( *ptr, [&]( auto& gtx ) {
gtx.sender = receiver;
gtx.sender_id = sender_id;
gtx.payer = payer;
gtx.published = control.pending_block_time();
gtx.delay_until = gtx.published + delay;
gtx.expiration = gtx.delay_until + fc::seconds(control.get_global_properties().configuration.deferred_trx_expiration_window);
trx_size = gtx.set( trx );
});
} else {
d.create( [&]( auto& gtx ) {
gtx.trx_id = trx.id();
gtx.sender = receiver;
gtx.sender_id = sender_id;
gtx.payer = payer;
gtx.published = control.pending_block_time();
gtx.delay_until = gtx.published + delay;
gtx.expiration = gtx.delay_until + fc::seconds(control.get_global_properties().configuration.deferred_trx_expiration_window);
trx_size = gtx.set( trx );
});
}
在producer_plugin.cpp中,生产完区块后
如果是生产节点,直接在producer_plugin.cpp中:
。。。。
//得到所有的延迟交易
auto scheduled_trxs = chain.get_scheduled_transactions();
。。。。
//提交延迟交易
auto trace = chain.push_scheduled_transaction(trx, deadline);
。。。。。
// 删除延时交易在内存里记录
remove_scheduled_transaction(gto);
。。。。
// 执行transaction
try {
trx_context.init_for_deferred_trx( gtrx.published );
trx_context.exec();
trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful
auto restore = make_block_restore_point();
// 回执
trace->receipt = push_receipt( gtrx.trx_id,
transaction_receipt::executed,
trx_context.billed_cpu_time_us,
trace->net_usage );
。。。。。。
区块中的交易是以receipt形式存在的
const transaction_receipt& push_receipt( const T& trx, transaction_receipt_header::status_enum status,
uint64_t cpu_usage_us, uint64_t net_usage ) {
uint64_t net_usage_words = net_usage / 8;
EOS_ASSERT( net_usage_words*8 == net_usage, transaction_exception, "net_usage is not divisible by 8" );
pending->_pending_block_state->block->transactions.emplace_back( trx );
transaction_receipt& r = pending->_pending_block_state->block->transactions.back();
r.cpu_usage_us = cpu_usage_us;
r.net_usage_words = net_usage_words;
r.status = status;
return r;
}
见证节点在接受一个区块的时候,apply_block的时候将会根据不同的回执执行不同的交易。
void apply_block( const signed_block_ptr& b, controller::block_status s ) { try {
try {
EOS_ASSERT( b->block_extensions.size() == 0, block_validate_exception, "no supported extensions" );
start_block( b->timestamp, b->confirmed, s );
transaction_trace_ptr trace;
for( const auto& receipt : b->transactions ) {
auto num_pending_receipts = pending->_pending_block_state->block->transactions.size();
if( receipt.trx.contains() ) {
auto& pt = receipt.trx.get();
auto mtrx = std::make_shared(pt);
trace = push_transaction( mtrx, fc::time_point::maximum(), receipt.cpu_usage_us, true );
} else if( receipt.trx.contains() ) {
trace = push_scheduled_transaction( receipt.trx.get(), fc::time_point::maximum(), receipt.cpu_usage_us, true );
} else {
EOS_ASSERT( false, block_validate_exception, "encountered unexpected receipt type" );
}
.......
.......
综上,延迟交易发起后,会先判断权限,如果权限验证通过,则把交易存放在内存里面,如果不通过,则直接拒绝,此外,net资源的使用也是在schedule_deferred_transaction函数中进行的,使用的是当前调用合约对象的内存。