delaredInterface, T instance) {
+ super(address, version, resolveContractDefinition(delaredInterface, instance.getClass()));
+ this.instance = instance;
+ }
+
+ private static ContractDefinition resolveContractDefinition(Class> declaredIntf, Class> implementedClass) {
+ ContractType contractType = ContractType.resolve(declaredIntf);
+ return new ContractDefinition(contractType, implementedClass);
+ }
+
+ @Override
+ protected T getContractInstance() {
+ return instance;
+ }
+
+ }
\ No newline at end of file
diff --git a/source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/ECDSAUtils.java b/source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/ECDSAUtils.java
index 097ce957..73b9e7b3 100644
--- a/source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/ECDSAUtils.java
+++ b/source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/ECDSAUtils.java
@@ -7,6 +7,8 @@ import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.ECDSASigner;
+import org.bouncycastle.jce.ECNamedCurveTable;
+import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
@@ -28,12 +30,10 @@ public class ECDSAUtils {
// p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1
// the curve equation is y^2 = x^3 + 7.
- private static final X9ECParameters X9_PARAMS = SECNamedCurves.getByName("secp256k1");
- private static final ECCurve CURVE = X9_PARAMS.getCurve();
- private static final ECPoint ECDSA_G = X9_PARAMS.getG();
- private static final BigInteger ECDSA_H = X9_PARAMS.getH();
- private static final BigInteger ECDSA_N = X9_PARAMS.getN();
- private static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, ECDSA_G,ECDSA_N,ECDSA_H);
+ private static final ECNamedCurveParameterSpec PARAMS = ECNamedCurveTable.getParameterSpec("secp256k1");
+ private static final ECCurve CURVE = PARAMS.getCurve();
+ private static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(
+ CURVE, PARAMS.getG(), PARAMS.getN(), PARAMS.getH());
//-----------------Key Generation Algorithm-----------------
diff --git a/source/crypto/crypto-pki/src/main/java/com/jd/blockchain/crypto/utils/CSRBuilder.java b/source/crypto/crypto-pki/src/main/java/com/jd/blockchain/crypto/utils/CSRBuilder.java
index 790cb7db..3de606db 100644
--- a/source/crypto/crypto-pki/src/main/java/com/jd/blockchain/crypto/utils/CSRBuilder.java
+++ b/source/crypto/crypto-pki/src/main/java/com/jd/blockchain/crypto/utils/CSRBuilder.java
@@ -16,6 +16,9 @@ import org.bouncycastle.util.encoders.Base64;
import java.io.IOException;
import java.security.*;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
/**
* @author zhanglin33
@@ -25,16 +28,18 @@ import java.security.*;
*/
public class CSRBuilder {
- private String BC = BouncyCastleProvider.PROVIDER_NAME;
+ private final String BC = BouncyCastleProvider.PROVIDER_NAME;
private PublicKey pubKey;
private PrivateKey privKey;
private String algoName;
+ private int keyLength;
public void init() {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
algoName = "SHA1withRSA";
+ keyLength = 2048;
KeyPairGenerator generator;
try {
generator = KeyPairGenerator.getInstance("RSA", BC);
@@ -47,10 +52,11 @@ public class CSRBuilder {
}
}
- public void init(String algoName, int KeyLength) {
+ public void init(String algoName, int keyLength) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
- this.algoName = algoName;
+ this.algoName = algoName;
+ this.keyLength = keyLength;
KeyPairGenerator generator;
KeyPair keyPair;
@@ -60,34 +66,69 @@ public class CSRBuilder {
switch (hashAndSignature[1]) {
case "RSA": {
generator = KeyPairGenerator.getInstance("RSA", BC);
- generator.initialize(KeyLength);
- keyPair = generator.generateKeyPair();
- pubKey = keyPair.getPublic();
- privKey = keyPair.getPrivate();
+ generator.initialize(keyLength);
break;
}
case "SM2": {
generator = KeyPairGenerator.getInstance("EC", BC);
- if (KeyLength != 256) {
+ if (keyLength != 256) {
throw new CryptoException("SM3withSM2 with unsupported key length [" +
- KeyLength +"] in CSR!");
+ keyLength +"] in CSR!");
}
generator.initialize(new ECNamedCurveGenParameterSpec("sm2p256v1"));
- keyPair = generator.generateKeyPair();
- pubKey = keyPair.getPublic();
- privKey = keyPair.getPrivate();
break;
}
default: throw new CryptoException("Unsupported algorithm [" + algoName + "] with key length [" +
- KeyLength +"] in CSR!");
+ keyLength +"] in CSR!");
}
+ keyPair = generator.generateKeyPair();
+ pubKey = keyPair.getPublic();
+ privKey = keyPair.getPrivate();
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
throw new CryptoException(e.getMessage(), e);
}
}
+ public void init(String algoName, byte[] pubKeyBytes, byte[] privKeyBytes) {
+
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+ this.algoName = algoName;
+ String[] hashAndSignature = algoName.split("with");
+
+ X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(pubKeyBytes);
+ PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privKeyBytes);
+
+ KeyFactory keyFactory;
+
+ try {
+ switch (hashAndSignature[1]) {
+ case "RSA": {
+ keyFactory = KeyFactory.getInstance("RSA");
+ privKey = keyFactory.generatePrivate(privKeySpec);
+ pubKey = keyFactory.generatePublic(pubKeySpec);
+ keyLength = (pubKey.getEncoded().length < 4096 / 8)? 2048: 4096;
+ break;
+ }
+
+ case "SM2": {
+ keyFactory = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
+ privKey = keyFactory.generatePrivate(privKeySpec);
+ pubKey = keyFactory.generatePublic(pubKeySpec);
+ keyLength = 256;
+ break;
+ }
+
+ default: throw new CryptoException("Unsupported algorithm [" + algoName + "] with the given key pair!");
+ }
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) {
+ throw new CryptoException(e.getMessage(), e);
+ }
+
+
+ }
+
public String buildRequest(String countryName, String stateName, String cityName,
String organizationName, String departmentName, String domainName,
String emailName) {
@@ -126,4 +167,12 @@ public class CSRBuilder {
public PrivateKey getPrivKey() {
return privKey;
}
+
+ public String getAlgoName() {
+ return algoName;
+ }
+
+ public int getKeyLength() {
+ return keyLength;
+ }
}
diff --git a/source/crypto/crypto-pki/src/main/java/com/jd/blockchain/crypto/utils/CertParser.java b/source/crypto/crypto-pki/src/main/java/com/jd/blockchain/crypto/utils/CertParser.java
index 8267d28b..e54c17db 100644
--- a/source/crypto/crypto-pki/src/main/java/com/jd/blockchain/crypto/utils/CertParser.java
+++ b/source/crypto/crypto-pki/src/main/java/com/jd/blockchain/crypto/utils/CertParser.java
@@ -25,6 +25,7 @@ public class CertParser {
private String sigAlgName;
private String userName;
private String issuerName;
+ private int keyLength;
private Date startTime;
private Date endTime;
@@ -71,6 +72,17 @@ public class CertParser {
sigAlgName = userCert.getSigAlgName();
issuerName = userCert.getIssuerX500Principal().getName();
userName = userCert.getSubjectX500Principal().getName();
+
+ switch (sigAlgName) {
+ case "SM3WITHSM2": {
+ keyLength = 256;
+ break;
+ }
+ case "SHA1WITHRSA": {
+ keyLength = (pubKey.getEncoded().length < 4096 / 8)? 2048: 4096;
+ break;
+ }
+ }
}
// certificate string in Base64 format
@@ -121,6 +133,10 @@ public class CertParser {
return issuerName;
}
+ public int getKeyLength() {
+ return keyLength;
+ }
+
public Date getStartTime() {
return startTime;
}
diff --git a/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SHA1WITHRSA2048SignatureFunctionTest.java b/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SHA1WITHRSA2048SignatureFunctionTest.java
index b7c7d831..64ca7eb5 100644
--- a/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SHA1WITHRSA2048SignatureFunctionTest.java
+++ b/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SHA1WITHRSA2048SignatureFunctionTest.java
@@ -1,9 +1,13 @@
package com.jd.blockchain.crypto.service.pki;
import com.jd.blockchain.crypto.*;
+import com.jd.blockchain.crypto.utils.CSRBuilder;
+import com.jd.blockchain.crypto.utils.CertParser;
import com.jd.blockchain.utils.io.BytesUtils;
+import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;
+import java.security.*;
import java.util.Random;
import static com.jd.blockchain.crypto.CryptoAlgorithm.*;
@@ -125,4 +129,145 @@ public class SHA1WITHRSA2048SignatureFunctionTest {
resolvedSignatureDigest.getAlgorithm());
assertArrayEquals(signatureDigestBytes, resolvedSignatureDigest.toBytes());
}
+
+ @Test
+ public void testWithCSRAndCert() {
+
+ String countryName = "CN";
+ String stateName = "Beijing";
+ String cityName = "Beijing";
+ String organizationName = "JD.com";
+ String departmentName = "Blockchain Department";
+ String domainName = "ledger.jd.com";
+ String emailName = "zhanglin33@jd.com";
+
+
+ String publicKeyStr = "30820122300d06092a864886f70d01010105000382010f003082010a0282010100c91e978897a36b" +
+ "30a57d265807441b0eff40c5572ecf029cd0cb2999b715edd2d5345c7b60c003075b1352629d03b943d08b25bfc5245" +
+ "a400f9ecbc1757394eac452d6316bf90cfcff5edfc427e277aa5266e89b1b2daa2e69dad5575515f49417c9ff332c83" +
+ "0dcd5537381f08e00b11123ad947bb11b18666d264ab5d8cdc92d0967fd7e0e6677746e2f01270d0594f74cda4e9d77" +
+ "4c6f3824499896bfb6424684af260d71b57a1366b741104fc647d9e38de055568daad60c116686e4afa1e9c83e9e30c" +
+ "7216e61353da2f75b2dde2c0ae870cf0ccc90490d1e22ecccbf3985d30933689847e6faf946993f930f83ac5b816075" +
+ "793a9a6df20727552a21be30203010001";
+
+ String privateKeyStr = "308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100c91e" +
+ "978897a36b30a57d265807441b0eff40c5572ecf029cd0cb2999b715edd2d5345c7b60c003075b1352629d03b943d08" +
+ "b25bfc5245a400f9ecbc1757394eac452d6316bf90cfcff5edfc427e277aa5266e89b1b2daa2e69dad5575515f49417" +
+ "c9ff332c830dcd5537381f08e00b11123ad947bb11b18666d264ab5d8cdc92d0967fd7e0e6677746e2f01270d0594f7" +
+ "4cda4e9d774c6f3824499896bfb6424684af260d71b57a1366b741104fc647d9e38de055568daad60c116686e4afa1e" +
+ "9c83e9e30c7216e61353da2f75b2dde2c0ae870cf0ccc90490d1e22ecccbf3985d30933689847e6faf946993f930f83" +
+ "ac5b816075793a9a6df20727552a21be30203010001028201003975637e93300d8a2ee573e47762f6461117cca96d46" +
+ "982cfc1be6ed3318f142a845d6dc2ad680a703d69fd56b9d6a3b1d23fbeb6f63c4e303736f2bfca5c2585631825f494" +
+ "534783d6f3a07bd0b5efbcaa1faf7814ac9118c8d8820f4be9a8b0ac6db819fc86b538bf284369d9f009a667668a82d" +
+ "224f7122041eddb492ef5b02d462aa11bc550bf3785a5d7538043586cfb0cd5c313a4746f22a5d4a89a412b279a7517" +
+ "176b1858a9677a1a60605aa0b2a7d260cf2e9e23a0e67c4a23ac046c62973239e84874c3695cc3f34073a62bd50b597" +
+ "ee06092a93fa6e41303ab4d2293ffad4c8db06e6516ae0a26a4d681305c3b7d535b540a638ca6cff1ce483750281810" +
+ "0e39967c5b287bd319b448255a1033591c74f4d688eb71f89a674cdb1be999e9abcf40e6b7f0bb3054262d7625002da" +
+ "7d34e56055ee130b66d682a0b9f8ea240a7e2dd1ff3f4b32f3bd352ba0a5dffc0315da34922493c267597787222213b" +
+ "f87d99fbd2a809c2647a1937cd1e303d746373db7c409a2ef33f8c06bb838bc612702818100e2374c71db5f0b4a199f" +
+ "f9d646a1785a84383a14aceb506f515b380c69ff45b51d0e941f55f0a234d8a3ee30bed656142bb5b985e462c44d234" +
+ "cfd424ecd6ca5fc70503862978ffe42b45bd698a99091d6fc65e2d85652e0ef56c52e8e05a6c5e879922c1d794e22f3" +
+ "51998217b5c6637a6abb716bf90cd1f23a2eedcdfface50281805d28a872224e2f2183e539d7e4ccd47b73f240c4004" +
+ "e72493c69e8dbcd2141eb22565f249edee20ad00e770c95a5655b0470b2cad964d030eab293292bfa62802cff824a10" +
+ "d52de8d8545024346106dd186fb53ef05bcea1d0dbfce2fac1cc8ec583fdc0ccdd9d498a983cea081ac55dc734aae84" +
+ "1ed802d6caf0e285c88b6d702818068b2a752daf1364c6967bd3e0b1a98956c3489cd1feb19232c4847bc97226aa4d4" +
+ "79f6dc39ee51649c0fe321f471470db6dd38ac5b73caded8c3bd437f2d5c67c65a450693bb0a0de7d989d7dc783e4d0" +
+ "16f77c871d02233b1123bd8bc2aa97157934cafd6445a819a93ddb4743cd141215b5cbdb5f7629398c48d0bcb17d671" +
+ "028181009282554329b89bcb2236a7e2a5bff53627e3ca7cc792d65236085741bc62a3f5767654722af561eff175664" +
+ "1af60e3d3959f63f0fec00cb29443ffca4ff751a76ecc6fa192ebe08ec9f643b9bb0d11bc90c1e850f0528ef223ea5f" +
+ "e4b4c107cc3a9eb26e6b84d74d87acbf7cc760cd788cbc30f95f8399077b25cdd924604c01";
+
+ String expectedCSR = "MIIC4jCCAcoCAQAwgZwxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW" +
+ "5nMQ8wDQYDVQQKDAZKRC5jb20xHjAcBgNVBAsMFUJsb2NrY2hhaW4gRGVwYXJ0bWVudDEWMBQGA1UEAwwNbGVkZ2VyLmpkL" +
+ "mNvbTEgMB4GCSqGSIb3DQEJARYRemhhbmdsaW4zM0BqZC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJ" +
+ "HpeIl6NrMKV9JlgHRBsO/0DFVy7PApzQyymZtxXt0tU0XHtgwAMHWxNSYp0DuUPQiyW/xSRaQA+ey8F1c5TqxFLWMWv5DPz" +
+ "/Xt/EJ+J3qlJm6JsbLaouadrVV1UV9JQXyf8zLIMNzVU3OB8I4AsREjrZR7sRsYZm0mSrXYzcktCWf9fg5md3RuLwEnDQWU" +
+ "90zaTp13TG84JEmYlr+2QkaEryYNcbV6E2a3QRBPxkfZ443gVVaNqtYMEWaG5K+h6cg+njDHIW5hNT2i91st3iwK6HDPDMy" +
+ "QSQ0eIuzMvzmF0wkzaJhH5vr5Rpk/kw+DrFuBYHV5Oppt8gcnVSohvjAgMBAAGgADANBgkqhkiG9w0BAQUFAAOCAQEALKHw" +
+ "BrlppbN+CSJwHxO99rv2XVD7L8lwwiMqdMzoNHxZUoob4AcGS3Iyxy6obX8fZh7DCA4nN0AmvrJCmWD3hCLclWKlwXTwvFe" +
+ "WIeB4xEB9MVPV6/Hs/QaI9Rjhd+O/Alfzov8y+KyYHEWmwxHj2RtQm1ZeulELzMvRjsqP+m5tOM1hLnPMU+AF1tZ9Fc7Jbm" +
+ "7Dwh6TAPpmaH1aVbHsnlZyCp8K4nMozVogcXJqg3qsbeJ6c/TofL0fURqiTJxLKMC0aD0TwVcNwDWEmc8cpqGheG1g6UF5l" +
+ "QBpeR2Br4f3LXJgr1Op1RxRy6I+X7h1IL38Q3jAOoU4y04O/+an7g==";
+
+ String expectedUserCert = "MIIERjCCAy6gAwIBAgIFIChmYJgwDQYJKoZIhvcNAQEFBQAwWTELMAkGA1UEBhMCQ04xMDAuBgNVB" +
+ "AoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEYMBYGA1UEAxMPQ0ZDQSBURVNUIE9DQTExMB4X" +
+ "DTE5MDgyODA2MzEyNVoXDTIxMDgyODA2MzEyNVoweDELMAkGA1UEBhMCQ04xGDAWBgNVBAoTD0NGQ0EgVEVTVCBPQ0ExMTE" +
+ "RMA8GA1UECxMITG9jYWwgUkExFTATBgNVBAsTDEluZGl2aWR1YWwtMTElMCMGA1UEAxQcMDUxQHpoYW5nbGluIUBaMTg2MT" +
+ "IyMjkyOTVANTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMkel4iXo2swpX0mWAdEGw7/QMVXLs8CnNDLKZm3F" +
+ "e3S1TRce2DAAwdbE1JinQO5Q9CLJb/FJFpAD57LwXVzlOrEUtYxa/kM/P9e38Qn4neqUmbomxstqi5p2tVXVRX0lBfJ/zMs" +
+ "gw3NVTc4HwjgCxESOtlHuxGxhmbSZKtdjNyS0JZ/1+DmZ3dG4vAScNBZT3TNpOnXdMbzgkSZiWv7ZCRoSvJg1xtXoTZrdBE" +
+ "E/GR9njjeBVVo2q1gwRZobkr6HpyD6eMMchbmE1PaL3Wy3eLArocM8MzJBJDR4i7My/OYXTCTNomEfm+vlGmT+TD4OsW4Fg" +
+ "dXk6mm3yBydVKiG+MCAwEAAaOB9TCB8jAfBgNVHSMEGDAWgBT8C7xEmg4xoYOpgYcnHgVCxr9W+DBIBgNVHSAEQTA/MD0GC" +
+ "GCBHIbvKgECMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2ZjYS5jb20uY24vdXMvdXMtMTUuaHRtMDoGA1UdHwQzMDEw" +
+ "L6AtoCuGKWh0dHA6Ly8yMTAuNzQuNDIuMy9PQ0ExMS9SU0EvY3JsMjY2NTAuY3JsMAsGA1UdDwQEAwID6DAdBgNVHQ4EFgQ" +
+ "UOTy45HymVDivPwA83lRoMpShasQwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMA0GCSqGSIb3DQEBBQUAA4IBAQ" +
+ "A7NFkDMV1mRV/07DJUdZJzvkm+QihhpLp7D2dUblCCEt0jjY5RdLiWG7HvJnlPTThiSkiejEO0Fy1cQzA5jYRhwp+70X8X9" +
+ "bt5jEBP/V4PyRXKKEvKZMdppLIeVI6rZk/gJzPh2FQYv3qWaINilxLOBP8Qa0kdMBwo6D6/MYwnSGv5zP4NLFUysLUJiKoM" +
+ "lAzEQSPNnkYRX6nogpkdN91/xgH3GA7fiihrjm5oxMAupCli9LQqvlUvRtv5EKIN9c+ixCAYFagG9IIjMDXbDne77n15i01" +
+ "420N8sjfTlr9v3W0v1gBSzjzFOT+TTTUtrjfO/Vm8iqq+z22QKIXgYjSF";
+
+ String issuerCert =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDzzCCAregAwIBAgIKUalCR1Mt5ZSK8jANBgkqhkiG9w0BAQUFADBZMQswCQYD\n" +
+ "VQQGEwJDTjEwMC4GA1UEChMnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24g\n" +
+ "QXV0aG9yaXR5MRgwFgYDVQQDEw9DRkNBIFRFU1QgQ1MgQ0EwHhcNMTIwODI5MDU1\n" +
+ "NDM2WhcNMzIwODI0MDU1NDM2WjBZMQswCQYDVQQGEwJDTjEwMC4GA1UEChMnQ2hp\n" +
+ "bmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRgwFgYDVQQDEw9D\n" +
+ "RkNBIFRFU1QgT0NBMTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8\n" +
+ "jn0n8Fp6hcRdACdn1+Y6GAkC6KGgNdKyHPrmsdmhCjnd/i4qUFwnG8cp3D4lFw1G\n" +
+ "jmjSO5yVYbik/NbS6lbNpRgTK3fDfMFvLJpRIC+IFhG9SdAC2hwjsH9qTpL9cK2M\n" +
+ "bSdrC6pBdDgnbzaM9AGBF4Y6vXhj5nah4ZMsBvDp19LzXjlGuTPLuAgv9ZlWknsd\n" +
+ "RN70PIAmvomd10uqX4GIJ4Jq/FdKXOLZ2DNK/NhRyN6Yq71L3ham6tutXeZow5t5\n" +
+ "0254AnUlo1u6SeH9F8itER653o/oMLFwp+63qXAcqrHUlOQPX+JI8fkumSqZ4F2F\n" +
+ "t/HfVMnqdtFNCnh5+eIBAgMBAAGjgZgwgZUwHwYDVR0jBBgwFoAUdN7FjQp9EBqq\n" +
+ "aYNbTSHOhpvMcTgwDAYDVR0TBAUwAwEB/zA4BgNVHR8EMTAvMC2gK6AphidodHRw\n" +
+ "Oi8vMjEwLjc0LjQyLjMvdGVzdHJjYS9SU0EvY3JsMS5jcmwwCwYDVR0PBAQDAgEG\n" +
+ "MB0GA1UdDgQWBBT8C7xEmg4xoYOpgYcnHgVCxr9W+DANBgkqhkiG9w0BAQUFAAOC\n" +
+ "AQEAb7W0K9fZPA+JPw6lRiMDaUJ0oh052yEXreMBfoPulxkBj439qombDiFggRLc\n" +
+ "3g8wIEKzMOzOKXTWtnzYwN3y/JQSuJb/M1QqOEEM2PZwCxI4AkBuH6jg03RjlkHg\n" +
+ "/kTtuIFp9ItBCC2/KkKlp0ENfn4XgVg2KtAjZ7lpyVU0LPnhEqqUVY/xthjlCSa7\n" +
+ "/XHNStRxsfCTIBUWJ8n2FZyQhfV/UkMNHDBIiJR0v6C4Ai0/290WvbPEIAq+03Si\n" +
+ "fsHzBeA0C8lP5VzfAr6wWePaZMCpStpLaoXNcAqReKxQllElOqAhRxC5VKH+rnIQ\n" +
+ "OMRZvB7FRyE9IfwKApngcZbA5g==\n" +
+ "-----END CERTIFICATE-----";
+
+ byte[] rawPublicKeyBytes = Hex.decode(publicKeyStr);
+ byte[] rawPrivateKeyBytes = Hex.decode(privateKeyStr);
+
+ CSRBuilder builder = new CSRBuilder();
+ builder.init("SHA1withRSA", rawPublicKeyBytes, rawPrivateKeyBytes);
+
+ String csr = builder.buildRequest(countryName,stateName,cityName,
+ organizationName,departmentName,domainName,
+ emailName);
+
+ assertEquals(expectedCSR,csr);
+
+ CertParser parser = new CertParser();
+ parser.parse(expectedUserCert,issuerCert);
+
+ PublicKey rawPublicKeyInCert = parser.getPubKey();
+ // check that the public key in inputs and the public key in certificate are consistent
+ assertArrayEquals(rawPublicKeyBytes, rawPublicKeyInCert.getEncoded());
+
+ String algoName = parser.getSigAlgName();
+ int keyLength = parser.getKeyLength();
+ String length = String.valueOf(keyLength);
+ String algo = (algoName.contains("RSA")? (algoName + length).toUpperCase(): algoName.toUpperCase());
+
+ CryptoAlgorithm algorithm = Crypto.getAlgorithm(algo);
+ assertNotNull(algorithm);
+ SignatureFunction signatureFunction = Crypto.getSignatureFunction(algorithm);
+
+ PubKey pubKey = new PubKey(algorithm, rawPublicKeyBytes);
+ PrivKey privKey = new PrivKey(algorithm, rawPrivateKeyBytes);
+
+ // signTest
+ byte[] data = new byte[1024];
+ Random random = new Random();
+ random.nextBytes(data);
+
+ SignatureDigest signature = signatureFunction.sign(privKey, data);
+ assertTrue(signatureFunction.verify(signature, pubKey, data));
+ }
}
diff --git a/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SHA1WITHRSA4096SignatureFunctionTest.java b/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SHA1WITHRSA4096SignatureFunctionTest.java
index 9444771b..466e245d 100644
--- a/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SHA1WITHRSA4096SignatureFunctionTest.java
+++ b/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SHA1WITHRSA4096SignatureFunctionTest.java
@@ -1,9 +1,13 @@
package com.jd.blockchain.crypto.service.pki;
import com.jd.blockchain.crypto.*;
+import com.jd.blockchain.crypto.utils.CSRBuilder;
+import com.jd.blockchain.crypto.utils.CertParser;
import com.jd.blockchain.utils.io.BytesUtils;
+import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;
+import java.security.PublicKey;
import java.util.Random;
import static com.jd.blockchain.crypto.CryptoAlgorithm.ASYMMETRIC_KEY;
@@ -125,4 +129,186 @@ public class SHA1WITHRSA4096SignatureFunctionTest {
resolvedSignatureDigest.getAlgorithm());
assertArrayEquals(signatureDigestBytes, resolvedSignatureDigest.toBytes());
}
+
+ @Test
+ public void testWithCSRAndCert() {
+
+ String countryName = "CN";
+ String stateName = "Beijing";
+ String cityName = "Beijing";
+ String organizationName = "JD.com";
+ String departmentName = "Blockchain Department";
+ String domainName = "ledger.jd.com";
+ String emailName = "zhanglin33@jd.com";
+
+
+ String publicKeyStr = "30820222300d06092a864886f70d01010105000382020f003082020a0282020100b40edee83b609e" +
+ "aa001f2496f95d2f3302513306ec9a8e7fce0d2e141fce7ee357a7465314c3f5f4b08cb6c95803c368ddbfedba483cb" +
+ "5c45914037ceee5783fc971a12ef9b0e4158dc379b59499eee629324a9beb350c4c10e50837345be128b91f43a03381" +
+ "758bbefe41c45712e4b5fdd5bde780167283706b24e37dd753db65b4c6b3e49cd8be825665d9a29a24b77e76473df02" +
+ "2327873555aa33ba2ffcc766cbefedc46ec868d10f817822540eaf5754c074dd6d428355ce24a058a4c9ce41e48aad5" +
+ "92e7955cf93d779d03d3acf25ae271346a9e4255e4ed902ae016032b04efbee98f43cd767653e089b37540e537aede9" +
+ "dbc04f8f1a858b2764b9eedac80b6a8da5ff02aab4be94e071c70718fde7227cdefec31600a1c55bac16f4de9dea8ab" +
+ "7824c1ec783b818dfe005f040a3f6872b1c7a6c31a66c1b06eb8d872a23d1b4fdadf9eed58f93b2a2bc145638a79a81" +
+ "904d39b22128ced18a2556c21888ed1ec8ad59bd6764f1ea16eb7f3574c53166f0827b5072d23017bd725adeb63eeb2" +
+ "29a4e78d4d7426e936753902bb51e3cd90630314f4ab41272a9e36cb668b2ba9c2ebc02e9ef0c377c88482e839f2f4d" +
+ "5c8efcfbe1280e52c6bdf80aa487ae03ff9dd9fd981f78172bc1141ce6031b0b8915658d830c696662507d3cf4ddba8" +
+ "7daabf97c15cbef58b15e84f16f879328c7c65076d94fc6b4514549831850203010001";
+
+ String privateKeyStr = "30820942020100300d06092a864886f70d01010105000482092c308209280201000282020100b40e" +
+ "dee83b609eaa001f2496f95d2f3302513306ec9a8e7fce0d2e141fce7ee357a7465314c3f5f4b08cb6c95803c368ddb" +
+ "fedba483cb5c45914037ceee5783fc971a12ef9b0e4158dc379b59499eee629324a9beb350c4c10e50837345be128b9" +
+ "1f43a03381758bbefe41c45712e4b5fdd5bde780167283706b24e37dd753db65b4c6b3e49cd8be825665d9a29a24b77" +
+ "e76473df022327873555aa33ba2ffcc766cbefedc46ec868d10f817822540eaf5754c074dd6d428355ce24a058a4c9c" +
+ "e41e48aad592e7955cf93d779d03d3acf25ae271346a9e4255e4ed902ae016032b04efbee98f43cd767653e089b3754" +
+ "0e537aede9dbc04f8f1a858b2764b9eedac80b6a8da5ff02aab4be94e071c70718fde7227cdefec31600a1c55bac16f" +
+ "4de9dea8ab7824c1ec783b818dfe005f040a3f6872b1c7a6c31a66c1b06eb8d872a23d1b4fdadf9eed58f93b2a2bc14" +
+ "5638a79a81904d39b22128ced18a2556c21888ed1ec8ad59bd6764f1ea16eb7f3574c53166f0827b5072d23017bd725" +
+ "adeb63eeb229a4e78d4d7426e936753902bb51e3cd90630314f4ab41272a9e36cb668b2ba9c2ebc02e9ef0c377c8848" +
+ "2e839f2f4d5c8efcfbe1280e52c6bdf80aa487ae03ff9dd9fd981f78172bc1141ce6031b0b8915658d830c696662507" +
+ "d3cf4ddba87daabf97c15cbef58b15e84f16f879328c7c65076d94fc6b451454983185020301000102820200063fece" +
+ "3452a579f817513454d3efb842afcac077dbf689a4d89de13533e4cdfb1bb6be0b6dc0d65b29a13bf1dd7b598e67782" +
+ "b6204b4128e149a54c59136c6ed45c661296169a78180d54a46595c939c26ccd33a7c095de6f08b01610726ef885a26" +
+ "cebbad5efc14bbe1204d15be5c5de5b64a5cc279b4e6e20bded4a8126973b2ac0e9de11c6a1282f7d060693909a30e0" +
+ "c49cc500bedd38ed99c18830a26dd39f772aabf527410d54ed338db022d674f21f1332d3b5d5f67234a58a97300d130" +
+ "aed0d46effc2b4e4895665934188d0c75749e26ca5b97645957989530657b332b4ef202b3d70fe2e07d0d526240fbe1" +
+ "68e320357be0f54e18008a233a8137e23ca1c54074b31c57eebee49bf2f1c66ea97a2c846a8d26680b97e1240d6763e" +
+ "60bcd8d696c806362b18bf0504a39e4d465a9548091dbe97c36f6d8e038d95a72c0a88ff524dc81fe0ed2afd69f4251" +
+ "1a90687a4c632c812234a19a7312b2dea3fcd4515800eca700733b0f83509184fe8d3cd21385f0ef0cec37c433d354f" +
+ "aed61662a62902c8708c81e2af20898f649c1bafd600baa0409c943cf82bc90ea20a8972da7ab3a252f4f08df2509de" +
+ "e1dacfe787eef4d60c0c92ec7a3c6277d7be39deed7704ac721d8efd138a410d632a32535142cf977b09d9fb680bc96" +
+ "c538c00440d5e1ed71f8510e6524e564d69a24d03f1a9a0c326b421e32550801c890282010100e8d72f8f1fbeada724" +
+ "3e6df25337a2e7fc43e3d4f39877f89abb3b5e453f20f339a1f35e0a2847122f0bd835e6b43fd276f447da04f85cfca" +
+ "0fa8bf49b313239e36f9595ef1bfb9b8247bf01cc407dfc444421161a5b2e96d4d1d90cea185945e9447d21a2d8a461" +
+ "f43dff9ba58043feeb49552a3bf2472eea59b2aa8631048c76a15898065be0ef957d4802c827e30339d66ad7aed5851" +
+ "a5896f12d33f45800dcb10859531c590af7a75e9fa81f1d937a287fb6b066d58720584af2ae161e083681655ae77f48" +
+ "34bfe0accf1d12bbdd8c2644f78b207cfcb2dedf9fe7d29e1ae5c2a5623f5a1770db27d2636450c79fd39bce39f009b" +
+ "598e0298e1f77bf8d3d0282010100c5f7af9258a42da93204cecb71c544397bc16691cf0d41308fbd3b88eca7d101d1" +
+ "377dfe3b7e66707d3e5543b21033ab6c5b0de577740c6e335f512eeb2a839c3f2baeca4ce80e3fbd8fb93fa983e1719" +
+ "5c5fee63bc163df334a80f2767871e3434adc0bc7030dea44fce414a08da7e918e6f030cfb20b2d29033e9ef1be3e08" +
+ "ef1f50df0c9325a20ff01d0b7a06761403ae3498aea8bad93110d61a6386d470e630990029e2cb098de40fafa330911" +
+ "6c3c6de180b7fa41ae14553e891148ab53e970ce372b1777826983baaccac08290e343761d8daa2505f1dc45b8511a3" +
+ "6e2d0909237be9ae7ebbaa00b31de1224f32959e4e6f3428140ca8e1e5580789e902820100674075609c8d2be880940" +
+ "6a17cf1a1160ab1f8684895862e023fa0f60ef30da38e1d1914cca04bd3ee74ec2e0ade47a70705108fc7c0734bbbff" +
+ "1eed1b9cd74f00624d0d2df954bc032bd9b1ec6774f6d736f70d1c26ef2407bffee65130f6f59f99b57ba3013af40d2" +
+ "12926565fe8c734835276e61a6c228bddb6f3138acd1f94c3bbcbbe9623cb5a9931c3ba0aa60a9a2d5137cfd9f3aa59" +
+ "3aa63c8b5b8162f07ab8df1391f092827bffe400e3bb73d8a9f8e88495357f3482b2c9a7153bc01c9b88dca4e7b6975" +
+ "db73e2aa213daa7462cfa4c63afc67d30bcd0a1d2657da323dc0b06e45d09240cab3e0ac1436922a0ede8a79ca0519d" +
+ "375a7621d23269690282010100bbc1299716d2bf2b94f0d260494ada65da6596adfb3d8af24fa11d71c36175eccf4c5" +
+ "e065cce88c16f474afea546907aa88dc3243aa2a9976ac99fe96bc82a8269b738534d9558ce432ea87724829bb26a66" +
+ "1a56a99dc4e6cf727dd17762cc40ca759934e24e9747f49e14832bb2ade97960adb4dd86f2eaa5d719f10d3d6d00742" +
+ "9b33d98638671a9c40507f9775f4da41ff86a465c68b9ccbb371458086c3b9755c8064bb378f55ac94dc73a72b96869" +
+ "cd969e1f69b36e7af091a024d8e2a4faf3af999811904937f171c58fd028fd272786cf1a286180f074fee1fdd6b8b5a" +
+ "9a8c42e0f3b95ef4474fbace54dbc887865467b0524e64dfda3be7b117e34e1028201001a7e846231c34400c1f704e7" +
+ "fec6e46c87d61f269b71942bc9cc72005ba30eea3db5d0e5b0b754f7a00b96c883399982b5b3a9916765c5ed9129e44" +
+ "a791ce6892f85758c637bc040da132b8f0cd0ac36ba3aae9334414a77f0b50c0aa03643bfd59b9a621342a4807e46fc" +
+ "52a5a12fd3ff6762e181c40c2baf3653043c836b14700463af5d68a2a2897897edb5f217d655d5bcd24e7910062f40e" +
+ "00f19e2f94b45efbbf60cbf734830756baf72dcfca8d2858ca5df63336999474945f3744a96e4ce23f9067bbca849ef" +
+ "1048cba3a4aad73ed73b0fcd8c2e9f6d06aa768548d7107aa58d9d296f853543f6569e4dd33270540d983460773794f" +
+ "e9196fc5a54cd";
+
+ String expectedCSR = "MIIE4jCCAsoCAQAwgZwxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW" +
+ "5nMQ8wDQYDVQQKDAZKRC5jb20xHjAcBgNVBAsMFUJsb2NrY2hhaW4gRGVwYXJ0bWVudDEWMBQGA1UEAwwNbGVkZ2VyLmpkL" +
+ "mNvbTEgMB4GCSqGSIb3DQEJARYRemhhbmdsaW4zM0BqZC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0" +
+ "Dt7oO2CeqgAfJJb5XS8zAlEzBuyajn/ODS4UH85+41enRlMUw/X0sIy2yVgDw2jdv+26SDy1xFkUA3zu5Xg/yXGhLvmw5BW" +
+ "Nw3m1lJnu5ikySpvrNQxMEOUINzRb4Si5H0OgM4F1i77+QcRXEuS1/dW954AWcoNwayTjfddT22W0xrPknNi+glZl2aKaJL" +
+ "d+dkc98CIyeHNVWqM7ov/Mdmy+/txG7IaNEPgXgiVA6vV1TAdN1tQoNVziSgWKTJzkHkiq1ZLnlVz5PXedA9Os8lricTRqn" +
+ "kJV5O2QKuAWAysE777pj0PNdnZT4ImzdUDlN67enbwE+PGoWLJ2S57trIC2qNpf8CqrS+lOBxxwcY/ecifN7+wxYAocVbrB" +
+ "b03p3qireCTB7Hg7gY3+AF8ECj9ocrHHpsMaZsGwbrjYcqI9G0/a357tWPk7KivBRWOKeagZBNObIhKM7RiiVWwhiI7R7Ir" +
+ "Vm9Z2Tx6hbrfzV0xTFm8IJ7UHLSMBe9clretj7rIppOeNTXQm6TZ1OQK7UePNkGMDFPSrQScqnjbLZosrqcLrwC6e8MN3yI" +
+ "SC6Dny9NXI78++EoDlLGvfgKpIeuA/+d2f2YH3gXK8EUHOYDGwuJFWWNgwxpZmJQfTz03bqH2qv5fBXL71ixXoTxb4eTKMf" +
+ "GUHbZT8a0UUVJgxhQIDAQABoAAwDQYJKoZIhvcNAQEFBQADggIBAKowJTG9mZLbDHRndmDUd7PWrq1AiCdk6DStV39B/REF" +
+ "OrMYcW9X4Ak69fhXQDtD4gu2lKtumDY0oJ8xleM2FHUSzdooTWb7P/QtCIBy27sH6nvlefRWi7ngSTNJlDmwgr0l07UzZU1" +
+ "Yl/ZULn0XlNAFal+qt+4ZNdiulNwkL6IofXV/8vqOeQw5iICDBYHItyY/mqD8IIaClVd8yNpEuE/W9GdJIDNXQjpug+BxL/" +
+ "FbjAs6P3ZzJboedJE5urbru2jjb7atl3w/eDo4r6+XNSD8d1PgVmVhzN2WpUWsZNeH2jd9AA6436GjsBssgSRKEc3FTJ+lO" +
+ "0Jw2d8GewXXkIv8CT4L3BFwqZhGQt27wlb87+W4dIC05JIaJx52869dvu1ky1CL73GROXeS8rVYJsPwVmK2xy3QTaeHGEQh" +
+ "kiVNeV1cc3mll2z7fgbkjPD8zDNBWUdzSXQMzecY1CBD02iz6LaHfvkI7gXXoiIf1cJrnLtYhv3lG45jKr0E45/Wn7oXmYk" +
+ "RM4/zHO4KnY7Pp3b3QTgkRJaPnZiG7aiCrdnTIopDSGpTWSPWDLjgwgaCPPz5Pd2Fk+SAE98o7Cu8O0vxMASpd4liaedASE" +
+ "n6hnrvcTkFLLG2ecZzJZ0aEqPi4es0FqlqVZrLPH2UYpECgfhkGQQKAx4eRTAZFhz1GSkd";
+
+ String expectedUserCert = "MIIFRjCCBC6gAwIBAgIFIChmYWIwDQYJKoZIhvcNAQEFBQAwWTELMAkGA1UEBhMCQ04xMDAuBgNVB" +
+ "AoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEYMBYGA1UEAxMPQ0ZDQSBURVNUIE9DQTExMB4X" +
+ "DTE5MDgyODA4MDAxMVoXDTIxMDgyODA4MDAxMVoweDELMAkGA1UEBhMCQ04xGDAWBgNVBAoTD0NGQ0EgVEVTVCBPQ0ExMTE" +
+ "RMA8GA1UECxMITG9jYWwgUkExFTATBgNVBAsTDEluZGl2aWR1YWwtMTElMCMGA1UEAxQcMDUxQHpoYW5nbGluIUBaMTg2MT" +
+ "IyMjkyOTVANzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALQO3ug7YJ6qAB8klvldLzMCUTMG7JqOf84NLhQfz" +
+ "n7jV6dGUxTD9fSwjLbJWAPDaN2/7bpIPLXEWRQDfO7leD/JcaEu+bDkFY3DebWUme7mKTJKm+s1DEwQ5Qg3NFvhKLkfQ6Az" +
+ "gXWLvv5BxFcS5LX91b3ngBZyg3BrJON911PbZbTGs+Sc2L6CVmXZopokt352Rz3wIjJ4c1Vaozui/8x2bL7+3Ebsho0Q+Be" +
+ "CJUDq9XVMB03W1Cg1XOJKBYpMnOQeSKrVkueVXPk9d50D06zyWuJxNGqeQlXk7ZAq4BYDKwTvvumPQ812dlPgibN1QOU3rt" +
+ "6dvAT48ahYsnZLnu2sgLao2l/wKqtL6U4HHHBxj95yJ83v7DFgChxVusFvTeneqKt4JMHseDuBjf4AXwQKP2hyscemwxpmw" +
+ "bBuuNhyoj0bT9rfnu1Y+TsqK8FFY4p5qBkE05siEoztGKJVbCGIjtHsitWb1nZPHqFut/NXTFMWbwgntQctIwF71yWt62Pu" +
+ "simk541NdCbpNnU5ArtR482QYwMU9KtBJyqeNstmiyupwuvALp7ww3fIhILoOfL01cjvz74SgOUsa9+Aqkh64D/53Z/Zgfe" +
+ "BcrwRQc5gMbC4kVZY2DDGlmYlB9PPTduofaq/l8FcvvWLFehPFvh5Mox8ZQdtlPxrRRRUmDGFAgMBAAGjgfUwgfIwHwYDVR" +
+ "0jBBgwFoAU/Au8RJoOMaGDqYGHJx4FQsa/VvgwSAYDVR0gBEEwPzA9BghggRyG7yoBAjAxMC8GCCsGAQUFBwIBFiNodHRwO" +
+ "i8vd3d3LmNmY2EuY29tLmNuL3VzL3VzLTE1Lmh0bTA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vMjEwLjc0LjQyLjMvT0NB" +
+ "MTEvUlNBL2NybDI2NjU1LmNybDALBgNVHQ8EBAMCA+gwHQYDVR0OBBYEFBl1Gmb89bqbEKyFcTU3eOY/5NmKMB0GA1UdJQQ" +
+ "WMBQGCCsGAQUFBwMCBggrBgEFBQcDBDANBgkqhkiG9w0BAQUFAAOCAQEADEU//9rnWN1s3/ariMHIUmgzRUdz3fWYiDRzGC" +
+ "mcnnETlXDstGmoYmwCM+QwHw6cyKXkwkg9zV7c7CgM471ZuF00gq115d432Ps3RXGCpfQ2fn3gs+91ky/YqJOOyBb8KL0IP" +
+ "r/Zh56/y3XX0gORn4GLqaj+oVZrFcmKrPtVhySlXNiD5BRMq39mUbuLBweGsgNVQ9VxiWc8ZBGjlJ6OVsngbvWrtl3zgkKb" +
+ "X9lhr8Bxq3G+jOV8jvr1Dkn4a65g2TWcFquxmPvRc5UwN29CimbC7RViCL3Jp+zrGasqbjycuqu5eSXb6gG4/aV0/K9yn5k" +
+ "YlZMIBlbsXSEi5J26pg==";
+
+ String issuerCert =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDzzCCAregAwIBAgIKUalCR1Mt5ZSK8jANBgkqhkiG9w0BAQUFADBZMQswCQYD\n" +
+ "VQQGEwJDTjEwMC4GA1UEChMnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24g\n" +
+ "QXV0aG9yaXR5MRgwFgYDVQQDEw9DRkNBIFRFU1QgQ1MgQ0EwHhcNMTIwODI5MDU1\n" +
+ "NDM2WhcNMzIwODI0MDU1NDM2WjBZMQswCQYDVQQGEwJDTjEwMC4GA1UEChMnQ2hp\n" +
+ "bmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRgwFgYDVQQDEw9D\n" +
+ "RkNBIFRFU1QgT0NBMTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8\n" +
+ "jn0n8Fp6hcRdACdn1+Y6GAkC6KGgNdKyHPrmsdmhCjnd/i4qUFwnG8cp3D4lFw1G\n" +
+ "jmjSO5yVYbik/NbS6lbNpRgTK3fDfMFvLJpRIC+IFhG9SdAC2hwjsH9qTpL9cK2M\n" +
+ "bSdrC6pBdDgnbzaM9AGBF4Y6vXhj5nah4ZMsBvDp19LzXjlGuTPLuAgv9ZlWknsd\n" +
+ "RN70PIAmvomd10uqX4GIJ4Jq/FdKXOLZ2DNK/NhRyN6Yq71L3ham6tutXeZow5t5\n" +
+ "0254AnUlo1u6SeH9F8itER653o/oMLFwp+63qXAcqrHUlOQPX+JI8fkumSqZ4F2F\n" +
+ "t/HfVMnqdtFNCnh5+eIBAgMBAAGjgZgwgZUwHwYDVR0jBBgwFoAUdN7FjQp9EBqq\n" +
+ "aYNbTSHOhpvMcTgwDAYDVR0TBAUwAwEB/zA4BgNVHR8EMTAvMC2gK6AphidodHRw\n" +
+ "Oi8vMjEwLjc0LjQyLjMvdGVzdHJjYS9SU0EvY3JsMS5jcmwwCwYDVR0PBAQDAgEG\n" +
+ "MB0GA1UdDgQWBBT8C7xEmg4xoYOpgYcnHgVCxr9W+DANBgkqhkiG9w0BAQUFAAOC\n" +
+ "AQEAb7W0K9fZPA+JPw6lRiMDaUJ0oh052yEXreMBfoPulxkBj439qombDiFggRLc\n" +
+ "3g8wIEKzMOzOKXTWtnzYwN3y/JQSuJb/M1QqOEEM2PZwCxI4AkBuH6jg03RjlkHg\n" +
+ "/kTtuIFp9ItBCC2/KkKlp0ENfn4XgVg2KtAjZ7lpyVU0LPnhEqqUVY/xthjlCSa7\n" +
+ "/XHNStRxsfCTIBUWJ8n2FZyQhfV/UkMNHDBIiJR0v6C4Ai0/290WvbPEIAq+03Si\n" +
+ "fsHzBeA0C8lP5VzfAr6wWePaZMCpStpLaoXNcAqReKxQllElOqAhRxC5VKH+rnIQ\n" +
+ "OMRZvB7FRyE9IfwKApngcZbA5g==\n" +
+ "-----END CERTIFICATE-----";
+
+ byte[] rawPublicKeyBytes = Hex.decode(publicKeyStr);
+ byte[] rawPrivateKeyBytes = Hex.decode(privateKeyStr);
+
+ CSRBuilder builder = new CSRBuilder();
+ builder.init("SHA1withRSA", rawPublicKeyBytes, rawPrivateKeyBytes);
+
+ String csr = builder.buildRequest(countryName,stateName,cityName,
+ organizationName,departmentName,domainName,
+ emailName);
+
+ assertEquals(expectedCSR,csr);
+
+ CertParser parser = new CertParser();
+ parser.parse(expectedUserCert,issuerCert);
+
+ PublicKey rawPublicKeyInCert = parser.getPubKey();
+ // check that the public key in inputs and the public key in certificate are consistent
+ assertArrayEquals(rawPublicKeyBytes, rawPublicKeyInCert.getEncoded());
+
+ String algoName = parser.getSigAlgName();
+ int keyLength = parser.getKeyLength();
+ String length = String.valueOf(keyLength);
+ String algo = (algoName.contains("RSA")? (algoName + length).toUpperCase(): algoName.toUpperCase());
+
+ CryptoAlgorithm algorithm = Crypto.getAlgorithm(algo);
+ assertNotNull(algorithm);
+ SignatureFunction signatureFunction = Crypto.getSignatureFunction(algorithm);
+
+ PubKey pubKey = new PubKey(algorithm, rawPublicKeyBytes);
+ PrivKey privKey = new PrivKey(algorithm, rawPrivateKeyBytes);
+
+ // signTest
+ byte[] data = new byte[1024];
+ Random random = new Random();
+ random.nextBytes(data);
+
+ SignatureDigest signature = signatureFunction.sign(privKey, data);
+ assertTrue(signatureFunction.verify(signature, pubKey, data));
+ }
}
diff --git a/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SM3WITHSM2SignatureFunctionTest.java b/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SM3WITHSM2SignatureFunctionTest.java
index 0b8063a9..54969745 100644
--- a/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SM3WITHSM2SignatureFunctionTest.java
+++ b/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/service/pki/SM3WITHSM2SignatureFunctionTest.java
@@ -1,9 +1,13 @@
package com.jd.blockchain.crypto.service.pki;
import com.jd.blockchain.crypto.*;
+import com.jd.blockchain.crypto.utils.CSRBuilder;
+import com.jd.blockchain.crypto.utils.CertParser;
import com.jd.blockchain.utils.io.BytesUtils;
+import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;
+import java.security.PublicKey;
import java.util.Random;
import static com.jd.blockchain.crypto.CryptoAlgorithm.ASYMMETRIC_KEY;
@@ -270,4 +274,80 @@ public class SM3WITHSM2SignatureFunctionTest {
resolvedSignatureDigest.getAlgorithm());
assertArrayEquals(signatureDigestBytes, resolvedSignatureDigest.toBytes());
}
+
+
+ @Test
+ public void testWithCSRAndCert() {
+
+ String publicKeyStr = "3059301306072a8648ce3d020106082a811ccf5501822d03420004aa6586478be879504fdd02892f" +
+ "b4cf2bdb3d96a316f41ff7bcadfabef4ea836678984f9ba6931a609391426ca7164592f5fccd062be56fc32b3eec3a5" +
+ "0400971";
+
+ String privateKeyStr = "308193020100301306072a8648ce3d020106082a811ccf5501822d0479307702010104207abd2953" +
+ "84f193abe4f3715c26bc15225061f007131755d94ca12c7e0ce0b3a0a00a06082a811ccf5501822da14403420004aa6" +
+ "586478be879504fdd02892fb4cf2bdb3d96a316f41ff7bcadfabef4ea836678984f9ba6931a609391426ca7164592f5" +
+ "fccd062be56fc32b3eec3a50400971";
+
+ String expectedUserCert = "MIICwjCCAmWgAwIBAgIFIChnMlIwDAYIKoEcz1UBg3UFADBdMQswCQYDVQQGEwJDTjEwMC4GA1UEC" +
+ "gwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRwwGgYDVQQDDBNDRkNBIFRFU1QgU00yIE9DQTEx" +
+ "MB4XDTE5MDgyODA4MTUzNloXDTIxMDgyODA4MTUzNloweDELMAkGA1UEBhMCQ04xGDAWBgNVBAoMD0NGQ0EgVEVTVCBPQ0E" +
+ "xMTERMA8GA1UECwwITG9jYWwgUkExFTATBgNVBAsMDEluZGl2aWR1YWwtMTElMCMGA1UEAwwcMDUxQHpoYW5nbGluIUBaMT" +
+ "g2MTIyMjkyOTVAODBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABKplhkeL6HlQT90CiS+0zyvbPZajFvQf97yt+r706oNme" +
+ "JhPm6aTGmCTkUJspxZFkvX8zQYr5W/DKz7sOlBACXGjgfQwgfEwHwYDVR0jBBgwFoAUvqZ+TT18j6BV5sEvCS4sIEOzQn8w" +
+ "SAYDVR0gBEEwPzA9BghggRyG7yoBAjAxMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmNmY2EuY29tLmNuL3VzL3VzLTE1Lmh" +
+ "0bTA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vMjEwLjc0LjQyLjMvT0NBMTEvU00yL2NybDI0OTkuY3JsMAsGA1UdDwQEAw" +
+ "ID6DAdBgNVHQ4EFgQU07tMtbs5PWkwN33OQVH116xd1kowHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMAwGCCqBH" +
+ "M9VAYN1BQADSQAwRgIhAPuuInppVhugw6EIG4wgkouxX/MX2dTLe478wx7LFXSQAiEAneiz51vrurICNCfeecaBHgzaj7+3" +
+ "kmrdIZJBoxYGbso=";
+
+ String issuerCert =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIICTzCCAfOgAwIBAgIKJFSZ4SRVDndYUTAMBggqgRzPVQGDdQUAMF0xCzAJBgNV\n" +
+ "BAYTAkNOMTAwLgYDVQQKDCdDaGluYSBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBB\n" +
+ "dXRob3JpdHkxHDAaBgNVBAMME0NGQ0EgVEVTVCBDUyBTTTIgQ0EwHhcNMTIwODI5\n" +
+ "MDU0ODQ3WhcNMzIwODI0MDU0ODQ3WjBdMQswCQYDVQQGEwJDTjEwMC4GA1UECgwn\n" +
+ "Q2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRwwGgYDVQQD\n" +
+ "DBNDRkNBIFRFU1QgU00yIE9DQTExMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE\n" +
+ "L1mx4wriQUojGsIkNL14kslv9nwiqsiVpELOZauzghrbccNlPYKNYKZOCvXwIIqU\n" +
+ "9QY02d4weqKqo/JMcNsKEaOBmDCBlTAfBgNVHSMEGDAWgBS12JBvXPDYM9JjvX6y\n" +
+ "w43GTxJ6YTAMBgNVHRMEBTADAQH/MDgGA1UdHwQxMC8wLaAroCmGJ2h0dHA6Ly8y\n" +
+ "MTAuNzQuNDIuMy90ZXN0cmNhL1NNMi9jcmwxLmNybDALBgNVHQ8EBAMCAQYwHQYD\n" +
+ "VR0OBBYEFL6mfk09fI+gVebBLwkuLCBDs0J/MAwGCCqBHM9VAYN1BQADSAAwRQIh\n" +
+ "AKuk7s3eYCZDck5NWU0eNQmLhBN/1zmKs517qFrDrkJWAiAP4cVfLtdza/OkwU9P\n" +
+ "PrIDl+E4aL3FypntFXHG3T+Keg==\n" +
+ "-----END CERTIFICATE-----";
+
+ byte[] rawPublicKeyBytes = Hex.decode(publicKeyStr);
+ byte[] rawPrivateKeyBytes = Hex.decode(privateKeyStr);
+
+ CSRBuilder builder = new CSRBuilder();
+ builder.init("SM3withSM2", rawPublicKeyBytes, rawPrivateKeyBytes);
+
+ CertParser parser = new CertParser();
+ parser.parse(expectedUserCert,issuerCert);
+
+ PublicKey rawPublicKeyInCert = parser.getPubKey();
+ // check that the public key in inputs and the public key in certificate are consistent
+ assertArrayEquals(rawPublicKeyBytes, rawPublicKeyInCert.getEncoded());
+
+ String algoName = parser.getSigAlgName();
+ int keyLength = parser.getKeyLength();
+ String length = String.valueOf(keyLength);
+ String algo = (algoName.contains("RSA")? (algoName + length).toUpperCase(): algoName.toUpperCase());
+
+ CryptoAlgorithm algorithm = Crypto.getAlgorithm(algo);
+ assertNotNull(algorithm);
+ SignatureFunction signatureFunction = Crypto.getSignatureFunction(algorithm);
+
+ PubKey pubKey = new PubKey(algorithm, rawPublicKeyBytes);
+ PrivKey privKey = new PrivKey(algorithm, rawPrivateKeyBytes);
+
+ // signTest
+ byte[] data = new byte[1024];
+ Random random = new Random();
+ random.nextBytes(data);
+
+ SignatureDigest signature = signatureFunction.sign(privKey, data);
+ assertTrue(signatureFunction.verify(signature, pubKey, data));
+ }
}
diff --git a/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/utils/CertParserTest.java b/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/utils/CertParserTest.java
index 24cdc414..263d3e88 100644
--- a/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/utils/CertParserTest.java
+++ b/source/crypto/crypto-pki/src/test/java/com/jd/blockchain/crypto/utils/CertParserTest.java
@@ -43,7 +43,8 @@ public class CertParserTest {
String userCert = "MIIEQDCCAyigAwIBAgIFICdVYzEwDQYJKoZIhvcNAQEFBQAwWTELMAkGA1UEBhMCQ04xMDAuBgNVBAoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEYMBYGA1UEAxMPQ0ZDQSBURVNUIE9DQTExMB4XDTE5MDUxMDExMjAyNFoXDTIxMDUxMDExMjAyNFowcjELMAkGA1UEBhMCQ04xGDAWBgNVBAoTD0NGQ0EgVEVTVCBPQ0ExMTERMA8GA1UECxMITG9jYWwgUkExFTATBgNVBAsTDEluZGl2aWR1YWwtMTEfMB0GA1UEAxQWMDUxQGFhYWFhQFpIMDkzNTgwMjhAMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJx3F2WD1dJPzK/nRHO7d1TJ1hTjzGTmv0PQ7ECsJAh3U3BtnGTpCB+b4+JMI4LO8nHkKIBQ3P9XnF+Bf1iXdWNAQ4aWCxa2nV7lCp4w0GliPu/EMgIfmsSDUtgqbM3cr8sR8r9m1xG3gt2TIQJ+jT7sAiguU/kyNzpjaccOUIgUFa8IDFq9UeB76MXtCuhlERRZQCl47e+9w7ZoxmE7e6IZORxPp7rQWVBHlR9ntWjJfNDTm3gMP5ehP+yIZnKx1LudxkBLQxpMmspzOyH1zqx5nkKe49AfWWpDxxRvYkriyYC3aE81qLsU/bhLwNEKOju7BGDF/mhJLZUedojM0gMCAwEAAaOB9TCB8jAfBgNVHSMEGDAWgBT8C7xEmg4xoYOpgYcnHgVCxr9W+DBIBgNVHSAEQTA/MD0GCGCBHIbvKgECMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuY2ZjYS5jb20uY24vdXMvdXMtMTUuaHRtMDoGA1UdHwQzMDEwL6AtoCuGKWh0dHA6Ly8yMTAuNzQuNDIuMy9PQ0ExMS9SU0EvY3JsMjU2OTMuY3JsMAsGA1UdDwQEAwID6DAdBgNVHQ4EFgQU5oKGaQs7Jt5Gfbt1XhFTWAySEKswHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMA0GCSqGSIb3DQEBBQUAA4IBAQAlmPRaImZV51iKjtpMKuyLMw7dX8L0lY3tl+pVZZSxHuwsN4GCCtV0Ej50up+/6EbfL4NUTiuHVAjCroKKvb+94CrdEwdnQGM5IbGSjT78nQpeASXbIWuUwA+ImjvZOzvq/0b56AzonNzBxOMGko/bj5smM6X8jrgJ0NQppo2KNSVNC4JbuoNWI4FM94SE4DUi9H7EYl4JdOtDaDtCsq49o/A1CZyYrmoOPCgxpQQXmuB3lGq/jyoOlW2aW8uee/hYG1JJcSHLBjF0WBwdxssgbBotA5f1PebiIMSbFgjk57bd4M80hhU/rI4Hkn9pcp5R7NsX95TtyDIg90LboBnW";
parser.parse(userCert, issuerCert);
- assertEquals("SHA1WITHRSA",parser.getSigAlgName());
+ assertEquals("SHA1WITHRSA", parser.getSigAlgName());
+ assertEquals(2048, parser.getKeyLength());
}
@Test
@@ -77,7 +78,8 @@ public class CertParserTest {
String userCert = "MIIFRjCCBC6gAwIBAgIFICdWiDMwDQYJKoZIhvcNAQEFBQAwWTELMAkGA1UEBhMCQ04xMDAuBgNVBAoTJ0NoaW5hIEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEYMBYGA1UEAxMPQ0ZDQSBURVNUIE9DQTExMB4XDTE5MDUxNjA3MDcyMloXDTIxMDUxNjA3MDcyMloweDELMAkGA1UEBhMCQ04xGDAWBgNVBAoTD0NGQ0EgVEVTVCBPQ0ExMTERMA8GA1UECxMITG9jYWwgUkExFTATBgNVBAsTDEluZGl2aWR1YWwtMTElMCMGA1UEAxQcMDUxQHpoYW5nbGluIUBaMTg2MTIyMjkyOTVAMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL0rTOxd8rsjPtFJ0aGVh9bZPy5Xo0SADaP7BbJsG4+ykLQMZHg9hTf/6fv1OsD2HEKFoMpIkW2gwCJW2nvicHcVql/shCoktc6ZBW6Dr/DxOgbO9tpoGxZ50xdI4Q0NsrxqtbCldW4ozPHdjgRJ83i1KSFh7evNrVByN/mB+jchrVGLWyJ1uIRgUUgpRZmZPoOHaizVJqrqWJGGk6xbDLR2gUQ1hTzetQaz1OeKtelHDk9FY08XSmNGssSMpuSjrxn78S888VW5WIxyo4cwrXSXFk3J7LNTy70Oga16HZjJD/vLTM6a4riPa8+uivPinKxK38/++nlBPNwhx6n46uYkd9Zvw+SJiJgpnuPJLtMZpKpJx7V1BDVEydKPUthilTdsmJtkBFSlFw0G1aKfuciBGzzJ3SKngJF/JqJAWIonVAFBGb6Gokp1Sw+T4KqXrdbjxYxiyyjZ++8O1vydgFAkx/NjsuwJnpKETiRKFJmY7YawcUvC4ixF7XQc0luFWRDYcbxOppir+ieMqhGXyaFhLUuB4WXv+rFxfa3NmkBW8q5TPzt/PwWcXpITsYTZYla/E/grB+OeZLYgjigT5YlgytPHG6Gt1ySCCd8WXFWpkBbQfXzqcvtU27RCcAUgfXk5NLb7NZCQg7heGjgzOdYJCPsa1d3m7l04+VIKGCZdAgMBAAGjgfUwgfIwHwYDVR0jBBgwFoAU/Au8RJoOMaGDqYGHJx4FQsa/VvgwSAYDVR0gBEEwPzA9BghggRyG7yoBAjAxMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmNmY2EuY29tLmNuL3VzL3VzLTE1Lmh0bTA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vMjEwLjc0LjQyLjMvT0NBMTEvUlNBL2NybDI1NzE3LmNybDALBgNVHQ8EBAMCA+gwHQYDVR0OBBYEFMjh6AzDCuNkD+pqQfiS6bqPGpI4MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDANBgkqhkiG9w0BAQUFAAOCAQEApZaLXS6/6FudPA3l2xom5U7nJUOpQ1E6DO/ic9dFGtLE0WgyAqB3JVPvXEntppX55x/dAV7rvvz9NaEdiAe8DAj7qyoPDvC8ZWQK8U4n9l8N78QwALOURxzQNs+CBatJQzbu2w1RKVwkfE6xEIVnu+wjiAtymfwdLHMagHxDIC/eOPbTnbbtITJk2ukFfoc0WJ6Awg5lW+x7nGokEn/XAjKyRHCpkRUFGQm4ww41zlrqCqQqnVGVABJtjbdtFf7nh33QHl0fkj19nfMox9eGuntPyM0bNA0XqPMA+FWSCqeDT6uLbyaOKWxlhv53U/NCJl76U3tssMEWsm9amEDDQg==";
parser.parse(userCert, issuerCert);
- assertEquals("SHA1WITHRSA",parser.getSigAlgName());
+ assertEquals("SHA1WITHRSA", parser.getSigAlgName());
+ assertEquals(4096, parser.getKeyLength());
}
@Test
@@ -103,7 +105,8 @@ public class CertParserTest {
String userCert = "MIICwDCCAmWgAwIBAgIFICdWkWgwDAYIKoEcz1UBg3UFADBdMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRwwGgYDVQQDDBNDRkNBIFRFU1QgU00yIE9DQTExMB4XDTE5MDUxNjA4MTA1MVoXDTIxMDUxNjA4MTA1MVoweDELMAkGA1UEBhMCQ04xGDAWBgNVBAoMD0NGQ0EgVEVTVCBPQ0ExMTERMA8GA1UECwwITG9jYWwgUkExFTATBgNVBAsMDEluZGl2aWR1YWwtMTElMCMGA1UEAwwcMDUxQHpoYW5nbGluIUBaMTg2MTIyMjkyOTVAMzBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABPvNXpdZ4/4g+wx5qKS94CPkMqpEDhlnXYYW7ZzsbNI4d28sVBz5Ji6dTT1Zx627Kvw4tdUaUt7BVMvZsu3BFlyjgfQwgfEwHwYDVR0jBBgwFoAUvqZ+TT18j6BV5sEvCS4sIEOzQn8wSAYDVR0gBEEwPzA9BghggRyG7yoBAjAxMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmNmY2EuY29tLmNuL3VzL3VzLTE1Lmh0bTA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vMjEwLjc0LjQyLjMvT0NBMTEvU00yL2NybDIxMDkuY3JsMAsGA1UdDwQEAwID6DAdBgNVHQ4EFgQUxR5C/VjASus5zrAAFS4ulMpRjKgwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMEMAwGCCqBHM9VAYN1BQADRwAwRAIgVBzVi/fgkknr+2BH2wXeGMXC+Pa6p7rbldUsYMOYoyUCIAmQ4KEk2U1xJZSBpOPy5jN9kmRb+0YH6x04O/2tqCgq";
parser.parse(userCert, issuerCert);
- assertEquals("SM3WITHSM2",parser.getSigAlgName());
+ assertEquals("SM3WITHSM2", parser.getSigAlgName());
+ assertEquals(256, parser.getKeyLength());
}
@Test
@@ -141,6 +144,7 @@ public class CertParserTest {
"PrIDl+E4aL3FypntFXHG3T+Keg==\n" +
"-----END CERTIFICATE-----";
parser.parse(issuerCert, CACert);
- assertEquals("SM3WITHSM2",parser.getSigAlgName());
+ assertEquals("SM3WITHSM2", parser.getSigAlgName());
+ assertEquals(256, parser.getKeyLength());
}
}
diff --git a/source/crypto/crypto-sm/src/main/java/com/jd/blockchain/crypto/utils/sm/SM2Utils.java b/source/crypto/crypto-sm/src/main/java/com/jd/blockchain/crypto/utils/sm/SM2Utils.java
index 94c0cf32..b3a65b18 100644
--- a/source/crypto/crypto-sm/src/main/java/com/jd/blockchain/crypto/utils/sm/SM2Utils.java
+++ b/source/crypto/crypto-sm/src/main/java/com/jd/blockchain/crypto/utils/sm/SM2Utils.java
@@ -13,6 +13,8 @@ import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.SM2Signer;
+import org.bouncycastle.jce.ECNamedCurveTable;
+import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.*;
import java.io.IOException;
@@ -29,19 +31,10 @@ public class SM2Utils {
// The length of sm3 output is 32 bytes
private static final int SM3DIGEST_LENGTH = 32;
- private static final BigInteger SM2_P = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16);
- private static final BigInteger SM2_A = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16);
- private static final BigInteger SM2_B = new BigInteger("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16);
- private static final BigInteger SM2_N = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16);
- private static final BigInteger SM2_H = ECConstants.ONE;
- private static final BigInteger SM2_GX = new BigInteger("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
- private static final BigInteger SM2_GY = new BigInteger("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
-
- // To get the curve from the equation y^2=x^3+ax+b according the coefficient a and b,
- // with the big prime p, the order n, the cofactor h, and obtain the generator g and the domain's parameters
- private static final ECCurve CURVE = new ECCurve.Fp(SM2_P, SM2_A, SM2_B, SM2_N, SM2_H);
- private static final ECPoint G = CURVE.createPoint(SM2_GX, SM2_GY);
- private static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G, SM2_N);
+ private static final ECNamedCurveParameterSpec PARAMS = ECNamedCurveTable.getParameterSpec("sm2p256v1");
+ private static final ECCurve CURVE = PARAMS.getCurve();
+ private static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(
+ CURVE, PARAMS.getG(), PARAMS.getN(), PARAMS.getH());
//-----------------Key Generation Algorithm-----------------
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java
index 792ca704..93286676 100644
--- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java
@@ -1,12 +1,11 @@
package com.jd.blockchain.ledger.core;
import com.jd.blockchain.binaryproto.BinaryProtocol;
-import com.jd.blockchain.binaryproto.PrimitiveType;
import com.jd.blockchain.crypto.HashDigest;
import com.jd.blockchain.crypto.PubKey;
import com.jd.blockchain.ledger.AccountHeader;
-import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.BytesData;
+import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.KVDataEntry;
import com.jd.blockchain.ledger.KVDataObject;
import com.jd.blockchain.utils.Bytes;
@@ -43,16 +42,85 @@ public class DataAccount implements AccountHeader, MerkleProvable {
}
+ /**
+ * Create or update the value associated the specified key if the version
+ * checking is passed.
+ *
+ * The value of the key will be updated only if it's latest version equals the
+ * specified version argument.
+ * If the key doesn't exist, the version checking will be ignored, and key will
+ * be created with a new sequence number as id.
+ * It also could specify the version argument to -1 to ignore the version
+ * checking.
+ *
+ * If updating is performed, the version of the key increase by 1.
+ * If creating is performed, the version of the key initialize by 0.
+ *
+ * @param key The key of data;
+ * @param value The value of data;
+ * @param version The expected version of the key.
+ * @return The new version of the key.
+ * If the key is new created success, then return 0;
+ * If the key is updated success, then return the new version;
+ * If this operation fail by version checking or other reason, then
+ * return -1;
+ */
public long setBytes(Bytes key, BytesValue value, long version) {
return baseAccount.setBytes(key, value, version);
}
+ /**
+ * Create or update the value associated the specified key if the version
+ * checking is passed.
+ *
+ * The value of the key will be updated only if it's latest version equals the
+ * specified version argument.
+ * If the key doesn't exist, the version checking will be ignored, and key will
+ * be created with a new sequence number as id.
+ * It also could specify the version argument to -1 to ignore the version
+ * checking.
+ *
+ * If updating is performed, the version of the key increase by 1.
+ * If creating is performed, the version of the key initialize by 0.
+ *
+ * @param key The key of data;
+ * @param value The value of data;
+ * @param version The expected version of the key.
+ * @return The new version of the key.
+ * If the key is new created success, then return 0;
+ * If the key is updated success, then return the new version;
+ * If this operation fail by version checking or other reason, then
+ * return -1;
+ */
public long setBytes(Bytes key, String value, long version) {
BytesValue bytesValue = BytesData.fromText(value);
return baseAccount.setBytes(key, bytesValue, version);
}
+ /**
+ * Create or update the value associated the specified key if the version
+ * checking is passed.
+ *
+ * The value of the key will be updated only if it's latest version equals the
+ * specified version argument.
+ * If the key doesn't exist, the version checking will be ignored, and key will
+ * be created with a new sequence number as id.
+ * It also could specify the version argument to -1 to ignore the version
+ * checking.
+ *
+ * If updating is performed, the version of the key increase by 1.
+ * If creating is performed, the version of the key initialize by 0.
+ *
+ * @param key The key of data;
+ * @param value The value of data;
+ * @param version The expected version of the key.
+ * @return The new version of the key.
+ * If the key is new created success, then return 0;
+ * If the key is updated success, then return the new version;
+ * If this operation fail by version checking or other reason, then
+ * return -1;
+ */
public long setBytes(Bytes key, byte[] value, long version) {
BytesValue bytesValue = BytesData.fromBytes(value);
return baseAccount.setBytes(key, bytesValue, version);
@@ -121,6 +189,29 @@ public class DataAccount implements AccountHeader, MerkleProvable {
public BytesValue getBytes(Bytes key, long version) {
return baseAccount.getBytes(key, version);
}
+
+ /**
+ * @param key
+ * @param version
+ * @return
+ */
+ public KVDataEntry getDataEntry(String key, long version) {
+ return getDataEntry(Bytes.fromString(key), version);
+ }
+
+ /**
+ * @param key
+ * @param version
+ * @return
+ */
+ public KVDataEntry getDataEntry(Bytes key, long version) {
+ BytesValue value = baseAccount.getBytes(key, version);
+ if (value == null) {
+ return new KVDataObject(key.toUTF8String(), -1, null);
+ }else {
+ return new KVDataObject(key.toUTF8String(), version, value);
+ }
+ }
/**
* return the specified index's KVDataEntry;
@@ -131,7 +222,7 @@ public class DataAccount implements AccountHeader, MerkleProvable {
*/
public KVDataEntry[] getDataEntries(int fromIndex, int count) {
- if (getDataEntriesTotalCount() == 0 || count == 0) {
+ if (count == 0 || getDataEntriesTotalCount() == 0) {
return null;
}
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataSet.java
index b8dd170b..59ebd13f 100644
--- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataSet.java
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataSet.java
@@ -168,7 +168,7 @@ public class MerkleDataSet implements Transactional, MerkleProvable {
*/
public String getKeyAtIndex(int fromIndex) {
MerkleDataNode dataNode = merkleTree.getData(fromIndex);
- return new String(dataNode.getKey().toBytes());
+ return dataNode.getKey().toUTF8String();
}
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerQueryService.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerQueryService.java
index 5696ded7..226be047 100644
--- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerQueryService.java
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerQueryService.java
@@ -20,6 +20,8 @@ import com.jd.blockchain.utils.QueryUtil;
public class LedgerQueryService implements BlockchainQueryService {
+ private static final KVDataEntry[] EMPTY_ENTRIES = new KVDataEntry[0];
+
private LedgerService ledgerService;
public LedgerQueryService(LedgerService ledgerService) {
@@ -254,7 +256,7 @@ public class LedgerQueryService implements BlockchainQueryService {
@Override
public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) {
if (keys == null || keys.length == 0) {
- return null;
+ return EMPTY_ENTRIES;
}
LedgerRepository ledger = ledgerService.getLedger(ledgerHash);
LedgerBlock block = ledger.getLatestBlock();
@@ -266,7 +268,7 @@ public class LedgerQueryService implements BlockchainQueryService {
for (int i = 0; i < entries.length; i++) {
final String currKey = keys[i];
- ver = dataAccount.getDataVersion(Bytes.fromString(currKey));
+ ver = dataAccount == null ? -1 : dataAccount.getDataVersion(Bytes.fromString(currKey));
if (ver < 0) {
entries[i] = new KVDataObject(currKey, -1, null);
diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountKVSetOperationHandle.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountKVSetOperationHandle.java
index 2745a377..75607b51 100644
--- a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountKVSetOperationHandle.java
+++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountKVSetOperationHandle.java
@@ -7,6 +7,7 @@ import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.DataAccountDoesNotExistException;
import com.jd.blockchain.ledger.DataAccountKVSetOperation;
import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry;
+import com.jd.blockchain.ledger.DataVersionConflictException;
import com.jd.blockchain.ledger.Operation;
import com.jd.blockchain.ledger.core.DataAccount;
import com.jd.blockchain.ledger.core.LedgerDataSet;
@@ -31,8 +32,12 @@ public class DataAccountKVSetOperationHandle implements OperationHandle {
throw new DataAccountDoesNotExistException("DataAccount doesn't exist!");
}
KVWriteEntry[] writeSet = kvWriteOp.getWriteSet();
+ long v = -1;
for (KVWriteEntry kvw : writeSet) {
- account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion());
+ v = account.setBytes(Bytes.fromString(kvw.getKey()), kvw.getValue(), kvw.getExpectedVersion());
+ if (v < 0) {
+ throw new DataVersionConflictException();
+ }
}
return null;
}
diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingHandle.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingHandle.java
index c0a31321..ef354223 100644
--- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingHandle.java
+++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingHandle.java
@@ -3,10 +3,8 @@ package test.com.jd.blockchain.ledger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import com.jd.blockchain.contract.ContractType;
import com.jd.blockchain.contract.engine.ContractCode;
-import com.jd.blockchain.contract.jvm.AbstractContractCode;
-import com.jd.blockchain.contract.jvm.ContractDefinition;
+import com.jd.blockchain.contract.jvm.InstantiatedContractCode;
import com.jd.blockchain.ledger.core.ContractAccount;
import com.jd.blockchain.ledger.core.impl.handles.AbtractContractEventHandle;
import com.jd.blockchain.utils.Bytes;
@@ -21,30 +19,10 @@ public class ContractInvokingHandle extends AbtractContractEventHandle {
}
public ContractCode setup(Bytes address, Class contractIntf, T instance) {
- ContractCodeInstance contract = new ContractCodeInstance(address, 0, contractIntf, instance);
+ InstantiatedContractCode contract = new InstantiatedContractCode(address, 0, contractIntf, instance);
contractInstances.put(address, contract);
return contract;
}
- private static class ContractCodeInstance extends AbstractContractCode {
-
- private T instance;
-
- public ContractCodeInstance(Bytes address, long version, Class delaredInterface, T instance) {
- super(address, version, resolveContractDefinition(delaredInterface, instance.getClass()));
- this.instance = instance;
- }
-
- private static ContractDefinition resolveContractDefinition(Class> declaredIntf, Class> implementedClass) {
- ContractType contractType = ContractType.resolve(declaredIntf);
- return new ContractDefinition(contractType, implementedClass);
- }
-
- @Override
- protected T getContractInstance() {
- return instance;
- }
-
- }
}
diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingTest.java
index 0523ec64..2753761e 100644
--- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingTest.java
+++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/ContractInvokingTest.java
@@ -1,27 +1,42 @@
package test.com.jd.blockchain.ledger;
+import static com.jd.blockchain.transaction.ContractReturnValue.decode;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Random;
+
+import com.jd.blockchain.ledger.*;
+import org.junit.Test;
+import org.mockito.Mockito;
+
import com.jd.blockchain.binaryproto.BinaryProtocol;
import com.jd.blockchain.binaryproto.DataContractRegistry;
import com.jd.blockchain.crypto.HashDigest;
-import com.jd.blockchain.ledger.*;
-import com.jd.blockchain.ledger.core.*;
+import com.jd.blockchain.ledger.core.LedgerDataSet;
+import com.jd.blockchain.ledger.core.LedgerEditor;
+import com.jd.blockchain.ledger.core.LedgerRepository;
+import com.jd.blockchain.ledger.core.LedgerService;
+import com.jd.blockchain.ledger.core.LedgerTransactionContext;
+import com.jd.blockchain.ledger.core.UserAccount;
import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration;
import com.jd.blockchain.ledger.core.impl.LedgerManager;
import com.jd.blockchain.ledger.core.impl.LedgerTransactionalEditor;
+import com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration;
import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor;
import com.jd.blockchain.service.TransactionBatchResultHandle;
import com.jd.blockchain.storage.service.utils.MemoryKVStorage;
+import com.jd.blockchain.transaction.BooleanValueHolder;
import com.jd.blockchain.transaction.TxBuilder;
import com.jd.blockchain.utils.Bytes;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-import java.util.Random;
-
-import static org.junit.Assert.*;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.*;
public class ContractInvokingTest {
static {
@@ -32,8 +47,10 @@ public class ContractInvokingTest {
DataContractRegistry.register(EndpointRequest.class);
DataContractRegistry.register(TransactionResponse.class);
DataContractRegistry.register(UserRegisterOperation.class);
+ DataContractRegistry.register(DataAccountRegisterOperation.class);
DataContractRegistry.register(ParticipantRegisterOperation.class);
DataContractRegistry.register(ParticipantStateUpdateOperation.class);
+
}
private static final String LEDGER_KEY_PREFIX = "LDG://";
@@ -47,7 +64,7 @@ public class ContractInvokingTest {
private MemoryKVStorage storage = new MemoryKVStorage();
@Test
- public void test() {
+ public void testNormal() {
// 初始化账本到指定的存储库;
HashDigest ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3);
@@ -71,7 +88,6 @@ public class ContractInvokingTest {
// 发布指定地址合约
deploy(ledgerRepo, ledgerManager, opReg, ledgerHash, contractKey);
-
// 创建新区块的交易处理器;
LedgerBlock preBlock = ledgerRepo.getLatestBlock();
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock);
@@ -124,9 +140,243 @@ public class ContractInvokingTest {
}
+// @Test
+ public void testReadNewWritting() {
+ // 初始化账本到指定的存储库;
+ HashDigest ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3);
+
+ // 重新加载账本;
+ LedgerManager ledgerManager = new LedgerManager();
+ LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage);
+
+ // 创建合约处理器;
+ ContractInvokingHandle contractInvokingHandle = new ContractInvokingHandle();
+
+ // 创建和加载合约实例;
+ BlockchainKeypair contractKey = BlockchainKeyGenerator.getInstance().generate();
+ Bytes contractAddress = contractKey.getAddress();
+ TxTestContractImpl contractInstance = new TxTestContractImpl();
+ contractInvokingHandle.setup(contractAddress, TxTestContract.class, contractInstance);
+
+ // 注册合约处理器;
+ DefaultOperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration();
+ opReg.insertAsTopPriority(contractInvokingHandle);
+
+ // 发布指定地址合约
+ deploy(ledgerRepo, ledgerManager, opReg, ledgerHash, contractKey);
+
+ // 创建新区块的交易处理器;
+ LedgerBlock preBlock = ledgerRepo.getLatestBlock();
+ LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock);
+
+ // 加载合约
+ LedgerEditor newBlockEditor = ledgerRepo.createNextBlock();
+ TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset,
+ opReg, ledgerManager);
+
+ String key = TxTestContractImpl.KEY;
+ String value = "VAL";
+
+ TxBuilder txBuilder = new TxBuilder(ledgerHash);
+ BlockchainKeypair kpDataAccount = BlockchainKeyGenerator.getInstance().generate();
+ contractInstance.setDataAddress(kpDataAccount.getAddress());
+
+ txBuilder.dataAccounts().register(kpDataAccount.getIdentity());
+ TransactionRequestBuilder txReqBuilder1 = txBuilder.prepareRequest();
+ txReqBuilder1.signAsEndpoint(parti0);
+ txReqBuilder1.signAsNode(parti0);
+ TransactionRequest txReq1 = txReqBuilder1.buildRequest();
+
+ // 构建基于接口调用合约的交易请求,用于测试合约调用;
+ txBuilder = new TxBuilder(ledgerHash);
+ TxTestContract contractProxy = txBuilder.contract(contractAddress, TxTestContract.class);
+ BooleanValueHolder readableHolder = decode(contractProxy.testReadable());
+
+ TransactionRequestBuilder txReqBuilder2 = txBuilder.prepareRequest();
+ txReqBuilder2.signAsEndpoint(parti0);
+ txReqBuilder2.signAsNode(parti0);
+ TransactionRequest txReq2 = txReqBuilder2.buildRequest();
+
+ TransactionResponse resp1 = txbatchProcessor.schedule(txReq1);
+ TransactionResponse resp2 = txbatchProcessor.schedule(txReq2);
+
+ // 提交区块;
+ TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare();
+ txResultHandle.commit();
+
+ BytesValue latestValue = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getBytes(key,
+ -1);
+ System.out.printf("latest value=[%s] %s \r\n", latestValue.getType(), latestValue.getValue().toUTF8String());
+
+ boolean readable = readableHolder.get();
+ assertTrue(readable);
+
+ LedgerBlock latestBlock = ledgerRepo.getLatestBlock();
+ assertEquals(preBlock.getHeight() + 1, latestBlock.getHeight());
+ assertEquals(resp1.getBlockHeight(), latestBlock.getHeight());
+ assertEquals(resp1.getBlockHash(), latestBlock.getHash());
+ }
+
+ /**
+ * 验证在合约方法中写入数据账户时,如果版本校验失败是否会引发异常而导致回滚;
+ * 期待正确的表现是引发异常而回滚当前交易;
+ */
+ @Test
+ public void testRollbackWhileVersionConfliction() {
+ // 初始化账本到指定的存储库;
+ HashDigest ledgerHash = initLedger(storage, parti0, parti1, parti2, parti3);
+
+ // 重新加载账本;
+ LedgerManager ledgerManager = new LedgerManager();
+ LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, storage);
+
+ // 创建合约处理器;
+ ContractInvokingHandle contractInvokingHandle = new ContractInvokingHandle();
+
+ // 创建和加载合约实例;
+ BlockchainKeypair contractKey = BlockchainKeyGenerator.getInstance().generate();
+ Bytes contractAddress = contractKey.getAddress();
+ TxTestContractImpl contractInstance = new TxTestContractImpl();
+ contractInvokingHandle.setup(contractAddress, TxTestContract.class, contractInstance);
+
+ // 注册合约处理器;
+ DefaultOperationHandleRegisteration opReg = new DefaultOperationHandleRegisteration();
+ opReg.insertAsTopPriority(contractInvokingHandle);
+
+ // 发布指定地址合约
+ deploy(ledgerRepo, ledgerManager, opReg, ledgerHash, contractKey);
+
+ // 注册数据账户;
+ BlockchainKeypair kpDataAccount = BlockchainKeyGenerator.getInstance().generate();
+ contractInstance.setDataAddress(kpDataAccount.getAddress());
+ registerDataAccount(ledgerRepo, ledgerManager, opReg, ledgerHash, kpDataAccount);
+
+ // 调用合约
+ // 构建基于接口调用合约的交易请求,用于测试合约调用;
+ buildBlock(ledgerRepo, ledgerManager, opReg, new TxDefinitor() {
+ @Override
+ public void buildTx(TxBuilder txBuilder) {
+ TxTestContract contractProxy = txBuilder.contract(contractAddress, TxTestContract.class);
+ contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K1", "V1-0",
+ -1);
+ contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K2", "V2-0",
+ -1);
+ }
+ });
+ // 预期数据都能够正常写入;
+ KVDataEntry kv1 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K1",
+ 0);
+ KVDataEntry kv2 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K2",
+ 0);
+ assertEquals(0, kv1.getVersion());
+ assertEquals(0, kv2.getVersion());
+ assertEquals("V1-0", kv1.getValue());
+ assertEquals("V2-0", kv2.getValue());
+
+ // 构建基于接口调用合约的交易请求,用于测试合约调用;
+ buildBlock(ledgerRepo, ledgerManager, opReg, new TxDefinitor() {
+ @Override
+ public void buildTx(TxBuilder txBuilder) {
+ TxTestContract contractProxy = txBuilder.contract(contractAddress, TxTestContract.class);
+ contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K1", "V1-1",
+ 0);
+ contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K2", "V2-1",
+ 0);
+ }
+ });
+ // 预期数据都能够正常写入;
+ kv1 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K1", 1);
+ kv2 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K2", 1);
+ assertEquals(1, kv1.getVersion());
+ assertEquals(1, kv2.getVersion());
+ assertEquals("V1-1", kv1.getValue());
+ assertEquals("V2-1", kv2.getValue());
+
+ // 构建基于接口调用合约的交易请求,用于测试合约调用;
+ buildBlock(ledgerRepo, ledgerManager, opReg, new TxDefinitor() {
+ @Override
+ public void buildTx(TxBuilder txBuilder) {
+ TxTestContract contractProxy = txBuilder.contract(contractAddress, TxTestContract.class);
+ contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K1", "V1-2",
+ 1);
+ contractProxy.testRollbackWhileVersionConfliction(kpDataAccount.getAddress().toBase58(), "K2", "V2-2",
+ 0);
+ }
+ });
+ // 预期数据都能够正常写入;
+ kv1 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K1", 1);
+ assertEquals(1, kv1.getVersion());
+ assertEquals("V1-1", kv1.getValue());
+ kv1 = ledgerRepo.getDataAccountSet().getDataAccount(kpDataAccount.getAddress()).getDataEntry("K1", 2);
+ assertEquals(-1, kv1.getVersion());
+ assertEquals(null, kv1.getValue());
+
+ }
+
+ private LedgerBlock buildBlock(LedgerRepository ledgerRepo, LedgerService ledgerService,
+ OperationHandleRegisteration opReg, TxDefinitor txDefinitor) {
+ LedgerBlock preBlock = ledgerRepo.getLatestBlock();
+ LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock);
+ LedgerEditor newBlockEditor = ledgerRepo.createNextBlock();
+ TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset,
+ opReg, ledgerService);
+
+ TxBuilder txBuilder = new TxBuilder(ledgerRepo.getHash());
+ txDefinitor.buildTx(txBuilder);
+
+ TransactionRequest txReq = buildAndSignRequest(txBuilder, parti0, parti0);
+ TransactionResponse resp = txbatchProcessor.schedule(txReq);
+
+ // 提交区块;
+ TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare();
+ txResultHandle.commit();
+
+ LedgerBlock latestBlock = ledgerRepo.getLatestBlock();
+ assertNotNull(resp.getBlockHash());
+ assertEquals(preBlock.getHeight() + 1, resp.getBlockHeight());
+ return latestBlock;
+ }
+
+ private TransactionRequest buildAndSignRequest(TxBuilder txBuilder, BlockchainKeypair endpointKey,
+ BlockchainKeypair nodeKey) {
+ TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest();
+ txReqBuilder.signAsEndpoint(endpointKey);
+ txReqBuilder.signAsNode(nodeKey);
+ TransactionRequest txReq = txReqBuilder.buildRequest();
+ return txReq;
+ }
+
+ private void registerDataAccount(LedgerRepository ledgerRepo, LedgerManager ledgerManager,
+ DefaultOperationHandleRegisteration opReg, HashDigest ledgerHash, BlockchainKeypair kpDataAccount) {
+ LedgerBlock preBlock = ledgerRepo.getLatestBlock();
+ LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock);
+
+ // 加载合约
+ LedgerEditor newBlockEditor = ledgerRepo.createNextBlock();
+ TransactionBatchProcessor txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset,
+ opReg, ledgerManager);
+
+ // 注册数据账户;
+ TxBuilder txBuilder = new TxBuilder(ledgerHash);
+
+ txBuilder.dataAccounts().register(kpDataAccount.getIdentity());
+ TransactionRequestBuilder txReqBuilder1 = txBuilder.prepareRequest();
+ txReqBuilder1.signAsEndpoint(parti0);
+ txReqBuilder1.signAsNode(parti0);
+ TransactionRequest txReq = txReqBuilder1.buildRequest();
+
+ TransactionResponse resp = txbatchProcessor.schedule(txReq);
+
+ TransactionBatchResultHandle txResultHandle = txbatchProcessor.prepare();
+ txResultHandle.commit();
+
+ assertNotNull(resp.getBlockHash());
+ assertEquals(TransactionState.SUCCESS, resp.getExecutionState());
+ assertEquals(preBlock.getHeight() + 1, resp.getBlockHeight());
+ }
+
private void deploy(LedgerRepository ledgerRepo, LedgerManager ledgerManager,
- DefaultOperationHandleRegisteration opReg, HashDigest ledgerHash,
- BlockchainKeypair contractKey) {
+ DefaultOperationHandleRegisteration opReg, HashDigest ledgerHash, BlockchainKeypair contractKey) {
// 创建新区块的交易处理器;
LedgerBlock preBlock = ledgerRepo.getLatestBlock();
LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(preBlock);
@@ -195,4 +445,10 @@ public class ContractInvokingTest {
new Random().nextBytes(chainCode);
return chainCode;
}
+
+ public static interface TxDefinitor {
+
+ void buildTx(TxBuilder txBuilder);
+
+ }
}
diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/KeyValueEntry.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/KeyValueEntry.java
new file mode 100644
index 00000000..c4b40d59
--- /dev/null
+++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/KeyValueEntry.java
@@ -0,0 +1,19 @@
+package test.com.jd.blockchain.ledger;
+
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.binaryproto.DataField;
+import com.jd.blockchain.binaryproto.PrimitiveType;
+
+@DataContract(code = 0x4010)
+public interface KeyValueEntry {
+
+ @DataField(order = 1, primitiveType = PrimitiveType.TEXT)
+ String getKey();
+
+ @DataField(order = 2, primitiveType = PrimitiveType.TEXT)
+ String getValue();
+
+ @DataField(order = 3, primitiveType = PrimitiveType.INT64)
+ long getVersion();
+
+}
diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/KeyValueObject.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/KeyValueObject.java
new file mode 100644
index 00000000..24215ea7
--- /dev/null
+++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/KeyValueObject.java
@@ -0,0 +1,47 @@
+package test.com.jd.blockchain.ledger;
+
+public class KeyValueObject implements KeyValueEntry {
+
+ private String key;
+
+ private String value;
+
+ private long version;
+
+ public KeyValueObject() {
+ }
+
+ public KeyValueObject(String key, String value, long version) {
+ this.key = key;
+ this.value = value;
+ this.version = version;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public long getVersion() {
+ return version;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public void setVersion(long version) {
+ this.version = version;
+ }
+
+}
diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java
index ce571d71..7bbe7682 100644
--- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java
+++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java
@@ -32,6 +32,38 @@ public class MerkleDataSetTest {
private static final String[] SUPPORTED_PROVIDERS = { ClassicCryptoService.class.getName(),
SMCryptoService.class.getName() };
+
+ /**
+ * 测试存储的增长;
+ */
+ @Test
+ public void testKeyIndex() {
+
+ CryptoProvider[] supportedProviders = new CryptoProvider[SUPPORTED_PROVIDERS.length];
+ for (int i = 0; i < SUPPORTED_PROVIDERS.length; i++) {
+ supportedProviders[i] = Crypto.getProvider(SUPPORTED_PROVIDERS[i]);
+ }
+
+ String keyPrefix = "";
+ CryptoConfig cryptoConfig = new CryptoConfig();
+ cryptoConfig.setSupportedProviders(supportedProviders);
+ cryptoConfig.setHashAlgorithm(ClassicAlgorithm.SHA256);
+ cryptoConfig.setAutoVerifyHash(true);
+
+ MemoryKVStorage storage = new MemoryKVStorage();
+
+ MerkleDataSet mds = new MerkleDataSet(cryptoConfig, keyPrefix, storage, storage);
+ mds.setValue("A", "A".getBytes(), -1);
+ mds.setValue("B", "B".getBytes(), -1);
+ mds.setValue("C", "C".getBytes(), -1);
+
+ mds.commit();
+
+ //校验 Key 的正确性;
+ assertEquals("A", mds.getKeyAtIndex(0));
+ assertEquals("B", mds.getKeyAtIndex(1));
+ assertEquals("C", mds.getKeyAtIndex(2));
+ }
/**
* 测试存储的增长;
@@ -59,6 +91,7 @@ public class MerkleDataSetTest {
mds.commit();
HashDigest root1 = mds.getRootHash();
+
// 1个KV项的存储KEY的数量= 1 + 1(保存SN) + Merkle节点数量;
// 所以:3 项;
diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java
index f857a6ad..f51f211d 100644
--- a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java
+++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java
@@ -14,6 +14,7 @@ import com.jd.blockchain.ledger.BlockchainKeyGenerator;
import com.jd.blockchain.ledger.BlockchainKeypair;
import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.DataAccountRegisterOperation;
+import com.jd.blockchain.ledger.DataVersionConflictException;
import com.jd.blockchain.ledger.EndpointRequest;
import com.jd.blockchain.ledger.LedgerBlock;
import com.jd.blockchain.ledger.LedgerInitSetting;
@@ -245,7 +246,7 @@ public class TransactionBatchProcessorTest {
}
@Test
- public void testTxRollbackByVersionsConfliction() {
+ public void testTxRollbackByVersionsConflict() {
final MemoryKVStorage STORAGE = new MemoryKVStorage();
// 初始化账本到指定的存储库;
@@ -288,6 +289,8 @@ public class TransactionBatchProcessorTest {
"K2", "V-2-1", -1, ledgerHash, parti0, parti0);
TransactionRequest txreq3 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(),
"K3", "V-3-1", -1, ledgerHash, parti0, parti0);
+
+ // 连续写 K1,K1的版本将变为1;
TransactionRequest txreq4 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(),
"K1", "V-1-2", 0, ledgerHash, parti0, parti0);
@@ -316,14 +319,14 @@ public class TransactionBatchProcessorTest {
assertNotNull(v1_1);
assertNotNull(v2);
assertNotNull(v3);
-
+
assertEquals("V-1-1", v1_0.getValue().toUTF8String());
assertEquals("V-1-2", v1_1.getValue().toUTF8String());
assertEquals("V-2-1", v2.getValue().toUTF8String());
assertEquals("V-3-1", v3.getValue().toUTF8String());
// 提交多笔数据写入的交易,包含存在数据版本冲突的交易,验证交易是否正确回滚;
-
+ // 先写一笔正确的交易; k3 的版本将变为 1 ;
TransactionRequest txreq5 = LedgerTestUtils.createTxRequest_DataAccountWrite(dataAccountKeypair.getAddress(),
"K3", "V-3-2", 0, ledgerHash, parti0, parti0);
// 指定冲突的版本号,正确的应该是版本1;
@@ -335,7 +338,14 @@ public class TransactionBatchProcessorTest {
txbatchProcessor = new TransactionBatchProcessor(newBlockEditor, previousBlockDataset, opReg, ledgerManager);
txbatchProcessor.schedule(txreq5);
- txbatchProcessor.schedule(txreq6);
+ // 预期会产生版本冲突异常; DataVersionConflictionException;
+ DataVersionConflictException versionConflictionException = null;
+ try {
+ txbatchProcessor.schedule(txreq6);
+ } catch (DataVersionConflictException e) {
+ versionConflictionException = e;
+ }
+ assertNotNull(versionConflictionException);
newBlock = newBlockEditor.prepare();
newBlockEditor.commit();
@@ -343,11 +353,15 @@ public class TransactionBatchProcessorTest {
BytesValue v1 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K1");
v3 = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getBytes("K3");
- long k1_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getDataVersion("K1");
+ // k1 的版本仍然为1,没有更新;
+ long k1_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress())
+ .getDataVersion("K1");
assertEquals(1, k1_version);
- long k3_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress()).getDataVersion("K3");
+
+ long k3_version = ledgerRepo.getDataAccountSet().getDataAccount(dataAccountKeypair.getAddress())
+ .getDataVersion("K3");
assertEquals(1, k3_version);
-
+
assertNotNull(v1);
assertNotNull(v3);
assertEquals("V-1-2", v1.getValue().toUTF8String());
diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TxTestContract.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TxTestContract.java
new file mode 100644
index 00000000..80ee477f
--- /dev/null
+++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TxTestContract.java
@@ -0,0 +1,15 @@
+package test.com.jd.blockchain.ledger;
+
+import com.jd.blockchain.contract.Contract;
+import com.jd.blockchain.contract.ContractEvent;
+
+@Contract
+public interface TxTestContract {
+
+ @ContractEvent(name = "testReadable")
+ boolean testReadable();
+
+ @ContractEvent(name = "testRollbackWhileVersionConfliction")
+ void testRollbackWhileVersionConfliction(String address, String key, String value, long version);
+
+}
diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TxTestContractImpl.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TxTestContractImpl.java
new file mode 100644
index 00000000..60ee6864
--- /dev/null
+++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TxTestContractImpl.java
@@ -0,0 +1,74 @@
+package test.com.jd.blockchain.ledger;
+
+import com.jd.blockchain.contract.ContractEventContext;
+import com.jd.blockchain.contract.ContractLifecycleAware;
+import com.jd.blockchain.contract.EventProcessingAware;
+import com.jd.blockchain.ledger.KVDataEntry;
+import com.jd.blockchain.utils.Bytes;
+
+public class TxTestContractImpl implements TxTestContract, ContractLifecycleAware, EventProcessingAware {
+
+ private ContractEventContext eventContext;
+
+ private Bytes dataAddress;
+
+ public static String KEY = "k1";
+
+ @Override
+ public boolean testReadable() {
+ KVDataEntry v1 = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(),
+ dataAddress.toBase58(), KEY)[0];
+ String text1 = (String) v1.getValue();
+ System.out.printf("k1=%s, version=%s \r\n", text1, v1.getVersion());
+
+ text1 = null == text1 ? "v" : text1;
+ String newValue = text1 + "-" + (v1.getVersion() + 1);
+ System.out.printf("new value = %s\r\n", newValue);
+ eventContext.getLedger().dataAccount(dataAddress).setText(KEY, newValue, v1.getVersion());
+
+ KVDataEntry v2 = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(),
+ dataAddress.toBase58(), KEY)[0];
+ System.out.printf("---- read new value ----\r\nk1=%s, version=%s \r\n", v2.getValue(), v2.getVersion());
+
+ String text2 = (String) v2.getValue();
+ return text1.equals(text2);
+ }
+
+ @Override
+ public void testRollbackWhileVersionConfliction(String address, String key, String value, long version) {
+ eventContext.getLedger().dataAccount(address).setText(key, value, version);
+ }
+
+
+ @Override
+ public void postConstruct() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void beforeDestroy() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void beforeEvent(ContractEventContext eventContext) {
+ this.eventContext = eventContext;
+ }
+
+ @Override
+ public void postEvent(ContractEventContext eventContext, Exception error) {
+ this.eventContext = null;
+ }
+
+ public Bytes getDataAddress() {
+ return dataAddress;
+ }
+
+ public void setDataAddress(Bytes dataAddress) {
+ this.dataAddress = dataAddress;
+ }
+
+
+}
diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValueEncoding.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValueEncoding.java
index af67719e..baee5868 100644
--- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValueEncoding.java
+++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValueEncoding.java
@@ -1,32 +1,36 @@
package com.jd.blockchain.ledger;
-import com.jd.blockchain.binaryproto.BinaryProtocol;
-import com.jd.blockchain.binaryproto.DataContract;
-import com.jd.blockchain.ledger.resolver.*;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import com.jd.blockchain.binaryproto.BinaryProtocol;
+import com.jd.blockchain.binaryproto.DataContract;
+import com.jd.blockchain.ledger.resolver.BooleanToBytesValueResolver;
+import com.jd.blockchain.ledger.resolver.BytesToBytesValueResolver;
+import com.jd.blockchain.ledger.resolver.BytesValueResolver;
+import com.jd.blockchain.ledger.resolver.IntegerToBytesValueResolver;
+import com.jd.blockchain.ledger.resolver.LongToBytesValueResolver;
+import com.jd.blockchain.ledger.resolver.ShortToBytesValueResolver;
+import com.jd.blockchain.ledger.resolver.StringToBytesValueResolver;
+
public class BytesValueEncoding {
private static final Map, BytesValueResolver> CLASS_RESOLVER_MAP = new ConcurrentHashMap<>();
private static final Map DATA_TYPE_RESOLVER_MAP = new ConcurrentHashMap<>();
+ private static final Object[] EMPTY_OBJECTS = {};
+
static {
init();
}
private static void init() {
- BytesValueResolver[] resolvers = new BytesValueResolver[]{
- new BytesToBytesValueResolver(),
- new IntegerToBytesValueResolver(),
- new LongToBytesValueResolver(),
- new ShortToBytesValueResolver(),
- new StringToBytesValueResolver()
- };
+ BytesValueResolver[] resolvers = new BytesValueResolver[] { new BooleanToBytesValueResolver(),
+ new BytesToBytesValueResolver(), new IntegerToBytesValueResolver(), new LongToBytesValueResolver(),
+ new ShortToBytesValueResolver(), new StringToBytesValueResolver() };
for (BytesValueResolver currResolver : resolvers) {
// 填充classMAP
@@ -47,7 +51,6 @@ public class BytesValueEncoding {
}
}
-
public static BytesValue encodeSingle(Object value, Class> type) {
if (value == null) {
return null;
@@ -60,7 +63,8 @@ public class BytesValueEncoding {
if (type.isInterface()) {
// 判断是否含有DataContract注解
if (!type.isAnnotationPresent(DataContract.class)) {
- throw new IllegalStateException(String.format("Interface[%s] can not be serialize !!!", type.getName()));
+ throw new IllegalStateException(
+ String.format("Interface[%s] can not be serialize !!!", type.getName()));
}
// 将对象序列化
byte[] serialBytes = BinaryProtocol.encode(value, type);
@@ -72,7 +76,7 @@ public class BytesValueEncoding {
}
return bytesValueResolver.encode(value, type);
}
-
+
public static BytesValueList encodeArray(Object[] values, Class>[] types) {
if (values == null || values.length == 0) {
return null;
@@ -101,11 +105,14 @@ public class BytesValueEncoding {
}
return type == null ? valueResolver.decode(value) : valueResolver.decode(value, type);
}
-
+
public static Object[] decode(BytesValueList values, Class>[] types) {
+ if (values == null) {
+ return EMPTY_OBJECTS;
+ }
BytesValue[] bytesValues = values.getValues();
if (bytesValues == null || bytesValues.length == 0) {
- return null;
+ return EMPTY_OBJECTS;
}
// 允许types为null,此时每个BytesValue按照当前的对象来处理
// 若types不为null,则types's长度必须和bytesValues一致
@@ -120,7 +127,8 @@ public class BytesValueEncoding {
DataType dataType = bytesValue.getType();
BytesValueResolver valueResolver = DATA_TYPE_RESOLVER_MAP.get(dataType);
if (valueResolver == null) {
- throw new IllegalStateException(String.format("DataType[%s] can not find encoder !!!", dataType.name()));
+ throw new IllegalStateException(
+ String.format("DataType[%s] can not find encoder !!!", dataType.name()));
}
resolveObjs[i] = valueResolver.decode(bytesValue);
}
@@ -132,7 +140,7 @@ public class BytesValueEncoding {
}
return resolveObjs;
}
-
+
public static Object getDefaultValue(Class> type) {
if (type == void.class || type == Void.class) {
return null;
@@ -174,14 +182,27 @@ public class BytesValueEncoding {
if (currParamType.isInterface()) {
// 接口序列化必须实现DataContract注解
if (!currParamType.isAnnotationPresent(DataContract.class)) {
- throw new IllegalStateException(String.format("Interface[%s] can not be serialize !!!", currParamType.getName()));
+ throw new IllegalStateException(
+ String.format("Interface[%s] can not be annotated as a DataContract!!!", currParamType.getName()));
}
return true;
}
+
+ if (currParamType.isArray() ) {
+ Class> componentType = currParamType.getComponentType();
+ if (componentType.isInterface()) {
+ // 接口序列化必须实现DataContract注解
+ if (!componentType.isAnnotationPresent(DataContract.class)) {
+ throw new IllegalStateException(
+ String.format("Interface[%s] can not be annotated as a DataContract!!!", currParamType.getName()));
+ }
+ return true;
+ }
+ }
+
return CLASS_RESOLVER_MAP.containsKey(currParamType);
}
-
public static class BytesValueListData implements BytesValueList {
private List bytesValues = new ArrayList<>();
diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataVersionConflictException.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataVersionConflictException.java
new file mode 100644
index 00000000..8af67d01
--- /dev/null
+++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataVersionConflictException.java
@@ -0,0 +1,27 @@
+package com.jd.blockchain.ledger;
+
+public class DataVersionConflictException extends BlockRollbackException {
+
+ private static final long serialVersionUID = 3583192000738807503L;
+
+ private TransactionState state;
+
+ public DataVersionConflictException() {
+ this(TransactionState.DATA_VERSION_CONFLICT, null);
+ }
+
+ public DataVersionConflictException(String message) {
+ this(TransactionState.DATA_VERSION_CONFLICT, message);
+ }
+
+ private DataVersionConflictException(TransactionState state, String message) {
+ super(message);
+ assert TransactionState.SUCCESS != state;
+ this.state = state;
+ }
+
+ public TransactionState getState() {
+ return state;
+ }
+
+}
diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java
index 6955eb94..55390655 100644
--- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java
+++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java
@@ -38,6 +38,11 @@ public enum TransactionState {
* 合约不存在;
*/
CONTRACT_DOES_NOT_EXIST((byte) 0x04),
+
+ /**
+ * 数据写入时版本冲突;
+ */
+ DATA_VERSION_CONFLICT((byte) 0x05),
/**
* 由于在错误的账本上执行交易而被丢弃;
diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BooleanToBytesValueResolver.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BooleanToBytesValueResolver.java
new file mode 100644
index 00000000..0664fdb9
--- /dev/null
+++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BooleanToBytesValueResolver.java
@@ -0,0 +1,58 @@
+package com.jd.blockchain.ledger.resolver;
+
+import com.jd.blockchain.ledger.BytesData;
+import com.jd.blockchain.ledger.BytesValue;
+import com.jd.blockchain.ledger.DataType;
+import com.jd.blockchain.utils.Bytes;
+import com.jd.blockchain.utils.io.BytesUtils;
+
+import java.util.Set;
+
+public class BooleanToBytesValueResolver extends AbstractBytesValueResolver {
+
+ private final Class>[] supportClasses = { Boolean.class, boolean.class };
+
+ private final DataType[] supportDataTypes = { DataType.BOOLEAN };
+
+ private final Set> convertClasses = initBooleanConvertSet();
+
+ @Override
+ public BytesValue encode(Object value, Class> type) {
+ if (!isSupport(type)) {
+ throw new IllegalStateException(String.format("Un-support encode Class[%s] Object !!!", type.getName()));
+ }
+ return BytesData.fromBoolean((boolean) value);
+ }
+
+ @Override
+ public Class>[] supportClasses() {
+ return supportClasses;
+ }
+
+ @Override
+ public DataType[] supportDataTypes() {
+ return supportDataTypes;
+ }
+
+ @Override
+ protected Object decode(Bytes value) {
+ return BytesUtils.toInt(value.toBytes());
+ }
+
+ @Override
+ public Object decode(BytesValue value, Class> clazz) {
+ // 支持转换为short、int、long
+ int intVal = (int) decode(value);
+ if (convertClasses.contains(clazz)) {
+ // 对于short和Short需要强制类型转换
+ if (clazz.equals(short.class) || clazz.equals(Short.class)) {
+ return (short) intVal;
+ } else if (clazz.equals(long.class) || clazz.equals(Long.class)) {
+ return (long) intVal;
+ }
+ return intVal;
+ } else {
+ throw new IllegalStateException(String.format("Un-Support decode value to class[%s] !!!", clazz.getName()));
+ }
+ }
+}
diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BytesValueResolver.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BytesValueResolver.java
index 08e48658..4c659567 100644
--- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BytesValueResolver.java
+++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/resolver/BytesValueResolver.java
@@ -10,71 +10,79 @@ import java.util.Set;
public interface BytesValueResolver {
- /**
- * Int相关的可转换Class集合
- */
- Class>[] supportIntConvertClasses = {
- short.class, Short.class, int.class, Integer.class, long.class, Long.class};
+ /**
+ * Boolean相关的可转换Class集合
+ */
+ Class>[] supportBooleanConvertClasses = { boolean.class, Boolean.class };
- /**
- * 字节数组(字符串)相关可转换的Class集合
- */
- Class>[] supportByteConvertClasses = {
- String.class, Bytes.class, byte[].class};
+ /**
+ * Int相关的可转换Class集合
+ */
+ Class>[] supportIntConvertClasses = { short.class, Short.class, int.class, Integer.class, long.class,
+ Long.class };
- default Set> initIntConvertSet() {
- return new HashSet<>(Arrays.asList(supportIntConvertClasses));
- }
+ /**
+ * 字节数组(字符串)相关可转换的Class集合
+ */
+ Class>[] supportByteConvertClasses = { String.class, Bytes.class, byte[].class };
+
+ default Set> initBooleanConvertSet() {
+ return new HashSet<>(Arrays.asList(supportBooleanConvertClasses));
+ }
- default Set> initByteConvertSet() {
- return new HashSet<>(Arrays.asList(supportByteConvertClasses));
- }
+ default Set> initIntConvertSet() {
+ return new HashSet<>(Arrays.asList(supportIntConvertClasses));
+ }
- /**
- * 将对象转换为BytesValue
- *
- * @param value
- * @return
- */
- BytesValue encode(Object value);
+ default Set> initByteConvertSet() {
+ return new HashSet<>(Arrays.asList(supportByteConvertClasses));
+ }
- /**
- * 将对象转换为BytesValue
- *
- * @param value
- * @param type
- * @return
- */
- BytesValue encode(Object value, Class> type);
+ /**
+ * 将对象转换为BytesValue
+ *
+ * @param value
+ * @return
+ */
+ BytesValue encode(Object value);
- /**
- * 当前解析器支持的Class列表
- *
- * @return
- */
- Class>[] supportClasses();
+ /**
+ * 将对象转换为BytesValue
+ *
+ * @param value
+ * @param type
+ * @return
+ */
+ BytesValue encode(Object value, Class> type);
- /**
- * 当前解析器支持的DataType列表
- *
- * @return
- */
- DataType[] supportDataTypes();
+ /**
+ * 当前解析器支持的Class列表
+ *
+ * @return
+ */
+ Class>[] supportClasses();
- /**
- * 将BytesValue解析为对应的Object
- *
- * @param value
- * @return
- */
- Object decode(BytesValue value);
+ /**
+ * 当前解析器支持的DataType列表
+ *
+ * @return
+ */
+ DataType[] supportDataTypes();
- /**
- * 将BytesValue转换为指定Class的Object
- *
- * @param value
- * @param clazz
- * @return
- */
- Object decode(BytesValue value, Class> clazz);
+ /**
+ * 将BytesValue解析为对应的Object
+ *
+ * @param value
+ * @return
+ */
+ Object decode(BytesValue value);
+
+ /**
+ * 将BytesValue转换为指定Class的Object
+ *
+ * @param value
+ * @param clazz
+ * @return
+ */
+ Object decode(BytesValue value, Class> clazz);
}
diff --git a/source/pom.xml b/source/pom.xml
index 2e93f9ba..af02b0af 100644
--- a/source/pom.xml
+++ b/source/pom.xml
@@ -523,4 +523,4 @@
-
\ No newline at end of file
+
diff --git a/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/RuntimeContext.java b/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/RuntimeContext.java
index 569a95a5..04bc55cd 100644
--- a/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/RuntimeContext.java
+++ b/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/RuntimeContext.java
@@ -81,7 +81,7 @@ public abstract class RuntimeContext {
if (jarFile.isFile()) {
FileUtils.deleteFile(jarFile);
} else {
- throw new IllegalStateException("Code storage confliction! --" + jarFile.getAbsolutePath());
+ throw new IllegalStateException("Code storage conflict! --" + jarFile.getAbsolutePath());
}
}
FileUtils.writeBytes(jarBytes, jarFile);
diff --git a/source/test/test-contract/pom.xml b/source/test/test-contract/pom.xml
new file mode 100644
index 00000000..a9c59ddf
--- /dev/null
+++ b/source/test/test-contract/pom.xml
@@ -0,0 +1,34 @@
+
+ 4.0.0
+
+ com.jd.blockchain
+ test
+ 1.0.1.RELEASE
+
+ test-contract
+
+
+
+ com.jd.blockchain
+ contract-jvm
+ ${project.version}
+
+
+ com.jd.blockchain
+ ledger-core
+ ${project.version}
+
+
+ com.jd.blockchain
+ storage-rocksdb
+ ${project.version}
+
+
+ com.jd.blockchain
+ crypto-classic
+ ${project.version}
+
+
+
\ No newline at end of file
diff --git a/source/test/test-contract/src/test/java/test/com/jd/blockchain/contract/ContractTransactionRollbackTest.java b/source/test/test-contract/src/test/java/test/com/jd/blockchain/contract/ContractTransactionRollbackTest.java
new file mode 100644
index 00000000..bda44aed
--- /dev/null
+++ b/source/test/test-contract/src/test/java/test/com/jd/blockchain/contract/ContractTransactionRollbackTest.java
@@ -0,0 +1,14 @@
+package test.com.jd.blockchain.contract;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class ContractTransactionRollbackTest {
+
+ @Test
+ public void test() {
+
+ }
+
+}
diff --git a/source/test/test-integration/src/test/resources/ledger_init_test.init b/source/test/test-integration/src/test/resources/ledger_init_test.init
index a1dde11f..7b303e35 100644
--- a/source/test/test-integration/src/test/resources/ledger_init_test.init
+++ b/source/test/test-integration/src/test/resources/ledger_init_test.init
@@ -3,7 +3,7 @@
ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe
#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途;
-ledger.name=
+ledger.name=test-ledger
#声明的账本创建时间;格式为 “yyyy-MM-dd HH:mm:ss.SSSZ”,表示”年-月-日 时:分:秒:毫秒时区“;例如:“2019-08-01 14:26:58.069+0800”,其中,+0800 表示时区是东8区
created-time=2019-08-01 14:26:58.069+0800
diff --git a/source/tools/tools-initializer/src/test/resources/ledger-binding.conf b/source/tools/tools-initializer/src/test/resources/ledger-binding.conf
index e4cf6c2b..99df03d0 100644
--- a/source/tools/tools-initializer/src/test/resources/ledger-binding.conf
+++ b/source/tools/tools-initializer/src/test/resources/ledger-binding.conf
@@ -6,7 +6,9 @@ j5kLUENMvcUooZjKfz2bEYU6zoK9DAqbdDDU8aZEZFR4qf
#第1个账本[j5ptBmn67B2p3yki3ji1j2ZMjnJhrUvP4kFpGmcXgvrhmk]的配置;
#账本的当前共识参与方的ID;
+binding.j5ptBmn67B2p3yki3ji1j2ZMjnJhrUvP4kFpGmcXgvrhmk.name = Test-Ledger-01
binding.j5ptBmn67B2p3yki3ji1j2ZMjnJhrUvP4kFpGmcXgvrhmk.parti.address=1
+binding.j5ptBmn67B2p3yki3ji1j2ZMjnJhrUvP4kFpGmcXgvrhmk.parti.name=parti-1
#账本的当前共识参与方的私钥文件的保存路径;
binding.j5ptBmn67B2p3yki3ji1j2ZMjnJhrUvP4kFpGmcXgvrhmk.parti.pk-path=keys/jd-com.priv
#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性;
@@ -21,7 +23,9 @@ binding.j5ptBmn67B2p3yki3ji1j2ZMjnJhrUvP4kFpGmcXgvrhmk.db.pwd=kksfweffj
#第2个账本[j5kLUENMvcUooZjKfz2bEYU6zoK9DAqbdDDU8aZEZFR4qf]的配置;
#账本的当前共识参与方的ID;
+binding.j5kLUENMvcUooZjKfz2bEYU6zoK9DAqbdDDU8aZEZFR4qf.name=Test-Ledger-02
binding.j5kLUENMvcUooZjKfz2bEYU6zoK9DAqbdDDU8aZEZFR4qf.parti.address=2
+binding.j5kLUENMvcUooZjKfz2bEYU6zoK9DAqbdDDU8aZEZFR4qf.parti.name=parti-2
#账本的当前共识参与方的私钥文件的保存路径;
binding.j5kLUENMvcUooZjKfz2bEYU6zoK9DAqbdDDU8aZEZFR4qf.parti.pk-path=keys/jd-com-1.priv
#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性;
diff --git a/source/utils/utils-http/pom.xml b/source/utils/utils-http/pom.xml
index 53053a54..e62e9564 100644
--- a/source/utils/utils-http/pom.xml
+++ b/source/utils/utils-http/pom.xml
@@ -43,6 +43,11 @@
org.springframework
spring-beans
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+ test
+
-
\ No newline at end of file
+