### 获取当前区块
```
{"method":"omni_getinfo"}
```
### 获取地址余额
```
$this->bitcoin->omni_getbalance("n4cjbUjEL4hKBfbyK96W5CWWvsQWxok6wG",31)
```
### 获取所有钱包地址
```
$this->bitcoin->getaddressesbyaccount('')
```
### 根据区块高度获取区块hash
```
$this->bitcoin->getblockhash(628087);
```
### 根据区块hash查询区块信息
```
$this->bitcoin->getblock($hash);
```
### 获取测试币
[水龙头网站,需要翻墙 https://coinfaucet.eu/en/btc-testnet/](https://coinfaucet.eu/en/btc-testnet/)
###
```
$ ./omnicore-cli getinfo
{
"version": 130200,
"protocolversion": 70015,
"walletversion": 130000,
"balance": 4.85909131, //钱包比特币总余额
"blocks": 1413349,
"timeoffset": 0,
"connections": 0,
"proxy": "",
"difficulty": 56234572.68927951,
"testnet": true, //是否是测试网
"keypoololdest": 1535371434,
"keypoolsize": 100,
"paytxfee": 0.00010000,
"relayfee": 0.00001000,
"errors": ""
}
```
```
$ ./omnicore-cli omni_getinfo
{
"omnicoreversion_int": 30001000,
"omnicoreversion": "0.3.1", //omni core 版本
"mastercoreversion": "0.3.1",
"bitcoincoreversion": "0.13.2", //基于比特币版本
"block": 1413349,//区块要举高高
"blocktime": 1536841368,
"blocktransactions": 0,
"totaltrades": 15601,
"totaltransactions": 43731,
"alerts": [
]
}
```
### 四.中心化钱包模式
中心化钱包的本质是代替用户托管资产,钱包保存了所有地址的私钥,对上面的令牌有完全的使用权。对于用户而言,对资产的流动有知情权,但并没有实际控制权。一个完整的中心化钱包可以分为两层,记账层和区块底层,至少需要集成四个基本的业务功能:
1.地址生成
USDT地址即比特币区块链上的地址,借助比特币内核`getnewaddress`可以从同一个种子推导出无数个地址,生成地址的过程类似与把一枚硬币连续抛255次。服务端需要在自己的用户系统中为每个用户生成不同的地址,用户的看到的资产实际上服务端的记账状态,并非真实资产。
2.扫描充值事务
钱包一旦启动,会开启同步区块的守护进程,服务端不需要进行手动的区块同步操作。但服务端需要定期的扫描区块以发现并确认充值事务。通过`omni_listtransactions`可以查询当前钱包内的事务列表,根据业务需要,可以定时每小时全量扫描一次,每次最多返回100条事务。遍历每条事务,如果事务已经验证且确认数大于等于6,那么被认为是一条有效的充值记录。然后判断记账层是否已经记录了该事务,如果没有记录则写入充值记录表,同时查询绑定该地址的用户,在余额表中该用户的可用余额加上充值金额。如果已经写入了那么跳过本次事务。单次的事务处理流程如下:
![](https://upload-images.jianshu.io/upload_images/1795529-ecde9c85e9038edc.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/284/format/webp)
这是最简易的模式,根据业务情景可以适当调整扫描周期和最大事务数。
3.转移充值余额
用户充值后USDT保留在用户绑定的区块地址中,需要及时的转移到中央地址中去。中央地址即保存整个平台资产的一个或者多个地址。可以使用与普通用户相同的“种子”,也可以单独使用一个钱包,或者直接使用冷钱包离线保存。在保证安全和效率的情况下,越少的转账次数越好,可以最大限度的节省手续费。获取钱包地址USDT余额列表有多种方式 ,从v0.3.1开始可以使用`omni_getwalletaddressbalances`直接返回所有每个地址的所有令牌列表。一旦检测id=31的令牌余额不为0,且大于最小额度(一般大于预估的手续费)则使用`omni_funded_sendall`转移所有的USDT到指定的中央钱包。
但需要注意的是,在Omnicore上从发送者转账转移指定id的令牌到接受者,当交易被创建且被发送成功后,交易验证需要一定时间,发送者的令牌余额不会立即变化。所以如果扫描余额的时间周期太短,会造成一个地址上的余额被多次转移,虽然只会有一次成功但会重复消耗手续费,所以建议2-6hour扫描一次本地钱包余额列表。
4.提现事件
提现是指用户把实际资产从平台钱包中转移出去,只要判断是本人操作而且提现金额小于可用额度就被认为是有效的提现请求。根据提现地址的不同有两种情况:
当提现地址是钱包内的地址时(即平台内的另外一个用户)属于内部转账。这种方式并不需在从中央钱包发送USDT到指定地址,只需要在记账层进行依次对两个账户上的USDT余额进行修改,几乎没有时间延迟。
当提现地址是不是钱包内的地址时(非平台用户)属于外部转账。这种方式需要操作区块链,不会马上进行确认,根据手续费设定和当前比特币主网拥堵状况可能需要几小时到一天的确认时间。
对于外部转账,如果用户绑定的区块地址上还存在余额,那么优先使用该地址进行转账,其次选择中央钱包进行转账。可以使用`omni_funded_send`来进行创建USDT交易并广播,交易发送成功后会生成的事务哈希。根据事务哈希,通过`omni_gettransaction`可以进行提现进度的跟踪。
![](https://upload-images.jianshu.io/upload_images/1795529-b042408b995bb7bd.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/438/format/webp)
### 五.补充
### 1.omni\_funded\_send -212错误
从v0.3.1开始omnicore提供了新的转账api[omni\_funded\_send](https://github.com/OmniLayer/omnicore/blob/master/src/omnicore/doc/rpc-api.md#omni_funded_send)来支持USDT的转账操作。与之前的方式不同在于,omni\_funded\_send支持从第三方支付手续费。但在实际操作,明明拥有充足的btc用于支付手续费,也有充分的usdt用于转账,还是会频繁出现编号**\-212**错误:
~~~
$ ./omnicore-cli omni_funded_send "mrAVAPxdQEZxFkunh56skB6sgJa6vrfrpo" "msJ2h47ZrxFJjksVvPy8ik4h2HFfa9W1zV" 31 "100.01" "mpaumxor659PhoJhXp1VCVHVwbFCZSRmuf"error code: -212error message:Error choosing inputs for the send transaction
~~~
[issues/760#](https://github.com/OmniLayer/omnicore/issues/760#issuecomment-434745561)详细讨论了这个问题。
USDT的转账本质上属于比特币的一种特殊交易(**OP\_RETUTRN**),需要一部分BTC来支付矿工费用。同时,根据omnilayer协议,还需要微量的比特币用于标记omni事务的接受者,这部分btc无法从手续费地址扣除,只能从发送地址扣除。因此,当发送地址缺乏btc(也就是UTXO)时,会出现 “*Error choosing inputs for the send transaction*”的错误。经过测试,默认情况下用于标记omni事务的BTC为0.0000546。因此解决这个问题的唯一方法是向发送地址中转入>0.0000546的BTC。
但需要注意的是,发送地址上的某个UTXO将全部消耗掉,不会有找零。例如A地址向B地址转移USDT,A地址中拥有两个UTXO,分别为0.0001和0.0002,那么转账成功后,0.0001的UTXO将会被消耗掉,只剩下额度为0.0002的UTXO。
但在交易所模式下,这种错误几乎不会发生。因为在中心化钱包的模式下,充值的过程会自动携带了微量的btc,这一部分比特币足够用于标记omni事务,用于下一次转账,但也仅限一次。因此,只要不使用同一个地址发送两次USDT,这种错误几乎不会出现。
### 2\. 钱包安全
数字资产不同于一般性的资产,一旦被盗或丢失,几乎无法找回,也无法进行数据回滚。对于中心化的钱包而言,用户本身对资产没有直接控制权,所有的资产安全性全部取决于中心化钱包的安全策略,因此非常有必要对安全性进行设计。以下方式仅供参考。
2.1 限制访问权限
omnicore基于bitcore,自带有加密访问权限设置,可以通过bitcoin.conf文件可以进行配置。
~~~
rpcuser=你的rpc用户名rpcpassword=你的rpc密码rpcallowip=127.0.0.1 rpcport=8332
~~~
其中`rpcuser`和`rpcpassword`分别配置访问用户和访问密码,`rpcallowip`和`rpcport`分别配置可访问的网络地址。测试环境下,该地址可以为0.0.0.0/0 为全地址访问。正式环境下,需要配置为制定的访问服务器地址或者本地地址。值得注意的是,这里的安全原理是限定钱包访问对象,一旦密码泄漏或服务器被黑,那么整个钱包相当于裸奔。
2.2 离线生成地址
资产的唯一凭证是私钥,因此私钥的保存非常重要。一般情况下,根据业务需求,服务端需要给每个用户分配一个地址。这里的建议是不应该分配地址的时候“即使”的去调用`getnewaddresss`,而是通过离线的方式批量生产大量地址,然后连同地址、私钥保存到数据库,同时导出地址列表给服务端进行使用。这样服务端需要分配地址时,只需要从数据库读取即可,而不需要与钱包进行交互。
> 为了私钥的安全性,保存到数据库时可以进行二次加密。
2.3 钱包文件备份
钱包文件的备份可以直接使用bitcore的导出数据方式`dumpwallet`,每天定期保存到文件,例如:
~~~
/*** * 导出钱包数据以人类可读的方式 * @param: [] * @return: java.lang.String **/ public String dumpWallet() { System.currentTimeMillis(); String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); String file = String.format("wallet-%s.txt", time); http.engine("dumpwallet", file); return file; }
~~~
一旦需要恢复,使用`importwallet`即可,例如:
~~~
/*** * 钱包数据导入 * @param: [] * @return: void * https://bitcoin.org/en/developer-reference#importwallet *对于影响新添加的密钥的事务,调用可能需要重新扫描整个链,可能需要几分钟。 **/ public void importWallet(String fielName) { http.engine("importwallet", fielName); }
~~~
2.4 冷钱包
冷钱包即不联网的钱包。对于交易所模式下而言,钱包的作业其实就相当于银行的存款系统,充值的过程相当于把金额全部汇总到中央地址。因此可以使用一台不联网的钱包生成中央地址若干,将私钥保存到U盘或其他加密硬件中,充值金额将汇集到冷钱包中保存。一旦需要提现,可以根据金额大小。小额提现可以通过热钱包直接发消息转账;大额可以使用需要使用指定U盘将私钥或钱包文件导入到钱包中,然后联网转账。操作完成后断网,重新保存硬件即可。
> 这一过程需要注意,冷钱包因为无法联网,自然也无法知道余额。那么可以设置观察钱包对中央钱包地址进行余额观察,以确保充值到账。在实际业务中,提现过程可能还需要人工进行审核。
2.5 钱包与服务端隔离
一般情况下,钱包与服务端会部署在同一台服务器上。这种方式有两个缺陷,第一是钱包需要的存储空间庞大,但需要的网络带宽很低,与业务相关的服务器部署子啊同一服务器上将造成资源的浪费。第二是这种方式有一些安全隐患。所以建议钱包单独部署到一台服务器上,如果业务允许,还可以同时备份到多台冗余服务器上。钱包与服务端的通讯可以使用 “消息队列”。
> 注意使用**Queue**,而不是**Topics**,同一时间内,只有一个消费者和一个生产者。
![](https://upload-images.jianshu.io/upload_images/1795529-da857a39d99d32a5.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/768/format/webp)