├── .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 | }
--------------------------------------------------------------------------------