diff --git a/.idea/gradle.xml b/.idea/gradle.xml index aa573cb..224680d 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,5 +1,6 @@ + diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index a5f05cd..2370474 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -21,5 +21,10 @@ \ No newline at end of file diff --git a/SignSdk/build.gradle b/SignSdk/build.gradle index 0a913a1..68c7be2 100644 --- a/SignSdk/build.gradle +++ b/SignSdk/build.gradle @@ -25,9 +25,6 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) - implementation 'androidx.appcompat:appcompat:1.2.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + implementation 'com.github.alibaba:fastjson:1.2.73' } \ No newline at end of file diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/bean/AccountBean.java b/SignSdk/src/main/java/com/onlychain/signsdk/bean/AccountBean.java new file mode 100644 index 0000000..6a89649 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/bean/AccountBean.java @@ -0,0 +1,41 @@ +package com.onlychain.signsdk.bean; + + +import com.onlychain.signsdk.utils.OcMath; + +public class AccountBean { + private String privateKey; + private String publicKey; + private String address; + + public String getPrivateKey() { + return privateKey; + } + public byte[] getPrivateKeyBin() { + return OcMath.hexStringToByteArray(privateKey); + } + + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + } + + public String getPublicKey() { + return publicKey; + } + public byte[] getPublicKeyBin() { + return OcMath.hexStringToByteArray(publicKey); + } + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getAddress() { + return address; + } + public String getAddressNoPrefix() { + return address.substring(2); + } + public void setAddress(String address) { + this.address = "oc"+address; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/bean/ActionsBean.java b/SignSdk/src/main/java/com/onlychain/signsdk/bean/ActionsBean.java new file mode 100644 index 0000000..c3ca777 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/bean/ActionsBean.java @@ -0,0 +1,302 @@ +package com.onlychain.signsdk.bean; + +import java.util.List; + +public class ActionsBean { + private String version; + private String actionType; + private String hasTrading; + private String hasAction; + private TradingBean trading; + private ActionBean action; + private long time; + private long createdBlock; + private String originator; + private String insLength; + private String ins; + private String actionSign; + private String txId; + + public String getInsLength() { + return insLength; + } + + public void setInsLength(String insLength) { + this.insLength = insLength; + } + + public String getHasAction() { + return hasAction; + } + + public void setHasAction(String hasAction) { + this.hasAction = hasAction; + } + + public String getHasTrading() { + return hasTrading; + } + + public void setHasTrading(String hasTrading) { + this.hasTrading = hasTrading; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getActionType() { + return actionType; + } + + public void setActionType(String actionType) { + this.actionType = actionType; + } + + public TradingBean getTrading() { + return trading; + } + + public void setTrading(TradingBean trading) { + this.trading = trading; + } + + public ActionBean getAction() { + return action; + } + + public void setAction(ActionBean action) { + this.action = action; + } + + public long getTime() { + return time; + } + + public void setTime(long time) { + this.time = time; + } + + public long getCreatedBlock() { + return createdBlock; + } + + public void setCreatedBlock(long createdBlock) { + this.createdBlock = createdBlock; + } + + public String getOriginator() { + return originator; + } + + public void setOriginator(String originator) { + this.originator = originator; + } + + public String getIns() { + return ins; + } + + public void setIns(String ins) { + this.ins = ins; + } + + public String getActionSign() { + return actionSign; + } + + public void setActionSign(String actionSign) { + this.actionSign = actionSign; + } + + public String getTxId() { + return txId; + } + + public void setTxId(String txId) { + this.txId = txId; + } + + public static class TradingBean { + + private long lockTime; + private long cost; + private long vinNum; + private long voutNum; + private List vin; + private List vout; + + + public long getVinNum() { + return vinNum; + } + + public void setVinNum(long vinNum) { + this.vinNum = vinNum; + } + + public long getVoutNum() { + return voutNum; + } + + public void setVoutNum(long voutNum) { + this.voutNum = voutNum; + } + + public long getLockTime() { + return lockTime; + } + + public void setLockTime(long lockTime) { + this.lockTime = lockTime; + } + + public long getCost() { + return cost; + } + + public void setCost(long cost) { + this.cost = cost; + } + + public List getVin() { + return vin; + } + + public void setVin(List vin) { + this.vin = vin; + } + + public List getVout() { + return vout; + } + + public void setVout(List vout) { + this.vout = vout; + } + + public static class VinBean { + private String txId; + private String n; + private int scriptLength; + private String scriptSig; + + public int getScriptLength() { + return scriptLength; + } + + public void setScriptLength(int scriptLength) { + this.scriptLength = scriptLength; + } + + public String getTxId() { + return txId; + } + + public void setTxId(String txId) { + this.txId = txId; + } + + public String getN() { + return n; + } + + public void setN(String n) { + this.n = n; + } + + public String getScriptSig() { + return scriptSig; + } + + public void setScriptSig(String scriptSig) { + this.scriptSig = scriptSig; + } + } + + public static class VoutBean { + + private long value; + private String n; + private String reqSigs; + private String address; + + public String getN() { + return n; + } + + public void setN(String n) { + this.n = n; + } + + public long getValue() { + return value; + } + + public void setValue(long value) { + this.value = value; + } + + + + public String getReqSigs() { + return reqSigs; + } + + public void setReqSigs(String reqSigs) { + this.reqSigs = reqSigs; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + } + } + + public static class ActionBean { + private String voter; + private int again; + private int rounds; + private List candidate; + + public String getVoter() { + return voter; + } + + public void setVoter(String voter) { + this.voter = voter; + } + + public int getAgain() { + return again; + } + + public void setAgain(int again) { + this.again = again; + } + + public int getRounds() { + return rounds; + } + + public void setRounds(int rounds) { + this.rounds = rounds; + } + + public List getCandidate() { + return candidate; + } + + public void setCandidate(List candidate) { + this.candidate = candidate; + } + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/bean/AnalysisBean.java b/SignSdk/src/main/java/com/onlychain/signsdk/bean/AnalysisBean.java new file mode 100644 index 0000000..8a1658b --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/bean/AnalysisBean.java @@ -0,0 +1,209 @@ +package com.onlychain.signsdk.bean; + +import java.util.List; + +public class AnalysisBean { + + private String version; + private String actionType; + private TradingBean trading; + private String time; + private String createdBlock; + private String originator; + private String ins; + private String actionSign; + private String txId; + private List action; + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getActionType() { + return actionType; + } + + public void setActionType(String actionType) { + this.actionType = actionType; + } + + public TradingBean getTrading() { + return trading; + } + + public void setTrading(TradingBean trading) { + this.trading = trading; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public String getCreatedBlock() { + return createdBlock; + } + + public void setCreatedBlock(String createdBlock) { + this.createdBlock = createdBlock; + } + + public String getOriginator() { + return originator; + } + + public void setOriginator(String originator) { + this.originator = originator; + } + + public String getIns() { + return ins; + } + + public void setIns(String ins) { + this.ins = ins; + } + + public String getActionSign() { + return actionSign; + } + + public void setActionSign(String actionSign) { + this.actionSign = actionSign; + } + + public String getTxId() { + return txId; + } + + public void setTxId(String txId) { + this.txId = txId; + } + + public List getAction() { + return action; + } + + public void setAction(List action) { + this.action = action; + } + + public static class TradingBean { + + private String lockTime; + private String cost; + private List vin; + private List vout; + + public String getLockTime() { + return lockTime; + } + + public void setLockTime(String lockTime) { + this.lockTime = lockTime; + } + + public String getCost() { + return cost; + } + + public void setCost(String cost) { + this.cost = cost; + } + + public List getVin() { + return vin; + } + + public void setVin(List vin) { + this.vin = vin; + } + + public List getVout() { + return vout; + } + + public void setVout(List vout) { + this.vout = vout; + } + + public static class VinBean { + + + private String txId; + private String n; + private String scriptSig; + + public String getTxId() { + return txId; + } + + public void setTxId(String txId) { + this.txId = txId; + } + + public String getN() { + return n; + } + + public void setN(String n) { + this.n = n; + } + + public String getScriptSig() { + return scriptSig; + } + + public void setScriptSig(String scriptSig) { + this.scriptSig = scriptSig; + } + } + + public static class VoutBean { + + private String value; + private String n; + private String reqSigs; + private String address; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getN() { + return n; + } + + public void setN(String n) { + this.n = n; + } + + public String getReqSigs() { + return reqSigs; + } + + public void setReqSigs(String reqSigs) { + this.reqSigs = reqSigs; + } + + public String getAddress() { + return "oc"+address; + } + + public void setAddress(String address) { + this.address = address; + } + } + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/bean/BaseActionBean.java b/SignSdk/src/main/java/com/onlychain/signsdk/bean/BaseActionBean.java new file mode 100644 index 0000000..601cd53 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/bean/BaseActionBean.java @@ -0,0 +1,101 @@ +package com.onlychain.signsdk.bean; + + +import com.onlychain.signsdk.crypto.Hash; +import com.onlychain.signsdk.secp256k1.PublicKey; +import com.onlychain.signsdk.secp256k1.Secp256k1; +import com.onlychain.signsdk.secp256k1.Signature; +import com.onlychain.signsdk.utils.OcMath; +import com.onlychain.signsdk.utils.WalletUtils; + +public class BaseActionBean { + private String message; + private String sigStr; + private String txid; + private String commitData; + + /** + * s使用公钥验证消息的真实性 + * @param publicKeyBin + * @return + */ + public boolean checkSig(byte[] publicKeyBin){ + if (Secp256k1.verify(PublicKey.parse(publicKeyBin), Hash.sha256(Hash.sha256(OcMath.hexStringToByteArray(message))), Signature.parse(OcMath.hexStringToByteArray(sigStr)))) + return true; + else + return false; + } + + /** + * 验证自定义消息 + * @param publicKeyBin + * @param message + * @param sigStr + * @return + */ + public boolean checkApiSign(byte[] publicKeyBin,String message,String sigStr){ + if (Secp256k1.verify(PublicKey.parse(publicKeyBin), Hash.sha256(Hash.sha256(message.getBytes())), Signature.parse(OcMath.hexStringToByteArray(sigStr)))) + return true; + else + return false; + } + + public boolean checkSign(byte[] publicKeyBin,String message,String sigStr){ + if (Secp256k1.verify(PublicKey.parse(publicKeyBin), Hash.sha256(Hash.sha256(OcMath.hexStringToByteArray(message))), Signature.parse(OcMath.hexStringToByteArray(sigStr)))) + return true; + else + return false; + } + + + /** + * 使用私钥将对应公钥与自定义消息进行加签 + * @param privateKeyBin + * @param message + * @return + */ + public String makeSign(byte[] privateKeyBin,String message){ + return OcMath.toHexStringNoPrefix(Secp256k1.sign(privateKeyBin,Hash.sha256(Hash.sha256((message).getBytes()))).serialize()); + } + + public String getCommitData() { + return message+sigStr; + } + + public void setCommitData(String commitData) { + this.commitData = commitData; + } + + public void setMessage(String message) { + this.message = message; + } + + + public String getMessage() { + return message; + } + + + public String getTxid() { + return WalletUtils.getTxId(getCommitData()); + } + + public byte[] getTxidBin() { + return WalletUtils.getTxIdBin(getCommitData()); + } + + public String getSignStr() { + return sigStr; + } + + public void setSignStr(String sigStr) { + this.sigStr = sigStr; + } + + public void setTxid(String txid) { + this.txid = txid; + } + + +} + diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/bean/BlockBean.java b/SignSdk/src/main/java/com/onlychain/signsdk/bean/BlockBean.java new file mode 100644 index 0000000..009cb66 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/bean/BlockBean.java @@ -0,0 +1,119 @@ +package com.onlychain.signsdk.bean; + +import java.util.List; + +public class BlockBean { + + private int code; + private RecordBean record; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public RecordBean getRecord() { + return record; + } + + public void setRecord(RecordBean record) { + this.record = record; + } + + public static class RecordBean { + + private String parentHash; + private String merkleRoot; + private int version; + private int thisTime; + private int height; + private int tradingNum; + private String signature; + private String headHash; + private String blockSign; + private List tradingInfo; + + public String getParentHash() { + return parentHash; + } + + public void setParentHash(String parentHash) { + this.parentHash = parentHash; + } + + public String getMerkleRoot() { + return merkleRoot; + } + + public void setMerkleRoot(String merkleRoot) { + this.merkleRoot = merkleRoot; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public int getThisTime() { + return thisTime; + } + + public void setThisTime(int thisTime) { + this.thisTime = thisTime; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public int getTradingNum() { + return tradingNum; + } + + public void setTradingNum(int tradingNum) { + this.tradingNum = tradingNum; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getHeadHash() { + return headHash; + } + + public void setHeadHash(String headHash) { + this.headHash = headHash; + } + + public String getBlockSign() { + return blockSign; + } + + public void setBlockSign(String blockSign) { + this.blockSign = blockSign; + } + + public List getTradingInfo() { + return tradingInfo; + } + + public void setTradingInfo(List tradingInfo) { + this.tradingInfo = tradingInfo; + } + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/bean/EndBean.java b/SignSdk/src/main/java/com/onlychain/signsdk/bean/EndBean.java new file mode 100644 index 0000000..f6d5d46 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/bean/EndBean.java @@ -0,0 +1,40 @@ +package com.onlychain.signsdk.bean; + + + +import com.onlychain.signsdk.utils.Leb128Utils; +import com.onlychain.signsdk.utils.OcMath; + +import java.math.BigInteger; +import java.sql.Time; + +public class EndBean { + private String lockTime=""; + private String coat=""; + private String isAcitonLabel=""; + private String time=""; + private String height=""; + private String insSize=""; + private String ins=""; + private String publicKey=""; + private String sig=""; + + public EndBean(String height, String publicKey, String sig) { + this.lockTime = Leb128Utils.encodeUleb128(0); + this.coat = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(0)),16); + this.isAcitonLabel = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(0)),2);; + this.time = Leb128Utils.encodeUleb128(System.currentTimeMillis());; + this.height = Leb128Utils.encodeUleb128(height); + this.ins = "00"; + this.insSize = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(OcMath.hexStringToByteArray(this.ins).length)),2); + this.publicKey = publicKey; + this.sig = sig; + } + + + @Override + public String toString() { + StringBuffer result=new StringBuffer(); + return result.append(lockTime).append(coat).append(isAcitonLabel).append(time).append(height).append(publicKey).append(ins).append(insSize).append(sig).toString(); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/bean/GetSystemInfoBean.java b/SignSdk/src/main/java/com/onlychain/signsdk/bean/GetSystemInfoBean.java new file mode 100644 index 0000000..c21042b --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/bean/GetSystemInfoBean.java @@ -0,0 +1,96 @@ +package com.onlychain.signsdk.bean; + +public class GetSystemInfoBean { + + + /** + * code : 200 + * record : {"rounds":34639,"sysTime":4364508,"thisTime":120,"blockHash":"47ec88cd3139b691ebef0e2e28f1692e1dccbca8acce002a7ef149251d4ff53f","blockHeight":1969474,"serverName":"f87c505b8b2ee130e38986c703b4e94cbdaadd04"} + */ + + private int code; + private RecordBean record; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public RecordBean getRecord() { + return record; + } + + public void setRecord(RecordBean record) { + this.record = record; + } + + public static class RecordBean { + /** + * rounds : 34639 + * sysTime : 4364508 + * thisTime : 120 + * blockHash : 47ec88cd3139b691ebef0e2e28f1692e1dccbca8acce002a7ef149251d4ff53f + * blockHeight : 1969474 + * serverName : f87c505b8b2ee130e38986c703b4e94cbdaadd04 + */ + + private int rounds; + private int sysTime; + private int thisTime; + private String blockHash; + private long blockHeight; + private String serverName; + + public int getRounds() { + return rounds; + } + + public void setRounds(int rounds) { + this.rounds = rounds; + } + + public int getSysTime() { + return sysTime; + } + + public void setSysTime(int sysTime) { + this.sysTime = sysTime; + } + + public int getThisTime() { + return thisTime; + } + + public void setThisTime(int thisTime) { + this.thisTime = thisTime; + } + + public String getBlockHash() { + return blockHash; + } + + public void setBlockHash(String blockHash) { + this.blockHash = blockHash; + } + + public long getBlockHeight() { + return blockHeight; + } + + public void setBlockHeight(long blockHeight) { + this.blockHeight = blockHeight; + } + + public String getServerName() { + return serverName; + } + + public void setServerName(String serverName) { + this.serverName = serverName; + } + } +} + diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/bean/HeadBean.java b/SignSdk/src/main/java/com/onlychain/signsdk/bean/HeadBean.java new file mode 100644 index 0000000..c33d22e --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/bean/HeadBean.java @@ -0,0 +1,81 @@ +package com.onlychain.signsdk.bean; + + + +import com.onlychain.signsdk.utils.Leb128Utils; +import com.onlychain.signsdk.utils.OcMath; + +import java.math.BigInteger; +import java.util.Calendar; +import java.util.Date; + +public class HeadBean { + private String version; + private String actionType; + private String hasTranfer; + private String Body; + + public HeadBean(int version, int actionType, int hasTranfer,String Body) { + this.Body=Body; + this.version = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(version)),2); + this.actionType = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(actionType)),2); + this.hasTranfer = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(hasTranfer)),2); + } + + public HeadBean(int actionType,String Body) { + this.Body=Body; + this.version = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(1)),2); + this.actionType = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(actionType)),2); + this.hasTranfer = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(1)),2); + } + + @Override + public String toString() { + StringBuffer result=new StringBuffer(); + + return result.append(version).append(actionType).append(hasTranfer).append(Body).toString(); + } + + + public static class EndBean { + + private String lockTime = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(Leb128Utils.encodeUleb128(0)),2); + private String coat=""; + private String isAcitonLabel=""; + private String time=""; + private String height=""; + private String insSize=""; + private String ins=""; + private String publicKey=""; + private String head=""; + + public EndBean(String height, String publicKey, String head) { + this.coat = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(0)),16); + this.isAcitonLabel = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(0)),2);; + this.time = Leb128Utils.encodeUleb128(String.valueOf(System.currentTimeMillis()/1000));; + this.height = Leb128Utils.encodeUleb128(height); + this.ins = "00"; + this.insSize = OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(OcMath.hexStringToByteArray(this.ins).length)),2); + this.publicKey = publicKey; + this.head = head; + } + + public void setLockTimeAdd1Year(String height) { + long lockHeight=Long.valueOf(height)+15768001l; + this.lockTime = Leb128Utils.encodeUleb128(lockHeight); + } + + public void setLockTimeForCoin(long lockHeight) { + this.lockTime = Leb128Utils.encodeUleb128(lockHeight); + } + + + public String getResult() { + StringBuffer result=new StringBuffer(); + + + return head+result.append(lockTime).append(coat).append(isAcitonLabel).append(time).append(height).append(publicKey).append(insSize).append(ins).toString(); + } + } + +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/bean/OutBean.java b/SignSdk/src/main/java/com/onlychain/signsdk/bean/OutBean.java new file mode 100644 index 0000000..2b87f2d --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/bean/OutBean.java @@ -0,0 +1,31 @@ +package com.onlychain.signsdk.bean; + +public class OutBean { + private long value; + private String address; + + public OutBean(long value, String address) { + this.value = value; + this.address = address; + } + public OutBean() { + + } + public long getValue() { + return value; + } + + public void setValue(long value) { + this.value = value; + } + + public String getAddress() { + if(address.length()==42) + address=address.substring(2); + return address; + } + + public void setAddress(String address) { + this.address = address; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/bean/PurseBean.java b/SignSdk/src/main/java/com/onlychain/signsdk/bean/PurseBean.java new file mode 100644 index 0000000..64d1a8b --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/bean/PurseBean.java @@ -0,0 +1,116 @@ +package com.onlychain.signsdk.bean; + +import java.util.List; + +public class PurseBean { + + /** + * code : 200 + * record : [{"txId":"2cdbff7cd8cf9e2df0ba114aa5b40ace73166899f63b1df1ff0355e119bfb030","n":0,"value":29089999999,"reqSigs":"76a914e18da4602d2615ce76b45f6ae9cd3df041e2debc88ac","createdBlock":1173178,"lockTime":1173178,"address":"e18da4602d2615ce76b45f6ae9cd3df041e2debc","actionType":1},{"txId":"2ffb849b6fbefe254a0af0d30ca3a8d411b4823b33c1dc2dafbc2d4bccda045d","n":8,"value":124806000000,"reqSigs":"76a914e18da4602d2615ce76b45f6ae9cd3df041e2debc88ac","createdBlock":1545253,"lockTime":1545253,"address":"e18da4602d2615ce76b45f6ae9cd3df041e2debc","actionType":1},{"txId":"39320abc22cf631b5dae8c79cd3d9c2f76932ba569da1a182969ecc22f308ae5","n":11,"value":47058000000,"reqSigs":"76a914e18da4602d2615ce76b45f6ae9cd3df041e2debc88ac","createdBlock":1089849,"lockTime":1089849,"address":"e18da4602d2615ce76b45f6ae9cd3df041e2debc","actionType":1},{"txId":"44811a3b160ab2488dbaf62188ab47d39a0f76a5dcdaf2c873ae8e5408b35171","n":0,"value":10000000000,"reqSigs":"76a914e18da4602d2615ce76b45f6ae9cd3df041e2debc88ac","createdBlock":710684,"lockTime":16478684,"address":"e18da4602d2615ce76b45f6ae9cd3df041e2debc","actionType":4},{"txId":"56f2e628a63ba91089313d443bcd76852f0cc7653613d81ccaeec08d99207cf6","n":0,"value":18972000000,"reqSigs":"76a914e18da4602d2615ce76b45f6ae9cd3df041e2debc88ac","createdBlock":1175829,"lockTime":1175829,"address":"e18da4602d2615ce76b45f6ae9cd3df041e2debc","actionType":1},{"txId":"6413cfc451092a9497f6b743595831d5cd6387056226af5a99e0d781b3c9184a","n":13,"value":45542100000,"reqSigs":"76a914e18da4602d2615ce76b45f6ae9cd3df041e2debc88ac","createdBlock":1025399,"lockTime":1025399,"address":"e18da4602d2615ce76b45f6ae9cd3df041e2debc","actionType":1},{"txId":"6a1ea1b8cadfd83a48b8b768502c4317bf00247058863aa7db49a81c2b5063cf","n":11,"value":214923000000,"reqSigs":"76a914e18da4602d2615ce76b45f6ae9cd3df041e2debc88ac","createdBlock":1324315,"lockTime":1324315,"address":"e18da4602d2615ce76b45f6ae9cd3df041e2debc","actionType":1},{"txId":"9bde6e43707a6ed960894f0194dd835a74de401cff64c2aeaf3bf6a55b487c4f","n":5,"value":121179000000,"reqSigs":"76a914e18da4602d2615ce76b45f6ae9cd3df041e2debc88ac","createdBlock":1056809,"lockTime":1056809,"address":"e18da4602d2615ce76b45f6ae9cd3df041e2debc","actionType":1},{"txId":"ab0c8039141e6ea86c8ca4b444b7913a322c2e3d2f6c253b701552f0c1f1ea99","n":6,"value":28737000000,"reqSigs":"76a914e18da4602d2615ce76b45f6ae9cd3df041e2debc88ac","createdBlock":1056790,"lockTime":1056790,"address":"e18da4602d2615ce76b45f6ae9cd3df041e2debc","actionType":1}] + */ + + private int code; + private List record; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getRecord() { + return record; + } + + public void setRecord(List record) { + this.record = record; + } + + public static class RecordBean { + /** + * txId : 2cdbff7cd8cf9e2df0ba114aa5b40ace73166899f63b1df1ff0355e119bfb030 + * n : 0 + * value : 29089999999 + * reqSigs : 76a914e18da4602d2615ce76b45f6ae9cd3df041e2debc88ac + * createdBlock : 1173178 + * lockTime : 1173178 + * address : e18da4602d2615ce76b45f6ae9cd3df041e2debc + * actionType : 1 + */ + + private String txId; + private int n; + private long value; + private String reqSigs; + private long createdBlock; + private long lockTime; + private String address; + private int actionType; + + public String getTxId() { + return txId; + } + + public void setTxId(String txId) { + this.txId = txId; + } + + public int getN() { + return n; + } + + public void setN(int n) { + this.n = n; + } + + public long getValue() { + return value; + } + + public void setValue(long value) { + this.value = value; + } + + public String getReqSigs() { + return reqSigs; + } + + public void setReqSigs(String reqSigs) { + this.reqSigs = reqSigs; + } + + public long getCreatedBlock() { + return createdBlock; + } + + public void setCreatedBlock(long createdBlock) { + this.createdBlock = createdBlock; + } + + public long getLockTime() { + return lockTime; + } + + public void setLockTime(long lockTime) { + this.lockTime = lockTime; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public int getActionType() { + return actionType; + } + + public void setActionType(int actionType) { + this.actionType = actionType; + } + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/crypto/Hash.java b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/Hash.java new file mode 100644 index 0000000..bd6caeb --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/Hash.java @@ -0,0 +1,18 @@ +package com.onlychain.signsdk.crypto; + + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class Hash { + private Hash() { } + + public static byte[] sha256(byte[] input) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + return digest.digest(input); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Couldn't find a SHA-256 provider", e); + } + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/crypto/RIPEMD160Digest.java b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/RIPEMD160Digest.java new file mode 100644 index 0000000..3bc350e --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/RIPEMD160Digest.java @@ -0,0 +1,433 @@ +package com.onlychain.signsdk.crypto; + + +import com.onlychain.signsdk.crypto.base.GeneralDigest; +import com.onlychain.signsdk.crypto.base.Memoable; + +public class RIPEMD160Digest extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 20; + + private int H0, H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + + public RIPEMD160Digest() + { + reset(); + } + + public RIPEMD160Digest(RIPEMD160Digest t) + { + super(t); + + copyIn(t); + } + + private void copyIn(RIPEMD160Digest t) + { + super.copyIn(t); + + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "RIPEMD160"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H0, out, outOff); + unpackWord(H1, out, outOff + 4); + unpackWord(H2, out, outOff + 8); + unpackWord(H3, out, outOff + 12); + unpackWord(H4, out, outOff + 16); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H0 = 0x67452301; + H1 = 0xefcdab89; + H2 = 0x98badcfe; + H3 = 0x10325476; + H4 = 0xc3d2e1f0; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * f1,f2,f3,f4,f5 are the basic RIPEMD160 functions. + */ + + /* + * rounds 0-15 + */ + private int f1( + int x, + int y, + int z) + { + return x ^ y ^ z; + } + + /* + * rounds 16-31 + */ + private int f2( + int x, + int y, + int z) + { + return (x & y) | (~x & z); + } + + /* + * rounds 32-47 + */ + private int f3( + int x, + int y, + int z) + { + return (x | ~y) ^ z; + } + + /* + * rounds 48-63 + */ + private int f4( + int x, + int y, + int z) + { + return (x & z) | (y & ~z); + } + + /* + * rounds 64-79 + */ + private int f5( + int x, + int y, + int z) + { + return x ^ (y | ~z); + } + + protected void processBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + int e, ee; + + a = aa = H0; + b = bb = H1; + c = cc = H2; + d = dd = H3; + e = ee = H4; + + // + // Rounds 1 - 16 + // + // left + a = RL(a + f1(b,c,d) + X[ 0], 11) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[ 1], 14) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[ 2], 15) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[ 3], 12) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[ 4], 5) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[ 5], 8) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[ 6], 7) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[ 7], 9) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[ 8], 11) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[ 9], 13) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[10], 14) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[11], 15) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[12], 6) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[13], 7) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[14], 9) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[15], 8) + e; c = RL(c, 10); + + // right + aa = RL(aa + f5(bb,cc,dd) + X[ 5] + 0x50a28be6, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[14] + 0x50a28be6, 9) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 7] + 0x50a28be6, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[ 0] + 0x50a28be6, 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 9] + 0x50a28be6, 13) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[ 2] + 0x50a28be6, 15) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[11] + 0x50a28be6, 15) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 4] + 0x50a28be6, 5) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[13] + 0x50a28be6, 7) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 6] + 0x50a28be6, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[15] + 0x50a28be6, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[ 8] + 0x50a28be6, 11) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 1] + 0x50a28be6, 14) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[10] + 0x50a28be6, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 3] + 0x50a28be6, 12) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[12] + 0x50a28be6, 6) + ee; cc = RL(cc, 10); + + // + // Rounds 16-31 + // + // left + e = RL(e + f2(a,b,c) + X[ 7] + 0x5a827999, 7) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[ 4] + 0x5a827999, 6) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[13] + 0x5a827999, 8) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[ 1] + 0x5a827999, 13) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[10] + 0x5a827999, 11) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 6] + 0x5a827999, 9) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[15] + 0x5a827999, 7) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[ 3] + 0x5a827999, 15) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[12] + 0x5a827999, 7) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[ 0] + 0x5a827999, 12) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 9] + 0x5a827999, 15) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[ 5] + 0x5a827999, 9) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[ 2] + 0x5a827999, 11) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[14] + 0x5a827999, 7) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[11] + 0x5a827999, 13) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 8] + 0x5a827999, 12) + d; b = RL(b, 10); + + // right + ee = RL(ee + f4(aa,bb,cc) + X[ 6] + 0x5c4dd124, 9) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[11] + 0x5c4dd124, 13) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[ 3] + 0x5c4dd124, 15) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[ 7] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[ 0] + 0x5c4dd124, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[13] + 0x5c4dd124, 8) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[ 5] + 0x5c4dd124, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[10] + 0x5c4dd124, 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[14] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[15] + 0x5c4dd124, 7) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[ 8] + 0x5c4dd124, 12) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[12] + 0x5c4dd124, 7) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[ 4] + 0x5c4dd124, 6) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[ 9] + 0x5c4dd124, 15) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[ 1] + 0x5c4dd124, 13) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[ 2] + 0x5c4dd124, 11) + dd; bb = RL(bb, 10); + + // + // Rounds 32-47 + // + // left + d = RL(d + f3(e,a,b) + X[ 3] + 0x6ed9eba1, 11) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[10] + 0x6ed9eba1, 13) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[14] + 0x6ed9eba1, 6) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[ 4] + 0x6ed9eba1, 7) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 9] + 0x6ed9eba1, 14) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[15] + 0x6ed9eba1, 9) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[ 8] + 0x6ed9eba1, 13) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[ 1] + 0x6ed9eba1, 15) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[ 2] + 0x6ed9eba1, 14) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 7] + 0x6ed9eba1, 8) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[ 0] + 0x6ed9eba1, 13) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[ 6] + 0x6ed9eba1, 6) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[13] + 0x6ed9eba1, 5) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[11] + 0x6ed9eba1, 12) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 5] + 0x6ed9eba1, 7) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[12] + 0x6ed9eba1, 5) + c; a = RL(a, 10); + + // right + dd = RL(dd + f3(ee,aa,bb) + X[15] + 0x6d703ef3, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 5] + 0x6d703ef3, 7) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[ 1] + 0x6d703ef3, 15) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[ 3] + 0x6d703ef3, 11) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 7] + 0x6d703ef3, 8) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[14] + 0x6d703ef3, 6) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 6] + 0x6d703ef3, 6) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[ 9] + 0x6d703ef3, 14) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[11] + 0x6d703ef3, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 8] + 0x6d703ef3, 13) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[12] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 2] + 0x6d703ef3, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[10] + 0x6d703ef3, 13) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[ 0] + 0x6d703ef3, 13) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 4] + 0x6d703ef3, 7) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[13] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10); + + // + // Rounds 48-63 + // + // left + c = RL(c + f4(d,e,a) + X[ 1] + 0x8f1bbcdc, 11) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[ 9] + 0x8f1bbcdc, 12) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[11] + 0x8f1bbcdc, 14) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[10] + 0x8f1bbcdc, 15) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 0] + 0x8f1bbcdc, 14) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 8] + 0x8f1bbcdc, 15) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[12] + 0x8f1bbcdc, 9) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[ 4] + 0x8f1bbcdc, 8) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[13] + 0x8f1bbcdc, 9) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 3] + 0x8f1bbcdc, 14) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 7] + 0x8f1bbcdc, 5) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[15] + 0x8f1bbcdc, 6) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[14] + 0x8f1bbcdc, 8) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[ 5] + 0x8f1bbcdc, 6) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 6] + 0x8f1bbcdc, 5) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 2] + 0x8f1bbcdc, 12) + b; e = RL(e, 10); + + // right + cc = RL(cc + f2(dd,ee,aa) + X[ 8] + 0x7a6d76e9, 15) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[ 6] + 0x7a6d76e9, 5) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 4] + 0x7a6d76e9, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 1] + 0x7a6d76e9, 11) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[ 3] + 0x7a6d76e9, 14) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[11] + 0x7a6d76e9, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[15] + 0x7a6d76e9, 6) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 0] + 0x7a6d76e9, 14) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 5] + 0x7a6d76e9, 6) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[12] + 0x7a6d76e9, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[ 2] + 0x7a6d76e9, 12) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[13] + 0x7a6d76e9, 9) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 9] + 0x7a6d76e9, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 7] + 0x7a6d76e9, 5) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[10] + 0x7a6d76e9, 15) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[14] + 0x7a6d76e9, 8) + bb; ee = RL(ee, 10); + + // + // Rounds 64-79 + // + // left + b = RL(b + f5(c,d,e) + X[ 4] + 0xa953fd4e, 9) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 0] + 0xa953fd4e, 15) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[ 5] + 0xa953fd4e, 5) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[ 9] + 0xa953fd4e, 11) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[ 7] + 0xa953fd4e, 6) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[12] + 0xa953fd4e, 8) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 2] + 0xa953fd4e, 13) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[10] + 0xa953fd4e, 12) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[14] + 0xa953fd4e, 5) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[ 1] + 0xa953fd4e, 12) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[ 3] + 0xa953fd4e, 13) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 8] + 0xa953fd4e, 14) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[11] + 0xa953fd4e, 11) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[ 6] + 0xa953fd4e, 8) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[15] + 0xa953fd4e, 5) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[13] + 0xa953fd4e, 6) + a; d = RL(d, 10); + + // right + bb = RL(bb + f1(cc,dd,ee) + X[12], 8) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[15], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[10], 12) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 4], 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 1], 12) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[ 5], 5) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[ 8], 14) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[ 7], 6) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 6], 8) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 2], 13) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[13], 6) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[14], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[ 0], 15) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 3], 13) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 9], 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[11], 11) + aa; dd = RL(dd, 10); + + dd += c + H1; + H1 = H2 + d + ee; + H2 = H3 + e + aa; + H3 = H4 + a + bb; + H4 = H0 + b + cc; + H0 = dd; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + public Memoable copy() + { + return new RIPEMD160Digest(this); + } + + public void reset(Memoable other) + { + RIPEMD160Digest d = (RIPEMD160Digest)other; + + copyIn(d); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/Digest.java b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/Digest.java new file mode 100644 index 0000000..f34f0f2 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/Digest.java @@ -0,0 +1,22 @@ +package com.onlychain.signsdk.crypto.base; + + +public interface Digest +{ + + public String getAlgorithmName(); + + + public int getDigestSize(); + + public void update(byte in); + + + public void update(byte[] in, int inOff, int len); + + + public int doFinal(byte[] out, int outOff); + + + public void reset(); +} \ No newline at end of file diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/ExtendedDigest.java b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/ExtendedDigest.java new file mode 100644 index 0000000..eeda815 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/ExtendedDigest.java @@ -0,0 +1,8 @@ +package com.onlychain.signsdk.crypto.base; + +public interface ExtendedDigest + extends Digest +{ + + public int getByteLength(); +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/GeneralDigest.java b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/GeneralDigest.java new file mode 100644 index 0000000..8d562ad --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/GeneralDigest.java @@ -0,0 +1,146 @@ +package com.onlychain.signsdk.crypto.base; + + + +public abstract class GeneralDigest implements ExtendedDigest, Memoable +{ + private static final int BYTE_LENGTH = 64; + + private final byte[] xBuf = new byte[4]; + private int xBufOff; + + private long byteCount; + + + protected GeneralDigest() + { + xBufOff = 0; + } + + protected GeneralDigest(GeneralDigest t) + { + copyIn(t); + } + + protected GeneralDigest(byte[] encodedState) + { + System.arraycopy(encodedState, 0, xBuf, 0, xBuf.length); + xBufOff = Pack.bigEndianToInt(encodedState, 4); + byteCount = Pack.bigEndianToLong(encodedState, 8); + } + + protected void copyIn(GeneralDigest t) + { + System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); + + xBufOff = t.xBufOff; + byteCount = t.byteCount; + } + + public void update( + byte in) + { + xBuf[xBufOff++] = in; + + if (xBufOff == xBuf.length) + { + processWord(xBuf, 0); + xBufOff = 0; + } + + byteCount++; + } + + public void update( + byte[] in, + int inOff, + int len) + { + len = Math.max(0, len); + + // + // fill the current word + // + int i = 0; + if (xBufOff != 0) + { + while (i < len) + { + xBuf[xBufOff++] = in[inOff + i++]; + if (xBufOff == 4) + { + processWord(xBuf, 0); + xBufOff = 0; + break; + } + } + } + + // + // process whole words. + // + int limit = ((len - i) & ~3) + i; + for (; i < limit; i += 4) + { + processWord(in, inOff + i); + } + + // + // load in the remainder. + // + while (i < len) + { + xBuf[xBufOff++] = in[inOff + i++]; + } + + byteCount += len; + } + + public void finish() + { + long bitLength = (byteCount << 3); + + // + // add the pad bytes. + // + update((byte)128); + + while (xBufOff != 0) + { + update((byte)0); + } + + processLength(bitLength); + + processBlock(); + } + + public void reset() + { + byteCount = 0; + + xBufOff = 0; + for (int i = 0; i < xBuf.length; i++) + { + xBuf[i] = 0; + } + } + + protected void populateState(byte[] state) + { + System.arraycopy(xBuf, 0, state, 0, xBufOff); + Pack.intToBigEndian(xBufOff, state, 4); + Pack.longToBigEndian(byteCount, state, 8); + } + + public int getByteLength() + { + return BYTE_LENGTH; + } + + protected abstract void processWord(byte[] in, int inOff); + + protected abstract void processLength(long bitLength); + + protected abstract void processBlock(); +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/Memoable.java b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/Memoable.java new file mode 100644 index 0000000..53eabb4 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/Memoable.java @@ -0,0 +1,10 @@ +package com.onlychain.signsdk.crypto.base; + +public interface Memoable +{ + + Memoable copy(); + + + void reset(Memoable other); +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/Pack.java b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/Pack.java new file mode 100644 index 0000000..bee612b --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/crypto/base/Pack.java @@ -0,0 +1,257 @@ +package com.onlychain.signsdk.crypto.base; + +public abstract class Pack +{ + public static short bigEndianToShort(byte[] bs, int off) + { + int n = (bs[ off] & 0xff) << 8; + n |= (bs[++off] & 0xff); + return (short)n; + } + + public static int bigEndianToInt(byte[] bs, int off) + { + int n = bs[ off] << 24; + n |= (bs[++off] & 0xff) << 16; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff); + return n; + } + + public static void bigEndianToInt(byte[] bs, int off, int[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = bigEndianToInt(bs, off); + off += 4; + } + } + + public static byte[] intToBigEndian(int n) + { + byte[] bs = new byte[4]; + intToBigEndian(n, bs, 0); + return bs; + } + + public static void intToBigEndian(int n, byte[] bs, int off) + { + bs[ off] = (byte)(n >>> 24); + bs[++off] = (byte)(n >>> 16); + bs[++off] = (byte)(n >>> 8); + bs[++off] = (byte)(n ); + } + + public static byte[] intToBigEndian(int[] ns) + { + byte[] bs = new byte[4 * ns.length]; + intToBigEndian(ns, bs, 0); + return bs; + } + + public static void intToBigEndian(int[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + intToBigEndian(ns[i], bs, off); + off += 4; + } + } + + public static long bigEndianToLong(byte[] bs, int off) + { + int hi = bigEndianToInt(bs, off); + int lo = bigEndianToInt(bs, off + 4); + return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL); + } + + public static void bigEndianToLong(byte[] bs, int off, long[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = bigEndianToLong(bs, off); + off += 8; + } + } + + public static byte[] longToBigEndian(long n) + { + byte[] bs = new byte[8]; + longToBigEndian(n, bs, 0); + return bs; + } + + public static void longToBigEndian(long n, byte[] bs, int off) + { + intToBigEndian((int)(n >>> 32), bs, off); + intToBigEndian((int)(n & 0xffffffffL), bs, off + 4); + } + + public static byte[] longToBigEndian(long[] ns) + { + byte[] bs = new byte[8 * ns.length]; + longToBigEndian(ns, bs, 0); + return bs; + } + + public static void longToBigEndian(long[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + longToBigEndian(ns[i], bs, off); + off += 8; + } + } + + public static short littleEndianToShort(byte[] bs, int off) + { + int n = bs[ off] & 0xff; + n |= (bs[++off] & 0xff) << 8; + return (short)n; + } + + public static int littleEndianToInt(byte[] bs, int off) + { + int n = bs[ off] & 0xff; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff) << 16; + n |= bs[++off] << 24; + return n; + } + + public static void littleEndianToInt(byte[] bs, int off, int[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = littleEndianToInt(bs, off); + off += 4; + } + } + + public static void littleEndianToInt(byte[] bs, int bOff, int[] ns, int nOff, int count) + { + for (int i = 0; i < count; ++i) + { + ns[nOff + i] = littleEndianToInt(bs, bOff); + bOff += 4; + } + } + + public static int[] littleEndianToInt(byte[] bs, int off, int count) + { + int[] ns = new int[count]; + for (int i = 0; i < ns.length; ++i) + { + ns[i] = littleEndianToInt(bs, off); + off += 4; + } + return ns; + } + + public static byte[] shortToLittleEndian(short n) + { + byte[] bs = new byte[2]; + shortToLittleEndian(n, bs, 0); + return bs; + } + + public static void shortToLittleEndian(short n, byte[] bs, int off) + { + bs[ off] = (byte)(n ); + bs[++off] = (byte)(n >>> 8); + } + + public static byte[] intToLittleEndian(int n) + { + byte[] bs = new byte[4]; + intToLittleEndian(n, bs, 0); + return bs; + } + + public static void intToLittleEndian(int n, byte[] bs, int off) + { + bs[ off] = (byte)(n ); + bs[++off] = (byte)(n >>> 8); + bs[++off] = (byte)(n >>> 16); + bs[++off] = (byte)(n >>> 24); + } + + public static byte[] intToLittleEndian(int[] ns) + { + byte[] bs = new byte[4 * ns.length]; + intToLittleEndian(ns, bs, 0); + return bs; + } + + public static void intToLittleEndian(int[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + intToLittleEndian(ns[i], bs, off); + off += 4; + } + } + + public static long littleEndianToLong(byte[] bs, int off) + { + int lo = littleEndianToInt(bs, off); + int hi = littleEndianToInt(bs, off + 4); + return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL); + } + + public static void littleEndianToLong(byte[] bs, int off, long[] ns) + { + for (int i = 0; i < ns.length; ++i) + { + ns[i] = littleEndianToLong(bs, off); + off += 8; + } + } + + public static void littleEndianToLong(byte[] bs, int bsOff, long[] ns, int nsOff, int nsLen) + { + for (int i = 0; i < nsLen; ++i) + { + ns[nsOff + i] = littleEndianToLong(bs, bsOff); + bsOff += 8; + } + } + + public static byte[] longToLittleEndian(long n) + { + byte[] bs = new byte[8]; + longToLittleEndian(n, bs, 0); + return bs; + } + + public static void longToLittleEndian(long n, byte[] bs, int off) + { + intToLittleEndian((int)(n & 0xffffffffL), bs, off); + intToLittleEndian((int)(n >>> 32), bs, off + 4); + } + + public static byte[] longToLittleEndian(long[] ns) + { + byte[] bs = new byte[8 * ns.length]; + longToLittleEndian(ns, bs, 0); + return bs; + } + + public static void longToLittleEndian(long[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.length; ++i) + { + longToLittleEndian(ns[i], bs, off); + off += 8; + } + } + + public static void longToLittleEndian(long[] ns, int nsOff, int nsLen, byte[] bs, int bsOff) + { + for (int i = 0; i < nsLen; ++i) + { + longToLittleEndian(ns[nsOff + i], bs, bsOff); + bsOff += 8; + } + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/exceptions/MessageDecodingException.java b/SignSdk/src/main/java/com/onlychain/signsdk/exceptions/MessageDecodingException.java new file mode 100644 index 0000000..1edfd97 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/exceptions/MessageDecodingException.java @@ -0,0 +1,11 @@ +package com.onlychain.signsdk.exceptions; + +public class MessageDecodingException extends RuntimeException { + public MessageDecodingException(String message) { + super(message); + } + + public MessageDecodingException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/exceptions/MessageEncodingException.java b/SignSdk/src/main/java/com/onlychain/signsdk/exceptions/MessageEncodingException.java new file mode 100644 index 0000000..70d7922 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/exceptions/MessageEncodingException.java @@ -0,0 +1,15 @@ +package com.onlychain.signsdk.exceptions; + + +/** + * Encoding exception. + */ +public class MessageEncodingException extends RuntimeException { + public MessageEncodingException(String message) { + super(message); + } + + public MessageEncodingException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/net/Request.java b/SignSdk/src/main/java/com/onlychain/signsdk/net/Request.java new file mode 100644 index 0000000..4723e25 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/net/Request.java @@ -0,0 +1,117 @@ +package com.onlychain.signsdk.net; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +public abstract class Request { + public abstract void success(StringBuffer json); + public abstract void fail(Exception e); + public Request(String urls) { + get(urls); + } + + public Request(String post_url,LinkedHashMap parameter) { + post(post_url,parameter); + } + public void get(final String get_urls) { + new Thread(new Runnable() { + @Override + public void run() { + StringBuffer stringBuffer=new StringBuffer(); + try { + URL url = new URL(get_urls); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setConnectTimeout(5*1000);//链接超时 + urlConnection.setReadTimeout(5*1000);//返回数据超时 + //getResponseCode (1.200请求成功 2.404请求失败) + if(urlConnection.getResponseCode()==200){ + //获得读取流写入 + InputStream inputStream = urlConnection.getInputStream(); + byte[] bytes=new byte[1024]; + int len=0; + while ((len=inputStream.read(bytes))!=-1){ + stringBuffer.append(new String(bytes,0,len)); + } + success(stringBuffer); + } + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }).start(); + } + + private void post(final String post_url, final LinkedHashMap parameter) { + new Thread(new Runnable() { + @Override + public void run() { + StringBuffer stringBuffer=new StringBuffer(); + try { + URL url = new URL(post_url); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setConnectTimeout(5*1000); + urlConnection.setReadTimeout(5*1000); + //设置请求方式,大写的POST + urlConnection.setRequestMethod("POST"); + //一定要设置 Content-Type 要不然服务端接收不到参数 + urlConnection.setRequestProperty("Content-Type", "application/JSON; charset=UTF-8"); + //设置允许输出 + urlConnection.setDoOutput(true); + urlConnection.setUseCaches(false); + + OutputStream out = urlConnection.getOutputStream(); + out.write(hashMapToJson(parameter).getBytes());//请求参数放到请求体 + out.flush(); + out.close(); + if(urlConnection.getResponseCode()==200){ + String result=""; + BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(),"UTF-8")); + while((result=br.readLine()) != null){ + stringBuffer.append(result+"\n"); + } + br.close(); + success(stringBuffer); + } + } catch (MalformedURLException e) { + e.printStackTrace(); + fail(e); + } catch (IOException e) { + e.printStackTrace(); + fail(e); + } catch (Exception e) { + e.printStackTrace(); + fail(e); + } + } + }).start(); + } + public static String hashMapToJson(LinkedHashMap map) { + String string = "{"; + for (Iterator it = map.entrySet().iterator(); it.hasNext();) { + Map.Entry e = (Map.Entry) it.next(); + string += "\"" + e.getKey() + "\":"; + string += "\"" + e.getValue() + "\","; + } + string = string.substring(0, string.lastIndexOf(",")); + string += "}"; + return string; + } +} + + + + + + + + + + + diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Fraction.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Fraction.java new file mode 100644 index 0000000..f3b4fa8 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Fraction.java @@ -0,0 +1,31 @@ +package com.onlychain.signsdk.secp256k1; + +import java.math.BigInteger; + +public final class Fraction { + public static final Fraction ZERO = new Fraction(BigInteger.ZERO); + + private final BigInteger num, den; + + public BigInteger getNum() { + return num; + } + + public BigInteger getDen() { + return den; + } + + public Fraction(BigInteger num, BigInteger den) { + if (den.equals(BigInteger.ZERO)) throw new ArithmeticException("分母不能是0"); + this.num = num; + this.den = den; + } + + public Fraction(BigInteger num) { + this(num, BigInteger.ONE); + } + + public boolean isZero() { + return num.equals(BigInteger.ZERO); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidMessageException.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidMessageException.java new file mode 100644 index 0000000..1d4d908 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidMessageException.java @@ -0,0 +1,8 @@ +package com.onlychain.signsdk.secp256k1; + +@SuppressWarnings("serial") +public final class InvalidMessageException extends RuntimeException { + public InvalidMessageException() { + super("消息长度必须是32字节"); + } +} \ No newline at end of file diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidPrivateKeyException.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidPrivateKeyException.java new file mode 100644 index 0000000..b3ea8cd --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidPrivateKeyException.java @@ -0,0 +1,12 @@ +package com.onlychain.signsdk.secp256k1; + +@SuppressWarnings("serial") +public final class InvalidPrivateKeyException extends RuntimeException { + public InvalidPrivateKeyException() { + super("无效的私钥"); + } + + public InvalidPrivateKeyException(String message) { + super(message); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidPublicKeyException.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidPublicKeyException.java new file mode 100644 index 0000000..f418f57 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidPublicKeyException.java @@ -0,0 +1,8 @@ +package com.onlychain.signsdk.secp256k1; + +@SuppressWarnings("serial") +public final class InvalidPublicKeyException extends RuntimeException { + public InvalidPublicKeyException(String message) { + super(message); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidSignatureException.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidSignatureException.java new file mode 100644 index 0000000..0c651ef --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/InvalidSignatureException.java @@ -0,0 +1,12 @@ +package com.onlychain.signsdk.secp256k1; + +@SuppressWarnings("serial") +public final class InvalidSignatureException extends RuntimeException { + public InvalidSignatureException(String message) { + super(message); + } + + public InvalidSignatureException() { + super("无效的签名"); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/ModN.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/ModN.java new file mode 100644 index 0000000..de7bfa0 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/ModN.java @@ -0,0 +1,49 @@ +package com.onlychain.signsdk.secp256k1; + +import java.math.BigInteger; + +public class ModN { + private ModN() { + } + + public final static BigInteger N = new BigInteger("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16); + + public static BigInteger byteArrayToU256(byte[] bytes) { + if (bytes.length != 32) { + throw new ArithmeticException("�ֽ����������32�ֽ�"); + } + BigInteger x = new BigInteger(1, bytes); + if (x.compareTo(N) >= 0) { + x = x.subtract(N); + } + return x; + } + + public static BigInteger add(BigInteger a, BigInteger b) { + BigInteger c = a.add(b); + if (c.compareTo(N) >= 0) { + c = c.subtract(N); + } + return c; + } + + public static BigInteger sub(BigInteger a, BigInteger b) { + if (a.compareTo(b) < 0) { + return a.add(N).subtract(b); + } else { + return a.subtract(b); + } + } + + public static BigInteger mul(BigInteger a, BigInteger b) { + return a.multiply(b).mod(N); + } + + public static BigInteger div(BigInteger a, BigInteger b) { + return mul(a, inv(b)); + } + + public static BigInteger inv(BigInteger a) { + return a.modInverse(N); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/ModP.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/ModP.java new file mode 100644 index 0000000..d6256fb --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/ModP.java @@ -0,0 +1,208 @@ +package com.onlychain.signsdk.secp256k1; + +import java.math.BigInteger; +public final class ModP { + private ModP() { + } + + public final static BigInteger P = new BigInteger("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 16); + + public static BigInteger byteArrayToU256(byte[] bytes) { + if (bytes.length != 32) { + throw new ArithmeticException("字节数组必须是32字节"); + } + BigInteger x = new BigInteger(1, bytes); + if (x.compareTo(P) >= 0) { + x = x.subtract(P); + } + return x; + } + + public static BigInteger add(BigInteger a, BigInteger b) { + BigInteger c = a.add(b); + if (c.compareTo(P) >= 0) { + c = c.subtract(P); + } + return c; + } + + public static BigInteger sub(BigInteger a, BigInteger b) { + if (a.compareTo(b) < 0) { + return a.add(P).subtract(b); + } else { + return a.subtract(b); + } + } + + public static BigInteger mul(BigInteger a, BigInteger b) { + return a.multiply(b).mod(P); + } + + public static BigInteger div(BigInteger a, BigInteger b) { + return mul(a, inv(b)); + } + + public static BigInteger neg(BigInteger a) { + if (a.equals(BigInteger.ZERO)) return BigInteger.ZERO; + return P.subtract(a); + } + + public static BigInteger square(BigInteger a) { + return a.multiply(a).mod(P); + } + + public static BigInteger inv(BigInteger a) { + return a.modInverse(P); + } + + public static BigInteger sqrt(BigInteger a) { + if (!a.modPow(P.subtract(BigInteger.ONE).shiftRight(1), P).equals(BigInteger.ONE)) { + throw new ArithmeticException("'" + toString(a) + "'在素域P中没有开平方。"); + } + return fastSqrt(a); + } + + public static BigInteger fastSqrt(BigInteger a) { + return a.modPow(P.add(BigInteger.ONE).shiftRight(2), P); + } + + public static BigInteger pow(BigInteger a, BigInteger b) { + return a.modPow(b, P); + } + + public static String toString(BigInteger a) { + if (a.compareTo(BigInteger.ZERO) < 0 || a.compareTo(P) >= 0) { + throw new ArithmeticException("给定的整数必须>=0且 64) { + return hex.substring(hex.length() - 64); + } else { + return hex; + } + } + + /** + * 返回大整型的字节数组(固定32字节) + * @param a + * @return + */ + public static byte[] toByteArray(BigInteger a) { + if (a.compareTo(BigInteger.ZERO) < 0 || a.compareTo(P) >= 0) { + throw new ArithmeticException("给定的整数必须>=0且= 32) { + System.arraycopy(tempBytes, tempBytes.length - 32, bytes, 0, 32); + } else { + System.arraycopy(tempBytes, 0, bytes, 32 - tempBytes.length, tempBytes.length); + } + return bytes; + } + + public static Fraction add(Fraction a, Fraction b) { + if (a.getDen().equals(b.getDen())) { + return new Fraction(add(a.getNum(), b.getNum()), a.getDen()); + } else { + return new Fraction(add(mul(a.getNum(), b.getDen()), mul(a.getDen(), b.getNum())), mul(a.getDen(), b.getDen())); + } + } + + public static Fraction sub(Fraction a, Fraction b) { + if (a.getDen().equals(b.getDen())) { + return new Fraction(sub(a.getNum(), b.getNum()), a.getDen()); + } else { + return new Fraction(sub(mul(a.getNum(), b.getDen()), mul(a.getDen(), b.getNum())), mul(a.getDen(), b.getDen())); + } + } + + public static Fraction neg(Fraction a) { + return new Fraction(neg(a.getNum()), a.getDen()); + } + + public static Fraction mul(Fraction a, Fraction b) { + return new Fraction(mul(a.getNum(), b.getNum()), mul(a.getDen(), b.getDen())); + } + + public static Fraction div(Fraction a, Fraction b) { + return new Fraction(mul(a.getNum(), b.getDen()), mul(a.getDen(), b.getNum())); + } + + public static BigInteger toU256(Fraction a) { + return div(a.getNum(), a.getDen()); + } + + public static boolean equal(Fraction a, Fraction b) { + return mul(a.getNum(), b.getDen()).equals(mul(a.getDen(), b.getNum())); + } + + static Point mul2(Point a) { + Fraction lambda = div(mul(mul(a.getX(), a.getX()), new Fraction(BigInteger.valueOf(3))), add(a.getY(), a.getY())); + Fraction x = sub(mul(lambda, lambda), add(a.getX(), a.getX())); + Fraction y = sub(mul(lambda, sub(a.getX(), x)), a.getY()); + return new Point(x, y); + } + + public static Point add(Point a, Point b) { + if (a.isZero()) return b; + if (b.isZero()) return a; + + if (equal(a.getX(), b.getX())) { + if (equal(a.getY(), b.getY())) { + return mul2(a); + } else { + return Point.ZERO; + } + } else { + Fraction lambda = div(sub(b.getY(), a.getY()), sub(b.getX(), a.getX())); + Fraction x = sub(mul(lambda, lambda), add(a.getX(), b.getX())); + Fraction y = sub(mul(lambda, sub(a.getX(), x)), a.getY()); + return new Point(x, y); + } + } + + public static Point mul(Point p, BigInteger n) { + if (p.isZero() || n.equals(BigInteger.ZERO)) return Point.ZERO; + + int bitLength = n.bitLength(); + Point ret = Point.ZERO, temp = p; + for (int i = 0; i < bitLength; i++) { + if (n.testBit(i)) { + ret = add(ret, temp); + } + if (i != bitLength - 1) { + temp = mul2(temp); + } + } + return ret; + } + + + static final BigInteger G_X = new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16); + static final BigInteger G_Y = new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16); + static final Point G = new Point(new Fraction(G_X), new Fraction(G_Y)); + static final Point[] G_TABLE = new Point[256]; + + static { + G_TABLE[0] = G; + for (int i = 1; i < 256; i++) { + G_TABLE[i] = mul2(G_TABLE[i - 1]); + } + } + + public static Point mulG(BigInteger n) { + Point r = Point.ZERO; + for (int i = 0; i < 256; i++) { + if (n.testBit(i)) { + r = add(r, G_TABLE[i]); + } + } + return r; + } + +} \ No newline at end of file diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Point.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Point.java new file mode 100644 index 0000000..d8ee474 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Point.java @@ -0,0 +1,35 @@ +package com.onlychain.signsdk.secp256k1; + +public final class Point { + public static final Point ZERO = new Point(); + + private final Fraction x, y; + + public Fraction getX() { + return x; + } + + public Fraction getY() { + return y; + } + + private Point() { + x = null; + y = null; + } + + public Point(Fraction x, Fraction y) { + if (x == null || y == null) throw new NullPointerException(); + this.x = x; + this.y = y; + } + + public boolean isZero() { + return x == null; + } + + @Override + public String toString() { + return String.format("(%s, %s)", ModP.toString(ModP.toU256(x)), ModP.toString(ModP.toU256(y))); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/PublicKey.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/PublicKey.java new file mode 100644 index 0000000..9af5358 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/PublicKey.java @@ -0,0 +1,65 @@ +package com.onlychain.signsdk.secp256k1; + +import java.math.BigInteger; +import java.util.Arrays; + +public final class PublicKey { + private BigInteger x, y; + + public BigInteger getX() { + return x; + } + + public BigInteger getY() { + return y; + } + + public PublicKey(BigInteger x, BigInteger y) { + this.x = x; + this.y = y; + } + + static final byte EVEN_PUBLIC_KEY = 2; + static final byte ODD_PUBLIC_KEY = 3; + static final byte FULL_PUBLIC_KEY = 4; + + public byte[] serialize(boolean compressed) { + int len = compressed ? 33 : 65; + byte[] ret = new byte[len]; + byte[] x = ModP.toByteArray(this.x); + System.arraycopy(x, 0, ret, 1, 32); + if (compressed) { + ret[0] = y.testBit(0) ? ODD_PUBLIC_KEY : EVEN_PUBLIC_KEY; + } else { + byte[] y = ModP.toByteArray(this.y); + System.arraycopy(y, 0, ret, 33, 32); + ret[0] = FULL_PUBLIC_KEY; + } + return ret; + } + + public static PublicKey parse(byte[] publicKeyBytes) { + if (publicKeyBytes.length < 33) throw new InvalidPublicKeyException("长度太小"); + BigInteger x = new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 1, 33)); + if (publicKeyBytes[0] == EVEN_PUBLIC_KEY || publicKeyBytes[0] == ODD_PUBLIC_KEY) { + try { + BigInteger y = ModP.sqrt(ModP.add(ModP.pow(x, BigInteger.valueOf(3)), BigInteger.valueOf(7))); + if (y.testBit(0) != (publicKeyBytes[0] == ODD_PUBLIC_KEY)) { + y = ModP.neg(y); + } + return new PublicKey(x, y); + } catch (ArithmeticException e) { + throw new InvalidPublicKeyException("无效的公钥"); + } + } else if (publicKeyBytes[0] == FULL_PUBLIC_KEY) { + if (publicKeyBytes.length < 65) throw new InvalidPublicKeyException("长度太小"); + BigInteger y = new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 33, 65)); + if (!ModP.add(ModP.pow(x, BigInteger.valueOf(3)), BigInteger.valueOf(7)).equals(ModP.square(y))) { + throw new InvalidPublicKeyException("无效的公钥"); + } + return new PublicKey(x, y); + } else { + throw new InvalidPublicKeyException("不支持的公钥类型"); + } + } +} \ No newline at end of file diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Secp256k1.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Secp256k1.java new file mode 100644 index 0000000..c8235f2 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Secp256k1.java @@ -0,0 +1,59 @@ +package com.onlychain.signsdk.secp256k1; + +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public final class Secp256k1 { + private Secp256k1() { + } + + static SecureRandom RNG; + + static { + try { + RNG = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + public static byte[] createPrivateKey() { + while (true) { + BigInteger k = new BigInteger(256, RNG); + if (k.equals(BigInteger.ZERO) || k.compareTo(ModN.N) >= 0) continue; + return ModP.toByteArray(k); + } + } + + public static PublicKey createPublicKey(byte[] privateKey) { + BigInteger k = ModP.byteArrayToU256(privateKey); + if (k.equals(BigInteger.ZERO) || k.compareTo(ModN.N) >= 0) throw new InvalidPrivateKeyException(); + Point p = ModP.mul(ModP.G, k); + return new PublicKey(ModP.toU256(p.getX()), ModP.toU256(p.getY())); + } + + public static Signature sign(byte[] privateKey, byte[] message) { + if (privateKey.length != 32) throw new InvalidPrivateKeyException("私钥长度必须是32字节"); + if (message.length != 32) throw new InvalidMessageException(); + + BigInteger dA = ModN.byteArrayToU256(privateKey); + BigInteger m = ModN.byteArrayToU256(message); + byte[] tempPrivKey = createPrivateKey(); + PublicKey tempPubKey = createPublicKey(tempPrivKey); + BigInteger k = ModN.byteArrayToU256(tempPrivKey); + BigInteger s = ModN.div(ModN.add(m, ModN.mul(dA, tempPubKey.getX())), k); + return new Signature(tempPubKey.getX(), s); + } + + public static boolean verify(PublicKey publicKey, byte[] message, Signature signature) { + if (message.length != 32) throw new InvalidMessageException(); + + BigInteger m = ModN.byteArrayToU256(message); + BigInteger s_inv = ModN.inv(signature.getS()); + BigInteger u1 = ModN.mul(s_inv, m); + BigInteger u2 = ModN.mul(s_inv, signature.getR()); + Point p = ModP.add(ModP.mulG(u1), ModP.mul(new Point(new Fraction(publicKey.getX()), new Fraction(publicKey.getY())), u2)); + return ModP.equal(p.getX(), new Fraction(signature.getR())); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Signature.java b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Signature.java new file mode 100644 index 0000000..be95b81 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/secp256k1/Signature.java @@ -0,0 +1,105 @@ +package com.onlychain.signsdk.secp256k1; + +import java.math.BigInteger; + +public final class Signature { + private BigInteger r, s; + + public BigInteger getR() { + return r; + } + + public BigInteger getS() { + return s; + } + + public Signature(BigInteger r, BigInteger s) { + this.r = r; + this.s = s; + } + + static final class ParseIndex { + public int value; + } + + static int parseInt(byte[] bytes, ParseIndex index) { + if (index.value >= bytes.length) throw new InvalidSignatureException(); + int len = bytes[index.value] & 0xff; + if (len < 128) { + index.value++; + if (index.value + len > bytes.length) throw new InvalidSignatureException(); + return len; + } + int lenBytes = len ^ 0x80; + if (lenBytes > 4 || bytes.length - index.value < 1 + lenBytes) throw new InvalidSignatureException(); + int dataLen = bytes[index.value + 1] & 0xff; + int dataOffset = index.value + 1 + dataLen; + for (int i = index.value + 2; i < dataOffset; i++) { + dataLen = (dataLen << 8) | (bytes[i] & 0xff); + } + if (bytes.length - index.value < 1 + lenBytes + dataOffset) throw new InvalidSignatureException(); + index.value = dataOffset; + return dataLen; + } + + static BigInteger parseU256(byte[] bytes, ParseIndex index) { + if (bytes[index.value++] != 2) throw new InvalidSignatureException(); + int u256Len = parseInt(bytes, index); + byte[] buffer = new byte[32]; + if (u256Len > 32) { + while (bytes[index.value] == 0) { + u256Len--; + index.value++; + } + if (u256Len > 32) throw new InvalidSignatureException(); + } + System.arraycopy(bytes, index.value, buffer, 32 - u256Len, u256Len); + index.value += u256Len; + return new BigInteger(1, buffer); + } + + public static Signature parse(byte[] signBytes) { + if (signBytes.length == 0) throw new InvalidSignatureException("长度太小"); + ParseIndex i = new ParseIndex(); + if (signBytes[i.value++] != 0x30) throw new InvalidSignatureException(); + parseInt(signBytes, i); + + BigInteger r = parseU256(signBytes, i); + BigInteger s = parseU256(signBytes, i); + return new Signature(r, s); + } + + public byte[] serialize() { + byte[] R = ModP.toByteArray(r); + byte[] S = ModP.toByteArray(s); + int rlen = 33; + do { + if (R[33 - rlen] < 0) break; + rlen--; + } while (R[32 - rlen] == 0); + int slen = 33; + do { + if (S[33 - slen] < 0) break; + slen--; + } while (S[32 - slen] == 0); + + byte[] buffer = new byte[6 + rlen + slen]; + buffer[0] = 0x30; + buffer[1] = (byte)(4 + rlen + slen); + buffer[2] = 2; + buffer[3] = (byte)rlen; + if (rlen == 33) { + System.arraycopy(R, 0, buffer, 5, 32); + } else { + System.arraycopy(R, 32 - rlen, buffer, 4, rlen); + } + buffer[4 + rlen] = 2; + buffer[5 + rlen] = (byte)slen; + if (slen == 33) { + System.arraycopy(S, 0, buffer, 7 + rlen, 32); + } else { + System.arraycopy(S, 32 - slen, buffer, 6 + rlen, slen); + } + return buffer; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/utils/Assertions.java b/SignSdk/src/main/java/com/onlychain/signsdk/utils/Assertions.java new file mode 100644 index 0000000..709045e --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/utils/Assertions.java @@ -0,0 +1,16 @@ +package com.onlychain.signsdk.utils; + +public class Assertions { + + /** + * Verify that the provided precondition holds true. + * + * @param assertionResult assertion value + * @param errorMessage error message if precondition failure + */ + public static void verifyPrecondition(boolean assertionResult, String errorMessage) { + if (!assertionResult) { + throw new RuntimeException(errorMessage); + } + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/utils/Bytes.java b/SignSdk/src/main/java/com/onlychain/signsdk/utils/Bytes.java new file mode 100644 index 0000000..12aefad --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/utils/Bytes.java @@ -0,0 +1,22 @@ +package com.onlychain.signsdk.utils; + +import java.util.Arrays; + +public class Bytes { + + private Bytes() {} + + public static byte[] trimLeadingBytes(byte[] bytes, byte b) { + int offset = 0; + for (; offset < bytes.length - 1; offset++) { + if (bytes[offset] != b) { + break; + } + } + return Arrays.copyOfRange(bytes, offset, bytes.length); + } + + public static byte[] trimLeadingZeroes(byte[] bytes) { + return trimLeadingBytes(bytes, (byte) 0); + } +} \ No newline at end of file diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/utils/CheckUtils.java b/SignSdk/src/main/java/com/onlychain/signsdk/utils/CheckUtils.java new file mode 100644 index 0000000..917664a --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/utils/CheckUtils.java @@ -0,0 +1,26 @@ +package com.onlychain.signsdk.utils; + +/** + * 验证签名 + */ +public class CheckUtils { + + /** + * 检查是否属于OC合法地址 + * @param input + * @return + */ + public static boolean checkAddress(String input){ + if(input.length()==42){ + String head=input.substring(0,2).toLowerCase(); + String body=input.substring(2,input.length()).toLowerCase(); + String regex="^[A-Fa-f0-9]+$"; + if(head.equals("oc")&&body.matches(regex)) + return true; + } + return false; + } + + + +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/utils/Leb128Utils.java b/SignSdk/src/main/java/com/onlychain/signsdk/utils/Leb128Utils.java new file mode 100644 index 0000000..c4ca54c --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/utils/Leb128Utils.java @@ -0,0 +1,125 @@ +package com.onlychain.signsdk.utils; + +/** + * @James + * 对数值进行LEB128编码和解码 + */ +public final class Leb128Utils { + + private Leb128Utils() { + // This space intentionally left blank. + } + + + public static int unsignedLeb128Size(long value) { + // TODO: This could be much cleverer. + + long remaining = value >>> 7; + int count = 0; + + while (remaining != 0) { + value = remaining; + remaining >>>= 7; + count++; + } + return count + 1; + } + + + public static int signedLeb128Size(int value) { + int remaining = value >> 7; + int count = 0; + boolean hasMore = true; + int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; + + while (hasMore) { + hasMore = (remaining != end) + || ((remaining & 1) != ((value >> 6) & 1)); + + value = remaining; + remaining >>= 7; + count++; + } + + return count; + } + + + + /** + * 根据长整型获取无符号LEB128编码 + * @param value + * @return + */ + public static String encodeUleb128(long value) { + byte[] buffer = new byte[unsignedLeb128Size(value)]; + int bufferIndex=0; + long remaining = value >>> 7; + while (remaining != 0) { + buffer[bufferIndex] = (byte)((value & 0x7f) | 0x80); + bufferIndex++; + value = remaining; + remaining >>>= 7; + } + buffer[bufferIndex] = (byte)(value & 0x7f); + return OcMath.toHexStringNoPrefix(buffer); + } + + /** + * 根据字符串获取无符号LEB128编码 + * 输入值不超过long的最大值 + * @param value + * @return + */ + public static String encodeUleb128(String value) { + return encodeUleb128(Long.valueOf(value)); + } + + + /** + *解码LEB128 支持64位 + * @param input + * @return + */ + public static long decodeUnsigned(byte[] input) { + long result = 0; + long shift = 0; + int i = 0; + while (true) { + long byteTemp= Long.valueOf(input[i++] & 0xfF); + result |= Long.valueOf((byteTemp & 0x7F) << shift); + if ((byteTemp & 0x80) == 0) + break; + shift += 7; + } + return result; + } + + /** + * 校验是否到达LEB最末尾数 + * @param hex + * @return true:已经到达leb末尾 + * @throws Exception + */ + public static boolean isLebEnd(String hex) { + if (hex.length()==2){ + byte[] data= OcMath.hexStringToByteArray(hex); + int resultSig=((data[0]& 0xfF)& 0x80); + if(resultSig!=0) + return false; + else + return true; + } + return true; + } + + + /** + * 解码LEB128 支持64位 + * @param input + * @return + */ + public static long decodeUnsigned(String input) { + return decodeUnsigned(OcMath.hexStringToByteArray(input)); + } +} \ No newline at end of file diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/utils/Numeric.java b/SignSdk/src/main/java/com/onlychain/signsdk/utils/Numeric.java new file mode 100644 index 0000000..0a8d61a --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/utils/Numeric.java @@ -0,0 +1,227 @@ +package com.onlychain.signsdk.utils; + + +import com.onlychain.signsdk.exceptions.MessageDecodingException; +import com.onlychain.signsdk.exceptions.MessageEncodingException; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Arrays; + + +/** + *

Message codec functions.

+ * + *

Implementation as per https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding

+ */ +public final class Numeric { + + private static final String HEX_PREFIX = "0x"; + + private Numeric() { + } + + public static String encodeQuantity(BigInteger value) { + if (value.signum() != -1) { + return HEX_PREFIX + value.toString(16); + } else { + throw new MessageEncodingException("Negative values are not supported"); + } + } + + public static BigInteger decodeQuantity(String value) { + if (!isValidHexQuantity(value)) { + throw new MessageDecodingException("Value must be in format 0x[1-9]+[0-9]* or 0x0"); + } + try { + return new BigInteger(value.substring(2), 16); + } catch (NumberFormatException e) { + throw new MessageDecodingException("Negative ", e); + } + } + + private static boolean isValidHexQuantity(String value) { + if (value == null) { + return false; + } + + if (value.length() < 3) { + return false; + } + + if (!value.startsWith(HEX_PREFIX)) { + return false; + } + + // If TestRpc resolves the following issue, we can reinstate this code + // https://github.com/ethereumjs/testrpc/issues/220 + // if (value.length() > 3 && value.charAt(2) == '0') { + // return false; + // } + + return true; + } + + public static String cleanHexPrefix(String input) { + if (containsHexPrefix(input)) { + return input.substring(2); + } else { + return input; + } + } + + public static String prependHexPrefix(String input) { + if (!containsHexPrefix(input)) { + return HEX_PREFIX + input; + } else { + return input; + } + } + + public static boolean containsHexPrefix(String input) { + return input.length() > 1 && input.charAt(0) == '0' && input.charAt(1) == 'x'; + } + + public static BigInteger toBigInt(byte[] value, int offset, int length) { + return toBigInt((Arrays.copyOfRange(value, offset, offset + length))); + } + + public static BigInteger toBigInt(byte[] value) { + return new BigInteger(1, value); + } + + public static BigInteger toBigInt(String hexValue) { + String cleanValue = cleanHexPrefix(hexValue); + return toBigIntNoPrefix(cleanValue); + } + + public static BigInteger toBigIntNoPrefix(String hexValue) { + return new BigInteger(hexValue, 16); + } + + public static String toHexStringWithPrefix(BigInteger value) { + return HEX_PREFIX + value.toString(16); + } + + public static String toHexStringNoPrefix(BigInteger value) { + return value.toString(16); + } + + public static String toHexStringNoPrefix(byte[] input) { + return toHexString(input, 0, input.length, false); + } + + public static String toHexStringWithPrefixZeroPadded(BigInteger value, int size) { + return toHexStringZeroPadded(value, size, true); + } + + public static String toHexStringWithPrefixSafe(BigInteger value) { + String result = toHexStringNoPrefix(value); + if (result.length() < 2) { + result = Strings.zeros(1) + result; + } + return HEX_PREFIX + result; + } + + public static String toHexStringNoPrefixZeroPadded(BigInteger value, int size) { + return toHexStringZeroPadded(value, size, false); + } + + private static String toHexStringZeroPadded(BigInteger value, int size, boolean withPrefix) { + String result = toHexStringNoPrefix(value); + + int length = result.length(); + if (length > size) { + throw new UnsupportedOperationException( + "Value " + result + "is larger then length " + size); + } else if (value.signum() < 0) { + throw new UnsupportedOperationException("Value cannot be negative"); + } + + if (length < size) { + result = Strings.zeros(size - length) + result; + } + + if (withPrefix) { + return HEX_PREFIX + result; + } else { + return result; + } + } + + public static byte[] toBytesPadded(BigInteger value, int length) { + byte[] result = new byte[length]; + byte[] bytes = value.toByteArray(); + + int bytesLength; + int srcOffset; + if (bytes[0] == 0) { + bytesLength = bytes.length - 1; + srcOffset = 1; + } else { + bytesLength = bytes.length; + srcOffset = 0; + } + + if (bytesLength > length) { + throw new RuntimeException("Input is too large to put in byte array of size " + length); + } + + int destOffset = length - bytesLength; + System.arraycopy(bytes, srcOffset, result, destOffset, bytesLength); + return result; + } + + public static byte[] hexStringToByteArray(String input) { + String cleanInput = cleanHexPrefix(input); + + int len = cleanInput.length(); + + if (len == 0) { + return new byte[] {}; + } + + byte[] data; + int startIdx; + if (len % 2 != 0) { + data = new byte[(len / 2) + 1]; + data[0] = (byte) Character.digit(cleanInput.charAt(0), 16); + startIdx = 1; + } else { + data = new byte[len / 2]; + startIdx = 0; + } + + for (int i = startIdx; i < len; i += 2) { + data[(i + 1) / 2] = (byte) ((Character.digit(cleanInput.charAt(i), 16) << 4) + + Character.digit(cleanInput.charAt(i + 1), 16)); + } + return data; + } + + public static String toHexString(byte[] input, int offset, int length, boolean withPrefix) { + StringBuilder stringBuilder = new StringBuilder(); + if (withPrefix) { + stringBuilder.append("0x"); + } + for (int i = offset; i < offset + length; i++) { + stringBuilder.append(String.format("%02x", input[i] & 0xFF)); + } + + return stringBuilder.toString(); + } + + public static String toHexString(byte[] input) { + return toHexString(input, 0, input.length, true); + } + + public static byte asByte(int m, int n) { + return (byte) ((m << 4) | n); + } + + public static boolean isIntegerValue(BigDecimal value) { + return value.signum() == 0 + || value.scale() <= 0 + || value.stripTrailingZeros().scale() <= 0; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/utils/OcMath.java b/SignSdk/src/main/java/com/onlychain/signsdk/utils/OcMath.java new file mode 100644 index 0000000..4dabfa2 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/utils/OcMath.java @@ -0,0 +1,228 @@ +package com.onlychain.signsdk.utils; + + + +import com.onlychain.signsdk.exceptions.MessageDecodingException; +import com.onlychain.signsdk.exceptions.MessageEncodingException; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +public final class OcMath { + + private static final String HEX_PREFIX = "0x"; + + private OcMath() { + } + + + public static String hexStringToText(String input) { + return new String(OcMath.hexStringToByteArray(input), StandardCharsets.UTF_8); + } + + public static String encodeQuantity(BigInteger value) { + if (value.signum() != -1) { + return HEX_PREFIX + value.toString(16); + } else { + throw new MessageEncodingException("Negative values are not supported"); + } + } + + public static BigInteger decodeQuantity(String value) { + if (!isValidHexQuantity(value)) { + throw new MessageDecodingException("Value must be in format 0x[1-9]+[0-9]* or 0x0"); + } + try { + return new BigInteger(value.substring(2), 16); + } catch (NumberFormatException e) { + throw new MessageDecodingException("Negative ", e); + } + } + + private static boolean isValidHexQuantity(String value) { + if (value == null) { + return false; + } + + if (value.length() < 3) { + return false; + } + + if (!value.startsWith(HEX_PREFIX)) { + return false; + } + + // If TestRpc resolves the following issue, we can reinstate this code + // https://github.com/ethereumjs/testrpc/issues/220 + // if (value.length() > 3 && value.charAt(2) == '0') { + // return false; + // } + + return true; + } + + public static String cleanHexPrefix(String input) { + if (containsHexPrefix(input)) { + return input.substring(2); + } else { + return input; + } + } + + public static String prependHexPrefix(String input) { + if (!containsHexPrefix(input)) { + return HEX_PREFIX + input; + } else { + return input; + } + } + + public static boolean containsHexPrefix(String input) { + return input.length() > 1 && input.charAt(0) == '0' && input.charAt(1) == 'x'; + } + + public static BigInteger toBigInt(byte[] value, int offset, int length) { + return toBigInt((Arrays.copyOfRange(value, offset, offset + length))); + } + + public static BigInteger toBigInt(byte[] value) { + return new BigInteger(1, value); + } + + public static BigInteger toBigInt(String hexValue) { + String cleanValue = cleanHexPrefix(hexValue); + return toBigIntNoPrefix(cleanValue); + } + + public static BigInteger toBigIntNoPrefix(String hexValue) { + return new BigInteger(hexValue, 16); + } + + public static String toHexStringWithPrefix(BigInteger value) { + return HEX_PREFIX + value.toString(16); + } + + public static String toHexStringNoPrefix(BigInteger value) { + return value.toString(16); + } + + public static String toHexStringNoPrefix(byte[] input) { + return toHexString(input, 0, input.length, false); + } + + public static String toHexStringWithPrefixZeroPadded(BigInteger value, int size) { + return toHexStringZeroPadded(value, size, true); + } + + public static String toHexStringWithPrefixSafe(BigInteger value) { + String result = toHexStringNoPrefix(value); + if (result.length() < 2) { + result = Strings.zeros(1) + result; + } + return HEX_PREFIX + result; + } + + public static String toHexStringNoPrefixZeroPadded(BigInteger value, int size) { + return toHexStringZeroPadded(value, size, false); + } + + private static String toHexStringZeroPadded(BigInteger value, int size, boolean withPrefix) { + String result = toHexStringNoPrefix(value); + + int length = result.length(); + if (length > size) { + throw new UnsupportedOperationException( + "Value " + result + "is larger then length " + size); + } else if (value.signum() < 0) { + throw new UnsupportedOperationException("Value cannot be negative"); + } + + if (length < size) { + result = Strings.zeros(size - length) + result; + } + + if (withPrefix) { + return HEX_PREFIX + result; + } else { + return result; + } + } + + public static byte[] toBytesPadded(BigInteger value, int length) { + byte[] result = new byte[length]; + byte[] bytes = value.toByteArray(); + + int bytesLength; + int srcOffset; + if (bytes[0] == 0) { + bytesLength = bytes.length - 1; + srcOffset = 1; + } else { + bytesLength = bytes.length; + srcOffset = 0; + } + + if (bytesLength > length) { + throw new RuntimeException("Input is too large to put in byte array of size " + length); + } + + int destOffset = length - bytesLength; + System.arraycopy(bytes, srcOffset, result, destOffset, bytesLength); + return result; + } + + public static byte[] hexStringToByteArray(String input) { + String cleanInput = cleanHexPrefix(input); + + int len = cleanInput.length(); + + if (len == 0) { + return new byte[] {}; + } + + byte[] data; + int startIdx; + if (len % 2 != 0) { + data = new byte[(len / 2) + 1]; + data[0] = (byte) Character.digit(cleanInput.charAt(0), 16); + startIdx = 1; + } else { + data = new byte[len / 2]; + startIdx = 0; + } + + for (int i = startIdx; i < len; i += 2) { + data[(i + 1) / 2] = (byte) ((Character.digit(cleanInput.charAt(i), 16) << 4) + + Character.digit(cleanInput.charAt(i + 1), 16)); + } + return data; + } + + public static String toHexString(byte[] input, int offset, int length, boolean withPrefix) { + StringBuilder stringBuilder = new StringBuilder(); + if (withPrefix) { + stringBuilder.append("0x"); + } + for (int i = offset; i < offset + length; i++) { + stringBuilder.append(String.format("%02x", input[i] & 0xFF)); + } + + return stringBuilder.toString(); + } + + public static String toHexString(byte[] input) { + return toHexString(input, 0, input.length, true); + } + + public static byte asByte(int m, int n) { + return (byte) ((m << 4) | n); + } + + public static boolean isIntegerValue(BigDecimal value) { + return value.signum() == 0 + || value.scale() <= 0 + || value.stripTrailingZeros().scale() <= 0; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/utils/Strings.java b/SignSdk/src/main/java/com/onlychain/signsdk/utils/Strings.java new file mode 100644 index 0000000..e27aba8 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/utils/Strings.java @@ -0,0 +1,57 @@ +package com.onlychain.signsdk.utils; + +import java.util.List; + +/** + * String utility functions. + */ +public class Strings { + + private Strings() {} + + public static String toCsv(List src) { + return join(src, ", "); + } + + public static String join(List src, String delimiter) { + if (src != null) { + StringBuilder builder = new StringBuilder(); + if (!src.isEmpty()) { + builder.append(src.get(0)); + } + for (int i = 1; i < src.size(); i++) { + builder.append(delimiter).append(src.get(i)); + } + return builder.toString(); + } + return null; + } + + public static String capitaliseFirstLetter(String string) { + if (string == null || string.length() == 0) { + return string; + } else { + return string.substring(0, 1).toUpperCase() + string.substring(1); + } + } + + public static String lowercaseFirstLetter(String string) { + if (string == null || string.length() == 0) { + return string; + } else { + return string.substring(0, 1).toLowerCase() + string.substring(1); + } + } + + public static String zeros(int n) { + return repeat('0', n); + } + + public static String repeat(char value, int n) { + return new String(new char[n]).replace("\0", String.valueOf(value)); + } + + public static boolean isEmpty(String s) { + return s == null || s.length() == 0; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/utils/WalletUtils.java b/SignSdk/src/main/java/com/onlychain/signsdk/utils/WalletUtils.java new file mode 100644 index 0000000..7e91674 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/utils/WalletUtils.java @@ -0,0 +1,71 @@ +package com.onlychain.signsdk.utils; + + +import com.onlychain.signsdk.bean.AccountBean; +import com.onlychain.signsdk.crypto.Hash; +import com.onlychain.signsdk.crypto.RIPEMD160Digest; +import com.onlychain.signsdk.secp256k1.Secp256k1; + +public class WalletUtils { + + /** + * 根据私钥生成账户信息 + * @return + */ + public static AccountBean createAccount(){ + return createAccount(Secp256k1.createPrivateKey()); + } + + /** + * 随机创建一组私钥、公钥、地址 + * @return + */ + public static AccountBean createAccount(byte[] privateKey){ + AccountBean mAccountBean=new AccountBean(); + try { + byte[] publicKey = Secp256k1.createPublicKey(privateKey).serialize(true); + mAccountBean.setPrivateKey(OcMath.toHexStringNoPrefix(privateKey)); + mAccountBean.setPublicKey(OcMath.toHexStringNoPrefix(publicKey)); + mAccountBean.setAddress(createAddress(publicKey)); + }catch (Exception e){ + + } + return mAccountBean; + } + + + /** + * 获取整交易的TxId + * @param input + * @return + */ + public static String getTxId(byte[] input){ + return OcMath.toHexStringNoPrefix(Hash.sha256(Hash.sha256(input))); + } + + /** + * 获取整交易的TxId + * @param input + * @return + */ + public static String getTxId(String input){ + return OcMath.toHexStringNoPrefix(Hash.sha256(Hash.sha256(OcMath.hexStringToByteArray(input)))); + } + + public static byte[] getTxIdBin(String input){ + return Hash.sha256(Hash.sha256(OcMath.hexStringToByteArray(input))); + } + + public static byte[] getTxIdBin(byte[] input){ + return Hash.sha256(Hash.sha256(input)); + } + + public static String createAddress(byte[] publicKey) { + byte[] sha256 = Hash.sha256(publicKey); + RIPEMD160Digest digest = new RIPEMD160Digest(); + digest.update(sha256, 0, sha256.length); + byte[] out = new byte[digest.getDigestSize()]; + digest.doFinal(out, 0); + return OcMath.toHexStringNoPrefix(out).toLowerCase(); + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/base/ApiConfig.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/base/ApiConfig.java new file mode 100644 index 0000000..e175d3a --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/base/ApiConfig.java @@ -0,0 +1,87 @@ +package com.onlychain.signsdk.wallet.base; + +import java.util.LinkedHashMap; + +public class ApiConfig { + public static String API_selectPurse; + public static String API_receiveAction; + public static String API_getSystemInfo; + public static String API_queryAction; + public static String API_queryBlock; + public static String IPs; + + private static ApiConfig INSTANCE = null; + public static ApiConfig getInstance() { + if(INSTANCE == null){ + INSTANCE = new ApiConfig(); + } + return INSTANCE; + } + + + /** + * 初始化 + * @param IP + */ + public static ApiConfig init(String IP) { + IPs=IP; + //获取钱包资产列表 + API_selectPurse= IP+"/Trading/selectPurse"; + //提交交易 + API_receiveAction= IP+"/Action/receiveAction"; + //获取系统时间、高度、轮次、轮次时间 + API_getSystemInfo= IP+"/Node/getSystemInfo"; + //查询某一个区块 + API_queryBlock= IP+"/Block/queryBlock"; + //查询某一个Txid的Action + API_queryAction= IP+"/Action/queryAction"; + return getInstance(); + } + + //获取钱包资产列表 + public String getSystemInfoUrl(){ + return IPs+"/Node/getSystemInfo"; + } + + //获取钱包资产列表 + public LinkedHashMap selectPurseUrl(String address){ + return selectPurse(address); + } + public static LinkedHashMap selectPurse(String address){ + LinkedHashMap map=new LinkedHashMap<>(); + map.put("address",address); + return map; + } + + //提交交易 + public LinkedHashMap receiveActionUrl(String message){ + return receiveAction(message); + } + public static LinkedHashMap receiveAction(String message){ + LinkedHashMap map=new LinkedHashMap<>(); + map.put("message",message); + return map; + } + + //查询某一个区块 + public LinkedHashMap queryBlockUrl(String height){ + return queryBlock(height); + } + public static LinkedHashMap queryBlock(String height){ + LinkedHashMap map=new LinkedHashMap<>(); + map.put("height",Integer.valueOf(height)); + return map; + } + + //查询某一个Txid的Action + public LinkedHashMap queryActionUrl(String txId){ + return queryAction(txId); + } + public static LinkedHashMap queryAction(String txId){ + LinkedHashMap map=new LinkedHashMap<>(); + map.put("txId",txId); + return map; + } + + +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/iface/ImpGetAction.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/iface/ImpGetAction.java new file mode 100644 index 0000000..938102e --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/iface/ImpGetAction.java @@ -0,0 +1,6 @@ +package com.onlychain.signsdk.wallet.iface; + +public interface ImpGetAction { + + public void receive(String actionStr); +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/iface/ImpQueryAction.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/iface/ImpQueryAction.java new file mode 100644 index 0000000..c8b9f2e --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/iface/ImpQueryAction.java @@ -0,0 +1,9 @@ +package com.onlychain.signsdk.wallet.iface; + +public interface ImpQueryAction { + + + public void inChainSuceess(StringBuffer json); + public void inChainFail(); + +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/GetVinCoin.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/GetVinCoin.java new file mode 100644 index 0000000..fe6c632 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/GetVinCoin.java @@ -0,0 +1,234 @@ +package com.onlychain.signsdk.wallet.tranfer; + +import com.alibaba.fastjson.JSON; +import com.onlychain.signsdk.bean.AccountBean; +import com.onlychain.signsdk.bean.PurseBean; +import com.onlychain.signsdk.net.Request; +import com.onlychain.signsdk.utils.CheckUtils; +import com.onlychain.signsdk.wallet.base.ApiConfig; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + +public abstract class GetVinCoin { + +public abstract void walletCoinList(StringBuffer json); +public abstract void getCoinFail(Exception e); + //代表被开通权益的资产 + public static final int TYPE_4_FOR_INTEREST=4; + //代表可用于普通转账的资产 + public static final int TYPE_1_FOR_TRANSFER=1; + //计算单位基数 + public static final String BASE_NUMBER="100000000"; + + private StringBuffer jsons; + public GetVinCoin(AccountBean mAccountBean) { + if (CheckUtils.checkAddress(mAccountBean.getAddress())) + { + new Request(ApiConfig.API_selectPurse, ApiConfig.selectPurse(mAccountBean.getAddressNoPrefix())) { + @Override + public void success(StringBuffer json) { + jsons=json; + walletCoinList(json); + } + + @Override + public void fail(Exception e) { + getCoinFail(e); + } + }; + }else { + getCoinFail(new Exception("请输入正确的地址 = oc+40位16进制chars")); + } + } + public GetVinCoin(String address) { + if (CheckUtils.checkAddress(address)) + { + new Request(ApiConfig.API_selectPurse, ApiConfig.selectPurse(address.split("oc")[1])) { + @Override + public void success(StringBuffer json) { + jsons=json; + walletCoinList(json); + } + + @Override + public void fail(Exception e) { + getCoinFail(e); + } + }; + }else { + getCoinFail(new Exception("请输入正确的地址 = oc+40位16进制chars")); + } + } + + /** + * 获取所有零钱列表 + * @return + */ + public List getCoinList(){ + return JSON.parseObject(jsons.toString(), PurseBean.class).getRecord(); + } + public List getCoinList(StringBuffer jsons){ + return JSON.parseObject(jsons.toString(), PurseBean.class).getRecord(); + } + /** + * 根据类型获取零钱列表 + * @param type + * @return + */ + public List getCoinList(int type){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: getCoinList()) + if (coin.getActionType()==type) + coinList.add(coin); + return coinList; + } + public List getCoinList(List coinsList,int type){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: coinsList) + if (coin.getActionType()==type) + coinList.add(coin); + return coinList; + } + public List getCoinList(StringBuffer jsons,int type){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: getCoinList(jsons)) + if (coin.getActionType()==type) + coinList.add(coin); + return coinList; + } + /** + * 根据是否小于指定高度获取指定零钱,比如获取已解锁零钱 + * @param height + * @return + */ + public List getCoinListForHeight(long height){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: getCoinList(TYPE_1_FOR_TRANSFER)) + if (coin.getLockTime() < height) + coinList.add(coin); + return coinList; + } + public List getCoinListForHeight(StringBuffer json,long height){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: getCoinList(json,TYPE_1_FOR_TRANSFER)) + if (coin.getLockTime() < height) + coinList.add(coin); + return coinList; + } + + public List getCoinListForLock(long height){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: getCoinList(TYPE_1_FOR_TRANSFER)) + if (coin.getLockTime() >= height) + coinList.add(coin); + return coinList; + } + /** + * 获取指定金额范围内的可用零钱列表 + * @param startValue + * @param endValue + * @return + */ + public List getCoinListByRange(String startValue,String endValue){ + List coinList=new ArrayList<>(); + + BigDecimal startDecimal=new BigDecimal(startValue); + BigDecimal endDecimal=new BigDecimal(endValue); + startDecimal=startDecimal.multiply(new BigDecimal(BASE_NUMBER)); + endDecimal=endDecimal.multiply(new BigDecimal(BASE_NUMBER)); + for (PurseBean.RecordBean coin : getCoinList(TYPE_1_FOR_TRANSFER)) + { + BigDecimal valueDecimal= new BigDecimal(coin.getValue()); + int startFlag =valueDecimal.compareTo(startDecimal); + int endFlag = valueDecimal.compareTo(endDecimal); + if (startFlag>-1 && endFlag<1) + coinList.add(coin); + } + + return coinList; + } + + /** + * 获取最适合开通权益的零钱 + * @return + */ + public PurseBean.RecordBean getCoinForInterest(){ + return getCoinForMin("100","120"); + } + + /** + * 自定义获取指定范围内的一笔最小零钱 + * @param startValue + * @param endValue + * @return + */ + public PurseBean.RecordBean getCoinForMin(String startValue,String endValue){ + List getCoinListByRange = getCoinListByRange(startValue,endValue); + List bigDecimal = new ArrayList(); + for (PurseBean.RecordBean coin : getCoinListByRange) + bigDecimal.add(new BigDecimal(coin.getValue())); + for (PurseBean.RecordBean coin : getCoinListByRange) + if (coin.getValue() == Float.valueOf(Collections.min(bigDecimal).toString())) + return coin; + return new PurseBean.RecordBean(); + } + + /** + * 自定义获取指定范围内的一笔最大零钱 + * @param startValue + * @param endValue + * @return + */ + public PurseBean.RecordBean getCoinForMax(String startValue,String endValue){ + List getCoinListByRange = getCoinListByRange(startValue,endValue); + List bigDecimal = new ArrayList(); + for (PurseBean.RecordBean coin : getCoinListByRange) + bigDecimal.add(new BigDecimal(coin.getValue())); + for (PurseBean.RecordBean coin : getCoinListByRange) + if (coin.getValue() == Float.valueOf(Collections.max(bigDecimal).toString())) + return coin; + return new PurseBean.RecordBean(); + } + + /** + * 获取余额,包含可用和不可用 + * @return + */ + public BigDecimal getBalance(){ + BigDecimal sum=new BigDecimal("0"); + for (PurseBean.RecordBean coin : getCoinList()) + sum=sum.add(new BigDecimal(coin.getValue())); + return sum.divide(new BigDecimal(BASE_NUMBER)); + } + + /** + * 根据类型获取余额 + * @param type + * @return + */ + public BigDecimal getBalance(int type){ + BigDecimal sum=new BigDecimal("0"); + for (PurseBean.RecordBean coin : getCoinList(type)) + sum=sum.add(new BigDecimal(coin.getValue())); + return sum.divide(new BigDecimal(BASE_NUMBER)); + } + + public BigDecimal getBalance(List coins){ + BigDecimal sum=new BigDecimal("0"); + for (PurseBean.RecordBean coin : coins) + sum=sum.add(new BigDecimal(coin.getValue())); + return sum.divide(new BigDecimal(BASE_NUMBER)); + } + + + public BigDecimal getBalanceLong(List coins){ + BigDecimal sum=new BigDecimal("0"); + for (PurseBean.RecordBean coin : coins) + sum=sum.add(new BigDecimal(coin.getValue())); + return sum; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/GetVinCoinLocal.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/GetVinCoinLocal.java new file mode 100644 index 0000000..f3ec3df --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/GetVinCoinLocal.java @@ -0,0 +1,198 @@ +package com.onlychain.signsdk.wallet.tranfer; + +import com.alibaba.fastjson.JSON; +import com.onlychain.signsdk.bean.AccountBean; +import com.onlychain.signsdk.bean.PurseBean; +import com.onlychain.signsdk.net.Request; +import com.onlychain.signsdk.utils.CheckUtils; +import com.onlychain.signsdk.wallet.base.ApiConfig; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + +public class GetVinCoinLocal { + + //代表被开通权益的资产 + public static final int TYPE_4_FOR_INTEREST=4; + //代表可用于普通转账的资产 + public static final int TYPE_1_FOR_TRANSFER=1; + //计算单位基数 + public static final String BASE_NUMBER="100000000"; + + private StringBuffer jsons; + public GetVinCoinLocal(StringBuffer jsons) { + this.jsons=jsons; + } + + + /** + * 获取所有零钱列表 + * @return + */ + public List getCoinList(){ + return JSON.parseObject(jsons.toString(), PurseBean.class).getRecord(); + } + public List getCoinList(StringBuffer jsons){ + return JSON.parseObject(jsons.toString(), PurseBean.class).getRecord(); + } + /** + * 根据类型获取零钱列表 + * @param type + * @return + */ + public List getCoinList(int type){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: getCoinList()) + if (coin.getActionType()==type) + coinList.add(coin); + return coinList; + } + public List getCoinList(List coinsList,int type){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: coinsList) + if (coin.getActionType()==type) + coinList.add(coin); + return coinList; + } + public List getCoinList(StringBuffer jsons,int type){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: getCoinList(jsons)) + if (coin.getActionType()==type) + coinList.add(coin); + return coinList; + } + /** + * 根据是否小于指定高度获取指定零钱,比如获取已解锁零钱 + * @param height + * @return + */ + public List getCoinListForHeight(long height){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: getCoinList(TYPE_1_FOR_TRANSFER)) + if (coin.getLockTime() < height) + coinList.add(coin); + return coinList; + } + public List getCoinListForHeight(StringBuffer json,long height){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: getCoinList(json,TYPE_1_FOR_TRANSFER)) + if (coin.getLockTime() < height) + coinList.add(coin); + return coinList; + } + + public List getCoinListForLock(long height){ + List coinList=new ArrayList<>(); + for (PurseBean.RecordBean coin: getCoinList(TYPE_1_FOR_TRANSFER)) + if (coin.getLockTime() >= height) + coinList.add(coin); + return coinList; + } + /** + * 获取指定金额范围内的可用零钱列表 + * @param startValue + * @param endValue + * @return + */ + public List getCoinListByRange(String startValue,String endValue){ + List coinList=new ArrayList<>(); + + BigDecimal startDecimal=new BigDecimal(startValue); + BigDecimal endDecimal=new BigDecimal(endValue); + startDecimal=startDecimal.multiply(new BigDecimal(BASE_NUMBER)); + endDecimal=endDecimal.multiply(new BigDecimal(BASE_NUMBER)); + for (PurseBean.RecordBean coin : getCoinList(TYPE_1_FOR_TRANSFER)) + { + BigDecimal valueDecimal= new BigDecimal(coin.getValue()); + int startFlag =valueDecimal.compareTo(startDecimal); + int endFlag = valueDecimal.compareTo(endDecimal); + if (startFlag>-1 && endFlag<1) + coinList.add(coin); + } + + return coinList; + } + + /** + * 获取最适合开通权益的零钱 + * @return + */ + public PurseBean.RecordBean getCoinForInterest(){ + return getCoinForMin("100","120"); + } + + /** + * 自定义获取指定范围内的一笔最小零钱 + * @param startValue + * @param endValue + * @return + */ + public PurseBean.RecordBean getCoinForMin(String startValue,String endValue){ + List getCoinListByRange = getCoinListByRange(startValue,endValue); + List bigDecimal = new ArrayList(); + for (PurseBean.RecordBean coin : getCoinListByRange) + bigDecimal.add(new BigDecimal(coin.getValue())); + for (PurseBean.RecordBean coin : getCoinListByRange) + if (coin.getValue() == Float.valueOf(Collections.min(bigDecimal).toString())) + return coin; + return new PurseBean.RecordBean(); + } + + /** + * 自定义获取指定范围内的一笔最大零钱 + * @param startValue + * @param endValue + * @return + */ + public PurseBean.RecordBean getCoinForMax(String startValue,String endValue){ + List getCoinListByRange = getCoinListByRange(startValue,endValue); + List bigDecimal = new ArrayList(); + for (PurseBean.RecordBean coin : getCoinListByRange) + bigDecimal.add(new BigDecimal(coin.getValue())); + for (PurseBean.RecordBean coin : getCoinListByRange) + if (coin.getValue() == Float.valueOf(Collections.max(bigDecimal).toString())) + return coin; + return new PurseBean.RecordBean(); + } + + /** + * 获取余额,包含可用和不可用 + * @return + */ + public BigDecimal getBalance(){ + BigDecimal sum=new BigDecimal("0"); + for (PurseBean.RecordBean coin : getCoinList()) + sum=sum.add(new BigDecimal(coin.getValue())); + return sum.divide(new BigDecimal(BASE_NUMBER)); + } + + /** + * 根据类型获取余额 + * @param type + * @return + */ + public BigDecimal getBalance(int type){ + BigDecimal sum=new BigDecimal("0"); + for (PurseBean.RecordBean coin : getCoinList(type)) + sum=sum.add(new BigDecimal(coin.getValue())); + return sum.divide(new BigDecimal(BASE_NUMBER)); + } + + public BigDecimal getBalance(List coins){ + BigDecimal sum=new BigDecimal("0"); + for (PurseBean.RecordBean coin : coins) + sum=sum.add(new BigDecimal(coin.getValue())); + return sum.divide(new BigDecimal(BASE_NUMBER)); + } + + + public BigDecimal getBalanceLong(List coins){ + BigDecimal sum=new BigDecimal("0"); + for (PurseBean.RecordBean coin : coins) + sum=sum.add(new BigDecimal(coin.getValue())); + return sum; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/MakeAction.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/MakeAction.java new file mode 100644 index 0000000..4e17c8c --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/MakeAction.java @@ -0,0 +1,96 @@ +package com.onlychain.signsdk.wallet.tranfer; + + +import com.onlychain.signsdk.bean.AccountBean; +import com.onlychain.signsdk.bean.BaseActionBean; +import com.onlychain.signsdk.bean.HeadBean; +import com.onlychain.signsdk.bean.OutBean; +import com.onlychain.signsdk.bean.PurseBean; +import com.onlychain.signsdk.crypto.Hash; +import com.onlychain.signsdk.secp256k1.Secp256k1; +import com.onlychain.signsdk.utils.Leb128Utils; +import com.onlychain.signsdk.utils.OcMath; +import com.onlychain.signsdk.utils.WalletUtils; + +import java.math.BigInteger; +import java.util.List; + +public class MakeAction { + AccountBean mAccountBean; + List coinList; + List outList; + int actionType=1; + long lockHeight=0; + + public MakeAction(AccountBean mAccountBean,int actionType,List coinList, List outList) { + this.mAccountBean = mAccountBean; + this.actionType=actionType; + this.coinList = coinList; + this.outList=outList; + this.lockHeight=-1; + } + + public MakeAction(AccountBean mAccountBean,int actionType,List coinList, List outList,long lockHeight) { + this.mAccountBean = mAccountBean; + this.actionType=actionType; + this.coinList = coinList; + this.outList=outList; + this.lockHeight=lockHeight; + } + + + /** + * 普通交易 + * @return + */ + public BaseActionBean createAction(String height){ + HeadBean head=new HeadBean(actionType,setVInBody()+setVOutBody()); + HeadBean.EndBean mEndBean = new HeadBean.EndBean(height,mAccountBean.getPublicKey(),head.toString()); + if(actionType==4) + mEndBean.setLockTimeAdd1Year(height); + + if(actionType==1) + if (lockHeight>0) + mEndBean.setLockTimeForCoin(lockHeight); + + String result=mEndBean.getResult(); + String sigStr = OcMath.toHexStringNoPrefix(Secp256k1.sign(mAccountBean.getPrivateKeyBin(), WalletUtils.getTxIdBin(result)).serialize()); + BaseActionBean mBaseActionBean=new BaseActionBean(); + mBaseActionBean.setMessage(result); + mBaseActionBean.setSignStr(sigStr); +// mBaseActionBean.setTxid(WalletUtils.getTxId(result)); + return mBaseActionBean; + } + + + private String setVInBody(){ + StringBuffer result=new StringBuffer(); + //LEB128 输入笔数 + String number= Leb128Utils.encodeUleb128(coinList.size()); + for(PurseBean.RecordBean coin:coinList) + { +// String txN=Leb128Utils.encodeUleb128(coin.getN()); + String txN=OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(coin.getN())),2); + String scriptContent = coin.getTxId()+txN+Script.getLockScript(mAccountBean.getAddressNoPrefix()); +// System.out.println("text--------------"+scriptContent); + scriptContent= Script.vInScript(mAccountBean, Hash.sha256(Hash.sha256(OcMath.hexStringToByteArray(scriptContent)))); + result = result.append(coin.getTxId()).append(txN).append(scriptContent); + } + return number+result.toString(); + } + + + private String setVOutBody(){ + StringBuffer result=new StringBuffer(); + //LEB128 输出笔数 + String number= Leb128Utils.encodeUleb128(outList.size()); + for(OutBean out:outList) + { + result = result.append(OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(out.getValue())),16)).append(Script.vOutScript(out.getAddress())); + } + return number+result.toString(); + } + + + +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/QueryUtils.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/QueryUtils.java new file mode 100644 index 0000000..49db9fe --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/QueryUtils.java @@ -0,0 +1,83 @@ +package com.onlychain.signsdk.wallet.tranfer; + + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.onlychain.signsdk.net.Request; +import com.onlychain.signsdk.wallet.base.ApiConfig; +import com.onlychain.signsdk.wallet.iface.ImpQueryAction; + +public class QueryUtils { + private ImpQueryAction impQueryAction; + private String txId; + private int number=0; + private Thread runTask; + private int howNumStop=0; + + + /** + * 轮询确认对应TXID是否成功上链 + * @param howNumStop 尝试查询次数,次数满回调为失败 + * @param txId + * @param impQueryAction + */ + public QueryUtils(int howNumStop,final String txId,ImpQueryAction impQueryAction) { + this.txId=txId; + this.impQueryAction=impQueryAction; + this.howNumStop=howNumStop; + runTask=new Thread(new Runnable() { + @Override + public void run() { + while (runTask!=null){ + queryAction(txId); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }); + runTask.start(); + } + + public QueryUtils() { + + } + + + public void queryAction(String txId){ + new Request(ApiConfig.API_queryAction, ApiConfig.queryAction(txId)) { + @Override + public void success(StringBuffer json) { + try{ + JSONObject record = JSON.parseObject(json.toString()); + number+=1; + if(record!=null && !record.get("record").toString().equals("[]") ) + { + runTask=null; + impQueryAction.inChainSuceess(json); + } + else if(number>=howNumStop){ + runTask=null; + impQueryAction.inChainFail(); + } + }catch (Exception e){ + e.printStackTrace(); + runTask=null; + impQueryAction.inChainFail(); + System.out.println("请检查节点或网络"); + } + + + } + @Override + public void fail(Exception e) { + + } + }; + + } + + +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/Script.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/Script.java new file mode 100644 index 0000000..bfc22c0 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/Script.java @@ -0,0 +1,56 @@ +package com.onlychain.signsdk.wallet.tranfer; + + + + +import com.onlychain.signsdk.bean.AccountBean; +import com.onlychain.signsdk.secp256k1.Secp256k1; +import com.onlychain.signsdk.secp256k1.Signature; +import com.onlychain.signsdk.utils.OcMath; + +import java.math.BigInteger; + +public class Script { + + /** + * 交易输入脚本 + * @param mAccountBean 账户对象 + * @param message 消息 + * @return 脚本结果 + */ + public static String vInScript(AccountBean mAccountBean, byte[] message){ + String sigResult; + String sigContent; + Signature sig = Secp256k1.sign(mAccountBean.getPrivateKeyBin(), message); +// System.out.println("sig--------------"+OcMath.toHexStringNoPrefix(sig.serialize())); + byte[] sigByte=sig.serialize(); + String sigStr= OcMath.toHexStringNoPrefix(sigByte); + String publicKeyStr=OcMath.toHexStringNoPrefix(mAccountBean.getPublicKeyBin()); + + sigContent=OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(sigByte.length)),2)+sigStr+OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(mAccountBean.getPublicKeyBin().length)),2)+publicKeyStr; + sigResult=OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(OcMath.hexStringToByteArray(sigContent).length)),4)+sigContent; + return sigResult; + } + + + /** + * 交易输出脚本 + * @param address 地址 + * @return 脚本结果 + */ + public static String vOutScript(String address){ + String scriptResult=getLockScript(address); + scriptResult=OcMath.toHexStringNoPrefixZeroPadded(new BigInteger(String.valueOf(OcMath.hexStringToByteArray(scriptResult).length)),4)+scriptResult; + return scriptResult; + } + + + /** + * 锁定脚本 + * @param address + * @return + */ + public static String getLockScript(String address){ + return "76a914"+address+"88ac"; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/StartTranfer.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/StartTranfer.java new file mode 100644 index 0000000..b89af07 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/StartTranfer.java @@ -0,0 +1,257 @@ +package com.onlychain.signsdk.wallet.tranfer; + + +import com.alibaba.fastjson.JSON; +import com.onlychain.signsdk.bean.AccountBean; +import com.onlychain.signsdk.bean.BaseActionBean; +import com.onlychain.signsdk.bean.GetSystemInfoBean; +import com.onlychain.signsdk.bean.OutBean; +import com.onlychain.signsdk.bean.PurseBean; +import com.onlychain.signsdk.net.Request; +import com.onlychain.signsdk.wallet.base.ApiConfig; +import com.onlychain.signsdk.wallet.iface.ImpGetAction; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import static com.onlychain.signsdk.wallet.tranfer.GetVinCoin.BASE_NUMBER; +import static com.onlychain.signsdk.wallet.tranfer.GetVinCoin.TYPE_1_FOR_TRANSFER; +import static com.onlychain.signsdk.wallet.tranfer.GetVinCoin.TYPE_4_FOR_INTEREST; + +/** + * 各类交易类型封装在此 + */ +public abstract class StartTranfer { + public abstract void receiveAction(BaseActionBean localCommitBean, StringBuffer json); + public abstract void receiveFail(Exception e); + private String ActionMsg; + private String Json; + private AccountBean mAccountBean; + private List outList; + private List coinLis; + + public StartTranfer(AccountBean mAccountBean) { + this.mAccountBean=mAccountBean; + } + + + public StartTranfer inputCoin(List coinLis){ + this.coinLis=coinLis; + return this; + } + + public StartTranfer inputCoin(PurseBean.RecordBean coin){ + List coinLis=new ArrayList<>(); + coinLis.add(coin); + this.coinLis=coinLis; + return this; + } + + + public StartTranfer inputAddress(List outList){ + this.outList=outList; + return this; + } + + public StartTranfer inputAddressList(List outoutList){ + //最多不要超过50 + if (outoutList.size()>50) + return null; + this.outList=outoutList; + return this; + } + + + public StartTranfer inputAddress(OutBean out){ + this.outList=new ArrayList<>(); + this.outList.add(out); + return this; + } + + public StartTranfer openInterest(PurseBean.RecordBean coin){ + List coinLis=new ArrayList<>(); + coinLis.add(coin); + this.coinLis=coinLis; +// commit(TYPE_4_FOR_INTEREST); + return this; + } + + + public void commit(){ + commit(TYPE_1_FOR_TRANSFER); + } + + /** + * 获取质押裸交易数据 + * @return + */ + public void getPledgeSignData(final ImpGetAction mImpGetAction){ + final long pledgeCoin=Long.valueOf(String.valueOf(sumCoinList(this.coinLis))); + this.outList=new ArrayList<>(); + this.outList.add(new OutBean(pledgeCoin,mAccountBean.getAddressNoPrefix())); + + new Request(ApiConfig.API_getSystemInfo) { + @Override + public void success(StringBuffer json) { + GetSystemInfoBean.RecordBean mGetSystemInfoBean= JSON.parseObject(json.toString(), GetSystemInfoBean.class).getRecord(); + //根据coin数量计算锁定高度 + long lockHeight=(30*(int)Math.floor(pledgeCoin/Long.valueOf(BASE_NUMBER)))+Long.valueOf(String.valueOf(mGetSystemInfoBean.getBlockHeight())); + MakeAction mMakeAction = new MakeAction(mAccountBean,TYPE_1_FOR_TRANSFER,coinLis,outList,lockHeight); + final BaseActionBean localCommitBean=mMakeAction.createAction(String.valueOf(mGetSystemInfoBean.getBlockHeight())); + mImpGetAction.receive(localCommitBean.getCommitData()); + } + @Override + public void fail(Exception e) { + receiveFail(new Exception("获取最新高度失败,请检查节点是否正常")); + } + }; + } + + + /** + * 合并零钱或拆额零钱 + * @param excreteCoin + * @return + */ + public StartTranfer inputExcreteCoins(long excreteCoin){ + this.outList=new ArrayList<>(); + this.outList.add(new OutBean(excreteCoin,mAccountBean.getAddressNoPrefix())); + return this; + } + + /** + * 获取签名数据 + * @param actionType + * @param mImpGetAction + */ + public void getAction(final int actionType,final ImpGetAction mImpGetAction){ + //找零 + long findCoin=0; + if(actionType==TYPE_1_FOR_TRANSFER){ + findCoin= Long.valueOf(sumCoinList(coinLis).subtract(sumOutList(outList)).toString()); + //如果零钱全部转出则无需给自己找零 + if (findCoin!=0) + this.outList.add(new OutBean(findCoin,mAccountBean.getAddressNoPrefix())); + }else if(actionType==TYPE_4_FOR_INTEREST){ + this.outList=new ArrayList<>(); + this.outList.add(new OutBean(this.coinLis.get(0).getValue(),mAccountBean.getAddressNoPrefix())); + } + + + if(coinLis!=null&&coinLis.size()>0&&Long.valueOf(sumCoinList(coinLis).toString())>0) + { + if (findCoin<0) + { + System.out.println("有余额但不够抵扣"); + return; + } + }else { + System.out.println("完全没有余额"); + return; + } + new Request(ApiConfig.API_getSystemInfo) { + @Override + public void success(StringBuffer json) { + + GetSystemInfoBean.RecordBean mGetSystemInfoBean=JSON.parseObject(json.toString(), GetSystemInfoBean.class).getRecord(); + + MakeAction mMakeAction = new MakeAction(mAccountBean,actionType,coinLis,outList); + final BaseActionBean localCommitBean=mMakeAction.createAction(String.valueOf(mGetSystemInfoBean.getBlockHeight())); + mImpGetAction.receive(localCommitBean.getCommitData()); + } + @Override + public void fail(Exception e) { + receiveFail(new Exception("获取最新高度失败,请检查节点是否正常")); + } + }; + } + + + public void commit(final int actionType){ + //找零 + long findCoin=0; + if(actionType==TYPE_1_FOR_TRANSFER){ + findCoin= Long.valueOf(sumCoinList(coinLis).subtract(sumOutList(outList)).toString()); + //如果零钱全部转出则无需给自己找零 + if (findCoin!=0) + this.outList.add(new OutBean(findCoin,mAccountBean.getAddressNoPrefix())); + }else if(actionType==TYPE_4_FOR_INTEREST){ + this.outList=new ArrayList<>(); + this.outList.add(new OutBean(this.coinLis.get(0).getValue(),mAccountBean.getAddressNoPrefix())); + } + + + if(coinLis!=null&&coinLis.size()>0&&Long.valueOf(sumCoinList(coinLis).toString())>0) + { + if (findCoin<0) + { + System.out.println("有余额但不够抵扣"); + return; + } + }else { + System.out.println("完全没有余额"); + return; + } + + System.out.println(sumCoinList(coinLis)); + System.out.println(sumOutList(outList)); + System.out.println(findCoin); + + doCommit(actionType); + } + + + + + /** + * 做最后整合提交 + */ + public void doCommit(final int actionType){ + //先从节点获取最新高度信息 + new Request(ApiConfig.API_getSystemInfo) { + @Override + public void success(StringBuffer json) { + GetSystemInfoBean.RecordBean mGetSystemInfoBean=JSON.parseObject(json.toString(), GetSystemInfoBean.class).getRecord(); + + MakeAction mMakeAction = new MakeAction(mAccountBean,actionType,coinLis,outList); + final BaseActionBean localCommitBean=mMakeAction.createAction(String.valueOf(mGetSystemInfoBean.getBlockHeight())); + + System.out.println("提交的目标数据:"+localCommitBean.getCommitData()); + + //开始将Action序列化信息提交至节点 + new Request(ApiConfig.API_receiveAction,ApiConfig.receiveAction(localCommitBean.getCommitData())) { + @Override + public void success(StringBuffer json) { + receiveAction(localCommitBean,json); + } + + @Override + public void fail(Exception e) { + receiveFail(e); + } + }; + } + @Override + public void fail(Exception e) { + receiveFail(new Exception("获取最新高度失败,请检查节点是否正常")); + } + }; + } + + + + public BigDecimal sumCoinList(List coinList){ + BigDecimal sum=new BigDecimal("0"); + for (PurseBean.RecordBean coin : coinList) + sum=sum.add(new BigDecimal(coin.getValue())); + return sum; + } + + public BigDecimal sumOutList(List outList){ + BigDecimal sum=new BigDecimal("0"); + for (OutBean out : outList) + sum=sum.add(new BigDecimal(out.getValue())); + return sum; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/StartTranferLocal.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/StartTranferLocal.java new file mode 100644 index 0000000..fb1a1ec --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/tranfer/StartTranferLocal.java @@ -0,0 +1,205 @@ +package com.onlychain.signsdk.wallet.tranfer; + + +import com.alibaba.fastjson.JSON; +import com.onlychain.signsdk.bean.AccountBean; +import com.onlychain.signsdk.bean.BaseActionBean; +import com.onlychain.signsdk.bean.GetSystemInfoBean; +import com.onlychain.signsdk.bean.OutBean; +import com.onlychain.signsdk.bean.PurseBean; +import com.onlychain.signsdk.net.Request; +import com.onlychain.signsdk.wallet.base.ApiConfig; +import com.onlychain.signsdk.wallet.iface.ImpGetAction; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import static com.onlychain.signsdk.wallet.tranfer.GetVinCoin.BASE_NUMBER; +import static com.onlychain.signsdk.wallet.tranfer.GetVinCoin.TYPE_1_FOR_TRANSFER; +import static com.onlychain.signsdk.wallet.tranfer.GetVinCoin.TYPE_4_FOR_INTEREST; + +/** + * 各类交易类型封装在此 + */ +public class StartTranferLocal { + private String ActionMsg; + private String Json; + private AccountBean mAccountBean; + private List outList; + private List coinLis; + + public StartTranferLocal(AccountBean mAccountBean) { + this.mAccountBean=mAccountBean; + } + + + public StartTranferLocal inputCoin(List coinLis){ + this.coinLis=coinLis; + return this; + } + + public StartTranferLocal inputCoin(PurseBean.RecordBean coin){ + List coinLis=new ArrayList<>(); + coinLis.add(coin); + this.coinLis=coinLis; + return this; + } + + + public StartTranferLocal inputAddress(List outList){ + this.outList=outList; + return this; + } + + public StartTranferLocal inputAddressList(List outoutList){ + //最多不要超过50 + if (outoutList.size()>50) + return null; + this.outList=outoutList; + return this; + } + + + public StartTranferLocal inputAddress(OutBean out){ + this.outList=new ArrayList<>(); + this.outList.add(out); + return this; + } + + public StartTranferLocal openInterest(PurseBean.RecordBean coin){ + List coinLis=new ArrayList<>(); + coinLis.add(coin); + this.coinLis=coinLis; +// commit(TYPE_4_FOR_INTEREST); + return this; + } + +/* + public void commit(){ + commit(TYPE_1_FOR_TRANSFER); + }*/ + + /** + * 获取质押裸交易数据 + * @return + */ + public BaseActionBean getPledgeSignData(long Height){ + final long pledgeCoin=Long.valueOf(String.valueOf(sumCoinList(this.coinLis))); + this.outList=new ArrayList<>(); + this.outList.add(new OutBean(pledgeCoin,mAccountBean.getAddressNoPrefix())); + //根据coin数量计算锁定高度 + long lockHeight=(30*(int)Math.floor(pledgeCoin/Long.valueOf(BASE_NUMBER)))+Long.valueOf(String.valueOf(Height)); + MakeAction mMakeAction = new MakeAction(mAccountBean,TYPE_1_FOR_TRANSFER,coinLis,outList,lockHeight); + return mMakeAction.createAction(String.valueOf(Height)); + } + + + /** + * 合并零钱或拆额零钱 + * @param excreteCoin + * @return + */ + public StartTranferLocal inputExcreteCoins(long excreteCoin){ + this.outList=new ArrayList<>(); + this.outList.add(new OutBean(excreteCoin,mAccountBean.getAddressNoPrefix())); + return this; + } + + /** + * 获取签名数据 + * @param actionType + * @param height + */ + public BaseActionBean getAction( int actionType,long height){ + //找零 + long findCoin=0; + if(actionType==TYPE_1_FOR_TRANSFER){ + findCoin= Long.valueOf(sumCoinList(coinLis).subtract(sumOutList(outList)).toString()); + //如果零钱全部转出则无需给自己找零 + if (findCoin!=0) + this.outList.add(new OutBean(findCoin,mAccountBean.getAddressNoPrefix())); + }else if(actionType==TYPE_4_FOR_INTEREST){ + this.outList=new ArrayList<>(); + this.outList.add(new OutBean(this.coinLis.get(0).getValue(),mAccountBean.getAddressNoPrefix())); + } + + + if(coinLis!=null&&coinLis.size()>0&&Long.valueOf(sumCoinList(coinLis).toString())>0) + { + if (findCoin<0) + { + System.out.println("有余额但不够抵扣"); + return null; + } + }else { + System.out.println("完全没有余额"); + return null; + } + + MakeAction mMakeAction = new MakeAction(mAccountBean,actionType,coinLis,outList); + return mMakeAction.createAction(String.valueOf(height)); + } + + + public void commit(int actionType,long height){ + //找零 + long findCoin=0; + if(actionType==TYPE_1_FOR_TRANSFER){ + findCoin= Long.valueOf(sumCoinList(coinLis).subtract(sumOutList(outList)).toString()); + //如果零钱全部转出则无需给自己找零 + if (findCoin!=0) + this.outList.add(new OutBean(findCoin,mAccountBean.getAddressNoPrefix())); + }else if(actionType==TYPE_4_FOR_INTEREST){ + this.outList=new ArrayList<>(); + this.outList.add(new OutBean(this.coinLis.get(0).getValue(),mAccountBean.getAddressNoPrefix())); + } + + + if(coinLis!=null&&coinLis.size()>0&&Long.valueOf(sumCoinList(coinLis).toString())>0) + { + if (findCoin<0) + { + System.out.println("有余额但不够抵扣"); + return; + } + }else { + System.out.println("完全没有余额"); + return; + } + + System.out.println(sumCoinList(coinLis)); + System.out.println(sumOutList(outList)); + System.out.println(findCoin); + + doCommit(actionType, height); + } + + + + + /** + * 做最后整合提交 + */ + public BaseActionBean doCommit( int actionType,long height){ + //先从节点获取最新高度信息 + MakeAction mMakeAction = new MakeAction(mAccountBean,actionType,coinLis,outList); + return mMakeAction.createAction(String.valueOf(height)); + } + + + + public BigDecimal sumCoinList(List coinList){ + BigDecimal sum=new BigDecimal("0"); + for (PurseBean.RecordBean coin : coinList) + sum=sum.add(new BigDecimal(coin.getValue())); + return sum; + } + + public BigDecimal sumOutList(List outList){ + BigDecimal sum=new BigDecimal("0"); + for (OutBean out : outList) + sum=sum.add(new BigDecimal(out.getValue())); + return sum; + } +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/viewblock/GetBlockData.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/viewblock/GetBlockData.java new file mode 100644 index 0000000..f691f76 --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/viewblock/GetBlockData.java @@ -0,0 +1,38 @@ +package com.onlychain.signsdk.wallet.viewblock; + + +import com.alibaba.fastjson.JSON; +import com.onlychain.signsdk.bean.BlockBean; +import com.onlychain.signsdk.net.Request; +import com.onlychain.signsdk.wallet.base.ApiConfig; + +import java.util.List; + +public abstract class GetBlockData { + private String height; + public abstract void getSuccess(StringBuffer json,List tradList); + + + public GetBlockData(String height) { + this.height = height; + queryBlock(); + } + + + private void queryBlock(){ + new Request(ApiConfig.API_queryBlock,ApiConfig.queryBlock(height)) { + @Override + public void success(StringBuffer json) { + + BlockBean.RecordBean mRecord= JSON.parseObject(json.toString(), BlockBean.class).getRecord(); + getSuccess(json,mRecord.getTradingInfo()); + } + @Override + public void fail(Exception e) { + + } + }; + + } + +} diff --git a/SignSdk/src/main/java/com/onlychain/signsdk/wallet/viewblock/HexConvertJson.java b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/viewblock/HexConvertJson.java new file mode 100644 index 0000000..52d2b2b --- /dev/null +++ b/SignSdk/src/main/java/com/onlychain/signsdk/wallet/viewblock/HexConvertJson.java @@ -0,0 +1,140 @@ +package com.onlychain.signsdk.wallet.viewblock; + + + +import com.alibaba.fastjson.JSON; +import com.onlychain.signsdk.bean.AnalysisBean; +import com.onlychain.signsdk.utils.Leb128Utils; +import com.onlychain.signsdk.utils.OcMath; +import com.onlychain.signsdk.utils.WalletUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * 16进制转Json + */ +public class HexConvertJson { + private String actionStr; + private AnalysisBean actionBaseBean; + private String headStr; + public HexConvertJson(String actionStr) { + this.actionStr = actionStr; + actionBaseBean=new AnalysisBean(); + + setHead(); + } + + + private void setHead(){ + headStr=actionStr.substring(0,6); + actionBaseBean.setVersion(OcMath.toBigIntNoPrefix(headStr.substring(0,2)).toString()); + actionBaseBean.setActionType(OcMath.toBigIntNoPrefix(headStr.substring(2,4)).toString()); + actionBaseBean.setTxId(OcMath.toHexStringNoPrefix(WalletUtils.getTxIdBin(actionStr))); + + if(isTypes()) + { + setBody(); + }else { +// System.out.println("只支持actionType为1或4的交易类型解析"); + } + } + + /** + * actionType类型过滤 + * @return + */ + private boolean isTypes(){ + if(actionBaseBean.getActionType().equals("1")||actionBaseBean.getActionType().equals("4")) + return true; + else return false; + } + + private void setBody(){ + int bodyStartIndex=headStr.length()+getLebNum(headStr).length(); + AnalysisBean.TradingBean mTradingBean=new AnalysisBean.TradingBean(); + List vinList=new ArrayList<>(); + List voutList=new ArrayList<>(); + for (int i = 0; i< Leb128Utils.decodeUnsigned(getLebNum(headStr)); i++) + { + AnalysisBean.TradingBean.VinBean vinObj=new AnalysisBean.TradingBean.VinBean(); + bodyStartIndex=bodyStartIndex+64; + vinObj.setTxId(actionStr.substring(bodyStartIndex-64,bodyStartIndex)); + String lebStrN=actionStr.substring(bodyStartIndex,bodyStartIndex+2); + vinObj.setN(String.valueOf(OcMath.toBigIntNoPrefix(lebStrN))); + bodyStartIndex=bodyStartIndex+lebStrN.length(); + +// System.out.println(vinObj.getN()); + int scriptLength=Integer.valueOf(OcMath.toBigIntNoPrefix(actionStr.substring(bodyStartIndex,bodyStartIndex+4)).toString())*2; + bodyStartIndex=bodyStartIndex+4; + vinObj.setScriptSig(actionStr.substring(bodyStartIndex,bodyStartIndex+scriptLength)); + bodyStartIndex=bodyStartIndex+scriptLength; + + vinList.add(vinObj); + } + + String outSizeLeb=getLebNum(actionStr.substring(0,bodyStartIndex)); + bodyStartIndex=bodyStartIndex+outSizeLeb.length(); + for (int i=0;i coinList = getCoinList(json); + for (PurseBean.RecordBean pur:coinList) + System.out.println("(含权益、锁定、可流通的零钱)"+pur.getValue()+" TYPE:"+ pur.getActionType()); + + System.out.println("\n"); + + List coinListFor4 = getCoinList(coinList,TYPE_4_FOR_INTEREST); + for (PurseBean.RecordBean pur:coinListFor4) + System.out.println("(含权益的零钱)"+pur.getValue()+" TYPE:"+ pur.getActionType()); + + System.out.println("\n"); + + List coinListFor1 = getCoinList(coinList,TYPE_1_FOR_TRANSFER); + for (PurseBean.RecordBean pur:coinListFor1) + System.out.println("(含锁定、可流通的零钱)"+pur.getValue()+" TYPE:"+ pur.getActionType()); + + + System.out.println("\n"); + + //获取主网最新高度进行零钱锁定筛选可获得可流通的零钱和质押中零钱 + new Request(ApiConfig.API_getSystemInfo) { + @Override + public void success(StringBuffer json) { + long lastHeight=JSON.parseObject(json.toString(),GetSystemInfoBean.class).getRecord().getBlockHeight(); + List coinPurse=getCoinListForHeight(lastHeight); + for (PurseBean.RecordBean pur:coinPurse) + System.out.println("(可流通的零钱)"+pur.getValue()+" TYPE:"+ pur.getActionType()); + + System.out.println("\n"); + + + List coinPurseLock=getCoinListForLock(lastHeight); + for (PurseBean.RecordBean pur:coinPurseLock) + System.out.println("(投票质押中的零钱)"+pur.getValue()+" TYPE:"+ pur.getActionType()); + + } + @Override + public void fail(Exception e) { + + } + }; + + } + + @Override + public void getCoinFail(Exception e) { + //地址错误时候抛出异常 + System.out.println(e); + } + }; + + + + + + + + + + + + + + } + + + +} diff --git a/app/src/main/java/com/onlychain/ocsignsdk/AboutSign.java b/app/src/main/java/com/onlychain/ocsignsdk/AboutSign.java new file mode 100644 index 0000000..b662ec6 --- /dev/null +++ b/app/src/main/java/com/onlychain/ocsignsdk/AboutSign.java @@ -0,0 +1,118 @@ +package com.onlychain.ocsignsdk; + +import com.onlychain.ocsignsdk.data.JsonData; +import com.onlychain.signsdk.bean.AccountBean; +import com.onlychain.signsdk.bean.BaseActionBean; +import com.onlychain.signsdk.bean.OutBean; +import com.onlychain.signsdk.bean.PurseBean; +import com.onlychain.signsdk.net.Request; +import com.onlychain.signsdk.utils.OcMath; +import com.onlychain.signsdk.utils.WalletUtils; +import com.onlychain.signsdk.wallet.base.ApiConfig; +import com.onlychain.signsdk.wallet.tranfer.GetVinCoinLocal; +import com.onlychain.signsdk.wallet.tranfer.StartTranferLocal; + +import java.util.ArrayList; +import java.util.List; + +import static com.onlychain.signsdk.wallet.tranfer.GetVinCoinLocal.BASE_NUMBER; +import static com.onlychain.signsdk.wallet.tranfer.GetVinCoinLocal.TYPE_1_FOR_TRANSFER; +import static com.onlychain.signsdk.wallet.tranfer.GetVinCoinLocal.TYPE_4_FOR_INTEREST; + +/** + * 关于所有的裸签名操作 + */ +public class AboutSign { + + public static void main(String[] args) { + + //解析本地零钱json,或取出数据库中零钱列表对象 + //TODO 注意!!! + //TODO 注意!!! + //TODO 注意!!!,此处输入的JsonData.coinListJson零钱数据一定要正确,否则以下签名结果都是无法通过主网验证,此处JsonData.coinListJson零钱为模拟数据,非实际测试零钱 + GetVinCoinLocal mGetVinCoinLocal = new GetVinCoinLocal(new StringBuffer(JsonData.coinListJson)); + + //通过高度筛选出可用的零钱 + List coinPurse= mGetVinCoinLocal.getCoinListForHeight(1570004); + for (PurseBean.RecordBean pur:coinPurse) + System.out.println("(可流通的零钱)"+pur.getValue()+" TYPE:"+ pur.getActionType()); + + //———————————————————————————————————————————————————————————————————————————————————————————————————————————— + //初始化IP配置 + String ip="http://39.98.135.66:9082"; + ApiConfig.init(ip); + + //模拟一个最新高度 + long height=1570004; + //打印获取当前主网最新高度的API + System.out.println(ApiConfig.API_getSystemInfo); + System.out.println(ApiConfig.init(ip).getSystemInfoUrl()); + + //带网络请求获取最新高度,如需请求其它功能只需替换 ApiConfig.API_getSystemInfo ,如 ApiConfig.API_selectPurse + new Request(ApiConfig.API_getSystemInfo){ + @Override + public void success(StringBuffer json) { + //通过解析json取得高度,可将次高度存全局数据库进行控制零钱是否质押解锁 + } + + @Override + public void fail(Exception e) { + + } + }; + + //导入账户 + AccountBean mAccountBean=WalletUtils.createAccount(OcMath.hexStringToByteArray("ea23e889d590a443831a785a398ce74179f09dece2fe5bfda41f795c50240c62")); + StartTranferLocal mStartTranferLocal= new StartTranferLocal(mAccountBean); + + //TODO 生成单笔转账交易签名 + OutBean mOutBean= new OutBean(); + //设置目标转账地址 + mOutBean.setAddress("274579901ace0417d662a203c8c3dbbb40693d8d"); + mOutBean.setValue(10000*Long.valueOf(BASE_NUMBER)); + BaseActionBean singleTrans = mStartTranferLocal.inputCoin(coinPurse).inputAddress(mOutBean).getAction(TYPE_1_FOR_TRANSFER,height); + System.out.println("单笔转账:"+singleTrans.getCommitData()); + + //TODO 生成批量转账交易签名 + List outoutList = new ArrayList<>(); + outoutList.add(mOutBean); + outoutList.add(new OutBean(10000*Long.valueOf(BASE_NUMBER),"374579901ace0417d662a203c8c3dbbb40693d8d")); + BaseActionBean moreTrans = mStartTranferLocal.inputCoin(coinPurse).inputAddressList(outoutList).getAction(TYPE_1_FOR_TRANSFER,height); + System.out.println("多笔转账:"+moreTrans.getCommitData()); + + //TODO 单笔或多笔零钱质押签名 + BaseActionBean mPledge = mStartTranferLocal.inputCoin(coinPurse).getPledgeSignData(height); + System.out.println("单笔或多笔零钱质押:"+mPledge.getCommitData()); + + //TODO 单笔或多笔零钱拆额与合并签名 + BaseActionBean mExcreteCoins = mStartTranferLocal.inputCoin(coinPurse).inputExcreteCoins(990*Long.valueOf(BASE_NUMBER)).getAction(TYPE_1_FOR_TRANSFER,height); + System.out.println("单笔或多笔零钱拆额与合并:"+mExcreteCoins.getCommitData()); + + //TODO 开通权益签名,有符合指定范围的零钱才会触发以下打印 + BaseActionBean mOpenInterest = mStartTranferLocal.openInterest(mGetVinCoinLocal.getCoinForMin("20","30")).getAction(TYPE_4_FOR_INTEREST,height); + System.out.println("开通权益:"+mOpenInterest.getCommitData()); + + //TODO 获取某交易签名的订单号 + System.out.println("签名数据的订单凭据(TXID):"+mOpenInterest.getTxid()); + + //TODO 获取验证构造的签名合法性 + System.out.println("验证提交签名的合法性:"+mOpenInterest.checkSig(mAccountBean.getPublicKeyBin())); + + //TODO 获取验证构造的签名合法性 + System.out.println("验证指定签名的合法性:"+mOpenInterest.checkSign(mAccountBean.getPublicKeyBin(),mOpenInterest.getMessage(),mOpenInterest.getSignStr())); + + + //TODO 提交签名至主网,谨慎开启 +/* new Request(ApiConfig.API_receiveAction,ApiConfig.receiveAction(mOpenInterest.getCommitData())) { + @Override + public void success(StringBuffer json) { + + } + + @Override + public void fail(Exception e) { + + } + };*/ + } +} diff --git a/app/src/main/java/com/onlychain/ocsignsdk/ApiSignTools.java b/app/src/main/java/com/onlychain/ocsignsdk/ApiSignTools.java new file mode 100644 index 0000000..fdd4290 --- /dev/null +++ b/app/src/main/java/com/onlychain/ocsignsdk/ApiSignTools.java @@ -0,0 +1,31 @@ +package com.onlychain.ocsignsdk; + + +import com.onlychain.signsdk.bean.AccountBean; +import com.onlychain.signsdk.bean.BaseActionBean; +import com.onlychain.signsdk.utils.OcMath; +import com.onlychain.signsdk.utils.WalletUtils; + +/** + * 对自定义消息进行加签和解签 + * 使用场景:只允许来自本人的私钥(公钥、地址)身份进行访问自己数据,避免有人通过接口可以查看他人数据 + */ +public class ApiSignTools { + public static void main(String[] args) { + //根据确定的私钥生成账户 + final AccountBean mAccountBean= WalletUtils.createAccount(OcMath.hexStringToByteArray("1a23e889d590a443831a785a398ce74179f09dece2fe5bfda41f795c50240c62")); + //对消息进行加签和验签 + BaseActionBean mBaseActionBean=new BaseActionBean(); + + //移动端:对带有公钥的json数据加签,将签名结果加入请求header中 + String jsonMsgX="{\\\"msg\\\":{\\\"publicKey\\\":\\\"0355c48844bb6392dbd251167c92904eabbbc1982b39dc7ebcb2141063f0281e1f\\\",\\\"title\\\":\\\"下方标题\\\",\\\"content\\\":\\\"这是内容\\\"}}"; + String jsonMsg="ismessage"; + System.out.println(mAccountBean.getPrivateKey()); + System.out.println(mBaseActionBean.makeSign(mAccountBean.getPrivateKeyBin(),jsonMsg)); + + + + //服务端:提取json中公钥、整json内容以及header中的sign对其验签 + System.out.println(mBaseActionBean.checkApiSign(mAccountBean.getPublicKeyBin(),jsonMsg,"30450221008fe7f0f7bb55da842522ba91565065eae37c0bf3b06b402b129c34b288d8084402202247716b7cb03545eb455ef36c7c4070db905250615a9a4b975d49f5dadc2441")); + } +} diff --git a/app/src/main/java/com/onlychain/ocsignsdk/CommitBlockApp.java b/app/src/main/java/com/onlychain/ocsignsdk/CommitBlockApp.java new file mode 100644 index 0000000..04007d9 --- /dev/null +++ b/app/src/main/java/com/onlychain/ocsignsdk/CommitBlockApp.java @@ -0,0 +1,224 @@ +package com.onlychain.ocsignsdk; + +import com.alibaba.fastjson.JSON; +import com.onlychain.signsdk.bean.AccountBean; +import com.onlychain.signsdk.bean.BaseActionBean; +import com.onlychain.signsdk.bean.GetSystemInfoBean; +import com.onlychain.signsdk.bean.OutBean; +import com.onlychain.signsdk.bean.PurseBean; +import com.onlychain.signsdk.net.Request; +import com.onlychain.signsdk.utils.OcMath; +import com.onlychain.signsdk.utils.WalletUtils; +import com.onlychain.signsdk.wallet.base.ApiConfig; +import com.onlychain.signsdk.wallet.iface.ImpGetAction; +import com.onlychain.signsdk.wallet.iface.ImpQueryAction; +import com.onlychain.signsdk.wallet.tranfer.GetVinCoin; +import com.onlychain.signsdk.wallet.tranfer.QueryUtils; +import com.onlychain.signsdk.wallet.tranfer.StartTranfer; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +public class CommitBlockApp +{ + static List coinPurse; + public static void main(String[] args ) + { + //根据随机私钥生成账户 + AccountBean mAccountBeanRandom= WalletUtils.createAccount(); + //获取带oc前缀的地址 + System.out.println("随机版,带oc前缀的地址 "+mAccountBeanRandom.getAddress()); + //获取不带oc前缀的地址 + System.out.println("随机版,不带oc前缀的地址 "+mAccountBeanRandom.getAddressNoPrefix()); + //获取公钥 + System.out.println("随机版,公钥 "+mAccountBeanRandom.getPublicKey()); + + //根据确定的私钥生成账户 + final AccountBean mAccountBean=WalletUtils.createAccount(OcMath.hexStringToByteArray("ea23e889d590a443831a785a398ce74179f09dece2fe5bfda41f795c50240c62")); + //获取带oc前缀的地址 + System.out.println("带oc前缀的地址 "+mAccountBean.getAddress()); + //获取不带oc前缀的地址 + System.out.println("不带oc前缀的地址 "+mAccountBean.getAddressNoPrefix()); + //获取公钥 + System.out.println("公钥 "+mAccountBean.getPublicKey()); + + //更换节点IP + ApiConfig.init("http://39.98.135.66:9082"); + + //获取链上指定地址的钱包零钱 + new GetVinCoin(mAccountBean) { + @Override + public void walletCoinList(StringBuffer json) { + + //获取钱包所有的零钱列表 + getCoinList(); + //获取钱包可流通的零钱列表 + getCoinList(TYPE_1_FOR_TRANSFER); + //获取钱包开通权益的零钱列表 + getCoinList(TYPE_4_FOR_INTEREST); + + //获取所有类型的余额 + System.out.println("获取所有类型的余额 "+getBalance()); + //获取可流通类型的余额 + System.out.println("获取零钱类型为1的所有余额 "+getBalance(TYPE_1_FOR_TRANSFER)); + //获取被开通权益的余额 + System.out.println("获取被开通权益的余额 "+getBalance(TYPE_4_FOR_INTEREST)); + //判断权益是否开通成功 + System.out.println("是否拥有权益:"+(getBalance(TYPE_4_FOR_INTEREST).compareTo(new BigDecimal("20"))>-1 ? "有":"没有")); + + //获取一笔最适合开通权益的零钱(默认在20~30之间取) + System.out.println("获取一笔最适合开通权益的零钱 "+getCoinForInterest().getValue()/Long.valueOf(BASE_NUMBER)); + //在指定范围内,获取一笔最大的零钱 + System.out.println("在指定范围内获取一笔最大的零钱 "+getCoinForMax("0","10000").getValue()/Long.valueOf(BASE_NUMBER)); + //在指定范围内,获取一笔最小的零钱 + System.out.println("在指定范围内获取一笔最小的零钱 "+getCoinForMin("10","10000").getValue()/Long.valueOf(BASE_NUMBER)); + + + final StartTranfer mStartTranfer=new StartTranfer(mAccountBean) { + @Override + public void receiveAction(final BaseActionBean localCommitBean, StringBuffer json) { + //获取Action的签名 + System.out.println(localCommitBean.getSignStr()); + //获取Action的消息体 + System.out.println(localCommitBean.getMessage()); + //获取Action的txid + System.out.println(localCommitBean.getTxid()); + //使用公钥验证消息签名的真实性 + System.out.println(localCommitBean.checkSig(mAccountBean.getPublicKeyBin())); + //获取提交后的结果,提交成功不等于上链成功,需要通过查询txid为准 + System.out.println("提交成功的返回结果=="+json); + + //通过查询Txid来确定是否上链成功 + new QueryUtils(4,localCommitBean.getTxid(), new ImpQueryAction() { + @Override + public void inChainSuceess(StringBuffer json) { + System.out.println("上链成功的交易数据:"+json); + System.out.println("上链成功TXID:"+localCommitBean.getTxid()); + } + @Override + public void inChainFail() { + System.out.println("上链失败"); + } + }); + } + @Override + public void receiveFail(Exception e) { + System.out.println(e); + } + }; + + // 开始向节点提交签名后的数据 + //获取当前钱包的零钱是否存在至少一笔type为4的标准权益质押量 + if(getBalance(TYPE_4_FOR_INTEREST).compareTo(new BigDecimal("20"))>-1) + { + //TODO --------------------------------以下业务不能同时运行 + + new Request(ApiConfig.API_getSystemInfo) { + @Override + public void success(StringBuffer json) { + //设定转账的目标地址 + OutBean mOutBean= new OutBean(); + mOutBean.setAddress("274579901ace0417d662a203c8c3dbbb40693d8d"); + mOutBean.setValue(10000*Long.valueOf(BASE_NUMBER)); + + OutBean mOutBean2= new OutBean(); + mOutBean2.setAddress("419d0c3fde261eeaecd2c47b484f20db3ef7558b"); + mOutBean2.setValue(1*Long.valueOf(BASE_NUMBER)); + + OutBean mOutBean3= new OutBean(); + mOutBean3.setAddress("479d0c3fde261eeaecd2c47b484f20db3ef7558b"); + mOutBean3.setValue(1*Long.valueOf(BASE_NUMBER)); + + List outoutList = new ArrayList<>(); + outoutList.add(mOutBean); + outoutList.add(mOutBean2); + outoutList.add(mOutBean3); + + + GetSystemInfoBean.RecordBean mGetSystemInfoBean= JSON.parseObject(json.toString(), GetSystemInfoBean.class).getRecord(); + System.out.println("最新高度=="+mGetSystemInfoBean.getBlockHeight()); + /* for (PurseBean.RecordBean pr:getCoinListForHeight(mGetSystemInfoBean.getBlockHeight())) + System.out.println(pr.getValue());*/ + + //获取可用零钱(不含权益零钱、锁定零钱) + coinPurse=getCoinListForHeight(mGetSystemInfoBean.getBlockHeight()); + //获取可流通类型的余额 + System.out.println("获取可流通类型的余额 "+getBalance(coinPurse)); + + + //TODO 提交普通单笔转账交易 +// mStartTranfer.inputCoin(coinPurse).inputAddress(mOutBean).commit(); + //TODO 获取单笔转账签名 + mStartTranfer.inputCoin(coinPurse).inputAddress(mOutBean).getAction(TYPE_1_FOR_TRANSFER, new ImpGetAction() { + @Override + public void receive(String actionStr) { + System.out.println(actionStr); + } + }); + + //TODO 提交普通批量转账交易 + //mStartTranfer.inputCoin(coinPurse).inputAddressList(outoutList).commit(); + //TODO 获取普通批量转账交易签名 + /* mStartTranfer.inputCoin(coinPurse).inputAddressList(outoutList).getAction(TYPE_1_FOR_TRANSFER, new ImpGetAction() { + @Override + public void receive(String actionStr) { + System.out.println(actionStr); + } + });*/ + //TODO 获取质押的裸交易数据 + /* mStartTranfer.inputCoin(coinPurse).getPledgeSignData(new ImpGetAction() { + @Override + public void receive(String actionStr) { + System.out.println("得到质押裸交易签名==="+actionStr); + } + });*/ + + //TODO 对零钱进行合并或拆额,如果自己的out为1个则为合并,如果自己的out为2个则为拆额 ,取整用 X*Long.valueOf(BASE_NUMBER) + //TODO 选某笔零钱进行拆额 +// mStartTranfer.inputCoin(coinPurse).inputExcreteCoins(20*Long.valueOf(BASE_NUMBER)).commit(); + //TODO 获取选某笔零钱进行拆额签名 + /* mStartTranfer.inputCoin(coinPurse.get(0)).inputExcreteCoins(3900l).getAction(TYPE_1_FOR_TRANSFER, new ImpGetAction() { + @Override + public void receive(String actionStr) { + System.out.println(actionStr); + } + });*/ + + //TODO 选多笔零钱进行合并 +// mStartTranfer.inputCoin(coinPurse).inputExcreteCoins(getBalanceLong(coinPurse).longValue()).commit(); + //TODO 获取多笔零钱进行合并的签名,输入coinPurse零钱如果只有一笔,则没有合并的意义 + /* mStartTranfer.inputCoin(coinPurse).inputExcreteCoins(getBalanceLong(coinPurse).longValue()).getAction(TYPE_1_FOR_TRANSFER, new ImpGetAction() { + @Override + public void receive(String actionStr) { + System.out.println(actionStr); + } + });*/ + } + @Override + public void fail(Exception e) { + + } + }; + + } else{ + //TODO 开通权益 + mStartTranfer.openInterest(getCoinForMin("20","30")).commit(TYPE_4_FOR_INTEREST); + //TODO 获取开通权益签名 + /* mStartTranfer.openInterest(getCoinForMin("20","30")).getAction(TYPE_4_FOR_INTEREST, new ImpGetAction() { + @Override + public void receive(String actionStr) { + System.out.println(actionStr); + } + });*/ + } + } + + @Override + public void getCoinFail(Exception e) { + System.out.println((e)); + } + }; + + } +} diff --git a/app/src/main/java/com/onlychain/ocsignsdk/ViewBlockApp.java b/app/src/main/java/com/onlychain/ocsignsdk/ViewBlockApp.java new file mode 100644 index 0000000..8864003 --- /dev/null +++ b/app/src/main/java/com/onlychain/ocsignsdk/ViewBlockApp.java @@ -0,0 +1,42 @@ +package com.onlychain.ocsignsdk; + + + +import com.onlychain.signsdk.wallet.base.ApiConfig; +import com.onlychain.signsdk.wallet.viewblock.GetBlockData; +import com.onlychain.signsdk.wallet.viewblock.HexConvertJson; + +import java.util.List; + +/** + * 16进制Action本地解析,目前只保留actionType1和actionType4 + */ +public class ViewBlockApp { + public static void main(String[] args) { + //更换节点IP + ApiConfig.init("http://39.98.135.66:9082"); + + //获取指定高度的区块数据并解析成json,目前只保留actionType1和actionType4 + final long height=1349624; + new GetBlockData(String.valueOf(height)) { + @Override + public void getSuccess(StringBuffer json,List tradList) { + System.out.println("通过API获取到指定高度的区块数据 : "+json); + for (String temp:tradList) + { + String result = new HexConvertJson(temp).getJson(); + + if (result!=null) + System.out.println("得到高度: "+height+" 的 [tradingInfo] 数据,且"+"TYPE为"+ + temp.substring(0,2)+"的数据 : " + +result); + } + } + }; + + + //对指定的序列化Action数据进行解析,格式为json + System.out.println(); + System.out.println("指定Action解析 : "+new HexConvertJson("010101021aaea3ca7f1c55967907869884386ac47f291a2cb618f5052385a787bb4e7ddb00006a47304502206f77a998f3da616e246b1f7e8f9828a390da606d31825f0b906e773b77c47603022100da17bab74105c9333c240c4d5361d030664056b4a767b4b99e7d8be002c8982e210355c48844bb6392dbd251167c92904eabbbc1982b39dc7ebcb2141063f0281e1f1aaea3ca7f1c55967907869884386ac47f291a2cb618f5052385a787bb4e7ddb01006a47304502202b0a958bfd2fef35281273a1386ccca23112fe755bf4778ee73f9424d24f948c022100d7cee58f73d6c2968c7fdfa200392d7f2a240a6ef67298f6e3a4355d97fb5190210355c48844bb6392dbd251167c92904eabbbc1982b39dc7ebcb2141063f0281e1f02000000e8d4a51000001976a914274579901ace0417d662a203c8c3dbbb40693d8d88ac000064d9f81b8550001976a9143b349701a36b457339e53ead5159750efc1f2a8988ac00000000000000000000edbebafa05d4e95f0355c48844bb6392dbd251167c92904eabbbc1982b39dc7ebcb2141063f0281e1f01003045022006c695010408f24d99376fcaea26ca9e3e40a2a8bb0fd5880eb3867920d2db62022100f05337482d70aed28cd09e7c1182b173336f14212d086071ab7e269d67bf007d").getJson()); + } +} diff --git a/app/src/main/java/com/onlychain/ocsignsdk/data/JsonData.java b/app/src/main/java/com/onlychain/ocsignsdk/data/JsonData.java new file mode 100644 index 0000000..9afdcd7 --- /dev/null +++ b/app/src/main/java/com/onlychain/ocsignsdk/data/JsonData.java @@ -0,0 +1,49 @@ +package com.onlychain.ocsignsdk.data; + +public class JsonData { + public static String coinListJson="{\n" + + " \"code\": 200,\n" + + " \"record\": [\n" + + " {\n" + + " \"txId\": \"15216c78776a7b039a3783314aeb343971c828487091774d7344f1756ee95618\",\n" + + " \"n\": 0,\n" + + " \"value\": 2600000000,\n" + + " \"reqSigs\": \"76a9143b349701a36b457339e53ead5159750efc1f2a8988ac\",\n" + + " \"createdBlock\": 693,\n" + + " \"lockTime\": 15768694,\n" + + " \"address\": \"3b349701a36b457339e53ead5159750efc1f2a89\",\n" + + " \"actionType\": 4\n" + + " },\n" + + " {\n" + + " \"txId\": \"1aaea3ca7f1c55967907869884386ac47f291a2cb618f5052385a787bb4e7ddb\",\n" + + " \"n\": 0,\n" + + " \"value\": 2000000000,\n" + + " \"reqSigs\": \"76a9143b349701a36b457339e53ead5159750efc1f2a8988ac\",\n" + + " \"createdBlock\": 1529147,\n" + + " \"lockTime\": 0,\n" + + " \"address\": \"3b349701a36b457339e53ead5159750efc1f2a89\",\n" + + " \"actionType\": 1\n" + + " },\n" + + " {\n" + + " \"txId\": \"1aaea3ca7f1c55967907869884386ac47f291a2cb618f5052385a787bb4e7ddb\",\n" + + " \"n\": 1,\n" + + " \"value\": 111885333234000,\n" + + " \"reqSigs\": \"76a9143b349701a36b457339e53ead5159750efc1f2a8988ac\",\n" + + " \"createdBlock\": 1529147,\n" + + " \"lockTime\": 0,\n" + + " \"address\": \"3b349701a36b457339e53ead5159750efc1f2a89\",\n" + + " \"actionType\": 1\n" + + " },\n" + + " {\n" + + " \"txId\": \"4a0da305c486f2f6c26ad8b1353edbb8078bb8443b9739ddbd2b88249de7c208\",\n" + + " \"n\": 0,\n" + + " \"value\": 10000000000,\n" + + " \"reqSigs\": \"76a9143b349701a36b457339e53ead5159750efc1f2a8988ac\",\n" + + " \"createdBlock\": 908843,\n" + + " \"lockTime\": 100000000,\n" + + " \"address\": \"3b349701a36b457339e53ead5159750efc1f2a89\",\n" + + " \"actionType\": 4\n" + + " }\n" + + " ]\n" + + "}"; +} diff --git a/build.gradle b/build.gradle index df01e52..dbd9d97 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,7 @@ allprojects { repositories { google() jcenter() + maven { url 'https://jitpack.io' } } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1b16c34..3c1c441 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue Sep 01 18:33:26 CST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip