bitcion 离线签名
加入依赖
<dependency>
<groupId>org.bitcoinj</groupId>
<artifactId>bitcoinj-core</artifactId>
<version>0.14.7</version>
</dependency>
//rpc
<dependency>
<groupId>com.github.briandilley.jsonrpc4j</groupId>
<artifactId>jsonrpc4j</artifactId>
<version>1.5.3</version>
</dependency>
//构建交易
public String create(String addrfrom, String addrto, BigDecimal amount,BigDecimal mbfee ){
//创建rpc 客户端
BitcoinRpc client = new BitcoinRpc();
List<BtcRpcUnspent> mBtcRpcUnspent = client.getListUnspent(6L,9999L,Arrays.asList(addrfrom));
//根据转账数量和交易费过滤utxo
.....
NetworkParameters params = MainNetParams.get();
Transaction spendTx = new Transaction(params);
Address address = Address.fromBase58(params, addrfrom);
Address toaddress = Address.fromBase58(params, addrto);
//构建交易数据
for (BtcRpcUnspent utxo : mBtcRpcUnspent)
{
String hash = utxo.getTransactionHash();
int index = utxo.getN();
Script scriptPubKey =ScriptBuilder.createOutputScript(address);
Sha256Hash mHash =new Sha256Hash(hash);
spendTx.addInput(mHash,index,scriptPubKey);
}
//输出
spendTx.addOutput(Coin.valueOf(amountValue), toaddress);
//找零
spendTx.addOutput(toself, address);//toself 根据选择的utxo总额 减去 转出 减去交易费
byte[] spendbyte=spendTx.bitcoinSerialize();
String spendstring = HEX.encode(spendbyte);
return spendstring ;
}
//bip44
public static final ChildNumber ONE_HARDENED = new ChildNumber(1, true);
public static final ChildNumber ZERO_HARDENED = new ChildNumber(0, true);
public ECKey createBip44Address(List<String> mnemonicWords, String passPhrase,int number){
byte[] seeded = MnemonicCode.toSeed(mnemonicWords, passPhrase);
ChildNumber btcindex = checkNetStatus()?ZERO_HARDENED:ONE_HARDENED;
DeterministicSeed seed = new DeterministicSeed(mnemonicWords, seeded, "", Utils.currentTimeSeconds());
DeterministicKey rootPrivateKey = HDKeyDerivation.createMasterPrivateKey(seed.getSeedBytes());
DeterministicHierarchy deterministicHierarchy = new DeterministicHierarchy(rootPrivateKey);
ImmutableList<ChildNumber> path = ImmutableList.of(new ChildNumber(44, true),btcindex, ChildNumber.ZERO_HARDENED);
DeterministicKey fourpath = deterministicHierarchy.get(path, true, true);
DeterministicKey fourpathhd = HDKeyDerivation.deriveChildKey(fourpath, 0);
DeterministicKey fivepathhd = HDKeyDerivation.deriveChildKey(fourpathhd, number);
ECKey ecKey = ECKey.fromPrivate(fivepathhd.getPrivKey());
// Address address = ecKey.toAddress(params);
return ecKey;
}
//签名
public String signTransaction(String inputTransaction, String addr, List<String> mnemonicWords, String passPhrase) {
//inputTransaction 为上面构建后序列化的未签名的交易
byte[] bytes = HEX.decode(inputTransaction);
Transaction transaction = new Transaction(params, bytes);
ECKey ecKey=createBip44Address(List<String> mnemonicWords, String passPhrase,int number);
int numInputs = transaction.getInputs().size();
for (int i = 0; i < numInputs; i++) {
TransactionInput txIn = transaction.getInput(i);
Address address = new Address(params, addr);
Script scriptPubKey = ScriptBuilder.createOutputScript(address);
byte[] pubKeyHash = scriptPubKey.getPubKeyHash();
System.out.println("getWallet()===" + mWallet);
if (ecKey == null) {
loggor.error("签名失败 ecKey is null!!!");
return "";
}
txIn.setScriptSig(scriptPubKey.createEmptyInputScript(ecKey, scriptPubKey));
Script inputScript = txIn.getScriptSig();
byte[] script = scriptPubKey.getProgram(); // 应该是byte类型的上笔交易的输出脚本
try {
TransactionSignature signature = transaction.calculateSignature(i, ecKey, script, Transaction.SigHash.ALL, false);
int sigIndex = 0;
inputScript = scriptPubKey.getScriptSigWithSignature(inputScript, signature.encodeToBitcoin(), sigIndex);
txIn.setScriptSig(inputScript);
} catch (ECKey.KeyIsEncryptedException e) {
throw e;
} catch (ECKey.MissingPrivateKeyException e) {
System.out.println("ecKey===" + e);
}
}
// 已签名的交易对象序列化
byte[] spendBytes = transaction.bitcoinSerialize();
String txid = transaction.getHashAsString();
String signDataStr = HEX.encode(spendBytes);
return signDataStr;//通过client.sendRawTransaction(signDataStr)就能发送交易了
}
@Data
public class BtcRpcUnspent {
private int n;
private String transactionHash;
private String address;
private String script;
private double value;
private long confirmations;
private String account;
public BtcRpcUnspent(int n, String transactionHash, String address, String script, double value, long confirmations,String account) {
this.n = n;
this.transactionHash = transactionHash;
this.address = address;
this.script = script;
this.value = value;
this.confirmations = confirmations;
this.account = account;
}
public BtcRpcUnspent(JsonObject o) {
this( o.has("vout") ? o.get("vout").getAsInt() : -1,
o.has("txid") ? o.get("txid").getAsString() :"",
o.has("address") ? o.get("address").getAsString() :"",
o.has("scriptPubKey") ? o.get("scriptPubKey").getAsString() :"",
o.has("amount") ? o.get("amount").getAsDouble() : -1,
o.has("confirmations") ? o.get("confirmations").getAsLong() : -1,
o.has("account") ? o.get("account").getAsString() :""
);
}
}
在构建交易时 拿到UTXO后可以通过rpc来构建未签名的交易
List<BtcListUnspent> btcListUnspents = btcListUnspents = client.getListUnspent(6L, 9999L, Arrays.asList(addrfrom));
//过滤
List<BtcCreateVin> inputs = new ArrayList<>();
BigDecimal selectAmount = BigDecimal.ZERO;
for (BtcListUnspent unspent : btcListUnspents) {
if (selectAmount.compareTo(amount.add(mbfee)) == -1) {
selectAmount = selectAmount.add(unspent.getAmount());
inputs.add(new BtcCreateVin(unspent.getTxid(), unspent.getVout()));
} else {
break;
}
}
//输出
HashMap<String, BigDecimal> outputs = new HashMap<>();
outputs.put(addrfrom, uxtofrom);
outputs.put(addrto, amount);
String signHash = client.createrawtransaction(inputs, outputs);
return signHash ;
@Data
public class BtcListUnspent {
//交易哈希
private String txid;
//位置
private int vout;
//地址
private String address;
//标签
private String label;
//公钥哈希
private String scriptPubKey;
//金额
private BigDecimal amount;
//确认数
private long confirmations;
//赎回脚本
private String redeemScript;
//隔离见证脚本
private String witnessScript;
//本地可花费
private boolean spendable;
private boolean solvable;
private String desc;
private boolean safe;
}
@Data
public class BtcVin {
private String txid;
private long vout;
private List<String> txinwitness;
private long sequence;
private BtcScriptSig scriptSig;
}
//也可以拿到UTXO传到前端 由前端构建与签名(缺点是传输数据大)
public String createandsign(String addrfrom, String addrto, BigDecimal amount,BigDecimal mbfee , List<BtcListUnspent> btcListUnspents,List<String> words){
Transaction transaction = new Transaction(params);
ECKey ecKey = createBip44Address(words, "", number);
transaction.addOutput(Coin.parseCoin(amount.toString()), Address.fromBase58(params, to));
if (change.compareTo(BigDecimal.ZERO) > 0) {
transaction.addOutput(Coin.parseCoin(change.toString()), Address.fromBase58(params, from));
}
for (BtcListUnspent btcListUnspent:btcListUnspents) {
TransactionOutPoint transactionOutPoint = new TransactionOutPoint(params,btcListUnspent.getVout(), Sha256Hash.wrap(btcListUnspent.getTxid()));
// Script utxo_script = (new ScriptBuilder()).createOutputScript(ecKey.toAddress(params));
Script utxo_script = (new Script(HEX.decode(btcListUnspent.getScriptPubKey()));
transaction.addSignedInput(transactionOutPoint,utxo_script,ecKey);
}
byte[] spendBytes = transaction.bitcoinSerialize();
String signDataStr = HEX.encode(spendBytes);
BchRpc instance = BchRpc.getInstance();
String s = instance.sendRawTransaction(signDataStr);
}
网友评论