无法使用web3j(Java客户端)将智能合约部署到私有链上,错误提示信息是超时。
我用web3j与我在Azure中创建的私有链进行交互。我使用Remix和Metamask创建了以太坊智能合约,并且能够从Java中查看该智能合约。
但是,我无法从Java部署或创建合同。我遵循了web3j的命令但是抛出了这个:
1.错误提示:Transaction receipt was not generated after 600 seconds for transaction
2.报错原因是:org.web3j.protocol.exceptions.TransactionTimeoutException
我的代码如下:
ContractRunner.java
public static void main(String args[]) throws InterruptedException, ExecutionException, IOException, CipherException, TransactionTimeoutException {
Web3j web3 = Web3j.build(new HttpService("http://xxxxxxxxx.westus.cloudapp.azure.com:8545/")); // defaults to http://localhost:8545/
Web3ClientVersion web3ClientVersion = web3.web3ClientVersion().sendAsync().get();
String clientVersion = web3ClientVersion.getWeb3ClientVersion();
System.out.println(clientVersion);
Credentials credentials = WalletUtils.loadCredentials("MyPassword", "C:\\Users\\adheep_m\\AppData\\Roaming\\Ethereum\\keystore\\UTC--2017-07-07T13-52-18.006069200Z--3b0d3fa08f0e0b3da8fe1f8ac0e05861bfdada25");
System.out.println(credentials.getAddress());
Token contract = Token.deploy(web3,credentials,BigInteger.valueOf(3000000),BigInteger.valueOf(3000000),BigInteger.valueOf(0)).get();
System.out.println(contract.getContractAddress());
}
Token.sol
pragma solidity ^0.4.0;
contract Token {
mapping (address => uint) public balances;
function Token() {
balances[msg.sender] = 1000000;
}
function transfer(address _to, uint _amount) {
if (balances[msg.sender] < _amount) {
throw;
}
balances[msg.sender] -= _amount;
balances[_to] += _amount;
}
Token.java (Generated from Web3j)
public final class Token extends Contract {
private static final String BINARY = "6060604052341561000f57600080fd5b5b600160a060020a0333166000908152602081905260409020620f424090555b5b6101678061003f6000396000f300606060405263ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166327e235e38114610048578063a9059cbb14610086575b600080fd5b341561005357600080fd5b61007473ffffffffffffffffffffffffffffffffffffffff600435166100b7565b60405190815260200160405180910390f35b341561009157600080fd5b6100b573ffffffffffffffffffffffffffffffffffffffff600435166024356100c9565b005b60006020819052908152604090205481565b73ffffffffffffffffffffffffffffffffffffffff3316600090815260208190526040902054819010156100fc57600080fd5b73ffffffffffffffffffffffffffffffffffffffff338116600090815260208190526040808220805485900390559184168152208054820190555b50505600a165627a7a7230582081fd33c821a86127abf00c9fafe2e14e4db6279ab9dd788e3ad3597d2280b6cf0029";
private Token(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
}
private Token(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}
public Future<Uint256> balances(Address param0) {
Function function = new Function("balances",
Arrays.<Type>asList(param0),
Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {}));
return executeCallSingleValueReturnAsync(function);
}
public Future<TransactionReceipt> transfer(Address _to, Uint256 _amount) {
Function function = new Function("transfer", Arrays.<Type>asList(_to, _amount), Collections.<TypeReference<?>>emptyList());
return executeTransactionAsync(function);
}
public static Future<Token> deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, BigInteger initialWeiValue) {
return deployAsync(Token.class, web3j, credentials, gasPrice, gasLimit, BINARY, "", initialWeiValue);
}
public static Future<Token> deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, BigInteger initialWeiValue) {
return deployAsync(Token.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, "", initialWeiValue);
}
public static Token load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
return new Token(contractAddress, web3j, credentials, gasPrice, gasLimit);
}
public static Token load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
return new Token(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}
}
我不确定代码有什么问题。
问题的解决
我知道代码出了什么问题。我调试了web3j的源代码,发现由于挖掘节点在40
次重试后不接受交易,其休眠持续时间为15000
,因此引发交易TransactionTimeoutException
,并且不生成TransactionReceipt
。从TransactionManager.java
看下面的web3j代码:
Source of the Exception
private static final int SLEEP_DURATION = 15000;
private static final int ATTEMPTS = 40;
private TransactionReceipt getTransactionReceipt(
String transactionHash, int sleepDuration, int attempts)
throws IOException, InterruptedException, TransactionTimeoutException {
Optional<TransactionReceipt> receiptOptional =
sendTransactionReceiptRequest(transactionHash);
for (int i = 0; i < attempts; i++) {
if (!receiptOptional.isPresent()) {
Thread.sleep(sleepDuration);
receiptOptional = sendTransactionReceiptRequest(transactionHash);
} else {
return receiptOptional.get();
}
}
throw new TransactionTimeoutException("Transaction receipt was not generated after " +
((sleepDuration * attempts) / 1000 +
" seconds for transaction: " + transactionHash));
}
真正的原因是
发现问题真正的原因是GAS_PRICE和GAS_LIMIT。由于价值低,矿工没有开采这个交易。
更新代码,从web3j的智能合约Contract
类,使用默认的GAS_PRICE和GAS_LIMIT限制。参见下面更新的代码
BigInteger GAS = Contract.GAS_LIMIT;
BigInteger GAS_PRICE = Contract.GAS_PRICE;
Token contract = Token.deploy(web3,credentials,GAS_PRICE,GAS,ETHER);
System.out.println("Deployed Contract at "+contract.getContractAddress());
OK,完事大吉。