From 28a6d81f0ced94e3b69ef7fe8cd6d656d6b92740 Mon Sep 17 00:00:00 2001 From: zhaoguangwei Date: Wed, 29 May 2019 19:39:03 +0800 Subject: [PATCH] IntegrationTest4Contract.java can run OK. --- .../contract/ContractSerializeUtils.java | 11 ++++- .../blockchain/ledger/ContractBizContent.java | 13 ++++-- .../sdk/client/GatewayServiceFactory.java | 2 +- .../contract/samples/AssetContract2.java | 14 +++--- .../sdk/test/SDK_Contract_Test.java | 44 +++++++++++++++--- .../src/test/resources/contract.jar | Bin 7407 -> 7914 bytes .../jd/blockchain/intgr/IntegrationBase.java | 28 +++++++---- .../intgr/contract/AssetContract2.java | 31 +++++------- .../src/test/resources/contract.jar | Bin 7648 -> 7914 bytes 9 files changed, 94 insertions(+), 49 deletions(-) diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractSerializeUtils.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractSerializeUtils.java index 9d5d3d77..2a09cd1b 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractSerializeUtils.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/contract/ContractSerializeUtils.java @@ -5,6 +5,7 @@ import com.jd.blockchain.binaryproto.DataContract; import com.jd.blockchain.consts.DataCodes; import com.jd.blockchain.ledger.*; import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.IllegalDataException; import org.springframework.util.ReflectionUtils; import java.math.BigDecimal; import java.nio.ByteBuffer; @@ -117,7 +118,7 @@ public class ContractSerializeUtils { DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_TEXT, CONTRACT_TEXT.class); DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BINARY, CONTRACT_BINARY.class); DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BIG_INT, CONTRACT_BIG_INT.class); -// DATA_CONTRACT_MAP.put(DataCodes.TX_CONTENT_BODY, TransactionContentBody.class); + DATA_CONTRACT_MAP.put(DataCodes.CONTRACT_BIZ_CONTENT, ContractBizContent.class); return DATA_CONTRACT_MAP; } @@ -140,8 +141,12 @@ public class ContractSerializeUtils { return (CONTRACT_BINARY) () -> (Bytes) object; }else if(getDataIntf().get(dataContract.code()).equals(CONTRACT_BIG_INT.class)){ return (CONTRACT_BIG_INT) () -> new BigDecimal(object.toString()); + }else if(getDataIntf().get(dataContract.code()).equals(ContractBizContent.class)){ + ContractBizContent contractBizContent = (ContractBizContent)object; + return contractBizContent; + }else { + throw new IllegalDataException("cann't get new Object by dataContract and object."); } - return null; } /** @@ -164,6 +169,8 @@ public class ContractSerializeUtils { return CONTRACT_TEXT.class; }else if(classType.equals(Bytes.class)){ return CONTRACT_BINARY.class; + }else if(classType.equals(BigDecimal.class)){ + return CONTRACT_BIG_INT.class; } return null; } diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractBizContent.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractBizContent.java index e9cabf75..ab2ff4c3 100644 --- a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractBizContent.java +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractBizContent.java @@ -22,9 +22,16 @@ public interface ContractBizContent { HashDigest getLedgerHash(); /** - * 操作列表; + * 地址; * @return */ - @DataField(order = 2, list = true, refContract = true, genericContract = true) - Operation[] getOperations(); + @DataField(order = 2, primitiveType = PrimitiveType.TEXT) + String getAddr(); + + /** + * 年龄; + * @return + */ + @DataField(order = 3, primitiveType = PrimitiveType.INT32) + int getAge(); } diff --git a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/GatewayServiceFactory.java b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/GatewayServiceFactory.java index f3e3b42c..666677d8 100644 --- a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/GatewayServiceFactory.java +++ b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/GatewayServiceFactory.java @@ -62,7 +62,7 @@ public class GatewayServiceFactory implements BlockchainServiceFactory, Closeabl DataContractRegistry.register(CONTRACT_INT64.class); DataContractRegistry.register(CONTRACT_TEXT.class); DataContractRegistry.register(CONTRACT_BINARY.class); -// DataContractRegistry.register(CONTRACT_BIG_INT.class); + DataContractRegistry.register(ContractBizContent.class); ByteArrayObjectUtil.init(); } diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContract2.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContract2.java index 8bb2256e..03e6e720 100644 --- a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContract2.java +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContract2.java @@ -4,9 +4,12 @@ import com.jd.blockchain.binaryproto.DataContract; import com.jd.blockchain.consts.DataCodes; import com.jd.blockchain.contract.Contract; import com.jd.blockchain.contract.ContractEvent; +import com.jd.blockchain.ledger.ContractBizContent; import com.jd.blockchain.ledger.TransactionContentBody; import com.jd.blockchain.utils.Bytes; +import java.math.BigDecimal; + /** * 示例:一个“资产管理”智能合约; * @@ -22,19 +25,16 @@ public interface AssetContract2 { * 新发行的资产的持有账户; */ @ContractEvent(name = "issue-asset-0") - void issue(@DataContract(code = DataCodes.TX_CONTENT_BODY) TransactionContentBody transactionContentBody, - @DataContract(code = DataCodes.CONTRACT_TEXT) String assetHolderAddress); + void issue(ContractBizContent contractBizContent, String assetHolderAddress); /** * issue asset; - * @param transactionContentBody + * @param contractBizContent * @param assetHolderAddress * @param cashNumber */ @ContractEvent(name = "issue-asset") - public void issue(@DataContract(code = DataCodes.TX_CONTENT_BODY) TransactionContentBody transactionContentBody, - @DataContract(code = DataCodes.CONTRACT_TEXT) String assetHolderAddress, - @DataContract(code = DataCodes.CONTRACT_INT64) long cashNumber); + public void issue(ContractBizContent contractBizContent, String assetHolderAddress, long cashNumber); /** * Bytes can bring the byte[]; @@ -49,5 +49,5 @@ public interface AssetContract2 { void issue(Byte bytes, String assetHolderAddress, long cashNumber); @ContractEvent(name = "issue-asset-4") - void issue1(byte[] bytes, String assetHolderAddress, long cashNumber); + void issue(Byte byteObj, String assetHolderAddress, Bytes cashNumber); } \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_Contract_Test.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_Contract_Test.java index 000e7879..12954711 100644 --- a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_Contract_Test.java +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_Contract_Test.java @@ -23,7 +23,9 @@ import org.springframework.core.io.ClassPathResource; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.math.BigDecimal; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** @@ -64,21 +66,27 @@ public class SDK_Contract_Test { /** * 演示合约执行的过程; */ - @Test +// @Test public void demoContract1() { + String dataAddress = registerData4Contract(); // 发起交易; TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); - String contractAddress = "LdeNhjPGzHcHL6rLcJ7whHxUbn9Tv7qSKRfEA"; + String contractAddress = "LdeNg8JHFCKABJt6AaRNVCZPgY4ofGPd8MgcR"; AssetContract2 assetContract = txTemp.contract(contractAddress, AssetContract2.class); - TransactionContentBody transactionContentBody = new TransactionContentBody() { + ContractBizContent contractBizContent = new ContractBizContent() { @Override public HashDigest getLedgerHash() { return new HashDigest(ClassicAlgorithm.SHA256, "zhaogw".getBytes()); } @Override - public Operation[] getOperations() { - return new Operation[0]; + public String getAddr() { + return "jinghailu street."; + } + + @Override + public int getAge() { + return 100; } }; // assetContract.issue(transactionContentBody,contractAddress); @@ -86,13 +94,19 @@ public class SDK_Contract_Test { // assetContract.issue(Bytes.fromString("zhaogw, contract based interface is OK!"),contractAddress,77777); // assetContract.issue(Bytes.fromString("zhaogw, contract based interface is OK!"),contractAddress,77777); Byte byteObj = Byte.parseByte("127"); - assetContract.issue(byteObj,contractAddress,321123); + assetContract.issue(byteObj,dataAddress,321123); + assetContract.issue(contractBizContent,dataAddress); + assetContract.issue(Byte.parseByte("126"),dataAddress,Bytes.fromString("100.234")); // TX 准备就绪; PreparedTransaction prepTx = txTemp.prepare(); prepTx.sign(signKeyPair); // 提交交易; - prepTx.commit(); + TransactionResponse transactionResponse = prepTx.commit(); + + //check; + KVDataEntry[] dataEntries = bcsrv.getDataEntries(ledgerHash,dataAddress,"total"); + assertEquals("100",dataEntries[0].getValue().toString()); } @Test @@ -126,6 +140,22 @@ public class SDK_Contract_Test { this.setDataInDataAddress(dataAccount.getAddress(),keys,values2,1); } + private String registerData4Contract(){ + // 在本地定义 TX 模板 + TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); + BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate(); + txTemp.dataAccounts().register(dataAccount.getIdentity()); + txTemp.dataAccount(dataAccount.getAddress()).set("total", 200, -1); + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + prepTx.sign(signKeyPair); + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + assertTrue(transactionResponse.isSuccess()); + + return dataAccount.getAddress().toBase58(); + } + private void setDataInDataAddress(Bytes dataAddress, String[] keys, String[] values, long version){ // 在本地定义 TX 模板 TransactionTemplate txTemp = bcsrv.newTransaction(ledgerHash); diff --git a/source/sdk/sdk-samples/src/test/resources/contract.jar b/source/sdk/sdk-samples/src/test/resources/contract.jar index 437022505ab92b5c2ef6100ee8d5cb79131287dd..3f21f9b388229093ba00383b91e01b4389f40378 100644 GIT binary patch delta 2492 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 delta 1981 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 delta 2224 zcmY*bc{~#gAD?4{4UZ$om>5m&BdV>lU`=ikm(ORKE4%;Yp?XM0IW-}V5;iZ$0t7wtQX9o&w#pI9wH$;#)eRC})4 z{FC!!{!bF=fGaSr!4SLU=+&$Ak3lM{K13o*CqO$KsWXEAaw=`9#$T&=@dnlhE?1Rk zq&a1^{)9O~QoY*PNuRA5wvJw{7#-LOOTv9*7Pb=mvx6Q(`|rQ*P5^|05HKw{k9yNV z>Q8RCtvuSh*Jp&;b(#+RMh+g`jKWWfFSMOxbhp%V7&pVnE0%oI2fj(V`;OrwV|;cg zzP(yB=aLedm)zjn?o_-fmvl*@58ic7o0wtXHn6O6*B- znDDYZ`}(ys)4a|o+ab+#H$Sn-lvUFT%Fq85o*W;omzB??V8(ceFr?&!06{3m$?}R| zB-~Om_(jr9Vq|v)tDF75HRi=4j8v?mI>#a4|J0s1qP8xK)A|Je5r>th1!8xB0035i zJ_6DwRXF%L2%CvVa|!9FC@U$IPVi2;cc|VUejF$Ii7UyHr^_zlI=vis_H9x#B<;P6 zi_5LH4?oJ<@{y`U$<1uHfGxG_Q|@hIzv`4TrX`EaiTENIKkyw4aJ*j8L*r zhIgHKGCarer^$ppJvi&x=~weNCDdmxN(fyc&Glz^m6}s%#7?842l*^s-ClBa`y;j3 z{MhPV)hY@tokqvCr=|30C*$t9_XnbBHRM&GJx86LH<)bS_G5MU9 zm7vPR_myWgWjmuoVT=M9Rqe&j?wLN{AqI{@nQwZ8fQzrdMw}?^PqSW&>-2=0keboG z3fg`sYj59Tg=@_5wQ}Oe{TVlq9$(Xd;FE3yaXP!s(kP{$z#XSuIvsWgFlZXt=-m*4 zs`EI4Ih71a`mrvK1JKakIlaP4spOB^N`oaFxvypBr|l7NP80|5?l)%6ePx zox~htcuyMwR^ueXti?cPL%wySri#m^DDaCAdwz3Bliwg08xW*z_IXl%ZWO-zgI_a* zF>hOTGE2YcrPcVVN!mDg!g4OKy7EQ)ZVo7m;6?3k{;N)(%t17I{GZ;wiqa zTAVu_WDO;Ki>9`LZjSigGZ$DC=yF*$)K``bya~?q{ zIIG2E-tiY9ft^2ODY+?oHDJK%Qjd}jzul$ODbcY}(K6tn{;274;Q}Qd@5>4sU|y!wou;m zm1_d=z+4%3N<~u&NnLNf>%Nhhs6oLjxW&S$@|_$@6&NtjB;OEeNpQ)TLjpJF8_5D9 zk#SVT-1m7_=DCyVEIjbxEHlRTdb1&`XX=bYE3qa%5#6)lhLAJvCWJ&^^rDj2-+zsY zt%?XEk9j>V!#1SBSi!)nvkbDjXym;r(a5_2Wwqo)^AM7#ksDtNc~c~kDkHz&TN7Qd z?OeXCog?YBgB;I2pJ6U~3)6g=ktL^OIkp@+Q_vCj`lSXsZ^dhRBq)TH1vD^3+&6R3 z#b35(Cxnc%^{jRN^bOkzG1rn6B@FnTx=u%^tCqcu z3H!{u=Q8-5v;@3hjVMX&Ei~E~>`@jdwoL+M)R2Ot7bLp^8Nvwy)jUM3-$`MSbWnhM zMGB94cC%3;|Mz@<6X_{mR0cTU;+J@(u^a;!vUyn*1uHx|8O6QkkUh=|CZptPC#{Tbp!J*UHPpvQDKK*f|CPq4)Uz?BH zK(RhIlX*V4q$xhr$I3>*(ni6xWY?c0-s2l^?-s11b?`fQCb$GnF0mC7`Bs0M_9+cs zRNTN)uOFC5@GN(SFUp=3Tef}$L~M9C%&CR_ke4?;EP$@RC+BqfI2rH0a`=%sthGUG zLLFMLvO8SIU>B{62t#gbQPp|T_X)2G;jW{?-Dh%6Msz43aU>+cM=v3`5=!b%!ic@u zE`_7Cfj%^IieXHXWlWRed0}I;g9SRHE#E>i&-fiO2Hu+n(GB^we`m?EgDxLtFW7jp z!jcT0JHI`D3o7QSvff`q4PkHNJn{wfDo$Ry6clvocgX<&&K;YL|D~ck0^notA&8G21qB`dNQ6Sz W9fg0brRfhu`03+NK`xr)5&mED$q=sq