Bitshares研究系列【问题集一】
https://steemit.com/bitshares/@chaimyu/34sykz-bitshares
1. import_key时失败帐户数据也会加入钱包,用list_my_accounts能查到数据?
2.怎么发送operation?
3. 没有提供api的operation怎么发送?
4. faucet.yml配置url和port不生效
5. python-faucet怎么输出调试日志?
6. nathan、init0等帐号怎么生成的?
7. bitshares的chainid是什么?
在bitshares学习过程中,有一些零散问题整理在一块,互相之间并不一定存在关联性,计划每几个问题做一集发表。
1. import_key时失败帐户数据也会加入钱包,用list_my_accounts能查到数据?
例如公网引入"nathan"帐号
Chaim:cli_wallet Chaim$ ./cli_wallet --server-rpc-endpoint=wss://bitshares.dacplay.org/ws
Logging RPC to file: logs/rpc/rpc.log
1125691ms th_a main.cpp:136 main ] key_to_wif( committee_private_key ): 5KCBDTcyDqzsqehcb52tW5nU6pXife6V2rX9Yf7c3saYSzbDZ5W
1125692ms th_a main.cpp:140 main ] nathan_pub_key: BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
1125692ms th_a main.cpp:141 main ] key_to_wif( nathan_private_key ): 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
Starting a new wallet with chain ID 4018d7844c78f6a6c41c6a552b898022310fc5dec06da467ee7905a8dad512c8 (from egenesis)
1125692ms th_a main.cpp:188 main ] wdata.ws_server: wss://bitshares.dacplay.org/ws
1125918ms th_a main.cpp:193 main ] wdata.ws_user: wdata.ws_password:
Please use the set_password method to initialize a new wallet before continuing
new >>> set_password 123456
set_password 123456
null
locked >>> unlock 123456
unlock 123456
null
unlocked >>> import_key nathan 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
import_key nathan 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
false
unlocked >>> list_my_accounts
list_my_accounts
[{
"id": "1.2.298",
"membership_expiration_date": "1969-12-31T23:59:59",
...
}
]
查看import_key源码
bool import_key(string account_name_or_id, string wif_key)
{
fc::optional optional_private_key = wif_to_key(wif_key);
if (!optional_private_key)
FC_THROW("Invalid private key");
graphene::chain::public_key_type wif_pub_key = optional_private_key->get_public_key();
account_object account = get_account( account_name_or_id );
// make a list of all current public keys for the named account
flat_set all_keys_for_account;
std::vector active_keys = account.active.get_keys();
std::vector owner_keys = account.owner.get_keys();
std::copy(active_keys.begin(), active_keys.end(), std::inserter(all_keys_for_account, all_keys_for_account.end()));
std::copy(owner_keys.begin(), owner_keys.end(), std::inserter(all_keys_for_account, all_keys_for_account.end()));
all_keys_for_account.insert(account.options.memo_key);
_keys[wif_pub_key] = wif_key;
_wallet.update_account(account);
_wallet.extra_keys[account.id].insert(wif_pub_key);
return all_keys_for_account.find(wif_pub_key) != all_keys_for_account.end();
}
此函数先根据wif_key取得公钥wif_pub_key,然后取得帐户的active key和owner key并加入all_keys_for_account中,函数返回结果是看wif_pub_key是否在all_keys_for_account中。
如果公网"nathan"帐户取的公钥是不对的,但 _wallet.update_account(account) 仍会把帐户信息加入本地钱包中。
2.怎么发送operation?
很多operation会提供对应的api函数,例如喂价api:
signed_transaction publish_asset_feed(string publishing_account, string symbol, price_feed feed, bool broadcast)
而operation的json数据格式可以用 get_prototype_operation 取得:
unlocked >>> get_prototype_operation asset_publish_feed_operation
get_prototype_operation asset_publish_feed_operation
[
19,{
"fee": {
"amount": 0,
"asset_id": "1.3.0"
},
"publisher": "1.2.0",
"asset_id": "1.3.0",
"feed": {
"settlement_price": {
"base": {
"amount": 0,
"asset_id": "1.3.0"
},
"quote": {
"amount": 0,
"asset_id": "1.3.0"
}
},
"maintenance_collateral_ratio": 1750,
"maximum_short_squeeze_ratio": 1500,
"core_exchange_rate": {
"base": {
"amount": 0,
"asset_id": "1.3.0"
},
"quote": {
"amount": 0,
"asset_id": "1.3.0"
}
}
},
"extensions": []
}
]
很多operation都会有很多参数,例如以上发布喂价,就需要指定一个json格式的参数price_feed,而这个参数格式就是operation参数中,去掉operation id,去掉api已传参数的json格式即可,所以发布喂价的这个price_feed类似如下:
{"settlement_price": {"base": {"amount": 25,"asset_id": "1.3.1"},"quote": {"amount": 63647,"asset_id": "1.3.0"}},"maintenance_collateral_ratio": 1750,"maximum_short_squeeze_ratio":1100,"core_exchange_rate": {"base": {"amount": 25,"asset_id": "1.3.1"},"quote": {"amount": 66829,"asset_id": "1.3.0"}}}
3. 没有提供api的operation怎么发送?
可以手动构造operation并发送,调用流程:
begin_builder_transaction
add_operation_to_builder_transaction oid [opId, {operation}]
set_fees_on_builder_transaction oid BTS
sign_builder_transaction oid true
operation id参见bitshares研究系列【operation的实现】
手动构造transfer operation数据并发送交易,按如下操作:
先查看operation格式,以下构造operation时需要使用其中部分数据
unlocked >>> get_prototype_operation transfer_operation
get_prototype_operation transfer_operation
[
0,{
"fee": {
"amount": 0,
"asset_id": "1.3.0"
},
"from": "1.2.0",
"to": "1.2.0",
"amount": {
"amount": 0,
"asset_id": "1.3.0"
},
"extensions": []
}
]
unlocked >>> list_account_balances barnard18
list_account_balances barnard18
6.85982 BTS
unlocked >>> begin_builder_transaction
begin_builder_transaction
0
unlocked >>> add_operation_to_builder_transaction 0 [0,{"from": "1.2.861586", "to": "1.2.879822", "amount": {"amount": 100000, "asset_id": "1.3.0"}}]
add_operation_to_builder_transaction 0 [0,{"from": "1.2.861586", "to": "1.2.879822", "amount": {"amount": 100000, "asset_id": "1.3.0"}}]
null
unlocked >>> set_fees_on_builder_transaction 0 BTS
set_fees_on_builder_transaction 0 BTS
{
"amount": 10420,
"asset_id": "1.3.0"
}
unlocked >>> sign_builder_transaction 0 true
sign_builder_transaction 0 true
{
"ref_block_num": 54238,
"ref_block_prefix": 2893069574,
"expiration": "2018-05-08T07:47:03",
"operations": [[
0,{
"fee": {
"amount": 10420,
"asset_id": "1.3.0"
},
"from": "1.2.861586",
"to": "1.2.879822",
"amount": {
"amount": 100000,
"asset_id": "1.3.0"
},
"extensions": []
}
]
],
"extensions": [],
"signatures": [
"1f40a75c45c544217e4af6a8ecae8e04f21d7adad7db91460284debfc52736a9797b5ee906dce9eed0ae47a4ee7c2f5f3532174fe64852c7e93a8469f09ac76b5a"
]
}
unlocked >>> list_account_balances barnard18
list_account_balances barnard18
5.75562 BTS
operation需要import_key保证有私钥和权限
4. faucet.yml配置url和port不生效
配了也是监听localhost:3000
=> Rails 4.2.4 application starting in development on http://localhost:3000
没具体去查原因,直接启动时指定监听地址和端口
bash-3.2$ rails s -b 0.0.0.0 -p 3000
5. python-faucet怎么输出调试日志?
在用python-faucet时详细日志看不到,而运行又出现问题,查看graphene这些库都用的是logging
在manage.py中增加以下语句:
import logging
logging.basicConfig(level=logging.DEBUG)
这样就能打出所有调试信息了,同时也可以在这通过filename参数指定日志文件。
6. nathan、init0等帐号怎么生成的?
我们知道bitshares里有一些帐号,例如nathan、init0等,也知道这些帐号是内置的,但是怎么内置进去的呢?
在创世区块时需要指定genesis.json文件,可以用以下方式生成一个例子文件:
./witness_node --create-genesis-json my-genesis.json
文件中指定了nathan、init0等帐户的公私钥信息:
"initial_accounts": [{
"name": "init0",
"owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": true
},
...
{
"name": "nathan",
"owner_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"active_key": "BTS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
"is_lifetime_member": false
}
]
在db_init.cpp中有初始化数据库的操作,其中对初始帐户的操作如下:
void database::init_genesis(const genesis_state_type& genesis_state)
{
...
// Create initial accounts
for( const auto& account : genesis_state.initial_accounts )
{
account_create_operation cop;
cop.name = account.name;
cop.registrar = GRAPHENE_TEMP_ACCOUNT;
cop.owner = authority(1, account.owner_key, 1);
if( account.active_key == public_key_type() )
{
cop.active = cop.owner;
cop.options.memo_key = account.owner_key;
}
else
{
cop.active = authority(1, account.active_key, 1);
cop.options.memo_key = account.active_key;
}
account_id_type account_id(apply_operation(genesis_eval_state, cop).get());
if( account.is_lifetime_member )
{
account_upgrade_operation op;
op.account_to_upgrade = account_id;
op.upgrade_to_lifetime_member = true;
apply_operation(genesis_eval_state, op);
}
}
...
}
7. bitshares的chainid是什么?
使用cli_wallet等都可能需要指定chainid,而在bitshares-core修改时经常发现chainid变化了,什么原因呢?
在genesis.json中有个初始chainid,如下:
"initial_chain_id": "aa34045518f1469a28fa4578240d5f039afa9959c0b95ce3b39674efa691fb21",
在application.cpp中有此函数:
void startup()
{
...
if( modified_genesis )
{
std::cerr << "WARNING: GENESIS WAS MODIFIED, YOUR CHAIN ID MAY BE DIFFERENT\n";
genesis_str += "BOGUS";
genesis.initial_chain_id = fc::sha256::hash( genesis_str );
}
else
genesis.initial_chain_id = fc::sha256::hash( genesis_str );
...
}
其中针对genesis.json做了hash计算并得出chainid,也就是说genesis.json改了链id就变了,这也符合实际情况,创世区块一旦创建链id就确定了。
如果为了开发调试需要,当然可以把这个写成固定的链id,这样各方就不用变动了。
感谢您阅读 @chaimyu 的帖子,期待您能留言交流!