Skip to content

Commit 61c78e3

Browse files
Merge pull request #14 from TIHBS/fix/issue-13
Allow Invoking Read-only Ethereum Smart Contracts Asynchronously
2 parents 5694cd2 + 7615847 commit 61c78e3

File tree

1 file changed

+38
-46
lines changed

1 file changed

+38
-46
lines changed

src/main/java/blockchains/iaas/uni/stuttgart/de/adaptation/adapters/ethereum/EthereumAdapter.java

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@
7676
import org.web3j.protocol.core.JsonRpc2_0Web3j;
7777
import org.web3j.protocol.core.methods.request.EthFilter;
7878
import org.web3j.protocol.core.methods.response.EthBlock;
79-
import org.web3j.protocol.core.methods.response.EthCall;
8079
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
8180
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
8281
import org.web3j.protocol.core.methods.response.EthLog;
@@ -356,17 +355,17 @@ public CompletableFuture<Transaction> invokeSmartContract(
356355

357356
// if we are expecting a return value, we try to invoke as a method call, otherwise, we try a transaction
358357
if (outputParameters.size() > 0) {
359-
Transaction resultFromEthCall = invokeFunctionByMethodCall(encodedFunction, smartContractAddress, outputs, function.getOutputParameters());
360-
361-
if (resultFromEthCall != null) {
362-
return CompletableFuture.completedFuture(resultFromEthCall);
363-
} else {
364-
CompletableFuture<Transaction> future = new CompletableFuture<>();
365-
future.completeExceptionally(new InvokeSmartContractFunctionFailure("Failed to invoke read-only smart contract function"));
366-
return future;
367-
}
358+
return this.invokeFunctionByMethodCall(
359+
encodedFunction,
360+
smartContractAddress,
361+
outputs,
362+
function.getOutputParameters());
368363
} else {
369-
return this.invokeFunctionByTransaction(waitFor, encodedFunction, smartContractAddress, timeoutMillis);
364+
return this.invokeFunctionByTransaction(
365+
waitFor,
366+
encodedFunction,
367+
smartContractAddress,
368+
timeoutMillis);
370369
}
371370
} catch (Exception e) {
372371
log.error("Decoding smart contract function call failed. Reason: {}", e.getMessage());
@@ -572,37 +571,34 @@ private EthFilter generateFilter(String smartContractAddress, Event event, int p
572571
return filter;
573572
}
574573

575-
private Transaction invokeFunctionByMethodCall(String encodedFunction, String scAddress, List<Parameter> outputs,
576-
List<TypeReference<Type>> returnTypes) throws InvokeSmartContractFunctionFailure {
577-
try {
578-
org.web3j.protocol.core.methods.request.Transaction transaction = org.web3j.protocol.core.methods.request.Transaction
579-
.createEthCallTransaction(credentials.getAddress(), scAddress, encodedFunction);
580-
EthCall ethCall = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).send();
581-
List<Type> decoded = FunctionReturnDecoder.decode(ethCall.getValue(), returnTypes);
582-
583-
if (returnTypes.size() != decoded.size())
584-
throw new InvokeSmartContractFunctionFailure("Failed to invoke function by ethCall");
585-
586-
Transaction tx = new LinearChainTransaction();
587-
tx.setState(TransactionState.RETURN_VALUE);
588-
List<Parameter> returnedValues = new ArrayList<>();
589-
590-
for (int i = 0; i < decoded.size(); i++) {
591-
returnedValues.add(Parameter
592-
.builder()
593-
.name(outputs.get(i).getName())
594-
.value(ParameterDecoder.decode(decoded.get(i)))
595-
.build());
596-
}
574+
private CompletableFuture<Transaction> invokeFunctionByMethodCall(String encodedFunction, String scAddress, List<Parameter> outputs,
575+
List<TypeReference<Type>> returnTypes) {
576+
org.web3j.protocol.core.methods.request.Transaction transaction = org.web3j.protocol.core.methods.request.Transaction
577+
.createEthCallTransaction(credentials.getAddress(), scAddress, encodedFunction);
578+
579+
return web3j.ethCall(transaction, DefaultBlockParameterName.LATEST)
580+
.sendAsync()
581+
.thenApply(ethCall -> FunctionReturnDecoder.decode(ethCall.getValue(), returnTypes))
582+
.thenApply(decoded -> {
583+
if (returnTypes.size() != decoded.size())
584+
throw new InvokeSmartContractFunctionFailure("Failed to invoke read-only Ethereum smart contract function");
585+
586+
Transaction tx = new LinearChainTransaction();
587+
tx.setState(TransactionState.RETURN_VALUE);
588+
List<Parameter> returnedValues = new ArrayList<>();
589+
590+
for (int i = 0; i < decoded.size(); i++) {
591+
returnedValues.add(Parameter
592+
.builder()
593+
.name(outputs.get(i).getName())
594+
.value(ParameterDecoder.decode(decoded.get(i)))
595+
.build());
596+
}
597597

598-
tx.setReturnValues(returnedValues);
598+
tx.setReturnValues(returnedValues);
599599

600-
return tx;
601-
} catch (Exception e) {
602-
log.debug("Failed to execute smart contract function via eth_call. Reason: {}", e.getMessage());
603-
// this is important so we know we should try a transaction
604-
return null;
605-
}
600+
return tx;
601+
});
606602
}
607603

608604
private CompletableFuture<Transaction> invokeFunctionByTransaction(long waitFor, String encodedFunction, String scAddress, long timeoutMillis) {
@@ -652,21 +648,17 @@ private CompletableFuture<TransactionReceipt> waitUntilTransactionIsMined(final
652648
result.completeExceptionally(exception);
653649
} else {
654650
EthGetTransactionReceipt receipt = web3j.ethGetTransactionReceipt(txHash).send();
655-
if(receipt != null && receipt.getTransactionReceipt().isPresent()) {
651+
if (receipt != null && receipt.getTransactionReceipt().isPresent()) {
656652
result.complete(receipt.getResult());
657653
}
658654
}
659-
660655
} catch (IOException e) {
661656
result.completeExceptionally(e);
662657
}
663658
});
664659

665660
//dispose the flowable when the CompletableFuture completes (either when detecting an event, or manually)
666-
result.whenComplete(
667-
(v, e) -> {
668-
subscription.dispose();
669-
});
661+
result.whenComplete((v, e) -> subscription.dispose());
670662

671663
return result;
672664
}

0 commit comments

Comments
 (0)