OmniTool.Java開發包适用于為Java應用快速增加對Omni/USDT數字資産的支援能力,即支援使用自有Omni節點的應用場景,也支援基于第三方API服務和離線裸交易的輕量級部署場景。官方下載下傳位址: http://sc.hubwiz.com/codebag/omni-java-lib/ 。
1、開發包概述
OmniTool.Java開發包主要包含以下特性:
- 完善的Bitcoin/Omni Layer RPC API封裝
- 支援利用自有節點或第三方服務擷取指定位址的比特币utxo集合
- 支援離線生成Omni代币或比特币轉賬裸交易
- 支援利用自有節點或第三方服務廣播裸交易
OmniTool.Java支援本地部署的Omnicored節點,也支援第三方服務提供的開放API,要增加新的第三方服務也非常簡單,隻需要參考代碼實作如下接口:
- IUtxoCollector:Utxo采集器
- IBroadcaster:裸交易廣播器
OmniTool.Java軟體包目前版本1.0.0,主要類/接口及關系如下圖所示:

OmniTool.Java軟體包主要代碼檔案清單如下:
代碼檔案 | 說明 |
---|---|
omnitool/build.gradle | OmniTool.Java開發庫項目建構檔案 |
omnitool/src/main/java/omnitool/RpcClient.java | OmniLayer/Bitcoin RPC API用戶端實作類 |
omnitool/src/main/java/omnitool/RpcRequest.java | RPC API請求類 |
omnitool/src/main/java/omnitool/RpcResponse.java | RPC API響應類 |
omnitool/src/main/java/omnitool/ToolKit.java | 開發庫入口類 |
omnitool/src/main/java/omnitool/KeyStore.java | 密鑰存儲接口 |
omnitool/src/main/java/omnitool/UtxoCollector.java | UTXO采集器接口 |
omnitool/src/main/java/omnitool/UtxoSelector.java | UTXO選擇器接口 |
omnitool/src/main/java/omnitool/Broadcaster.java | 裸交易廣播器接口 |
omnitool/src/main/java/omnitool/KeyStoreMemory.java | 記憶體密鑰存儲庫實作類 |
omnitool/src/main/java/omnitool/KeyStoreSql.java | SQL資料庫密鑰存儲庫實作類 |
omnitool/src/main/java/omnitool/UtxoCollectorRpc.java | 基于RPC API的UTXO采集器實作類 |
omnitool/src/main/java/omnitool/UtxoCollectorSmartbit.java | 基于雲端第三方API的UTXO采集器實作類 |
omnitool/src/main/java/omnitool/UtxoSelectorDefault.java | UTXO選擇器接口的預設政策實作類 |
omnitool/src/main/java/omnitool/BroadcasterRpc.java | 基于RPC API的裸交易廣播器實作類 |
omnitool/src/main/java/omnitool/BroadcasterSmartbit.java | 基于雲端第三方API的裸交易廣播器實作類 |
omnitool/src/main/java/omnitool/UtxoBag.java | UTXO集合類 |
omnitool/src/main/java/omnitool/Utxo.java | UTXO模型類 |
omnitool/src/main/java/omnitool/KeyStoreItem.java | 密鑰存儲記錄類 |
omnitool/src/main/java/omnitool/Utils.java | 輔助工具類 |
demo/build.gradle | OmniTool.Java示範應用項目檔案 |
demo/src/App.java | 示範應用入口代碼 |
demo/src/RpcClientDemo.java | Omni/Btc RPC API用戶端使用方法示範代碼 |
demo/src/KeyStoreMemoryDemo.java | 記憶體密鑰庫使用方法示範代碼 |
demo/src/KeyStoreSqlDemo.java | Sql密鑰庫使用方法示範代碼 |
demo/src/BtcTxRpcDemo.java | 比特币轉賬交易示範代碼,基于RPC API |
demo/src/BtcTxCloudDemo.java | 比特币轉賬交易示範代碼,基于第三方服務API |
demo/src/OmniTxRpcDemo.java | Omni/USDT代币轉賬交易示範代碼,基于RPC API |
demo/src/OmniTxCloudDemo.java | Omni/USDT代币轉賬交易示範代碼,基于第三方服務API |
demo/resources/log4j.properties | 示範項目日志配置檔案 |
build.gradle | 根項目建構檔案 |
settings.gradle | 根項目配置檔案 |
2、RpcClient類使用說明
RpcClient類封裝了比特币以及Omni Layer的RPC API接口協定。建立RpcClient對象時,需要傳入包含有效身份資訊的節點RPC URL。例如,假設安裝在本機的omnicored節點軟體接入主網,其配置如下:
- rpcuser:user
- rpcpassword:123456
- rpcport:8332
那麼可以使用如下的代碼來執行個體化RpcClient:
import omnitool.RpcClient;
RpcClient client = new RpcClient(
"http://user:[email protected]:8332" /*節點RPC API的URL*/
);
使用RpcClient的
call()
方法可以調用Bitcoin層和omni層的所有RPC API。例如,使用
listunspent調用來擷取本地節點中指定位址的utxo:
//import java.util.Map;
Map[] unspents = client.call(
Map[].class, /*傳回結果類型*/
"listunspent", /*RPC API名稱*/
6, /*最小确認數*/
999999, /*最大确認數*/
new String[]{"mgnucj8nYqdrPFh2JfZSB1NmUThUGnmsqe"} /*位址清單*/
);
for(Object unspent: unspents) {
System.out.printf("txid: %s\n",(String)unspent.get("txid"));
}
call()
方法的傳回結果對應于RPC API的JSON響應中的
result
字段,其類型取決于我們傳入的第一個參數。
call()
方法的第一個參數聲明方法傳回的結果類型的Class對象,方法會将RPC API的JSON響應中的result字段解碼為該參數指定的類型。通常我們都可以使用
Map
或
Map[]
來對應JSON響應中的result字段的内容,例如上例所示。這種處理方式可以适應不斷變化中的RPC API,但從結果中提取資料時,不得不小心處理類型轉換的問題。
call()
方法的第二個參數聲明要調用的RPC API方法名,從第三個參數開始的其他參數則表示所指定的RPC API方法的參數。
2.1 定義自己的結果類
可選地,也可以自己定義一個類來簡化從
call()
方法的傳回結果中提取資料的難度。例如,對于上面的示例,我們可以定義一個
Unspent
類來描述
listupsent
響應中的JSON對象(不需要定義所有的字段,按自己的需求選擇):
class Unspent{
public String txid;
public long vout;
public String account;
public String scriptPubKey;
public double amount;
public long confirmations;
}
那麼我們可以按如下的方式調用RpcClient:
Unspent[] unspents = client.call(
Unspent[].class, /*傳回結果類型*/
"listunspent", /*RPC API方法名*/
6, /*最小确認數*/
999999, /*最大确認數*/
new String[]{"mgnucj8nYqdrPFh2JfZSB1NmUThUGnmsqe"} /*位址清單*/
);
for(Object unspent: unspents) {
System.out.printf("txid: %s\n",unspent.txid);
System.out.printf("vout: %d\n",unspent.vout);
System.out.printf("amount:%f\n",unspent.amount);
}
顯然,定義自己的結果類可以将RPC API的JSON響應直接反序列化到指定的類型,對于操作複雜響應結果會很有幫助。但比特币和Omni層的RPC API不僅在動态演化中,而且有些JSON響應的結構本身就是動态的,是以往往還需要結合使用前面更通用的
Map
Map[]
類型。
2.2 Omni層RPC API
OmniCore節點在比特币原有的RPC接口之外,擴充了額外的接口用來操作Omni層的資料,這些擴充的RPC接口采用
omni_
字首以區隔于Bitcoin的原有RPC接口。
例如,擷取某個位址的USDT代币餘額需要使用Omni層的
omni_getbalance調用,下面的代碼擷取位址
1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P
的USDT(資産ID:31)餘額:
Map[] balances = client.call(
Map[].class, /*傳回結果類型*/
"omni_getbalance" /*Omni RPC API方法名*/
"1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P", /*賬戶位址*/
31 /*Omni資産ID:USDT=31*/
);
for(Map b:balances){
System.out.printf("balance: %s\n",(String)b.balance);
}
類似的,可以使用
omni_send調用來執行簡單的USDT轉賬。例如,下面的代碼從位址
3M9qvHKtgARhqcMtM5cRT9VaiDJ5PSfQGY
向位址
37FaKponF7zqoMLUjEiko25pDiuVH5YLEa
轉入100.0個USDT代币:
String txid = client.call(
String.class, /*傳回結果類型*/
"omni_send", /*RPC API方法名*/
"3M9qvHKtgARhqcMtM5cRT9VaiDJ5PSfQGY", /*代币轉出位址*/
"37FaKponF7zqoMLUjEiko25pDiuVH5YLEa", /*代币轉入位址*/
31, /*代币ID:USDT*/
"100.00" /*轉移的代币數量*/
);
System.out.printf("tx hash => %s\n",txid);
開發包中的
demo/RpcClientDemo.java
示例代碼使用RpcClient完整示範了在Omni層的代币發行與轉賬功能,如果你計劃搭建自己的Omni Core節點,相信這個示例會有很大幫助。
3、ToolKit類使用說明
如果不願意搭建自己的Omni Core節點,而是希望基于第三方API為自己的Java應用增加對Omni Layer/USDT的支援,那麼最簡單的方法是使用離線交易的入口類ToolKit。
ToolKit類的主要作用是建立并廣播Omni代币或比特币轉賬裸交易,它的基本使用步驟如下:
- 建立一個ToolKit執行個體
- 使用
方法将必要的私鑰加入該ToolKit執行個體,例如轉出位址的私鑰,因為ToolKit需要利用私鑰對裸交易進行簽名AddKey()
-
方法生成并廣播Omni代币轉賬裸交易,或者使用SendOmnicoin()
方法生成并廣播比特币轉賬裸交易SendBitcoin()
3.1 Omni/USDT代币轉賬
使用ToolKit實作的Omni/USDT代币轉賬示例代碼如下,說明見注釋:
import omnitool.*;
String network = "main";
ToolKit kit = new ToolKit(
network, /*接入的網絡*/
new KeyStoreMemory(), /*使用記憶體密鑰庫*/
new UtxoCollectorSmartbit(network), /*使用雲端Utxo采集器*/
new UtxoSelectorDefault(), /*使用預設政策Utxo選擇器*/
new BroadcasterSmartbit(network) /*使用雲端裸交易廣播器*/
);
String privHex = "4aec8e45106....00d5c5a05b"; /*私鑰:16進制字元串*/
kit.addKey(privHex); /*将私鑰加入ToolKit*/
String from = kit.getKeyStore()
.getByKey(privHex).address; /*私鑰對應的位址作為發起賬号*/
String to = "1GxX5tQR1C.....x2zbdj4mMuDcWR"; /*接收位址*/
String txid = kit.sendOmnicoin(
from, /*發送方位址,私鑰必須已經加入錢包*/
to, /*接收方位址*/
31, /*轉賬代币ID,USDT=31*/
10000 /*轉賬代币數量,調整為最小機關計量的整數*/
null, /*比特币手續費支付位址,私鑰必須已加入ToolKit*/
546, /*向接收方發送的流通比特币,機關:satoshi*/
1000, /*交易手續費,機關:satoshi*/
true /*是否廣播*/
);
System.out.printf("txid => %s\n",txid); /*列印交易哈希*/
注意:
-
ToolKit執行個體利用錢包中的私鑰生成位址清單,并利用這些位址從第三方服務擷取utxo資訊。 是以需要錢包中
的私鑰對應位址在鍊上有utxo存在,ToolKit對象才能夠成功構造并簽名裸交易。
- 轉賬目标位址應當與建立Toolkit對象時指定的網絡一緻,例如主網的
位址,字首應當為p2pkh
1
3.2 指定Omni交易的手續費支付位址
在Omni協定層不需要支付交易手續費,但是Omni交易所嵌入的比特币交易依然需要支付手續費。當
sendOmnicoin()
方法的手續費支付位址設定為
null
時,将使用發送方位址支付比特币交易手續費。當你的Java應用需要實作多賬戶歸集功能時,使用統一的手續費支付位址會更容易管理一些。
例如,下面的代碼使用位址
35stX1w6LKHj7hGHz6GVNzXZCdUhAeqDb6
支付Omni交易的手續費:
String txid = kit.sendOmnicoin(
from, /*發送方位址,私鑰必須已加入ToolKit*/
to, /*接收方位址*/
31, /*轉賬OMNI代币ID,31:USDT*/
10000, /*轉賬OMNI代币數量,已調整至最小機關*/
"35stX1w6LKH...CdUhAeqDb6" /*交易手續費支付位址,私鑰必須已加入ToolKit*/
546, /*向接收方發送的流通比特币,機關:satoshi*/
1000, /*交易手續費,機關:satoshi*/
true /*是否廣播*/
);
- 即使指定了餘額充足的手續費支付位址,Omni交易的發送方依然必須有微量的比特币 餘額(546 SATOSHI),因為Omni協定需要交易發送方至少有一個可用UTXO。
- 手續費支付位址同時也是找零位址,多餘的比特币将傳回至該位址
3.3 指定Omni交易的比特币轉賬數量
由于Omni交易要求發送方必須有可用的UTXO,是以為了便于接收Omni代币的位址可以繼續流通所持有的Omni代币,
sendOmnicoin()
方法需要至少向接收方位址轉入546 SATOSHI的比特币,可以在調用該方法時修改這個預設數值。
例如,下面的代碼轉入接收方1000個SATOSHI:
String txid = kit.SendOmnicoin(
from, /*發送方位址,私鑰必須已加入ToolKit*/
to, /*接收方位址*/
31, /*轉賬OMNI代币ID,31:USDT*/
10000, /*轉賬OMNI代币數量,已調整至最小機關*/
fundAddr: "35stX1w6LKH...CdUhAeqDb6" /*交易手續費支付位址,私鑰必須已加入ToolKit*/
1000, /*向接收方發送的流通比特币,機關:satoshi*/
1000, /*交易手續費,機關:satoshi*/
true /*是否廣播*/
);
3.4 指定Omni交易的手續費
sendOmnicoin()
方法可以設定交易手續費,例如設定為
3000 SATOSHI
:
String txid = kit.SendOmnicoin(
from, /*發送方位址,私鑰必須已加入ToolKit*/
to, /*接收方位址*/
31, /*轉賬OMNI代币ID,31:USDT*/
10000, /*轉賬OMNI代币數量*/
fundAddr: "35stX1w6LKH...CdUhAeqDb6" /*交易手續費支付位址,私鑰必須已加入ToolKit*/
1000, /*向接收方發送的流通比特币,機關:satoshi*/
3000, /*交易手續費,機關:SATOSHI*/
true /*是否廣播*/
);
3.5 僅生成Omni裸交易但不廣播
有時可能隻需要生成Omni轉賬裸交易但并不需要廣播出去,可以将
sendOmnicoin()
方法的最後一個參數設定為
false
來取消廣播,這時将傳回生成的裸交易。例如:
String rawtx = kit.SendOmnicoin(
from, /*發送方位址,私鑰必須已加入ToolKit*/
to, /*接收方位址*/
31, /*轉賬OMNI代币ID,31:USDT*/
10000, /*轉賬OMNI代币數量,已調整至最小機關*/
fundAddr: "35stX1w6LKH...CdUhAeqDb6" /*交易手續費支付位址,私鑰必須已加入ToolKit*/
1000, /*向接收方發送的流通比特币,機關:satoshi*/
3000, /*交易手續費,機關:SATOSHI*/
false /*是否廣播*/
);
System.out.println(rawtx); /*列印裸交易内容*/
3.6 比特币轉賬
OmniTool.Java也支援比特币轉賬裸交易的生成與廣播。
例如,下面的代碼從ToolKit的某個位址向其他位址轉
10000 SATOSHI
String privHex = "4aec8e45106....00d5c5a05b"; /*私鑰:16進制字元串*/
kit.addKey(privHex); /*将私鑰加入ToolKit*/
String from = kit.getKeyStore()
.getByKey(privHex).address; /*私鑰對應的位址作為發起賬号*/
String to = "1GxX5tQR1C.....x2zbdj4mMuDcWR"; /*接收位址*/
String txid = kit.sendBitcoin(
from, /*發送方位址*/
to, /*接收方位址*/
10000, /*轉賬比特币數量,機關:SATOSHI*/
1500, /*手續費,機關:SATOSHI*/
null, /*找零位址*/
true /*是否廣播*/
);
當找零位址設定為
null
時,
SendBitcoin()
方法使用發送方位址作為找零位址。下面的代碼建立一個新位址接收找零:
String changeAddr = kit.newAddress(); /*建立新位址*/
String txid = kit.sendBitcoin(
from, /*發送方位址*/
to, /*接收方位址*/
10000, /*轉賬比特币數量,機關:SATOSHI*/
1500, /*手續費,機關:SATOSHI*/
changeAddr, /*找零位址*/
true /*是否廣播*/
);
類似的,當隻需要生成裸交易而不希望廣播時,可以設定最後一個參數為
false
4、UTXO采集器
OmniTool.Java使用接口
UtxoCollector
來約定UTXO的采集功能。該接口的實作需要支援擷取指定位址的候選UTXO集合,可指定多個位址。
接口方法:
UtxoBag collect(String[] addresses); /*提取并傳回候選UTXO集合*/
參數
addresses
用來聲明要收集UTXO的位址清單。
目前實作類:
- UtxoCollectorSmartbit:基于雲端第三方API實作的Utxo采集器
- UtxoCollectorRpc:基于omnicored節點RPC API實作的Utxo采集器
例如,下面的代碼使用UtxoCollectorSmartbit擷取測試鍊某個指定位址的UTXO:
UtxoCollector collector = new UtxoCollectorSmartbit(
"main" /*主鍊*/
);
String[] addresses = new String[]{"1C3TZ...brS2xHM"};
UtxoBag collected = collector.Collect(
addresses /*位址清單*/
);
5、UTXO選擇器
UtxoSelector
來約定UTXO的篩選政策。該接口的實作需要根據目标金額從候選UTXO中選擇可用UTXO,并傳回新的UtxoBag執行個體。
UtxoBag select(long target,UtxoBag collected); /*選擇可消費UTXO,傳回UtxoBag對象*/
target
聲明要達成的最低金額目标,機關:SATOSHI。
collected
是候選的utxo集合,通常是UtxoCollector的
collect()
調用傳回的結果。
- UtxoSelectorDefault:選擇不少于6個确認的未消費UTXO
例如下面的代碼使用
UtxoSelectorDefault
執行個體從候選UTXO中删選出至少
100000 SATOSHI
的UTXO:
//collected表示候選UTXO集合,來自Utxo采集器的collect()調用結果
UtxoSelector selector = new UtxoSelectorDefault();
UtxoBag selected = selector.select(
100000, /*最低目标金額*/
collected /*候選UTXO集合*/
);
System.out.printf("total:%d\n":selected.getTotal()); /*列印輸出選中utxo總額*/
考慮到UTXO的不可分割性,篩選出的若幹UTXO的總和,有可能超過目标金額。可以使用UtxoBag執行個體的
getTotal()
方法檢視集合中的UTXO總額,如上。
6、裸交易廣播器
OmniTool.Java使用
Broadcaster
接口約定裸交易廣播的功能規格。該接口的實作應當将裸交易廣播到Omni/Btc網絡中。
String broadcast(String rawtx); /*廣播裸交易*/
rawtx
用來聲明要廣播的裸交易,類型為16進制字元串。
- BroadcasterSmartbit
- BroadcasterRpc
例如,下面的代碼使用
BroadcasterSmartbit
将裸交易碼流廣播到Omni/Btc網絡中:
Broadcaster broadcaster = new BroadcasterSmartbit(
"testnet" /*測試鍊*/
);
String txid = broadcaster.broadcast(
"01000000011da9283b4...59f58488ac00000000" /*裸交易*/
);
7、密鑰存儲接口
KeyStore
約定密鑰存儲的功能規格。
bool add(KeyStoreItem item); /*存入密鑰*/
KeyStoreItem[] list(); /*浏覽全部密鑰*/
KeyStoreItem getByKey(); /*查詢指定16進制私鑰對應的密鑰資訊*/
KeyStoreItem getByWif(); /*查詢指定WIF格式私鑰對應的密鑰資訊*/
KeyStoreItem getByAddress(); /*查詢指定位址對應的密鑰資訊*/
KeyStoreItem getByScript(); /*查詢指定公鑰腳本對應的密鑰資訊*/
KeyStore
目前實作類有兩個:
- KeyStoreMemory:基于記憶體字典實作,沒有持久化能力,适合調試
- KeyStoreSql:基于Sql資料庫實作,适合作為生産環境密鑰存儲的參考實作
密鑰存儲執行個體的主要功能就是為ToolKit提供密鑰存儲和查詢能力。下面的代碼使用KeyStoreSql來啟動ToolKit,生成幾個不同類型的位址,導入16進制私鑰和WIF私鑰,然後進行查詢:
ToolKit kit = new ToolKit(
"testnet",
new KeyStoreSqlite("testnet.wallet"),
null,null,null
);
String addr1 = kit.newAddress("SEGWIT-P2SH"); /*生成隔離見證p2sh位址*/
String addr2 = kit.newAddress("SEGWIT"); /*生成隔離見證位址*/
String addr3 = kit.newAddress("P2PKH"); /*生成P2PKH位址,預設選項*/
String addr4 = kit.addKey( /*導入16進制私鑰*/
"4aec8e45106....00d5c5a05b",
"SEGWIT-P2SH" /*使用該私鑰的SEGWIT-P2SH位址*/
);
String addr5 = kit.addWif( /*導入WIF格式的私鑰*/
"cNJFgo1driF...SkdcF6JXXwHMm"
); /*預設使用私鑰的P2PKH位址*/
KeyStoreItem[] items = kit.list(); /*傳回全部密鑰記錄*/
for(KeyStoreItem item:items)
{
System.out.printf("key => %s\n",item.key);
System.out.printf("wif => %s\n",item.wif);
System.out.printf("address => %s\n",item.address);
System.out.printf("script => %s\n",item.script);
}
KeyStoreItem item = kit.getByAddress(addr1); /*查詢指定位址的密鑰記錄*/
System.out.printf("key => %s\n",item.key);
下載下傳位址:
Omni/USDT Java開發包 - 彙智網