EOS合約開發第十七章-合約通訊程式設計
合約通訊程式設計
一、EOS通知概述
我們首先看一看eosio.token合約中issue的通知。跳過基本的合約和賬戶配置,我們直接進入eosio.token合約,首先建立一個token:
[[email protected] nodeos1]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action eosio.token create '{"issuer":"eosio","maximum_supply":"100000000000.0000 EOS","can_freeze":0,"can_recall":0,"can_whitelist":0}' -p eosio.token executed transaction: 756ecf4050184dd06c7f27d77cca3864e41fa3c319b23c326180d7cc386b62d9 120 bytes 6597 us # eosio.token <= eosio.token::create {"issuer":"eosio","maximum_supply":"100000000000.0000 EOS","can_freeze":0,"can_recall":0,"can_whitel... warning: transaction executed locally, but may not be confirmed by the network yet
然後eosio向配置好的賬戶helloworld發行token:
[[email protected] dice]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action eosio.token issue '{"to":"helloworld", "quantity":"100.0000 EOS", "memo":"m"}' -p eosio executed transaction: 732e883ae259644de6c4442a0b4007fdbc0d1b95157c5b000a3f7c9cbb1eecf7 120 bytes 1871 us # eosio.token <= eosio.token::issue {"to":"helloworld","quantity":"100.0000 EOS","memo":"m"} >> issueeosio balance: 100.0000 EOS # eosio.token <= eosio.token::transfer {"from":"eosio","to":"helloworld","quantity":"100.0000 EOS","memo":"m"} >> transfer from eosio to helloworld 100.0000 EOS # eosio <= eosio.token::transfer {"from":"eosio","to":"helloworld","quantity":"100.0000 EOS","memo":"m"} # helloworld <= eosio.token::transfer {"from":"eosio","to":"helloworld","quantity":"100.0000 EOS","memo":"m"} warning: transaction executed locally, but may not be confirmed by the network yet
從以上issue合約執行的日誌中可以看出賬戶之間存在多次的通知,可以用以下圖來描述整個訊息流:
eosio.token合約首先收到issue action的訊息,在eosio.token合約執行時發起了一個新的transfer action訊息給eosio.token合約,即發給自己,如下是issue action程式碼片段:
void token::issue( account_name to, asset quantity, string memo ) { print( "issue" ); auto sym = quantity.symbol; eosio_assert( sym.is_valid(), "invalid symbol name" ); auto sym_name = sym.name(); stats statstable( _self, sym_name ); auto existing = statstable.find( sym_name ); eosio_assert( existing != statstable.end(), "token with symbol does not exist, create token before issue" ); const auto& st = *existing; require_auth( st.issuer ); eosio_assert( quantity.is_valid(), "invalid quantity" ); eosio_assert( quantity.amount > 0, "must issue positive quantity" ); eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); eosio_assert( quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply"); statstable.modify( st, 0, [&]( auto& s ) { s.supply += quantity; }); add_balance( st.issuer, quantity, st, st.issuer ); if( to != st.issuer ) { SEND_INLINE_ACTION( *this, transfer, {st.issuer,N(active)}, {st.issuer, to, quantity, memo} ); } }
我可以看到在第27行,傳送了一個action,採用的是inline方式(這是eos兩種通訊方式的一種,還有一種是Deferred ),傳送到自己即eosio.token的transfer action上。
以下是eosio.token的transfer action程式碼片段:
void token::transfer( account_name from,
account_name to,
asset quantity,
string /*memo*/ )
{
print( "transfer from ", eosio::name{from}, " to ", eosio::name{to}, " ", quantity, "\n" );
eosio_assert( from != to, "cannot transfer to self" );
require_auth( from );
eosio_assert( is_account( to ), "to account does not exist");
auto sym = quantity.symbol.name();
stats statstable( _self, sym );
const auto& st = statstable.get( sym );
require_recipient( from );
require_recipient( to );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must transfer positive quantity" );
eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" );
sub_balance( from, quantity, st );
add_balance( to, quantity, st, from );
}
從第14、15行可以看到其給from和to(即eosio和helloworld)各發送一個通知訊息。
那麼該通知資訊會被eosio和helloworld兩個賬戶收到嗎?如果賬戶能收到通知訊息,通知訊息傳送給賬戶時的引數又是什麼呢?那麼賬戶怎麼處理通知訊息呢?通過以下程式設計來深入瞭解EOS的通知訊息。
二、EOS通知程式設計
我們建立三個合約,模擬兩個遊戲玩家在對戰平臺玩遊戲的場景,遊戲玩家1執行合約action後,遊戲平臺將收到玩家1請求,平臺合約將執行玩家1的請求,同時將結果分別通知給遊戲玩家1和遊戲玩家2以通知其更新資料,遊戲玩家完成更新後再通知平臺合約已完成更新。在每個合約入口我們都首先將合約引數寫入資料庫中以跟蹤整個通知流程。
遊戲平臺合約:
/**
* * The apply() methods must have C calling convention so that the blockchain can lookup and
* * call these methods.
* */
#include <eosiolib/eosio.hpp>
extern "C" {
struct gameinfo {
uint64_t id;
account_name player1;
uint64_t x1;
uint64_t y1;
account_name player2;
uint64_t x2;
uint64_t y2;
uint64_t info;
uint64_t primary_key()const { return id; }
//EOSLIB_SERIALIZE( gameinfo, (id)(player)(x)(y) )
};
typedef eosio::multi_index< N(gameinfo), gameinfo> gameinfo_index;
struct notifyinfo {
uint64_t id;
account_name receiver;
account_name code;
account_name action;
uint64_t primary_key()const { return id; }
//EOSLIB_SERIALIZE( notifyinfo, (id)(receiver)(code)(action) )
};
typedef eosio::multi_index< N(notifyinfo), notifyinfo> notifyinfo_index;
struct dummy_action {
account_name player1;
account_name player2;
uint64_t info;
};
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
//
auto _self = receiver;
{
notifyinfo_index notifyinfos(_self, _self);
auto new_notifyinfo_itr = notifyinfos.emplace(_self, [&](auto& info){
info.id = notifyinfos.available_primary_key();
info.receiver = receiver;
info.code = code;
info.action = action;
});
}
//
if(receiver == code && action == N(creategame)) {
dummy_action dummy = eosio::unpack_action_data<dummy_action>();
gameinfo_index gameinfos(_self, _self);
auto new_gameinfos_itr = gameinfos.emplace(_self, [&](auto& info){
info.id = gameinfos.available_primary_key();
info.player1 = dummy.player1;
info.x1 = 0;
info.y1 = 0;
info.player2 = dummy.player2;
info.x2 = 0;
info.y2 = 0;
info.info = dummy.info;
});
//
require_recipient(dummy.player1);
require_recipient(dummy.player2);
} else if(action == N(creategame)) {
// do something....
//
} else if(receiver == code && action == N(reset)) {
gameinfo_index gameinfos(_self, _self);
auto cur_gameinfos_itr = gameinfos.begin();
while(cur_gameinfos_itr != gameinfos.end()) {
cur_gameinfos_itr = gameinfos.erase(cur_gameinfos_itr);
}
notifyinfo_index notifyinfos(_self, _self);
auto cur_notifyinfos_itr = notifyinfos.begin();
while(cur_notifyinfos_itr != notifyinfos.end()) {
cur_notifyinfos_itr = notifyinfos.erase(cur_notifyinfos_itr);
}
}
}
} // extern "C"
使用者合約程式碼:
/**
* * The apply() methods must have C calling convention so that the blockchain can lookup and
* * call these methods.
* */
#include <eosiolib/eosio.hpp>
extern "C" {
struct gameinfo {
uint64_t id;
account_name player1;
uint64_t x1;
uint64_t y1;
account_name player2;
uint64_t x2;
uint64_t y2;
uint64_t info;
uint64_t primary_key()const { return id; }
//EOSLIB_SERIALIZE( gameinfo, (id)(player)(x)(y) )
};
typedef eosio::multi_index< N(gameinfo), gameinfo> gameinfo_index;
struct notifyinfo {
uint64_t id;
account_name receiver;
account_name code;
account_name action;
uint64_t primary_key()const { return id; }
//EOSLIB_SERIALIZE( notifyinfo, (id)(receiver)(code)(action) )
};
typedef eosio::multi_index< N(notifyinfo), notifyinfo> notifyinfo_index;
struct dummy_action {
account_name player1;
account_name player2;
uint64_t info;
};
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
auto _self = receiver;
{
notifyinfo_index notifyinfos(_self, _self);
auto new_notifyinfo_itr = notifyinfos.emplace(_self, [&](auto& info){
info.id = notifyinfos.available_primary_key();
info.receiver = receiver;
info.code = code;
info.action = action;
});
}
//
if(action == N(creategame)) {
dummy_action dummy = eosio::unpack_action_data<dummy_action>();
gameinfo_index gameinfos(_self, _self);
auto new_gameinfos_itr = gameinfos.emplace(_self, [&](auto& info){
info.id = gameinfos.available_primary_key();
info.player1 = dummy.player1;
info.x1 = 0;
info.y1 = 0;
info.player2 = dummy.player2;
info.x2 = 0;
info.y2 = 0;
info.info = dummy.info;
});
//
require_recipient(code);
}
}
} // extern "C"
我們將建立三個賬戶,分別運行遊戲平臺合約和兩個遊戲玩家合約:
[[email protected] test_notify_s]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 create account eosio game.s EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
executed transaction: c7dc538ecf56ba612eae50eb266848e1ae6a9907cbbab62ae8ade48018c38644 200 bytes 451 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"game.s","owner":{"threshold":1,"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJq...
warning: transaction executed locally, but may not be confirmed by the network yet
[[email protected] test_notify_s]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 create account eosio game.c.1 EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
executed transaction: f44bdfa622c06fb170e5b564bca0fd52f69d4cfc068bad9546a820ad822d6ece 200 bytes 403 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"game.c.1","owner":{"threshold":1,"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVP...
warning: transaction executed locally, but may not be confirmed by the network yet
[[email protected] test_notify_s]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 create account eosio game.c.2 EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
executed transaction: c52b61f22f6df8b46ac7a8623e3114404cfc2dceec81363abfee094f7e450c28 200 bytes 476 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"game.c.2","owner":{"threshold":1,"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcfnVP...
warning: transaction executed locally, but may not be confirmed by the network yet
在遊戲平臺賬戶上部署遊戲平臺合約:
[[email protected] test_notify_s]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 set contract game.s /home/kingnet/tangy/eos/mycontracts/test_notify_s/
Reading WAST/WASM from /home/kingnet/tangy/eos/mycontracts/test_notify_s/test_notify_s.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 240396da31c7d4d193befc07bfe4db4956a250e0760a0156adeae8d3979a7017 3848 bytes 1273 us
# eosio <= eosio::setcode {"account":"game.s","vmtype":0,"vmversion":0,"code":"0061736d0100000001560f6000006000017e60027e7e006...
# eosio <= eosio::setabi {"account":"game.s","abi":{"types":[],"structs":[{"name":"creategame","base":"","fields":[{"name":"p...
warning: transaction executed locally, but may not be confirmed by the network yet
分別在兩個遊戲玩家賬戶張部署遊戲客戶端合約:
[[email protected] test_notify_g1]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 set contract game.c.1 /home/kingnet/tangy/eos/mycontracts/test_notify_g1
Reading WAST/WASM from /home/kingnet/tangy/eos/mycontracts/test_notify_g1/test_notify_g1.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 1c1b448f52cd4bca5c251cacf56c93ffaa49abfb2a03d7e065b18068f522cb9b 3368 bytes 1201 us
# eosio <= eosio::setcode {"account":"game.c.1","vmtype":0,"vmversion":0,"code":"0061736d0100000001560f6000006000017e60027e7e0...
# eosio <= eosio::setabi {"account":"game.c.1","abi":{"types":[],"structs":[{"name":"gameinfo","base":"","fields":[{"name":"i...
warning: transaction executed locally, but may not be confirmed by the network yet
[[email protected] test_notify_g1]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 set contract game.c.2 /home/kingnet/tangy/eos/mycontracts/test_notify_g1
Reading WAST/WASM from /home/kingnet/tangy/eos/mycontracts/test_notify_g1/test_notify_g1.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: d3456d22a8ea450edbf1ee014833dbd00aecc6fd22883fbe500460681bae653b 3368 bytes 1000 us
# eosio <= eosio::setcode {"account":"game.c.2","vmtype":0,"vmversion":0,"code":"0061736d0100000001560f6000006000017e60027e7e0...
# eosio <= eosio::setabi {"account":"game.c.2","abi":{"types":[],"structs":[{"name":"gameinfo","base":"","fields":[{"name":"i...
warning: transaction executed locally, but may not be confirmed by the network yet
我們接下來執行一次遊戲建立操作:
[[email protected] test_notify_g1]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action game.s creategame '{"player1":"game.c.1","player2":"game.c.2","info":1}' -p game.s
executed transaction: 5048ffd4082325ca9f72ad9ac2c6fd7f3a6befbf87766343b064b57b6fac2fd5 120 bytes 4871 us
# game.s <= game.s::creategame {"player1":"game.c.1","player2":"game.c.2","info":1}
# game.c.1 <= game.s::creategame {"player1":"game.c.1","player2":"game.c.2","info":1}
# game.c.2 <= game.s::creategame {"player1":"game.c.1","player2":"game.c.2","info":1}
warning: transaction executed locally, but may not be confirmed by the network yet
從以上執行的結果可以看到,creategame這個action首先發給了game.s,這是我們發起的,這個action訊息又轉發給了game.c.1和game.c.2,這是我們在creategame這個action中傳送的通知訊息。
我們可以繼續看看每個合約賬戶上收到的action通知訊息:
[[email protected] test_notify_g1]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 get actions game.s
# seq when contract::action => receiver trx id... args
================================================================================================================
# 0 2018-05-17T09:56:55.500 eosio::setcode => eosio 240396da... {"account":"game.s","vmtype":0,"vmversion":0,"code":"0061736...
# 1 2018-05-17T09:56:55.500 eosio::setabi => eosio 240396da... {"account":"game.s","abi":{"types":[],"structs":[{"name":"cr...
# 2 2018-05-17T10:03:41.000 game.s::creategame => game.s 5048ffd4... {"player1":"game.c.1","player2":"game.c.2","info":1}...
# 3 2018-05-17T10:03:41.000 game.s::creategame => game.c.1 5048ffd4... {"player1":"game.c.1","player2":"game.c.2","info":1}...
# 4 2018-05-17T10:03:41.000 game.s::creategame => game.c.2 5048ffd4... {"player1":"game.c.1","player2":"game.c.2","info":1}...
[[email protected] test_notify_g1]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 get actions game.c.1
# seq when contract::action => receiver trx id... args
================================================================================================================
# 0 2018-05-17T09:59:50.500 eosio::setcode => eosio 1c1b448f... {"account":"game.c.1","vmtype":0,"vmversion":0,"code":"00617...
# 1 2018-05-17T09:59:50.500 eosio::setabi => eosio 1c1b448f... {"account":"game.c.1","abi":{"types":[],"structs":[{"name":"...
# 2 2018-05-17T10:03:41.000 game.s::creategame => game.c.1 5048ffd4... {"player1":"game.c.1","player2":"game.c.2","info":1}...
[[email protected] test_notify_g1]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 get actions game.c.2
# seq when contract::action => receiver trx id... args
================================================================================================================
# 0 2018-05-17T10:00:05.000 eosio::setcode => eosio d3456d22... {"account":"game.c.2","vmtype":0,"vmversion":0,"code":"00617...
# 1 2018-05-17T10:00:05.000 eosio::setabi => eosio d3456d22... {"account":"game.c.2","abi":{"types":[],"structs":[{"name":"...
# 2 2018-05-17T10:03:41.000 game.s::creategame => game.c.2 5048ffd4... {"player1":"game.c.1","player2":"game.c.2","info":1}...
我們在入口跟蹤了通知訊息並寫入了資料庫,如果檢視資料庫可以得出一致的訊息,同時我們在收到通知後也正確解析了請求的引數,檢視資料庫可以得到資料庫中三個表的內容完全一致。
我們現在可以回答上面關於eos通知訊息的問題了:
1. 向一個合約傳送的通知會被這個合約接收並在應用層處理嗎?
一個合約向另一個合約發出的通知訊息會被另一個合約收到,不管有沒有ABI定義介面,都會在應用層收到。
2. 引數是什麼呢?
一個合約向另一個合約傳送的通知訊息就是本合約收到的訊息,包括code、action和引數
3. 賬戶如何處理該通知訊息呢?
和處理正常的訊息一樣,根據code、action和引數進行邏輯處理
三、拒絕接受轉賬交易及DoS
我們目前已經清楚了eos合約通知機制,同時也知道了給一個賬戶轉賬交易會給該賬戶訊息通知,其實我們還有一個疑問,如果賬戶合約處理通知訊息失敗了,那麼這個交易狀態會回滾麼?
我們現在開發一個合約,合約處理交易的transfer事件通知,在通知處理程式中,我們丟擲一個異常,使得該通知處理失敗。同時我們照樣在入口記錄所有收到的action。
我們會將該合約部署在賬戶test.notify上。我們轉賬給test.notify,那麼eosio.token合約的transfer action會被執行,在eosio.token合約執行轉賬時,給向目標賬戶test.notify傳送事件通知,但事件通知處理失敗了,到底這個轉賬交易會回滾麼?
拒絕接收轉賬合約:
/**
* * The apply() methods must have C calling convention so that the blockchain can lookup and
* * call these methods.
* */
#include <eosiolib/eosio.hpp>
extern "C" {
struct notifyinfo {
uint64_t id;
account_name receiver;
account_name code;
account_name action;
uint64_t primary_key()const { return id; }
//EOSLIB_SERIALIZE( notifyinfo, (id)(receiver)(code)(action) )
};
typedef eosio::multi_index< N(notifyinfo), notifyinfo> notifyinfo_index;
/// The apply method implements the dispatch of events to this contract
void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
auto _self = receiver;
notifyinfo_index notifyinfos(_self, _self);
auto new_notifyinfo_itr = notifyinfos.emplace(_self, [&](auto& info){
info.id = notifyinfos.available_primary_key();
info.receiver = receiver;
info.code = code;
info.action = action;
});
if(action == N(transfer)) {
eosio_assert("reject transfer!");
}
if(receiver == code && action == N(reset)) {
auto cur_notify_itr = notifyinfos.begin();
while(cur_notify_itr != notifyinfos.end()) {
cur_notify_itr = notifyinfos.erase(cur_notify_itr);
}
}
}
} // extern "C"
我們執行轉賬操作並檢查結果:
[[email protected] test_notify]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 create account eosio test.notify EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
executed transaction: 4576dec103dab2abfbfff775f8208c8fff46996cc9d3717d576574544f381d72 200 bytes 476 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"test.notify","owner":{"threshold":1,"keys":[{"key":"EOS6MRyAjQq8ud7hVNYcf...
warning: transaction executed locally, but may not be confirmed by the network yet
[[email protected] test_notify]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 set contract test.notify /home/kingnet/tangy/eos/mycontracts/test_notify
Reading WAST/WASM from /home/kingnet/tangy/eos/mycontracts/test_notify/test_notify.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: 7bd410a214ed2700eea182bebcdf94304ee49911e8f0c53dcfa58068150067d4 5048 bytes 1200 us
# eosio <= eosio::setcode {"account":"test.notify","vmtype":0,"vmversion":0,"code":"0061736d0100000001520e6000006000017e60027e...
# eosio <= eosio::setabi {"account":"test.notify","abi":{"types":[],"structs":[{"name":"hi","base":"","fields":[{"name":"user...
warning: transaction executed locally, but may not be confirmed by the network yet
[[email protected] test_notify]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action eosio.token issue '{"to":"test.notify","quantity":"1001.0000 EOS", "memo":"m"}' -p eosio
executed transaction: 995b01cd4206ee19894400f09d2d0bbbba1b5d9e74010438447387e4be1f226e 120 bytes 10667 us
# eosio.token <= eosio.token::issue {"to":"test.notify","quantity":"1001.0000 EOS","memo":"m"}
>> issueeosio balance: 1001.0000 EOS
# eosio.token <= eosio.token::transfer {"from":"eosio","to":"test.notify","quantity":"1001.0000 EOS","memo":"m"}
>> transfer from eosio to test.notify 1.0000 EOS
# eosio <= eosio.token::transfer {"from":"eosio","to":"test.notify","quantity":"1001.0000 EOS","memo":"m"}
# test.notify <= eosio.token::transfer {"from":"eosio","to":"test.notify","quantity":"1001.0000 EOS","memo":"m"}
warning: transaction executed locally, but may not be confirmed by the network yet
[[email protected] test_notify]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 get currency balance eosio.token test.notify
1001.0000 EOS
[[email protected] test_notify]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 push action eosio.token transfer '{"from":"alice","to":"test.notify","quantity":"1.0000 EOS","memo":"m"}' -p alice
1566956ms thread-0 main.cpp:2316 main ] Failed with error: Assert Exception (10)
condition: assertion failed: reject transfer!
[[email protected] test_notify]$ cleos --wallet-url http://localhost:9800 --url http://localhost:9800 get currency balance eosio.token test.notify
1001.0000 EOS
我們可以看到轉賬失敗了,這個失敗是由轉賬的接收方處理轉賬通知事件時給出錯誤應答引起的,這樣就可以讓一個賬戶拒絕接收轉賬。
這個例子演示了時事件通知處理失敗會導致整個交易回滾,如果一個transaction中包含了許多的action,但只要某一個賬戶處理action的通知事件失敗將引起整個交易回滾,這樣某一個賬戶可以進行DoS(拒絕服務攻擊)。
下一章介紹EOS合約中通訊程式設計。
相關推薦
EOS合約開發第十七章-合約通訊程式設計
合約通訊程式設計一、EOS通知概述我們首先看一看eosio.token合約中issue的通知。跳過基本的合約和賬戶配置,我們直接進入eosio.token合約,首先建立一個token:[[email protected] nodeos1]$ cleos --wall
EOS合約開發第十八章-合約通訊程式設計(2)
合約通訊程式設計 一、通訊模型和執行流程 EOSIO智慧合約可以相互通訊,例如讓另一個合約執行某些與當前action相關的操作,或觸發當前action範圍之外的未來交易。 EOSIO支援Inline和Deferred兩種基本通訊模式。Inline通訊可以理解為在當前
EOS合約開發第十九章-集中博彩遊戲合約設計
集中博彩遊戲合約設計 一、功能介面 1. 質押deposit 由使用者發起,使用者將個人賬戶中token質押給平臺,從而可以進入平臺去參與平臺活動。 2. 贖回withdraw 由使用者發起,在使用者結束平臺活動需要離開時,發起贖回曾質押給平臺的token到個人賬戶
SpringBoot | 第十七章:web 應用開發之檔案上傳
前言 上一章節,我們講解了利用模版引擎實現前端頁面渲染,從而實現動態網頁的功能,同時也提出了相容jsp專案的解決方案。既然開始講解web開發了,我們就接著繼續往web這個方向繼續吧。通常,我們在做web開發時,碰見諸如附件上傳、圖片頭像上傳等檔案的上傳需求也是再正常不過的。
敏捷軟件開發 第十三章~第十七章
arr spa ade 角度 ati strategy 每次 命令執行 管理器 這幾章主要講了幾個設計模式,分別是: Command (命令模式) Active Object (主動對象模式) Template Method (模板方法模式) Strategy
我的學習之路_第十七章_JavaUtils
包含 string 和數 類的方法 objc 基本數據類型 通過 序列 setprop 【BeanUtils工具類】 javaBaen : Java和數據庫所對應關系實體類 表(Utils)-->類(User) 表中的列-->類中字段(屬性) 表中的行-->
第十七章 MariaDB
mariadb mysql 關系型數據庫 rdbms 關系型數據庫範式 17.1 MariaDB簡介 MariaDB數據庫管理系統是MySQL的一個分支,主要由開源社區維護,采用GPL授權許可。MariaDB問世的目的是完全兼容MySQL,包括API和命令行,使之能輕松成為MySQL的代
第十七章 使用DQL命令查詢數據
java基礎 基礎 模糊查詢 註意 subject des avg dql 最大數 1.查詢所有:select * from subject; 2.查詢指定列:SELECT studentNo,Phone FROM student; 3.As作用: 給數據列取別名:se
第十七章.網絡編程
負責 定位 gen rac isp mission b- rpo rop Java的基本網絡支持: 使用InetAddress: 1 import java.net.InetAddress; 2 3 public class InetAddressTest{
【第十七章】 springboot + devtools(熱部署)
logs 只需要 gin 排除 pre pub ron 這樣的 plugin 技術介紹 devtools:是boot的一個熱部署工具,當我們修改了classpath下的文件(包括類文件、屬性文件、頁面等)時,會重新啟動應用(由於其采用的雙類加載器機制,這個啟動會非常快
構建之法第四章第十七章
height 多文檔 缺失 後來 更強 最大的 fun 影響 手機 一、關於goto函數:濫用goto語句會使程序變得很難理解,而不是所有人能正確的使用goto函數,我的問題是:是不是因為這樣所以很多文檔規定禁用或少用goto函數?但其實如果可以正確的使用goto語句就不能
讀《構建之法》第四章、第十七章
span 指定 十分 鸚鵡 市場 utf 亂碼 修改 職業道德 第四章《兩人合作》 1.原文:“註釋(包括所有源代碼)應該只用ASCLL字符,不要使用中文和其他字符,否則會極大影響程序的可植性” 疑問:引擎根本不對空行和註釋進行解析,直接忽略掉,它們不參與計算代碼行數也不參
讀構建之法第四章第十七章有感
限制 選擇 class blog 了解 什麽 靈活 多重循環 價值 第四章 1、原文;“函數最好有單一的出口,為了達到這個目的,可以使用goto.只要有助於程序邏輯的清晰體現,什麽方法都可以使用。——P69” 問題:關於goto,我記得老師講過,這個在編程中是盡力避
《構建之法》第四、第十七章讀後感
可能 學習 www. 我沒 方式 去掉 bat log http 第四章 在這一章最後一頁“ 讓{}獨占一樣還有一個好處:一眼就能看出是否有多余的代碼行 ,還有些情況下是致命的錯誤”給出的參考鏈接http://lpar.ath0.com/2014/02/23/l
讀《構建之法》第四章、第十七章有感
author 基礎 忽略 旁觀者 才有 htm 心理 核心 選擇 書是我們永遠的朋友 它陪伴我們走過人生的春夏秋冬 在我們的生命中生根、發芽、枝繁葉茂 書是人類發展的錄像機 我們可以在其中看到前輩的足跡 書是知識的海洋 我願是一葉輕舟,載著理想之帆 在海
閱讀《構建之法》第四章、第十七章收獲
... 如果 spa exist 通用 類成員函數 根據 認識 ron 閱讀《構建之法》第四章、第十七章 閱讀這一章的時候,我意識到了很多以前寫程序沒有註意到的地方,以前寫程序就只知道能運行就好,根本不管自己寫的程序占多少內存,運行的時間,是否有優化的空間,寫代碼的時候也不
第十七章 MySQL主從配置
第十七章 MySQL主從配置17.1 MySQL主從介紹 MySQL主從又叫做Replication、AB復制。簡單講就是A和B兩臺機器做主從後,在A上寫數據,另外一臺B也會跟著寫數據,兩者數據實時同步。MySQL主從是基於binlog的,主上須開啟binlog才能進行主從。 主從過程 主將更改操作記錄到bi
第十七章 MySQL主從配置
linux17.1 MySQL主從介紹MySQL主從又叫做Replication、AB復制。簡單講就是A和B兩臺機器做主從後,在A上寫數據,另外一臺B也會跟著寫數據,兩者數據實時同步的MySQL主從是基於binlog的,主上須開啟binlog才能進行主從。主從過程大致有3個步驟1)主將更改操作記錄到binlo
第十七章 按列切分文件字段工具:cut命令
con 默認 ont del int ESS cat 一是 合並 第十七章 按列切分文件字段工具:cut命令 名詞解釋 cut 命令 用來顯示行中的指定部分內容,刪除文件中指定字段。cut經常用來顯示文件的內容,類似於type命令。 說明:該命令有兩項功能,其一是用來顯示文
《Java編程思想》筆記 第十七章 容器深入研究
let 類庫 font 有關 有一個 www. 強引用 容器類 刪除 1 容器分類 容器分為Collection集合類,和Map鍵值對類2種 使用最多的就是第三層的容器類,其實在第三層之上還有一層Abstract 抽象類,如果要實現自己的集合類,可以繼承Abstract類