+++ /dev/null
-package io.bytom.api;\r
-\r
-import io.bytom.common.DerivePrivateKey;\r
-import io.bytom.common.DeriveXpub;\r
-import io.bytom.common.ExpandedPrivateKey;\r
-import io.bytom.common.Utils;\r
-import org.bouncycastle.jcajce.provider.digest.SHA3;\r
-import org.bouncycastle.util.encoders.Hex;\r
-\r
-import java.io.ByteArrayOutputStream;\r
-import java.io.IOException;\r
-import java.math.BigInteger;\r
-import java.security.InvalidKeyException;\r
-import java.security.NoSuchAlgorithmException;\r
-import java.security.SignatureException;\r
-\r
-\r
-/**\r
- * Created by liqiang on 2018/10/24.\r
- */\r
-public class SignTransaction {\r
-\r
- private final String OP_TXSIGHASH = "ae";\r
- private final String OP_CHECKMULTISIG = "ad";\r
-\r
- public String serializeTransaction(Transaction tx) {\r
- String txSign = null;\r
- //开始序列化\r
- ByteArrayOutputStream stream = new ByteArrayOutputStream();\r
- try {\r
- stream.write(7);\r
- // version\r
- if (null != tx.version)\r
- Utils.writeVarint(tx.version, stream);\r
- if (null != tx.timeRange)\r
- Utils.writeVarint(tx.timeRange, stream);\r
- //inputs\r
- if (null != tx.inputs && tx.inputs.size() > 0) {\r
- Utils.writeVarint(tx.inputs.size(), stream);\r
- for (Transaction.AnnotatedInput input : tx.inputs) {\r
- if (input.type == 1) {\r
- //assertVersion\r
- Utils.writeVarint(tx.version, stream); //AssetVersion是否默认为1\r
-\r
- //inputCommitment\r
- ByteArrayOutputStream inputCommitStream = new ByteArrayOutputStream();\r
- //spend type flag\r
- Utils.writeVarint(input.type, inputCommitStream);\r
- //spendCommitment\r
- ByteArrayOutputStream spendCommitSteam = new ByteArrayOutputStream();\r
- spendCommitSteam.write(Hex.decode(input.sourceId)); //计算muxID\r
- spendCommitSteam.write(Hex.decode(input.assetId));\r
- Utils.writeVarint(input.amount, spendCommitSteam);\r
- //sourcePosition\r
- Utils.writeVarint(input.sourcePosition, spendCommitSteam); //db中获取position\r
- //vmVersion\r
- Utils.writeVarint(1, spendCommitSteam); //db中获取vm_version\r
- //controlProgram\r
- Utils.writeVarStr(Hex.decode(input.controlProgram), spendCommitSteam);\r
-\r
- byte[] dataSpendCommit = spendCommitSteam.toByteArray();\r
-\r
- Utils.writeVarint(dataSpendCommit.length, inputCommitStream);\r
- inputCommitStream.write(dataSpendCommit);\r
- byte[] dataInputCommit = inputCommitStream.toByteArray();\r
- //inputCommit的length\r
- Utils.writeVarint(dataInputCommit.length, stream);\r
- stream.write(dataInputCommit);\r
-\r
- //inputWitness\r
- if (null != input.witnessComponent) {\r
- ByteArrayOutputStream witnessStream = new ByteArrayOutputStream();\r
- //arguments\r
- int lenSigs = input.witnessComponent.signatures.length;\r
- //arguments的length\r
- Utils.writeVarint(lenSigs, witnessStream);\r
- for (int i = 0; i < lenSigs; i++) {\r
- String sig = input.witnessComponent.signatures[i];\r
- Utils.writeVarStr(Hex.decode(sig), witnessStream);\r
- }\r
- byte[] dataWitnessComponets = witnessStream.toByteArray();\r
- //witness的length\r
- Utils.writeVarint(dataWitnessComponets.length, stream);\r
- stream.write(dataWitnessComponets);\r
- }\r
- }\r
- if (input.type == 0) {\r
- //assetVersion\r
- Utils.writeVarint(01, stream);\r
- //写入 type nonce assetId amount\r
- ByteArrayOutputStream issueInfo = new ByteArrayOutputStream();\r
- //写入 input.type==00 issue\r
- Utils.writeVarint(input.type, issueInfo);\r
- //写入 8个字节的随机数\r
- Utils.writeVarStr(Hex.decode(input.nonce), issueInfo);\r
- issueInfo.write(Hex.decode(input.assetId));\r
- Utils.writeVarint(input.amount, issueInfo);\r
- stream.write(issueInfo.toByteArray().length);\r
- stream.write(issueInfo.toByteArray());\r
-\r
- ByteArrayOutputStream issueInfo1 = new ByteArrayOutputStream();\r
- //未知\r
- Utils.writeVarint(1, issueInfo1);\r
- //写入assetDefine\r
- Utils.writeVarStr(Hex.decode(input.assetDefinition), issueInfo1);\r
- //vm.version\r
- Utils.writeVarint(1, issueInfo1);\r
- //controlProgram\r
- Utils.writeVarStr(Hex.decode(input.controlProgram), issueInfo1);\r
-\r
- //inputWitness\r
- if (null != input.witnessComponent) {\r
- ByteArrayOutputStream witnessStream = new ByteArrayOutputStream();\r
- //arguments\r
- int lenSigs = input.witnessComponent.signatures.length;\r
- //arguments的length\r
- Utils.writeVarint(lenSigs, witnessStream);\r
- for (int i = 0; i < lenSigs; i++) {\r
- String sig = input.witnessComponent.signatures[i];\r
- Utils.writeVarStr(Hex.decode(sig), witnessStream);\r
- }\r
-// byte[] dataWitnessComponets = witnessStream.toByteArray();\r
- //witness的length\r
-// Utils.writeVarint(dataWitnessComponets.length, issueInfo1);\r
-\r
- issueInfo1.write(witnessStream.toByteArray());\r
- }\r
- stream.write(issueInfo1.toByteArray().length - 1);\r
- stream.write(issueInfo1.toByteArray());\r
-\r
- }\r
- }\r
- }\r
-\r
- //outputs\r
- if (null != tx.outputs && tx.outputs.size() > 0) {\r
- Utils.writeVarint(tx.outputs.size(), stream);\r
- for (Transaction.AnnotatedOutput output : tx.outputs) {\r
- //assertVersion\r
- Utils.writeVarint(tx.version, stream); //AssetVersion是否默认为1\r
- //outputCommit\r
- ByteArrayOutputStream outputCommitSteam = new ByteArrayOutputStream();\r
- //assetId\r
- outputCommitSteam.write(Hex.decode(output.assetId));\r
- //amount\r
- Utils.writeVarint(output.amount, outputCommitSteam);\r
- //vmVersion\r
- Utils.writeVarint(1, outputCommitSteam); //db中获取vm_version\r
- //controlProgram\r
- Utils.writeVarStr(Hex.decode(output.controlProgram), outputCommitSteam);\r
-\r
- byte[] dataOutputCommit = outputCommitSteam.toByteArray();\r
- //outputCommit的length\r
- Utils.writeVarint(dataOutputCommit.length, stream);\r
- stream.write(dataOutputCommit);\r
-\r
- //outputWitness\r
- Utils.writeVarint(0, stream);\r
- }\r
- }\r
-\r
- byte[] data = stream.toByteArray();\r
- txSign = Hex.toHexString(data);\r
-\r
- } catch (IOException e) {\r
- throw new RuntimeException(e);\r
- }\r
- return txSign;\r
- }\r
-\r
- public Integer getTransactionSize(Transaction tx) {\r
- String result = serializeTransaction(tx);\r
- return Hex.decode(result).length;\r
- }\r
-\r
- //签名组装witness\r
- public Transaction buildWitness(Transaction transaction, int index, byte[] privateKeyBytes) {\r
-\r
- Transaction.AnnotatedInput input = transaction.inputs.get(index);\r
- if (null == input.witnessComponent)\r
-\r
- if (null != input) {\r
- try {\r
- input.witnessComponent = new Transaction.InputWitnessComponent();\r
- byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(transaction.txID));\r
- byte[] expandedPrivateKey = ExpandedPrivateKey.ExpandedPrivateKey(privateKeyBytes);\r
- byte[] sig = Signer.Ed25519InnerSign(expandedPrivateKey, message);\r
-\r
- switch (input.type) {\r
- case 1: {\r
- input.witnessComponent.signatures = new String[2];\r
- input.witnessComponent.signatures[0] = Hex.toHexString(sig);\r
- byte[] deriveXpub = DeriveXpub.deriveXpub(privateKeyBytes);\r
- String pubKey = Hex.toHexString(deriveXpub).substring(0, 64);\r
- input.witnessComponent.signatures[1] = pubKey;\r
- break;\r
- }\r
- case 0: {\r
- input.witnessComponent.signatures = new String[1];\r
- input.witnessComponent.signatures[0] = Hex.toHexString(sig);\r
- break;\r
- }\r
- }\r
-\r
- } catch (Exception e) {\r
- throw new RuntimeException(e);\r
- }\r
- } else {\r
- System.out.println("build witness failed.");\r
- }\r
- return transaction;\r
- }\r
-\r
- //多签单输入\r
- public void buildWitness(Transaction transaction, int index, String[] privateKeys) {\r
- Transaction.AnnotatedInput input = transaction.inputs.get(index);\r
- if (null == input.witnessComponent)\r
- input.witnessComponent = new Transaction.InputWitnessComponent();\r
- try {\r
-\r
- //TODO 多签issue\r
- StringBuilder sb = new StringBuilder(OP_TXSIGHASH);\r
- input.witnessComponent.signatures = new String[privateKeys.length + 1];\r
- for (int i = 0; i < privateKeys.length; i++) {\r
- System.out.println(input.isChange());\r
- byte[] key = DerivePrivateKey.derivePrivateKey(privateKeys[i], 1, input.isChange(), input.getControlProgramIndex());\r
- byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(transaction.txID));\r
- byte[] expandedPrivateKey = ExpandedPrivateKey.ExpandedPrivateKey(key);\r
- byte[] sig = Signer.Ed25519InnerSign(expandedPrivateKey, message);\r
- input.witnessComponent.signatures[i] = Hex.toHexString(sig);\r
- byte[] deriveXpub = DeriveXpub.deriveXpub(expandedPrivateKey);\r
- String publicKey = Hex.toHexString(deriveXpub).substring(0, 64);\r
- sb.append(Integer.toString(Hex.decode(publicKey).length,16)).append(publicKey);\r
- }\r
- //TODO 签名数跟公钥数量不同\r
- sb.append(Utils.pushDataInt(privateKeys.length)); //should be nrequired\r
- sb.append(Utils.pushDataInt(privateKeys.length));\r
- sb.append(OP_CHECKMULTISIG);\r
- input.witnessComponent.signatures[privateKeys.length] = sb.toString();\r
-\r
- } catch (Exception e) {\r
- throw new RuntimeException(e);\r
- }\r
-\r
- }\r
-\r
- public static byte[] hashFn(byte[] hashedInputHex, byte[] txID) {\r
-\r
- SHA3.Digest256 digest256 = new SHA3.Digest256();\r
- // data = hashedInputHex + txID\r
- ByteArrayOutputStream out = new ByteArrayOutputStream();\r
- out.write(hashedInputHex, 0, hashedInputHex.length);\r
- out.write(txID, 0, txID.length);\r
- byte[] data = out.toByteArray();\r
- return digest256.digest(data);\r
- }\r
-\r
-\r
- public Transaction generateSignatures(Transaction Transaction, BigInteger[] keys) {\r
- Transaction.AnnotatedInput input = Transaction.inputs.get(0);\r
- input.witnessComponent.signatures = new String[keys.length];\r
- for (int i = 0; i < keys.length; i++) {\r
- if (null != input) {\r
- try {\r
- byte[] message = hashFn(Hex.decode(input.inputID), Hex.decode(Transaction.txID));\r
- byte[] expandedPrv = Utils.BigIntegerToBytes(keys[i]);\r
- byte[] priKey = ExpandedPrivateKey.ExpandedPrivateKey(expandedPrv);\r
- byte[] sig = Signer.Ed25519InnerSign(priKey, message);\r
- input.witnessComponent.signatures[i] = Hex.toHexString(sig);\r
-\r
- } catch (Exception e) {\r
- e.printStackTrace();\r
- }\r
- } else {\r
- System.out.println("generate signatures failed.");\r
- }\r
- }\r
- return Transaction;\r
- }\r
-\r
- //根据主私钥对交易签名,生成序列化的rawTransaction\r
- public String rawTransaction(String rootPrivateKey, Transaction tx) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
- byte[] privateChild;\r
- for (int i = 0; i < tx.inputs.size(); i++) {\r
- if (tx.inputs.get(i).type == 1) {\r
- privateChild = DerivePrivateKey.derivePrivateKey(rootPrivateKey, 1, tx.inputs.get(i).isChange(), tx.inputs.get(i).getControlProgramIndex());\r
- } else {\r
- privateChild = DerivePrivateKey.derivePrivateKey(rootPrivateKey, tx.inputs.get(i).getKeyIndex());\r
- }\r
- buildWitness(tx, i, privateChild);\r
- }\r
- return serializeTransaction(tx);\r
- }\r
-\r
- //多签\r
- public String rawTransaction(String[] rootPrivateKey, Transaction tx) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {\r
- String privateChild;\r
- for (int i = 0; i < tx.inputs.size(); i++) {\r
- if (tx.inputs.get(i).controlProgram.length() != 44) {\r
- buildWitness(tx, i, rootPrivateKey);\r
- }\r
-\r
- }\r
- return serializeTransaction(tx);\r
- }\r
-\r
-\r
-}\r
-\r
-\r