package cn.chain33.javasdk.utils; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.List; import java.util.Random; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.Sha256Hash; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECKeyGenerationParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import cn.chain33.javasdk.model.Address; import cn.chain33.javasdk.model.Signature; import cn.chain33.javasdk.model.Transaction; import cn.chain33.javasdk.model.TransferBalanceRequest; import cn.chain33.javasdk.model.decode.DecodeRawTransaction; import cn.chain33.javasdk.model.enums.SignType; import cn.chain33.javasdk.model.protobuf.ManageProtobuf; import cn.chain33.javasdk.model.protobuf.ManageProtobuf.ManageAction; import cn.chain33.javasdk.model.protobuf.ManageProtobuf.ModifyConfig.Builder; import cn.chain33.javasdk.model.protobuf.RawTransactionProtobuf; import cn.chain33.javasdk.model.protobuf.TokenActionProtoBuf; import cn.chain33.javasdk.model.protobuf.TokenActionProtoBuf.TokenAction; import cn.chain33.javasdk.model.protobuf.TokenActionProtoBuf.TokenFinishCreate; import cn.chain33.javasdk.model.protobuf.TokenActionProtoBuf.TokenPreCreate; import cn.chain33.javasdk.model.protobuf.TransactionProtoBuf; import cn.chain33.javasdk.model.protobuf.TransferProtoBuf; import cn.chain33.javasdk.model.protobuf.TransferProtoBuf.AssetsTransfer; import cn.chain33.javasdk.model.protobuf.TransferProtoBuf.CoinsAction; import cn.chain33.javasdk.model.gm.SM2Util; import cn.chain33.javasdk.model.gm.SM2Util.SM2Signature; import cn.chain33.javasdk.model.gm.SM2KeyPair; import net.vrallev.java.ecc.Ecc25519Helper; /** * * @author logan 2018年5月14日 */ public class TransactionUtil { private static final SignType DEFAULT_SIGNTYPE = SignType.SECP256K1; public static final long DEFAULT_FEE = 1000000; private final static Long TX_HEIGHT_OFFSET = 1L << 62; private static byte[] addrSeed = "address seed bytes for public key".getBytes(); private static final long DURATION = 1; private static final long MICROSECOND = DURATION * 1000; private static final long MILLISECOND = MICROSECOND * 1000; private static final long SECOND = MILLISECOND * 1000; // private static final long MINUTE = SECOND * 1000; private static final long EXPIREBOUND = 1000000000; public static String toHexString(byte[] byteArr) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < byteArr.length; i++) { int b = byteArr[i] & 0xff; String hexString = Integer.toHexString(b); sb.append(hexString); } return sb.toString(); } /** * * @description expire转换为纳秒为单位 * @param expire * 单位为秒 * @return * */ public static long getExpire(long expire) { expire = expire * EXPIREBOUND; if (expire > EXPIREBOUND) { if (expire < SECOND * 120) { expire = SECOND * 120; } expire = System.currentTimeMillis() / 1000l + expire / SECOND; return expire; } else { return expire; } } /** * byte数组合并 * * @param byte_1 * @param byte_2 * @return */ public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) { byte[] byte_3 = new byte[byte_1.length + byte_2.length]; System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length); System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length); return byte_3; } /** * * @description 通过公钥生成地址 * @param pubKey * 公钥 * @return 地址 */ public static String genAddress(byte[] pubKey) { byte[] sha256 = TransactionUtil.Sha256(pubKey); byte[] ripemd160 = TransactionUtil.ripemd160(sha256); Address address = new Address(); address.setHash160(ripemd160); return addressToString(address); } /** * * @description 校验地址是否符合规则 * @param address * 地址 * @return 校验结果 */ public static boolean validAddress(String address) { try { byte[] decodeBytes = Base58Util.decode(address); byte[] checkByteByte = ByteUtils.subArray(decodeBytes, decodeBytes.length - 4); byte[] noCheckByte = ByteUtils.subArray(decodeBytes, 0, decodeBytes.length - 4); byte[] sha256 = Sha256(noCheckByte); byte[] twice = Sha256(sha256); for (int i = 0; i < 4; i++) { if (twice[i] != checkByteByte[i]) { return false; } } return true; } catch (Exception e) { return false; } } /** * @description byte数组截取 * * @param byteArr * @param start * @param end * @return */ public static byte[] subByteArr(byte[] byteArr, Integer start, Integer end) { Integer diff = end - start; byte[] byteTarget = new byte[diff]; if (diff > byteArr.length) { diff = byteArr.length; } for (int i = 0; i < diff; i++) { byteTarget[i] = byteArr[i]; } return byteTarget; } public static byte[] Sha256(byte[] sourceByte) { try { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(sourceByte); byte byteData[] = md.digest(); return byteData; } catch (Exception e) { e.printStackTrace(); return null; } } public static Long getRandomNonce() { Random random = new Random(System.currentTimeMillis()); return Math.abs(random.nextLong()); } /** * * @description 本地创建coins转账payload * @param to 目标地址 * @param amount 数量 * @param coinToken 主代币则为"" * @param note 备注,没有为"" * @return payload */ public static byte[] createTransferPayLoad(String to, Long amount, String coinToken, String note) { TransferProtoBuf.AssetsTransfer.Builder assetsTransferBuilder = TransferProtoBuf.AssetsTransfer.newBuilder(); assetsTransferBuilder.setCointoken(coinToken); assetsTransferBuilder.setAmount(amount); try { assetsTransferBuilder.setNote(ByteString.copyFrom(note, "utf-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } assetsTransferBuilder.setTo(to); AssetsTransfer assetsTransfer = assetsTransferBuilder.build(); TransferProtoBuf.CoinsAction.Builder coinsActionBuilder = TransferProtoBuf.CoinsAction.newBuilder(); coinsActionBuilder.setTy(1); coinsActionBuilder.setTransfer(assetsTransfer); CoinsAction coinsAction = coinsActionBuilder.build(); byte[] payload = coinsAction.toByteArray(); return payload; } /** * * @description 本地创建转账交易 * @param privateKey * @param toAddress * @param execer * @param payLoad * @return * */ public static String createTransferTx(String privateKey, String toAddress, String execer, byte[] payLoad) { byte[] privateKeyBytes = HexUtil.fromHexString(privateKey); return createTxMain(privateKeyBytes, toAddress, execer.getBytes(), payLoad, DEFAULT_SIGNTYPE, DEFAULT_FEE); } public static String createTx(String privateKey, String execer, String payLoad) { byte[] privateKeyBytes = HexUtil.fromHexString(privateKey); return createTx(privateKeyBytes, execer.getBytes(), payLoad.getBytes(), DEFAULT_SIGNTYPE, DEFAULT_FEE); } public static String createTx(String privateKey, String execer, String payLoad, long fee) { byte[] privateKeyBytes = HexUtil.fromHexString(privateKey); return createTx(privateKeyBytes, execer.getBytes(), payLoad.getBytes(), DEFAULT_SIGNTYPE, fee); } public static String createTx(byte[] privateKey, byte[] execer, byte[] payLoad, SignType signType, long fee) { String toAddress = getToAddress(execer); return createTxMain(privateKey, toAddress, execer, payLoad, signType, fee); } private static String createTxMain(byte[] privateKey, String toAddress, byte[] execer, byte[] payLoad, SignType signType, long fee) { if (signType == null) signType = DEFAULT_SIGNTYPE; // 如果没有私钥,创建私钥 privateKey = if (privateKey == null) { TransactionUtil.generatorPrivateKey(); } Transaction transation = new Transaction(); transation.setExecer(execer); transation.setPayload(payLoad); transation.setFee(fee); transation.setNonce(TransactionUtil.getRandomNonce()); // 计算To transation.setTo(toAddress); // 签名 byte[] protobufData = encodeProtobuf(transation); sign(signType, protobufData, privateKey, transation); // 序列化 byte[] encodeProtobufWithSign = encodeProtobufWithSign(transation); String transationStr = HexUtil.toHexString(encodeProtobufWithSign); return transationStr; } /** * * @description 本地构造交易 * @param privateKey * 私钥 * @param toAddress * 目标地址 * @param execer * 例如user.p.xxchain.token * @param payLoad * 内容 * @param signType * 签名方式,默认SignType.SECP256K1 * @param fee * 手续费 * @param txHeight * 联盟链需要,其他为null * @return * */ public static String createTxMain(byte[] privateKey, String toAddress, byte[] execer, byte[] payLoad, SignType signType, long fee, Long txHeight) { if (signType == null) signType = DEFAULT_SIGNTYPE; // 如果没有私钥,创建私钥 privateKey = if (privateKey == null) { TransactionUtil.generatorPrivateKey(); } Transaction transation = new Transaction(); transation.setExecer(execer); transation.setPayload(payLoad); transation.setFee(fee); transation.setNonce(TransactionUtil.getRandomNonce()); if (txHeight != null) { transation.setExpire(txHeight + TX_HEIGHT_OFFSET); } // 计算To transation.setTo(toAddress); // 签名 byte[] protobufData = encodeProtobuf(transation); sign(signType, protobufData, privateKey, transation); // 序列化 byte[] encodeProtobufWithSign = encodeProtobufWithSign(transation); String transationHash = HexUtil.toHexString(encodeProtobufWithSign); return transationHash; } /** * 构造转帐交易,并签名 * * @return 交易hash */ public static String transferBalanceMain(TransferBalanceRequest transferBalanceRequest) { String to = transferBalanceRequest.getTo(); Long amount = transferBalanceRequest.getAmount(); String coinToken = transferBalanceRequest.getCoinToken(); String note = transferBalanceRequest.getNote(); SignType signType = transferBalanceRequest.getSignType(); String privateKey = transferBalanceRequest.getFromPrivateKey(); String execer = transferBalanceRequest.getExecer(); long fee = transferBalanceRequest.getFee(); byte[] payload = createTransferPayLoad(to, amount, coinToken, note); byte[] execerBytes; if (StringUtil.isNotEmpty(execer)) { execerBytes = execer.getBytes(); } else { execerBytes = "none".getBytes(); } byte[] privateKeyBytes = HexUtil.fromHexString(privateKey); String transferTx = createTxMain(privateKeyBytes, to, execerBytes, payload, signType, fee); return transferTx; } /** * 计算to * * @param execer * @return */ private static String getToAddress(byte[] execer) { byte[] mergeredByte = TransactionUtil.byteMerger(addrSeed, execer); byte[] sha256_1 = TransactionUtil.Sha256(mergeredByte); for (int i = 0; i < sha256_1.length; i++) { sha256_1[i] = (byte) (sha256_1[i] & 0xff); } byte[] sha256_2 = TransactionUtil.Sha256(sha256_1); byte[] sha256_3 = TransactionUtil.Sha256(sha256_2); byte[] ripemd160 = TransactionUtil.ripemd160(sha256_3); Address address = new Address(); address.setHash160(ripemd160); return addressToString(address); } /** * @description 创建私钥和公钥 * * @return 私钥 */ public static byte[] generatorPrivateKey() { int length = 0; byte[] privateKey; do { ECKeyPairGenerator gen = new ECKeyPairGenerator(); SecureRandom secureRandom = new SecureRandom(); X9ECParameters secnamecurves = SECNamedCurves.getByName("secp256k1"); ECDomainParameters ecParams = new ECDomainParameters(secnamecurves.getCurve(), secnamecurves.getG(), secnamecurves.getN(), secnamecurves.getH()); ECKeyGenerationParameters keyGenParam = new ECKeyGenerationParameters(ecParams, secureRandom); gen.init(keyGenParam); AsymmetricCipherKeyPair kp = gen.generateKeyPair(); ECPrivateKeyParameters privatekey = (ECPrivateKeyParameters) kp.getPrivate(); privateKey = privatekey.getD().toByteArray(); length = privatekey.getD().toByteArray().length; } while (length != 32); return privateKey; } /** * * @description 生成私钥 * @return 私钥 * */ public static String generatorPrivateKeyString() { byte[] generatorPrivateKey = generatorPrivateKey(); ECKey eckey = ECKey.fromPrivate(generatorPrivateKey); return eckey.getPrivateKeyAsHex(); } /** * * @description 通过私钥生成公钥 * @param privateKey * 私钥 * @return 公钥 * */ public static String getHexPubKeyFromPrivKey(String privateKey) { ECKey eckey = ECKey.fromPrivate(HexUtil.fromHexString(privateKey)); byte[] pubKey = eckey.getPubKey(); String pubKeyStr = HexUtil.toHexString(pubKey); return pubKeyStr; } /** * 构造交易 * * @param transaction * @return */ public static byte[] encodeProtobuf(Transaction transaction) { TransactionProtoBuf.Transaction.Builder builder = TransactionProtoBuf.Transaction.newBuilder(); builder.setExecer(ByteString.copyFrom(transaction.getExecer())); builder.setExpire(transaction.getExpire()); builder.setFee(transaction.getFee()); builder.setNonce(transaction.getNonce()); builder.setPayload(ByteString.copyFrom(transaction.getPayload())); builder.setTo(transaction.getTo()); TransactionProtoBuf.Transaction build = builder.build(); byte[] byteArray = build.toByteArray(); return byteArray; } /** * 构造带签名的交易 * * @param transaction * @return */ public static byte[] encodeProtobufWithSign(Transaction transaction) { TransactionProtoBuf.Transaction.Builder builder = TransactionProtoBuf.Transaction.newBuilder(); builder.setExecer(ByteString.copyFrom(transaction.getExecer())); builder.setExpire(transaction.getExpire()); builder.setFee(transaction.getFee()); builder.setNonce(transaction.getNonce()); builder.setPayload(ByteString.copyFrom(transaction.getPayload())); builder.setTo(transaction.getTo()); TransactionProtoBuf.Signature.Builder signatureBuilder = builder.getSignatureBuilder(); signatureBuilder.setPubkey(ByteString.copyFrom(transaction.getSignature().getPubkey())); signatureBuilder.setTy(transaction.getSignature().getTy()); signatureBuilder.setSignature(ByteString.copyFrom(transaction.getSignature().getSignature())); TransactionProtoBuf.Signature signatureBuild = signatureBuilder.build(); builder.setSignature(signatureBuild); TransactionProtoBuf.Transaction build = builder.build(); byte[] byteArray = build.toByteArray(); return byteArray; } /** * 签名 * * @param signType * 签名类型 * @param data * 加密数据 * @param privateKey * 私钥 * @param transaction * 交易 */ private static void sign(SignType signType, byte[] data, byte[] privateKey, Transaction transaction) { switch (signType) { case SECP256K1: { Signature btcCoinSign = btcCoinSign(data, privateKey); transaction.setSignature(btcCoinSign); } break; case SM2: { SM2KeyPair keyPair = SM2Util.fromPrivateKey(privateKey); byte[] derSignBytes; try { derSignBytes = SM2Util.sign(data, null, keyPair); } catch (IOException e) { break; } Signature signature = new Signature(); signature.setPubkey(keyPair.getPublicKey().getEncoded(true)); signature.setSignature(derSignBytes); signature.setTy(signType.getType()); transaction.setSignature(signature); } break; case ED25519: { Ecc25519Helper helper1 = new Ecc25519Helper(privateKey); byte[] publicKey = helper1.getKeyHolder().getPublicKeySignature(); byte[] sign = helper1.sign(data); Signature signature = new Signature(); signature.setPubkey(publicKey); signature.setSignature(sign); signature.setTy(signType.getType()); transaction.setSignature(signature); } break; default: break; } } /** * * @description 本地签名 * @param privateKey * 私钥 * @param expire * 秒数 * @param txHex * 上一步CreateNoBalanceTransaction生成的交易hash 16进制 * @param index * 是签名交易组,则为要签名的交易序号,从1开始,小于等于0则为签名组内全部交易 * @return * */ public static String signRawTx(String privateKey, long expire, String txHex, Integer index) throws Exception { // 1.检查私钥是否存在 ->存在:->byte if (StringUtil.isEmpty(privateKey)) { throw new Exception("privateKey not Exist"); } byte[] privKeyBytes = HexUtil.fromHexString(privateKey); RawTransactionProtobuf.Transaction.Builder txBuilder = RawTransactionProtobuf.Transaction.newBuilder(); RawTransactionProtobuf.Transaction rawtransactionProtobuf = txBuilder.mergeFrom(HexUtil.fromHexString(txHex)) .build(); long changedExpire = getExpire(expire); txBuilder.setExpire(changedExpire); // 如果执行器为privacy 暂时不处理 /* * if(Arrays.equals(ExecerPrivacy,rawtransactionProtobuf.getExecer(). * toByteArray ())) { //signTxWithPrivacy } */ int groupCount = rawtransactionProtobuf.getGroupCount(); if (groupCount < 0 || groupCount == 1 || groupCount > 20) { throw new Exception("ErrTxGroupCount"); } else if (groupCount > 0) { byte[] txsBytes = rawtransactionProtobuf.getHeader().toByteArray(); RawTransactionProtobuf.Transactions.Builder txsBuilder = RawTransactionProtobuf.Transactions.newBuilder(); RawTransactionProtobuf.Transactions txs = txsBuilder.mergeFrom(txsBytes).build(); List<RawTransactionProtobuf.Transaction> txsList = txs.getTxsList(); if (index > txsList.size()) { throw new Exception("ErrIndex"); } if (index <= 0) { for (int i = 0; i < txsList.size(); i++) { RawTransactionProtobuf.Transaction signTransactionsN = signTransactionsN(i, txsList, privKeyBytes); txsList.set(i, signTransactionsN); } RawTransactionProtobuf.Transaction transaction = txsList.get(0); transaction.toBuilder().setHeader(ByteString.copyFrom(txs.toByteArray())); byte[] byteArray = transaction.toByteArray(); String signHexString = HexUtil.toHexString(byteArray); return signHexString; } index--; RawTransactionProtobuf.Transaction signTransactionsN = signTransactionsN(index, txsList, privKeyBytes); txsList.set(index, signTransactionsN); RawTransactionProtobuf.Transaction transactionFirst = txsList.get(0); transactionFirst.toBuilder().setHeader(ByteString.copyFrom(txs.toByteArray())); byte[] byteArray = transactionFirst.toByteArray(); String signHexString = HexUtil.toHexString(byteArray); return signHexString; } else { byte[] rawTxProtobufBytes = txBuilder.build().toByteArray(); RawTransactionProtobuf.Signature signatureProtobuf = signRawTx(rawTxProtobufBytes, privKeyBytes, txBuilder); txBuilder.setSignature(signatureProtobuf); String signedTx = HexUtil.toHexString(txBuilder.build().toByteArray()); return signedTx; } } private static RawTransactionProtobuf.Transaction signTransactionsN(int n, List<RawTransactionProtobuf.Transaction> transactionList, byte[] privKeyBytes) { RawTransactionProtobuf.Transaction.Builder newTxBuilder = transactionList.get(n).toBuilder(); RawTransactionProtobuf.Signature txSignature = signRawTx(transactionList.get(n).toByteArray(), privKeyBytes, newTxBuilder); newTxBuilder.setSignature(txSignature); return newTxBuilder.build(); } private static RawTransactionProtobuf.Signature signRawTx(byte[] data, byte[] privateKey, RawTransactionProtobuf.Transaction.Builder txBuilder) { Signature btcCoinSign = btcCoinSign(data, privateKey); RawTransactionProtobuf.Signature.Builder signatureBuilder = RawTransactionProtobuf.Signature.newBuilder(); signatureBuilder.setPubkey(ByteString.copyFrom(btcCoinSign.getPubkey())); signatureBuilder.setTy(btcCoinSign.getTy()); signatureBuilder.setSignature(ByteString.copyFrom(btcCoinSign.getSignature())); RawTransactionProtobuf.Signature signatureProtuBuff = signatureBuilder.build(); return signatureProtuBuff; } public static String addressToString(Address address) { if (StringUtil.isEmpty(address.getEnc58Str())) { byte[] ad = new byte[25]; ad[0] = address.getVersion(); for (int i = 1; i < 21; i++) { ad[i] = address.getHash160()[i - 1]; } byte[] checkSum = getAddressSh(ad); address.setCheckSum(checkSum); for (int i = 21, j = 0; i < 25; i++, j++) { ad[i] = checkSum[j]; } String Enc58Str = Base58Util.encode(ad); address.setEnc58Str(Enc58Str); } return address.getEnc58Str(); } /** * @description 数据处理,sha256 2次 * * @param sourceByte */ private static byte[] getAddressSh(byte[] sourceByte) { byte[] subByteArr = TransactionUtil.subByteArr(sourceByte, 0, 21); byte[] sha256_1 = TransactionUtil.Sha256(subByteArr); byte[] sha256_2 = TransactionUtil.Sha256(sha256_1); return sha256_2; } public static byte[] ripemd160(byte[] sourceByte) { byte[] hash = Ripemd160Util.getHash(sourceByte); return hash; } private static Signature btcCoinSign(byte[] data, byte[] privateKey) { byte[] sha256 = TransactionUtil.Sha256(data); Sha256Hash sha256Hash = Sha256Hash.wrap(sha256); ECKey ecKey = ECKey.fromPrivate(privateKey); ECKey.ECDSASignature ecdsas = ecKey.sign(sha256Hash); byte[] signByte = ecdsas.encodeToDER(); Signature signature = new Signature(); signature.setPubkey(ecKey.getPubKey()); signature.setSignature(signByte); signature.setTy(SignType.SECP256K1.getType()); return signature; } public static TransactionProtoBuf.Transaction decodeTxToProtobuf(DecodeRawTransaction unSignedTransaction, String execerAddress) { TransactionProtoBuf.Transaction.Builder newBuilder = TransactionProtoBuf.Transaction.newBuilder(); newBuilder.setExecer(ByteString.copyFrom(unSignedTransaction.getExecer().getBytes())); newBuilder.setExpire(unSignedTransaction.getExpire()); newBuilder.setFee(unSignedTransaction.getFee()); newBuilder.setNonce(unSignedTransaction.getNonce()); newBuilder.setPayload(ByteString.copyFrom(HexUtil.fromHexString(unSignedTransaction.getRawPayload()))); if (StringUtil.isEmpty(execerAddress)) { newBuilder.setTo(unSignedTransaction.getTo()); } else { newBuilder.setTo(execerAddress); } newBuilder.setHeader(ByteString.copyFrom(HexUtil.fromHexString(unSignedTransaction.getHeader()))); newBuilder.setNonce(unSignedTransaction.getNonce()); if (StringUtil.isNotEmpty(unSignedTransaction.getNext())) { newBuilder.setNext(ByteString.copyFrom(HexUtil.fromHexString(unSignedTransaction.getNext()))); } if (unSignedTransaction.getPayload() == null) { newBuilder.setPayload(ByteString.copyFrom(HexUtil.fromHexString(unSignedTransaction.getRawPayload()))); } if (unSignedTransaction.getGroupCount() != null) { newBuilder.setGroupCount(unSignedTransaction.getGroupCount()); } TransactionProtoBuf.Signature.Builder signatureBuilder = TransactionProtoBuf.Signature.newBuilder(); signatureBuilder.setTy(unSignedTransaction.getSignature().getTy()); signatureBuilder .setPubkey(ByteString.copyFrom(HexUtil.fromHexString(unSignedTransaction.getSignature().getPubkey()))); signatureBuilder.setSignature( ByteString.copyFrom(HexUtil.fromHexString(unSignedTransaction.getSignature().getSignature()))); newBuilder.setSignature(signatureBuilder.build()); return newBuilder.build(); } /** * 重组并签名交易组 * * @param decodeRawTransactions * @param execerAddress * @param fromAddressPriveteKey * @param withHoldPrivateKey * @return */ public static String signDecodeTx(List<DecodeRawTransaction> decodeRawTransactions, String execerAddress, String fromAddressPriveteKey, String withHoldPrivateKey) { DecodeRawTransaction unSignedTransaction = null; DecodeRawTransaction signedSeconedTx = null; for (DecodeRawTransaction decodeRawTransaction2 : decodeRawTransactions) { if (StringUtil.isEmpty(decodeRawTransaction2.getSignature().getSignature())) { unSignedTransaction = decodeRawTransaction2; } else { signedSeconedTx = decodeRawTransaction2; } } // 签名none交易 用代扣地址签名 TransactionProtoBuf.Transaction noneTx = decodeTxToProtobuf(unSignedTransaction, null); TransactionProtoBuf.Transaction unNoneTx = TransactionUtil.decodeTxToProtobuf(signedSeconedTx, execerAddress); TransactionProtoBuf.Transaction.Builder unNoneTxBuilder = unNoneTx.toBuilder(); String unNoneHash = TransactionUtil.getHash(unNoneTxBuilder.build(), execerAddress); TransactionProtoBuf.Transaction.Builder noneBuilder = TransactionProtoBuf.Transaction.newBuilder(noneTx); noneBuilder.setNext(ByteString.copyFrom(HexUtil.fromHexString(unNoneHash))); // noneBuilder.setGroupCount(2); String noneHash = TransactionUtil.getHash(noneBuilder.build()); noneBuilder.setHeader(ByteString.copyFrom(HexUtil.fromHexString(noneHash))); unNoneTxBuilder.setHeader(ByteString.copyFrom(HexUtil.fromHexString(noneHash))); TransactionProtoBuf.Transaction firstTxNew = unNoneTxBuilder.build(); TransactionProtoBuf.Transaction noneTxNew = noneBuilder.build(); noneTxNew = TransactionUtil.signProbuf(noneTxNew, withHoldPrivateKey); firstTxNew = TransactionUtil.signProbuf(firstTxNew, fromAddressPriveteKey); // 创建交易组 TransactionProtoBuf.Transactions.Builder txsBuilder = TransactionProtoBuf.Transactions.newBuilder(); txsBuilder.addTxs(noneTxNew); txsBuilder.addTxs(firstTxNew); TransactionProtoBuf.Transactions txs = txsBuilder.build(); TransactionProtoBuf.Transaction.Builder thirdBuilder = TransactionProtoBuf.Transaction.newBuilder(noneTxNew); thirdBuilder.setHeader(ByteString.copyFrom(txs.toByteArray())); TransactionProtoBuf.Transaction submitTx = thirdBuilder.build(); String groupTx = HexUtil.toHexString(submitTx.toByteArray()); return groupTx; } public static String getHash(TransactionProtoBuf.Transaction transaction) { TransactionProtoBuf.Transaction.Builder builder = TransactionProtoBuf.Transaction.newBuilder(); if (transaction.getPayload() != ByteString.EMPTY) { builder.setPayload(transaction.getPayload()); } builder.setExecer(transaction.getExecer()); builder.setFee(transaction.getFee()); builder.setExpire(transaction.getExpire()); builder.setNonce(transaction.getNonce()); builder.setTo(transaction.getTo()); builder.setGroupCount(transaction.getGroupCount()); if (transaction.getNext() != ByteString.EMPTY) { builder.setNext(transaction.getNext()); } TransactionProtoBuf.Transaction build = builder.build(); byte[] byteArray = build.toByteArray(); return HexUtil.toHexString(Sha256(byteArray)); } public static String getHash(TransactionProtoBuf.Transaction transaction, String to) { TransactionProtoBuf.Transaction.Builder builder = TransactionProtoBuf.Transaction.newBuilder(); if (transaction.getPayload() != ByteString.EMPTY) { builder.setPayload(transaction.getPayload()); } builder.setExecer(transaction.getExecer()); builder.setFee(transaction.getFee()); builder.setExpire(0L); builder.setNonce(transaction.getNonce()); if (!StringUtil.isEmpty(to)) { builder.setTo(to); } else { builder.setTo(transaction.getTo()); } builder.setGroupCount(transaction.getGroupCount()); if (transaction.getNext() != ByteString.EMPTY) { builder.setNext(transaction.getNext()); } TransactionProtoBuf.Transaction build = builder.build(); byte[] byteArray = build.toByteArray(); return HexUtil.toHexString(Sha256(byteArray)); } /** * * @description 签名 * @param tx * @param privateKey * @return * * @create 2020年1月9日 下午6:35:16 */ public static TransactionProtoBuf.Transaction signProbuf(TransactionProtoBuf.Transaction tx, String privateKey) { TransactionProtoBuf.Transaction encodeTx = getSignProbuf(tx); byte[] protobufData = encodeTx.toByteArray(); byte[] privateKeyBytes = HexUtil.fromHexString(privateKey); Signature btcCoinSign = btcCoinSign(protobufData, privateKeyBytes); TransactionProtoBuf.Transaction.Builder builder = tx.toBuilder(); TransactionProtoBuf.Signature.Builder signatureBuilder = TransactionProtoBuf.Signature.newBuilder(); signatureBuilder.setPubkey(ByteString.copyFrom(btcCoinSign.getPubkey())); signatureBuilder.setTy(btcCoinSign.getTy()); signatureBuilder.setSignature(ByteString.copyFrom(btcCoinSign.getSignature())); // 序列化 TransactionProtoBuf.Transaction.Builder setSignature = builder.setSignature(signatureBuilder.build()); return setSignature.build(); } /** * * @description 获取签名需要的protobuf * @param tx * @return * * @author lgang * @create 2020年1月9日 下午6:35:30 */ public static TransactionProtoBuf.Transaction getSignProbuf(TransactionProtoBuf.Transaction tx) { TransactionProtoBuf.Transaction.Builder builder = TransactionProtoBuf.Transaction.newBuilder(); builder.setExecer(tx.getExecer()); builder.setExpire(tx.getExpire()); builder.setFee(tx.getFee()); builder.setNonce(tx.getNonce()); builder.setPayload(tx.getPayload()); builder.setTo(tx.getTo()); if (tx.getNext() != null) { builder.setNext(tx.getNext()); } if (tx.getHeader() != null) { builder.setHeader(tx.getHeader()); } if (tx.getGroupCount() != 0) { builder.setGroupCount(tx.getGroupCount()); } TransactionProtoBuf.Transaction build = builder.build(); return build; } /** * 创建并签名管理合约交易 * @param key * @param value * @param op 操作符,add或delete * @return */ public static String createManage(String key, String value, String op, String privateKey, String execer) { Builder managerBuilder = ManageProtobuf.ModifyConfig.newBuilder(); managerBuilder.setKey(key); managerBuilder.setValue(value); managerBuilder.setOp(op); cn.chain33.javasdk.model.protobuf.ManageProtobuf.ManageAction.Builder actionBuilder = ManageProtobuf.ManageAction.newBuilder(); actionBuilder.setTy(0); actionBuilder.setModify(managerBuilder.build()); ManageAction managerAction = actionBuilder.build(); String createTxWithoutSign = TransactionUtil.createTxWithoutSign(execer.getBytes(), managerAction.toByteArray(), DEFAULT_FEE, 0); byte[] fromHexString = HexUtil.fromHexString(createTxWithoutSign); TransactionProtoBuf.Transaction parseFrom = null; try { parseFrom = TransactionProtoBuf.Transaction.parseFrom(fromHexString); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); return null; } TransactionProtoBuf.Transaction signProbuf = signProbuf(parseFrom, privateKey); return HexUtil.toHexString(signProbuf.toByteArray()); } /** * * @description 将执行器名称转为地址 * @param exec 执行器名称 * @return * */ public static String convertExectoAddr(String exec) { String toAddress = getToAddress(exec.getBytes()); return toAddress; } public static String getExecerAddress(String exec) { String toAddress = getToAddress(exec.getBytes()); return toAddress; } /** * * @description 本地构造 预创建积分交易 * @param execer user.p.xxx.token * @param name token名称 * @param symbol tokenSymbol * @param introduction token介绍 * @param total 发行总量,需要乘以10的8次方,比如要发行100个币,需要100*1e8 * @param price 发行该token愿意承担的费用 * @param owner token地址拥有者 * @param category token属性类别, 0 为普通token, 1 可增发和燃烧 * @param privateKey 超级管理员私钥 * @return 交易 * */ public static String createPrecreateTokenTx(String execer, String name, String symbol, String introduction, Long total, Long price, String owner, Integer category, String privateKey) { TokenActionProtoBuf.TokenPreCreate.Builder precreateBuilder = TokenActionProtoBuf.TokenPreCreate.newBuilder(); precreateBuilder.setName(name); precreateBuilder.setSymbol(symbol); precreateBuilder.setIntroduction(introduction); precreateBuilder.setTotal(total); precreateBuilder.setPrice(price); precreateBuilder.setOwner(owner); precreateBuilder.setCategory(category); TokenPreCreate tokenPreCreate = precreateBuilder.build(); TokenActionProtoBuf.TokenAction.Builder tokenActionBuilder = TokenActionProtoBuf.TokenAction.newBuilder(); tokenActionBuilder.setTy(7); tokenActionBuilder.setTokenPreCreate(tokenPreCreate); TokenAction tokenAction = tokenActionBuilder.build(); String createTxWithoutSign = TransactionUtil.createTxWithoutSign(execer.getBytes(), tokenAction.toByteArray(), DEFAULT_FEE, 0); byte[] fromHexString = HexUtil.fromHexString(createTxWithoutSign); TransactionProtoBuf.Transaction parseFrom = null; try { parseFrom = TransactionProtoBuf.Transaction.parseFrom(fromHexString); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); return null; } TransactionProtoBuf.Transaction signProbuf = signProbuf(parseFrom, privateKey); return HexUtil.toHexString(signProbuf.toByteArray()); } /** * * @description 本地创建token完成交易 * @param symbol token地址 * @param execer user.p.xxx.token * @param ownerAddr token拥有者地址 * @param managerPrivateKey 超级管理员私钥 * @return 交易 * */ public static String createTokenFinishTx(String symbol, String execer, String ownerAddr, String managerPrivateKey) { TokenActionProtoBuf.TokenFinishCreate.Builder payloadBuilder = TokenActionProtoBuf.TokenFinishCreate .newBuilder(); payloadBuilder.setSymbol(symbol); payloadBuilder.setOwner(ownerAddr); TokenFinishCreate tokenFinishCreate = payloadBuilder.build(); TokenActionProtoBuf.TokenAction.Builder tokenActionBuilder = TokenActionProtoBuf.TokenAction.newBuilder(); tokenActionBuilder.setTy(8); tokenActionBuilder.setTokenFinishCreate(tokenFinishCreate); TokenAction tokenAction = tokenActionBuilder.build(); String createTxWithoutSign = TransactionUtil.createTxWithoutSign(execer.getBytes(), tokenAction.toByteArray(), DEFAULT_FEE, 0); byte[] fromHexString = HexUtil.fromHexString(createTxWithoutSign); TransactionProtoBuf.Transaction parseFrom = null; try { parseFrom = TransactionProtoBuf.Transaction.parseFrom(fromHexString); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } TransactionProtoBuf.Transaction signProbuf = TransactionUtil.signProbuf(parseFrom, managerPrivateKey); String hexString = HexUtil.toHexString(signProbuf.toByteArray()); return hexString; } /** * * @description 本地创建并签名token转账交易 * @param privateKey * @param toAddress * @param execer * @param amount * @param coinToken * @param note * @return * */ public static String createTokenTransferTx(String privateKey, String toAddress, String execer, Long amount, String coinToken, String note) { byte[] payload = createtTokenTransferPayLoad(toAddress, amount, coinToken, note); byte[] privateKeyBytes = HexUtil.fromHexString(privateKey); return createTxMain(privateKeyBytes, toAddress, execer.getBytes(), payload, DEFAULT_SIGNTYPE, DEFAULT_FEE); } /** * * @description 本地创建token转账payload * @param to 目标地址 * @param amount 数量 * @param coinToken token symbol * @param note 备注,没有为"" * @return payload */ public static byte[] createtTokenTransferPayLoad(String to, Long amount, String coinToken, String note) { TokenActionProtoBuf.AssetsTransfer.Builder assetsTransferBuilder = TokenActionProtoBuf.AssetsTransfer.newBuilder(); assetsTransferBuilder.setCointoken(coinToken); assetsTransferBuilder.setAmount(amount); try { assetsTransferBuilder.setNote(ByteString.copyFrom(note, "utf-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } assetsTransferBuilder.setTo(to); cn.chain33.javasdk.model.protobuf.TokenActionProtoBuf.AssetsTransfer assetsTransfer = assetsTransferBuilder.build(); TokenActionProtoBuf.TokenAction.Builder tokenActionBuilder = TokenActionProtoBuf.TokenAction.newBuilder(); tokenActionBuilder.setTy(4); tokenActionBuilder.setTransfer(assetsTransfer); TokenAction tokenAction = tokenActionBuilder.build(); byte[] payload = tokenAction.toByteArray(); return payload; } /** * 创建交易,不签名 默认使用比特币seck256K1 * * @param execer 执行器名称 * @param payLoad 内容 * @return */ public static String createTxWithoutSign(String execer, String payLoad) { return createTxWithoutSign(execer.getBytes(), payLoad.getBytes(), DEFAULT_FEE, 0); } /** * 创建没有签名的交易 * * @param execer * @param payLoad * @param fee * @param txHeight * @return */ public static String createTxWithoutSign(byte[] execer, byte[] payLoad, long fee, long txHeight) { Transaction transation = new Transaction(); transation.setExecer(execer); transation.setPayload(payLoad); transation.setFee(fee); transation.setExpire(TX_HEIGHT_OFFSET + txHeight); transation.setNonce(TransactionUtil.getRandomNonce()); // 计算To String toAddress = getToAddress(execer); transation.setTo(toAddress); // 签名 byte[] protobufData = encodeProtobuf(transation); // 序列化 String transationStr = HexUtil.toHexString(protobufData); return transationStr; } }