【EOS踩坑记】
1、每个account只能更新自己的contract,即使两个account的秘钥相同,也不允许。
如下,使用alice的权限来更新james的contract。会返回 Missing required authority错误。
cleos set contract james /home/ubuntu/contracts/hello/jameshello -p alice@active
应该改为:
cleos set contract james /home/ubuntu/contracts/hello/jameshello -p james@active
2、contract class 名可以与 account 名不一致。如,以下调用是OK的。
cleos set contract james /home/ubuntu/contracts/hello/xhello -p james@active
3、一个account只能有一个contract。
例如,hello 账户有一个hi函数,set contract,此时abi 为:
此时,有一个xhello contract,内含一个 hi2 函数,将xhello合约提交。
cleos set contract hello /home/ubuntu/contracts/hello/xhello -p hello@active
则 hello 账户的 abi 会变为:
可以看见,原有的hi action不见了。
4、contract class 名、目录名、文件名必须一致。
1)如下,文件名为hello,目录名为xhello,两者不一致。
cleos set contract james /home/ubuntu/contracts/hello/xhello -p james@active
执行后,会返回找不到文件错误。
2)如下,contract class名为 jameshello,而目录名为hello。
cleos set contract james /home/ubuntu/contracts/hello/hello -p james@active
虽然可以成功发布合约,但合约的abi是空的,也就是实际等于没有发布成功。
5、任意account都有权调用他人的contract。如,下面用bob的权限调用james的hi action,是合法的,不论两者的公钥是否相同。
cleos push action james hi '["bob"]' -p bob@active
6、require_auth(name n)
Verifies that @ref name exists in the set of provided auths on a action. Fails if not found.
检测name是否在通过-p提供的名单里。
可以用于限制每个account在调用action时,只能修改自身相关信息。例如:
[[eosio::action]]
void hi( name user )
{
require_auth(user);
print("Hello, ", user);
}
7、contract 类中的 get_self()方法、_self。
1)_self 是 contract 类的一个 protected member,其类型为 name,可以知道,它存的是一个账号。
2)_self的赋值来源于 receiver,所以_self就是receiver。
3)get_self() 返回的就是 _self,也就是receiver,也就是一个account。
8、symbol_code
symbol_code 是一个 class.
只含有一个 uint64_t value 成员变量。
symbol_code 将长度为7以内(包括7)的字符串 encode 为 uint64_t value。(注意 uint64_t可以存储8个byte)
9、symbol
symbol 是一个class
只包括一个成员变量
通过构造函数可以看到 symbol可以存储8个Byte,上文的symbol_code使用了7个Byte,所以symbol可以将symbol_code存储在自身,并且额外加了一个precision字段。
symbol_code 指的是 TOKEN Name,如DICE、EOS。
precision 指的是一个TOKEN的精度,比如 100000000.0000,上限是1亿,precision是4。
10、asset
下面的命令可以调用 eosio.token 的action,创建一个新的TOKEN.
cleos push action eosio.token create '[ "eosio", "1000000000.0000 SYS"]' -p eosio.token@active
而 asset 代表的就是 "1000000000.0000 SYS"。
asset 是一个class。他只有2个成员变量,amount对应上述命令的 1000000000.0000, 而symbol则代码SYS,以及精度4。
11、multi_index
multi_index 是一个class,意为可以包含多个索引的table。eosio.cdt源码中有一个完整的示例:
* #include <eosiolib/eosio.hpp>
* using namespace eosio;
* class mycontract: contract {
* struct record {
* uint64_t primary;
* uint64_t secondary_1;
* uint128_t secondary_2;
* uint256_t secondary_3;
* double secondary_4;
* long double secondary_5;
* uint64_t primary_key() const { return primary; }
* uint64_t get_secondary_1() const { return secondary_1; }
* uint128_t get_secondary_2() const { return secondary_2; }
* uint256_t get_secondary_3() const { return secondary_3; }
* double get_secondary_4() const { return secondary_4; }
* long double get_secondary_5() const { return secondary_5; }
* };
* public:
* mycontract(name receiver, name code, datastream<const char*> ds):contract(receiver, code, ds){}
* void myaction() {
* auto code = _self;
* auto scope = _self;
* multi_index<"mytable"_n, record,
* indexed_by< "bysecondary1"_n, const_mem_fun<record, uint64_t, &record::get_secondary_1> >,
* indexed_by< "bysecondary2"_n, const_mem_fun<record, uint128_t, &record::get_secondary_2> >,
* indexed_by< "bysecondary3"_n, const_mem_fun<record, uint256_t, &record::get_secondary_3> >,
* indexed_by< "bysecondary4"_n, const_mem_fun<record, double, &record::get_secondary_4> >,
* indexed_by< "bysecondary5"_n, const_mem_fun<record, long double, &record::get_secondary_5> >
* > table( code, scope);
* }
* }
* EOSIO_DISPATCH( mycontract, (myaction) )
第一个参数是拥有这张表的account,第二个参数是 scope。
find参数可以用来根据主键查找。
下面是 eosio.token 中的使用代码:
stats statstable( _self, sym.code().raw() );
auto existing = statstable.find( sym.code().raw() );
eosio_assert( existing == statstable.end(), "token with symbol already exists" ); statstable.emplace( _self, [&]( auto& s ) {
s.supply.symbol = maximum_supply.symbol;
s.max_supply = maximum_supply;
s.issuer = issuer;
});
12、SEND_INLINE_ACTION
SEND_INLINE_ACTION 是eosio.cdt的action.hpp中定义的一个宏.
在 eosio.token 中的使用如下:
第一个参数是 account(也即 contract),第二个参数是 action name,第三个参数是权限。之后的参数全部传递给 action。
可以看一下 transfer 函数的原型。
13、require_recipient
这个方法用于通知 notify_account,当前调用方法以某些参数被调用。
例如,在transfer(from,to,quantity,memo)中调用了
require_recipient(from)
require_recipient(to)
则from、to分别会拥有一条执行了transfer() action的记录:
这只是一条记录,并不说明 transfer被多执行了两次。
14、multi_index 中的 scope
multi_index 类似于数据库中的Table,struct 定义了Row的结构。
为了提升多线程读写性能,eos加入了 scope概念,scope是一个 uint64_t。一个scope下包含多个row。所以形成了 Table-Scope-Row 的结构。
在 eosio.token 示例中,分别使用 symbol.code().raw() 作为 scope,以及使用 owner.value 账户名作为 scope。
从第2个索引起,the name is less important as you will pass the getter name into the typedef.
15、code 是发布合约的account. 详情参考[3]
16、permission_level
permission_level 是一个 struct,只包含 name actor; name permission; 两个字段。
17、eosio.code
面临的问题:
比如alice调用智能合约contract1.test,一开始alice看过contract1.test的逻辑,发现它只是一个打印函数,并不会调用其他合约。所以alice以自己active的权限alice@active去执行contract1.test。
但是contract1的拥有者某一天可能偷偷更改了test的实现,在test函数中调用eosio.token的transfer函数以alice@active权限就可以取走alice的EOS.
解决方案:
为了解决权限乱用问题,EOS新增了eosio.code这个特殊权限。采用eosio.code后,contract1.test要以alice@active去调用eosio.token,必须得到alice的授权,即必须在alice@active里添加contrac1@eosio.code授权.
$cleos set account permission alice active '{"threshold": 1,"keys": [{"key":"EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", "weight":1}],"accounts": [{"permission":{"actor":"contract1","permission":"eosio.code"},"weight":1}]}' owner -p alice@owner
account_1:alive : 1 account_2@eosio.code 的含意为:当account_1调用account_2的合约代码时,account_2可以以account_1.alive 权限去调用外部 contract action.
更详细资料可见[参考4].
18、The primary key of the object in the eosio::multi_index
container must be a unique unsigned 64-bit integer. The objects in the eosio::multi_index
container are sorted by the primary key index in ascending order of the unsigned 64-bit integer primary key.
A key differentiator of the EOSIO persistence services over other blockchain infrastructures is its Multi-Index iterators. Unlike some other blockchains that only provide a key-value store, EOSIO Multi-Index tables allow a contract developer to keep a collection of objects sorted by a variety of different key types, which could be derived from the data within the object. This enables rich retrieval capabilities. Up to 16 secondary indices can be defined, each having its own way of ordering and retrieving table contents.
19、eosio.system
eos区块链启动时,默认有一个eosio account,该账户默认会 set eosio.system contract.
通过执行以下命令,可以看到 eosio是有 setcode、setabi 等 eosio.system中的action的。
cleos get abi eosio
20、eosio.bios
In a public blockchain, this contract will manage the staking and unstaking of tokens to reserve bandwidth for CPU and network activity, and memory for contracts.
21、eosio::history_api_plugin
nodeos 启动时只有加载了这个插件,才能执行 get accounts pub-key 命令.
cleos get accounts EOSXXXXXXXXXXXXXXXXXXXX
详细见参考[6].
22、Future versions of this contract may allow other parties to buy symbol names automatically.
EOS的SYMBOL未来将会采取体售卖的形式。
23、
参考:
1、https://developers.eos.io/eosio-home/docs/your-first-contract
2、https://developers.eos.io/eosio-home/docs/token-contract
3、https://developers.eos.io/eosio-home/docs/data-persistence
4、https://blog.csdn.net/itleaks/article/details/80557560
5、https://developers.eos.io/eosio-cpp/docs/db-api
6、https://developers.eos.io/eosio-cpp/docs/introduction-to-smart-contracts