关于omni-usdt离线交易:
如果是正式链,可以通过下面这个api获取utxo:
blockchain : https://blockchain.info/unspent?active=你需要用到的地址
拿到未使用的utxo就可以进行离线交易了。
添加的依赖:
<dependency>
<groupId>org.bitcoinj</groupId>
<artifactId>bitcoinj-tools</artifactId>
<version>0.14.7</version>
</dependency>
<dependency>
<groupId>org.bitcoinj</groupId>
<artifactId>bitcoinj-examples</artifactId>
<version>0.14.7</version>
</dependency>
<dependency>
<groupId>org.bitcoinj</groupId>
<artifactId>bitcoinj-core</artifactId>
<version>0.14.7</version>
</dependency>
构建一个实体类:
@Data
public class UnSpentUtxo implements Serializable {
private static final long serialVersionUID = -7417428486644921613L;
private String hash; //未交易hash
private long txN;//vout
private long value;//金额
private int height; //高度
private String script;//签名
private String address;//钱包地址
}
调用方法:
/**
*
* @param ptivateKey btc私钥
* @param fromAddress 转出地址
* @param privateUsdtKey usdt密钥
* @param recivAddress usdt接收地址
* @param fee 手续费
* @param omniHex usdthex
* @param unBtcUtxo btc utxo --手续费用
* @param unSpentUsdtUtxo usdt utxo --转账用
* @return
*/
public String CreateRawTransaction(String ptivateKey,String fromAddress,String privateUsdtKey,String recivAddress,long fee,String omniHex,
List<UnSpentUtxo> unBtcUtxo,List<UnSpentUtxo> unSpentUsdtUtxo){
List<UTXO> btcUtxos = new ArrayList<>();
List<UTXO> usdtUtxos = new ArrayList<>();
try {
if(!unBtcUtxo.isEmpty() && !unSpentUsdtUtxo.isEmpty()){
//find a btc eckey info
DumpedPrivateKey btcPrikey = DumpedPrivateKey.fromBase58(TestNet3Params.get(), ptivateKey);
ECKey btcKey = btcPrikey.getKey();
//find a usdt eckey info
DumpedPrivateKey usdtPrivateKey = DumpedPrivateKey.fromBase58(TestNet3Params.get(), privateUsdtKey);
ECKey usdtKey = usdtPrivateKey.getKey();
//rec 地址
Address receiveAddress = Address.fromBase58(TestNet3Params.get(),recivAddress);
//create a transaction
Transaction tx = new Transaction(TestNet3Params.get());
//odd Address;
Address oddAddress = Address.fromBase58(TestNet3Params.get(), fromAddress);
//如果需要找零 消费列表总额-已经转账的金额 -手续费
long valueBtc = unBtcUtxo.stream().mapToLong(UnSpentUtxo::getValue).sum();
long valueUSDT = unSpentUsdtUtxo.stream().mapToLong(UnSpentUtxo::getValue).sum();
//总输入 - 手续费 - 546 - 546 = 找零金额
long leave = (valueBtc + valueUSDT) - fee - 1092;
if(leave > 0){
tx.addOutput(Coin.valueOf(leave),oddAddress);
}
//usdt transaction
tx.addOutput(Coin.valueOf(546),new Script(Utils.HEX.decode(omniHex)));
//send to address
tx.addOutput(Coin.valueOf(546),receiveAddress);
//btc utxos is an array of inputs from my wallet
for(UnSpentUtxo unUtxo : unBtcUtxo){
btcUtxos.add(new UTXO(Sha256Hash.wrap(unUtxo.getHash()),
unUtxo.getTxN(),
Coin.valueOf(unUtxo.getValue()),
unUtxo.getHeight(),
false,
new Script(Utils.HEX.decode(unUtxo.getScript())),
unUtxo.getAddress()));
}
//usdt utxos is an array of inputs from my wallet
for(UnSpentUtxo unUtxo: unSpentUsdtUtxo){
usdtUtxos.add(new UTXO(Sha256Hash.wrap(unUtxo.getHash()),
unUtxo.getTxN(),
Coin.valueOf(unUtxo.getValue()),
unUtxo.getHeight(),
false,
new Script(Utils.HEX.decode(unUtxo.getScript())),
unUtxo.getAddress()));
}
//create usdt utxo data
for(UTXO utxo : usdtUtxos){
TransactionOutPoint outPoint = new TransactionOutPoint(TestNet3Params.get(), utxo.getIndex(), utxo.getHash());
tx.addSignedInput(outPoint, utxo.getScript(),usdtKey,Transaction.SigHash.ALL,true);
}
//create btc utxo data
for(UTXO utxo : btcUtxos){
TransactionOutPoint outPoint = new TransactionOutPoint(TestNet3Params.get(), utxo.getIndex(), utxo.getHash());
tx.addSignedInput(outPoint, utxo.getScript(),btcKey,Transaction.SigHash.ALL,true);
}
Context context = new Context(TestNet3Params.get());
tx.getConfidence().setSource(TransactionConfidence.Source.NETWORK);
tx.setPurpose(Transaction.Purpose.USER_PAYMENT);
log.info("===============[USDT sign success,hash is {}==========", tx.getHashAsString());
return new String(Hex.encodeHex(tx.bitcoinSerialize()));
}}catch (Exception e){
log.info("========com.bscoin.coldwallet.cointype.usdt.RawTransaction.createRawTransaction(String, String, String, String, String, long, String, List<UnSpentUtxo>, List<UnSpentUtxo>):{} ==="
,e.getMessage(),e);
}
return null;
}
调用:
public static void main(String[] args) {
String mkPrivateKey = "";
String usdtPri = "";
//构造Data前缀
String methodStrart = "6a146f6d6e69";
//币种唯一标识的16进制---这个是1,我用测试链的omni代币
String coinProperity1 = "0000000000000001";
//币种唯一标识的16进制---这是31,usdt。
String coinProperity31 = "000000000000001f";
//构造Data的数量16进制数。 需要 基本数量 *10^8再转,我转的数量为5个
String Hex16ToBalance = "000000001dcd6500";
//构造的Data 为方法前缀+币唯一标识16进制+转账数量的16进制
String omniHex = methodStrart+coinProperity1+Hex16ToBalance;
Map m = new HashMap();
List<UnSpentUtxo> us = new ArrayList<UnSpentUtxo>();
//第一个utxo为手续费用到的
UnSpentUtxo u = new UnSpentUtxo();
u.setAddress("mkUWwGMQ2nHbRPyRL2czV9HnFGqg6XMqUL");//地址
u.setHash("af2ec076ae16a0e0ea1aac82af35817b8b2ba81edc5a4f7f77597de7a623ece8");//交易hash
u.setHeight(1696973);//确认交易的区块高度
u.setScript("76a9143662a0fc506262a3c658b46df05183b19b64f8e688ac");//script
u.setTxN(0);//utxo中的vout
u.setValue(1900000);//数量--如果用listunspent的话,需要*10^8
us.add(u);
//第二个为载体(真正需要转出去用到的)
List<UnSpentUtxo> us2 = new ArrayList<UnSpentUtxo>();
UnSpentUtxo u3 = new UnSpentUtxo();
u3.setAddress("msCzB99PdAZRfeaFSkrNZjfqBgavWU2Kjs");
u3.setHash("7e3d4fed3c849960758c505b56cf967b6c3b36b6aedbfa39c39b8b3690415732");
u3.setHeight(1612275);
u3.setScript("76a914803b979506bf04579b2a24d3c5fd01895272fac488ac");
u3.setTxN(2);
u3.setValue(546);
us2.add(u3);
m.put("btcUtxo", us);
m.put("usdtUtxo", us2);
m.put("omniHex", methodStrart+coinProperity1+Hex16ToBalance);
System.out.println("传输参数:" + JSONObject.toJSONString(m));
/**
* 第一个,手续费的私钥
* 第二个,手续费地址
* 第三个:要付款的usdt私钥--(付款了5个数量为16进制,需要5*10^8 再转16)
* 第四个:接收地址
* 第五个:手续费费用 需要/10^8才是真实,例如下面的就是0.0001个btc手续费
* 第五个,构造的data,--为 固定前缀6a146f6d6e69 + 币种唯一标识的十六进制 + 转账数量 * 10^8 再转16进制
* 第六个,手续费utxo
* 第七个,转账的0.00000546 utxo
*
* 调用了之后,会生成已经签好名的交易,
* 通过广播sendrawtransaction接口返回生成的hashId。
*
* 消耗了手续费的utxo、转账的utxo
* 接收地址会收到一个新的0.00000546UTXO,
* 手续费会返回一个新的utxo
*/
String c = new UnSpentUtxo().CreateRawTransaction(mkPrivateKey, "mkUWwGMQ2nHbRPyRL2czV9HnFGqg6XMqUL",usdtPri,
"mkUWwGMQ2nHbRPyRL2czV9HnFGqg6XMqUL",10000,omniHex,us,us2);//手续费是0.0001BTC
System.out.println(c);
try {
String txid = rpcUtils.invoke("http://127.0.0.1:8888", "sendrawtransaction","c3czZGY6d2Vyc3ZzZGY=" , new Object[]{c});
System.out.println(txid);
} catch (Exception e) {
log.error("调用失败:{}", e.getMessage());
}
}
调用结果:
传输参数:{"omniHex":"6a146f6d6e690000000000000001000000001dcd6500","usdtUtxo":[{"address":"msCzB99PdAZRfeaFSkrNZjfqBgavWU2Kjs","hash":"7e3d4fed3c849960758c505b56cf967b6c3b36b6aedbfa39c39b8b3690415732","height":1612275,"script":"76a914803b979506bf04579b2a24d3c5fd01895272fac488ac","txN":2,"value":546}],"btcUtxo":[{"address":"mkUWwGMQ2nHbRPyRL2czV9HnFGqg6XMqUL","hash":"af2ec076ae16a0e0ea1aac82af35817b8b2ba81edc5a4f7f77597de7a623ece8","height":1696973,"script":"76a9143662a0fc506262a3c658b46df05183b19b64f8e688ac","txN":0,"value":1900000}]}
四月 15, 2020 9:57:31 上午 org.bitcoin.Secp256k1Context <clinit>
信息: java.lang.UnsatisfiedLinkError: no secp256k1 in java.library.path
四月 15, 2020 9:57:31 上午 org.bitcoinj.core.Context <init>
信息: Creating bitcoinj 0.14.7 context.
四月 15, 2020 9:57:31 上午 com.hlgj.entity.UnSpentUtxo CreateRawTransaction
信息: ===============[USDT sign success,hash is 2549a0c2d058e4f262db57ce498b6fa42e46bc3014420808053fe0bd0b77e9e3==========
010000000232574190368b9bc339fadbaeb6363b6c7b96cf565b508c756099843ced4f3d7e020000006b483045022100f76ab6398dcf01a118c22ff1f9cfc254e0eb7a4f355a0be96835a90b1b0640d7022042f5eff1026fa8ffa58c07084565893fb2844e2fb03466bdb05f8e74673d78308121031b6c481878b001ee6971bc494dbed93f88120844050d81e79481c71576840f39ffffffffe8ec23a6e77d59777f4f5adc1ea82b8b7b8135af82ac1aeae0a016ae76c02eaf000000006a47304402201ed328cdb85468ec850ebff23c9074eef37249a8f91d7bd8778c509648c3855e02202cd74327ea177c3338929e1c0fbc8572ef89ad905881e91a2513fe4581837b96812103de8addf3269ab5a9cfe55e1ba9c7e395dec92a04853f1c5c43a815ae20c4f7e0ffffffff03aed41c00000000001976a9143662a0fc506262a3c658b46df05183b19b64f8e688ac2202000000000000166a146f6d6e690000000000000001000000001dcd650022020000000000001976a9143662a0fc506262a3c658b46df05183b19b64f8e688ac00000000//已经签名的交易
2549a0c2d058e4f262db57ce498b6fa42e46bc3014420808053fe0bd0b77e9e3//这个是调用之后返回的hash
调用了之后, 会发现:
omni钱包,少掉了两个utxo,
就是上述填入的u 还有 us3.
但同时手续费地址会收到一笔新的utxo(找零返回的),接收地址也多了一笔0.00000546的utxo:
{
"txid": "2549a0c2d058e4f262db57ce498b6fa42e46bc3014420808053fe0bd0b77e9e3",
"vout": 0,
"address": "mkUWwGMQ2nHbRPyRL2czV9HnFGqg6XMqUL",
"label": "watch-only test",
"scriptPubKey": "xxxxxxxx",
"amount": 0.01889454,
"confirmations": 56,
"spendable": false,
"solvable": false,
"safe": true
}这笔是找零返回的。