id:BSN_2021
公众号:BSN研习社
红枣科技何来亮
背景:今年3月开放联盟链“中移链”在区块链服务网络(BSN[1])中完成适配并上线发布,吸引了大批开发者,部分开发者提出了一些共性问题
目的:本篇文章是为了让读者了解如何在中移链(基于EOS)上调用RPC接口组装交易、签名、上链以及查询上链结果
适用对象:适用于BSN开放联盟链--中移链(基于EOS)开发者
1. 准备
•需要现在bsn[2]创建项目和创建一个账户,可以参考:https://bsnbase.com/static/tmpFile/bzsc/openper/7-3-6.html
•可以获取到eos 端点:https://opbningxia.bsngate.com:18602/api/aecb28acfd154cfeb90d0b6a8ecab1e7/rpc
•eos账户/合约地址:helailiang14
•为账户购买足够的资源(cpu、net、ram)
•安装EOSIO开发环境:需要安装cleos和keosd, 可以参考:https://developers.eos.io/welcome/latest/getting-started-guide/local-development-environment/installing-eosio-binaries •部署eos合约: hello.cpp
#include <eosio/eosio.hpp>
#include <eosio/transaction.hpp>
using namespace eosio;
// 通过[[eosio::contract]]标注这个类是一个合约
class [[eosio::contract]] hello : public contract
{
public:
using contract::contract;
// 在构造函数进行表对象的实例化, 标准合约构造函数,receiver也就是我们的合约账号(一般情况下),code就是我们的action名称,ds就是数据流
// get_self() 合约所在的账号 get_code() 当前交易请求的action方法名 get_datastream() 当前数据流
hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds), friend_table(get_self(), get_self().value)
{
}
// 用[[eosio::action]]标注这个方法是一个合约action就行
// 注意:action的名称要求符合name类型的规则
[[eosio::action]] void hi(name user)
{
print("Hello, ", user);
print("get_self,", get_self().value);
// print("get_code,", get_code().value);
uint32_t now = current_time_point().sec_since_epoch();
auto friend_itr = friend_table.find(user.value);
// 数据不存在
if (friend_itr == friend_table.end())
{
// 第一个参数就是内存使用的对象,第二个参数就是添加表对象时的委托方法。
friend_table.emplace(get_self(), [&](auto &f) {
f.friend_name = user;
f.visit_time = now;
});
}
else
{
// 第一个参数是传递需要修改的数据指针,第二个参数是内存使用的对象,第二个参数就是表对象修改时的委托方法
friend_table.modify(friend_itr, get_self(), [&](auto &f) {
f.visit_time = now;
});
}
}
[[eosio::action]] void nevermeet(name user)
{
print("Never see you again, ", user);
auto friend_itr = friend_table.find(user.value);
check(friend_itr != friend_table.end(), "I don't know who you are.");
// 只有一个参数,就是要删除的对象指针
friend_table.erase(friend_itr);
}
[[eosio::action]] void meetagain()
{
uint32_t now = current_time_point().sec_since_epoch();
auto time_idx = friend_table.get_index<"time"_n>();
auto last_meet_itr = time_idx.begin();
check(last_meet_itr != time_idx.end(), "I don't have a friend.");
time_idx.modify(last_meet_itr, get_self(), [&](auto &f) {
f.visit_time = now;
});
}
private:
// 定义一个结构体,然后用[[eosio::table]]标注这个结构体是一个合约表。在结构体里定义一个函数名primary_key,返回uint64_t类型,作为主键的定义
struct [[eosio::table]] my_friend
{
name friend_name;
uint64_t visit_time;
uint64_t primary_key() const { return friend_name.value; }
double by_secondary() const { return -visit_time; }
};
// 定义表名和查询索引 "friends"_n就是定义表名,所以使用了name类型,之后my_friend是表的结构类
typedef eosio::multi_index<"friends"_n, my_friend> friends;
friends friend_table;
};
2. rpc调用流程
curl -X POST 'https://opbningxia.bsngate.com:18602/api/{您的开放联盟链项目ID/rpc/v1/chain/abi_json_to_bin' \
-d '{
"code": "helailiang14",
"action": "hi",
"args": {
"user": "helloworld"
}
}'
------------------
return
{"binargs":"00408a97721aa36a"}
curl GET 'https://opbningxia.bsngate.com:18602/api/{您的开放联盟链项目ID/rpc/v1/chain/get_info'
------------------
return
{
"server_version":"11d35f0f",
"chain_id":"9b4c6015f8b73b2d7ee3ebd92d249a1aba06a614e9990dcf54f7cf2e3d5172e1",
"head_block_num":15134328,
"last_irreversible_block_num":15134262,
"last_irreversible_block_id":"00e6ee360b5e7680a526ddea45db1be15c4be2cd2389020688218fe765be6db7",
"head_block_id":"00e6ee7889523875a28284effecdd1199cc960adb14c14c36cd1bd52afed6824",
"head_block_time":"2022-04-27T09:08:08.500",
"head_block_producer":"prod.b",
"virtual_block_cpu_limit":200000000,
"virtual_block_net_limit":1048576000,
"block_cpu_limit":199900,
"block_net_limit":1048576,
"server_version_string":"v3af0a20",
"fork_db_head_block_num":15134328,
"fork_db_head_block_id":"00e6ee7889523875a28284effecdd1199cc960adb14c14c36cd1bd52afed6824",
"server_full_version_string":"v3af0a20",
"last_irreversible_block_time":"2022-04-27T09:07:35.500"
}
获取到head_block_num : 15134328
引用
获取 head_block_id:00e6ee7889523875a28284effecdd1199cc960adb14c14c36cd1bd52afed6824
引用
获取 chain_id: 9b4c6015f8b73b2d7ee3ebd92d249a1aba06a614e9990dcf54f7cf2e3d5172e1
curl -X POST 'https://opbningxia.bsngate.com:18602/api/{您的开放联盟链项目ID/rpc/v1/chain/get_block' \
-d '{
"block_num_or_id": "15130610"
}'
------------------
return
{
"timestamp": "2022-04-27T09:08:08.500",
"producer": "prod.b",
"confirmed": 0,
"previous": "00e6ee77f2655528739622d2c9235026d4f10138b9821e46ea35165cb086d12d",
"transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000",
"action_mroot": "665584b582b234bf58d3708b31da20e14d266713e3bc6ce79ea3187cc2ffa5a4",
"schedule_version": 2,
"new_producers": null,
"producer_signature": "SIG_K1_KiYCDLMgE6gE1nNqQQL2jEEF3VVd6iaspAePvvJMjKwgg2Yf6GiTYcznrkymAdtZUAUFh28N8r9RzX936cASKDB6JW6ga3",
"transactions": [],
"id": "00e6ee7889523875a28284effecdd1199cc960adb14c14c36cd1bd52afed6824",
"block_num": 15134328,
"ref_block_prefix": 4018438818
}
获取到
timestamp: 2022-04-27T09:08:08
使用EOSIO提供的签名工具实现签名:启动keosd后,才会提供签名服务

