diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f1c402..3af98c60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ All notable changes to this project will be documented in this file. +# [v0.22.2](https://github.com/cryptape/cita-sdk-java/compare/v0.22.1...v0.22.2) (2019-04-09) + +### Feature + +* refactor http json response handle method + # [v0.22.1](https://github.com/cryptape/cita-sdk-java/compare/v0.22...v0.22.1) (2019-03-29) ### Feature diff --git a/README.md b/README.md index eb406bfa..69fb18fe 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ cita-sdk-java, originally adapted from Ethereum web3j, is a Java library for working with Smart Contract and integrating with clients on CITA. +For detailed documentation, see [documentation](docs/index.md). + ## Features - Complete implementation of CITA JSON-RPC API over HTTP. @@ -27,12 +29,12 @@ maven com.cryptape.cita core - 0.22.1 + 0.22.2 ``` Gradle ``` -compile 'com.cryptape.cita:core:0.22.1' +compile 'com.cryptape.cita:core:0.22.2' ``` Install manually @@ -144,9 +146,12 @@ Please find complete code in [TokenAccountExample](https://github.com/cryptape/c ## 简介 cita-sdk-java 是对以太坊 Web3j 进行改写,适配 CITA 的一个 Java 开发包。cita-sdk-java 集成了与 CITA 客户端交互的功能,可以用来对 CITA 发送交易,系统配置,信息查询。 + +开发请参考[详细文档](docs/index.md)。 + ## 特性 - 通过 HTTP 协议,实现了 CITA 所定义的所有 JSON-RPC 方法。 -- 可以通过 Solidity 智能合约生成该合约的 Java 类。这个智能合约的 Java 类作为 java 对只能合约的包裹层,可以使开发和通过 java 方便地对智能合约进行部署和合约方法的调用(支持Solidity 和 Truffle 的格式)。 +- 可以通过 Solidity 智能合约生成该合约的 Java 类。这个智能合约的 Java 类作为 java 对智能合约的包裹层,可以使开发和通过 java 方便地对智能合约进行部署和合约方法的调用(支持Solidity 和 Truffle 的格式)。 - 适配安卓 ## 开始 @@ -161,12 +166,12 @@ Gradle 4.3 com.cryptape.cita core - 0.22.1 + 0.22.2 ``` Gradle ``` -compile 'com.cryptape.cita:core:0.22.1' +compile 'com.cryptape.cita:core:0.22.2' ``` 手动安装 diff --git a/core/src/main/java/com/cryptape/cita/protocol/account/Account.java b/core/src/main/java/com/cryptape/cita/protocol/account/Account.java index c6762fd7..de3592ab 100644 --- a/core/src/main/java/com/cryptape/cita/protocol/account/Account.java +++ b/core/src/main/java/com/cryptape/cita/protocol/account/Account.java @@ -59,7 +59,7 @@ public AppSendTransaction deploy( version, chainId, value); } - public Future deployAsync( + public Flowable deployAsync( File contractFile, String nonce, long quota, int version, BigInteger chainId, String value) throws IOException, InterruptedException, CompiledContract.ContractCompileError { @@ -70,7 +70,6 @@ public Future deployAsync( version, chainId, value); } - // eth_call: nonce and quota is null // sendTransaction: nonce and quota is necessary public Object callContract( String contractAddress, String funcName, @@ -101,7 +100,7 @@ public Object callContract( Function func; if (functionAbi.isConstant()) { - // eth_call + // call List retsType = new ArrayList<>(); List> retsTypeRef = new ArrayList<>(); List outputs = functionAbi.getOutputs(); @@ -111,7 +110,7 @@ public Object callContract( retsTypeRef.add(retType.getTypeReference()); } func = new Function(functionAbi.getName(), params, retsTypeRef); - return ethCall(contractAddress, func, retsType); + return appCall(contractAddress, func, retsType); } else { // send_transaction func = new Function(functionAbi.getName(), params, Collections.emptyList()); @@ -120,7 +119,7 @@ public Object callContract( } } - public Object ethCall(String contractAddress, Function func, List retsType) + public Object appCall(String contractAddress, Function func, List retsType) throws IOException { String data = FunctionEncoder.encode(func); AppCall call = this.service.appCall(new Call(this.transactionManager.getFromAddress(), diff --git a/core/src/main/java/com/cryptape/cita/protocol/core/methods/response/AppBlock.java b/core/src/main/java/com/cryptape/cita/protocol/core/methods/response/AppBlock.java index d6e45e70..66c8cd26 100644 --- a/core/src/main/java/com/cryptape/cita/protocol/core/methods/response/AppBlock.java +++ b/core/src/main/java/com/cryptape/cita/protocol/core/methods/response/AppBlock.java @@ -1,10 +1,22 @@ package com.cryptape.cita.protocol.core.methods.response; -import java.math.BigInteger; -import java.util.List; - +import com.cryptape.cita.protocol.ObjectMapperFactory; import com.cryptape.cita.protocol.core.Response; import com.cryptape.cita.utils.Numeric; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; public class AppBlock extends Response { @@ -21,187 +33,42 @@ public boolean isEmpty() { } public static class TendermintCommit { - private String commitAddress; - private String commit; + public String commitAddress; + public String commit; public TendermintCommit() {} - public TendermintCommit(String commitAddress, String commit) { - this.commitAddress = commitAddress; - this.commit = commit; - } - - public String getCommitAddress() { - return this.commitAddress; - } - - public void setCommitAddress(String commitAddress) { - this.commitAddress = commitAddress; - } - - public String getCommit() { - return this.commit; - } - - public void setCommit(String commit) { - this.commit = commit; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof TendermintCommit)) { - return false; - } - - TendermintCommit commit = (TendermintCommit) o; - - if (getCommitAddress() != null - ? !getCommitAddress().equals(commit.getCommitAddress()) - : commit.getCommitAddress() != null) { - return false; - } - return (getCommit() != null - ? getCommit().equals(commit.getCommit()) : commit.getCommit() == null); - } - - @Override - public int hashCode() { - int result = getCommitAddress() != null ? getCommitAddress().hashCode() : 0; - result = 31 * result + (getCommit() != null ? getCommit().hashCode() : 0); - return result; - } } public static class Tendermint { - private String proposal; - private String height; - private String round; - private TendermintCommit[] tendermintCommits; + public String proposal; + public String height; + public String round; + public TendermintCommit[] tendermintCommits; public Tendermint() {} - public Tendermint(String proposal, String height, - String round, TendermintCommit[] tendermintCommits) { - this.proposal = proposal; - this.height = height; - this.round = round; - this.tendermintCommits = tendermintCommits; - } - - public String getProposal() { - return proposal; - } - - public void setProposal(String proposal) { - this.proposal = proposal; - } - - public String getHeight() { - return height; - } - - public void setHeight(String height) { - this.height = height; - } - - public String getRound() { - return round; - } - - public void setRound(String round) { - this.round = round; - } - - public TendermintCommit[] getTendermintCommits() { - return this.tendermintCommits; - } - - public void setTendermintCommits( - TendermintCommit[] tendermintCommits) { - this.tendermintCommits = tendermintCommits; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - - if (!(o instanceof Tendermint)) { - return false; - } - - Tendermint tendermint = (Tendermint) o; + } - if (getHeight() != null - ? !getHeight().equals(tendermint.getHeight()) - : tendermint.getHeight() != null) { - return false; - } + public static class Bft { - if (getProposal() != null - ? !getProposal().equals(tendermint.getProposal()) - : tendermint.getProposal() != null) { - return false; - } + public String proposal; + public int height; + public int round; + public Map commits; - return (getRound() != null - ? getRound().equals(tendermint.getRound()) - : tendermint.getRound() == null); - } + public Bft() {} - @Override - public int hashCode() { - int result = getProposal() != null ? getProposal().hashCode() : 0; - result = 31 * result + (getHeight() != null ? getHeight().hashCode() : 0); - result = 31 * result + (getRound() != null ? getRound().hashCode() : 0); - return result; - } } public static class Proof { - private Tendermint tendermint; - - public Proof() { - } - - public Proof(Tendermint tendermint) { - this.tendermint = tendermint; - } - - public Tendermint getTendermint() { - return this.tendermint; - } - - public void setTendermint(Tendermint tendermint) { - this.tendermint = tendermint; - } - - @Override - public int hashCode() { - return getTendermint() != null - ? getTendermint().hashCode() : 0; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Proof)) { - return false; - } + public Tendermint tendermint; + @JsonProperty("Bft") + public Bft bft; - Proof proof = (Proof) o; + public Proof() {} - return (getTendermint() != null - ? getTendermint().equals(proof.getTendermint()) - : proof.getTendermint() == null); - } } @@ -212,7 +79,7 @@ public static class Header { private String stateRoot; private String transactionsRoot; private String receiptsRoot; - private String gasUsed; + private String quotaUsed; private Proof proof; private String proposer; @@ -222,14 +89,14 @@ public Header() { public Header(long timestamp, String prevHash, String number, String stateRoot, String transactionsRoot, String receiptsRoot, - String gasUsed, Proof proof, String proposer) { + String quotaUsed, Proof proof, String proposer) { this.timestamp = timestamp; this.prevHash = prevHash; this.number = number; this.stateRoot = stateRoot; this.transactionsRoot = transactionsRoot; this.receiptsRoot = receiptsRoot; - this.gasUsed = gasUsed; + this.quotaUsed = quotaUsed; this.proof = proof; this.proposer = proposer; } @@ -291,15 +158,15 @@ public void setReceiptsRoot(String receiptsRoot) { } public BigInteger getGasUsedDec() { - return Numeric.decodeQuantity(gasUsed); + return Numeric.decodeQuantity(quotaUsed); } - public String getGasUsed() { - return gasUsed; + public String getQuotaUsed() { + return quotaUsed; } - public void setGasUsed(String gasUsed) { - this.gasUsed = gasUsed; + public void setQuotaUsed(String quotaUsed) { + this.quotaUsed = quotaUsed; } public Proof getProof() { @@ -318,78 +185,6 @@ public void setProposer(String proposer) { this.proposer = proposer; } - @Override - public int hashCode() { - int result = 0; - result = 31 * result + (getPrevHash() != null - ? getPrevHash().hashCode() : 0); - result = 31 * result + (getNumber() != null - ? getNumber().hashCode() : 0); - result = 31 * result + (getStateRoot() != null - ? getStateRoot().hashCode() : 0); - result = 31 * result + (getTransactionsRoot() != null - ? getTransactionsRoot().hashCode() : 0); - result = 31 * result + (getReceiptsRoot() != null - ? getReceiptsRoot().hashCode() : 0); - result = 31 * result + (getGasUsed() != null - ? getGasUsed().hashCode() : 0); - result = 31 * result + (getProof() != null - ? getProof().hashCode() : 0); - return result; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Header)) { - return false; - } - - Header header = (Header) o; - - if (getTimestamp() != null - ? !getTimestamp().equals(header.getTimestamp()) - : header.getTimestamp() != null) { - return false; - } - if (getPrevHash() != null - ? !getPrevHash().equals(header.getPrevHash()) - : header.getPrevHash() != null) { - return false; - } - if (getNumber() != null - ? !getNumber().equals(header.getNumber()) : header.getNumber() != null) { - return false; - } - if (getStateRoot() != null - ? !getStateRoot().equals(header.getStateRoot()) - : header.getStateRoot() != null) { - return false; - } - if (getTransactionsRoot() != null - ? !getTransactionsRoot().equals(header.getTransactionsRoot()) - : header.getTransactionsRoot() != null) { - return false; - } - if (getReceiptsRoot() != null - ? !getReceiptsRoot().equals(header.getReceiptsRoot()) - : header.getReceiptsRoot() != null) { - return false; - } - if (getGasUsed() != null - ? !getGasUsed().equals(header.getGasUsed()) : header.getGasUsed() != null) { - return false; - } - if (getReceiptsRoot() != null - ? !getReceiptsRoot().equals(header.getReceiptsRoot()) - : header.getReceiptsRoot() != null) { - return false; - } - return (getProof() != null - ? getProof().equals(header.getProof()) : header.getProof() == null); - } } public static class Body { @@ -406,30 +201,11 @@ public List getTransactions() { return transactions; } + @JsonDeserialize(using = TransactionResultDeserializer.class) public void setTransactions(List transactions) { this.transactions = transactions; } - @Override - public int hashCode() { - int result = getTransactions() != null ? getTransactions().hashCode() : 0; - return result; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Body)) { - return false; - } - - Body body = (Body) o; - return (getTransactions() != null - ? getTransactions().equals(body.getTransactions()) - : body.getTransactions() == null); - } } public static class Block { @@ -480,41 +256,6 @@ public void setBody(Body body) { this.body = body; } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Block)) { - return false; - } - - Block block = (Block) o; - - if (getHeader() != null - ? !getHeader().equals(block.getHeader()) : block.getHeader() != null) { - return false; - } - if (getBody() != null - ? !getBody().equals(block.getBody()) : block.getBody() != null) { - return false; - } - if (getHash() != null - ? !getHash().equals(block.getHash()) : block.getHash() != null) { - return false; - } - return (getVersion() != null - ? getVersion().equals(block.getVersion()) : block.getVersion() == null); - } - - @Override - public int hashCode() { - int result = getVersion() != null ? getVersion().hashCode() : 0; - result = 31 * result + (getHash() != null ? getHash().hashCode() : 0); - result = 31 * result + (getHeader() != null ? getHeader().hashCode() : 0); - result = 31 * result + (getBody() != null ? getBody().hashCode() : 0); - return result; - } } public interface TransactionResult { @@ -537,4 +278,57 @@ public Transaction get() { } } + public static class Hash implements TransactionResult { + private String value; + + public Hash() { + } + + public Hash(String value) { + this.value = value; + } + + @Override + public String get() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + } + + public static class TransactionResultDeserializer + extends JsonDeserializer> { + + private ObjectReader objectReader = ObjectMapperFactory.getObjectReader(); + + @Override + public List deserialize( + JsonParser jsonParser, + DeserializationContext deserializationContext) throws IOException { + + List transactionResults = new ArrayList<>(); + JsonToken nextToken = jsonParser.nextToken(); + + if (nextToken == JsonToken.START_OBJECT) { + Iterator transactionObjectIterator = + objectReader.readValues(jsonParser, TransactionObject.class); + while (transactionObjectIterator.hasNext()) { + transactionResults.add(transactionObjectIterator.next()); + } + } else if (nextToken == JsonToken.VALUE_STRING) { + jsonParser.getValueAsString(); + + Iterator transactionHashIterator = + objectReader.readValues(jsonParser, Hash.class); + while (transactionHashIterator.hasNext()) { + transactionResults.add(transactionHashIterator.next()); + } + } + return transactionResults; + } + } + } diff --git a/core/src/main/java/com/cryptape/cita/protocol/system/CITASystemContract.java b/core/src/main/java/com/cryptape/cita/protocol/system/CITASystemContract.java index d88d2cfa..c0a25ba8 100644 --- a/core/src/main/java/com/cryptape/cita/protocol/system/CITASystemContract.java +++ b/core/src/main/java/com/cryptape/cita/protocol/system/CITASystemContract.java @@ -227,5 +227,5 @@ static AppCall sendCall(String from, String addr, return service.appCall(call, DEFAULT_BLOCK_PARAMETER).send(); } - int getQuotaPrice(String from) throws IOException; + long getQuotaPrice(String from) throws IOException; } \ No newline at end of file diff --git a/core/src/main/java/com/cryptape/cita/protocol/system/CITAjSystemContract.java b/core/src/main/java/com/cryptape/cita/protocol/system/CITAjSystemContract.java index ee19fd4b..a9c8152b 100644 --- a/core/src/main/java/com/cryptape/cita/protocol/system/CITAjSystemContract.java +++ b/core/src/main/java/com/cryptape/cita/protocol/system/CITAjSystemContract.java @@ -104,14 +104,14 @@ public int stakePermillage(String from) throws IOException { return Integer.parseInt(resultTypes.get(0).getValue().toString()); } - public int getQuotaPrice(String from) throws IOException { + public long getQuotaPrice(String from) throws IOException { String callData = CITASystemContract.encodeCall(PRICE_MANAGER_GET_QUOTA_PRICE); AppCall callResult = CITASystemContract.sendCall( from, PRICE_MANAGER_ADDR, callData, service); - List> outputParamters + List> outputParameters = Collections.singletonList(new TypeReference() {}); - List resultTypes = CITASystemContract.decodeCallResult(callResult, outputParamters); - return Integer.parseInt( + List resultTypes = CITASystemContract.decodeCallResult(callResult, outputParameters); + return Long.parseLong( resultTypes.get(0).getValue().toString()); } diff --git a/core/src/main/java/com/cryptape/cita/tx/RawTransactionManager.java b/core/src/main/java/com/cryptape/cita/tx/RawTransactionManager.java index 5c427a19..90ecd99f 100644 --- a/core/src/main/java/com/cryptape/cita/tx/RawTransactionManager.java +++ b/core/src/main/java/com/cryptape/cita/tx/RawTransactionManager.java @@ -14,6 +14,7 @@ import com.cryptape.cita.protocol.core.methods.response.AppGetTransactionCount; import com.cryptape.cita.protocol.core.methods.response.AppSendTransaction; import com.cryptape.cita.utils.Numeric; +import io.reactivex.Flowable; public class RawTransactionManager extends TransactionManager { @@ -108,7 +109,7 @@ public AppSendTransaction sendTransaction( return citaj.appSendRawTransaction(rawTx).send(); } - public Future sendTransactionAsync( + public Flowable sendTransactionAsync( String to, String data, long quota, String nonce, long validUntilBlock, int version, BigInteger chainId, String value) throws IOException { @@ -124,7 +125,7 @@ public Future sendTransactionAsync( } else if (this.signature != null) { rawTx = transaction.sign(this.signature); } - return citaj.appSendRawTransaction(rawTx).sendAsync(); + return citaj.appSendRawTransaction(rawTx).flowable(); } @Override diff --git a/core/src/main/java/com/cryptape/cita/tx/TransactionManager.java b/core/src/main/java/com/cryptape/cita/tx/TransactionManager.java index 110ed81b..280a7c5d 100644 --- a/core/src/main/java/com/cryptape/cita/tx/TransactionManager.java +++ b/core/src/main/java/com/cryptape/cita/tx/TransactionManager.java @@ -12,11 +12,10 @@ import com.cryptape.cita.protocol.exceptions.TransactionException; /** - * Transaction manager abstraction for executing transactions with Ethereum client via + * Transaction manager abstraction for executing transactions with CITA client via * various mechanisms. */ -///TODO this class includes ethereum methods: remove them later public abstract class TransactionManager { public static final int DEFAULT_POLLING_ATTEMPTS_PER_TX_HASH = 40; diff --git a/core/src/test/java/com/cryptape/cita/protocol/core/EqualsVerifierResponseTest.java b/core/src/test/java/com/cryptape/cita/protocol/core/EqualsVerifierResponseTest.java deleted file mode 100644 index 0ad1dd39..00000000 --- a/core/src/test/java/com/cryptape/cita/protocol/core/EqualsVerifierResponseTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.cryptape.cita.protocol.core; - -import com.cryptape.cita.protocol.core.methods.response.AppBlock; -import com.cryptape.cita.protocol.core.methods.response.AppLog; -import com.cryptape.cita.protocol.core.methods.response.Log; -import com.cryptape.cita.protocol.core.methods.response.TransactionReceipt; -import nl.jqno.equalsverifier.EqualsVerifier; -import nl.jqno.equalsverifier.Warning; -import org.junit.Test; - -import com.cryptape.cita.protocol.core.methods.response.AbiDefinition; -import com.cryptape.cita.protocol.core.methods.response.Transaction; - -public class EqualsVerifierResponseTest { - - @Test - public void testBlock() { - EqualsVerifier.forClass(AppBlock.Block.class) - .suppress(Warning.NONFINAL_FIELDS) - .suppress(Warning.STRICT_INHERITANCE) - .verify(); - } - - @Test - public void testTransaction() { - EqualsVerifier.forClass(Transaction.class) - .suppress(Warning.NONFINAL_FIELDS) - .suppress(Warning.STRICT_INHERITANCE) - .verify(); - } - - @Test - public void testTransactionReceipt() { - EqualsVerifier.forClass(TransactionReceipt.class) - .suppress(Warning.NONFINAL_FIELDS) - .suppress(Warning.STRICT_INHERITANCE) - .verify(); - } - - @Test - public void testLog() { - EqualsVerifier.forClass(Log.class) - .suppress(Warning.NONFINAL_FIELDS) - .suppress(Warning.STRICT_INHERITANCE) - .verify(); - } - - @Test - public void testAbiDefinition() { - EqualsVerifier.forClass(AbiDefinition.class) - .suppress(Warning.NONFINAL_FIELDS) - .suppress(Warning.STRICT_INHERITANCE) - .verify(); - } - - @Test - public void testAbiDefinitionNamedType() { - EqualsVerifier.forClass(AbiDefinition.NamedType.class) - .suppress(Warning.NONFINAL_FIELDS) - .suppress(Warning.STRICT_INHERITANCE) - .verify(); - } - - @Test - public void testHash() { - EqualsVerifier.forClass(AppLog.Hash.class) - .suppress(Warning.NONFINAL_FIELDS) - .suppress(Warning.STRICT_INHERITANCE) - .verify(); - } - - @Test - public void testTransactionObject() { - EqualsVerifier.forClass(AppBlock.TransactionObject.class) - .suppress(Warning.NONFINAL_FIELDS) - .suppress(Warning.STRICT_INHERITANCE) - .verify(); - } - - @Test - public void testError() { - EqualsVerifier.forClass(Response.Error.class) - .suppress(Warning.NONFINAL_FIELDS) - .suppress(Warning.STRICT_INHERITANCE) - .verify(); - } -} diff --git a/docs/account.md b/docs/account.md new file mode 100644 index 00000000..27b052dd --- /dev/null +++ b/docs/account.md @@ -0,0 +1,94 @@ +## Account + +Account 封装了 TransactionManager,通过 CITAj 和账户的私钥进行实例化。 + +Account 使用了 CompiledContract 类,可以直接读取 Solidity 合约文件,生成 ABI 和 BIN 文件以供 TransactionManager 使用。 + +* [New account](#new-account) +* [Deploy contract](#deploy-contract) +* [Call Contract](#call-contract) + +### New account + +**方法名** + +`Account(String privateKey, CITAj service)` + +实例化Account对象。 + +**参数** + +* privateKey - 发送交易地址的私钥 +* service - CITAj 实例 + +**返回值** + +Account + +**示例** + +``` +String privateKey = "{private key}"; +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +Account account = new Account(privateKey, service); +``` +### Deploy contract + +**方法名** + +`AppSendTransaction deploy(File contractFile, BigInteger nonce, long quota, int version, int chainId, String value)` + +部署合约。 + +**参数** + +* contractFile - solidity智能合约文件 +* nonce - 随机数用于防止重放攻击 +* quota - 用户支付矿工的费用 +* version - 链的版本信息 +* chainId - 链Id +* value - 交易中原生token的数量 + +**返回值** + +AppSendTransaction + +**示例** +``` +String privateKey = "{private key}"; +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +Account account = new Account(privateKey, service); +AppSendTransaction appSendTransaction = account.deploy(new File(path), randomNonce(), quota, version, chainId, value); +``` + +### Call contract + +**方法名** + +`Object callContract(String contractAddress, String funcName, BigInteger nonce, long quota, int version, int chainId, String value, Object... args)` + +调用合约方法,根据Abi中对方法的定义判断使用sendRawTransaction还是app_call。 + +**参数** + +* contractAddress - 合约地址 +* funcName - 合约方法名称 +* nonce - 随机数用于防止重放攻击 +* quota - 用户支付矿工的费用 +* version - 链的版本信息 +* chainId - 链Id +* value - 交易中原生token的数量 +* args - 合约方法参数 + +**返回值** + +Object + +**示例** + +``` +String privateKey = "{private key}"; +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +Account account = new Account(privateKey, service); +AppSendTransaction appSendTransaction = (AppSendTransaction) account.callContract(contractAddress, transfer, randomNonce(), quota, version, chainId, value, toAddress, amount); +``` \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..2ef4a712 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,19 @@ +# cita-sdk-java API 文档 + +## 概述: + +cita-sdk-java 是对于 CITA 进行交互的 Java SDK 包,cita-sdk-java 使用 JSON-RPC 协议对 CITA 网络节点发送包含方法名和参数的请求,请求可以是转账或者合约的部署和调用。 + +在 cita-sdk-java 中,一共有三种方式实现和链上节点的交互: + +第一种: 通过 cita-sdk-java 定义的方法手动输入包括合约的Abi和Bin来进行操作。 + +第二种: 通过codeGen工具将Solidity合约生成Java类,该Java类继承自Contract类并包含合约中定义的所有方法。 +相比于第一种方法的手动拼接参数,这个方式可以不用生成Abi和Bin文件而直接通过Java类中的方法来进行合约的部署和调用。 + +第三种: 通过封装在Account中的方法来构建并发送交易,Account会实例化TransactionManager,TransactionManager 提供了异步和同步方式对合约进行部署和调用。 + +## 目录: +1. [JSON-RPC](jsonrpc.md) +2. [Transaction](transaction.md) +3. [Account](account.md) \ No newline at end of file diff --git a/docs/jsonrpc.md b/docs/jsonrpc.md new file mode 100644 index 00000000..27b729bf --- /dev/null +++ b/docs/jsonrpc.md @@ -0,0 +1,544 @@ +## JSONRPC + +CITAj 接口继承了 CITA 和 CITAjRx 两个接口,CITAj 的实现类(比如JsonRpc2_0CITAj),提供了方法以发送交易的方式对合约进行部署和函数调用。 + +* [Build CITAj](#build-citaj) +* [netPeer](#netpeer) +* [appMetaData](#appmetadata) +* [appBlockNumber](#appblocknumber) +* [appGetBalance](#appgetbalance) +* [appGetAbi](#appgetabi) +* [appGetTransactionCount](#appgettransactioncount) +* [appGetCode](#appgetcode) +* [appSendRawTransaction](#appsendrawtransaction) +* [appCall](#appcall) +* [appGetBlockByHash](#appgetblockbyhash) +* [appGetBlockByNumber](#appgetblockbynumber) +* [appGetTransactionByHash](#appgettransactionbyhash) +* [appGetTransactionReceipt](#appgettransactionreceipt) +* [appNewBlockFilter](#appnewblockfilter) +* [appBlockHashObservable](#appblockhashobservable) +* [appNewFilter](#appnewfilter) +* [appUninstallFilter](#appuninstallfilter) +* [appGetFilterChanges](#appgetfilterchanges) +* [appGetFilterLogs](#appgetfilterlogs) +* [appLogObservable](#applogobservable) + +### Build CITAj + +**方法名** + +`CITAj build (CITAjService citaj)` +根据 CITAjService 类型实例化 CITAj。 + +**参数** + +citaj - CITAjService 实例 + +**返回值** + +CITAj 实例 + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +``` + +### netPeer + +**方法名** + +`Request netPeer()` +获取当前连接节点数。 + +**参数** + +无 + +**返回值** + +Request + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +NetPeerCount netPeerCount = service.netPeerCount().send(); +BigInteger peerCount = netPeerCount.getQuantity(); +``` + +### appMetaData + +**方法名** + +`Request appMetaData(DefaultBlockParameter defaultBlockParameter)` +获取指定块高的元数据。 + +**参数** + +* defaultBlockParameter - 块高度的接口:数字或者关键字 + +**返回值** + +Request + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +DefaultBlockParameter defaultParam = DefaultBlockParameter.valueOf("latest"); +AppMetaDataResult result = service.appMetaData(defaultParam).send(); +int chainId = result.chainId; +String chainName = result.chainName; +String genesisTS = result.genesisTimestamp; +``` +### appBlockNumber + +**方法名** + +`Request appBlockNumber()` +获取当前块高度。 + +**参数** + +无 + +**返回值** + +Request + +**示例** + +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +AppBlockNumber result = service.appBlockNumber().send(); +BigInteger blockNumber = result.getBlockNumber(); +``` +### appGetBalance + +**方法名** + +`Request appGetBalance(String address, DefaultBlockParameter defaultBlockParameter))` + +获取地址余额。 + +**参数** + +* address - 所要查询的地址 +* defaultBlockParameter - 块高度的接口:数字或者关键字 + +**返回值** + +Request + +**示例** + +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +string addr = "{hex cita address starting with 0x}"; +DefaultBlockParameter defaultBlockParameter = DefaultBlockParamter.valueOf("latest"); +AppGetBalance getBalance = service.appGetBalance(addr, defaultBlockParamter).send(); +BigInteger balance = getBalance.getBalance(); +``` + +### appGetAbi + +**方法名** + +`Request appGetAbi(String contractAddress, DefaultBlockParameter defaultBlockParameter)` +获取合约的Abi。 + +**参数** + +* address - 所要查询Abi的合约地址 +* defaultBlockParameter - 块高度的接口:数字或者关键字 + +**返回值** + +Request + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +string addr = "{hex cita address starting with 0x}"; +DefaultBlockParameter defaultBlockParameter = DefaultBlockParamter.valueOf("latest"); +AppGetAbi getAbi = service.appGetAbi(addr, defaultBlockParamter).send(); +String abi = getAbi.getAbi(); +``` + +### appGetTransactionCount + +**方法名** + +`Request appGetTransactionCount(String address, DefaultBlockParameter defaultBlockParameter)` + +获取账户发送合约数量。 + +**参数** + +* address - 所要查询的地址 +* defaultBlockParameter - 块高度的接口:数字或者关键字 + +**返回值** + +Request + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +string addr = "{hex cita address starting with 0x}"; +DefaultBlockParameter defaultBlockParameter = DefaultBlockParamter.valueOf("latest"); +AppGetTransactionCount getTransactionCount = service.appGetTransactionCount(addr, defaultBlockParamter).send(); +BigInteger txCount = getTransactionCount.getTransactionCount(); +``` + +### appGetCode + +**方法名** + +`Request appGetCode(String address, DefaultBlockParameter defaultBlockParameter)` + +获取合约代码。 + +**参数** + +* address - 所要查询的地址 +* defaultBlockParameter - 块高度的接口:数字或者关键字 + +**返回值** + +Request + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +string addr = "{hex cita address starting with 0x}"; +DefaultBlockParameter defaultBlockParameter = DefaultBlockParamter.valueOf("latest"); +AppGetCode getCode = service.appGetCode(addr, defaultBlockParamter).send(); +Sring code = getCode.getCode(); +``` + +### appSendRawTransaction + +**方法名** + +`Request appSendRawTransaction(String signedTransactionData)` + +向区块链节点发送序列化交易。 + +**参数** + +* signedTransaction - 经过签名的交易数据 + +**返回值** + +Request + +**示例** + +``` +//create a signed transaction +Transaction tx = Transaction.createContractTransaction(BigInteger.valueOf(nonce), this.config.getQuota(), this.currentHeight + 88, 0, chainId, value, this.config.getCode()); +tx.sign(this.config.getPrivateKey(), false, false); + +//instantiate a CITAj and send the transaction +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +AppSendTransaction sendTransaction = service.appSendRawTransaction(tx).send(); + +//get hash of the transaction +String hash = sendTransaction.getHash(); +``` + +### appCall + +**方法名** + +`Request appCall(Call call, DefaultBlockParameter defaultBlockParameter)` + +调用合约接口。 + +**参数** + +* call - 合约方法的call的封装 +* defaultBlockParameter - 块高度的接口:数字或者关键字 + +**返回值** + +Request + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +Call call = new Call(from, to, data); +AppCall appCall = service.appCall(call, DefaultBlockParameter.valueOf("latest")).send(); +String result = call.getValue(); +``` + +### appGetBlockByHash + +**方法名** + +`Request appGetBlockByHash(String blockHash, boolean returnFullTransactionObjects)` + +根据块的哈希值查询块信息。 + +**参数** + +* blockHash - 块高度的接口:数字或者关键字 +* returnFullTransactionObjects - 是否返回交易信息 (True: 返回详细交易列表| False: 只返回交易hash) + +**返回值** + +Request + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +String blockHash = "{block hash to search}"; +AppBlock appBlock = service.appGetBlockByHash(blockHash, false).send(); +``` + +### appGetBlockByNumber + +**方法名** + +`Request appGetBlockByNumber(DefaultBlockParameter defaultBlockParameter, boolean returnFullTransactionObjects)` + +根据块高度查询块信息。 + +**参数** + +* defaultBlockParameter - 块高度的接口:数字或者关键字 +* returnFullTransactionObjects - 是否返回交易信息 (True: 返回详细交易列表| False: 只返回交易hash) + +**返回值** + +Request + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +AppBlock appBlock = service.appGetBlockByHash(DefaultBlockParameter.valueOf("latest"), false).send(); +``` + + +### appGetTransactionByHash + +**方法名** + +`Request appGetTransactionByHash(String transactionHash)` + +根据哈希查询交易信息。 + +**参数** + +* transactionHash - 交易哈希 + +**返回值** + +Request + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +String txHash = "{hash of transactino to be searched}"; +AppTransaction responseTx = service.appGetTransactionByHash(txHash).send(); +``` +### appGetTransactionReceipt + +**方法名** + +`Request appGetTransactionReceipt(String transactionHash)` +根据交易哈希查询交易回执。 + +**参数** + +* transactionHash - 交易哈希 + +**返回值** + +Request + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +String txHash = "{hash of transactino to be searched}"; +AppGetTransactionReceipt txReceipt = service.appGetTransactionReceipt(txHash).send(); +``` + +### appNewBlockFilter + +**方法名** + +`Request appNewBlockFilter()` + +创建一个新的块过滤器,当有新的块写入时通知。 + +**参数** + +无 + +**返回值** + +Request + +**示例** + +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +AppFilter appFilter = service.appNewBlockFilter().send(); +BigInteger filterId = appFilter.getFilterId(); +``` + +### appBlockHashObservable + +**方法名** + +`Observable appBlockHashObservable()` +新建一个Block Filter来监听新增块的哈希,返回一个Observable,可以用交互的形式来监听块高的变化。 + +**参数** + +无 + +**返回值** + +Observable + +**示例** +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +Observable blockFitlerObservable = service.appBlockHashObservable(); +AppLog logs = service.appGetFilterLogs(filterId).send(); +blockFitlerObservable.subscribe(block -> { + System.out.println(block.toString()); +}); +``` + +### appNewFilter + +**方法名** + +`Request appNewFilter(com.cryptape.cita.protocol.core.methods.request.AppFilter appFilter)` + +创建一个新的Event过滤器以用来监听合约中的Event。 + +**参数** + +* appFilter - 针对于 CITA 智能合约event的过滤器(定义在Request中的appFilter) + +**返回值** + +Request + +**示例** + +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +com.cryptape.cita.protocol.core.methods.request.AppFilter appFilter = new AppFilter(fromBlock, toBlock, addresses); +AppFilter appFilter = service.appNewFilter(txHash).send(); +BigInteger filterId = appFilter.getFilterId(); +``` + +### appUninstallFilter + +**方法名** + +`Request appUninstallFilter(BigInteger filterId)` + +移除已部署的过滤器。 + +**参数** + +* filterId - 过滤器Id + +**返回值** + +Request + +**示例** + +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +BigInteger filterId = {your filter Id }; +AppUninstallFilter uninstallFilter = service.appUninstallFilter(filterId).send(); +``` + +### appGetFilterChanges + +**方法名** + +`Request appGetFilterChanges(BigInteger filterId)` + +根据过滤器Id查询log,返回上一次查询之后的所有log。 + +**参数** + +* filterId - 过滤器Id + +**返回值** + +Request + +**示例** + +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +BigInteger filterId = {your filter Id }; +AppLog logs = service.appGetFilterChanges(filterId).send(); +List results = logs.getLogs(); +``` + +### appGetFilterLogs + +**方法名** + +`Request appGetFilterLogs(BigInteger filterId)` + +根据过滤器Id查询log,返回符合输入filter Id的所有log。 + +**参数** + +* filterId - 过滤器Id + +**返回值** + +Request + +**示例** + +``` +CITAj service = CITAj.build(new HttpService("http://127.0.0.1")); +BigInteger filterId = {your filter Id }; +AppLog logs = service.appGetFilterLogs(filterId).send(); +List results = logs.getLogs(); +``` + +### appLogObservable + +**方法名** + +`Observable appLogObservable(AppFilter appFilter)` + +根据AppFilter来安装一个新的Filter用以获取历史log和监听新的Log,返回一个Observable以交互的模式监听Log。 + +**参数** + +* AppFilter - 过滤器可以由`appNewFilter`来新建 + +**返回值** + +Observable + +**示例** +``` +Observable appLogObservable = service.appLogObservable(filter); +Observable reponse = appLogObservable.map( + (log) -> { + EventValues eventValues = staticExtractEventParameters(event, (Log)log); + return (String) eventValues.getIndexedValues().get(0).getValue();; + } +); + +reponse.subscribe(x -> { + System.out.println(x); +}); +``` \ No newline at end of file diff --git a/docs/transaction.md b/docs/transaction.md new file mode 100644 index 00000000..4a45ad89 --- /dev/null +++ b/docs/transaction.md @@ -0,0 +1,229 @@ +## Transaction + +Transction定义在core.request中,用于将交易数据封装并且签名(如果需要),交易数据或者签名后的交易数据被appCall()或者appSendRawTransaction()所使用进行合约的调用或者部署。 + +* [Transaction](#transaction) +* [createContractTransaction](#createcontracttransaction) +* [createFunctionCallTransaction](#createfunctioncalltransaction) +* [TransactionManager](#transactionmanager) +* [sendTransaction](#sendtransaction) +* [sendTransactionAsync](#sendtransactionasync) + +### Transaction + +**方法名** + +`Transaction(String to, BigInteger nonce, long quota, long valid_until_block, int version, int chainId, String value, String data)` + +根据参数新建一个交易。 + +**参数** + +* to - 交易将要的发送地址 +* nonce - 随机数用于防止重放攻击 +* quota - 用户支付矿工的费用 +* valid_until_block - 超时机制,超过设定块高取消交易 +* version - 链的版本信息 +* chainId - 链Id +* value - 交易中原生token的数量 +* data - 编码后交易数据(abi) + +**返回值** + +Transaction实例 + +**示例** +``` +CITAj service = CITAj.build(new HttpService("127.0.0.1")); +String to = "{address to which the tx is sent}"; +BigInteger nonce = BigInteger.valueOf(Math.abs(this.random.nextLong())); +long quota = 9999; +long valid_until_block = service.appBlockNumber().send().getBlockNumber() + 88; +int version = 0; +in chainId = 1; +String value = "100000000"; +String init = "{encoded abi}"; +Transaction tx = Transction.createContractTransaction(nonce, quota, valid_until_block, version, chainId, value, init); +``` +### createContractTransaction + +**方法名** + +`createContractTransaction(BigInteger nonce, long quota, long valid_until_block, int version, int chainId, String value, String init)` + +根据参数新建一个部署合约的交易。 + +**参数** + +* nonce - 随机数用于防止重放攻击 +* quota - 用户支付矿工的费用 +* valid_until_block - 超时机制,超过设定块高取消交易 +* version - 链的版本信息 +* chainId - 链Id +* value - 交易中原生token的数量 +* init - 合约编码后数据(abi) + +**返回值** + +Transaction实例 + +**示例** +``` +//create new CITAj service +CITAj service = CITAj.build(new HttpService("127.0.0.1")); + +//settings initiation +BigInteger nonce = BigInteger.valueOf(Math.abs(this.random.nextLong())); +long quota = 9999; +long valid_until_block = service.appBlockNumber().send().getBlockNumber() + 88; +int version = 0; +in chainId = 1; +String value = "100000000"; +String init = "{encoded abi}"; + +//construct transaction +Transaction txToDeployContract = Transction.createContractTransaction(nonce, quota, valid_until_block, version, chainId, value, init); +String signedTx = txToDeployContract.sign(this.config.getPrivateKey(), false, false); +AppSendTransaction appSendTx = service.sendRawTransaction(signedTx); +``` + +### createFunctionCallTransaction + +**方法名** + +`createFunctionCallTransaction(String to, BigInteger nonce, long quota, long valid_until_block, int version, int chainId, String value, String data)` + +根据参数新建一个合约调用的交易。 + +**参数** + +* to - 交易将要的发送地址 +* nonce - 随机数用于防止重放攻击 +* quota - 用户支付矿工的费用 +* valid_until_block - 超时机制,超过设定块高取消交易 +* version - 链的版本信息 +* chainId - 链Id +* value - 交易中原生token的数量 +* data - 编码后交易数据(abi) + +**返回值** + +Transaction实例 + +**示例** + +``` +//create new CITAj service +CITAj service = CITAj.build(new HttpService("127.0.0.1")); + +//settings initiation +String to = "{smart contract address}"; +BigInteger nonce = BigInteger.valueOf(Math.abs(this.random.nextLong())); +long quota = 9999; +long valid_until_block = service.appBlockNumber().send().getBlockNumber() + 88; +int version = 0; +in chainId = 1; +String value = "100000000"; +String init = "{encoded abi}"; + +//construct transaction +Transaction txToDeployContract = Transction.createFunctionCallTransaction(to, nonce, quota, valid_until_block, version, chainId, value, init); +String signedTx = txToDeployContract.sign(this.config.getPrivateKey(), false, false); +AppSendTransaction appSendTx = service.sendRawTransaction(signedTx); +``` +### TransactionManager + +**方法名** + +`TransactionManager(CITAj citaj, Credentials credentials)` + +**参数** + +* citaj - CITAj实例 +* credentials - 发起交易账户的credential + +**返回值** + +TransactionManager实例 + +**示例** +``` +Credentials credentials = Credentials.create(privateKey); +CITAj service = CITAj.build(new HttpService("127.0.0.1")); +TransactionManager transactionManager = new TransactionManager(service, credentials); +``` + +### sendTransaction + +**方法名** + +`AppSendTransaction sendTransaction(String to, String data, long quota, BigInteger nonce, long validUntilBlock, int version, int chainId, String value)` + +通过TransactionManager发送交易。 + +**参数** + +* to - 交易将要的发送地址 +* data - 编码后交易数据(abi) +* quota - 用户支付矿工的费用 +* nonce - 随机数用于防止重放攻击 +* valid_until_block - 超时机制,超过设定块高取消交易 +* version - 链的版本信息 +* chainId - 链Id +* value - 交易中原生token的数量 + +**返回值** + +AppSendTransaction + +**示例** + +``` +CitaTransactionManager transactionManager = new CitaTransactionManager(service, credentials); +String to = "{address to which the contract is sent}"; +String contractBin = "{contract bin or function call bin}"; +BigInteger quota = 99999; +BigInteger nonce = BigInteger.valueOf(Math.abs(this.random.nextLong())); +long valid_until_block = service.appBlockNumber().send().getBlockNumber() + 88; +int version = 0; +int chainId = 1; +String value = "0"; +AppSendTransaction appSendTransaction = citaTransactionManager.sendTransaction(to, contractBin, quota, nonce, valid_until_block, BigInteger.valueOf(version), chainId, value); +``` + +### sendTransactionAsync + +**方法名** + +`Flowable sendTransactionAsync(String to, String data, long quota, BigInteger nonce, long validUntilBlock, int version, int chainId, String value)` +通过TransactionManager发送交易。 + +**参数** + +* to - 交易将要的发送地址 +* data - 编码后交易数据(abi) +* quota - 用户支付矿工的费用 +* nonce - 随机数用于防止重放攻击 +* valid_until_block - 超时机制,超过设定块高取消交易 +* version - 链的版本信息 +* chainId - 链Id +* value - 交易中原生token的数量 + +**返回值** + +AppSendTransaction + +**示例** + +``` +TransactionManager transactionManager = new TransactionManager(service, credentials); +String to = "{address to which the contract is sent}"; +String contractBin = "{contract bin or function call bin}"; +BigInteger quota = 99999; +BigInteger nonce = BigInteger.valueOf(Math.abs(this.random.nextLong())); +long valid_until_block = service.appBlockNumber().send().getBlockNumber() + 88; +int version = 0; +int chainId = 1; +String value = "0"; +Flowable appSendTransaction = transactionManager.sendTransaction(to, contractBin, quota, nonce, valid_until_block, BigInteger.valueOf(version), chainId, value); +``` \ No newline at end of file diff --git a/tests/src/main/java/com/cryptape/cita/tests/InterfaceTest.java b/tests/src/main/java/com/cryptape/cita/tests/InterfaceTest.java index 71862668..24b717dc 100644 --- a/tests/src/main/java/com/cryptape/cita/tests/InterfaceTest.java +++ b/tests/src/main/java/com/cryptape/cita/tests/InterfaceTest.java @@ -44,7 +44,7 @@ public class InterfaceTest { static { Config conf = new Config(); - conf.buildService(false); + conf.buildService(true); service = conf.service; privateKey = conf.primaryPrivKey; quotaToDeploy = Long.parseLong(conf.defaultQuotaDeployment); @@ -56,7 +56,7 @@ public class InterfaceTest { public static void main(String[] args) throws Exception { - testGetBlockByNumber(BigInteger.valueOf(1)); + testGetBlockByNumber(BigInteger.valueOf(47)); testGetBalance(); @@ -140,7 +140,7 @@ public static void main(String[] args) throws Exception { private static void testGetBlockByNumber(BigInteger number) throws IOException { - AppBlock.Block block = service.appGetBlockByNumber(DefaultBlockParameter.valueOf(number), true).send().getBlock(); + AppBlock.Block block = service.appGetBlockByNumber(DefaultBlockParameter.valueOf(number), false).send().getBlock(); System.out.println("block " + number.toString() + " is: " + new Gson().toJson(block)); } diff --git a/tests/src/main/java/com/cryptape/cita/tests/SystemContractExample.java b/tests/src/main/java/com/cryptape/cita/tests/SystemContractExample.java index f038e32c..5bddab7b 100644 --- a/tests/src/main/java/com/cryptape/cita/tests/SystemContractExample.java +++ b/tests/src/main/java/com/cryptape/cita/tests/SystemContractExample.java @@ -27,7 +27,7 @@ public class SystemContractExample { public static void main(String[] args) throws Exception { CITAjSystemContract sysContract = new CITAjSystemContract(service); - int quotaPrice = sysContract.getQuotaPrice(senderAddr); + long quotaPrice = sysContract.getQuotaPrice(senderAddr); System.out.println("Quota price is: " + quotaPrice); List nodes = sysContract.listNode(senderAddr);