Omni Layer USDT區塊鏈開發包簡介【OmniTool.Java】

  • 2019 年 12 月 1 日
  • 筆記

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軟體包主要程式碼文件清單請訪問官網:http://sc.hubwiz.com/codebag/omni-java-lib/

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: %sn",(String)unspent.get("txid"));  }

call()方法的返回結果對應於RPC API的JSON響應中的result欄位,其類型取決於我們傳入的第一個參數。

call()方法的第一個參數聲明方法返回的結果類型的Class對象,方法會將RPC API的JSON響應中的result欄位解碼為該參數指定的類型。通常我們都可以使用MapMap[]來對應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: %sn",unspent.txid);    System.out.printf("vout: %dn",unspent.vout);    System.out.printf("amount:%fn",unspent.amount);  }

顯然,定義自己的結果類可以將RPC API的JSON響應直接反序列化到指定的類型,對於操作複雜響應結果會很有幫助。但比特幣和Omni層的RPC API不僅在動態演化中,而且有些JSON響應的結構本身就是動態的,因此往往還需要結合使用前面更通用的MapMap[]類型。

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: %sn",(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 => %sn",txid);

開發包中的demo/RpcClientDemo.java示例程式碼使用RpcClient完整演示了在Omni層的代幣發行與轉賬功能,如果你計劃搭建自己的Omni Core節點,相信這個示例會有很大幫助。

3、ToolKit類使用說明

如果不願意搭建自己的Omni Core節點,而是希望基於第三方API為自己的Java應用增加對Omni Layer/USDT的支援,那麼最簡單的方法是使用離線交易的入口類ToolKit

ToolKit類的主要作用是創建並廣播Omni代幣或比特幣轉賬裸交易,它的基本使用步驟如下:

  • 創建一個ToolKit實例
  • 使用AddKey()方法將必要的私鑰加入該ToolKit實例,例如轉出地址的私鑰,因為ToolKit需要利用私鑰對裸交易進行簽名
  • 使用SendOmnicoin()方法生成並廣播Omni代幣轉賬裸交易,或者使用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 => %sn",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選擇器

OmniTool.Java使用介面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:%dn":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、密鑰存儲介面

OmniTool.Java使用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 => %sn",item.key);    System.out.printf("wif => %sn",item.wif);    System.out.printf("address => %sn",item.address);    System.out.printf("script => %sn",item.script);  }    KeyStoreItem item = kit.getByAddress(addr1);          /*查詢指定地址的密鑰記錄*/  System.out.printf("key => %sn",item.key);  

下載地址:Omni/USDT Java開發包 – 匯智網

(adsbygoogle = window.adsbygoogle || []).push({});