3月15日,mercatox 遭受(hard_fail)攻击,黑客获利数千 EOS,约合数万人民币。
3月11日,EOS DApp nkpaymentcap 被攻击,黑客成功获利 5 万 EOS,约合人民币100多万元。经分析发现,攻击者采用假转账通知攻击获取大量合约代币,又将代币通过 DApp 合约兑换成真 EOS 进行套现。
3月10日,EOS 竞猜类游戏 Vegas Town 被连续(hard_fail)攻击,黑客获利数千枚 EOS 。
3月9日,EOS 竞猜类游戏 Gamble EOS 遭受假转账攻击,黑客成功获利数千 EOS。
……
仅仅三月份,EOS 区块链生态中利用交易验证漏洞进行的攻击,造成的损失就达上千万人民币。
人们不了解交易验证。
但是黑客了解。
因为在支付领域,这很重要。在区块链领域,无论做 DApp,还是接入数字货币支付,交易验证都是重要部分。
交易验证,说起来也简单:就是如何确认一笔金额已经到账。
说起来也不简单,因为会有无数黑客试图让并未成功的交易通过验证,从而空手套白狼。
比如最近针对 EOS 交易的 hard_fail 状态攻击,就是这样一种黑客攻击。
hard_fail 状态攻击
在 EOS 区块链上,每笔交易(transaction)都有一个状态(status)参数,只有当这个参数为 executed(已执行),才说明交易成功。但是因为一般来说,失败的交易都不会提交到链上,所以,一些不谨慎的交易所、DApp 甚至没有验证这个参数。
这才有了最近的 hard_fail 状态攻击。其实这个攻击手法的背后原理远比它的名字简单,就是黑客发起了注定失败的、但是又能上链的交易,专门攻击那些只要交易上链就视作交易成功的平台。
不试不知道,一试吓一跳,真的有很多平台没验证这个参数……
Well,那么,EOS 交易验证,到底需要验证些啥?
EOS 交易验证涉及的参数
EOS 交易验证,需要验证:
1.交易 excuted,验证 transaction status 参数为 executed,验证这个参数可以有效避免 hard_fail 状态攻击;
说简单点,这一步是验证交易是否正常执行。
2.不可逆,即交易所在区块号低于当前最新不可逆块号,需要获得 transaction 所在区块的区块号以及主网最新不可逆块的区块号(不可逆区块高度),判断block_num<last_irreversible_block_num;
说简单点,这一步是验证交易是否已经同步。
区块链是分布式账本,交易提交上去后,需要大多数节点记录了这笔交易,才能认为交易已上链并不可篡改。不然的话,比如如果只有一个节点有这笔交易,则只要这个节点的交易信息一改,这笔交易就变了。
3.合约账户和货币符号;
合约账户就是货币的智能合约账户。EOS 区块链转账都是基于智能合约的,比如 EOS 币的智能合约账户就是 eosio.token,每一次进行 EOS 币转账,都会调用这个合约。
假币攻击就是因为交易所没验证合约账户。
4.交易(action)类型为 transfer;
EOS 区块链上的交互都是以 transaction 进行,无一例外。所以,不是每一笔 transaction 都包含转账。EOS 一笔 transaction 可以有多个 action,只有类型为 transfer(转账)的 action,才是转账,才是需要做交易验证的。
5.From 和 to,即转入转出的账户需要再次确认。
交易(Transaction)同时满足这些条件,才能判断为交易成功,并执行下一步程序。而且,如果是通过公共 API 或 API 服务商提供的数据进行交易验证,需要使用不同服务提供商的 API 至少进行二次确认,以防止 API 信息出错导致问题。
那么问题又来了,如何通过公共 API 进行交易验证呢?
通过 EOSPark 的 API 获取交易进行交易验证
让我们以 EOSPark 的 API 服务做个基本示例。
EOSPark 本身是一个主流 EOS 区块浏览器,但他们也面向开发者提供 API、代码一致性校验、合约安全(SEC)、合约语义化等服务。
EOSPark API 官网:https://eospark.com/openapi
EOSPark API 官方文档:https://developer.eospark.com/api-doc/zh/https/
EOSPark API 服务思维导图:
EOSPark API 有四种查询 EOS 交易的方式。
根据账户查询:
HTTPS API get_account_related_trx_info
Websocket API subscribe_account
根据 txID 查询:
HTTPS API get_transaction_detail_info
HTTPS API get_transaction_action_info
这里以 HTTPS API get_account_related_trx_info 为例,这个接口能查询对应账户的进出交易,无论是收到的转账还是发起的转账,都可以一起查到:
get_account_related_trx_info 的基本查询语句如下:
https://api.eospark.com/api?module=account&action=get_account_related_trx_info&apikey={这里放你的 API KEY}&account={这里放 EOS 账户名}&page=1&size=10
语句填好参数后可以直接在普通浏览器中打开查询信息,不过当然,我们更习惯用 IDE。
Node.js 代码示例:
const fetch = require('node-fetch');
fetch('https://api.eospark.com/api?module=account&' +
'action=get_account_related_trx_info&' +
'apikey={这里放你的 API KEY}&' +
'account={这里放 EOS 账户名}&page=1&size=10', {
method: 'get',
}).then(response => response.json()
.then(data => console.log(data.data)));
返回 JSON 示例:
{
"errno": 0,
"errmsg": "",
"data": {
"trace_count": 205041,
"last_irreversible_block_num": 48019699,
"trace_list": [
{
"data_md5": "f75f4f16392dc7b27dc31c4e9713f4c2",
"trx_id": "4b173d9a6484a2e9eded03fa490efa2a374dde10308a6351a109fa9af9d19c83",
"timestamp": "2019-03-17T03:33:49.000",
"receiver": "eosio.vpay",
"sender": "eosio",
"code": "eosio.token",
"quantity": "344.5620",
"memo": "fund per-vote bucket",
"symbol": "EOS",
"status": "executed",
"block_num": 48020026
},
{
"data_md5": "ecf5c5ae13ce70f1a158509d1173c9b6",
"trx_id": "4b173d9a6484a2e9eded03fa490efa2a374dde10308a6351a109fa9af9d19c83",
"timestamp": "2019-03-17T03:33:49.000",
"receiver": "eosio.bpay",
"sender": "eosio",
"code": "eosio.token",
"quantity": "114.8539",
"memo": "fund per-block bucket",
"symbol": "EOS",
"status": "executed",
"block_num": 48020026
}
]
}
}
可以看到在 get_account_related_trx_info 返回的信息中,本来就包括交易所在区块号(block_num)和不可逆区块高度(last_irreversible_block_num)。同时status、code(合约账户)、symbol、From(这里是 sender)和 to(这里是 receiver)这些参数也一应俱全。(交易类型因为这个接口本身就是返回转账交易,所以这里可以不作验证。)
也就是说,使用这个接口,查询一次就可以直接验证获取到的交易。
那么问题又来了,也就是说,有些时候查询的信息不能直接进行交易验证?
很不幸,是滴。
获取不可逆区块高度和交易状态的补充方法
有些接口返回的交易信息不包含交易验证所需所有参数,需要再另外获取。最常见的就是不可逆区块高度的缺失,还有一些直接查询 actions 的接口有交易状态参数缺失。
不过处理起来都很简单。
同样以 EOSPark API 服务为例:
获取不可逆区块高度,使用 RPC 接口 get_info 就好,这是一个获取 EOS 主网基本信息的接口:
Node.js 代码示例:
const fetch = require('node-fetch');
fetch("https://api.eospark.com/v1/chain/get_info?apikey=/*这里放你的API KEY*/")
.then(response => response.json()
.then(data => console.log(data)));
返回 JSON 示例:
{
"server_version": "0f6695cb",
"chain_id": "687fa513e18843ad3e820744f4ffcf93b1354036d80737db8dc444fe4b15ad17",
"head_block_num": 20583056,
"last_irreversible_block_num": 20583039,
"last_irreversible_block_id": "013a127fab9a79403a20b55914cdc7e1ac136618387325ad3c1914d27528a1f1",
"head_block_id": "013a129048f4486ce8a5ac8380870a8ce1bcbd4ff45b40fd0792503dc44c427d",
"head_block_time": "2019-01-25T16:39:38.000",
"head_block_producer": "blkproducer1",
"virtual_block_cpu_limit": 200000000,
"virtual_block_net_limit": 1048576000,
"block_cpu_limit": 199900,
"block_net_limit": 1048576,
"server_version_string": "v1.3.0"
}
获取状态参数,可以使用 HTTPS 接口 get_transaction_detail_info,这是一个根据 txID 查询交易(transaction)详情的接口:
查询语句:
https://api.eospark.com/api?module=transaction&action=get_transaction_detail_info&apikey={这里放你的API KEY}&trx_id={这里放查询的交易ID}
返回 JSON 示例:
{
"errno": 0,
"errmsg": "Success",
"data": {
"block_num": 48400644,
"cpu_usage_us": 934,
"eospark_trx_type": "ordinary",
"net_usage_words": 13,
"status": "executed",
"timestamp": "2019-03-19T08:29:01.500",
"trx": {
"compression": "none",
"context_free_data": [],
"id": "b887096b4dddfc103311789dbbff9c0435dcdf8811fab6599f32acf6b833e5d8",
"packed_context_free_data": "",
"packed_trx": "6ba8905cbc87d0fb0c5000000000010000000000ea305580d3355c5de94c440150cfa54b4d8aa96200000000a8ed32320850cfa54b4d8aa96200",
"signatures": [
"SIG_K1_KZfPeMSKyhp1BAM93b2EkGQaJCeLPfgb56qggfLKupsqBLy1bn9ZexZ6gy4C4sghKXBthTtSLVwbzrRRQzHSwfSy4yVtXk"
],
"transaction": {
"actions": [
{
"account": "eosio",
"authorization": [
{
"actor": "geosoneforbp",
"permission": "active"
}
],
"data": {
"owner": "geosoneforbp"
},
"hex_data": "50cfa54b4d8aa962",
"name": "claimrewards"
}
],
"context_free_actions": [],
"delay_sec": 0,
"expiration": "2019-03-19T08:29:31",
"max_cpu_usage_ms": 0,
"max_net_usage_words": 0,
"ref_block_num": 34748,
"ref_block_prefix": 1343028176,
"transaction_extensions": []
}
}
}
}
至此,我们就简单说完了 EOS 区块链交易验证的一个基本思路。当然,具体情况具体分析,具体开发时验证的方式不一定按这样来。不过条条大路通罗马,思路是一样的,验证的参数也基本都是这些。
欢迎补充,欢迎拍砖!
原创内容,欢迎转载,但转载请标明出处。
我们有一个区块链知识星球,做区块链前沿资料的归纳整理以方便大家检索查询使用,也是国内顶尖区块链技术社区,欢迎感兴趣的朋友加入。如果你对上面内容有疑问,也可以加入知识星球提问我: