├── .gitignore ├── ReadMe.md ├── pom.xml └── src ├── main ├── java │ ├── com │ │ └── genesis │ │ │ └── api │ │ │ ├── AbiTool.java │ │ │ ├── CryptoUtil.java │ │ │ ├── EventCallBack.java │ │ │ ├── GenesisUtil.java │ │ │ ├── LogObserver.java │ │ │ ├── NodeApi.java │ │ │ ├── NodeSrv.java │ │ │ ├── QueryRecTask.java │ │ │ ├── QueryType.java │ │ │ ├── TransactionUtil.java │ │ │ ├── bean │ │ │ ├── exception │ │ │ │ ├── BaseException.java │ │ │ │ ├── ErrCode.java │ │ │ │ └── LIBException.java │ │ │ ├── genesis │ │ │ │ ├── BaseResp.java │ │ │ │ ├── ResultBlock.java │ │ │ │ ├── ResultCommit.java │ │ │ │ └── ResultQuery.java │ │ │ └── model │ │ │ │ ├── DeployContractResult.java │ │ │ │ ├── QueryBlockTransactionHashs.java │ │ │ │ ├── QueryKVResult.java │ │ │ │ ├── QueryPrefixKVs.java │ │ │ │ ├── QueryTransaction.java │ │ │ │ ├── QueryTransactionPayload.java │ │ │ │ ├── QueryTransactionReceipt.java │ │ │ │ ├── RawTransactionData.java │ │ │ │ ├── ReceiptLogs.java │ │ │ │ ├── ReceiptObject.java │ │ │ │ ├── SendTransactionResult.java │ │ │ │ └── TransactionKVTx.java │ │ │ ├── crypto │ │ │ ├── PrivateKey.java │ │ │ ├── PrivateKeyECDSA.java │ │ │ ├── PublicKey.java │ │ │ ├── PublicKeyECDSA.java │ │ │ ├── Signature.java │ │ │ └── SignatureECDSA.java │ │ │ ├── util │ │ │ ├── ByteUtil.java │ │ │ └── OsUtil.java │ │ │ └── wallet │ │ │ └── Wallet.java │ └── message.proto └── resources │ ├── lib │ └── gmhelper.jar │ └── logback.xml └── test └── java ├── AbiToolTest.java ├── CryptoUtilTest.java ├── DemoEventCallBack.java ├── NodeApiTest.java ├── com └── genesis │ └── api │ └── crypto │ └── CryptoECDSATest.java └── test.sol /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/dictionaries 10 | 11 | # Sensitive or high-churn files: 12 | .idea/**/dataSources/ 13 | .idea/**/dataSources.ids 14 | .idea/**/dataSources.xml 15 | .idea/**/dataSources.local.xml 16 | .idea/**/sqlDataSources.xml 17 | .idea/**/dynamic.xml 18 | .idea/**/uiDesigner.xml 19 | 20 | # Gradle: 21 | .idea/**/gradle.xml 22 | .idea/**/libraries 23 | 24 | # CMake 25 | cmake-build-debug/ 26 | 27 | # Mongo Explorer plugin: 28 | .idea/**/mongoSettings.xml 29 | 30 | ## File-based project format: 31 | *.iws 32 | 33 | ## Plugin-specific files: 34 | 35 | # IntelliJ 36 | out/ 37 | 38 | # mpeltonen/sbt-idea plugin 39 | .idea_modules/ 40 | 41 | # JIRA plugin 42 | atlassian-ide-plugin.xml 43 | 44 | # Cursive Clojure plugin 45 | .idea/replstate.xml 46 | 47 | # Crashlytics plugin (for Android Studio and IntelliJ) 48 | com_crashlytics_export_strings.xml 49 | crashlytics.properties 50 | crashlytics-build.properties 51 | fabric.properties 52 | ### Maven template 53 | target/ 54 | pom.xml.tag 55 | pom.xml.releaseBackup 56 | pom.xml.versionsBackup 57 | pom.xml.next 58 | release.properties 59 | dependency-reduced-pom.xml 60 | buildNumber.properties 61 | .mvn/timing.properties 62 | 63 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 64 | !/.mvn/wrapper/maven-wrapper.jar 65 | ### Eclipse template 66 | 67 | .metadata 68 | bin/ 69 | tmp/ 70 | *.tmp 71 | *.bak 72 | *.swp 73 | *~.nib 74 | local.properties 75 | .settings/ 76 | .project 77 | .classpath 78 | .loadpath 79 | .recommenders 80 | 81 | # External tool builders 82 | .externalToolBuilders/ 83 | 84 | # Locally stored "Eclipse launch configurations" 85 | *.launch 86 | 87 | # PyDev specific (Python IDE for Eclipse) 88 | *.pydevproject 89 | 90 | # CDT-specific (C/C++ Development Tooling) 91 | .cproject 92 | 93 | # Java annotation processor (APT) 94 | .factorypath 95 | 96 | # PDT-specific (PHP Development Tools) 97 | .buildpath 98 | 99 | # sbteclipse plugin 100 | .target 101 | 102 | # Tern plugin 103 | .tern-project 104 | 105 | # TeXlipse plugin 106 | .texlipse 107 | 108 | # STS (Spring Tool Suite) 109 | .springBeans 110 | 111 | # Code Recommenders 112 | .recommenders/ 113 | 114 | # Scala IDE specific (Scala & Java development for Eclipse) 115 | .cache-main 116 | .scala_dependencies 117 | .worksheet 118 | ### Example user template template 119 | ### Example user template 120 | 121 | # IntelliJ project files 122 | .idea 123 | *.iml 124 | out 125 | gen 126 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # 众安链SDK 2 | 目前SDK支持智能合约的部署、调用和查询功能。 3 | 4 | ## 准备 5 | 编译solidity合约请自行完成
6 | (可以通过https://remix.ethereum.org/在线编译或使用solc本地编译
7 | 生成byteCode) 8 | + 编译 test.sol 9 | ``` shell 10 | solc test.sol 11 | ``` 12 | 13 | ## 使用说明(示例) 14 | ### 初始化节点服务 15 | ``` java 16 | NodeSrv nodeSrv = new NodeSrv("http://${HOST}:${PORT}/"); 17 | ``` 18 | 19 | ### 生成账户私钥和地址(支持PrivateKeyECDSA) 20 | ``` java 21 | PrivateKey privKey = PrivateKeyECDSA.Create(); 22 | String privKeyStr = privKey.toHexString(); 23 | String address = privKey.getAddress(); 24 | ``` 25 | 26 | ### 从已有私钥16进制字符初始化私钥对象 27 | ``` java 28 | PrivateKey privKey = new PrivateKeyECDSA("0x6d79264403d667f75caf2f1dca6412c6922b15433b515850c5c00a2aa12010e9"); 29 | String address = privKey.getAddress(); 30 | ``` 31 | ### 查询nonce 32 | 33 | ``` java 34 | PrivateKey privKey; 35 | String address = privKey.getAddress(); 36 | int nonce = nodeSrv.queryNonce(address); 37 | ``` 38 | 39 | ### 查询pending nonce 40 | 41 | ``` java 42 | PrivateKey privKey; 43 | String address = privKey.getAddress(); 44 | int nonce = nodeSrv.queryPendingNonce(address); 45 | ``` 46 | 47 | ### 部署合约 48 | 部署之前编译生成的byteCode到节点 49 | 50 | ``` java 51 | PrivateKey privKey = new PrivateKeyECDSA(${privKeyStr}); 52 | String contractAddr = nodeSrv.deployContract(${byteCode}, Arrays.asList(), privKey, BigInteger.valueOf(nonce)); 53 | ``` 54 | 55 | ### 调用合约(默认同步调用) 56 | ``` java 57 | // event 定义 58 | Event DEPOSIT = new Event("Deposit", 59 | Arrays.asList(), 60 | Arrays.asList(new TypeReference
() { 61 | }, new TypeReference
() { 62 | }, new TypeReference() { 63 | })); 64 | 65 | Function functionDef = new Function("deposit", // function we're calling 66 | Arrays.asList(), // Parameters to pass as Solidity Types 67 | Arrays.asList() 68 | ); 69 | 70 | String resp = nodeSrv.CallContract(BigInteger.valueOf(nonce), 71 | "0xee463ba03224a14706728cbede6abcd76ed4e7cc", 72 | functionDef, //函数接口定义 73 | privKey, 74 | new DemoEventCallBack(DEPOSIT)); 75 | ``` 76 | ### 异步调用合约 77 | ``` 78 | // 在调用callContract方法时指定isSync=false 79 | String resp = nodeSrv.CallContract(BigInteger.valueOf(nonce), 80 | "0xee463ba03224a14706728cbede6abcd76ed4e7cc", 81 | functionDef, //函数接口定义 82 | privKey, 83 | new DemoEventCallBack(DEPOSIT), false); 84 | ``` 85 | ### 编写event 处理逻辑 86 | EventCallBack通过轮询方式监听EVM events 87 | ``` java 88 | import com.rendez.api.EventCallBack; 89 | import lombok.extern.slf4j.Slf4j; 90 | import org.web3j.abi.datatypes.Event; 91 | import org.web3j.abi.datatypes.Type; 92 | import java.util.List; 93 | @Slf4j 94 | public class DemoEventCallBack extends EventCallBack { 95 | 96 | /** 97 | * 98 | * @param pollTime 轮询时间 99 | * @param event 监听事件的格式 100 | */ 101 | public DemoEventCallBack(int pollTime, Event event) { 102 | super(pollTime, event); 103 | } 104 | 105 | public DemoEventCallBack(Event event) { 106 | super(event); 107 | } 108 | 109 | /** 110 | * 业务处理 111 | * @param decodeResult 112 | */ 113 | @Override 114 | public void handleEvent(List decodeResult) { 115 | log.info("decoded result" + decodeResult); 116 | log.info("start to Process Data"); 117 | } 118 | } 119 | ``` 120 | 121 | ### 查询合约 122 | ``` java 123 | PrivateKey privKey = new PrivateKeyECDSA(${privKeyStr}); 124 | String address = privKey.getAddress(); 125 | int nonce = nodeSrv.queryNonce(address); 126 | log.info("nonce {}" , nonce); 127 | Function functionDef = new Function("queryInfo", Arrays.asList(), Arrays.asList(new TypeReference() { 128 | })); 129 | List resp = nodeSrv.queryContract(BigInteger.valueOf(nonce), contractAddress, functionDef, privKey); 130 | log.info("resp {}", resp); 131 | ``` 132 | 133 | ### 根据块高度查询合约 134 | ``` java 135 | PrivateKey privKey = new PrivateKeyECDSA(${privKeyStr}); 136 | String address = privKey.getAddress(); 137 | int nonce = nodeSrv.queryNonce(address); 138 | int height = 100; 139 | log.info("nonce {}" , nonce); 140 | Function functionDef = new Function("queryInfo", Arrays.asList(), Arrays.asList(new TypeReference() { 141 | })); 142 | List resp = nodeSrv.queryContractByHeight(BigInteger.valueOf(nonce), contractAddress, functionDef, privKey, BigInteger.valueOf(height)); 143 | log.info("resp {}", resp); 144 | ``` 145 | 146 | ### 根据块高度查询交易hash列表 147 | ``` java 148 | int height = 100; 149 | String[] resp = nodeSrv.queryTransactionHashsByHeight(BigInteger.valueOf(height)); 150 | log.info("resp {}", resp); 151 | ``` 152 | 153 | ### 根据交易hash查询交易数据 154 | ``` java 155 | String txhash = ""; 156 | RawTransactionData resp = nodeSrv.queryRawTransactionByHash(txhash); 157 | log.info("resp {}", resp); 158 | ``` 159 | 160 | 161 | ### TransactionUtil类 162 | #### decodeTxMsg 163 | ```java 164 | static SignedRawTransaction decodeTxMsg(String txMsg) 165 | ``` 166 | decode链上交易, 返回交易详细信息 167 | 168 | ### NodeSrv类 169 | #### queryNonce 170 | ```java 171 | Integer queryNonce(String address) 172 | ``` 173 | 根据账户地址查询nonce 174 | 175 | #### queryRawTransactionByHash 176 | ```java 177 | RawTransactionData queryRawTransactionByHash(String txhash) 178 | ``` 179 | 根据交易hash查询交易数据 180 | 181 | #### queryTransactionHashsByHeight 182 | ```java 183 | String[] queryTransactionHashsByHeight(BigInteger height) 184 | ``` 185 | 根据块高度查询交易hash列表 186 | 187 | #### deployContract 188 | ```java 189 | String deployContract(String binaryCode, List constructorParameters, PrivateKey privKey, BigInteger nonce) 190 | ``` 191 | 部署合约,返回合约地址 192 | 193 | #### callContractEvm 194 | ```java 195 | String callContractEvm(BigInteger nonce, String contractAddress, Function function, PrivateKey privKey, EventCallBack callBack, boolean isSyncCall) 196 | ``` 197 | 调用evm 合约 198 | 199 | #### queryContract 200 | ```java 201 | List queryContract(BigInteger nonce, String contractAddress, Function function, PrivateKey privKey) 202 | ``` 203 | 查询合约 204 | 205 | #### queryContractByHeight 206 | ```java 207 | List queryContractByHeight(BigInteger nonce, String contractAddress, Function function, PrivateKey privKey, BigInteger height) 208 | ``` 209 | 指定区块高度查询合约 210 | 211 | #### queryContractWithSig 212 | ```java 213 | queryContractWithSig(BigInteger nonce, String contractAddress, Function function, Signature sig) 214 | ``` 215 | 签名查询合约 216 | 217 | #### queryReceiptRaw 218 | ```java 219 | TransactionReceipt queryReceiptRaw(String txHash) 220 | ``` 221 | 查询交易执行receipt 222 | 223 | #### queryReceipt 224 | ```java 225 | List queryReceipt(String txHash) 226 | ``` 227 | 查询交易执行event log 228 | 229 | 230 | ## 贡献代码 231 | 232 | 如果你有任何问题,可以提交[issue](https://github.com/dappledger/AnnChain/issues). 233 |
234 | 如果你想贡献代码, 可以fork本仓库, 提交 [pull request](https://github.com/dappledger/ann-java-sdk/pulls) ,维护者将会细心review 你的代码,若一切ok,则会合并到主仓库 235 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.za.rendez.sdk 8 | sdk 9 | 1.4.4-SNAPSHOT 10 | 11 | 4.2.0 12 | 13 | 14 | 15 | releases 16 | releases 17 | http://10.139.32.180:8080/nexus/content/repositories/releases/ 18 | 19 | 20 | snapshots 21 | snapshots 22 | http://10.139.32.180:8080/nexus/content/repositories/snapshots/ 23 | 24 | 25 | 26 | 27 | 28 | 29 | true 30 | 31 | bintray-ethereum-maven 32 | bintray 33 | https://dl.bintray.com/ethereum/maven 34 | 35 | 36 | 37 | 38 | 39 | org.web3j 40 | abi 41 | 3.4.0 42 | 43 | 44 | 45 | org.web3j 46 | core 47 | ${web3j.version} 48 | 49 | 50 | 51 | com.google.protobuf 52 | protobuf-java 53 | 3.3.1 54 | 55 | 56 | 57 | junit 58 | junit 59 | 4.12 60 | test 61 | 62 | 63 | 64 | org.web3j 65 | crypto 66 | ${web3j.version} 67 | 68 | 69 | 70 | org.slf4j 71 | 72 | slf4j-api 73 | 1.7.25 74 | 75 | 76 | 77 | com.squareup.retrofit2 78 | retrofit 79 | 2.0.2 80 | 81 | 82 | com.squareup.retrofit2 83 | converter-gson 84 | 2.0.2 85 | 86 | 87 | org.projectlombok 88 | lombok 89 | 1.16.20 90 | 91 | 92 | 93 | ch.qos.logback 94 | logback-core 95 | 1.1.7 96 | 97 | 98 | ch.qos.logback 99 | logback-access 100 | 1.1.7 101 | 102 | 103 | ch.qos.logback 104 | logback-classic 105 | 1.1.7 106 | 107 | 108 | 109 | io.reactivex.rxjava2 110 | rxjava 111 | 2.1.16 112 | 113 | 114 | 115 | commons-lang 116 | commons-lang 117 | 2.3 118 | 119 | 120 | 121 | com.typesafe 122 | config 123 | 1.3.3 124 | 125 | 126 | 127 | org.apache.commons 128 | commons-lang3 129 | 3.7 130 | 131 | 132 | 133 | org.web3j 134 | rlp 135 | ${web3j.version} 136 | 137 | 138 | 139 | com.github.jtendermint 140 | crypto 141 | 0.0.1 142 | 143 | 144 | 145 | org.ethereum 146 | ethereumj-core 147 | 1.5.0-RELEASE 148 | 149 | 150 | com.madgag.spongycastle 151 | core 152 | 1.58.0.0 153 | 154 | 155 | 156 | org.apache.commons 157 | commons-collections4 158 | 4.2 159 | 160 | 161 | org.bouncycastle 162 | bcprov-jdk15on 163 | 1.60 164 | 165 | 166 | org.bouncycastle 167 | bcpkix-jdk15on 168 | 1.60 169 | 170 | 171 | 172 | 173 | 174 | 175 | org.apache.maven.plugins 176 | maven-compiler-plugin 177 | 3.1 178 | 179 | 1.8 180 | 1.8 181 | 182 | 183 | 184 | maven-source-plugin 185 | 186 | 187 | attach-sources 188 | verify 189 | 190 | jar-no-fork 191 | test-jar-no-fork 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | src/main/resources 201 | 202 | **/** 203 | 204 | 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/AbiTool.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api; 2 | 3 | import org.ethereum.solidity.Abi; 4 | import org.web3j.abi.FunctionReturnDecoder; 5 | import org.web3j.abi.TypeReference; 6 | import org.web3j.abi.datatypes.*; 7 | import org.web3j.abi.datatypes.generated.AbiTypes; 8 | import org.web3j.abi.datatypes.generated.Uint256; 9 | import org.web3j.crypto.Hash; 10 | import org.web3j.utils.Numeric; 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | public class AbiTool { 16 | 17 | private Abi abiDef; 18 | 19 | /** 20 | * 初始化abiDefinition 21 | * @param abiDefinition 22 | */ 23 | public AbiTool(String abiDefinition){ 24 | abiDef = Abi.fromJson(abiDefinition); 25 | } 26 | 27 | /** 28 | * 根据abiCode和encodedFunc解析function 29 | * @param encodedFunc 30 | * @return 31 | */ 32 | public Function decodeFunction(String encodedFunc){ 33 | if (!encodedFunc.startsWith("0x")){ 34 | encodedFunc = "0x" + encodedFunc; 35 | } 36 | 37 | String methodId = encodedFunc.substring(0, 10); 38 | String rawInput = encodedFunc.substring(10); 39 | for (Abi.Entry entry : this.abiDef){ 40 | if (entry.type.name().equals("function") && buildMethodId(entry.formatSignature()).equals(methodId)) { 41 | List> inputTypeRefer = findInputTypeReference(entry.inputs); 42 | List inputs = decodeInputs(rawInput, inputTypeRefer); 43 | Function func = new Function(entry.name, inputs, Collections.emptyList()); 44 | return func; 45 | } 46 | } 47 | return null; 48 | } 49 | 50 | 51 | /** 52 | * 根据参数类型名称解析原始类型 53 | * @param typeName 参数类型, eg: uint256[], uint8[], address 54 | * @return 55 | */ 56 | private static TypeReference parseTypeReference(String typeName) { 57 | if (typeName.contains("[")) return parseArrayTypeRefer(typeName); 58 | if ("bool".equals(typeName)) return new TypeReference() {}; 59 | if ("address".equals(typeName)) return new TypeReference
() {}; 60 | if ("string".equals(typeName)) return new TypeReference() {}; 61 | if ("bytes".equals(typeName)) return new TypeReference() {}; 62 | Class cl = AbiTypes.getType(typeName); 63 | return TypeReference.create(cl); 64 | } 65 | 66 | 67 | /** 68 | * todo 目前默认uint256, 其他类型数组如何处理? 69 | * @param typeName 70 | * @return 71 | */ 72 | private static TypeReference parseArrayTypeRefer(String typeName) { 73 | int idx1 = typeName.indexOf("["); 74 | int idx2 = typeName.indexOf("]", idx1); 75 | TypeReference elementTypeRefer = parseTypeReference(typeName.substring(0, idx1)); 76 | if (idx1 + 1 == idx2) { 77 | return new TypeReference>(){}; 78 | }else{ 79 | int staticArraySize =Integer.parseInt(typeName.substring(idx1+1, idx2)); 80 | return new TypeReference.StaticArrayTypeReference>(staticArraySize) {}; 81 | } 82 | } 83 | 84 | 85 | /** 86 | * 解析inputs 87 | * @param rawInput 88 | * @param inputTypeRefer1 89 | * @return 90 | */ 91 | private List decodeInputs(String rawInput, List> inputTypeRefer1){ 92 | Function func = new Function("", Collections.emptyList(), inputTypeRefer1); 93 | List> inputTypeRefer2 = func.getOutputParameters(); 94 | List inputs = FunctionReturnDecoder.decode( 95 | rawInput, inputTypeRefer2); 96 | return inputs; 97 | } 98 | 99 | 100 | /** 101 | * 获取input类型 102 | * @param inputs 103 | * @return 104 | */ 105 | private List> findInputTypeReference(List inputs){ 106 | List> inputTypeRefer = new ArrayList<>(); 107 | for (Abi.Entry.Param param: inputs){ 108 | inputTypeRefer.add(parseTypeReference(param.type.getName())); 109 | } 110 | return inputTypeRefer; 111 | } 112 | 113 | 114 | private static String buildMethodId(String methodSignature) { 115 | byte[] input = methodSignature.getBytes(); 116 | byte[] hash = Hash.sha3(input); 117 | return Numeric.toHexString(hash).substring(0, 10); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/CryptoUtil.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api; 2 | 3 | import com.genesis.api.crypto.PrivateKey; 4 | import com.genesis.api.crypto.Signature; 5 | import com.genesis.api.util.ByteUtil; 6 | import org.web3j.crypto.*; 7 | import org.web3j.utils.Numeric; 8 | import java.io.ByteArrayOutputStream; 9 | import java.math.BigInteger; 10 | 11 | public class CryptoUtil { 12 | 13 | /** 14 | * 生成签名 15 | * @param rtx 原始交易 16 | * @param privateKey 私钥 17 | * @return 签名 18 | */ 19 | public static Signature generateSignature(RawTransaction rtx, PrivateKey privateKey){ 20 | return privateKey.sign(TransactionUtil.encode(rtx)); 21 | } 22 | 23 | 24 | 25 | /** 26 | * 生成交易哈希 27 | * @param message 交易信息 28 | * @return txHash 29 | */ 30 | public static String txHash(byte[] message){ 31 | String txHash = Numeric.toHexString(Hash.sha3((message))); 32 | return txHash; 33 | } 34 | 35 | 36 | /** 37 | * note: 修改jtendermint.go-wire, varint作为varuint处理 38 | * @param inputbytes 39 | * @return 40 | */ 41 | private static byte[] wireWithVarUint(byte[] inputbytes) { 42 | try { 43 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 44 | Throwable var2 = null; 45 | 46 | byte[] var9; 47 | try { 48 | if (inputbytes == null) { 49 | inputbytes = new byte[0]; 50 | } 51 | 52 | long length = inputbytes.length; 53 | BigInteger bt = BigInteger.valueOf(length); 54 | byte[] varint = ByteUtil.BigIntToRawBytes(bt); 55 | long varintLength = (long)varint.length; 56 | byte[] varintPrefix = ByteUtil.BigIntToRawBytes(BigInteger.valueOf(varintLength)); 57 | bos.write(varintPrefix); 58 | if (inputbytes.length > 0) { 59 | bos.write(varint); 60 | bos.write(inputbytes); 61 | } 62 | 63 | var9 = bos.toByteArray(); 64 | } catch (Throwable var19) { 65 | var2 = var19; 66 | throw var19; 67 | } finally { 68 | if (bos != null) { 69 | if (var2 != null) { 70 | try { 71 | bos.close(); 72 | } catch (Throwable var18) { 73 | var2.addSuppressed(var18); 74 | } 75 | } else { 76 | bos.close(); 77 | } 78 | } 79 | 80 | } 81 | 82 | return var9; 83 | } catch (Exception var21) { 84 | return new byte[0]; 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/EventCallBack.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api; 2 | 3 | import lombok.Data; 4 | import org.web3j.abi.FunctionReturnDecoder; 5 | import org.web3j.abi.datatypes.Event; 6 | import org.web3j.abi.datatypes.Type; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 用户自定义处理event 的callback 12 | * 13 | * @param 14 | */ 15 | @Data 16 | public abstract class EventCallBack { 17 | 18 | //默认超时时间 30s 19 | private static final int DEFAULT_WAITE = 30; // poll 10s 20 | 21 | //超时时间 22 | private int pollTime; 23 | 24 | //事件格式定义 25 | private Event event; 26 | 27 | public EventCallBack(int pollTime, Event event) { 28 | this.pollTime = pollTime; 29 | this.event = event; 30 | } 31 | 32 | public EventCallBack(Event event) { 33 | this.event = event; 34 | this.pollTime = DEFAULT_WAITE; 35 | } 36 | 37 | final void handleLogs(String logData) { 38 | List decodeResult = FunctionReturnDecoder.decode(logData, event.getNonIndexedParameters()); 39 | handleEvent(decodeResult); 40 | } 41 | 42 | /** 43 | * 具体处理业务逻辑代码 44 | * @param decodeResult 45 | */ 46 | public abstract void handleEvent(List decodeResult); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/GenesisUtil.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.math.BigInteger; 7 | import java.nio.ByteBuffer; 8 | import java.util.Arrays; 9 | 10 | /** 11 | * 众安链一些编码和工具的java实现 12 | * 参考 众安链:rendezvous 13 | */ 14 | public class GenesisUtil { 15 | public static long DynamicBytesToLong(byte[] bs){ 16 | ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); 17 | for (int i= 0; i< (Long.BYTES- bs.length);i++){ 18 | buffer.put((byte)0); 19 | } 20 | buffer.put(bs,0, bs.length ); 21 | buffer.flip();//need flip 22 | return buffer.getLong(); 23 | } 24 | 25 | public static int DynamicBytesToInt(byte[] bs){ 26 | ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); 27 | for (int i= 0; i< (Integer.BYTES- bs.length);i++){ 28 | buffer.put((byte)0); 29 | } 30 | buffer.put(bs,0, bs.length ); 31 | buffer.flip();//need flip 32 | return buffer.getInt(); 33 | } 34 | /** 35 | * int转为4字节表示 36 | * @param i 37 | * @return 38 | */ 39 | public static byte[] ToInt32Bytes(int i){ 40 | return ByteBuffer.allocate(4).putInt(i).array(); 41 | } 42 | 43 | /** 44 | * int包含几个有效字节 45 | * @param i 46 | * @return 47 | */ 48 | public static int varIntSize(int i){ 49 | if (i == 0){ 50 | return 0; 51 | } 52 | if (i < 1<<8){ 53 | return 1; 54 | } 55 | 56 | if (i < 1<<16){ 57 | return 2; 58 | } 59 | if (i < 1<<24){ 60 | return 3; 61 | } 62 | return 4; 63 | } 64 | 65 | /** 66 | * 将data字节数组的长度有效字节长度, 长度有效字节, data依次写入outputStream 67 | * 参考 众安链:rendezvous.serialtool.go 68 | * @param output 69 | * @param data 70 | * @throws IOException 71 | */ 72 | public static void writeToStream(ByteArrayOutputStream output, byte[] data) throws IOException{ 73 | int len = data.length; 74 | int lenSize = varIntSize(len); // len实际占用几个字节 75 | byte[] lenBytes = BigInteger.valueOf(len).toByteArray(); // // len的字节表示 76 | if (lenBytes[0] == 0) { 77 | // 转为unsigned int 78 | byte[] tmp = new byte[lenBytes.length - 1]; 79 | System.arraycopy(lenBytes, 1, tmp, 0, tmp.length); 80 | lenBytes = tmp; 81 | } 82 | byte[] slice = Arrays.copyOfRange(lenBytes, lenBytes.length - lenSize, lenBytes.length); // 去掉空的0字节,只保留有效数字 83 | output.write(lenSize); 84 | output.write(slice); 85 | output.write(data); 86 | } 87 | 88 | /** 89 | * 从inputstream中根据data长度的字节表示读取出data 90 | * 参考 众安链:rendezvous.serialtool.go 91 | * @param input 92 | * @throws IOException 93 | */ 94 | public static byte[] readFromInputStream(ByteArrayInputStream input) throws IOException{ 95 | int dataLenSize = input.read(); // data长度占几个字节 96 | byte[] dataLenByteSlice = new byte[dataLenSize]; // data长度的字节表示 97 | input.read(dataLenByteSlice); 98 | int txLen = DynamicBytesToInt(dataLenByteSlice); 99 | byte[] data = new byte[txLen]; // data 100 | input.read(data); 101 | return data; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/LogObserver.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api; 2 | 3 | import io.reactivex.Observer; 4 | import io.reactivex.disposables.Disposable; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.bouncycastle.util.encoders.Hex; 7 | import org.ethereum.vm.LogInfo; 8 | 9 | import java.util.List; 10 | 11 | @Slf4j 12 | public class LogObserver implements Observer { 13 | //1s 抓一次 14 | private static final long Gape = 1000; 15 | 16 | @Override 17 | public void onSubscribe(Disposable d) { 18 | 19 | } 20 | 21 | @Override 22 | public void onNext(QueryRecTask task) { 23 | log.debug("get task {}", task); 24 | long end = System.currentTimeMillis() + task.getEventCallBack().getPollTime() * 1000; 25 | while (System.currentTimeMillis() < end) { 26 | try { 27 | List recp = task.getNodeSrv().queryReceipt(task.getTxHash()); 28 | if (recp != null) { 29 | recp.forEach(log -> task.getEventCallBack().handleLogs(Hex.toHexString(log.getData()))); 30 | return; 31 | } 32 | } catch (Exception e) { 33 | log.warn("LogObserver queryReceipt", e); 34 | } 35 | 36 | try { 37 | // always sleep if receipt not found 38 | Thread.sleep(Gape); 39 | } catch (InterruptedException ignored) { 40 | } 41 | } 42 | 43 | log.warn("time out for task {}", task); 44 | 45 | } 46 | 47 | @Override 48 | public void onError(Throwable e) { 49 | log.error("error on observe ", e); 50 | } 51 | 52 | @Override 53 | public void onComplete() { 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/NodeApi.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api; 2 | 3 | import java.math.BigInteger; 4 | 5 | import com.genesis.api.bean.genesis.BaseResp; 6 | import com.genesis.api.bean.genesis.ResultBlock; 7 | import com.genesis.api.bean.genesis.ResultCommit; 8 | import com.genesis.api.bean.genesis.ResultQuery; 9 | import retrofit2.Call; 10 | import retrofit2.http.*; 11 | 12 | public interface NodeApi { 13 | 14 | /** 15 | * 同步广播交易 16 | * @param tx 17 | * @return 18 | */ 19 | @POST("broadcast_tx_commit") 20 | @FormUrlEncoded 21 | Call> broadcastTxCommit(@Field("tx") String tx); 22 | 23 | 24 | 25 | /** 26 | * 异步广播交易 27 | * @param tx 28 | * @return 29 | */ 30 | @POST("broadcast_tx_async") 31 | @FormUrlEncoded 32 | Call> broadcastTxAsync(@Field("tx") String tx); 33 | 34 | /** 35 | * 查询 36 | * @param query 37 | * @return 38 | */ 39 | @GET("query") 40 | Call> query(@Query("query") String query); 41 | 42 | 43 | /** 44 | * 查询交易 45 | * @param transaction 46 | * @return 47 | */ 48 | @GET("transaction") 49 | Call> transaction(@Query("tx") String tx); 50 | 51 | /** 52 | * 查询区块 53 | * @param block 54 | * @return 55 | */ 56 | @GET("block") 57 | Call> block(@Query("height") BigInteger height); 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/NodeSrv.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api; 2 | 3 | import com.genesis.api.bean.exception.BaseException; 4 | import com.genesis.api.bean.exception.ErrCode; 5 | import com.genesis.api.bean.model.DeployContractResult; 6 | import com.genesis.api.bean.model.QueryBlockTransactionHashs; 7 | import com.genesis.api.bean.model.QueryKVResult; 8 | import com.genesis.api.bean.model.QueryPrefixKVs; 9 | import com.genesis.api.bean.model.QueryTransaction; 10 | import com.genesis.api.bean.model.QueryTransactionPayload; 11 | import com.genesis.api.bean.model.QueryTransactionReceipt; 12 | import com.genesis.api.bean.model.RawTransactionData; 13 | import com.genesis.api.bean.model.SendTransactionResult; 14 | import com.genesis.api.bean.model.TransactionKVTx; 15 | import com.genesis.api.bean.genesis.*; 16 | import com.genesis.api.bean.genesis.ResultBlock.TxData; 17 | import com.genesis.api.crypto.PrivateKey; 18 | import com.genesis.api.crypto.Signature; 19 | import com.genesis.api.crypto.SignatureECDSA; 20 | import io.reactivex.schedulers.Schedulers; 21 | import io.reactivex.subjects.PublishSubject; 22 | import io.reactivex.subjects.Subject; 23 | import lombok.Data; 24 | import lombok.extern.slf4j.Slf4j; 25 | import okhttp3.OkHttpClient; 26 | import org.apache.commons.lang.StringUtils; 27 | import org.ethereum.util.RLP; 28 | import org.ethereum.util.RLP.LList; 29 | import org.ethereum.vm.LogInfo; 30 | import org.spongycastle.util.encoders.Hex; 31 | import org.web3j.abi.FunctionReturnDecoder; 32 | import org.web3j.abi.datatypes.Function; 33 | import org.web3j.abi.datatypes.Type; 34 | import org.web3j.crypto.*; 35 | import org.web3j.rlp.RlpDecoder; 36 | import org.web3j.rlp.RlpList; 37 | import org.web3j.rlp.RlpString; 38 | import org.web3j.utils.Numeric; 39 | 40 | import retrofit2.Response; 41 | import retrofit2.Retrofit; 42 | import retrofit2.converter.gson.GsonConverterFactory; 43 | 44 | import java.io.IOException; 45 | import java.math.BigInteger; 46 | import java.security.SignatureException; 47 | import java.util.Arrays; 48 | import java.util.List; 49 | import java.util.concurrent.TimeUnit; 50 | 51 | 52 | @Data 53 | @Slf4j 54 | public class NodeSrv { 55 | private Subject sb; 56 | private NodeApi stub; 57 | 58 | public NodeSrv(String url) { 59 | OkHttpClient okClient = new OkHttpClient.Builder() 60 | .readTimeout(120, TimeUnit.SECONDS) 61 | .connectTimeout(30, TimeUnit.SECONDS) 62 | .build(); 63 | Retrofit retrofit = new Retrofit.Builder() 64 | .baseUrl(url) 65 | .addConverterFactory(GsonConverterFactory.create()) 66 | .client(okClient) 67 | .build(); 68 | stub = retrofit.create(NodeApi.class); 69 | 70 | sb = PublishSubject.create(); 71 | 72 | sb.subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).subscribe(new LogObserver()); 73 | } 74 | 75 | /** 76 | * 查询nonce 77 | * 78 | * @param address 账户地址 79 | * @return nonce 80 | * @throws IOException 81 | */ 82 | public Integer queryNonce(String address) throws IOException { 83 | Response> httpResp = stub.query(QueryType.Nonce.padd(address)).execute(); 84 | handleRespQuery(httpResp); 85 | String nonceStr = httpResp.body().getResult().getResult().getData(); 86 | if (StringUtils.isBlank(nonceStr)) { 87 | throw new BaseException("can not obtain nonce for address " + address); 88 | } 89 | byte[] rlpEncoded = Numeric.hexStringToByteArray(nonceStr); 90 | RlpList decode = RlpDecoder.decode(rlpEncoded); 91 | String hexValue = ((RlpString) decode.getValues().get(0)).asString(); 92 | String cleanHexPrefix = Numeric.cleanHexPrefix(hexValue); 93 | return StringUtils.isBlank(cleanHexPrefix) ? 0 : Integer.parseInt(cleanHexPrefix, 16); 94 | } 95 | 96 | /** 97 | * 查询pending nonce 98 | * 99 | * @param address 账户地址 100 | * @return pending nonce 101 | * @throws IOException 102 | */ 103 | public Integer queryPendingNonce(String address) throws IOException { 104 | Response> httpResp = stub.query(QueryType.PendingNonce.padd(address)).execute(); 105 | handleRespQuery(httpResp); 106 | String nonceStr = httpResp.body().getResult().getResult().getData(); 107 | if (StringUtils.isBlank(nonceStr)) { 108 | throw new BaseException("can not obtain pending nonce for address " + address); 109 | } 110 | byte[] rlpEncoded = Numeric.hexStringToByteArray(nonceStr); 111 | RlpList decode = RlpDecoder.decode(rlpEncoded); 112 | String hexValue = ((RlpString) decode.getValues().get(0)).asString(); 113 | String cleanHexPrefix = Numeric.cleanHexPrefix(hexValue); 114 | return StringUtils.isBlank(cleanHexPrefix) ? 0 : Integer.parseInt(cleanHexPrefix, 16); 115 | } 116 | 117 | /** 118 | * 根据hash查询tx 119 | * 120 | * @param hash 交易hash 121 | * @return tx 122 | * @throws IOException 123 | */ 124 | public QueryTransaction queryTxByHash(String hashByte) throws IOException { 125 | Response> httpResp = stub.transaction(hashByte).execute(); 126 | handleRespQuery(httpResp); 127 | BaseResp resp = httpResp.body(); 128 | log.debug(resp.toString()); 129 | if (resp.getResult().getResult().getData() != null) { 130 | byte[] rlp = Numeric.hexStringToByteArray(resp.getResult().getResult().getData()); 131 | QueryTransaction res = new QueryTransaction(rlp); 132 | return res; 133 | } else { 134 | return null; 135 | } 136 | } 137 | 138 | public RawTransactionData queryRawTransactionByHash(String hashByte) throws IOException { 139 | Response> httpResp = stub.transaction(hashByte).execute(); 140 | handleRespQuery(httpResp); 141 | BaseResp resp = httpResp.body(); 142 | log.debug(resp.toString()); 143 | if (resp.getResult().getResult().getData() != null) { 144 | byte[] rlp = Numeric.hexStringToByteArray(resp.getResult().getResult().getData()); 145 | QueryTransaction res = new QueryTransaction(rlp); 146 | RawTransactionData txdata = new RawTransactionData(res.getRawTransaction()); 147 | return txdata; 148 | } else { 149 | return null; 150 | } 151 | } 152 | 153 | /** 154 | * 根据区块高度查询txhashs 155 | * 156 | * @param height 区块高度 157 | * @return TransactionHashs 158 | * @throws IOException 159 | */ 160 | public QueryBlockTransactionHashs queryTransactionHashsByHeight(BigInteger height) throws IOException{ 161 | Response> httpResp = stub.block(height).execute(); 162 | handleRespBlock(httpResp); 163 | BaseResp resp = httpResp.body(); 164 | if (resp.getResult().getBlock() != null) { 165 | long total = resp.getResult().getBlock().getHeader().getNum_txs(); 166 | TxData blockdata = resp.getResult().getBlock().getData(); 167 | if (total != blockdata.getExtxs().length + blockdata.getTxs().length) { 168 | return null; 169 | } 170 | 171 | String[] txs = blockdata.getTxs(); 172 | String[] extxs = blockdata.getExtxs(); 173 | int base = txs.length; 174 | int ex = extxs.length; 175 | String[] hashs = new String[base+ex]; 176 | for (int i = 0; i < base; i++) { 177 | byte[] txByte = Numeric.hexStringToByteArray(txs[i]); 178 | byte[] txhash = Hash.sha3(txByte); 179 | hashs[i] = Numeric.toHexString(txhash); 180 | } 181 | 182 | for (int i = 0; i queryReceipt(String txHash) throws IOException { 202 | QueryTransactionReceipt receipt = queryReceiptRaw(txHash); 203 | if (receipt != null) { 204 | return receipt.getLogInfoList(); 205 | } else { 206 | return null; 207 | } 208 | } 209 | 210 | /** 211 | * 212 | * 查询交易执行receipt 213 | * 214 | * @param txHash 交易hash 215 | * @return 216 | * @throws IOException 217 | */ 218 | public QueryTransactionReceipt queryReceiptRaw(String txHash) throws IOException { 219 | Response> httpRes = stub.query(QueryType.Receipt.padd(txHash)).execute(); 220 | handleRespQuery(httpRes); 221 | BaseResp resp = httpRes.body(); 222 | log.debug(resp.toString()); 223 | QueryTransactionReceipt res = new QueryTransactionReceipt(); 224 | if (resp.getResult().getResult().getData() != null) { 225 | byte[] rlp = Numeric.hexStringToByteArray(resp.getResult().getResult().getData()); 226 | res = new QueryTransactionReceipt(rlp); 227 | } else { 228 | return null; 229 | } 230 | 231 | QueryTransaction tx = queryTxByHash(txHash); 232 | if (tx == null) { 233 | return null; 234 | } 235 | 236 | RawTransactionData txdata = new RawTransactionData(tx.getRawTransaction()); 237 | 238 | String fromAddr = new String(); 239 | try { 240 | fromAddr = getFromAddress(txdata); 241 | }catch(Exception e){ 242 | e.printStackTrace(); 243 | } 244 | 245 | if (fromAddr.indexOf("0x") != -1){ 246 | fromAddr = fromAddr.substring(2); 247 | } 248 | 249 | res.setBlockHash(tx.getBlockHash()); 250 | res.setBlockHeight(tx.getBlockHeight()); 251 | res.setTransactionIndex(tx.getTransactionIndex()); 252 | res.setFrom(Hex.decode(fromAddr)); 253 | res.setTimestamp(tx.getTimestamp()); 254 | res.setTo(txdata.getTo()); 255 | 256 | return res; 257 | } 258 | 259 | private String getFromAddress(RawTransactionData txdata) throws SignatureException{ 260 | RawTransaction rtx = RawTransaction.createTransaction(txdata.getNonce(), txdata.getGasprice(), txdata.getGas(), Hex.toHexString(txdata.getTo()), txdata.getValue(), Hex.toHexString(txdata.getInput())); 261 | Signature sig = new SignatureECDSA(txdata.getV().byteValue(), txdata.getR().toByteArray(), txdata.getS().toByteArray()); 262 | String txMsg = Numeric.toHexString(TransactionUtil.encodeWithSig(rtx, sig)); 263 | SignedRawTransaction signedRawTransaction = (SignedRawTransaction) TransactionDecoder.decode(txMsg); 264 | String signerAddress = signedRawTransaction.getFrom(); 265 | return signerAddress; 266 | } 267 | 268 | 269 | /** 270 | * 用秘钥查询合约 271 | * @param nonce 272 | * @param contractAddress 合约地址 273 | * @param function 合约方法 274 | * @param privateKey 秘钥 275 | * @return 276 | * @throws IOException 277 | */ 278 | public List queryContract(BigInteger nonce, String contractAddress, Function function, PrivateKey privateKey) throws IOException{ 279 | RawTransaction tx = TransactionUtil.createCallContractTransaction(nonce, contractAddress, function); 280 | Signature sig = CryptoUtil.generateSignature(tx, privateKey); 281 | return queryContractWithSig(nonce, contractAddress, function, BigInteger.valueOf(0), sig); 282 | } 283 | 284 | /** 285 | * 签名查询合约 286 | * @param nonce 287 | * @param contractAddress 合约地址 288 | * @param function 合约方法 289 | * @param sig 签名 290 | * @return 291 | * @throws IOException 292 | */ 293 | public List queryContractWithSig(BigInteger nonce, String contractAddress, Function function, BigInteger height, Signature sig) throws IOException { 294 | RawTransaction tx = TransactionUtil.createCallContractTransaction(nonce, contractAddress, function); 295 | byte[] message = TransactionUtil.encodeWithSig(tx, sig); 296 | 297 | byte[] h = Numeric.toBytesPadded(height,8); 298 | 299 | byte[] par = new byte[message.length+h.length]; 300 | System.arraycopy(message, 0, par, 0, message.length); 301 | System.arraycopy(h, 0, par, message.length, h.length); 302 | 303 | 304 | 305 | Response> httpRes = stub.query(QueryType.ContractByHeight.paddByte(par)).execute(); 306 | handleRespQuery(httpRes); 307 | BaseResp resp = httpRes.body(); 308 | if (resp.getResult().getResult().getData() != null) { 309 | String respData = resp.getResult().getResult().getData(); 310 | byte[] rlpEncoded = Numeric.hexStringToByteArray(respData); 311 | List someTypes = FunctionReturnDecoder.decode( 312 | Numeric.toHexString(rlpEncoded), function.getOutputParameters()); 313 | return someTypes; 314 | } else { 315 | return null; 316 | } 317 | } 318 | 319 | /** 320 | * 321 | * 根据高度查询 合约 322 | * 323 | * @param nonce 324 | * @param contractAddress 合约地址 325 | * @param function 合约函数定义 326 | * @param privateKey 327 | * @return 交易hash 328 | * @throws IOException 329 | */ 330 | public List queryContractByHeight(BigInteger nonce, String contractAddress, Function function, PrivateKey privateKey, BigInteger height) throws IOException{ 331 | RawTransaction tx = TransactionUtil.createCallContractTransaction(nonce, contractAddress, function); 332 | Signature sig = CryptoUtil.generateSignature(tx, privateKey); 333 | return queryContractWithSig(nonce, contractAddress, function, height, sig); 334 | } 335 | 336 | 337 | /** 338 | * 339 | * 调用evm 合约 340 | * 341 | * @param nonce 342 | * @param contractAddress 合约地址 343 | * @param function 合约函数定义 344 | * @param privateKey 345 | * @return 交易hash 346 | * @throws IOException 347 | */ 348 | public String callContractEvm(BigInteger nonce, String contractAddress, Function function, PrivateKey privateKey) throws IOException { 349 | return callContractEvm(nonce, contractAddress, function, privateKey, null); 350 | } 351 | 352 | 353 | public String callContractEvm(BigInteger nonce, String contractAddress, Function function, PrivateKey privateKey, EventCallBack callBack) throws IOException { 354 | // default call sync 355 | return callContractEvm(nonce, contractAddress, function, privateKey, callBack, true); 356 | } 357 | 358 | /** 359 | * 360 | * 调用evm 合约 361 | * 362 | * @param nonce nonce 363 | * @param contractAddress 合约地址 364 | * @param function 函数定义 365 | * @param privateKey 秘钥 366 | * @param callBack 回调函数 367 | * @param isSyncCall 是否同步调用 368 | * @return txHash 交易哈希 369 | * @throws IOException 370 | */ 371 | public String callContractEvm(BigInteger nonce, String contractAddress, Function function, PrivateKey privateKey, EventCallBack callBack, boolean isSyncCall) throws IOException{ 372 | RawTransaction tx = TransactionUtil.createCallContractTransaction(nonce, contractAddress, function); 373 | Signature sig = CryptoUtil.generateSignature(tx, privateKey); 374 | return callContractEvmWithSig(nonce, contractAddress, function, sig, callBack, isSyncCall); 375 | } 376 | 377 | 378 | public String callContractEvmWithSig(BigInteger nonce, String contractAddress, Function function, Signature sig, EventCallBack callBack) throws IOException { 379 | // default call sync 380 | return callContractEvmWithSig(nonce, contractAddress, function, sig, callBack, true); 381 | } 382 | 383 | /** 384 | * 385 | * 使用生成好的签名调用evm 合约 386 | * 387 | * @param nonce nonce 388 | * @param contractAddress 合约地址 389 | * @param function 函数定义 390 | * @param sig 交易签名 391 | * @param callBack 回调函数 392 | * @param isSyncCall 是否同步调用 393 | * @return txHash 交易哈希 394 | * @throws IOException 395 | */ 396 | public String callContractEvmWithSig(BigInteger nonce, String contractAddress, Function function, Signature sig, EventCallBack callBack, boolean isSyncCall) throws IOException { 397 | RawTransaction tx = TransactionUtil.createCallContractTransaction(nonce, contractAddress, function); 398 | byte[] message = TransactionUtil.encodeWithSig(tx, sig); 399 | String txHash = sendTxToNode(message, isSyncCall, callBack); 400 | return txHash; 401 | } 402 | 403 | 404 | /** 405 | * 将序列化好的交易广播到链节点 406 | * broadcast signed and serialized tx to node rpc 407 | * @param txMessage 交易(签名并序列化完成) 408 | * @param isSyncCall 同步/异步上链 409 | * @param callBack 回调 410 | * @return 411 | * @throws IOException 412 | */ 413 | public String sendTxToNode(byte[] txMessage, boolean isSyncCall, EventCallBack callBack) throws IOException { 414 | Response> httpRes; 415 | if (isSyncCall){ 416 | httpRes = stub.broadcastTxCommit(Numeric.toHexString(txMessage)).execute(); 417 | }else { 418 | httpRes = stub.broadcastTxAsync(Numeric.toHexString(txMessage)).execute(); 419 | } 420 | handleRespCommit(httpRes); 421 | String txHash = httpRes.body().getResult().getTx_hash(); 422 | if (callBack != null) { 423 | sb.onNext(new QueryRecTask(txHash, callBack, this)); 424 | } 425 | return txHash; 426 | } 427 | 428 | /** 429 | * 将序列化好的KV交易广播到链节点 430 | * broadcast signed and serialized tx to node rpc 431 | * @param txMessage 交易(签名并序列化完成) 432 | * @param isSyncCall 同步/异步上链 433 | * @param callBack 回调 434 | * @return 435 | * @throws IOException 436 | */ 437 | public SendTransactionResult sendTxToNodeWithError(byte[] txMessage, boolean isSyncCall, EventCallBack callBack) throws IOException { 438 | Response> httpRes; 439 | if (isSyncCall){ 440 | httpRes = stub.broadcastTxCommit(Numeric.toHexString(txMessage)).execute(); 441 | }else { 442 | httpRes = stub.broadcastTxAsync(Numeric.toHexString(txMessage)).execute(); 443 | } 444 | handleRespCommitError(httpRes); 445 | 446 | SendTransactionResult res = new SendTransactionResult(); 447 | if (StringUtils.isNotBlank(httpRes.body().getError())) { 448 | res.setErrMsg(httpRes.body().getError()); 449 | }else { 450 | res.setTxHash(httpRes.body().getResult().getTx_hash()); 451 | } 452 | return res; 453 | } 454 | 455 | /** 456 | * 457 | * 使用私钥部署合约 458 | * 459 | * @param binaryCode 合约二进制编译结果 460 | * @param 461 | * @param privateKey 462 | * @param nonce 463 | * @return contract address 合约地址 464 | * @throws IOException 465 | */ 466 | public String deployContract(String binaryCode, List constructorParameters, PrivateKey privateKey, BigInteger nonce) throws IOException { 467 | RawTransaction tx = TransactionUtil.createDelopyContractTransaction(binaryCode, constructorParameters, nonce); 468 | Signature sig = CryptoUtil.generateSignature(tx, privateKey); 469 | DeployContractResult res = doDeployContract(binaryCode, constructorParameters, nonce, sig, privateKey.getAddress()); 470 | return res.getContractAddr(); 471 | } 472 | 473 | public DeployContractResult deployContractCompl(String binaryCode, List constructorParameters, PrivateKey privateKey, BigInteger nonce) throws IOException { 474 | RawTransaction tx = TransactionUtil.createDelopyContractTransaction(binaryCode, constructorParameters, nonce); 475 | Signature sig = CryptoUtil.generateSignature(tx, privateKey); 476 | DeployContractResult res = doDeployContract(binaryCode, constructorParameters, nonce, sig, privateKey.getAddress()); 477 | return res; 478 | } 479 | 480 | 481 | /** 482 | * 483 | * 使用签名部署合约 484 | * 485 | * @param binaryCode 合约二进制编译结果 486 | * @param 487 | * @param address 用户账户地址 488 | * @param nonce 489 | * @return contract address 合约地址 490 | * @throws IOException 491 | */ 492 | public String deployContractWithSig(String binaryCode, List constructorParameters, BigInteger nonce, Signature sig, String address) throws IOException { 493 | DeployContractResult deployContractResult = doDeployContract(binaryCode, constructorParameters, nonce, sig, address); 494 | return deployContractResult.getContractAddr(); 495 | } 496 | 497 | private DeployContractResult doDeployContract(String binaryCode, List constructorParameters, BigInteger nonce, Signature sig, String address) throws IOException { 498 | RawTransaction tx = TransactionUtil.createDelopyContractTransaction(binaryCode, constructorParameters, nonce); 499 | byte[] message = TransactionUtil.encodeWithSig(tx, sig); 500 | String txHash = sendTxToNode(message, true, null); 501 | DeployContractResult deployContractResult = new DeployContractResult(); 502 | deployContractResult.setTxHash(txHash); 503 | deployContractResult.setContractAddr(ContractUtils.generateContractAddress(address, nonce)); 504 | return deployContractResult; 505 | } 506 | 507 | 508 | /** 509 | * 510 | * 调用kv 交易 511 | * 512 | * @param nonce 513 | * @param key 合约地址 514 | * @param value 合约函数定义 515 | * @param privateKey 516 | * @return 交易hash 517 | * @throws IOException 518 | */ 519 | public SendTransactionResult putKv(BigInteger nonce, String key, String value, PrivateKey privateKey) throws IOException { 520 | RawTransaction tx = TransactionUtil.createPutKVTransaction(nonce,key,value); 521 | Signature sig = CryptoUtil.generateSignature(tx, privateKey); 522 | SendTransactionResult res = callKvTxWithSig(nonce, key, value, sig, true); 523 | return res; 524 | } 525 | 526 | public SendTransactionResult putKvAsync(BigInteger nonce,String key, String value, PrivateKey privateKey) throws IOException { 527 | RawTransaction tx = TransactionUtil.createPutKVTransaction(nonce,key,value); 528 | Signature sig = CryptoUtil.generateSignature(tx, privateKey); 529 | SendTransactionResult res = callKvTxWithSig(nonce, key, value, sig, false); 530 | return res; 531 | } 532 | 533 | public SendTransactionResult putKvSignature(BigInteger nonce,String key, String value, Signature sig) throws IOException { 534 | SendTransactionResult res = callKvTxWithSig(nonce, key, value, sig, true); 535 | return res; 536 | } 537 | 538 | public SendTransactionResult putKvSignatureAsync(BigInteger nonce,String key, String value, Signature sig) throws IOException { 539 | SendTransactionResult res = callKvTxWithSig(nonce, key, value, sig, false); 540 | return res; 541 | } 542 | 543 | /** 544 | * 545 | * 使用生成好的签名调用kv 交易 546 | * 547 | * @param nonce nonce 548 | * @param key key 549 | * @param value value 550 | * @param sig 交易签名 551 | * @param isSyncCall 是否同步调用 552 | * @return txHash 交易哈希 553 | * @throws IOException 554 | */ 555 | public SendTransactionResult callKvTxWithSig(BigInteger nonce, String key, String value, Signature sig, boolean isSyncCall) throws IOException { 556 | RawTransaction tx = TransactionUtil.createPutKVTransaction(nonce,key,value); 557 | byte[] message = TransactionUtil.encodeWithSig(tx, sig); 558 | SendTransactionResult res = sendTxToNodeWithError(message, isSyncCall, null); 559 | return res; 560 | } 561 | 562 | /** 563 | * 564 | *查詢kv 交易 565 | * 566 | * @return value 567 | * @throws IOException 568 | */ 569 | public QueryKVResult getKvValueWithKey(String key) throws IOException { 570 | Response> httpResp = stub.query(QueryType.Key.paddString(key)).execute(); 571 | handleRespKVQuery(httpResp); 572 | BaseResp resp = httpResp.body(); 573 | log.debug(resp.toString()); 574 | QueryKVResult res = new QueryKVResult(); 575 | if (resp.getResult().getResult().isSuccess()) { 576 | String value = resp.getResult().getResult().getData(); 577 | res.setValue(value); 578 | }else { 579 | res.setErrMsg(resp.getResult().getResult().getLog()); 580 | } 581 | return res; 582 | } 583 | 584 | public String hexToASCII(String hexValue) 585 | { 586 | StringBuilder output = new StringBuilder(""); 587 | for (int i = 0; i < hexValue.length(); i += 2) 588 | { 589 | String str = hexValue.substring(i, i + 2); 590 | output.append((char) Integer.parseInt(str, 16)); 591 | } 592 | return output.toString(); 593 | } 594 | 595 | /** 596 | * 597 | * 批量查詢kv 交易 598 | * @param prefix 前缀 599 | * @param lastkey 起始前一个key 600 | * @param limit 返回數量 601 | * @return value 602 | * @throws IOException 603 | */ 604 | public QueryPrefixKVs getKvValueWithPrefix(String prefix,String lastkey,BigInteger limit) throws IOException { 605 | byte[] quryData = TransactionUtil.encodeKVPrefixQuery(prefix,lastkey,limit); 606 | Response> httpResp = stub.query(QueryType.KeyPrefix.paddByte(quryData)).execute(); 607 | handleRespKVQuery(httpResp); 608 | BaseResp resp = httpResp.body(); 609 | log.debug(resp.toString()); 610 | 611 | QueryPrefixKVs res = new QueryPrefixKVs(); 612 | if (resp.getResult().getResult().isSuccess()) { 613 | String respData = resp.getResult().getResult().getData(); 614 | byte[] rlpEncoded = Numeric.hexStringToByteArray(respData); 615 | LList list = RLP.decodeLazyList(rlpEncoded); 616 | TransactionKVTx[] kvs = new TransactionKVTx[list.size()]; 617 | for (int i = 0; i > httpRes) { 630 | if (!httpRes.isSuccessful()) { 631 | throw new BaseException(ErrCode.ERR_NODEAPI, httpRes.raw().toString()); 632 | } 633 | } 634 | 635 | private void handleRespCommit(Response> httpRes) { 636 | if (!httpRes.isSuccessful()) { 637 | throw new BaseException(ErrCode.ERR_NODEAPI, httpRes.raw().toString()); 638 | } 639 | if (StringUtils.isNotBlank(httpRes.body().getError())) { 640 | throw new BaseException(ErrCode.ERR_NODEAPI, httpRes.body().getError()); 641 | } 642 | if (!httpRes.body().getResult().isSuccess()) { 643 | throw new BaseException(httpRes.body().getResult().getLog()); 644 | } 645 | } 646 | 647 | 648 | private void handleRespQuery(Response> httpRes) { 649 | if (!httpRes.isSuccessful()) { 650 | throw new BaseException(ErrCode.ERR_NODEAPI, httpRes.raw().toString()); 651 | } 652 | if (StringUtils.isNotBlank(httpRes.body().getError())) { 653 | throw new BaseException(ErrCode.ERR_NODEAPI, httpRes.body().getError()); 654 | } 655 | if (!httpRes.body().getResult().getResult().isSuccess()) { 656 | throw new BaseException(httpRes.body().getResult().getResult().getLog()); 657 | } 658 | } 659 | 660 | private void handleRespKVQuery(Response> httpRes) { 661 | if (!httpRes.isSuccessful()) { 662 | throw new BaseException(ErrCode.ERR_NODEAPI, httpRes.raw().toString()); 663 | } 664 | } 665 | 666 | private void handleRespBlock(Response> httpRes) { 667 | if (!httpRes.isSuccessful()) { 668 | throw new BaseException(ErrCode.ERR_NODEAPI, httpRes.raw().toString()); 669 | } 670 | if (StringUtils.isNotBlank(httpRes.body().getError())) { 671 | throw new BaseException(ErrCode.ERR_NODEAPI, httpRes.body().getError()); 672 | } 673 | } 674 | 675 | 676 | /** 677 | * 678 | * 调用payload交易 679 | * 680 | * @param nonce 681 | * @param key 合约地址 682 | * @param value 合约函数定义 683 | * @param privateKey 684 | * @return 交易hash 685 | * @throws IOException 686 | */ 687 | public SendTransactionResult sendPayloadTx(BigInteger nonce,String to, String payload, BigInteger value, PrivateKey privateKey) throws IOException { 688 | if (payload.getBytes().length > 4096) { 689 | SendTransactionResult res = new SendTransactionResult(); 690 | res.setErrMsg("payload length must be less than 4096"); 691 | res.setTxHash(""); 692 | return res; 693 | } 694 | RawTransaction tx = TransactionUtil.createPayloadTransaction(nonce,to,payload,value); 695 | Signature sig = CryptoUtil.generateSignature(tx, privateKey); 696 | SendTransactionResult res = callPayloadWithSig(nonce,to, payload, value, sig, true); 697 | return res; 698 | } 699 | 700 | public SendTransactionResult sendPayloadTxAsync(BigInteger nonce,String to,String payload, BigInteger value, PrivateKey privateKey) throws IOException { 701 | if (payload.getBytes().length > 4096) { 702 | SendTransactionResult res = new SendTransactionResult(); 703 | res.setErrMsg("payload length must be less than 4096"); 704 | res.setTxHash(""); 705 | return res; 706 | } 707 | RawTransaction tx = TransactionUtil.createPayloadTransaction(nonce,to,payload,value); 708 | Signature sig = CryptoUtil.generateSignature(tx, privateKey); 709 | SendTransactionResult res = callPayloadWithSig(nonce,to, payload, value, sig, false); 710 | return res; 711 | } 712 | 713 | public SendTransactionResult callPayloadWithSig(BigInteger nonce,String to,String payload, BigInteger value, Signature sig, boolean isSyncCall) throws IOException { 714 | RawTransaction tx = TransactionUtil.createPayloadTransaction(nonce,to, payload, value); 715 | byte[] message = TransactionUtil.encodeWithSig(tx, sig); 716 | SendTransactionResult res = sendTxToNodeWithError(message, isSyncCall, null); 717 | return res; 718 | } 719 | 720 | /** 721 | * 722 | * 查詢payload 交易 723 | * @param txhash 724 | * @return 725 | * @throws IOException 726 | */ 727 | public QueryTransactionPayload getPayloadWithHash(String hash) throws IOException { 728 | Response> httpResp = stub.query(QueryType.Payload.ppadd(hash)).execute(); 729 | handleRespKVQuery(httpResp); 730 | BaseResp resp = httpResp.body(); 731 | log.debug(resp.toString()); 732 | QueryTransactionPayload res = new QueryTransactionPayload(); 733 | if (resp.getResult().getResult().isSuccess()) { 734 | String value = resp.getResult().getResult().getData(); 735 | res.setPayload(value); 736 | }else { 737 | res.setErrMsg(resp.getResult().getResult().getLog()); 738 | } 739 | return res; 740 | } 741 | } 742 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/QueryRecTask.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @AllArgsConstructor 7 | @Data 8 | public class QueryRecTask { 9 | private String txHash; 10 | private EventCallBack eventCallBack; 11 | private NodeSrv nodeSrv; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/QueryType.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api; 2 | 3 | import org.web3j.utils.Numeric; 4 | 5 | public enum QueryType { 6 | Contract("0"), 7 | Nonce("1"), 8 | Receipt("3"), 9 | Payload("5"), 10 | ContractByHeight("10"), 11 | Key("11"), 12 | KeyPrefix("12"), 13 | PendingNonce("13"); 14 | 15 | QueryType(String code) { 16 | this.code = new Byte(code); 17 | } 18 | 19 | byte code; 20 | 21 | public byte getCode() { 22 | return code; 23 | } 24 | 25 | public String padd(String address) { 26 | byte[] addressByte = Numeric.hexStringToByteArray(address); 27 | byte[] resp = new byte[addressByte.length + 1]; 28 | resp[0] = this.getCode(); 29 | System.arraycopy(addressByte, 0, resp, 1, addressByte.length); 30 | return Numeric.toHexString(resp); 31 | } 32 | 33 | public String ppadd(String address) { 34 | byte[] addressByte = Numeric.hexStringToByteArray(address); 35 | byte[] resp = new byte[addressByte.length + 2]; 36 | resp[0] = this.getCode(); 37 | resp[1] = 0x01; 38 | System.arraycopy(addressByte, 0, resp, 2, addressByte.length); 39 | return Numeric.toHexString(resp); 40 | 41 | } 42 | 43 | public String paddByte(byte[] txBytes) { 44 | return padd(Numeric.toHexString(txBytes)); 45 | } 46 | 47 | public String paddString(String str) { 48 | byte[] strByte = str.getBytes(); 49 | byte[] resp = new byte[strByte.length + 1]; 50 | resp[0] = this.getCode(); 51 | System.arraycopy(strByte, 0, resp, 1, strByte.length); 52 | return Numeric.toHexString(resp); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/TransactionUtil.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api; 2 | 3 | import com.genesis.api.bean.exception.BaseException; 4 | import com.genesis.api.bean.exception.ErrCode; 5 | import com.genesis.api.crypto.PrivateKey; 6 | import com.genesis.api.crypto.Signature; 7 | import org.spongycastle.util.encoders.Hex; 8 | import org.web3j.abi.FunctionEncoder; 9 | import org.web3j.abi.datatypes.Function; 10 | import org.web3j.abi.datatypes.Type; 11 | import org.web3j.crypto.*; 12 | import org.web3j.rlp.RlpEncoder; 13 | import org.web3j.rlp.RlpList; 14 | import org.web3j.rlp.RlpString; 15 | import org.web3j.rlp.RlpType; 16 | import org.web3j.utils.Bytes; 17 | import org.web3j.utils.Numeric; 18 | import java.io.IOException; 19 | import java.math.BigInteger; 20 | import java.util.ArrayList; 21 | import java.util.LinkedList; 22 | import java.util.List; 23 | 24 | public class TransactionUtil { 25 | /** 26 | * 创建合约调用交易 27 | * 28 | * @param nonce 29 | * @param contractAddress 30 | * @param function 函数定义 31 | * @return 32 | */ 33 | public static RawTransaction createCallContractTransaction(BigInteger nonce, String contractAddress, Function function){ 34 | RawTransaction rtx = RawTransaction.createTransaction(nonce, BigInteger.ZERO, BigInteger.valueOf(1000000000L), contractAddress, FunctionEncoder.encode(function)); 35 | return rtx; 36 | } 37 | 38 | /** 39 | * 创建部署合约交易 40 | * 41 | * @param binaryCode 二进制合约代码 42 | * @param constructorParameters 构建参数 43 | * @param nonce 44 | * @return 45 | */ 46 | public static RawTransaction createDelopyContractTransaction(String binaryCode, List constructorParameters, BigInteger nonce){ 47 | RawTransaction rtx = RawTransaction.createTransaction(nonce, BigInteger.ZERO, BigInteger.valueOf(1000000000L), null, binaryCode + FunctionEncoder.encodeConstructor(constructorParameters)); 48 | return rtx; 49 | } 50 | 51 | /** 52 | * 创建kv交易 53 | * 54 | * @param nonce 55 | * @param key 56 | * @param value 57 | * @return 58 | */ 59 | public static RawTransaction createPutKVTransaction(BigInteger nonce, String key,String value){ 60 | byte[] kvByte = encodeWithKV(key,value); 61 | RawTransaction rtx = RawTransaction.createTransaction(nonce, BigInteger.ZERO, BigInteger.valueOf(1000000000L), null, paddType(Numeric.toHexString(kvByte))); 62 | return rtx; 63 | } 64 | 65 | public static String paddType(String data) { 66 | byte[] strByte = Numeric.hexStringToByteArray(data); 67 | byte[] type = "kvTx-".getBytes(); 68 | byte[] resp = new byte[strByte.length + type.length]; 69 | System.arraycopy(type, 0, resp, 0, type.length); 70 | System.arraycopy(strByte, 0, resp, type.length, strByte.length); 71 | return Hex.toHexString(resp); 72 | } 73 | 74 | /** 75 | * 创建payload交易 76 | * 77 | * @param nonce 78 | * @param payload 79 | * @param value 80 | * @return 81 | */ 82 | public static RawTransaction createPayloadTransaction(BigInteger nonce,String to, String payload,BigInteger value){ 83 | RawTransaction rtx = RawTransaction.createTransaction(nonce, BigInteger.ZERO, BigInteger.valueOf(1000000000L), to, value, Numeric.toHexString(payload.getBytes())); 84 | return rtx; 85 | } 86 | 87 | /** 88 | * decode单笔链上交易 89 | * 90 | * @param txMsg 链上交易 91 | * @return 原始交易信息 92 | */ 93 | public static SignedRawTransaction decodeTxMsg(String txMsg){ 94 | SignedRawTransaction signedRawTransaction = (SignedRawTransaction) TransactionDecoder.decode(txMsg); 95 | return signedRawTransaction; 96 | } 97 | 98 | 99 | /** 100 | * decode链上交易(单笔或批量) 101 | * 102 | * @param txMsg 未知类型链上交易(单笔或批量) 103 | * @return 原始交易信息 104 | */ 105 | public static List decodeArbitrayTxMsg(String txMsg) throws IOException { 106 | List rtxs = new LinkedList<>(); 107 | // 单笔交易 108 | rtxs.add(decodeTxMsg(txMsg)); 109 | return rtxs; 110 | } 111 | 112 | /** 113 | * encode交易 114 | * 115 | * @param tx 116 | * @return 117 | */ 118 | public static byte[] encode(RawTransaction tx) { 119 | return TransactionEncoder.encode(tx); 120 | } 121 | 122 | /** 123 | * encode原始交易和签名 124 | * 125 | * @param tx 126 | * @return 127 | */ 128 | public static byte[] encode(RawTransaction tx, PrivateKey privateKey){ 129 | Signature signature = CryptoUtil.generateSignature(tx, privateKey); 130 | return encodeWithSig(tx, signature); 131 | } 132 | 133 | 134 | /** 135 | * 使用已生成的签名encode交易 136 | * 137 | * @param tx 138 | * @return 139 | */ 140 | public static byte[] encodeWithSig(RawTransaction tx, Signature sig) { 141 | List values = asRlpValues(tx, sig); 142 | RlpList rlpList = new RlpList(values); 143 | return RlpEncoder.encode(rlpList); 144 | } 145 | 146 | 147 | private static List asRlpValues(RawTransaction rawTransaction, Signature sig) { 148 | 149 | List result = new ArrayList(); 150 | result.add(RlpString.create(rawTransaction.getNonce())); 151 | result.add(RlpString.create(rawTransaction.getGasPrice())); 152 | result.add(RlpString.create(rawTransaction.getGasLimit())); 153 | String to = rawTransaction.getTo(); 154 | if (to != null && to.length() > 0) { 155 | result.add(RlpString.create(Numeric.hexStringToByteArray(to))); 156 | } else { 157 | result.add(RlpString.create("")); 158 | } 159 | 160 | result.add(RlpString.create(rawTransaction.getValue())); 161 | byte[] data = Numeric.hexStringToByteArray(rawTransaction.getData()); 162 | result.add(RlpString.create(data)); 163 | if (sig != null) { 164 | result.add(RlpString.create(sig.getV())); 165 | result.add(RlpString.create(Bytes.trimLeadingZeroes(sig.getR()))); 166 | result.add(RlpString.create(Bytes.trimLeadingZeroes(sig.getS()))); 167 | } 168 | return result; 169 | } 170 | 171 | /** 172 | * encode kv交易 173 | * 174 | * @param kvtx 175 | * @return 176 | */ 177 | public static byte[] encodeWithKV(String key,String value) { 178 | List values = asKVRlpValues(key,value); 179 | RlpList rlpList = new RlpList(values); 180 | return RlpEncoder.encode(rlpList); 181 | } 182 | 183 | private static List asKVRlpValues(String key,String value) { 184 | List result = new ArrayList(); 185 | result.add(RlpString.create(key)); 186 | result.add(RlpString.create(value)); 187 | return result; 188 | } 189 | 190 | /** 191 | * encode kv prefix查詢 192 | * 193 | * @param prefix 194 | * @param key 195 | * @param limit 196 | * @return 197 | */ 198 | public static byte[] encodeKVPrefixQuery(String prefix, String lastKey,BigInteger limit) { 199 | List values = asKVPrefixValues(prefix,lastKey,limit); 200 | RlpList rlpList = new RlpList(values); 201 | return RlpEncoder.encode(rlpList); 202 | } 203 | 204 | private static List asKVPrefixValues(String prefix, String lastKey,BigInteger limit) { 205 | List result = new ArrayList(); 206 | result.add(RlpString.create(prefix)); 207 | result.add(RlpString.create(lastKey)); 208 | result.add(RlpString.create(limit)); 209 | return result; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/exception/BaseException.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.exception; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class BaseException extends RuntimeException { 7 | private ErrCode error; 8 | 9 | public BaseException(ErrCode error, String msg){ 10 | super(error.getDesc() + msg); 11 | this.setError(error); 12 | } 13 | 14 | public BaseException(String msg) { 15 | super(ErrCode.ERR_BAD_CALL.getDesc() + msg); 16 | this.setError(ErrCode.ERR_BAD_CALL); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/exception/ErrCode.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.exception; 2 | 3 | /** 4 | * Created by za-zhaogang on 2018/1/13. 5 | */ 6 | public enum ErrCode { 7 | 8 | ERR_BAD_CALL("5001, 调用SDK出错:"), 9 | ERR_NODEAPI("5002, 调用节点错误:"), 10 | ERR_LIBEXCEPTION("5003, 调用库报错:"); 11 | 12 | private final String desc; 13 | ErrCode(String desc) { 14 | this.desc = desc; 15 | } 16 | public String getDesc() { return desc; } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/exception/LIBException.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.exception; 2 | 3 | public class LIBException extends BaseException{ 4 | public LIBException(Exception ex){ 5 | super(ErrCode.ERR_BAD_CALL.getDesc() + ex.getMessage()); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/genesis/BaseResp.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.genesis; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor 8 | public class BaseResp { 9 | 10 | private String jsonrpc; 11 | private String id; 12 | private T result; 13 | private String error; 14 | 15 | public BaseResp(){ 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/genesis/ResultBlock.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.genesis; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ResultBlock { 7 | 8 | private BlockMeta block_meta; 9 | private Block block; 10 | 11 | @Data 12 | public static class BlockMeta { 13 | private String hash; 14 | private Header header; 15 | private PartSetHeader parts_header; 16 | } 17 | 18 | @Data 19 | public static class Block { 20 | private Header header; 21 | private TxData data; 22 | private LastCommit last_commit; 23 | } 24 | 25 | @Data 26 | public static class Header { 27 | private String chain_id; 28 | private String height; 29 | private String time; 30 | private long num_txs; 31 | private BlockID last_block_id; 32 | private String last_commit_hash; 33 | private String data_hash; 34 | private String validators_hash; 35 | private String app_hash; 36 | private String recepits_hash; 37 | private String proposer_address; 38 | } 39 | 40 | @Data 41 | public static class BlockID { 42 | private PartSetHeader parts; 43 | private String hash; 44 | } 45 | 46 | @Data 47 | public static class PartSetHeader { 48 | private int total; 49 | private String hash; 50 | } 51 | 52 | @Data 53 | public static class TxData { 54 | private String[] txs; 55 | private String[] extxs; 56 | private String hash; 57 | } 58 | 59 | @Data 60 | public static class LastCommit { 61 | private BlockID blockID; 62 | private Vote[] precommits; 63 | } 64 | 65 | @Data 66 | public static class Vote { 67 | private String validator_address; 68 | private int validator_index; 69 | private String height; 70 | private long round; 71 | private byte type; 72 | private BlockID block_id; 73 | private String signature; 74 | } 75 | 76 | @Data 77 | public static class Mutex{ 78 | private int state; 79 | private int sema; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/genesis/ResultCommit.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.genesis; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ResultCommit { 7 | private int Code; 8 | private String Data; 9 | private String Log; 10 | private String tx_hash; 11 | 12 | public boolean isSuccess(){ 13 | return this.Code == 0; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/genesis/ResultQuery.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.genesis; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class ResultQuery { 7 | 8 | private ResultBean result; 9 | 10 | @Data 11 | public static class ResultBean { 12 | 13 | private int Code; 14 | private String Data; 15 | private String Log; 16 | public boolean isSuccess(){ 17 | return this.Code == 0; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/model/DeployContractResult.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class DeployContractResult { 7 | private String contractAddr; 8 | private String txHash; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/model/QueryBlockTransactionHashs.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class QueryBlockTransactionHashs { 7 | private String[] hashs; 8 | private long count; 9 | 10 | public QueryBlockTransactionHashs(String[] txhashs, long num){ 11 | hashs = txhashs; 12 | count = num; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/model/QueryKVResult.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class QueryKVResult { 7 | private String value; 8 | private String errMsg; 9 | 10 | public void setValue(String v) { 11 | value = hexToASCII(v); 12 | } 13 | 14 | public String hexToASCII(String hexValue) 15 | { 16 | StringBuilder output = new StringBuilder(""); 17 | for (int i = 0; i < hexValue.length(); i += 2) 18 | { 19 | String str = hexValue.substring(i, i + 2); 20 | output.append((char) Integer.parseInt(str, 16)); 21 | } 22 | return output.toString(); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/model/QueryPrefixKVs.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.model; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | @NoArgsConstructor 7 | @Data 8 | public class QueryPrefixKVs { 9 | private TransactionKVTx[] txs; 10 | private String errMsg; 11 | } -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/model/QueryTransaction.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.model; 2 | 3 | import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY; 4 | import java.math.BigInteger; 5 | import org.web3j.utils.Numeric; 6 | import org.ethereum.util.RLP; 7 | import org.ethereum.util.RLPItem; 8 | import org.ethereum.util.RLPList; 9 | 10 | public class QueryTransaction { 11 | private byte[] blockHash = EMPTY_BYTE_ARRAY; 12 | private byte[] blockHeight; 13 | private BigInteger transactionIndex; 14 | private byte[] rawTransaction = EMPTY_BYTE_ARRAY; 15 | private BigInteger timestamp; 16 | 17 | public QueryTransaction() { 18 | 19 | } 20 | 21 | public QueryTransaction(byte[] rlp) { 22 | RLPList params = RLP.decode2(rlp); 23 | RLPList result = (RLPList) params.get(0); 24 | 25 | RLPItem blockHashRLP = (RLPItem) result.get(0); 26 | RLPItem blockHeightRLP = (RLPItem) result.get(1); 27 | RLPItem transactionIndexRLP = (RLPItem) result.get(2); 28 | RLPItem rawTransactionRLP = (RLPItem) result.get(3); 29 | RLPItem timestampRLP = (RLPItem) result.get(4); 30 | 31 | blockHash = blockHashRLP.getRLPData(); 32 | blockHeight = blockHeightRLP.getRLPData(); 33 | rawTransaction = rawTransactionRLP.getRLPData(); 34 | timestamp = Numeric.toBigInt(timestampRLP.getRLPData()); 35 | 36 | if(transactionIndexRLP.getRLPData() == null) { 37 | transactionIndex = BigInteger.valueOf(0); 38 | }else { 39 | transactionIndex = Numeric.toBigInt(transactionIndexRLP.getRLPData()); 40 | } 41 | } 42 | 43 | public byte[] getBlockHash() { 44 | return blockHash; 45 | } 46 | 47 | public byte[] getBlockHeight() { 48 | return blockHeight; 49 | } 50 | 51 | public BigInteger getTransactionIndex() { 52 | return transactionIndex; 53 | } 54 | 55 | public byte[] getRawTransaction() { 56 | return rawTransaction; 57 | } 58 | 59 | public BigInteger getTimestamp() { 60 | return timestamp; 61 | } 62 | 63 | public void setBlockHash(byte[] blockHash) { 64 | this.blockHash = blockHash; 65 | } 66 | 67 | public void setBlockHeight(byte[] blockHeight) { 68 | this.blockHeight = blockHeight; 69 | } 70 | 71 | public void setTransactionIndex(BigInteger transactionIndex) { 72 | this.transactionIndex = transactionIndex; 73 | } 74 | 75 | public void setRawTransaction(byte[] rawTransaction) { 76 | this.rawTransaction = rawTransaction; 77 | } 78 | 79 | public void setTimestamp(BigInteger timestamp) { 80 | this.timestamp = timestamp; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/model/QueryTransactionPayload.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.model; 2 | 3 | import org.web3j.utils.Numeric; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class QueryTransactionPayload { 9 | private String payload; 10 | private String errMsg; 11 | 12 | public void setPayload(String p) { 13 | payload = hexToASCII(p); 14 | } 15 | 16 | public String hexToASCII(String hexValue){ 17 | byte[] baKeyword = new byte[hexValue.length() / 2]; 18 | for (int i = 0; i < baKeyword.length; i++) { 19 | try { 20 | baKeyword[i] = (byte) (0xff & Integer.parseInt(hexValue.substring( 21 | i * 2, i * 2 + 2), 16)); 22 | } catch (Exception e) { 23 | e.printStackTrace(); 24 | } 25 | } 26 | try { 27 | hexValue = new String(baKeyword, "utf-8");// UTF-16le:Not 28 | } catch (Exception e1) { 29 | e1.printStackTrace(); 30 | } 31 | return hexValue; 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/com/genesis/api/bean/model/QueryTransactionReceipt.java: -------------------------------------------------------------------------------- 1 | package com.genesis.api.bean.model; 2 | 3 | import org.ethereum.core.Bloom; 4 | import org.ethereum.core.Transaction; 5 | import org.ethereum.util.*; 6 | import org.ethereum.vm.LogInfo; 7 | import org.spongycastle.util.encoders.Hex; 8 | import org.web3j.utils.Numeric; 9 | 10 | import java.math.BigInteger; 11 | import java.nio.charset.StandardCharsets; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | import static org.apache.commons.lang3.ArrayUtils.nullToEmpty; 16 | import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY; 17 | 18 | /** 19 | * The transaction receipt is a tuple of three items 20 | * comprising the transaction, together with the post-transaction state, 21 | * and the cumulative gas used in the block containing the transaction receipt 22 | * as of immediately after the transaction has happened, 23 | */ 24 | 25 | public class QueryTransactionReceipt { 26 | 27 | private Transaction transaction; 28 | private byte[] postTxState = EMPTY_BYTE_ARRAY; 29 | private BigInteger cumulativeGas; 30 | private Bloom bloomFilter = new Bloom(); 31 | private List logInfoList = new ArrayList<>(); 32 | 33 | private byte[] transactionHash; 34 | private byte[] contractAddress; 35 | private BigInteger gasUsed; 36 | 37 | private BigInteger transactionIndex; 38 | private BigInteger height; 39 | private byte[] blockHash = EMPTY_BYTE_ARRAY; 40 | private byte[] from; 41 | private byte[] to; 42 | private BigInteger timestamp; 43 | 44 | private byte[] executionResult = EMPTY_BYTE_ARRAY; 45 | private String error = ""; 46 | /* Tx Receipt in encoded form */ 47 | private byte[] rlpEncoded; 48 | 49 | public QueryTransactionReceipt() { 50 | } 51 | 52 | public QueryTransactionReceipt(byte[] rlp) { 53 | RLPList params = RLP.decode2(rlp); 54 | RLPList receipt = (RLPList) params.get(0); 55 | 56 | RLPItem postTxStateRLP = (RLPItem) receipt.get(0); 57 | RLPItem cumulativeGasRLP = (RLPItem) receipt.get(1); 58 | RLPItem bloomRLP = (RLPItem) receipt.get(2); 59 | RLPItem transactionHashRLP = (RLPItem) receipt.get(3); 60 | RLPList logs = (RLPList) receipt.get(5); 61 | RLPItem gasUsedRLP = (RLPItem) receipt.get(6); 62 | RLPItem contractAddressRLP = (RLPItem) receipt.get(4); 63 | 64 | postTxState = nullToEmpty(postTxStateRLP.getRLPData()); 65 | 66 | if(cumulativeGasRLP.getRLPData() == null) { 67 | cumulativeGas = BigInteger.valueOf(0); 68 | }else { 69 | cumulativeGas = Numeric.toBigInt(cumulativeGasRLP.getRLPData()); 70 | } 71 | 72 | bloomFilter = new Bloom(bloomRLP.getRLPData()); 73 | 74 | if(gasUsedRLP.getRLPData() == null) { 75 | gasUsed = BigInteger.valueOf(0); 76 | }else { 77 | gasUsed = Numeric.toBigInt(gasUsedRLP.getRLPData()); 78 | } 79 | 80 | transactionHash = transactionHashRLP.getRLPData(); 81 | contractAddress = contractAddressRLP.getRLPData(); 82 | for (RLPElement log : logs) { 83 | LogInfo logInfo = new LogInfo(log.getRLPData()); 84 | logInfoList.add(logInfo); 85 | } 86 | 87 | rlpEncoded = rlp; 88 | } 89 | 90 | 91 | public QueryTransactionReceipt(byte[] postTxState, byte[] cumulativeGas, 92 | Bloom bloomFilter, List logInfoList) { 93 | this.postTxState = postTxState; 94 | this.cumulativeGas = Numeric.toBigInt(cumulativeGas); 95 | this.bloomFilter = bloomFilter; 96 | this.logInfoList = logInfoList; 97 | } 98 | 99 | public QueryTransactionReceipt(final RLPList rlpList) { 100 | if (rlpList == null || rlpList.size() != 4) 101 | throw new RuntimeException("Should provide RLPList with postTxState, cumulativeGas, bloomFilter, logInfoList"); 102 | 103 | this.postTxState = rlpList.get(0).getRLPData(); 104 | this.cumulativeGas = Numeric.toBigInt(rlpList.get(1).getRLPData()); 105 | this.bloomFilter = new Bloom(rlpList.get(2).getRLPData()); 106 | 107 | List logInfos = new ArrayList<>(); 108 | for (RLPElement logInfoEl: (RLPList) rlpList.get(3)) { 109 | LogInfo logInfo = new LogInfo(logInfoEl.getRLPData()); 110 | logInfos.add(logInfo); 111 | } 112 | this.logInfoList = logInfos; 113 | } 114 | 115 | public byte[] getPostTxState() { 116 | return postTxState; 117 | } 118 | 119 | public BigInteger getCumulativeGas() { 120 | return cumulativeGas; 121 | } 122 | 123 | public BigInteger getGasUsed() { 124 | return gasUsed; 125 | } 126 | 127 | public byte[] getExecutionResult() { 128 | return executionResult; 129 | } 130 | 131 | public BigInteger getCumulativeGasLong() { 132 | return cumulativeGas; 133 | } 134 | 135 | public byte[] getTransactionHash() { 136 | return transactionHash; 137 | } 138 | 139 | public byte[] getContractAddress() { 140 | return contractAddress; 141 | } 142 | 143 | public Bloom getBloomFilter() { 144 | return bloomFilter; 145 | } 146 | 147 | public List getLogInfoList() { 148 | return logInfoList; 149 | } 150 | 151 | public boolean isValid() { 152 | return gasUsed.floatValue() > 0; 153 | } 154 | 155 | public boolean isSuccessful() { 156 | return error.isEmpty(); 157 | } 158 | 159 | public String getError() { 160 | return error; 161 | } 162 | 163 | /** 164 | * Used for Receipt trie hash calculation. Should contain only the following items encoded: 165 | * [postTxState, cumulativeGas, bloomFilter, logInfoList] 166 | */ 167 | public byte[] getReceiptTrieEncoded() { 168 | return getEncoded(true); 169 | } 170 | 171 | /** 172 | * Used for serialization, contains all the receipt data encoded 173 | */ 174 | public byte[] getEncoded() { 175 | if (rlpEncoded == null) { 176 | rlpEncoded = getEncoded(false); 177 | } 178 | 179 | return rlpEncoded; 180 | } 181 | 182 | public byte[] getEncoded(boolean receiptTrie) { 183 | 184 | byte[] postTxStateRLP = RLP.encodeElement(this.postTxState); 185 | byte[] cumulativeGasRLP = RLP.encodeElement(this.cumulativeGas.toByteArray()); 186 | byte[] bloomRLP = RLP.encodeElement(this.bloomFilter.getData()); 187 | 188 | final byte[] logInfoListRLP; 189 | if (logInfoList != null) { 190 | byte[][] logInfoListE = new byte[logInfoList.size()][]; 191 | 192 | int i = 0; 193 | for (LogInfo logInfo : logInfoList) { 194 | logInfoListE[i] = logInfo.getEncoded(); 195 | ++i; 196 | } 197 | logInfoListRLP = RLP.encodeList(logInfoListE); 198 | } else { 199 | logInfoListRLP = RLP.encodeList(); 200 | } 201 | 202 | return receiptTrie ? 203 | RLP.encodeList(postTxStateRLP, cumulativeGasRLP, bloomRLP, logInfoListRLP): 204 | RLP.encodeList(postTxStateRLP, cumulativeGasRLP, bloomRLP, logInfoListRLP, 205 | gasUsed.toByteArray(), RLP.encodeElement(executionResult), 206 | RLP.encodeElement(error.getBytes(StandardCharsets.UTF_8))); 207 | 208 | } 209 | 210 | public void setPostTxState(byte[] postTxState) { 211 | this.postTxState = postTxState; 212 | rlpEncoded = null; 213 | } 214 | 215 | public void setCumulativeGas(BigInteger cumulativeGas) { 216 | this.cumulativeGas = cumulativeGas; 217 | rlpEncoded = null; 218 | } 219 | 220 | public void setGasUsed(BigInteger gasUsed) { 221 | this.gasUsed = gasUsed; 222 | rlpEncoded = null; 223 | } 224 | 225 | public void setExecutionResult(byte[] executionResult) { 226 | this.executionResult = executionResult; 227 | rlpEncoded = null; 228 | } 229 | 230 | public void setError(String error) { 231 | this.error = error == null ? "" : error; 232 | } 233 | 234 | public void setLogInfoList(List logInfoList) { 235 | if (logInfoList == null) return; 236 | this.logInfoList = logInfoList; 237 | 238 | for (LogInfo loginfo : logInfoList) { 239 | bloomFilter.or(loginfo.getBloom()); 240 | } 241 | rlpEncoded = null; 242 | } 243 | 244 | public void setTransaction(Transaction transaction) { 245 | this.transaction = transaction; 246 | } 247 | 248 | public void setContractAddress(byte[] contractAddress) { 249 | this.contractAddress = contractAddress; 250 | } 251 | 252 | public void setTransactionHash(byte[] transactionHash) { 253 | this.transactionHash = transactionHash; 254 | } 255 | 256 | public Transaction getTransaction() { 257 | if (transaction == null) throw new NullPointerException("Transaction is not initialized. Use TransactionInfo and BlockStore to setup Transaction instance"); 258 | return transaction; 259 | } 260 | 261 | public byte[] getBlockHash() { 262 | return blockHash; 263 | } 264 | 265 | public BigInteger getHeight() { 266 | return height; 267 | } 268 | 269 | public BigInteger getTransactionIndex() { 270 | return transactionIndex; 271 | } 272 | 273 | 274 | public BigInteger getTimestamp() { 275 | return timestamp; 276 | } 277 | 278 | public byte[] getFrom() { 279 | return from; 280 | } 281 | 282 | public byte[] getTo() { 283 | return to; 284 | } 285 | 286 | public void setBlockHash(byte[] blockHash) { 287 | this.blockHash = blockHash; 288 | } 289 | 290 | public void setBlockHeight(byte[] height) { 291 | this.height = Numeric.toBigInt(height); 292 | } 293 | 294 | public void setTransactionIndex(BigInteger transactionIndex) { 295 | this.transactionIndex = transactionIndex; 296 | } 297 | 298 | public void setTimestamp(BigInteger timestamp) { 299 | this.timestamp = timestamp; 300 | } 301 | 302 | public void setFrom(byte[] from) { 303 | this.from = from; 304 | } 305 | 306 | public void setTo(byte[] to) { 307 | this.to = to; 308 | } 309 | 310 | public ReceiptObject getReceiptObject(){ 311 | ReceiptObject object = new ReceiptObject(); 312 | object.setBloom(bloomFilter.toString()); 313 | object.setContractAddress(Hex.toHexString(to)); 314 | object.setTxHash(Hex.toHexString(transactionHash)); 315 | object.setCumulativeGasUsed(cumulativeGas); 316 | object.setGasUsed(gasUsed); 317 | object.setHeight(height); 318 | object.setPostState(Hex.toHexString(postTxState)); 319 | ReceiptLogs[] logs = new ReceiptLogs[logInfoList.size()]; 320 | for(int i=0;i 2 | 3 | 4 | 5 | 6 | %-4relative [%thread] %-5level %logger{35} - %msg %n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/test/java/AbiToolTest.java: -------------------------------------------------------------------------------- 1 | import com.genesis.api.AbiTool; 2 | import com.genesis.api.CryptoUtil; 3 | import com.genesis.api.TransactionUtil; 4 | import com.genesis.api.crypto.PrivateKeyECDSA; 5 | import com.genesis.api.crypto.Signature; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.ethereum.solidity.Abi; 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | import org.web3j.abi.FunctionEncoder; 11 | import org.web3j.abi.FunctionReturnDecoder; 12 | import org.web3j.abi.TypeReference; 13 | import org.web3j.abi.datatypes.*; 14 | import org.web3j.abi.datatypes.generated.AbiTypes; 15 | import org.web3j.abi.datatypes.generated.Uint240; 16 | import org.web3j.abi.datatypes.generated.Uint256; 17 | import org.web3j.crypto.*; 18 | import org.web3j.utils.Numeric; 19 | 20 | import java.math.BigInteger; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | @Slf4j 25 | public class AbiToolTest { 26 | 27 | String abiCode1 = "[{\"constant\":false,\"inputs\":[{\"name\":\"_lotteryId\",\"type\":\"string\"}],\"name\":\"getRaffle\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_lotteryId\",\"type\":\"string\"}],\"name\":\"getWinner\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_lotteryId\",\"type\":\"uint256[2]\"},{\"name\":\"_name\",\"type\":\"string\"},{\"name\":\"_lotteryTime\",\"type\":\"string\"},{\"name\":\"_prizeName\",\"type\":\"string\"},{\"name\":\"_amount\",\"type\":\"uint256\"},{\"name\":\"_lotteryOwner\",\"type\":\"address\"}],\"name\":\"initLottery\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_lotteryId\",\"type\":\"string\"}],\"name\":\"getLottery\",\"outputs\":[{\"name\":\"_reffleNum\",\"type\":\"uint256\"},{\"name\":\"_drawRandom\",\"type\":\"uint256\"},{\"name\":\"_winners\",\"type\":\"address[]\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_lotteryId\",\"type\":\"string\"}],\"name\":\"draw\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_lotteryId\",\"type\":\"string\"},{\"name\":\"_participantAddress\",\"type\":\"address\"},{\"name\":\"_raffleTime\",\"type\":\"string\"},{\"name\":\"_raffleRandom\",\"type\":\"uint256\"}],\"name\":\"raffle\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"; 28 | String abiCode2 = "[{\"constant\":false,\"inputs\":[{\"name\":\"a\",\"type\":\"uint256\"},{\"name\":\"b\",\"type\":\"uint256[]\"}],\"name\":\"queryArray\",\"outputs\":[{\"name\":\"ar\",\"type\":\"uint256[]\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"deposit\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"js\",\"type\":\"string\"}],\"name\":\"queryInfo\",\"outputs\":[{\"name\":\"info\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"abc\",\"type\":\"string\"}],\"name\":\"Deposit\",\"type\":\"event\"}]"; 29 | 30 | 31 | @Test 32 | public void decodeFunction1(){ 33 | AbiTool abiTool = new AbiTool(abiCode1); 34 | PrivateKeyECDSA privateKey = new PrivateKeyECDSA("16183ac81b7888e9c80aa7c586ed9337478fc6d8bf1f11759a9bb0aed41fc987"); 35 | BigInteger nonce = BigInteger.valueOf(99); 36 | List inputParameters = new ArrayList<>(); 37 | inputParameters.add(new StaticArray(new Uint256(1), new Uint256(99))); 38 | inputParameters.add(new Utf8String("抽奖")); 39 | inputParameters.add(new Utf8String("201901031836")); 40 | inputParameters.add(new Utf8String("iphoneXR")); 41 | inputParameters.add(new Uint256(10)); 42 | inputParameters.add(new Address("0x2b35e1555e3d9e31b0099733cda6a6dfa88bbfdd")); 43 | List> outputParameters = new ArrayList<>(); 44 | Function function = new Function("initLottery", inputParameters, outputParameters); 45 | RawTransaction tx = TransactionUtil.createCallContractTransaction(nonce, "0xe52c09567254855381395b99decbc84b96de513e", function); 46 | String rawTxMsg = Numeric.toHexString(TransactionUtil.encode(tx, privateKey)); 47 | 48 | // input txmsg here 49 | // 1. decode tx 50 | SignedRawTransaction rtx = TransactionUtil.decodeTxMsg(rawTxMsg); 51 | // 2. decode function 52 | Function func = abiTool.decodeFunction(rtx.getData()); 53 | for (Type t : func.getInputParameters()){ 54 | log.info("{} {}", t.getTypeAsString(), t.getValue()); 55 | } 56 | Assert.assertEquals(func.getName(), "initLottery"); 57 | Assert.assertEquals(func.getInputParameters().size(), 6); 58 | Assert.assertEquals(func.getInputParameters().get(0).getTypeAsString(), "uint256[2]"); 59 | Assert.assertEquals(func.getInputParameters().get(5).getValue().toString(), "0x2b35e1555e3d9e31b0099733cda6a6dfa88bbfdd"); 60 | } 61 | 62 | 63 | @Test 64 | public void decode2(){ 65 | List inputParameters = new ArrayList<>(); 66 | List> outputParameters = new ArrayList<>(); 67 | Function function = new Function("deposit", inputParameters, outputParameters); 68 | AbiTool abiTool = new AbiTool(abiCode2); 69 | Function func = abiTool.decodeFunction(FunctionEncoder.encode(function)); 70 | log.info("{} {}", func.getName(), func.getInputParameters()); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/CryptoUtilTest.java: -------------------------------------------------------------------------------- 1 | import com.genesis.api.CryptoUtil; 2 | import com.genesis.api.TransactionUtil; 3 | import com.genesis.api.crypto.PrivateKey; 4 | import com.genesis.api.crypto.PrivateKeyECDSA; 5 | import com.genesis.api.crypto.Signature; 6 | import com.genesis.api.crypto.SignatureECDSA; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | import java.math.BigInteger; 11 | import java.security.SignatureException; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | import lombok.extern.slf4j.Slf4j; 16 | import org.web3j.abi.TypeReference; 17 | import org.web3j.abi.datatypes.Address; 18 | import org.web3j.abi.datatypes.Function; 19 | import org.web3j.abi.datatypes.Type; 20 | import org.web3j.abi.datatypes.Utf8String; 21 | import org.web3j.abi.datatypes.generated.Uint256; 22 | import org.web3j.crypto.*; 23 | import org.web3j.utils.Numeric; 24 | 25 | 26 | @Slf4j 27 | public class CryptoUtilTest { 28 | 29 | @Test 30 | public void TestDecodeTxMsg(){ 31 | String txmsg = "0xf902eb01808094b6afcb28900292b497c61a773b6110c4addc65e089010000000000000000b90283608060405234801561001057600080fd5b50610263806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632b823efe14610046575b600080fd5b34801561005257600080fd5b5061005b610144565b604051808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838152602001828152602001965050505050505060405180910390f35b600080600080600080600080600080600080735e72914535f202659083db3a02c984188fa26e9f63c6b6e6396040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160606040518083038186803b1580156101b657600080fd5b505af41580156101ca573d6000803e3d6000fd5b505050506040513d60608110156101e057600080fd5b810190808051906020019092919080519060200190929190805190602001909291905050509550955095503033349250925092508583868487858494509b509b509b509b509b509b505050505050509091929394955600a165627a7a72305820c9ec566747f7c81af44d23293bc4feaaa84b1f02eb61d603c5786d7f95c8584d00291ca0f4cf29659ae8eedb66630e09f62b902f5ee9b87c810397f16c1b032c759f477ca071e763b986b17c085917f6e0e8e10104d908fd3532bd2dfec32287c83cf27ddf"; 32 | SignedRawTransaction rtx = TransactionUtil.decodeTxMsg(txmsg); 33 | Assert.assertEquals(rtx.getNonce(), BigInteger.valueOf(1)); 34 | Assert.assertEquals(rtx.getTo(), "0xb6afcb28900292b497c61a773b6110c4addc65e0"); 35 | Assert.assertEquals(rtx.getData(), "608060405234801561001057600080fd5b50610263806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632b823efe14610046575b600080fd5b34801561005257600080fd5b5061005b610144565b604051808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838152602001828152602001965050505050505060405180910390f35b600080600080600080600080600080600080735e72914535f202659083db3a02c984188fa26e9f63c6b6e6396040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160606040518083038186803b1580156101b657600080fd5b505af41580156101ca573d6000803e3d6000fd5b505050506040513d60608110156101e057600080fd5b810190808051906020019092919080519060200190929190805190602001909291905050509550955095503033349250925092508583868487858494509b509b509b509b509b509b505050505050509091929394955600a165627a7a72305820c9ec566747f7c81af44d23293bc4feaaa84b1f02eb61d603c5786d7f95c8584d0029"); 36 | } 37 | 38 | 39 | @Test 40 | public void TestEncodeTx(){ 41 | PrivateKey privateKey = new PrivateKeyECDSA("16183ac81b7888e9c80aa7c586ed9337478fc6d8bf1f11759a9bb0aed41fc987"); 42 | BigInteger nonce = BigInteger.valueOf(99); 43 | List inputParameters = new ArrayList<>(); 44 | inputParameters.add(new Utf8String("2")); 45 | inputParameters.add(new Utf8String("抽奖")); 46 | inputParameters.add(new Utf8String("201901031836")); 47 | inputParameters.add(new Utf8String("iphoneXR")); 48 | inputParameters.add(new Uint256(10)); 49 | inputParameters.add(new Address("0x2b35e1555e3d9e31b0099733cda6a6dfa88bbfdd")); 50 | List> outputParameters = new ArrayList<>(); 51 | outputParameters.add(new TypeReference() {}); 52 | Function function = new Function("initLottery", inputParameters, outputParameters); 53 | RawTransaction tx = TransactionUtil.createCallContractTransaction(nonce, "0xe52c09567254855381395b99decbc84b96de513e", function); 54 | byte[] message = TransactionUtil.encode(tx, privateKey); 55 | String rawTxmsg = Numeric.toHexString(message); 56 | log.info(rawTxmsg); 57 | } 58 | 59 | @Test 60 | public void TestSignature() throws SignatureException { 61 | String data = "608060405234801561001057600080fd5b50610263806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632b823efe14610046575b600080fd5b34801561005257600080fd5b5061005b610144565b604051808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838152602001828152602001965050505050505060405180910390f35b600080600080600080600080600080600080735e72914535f202659083db3a02c984188fa26e9f63c6b6e6396040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160606040518083038186803b1580156101b657600080fd5b505af41580156101ca573d6000803e3d6000fd5b505050506040513d60608110156101e057600080fd5b810190808051906020019092919080519060200190929190805190602001909291905050509550955095503033349250925092508583868487858494509b509b509b509b509b509b505050505050509091929394955600a165627a7a72305820c9ec566747f7c81af44d23293bc4feaaa84b1f02eb61d603c5786d7f95c8584d0029"; 62 | String toAddress = "0xb6afcb28900292b497c61a773b6110c4addc65e0"; 63 | byte v = 28; 64 | byte[] r = Numeric.hexStringToByteArray("f4cf29659ae8eedb66630e09f62b902f5ee9b87c810397f16c1b032c759f477c"); 65 | byte[] s = Numeric.hexStringToByteArray("71e763b986b17c085917f6e0e8e10104d908fd3532bd2dfec32287c83cf27ddf"); 66 | BigInteger value = Numeric.toBigInt("0x010000000000000000"); 67 | RawTransaction rtx = RawTransaction.createTransaction(BigInteger.valueOf(1), BigInteger.valueOf(0), BigInteger.valueOf(0), toAddress, value, data); 68 | Signature sig = new SignatureECDSA(v, r, s); 69 | String txMsg = Numeric.toHexString(TransactionUtil.encodeWithSig(rtx, sig)); 70 | SignedRawTransaction signedRawTransaction = (SignedRawTransaction) TransactionDecoder.decode(txMsg); 71 | String signerAddress = signedRawTransaction.getFrom(); 72 | log.info("{}", signerAddress); 73 | Assert.assertEquals(signerAddress, "0x0b1f66c31e7ee5674003a304c4c40e078f07c469"); 74 | } 75 | 76 | 77 | @Test 78 | public void TestHashTx(){ 79 | String msg = "f8862b8085174876e80094e577c05de9be1818a322b7e61227c7a4d54c543480a4179d375c00000000000000000000000000000000000000000000000000000000000000021ca0936f5aeffe0d60811812c1bd6152707a0e3f0aa093a198fb6cade2ea4f41b9daa0421a414a6981f19e6333710d38ec81dfc05fc9d895257ec60b78c17856be8a13"; 80 | byte[] txBytes = Numeric.hexStringToByteArray(msg); 81 | String txHash = CryptoUtil.txHash(txBytes); 82 | log.info("txHash {}", txHash); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/DemoEventCallBack.java: -------------------------------------------------------------------------------- 1 | import com.genesis.api.EventCallBack; 2 | import lombok.extern.slf4j.Slf4j; 3 | import org.web3j.abi.datatypes.Event; 4 | import org.web3j.abi.datatypes.Type; 5 | 6 | import java.util.List; 7 | 8 | @Slf4j 9 | public class DemoEventCallBack extends EventCallBack { 10 | 11 | /** 12 | * @param pollTime 轮询时间 13 | * @param event 监听事件的格式 14 | */ 15 | public DemoEventCallBack(int pollTime, Event event) { 16 | super(pollTime, event); 17 | } 18 | 19 | public DemoEventCallBack(Event event) { 20 | super(event); 21 | } 22 | 23 | /** 24 | * 业务处理 25 | * 26 | * @param decodeResult 27 | */ 28 | @Override 29 | public void handleEvent(List decodeResult) { 30 | log.info("decoded result {}", decodeResult); 31 | log.info("start to Process Data"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/NodeApiTest.java: -------------------------------------------------------------------------------- 1 | 2 | import com.genesis.api.CryptoUtil; 3 | import com.genesis.api.NodeSrv; 4 | import com.genesis.api.TransactionUtil; 5 | import com.genesis.api.bean.model.DeployContractResult; 6 | import com.genesis.api.bean.model.QueryBlockTransactionHashs; 7 | import com.genesis.api.bean.model.QueryKVResult; 8 | import com.genesis.api.bean.model.QueryPrefixKVs; 9 | import com.genesis.api.bean.model.QueryTransactionPayload; 10 | import com.genesis.api.bean.model.QueryTransactionReceipt; 11 | import com.genesis.api.bean.model.RawTransactionData; 12 | import com.genesis.api.bean.model.SendTransactionResult; 13 | import com.genesis.api.bean.model.TransactionKVTx; 14 | import com.genesis.api.crypto.PrivateKey; 15 | import com.genesis.api.crypto.PrivateKeyECDSA; 16 | import com.genesis.api.crypto.Signature; 17 | import lombok.extern.slf4j.Slf4j; 18 | import org.bouncycastle.crypto.CryptoException; 19 | import org.bouncycastle.util.encoders.Hex; 20 | import org.junit.Assert; 21 | import org.junit.BeforeClass; 22 | import org.junit.Test; 23 | import org.web3j.abi.FunctionReturnDecoder; 24 | import org.web3j.abi.TypeReference; 25 | import org.web3j.abi.datatypes.*; 26 | import org.web3j.abi.datatypes.generated.Uint256; 27 | import org.web3j.crypto.RawTransaction; 28 | import org.web3j.rlp.*; 29 | import org.web3j.utils.Numeric; 30 | 31 | import java.io.IOException; 32 | import java.math.BigInteger; 33 | import java.util.Arrays; 34 | import java.util.List; 35 | 36 | 37 | @Slf4j 38 | public class NodeApiTest { 39 | 40 | //智能合约二进制代码 41 | private static final String bCode = "608060405234801561001057600080fd5b50610397806100206000396000f3fe608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632c560ec01461005c5780635d46732614610087578063f1215d25146100ec575b600080fd5b34801561006857600080fd5b506100716101be565b6040518082815260200191505060405180910390f35b34801561009357600080fd5b506100d6600480360360208110156100aa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610204565b6040518082815260200191505060405180910390f35b3480156100f857600080fd5b506101bc6004803603604081101561010f57600080fd5b81019080803590602001909291908035906020019064010000000081111561013657600080fd5b82018360208201111561014857600080fd5b8035906020019184600183028401116401000000008311171561016a57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061024c565b005b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905090565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f643e927b32d5bfd08eccd2fcbd97057ad413850f857a2359639114e8e8dd3d7b338383604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561032b578082015181840152602081019050610310565b50505050905090810190601f1680156103585780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a1505056fea165627a7a7230582013df8e0098a02a38849c352af9c092166e0876d055eb7c2f976fdf21e75a849b0029"; 42 | //私钥 43 | private static final String PRIVATE_KEY = "6d79264403d667f75caf2f1dca6412c6922b15433b515850c5c00a2aa12010e9"; 44 | private static final String PRIVATE_KEY2 = "37F1055CFD87F5B071AA6293DA4C8B79DB57949C07CD87BA20493A0C9D254DE9"; 45 | //节点服务 46 | private static NodeSrv nodeSrv; 47 | //合约地址 48 | private static String contractAddress; 49 | //交易哈希 50 | private static String existTxHash; 51 | //交易块高度 52 | private static BigInteger blockHeight; 53 | private static PrivateKey privKey = new PrivateKeyECDSA(PRIVATE_KEY); 54 | private static PrivateKey privKey2 = new PrivateKeyECDSA(PRIVATE_KEY2); 55 | 56 | @BeforeClass 57 | public static void init() throws IOException{ 58 | nodeSrv = new NodeSrv("http://localhost:46657/"); 59 | } 60 | 61 | @Test 62 | public void test() throws IOException, InterruptedException, CryptoException{ 63 | // nonce 64 | testQueryNonce(); 65 | testQueryPendingNonce(); 66 | 67 | // contract 68 | testDeployContract(); 69 | testCallContactWithSig(); 70 | testCallContactAsync(); 71 | testCallContactAsyncTxs(); 72 | testQueryContract(); 73 | testQueryContractWithSig(); 74 | testQueryRecp(); 75 | testRlp(); 76 | testQueryTxHashsByHeight(); 77 | testQueryRawTransactionByHash(); 78 | 79 | // KV 80 | testPutKV(); 81 | queryKV(); 82 | testPutKVAsync(); 83 | queryKV2(); 84 | queryKVNotExist(); 85 | testPutKVs(); 86 | queryPreKV(); 87 | queryPreKV2(); 88 | 89 | // payload 90 | testSendPayload(); 91 | } 92 | 93 | // @Test 94 | public void testDeployContract() throws IOException, InterruptedException { 95 | String accountAddress = privKey.getAddress(); 96 | //获取nonce 97 | int nonce = nodeSrv.queryPendingNonce(accountAddress); 98 | log.info("nonce {}" , nonce); 99 | //部署合约 100 | DeployContractResult deployRes = nodeSrv.deployContractCompl(bCode, Arrays.asList(), privKey, BigInteger.valueOf(nonce)); 101 | contractAddress = deployRes.getContractAddr(); 102 | log.info("contractAddr {}",contractAddress); 103 | log.info("deploy txHash {}",deployRes.getTxHash()); 104 | Thread.sleep(2000); 105 | } 106 | 107 | // @Test 108 | public void testCallContactWithSig() throws IOException, InterruptedException, CryptoException { 109 | String address = privKey.getAddress(); 110 | int nonce = nodeSrv.queryPendingNonce(address); 111 | log.info("nonce {}" , nonce); 112 | 113 | // event 定义 114 | Event DEPOSIT = new Event("deposit", 115 | Arrays.asList(), 116 | Arrays.asList(new TypeReference
() { 117 | }, new TypeReference() { 118 | }, new TypeReference() { 119 | })); 120 | 121 | Function functionDef = new Function("deposit", 122 | Arrays.asList( 123 | new org.web3j.abi.datatypes.generated.Uint256(18), 124 | new org.web3j.abi.datatypes.Utf8String("test1")), 125 | Arrays.asList()); 126 | RawTransaction tx = TransactionUtil.createCallContractTransaction(BigInteger.valueOf(nonce), contractAddress, functionDef); 127 | Signature sig = CryptoUtil.generateSignature(tx, privKey); 128 | String resp = nodeSrv.callContractEvmWithSig(BigInteger.valueOf(nonce), 129 | contractAddress, 130 | functionDef, //函数接口定义 131 | sig, 132 | new DemoEventCallBack(DEPOSIT)); //callBack 133 | existTxHash = resp; 134 | log.info("call contract resp with sig:" + resp); 135 | 136 | Thread.sleep(2000); 137 | } 138 | 139 | 140 | // @Test 141 | public void testCallContactAsync() throws IOException, InterruptedException, CryptoException { 142 | 143 | /** 144 | * 参考test.sol 145 | */ 146 | String address2 = privKey2.getAddress(); 147 | int nonce = nodeSrv.queryPendingNonce(address2); 148 | log.info("nonce {}" , nonce); 149 | 150 | // event 定义 151 | Event DEPOSIT = new Event("Deposit", 152 | Arrays.asList(), 153 | Arrays.asList(new TypeReference
() { 154 | }, new TypeReference() { 155 | }, new TypeReference() { 156 | })); 157 | 158 | Function functionDef = new Function("deposit", 159 | Arrays.asList( 160 | new org.web3j.abi.datatypes.generated.Uint256(19), 161 | new org.web3j.abi.datatypes.Utf8String("test2")), 162 | Arrays.asList()); 163 | 164 | String resp = nodeSrv.callContractEvm(BigInteger.valueOf(nonce), 165 | contractAddress, 166 | functionDef, //函数接口定义 167 | privKey2, 168 | new DemoEventCallBack(DEPOSIT), false); //callBack 169 | log.info("call contract resp:" + resp); 170 | 171 | Thread.sleep(2000); 172 | } 173 | 174 | // @Test 175 | public void testCallContactAsyncTxs() throws IOException, InterruptedException, CryptoException { 176 | String address2 = privKey2.getAddress(); 177 | // event 定义 178 | Function functionDef = new Function("deposit", 179 | Arrays.asList( 180 | new org.web3j.abi.datatypes.generated.Uint256(19), 181 | new org.web3j.abi.datatypes.Utf8String("test2")), 182 | Arrays.asList()); 183 | for (int i=0;i<20;i++) { 184 | int nonce = nodeSrv.queryPendingNonce(address2); 185 | log.info("nonce {}" , nonce); 186 | String resp = nodeSrv.callContractEvm(BigInteger.valueOf(nonce), 187 | contractAddress, 188 | functionDef, //函数接口定义 189 | privKey2, 190 | null, false); //callBack 191 | log.info("call contract resp:" + resp); 192 | } 193 | } 194 | 195 | // @Test 196 | public void testQueryContract() throws IOException, InterruptedException { 197 | String address = privKey.getAddress(); 198 | int nonce = nodeSrv.queryPendingNonce(address); 199 | log.info("nonce {}" , nonce); 200 | Function functionDef = new Function("queryInfo", 201 | Arrays.asList(), 202 | Arrays.asList(new TypeReference() { 203 | })); 204 | List resp = nodeSrv.queryContract(BigInteger.valueOf(nonce), contractAddress, functionDef, privKey); 205 | log.info("query resp {}", resp.get(0).getValue()); 206 | Thread.sleep(2000); 207 | } 208 | 209 | // @Test 210 | public void testQueryContractWithSig() throws IOException, CryptoException { 211 | String address2 = privKey2.getAddress(); 212 | int nonce = nodeSrv.queryPendingNonce(address2); 213 | log.info("nonce {}" , nonce); 214 | Function functionDef = new Function("queryInfoWithAddress", 215 | Arrays.asList(new org.web3j.abi.datatypes.Address(address2)), 216 | Arrays.asList(new TypeReference() { 217 | })); 218 | RawTransaction tx = TransactionUtil.createCallContractTransaction(BigInteger.valueOf(nonce), contractAddress, functionDef); 219 | Signature sig = CryptoUtil.generateSignature(tx, privKey2); 220 | List resp = nodeSrv.queryContractWithSig(BigInteger.valueOf(nonce), contractAddress, functionDef, BigInteger.valueOf(0), sig); 221 | log.info("query resp2 {}", resp.get(0).getValue()); 222 | } 223 | 224 | // @Test 225 | public void testQueryNonce() throws IOException { 226 | String address = privKey.getAddress(); 227 | int resp = nodeSrv.queryNonce(address); 228 | log.info("testQueryNonce {}", resp); 229 | } 230 | 231 | // @Test 232 | public void testQueryPendingNonce() throws IOException{ 233 | String address = privKey.getAddress(); 234 | int resp = nodeSrv.queryPendingNonce(address); 235 | log.info("testQueryPendingNonce {}", resp); 236 | } 237 | 238 | // @Test 239 | public void testQueryRecp() throws IOException { 240 | log.info("receipt_hash:" + existTxHash); 241 | QueryTransactionReceipt resp = nodeSrv.queryReceiptRaw(existTxHash); 242 | Assert.assertNotNull(resp); 243 | blockHeight = resp.getHeight(); 244 | log.info("receipt:" + resp); 245 | } 246 | 247 | // @Test 248 | public void testRlp() { 249 | byte[] rlpEncoded = Numeric.hexStringToByteArray("80"); 250 | RlpList decode = RlpDecoder.decode(rlpEncoded); 251 | String hexValue = ((RlpString) decode.getValues().get(0)).asString(); 252 | Assert.assertEquals(hexValue, "0x"); 253 | 254 | // event 定义 255 | Event DEPOSIT = new Event("Deposit", 256 | Arrays.asList(), 257 | Arrays.asList(new TypeReference
() { 258 | }, new TypeReference
() { 259 | }, new TypeReference() { 260 | })); 261 | 262 | byte[] rlp = Hex.decode("f9025f80825318b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000020000000000000009453e952a018d2aa979ae15e82300e95621a47b644940000000000000000000000000000000000000000f90124f9012194ee463ba03224a14706728cbede6abcd76ed4e7cce1a04db2efd59f3e5a97173b6b255ce6ebaea50d7f27654cf39705afa269e2529b3ab8a0000000000000000000000000402c3035ec3ec9f320ffc4b20d22b1f39b6caddb000000000000000000000000402c3035ec3ec9f320ffc4b20d22b1f39b6caddb00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003616263000000000000000000000000000000000000000000000000000000000083118c30a046a2d7c505b3394f845061287a7630e106910b8c6a1e4d2185366eb870318deb80a000000000000000000000000025bd77dee7698dc5d730dadb00753dd1177cd87f8082531883014201"); 263 | QueryTransactionReceipt res = new QueryTransactionReceipt(rlp); 264 | res.getLogInfoList().forEach(i -> { 265 | List respp = FunctionReturnDecoder.decode(Hex.toHexString(i.getData()), DEPOSIT.getNonIndexedParameters()); 266 | Assert.assertEquals(respp.toString(), "[0x402c3035ec3ec9f320ffc4b20d22b1f39b6caddb, 0x402c3035ec3ec9f320ffc4b20d22b1f39b6caddb, abc]"); 267 | }); 268 | } 269 | 270 | // @Test 271 | public void testQueryTxHashsByHeight() throws IOException { 272 | QueryBlockTransactionHashs txHashs = nodeSrv.queryTransactionHashsByHeight(blockHeight); 273 | log.info("count:"+txHashs.getCount()); 274 | for (int i =0;i uint256) OrgInfo; 11 | 12 | constructor() public { 13 | } 14 | 15 | function deposit(uint256 _age,string memory _comment) public { 16 | OrgInfo[msg.sender] = _age; 17 | emit Deposit(msg.sender,_age,_comment); 18 | } 19 | 20 | 21 | function queryInfo() public view returns(uint256){ 22 | return OrgInfo[msg.sender]; 23 | } 24 | 25 | function queryInfoWithAddress(address _owner) public view returns(uint256){ 26 | return OrgInfo[_owner]; 27 | } 28 | } --------------------------------------------------------------------------------