curl -X POST POST 'http://192.168.1.46:8800/v1/wallet/sign_transaction' \
-d '[
{
"expiration": "2022-04-27T10:08:08",
"ref_block_num": 61048,
"ref_block_prefix": 4018438818,
"max_net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "helailiang14",
"name": "hi",
"authorization": [
{
"actor": "helailiang14",
"permission": "active"
}
],
"data": "00408a97721aa36a"
}
],
"transaction_extensions": [],
"signatures": [],
"context_free_data": []
},
[
"EOS6F6PRkSaPyijTDBYskFbsxpGz53JMTFbEhua94fQEyf7pAMc7Y"
],
"9b4c6015f8b73b2d7ee3ebd92d249a1aba06a614e9990dcf54f7cf2e3d5172e1"
]'
------------------
return :
{
"expiration":"2022-04-27T10:08:08",
"ref_block_num":61048,
"ref_block_prefix":4018438818,
"max_net_usage_words":0,
"max_cpu_usage_ms":0,
"delay_sec":0,
"context_free_actions":[
],
"actions":[
{
"account":"helailiang14",
"name":"hi",
"authorization":[
{
"actor":"helailiang14",
"permission":"active"
}
],
"data":"00408a97721aa36a"
}
],
"transaction_extensions":[
],
"signatures":[
"SIG_K1_K2AzV2Pk4SP3PQhcdQ1bYgGZgr7PUUcJkAGowvncFV1ngrZufeCQpveAUBRYvNA5uyxFk2hKiot3Mu7FCW5rqqeoU5SVTo"
],
"context_free_data":[
]
}
•
expiration过期时间。这里将timestamp加上了60分钟.•
ref_block_num: 由第二步返回的head_block_id 16进制的 然后字段截取前八位低4位 转10进制, ee78 ==> 61048•
ref_block_prefix:由第二步返回的head_block_id 16进制的 然后字段截取16到24 然后两个字节反转(a28284ef反转为ef8482a2) 转10进制, ef8482a2 ==> 4018438818•
account合约名称,即部署合约的账户•
name调用的合约方法。•
actor调用者。签名者•
data:第一步生成的bin字符串•
permission使用的权限类型•
EOS6F6PRkSaPyijTDBYskFbsxpGz53JMTFbEhua94fQEyf7pAMc7Y:签署此交易的公钥。实际上是由钱包中对应的私钥来签•
9b4c6015f8b73b2d7ee3ebd92d249a1aba06a614e9990dcf54f7cf2e3d5172e1: 是第二步获取的chain_id•
signatures: 签名结果SIG_K1_K2AzV2Pk4SP3PQhcdQ1bYgGZgr7PUUcJkAGowvncFV1ngrZufeCQpveAUBRYvNA5uyxFk2hKiot3Mu7FCW5rqqeoU5SVTo
使用 cleos convert pack_transaction将交易报文转换成 packed 格式
cleos convert pack_transaction '{
"expiration":"2022-04-27T10:08:08",
"ref_block_num":61048,
"ref_block_prefix":4018438818,
"max_net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [
{
"account": "helailiang14",
"name": "hi",
"authorization": [
{
"actor": "helailiang14",
"permission": "active"
}
],
"data": "00408a97721aa36a"
}
],
"transaction_extensions": []
}'
------------------
return:
{
"signatures": [],
"compression": "none",
"packed_context_free_data": "",
"packed_trx": "0816696278eea28284ef000000000140029bc64567a26a000000000000806b0140029bc64567a26a00000000a8ed32320800408a97721aa36a00"
}
packed_trx: 为交易报文的 packed 格式
curl -X POST 'https://opbningxia.bsngate.com:18602/api/{您的开放联盟链项目ID/rpc/v1/chain/push_transaction' \
-d '{
"signatures": [
"SIG_K1_K2AzV2Pk4SP3PQhcdQ1bYgGZgr7PUUcJkAGowvncFV1ngrZufeCQpveAUBRYvNA5uyxFk2hKiot3Mu7FCW5rqqeoU5SVTo"
],
"compression": "none",
"packed_context_free_data": "",
"packed_trx": "0816696278eea28284ef000000000140029bc64567a26a000000000000806b0140029bc64567a26a00000000a8ed32320800408a97721aa36a00"
}'
------------------
return:
{
"transaction_id":"a69d03f6b1bab4bd8908124eef5e59d3e47df4063e697a07487308cde63a9f79",
"processed":{
"id":"a69d03f6b1bab4bd8908124eef5e59d3e47df4063e697a07487308cde63a9f79",
"block_num":15136664,
"block_time":"2022-04-27T09:27:36.500",
"producer_block_id":null,
"receipt":{
"status":"executed",
"cpu_usage_us":272,
"net_usage_words":13
},
"elapsed":272,
"net_usage":104,
"scheduled":false,
"action_traces":[
{
"action_ordinal":1,
"creator_action_ordinal":0,
"closest_unnotified_ancestor_action_ordinal":0,
"receipt":{
"receiver":"helailiang14",
"act_digest":"31ff1ecb2b0b0c89911b74c7930f08ecfefbd24ba59ef30a905d44068d2d8910",
"global_sequence":15199315,
"recv_sequence":2,
"auth_sequence":[
[
"helailiang14",
4
]
],
"code_sequence":1,
"abi_sequence":1
},
"receiver":"helailiang14",
"act":{
"account":"helailiang14",
"name":"hi",
"authorization":[
{
"actor":"helailiang14",
"permission":"active"
}
],
"data":{
"user":"helloworld"
},
"hex_data":"00408a97721aa36a"
},
"context_free":false,
"elapsed":63,
"console":"Hello, helloworldget_self,7683817463629939264",
"trx_id":"a69d03f6b1bab4bd8908124eef5e59d3e47df4063e697a07487308cde63a9f79",
"block_num":15136664,
"block_time":"2022-04-27T09:27:36.500",
"producer_block_id":null,
"account_ram_deltas":[
],
"account_disk_deltas":[
],
"except":null,
"error_code":null,
"return_value_hex_data":"",
"inline_traces":[
]
}
],
"account_ram_delta":null,
"except":null,
"error_code":null
}
}
signatures: 为第4步签名的结果
packed_trx: 为第5步的报文转换结果
curl -X POST 'https://opbningxia.bsngate.com:18602/api/{您的开放联盟链项目ID/rpc/v1/chain/get_table_rows' \
-d'{
"code": "helailiang14",
"table": "friends",
"scope": "helailiang14",
"json": true
}'
------------------
return:
{
"rows":[
{
"friend_name":"helloworld",
"visit_time":1651051656
}
],
"more":false,
"next_key":"",
"next_key_bytes":""
}
数据[helloworld]已经写到table中
[1] BSN: https://bsnbase.com/[2] bsn: https://bsnbase.com/p/home/Openalliance/projectManagement
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为